From e7499dc7167e973ad387e3b3d0d23226544ef3f2 Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Fri, 20 Mar 2026 08:44:12 -0400 Subject: [PATCH] master into dependabot Target (#14620) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Rubicon Bid Adapter: add support for primaryCatId and secondaryCatIds (#14361) * Bump lodash from 4.17.21 to 4.17.23 (#14368) Bumps [lodash](https://github.com/lodash/lodash) from 4.17.21 to 4.17.23. - [Release notes](https://github.com/lodash/lodash/releases) - [Commits](https://github.com/lodash/lodash/compare/4.17.21...4.17.23) --- updated-dependencies: - dependency-name: lodash dependency-version: 4.17.23 dependency-type: direct:development ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Yield one bid adapter: support Interstitial (instl param) in building server request (#14370) * Support Interstitial (instl param) in building server request * adjust unit tests for Instl param * New Adapter: Panxo - AI traffic monetization SSP (#14365) This adapter enables publishers to monetize AI-referred traffic through Prebid.js. - Bidder code: panxo - GVL ID: 1527 - Media types: Banner - Features: GDPR/TCF 2.0, CCPA, GPP, COPPA, schain, First Party Data, Price Floors - Requires Panxo Signal script to be loaded before Prebid Documentation: modules/panxoBidAdapter.md Tests: test/spec/modules/panxoBidAdapter_spec.js * Bridgewell Bid Adapter: expand request data (#14320) * enhance adapter with additional bid parameters * Add additional bid parameters to tests of bridgewellBidAdapter * pass mediaType and size to getFloor --------- Co-authored-by: Laneser * Remove "emetriq" as "appnexus" alias (#14369) * Added size ids for 1080x1920 (#14376) * IntentIq ID Module & Analytical Adapter: increase default server call time, support region, bugfixes (#14374) * AGT-734: Support region for prebid modules (merge 0_3_4 with master) * AGT-730: move spd to partnerData (merge 0_3_4 to master) * AGT-765: Send ad size and pos in impression reporting module (#58) * AGT-765: pos and size * AGT-765: Tests for position resolving * AGT-765: Test fix * AGT-756: Missed vrref in payload fix (#56) * AGT-756: vrref in payload fix * remove comment * AGT-756: Fix vrref bug * AGT-756: Remove comments * AGT-756: Test for vrref * update requestRtt to show more clear time (#59) * AGT-739: Change time to call server (#57) * fix typo, remove parameter duplication (#60) * fix typo, remove parameter duplication * update doc examples * AGT-721: Documentation for region, size, pos (#61) * fix region parameter in table (#62) * update tests * remove unused test --------- Co-authored-by: dmytro-po * Core: adding ima params to local cache request (#14312) * Core: adding ima params to local cache request * retrieving ima params * usp data handler * AdOceanBidAdapter: add gvlid (#14382) * Connatix Bid Adapter: Add coppa & gpp signals (#14379) * added modules and command for fandom build * revert: cnx master changes * feat: stop storing ids in cookie * test: update tests * fix: remove trailing space * Add coppa & gpp signals * update unit tests --------- Co-authored-by: dragos.baci Co-authored-by: DragosBaci <118546616+DragosBaci@users.noreply.github.com> Co-authored-by: Alex Co-authored-by: Gaina Dan-Lucian Co-authored-by: Gaina Dan-Lucian <83463253+Dan-Lucian@users.noreply.github.com> * Core: granular settings for main thread yielding (#13789) * turn off yielding with scheudler === false * sync renderAd * what would we do without our Linter * Turn off yielding completely * linting (again) * granular yielding, off by default * Too many callbacks * Avoid yielding the main thread during renderAd * Expose requestBids hooks * lint * Revert "Expose requestBids hooks" This reverts commit ae0062a3ecc961e61d49fa05acdcfb7a372d65bb. * Revert "Core: wait for creative document DOMContentLoaded (#13991)" This reverts commit 2870230d47be17201df3812782083a54b462774a. * Revert "Core: remove use of document.write in rendering (#13851)" This reverts commit 48419a62d330a48433b4ab7163ca538966f9ed09. * lint * simplify yield config, default to true, add auctionOptions.legacyRender * lint * update e2e tests --------- Co-authored-by: Patrick McCann Co-authored-by: Patrick McCann * Taboola support extra signals (#14299) * add deferredBilling support using onBidBillable * update burl setting * support nurl firing logic * add extra signals to taboola request * add extra ad signals * fix missing semicolon * use Prebid's built-in counters * updated detectBot logic * Prebid 10.23.0 release * Increment version to 10.24.0-pre * udpate variable (#14380) * Replace global.navigator with window.navigator (#14389) This throws in production since upgrade to 10+ No other module is using global.navigator all references goes to window.navigator * s3rtd: update default params and docs (#14378) * Adkernel Bid Adapter: add Intellectscoop alias (#14395) Co-authored-by: Patrick McCann * Bump fast-xml-parser from 5.2.5 to 5.3.4 (#14401) Bumps [fast-xml-parser](https://github.com/NaturalIntelligence/fast-xml-parser) from 5.2.5 to 5.3.4. - [Release notes](https://github.com/NaturalIntelligence/fast-xml-parser/releases) - [Changelog](https://github.com/NaturalIntelligence/fast-xml-parser/blob/master/CHANGELOG.md) - [Commits](https://github.com/NaturalIntelligence/fast-xml-parser/compare/v5.2.5...v5.3.4) --- updated-dependencies: - dependency-name: fast-xml-parser dependency-version: 5.3.4 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump actions/upload-artifact from 4 to 6 (#14402) Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4 to 6. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v4...v6) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Updated size id from rubicon production, api endpoint (#14377) Co-authored-by: Patrick McCann * BeOp Bid Adapter: Fix slot name detection to use best practices (#14399) * Fix slot name detection to use best practices * Post review commit * Linter fix * Last post review commit List of concerned names managed | adUnitCode | Résultat | | ---------------------------------- | ---------------- | | `div-gpt-ad-article_top_123456` | `article_top` | | `div-gpt-ad-sidebar-1678459238475` | `sidebar` | | `div-gpt-ad-topbanner-1` | `topbanner-1` ✅ | | `div-gpt-ad-topbanner-2` | `topbanner-2` ✅ | | `sidebar-123456` | `sidebar-123456` | | `article_bottom` | `article_bottom` | * Only normalize GPT codes, leave others unchanged * Add tests on FIX and missing code coverage * realTimeData: fix bug where setting circular references in FPD causes activity checks to get stuck in an infinite loop (#14366) * realTimeData: fix bug where setting circular references in FPD causes activity checks to get stuck in an infinite loop * handle circular references in unguarded properties * prefer allowing more data over avoiding leaks * more edge cases * Bump @isaacs/brace-expansion from 5.0.0 to 5.0.1 (#14410) Bumps @isaacs/brace-expansion from 5.0.0 to 5.0.1. --- updated-dependencies: - dependency-name: "@isaacs/brace-expansion" dependency-version: 5.0.1 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * ID5 ID module: add option to use custom external targeting (#14324) * Add custom tag reporting mechanism for Id5IdSystem module * Add documentation * empty line rm, lint failure * empty line rm, lint failure * cannot use withResolvers in tests * type change in doc * fix example * Core: loading external scripts linting rule (#14354) * Core: loading external scripts linting rule * using default rule instead of custom * fixing overwritten event/adLoader rule * change to no-restricted-syntax * no-restricted-imports --------- Co-authored-by: Demetrio Girardi * Remove PAAPI-related functionality from Unruly adapter (#14358) * SBE-2291 Remove protected audience related test code * SBE-2291 Remove protected audience related code * New library: placement position &bbidmaticBidAdapter: update viewability tracking logic (#14372) * bidmaticBidAdapter: update viewability tracking logic * bidmaticBidAdapter: update viewability tracking logic * bidmaticBidAdapter: update viewability tracking logic * bidmaticBidAdapter: update viewability tracking logic * Humansecurity RTD Provider: migrate to TypeScript and optimize token handling (#14362) * - Migrated to TypeScript - Removed hardcoded token injection into ortb2Fragments and delegate to the HUMAN implementation, enabling management of which bidders receive tokens and enhancing monitoring and control of latency and performance - Introduce a cached implementation reference via getImpl() - Add module version query parameter when loading the implementation script - Wire onAuctionInitEvent so the implementation can collect QoS, telemetry and statistics per auction * Apply suggestion from @Copilot Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * - validate module version parameter in script URL - add HumanSecurityImpl interface --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * WURFL RTD: update module documentation (#14364) * WURFL RTD: update module documentation * WURFL RTD: update module documentation * Change humansecurityRtdProvider.js to .ts extension * Kobler bid adapter: pass adunitcode in bid request. (#14392) * Page view ID. * Page view ID. * Removed console.log. * Removed unused import. * Improved example. * Fixed some tests. * Kobler bid adapter: pass adunitcode in bid request. * percentInView: fix bug where viewability calculation is inaccurate inside friendly iframes (#14414) * percentInView: fix bug where viewability calculation is inaccurate inside friendly iframes * use boundingClientRect * New module: Shaping rules (#14079) * initial commit * storeSplits method * unit tests * remove test * removing storing & extract merging ortb2 * lint * adding default handling * lint * merge fpd cache * review fixes * refactoring, bug fixing, tests * lint * extending bidPrice schema function * removing invalid example * session random * rename * module name * random per auction * modifies some schema function to return value instead of condition result * extra schema evaluators * json fix * evaluating rules per auction * evaluating rules per auction * static config * auctionId * safe guards * registering activities only once * expose configuration type; update integ example rules json URL * evaluating rules fix * fixing model group selection --------- Co-authored-by: Demetrio Girardi Co-authored-by: Patrick McCann * Update yandexBidAdapter.md (#14416) - Added email address to the main description - Added 'cur' parameter to the parameters list - Minor changes in the Adunit config examples * new rubicon apex url (#14417) * TeqBlaze Sales Agent Bid Adapter: initial release (#14413) * TeqBlazeSalesAgent Bid Adapter: initial release * update doc --------- Co-authored-by: Patrick McCann * Prebid 10.24.0 release * Increment version to 10.25.0-pre * Yahoo Ads Bid Adapter: Add Transaction ID (TID) Support (#14403) * tesing * adding tests * md file * Update yahooAdsBidAdapter_spec.js * Fix formatting in yahooAdsBidAdapter_spec.js * Fix casing for enableTIDs in documentation --------- Co-authored-by: dsravana Co-authored-by: Patrick McCann * gam video module: Include us_privacy based on gpp when downloading VAST for the IMA player (#14424) * Include us_privacy based on gpp when downloading VAST for the IMA player IMA player has no support for GPP, but does support uspapi. When on `window`, there is only `__gpp`, we can still pass a US Privacy string by deriving the string from the `__gpp` data, * Added tests for the retrieveUspInfoFromGpp method * Re-write the test to use the public API instead of internal methods * Deal with the option that the parsedSections contains sections which are arrays * Deal with possible null/missing values in the GPP parsedSection * Optidigital Bid Adapter: Adding ortb2 device, keywords, addtlConsent, gpid (#14383) * add gpp suport * update of the optidigital adapter * fix the lint issue * refactor ortb2 keywords trim logic --------- Co-authored-by: Dawid W Co-authored-by: Patrick McCann * LocID User ID Submodule: add locIdSystem (#14367) * chore(prebid): scaffold locid user id module * update LocID module to support first-party endpoint fetching and privacy signal handling * update LocID system tests for gdpr handling and consent string validation * Enhance LocID module documentation and tests for privacy signal handling. Updated comments for clarity, added test cases for maximum ID length and empty endpoint handling, and refined privacy configuration notes in the documentation. * Refactor LocID module to standardize naming conventions and enhance privacy signal handling. Updated module name to 'locId', improved consent data processing functions, and revised documentation and tests accordingly. * Added Apache 2.0 license header. * Add LocID User ID sub-module documentation and refactor ajax usage in locIdSystem module - Introduced locid.md documentation detailing the LocID User ID sub-module, including installation, configuration, parameters, and privacy handling. - Refactored locIdSystem.js to utilize ajaxBuilder for improved request handling. - Updated tests in locIdSystem_spec.js to accommodate changes in ajax usage and ensure proper timeout configurations. * Remove docs folder - to be submitted to prebid.github.io separately * Update LocID atype to 1 for compliance with OpenRTB 2.6 specifications - Changed the default EID atype in locIdSystem.js and related documentation from 3384 to 1, reflecting the correct device identifier as per OpenRTB 2.6 Extended Identifiers spec. - Updated references in locIdSystem.md and userId/eids.md to ensure consistency across documentation. - Adjusted unit tests in locIdSystem_spec.js to validate the new atype value. * Enhance LocID module with connection IP handling and response parsing improvements - Introduced connection IP validation and storage alongside LocID to support IP-aware caching. - Updated response parsing to only extract `tx_cloc` and `connection_ip`, ignoring `stable_cloc`. - Enhanced documentation to reflect changes in stored value format and endpoint response requirements. - Modified unit tests to cover new functionality, including connection IP checks and expiration handling. * fix getValue string handling, GDPR enforcement gating, extendId docs * LocID: enforce IP cache TTL in extendId and update tests/docs * LocID: honor null tx_cloc, reject whitespace-only IDs, add stable_cloc exclusion test * LocID: remove legacy 3384 references and enforce atype 1 * LocID: add vendorless TCF marker and scope 3384 guard * enhance locIdSystem to handle whitespace-only tx_cloc and update documentation. Ensure null IDs are cached correctly when tx_cloc is empty or whitespace, and adjust caching logic to honor null responses from the main endpoint. --------- Co-authored-by: Brian * Github Actions: bump download artifact (#14440) * CI: harden artifact restore for browserstack workflows * Update run-tests.yml * Fix comment formatting in test.yml * Retry on artifact download failure --------- Co-authored-by: Demetrio Girardi * 33acrossId System: stabilize ID wipe unit tests (#14441) * Bump axios from 1.13.2 to 1.13.5 (#14443) Bumps [axios](https://github.com/axios/axios) from 1.13.2 to 1.13.5. - [Release notes](https://github.com/axios/axios/releases) - [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md) - [Commits](https://github.com/axios/axios/compare/v1.13.2...v1.13.5) --- updated-dependencies: - dependency-name: axios dependency-version: 1.13.5 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Core: update storage disclosure for prebid.storage (#14442) * Build process: Add .cache to gulp clean (#14438) * build: include webpack cache in clean task * Remove comment from clean function Removed comment about webpack filesystem cache in clean function. * Add GPP consent support to user sync URL in Missena adapter (#14436) * Agent guidelines: Add context for repo history access (#14430) Added additional context for accessing repo history. * Set alwaysHasCapacity for Sovrn bid adapter (#14454) * Sevio bid adapter fallback natives (#14390) * [SevioBidAdapter] - fix mapping by id for the native ads * [SevioBidAdapter] - fix mapping by id for the native ads * Add native parsing tests * Yaleo Bid Adapter: initial release (#14452) Co-authored-by: Alexandr Kim * feat: Mile Bid Adapter - Initial release (#14388) Co-authored-by: Shashank <=> * bidMatic bid adapter: update placement info logic (#14418) * Panxo RTD Provider: initial release (#14419) * New module: Panxo RTD Provider Add Panxo RTD submodule that enriches OpenRTB bid requests with real-time AI traffic classification signals through device.ext.panxo and site.ext.data.panxo. * fix: fail open when bridge is unavailable and guard null messages - Flush pending auction callbacks when the implementation bridge cannot be reached, so auctions are never blocked indefinitely. - Guard against null bridge messages to prevent runtime errors. - Add tests for both scenarios. --------- Co-authored-by: Monis Qadri * Core: remove type declaration for `getStatusCode` (#14431) * Core: reintroduce bidResponse.getStatusCode * Revert "Core: reintroduce bidResponse.getStatusCode" This reverts commit dcf61ba5614144355f0f0b3a9c3764e618802265. * Core: remove getStatusCode typing * Prebid 10.25.0 release * Increment version to 10.26.0-pre * limelight: Send open rtb bid requests from our prebid js adapter (#14397) * changed requests format to OpenRTB. * Added more tests * fix refererInfo.page fallback for site.page * fix built imp for size-only * fix built imp for size-only * OMS Bid Adapter: add prebid js version to request payload (#14421) * Bump qs from 6.14.1 to 6.14.2 (#14478) Bumps [qs](https://github.com/ljharb/qs) from 6.14.1 to 6.14.2. - [Changelog](https://github.com/ljharb/qs/blob/main/CHANGELOG.md) - [Commits](https://github.com/ljharb/qs/compare/v6.14.1...v6.14.2) --- updated-dependencies: - dependency-name: qs dependency-version: 6.14.2 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump fast-xml-parser from 5.3.4 to 5.3.6 (#14482) Bumps [fast-xml-parser](https://github.com/NaturalIntelligence/fast-xml-parser) from 5.3.4 to 5.3.6. - [Release notes](https://github.com/NaturalIntelligence/fast-xml-parser/releases) - [Changelog](https://github.com/NaturalIntelligence/fast-xml-parser/blob/master/CHANGELOG.md) - [Commits](https://github.com/NaturalIntelligence/fast-xml-parser/compare/v5.3.4...v5.3.6) --- updated-dependencies: - dependency-name: fast-xml-parser dependency-version: 5.3.6 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Floor module: add enforcement allowlist config option (#14455) * Core: add bidder-scoped floor enforcement * Core: enforce floors allowlist against adapterCode * Various modules: remove legacy GPT targeting fallbacks (#14450) * Core: remove legacy GPT targeting fallbacks * Reconciliation RTD Provider: handle missing RSDK_ADID targeting * Core: fix targetingLock GPT test mock for getConfig * TargetVideo bid adapter: send price floor param (#14406) * TargetVideo bid adapter: send price floor param * Add support for the price floors module * Add imports * Fix getBidFloor function floor params --------- Co-authored-by: dnrstc * New adapter: Verben (#14494) Co-authored-by: Verben Co-authored-by: Patrick McCann * Teal bid adapter: include native and video media types (#14493) * Proxistore Bid Adapter: migration to OpenRTB (#14411) * Update Proxistore endpoint URLs in adapter and tests Updated the Proxistore `COOKIE_BASE_URL` and `COOKIE_LESS_URL` to the new `abs` domain in both the adapter and its corresponding test file. This ensures consistency with the updated API endpoints. * Integrate OpenRTB converter for Proxistore bid adapter, and add OpenRTB request as a separate field. * Refactor Proxistore bid adapter to improve OpenRTB handling, add GDPR-specific URL selection, and enhance test coverage. * Add support for website and language parameters in Proxistore bid adapter requests, with corresponding test coverage. * Handle empty response body in Proxistore bid adapter to avoid runtime errors. --------- Co-authored-by: Anthony Richir * Floxis Bid Adapter: initial release (#13934) * Floxis Bid Adapter : initial release * Added ORTB parameters for blocking * Adjusted documentation with maintainer info * Added more validations, extracted converter to a const * Floxis Bid Adapter: redesign to seat-based architecture with ortbConverter Major rewrite replacing teqblazeUtils with ortbConverter for ORTB 2.x compliance. Changes: - New params: seat (required), region (required), partner (required) - Endpoint URL: https://{subdomain}.floxis.tech/pbjs?seat={seat} - subdomain = region for 'floxis' partner - subdomain = {partner}-{region} for white-label partners - ORTB-native implementation with Floors Module support - 40 comprehensive tests with full code coverage - Updated documentation with examples Addresses all PR #13934 review comments from @osazos * Rename FloxisBidAdapter.md to floxisBidAdapter.md * Code review adjustments --------- Co-authored-by: Patrick McCann * InsurAds Bid Adapter: Initial Implementation (#14470) * InsurAds Bid Adapter Implementation * Remove test alias * Storage Settings Example * Adapter rename 1/2 * Adapter rename 2/2 * Update modules/insuradsBidAdapter.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update modules/insuradsBidAdapter.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update test/spec/modules/insuradsBidAdapter_spec.js Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * The params need to be registered under nexx360 --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * LeagueM BId Adapter: initial release (#14479) * LeagueM BId Adapter: initial release * Update test/spec/modules/leagueMBidAdapter_spec.js Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update test/spec/modules/leagueMBidAdapter_spec.js Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update test/spec/modules/leagueMBidAdapter_spec.js Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update modules/leagueMBidAdapter.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Patrick McCann * New Adapter: Harion (#14398) * new adapter harion * refactor(adapter): guard added to interpretResponse * Core: disabling fingerprinting apis (#14404) * Core: disabling fingerprinting apis * getFallbackWindow -> utils * Fix several typos in comments and tests (#14498) * Bump ajv from 6.12.6 to 6.14.0 (#14499) Bumps [ajv](https://github.com/ajv-validator/ajv) from 6.12.6 to 6.14.0. - [Release notes](https://github.com/ajv-validator/ajv/releases) - [Commits](https://github.com/ajv-validator/ajv/compare/v6.12.6...v6.14.0) --- updated-dependencies: - dependency-name: ajv dependency-version: 6.14.0 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Setting alwaysHasCapacity flag to true (#14500) * Adcluster Bid Adapter: Support Adcluster (#14050) * adcluster - new adapter * fixes * video preview id change * video preview id change * Update adclusterBidAdapter_spec.js * fallback * multiformat fix --------- Co-authored-by: Patrick McCann Co-authored-by: Patrick McCann * ReVantage Bid Adapter: initial release (#14180) * Create revantageBidAdapter.js * Create revantageBidAdapter.md * Update revantageBidAdapter.js * Update revantageBidAdapter.js * Update revantageBidAdapter.js * Update revantageBidAdapter.js Fixed trailing slash Error on Line 123 * Create revantageBidAdapter_spec.js * Update revantageBidAdapter_spec.js Fixed Trailing slashes (again) * Update revantageBidAdapter_spec.js * Update revantageBidAdapter_spec.js same thing again * Refactor Revantage Bid Adapter for media types and bids Refactor Revantage Bid Adapter to use media type constants and improve bid response handling. * Refactor RevantageBidAdapter tests for GPP consent * Update modules/revantageBidAdapter.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update modules/revantageBidAdapter.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Validate feedId consistency in batch bid requests Added validation to ensure all bid requests in a batch have the same feedId, logging a warning if they do not. * Add test for rejecting batch with different feedIds * Update syncOptions for image sync URL parameters * Update sync URL to use 'tag=img' instead of 'type=img' * Update print statement from 'Hello' to 'Goodbye' * fixed * Enhance video bid handling and add utility functions Added functions to handle video size extraction and VAST detection. --------- Co-authored-by: Patrick McCann Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * AdMatic Bid Adapter : add adrubi alias (#14504) * Admatic Bidder Adaptor * Update admaticBidAdapter.md * Update admaticBidAdapter.md * remove floor parameter * Update admaticBidAdapter.js * Admatic Bid Adapter: alias and bid floor features activated * Admatic adapter: host param control changed * Alias name changed. * Revert "Admatic adapter: host param control changed" This reverts commit de7ac85981b1ba3ad8c5d1dc95c5dadbdf5b9895. * added alias feature and host param * Revert "added alias feature and host param" This reverts commit 6ec8f4539ea6be403a0d7e08dad5c7a5228f28a1. * Revert "Alias name changed." This reverts commit 661c54f9b2397e8f25c257144d73161e13466281. * Revert "Admatic Bid Adapter: alias and bid floor features activated" This reverts commit 7a2e0e29c49e2f876b68aafe886b336fe2fe6fcb. * Revert "Update admaticBidAdapter.js" This reverts commit 7a845b7151bbb08addfb58ea9bd5b44167cc8a4e. * Revert "remove floor parameter" This reverts commit 7a23b055ccd4ea23d23e73248e82b21bc6f69d90. * Admatic adapter: host param control && Add new Bidder * Revert "Admatic adapter: host param control && Add new Bidder" This reverts commit 3c797b120c8e0fe2b851381300ac5c4b1f92c6e2. * commit new features * Update admaticBidAdapter.js * updated for coverage * sync updated * Update adloader.js * AdMatic Bidder: development of user sync url * Update admaticBidAdapter.js * Set currency for AdserverCurrency: bug fix * Update admaticBidAdapter.js * update * admatic adapter video params update * Update admaticBidAdapter.js * update * Update admaticBidAdapter.js * update * update * Update admaticBidAdapter_spec.js * Update admaticBidAdapter.js * Update admaticBidAdapter.js * Revert "Update admaticBidAdapter.js" This reverts commit 1216892fe55e5ab24dda8e045ea007ee6bb40ff8. * Revert "Update admaticBidAdapter.js" This reverts commit b1929ece33bb4040a3bcd6b9332b50335356829c. * Revert "Update admaticBidAdapter_spec.js" This reverts commit 1ca659798b0c9b912634b1673e15e54e547b81e7. * Revert "update" This reverts commit 689ce9d21e08c27be49adb35c5fd5205aef5c35c. * Revert "update" This reverts commit f381a453f9389bebd58dcfa719e9ec17f939f338. * Revert "Update admaticBidAdapter.js" This reverts commit 38fd7abec701d8a4750f9e95eaeb40fb67e9f0e6. * Revert "update" This reverts commit a5316e74b612a5b2cd16cf42586334321fc87770. * Revert "Update admaticBidAdapter.js" This reverts commit 60a28cae302b711366dab0bff9f49b11862fb8ee. * Revert "admatic adapter video params update" This reverts commit 31e69e88fd9355e143f736754ac2e47fe49b65b6. * update * Update admaticBidAdapter.js * Update admaticBidAdapter_spec.js * mime_type add * add native adapter * AdMatic Adapter: Consent Management * added gvlid * Update admaticBidAdapter.js * admatic cur update * Update admaticBidAdapter.js * Update admaticBidAdapter.js * Update admaticBidAdapter.js * Update admaticBidAdapter.js * Update admaticBidAdapter.js * Update admaticBidAdapter.js * Update admaticBidAdapter.js * admatic sync update * Update admaticBidAdapter.js * Revert "Update admaticBidAdapter.js" This reverts commit 11e053f0743f2df0b88bb2010f8c26b08653516a. * Revert "Update admaticBidAdapter.js" This reverts commit 11e053f0743f2df0b88bb2010f8c26b08653516a. * Update admaticBidAdapter.js * OMS Bid Adapter: add instl flag to imp in request (#14501) * Zeta SSP Analytics Adapter: pass floors. (#14350) * Zeta SSP Analytics Adapter: pass floors. * Zeta SSP Analytics Adapter: minor fix. * Zeta SSP Analytics Adapter: fix tests. * Revert "Various modules: remove legacy GPT targeting fallbacks (#14450)" (#14510) This reverts commit 299f20742da2c832cf3f0eaeceb9ee60a043c5a1. * Prebid 10.26.0 release * Increment version to 10.27.0-pre * DPAI bid adapter: initial release (#14434) * New adapter DPAI * New adapter DPAI * New adapter DPAI: Added end line * New adapter DPAI: Added end line --------- Co-authored-by: Patrick McCann * Core: remove stale transformBidParams references (#14512) * docs: adding documentation for mobianMpaaRating, mobianContentTaxonomy, mobianEsrbRating (#14513) * Bump minimatch (#14520) Bumps and [minimatch](https://github.com/isaacs/minimatch). These dependencies needed to be updated together. Updates `minimatch` from 3.1.2 to 3.1.4 - [Changelog](https://github.com/isaacs/minimatch/blob/main/changelog.md) - [Commits](https://github.com/isaacs/minimatch/compare/v3.1.2...v3.1.4) Updates `minimatch` from 9.0.5 to 9.0.7 - [Changelog](https://github.com/isaacs/minimatch/blob/main/changelog.md) - [Commits](https://github.com/isaacs/minimatch/compare/v3.1.2...v3.1.4) Updates `minimatch` from 5.1.6 to 5.1.8 - [Changelog](https://github.com/isaacs/minimatch/blob/main/changelog.md) - [Commits](https://github.com/isaacs/minimatch/compare/v3.1.2...v3.1.4) Updates `minimatch` from 9.0.4 to 9.0.7 - [Changelog](https://github.com/isaacs/minimatch/blob/main/changelog.md) - [Commits](https://github.com/isaacs/minimatch/compare/v3.1.2...v3.1.4) Updates `minimatch` from 10.0.3 to 10.2.3 - [Changelog](https://github.com/isaacs/minimatch/blob/main/changelog.md) - [Commits](https://github.com/isaacs/minimatch/compare/v3.1.2...v3.1.4) --- updated-dependencies: - dependency-name: minimatch dependency-version: 3.1.4 dependency-type: indirect - dependency-name: minimatch dependency-version: 9.0.7 dependency-type: indirect - dependency-name: minimatch dependency-version: 5.1.8 dependency-type: indirect - dependency-name: minimatch dependency-version: 9.0.7 dependency-type: indirect - dependency-name: minimatch dependency-version: 10.2.3 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump basic-ftp from 5.0.5 to 5.2.0 (#14522) Bumps [basic-ftp](https://github.com/patrickjuchli/basic-ftp) from 5.0.5 to 5.2.0. - [Release notes](https://github.com/patrickjuchli/basic-ftp/releases) - [Changelog](https://github.com/patrickjuchli/basic-ftp/blob/master/CHANGELOG.md) - [Commits](https://github.com/patrickjuchli/basic-ftp/compare/v5.0.5...v5.2.0) --- updated-dependencies: - dependency-name: basic-ftp dependency-version: 5.2.0 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Chrome AI RTD Provider: fix QuotaExceededError with large page content (#14295) * Chrome AI RTD Provider: fix QuotaExceededError with large page content Added MAX_TEXT_LENGTH constant (1000 chars) and text truncation logic in getPageText() to prevent QuotaExceededError when Chrome AI APIs process pages with large amounts of text content. Without this fix, pages with extensive text content can cause the Chrome AI APIs (LanguageDetector, Summarizer) to throw QuotaExceededError exceptions. * Chrome AI RTD Provider: add unit tests for MAX_TEXT_LENGTH and text truncation Adds tests covering: - MAX_TEXT_LENGTH constant exists and equals 1000 - getPageText returns null for text below MIN_TEXT_LENGTH - getPageText returns null for empty text - getPageText returns full text when between MIN and MAX length - getPageText does not truncate text at exactly MAX_TEXT_LENGTH - getPageText truncates text exceeding MAX_TEXT_LENGTH - getPageText logs a message when truncating Co-authored-by: Cursor --------- Co-authored-by: Paul Farrow Co-authored-by: Cursor * Neuwo Rtd Module: Version v2.0.0 and Quality of Life Improvements (#14323) * feat: add live display and access examples of Neuwo API data to example page * feat: add Prebid.js event timing tests to Neuwo RTD example page * style: standardize code style in Neuwo RTD example - Convert single quotes to double quotes for consistency - Format code with Prettier * docs: add guide for accessing Neuwo RTD data outside Prebid.js * feat: add concurrent bid request handling to Neuwo RTD Module * chore: standardize log message format in Neuwo RTD Module * feat: add product identifier to Neuwo API requests * docs: fix typo in test file name in Neuwo RTD Module documentation * style: format Neuwo RTD Module with Prettier - Add `// prettier-ignore` comment to preserve quoted keys in `IAB_CONTENT_TAXONOMY_MAP` - Apply Prettier formatting to *modules/neuwoRtdProvider.js* * test: add coverage for concurrent requests and product identifier in Neuwo RTD Module * feat: add per-tier IAB taxonomy filtering to Neuwo RTD Module Add optional filtering configuration for IAB Content and Audience taxonomies, allowing publishers to control the quantity and quality of categories injected into bid requests. - Add `iabTaxonomyFilters` parameter to module configuration for per-tier filtering - Implement `filterIabTaxonomyTier()` function to filter taxonomies by relevance threshold and limit count - Implement `filterIabTaxonomies()` function to apply filters across all tiers using tier key mapping - Add integration example checkbox to enable/disable filtering with hardcoded filter values * docs: add IAB taxonomy filtering documentation to Neuwo RTD Module * test: add error handling and edge case tests for Neuwo RTD Module - Add test for API URL construction when `neuwoApiUrl` contains existing query parameters - Add tests for error response handling: 404 errors, pending request cleanup, and retry behaviour - Add tests for concurrent requests with errors ensuring all callbacks are invoked - Add tests for JSON parsing errors in success callback - Add test for retry after JSON parsing error - Add test for missing `marketing_categories` field in API response - Add test for sorting items with undefined/null relevance values - Reorganise test structure: nest caching, URL stripping, and filtering describes under main `getBidRequestData` block - Add documentation for generating test coverage reports with viewing instructions * style: apply formatting to Neuwo RTD Module markdown documentation * refactor: remove name field from IAB segments in ORTB2 output - Update `buildIabData()` to only include `id` in segment objects - Remove `label` requirement; segments now only need valid `ID` - Update documentation examples to reflect simplified segment structure - Update unit tests to match new segment format * feat: migrate Neuwo RTD Module to new API architecture (v2.0.0) and maintain backward compatibility - Add version 2.0.0 with multi-version IAB Content Taxonomy support - Implement automatic API capability detection from endpoint URL format - Maintain backward compatibility with legacy GET endpoints - Consolidate default IAB Content Taxonomy version to "2.2" constant - Transform legacy API responses to unified segtax-based format - Add `buildIabFilterConfig()` for filter configuration conversion - Add `transformV1ResponseToV2()` and `transformSegmentsV1ToV2()` helpers - Refactor `buildIabData()` to dynamically process all tiers - Update `injectIabCategories()` to work with unified response format - Enhance JSDoc documentation with format examples and API details - Add conditional logging for POST request bodies - Optimise filtering: apply once before caching for legacy endpoints * test: update Neuwo RTD Module tests for new API architecture - Update all test mocks to use new segtax-based response format - Add tests for `buildIabFilterConfig()` function - Add tests for `injectIabCategories()` function - Add tests for `transformV1ResponseToV2()` function - Add tests for `transformSegmentsV1ToV2()` function - Update `buildIabData()` tests to use new tierData parameter structure - Add V1 API backward compatibility test suite with client-side filtering tests - Verify POST method and iabVersions query parameters for new API - Verify GET method and no iabVersions parameter for legacy V1 API - Update edge case tests for empty responses and error handling - Update integration tests for URL parameter handling with query strings - Remove hardcoded tier array constants (CONTENT_TIERS, AUDIENCE_TIERS) * docs: update Neuwo RTD Module documentation for new API version - Change default IAB Content Taxonomy version from "3.0" to "2.2" - Add IAB Content Taxonomy version "1.0" to supported values list - Add server-side filtering explanation to IAB Taxonomy Filtering section - Update Available Tiers table to focus on taxonomy types instead of API internals - Add recommended configuration comments for `auctionDelay` and `waitForIt` - Update all configuration examples to use "2.2" as default taxonomy version - Remove "How it works" section * feat: add OpenRTB 2.5 category fields support to Neuwo RTD Module Add support for populating OpenRTB 2.5 category fields (`site.cat`, `site.sectioncat`, `site.pagecat`, `site.content.cat`) with IAB Content Taxonomy 1.0 segments. Changes: - Add `enableOrtb25Fields` configuration parameter (default: true) - Add `extractCategoryIds()` helper function to extract segment IDs from tier data - Refactor `buildIabData()` to use `extractCategoryIds()` for code reuse - Extend `buildIabFilterConfig()` to apply filters to IAB 1.0 when feature enabled - Modify `getBidRequestData()` to request IAB 1.0 data (segtax 1) when feature enabled - Add warning when feature enabled with legacy API endpoint - Extend `injectIabCategories()` to populate four OpenRTB 2.5 category fields - Update version to 2.1.0 * ci: add OpenRTB 2.5 category fields support to integration example Changes: - Add checkbox UI control for `enableOrtb25Fields` option (default: checked) - Add display section for OpenRTB 2.5 category fields data - Extract and display `site.cat`, `site.sectioncat`, `site.pagecat`, and `site.content.cat` fields - Update `bidRequested` event handler to capture category fields - Add localStorage persistence for `enableOrtb25Fields` setting - Pass `enableOrtb25Fields` parameter to module configuration * test: add unit tests for OpenRTB 2.5 category fields Changes: - Add 11 new `buildIabFilterConfig()` tests for OpenRTB 2.5 feature (enabled/disabled scenarios) - Add 11 new `extractCategoryIds()` tests covering all edge cases - Add 11 new `injectIabCategories()` tests for category field injection - Update 5 existing `buildIabFilterConfig()` tests - Add 6 new `getBidRequestData()` integration tests for V2 API with feature enabled/disabled - Move legacy API compatibility tests from V2 section to V1 section * docs: add OpenRTB 2.5 category fields support documentation to Neuwo RTD Module Changes: - Add OpenRTB 2.5 feature description to module overview - Add `enableOrtb25Fields` parameter to parameters table - Add dedicated "OpenRTB 2.5 Category Fields" section with examples - Update ORTB2 data structure examples to show category fields - Add filtering section explaining IAB 1.0 filter application - Update "Accessing Neuwo Data" section with category field extraction example - Add "Building for Production" section with build command - Update segtax value in example (7 → 6) - Update version to 2.1.0 * perf: change Content-Type to text/plain to avoid CORS preflight * refactor: change /v1/iab endpoint from POST to GET with flattened query params Replace POST request with GET and send IAB taxonomy filters as flattened URL query parameters instead of JSON body to avoid CORS preflight requests. Changes: - Replace `buildIabFilterConfig()` with `buildFilterQueryParams()` function - Change output from nested object to array of query parameter strings (e.g., `["filter_6_1_limit=3"]`) - Remove POST method and request body from ajax call - Add filter parameters directly to URL for /v1/iab endpoint - Maintain OpenRTB 2.5 filter application (ContentTier1/2 → segtax 1) - Update all unit tests to test `buildFilterQueryParams()` instead of `buildIabFilterConfig()` - Update tests expecting POST requests to expect GET requests - Update tests checking request body to check URL parameters - Update version to 2.2.0 * feat: key cache by full API URL to support config changes between auctions - Replace global `cachedResponses` and `pendingRequests` singletons with objects keyed by `neuwoApiUrlFull` - Concurrent or sequential calls with different parameters (URL, taxonomy version, filters) maintain separate cache entries - Add LRU-style eviction capped at 10 entries to prevent unbounded cache growth - Log module version during `init()` - Add unit tests for cache isolation * fix: guard empty URLs, inject content/audience independently, allow `limit: 0` to suppress tiers - Separate JSON parsing from response processing into distinct try/catch blocks - Add `Array.isArray` guard to `extractCategoryIds` - Only cache valid object responses so failed requests can be retried - Rename `isV2Api` to `isIabEndpoint` for clarity - Fix typo in log message and JSDoc (POST -> GET) - Bump to 2.2.1 * fix: isolate legacy endpoint cache keys by taxonomy version and filters * fix: prevent duplicate filter query params when IAB Content Taxonomy is 1.0 * fix: only sort and filter taxonomy tiers when filter is configured * fix: detect /v1/iab endpoint from URL pathname instead of full string * fix: prevent duplicate `iabVersions=1` query param when content taxonomy is 1.0 --------- Co-authored-by: grzgm <125459798+grzgm@users.noreply.github.com> Co-authored-by: gregneuwo <226034698+gregneuwo@users.noreply.github.com> * aceexBidAdapter: initial release (#14352) * aceexBidAdapter/setup * aceexBidAdapter/setup and fixes * aceexBidAdapter/setup merge * prices logic fix * prices logic fix * prices logic fix * prices logic fix * prices logic fix * prices logic fix * review smallfixes --------- Co-authored-by: vrishko * Mediasquare bid adapter: manage burl object (#14357) * manage burl object * fix lint * burl to burls * stringify only when method post * Insticator Bid Adapter: Add support for 2.6rtb request/response (#14373) * enhance ORTB 2.6 support and enhance bid response handling * remove onBidWon * add tests for handling 204 No Content responses in InsticatorBidAdapter * update InsticatorBidAdapter and improve TTL handling * udpate media type detection in InsticatorBidAdapter to support ORTB 2.6 standards * remove adpod fields references * Conceptx bid adapter: Update request destination (#14420) * New adapter: concepx * Syntax change * Revert syntax change * Defensive check for response from bidder server * Add better validation for the request * Merge branch 'master' of https://github.com/prebid/Prebid.js * Don't append url on every buildrequest * Add gvlId to conceptX * Change conceptx adapter, to directly request our PBS * Add empty getUserSync, as the syncing will be delegated (runPbsCookieSync) --------- Co-authored-by: Patrick McCann * Refactor TTL usage in ttdBidAdapter (#14517) * Shaping rules: Make some TypeScript fields optional (#14514) * Make TypeScript fields optional * Fix condition name * OMS Adapter: extract shared OMS/Onomagic helper utilities (#14508) * OMS Adapter: extract shared OMS/Onomagic helper utilities * OMS Adapter: extract shared banner size/viewability processing * OMS Adapter: isolate viewability helpers to avoid eager side effects * bidResponseFilter: cattax handling (#14428) * bidResponseFilter: cattax handling * default meta value * Normalize cattax values for accurate comparison Normalize cattax values before comparison to ensure accurate matching. --------- Co-authored-by: Patrick McCann * new adapter - Apester; remove alias from limelightDigital (#14516) Co-authored-by: Anna Yablonsky * New adapter - Adnimation (#14502) * adding new adapter for Admination * remove alias for adnimation form limelightDigital following new partnership --------- Co-authored-by: Anna Yablonsky * TeqBlaze Bidder Utils: fix uspConsent string handling in getUserSyncs (#14515) * TeqBlazeSalesAgent Bid Adapter: initial release * update doc * fix for uspConsent string in getUserSyncs * fix test --------- Co-authored-by: Patrick McCann * GumGum Adapter: migrate identity extraction to EIDs (#14511) * ADJS-1646-update-userId-handling * added test: should filter pubProvidedId entries by allowed sources * support ortb2.user.ext.eids fallback and add identity parity tests * Prioritize prebid.js 10 structure * GumGum Adapter: fix TDID extraction across all EID uids and add edge-case tests * mediafuseBidAdapter - Updates and Refactor (#14469) * mediafuseBidAdapter Updates and Refactor * Addressed Flagged Issues and removed ADPOD import. * More Fixes * bug fixes, code quality improvements, and expanded test coverage * Updates and Fixes * Flagged Issues Fixes * Revert "mediafuseBidAdapter - Updates and Refactor (#14469)" (#14529) This reverts commit 192f96a8c3f42a24a23930b69d36b3791a76ab1e. * Performax adapter: Add user sync and reporting URLs (#14429) * Add user sync and reporting urls * add tests, minor refactor * add window.addEventListener only once * fix JSON.parse can return null * Fix unconditional setting user.ext.uids * Add test * swap uids from storage and original user.ext.uids * Add keepalive and log only when debug is turned on --------- Co-authored-by: Michal Kváček * Revert "Performax adapter: Add user sync and reporting URLs (#14429)" (#14532) This reverts commit d8fb4350befed2c368da2f0f10260245d7b94a73. * Bump browserstack-local from 1.5.5 to 1.5.11 (#14533) Bumps [browserstack-local](https://github.com/browserstack/browserstack-local-nodejs) from 1.5.5 to 1.5.11. - [Release notes](https://github.com/browserstack/browserstack-local-nodejs/releases) - [Commits](https://github.com/browserstack/browserstack-local-nodejs/compare/v1.5.5...v1.5.11) --- updated-dependencies: - dependency-name: browserstack-local dependency-version: 1.5.11 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump fast-xml-parser from 5.3.6 to 5.4.1 (#14534) Bumps [fast-xml-parser](https://github.com/NaturalIntelligence/fast-xml-parser) from 5.3.6 to 5.4.1. - [Release notes](https://github.com/NaturalIntelligence/fast-xml-parser/releases) - [Changelog](https://github.com/NaturalIntelligence/fast-xml-parser/blob/master/CHANGELOG.md) - [Commits](https://github.com/NaturalIntelligence/fast-xml-parser/compare/v5.3.6...v5.4.1) --- updated-dependencies: - dependency-name: fast-xml-parser dependency-version: 5.4.1 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * pubstackBidAdapter: initial release (#14490) * feat: add pubstackBidAdapter (#1) * feat: add SparkBidAdapter * fix: reviews * fix: use iframe for user_sync * fix: change adapter name * fix: add test file * feat: add viewport value in imp[].ext.prebid.bidder.pubstack.vpl * fix: remove unused context * Pubstack Adapter: update utils and align adapter tests * Pubstack Bid Adapter: apply lint-driven TypeScript cleanups --------- Co-authored-by: gpolaert * Update modules/pubstackBidAdapter.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update modules/pubstackBidAdapter.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update libraries/pubstackUtils/index.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update modules/pubstackBidAdapter.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Pubstack Adapter: use placementPositionInfo telemetry --------- Co-authored-by: Stéphane Deluce Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Bump actions/upload-artifact from 6 to 7 (#14539) Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 6 to 7. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v6...v7) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-version: '7' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump actions/download-artifact from 7 to 8 (#14540) Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 7 to 8. - [Release notes](https://github.com/actions/download-artifact/releases) - [Commits](https://github.com/actions/download-artifact/compare/v7...v8) --- updated-dependencies: - dependency-name: actions/download-artifact dependency-version: '8' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Prebid 10.27.0 release * Increment version to 10.28.0-pre * MediaGo Bid Adapter: sends transactionId and Prebid.js version in the request payload, and optimizes deprecated navigator.platform (#14538) * notify server if the page is secure * remove undefined initial * MediaGo Bid Adapter: update adapter, test page and spec Made-with: Cursor * MediaGo: remove redundant debug-info, use Network tab for verification; remove Chinese comments Made-with: Cursor --------- Co-authored-by: fangsimin@baidu.com Co-authored-by: 方思敏 * Screencore Bid Adapter: fix region routing and endpoint path (#14544) * Screencore prebid adapter * rearrange code * use lowercase screncore bidder code * fix tests * update tests * trigger CI * Screencore Bid Adapter: add endpointId parameter * Updated adapter to use teqblazeUtils library * Added endpointId parameter support in test parameters * Updated test specs to include endpointId validation * Screencore Bid Adapter: update sync URL to base domain Update SYNC_URL constant to use base domain. The getUserSyncs function from teqblazeUtils will append the appropriate path. * Screencore Bid Adapter: migrate to teqblazeUtils library - Update imports to use buildRequestsBase, interpretResponse, getUserSyncs, isBidRequestValid, and buildPlacementProcessingFunction from teqblazeUtils - Remove storage manager dependency (no longer needed) - Update isBidRequestValid to use placementId/endpointId params validation - Refactor buildRequests to use buildRequestsBase pattern - Rewrite test suite to match teqblazeUtils API: - Simplify test data structures - Update server response format (body as array) - Add tests for placementId/endpointId validation - Update getUserSyncs URL format expectations * fix(screencore): correct region routing and endpoint path - add US/ and Canada/ timezone prefixes to getRegionSubdomainSuffix() - fix endpoint path from /prebid to /pbjs - move AD_URL inside buildRequests to compute per request --------- Co-authored-by: Kostiantyn Karchevsky Co-authored-by: Demetrio Girardi Co-authored-by: Patrick McCann Co-authored-by: Pavlo Samonin * GumGum Prebid Adapter: Send App / Site Content Data to GG Ad Exchange (#14535) * Gumgum - ADTS-175 Support multiple GG params * ADJS-1165-prebid-adaptor-changes-to-support-jp-products * made tweaks to the skin product for the gumgumBidAdapter * added test for new product id * updated skins parameter for consistency * updated test for updated skins parameter * ADJS-1271-send-envelope-param-for-lexicon * ADJS-1646-update-userId-handling * added test: should filter pubProvidedId entries by allowed sources * support ortb2.user.ext.eids fallback and add identity parity tests * ADTS-616-send-app-site-content-object-from-prebid-to-ad-server * Prioritize prebid.js 10 structure * GumGum Adapter: fix TDID extraction across all EID uids and add edge-case tests * added itype parameter * updated access to channel and network objects --------- Co-authored-by: Lisa Benmore Co-authored-by: John Bauzon Co-authored-by: ahzgg Co-authored-by: ahzgg <163184035+ahzgg@users.noreply.github.com> * MSFT Bid Adapter - update note in readme (#14552) * new alias embimedia (#14560) Co-authored-by: mderevyanko * Also derive and include a us_privacy string from the gpp info when asking Prebid for a GAM url (#14557) * Linting: remove spacing exceptions (#14531) * spacings * fix mediafuse * Format import statement for consistency * Update pubstackBidAdapter.ts * GumGum Bid Adapter: Send additional device params to GG ad server (#14566) * Gumgum - ADTS-175 Support multiple GG params * ADJS-1165-prebid-adaptor-changes-to-support-jp-products * made tweaks to the skin product for the gumgumBidAdapter * added test for new product id * updated skins parameter for consistency * updated test for updated skins parameter * ADJS-1271-send-envelope-param-for-lexicon * ADJS-1646-update-userId-handling * added test: should filter pubProvidedId entries by allowed sources * support ortb2.user.ext.eids fallback and add identity parity tests * ADTS-616-send-app-site-content-object-from-prebid-to-ad-server * Prioritize prebid.js 10 structure * GumGum Adapter: fix TDID extraction across all EID uids and add edge-case tests * added itype parameter * updated access to channel and network objects * AT-10850-send-device-parameters-from-prebid-to-hbid-endpoint * addressed copilot comments --------- Co-authored-by: Lisa Benmore Co-authored-by: John Ivan Bauzon Co-authored-by: ahzgg Co-authored-by: ahzgg <163184035+ahzgg@users.noreply.github.com> * wurfl rtd: add none_lce enrichment type for A/B test control group (#14527) * feat(wurfl-rtd): add none_lce enrichment type for A/B test control group Distinguish control-group beacons where LCE would have been used (no cache) from those where WURFL cache was present. - Add NONE_LCE enrichment type: control + no cache → 'none_lce' - Remove abExcludeLCE parameter - Simplify ABTestManager by removing LCE exclusion logic * fix(wurfl-rtd): read cache metadata in control group beacon Control group early return skipped reading wurfl_id, sampling_rate, tier and over_quota from cache, sending empty/default values in the beacon. * fix lint (#14570) * MediaEyes Bid Adapter : Support Video Type (#14565) * MediaEyes Bid Adapter : Update Support Video Type * fix eslint * modifications as suggested * improve code as suggested * Core: fix error handling when loading debugging-standalone (#14554) * Core: fix error handling when loading debugging-standalone * lint * run error callback once * lint * IntentIQ ID Module & Analytics Adapter: Fix deprecated GAM module methods (#14553) * AGT-734: Support region for prebid modules (merge 0_3_4 with master) * AGT-730: move spd to partnerData (merge 0_3_4 to master) * AGT-765: Send ad size and pos in impression reporting module (#58) * AGT-765: pos and size * AGT-765: Tests for position resolving * AGT-765: Test fix * AGT-756: Missed vrref in payload fix (#56) * AGT-756: vrref in payload fix * remove comment * AGT-756: Fix vrref bug * AGT-756: Remove comments * AGT-756: Test for vrref * update requestRtt to show more clear time (#59) * AGT-739: Change time to call server (#57) * fix typo, remove parameter duplication (#60) * fix typo, remove parameter duplication * update doc examples * AGT-721: Documentation for region, size, pos (#61) * fix region parameter in table (#62) * update tests * remove unused test * 0.35 master (#65) * Prebid 10.25.0 release * Increment version to 10.26.0-pre * limelight: Send open rtb bid requests from our prebid js adapter (#14397) * changed requests format to OpenRTB. * Added more tests * fix refererInfo.page fallback for site.page * fix built imp for size-only * fix built imp for size-only * OMS Bid Adapter: add prebid js version to request payload (#14421) * Bump qs from 6.14.1 to 6.14.2 (#14478) Bumps [qs](https://github.com/ljharb/qs) from 6.14.1 to 6.14.2. - [Changelog](https://github.com/ljharb/qs/blob/main/CHANGELOG.md) - [Commits](https://github.com/ljharb/qs/compare/v6.14.1...v6.14.2) --- updated-dependencies: - dependency-name: qs dependency-version: 6.14.2 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump fast-xml-parser from 5.3.4 to 5.3.6 (#14482) Bumps [fast-xml-parser](https://github.com/NaturalIntelligence/fast-xml-parser) from 5.3.4 to 5.3.6. - [Release notes](https://github.com/NaturalIntelligence/fast-xml-parser/releases) - [Changelog](https://github.com/NaturalIntelligence/fast-xml-parser/blob/master/CHANGELOG.md) - [Commits](https://github.com/NaturalIntelligence/fast-xml-parser/compare/v5.3.4...v5.3.6) --- updated-dependencies: - dependency-name: fast-xml-parser dependency-version: 5.3.6 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Floor module: add enforcement allowlist config option (#14455) * Core: add bidder-scoped floor enforcement * Core: enforce floors allowlist against adapterCode * Various modules: remove legacy GPT targeting fallbacks (#14450) * Core: remove legacy GPT targeting fallbacks * Reconciliation RTD Provider: handle missing RSDK_ADID targeting * Core: fix targetingLock GPT test mock for getConfig * TargetVideo bid adapter: send price floor param (#14406) * TargetVideo bid adapter: send price floor param * Add support for the price floors module * Add imports * Fix getBidFloor function floor params --------- Co-authored-by: dnrstc * New adapter: Verben (#14494) Co-authored-by: Verben Co-authored-by: Patrick McCann * Teal bid adapter: include native and video media types (#14493) * Proxistore Bid Adapter: migration to OpenRTB (#14411) * Update Proxistore endpoint URLs in adapter and tests Updated the Proxistore `COOKIE_BASE_URL` and `COOKIE_LESS_URL` to the new `abs` domain in both the adapter and its corresponding test file. This ensures consistency with the updated API endpoints. * Integrate OpenRTB converter for Proxistore bid adapter, and add OpenRTB request as a separate field. * Refactor Proxistore bid adapter to improve OpenRTB handling, add GDPR-specific URL selection, and enhance test coverage. * Add support for website and language parameters in Proxistore bid adapter requests, with corresponding test coverage. * Handle empty response body in Proxistore bid adapter to avoid runtime errors. --------- Co-authored-by: Anthony Richir * Floxis Bid Adapter: initial release (#13934) * Floxis Bid Adapter : initial release * Added ORTB parameters for blocking * Adjusted documentation with maintainer info * Added more validations, extracted converter to a const * Floxis Bid Adapter: redesign to seat-based architecture with ortbConverter Major rewrite replacing teqblazeUtils with ortbConverter for ORTB 2.x compliance. Changes: - New params: seat (required), region (required), partner (required) - Endpoint URL: https://{subdomain}.floxis.tech/pbjs?seat={seat} - subdomain = region for 'floxis' partner - subdomain = {partner}-{region} for white-label partners - ORTB-native implementation with Floors Module support - 40 comprehensive tests with full code coverage - Updated documentation with examples Addresses all PR #13934 review comments from @osazos * Rename FloxisBidAdapter.md to floxisBidAdapter.md * Code review adjustments --------- Co-authored-by: Patrick McCann * InsurAds Bid Adapter: Initial Implementation (#14470) * InsurAds Bid Adapter Implementation * Remove test alias * Storage Settings Example * Adapter rename 1/2 * Adapter rename 2/2 * Update modules/insuradsBidAdapter.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update modules/insuradsBidAdapter.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update test/spec/modules/insuradsBidAdapter_spec.js Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * The params need to be registered under nexx360 --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * LeagueM BId Adapter: initial release (#14479) * LeagueM BId Adapter: initial release * Update test/spec/modules/leagueMBidAdapter_spec.js Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update test/spec/modules/leagueMBidAdapter_spec.js Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update test/spec/modules/leagueMBidAdapter_spec.js Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update modules/leagueMBidAdapter.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Patrick McCann * New Adapter: Harion (#14398) * new adapter harion * refactor(adapter): guard added to interpretResponse * Core: disabling fingerprinting apis (#14404) * Core: disabling fingerprinting apis * getFallbackWindow -> utils * Fix several typos in comments and tests (#14498) * Bump ajv from 6.12.6 to 6.14.0 (#14499) Bumps [ajv](https://github.com/ajv-validator/ajv) from 6.12.6 to 6.14.0. - [Release notes](https://github.com/ajv-validator/ajv/releases) - [Commits](https://github.com/ajv-validator/ajv/compare/v6.12.6...v6.14.0) --- updated-dependencies: - dependency-name: ajv dependency-version: 6.14.0 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Setting alwaysHasCapacity flag to true (#14500) * Adcluster Bid Adapter: Support Adcluster (#14050) * adcluster - new adapter * fixes * video preview id change * video preview id change * Update adclusterBidAdapter_spec.js * fallback * multiformat fix --------- Co-authored-by: Patrick McCann Co-authored-by: Patrick McCann * ReVantage Bid Adapter: initial release (#14180) * Create revantageBidAdapter.js * Create revantageBidAdapter.md * Update revantageBidAdapter.js * Update revantageBidAdapter.js * Update revantageBidAdapter.js * Update revantageBidAdapter.js Fixed trailing slash Error on Line 123 * Create revantageBidAdapter_spec.js * Update revantageBidAdapter_spec.js Fixed Trailing slashes (again) * Update revantageBidAdapter_spec.js * Update revantageBidAdapter_spec.js same thing again * Refactor Revantage Bid Adapter for media types and bids Refactor Revantage Bid Adapter to use media type constants and improve bid response handling. * Refactor RevantageBidAdapter tests for GPP consent * Update modules/revantageBidAdapter.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update modules/revantageBidAdapter.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Validate feedId consistency in batch bid requests Added validation to ensure all bid requests in a batch have the same feedId, logging a warning if they do not. * Add test for rejecting batch with different feedIds * Update syncOptions for image sync URL parameters * Update sync URL to use 'tag=img' instead of 'type=img' * Update print statement from 'Hello' to 'Goodbye' * fixed * Enhance video bid handling and add utility functions Added functions to handle video size extraction and VAST detection. --------- Co-authored-by: Patrick McCann Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * AdMatic Bid Adapter : add adrubi alias (#14504) * Admatic Bidder Adaptor * Update admaticBidAdapter.md * Update admaticBidAdapter.md * remove floor parameter * Update admaticBidAdapter.js * Admatic Bid Adapter: alias and bid floor features activated * Admatic adapter: host param control changed * Alias name changed. * Revert "Admatic adapter: host param control changed" This reverts commit de7ac85981b1ba3ad8c5d1dc95c5dadbdf5b9895. * added alias feature and host param * Revert "added alias feature and host param" This reverts commit 6ec8f4539ea6be403a0d7e08dad5c7a5228f28a1. * Revert "Alias name changed." This reverts commit 661c54f9b2397e8f25c257144d73161e13466281. * Revert "Admatic Bid Adapter: alias and bid floor features activated" This reverts commit 7a2e0e29c49e2f876b68aafe886b336fe2fe6fcb. * Revert "Update admaticBidAdapter.js" This reverts commit 7a845b7151bbb08addfb58ea9bd5b44167cc8a4e. * Revert "remove floor parameter" This reverts commit 7a23b055ccd4ea23d23e73248e82b21bc6f69d90. * Admatic adapter: host param control && Add new Bidder * Revert "Admatic adapter: host param control && Add new Bidder" This reverts commit 3c797b120c8e0fe2b851381300ac5c4b1f92c6e2. * commit new features * Update admaticBidAdapter.js * updated for coverage * sync updated * Update adloader.js * AdMatic Bidder: development of user sync url * Update admaticBidAdapter.js * Set currency for AdserverCurrency: bug fix * Update admaticBidAdapter.js * update * admatic adapter video params update * Update admaticBidAdapter.js * update * Update admaticBidAdapter.js * update * update * Update admaticBidAdapter_spec.js * Update admaticBidAdapter.js * Update admaticBidAdapter.js * Revert "Update admaticBidAdapter.js" This reverts commit 1216892fe55e5ab24dda8e045ea007ee6bb40ff8. * Revert "Update admaticBidAdapter.js" This reverts commit b1929ece33bb4040a3bcd6b9332b50335356829c. * Revert "Update admaticBidAdapter_spec.js" This reverts commit 1ca659798b0c9b912634b1673e15e54e547b81e7. * Revert "update" This reverts commit 689ce9d21e08c27be49adb35c5fd5205aef5c35c. * Revert "update" This reverts commit f381a453f9389bebd58dcfa719e9ec17f939f338. * Revert "Update admaticBidAdapter.js" This reverts commit 38fd7abec701d8a4750f9e95eaeb40fb67e9f0e6. * Revert "update" This reverts commit a5316e74b612a5b2cd16cf42586334321fc87770. * Revert "Update admaticBidAdapter.js" This reverts commit 60a28cae302b711366dab0bff9f49b11862fb8ee. * Revert "admatic adapter video params update" This reverts commit 31e69e88fd9355e143f736754ac2e47fe49b65b6. * update * Update admaticBidAdapter.js * Update admaticBidAdapter_spec.js * mime_type add * add native adapter * AdMatic Adapter: Consent Management * added gvlid * Update admaticBidAdapter.js * admatic cur update * Update admaticBidAdapter.js * Update admaticBidAdapter.js * Update admaticBidAdapter.js * Update admaticBidAdapter.js * Update admaticBidAdapter.js * Update admaticBidAdapter.js * Update admaticBidAdapter.js * admatic sync update * Update admaticBidAdapter.js * Revert "Update admaticBidAdapter.js" This reverts commit 11e053f0743f2df0b88bb2010f8c26b08653516a. * Revert "Update admaticBidAdapter.js" This reverts commit 11e053f0743f2df0b88bb2010f8c26b08653516a. * Update admaticBidAdapter.js * OMS Bid Adapter: add instl flag to imp in request (#14501) * Zeta SSP Analytics Adapter: pass floors. (#14350) * Zeta SSP Analytics Adapter: pass floors. * Zeta SSP Analytics Adapter: minor fix. * Zeta SSP Analytics Adapter: fix tests. * Revert "Various modules: remove legacy GPT targeting fallbacks (#14450)" (#14510) This reverts commit 299f20742da2c832cf3f0eaeceb9ee60a043c5a1. * Prebid 10.26.0 release * Increment version to 10.27.0-pre * DPAI bid adapter: initial release (#14434) * New adapter DPAI * New adapter DPAI * New adapter DPAI: Added end line * New adapter DPAI: Added end line --------- Co-authored-by: Patrick McCann * Core: remove stale transformBidParams references (#14512) --------- Signed-off-by: dependabot[bot] Co-authored-by: Prebid.js automated release Co-authored-by: RuzannaAvetisyan <44729750+RuzannaAvetisyan@users.noreply.github.com> Co-authored-by: Siminko Vlad <85431371+siminkovladyslav@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Patrick McCann Co-authored-by: danijel-ristic <168181386+danijel-ristic@users.noreply.github.com> Co-authored-by: dnrstc Co-authored-by: verben-gh Co-authored-by: Verben Co-authored-by: johnclc Co-authored-by: Anthony Co-authored-by: Anthony Richir Co-authored-by: floxis-tech Co-authored-by: Patrick McCann Co-authored-by: Jose Climaco Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: morock <98119227+mo4rock@users.noreply.github.com> Co-authored-by: markappmedia Co-authored-by: mkomorski Co-authored-by: Vedant Madane <6527493+VedantMadane@users.noreply.github.com> Co-authored-by: pm-priyanka-deshmane <107103300+pm-priyanka-deshmane@users.noreply.github.com> Co-authored-by: adclusterdev Co-authored-by: v0idxyz <58184010+v0idxyz@users.noreply.github.com> Co-authored-by: Fatih Kaya Co-authored-by: abermanov-zeta <95416296+abermanov-zeta@users.noreply.github.com> Co-authored-by: Demetrio Girardi Co-authored-by: driftpixelai <166716541+driftpixelai@users.noreply.github.com> * AGT-790: Fix additional parameters (#63) * AGT-790: Fix additional parameters * Fix analytics doc * AGT-787: partnerAuctionId to analytical module (#68) * AGT-787: partnerAuctionId to analytical module * AGT-787: Some fix and tests * AGT-803-Deprecated-GAM-methods-module (#67) * AGT-803-Deprecated-GAM-methods-module * AGT-803-Deprecated-GAM-methods-module * AGT-803-Deprecated-GAM-methods-module * AGT-803-Deprecated-GAM-methods-module --------- Co-authored-by: Oleksii Petrychenko * update documentation * Merge branch '0.35-master' into release_0.35 * fix: apply URL length guard after payload append for GET reports --------- Signed-off-by: dependabot[bot] Co-authored-by: DimaIntentIQ <139111483+DimaIntentIQ@users.noreply.github.com> Co-authored-by: DimaIntentIQ Co-authored-by: Prebid.js automated release Co-authored-by: RuzannaAvetisyan <44729750+RuzannaAvetisyan@users.noreply.github.com> Co-authored-by: Siminko Vlad <85431371+siminkovladyslav@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Patrick McCann Co-authored-by: danijel-ristic <168181386+danijel-ristic@users.noreply.github.com> Co-authored-by: dnrstc Co-authored-by: verben-gh Co-authored-by: Verben Co-authored-by: johnclc Co-authored-by: Anthony Co-authored-by: Anthony Richir Co-authored-by: floxis-tech Co-authored-by: Patrick McCann Co-authored-by: Jose Climaco Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: morock <98119227+mo4rock@users.noreply.github.com> Co-authored-by: markappmedia Co-authored-by: mkomorski Co-authored-by: Vedant Madane <6527493+VedantMadane@users.noreply.github.com> Co-authored-by: pm-priyanka-deshmane <107103300+pm-priyanka-deshmane@users.noreply.github.com> Co-authored-by: adclusterdev Co-authored-by: v0idxyz <58184010+v0idxyz@users.noreply.github.com> Co-authored-by: Fatih Kaya Co-authored-by: abermanov-zeta <95416296+abermanov-zeta@users.noreply.github.com> Co-authored-by: Demetrio Girardi Co-authored-by: driftpixelai <166716541+driftpixelai@users.noreply.github.com> Co-authored-by: oleksii-intent Co-authored-by: Oleksii Petrychenko * Seedtag Adapter: change request params (#14521) * feat: change request params * Refactor seedtagBidAdapter: optimize query parameter construction in getTimeoutUrl function. * fix test --------- Co-authored-by: Yohan Boutin * Mobian RTD Provider: use URL based cache (#14481) * Mobian RTD Provider: use URL based cache * Apply suggestion from @Copilot Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * limit cache size * Update test/spec/modules/mobianRtdProvider_spec.js Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * sanitize cache size input * add missing import * use replaceState in tests * prevent duplicate calls * Update modules/mobianRtdProvider.js Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update test/spec/modules/mobianRtdProvider_spec.js Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * fix tests * Update modules/mobianRtdProvider.js Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Patrick McCann Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Patrick McCann * ConnatixBidAdapter: ADVS-5365 - Bugfix: Calculated viewability is always 0 (#14575) * uses existing viewability utils * updates tests * leaves comments * adds example * adds example page * supports both div id and element query * adds instructions * paragraphs * fallsback to query by id * improves dom querying * try catch around query selector * linter err * PubmaticBidAdapter: Adding support for privacyLink (#14491) * Adding support for privacyLink * Adding test cases * Test cases updated * Update pubmaticBidAdapter_spec.js --------- Co-authored-by: Patrick McCann Co-authored-by: Patrick McCann * APS Bid Adapter v2.1.0 (#14567) **Overview** ------------ Add support for APS age range parameter **Changes** ----------- - (feat) Add age range param to APS config * wurfl rtd: add UACH data to beacon payload in onAuctionEndEvent (#14572) * feat: add UACH data to beacon payload in onAuctionEndEvent Collect User-Agent Client Hints once in loadWurflJsAsync via a shared promise (uachPromise) and cache the resolved value in resolvedUACH. The beacon payload in onAuctionEndEvent reads the cached value synchronously, keeping the function fully synchronous. * chore: bump version to 2.7.0 * fix: collect UACH in getBidRequestData * refactor(wurfl-rtd): use Prebid SUA instead of direct UACH collection Replace navigator.userAgentData.getHighEntropyValues() with reading ORTB SUA from reqBidsConfigObj.ortb2Fragments.global.device.sua, letting publishers control which hints are collected via Prebid's enrichment pipeline. * mediafuseBidAdapter - Updates, Refactor and Fixes. (#14537) * MediaFuse Adapter: Fixed Test Issues * Fixed Lint Issue * fix video vastUrl not set on openrtb2 endpoint * Fixed lint errors --------- Co-authored-by: Patrick McCann * Revert "mediafuseBidAdapter - Updates, Refactor and Fixes. (#14537)" (#14580) This reverts commit 0d34a7c056991bf696a04bbb4a345ea0da5f84f9. * Mediafuse adapter update: resubmit refactor (#14581) * MediaFuse Adapter: Fixed Test Issues * Fixed Lint Issue * fix video vastUrl not set on openrtb2 endpoint * Fixed lint errors * Fixed PR Flagged Issues --------- Co-authored-by: unknown * Build system: separate `build-release` from `prepare-release` (#14578) * Core: fix error handling when loading debugging-standalone * lint * run error callback once * Separate build-release from prepare-release * adloox analytics: do not disable if not enabled (#14585) * Magnite Bid Adapter: New ORTB Adapter for magnite (rubicon) (#14476) * New Magnite Bid Adapter ORTB Converter * Update modules/magniteBidAdapter.js Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * codex suggestions * move outstream to shared util * fix tests * Update libraries/magniteUtils/outstream.js Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * revoke blob after ttl * translate adm_native if needed * remove transform bid params * set alwaysHasCapacity * fix lint --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Patrick McCann * Prebid 10.28.0 release * Increment version to 10.28.1-pre * Core: bid targeting exclusion (#14453) * Core: bid targeting exclusion * passing filteredBids to exclusion function * Add disableFingerprintingApis option in config Added option to disable specific fingerprinting APIs. * Update config.ts * handling exceptions * Update bidTargetingExclusion documentation Clarify the description of bidTargetingExclusion function. --------- Co-authored-by: Demetrio Girardi Co-authored-by: Patrick McCann * Document common adapter types and references (#14577) Added guidelines for common adapter types and type references. * adds badv and bcat support for deepintent bid adapter (#14528) * Performax adapter: Add user sync and reporting URLs (#14547) * Add user sync and reporting urls * add tests, minor refactor * add window.addEventListener only once * fix JSON.parse can return null * Fix unconditional setting user.ext.uids * Add test * swap uids from storage and original user.ext.uids * Add keepalive and log only when debug is turned on * add stub * Fixed codex issues with json parsing * Fix merge * Fix linter --------- Co-authored-by: Michal Kváček Co-authored-by: Martin Mach * Optout Bid Adapter: migrate endpoints and batch requests with custom params (#14282) * updated optout adapter to include single request, custom params * Update modules/optoutBidAdapter.js Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update modules/optoutBidAdapter.js Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update modules/optoutBidAdapter.js Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * changes for copilot review * more fixes for copilot review * Update modules/optoutBidAdapter.js Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update modules/optoutBidAdapter.js Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update modules/optoutBidAdapter.js Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update modules/optoutBidAdapter.js Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update modules/optoutBidAdapter.js Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update modules/optoutBidAdapter.js Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update modules/optoutBidAdapter.js Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * add more test coverage and improvements to adapter * fix errors * remove duplicate code/refactored code * fixed test case with invalid url * fixed test case with invalid url attempt 2 * remove invalid url check altogether * remove trailing spaces * remove trailing spaces * Update modules/optoutBidAdapter.js Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update test/spec/modules/optoutBidAdapter_spec.js Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update test/spec/modules/optoutBidAdapter_spec.js Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Taboola: support native (#14486) * add deferredBilling support using onBidBillable * update burl setting * support nurl firing logic * add extra signals to taboola request * add extra ad signals * fix missing semicolon * use Prebid's built-in counters * updated detectBot logic * In Taboola adapter, added support for native and adjusted the existing banner support. Added and updated tests. * In Taboola adapter, added support for native and adjusted the existing banner support. Added and updated tests. * removed test page pushed accidentally * Wrapped native tests with NATIVE feature check * updated media type checks * added missing tab * removed tab * support multiformat * updated media type resolving, removed some redundant code that's default behavior in prebid * removed mtype handling as taboola server doesn't return mtype, mediaType is based on the request context which is set per media type --------- Co-authored-by: Tal Avital Co-authored-by: tal avital * Taboola multiformat test page (#14543) * add deferredBilling support using onBidBillable * update burl setting * support nurl firing logic * add extra signals to taboola request * add extra ad signals * fix missing semicolon * use Prebid's built-in counters * updated detectBot logic * Add html test page for the Taboola adapter testing multiformat ad support (banner, native) --------- Co-authored-by: Tal Avital Co-authored-by: tal avital * Fix formatting of bidderReq object in test * fix version number for 10.29 * Prebid 10.29.0 release * Increment version to 10.29.1-pre * Prebid 11.0 (#14433) * Remove quantcastId (#14045) * Remove quantcastIdSystem. * Remove references to quantcastId. --------- Co-authored-by: Demetrio Girardi * Remove quantcast bid adapter (#14054) * Remove quantcast bid adapter. * Update metadata. * Update modules.json * Add quantcastBidAdapter.json metadata file --------- Co-authored-by: Patrick McCann Co-authored-by: Patrick McCann * Delete modules/ringieraxelspringerBidAdapter.md * Delete test/spec/modules/ringieraxelspringerBidAdapter_spec.js * Delete modules/ringieraxelspringerBidAdapter.js * Delete metadata/modules/ringieraxelspringerBidAdapter.json * Remove ringieraxelspringerBidAdapter from overrides * Delete modules/dfpAdServerVideo.js * Delete modules/dfpAdpod.js * Delete modules/express.js * Prebid 11: remove PAAPI (#14432) * core: remove paapi support * PAAPI: remove paapi modules * Core: remove getBidToRender * Debugging: remove paapi support * Core: remove paapi events * PBS bid adapter: remove paapi support * VidazooUtils: remove paapi support * paapiTools: remove paapiTools * remove stray references to paapi methods * criteo bid adapter: remove paapi support * ix bid adapter: remove paapi support * logicAd bid adapter: remove paapi support * gumgum bid adapter: remove paapi support * ccx bid adapter: remove paapi support * Remove stray paapi tests * kargo bid adapter: remove paapi support * rtb house bid adapter: remove references to fledge in docs * onetag bid adapter: remove paapi support * medianet bid adapter: remove paapi support * lucead bid adapter: remove paapi support * openxBidAdapter: remove paapi support * optable bid adapter: remove paapi support * sharethrough bid adapter: remove paapi support * silverpush bid adapter: remove paapi support * sspBC bid adapter: remove paapi support * taboola bid adapter: remove paapi support * triplelift bid adapter: remove paapi support * ozone bid adapter: remove paapi support * lint * Delete modules/optableBidAdapter.js * Delete modules/optableBidAdapter.md * Delete test/spec/modules/optableBidAdapter_spec.js --------- Co-authored-by: Patrick McCann * Fix JSON formatting in quantcastBidAdapter.json * Core: default storageControl enforcement to strict (#14439) * Core: remove bidAccepted event emission (#14449) * 33across ID System: fix getId storage test stub isolation (#14457) * ShinezRtb Adapter: stabilize unique deal id timing test (#14446) * ShinezRtb Adapter: stabilize unique deal id test (#14458) * Core: remove null callback placeholders from storage manager reads/writes (#14461) * Prebid 11: remove adpod mediatype (#14451) * Core: remove dead adpod code paths from appnexus and mediafuse * Delete libraries/categoryTranslationMapping/index.js * Delete modules/categoryTranslation.js * Delete metadata/modules/categoryTranslation.json * Delete test/spec/modules/categoryTranslation_spec.js * Delete metadata/disclosures/prebid/categoryTranslation.json * Remove notifyTranslationModule and its hook registration Removed notifyTranslationModule function and its registration. * Remove brand category exclusion from payload Removed brand category uniqueness setting from payload. * Remove brand category uniqueness from payload Removed brand category uniqueness setting from payload. * Update appnexusBidAdapter_spec.js * Update mediafuseBidAdapter_spec.js * Update gamAdServerVideo.js * Update test_deps.js * Adapters: remove adpod media type handling across bidders (#14456) * Update aidemBidAdapter.js * Update aidemBidAdapter_spec.js * Prebid 11: 33across id system: make compatible with storage control change (#14462) * 33across ID Adapter: stabilize hashed email cleanup test * 33across Id System: gate HEM persistence on storeFpid * Update 33acrossIdSystem.js * remove unnecessary storageControl import * Prebid 11: turn off storageControl during testing (#14464) * Prebid 11: turn off storageControl during testing * remove unnecessary setConfig * Revert "Core: remove null callback placeholders from storage manager reads/wr…" (#14465) This reverts commit 1794187c266838e230a04e70909a9f331342b4e7. * Prebid 11: remove superfluous addAdUnits and seat_non_bid event emission (#14466) * Core: remove addAdUnits event emission * Core: remove analytics subscriptions for addAdUnits * Core: remove addAdUnits event constant * Core: remove seatNonBid event constant * 33across Analytics Adapter: handle seatnonbid via pbsAnalytics (#14471) * Update 33acrossAnalyticsAdapter.js * Update 33acrossAnalyticsAdapter_spec.js * Prebid 11: remove deprecated DNT/DoNotTrack usage and force static dnt handling (#14448) * Fix data formatting in gmosspBidAdapter tests * Remove DNT signal handling from bid request Removed deprecated DNT signal handling from request. * Update video startdelay, skip, and bidfloor values * Update viewability checks to expect 0 instead of null * Update video startdelay and skip expectations * Update bidfloor and no bid response expectations * Remove dnt test case from rhythmoneBidAdapter_spec Removed test case for 'dnt is correctly set to 1' in rhythmoneBidAdapter_spec.js. * Update video startdelay and skip expectations * Update mileBidAdapter.ts * OpenX Adapter: restore getDNT usage for dnt field (#14472) * Core: restore getDNT payload mapping across adapters (#14474) * Update displayioBidAdapter.js * Update mediakeysBidAdapter.js * Update openxBidAdapter.js * Update yahooAdsBidAdapter.js * Update yieldmoBidAdapter.js * Update yieldmoBidAdapter_spec.js * Update enrichment.ts * Update openxBidAdapter.js * Update trafficgateBidAdapter_spec.js * Update trafficgateBidAdapter_spec.js * Update trafficgateBidAdapter_spec.js * Update trafficgateBidAdapter.js * Update trafficgateBidAdapter.js * Do not add DNT to adapters that don't care about it * linter --------- Co-authored-by: Demetrio Girardi * Prebid 11: add `adUnit.element` option (#14467) * Core: add elementSelector property of ad units * core: use getAdUnitElement * various adapters: use getAdUnitElement * various modules: use getAdUnitElement * various adapters: use getAdUnitElement * use .element, not .elementSelector * lint * fix secureCreatives tests * Update ixBidAdapter.js * Remove TODO note; update type * lint * pubmaticBidAdapter: use getAdUnitElement * placement position: use getAdUnitElement --------- Co-authored-by: Patrick McCann * Prebid 11: native event trackers handling (#14524) * Prebid 11: native event trackers handling * field name fix * Prebid 11: gpt slots matching to ad units logic unification (#14480) * Promoting customSlotMatching to config * Update gptPreAuction.ts * Update gptPreAuction.ts * renaming --------- Co-authored-by: Patrick McCann * Module: Remove dmd userid module (#14545) * Remove dmd userid module * Remove dmd userId module test coverage --------- Co-authored-by: mfitzgerald_dmd * lint fix * Prebid 11: document encouraging TypeScript for new source files (#14488) * Docs: require TypeScript for new src/modules/libraries files * Encourage TypeScript for new files in specific directories Rephrase recommendation for TypeScript usage in new files. * Update PR_REVIEW.md * CI: comment on newly added JS files in TS paths (#14526) * Reword to focus on public interface * Update linter.yml Linter check comment --------- Co-authored-by: Demetrio Girardi * Prebid 11: Bid viewability event trackers (#14505) * Prebid 11: Bid viewability event trackers * naming conflict fix * sspBCBidAdapter vurls fallback * removing firePixels config, fixing event listener, removing gdpr, adding lib spec file * native & js handling * lint * revert adapter changes * revert * remove gpt impression listener first * sspBCBidAdapter refactor * sspBCBidAdapter tests fix * missing import * Update bidViewability.js --------- Co-authored-by: Patrick McCann * Prebid 11: remove intersectionRtdProvider, use IntersectionObserver in percentInView (#14525) * Core: add elementSelector property of ad units * core: use getAdUnitElement * various adapters: use getAdUnitElement * various modules: use getAdUnitElement * various adapters: use getAdUnitElement * use .element, not .elementSelector * lint * fix secureCreatives tests * Update ixBidAdapter.js * Remove TODO note; update type * lint * pubmaticBidAdapter: use getAdUnitElement * placement position: use getAdUnitElement * percentInView: use IntersectionObserver * adlooxRtdProvider: do not depend on intersectionObserver * remove intersectionrtdprovider * check the right element has intersected * keep size override * handle missing intersection observer * Fix tests * DOMRect weirdness * remove unnecessary sort * More aggressive static fallback * Update percentInView.js * Update percentInView.js * whitespace --------- Co-authored-by: Patrick McCann * Prebid 11: toss mediatype mismatch bids; avoid mediaType mismatch rejects when adapter omits mediaType (#14496) * Core: preserve omitted mediaType behavior in bid validation * Update bidderFactory to retain mediaType Retain mediaType information from adapter response. * tests fixing * lint * Core: split unknown vs invalid mediaType rejection flags (#14569) --------- Co-authored-by: mkomorski * adloox analytics: do not disable if not enabled * connatixBidAdapter: fix tests * connatixBidAdapter: fix the fix to the test * percentInView: add timeout, adjust intersection thresholds (#14579) * remove debugger statement * mediaFuse: fix test failing on safari (regex lookbehind not supported) * Revert "mediaFuse: fix test failing on safari (regex lookbehind not supported)" This reverts commit 85860a8278f8cf1cea5c4809c5e36eadff3f0d0e. * mediafuse: reapply adpod changes * mediafuse: fix lint * mediafuse: use getAdUnitElement * magnite utils: use getAdUnitElement * Prebid 11: normalize onBidViewable behavior (#14586) * Prebid 11: normalize onBidViewable behavior * lint * Delete scope3_segtax_pr.md * taboola: fix missing import --------- Co-authored-by: dpapworth-qc <50959025+dpapworth-qc@users.noreply.github.com> Co-authored-by: Demetrio Girardi Co-authored-by: mkomorski Co-authored-by: Matt Fitzgerald Co-authored-by: mfitzgerald_dmd * Prebid 11.0.0 release * Increment version to 11.1.1-pre * StroeerCore Bid Adapter: add support for TIDs and GPID (#14548) * StroeerCore Bid Adapter: add support for TIDs and GPID * Fix lint * Incorporate ortb2Imp.ext.data as kv in ad request. (#14549) * Various modules: revert GPT targeting changes (#14591) * Various modules: revert GPT targeting changes * keep intentiq changes * Prebid 11.1.0 release * Increment version to 11.2.0-pre * Bump flatted from 3.3.1 to 3.4.1 (#14593) Bumps [flatted](https://github.com/WebReflection/flatted) from 3.3.1 to 3.4.1. - [Commits](https://github.com/WebReflection/flatted/compare/v3.3.1...v3.4.1) --- updated-dependencies: - dependency-name: flatted dependency-version: 3.4.1 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump undici from 6.23.0 to 6.24.1 (#14595) Bumps [undici](https://github.com/nodejs/undici) from 6.23.0 to 6.24.1. - [Release notes](https://github.com/nodejs/undici/releases) - [Commits](https://github.com/nodejs/undici/compare/v6.23.0...v6.24.1) --- updated-dependencies: - dependency-name: undici dependency-version: 6.24.1 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Holid Bid Adapter: respect auction timeout, ORTB merges, usersync robustness (#14530) * Holid: respect auction timeout, safer ortb merges, improve usersync * Holid Bid Adapter: add unit tests * chore: re-run CI * Add Alliance Gravity Bid Adapter (#14267) * feat(adapter): ts implementation * feat(adapter): removed amx support * test: added tests * fix(bid-adapter): renamed file * fix(library): folder name * update(bid-adapter): imported module * update: test creds in adUnit * fix(cookie): added guard on object slicing * fix(enrichImp): removed divId parameter * fix(tests): adapted test w/o divId --------- Co-authored-by: Mickael van der Beek * gppControl modules: add missing transmitUfpd check (#14604) * gppControl modules: add missing transmitUfpd check * Update activityControls.js --------- Co-authored-by: Patrick McCann * Bump fast-xml-parser from 5.4.1 to 5.5.6 (#14608) Bumps [fast-xml-parser](https://github.com/NaturalIntelligence/fast-xml-parser) from 5.4.1 to 5.5.6. - [Release notes](https://github.com/NaturalIntelligence/fast-xml-parser/releases) - [Changelog](https://github.com/NaturalIntelligence/fast-xml-parser/blob/master/CHANGELOG.md) - [Commits](https://github.com/NaturalIntelligence/fast-xml-parser/compare/v5.4.1...v5.5.6) --- updated-dependencies: - dependency-name: fast-xml-parser dependency-version: 5.5.6 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * 51DegreesRtdProvider: populate device.hwv, improve device.model (#14598) * 51d: populate device.hwv + optionally use hardwarenameprefix as device.module * 51d update doc * fix typo * adtelligentBidAdapter: gather placement pos info (#14555) * adtelligentBidAdapter-update * lint fix --------- Co-authored-by: k-piekharieva Co-authored-by: Patrick McCann * Reformat import statements in gravityBidAdapter (#14610) * Reformat import statements in gravityBidAdapter * Fix import statement formatting for Renderer * Fix formatting of cookies in user sync test * Format code for consistency in buildRequests function * BeOpBidAdapter: Refacto beopid cookie to caudid (#14584) * Change beopid cookie to caudid * Add caudid_date cookie * Post review commit * Bump socket.io-parser from 4.2.4 to 4.2.6 (#14613) Bumps [socket.io-parser](https://github.com/socketio/socket.io) from 4.2.4 to 4.2.6. - [Release notes](https://github.com/socketio/socket.io/releases) - [Changelog](https://github.com/socketio/socket.io/blob/main/CHANGELOG.md) - [Commits](https://github.com/socketio/socket.io/compare/socket.io-parser@4.2.4...socket.io-parser@4.2.6) --- updated-dependencies: - dependency-name: socket.io-parser dependency-version: 4.2.6 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Add 'device.ifa' to user paths in redactor.ts (#14606) * Core: allow vast xml without using cache (#14611) * Core: allow vast xml without using cache * renaming * Bump fast-xml-parser from 5.5.6 to 5.5.7 (#14619) Bumps [fast-xml-parser](https://github.com/NaturalIntelligence/fast-xml-parser) from 5.5.6 to 5.5.7. - [Release notes](https://github.com/NaturalIntelligence/fast-xml-parser/releases) - [Changelog](https://github.com/NaturalIntelligence/fast-xml-parser/blob/master/CHANGELOG.md) - [Commits](https://github.com/NaturalIntelligence/fast-xml-parser/compare/v5.5.6...v5.5.7) --- updated-dependencies: - dependency-name: fast-xml-parser dependency-version: 5.5.7 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --------- Signed-off-by: dependabot[bot] Co-authored-by: ourcraig Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: alukonin1 <152840501+alukonin1@users.noreply.github.com> Co-authored-by: PanxoDev Co-authored-by: donnychang Co-authored-by: Laneser Co-authored-by: Tjorven Co-authored-by: Tomas Roos Co-authored-by: DimaIntentIQ <139111483+DimaIntentIQ@users.noreply.github.com> Co-authored-by: dmytro-po Co-authored-by: mkomorski Co-authored-by: Marcin Muras <47107445+mmuras@users.noreply.github.com> Co-authored-by: daniel-barac <55977021+daniel-barac@users.noreply.github.com> Co-authored-by: dragos.baci Co-authored-by: DragosBaci <118546616+DragosBaci@users.noreply.github.com> Co-authored-by: Alex Co-authored-by: Gaina Dan-Lucian Co-authored-by: Gaina Dan-Lucian <83463253+Dan-Lucian@users.noreply.github.com> Co-authored-by: Demetrio Girardi Co-authored-by: tal avital Co-authored-by: Prebid.js automated release Co-authored-by: ym-aaron <89419575+ym-aaron@users.noreply.github.com> Co-authored-by: Gabriel Gravel Co-authored-by: Denis Logachov Co-authored-by: SebRobert Co-authored-by: pgomulka-id5 Co-authored-by: mpimentel-nexxen Co-authored-by: Vadym Shatov <135347097+Gunnar97@users.noreply.github.com> Co-authored-by: Florian Erl <46747754+florianerl@users.noreply.github.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Luca Corbo Co-authored-by: Bendegúz Ács <30595431+acsbendi@users.noreply.github.com> Co-authored-by: yndxcdn Co-authored-by: Robert Ray Martinez III Co-authored-by: MaksymTeqBlaze Co-authored-by: Deepthi Neeladri Co-authored-by: dsravana Co-authored-by: robin-crazygames Co-authored-by: optidigital-prebid <124287395+optidigital-prebid@users.noreply.github.com> Co-authored-by: Dawid W Co-authored-by: Brian Weiss Co-authored-by: Brian Co-authored-by: ysfbsf Co-authored-by: Dmitry Borisenko Co-authored-by: Petrică Nancă Co-authored-by: Alexandr Kim <47887567+alexandr-kim-vl@users.noreply.github.com> Co-authored-by: Alexandr Kim Co-authored-by: Shashank Pradeep <101392500+shashankatd@users.noreply.github.com> Co-authored-by: Monis Qadri Co-authored-by: RuzannaAvetisyan <44729750+RuzannaAvetisyan@users.noreply.github.com> Co-authored-by: Siminko Vlad <85431371+siminkovladyslav@users.noreply.github.com> Co-authored-by: danijel-ristic <168181386+danijel-ristic@users.noreply.github.com> Co-authored-by: dnrstc Co-authored-by: verben-gh Co-authored-by: Verben Co-authored-by: johnclc Co-authored-by: Anthony Co-authored-by: Anthony Richir Co-authored-by: floxis-tech Co-authored-by: Jose Climaco Co-authored-by: morock <98119227+mo4rock@users.noreply.github.com> Co-authored-by: markappmedia Co-authored-by: Vedant Madane <6527493+VedantMadane@users.noreply.github.com> Co-authored-by: pm-priyanka-deshmane <107103300+pm-priyanka-deshmane@users.noreply.github.com> Co-authored-by: adclusterdev Co-authored-by: v0idxyz <58184010+v0idxyz@users.noreply.github.com> Co-authored-by: Fatih Kaya Co-authored-by: abermanov-zeta <95416296+abermanov-zeta@users.noreply.github.com> Co-authored-by: driftpixelai <166716541+driftpixelai@users.noreply.github.com> Co-authored-by: briguy-mobian Co-authored-by: Paul Farrow Co-authored-by: Paul Farrow Co-authored-by: Cursor Co-authored-by: gregneuwo Co-authored-by: grzgm <125459798+grzgm@users.noreply.github.com> Co-authored-by: gregneuwo <226034698+gregneuwo@users.noreply.github.com> Co-authored-by: rishko00 <43280707+rishko00@users.noreply.github.com> Co-authored-by: vrishko Co-authored-by: matthieularere-msq <63732822+matthieularere-msq@users.noreply.github.com> Co-authored-by: Shubham <127132399+shubhamc-ins@users.noreply.github.com> Co-authored-by: cpcpn-emil <115714010+cpcpn-emil@users.noreply.github.com> Co-authored-by: Roger <104763658+rogerDyl@users.noreply.github.com> Co-authored-by: Anna Yablonsky Co-authored-by: Anna Yablonsky Co-authored-by: ahzgg <163184035+ahzgg@users.noreply.github.com> Co-authored-by: ibhattacharya-dev Co-authored-by: Michal Kváček Co-authored-by: Michal Kváček Co-authored-by: Guillaume Polaert Co-authored-by: Stéphane Deluce Co-authored-by: 方思敏 <506374983@qq.com> Co-authored-by: fangsimin@baidu.com Co-authored-by: 方思敏 Co-authored-by: Screencore Developer Co-authored-by: Kostiantyn Karchevsky Co-authored-by: Pavlo Samonin Co-authored-by: John Ivan Bauzon Co-authored-by: Lisa Benmore Co-authored-by: John Bauzon Co-authored-by: ahzgg Co-authored-by: jsnellbaker <31102355+jsnellbaker@users.noreply.github.com> Co-authored-by: quietPusher <129727954+quietPusher@users.noreply.github.com> Co-authored-by: mderevyanko Co-authored-by: giathinhly <96400885+giathinhly@users.noreply.github.com> Co-authored-by: DimaIntentIQ Co-authored-by: oleksii-intent Co-authored-by: Oleksii Petrychenko Co-authored-by: dalmenarDevST <116064809+dalmenarDevST@users.noreply.github.com> Co-authored-by: Yohan Boutin Co-authored-by: Rômulo Vitoi Co-authored-by: Karim Mourra Co-authored-by: Brett Oberg Co-authored-by: Parth Shah Co-authored-by: Martin Mach Co-authored-by: Rob Co-authored-by: ronishefi <168074830+ronishefi9@users.noreply.github.com> Co-authored-by: tal avital Co-authored-by: dpapworth-qc <50959025+dpapworth-qc@users.noreply.github.com> Co-authored-by: Matt Fitzgerald Co-authored-by: mfitzgerald_dmd Co-authored-by: Philip Watson Co-authored-by: Antonios Sarhanis Co-authored-by: Richard Andersson <72393300+holidio@users.noreply.github.com> Co-authored-by: Daniel Baud <52867769+danielbaud@users.noreply.github.com> Co-authored-by: Mickael van der Beek Co-authored-by: Eugene Dorfman Co-authored-by: PiekharievaK <86105196+PiekharievaK@users.noreply.github.com> Co-authored-by: k-piekharieva --- .github/actions/load/action.yml | 10 +- .github/actions/unzip-artifact/action.yml | 3 + .../codeql/queries/autogen_fpDOMMethod.qll | 4 +- .../queries/autogen_fpEventProperty.qll | 16 +- .../queries/autogen_fpGlobalConstructor.qll | 10 +- .../autogen_fpGlobalObjectProperty0.qll | 54 +- .../autogen_fpGlobalObjectProperty1.qll | 2 +- .../queries/autogen_fpGlobalTypeProperty0.qll | 6 +- .../queries/autogen_fpGlobalTypeProperty1.qll | 2 +- .../codeql/queries/autogen_fpGlobalVar.qll | 18 +- .../autogen_fpRenderingContextProperty.qll | 30 +- .../queries/autogen_fpSensorProperty.qll | 2 +- .github/workflows/PR-assignment-deps.yml | 4 +- .github/workflows/jscpd.yml | 6 +- .github/workflows/linter.yml | 42 +- .github/workflows/run-tests.yml | 9 +- .github/workflows/test.yml | 4 + AGENTS.md | 13 + CONTRIBUTING.md | 3 + PR_REVIEW.md | 1 + creative/constants.js | 4 +- creative/crossDomain.js | 10 +- creative/renderers/display/renderer.js | 6 +- creative/renderers/native/renderer.js | 18 +- eslint.config.js | 56 +- gulpfile.js | 7 +- integrationExamples/audio/audioGam.html | 4 +- .../gpt/adcluster_banner_example.html | 115 + .../gpt/adcluster_video_example.html | 291 ++ integrationExamples/gpt/insurads.html | 121 + integrationExamples/gpt/localCacheGam.html | 6 +- integrationExamples/gpt/mediago_test.html | 114 +- .../gpt/neuwoRtdProvider_example.html | 439 +- .../gpt/proxistore_example.html | 2 +- .../gpt/raveltechRtdProvider_example.html | 3 - .../gpt/taboola_multiformat.html | 492 ++ integrationExamples/gpt/userId_example.html | 3 - .../longform/basic_w_bidderSettings.html | 148 - .../basic_w_custom_adserver_translation.html | 135 - .../longform/basic_w_priceGran.html | 156 - .../basic_w_requireExactDuration.html | 133 - .../basic_wo_brandCategoryExclusion.html | 133 - .../basic_wo_requireExactDuration.html | 134 - .../noadserver/connatixBidAdapter_sample.html | 97 + integrationExamples/shapingRules/rules.json | 151 + .../shapingRules/shapingRulesModule.html | 168 + libraries/adkernelUtils/adkernelUtils.js | 2 +- libraries/adrelevantisUtils/bidderUtils.js | 6 +- .../adtelligentUtils/adtelligentUtils.js | 7 +- libraries/advangUtils/index.js | 24 +- libraries/alliance_gravityUtils/index.ts | 149 + .../analyticsAdapter/AnalyticsAdapter.ts | 18 +- libraries/appnexusUtils/anKeywords.js | 8 +- libraries/appnexusUtils/anUtils.js | 3 +- libraries/audUtils/bidderUtils.js | 2 +- libraries/bidViewabilityPixels/index.js | 60 + libraries/braveUtils/buildAndInterpret.js | 2 +- libraries/braveUtils/index.js | 2 +- libraries/categoryTranslationMapping/index.js | 100 - libraries/cmp/cmpClient.js | 4 +- libraries/consentManagement/cmUtils.ts | 26 +- libraries/cookieSync/cookieSync.js | 2 +- libraries/currencyUtils/currency.js | 4 +- .../devicePixelRatio/devicePixelRatio.js | 14 +- libraries/dfpUtils/dfpUtils.js | 2 +- libraries/dnt/index.js | 16 +- libraries/dspxUtils/bidderUtils.js | 2 +- libraries/dxUtils/common.js | 6 +- libraries/fingerprinting/fingerprinting.js | 12 + libraries/gamUtils/gamUtils.js | 2 +- libraries/gptUtils/gptUtils.js | 16 +- libraries/greedy/greedyPromise.js | 2 +- libraries/hybridVoxUtils/index.js | 4 +- .../intentIqConstants/intentIqConstants.js | 3 +- .../intentIqUtils/gamPredictionReport.js | 30 +- libraries/intentIqUtils/getRefferer.js | 33 +- libraries/intentIqUtils/getUnitPosition.js | 17 + libraries/intentIqUtils/intentIqConfig.js | 36 +- libraries/intentIqUtils/storageUtils.js | 10 +- libraries/intentIqUtils/urlUtils.js | 10 +- libraries/interpretResponseUtils/index.js | 4 +- libraries/keywords/keywords.js | 4 +- libraries/liveIntentId/idSystem.js | 2 +- libraries/liveIntentId/shared.js | 8 +- libraries/magniteUtils/outstream.js | 80 + libraries/medianetUtils/logKeys.js | 6 +- libraries/medianetUtils/logger.js | 4 +- libraries/medianetUtils/utils.js | 4 +- libraries/metadata/metadata.js | 4 +- libraries/mspa/activityControls.js | 20 +- libraries/nativeAssetsUtils.js | 12 +- libraries/nexx360Utils/index.ts | 2 +- libraries/objectGuard/objectGuard.js | 33 +- libraries/objectGuard/ortbGuard.js | 10 +- libraries/omsUtils/index.js | 27 +- libraries/omsUtils/viewability.js | 19 + libraries/ortb2.5StrictTranslator/spec.js | 2 +- .../ortb2.5StrictTranslator/translator.js | 6 +- libraries/ortb2.5Translator/translator.js | 2 +- libraries/ortbConverter/README.md | 2 +- libraries/ortbConverter/converter.ts | 52 +- .../ortbConverter/lib/mergeProcessors.js | 2 +- libraries/ortbConverter/processors/banner.js | 4 +- libraries/ortbConverter/processors/default.js | 17 +- .../ortbConverter/processors/mediaType.js | 2 +- libraries/ortbConverter/processors/native.js | 4 +- libraries/ortbConverter/processors/video.js | 6 +- libraries/paapiTools/buyerOrigins.js | 35 - libraries/pbsExtensions/pbsExtensions.js | 10 +- .../pbsExtensions/processors/adUnitCode.js | 2 +- libraries/pbsExtensions/processors/aliases.js | 6 +- .../pbsExtensions/processors/eventTrackers.js | 4 +- .../pbsExtensions/processors/mediaType.js | 4 +- .../pbsExtensions/processors/pageViewIds.js | 2 +- libraries/pbsExtensions/processors/params.js | 2 +- libraries/pbsExtensions/processors/pbs.js | 22 +- .../processors/requestExtPrebid.js | 6 +- libraries/pbsExtensions/processors/video.js | 6 +- libraries/percentInView/percentInView.js | 153 +- .../placementPositionInfo.js | 87 + libraries/riseUtils/constants.js | 2 +- libraries/riseUtils/index.js | 11 +- libraries/storageDisclosure/summary.mjs | 4 +- libraries/targetVideoUtils/bidderUtils.js | 14 +- libraries/teqblazeUtils/bidderUtils.js | 6 +- libraries/timezone/timezone.js | 5 + .../transformParamsUtils/convertTypes.js | 2 +- libraries/uid1Eids/uid1Eids.js | 2 +- .../uid2IdSystemShared/uid2IdSystem_shared.js | 49 +- libraries/utiqUtils/utiqUtils.ts | 2 +- libraries/vastTrackers/vastTrackers.js | 30 +- libraries/vidazooUtils/bidderUtils.js | 37 +- libraries/viewport/viewport.js | 2 +- libraries/weakStore/weakStore.js | 4 +- libraries/webdriver/webdriver.js | 48 +- libraries/xeUtils/bidderUtils.js | 10 +- metadata/core.json | 6 + .../prebid/categoryTranslation.json | 26 - metadata/disclosures/prebid/probes.json | 2 +- metadata/modules.json | 222 +- metadata/modules/33acrossBidAdapter.json | 2 +- metadata/modules/33acrossIdSystem.json | 2 +- metadata/modules/aceexBidAdapter.json | 18 + metadata/modules/acuityadsBidAdapter.json | 2 +- metadata/modules/adagioBidAdapter.json | 2 +- metadata/modules/adagioRtdProvider.json | 2 +- metadata/modules/adbroBidAdapter.json | 2 +- ...dAdapter.json => adclusterBidAdapter.json} | 9 +- metadata/modules/addefendBidAdapter.json | 2 +- metadata/modules/adfBidAdapter.json | 2 +- metadata/modules/adfusionBidAdapter.json | 2 +- metadata/modules/adheseBidAdapter.json | 2 +- metadata/modules/adipoloBidAdapter.json | 2 +- metadata/modules/adkernelAdnBidAdapter.json | 2 +- metadata/modules/adkernelBidAdapter.json | 17 +- metadata/modules/admaticBidAdapter.json | 11 +- metadata/modules/admixerBidAdapter.json | 2 +- metadata/modules/admixerIdSystem.json | 2 +- metadata/modules/adnimationBidAdapter.json | 13 + metadata/modules/adnowBidAdapter.json | 2 +- metadata/modules/adnuntiusBidAdapter.json | 2 +- metadata/modules/adnuntiusRtdProvider.json | 2 +- metadata/modules/adoceanBidAdapter.json | 273 +- metadata/modules/adotBidAdapter.json | 2 +- metadata/modules/adponeBidAdapter.json | 2 +- metadata/modules/adqueryBidAdapter.json | 2 +- metadata/modules/adqueryIdSystem.json | 2 +- metadata/modules/adrinoBidAdapter.json | 2 +- .../modules/ads_interactiveBidAdapter.json | 2 +- metadata/modules/adtargetBidAdapter.json | 2 +- metadata/modules/adtelligentBidAdapter.json | 6 +- metadata/modules/adtelligentIdSystem.json | 2 +- metadata/modules/aduptechBidAdapter.json | 2 +- metadata/modules/adyoulikeBidAdapter.json | 4 +- metadata/modules/airgridRtdProvider.json | 2 +- metadata/modules/alkimiBidAdapter.json | 2 +- metadata/modules/allegroBidAdapter.json | 2 +- metadata/modules/amxBidAdapter.json | 29 +- metadata/modules/amxIdSystem.json | 29 +- metadata/modules/aniviewBidAdapter.json | 2 +- metadata/modules/anonymisedRtdProvider.json | 6 +- metadata/modules/apesterBidAdapter.json | 18 + metadata/modules/appStockSSPBidAdapter.json | 2 +- metadata/modules/appierBidAdapter.json | 2 +- metadata/modules/appnexusBidAdapter.json | 19 +- metadata/modules/appushBidAdapter.json | 2 +- metadata/modules/apsBidAdapter.json | 2 +- metadata/modules/apstreamBidAdapter.json | 2 +- metadata/modules/audiencerunBidAdapter.json | 2 +- metadata/modules/axisBidAdapter.json | 2 +- metadata/modules/azerionedgeRtdProvider.json | 2 +- metadata/modules/beachfrontBidAdapter.json | 2 +- metadata/modules/beopBidAdapter.json | 2 +- metadata/modules/betweenBidAdapter.json | 2 +- metadata/modules/bidfuseBidAdapter.json | 2 +- metadata/modules/bidmaticBidAdapter.json | 2 +- metadata/modules/bidtheatreBidAdapter.json | 2 +- metadata/modules/bliinkBidAdapter.json | 2 +- metadata/modules/blockthroughBidAdapter.json | 320 +- metadata/modules/blueBidAdapter.json | 2 +- metadata/modules/bmsBidAdapter.json | 2 +- metadata/modules/boldwinBidAdapter.json | 2 +- metadata/modules/bridBidAdapter.json | 2 +- metadata/modules/browsiBidAdapter.json | 2 +- metadata/modules/bucksenseBidAdapter.json | 2 +- metadata/modules/carodaBidAdapter.json | 2 +- metadata/modules/categoryTranslation.json | 19 +- metadata/modules/ceeIdSystem.json | 2 +- metadata/modules/chromeAiRtdProvider.json | 2 +- metadata/modules/clickioBidAdapter.json | 2 +- metadata/modules/compassBidAdapter.json | 2 +- metadata/modules/conceptxBidAdapter.json | 2 +- metadata/modules/connatixBidAdapter.json | 2 +- metadata/modules/connectIdSystem.json | 2 +- metadata/modules/connectadBidAdapter.json | 2 +- .../modules/contentexchangeBidAdapter.json | 4 +- metadata/modules/conversantBidAdapter.json | 2 +- metadata/modules/copper6sspBidAdapter.json | 2 +- metadata/modules/cpmstarBidAdapter.json | 2 +- metadata/modules/criteoBidAdapter.json | 6 +- metadata/modules/criteoIdSystem.json | 6 +- metadata/modules/cwireBidAdapter.json | 2 +- metadata/modules/czechAdIdSystem.json | 2 +- metadata/modules/dailymotionBidAdapter.json | 2 +- metadata/modules/debugging.json | 2 +- metadata/modules/deepintentBidAdapter.json | 4 +- metadata/modules/defineMediaBidAdapter.json | 2 +- metadata/modules/deltaprojectsBidAdapter.json | 2 +- metadata/modules/dianomiBidAdapter.json | 2 +- metadata/modules/digitalMatterBidAdapter.json | 2 +- metadata/modules/distroscaleBidAdapter.json | 2 +- .../modules/docereeAdManagerBidAdapter.json | 2 +- metadata/modules/docereeBidAdapter.json | 2 +- ...bleBidAdapter.json => dpaiBidAdapter.json} | 2 +- metadata/modules/dspxBidAdapter.json | 2 +- metadata/modules/e_volutionBidAdapter.json | 2 +- metadata/modules/edge226BidAdapter.json | 2 +- metadata/modules/empowerBidAdapter.json | 2 +- metadata/modules/equativBidAdapter.json | 2 +- metadata/modules/eskimiBidAdapter.json | 2 +- metadata/modules/etargetBidAdapter.json | 2 +- metadata/modules/euidIdSystem.json | 2 +- metadata/modules/exadsBidAdapter.json | 2 +- metadata/modules/feedadBidAdapter.json | 2 +- metadata/modules/floxisBidAdapter.json | 13 + metadata/modules/fwsspBidAdapter.json | 2 +- metadata/modules/gamoshiBidAdapter.json | 6 +- metadata/modules/gemiusIdSystem.json | 2 +- metadata/modules/glomexBidAdapter.json | 2 +- metadata/modules/goldbachBidAdapter.json | 2 +- metadata/modules/gridBidAdapter.json | 2 +- metadata/modules/gumgumBidAdapter.json | 2 +- metadata/modules/hadronIdSystem.json | 2 +- metadata/modules/hadronRtdProvider.json | 2 +- metadata/modules/harionBidAdapter.json | 18 + metadata/modules/holidBidAdapter.json | 2 +- metadata/modules/hybridBidAdapter.json | 2 +- metadata/modules/id5IdSystem.json | 246 +- metadata/modules/identityLinkIdSystem.json | 2 +- metadata/modules/illuminBidAdapter.json | 2 +- metadata/modules/impactifyBidAdapter.json | 2 +- .../modules/improvedigitalBidAdapter.json | 2 +- metadata/modules/inmobiBidAdapter.json | 2 +- metadata/modules/insticatorBidAdapter.json | 2 +- ...idAdapter.json => insuradsBidAdapter.json} | 28 +- metadata/modules/intentIqIdSystem.json | 2 +- metadata/modules/invibesBidAdapter.json | 2 +- metadata/modules/ipromBidAdapter.json | 2 +- metadata/modules/ixBidAdapter.json | 2 +- metadata/modules/justIdSystem.json | 2 +- metadata/modules/justpremiumBidAdapter.json | 2 +- metadata/modules/jwplayerBidAdapter.json | 2 +- metadata/modules/kargoBidAdapter.json | 2 +- metadata/modules/kueezRtbBidAdapter.json | 2 +- metadata/modules/leagueMBidAdapter.json | 13 + .../modules/limelightDigitalBidAdapter.json | 23 +- metadata/modules/liveIntentIdSystem.json | 2 +- metadata/modules/liveIntentRtdProvider.json | 2 +- metadata/modules/livewrappedBidAdapter.json | 2 +- metadata/modules/locIdSystem.json | 20 + metadata/modules/loopmeBidAdapter.json | 2 +- metadata/modules/lotamePanoramaIdSystem.json | 95 +- metadata/modules/luponmediaBidAdapter.json | 2 +- metadata/modules/madvertiseBidAdapter.json | 2 +- metadata/modules/magniteBidAdapter.json | 18 + metadata/modules/marsmediaBidAdapter.json | 2 +- .../modules/mediaConsortiumBidAdapter.json | 2 +- metadata/modules/mediaforceBidAdapter.json | 2 +- metadata/modules/mediafuseBidAdapter.json | 2 +- metadata/modules/mediagoBidAdapter.json | 2 +- metadata/modules/mediakeysBidAdapter.json | 2 +- metadata/modules/medianetBidAdapter.json | 4 +- metadata/modules/mediasquareBidAdapter.json | 2 +- metadata/modules/mgidBidAdapter.json | 2 +- metadata/modules/mgidRtdProvider.json | 2 +- metadata/modules/mgidXBidAdapter.json | 2 +- .../{dmdIdSystem.json => mileBidAdapter.json} | 8 +- metadata/modules/minutemediaBidAdapter.json | 2 +- metadata/modules/missenaBidAdapter.json | 2 +- metadata/modules/mobianRtdProvider.json | 2 +- metadata/modules/mobkoiBidAdapter.json | 2 +- metadata/modules/mobkoiIdSystem.json | 2 +- metadata/modules/msftBidAdapter.json | 2 +- metadata/modules/nativeryBidAdapter.json | 2 +- metadata/modules/nativoBidAdapter.json | 2 +- metadata/modules/newspassidBidAdapter.json | 2 +- .../modules/nextMillenniumBidAdapter.json | 2 +- metadata/modules/nextrollBidAdapter.json | 2 +- metadata/modules/nexx360BidAdapter.json | 12 +- metadata/modules/nobidBidAdapter.json | 2 +- metadata/modules/nodalsAiRtdProvider.json | 2 +- metadata/modules/novatiqIdSystem.json | 2 +- metadata/modules/oguryBidAdapter.json | 2 +- metadata/modules/omnidexBidAdapter.json | 2 +- metadata/modules/omsBidAdapter.json | 2 +- metadata/modules/onetagBidAdapter.json | 2 +- metadata/modules/openwebBidAdapter.json | 2 +- metadata/modules/openxBidAdapter.json | 2 +- metadata/modules/operaadsBidAdapter.json | 2 +- metadata/modules/optidigitalBidAdapter.json | 2 +- metadata/modules/optoutBidAdapter.json | 2 +- metadata/modules/orbidderBidAdapter.json | 2 +- metadata/modules/outbrainBidAdapter.json | 2 +- metadata/modules/ozoneBidAdapter.json | 2 +- metadata/modules/pairIdSystem.json | 2 +- metadata/modules/panxoBidAdapter.json | 46 + ...RtdProvider.json => panxoRtdProvider.json} | 2 +- metadata/modules/performaxBidAdapter.json | 2 +- .../permutiveIdentityManagerIdSystem.json | 2 +- metadata/modules/permutiveRtdProvider.json | 2 +- metadata/modules/pixfutureBidAdapter.json | 2 +- metadata/modules/playdigoBidAdapter.json | 2 +- metadata/modules/prebid-core.json | 9 +- metadata/modules/precisoBidAdapter.json | 2 +- metadata/modules/prismaBidAdapter.json | 2 +- metadata/modules/programmaticXBidAdapter.json | 2 +- metadata/modules/proxistoreBidAdapter.json | 2 +- metadata/modules/publinkIdSystem.json | 2 +- metadata/modules/pubmaticBidAdapter.json | 2 +- metadata/modules/pubmaticIdSystem.json | 2 +- metadata/modules/pubstackBidAdapter.json | 25 + metadata/modules/pulsepointBidAdapter.json | 2 +- metadata/modules/quantcastIdSystem.json | 51 - metadata/modules/r2b2BidAdapter.json | 2 +- metadata/modules/readpeakBidAdapter.json | 2 +- metadata/modules/relayBidAdapter.json | 4 +- .../modules/relevantdigitalBidAdapter.json | 2 +- metadata/modules/resetdigitalBidAdapter.json | 2 +- metadata/modules/responsiveAdsBidAdapter.json | 2 +- metadata/modules/revantageBidAdapter.json | 13 + metadata/modules/revcontentBidAdapter.json | 2 +- metadata/modules/revnewBidAdapter.json | 2 +- metadata/modules/rhythmoneBidAdapter.json | 2 +- metadata/modules/richaudienceBidAdapter.json | 2 +- metadata/modules/riseBidAdapter.json | 4 +- metadata/modules/rixengineBidAdapter.json | 2 +- metadata/modules/rtbhouseBidAdapter.json | 2 +- metadata/modules/rubiconBidAdapter.json | 2 +- metadata/modules/scaliburBidAdapter.json | 2 +- metadata/modules/screencoreBidAdapter.json | 4 +- .../modules/seedingAllianceBidAdapter.json | 2 +- metadata/modules/seedtagBidAdapter.json | 2 +- metadata/modules/semantiqRtdProvider.json | 2 +- metadata/modules/setupadBidAdapter.json | 2 +- metadata/modules/sevioBidAdapter.json | 2 +- metadata/modules/sharedIdSystem.json | 2 +- metadata/modules/sharethroughBidAdapter.json | 2 +- metadata/modules/showheroes-bsBidAdapter.json | 2 +- metadata/modules/silvermobBidAdapter.json | 2 +- metadata/modules/sirdataRtdProvider.json | 2 +- metadata/modules/smaatoBidAdapter.json | 2 +- metadata/modules/smartadserverBidAdapter.json | 2 +- metadata/modules/smartxBidAdapter.json | 2 +- metadata/modules/smartyadsBidAdapter.json | 2 +- metadata/modules/smilewantedBidAdapter.json | 2 +- metadata/modules/snigelBidAdapter.json | 2 +- metadata/modules/sonaradsBidAdapter.json | 2 +- metadata/modules/sonobiBidAdapter.json | 2 +- metadata/modules/sovrnBidAdapter.json | 2 +- metadata/modules/sparteoBidAdapter.json | 2 +- metadata/modules/ssmasBidAdapter.json | 2 +- metadata/modules/sspBCBidAdapter.json | 2 +- metadata/modules/stackadaptBidAdapter.json | 24 +- metadata/modules/startioBidAdapter.json | 2 +- metadata/modules/stroeerCoreBidAdapter.json | 2 +- metadata/modules/stvBidAdapter.json | 2 +- metadata/modules/sublimeBidAdapter.json | 2 +- metadata/modules/taboolaBidAdapter.json | 2 +- metadata/modules/taboolaIdSystem.json | 2 +- metadata/modules/tadvertisingBidAdapter.json | 2 +- metadata/modules/tappxBidAdapter.json | 2 +- metadata/modules/targetVideoBidAdapter.json | 2 +- metadata/modules/teadsBidAdapter.json | 2 +- metadata/modules/teadsIdSystem.json | 2 +- metadata/modules/tealBidAdapter.json | 2 +- .../modules/teqBlazeSalesAgentBidAdapter.json | 13 + metadata/modules/tncIdSystem.json | 2 +- metadata/modules/topicsFpdModule.json | 2 +- metadata/modules/toponBidAdapter.json | 2 +- metadata/modules/tripleliftBidAdapter.json | 2 +- metadata/modules/ttdBidAdapter.json | 2 +- metadata/modules/twistDigitalBidAdapter.json | 2 +- metadata/modules/underdogmediaBidAdapter.json | 2 +- metadata/modules/undertoneBidAdapter.json | 2 +- metadata/modules/unifiedIdSystem.json | 2 +- metadata/modules/unrulyBidAdapter.json | 2 +- metadata/modules/userId.json | 2 +- metadata/modules/utiqIdSystem.json | 2 +- metadata/modules/utiqMtpIdSystem.json | 2 +- metadata/modules/validationFpdModule.json | 2 +- metadata/modules/valuadBidAdapter.json | 2 +- metadata/modules/verbenBidAdapter.json | 13 + metadata/modules/vidazooBidAdapter.json | 2 +- metadata/modules/vidoomyBidAdapter.json | 2 +- metadata/modules/viouslyBidAdapter.json | 2 +- metadata/modules/visxBidAdapter.json | 2 +- metadata/modules/vlybyBidAdapter.json | 2 +- metadata/modules/voxBidAdapter.json | 2 +- metadata/modules/vrtcalBidAdapter.json | 2 +- metadata/modules/vuukleBidAdapter.json | 2 +- metadata/modules/weboramaRtdProvider.json | 6 +- metadata/modules/welectBidAdapter.json | 2 +- metadata/modules/yahooAdsBidAdapter.json | 2 +- metadata/modules/yaleoBidAdapter.json | 18 + metadata/modules/yieldlabBidAdapter.json | 2 +- metadata/modules/yieldloveBidAdapter.json | 2 +- metadata/modules/yieldmoBidAdapter.json | 2 +- metadata/modules/zeotapIdPlusIdSystem.json | 2 +- metadata/modules/zeta_globalBidAdapter.json | 2 +- .../modules/zeta_global_sspBidAdapter.json | 2 +- metadata/overrides.mjs | 3 +- modules/.submodules.json | 14 +- modules/33acrossAnalyticsAdapter.js | 5 +- modules/33acrossBidAdapter.js | 44 +- modules/33acrossIdSystem.js | 4 +- modules/51DegreesRtdProvider.js | 23 +- modules/51DegreesRtdProvider.md | 10 +- modules/AsteriobidPbmAnalyticsAdapter.js | 17 +- modules/_moduleMetadata.js | 8 +- modules/a1MediaBidAdapter.js | 4 +- modules/a1MediaRtdProvider.js | 4 +- modules/a4gBidAdapter.js | 2 +- modules/aaxBlockmeterRtdProvider.js | 4 +- modules/ablidaBidAdapter.js | 8 +- modules/aceexBidAdapter.js | 97 + modules/aceexBidAdapter.md | 67 + modules/adWMGAnalyticsAdapter.js | 2 +- modules/adWMGBidAdapter.js | 2 +- modules/adagioAnalyticsAdapter.js | 2 +- modules/adagioBidAdapter.js | 3 +- modules/adagioRtdProvider.js | 3 +- modules/adclusterBidAdapter.js | 183 + modules/adclusterBidAdapter.md | 46 + modules/addefendBidAdapter.js | 2 +- modules/adfBidAdapter.js | 16 +- modules/adgenerationBidAdapter.js | 4 +- modules/adgridBidAdapter.ts | 2 +- modules/adhashBidAdapter.js | 2 +- modules/adheseBidAdapter.js | 2 +- modules/adipoloBidAdapter.js | 6 +- modules/adkernelAdnAnalyticsAdapter.js | 22 +- modules/adkernelAdnBidAdapter.js | 14 +- modules/adkernelBidAdapter.js | 133 +- modules/adlooxAdServerVideo.js | 30 +- modules/adlooxAnalyticsAdapter.js | 31 +- modules/adlooxRtdProvider.js | 59 +- modules/admaruBidAdapter.js | 4 +- modules/admaticBidAdapter.js | 1 + modules/admediaBidAdapter.js | 4 +- modules/admixerBidAdapter.js | 31 +- modules/admixerIdSystem.js | 12 +- modules/adnimationBidAdapter.js | 47 + modules/adnimationBidAdapter.md | 36 + modules/adnowBidAdapter.js | 12 +- modules/adnuntiusAnalyticsAdapter.js | 17 +- modules/adnuntiusBidAdapter.js | 49 +- modules/adoceanBidAdapter.js | 22 +- modules/adotBidAdapter.js | 3 +- modules/adplayerproVideoProvider.js | 10 +- modules/adpod.js | 657 --- modules/adponeBidAdapter.js | 6 +- modules/adqueryBidAdapter.js | 6 +- modules/adqueryIdSystem.js | 18 +- modules/adrelevantisBidAdapter.js | 39 +- modules/adrinoBidAdapter.js | 8 +- modules/adriverBidAdapter.js | 6 +- modules/adriverIdSystem.js | 10 +- modules/adspiritBidAdapter.js | 6 +- modules/adtargetBidAdapter.js | 10 +- modules/adtelligentBidAdapter.js | 41 +- modules/adtelligentIdSystem.js | 2 +- modules/adtrgtmeBidAdapter.js | 3 +- modules/adtrueBidAdapter.js | 60 +- modules/aduptechBidAdapter.js | 8 +- modules/advRedAnalyticsAdapter.js | 12 +- modules/advertisingBidAdapter.js | 16 +- modules/adverxoBidAdapter.js | 22 +- modules/adxcgAnalyticsAdapter.js | 4 +- modules/adxpremiumAnalyticsAdapter.js | 4 +- modules/adyoulikeBidAdapter.js | 8 +- modules/afpBidAdapter.js | 22 +- modules/agmaAnalyticsAdapter.js | 4 +- modules/aidemBidAdapter.js | 22 +- modules/airgridRtdProvider.js | 12 +- modules/ajaBidAdapter.js | 4 +- modules/alkimiBidAdapter.js | 24 +- modules/allegroBidAdapter.js | 16 +- modules/alliance_gravityBidAdapter.md | 33 + modules/alliance_gravityBidAdapter.ts | 81 + modules/allowActivities.js | 8 +- modules/ampliffyBidAdapter.js | 8 +- modules/amxBidAdapter.js | 5 +- modules/amxIdSystem.js | 22 +- modules/anonymisedRtdProvider.js | 14 +- modules/anyclipBidAdapter.js | 8 +- modules/apacdexBidAdapter.js | 6 +- modules/apesterBidAdapter.js | 49 + modules/apesterBidAdapter.md | 36 + modules/appierAnalyticsAdapter.js | 14 +- modules/appnexusBidAdapter.js | 142 +- modules/apsBidAdapter.js | 10 +- modules/apstreamBidAdapter.js | 6 +- modules/arcspanRtdProvider.js | 2 +- modules/asoBidAdapter.js | 24 +- modules/asteriobidAnalyticsAdapter.js | 5 +- modules/astraoneBidAdapter.js | 2 +- modules/atsAnalyticsAdapter.js | 18 +- modules/automatadAnalyticsAdapter.js | 6 +- modules/automatadBidAdapter.js | 12 +- modules/axonixBidAdapter.js | 12 +- modules/beachfrontBidAdapter.js | 15 +- modules/bedigitechBidAdapter.js | 8 +- modules/beopBidAdapter.js | 91 +- modules/betweenBidAdapter.js | 8 +- modules/bidResponseFilter/index.js | 20 +- modules/bidViewability.js | 68 +- modules/bidViewability.md | 3 +- modules/bidViewabilityIO.js | 10 +- modules/biddoBidAdapter.js | 4 +- modules/bidglassBidAdapter.js | 4 +- modules/bidmaticBidAdapter.js | 53 +- modules/bidtheatreBidAdapter.js | 6 +- modules/big-richmediaBidAdapter.js | 10 +- modules/bitmediaBidAdapter.js | 22 +- modules/blueconicRtdProvider.js | 12 +- modules/bmtmBidAdapter.js | 2 +- modules/bridBidAdapter.js | 14 +- modules/bridgewellBidAdapter.js | 84 +- modules/browsiBidAdapter.js | 18 +- modules/buzzoolaBidAdapter.js | 10 +- modules/byDataAnalyticsAdapter.js | 8 +- modules/cadent_aperture_mxBidAdapter.js | 25 +- modules/categoryTranslation.js | 105 - modules/ccxBidAdapter.js | 22 +- modules/chromeAiRtdProvider.js | 6 + modules/chtnwBidAdapter.js | 6 +- modules/clickforceBidAdapter.js | 4 +- modules/clickioBidAdapter.js | 12 +- modules/clydoBidAdapter.js | 8 +- modules/codefuelBidAdapter.js | 8 +- modules/cointrafficBidAdapter.js | 2 +- modules/coinzillaBidAdapter.js | 2 +- modules/colombiaBidAdapter.js | 4 +- modules/colossussspBidAdapter.js | 2 +- modules/conceptxBidAdapter.js | 279 +- modules/concertAnalyticsAdapter.js | 4 +- modules/concertBidAdapter.js | 3 +- modules/connatixBidAdapter.js | 76 +- modules/connectIdSystem.js | 22 +- modules/connectadBidAdapter.js | 8 +- modules/consentManagementGpp.ts | 20 +- modules/consentManagementTcf.ts | 26 +- modules/consentManagementUsp.ts | 28 +- modules/consumableBidAdapter.js | 2 +- modules/contxtfulRtdProvider.js | 2 + modules/conversantBidAdapter.ts | 18 +- modules/craftBidAdapter.js | 27 +- modules/criteoBidAdapter.js | 50 +- modules/currency.ts | 34 +- modules/cwireBidAdapter.js | 3 +- modules/czechAdIdSystem.js | 4 +- modules/dacIdSystem.js | 4 +- modules/dailyhuntBidAdapter.js | 33 +- modules/dataControllerModule/index.js | 14 +- modules/datablocksBidAdapter.js | 71 +- modules/datawrkzBidAdapter.js | 6 +- modules/dchain.ts | 10 +- modules/debugging/bidInterceptor.js | 52 +- modules/debugging/debugging.js | 59 +- modules/debugging/index.js | 16 +- modules/debugging/legacy.js | 16 +- modules/debugging/pbsInterceptor.js | 17 +- modules/debugging/responses.js | 2 +- modules/debugging/standalone.js | 2 +- modules/deepintentBidAdapter.js | 12 +- modules/deepintentDpesIdSystem.js | 8 +- modules/defineMediaBidAdapter.js | 6 +- modules/deltaprojectsBidAdapter.js | 2 +- modules/dfpAdServerVideo.js | 11 - modules/dfpAdpod.js | 10 - modules/dianomiBidAdapter.js | 2 +- modules/digitalMatterBidAdapter.js | 22 +- modules/digitalcaramelBidAdapter.js | 4 +- modules/discoveryBidAdapter.js | 2 +- modules/displayioBidAdapter.js | 20 +- modules/distroscaleBidAdapter.js | 8 +- modules/djaxBidAdapter.js | 6 +- modules/dmdIdSystem.js | 104 - modules/dmdIdSystem.md | 26 - modules/docereeBidAdapter.js | 4 +- modules/dpaiBidAdapter.js | 19 + modules/dpaiBidAdapter.md | 79 + modules/driftpixelBidAdapter.js | 6 +- modules/dsaControl.js | 12 +- modules/dspxBidAdapter.js | 8 +- modules/dvgroupBidAdapter.js | 6 +- modules/dxkultureBidAdapter.js | 12 +- modules/dxtechBidAdapter.js | 8 +- modules/eightPodAnalyticsAdapter.js | 14 +- modules/empowerBidAdapter.js | 520 +- modules/engageyaBidAdapter.js | 2 +- modules/enrichmentLiftMeasurement/index.js | 20 +- modules/eplanningBidAdapter.js | 19 +- modules/eskimiBidAdapter.js | 24 +- modules/etargetBidAdapter.js | 4 +- modules/euidIdSystem.js | 8 +- modules/excoBidAdapter.js | 4 +- modules/experianRtdProvider.js | 4 +- modules/express.js | 210 - modules/fabrickIdSystem.js | 4 +- modules/fanBidAdapter.js | 6 +- modules/feedadBidAdapter.js | 10 +- modules/finativeBidAdapter.js | 12 +- modules/fintezaAnalyticsAdapter.js | 14 +- modules/flippBidAdapter.js | 10 +- modules/floxisBidAdapter.js | 149 + modules/floxisBidAdapter.md | 59 + modules/fpdModule/index.js | 14 +- modules/freeWheelAdserverVideo.js | 19 - modules/freepassBidAdapter.js | 10 +- modules/freepassIdSystem.js | 2 +- modules/ftrackIdSystem.js | 12 +- modules/fwsspBidAdapter.js | 2 +- modules/gamAdServerVideo.js | 89 +- modules/gamAdpod.js | 95 - modules/gamoshiBidAdapter.js | 19 +- modules/gemiusIdSystem.ts | 10 +- modules/genericAnalyticsAdapter.ts | 24 +- modules/geolocationRtdProvider.ts | 8 +- modules/getintentBidAdapter.js | 2 +- modules/gjirafaBidAdapter.js | 2 +- modules/glomexBidAdapter.js | 7 +- modules/gmosspBidAdapter.js | 3 - modules/gnetBidAdapter.js | 4 +- modules/goldbachBidAdapter.js | 4 +- modules/gppControl_usnat.js | 4 +- modules/gppControl_usstates.ts | 14 +- modules/gptPreAuction.ts | 27 +- modules/gravitoIdSystem.js | 10 +- modules/greenbidsAnalyticsAdapter.js | 16 +- modules/gridBidAdapter.js | 18 +- modules/growadsBidAdapter.js | 6 +- modules/growthCodeAnalyticsAdapter.js | 21 +- modules/growthCodeIdSystem.js | 6 +- modules/growthCodeRtdProvider.js | 4 +- modules/gumgumBidAdapter.js | 25 +- modules/h12mediaBidAdapter.js | 7 +- modules/hadronAnalyticsAdapter.js | 17 +- modules/hadronIdSystem.js | 18 +- modules/hadronRtdProvider.js | 22 +- modules/harionBidAdapter.js | 25 + modules/harionBidAdapter.md | 79 + modules/holidBidAdapter.js | 197 +- modules/humansecurityRtdProvider.js | 180 - modules/humansecurityRtdProvider.md | 28 +- modules/humansecurityRtdProvider.ts | 204 + modules/hybridBidAdapter.js | 11 +- modules/hypelabBidAdapter.js | 7 +- modules/iasRtdProvider.js | 10 +- modules/id5AnalyticsAdapter.js | 8 +- modules/id5IdSystem.js | 57 +- modules/id5IdSystem.md | 40 +- modules/idImportLibrary.js | 2 +- modules/identityLinkIdSystem.js | 8 +- modules/idxBidAdapter.js | 2 +- modules/idxIdSystem.js | 6 +- modules/illuminBidAdapter.js | 8 +- modules/imRtdProvider.js | 24 +- modules/impactifyBidAdapter.js | 2 +- modules/improvedigitalBidAdapter.js | 32 +- modules/imuIdSystem.js | 6 +- modules/insticatorBidAdapter.js | 135 +- modules/instreamTracking.js | 4 +- modules/insuradsBidAdapter.md | 55 + modules/insuradsBidAdapter.ts | 149 + modules/integr8BidAdapter.js | 2 +- modules/intentIqAnalyticsAdapter.js | 93 +- modules/intentIqAnalyticsAdapter.md | 67 +- modules/intentIqIdSystem.js | 81 +- modules/intentIqIdSystem.md | 11 +- modules/interactiveOffersBidAdapter.js | 8 +- modules/intersectionRtdProvider.js | 118 - modules/invamiaBidAdapter.js | 4 +- modules/invibesBidAdapter.js | 10 +- modules/invisiblyAnalyticsAdapter.js | 5 - modules/iqxBidAdapter.js | 6 +- modules/ivsBidAdapter.js | 4 +- modules/ixBidAdapter.js | 91 +- modules/ixBidAdapter.md | 5 - modules/jixieBidAdapter.js | 24 +- modules/justIdSystem.js | 4 +- modules/jwplayerBidAdapter.js | 2 +- modules/jwplayerRtdProvider.js | 10 +- modules/jwplayerVideoProvider.js | 2 +- modules/kargoBidAdapter.js | 19 +- modules/kimberliteBidAdapter.js | 6 +- modules/kinessoIdSystem.js | 10 +- modules/koblerBidAdapter.js | 13 +- modules/kubientBidAdapter.js | 8 +- modules/kueezRtbBidAdapter.js | 10 +- modules/lassoBidAdapter.js | 2 +- modules/leagueMBidAdapter.js | 17 + modules/leagueMBidAdapter.md | 54 + modules/lemmaDigitalBidAdapter.js | 2 +- modules/lifestreetBidAdapter.js | 6 +- modules/limelightDigitalBidAdapter.js | 209 +- modules/liveIntentAnalyticsAdapter.js | 6 +- modules/liveIntentRtdProvider.js | 2 +- modules/livewrappedAnalyticsAdapter.js | 17 +- modules/livewrappedBidAdapter.js | 21 +- modules/lkqdBidAdapter.js | 5 - modules/lm_kiviadsBidAdapter.js | 6 +- modules/locIdSystem.js | 676 +++ modules/locIdSystem.md | 250 + modules/lockerdomeBidAdapter.js | 6 +- modules/logicadBidAdapter.js | 20 +- modules/loopmeBidAdapter.js | 16 +- modules/lotamePanoramaIdSystem.js | 6 +- modules/luceadBidAdapter.js | 39 +- modules/luponmediaBidAdapter.js | 12 +- modules/madvertiseBidAdapter.js | 4 +- modules/magniteAnalyticsAdapter.js | 16 +- modules/magniteBidAdapter.js | 363 ++ modules/magniteBidAdapter.md | 85 + modules/malltvAnalyticsAdapter.js | 12 +- modules/malltvBidAdapter.js | 2 +- modules/mantisBidAdapter.js | 10 +- modules/marsmediaBidAdapter.js | 13 +- modules/mediaeyesBidAdapter.js | 60 +- modules/mediaeyesBidAdapter.md | 21 + modules/mediaforceBidAdapter.js | 6 +- modules/mediafuseBidAdapter.js | 1744 +++---- modules/mediafuseBidAdapter.md | 8 +- modules/mediagoBidAdapter.js | 46 +- modules/mediakeysBidAdapter.js | 15 +- modules/medianetAnalyticsAdapter.js | 35 +- modules/medianetBidAdapter.js | 71 +- modules/medianetBidAdapter.md | 20 - modules/medianetRtdProvider.js | 14 +- modules/mediasniperBidAdapter.js | 4 +- modules/mediasquareBidAdapter.js | 39 +- modules/merkleIdSystem.js | 22 +- modules/mgidBidAdapter.js | 16 +- modules/mgidRtdProvider.js | 10 +- modules/michaoBidAdapter.ts | 2 +- modules/microadBidAdapter.js | 18 +- modules/mileBidAdapter.md | 72 + modules/mileBidAdapter.ts | 428 ++ modules/minutemediaBidAdapter.js | 6 +- modules/missenaBidAdapter.js | 8 + modules/mobianRtdProvider.js | 47 +- modules/mobianRtdProvider.md | 30 + modules/mobilefuseBidAdapter.js | 14 +- modules/mobkoiAnalyticsAdapter.js | 13 +- modules/mobkoiBidAdapter.js | 4 +- modules/msftBidAdapter.js | 19 +- modules/msftBidAdapter.md | 2 +- modules/multibid/index.ts | 22 +- modules/mwOpenLinkIdSystem.js | 8 +- modules/my6senseBidAdapter.js | 2 +- modules/nativeRendering.js | 10 +- modules/nativoBidAdapter.js | 1 + modules/naveggIdSystem.js | 6 +- modules/netIdSystem.js | 2 +- modules/neuwoRtdProvider.js | 793 ++- modules/neuwoRtdProvider.md | 279 +- modules/nextMillenniumBidAdapter.js | 34 +- modules/nextrollBidAdapter.js | 23 +- modules/nexverseBidAdapter.js | 13 +- modules/nexx360BidAdapter.ts | 10 +- modules/nobidAnalyticsAdapter.js | 12 +- modules/nobidBidAdapter.js | 10 +- modules/nodalsAiRtdProvider.js | 6 +- modules/novatiqIdSystem.js | 14 +- modules/oguryBidAdapter.js | 8 +- modules/omnidexBidAdapter.js | 10 +- modules/omsBidAdapter.js | 33 +- modules/oneKeyIdSystem.js | 4 +- modules/onetagBidAdapter.js | 21 +- modules/onomagicBidAdapter.js | 21 +- modules/opaMarketplaceBidAdapter.js | 10 +- modules/open8BidAdapter.js | 6 +- modules/openPairIdSystem.js | 16 +- modules/openwebBidAdapter.js | 6 +- modules/openxBidAdapter.js | 52 +- modules/operaadsBidAdapter.js | 16 +- modules/operaadsIdSystem.js | 2 +- modules/oprxBidAdapter.js | 2 +- modules/opscoBidAdapter.js | 18 +- modules/optableBidAdapter.js | 67 - modules/optableBidAdapter.md | 41 - modules/optableRtdProvider.js | 22 +- modules/optidigitalBidAdapter.js | 30 +- modules/optoutBidAdapter.js | 330 +- modules/orbitsoftBidAdapter.js | 6 +- modules/otmBidAdapter.js | 2 +- modules/outbrainBidAdapter.js | 20 +- modules/oxxionAnalyticsAdapter.js | 8 +- modules/oxxionRtdProvider.js | 4 +- modules/ozoneBidAdapter.js | 102 +- modules/paapi.js | 808 --- modules/paapiForGpt.js | 166 - modules/paapiForGpt.md | 123 - modules/padsquadBidAdapter.js | 10 +- modules/pairIdSystem.js | 10 +- modules/pangleBidAdapter.js | 2 +- modules/panxoBidAdapter.js | 358 ++ modules/panxoBidAdapter.md | 229 + modules/panxoRtdProvider.js | 227 + modules/panxoRtdProvider.md | 45 + modules/performaxBidAdapter.js | 160 +- modules/permutiveIdentityManagerIdSystem.js | 16 +- modules/permutiveRtdProvider.js | 16 +- .../prebidServerBidAdapter/bidderConfig.js | 28 +- modules/prebidServerBidAdapter/index.ts | 64 +- .../prebidServerBidAdapter/ortbConverter.js | 78 +- modules/previousAuctionInfo/index.js | 10 +- modules/priceFloors.ts | 100 +- modules/prismaBidAdapter.js | 14 +- modules/programmaticXBidAdapter.js | 8 +- modules/programmaticaBidAdapter.js | 4 +- modules/proxistoreBidAdapter.js | 239 +- modules/pubProvidedIdSystem.js | 8 +- modules/pubgeniusBidAdapter.js | 10 +- modules/publicGoodBidAdapter.js | 6 +- modules/publinkIdSystem.js | 18 +- modules/pubmaticAnalyticsAdapter.js | 2 +- modules/pubmaticBidAdapter.js | 47 +- modules/pubstackBidAdapter.md | 30 + modules/pubstackBidAdapter.ts | 156 + modules/pubwiseAnalyticsAdapter.js | 20 +- modules/pulsepointBidAdapter.js | 4 +- modules/pwbidBidAdapter.js | 4 +- modules/pxyzBidAdapter.js | 6 +- modules/qortexRtdProvider.js | 8 +- modules/quantcastBidAdapter.js | 297 -- modules/quantcastBidAdapter.md | 74 - modules/quantcastIdSystem.js | 230 - modules/quantcastIdSystem.md | 46 - modules/r2b2AnalyticsAdapter.js | 26 +- modules/r2b2BidAdapter.js | 18 +- modules/raveltechRtdProvider.js | 14 +- modules/readpeakBidAdapter.js | 4 +- modules/reconciliationRtdProvider.js | 9 +- modules/relaidoBidAdapter.js | 2 +- modules/relevadRtdProvider.js | 22 +- modules/relevantdigitalBidAdapter.js | 18 +- modules/responsiveAdsBidAdapter.js | 2 +- modules/retailspotBidAdapter.js | 6 +- modules/revantageBidAdapter.js | 407 ++ modules/revantageBidAdapter.md | 34 + modules/revcontentBidAdapter.js | 14 +- modules/revnewBidAdapter.ts | 10 +- modules/rewardedInterestIdSystem.js | 6 +- modules/rhythmoneBidAdapter.js | 4 +- modules/richaudienceBidAdapter.js | 14 +- modules/ringieraxelspringerBidAdapter.js | 8 - modules/ringieraxelspringerBidAdapter.md | 8 - modules/riseBidAdapter.js | 6 +- modules/rivrAnalyticsAdapter.js | 8 +- modules/robustAppsBidAdapter.js | 6 +- modules/roxotAnalyticsAdapter.js | 14 +- modules/rtbhouseBidAdapter.js | 18 +- modules/rtbhouseBidAdapter.md | 43 - modules/rtbsapeBidAdapter.js | 10 +- modules/rtdModule/index.ts | 31 +- modules/rtdModule/spec.ts | 15 +- modules/rubiconBidAdapter.js | 135 +- modules/rules/index.ts | 506 ++ modules/rumbleBidAdapter.js | 4 +- modules/s2sTesting.js | 10 +- modules/scaleableAnalyticsAdapter.js | 6 +- modules/scaliburBidAdapter.js | 21 +- modules/schain.ts | 8 +- modules/scope3RtdProvider.md | 118 +- modules/scope3RtdProvider.ts | 6 +- modules/screencoreBidAdapter.js | 8 +- modules/seedingAllianceBidAdapter.js | 16 +- modules/seedtagBidAdapter.js | 38 +- modules/sevioBidAdapter.js | 49 +- modules/sharedIdSystem.ts | 32 +- modules/sharethroughAnalyticsAdapter.js | 2 +- modules/sharethroughBidAdapter.js | 18 +- modules/shinezBidAdapter.js | 6 +- modules/shinezRtbBidAdapter.js | 8 +- modules/showheroes-bsBidAdapter.js | 2 +- modules/silvermobBidAdapter.js | 4 +- modules/silverpushBidAdapter.js | 23 +- modules/sizeMapping.js | 16 +- modules/sizeMappingV2.js | 4 +- modules/slimcutBidAdapter.js | 4 +- modules/smaatoBidAdapter.js | 211 +- modules/smarthubBidAdapter.js | 30 +- modules/smarticoBidAdapter.js | 6 +- modules/smartxBidAdapter.js | 2 +- modules/smartyadsAnalyticsAdapter.js | 4 +- modules/smartytechBidAdapter.js | 16 +- modules/smilewantedBidAdapter.js | 14 +- modules/snigelBidAdapter.js | 16 +- modules/sonaradsBidAdapter.js | 10 +- modules/sovrnBidAdapter.js | 7 +- modules/sparteoBidAdapter.js | 5 +- modules/ssmasBidAdapter.js | 2 +- modules/sspBCBidAdapter.js | 11 +- modules/ssp_genieeBidAdapter.js | 8 +- modules/stackadaptBidAdapter.js | 2 +- modules/startioBidAdapter.js | 2 +- modules/stnBidAdapter.js | 6 +- modules/storageControl.ts | 41 +- modules/stroeerCoreBidAdapter.js | 60 +- modules/stvBidAdapter.js | 6 +- modules/symitriAnalyticsAdapter.js | 6 +- modules/symitriDapRtdProvider.js | 36 +- modules/taboolaBidAdapter.js | 312 +- modules/taboolaIdSystem.js | 26 +- modules/tadvertisingBidAdapter.js | 16 +- modules/tagorasBidAdapter.js | 8 +- modules/talkadsBidAdapter.js | 4 +- modules/tappxBidAdapter.js | 6 +- modules/targetVideoAdServerVideo.js | 4 +- modules/targetVideoBidAdapter.js | 38 +- modules/targetVideoBidAdapter.md | 1 + modules/tcfControl.ts | 26 +- modules/teadsBidAdapter.js | 21 +- modules/teadsIdSystem.js | 18 +- modules/tealBidAdapter.js | 18 +- modules/teqBlazeSalesAgentBidAdapter.js | 41 + modules/teqBlazeSalesAgentBidAdapter.md | 79 + modules/terceptAnalyticsAdapter.js | 2 +- modules/theAdxBidAdapter.js | 2 +- modules/tncIdSystem.js | 2 +- modules/topLevelPaapi.js | 214 - modules/topicsFpdModule.js | 34 +- modules/tpmnBidAdapter.js | 6 +- modules/trafficgateBidAdapter.js | 18 +- modules/trionBidAdapter.js | 12 +- modules/tripleliftBidAdapter.js | 30 +- modules/trustxBidAdapter.js | 16 +- modules/ttdBidAdapter.js | 9 +- modules/twistDigitalBidAdapter.js | 8 +- modules/ucfunnelAnalyticsAdapter.js | 12 +- modules/ucfunnelBidAdapter.js | 10 +- modules/uid2IdSystem.js | 8 +- modules/underdogmediaBidAdapter.js | 17 +- modules/undertoneBidAdapter.js | 15 +- modules/unicornBidAdapter.js | 10 +- modules/unifiedIdSystem.js | 10 +- modules/uniquestAnalyticsAdapter.js | 12 +- modules/uniquestBidAdapter.js | 10 +- modules/uniquest_widgetBidAdapter.js | 10 +- modules/unrulyBidAdapter.js | 48 +- modules/userId/eids.js | 6 +- modules/userId/eids.md | 8 + modules/userId/index.ts | 100 +- modules/userId/spec.ts | 10 +- modules/userId/userId.md | 20 +- modules/validationFpdModule/index.ts | 12 +- modules/valuadBidAdapter.js | 9 +- modules/ventesBidAdapter.js | 13 +- modules/verbenBidAdapter.js | 17 + modules/verbenBidAdapter.md | 79 + modules/viantBidAdapter.js | 14 +- modules/vibrantmediaBidAdapter.js | 8 +- modules/vidazooBidAdapter.js | 10 +- modules/videoModule/adQueue.js | 2 +- modules/videoModule/index.ts | 44 +- modules/videobyteBidAdapter.js | 10 +- modules/videojsVideoProvider.js | 6 +- modules/videonowBidAdapter.js | 8 +- modules/videoreachBidAdapter.js | 4 +- modules/vidoomyBidAdapter.js | 14 +- modules/viewdeosDXBidAdapter.js | 10 +- modules/vistarsBidAdapter.js | 4 +- modules/visxBidAdapter.js | 29 +- modules/voxBidAdapter.js | 11 +- modules/vrtcalBidAdapter.js | 10 +- modules/vuukleBidAdapter.js | 4 +- modules/waardexBidAdapter.js | 13 +- modules/weboramaRtdProvider.js | 1 + modules/widespaceBidAdapter.js | 17 +- modules/winrBidAdapter.js | 18 +- modules/wipesBidAdapter.js | 6 +- modules/wurflRtdProvider.js | 76 +- modules/wurflRtdProvider.md | 33 +- modules/xeBidAdapter.js | 6 +- modules/yahooAdsBidAdapter.js | 11 +- modules/yahooAdsBidAdapter.md | 35 + modules/yaleoBidAdapter.md | 39 + modules/yaleoBidAdapter.ts | 80 + modules/yandexBidAdapter.js | 3 +- modules/yandexBidAdapter.md | 36 +- modules/yieldlabBidAdapter.js | 3 +- modules/yieldliftBidAdapter.js | 6 +- modules/yieldloveBidAdapter.js | 4 +- modules/yieldmoBidAdapter.js | 4 +- modules/yieldoneAnalyticsAdapter.js | 16 +- modules/yieldoneBidAdapter.js | 24 +- modules/yuktamediaAnalyticsAdapter.js | 12 +- modules/zeotapIdPlusIdSystem.js | 8 +- modules/zeta_globalBidAdapter.js | 10 +- modules/zeta_global_sspAnalyticsAdapter.js | 101 +- modules/zeta_global_sspBidAdapter.js | 18 +- modules/zmaticooBidAdapter.js | 10 +- package-lock.json | 874 ++-- package.json | 3 +- plugins/buildOptions.js | 2 +- .../eslint/approvedLoadExternalScriptPaths.js | 48 + scope3_segtax_pr.md | 35 - src/Renderer.js | 2 +- src/activities/activities.js | 5 + src/activities/activityParams.js | 2 +- src/activities/params.js | 4 +- src/activities/redactor.ts | 10 +- src/activities/rules.js | 10 +- src/adRendering.ts | 106 +- src/adUnits.ts | 15 +- src/adapterManager.ts | 155 +- src/adapters/bidderFactory.ts | 112 +- src/adloader.js | 80 +- src/adserver.js | 2 +- src/ajax.ts | 30 +- src/auction.ts | 117 +- src/auctionIndex.js | 15 +- src/auctionManager.js | 10 +- src/audio.ts | 54 +- src/banner.ts | 30 +- src/bidTTL.ts | 4 +- src/bidderSettings.ts | 12 +- src/bidfactory.ts | 23 +- src/config.ts | 93 +- src/consentHandler.ts | 12 +- src/constants.ts | 7 - src/creativeRenderers.js | 8 +- src/debugging.js | 40 +- src/eventTrackers.js | 3 +- src/events.ts | 8 +- src/fpd/enrichment.ts | 28 +- src/fpd/normalize.js | 4 +- src/fpd/oneClient.js | 2 +- src/fpd/rootDomain.js | 4 +- src/fpd/sua.js | 10 +- src/hook.ts | 12 +- src/mediaTypes.ts | 10 +- src/native.ts | 57 +- src/pbjsORTB.ts | 4 +- src/prebid.public.ts | 2 +- src/prebid.ts | 149 +- src/prebidGlobal.ts | 6 +- src/refererDetection.ts | 4 +- src/secureCreatives.js | 33 +- src/storageManager.ts | 34 +- src/targeting.ts | 73 +- src/targeting/lock.ts | 12 +- src/types/common.d.ts | 4 +- src/types/ortb/ext/dchain.d.ts | 2 +- src/types/ortb/native.d.ts | 2 +- src/types/ortb/request.d.ts | 6 +- src/types/ortb/response.d.ts | 8 +- src/types/summary/exports.d.ts | 18 +- src/userSync.ts | 16 +- src/utils.js | 36 +- src/utils/adUnits.ts | 21 + src/utils/cpm.js | 8 +- src/utils/objects.ts | 6 +- src/utils/perfMetrics.ts | 34 +- src/utils/prerendering.ts | 8 +- src/utils/promise.ts | 6 +- src/utils/ttlCollection.ts | 6 +- src/utils/winDimensions.js | 4 +- src/utils/yield.ts | 66 +- src/video.ts | 94 +- src/videoCache.ts | 24 +- test/build-logic/disclosure_spec.mjs | 6 +- test/build-logic/gvl_spec.mjs | 6 +- test/build-logic/no_3384_spec.mjs | 47 + test/fixtures/fixtures.js | 4 +- test/helpers/analytics.js | 4 +- test/helpers/consentData.js | 4 +- test/helpers/fpd.js | 8 +- test/helpers/global_hooks.js | 2 +- test/helpers/hookSetup.js | 2 +- test/helpers/indexStub.js | 4 +- test/helpers/index_adapter_utils.js | 6 +- test/helpers/pbjs-test-only.js | 2 +- test/helpers/prebidGlobal.js | 2 +- test/helpers/refererDetectionHelper.js | 2 +- test/helpers/testing-utils.js | 6 +- test/mocks/analyticsStub.js | 2 +- test/mocks/ortbConverter.js | 4 +- test/mocks/videoCacheStub.js | 2 +- test/mocks/xhr.js | 10 +- test/spec/AnalyticsAdapter_spec.js | 56 +- test/spec/activities/allowActivites_spec.js | 18 +- test/spec/activities/objectGuard_spec.js | 199 +- test/spec/activities/ortbGuard_spec.js | 32 +- test/spec/activities/params_spec.js | 8 +- test/spec/activities/redactor_spec.js | 32 +- test/spec/activities/rules_spec.js | 40 +- test/spec/adUnits_spec.js | 6 +- test/spec/adloader_spec.js | 49 +- test/spec/adserver_spec.js | 2 +- test/spec/aliasBidder_spec.js | 2 +- test/spec/api_spec.js | 4 +- test/spec/appnexusKeywords_spec.js | 4 +- test/spec/auctionmanager_spec.js | 200 +- test/spec/banner_spec.js | 24 +- test/spec/config_spec.js | 91 +- .../spec/creative/crossDomainCreative_spec.js | 36 +- test/spec/creative/displayRenderer_spec.js | 14 +- test/spec/creative/nativeRenderer_spec.js | 50 +- test/spec/debugging_spec.js | 23 +- test/spec/e2e/banner/basic_banner_ad.spec.js | 2 +- .../e2e/modules/e2e_bidderSettings.spec.js | 2 +- test/spec/fingerprinting_spec.js | 60 + test/spec/fpd/enrichment_spec.js | 38 +- test/spec/fpd/gdpr_spec.js | 10 +- test/spec/fpd/normalize_spec.js | 30 +- test/spec/fpd/oneClient_spec.js | 4 +- test/spec/fpd/rootDomain_spec.js | 6 +- test/spec/fpd/sua_spec.js | 4 +- test/spec/fpd/usp_spec.js | 4 +- test/spec/hook_spec.js | 2 +- test/spec/integration/faker/googletag.js | 2 +- test/spec/keywords_spec.js | 4 +- test/spec/libraries/autoplayDetection_spec.js | 2 +- .../libraries/bidViewabilityPixels_spec.js | 178 + .../spec/libraries/boundingClientRect_spec.js | 2 +- test/spec/libraries/cmUtils_spec.js | 58 +- test/spec/libraries/cmp/cmpClient_spec.js | 60 +- test/spec/libraries/currencyUtils_spec.js | 10 +- test/spec/libraries/dnt_spec.js | 34 - .../domainOverrideToRootDomain/index_spec.js | 6 +- .../libraries/gamPredictionReport_spec.js | 114 + .../libraries/greedy/greedyPromise_spec.js | 6 +- .../libraries/magniteUtils/outstream_spec.js | 157 + test/spec/libraries/metadata_spec.js | 6 +- .../libraries/mspa/activityControls_spec.js | 18 +- test/spec/libraries/percentInView_spec.js | 272 + .../libraries/placementPositionInfo_spec.js | 459 ++ .../precisoUtils/bidNativeUtils_spec.js | 2 +- .../precisoUtils/bidUtilsCommon_spec.js | 2 +- test/spec/libraries/processResponse_spec.js | 2 +- .../plugins/floorProvider_spec.js | 4 +- test/spec/libraries/sizeUtils_spec.js | 14 +- test/spec/libraries/storageDisclosure_spec.js | 9 +- .../teqblazeUtils/bidderUtils_spec.js | 8 +- test/spec/libraries/timeoutQueue_spec.js | 4 +- test/spec/libraries/urlUtils_spec.js | 2 +- test/spec/libraries/vastTrackers_spec.js | 24 +- test/spec/libraries/weakStore_spec.js | 8 +- test/spec/modules/1plusXRtdProvider_spec.js | 4 +- .../modules/33acrossAnalyticsAdapter_spec.js | 20 +- test/spec/modules/33acrossBidAdapter_spec.js | 144 +- test/spec/modules/33acrossIdSystem_spec.js | 151 +- .../spec/modules/360playvidBidAdapter_spec.js | 8 +- .../spec/modules/51DegreesRtdProvider_spec.js | 112 +- .../AsteriobidPbmAnalyticsAdapter_spec.js | 8 +- test/spec/modules/ViouslyBidAdapter_spec.js | 4 +- test/spec/modules/a1MediaBidAdapter_spec.js | 7 +- test/spec/modules/a1MediaRtdProvider_spec.js | 2 +- test/spec/modules/aaxBlockmeter_spec.js | 12 +- test/spec/modules/ablidaBidAdapter_spec.js | 12 +- test/spec/modules/aceexBidAdapter_spec.js | 248 + test/spec/modules/acuityadsBidAdapter_spec.js | 8 +- .../modules/adWMGAnalyticsAdapter_spec.js | 6 +- .../modules/adagioAnalyticsAdapter_spec.js | 32 +- test/spec/modules/adagioBidAdapter_spec.js | 60 +- test/spec/modules/adagioRtdProvider_spec.js | 13 +- test/spec/modules/adbroBidAdapter_spec.js | 62 +- test/spec/modules/adclusterBidAdapter_spec.js | 415 ++ test/spec/modules/addefendBidAdapter_spec.js | 18 +- test/spec/modules/adfBidAdapter_spec.js | 90 +- .../modules/adgenerationBidAdapter_spec.js | 42 +- test/spec/modules/adhashBidAdapter_spec.js | 8 +- test/spec/modules/adheseBidAdapter_spec.js | 72 +- test/spec/modules/adipoloBidAdapter_spec.js | 48 +- .../spec/modules/adkernelAdnAnalytics_spec.js | 14 +- .../modules/adkernelAdnBidAdapter_spec.js | 30 +- test/spec/modules/adkernelBidAdapter_spec.js | 165 +- test/spec/modules/adlaneRtdProvider_spec.js | 2 +- test/spec/modules/adlooxAdServerVideo_spec.js | 8 +- .../modules/adlooxAnalyticsAdapter_spec.js | 8 +- test/spec/modules/adlooxRtdProvider_spec.js | 24 +- test/spec/modules/admaticBidAdapter_spec.js | 136 +- test/spec/modules/admediaBidAdapter_spec.js | 10 +- .../spec/modules/adminationBidAdapter_spec.js | 774 +++ test/spec/modules/admixerBidAdapter_spec.js | 14 +- test/spec/modules/admixerIdSystem_spec.js | 14 +- test/spec/modules/adnowBidAdapter_spec.js | 4 +- .../modules/adnuntiusAnalyticsAdapter_spec.js | 4 +- test/spec/modules/adnuntiusBidAdapter_spec.js | 329 +- test/spec/modules/adoceanBidAdapter_spec.js | 69 +- test/spec/modules/adpartnerBidAdapter_spec.js | 26 +- test/spec/modules/adplusBidAdapter_spec.js | 6 +- test/spec/modules/adplusIdSystem_spec.js | 2 +- test/spec/modules/adpod_spec.js | 1238 ----- test/spec/modules/adponeBidAdapter_spec.js | 6 +- test/spec/modules/adprimeBidAdapter_spec.js | 8 +- test/spec/modules/adqueryBidAdapter_spec.js | 12 +- test/spec/modules/adqueryIdSystem_spec.js | 10 +- .../modules/adrelevantisBidAdapter_spec.js | 94 +- test/spec/modules/adrinoBidAdapter_spec.js | 36 +- test/spec/modules/adriverBidAdapter_spec.js | 6 +- .../modules/ads_interactiveBidAdapter_spec.js | 8 +- test/spec/modules/adstirBidAdapter_spec.js | 6 +- .../modules/adtelligentBidAdapter_spec.js | 51 +- test/spec/modules/adtrgtmeBidAdapter_spec.js | 98 +- test/spec/modules/adtrueBidAdapter_spec.js | 12 +- test/spec/modules/aduptechBidAdapter_spec.js | 2 +- .../modules/advRedAnalyticsAdapter_spec.js | 6 +- .../modules/advangelistsBidAdapter_spec.js | 21 +- .../modules/advertisingBidAdapter_spec.js | 4 +- test/spec/modules/adverxoBidAdapter_spec.js | 58 +- test/spec/modules/adxcgBidAdapter_spec.js | 8 +- test/spec/modules/adyoulikeBidAdapter_spec.js | 130 +- test/spec/modules/afpBidAdapter_spec.js | 6 +- test/spec/modules/aidemBidAdapter_spec.js | 34 +- test/spec/modules/airgridRtdProvider_spec.js | 6 +- test/spec/modules/ajaBidAdapter_spec.js | 10 +- test/spec/modules/allegroBidAdapter_spec.js | 46 +- .../alliance_gravityBidAdapter_spec.js | 536 ++ test/spec/modules/ampliffyBidAdapter_spec.js | 20 +- test/spec/modules/amxBidAdapter_spec.js | 2 +- test/spec/modules/amxIdSystem_spec.js | 6 +- .../modules/anonymisedRtdProvider_spec.js | 4 +- test/spec/modules/anyclipBidAdapter_spec.js | 48 +- test/spec/modules/apesterBidAdapter_spec.js | 774 +++ .../modules/appStockSSPBidAdapter_spec.js | 8 +- .../modules/appierAnalyticsAdapter_spec.js | 4 +- test/spec/modules/appierBidAdapter_spec.js | 6 +- test/spec/modules/appnexusBidAdapter_spec.js | 263 +- test/spec/modules/apsBidAdapter_spec.js | 24 + test/spec/modules/apstreamBidAdapter_spec.js | 12 +- test/spec/modules/asoBidAdapter_spec.js | 16 +- .../asteriobidAnalyticsAdapter_spec.js | 8 +- test/spec/modules/atsAnalyticsAdapter_spec.js | 20 +- .../modules/automatadAnalyticsAdapter_spec.js | 78 +- test/spec/modules/automatadBidAdapter_spec.js | 12 +- .../modules/azerionedgeRtdProvider_spec.js | 6 +- .../spec/modules/beachfrontBidAdapter_spec.js | 110 +- test/spec/modules/beopBidAdapter_spec.js | 218 +- test/spec/modules/betweenBidAdapter_spec.js | 8 +- .../modules/beyondmediaBidAdapter_spec.js | 8 +- test/spec/modules/bidResponseFilter_spec.js | 130 +- test/spec/modules/bidViewabilityIO_spec.js | 68 + test/spec/modules/bidViewability_spec.js | 186 +- test/spec/modules/biddoBidAdapter_spec.js | 20 +- test/spec/modules/bidfuseBidAdapter_spec.js | 12 +- test/spec/modules/bidmaticBidAdapter_spec.js | 8 +- .../spec/modules/bidtheatreBidAdapter_spec.js | 18 +- .../modules/big-richmediaBidAdapter_spec.js | 4 +- test/spec/modules/bitmediaBidAdapter_spec.js | 18 +- test/spec/modules/blastoBidAdapter_spec.js | 2 +- test/spec/modules/bliinkBidAdapter_spec.js | 24 +- .../spec/modules/blueconicRtdProvider_spec.js | 36 +- test/spec/modules/boldwinBidAdapter_spec.js | 8 +- .../modules/brandmetricsRtdProvider_spec.js | 8 +- test/spec/modules/braveBidAdapter_spec.js | 52 +- test/spec/modules/bridBidAdapter_spec.js | 8 +- .../spec/modules/bridgewellBidAdapter_spec.js | 200 +- test/spec/modules/browsiBidAdapter_spec.js | 30 +- test/spec/modules/bucksenseBidAdapter_spec.js | 10 +- test/spec/modules/buzzoolaBidAdapter_spec.js | 30 +- .../modules/byDataAnalyticsAdapter_spec.js | 12 +- .../cadent_aperture_mxBidAdapter_spec.js | 6 +- test/spec/modules/carodaBidAdapter_spec.js | 16 +- test/spec/modules/categoryTranslation_spec.js | 109 - test/spec/modules/ccxBidAdapter_spec.js | 106 +- test/spec/modules/chromeAiRtdProvider_spec.js | 65 + .../spec/modules/clickforceBidAdapter_spec.js | 4 +- test/spec/modules/codefuelBidAdapter_spec.js | 24 +- test/spec/modules/coinzillaBidAdapter_spec.js | 8 +- .../modules/colossussspBidAdapter_spec.js | 2 +- test/spec/modules/compassBidAdapter_spec.js | 8 +- test/spec/modules/conceptxBidAdapter_spec.js | 212 +- .../modules/concertAnalyticsAdapter_spec.js | 2 +- test/spec/modules/concertBidAdapter_spec.js | 7 +- test/spec/modules/connatixBidAdapter_spec.js | 290 +- test/spec/modules/connectIdSystem_spec.js | 102 +- test/spec/modules/connectadBidAdapter_spec.js | 30 +- .../spec/modules/consentManagementGpp_spec.js | 54 +- .../spec/modules/consentManagementUsp_spec.js | 22 +- test/spec/modules/consentManagement_spec.js | 32 +- .../spec/modules/consumableBidAdapter_spec.js | 24 +- .../modules/contentexchangeBidAdapter_spec.js | 8 +- .../spec/modules/conversantBidAdapter_spec.js | 87 +- .../spec/modules/copper6sspBidAdapter_spec.js | 8 +- test/spec/modules/craftBidAdapter_spec.js | 20 +- test/spec/modules/criteoBidAdapter_spec.js | 302 +- test/spec/modules/criteoIdSystem_spec.js | 8 +- test/spec/modules/currency_spec.js | 29 +- test/spec/modules/cwireBidAdapter_spec.js | 14 +- test/spec/modules/czechAdIdSystem_spec.js | 16 +- test/spec/modules/dacIdSystem_spec.js | 12 +- test/spec/modules/dataController_spec.js | 10 +- .../spec/modules/datablocksBidAdapter_spec.js | 8 +- test/spec/modules/datawrkzBidAdapter_spec.js | 186 +- test/spec/modules/debugging_mod_spec.js | 257 +- .../spec/modules/deepintentBidAdapter_spec.js | 45 +- .../modules/deepintentDpesIdsystem_spec.js | 12 +- .../modules/deltaprojectsBidAdapter_spec.js | 14 +- test/spec/modules/dianomiBidAdapter_spec.js | 2 +- .../modules/digitalMatterBidAdapter_spec.js | 18 +- test/spec/modules/discoveryBidAdapter_spec.js | 2 +- test/spec/modules/displayioBidAdapter_spec.js | 4 +- test/spec/modules/dmdIdSystem_spec.js | 101 - test/spec/modules/docereeBidAdapter_spec.js | 4 +- test/spec/modules/dpaiBidAdapter_spec.js | 511 ++ .../spec/modules/driftpixelBidAdapter_spec.js | 48 +- test/spec/modules/dsaControl_spec.js | 22 +- test/spec/modules/dspxBidAdapter_spec.js | 36 +- test/spec/modules/dxkultureBidAdapter_spec.js | 50 +- test/spec/modules/dxtechBidAdapter_spec.js | 48 +- test/spec/modules/eids_spec.js | 2 +- .../modules/eightPodAnalyticsAdapter_spec.js | 2 +- test/spec/modules/eightPodBidAdapter_spec.js | 3 +- test/spec/modules/emtvBidAdapter_spec.js | 8 +- .../modules/enrichmentLiftMeasurement_spec.js | 94 +- test/spec/modules/eplanningBidAdapter_spec.js | 44 +- test/spec/modules/escalaxBidAdapter_spec.js | 2 +- test/spec/modules/eskimiBidAdapter_spec.js | 14 +- test/spec/modules/etargetBidAdapter_spec.js | 16 +- test/spec/modules/euidIdSystem_spec.js | 40 +- test/spec/modules/experianRtdProvider_spec.js | 29 +- test/spec/modules/fabrickIdSystem_spec.js | 6 +- test/spec/modules/fanBidAdapter_spec.js | 2 +- test/spec/modules/feedadBidAdapter_spec.js | 102 +- test/spec/modules/finativeBidAdapter_spec.js | 18 +- .../modules/fintezaAnalyticsAdapter_spec.js | 2 +- test/spec/modules/flippBidAdapter_spec.js | 12 +- test/spec/modules/floxisBidAdapter_spec.js | 501 ++ test/spec/modules/fluctBidAdapter_spec.js | 8 +- test/spec/modules/fpdModule_spec.js | 14 +- .../modules/freeWheelAdserverVideo_spec.js | 350 -- test/spec/modules/freepassBidAdapter_spec.js | 6 +- test/spec/modules/ftrackIdSystem_spec.js | 30 +- test/spec/modules/gamAdServerVideo_spec.js | 393 +- test/spec/modules/gamAdpod_spec.js | 257 - test/spec/modules/gammaBidAdapter_spec.js | 2 +- test/spec/modules/gamoshiBidAdapter_spec.js | 82 +- test/spec/modules/gemiusIdSystem_spec.js | 18 +- .../modules/genericAnalyticsAdapter_spec.js | 52 +- .../modules/geolocationRtdProvider_spec.js | 18 +- test/spec/modules/getintentBidAdapter_spec.js | 4 +- test/spec/modules/glomexBidAdapter_spec.js | 4 +- test/spec/modules/gmosspBidAdapter_spec.js | 4 +- test/spec/modules/goldbachBidAdapter_spec.js | 20 +- .../modules/goldfishAdsRtdProvider_spec.js | 4 +- test/spec/modules/gppControl_usstates_spec.js | 22 +- test/spec/modules/gptPreAuction_spec.js | 33 +- test/spec/modules/gravitoIdSystem_spec.js | 4 +- .../modules/greenbidsAnalyticsAdapter_spec.js | 2 +- test/spec/modules/greenbidsBidAdapter_spec.js | 2 +- test/spec/modules/gridBidAdapter_spec.js | 188 +- test/spec/modules/growadsBidAdapter_spec.js | 14 +- .../growthCodeAnalyticsAdapter_spec.js | 1 - test/spec/modules/growthCodeIdSystem_spec.js | 42 +- .../modules/growthCodeRtdProvider_spec.js | 47 +- test/spec/modules/gumgumBidAdapter_spec.js | 44 +- test/spec/modules/h12mediaBidAdapter_spec.js | 70 +- .../modules/hadronAnalyticsAdapter_spec.js | 2 +- test/spec/modules/hadronIdSystem_spec.js | 12 +- test/spec/modules/hadronRtdProvider_spec.js | 38 +- test/spec/modules/harionBidAdapter_spec.js | 475 ++ test/spec/modules/holidBidAdapter_spec.js | 139 +- .../modules/humansecurityRtdProvider_spec.js | 117 +- test/spec/modules/hybridBidAdapter_spec.js | 6 +- test/spec/modules/hypelabBidAdapter_spec.js | 2 +- test/spec/modules/id5AnalyticsAdapter_spec.js | 18 +- test/spec/modules/id5IdSystem_spec.js | 427 +- test/spec/modules/idImportLibrary_spec.js | 12 +- .../spec/modules/identityLinkIdSystem_spec.js | 36 +- test/spec/modules/idxIdSystem_spec.js | 4 +- test/spec/modules/illuminBidAdapter_spec.js | 85 +- test/spec/modules/imRtdProvider_spec.js | 24 +- test/spec/modules/impactifyBidAdapter_spec.js | 2 +- .../modules/improvedigitalBidAdapter_spec.js | 106 +- test/spec/modules/imuIdSystem_spec.js | 16 +- test/spec/modules/inmobiBidAdapter_spec.js | 38 +- .../spec/modules/insticatorBidAdapter_spec.js | 659 ++- test/spec/modules/instreamTracking_spec.js | 24 +- test/spec/modules/insuradsBidAdapter_spec.js | 737 +++ .../modules/intentIqAnalyticsAdapter_spec.js | 232 +- test/spec/modules/intentIqIdSystem_spec.js | 260 +- .../interactiveOffersBidAdapter_spec.js | 16 +- .../modules/intersectionRtdProvider_spec.js | 164 - test/spec/modules/invamiaBidAdapter_spec.js | 20 +- test/spec/modules/invibesBidAdapter_spec.js | 118 +- .../modules/invisiblyAnalyticsAdapter_spec.js | 28 +- test/spec/modules/ipromBidAdapter_spec.js | 7 +- test/spec/modules/iqxBidAdapter_spec.js | 44 +- test/spec/modules/iqzoneBidAdapter_spec.js | 8 +- test/spec/modules/ixBidAdapter_spec.js | 301 +- test/spec/modules/jixieBidAdapter_spec.js | 10 +- test/spec/modules/jixieIdSystem_spec.js | 32 +- test/spec/modules/justIdSystem_spec.js | 8 +- .../modules/justpremiumBidAdapter_spec.js | 12 +- test/spec/modules/jwplayerBidAdapter_spec.js | 8 +- test/spec/modules/jwplayerRtdProvider_spec.js | 32 +- test/spec/modules/kargoBidAdapter_spec.js | 579 ++- .../spec/modules/kimberliteBidAdapter_spec.js | 6 +- test/spec/modules/kinessoIdSystem_spec.js | 20 +- test/spec/modules/kiviadsBidAdapter_spec.js | 8 +- test/spec/modules/koblerBidAdapter_spec.js | 58 +- .../spec/modules/krushmediaBidAdapter_spec.js | 8 +- test/spec/modules/kubientBidAdapter_spec.js | 22 +- test/spec/modules/kueezRtbBidAdapter_spec.js | 87 +- test/spec/modules/leagueMBidAdapter_spec.js | 441 ++ .../limelightDigitalBidAdapter_spec.js | 1298 +++-- .../liveIntentAnalyticsAdapter_spec.js | 4 +- .../liveIntentExternalIdSystem_spec.js | 94 +- .../modules/liveIntentIdMinimalSystem_spec.js | 108 +- test/spec/modules/liveIntentIdSystem_spec.js | 206 +- .../modules/liveIntentRtdProvider_spec.js | 10 +- .../livewrappedAnalyticsAdapter_spec.js | 5 +- .../modules/livewrappedBidAdapter_spec.js | 192 +- .../spec/modules/lm_kiviadsBidAdapter_spec.js | 44 +- test/spec/modules/lmpIdSystem_spec.js | 4 +- test/spec/modules/locIdSystem_spec.js | 2090 ++++++++ test/spec/modules/loganBidAdapter_spec.js | 4 +- test/spec/modules/logicadBidAdapter_spec.js | 69 +- test/spec/modules/loopmeBidAdapter_spec.js | 32 +- .../modules/lotamePanoramaIdSystem_spec.js | 4 +- test/spec/modules/luceadBidAdapter_spec.js | 30 +- .../modules/lunamediahbBidAdapter_spec.js | 12 +- test/spec/modules/mabidderBidAdapter_spec.js | 2 +- .../spec/modules/madvertiseBidAdapter_spec.js | 36 +- .../modules/magniteAnalyticsAdapter_spec.js | 17 +- test/spec/modules/magniteBidAdapter_spec.js | 924 ++++ .../modules/malltvAnalyticsAdapter_spec.js | 2 +- test/spec/modules/mantisBidAdapter_spec.js | 24 +- test/spec/modules/marsmediaBidAdapter_spec.js | 26 +- .../modules/mathildeadsBidAdapter_spec.js | 8 +- .../modules/mediaConsortiumBidAdapter_spec.js | 50 +- .../spec/modules/mediabramaBidAdapter_spec.js | 4 +- test/spec/modules/mediaeyesBidAdapter_spec.js | 84 +- .../spec/modules/mediaforceBidAdapter_spec.js | 57 +- test/spec/modules/mediafuseBidAdapter_spec.js | 2942 ++++++----- test/spec/modules/mediagoBidAdapter_spec.js | 13 + .../modules/mediaimpactBidAdapter_spec.js | 26 +- test/spec/modules/mediakeysBidAdapter_spec.js | 4 +- .../modules/medianetAnalyticsAdapter_spec.js | 94 +- test/spec/modules/medianetBidAdapter_spec.js | 98 +- test/spec/modules/medianetRtdProvider_spec.js | 12 +- .../modules/mediasquareBidAdapter_spec.js | 95 +- test/spec/modules/merkleIdSystem_spec.js | 24 +- test/spec/modules/mgidBidAdapter_spec.js | 161 +- test/spec/modules/mgidRtdProvider_spec.js | 42 +- test/spec/modules/mgidXBidAdapter_spec.js | 18 +- test/spec/modules/microadBidAdapter_spec.js | 56 +- test/spec/modules/mileBidAdapter_spec.js | 691 +++ .../modules/minutemediaBidAdapter_spec.js | 30 +- test/spec/modules/missenaBidAdapter_spec.js | 66 +- test/spec/modules/mobianRtdProvider_spec.js | 176 + .../spec/modules/mobilefuseBidAdapter_spec.js | 6 +- .../modules/mobkoiAnalyticsAdapter_spec.js | 6 +- test/spec/modules/msftBidAdapter_spec.js | 1 - test/spec/modules/multibid_spec.js | 150 +- test/spec/modules/mwOpenLinkIdSystem_spec.js | 4 +- test/spec/modules/my6senseBidAdapter_spec.js | 2 +- .../modules/mycodemediaBidAdapter_spec.js | 8 +- test/spec/modules/mygaruIdSystem_spec.js | 4 +- test/spec/modules/netIdSystem_spec.js | 10 +- test/spec/modules/neuwoRtdProvider_spec.js | 4465 +++++++++++++---- .../spec/modules/newspassidBidAdapter_spec.js | 28 +- .../modules/nextMillenniumBidAdapter_spec.js | 406 +- test/spec/modules/nextrollBidAdapter_spec.js | 52 +- test/spec/modules/nexverseBidAdapter_spec.js | 6 +- test/spec/modules/nexx360BidAdapter_spec.js | 4 +- .../modules/nobidAnalyticsAdapter_spec.js | 78 +- test/spec/modules/nobidBidAdapter_spec.js | 72 +- test/spec/modules/nodalsAiRtdProvider_spec.js | 38 +- test/spec/modules/novatiqIdSystem_spec.js | 2 +- test/spec/modules/oftmediaRtdProvider_spec.js | 2 +- test/spec/modules/oguryBidAdapter_spec.js | 8 +- test/spec/modules/omnidexBidAdapter_spec.js | 87 +- test/spec/modules/omsBidAdapter_spec.js | 48 +- test/spec/modules/oneKeyRtdProvider_spec.js | 6 +- test/spec/modules/onetagBidAdapter_spec.js | 56 +- test/spec/modules/onomagicBidAdapter_spec.js | 21 +- .../spec/modules/ooloAnalyticsAdapter_spec.js | 4 +- .../modules/opaMarketplaceBidAdapter_spec.js | 87 +- test/spec/modules/openPairIdSystem_spec.js | 45 +- test/spec/modules/openwebBidAdapter_spec.js | 30 +- test/spec/modules/openxBidAdapter_spec.js | 321 +- test/spec/modules/operaadsBidAdapter_spec.js | 12 +- test/spec/modules/operaadsIdSystem_spec.js | 6 +- test/spec/modules/opscoBidAdapter_spec.js | 42 +- test/spec/modules/optableBidAdapter_spec.js | 116 - test/spec/modules/optableRtdProvider_spec.js | 48 +- .../modules/optidigitalBidAdapter_spec.js | 122 +- .../modules/optimonAnalyticsAdapter_spec.js | 2 +- test/spec/modules/optoutBidAdapter_spec.js | 946 +++- test/spec/modules/orakiBidAdapter_spec.js | 8 +- test/spec/modules/orbidderBidAdapter_spec.js | 2 +- test/spec/modules/orbitsoftBidAdapter_spec.js | 20 +- test/spec/modules/otmBidAdapter_spec.js | 6 +- test/spec/modules/outbrainBidAdapter_spec.js | 2 +- .../modules/oxxionAnalyticsAdapter_spec.js | 4 +- test/spec/modules/oxxionRtdProvider_spec.js | 18 +- test/spec/modules/ozoneBidAdapter_spec.js | 308 +- test/spec/modules/paapiForGpt_spec.js | 216 - test/spec/modules/paapi_spec.js | 2014 -------- test/spec/modules/padsquadBidAdapter_spec.js | 14 +- test/spec/modules/pairIdSystem_spec.js | 25 +- test/spec/modules/panxoBidAdapter_spec.js | 346 ++ test/spec/modules/panxoRtdProvider_spec.js | 311 ++ test/spec/modules/performaxBidAdapter_spec.js | 396 +- test/spec/modules/permutiveCombined_spec.js | 2 +- test/spec/modules/pgamsspBidAdapter_spec.js | 8 +- .../modules/pianoDmpAnalyticsAdapter_spec.js | 2 +- test/spec/modules/pixfutureBidAdapter_spec.js | 2 +- .../modules/prebidServerBidAdapter_spec.js | 459 +- test/spec/modules/previousAuctionInfo_spec.js | 18 +- test/spec/modules/priceFloors_spec.js | 258 +- test/spec/modules/prismaBidAdapter_spec.js | 90 +- .../modules/programmaticXBidAdapter_spec.js | 87 +- .../spec/modules/proxistoreBidAdapter_spec.js | 584 ++- test/spec/modules/pubCircleBidAdapter_spec.js | 8 +- test/spec/modules/publinkIdSystem_spec.js | 34 +- test/spec/modules/publirBidAdapter_spec.js | 20 +- test/spec/modules/pubmaticBidAdapter_spec.js | 54 +- test/spec/modules/pubmaticIdSystem_spec.js | 2 +- .../modules/pubperfAnalyticsAdapter_spec.js | 6 +- test/spec/modules/pubriseBidAdapter_spec.js | 8 +- .../modules/pubstackAnalyticsAdapter_spec.js | 2 +- test/spec/modules/pubstackBidAdapter_spec.js | 348 ++ .../modules/pubwiseAnalyticsAdapter_spec.js | 14 +- test/spec/modules/pubxBidAdapter_spec.js | 6 +- .../spec/modules/pulsepointBidAdapter_spec.js | 58 +- test/spec/modules/pwbidBidAdapter_spec.js | 20 +- test/spec/modules/pxyzBidAdapter_spec.js | 10 +- test/spec/modules/qortexRtdProvider_spec.js | 16 +- test/spec/modules/qtBidAdapter_spec.js | 8 +- test/spec/modules/quantcastBidAdapter_spec.js | 859 ---- test/spec/modules/quantcastIdSystem_spec.js | 405 -- test/spec/modules/qwarryBidAdapter_spec.js | 2 +- .../spec/modules/r2b2AnalytiscAdapter_spec.js | 29 +- test/spec/modules/r2b2BidAdapter_spec.js | 110 +- test/spec/modules/rakutenBidAdapter_spec.js | 8 +- .../spec/modules/raveltechRtdProvider_spec.js | 20 +- test/spec/modules/readpeakBidAdapter_spec.js | 8 +- test/spec/modules/realTimeDataModule_spec.js | 42 +- .../modules/reconciliationRtdProvider_spec.js | 14 +- test/spec/modules/redtramBidAdapter_spec.js | 4 +- test/spec/modules/relaidoBidAdapter_spec.js | 18 +- test/spec/modules/relevadRtdProvider_spec.js | 42 +- .../modules/relevantdigitalBidAdapter_spec.js | 6 +- .../modules/resetdigitalBidAdapter_spec.js | 2 +- .../spec/modules/retailspotBidAdapter_spec.js | 28 +- test/spec/modules/revantageBidAdapter_spec.js | 994 ++++ .../spec/modules/revcontentBidAdapter_spec.js | 36 +- test/spec/modules/revnewBidAdapter_spec.js | 2 +- .../modules/rewardedInterestIdSystem_spec.js | 10 +- test/spec/modules/rhythmoneBidAdapter_spec.js | 36 +- .../modules/richaudienceBidAdapter_spec.js | 66 +- .../ringieraxelspringerBidAdapter_spec.js | 10 - test/spec/modules/riseBidAdapter_spec.js | 34 +- .../spec/modules/rivrAnalyticsAdapter_spec.js | 5 +- .../spec/modules/robustAppsBidAdapter_spec.js | 48 +- test/spec/modules/rocketlabBidAdapter_spec.js | 8 +- .../modules/roxotAnalyticsAdapter_spec.js | 10 +- test/spec/modules/rtbhouseBidAdapter_spec.js | 18 +- test/spec/modules/rtbsapeBidAdapter_spec.js | 30 +- test/spec/modules/rubiconBidAdapter_spec.js | 490 +- test/spec/modules/rules_spec.js | 856 ++++ test/spec/modules/rumbleBidAdapter_spec.js | 8 +- test/spec/modules/s2sTesting_spec.js | 158 +- .../modules/scaleableAnalyticsAdapter_spec.js | 2 +- test/spec/modules/scaliburBidAdapter_spec.js | 18 +- test/spec/modules/schain_spec.js | 6 +- test/spec/modules/scope3RtdProvider_spec.js | 4 +- .../spec/modules/screencoreBidAdapter_spec.js | 35 +- .../modules/seedingAllianceAdapter_spec.js | 28 +- test/spec/modules/seedtagBidAdapter_spec.js | 139 +- test/spec/modules/sevioBidAdapter_spec.js | 109 +- test/spec/modules/sharedIdSystem_spec.js | 18 +- .../modules/sharethroughBidAdapter_spec.js | 63 - test/spec/modules/shinezBidAdapter_spec.js | 12 +- test/spec/modules/shinezRtbBidAdapter_spec.js | 123 +- .../modules/showheroes-bsBidAdapter_spec.js | 8 +- test/spec/modules/silvermobBidAdapter_spec.js | 4 +- test/spec/modules/sirdataRtdProvider_spec.js | 50 +- test/spec/modules/sizeMappingV2_spec.js | 8 +- test/spec/modules/sizeMapping_spec.js | 26 +- test/spec/modules/smaatoBidAdapter_spec.js | 438 +- test/spec/modules/smarthubBidAdapter_spec.js | 10 +- test/spec/modules/smarticoBidAdapter_spec.js | 13 +- test/spec/modules/smartyadsBidAdapter_spec.js | 8 +- .../spec/modules/smartytechBidAdapter_spec.js | 12 +- .../modules/smilewantedBidAdapter_spec.js | 4 +- test/spec/modules/smootBidAdapter_spec.js | 8 +- test/spec/modules/snigelBidAdapter_spec.js | 52 +- test/spec/modules/sonaradsBidAdapter_spec.js | 18 +- test/spec/modules/sonobiBidAdapter_spec.js | 2 +- test/spec/modules/sovrnBidAdapter_spec.js | 26 +- test/spec/modules/ssmasBidAdapter_spec.js | 4 +- test/spec/modules/sspBCBidAdapter_spec.js | 15 +- .../spec/modules/ssp_genieeBidAdapter_spec.js | 6 +- .../spec/modules/stackadaptBidAdapter_spec.js | 40 +- test/spec/modules/startioBidAdapter_spec.js | 6 +- test/spec/modules/stnBidAdapter_spec.js | 30 +- test/spec/modules/storageControl_spec.js | 43 +- .../modules/stroeerCoreBidAdapter_spec.js | 227 +- .../modules/symitriDapRtdProvider_spec.js | 40 +- test/spec/modules/taboolaBidAdapter_spec.js | 1053 +++- test/spec/modules/taboolaIdSystem_spec.js | 86 +- .../modules/tadvertisingBidAdapter_spec.js | 78 +- test/spec/modules/tagorasBidAdapter_spec.js | 87 +- test/spec/modules/talkadsBidAdapter_spec.js | 10 +- test/spec/modules/tapadIdSystem_spec.js | 8 +- test/spec/modules/tappxBidAdapter_spec.js | 20 +- .../modules/targetVideoAdServerVideo_spec.js | 8 +- .../modules/targetVideoBidAdapter_spec.js | 62 +- test/spec/modules/tcfControl_spec.js | 60 +- test/spec/modules/teadsBidAdapter_spec.js | 24 +- test/spec/modules/teadsIdSystem_spec.js | 8 +- test/spec/modules/tealBidAdapter_spec.js | 4 +- test/spec/modules/temedyaBidAdapter_spec.js | 6 +- .../teqBlazeSalesAgentBidAdapter_spec.js | 440 ++ test/spec/modules/theAdxBidAdapter_spec.js | 8 +- test/spec/modules/timeoutRtdProvider_spec.js | 4 +- test/spec/modules/tncIdSystem_spec.js | 18 +- test/spec/modules/topLevelPaapi_spec.js | 511 -- test/spec/modules/topicsFpdModule_spec.js | 66 +- test/spec/modules/tpmnBidAdapter_spec.js | 24 +- .../modules/trafficgateBidAdapter_spec.js | 119 +- test/spec/modules/trionBidAdapter_spec.js | 38 +- .../spec/modules/tripleliftBidAdapter_spec.js | 103 +- test/spec/modules/truereachBidAdapter_spec.js | 4 +- test/spec/modules/trustxBidAdapter_spec.js | 56 +- test/spec/modules/ttdBidAdapter_spec.js | 28 +- .../modules/twistDigitalBidAdapter_spec.js | 147 +- .../modules/ucfunnelAnalyticsAdapter_spec.js | 4 +- test/spec/modules/ucfunnelBidAdapter_spec.js | 44 +- test/spec/modules/uid2IdSystem_helpers.js | 14 +- test/spec/modules/uid2IdSystem_spec.js | 58 +- test/spec/modules/undertoneBidAdapter_spec.js | 36 +- test/spec/modules/unicornBidAdapter_spec.js | 6 +- test/spec/modules/unifiedIdSystem_spec.js | 20 +- .../modules/uniquestAnalyticsAdapter_spec.js | 6 +- test/spec/modules/unrulyBidAdapter_spec.js | 447 +- test/spec/modules/userId_spec.js | 478 +- test/spec/modules/utiqIdSystem_spec.js | 8 +- test/spec/modules/utiqMtpIdSystem_spec.js | 8 +- test/spec/modules/validationFpdModule_spec.js | 32 +- test/spec/modules/valuadBidAdapter_spec.js | 2 - test/spec/modules/vdoaiBidAdapter_spec.js | 24 +- test/spec/modules/verbenBidAdapter_spec.js | 478 ++ test/spec/modules/viantBidAdapter_spec.js | 20 +- .../modules/vibrantmediaBidAdapter_spec.js | 10 +- test/spec/modules/vidazooBidAdapter_spec.js | 147 +- test/spec/modules/videoModule/adQueue_spec.js | 2 +- test/spec/modules/videoModule/pbVideo_spec.js | 8 +- .../videoModule/shared/vastXmlBuilder_spec.js | 8 +- .../adplayerproVideoProvider_spec.js | 22 +- .../submodules/jwplayerVideoProvider_spec.js | 76 +- .../submodules/videojsVideoProvider_spec.js | 30 +- test/spec/modules/videobyteBidAdapter_spec.js | 20 +- .../modules/videoheroesBidAdapter_spec.js | 52 +- test/spec/modules/videonowBidAdapter_spec.js | 10 +- .../spec/modules/videoreachBidAdapter_spec.js | 6 +- .../spec/modules/viewdeosDXBidAdapter_spec.js | 40 +- test/spec/modules/viqeoBidAdapter_spec.js | 10 +- .../modules/visiblemeasuresBidAdapter_spec.js | 8 +- test/spec/modules/visxBidAdapter_spec.js | 243 +- test/spec/modules/vlybyBidAdapter_spec.js | 6 +- test/spec/modules/voxBidAdapter_spec.js | 10 +- test/spec/modules/vrtcalBidAdapter_spec.js | 14 +- test/spec/modules/vuukleBidAdapter_spec.js | 4 +- test/spec/modules/waardexBidAdapter_spec.js | 8 +- test/spec/modules/widespaceBidAdapter_spec.js | 6 +- test/spec/modules/winrBidAdapter_spec.js | 20 +- test/spec/modules/wipesBidAdapter_spec.js | 6 +- test/spec/modules/wurflRtdProvider_spec.js | 385 +- test/spec/modules/xeBidAdapter_spec.js | 48 +- test/spec/modules/yahooAdsBidAdapter_spec.js | 274 +- test/spec/modules/yaleoBidAdapter_spec.js | 213 + .../modules/yandexAnalyticsAdapter_spec.js | 4 +- test/spec/modules/yandexBidAdapter_spec.js | 6 +- test/spec/modules/yandexIdSystem_spec.js | 6 +- test/spec/modules/yieldlabBidAdapter_spec.js | 54 +- test/spec/modules/yieldliftBidAdapter_spec.js | 18 +- test/spec/modules/yieldloveBidAdapter_spec.js | 2 +- test/spec/modules/yieldmoBidAdapter_spec.js | 172 +- .../modules/yieldoneAnalyticsAdapter_spec.js | 34 +- test/spec/modules/yieldoneBidAdapter_spec.js | 144 +- .../spec/modules/zeotapIdPlusIdSystem_spec.js | 12 +- .../zeta_global_sspAnalyticsAdapter_spec.js | 118 +- .../modules/zeta_global_sspBidAdapter_spec.js | 36 +- test/spec/modules/zmaticooBidAdapter_spec.js | 6 +- test/spec/native_spec.js | 152 +- test/spec/ortb2.5StrictTranslator/dsl_spec.js | 38 +- .../spec/ortb2.5StrictTranslator/spec_spec.js | 2 +- .../translator_spec.js | 8 +- .../spec/ortb2.5Translator/translator_spec.js | 14 +- test/spec/ortbConverter/banner_spec.js | 30 +- test/spec/ortbConverter/common_spec.js | 10 +- test/spec/ortbConverter/composer_spec.js | 2 +- test/spec/ortbConverter/converter_spec.js | 40 +- test/spec/ortbConverter/currency_spec.js | 6 +- test/spec/ortbConverter/gdpr_spec.js | 4 +- test/spec/ortbConverter/mediaTypes_spec.js | 14 +- .../ortbConverter/mergeProcessors_spec.js | 4 +- test/spec/ortbConverter/multibid_spec.js | 6 +- test/spec/ortbConverter/native_spec.js | 32 +- test/spec/ortbConverter/pbjsORTB_spec.js | 6 +- .../pbsExtensions/adUnitCode_spec.js | 6 +- .../pbsExtensions/aliases_spec.js | 10 +- .../pbsExtensions/params_spec.js | 6 +- .../pbsExtensions/trackers_spec.js | 12 +- .../ortbConverter/pbsExtensions/video_spec.js | 8 +- test/spec/ortbConverter/priceFloors_spec.js | 38 +- test/spec/ortbConverter/video_spec.js | 14 +- test/spec/refererDetection_spec.js | 20 +- test/spec/renderer_spec.js | 2 +- .../schainSerializer/schainSerializer_spec.js | 2 +- test/spec/unit/adRendering_spec.js | 90 +- test/spec/unit/core/adapterManager_spec.js | 214 +- test/spec/unit/core/ajax_spec.js | 58 +- test/spec/unit/core/auctionIndex_spec.js | 54 +- test/spec/unit/core/bidderFactory_spec.js | 235 +- test/spec/unit/core/bidderSettings_spec.js | 8 +- test/spec/unit/core/consentHandler_spec.js | 32 +- test/spec/unit/core/eventTrackers_spec.js | 16 +- test/spec/unit/core/events_spec.js | 8 +- test/spec/unit/core/storageManager_spec.js | 32 +- test/spec/unit/core/targetingLock_spec.js | 4 +- test/spec/unit/core/targeting_spec.js | 192 +- test/spec/unit/pbjs_api_spec.js | 319 +- test/spec/unit/secureCreatives_spec.js | 59 +- test/spec/unit/utils/cpm_spec.js | 24 +- test/spec/unit/utils/focusTimeout_spec.js | 2 +- test/spec/unit/utils/perfMetrics_spec.js | 34 +- test/spec/unit/utils/promise_spec.js | 2 +- test/spec/unit/utils/reducers_spec.js | 16 +- test/spec/unit/utils/ttlCollection_spec.js | 36 +- test/spec/userSync_spec.js | 44 +- test/spec/utils/adUnits_spec.js | 40 + test/spec/utils/cachedApiWrapper_spec.js | 2 +- test/spec/utils/prerendering_spec.js | 2 +- test/spec/utils/yield_spec.js | 85 + test/spec/utils_spec.js | 42 +- test/spec/videoCache_spec.js | 18 +- test/spec/video_spec.js | 57 +- test/test_deps.js | 13 +- test/test_index.js | 2 +- webpack.conf.js | 2 +- 1768 files changed, 57394 insertions(+), 36070 deletions(-) create mode 100644 integrationExamples/gpt/adcluster_banner_example.html create mode 100644 integrationExamples/gpt/adcluster_video_example.html create mode 100644 integrationExamples/gpt/insurads.html create mode 100644 integrationExamples/gpt/taboola_multiformat.html delete mode 100644 integrationExamples/longform/basic_w_bidderSettings.html delete mode 100644 integrationExamples/longform/basic_w_custom_adserver_translation.html delete mode 100644 integrationExamples/longform/basic_w_priceGran.html delete mode 100644 integrationExamples/longform/basic_w_requireExactDuration.html delete mode 100644 integrationExamples/longform/basic_wo_brandCategoryExclusion.html delete mode 100644 integrationExamples/longform/basic_wo_requireExactDuration.html create mode 100644 integrationExamples/noadserver/connatixBidAdapter_sample.html create mode 100644 integrationExamples/shapingRules/rules.json create mode 100644 integrationExamples/shapingRules/shapingRulesModule.html create mode 100644 libraries/alliance_gravityUtils/index.ts create mode 100644 libraries/bidViewabilityPixels/index.js delete mode 100644 libraries/categoryTranslationMapping/index.js create mode 100644 libraries/fingerprinting/fingerprinting.js create mode 100644 libraries/intentIqUtils/getUnitPosition.js create mode 100644 libraries/magniteUtils/outstream.js create mode 100644 libraries/omsUtils/viewability.js delete mode 100644 libraries/paapiTools/buyerOrigins.js create mode 100644 libraries/placementPositionInfo/placementPositionInfo.js delete mode 100644 metadata/disclosures/prebid/categoryTranslation.json create mode 100644 metadata/modules/aceexBidAdapter.json rename metadata/modules/{ringieraxelspringerBidAdapter.json => adclusterBidAdapter.json} (55%) create mode 100644 metadata/modules/adnimationBidAdapter.json create mode 100644 metadata/modules/apesterBidAdapter.json rename metadata/modules/{optableBidAdapter.json => dpaiBidAdapter.json} (87%) create mode 100644 metadata/modules/floxisBidAdapter.json create mode 100644 metadata/modules/harionBidAdapter.json rename metadata/modules/{quantcastBidAdapter.json => insuradsBidAdapter.json} (50%) create mode 100644 metadata/modules/leagueMBidAdapter.json create mode 100644 metadata/modules/locIdSystem.json create mode 100644 metadata/modules/magniteBidAdapter.json rename metadata/modules/{dmdIdSystem.json => mileBidAdapter.json} (58%) create mode 100644 metadata/modules/panxoBidAdapter.json rename metadata/modules/{intersectionRtdProvider.json => panxoRtdProvider.json} (84%) create mode 100644 metadata/modules/pubstackBidAdapter.json delete mode 100644 metadata/modules/quantcastIdSystem.json create mode 100644 metadata/modules/revantageBidAdapter.json create mode 100644 metadata/modules/teqBlazeSalesAgentBidAdapter.json create mode 100644 metadata/modules/verbenBidAdapter.json create mode 100644 metadata/modules/yaleoBidAdapter.json create mode 100644 modules/aceexBidAdapter.js create mode 100644 modules/aceexBidAdapter.md create mode 100644 modules/adclusterBidAdapter.js create mode 100644 modules/adclusterBidAdapter.md create mode 100644 modules/adnimationBidAdapter.js create mode 100644 modules/adnimationBidAdapter.md delete mode 100644 modules/adpod.js create mode 100644 modules/alliance_gravityBidAdapter.md create mode 100644 modules/alliance_gravityBidAdapter.ts create mode 100644 modules/apesterBidAdapter.js create mode 100644 modules/apesterBidAdapter.md delete mode 100644 modules/categoryTranslation.js delete mode 100644 modules/dfpAdServerVideo.js delete mode 100644 modules/dfpAdpod.js delete mode 100644 modules/dmdIdSystem.js delete mode 100644 modules/dmdIdSystem.md create mode 100644 modules/dpaiBidAdapter.js create mode 100644 modules/dpaiBidAdapter.md delete mode 100644 modules/express.js create mode 100644 modules/floxisBidAdapter.js create mode 100644 modules/floxisBidAdapter.md delete mode 100644 modules/freeWheelAdserverVideo.js delete mode 100644 modules/gamAdpod.js create mode 100644 modules/harionBidAdapter.js create mode 100644 modules/harionBidAdapter.md delete mode 100644 modules/humansecurityRtdProvider.js create mode 100644 modules/humansecurityRtdProvider.ts create mode 100644 modules/insuradsBidAdapter.md create mode 100644 modules/insuradsBidAdapter.ts delete mode 100644 modules/intersectionRtdProvider.js create mode 100644 modules/leagueMBidAdapter.js create mode 100644 modules/leagueMBidAdapter.md create mode 100644 modules/locIdSystem.js create mode 100644 modules/locIdSystem.md create mode 100644 modules/magniteBidAdapter.js create mode 100644 modules/magniteBidAdapter.md create mode 100644 modules/mileBidAdapter.md create mode 100644 modules/mileBidAdapter.ts delete mode 100644 modules/optableBidAdapter.js delete mode 100644 modules/optableBidAdapter.md delete mode 100644 modules/paapi.js delete mode 100644 modules/paapiForGpt.js delete mode 100644 modules/paapiForGpt.md create mode 100644 modules/panxoBidAdapter.js create mode 100644 modules/panxoBidAdapter.md create mode 100644 modules/panxoRtdProvider.js create mode 100644 modules/panxoRtdProvider.md create mode 100644 modules/pubstackBidAdapter.md create mode 100644 modules/pubstackBidAdapter.ts delete mode 100644 modules/quantcastBidAdapter.js delete mode 100644 modules/quantcastBidAdapter.md delete mode 100644 modules/quantcastIdSystem.js delete mode 100644 modules/quantcastIdSystem.md create mode 100644 modules/revantageBidAdapter.js create mode 100644 modules/revantageBidAdapter.md delete mode 100644 modules/ringieraxelspringerBidAdapter.js delete mode 100644 modules/ringieraxelspringerBidAdapter.md create mode 100644 modules/rules/index.ts create mode 100644 modules/teqBlazeSalesAgentBidAdapter.js create mode 100644 modules/teqBlazeSalesAgentBidAdapter.md delete mode 100644 modules/topLevelPaapi.js create mode 100644 modules/verbenBidAdapter.js create mode 100644 modules/verbenBidAdapter.md create mode 100644 modules/yaleoBidAdapter.md create mode 100755 modules/yaleoBidAdapter.ts create mode 100644 plugins/eslint/approvedLoadExternalScriptPaths.js delete mode 100644 scope3_segtax_pr.md create mode 100644 src/utils/adUnits.ts create mode 100644 test/build-logic/no_3384_spec.mjs create mode 100644 test/spec/fingerprinting_spec.js create mode 100644 test/spec/libraries/bidViewabilityPixels_spec.js delete mode 100644 test/spec/libraries/dnt_spec.js create mode 100644 test/spec/libraries/gamPredictionReport_spec.js create mode 100644 test/spec/libraries/magniteUtils/outstream_spec.js create mode 100644 test/spec/libraries/percentInView_spec.js create mode 100644 test/spec/libraries/placementPositionInfo_spec.js create mode 100644 test/spec/modules/aceexBidAdapter_spec.js create mode 100644 test/spec/modules/adclusterBidAdapter_spec.js create mode 100644 test/spec/modules/adminationBidAdapter_spec.js delete mode 100644 test/spec/modules/adpod_spec.js create mode 100644 test/spec/modules/alliance_gravityBidAdapter_spec.js create mode 100644 test/spec/modules/apesterBidAdapter_spec.js delete mode 100644 test/spec/modules/categoryTranslation_spec.js delete mode 100644 test/spec/modules/dmdIdSystem_spec.js create mode 100644 test/spec/modules/dpaiBidAdapter_spec.js create mode 100644 test/spec/modules/floxisBidAdapter_spec.js delete mode 100644 test/spec/modules/freeWheelAdserverVideo_spec.js delete mode 100644 test/spec/modules/gamAdpod_spec.js create mode 100644 test/spec/modules/harionBidAdapter_spec.js create mode 100644 test/spec/modules/insuradsBidAdapter_spec.js delete mode 100644 test/spec/modules/intersectionRtdProvider_spec.js create mode 100644 test/spec/modules/leagueMBidAdapter_spec.js create mode 100644 test/spec/modules/locIdSystem_spec.js create mode 100644 test/spec/modules/magniteBidAdapter_spec.js create mode 100644 test/spec/modules/mileBidAdapter_spec.js delete mode 100644 test/spec/modules/optableBidAdapter_spec.js delete mode 100644 test/spec/modules/paapiForGpt_spec.js delete mode 100644 test/spec/modules/paapi_spec.js create mode 100644 test/spec/modules/panxoBidAdapter_spec.js create mode 100644 test/spec/modules/panxoRtdProvider_spec.js create mode 100644 test/spec/modules/pubstackBidAdapter_spec.js delete mode 100644 test/spec/modules/quantcastBidAdapter_spec.js delete mode 100644 test/spec/modules/quantcastIdSystem_spec.js create mode 100644 test/spec/modules/revantageBidAdapter_spec.js delete mode 100644 test/spec/modules/ringieraxelspringerBidAdapter_spec.js create mode 100644 test/spec/modules/rules_spec.js create mode 100644 test/spec/modules/teqBlazeSalesAgentBidAdapter_spec.js delete mode 100644 test/spec/modules/topLevelPaapi_spec.js create mode 100644 test/spec/modules/verbenBidAdapter_spec.js create mode 100644 test/spec/modules/yaleoBidAdapter_spec.js create mode 100644 test/spec/utils/adUnits_spec.js create mode 100644 test/spec/utils/yield_spec.js diff --git a/.github/actions/load/action.yml b/.github/actions/load/action.yml index 0102608dbd1..e3fc00dc6ae 100644 --- a/.github/actions/load/action.yml +++ b/.github/actions/load/action.yml @@ -24,10 +24,14 @@ runs: rm -r "$(pwd)"/* - name: Download artifact - uses: actions/download-artifact@v5 + uses: Wandalen/wretry.action@v3.8.0 with: - path: '${{ runner.temp }}' - name: '${{ inputs.name }}' + action: actions/download-artifact@v7 + attempt_limit: 2 + attempt_delay: 10000 + with: | + path: '${{ runner.temp }}' + name: '${{ inputs.name }}' - name: 'Untar working directory' shell: bash diff --git a/.github/actions/unzip-artifact/action.yml b/.github/actions/unzip-artifact/action.yml index 672fae7af85..a05c15a00cd 100644 --- a/.github/actions/unzip-artifact/action.yml +++ b/.github/actions/unzip-artifact/action.yml @@ -11,6 +11,9 @@ outputs: runs: using: 'composite' steps: + - name: 'Delay waiting for artifacts to be ready' + shell: bash + run: sleep 10 - name: 'Download artifact' id: download uses: actions/github-script@v8 diff --git a/.github/codeql/queries/autogen_fpDOMMethod.qll b/.github/codeql/queries/autogen_fpDOMMethod.qll index 61555d5852f..164a699e97b 100644 --- a/.github/codeql/queries/autogen_fpDOMMethod.qll +++ b/.github/codeql/queries/autogen_fpDOMMethod.qll @@ -7,9 +7,9 @@ class DOMMethod extends string { DOMMethod() { - ( this = "toDataURL" and weight = 32.78 and type = "HTMLCanvasElement" ) + ( this = "toDataURL" and weight = 32.64 and type = "HTMLCanvasElement" ) or - ( this = "getChannelData" and weight = 1033.52 and type = "AudioBuffer" ) + ( this = "getChannelData" and weight = 1009.41 and type = "AudioBuffer" ) } float getWeight() { diff --git a/.github/codeql/queries/autogen_fpEventProperty.qll b/.github/codeql/queries/autogen_fpEventProperty.qll index a102dc216aa..25ecd018f0f 100644 --- a/.github/codeql/queries/autogen_fpEventProperty.qll +++ b/.github/codeql/queries/autogen_fpEventProperty.qll @@ -7,21 +7,21 @@ class EventProperty extends string { EventProperty() { - ( this = "accelerationIncludingGravity" and weight = 195.95 and event = "devicemotion" ) + ( this = "candidate" and weight = 54.73 and event = "icecandidate" ) or - ( this = "beta" and weight = 889.02 and event = "deviceorientation" ) + ( this = "rotationRate" and weight = 63.55 and event = "devicemotion" ) or - ( this = "gamma" and weight = 318.9 and event = "deviceorientation" ) + ( this = "accelerationIncludingGravity" and weight = 205.08 and event = "devicemotion" ) or - ( this = "alpha" and weight = 748.66 and event = "deviceorientation" ) + ( this = "acceleration" and weight = 64.53 and event = "devicemotion" ) or - ( this = "candidate" and weight = 48.4 and event = "icecandidate" ) + ( this = "alpha" and weight = 784.67 and event = "deviceorientation" ) or - ( this = "acceleration" and weight = 59.13 and event = "devicemotion" ) + ( this = "beta" and weight = 801.42 and event = "deviceorientation" ) or - ( this = "rotationRate" and weight = 58.73 and event = "devicemotion" ) + ( this = "gamma" and weight = 300.01 and event = "deviceorientation" ) or - ( this = "absolute" and weight = 480.46 and event = "deviceorientation" ) + ( this = "absolute" and weight = 281.45 and event = "deviceorientation" ) } float getWeight() { diff --git a/.github/codeql/queries/autogen_fpGlobalConstructor.qll b/.github/codeql/queries/autogen_fpGlobalConstructor.qll index 1bd3776448a..8feceaae940 100644 --- a/.github/codeql/queries/autogen_fpGlobalConstructor.qll +++ b/.github/codeql/queries/autogen_fpGlobalConstructor.qll @@ -6,15 +6,15 @@ class GlobalConstructor extends string { GlobalConstructor() { - ( this = "OfflineAudioContext" and weight = 1249.69 ) + ( this = "SharedWorker" and weight = 74.12 ) or - ( this = "SharedWorker" and weight = 78.96 ) + ( this = "OfflineAudioContext" and weight = 1062.83 ) or - ( this = "RTCPeerConnection" and weight = 36.22 ) + ( this = "RTCPeerConnection" and weight = 36.17 ) or - ( this = "Gyroscope" and weight = 94.31 ) + ( this = "Gyroscope" and weight = 100.27 ) or - ( this = "AudioWorkletNode" and weight = 106.77 ) + ( this = "AudioWorkletNode" and weight = 145.12 ) } float getWeight() { diff --git a/.github/codeql/queries/autogen_fpGlobalObjectProperty0.qll b/.github/codeql/queries/autogen_fpGlobalObjectProperty0.qll index 622b4097377..19489a50149 100644 --- a/.github/codeql/queries/autogen_fpGlobalObjectProperty0.qll +++ b/.github/codeql/queries/autogen_fpGlobalObjectProperty0.qll @@ -7,59 +7,57 @@ class GlobalObjectProperty0 extends string { GlobalObjectProperty0() { - ( this = "availWidth" and weight = 62.91 and global0 = "screen" ) + ( this = "availHeight" and weight = 65.33 and global0 = "screen" ) or - ( this = "availHeight" and weight = 66.51 and global0 = "screen" ) + ( this = "availWidth" and weight = 61.95 and global0 = "screen" ) or - ( this = "colorDepth" and weight = 36.87 and global0 = "screen" ) + ( this = "colorDepth" and weight = 38.5 and global0 = "screen" ) or - ( this = "pixelDepth" and weight = 43.1 and global0 = "screen" ) + ( this = "availTop" and weight = 1305.37 and global0 = "screen" ) or - ( this = "availLeft" and weight = 730.43 and global0 = "screen" ) + ( this = "plugins" and weight = 15.16 and global0 = "navigator" ) or - ( this = "availTop" and weight = 1485.89 and global0 = "screen" ) + ( this = "deviceMemory" and weight = 64.15 and global0 = "navigator" ) or - ( this = "orientation" and weight = 33.81 and global0 = "screen" ) + ( this = "getBattery" and weight = 41.16 and global0 = "navigator" ) or - ( this = "vendorSub" and weight = 1822.98 and global0 = "navigator" ) + ( this = "webdriver" and weight = 27.64 and global0 = "navigator" ) or - ( this = "productSub" and weight = 381.55 and global0 = "navigator" ) + ( this = "permission" and weight = 24.67 and global0 = "Notification" ) or - ( this = "plugins" and weight = 15.37 and global0 = "navigator" ) + ( this = "storage" and weight = 35.77 and global0 = "navigator" ) or - ( this = "mimeTypes" and weight = 15.39 and global0 = "navigator" ) + ( this = "onLine" and weight = 18.84 and global0 = "navigator" ) or - ( this = "webkitTemporaryStorage" and weight = 32.87 and global0 = "navigator" ) + ( this = "pixelDepth" and weight = 45.77 and global0 = "screen" ) or - ( this = "hardwareConcurrency" and weight = 55.54 and global0 = "navigator" ) + ( this = "availLeft" and weight = 624.44 and global0 = "screen" ) or - ( this = "appCodeName" and weight = 167.7 and global0 = "navigator" ) + ( this = "orientation" and weight = 34.16 and global0 = "screen" ) or - ( this = "onLine" and weight = 18.14 and global0 = "navigator" ) + ( this = "vendorSub" and weight = 1873.27 and global0 = "navigator" ) or - ( this = "webdriver" and weight = 28.99 and global0 = "navigator" ) + ( this = "productSub" and weight = 381.87 and global0 = "navigator" ) or - ( this = "keyboard" and weight = 5673.26 and global0 = "navigator" ) + ( this = "webkitTemporaryStorage" and weight = 37.97 and global0 = "navigator" ) or - ( this = "mediaDevices" and weight = 123.32 and global0 = "navigator" ) + ( this = "hardwareConcurrency" and weight = 51.78 and global0 = "navigator" ) or - ( this = "storage" and weight = 30.23 and global0 = "navigator" ) + ( this = "appCodeName" and weight = 173.35 and global0 = "navigator" ) or - ( this = "deviceMemory" and weight = 62.29 and global0 = "navigator" ) + ( this = "keyboard" and weight = 1722.82 and global0 = "navigator" ) or - ( this = "mediaCapabilities" and weight = 148.31 and global0 = "navigator" ) + ( this = "mediaDevices" and weight = 149.07 and global0 = "navigator" ) or - ( this = "permissions" and weight = 92.01 and global0 = "navigator" ) + ( this = "mediaCapabilities" and weight = 142.34 and global0 = "navigator" ) or - ( this = "permission" and weight = 25.87 and global0 = "Notification" ) + ( this = "permissions" and weight = 89.71 and global0 = "navigator" ) or - ( this = "getBattery" and weight = 40.45 and global0 = "navigator" ) + ( this = "webkitPersistentStorage" and weight = 134.12 and global0 = "navigator" ) or - ( this = "webkitPersistentStorage" and weight = 121.43 and global0 = "navigator" ) + ( this = "requestMediaKeySystemAccess" and weight = 18.22 and global0 = "navigator" ) or - ( this = "requestMediaKeySystemAccess" and weight = 22.53 and global0 = "navigator" ) - or - ( this = "getGamepads" and weight = 275.28 and global0 = "navigator" ) + ( this = "getGamepads" and weight = 209.55 and global0 = "navigator" ) } float getWeight() { diff --git a/.github/codeql/queries/autogen_fpGlobalObjectProperty1.qll b/.github/codeql/queries/autogen_fpGlobalObjectProperty1.qll index 3be175f2c11..4ba664c998f 100644 --- a/.github/codeql/queries/autogen_fpGlobalObjectProperty1.qll +++ b/.github/codeql/queries/autogen_fpGlobalObjectProperty1.qll @@ -8,7 +8,7 @@ class GlobalObjectProperty1 extends string { GlobalObjectProperty1() { - ( this = "enumerateDevices" and weight = 361.7 and global0 = "navigator" and global1 = "mediaDevices" ) + ( this = "enumerateDevices" and weight = 595.56 and global0 = "navigator" and global1 = "mediaDevices" ) } float getWeight() { diff --git a/.github/codeql/queries/autogen_fpGlobalTypeProperty0.qll b/.github/codeql/queries/autogen_fpGlobalTypeProperty0.qll index 489d3f0f3ae..b26e3689251 100644 --- a/.github/codeql/queries/autogen_fpGlobalTypeProperty0.qll +++ b/.github/codeql/queries/autogen_fpGlobalTypeProperty0.qll @@ -7,11 +7,11 @@ class GlobalTypeProperty0 extends string { GlobalTypeProperty0() { - ( this = "x" and weight = 5673.26 and global0 = "Gyroscope" ) + ( this = "x" and weight = 4255.55 and global0 = "Gyroscope" ) or - ( this = "y" and weight = 5673.26 and global0 = "Gyroscope" ) + ( this = "y" and weight = 4255.55 and global0 = "Gyroscope" ) or - ( this = "z" and weight = 5673.26 and global0 = "Gyroscope" ) + ( this = "z" and weight = 4255.55 and global0 = "Gyroscope" ) } float getWeight() { diff --git a/.github/codeql/queries/autogen_fpGlobalTypeProperty1.qll b/.github/codeql/queries/autogen_fpGlobalTypeProperty1.qll index 2f290d30132..084e91305b6 100644 --- a/.github/codeql/queries/autogen_fpGlobalTypeProperty1.qll +++ b/.github/codeql/queries/autogen_fpGlobalTypeProperty1.qll @@ -8,7 +8,7 @@ class GlobalTypeProperty1 extends string { GlobalTypeProperty1() { - ( this = "resolvedOptions" and weight = 18.94 and global0 = "Intl" and global1 = "DateTimeFormat" ) + ( this = "resolvedOptions" and weight = 19.01 and global0 = "Intl" and global1 = "DateTimeFormat" ) } float getWeight() { diff --git a/.github/codeql/queries/autogen_fpGlobalVar.qll b/.github/codeql/queries/autogen_fpGlobalVar.qll index debc39522ee..a28f1c7772c 100644 --- a/.github/codeql/queries/autogen_fpGlobalVar.qll +++ b/.github/codeql/queries/autogen_fpGlobalVar.qll @@ -6,23 +6,23 @@ class GlobalVar extends string { GlobalVar() { - ( this = "devicePixelRatio" and weight = 18.84 ) + ( this = "devicePixelRatio" and weight = 18.39 ) or - ( this = "outerWidth" and weight = 104.3 ) + ( this = "screenX" and weight = 366.36 ) or - ( this = "outerHeight" and weight = 177.3 ) + ( this = "screenY" and weight = 320.66 ) or - ( this = "indexedDB" and weight = 21.68 ) + ( this = "outerWidth" and weight = 104.67 ) or - ( this = "screenX" and weight = 411.93 ) + ( this = "outerHeight" and weight = 154.1 ) or - ( this = "screenY" and weight = 369.99 ) + ( this = "screenLeft" and weight = 321.49 ) or - ( this = "screenLeft" and weight = 344.06 ) + ( this = "screenTop" and weight = 322.32 ) or - ( this = "screenTop" and weight = 343.13 ) + ( this = "indexedDB" and weight = 23.36 ) or - ( this = "openDatabase" and weight = 128.91 ) + ( this = "openDatabase" and weight = 146.11 ) } float getWeight() { diff --git a/.github/codeql/queries/autogen_fpRenderingContextProperty.qll b/.github/codeql/queries/autogen_fpRenderingContextProperty.qll index 1f23b1a5057..e508d42520b 100644 --- a/.github/codeql/queries/autogen_fpRenderingContextProperty.qll +++ b/.github/codeql/queries/autogen_fpRenderingContextProperty.qll @@ -7,35 +7,35 @@ class RenderingContextProperty extends string { RenderingContextProperty() { - ( this = "getImageData" and weight = 55.51 and contextType = "2d" ) + ( this = "getExtension" and weight = 24.59 and contextType = "webgl" ) or - ( this = "getParameter" and weight = 30.58 and contextType = "webgl" ) + ( this = "getParameter" and weight = 28.11 and contextType = "webgl" ) or - ( this = "measureText" and weight = 46.82 and contextType = "2d" ) + ( this = "getImageData" and weight = 62.25 and contextType = "2d" ) or - ( this = "getParameter" and weight = 70.22 and contextType = "webgl2" ) + ( this = "measureText" and weight = 43.06 and contextType = "2d" ) or - ( this = "getShaderPrecisionFormat" and weight = 128.74 and contextType = "webgl2" ) + ( this = "getParameter" and weight = 67.61 and contextType = "webgl2" ) or - ( this = "getExtension" and weight = 71.78 and contextType = "webgl2" ) + ( this = "getShaderPrecisionFormat" and weight = 138.74 and contextType = "webgl2" ) or - ( this = "getContextAttributes" and weight = 190.28 and contextType = "webgl2" ) + ( this = "getExtension" and weight = 69.66 and contextType = "webgl2" ) or - ( this = "getSupportedExtensions" and weight = 560.85 and contextType = "webgl2" ) + ( this = "getContextAttributes" and weight = 201.04 and contextType = "webgl2" ) or - ( this = "getExtension" and weight = 26.27 and contextType = "webgl" ) + ( this = "getSupportedExtensions" and weight = 360.36 and contextType = "webgl2" ) or - ( this = "getShaderPrecisionFormat" and weight = 1175.17 and contextType = "webgl" ) + ( this = "readPixels" and weight = 24.33 and contextType = "webgl" ) or - ( this = "getContextAttributes" and weight = 1998.53 and contextType = "webgl" ) + ( this = "getShaderPrecisionFormat" and weight = 1347.35 and contextType = "webgl" ) or - ( this = "getSupportedExtensions" and weight = 1388.64 and contextType = "webgl" ) + ( this = "getContextAttributes" and weight = 2411.38 and contextType = "webgl" ) or - ( this = "readPixels" and weight = 22.43 and contextType = "webgl" ) + ( this = "getSupportedExtensions" and weight = 1484.82 and contextType = "webgl" ) or - ( this = "isPointInPath" and weight = 5210.68 and contextType = "2d" ) + ( this = "isPointInPath" and weight = 4255.55 and contextType = "2d" ) or - ( this = "readPixels" and weight = 610.19 and contextType = "webgl2" ) + ( this = "readPixels" and weight = 1004.16 and contextType = "webgl2" ) } float getWeight() { diff --git a/.github/codeql/queries/autogen_fpSensorProperty.qll b/.github/codeql/queries/autogen_fpSensorProperty.qll index 74bf3e4f988..bfc5c329068 100644 --- a/.github/codeql/queries/autogen_fpSensorProperty.qll +++ b/.github/codeql/queries/autogen_fpSensorProperty.qll @@ -6,7 +6,7 @@ class SensorProperty extends string { SensorProperty() { - ( this = "start" and weight = 92.53 ) + ( this = "start" and weight = 105.54 ) } float getWeight() { diff --git a/.github/workflows/PR-assignment-deps.yml b/.github/workflows/PR-assignment-deps.yml index 587555b705c..2d7fce88837 100644 --- a/.github/workflows/PR-assignment-deps.yml +++ b/.github/workflows/PR-assignment-deps.yml @@ -20,7 +20,7 @@ jobs: run: | npx gulp build - name: Upload dependencies.json - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: dependencies.json path: ./build/dist/dependencies.json @@ -28,7 +28,7 @@ jobs: run: | echo '{ "prNo": ${{ github.event.pull_request.number }} }' >> ${{ runner.temp}}/prInfo.json - name: Upload PR info - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: prInfo path: ${{ runner.temp}}/prInfo.json diff --git a/.github/workflows/jscpd.yml b/.github/workflows/jscpd.yml index c3021b2ced7..a4b2a14861f 100644 --- a/.github/workflows/jscpd.yml +++ b/.github/workflows/jscpd.yml @@ -56,7 +56,7 @@ jobs: - name: Upload unfiltered jscpd report if: always() - uses: actions/upload-artifact@v6 + uses: actions/upload-artifact@v7 with: name: unfiltered-jscpd-report path: ./jscpd-report.json @@ -89,7 +89,7 @@ jobs: - name: Upload filtered jscpd report if: env.filtered_report_exists == 'true' - uses: actions/upload-artifact@v6 + uses: actions/upload-artifact@v7 with: name: filtered-jscpd-report path: ./filtered-jscpd-report.json @@ -119,7 +119,7 @@ jobs: - name: Upload comment data if: env.filtered_report_exists == 'true' - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: comment path: ${{ runner.temp }}/comment.json diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index 39fdcc4067b..96d4f027324 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -16,7 +16,7 @@ jobs: - name: Set up Node.js uses: actions/setup-node@v6 with: - node-version: '20' + node-version: "20" - name: Checkout code uses: actions/checkout@v6 @@ -35,6 +35,9 @@ jobs: - name: Get the diff run: git diff --name-only origin/${{ github.event.pull_request.base.ref }}...refs/remotes/pull/${{ github.event.pull_request.number }}/merge | grep '^\(modules\|src\|libraries\|creative\)/.*\.js$' > __changed_files.txt || true + - name: Get newly added JS files in TS migration paths + run: git diff --name-only --diff-filter=A origin/${{ github.event.pull_request.base.ref }}...refs/remotes/pull/${{ github.event.pull_request.number }}/merge | grep '^\(modules\|src\|libraries\)/.*\.js$' > __new_js_files.txt || true + - name: Run linter on base branch run: npx eslint --no-inline-config --format json $(cat __changed_files.txt | xargs stat --printf '%n\n' 2> /dev/null) > __base.json || true @@ -56,7 +59,7 @@ jobs: const fs = require('fs'); const path = require('path'); const process = require('process'); - + function parse(fn) { return JSON.parse(fs.readFileSync(fn)).reduce((memo, data) => { const file = path.relative(process.cwd(), data.filePath); @@ -67,7 +70,7 @@ jobs: return memo; }, {}) } - + function mkDiff(old, new_) { const files = Object.fromEntries( Object.entries(new_) @@ -83,12 +86,23 @@ jobs: return memo; }, {errors: 0, warnings: 0, files}) } - - function mkComment({errors, warnings, files}) { + + function mkComment({errors, warnings, files}, newJsFiles) { function pl(noun, number) { return noun + (number === 1 ? '' : 's') } - if (errors === 0 && warnings === 0) return; + const comments = []; + + if (newJsFiles.length > 0) { + let jsComment = 'Whoa there partner! This project is migrating to typescript. Consider changing the new JS files to TS, with well-defined types for what interacts with the prebid public API (for example: bid params and configuration). Thanks!\n\n'; + newJsFiles.forEach((file) => { + jsComment += ` * \`${file}\`\n`; + }); + comments.push(jsComment); + } + + if (errors === 0 && warnings === 0) return comments.length > 0 ? comments.join('\n') : undefined; + const summary = []; if (errors) summary.push(`**${errors}** linter ${pl('error', errors)}`) if (warnings) summary.push(`**${warnings}** linter ${pl('warning', warnings)}`) @@ -99,12 +113,18 @@ jobs: if (warnings) summary.push(`+${warnings} ${pl('warning', warnings)}`) cm += ` * \`${file}\` (${summary.join(', ')})\n` }) - return cm; + comments.push(cm); + return comments.join('\n'); + } + + function readLines(fn) { + if (!fs.existsSync(fn)) return []; + return fs.readFileSync(fn, 'utf8').split('\n').map(line => line.trim()).filter(Boolean); } - + const [base, pr] = ['__base.json', '__pr.json'].map(parse); - const comment = mkComment(mkDiff(base, pr)); - + const comment = mkComment(mkDiff(base, pr), readLines('__new_js_files.txt')); + if (comment) { fs.writeFileSync("${{ runner.temp }}/comment.json", JSON.stringify({ issue_number: context.issue.number, @@ -117,7 +137,7 @@ jobs: - name: Upload comment data if: ${{ steps.comment.outputs.result == 'true' }} - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: comment path: ${{ runner.temp }}/comment.json diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 1cc6808f29f..924683ec0e0 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -71,6 +71,11 @@ on: BROWSERSTACK_ACCESS_KEY: description: "Browserstack access key" + +permissions: + contents: read + actions: read + jobs: checkout: name: "Define chunks" @@ -201,7 +206,7 @@ jobs: - name: 'Save coverage result' if: ${{ steps.coverage.outputs.coverage }} - uses: actions/upload-artifact@v6 + uses: actions/upload-artifact@v7 with: name: coverage-partial-${{inputs.test-cmd}}-${{ matrix.chunk-no }} path: ./build/coverage @@ -224,7 +229,7 @@ jobs: name: ${{ needs.build.outputs.built-key }} - name: Download coverage results - uses: actions/download-artifact@v7 + uses: actions/download-artifact@v8 with: path: ./build/coverage pattern: coverage-partial-${{ inputs.test-cmd }}-* diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 1209ca2057d..ed12de000c8 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -8,6 +8,10 @@ on: pull_request: types: [opened, synchronize, reopened] +permissions: + contents: read + actions: read + concurrency: group: test-${{ github.head_ref || github.ref }} cancel-in-progress: true diff --git a/AGENTS.md b/AGENTS.md index ce4e1353a9a..5bedeec8a9d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -21,6 +21,7 @@ This file contains instructions for the Codex agent and its friends when working ## General guidance - Node.js `>=20` is required; dependencies are managed with `npm`. +- Whenever possible, new modules should provide Typescript types for their public interface. - Added or modified code must have at least 80% unit test coverage. - Link any required documentation PRs in the PR description. - Avoid modifying files in `node_modules` or generated build artifacts under `build`. @@ -45,3 +46,15 @@ This file contains instructions for the Codex agent and its friends when working - Avoid running Babel over the entire project for incremental test runs. - Use `gulp serve-and-test --file ` or `gulp test --file` so Babel processes only the specified files. - Do not invoke commands that rebuild all modules when only a subset are changed. + +## Additional context +- for additional context on repo history, consult https://github.com/prebid/github-activity-db/blob/main/CLAUDE.md on how to download and access repo history in a database you can search locally. + +## Common adapter types +- When bid adapter changes need shared type references, look in the core source modules first: +- `src/adapters/bidderFactory.js` for bidder registration/build and bidder-spec wiring concepts. +- `src/userSync.js` for user sync interfaces, sync option handling, and sync registration behavior. +- `src/adapterManager.js` for adapter manager orchestration and type usage patterns around bidder lifecycle. +- Prefer importing or mirroring conventions from these modules instead of redefining local ad-hoc shapes. +- Use imported types for id, analytics, and rtd modules as well whenever possible. +- Always define types for public interface to an adapter, eg each bidder parameter. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b7a797beeb4..8ff2fd54055 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -6,6 +6,9 @@ master branch. Pull requests must have 80% code coverage before being considered for merge. Additional details about the process can be found [here](./PR_REVIEW.md). +Whenever possible, new modules should provide Typescript types for their public interface. +Examples of public interface are bid parameters and configuration (including configuration for analytics, userId, or real time data modules). + There are more details available if you'd like to contribute a [bid adapter](https://docs.prebid.org/dev-docs/bidder-adaptor.html) or [analytics adapter](https://docs.prebid.org/dev-docs/integrate-with-the-prebid-analytics-api.html). ## Issues diff --git a/PR_REVIEW.md b/PR_REVIEW.md index 94fe06c0f0c..75e3e0b35a6 100644 --- a/PR_REVIEW.md +++ b/PR_REVIEW.md @@ -19,6 +19,7 @@ General gulp commands include separate commands for serving the codebase on a bu ### General PR review Process +- Whenever possible, new modules should provide Typescript types for their public interface. Examples of public interface are bid parameters and configuration (including configuration for analytics, userId, or real time data modules). - All required global and bidder-adapter rules defined in the [Module Rules](https://docs.prebid.org/dev-docs/module-rules.html) must be followed. Please review these rules often - we depend on reviewers to enforce them. - Checkout the branch (these instructions are available on the GitHub PR page as well). - Verify PR is a single change type. Example, refactor OR bugfix. If more than 1 type, ask submitter to break out requests. diff --git a/creative/constants.js b/creative/constants.js index 26922cd8d79..2cf79d6202b 100644 --- a/creative/constants.js +++ b/creative/constants.js @@ -1,8 +1,8 @@ // eslint-disable-next-line prebid/validate-imports -import {AD_RENDER_FAILED_REASON, EVENTS, MESSAGES} from '../src/constants.js'; +import { AD_RENDER_FAILED_REASON, EVENTS, MESSAGES } from '../src/constants.js'; // eslint-disable-next-line prebid/validate-imports -export {PB_LOCATOR} from '../src/constants.js'; +export { PB_LOCATOR } from '../src/constants.js'; export const MESSAGE_REQUEST = MESSAGES.REQUEST; export const MESSAGE_RESPONSE = MESSAGES.RESPONSE; export const MESSAGE_EVENT = MESSAGES.EVENT; diff --git a/creative/crossDomain.js b/creative/crossDomain.js index 550f944ba5f..baa1cfa7a57 100644 --- a/creative/crossDomain.js +++ b/creative/crossDomain.js @@ -40,13 +40,13 @@ export function renderer(win) { } catch (e) { } - return function ({adId, pubUrl, clickUrl}) { + return function ({ adId, pubUrl, clickUrl }) { const pubDomain = new URL(pubUrl, window.location).origin; function sendMessage(type, payload, responseListener) { const channel = new MessageChannel(); channel.port1.onmessage = guard(responseListener); - target.postMessage(JSON.stringify(Object.assign({message: type, adId}, payload)), pubDomain, [channel.port2]); + target.postMessage(JSON.stringify(Object.assign({ message: type, adId }, payload)), pubDomain, [channel.port2]); } function onError(e) { @@ -88,8 +88,8 @@ export function renderer(win) { const W = renderer.contentWindow; // NOTE: on Firefox, `Promise.resolve(P)` or `new Promise((resolve) => resolve(P))` // does not appear to work if P comes from another frame - W.Promise.resolve(W.render(data, {sendMessage, mkFrame}, win)).then( - () => sendMessage(MESSAGE_EVENT, {event: EVENT_AD_RENDER_SUCCEEDED}), + W.Promise.resolve(W.render(data, { sendMessage, mkFrame }, win)).then( + () => sendMessage(MESSAGE_EVENT, { event: EVENT_AD_RENDER_SUCCEEDED }), onError ); }); @@ -101,7 +101,7 @@ export function renderer(win) { } sendMessage(MESSAGE_REQUEST, { - options: {clickUrl} + options: { clickUrl } }, onMessage); }; } diff --git a/creative/renderers/display/renderer.js b/creative/renderers/display/renderer.js index e2f6451bf62..7cbe78a93ef 100644 --- a/creative/renderers/display/renderer.js +++ b/creative/renderers/display/renderer.js @@ -1,8 +1,8 @@ import { registerReportingObserver } from '../../reporting.js'; import { BROWSER_INTERVENTION, MESSAGE_EVENT } from '../../constants.js'; -import {ERROR_NO_AD} from './constants.js'; +import { ERROR_NO_AD } from './constants.js'; -export function render({ad, adUrl, width, height, instl}, {mkFrame, sendMessage}, win) { +export function render({ ad, adUrl, width, height, instl }, { mkFrame, sendMessage }, win) { registerReportingObserver((report) => { sendMessage(MESSAGE_EVENT, { event: BROWSER_INTERVENTION, @@ -24,7 +24,7 @@ export function render({ad, adUrl, width, height, instl}, {mkFrame, sendMessage} }); } const doc = win.document; - const attrs = {width: width ?? '100%', height: height ?? '100%'}; + const attrs = { width: width ?? '100%', height: height ?? '100%' }; if (adUrl && !ad) { attrs.src = adUrl; } else { diff --git a/creative/renderers/native/renderer.js b/creative/renderers/native/renderer.js index 86236c8f948..badbc121fb5 100644 --- a/creative/renderers/native/renderer.js +++ b/creative/renderers/native/renderer.js @@ -1,9 +1,9 @@ import { registerReportingObserver } from '../../reporting.js'; import { BROWSER_INTERVENTION, MESSAGE_EVENT } from '../../constants.js'; -import {ACTION_CLICK, ACTION_IMP, ACTION_RESIZE, MESSAGE_NATIVE, ORTB_ASSETS} from './constants.js'; +import { ACTION_CLICK, ACTION_IMP, ACTION_RESIZE, MESSAGE_NATIVE, ORTB_ASSETS } from './constants.js'; -export function getReplacer(adId, {assets = [], ortb, nativeKeys = {}}) { - const assetValues = Object.fromEntries((assets).map(({key, value}) => [key, value])); +export function getReplacer(adId, { assets = [], ortb, nativeKeys = {} }) { + const assetValues = Object.fromEntries((assets).map(({ key, value }) => [key, value])); let repl = Object.fromEntries( Object.entries(nativeKeys).flatMap(([name, key]) => { const value = assetValues.hasOwnProperty(name) ? assetValues[name] : undefined; @@ -58,7 +58,7 @@ function getInnerHTML(node) { } export function getAdMarkup(adId, nativeData, replacer, win, load = loadScript) { - const {rendererUrl, assets, ortb, adTemplate} = nativeData; + const { rendererUrl, assets, ortb, adTemplate } = nativeData; const doc = win.document; if (rendererUrl) { return load(rendererUrl, doc).then(() => { @@ -74,14 +74,14 @@ export function getAdMarkup(adId, nativeData, replacer, win, load = loadScript) } } -export function render({adId, native}, {sendMessage}, win, getMarkup = getAdMarkup) { +export function render({ adId, native }, { sendMessage }, win, getMarkup = getAdMarkup) { registerReportingObserver((report) => { sendMessage(MESSAGE_EVENT, { event: BROWSER_INTERVENTION, intervention: report }); }, ['intervention']); - const {head, body} = win.document; + const { head, body } = win.document; const resize = () => { // force redraw - for some reason this is needed to get the right dimensions body.style.display = 'none'; @@ -103,13 +103,13 @@ export function render({adId, native}, {sendMessage}, win, getMarkup = getAdMark return getMarkup(adId, native, replacer, win).then(markup => { replaceMarkup(body, markup); if (typeof win.postRenderAd === 'function') { - win.postRenderAd({adId, ...native}); + win.postRenderAd({ adId, ...native }); } win.document.querySelectorAll('.pb-click').forEach(el => { const assetId = el.getAttribute('hb_native_asset_id'); - el.addEventListener('click', () => sendMessage(MESSAGE_NATIVE, {action: ACTION_CLICK, assetId})); + el.addEventListener('click', () => sendMessage(MESSAGE_NATIVE, { action: ACTION_CLICK, assetId })); }); - sendMessage(MESSAGE_NATIVE, {action: ACTION_IMP}); + sendMessage(MESSAGE_NATIVE, { action: ACTION_IMP }); win.document.readyState === 'complete' ? resize() : win.onload = resize; }); } diff --git a/eslint.config.js b/eslint.config.js index a9b7fe04153..a5310749559 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -9,6 +9,7 @@ const path = require('path'); const _ = require('lodash'); const tseslint = require('typescript-eslint'); const {getSourceFolders} = require('./gulpHelpers.js'); +const APPROVED_LOAD_EXTERNAL_SCRIPT_PATHS = require('./plugins/eslint/approvedLoadExternalScriptPaths.js'); function jsPattern(name) { return [`${name}/**/*.js`, `${name}/**/*.mjs`] @@ -106,10 +107,13 @@ module.exports = [ }, rules: { 'comma-dangle': 'off', + '@stylistic/comma-dangle': 'off', semi: 'off', + '@stylistic/semi': 'off', 'no-undef': 2, 'no-console': 'error', 'space-before-function-paren': 'off', + '@stylistic/space-before-function-paren': 'off', 'import/extensions': ['error', 'ignorePackages'], 'no-restricted-syntax': [ 'error', @@ -122,6 +126,13 @@ module.exports = [ message: "Assigning a function to 'logResult, 'logMessage', 'logInfo', 'logWarn', or 'logError' is not allowed." }, ], + 'no-restricted-imports': [ + 'error', { + patterns: [ + '**/src/adloader.js' + ] + } + ], // Exceptions below this line are temporary (TM), so that eslint can be added into the CI process. // Violations of these styles should be fixed, and the exceptions removed over time. @@ -156,19 +167,9 @@ module.exports = [ 'object-shorthand': 'off', 'prefer-regex-literals': 'off', 'no-case-declarations': 'off', - 'no-useless-catch': 'off', '@stylistic/quotes': 'off', '@stylistic/quote-props': 'off', - '@stylistic/array-bracket-spacing': 'off', - '@stylistic/object-curly-spacing': 'off', - '@stylistic/semi': 'off', - '@stylistic/space-before-function-paren': 'off', '@stylistic/multiline-ternary': 'off', - '@stylistic/computed-property-spacing': 'off', - '@stylistic/lines-between-class-members': 'off', - '@stylistic/comma-dangle': 'off', - '@stylistic/object-curly-newline': 'off', - '@stylistic/object-property-newline': 'off', } }, ...Object.entries(allowedImports).map(([path, allowed]) => { @@ -201,6 +202,21 @@ module.exports = [ object: 'navigator', message: 'use ajax.js instead' }, + { + property: 'doNotTrack', + object: 'navigator', + message: 'DNT was deprecated by W3C; Prebid no longer supports DNT signals' + }, + { + property: 'msDoNotTrack', + object: 'navigator', + message: 'DNT was deprecated by W3C; Prebid no longer supports DNT signals' + }, + { + property: 'doNotTrack', + object: 'window', + message: 'DNT was deprecated by W3C; Prebid no longer supports DNT signals' + }, ...['outerText', 'innerText'].map(property => ({ property, message: 'use .textContent instead' @@ -273,4 +289,22 @@ module.exports = [ '@typescript-eslint/no-require-imports': 'off' } }, -] + // Override: allow loadExternalScript import in approved files (excluding BidAdapters) + { + files: APPROVED_LOAD_EXTERNAL_SCRIPT_PATHS.filter(p => !p.includes('BidAdapter')).map(p => { + // If path doesn't end with .js/.ts/.mjs, treat as folder pattern + if (!p.match(/\.(js|ts|mjs)$/)) { + return `${p}/**/*.{js,ts,mjs}`; + } + return p; + }), + rules: { + 'no-restricted-imports': [ + 'error', + { + patterns: [] + } + ], + } + }, + ] diff --git a/gulpfile.js b/gulpfile.js index e5a4db41884..fd1f7f476d6 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -53,7 +53,7 @@ function bundleToStdout() { bundleToStdout.displayName = 'bundle-to-stdout'; function clean() { - return gulp.src(['build', 'dist'], { + return gulp.src(['.cache', 'build', 'dist'], { read: false, allowEmpty: true }) @@ -540,7 +540,10 @@ gulp.task('update-codeql', function (done) { // npm will by default use .gitignore, so create an .npmignore that is a copy of it except it includes "dist" gulp.task('setup-npmignore', execaTask("sed 's/^\\/\\?dist\\/\\?$//g;w .npmignore' .gitignore", {quiet: true})); gulp.task('build', gulp.series(clean, 'build-bundle-prod', setupDist)); -gulp.task('build-release', gulp.series('update-codeql', 'build', updateCreativeExample, 'update-browserslist', 'setup-npmignore')); +// build for release - in addition to 'build', run tasks that update the codebase to be included in a release commit +gulp.task('build-release', gulp.series('update-codeql', 'build', updateCreativeExample, 'update-browserslist')); +// prepare NPM release - 'build' to generate files in dist/; 'setup-npmignore' to make sure 'dist' is published in NPM +gulp.task('prepare-release', gulp.series('build', 'setup-npmignore')) gulp.task('build-postbid', gulp.series(escapePostbidConfig, buildPostbid)); gulp.task('serve', gulp.series(clean, lint, precompile(), gulp.parallel('build-bundle-dev-no-precomp', watch, test))); diff --git a/integrationExamples/audio/audioGam.html b/integrationExamples/audio/audioGam.html index 8a8a4398637..6392e5f0b6e 100644 --- a/integrationExamples/audio/audioGam.html +++ b/integrationExamples/audio/audioGam.html @@ -115,9 +115,11 @@ const bid = bidResponse.bids[0]; + const adUnit = adUnits.find(au => au.code === 'div-gpt-ad-51545-0'); + const adXml = await pbjs.adServers.gam.getVastXml({ bid, - adUnit: 'div-gpt-ad-51545-0', + adUnit, params: { iu: '/41758329/localcache', url: "https://pubads.g.doubleclick.net/gampad/ads?iu=/41758329/localcache&sz=640x480&gdfp_req=1&output=vast&env=vp", diff --git a/integrationExamples/gpt/adcluster_banner_example.html b/integrationExamples/gpt/adcluster_banner_example.html new file mode 100644 index 00000000000..4f7bf646bb9 --- /dev/null +++ b/integrationExamples/gpt/adcluster_banner_example.html @@ -0,0 +1,115 @@ + + + + + Adcluster Adapter Test + + + + + + + + + + +

Prebid.js Live Adapter Test

+
+ + + + diff --git a/integrationExamples/gpt/adcluster_video_example.html b/integrationExamples/gpt/adcluster_video_example.html new file mode 100644 index 00000000000..0a309b24749 --- /dev/null +++ b/integrationExamples/gpt/adcluster_video_example.html @@ -0,0 +1,291 @@ + + + + + Adcluster Adapter – Outstream Test with Fallback + + + + + + + + + +

Adcluster Adapter – Outstream Test (AN renderer + IMA fallback)

+
+ +
+ + + + diff --git a/integrationExamples/gpt/insurads.html b/integrationExamples/gpt/insurads.html new file mode 100644 index 00000000000..92f6b7df8b2 --- /dev/null +++ b/integrationExamples/gpt/insurads.html @@ -0,0 +1,121 @@ + + + + + + + + + + + + + +

Prebid.js Test

+
Div-1
+
+ +
+ + + + diff --git a/integrationExamples/gpt/localCacheGam.html b/integrationExamples/gpt/localCacheGam.html index 6b203d33ee9..9169fcdf5e3 100644 --- a/integrationExamples/gpt/localCacheGam.html +++ b/integrationExamples/gpt/localCacheGam.html @@ -13,6 +13,8 @@ mediaTypes: { video: { playerSize: [640, 360], + playbackmethod: [2, 6], + api: [2, 7, 8], } }, video: { @@ -95,9 +97,11 @@ const bid = bidResponse.bids[0]; + const adUnit = adUnits.find(au => au.code === 'div-gpt-ad-51545-0'); + const vastXml = await pbjs.adServers.gam.getVastXml({ bid, - adUnit: 'div-gpt-ad-51545-0', + adUnit, params: { iu: '/41758329/localcache', url: "https://pubads.g.doubleclick.net/gampad/ads?iu=/41758329/localcache&sz=640x480&gdfp_req=1&output=vast&env=vp", diff --git a/integrationExamples/gpt/mediago_test.html b/integrationExamples/gpt/mediago_test.html index d09e814eda4..5df9129ef3b 100644 --- a/integrationExamples/gpt/mediago_test.html +++ b/integrationExamples/gpt/mediago_test.html @@ -121,116 +121,6 @@ }] } ]; - - var pbjs = pbjs || {}; - pbjs.que = pbjs.que || []; - - // Store request information for debugging - var requestLogs = []; - - // Intercept AJAX requests to view sent data - var originalOpen = XMLHttpRequest.prototype.open; - var originalSend = XMLHttpRequest.prototype.send; - - XMLHttpRequest.prototype.open = function(method, url, ...args) { - this._url = url; - this._method = method; - return originalOpen.apply(this, [method, url, ...args]); - }; - - XMLHttpRequest.prototype.send = function(data) { - if (this._url && this._url.includes('mediago.io')) { - try { - var requestData = JSON.parse(data); - requestLogs.push({ - url: this._url, - method: this._method, - data: requestData, - timestamp: new Date().toISOString() - }); - console.log('Mediago Request:', requestData); - - // Check if IDs in imp array are unique - if (requestData.imp && Array.isArray(requestData.imp)) { - var ids = requestData.imp.map(imp => imp.id).filter(Boolean); - var uniqueIds = [...new Set(ids)]; - var isUnique = ids.length === uniqueIds.length; - - console.log('ID Uniqueness Check:'); - console.log(' Total IDs:', ids.length); - console.log(' Unique IDs:', uniqueIds.length); - console.log(' Is Unique:', isUnique ? '✓ Yes' : '✗ No'); - console.log(' ID List:', ids); - - if (!isUnique) { - console.warn('Warning: Duplicate IDs found!', ids); - } - - updateDebugInfo(requestData, isUnique, ids, uniqueIds); - } - } catch (e) { - console.error('Failed to parse request data:', e); - } - } - - // Listen for response - this.addEventListener('load', function() { - if (this._url && this._url.includes('mediago.io')) { - try { - var response = JSON.parse(this.responseText); - console.log('Mediago Response:', response); - updateResponseInfo(response); - } catch (e) { - console.error('Failed to parse response data:', e); - } - } - }); - - return originalSend.apply(this, [data]); - }; - - function updateDebugInfo(requestData, isUnique, ids, uniqueIds) { - var debugDiv = document.getElementById('debug-info'); - if (!debugDiv) return; - - var html = '

Request Debug Info

'; - html += '

Time: ' + new Date().toLocaleString() + '

'; - html += '

ID Uniqueness: ' + (isUnique ? '✓ Pass' : '✗ Fail') + '

'; - html += '

Total IDs: ' + ids.length + '

'; - html += '

Unique IDs: ' + uniqueIds.length + '

'; - html += '

ID List:

'; - html += '
    '; - ids.forEach((id, index) => { - var isDup = ids.indexOf(id) !== ids.lastIndexOf(id); - html += '
  • '; - html += 'Ad Unit ' + (index + 1) + ': ' + id + ''; - if (isDup) html += ' (Duplicate!)'; - html += '
  • '; - }); - html += '
'; - - html += '

Request Data (imp section):

'; - html += '
' + JSON.stringify(requestData.imp, null, 2) + '
'; - - debugDiv.innerHTML = html; - } - - function updateResponseInfo(response) { - var responseDiv = document.getElementById('response-info'); - if (!responseDiv) return; - - var html = '

Response Info

'; - html += '

Time: ' + new Date().toLocaleString() + '

'; - if (response.seatbid && response.seatbid.length > 0) { - html += '

Number of bids returned: ' + response.seatbid[0].bid.length + '

'; - html += '
' + JSON.stringify(response, null, 2) + '
'; - } else { - html += '

No valid response received

'; - } - - responseDiv.innerHTML = html; - } - @@ -282,6 +172,9 @@

Ad Unit 3 (728x90)

pbjs.que.push(function() { console.log('Prebid.js loaded'); + // Enable TID (Transaction ID) transmission - required since Prebid 8 for ortb2Imp.ext.tid to reach bidders + pbjs.setConfig({ enableTIDs: true }); + // Set pageUrl and ortb2 site configuration to simulate other site // pageUrl will override Prebid's referrer detection // pbjs.setConfig({ @@ -420,7 +313,6 @@

Ad Unit 3 (728x90)

function clearDebug() { document.getElementById('debug-info').innerHTML = '

Debug info cleared

'; document.getElementById('response-info').innerHTML = '

Response info cleared

'; - requestLogs = []; // Clear ad containers ['mediago-ad-1', 'mediago-ad-2', 'mediago-ad-3'].forEach(function(id) { diff --git a/integrationExamples/gpt/neuwoRtdProvider_example.html b/integrationExamples/gpt/neuwoRtdProvider_example.html index 3d6fef98995..68c95fe8b4f 100644 --- a/integrationExamples/gpt/neuwoRtdProvider_example.html +++ b/integrationExamples/gpt/neuwoRtdProvider_example.html @@ -26,28 +26,28 @@ var adUnits = [ { - code: '/19968336/header-bid-tag-1', + code: "/19968336/header-bid-tag-1", mediaTypes: { banner: { sizes: div_1_sizes } }, bids: [{ - bidder: 'appnexus', + bidder: "appnexus", params: { placementId: 13144370 } }] }, { - code: '/19968336/header-bid-tag-1', + code: "/19968336/header-bid-tag-1", mediaTypes: { banner: { sizes: div_2_sizes } }, bids: [{ - bidder: 'appnexus', + bidder: "appnexus", params: { placementId: 13144370 } @@ -86,12 +86,12 @@ // Custom Timeout logic in onSettingsUpdate() googletag.cmd.push(function () { - googletag.defineSlot('/19968336/header-bid-tag-1', div_1_sizes, 'div-1').addService(googletag.pubads()); + googletag.defineSlot("/19968336/header-bid-tag-1", div_1_sizes, "div-1").addService(googletag.pubads()); googletag.pubads().enableSingleRequest(); googletag.enableServices(); }); googletag.cmd.push(function () { - googletag.defineSlot('/19968336/header-bid-tag-1', div_2_sizes, 'div-2').addService(googletag.pubads()); + googletag.defineSlot("/19968336/header-bid-tag-1", div_2_sizes, "div-2").addService(googletag.pubads()); googletag.pubads().enableSingleRequest(); googletag.enableServices(); }); @@ -99,47 +99,65 @@ // 3. User Triggered Setup (RTD Module) function onSettingsUpdate() { - const inputNeuwoApiToken = document.getElementById('neuwo-api-token'); - const neuwoApiToken = inputNeuwoApiToken ? inputNeuwoApiToken.value : ''; + const inputNeuwoApiToken = document.getElementById("neuwo-api-token"); + const neuwoApiToken = inputNeuwoApiToken ? inputNeuwoApiToken.value : ""; if (!neuwoApiToken) { - alert('Please enter your token for Neuwo AI API to the field'); + alert("Please enter your token for Neuwo AI API to the field"); if (inputNeuwoApiToken) inputNeuwoApiToken.focus(); return; } - const inputNeuwoApiUrl = document.getElementById('neuwo-api-url'); - const neuwoApiUrl = inputNeuwoApiUrl ? inputNeuwoApiUrl.value : ''; + const inputNeuwoApiUrl = document.getElementById("neuwo-api-url"); + const neuwoApiUrl = inputNeuwoApiUrl ? inputNeuwoApiUrl.value : ""; if (!neuwoApiUrl) { - alert('Please enter Neuwo AI API url to the field'); + alert("Please enter Neuwo AI API url to the field"); if (inputNeuwoApiUrl) inputNeuwoApiUrl.focus(); return; } - const inputWebsiteToAnalyseUrl = document.getElementById('website-to-analyse-url'); + const inputWebsiteToAnalyseUrl = document.getElementById("website-to-analyse-url"); const websiteToAnalyseUrl = inputWebsiteToAnalyseUrl ? inputWebsiteToAnalyseUrl.value : undefined; - const inputIabContentTaxonomyVersion = document.getElementById('iab-content-taxonomy-version'); + const inputIabContentTaxonomyVersion = document.getElementById("iab-content-taxonomy-version"); const iabContentTaxonomyVersion = inputIabContentTaxonomyVersion ? inputIabContentTaxonomyVersion.value : undefined; // Cache Option - const inputEnableCache = document.getElementById('enable-cache'); + const inputEnableCache = document.getElementById("enable-cache"); const enableCache = inputEnableCache ? inputEnableCache.checked : undefined; + // OpenRTB 2.5 Category Fields Option + const inputEnableOrtb25Fields = document.getElementById("enable-ortb25-fields"); + const enableOrtb25Fields = inputEnableOrtb25Fields ? inputEnableOrtb25Fields.checked : true; + // URL Stripping Options - const inputStripAllQueryParams = document.getElementById('strip-all-query-params'); + const inputStripAllQueryParams = document.getElementById("strip-all-query-params"); const stripAllQueryParams = inputStripAllQueryParams ? inputStripAllQueryParams.checked : undefined; - const inputStripQueryParamsForDomains = document.getElementById('strip-query-params-for-domains'); - const stripQueryParamsForDomainsValue = inputStripQueryParamsForDomains ? inputStripQueryParamsForDomains.value.trim() : ''; - const stripQueryParamsForDomains = stripQueryParamsForDomainsValue ? stripQueryParamsForDomainsValue.split(',').map(d => d.trim()).filter(d => d) : undefined; + const inputStripQueryParamsForDomains = document.getElementById("strip-query-params-for-domains"); + const stripQueryParamsForDomainsValue = inputStripQueryParamsForDomains ? inputStripQueryParamsForDomains.value.trim() : ""; + const stripQueryParamsForDomains = stripQueryParamsForDomainsValue ? stripQueryParamsForDomainsValue.split(",").map(d => d.trim()).filter(d => d) : undefined; - const inputStripQueryParams = document.getElementById('strip-query-params'); - const stripQueryParamsValue = inputStripQueryParams ? inputStripQueryParams.value.trim() : ''; - const stripQueryParams = stripQueryParamsValue ? stripQueryParamsValue.split(',').map(p => p.trim()).filter(p => p) : undefined; + const inputStripQueryParams = document.getElementById("strip-query-params"); + const stripQueryParamsValue = inputStripQueryParams ? inputStripQueryParams.value.trim() : ""; + const stripQueryParams = stripQueryParamsValue ? stripQueryParamsValue.split(",").map(p => p.trim()).filter(p => p) : undefined; - const inputStripFragments = document.getElementById('strip-fragments'); + const inputStripFragments = document.getElementById("strip-fragments"); const stripFragments = inputStripFragments ? inputStripFragments.checked : undefined; + // IAB Taxonomy Filtering Option + const inputEnableFiltering = document.getElementById("enable-iab-filtering"); + const enableIabFiltering = inputEnableFiltering ? inputEnableFiltering.checked : false; + + // Build iabTaxonomyFilters object only if filtering is enabled + const iabTaxonomyFilters = enableIabFiltering ? { + ContentTier1: { limit: 1, threshold: 0.1 }, + ContentTier2: { limit: 2, threshold: 0.1 }, + ContentTier3: { limit: 3, threshold: 0.15 }, + AudienceTier3: { limit: 3, threshold: 0.2 }, + AudienceTier4: { limit: 5, threshold: 0.2 }, + AudienceTier5: { limit: 7, threshold: 0.3 }, + } : undefined; + pbjs.que.push(function () { pbjs.setConfig({ debug: true, @@ -155,10 +173,12 @@ websiteToAnalyseUrl, iabContentTaxonomyVersion, enableCache, + enableOrtb25Fields, stripAllQueryParams, stripQueryParamsForDomains, stripQueryParams, - stripFragments + stripFragments, + iabTaxonomyFilters, } } ] @@ -185,7 +205,7 @@

Basic Prebid.js Example using Neuwo Rtd Provider

- Looks like you're not following the testing environment setup, try accessing + Looks like you"re not following the testing environment setup, try accessing http://localhost:9999/integrationExamples/gpt/neuwoRtdProvider_example.html @@ -215,12 +235,14 @@

Neuwo Rtd Provider Configuration

- +

IAB Content Taxonomy Options

- +

Cache Options

@@ -231,6 +253,14 @@

Cache Options

+

OpenRTB 2.5 Category Fields

+
+ +
+

URL Cleaning Options

- +
- +
+

IAB Taxonomy Filtering Options

+
+ +
+ When enabled, uses these hardcoded filters:
+ • ContentTier1: top 1 (≥10% relevance)
+ • ContentTier2: top 2 (≥10% relevance)
+ • ContentTier3: top 3 (≥15% relevance)
+ • AudienceTier3: top 3 (≥20% relevance)
+ • AudienceTier4: top 5 (≥20% relevance)
+ • AudienceTier5: top 7 (≥30% relevance) +
+
+ @@ -259,10 +309,10 @@

Ad Examples

Div-1

-
- Ad spot div-1: This content will be replaced by prebid.js and/or related components once you click @@ -272,10 +322,10 @@

Div-1

Div-2

-
- Ad spot div-2: This content will be replaced by prebid.js and/or related components once you click @@ -286,82 +336,327 @@

Div-2

Neuwo Data in Bid Request

-

The retrieved data from Neuwo API is injected into the bid request as OpenRTB (ORTB2)`site.content.data` and - `user.data`. Full bid request can be inspected in Developer Tools Console under +

The retrieved data from Neuwo API is injected into the bid request as OpenRTB (ORTB2) + site.content.data and + user.data. Full bid request can be inspected in Developer Tools Console under INFO: NeuwoRTDModule injectIabCategories: post-injection bidsConfig

+

Neuwo Site Content Data

+
No data yet. Click "Update" to fetch data.
+

Neuwo User Data

+
No data yet. Click "Update" to fetch data.
+

Neuwo OpenRTB 2.5 Category Fields (IAB Content Taxonomy 1.0) Data

+
No data yet. Click "Update" to fetch data (requires enableOrtb25Fields and /v1/iab endpoint).
+
+ +
+

Accessing Neuwo Data in JavaScript

+

Listen to the bidRequested event to access the enriched ORTB2 data:

+
+pbjs.onEvent("bidRequested", function(bidRequest) {
+    const ortb2 = bidRequest.ortb2;
+    const neuwoSiteData = ortb2?.site?.content?.data?.find(d => d.name === "www.neuwo.ai");
+    const neuwoUserData = ortb2?.user?.data?.find(d => d.name === "www.neuwo.ai");
+    console.log("Neuwo data:", { siteContent: neuwoSiteData, user: neuwoUserData });
+});
+        
+

After clicking "Update", the Neuwo data is stored in the global neuwoData variable. Open + Developer Tools Console to see the logged data.

+

Note: Event timing tests for multiple Prebid.js events (auctionInit, bidRequested, + beforeBidderHttp, bidResponse, auctionEnd) are available in the page source code but are commented out. To + enable them, uncomment the timing test section in the JavaScript code.

+
+ +
+

For more information about Neuwo RTD Module configuration and accessing data retrieved from Neuwo API, see modules/neuwoRtdProvider.md.

+ + + + + + + + + + + + + +

Taboola Multiformat Test

+

The multiformat ad unit should generate requests to both endpoints.

+ +

Bid Results

+
Waiting for bids...
+ +
+
Banner Only
+
Waiting for bids...
+
Waiting for bids...
+
Debug: div-banner
Loading...
+ +
+
Native Only
+
Waiting for bids...
+
Waiting for bids...
+
Debug: div-native
Loading...
+ +
+
Multiformat (Banner + Native)
+
Waiting for bids...
+
Waiting for bids...
+
Debug: div-multiformat
Loading...
+ + + diff --git a/integrationExamples/gpt/userId_example.html b/integrationExamples/gpt/userId_example.html index 3861037b401..618874f0bd5 100644 --- a/integrationExamples/gpt/userId_example.html +++ b/integrationExamples/gpt/userId_example.html @@ -234,9 +234,6 @@ "expires": 28 } }, - { - "name": "quantcastId" - }, { "name": "criteo" }, diff --git a/integrationExamples/longform/basic_w_bidderSettings.html b/integrationExamples/longform/basic_w_bidderSettings.html deleted file mode 100644 index fb87ea5d990..00000000000 --- a/integrationExamples/longform/basic_w_bidderSettings.html +++ /dev/null @@ -1,148 +0,0 @@ - - - - - Prebid Freewheel Integration Demo - - - - - - - - - - - - - - - - - - - -

Prebid Freewheel Test Page

-

requireExactDuration = false

-
-
- -
-
-
-
-

- -

-
-
-
- // bids -
-
-
-
-
-

- -

-
-
-
- // bids -
-
-
-
-
-
-
- - - - \ No newline at end of file diff --git a/integrationExamples/longform/basic_w_custom_adserver_translation.html b/integrationExamples/longform/basic_w_custom_adserver_translation.html deleted file mode 100644 index 2dbb89506b5..00000000000 --- a/integrationExamples/longform/basic_w_custom_adserver_translation.html +++ /dev/null @@ -1,135 +0,0 @@ - - - - - Prebid Freewheel Integration Demo - - - - - - - - - - - - - - - - - - - -

Prebid Freewheel Integration Demo

-

custom adserver translation file

-
-
- -
-
-
-
-

- -

-
-
-
- // bids -
-
-
-
-
-

- -

-
-
-
- // bids -
-
-
-
-
-
-
- - - - diff --git a/integrationExamples/longform/basic_w_priceGran.html b/integrationExamples/longform/basic_w_priceGran.html deleted file mode 100644 index 4ea9d5d19be..00000000000 --- a/integrationExamples/longform/basic_w_priceGran.html +++ /dev/null @@ -1,156 +0,0 @@ - - - - - Prebid Freewheel Integration Demo - - - - - - - - - - - - - - - - - - - -

Prebid Freewheel Test Page

-

requireExactDuration = false

-
-
- -
-
-
-
-

- -

-
-
-
- // bids -
-
-
-
-
-

- -

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

Prebid Freewheel Test Page

-

requireExactDuration = true

-
-
- -
-
-
-
-

- -

-
-
-
- // bids -
-
-
-
-
-

- -

-
-
-
- // bids -
-
-
-
-
-
-
- - - - diff --git a/integrationExamples/longform/basic_wo_brandCategoryExclusion.html b/integrationExamples/longform/basic_wo_brandCategoryExclusion.html deleted file mode 100644 index 47ea4b7f47d..00000000000 --- a/integrationExamples/longform/basic_wo_brandCategoryExclusion.html +++ /dev/null @@ -1,133 +0,0 @@ - - - - - Prebid Freewheel Integration Demo - - - - - - - - - - - - - - - - - - - -

Prebid Freewheel Test Page

-

brandCategoryExclusion = false

-
-
- -
-
-
-
-

- -

-
-
-
- // bids -
-
-
-
-
-

- -

-
-
-
- // bids -
-
-
-
-
-
-
- - - - diff --git a/integrationExamples/longform/basic_wo_requireExactDuration.html b/integrationExamples/longform/basic_wo_requireExactDuration.html deleted file mode 100644 index 6dbedbc6d39..00000000000 --- a/integrationExamples/longform/basic_wo_requireExactDuration.html +++ /dev/null @@ -1,134 +0,0 @@ - - - - - Prebid Freewheel Integration Demo - - - - - - - - - - - - - - - - - - - -

Prebid Freewheel Test Page

-

requireExactDuration = false

-
-
- -
-
-
-
-

- -

-
-
-
- // bids -
-
-
-
-
-

- -

-
-
-
- // bids -
-
-
-
-
-
-
- - - - diff --git a/integrationExamples/noadserver/connatixBidAdapter_sample.html b/integrationExamples/noadserver/connatixBidAdapter_sample.html new file mode 100644 index 00000000000..2093397ad86 --- /dev/null +++ b/integrationExamples/noadserver/connatixBidAdapter_sample.html @@ -0,0 +1,97 @@ + + + + + + + + + +

Connatix viewability demo

+

Scroll the page to move the viewability container in and out of view.

+

The reported viewability percentage updates based on how much of the container is visible.

+

Open the browser console (debug mode) to inspect bid requests and viewability values.

+
+
+

Viewability Container

+

scroll to change % in view

+
+
+ + diff --git a/integrationExamples/shapingRules/rules.json b/integrationExamples/shapingRules/rules.json new file mode 100644 index 00000000000..3be6ecfb574 --- /dev/null +++ b/integrationExamples/shapingRules/rules.json @@ -0,0 +1,151 @@ +{ + "enabled": true, + "generateRulesFromBidderConfig": true, + "timestamp": "20250131 00:00:00", + "ruleSets": [ + { + "stage": "processed-auction-request", + "name": "exclude-in-jpn", + "version": "1234", + "modelGroups": [ + { + "weight": 98, + "analyticsKey": "experiment-name", + "version": "4567", + "schema": [ + { "function": "deviceCountryIn", "args": [["JPN"]] } + ], + "default": [], + "rules": [ + { + "conditions": ["true"], + "results": [ + { + "function": "excludeBidders", + "args": [ + { "bidders": ["testBidder"], "seatnonbid": 203, "analyticsValue": "rmjpn" }, + { "bidders": ["bidderD"], "seatnonbid": 203, "ifSyncedId": false, "analyticsValue": "rmjpn" } + ] + } + ] + }, + { + "conditions": [], + "results": [] + } + ] + }, + { + "weight": 2, + "analyticsKey": "experiment-name-2", + "version": "4567", + "schema": [ + { "function": "adUnitCode" } + ], + "default": [], + "rules": [ + { + "conditions": ["adUnit-0000"], + "results": [ + { + "function": "excludeBidders", + "args": [ + { "bidders": ["testBidder"], "seatnonbid": 203, "analyticsValue": "rmjpn" }, + { "bidders": ["bidderD"], "seatnonbid": 203, "ifSyncedId": false, "analyticsValue": "rmjpn" } + ] + } + ] + } + ] + } + ] + }, + { + "stage": "processed-auction", + "modelGroups": [ + { + "schema": [{ "function": "percent", "args": [5] }], + "analyticsKey": "bidderC-testing", + "default": [ + { + "function": "logAtag", + "args": { "analyticsValue": "default-allow" } + } + ], + "rules": [ + { + "conditions": ["false"], + "results": [ + { + "function": "excludeBidders", + "args": [ + { + "bidders": ["bidderC"], + "seatnonbid": 203, + "analyticsValue": "excluded" + } + ] + } + ] + } + ] + } + ] + }, + { + "stage": "processed-auction", + "modelGroups": [ + { + "schema": [ + { + "function": "deviceCountry", "args": ["USA"] + } + ], + "rules": [ + { + "conditions": ["true"], + "results": [ + { + "function": "excludeBidders", + "args": [ + { + "bidders": ["bidderM", "bidderN", "bidderO", "bidderP"], + "seatNonBid": 203 + } + ] + } + ] + } + ] + } + ] + }, + { + "stage": "processed-auction", + "modelGroups": [ + { + "schema": [ + { "function": "deviceCountryIn", "args": [["USA", "CAN"]] } + ], + "analyticsKey": "bidder-yaml", + "rules": [ + { + "conditions": ["false"], + "results": [ + { + "function": "excludeBidders", + "args": [ + { + "bidders": ["bidderX"], + "seatNonBid": 203 + } + ] + } + ] + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/integrationExamples/shapingRules/shapingRulesModule.html b/integrationExamples/shapingRules/shapingRulesModule.html new file mode 100644 index 00000000000..8843348e731 --- /dev/null +++ b/integrationExamples/shapingRules/shapingRulesModule.html @@ -0,0 +1,168 @@ + + + Prebid Test Bidder Example + + + + +

Prebid Test Bidder Example

+
Banner ad
+ + + diff --git a/libraries/adkernelUtils/adkernelUtils.js b/libraries/adkernelUtils/adkernelUtils.js index 0b2d48f3824..8dfae13010e 100644 --- a/libraries/adkernelUtils/adkernelUtils.js +++ b/libraries/adkernelUtils/adkernelUtils.js @@ -2,7 +2,7 @@ export function getBidFloor(bid, mediaType, sizes) { var floor; var size = sizes.length === 1 ? sizes[0] : '*'; if (typeof bid.getFloor === 'function') { - const floorInfo = bid.getFloor({currency: 'USD', mediaType, size}); + const floorInfo = bid.getFloor({ currency: 'USD', mediaType, size }); if (typeof floorInfo === 'object' && floorInfo.currency === 'USD' && !isNaN(parseFloat(floorInfo.floor))) { floor = parseFloat(floorInfo.floor); } diff --git a/libraries/adrelevantisUtils/bidderUtils.js b/libraries/adrelevantisUtils/bidderUtils.js index 04396e76964..70a28a7e65a 100644 --- a/libraries/adrelevantisUtils/bidderUtils.js +++ b/libraries/adrelevantisUtils/bidderUtils.js @@ -1,4 +1,4 @@ -import {isFn, isPlainObject} from '../../src/utils.js'; +import { isFn, isPlainObject } from '../../src/utils.js'; export function hasUserInfo(bid) { return !!(bid.params && bid.params.user); @@ -15,9 +15,9 @@ export function hasAppId(bid) { export function addUserId(eids, id, source, rti) { if (id) { if (rti) { - eids.push({source, id, rti_partner: rti}); + eids.push({ source, id, rti_partner: rti }); } else { - eids.push({source, id}); + eids.push({ source, id }); } } return eids; diff --git a/libraries/adtelligentUtils/adtelligentUtils.js b/libraries/adtelligentUtils/adtelligentUtils.js index 9769102ed69..50911f97b6f 100644 --- a/libraries/adtelligentUtils/adtelligentUtils.js +++ b/libraries/adtelligentUtils/adtelligentUtils.js @@ -1,6 +1,7 @@ -import {deepAccess, isArray} from '../../src/utils.js'; +import { deepAccess, isArray } from '../../src/utils.js'; import { config } from '../../src/config.js'; -import {BANNER, VIDEO} from '../../src/mediaTypes.js'; +import { BANNER, VIDEO } from '../../src/mediaTypes.js'; +import { getPlacementPositionUtils } from "../placementPositionInfo/placementPositionInfo.js"; export const supportedMediaTypes = [VIDEO, BANNER] @@ -50,9 +51,11 @@ export function getUserSyncsFn (syncOptions, serverResponses, syncsCache = {}) { } export function createTag(bidRequests, adapterRequest) { + const placementEnv = getPlacementPositionUtils().getPlacementEnv() const tag = { // TODO: is 'page' the right value here? Domain: deepAccess(adapterRequest, 'refererInfo.page'), + ...placementEnv }; if (config.getConfig('coppa') === true) { diff --git a/libraries/advangUtils/index.js b/libraries/advangUtils/index.js index 7d869cef9e1..cf15c5025b5 100644 --- a/libraries/advangUtils/index.js +++ b/libraries/advangUtils/index.js @@ -1,6 +1,6 @@ import { generateUUID, isFn, parseSizesInput, parseUrl } from '../../src/utils.js'; -import { getDNT as getNavigatorDNT } from '../dnt/index.js'; import { config } from '../../src/config.js'; +import { getDNT } from '../dnt/index.js'; export const DEFAULT_MIMES = ['video/mp4', 'application/javascript']; @@ -46,10 +46,6 @@ export function isConnectedTV() { return (/(smart[-]?tv|hbbtv|appletv|googletv|hdmi|netcast\.tv|viera|nettv|roku|\bdtv\b|sonydtv|inettvbrowser|\btv\b)/i).test(navigator.userAgent); } -export function getDoNotTrack(win = typeof window !== 'undefined' ? window : undefined) { - return getNavigatorDNT(win); -} - export function findAndFillParam(o, key, value) { try { if (typeof value === 'function') { @@ -87,7 +83,7 @@ export function getFirstSize(sizes) { export function parseSizes(sizes) { return parseSizesInput(sizes).map(size => { - const [ width, height ] = size.split('x'); + const [width, height] = size.split('x'); return { w: parseInt(width, 10) || undefined, h: parseInt(height, 10) || undefined @@ -108,7 +104,7 @@ export function getTopWindowReferrer(bidderRequest) { } export function getTopWindowLocation(bidderRequest) { - return parseUrl(bidderRequest?.refererInfo?.page, {decodeSearchAsString: true}); + return parseUrl(bidderRequest?.refererInfo?.page, { decodeSearchAsString: true }); } export function getVideoTargetingParams(bid, VIDEO_TARGETING) { @@ -117,12 +113,12 @@ export function getVideoTargetingParams(bid, VIDEO_TARGETING) { Object.keys(Object(bid.mediaTypes.video)) .filter(key => !excludeProps.includes(key)) .forEach(key => { - result[ key ] = bid.mediaTypes.video[ key ]; + result[key] = bid.mediaTypes.video[key]; }); Object.keys(Object(bid.params.video)) .filter(key => VIDEO_TARGETING.includes(key)) .forEach(key => { - result[ key ] = bid.params.video[ key ]; + result[key] = bid.params.video[key]; }); return result; } @@ -145,7 +141,7 @@ export function createRequestData(bid, bidderRequest, isVideo, getBidParam, getS const o = { 'device': { 'langauge': (global.navigator.language).split('-')[0], - 'dnt': getDoNotTrack(global) ? 1 : 0, + 'dnt': getDNT() ? 1 : 0, 'devicetype': isMobile() ? 4 : isConnectedTV() ? 3 : 2, 'js': 1, 'os': getOsVersion() @@ -170,7 +166,7 @@ export function createRequestData(bid, bidderRequest, isVideo, getBidParam, getS o.site['ref'] = topReferrer; o.site['mobile'] = isMobile() ? 1 : 0; const secure = topLocation.protocol.indexOf('https') === 0 ? 1 : 0; - o.device['dnt'] = getDoNotTrack(global) ? 1 : 0; + o.device['dnt'] = getDNT() ? 1 : 0; findAndFillParam(o.site, 'name', function() { return global.top.document.title; @@ -215,13 +211,13 @@ export function createRequestData(bid, bidderRequest, isVideo, getBidParam, getS } if (coppa) { - o.regs.ext = {'coppa': 1}; + o.regs.ext = { 'coppa': 1 }; } if (bidderRequest && bidderRequest.gdprConsent) { const { gdprApplies, consentString } = bidderRequest.gdprConsent; - o.regs.ext = {'gdpr': gdprApplies ? 1 : 0}; - o.user.ext = {'consent': consentString}; + o.regs.ext = { 'gdpr': gdprApplies ? 1 : 0 }; + o.user.ext = { 'consent': consentString }; } return o; diff --git a/libraries/alliance_gravityUtils/index.ts b/libraries/alliance_gravityUtils/index.ts new file mode 100644 index 00000000000..52a011bbcb4 --- /dev/null +++ b/libraries/alliance_gravityUtils/index.ts @@ -0,0 +1,149 @@ +import { deepAccess, deepSetValue, logInfo } from '../../src/utils.js'; +import { Renderer } from '../../src/Renderer.js'; +import { INSTREAM, OUTSTREAM } from '../../src/video.js'; +import { BANNER, MediaType, NATIVE, VIDEO } from '../../src/mediaTypes.js'; +import { BidResponse, VideoBidResponse } from '../../src/bidfactory.js'; +import { BidRequest, ORTBImp, ORTBResponse } from '../../src/prebid.public.js'; +import { AdapterResponse, ServerResponse } from '../../src/adapters/bidderFactory.js'; + +const OUTSTREAM_RENDERER_URL = 'https://acdn.adnxs.com/video/outstream/ANOutstreamVideo.js'; + +export function getUserSyncs(syncOptions, serverResponses, gdprConsent, uspConsent) { + if (typeof serverResponses === 'object' && + serverResponses != null && + serverResponses.length > 0 && + serverResponses[0].hasOwnProperty('body') && + serverResponses[0].body.hasOwnProperty('ext') && + serverResponses[0].body.ext.hasOwnProperty('cookies') && + typeof serverResponses[0].body.ext.cookies === 'object' && + Array.isArray(serverResponses[0].body.ext.cookies)) { + return serverResponses[0].body.ext.cookies.slice(0, 5); + } else { + return []; + } +}; + +const createOustreamRendererFunction = ( + adUnitCode: string, + width: number, + height: number +) => (bidResponse: VideoBidResponse) => { + bidResponse.renderer.push(() => { + (window as any).ANOutstreamVideo.renderAd({ + sizes: [width, height], + targetId: adUnitCode, + adResponse: bidResponse.vastXml, + rendererOptions: { + showBigPlayButton: false, + showProgressBar: 'bar', + showVolume: false, + allowFullscreen: true, + skippable: false, + content: bidResponse.vastXml + } + }); + }); +}; + +export type CreateRenderPayload = { + requestId: string, + vastXml: string, + adUnitCode: string, + width: number, + height: number +} + +export const createRenderer = ( + { requestId, vastXml, adUnitCode, width, height }: CreateRenderPayload +): Renderer | undefined => { + if (!vastXml) { + logInfo('No VAST in bidResponse'); + return; + } + const installPayload = { + id: requestId, + url: OUTSTREAM_RENDERER_URL, + loaded: false, + adUnitCode: adUnitCode, + targetId: adUnitCode, + }; + const renderer = Renderer.install(installPayload); + renderer.setRender(createOustreamRendererFunction(adUnitCode, width, height)); + return renderer; +}; + +export const enrichImp = (imp:ORTBImp, bidRequest:BidRequest): ORTBImp => { + deepSetValue(imp, 'tagid', bidRequest.adUnitCode); + deepSetValue(imp, 'ext.adUnitCode', bidRequest.adUnitCode); + if (imp.video) { + const playerSize = deepAccess(bidRequest, 'mediaTypes.video.playerSize'); + const videoContext = deepAccess(bidRequest, 'mediaTypes.video.context'); + deepSetValue(imp, 'video.ext.playerSize', playerSize); + deepSetValue(imp, 'video.ext.context', videoContext); + } + return imp; +} + +export function createResponse(bid:any, ortbResponse:any): BidResponse { + let mediaType: MediaType = BANNER; + if ([INSTREAM, OUTSTREAM].includes(bid.ext.mediaType as string)) mediaType = VIDEO; + if (bid.ext.mediaType === NATIVE) mediaType = NATIVE; + const response:any = { + requestId: bid.impid, + cpm: bid.price, + width: bid.w, + height: bid.h, + creativeId: bid.crid, + currency: ortbResponse.cur, + netRevenue: true, + ttl: 120, + mediaType, + meta: { + advertiserDomains: bid.adomain, + demandSource: bid.ext.ssp, + }, + }; + if (bid.dealid) response.dealid = bid.dealid; + + if (bid.ext.mediaType === BANNER) response.ad = bid.adm; + if ([INSTREAM, OUTSTREAM].includes(bid.ext.mediaType as string)) response.vastXml = bid.adm; + if (bid.ext.mediaType === OUTSTREAM && (bid.ext.adUnitCode)) { + const renderer = createRenderer({ + requestId: response.requestId, + vastXml: response.vastXml, + adUnitCode: bid.ext.adUnitCode, + width: response.width, + height: response.height + }); + if (renderer) { + response.renderer = renderer; + response.adUnitCode = bid.ext.adUnitCode; + } else { + logInfo('Could not create renderer for outstream bid'); + } + }; + + if (bid.ext.mediaType === NATIVE) { + try { + response.native = { ortb: JSON.parse(bid.adm) } + } catch (e) {} + } + return response as BidResponse; +} + +export const interpretResponse = (serverResponse: ServerResponse): AdapterResponse => { + if (!serverResponse.body) return []; + const respBody = serverResponse.body as ORTBResponse; + if (!respBody.seatbid || respBody.seatbid.length === 0) return []; + + const responses: BidResponse[] = []; + for (let i = 0; i < respBody.seatbid.length; i++) { + const seatbid = respBody.seatbid[i]; + for (let j = 0; j < seatbid.bid.length; j++) { + const bid = seatbid.bid[j]; + const response:BidResponse = createResponse(bid, respBody); + responses.push(response); + } + } + return responses; +} diff --git a/libraries/analyticsAdapter/AnalyticsAdapter.ts b/libraries/analyticsAdapter/AnalyticsAdapter.ts index fd6cc601442..0cf77d6ed5c 100644 --- a/libraries/analyticsAdapter/AnalyticsAdapter.ts +++ b/libraries/analyticsAdapter/AnalyticsAdapter.ts @@ -1,8 +1,8 @@ import { EVENTS } from '../../src/constants.js'; -import {ajax} from '../../src/ajax.js'; -import {logError, logMessage} from '../../src/utils.js'; +import { ajax } from '../../src/ajax.js'; +import { logError, logMessage } from '../../src/utils.js'; import * as events from '../../src/events.js'; -import {config} from '../../src/config.js'; +import { config } from '../../src/config.js'; export const _internal = { ajax @@ -30,7 +30,7 @@ export function setLabels(internalLabels) { allLabels = combineLabels(); }; -const combineLabels = () => Object.values(labels).reduce((acc, curr) => ({...acc, ...curr}), {}); +const combineLabels = () => Object.values(labels).reduce((acc, curr) => ({ ...acc, ...curr }), {}); export const DEFAULT_INCLUDE_EVENTS = Object.values(EVENTS) .filter(ev => ev !== EVENTS.AUCTION_DEBUG); @@ -146,7 +146,7 @@ export default function AnalyticsAdapter({ u }); function _track(arg) { - const {eventType, args} = arg; + const { eventType, args } = arg; if (this.getAdapterType() === BUNDLE) { (window[global] as any)(handler, eventType, args); } @@ -160,7 +160,7 @@ export default function AnalyticsAdapter({ u _internal.ajax(url, callback, JSON.stringify({ eventType, args, labels: allLabels })); } - function _enqueue({eventType, args}) { + function _enqueue({ eventType, args }) { queue.push(() => { if (Object.keys(allLabels || []).length > 0) { args = { @@ -168,7 +168,7 @@ export default function AnalyticsAdapter({ u ...args, } } - this.track({eventType, labels: allLabels, args}); + this.track({ eventType, labels: allLabels, args }); }); emptyQueue(); } @@ -184,7 +184,7 @@ export default function AnalyticsAdapter({ u if (sampled) { const trackedEvents: Set = (() => { - const {includeEvents = DEFAULT_INCLUDE_EVENTS, excludeEvents = []} = (config || {}); + const { includeEvents = DEFAULT_INCLUDE_EVENTS, excludeEvents = [] } = (config || {}); return new Set( Object.values(EVENTS) .filter(ev => includeEvents.includes(ev)) @@ -206,7 +206,7 @@ export default function AnalyticsAdapter({ u handlers = Object.fromEntries( Array.from(trackedEvents) .map((ev) => { - const handler = (args) => this.enqueue({eventType: ev, args}); + const handler = (args) => this.enqueue({ eventType: ev, args }); events.on(ev, handler); return [ev, handler]; }) diff --git a/libraries/appnexusUtils/anKeywords.js b/libraries/appnexusUtils/anKeywords.js index 8246b1e4f65..63f7df423f6 100644 --- a/libraries/appnexusUtils/anKeywords.js +++ b/libraries/appnexusUtils/anKeywords.js @@ -1,6 +1,6 @@ -import {_each, deepAccess, isArray, isNumber, isStr, mergeDeep, logWarn} from '../../src/utils.js'; -import {getAllOrtbKeywords} from '../keywords/keywords.js'; -import {CLIENT_SECTIONS} from '../../src/fpd/oneClient.js'; +import { _each, deepAccess, isArray, isNumber, isStr, mergeDeep, logWarn } from '../../src/utils.js'; +import { getAllOrtbKeywords } from '../keywords/keywords.js'; +import { CLIENT_SECTIONS } from '../../src/fpd/oneClient.js'; const ORTB_SEGTAX_KEY_MAP = { 526: '1plusX', @@ -56,7 +56,7 @@ export function transformBidderParamKeywords(keywords, paramName = 'keywords') { } // unsuported types - don't send a key } v = v.filter(kw => kw !== '') - const entry = {key: k} + const entry = { key: k } if (v.length > 0) { entry.value = v; } diff --git a/libraries/appnexusUtils/anUtils.js b/libraries/appnexusUtils/anUtils.js index 89cbaa95040..191512f2fea 100644 --- a/libraries/appnexusUtils/anUtils.js +++ b/libraries/appnexusUtils/anUtils.js @@ -2,7 +2,7 @@ * Converts a string value in camel-case to underscore eg 'placementId' becomes 'placement_id' * @param {string} value string value to convert */ -import {deepClone, isPlainObject} from '../../src/utils.js'; +import { deepClone, isPlainObject } from '../../src/utils.js'; export function convertCamelToUnderscore(value) { return value.replace(/(?:^|\.?)([A-Z])/g, function (x, y) { @@ -12,7 +12,6 @@ export function convertCamelToUnderscore(value) { export const appnexusAliases = [ { code: 'appnexusAst', gvlid: 32 }, - { code: 'emetriq', gvlid: 213 }, { code: 'pagescience', gvlid: 32 }, { code: 'gourmetads', gvlid: 32 }, { code: 'newdream', gvlid: 32 }, diff --git a/libraries/audUtils/bidderUtils.js b/libraries/audUtils/bidderUtils.js index 18e8e669a71..660f871ab8a 100644 --- a/libraries/audUtils/bidderUtils.js +++ b/libraries/audUtils/bidderUtils.js @@ -160,7 +160,7 @@ const getSiteDetails = (bidderRequest) => { page = bidderRequest.refererInfo.page; name = bidderRequest.refererInfo.domain; } - return {page: page, name: name}; + return { page: page, name: name }; } // Function to build the user object const getUserDetails = (bidReq) => { diff --git a/libraries/bidViewabilityPixels/index.js b/libraries/bidViewabilityPixels/index.js new file mode 100644 index 00000000000..63417940d53 --- /dev/null +++ b/libraries/bidViewabilityPixels/index.js @@ -0,0 +1,60 @@ +import { EVENT_TYPE_VIEWABLE, parseEventTrackers, TRACKER_METHOD_IMG, TRACKER_METHOD_JS } from '../../src/eventTrackers.js'; +import { filterEventTrackers, legacyPropertiesToOrtbNative } from '../../src/native.js'; +import { triggerPixel, insertHtmlIntoIframe } from '../../src/utils.js'; +import * as events from '../../src/events.js'; +import { EVENTS } from '../../src/constants.js'; +import adapterManager from '../../src/adapterManager.js'; + +/** + * Collects viewable tracking URLs from bid.eventtrackers for EVENT_TYPE_VIEWABLE (IMG and JS methods). + * @param {Object} bid - bid object that may have eventtrackers array + * @returns {{ img: string[], js: string[] }} img and js URLs to fire + */ +export function getViewabilityTrackersFromBid(bid) { + const eventTrackers = bid?.eventtrackers; + if (!eventTrackers || !Array.isArray(eventTrackers)) return { img: [], js: [] }; + const parsed = parseEventTrackers(eventTrackers); + const viewableTrackers = parsed[EVENT_TYPE_VIEWABLE]; + if (!viewableTrackers || typeof viewableTrackers !== 'object') return { img: [], js: [] }; + const img = viewableTrackers[TRACKER_METHOD_IMG]; + const js = viewableTrackers[TRACKER_METHOD_JS]; + return { + img: Array.isArray(img) ? img : [], + js: Array.isArray(js) ? js : [] + }; +} + +/** + * Fires viewability trackers for a bid. IMG URLs via triggerPixel, JS URLs via insertHtmlIntoIframe (script tag). + * Uses EVENT_TYPE_VIEWABLE trackers only (both TRACKER_METHOD_IMG and TRACKER_METHOD_JS). + * @param {Object} bid - bid with eventtrackers + */ +export function fireViewabilityPixels(bid) { + let { img, js } = getViewabilityTrackersFromBid(bid); + + const nativeResponse = bid.native && (bid?.native?.ortb || legacyPropertiesToOrtbNative(bid.native)); + if (nativeResponse && nativeResponse.eventtrackers) { + const filteredEventTrackers = filterEventTrackers(nativeResponse, bid); + const { [TRACKER_METHOD_IMG]: nativeImg = [], [TRACKER_METHOD_JS]: nativeJs = [] } = parseEventTrackers( + filteredEventTrackers || [] + )[EVENT_TYPE_VIEWABLE] || {}; + img = img.concat(Array.isArray(nativeImg) ? nativeImg : []); + js = js.concat(Array.isArray(nativeJs) ? nativeJs : []); + } + + img.forEach(triggerPixel); + if (js.length > 0) { + const markup = js.map(url => ``).join('\n'); + insertHtmlIntoIframe(markup); + } +} + +export function triggerBidViewable(bid) { + fireViewabilityPixels(bid); + // trigger respective bidder's onBidViewable handler + adapterManager.callBidViewableBidder(bid.adapterCode || bid.bidder, bid); + if (bid.deferBilling) { + adapterManager.triggerBilling(bid); + } + events.emit(EVENTS.BID_VIEWABLE, bid); +} diff --git a/libraries/braveUtils/buildAndInterpret.js b/libraries/braveUtils/buildAndInterpret.js index 7974b1f079e..bfcb5d6c2dd 100644 --- a/libraries/braveUtils/buildAndInterpret.js +++ b/libraries/braveUtils/buildAndInterpret.js @@ -1,5 +1,5 @@ import { isEmpty } from '../../src/utils.js'; -import {config} from '../../src/config.js'; +import { config } from '../../src/config.js'; import { createNativeRequest, createBannerRequest, createVideoRequest, getFloor, prepareSite, prepareConsents, prepareEids } from './index.js'; import { convertOrtbRequestToProprietaryNative } from '../../src/native.js'; diff --git a/libraries/braveUtils/index.js b/libraries/braveUtils/index.js index fe9d68107cb..34742f41ed8 100644 --- a/libraries/braveUtils/index.js +++ b/libraries/braveUtils/index.js @@ -66,7 +66,7 @@ export function createBannerRequest(br) { * @returns {object} The video request object */ export function createVideoRequest(br) { - const videoObj = {...br.mediaTypes.video, id: br.transactionId}; + const videoObj = { ...br.mediaTypes.video, id: br.transactionId }; if (videoObj.playerSize) { const size = Array.isArray(videoObj.playerSize[0]) ? videoObj.playerSize[0] : videoObj.playerSize; diff --git a/libraries/categoryTranslationMapping/index.js b/libraries/categoryTranslationMapping/index.js deleted file mode 100644 index 13b10423450..00000000000 --- a/libraries/categoryTranslationMapping/index.js +++ /dev/null @@ -1,100 +0,0 @@ -/** - * Provides mapping objects used by bidders for categoryTranslation type logic for Adpod feature - */ -export const APPNEXUS_CATEGORY_MAPPING = { - '1': 'IAB20-3', - '2': 'IAB18-5', - '3': 'IAB10-1', - '4': 'IAB2-3', - '5': 'IAB19-8', - '6': 'IAB22-1', - '7': 'IAB18-1', - '8': 'IAB12-3', - '9': 'IAB5-1', - '10': 'IAB4-5', - '11': 'IAB13-4', - '12': 'IAB8-7', - '13': 'IAB9-7', - '14': 'IAB7-1', - '15': 'IAB20-18', - '16': 'IAB10-7', - '17': 'IAB19-18', - '18': 'IAB13-6', - '19': 'IAB18-4', - '20': 'IAB1-5', - '21': 'IAB1-6', - '22': 'IAB3-4', - '23': 'IAB19-13', - '24': 'IAB22-2', - '25': 'IAB3-9', - '26': 'IAB17-18', - '27': 'IAB19-6', - '28': 'IAB1-7', - '29': 'IAB9-30', - '30': 'IAB20-7', - '31': 'IAB20-17', - '32': 'IAB7-32', - '33': 'IAB16-5', - '34': 'IAB19-34', - '35': 'IAB11-5', - '36': 'IAB12-3', - '37': 'IAB11-4', - '38': 'IAB12-3', - '39': 'IAB9-30', - '41': 'IAB7-44', - '42': 'IAB7-1', - '43': 'IAB7-30', - '50': 'IAB19-30', - '51': 'IAB17-12', - '52': 'IAB19-30', - '53': 'IAB3-1', - '55': 'IAB13-2', - '56': 'IAB19-30', - '57': 'IAB19-30', - '58': 'IAB7-39', - '59': 'IAB22-1', - '60': 'IAB7-39', - '61': 'IAB21-3', - '62': 'IAB5-1', - '63': 'IAB12-3', - '64': 'IAB20-18', - '65': 'IAB11-2', - '66': 'IAB17-18', - '67': 'IAB9-9', - '68': 'IAB9-5', - '69': 'IAB7-44', - '71': 'IAB22-3', - '73': 'IAB19-30', - '74': 'IAB8-5', - '78': 'IAB22-1', - '85': 'IAB12-2', - '86': 'IAB22-3', - '87': 'IAB11-3', - '112': 'IAB7-32', - '113': 'IAB7-32', - '114': 'IAB7-32', - '115': 'IAB7-32', - '118': 'IAB9-5', - '119': 'IAB9-5', - '120': 'IAB9-5', - '121': 'IAB9-5', - '122': 'IAB9-5', - '123': 'IAB9-5', - '124': 'IAB9-5', - '125': 'IAB9-5', - '126': 'IAB9-5', - '127': 'IAB22-1', - '132': 'IAB1-2', - '133': 'IAB19-30', - '137': 'IAB3-9', - '138': 'IAB19-3', - '140': 'IAB2-3', - '141': 'IAB2-1', - '142': 'IAB2-3', - '143': 'IAB17-13', - '166': 'IAB11-4', - '175': 'IAB3-1', - '176': 'IAB13-4', - '182': 'IAB8-9', - '183': 'IAB3-5' -}; diff --git a/libraries/cmp/cmpClient.js b/libraries/cmp/cmpClient.js index 9e7e225ddb4..1ad691b824c 100644 --- a/libraries/cmp/cmpClient.js +++ b/libraries/cmp/cmpClient.js @@ -1,4 +1,4 @@ -import {PbPromise} from '../../src/utils/promise.js'; +import { PbPromise } from '../../src/utils/promise.js'; /** * @typedef {function} CMPClient @@ -109,7 +109,7 @@ export function cmpClient( } function resolveParams(params) { - params = Object.assign({version: apiVersion}, params); + params = Object.assign({ version: apiVersion }, params); return apiArgs.map(arg => [arg, params[arg]]) } diff --git a/libraries/consentManagement/cmUtils.ts b/libraries/consentManagement/cmUtils.ts index d8012241fc7..44553ed5f06 100644 --- a/libraries/consentManagement/cmUtils.ts +++ b/libraries/consentManagement/cmUtils.ts @@ -1,14 +1,14 @@ -import {timedAuctionHook} from '../../src/utils/perfMetrics.js'; -import {isNumber, isPlainObject, isStr, logError, logInfo, logWarn} from '../../src/utils.js'; -import {ConsentHandler} from '../../src/consentHandler.js'; -import {PbPromise} from '../../src/utils/promise.js'; -import {buildActivityParams} from '../../src/activities/params.js'; -import {getHook} from '../../src/hook.js'; +import { timedAuctionHook } from '../../src/utils/perfMetrics.js'; +import { isNumber, isPlainObject, isStr, logError, logInfo, logWarn } from '../../src/utils.js'; +import { ConsentHandler } from '../../src/consentHandler.js'; +import { PbPromise } from '../../src/utils/promise.js'; +import { buildActivityParams } from '../../src/activities/params.js'; +import { getHook } from '../../src/hook.js'; export function consentManagementHook(name, loadConsentData) { const SEEN = new WeakSet(); return timedAuctionHook(name, function requestBidsHook(fn, reqBidsConfigObj) { - return loadConsentData().then(({consentData, error}) => { + return loadConsentData().then(({ consentData, error }) => { if (error && (!consentData || !SEEN.has(error))) { SEEN.add(error); logWarn(error.message, ...(error.args || [])); @@ -83,14 +83,14 @@ export function lookupConsentData( const consentData = consentDataHandler.getConsentData() ?? (cmpLoaded ? provisionalConsent : getNullConsent()); const message = `timeout waiting for ${cmpLoaded ? 'user action on CMP' : 'CMP to load'}`; consentDataHandler.setConsentData(consentData); - resolve({consentData, error: new Error(`${name} ${message}`)}); + resolve({ consentData, error: new Error(`${name} ${message}`) }); }, timeout); } else { timeoutHandle = null; } } setupCmp(setProvisionalConsent) - .then(() => resolve({consentData: consentDataHandler.getConsentData()}), reject); + .then(() => resolve({ consentData: consentDataHandler.getConsentData() }), reject); cmpTimeout != null && resetTimeout(cmpTimeout); }).finally(() => { timeoutHandle && clearTimeout(timeoutHandle); @@ -153,11 +153,11 @@ export function configParser( let requestBidsHook, cdLoader, staticConsentData; function attachActivityParams(next, params) { - return next(Object.assign({[`${namespace}Consent`]: consentDataHandler.getConsentData()}, params)); + return next(Object.assign({ [`${namespace}Consent`]: consentDataHandler.getConsentData() }, params)); } function loadConsentData() { - return cdLoader().then(({error}) => ({error, consentData: consentDataHandler.getConsentData()})) + return cdLoader().then(({ error }) => ({ error, consentData: consentDataHandler.getConsentData() })) } function activate() { @@ -171,8 +171,8 @@ export function configParser( function reset() { if (requestBidsHook != null) { - getHook('requestBids').getHooks({hook: requestBidsHook}).remove(); - buildActivityParams.getHooks({hook: attachActivityParams}).remove(); + getHook('requestBids').getHooks({ hook: requestBidsHook }).remove(); + buildActivityParams.getHooks({ hook: attachActivityParams }).remove(); requestBidsHook = null; logInfo(`${displayName} consentManagement module has been deactivated...`); } diff --git a/libraries/cookieSync/cookieSync.js b/libraries/cookieSync/cookieSync.js index c51d61e240b..13e672005eb 100644 --- a/libraries/cookieSync/cookieSync.js +++ b/libraries/cookieSync/cookieSync.js @@ -2,7 +2,7 @@ import { getStorageManager } from '../../src/storageManager.js'; const COOKIE_KEY_MGUID = '__mguid_'; export function cookieSync(syncOptions, gdprConsent, uspConsent, bidderCode, cookieOrigin, ckIframeUrl, cookieTime) { - const storage = getStorageManager({bidderCode: bidderCode}); + const storage = getStorageManager({ bidderCode: bidderCode }); const origin = encodeURIComponent(location.origin || `https://${location.host}`); let syncParamUrl = `dm=${origin}`; diff --git a/libraries/currencyUtils/currency.js b/libraries/currencyUtils/currency.js index 924f8f200d8..c5a23d34e36 100644 --- a/libraries/currencyUtils/currency.js +++ b/libraries/currencyUtils/currency.js @@ -1,5 +1,5 @@ -import {getGlobal} from '../../src/prebidGlobal.js'; -import {keyCompare} from '../../src/utils/reducers.js'; +import { getGlobal } from '../../src/prebidGlobal.js'; +import { keyCompare } from '../../src/utils/reducers.js'; /** * Attempt to convert `amount` from the currency `fromCur` to the currency `toCur`. diff --git a/libraries/devicePixelRatio/devicePixelRatio.js b/libraries/devicePixelRatio/devicePixelRatio.js index 8bfe23bcb3d..f6e9d85cac7 100644 --- a/libraries/devicePixelRatio/devicePixelRatio.js +++ b/libraries/devicePixelRatio/devicePixelRatio.js @@ -1,14 +1,10 @@ -import {canAccessWindowTop, internal as utilsInternals} from '../../src/utils.js'; - -function getFallbackWindow(win) { - if (win) { - return win; - } - - return canAccessWindowTop() ? utilsInternals.getWindowTop() : utilsInternals.getWindowSelf(); -} +import { isFingerprintingApiDisabled } from '../fingerprinting/fingerprinting.js'; +import { getFallbackWindow } from '../../src/utils.js'; export function getDevicePixelRatio(win) { + if (isFingerprintingApiDisabled('devicepixelratio')) { + return 1; + } try { return getFallbackWindow(win).devicePixelRatio; } catch (e) { diff --git a/libraries/dfpUtils/dfpUtils.js b/libraries/dfpUtils/dfpUtils.js index 4b957eb4999..bad6c05b356 100644 --- a/libraries/dfpUtils/dfpUtils.js +++ b/libraries/dfpUtils/dfpUtils.js @@ -1,4 +1,4 @@ -import {gdprDataHandler} from '../../src/consentHandler.js'; +import { gdprDataHandler } from '../../src/consentHandler.js'; /** Safe defaults which work on pretty much all video calls. */ export const DEFAULT_DFP_PARAMS = { diff --git a/libraries/dnt/index.js b/libraries/dnt/index.js index 1b03e848ca5..66aad467115 100644 --- a/libraries/dnt/index.js +++ b/libraries/dnt/index.js @@ -1,11 +1,7 @@ -function _getDNT(win) { - return win.navigator.doNotTrack === '1' || win.doNotTrack === '1' || win.navigator.msDoNotTrack === '1' || win.navigator.doNotTrack?.toLowerCase?.() === 'yes'; -} - -export function getDNT(win = window) { - try { - return _getDNT(win) || (win !== win.top && _getDNT(win.top)); - } catch (e) { - return false; - } +/** + * DNT was deprecated by W3C; Prebid no longer supports DNT signals. + * Keep this helper for backwards compatibility with adapters that still invoke getDNT(). + */ +export function getDNT() { + return false; } diff --git a/libraries/dspxUtils/bidderUtils.js b/libraries/dspxUtils/bidderUtils.js index 29e44313a62..b238d9dc2eb 100644 --- a/libraries/dspxUtils/bidderUtils.js +++ b/libraries/dspxUtils/bidderUtils.js @@ -1,5 +1,5 @@ import { BANNER, VIDEO } from '../../src/mediaTypes.js'; -import {deepAccess, isArray, isEmptyStr, isFn} from '../../src/utils.js'; +import { deepAccess, isArray, isEmptyStr, isFn } from '../../src/utils.js'; /** * @typedef {import('../src/adapters/bidderFactory.js').BidderRequest} BidderRequest * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest diff --git a/libraries/dxUtils/common.js b/libraries/dxUtils/common.js index 3e5e43de0d6..ac0ce6e4433 100644 --- a/libraries/dxUtils/common.js +++ b/libraries/dxUtils/common.js @@ -6,9 +6,9 @@ import { deepSetValue, mergeDeep } from '../../src/utils.js'; -import {BANNER, VIDEO} from '../../src/mediaTypes.js'; +import { BANNER, VIDEO } from '../../src/mediaTypes.js'; import { Renderer } from '../../src/Renderer.js'; -import {ortbConverter} from '../ortbConverter/converter.js'; +import { ortbConverter } from '../ortbConverter/converter.js'; /** * @typedef {import('../../src/adapters/bidderFactory.js').BidRequest} BidRequest @@ -67,7 +67,7 @@ export function createDxConverter(config) { }, bidResponse(buildBidResponse, bid, context) { let resMediaType; - const {bidRequest} = context; + const { bidRequest } = context; if (bid.adm && bid.adm.trim().startsWith(' String(item).toLowerCase() === apiName.toLowerCase()); +} diff --git a/libraries/gamUtils/gamUtils.js b/libraries/gamUtils/gamUtils.js index f1c4f1c6554..3c2590271e0 100644 --- a/libraries/gamUtils/gamUtils.js +++ b/libraries/gamUtils/gamUtils.js @@ -1 +1 @@ -export {DEFAULT_DFP_PARAMS as DEFAULT_GAM_PARAMS, DFP_ENDPOINT as GAM_ENDPOINT, gdprParams} from '../dfpUtils/dfpUtils.js'; +export { DEFAULT_DFP_PARAMS as DEFAULT_GAM_PARAMS, DFP_ENDPOINT as GAM_ENDPOINT, gdprParams } from '../dfpUtils/dfpUtils.js'; diff --git a/libraries/gptUtils/gptUtils.js b/libraries/gptUtils/gptUtils.js index 17ca64483ab..b98b890d35a 100644 --- a/libraries/gptUtils/gptUtils.js +++ b/libraries/gptUtils/gptUtils.js @@ -1,5 +1,5 @@ import { CLIENT_SECTIONS } from '../../src/fpd/oneClient.js'; -import {compareCodeAndSlot, deepAccess, isGptPubadsDefined, uniques, isEmpty} from '../../src/utils.js'; +import { deepAccess, isGptPubadsDefined, uniques, isEmpty, isAdUnitCodeMatchingSlot } from '../../src/utils.js'; const slotInfoCache = new Map(); @@ -13,7 +13,10 @@ export function clearSlotInfoCache() { * @return {function} filter function */ export function isSlotMatchingAdUnitCode(adUnitCode) { - return (slot) => compareCodeAndSlot(slot, adUnitCode); + return (slot) => { + const match = isAdUnitCodeMatchingSlot(slot); + return match(adUnitCode); + } } /** @@ -21,7 +24,7 @@ export function isSlotMatchingAdUnitCode(adUnitCode) { */ export function setKeyValue(key, value) { if (!key || typeof key !== 'string') return false; - window.googletag = window.googletag || {cmd: []}; + window.googletag = window.googletag || { cmd: [] }; window.googletag.cmd = window.googletag.cmd || []; window.googletag.cmd.push(() => { window.googletag.pubads().setTargeting(key, value); @@ -35,7 +38,10 @@ export function getGptSlotForAdUnitCode(adUnitCode) { let matchingSlot; if (isGptPubadsDefined()) { // find the first matching gpt slot on the page - matchingSlot = window.googletag.pubads().getSlots().find(isSlotMatchingAdUnitCode(adUnitCode)); + matchingSlot = window.googletag.pubads().getSlots().find(slot => { + const match = isAdUnitCodeMatchingSlot(slot); + return match(adUnitCode); + }); } return matchingSlot; } @@ -65,7 +71,7 @@ export function getSignals(fpd) { const signals = Object.entries({ [taxonomies[0]]: getSegments(fpd, ['user.data'], 4), [taxonomies[1]]: getSegments(fpd, CLIENT_SECTIONS.map(section => `${section}.content.data`), 6) - }).map(([taxonomy, values]) => values.length ? {taxonomy, values} : null) + }).map(([taxonomy, values]) => values.length ? { taxonomy, values } : null) .filter(ob => ob); return signals; diff --git a/libraries/greedy/greedyPromise.js b/libraries/greedy/greedyPromise.js index 74b105297dc..5d7713424db 100644 --- a/libraries/greedy/greedyPromise.js +++ b/libraries/greedy/greedyPromise.js @@ -101,7 +101,7 @@ export class GreedyPromise { return new this((resolve) => { const res = []; this.#collect(promises, (success, val, i) => { - res[i] = success ? {status: 'fulfilled', value: val} : {status: 'rejected', reason: val}; + res[i] = success ? { status: 'fulfilled', value: val } : { status: 'rejected', reason: val }; }, () => resolve(res)) }) } diff --git a/libraries/hybridVoxUtils/index.js b/libraries/hybridVoxUtils/index.js index f9f5c21b1cb..5f4de42e43c 100644 --- a/libraries/hybridVoxUtils/index.js +++ b/libraries/hybridVoxUtils/index.js @@ -1,6 +1,6 @@ // Utility functions extracted by codex bot -import {Renderer} from '../../src/Renderer.js'; -import {logWarn, deepAccess, isArray} from '../../src/utils.js'; +import { Renderer } from '../../src/Renderer.js'; +import { logWarn, deepAccess, isArray } from '../../src/utils.js'; export const outstreamRender = bid => { bid.renderer.push(() => { diff --git a/libraries/intentIqConstants/intentIqConstants.js b/libraries/intentIqConstants/intentIqConstants.js index 0fa479a19ad..432ea6ff6a4 100644 --- a/libraries/intentIqConstants/intentIqConstants.js +++ b/libraries/intentIqConstants/intentIqConstants.js @@ -9,9 +9,10 @@ export const BLACK_LIST = "L"; export const CLIENT_HINTS_KEY = "_iiq_ch"; export const EMPTY = "EMPTY"; export const GVLID = "1323"; -export const VERSION = 0.33; +export const VERSION = 0.35; export const PREBID = "pbjs"; export const HOURS_24 = 86400000; +export const HOURS_72 = HOURS_24 * 3; export const INVALID_ID = "INVALID_ID"; diff --git a/libraries/intentIqUtils/gamPredictionReport.js b/libraries/intentIqUtils/gamPredictionReport.js index 2191ade6d35..69a81a8a5e6 100644 --- a/libraries/intentIqUtils/gamPredictionReport.js +++ b/libraries/intentIqUtils/gamPredictionReport.js @@ -1,17 +1,35 @@ import { getEvents } from '../../src/events.js'; -import { logError } from '../../src/utils.js'; +import { isPlainObject, logError } from '../../src/utils.js'; export function gamPredictionReport (gamObjectReference, sendData) { try { - if (!gamObjectReference || !sendData) logError('Failed to get gamPredictionReport, required data is missed'); + if (!gamObjectReference || !sendData) { + logError('Failed to get gamPredictionReport, required data is missed'); + return + } const getSlotTargeting = (slot) => { const kvs = {}; try { - (slot.getTargetingKeys() || []).forEach((k) => { - kvs[k] = slot.getTargeting(k); - }); + if (typeof slot.getConfig === 'function') { + const current = slot.getConfig('targeting'); + const targeting = isPlainObject(current?.targeting) + ? current.targeting + : (isPlainObject(current) ? current : {}); + for (const k in targeting) { + const v = targeting[k]; + if (v == null) continue; + kvs[k] = Array.isArray(v) ? v : [typeof v === 'string' ? v : String(v)]; + } + return kvs; + } + // Fallback in case an older version of Google Publisher Tag is used. + if (typeof slot.getTargetingKeys === 'function' && typeof slot.getTargeting === 'function') { + (slot.getTargetingKeys() || []).forEach((k) => { + kvs[k] = slot.getTargeting(k); + }); + } } catch (e) { - logError('Failed to get targeting keys: ' + e); + logError('Failed to get slot targeting: ' + e); } return kvs; }; diff --git a/libraries/intentIqUtils/getRefferer.js b/libraries/intentIqUtils/getRefferer.js index 20c6a6a5b47..2ed8a668a56 100644 --- a/libraries/intentIqUtils/getRefferer.js +++ b/libraries/intentIqUtils/getRefferer.js @@ -4,19 +4,25 @@ import { getWindowTop, logError, getWindowLocation, getWindowSelf } from '../../ * Determines if the script is running inside an iframe and retrieves the URL. * @return {string} The encoded vrref value representing the relevant URL. */ -export function getReferrer() { + +export function getCurrentUrl() { + let url = ''; try { - const url = getWindowSelf() === getWindowTop() - ? getWindowLocation().href - : getWindowTop().location.href; + if (getWindowSelf() === getWindowTop()) { + // top page + url = getWindowLocation().href || ''; + } else { + // iframe + url = getWindowTop().location.href || ''; + } if (url.length >= 50) { - const { origin } = new URL(url); - return origin; - } + return new URL(url).origin; + }; return url; } catch (error) { + // Handling access errors, such as cross-domain restrictions logError(`Error accessing location: ${error}`); return ''; } @@ -31,12 +37,12 @@ export function getReferrer() { * @return {string} The modified URL with appended `vrref` or `fui` parameters. */ export function appendVrrefAndFui(url, domainName) { - const fullUrl = encodeURIComponent(getReferrer()); + const fullUrl = getCurrentUrl(); if (fullUrl) { return (url += '&vrref=' + getRelevantRefferer(domainName, fullUrl)); } url += '&fui=1'; // Full Url Issue - url += '&vrref=' + encodeURIComponent(domainName || ''); + if (domainName) url += '&vrref=' + encodeURIComponent(domainName); return url; } @@ -47,10 +53,9 @@ export function appendVrrefAndFui(url, domainName) { * @return {string} The relevant referrer */ export function getRelevantRefferer(domainName, fullUrl) { - if (domainName && isDomainIncluded(fullUrl, domainName)) { - return fullUrl; - } - return domainName ? encodeURIComponent(domainName) : fullUrl; + return encodeURIComponent( + domainName && isDomainIncluded(fullUrl, domainName) ? fullUrl : (domainName || fullUrl) + ); } /** @@ -61,7 +66,7 @@ export function getRelevantRefferer(domainName, fullUrl) { */ export function isDomainIncluded(fullUrl, domainName) { try { - return fullUrl.includes(domainName); + return new URL(fullUrl).hostname === domainName; } catch (error) { logError(`Invalid URL provided: ${error}`); return false; diff --git a/libraries/intentIqUtils/getUnitPosition.js b/libraries/intentIqUtils/getUnitPosition.js new file mode 100644 index 00000000000..025a06a9964 --- /dev/null +++ b/libraries/intentIqUtils/getUnitPosition.js @@ -0,0 +1,17 @@ +export function getUnitPosition(pbjs, adUnitCode) { + const adUnits = pbjs?.adUnits; + if (!Array.isArray(adUnits) || !adUnitCode) return; + + for (let i = 0; i < adUnits.length; i++) { + const adUnit = adUnits[i]; + if (adUnit?.code !== adUnitCode) continue; + + const mediaTypes = adUnit?.mediaTypes; + if (!mediaTypes || typeof mediaTypes !== 'object') return; + + const firstKey = Object.keys(mediaTypes)[0]; + const pos = mediaTypes[firstKey]?.pos; + + return typeof pos === 'number' ? pos : undefined; + } +} diff --git a/libraries/intentIqUtils/intentIqConfig.js b/libraries/intentIqUtils/intentIqConfig.js index 3f2572f14fa..41c731f646b 100644 --- a/libraries/intentIqUtils/intentIqConfig.js +++ b/libraries/intentIqUtils/intentIqConfig.js @@ -1,3 +1,33 @@ -export const iiqServerAddress = (configParams, gdprDetected) => typeof configParams?.iiqServerAddress === 'string' ? configParams.iiqServerAddress : gdprDetected ? 'https://api-gdpr.intentiq.com' : 'https://api.intentiq.com' -export const iiqPixelServerAddress = (configParams, gdprDetected) => typeof configParams?.iiqPixelServerAddress === 'string' ? configParams.iiqPixelServerAddress : gdprDetected ? 'https://sync-gdpr.intentiq.com' : 'https://sync.intentiq.com' -export const reportingServerAddress = (reportEndpoint, gdprDetected) => reportEndpoint && typeof reportEndpoint === 'string' ? reportEndpoint : gdprDetected ? 'https://reports-gdpr.intentiq.com/report' : 'https://reports.intentiq.com/report' +const REGION_MAPPING = { + gdpr: true, + apac: true, + emea: true +}; + +function checkRegion(region) { + if (typeof region !== 'string') return ''; + const lower = region.toLowerCase(); + return REGION_MAPPING[lower] ? lower : ''; +} + +function buildServerAddress(baseName, region) { + const checkedRegion = checkRegion(region); + if (checkedRegion) return `https://${baseName}-${checkedRegion}.intentiq.com`; + return `https://${baseName}.intentiq.com`; +} + +export const getIiqServerAddress = (configParams = {}) => { + if (typeof configParams?.iiqServerAddress === 'string') return configParams.iiqServerAddress; + return buildServerAddress('api', configParams.region); +}; + +export const iiqPixelServerAddress = (configParams = {}) => { + if (typeof configParams?.iiqPixelServerAddress === 'string') return configParams.iiqPixelServerAddress; + return buildServerAddress('sync', configParams.region); +}; + +export const reportingServerAddress = (reportEndpoint, region) => { + if (reportEndpoint && typeof reportEndpoint === 'string') return reportEndpoint; + const host = buildServerAddress('reports', region); + return `${host}/report`; +}; diff --git a/libraries/intentIqUtils/storageUtils.js b/libraries/intentIqUtils/storageUtils.js index 338333ef3d1..a15e09bbac2 100644 --- a/libraries/intentIqUtils/storageUtils.js +++ b/libraries/intentIqUtils/storageUtils.js @@ -1,12 +1,12 @@ -import {logError, logInfo} from '../../src/utils.js'; -import {SUPPORTED_TYPES, FIRST_PARTY_KEY} from '../../libraries/intentIqConstants/intentIqConstants.js'; -import {getStorageManager} from '../../src/storageManager.js'; -import {MODULE_TYPE_UID} from '../../src/activities/modules.js'; +import { logError, logInfo } from '../../src/utils.js'; +import { SUPPORTED_TYPES, FIRST_PARTY_KEY } from '../../libraries/intentIqConstants/intentIqConstants.js'; +import { getStorageManager } from '../../src/storageManager.js'; +import { MODULE_TYPE_UID } from '../../src/activities/modules.js'; const MODULE_NAME = 'intentIqId'; const PCID_EXPIRY = 365; -export const storage = getStorageManager({moduleType: MODULE_TYPE_UID, moduleName: MODULE_NAME}); +export const storage = getStorageManager({ moduleType: MODULE_TYPE_UID, moduleName: MODULE_NAME }); /** * Read data from local storage or cookie based on allowed storage types. diff --git a/libraries/intentIqUtils/urlUtils.js b/libraries/intentIqUtils/urlUtils.js index 4cfb8273eab..78579aeb67d 100644 --- a/libraries/intentIqUtils/urlUtils.js +++ b/libraries/intentIqUtils/urlUtils.js @@ -1,5 +1,7 @@ -export function appendSPData (url, firstPartyData) { - const spdParam = firstPartyData?.spd ? encodeURIComponent(typeof firstPartyData.spd === 'object' ? JSON.stringify(firstPartyData.spd) : firstPartyData.spd) : ''; - url += spdParam ? '&spd=' + spdParam : ''; - return url +export function appendSPData (url, partnerData) { + const spdParam = partnerData?.spd ? encodeURIComponent(typeof partnerData.spd === 'object' ? JSON.stringify(partnerData.spd) : partnerData.spd) : ''; + if (!spdParam) { + return url; + } + return `${url}&spd=${spdParam}`; }; diff --git a/libraries/interpretResponseUtils/index.js b/libraries/interpretResponseUtils/index.js index 6d081e4c272..6021d2fdbe5 100644 --- a/libraries/interpretResponseUtils/index.js +++ b/libraries/interpretResponseUtils/index.js @@ -1,6 +1,6 @@ -import {logError} from '../../src/utils.js'; +import { logError } from '../../src/utils.js'; -export function interpretResponseUtil(serverResponse, {bidderRequest}, eachBidCallback) { +export function interpretResponseUtil(serverResponse, { bidderRequest }, eachBidCallback) { const bids = []; if (!serverResponse.body || serverResponse.body.error) { let errorMessage = `in response for ${bidderRequest.bidderCode} adapter`; diff --git a/libraries/keywords/keywords.js b/libraries/keywords/keywords.js index b317bcf0c6b..04912a84b5b 100644 --- a/libraries/keywords/keywords.js +++ b/libraries/keywords/keywords.js @@ -1,5 +1,5 @@ -import {CLIENT_SECTIONS} from '../../src/fpd/oneClient.js'; -import {deepAccess} from '../../src/utils.js'; +import { CLIENT_SECTIONS } from '../../src/fpd/oneClient.js'; +import { deepAccess } from '../../src/utils.js'; const ORTB_KEYWORDS_PATHS = ['user.keywords'].concat( CLIENT_SECTIONS.flatMap((prefix) => ['keywords', 'content.keywords'].map(suffix => `${prefix}.${suffix}`)) diff --git a/libraries/liveIntentId/idSystem.js b/libraries/liveIntentId/idSystem.js index 0ac38feee79..0c8c1739207 100644 --- a/libraries/liveIntentId/idSystem.js +++ b/libraries/liveIntentId/idSystem.js @@ -21,7 +21,7 @@ import { DEFAULT_AJAX_TIMEOUT, MODULE_NAME, composeResult, eids, GVLID, DEFAULT_ const EVENTS_TOPIC = 'pre_lips'; -export const storage = getStorageManager({moduleType: MODULE_TYPE_UID, moduleName: MODULE_NAME}); +export const storage = getStorageManager({ moduleType: MODULE_TYPE_UID, moduleName: MODULE_NAME }); const calls = { ajaxGet: (url, onSuccess, onError, timeout, headers) => { ajaxBuilder(timeout)( diff --git a/libraries/liveIntentId/shared.js b/libraries/liveIntentId/shared.js index 77ef0f53736..56ce8c3c8cb 100644 --- a/libraries/liveIntentId/shared.js +++ b/libraries/liveIntentId/shared.js @@ -1,5 +1,5 @@ -import {UID1_EIDS} from '../uid1Eids/uid1Eids.js'; -import {UID2_EIDS} from '../uid2Eids/uid2Eids.js'; +import { UID1_EIDS } from '../uid1Eids/uid1Eids.js'; +import { UID2_EIDS } from '../uid2Eids/uid2Eids.js'; import { getRefererInfo } from '../../src/refererDetection.js'; import { isNumber } from '../../src/utils.js' @@ -17,7 +17,7 @@ export function parseRequestedAttributes(overrides) { return Object.entries(config).flatMap(([k, v]) => (typeof v === 'boolean' && v) ? [k] : []); } if (typeof overrides === 'object') { - return createParameterArray({...DEFAULT_REQUESTED_ATTRIBUTES, ...overrides}); + return createParameterArray({ ...DEFAULT_REQUESTED_ATTRIBUTES, ...overrides }); } else { return createParameterArray(DEFAULT_REQUESTED_ATTRIBUTES); } @@ -112,7 +112,7 @@ function composeIdObject(value) { } if (value.thetradedesk) { - result.lipb = {...result.lipb, tdid: value.thetradedesk} + result.lipb = { ...result.lipb, tdid: value.thetradedesk } result.tdid = { 'id': value.thetradedesk, ext: { rtiPartner: 'TDID', provider: getRefererInfo().domain || LI_PROVIDER_DOMAIN } } delete result.lipb.thetradedesk } diff --git a/libraries/magniteUtils/outstream.js b/libraries/magniteUtils/outstream.js new file mode 100644 index 00000000000..59daeacae0c --- /dev/null +++ b/libraries/magniteUtils/outstream.js @@ -0,0 +1,80 @@ +import { Renderer } from '../../src/Renderer.js'; +import { logWarn } from '../../src/utils.js'; +import { getAdUnitElement } from '../../src/utils/adUnits.js'; + +export const DEFAULT_RENDERER_URL = 'https://video-outstream.rubiconproject.com/apex-2.3.7.js'; + +export function bidShouldUsePlayerWidthAndHeight(bidResponse) { + const doesNotHaveDimensions = typeof bidResponse.width !== 'number' || typeof bidResponse.height !== 'number'; + const hasPlayerSize = typeof bidResponse.playerWidth === 'number' && typeof bidResponse.playerHeight === 'number'; + return doesNotHaveDimensions && hasPlayerSize; +} + +export function hideGoogleAdsDiv(adUnit) { + const el = adUnit.querySelector("div[id^='google_ads']"); + if (el) { + el.style.setProperty('display', 'none'); + } +} + +export function hideSmartAdServerIframe(adUnit) { + const el = adUnit.querySelector("script[id^='sas_script']"); + const nextSibling = el && el.nextSibling; + if (nextSibling && nextSibling.localName === 'iframe') { + nextSibling.style.setProperty('display', 'none'); + } +} + +export function renderBid(bid) { + // hide existing ad units + let adUnitElement = getAdUnitElement(bid); + if (!adUnitElement) { + logWarn(`Magnite: unable to find ad unit element with id "${bid.adUnitCode}" for rendering.`); + return; + } + + // try to get child element of adunit + const firstChild = adUnitElement.firstElementChild; + if (firstChild?.tagName === 'DIV') { + adUnitElement = firstChild; + } + + hideGoogleAdsDiv(adUnitElement); + hideSmartAdServerIframe(adUnitElement); + + // configure renderer + const config = bid.renderer.getConfig(); + bid.renderer.push(() => { + globalThis.MagniteApex.renderAd({ + width: bid.width, + height: bid.height, + vastUrl: bid.vastUrl, + placement: { + attachTo: adUnitElement, + align: config.align || 'center', + position: config.position || 'prepend' + }, + closeButton: config.closeButton || false, + label: config.label, + replay: config.replay ?? true + }); + }); +} + +export function outstreamRenderer(rtbBid, rendererUrl, rendererConfig) { + const renderer = Renderer.install({ + id: rtbBid.adId, + url: rendererUrl || DEFAULT_RENDERER_URL, + config: rendererConfig || {}, + loaded: false, + adUnitCode: rtbBid.adUnitCode + }); + + try { + renderer.setRender(renderBid); + } catch (err) { + logWarn('Prebid Error calling setRender on renderer', err); + } + + return renderer; +} diff --git a/libraries/medianetUtils/logKeys.js b/libraries/medianetUtils/logKeys.js index 94ddcb82abb..eedc4a726ea 100644 --- a/libraries/medianetUtils/logKeys.js +++ b/libraries/medianetUtils/logKeys.js @@ -43,7 +43,7 @@ export const KeysMap = { AdSlot: [ 'code', 'ext as adext', - 'logged', () => ({[LOG_APPR]: false, [LOG_RA]: false}), + 'logged', () => ({ [LOG_APPR]: false, [LOG_RA]: false }), 'supcrid', (_, __, adUnit) => adUnit.emsCode || adUnit.code, 'ortb2Imp', ], @@ -98,7 +98,7 @@ export const KeysMap = { 'inCurrMul as imul', 'mediaTypes as req_mtype', (mediaTypes) => mediaTypes.join('|'), 'mediaType as res_mtype', - 'mediaType as mtype', (mediaType, __, {mediaTypes}) => mediaType || mediaTypes.join('|'), + 'mediaType as mtype', (mediaType, __, { mediaTypes }) => mediaType || mediaTypes.join('|'), 'ext.seat as ortbseat', 'ext.int_dsp_id as mx_int_dsp_id', 'ext.int_agency_id as mx_int_agency_id', @@ -109,7 +109,7 @@ export const KeysMap = { 'originalRequestId as ogReqId', 'adId as adid', 'originalBidder as og_pvnm', - 'bidderCode as pvnm', (bidderCode, _, {bidder}) => bidderCode || bidder, + 'bidderCode as pvnm', (bidderCode, _, { bidder }) => bidderCode || bidder, 'src', 'originalCpm as ogbdp', 'bdp', (bdp, _, bidObj) => bdp || bidObj.cpm, diff --git a/libraries/medianetUtils/logger.js b/libraries/medianetUtils/logger.js index d3a5dea1551..d3dc46419f4 100644 --- a/libraries/medianetUtils/logger.js +++ b/libraries/medianetUtils/logger.js @@ -23,7 +23,7 @@ export function shouldLogAPPR(auctionData, adUnitId) { // common error logger for medianet analytics and bid adapter export function errorLogger(event, data = undefined, analytics = true) { - const { name, cid, value, relatedData, logData, project } = isPlainObject(event) ? {...event, logData: data} : { name: event, relatedData: data }; + const { name, cid, value, relatedData, logData, project } = isPlainObject(event) ? { ...event, logData: data } : { name: event, relatedData: data }; const refererInfo = mnetGlobals.refererInfo || getRefererInfo(); const errorData = Object.assign({}, { @@ -88,7 +88,7 @@ export function fireAjaxLog(loggingHost, payload, errorData = {}) { ajax(loggingHost, { success: () => undefined, - error: (_, {reason}) => errorLogger(Object.assign(errorData, {name: 'ajax_log_failed', relatedData: reason})).send() + error: (_, { reason }) => errorLogger(Object.assign(errorData, { name: 'ajax_log_failed', relatedData: reason })).send() }, payload, { diff --git a/libraries/medianetUtils/utils.js b/libraries/medianetUtils/utils.js index 667c52e9fb2..800ff80ef99 100644 --- a/libraries/medianetUtils/utils.js +++ b/libraries/medianetUtils/utils.js @@ -1,6 +1,6 @@ import { _map, deepAccess, isFn, isPlainObject, uniques } from '../../src/utils.js'; -import {mnetGlobals} from './constants.js'; -import {getViewportSize} from '../viewport/viewport.js'; +import { mnetGlobals } from './constants.js'; +import { getViewportSize } from '../viewport/viewport.js'; export function findBidObj(list = [], key, value) { return list.find((bid) => { diff --git a/libraries/metadata/metadata.js b/libraries/metadata/metadata.js index dcabc99ac97..59d073de97f 100644 --- a/libraries/metadata/metadata.js +++ b/libraries/metadata/metadata.js @@ -33,8 +33,8 @@ export function metadataRepository() { if (components.length === 0) return null; const disclosures = Object.fromEntries( components - .filter(({disclosureURL}) => disclosureURL != null) - .map(({disclosureURL}) => [disclosureURL, repo.getStorageDisclosure(disclosureURL)]) + .filter(({ disclosureURL }) => disclosureURL != null) + .map(({ disclosureURL }) => [disclosureURL, repo.getStorageDisclosure(disclosureURL)]) ) return { disclosures, diff --git a/libraries/mspa/activityControls.js b/libraries/mspa/activityControls.js index c93748f73c7..fc049b53fff 100644 --- a/libraries/mspa/activityControls.js +++ b/libraries/mspa/activityControls.js @@ -1,12 +1,13 @@ -import {registerActivityControl} from '../../src/activities/rules.js'; +import { registerActivityControl } from '../../src/activities/rules.js'; import { ACTIVITY_ENRICH_EIDS, ACTIVITY_ENRICH_UFPD, ACTIVITY_SYNC_USER, - ACTIVITY_TRANSMIT_PRECISE_GEO + ACTIVITY_TRANSMIT_PRECISE_GEO, + ACTIVITY_TRANSMIT_UFPD } from '../../src/activities/activities.js'; -import {gppDataHandler} from '../../src/adapterManager.js'; -import {logInfo} from '../../src/utils.js'; +import { gppDataHandler } from '../../src/adapterManager.js'; +import { logInfo } from '../../src/utils.js'; // default interpretation for MSPA consent(s): // https://docs.prebid.org/features/mspa-usnat.html @@ -79,7 +80,7 @@ export const isTransmitUfpdConsentDenied = (() => { })() return function (cd) { - const {cannotBeInScope, mustHaveConsent, allExceptGeo} = sensitiveFlags[cd.Version]; + const { cannotBeInScope, mustHaveConsent, allExceptGeo } = sensitiveFlags[cd.Version]; return isConsentDenied(cd) || // no notice about sensitive data was given sensitiveNoticeIs(cd, 2) || @@ -105,7 +106,8 @@ export function isTransmitGeoConsentDenied(cd) { const CONSENT_RULES = { [ACTIVITY_SYNC_USER]: isConsentDenied, [ACTIVITY_ENRICH_EIDS]: isConsentDenied, - [ACTIVITY_ENRICH_UFPD]: isTransmitUfpdConsentDenied, + [ACTIVITY_ENRICH_UFPD]: isConsentDenied, + [ACTIVITY_TRANSMIT_UFPD]: isTransmitUfpdConsentDenied, [ACTIVITY_TRANSMIT_PRECISE_GEO]: isTransmitGeoConsentDenied }; @@ -114,13 +116,13 @@ export function mspaRule(sids, getConsent, denies, applicableSids = () => gppDat if (applicableSids().some(sid => sids.includes(sid))) { const consent = getConsent(); if (consent == null) { - return {allow: false, reason: 'consent data not available'}; + return { allow: false, reason: 'consent data not available' }; } if (![1, 2].includes(consent.Version)) { - return {allow: false, reason: `unsupported consent specification version "${consent.Version}"`} + return { allow: false, reason: `unsupported consent specification version "${consent.Version}"` } } if (denies(consent)) { - return {allow: false}; + return { allow: false }; } } }; diff --git a/libraries/nativeAssetsUtils.js b/libraries/nativeAssetsUtils.js index 9a59716cc68..4f985abaab8 100644 --- a/libraries/nativeAssetsUtils.js +++ b/libraries/nativeAssetsUtils.js @@ -87,9 +87,9 @@ export function buildNativeRequest(nativeParams) { if (nativeParams) { Object.keys(nativeParams).forEach((key) => { if (NATIVE_PARAMS[key]) { - const {name, type, id} = NATIVE_PARAMS[key]; - const assetObj = type ? {type} : {}; - let {len, sizes, required, aspect_ratios: aRatios} = nativeParams[key]; + const { name, type, id } = NATIVE_PARAMS[key]; + const assetObj = type ? { type } : {}; + let { len, sizes, required, aspect_ratios: aRatios } = nativeParams[key]; if (len) { assetObj.len = len; } @@ -105,7 +105,7 @@ export function buildNativeRequest(nativeParams) { assetObj.w = sizes[0]; assetObj.h = sizes[1]; } - const asset = {required: required ? 1 : 0, id}; + const asset = { required: required ? 1 : 0, id }; asset[name] = assetObj; assets.push(asset); } @@ -123,7 +123,7 @@ export function buildNativeRequest(nativeParams) { } export function parseNativeResponse(native) { - const {assets, link, imptrackers, jstracker} = native; + const { assets, link, imptrackers, jstracker } = native; const result = { clickUrl: link.url, clickTrackers: link.clicktrackers || [], @@ -132,7 +132,7 @@ export function parseNativeResponse(native) { }; (assets || []).forEach((asset) => { - const {id, img, data, title} = asset; + const { id, img, data, title } = asset; const key = NATIVE_ID_MAP[id]; if (key) { if (!isEmpty(title)) { diff --git a/libraries/nexx360Utils/index.ts b/libraries/nexx360Utils/index.ts index 0b8ed3fd719..f0bb2ce39ea 100644 --- a/libraries/nexx360Utils/index.ts +++ b/libraries/nexx360Utils/index.ts @@ -1,5 +1,5 @@ import { deepAccess, deepSetValue, generateUUID, logInfo } from '../../src/utils.js'; -import {Renderer} from '../../src/Renderer.js'; +import { Renderer } from '../../src/Renderer.js'; import { getCurrencyFromBidderRequest } from '../ortb2Utils/currency.js'; import { INSTREAM, OUTSTREAM } from '../../src/video.js'; import { BANNER, MediaType, NATIVE, VIDEO } from '../../src/mediaTypes.js'; diff --git a/libraries/objectGuard/objectGuard.js b/libraries/objectGuard/objectGuard.js index 973e56aad5f..4fbd58080f9 100644 --- a/libraries/objectGuard/objectGuard.js +++ b/libraries/objectGuard/objectGuard.js @@ -1,5 +1,5 @@ -import {isData, sessionedApplies} from '../../src/activities/redactor.js'; -import {deepEqual, logWarn} from '../../src/utils.js'; +import { isData, sessionedApplies } from '../../src/activities/redactor.js'; +import { deepEqual, logWarn } from '../../src/utils.js'; /** * @typedef {import('../src/activities/redactor.js').TransformationRuleDef} TransformationRuleDef @@ -139,20 +139,46 @@ export function objectGuard(rules) { return true; } + const TARGET = Symbol('TARGET'); + function mkGuard(obj, tree, final, applies, cache = new WeakMap()) { // If this object is already proxied, return the cached proxy if (cache.has(obj)) { return cache.get(obj); } + /** + * Dereference (possibly nested) proxies to their underlying objects. + * + * This is to accommodate usage patterns like: + * + * guardedObject.property = [...guardedObject.property, additionalData]; + * + * where the `set` proxy trap would get an already proxied object as argument. + */ + function deref(obj, visited = new Set()) { + if (cache.has(obj?.[TARGET])) return obj[TARGET]; + if (obj == null || typeof obj !== 'object') return obj; + if (visited.has(obj)) return obj; + visited.add(obj); + Object.keys(obj).forEach(k => { + const sub = deref(obj[k], visited); + if (sub !== obj[k]) { + obj[k] = sub; + } + }) + return obj; + } + const proxy = new Proxy(obj, { get(target, prop, receiver) { + if (prop === TARGET) return target; const val = Reflect.get(target, prop, receiver); if (final && val != null && typeof val === 'object') { // a parent property has write protect rules, keep guarding return mkGuard(val, tree, final, applies, cache) } else if (tree.children?.hasOwnProperty(prop)) { - const {children, hasWP} = tree.children[prop]; + const { children, hasWP } = tree.children[prop]; if (isData(val)) { // if this property has redact rules, apply them const rule = getRedactRule(tree.children[prop]); @@ -175,6 +201,7 @@ export function objectGuard(rules) { return true; } } + newValue = deref(newValue); if (tree.children?.hasOwnProperty(prop)) { // apply all (possibly nested) write protect rules const curValue = Reflect.get(target, prop, receiver); diff --git a/libraries/objectGuard/ortbGuard.js b/libraries/objectGuard/ortbGuard.js index 324c7976ab1..7587975ac06 100644 --- a/libraries/objectGuard/ortbGuard.js +++ b/libraries/objectGuard/ortbGuard.js @@ -1,13 +1,13 @@ -import {isActivityAllowed} from '../../src/activities/rules.js'; -import {ACTIVITY_ENRICH_EIDS, ACTIVITY_ENRICH_UFPD} from '../../src/activities/activities.js'; +import { isActivityAllowed } from '../../src/activities/rules.js'; +import { ACTIVITY_ENRICH_EIDS, ACTIVITY_ENRICH_UFPD } from '../../src/activities/activities.js'; import { appliesWhenActivityDenied, ortb2TransmitRules, ORTB_EIDS_PATHS, ORTB_UFPD_PATHS } from '../../src/activities/redactor.js'; -import {objectGuard, writeProtectRule} from './objectGuard.js'; -import {logError} from '../../src/utils.js'; +import { objectGuard, writeProtectRule } from './objectGuard.js'; +import { logError } from '../../src/utils.js'; function ortb2EnrichRules(isAllowed = isActivityAllowed) { return [ @@ -70,7 +70,7 @@ export function ortb2FragmentsGuardFactory(guardOrtb2 = ortb2Guard) { {}, Object.fromEntries( // disallow overwriting of the top level `global` / `bidder` - Object.entries(guard).map(([prop, obj]) => [prop, {get: () => obj}]) + Object.entries(guard).map(([prop, obj]) => [prop, { get: () => obj }]) ) ) } diff --git a/libraries/omsUtils/index.js b/libraries/omsUtils/index.js index b2523749080..421abeb8801 100644 --- a/libraries/omsUtils/index.js +++ b/libraries/omsUtils/index.js @@ -1,4 +1,4 @@ -import {getWindowSelf, getWindowTop, isFn, isPlainObject} from '../../src/utils.js'; +import { createTrackPixelHtml, getWindowSelf, getWindowTop, isArray, isFn, isPlainObject } from '../../src/utils.js'; export function getBidFloor(bid) { if (!isFn(bid.getFloor)) { @@ -21,3 +21,28 @@ export function isIframe() { return true; } } + +export function getProcessedSizes(sizes = []) { + const bidSizes = ((isArray(sizes) && isArray(sizes[0])) ? sizes : [sizes]).filter(size => isArray(size)); + return bidSizes.map(size => ({ w: parseInt(size[0], 10), h: parseInt(size[1], 10) })); +} + +export function getDeviceType(ua = navigator.userAgent, sua) { + if (sua?.mobile || (/(ios|ipod|ipad|iphone|android)/i).test(ua)) { + return 1; + } + + if ((/(smart[-]?tv|hbbtv|appletv|googletv|hdmi|netcast\.tv|viera|nettv|roku|\bdtv\b|sonydtv|inettvbrowser|\btv\b)/i).test(ua)) { + return 3; + } + + return 2; +} + +export function getAdMarkup(bid) { + let adm = bid.adm; + if ('nurl' in bid) { + adm += createTrackPixelHtml(bid.nurl); + } + return adm; +} diff --git a/libraries/omsUtils/viewability.js b/libraries/omsUtils/viewability.js new file mode 100644 index 00000000000..6d4b12c5e94 --- /dev/null +++ b/libraries/omsUtils/viewability.js @@ -0,0 +1,19 @@ +import { getWindowTop } from '../../src/utils.js'; +import { percentInView } from '../percentInView/percentInView.js'; +import { getMinSize } from '../sizeUtils/sizeUtils.js'; +import { isIframe } from './index.js'; + +export function getRoundedViewability(adUnitCode, processedSizes) { + const element = document.getElementById(adUnitCode); + const minSize = getMinSize(processedSizes); + const viewabilityAmount = isViewabilityMeasurable(element) ? getViewability(element, minSize) : 'na'; + return isNaN(viewabilityAmount) ? viewabilityAmount : Math.round(viewabilityAmount); +} + +function isViewabilityMeasurable(element) { + return !isIframe() && element !== null; +} + +function getViewability(element, { w, h } = {}) { + return getWindowTop().document.visibilityState === 'visible' ? percentInView(element, { w, h }) : 0; +} diff --git a/libraries/ortb2.5StrictTranslator/spec.js b/libraries/ortb2.5StrictTranslator/spec.js index 0ffb17a2e72..26be3f5b816 100644 --- a/libraries/ortb2.5StrictTranslator/spec.js +++ b/libraries/ortb2.5StrictTranslator/spec.js @@ -1,4 +1,4 @@ -import {Arr, extend, ID, IntEnum, Named, Obj} from './dsl.js'; +import { Arr, extend, ID, IntEnum, Named, Obj } from './dsl.js'; const CatDomain = Named[extend](['cat', 'domain']); const Segment = Named[extend](['value']); diff --git a/libraries/ortb2.5StrictTranslator/translator.js b/libraries/ortb2.5StrictTranslator/translator.js index c6f651e2476..7704bee0b92 100644 --- a/libraries/ortb2.5StrictTranslator/translator.js +++ b/libraries/ortb2.5StrictTranslator/translator.js @@ -1,6 +1,6 @@ -import {BidRequest} from './spec.js'; -import {logWarn} from '../../src/utils.js'; -import {toOrtb25} from '../ortb2.5Translator/translator.js'; +import { BidRequest } from './spec.js'; +import { logWarn } from '../../src/utils.js'; +import { toOrtb25 } from '../ortb2.5Translator/translator.js'; function deleteField(errno, path, obj, field, value) { logWarn(`${path} is not valid ORTB 2.5, field will be removed from request:`, value); diff --git a/libraries/ortb2.5Translator/translator.js b/libraries/ortb2.5Translator/translator.js index 1634d6584d0..f65de19b306 100644 --- a/libraries/ortb2.5Translator/translator.js +++ b/libraries/ortb2.5Translator/translator.js @@ -1,4 +1,4 @@ -import {deepAccess, deepSetValue, logError} from '../../src/utils.js'; +import { deepAccess, deepSetValue, logError } from '../../src/utils.js'; export const EXT_PROMOTIONS = [ 'device.sua', diff --git a/libraries/ortbConverter/README.md b/libraries/ortbConverter/README.md index c67533ae1de..691ff7bceb7 100644 --- a/libraries/ortbConverter/README.md +++ b/libraries/ortbConverter/README.md @@ -378,7 +378,7 @@ For ease of use, the conversion logic gives special meaning to some context prop ## Prebid Server extensions -If your endpoint is a Prebid Server instance, you may take advantage of the `pbsExtension` companion library, which adds a number of processors that can populate and parse PBS-specific extensions (typically prefixed `ext.prebid`); these include bidder params (with `transformBidParams`), bidder aliases, targeting keys, and others. +If your endpoint is a Prebid Server instance, you may take advantage of the `pbsExtension` companion library, which adds a number of processors that can populate and parse PBS-specific extensions (typically prefixed `ext.prebid`); these include bidder params, bidder aliases, targeting keys, and others. ```javascript import {pbsExtensions} from '../../libraries/pbsExtensions/pbsExtensions.js' diff --git a/libraries/ortbConverter/converter.ts b/libraries/ortbConverter/converter.ts index 1f848363879..54e35ad5419 100644 --- a/libraries/ortbConverter/converter.ts +++ b/libraries/ortbConverter/converter.ts @@ -1,16 +1,16 @@ -import {compose} from './lib/composer.js'; -import {logError, memoize} from '../../src/utils.js'; -import {DEFAULT_PROCESSORS} from './processors/default.js'; -import {BID_RESPONSE, DEFAULT, getProcessors, IMP, REQUEST, RESPONSE} from '../../src/pbjsORTB.js'; -import {mergeProcessors} from './lib/mergeProcessors.js'; -import type {MediaType} from "../../src/mediaTypes.ts"; -import type {NativeRequest} from '../../src/types/ortb/native.d.ts'; -import type {ORTBImp, ORTBRequest} from "../../src/types/ortb/request.d.ts"; -import type {Currency, BidderCode} from "../../src/types/common.d.ts"; -import type {BidderRequest, BidRequest} from "../../src/adapterManager.ts"; -import type {BidResponse} from "../../src/bidfactory.ts"; -import type {AdapterResponse} from "../../src/adapters/bidderFactory.ts"; -import type {ORTBResponse} from "../../src/types/ortb/response"; +import { compose } from './lib/composer.js'; +import { logError, memoize } from '../../src/utils.js'; +import { DEFAULT_PROCESSORS } from './processors/default.js'; +import { BID_RESPONSE, DEFAULT, getProcessors, IMP, REQUEST, RESPONSE } from '../../src/pbjsORTB.js'; +import { mergeProcessors } from './lib/mergeProcessors.js'; +import type { MediaType } from "../../src/mediaTypes.ts"; +import type { NativeRequest } from '../../src/types/ortb/native.d.ts'; +import type { ORTBImp, ORTBRequest } from "../../src/types/ortb/request.d.ts"; +import type { Currency, BidderCode } from "../../src/types/common.d.ts"; +import type { BidderRequest, BidRequest } from "../../src/adapterManager.ts"; +import type { BidResponse } from "../../src/bidfactory.ts"; +import type { AdapterResponse } from "../../src/adapters/bidderFactory.ts"; +import type { ORTBResponse } from "../../src/types/ortb/response"; type Context = { [key: string]: unknown; @@ -147,18 +147,18 @@ export function ortbConverter({ return imp; }, function (error, bidRequest, context) { - logError('Error while converting bidRequest to ORTB imp; request skipped.', {error, bidRequest, context}); + logError('Error while converting bidRequest to ORTB imp; request skipped.', { error, bidRequest, context }); } ); const buildRequest = builder(REQUEST, request, function (process, imps, bidderRequest, context) { - const ortbRequest = {imp: imps}; + const ortbRequest = { imp: imps }; process(ortbRequest, bidderRequest, context); return ortbRequest; }, function (error, imps, bidderRequest, context) { - logError('Error while converting to ORTB request', {error, imps, bidderRequest, context}); + logError('Error while converting to ORTB request', { error, imps, bidderRequest, context }); throw error; } ); @@ -170,40 +170,40 @@ export function ortbConverter({ return bidResponse; }, function (error, bid, context) { - logError('Error while converting ORTB seatbid.bid to bidResponse; bid skipped.', {error, bid, context}); + logError('Error while converting ORTB seatbid.bid to bidResponse; bid skipped.', { error, bid, context }); } ); const buildResponse = builder(RESPONSE, response, function (process, bidResponses, ortbResponse, context) { - const response = {bids: bidResponses}; + const response = { bids: bidResponses }; process(response, ortbResponse, context); return response; }, function (error, bidResponses, ortbResponse, context) { - logError('Error while converting from ORTB response', {error, bidResponses, ortbResponse, context}); + logError('Error while converting from ORTB response', { error, bidResponses, ortbResponse, context }); throw error; } ); return { - toORTB({bidderRequest, bidRequests, context = {}}: { + toORTB({ bidderRequest, bidRequests, context = {} }: { bidderRequest: BidderRequest, bidRequests?: BidRequest[], context?: Context }): ORTBRequest { bidRequests = bidRequests || bidderRequest.bids; const ctx = { - req: Object.assign({bidRequests}, defaultContext, context), + req: Object.assign({ bidRequests }, defaultContext, context), imp: {} } ctx.req.impContext = ctx.imp; const imps = bidRequests.map(bidRequest => { - const impContext = Object.assign({bidderRequest, reqContext: ctx.req}, defaultContext, context); + const impContext = Object.assign({ bidderRequest, reqContext: ctx.req }, defaultContext, context); const result = buildImp(bidRequest, impContext); if (result != null) { if (result.hasOwnProperty('id')) { - Object.assign(impContext, {bidRequest, imp: result}); + Object.assign(impContext, { bidRequest, imp: result }); ctx.imp[result.id] = impContext; return result; } @@ -219,7 +219,7 @@ export function ortbConverter({ } return request; }, - fromORTB({request, response}: { + fromORTB({ request, response }: { request: ORTBRequest; response: ORTBResponse | null; }): AdapterResponse { @@ -228,13 +228,13 @@ export function ortbConverter({ throw new Error('ortbRequest passed to `fromORTB` must be the same object returned by `toORTB`') } function augmentContext(ctx, extraParams = {}) { - return Object.assign(ctx, {ortbRequest: request}, extraParams); + return Object.assign(ctx, { ortbRequest: request }, extraParams); } const impsById = Object.fromEntries((request.imp || []).map(imp => [imp.id, imp])); const bidResponses = (response?.seatbid || []).flatMap(seatbid => (seatbid.bid || []).map((bid) => { if (impsById.hasOwnProperty(bid.impid) && ctx.imp.hasOwnProperty(bid.impid)) { - return buildBidResponse(bid, augmentContext(ctx.imp[bid.impid], {imp: impsById[bid.impid], seatbid, ortbResponse: response})); + return buildBidResponse(bid, augmentContext(ctx.imp[bid.impid], { imp: impsById[bid.impid], seatbid, ortbResponse: response })); } logError('ORTB response seatbid[].bid[].impid does not match any imp in request; ignoring bid', bid); return undefined; diff --git a/libraries/ortbConverter/lib/mergeProcessors.js b/libraries/ortbConverter/lib/mergeProcessors.js index 357cecd45aa..60892c36897 100644 --- a/libraries/ortbConverter/lib/mergeProcessors.js +++ b/libraries/ortbConverter/lib/mergeProcessors.js @@ -1,4 +1,4 @@ -import {PROCESSOR_TYPES} from '../../../src/pbjsORTB.js'; +import { PROCESSOR_TYPES } from '../../../src/pbjsORTB.js'; export function mergeProcessors(...processors) { const left = processors.shift(); diff --git a/libraries/ortbConverter/processors/banner.js b/libraries/ortbConverter/processors/banner.js index 516016caa0a..007cbdbdf64 100644 --- a/libraries/ortbConverter/processors/banner.js +++ b/libraries/ortbConverter/processors/banner.js @@ -6,7 +6,7 @@ import { sizeTupleToRtbSize, encodeMacroURI } from '../../../src/utils.js'; -import {BANNER} from '../../../src/mediaTypes.js'; +import { BANNER } from '../../../src/mediaTypes.js'; /** * fill in a request `imp` with banner parameters from `bidRequest`. @@ -30,7 +30,7 @@ export function fillBannerImp(imp, bidRequest, context) { } } -export function bannerResponseProcessor({createPixel = (url) => createTrackPixelHtml(decodeURIComponent(url), encodeMacroURI)} = {}) { +export function bannerResponseProcessor({ createPixel = (url) => createTrackPixelHtml(decodeURIComponent(url), encodeMacroURI) } = {}) { return function fillBannerResponse(bidResponse, bid) { if (bidResponse.mediaType === BANNER) { if (bid.adm && bid.nurl) { diff --git a/libraries/ortbConverter/processors/default.js b/libraries/ortbConverter/processors/default.js index b1fb5be77a5..3b6b1156063 100644 --- a/libraries/ortbConverter/processors/default.js +++ b/libraries/ortbConverter/processors/default.js @@ -1,10 +1,10 @@ -import {generateUUID, mergeDeep} from '../../../src/utils.js'; -import {bannerResponseProcessor, fillBannerImp} from './banner.js'; -import {fillVideoImp, fillVideoResponse} from './video.js'; -import {setResponseMediaType} from './mediaType.js'; -import {fillNativeImp, fillNativeResponse} from './native.js'; -import {BID_RESPONSE, IMP, REQUEST} from '../../../src/pbjsORTB.js'; -import {clientSectionChecker} from '../../../src/fpd/oneClient.js'; +import { generateUUID, mergeDeep } from '../../../src/utils.js'; +import { bannerResponseProcessor, fillBannerImp } from './banner.js'; +import { fillVideoImp, fillVideoResponse } from './video.js'; +import { setResponseMediaType } from './mediaType.js'; +import { fillNativeImp, fillNativeResponse } from './native.js'; +import { BID_RESPONSE, IMP, REQUEST } from '../../../src/pbjsORTB.js'; +import { clientSectionChecker } from '../../../src/fpd/oneClient.js'; import { fillAudioImp, fillAudioResponse } from './audio.js'; export const DEFAULT_PROCESSORS = { @@ -111,6 +111,9 @@ export const DEFAULT_PROCESSORS = { if (bid.ext?.eventtrackers) { bidResponse.eventtrackers = (bidResponse.eventtrackers ?? []).concat(bid.ext.eventtrackers); } + if (bid.cattax) { + bidResponse.meta.cattax = bid.cattax; + } } } } diff --git a/libraries/ortbConverter/processors/mediaType.js b/libraries/ortbConverter/processors/mediaType.js index 67232b3ca44..4d1aed6cac9 100644 --- a/libraries/ortbConverter/processors/mediaType.js +++ b/libraries/ortbConverter/processors/mediaType.js @@ -1,4 +1,4 @@ -import {BANNER, NATIVE, VIDEO} from '../../../src/mediaTypes.js'; +import { BANNER, NATIVE, VIDEO } from '../../../src/mediaTypes.js'; export const ORTB_MTYPES = { 1: BANNER, diff --git a/libraries/ortbConverter/processors/native.js b/libraries/ortbConverter/processors/native.js index ff231ce2b55..158bffca5fc 100644 --- a/libraries/ortbConverter/processors/native.js +++ b/libraries/ortbConverter/processors/native.js @@ -1,5 +1,5 @@ -import {isPlainObject, logWarn, mergeDeep} from '../../../src/utils.js'; -import {NATIVE} from '../../../src/mediaTypes.js'; +import { isPlainObject, logWarn, mergeDeep } from '../../../src/utils.js'; +import { NATIVE } from '../../../src/mediaTypes.js'; export function fillNativeImp(imp, bidRequest, context) { if (context.mediaType && context.mediaType !== NATIVE) return; diff --git a/libraries/ortbConverter/processors/video.js b/libraries/ortbConverter/processors/video.js index 3bb4e69e24d..dc7566ecb98 100644 --- a/libraries/ortbConverter/processors/video.js +++ b/libraries/ortbConverter/processors/video.js @@ -1,7 +1,7 @@ -import {isEmpty, logWarn, mergeDeep, sizesToSizeTuples, sizeTupleToRtbSize} from '../../../src/utils.js'; -import {VIDEO} from '../../../src/mediaTypes.js'; +import { isEmpty, logWarn, mergeDeep, sizesToSizeTuples, sizeTupleToRtbSize } from '../../../src/utils.js'; +import { VIDEO } from '../../../src/mediaTypes.js'; -import {ORTB_VIDEO_PARAMS} from '../../../src/video.js'; +import { ORTB_VIDEO_PARAMS } from '../../../src/video.js'; export function fillVideoImp(imp, bidRequest, context) { if (context.mediaType && context.mediaType !== VIDEO) return; diff --git a/libraries/paapiTools/buyerOrigins.js b/libraries/paapiTools/buyerOrigins.js deleted file mode 100644 index ace9b7da073..00000000000 --- a/libraries/paapiTools/buyerOrigins.js +++ /dev/null @@ -1,35 +0,0 @@ -/* - This list is several known buyer origins for PAAPI auctions. - Bidders should add anyone they like to it. - It is not intended to be comphensive nor maintained by the Core team. - Rather, Bid adapters should simply append additional constants whenever - the need arises in their adapter. - - The goal is to reduce expression of common constants over many - bid adapters attempting to define interestGroupBuyers - in advance of network traffic. - - Bidders should consider updating their interstGroupBuyer list - with server communication for auctions initiated after the first bid response. - - Known buyers without current importers are commented out. If you need one, uncomment it. -*/ - -export const BO_CSR_ONET = 'https://csr.onet.pl'; -// export const BO_DOUBLECLICK_GOOGLEADS = 'https://googleads.g.doubleclick.net'; -// export const BO_DOUBLECLICK_TD = 'https://td.doubleclick.net'; -// export const BO_RTBHOUSE = 'https://f.creativecdn.com'; -// export const BO_CRITEO_US = 'https://fledge.us.criteo.com'; -// export const BO_CRITEO_EU = 'https://fledge.eu.criteo.com'; -// export const BO_CRITEO_AS = 'https://fledge.as.criteo.com'; -// export const BO_CRITEO_GRID_MERCURY = 'https://grid-mercury.criteo.com'; -// export const BO_CRITEO_BIDSWITCH_TRADR = 'https://tradr.bsw-sb.criteo.com'; -// export const BO_CRITEO_BIDSWITCH_SANDBOX = 'https://dsp-paapi-sandbox.bsw-ig.criteo.com'; -// export const BO_APPSPOT = 'https://fledge-buyer-testing-1.uc.r.appspot.com'; -// export const BO_OPTABLE = 'https://ads.optable.co'; -// export const BO_ADROLL = 'https://x.adroll.com'; -// export const BO_ADFORM = 'https://a2.adform.net'; -// export const BO_RETARGETLY = 'https://cookieless-campaign.prd-00.retargetly.com'; -// export const BO_AUDIGENT = 'https://proton.ad.gt'; -// export const BO_YAHOO = 'https://pa.ybp.yahoo.com'; -// export const BO_DOTOMI = 'https://usadmm.dotomi.com'; diff --git a/libraries/pbsExtensions/pbsExtensions.js b/libraries/pbsExtensions/pbsExtensions.js index 1efded6173f..d66e8efb1e3 100644 --- a/libraries/pbsExtensions/pbsExtensions.js +++ b/libraries/pbsExtensions/pbsExtensions.js @@ -1,8 +1,8 @@ -import {mergeProcessors} from '../ortbConverter/lib/mergeProcessors.js'; -import {PBS_PROCESSORS} from './processors/pbs.js'; -import {getProcessors, PBS} from '../../src/pbjsORTB.js'; -import {defaultProcessors} from '../ortbConverter/converter.js'; -import {memoize} from '../../src/utils.js'; +import { mergeProcessors } from '../ortbConverter/lib/mergeProcessors.js'; +import { PBS_PROCESSORS } from './processors/pbs.js'; +import { getProcessors, PBS } from '../../src/pbjsORTB.js'; +import { defaultProcessors } from '../ortbConverter/converter.js'; +import { memoize } from '../../src/utils.js'; /** * ORTB converter processor set that understands Prebid Server extensions. diff --git a/libraries/pbsExtensions/processors/adUnitCode.js b/libraries/pbsExtensions/processors/adUnitCode.js index f936e0f662f..529c01aeb38 100644 --- a/libraries/pbsExtensions/processors/adUnitCode.js +++ b/libraries/pbsExtensions/processors/adUnitCode.js @@ -1,4 +1,4 @@ -import {deepSetValue} from '../../../src/utils.js'; +import { deepSetValue } from '../../../src/utils.js'; export function setImpAdUnitCode(imp, bidRequest) { const adUnitCode = bidRequest.adUnitCode; diff --git a/libraries/pbsExtensions/processors/aliases.js b/libraries/pbsExtensions/processors/aliases.js index 42dea969e6b..6a10400533c 100644 --- a/libraries/pbsExtensions/processors/aliases.js +++ b/libraries/pbsExtensions/processors/aliases.js @@ -1,8 +1,8 @@ import adapterManager from '../../../src/adapterManager.js'; -import {config} from '../../../src/config.js'; -import {deepSetValue} from '../../../src/utils.js'; +import { config } from '../../../src/config.js'; +import { deepSetValue } from '../../../src/utils.js'; -export function setRequestExtPrebidAliases(ortbRequest, bidderRequest, context, {am = adapterManager} = {}) { +export function setRequestExtPrebidAliases(ortbRequest, bidderRequest, context, { am = adapterManager } = {}) { if (am.aliasRegistry[bidderRequest.bidderCode]) { const bidder = am.bidderRegistry[bidderRequest.bidderCode]; // adding alias only if alias source bidder exists and alias isn't configured to be standalone diff --git a/libraries/pbsExtensions/processors/eventTrackers.js b/libraries/pbsExtensions/processors/eventTrackers.js index 287084a3e21..fba38168b7c 100644 --- a/libraries/pbsExtensions/processors/eventTrackers.js +++ b/libraries/pbsExtensions/processors/eventTrackers.js @@ -1,4 +1,4 @@ -import {EVENT_TYPE_IMPRESSION, EVENT_TYPE_WIN, TRACKER_METHOD_IMG} from '../../../src/eventTrackers.js'; +import { EVENT_TYPE_IMPRESSION, EVENT_TYPE_WIN, TRACKER_METHOD_IMG } from '../../../src/eventTrackers.js'; export function addEventTrackers(bidResponse, bid) { bidResponse.eventtrackers = bidResponse.eventtrackers || []; @@ -6,7 +6,7 @@ export function addEventTrackers(bidResponse, bid) { [bid.burl, EVENT_TYPE_IMPRESSION], // core used to fire burl directly, but only for bids coming from PBS [bid?.ext?.prebid?.events?.win, EVENT_TYPE_WIN] ].filter(([winUrl, type]) => winUrl && bidResponse.eventtrackers.find( - ({method, event, url}) => event === type && method === TRACKER_METHOD_IMG && url === winUrl + ({ method, event, url }) => event === type && method === TRACKER_METHOD_IMG && url === winUrl ) == null) .forEach(([url, event]) => { bidResponse.eventtrackers.push({ diff --git a/libraries/pbsExtensions/processors/mediaType.js b/libraries/pbsExtensions/processors/mediaType.js index cbcf9a013b1..1045a6ced70 100644 --- a/libraries/pbsExtensions/processors/mediaType.js +++ b/libraries/pbsExtensions/processors/mediaType.js @@ -1,5 +1,5 @@ -import {BANNER, NATIVE, VIDEO} from '../../../src/mediaTypes.js'; -import {ORTB_MTYPES} from '../../ortbConverter/processors/mediaType.js'; +import { BANNER, NATIVE, VIDEO } from '../../../src/mediaTypes.js'; +import { ORTB_MTYPES } from '../../ortbConverter/processors/mediaType.js'; export const SUPPORTED_MEDIA_TYPES = { // map from pbjs mediaType to its corresponding imp property diff --git a/libraries/pbsExtensions/processors/pageViewIds.js b/libraries/pbsExtensions/processors/pageViewIds.js index c71d32b7735..22b422e3e80 100644 --- a/libraries/pbsExtensions/processors/pageViewIds.js +++ b/libraries/pbsExtensions/processors/pageViewIds.js @@ -1,4 +1,4 @@ -import {deepSetValue} from '../../../src/utils.js'; +import { deepSetValue } from '../../../src/utils.js'; export function setRequestExtPrebidPageViewIds(ortbRequest, bidderRequest) { deepSetValue( diff --git a/libraries/pbsExtensions/processors/params.js b/libraries/pbsExtensions/processors/params.js index 1dadb02fde3..949835382ff 100644 --- a/libraries/pbsExtensions/processors/params.js +++ b/libraries/pbsExtensions/processors/params.js @@ -1,4 +1,4 @@ -import {deepSetValue} from '../../../src/utils.js'; +import { deepSetValue } from '../../../src/utils.js'; export function setImpBidParams(imp, bidRequest) { const params = bidRequest.params; diff --git a/libraries/pbsExtensions/processors/pbs.js b/libraries/pbsExtensions/processors/pbs.js index 3fa97ae674b..001a4db07bc 100644 --- a/libraries/pbsExtensions/processors/pbs.js +++ b/libraries/pbsExtensions/processors/pbs.js @@ -1,13 +1,13 @@ -import {BID_RESPONSE, IMP, REQUEST, RESPONSE} from '../../../src/pbjsORTB.js'; -import {isPlainObject, isStr, mergeDeep} from '../../../src/utils.js'; -import {extPrebidMediaType} from './mediaType.js'; -import {setRequestExtPrebidAliases} from './aliases.js'; -import {setImpBidParams} from './params.js'; -import {setImpAdUnitCode} from './adUnitCode.js'; -import {setRequestExtPrebid, setRequestExtPrebidChannel} from './requestExtPrebid.js'; -import {setBidResponseVideoCache} from './video.js'; -import {addEventTrackers} from './eventTrackers.js'; -import {setRequestExtPrebidPageViewIds} from './pageViewIds.js'; +import { BID_RESPONSE, IMP, REQUEST, RESPONSE } from '../../../src/pbjsORTB.js'; +import { isPlainObject, isStr, mergeDeep } from '../../../src/utils.js'; +import { extPrebidMediaType } from './mediaType.js'; +import { setRequestExtPrebidAliases } from './aliases.js'; +import { setImpBidParams } from './params.js'; +import { setImpAdUnitCode } from './adUnitCode.js'; +import { setRequestExtPrebid, setRequestExtPrebidChannel } from './requestExtPrebid.js'; +import { setBidResponseVideoCache } from './video.js'; +import { addEventTrackers } from './eventTrackers.js'; +import { setRequestExtPrebidPageViewIds } from './pageViewIds.js'; export const PBS_PROCESSORS = { [REQUEST]: { @@ -30,7 +30,7 @@ export const PBS_PROCESSORS = { }, [IMP]: { params: { - // sets bid ext.prebid.bidder.[bidderCode] with bidRequest.params, passed through transformBidParams if necessary + // sets bid ext.prebid.bidder.[bidderCode] with bidRequest.params fn: setImpBidParams }, adUnitCode: { diff --git a/libraries/pbsExtensions/processors/requestExtPrebid.js b/libraries/pbsExtensions/processors/requestExtPrebid.js index bbb6add45ce..780a2d08883 100644 --- a/libraries/pbsExtensions/processors/requestExtPrebid.js +++ b/libraries/pbsExtensions/processors/requestExtPrebid.js @@ -1,6 +1,6 @@ -import {deepSetValue, mergeDeep} from '../../../src/utils.js'; -import {config} from '../../../src/config.js'; -import {getGlobal} from '../../../src/prebidGlobal.js'; +import { deepSetValue, mergeDeep } from '../../../src/utils.js'; +import { config } from '../../../src/config.js'; +import { getGlobal } from '../../../src/prebidGlobal.js'; export function setRequestExtPrebid(ortbRequest, bidderRequest) { deepSetValue( diff --git a/libraries/pbsExtensions/processors/video.js b/libraries/pbsExtensions/processors/video.js index bcc24eea1b1..235c1ac3947 100644 --- a/libraries/pbsExtensions/processors/video.js +++ b/libraries/pbsExtensions/processors/video.js @@ -1,12 +1,12 @@ -import {VIDEO} from '../../../src/mediaTypes.js'; +import { VIDEO } from '../../../src/mediaTypes.js'; export function setBidResponseVideoCache(bidResponse, bid) { if (bidResponse.mediaType === VIDEO) { // try to get cache values from 'response.ext.prebid.cache' // else try 'bid.ext.prebid.targeting' as fallback - let {cacheId: videoCacheKey, url: vastUrl} = bid?.ext?.prebid?.cache?.vastXml ?? {}; + let { cacheId: videoCacheKey, url: vastUrl } = bid?.ext?.prebid?.cache?.vastXml ?? {}; if (!videoCacheKey || !vastUrl) { - const {hb_uuid: uuid, hb_cache_host: cacheHost, hb_cache_path: cachePath} = bid?.ext?.prebid?.targeting ?? {}; + const { hb_uuid: uuid, hb_cache_host: cacheHost, hb_cache_path: cachePath } = bid?.ext?.prebid?.targeting ?? {}; if (uuid && cacheHost && cachePath) { videoCacheKey = uuid; vastUrl = `https://${cacheHost}${cachePath}?uuid=${uuid}`; diff --git a/libraries/percentInView/percentInView.js b/libraries/percentInView/percentInView.js index 27148e40941..21605c194a2 100644 --- a/libraries/percentInView/percentInView.js +++ b/libraries/percentInView/percentInView.js @@ -1,8 +1,34 @@ import { getWinDimensions, inIframe } from '../../src/utils.js'; import { getBoundingClientRect } from '../boundingClientRect/boundingClientRect.js'; +import { defer, PbPromise, delay } from '../../src/utils/promise.js'; +import { startAuction } from '../../src/prebid.js'; +import { getAdUnitElement } from '../../src/utils/adUnits.js'; -export function getBoundingBox(element, {w, h} = {}) { - let {width, height, left, top, right, bottom, x, y} = getBoundingClientRect(element); +/** + * return the offset between the given window's viewport and the top window's. + */ +export function getViewportOffset(win = window) { + let x = 0; + let y = 0; + try { + while (win?.frameElement != null) { + const rect = getBoundingClientRect(win.frameElement); + x += rect.left; + y += rect.top; + win = win.parent; + } + } catch (e) { + // offset cannot be calculated as some parents are cross-frame + // fallback to 0,0 + x = 0; + y = 0; + } + + return { x, y }; +} + +function applySize(bbox, { w, h }) { + let { width, height, left, top, right, bottom, x, y } = bbox; if ((width === 0 || height === 0) && w && h) { width = w; @@ -11,7 +37,11 @@ export function getBoundingBox(element, {w, h} = {}) { bottom = top + h; } - return {width, height, left, top, right, bottom, x, y}; + return { width, height, left, top, right, bottom, x, y }; +} + +export function getBoundingBox(element, { w, h } = {}) { + return applySize(getBoundingClientRect(element), { w, h }); } function getIntersectionOfRects(rects) { @@ -41,17 +71,27 @@ function getIntersectionOfRects(rects) { return bbox; } -export const percentInView = (element, {w, h} = {}) => { - const elementBoundingBox = getBoundingBox(element, {w, h}); +const percentInViewStatic = (element, { w, h } = {}) => { + const elementBoundingBox = getBoundingBox(element, { w, h }); - const { innerHeight, innerWidth } = getWinDimensions(); + // when in an iframe, the bounding box is relative to the iframe's viewport + // since we are intersecting it with the top window's viewport, attempt to + // compensate for the offset between them + + const offset = getViewportOffset(element?.ownerDocument?.defaultView); + elementBoundingBox.left += offset.x; + elementBoundingBox.right += offset.x; + elementBoundingBox.top += offset.y; + elementBoundingBox.bottom += offset.y; + + const dims = getWinDimensions(); // Obtain the intersection of the element and the viewport const elementInViewBoundingBox = getIntersectionOfRects([{ left: 0, top: 0, - right: innerWidth, - bottom: innerHeight + right: dims.document.documentElement.clientWidth, + bottom: dims.document.documentElement.clientHeight }, elementBoundingBox]); let elementInViewArea, elementTotalArea; @@ -69,6 +109,103 @@ export const percentInView = (element, {w, h} = {}) => { return 0; } +/** + * A wrapper around an IntersectionObserver that keeps track of the latest IntersectionEntry that was observed + * for each observed element. + * + * @param mkObserver + */ +export function intersections(mkObserver) { + const intersections = new WeakMap(); + let next = defer(); + function observerCallback(entries) { + entries.forEach(entry => { + if ((intersections.get(entry.target)?.time ?? -1) < entry.time) { + intersections.set(entry.target, entry) + next.resolve(); + next = defer(); + } + }) + } + + let obs = null; + try { + obs = mkObserver(observerCallback); + } catch (e) { + // IntersectionObserver not supported + } + + async function waitFor(element) { + const intersection = getIntersection(element); + if (intersection != null) { + return intersection; + } else { + return next.promise.then(() => waitFor(element)); + } + } + /** + * Observe the given element; returns a promise to the first available intersection observed for it. + */ + async function observe(element) { + if (obs != null && !intersections.has(element)) { + obs.observe(element); + intersections.set(element, null); + return waitFor(element); + } else { + return PbPromise.resolve(getIntersection(element)); + } + } + + /** + * Return the latest intersection that was observed for the given element. + */ + function getIntersection(element) { + return intersections.get(element); + } + + return { + observe, + getIntersection, + } +} + +export const viewportIntersections = intersections((callback) => new IntersectionObserver(callback, { + // update percentInView when visibility varies by 1% + threshold: Array.from({ length: 101 }, (e, i) => i / 100) +})); + +export function mkIntersectionHook(intersections = viewportIntersections) { + return function (next, request) { + PbPromise.race([ + PbPromise.allSettled((request.adUnits ?? []).map(adUnit => + intersections.observe(getAdUnitElement(adUnit)) + )), + // according to MDN, with threshold 0 "the callback will be run as soon as the target element intersects or touches the boundary of the root, even if no pixels are yet visible" + // https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API + // However, browsers appear to run it even when the element is outside the DOM + // just to be sure, cap the amount of time we wait for intersections + delay(20) + ]).then(() => next.call(this, request)); + } +} + +startAuction.before(mkIntersectionHook()); + +export function percentInView(element, { w, h } = {}) { + const intersection = viewportIntersections.getIntersection(element); + if (intersection == null) { + viewportIntersections.observe(element); + return percentInViewStatic(element, { w, h }); + } else { + const adjusted = applySize(intersection.boundingClientRect, { w, h }); + if (adjusted.width !== intersection.boundingClientRect.width || adjusted.height !== intersection.boundingClientRect.height) { + // use w/h override + return percentInViewStatic(element, { w, h }); + } + return intersection.isIntersecting ? intersection.intersectionRatio * 100 : 0; + } +} + /** * Checks if viewability can be measured for an element * @param {HTMLElement} element - DOM element to check diff --git a/libraries/placementPositionInfo/placementPositionInfo.js b/libraries/placementPositionInfo/placementPositionInfo.js new file mode 100644 index 00000000000..d0387c4bb0b --- /dev/null +++ b/libraries/placementPositionInfo/placementPositionInfo.js @@ -0,0 +1,87 @@ +import { getBoundingClientRect } from '../boundingClientRect/boundingClientRect.js'; +import { canAccessWindowTop, cleanObj, getWinDimensions, getWindowSelf, getWindowTop } from '../../src/utils.js'; +import { getViewability, getViewportOffset } from '../percentInView/percentInView.js'; +import { getAdUnitElement } from '../../src/utils/adUnits.js'; + +export function getPlacementPositionUtils() { + const topWin = canAccessWindowTop() ? getWindowTop() : getWindowSelf(); + + const getViewportHeight = () => { + const dim = getWinDimensions(); + return dim.innerHeight || dim.document.documentElement.clientHeight || dim.document.body.clientHeight || 0; + }; + + const getPageHeight = () => { + const dim = getWinDimensions(); + const body = dim.document.body; + const html = dim.document.documentElement; + if (!body || !html) return 0; + + return Math.max( + body.scrollHeight, + body.offsetHeight, + html.clientHeight, + html.scrollHeight, + html.offsetHeight + ); + }; + + const getViewableDistance = (element, frameOffset) => { + if (!element) return { distanceToView: 0, elementHeight: 0 }; + + const elementRect = getBoundingClientRect(element); + if (!elementRect) return { distanceToView: 0, elementHeight: 0 }; + + const elementTop = elementRect.top + frameOffset.y; + const elementBottom = elementRect.bottom + frameOffset.y; + const viewportHeight = getViewportHeight(); + + let distanceToView; + if (elementTop - viewportHeight <= 0 && elementBottom >= 0) { + distanceToView = 0; + } else if (elementTop - viewportHeight > 0) { + distanceToView = Math.round(elementTop - viewportHeight); + } else { + distanceToView = Math.round(elementBottom); + } + + return { distanceToView, elementHeight: elementRect.height }; + }; + + function getPlacementInfo(bidReq) { + const element = getAdUnitElement(bidReq); + const frameOffset = getViewportOffset(); + const { distanceToView, elementHeight } = getViewableDistance(element, frameOffset); + + const sizes = (bidReq.sizes || []).map(size => ({ + w: Number.parseInt(size[0], 10), + h: Number.parseInt(size[1], 10) + })); + const size = sizes.length > 0 + ? sizes.reduce((min, size) => size.h * size.w < min.h * min.w ? size : min, sizes[0]) + : {}; + + const placementPercentView = element ? getViewability(element, topWin, size) : 0; + + return cleanObj({ + AuctionsCount: bidReq.auctionsCount, + DistanceToView: distanceToView, + PlacementPercentView: Math.round(placementPercentView), + ElementHeight: Math.round(elementHeight) || 1 + }); + } + + function getPlacementEnv() { + return cleanObj({ + TimeFromNavigation: Math.floor(performance.now()), + TabActive: topWin.document.visibilityState === 'visible', + PageHeight: getPageHeight(), + ViewportHeight: getViewportHeight() + }); + } + + return { + getPlacementInfo, + getPlacementEnv + }; +} diff --git a/libraries/riseUtils/constants.js b/libraries/riseUtils/constants.js index 7c2e4b52f8c..0ed9691db6e 100644 --- a/libraries/riseUtils/constants.js +++ b/libraries/riseUtils/constants.js @@ -1,4 +1,4 @@ -import {BANNER, NATIVE, VIDEO} from '../../src/mediaTypes.js'; +import { BANNER, NATIVE, VIDEO } from '../../src/mediaTypes.js'; const OW_GVLID = 280 export const SUPPORTED_AD_TYPES = [BANNER, VIDEO, NATIVE]; diff --git a/libraries/riseUtils/index.js b/libraries/riseUtils/index.js index 77ca87f0fca..c818dc8bc21 100644 --- a/libraries/riseUtils/index.js +++ b/libraries/riseUtils/index.js @@ -10,12 +10,11 @@ import { logInfo, triggerPixel } from '../../src/utils.js'; -import {BANNER, NATIVE, VIDEO} from '../../src/mediaTypes.js'; -import {config} from '../../src/config.js'; +import { BANNER, NATIVE, VIDEO } from '../../src/mediaTypes.js'; +import { config } from '../../src/config.js'; import { getDNT } from '../dnt/index.js'; -import {ADAPTER_VERSION, DEFAULT_CURRENCY, DEFAULT_TTL, SUPPORTED_AD_TYPES} from './constants.js'; - -import {getGlobalVarName} from '../../src/buildOptions.js'; +import { ADAPTER_VERSION, DEFAULT_CURRENCY, DEFAULT_TTL, SUPPORTED_AD_TYPES } from './constants.js'; +import { getGlobalVarName } from '../../src/buildOptions.js'; export const makeBaseSpec = (baseUrl, modes) => { return { @@ -350,7 +349,7 @@ export function buildBidResponse(adUnit) { } else if (adUnit.mediaType === BANNER) { bidResponse.ad = adUnit.ad; } else if (adUnit.mediaType === NATIVE) { - bidResponse.native = {ortb: adUnit.native}; + bidResponse.native = { ortb: adUnit.native }; } if (adUnit.adomain && adUnit.adomain.length) { diff --git a/libraries/storageDisclosure/summary.mjs b/libraries/storageDisclosure/summary.mjs index 3946efaddd8..7953fd06a12 100644 --- a/libraries/storageDisclosure/summary.mjs +++ b/libraries/storageDisclosure/summary.mjs @@ -6,9 +6,9 @@ export function getStorageDisclosureSummary(moduleNames, getModuleMetadata) { moduleNames.forEach(moduleName => { const disclosure = getModuleMetadata(moduleName)?.disclosures; if (!disclosure) return; - Object.entries(disclosure).forEach(([url, {disclosures: identifiers}]) => { + Object.entries(disclosure).forEach(([url, { disclosures: identifiers }]) => { if (summary.hasOwnProperty(url)) { - summary[url].forEach(({disclosedBy}) => disclosedBy.push(moduleName)); + summary[url].forEach(({ disclosedBy }) => disclosedBy.push(moduleName)); } else if (identifiers?.length > 0) { summary[url] = identifiers.map(identifier => ({ disclosedIn: url, diff --git a/libraries/targetVideoUtils/bidderUtils.js b/libraries/targetVideoUtils/bidderUtils.js index b082cfbe5cf..a30e572d83a 100644 --- a/libraries/targetVideoUtils/bidderUtils.js +++ b/libraries/targetVideoUtils/bidderUtils.js @@ -1,7 +1,7 @@ -import {SYNC_URL} from './constants.js'; -import {VIDEO} from '../../src/mediaTypes.js'; -import {getRefererInfo} from '../../src/refererDetection.js'; -import {createTrackPixelHtml, getBidRequest, formatQS} from '../../src/utils.js'; +import { SYNC_URL } from './constants.js'; +import { VIDEO } from '../../src/mediaTypes.js'; +import { getRefererInfo } from '../../src/refererDetection.js'; +import { createTrackPixelHtml, getBidRequest, formatQS } from '../../src/utils.js'; export function getSizes(request) { let sizes = request.sizes; @@ -18,7 +18,7 @@ export function getSizes(request) { return sizes; } -export function formatRequest({payload, url, bidderRequest, bidId}) { +export function formatRequest({ payload, url, bidderRequest, bidId }) { const request = { method: 'POST', data: JSON.stringify(payload), @@ -94,7 +94,7 @@ export function bannerBid(serverBid, rtbBid, bidderRequest, margin) { } export function videoBid(serverBid, requestId, currency, params, ttl) { - const {ad, adUrl, vastUrl, vastXml} = getAd(serverBid); + const { ad, adUrl, vastUrl, vastXml } = getAd(serverBid); const bid = { requestId, @@ -165,7 +165,7 @@ export function getAd(bid) { }; } - return {ad, adUrl, vastXml, vastUrl}; + return { ad, adUrl, vastXml, vastUrl }; } export function getSyncResponse(syncOptions, gdprConsent, uspConsent, gppConsent, endpoint) { diff --git a/libraries/teqblazeUtils/bidderUtils.js b/libraries/teqblazeUtils/bidderUtils.js index f310f2304d2..f57cb30ffa7 100644 --- a/libraries/teqblazeUtils/bidderUtils.js +++ b/libraries/teqblazeUtils/bidderUtils.js @@ -200,7 +200,7 @@ export const buildRequests = (adUrl) => (validBidRequests = [], bidderRequest = return buildRequestsBase({ adUrl, validBidRequests, bidderRequest, placementProcessingFunction }); }; -export function interpretResponseBuilder({addtlBidValidation = (bid) => true} = {}) { +export function interpretResponseBuilder({ addtlBidValidation = (bid) => true } = {}) { return function (serverResponse) { const response = []; for (let i = 0; i < serverResponse.body.length; i++) { @@ -231,8 +231,8 @@ export const getUserSyncs = (syncUrl) => (syncOptions, serverResponses, gdprCons } } - if (uspConsent && uspConsent.consentString) { - url += `&ccpa_consent=${uspConsent.consentString}`; + if (uspConsent) { + url += `&ccpa_consent=${uspConsent}`; } if (gppConsent?.gppString && gppConsent?.applicableSections?.length) { diff --git a/libraries/timezone/timezone.js b/libraries/timezone/timezone.js index e4ef39f28ef..87d00bee43c 100644 --- a/libraries/timezone/timezone.js +++ b/libraries/timezone/timezone.js @@ -1,3 +1,8 @@ +import { isFingerprintingApiDisabled } from '../fingerprinting/fingerprinting.js'; + export function getTimeZone() { + if (isFingerprintingApiDisabled('resolvedoptions')) { + return ''; + } return Intl.DateTimeFormat().resolvedOptions().timeZone; } diff --git a/libraries/transformParamsUtils/convertTypes.js b/libraries/transformParamsUtils/convertTypes.js index 813d8e6e693..59611d52817 100644 --- a/libraries/transformParamsUtils/convertTypes.js +++ b/libraries/transformParamsUtils/convertTypes.js @@ -1,4 +1,4 @@ -import {isFn} from '../../src/utils.js'; +import { isFn } from '../../src/utils.js'; /** * Try to convert a value to a type. diff --git a/libraries/uid1Eids/uid1Eids.js b/libraries/uid1Eids/uid1Eids.js index 5bf3dde5c6c..4b45c984058 100644 --- a/libraries/uid1Eids/uid1Eids.js +++ b/libraries/uid1Eids/uid1Eids.js @@ -10,7 +10,7 @@ export const UID1_EIDS = { } }, getUidExt: function(data) { - return {...{rtiPartner: 'TDID'}, ...data.ext} + return { ...{ rtiPartner: 'TDID' }, ...data.ext } } } } diff --git a/libraries/uid2IdSystemShared/uid2IdSystem_shared.js b/libraries/uid2IdSystemShared/uid2IdSystem_shared.js index 70a607a0f8f..aa9af0844d7 100644 --- a/libraries/uid2IdSystemShared/uid2IdSystem_shared.js +++ b/libraries/uid2IdSystemShared/uid2IdSystem_shared.js @@ -33,14 +33,17 @@ export class Uid2ApiClient { } return arrayBuffer; } + hasStatusResponse(response) { return typeof (response) === 'object' && response && response.status; } + isValidRefreshResponse(response) { return this.hasStatusResponse(response) && ( response.status === 'optout' || response.status === 'expired_token' || (response.status === 'success' && response.body && isValidIdentity(response.body)) ); } + ResponseToRefreshResult(response) { if (this.isValidRefreshResponse(response)) { if (response.status === 'success') { return { status: response.status, identity: response.body }; } @@ -48,6 +51,7 @@ export class Uid2ApiClient { return response; } else { return prependMessage(`Response didn't contain a valid status`); } } + callRefreshApi(refreshDetails) { const url = this._baseUrl + '/v2/token/refresh'; let resolvePromise; @@ -98,10 +102,12 @@ export class Uid2ApiClient { rejectPromise(prependMessage(error)); } } - }, refreshDetails.refresh_token, { method: 'POST', + }, refreshDetails.refresh_token, { + method: 'POST', customHeaders: { 'X-UID2-Client-Version': this._clientVersion - } }); + } + }); return promise; } } @@ -112,30 +118,39 @@ export class Uid2StorageManager { this._storageName = storageName; this._logInfo = (...args) => logInfoWrapper(logInfo, ...args); } + readCookie(cookieName) { return this._storage.cookiesAreEnabled() ? this._storage.getCookie(cookieName) : null; } + readLocalStorage(key) { return this._storage.localStorageIsEnabled() ? this._storage.getDataFromLocalStorage(key) : null; } + readModuleCookie() { return this.parseIfContainsBraces(this.readCookie(this._storageName)); } + writeModuleCookie(value) { this._storage.setCookie(this._storageName, JSON.stringify(value), Date.now() + 60 * 60 * 24 * 1000); } + readModuleStorage() { return this.parseIfContainsBraces(this.readLocalStorage(this._storageName)); } + writeModuleStorage(value) { this._storage.setDataInLocalStorage(this._storageName, JSON.stringify(value)); } + readProvidedCookie(cookieName) { return JSON.parse(this.readCookie(cookieName)); } + parseIfContainsBraces(value) { return (value?.includes('{')) ? JSON.parse(value) : value; } + storeValue(value) { if (this._preferLocalStorage) { this.writeModuleStorage(value); @@ -176,7 +191,7 @@ export class Uid2StorageManager { function refreshTokenAndStore(baseUrl, token, clientId, storageManager, _logInfo, _logWarn) { _logInfo('UID2 base url provided: ', baseUrl); - const client = new Uid2ApiClient({baseUrl}, clientId, _logInfo, _logWarn); + const client = new Uid2ApiClient({ baseUrl }, clientId, _logInfo, _logWarn); return client.callRefreshApi(token).then((response) => { _logInfo('Refresh endpoint responded with:', response); const tokens = { @@ -744,12 +759,14 @@ export function Uid2GetId(config, prebidStorageManager, _logInfo, _logWarn) { if (!storedTokens || Date.now() > storedTokens.latestToken.refresh_expires) { const promise = clientSideTokenGenerator.generateTokenAndStore(config.apiBaseUrl, config.cstg, cstgIdentity, storageManager, logInfo, _logWarn); logInfo('Generate token using CSTG'); - return { callback: (cb) => { - promise.then((result) => { - logInfo('Token generation responded, passing the new token on.', result); - cb(result); - }).catch((e) => { logError('error generating token: ', e); }); - } }; + return { + callback: (cb) => { + promise.then((result) => { + logInfo('Token generation responded, passing the new token on.', result); + cb(result); + }).catch((e) => { logError('error generating token: ', e); }); + } + }; } } } @@ -764,12 +781,14 @@ export function Uid2GetId(config, prebidStorageManager, _logInfo, _logWarn) { if (Date.now() > newestAvailableToken.identity_expires) { const promise = refreshTokenAndStore(config.apiBaseUrl, newestAvailableToken, config.clientId, storageManager, logInfo, _logWarn); logInfo('Token is expired but can be refreshed, attempting refresh.'); - return { callback: (cb) => { - promise.then((result) => { - logInfo('Refresh reponded, passing the updated token on.', result); - cb(result); - }).catch((e) => { logError('error refreshing token: ', e); }); - } }; + return { + callback: (cb) => { + promise.then((result) => { + logInfo('Refresh reponded, passing the updated token on.', result); + cb(result); + }).catch((e) => { logError('error refreshing token: ', e); }); + } + }; } const tokens = { originalToken: suppliedToken ?? storedTokens?.originalToken, diff --git a/libraries/utiqUtils/utiqUtils.ts b/libraries/utiqUtils/utiqUtils.ts index 347c5a973d2..dd2ea9ff06c 100644 --- a/libraries/utiqUtils/utiqUtils.ts +++ b/libraries/utiqUtils/utiqUtils.ts @@ -30,7 +30,7 @@ export function findUtiqService(storage: any, refreshUserIds: () => void, logPre logInfo(`${logPrefix}: frame found: `, Boolean(utiqFrame)); if (utiqFrame) { window.addEventListener('message', (event) => { - const {action, idGraphData, description} = event.data; + const { action, idGraphData, description } = event.data; if (action === 'returnIdGraphEntry' && description.moduleName === moduleName) { // Use the IDs received from the parent website if (event.origin !== window.origin) { diff --git a/libraries/vastTrackers/vastTrackers.js b/libraries/vastTrackers/vastTrackers.js index 7ab1650e9f9..0936ab02609 100644 --- a/libraries/vastTrackers/vastTrackers.js +++ b/libraries/vastTrackers/vastTrackers.js @@ -1,10 +1,10 @@ -import {callPrebidCache} from '../../src/auction.js'; -import {VIDEO} from '../../src/mediaTypes.js'; -import {logError} from '../../src/utils.js'; -import {isActivityAllowed} from '../../src/activities/rules.js'; -import {ACTIVITY_REPORT_ANALYTICS} from '../../src/activities/activities.js'; -import {activityParams} from '../../src/activities/activityParams.js'; -import {auctionManager} from '../../src/auctionManager.js'; +import { callPrebidCache } from '../../src/auction.js'; +import { VIDEO } from '../../src/mediaTypes.js'; +import { logError } from '../../src/utils.js'; +import { isActivityAllowed } from '../../src/activities/rules.js'; +import { ACTIVITY_REPORT_ANALYTICS } from '../../src/activities/activities.js'; +import { activityParams } from '../../src/activities/activityParams.js'; +import { auctionManager } from '../../src/auctionManager.js'; const vastTrackers = []; let enabled = false; @@ -22,15 +22,15 @@ export function enable() { export function disable() { if (enabled) { - callPrebidCache.getHooks({hook: addTrackersToResponse}).remove(); + callPrebidCache.getHooks({ hook: addTrackersToResponse }).remove(); enabled = false; } } -export function cacheVideoBidHook({index = auctionManager.index} = {}) { +export function cacheVideoBidHook({ index = auctionManager.index } = {}) { return function addTrackersToResponse(next, auctionInstance, bidResponse, afterBidAdded, videoMediaType) { if (FEATURES.VIDEO && bidResponse.mediaType === VIDEO) { - const vastTrackers = getVastTrackers(bidResponse, {index}); + const vastTrackers = getVastTrackers(bidResponse, { index }); if (vastTrackers) { bidResponse.vastXml = insertVastTrackers(vastTrackers, bidResponse.vastXml); const impTrackers = vastTrackers.get('impressions'); @@ -48,7 +48,7 @@ enable(); export function registerVastTrackers(moduleType, moduleName, trackerFn) { if (typeof trackerFn === 'function') { - vastTrackers.push({'moduleType': moduleType, 'moduleName': moduleName, 'trackerFn': trackerFn}); + vastTrackers.push({ 'moduleType': moduleType, 'moduleName': moduleName, 'trackerFn': trackerFn }); } } @@ -74,7 +74,7 @@ export function insertVastTrackers(trackers, vastXml) { return vastXml; } -export function getVastTrackers(bid, {index = auctionManager.index}) { +export function getVastTrackers(bid, { index = auctionManager.index }) { const trackers = []; vastTrackers.filter( ({ @@ -82,10 +82,10 @@ export function getVastTrackers(bid, {index = auctionManager.index}) { moduleName, trackerFn }) => isActivityAllowed(ACTIVITY_REPORT_ANALYTICS, activityParams(moduleType, moduleName)) - ).forEach(({trackerFn}) => { + ).forEach(({ trackerFn }) => { const auction = index.getAuction(bid).getProperties(); const bidRequest = index.getBidRequest(bid); - const trackersToAdd = trackerFn(bid, {auction, bidRequest}); + const trackersToAdd = trackerFn(bid, { auction, bidRequest }); trackersToAdd.forEach(trackerToAdd => { if (isValidVastTracker(trackers, trackerToAdd)) { trackers.push(trackerToAdd); @@ -101,7 +101,7 @@ function isValidVastTracker(trackers, trackerToAdd) { } function trackersToMap(trackers) { - return trackers.reduce((map, {url, event}) => { + return trackers.reduce((map, { url, event }) => { !map.has(event) && map.set(event, new Set()); map.get(event).add(url); return map; diff --git a/libraries/vidazooUtils/bidderUtils.js b/libraries/vidazooUtils/bidderUtils.js index fa2cea1b6fa..f7a4bd3cc7a 100644 --- a/libraries/vidazooUtils/bidderUtils.js +++ b/libraries/vidazooUtils/bidderUtils.js @@ -9,11 +9,11 @@ import { uniques, getWinDimensions } from '../../src/utils.js'; -import {chunk} from '../chunk/chunk.js'; -import {CURRENCY, DEAL_ID_EXPIRY, SESSION_ID_KEY, TTL_SECONDS, UNIQUE_DEAL_ID_EXPIRY} from './constants.js'; -import {bidderSettings} from '../../src/bidderSettings.js'; -import {config} from '../../src/config.js'; -import {BANNER, VIDEO} from '../../src/mediaTypes.js'; +import { chunk } from '../chunk/chunk.js'; +import { CURRENCY, DEAL_ID_EXPIRY, SESSION_ID_KEY, TTL_SECONDS, UNIQUE_DEAL_ID_EXPIRY } from './constants.js'; +import { bidderSettings } from '../../src/bidderSettings.js'; +import { config } from '../../src/config.js'; +import { BANNER, VIDEO } from '../../src/mediaTypes.js'; export function createSessionId() { return 'wsid_' + parseInt(Date.now() * Math.random()); @@ -21,7 +21,7 @@ export function createSessionId() { export function getTopWindowQueryParams() { try { - const parsedUrl = parseUrl(window.top.document.URL, {decodeSearchAsString: true}); + const parsedUrl = parseUrl(window.top.document.URL, { decodeSearchAsString: true }); return parsedUrl.search; } catch (e) { return ''; @@ -56,7 +56,7 @@ export function tryParseJSON(value) { export function setStorageItem(storage, key, value, timestamp) { try { const created = timestamp || Date.now(); - const data = JSON.stringify({value, created}); + const data = JSON.stringify({ value, created }); storage.setDataInLocalStorage(key, data); } catch (e) { } @@ -168,9 +168,9 @@ export function createUserSyncGetter(options = { }) { return function getUserSyncs(syncOptions, responses, gdprConsent = {}, uspConsent = '', gppConsent = {}) { const syncs = []; - const {iframeEnabled, pixelEnabled} = syncOptions; - const {gdprApplies, consentString = ''} = gdprConsent; - const {gppString, applicableSections} = gppConsent; + const { iframeEnabled, pixelEnabled } = syncOptions; + const { gdprApplies, consentString = '' } = gdprConsent; + const { gppString, applicableSections } = gppConsent; const coppa = config.getConfig('coppa') ? 1 : 0; const cidArr = responses.filter(resp => resp?.body?.cid).map(resp => resp.body.cid).filter(uniques); @@ -239,8 +239,8 @@ export function buildRequestData(bid, topWindowUrl, sizes, bidderRequest, bidder bidderRequestsCount, bidderWinsCount } = bid; - const {ext} = params; - let {bidFloor} = params; + const { ext } = params; + let { bidFloor } = params; const hashUrl = hashCode(topWindowUrl); const uniqueRequestData = isFn(getUniqueRequestData) ? getUniqueRequestData(hashUrl, bid) : {}; const uniqueDealId = getUniqueDealId(storage, hashUrl); @@ -339,13 +339,6 @@ export function buildRequestData(bid, topWindowUrl, sizes, bidderRequest, bidder data.gppSid = bidderRequest.ortb2.regs.gpp_sid; } - if (bidderRequest.paapi?.enabled) { - const fledge = bidderRequest?.ortb2Imp?.ext?.ae; - if (fledge) { - data.fledge = fledge; - } - } - const api = mediaTypes?.video?.api || []; if (api.includes(7)) { const sourceExt = bidderRequest?.ortb2?.source?.ext; @@ -392,7 +385,7 @@ export function createInterpretResponseFn(bidderCode, allowSingleRequest) { const singleRequestMode = allowSingleRequest && config.getConfig(`${bidderCode}.singleRequest`); const reqBidId = request?.data?.bidId; - const {results} = serverResponse.body; + const { results } = serverResponse.body; const output = []; @@ -465,7 +458,7 @@ export function createInterpretResponseFn(bidderCode, allowSingleRequest) { export function createBuildRequestsFn(createRequestDomain, createUniqueRequestData, storage, bidderCode, bidderVersion, allowSingleRequest) { function buildRequest(bid, topWindowUrl, sizes, bidderRequest, bidderTimeout) { - const {params} = bid; + const { params } = bid; const cId = extractCID(params); const subDomain = extractSubDomain(params); const data = buildRequestData(bid, topWindowUrl, sizes, bidderRequest, bidderTimeout, storage, bidderVersion, bidderCode, createUniqueRequestData); @@ -476,7 +469,7 @@ export function createBuildRequestsFn(createRequestDomain, createUniqueRequestDa } function buildSingleRequest(bidRequests, bidderRequest, topWindowUrl, bidderTimeout) { - const {params} = bidRequests[0]; + const { params } = bidRequests[0]; const cId = extractCID(params); const subDomain = extractSubDomain(params); const data = bidRequests.map(bid => { diff --git a/libraries/viewport/viewport.js b/libraries/viewport/viewport.js index 18c3818de00..77b4da35699 100644 --- a/libraries/viewport/viewport.js +++ b/libraries/viewport/viewport.js @@ -1,4 +1,4 @@ -import {getWinDimensions, getWindowTop} from '../../src/utils.js'; +import { getWinDimensions, getWindowTop } from '../../src/utils.js'; export function getViewportCoordinates() { try { diff --git a/libraries/weakStore/weakStore.js b/libraries/weakStore/weakStore.js index 09606354dae..30b862b76ec 100644 --- a/libraries/weakStore/weakStore.js +++ b/libraries/weakStore/weakStore.js @@ -1,4 +1,4 @@ -import {auctionManager} from '../../src/auctionManager.js'; +import { auctionManager } from '../../src/auctionManager.js'; export function weakStore(get) { const store = new WeakMap(); @@ -12,4 +12,4 @@ export function weakStore(get) { }; } -export const auctionStore = () => weakStore((auctionId) => auctionManager.index.getAuction({auctionId})); +export const auctionStore = () => weakStore((auctionId) => auctionManager.index.getAuction({ auctionId })); diff --git a/libraries/webdriver/webdriver.js b/libraries/webdriver/webdriver.js index 957fea62ed8..de53fb6bc8e 100644 --- a/libraries/webdriver/webdriver.js +++ b/libraries/webdriver/webdriver.js @@ -1,9 +1,49 @@ -import {canAccessWindowTop, internal as utilsInternals} from '../../src/utils.js'; +import { isFingerprintingApiDisabled } from '../fingerprinting/fingerprinting.js'; +import { getFallbackWindow } from '../../src/utils.js'; /** * Warning: accessing navigator.webdriver may impact fingerprinting scores when this API is included in the built script. + * @param {Window} [win] Window to check (defaults to top or self) + * @returns {boolean} */ -export function isWebdriverEnabled() { - const win = canAccessWindowTop() ? utilsInternals.getWindowTop() : utilsInternals.getWindowSelf(); - return win.navigator?.webdriver === true; +export function isWebdriverEnabled(win) { + if (isFingerprintingApiDisabled('webdriver')) { + return false; + } + return getFallbackWindow(win).navigator?.webdriver === true; +} + +/** + * Detects Selenium/WebDriver via document/window properties (e.g. __webdriver_script_fn, attributes). + * @param {Window} [win] Window to check + * @param {Document} [doc] Document to check (defaults to win.document) + * @returns {boolean} + */ +export function isSeleniumDetected(win, doc) { + if (isFingerprintingApiDisabled('webdriver')) { + return false; + } + const _win = win || (typeof window !== 'undefined' ? window : undefined); + const _doc = doc || (_win?.document); + if (!_win || !_doc) return false; + const checks = [ + 'webdriver' in _win, + '_Selenium_IDE_Recorder' in _win, + 'callSelenium' in _win, + '_selenium' in _win, + '__webdriver_script_fn' in _doc, + '__driver_evaluate' in _doc, + '__webdriver_evaluate' in _doc, + '__selenium_evaluate' in _doc, + '__fxdriver_evaluate' in _doc, + '__driver_unwrapped' in _doc, + '__webdriver_unwrapped' in _doc, + '__selenium_unwrapped' in _doc, + '__fxdriver_unwrapped' in _doc, + '__webdriver_script_func' in _doc, + _doc.documentElement?.getAttribute('selenium') !== null, + _doc.documentElement?.getAttribute('webdriver') !== null, + _doc.documentElement?.getAttribute('driver') !== null + ]; + return checks.some(Boolean); } diff --git a/libraries/xeUtils/bidderUtils.js b/libraries/xeUtils/bidderUtils.js index dbf9d79207d..91f5992921f 100644 --- a/libraries/xeUtils/bidderUtils.js +++ b/libraries/xeUtils/bidderUtils.js @@ -1,5 +1,5 @@ -import {deepAccess, getBidIdParameter, isFn, logError, isArray, parseSizesInput, isPlainObject} from '../../src/utils.js'; -import {getAdUnitSizes} from '../sizeUtils/sizeUtils.js'; +import { deepAccess, getBidIdParameter, isFn, logError, isArray, parseSizesInput, isPlainObject } from '../../src/utils.js'; +import { getAdUnitSizes } from '../sizeUtils/sizeUtils.js'; export function getBidFloor(bid, currency = 'USD') { if (!isFn(bid.getFloor)) { @@ -41,7 +41,7 @@ export function isBidRequestValid(bid, requiredParams = ['pid', 'env']) { } export function buildRequests(validBidRequests, bidderRequest, endpoint) { - const {refererInfo = {}, gdprConsent = {}, uspConsent} = bidderRequest; + const { refererInfo = {}, gdprConsent = {}, uspConsent } = bidderRequest; const requests = validBidRequests.map(req => { const request = {}; request.tmax = bidderRequest.timeout || 0; @@ -108,7 +108,7 @@ export function buildRequests(validBidRequests, bidderRequest, endpoint) { }; } -export function interpretResponse(serverResponse, {bidderRequest}) { +export function interpretResponse(serverResponse, { bidderRequest }) { const response = []; if (!isArray(deepAccess(serverResponse, 'body.data'))) { return response; @@ -143,7 +143,7 @@ export function getUserSyncs(syncOptions, serverResponses, gdprConsent = {}, usp pixels.forEach(pixel => { const [type, url] = pixel; - const sync = {type, url: `${url}&${usPrivacy}${gdprFlag}${gdprString}`}; + const sync = { type, url: `${url}&${usPrivacy}${gdprFlag}${gdprString}` }; if (type === 'iframe' && syncOptions.iframeEnabled) { syncs.push(sync) } else if (type === 'image' && syncOptions.pixelEnabled) { diff --git a/metadata/core.json b/metadata/core.json index 1e43a89e586..faacd3ceed4 100644 --- a/metadata/core.json +++ b/metadata/core.json @@ -11,6 +11,12 @@ "moduleName": "prebid-core", "disclosureURL": "local://prebid/probes.json" }, + { + "componentType": "prebid", + "componentName": "storage", + "moduleName": "prebid-core", + "disclosureURL": "local://prebid/probes.json" + }, { "componentType": "prebid", "componentName": "debugging", diff --git a/metadata/disclosures/prebid/categoryTranslation.json b/metadata/disclosures/prebid/categoryTranslation.json deleted file mode 100644 index 82934ef440e..00000000000 --- a/metadata/disclosures/prebid/categoryTranslation.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "disclosures": [ - { - "identifier": "iabToFwMappingkey", - "type": "web", - "domains": ["*"], - "purposes": [ - 1 - ] - }, - { - "identifier": "iabToFwMappingkeyPub", - "type": "web", - "domains": ["*"], - "purposes": [ - 1 - ] - } - ], - "domains": [ - { - "domain": "*", - "use": "Category translation mappings are cached in localStorage" - } - ] -} diff --git a/metadata/disclosures/prebid/probes.json b/metadata/disclosures/prebid/probes.json index c371cef1d4e..16cc5dec160 100644 --- a/metadata/disclosures/prebid/probes.json +++ b/metadata/disclosures/prebid/probes.json @@ -22,7 +22,7 @@ "domains": [ { "domain": "*", - "use": "Temporary 'probing' cookies are written (and deleted) to determine the top-level domain; likewise, probes are temporarily written to local and sessionStorage to determine their availability" + "use": "Temporary 'probing' cookies are written (and deleted) to determine the top-level domain and availability of cookies; likewise, probes are temporarily written to local and sessionStorage to determine their availability" } ] } diff --git a/metadata/modules.json b/metadata/modules.json index 6bba23bd092..45362e4421e 100644 --- a/metadata/modules.json +++ b/metadata/modules.json @@ -43,6 +43,13 @@ "gvlid": null, "disclosureURL": null }, + { + "componentType": "bidder", + "componentName": "aceex", + "aliasOf": null, + "gvlid": 1387, + "disclosureURL": null + }, { "componentType": "bidder", "componentName": "acuityads", @@ -106,6 +113,13 @@ "gvlid": null, "disclosureURL": null }, + { + "componentType": "bidder", + "componentName": "adcluster", + "aliasOf": null, + "gvlid": null, + "disclosureURL": null + }, { "componentType": "bidder", "componentName": "addefend", @@ -519,6 +533,13 @@ "gvlid": 1283, "disclosureURL": null }, + { + "componentType": "bidder", + "componentName": "intlscoop", + "aliasOf": "adkernel", + "gvlid": null, + "disclosureURL": null + }, { "componentType": "bidder", "componentName": "admaru", @@ -568,6 +589,13 @@ "gvlid": 779, "disclosureURL": null }, + { + "componentType": "bidder", + "componentName": "adrubi", + "aliasOf": "admatic", + "gvlid": 779, + "disclosureURL": null + }, { "componentType": "bidder", "componentName": "yobee", @@ -638,6 +666,13 @@ "gvlid": null, "disclosureURL": null }, + { + "componentType": "bidder", + "componentName": "adnimation", + "aliasOf": null, + "gvlid": null, + "disclosureURL": null + }, { "componentType": "bidder", "componentName": "adnow", @@ -691,7 +726,7 @@ "componentType": "bidder", "componentName": "adocean", "aliasOf": null, - "gvlid": null, + "gvlid": 328, "disclosureURL": null }, { @@ -1163,6 +1198,13 @@ "gvlid": null, "disclosureURL": null }, + { + "componentType": "bidder", + "componentName": "apester", + "aliasOf": null, + "gvlid": 354, + "disclosureURL": null + }, { "componentType": "bidder", "componentName": "appStockSSP", @@ -1212,13 +1254,6 @@ "gvlid": 32, "disclosureURL": null }, - { - "componentType": "bidder", - "componentName": "emetriq", - "aliasOf": "appnexus", - "gvlid": 213, - "disclosureURL": null - }, { "componentType": "bidder", "componentName": "pagescience", @@ -2108,6 +2143,13 @@ "gvlid": null, "disclosureURL": null }, + { + "componentType": "bidder", + "componentName": "dpai", + "aliasOf": null, + "gvlid": null, + "disclosureURL": null + }, { "componentType": "bidder", "componentName": "driftpixel", @@ -2297,6 +2339,13 @@ "gvlid": null, "disclosureURL": null }, + { + "componentType": "bidder", + "componentName": "floxis", + "aliasOf": null, + "gvlid": null, + "disclosureURL": null + }, { "componentType": "bidder", "componentName": "fluct", @@ -2486,6 +2535,13 @@ "gvlid": null, "disclosureURL": null }, + { + "componentType": "bidder", + "componentName": "harion", + "aliasOf": null, + "gvlid": 1406, + "disclosureURL": null + }, { "componentType": "bidder", "componentName": "holid", @@ -2591,6 +2647,13 @@ "gvlid": 910, "disclosureURL": null }, + { + "componentType": "bidder", + "componentName": "insurads", + "aliasOf": null, + "gvlid": 596, + "disclosureURL": null + }, { "componentType": "bidder", "componentName": "integr8", @@ -2745,6 +2808,13 @@ "gvlid": null, "disclosureURL": null }, + { + "componentType": "bidder", + "componentName": "leagueM", + "aliasOf": null, + "gvlid": null, + "disclosureURL": null + }, { "componentType": "bidder", "componentName": "lemmadigital", @@ -2787,13 +2857,6 @@ "gvlid": 1358, "disclosureURL": null }, - { - "componentType": "bidder", - "componentName": "apester", - "aliasOf": "limelightDigital", - "gvlid": null, - "disclosureURL": null - }, { "componentType": "bidder", "componentName": "adsyield", @@ -2859,42 +2922,42 @@ }, { "componentType": "bidder", - "componentName": "adnimation", + "componentName": "rtbdemand", "aliasOf": "limelightDigital", "gvlid": null, "disclosureURL": null }, { "componentType": "bidder", - "componentName": "rtbdemand", + "componentName": "altstar", "aliasOf": "limelightDigital", "gvlid": null, "disclosureURL": null }, { "componentType": "bidder", - "componentName": "altstar", + "componentName": "vaayaMedia", "aliasOf": "limelightDigital", "gvlid": null, "disclosureURL": null }, { "componentType": "bidder", - "componentName": "vaayaMedia", + "componentName": "performist", "aliasOf": "limelightDigital", "gvlid": null, "disclosureURL": null }, { "componentType": "bidder", - "componentName": "performist", + "componentName": "oveeo", "aliasOf": "limelightDigital", "gvlid": null, "disclosureURL": null }, { "componentType": "bidder", - "componentName": "oveeo", + "componentName": "embimedia", "aliasOf": "limelightDigital", "gvlid": null, "disclosureURL": null @@ -3011,6 +3074,13 @@ "gvlid": 153, "disclosureURL": null }, + { + "componentType": "bidder", + "componentName": "magnite", + "aliasOf": null, + "gvlid": 52, + "disclosureURL": null + }, { "componentType": "bidder", "componentName": "malltv", @@ -3165,6 +3235,13 @@ "gvlid": null, "disclosureURL": null }, + { + "componentType": "bidder", + "componentName": "mile", + "aliasOf": null, + "gvlid": null, + "disclosureURL": null + }, { "componentType": "bidder", "componentName": "minutemedia", @@ -3543,13 +3620,6 @@ "gvlid": null, "disclosureURL": null }, - { - "componentType": "bidder", - "componentName": "optable", - "aliasOf": null, - "gvlid": null, - "disclosureURL": null - }, { "componentType": "bidder", "componentName": "optidigital", @@ -3648,6 +3718,13 @@ "gvlid": null, "disclosureURL": null }, + { + "componentType": "bidder", + "componentName": "panxo", + "aliasOf": null, + "gvlid": 1527, + "disclosureURL": null + }, { "componentType": "bidder", "componentName": "performax", @@ -3802,6 +3879,20 @@ "gvlid": null, "disclosureURL": null }, + { + "componentType": "bidder", + "componentName": "pubstack", + "aliasOf": null, + "gvlid": 1408, + "disclosureURL": null + }, + { + "componentType": "bidder", + "componentName": "pubstack_server", + "aliasOf": "pubstack", + "gvlid": 1408, + "disclosureURL": null + }, { "componentType": "bidder", "componentName": "pubx", @@ -3865,13 +3956,6 @@ "gvlid": null, "disclosureURL": null }, - { - "componentType": "bidder", - "componentName": "quantcast", - "aliasOf": null, - "gvlid": "11", - "disclosureURL": null - }, { "componentType": "bidder", "componentName": "qwarry", @@ -3970,6 +4054,13 @@ "gvlid": null, "disclosureURL": null }, + { + "componentType": "bidder", + "componentName": "revantage", + "aliasOf": null, + "gvlid": null, + "disclosureURL": null + }, { "componentType": "bidder", "componentName": "revcontent", @@ -4579,6 +4670,13 @@ "gvlid": null, "disclosureURL": null }, + { + "componentType": "bidder", + "componentName": "teqBlazeSalesAgent", + "aliasOf": null, + "gvlid": null, + "disclosureURL": null + }, { "componentType": "bidder", "componentName": "theadx", @@ -4747,6 +4845,13 @@ "gvlid": null, "disclosureURL": null }, + { + "componentType": "bidder", + "componentName": "verben", + "aliasOf": null, + "gvlid": null, + "disclosureURL": null + }, { "componentType": "bidder", "componentName": "viant", @@ -4985,6 +5090,13 @@ "gvlid": 25, "disclosureURL": null }, + { + "componentType": "bidder", + "componentName": "yaleo", + "aliasOf": null, + "gvlid": 783, + "disclosureURL": null + }, { "componentType": "bidder", "componentName": "yandex", @@ -5274,12 +5386,6 @@ "gvlid": null, "disclosureURL": null }, - { - "componentType": "rtd", - "componentName": "intersection", - "gvlid": null, - "disclosureURL": null - }, { "componentType": "rtd", "componentName": "jwplayer", @@ -5364,6 +5470,12 @@ "gvlid": null, "disclosureURL": null }, + { + "componentType": "rtd", + "componentName": "panxo", + "gvlid": null, + "disclosureURL": null + }, { "componentType": "rtd", "componentName": "permutive", @@ -5545,13 +5657,6 @@ "disclosureURL": null, "aliasOf": null }, - { - "componentType": "userId", - "componentName": "dmdId", - "gvlid": null, - "disclosureURL": null, - "aliasOf": null - }, { "componentType": "userId", "componentName": "euid", @@ -5678,6 +5783,20 @@ "disclosureURL": null, "aliasOf": null }, + { + "componentType": "userId", + "componentName": "locId", + "gvlid": null, + "disclosureURL": null, + "aliasOf": null + }, + { + "componentType": "userId", + "componentName": "locid", + "gvlid": null, + "disclosureURL": null, + "aliasOf": "locId" + }, { "componentType": "userId", "componentName": "lockrAIMId", @@ -5797,13 +5916,6 @@ "disclosureURL": null, "aliasOf": null }, - { - "componentType": "userId", - "componentName": "quantcastId", - "gvlid": "11", - "disclosureURL": null, - "aliasOf": null - }, { "componentType": "userId", "componentName": "rewardedInterestId", diff --git a/metadata/modules/33acrossBidAdapter.json b/metadata/modules/33acrossBidAdapter.json index 28bc19ea084..823551fb1f5 100644 --- a/metadata/modules/33acrossBidAdapter.json +++ b/metadata/modules/33acrossBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://platform.33across.com/disclosures.json": { - "timestamp": "2026-01-21T15:16:22.814Z", + "timestamp": "2026-03-14T22:01:05.187Z", "disclosures": [] } }, diff --git a/metadata/modules/33acrossIdSystem.json b/metadata/modules/33acrossIdSystem.json index 6c4fc8ee6fa..2c4f869236b 100644 --- a/metadata/modules/33acrossIdSystem.json +++ b/metadata/modules/33acrossIdSystem.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://platform.33across.com/disclosures.json": { - "timestamp": "2026-01-21T15:16:22.912Z", + "timestamp": "2026-03-14T22:01:05.286Z", "disclosures": [] } }, diff --git a/metadata/modules/aceexBidAdapter.json b/metadata/modules/aceexBidAdapter.json new file mode 100644 index 00000000000..5e843cac764 --- /dev/null +++ b/metadata/modules/aceexBidAdapter.json @@ -0,0 +1,18 @@ +{ + "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", + "disclosures": { + "https://aceex.io/tcf.json": { + "timestamp": "2026-03-14T22:01:05.288Z", + "disclosures": [] + } + }, + "components": [ + { + "componentType": "bidder", + "componentName": "aceex", + "aliasOf": null, + "gvlid": 1387, + "disclosureURL": "https://aceex.io/tcf.json" + } + ] +} \ No newline at end of file diff --git a/metadata/modules/acuityadsBidAdapter.json b/metadata/modules/acuityadsBidAdapter.json index 6f890a405b2..0aaec8d2cf8 100644 --- a/metadata/modules/acuityadsBidAdapter.json +++ b/metadata/modules/acuityadsBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://privacy.acuityads.com/deviceStorageDisclosure.json": { - "timestamp": "2026-01-21T15:16:22.914Z", + "timestamp": "2026-03-14T22:01:05.335Z", "disclosures": [] } }, diff --git a/metadata/modules/adagioBidAdapter.json b/metadata/modules/adagioBidAdapter.json index 31087ee5c76..5281067bde6 100644 --- a/metadata/modules/adagioBidAdapter.json +++ b/metadata/modules/adagioBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://adagio.io/deviceStorageDisclosure.json": { - "timestamp": "2026-01-21T15:16:22.947Z", + "timestamp": "2026-03-14T22:01:05.367Z", "disclosures": [] } }, diff --git a/metadata/modules/adagioRtdProvider.json b/metadata/modules/adagioRtdProvider.json index 60dc0446c5d..d0a8381c614 100644 --- a/metadata/modules/adagioRtdProvider.json +++ b/metadata/modules/adagioRtdProvider.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://adagio.io/deviceStorageDisclosure.json": { - "timestamp": "2026-01-21T15:16:23.014Z", + "timestamp": "2026-03-14T22:01:05.416Z", "disclosures": [] } }, diff --git a/metadata/modules/adbroBidAdapter.json b/metadata/modules/adbroBidAdapter.json index 7eb1502f89b..f1f21fc2a1f 100644 --- a/metadata/modules/adbroBidAdapter.json +++ b/metadata/modules/adbroBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://tag.adbro.me/privacy/devicestorage.json": { - "timestamp": "2026-01-21T15:16:23.014Z", + "timestamp": "2026-03-14T22:01:05.416Z", "disclosures": [] } }, diff --git a/metadata/modules/ringieraxelspringerBidAdapter.json b/metadata/modules/adclusterBidAdapter.json similarity index 55% rename from metadata/modules/ringieraxelspringerBidAdapter.json rename to metadata/modules/adclusterBidAdapter.json index 1ca1c72523f..72d44231bb6 100644 --- a/metadata/modules/ringieraxelspringerBidAdapter.json +++ b/metadata/modules/adclusterBidAdapter.json @@ -4,17 +4,10 @@ "components": [ { "componentType": "bidder", - "componentName": "das", + "componentName": "adcluster", "aliasOf": null, "gvlid": null, "disclosureURL": null - }, - { - "componentType": "bidder", - "componentName": "ringieraxelspringer", - "aliasOf": "das", - "gvlid": null, - "disclosureURL": null } ] } \ No newline at end of file diff --git a/metadata/modules/addefendBidAdapter.json b/metadata/modules/addefendBidAdapter.json index 201cb7effbd..1008ff63422 100644 --- a/metadata/modules/addefendBidAdapter.json +++ b/metadata/modules/addefendBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://www.addefend.com/deviceStorage.json": { - "timestamp": "2026-01-21T15:16:23.303Z", + "timestamp": "2026-03-14T22:01:06.169Z", "disclosures": [] } }, diff --git a/metadata/modules/adfBidAdapter.json b/metadata/modules/adfBidAdapter.json index 5aef107fb2f..2b0cbc54873 100644 --- a/metadata/modules/adfBidAdapter.json +++ b/metadata/modules/adfBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://site.adform.com/assets/devicestorage.json": { - "timestamp": "2026-01-21T15:16:24.107Z", + "timestamp": "2026-03-14T22:01:06.841Z", "disclosures": [] } }, diff --git a/metadata/modules/adfusionBidAdapter.json b/metadata/modules/adfusionBidAdapter.json index a838f9f9567..2a22045409a 100644 --- a/metadata/modules/adfusionBidAdapter.json +++ b/metadata/modules/adfusionBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://spicyrtb.com/static/iab-disclosure.json": { - "timestamp": "2026-01-21T15:16:24.108Z", + "timestamp": "2026-03-14T22:01:06.841Z", "disclosures": [] } }, diff --git a/metadata/modules/adheseBidAdapter.json b/metadata/modules/adheseBidAdapter.json index 8f83e1080a0..4850025b7c1 100644 --- a/metadata/modules/adheseBidAdapter.json +++ b/metadata/modules/adheseBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://adhese.com/deviceStorage.json": { - "timestamp": "2026-01-21T15:16:24.474Z", + "timestamp": "2026-03-14T22:01:07.200Z", "disclosures": [] } }, diff --git a/metadata/modules/adipoloBidAdapter.json b/metadata/modules/adipoloBidAdapter.json index 9c96cfb8a1c..e3c0253bd0a 100644 --- a/metadata/modules/adipoloBidAdapter.json +++ b/metadata/modules/adipoloBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://adipolo.com/device_storage_disclosure.json": { - "timestamp": "2026-01-21T15:16:24.737Z", + "timestamp": "2026-03-14T22:01:07.460Z", "disclosures": [] } }, diff --git a/metadata/modules/adkernelAdnBidAdapter.json b/metadata/modules/adkernelAdnBidAdapter.json index c2443f34504..d4eff406316 100644 --- a/metadata/modules/adkernelAdnBidAdapter.json +++ b/metadata/modules/adkernelAdnBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://static.adkernel.com/deviceStorage.json": { - "timestamp": "2026-01-21T15:16:24.892Z", + "timestamp": "2026-03-14T22:01:07.594Z", "disclosures": [ { "identifier": "adk_rtb_conv_id", diff --git a/metadata/modules/adkernelBidAdapter.json b/metadata/modules/adkernelBidAdapter.json index 533adb5b635..9380f7b78fe 100644 --- a/metadata/modules/adkernelBidAdapter.json +++ b/metadata/modules/adkernelBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://static.adkernel.com/deviceStorage.json": { - "timestamp": "2026-01-21T15:16:24.933Z", + "timestamp": "2026-03-14T22:01:07.640Z", "disclosures": [ { "identifier": "adk_rtb_conv_id", @@ -17,19 +17,19 @@ ] }, "https://data.converge-digital.com/deviceStorage.json": { - "timestamp": "2026-01-21T15:16:24.933Z", + "timestamp": "2026-03-14T22:01:07.640Z", "disclosures": [] }, "https://spinx.biz/tcf-spinx.json": { - "timestamp": "2026-01-21T15:16:24.979Z", + "timestamp": "2026-03-14T22:01:07.684Z", "disclosures": [] }, "https://gdpr.memob.com/deviceStorage.json": { - "timestamp": "2026-01-21T15:16:25.740Z", + "timestamp": "2026-03-14T22:01:08.426Z", "disclosures": [] }, "https://appmonsta.ai/DeviceStorageDisclosure.json": { - "timestamp": "2026-01-21T15:16:25.757Z", + "timestamp": "2026-03-14T22:01:08.449Z", "disclosures": [] } }, @@ -348,6 +348,13 @@ "aliasOf": "adkernel", "gvlid": 1283, "disclosureURL": "https://appmonsta.ai/DeviceStorageDisclosure.json" + }, + { + "componentType": "bidder", + "componentName": "intlscoop", + "aliasOf": "adkernel", + "gvlid": null, + "disclosureURL": null } ] } \ No newline at end of file diff --git a/metadata/modules/admaticBidAdapter.json b/metadata/modules/admaticBidAdapter.json index fb5475a18cb..8e3f8af8ef7 100644 --- a/metadata/modules/admaticBidAdapter.json +++ b/metadata/modules/admaticBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://static.admatic.de/iab-europe/tcfv2/disclosure.json": { - "timestamp": "2026-01-21T15:16:27.252Z", + "timestamp": "2026-03-14T22:01:08.751Z", "disclosures": [ { "identifier": "px_pbjs", @@ -12,7 +12,7 @@ ] }, "https://adtarget.com.tr/.well-known/deviceStorage.json": { - "timestamp": "2026-01-21T15:16:27.092Z", + "timestamp": "2026-03-14T22:01:08.751Z", "disclosures": [ { "identifier": "adt_pbjs", @@ -65,6 +65,13 @@ "gvlid": 779, "disclosureURL": "https://adtarget.com.tr/.well-known/deviceStorage.json" }, + { + "componentType": "bidder", + "componentName": "adrubi", + "aliasOf": "admatic", + "gvlid": 779, + "disclosureURL": "https://adtarget.com.tr/.well-known/deviceStorage.json" + }, { "componentType": "bidder", "componentName": "yobee", diff --git a/metadata/modules/admixerBidAdapter.json b/metadata/modules/admixerBidAdapter.json index 0bac310b608..04359e7862e 100644 --- a/metadata/modules/admixerBidAdapter.json +++ b/metadata/modules/admixerBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://admixer.com/tcf.json": { - "timestamp": "2026-01-21T15:16:27.253Z", + "timestamp": "2026-03-14T22:01:08.752Z", "disclosures": [] } }, diff --git a/metadata/modules/admixerIdSystem.json b/metadata/modules/admixerIdSystem.json index ff55aa04384..901e303a8b7 100644 --- a/metadata/modules/admixerIdSystem.json +++ b/metadata/modules/admixerIdSystem.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://admixer.com/tcf.json": { - "timestamp": "2026-01-21T15:16:27.746Z", + "timestamp": "2026-03-14T22:01:09.129Z", "disclosures": [] } }, diff --git a/metadata/modules/adnimationBidAdapter.json b/metadata/modules/adnimationBidAdapter.json new file mode 100644 index 00000000000..7bb7fce194e --- /dev/null +++ b/metadata/modules/adnimationBidAdapter.json @@ -0,0 +1,13 @@ +{ + "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", + "disclosures": {}, + "components": [ + { + "componentType": "bidder", + "componentName": "adnimation", + "aliasOf": null, + "gvlid": null, + "disclosureURL": null + } + ] +} \ No newline at end of file diff --git a/metadata/modules/adnowBidAdapter.json b/metadata/modules/adnowBidAdapter.json index b7cdcbe11c1..0d5b8e7a5da 100644 --- a/metadata/modules/adnowBidAdapter.json +++ b/metadata/modules/adnowBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://adnow.com/vdsod.json": { - "timestamp": "2026-01-21T15:16:27.746Z", + "timestamp": "2026-03-14T22:01:09.130Z", "disclosures": [ { "identifier": "SC_unique_*", diff --git a/metadata/modules/adnuntiusBidAdapter.json b/metadata/modules/adnuntiusBidAdapter.json index 9978243ca77..7853de97ee5 100644 --- a/metadata/modules/adnuntiusBidAdapter.json +++ b/metadata/modules/adnuntiusBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://delivery.adnuntius.com/.well-known/deviceStorage.json": { - "timestamp": "2026-01-21T15:16:27.974Z", + "timestamp": "2026-03-14T22:01:09.365Z", "disclosures": [ { "identifier": "adn.metaData", diff --git a/metadata/modules/adnuntiusRtdProvider.json b/metadata/modules/adnuntiusRtdProvider.json index 6b28926eab7..325673348e5 100644 --- a/metadata/modules/adnuntiusRtdProvider.json +++ b/metadata/modules/adnuntiusRtdProvider.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://delivery.adnuntius.com/.well-known/deviceStorage.json": { - "timestamp": "2026-01-21T15:16:28.288Z", + "timestamp": "2026-03-14T22:01:09.679Z", "disclosures": [ { "identifier": "adn.metaData", diff --git a/metadata/modules/adoceanBidAdapter.json b/metadata/modules/adoceanBidAdapter.json index 9743e913146..cf3f853a8cf 100644 --- a/metadata/modules/adoceanBidAdapter.json +++ b/metadata/modules/adoceanBidAdapter.json @@ -1,13 +1,280 @@ { "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", - "disclosures": {}, + "disclosures": { + "https://gemius.com/media/documents/Gemius_SA_Vendor_Device_Storage.json": { + "timestamp": "2026-03-14T22:01:09.680Z", + "disclosures": [ + { + "identifier": "__gsyncs_gdpr", + "type": "cookie", + "maxAgeSeconds": 157680000, + "cookieRefresh": false, + "purposes": [ + 1 + ] + }, + { + "identifier": "__gsync_gdpr", + "type": "cookie", + "maxAgeSeconds": 157680000, + "cookieRefresh": true, + "purposes": [ + 1 + ] + }, + { + "identifier": "__gsync_s_gdpr", + "type": "cookie", + "maxAgeSeconds": 157680000, + "cookieRefresh": true, + "purposes": [ + 1 + ] + }, + { + "identifier": "__gfp_cap", + "type": "cookie", + "maxAgeSeconds": 34128000, + "cookieRefresh": true, + "purposes": [ + 1, + 7, + 8, + 9, + 10 + ] + }, + { + "identifier": "__gfp_s_cap", + "type": "cookie", + "maxAgeSeconds": 34128000, + "cookieRefresh": true, + "purposes": [ + 1, + 7, + 8, + 9, + 10 + ] + }, + { + "identifier": "__gfp_cache", + "type": "cookie", + "maxAgeSeconds": 34128000, + "cookieRefresh": true, + "purposes": [ + 1, + 7, + 8, + 9, + 10 + ] + }, + { + "identifier": "__gfp_s_cache", + "type": "cookie", + "maxAgeSeconds": 34128000, + "cookieRefresh": true, + "purposes": [ + 1, + 7, + 8, + 9, + 10 + ] + }, + { + "identifier": "__gfp_64b", + "type": "cookie", + "maxAgeSeconds": 34128000, + "cookieRefresh": true, + "purposes": [ + 1, + 7, + 8, + 9, + 10 + ] + }, + { + "identifier": "__gfp_s_64b", + "type": "cookie", + "maxAgeSeconds": 34128000, + "cookieRefresh": true, + "purposes": [ + 1, + 7, + 8, + 9, + 10 + ] + }, + { + "identifier": "__gfps_64b", + "type": "cookie", + "maxAgeSeconds": 34128000, + "cookieRefresh": true, + "purposes": [ + 1, + 7, + 8, + 9, + 10 + ] + }, + { + "identifier": "gemius_ruid", + "type": "web", + "maxAgeSeconds": null, + "cookieRefresh": null, + "purposes": [ + 1, + 7, + 8, + 9, + 10 + ] + }, + { + "identifier": "__gfp_dnt", + "type": "cookie", + "maxAgeSeconds": 157680000, + "cookieRefresh": true, + "purposes": [ + 1 + ], + "optOut": true + }, + { + "identifier": "__gfp_s_dnt", + "type": "cookie", + "maxAgeSeconds": 157680000, + "cookieRefresh": true, + "purposes": [ + 1 + ], + "optOut": true + }, + { + "identifier": "__gfp_ruid", + "type": "cookie", + "maxAgeSeconds": 34128000, + "cookieRefresh": true, + "purposes": [ + 1, + 7, + 8, + 9, + 10 + ] + }, + { + "identifier": "__gfp_s_ruid", + "type": "cookie", + "maxAgeSeconds": 34128000, + "cookieRefresh": true, + "purposes": [ + 1, + 7, + 8, + 9, + 10 + ] + }, + { + "identifier": "__gfp_ruid_pub", + "type": "cookie", + "maxAgeSeconds": 34128000, + "cookieRefresh": true, + "purposes": [ + 1, + 7, + 8, + 9, + 10 + ] + }, + { + "identifier": "__gfp_s_ruid_pub", + "type": "cookie", + "maxAgeSeconds": 34128000, + "cookieRefresh": true, + "purposes": [ + 1, + 7, + 8, + 9, + 10 + ] + }, + { + "identifier": "__gfp_gdpr", + "type": "cookie", + "maxAgeSeconds": 157680000, + "cookieRefresh": false, + "purposes": [ + 1 + ] + }, + { + "identifier": "__gfp_s_gdpr", + "type": "cookie", + "maxAgeSeconds": 157680000, + "cookieRefresh": false, + "purposes": [ + 1 + ] + }, + { + "identifier": "ao-fpgad", + "type": "cookie", + "maxAgeSeconds": 33696000, + "cookieRefresh": true, + "purposes": [ + 1, + 2, + 3, + 4, + 7, + 8, + 9, + 10 + ] + }, + { + "identifier": "AO-OPT-OUT", + "type": "cookie", + "maxAgeSeconds": 155520000, + "cookieRefresh": false, + "purposes": [ + 1 + ], + "optOut": true + }, + { + "identifier": "_ao_consent_data", + "type": "web", + "maxAgeSeconds": null, + "cookieRefresh": null, + "purposes": [] + }, + { + "identifier": "_ao_chints", + "type": "web", + "maxAgeSeconds": null, + "cookieRefresh": null, + "purposes": [] + } + ] + } + }, "components": [ { "componentType": "bidder", "componentName": "adocean", "aliasOf": null, - "gvlid": null, - "disclosureURL": null + "gvlid": 328, + "disclosureURL": "https://gemius.com/media/documents/Gemius_SA_Vendor_Device_Storage.json" } ] } \ No newline at end of file diff --git a/metadata/modules/adotBidAdapter.json b/metadata/modules/adotBidAdapter.json index 76a8d448590..16d18a7e5ee 100644 --- a/metadata/modules/adotBidAdapter.json +++ b/metadata/modules/adotBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://assets.adotmob.com/tcf/tcf.json": { - "timestamp": "2026-01-21T15:16:28.289Z", + "timestamp": "2026-03-14T22:01:10.148Z", "disclosures": [] } }, diff --git a/metadata/modules/adponeBidAdapter.json b/metadata/modules/adponeBidAdapter.json index 562e9b3191b..1c2087b3fd5 100644 --- a/metadata/modules/adponeBidAdapter.json +++ b/metadata/modules/adponeBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://adserver.adpone.com/deviceStorage.json": { - "timestamp": "2026-01-21T15:16:28.330Z", + "timestamp": "2026-03-14T22:01:10.184Z", "disclosures": [] } }, diff --git a/metadata/modules/adqueryBidAdapter.json b/metadata/modules/adqueryBidAdapter.json index 4a4c5e0437e..a0e90191b29 100644 --- a/metadata/modules/adqueryBidAdapter.json +++ b/metadata/modules/adqueryBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://api.adquery.io/tcf/adQuery.json": { - "timestamp": "2026-01-21T15:16:28.350Z", + "timestamp": "2026-03-14T22:01:10.212Z", "disclosures": [] } }, diff --git a/metadata/modules/adqueryIdSystem.json b/metadata/modules/adqueryIdSystem.json index c3c47ba2f4d..927f651bb9c 100644 --- a/metadata/modules/adqueryIdSystem.json +++ b/metadata/modules/adqueryIdSystem.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://api.adquery.io/tcf/adQuery.json": { - "timestamp": "2026-01-21T15:16:28.687Z", + "timestamp": "2026-03-14T22:01:10.553Z", "disclosures": [] } }, diff --git a/metadata/modules/adrinoBidAdapter.json b/metadata/modules/adrinoBidAdapter.json index d9be264c1a8..be0fbf1c17b 100644 --- a/metadata/modules/adrinoBidAdapter.json +++ b/metadata/modules/adrinoBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://cdn.adrino.cloud/iab/device-storage.json": { - "timestamp": "2026-01-21T15:16:28.688Z", + "timestamp": "2026-03-14T22:01:10.553Z", "disclosures": [] } }, diff --git a/metadata/modules/ads_interactiveBidAdapter.json b/metadata/modules/ads_interactiveBidAdapter.json index 27972eafcba..5770a1dbd48 100644 --- a/metadata/modules/ads_interactiveBidAdapter.json +++ b/metadata/modules/ads_interactiveBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://adsinteractive.com/vendor.json": { - "timestamp": "2026-01-21T15:16:28.779Z", + "timestamp": "2026-03-14T22:01:10.635Z", "disclosures": [] } }, diff --git a/metadata/modules/adtargetBidAdapter.json b/metadata/modules/adtargetBidAdapter.json index ba378474003..9d0b6c4de36 100644 --- a/metadata/modules/adtargetBidAdapter.json +++ b/metadata/modules/adtargetBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://adtarget.com.tr/.well-known/deviceStorage.json": { - "timestamp": "2026-01-21T15:16:29.070Z", + "timestamp": "2026-03-14T22:01:10.923Z", "disclosures": [ { "identifier": "adt_pbjs", diff --git a/metadata/modules/adtelligentBidAdapter.json b/metadata/modules/adtelligentBidAdapter.json index f88d4766a3d..e6ddfa5f318 100644 --- a/metadata/modules/adtelligentBidAdapter.json +++ b/metadata/modules/adtelligentBidAdapter.json @@ -2,11 +2,11 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://adtelligent.com/.well-known/deviceStorage.json": { - "timestamp": "2026-01-21T15:16:29.071Z", + "timestamp": "2026-03-14T22:01:10.924Z", "disclosures": [] }, "https://www.selectmedia.asia/gdpr/devicestorage.json": { - "timestamp": "2026-01-21T15:16:29.090Z", + "timestamp": "2026-03-14T22:01:10.941Z", "disclosures": [ { "identifier": "waterFallCacheAnsKey_*", @@ -81,7 +81,7 @@ ] }, "https://orangeclickmedia.com/device_storage_disclosure.json": { - "timestamp": "2026-01-21T15:16:29.271Z", + "timestamp": "2026-03-14T22:01:11.077Z", "disclosures": [] } }, diff --git a/metadata/modules/adtelligentIdSystem.json b/metadata/modules/adtelligentIdSystem.json index dff1fc84910..483d850de86 100644 --- a/metadata/modules/adtelligentIdSystem.json +++ b/metadata/modules/adtelligentIdSystem.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://adtelligent.com/.well-known/deviceStorage.json": { - "timestamp": "2026-01-21T15:16:29.346Z", + "timestamp": "2026-03-14T22:01:11.152Z", "disclosures": [] } }, diff --git a/metadata/modules/aduptechBidAdapter.json b/metadata/modules/aduptechBidAdapter.json index 64d433e132d..c2b05a5ca7c 100644 --- a/metadata/modules/aduptechBidAdapter.json +++ b/metadata/modules/aduptechBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://s.d.adup-tech.com/gdpr/deviceStorage.json": { - "timestamp": "2026-01-21T15:16:29.347Z", + "timestamp": "2026-03-14T22:01:11.155Z", "disclosures": [] } }, diff --git a/metadata/modules/adyoulikeBidAdapter.json b/metadata/modules/adyoulikeBidAdapter.json index cf1f7645e87..f7ab278cf36 100644 --- a/metadata/modules/adyoulikeBidAdapter.json +++ b/metadata/modules/adyoulikeBidAdapter.json @@ -2,8 +2,8 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://adyoulike.com/deviceStorageDisclosureURL.json": { - "timestamp": "2026-01-21T15:16:29.372Z", - "disclosures": null + "timestamp": "2026-03-14T22:01:11.376Z", + "disclosures": [] } }, "components": [ diff --git a/metadata/modules/airgridRtdProvider.json b/metadata/modules/airgridRtdProvider.json index 508624393d0..38287067545 100644 --- a/metadata/modules/airgridRtdProvider.json +++ b/metadata/modules/airgridRtdProvider.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://www.wearemiq.com/privacy-and-compliance/devicestoragedisclosures.json": { - "timestamp": "2026-01-21T15:16:32.939Z", + "timestamp": "2026-03-14T22:01:11.841Z", "disclosures": [] } }, diff --git a/metadata/modules/alkimiBidAdapter.json b/metadata/modules/alkimiBidAdapter.json index 207b205aa45..3b79fd3bc8d 100644 --- a/metadata/modules/alkimiBidAdapter.json +++ b/metadata/modules/alkimiBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://d1xjh92lb8fey3.cloudfront.net/tcf/alkimi_exchange_tcf.json": { - "timestamp": "2026-01-21T15:16:32.976Z", + "timestamp": "2026-03-14T22:01:11.877Z", "disclosures": [] } }, diff --git a/metadata/modules/allegroBidAdapter.json b/metadata/modules/allegroBidAdapter.json index 616a2162531..e2061f1c026 100644 --- a/metadata/modules/allegroBidAdapter.json +++ b/metadata/modules/allegroBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://assets.allegrostatic.com/dsp-tcf-external/device-storage.json": { - "timestamp": "2026-01-21T15:16:33.255Z", + "timestamp": "2026-03-14T22:01:12.178Z", "disclosures": [] } }, diff --git a/metadata/modules/amxBidAdapter.json b/metadata/modules/amxBidAdapter.json index 6e3a3bff685..a21f00f877a 100644 --- a/metadata/modules/amxBidAdapter.json +++ b/metadata/modules/amxBidAdapter.json @@ -2,8 +2,33 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://assets.a-mo.net/tcf/device-storage.json": { - "timestamp": "2026-01-21T15:16:33.716Z", - "disclosures": [] + "timestamp": "2026-03-14T22:01:12.556Z", + "disclosures": [ + { + "identifier": "amuid2", + "type": "cookie", + "maxAgeSeconds": 31536000, + "cookieRefresh": false, + "purposes": [ + 1, + 2, + 4, + 7 + ] + }, + { + "identifier": "__amuidpb", + "type": "web", + "maxAgeSeconds": null, + "cookieRefresh": false, + "purposes": [ + 1, + 2, + 4, + 7 + ] + } + ] } }, "components": [ diff --git a/metadata/modules/amxIdSystem.json b/metadata/modules/amxIdSystem.json index 368ed164dd3..a6ee4857873 100644 --- a/metadata/modules/amxIdSystem.json +++ b/metadata/modules/amxIdSystem.json @@ -2,8 +2,33 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://assets.a-mo.net/tcf/device-storage.json": { - "timestamp": "2026-01-21T15:16:33.785Z", - "disclosures": [] + "timestamp": "2026-03-14T22:01:12.616Z", + "disclosures": [ + { + "identifier": "amuid2", + "type": "cookie", + "maxAgeSeconds": 31536000, + "cookieRefresh": false, + "purposes": [ + 1, + 2, + 4, + 7 + ] + }, + { + "identifier": "__amuidpb", + "type": "web", + "maxAgeSeconds": null, + "cookieRefresh": false, + "purposes": [ + 1, + 2, + 4, + 7 + ] + } + ] } }, "components": [ diff --git a/metadata/modules/aniviewBidAdapter.json b/metadata/modules/aniviewBidAdapter.json index 67abd000a1e..f0657a14619 100644 --- a/metadata/modules/aniviewBidAdapter.json +++ b/metadata/modules/aniviewBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://player.aniview.com/gdpr/gdpr.json": { - "timestamp": "2026-01-21T15:16:33.785Z", + "timestamp": "2026-03-14T22:01:12.617Z", "disclosures": [ { "identifier": "av_*", diff --git a/metadata/modules/anonymisedRtdProvider.json b/metadata/modules/anonymisedRtdProvider.json index c347b6c2e2e..1bf8a30eb7d 100644 --- a/metadata/modules/anonymisedRtdProvider.json +++ b/metadata/modules/anonymisedRtdProvider.json @@ -1,8 +1,8 @@ { "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { - "https://static.anonymised.io/deviceStorage.json": { - "timestamp": "2026-01-21T15:16:34.077Z", + "https://cdn1.anonymised.io/deviceStorage.json": { + "timestamp": "2026-03-14T22:01:12.709Z", "disclosures": [ { "identifier": "oidc.user*", @@ -67,7 +67,7 @@ "componentType": "rtd", "componentName": "anonymised", "gvlid": 1116, - "disclosureURL": "https://static.anonymised.io/deviceStorage.json" + "disclosureURL": "https://cdn1.anonymised.io/deviceStorage.json" } ] } \ No newline at end of file diff --git a/metadata/modules/apesterBidAdapter.json b/metadata/modules/apesterBidAdapter.json new file mode 100644 index 00000000000..e8431ff497d --- /dev/null +++ b/metadata/modules/apesterBidAdapter.json @@ -0,0 +1,18 @@ +{ + "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", + "disclosures": { + "https://apester.com/deviceStorage.json": { + "timestamp": "2026-03-14T22:01:14.502Z", + "disclosures": [] + } + }, + "components": [ + { + "componentType": "bidder", + "componentName": "apester", + "aliasOf": null, + "gvlid": 354, + "disclosureURL": "https://apester.com/deviceStorage.json" + } + ] +} \ No newline at end of file diff --git a/metadata/modules/appStockSSPBidAdapter.json b/metadata/modules/appStockSSPBidAdapter.json index fab273d8e3b..d4af6014060 100644 --- a/metadata/modules/appStockSSPBidAdapter.json +++ b/metadata/modules/appStockSSPBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://app-stock.com/deviceStorage.json": { - "timestamp": "2026-01-21T15:16:34.250Z", + "timestamp": "2026-03-14T22:01:14.601Z", "disclosures": [] } }, diff --git a/metadata/modules/appierBidAdapter.json b/metadata/modules/appierBidAdapter.json index e82a329c3ce..0cb5c1dc07e 100644 --- a/metadata/modules/appierBidAdapter.json +++ b/metadata/modules/appierBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://tcf.appier.com/deviceStorage2025.json": { - "timestamp": "2026-01-21T15:16:34.279Z", + "timestamp": "2026-03-14T22:01:14.628Z", "disclosures": [ { "identifier": "_atrk_ssid", diff --git a/metadata/modules/appnexusBidAdapter.json b/metadata/modules/appnexusBidAdapter.json index 823f03e8991..c1dadaed616 100644 --- a/metadata/modules/appnexusBidAdapter.json +++ b/metadata/modules/appnexusBidAdapter.json @@ -2,23 +2,19 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://acdn.adnxs.com/gvl/1d/xandrdevicestoragedisclosures.json": { - "timestamp": "2026-01-21T15:16:34.997Z", - "disclosures": [] - }, - "https://tcf.emetriq.de/deviceStorageDisclosure.json": { - "timestamp": "2026-01-21T15:16:34.419Z", + "timestamp": "2026-03-14T22:01:16.114Z", "disclosures": [] }, "https://beintoo-support.b-cdn.net/deviceStorage.json": { - "timestamp": "2026-01-21T15:16:34.437Z", + "timestamp": "2026-03-14T22:01:14.730Z", "disclosures": [] }, "https://projectagora.net/1032_deviceStorageDisclosure.json": { - "timestamp": "2026-01-21T15:16:34.549Z", + "timestamp": "2026-03-14T22:01:15.705Z", "disclosures": [] }, "https://adzymic.com/tcf.json": { - "timestamp": "2026-01-21T15:16:34.997Z", + "timestamp": "2026-03-14T22:01:16.114Z", "disclosures": [] } }, @@ -37,13 +33,6 @@ "gvlid": 32, "disclosureURL": "https://acdn.adnxs.com/gvl/1d/xandrdevicestoragedisclosures.json" }, - { - "componentType": "bidder", - "componentName": "emetriq", - "aliasOf": "appnexus", - "gvlid": 213, - "disclosureURL": "https://tcf.emetriq.de/deviceStorageDisclosure.json" - }, { "componentType": "bidder", "componentName": "pagescience", diff --git a/metadata/modules/appushBidAdapter.json b/metadata/modules/appushBidAdapter.json index 362373e14e1..01e0ebcf3a0 100644 --- a/metadata/modules/appushBidAdapter.json +++ b/metadata/modules/appushBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://www.thebiding.com/disclosures.json": { - "timestamp": "2026-01-21T15:16:35.025Z", + "timestamp": "2026-03-14T22:01:16.140Z", "disclosures": [] } }, diff --git a/metadata/modules/apsBidAdapter.json b/metadata/modules/apsBidAdapter.json index de3d4b624ca..416ffe8696b 100644 --- a/metadata/modules/apsBidAdapter.json +++ b/metadata/modules/apsBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://m.media-amazon.com/images/G/01/adprefs/deviceStorageDisclosure.json": { - "timestamp": "2026-01-21T15:16:35.094Z", + "timestamp": "2026-03-14T22:01:16.210Z", "disclosures": [ { "identifier": "vendor-id", diff --git a/metadata/modules/apstreamBidAdapter.json b/metadata/modules/apstreamBidAdapter.json index dd2c47436ea..e02f46fe432 100644 --- a/metadata/modules/apstreamBidAdapter.json +++ b/metadata/modules/apstreamBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://sak.userreport.com/tcf.json": { - "timestamp": "2026-01-21T15:16:35.116Z", + "timestamp": "2026-03-14T22:01:16.229Z", "disclosures": [ { "identifier": "apr_dsu", diff --git a/metadata/modules/audiencerunBidAdapter.json b/metadata/modules/audiencerunBidAdapter.json index 78f2422c707..12cd862dcef 100644 --- a/metadata/modules/audiencerunBidAdapter.json +++ b/metadata/modules/audiencerunBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://www.audiencerun.com/tcf.json": { - "timestamp": "2026-01-21T15:16:35.141Z", + "timestamp": "2026-03-14T22:01:16.249Z", "disclosures": [] } }, diff --git a/metadata/modules/axisBidAdapter.json b/metadata/modules/axisBidAdapter.json index e1beeb35be1..7b4cf74d42c 100644 --- a/metadata/modules/axisBidAdapter.json +++ b/metadata/modules/axisBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://axis-marketplace.com/tcf.json": { - "timestamp": "2026-01-21T15:16:35.201Z", + "timestamp": "2026-03-14T22:01:16.297Z", "disclosures": [] } }, diff --git a/metadata/modules/azerionedgeRtdProvider.json b/metadata/modules/azerionedgeRtdProvider.json index c5b957dbb02..3c3f592335d 100644 --- a/metadata/modules/azerionedgeRtdProvider.json +++ b/metadata/modules/azerionedgeRtdProvider.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://sellers.improvedigital.com/tcf-cookies.json": { - "timestamp": "2026-01-21T15:16:35.242Z", + "timestamp": "2026-03-14T22:01:16.344Z", "disclosures": [ { "identifier": "tuuid", diff --git a/metadata/modules/beachfrontBidAdapter.json b/metadata/modules/beachfrontBidAdapter.json index f150a9a3762..8cbc30e6581 100644 --- a/metadata/modules/beachfrontBidAdapter.json +++ b/metadata/modules/beachfrontBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://tcf.seedtag.com/vendor.json": { - "timestamp": "2026-01-21T15:16:35.274Z", + "timestamp": "2026-03-14T22:01:16.369Z", "disclosures": [] } }, diff --git a/metadata/modules/beopBidAdapter.json b/metadata/modules/beopBidAdapter.json index cf50470b47f..49afa1d7fcf 100644 --- a/metadata/modules/beopBidAdapter.json +++ b/metadata/modules/beopBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://beop.io/deviceStorage.json": { - "timestamp": "2026-01-21T15:16:35.627Z", + "timestamp": "2026-03-14T22:01:16.490Z", "disclosures": [] } }, diff --git a/metadata/modules/betweenBidAdapter.json b/metadata/modules/betweenBidAdapter.json index 4df09f96284..18b2eb8ccf6 100644 --- a/metadata/modules/betweenBidAdapter.json +++ b/metadata/modules/betweenBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://en.betweenx.com/deviceStorage.json": { - "timestamp": "2026-01-21T15:16:35.751Z", + "timestamp": "2026-03-14T22:01:16.594Z", "disclosures": [] } }, diff --git a/metadata/modules/bidfuseBidAdapter.json b/metadata/modules/bidfuseBidAdapter.json index dd88bf2e743..2f8dad2ef5a 100644 --- a/metadata/modules/bidfuseBidAdapter.json +++ b/metadata/modules/bidfuseBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://bidfuse.com/disclosure.json": { - "timestamp": "2026-01-21T15:16:35.815Z", + "timestamp": "2026-03-12T23:08:04.348Z", "disclosures": [] } }, diff --git a/metadata/modules/bidmaticBidAdapter.json b/metadata/modules/bidmaticBidAdapter.json index bab8bed298e..1c8a8efcbe5 100644 --- a/metadata/modules/bidmaticBidAdapter.json +++ b/metadata/modules/bidmaticBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://bidmatic.io/.well-known/deviceStorage.json": { - "timestamp": "2026-01-21T15:16:35.994Z", + "timestamp": "2026-03-14T22:01:49.175Z", "disclosures": [] } }, diff --git a/metadata/modules/bidtheatreBidAdapter.json b/metadata/modules/bidtheatreBidAdapter.json index 10949a5d4bd..ca0e7996ca6 100644 --- a/metadata/modules/bidtheatreBidAdapter.json +++ b/metadata/modules/bidtheatreBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://privacy.bidtheatre.com/deviceStorage.json": { - "timestamp": "2026-01-21T15:16:36.045Z", + "timestamp": "2026-03-14T22:01:49.225Z", "disclosures": [] } }, diff --git a/metadata/modules/bliinkBidAdapter.json b/metadata/modules/bliinkBidAdapter.json index 8ec015b1f9a..5ec1afdc7b5 100644 --- a/metadata/modules/bliinkBidAdapter.json +++ b/metadata/modules/bliinkBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://bliink.io/disclosures.json": { - "timestamp": "2026-01-21T15:16:36.316Z", + "timestamp": "2026-03-14T22:01:49.524Z", "disclosures": [] } }, diff --git a/metadata/modules/blockthroughBidAdapter.json b/metadata/modules/blockthroughBidAdapter.json index f26d883d1d5..e04ba439c5e 100644 --- a/metadata/modules/blockthroughBidAdapter.json +++ b/metadata/modules/blockthroughBidAdapter.json @@ -2,8 +2,324 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://blockthrough.com/tcf_disclosures.json": { - "timestamp": "2026-01-21T15:16:36.668Z", - "disclosures": null + "timestamp": "2026-03-14T22:01:49.844Z", + "disclosures": [ + { + "identifier": "BT_AA_DETECTION", + "type": "web", + "maxAgeSeconds": null, + "cookieRefresh": false, + "purposes": [ + 1, + 2, + 3, + 4, + 7, + 9, + 10 + ] + }, + { + "identifier": "btUserCountry", + "type": "web", + "maxAgeSeconds": null, + "cookieRefresh": false, + "purposes": [ + 1, + 2, + 3, + 4, + 7, + 9, + 10 + ] + }, + { + "identifier": "btUserCountryExpiry", + "type": "web", + "maxAgeSeconds": null, + "cookieRefresh": false, + "purposes": [ + 1, + 2, + 3, + 4, + 7, + 9, + 10 + ] + }, + { + "identifier": "btUserIsFromRestrictedCountry", + "type": "web", + "maxAgeSeconds": null, + "cookieRefresh": false, + "purposes": [ + 1, + 2, + 3, + 4, + 7, + 9, + 10 + ] + }, + { + "identifier": "BT_BUNDLE_VERSION", + "type": "web", + "maxAgeSeconds": null, + "cookieRefresh": false, + "purposes": [ + 1, + 2, + 3, + 4, + 7, + 9, + 10 + ] + }, + { + "identifier": "BT_DIGEST_VERSION", + "type": "web", + "maxAgeSeconds": null, + "cookieRefresh": false, + "purposes": [ + 1, + 2, + 3, + 4, + 7, + 9, + 10 + ] + }, + { + "identifier": "BT_sid", + "type": "web", + "maxAgeSeconds": null, + "cookieRefresh": false, + "purposes": [ + 1, + 2, + 3, + 4, + 7, + 9, + 10 + ] + }, + { + "identifier": "BT_traceID", + "type": "web", + "maxAgeSeconds": null, + "cookieRefresh": false, + "purposes": [ + 1, + 2, + 3, + 4, + 7, + 9, + 10 + ] + }, + { + "identifier": "uids", + "type": "cookie", + "maxAgeSeconds": 7776000, + "cookieRefresh": true, + "purposes": [ + 1, + 2, + 3, + 4, + 7, + 9, + 10 + ] + }, + { + "identifier": "BT_pvSent", + "type": "web", + "maxAgeSeconds": null, + "cookieRefresh": false, + "purposes": [ + 1, + 2, + 3, + 4, + 7, + 9, + 10 + ] + }, + { + "identifier": "BT_WHITELISTING_IFRAME_ACCESS", + "type": "web", + "maxAgeSeconds": null, + "cookieRefresh": false, + "purposes": [ + 1, + 2, + 3, + 4, + 7, + 9, + 10 + ] + }, + { + "identifier": "BT_BLOCKLISTED_CREATIVES", + "type": "web", + "maxAgeSeconds": null, + "cookieRefresh": false, + "purposes": [ + 1, + 2, + 3, + 4, + 7, + 9, + 10 + ] + }, + { + "identifier": "BT_AM_SOFTWALL_RENDERED", + "type": "web", + "maxAgeSeconds": null, + "cookieRefresh": false, + "purposes": [ + 1, + 2, + 3, + 4, + 7, + 9, + 10 + ] + }, + { + "identifier": "BT_AM_SOFTWALL_DISMISSED", + "type": "web", + "maxAgeSeconds": null, + "cookieRefresh": false, + "purposes": [ + 1, + 2, + 3, + 4, + 7, + 9, + 10 + ] + }, + { + "identifier": "BT_AM_SOFTWALL_RECOVERED", + "type": "web", + "maxAgeSeconds": null, + "cookieRefresh": false, + "purposes": [ + 1, + 2, + 3, + 4, + 7, + 9, + 10 + ] + }, + { + "identifier": "BT_AM_SOFTWALL_RENDER_COUNT", + "type": "web", + "maxAgeSeconds": null, + "cookieRefresh": false, + "purposes": [ + 1, + 2, + 3, + 4, + 7, + 9, + 10 + ] + }, + { + "identifier": "BT_AM_SOFTWALL_ABTEST", + "type": "web", + "maxAgeSeconds": null, + "cookieRefresh": false, + "purposes": [ + 1, + 2, + 3, + 4, + 7, + 9, + 10 + ] + }, + { + "identifier": "BT_AM_ATTRIBUTION_EXPIRY", + "type": "web", + "maxAgeSeconds": null, + "cookieRefresh": false, + "purposes": [ + 1, + 2, + 3, + 4, + 7, + 9, + 10 + ] + }, + { + "identifier": "BT_AM_PREMIUM_ADBLOCK_USER_DETECTED", + "type": "web", + "maxAgeSeconds": null, + "cookieRefresh": false, + "purposes": [ + 1, + 2, + 3, + 4, + 7, + 9, + 10 + ] + }, + { + "identifier": "BT_AM_PREMIUM_ADBLOCK_USER_DETECTION_DATE", + "type": "web", + "maxAgeSeconds": null, + "cookieRefresh": false, + "purposes": [ + 1, + 2, + 3, + 4, + 7, + 9, + 10 + ] + }, + { + "identifier": "BT_AM_SCA_SUCCEED", + "type": "web", + "maxAgeSeconds": null, + "cookieRefresh": false, + "purposes": [ + 1, + 2, + 3, + 4, + 7, + 9, + 10 + ] + } + ] } }, "components": [ diff --git a/metadata/modules/blueBidAdapter.json b/metadata/modules/blueBidAdapter.json index e32587ebe24..153b192f3dc 100644 --- a/metadata/modules/blueBidAdapter.json +++ b/metadata/modules/blueBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://getblue.io/iab/iab.json": { - "timestamp": "2026-01-21T15:16:36.766Z", + "timestamp": "2026-03-14T22:01:49.946Z", "disclosures": [] } }, diff --git a/metadata/modules/bmsBidAdapter.json b/metadata/modules/bmsBidAdapter.json index 16940b8531e..0a9f5f2d60c 100644 --- a/metadata/modules/bmsBidAdapter.json +++ b/metadata/modules/bmsBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://bluems.com/iab.json": { - "timestamp": "2026-01-21T15:16:37.120Z", + "timestamp": "2026-03-14T22:01:50.302Z", "disclosures": [] } }, diff --git a/metadata/modules/boldwinBidAdapter.json b/metadata/modules/boldwinBidAdapter.json index 0ed9fd7923f..04384e3c493 100644 --- a/metadata/modules/boldwinBidAdapter.json +++ b/metadata/modules/boldwinBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://magav.videowalldirect.com/iab/videowalldirectiab.json": { - "timestamp": "2026-01-21T15:16:37.138Z", + "timestamp": "2026-03-14T22:01:50.317Z", "disclosures": [] } }, diff --git a/metadata/modules/bridBidAdapter.json b/metadata/modules/bridBidAdapter.json index f11ad1153ef..cc3d804f9fe 100644 --- a/metadata/modules/bridBidAdapter.json +++ b/metadata/modules/bridBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://target-video.com/vendors-device-storage-and-operational-disclosures.json": { - "timestamp": "2026-01-21T15:16:37.160Z", + "timestamp": "2026-03-14T22:01:50.336Z", "disclosures": [ { "identifier": "brid_location", diff --git a/metadata/modules/browsiBidAdapter.json b/metadata/modules/browsiBidAdapter.json index 0cb4c6eb267..6a25a069e78 100644 --- a/metadata/modules/browsiBidAdapter.json +++ b/metadata/modules/browsiBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://cdn.browsiprod.com/ads/tcf.json": { - "timestamp": "2026-01-21T15:16:37.299Z", + "timestamp": "2026-03-14T22:01:50.476Z", "disclosures": [] } }, diff --git a/metadata/modules/bucksenseBidAdapter.json b/metadata/modules/bucksenseBidAdapter.json index 87c476f0b36..3b972e155f5 100644 --- a/metadata/modules/bucksenseBidAdapter.json +++ b/metadata/modules/bucksenseBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://j.bksnimages.com/iab/devsto02.json": { - "timestamp": "2026-01-21T15:16:37.355Z", + "timestamp": "2026-03-14T22:01:50.530Z", "disclosures": [] } }, diff --git a/metadata/modules/carodaBidAdapter.json b/metadata/modules/carodaBidAdapter.json index 4efb66f4d89..f2cd0dd3d15 100644 --- a/metadata/modules/carodaBidAdapter.json +++ b/metadata/modules/carodaBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://cdn2.caroda.io/tcfvds/2022-05-17/deviceStorage.json": { - "timestamp": "2026-01-21T15:16:37.394Z", + "timestamp": "2026-03-14T22:01:50.644Z", "disclosures": [] } }, diff --git a/metadata/modules/categoryTranslation.json b/metadata/modules/categoryTranslation.json index e266b2b30f3..bdf15dcb863 100644 --- a/metadata/modules/categoryTranslation.json +++ b/metadata/modules/categoryTranslation.json @@ -2,23 +2,8 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://cdn.jsdelivr.net/gh/prebid/Prebid.js/metadata/disclosures/prebid/categoryTranslation.json": { - "timestamp": "2026-01-21T15:16:22.812Z", - "disclosures": [ - { - "identifier": "iabToFwMappingkey", - "type": "web", - "purposes": [ - 1 - ] - }, - { - "identifier": "iabToFwMappingkeyPub", - "type": "web", - "purposes": [ - 1 - ] - } - ] + "timestamp": "2026-03-14T22:01:05.184Z", + "disclosures": null } }, "components": [ diff --git a/metadata/modules/ceeIdSystem.json b/metadata/modules/ceeIdSystem.json index ecd8cbbbc22..7a44080862b 100644 --- a/metadata/modules/ceeIdSystem.json +++ b/metadata/modules/ceeIdSystem.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://ssp.wp.pl/deviceStorage.json": { - "timestamp": "2026-01-21T15:16:37.699Z", + "timestamp": "2026-03-14T22:01:50.959Z", "disclosures": null } }, diff --git a/metadata/modules/chromeAiRtdProvider.json b/metadata/modules/chromeAiRtdProvider.json index f39b94c7976..1037dfaf711 100644 --- a/metadata/modules/chromeAiRtdProvider.json +++ b/metadata/modules/chromeAiRtdProvider.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://cdn.jsdelivr.net/gh/prebid/Prebid.js/metadata/disclosures/modules/chromeAiRtdProvider.json": { - "timestamp": "2026-01-21T15:16:38.027Z", + "timestamp": "2026-03-14T22:01:51.286Z", "disclosures": [ { "identifier": "chromeAi_detected_data", diff --git a/metadata/modules/clickioBidAdapter.json b/metadata/modules/clickioBidAdapter.json index 94f7f16f52f..35d6b21bf15 100644 --- a/metadata/modules/clickioBidAdapter.json +++ b/metadata/modules/clickioBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://o.clickiocdn.com/tcf_storage_info.json": { - "timestamp": "2026-01-21T15:16:38.028Z", + "timestamp": "2026-03-14T22:01:51.287Z", "disclosures": [] } }, diff --git a/metadata/modules/compassBidAdapter.json b/metadata/modules/compassBidAdapter.json index 09e49cec04d..3e97174d9e9 100644 --- a/metadata/modules/compassBidAdapter.json +++ b/metadata/modules/compassBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://cdn.marphezis.com/tcf-vendor-disclosures.json": { - "timestamp": "2026-01-21T15:16:38.465Z", + "timestamp": "2026-03-14T22:01:51.731Z", "disclosures": [] } }, diff --git a/metadata/modules/conceptxBidAdapter.json b/metadata/modules/conceptxBidAdapter.json index 1f890db7a88..2576eb4d687 100644 --- a/metadata/modules/conceptxBidAdapter.json +++ b/metadata/modules/conceptxBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://cncptx.com/device_storage_disclosure.json": { - "timestamp": "2026-01-21T15:16:38.481Z", + "timestamp": "2026-03-14T22:01:51.749Z", "disclosures": [] } }, diff --git a/metadata/modules/connatixBidAdapter.json b/metadata/modules/connatixBidAdapter.json index f62456c3b1d..21753dc78e6 100644 --- a/metadata/modules/connatixBidAdapter.json +++ b/metadata/modules/connatixBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://connatix.com/iab-tcf-disclosure.json": { - "timestamp": "2026-01-21T15:16:38.511Z", + "timestamp": "2026-03-14T22:01:51.769Z", "disclosures": [ { "identifier": "cnx_userId", diff --git a/metadata/modules/connectIdSystem.json b/metadata/modules/connectIdSystem.json index 711894bc75a..d717b5070c1 100644 --- a/metadata/modules/connectIdSystem.json +++ b/metadata/modules/connectIdSystem.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://meta.legal.yahoo.com/iab-tcf/v2/device-storage-disclosure.json": { - "timestamp": "2026-01-21T15:16:38.586Z", + "timestamp": "2026-03-14T22:01:51.851Z", "disclosures": [ { "identifier": "vmcid", diff --git a/metadata/modules/connectadBidAdapter.json b/metadata/modules/connectadBidAdapter.json index 2a5bfbb5eb8..88634bfb4e5 100644 --- a/metadata/modules/connectadBidAdapter.json +++ b/metadata/modules/connectadBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://cdn.connectad.io/tcf_storage_info.json": { - "timestamp": "2026-01-21T15:16:38.608Z", + "timestamp": "2026-03-14T22:01:51.873Z", "disclosures": [] } }, diff --git a/metadata/modules/contentexchangeBidAdapter.json b/metadata/modules/contentexchangeBidAdapter.json index 2c15133d291..bf878caad40 100644 --- a/metadata/modules/contentexchangeBidAdapter.json +++ b/metadata/modules/contentexchangeBidAdapter.json @@ -2,8 +2,8 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://hb.contentexchange.me/template/device_storage.json": { - "timestamp": "2026-01-21T15:16:39.025Z", - "disclosures": null + "timestamp": "2026-03-14T22:01:52.316Z", + "disclosures": [] } }, "components": [ diff --git a/metadata/modules/conversantBidAdapter.json b/metadata/modules/conversantBidAdapter.json index bd8e9426455..8cae159b7ab 100644 --- a/metadata/modules/conversantBidAdapter.json +++ b/metadata/modules/conversantBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://s-usweb.dotomi.com/assets/js/taggy-js/2.18.9/device_storage_disclosure.json": { - "timestamp": "2026-01-21T15:16:39.056Z", + "timestamp": "2026-03-14T22:01:52.660Z", "disclosures": [ { "identifier": "dtm_status", diff --git a/metadata/modules/copper6sspBidAdapter.json b/metadata/modules/copper6sspBidAdapter.json index 1275c106723..4e7981bb6bd 100644 --- a/metadata/modules/copper6sspBidAdapter.json +++ b/metadata/modules/copper6sspBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://ssp.copper6.com/deviceStorage.json": { - "timestamp": "2026-01-21T15:16:39.070Z", + "timestamp": "2026-03-14T22:01:52.678Z", "disclosures": [] } }, diff --git a/metadata/modules/cpmstarBidAdapter.json b/metadata/modules/cpmstarBidAdapter.json index d689d4037c0..ba4f27d0ab4 100644 --- a/metadata/modules/cpmstarBidAdapter.json +++ b/metadata/modules/cpmstarBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://www.aditude.com/storageaccess.json": { - "timestamp": "2026-01-21T15:16:39.112Z", + "timestamp": "2026-03-14T22:01:52.763Z", "disclosures": [] } }, diff --git a/metadata/modules/criteoBidAdapter.json b/metadata/modules/criteoBidAdapter.json index ed25ca22c17..7b862f48b6a 100644 --- a/metadata/modules/criteoBidAdapter.json +++ b/metadata/modules/criteoBidAdapter.json @@ -1,8 +1,8 @@ { "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { - "https://privacy.criteo.com/iab-europe/tcfv2/disclosure": { - "timestamp": "2026-01-21T15:16:39.189Z", + "https://privacy.criteo.com/iab-europe/tcfv2/disclosure.json": { + "timestamp": "2026-03-14T22:01:52.818Z", "disclosures": [ { "identifier": "criteo_fast_bid", @@ -69,7 +69,7 @@ "componentName": "criteo", "aliasOf": null, "gvlid": 91, - "disclosureURL": "https://privacy.criteo.com/iab-europe/tcfv2/disclosure" + "disclosureURL": "https://privacy.criteo.com/iab-europe/tcfv2/disclosure.json" } ] } \ No newline at end of file diff --git a/metadata/modules/criteoIdSystem.json b/metadata/modules/criteoIdSystem.json index fb4af1fede1..9dadb955104 100644 --- a/metadata/modules/criteoIdSystem.json +++ b/metadata/modules/criteoIdSystem.json @@ -1,8 +1,8 @@ { "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { - "https://privacy.criteo.com/iab-europe/tcfv2/disclosure": { - "timestamp": "2026-01-21T15:16:39.203Z", + "https://privacy.criteo.com/iab-europe/tcfv2/disclosure.json": { + "timestamp": "2026-03-14T22:01:52.839Z", "disclosures": [ { "identifier": "criteo_fast_bid", @@ -68,7 +68,7 @@ "componentType": "userId", "componentName": "criteo", "gvlid": 91, - "disclosureURL": "https://privacy.criteo.com/iab-europe/tcfv2/disclosure", + "disclosureURL": "https://privacy.criteo.com/iab-europe/tcfv2/disclosure.json", "aliasOf": null } ] diff --git a/metadata/modules/cwireBidAdapter.json b/metadata/modules/cwireBidAdapter.json index 8c7fd24ddff..eb9f73c1fb3 100644 --- a/metadata/modules/cwireBidAdapter.json +++ b/metadata/modules/cwireBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://cdn.cwi.re/artifacts/iab/iab.json": { - "timestamp": "2026-01-21T15:16:39.204Z", + "timestamp": "2026-03-14T22:01:52.839Z", "disclosures": [] } }, diff --git a/metadata/modules/czechAdIdSystem.json b/metadata/modules/czechAdIdSystem.json index ee9919cf839..da769f7913c 100644 --- a/metadata/modules/czechAdIdSystem.json +++ b/metadata/modules/czechAdIdSystem.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://cpex.cz/storagedisclosure.json": { - "timestamp": "2026-01-21T15:16:39.586Z", + "timestamp": "2026-03-14T22:01:52.864Z", "disclosures": [] } }, diff --git a/metadata/modules/dailymotionBidAdapter.json b/metadata/modules/dailymotionBidAdapter.json index 800dd57eb28..e09257d90ce 100644 --- a/metadata/modules/dailymotionBidAdapter.json +++ b/metadata/modules/dailymotionBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://statics.dmcdn.net/a/vds.json": { - "timestamp": "2026-01-21T15:16:39.992Z", + "timestamp": "2026-03-14T22:01:53.267Z", "disclosures": [ { "identifier": "uid_dm", diff --git a/metadata/modules/debugging.json b/metadata/modules/debugging.json index b4c2ae2c42e..328bddc340e 100644 --- a/metadata/modules/debugging.json +++ b/metadata/modules/debugging.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://cdn.jsdelivr.net/gh/prebid/Prebid.js/metadata/disclosures/prebid/debugging.json": { - "timestamp": "2026-01-21T15:16:22.811Z", + "timestamp": "2026-03-14T22:01:05.183Z", "disclosures": [ { "identifier": "__*_debugging__", diff --git a/metadata/modules/deepintentBidAdapter.json b/metadata/modules/deepintentBidAdapter.json index 73c3d103901..9e6cc8c3bb8 100644 --- a/metadata/modules/deepintentBidAdapter.json +++ b/metadata/modules/deepintentBidAdapter.json @@ -2,8 +2,8 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://www.deepintent.com/iabeurope_vendor_disclosures.json": { - "timestamp": "2026-01-21T15:16:40.098Z", - "disclosures": [] + "timestamp": "2026-03-14T22:01:53.372Z", + "disclosures": null } }, "components": [ diff --git a/metadata/modules/defineMediaBidAdapter.json b/metadata/modules/defineMediaBidAdapter.json index fa316818b9e..52ebdc1fc99 100644 --- a/metadata/modules/defineMediaBidAdapter.json +++ b/metadata/modules/defineMediaBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://definemedia.de/tcf/deviceStorageDisclosureURL.json": { - "timestamp": "2026-01-21T15:16:40.219Z", + "timestamp": "2026-03-14T22:01:53.532Z", "disclosures": [ { "identifier": "conative$dataGathering$Adex", diff --git a/metadata/modules/deltaprojectsBidAdapter.json b/metadata/modules/deltaprojectsBidAdapter.json index d997f834961..e27020bdca8 100644 --- a/metadata/modules/deltaprojectsBidAdapter.json +++ b/metadata/modules/deltaprojectsBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://cdn.de17a.com/policy/deviceStorage.json": { - "timestamp": "2026-01-21T15:16:40.641Z", + "timestamp": "2026-03-14T22:01:53.981Z", "disclosures": [] } }, diff --git a/metadata/modules/dianomiBidAdapter.json b/metadata/modules/dianomiBidAdapter.json index a90fafec29e..6ee324e79db 100644 --- a/metadata/modules/dianomiBidAdapter.json +++ b/metadata/modules/dianomiBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://www.dianomi.com/device_storage.json": { - "timestamp": "2026-01-21T15:16:40.716Z", + "timestamp": "2026-03-14T22:01:54.413Z", "disclosures": [] } }, diff --git a/metadata/modules/digitalMatterBidAdapter.json b/metadata/modules/digitalMatterBidAdapter.json index 359c944b2e1..01b0a47b636 100644 --- a/metadata/modules/digitalMatterBidAdapter.json +++ b/metadata/modules/digitalMatterBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://digitalmatter.ai/disclosures.json": { - "timestamp": "2026-01-21T15:16:40.716Z", + "timestamp": "2026-03-14T22:01:54.413Z", "disclosures": [] } }, diff --git a/metadata/modules/distroscaleBidAdapter.json b/metadata/modules/distroscaleBidAdapter.json index 249539c9258..142b0647ede 100644 --- a/metadata/modules/distroscaleBidAdapter.json +++ b/metadata/modules/distroscaleBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://a.jsrdn.com/tcf/tcf-vendor-disclosure.json": { - "timestamp": "2026-01-21T15:16:41.071Z", + "timestamp": "2026-03-14T22:01:54.780Z", "disclosures": [] } }, diff --git a/metadata/modules/docereeAdManagerBidAdapter.json b/metadata/modules/docereeAdManagerBidAdapter.json index f5454f2309a..fa9cb23f5f3 100644 --- a/metadata/modules/docereeAdManagerBidAdapter.json +++ b/metadata/modules/docereeAdManagerBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://doceree.com/.well-known/iab/deviceStorage.json": { - "timestamp": "2026-01-21T15:16:41.165Z", + "timestamp": "2026-03-14T22:01:55.075Z", "disclosures": [] } }, diff --git a/metadata/modules/docereeBidAdapter.json b/metadata/modules/docereeBidAdapter.json index 0815e04e6d2..fcf92c31563 100644 --- a/metadata/modules/docereeBidAdapter.json +++ b/metadata/modules/docereeBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://doceree.com/.well-known/iab/deviceStorage.json": { - "timestamp": "2026-01-21T15:16:41.920Z", + "timestamp": "2026-03-14T22:01:55.828Z", "disclosures": [] } }, diff --git a/metadata/modules/optableBidAdapter.json b/metadata/modules/dpaiBidAdapter.json similarity index 87% rename from metadata/modules/optableBidAdapter.json rename to metadata/modules/dpaiBidAdapter.json index 52fd4a88cd7..901b09a3355 100644 --- a/metadata/modules/optableBidAdapter.json +++ b/metadata/modules/dpaiBidAdapter.json @@ -4,7 +4,7 @@ "components": [ { "componentType": "bidder", - "componentName": "optable", + "componentName": "dpai", "aliasOf": null, "gvlid": null, "disclosureURL": null diff --git a/metadata/modules/dspxBidAdapter.json b/metadata/modules/dspxBidAdapter.json index 025ad773f48..2de17defd7b 100644 --- a/metadata/modules/dspxBidAdapter.json +++ b/metadata/modules/dspxBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://tcf.adtech.app/gen/deviceStorageDisclosure/os.json": { - "timestamp": "2026-01-21T15:16:41.920Z", + "timestamp": "2026-03-14T22:01:55.830Z", "disclosures": [] } }, diff --git a/metadata/modules/e_volutionBidAdapter.json b/metadata/modules/e_volutionBidAdapter.json index fb7d0dab4f3..16ecd4a10a2 100644 --- a/metadata/modules/e_volutionBidAdapter.json +++ b/metadata/modules/e_volutionBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://e-volution.ai/file.json": { - "timestamp": "2026-01-21T15:16:42.646Z", + "timestamp": "2026-03-14T22:01:56.484Z", "disclosures": [] } }, diff --git a/metadata/modules/edge226BidAdapter.json b/metadata/modules/edge226BidAdapter.json index 2d80d88cb30..62050c87076 100644 --- a/metadata/modules/edge226BidAdapter.json +++ b/metadata/modules/edge226BidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://cdn.serveteck.com/cdn_storage/tcf/tcf.json?a=1.io": { - "timestamp": "2026-01-21T15:16:43.012Z", + "timestamp": "2026-03-14T22:01:56.828Z", "disclosures": [] } }, diff --git a/metadata/modules/empowerBidAdapter.json b/metadata/modules/empowerBidAdapter.json index 7896d5f362a..d8cc897f838 100644 --- a/metadata/modules/empowerBidAdapter.json +++ b/metadata/modules/empowerBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://cdn.empower.net/vendor/vendor.json": { - "timestamp": "2026-01-21T15:16:43.070Z", + "timestamp": "2026-03-14T22:01:56.884Z", "disclosures": [] } }, diff --git a/metadata/modules/equativBidAdapter.json b/metadata/modules/equativBidAdapter.json index 9a871ee8f33..5dfbad9e9a3 100644 --- a/metadata/modules/equativBidAdapter.json +++ b/metadata/modules/equativBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://apps.smartadserver.com/device-storage-disclosures/equativDeviceStorageDisclosures.json": { - "timestamp": "2026-01-21T15:16:43.095Z", + "timestamp": "2026-03-14T22:01:57.159Z", "disclosures": [] } }, diff --git a/metadata/modules/eskimiBidAdapter.json b/metadata/modules/eskimiBidAdapter.json index d72275c721a..11a1fc49317 100644 --- a/metadata/modules/eskimiBidAdapter.json +++ b/metadata/modules/eskimiBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://dsp-media.eskimi.com/deviceStorage.json": { - "timestamp": "2026-01-21T15:16:43.802Z", + "timestamp": "2026-03-14T22:01:57.203Z", "disclosures": [] } }, diff --git a/metadata/modules/etargetBidAdapter.json b/metadata/modules/etargetBidAdapter.json index f6db2e107f5..0a3c97223f9 100644 --- a/metadata/modules/etargetBidAdapter.json +++ b/metadata/modules/etargetBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://www.etarget.sk/cookies3.json": { - "timestamp": "2026-01-21T15:16:43.820Z", + "timestamp": "2026-03-14T22:01:57.231Z", "disclosures": [] } }, diff --git a/metadata/modules/euidIdSystem.json b/metadata/modules/euidIdSystem.json index f77752e6aca..c04ec332848 100644 --- a/metadata/modules/euidIdSystem.json +++ b/metadata/modules/euidIdSystem.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://ttd-misc-public-assets.s3.us-west-2.amazonaws.com/deviceStorageDisclosureURL.json": { - "timestamp": "2026-01-21T15:16:44.520Z", + "timestamp": "2026-03-14T22:01:57.808Z", "disclosures": [] } }, diff --git a/metadata/modules/exadsBidAdapter.json b/metadata/modules/exadsBidAdapter.json index 4330e255da3..7d9f84855c0 100644 --- a/metadata/modules/exadsBidAdapter.json +++ b/metadata/modules/exadsBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://a.native7.com/tcf/deviceStorage.php": { - "timestamp": "2026-01-21T15:16:44.725Z", + "timestamp": "2026-03-14T22:01:58.009Z", "disclosures": [ { "identifier": "pn-zone-*", diff --git a/metadata/modules/feedadBidAdapter.json b/metadata/modules/feedadBidAdapter.json index 611343dbeb1..dca3b326fd4 100644 --- a/metadata/modules/feedadBidAdapter.json +++ b/metadata/modules/feedadBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://api.feedad.com/tcf-device-disclosures.json": { - "timestamp": "2026-01-21T15:16:44.906Z", + "timestamp": "2026-03-14T22:01:58.194Z", "disclosures": [ { "identifier": "__fad_data", diff --git a/metadata/modules/floxisBidAdapter.json b/metadata/modules/floxisBidAdapter.json new file mode 100644 index 00000000000..c58d76bc57a --- /dev/null +++ b/metadata/modules/floxisBidAdapter.json @@ -0,0 +1,13 @@ +{ + "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", + "disclosures": {}, + "components": [ + { + "componentType": "bidder", + "componentName": "floxis", + "aliasOf": null, + "gvlid": null, + "disclosureURL": null + } + ] +} \ No newline at end of file diff --git a/metadata/modules/fwsspBidAdapter.json b/metadata/modules/fwsspBidAdapter.json index 35b267448e0..31c9d286492 100644 --- a/metadata/modules/fwsspBidAdapter.json +++ b/metadata/modules/fwsspBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://iab.fwmrm.net/g/devicedisclosure.json": { - "timestamp": "2026-01-21T15:16:45.058Z", + "timestamp": "2026-03-14T22:01:58.315Z", "disclosures": [] } }, diff --git a/metadata/modules/gamoshiBidAdapter.json b/metadata/modules/gamoshiBidAdapter.json index f7d9244f89e..d392c55e551 100644 --- a/metadata/modules/gamoshiBidAdapter.json +++ b/metadata/modules/gamoshiBidAdapter.json @@ -1,8 +1,8 @@ { "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { - "https://www.gamoshi.com/disclosures-client-storage.json": { - "timestamp": "2026-01-21T15:16:45.714Z", + "https://resources.gamoshi.io/disclosures-client-storage.json": { + "timestamp": "2026-03-14T22:01:58.534Z", "disclosures": [] } }, @@ -12,7 +12,7 @@ "componentName": "gamoshi", "aliasOf": null, "gvlid": 644, - "disclosureURL": "https://www.gamoshi.com/disclosures-client-storage.json" + "disclosureURL": "https://resources.gamoshi.io/disclosures-client-storage.json" }, { "componentType": "bidder", diff --git a/metadata/modules/gemiusIdSystem.json b/metadata/modules/gemiusIdSystem.json index b8267d82a05..cbff60090cd 100644 --- a/metadata/modules/gemiusIdSystem.json +++ b/metadata/modules/gemiusIdSystem.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://gemius.com/media/documents/Gemius_SA_Vendor_Device_Storage.json": { - "timestamp": "2026-01-21T15:16:45.823Z", + "timestamp": "2026-03-14T22:01:58.649Z", "disclosures": [ { "identifier": "__gsyncs_gdpr", diff --git a/metadata/modules/glomexBidAdapter.json b/metadata/modules/glomexBidAdapter.json index 12a4723bea3..85a3afd0138 100644 --- a/metadata/modules/glomexBidAdapter.json +++ b/metadata/modules/glomexBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://player.glomex.com/.well-known/deviceStorage.json": { - "timestamp": "2026-01-21T15:16:46.373Z", + "timestamp": "2026-03-14T22:01:58.651Z", "disclosures": [ { "identifier": "glomexUser", diff --git a/metadata/modules/goldbachBidAdapter.json b/metadata/modules/goldbachBidAdapter.json index 170fcc59d4a..0711b6a6db5 100644 --- a/metadata/modules/goldbachBidAdapter.json +++ b/metadata/modules/goldbachBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://gb-next.ch/TcfGoldbachDeviceStorage.json": { - "timestamp": "2026-01-21T15:16:46.393Z", + "timestamp": "2026-03-14T22:01:58.673Z", "disclosures": [ { "identifier": "dakt_2_session_id", diff --git a/metadata/modules/gridBidAdapter.json b/metadata/modules/gridBidAdapter.json index 5ada86e2d1a..07539dd6c09 100644 --- a/metadata/modules/gridBidAdapter.json +++ b/metadata/modules/gridBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://www.themediagrid.com/devicestorage.json": { - "timestamp": "2026-01-21T15:16:46.414Z", + "timestamp": "2026-03-14T22:01:58.700Z", "disclosures": [] } }, diff --git a/metadata/modules/gumgumBidAdapter.json b/metadata/modules/gumgumBidAdapter.json index 7d4c95280f3..367f4632a58 100644 --- a/metadata/modules/gumgumBidAdapter.json +++ b/metadata/modules/gumgumBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://marketing.gumgum.com/devicestoragedisclosures.json": { - "timestamp": "2026-01-21T15:16:46.595Z", + "timestamp": "2026-03-14T22:01:58.789Z", "disclosures": [] } }, diff --git a/metadata/modules/hadronIdSystem.json b/metadata/modules/hadronIdSystem.json index 382e7a5b692..5cf1d5d8ca5 100644 --- a/metadata/modules/hadronIdSystem.json +++ b/metadata/modules/hadronIdSystem.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://p.ad.gt/static/iab_tcf.json": { - "timestamp": "2026-01-21T15:16:46.652Z", + "timestamp": "2026-03-14T22:01:58.839Z", "disclosures": [ { "identifier": "au/sid", diff --git a/metadata/modules/hadronRtdProvider.json b/metadata/modules/hadronRtdProvider.json index 4092a2025aa..2f69e177575 100644 --- a/metadata/modules/hadronRtdProvider.json +++ b/metadata/modules/hadronRtdProvider.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://p.ad.gt/static/iab_tcf.json": { - "timestamp": "2026-01-21T15:16:46.764Z", + "timestamp": "2026-03-14T22:01:59.001Z", "disclosures": [ { "identifier": "au/sid", diff --git a/metadata/modules/harionBidAdapter.json b/metadata/modules/harionBidAdapter.json new file mode 100644 index 00000000000..56500317adf --- /dev/null +++ b/metadata/modules/harionBidAdapter.json @@ -0,0 +1,18 @@ +{ + "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", + "disclosures": { + "https://markappmedia.site/vendor.json": { + "timestamp": "2026-03-14T22:01:59.001Z", + "disclosures": [] + } + }, + "components": [ + { + "componentType": "bidder", + "componentName": "harion", + "aliasOf": null, + "gvlid": 1406, + "disclosureURL": "https://markappmedia.site/vendor.json" + } + ] +} \ No newline at end of file diff --git a/metadata/modules/holidBidAdapter.json b/metadata/modules/holidBidAdapter.json index 32c0d348000..e7f39170a7e 100644 --- a/metadata/modules/holidBidAdapter.json +++ b/metadata/modules/holidBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://ads.holid.io/devicestorage.json": { - "timestamp": "2026-01-21T15:16:46.764Z", + "timestamp": "2026-03-14T22:01:59.345Z", "disclosures": [ { "identifier": "uids", diff --git a/metadata/modules/hybridBidAdapter.json b/metadata/modules/hybridBidAdapter.json index 71e979382ca..37cf38619ce 100644 --- a/metadata/modules/hybridBidAdapter.json +++ b/metadata/modules/hybridBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://st.hybrid.ai/policy/deviceStorage.json": { - "timestamp": "2026-01-21T15:16:47.041Z", + "timestamp": "2026-03-14T22:01:59.581Z", "disclosures": [] } }, diff --git a/metadata/modules/id5IdSystem.json b/metadata/modules/id5IdSystem.json index d672e3d95e8..84b13e339ef 100644 --- a/metadata/modules/id5IdSystem.json +++ b/metadata/modules/id5IdSystem.json @@ -2,8 +2,250 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://id5-sync.com/tcf/disclosures.json": { - "timestamp": "2026-01-21T15:16:47.254Z", - "disclosures": [] + "timestamp": "2026-03-14T22:01:59.807Z", + "disclosures": [ + { + "identifier": "id5id", + "type": "web", + "maxAgeSeconds": 7776000, + "purposes": [ + 1, + 3 + ] + }, + { + "identifier": "id5id_exp", + "type": "web", + "maxAgeSeconds": 7776000, + "purposes": [ + 1, + 3 + ] + }, + { + "identifier": "id5id_last", + "type": "web", + "maxAgeSeconds": 7776000, + "purposes": [ + 1, + 3 + ] + }, + { + "identifier": "id5id_last_exp", + "type": "web", + "maxAgeSeconds": 7776000, + "purposes": [ + 1, + 3 + ] + }, + { + "identifier": "id5id_cached_consent_data", + "type": "web", + "maxAgeSeconds": 2592000, + "purposes": [ + 1, + 3 + ] + }, + { + "identifier": "id5id_cached_consent_data_exp", + "type": "web", + "maxAgeSeconds": 2592000, + "purposes": [ + 1, + 3 + ] + }, + { + "identifier": "id5id_cached_pd_{partnerId}", + "type": "web", + "maxAgeSeconds": 2592000, + "purposes": [ + 1, + 3 + ] + }, + { + "identifier": "id5id_cached_pd_exp", + "type": "web", + "maxAgeSeconds": 2592000, + "purposes": [ + 1, + 3 + ] + }, + { + "identifier": "id5id_cached_pd", + "type": "web", + "maxAgeSeconds": 2592000, + "purposes": [ + 1, + 3 + ] + }, + { + "identifier": "id5id_privacy", + "type": "web", + "maxAgeSeconds": 2592000, + "purposes": [ + 1, + 3 + ] + }, + { + "identifier": "id5id_privacy_exp", + "type": "web", + "maxAgeSeconds": 2592000, + "purposes": [ + 1, + 3 + ] + }, + { + "identifier": "id5id_{partnerId}_nb", + "type": "web", + "maxAgeSeconds": 7776000, + "purposes": [ + 1, + 3 + ] + }, + { + "identifier": "id5id_{partnerId}_nb_exp", + "type": "web", + "maxAgeSeconds": 7776000, + "purposes": [ + 1, + 3 + ] + }, + { + "identifier": "id5id_v2_{cacheId}", + "type": "web", + "maxAgeSeconds": 1296000, + "purposes": [ + 1, + 3 + ] + }, + { + "identifier": "id5id_v2_signature", + "type": "web", + "maxAgeSeconds": 1296000, + "purposes": [ + 1, + 3 + ] + }, + { + "identifier": "id5id_extensions", + "type": "web", + "maxAgeSeconds": 28800, + "purposes": [ + 1, + 3 + ] + }, + { + "identifier": "id5id_cached_segments_{partnerId}", + "type": "web", + "maxAgeSeconds": 2592000, + "purposes": [ + 1, + 3 + ] + }, + { + "identifier": "id5id_cached_segments_{partnerId}_exp", + "type": "web", + "maxAgeSeconds": 2592000, + "purposes": [ + 1, + 3 + ] + }, + { + "identifier": "id5_trueLink_privacy", + "type": "web", + "maxAgeSeconds": 2592000, + "purposes": [ + 1, + 3 + ] + }, + { + "identifier": "id5-tl-ts", + "type": "cookie", + "maxAgeSeconds": 7776000, + "cookieRefresh": true, + "purposes": [ + 1, + 3 + ] + }, + { + "identifier": "id5-tl-redirect-timestamp", + "type": "cookie", + "maxAgeSeconds": 7776000, + "cookieRefresh": true, + "purposes": [ + 1, + 3 + ] + }, + { + "identifier": "id5-tl-redirect-fail", + "type": "cookie", + "maxAgeSeconds": 604800, + "cookieRefresh": true, + "purposes": [ + 1, + 3 + ] + }, + { + "identifier": "id5-tl-optout", + "type": "cookie", + "maxAgeSeconds": 7776000, + "cookieRefresh": true, + "purposes": [ + 1, + 3 + ] + }, + { + "identifier": "id5-true-link", + "type": "cookie", + "maxAgeSeconds": 7776000, + "cookieRefresh": true, + "purposes": [ + 1, + 3 + ] + }, + { + "identifier": "id5-true-link-refresh", + "type": "cookie", + "maxAgeSeconds": 7776000, + "cookieRefresh": true, + "purposes": [ + 1, + 3 + ] + }, + { + "identifier": "id5-true-link-refresh-exp", + "type": "cookie", + "maxAgeSeconds": 7776000, + "cookieRefresh": true, + "purposes": [ + 1, + 3 + ] + } + ] } }, "components": [ diff --git a/metadata/modules/identityLinkIdSystem.json b/metadata/modules/identityLinkIdSystem.json index d745841ac6e..9fdccd08fc5 100644 --- a/metadata/modules/identityLinkIdSystem.json +++ b/metadata/modules/identityLinkIdSystem.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://tcf.ats.rlcdn.com/device-storage-disclosure.json": { - "timestamp": "2026-01-21T15:16:47.533Z", + "timestamp": "2026-03-14T22:02:00.099Z", "disclosures": [ { "identifier": "_lr_retry_request", diff --git a/metadata/modules/illuminBidAdapter.json b/metadata/modules/illuminBidAdapter.json index c219534cb5d..af522c3fa5e 100644 --- a/metadata/modules/illuminBidAdapter.json +++ b/metadata/modules/illuminBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://admanmedia.com/deviceStorage.json": { - "timestamp": "2026-01-21T15:16:47.554Z", + "timestamp": "2026-03-14T22:02:00.124Z", "disclosures": [] } }, diff --git a/metadata/modules/impactifyBidAdapter.json b/metadata/modules/impactifyBidAdapter.json index f56e8b665b9..29d9564995d 100644 --- a/metadata/modules/impactifyBidAdapter.json +++ b/metadata/modules/impactifyBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://ad.impactify.io/tcfvendors.json": { - "timestamp": "2026-01-21T15:16:47.845Z", + "timestamp": "2026-03-14T22:02:00.414Z", "disclosures": [ { "identifier": "_im*", diff --git a/metadata/modules/improvedigitalBidAdapter.json b/metadata/modules/improvedigitalBidAdapter.json index 2f35776ec38..cb84525d993 100644 --- a/metadata/modules/improvedigitalBidAdapter.json +++ b/metadata/modules/improvedigitalBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://sellers.improvedigital.com/tcf-cookies.json": { - "timestamp": "2026-01-21T15:16:48.129Z", + "timestamp": "2026-03-14T22:02:00.713Z", "disclosures": [ { "identifier": "tuuid", diff --git a/metadata/modules/inmobiBidAdapter.json b/metadata/modules/inmobiBidAdapter.json index e3a538055dc..d4c0b639e9d 100644 --- a/metadata/modules/inmobiBidAdapter.json +++ b/metadata/modules/inmobiBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://publisher.inmobi.com/public/disclosure": { - "timestamp": "2026-01-21T15:16:48.130Z", + "timestamp": "2026-03-14T22:02:00.713Z", "disclosures": [] } }, diff --git a/metadata/modules/insticatorBidAdapter.json b/metadata/modules/insticatorBidAdapter.json index a0c29a3b05e..ed9d069e59c 100644 --- a/metadata/modules/insticatorBidAdapter.json +++ b/metadata/modules/insticatorBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://cdn.insticator.com/iab/device-storage-disclosure.json": { - "timestamp": "2026-01-21T15:16:48.208Z", + "timestamp": "2026-03-14T22:02:00.831Z", "disclosures": [ { "identifier": "visitorGeo", diff --git a/metadata/modules/quantcastBidAdapter.json b/metadata/modules/insuradsBidAdapter.json similarity index 50% rename from metadata/modules/quantcastBidAdapter.json rename to metadata/modules/insuradsBidAdapter.json index 605822743f3..16588e6a6d7 100644 --- a/metadata/modules/quantcastBidAdapter.json +++ b/metadata/modules/insuradsBidAdapter.json @@ -1,37 +1,31 @@ { "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { - "https://www.quantcast.com/.well-known/devicestorage.json": { - "timestamp": "2026-01-21T15:16:59.937Z", + "https://www.insurads.com/tcf-vdsod.json": { + "timestamp": "2026-03-14T22:02:00.894Z", "disclosures": [ { - "identifier": "__qca", + "identifier": "___iat_ses", "type": "cookie", - "maxAgeSeconds": 33868800, - "cookieRefresh": false, + "maxAgeSeconds": 1800, + "cookieRefresh": true, "purposes": [ 1, 2, - 3, - 4, 7, - 8, 9, 10 ] }, { - "identifier": "__dlt", + "identifier": "___iat_vis", "type": "cookie", - "maxAgeSeconds": 0, - "cookieRefresh": false, + "maxAgeSeconds": 15552000, + "cookieRefresh": true, "purposes": [ 1, 2, - 3, - 4, 7, - 8, 9, 10 ] @@ -42,10 +36,10 @@ "components": [ { "componentType": "bidder", - "componentName": "quantcast", + "componentName": "insurads", "aliasOf": null, - "gvlid": "11", - "disclosureURL": "https://www.quantcast.com/.well-known/devicestorage.json" + "gvlid": 596, + "disclosureURL": "https://www.insurads.com/tcf-vdsod.json" } ] } \ No newline at end of file diff --git a/metadata/modules/intentIqIdSystem.json b/metadata/modules/intentIqIdSystem.json index 76d0db7687c..8cacf476f6b 100644 --- a/metadata/modules/intentIqIdSystem.json +++ b/metadata/modules/intentIqIdSystem.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://agent.intentiq.com/GDPR/gdpr.json": { - "timestamp": "2026-01-21T15:16:48.367Z", + "timestamp": "2026-03-14T22:02:01.202Z", "disclosures": [] } }, diff --git a/metadata/modules/invibesBidAdapter.json b/metadata/modules/invibesBidAdapter.json index 802498d5930..bf0778356e8 100644 --- a/metadata/modules/invibesBidAdapter.json +++ b/metadata/modules/invibesBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://tcf.invibes.com/deviceStorage.json": { - "timestamp": "2026-01-21T15:16:48.425Z", + "timestamp": "2026-03-14T22:02:01.261Z", "disclosures": [ { "identifier": "ivvcap", diff --git a/metadata/modules/ipromBidAdapter.json b/metadata/modules/ipromBidAdapter.json index a9767514ee7..d628e552649 100644 --- a/metadata/modules/ipromBidAdapter.json +++ b/metadata/modules/ipromBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://core.iprom.net/info/deviceStorage.json": { - "timestamp": "2026-01-21T15:16:48.800Z", + "timestamp": "2026-03-14T22:02:01.596Z", "disclosures": [] } }, diff --git a/metadata/modules/ixBidAdapter.json b/metadata/modules/ixBidAdapter.json index 52b765cb72c..70550fa3f5f 100644 --- a/metadata/modules/ixBidAdapter.json +++ b/metadata/modules/ixBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://cdn.indexexchange.com/device_storage_disclosure.json": { - "timestamp": "2026-01-21T15:16:49.270Z", + "timestamp": "2026-03-14T22:02:02.085Z", "disclosures": [ { "identifier": "ix_features", diff --git a/metadata/modules/justIdSystem.json b/metadata/modules/justIdSystem.json index 89d68f3ea81..7b0f158f03c 100644 --- a/metadata/modules/justIdSystem.json +++ b/metadata/modules/justIdSystem.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://audience-solutions.com/.well-known/deviceStorage.json": { - "timestamp": "2026-01-21T15:16:49.303Z", + "timestamp": "2026-03-14T22:02:02.363Z", "disclosures": [ { "identifier": "__jtuid", diff --git a/metadata/modules/justpremiumBidAdapter.json b/metadata/modules/justpremiumBidAdapter.json index 6115b2cf5b8..727d51b2211 100644 --- a/metadata/modules/justpremiumBidAdapter.json +++ b/metadata/modules/justpremiumBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://cdn.justpremium.com/devicestoragedisclosures.json": { - "timestamp": "2026-01-21T15:16:49.828Z", + "timestamp": "2026-03-14T22:02:02.891Z", "disclosures": [] } }, diff --git a/metadata/modules/jwplayerBidAdapter.json b/metadata/modules/jwplayerBidAdapter.json index bea808a6708..720a59c48c9 100644 --- a/metadata/modules/jwplayerBidAdapter.json +++ b/metadata/modules/jwplayerBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://www.jwplayer.com/devicestorage.json": { - "timestamp": "2026-01-21T15:16:49.848Z", + "timestamp": "2026-03-14T22:02:03.137Z", "disclosures": [] } }, diff --git a/metadata/modules/kargoBidAdapter.json b/metadata/modules/kargoBidAdapter.json index 3251e4603d6..965e233a12e 100644 --- a/metadata/modules/kargoBidAdapter.json +++ b/metadata/modules/kargoBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://storage.cloud.kargo.com/device_storage_disclosure.json": { - "timestamp": "2026-01-21T15:16:50.140Z", + "timestamp": "2026-03-14T22:02:03.342Z", "disclosures": [ { "identifier": "krg_crb", diff --git a/metadata/modules/kueezRtbBidAdapter.json b/metadata/modules/kueezRtbBidAdapter.json index 64f287d76f5..71fcf092a85 100644 --- a/metadata/modules/kueezRtbBidAdapter.json +++ b/metadata/modules/kueezRtbBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://en.kueez.com/tcf.json": { - "timestamp": "2026-01-21T15:16:50.192Z", + "timestamp": "2026-03-14T22:02:03.367Z", "disclosures": [ { "identifier": "ck48wz12sqj7", diff --git a/metadata/modules/leagueMBidAdapter.json b/metadata/modules/leagueMBidAdapter.json new file mode 100644 index 00000000000..f4b02875bbb --- /dev/null +++ b/metadata/modules/leagueMBidAdapter.json @@ -0,0 +1,13 @@ +{ + "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", + "disclosures": {}, + "components": [ + { + "componentType": "bidder", + "componentName": "leagueM", + "aliasOf": null, + "gvlid": null, + "disclosureURL": null + } + ] +} \ No newline at end of file diff --git a/metadata/modules/limelightDigitalBidAdapter.json b/metadata/modules/limelightDigitalBidAdapter.json index f75b4f7757b..dd78b521fdb 100644 --- a/metadata/modules/limelightDigitalBidAdapter.json +++ b/metadata/modules/limelightDigitalBidAdapter.json @@ -2,11 +2,11 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://policy.iion.io/deviceStorage.json": { - "timestamp": "2026-01-21T15:16:50.236Z", + "timestamp": "2026-03-14T22:02:03.408Z", "disclosures": [] }, "https://orangeclickmedia.com/device_storage_disclosure.json": { - "timestamp": "2026-01-21T15:16:50.390Z", + "timestamp": "2026-03-14T22:02:03.456Z", "disclosures": [] } }, @@ -32,13 +32,6 @@ "gvlid": 1358, "disclosureURL": "https://policy.iion.io/deviceStorage.json" }, - { - "componentType": "bidder", - "componentName": "apester", - "aliasOf": "limelightDigital", - "gvlid": null, - "disclosureURL": null - }, { "componentType": "bidder", "componentName": "adsyield", @@ -104,42 +97,42 @@ }, { "componentType": "bidder", - "componentName": "adnimation", + "componentName": "rtbdemand", "aliasOf": "limelightDigital", "gvlid": null, "disclosureURL": null }, { "componentType": "bidder", - "componentName": "rtbdemand", + "componentName": "altstar", "aliasOf": "limelightDigital", "gvlid": null, "disclosureURL": null }, { "componentType": "bidder", - "componentName": "altstar", + "componentName": "vaayaMedia", "aliasOf": "limelightDigital", "gvlid": null, "disclosureURL": null }, { "componentType": "bidder", - "componentName": "vaayaMedia", + "componentName": "performist", "aliasOf": "limelightDigital", "gvlid": null, "disclosureURL": null }, { "componentType": "bidder", - "componentName": "performist", + "componentName": "oveeo", "aliasOf": "limelightDigital", "gvlid": null, "disclosureURL": null }, { "componentType": "bidder", - "componentName": "oveeo", + "componentName": "embimedia", "aliasOf": "limelightDigital", "gvlid": null, "disclosureURL": null diff --git a/metadata/modules/liveIntentIdSystem.json b/metadata/modules/liveIntentIdSystem.json index 8b54b888d02..d896e5432bf 100644 --- a/metadata/modules/liveIntentIdSystem.json +++ b/metadata/modules/liveIntentIdSystem.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://b-code.liadm.com/deviceStorage.json": { - "timestamp": "2026-01-21T15:16:50.390Z", + "timestamp": "2026-03-14T22:02:03.457Z", "disclosures": [ { "identifier": "_lc2_fpi", diff --git a/metadata/modules/liveIntentRtdProvider.json b/metadata/modules/liveIntentRtdProvider.json index 1e5085a81df..5beaf313598 100644 --- a/metadata/modules/liveIntentRtdProvider.json +++ b/metadata/modules/liveIntentRtdProvider.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://b-code.liadm.com/deviceStorage.json": { - "timestamp": "2026-01-21T15:16:50.532Z", + "timestamp": "2026-03-14T22:02:03.469Z", "disclosures": [ { "identifier": "_lc2_fpi", diff --git a/metadata/modules/livewrappedBidAdapter.json b/metadata/modules/livewrappedBidAdapter.json index 6b5a309d7da..c1b0155cebc 100644 --- a/metadata/modules/livewrappedBidAdapter.json +++ b/metadata/modules/livewrappedBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://content.lwadm.com/deviceStorageDisclosure.json": { - "timestamp": "2026-01-21T15:16:50.533Z", + "timestamp": "2026-03-14T22:02:03.470Z", "disclosures": [ { "identifier": "uid", diff --git a/metadata/modules/locIdSystem.json b/metadata/modules/locIdSystem.json new file mode 100644 index 00000000000..87e8fc03475 --- /dev/null +++ b/metadata/modules/locIdSystem.json @@ -0,0 +1,20 @@ +{ + "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", + "disclosures": {}, + "components": [ + { + "componentType": "userId", + "componentName": "locId", + "gvlid": null, + "disclosureURL": null, + "aliasOf": null + }, + { + "componentType": "userId", + "componentName": "locid", + "gvlid": null, + "disclosureURL": null, + "aliasOf": "locId" + } + ] +} \ No newline at end of file diff --git a/metadata/modules/loopmeBidAdapter.json b/metadata/modules/loopmeBidAdapter.json index 61d4939283f..a1ce0cf0696 100644 --- a/metadata/modules/loopmeBidAdapter.json +++ b/metadata/modules/loopmeBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://co.loopme.com/deviceStorageDisclosure.json": { - "timestamp": "2026-01-21T15:16:50.558Z", + "timestamp": "2026-03-14T22:02:03.813Z", "disclosures": [] } }, diff --git a/metadata/modules/lotamePanoramaIdSystem.json b/metadata/modules/lotamePanoramaIdSystem.json index 7ddaac4b4e8..e0dbb7d8223 100644 --- a/metadata/modules/lotamePanoramaIdSystem.json +++ b/metadata/modules/lotamePanoramaIdSystem.json @@ -2,8 +2,65 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://tags.crwdcntrl.net/privacy/tcf-purposes.json": { - "timestamp": "2026-01-21T15:16:50.626Z", + "timestamp": "2026-03-14T22:02:03.926Z", "disclosures": [ + { + "identifier": "_cc_id", + "type": "cookie", + "maxAgeSeconds": 23328000, + "cookieRefresh": true, + "purposes": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11 + ] + }, + { + "identifier": "_cc_cc", + "type": "cookie", + "maxAgeSeconds": 23328000, + "cookieRefresh": true, + "purposes": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11 + ] + }, + { + "identifier": "_cc_aud", + "type": "cookie", + "maxAgeSeconds": 23328000, + "cookieRefresh": true, + "purposes": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11 + ] + }, { "identifier": "lotame_domain_check", "type": "cookie", @@ -23,6 +80,25 @@ 11 ] }, + { + "identifier": "_pubcid", + "type": "cookie", + "maxAgeSeconds": 23328000, + "cookieRefresh": true, + "purposes": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11 + ] + }, { "identifier": "panoramaId", "type": "web", @@ -124,6 +200,23 @@ 10, 11 ] + }, + { + "identifier": "_pubcid", + "type": "web", + "purposes": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11 + ] } ] } diff --git a/metadata/modules/luponmediaBidAdapter.json b/metadata/modules/luponmediaBidAdapter.json index 692bc08f9ed..c62987cbce7 100644 --- a/metadata/modules/luponmediaBidAdapter.json +++ b/metadata/modules/luponmediaBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://luponmedia.com/vendor_device_storage.json": { - "timestamp": "2026-01-21T15:16:50.642Z", + "timestamp": "2026-03-14T22:02:03.989Z", "disclosures": [] } }, diff --git a/metadata/modules/madvertiseBidAdapter.json b/metadata/modules/madvertiseBidAdapter.json index 93d00207b7f..5bf1d51d8e7 100644 --- a/metadata/modules/madvertiseBidAdapter.json +++ b/metadata/modules/madvertiseBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://adserver.bluestack.app/deviceStorage.json": { - "timestamp": "2026-01-21T15:16:51.084Z", + "timestamp": "2026-03-14T22:02:04.404Z", "disclosures": [] } }, diff --git a/metadata/modules/magniteBidAdapter.json b/metadata/modules/magniteBidAdapter.json new file mode 100644 index 00000000000..789c5c69182 --- /dev/null +++ b/metadata/modules/magniteBidAdapter.json @@ -0,0 +1,18 @@ +{ + "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", + "disclosures": { + "https://gdpr.rubiconproject.com/dvplus/devicestoragedisclosure.json": { + "timestamp": "2026-03-14T22:02:04.752Z", + "disclosures": [] + } + }, + "components": [ + { + "componentType": "bidder", + "componentName": "magnite", + "aliasOf": null, + "gvlid": 52, + "disclosureURL": "https://gdpr.rubiconproject.com/dvplus/devicestoragedisclosure.json" + } + ] +} \ No newline at end of file diff --git a/metadata/modules/marsmediaBidAdapter.json b/metadata/modules/marsmediaBidAdapter.json index 0fada8419a6..9262e678022 100644 --- a/metadata/modules/marsmediaBidAdapter.json +++ b/metadata/modules/marsmediaBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://mars.media/apis/tcf-v2.json": { - "timestamp": "2026-01-21T15:16:51.439Z", + "timestamp": "2026-03-14T22:02:04.988Z", "disclosures": [] } }, diff --git a/metadata/modules/mediaConsortiumBidAdapter.json b/metadata/modules/mediaConsortiumBidAdapter.json index c7e5eaf0103..2089c0230fa 100644 --- a/metadata/modules/mediaConsortiumBidAdapter.json +++ b/metadata/modules/mediaConsortiumBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://cdn.hubvisor.io/assets/deviceStorage.json": { - "timestamp": "2026-01-21T15:16:51.547Z", + "timestamp": "2026-03-14T22:02:05.099Z", "disclosures": [ { "identifier": "hbv:turbo-cmp", diff --git a/metadata/modules/mediaforceBidAdapter.json b/metadata/modules/mediaforceBidAdapter.json index 3246a3f6fc2..e02a1f722a5 100644 --- a/metadata/modules/mediaforceBidAdapter.json +++ b/metadata/modules/mediaforceBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://comparisons.org/privacy.json": { - "timestamp": "2026-01-21T15:16:51.676Z", + "timestamp": "2026-03-14T22:02:05.227Z", "disclosures": [] } }, diff --git a/metadata/modules/mediafuseBidAdapter.json b/metadata/modules/mediafuseBidAdapter.json index de1ccf74863..d2ef46f4aa0 100644 --- a/metadata/modules/mediafuseBidAdapter.json +++ b/metadata/modules/mediafuseBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://acdn.adnxs.com/gvl/1d/xandrdevicestoragedisclosures.json": { - "timestamp": "2026-01-21T15:16:51.693Z", + "timestamp": "2026-03-14T22:02:05.267Z", "disclosures": [] } }, diff --git a/metadata/modules/mediagoBidAdapter.json b/metadata/modules/mediagoBidAdapter.json index 63a87575cfb..1a799beb444 100644 --- a/metadata/modules/mediagoBidAdapter.json +++ b/metadata/modules/mediagoBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://cdn.mediago.io/js/tcf.json": { - "timestamp": "2026-01-21T15:16:51.693Z", + "timestamp": "2026-03-14T22:02:05.268Z", "disclosures": [] } }, diff --git a/metadata/modules/mediakeysBidAdapter.json b/metadata/modules/mediakeysBidAdapter.json index 232b085d059..9481792d601 100644 --- a/metadata/modules/mediakeysBidAdapter.json +++ b/metadata/modules/mediakeysBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://s3.eu-west-3.amazonaws.com/adserving.resourcekeys.com/deviceStorageDisclosure.json": { - "timestamp": "2026-01-21T15:16:51.810Z", + "timestamp": "2026-03-14T22:02:05.334Z", "disclosures": [] } }, diff --git a/metadata/modules/medianetBidAdapter.json b/metadata/modules/medianetBidAdapter.json index e6ad2a0a506..f1fa7d445cd 100644 --- a/metadata/modules/medianetBidAdapter.json +++ b/metadata/modules/medianetBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://www.media.net/tcfv2/gvl/deviceStorage.json": { - "timestamp": "2026-01-21T15:16:52.094Z", + "timestamp": "2026-03-14T22:02:05.620Z", "disclosures": [ { "identifier": "_mNExInsl", @@ -246,7 +246,7 @@ ] }, "https://trustedstack.com/tcf/gvl/deviceStorage.json": { - "timestamp": "2026-01-21T15:16:52.205Z", + "timestamp": "2026-03-14T22:02:05.660Z", "disclosures": [ { "identifier": "usp_status", diff --git a/metadata/modules/mediasquareBidAdapter.json b/metadata/modules/mediasquareBidAdapter.json index af36fd36c88..70a4396e8ed 100644 --- a/metadata/modules/mediasquareBidAdapter.json +++ b/metadata/modules/mediasquareBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://mediasquare.fr/deviceStorage.json": { - "timestamp": "2026-01-21T15:16:52.270Z", + "timestamp": "2026-03-14T22:02:05.693Z", "disclosures": [] } }, diff --git a/metadata/modules/mgidBidAdapter.json b/metadata/modules/mgidBidAdapter.json index d7336894b00..4c6d5f80f47 100644 --- a/metadata/modules/mgidBidAdapter.json +++ b/metadata/modules/mgidBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://www.mgid.com/assets/devicestorage.json": { - "timestamp": "2026-01-21T15:16:52.804Z", + "timestamp": "2026-03-14T22:02:06.247Z", "disclosures": [] } }, diff --git a/metadata/modules/mgidRtdProvider.json b/metadata/modules/mgidRtdProvider.json index 783780e0c0f..9361aac4544 100644 --- a/metadata/modules/mgidRtdProvider.json +++ b/metadata/modules/mgidRtdProvider.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://www.mgid.com/assets/devicestorage.json": { - "timestamp": "2026-01-21T15:16:52.890Z", + "timestamp": "2026-03-14T22:02:06.312Z", "disclosures": [] } }, diff --git a/metadata/modules/mgidXBidAdapter.json b/metadata/modules/mgidXBidAdapter.json index 28b909ab5ed..d6ae6f2a58e 100644 --- a/metadata/modules/mgidXBidAdapter.json +++ b/metadata/modules/mgidXBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://www.mgid.com/assets/devicestorage.json": { - "timestamp": "2026-01-21T15:16:52.890Z", + "timestamp": "2026-03-14T22:02:06.312Z", "disclosures": [] } }, diff --git a/metadata/modules/dmdIdSystem.json b/metadata/modules/mileBidAdapter.json similarity index 58% rename from metadata/modules/dmdIdSystem.json rename to metadata/modules/mileBidAdapter.json index 1bad2dec26e..ba3e8b683fc 100644 --- a/metadata/modules/dmdIdSystem.json +++ b/metadata/modules/mileBidAdapter.json @@ -3,11 +3,11 @@ "disclosures": {}, "components": [ { - "componentType": "userId", - "componentName": "dmdId", + "componentType": "bidder", + "componentName": "mile", + "aliasOf": null, "gvlid": null, - "disclosureURL": null, - "aliasOf": null + "disclosureURL": null } ] } \ No newline at end of file diff --git a/metadata/modules/minutemediaBidAdapter.json b/metadata/modules/minutemediaBidAdapter.json index 52975bf0e9b..97c5b22cad6 100644 --- a/metadata/modules/minutemediaBidAdapter.json +++ b/metadata/modules/minutemediaBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://disclosures.mmctsvc.com/device-storage.json": { - "timestamp": "2026-01-21T15:16:52.891Z", + "timestamp": "2026-03-14T22:02:06.313Z", "disclosures": [] } }, diff --git a/metadata/modules/missenaBidAdapter.json b/metadata/modules/missenaBidAdapter.json index 50dbae57589..51d17ed2547 100644 --- a/metadata/modules/missenaBidAdapter.json +++ b/metadata/modules/missenaBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://ad.missena.io/iab.json": { - "timestamp": "2026-01-21T15:16:52.910Z", + "timestamp": "2026-03-14T22:02:06.331Z", "disclosures": [] } }, diff --git a/metadata/modules/mobianRtdProvider.json b/metadata/modules/mobianRtdProvider.json index 559a86ed33e..afa8e75fc0e 100644 --- a/metadata/modules/mobianRtdProvider.json +++ b/metadata/modules/mobianRtdProvider.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://js.outcomes.net/tcf.json": { - "timestamp": "2026-01-21T15:16:52.961Z", + "timestamp": "2026-03-14T22:02:06.402Z", "disclosures": [] } }, diff --git a/metadata/modules/mobkoiBidAdapter.json b/metadata/modules/mobkoiBidAdapter.json index b7c46364245..ea620e11a0b 100644 --- a/metadata/modules/mobkoiBidAdapter.json +++ b/metadata/modules/mobkoiBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://cdn.maximus.mobkoi.com/tcf/deviceStorageDisclosure.json": { - "timestamp": "2026-01-21T15:16:52.980Z", + "timestamp": "2026-03-14T22:02:06.419Z", "disclosures": [] } }, diff --git a/metadata/modules/mobkoiIdSystem.json b/metadata/modules/mobkoiIdSystem.json index 50b2b2c15d4..bb900b76877 100644 --- a/metadata/modules/mobkoiIdSystem.json +++ b/metadata/modules/mobkoiIdSystem.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://cdn.maximus.mobkoi.com/tcf/deviceStorageDisclosure.json": { - "timestamp": "2026-01-21T15:16:53.000Z", + "timestamp": "2026-03-14T22:02:06.437Z", "disclosures": [] } }, diff --git a/metadata/modules/msftBidAdapter.json b/metadata/modules/msftBidAdapter.json index cebddea5825..751e4040f2b 100644 --- a/metadata/modules/msftBidAdapter.json +++ b/metadata/modules/msftBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://acdn.adnxs.com/gvl/1d/xandrdevicestoragedisclosures.json": { - "timestamp": "2026-01-21T15:16:53.001Z", + "timestamp": "2026-03-14T22:02:06.437Z", "disclosures": [] } }, diff --git a/metadata/modules/nativeryBidAdapter.json b/metadata/modules/nativeryBidAdapter.json index f77162977c5..14409dbb1f4 100644 --- a/metadata/modules/nativeryBidAdapter.json +++ b/metadata/modules/nativeryBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://cdnimg.nativery.com/widget/js/deviceStorageDisclosure.json": { - "timestamp": "2026-01-21T15:16:53.001Z", + "timestamp": "2026-03-14T22:02:06.438Z", "disclosures": [] } }, diff --git a/metadata/modules/nativoBidAdapter.json b/metadata/modules/nativoBidAdapter.json index 9f6f3ecc4ad..9c7e5071724 100644 --- a/metadata/modules/nativoBidAdapter.json +++ b/metadata/modules/nativoBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://iab.nativo.com/tcf-disclosures.json": { - "timestamp": "2026-01-21T15:16:53.312Z", + "timestamp": "2026-03-14T22:02:06.752Z", "disclosures": [] } }, diff --git a/metadata/modules/newspassidBidAdapter.json b/metadata/modules/newspassidBidAdapter.json index c4d372be80c..3ab54085739 100644 --- a/metadata/modules/newspassidBidAdapter.json +++ b/metadata/modules/newspassidBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://www.aditude.com/storageaccess.json": { - "timestamp": "2026-01-21T15:16:53.353Z", + "timestamp": "2026-03-14T22:02:06.780Z", "disclosures": [] } }, diff --git a/metadata/modules/nextMillenniumBidAdapter.json b/metadata/modules/nextMillenniumBidAdapter.json index 67ed02b3065..c4def05e807 100644 --- a/metadata/modules/nextMillenniumBidAdapter.json +++ b/metadata/modules/nextMillenniumBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://nextmillennium.io/deviceStorage.json": { - "timestamp": "2026-01-21T15:16:53.353Z", + "timestamp": "2026-03-14T22:02:06.781Z", "disclosures": [] } }, diff --git a/metadata/modules/nextrollBidAdapter.json b/metadata/modules/nextrollBidAdapter.json index 34f85259bd2..a135f7c6e68 100644 --- a/metadata/modules/nextrollBidAdapter.json +++ b/metadata/modules/nextrollBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://s.adroll.com/shares/device_storage.json": { - "timestamp": "2026-01-21T15:16:53.462Z", + "timestamp": "2026-03-14T22:02:06.828Z", "disclosures": [ { "identifier": "__adroll_fpc", diff --git a/metadata/modules/nexx360BidAdapter.json b/metadata/modules/nexx360BidAdapter.json index 96a33e5532f..52c98b840bf 100644 --- a/metadata/modules/nexx360BidAdapter.json +++ b/metadata/modules/nexx360BidAdapter.json @@ -2,19 +2,19 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://fast.nexx360.io/deviceStorage.json": { - "timestamp": "2026-01-21T15:16:54.527Z", + "timestamp": "2026-03-14T22:02:07.684Z", "disclosures": [] }, "https://static.first-id.fr/tcf/cookie.json": { - "timestamp": "2026-01-21T15:16:53.741Z", + "timestamp": "2026-03-14T22:02:07.085Z", "disclosures": [] }, "https://i.plug.it/banners/js/deviceStorage.json": { - "timestamp": "2026-01-21T15:16:53.768Z", + "timestamp": "2026-03-14T22:02:07.119Z", "disclosures": [] }, "https://player.glomex.com/.well-known/deviceStorage.json": { - "timestamp": "2026-01-21T15:16:54.132Z", + "timestamp": "2026-03-14T22:02:07.243Z", "disclosures": [ { "identifier": "glomexUser", @@ -46,7 +46,7 @@ ] }, "https://gdpr.pubx.ai/devicestoragedisclosure.json": { - "timestamp": "2026-01-21T15:16:54.132Z", + "timestamp": "2026-03-14T22:02:07.243Z", "disclosures": [ { "identifier": "pubx:defaults", @@ -61,7 +61,7 @@ ] }, "https://yieldbird.com/devicestorage.json": { - "timestamp": "2026-01-21T15:16:54.171Z", + "timestamp": "2026-03-14T22:02:07.310Z", "disclosures": [] } }, diff --git a/metadata/modules/nobidBidAdapter.json b/metadata/modules/nobidBidAdapter.json index e59b6a10941..39b9f6375b2 100644 --- a/metadata/modules/nobidBidAdapter.json +++ b/metadata/modules/nobidBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://public.servenobid.com/gdpr_tcf/vendor_device_storage_operational_disclosures.json": { - "timestamp": "2026-01-21T15:16:54.528Z", + "timestamp": "2026-03-14T22:02:07.685Z", "disclosures": [] } }, diff --git a/metadata/modules/nodalsAiRtdProvider.json b/metadata/modules/nodalsAiRtdProvider.json index b62b673da96..9a70208c6fe 100644 --- a/metadata/modules/nodalsAiRtdProvider.json +++ b/metadata/modules/nodalsAiRtdProvider.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://static.nodals.ai/vendor.json": { - "timestamp": "2026-01-21T15:16:54.543Z", + "timestamp": "2026-03-14T22:02:07.697Z", "disclosures": [ { "identifier": "localStorage", diff --git a/metadata/modules/novatiqIdSystem.json b/metadata/modules/novatiqIdSystem.json index 7f89bd60fcd..7b3e39972ed 100644 --- a/metadata/modules/novatiqIdSystem.json +++ b/metadata/modules/novatiqIdSystem.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://novatiq.com/privacy/iab/novatiq.json": { - "timestamp": "2026-01-21T15:16:56.488Z", + "timestamp": "2026-03-14T22:02:09.124Z", "disclosures": [ { "identifier": "novatiq", diff --git a/metadata/modules/oguryBidAdapter.json b/metadata/modules/oguryBidAdapter.json index e42e2f0be32..c06a56938c3 100644 --- a/metadata/modules/oguryBidAdapter.json +++ b/metadata/modules/oguryBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://privacy.ogury.co/disclosure.json": { - "timestamp": "2026-01-21T15:16:56.828Z", + "timestamp": "2026-03-14T22:02:09.450Z", "disclosures": [] } }, diff --git a/metadata/modules/omnidexBidAdapter.json b/metadata/modules/omnidexBidAdapter.json index df49e06a8f8..07c03e06a12 100644 --- a/metadata/modules/omnidexBidAdapter.json +++ b/metadata/modules/omnidexBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://www.omni-dex.io/devicestorage.json": { - "timestamp": "2026-01-21T15:16:56.874Z", + "timestamp": "2026-03-14T22:02:09.497Z", "disclosures": [ { "identifier": "ck48wz12sqj7", diff --git a/metadata/modules/omsBidAdapter.json b/metadata/modules/omsBidAdapter.json index ad4398c4181..6dae74d1b1e 100644 --- a/metadata/modules/omsBidAdapter.json +++ b/metadata/modules/omsBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://cdn.marphezis.com/tcf-vendor-disclosures.json": { - "timestamp": "2026-01-21T15:16:56.932Z", + "timestamp": "2026-03-14T22:02:09.582Z", "disclosures": [] } }, diff --git a/metadata/modules/onetagBidAdapter.json b/metadata/modules/onetagBidAdapter.json index 9bde7edca00..058ec1b6ae9 100644 --- a/metadata/modules/onetagBidAdapter.json +++ b/metadata/modules/onetagBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://onetag-cdn.com/privacy/tcf_storage.json": { - "timestamp": "2026-01-21T15:16:56.933Z", + "timestamp": "2026-03-14T22:02:09.583Z", "disclosures": [ { "identifier": "onetag_sid", diff --git a/metadata/modules/openwebBidAdapter.json b/metadata/modules/openwebBidAdapter.json index abb241f2a18..f6ccc348df9 100644 --- a/metadata/modules/openwebBidAdapter.json +++ b/metadata/modules/openwebBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://spotim-prd-static-assets.s3.amazonaws.com/iab/device-storage.json": { - "timestamp": "2026-01-21T15:16:57.299Z", + "timestamp": "2026-03-14T22:02:09.841Z", "disclosures": [] } }, diff --git a/metadata/modules/openxBidAdapter.json b/metadata/modules/openxBidAdapter.json index b36bfd7310a..1a0fa0a700d 100644 --- a/metadata/modules/openxBidAdapter.json +++ b/metadata/modules/openxBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://www.openx.com/device-storage.json": { - "timestamp": "2026-01-21T15:16:57.350Z", + "timestamp": "2026-03-14T22:02:09.883Z", "disclosures": [] } }, diff --git a/metadata/modules/operaadsBidAdapter.json b/metadata/modules/operaadsBidAdapter.json index 8c918c7098e..f085497cfd7 100644 --- a/metadata/modules/operaadsBidAdapter.json +++ b/metadata/modules/operaadsBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://res.adx.opera.com/dsd.json": { - "timestamp": "2026-01-21T15:16:57.428Z", + "timestamp": "2026-03-14T22:02:10.004Z", "disclosures": [] } }, diff --git a/metadata/modules/optidigitalBidAdapter.json b/metadata/modules/optidigitalBidAdapter.json index 0375f29d97d..683c535c10a 100644 --- a/metadata/modules/optidigitalBidAdapter.json +++ b/metadata/modules/optidigitalBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://scripts.opti-digital.com/deviceStorageDisclosure.json": { - "timestamp": "2026-01-21T15:16:57.592Z", + "timestamp": "2026-03-14T22:02:10.171Z", "disclosures": [] } }, diff --git a/metadata/modules/optoutBidAdapter.json b/metadata/modules/optoutBidAdapter.json index b54cf7e781f..949e55b93a5 100644 --- a/metadata/modules/optoutBidAdapter.json +++ b/metadata/modules/optoutBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://adserving.optoutadvertising.com/dsd": { - "timestamp": "2026-01-21T15:16:57.630Z", + "timestamp": "2026-03-14T22:02:10.226Z", "disclosures": [] } }, diff --git a/metadata/modules/orbidderBidAdapter.json b/metadata/modules/orbidderBidAdapter.json index 44010d8a596..1ce96df724d 100644 --- a/metadata/modules/orbidderBidAdapter.json +++ b/metadata/modules/orbidderBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://orbidder.otto.de/disclosure/dsd.json": { - "timestamp": "2026-01-21T15:16:57.892Z", + "timestamp": "2026-03-14T22:02:10.495Z", "disclosures": [] } }, diff --git a/metadata/modules/outbrainBidAdapter.json b/metadata/modules/outbrainBidAdapter.json index a9af8e17e3d..4e6331002a9 100644 --- a/metadata/modules/outbrainBidAdapter.json +++ b/metadata/modules/outbrainBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://www.outbrain.com/privacy/wp-json/privacy/v2/devicestorage.json": { - "timestamp": "2026-01-21T15:16:58.199Z", + "timestamp": "2026-03-14T22:02:10.805Z", "disclosures": [ { "identifier": "dicbo_id", diff --git a/metadata/modules/ozoneBidAdapter.json b/metadata/modules/ozoneBidAdapter.json index bf1fc0e1552..6ce9ad5f1ac 100644 --- a/metadata/modules/ozoneBidAdapter.json +++ b/metadata/modules/ozoneBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://prebid.the-ozone-project.com/deviceStorage.json": { - "timestamp": "2026-01-21T15:16:58.400Z", + "timestamp": "2026-03-14T22:02:11.034Z", "disclosures": [] } }, diff --git a/metadata/modules/pairIdSystem.json b/metadata/modules/pairIdSystem.json index 31cbc6f678a..30d67c661d2 100644 --- a/metadata/modules/pairIdSystem.json +++ b/metadata/modules/pairIdSystem.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://www.gstatic.com/iabtcf/deviceStorageDisclosure.json": { - "timestamp": "2026-01-21T15:16:58.545Z", + "timestamp": "2026-03-14T22:02:11.199Z", "disclosures": [ { "identifier": "__gads", diff --git a/metadata/modules/panxoBidAdapter.json b/metadata/modules/panxoBidAdapter.json new file mode 100644 index 00000000000..657a437ac4e --- /dev/null +++ b/metadata/modules/panxoBidAdapter.json @@ -0,0 +1,46 @@ +{ + "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", + "disclosures": { + "https://cdn.panxo.ai/tcf/device-storage.json": { + "timestamp": "2026-03-14T22:02:11.220Z", + "disclosures": [ + { + "identifier": "panxo_uid", + "type": "web", + "maxAgeSeconds": null, + "cookieRefresh": false, + "purposes": [ + 1 + ] + }, + { + "identifier": "panxo_aso_sync", + "type": "web", + "maxAgeSeconds": null, + "cookieRefresh": false, + "purposes": [ + 1 + ] + }, + { + "identifier": "panxo_debug", + "type": "web", + "maxAgeSeconds": null, + "cookieRefresh": false, + "purposes": [ + 1 + ] + } + ] + } + }, + "components": [ + { + "componentType": "bidder", + "componentName": "panxo", + "aliasOf": null, + "gvlid": 1527, + "disclosureURL": "https://cdn.panxo.ai/tcf/device-storage.json" + } + ] +} \ No newline at end of file diff --git a/metadata/modules/intersectionRtdProvider.json b/metadata/modules/panxoRtdProvider.json similarity index 84% rename from metadata/modules/intersectionRtdProvider.json rename to metadata/modules/panxoRtdProvider.json index ef41a3ebacf..5ad682b104d 100644 --- a/metadata/modules/intersectionRtdProvider.json +++ b/metadata/modules/panxoRtdProvider.json @@ -4,7 +4,7 @@ "components": [ { "componentType": "rtd", - "componentName": "intersection", + "componentName": "panxo", "gvlid": null, "disclosureURL": null } diff --git a/metadata/modules/performaxBidAdapter.json b/metadata/modules/performaxBidAdapter.json index f157ca8cce0..e18a9d4fe98 100644 --- a/metadata/modules/performaxBidAdapter.json +++ b/metadata/modules/performaxBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://www.performax.cz/device_storage.json": { - "timestamp": "2026-01-21T15:16:58.578Z", + "timestamp": "2026-03-14T22:02:11.434Z", "disclosures": [ { "identifier": "px2uid", diff --git a/metadata/modules/permutiveIdentityManagerIdSystem.json b/metadata/modules/permutiveIdentityManagerIdSystem.json index b86661062c3..1d85e6e5923 100644 --- a/metadata/modules/permutiveIdentityManagerIdSystem.json +++ b/metadata/modules/permutiveIdentityManagerIdSystem.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://assets.permutive.app/tcf/tcf.json": { - "timestamp": "2026-01-21T15:16:58.990Z", + "timestamp": "2026-03-14T22:02:11.848Z", "disclosures": [ { "identifier": "_pdfps", diff --git a/metadata/modules/permutiveRtdProvider.json b/metadata/modules/permutiveRtdProvider.json index 2333450a805..60bc352fc0b 100644 --- a/metadata/modules/permutiveRtdProvider.json +++ b/metadata/modules/permutiveRtdProvider.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://assets.permutive.app/tcf/tcf.json": { - "timestamp": "2026-01-21T15:16:59.185Z", + "timestamp": "2026-03-14T22:02:12.070Z", "disclosures": [ { "identifier": "_pdfps", diff --git a/metadata/modules/pixfutureBidAdapter.json b/metadata/modules/pixfutureBidAdapter.json index c88eda96775..962b2c6a6a7 100644 --- a/metadata/modules/pixfutureBidAdapter.json +++ b/metadata/modules/pixfutureBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://www.pixfuture.com/vendor-disclosures.json": { - "timestamp": "2026-01-21T15:16:59.186Z", + "timestamp": "2026-03-14T22:02:12.070Z", "disclosures": [] } }, diff --git a/metadata/modules/playdigoBidAdapter.json b/metadata/modules/playdigoBidAdapter.json index ae46cc67ea4..004eaa59c14 100644 --- a/metadata/modules/playdigoBidAdapter.json +++ b/metadata/modules/playdigoBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://playdigo.com/file.json": { - "timestamp": "2026-01-21T15:16:59.235Z", + "timestamp": "2026-03-14T22:02:12.136Z", "disclosures": [] } }, diff --git a/metadata/modules/prebid-core.json b/metadata/modules/prebid-core.json index 458f4ba682c..832a6d38cf8 100644 --- a/metadata/modules/prebid-core.json +++ b/metadata/modules/prebid-core.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://cdn.jsdelivr.net/gh/prebid/Prebid.js/metadata/disclosures/prebid/probes.json": { - "timestamp": "2026-01-21T15:16:22.809Z", + "timestamp": "2026-03-14T22:01:05.183Z", "disclosures": [ { "identifier": "_rdc*", @@ -23,7 +23,7 @@ ] }, "https://cdn.jsdelivr.net/gh/prebid/Prebid.js/metadata/disclosures/prebid/debugging.json": { - "timestamp": "2026-01-21T15:16:22.811Z", + "timestamp": "2026-03-14T22:01:05.183Z", "disclosures": [ { "identifier": "__*_debugging__", @@ -41,6 +41,11 @@ "componentName": "fpdEnrichment", "disclosureURL": "https://cdn.jsdelivr.net/gh/prebid/Prebid.js/metadata/disclosures/prebid/probes.json" }, + { + "componentType": "prebid", + "componentName": "storage", + "disclosureURL": "https://cdn.jsdelivr.net/gh/prebid/Prebid.js/metadata/disclosures/prebid/probes.json" + }, { "componentType": "prebid", "componentName": "debugging", diff --git a/metadata/modules/precisoBidAdapter.json b/metadata/modules/precisoBidAdapter.json index 7b260064f90..c36ccb1501c 100644 --- a/metadata/modules/precisoBidAdapter.json +++ b/metadata/modules/precisoBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://preciso.net/deviceStorage.json": { - "timestamp": "2026-01-21T15:16:59.410Z", + "timestamp": "2026-03-14T22:02:12.308Z", "disclosures": [ { "identifier": "XXXXX_viewnew", diff --git a/metadata/modules/prismaBidAdapter.json b/metadata/modules/prismaBidAdapter.json index d7c0d89c2c1..fac8ccdb714 100644 --- a/metadata/modules/prismaBidAdapter.json +++ b/metadata/modules/prismaBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://fast.nexx360.io/deviceStorage.json": { - "timestamp": "2026-01-21T15:16:59.471Z", + "timestamp": "2026-03-14T22:02:12.522Z", "disclosures": [] } }, diff --git a/metadata/modules/programmaticXBidAdapter.json b/metadata/modules/programmaticXBidAdapter.json index a95bd4f0716..335a7394f3a 100644 --- a/metadata/modules/programmaticXBidAdapter.json +++ b/metadata/modules/programmaticXBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://progrtb.com/tcf-vendor-disclosures.json": { - "timestamp": "2026-01-21T15:16:59.472Z", + "timestamp": "2026-03-14T22:02:12.522Z", "disclosures": [] } }, diff --git a/metadata/modules/proxistoreBidAdapter.json b/metadata/modules/proxistoreBidAdapter.json index ae2a16d3c3d..f351ac36c56 100644 --- a/metadata/modules/proxistoreBidAdapter.json +++ b/metadata/modules/proxistoreBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://abs.proxistore.com/assets/json/proxistore_device_storage_disclosure.json": { - "timestamp": "2026-01-21T15:16:59.524Z", + "timestamp": "2026-03-14T22:02:12.576Z", "disclosures": [] } }, diff --git a/metadata/modules/publinkIdSystem.json b/metadata/modules/publinkIdSystem.json index 8fb9399e55c..e71f1f4cb21 100644 --- a/metadata/modules/publinkIdSystem.json +++ b/metadata/modules/publinkIdSystem.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://s-usweb.dotomi.com/assets/js/taggy-js/2.18.9/device_storage_disclosure.json": { - "timestamp": "2026-01-21T15:16:59.902Z", + "timestamp": "2026-03-14T22:02:12.952Z", "disclosures": [ { "identifier": "dtm_status", diff --git a/metadata/modules/pubmaticBidAdapter.json b/metadata/modules/pubmaticBidAdapter.json index f3aa36d69e5..94f71bfdbfe 100644 --- a/metadata/modules/pubmaticBidAdapter.json +++ b/metadata/modules/pubmaticBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://cdn.pubmatic.com/devicestorage.json": { - "timestamp": "2026-01-21T15:16:59.903Z", + "timestamp": "2026-03-14T22:02:12.953Z", "disclosures": [] } }, diff --git a/metadata/modules/pubmaticIdSystem.json b/metadata/modules/pubmaticIdSystem.json index ff3fbd66324..310dca55b6b 100644 --- a/metadata/modules/pubmaticIdSystem.json +++ b/metadata/modules/pubmaticIdSystem.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://cdn.pubmatic.com/devicestorage.json": { - "timestamp": "2026-01-21T15:16:59.918Z", + "timestamp": "2026-03-14T22:02:12.974Z", "disclosures": [] } }, diff --git a/metadata/modules/pubstackBidAdapter.json b/metadata/modules/pubstackBidAdapter.json new file mode 100644 index 00000000000..a0e92337802 --- /dev/null +++ b/metadata/modules/pubstackBidAdapter.json @@ -0,0 +1,25 @@ +{ + "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", + "disclosures": { + "https://cdn.pbstck.com/privacy_policies/device_storage_disclosures.json": { + "timestamp": "2026-03-14T22:02:13.015Z", + "disclosures": [] + } + }, + "components": [ + { + "componentType": "bidder", + "componentName": "pubstack", + "aliasOf": null, + "gvlid": 1408, + "disclosureURL": "https://cdn.pbstck.com/privacy_policies/device_storage_disclosures.json" + }, + { + "componentType": "bidder", + "componentName": "pubstack_server", + "aliasOf": "pubstack", + "gvlid": 1408, + "disclosureURL": "https://cdn.pbstck.com/privacy_policies/device_storage_disclosures.json" + } + ] +} \ No newline at end of file diff --git a/metadata/modules/pulsepointBidAdapter.json b/metadata/modules/pulsepointBidAdapter.json index 7bccc16f3ce..03c8fa2cd67 100644 --- a/metadata/modules/pulsepointBidAdapter.json +++ b/metadata/modules/pulsepointBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://bh.contextweb.com/tcf/vendorInfo.json": { - "timestamp": "2026-01-21T15:16:59.919Z", + "timestamp": "2026-03-14T22:02:13.016Z", "disclosures": [] } }, diff --git a/metadata/modules/quantcastIdSystem.json b/metadata/modules/quantcastIdSystem.json deleted file mode 100644 index 888e03b2fcb..00000000000 --- a/metadata/modules/quantcastIdSystem.json +++ /dev/null @@ -1,51 +0,0 @@ -{ - "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", - "disclosures": { - "https://www.quantcast.com/.well-known/devicestorage.json": { - "timestamp": "2026-01-21T15:17:00.116Z", - "disclosures": [ - { - "identifier": "__qca", - "type": "cookie", - "maxAgeSeconds": 33868800, - "cookieRefresh": false, - "purposes": [ - 1, - 2, - 3, - 4, - 7, - 8, - 9, - 10 - ] - }, - { - "identifier": "__dlt", - "type": "cookie", - "maxAgeSeconds": 0, - "cookieRefresh": false, - "purposes": [ - 1, - 2, - 3, - 4, - 7, - 8, - 9, - 10 - ] - } - ] - } - }, - "components": [ - { - "componentType": "userId", - "componentName": "quantcastId", - "gvlid": "11", - "disclosureURL": "https://www.quantcast.com/.well-known/devicestorage.json", - "aliasOf": null - } - ] -} \ No newline at end of file diff --git a/metadata/modules/r2b2BidAdapter.json b/metadata/modules/r2b2BidAdapter.json index 62b7af885f4..d674b5e77ac 100644 --- a/metadata/modules/r2b2BidAdapter.json +++ b/metadata/modules/r2b2BidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://delivery.r2b2.io/cookie_disclosure": { - "timestamp": "2026-01-21T15:17:00.117Z", + "timestamp": "2026-03-14T22:02:13.033Z", "disclosures": [ { "identifier": "AdTrack-hide-*", diff --git a/metadata/modules/readpeakBidAdapter.json b/metadata/modules/readpeakBidAdapter.json index 405dfdb16fb..8c581cc2e92 100644 --- a/metadata/modules/readpeakBidAdapter.json +++ b/metadata/modules/readpeakBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://static.readpeak.com/tcf/deviceStorage.json": { - "timestamp": "2026-01-21T15:17:00.570Z", + "timestamp": "2026-03-14T22:02:13.492Z", "disclosures": [ { "identifier": "rp_uidfp", diff --git a/metadata/modules/relayBidAdapter.json b/metadata/modules/relayBidAdapter.json index 4bd86b056b8..505ea7f747f 100644 --- a/metadata/modules/relayBidAdapter.json +++ b/metadata/modules/relayBidAdapter.json @@ -2,8 +2,8 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://relay42.com/hubfs/raw_assets/public/IAB.json": { - "timestamp": "2026-01-21T15:17:00.595Z", - "disclosures": [] + "timestamp": "2026-03-14T22:02:13.832Z", + "disclosures": null } }, "components": [ diff --git a/metadata/modules/relevantdigitalBidAdapter.json b/metadata/modules/relevantdigitalBidAdapter.json index ba667884940..674247e719e 100644 --- a/metadata/modules/relevantdigitalBidAdapter.json +++ b/metadata/modules/relevantdigitalBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://cdn.relevant-digital.com/resources/deviceStorage.json": { - "timestamp": "2026-01-21T15:17:00.656Z", + "timestamp": "2026-03-14T22:02:14.478Z", "disclosures": [] } }, diff --git a/metadata/modules/resetdigitalBidAdapter.json b/metadata/modules/resetdigitalBidAdapter.json index 1cb0456e43c..dddacf422bd 100644 --- a/metadata/modules/resetdigitalBidAdapter.json +++ b/metadata/modules/resetdigitalBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://resetdigital.co/GDPR-TCF.json": { - "timestamp": "2026-01-21T15:17:00.945Z", + "timestamp": "2026-03-14T22:02:14.777Z", "disclosures": [] } }, diff --git a/metadata/modules/responsiveAdsBidAdapter.json b/metadata/modules/responsiveAdsBidAdapter.json index 14820adf1f9..b7687be0b8a 100644 --- a/metadata/modules/responsiveAdsBidAdapter.json +++ b/metadata/modules/responsiveAdsBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://publish.responsiveads.com/tcf/tcf-v2.json": { - "timestamp": "2026-01-21T15:17:00.984Z", + "timestamp": "2026-03-14T22:02:14.824Z", "disclosures": [] } }, diff --git a/metadata/modules/revantageBidAdapter.json b/metadata/modules/revantageBidAdapter.json new file mode 100644 index 00000000000..90eda1e36ad --- /dev/null +++ b/metadata/modules/revantageBidAdapter.json @@ -0,0 +1,13 @@ +{ + "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", + "disclosures": {}, + "components": [ + { + "componentType": "bidder", + "componentName": "revantage", + "aliasOf": null, + "gvlid": null, + "disclosureURL": null + } + ] +} \ No newline at end of file diff --git a/metadata/modules/revcontentBidAdapter.json b/metadata/modules/revcontentBidAdapter.json index 6c84249b171..e16cb5ea621 100644 --- a/metadata/modules/revcontentBidAdapter.json +++ b/metadata/modules/revcontentBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://sothebys.revcontent.com/static/device_storage.json": { - "timestamp": "2026-01-21T15:17:01.026Z", + "timestamp": "2026-03-14T22:02:14.860Z", "disclosures": [ { "identifier": "__ID", diff --git a/metadata/modules/revnewBidAdapter.json b/metadata/modules/revnewBidAdapter.json index 695223069a3..6c83c177af3 100644 --- a/metadata/modules/revnewBidAdapter.json +++ b/metadata/modules/revnewBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://mediafuse.com/deviceStorage.json": { - "timestamp": "2026-01-21T15:17:01.059Z", + "timestamp": "2026-03-14T22:02:14.879Z", "disclosures": [] } }, diff --git a/metadata/modules/rhythmoneBidAdapter.json b/metadata/modules/rhythmoneBidAdapter.json index 9728a7aa7e8..d57b3c0c37a 100644 --- a/metadata/modules/rhythmoneBidAdapter.json +++ b/metadata/modules/rhythmoneBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://video.unrulymedia.com/deviceStorageDisclosure.json": { - "timestamp": "2026-01-21T15:17:01.128Z", + "timestamp": "2026-03-14T22:02:14.948Z", "disclosures": [] } }, diff --git a/metadata/modules/richaudienceBidAdapter.json b/metadata/modules/richaudienceBidAdapter.json index 208ef2a7425..f5f6da4f899 100644 --- a/metadata/modules/richaudienceBidAdapter.json +++ b/metadata/modules/richaudienceBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://cdnj.richaudience.com/52a26ab9400b2a9f5aabfa20acf3196g.json": { - "timestamp": "2026-01-21T15:17:01.496Z", + "timestamp": "2026-03-14T22:02:15.182Z", "disclosures": [] } }, diff --git a/metadata/modules/riseBidAdapter.json b/metadata/modules/riseBidAdapter.json index 0c746db669d..a2aafc0e329 100644 --- a/metadata/modules/riseBidAdapter.json +++ b/metadata/modules/riseBidAdapter.json @@ -2,11 +2,11 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://d2pm7iglz0b6eq.cloudfront.net/RiseDeviceStorage.json": { - "timestamp": "2026-01-21T15:17:01.568Z", + "timestamp": "2026-03-14T22:02:15.255Z", "disclosures": [] }, "https://spotim-prd-static-assets.s3.amazonaws.com/iab/device-storage.json": { - "timestamp": "2026-01-21T15:17:01.568Z", + "timestamp": "2026-03-14T22:02:15.255Z", "disclosures": [] } }, diff --git a/metadata/modules/rixengineBidAdapter.json b/metadata/modules/rixengineBidAdapter.json index 0aacc76bf1b..7cbe170097c 100644 --- a/metadata/modules/rixengineBidAdapter.json +++ b/metadata/modules/rixengineBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://www.algorix.co/gdpr-disclosure.json": { - "timestamp": "2026-01-21T15:17:01.569Z", + "timestamp": "2026-03-14T22:02:15.256Z", "disclosures": [] } }, diff --git a/metadata/modules/rtbhouseBidAdapter.json b/metadata/modules/rtbhouseBidAdapter.json index 7fa16e457e7..9d194a92ea1 100644 --- a/metadata/modules/rtbhouseBidAdapter.json +++ b/metadata/modules/rtbhouseBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://rtbhouse.com/DeviceStorage.json": { - "timestamp": "2026-01-21T15:17:01.676Z", + "timestamp": "2026-03-14T22:02:15.326Z", "disclosures": [ { "identifier": "_rtbh.*", diff --git a/metadata/modules/rubiconBidAdapter.json b/metadata/modules/rubiconBidAdapter.json index ae7a9c840ff..65f0bcf2768 100644 --- a/metadata/modules/rubiconBidAdapter.json +++ b/metadata/modules/rubiconBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://gdpr.rubiconproject.com/dvplus/devicestoragedisclosure.json": { - "timestamp": "2026-01-21T15:17:01.886Z", + "timestamp": "2026-03-14T22:02:15.622Z", "disclosures": [] } }, diff --git a/metadata/modules/scaliburBidAdapter.json b/metadata/modules/scaliburBidAdapter.json index 95985ef007d..0c3ee7b2a64 100644 --- a/metadata/modules/scaliburBidAdapter.json +++ b/metadata/modules/scaliburBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://legal.overwolf.com/docs/overwolf/website/deviceStorageDisclosure.json": { - "timestamp": "2026-01-21T15:17:02.149Z", + "timestamp": "2026-03-14T22:02:15.622Z", "disclosures": [ { "identifier": "scluid", diff --git a/metadata/modules/screencoreBidAdapter.json b/metadata/modules/screencoreBidAdapter.json index 9e06b18660e..079c53814c3 100644 --- a/metadata/modules/screencoreBidAdapter.json +++ b/metadata/modules/screencoreBidAdapter.json @@ -2,8 +2,8 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://screencore.io/tcf.json": { - "timestamp": "2026-01-21T15:17:02.166Z", - "disclosures": null + "timestamp": "2026-03-14T22:02:15.638Z", + "disclosures": [] } }, "components": [ diff --git a/metadata/modules/seedingAllianceBidAdapter.json b/metadata/modules/seedingAllianceBidAdapter.json index 3174123fbb7..da6a087f60e 100644 --- a/metadata/modules/seedingAllianceBidAdapter.json +++ b/metadata/modules/seedingAllianceBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://s.nativendo.de/cdn/asset/tcf/purpose-specific-storage-and-access-information.json": { - "timestamp": "2026-01-21T15:17:04.761Z", + "timestamp": "2026-03-14T22:02:15.682Z", "disclosures": [] } }, diff --git a/metadata/modules/seedtagBidAdapter.json b/metadata/modules/seedtagBidAdapter.json index 79371740968..1430504b5e3 100644 --- a/metadata/modules/seedtagBidAdapter.json +++ b/metadata/modules/seedtagBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://tcf.seedtag.com/vendor.json": { - "timestamp": "2026-01-21T15:17:04.790Z", + "timestamp": "2026-03-14T22:02:15.732Z", "disclosures": [] } }, diff --git a/metadata/modules/semantiqRtdProvider.json b/metadata/modules/semantiqRtdProvider.json index d3145dff825..85ec5fe5316 100644 --- a/metadata/modules/semantiqRtdProvider.json +++ b/metadata/modules/semantiqRtdProvider.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://audienzz.com/device_storage_disclosure_vendor_783.json": { - "timestamp": "2026-01-21T15:17:04.790Z", + "timestamp": "2026-03-14T22:02:15.732Z", "disclosures": [] } }, diff --git a/metadata/modules/setupadBidAdapter.json b/metadata/modules/setupadBidAdapter.json index 5266468465d..20bb4dab06f 100644 --- a/metadata/modules/setupadBidAdapter.json +++ b/metadata/modules/setupadBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://cookies.stpd.cloud/disclosures.json": { - "timestamp": "2026-01-21T15:17:04.866Z", + "timestamp": "2026-03-02T14:46:07.471Z", "disclosures": [] } }, diff --git a/metadata/modules/sevioBidAdapter.json b/metadata/modules/sevioBidAdapter.json index b0e7c8d5353..b72e58a0347 100644 --- a/metadata/modules/sevioBidAdapter.json +++ b/metadata/modules/sevioBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://sevio.com/tcf.json": { - "timestamp": "2026-01-21T15:17:04.978Z", + "timestamp": "2026-03-14T22:02:15.817Z", "disclosures": [] } }, diff --git a/metadata/modules/sharedIdSystem.json b/metadata/modules/sharedIdSystem.json index f9cbc4b88a2..19ebfc6653e 100644 --- a/metadata/modules/sharedIdSystem.json +++ b/metadata/modules/sharedIdSystem.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://cdn.jsdelivr.net/gh/prebid/Prebid.js/metadata/disclosures/prebid/sharedId-optout.json": { - "timestamp": "2026-01-21T15:17:05.148Z", + "timestamp": "2026-03-14T22:02:15.941Z", "disclosures": [ { "identifier": "_pubcid_optout", diff --git a/metadata/modules/sharethroughBidAdapter.json b/metadata/modules/sharethroughBidAdapter.json index d7f8f4d4242..e930f3bdbf6 100644 --- a/metadata/modules/sharethroughBidAdapter.json +++ b/metadata/modules/sharethroughBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://assets.sharethrough.com/gvl.json": { - "timestamp": "2026-01-21T15:17:05.148Z", + "timestamp": "2026-03-14T22:02:15.942Z", "disclosures": [] } }, diff --git a/metadata/modules/showheroes-bsBidAdapter.json b/metadata/modules/showheroes-bsBidAdapter.json index 0fe06a9f92c..377e97140e2 100644 --- a/metadata/modules/showheroes-bsBidAdapter.json +++ b/metadata/modules/showheroes-bsBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://static-origin.showheroes.com/gvl_storage_disclosure.json": { - "timestamp": "2026-01-21T15:17:05.170Z", + "timestamp": "2026-03-14T22:02:15.968Z", "disclosures": [] } }, diff --git a/metadata/modules/silvermobBidAdapter.json b/metadata/modules/silvermobBidAdapter.json index 9a78a647f02..91c504f7441 100644 --- a/metadata/modules/silvermobBidAdapter.json +++ b/metadata/modules/silvermobBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://silvermob.com/deviceStorageDisclosure.json": { - "timestamp": "2026-01-21T15:17:05.593Z", + "timestamp": "2026-03-14T22:02:16.412Z", "disclosures": [] } }, diff --git a/metadata/modules/sirdataRtdProvider.json b/metadata/modules/sirdataRtdProvider.json index 60ffec07374..7c1d268f5dd 100644 --- a/metadata/modules/sirdataRtdProvider.json +++ b/metadata/modules/sirdataRtdProvider.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://cdn.sirdata.eu/sirdata_device_storage_disclosure.json": { - "timestamp": "2026-01-21T15:17:05.611Z", + "timestamp": "2026-03-14T22:02:16.429Z", "disclosures": [] } }, diff --git a/metadata/modules/smaatoBidAdapter.json b/metadata/modules/smaatoBidAdapter.json index 6e21316b38a..b22dad578f5 100644 --- a/metadata/modules/smaatoBidAdapter.json +++ b/metadata/modules/smaatoBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://resources.smaato.com/hubfs/Smaato/IAB/deviceStorage.json": { - "timestamp": "2026-01-21T15:17:05.910Z", + "timestamp": "2026-03-14T22:02:16.732Z", "disclosures": [] } }, diff --git a/metadata/modules/smartadserverBidAdapter.json b/metadata/modules/smartadserverBidAdapter.json index e7edb4d7137..48b7c70c1c0 100644 --- a/metadata/modules/smartadserverBidAdapter.json +++ b/metadata/modules/smartadserverBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://apps.smartadserver.com/device-storage-disclosures/equativDeviceStorageDisclosures.json": { - "timestamp": "2026-01-21T15:17:05.987Z", + "timestamp": "2026-03-14T22:02:16.809Z", "disclosures": [] } }, diff --git a/metadata/modules/smartxBidAdapter.json b/metadata/modules/smartxBidAdapter.json index 8803c85fa7d..aff5374272d 100644 --- a/metadata/modules/smartxBidAdapter.json +++ b/metadata/modules/smartxBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://cdn.smartclip.net/iab/deviceStorageDisclosure.json": { - "timestamp": "2026-01-21T15:17:05.987Z", + "timestamp": "2026-03-14T22:02:16.810Z", "disclosures": [] } }, diff --git a/metadata/modules/smartyadsBidAdapter.json b/metadata/modules/smartyadsBidAdapter.json index ee908d6e2fe..c35c87b17ef 100644 --- a/metadata/modules/smartyadsBidAdapter.json +++ b/metadata/modules/smartyadsBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://smartyads.com/tcf.json": { - "timestamp": "2026-01-21T15:17:06.011Z", + "timestamp": "2026-03-14T22:02:16.846Z", "disclosures": [] } }, diff --git a/metadata/modules/smilewantedBidAdapter.json b/metadata/modules/smilewantedBidAdapter.json index b658597c747..0e5dfe73669 100644 --- a/metadata/modules/smilewantedBidAdapter.json +++ b/metadata/modules/smilewantedBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://smilewanted.com/vendor-device-storage-disclosures.json": { - "timestamp": "2026-01-21T15:17:06.048Z", + "timestamp": "2026-03-14T22:02:16.909Z", "disclosures": [] } }, diff --git a/metadata/modules/snigelBidAdapter.json b/metadata/modules/snigelBidAdapter.json index d17219aeddc..07f97e29d0a 100644 --- a/metadata/modules/snigelBidAdapter.json +++ b/metadata/modules/snigelBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://cdn.snigelweb.com/gvl/deviceStorageDisclosure.json": { - "timestamp": "2026-01-21T15:17:06.506Z", + "timestamp": "2026-03-14T22:02:17.360Z", "disclosures": [] } }, diff --git a/metadata/modules/sonaradsBidAdapter.json b/metadata/modules/sonaradsBidAdapter.json index 5417992dc11..b227a79b25e 100644 --- a/metadata/modules/sonaradsBidAdapter.json +++ b/metadata/modules/sonaradsBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://bridgeupp.com/device-storage-disclosure.json": { - "timestamp": "2026-01-21T15:17:06.558Z", + "timestamp": "2026-03-14T22:02:17.405Z", "disclosures": [] } }, diff --git a/metadata/modules/sonobiBidAdapter.json b/metadata/modules/sonobiBidAdapter.json index a3434d63755..06b3a2fd635 100644 --- a/metadata/modules/sonobiBidAdapter.json +++ b/metadata/modules/sonobiBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://sonobi.com/tcf2-device-storage-disclosure.json": { - "timestamp": "2026-01-21T15:17:06.775Z", + "timestamp": "2026-03-14T22:02:17.642Z", "disclosures": [] } }, diff --git a/metadata/modules/sovrnBidAdapter.json b/metadata/modules/sovrnBidAdapter.json index 1d13462ea77..423e5753aa9 100644 --- a/metadata/modules/sovrnBidAdapter.json +++ b/metadata/modules/sovrnBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://cdn.sovrn.com/tcf-cookie-disclosure/disclosure.json": { - "timestamp": "2026-01-21T15:17:07.010Z", + "timestamp": "2026-03-14T22:02:17.877Z", "disclosures": [] } }, diff --git a/metadata/modules/sparteoBidAdapter.json b/metadata/modules/sparteoBidAdapter.json index 6dcb320ef39..ad551f9d5e9 100644 --- a/metadata/modules/sparteoBidAdapter.json +++ b/metadata/modules/sparteoBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://bid.bricks-co.com/.well-known/deviceStorage.json": { - "timestamp": "2026-01-21T15:17:07.032Z", + "timestamp": "2026-03-14T22:02:17.899Z", "disclosures": [ { "identifier": "fastCMP-addtlConsent", diff --git a/metadata/modules/ssmasBidAdapter.json b/metadata/modules/ssmasBidAdapter.json index 8d1fc62e602..02f783d187c 100644 --- a/metadata/modules/ssmasBidAdapter.json +++ b/metadata/modules/ssmasBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://semseoymas.com/iab.json": { - "timestamp": "2026-01-21T15:17:07.312Z", + "timestamp": "2026-03-14T22:02:18.173Z", "disclosures": null } }, diff --git a/metadata/modules/sspBCBidAdapter.json b/metadata/modules/sspBCBidAdapter.json index 73dc546a4f4..6e3ba064625 100644 --- a/metadata/modules/sspBCBidAdapter.json +++ b/metadata/modules/sspBCBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://ssp.wp.pl/deviceStorage.json": { - "timestamp": "2026-01-21T15:17:07.901Z", + "timestamp": "2026-03-14T22:02:18.816Z", "disclosures": null } }, diff --git a/metadata/modules/stackadaptBidAdapter.json b/metadata/modules/stackadaptBidAdapter.json index d4ae9c3fbb2..9d9d745a1b9 100644 --- a/metadata/modules/stackadaptBidAdapter.json +++ b/metadata/modules/stackadaptBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://s3.amazonaws.com/stackadapt_public/disclosures.json": { - "timestamp": "2026-01-21T15:17:07.902Z", + "timestamp": "2026-03-14T22:02:18.817Z", "disclosures": [ { "identifier": "sa-camp-*", @@ -62,6 +62,17 @@ 4 ] }, + { + "identifier": "sa-user-id-v4", + "type": "cookie", + "maxAgeSeconds": 31536000, + "cookieRefresh": false, + "purposes": [ + 1, + 3, + 4 + ] + }, { "identifier": "sa-user-id", "type": "web", @@ -84,6 +95,17 @@ 4 ] }, + { + "identifier": "sa-user-id-v4", + "type": "web", + "maxAgeSeconds": null, + "cookieRefresh": false, + "purposes": [ + 1, + 3, + 4 + ] + }, { "identifier": "sa-camp-*", "type": "web", diff --git a/metadata/modules/startioBidAdapter.json b/metadata/modules/startioBidAdapter.json index f9e849f8d44..c77c2eccdbd 100644 --- a/metadata/modules/startioBidAdapter.json +++ b/metadata/modules/startioBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://info.startappservice.com/tcf/start.io_domains.json": { - "timestamp": "2026-01-21T15:17:07.940Z", + "timestamp": "2026-03-14T22:02:18.857Z", "disclosures": [] } }, diff --git a/metadata/modules/stroeerCoreBidAdapter.json b/metadata/modules/stroeerCoreBidAdapter.json index 38a3b050b5f..9e59f452069 100644 --- a/metadata/modules/stroeerCoreBidAdapter.json +++ b/metadata/modules/stroeerCoreBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://www.stroeer.de/StroeerSSP_deviceStorage.json": { - "timestamp": "2026-01-21T15:17:07.958Z", + "timestamp": "2026-03-14T22:02:18.876Z", "disclosures": [] } }, diff --git a/metadata/modules/stvBidAdapter.json b/metadata/modules/stvBidAdapter.json index cc987faae96..fb3a6050139 100644 --- a/metadata/modules/stvBidAdapter.json +++ b/metadata/modules/stvBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://tcf.adtech.app/gen/deviceStorageDisclosure/stv.json": { - "timestamp": "2026-01-21T15:17:08.380Z", + "timestamp": "2026-03-14T22:02:19.202Z", "disclosures": [] } }, diff --git a/metadata/modules/sublimeBidAdapter.json b/metadata/modules/sublimeBidAdapter.json index 8f84b5b6227..ddeaaa5dca5 100644 --- a/metadata/modules/sublimeBidAdapter.json +++ b/metadata/modules/sublimeBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://gdpr.ayads.co/cookiepolicy.json": { - "timestamp": "2026-01-21T15:17:09.023Z", + "timestamp": "2026-03-14T22:02:19.869Z", "disclosures": [ { "identifier": "dnt", diff --git a/metadata/modules/taboolaBidAdapter.json b/metadata/modules/taboolaBidAdapter.json index f8d9b90381e..b3f833e3876 100644 --- a/metadata/modules/taboolaBidAdapter.json +++ b/metadata/modules/taboolaBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://accessrequest.taboola.com/iab-tcf-v2-disclosure.json": { - "timestamp": "2026-01-21T15:17:09.296Z", + "timestamp": "2026-03-14T22:02:20.135Z", "disclosures": [ { "identifier": "trc_cookie_storage", diff --git a/metadata/modules/taboolaIdSystem.json b/metadata/modules/taboolaIdSystem.json index 360ed099e0f..6bed602a7de 100644 --- a/metadata/modules/taboolaIdSystem.json +++ b/metadata/modules/taboolaIdSystem.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://accessrequest.taboola.com/iab-tcf-v2-disclosure.json": { - "timestamp": "2026-01-21T15:17:09.905Z", + "timestamp": "2026-03-14T22:02:20.348Z", "disclosures": [ { "identifier": "trc_cookie_storage", diff --git a/metadata/modules/tadvertisingBidAdapter.json b/metadata/modules/tadvertisingBidAdapter.json index a22f75b54ad..7ecc943ebc3 100644 --- a/metadata/modules/tadvertisingBidAdapter.json +++ b/metadata/modules/tadvertisingBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://tcf.emetriq.de/deviceStorageDisclosure.json": { - "timestamp": "2026-01-21T15:17:09.906Z", + "timestamp": "2026-03-14T22:02:20.348Z", "disclosures": [] } }, diff --git a/metadata/modules/tappxBidAdapter.json b/metadata/modules/tappxBidAdapter.json index 013a74f81be..0ac1d4fd3fa 100644 --- a/metadata/modules/tappxBidAdapter.json +++ b/metadata/modules/tappxBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://tappx.com/devicestorage.json": { - "timestamp": "2026-01-21T15:17:09.907Z", + "timestamp": "2026-03-14T22:02:20.368Z", "disclosures": [] } }, diff --git a/metadata/modules/targetVideoBidAdapter.json b/metadata/modules/targetVideoBidAdapter.json index 7216e023297..84ee2c09d9a 100644 --- a/metadata/modules/targetVideoBidAdapter.json +++ b/metadata/modules/targetVideoBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://target-video.com/vendors-device-storage-and-operational-disclosures.json": { - "timestamp": "2026-01-21T15:17:09.935Z", + "timestamp": "2026-03-14T22:02:20.397Z", "disclosures": [ { "identifier": "brid_location", diff --git a/metadata/modules/teadsBidAdapter.json b/metadata/modules/teadsBidAdapter.json index d1e45cb2c97..af918455c33 100644 --- a/metadata/modules/teadsBidAdapter.json +++ b/metadata/modules/teadsBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://iab-cookie-disclosure.teads.tv/deviceStorage.json": { - "timestamp": "2026-01-21T15:17:09.935Z", + "timestamp": "2026-03-14T22:02:20.397Z", "disclosures": [] } }, diff --git a/metadata/modules/teadsIdSystem.json b/metadata/modules/teadsIdSystem.json index 42f4ebb812f..ba3b6b67ec1 100644 --- a/metadata/modules/teadsIdSystem.json +++ b/metadata/modules/teadsIdSystem.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://iab-cookie-disclosure.teads.tv/deviceStorage.json": { - "timestamp": "2026-01-21T15:17:09.960Z", + "timestamp": "2026-03-14T22:02:20.421Z", "disclosures": [] } }, diff --git a/metadata/modules/tealBidAdapter.json b/metadata/modules/tealBidAdapter.json index 5ac1fe8c1d9..b447dfefac5 100644 --- a/metadata/modules/tealBidAdapter.json +++ b/metadata/modules/tealBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://c.bids.ws/iab/disclosures.json": { - "timestamp": "2026-01-21T15:17:09.960Z", + "timestamp": "2026-03-14T22:02:20.421Z", "disclosures": [] } }, diff --git a/metadata/modules/teqBlazeSalesAgentBidAdapter.json b/metadata/modules/teqBlazeSalesAgentBidAdapter.json new file mode 100644 index 00000000000..2442df240d1 --- /dev/null +++ b/metadata/modules/teqBlazeSalesAgentBidAdapter.json @@ -0,0 +1,13 @@ +{ + "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", + "disclosures": {}, + "components": [ + { + "componentType": "bidder", + "componentName": "teqBlazeSalesAgent", + "aliasOf": null, + "gvlid": null, + "disclosureURL": null + } + ] +} \ No newline at end of file diff --git a/metadata/modules/tncIdSystem.json b/metadata/modules/tncIdSystem.json index 41c9b2b619f..f146936223f 100644 --- a/metadata/modules/tncIdSystem.json +++ b/metadata/modules/tncIdSystem.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://js.tncid.app/iab-tcf-device-storage-disclosure.json": { - "timestamp": "2026-01-21T15:17:10.014Z", + "timestamp": "2026-03-14T22:02:20.457Z", "disclosures": [] } }, diff --git a/metadata/modules/topicsFpdModule.json b/metadata/modules/topicsFpdModule.json index ddb49386c15..c1dd75347e8 100644 --- a/metadata/modules/topicsFpdModule.json +++ b/metadata/modules/topicsFpdModule.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://cdn.jsdelivr.net/gh/prebid/Prebid.js/metadata/disclosures/prebid/topicsFpdModule.json": { - "timestamp": "2026-01-21T15:16:22.811Z", + "timestamp": "2026-03-14T22:01:05.183Z", "disclosures": [ { "identifier": "prebid:topics", diff --git a/metadata/modules/toponBidAdapter.json b/metadata/modules/toponBidAdapter.json index 74feb9d7cd3..95264b4d75c 100644 --- a/metadata/modules/toponBidAdapter.json +++ b/metadata/modules/toponBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://mores.toponad.net/tmp/tpn/toponads_tcf_disclosure.json": { - "timestamp": "2026-01-21T15:17:10.034Z", + "timestamp": "2026-03-14T22:02:20.479Z", "disclosures": [] } }, diff --git a/metadata/modules/tripleliftBidAdapter.json b/metadata/modules/tripleliftBidAdapter.json index 038268be8a9..e27a19f128d 100644 --- a/metadata/modules/tripleliftBidAdapter.json +++ b/metadata/modules/tripleliftBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://triplelift.com/.well-known/deviceStorage.json": { - "timestamp": "2026-01-21T15:17:10.059Z", + "timestamp": "2026-03-14T22:02:20.525Z", "disclosures": [] } }, diff --git a/metadata/modules/ttdBidAdapter.json b/metadata/modules/ttdBidAdapter.json index 23816f1bdad..09b48d5897a 100644 --- a/metadata/modules/ttdBidAdapter.json +++ b/metadata/modules/ttdBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://ttd-misc-public-assets.s3.us-west-2.amazonaws.com/deviceStorageDisclosureURL.json": { - "timestamp": "2026-01-21T15:17:10.088Z", + "timestamp": "2026-03-14T22:02:20.555Z", "disclosures": [] } }, diff --git a/metadata/modules/twistDigitalBidAdapter.json b/metadata/modules/twistDigitalBidAdapter.json index 45ae377ade9..b4b49827360 100644 --- a/metadata/modules/twistDigitalBidAdapter.json +++ b/metadata/modules/twistDigitalBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://twistdigital.net/iab.json": { - "timestamp": "2026-01-21T15:17:10.089Z", + "timestamp": "2026-03-14T22:02:20.556Z", "disclosures": [ { "identifier": "vdzj1_{id}", diff --git a/metadata/modules/underdogmediaBidAdapter.json b/metadata/modules/underdogmediaBidAdapter.json index a70b4a279d9..d36661239fa 100644 --- a/metadata/modules/underdogmediaBidAdapter.json +++ b/metadata/modules/underdogmediaBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://bid.underdog.media/deviceStorage.json": { - "timestamp": "2026-01-21T15:17:10.161Z", + "timestamp": "2026-03-14T22:02:20.608Z", "disclosures": [] } }, diff --git a/metadata/modules/undertoneBidAdapter.json b/metadata/modules/undertoneBidAdapter.json index c652f3ca0b7..08e33c8fda6 100644 --- a/metadata/modules/undertoneBidAdapter.json +++ b/metadata/modules/undertoneBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://cdn.undertone.com/js/deviceStorage.json": { - "timestamp": "2026-01-21T15:17:10.182Z", + "timestamp": "2026-03-14T22:02:20.627Z", "disclosures": [] } }, diff --git a/metadata/modules/unifiedIdSystem.json b/metadata/modules/unifiedIdSystem.json index 45971596b47..c30afe5d6cf 100644 --- a/metadata/modules/unifiedIdSystem.json +++ b/metadata/modules/unifiedIdSystem.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://ttd-misc-public-assets.s3.us-west-2.amazonaws.com/deviceStorageDisclosureURL.json": { - "timestamp": "2026-01-21T15:17:10.198Z", + "timestamp": "2026-03-14T22:02:20.812Z", "disclosures": [] } }, diff --git a/metadata/modules/unrulyBidAdapter.json b/metadata/modules/unrulyBidAdapter.json index 30fd1495008..a5652561f1c 100644 --- a/metadata/modules/unrulyBidAdapter.json +++ b/metadata/modules/unrulyBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://video.unrulymedia.com/deviceStorageDisclosure.json": { - "timestamp": "2026-01-21T15:17:10.198Z", + "timestamp": "2026-03-14T22:02:20.813Z", "disclosures": [] } }, diff --git a/metadata/modules/userId.json b/metadata/modules/userId.json index e5269440454..54aeb8f8b2e 100644 --- a/metadata/modules/userId.json +++ b/metadata/modules/userId.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://cdn.jsdelivr.net/gh/prebid/Prebid.js/metadata/disclosures/prebid/userId-optout.json": { - "timestamp": "2026-01-21T15:16:22.813Z", + "timestamp": "2026-03-14T22:01:05.186Z", "disclosures": [ { "identifier": "_pbjs_id_optout", diff --git a/metadata/modules/utiqIdSystem.json b/metadata/modules/utiqIdSystem.json index 1d23694cf77..f7202c55338 100644 --- a/metadata/modules/utiqIdSystem.json +++ b/metadata/modules/utiqIdSystem.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://cdn.jsdelivr.net/gh/prebid/Prebid.js/metadata/disclosures/modules/utiqDeviceStorageDisclosure.json": { - "timestamp": "2026-01-21T15:17:10.198Z", + "timestamp": "2026-03-14T22:02:20.813Z", "disclosures": [ { "identifier": "utiqPass", diff --git a/metadata/modules/utiqMtpIdSystem.json b/metadata/modules/utiqMtpIdSystem.json index eb49f9d9027..59aca40ea45 100644 --- a/metadata/modules/utiqMtpIdSystem.json +++ b/metadata/modules/utiqMtpIdSystem.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://cdn.jsdelivr.net/gh/prebid/Prebid.js/metadata/disclosures/modules/utiqDeviceStorageDisclosure.json": { - "timestamp": "2026-01-21T15:17:10.199Z", + "timestamp": "2026-03-14T22:02:20.814Z", "disclosures": [ { "identifier": "utiqPass", diff --git a/metadata/modules/validationFpdModule.json b/metadata/modules/validationFpdModule.json index 375ed570cb5..e7fdb0b4a7f 100644 --- a/metadata/modules/validationFpdModule.json +++ b/metadata/modules/validationFpdModule.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://cdn.jsdelivr.net/gh/prebid/Prebid.js/metadata/disclosures/prebid/sharedId-optout.json": { - "timestamp": "2026-01-21T15:16:22.812Z", + "timestamp": "2026-03-14T22:01:05.184Z", "disclosures": [ { "identifier": "_pubcid_optout", diff --git a/metadata/modules/valuadBidAdapter.json b/metadata/modules/valuadBidAdapter.json index e7aef4febc8..3cddb70df24 100644 --- a/metadata/modules/valuadBidAdapter.json +++ b/metadata/modules/valuadBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://cdn.valuad.cloud/tcfdevice.json": { - "timestamp": "2026-01-21T15:17:10.199Z", + "timestamp": "2026-03-14T22:02:20.814Z", "disclosures": [] } }, diff --git a/metadata/modules/verbenBidAdapter.json b/metadata/modules/verbenBidAdapter.json new file mode 100644 index 00000000000..47a939ac97a --- /dev/null +++ b/metadata/modules/verbenBidAdapter.json @@ -0,0 +1,13 @@ +{ + "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", + "disclosures": {}, + "components": [ + { + "componentType": "bidder", + "componentName": "verben", + "aliasOf": null, + "gvlid": null, + "disclosureURL": null + } + ] +} \ No newline at end of file diff --git a/metadata/modules/vidazooBidAdapter.json b/metadata/modules/vidazooBidAdapter.json index b45149019ba..f6d1cd6ebc8 100644 --- a/metadata/modules/vidazooBidAdapter.json +++ b/metadata/modules/vidazooBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://vidazoo.com/gdpr-tcf/deviceStorage.json": { - "timestamp": "2026-01-21T15:17:10.377Z", + "timestamp": "2026-03-14T22:02:21.033Z", "disclosures": [ { "identifier": "ck48wz12sqj7", diff --git a/metadata/modules/vidoomyBidAdapter.json b/metadata/modules/vidoomyBidAdapter.json index a4fbba104e1..e8220d56bc8 100644 --- a/metadata/modules/vidoomyBidAdapter.json +++ b/metadata/modules/vidoomyBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://vidoomy.com/storageurl/devicestoragediscurl.json": { - "timestamp": "2026-01-21T15:17:10.438Z", + "timestamp": "2026-03-14T22:02:21.092Z", "disclosures": [] } }, diff --git a/metadata/modules/viouslyBidAdapter.json b/metadata/modules/viouslyBidAdapter.json index 8e77e1a135e..87159daf7f8 100644 --- a/metadata/modules/viouslyBidAdapter.json +++ b/metadata/modules/viouslyBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://bid.bricks-co.com/.well-known/deviceStorage.json": { - "timestamp": "2026-01-21T15:17:10.552Z", + "timestamp": "2026-03-14T22:02:21.621Z", "disclosures": [ { "identifier": "fastCMP-addtlConsent", diff --git a/metadata/modules/visxBidAdapter.json b/metadata/modules/visxBidAdapter.json index 53c66a42db3..2db2d7789a8 100644 --- a/metadata/modules/visxBidAdapter.json +++ b/metadata/modules/visxBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://cdn.yoc.com/visx/sellers/deviceStorage.json": { - "timestamp": "2026-01-21T15:17:10.553Z", + "timestamp": "2026-03-14T22:02:21.621Z", "disclosures": [ { "identifier": "__vads", diff --git a/metadata/modules/vlybyBidAdapter.json b/metadata/modules/vlybyBidAdapter.json index a25400030ad..5eb93b35b84 100644 --- a/metadata/modules/vlybyBidAdapter.json +++ b/metadata/modules/vlybyBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://cdn.vlyby.com/conf/iab/gvl.json": { - "timestamp": "2026-01-21T15:17:10.747Z", + "timestamp": "2026-03-14T22:02:21.914Z", "disclosures": [] } }, diff --git a/metadata/modules/voxBidAdapter.json b/metadata/modules/voxBidAdapter.json index d8a01c72033..e40b19232f2 100644 --- a/metadata/modules/voxBidAdapter.json +++ b/metadata/modules/voxBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://st.hybrid.ai/policy/deviceStorage.json": { - "timestamp": "2026-01-21T15:17:11.165Z", + "timestamp": "2026-03-14T22:02:22.260Z", "disclosures": [] } }, diff --git a/metadata/modules/vrtcalBidAdapter.json b/metadata/modules/vrtcalBidAdapter.json index 718bf2e62e9..254c2781ce5 100644 --- a/metadata/modules/vrtcalBidAdapter.json +++ b/metadata/modules/vrtcalBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://vrtcal.com/docs/gdpr-tcf-disclosures.json": { - "timestamp": "2026-01-21T15:17:11.166Z", + "timestamp": "2026-03-14T22:02:22.260Z", "disclosures": [] } }, diff --git a/metadata/modules/vuukleBidAdapter.json b/metadata/modules/vuukleBidAdapter.json index e1ec79ea855..cfec24f1f1f 100644 --- a/metadata/modules/vuukleBidAdapter.json +++ b/metadata/modules/vuukleBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://cdn.vuukle.com/data-privacy/deviceStorage.json": { - "timestamp": "2026-01-21T15:17:11.180Z", + "timestamp": "2026-03-14T22:02:22.276Z", "disclosures": [ { "identifier": "vuukle_token", diff --git a/metadata/modules/weboramaRtdProvider.json b/metadata/modules/weboramaRtdProvider.json index 8708b232e86..311a492a314 100644 --- a/metadata/modules/weboramaRtdProvider.json +++ b/metadata/modules/weboramaRtdProvider.json @@ -1,8 +1,8 @@ { "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { - "https://weborama.com/deviceStorage.json": { - "timestamp": "2026-01-21T15:17:11.454Z", + "https://cstatic.weborama.fr/tcf/deviceStorage.json": { + "timestamp": "2026-03-14T22:02:22.551Z", "disclosures": [] } }, @@ -11,7 +11,7 @@ "componentType": "rtd", "componentName": "weborama", "gvlid": 284, - "disclosureURL": "https://weborama.com/deviceStorage.json" + "disclosureURL": "https://cstatic.weborama.fr/tcf/deviceStorage.json" } ] } \ No newline at end of file diff --git a/metadata/modules/welectBidAdapter.json b/metadata/modules/welectBidAdapter.json index 508cece746c..580ef728ba0 100644 --- a/metadata/modules/welectBidAdapter.json +++ b/metadata/modules/welectBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://www.welect.de/deviceStorage.json": { - "timestamp": "2026-01-21T15:17:11.708Z", + "timestamp": "2026-03-14T22:02:22.712Z", "disclosures": [] } }, diff --git a/metadata/modules/yahooAdsBidAdapter.json b/metadata/modules/yahooAdsBidAdapter.json index 32a6ab9844c..5d0c8795bda 100644 --- a/metadata/modules/yahooAdsBidAdapter.json +++ b/metadata/modules/yahooAdsBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://meta.legal.yahoo.com/iab-tcf/v2/device-storage-disclosure.json": { - "timestamp": "2026-01-21T15:17:12.126Z", + "timestamp": "2026-03-14T22:02:23.088Z", "disclosures": [ { "identifier": "vmcid", diff --git a/metadata/modules/yaleoBidAdapter.json b/metadata/modules/yaleoBidAdapter.json new file mode 100644 index 00000000000..1baceb88c18 --- /dev/null +++ b/metadata/modules/yaleoBidAdapter.json @@ -0,0 +1,18 @@ +{ + "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", + "disclosures": { + "https://audienzz.com/device_storage_disclosure_vendor_783.json": { + "timestamp": "2026-03-14T22:02:23.088Z", + "disclosures": [] + } + }, + "components": [ + { + "componentType": "bidder", + "componentName": "yaleo", + "aliasOf": null, + "gvlid": 783, + "disclosureURL": "https://audienzz.com/device_storage_disclosure_vendor_783.json" + } + ] +} \ No newline at end of file diff --git a/metadata/modules/yieldlabBidAdapter.json b/metadata/modules/yieldlabBidAdapter.json index 6a868978be0..b439a5d0bda 100644 --- a/metadata/modules/yieldlabBidAdapter.json +++ b/metadata/modules/yieldlabBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://ad.yieldlab.net/deviceStorage.json": { - "timestamp": "2026-01-21T15:17:12.127Z", + "timestamp": "2026-03-14T22:02:23.088Z", "disclosures": [] } }, diff --git a/metadata/modules/yieldloveBidAdapter.json b/metadata/modules/yieldloveBidAdapter.json index f9f446b01c7..375051960d7 100644 --- a/metadata/modules/yieldloveBidAdapter.json +++ b/metadata/modules/yieldloveBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://cdn-a.yieldlove.com/deviceStorage.json": { - "timestamp": "2026-01-21T15:17:12.242Z", + "timestamp": "2026-03-14T22:02:23.208Z", "disclosures": [ { "identifier": "session_id", diff --git a/metadata/modules/yieldmoBidAdapter.json b/metadata/modules/yieldmoBidAdapter.json index 0eef8ded2e6..728f8d5e421 100644 --- a/metadata/modules/yieldmoBidAdapter.json +++ b/metadata/modules/yieldmoBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://devicestoragedisclosureurl.yieldmo.com/deviceStorage.json": { - "timestamp": "2026-01-21T15:17:12.263Z", + "timestamp": "2026-03-14T22:02:23.250Z", "disclosures": [] } }, diff --git a/metadata/modules/zeotapIdPlusIdSystem.json b/metadata/modules/zeotapIdPlusIdSystem.json index 60a5aa93435..f0787ac7565 100644 --- a/metadata/modules/zeotapIdPlusIdSystem.json +++ b/metadata/modules/zeotapIdPlusIdSystem.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://spl.zeotap.com/assets/iab-disclosure.json": { - "timestamp": "2026-01-21T15:17:12.335Z", + "timestamp": "2026-03-14T22:02:23.338Z", "disclosures": [] } }, diff --git a/metadata/modules/zeta_globalBidAdapter.json b/metadata/modules/zeta_globalBidAdapter.json index 52293da3a42..f496360fa3b 100644 --- a/metadata/modules/zeta_globalBidAdapter.json +++ b/metadata/modules/zeta_globalBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://zetaglobal.com/ZetaDeviceStorageDisclosure.json": { - "timestamp": "2026-01-21T15:17:12.498Z", + "timestamp": "2026-03-14T22:02:23.449Z", "disclosures": [] } }, diff --git a/metadata/modules/zeta_global_sspBidAdapter.json b/metadata/modules/zeta_global_sspBidAdapter.json index 305a257359d..d623f5e60ec 100644 --- a/metadata/modules/zeta_global_sspBidAdapter.json +++ b/metadata/modules/zeta_global_sspBidAdapter.json @@ -2,7 +2,7 @@ "NOTICE": "do not edit - this file is autogenerated by `gulp update-metadata`", "disclosures": { "https://zetaglobal.com/ZetaDeviceStorageDisclosure.json": { - "timestamp": "2026-01-21T15:17:12.572Z", + "timestamp": "2026-03-14T22:02:23.538Z", "disclosures": [] } }, diff --git a/metadata/overrides.mjs b/metadata/overrides.mjs index 214f778485c..869069f94d3 100644 --- a/metadata/overrides.mjs +++ b/metadata/overrides.mjs @@ -16,6 +16,5 @@ export default { operaadsIdSystem: 'operaId', relevadRtdProvider: 'RelevadRTDModule', sirdataRtdProvider: 'SirdataRTDModule', - fanBidAdapter: 'freedomadnetwork', - ringieraxelspringerBidAdapter: 'das' + fanBidAdapter: 'freedomadnetwork' } diff --git a/modules/.submodules.json b/modules/.submodules.json index c6274fdcbfd..244104c93da 100644 --- a/modules/.submodules.json +++ b/modules/.submodules.json @@ -14,7 +14,6 @@ "czechAdIdSystem", "dacIdSystem", "deepintentDpesIdSystem", - "dmdIdSystem", "euidIdSystem", "fabrickIdSystem", "freepassIdSystem", @@ -32,6 +31,7 @@ "kinessoIdSystem", "liveIntentIdSystem", "lmpIdSystem", + "locIdSystem", "lockrAIMIdSystem", "lotamePanoramaIdSystem", "merkleIdSystem", @@ -49,7 +49,6 @@ "pubProvidedIdSystem", "publinkIdSystem", "pubmaticIdSystem", - "quantcastIdSystem", "rewardedInterestIdSystem", "sharedIdSystem", "taboolaIdSystem", @@ -64,10 +63,6 @@ "yandexIdSystem", "zeotapIdPlusIdSystem" ], - "adpod": [ - "freeWheelAdserverVideo", - "gamAdpod" - ], "rtdModule": [ "1plusXRtdProvider", "51DegreesRtdProvider", @@ -117,6 +112,7 @@ "optimeraRtdProvider", "overtoneRtdProvider", "oxxionRtdProvider", + "panxoRtdProvider", "permutiveRtdProvider", "pubmaticRtdProvider", "pubxaiRtdProvider", @@ -141,10 +137,6 @@ "jwplayerVideoProvider", "videojsVideoProvider", "adplayerproVideoProvider" - ], - "paapi": [ - "paapiForGpt", - "topLevelPaapi" ] } -} \ No newline at end of file +} diff --git a/modules/33acrossAnalyticsAdapter.js b/modules/33acrossAnalyticsAdapter.js index ad9b33d6762..16a55eba026 100644 --- a/modules/33acrossAnalyticsAdapter.js +++ b/modules/33acrossAnalyticsAdapter.js @@ -337,7 +337,7 @@ function createReportFromCache(analyticsCache, completedAuctionId) { src: 'pbjs', analyticsVersion: ANALYTICS_VERSION, pbjsVersion: '$prebid.version$', // Replaced by build script - auctions: [ auctions[completedAuctionId] ], + auctions: [auctions[completedAuctionId]], } if (uspDataHandler.getConsentData()) { report.usPrivacy = uspDataHandler.getConsentData(); @@ -401,8 +401,7 @@ function analyticEventHandler({ eventType, args }) { case EVENTS.BID_REJECTED: onBidRejected(args); break; - case EVENTS.NO_BID: - case EVENTS.SEAT_NON_BID: + case EVENTS.NO_BID: // todo: need to also consider pbsanalytics where nonbid is not null setCachedBidStatus(args.auctionId, args.bidId, BidStatus.NOBID); break; case EVENTS.BIDDER_ERROR: diff --git a/modules/33acrossBidAdapter.js b/modules/33acrossBidAdapter.js index 3c2c364fafd..5e50f6a8792 100644 --- a/modules/33acrossBidAdapter.js +++ b/modules/33acrossBidAdapter.js @@ -1,5 +1,5 @@ -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {config} from '../src/config.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { config } from '../src/config.js'; import { deepAccess, getWindowSelf, @@ -11,12 +11,13 @@ import { mergeDeep, uniques } from '../src/utils.js'; -import {BANNER, VIDEO} from '../src/mediaTypes.js'; -import {isSlotMatchingAdUnitCode} from '../libraries/gptUtils/gptUtils.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; +import { isSlotMatchingAdUnitCode } from '../libraries/gptUtils/gptUtils.js'; import { ortbConverter } from '../libraries/ortbConverter/converter.js'; import { percentInView } from '../libraries/percentInView/percentInView.js'; -import {getMinSize} from '../libraries/sizeUtils/sizeUtils.js'; -import {isIframe} from '../libraries/omsUtils/index.js'; +import { getMinSize } from '../libraries/sizeUtils/sizeUtils.js'; +import { isIframe } from '../libraries/omsUtils/index.js'; +import { getAdUnitElement } from '../src/utils/adUnits.js'; // **************************** UTILS ************************** // const BIDDER_CODE = '33across'; @@ -81,8 +82,8 @@ function getTTXConfig() { } function collapseFalsy(obj) { - const data = Array.isArray(obj) ? [ ...obj ] : Object.assign({}, obj); - const falsyValuesToCollapse = [ null, undefined, '' ]; + const data = Array.isArray(obj) ? [...obj] : Object.assign({}, obj); + const falsyValuesToCollapse = [null, undefined, '']; for (const key in data) { if (falsyValuesToCollapse.includes(data[key]) || (Array.isArray(data[key]) && data[key].length === 0)) { @@ -166,7 +167,7 @@ function hasValidVideoProperties(bid) { } // If placement if defined, it must be a number - if ([ videoParams.placement, videoParams.plcmt ].some(value => ( + if ([videoParams.placement, videoParams.plcmt].some(value => ( typeof value !== 'undefined' && typeof value !== 'number' ))) { @@ -187,7 +188,7 @@ function hasValidVideoProperties(bid) { // **************************** BUILD REQUESTS *************************** // function buildRequests(bidRequests, bidderRequest = {}) { - const convertedORTB = converter.toORTB({bidRequests, bidderRequest}); + const convertedORTB = converter.toORTB({ bidRequests, bidderRequest }); const { ttxSettings, gdprConsent, @@ -280,10 +281,10 @@ function _createServerRequest({ bidRequests, gdprConsent = {}, referer, ttxSetti ext: { ttx: { prebidStartedAt: Date.now(), - caller: [ { + caller: [{ 'name': 'prebidjs', 'version': '$prebid.version$' - } ] + }] } }, test: test === 1 ? 1 : null @@ -336,7 +337,7 @@ function _buildImpORTB(bidRequest) { // BUILD REQUESTS: SIZE INFERENCE function _transformSizes(sizes) { if (isArray(sizes) && sizes.length === 2 && !isArray(sizes[0])) { - return [ _getSize(sizes) ]; + return [_getSize(sizes)]; } return sizes.map(_getSize); @@ -373,7 +374,7 @@ function _getProduct(bidRequest) { // BUILD REQUESTS: BANNER function _buildBannerORTB(bidRequest) { const bannerAdUnit = deepAccess(bidRequest, 'mediaTypes.banner', {}); - const element = _getAdSlotHTMLElement(bidRequest.adUnitCode); + const element = _getAdSlotHTMLElement(bidRequest); const sizes = _transformSizes(bannerAdUnit.sizes); @@ -387,7 +388,7 @@ function _buildBannerORTB(bidRequest) { formatExt = { ext: { ttx: { - bidfloors: [ bidfloors ] + bidfloors: [bidfloors] } } } @@ -444,7 +445,7 @@ function _buildVideoORTB(bidRequest) { Object.assign(video, { ext: { ttx: { - bidfloors: [ bidfloors ] + bidfloors: [bidfloors] } } }); @@ -459,7 +460,7 @@ function _getBidFloors(bidRequest, size, mediaType) { const bidFloors = bidRequest.getFloor({ currency: CURRENCY, mediaType, - size: [ size.w, size.h ] + size: [size.w, size.h] }); if (!isNaN(bidFloors?.floor) && (bidFloors?.currency === CURRENCY)) { @@ -478,6 +479,7 @@ function _getViewability(element, topWin, { w, h } = {}) { : 0; } +// TODO use utils/adUnits once that's unified in 11 function _mapAdUnitPathToElementId(adUnitCode) { if (isGptPubadsDefined()) { // eslint-disable-next-line no-undef @@ -500,9 +502,9 @@ function _mapAdUnitPathToElementId(adUnitCode) { return null; } -function _getAdSlotHTMLElement(adUnitCode) { - return document.getElementById(adUnitCode) || - document.getElementById(_mapAdUnitPathToElementId(adUnitCode)); +function _getAdSlotHTMLElement(bidRequest) { + return getAdUnitElement(bidRequest) || + document.getElementById(_mapAdUnitPathToElementId(bidRequest.adUnitCode)); } /** @@ -644,7 +646,7 @@ export const spec = { code: BIDDER_CODE, aliases: BIDDER_ALIASES, - supportedMediaTypes: [ BANNER, VIDEO ], + supportedMediaTypes: [BANNER, VIDEO], gvlid: GVLID, isBidRequestValid, buildRequests, diff --git a/modules/33acrossIdSystem.js b/modules/33acrossIdSystem.js index f0b58297da9..707b1cc9ce5 100644 --- a/modules/33acrossIdSystem.js +++ b/modules/33acrossIdSystem.js @@ -170,7 +170,7 @@ function filterEnabledSupplementalIds({ tp, fp, hem }, { storeFpid, storeTpid, e } function updateSupplementalIdStorage(supplementalId, storageConfig) { - const [ key, id, clear ] = supplementalId; + const [key, id, clear] = supplementalId; if (clear) { deleteFromStorage(key); @@ -222,7 +222,7 @@ export const thirtyThreeAcrossIdSubmodule = { * @param {SubmoduleConfig} [config] * @returns {IdResponse|undefined} */ - getId({ params = { }, enabledStorageTypes = [], storage: storageConfig = {} }, {gdpr: gdprConsentData} = {}) { + getId({ params = { }, enabledStorageTypes = [], storage: storageConfig = {} }, { gdpr: gdprConsentData } = {}) { if (typeof params.pid !== 'string') { logError(`${MODULE_NAME}: Submodule requires a partner ID to be defined`); diff --git a/modules/51DegreesRtdProvider.js b/modules/51DegreesRtdProvider.js index f5c76357ffc..d4f32758752 100644 --- a/modules/51DegreesRtdProvider.js +++ b/modules/51DegreesRtdProvider.js @@ -1,6 +1,6 @@ import { MODULE_TYPE_RTD } from '../src/activities/modules.js'; -import {loadExternalScript} from '../src/adloader.js'; -import {submodule} from '../src/hook.js'; +import { loadExternalScript } from '../src/adloader.js'; +import { submodule } from '../src/hook.js'; import { deepAccess, deepSetValue, @@ -8,10 +8,11 @@ import { mergeDeep, prefixLog, } from '../src/utils.js'; +import { getDevicePixelRatio } from '../libraries/devicePixelRatio/devicePixelRatio.js'; const MODULE_NAME = '51Degrees'; export const LOG_PREFIX = `[${MODULE_NAME} RTD Submodule]:`; -const {logMessage, logWarn, logError} = prefixLog(LOG_PREFIX); +const { logMessage, logWarn, logError } = prefixLog(LOG_PREFIX); // ORTB device types const ORTB_DEVICE_TYPE = { @@ -100,7 +101,7 @@ export const extractConfig = (moduleConfig, reqBidsConfigObj) => { throw new Error(LOG_PREFIX + ' replace in configuration with a resource key obtained from https://configure.51degrees.com/HNZ75HT1'); } - return {resourceKey, onPremiseJSUrl}; + return { resourceKey, onPremiseJSUrl }; } /** @@ -126,7 +127,7 @@ export const get51DegreesJSURL = (pathData, win) => { ); deepSetNotEmptyValue(qs, '51D_ScreenPixelsHeight', _window?.screen?.height); deepSetNotEmptyValue(qs, '51D_ScreenPixelsWidth', _window?.screen?.width); - deepSetNotEmptyValue(qs, '51D_PixelRatio', _window?.devicePixelRatio); + deepSetNotEmptyValue(qs, '51D_PixelRatio', getDevicePixelRatio(_window)); const _qs = formatQS(qs); const _qsString = _qs ? `${queryPrefix}${_qs}` : ''; @@ -219,6 +220,8 @@ export const convert51DegreesDataToOrtb2 = (data51) => { * @param {string} [device.hardwarevendor] Hardware vendor * @param {string} [device.hardwaremodel] Hardware model * @param {string[]} [device.hardwarename] Hardware name + * @param {string} [device.hardwarenameprefix] Hardware name prefix (e.g. "iPhone" from "iPhone 12 Pro Max") + * @param {string} [device.hardwarenameversion] Hardware name version (e.g. "12 Pro Max" from "iPhone 12 Pro Max") * @param {string} [device.platformname] Platform name * @param {string} [device.platformversion] Platform version * @param {number} [device.screenpixelsheight] Screen height in pixels @@ -239,6 +242,7 @@ export const convert51DegreesDeviceToOrtb2 = (device) => { } const deviceModel = + device.hardwarenameprefix || device.hardwaremodel || ( device.hardwarename && device.hardwarename.length ? device.hardwarename.join(',') @@ -256,6 +260,7 @@ export const convert51DegreesDeviceToOrtb2 = (device) => { deepSetNotEmptyValue(ortb2Device, 'devicetype', ORTB_DEVICE_TYPE_MAP.get(device.devicetype)); deepSetNotEmptyValue(ortb2Device, 'make', device.hardwarevendor); deepSetNotEmptyValue(ortb2Device, 'model', deviceModel); + deepSetNotEmptyValue(ortb2Device, 'hwv', device.hardwarenameversion); deepSetNotEmptyValue(ortb2Device, 'os', device.platformname); deepSetNotEmptyValue(ortb2Device, 'osv', device.platformversion); deepSetNotEmptyValue(ortb2Device, 'h', device.screenpixelsphysicalheight || device.screenpixelsheight); @@ -269,7 +274,7 @@ export const convert51DegreesDeviceToOrtb2 = (device) => { deepSetValue(ortb2Device, 'ext.fod.tpc', device.thirdpartycookiesenabled === 'True' ? 1 : 0); } - return {device: ortb2Device}; + return { device: ortb2Device }; } /** @@ -281,7 +286,7 @@ export const convert51DegreesDeviceToOrtb2 = (device) => { export const getBidRequestData = (reqBidsConfigObj, callback, moduleConfig, userConsent) => { try { // Get the required config - const {resourceKey, onPremiseJSUrl} = extractConfig(moduleConfig, reqBidsConfigObj); + const { resourceKey, onPremiseJSUrl } = extractConfig(moduleConfig, reqBidsConfigObj); logMessage('Resource key: ', resourceKey); logMessage('On-premise JS URL: ', onPremiseJSUrl); @@ -295,7 +300,7 @@ export const getBidRequestData = (reqBidsConfigObj, callback, moduleConfig, user getHighEntropyValues(['model', 'platform', 'platformVersion', 'fullVersionList']).then((hev) => { // Get 51Degrees JS URL, which is either cloud or on-premise - const scriptURL = get51DegreesJSURL({resourceKey, onPremiseJSUrl, hev}); + const scriptURL = get51DegreesJSURL({ resourceKey, onPremiseJSUrl, hev }); logMessage('URL of the script to be injected: ', scriptURL); // Inject 51Degrees script, get device data and merge it into the ORTB2 object @@ -312,7 +317,7 @@ export const getBidRequestData = (reqBidsConfigObj, callback, moduleConfig, user logMessage('reqBidsConfigObj: ', reqBidsConfigObj); callback(); }); - }, document, {crossOrigin: 'anonymous'}); + }, document, { crossOrigin: 'anonymous' }); }); } catch (error) { // In case of an error, log it and continue diff --git a/modules/51DegreesRtdProvider.md b/modules/51DegreesRtdProvider.md index db4b930c25e..b38fed3dfaf 100644 --- a/modules/51DegreesRtdProvider.md +++ b/modules/51DegreesRtdProvider.md @@ -10,7 +10,7 @@ 51Degrees module enriches an OpenRTB request with [51Degrees Device Data](https://51degrees.com/documentation/index.html). -51Degrees module sets the following fields of the device object: `devicetype`, `make`, `model`, `os`, `osv`, `h`, `w`, `ppi`, `pxratio`. Interested bidder adapters may use these fields as needed. +51Degrees module sets the following fields of the device object: `devicetype`, `make`, `model`, `hwv`, `os`, `osv`, `h`, `w`, `ppi`, `pxratio`. Interested bidder adapters may use these fields as needed. The module also adds a `device.ext.fod` extension object (fod == fifty one degrees) and sets `device.ext.fod.deviceId` to a permanent device ID, which can be rapidly looked up in on-premise data, exposing over 250 properties, including device age, chipset, codec support, price, operating system and app/browser versions, age, and embedded features. @@ -18,7 +18,7 @@ It also sets `device.ext.fod.tpc` key to a binary value to indicate whether thir The module supports on-premise and cloud device detection services, with free options for both. -A free resource key for use with 51Degrees cloud service can be obtained from [51Degrees cloud configuration](https://configure.51degrees.com/7bL8jDGz). This is the simplest approach to trial the module. +A free resource key for use with 51Degrees cloud service can be obtained from [51Degrees cloud configuration](https://configure.51degrees.com/jJqVnTJR). This is the simplest approach to trial the module. An interface-compatible self-hosted service can be used with .NET, Java, Node, PHP, and Python. See [51Degrees examples](https://51degrees.com/documentation/_examples__device_detection__getting_started__web__on_premise.html). @@ -40,12 +40,14 @@ gulp build --modules=rtdModule,51DegreesRtdProvider,appnexusBidAdapter,... #### Resource Key -In order to use the module, please first obtain a Resource Key using the [Configurator tool](https://configure.51degrees.com/7bL8jDGz) - choose the following properties: +In order to use the module, please first obtain a Resource Key using the [Configurator tool](https://configure.51degrees.com/jJqVnTJR) - choose the following properties: * DeviceId * DeviceType * HardwareVendor * HardwareName +* HardwareNamePrefix +* HardwareNameVersion * HardwareModel * PlatformName * PlatformVersion @@ -111,7 +113,7 @@ pbjs.setConfig({ waitForIt: true, // should be true, otherwise the auctionDelay will be ignored params: { resourceKey: '', - // Get your resource key from https://configure.51degrees.com/7bL8jDGz + // Get your resource key from https://configure.51degrees.com/jJqVnTJR // alternatively, you can use the on-premise version of the 51Degrees service and connect to your chosen endpoint // onPremiseJSUrl: 'https://localhost/51Degrees.core.js' }, diff --git a/modules/AsteriobidPbmAnalyticsAdapter.js b/modules/AsteriobidPbmAnalyticsAdapter.js index 3783f6c3765..5dc953174c9 100644 --- a/modules/AsteriobidPbmAnalyticsAdapter.js +++ b/modules/AsteriobidPbmAnalyticsAdapter.js @@ -1,17 +1,17 @@ import { deepClone, generateUUID, getParameterByName, hasNonSerializableProperty, logError, parseUrl, logInfo } from '../src/utils.js'; -import {ajaxBuilder} from '../src/ajax.js'; +import { ajaxBuilder } from '../src/ajax.js'; import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; -import {getStorageManager} from '../src/storageManager.js'; +import { getStorageManager } from '../src/storageManager.js'; import { EVENTS } from '../src/constants.js'; -import {MODULE_TYPE_ANALYTICS} from '../src/activities/modules.js'; +import { MODULE_TYPE_ANALYTICS } from '../src/activities/modules.js'; import { getViewportSize } from '../libraries/viewport/viewport.js'; import { collectUtmTagData, trimAdUnit, trimBid, trimBidderRequest } from '../libraries/asteriobidUtils/asteriobidUtils.js'; /** * prebidmanagerAnalyticsAdapter.js - analytics adapter for prebidmanager */ -export const storage = getStorageManager({moduleType: MODULE_TYPE_ANALYTICS, moduleName: 'asteriobidpbm'}); +export const storage = getStorageManager({ moduleType: MODULE_TYPE_ANALYTICS, moduleName: 'asteriobidpbm' }); const DEFAULT_EVENT_URL = 'https://endpt.prebidmanager.com/endpoint'; const analyticsType = 'endpoint'; const analyticsName = 'Asteriobid PBM Analytics'; @@ -26,7 +26,7 @@ var _bidRequestTimeout = 0; let flushInterval; var pmAnalyticsEnabled = false; -const {width: x, height: y} = getViewportSize(); +const { width: x, height: y } = getViewportSize(); var _pageView = { eventType: 'pageView', @@ -43,8 +43,8 @@ var _eventQueue = [ _pageView ]; -const prebidmanagerAnalytics = Object.assign(adapter({url: DEFAULT_EVENT_URL, analyticsType}), { - track({eventType, args}) { +const prebidmanagerAnalytics = Object.assign(adapter({ url: DEFAULT_EVENT_URL, analyticsType }), { + track({ eventType, args }) { handleEvent(eventType, args); } }); @@ -231,9 +231,6 @@ function handleEvent(eventType, eventArgs) { case EVENTS.REQUEST_BIDS: { break; } - case EVENTS.ADD_AD_UNITS: { - break; - } case EVENTS.AD_RENDER_FAILED: { pmEvent.bid = eventArgs.bid; pmEvent.message = eventArgs.message; diff --git a/modules/_moduleMetadata.js b/modules/_moduleMetadata.js index bddb48a165c..d3e9fb34376 100644 --- a/modules/_moduleMetadata.js +++ b/modules/_moduleMetadata.js @@ -3,10 +3,10 @@ * Cfr. `gulp extract-metadata` */ -import {getGlobal} from '../src/prebidGlobal.js'; +import { getGlobal } from '../src/prebidGlobal.js'; import adapterManager from '../src/adapterManager.js'; -import {hook} from '../src/hook.js'; -import {GDPR_GVLIDS, VENDORLESS_GVLID} from '../src/consentHandler.js'; +import { hook } from '../src/hook.js'; +import { GDPR_GVLIDS, VENDORLESS_GVLID } from '../src/consentHandler.js'; import { MODULE_TYPE_ANALYTICS, MODULE_TYPE_BIDDER, @@ -85,7 +85,7 @@ function uidMetadata() { function analyticsMetadata() { return Object.fromEntries( Object.entries(adapterManager.analyticsRegistry) - .map(([provider, {gvlid, adapter}]) => { + .map(([provider, { gvlid, adapter }]) => { return [ provider, { diff --git a/modules/a1MediaBidAdapter.js b/modules/a1MediaBidAdapter.js index d640bbfe2d7..1df230dcfc1 100644 --- a/modules/a1MediaBidAdapter.js +++ b/modules/a1MediaBidAdapter.js @@ -89,10 +89,10 @@ export const spec = { adm: replaceAuctionPrice(bidItem.adm, bidItem.price), nurl: replaceAuctionPrice(bidItem.nurl, bidItem.price) })); - return {...seatbidItem, bid: parsedBid}; + return { ...seatbidItem, bid: parsedBid }; }); - const responseBody = {...serverResponse.body, seatbid: parsedSeatbid}; + const responseBody = { ...serverResponse.body, seatbid: parsedSeatbid }; const bids = converter.fromORTB({ response: responseBody, request: bidRequest.data, diff --git a/modules/a1MediaRtdProvider.js b/modules/a1MediaRtdProvider.js index 1fbe88ecfa0..682b6145996 100644 --- a/modules/a1MediaRtdProvider.js +++ b/modules/a1MediaRtdProvider.js @@ -14,7 +14,7 @@ const SCRIPT_URL = 'https://linkback.contentsfeed.com/src'; export const A1_SEG_KEY = '__a1tg'; export const A1_AUD_KEY = 'a1_gid'; -export const storage = getStorageManager({moduleType: MODULE_TYPE_RTD, moduleName: MODULE_NAME}); +export const storage = getStorageManager({ moduleType: MODULE_TYPE_RTD, moduleName: MODULE_NAME }); /** @type {RtdSubmodule} */ export const subModuleObj = { @@ -64,7 +64,7 @@ function alterBidRequests(reqBidsConfigObj, callback, config, userConsent) { ext: { segtax: 900 }, - segment: a1seg.split(',').map(x => ({id: x})) + segment: a1seg.split(',').map(x => ({ id: x })) }; const a1UserEid = { diff --git a/modules/a4gBidAdapter.js b/modules/a4gBidAdapter.js index 5bc3591e502..0d8d7514004 100644 --- a/modules/a4gBidAdapter.js +++ b/modules/a4gBidAdapter.js @@ -1,4 +1,4 @@ -import {registerBidder} from '../src/adapters/bidderFactory.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; import { _each } from '../src/utils.js'; const A4G_BIDDER_CODE = 'a4g'; diff --git a/modules/aaxBlockmeterRtdProvider.js b/modules/aaxBlockmeterRtdProvider.js index 0a72e4e36f1..f36cc2434d9 100644 --- a/modules/aaxBlockmeterRtdProvider.js +++ b/modules/aaxBlockmeterRtdProvider.js @@ -1,5 +1,5 @@ -import {isEmptyStr, isStr, logError, isFn, logWarn} from '../src/utils.js'; -import {submodule} from '../src/hook.js'; +import { isEmptyStr, isStr, logError, isFn, logWarn } from '../src/utils.js'; +import { submodule } from '../src/hook.js'; import { loadExternalScript } from '../src/adloader.js'; import { MODULE_TYPE_RTD } from '../src/activities/modules.js'; diff --git a/modules/ablidaBidAdapter.js b/modules/ablidaBidAdapter.js index 3881d06f81a..9b61ad0d5b3 100644 --- a/modules/ablidaBidAdapter.js +++ b/modules/ablidaBidAdapter.js @@ -1,7 +1,7 @@ -import {triggerPixel} from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js'; -import {convertOrtbRequestToProprietaryNative} from '../src/native.js'; +import { triggerPixel } from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; +import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; import { getViewportSize } from '../libraries/viewport/viewport.js'; /** diff --git a/modules/aceexBidAdapter.js b/modules/aceexBidAdapter.js new file mode 100644 index 00000000000..71c3ac070dc --- /dev/null +++ b/modules/aceexBidAdapter.js @@ -0,0 +1,97 @@ +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; +import { + buildRequestsBase, + buildPlacementProcessingFunction, +} from '../libraries/teqblazeUtils/bidderUtils.js'; + +import { deepAccess } from '../src/utils.js'; + +const BIDDER_CODE = 'aceex'; +const GVLID = 1387; +const AD_REQUEST_URL = 'https://bl-us.aceex.io/?secret_key=prebidjs'; + +const addCustomFieldsToPlacement = (bid, bidderRequest, placement) => { + placement.trafficType = placement.adFormat; + placement.publisherId = bid.params.publisherId; + placement.internalKey = bid.params.internalKey; +}; + +const placementProcessingFunction = buildPlacementProcessingFunction({ addCustomFieldsToPlacement }); + +export const spec = { + code: BIDDER_CODE, + gvlid: GVLID, + supportedMediaTypes: [BANNER, VIDEO, NATIVE], + + isBidRequestValid: (bid) => { + return !!(bid.bidId && bid.params?.publisherId && bid.params?.trafficType); + }, + + buildRequests: (validBidRequests = [], bidderRequest) => { + const base = buildRequestsBase({ adUrl: AD_REQUEST_URL, validBidRequests, bidderRequest, placementProcessingFunction }); + + base.data.cat = deepAccess(bidderRequest, 'ortb2.cat'); + base.data.keywords = deepAccess(bidderRequest, 'ortb2.keywords'); + base.data.badv = deepAccess(bidderRequest, 'ortb2.badv'); + base.data.wseat = deepAccess(bidderRequest, 'ortb2.wseat'); + base.data.bseat = deepAccess(bidderRequest, 'ortb2.bseat'); + + return base; + }, + + interpretResponse: (serverResponse, bidRequest) => { + if (!serverResponse || !serverResponse.body || !Array.isArray(serverResponse.body.seatbid)) return []; + + const repackedBids = []; + + serverResponse.body.seatbid.forEach(seatbidItem => { + seatbidItem.bid.forEach((bid) => { + const originalPlacement = bidRequest.data.placements?.find(pl => pl.bidId === bid.id); + + const repackedBid = { + cpm: bid.price, + creativeId: bid.crid, + currency: 'USD', + dealId: bid.dealid, + height: bid.h, + width: bid.w, + mediaType: originalPlacement.adFormat, + netRevenue: true, + requestId: bid.id, + ttl: 1200, + meta: { + advertiserDomains: bid.adomain + }, + }; + + switch (originalPlacement.adFormat) { + case 'video': + repackedBid.vastXml = bid.adm; + break; + + case 'banner': + repackedBid.ad = bid.adm; + break; + + case 'native': + const nativeResponse = JSON.parse(bid.adm).native; + + const { assets, imptrackers, link } = nativeResponse; + repackedBid.native = { + ortb: { assets, imptrackers, link }, + }; + break; + + default: break; + }; + + repackedBids.push(repackedBid); + }) + }); + + return repackedBids; + }, +}; + +registerBidder(spec); diff --git a/modules/aceexBidAdapter.md b/modules/aceexBidAdapter.md new file mode 100644 index 00000000000..6efa00acd47 --- /dev/null +++ b/modules/aceexBidAdapter.md @@ -0,0 +1,67 @@ +# Overview + +``` +Module Name: Aceex Bidder Adapter +Module Type: Bidder Adapter +Maintainer: tech@aceex.io +``` + +# Description + +Module that connects Prebid.JS publishers to Aceex ad-exchange + +# Parameters + +| Name | Scope | Description | Example | +| :------------ | :------- | :------------------------ | :------------------- | +| `publisherId` | required | Publisher ID on platform | 219 | +| `trafficType` | required | Configures the mediaType that should be used. Values can be banner, native or video | "banner" | +| `internalKey` | required | Publisher hash on platform | "j1opp02hsma8119" | +| `bidfloor` | required | Bidfloor | 0.1 | + +# Test Parameters +``` + var adUnits = [ + // Will return static test banner + { + code: 'placementId_0', + mediaTypes: { + banner: { + sizes: [[300, 250]], + } + }, + bids: [ + { + bidder: 'aceex', + params: { + publisherId: 219, + internalKey: 'j1opp02hsma8119', + trafficType: 'banner', + bidfloor: 0.2 + } + } + ] + }, + // Will return test vast video + { + code: 'placementId_0', + mediaTypes: { + video: { + playerSize: [640, 480], + context: 'instream' + } + }, + bids: [ + { + bidder: 'aceex', + params: { + publisherId: 219, + internalKey: 'j1opp02hsma8119', + trafficType: 'video', + bidfloor: 1.1 + } + } + ] + } + ]; +``` diff --git a/modules/adWMGAnalyticsAdapter.js b/modules/adWMGAnalyticsAdapter.js index 73816422f04..f29ef639ab9 100644 --- a/modules/adWMGAnalyticsAdapter.js +++ b/modules/adWMGAnalyticsAdapter.js @@ -32,7 +32,7 @@ const bidWonObject = {}; let initOptions = {}; function postAjax(url, data) { - ajax(url, function () {}, data, {contentType: 'application/json', method: 'POST'}); + ajax(url, function () {}, data, { contentType: 'application/json', method: 'POST' }); } function handleInitSizes(adUnits) { diff --git a/modules/adWMGBidAdapter.js b/modules/adWMGBidAdapter.js index 8ae6d82ef61..d6ed1ff25b9 100644 --- a/modules/adWMGBidAdapter.js +++ b/modules/adWMGBidAdapter.js @@ -4,7 +4,7 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { config } from '../src/config.js'; import { BANNER } from '../src/mediaTypes.js'; import { parseUserAgentDetailed } from '../libraries/userAgentUtils/detailed.js'; -import {tryAppendQueryString} from '../libraries/urlUtils/urlUtils.js'; +import { tryAppendQueryString } from '../libraries/urlUtils/urlUtils.js'; const BIDDER_CODE = 'adWMG'; const ENDPOINT = 'https://hb.adwmg.com/hb'; diff --git a/modules/adagioAnalyticsAdapter.js b/modules/adagioAnalyticsAdapter.js index 1c3241ef19d..99661583743 100644 --- a/modules/adagioAnalyticsAdapter.js +++ b/modules/adagioAnalyticsAdapter.js @@ -158,7 +158,7 @@ function sendRequest(qp) { }, {}); const url = `${ENDPOINT}?${Object.keys(qp).map(key => `${key}=${enc(qp[key])}`).join('&')}`; - ajax(url, null, null, {method: 'GET'}); + ajax(url, null, null, { method: 'GET' }); }; /** diff --git a/modules/adagioBidAdapter.js b/modules/adagioBidAdapter.js index 9d04e166600..3da9746128b 100644 --- a/modules/adagioBidAdapter.js +++ b/modules/adagioBidAdapter.js @@ -25,6 +25,7 @@ import { getGptSlotInfoForAdUnitCode } from '../libraries/gptUtils/gptUtils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { userSync } from '../src/userSync.js'; import { validateOrtbFields } from '../src/prebid.js'; +import { getAdUnitElement } from '../src/utils/adUnits.js'; const BIDDER_CODE = 'adagio'; const LOG_PREFIX = 'Adagio:'; @@ -466,7 +467,7 @@ const OUTSTREAM_RENDERER = { return; } - const el = document.getElementById(bid.adUnitCode); + const el = getAdUnitElement(bid); renderer.bootstrap(config, el, override); }, diff --git a/modules/adagioRtdProvider.js b/modules/adagioRtdProvider.js index dfc6361234e..ac1246fdc0a 100644 --- a/modules/adagioRtdProvider.js +++ b/modules/adagioRtdProvider.js @@ -31,7 +31,7 @@ import { _ADAGIO, getBestWindowForAdagio } from '../libraries/adagioUtils/adagio import { getGptSlotInfoForAdUnitCode } from '../libraries/gptUtils/gptUtils.js'; import { getBoundingClientRect } from '../libraries/boundingClientRect/boundingClientRect.js'; -import {getGlobalVarName} from '../src/buildOptions.js'; +import { getGlobalVarName } from '../src/buildOptions.js'; /** * @typedef {import('../modules/rtdModule/index.js').RtdSubmodule} RtdSubmodule @@ -495,6 +495,7 @@ function getElementFromTopWindow(element, currentWindow) { }; function getSlotPosition(divId) { + // TODO: this should use getAdUnitElement if (!isSafeFrameWindow() && !canAccessWindowTop()) { return ''; } diff --git a/modules/adclusterBidAdapter.js b/modules/adclusterBidAdapter.js new file mode 100644 index 00000000000..b8b1f248582 --- /dev/null +++ b/modules/adclusterBidAdapter.js @@ -0,0 +1,183 @@ +import { registerBidder } from "../src/adapters/bidderFactory.js"; +import { BANNER, VIDEO } from "../src/mediaTypes.js"; + +const BIDDER_CODE = "adcluster"; +const ENDPOINT = "https://core.adcluster.com.tr/bid"; + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [BANNER, VIDEO], + + isBidRequestValid(bid) { + return !!bid?.params?.unitId; + }, + + buildRequests(validBidRequests, bidderRequest) { + const _auctionId = bidderRequest.auctionId || ""; + const payload = { + bidderCode: bidderRequest.bidderCode, + auctionId: _auctionId, + bidderRequestId: bidderRequest.bidderRequestId, + bids: validBidRequests.map((b) => buildImp(b)), + auctionStart: bidderRequest.auctionStart, + timeout: bidderRequest.timeout, + start: bidderRequest.start, + regs: { ext: {} }, + user: { ext: {} }, + source: { ext: {} }, + }; + + // privacy + if (bidderRequest?.gdprConsent) { + payload.regs = payload.regs || { ext: {} }; + payload.regs.ext = payload.regs.ext || {}; + payload.regs.ext.gdpr = bidderRequest.gdprConsent.gdprApplies ? 1 : 0; + payload.user.ext.consent = bidderRequest.gdprConsent.consentString || ""; + } + if (bidderRequest?.uspConsent) { + payload.regs = payload.regs || { ext: {} }; + payload.regs.ext.us_privacy = bidderRequest.uspConsent; + } + if (bidderRequest?.ortb2?.regs?.gpp) { + payload.regs = payload.regs || { ext: {} }; + payload.regs.ext.gpp = bidderRequest.ortb2.regs.gpp; + payload.regs.ext.gppSid = bidderRequest.ortb2.regs.gpp_sid; + } + if (validBidRequests[0]?.userIdAsEids) { + payload.user.ext.eids = validBidRequests[0].userIdAsEids; + } + if (validBidRequests[0]?.ortb2?.source?.ext?.schain) { + payload.source.ext.schain = validBidRequests[0].ortb2.source.ext.schain; + } + + return { + method: "POST", + url: ENDPOINT, + data: payload, + options: { contentType: "text/plain" }, + }; + }, + + interpretResponse(serverResponse) { + const body = serverResponse?.body; + if (!body || !Array.isArray(body)) return []; + const bids = []; + + body.forEach((b) => { + const mediaType = detectMediaType(b); + const bid = { + requestId: b.requestId, + cpm: b.cpm, + currency: b.currency, + width: b.width, + height: b.height, + creativeId: b.creativeId, + ttl: b.ttl, + netRevenue: b.netRevenue, + meta: { + advertiserDomains: b.meta?.advertiserDomains || [], + }, + mediaType, + }; + + if (mediaType === BANNER) { + bid.ad = b.ad; + } + if (mediaType === VIDEO) { + bid.vastUrl = b.ad; + } + bids.push(bid); + }); + + return bids; + }, +}; + +/* ---------- helpers ---------- */ + +function buildImp(bid) { + const _transactionId = bid.transactionId || ""; + const _adUnitId = bid.adUnitId || ""; + const _auctionId = bid.auctionId || ""; + const imp = { + params: { + unitId: bid.params.unitId, + }, + bidId: bid.bidId, + bidderRequestId: bid.bidderRequestId, + transactionId: _transactionId, + adUnitId: _adUnitId, + auctionId: _auctionId, + ext: { + floors: getFloorsAny(bid), + }, + }; + + if (bid.params && bid.params.previewMediaId) { + imp.params.previewMediaId = bid.params.previewMediaId; + } + + const mt = bid.mediaTypes || {}; + + // BANNER + if (mt.banner?.sizes?.length) { + imp.width = mt.banner.sizes[0] && mt.banner.sizes[0][0]; + imp.height = mt.banner.sizes[0] && mt.banner.sizes[0][1]; + } + if (mt.video) { + const v = mt.video; + const playerSize = toSizeArray(v.playerSize); + const [vw, vh] = playerSize?.[0] || []; + imp.width = vw; + imp.height = vh; + imp.video = { + minduration: v.minduration || 1, + maxduration: v.maxduration || 120, + ext: { + context: v.context || "instream", + floor: getFloors(bid, "video", playerSize?.[0]), + }, + }; + } + + return imp; +} + +function toSizeArray(s) { + if (!s) return null; + // playerSize can be [w,h] or [[w,h], [w2,h2]] + return Array.isArray(s[0]) ? s : [s]; +} + +function getFloors(bid, mediaType = "banner", size) { + try { + if (!bid.getFloor) return null; + // size can be [w,h] or '*' + const sz = Array.isArray(size) ? size : "*"; + const res = bid.getFloor({ mediaType, size: sz }); + return res && typeof res.floor === "number" ? res.floor : null; + } catch { + return null; + } +} + +function detectMediaType(bid) { + if (bid.mediaType === "video") return VIDEO; + else return BANNER; +} + +function getFloorsAny(bid) { + // Try to collect floors per type + const out = {}; + const mt = bid.mediaTypes || {}; + if (mt.banner) { + out.banner = getFloors(bid, "banner", "*"); + } + if (mt.video) { + const ps = toSizeArray(mt.video.playerSize); + out.video = getFloors(bid, "video", (ps && ps[0]) || "*"); + } + return out; +} + +registerBidder(spec); diff --git a/modules/adclusterBidAdapter.md b/modules/adclusterBidAdapter.md new file mode 100644 index 00000000000..59300e2c857 --- /dev/null +++ b/modules/adclusterBidAdapter.md @@ -0,0 +1,46 @@ +# Overview + +**Module Name**: Adcluster Bidder Adapter +**Module Type**: Bidder Adapter +**Maintainer**: dev@adcluster.com.tr + +# Description + +Prebid.js bidder adapter module for connecting to Adcluster. + +# Test Parameters + +``` +var adUnits = [ + { + code: 'adcluster-banner', + mediaTypes: { + banner: { + sizes: [[300, 250]], + } + }, + bids: [{ + bidder: 'adcluster', + params: { + unitId: '42d1f525-5792-47a6-846d-1825e53c97d6', + previewMediaId: "b4dbc48c-0b90-4628-bc55-f46322b89b63", + }, + }] + }, + { + code: 'adcluster-video', + mediaTypes: { + video: { + playerSize: [[640, 480]], + } + }, + bids: [{ + bidder: 'adcluster', + params: { + unitId: "37dd91b2-049d-4027-94b9-d63760fc10d3", + previewMediaId: "133b7dc9-bb6e-4ab2-8f95-b796cf19f27e", + }, + }] + } +]; +``` diff --git a/modules/addefendBidAdapter.js b/modules/addefendBidAdapter.js index 3ea95a6f63c..4393e3161e8 100644 --- a/modules/addefendBidAdapter.js +++ b/modules/addefendBidAdapter.js @@ -1,4 +1,4 @@ -import {registerBidder} from '../src/adapters/bidderFactory.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; const BIDDER_CODE = 'addefend'; const GVLID = 539; diff --git a/modules/adfBidAdapter.js b/modules/adfBidAdapter.js index cdc48b1e28d..2653880233c 100644 --- a/modules/adfBidAdapter.js +++ b/modules/adfBidAdapter.js @@ -1,10 +1,10 @@ // jshint esversion: 6, es3: false, node: true 'use strict'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js'; -import {deepAccess, deepClone, deepSetValue, getWinDimensions, parseSizesInput, setOnAny} from '../src/utils.js'; -import {Renderer} from '../src/Renderer.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; +import { deepAccess, deepClone, deepSetValue, getWinDimensions, parseSizesInput, setOnAny } from '../src/utils.js'; +import { Renderer } from '../src/Renderer.js'; import { getCurrencyFromBidderRequest } from '../libraries/ortb2Utils/currency.js'; const BIDDER_CODE = 'adf'; @@ -20,7 +20,7 @@ export const spec = { code: BIDDER_CODE, aliases: BIDDER_ALIAS, gvlid: GVLID, - supportedMediaTypes: [ NATIVE, BANNER, VIDEO ], + supportedMediaTypes: [NATIVE, BANNER, VIDEO], isBidRequestValid: (bid) => { const params = bid.params || {}; const { mid, inv, mname } = params; @@ -56,7 +56,7 @@ export const spec = { const pt = setOnAny(validBidRequests, 'params.pt') || setOnAny(validBidRequests, 'params.priceType') || 'net'; const test = setOnAny(validBidRequests, 'params.test'); const currency = getCurrencyFromBidderRequest(bidderRequest); - const cur = currency && [ currency ]; + const cur = currency && [currency]; const eids = setOnAny(validBidRequests, 'userIdAsEids'); const schain = setOnAny(validBidRequests, 'ortb2.source.ext.schain'); @@ -127,7 +127,7 @@ export const spec = { if (bannerParams && bannerParams.sizes) { const sizes = parseSizesInput(bannerParams.sizes); const format = sizes.map(size => { - const [ width, height ] = size.split('x'); + const [width, height] = size.split('x'); const w = parseInt(width, 10); const h = parseInt(height, 10); return { w, h }; @@ -223,7 +223,7 @@ export const spec = { } if (!bid.renderer && mediaType === VIDEO && deepAccess(bid, 'mediaTypes.video.context') === 'outstream') { - result.renderer = Renderer.install({id: bid.bidId, url: OUTSTREAM_RENDERER_URL, adUnitCode: bid.adUnitCode}); + result.renderer = Renderer.install({ id: bid.bidId, url: OUTSTREAM_RENDERER_URL, adUnitCode: bid.adUnitCode }); result.renderer.setRender(renderer); } diff --git a/modules/adgenerationBidAdapter.js b/modules/adgenerationBidAdapter.js index 779e40c8f9a..778cb697e80 100644 --- a/modules/adgenerationBidAdapter.js +++ b/modules/adgenerationBidAdapter.js @@ -61,9 +61,9 @@ export const spec = { * @return ServerRequest Info describing the request to the server. */ buildRequests: function (validBidRequests, bidderRequest) { - const ortbObj = converter.toORTB({bidRequests: validBidRequests, bidderRequest}); + const ortbObj = converter.toORTB({ bidRequests: validBidRequests, bidderRequest }); adgLogger.logInfo('ortbObj', ortbObj); - const {imp, ...rest} = ortbObj + const { imp, ...rest } = ortbObj const requests = imp.map((impObj) => { const customParams = impObj?.ext?.params; const id = getBidIdParameter('id', customParams); diff --git a/modules/adgridBidAdapter.ts b/modules/adgridBidAdapter.ts index e5ba2c8b672..6b5199817de 100644 --- a/modules/adgridBidAdapter.ts +++ b/modules/adgridBidAdapter.ts @@ -80,7 +80,7 @@ const buildRequests = ( bidRequests: BidRequest[], bidderRequest: ClientBidderRequest, ): AdapterRequest => { - const data:ORTBRequest = converter.toORTB({bidRequests, bidderRequest}) + const data:ORTBRequest = converter.toORTB({ bidRequests, bidderRequest }) const adapterRequest:AdapterRequest = { method: 'POST', url: REQUEST_URL, diff --git a/modules/adhashBidAdapter.js b/modules/adhashBidAdapter.js index a0846271ee5..a49345828ba 100644 --- a/modules/adhashBidAdapter.js +++ b/modules/adhashBidAdapter.js @@ -153,7 +153,7 @@ function brandSafety(badWords, maxScore) { export const spec = { code: ADHASH_BIDDER_CODE, - supportedMediaTypes: [ BANNER, VIDEO ], + supportedMediaTypes: [BANNER, VIDEO], isBidRequestValid: (bid) => { try { diff --git a/modules/adheseBidAdapter.js b/modules/adheseBidAdapter.js index 33b33ca998d..46b9039508b 100644 --- a/modules/adheseBidAdapter.js +++ b/modules/adheseBidAdapter.js @@ -88,7 +88,7 @@ export const spec = { syncurl += '&gdpr=' + (gdprConsent.gdprApplies ? 1 : 0); syncurl += '&consentString=' + encodeURIComponent(gdprConsent.consentString || ''); } - return [{type: 'iframe', url: syncurl}]; + return [{ type: 'iframe', url: syncurl }]; } } return []; diff --git a/modules/adipoloBidAdapter.js b/modules/adipoloBidAdapter.js index 51c2a97a1f0..4539c75574d 100644 --- a/modules/adipoloBidAdapter.js +++ b/modules/adipoloBidAdapter.js @@ -1,6 +1,6 @@ -import {BANNER, VIDEO} from '../src/mediaTypes.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {buildRequests, getUserSyncs, interpretResponse, isBidRequestValid} from '../libraries/xeUtils/bidderUtils.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { buildRequests, getUserSyncs, interpretResponse, isBidRequestValid } from '../libraries/xeUtils/bidderUtils.js'; import { getTimeZone } from '../libraries/timezone/timezone.js'; const BIDDER_CODE = 'adipolo'; diff --git a/modules/adkernelAdnAnalyticsAdapter.js b/modules/adkernelAdnAnalyticsAdapter.js index 725282ede6f..6e118f0a72c 100644 --- a/modules/adkernelAdnAnalyticsAdapter.js +++ b/modules/adkernelAdnAnalyticsAdapter.js @@ -1,18 +1,18 @@ import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; -import {EVENTS} from '../src/constants.js'; +import { EVENTS } from '../src/constants.js'; import adapterManager from '../src/adapterManager.js'; import { logError, parseUrl, _each } from '../src/utils.js'; -import {ajax} from '../src/ajax.js'; -import {getStorageManager} from '../src/storageManager.js'; -import {config} from '../src/config.js'; -import {MODULE_TYPE_ANALYTICS} from '../src/activities/modules.js'; +import { ajax } from '../src/ajax.js'; +import { getStorageManager } from '../src/storageManager.js'; +import { config } from '../src/config.js'; +import { MODULE_TYPE_ANALYTICS } from '../src/activities/modules.js'; const MODULE_CODE = 'adkernelAdn'; const GVLID = 14; const ANALYTICS_VERSION = '1.0.2'; const DEFAULT_QUEUE_TIMEOUT = 4000; const DEFAULT_HOST = 'tag.adkernel.com'; -const storageObj = getStorageManager({moduleType: MODULE_TYPE_ANALYTICS, moduleName: MODULE_CODE}); +const storageObj = getStorageManager({ moduleType: MODULE_TYPE_ANALYTICS, moduleName: MODULE_CODE }); const ADK_HB_EVENTS = { AUCTION_INIT: 'auctionInit', @@ -24,7 +24,7 @@ const ADK_HB_EVENTS = { }; function buildRequestTemplate(pubId) { - const {loc, ref} = getNavigationInfo(); + const { loc, ref } = getNavigationInfo(); return { ver: ANALYTICS_VERSION, @@ -43,9 +43,9 @@ function buildRequestTemplate(pubId) { } } -const analyticsAdapter = Object.assign(adapter({analyticsType: 'endpoint'}), +const analyticsAdapter = Object.assign(adapter({ analyticsType: 'endpoint' }), { - track({eventType, args}) { + track({ eventType, args }) { if (!analyticsAdapter.context) { return; } @@ -115,7 +115,7 @@ export default analyticsAdapter; function sendAll() { const events = analyticsAdapter.context.queue.popAll(); if (events.length !== 0) { - const req = Object.assign({}, analyticsAdapter.context.requestTemplate, {hb_ev: events}); + const req = Object.assign({}, analyticsAdapter.context.requestTemplate, { hb_ev: events }); analyticsAdapter.ajaxCall(JSON.stringify(req)); } } @@ -158,7 +158,7 @@ function trackBidTimeout(args) { } function createHbEvent(adapter, event, tagid = undefined, value = 0, time = 0) { - const ev = {event: event}; + const ev = { event: event }; if (adapter) { ev.adapter = adapter } diff --git a/modules/adkernelAdnBidAdapter.js b/modules/adkernelAdnBidAdapter.js index d7053120ae6..1ab96f7145b 100644 --- a/modules/adkernelAdnBidAdapter.js +++ b/modules/adkernelAdnBidAdapter.js @@ -1,8 +1,8 @@ -import {deepAccess, deepSetValue, isArray, isNumber, isStr, logInfo, parseSizesInput} from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER, VIDEO} from '../src/mediaTypes.js'; -import {config} from '../src/config.js'; -import {getBidFloor} from '../libraries/adkernelUtils/adkernelUtils.js' +import { deepAccess, deepSetValue, isArray, isNumber, isStr, logInfo, parseSizesInput } from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; +import { config } from '../src/config.js'; +import { getBidFloor } from '../libraries/adkernelUtils/adkernelUtils.js' const DEFAULT_ADKERNEL_DSP_DOMAIN = 'tag.adkernel.com'; const DEFAULT_MIMES = ['video/mp4', 'video/webm', 'application/x-shockwave-flash', 'application/javascript']; @@ -59,7 +59,7 @@ function canonicalizeSizesArray(sizes) { } function buildRequestParams(tags, bidderRequest) { - const {gdprConsent, uspConsent, refererInfo, ortb2} = bidderRequest; + const { gdprConsent, uspConsent, refererInfo, ortb2 } = bidderRequest; const req = { id: bidderRequest.bidderRequestId, // TODO: root-level `tid` is not ORTB; is this intentional? @@ -213,7 +213,7 @@ function buildSyncs(serverResponses, propName, type) { return serverResponses.filter(rps => rps.body && rps.body[propName]) .map(rsp => rsp.body[propName]) .reduce((a, b) => a.concat(b), []) - .map(syncUrl => ({type: type, url: syncUrl})); + .map(syncUrl => ({ type: type, url: syncUrl })); } registerBidder(spec); diff --git a/modules/adkernelBidAdapter.js b/modules/adkernelBidAdapter.js index d43002fd35e..3e22acb9f08 100644 --- a/modules/adkernelBidAdapter.js +++ b/modules/adkernelBidAdapter.js @@ -1,4 +1,3 @@ -import {getDNT} from '../libraries/dnt/index.js'; import { _each, contains, @@ -16,11 +15,11 @@ import { parseGPTSingleSizeArrayToRtbSize, triggerPixel } from '../src/utils.js'; -import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {config} from '../src/config.js'; -import {getAdUnitSizes} from '../libraries/sizeUtils/sizeUtils.js'; -import {getBidFloor} from '../libraries/adkernelUtils/adkernelUtils.js' +import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { config } from '../src/config.js'; +import { getAdUnitSizes } from '../libraries/sizeUtils/sizeUtils.js'; +import { getBidFloor } from '../libraries/adkernelUtils/adkernelUtils.js' /** * In case you're AdKernel whitelable platform's client who needs branded adapter to @@ -65,50 +64,51 @@ export const spec = { code: 'adkernel', gvlid: GVLID, aliases: [ - {code: 'headbidding'}, - {code: 'adsolut'}, - {code: 'oftmediahb'}, - {code: 'audiencemedia'}, - {code: 'waardex_ak'}, - {code: 'roqoon'}, - {code: 'adbite'}, - {code: 'houseofpubs'}, - {code: 'torchad'}, - {code: 'stringads'}, - {code: 'bcm'}, - {code: 'engageadx'}, - {code: 'converge', gvlid: 248}, - {code: 'adomega'}, - {code: 'denakop'}, - {code: 'rtbanalytica'}, - {code: 'unibots'}, - {code: 'ergadx'}, - {code: 'turktelekom'}, - {code: 'motionspots'}, - {code: 'sonic_twist'}, - {code: 'displayioads'}, - {code: 'rtbdemand_com'}, - {code: 'bidbuddy'}, - {code: 'didnadisplay'}, - {code: 'qortex'}, - {code: 'adpluto'}, - {code: 'headbidder'}, - {code: 'digiad'}, - {code: 'monetix'}, - {code: 'hyperbrainz'}, - {code: 'voisetech'}, - {code: 'global_sun'}, - {code: 'rxnetwork'}, - {code: 'revbid'}, - {code: 'spinx', gvlid: 1308}, - {code: 'oppamedia'}, - {code: 'pixelpluses', gvlid: 1209}, - {code: 'urekamedia'}, - {code: 'smartyexchange'}, - {code: 'infinety'}, - {code: 'qohere'}, - {code: 'blutonic'}, - {code: 'appmonsta', gvlid: 1283} + { code: 'headbidding' }, + { code: 'adsolut' }, + { code: 'oftmediahb' }, + { code: 'audiencemedia' }, + { code: 'waardex_ak' }, + { code: 'roqoon' }, + { code: 'adbite' }, + { code: 'houseofpubs' }, + { code: 'torchad' }, + { code: 'stringads' }, + { code: 'bcm' }, + { code: 'engageadx' }, + { code: 'converge', gvlid: 248 }, + { code: 'adomega' }, + { code: 'denakop' }, + { code: 'rtbanalytica' }, + { code: 'unibots' }, + { code: 'ergadx' }, + { code: 'turktelekom' }, + { code: 'motionspots' }, + { code: 'sonic_twist' }, + { code: 'displayioads' }, + { code: 'rtbdemand_com' }, + { code: 'bidbuddy' }, + { code: 'didnadisplay' }, + { code: 'qortex' }, + { code: 'adpluto' }, + { code: 'headbidder' }, + { code: 'digiad' }, + { code: 'monetix' }, + { code: 'hyperbrainz' }, + { code: 'voisetech' }, + { code: 'global_sun' }, + { code: 'rxnetwork' }, + { code: 'revbid' }, + { code: 'spinx', gvlid: 1308 }, + { code: 'oppamedia' }, + { code: 'pixelpluses', gvlid: 1209 }, + { code: 'urekamedia' }, + { code: 'smartyexchange' }, + { code: 'infinety' }, + { code: 'qohere' }, + { code: 'blutonic' }, + { code: 'appmonsta', gvlid: 1283 }, + { code: 'intlscoop' } ], supportedMediaTypes: [BANNER, VIDEO, NATIVE], @@ -140,7 +140,7 @@ export const spec = { const requests = []; const schain = bidRequests[0]?.ortb2?.source?.ext?.schain; _each(impGroups, impGroup => { - const {host, zoneId, imps} = impGroup; + const { host, zoneId, imps } = impGroup; const request = buildRtbRequest(imps, bidderRequest, schain); requests.push({ method: 'POST', @@ -242,7 +242,7 @@ export const spec = { return serverResponses.filter(rsp => rsp.body && rsp.body.ext && rsp.body.ext.adk_usersync) .map(rsp => rsp.body.ext.adk_usersync) .reduce((a, b) => a.concat(b), []) - .map(({url, type}) => ({type: SYNC_TYPES[type], url: url})); + .map(({ url, type }) => ({ type: SYNC_TYPES[type], url: url })); }, /** @@ -269,9 +269,9 @@ function groupImpressionsByHostZone(bidRequests, refererInfo) { bidRequests.map(bidRequest => buildImps(bidRequest, secure)) .reduce((acc, curr, index) => { const bidRequest = bidRequests[index]; - const {zoneId, host} = bidRequest.params; + const { zoneId, host } = bidRequest.params; const key = `${host}_${zoneId}`; - acc[key] = acc[key] || {host: host, zoneId: zoneId, imps: []}; + acc[key] = acc[key] || { host: host, zoneId: zoneId, imps: [] }; acc[key].imps.push(...curr); return acc; }, {}) @@ -299,7 +299,7 @@ function buildImps(bidRequest, secure) { if (mediaTypes?.banner) { if (isMultiformat) { - typedImp = {...imp}; + typedImp = { ...imp }; typedImp.id = imp.id + MULTI_FORMAT_SUFFIX_BANNER; } else { typedImp = imp; @@ -318,7 +318,7 @@ function buildImps(bidRequest, secure) { if (mediaTypes?.video) { if (isMultiformat) { - typedImp = {...imp}; + typedImp = { ...imp }; typedImp.id = typedImp.id + MULTI_FORMAT_SUFFIX_VIDEO; } else { typedImp = imp; @@ -341,7 +341,7 @@ function buildImps(bidRequest, secure) { if (mediaTypes?.native) { if (isMultiformat) { - typedImp = {...imp}; + typedImp = { ...imp }; typedImp.id = typedImp.id + MULTI_FORMAT_SUFFIX_NATIVE; } else { typedImp = imp; @@ -415,10 +415,7 @@ function makeDevice(fpd) { 'js': 1, 'language': getLanguage() }, fpd.device || {}); - if (getDNT()) { - device.dnt = 1; - } - return {device: device}; + return { device: device }; } /** @@ -428,12 +425,12 @@ function makeDevice(fpd) { * @returns {{site: Object}|{app: Object}} */ function makeSiteOrApp(bidderRequest, fpd) { - const {refererInfo} = bidderRequest; + const { refererInfo } = bidderRequest; const appConfig = config.getConfig('app'); if (isEmpty(appConfig)) { - return {site: createSite(refererInfo, fpd)} + return { site: createSite(refererInfo, fpd) } } else { - return {app: appConfig}; + return { app: appConfig }; } } @@ -444,7 +441,7 @@ function makeSiteOrApp(bidderRequest, fpd) { * @returns {{user: Object} | undefined} */ function makeUser(bidderRequest, fpd) { - const {gdprConsent} = bidderRequest; + const { gdprConsent } = bidderRequest; const user = fpd.user || {}; if (gdprConsent && gdprConsent.consentString !== undefined) { deepSetValue(user, 'ext.consent', gdprConsent.consentString); @@ -454,7 +451,7 @@ function makeUser(bidderRequest, fpd) { deepSetValue(user, 'ext.eids', eids); } if (!isEmpty(user)) { - return {user: user}; + return { user: user }; } } @@ -464,7 +461,7 @@ function makeUser(bidderRequest, fpd) { * @returns {{regs: Object} | undefined} */ function makeRegulations(bidderRequest) { - const {gdprConsent, uspConsent, gppConsent} = bidderRequest; + const { gdprConsent, uspConsent, gppConsent } = bidderRequest; const regs = {}; if (gdprConsent) { if (gdprConsent.gdprApplies !== undefined) { @@ -514,7 +511,7 @@ function makeBaseRequest(bidderRequest, imps, fpd) { * @param bidderRequest {BidderRequest} */ function makeSyncInfo(bidderRequest) { - const {bidderCode} = bidderRequest; + const { bidderCode } = bidderRequest; const syncMethod = getAllowedSyncMethod(bidderCode); if (syncMethod) { const res = {}; diff --git a/modules/adlooxAdServerVideo.js b/modules/adlooxAdServerVideo.js index d99d23743be..b7874b10997 100644 --- a/modules/adlooxAdServerVideo.js +++ b/modules/adlooxAdServerVideo.js @@ -174,22 +174,22 @@ function VASTWrapper(options, callback) { if (skipd) skip = durationToSeconds(skipd.trim()); const args = [ - [ 'client', '%%client%%' ], - [ 'platform_id', '%%platformid%%' ], - [ 'scriptname', 'adl_%%clientid%%' ], - [ 'tag_id', '%%tagid%%' ], - [ 'fwtype', 4 ], - [ 'vast', options.url ], - [ 'id11', 'video' ], - [ 'id12', '$ADLOOX_WEBSITE' ], - [ 'id18', (!skip || skip >= duration) ? 'fd' : 'od' ], - [ 'id19', 'na' ], - [ 'id20', 'na' ] + ['client', '%%client%%'], + ['platform_id', '%%platformid%%'], + ['scriptname', 'adl_%%clientid%%'], + ['tag_id', '%%tagid%%'], + ['fwtype', 4], + ['vast', options.url], + ['id11', 'video'], + ['id12', '$ADLOOX_WEBSITE'], + ['id18', (!skip || skip >= duration) ? 'fd' : 'od'], + ['id19', 'na'], + ['id20', 'na'] ]; - if (version && version !== 3) args.push([ 'version', version ]); - if (vpaid) args.push([ 'vpaid', 1 ]); - if (duration !== 15) args.push([ 'duration', duration ]); - if (skip) args.push([ 'skip', skip ]); + if (version && version !== 3) args.push(['version', version]); + if (vpaid) args.push(['vpaid', 1]); + if (duration !== 15) args.push(['duration', duration]); + if (skip) args.push(['skip', skip]); logInfo(MODULE, `processed VAST tag chain of depth ${chain.depth}, running callback`); diff --git a/modules/adlooxAnalyticsAdapter.js b/modules/adlooxAnalyticsAdapter.js index 99efaad3450..4b1461c79a5 100644 --- a/modules/adlooxAnalyticsAdapter.js +++ b/modules/adlooxAnalyticsAdapter.js @@ -6,11 +6,11 @@ import adapterManager from '../src/adapterManager.js'; import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; -import {loadExternalScript} from '../src/adloader.js'; -import {auctionManager} from '../src/auctionManager.js'; -import {AUCTION_COMPLETED} from '../src/auction.js'; -import {EVENTS} from '../src/constants.js'; -import {getRefererInfo} from '../src/refererDetection.js'; +import { loadExternalScript } from '../src/adloader.js'; +import { auctionManager } from '../src/auctionManager.js'; +import { AUCTION_COMPLETED } from '../src/auction.js'; +import { EVENTS } from '../src/constants.js'; +import { getRefererInfo } from '../src/refererDetection.js'; import { deepAccess, getUniqueIdentifierStr, @@ -26,7 +26,7 @@ import { mergeDeep, parseUrl } from '../src/utils.js'; -import {getGptSlotInfoForAdUnitCode} from '../libraries/gptUtils/gptUtils.js'; +import { getGptSlotInfoForAdUnitCode } from '../libraries/gptUtils/gptUtils.js'; import { MODULE_TYPE_ANALYTICS } from '../src/activities/modules.js'; const MODULE = 'adlooxAnalyticsAdapter'; @@ -159,9 +159,9 @@ analyticsAdapter.enableAnalytics = function(config) { .keys(config.options.params) .forEach(k => { if (!Array.isArray(config.options.params[k])) { - config.options.params[k] = [ config.options.params[k] ]; + config.options.params[k] = [config.options.params[k]]; } - config.options.params[k].forEach(v => analyticsAdapter.context.params.push([ k, v ])); + config.options.params[k].forEach(v => analyticsAdapter.context.params.push([k, v])); }); Object.keys(COMMAND_QUEUE).forEach(commandProcess); @@ -172,8 +172,9 @@ analyticsAdapter.enableAnalytics = function(config) { analyticsAdapter.originDisableAnalytics = analyticsAdapter.disableAnalytics; analyticsAdapter.disableAnalytics = function() { analyticsAdapter.context = null; - - analyticsAdapter.originDisableAnalytics(); + if (this.enabled) { + analyticsAdapter.originDisableAnalytics(); + } } analyticsAdapter.url = function(url, args, bid) { @@ -261,11 +262,11 @@ analyticsAdapter[`handle_${EVENTS.BID_WON}`] = function(bid) { logMessage(MODULE, `measuring '${bid.mediaType}' unit at '${bid.adUnitCode}'`); const params = analyticsAdapter.context.params.concat([ - [ 'tagid', '%%tagid%%' ], - [ 'platform', '%%platformid%%' ], - [ 'fwtype', 4 ], - [ 'targetelt', '%%targetelt%%' ], - [ 'creatype', '%%creatype%%' ] + ['tagid', '%%tagid%%'], + ['platform', '%%platformid%%'], + ['fwtype', 4], + ['targetelt', '%%targetelt%%'], + ['creatype', '%%creatype%%'] ]); loadExternalScript(analyticsAdapter.url(`${analyticsAdapter.context.js}#`, params, bid), MODULE_TYPE_ANALYTICS, 'adloox'); diff --git a/modules/adlooxRtdProvider.js b/modules/adlooxRtdProvider.js index 4fa954ded14..2a4e61f9f79 100644 --- a/modules/adlooxRtdProvider.js +++ b/modules/adlooxRtdProvider.js @@ -11,12 +11,12 @@ /* eslint prebid/validate-imports: "off" */ -import {auctionManager} from '../src/auctionManager.js'; -import {command as analyticsCommand, COMMAND} from './adlooxAnalyticsAdapter.js'; -import {submodule} from '../src/hook.js'; -import {ajax} from '../src/ajax.js'; -import {getGlobal} from '../src/prebidGlobal.js'; -import {getRefererInfo} from '../src/refererDetection.js'; +import { auctionManager } from '../src/auctionManager.js'; +import { command as analyticsCommand, COMMAND } from './adlooxAnalyticsAdapter.js'; +import { submodule } from '../src/hook.js'; +import { ajax } from '../src/ajax.js'; +import { getGlobal } from '../src/prebidGlobal.js'; +import { getRefererInfo } from '../src/refererDetection.js'; import { _each, _map, @@ -35,7 +35,9 @@ import { parseUrl, safeJSONParse } from '../src/utils.js'; -import {getGptSlotInfoForAdUnitCode} from '../libraries/gptUtils/gptUtils.js'; +import { getGptSlotInfoForAdUnitCode } from '../libraries/gptUtils/gptUtils.js'; +import { viewportIntersections } from '../libraries/percentInView/percentInView.js'; +import { getAdUnitElement } from '../src/utils/adUnits.js'; const MODULE_NAME = 'adloox'; const MODULE = `${MODULE_NAME}RtdProvider`; @@ -83,7 +85,7 @@ function init(config, userConsent) { return false; } - config.params.thresholds = config.params.thresholds || [ 50, 60, 70, 80, 90 ]; + config.params.thresholds = config.params.thresholds || [50, 60, 70, 80, 90]; function analyticsConfigCallback(data) { config = mergeDeep(config.params, data); @@ -104,25 +106,27 @@ function getBidRequestData(reqBidsConfigObj, callback, config, userConsent) { const adUnits = reqBidsConfigObj.adUnitCodes.map(code => adUnits0.find(unit => unit.code === code)); // buildUrl creates PHP style multi-parameters and includes undefined... (╯°□°)╯ ┻━┻ - const url = buildUrl(mergeDeep(parseUrl(`${API_ORIGIN}/q`), { search: { - 'v': 'pbjs-v' + '$prebid.version$', - 'c': config.params.clientid, - 'p': config.params.platformid, - 't': config.params.tagid, - 'imp': config.params.imps, - 'fc_ip': config.params.freqcap_ip, - 'fc_ipua': config.params.freqcap_ipua, - 'pn': (getRefererInfo().page || '').substr(0, 300).split(/[?#]/)[0], - 's': _map(adUnits, function(unit) { + const url = buildUrl(mergeDeep(parseUrl(`${API_ORIGIN}/q`), { + search: { + 'v': 'pbjs-v' + '$prebid.version$', + 'c': config.params.clientid, + 'p': config.params.platformid, + 't': config.params.tagid, + 'imp': config.params.imps, + 'fc_ip': config.params.freqcap_ip, + 'fc_ipua': config.params.freqcap_ipua, + 'pn': (getRefererInfo().page || '').substr(0, 300).split(/[?#]/)[0], + 's': _map(adUnits, function(unit) { // gptPreAuction runs *after* RTD so pbadslot may not be populated... (╯°□°)╯ ┻━┻ - const gpid = deepAccess(unit, 'ortb2Imp.ext.gpid') || + const gpid = deepAccess(unit, 'ortb2Imp.ext.gpid') || getGptSlotInfoForAdUnitCode(unit.code).gptSlot || unit.code; - const ref = [ gpid ]; - if (!config.params.slotinpath) ref.push(unit.code); - return ref.join('\t'); - }) - } })).replace(/\[\]|[^?&]+=undefined/g, '').replace(/([?&])&+/g, '$1'); + const ref = [gpid]; + if (!config.params.slotinpath) ref.push(unit.code); + return ref.join('\t'); + }) + } + })).replace(/\[\]|[^?&]+=undefined/g, '').replace(/([?&])&+/g, '$1'); ajax(url, function(responseText, q) { @@ -186,10 +190,9 @@ function getTargetingData(adUnitArray, config, userConsent, auction) { if (v) targeting[unit.code][`${ADSERVER_TARGETING_PREFIX}_${k}`] = v; }); - // ATF results shamelessly exfiltrated from intersectionRtdProvider - const bid = unit.bids.find(bid => !!bid.intersection); - if (bid) { - const v = val(config.params.thresholds.filter(t => t <= (bid.intersection.intersectionRatio * 100))); + const intersection = viewportIntersections.getIntersection(getAdUnitElement(unit)); + if (intersection) { + const v = val(config.params.thresholds.filter(t => t <= (intersection.intersectionRatio * 100))); if (v) targeting[unit.code][`${ADSERVER_TARGETING_PREFIX}_atf`] = v; } }); diff --git a/modules/admaruBidAdapter.js b/modules/admaruBidAdapter.js index f681a9a4191..364a13b55a1 100644 --- a/modules/admaruBidAdapter.js +++ b/modules/admaruBidAdapter.js @@ -1,5 +1,5 @@ -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER} from '../src/mediaTypes.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER } from '../src/mediaTypes.js'; const ADMARU_ENDPOINT = 'https://p1.admaru.net/AdCall'; const BIDDER_CODE = 'admaru'; diff --git a/modules/admaticBidAdapter.js b/modules/admaticBidAdapter.js index 4e75f7e583f..d83e7ed5ae9 100644 --- a/modules/admaticBidAdapter.js +++ b/modules/admaticBidAdapter.js @@ -28,6 +28,7 @@ export const spec = { { code: 'monetixads', gvlid: 1281 }, { code: 'netaddiction', gvlid: 1281 }, { code: 'adt', gvlid: 779 }, + { code: 'adrubi', gvlid: 779 }, { code: 'yobee', gvlid: 1281 } ], supportedMediaTypes: [BANNER, VIDEO, NATIVE], diff --git a/modules/admediaBidAdapter.js b/modules/admediaBidAdapter.js index e1cdbb86567..bf1aefe7521 100644 --- a/modules/admediaBidAdapter.js +++ b/modules/admediaBidAdapter.js @@ -1,5 +1,5 @@ -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER} from '../src/mediaTypes.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER } from '../src/mediaTypes.js'; /** * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest diff --git a/modules/admixerBidAdapter.js b/modules/admixerBidAdapter.js index b0fdf042fa5..59d97fc71cf 100644 --- a/modules/admixerBidAdapter.js +++ b/modules/admixerBidAdapter.js @@ -1,18 +1,18 @@ -import {isStr, logError, isFn, deepAccess} from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {config} from '../src/config.js'; -import {BANNER, VIDEO, NATIVE} from '../src/mediaTypes.js'; -import {convertOrtbRequestToProprietaryNative} from '../src/native.js'; +import { isStr, logError, isFn, deepAccess } from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { config } from '../src/config.js'; +import { BANNER, VIDEO, NATIVE } from '../src/mediaTypes.js'; +import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; const BIDDER_CODE = 'admixer'; const GVLID = 511; const ENDPOINT_URL = 'https://inv-nets.admixer.net/prebid.1.2.aspx'; const ALIASES = [ - {code: 'go2net', endpoint: 'https://ads.go2net.com.ua/prebid.1.2.aspx'}, + { code: 'go2net', endpoint: 'https://ads.go2net.com.ua/prebid.1.2.aspx' }, 'adblender', - {code: 'futureads', endpoint: 'https://ads.futureads.io/prebid.1.2.aspx'}, - {code: 'smn', endpoint: 'https://ads.smn.rs/prebid.1.2.aspx'}, - {code: 'admixeradx', endpoint: 'https://inv-nets.admixer.net/adxprebid.1.2.aspx'}, + { code: 'futureads', endpoint: 'https://ads.futureads.io/prebid.1.2.aspx' }, + { code: 'smn', endpoint: 'https://ads.smn.rs/prebid.1.2.aspx' }, + { code: 'admixeradx', endpoint: 'https://inv-nets.admixer.net/adxprebid.1.2.aspx' }, 'rtbstack', 'theads', ]; @@ -53,7 +53,8 @@ export const spec = { const payload = { imps: [], ortb2: bidderRequest.ortb2, - docReferrer: docRef}; + docReferrer: docRef + }; let endpointUrl; if (bidderRequest) { // checks if there is specified any endpointUrl in bidder config @@ -103,7 +104,7 @@ export const spec = { interpretResponse: function (serverResponse, bidRequest) { const bidResponses = []; try { - const {body: {ads = []} = {}} = serverResponse; + const { body: { ads = [] } = {} } = serverResponse; ads.forEach((ad) => bidResponses.push(ad)); } catch (e) { logError(e); @@ -112,13 +113,13 @@ export const spec = { }, getUserSyncs: function(syncOptions, serverResponses, gdprConsent) { const pixels = []; - serverResponses.forEach(({body: {cm = {}} = {}}) => { - const {pixels: img = [], iframes: frm = []} = cm; + serverResponses.forEach(({ body: { cm = {} } = {} }) => { + const { pixels: img = [], iframes: frm = [] } = cm; if (syncOptions.pixelEnabled) { - img.forEach((url) => pixels.push({type: 'image', url})); + img.forEach((url) => pixels.push({ type: 'image', url })); } if (syncOptions.iframeEnabled) { - frm.forEach((url) => pixels.push({type: 'iframe', url})); + frm.forEach((url) => pixels.push({ type: 'iframe', url })); } }); return pixels; diff --git a/modules/admixerIdSystem.js b/modules/admixerIdSystem.js index 04628d2356e..096d912426f 100644 --- a/modules/admixerIdSystem.js +++ b/modules/admixerIdSystem.js @@ -8,8 +8,8 @@ import { logError, logInfo } from '../src/utils.js' import { ajax } from '../src/ajax.js'; import { submodule } from '../src/hook.js'; -import {getStorageManager} from '../src/storageManager.js'; -import {MODULE_TYPE_UID} from '../src/activities/modules.js'; +import { getStorageManager } from '../src/storageManager.js'; +import { MODULE_TYPE_UID } from '../src/activities/modules.js'; /** * @typedef {import('../modules/userId/index.js').Submodule} Submodule @@ -19,7 +19,7 @@ import {MODULE_TYPE_UID} from '../src/activities/modules.js'; */ const NAME = 'admixerId'; -export const storage = getStorageManager({moduleType: MODULE_TYPE_UID, moduleName: NAME}); +export const storage = getStorageManager({ moduleType: MODULE_TYPE_UID, moduleName: NAME }); /** @type {Submodule} */ export const admixerIdSubmodule = { @@ -49,8 +49,8 @@ export const admixerIdSubmodule = { * @param {ConsentData} [consentData] * @returns {IdResponse|undefined} */ - getId(config, {gdpr: consentData} = {}) { - const {e, p, pid} = (config && config.params) || {}; + getId(config, { gdpr: consentData } = {}) { + const { e, p, pid } = (config && config.params) || {}; if (!pid || typeof pid !== 'string') { logError('admixerId submodule requires partner id to be defined'); return; @@ -90,7 +90,7 @@ export const admixerIdSubmodule = { function retrieveVisitorId(url, callback) { ajax(url, { success: response => { - const {setData: {visitorid} = {}} = JSON.parse(response || '{}'); + const { setData: { visitorid } = {} } = JSON.parse(response || '{}'); if (visitorid) { callback(visitorid); } else { diff --git a/modules/adnimationBidAdapter.js b/modules/adnimationBidAdapter.js new file mode 100644 index 00000000000..0c91d8e5f42 --- /dev/null +++ b/modules/adnimationBidAdapter.js @@ -0,0 +1,47 @@ +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; +import { getStorageManager } from '../src/storageManager.js'; +import { + isBidRequestValid, + onBidWon, + createUserSyncGetter, + createBuildRequestsFn, + createInterpretResponseFn +} from '../libraries/vidazooUtils/bidderUtils.js'; + +const DEFAULT_SUB_DOMAIN = 'exchange'; +const BIDDER_CODE = 'adnimation'; +const BIDDER_VERSION = '1.0.0'; +export const storage = getStorageManager({ bidderCode: BIDDER_CODE }); + +export function createDomain(subDomain = DEFAULT_SUB_DOMAIN) { + return `https://${subDomain}.adnimation.com`; +} + +function createUniqueRequestData(hashUrl, bid) { + const { auctionId, transactionId } = bid; + return { + auctionId, + transactionId + }; +} + +const buildRequests = createBuildRequestsFn(createDomain, createUniqueRequestData, storage, BIDDER_CODE, BIDDER_VERSION, false); +const interpretResponse = createInterpretResponseFn(BIDDER_CODE, false); +const getUserSyncs = createUserSyncGetter({ + iframeSyncUrl: 'https://sync.adnimation.com/api/sync/iframe', + imageSyncUrl: 'https://sync.adnimation.com/api/sync/image' +}); + +export const spec = { + code: BIDDER_CODE, + version: BIDDER_VERSION, + supportedMediaTypes: [BANNER, VIDEO], + isBidRequestValid, + buildRequests, + interpretResponse, + getUserSyncs, + onBidWon, +}; + +registerBidder(spec); diff --git a/modules/adnimationBidAdapter.md b/modules/adnimationBidAdapter.md new file mode 100644 index 00000000000..df62967bd8d --- /dev/null +++ b/modules/adnimationBidAdapter.md @@ -0,0 +1,36 @@ +# Overview + +**Module Name:** Adnimation Bidder Adapter + +**Module Type:** Bidder Adapter + +**Maintainer:** prebid@adnimation.com + +# Description + +Module that connects to Adnimation's demand sources. + +# Test Parameters + +```js +var adUnits = [ + { + code: 'test-ad', + sizes: [[300, 250]], + bids: [ + { + bidder: 'adnimation', + params: { + cId: '562524b21b1c1f08117667f9', + pId: '59ac17c192832d0016683fe3', + bidFloor: 0.0001, + ext: { + param1: 'loremipsum', + param2: 'dolorsitamet' + } + } + } + ] + } +]; +``` diff --git a/modules/adnowBidAdapter.js b/modules/adnowBidAdapter.js index 23b65a783e2..d04ec1173f7 100644 --- a/modules/adnowBidAdapter.js +++ b/modules/adnowBidAdapter.js @@ -1,6 +1,6 @@ -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER, NATIVE} from '../src/mediaTypes.js'; -import {deepAccess, parseQueryStringParameters, parseSizesInput} from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, NATIVE } from '../src/mediaTypes.js'; +import { deepAccess, parseQueryStringParameters, parseSizesInput } from '../src/utils.js'; import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; @@ -30,7 +30,7 @@ const ENDPOINT = 'https://n.nnowa.com/a'; export const spec = { code: BIDDER_CODE, gvlid: GVLID, - supportedMediaTypes: [ NATIVE, BANNER ], + supportedMediaTypes: [NATIVE, BANNER], /** * @param {object} bid @@ -122,11 +122,11 @@ export const spec = { bid.requestId = bidObj.bidId; if (mediaType === BANNER) { - return [ this._getBannerBid(bid) ]; + return [this._getBannerBid(bid)]; } if (mediaType === NATIVE) { - return [ this._getNativeBid(bid) ]; + return [this._getNativeBid(bid)]; } return []; diff --git a/modules/adnuntiusAnalyticsAdapter.js b/modules/adnuntiusAnalyticsAdapter.js index 6de06332e3e..a2fcc9018fd 100644 --- a/modules/adnuntiusAnalyticsAdapter.js +++ b/modules/adnuntiusAnalyticsAdapter.js @@ -1,8 +1,9 @@ import { timestamp, logInfo } from '../src/utils.js'; -import {ajax} from '../src/ajax.js'; +import { ajax } from '../src/ajax.js'; import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import { EVENTS } from '../src/constants.js'; import adapterManager from '../src/adapterManager.js'; +import { getAdUnitElement } from '../src/utils/adUnits.js'; const URL = 'https://analytics.adnuntius.com/prebid'; const REQUEST_SENT = 1; @@ -18,15 +19,15 @@ const cache = { auctions: {} }; -const adnAnalyticsAdapter = Object.assign(adapter({url: '', analyticsType: 'endpoint'}), { - track({eventType, args}) { +const adnAnalyticsAdapter = Object.assign(adapter({ url: '', analyticsType: 'endpoint' }), { + track({ eventType, args }) { const time = timestamp(); logInfo('ADN_EVENT:', [eventType, args]); switch (eventType) { case EVENTS.AUCTION_INIT: logInfo('ADN_AUCTION_INIT:', args); - cache.auctions[args.auctionId] = {bids: {}, bidAdUnits: {}}; + cache.auctions[args.auctionId] = { bids: {}, bidAdUnits: {} }; break; case EVENTS.BID_REQUESTED: logInfo('ADN_BID_REQUESTED:', args); @@ -36,7 +37,7 @@ const adnAnalyticsAdapter = Object.assign(adapter({url: '', analyticsType: 'endp cache.auctions[args.auctionId].gdprApplies = args.gdprConsent ? args.gdprConsent.gdprApplies : undefined; cache.auctions[args.auctionId].gdprConsent = args.gdprConsent ? args.gdprConsent.consentString : undefined; - const container = document.getElementById(bidReq.adUnitCode); + const container = getAdUnitElement(bidReq); const containerAttr = container ? container.getAttribute('data-adunitid') : undefined; const adUnitId = containerAttr || undefined; @@ -169,7 +170,7 @@ adnAnalyticsAdapter.sendEvents = function() { return; } - ajax(initOptions.endPoint || URL, undefined, JSON.stringify(events), {method: 'POST'}); + ajax(initOptions.endPoint || URL, undefined, JSON.stringify(events), { method: 'POST' }); }; function getSentRequests() { @@ -202,7 +203,7 @@ function getSentRequests() { }); }); - return {gdpr: gdpr, auctionIds: auctionIds, sentRequests: sentRequests}; + return { gdpr: gdpr, auctionIds: auctionIds, sentRequests: sentRequests }; } function getResponses(gdpr, auctionIds) { @@ -278,7 +279,7 @@ function getGdprPos(gdpr, auction) { } if (gdprPos === gdpr.length) { - gdpr[gdprPos] = {gdprApplies: auction.gdprApplies, gdprConsent: auction.gdprConsent}; + gdpr[gdprPos] = { gdprApplies: auction.gdprApplies, gdprConsent: auction.gdprConsent }; } return gdprPos; diff --git a/modules/adnuntiusBidAdapter.js b/modules/adnuntiusBidAdapter.js index 4a4556cfb1e..5c758c09c2e 100644 --- a/modules/adnuntiusBidAdapter.js +++ b/modules/adnuntiusBidAdapter.js @@ -1,5 +1,5 @@ -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; import { convertObjectToArray, deepAccess, @@ -7,13 +7,14 @@ import { getUnixTimestampFromNow, getWinDimensions, isArray, + isPlainObject, isEmpty, isStr } from '../src/utils.js'; -import {config} from '../src/config.js'; -import {getStorageManager} from '../src/storageManager.js'; -import {toLegacyResponse, toOrtbNativeRequest} from '../src/native.js'; -import {getGlobal} from '../src/prebidGlobal.js'; +import { config } from '../src/config.js'; +import { getStorageManager } from '../src/storageManager.js'; +import { toLegacyResponse, toOrtbNativeRequest } from '../src/native.js'; +import { getGlobal } from '../src/prebidGlobal.js'; const BIDDER_CODE = 'adnuntius'; const BIDDER_CODE_DEAL_ALIAS_BASE = 'adndeal'; @@ -254,10 +255,25 @@ const targetingTool = (function() { existingUrlRelatedData.segments = segments; }, - mergeKvsFromOrtb: function(bidTargeting, bidderRequest) { - const siteKvs = getKvsFromOrtb(bidderRequest || {}, 'site.ext.data'); - const userKvs = getKvsFromOrtb(bidderRequest || {}, 'user.ext.data'); - if (isEmpty(siteKvs) && isEmpty(userKvs)) { + mergeKvsFromOrtb: function(bidTargeting, bidderRequest, bid) { + function sanitizeKeyValues(kvs) { + return Object.keys(kvs || {}).reduce((acc, key) => { + const value = kvs[key]; + if (isArray(value)) { + acc[key] = value.map(v => { + return isPlainObject(v) ? JSON.stringify(v) : v; + }); + return acc; + } + acc[key] = value; + return acc; + }, {}); + } + + const siteKvs = sanitizeKeyValues(getKvsFromOrtb(bidderRequest || {}, 'site.ext.data')); + const userKvs = sanitizeKeyValues(getKvsFromOrtb(bidderRequest || {}, 'user.ext.data')); + const impKvs = sanitizeKeyValues(deepAccess(bid, 'ortb2Imp.ext.data')); + if (isEmpty(siteKvs) && isEmpty(userKvs) && isEmpty(impKvs)) { return; } if (bidTargeting.kv && !Array.isArray(bidTargeting.kv)) { @@ -270,6 +286,9 @@ const targetingTool = (function() { if (!isEmpty(userKvs)) { bidTargeting.kv = bidTargeting.kv.concat(convertObjectToArray(userKvs)); } + if (!isEmpty(impKvs)) { + bidTargeting.kv = bidTargeting.kv.concat(convertObjectToArray(impKvs)); + } } } })(); @@ -358,8 +377,8 @@ export const spec = { networks[network].metaData = payloadRelatedData; } - const bidTargeting = {...bid.params.targeting || {}}; - targetingTool.mergeKvsFromOrtb(bidTargeting, bidderRequest); + const bidTargeting = { ...bid.params.targeting || {} }; + targetingTool.mergeKvsFromOrtb(bidTargeting, bidderRequest, bid); const mediaTypes = bid.mediaTypes || {}; const validMediaTypes = SUPPORTED_MEDIA_TYPES.filter(mt => { return mediaTypes[mt]; @@ -375,7 +394,7 @@ export const spec = { return; } const targetId = (bid.params.targetId || bid.bidId) + (isSingleFormat || mediaType === BANNER ? '' : ('-' + mediaType)); - const adUnit = {...bidTargeting, auId: bid.params.auId, targetId: targetId}; + const adUnit = { ...bidTargeting, auId: bid.params.auId, targetId: targetId }; if (mediaType === VIDEO) { adUnit.adType = 'VAST'; } else if (mediaType === NATIVE) { @@ -395,9 +414,9 @@ export const spec = { 'methods': [1] } ]; - adUnit.nativeRequest = {ortb: nativeOrtb} + adUnit.nativeRequest = { ortb: nativeOrtb } } else { - adUnit.nativeRequest = {ortb: mediaTypeData.ortb}; + adUnit.nativeRequest = { ortb: mediaTypeData.ortb }; } } const dealId = deepAccess(bid, 'params.dealId') || deepAccess(bid, 'params.inventory.pmp.deals'); diff --git a/modules/adoceanBidAdapter.js b/modules/adoceanBidAdapter.js index 6f6548a1ba8..e6b89272bc7 100644 --- a/modules/adoceanBidAdapter.js +++ b/modules/adoceanBidAdapter.js @@ -3,6 +3,7 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; const BIDDER_CODE = 'adocean'; +const GVLID = 328; const URL_SAFE_FIELDS = { slaves: true }; @@ -53,17 +54,6 @@ function buildRequest(bid, gdprConsent) { } payload.spots = 1; } - if (bid.mediaTypes.video.context === 'adpod') { - const durationRangeSec = bid.mediaTypes.video.durationRangeSec; - if (!bid.mediaTypes.video.adPodDurationSec || !isArray(durationRangeSec) || durationRangeSec.length === 0) { - return; - } - const spots = calculateAdPodSpotsNumber(bid.mediaTypes.video.adPodDurationSec, bid.mediaTypes.video.durationRangeSec); - const maxDuration = Math.max(...durationRangeSec); - payload.dur = bid.mediaTypes.video.adPodDurationSec; - payload.maxdur = maxDuration; - payload.spots = spots; - } } else if (bid.mediaTypes.banner) { payload.aosize = parseSizesInput(bid.mediaTypes.banner.sizes).join(','); } @@ -76,12 +66,6 @@ function buildRequest(bid, gdprConsent) { }; } -function calculateAdPodSpotsNumber(adPodDurationSec, durationRangeSec) { - const minAllowedDuration = Math.min(...durationRangeSec); - const numberOfSpots = Math.floor(adPodDurationSec / minAllowedDuration); - return numberOfSpots; -} - function interpretResponse(placementResponse, bidRequest, bids) { const requestId = bidRequest.bidIdMap[placementResponse.id]; if (!placementResponse.error && requestId) { @@ -117,6 +101,7 @@ function interpretResponse(placementResponse, bidRequest, bids) { export const spec = { code: BIDDER_CODE, + gvlid: GVLID, supportedMediaTypes: [BANNER, VIDEO], isBidRequestValid: function(bid) { @@ -132,9 +117,6 @@ export const spec = { if (bid.mediaTypes.video.context === 'instream') { return true; } - if (bid.mediaTypes.video.context === 'adpod') { - return !bid.mediaTypes.video.requireExactDuration; - } } return false; }, diff --git a/modules/adotBidAdapter.js b/modules/adotBidAdapter.js index 121c9960ade..6105463d0f3 100644 --- a/modules/adotBidAdapter.js +++ b/modules/adotBidAdapter.js @@ -7,6 +7,7 @@ import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; import { isArray, isBoolean, isFn, isPlainObject, isStr, logError, replaceAuctionPrice } from '../src/utils.js'; import { OUTSTREAM } from '../src/video.js'; import { NATIVE_ASSETS_IDS as NATIVE_ID_MAPPING, NATIVE_ASSETS as NATIVE_PLACEMENTS } from '../libraries/braveUtils/nativeAssets.js'; +import { getAdUnitElement } from '../src/utils/adUnits.js'; /** * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest @@ -495,7 +496,7 @@ function buildRenderer(bid, mediaType) { ad.renderer.push(() => { const domContainer = container ? document.querySelector(container) - : document.getElementById(adUnitCode); + : getAdUnitElement(ad) const player = new window.VASTPlayer(domContainer); diff --git a/modules/adplayerproVideoProvider.js b/modules/adplayerproVideoProvider.js index 56200ed95fa..af4729b13ce 100644 --- a/modules/adplayerproVideoProvider.js +++ b/modules/adplayerproVideoProvider.js @@ -26,9 +26,9 @@ import { SETUP_FAILED, VOLUME } from '../libraries/video/constants/events.js'; -import {AD_PLAYER_PRO_VENDOR} from '../libraries/video/constants/vendorCodes.js'; -import {getEventHandler} from '../libraries/video/shared/eventHandler.js'; -import {submodule} from '../src/hook.js'; +import { AD_PLAYER_PRO_VENDOR } from '../libraries/video/constants/vendorCodes.js'; +import { getEventHandler } from '../libraries/video/shared/eventHandler.js'; +import { submodule } from '../src/hook.js'; const setupFailMessage = 'Failed to instantiate the player'; @@ -350,14 +350,14 @@ export const utils = { } }, - getPlaybackMethod: function ({autoplay, mute}) { + getPlaybackMethod: function ({ autoplay, mute }) { if (autoplay) { return mute ? PLAYBACK_METHODS.AUTOPLAY_MUTED : PLAYBACK_METHODS.AUTOPLAY; } return PLAYBACK_METHODS.CLICK_TO_PLAY; }, - getPlcmt: function ({type, autoplay, muted, file}) { + getPlcmt: function ({ type, autoplay, muted, file }) { type = type || 'inStream'; if (!file) { // INTERSTITIAL: primary focus of the page and take up the majority of the viewport and cannot be scrolled out of view. diff --git a/modules/adpod.js b/modules/adpod.js deleted file mode 100644 index 3d82c91e42e..00000000000 --- a/modules/adpod.js +++ /dev/null @@ -1,657 +0,0 @@ -/** - * This module houses the functionality to evaluate and process adpod adunits/bids. Specifically there are several hooked functions, - * that either supplement the base function (ie to check something additional or unique to adpod objects) or to replace the base function - * entirely when appropriate. - * - * Brief outline of each hook: - * - `callPrebidCacheHook` - for any adpod bids, this function will temporarily hold them in a queue in order to send the bids to Prebid Cache in bulk - * - `checkAdUnitSetupHook` - evaluates the adUnits to ensure that required fields for adpod adUnits are present. Invalid adpod adUntis are removed from the array. - * - `checkVideoBidSetupHook` - evaluates the adpod bid returned from an adaptor/bidder to ensure required fields are populated; also initializes duration bucket field. - * - * To initialize the module, there is an `initAdpodHooks()` function that should be imported and executed by a corresponding `...AdServerVideo` - * module that designed to support adpod video type ads. This import process allows this module to effectively act as a sub-module. - */ - -import { - deepAccess, - generateUUID, - groupBy, - isArray, - isArrayOfNums, - isNumber, - isPlainObject, - logError, - logInfo, - logWarn -} from '../src/utils.js'; -import { - addBidToAuction, - AUCTION_IN_PROGRESS, - getPriceByGranularity, - getPriceGranularity -} from '../src/auction.js'; -import {checkAdUnitSetup} from '../src/prebid.js'; -import {checkVideoBidSetup} from '../src/video.js'; -import {getHook, module, setupBeforeHookFnOnce} from '../src/hook.js'; -import {store} from '../src/videoCache.js'; -import {config} from '../src/config.js'; -import {ADPOD} from '../src/mediaTypes.js'; -import {auctionManager} from '../src/auctionManager.js'; -import { TARGETING_KEYS } from '../src/constants.js'; - -const TARGETING_KEY_PB_CAT_DUR = 'hb_pb_cat_dur'; -const TARGETING_KEY_CACHE_ID = 'hb_cache_id'; - -let queueTimeDelay = 50; -let queueSizeLimit = 5; -const bidCacheRegistry = createBidCacheRegistry(); - -/** - * Create a registry object that stores/manages bids while be held in queue for Prebid Cache. - * @returns registry object with defined accessor functions - */ -function createBidCacheRegistry() { - const registry = {}; - - function setupRegistrySlot(auctionId) { - registry[auctionId] = {}; - registry[auctionId].bidStorage = new Set(); - registry[auctionId].queueDispatcher = createDispatcher(queueTimeDelay); - registry[auctionId].initialCacheKey = generateUUID(); - } - - return { - addBid: function (bid) { - // create parent level object based on auction ID (in case there are concurrent auctions running) to store objects for that auction - if (!registry[bid.auctionId]) { - setupRegistrySlot(bid.auctionId); - } - registry[bid.auctionId].bidStorage.add(bid); - }, - removeBid: function (bid) { - registry[bid.auctionId].bidStorage.delete(bid); - }, - getBids: function (bid) { - return registry[bid.auctionId] && registry[bid.auctionId].bidStorage.values(); - }, - getQueueDispatcher: function (bid) { - return registry[bid.auctionId] && registry[bid.auctionId].queueDispatcher; - }, - setupInitialCacheKey: function (bid) { - if (!registry[bid.auctionId]) { - registry[bid.auctionId] = {}; - registry[bid.auctionId].initialCacheKey = generateUUID(); - } - }, - getInitialCacheKey: function (bid) { - return registry[bid.auctionId] && registry[bid.auctionId].initialCacheKey; - } - } -} - -/** - * Creates a function that when called updates the bid queue and extends the running timer (when called subsequently). - * Once the time threshold for the queue (defined by queueSizeLimit) is reached, the queue will be flushed by calling the `firePrebidCacheCall` function. - * If there is a long enough time between calls (based on timeoutDration), the queue will automatically flush itself. - * @param {Number} timeoutDuration number of milliseconds to pass before timer expires and current bid queue is flushed - * @returns {Function} - */ -function createDispatcher(timeoutDuration) { - let timeout; - let counter = 1; - - return function (auctionInstance, bidListArr, afterBidAdded, killQueue) { - const context = this; - - var callbackFn = function () { - firePrebidCacheCall.call(context, auctionInstance, bidListArr, afterBidAdded); - }; - - clearTimeout(timeout); - - if (!killQueue) { - // want to fire off the queue if either: size limit is reached or time has passed since last call to dispatcher - if (counter === queueSizeLimit) { - counter = 1; - callbackFn(); - } else { - counter++; - timeout = setTimeout(callbackFn, timeoutDuration); - } - } else { - counter = 1; - } - }; -} - -function getPricePartForAdpodKey(bid) { - let pricePart - const prioritizeDeals = config.getConfig('adpod.prioritizeDeals'); - if (prioritizeDeals && deepAccess(bid, 'video.dealTier')) { - const adpodDealPrefix = config.getConfig(`adpod.dealTier.${bid.bidderCode}.prefix`); - pricePart = (adpodDealPrefix) ? adpodDealPrefix + deepAccess(bid, 'video.dealTier') : deepAccess(bid, 'video.dealTier'); - } else { - const granularity = getPriceGranularity(bid); - pricePart = getPriceByGranularity(granularity)(bid); - } - return pricePart -} - -/** - * This function reads certain fields from the bid to generate a specific key used for caching the bid in Prebid Cache - * @param {Object} bid bid object to update - * @param {Boolean} brandCategoryExclusion value read from setConfig; influences whether category is required or not - */ -function attachPriceIndustryDurationKeyToBid(bid, brandCategoryExclusion) { - const initialCacheKey = bidCacheRegistry.getInitialCacheKey(bid); - const duration = deepAccess(bid, 'video.durationBucket'); - const pricePart = getPricePartForAdpodKey(bid); - let pcd; - - if (brandCategoryExclusion) { - const category = deepAccess(bid, 'meta.adServerCatId'); - pcd = `${pricePart}_${category}_${duration}s`; - } else { - pcd = `${pricePart}_${duration}s`; - } - - if (!bid.adserverTargeting) { - bid.adserverTargeting = {}; - } - bid.adserverTargeting[TARGETING_KEY_PB_CAT_DUR] = pcd; - bid.adserverTargeting[TARGETING_KEY_CACHE_ID] = initialCacheKey; - bid.videoCacheKey = initialCacheKey; - bid.customCacheKey = `${pcd}_${initialCacheKey}`; -} - -/** - * Updates the running queue for the associated auction. - * Does a check to ensure the auction is still running; if it's not - the previously running queue is killed. - * @param {*} auctionInstance running context of the auction - * @param {Object} bidResponse bid object being added to queue - * @param {Function} afterBidAdded callback function used when Prebid Cache responds - */ -function updateBidQueue(auctionInstance, bidResponse, afterBidAdded) { - const bidListIter = bidCacheRegistry.getBids(bidResponse); - - if (bidListIter) { - const bidListArr = Array.from(bidListIter); - const callDispatcher = bidCacheRegistry.getQueueDispatcher(bidResponse); - const killQueue = !!(auctionInstance.getAuctionStatus() !== AUCTION_IN_PROGRESS); - callDispatcher(auctionInstance, bidListArr, afterBidAdded, killQueue); - } else { - logWarn('Attempted to cache a bid from an unknown auction. Bid:', bidResponse); - } -} - -/** - * Small helper function to remove bids from internal storage; normally b/c they're about to sent to Prebid Cache for processing. - * @param {Array[Object]} bidResponses list of bids to remove - */ -function removeBidsFromStorage(bidResponses) { - for (let i = 0; i < bidResponses.length; i++) { - bidCacheRegistry.removeBid(bidResponses[i]); - } -} - -/** - * This function will send a list of bids to Prebid Cache. It also removes the same bids from the internal bidCacheRegistry - * to maintain which bids are in queue. - * If the bids are successfully cached, they will be added to the respective auction. - * @param {*} auctionInstance running context of the auction - * @param {Array[Object]} bidList list of bid objects that need to be sent to Prebid Cache - * @param {Function} afterBidAdded callback function used when Prebid Cache responds - */ -function firePrebidCacheCall(auctionInstance, bidList, afterBidAdded) { - // remove entries now so other incoming bids won't accidentally have a stale version of the list while PBC is processing the current submitted list - removeBidsFromStorage(bidList); - - store(bidList, function (error, cacheIds) { - if (error) { - logWarn(`Failed to save to the video cache: ${error}. Video bid(s) must be discarded.`); - } else { - for (let i = 0; i < cacheIds.length; i++) { - // when uuid in response is empty string then the key already existed, so this bid wasn't cached - if (cacheIds[i].uuid !== '') { - addBidToAuction(auctionInstance, bidList[i]); - } else { - logInfo(`Detected a bid was not cached because the custom key was already registered. Attempted to use key: ${bidList[i].customCacheKey}. Bid was: `, bidList[i]); - } - afterBidAdded(); - } - } - }); -} - -/** - * This is the main hook function to handle adpod bids; maintains the logic to temporarily hold bids in a queue in order to send bulk requests to Prebid Cache. - * @param {Function} fn reference to original function (used by hook logic) - * @param {*} auctionInstance running context of the auction - * @param {Object} bidResponse incoming bid; if adpod, will be processed through hook function. If not adpod, returns to original function. - * @param {Function} afterBidAdded callback function used when Prebid Cache responds - * @param {Object} videoConfig mediaTypes.video from the bid's adUnit - */ -export function callPrebidCacheHook(fn, auctionInstance, bidResponse, afterBidAdded, videoConfig) { - if (videoConfig && videoConfig.context === ADPOD) { - const brandCategoryExclusion = config.getConfig('adpod.brandCategoryExclusion'); - const adServerCatId = deepAccess(bidResponse, 'meta.adServerCatId'); - if (!adServerCatId && brandCategoryExclusion) { - logWarn('Detected a bid without meta.adServerCatId while setConfig({adpod.brandCategoryExclusion}) was enabled. This bid has been rejected:', bidResponse); - afterBidAdded(); - } else { - if (config.getConfig('adpod.deferCaching') === false) { - bidCacheRegistry.addBid(bidResponse); - attachPriceIndustryDurationKeyToBid(bidResponse, brandCategoryExclusion); - - updateBidQueue(auctionInstance, bidResponse, afterBidAdded); - } else { - // generate targeting keys for bid - bidCacheRegistry.setupInitialCacheKey(bidResponse); - attachPriceIndustryDurationKeyToBid(bidResponse, brandCategoryExclusion); - - // add bid to auction - addBidToAuction(auctionInstance, bidResponse); - afterBidAdded(); - } - } - } else { - fn.call(this, auctionInstance, bidResponse, afterBidAdded, videoConfig); - } -} - -/** - * This hook function will review the adUnit setup and verify certain required values are present in any adpod adUnits. - * If the fields are missing or incorrectly setup, the adUnit is removed from the list. - * @param {Function} fn reference to original function (used by hook logic) - * @param {Array[Object]} adUnits list of adUnits to be evaluated - * @returns {Array[Object]} list of adUnits that passed the check - */ -export function checkAdUnitSetupHook(fn, adUnits) { - const goodAdUnits = adUnits.filter(adUnit => { - const mediaTypes = deepAccess(adUnit, 'mediaTypes'); - const videoConfig = deepAccess(mediaTypes, 'video'); - if (videoConfig && videoConfig.context === ADPOD) { - // run check to see if other mediaTypes are defined (ie multi-format); reject adUnit if so - if (Object.keys(mediaTypes).length > 1) { - logWarn(`Detected more than one mediaType in adUnitCode: ${adUnit.code} while attempting to define an 'adpod' video adUnit. 'adpod' adUnits cannot be mixed with other mediaTypes. This adUnit will be removed from the auction.`); - return false; - } - - let errMsg = `Detected missing or incorrectly setup fields for an adpod adUnit. Please review the following fields of adUnitCode: ${adUnit.code}. This adUnit will be removed from the auction.`; - - const playerSize = !!( - ( - videoConfig.playerSize && ( - isArrayOfNums(videoConfig.playerSize, 2) || ( - isArray(videoConfig.playerSize) && videoConfig.playerSize.every(sz => isArrayOfNums(sz, 2)) - ) - ) - ) || (videoConfig.sizeConfig) - ); - const adPodDurationSec = !!(videoConfig.adPodDurationSec && isNumber(videoConfig.adPodDurationSec) && videoConfig.adPodDurationSec > 0); - const durationRangeSec = !!(videoConfig.durationRangeSec && isArrayOfNums(videoConfig.durationRangeSec) && videoConfig.durationRangeSec.every(range => range > 0)); - - if (!playerSize || !adPodDurationSec || !durationRangeSec) { - errMsg += (!playerSize) ? '\nmediaTypes.video.playerSize' : ''; - errMsg += (!adPodDurationSec) ? '\nmediaTypes.video.adPodDurationSec' : ''; - errMsg += (!durationRangeSec) ? '\nmediaTypes.video.durationRangeSec' : ''; - logWarn(errMsg); - return false; - } - } - return true; - }); - adUnits = goodAdUnits; - fn.call(this, adUnits); -} - -/** - * This check evaluates the incoming bid's `video.durationSeconds` field and tests it against specific logic depending on adUnit config. Summary of logic below: - * when adUnit.mediaTypes.video.requireExactDuration is true - * - only bids that exactly match those listed values are accepted (don't round at all). - * - populate the `bid.video.durationBucket` field with the matching duration value - * when adUnit.mediaTypes.video.requireExactDuration is false - * - round the duration to the next highest specified duration value based on adunit. If the duration is above a range within a set buffer, that bid falls down into that bucket. - * (eg if range was [5, 15, 30] -> 2s is rounded to 5s; 17s is rounded back to 15s; 18s is rounded up to 30s) - * - if the bid is above the range of the listed durations (and outside the buffer), reject the bid - * - set the rounded duration value in the `bid.video.durationBucket` field for accepted bids - * @param {Object} videoMediaType 'mediaTypes.video' associated to bidResponse - * @param {Object} bidResponse incoming bidResponse being evaluated by bidderFactory - * @returns {boolean} return false if bid duration is deemed invalid as per adUnit configuration; return true if fine - */ -function checkBidDuration(videoMediaType, bidResponse) { - const buffer = 2; - const bidDuration = deepAccess(bidResponse, 'video.durationSeconds'); - const adUnitRanges = videoMediaType.durationRangeSec; - adUnitRanges.sort((a, b) => a - b); // ensure the ranges are sorted in numeric order - - if (!videoMediaType.requireExactDuration) { - const max = Math.max(...adUnitRanges); - if (bidDuration <= (max + buffer)) { - const nextHighestRange = ((adUnitRanges) || []).find(range => (range + buffer) >= bidDuration); - bidResponse.video.durationBucket = nextHighestRange; - } else { - logWarn(`Detected a bid with a duration value outside the accepted ranges specified in adUnit.mediaTypes.video.durationRangeSec. Rejecting bid: `, bidResponse); - return false; - } - } else { - if (((adUnitRanges) || []).find(range => range === bidDuration)) { - bidResponse.video.durationBucket = bidDuration; - } else { - logWarn(`Detected a bid with a duration value not part of the list of accepted ranges specified in adUnit.mediaTypes.video.durationRangeSec. Exact match durations must be used for this adUnit. Rejecting bid: `, bidResponse); - return false; - } - } - return true; -} - -/** - * This hooked function evaluates an adpod bid and determines if the required fields are present. - * If it's found to not be an adpod bid, it will return to original function via hook logic - * @param {Function} fn reference to original function (used by hook logic) - * @param {Object} bid incoming bid object - * @param {Object} adUnit adUnit object of associated bid - * @param {Object} videoMediaType copy of the `bidRequest.mediaTypes.video` object; used in original function - * @param {String} context value of the `bidRequest.mediaTypes.video.context` field; used in original function - * @returns {boolean} this return is only used for adpod bids - */ -export function checkVideoBidSetupHook(fn, bid, adUnit, videoMediaType, context) { - if (context === ADPOD) { - let result = true; - const brandCategoryExclusion = config.getConfig('adpod.brandCategoryExclusion'); - if (brandCategoryExclusion && !deepAccess(bid, 'meta.primaryCatId')) { - result = false; - } - - if (deepAccess(bid, 'video')) { - if (!deepAccess(bid, 'video.context') || bid.video.context !== ADPOD) { - result = false; - } - - if (!deepAccess(bid, 'video.durationSeconds') || bid.video.durationSeconds <= 0) { - result = false; - } else { - const isBidGood = checkBidDuration(videoMediaType, bid); - if (!isBidGood) result = false; - } - } - - 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: "..."} }); - `); - result = false; - }; - - fn.bail(result); - } else { - fn.call(this, bid, adUnit, videoMediaType, context); - } -} - -/** - * This function reads the (optional) settings for the adpod as set from the setConfig() - * @param {Object} config contains the config settings for adpod module - */ -export function adpodSetConfig(config) { - if (config.bidQueueTimeDelay !== undefined) { - if (typeof config.bidQueueTimeDelay === 'number' && config.bidQueueTimeDelay > 0) { - queueTimeDelay = config.bidQueueTimeDelay; - } else { - logWarn(`Detected invalid value for adpod.bidQueueTimeDelay in setConfig; must be a positive number. Using default: ${queueTimeDelay}`) - } - } - - if (config.bidQueueSizeLimit !== undefined) { - if (typeof config.bidQueueSizeLimit === 'number' && config.bidQueueSizeLimit > 0) { - queueSizeLimit = config.bidQueueSizeLimit; - } else { - logWarn(`Detected invalid value for adpod.bidQueueSizeLimit in setConfig; must be a positive number. Using default: ${queueSizeLimit}`) - } - } -} -config.getConfig('adpod', config => adpodSetConfig(config.adpod)); - -/** - * This function initializes the adpod module's hooks. This is called by the corresponding adserver video module. - * PBJS 10: Adding a deprecation warning - */ -function initAdpodHooks() { - logWarn('DEPRECATION NOTICE: Prebid.js is not aware of any transactions requiring the ADPOD video mediatype context. Please open a github issue if you are relying on it as support for it may be removed in a future version.'); - - setupBeforeHookFnOnce(getHook('callPrebidCache'), callPrebidCacheHook); - setupBeforeHookFnOnce(checkAdUnitSetup, checkAdUnitSetupHook); - setupBeforeHookFnOnce(checkVideoBidSetup, checkVideoBidSetupHook); -} - -initAdpodHooks() - -/** - * - * @param {Array[Object]} bids list of 'winning' bids that need to be cached - * @param {Function} callback send the cached bids (or error) back to adserverVideoModule for further processing - }} - */ -export function callPrebidCacheAfterAuction(bids, callback) { - // will call PBC here and execute cb param to initialize player code - store(bids, function (error, cacheIds) { - if (error) { - callback(error, null); - } else { - const successfulCachedBids = []; - for (let i = 0; i < cacheIds.length; i++) { - if (cacheIds[i] !== '') { - successfulCachedBids.push(bids[i]); - } - } - callback(null, successfulCachedBids); - } - }) -} - -/** - * Compare function to be used in sorting long-form bids. This will compare bids on price per second. - */ -export function sortByPricePerSecond(a, b) { - if (a.adserverTargeting[TARGETING_KEYS.PRICE_BUCKET] / a.video.durationBucket < b.adserverTargeting[TARGETING_KEYS.PRICE_BUCKET] / b.video.durationBucket) { - return 1; - } - if (a.adserverTargeting[TARGETING_KEYS.PRICE_BUCKET] / a.video.durationBucket > b.adserverTargeting[TARGETING_KEYS.PRICE_BUCKET] / b.video.durationBucket) { - return -1; - } - return 0; -} - -/** - * This function returns targeting keyvalue pairs for long-form adserver modules. Freewheel and GAM are currently supporting Prebid long-form - * @param {Object} options - Options for targeting. - * @param {Array} options.codes - Array of ad unit codes. - * @param {function} options.callback - Callback function to handle the targeting key-value pairs. - * @returns {Object} Targeting key-value pairs for ad unit codes. - */ -export function getTargeting({ codes, callback } = {}) { - if (!callback) { - logError('No callback function was defined in the getTargeting call. Aborting getTargeting().'); - return; - } - codes = codes || []; - const adPodAdUnits = getAdPodAdUnits(codes); - const bidsReceived = auctionManager.getBidsReceived(); - const competiveExclusionEnabled = config.getConfig('adpod.brandCategoryExclusion'); - const deferCachingSetting = config.getConfig('adpod.deferCaching'); - const deferCachingEnabled = (typeof deferCachingSetting === 'boolean') ? deferCachingSetting : true; - - let bids = getBidsForAdpod(bidsReceived, adPodAdUnits); - bids = (competiveExclusionEnabled || deferCachingEnabled) ? getExclusiveBids(bids) : bids; - - const prioritizeDeals = config.getConfig('adpod.prioritizeDeals'); - if (prioritizeDeals) { - const [otherBids, highPriorityDealBids] = bids.reduce((partitions, bid) => { - const bidDealTier = deepAccess(bid, 'video.dealTier'); - const minDealTier = config.getConfig(`adpod.dealTier.${bid.bidderCode}.minDealTier`); - if (minDealTier && bidDealTier) { - if (bidDealTier >= minDealTier) { - partitions[1].push(bid) - } else { - partitions[0].push(bid) - } - } else if (bidDealTier) { - partitions[1].push(bid) - } else { - partitions[0].push(bid); - } - return partitions; - }, [[], []]); - highPriorityDealBids.sort(sortByPricePerSecond); - otherBids.sort(sortByPricePerSecond); - bids = highPriorityDealBids.concat(otherBids); - } else { - bids.sort(sortByPricePerSecond); - } - - const targeting = {}; - if (deferCachingEnabled === false) { - adPodAdUnits.forEach((adUnit) => { - const adPodTargeting = []; - let adPodDurationSeconds = deepAccess(adUnit, 'mediaTypes.video.adPodDurationSec'); - - bids - .filter((bid) => bid.adUnitCode === adUnit.code) - .forEach((bid, index, arr) => { - if (bid.video.durationBucket <= adPodDurationSeconds) { - adPodTargeting.push({ - [TARGETING_KEY_PB_CAT_DUR]: bid.adserverTargeting[TARGETING_KEY_PB_CAT_DUR] - }); - adPodDurationSeconds -= bid.video.durationBucket; - } - if (index === arr.length - 1 && adPodTargeting.length > 0) { - adPodTargeting.push({ - [TARGETING_KEY_CACHE_ID]: bid.adserverTargeting[TARGETING_KEY_CACHE_ID] - }); - } - }); - targeting[adUnit.code] = adPodTargeting; - }); - - callback(null, targeting); - } else { - const bidsToCache = []; - adPodAdUnits.forEach((adUnit) => { - let adPodDurationSeconds = deepAccess(adUnit, 'mediaTypes.video.adPodDurationSec'); - - bids - .filter((bid) => bid.adUnitCode === adUnit.code) - .forEach((bid) => { - if (bid.video.durationBucket <= adPodDurationSeconds) { - bidsToCache.push(bid); - adPodDurationSeconds -= bid.video.durationBucket; - } - }); - }); - - callPrebidCacheAfterAuction(bidsToCache, function (error, bidsSuccessfullyCached) { - if (error) { - callback(error, null); - } else { - const groupedBids = groupBy(bidsSuccessfullyCached, 'adUnitCode'); - Object.keys(groupedBids).forEach((adUnitCode) => { - const adPodTargeting = []; - - groupedBids[adUnitCode].forEach((bid, index, arr) => { - adPodTargeting.push({ - [TARGETING_KEY_PB_CAT_DUR]: bid.adserverTargeting[TARGETING_KEY_PB_CAT_DUR] - }); - - if (index === arr.length - 1 && adPodTargeting.length > 0) { - adPodTargeting.push({ - [TARGETING_KEY_CACHE_ID]: bid.adserverTargeting[TARGETING_KEY_CACHE_ID] - }); - } - }); - targeting[adUnitCode] = adPodTargeting; - }); - - callback(null, targeting); - } - }); - } - return targeting; -} - -/** - * This function returns the adunit of mediaType adpod - * @param {Array} codes adUnitCodes - * @returns {Array[Object]} adunits of mediaType adpod - */ -function getAdPodAdUnits(codes) { - return auctionManager.getAdUnits() - .filter((adUnit) => deepAccess(adUnit, 'mediaTypes.video.context') === ADPOD) - .filter((adUnit) => (codes.length > 0) ? codes.indexOf(adUnit.code) !== -1 : true); -} - -/** - * This function will create compare function to sort on object property - * @param {string} property - * @returns {function} compare function to be used in sorting - */ -function compareOn(property) { - return function compare(a, b) { - if (a[property] < b[property]) { - return 1; - } - if (a[property] > b[property]) { - return -1; - } - return 0; - } -} - -/** - * This function removes bids of same category. It will be used when competitive exclusion is enabled. - * @param {Array[Object]} bidsReceived - * @returns {Array[Object]} unique category bids - */ -function getExclusiveBids(bidsReceived) { - let bids = bidsReceived - .map((bid) => Object.assign({}, bid, { [TARGETING_KEY_PB_CAT_DUR]: bid.adserverTargeting[TARGETING_KEY_PB_CAT_DUR] })); - bids = groupBy(bids, TARGETING_KEY_PB_CAT_DUR); - const filteredBids = []; - Object.keys(bids).forEach((targetingKey) => { - bids[targetingKey].sort(compareOn('responseTimestamp')); - filteredBids.push(bids[targetingKey][0]); - }); - return filteredBids; -} - -/** - * This function returns bids for adpod adunits - * @param {Array[Object]} bidsReceived - * @param {Array[Object]} adPodAdUnits - * @returns {Array[Object]} bids of mediaType adpod - */ -function getBidsForAdpod(bidsReceived, adPodAdUnits) { - const adUnitCodes = adPodAdUnits.map((adUnit) => adUnit.code); - return bidsReceived - .filter((bid) => adUnitCodes.indexOf(bid.adUnitCode) !== -1 && (bid.video && bid.video.context === ADPOD)) -} - -const sharedMethods = { - TARGETING_KEY_PB_CAT_DUR: TARGETING_KEY_PB_CAT_DUR, - TARGETING_KEY_CACHE_ID: TARGETING_KEY_CACHE_ID, - 'getTargeting': getTargeting -} -Object.freeze(sharedMethods); - -module('adpod', function shareAdpodUtilities(...args) { - if (!isPlainObject(args[0])) { - logError('Adpod module needs plain object to share methods with submodule'); - return; - } - function addMethods(object, func) { - for (const name in func) { - object[name] = func[name]; - } - } - addMethods(args[0], sharedMethods); -}); diff --git a/modules/adponeBidAdapter.js b/modules/adponeBidAdapter.js index 4e457b86f84..dbe8cc5fc39 100644 --- a/modules/adponeBidAdapter.js +++ b/modules/adponeBidAdapter.js @@ -1,6 +1,6 @@ -import {BANNER} from '../src/mediaTypes.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {triggerPixel} from '../src/utils.js'; +import { BANNER } from '../src/mediaTypes.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { triggerPixel } from '../src/utils.js'; const ADPONE_CODE = 'adpone'; const ADPONE_ENDPOINT = 'https://rtb.adpone.com/bid-request'; diff --git a/modules/adqueryBidAdapter.js b/modules/adqueryBidAdapter.js index 36a7eee206c..ccbee1e7fd7 100644 --- a/modules/adqueryBidAdapter.js +++ b/modules/adqueryBidAdapter.js @@ -1,5 +1,5 @@ -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER, VIDEO} from '../src/mediaTypes.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; import { buildUrl, logInfo, @@ -328,7 +328,7 @@ function buildRequest(bid, bidderRequest, isVideo = false) { videoRequest.id = bid.bidId let currency = bid?.ortb2?.ext?.prebid?.adServerCurrency || "PLN"; - videoRequest.cur = [ currency ] + videoRequest.cur = [currency] let floorInfo; if (typeof bid.getFloor === 'function') { diff --git a/modules/adqueryIdSystem.js b/modules/adqueryIdSystem.js index 3f324506b45..f4c44d30ac1 100644 --- a/modules/adqueryIdSystem.js +++ b/modules/adqueryIdSystem.js @@ -5,11 +5,11 @@ * @requires module:modules/userId */ -import {ajax} from '../src/ajax.js'; -import {getStorageManager} from '../src/storageManager.js'; -import {submodule} from '../src/hook.js'; -import {isFn, isPlainObject, isStr, logError, logInfo, logMessage} from '../src/utils.js'; -import {MODULE_TYPE_UID} from '../src/activities/modules.js'; +import { ajax } from '../src/ajax.js'; +import { getStorageManager } from '../src/storageManager.js'; +import { submodule } from '../src/hook.js'; +import { isFn, isPlainObject, isStr, logError, logInfo, logMessage } from '../src/utils.js'; +import { MODULE_TYPE_UID } from '../src/activities/modules.js'; /** * @typedef {import('../modules/userId/index.js').Submodule} Submodule @@ -20,7 +20,7 @@ import {MODULE_TYPE_UID} from '../src/activities/modules.js'; const MODULE_NAME = 'qid'; const AU_GVLID = 902; -export const storage = getStorageManager({moduleType: MODULE_TYPE_UID, moduleName: 'qid'}); +export const storage = getStorageManager({ moduleType: MODULE_TYPE_UID, moduleName: 'qid' }); /** * Param or default. @@ -57,7 +57,7 @@ export const adqueryIdSubmodule = { * @returns {{qid:Object}} */ decode(value) { - return {qid: value} + return { qid: value } }, /** * performs action to obtain id and return a value in the callback's response argument @@ -121,9 +121,9 @@ export const adqueryIdSubmodule = { callback(); } }; - ajax(url + '?qid=' + qid, callbacks, undefined, {method: 'GET'}); + ajax(url + '?qid=' + qid, callbacks, undefined, { method: 'GET' }); }; - return {callback: resp}; + return { callback: resp }; }, eids: { 'qid': { diff --git a/modules/adrelevantisBidAdapter.js b/modules/adrelevantisBidAdapter.js index 7f2d850a23a..32b408f73b0 100644 --- a/modules/adrelevantisBidAdapter.js +++ b/modules/adrelevantisBidAdapter.js @@ -1,4 +1,4 @@ -import {Renderer} from '../src/Renderer.js'; +import { Renderer } from '../src/Renderer.js'; import { createTrackPixelHtml, deepAccess, @@ -12,15 +12,16 @@ import { logMessage, logWarn } from '../src/utils.js'; -import {config} from '../src/config.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js'; -import {INSTREAM, OUTSTREAM} from '../src/video.js'; +import { config } from '../src/config.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; +import { INSTREAM, OUTSTREAM } from '../src/video.js'; import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; -import {getANKeywordParam} from '../libraries/appnexusUtils/anKeywords.js'; -import {chunk} from '../libraries/chunk/chunk.js'; -import {transformSizes} from '../libraries/sizeUtils/tranformSize.js'; -import {hasUserInfo, hasAppDeviceInfo, hasAppId} from '../libraries/adrelevantisUtils/bidderUtils.js'; +import { getANKeywordParam } from '../libraries/appnexusUtils/anKeywords.js'; +import { chunk } from '../libraries/chunk/chunk.js'; +import { transformSizes } from '../libraries/sizeUtils/tranformSize.js'; +import { hasUserInfo, hasAppDeviceInfo, hasAppId } from '../libraries/adrelevantisUtils/bidderUtils.js'; +import { getAdUnitElement } from '../src/utils/adUnits.js'; /** * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest @@ -83,7 +84,7 @@ export const spec = { const userObjBid = ((bidRequests) || []).find(hasUserInfo); let userObj; if (config.getConfig('coppa') === true) { - userObj = {'coppa': true}; + userObj = { 'coppa': true }; } if (userObjBid) { userObj = {}; @@ -166,7 +167,7 @@ export const spec = { * @param {*} serverResponse A successful response from the server. * @return {Bid[]} An array of bids which were nested inside the server. */ - interpretResponse: function(serverResponse, {bidderRequest}) { + interpretResponse: function(serverResponse, { bidderRequest }) { serverResponse = serverResponse.body; const bids = []; if (!serverResponse || serverResponse.error) { @@ -250,10 +251,9 @@ function newRenderer(adUnitCode, rtbBid, rendererOptions = {}) { /** * This function hides google div container for outstream bids to remove unwanted space on page. Appnexus renderer creates a new iframe outside of google iframe to render the outstream creative. - * @param {string} elementId element id */ -function hidedfpContainer(elementId) { - var el = document.getElementById(elementId).querySelectorAll("div[id^='google_ads']"); +function hidedfpContainer(bid) { + var el = getAdUnitElement(bid).querySelectorAll("div[id^='google_ads']"); if (el[0]) { el[0].style.setProperty('display', 'none'); } @@ -261,7 +261,7 @@ function hidedfpContainer(elementId) { function outstreamRender(bid) { // push to render queue because ANOutstreamVideo may not be loaded yet - hidedfpContainer(bid.adUnitCode); + hidedfpContainer(bid); bid.renderer.push(() => { window.ANOutstreamVideo.renderAd({ tagId: bid.adResponse.tag_id, @@ -375,7 +375,8 @@ function newBid(serverBid, rtbBid, bidderRequest) { bid['native'].image = { url: nativeAd.main_img.url, height: nativeAd.main_img.height, - width: nativeAd.main_img.width}; + width: nativeAd.main_img.width + }; } if (nativeAd.icon) { bid['native'].icon = { @@ -419,7 +420,7 @@ function bidToTag(bid) { tag.prebid = true; tag.disable_psa = true; if (bid.params.position) { - tag.position = {'above': 1, 'below': 2}[bid.params.position] || 0; + tag.position = { 'above': 1, 'below': 2 }[bid.params.position] || 0; } else { const mediaTypePos = deepAccess(bid, `mediaTypes.banner.pos`) || deepAccess(bid, `mediaTypes.video.pos`); // only support unknown, atf, and btf values for position at this time @@ -459,7 +460,7 @@ function bidToTag(bid) { if (bid.nativeParams) { const nativeRequest = buildNativeRequest(bid.nativeParams); - tag[NATIVE] = {layouts: [nativeRequest]}; + tag[NATIVE] = { layouts: [nativeRequest] }; } } @@ -487,7 +488,7 @@ function bidToTag(bid) { } if (bid.renderer) { - tag.video = Object.assign({}, tag.video, {custom_renderer_present: true}); + tag.video = Object.assign({}, tag.video, { custom_renderer_present: true }); } if ( diff --git a/modules/adrinoBidAdapter.js b/modules/adrinoBidAdapter.js index bc3c929cd5a..f6a80e5b570 100644 --- a/modules/adrinoBidAdapter.js +++ b/modules/adrinoBidAdapter.js @@ -1,7 +1,7 @@ -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {triggerPixel} from '../src/utils.js'; -import {NATIVE, BANNER} from '../src/mediaTypes.js'; -import {config} from '../src/config.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { triggerPixel } from '../src/utils.js'; +import { NATIVE, BANNER } from '../src/mediaTypes.js'; +import { config } from '../src/config.js'; import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; const BIDDER_CODE = 'adrino'; diff --git a/modules/adriverBidAdapter.js b/modules/adriverBidAdapter.js index 541d8e733eb..24b1fdc3c46 100644 --- a/modules/adriverBidAdapter.js +++ b/modules/adriverBidAdapter.js @@ -1,5 +1,5 @@ // ADRIVER BID ADAPTER for Prebid 1.13 -import {logInfo, getWindowLocation, _each, getBidIdParameter, isPlainObject} from '../src/utils.js'; +import { logInfo, getWindowLocation, _each, getBidIdParameter, isPlainObject } from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { getStorageManager } from '../src/storageManager.js'; @@ -7,7 +7,7 @@ const BIDDER_CODE = 'adriver'; const ADRIVER_BID_URL = 'https://pb.adriver.ru/cgi-bin/bid.cgi'; const TIME_TO_LIVE = 3000; -export const storage = getStorageManager({bidderCode: BIDDER_CODE}); +export const storage = getStorageManager({ bidderCode: BIDDER_CODE }); export const spec = { code: BIDDER_CODE, @@ -73,7 +73,7 @@ export const spec = { } par = { 'id': bid.params.placementId, - 'ext': {'query': 'bn=15&custom=111=' + bid.bidId}, + 'ext': { 'query': 'bn=15&custom=111=' + bid.bidId }, 'banner': { 'w': width || undefined, 'h': height || undefined diff --git a/modules/adriverIdSystem.js b/modules/adriverIdSystem.js index 7e659e914b0..ad4c13c981e 100644 --- a/modules/adriverIdSystem.js +++ b/modules/adriverIdSystem.js @@ -8,8 +8,8 @@ import { logError, isPlainObject } from '../src/utils.js' import { ajax } from '../src/ajax.js'; import { submodule } from '../src/hook.js'; -import {getStorageManager} from '../src/storageManager.js'; -import {MODULE_TYPE_UID} from '../src/activities/modules.js'; +import { getStorageManager } from '../src/storageManager.js'; +import { MODULE_TYPE_UID } from '../src/activities/modules.js'; /** * @typedef {import('../modules/userId/index.js').Submodule} Submodule @@ -20,7 +20,7 @@ import {MODULE_TYPE_UID} from '../src/activities/modules.js'; const MODULE_NAME = 'adriverId'; -export const storage = getStorageManager({moduleType: MODULE_TYPE_UID, moduleName: MODULE_NAME}); +export const storage = getStorageManager({ moduleType: MODULE_TYPE_UID, moduleName: MODULE_NAME }); /** @type {Submodule} */ export const adriverIdSubmodule = { @@ -81,10 +81,10 @@ export const adriverIdSubmodule = { } }; const newUrl = url + '&cid=' + (storage.getDataFromLocalStorage('adrcid') || storage.getCookie('adrcid')); - ajax(newUrl, callbacks, undefined, {method: 'GET'}); + ajax(newUrl, callbacks, undefined, { method: 'GET' }); } }; - return {callback: resp}; + return { callback: resp }; } }; diff --git a/modules/adspiritBidAdapter.js b/modules/adspiritBidAdapter.js index f474298ba82..d484e0c95b7 100644 --- a/modules/adspiritBidAdapter.js +++ b/modules/adspiritBidAdapter.js @@ -74,9 +74,9 @@ export const spec = { : [ { id: 1, required: 1, title: { len: 100 } }, { id: 2, required: 1, img: { type: 3, wmin: 1200, hmin: 627, mimes: ['image/png', 'image/gif', 'image/jpeg'] } }, - { id: 4, required: 1, data: {type: 2, len: 150} }, - { id: 3, required: 0, data: {type: 12, len: 50} }, - { id: 6, required: 0, data: {type: 1, len: 50} }, + { id: 4, required: 1, data: { type: 2, len: 150 } }, + { id: 3, required: 0, data: { type: 12, len: 50 } }, + { id: 6, required: 0, data: { type: 1, len: 50 } }, { id: 5, required: 0, img: { type: 1, wmin: 50, hmin: 50, mimes: ['image/png', 'image/gif', 'image/jpeg'] } } ] diff --git a/modules/adtargetBidAdapter.js b/modules/adtargetBidAdapter.js index 138f1b0e013..a40326d2e48 100644 --- a/modules/adtargetBidAdapter.js +++ b/modules/adtargetBidAdapter.js @@ -1,8 +1,8 @@ -import {_map, deepAccess, flatten, isArray, logError, parseSizesInput} from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER, VIDEO} from '../src/mediaTypes.js'; -import {config} from '../src/config.js'; -import {chunk} from '../libraries/chunk/chunk.js'; +import { _map, deepAccess, flatten, isArray, logError, parseSizesInput } from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; +import { config } from '../src/config.js'; +import { chunk } from '../libraries/chunk/chunk.js'; import { createTag, getUserSyncsFn, isBidRequestValid, diff --git a/modules/adtelligentBidAdapter.js b/modules/adtelligentBidAdapter.js index 010d2b74409..b14b0f28d82 100644 --- a/modules/adtelligentBidAdapter.js +++ b/modules/adtelligentBidAdapter.js @@ -1,15 +1,15 @@ -import {_map, deepAccess, flatten, isArray, parseSizesInput} from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {ADPOD, BANNER, VIDEO} from '../src/mediaTypes.js'; -import {config} from '../src/config.js'; -import {Renderer} from '../src/Renderer.js'; -import {chunk} from '../libraries/chunk/chunk.js'; +import { _map, deepAccess, flatten, isArray, parseSizesInput } from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; +import { config } from '../src/config.js'; +import { Renderer } from '../src/Renderer.js'; +import { chunk } from '../libraries/chunk/chunk.js'; import { createTag, getUserSyncsFn, isBidRequestValid, supportedMediaTypes } from '../libraries/adtelligentUtils/adtelligentUtils.js'; - +import { getPlacementPositionUtils } from "../libraries/placementPositionInfo/placementPositionInfo.js"; /** * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid * @typedef {import('../src/adapters/bidderFactory.js').BidderRequest} BidderRequest @@ -30,7 +30,8 @@ const HOST_GETTERS = { ocm: () => 'ghb.cenarius.orangeclickmedia.com', '9dotsmedia': () => 'ghb.platform.audiodots.com', indicue: () => 'ghb.console.indicue.com', - stellormedia: () => 'ghb.ads.stellormedia.com'} + stellormedia: () => 'ghb.ads.stellormedia.com' +} const getUri = function (bidderCode) { const bidderWithoutSuffix = bidderCode.split('_')[0]; const getter = HOST_GETTERS[bidderWithoutSuffix] || HOST_GETTERS['default']; @@ -168,11 +169,13 @@ function prepareBidRequests(bidReq) { const mediaType = deepAccess(bidReq, 'mediaTypes.video') ? VIDEO : DISPLAY; const sizes = mediaType === VIDEO ? deepAccess(bidReq, 'mediaTypes.video.playerSize') : deepAccess(bidReq, 'mediaTypes.banner.sizes'); const gpid = deepAccess(bidReq, 'ortb2Imp.ext.gpid'); + const placementInfo = getPlacementPositionUtils().getPlacementInfo(bidReq) const bidReqParams = { 'CallbackId': bidReq.bidId, 'Aid': bidReq.params.aid, 'AdType': mediaType, - 'Sizes': parseSizesInput(sizes).join(',') + 'Sizes': parseSizesInput(sizes).join(','), + ...placementInfo }; bidReqParams.PlacementId = bidReq.adUnitCode; @@ -187,14 +190,6 @@ function prepareBidRequests(bidReq) { bidReqParams.GPID = gpid; } - if (mediaType === VIDEO) { - const context = deepAccess(bidReq, 'mediaTypes.video.context'); - - if (context === ADPOD) { - bidReqParams.Adpod = deepAccess(bidReq, 'mediaTypes.video'); - } - } - return bidReqParams; } @@ -237,18 +232,6 @@ function createBid(bidResponse, bidRequest) { adUrl: bidResponse.adUrl, }); } - if (context === ADPOD) { - Object.assign(bid, { - meta: { - primaryCatId: bidResponse.primaryCatId, - }, - video: { - context: ADPOD, - durationSeconds: bidResponse.durationSeconds - } - }); - } - Object.assign(bid, { vastUrl: bidResponse.vastUrl }); diff --git a/modules/adtelligentIdSystem.js b/modules/adtelligentIdSystem.js index 08d8a056dac..428633a6e4c 100644 --- a/modules/adtelligentIdSystem.js +++ b/modules/adtelligentIdSystem.js @@ -72,7 +72,7 @@ export const adtelligentIdModule = { * @param {ConsentData} [consentData] * @returns {IdResponse} */ - getId(config, {gdpr: consentData} = {}) { + getId(config, { gdpr: consentData } = {}) { const gdpr = consentData && consentData.gdprApplies ? 1 : 0; const gdprConsent = gdpr ? consentData.consentString : ''; const url = buildUrl({ diff --git a/modules/adtrgtmeBidAdapter.js b/modules/adtrgtmeBidAdapter.js index dc15dd2dc9f..a4badd11a4c 100644 --- a/modules/adtrgtmeBidAdapter.js +++ b/modules/adtrgtmeBidAdapter.js @@ -11,6 +11,7 @@ import { } from '../src/utils.js'; import { config } from '../src/config.js'; import { hasPurpose1Consent } from '../src/utils/gdpr.js'; +import { getDNT } from '../libraries/dnt/index.js'; const BIDDER_CODE = 'adtrgtme'; const BIDDER_VERSION = '1.0.7'; @@ -71,7 +72,7 @@ function createORTB(bR, bid) { ...site, }, device: { - dnt: bid?.params?.dnt ? 1 : 0, + dnt: getDNT() ? 1 : 0, ua: bid?.params?.ua || navigator.userAgent, ip, }, diff --git a/modules/adtrueBidAdapter.js b/modules/adtrueBidAdapter.js index 2c4278b9fb8..9fdb1c051c4 100644 --- a/modules/adtrueBidAdapter.js +++ b/modules/adtrueBidAdapter.js @@ -1,13 +1,13 @@ -import {getDNT} from '../libraries/dnt/index.js'; import { logWarn, isArray, inIframe, isNumber, isStr, deepClone, deepSetValue, logError, deepAccess, isBoolean } from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js'; -import {config} from '../src/config.js'; -import {getStorageManager} from '../src/storageManager.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; +import { config } from '../src/config.js'; +import { getStorageManager } from '../src/storageManager.js'; import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; +import { getDNT } from '../libraries/dnt/index.js'; const BIDDER_CODE = 'adtrue'; -const storage = getStorageManager({bidderCode: BIDDER_CODE}); +const storage = getStorageManager({ bidderCode: BIDDER_CODE }); const ADTRUE_CURRENCY = 'USD'; const ENDPOINT_URL = 'https://hb.adtrue.com/prebid/auction'; const LOG_WARN_PREFIX = 'AdTrue: '; @@ -50,28 +50,28 @@ const VIDEO_CUSTOM_PARAMS = { }; const NATIVE_ASSETS = { - 'TITLE': {ID: 1, KEY: 'title', TYPE: 0}, - 'IMAGE': {ID: 2, KEY: 'image', TYPE: 0}, - 'ICON': {ID: 3, KEY: 'icon', TYPE: 0}, - 'SPONSOREDBY': {ID: 4, KEY: 'sponsoredBy', TYPE: 1}, // please note that type of SPONSORED is also 1 - 'BODY': {ID: 5, KEY: 'body', TYPE: 2}, // please note that type of DESC is also set to 2 - 'CLICKURL': {ID: 6, KEY: 'clickUrl', TYPE: 0}, - 'VIDEO': {ID: 7, KEY: 'video', TYPE: 0}, - 'EXT': {ID: 8, KEY: 'ext', TYPE: 0}, - 'DATA': {ID: 9, KEY: 'data', TYPE: 0}, - 'LOGO': {ID: 10, KEY: 'logo', TYPE: 0}, - 'SPONSORED': {ID: 11, KEY: 'sponsored', TYPE: 1}, // please note that type of SPONSOREDBY is also set to 1 - 'DESC': {ID: 12, KEY: 'data', TYPE: 2}, // please note that type of BODY is also set to 2 - 'RATING': {ID: 13, KEY: 'rating', TYPE: 3}, - 'LIKES': {ID: 14, KEY: 'likes', TYPE: 4}, - 'DOWNLOADS': {ID: 15, KEY: 'downloads', TYPE: 5}, - 'PRICE': {ID: 16, KEY: 'price', TYPE: 6}, - 'SALEPRICE': {ID: 17, KEY: 'saleprice', TYPE: 7}, - 'PHONE': {ID: 18, KEY: 'phone', TYPE: 8}, - 'ADDRESS': {ID: 19, KEY: 'address', TYPE: 9}, - 'DESC2': {ID: 20, KEY: 'desc2', TYPE: 10}, - 'DISPLAYURL': {ID: 21, KEY: 'displayurl', TYPE: 11}, - 'CTA': {ID: 22, KEY: 'cta', TYPE: 12} + 'TITLE': { ID: 1, KEY: 'title', TYPE: 0 }, + 'IMAGE': { ID: 2, KEY: 'image', TYPE: 0 }, + 'ICON': { ID: 3, KEY: 'icon', TYPE: 0 }, + 'SPONSOREDBY': { ID: 4, KEY: 'sponsoredBy', TYPE: 1 }, // please note that type of SPONSORED is also 1 + 'BODY': { ID: 5, KEY: 'body', TYPE: 2 }, // please note that type of DESC is also set to 2 + 'CLICKURL': { ID: 6, KEY: 'clickUrl', TYPE: 0 }, + 'VIDEO': { ID: 7, KEY: 'video', TYPE: 0 }, + 'EXT': { ID: 8, KEY: 'ext', TYPE: 0 }, + 'DATA': { ID: 9, KEY: 'data', TYPE: 0 }, + 'LOGO': { ID: 10, KEY: 'logo', TYPE: 0 }, + 'SPONSORED': { ID: 11, KEY: 'sponsored', TYPE: 1 }, // please note that type of SPONSOREDBY is also set to 1 + 'DESC': { ID: 12, KEY: 'data', TYPE: 2 }, // please note that type of BODY is also set to 2 + 'RATING': { ID: 13, KEY: 'rating', TYPE: 3 }, + 'LIKES': { ID: 14, KEY: 'likes', TYPE: 4 }, + 'DOWNLOADS': { ID: 15, KEY: 'downloads', TYPE: 5 }, + 'PRICE': { ID: 16, KEY: 'price', TYPE: 6 }, + 'SALEPRICE': { ID: 17, KEY: 'saleprice', TYPE: 7 }, + 'PHONE': { ID: 18, KEY: 'phone', TYPE: 8 }, + 'ADDRESS': { ID: 19, KEY: 'address', TYPE: 9 }, + 'DESC2': { ID: 20, KEY: 'desc2', TYPE: 10 }, + 'DISPLAYURL': { ID: 21, KEY: 'displayurl', TYPE: 11 }, + 'CTA': { ID: 22, KEY: 'cta', TYPE: 12 } }; function _getDomainFromURL(url) { @@ -299,7 +299,7 @@ function _createBannerRequest(bid) { format = []; sizes.forEach(function (size) { if (size.length > 1) { - format.push({w: size[0], h: size[1]}); + format.push({ w: size[0], h: size[1] }); } }); if (format.length > 0) { @@ -400,7 +400,7 @@ function _createImpressionObject(bid, conf) { } } else { // mediaTypes is not present, so this is a banner only impression - // this part of code is required for older testcases with no 'mediaTypes' to run succesfully. + // this part of code is required for older testcases with no 'mediaTypes' to run successfully. bannerObj = { pos: 0, w: bid.params.width, diff --git a/modules/aduptechBidAdapter.js b/modules/aduptechBidAdapter.js index fdc1249ded4..96a1b51b1ae 100644 --- a/modules/aduptechBidAdapter.js +++ b/modules/aduptechBidAdapter.js @@ -1,8 +1,8 @@ -import {deepClone, isArray, isBoolean, isEmpty, isFn, isPlainObject} from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER, NATIVE} from '../src/mediaTypes.js'; +import { deepClone, isArray, isBoolean, isEmpty, isFn, isPlainObject } from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, NATIVE } from '../src/mediaTypes.js'; import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; -import {getAdUnitSizes} from '../libraries/sizeUtils/sizeUtils.js'; +import { getAdUnitSizes } from '../libraries/sizeUtils/sizeUtils.js'; /** * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest diff --git a/modules/advRedAnalyticsAdapter.js b/modules/advRedAnalyticsAdapter.js index 933d9bdc584..b9e0262a4ab 100644 --- a/modules/advRedAnalyticsAdapter.js +++ b/modules/advRedAnalyticsAdapter.js @@ -1,9 +1,9 @@ -import {generateUUID, logInfo} from '../src/utils.js' -import {ajaxBuilder} from '../src/ajax.js' +import { generateUUID, logInfo } from '../src/utils.js' +import { ajaxBuilder } from '../src/ajax.js' import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js' import adapterManager from '../src/adapterManager.js' -import {EVENTS} from '../src/constants.js' -import {getRefererInfo} from '../src/refererDetection.js'; +import { EVENTS } from '../src/constants.js' +import { getRefererInfo } from '../src/refererDetection.js'; /** * advRedAnalyticsAdapter.js - analytics adapter for AdvRed @@ -16,8 +16,8 @@ let initOptions let flushInterval let queue = [] -const advRedAnalytics = Object.assign(adapter({url: DEFAULT_EVENT_URL, analyticsType: 'endpoint'}), { - track({eventType, args}) { +const advRedAnalytics = Object.assign(adapter({ url: DEFAULT_EVENT_URL, analyticsType: 'endpoint' }), { + track({ eventType, args }) { handleEvent(eventType, args) } }) diff --git a/modules/advertisingBidAdapter.js b/modules/advertisingBidAdapter.js index 3fb035340c9..ac7bb125a7e 100644 --- a/modules/advertisingBidAdapter.js +++ b/modules/advertisingBidAdapter.js @@ -1,17 +1,17 @@ 'use strict'; -import {deepAccess, deepSetValue, isFn, isPlainObject, logWarn, mergeDeep} from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER, VIDEO} from '../src/mediaTypes.js'; +import { deepAccess, deepSetValue, isFn, isPlainObject, logWarn, mergeDeep } from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; -import {config} from '../src/config.js'; -import {getAdUnitSizes} from '../libraries/sizeUtils/sizeUtils.js'; +import { config } from '../src/config.js'; +import { getAdUnitSizes } from '../libraries/sizeUtils/sizeUtils.js'; const BID_SCHEME = 'https://'; const BID_DOMAIN = 'technoratimedia.com'; const USER_SYNC_IFRAME_URL = 'https://ad-cdn.technoratimedia.com/html/usersync.html'; const USER_SYNC_PIXEL_URL = 'https://sync.technoratimedia.com/services'; -const VIDEO_PARAMS = [ 'minduration', 'maxduration', 'startdelay', 'placement', 'plcmt', 'linearity', 'mimes', 'protocols', 'api' ]; +const VIDEO_PARAMS = ['minduration', 'maxduration', 'startdelay', 'placement', 'plcmt', 'linearity', 'mimes', 'protocols', 'api']; const BLOCKED_AD_SIZES = [ '1x1', '1x2' @@ -23,7 +23,7 @@ export const spec = { { code: 'synacormedia' }, { code: 'imds' } ], - supportedMediaTypes: [ BANNER, VIDEO ], + supportedMediaTypes: [BANNER, VIDEO], sizeMap: {}, isVideoBid: function(bid) { @@ -229,7 +229,7 @@ export const spec = { if (!serverResponse.body || typeof serverResponse.body !== 'object') { return; } - const {id, seatbid: seatbids} = serverResponse.body; + const { id, seatbid: seatbids } = serverResponse.body; const bids = []; if (id && seatbids) { seatbids.forEach(seatbid => { diff --git a/modules/adverxoBidAdapter.js b/modules/adverxoBidAdapter.js index a4e77ee9f30..0801fc021c1 100644 --- a/modules/adverxoBidAdapter.js +++ b/modules/adverxoBidAdapter.js @@ -1,10 +1,10 @@ import * as utils from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER, VIDEO, NATIVE} from '../src/mediaTypes.js'; -import {ortbConverter as OrtbConverter} from '../libraries/ortbConverter/converter.js'; -import {Renderer} from '../src/Renderer.js'; -import {deepAccess, deepSetValue} from '../src/utils.js'; -import {config} from '../src/config.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, VIDEO, NATIVE } from '../src/mediaTypes.js'; +import { ortbConverter as OrtbConverter } from '../libraries/ortbConverter/converter.js'; +import { Renderer } from '../src/Renderer.js'; +import { deepAccess, deepSetValue } from '../src/utils.js'; +import { config } from '../src/config.js'; /** * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid @@ -19,10 +19,10 @@ import {config} from '../src/config.js'; const BIDDER_CODE = 'adverxo'; const ALIASES = [ - {code: 'adport', skipPbsAliasing: true}, - {code: 'bidsmind', skipPbsAliasing: true}, - {code: 'harrenmedia', skipPbsAliasing: true}, - {code: 'alchemyx', skipPbsAliasing: true} + { code: 'adport', skipPbsAliasing: true }, + { code: 'bidsmind', skipPbsAliasing: true }, + { code: 'harrenmedia', skipPbsAliasing: true }, + { code: 'alchemyx', skipPbsAliasing: true } ]; const AUCTION_URLS = { @@ -161,7 +161,7 @@ const videoUtils = { win.adxVideoRenderer.renderAd({ targetId: bid.adUnitCode, - adResponse: {content: bid.vastXml} + adResponse: { content: bid.vastXml } }); }); } diff --git a/modules/adxcgAnalyticsAdapter.js b/modules/adxcgAnalyticsAdapter.js index 34570a8dd71..04ccc64c43a 100644 --- a/modules/adxcgAnalyticsAdapter.js +++ b/modules/adxcgAnalyticsAdapter.js @@ -19,7 +19,7 @@ var adxcgAnalyticsAdapter = Object.assign(adapter( emptyUrl, analyticsType }), { - track ({eventType, args}) { + track ({ eventType, args }) { switch (eventType) { case EVENTS.AUCTION_INIT: adxcgAnalyticsAdapter.context.events.auctionInit = mapAuctionInit(args); @@ -40,7 +40,7 @@ var adxcgAnalyticsAdapter = Object.assign(adapter( adxcgAnalyticsAdapter.context.events.bidResponses.push(mapBidResponse(args, eventType)); break; case EVENTS.BID_WON: - const outData2 = {bidWons: mapBidWon(args)}; + const outData2 = { bidWons: mapBidWon(args) }; send(outData2); break; case EVENTS.AUCTION_END: diff --git a/modules/adxpremiumAnalyticsAdapter.js b/modules/adxpremiumAnalyticsAdapter.js index 5329336fd32..cdd8bbb91a2 100644 --- a/modules/adxpremiumAnalyticsAdapter.js +++ b/modules/adxpremiumAnalyticsAdapter.js @@ -1,5 +1,5 @@ -import {deepClone, logError, logInfo} from '../src/utils.js'; -import {ajax} from '../src/ajax.js'; +import { deepClone, logError, logInfo } from '../src/utils.js'; +import { ajax } from '../src/ajax.js'; import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; import { EVENTS } from '../src/constants.js'; diff --git a/modules/adyoulikeBidAdapter.js b/modules/adyoulikeBidAdapter.js index 9054c197bbd..bb0f0b9a330 100644 --- a/modules/adyoulikeBidAdapter.js +++ b/modules/adyoulikeBidAdapter.js @@ -1,7 +1,7 @@ -import {buildUrl, deepAccess, parseSizesInput} from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; +import { buildUrl, deepAccess, parseSizesInput } from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; import { config } from '../src/config.js'; -import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js'; +import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; /** @@ -253,7 +253,7 @@ function getFloor(bidRequest, size, mediaType) { const bidFloors = bidRequest.getFloor({ currency: CURRENCY, mediaType, - size: [ size.width, size.height ] + size: [size.width, size.height] }); if (!isNaN(bidFloors?.floor) && (bidFloors?.currency === CURRENCY)) { diff --git a/modules/afpBidAdapter.js b/modules/afpBidAdapter.js index 3cb77a4eabc..a55bb007e5f 100644 --- a/modules/afpBidAdapter.js +++ b/modules/afpBidAdapter.js @@ -1,6 +1,6 @@ -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {Renderer} from '../src/Renderer.js'; -import {BANNER, VIDEO} from '../src/mediaTypes.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { Renderer } from '../src/Renderer.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; export const IS_DEV = location.hostname === 'localhost' export const BIDDER_CODE = 'afp' @@ -66,12 +66,12 @@ const createRenderer = (bid, dataToCreatePlace) => { export const spec = { code: BIDDER_CODE, supportedMediaTypes: [BANNER, VIDEO], - isBidRequestValid({mediaTypes, params}) { + isBidRequestValid({ mediaTypes, params }) { if (typeof params !== 'object' || typeof mediaTypes !== 'object') { return false } - const {placeId, placeType, imageUrl, imageWidth, imageHeight} = params + const { placeId, placeType, imageUrl, imageWidth, imageHeight } = params const media = mediaTypes[mediaTypeByPlaceType[placeType]] if (placeId && media) { @@ -94,14 +94,16 @@ export const spec = { } return false }, - buildRequests(validBidRequests, {refererInfo, gdprConsent}) { + buildRequests(validBidRequests, { refererInfo, gdprConsent }) { const payload = { pageUrl: IS_DEV ? TEST_PAGE_URL : refererInfo.page, gdprConsent: gdprConsent, bidRequests: validBidRequests.map(validBidRequest => { - const {bidId, ortb2Imp, sizes, params: { - placeId, placeType, imageUrl, imageWidth, imageHeight - }} = validBidRequest + const { + bidId, ortb2Imp, sizes, params: { + placeId, placeType, imageUrl, imageWidth, imageHeight + } + } = validBidRequest bidRequestMap[bidId] = validBidRequest const bidRequest = { bidId, @@ -133,7 +135,7 @@ export const spec = { let bids = serverResponse.body && serverResponse.body.bids bids = Array.isArray(bids) ? bids : [] - return bids.map(({bidId, cpm, width, height, creativeId, currency, netRevenue, adSettings, placeSettings}, index) => { + return bids.map(({ bidId, cpm, width, height, creativeId, currency, netRevenue, adSettings, placeSettings }, index) => { const bid = { requestId: bidId, cpm, diff --git a/modules/agmaAnalyticsAdapter.js b/modules/agmaAnalyticsAdapter.js index cacdc5db976..ee7a7c1eb10 100644 --- a/modules/agmaAnalyticsAdapter.js +++ b/modules/agmaAnalyticsAdapter.js @@ -27,10 +27,10 @@ const pageViewId = generateUUID(); // Helper functions const getScreen = () => { try { - const {width: x, height: y} = getViewportSize(); + const { width: x, height: y } = getViewportSize(); return { x, y }; } catch (e) { - return {x: 0, y: 0}; + return { x: 0, y: 0 }; } }; diff --git a/modules/aidemBidAdapter.js b/modules/aidemBidAdapter.js index 8999de001b8..49c325d80ed 100644 --- a/modules/aidemBidAdapter.js +++ b/modules/aidemBidAdapter.js @@ -1,17 +1,17 @@ -import {deepAccess, deepClone, deepSetValue, getWinDimensions, isBoolean, isNumber, isStr, logError, logInfo} from '../src/utils.js'; -import {config} from '../src/config.js'; -import {BANNER, VIDEO} from '../src/mediaTypes.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {getRefererInfo} from '../src/refererDetection.js'; -import {ajax} from '../src/ajax.js'; -import {ortbConverter} from '../libraries/ortbConverter/converter.js'; +import { deepAccess, deepClone, deepSetValue, getWinDimensions, isBoolean, isNumber, isStr, logError, logInfo } from '../src/utils.js'; +import { config } from '../src/config.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { getRefererInfo } from '../src/refererDetection.js'; +import { ajax } from '../src/ajax.js'; +import { ortbConverter } from '../libraries/ortbConverter/converter.js'; const BIDDER_CODE = 'aidem'; const BASE_URL = 'https://zero.aidemsrv.com'; const LOCAL_BASE_URL = 'http://127.0.0.1:8787'; const SUPPORTED_MEDIA_TYPES = [BANNER, VIDEO]; -const REQUIRED_VIDEO_PARAMS = [ 'mimes', 'protocols', 'context' ]; +const REQUIRED_VIDEO_PARAMS = ['mimes', 'protocols', 'context']; export const ERROR_CODES = { BID_SIZE_INVALID_FORMAT: 1, @@ -71,7 +71,7 @@ const converter = ortbConverter({ return imp; }, bidResponse(buildBidResponse, bid, context) { - const {bidRequest} = context; + const { bidRequest } = context; const bidResponse = buildBidResponse(bid, context); logInfo('Building bidResponse'); logInfo('bid', bid); @@ -262,7 +262,7 @@ export const spec = { buildRequests: function(bidRequests, bidderRequest) { logInfo('bidRequests: ', bidRequests); logInfo('bidderRequest: ', bidderRequest); - const data = converter.toORTB({bidRequests, bidderRequest}); + const data = converter.toORTB({ bidRequests, bidderRequest }); logInfo('request payload', data); return { method: 'POST', @@ -277,7 +277,7 @@ export const spec = { interpretResponse: function (serverResponse, request) { logInfo('serverResponse body: ', serverResponse.body); logInfo('request data: ', request.data); - const ortbBids = converter.fromORTB({response: serverResponse.body, request: request.data}).bids; + const ortbBids = converter.fromORTB({ response: serverResponse.body, request: request.data }).bids; logInfo('ortbBids: ', ortbBids); return ortbBids; }, diff --git a/modules/airgridRtdProvider.js b/modules/airgridRtdProvider.js index c547528a57e..fdcf57de873 100644 --- a/modules/airgridRtdProvider.js +++ b/modules/airgridRtdProvider.js @@ -5,11 +5,11 @@ * @module modules/airgridRtdProvider * @requires module:modules/realTimeData */ -import {submodule} from '../src/hook.js'; -import {deepAccess, deepSetValue, mergeDeep} from '../src/utils.js'; -import {getStorageManager} from '../src/storageManager.js'; -import {loadExternalScript} from '../src/adloader.js'; -import {MODULE_TYPE_RTD} from '../src/activities/modules.js'; +import { submodule } from '../src/hook.js'; +import { deepAccess, deepSetValue, mergeDeep } from '../src/utils.js'; +import { getStorageManager } from '../src/storageManager.js'; +import { loadExternalScript } from '../src/adloader.js'; +import { MODULE_TYPE_RTD } from '../src/activities/modules.js'; /** * @typedef {import('../modules/rtdModule/index.js').RtdSubmodule} RtdSubmodule @@ -81,7 +81,7 @@ export function setAudiencesAsBidderOrtb2(bidConfig, rtdConfig, audiences) { segtax: 540, }, name: 'airgrid', - segment: audiences.map((id) => ({id})) + segment: audiences.map((id) => ({ id })) } ] deepSetValue(agOrtb2, 'user.data', agUserData); diff --git a/modules/ajaBidAdapter.js b/modules/ajaBidAdapter.js index a00fd698932..75c3ec18141 100644 --- a/modules/ajaBidAdapter.js +++ b/modules/ajaBidAdapter.js @@ -1,7 +1,7 @@ -import {createTrackPixelHtml, logError, getBidIdParameter} from '../src/utils.js'; +import { createTrackPixelHtml, logError, getBidIdParameter } from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER } from '../src/mediaTypes.js'; -import {tryAppendQueryString} from '../libraries/urlUtils/urlUtils.js'; +import { tryAppendQueryString } from '../libraries/urlUtils/urlUtils.js'; /** * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest diff --git a/modules/alkimiBidAdapter.js b/modules/alkimiBidAdapter.js index 8e77e2507b7..9611cd93521 100644 --- a/modules/alkimiBidAdapter.js +++ b/modules/alkimiBidAdapter.js @@ -1,16 +1,16 @@ -import {getDNT} from '../libraries/dnt/index.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {deepAccess, deepClone, generateUUID, replaceAuctionPrice} from '../src/utils.js'; -import {ajax} from '../src/ajax.js'; -import {getStorageManager} from '../src/storageManager.js'; -import {VIDEO, BANNER} from '../src/mediaTypes.js'; -import {config} from '../src/config.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { deepAccess, deepClone, generateUUID, replaceAuctionPrice } from '../src/utils.js'; +import { ajax } from '../src/ajax.js'; +import { getStorageManager } from '../src/storageManager.js'; +import { VIDEO, BANNER } from '../src/mediaTypes.js'; +import { config } from '../src/config.js'; +import { getDNT } from '../libraries/dnt/index.js'; const BIDDER_CODE = 'alkimi'; const GVLID = 1169; const USER_ID_KEY = 'alkimiUserID'; export const ENDPOINT = 'https://exchange.alkimi-onboarding.com/bid?prebid=true'; -export const storage = getStorageManager({bidderCode: BIDDER_CODE}); +export const storage = getStorageManager({ bidderCode: BIDDER_CODE }); export const spec = { code: BIDDER_CODE, @@ -85,7 +85,7 @@ export const spec = { let payload = { requestId: generateUUID(), - signRequest: {bids, randomUUID: alkimiConfig && alkimiConfig.randomUUID}, + signRequest: { bids, randomUUID: alkimiConfig && alkimiConfig.randomUUID }, bidIds, referer: bidderRequest.refererInfo.page, signature: alkimiConfig && alkimiConfig.signature, @@ -148,7 +148,7 @@ export const spec = { return []; } - const {prebidResponse} = serverBody; + const { prebidResponse } = serverBody; if (!Array.isArray(prebidResponse)) { return []; } @@ -194,7 +194,7 @@ export const spec = { const urls = []; iframeList.forEach(url => { - urls.push({type: 'iframe', url}); + urls.push({ type: 'iframe', url }); }); return urls; @@ -204,7 +204,7 @@ export const spec = { }; function prepareSizes(sizes) { - return sizes ? sizes.map(size => ({width: size[0], height: size[1]})) : []; + return sizes ? sizes.map(size => ({ width: size[0], height: size[1] })) : []; } function prepareBidFloorSize(sizes) { diff --git a/modules/allegroBidAdapter.js b/modules/allegroBidAdapter.js index 3c42c9f1e60..55ef8dfee72 100644 --- a/modules/allegroBidAdapter.js +++ b/modules/allegroBidAdapter.js @@ -1,11 +1,11 @@ // jshint esversion: 6, es3: false, node: true 'use strict'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER, VIDEO, NATIVE} from '../src/mediaTypes.js'; -import {ortbConverter} from '../libraries/ortbConverter/converter.js'; -import {config} from '../src/config.js'; -import {triggerPixel, logInfo, logError} from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, VIDEO, NATIVE } from '../src/mediaTypes.js'; +import { ortbConverter } from '../libraries/ortbConverter/converter.js'; +import { config } from '../src/config.js'; +import { triggerPixel, logInfo, logError } from '../src/utils.js'; const BIDDER_CODE = 'allegro'; const BIDDER_URL = 'https://prebid.rtb.allegrogroup.com/v1/rtb/prebid/bid'; @@ -91,7 +91,7 @@ function moveExt(obj, newKey) { if (!obj || !obj.ext) { return; } - const extCopy = {...obj.ext}; + const extCopy = { ...obj.ext }; delete obj.ext; obj[newKey] = extCopy; } @@ -217,7 +217,7 @@ export const spec = { return { method: 'POST', url: url, - data: converter.toORTB({bidderRequest, bidRequests}), + data: converter.toORTB({ bidderRequest, bidRequests }), options: { contentType: 'text/plain' }, @@ -232,7 +232,7 @@ export const spec = { */ interpretResponse: function (response, request) { if (!response.body) return; - return converter.fromORTB({response: response.body, request: request.data}).bids; + return converter.fromORTB({ response: response.body, request: request.data }).bids; }, /** diff --git a/modules/alliance_gravityBidAdapter.md b/modules/alliance_gravityBidAdapter.md new file mode 100644 index 00000000000..98a9e17a4ed --- /dev/null +++ b/modules/alliance_gravityBidAdapter.md @@ -0,0 +1,33 @@ +# Overview + +``` +Module Name: Alliance Gravity Bid Adapter +Module Type: Bidder Adapter +Maintainer: produit@alliancegravity.com +``` + +# Description + +Sends bids to Alliance Gravity network + +Alliance Gravity bid adapter supports Banner, Video, Audio and Native formats + +# Test Parameters +```javascript +var adUnits = [ + { + code: 'banner-div', + mediaTypes: { + banner: { + sizes: [[300, 250], [300,600]] + } + }, + bids: [{ + bidder: 'alliance_gravity', + params: { + srid: "test-id" + } + }] + }, +]; +``` diff --git a/modules/alliance_gravityBidAdapter.ts b/modules/alliance_gravityBidAdapter.ts new file mode 100644 index 00000000000..d4b6d024904 --- /dev/null +++ b/modules/alliance_gravityBidAdapter.ts @@ -0,0 +1,81 @@ +import { deepSetValue } from '../src/utils.js'; +import { AdapterRequest, BidderSpec, registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; +import { ortbConverter } from '../libraries/ortbConverter/converter.js' + +import { interpretResponse, enrichImp, getUserSyncs } from '../libraries/alliance_gravityUtils/index.js'; +import { getBoundingClientRect } from '../libraries/boundingClientRect/boundingClientRect.js'; +import { BidRequest, ClientBidderRequest } from '../src/adapterManager.js'; +import { ORTBImp, ORTBRequest } from '../src/prebid.public.js'; + +const BIDDER_CODE = 'alliance_gravity'; +const REQUEST_URL = 'https://pbs.production.agrvt.com/openrtb2/auction'; +const GVLID = 501; + +const DEFAULT_GZIP_ENABLED = false; + +declare module '../src/adUnits' { + interface BidderParams { + srid: string + } +} + +const converter = ortbConverter({ + context: { + netRevenue: true, // or false if your adapter should set bidResponse.netRevenue = false + ttl: 90, // default bidResponse.ttl (when not specified in ORTB response.seatbid[].bid[].exp) + }, + imp(buildImp, bidRequest: BidRequest, context) { + let imp:ORTBImp = buildImp(bidRequest, context); + imp = enrichImp(imp, bidRequest); + const adUnitCode = bidRequest.adUnitCode; + const slotEl:HTMLElement | null = document.getElementById(adUnitCode); + if (slotEl) { + const { width, height } = getBoundingClientRect(slotEl); + deepSetValue(imp, 'ext.dimensions.slotW', width); + deepSetValue(imp, 'ext.dimensions.slotH', height); + deepSetValue(imp, 'ext.dimensions.cssMaxW', slotEl.style?.maxWidth); + deepSetValue(imp, 'ext.dimensions.cssMaxH', slotEl.style?.maxHeight); + } + deepSetValue(imp, 'ext.prebid.storedrequest.id', bidRequest.params.srid); + return imp; + }, + request(buildRequest, imps, bidderRequest, context) { + return buildRequest(imps, bidderRequest, context); + }, +}); + +const isBidRequestValid = (bid:BidRequest): boolean => { + if (!bid.params.srid || typeof bid.params.srid !== 'string' || bid.params.srid === '') { + return false; + } + return true; +}; + +const buildRequests = ( + bidRequests: BidRequest[], + bidderRequest: ClientBidderRequest, +): AdapterRequest => { + const data:ORTBRequest = converter.toORTB({ bidRequests, bidderRequest }) + const adapterRequest:AdapterRequest = { + method: 'POST', + url: REQUEST_URL, + data, + options: { + endpointCompression: DEFAULT_GZIP_ENABLED + }, + } + return adapterRequest; +} + +export const spec:BidderSpec = { + code: BIDDER_CODE, + gvlid: GVLID, + supportedMediaTypes: [BANNER, VIDEO, NATIVE], + isBidRequestValid, + buildRequests, + interpretResponse, + getUserSyncs, +}; + +registerBidder(spec); diff --git a/modules/allowActivities.js b/modules/allowActivities.js index 6af7eb36a62..1a9322b3659 100644 --- a/modules/allowActivities.js +++ b/modules/allowActivities.js @@ -1,5 +1,5 @@ -import {config} from '../src/config.js'; -import {registerActivityControl} from '../src/activities/rules.js'; +import { config } from '../src/config.js'; +import { registerActivityControl } from '../src/activities/rules.js'; const CFG_NAME = 'allowActivities'; const RULE_NAME = `${CFG_NAME} config`; @@ -34,7 +34,7 @@ export function updateRulesFromConfig(registerRule) { handles.set(priority, registerRule(activity, RULE_NAME, function (params) { for (const rule of rulesByActivity.get(activity).get(priority)) { if (!rule.condition || rule.condition(cleanParams(params))) { - return {allow: rule.allow, reason: rule} + return { allow: rule.allow, reason: rule } } } }, priority)); @@ -44,7 +44,7 @@ export function updateRulesFromConfig(registerRule) { function setupDefaultRule(activity) { if (!defaultRuleHandles.has(activity)) { defaultRuleHandles.set(activity, registerRule(activity, RULE_NAME, function () { - return {allow: false, reason: 'activity denied by default'} + return { allow: false, reason: 'activity denied by default' } }, Number.POSITIVE_INFINITY)) } } diff --git a/modules/ampliffyBidAdapter.js b/modules/ampliffyBidAdapter.js index 9eb2410e0f7..504becb106d 100644 --- a/modules/ampliffyBidAdapter.js +++ b/modules/ampliffyBidAdapter.js @@ -1,5 +1,5 @@ -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {logError, logInfo, triggerPixel} from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { logError, logInfo, triggerPixel } from '../src/utils.js'; const BIDDER_CODE = 'ampliffy'; const DEFAULT_ENDPOINT = 'bidder.ampliffy.com'; @@ -347,9 +347,9 @@ function getSyncData(options, syncs) { if (syncs?.length) { for (const sync of syncs) { if (sync.type === 'syncImage' && options.pixelEnabled) { - ret.push({url: sync.url, type: 'image'}); + ret.push({ url: sync.url, type: 'image' }); } else if (sync.type === 'syncIframe' && options.iframeEnabled) { - ret.push({url: sync.url, type: 'iframe'}); + ret.push({ url: sync.url, type: 'iframe' }); } } } diff --git a/modules/amxBidAdapter.js b/modules/amxBidAdapter.js index ef89ecdd40f..78cd68c20a5 100644 --- a/modules/amxBidAdapter.js +++ b/modules/amxBidAdapter.js @@ -17,7 +17,7 @@ import { getStorageManager } from '../src/storageManager.js'; import { fetch } from '../src/ajax.js'; import { getGlobal } from '../src/prebidGlobal.js'; -import {getGlobalVarName} from '../src/buildOptions.js'; +import { getGlobalVarName } from '../src/buildOptions.js'; const BIDDER_CODE = 'amx'; const storage = getStorageManager({ bidderCode: BIDDER_CODE }); @@ -334,7 +334,8 @@ export const spec = { : { bidderRequestsCount: 0, bidderWinsCount: 0, - bidRequestsCount: 0 }; + bidRequestsCount: 0 + }; const payload = { a: generateUUID(), diff --git a/modules/amxIdSystem.js b/modules/amxIdSystem.js index 560ea3f0e3a..a4f85528426 100644 --- a/modules/amxIdSystem.js +++ b/modules/amxIdSystem.js @@ -5,16 +5,16 @@ * @module modules/amxIdSystem * @requires module:modules/userId */ -import {uspDataHandler} from '../src/adapterManager.js'; -import {ajaxBuilder} from '../src/ajax.js'; -import {submodule} from '../src/hook.js'; -import {getRefererInfo} from '../src/refererDetection.js'; -import {deepAccess, logError} from '../src/utils.js'; -import {getStorageManager} from '../src/storageManager.js'; -import {MODULE_TYPE_UID} from '../src/activities/modules.js'; -import {domainOverrideToRootDomain} from '../libraries/domainOverrideToRootDomain/index.js'; +import { uspDataHandler } from '../src/adapterManager.js'; +import { ajaxBuilder } from '../src/ajax.js'; +import { submodule } from '../src/hook.js'; +import { getRefererInfo } from '../src/refererDetection.js'; +import { deepAccess, logError } from '../src/utils.js'; +import { getStorageManager } from '../src/storageManager.js'; +import { MODULE_TYPE_UID } from '../src/activities/modules.js'; +import { domainOverrideToRootDomain } from '../libraries/domainOverrideToRootDomain/index.js'; -import {getGlobalVarName} from '../src/buildOptions.js'; +import { getGlobalVarName } from '../src/buildOptions.js'; const NAME = 'amxId'; const GVL_ID = 737; @@ -22,9 +22,9 @@ const ID_KEY = NAME; const version = '2.0'; const SYNC_URL = 'https://id.a-mx.com/sync/'; const AJAX_TIMEOUT = 300; -const AJAX_OPTIONS = {method: 'GET', withCredentials: true, contentType: 'text/plain'}; +const AJAX_OPTIONS = { method: 'GET', withCredentials: true, contentType: 'text/plain' }; -export const storage = getStorageManager({moduleName: NAME, moduleType: MODULE_TYPE_UID}); +export const storage = getStorageManager({ moduleName: NAME, moduleType: MODULE_TYPE_UID }); const AMUID_KEY = '__amuidpb'; const getBidAdapterID = () => storage.localStorageIsEnabled() ? storage.getDataFromLocalStorage(AMUID_KEY) : null; diff --git a/modules/anonymisedRtdProvider.js b/modules/anonymisedRtdProvider.js index 98cf81edb2a..7de70668b2f 100644 --- a/modules/anonymisedRtdProvider.js +++ b/modules/anonymisedRtdProvider.js @@ -5,11 +5,11 @@ * @module modules/anonymisedRtdProvider * @requires module:modules/realTimeData */ -import {getStorageManager} from '../src/storageManager.js'; -import {submodule} from '../src/hook.js'; -import {isPlainObject, mergeDeep, logMessage, logWarn, logError} from '../src/utils.js'; -import {MODULE_TYPE_RTD} from '../src/activities/modules.js'; -import {loadExternalScript} from '../src/adloader.js'; +import { getStorageManager } from '../src/storageManager.js'; +import { submodule } from '../src/hook.js'; +import { isPlainObject, mergeDeep, logMessage, logWarn, logError } from '../src/utils.js'; +import { MODULE_TYPE_RTD } from '../src/activities/modules.js'; +import { loadExternalScript } from '../src/adloader.js'; /** * @typedef {import('../modules/rtdModule/index.js').RtdSubmodule} RtdSubmodule */ @@ -60,7 +60,7 @@ export function createRtdProvider(moduleName) { logMessage(`${SUBMODULE_NAME}RtdProvider: Marketing Tag already loaded`); return; } - const tagConfig = config.params?.tagConfig ? {...config.params.tagConfig, idw_client_id: config.params.tagConfig.clientId} : {}; + const tagConfig = config.params?.tagConfig ? { ...config.params.tagConfig, idw_client_id: config.params.tagConfig.clientId } : {}; delete tagConfig.clientId; const tagUrl = config.params.tagUrl ? config.params.tagUrl : `${MARKETING_TAG_URL}?ref=prebid`; @@ -100,7 +100,7 @@ export function createRtdProvider(moduleName) { ext: { segtax: config.params.segtax }, - segment: segments.map(x => ({id: x})) + segment: segments.map(x => ({ id: x })) } logMessage(`${SUBMODULE_NAME}RtdProvider: user.data.segment: `, udSegment); diff --git a/modules/anyclipBidAdapter.js b/modules/anyclipBidAdapter.js index 8a5906ebc93..ce8cece011b 100644 --- a/modules/anyclipBidAdapter.js +++ b/modules/anyclipBidAdapter.js @@ -1,11 +1,11 @@ -import {BANNER, VIDEO} from '../src/mediaTypes.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; import { buildRequests, getUserSyncs, interpretResponse, } from '../libraries/xeUtils/bidderUtils.js'; -import {deepAccess, getBidIdParameter, isArray, logError} from '../src/utils.js'; +import { deepAccess, getBidIdParameter, isArray, logError } from '../src/utils.js'; const BIDDER_CODE = 'anyclip'; const ENDPOINT = 'https://prebid.anyclip.com'; @@ -45,7 +45,7 @@ export const spec = { floor: req.floor }, })) - return {...builtRequests, data: JSON.stringify(updatedRequests)} + return { ...builtRequests, data: JSON.stringify(updatedRequests) } }, interpretResponse, getUserSyncs diff --git a/modules/apacdexBidAdapter.js b/modules/apacdexBidAdapter.js index cd433ef2c81..e4d19055ce0 100644 --- a/modules/apacdexBidAdapter.js +++ b/modules/apacdexBidAdapter.js @@ -1,9 +1,9 @@ -import {getDNT} from '../libraries/dnt/index.js'; import { deepAccess, isPlainObject, isArray, replaceAuctionPrice, isFn, logError, deepClone } from '../src/utils.js'; import { config } from '../src/config.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; -import {hasPurpose1Consent} from '../src/utils/gdpr.js'; -import {parseDomain} from '../src/refererDetection.js'; +import { hasPurpose1Consent } from '../src/utils/gdpr.js'; +import { parseDomain } from '../src/refererDetection.js'; +import { getDNT } from '../libraries/dnt/index.js'; const BIDDER_CODE = 'apacdex'; const ENDPOINT = 'https://useast.quantumdex.io/auction/pbjs' const USERSYNC = 'https://sync.quantumdex.io/usersync/pbjs' diff --git a/modules/apesterBidAdapter.js b/modules/apesterBidAdapter.js new file mode 100644 index 00000000000..6d9cedb16f2 --- /dev/null +++ b/modules/apesterBidAdapter.js @@ -0,0 +1,49 @@ +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; +import { getStorageManager } from '../src/storageManager.js'; +import { + isBidRequestValid, + onBidWon, + createUserSyncGetter, + createBuildRequestsFn, + createInterpretResponseFn +} from '../libraries/vidazooUtils/bidderUtils.js'; + +const DEFAULT_SUB_DOMAIN = 'bidder'; +const BIDDER_CODE = 'apester'; +const BIDDER_VERSION = '1.0.0'; +const GVLID = 354; +export const storage = getStorageManager({ bidderCode: BIDDER_CODE }); + +export function createDomain(subDomain = DEFAULT_SUB_DOMAIN) { + return `https://${subDomain}.apester.com`; +} + +function createUniqueRequestData(hashUrl, bid) { + const { auctionId, transactionId } = bid; + return { + auctionId, + transactionId + }; +} + +const buildRequests = createBuildRequestsFn(createDomain, createUniqueRequestData, storage, BIDDER_CODE, BIDDER_VERSION, false); +const interpretResponse = createInterpretResponseFn(BIDDER_CODE, false); +const getUserSyncs = createUserSyncGetter({ + iframeSyncUrl: 'https://sync.apester.com/api/sync/iframe', + imageSyncUrl: 'https://sync.apester.com/api/sync/image' +}); + +export const spec = { + code: BIDDER_CODE, + version: BIDDER_VERSION, + supportedMediaTypes: [BANNER, VIDEO], + gvlid: GVLID, + isBidRequestValid, + buildRequests, + interpretResponse, + getUserSyncs, + onBidWon, +}; + +registerBidder(spec); diff --git a/modules/apesterBidAdapter.md b/modules/apesterBidAdapter.md new file mode 100644 index 00000000000..c43707d8a28 --- /dev/null +++ b/modules/apesterBidAdapter.md @@ -0,0 +1,36 @@ +# Overview + +**Module Name:** Apester Bidder Adapter + +**Module Type:** Bidder Adapter + +**Maintainer:** roni.katz@apester.com + +# Description + +Module that connects to Apester's demand sources. + +# Test Parameters + +```js +var adUnits = [ + { + code: 'test-ad', + sizes: [[300, 250]], + bids: [ + { + bidder: 'apester', + params: { + cId: '562524b21b1c1f08117667f9', + pId: '59ac17c192832d0016683fe3', + bidFloor: 0.0001, + ext: { + param1: 'loremipsum', + param2: 'dolorsitamet' + } + } + } + ] + } +]; +``` diff --git a/modules/appierAnalyticsAdapter.js b/modules/appierAnalyticsAdapter.js index 4773945d85c..c4d3d745542 100644 --- a/modules/appierAnalyticsAdapter.js +++ b/modules/appierAnalyticsAdapter.js @@ -1,9 +1,9 @@ -import {ajax} from '../src/ajax.js'; +import { ajax } from '../src/ajax.js'; import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import { EVENTS } from '../src/constants.js'; import adapterManager from '../src/adapterManager.js'; -import {getGlobal} from '../src/prebidGlobal.js'; -import {logError, logInfo, deepClone} from '../src/utils.js'; +import { getGlobal } from '../src/prebidGlobal.js'; +import { logError, logInfo, deepClone } from '../src/utils.js'; const analyticsType = 'endpoint'; @@ -43,7 +43,7 @@ export const parseAdUnitCode = function (bidResponse) { return bidResponse.adUnitCode.toLowerCase(); }; -export const appierAnalyticsAdapter = Object.assign(adapter({DEFAULT_SERVER, analyticsType}), { +export const appierAnalyticsAdapter = Object.assign(adapter({ DEFAULT_SERVER, analyticsType }), { cachedAuctions: {}, @@ -135,7 +135,7 @@ export const appierAnalyticsAdapter = Object.assign(adapter({DEFAULT_SERVER, ana message.adUnits[adUnitCode][bidder] = bidResponse; }, createBidMessage(auctionEndArgs, winningBids, timeoutBids) { - const {auctionId, timestamp, timeout, auctionEnd, adUnitCodes, bidsReceived, noBids} = auctionEndArgs; + const { auctionId, timestamp, timeout, auctionEnd, adUnitCodes, bidsReceived, noBids } = auctionEndArgs; const message = this.createCommonMessage(auctionId); message.auctionElapsed = (auctionEnd - timestamp); @@ -176,7 +176,7 @@ export const appierAnalyticsAdapter = Object.assign(adapter({DEFAULT_SERVER, ana const adUnitCode = parseAdUnitCode(bid); const bidder = parseBidderCode(bid); message.adUnits[adUnitCode] = message.adUnits[adUnitCode] || {}; - message.adUnits[adUnitCode][bidder] = {ad: bid.ad}; + message.adUnits[adUnitCode][bidder] = { ad: bid.ad }; }); return message; }, @@ -207,7 +207,7 @@ export const appierAnalyticsAdapter = Object.assign(adapter({DEFAULT_SERVER, ana handleBidWon(bidWonArgs) { this.sendEventMessage('imp', this.createImpressionMessage(bidWonArgs)); }, - track({eventType, args}) { + track({ eventType, args }) { if (analyticsOptions.sampled) { switch (eventType) { case BID_WON: diff --git a/modules/appnexusBidAdapter.js b/modules/appnexusBidAdapter.js index 0204c3e02b7..cad4bd0c948 100644 --- a/modules/appnexusBidAdapter.js +++ b/modules/appnexusBidAdapter.js @@ -18,24 +18,24 @@ import { logWarn, mergeDeep } from '../src/utils.js'; -import {Renderer} from '../src/Renderer.js'; -import {config} from '../src/config.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {ADPOD, BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js'; -import {INSTREAM, OUTSTREAM} from '../src/video.js'; -import {getStorageManager} from '../src/storageManager.js'; -import {bidderSettings} from '../src/bidderSettings.js'; -import {hasPurpose1Consent} from '../src/utils/gdpr.js'; -import {convertOrtbRequestToProprietaryNative} from '../src/native.js'; -import {APPNEXUS_CATEGORY_MAPPING} from '../libraries/categoryTranslationMapping/index.js'; +import { Renderer } from '../src/Renderer.js'; +import { config } from '../src/config.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; +import { INSTREAM, OUTSTREAM } from '../src/video.js'; +import { getStorageManager } from '../src/storageManager.js'; +import { bidderSettings } from '../src/bidderSettings.js'; +import { hasPurpose1Consent } from '../src/utils/gdpr.js'; +import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; import { convertKeywordStringToANMap, getANKewyordParamFromMaps, getANKeywordParam } from '../libraries/appnexusUtils/anKeywords.js'; -import {convertCamelToUnderscore, fill, appnexusAliases} from '../libraries/appnexusUtils/anUtils.js'; -import {convertTypes} from '../libraries/transformParamsUtils/convertTypes.js'; -import {chunk} from '../libraries/chunk/chunk.js'; +import { convertCamelToUnderscore, appnexusAliases } from '../libraries/appnexusUtils/anUtils.js'; +import { convertTypes } from '../libraries/transformParamsUtils/convertTypes.js'; +import { chunk } from '../libraries/chunk/chunk.js'; +import { getAdUnitElement } from '../src/utils/adUnits.js'; /** * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest @@ -99,7 +99,7 @@ const NATIVE_MAPPING = { const SOURCE = 'pbjs'; const MAX_IMPS_PER_REQUEST = 15; const GVLID = 32; -const storage = getStorageManager({bidderCode: BIDDER_CODE}); +const storage = getStorageManager({ bidderCode: BIDDER_CODE }); // ORTB2 device types according to the OpenRTB specification const ORTB2_DEVICE_TYPE = { MOBILE_TABLET: 1, @@ -166,7 +166,7 @@ export const spec = { const segs = []; userObjBid.params.user[param].forEach(val => { if (isNumber(val)) { - segs.push({'id': val}); + segs.push({ 'id': val }); } else if (isPlainObject(val)) { segs.push(val); } @@ -295,10 +295,6 @@ export const spec = { } } - if (config.getConfig('adpod.brandCategoryExclusion')) { - payload.brand_category_uniqueness = true; - } - if (debugObjParams.enabled) { payload.debug = debugObjParams; logInfo('AppNexus Debug Auction Settings:\n\n' + JSON.stringify(debugObjParams, null, 4)); @@ -350,24 +346,12 @@ export const spec = { payload.referrer_detection = refererinfo; } - if (FEATURES.VIDEO) { - const hasAdPodBid = ((bidRequests) || []).find(hasAdPod); - if (hasAdPodBid) { - bidRequests.filter(hasAdPod).forEach(adPodBid => { - const adPodTags = createAdPodRequest(tags, adPodBid); - // don't need the original adpod placement because it's in adPodTags - const nonPodTags = payload.tags.filter(tag => tag.uuid !== adPodBid.bidId); - payload.tags = [...nonPodTags, ...adPodTags]; - }); - } - } - if (bidRequests[0].userIdAsEids?.length > 0) { const eids = []; bidRequests[0].userIdAsEids.forEach(eid => { if (!eid || !eid.uids || eid.uids.length < 1) { return; } eid.uids.forEach(uid => { - const tmp = {'source': eid.source, 'id': uid.id}; + const tmp = { 'source': eid.source, 'id': uid.id }; if (eid.source === 'adserver.org') { tmp.rti_partner = 'TDID'; } else if (eid.source === 'uidapi.com') { @@ -599,12 +583,13 @@ function newBid(serverBid, rtbBid, bidderRequest) { complete: 0, nodes: [{ bsid: rtbBid.buyer_member_id.toString() - }]}; + }] + }; return dchain; } if (rtbBid.buyer_member_id) { - bid.meta = Object.assign({}, bid.meta, {dchain: setupDChain(rtbBid)}); + bid.meta = Object.assign({}, bid.meta, { dchain: setupDChain(rtbBid) }); } if (rtbBid.brand_id) { @@ -612,7 +597,7 @@ function newBid(serverBid, rtbBid, bidderRequest) { } if (FEATURES.VIDEO && rtbBid.rtb.video) { - // shared video properties used for all 3 contexts + // shared video properties used for both stream contexts Object.assign(bid, { width: rtbBid.rtb.video.player_width, height: rtbBid.rtb.video.player_height, @@ -622,17 +607,6 @@ function newBid(serverBid, rtbBid, bidderRequest) { const videoContext = deepAccess(bidRequest, 'mediaTypes.video.context'); switch (videoContext) { - case ADPOD: - const primaryCatId = (APPNEXUS_CATEGORY_MAPPING[rtbBid.brand_category_id]) ? APPNEXUS_CATEGORY_MAPPING[rtbBid.brand_category_id] : null; - bid.meta = Object.assign({}, bid.meta, { primaryCatId }); - const dealTier = rtbBid.deal_priority; - bid.video = { - context: ADPOD, - durationSeconds: Math.floor(rtbBid.rtb.video.duration_ms / 1000), - dealTier - }; - bid.vastUrl = rtbBid.rtb.video.asset_url; - break; case OUTSTREAM: bid.adResponse = serverBid; bid.adResponse.ad = bid.adResponse.ads[0]; @@ -934,11 +908,7 @@ function bidToTag(bid) { const videoMediaType = deepAccess(bid, `mediaTypes.${VIDEO}`); const context = deepAccess(bid, 'mediaTypes.video.context'); - if (videoMediaType && context === 'adpod') { - tag.hb_source = 7; - } else { - tag.hb_source = 1; - } + tag.hb_source = 1; if (bid.mediaType === VIDEO || videoMediaType) { tag.ad_types.push(VIDEO); } @@ -1154,14 +1124,6 @@ function hasDebug(bid) { return !!bid.debug } -function hasAdPod(bid) { - return ( - bid.mediaTypes && - bid.mediaTypes.video && - bid.mediaTypes.video.context === ADPOD - ); -} - function hasOmidSupport(bid) { let hasOmid = false; const bidderParams = bid.params; @@ -1175,54 +1137,6 @@ function hasOmidSupport(bid) { return hasOmid; } -/** - * Expand an adpod placement into a set of request objects according to the - * total adpod duration and the range of duration seconds. Sets minduration/ - * maxduration video property according to requireExactDuration configuration - */ -function createAdPodRequest(tags, adPodBid) { - const { durationRangeSec, requireExactDuration } = adPodBid.mediaTypes.video; - - const numberOfPlacements = getAdPodPlacementNumber(adPodBid.mediaTypes.video); - const maxDuration = Math.max(...durationRangeSec); - - const tagToDuplicate = tags.filter(tag => tag.uuid === adPodBid.bidId); - const request = fill(...tagToDuplicate, numberOfPlacements); - - if (requireExactDuration) { - const divider = Math.ceil(numberOfPlacements / durationRangeSec.length); - const chunked = chunk(request, divider); - - // each configured duration is set as min/maxduration for a subset of requests - durationRangeSec.forEach((duration, index) => { - chunked[index].forEach(tag => { - setVideoProperty(tag, 'minduration', duration); - setVideoProperty(tag, 'maxduration', duration); - }); - }); - } else { - // all maxdurations should be the same - request.forEach(tag => setVideoProperty(tag, 'maxduration', maxDuration)); - } - - return request; -} - -function getAdPodPlacementNumber(videoParams) { - const { adPodDurationSec, durationRangeSec, requireExactDuration } = videoParams; - const minAllowedDuration = Math.min(...durationRangeSec); - const numberOfPlacements = Math.floor(adPodDurationSec / minAllowedDuration); - - return requireExactDuration - ? Math.max(numberOfPlacements, durationRangeSec.length) - : numberOfPlacements; -} - -function setVideoProperty(tag, key, value) { - if (isEmpty(tag.video)) { tag.video = {}; } - tag.video[key] = value; -} - function getRtbBid(tag) { return tag && tag.ads && tag.ads.length && ((tag.ads) || []).find(ad => ad.rtb); } @@ -1265,11 +1179,10 @@ function buildNativeRequest(params) { /** * This function hides google div container for outstream bids to remove unwanted space on page. Appnexus renderer creates a new iframe outside of google iframe to render the outstream creative. - * @param {string} elementId element id */ -function hidedfpContainer(elementId) { +function hidedfpContainer(container) { try { - const el = document.getElementById(elementId).querySelectorAll("div[id^='google_ads']"); + const el = container.querySelectorAll("div[id^='google_ads']"); if (el[0]) { el[0].style.setProperty('display', 'none'); } @@ -1278,10 +1191,10 @@ function hidedfpContainer(elementId) { } } -function hideSASIframe(elementId) { +function hideSASIframe(container) { try { // find script tag with id 'sas_script'. This ensures it only works if you're using Smart Ad Server. - const el = document.getElementById(elementId).querySelectorAll("script[id^='sas_script']"); + const el = container.querySelectorAll("script[id^='sas_script']"); if (el[0].nextSibling && el[0].nextSibling.localName === 'iframe') { el[0].nextSibling.style.setProperty('display', 'none'); } @@ -1291,8 +1204,9 @@ function hideSASIframe(elementId) { } function outstreamRender(bid, doc) { - hidedfpContainer(bid.adUnitCode); - hideSASIframe(bid.adUnitCode); + const container = getAdUnitElement(bid); + hidedfpContainer(container); + hideSASIframe(container); // push to render queue because ANOutstreamVideo may not be loaded yet bid.renderer.push(() => { const win = doc?.defaultView || window; diff --git a/modules/apsBidAdapter.js b/modules/apsBidAdapter.js index 732737d32b7..d801ee05d6e 100644 --- a/modules/apsBidAdapter.js +++ b/modules/apsBidAdapter.js @@ -13,7 +13,7 @@ import { ortbConverter } from '../libraries/ortbConverter/converter.js'; */ const GVLID = 793; -export const ADAPTER_VERSION = '2.0.0'; +export const ADAPTER_VERSION = '2.1.0'; const BIDDER_CODE = 'aps'; const AAX_ENDPOINT = 'https://web.ads.aps.amazon-adsystem.com/e/pb/bid'; const DEFAULT_PREBID_CREATIVE_JS_URL = @@ -146,6 +146,14 @@ export const converter = ortbConverter({ }; request.cur = request.cur ?? ['USD']; + const agerange = bidderRequest?.ortb2?.regs?.ext?.agerange; + if (typeof agerange === 'number') { + request.regs = request.regs ?? {}; + request.regs.ext = request.regs?.ext ?? {}; + request.regs.ext.agerange = agerange; + } + + // Validate and process impressions - fail fast on structural issues if (!request.imp || !Array.isArray(request.imp)) { return request; } diff --git a/modules/apstreamBidAdapter.js b/modules/apstreamBidAdapter.js index f432c85388f..32c5f8242ec 100644 --- a/modules/apstreamBidAdapter.js +++ b/modules/apstreamBidAdapter.js @@ -1,17 +1,17 @@ -import {getDNT} from '../libraries/dnt/index.js'; import { generateUUID, deepAccess, createTrackPixelHtml } from '../src/utils.js'; import { getDevicePixelRatio } from '../libraries/devicePixelRatio/devicePixelRatio.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { config } from '../src/config.js'; import { getStorageManager } from '../src/storageManager.js'; import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; +import { getDNT } from '../libraries/dnt/index.js'; const CONSTANTS = { DSU_KEY: 'apr_dsu', BIDDER_CODE: 'apstream', GVLID: 394 }; -const storage = getStorageManager({bidderCode: CONSTANTS.BIDDER_CODE}); +const storage = getStorageManager({ bidderCode: CONSTANTS.BIDDER_CODE }); var dsuModule = (function() { 'use strict'; @@ -19,7 +19,7 @@ var dsuModule = (function() { var DSU_KEY = 'apr_dsu'; var DSU_VERSION_NUMBER = '1'; var SIGNATURE_SALT = 'YicAu6ZpNG'; - var DSU_CREATOR = {'USERREPORT': '1'}; + var DSU_CREATOR = { 'USERREPORT': '1' }; function stringToU8(str) { if (typeof TextEncoder === 'function') { diff --git a/modules/arcspanRtdProvider.js b/modules/arcspanRtdProvider.js index 451b521130d..c5c7621e369 100644 --- a/modules/arcspanRtdProvider.js +++ b/modules/arcspanRtdProvider.js @@ -1,6 +1,6 @@ import { submodule } from '../src/hook.js'; import { mergeDeep } from '../src/utils.js'; -import {loadExternalScript} from '../src/adloader.js'; +import { loadExternalScript } from '../src/adloader.js'; import { MODULE_TYPE_RTD } from '../src/activities/modules.js'; /** diff --git a/modules/asoBidAdapter.js b/modules/asoBidAdapter.js index e1cde5bfd3f..08449d99b9b 100644 --- a/modules/asoBidAdapter.js +++ b/modules/asoBidAdapter.js @@ -1,8 +1,8 @@ -import {deepAccess, deepSetValue} from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {tryAppendQueryString} from '../libraries/urlUtils/urlUtils.js'; -import {ortbConverter} from '../libraries/ortbConverter/converter.js'; -import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js'; +import { deepAccess, deepSetValue } from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { tryAppendQueryString } from '../libraries/urlUtils/urlUtils.js'; +import { ortbConverter } from '../libraries/ortbConverter/converter.js'; +import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; const BIDDER_CODE = 'aso'; const DEFAULT_SERVER_URL = 'https://srv.aso1.net'; @@ -16,11 +16,11 @@ export const spec = { code: BIDDER_CODE, supportedMediaTypes: [BANNER, VIDEO, NATIVE], aliases: [ - {code: 'bcmint'}, - {code: 'bidgency'}, - {code: 'kuantyx'}, - {code: 'cordless'}, - {code: 'adklip'} + { code: 'bcmint' }, + { code: 'bidgency' }, + { code: 'kuantyx' }, + { code: 'cordless' }, + { code: 'adklip' } ], isBidRequestValid: bid => { @@ -31,7 +31,7 @@ export const spec = { const requests = []; bidRequests.forEach(bid => { - const data = converter.toORTB({bidRequests: [bid], bidderRequest}); + const data = converter.toORTB({ bidRequests: [bid], bidderRequest }); requests.push({ method: 'POST', url: getEndpoint(bid), @@ -48,7 +48,7 @@ export const spec = { interpretResponse: (response, request) => { if (response.body) { - return converter.fromORTB({response: response.body, request: request.data}).bids; + return converter.fromORTB({ response: response.body, request: request.data }).bids; } return []; }, diff --git a/modules/asteriobidAnalyticsAdapter.js b/modules/asteriobidAnalyticsAdapter.js index e4f7ee2a767..43c0643e394 100644 --- a/modules/asteriobidAnalyticsAdapter.js +++ b/modules/asteriobidAnalyticsAdapter.js @@ -5,7 +5,7 @@ import adapterManager from '../src/adapterManager.js' import { getStorageManager } from '../src/storageManager.js' import { EVENTS } from '../src/constants.js' import { MODULE_TYPE_ANALYTICS } from '../src/activities/modules.js' -import {getRefererInfo} from '../src/refererDetection.js'; +import { getRefererInfo } from '../src/refererDetection.js'; import { collectUtmTagData, trimAdUnit, trimBid, trimBidderRequest } from '../libraries/asteriobidUtils/asteriobidUtils.js' /** @@ -229,9 +229,6 @@ function handleEvent(eventType, eventArgs) { case EVENTS.REQUEST_BIDS: { break } - case EVENTS.ADD_AD_UNITS: { - break - } case EVENTS.AD_RENDER_FAILED: { pmEvent.bid = eventArgs.bid pmEvent.message = eventArgs.message diff --git a/modules/astraoneBidAdapter.js b/modules/astraoneBidAdapter.js index 216257fb7bc..5a8a7c1fd15 100644 --- a/modules/astraoneBidAdapter.js +++ b/modules/astraoneBidAdapter.js @@ -71,7 +71,7 @@ function wrapAd(bid, bidData) { parentDocument.style.height = "100%"; parentDocument.style.width = "100%"; } - var _html = "${encodeURIComponent(JSON.stringify({...bid, content: bidData.content}))}"; + var _html = "${encodeURIComponent(JSON.stringify({ ...bid, content: bidData.content }))}"; window._ao_ssp.registerInImage(JSON.parse(decodeURIComponent(_html))); diff --git a/modules/atsAnalyticsAdapter.js b/modules/atsAnalyticsAdapter.js index dcb43eb5f22..94e5264479e 100644 --- a/modules/atsAnalyticsAdapter.js +++ b/modules/atsAnalyticsAdapter.js @@ -2,13 +2,13 @@ import { logError, logInfo } from '../src/utils.js'; import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import { EVENTS } from '../src/constants.js'; import adaptermanager from '../src/adapterManager.js'; -import {ajax} from '../src/ajax.js'; -import {getStorageManager} from '../src/storageManager.js'; -import {getGlobal} from '../src/prebidGlobal.js'; +import { ajax } from '../src/ajax.js'; +import { getStorageManager } from '../src/storageManager.js'; +import { getGlobal } from '../src/prebidGlobal.js'; -import {MODULE_TYPE_ANALYTICS} from '../src/activities/modules.js'; +import { MODULE_TYPE_ANALYTICS } from '../src/activities/modules.js'; const MODULE_CODE = 'atsAnalytics'; -export const storage = getStorageManager({moduleType: MODULE_TYPE_ANALYTICS, moduleName: MODULE_CODE}); +export const storage = getStorageManager({ moduleType: MODULE_TYPE_ANALYTICS, moduleName: MODULE_CODE }); /** * Analytics adapter for - https://liveramp.com @@ -275,12 +275,12 @@ export function parseBrowser() { function sendDataToAnalytic (events) { // send data to ats analytic endpoint try { - const dataToSend = {'Data': events}; + const dataToSend = { 'Data': events }; const strJSON = JSON.stringify(dataToSend); logInfo('ATS Analytics - tried to send analytics data!'); ajax(analyticsUrl, function () { logInfo('ATS Analytics - events sent successfully!'); - }, strJSON, {method: 'POST', contentType: 'application/json'}); + }, strJSON, { method: 'POST', contentType: 'application/json' }); } catch (err) { logError('ATS Analytics - request encounter an error: ', err); } @@ -306,7 +306,7 @@ function preflightRequest (events) { atsAnalyticsAdapter.setSamplingCookie(0); logInfo('ATS Analytics - Sampling Rate Request Error!'); } - }, undefined, {method: 'GET', crossOrigin: true}); + }, undefined, { method: 'GET', crossOrigin: true }); } const atsAnalyticsAdapter = Object.assign(adapter( @@ -314,7 +314,7 @@ const atsAnalyticsAdapter = Object.assign(adapter( analyticsType }), { - track({eventType, args}) { + track({ eventType, args }) { if (typeof args !== 'undefined') { atsAnalyticsAdapter.callHandler(eventType, args); } diff --git a/modules/automatadAnalyticsAdapter.js b/modules/automatadAnalyticsAdapter.js index e27061150c6..725dcacd7f2 100644 --- a/modules/automatadAnalyticsAdapter.js +++ b/modules/automatadAnalyticsAdapter.js @@ -14,7 +14,7 @@ import { getStorageManager } from '../src/storageManager.js' /** Prebid Event Handlers */ const ADAPTER_CODE = 'automatadAnalytics' -export const storage = getStorageManager({moduleType: MODULE_TYPE_ANALYTICS, moduleName: ADAPTER_CODE}) +export const storage = getStorageManager({ moduleType: MODULE_TYPE_ANALYTICS, moduleName: ADAPTER_CODE }) const trialCountMilsMapping = [1500, 3000, 5000, 10000]; var isLoggingEnabled; var queuePointer = 0; var retryCount = 0; var timer = null; var __atmtdAnalyticsQueue = []; var qBeingUsed; var qTraversalComplete; @@ -197,14 +197,14 @@ const initializeQueue = () => { // ANALYTICS ADAPTER -const baseAdapter = adapter({analyticsType: 'bundle'}); +const baseAdapter = adapter({ analyticsType: 'bundle' }); const atmtdAdapter = Object.assign({}, baseAdapter, { disableAnalytics() { baseAdapter.disableAnalytics.apply(this, arguments); }, - track({eventType, args}) { + track({ eventType, args }) { const shouldNotPushToQueue = !self.qBeingUsed switch (eventType) { case EVENTS.AUCTION_INIT: diff --git a/modules/automatadBidAdapter.js b/modules/automatadBidAdapter.js index ad5ca6e128f..bf9f10eb816 100644 --- a/modules/automatadBidAdapter.js +++ b/modules/automatadBidAdapter.js @@ -1,7 +1,7 @@ -import {logInfo} from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER} from '../src/mediaTypes.js'; -import {ajax} from '../src/ajax.js'; +import { logInfo } from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER } from '../src/mediaTypes.js'; +import { ajax } from '../src/ajax.js'; const BIDDER = 'automatad' @@ -114,7 +114,7 @@ export const spec = { }, onTimeout: function(timeoutData) { const timeoutUrl = ENDPOINT_URL + '/timeout' - spec.ajaxCall(timeoutUrl, null, JSON.stringify(timeoutData), {method: 'POST', withCredentials: true}) + spec.ajaxCall(timeoutUrl, null, JSON.stringify(timeoutData), { method: 'POST', withCredentials: true }) }, onBidWon: function(bid) { if (!bid.nurl) { return } @@ -136,7 +136,7 @@ export const spec = { /\$\{AUCTION_ID\}/, bid.auctionId ) - spec.ajaxCall(winUrl, null, null, {method: 'GET', withCredentials: true}) + spec.ajaxCall(winUrl, null, null, { method: 'GET', withCredentials: true }) return true }, diff --git a/modules/axonixBidAdapter.js b/modules/axonixBidAdapter.js index c0b3f334c40..5c51167b63e 100644 --- a/modules/axonixBidAdapter.js +++ b/modules/axonixBidAdapter.js @@ -1,10 +1,10 @@ -import {getDNT} from '../libraries/dnt/index.js'; -import {deepAccess, isArray, isEmpty, logError, replaceAuctionPrice, triggerPixel} from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER, VIDEO} from '../src/mediaTypes.js'; -import {config} from '../src/config.js'; -import {ajax} from '../src/ajax.js'; +import { deepAccess, isArray, isEmpty, logError, replaceAuctionPrice, triggerPixel } from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; +import { config } from '../src/config.js'; +import { ajax } from '../src/ajax.js'; import { getConnectionInfo } from '../libraries/connectionInfo/connectionUtils.js'; +import { getDNT } from '../libraries/dnt/index.js'; const BIDDER_CODE = 'axonix'; const BIDDER_VERSION = '1.0.2'; diff --git a/modules/beachfrontBidAdapter.js b/modules/beachfrontBidAdapter.js index 3f627fe39e0..f85c233e644 100644 --- a/modules/beachfrontBidAdapter.js +++ b/modules/beachfrontBidAdapter.js @@ -7,11 +7,12 @@ import { logWarn, formatQS } from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {Renderer} from '../src/Renderer.js'; -import {BANNER, VIDEO} from '../src/mediaTypes.js'; -import { getFirstSize, getOsVersion, getVideoSizes, getBannerSizes, isConnectedTV, getDoNotTrack, isMobile, isBannerBid, isVideoBid, getBannerBidFloor, getVideoBidFloor, getVideoTargetingParams, getTopWindowLocation } from '../libraries/advangUtils/index.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { Renderer } from '../src/Renderer.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; +import { getFirstSize, getOsVersion, getVideoSizes, getBannerSizes, isConnectedTV, isMobile, isBannerBid, isVideoBid, getBannerBidFloor, getVideoBidFloor, getVideoTargetingParams, getTopWindowLocation } from '../libraries/advangUtils/index.js'; import { getConnectionInfo } from '../libraries/connectionInfo/connectionUtils.js'; +import { getDNT } from '../libraries/dnt/index.js'; const ADAPTER_VERSION = '1.21'; const GVLID = 157; @@ -39,7 +40,7 @@ let appId = ''; export const spec = { code: 'beachfront', - supportedMediaTypes: [ VIDEO, BANNER ], + supportedMediaTypes: [VIDEO, BANNER], gvlid: GVLID, isBidRequestValid(bid) { if (isVideoBid(bid)) { @@ -305,7 +306,7 @@ function createVideoRequestData(bid, bidderRequest) { ua: navigator.userAgent, language: navigator.language, devicetype: isMobile() ? 1 : isConnectedTV() ? 3 : 2, - dnt: getDoNotTrack() ? 1 : 0, + dnt: getDNT() ? 1 : 0, js: 1, geo: {} }, @@ -371,7 +372,7 @@ function createBannerRequestData(bids, bidderRequest) { ua: navigator.userAgent, deviceOs: getOsVersion(), isMobile: isMobile() ? 1 : 0, - dnt: getDoNotTrack() ? 1 : 0, + dnt: getDNT() ? 1 : 0, adapterVersion: ADAPTER_VERSION, adapterName: ADAPTER_NAME }; diff --git a/modules/bedigitechBidAdapter.js b/modules/bedigitechBidAdapter.js index 0baeea7470f..10fa08a2628 100644 --- a/modules/bedigitechBidAdapter.js +++ b/modules/bedigitechBidAdapter.js @@ -1,6 +1,6 @@ -import {BANNER, NATIVE} from '../src/mediaTypes.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {_each, isArray} from '../src/utils.js'; +import { BANNER, NATIVE } from '../src/mediaTypes.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { _each, isArray } from '../src/utils.js'; const BEDIGITECH_CODE = 'bedigitech'; const BEDIGITECH_ENDPOINT = 'https://bid.bedigitech.com/bid/pub_bid.php'; @@ -41,7 +41,7 @@ export const spec = { buildRequests: (bidRequests) => { return bidRequests.map(bid => { const url = BEDIGITECH_ENDPOINT; - const data = {'pid': bid.params.placementId}; + const data = { 'pid': bid.params.placementId }; return { method: BEDIGITECH_REQUEST_METHOD, url, diff --git a/modules/beopBidAdapter.js b/modules/beopBidAdapter.js index 16a67a42432..61412439703 100644 --- a/modules/beopBidAdapter.js +++ b/modules/beopBidAdapter.js @@ -4,7 +4,8 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { getRefererInfo } from '../src/refererDetection.js'; import { buildUrl, - deepAccess, generateUUID, getBidIdParameter, + deepAccess, + getBidIdParameter, getValue, isArray, isPlainObject, @@ -23,11 +24,32 @@ import { getStorageManager } from '../src/storageManager.js'; const BIDDER_CODE = 'beop'; const ENDPOINT_URL = 'https://hb.collectiveaudience.co/bid'; -const COOKIE_NAME = 'beopid'; +const COOKIE_NAME = 'caudid'; +const COOKIE_DATE_NAME = 'caudid_date'; const TCF_VENDOR_ID = 666; +const COOKIE_MAX_AGE_MS = 86400 * 365 * 1000; // 1 year -const validIdRegExp = /^[0-9a-fA-F]{24}$/ -const storage = getStorageManager({bidderCode: BIDDER_CODE}); +const validIdRegExp = /^[0-9a-fA-F]{24}$/; + +/** + * Generates a 24-char hex string compatible with MongoDB ObjectId semantics + * (4-byte timestamp + 16 random hex chars). Used for first-party user id (caudid). + * Timestamp is padded to 8 hex chars so that a client clock in the past (or mocked Date) + * cannot produce a shorter string that would fail the 24-char validation on later requests. + * @see https://www.mongodb.com/docs/manual/reference/method/objectid/ + * @return {string} + */ +function generateObjectId() { + const timestamp = (Math.floor(Date.now() / 1000)).toString(16).padStart(8, '0'); + const randomPart = Array.from({ length: 16 }, () => + (Math.floor(Math.random() * 16)).toString(16) + ).join(''); + return (timestamp + randomPart).toLowerCase(); +} +const storage = getStorageManager({ bidderCode: BIDDER_CODE }); + +/** Exported for unit tests (caudid / caudid_date cookie behavior). */ +export const __storage = storage; export const spec = { code: BIDDER_CODE, @@ -68,17 +90,20 @@ export const spec = { const kwdsFromRequest = firstSlot.kwds; const keywords = getAllOrtbKeywords(bidderRequest.ortb2, kwdsFromRequest); - let beopid = ''; - if (storage.cookiesAreEnabled) { - beopid = storage.getCookie(COOKIE_NAME, undefined); - if (!beopid) { - beopid = generateUUID(); + let caudid = ''; + if (storage.cookiesAreEnabled()) { + caudid = storage.getCookie(COOKIE_NAME, undefined); + if (!caudid || !validIdRegExp.test(caudid)) { + caudid = generateObjectId(); const expirationDate = new Date(); - expirationDate.setTime(expirationDate.getTime() + 86400 * 183 * 1000); - storage.setCookie(COOKIE_NAME, beopid, expirationDate.toUTCString()); + expirationDate.setTime(expirationDate.getTime() + COOKIE_MAX_AGE_MS); + storage.setCookie(COOKIE_NAME, caudid, expirationDate.toUTCString()); + const dateValue = String(Date.now()); + storage.setCookie(COOKIE_DATE_NAME, dateValue, expirationDate.toUTCString()); } } else { storage.setCookie(COOKIE_NAME, '', 0); + storage.setCookie(COOKIE_DATE_NAME, '', 0); } const payloadObject = { @@ -91,7 +116,7 @@ export const spec = { lang: (window.navigator.language || window.navigator.languages[0]), kwds: keywords, dbg: false, - fg: beopid, + fg: caudid, slts: slots, is_amp: deepAccess(bidderRequest, 'referrerInfo.isAmp'), gdpr_applies: gdpr ? gdpr.gdprApplies : false, @@ -192,12 +217,46 @@ function buildTrackingParams(data, info, value) { }; } +function normalizeAdUnitCode(adUnitCode) { + if (!adUnitCode || typeof adUnitCode !== 'string') return undefined; + + // Only normalize GPT auto-generated adUnitCodes (div-gpt-ad-*) + // For non-GPT codes, return original string unchanged to preserve case + if (!/^div-gpt-ad[-_]/i.test(adUnitCode)) { + return adUnitCode; + } + + // GPT handling: strip prefix and random suffix + let slot = adUnitCode; + slot = slot.replace(/^div-gpt-ad[-_]?/i, ''); + + /** + * Remove only long numeric suffixes (likely auto-generated IDs). + * Preserve short numeric suffixes as they may be meaningful slot indices. + * + * Examples removed: + * div-gpt-ad-article_top_123456 → article_top + * div-gpt-ad-sidebar-1678459238475 → sidebar + * + * Examples preserved: + * div-gpt-ad-topbanner-1 → topbanner-1 + * div-gpt-ad-topbanner-2 → topbanner-2 + */ + slot = slot.replace(/([_-])\d{6,}$/, ''); + + slot = slot.toLowerCase().trim(); + + if (slot.length < 3) return undefined; + + return slot; +} + function beOpRequestSlotsMaker(bid, bidderRequest) { const bannerSizes = deepAccess(bid, 'mediaTypes.banner.sizes'); const publisherCurrency = getCurrencyFromBidderRequest(bidderRequest) || getValue(bid.params, 'currency') || 'EUR'; let floor; if (typeof bid.getFloor === 'function') { - const floorInfo = bid.getFloor({currency: publisherCurrency, mediaType: 'banner', size: [1, 1]}); + const floorInfo = bid.getFloor({ currency: publisherCurrency, mediaType: 'banner', size: [1, 1] }); if (isPlainObject(floorInfo) && floorInfo.currency === publisherCurrency && !isNaN(parseFloat(floorInfo.floor))) { floor = parseFloat(floorInfo.floor); } @@ -211,7 +270,11 @@ function beOpRequestSlotsMaker(bid, bidderRequest) { nptnid: getValue(bid.params, 'networkPartnerId'), bid: getBidIdParameter('bidId', bid), brid: getBidIdParameter('bidderRequestId', bid), - name: getBidIdParameter('adUnitCode', bid), + name: deepAccess(bid, 'ortb2Imp.ext.gpid') || + deepAccess(bid, 'ortb2Imp.ext.data.adslot') || + deepAccess(bid, 'ortb2Imp.ext.data.adserver.adslot') || + bid.ortb2Imp?.tagid || + normalizeAdUnitCode(bid.adUnitCode), tid: bid.ortb2Imp?.ext?.tid || '', brc: getBidIdParameter('bidRequestsCount', bid), bdrc: getBidIdParameter('bidderRequestCount', bid), diff --git a/modules/betweenBidAdapter.js b/modules/betweenBidAdapter.js index d195f045b58..6bd1a413c18 100644 --- a/modules/betweenBidAdapter.js +++ b/modules/betweenBidAdapter.js @@ -1,7 +1,7 @@ -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {parseSizesInput} from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { parseSizesInput } from '../src/utils.js'; -import {getAdUnitSizes} from '../libraries/sizeUtils/sizeUtils.js'; +import { getAdUnitSizes } from '../libraries/sizeUtils/sizeUtils.js'; /** * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest @@ -104,7 +104,7 @@ export const spec = { } } - requests.push({data: params}); + requests.push({ data: params }); }) return { method: 'POST', diff --git a/modules/bidResponseFilter/index.js b/modules/bidResponseFilter/index.js index 64026958bc6..8da669dd8f8 100644 --- a/modules/bidResponseFilter/index.js +++ b/modules/bidResponseFilter/index.js @@ -25,29 +25,33 @@ function init() { export function reset() { enabled = false; - getHook('addBidResponse').getHooks({hook: addBidResponseHook}).remove(); + getHook('addBidResponse').getHooks({ hook: addBidResponseHook }).remove(); } export function addBidResponseHook(next, adUnitCode, bid, reject, index = auctionManager.index) { - const {bcat = [], badv = []} = index.getOrtb2(bid) || {}; + const { bcat = [], badv = [], cattax = 1 } = index.getOrtb2(bid) || {}; const bidRequest = index.getBidRequest(bid); const battr = bidRequest?.ortb2Imp[bid.mediaType]?.battr || index.getAdUnit(bid)?.ortb2Imp[bid.mediaType]?.battr || []; - const catConfig = {enforce: true, blockUnknown: true, ...(moduleConfig?.cat || {})}; - const advConfig = {enforce: true, blockUnknown: true, ...(moduleConfig?.adv || {})}; - const attrConfig = {enforce: true, blockUnknown: true, ...(moduleConfig?.attr || {})}; - const mediaTypesConfig = {enforce: true, blockUnknown: true, ...(moduleConfig?.mediaTypes || {})}; + const catConfig = { enforce: true, blockUnknown: true, ...(moduleConfig?.cat || {}) }; + const advConfig = { enforce: true, blockUnknown: true, ...(moduleConfig?.adv || {}) }; + const attrConfig = { enforce: true, blockUnknown: true, ...(moduleConfig?.attr || {}) }; + const mediaTypesConfig = { enforce: true, blockUnknown: true, ...(moduleConfig?.mediaTypes || {}) }; const { primaryCatId, secondaryCatIds = [], advertiserDomains = [], attr: metaAttr, mediaType: metaMediaType, + cattax: metaCattax = 1, } = bid.meta || {}; // checking if bid fulfills ortb2 fields rules - if ((catConfig.enforce && bcat.some(category => [primaryCatId, ...secondaryCatIds].includes(category))) || - (catConfig.blockUnknown && !primaryCatId)) { + const normalizedMetaCattax = Number(metaCattax); + const normalizedRequestCattax = Number(cattax); + const isCattaxMatch = normalizedMetaCattax === normalizedRequestCattax; + if ((catConfig.enforce && isCattaxMatch && bcat.some(category => [primaryCatId, ...secondaryCatIds].includes(category))) || + (catConfig.blockUnknown && (!isCattaxMatch || !primaryCatId))) { reject(BID_CATEGORY_REJECTION_REASON); } else if ((advConfig.enforce && badv.some(domain => advertiserDomains.includes(domain))) || (advConfig.blockUnknown && !advertiserDomains.length)) { diff --git a/modules/bidViewability.js b/modules/bidViewability.js index f1acc6096cc..191df92f8f6 100644 --- a/modules/bidViewability.js +++ b/modules/bidViewability.js @@ -2,83 +2,35 @@ // GPT API is used to find when a bid is viewable, https://developers.google.com/publisher-tag/reference#googletag.events.impressionviewableevent // Does not work with other than GPT integration -import {config} from '../src/config.js'; -import * as events from '../src/events.js'; -import {EVENTS} from '../src/constants.js'; -import {isFn, logWarn, triggerPixel} from '../src/utils.js'; -import {getGlobal} from '../src/prebidGlobal.js'; -import adapterManager, {gppDataHandler, uspDataHandler} from '../src/adapterManager.js'; -import {gdprParams} from '../libraries/dfpUtils/dfpUtils.js'; +import { config } from '../src/config.js'; +import { isAdUnitCodeMatchingSlot, logWarn } from '../src/utils.js'; +import { getGlobal } from '../src/prebidGlobal.js'; +import { triggerBidViewable } from '../libraries/bidViewabilityPixels/index.js'; const MODULE_NAME = 'bidViewability'; const CONFIG_ENABLED = 'enabled'; -const CONFIG_FIRE_PIXELS = 'firePixels'; -const CONFIG_CUSTOM_MATCH = 'customMatchFunction'; -const BID_VURL_ARRAY = 'vurls'; const GPT_IMPRESSION_VIEWABLE_EVENT = 'impressionViewable'; -export const isBidAdUnitCodeMatchingSlot = (bid, slot) => { - return (slot.getAdUnitPath() === bid.adUnitCode || slot.getSlotElementId() === bid.adUnitCode); -} - -export const getMatchingWinningBidForGPTSlot = (globalModuleConfig, slot) => { +export const getMatchingWinningBidForGPTSlot = (slot) => { + const match = isAdUnitCodeMatchingSlot(slot); return getGlobal().getAllWinningBids().find( // supports custom match function from config - bid => isFn(globalModuleConfig[CONFIG_CUSTOM_MATCH]) - ? globalModuleConfig[CONFIG_CUSTOM_MATCH](bid, slot) - : isBidAdUnitCodeMatchingSlot(bid, slot) + ({ adUnitCode }) => match(adUnitCode) ) || null; }; -export const fireViewabilityPixels = (globalModuleConfig, bid) => { - if (globalModuleConfig[CONFIG_FIRE_PIXELS] === true && bid.hasOwnProperty(BID_VURL_ARRAY)) { - const queryParams = gdprParams(); - - const uspConsent = uspDataHandler.getConsentData(); - if (uspConsent) { queryParams.us_privacy = uspConsent; } - - const gppConsent = gppDataHandler.getConsentData(); - if (gppConsent) { - // TODO - need to know what to set here for queryParams... - } - - bid[BID_VURL_ARRAY].forEach(url => { - // add '?' if not present in URL - if (Object.keys(queryParams).length > 0 && url.indexOf('?') === -1) { - url += '?'; - } - // append all query params, `&key=urlEncoded(value)` - url += Object.keys(queryParams).reduce((prev, key) => { - prev += `&${key}=${encodeURIComponent(queryParams[key])}`; - return prev; - }, ''); - triggerPixel(url) - }); - } -}; - export const logWinningBidNotFound = (slot) => { logWarn(`bid details could not be found for ${slot.getSlotElementId()}, probable reasons: a non-prebid bid is served OR check the prebid.AdUnit.code to GPT.AdSlot relation.`); }; export const impressionViewableHandler = (globalModuleConfig, event) => { const slot = event.slot; - const respectiveBid = getMatchingWinningBidForGPTSlot(globalModuleConfig, slot); + const respectiveBid = getMatchingWinningBidForGPTSlot(slot); if (respectiveBid === null) { logWinningBidNotFound(slot); } else { - // if config is enabled AND VURL array is present then execute each pixel - fireViewabilityPixels(globalModuleConfig, respectiveBid); - // trigger respective bidder's onBidViewable handler - adapterManager.callBidViewableBidder(respectiveBid.adapterCode || respectiveBid.bidder, respectiveBid); - - if (respectiveBid.deferBilling) { - adapterManager.triggerBilling(respectiveBid); - } - - // emit the BID_VIEWABLE event with bid details, this event can be consumed by bidders and analytics pixels - events.emit(EVENTS.BID_VIEWABLE, respectiveBid); + triggerBidViewable(respectiveBid); } }; @@ -90,7 +42,6 @@ const handleSetConfig = (config) => { // do nothing if module-config.enabled is not set to true // this way we are adding a way for bidders to know (using pbjs.getConfig('bidViewability').enabled === true) whether this module is added in build and is enabled const impressionViewableHandlerWrapper = (event) => { - window.googletag.pubads().removeEventListener(GPT_IMPRESSION_VIEWABLE_EVENT, impressionViewableHandlerWrapper); impressionViewableHandler(globalModuleConfig, event); }; @@ -102,6 +53,7 @@ const handleSetConfig = (config) => { } // add the GPT event listener window.googletag.cmd.push(() => { + window.googletag.pubads().removeEventListener(GPT_IMPRESSION_VIEWABLE_EVENT, impressionViewableHandlerWrapper); window.googletag.pubads().addEventListener(GPT_IMPRESSION_VIEWABLE_EVENT, impressionViewableHandlerWrapper); }); } diff --git a/modules/bidViewability.md b/modules/bidViewability.md index 922a4a9def4..24f7dcd019e 100644 --- a/modules/bidViewability.md +++ b/modules/bidViewability.md @@ -12,7 +12,7 @@ Maintainer: harshad.mane@pubmatic.com - GPT API is used to find when a bid is viewable, https://developers.google.com/publisher-tag/reference#googletag.events.impressionviewableevent . This event is fired when an impression becomes viewable, according to the Active View criteria. Refer: https://support.google.com/admanager/answer/4524488 - This module does not work with any adserver's other than GAM with GPT integration -- Logic used to find a matching pbjs-bid for a GPT slot is ``` (slot.getAdUnitPath() === bid.adUnitCode || slot.getSlotElementId() === bid.adUnitCode) ``` this logic can be changed by using param ```customMatchFunction``` +- Logic used to find a matching pbjs-bid for a GPT slot is ``` (slot.getAdUnitPath() === bid.adUnitCode || slot.getSlotElementId() === bid.adUnitCode) ``` this logic can be changed by using config param ```customGptSlotMatching``` - When a rendered PBJS bid is viewable the module will trigger a BID_VIEWABLE event, which can be consumed by bidders and analytics adapters - If the viewable bid contains a ```vurls``` param containing URL's and the Bid Viewability module is configured with ``` firePixels: true ``` then the URLs mentioned in bid.vurls will be called. Please note that GDPR and USP related parameters will be added to the given URLs - This module is also compatible with Prebid core's billing deferral logic, this means that bids linked to an ad unit marked with `deferBilling: true` will trigger a bid adapter's `onBidBillable` function (if present) indicating an ad slot was viewed and also billing ready (if it were deferred). @@ -20,7 +20,6 @@ Refer: https://support.google.com/admanager/answer/4524488 # Params - enabled [required] [type: boolean, default: false], when set to true, the module will emit BID_VIEWABLE when applicable - firePixels [optional] [type: boolean], when set to true, will fire the urls mentioned in bid.vurls which should be array of urls -- customMatchFunction [optional] [type: function(bid, slot)], when passed this function will be used to `find` the matching winning bid for the GPT slot. Default value is ` (bid, slot) => (slot.getAdUnitPath() === bid.adUnitCode || slot.getSlotElementId() === bid.adUnitCode) ` # Example of consuming BID_VIEWABLE event ``` diff --git a/modules/bidViewabilityIO.js b/modules/bidViewabilityIO.js index 195b551c85b..34dc03c168f 100644 --- a/modules/bidViewabilityIO.js +++ b/modules/bidViewabilityIO.js @@ -1,7 +1,9 @@ import { logMessage } from '../src/utils.js'; import { config } from '../src/config.js'; import * as events from '../src/events.js'; -import {EVENTS} from '../src/constants.js'; +import { EVENTS } from '../src/constants.js'; +import { triggerBidViewable } from '../libraries/bidViewabilityPixels/index.js'; +import { getAdUnitElement } from '../src/utils/adUnits.js'; const MODULE_NAME = 'bidViewabilityIO'; const CONFIG_ENABLED = 'enabled'; @@ -42,7 +44,7 @@ export const getViewableOptions = (bid) => { export const markViewed = (bid, entry, observer) => { return () => { observer.unobserve(entry.target); - events.emit(EVENTS.BID_VIEWABLE, bid); + triggerBidViewable(bid); _logMessage(`id: ${entry.target.getAttribute('id')} code: ${bid.adUnitCode} was viewed`); } } @@ -77,10 +79,10 @@ export const init = () => { if (conf[MODULE_NAME][CONFIG_ENABLED] && CLIENT_SUPPORTS_IO) { // if the module is enabled and the browser supports Intersection Observer, // then listen to AD_RENDER_SUCCEEDED to setup IO's for supported mediaTypes - events.on(EVENTS.AD_RENDER_SUCCEEDED, ({doc, bid, id}) => { + events.on(EVENTS.AD_RENDER_SUCCEEDED, ({ doc, bid, id }) => { if (isSupportedMediaType(bid)) { const viewable = new IntersectionObserver(viewCallbackFactory(bid), getViewableOptions(bid)); - const element = document.getElementById(bid.adUnitCode); + const element = getAdUnitElement(bid); viewable.observe(element); } }); diff --git a/modules/biddoBidAdapter.js b/modules/biddoBidAdapter.js index 6bfa0ac6ef8..fc9786a0b21 100644 --- a/modules/biddoBidAdapter.js +++ b/modules/biddoBidAdapter.js @@ -1,5 +1,5 @@ -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER} from '../src/mediaTypes.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER } from '../src/mediaTypes.js'; import { buildBannerRequests, interpretBannerResponse } from '../libraries/biddoInvamiaUtils/index.js'; /** diff --git a/modules/bidglassBidAdapter.js b/modules/bidglassBidAdapter.js index 75999a8123e..2461145e6b3 100644 --- a/modules/bidglassBidAdapter.js +++ b/modules/bidglassBidAdapter.js @@ -1,5 +1,5 @@ -import {_each, isArray, deepClone, getUniqueIdentifierStr, getBidIdParameter} from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; +import { _each, isArray, deepClone, getUniqueIdentifierStr, getBidIdParameter } from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; /** * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest diff --git a/modules/bidmaticBidAdapter.js b/modules/bidmaticBidAdapter.js index 4265b48428f..35d6e9b6078 100644 --- a/modules/bidmaticBidAdapter.js +++ b/modules/bidmaticBidAdapter.js @@ -4,7 +4,6 @@ import { cleanObj, deepAccess, flatten, - getWinDimensions, isArray, isNumber, logWarn, @@ -13,7 +12,7 @@ import { import { config } from '../src/config.js'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; import { chunk } from '../libraries/chunk/chunk.js'; -import { getBoundingClientRect } from '../libraries/boundingClientRect/boundingClientRect.js'; +import { getPlacementPositionUtils } from "../libraries/placementPositionInfo/placementPositionInfo.js"; /** * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid @@ -21,10 +20,13 @@ import { getBoundingClientRect } from '../libraries/boundingClientRect/boundingC * @typedef {import('../src/adapters/bidderFactory.js').BidderSpec} BidderSpec */ +const ADAPTER_VERSION = 'v1.0.0'; const URL = 'https://adapter.bidmatic.io/bdm/auction'; const BIDDER_CODE = 'bidmatic'; const SYNCS_DONE = new Set(); +const { getPlacementEnv, getPlacementInfo } = getPlacementPositionUtils() + /** @type {BidderSpec} */ export const spec = { code: BIDDER_CODE, @@ -144,6 +146,7 @@ export function parseResponseBody(serverResponse, adapterRequest) { export function remapBidRequest(bidRequests, adapterRequest) { const bidRequestBody = { + AdapterVersion: ADAPTER_VERSION, Domain: deepAccess(adapterRequest, 'refererInfo.page'), ...getPlacementEnv() }; @@ -237,50 +240,4 @@ export function createBid(bidResponse) { }; } -function getPlacementInfo(bidReq) { - const placementElementNode = document.getElementById(bidReq.adUnitCode); - try { - return cleanObj({ - AuctionsCount: bidReq.auctionsCount, - DistanceToView: getViewableDistance(placementElementNode) - }); - } catch (e) { - logWarn('Error while getting placement info', e); - return {}; - } -} - -/** - * @param element - */ -function getViewableDistance(element) { - if (!element) return 0; - const elementRect = getBoundingClientRect(element); - - if (!elementRect) { - return 0; - } - - const elementMiddle = elementRect.top + (elementRect.height / 2); - const viewportHeight = getWinDimensions().innerHeight - if (elementMiddle > window.scrollY + viewportHeight) { - // element is below the viewport - return Math.round(elementMiddle - (window.scrollY + viewportHeight)); - } - // element is above the viewport -> negative value - return Math.round(elementMiddle); -} - -function getPageHeight() { - return document.documentElement.scrollHeight || document.body.scrollHeight; -} - -function getPlacementEnv() { - return cleanObj({ - TimeFromNavigation: Math.floor(performance.now()), - TabActive: document.visibilityState === 'visible', - PageHeight: getPageHeight() - }) -} - registerBidder(spec); diff --git a/modules/bidtheatreBidAdapter.js b/modules/bidtheatreBidAdapter.js index a3e1a33c3b1..0216db6fbe9 100644 --- a/modules/bidtheatreBidAdapter.js +++ b/modules/bidtheatreBidAdapter.js @@ -11,7 +11,7 @@ const METHOD = 'POST'; const SUPPORTED_MEDIA_TYPES = [BANNER, VIDEO]; export const DEFAULT_CURRENCY = 'USD'; const BIDTHEATRE_COOKIE_NAME = '__kuid'; -const storage = getStorageManager({bidderCode: BIDDER_CODE}); +const storage = getStorageManager({ bidderCode: BIDDER_CODE }); const converter = ortbConverter({ context: { @@ -68,7 +68,7 @@ export const spec = { return syncs; }, buildRequests(bidRequests, bidderRequest) { - const data = converter.toORTB({bidRequests, bidderRequest}); + const data = converter.toORTB({ bidRequests, bidderRequest }); const cookieValue = storage.getCookie(BIDTHEATRE_COOKIE_NAME); if (cookieValue) { @@ -104,7 +104,7 @@ export const spec = { }); const macroReplacedResponseBody = { ...response.body, seatbid: macroReplacedSeatbid }; - const bids = converter.fromORTB({response: macroReplacedResponseBody, request: request.data}).bids; + const bids = converter.fromORTB({ response: macroReplacedResponseBody, request: request.data }).bids; return bids; }, onTimeout: function(timeoutData) {}, diff --git a/modules/big-richmediaBidAdapter.js b/modules/big-richmediaBidAdapter.js index 858dad2ffde..654a4b92eb1 100644 --- a/modules/big-richmediaBidAdapter.js +++ b/modules/big-richmediaBidAdapter.js @@ -1,7 +1,7 @@ -import {BANNER, VIDEO} from '../src/mediaTypes.js'; -import {config} from '../src/config.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {spec as baseAdapter} from './appnexusBidAdapter.js'; // eslint-disable-line prebid/validate-imports +import { BANNER, VIDEO } from '../src/mediaTypes.js'; +import { config } from '../src/config.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { spec as baseAdapter } from './appnexusBidAdapter.js'; // eslint-disable-line prebid/validate-imports /** * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest @@ -16,7 +16,7 @@ export const spec = { version: '1.5.1', code: BIDDER_CODE, gvlid: baseAdapter.GVLID, // use base adapter gvlid - supportedMediaTypes: [ BANNER, VIDEO ], + supportedMediaTypes: [BANNER, VIDEO], /** * Determines whether or not the given bid request is valid. diff --git a/modules/bitmediaBidAdapter.js b/modules/bitmediaBidAdapter.js index 7825c714f46..c3e4dcf0caf 100644 --- a/modules/bitmediaBidAdapter.js +++ b/modules/bitmediaBidAdapter.js @@ -1,5 +1,5 @@ -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {ortbConverter} from '../libraries/ortbConverter/converter.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { ortbConverter } from '../libraries/ortbConverter/converter.js'; import { generateUUID, isEmpty, @@ -9,8 +9,8 @@ import { logInfo, triggerPixel } from '../src/utils.js'; -import {BANNER} from '../src/mediaTypes.js'; -import {getStorageManager} from '../src/storageManager.js'; +import { BANNER } from '../src/mediaTypes.js'; +import { getStorageManager } from '../src/storageManager.js'; const BIDDER_CODE = 'bitmedia'; export const ENDPOINT_URL = 'https://cdn.bmcdn7.com/prebid/'; @@ -30,7 +30,7 @@ const ALLOWED_CURRENCIES = [ const DEFAULT_NET_REVENUE = true; const PREBID_VERSION = '$prebid.version$'; const ADAPTER_VERSION = '1.0'; -export const STORAGE = getStorageManager({bidderCode: BIDDER_CODE}); +export const STORAGE = getStorageManager({ bidderCode: BIDDER_CODE }); const USER_FINGERPRINT_KEY = 'bitmedia_fid'; const _handleOnBidWon = (endpoint) => { @@ -104,7 +104,7 @@ const CONVERTER = ortbConverter({ }); logInfo(BIDDER_CODE, 'Result imp objects for bidRequest', imps); // Should hasOwnProperty id. - return {id: bidRequest.bidId, imps}; + return { id: bidRequest.bidId, imps }; }, request(buildRequest, imps, bidderRequest, context) { @@ -174,8 +174,8 @@ const CONVERTER = ortbConverter({ const isBidRequestValid = (bid) => { logInfo(BIDDER_CODE, 'Validating bid request', bid); - const {banner} = bid.mediaTypes || {}; - const {adUnitID, currency} = bid.params || {}; + const { banner } = bid.mediaTypes || {}; + const { adUnitID, currency } = bid.params || {}; if (!banner || !Array.isArray(banner.sizes)) { logError(BIDDER_CODE, 'Invalid bid: missing or malformed banner sizes', banner); @@ -204,7 +204,7 @@ const isBidRequestValid = (bid) => { }; const buildRequests = (validBidRequests = [], bidderRequest = {}) => { - logInfo(BIDDER_CODE, 'Building OpenRTB request', {validBidRequests, bidderRequest}); + logInfo(BIDDER_CODE, 'Building OpenRTB request', { validBidRequests, bidderRequest }); const requests = validBidRequests.map(bidRequest => { const data = CONVERTER.toORTB({ bidRequests: [bidRequest], @@ -230,7 +230,7 @@ const buildRequests = (validBidRequests = [], bidderRequest = {}) => { }; const interpretResponse = (serverResponse, bidRequest) => { - logInfo(BIDDER_CODE, 'Interpreting server response', {serverResponse, bidRequest}); + logInfo(BIDDER_CODE, 'Interpreting server response', { serverResponse, bidRequest }); if (isEmpty(serverResponse.body)) { logInfo(BIDDER_CODE, 'Empty response'); @@ -249,7 +249,7 @@ const interpretResponse = (serverResponse, bidRequest) => { const onBidWon = (bid) => { const cpm = bid.adserverTargeting?.hb_pb || ''; - logInfo(BIDDER_CODE, `-----Bid won-----`, {bid, cpm: cpm}); + logInfo(BIDDER_CODE, `-----Bid won-----`, { bid, cpm: cpm }); _handleOnBidWon(bid.nurl); } diff --git a/modules/blueconicRtdProvider.js b/modules/blueconicRtdProvider.js index c09fc6ee34c..93a03db02f0 100644 --- a/modules/blueconicRtdProvider.js +++ b/modules/blueconicRtdProvider.js @@ -6,10 +6,10 @@ * @requires module:modules/realTimeData */ -import {getStorageManager} from '../src/storageManager.js'; -import {submodule} from '../src/hook.js'; -import {mergeDeep, isPlainObject, logMessage, logError} from '../src/utils.js'; -import {MODULE_TYPE_RTD} from '../src/activities/modules.js'; +import { getStorageManager } from '../src/storageManager.js'; +import { submodule } from '../src/hook.js'; +import { mergeDeep, isPlainObject, logMessage, logError } from '../src/utils.js'; +import { MODULE_TYPE_RTD } from '../src/activities/modules.js'; /** * @typedef {import('../modules/rtdModule/index.js').RtdSubmodule} RtdSubmodule @@ -20,7 +20,7 @@ const SUBMODULE_NAME = 'blueconic'; export const RTD_LOCAL_NAME = 'bcPrebidData'; -export const storage = getStorageManager({moduleType: MODULE_TYPE_RTD, moduleName: SUBMODULE_NAME}); +export const storage = getStorageManager({ moduleType: MODULE_TYPE_RTD, moduleName: SUBMODULE_NAME }); /** * Try parsing stringified array of data. @@ -61,7 +61,7 @@ export function getRealTimeData(reqBidsConfigObj, onDone, rtdConfig, userConsent if (!parsedData) { return; } - const userData = {name: 'blueconic', ...parsedData} + const userData = { name: 'blueconic', ...parsedData } logMessage('blueconicRtdProvider: userData: ', userData); const data = { ortb2: { diff --git a/modules/bmtmBidAdapter.js b/modules/bmtmBidAdapter.js index bc6a4415f97..a5663d35299 100644 --- a/modules/bmtmBidAdapter.js +++ b/modules/bmtmBidAdapter.js @@ -1,8 +1,8 @@ -import {getDNT} from '../libraries/dnt/index.js'; import { generateUUID, deepAccess, logWarn, deepSetValue, isPlainObject } from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; import { config } from '../src/config.js'; +import { getDNT } from '../libraries/dnt/index.js'; const BIDDER_CODE = 'bmtm'; const AD_URL = 'https://one.elitebidder.com/api/hb?sid='; diff --git a/modules/bridBidAdapter.js b/modules/bridBidAdapter.js index afe9442e3ac..5e747301973 100644 --- a/modules/bridBidAdapter.js +++ b/modules/bridBidAdapter.js @@ -1,8 +1,8 @@ -import {_each, deepAccess, getDefinedParams, parseGPTSingleSizeArrayToRtbSize} from '../src/utils.js'; -import {VIDEO} from '../src/mediaTypes.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {getAd, getSiteObj, getSyncResponse} from '../libraries/targetVideoUtils/bidderUtils.js' -import {GVLID, SOURCE, TIME_TO_LIVE, VIDEO_ENDPOINT_URL, VIDEO_PARAMS} from '../libraries/targetVideoUtils/constants.js'; +import { _each, deepAccess, getDefinedParams, parseGPTSingleSizeArrayToRtbSize } from '../src/utils.js'; +import { VIDEO } from '../src/mediaTypes.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { getAd, getSiteObj, getSyncResponse } from '../libraries/targetVideoUtils/bidderUtils.js' +import { GVLID, SOURCE, TIME_TO_LIVE, VIDEO_ENDPOINT_URL, VIDEO_PARAMS } from '../libraries/targetVideoUtils/constants.js'; /** * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest @@ -56,7 +56,7 @@ export const spec = { const imp = { ext: { prebid: { - storedrequest: {'id': placementId} + storedrequest: { 'id': placementId } } } }; @@ -139,7 +139,7 @@ export const spec = { const requestId = bidRequest.bidId; const params = bidRequest.params; - const {ad, adUrl, vastUrl, vastXml} = getAd(bid); + const { ad, adUrl, vastUrl, vastXml } = getAd(bid); const bidResponse = { requestId, diff --git a/modules/bridgewellBidAdapter.js b/modules/bridgewellBidAdapter.js index 0cddb617799..9d85b5b43fe 100644 --- a/modules/bridgewellBidAdapter.js +++ b/modules/bridgewellBidAdapter.js @@ -1,6 +1,6 @@ -import {_each, deepSetValue, inIframe} from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER, NATIVE} from '../src/mediaTypes.js'; +import { _each, deepSetValue, inIframe } from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, NATIVE } from '../src/mediaTypes.js'; import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; /** @@ -45,33 +45,82 @@ export const spec = { validBidRequests = convertOrtbRequestToProprietaryNative(validBidRequests); const adUnits = []; - var bidderUrl = REQUEST_ENDPOINT + Math.random(); + const bidderUrl = REQUEST_ENDPOINT + Math.random(); _each(validBidRequests, function (bid) { + const passthrough = bid.ortb2Imp?.ext?.prebid?.passthrough; + const filteredPassthrough = passthrough ? Object.fromEntries( + Object.entries({ + bucket: passthrough.bucket, + client: passthrough.client, + gamAdCode: passthrough.gamAdCode, + gamLoc: passthrough.gamLoc, + colo: passthrough.colo, + device: passthrough.device, + lang: passthrough.lang, + pt: passthrough.pt, + region: passthrough.region, + site: passthrough.site, + ver: passthrough.ver + }).filter(([_, value]) => value !== undefined) + ) : undefined; + const adUnit = { adUnitCode: bid.adUnitCode, requestId: bid.bidId, + transactionId: bid.transactionId, + adUnitId: bid.adUnitId, + sizes: bid.sizes, mediaTypes: bid.mediaTypes || { banner: { sizes: bid.sizes } }, - userIds: bid.userId || {}, - userIdAsEids: bid.userIdAsEids || {} + ortb2Imp: { + ext: { + prebid: { + passthrough: filteredPassthrough + }, + data: { + adserver: { + name: bid.ortb2Imp?.ext?.data?.adserver?.name, + adslot: bid.ortb2Imp?.ext?.data?.adserver?.adslot + }, + pbadslot: bid.ortb2Imp?.ext?.data?.pbadslot + }, + gpid: bid.ortb2Imp?.ext?.gpid + }, + banner: { + pos: bid.ortb2Imp?.banner?.pos + } + } }; - if (bid.params.cid) { + + if (bid.params?.cid) { adUnit.cid = bid.params.cid; - } else { + } else if (bid.params?.ChannelID) { adUnit.ChannelID = bid.params.ChannelID; } + + let floorInfo = {}; + if (typeof bid.getFloor === 'function') { + const mediaType = bid.mediaTypes?.banner ? BANNER : (bid.mediaTypes?.native ? NATIVE : '*'); + const sizes = bid.mediaTypes?.banner?.sizes || bid.sizes || []; + const size = sizes.length === 1 ? sizes[0] : '*'; + floorInfo = bid.getFloor({ currency: 'USD', mediaType: mediaType, size: size }) || {}; + } + adUnit.floor = floorInfo.floor; + adUnit.currency = floorInfo.currency; adUnits.push(adUnit); }); let topUrl = ''; - if (bidderRequest && bidderRequest.refererInfo) { + if (bidderRequest?.refererInfo?.page) { topUrl = bidderRequest.refererInfo.page; } + const firstBid = validBidRequests[0] || {}; + return { method: 'POST', url: bidderUrl, @@ -82,10 +131,23 @@ export const spec = { }, inIframe: inIframe(), url: topUrl, - referrer: bidderRequest.refererInfo.ref, + referrer: bidderRequest?.refererInfo?.ref, + auctionId: firstBid?.auctionId, + bidderRequestId: firstBid?.bidderRequestId, + src: firstBid?.src, + userIds: firstBid?.userId || {}, + userIdAsEids: firstBid?.userIdAsEids || [], + auctionsCount: firstBid?.auctionsCount, + bidRequestsCount: firstBid?.bidRequestsCount, + bidderRequestsCount: firstBid?.bidderRequestsCount, + bidderWinsCount: firstBid?.bidderWinsCount, + deferBilling: firstBid?.deferBilling, + metrics: firstBid?.metrics || {}, adUnits: adUnits, // TODO: please do not send internal data structures over the network - refererInfo: bidderRequest.refererInfo.legacy}, + refererInfo: bidderRequest?.refererInfo?.legacy, + ortb2: bidderRequest?.ortb2 + }, validBidRequests: validBidRequests }; }, diff --git a/modules/browsiBidAdapter.js b/modules/browsiBidAdapter.js index cb256254e12..b3049739323 100644 --- a/modules/browsiBidAdapter.js +++ b/modules/browsiBidAdapter.js @@ -1,7 +1,7 @@ -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {config} from '../src/config.js'; -import {VIDEO} from '../src/mediaTypes.js'; -import {logError, logInfo, isArray, isStr} from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { config } from '../src/config.js'; +import { VIDEO } from '../src/mediaTypes.js'; +import { logError, logInfo, isArray, isStr } from '../src/utils.js'; /** * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid @@ -29,8 +29,8 @@ export const spec = { if (!bid.params) { return false; } - const {pubId, tagId} = bid.params - const {mediaTypes} = bid; + const { pubId, tagId } = bid.params + const { mediaTypes } = bid; return !!(validateBrowsiIds(pubId, tagId) && mediaTypes?.[VIDEO]); }, /** @@ -41,9 +41,9 @@ export const spec = { */ buildRequests: function (validBidRequests, bidderRequest) { const requests = []; - const {refererInfo, bidderRequestId, gdprConsent, uspConsent} = bidderRequest; + const { refererInfo, bidderRequestId, gdprConsent, uspConsent } = bidderRequest; validBidRequests.forEach(bidRequest => { - const {bidId, adUnitCode, auctionId, ortb2Imp, params} = bidRequest; + const { bidId, adUnitCode, auctionId, ortb2Imp, params } = bidRequest; const schain = bidRequest?.ortb2?.source?.ext?.schain; const video = getVideoMediaType(bidRequest); @@ -141,7 +141,7 @@ export const spec = { onTimeout(timeoutData) { logInfo(`${BIDDER_CODE} bidder timed out`, timeoutData); }, - onBidderError: function ({error}) { + onBidderError: function ({ error }) { logError(`${BIDDER_CODE} bidder error`, error); } } diff --git a/modules/buzzoolaBidAdapter.js b/modules/buzzoolaBidAdapter.js index fd3d3cd189e..f118b8c3e16 100644 --- a/modules/buzzoolaBidAdapter.js +++ b/modules/buzzoolaBidAdapter.js @@ -1,8 +1,8 @@ import { deepAccess, deepClone } from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER, VIDEO, NATIVE} from '../src/mediaTypes.js'; -import {Renderer} from '../src/Renderer.js'; -import {OUTSTREAM} from '../src/video.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, VIDEO, NATIVE } from '../src/mediaTypes.js'; +import { Renderer } from '../src/Renderer.js'; +import { OUTSTREAM } from '../src/video.js'; import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; /** @@ -55,7 +55,7 @@ export const spec = { * @param {ServerResponse} serverResponse A successful response from the server. * @return {Bid[]} An array of bids which were nested inside the server. */ - interpretResponse: function ({body}, {data}) { + interpretResponse: function ({ body }, { data }) { const requestBids = {}; let response; diff --git a/modules/byDataAnalyticsAdapter.js b/modules/byDataAnalyticsAdapter.js index 265dcf2115b..57c85e290e6 100644 --- a/modules/byDataAnalyticsAdapter.js +++ b/modules/byDataAnalyticsAdapter.js @@ -5,10 +5,10 @@ import enc from 'crypto-js/enc-utf8'; import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import { EVENTS, BID_STATUS } from '../src/constants.js'; import adapterManager from '../src/adapterManager.js'; -import {getStorageManager} from '../src/storageManager.js'; +import { getStorageManager } from '../src/storageManager.js'; import { auctionManager } from '../src/auctionManager.js'; import { ajax } from '../src/ajax.js'; -import {MODULE_TYPE_ANALYTICS} from '../src/activities/modules.js'; +import { MODULE_TYPE_ANALYTICS } from '../src/activities/modules.js'; import { getViewportSize } from '../libraries/viewport/viewport.js'; import { getOsBrowserInfo } from '../libraries/userAgentUtils/detailed.js'; import { getTimeZone } from '../libraries/timezone/timezone.js'; @@ -21,7 +21,7 @@ const analyticsType = 'endpoint' const isBydata = isKeyInUrl('bydata_debug') const adunitsMap = {} const MODULE_CODE = 'bydata'; -const storage = getStorageManager({moduleType: MODULE_TYPE_ANALYTICS, moduleName: MODULE_CODE}); +const storage = getStorageManager({ moduleType: MODULE_TYPE_ANALYTICS, moduleName: MODULE_CODE }); let initOptions = {} var payload = {} @@ -208,7 +208,7 @@ ascAdapter.getVisitorData = function (data = {}) { return signedToken; } function detectWidth() { - const {width: viewportWidth} = getViewportSize(); + const { width: viewportWidth } = getViewportSize(); const windowDimensions = getWinDimensions(); return windowDimensions.screen.width || (windowDimensions.innerWidth && windowDimensions.document.documentElement.clientWidth) ? Math.min(windowDimensions.innerWidth, windowDimensions.document.documentElement.clientWidth) : viewportWidth; } diff --git a/modules/cadent_aperture_mxBidAdapter.js b/modules/cadent_aperture_mxBidAdapter.js index f13bda26102..fa8a283b924 100644 --- a/modules/cadent_aperture_mxBidAdapter.js +++ b/modules/cadent_aperture_mxBidAdapter.js @@ -1,4 +1,3 @@ -import {getDNT} from '../libraries/dnt/index.js'; import { _each, deepAccess, getBidIdParameter, @@ -9,10 +8,11 @@ import { logError, logWarn } from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER, VIDEO} from '../src/mediaTypes.js'; -import {Renderer} from '../src/Renderer.js'; -import {parseDomain} from '../src/refererDetection.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; +import { Renderer } from '../src/Renderer.js'; +import { parseDomain } from '../src/refererDetection.js'; +import { getDNT } from '../libraries/dnt/index.js'; const BIDDER_CODE = 'cadent_aperture_mx'; const ENDPOINT = 'hb.emxdgt.com'; @@ -20,10 +20,10 @@ const RENDERER_URL = 'https://js.brealtime.com/outstream/1.30.0/bundle.js'; const ADAPTER_VERSION = '1.5.1'; const DEFAULT_CUR = 'USD'; const ALIASES = [ - { code: 'emx_digital'}, - { code: 'cadent'}, - { code: 'emxdigital'}, - { code: 'cadentaperturemx'}, + { code: 'emx_digital' }, + { code: 'cadent' }, + { code: 'emxdigital' }, + { code: 'cadentaperturemx' }, ]; const EIDS_SUPPORTED = [ @@ -87,7 +87,8 @@ export const cadentAdapter = { h: screen.height, w: screen.width, devicetype: cadentAdapter.isMobile() ? 1 : cadentAdapter.isConnectedTV() ? 3 : 2, - language: (navigator.language || navigator.browserLanguage || navigator.userLanguage || navigator.systemLanguage)}; + language: (navigator.language || navigator.browserLanguage || navigator.userLanguage || navigator.systemLanguage) + }; }, cleanProtocols: (video) => { if (video.protocols && video.protocols.includes(7)) { @@ -173,7 +174,7 @@ export const cadentAdapter = { getGpp: (bidRequest, cadentData) => { if (bidRequest.gppConsent) { - const {gppString: gpp, applicableSections: gppSid} = bidRequest.gppConsent; + const { gppString: gpp, applicableSections: gppSid } = bidRequest.gppConsent; if (cadentData.regs) { cadentData.regs.gpp = gpp; cadentData.regs.gpp_sid = gppSid; @@ -315,7 +316,7 @@ export const spec = { cadentData.user.ext.eids = eids; } else { cadentData.user = { - ext: {eids} + ext: { eids } }; } } diff --git a/modules/categoryTranslation.js b/modules/categoryTranslation.js deleted file mode 100644 index a0ef902412e..00000000000 --- a/modules/categoryTranslation.js +++ /dev/null @@ -1,105 +0,0 @@ -/** - * This module translates iab category to freewheel industry using translation mapping file - * Publisher can set translation file by using setConfig method - * - * Example: - * config.setConfig({ - * 'brandCategoryTranslation': { - * 'translationFile': 'http://sample.com' - * } - * }); - * If publisher has not defined translation file than prebid will use default prebid translation file provided here //cdn.jsdelivr.net/gh/prebid/category-mapping-file@1/freewheel-mapping.json - */ - -import {config} from '../src/config.js'; -import {hook, setupBeforeHookFnOnce, ready} from '../src/hook.js'; -import {ajax} from '../src/ajax.js'; -import {logError, timestamp} from '../src/utils.js'; -import {addBidResponse} from '../src/auction.js'; -import {getCoreStorageManager} from '../src/storageManager.js'; -import {timedBidResponseHook} from '../src/utils/perfMetrics.js'; - -export const storage = getCoreStorageManager('categoryTranslation'); -const DEFAULT_TRANSLATION_FILE_URL = 'https://cdn.jsdelivr.net/gh/prebid/category-mapping-file@1/freewheel-mapping.json'; -const DEFAULT_IAB_TO_FW_MAPPING_KEY = 'iabToFwMappingkey'; -const DEFAULT_IAB_TO_FW_MAPPING_KEY_PUB = 'iabToFwMappingkeyPub'; -const refreshInDays = 1; - -export const registerAdserver = hook('async', function(adServer) { - let url; - if (adServer === 'freewheel') { - url = DEFAULT_TRANSLATION_FILE_URL; - initTranslation(url, DEFAULT_IAB_TO_FW_MAPPING_KEY); - } -}, 'registerAdserver'); - -ready.then(() => registerAdserver()); - -export const getAdserverCategoryHook = timedBidResponseHook('categoryTranslation', function getAdserverCategoryHook(fn, adUnitCode, bid, reject) { - if (!bid) { - return fn.call(this, adUnitCode, bid, reject); // if no bid, call original and let it display warnings - } - - if (!config.getConfig('adpod.brandCategoryExclusion')) { - return fn.call(this, adUnitCode, bid, reject); - } - - const localStorageKey = (config.getConfig('brandCategoryTranslation.translationFile')) ? DEFAULT_IAB_TO_FW_MAPPING_KEY_PUB : DEFAULT_IAB_TO_FW_MAPPING_KEY; - - if (bid.meta && !bid.meta.adServerCatId) { - let mapping = storage.getDataFromLocalStorage(localStorageKey); - if (mapping) { - try { - mapping = JSON.parse(mapping); - } catch (error) { - logError('Failed to parse translation mapping file'); - } - if (bid.meta.primaryCatId && mapping['mapping'] && mapping['mapping'][bid.meta.primaryCatId]) { - bid.meta.adServerCatId = mapping['mapping'][bid.meta.primaryCatId]['id']; - } else { - // This bid will be automatically ignored by adpod module as adServerCatId was not found - bid.meta.adServerCatId = undefined; - } - } else { - logError('Translation mapping data not found in local storage'); - } - } - fn.call(this, adUnitCode, bid, reject); -}); - -export function initTranslation(url, localStorageKey) { - setupBeforeHookFnOnce(addBidResponse, getAdserverCategoryHook, 50); - let mappingData = storage.getDataFromLocalStorage(localStorageKey); - try { - mappingData = mappingData ? JSON.parse(mappingData) : undefined; - if (!mappingData || timestamp() > mappingData.lastUpdated + refreshInDays * 24 * 60 * 60 * 1000) { - ajax(url, - { - success: (response) => { - try { - response = JSON.parse(response); - response['lastUpdated'] = timestamp(); - storage.setDataInLocalStorage(localStorageKey, JSON.stringify(response)); - } catch (error) { - logError('Failed to parse translation mapping file'); - } - }, - error: () => { - logError('Failed to load brand category translation file.') - } - }, - ); - } - } catch (error) { - logError('Failed to parse translation mapping file'); - } -} - -function setConfig(config) { - if (config.translationFile) { - // if publisher has defined the translation file, preload that file here - initTranslation(config.translationFile, DEFAULT_IAB_TO_FW_MAPPING_KEY_PUB); - } -} - -config.getConfig('brandCategoryTranslation', config => setConfig(config.brandCategoryTranslation)); diff --git a/modules/ccxBidAdapter.js b/modules/ccxBidAdapter.js index 14268185027..68e30854916 100644 --- a/modules/ccxBidAdapter.js +++ b/modules/ccxBidAdapter.js @@ -1,9 +1,9 @@ -import {_each, deepAccess, isArray, isEmpty, logWarn} from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {getStorageManager} from '../src/storageManager.js'; +import { _each, deepAccess, isArray, isEmpty, logWarn } from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { getStorageManager } from '../src/storageManager.js'; const BIDDER_CODE = 'ccx' -const storage = getStorageManager({bidderCode: BIDDER_CODE}); +const storage = getStorageManager({ bidderCode: BIDDER_CODE }); const BID_URL = 'https://delivery.clickonometrics.pl/ortb/prebid/bid' const SUPPORTED_VIDEO_PROTOCOLS = [2, 3, 5, 6] const SUPPORTED_VIDEO_MIMES = ['video/mp4', 'video/x-flv'] @@ -72,13 +72,13 @@ function _buildBid (bid, bidderRequest) { const sizes = deepAccess(bid, 'mediaTypes.banner.sizes') || deepAccess(bid, 'mediaTypes.video.playerSize') || deepAccess(bid, 'sizes') if (deepAccess(bid, 'mediaTypes.banner') || deepAccess(bid, 'mediaType') === 'banner' || (!deepAccess(bid, 'mediaTypes.video') && !deepAccess(bid, 'mediaType'))) { - placement.banner = {'format': []} + placement.banner = { 'format': [] } if (isArray(sizes[0])) { _each(sizes, function (size) { - placement.banner.format.push({'w': size[0], 'h': size[1]}) + placement.banner.format.push({ 'w': size[0], 'h': size[1] }) }) } else { - placement.banner.format.push({'w': sizes[0], 'h': sizes[1]}) + placement.banner.format.push({ 'w': sizes[0], 'h': sizes[1] }) } } else if (deepAccess(bid, 'mediaTypes.video') || deepAccess(bid, 'mediaType') === 'video') { placement.video = {} @@ -102,11 +102,7 @@ function _buildBid (bid, bidderRequest) { } } - placement.ext = {'pid': bid.params.placementId} - - if (bidderRequest.paapi?.enabled) { - placement.ext.ae = bid?.ortb2Imp?.ext?.ae - } + placement.ext = { 'pid': bid.params.placementId } return placement } @@ -181,7 +177,7 @@ export const spec = { requestBody.site = _getSiteObj(bidderRequest) requestBody.device = _getDeviceObj() requestBody.id = bidderRequest.bidderRequestId; - requestBody.ext = {'ce': (storage.cookiesAreEnabled() ? 1 : 0)} + requestBody.ext = { 'ce': (storage.cookiesAreEnabled() ? 1 : 0) } // Attaching GDPR Consent Params if (bidderRequest && bidderRequest.gdprConsent) { diff --git a/modules/chromeAiRtdProvider.js b/modules/chromeAiRtdProvider.js index 9fd2d4c6639..75f17b6312a 100644 --- a/modules/chromeAiRtdProvider.js +++ b/modules/chromeAiRtdProvider.js @@ -15,6 +15,7 @@ export const CONSTANTS = Object.freeze({ STORAGE_KEY: 'chromeAi_detected_data', // Single key for both language and keywords MIN_TEXT_LENGTH: 20, ACTIVATION_EVENTS: ['click', 'keydown', 'mousedown', 'touchend', 'pointerdown', 'pointerup'], + MAX_TEXT_LENGTH: 1000, // Limit to prevent QuotaExceededError with Chrome AI APIs DEFAULT_CONFIG: { languageDetector: { enabled: true, @@ -94,6 +95,11 @@ export const getPageText = () => { logMessage(`${CONSTANTS.LOG_PRE_FIX} Not enough text content (length: ${text?.length || 0}) for processing.`); return null; } + // Limit text length to prevent QuotaExceededError with Chrome AI APIs + if (text.length > CONSTANTS.MAX_TEXT_LENGTH) { + logMessage(`${CONSTANTS.LOG_PRE_FIX} Truncating text from ${text.length} to ${CONSTANTS.MAX_TEXT_LENGTH} chars.`); + return text.substring(0, CONSTANTS.MAX_TEXT_LENGTH); + } return text; }; diff --git a/modules/chtnwBidAdapter.js b/modules/chtnwBidAdapter.js index 3aea0016679..920c4670b84 100644 --- a/modules/chtnwBidAdapter.js +++ b/modules/chtnwBidAdapter.js @@ -1,4 +1,3 @@ -import {getDNT} from '../libraries/dnt/index.js'; import { generateUUID, _each, @@ -9,11 +8,12 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; import { getStorageManager } from '../src/storageManager.js'; import { ajax } from '../src/ajax.js'; -import {BANNER, VIDEO, NATIVE} from '../src/mediaTypes.js'; +import { BANNER, VIDEO, NATIVE } from '../src/mediaTypes.js'; +import { getDNT } from '../libraries/dnt/index.js'; const ENDPOINT_URL = 'https://prebid.cht.hinet.net/api/v1'; const BIDDER_CODE = 'chtnw'; const COOKIE_NAME = '__htid'; -const storage = getStorageManager({bidderCode: BIDDER_CODE}); +const storage = getStorageManager({ bidderCode: BIDDER_CODE }); const { getConfig } = config; diff --git a/modules/clickforceBidAdapter.js b/modules/clickforceBidAdapter.js index 6308774faad..d45a701cd5e 100644 --- a/modules/clickforceBidAdapter.js +++ b/modules/clickforceBidAdapter.js @@ -1,6 +1,6 @@ import { _each } from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER, NATIVE} from '../src/mediaTypes.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, NATIVE } from '../src/mediaTypes.js'; import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; /** diff --git a/modules/clickioBidAdapter.js b/modules/clickioBidAdapter.js index 3ba5094ffe5..2028256f18b 100644 --- a/modules/clickioBidAdapter.js +++ b/modules/clickioBidAdapter.js @@ -1,7 +1,7 @@ -import {deepSetValue} from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {ortbConverter} from '../libraries/ortbConverter/converter.js'; -import {BANNER} from '../src/mediaTypes.js'; +import { deepSetValue } from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { ortbConverter } from '../libraries/ortbConverter/converter.js'; +import { BANNER } from '../src/mediaTypes.js'; const BIDDER_CODE = 'clickio'; const IAB_GVL_ID = 1500; @@ -23,7 +23,7 @@ export const spec = { gvlid: IAB_GVL_ID, supportedMediaTypes: [BANNER], buildRequests(bidRequests, bidderRequest) { - const data = converter.toORTB({bidRequests, bidderRequest}) + const data = converter.toORTB({ bidRequests, bidderRequest }) return [{ method: 'POST', url: 'https://o.clickiocdn.com/bids', @@ -34,7 +34,7 @@ export const spec = { return true; }, interpretResponse(response, request) { - const bids = converter.fromORTB({response: response.body, request: request.data}).bids; + const bids = converter.fromORTB({ response: response.body, request: request.data }).bids; return bids; }, getUserSyncs(syncOptions, _, gdprConsent, uspConsent, gppConsent = {}) { diff --git a/modules/clydoBidAdapter.js b/modules/clydoBidAdapter.js index 1ffdd3df474..2f00d439748 100644 --- a/modules/clydoBidAdapter.js +++ b/modules/clydoBidAdapter.js @@ -35,7 +35,7 @@ export const spec = { return allowedRegions.includes(region); }, buildRequests: function(validBidRequests, bidderRequest) { - const data = converter.toORTB({bidRequests: validBidRequests, bidderRequest}); + const data = converter.toORTB({ bidRequests: validBidRequests, bidderRequest }); const { partnerId, region } = validBidRequests[0].params; if (Array.isArray(data.imp)) { @@ -47,7 +47,7 @@ export const spec = { const mediaType = imp.banner ? 'banner' : (imp.video ? 'video' : (imp.native ? 'native' : '*')); let floor = deepAccess(srcBid, 'floor'); if (!floor && isFn(srcBid.getFloor)) { - const floorInfo = srcBid.getFloor({currency: DEFAULT_CURRENCY, mediaType, size: '*'}); + const floorInfo = srcBid.getFloor({ currency: DEFAULT_CURRENCY, mediaType, size: '*' }); if (floorInfo && typeof floorInfo.floor === 'number') { floor = floorInfo.floor; } @@ -84,7 +84,7 @@ export const spec = { try { const parsed = JSON.parse(b.adm); if (parsed && parsed.native && Array.isArray(parsed.native.assets)) { - return {...b, adm: JSON.stringify(parsed.native)}; + return { ...b, adm: JSON.stringify(parsed.native) }; } } catch (e) {} } @@ -93,7 +93,7 @@ export const spec = { })) } : body; - bids = converter.fromORTB({response: normalized, request: request.data}).bids; + bids = converter.fromORTB({ response: normalized, request: request.data }).bids; } return bids; }, diff --git a/modules/codefuelBidAdapter.js b/modules/codefuelBidAdapter.js index 235ef613992..c8ec470dce5 100644 --- a/modules/codefuelBidAdapter.js +++ b/modules/codefuelBidAdapter.js @@ -1,6 +1,6 @@ -import {isArray, setOnAny} from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER} from '../src/mediaTypes.js'; +import { isArray, setOnAny } from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER } from '../src/mediaTypes.js'; /** * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest @@ -16,7 +16,7 @@ const CURRENCY = 'USD'; export const spec = { code: BIDDER_CODE, - supportedMediaTypes: [ BANNER ], + supportedMediaTypes: [BANNER], aliases: ['ex'], // short code /** * Determines whether or not the given bid request is valid. diff --git a/modules/cointrafficBidAdapter.js b/modules/cointrafficBidAdapter.js index d9394253497..93c031dcdf7 100644 --- a/modules/cointrafficBidAdapter.js +++ b/modules/cointrafficBidAdapter.js @@ -4,7 +4,7 @@ import { BANNER } from '../src/mediaTypes.js' import { config } from '../src/config.js' import { getCurrencyFromBidderRequest } from '../libraries/ortb2Utils/currency.js'; import { getViewportSize } from '../libraries/viewport/viewport.js' -import { getDNT } from '../libraries/dnt/index.js' +import { getDNT } from '../libraries/dnt/index.js'; /** * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest diff --git a/modules/coinzillaBidAdapter.js b/modules/coinzillaBidAdapter.js index 9ae2c74547d..fe09221790d 100644 --- a/modules/coinzillaBidAdapter.js +++ b/modules/coinzillaBidAdapter.js @@ -1,5 +1,5 @@ import { parseSizesInput } from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; /** * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest diff --git a/modules/colombiaBidAdapter.js b/modules/colombiaBidAdapter.js index 9beb4117986..e791c19fb9b 100644 --- a/modules/colombiaBidAdapter.js +++ b/modules/colombiaBidAdapter.js @@ -1,7 +1,7 @@ import { ajax } from '../src/ajax.js'; import * as utils from '../src/utils.js'; -import {config} from '../src/config.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; +import { config } from '../src/config.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER } from '../src/mediaTypes.js'; const BIDDER_CODE = 'colombia'; const ENDPOINT_URL = 'https://ade.clmbtech.com/cde/prebid.htm'; diff --git a/modules/colossussspBidAdapter.js b/modules/colossussspBidAdapter.js index 951a4144522..d0728989c3e 100644 --- a/modules/colossussspBidAdapter.js +++ b/modules/colossussspBidAdapter.js @@ -27,7 +27,7 @@ function getUserId(eids, id, source, uidExt) { } eids.push({ source, - uids: [ uid ] + uids: [uid] }); } } diff --git a/modules/conceptxBidAdapter.js b/modules/conceptxBidAdapter.js index 67ebd88e4e4..eef57e0aaa8 100644 --- a/modules/conceptxBidAdapter.js +++ b/modules/conceptxBidAdapter.js @@ -1,81 +1,258 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER } from '../src/mediaTypes.js'; -// import { logError, logInfo, logWarn, parseUrl } from '../src/utils.js'; const BIDDER_CODE = 'conceptx'; -const ENDPOINT_URL = 'https://conceptx.cncpt-central.com/openrtb'; -// const LOG_PREFIX = 'ConceptX: '; +const ENDPOINT_URL = 'https://cxba-s2s.cncpt.dk/openrtb2/auction'; const GVLID = 1340; export const spec = { code: BIDDER_CODE, supportedMediaTypes: [BANNER], gvlid: GVLID, + isBidRequestValid: function (bid) { - return !!(bid.bidId && bid.params.site && bid.params.adunit); + return !!(bid.bidId && bid.params && bid.params.adunit); }, buildRequests: function (validBidRequests, bidderRequest) { - // logWarn(LOG_PREFIX + 'all native assets containing URL should be sent as placeholders with sendId(icon, image, clickUrl, displayUrl, privacyLink, privacyIcon)'); const requests = []; - let requestUrl = `${ENDPOINT_URL}` - if (bidderRequest && bidderRequest.gdprConsent && bidderRequest.gdprConsent.gdprApplies) { - requestUrl += '?gdpr_applies=' + bidderRequest.gdprConsent.gdprApplies; - requestUrl += '&consentString=' + bidderRequest.gdprConsent.consentString; - } - for (var i = 0; i < validBidRequests.length; i++) { - const requestParent = { adUnits: [], meta: {} }; - const bid = validBidRequests[i] - const { adUnitCode, auctionId, bidId, bidder, bidderRequestId, ortb2 } = bid - requestParent.meta = { adUnitCode, auctionId, bidId, bidder, bidderRequestId, ortb2 } - - const { site, adunit } = bid.params - const adUnit = { site, adunit, targetId: bid.bidId } - if (bid.mediaTypes && bid.mediaTypes.banner && bid.mediaTypes.banner.sizes) adUnit.dimensions = bid.mediaTypes.banner.sizes - requestParent.adUnits.push(adUnit); + + for (let i = 0; i < validBidRequests.length; i++) { + const bid = validBidRequests[i]; + const { + adUnitCode, + auctionId, + bidId, + bidder, + bidderRequestId, + ortb2 = {}, + } = bid; + const params = bid.params || {}; + + // PBS URL + GDPR query params + let url = ENDPOINT_URL; + const query = []; + + // Only add GDPR params when gdprApplies is explicitly 0 or 1 + if (bidderRequest && bidderRequest.gdprConsent) { + let gdprApplies = bidderRequest.gdprConsent.gdprApplies; + if (typeof gdprApplies === 'boolean') { + gdprApplies = gdprApplies ? 1 : 0; + } + if (gdprApplies === 0 || gdprApplies === 1) { + query.push('gdpr_applies=' + gdprApplies); + if (bidderRequest.gdprConsent.consentString) { + query.push( + 'gdpr_consent=' + + encodeURIComponent(bidderRequest.gdprConsent.consentString) + ); + } + } + } + + if (query.length) { + url += '?' + query.join('&'); + } + + // site + const page = + params.site || (ortb2.site && ortb2.site.page) || ''; + const domain = + params.domain || (ortb2.site && ortb2.site.domain) || page; + + const site = { + id: domain || page || adUnitCode, + domain: domain || '', + page: page || '', + }; + + // banner sizes from mediaTypes.banner.sizes + const formats = []; + if ( + bid.mediaTypes && + bid.mediaTypes.banner && + bid.mediaTypes.banner.sizes + ) { + let sizes = bid.mediaTypes.banner.sizes; + if (sizes.length && typeof sizes[0] === 'number') { + sizes = [sizes]; + } + for (let j = 0; j < sizes.length; j++) { + const size = sizes[j]; + if (size && size.length === 2) { + formats.push({ w: size[0], h: size[1] }); + } + } + } + + const banner = formats.length ? { format: formats } : {}; + + // currency & timeout + let currency = 'DKK'; + if ( + bidderRequest && + bidderRequest.currency && + bidderRequest.currency.adServerCurrency + ) { + currency = bidderRequest.currency.adServerCurrency; + } + + const tmax = (bidderRequest && bidderRequest.timeout) || 500; + + // device + const ua = + typeof navigator !== 'undefined' && navigator.userAgent + ? navigator.userAgent + : 'Mozilla/5.0'; + const device = { ua }; + + // build OpenRTB request for PBS with stored requests + const ortbRequest = { + id: auctionId || bidId, + site, + device, + cur: [currency], + tmax, + imp: [ + { + id: bidId, + banner, + ext: { + prebid: { + storedrequest: { + id: params.adunit, + }, + }, + }, + }, + ], + ext: { + prebid: { + storedrequest: { + id: 'cx_global', + }, + custommeta: { + adUnitCode, + auctionId, + bidId, + bidder, + bidderRequestId, + }, + }, + }, + }; + + // GDPR in body + if (bidderRequest && bidderRequest.gdprConsent) { + let gdprAppliesBody = bidderRequest.gdprConsent.gdprApplies; + if (typeof gdprAppliesBody === 'boolean') { + gdprAppliesBody = gdprAppliesBody ? 1 : 0; + } + + if (!ortbRequest.user) ortbRequest.user = {}; + if (!ortbRequest.user.ext) ortbRequest.user.ext = {}; + + if (bidderRequest.gdprConsent.consentString) { + ortbRequest.user.ext.consent = + bidderRequest.gdprConsent.consentString; + } + + if (!ortbRequest.regs) ortbRequest.regs = {}; + if (!ortbRequest.regs.ext) ortbRequest.regs.ext = {}; + + if (gdprAppliesBody === 0 || gdprAppliesBody === 1) { + ortbRequest.regs.ext.gdpr = gdprAppliesBody; + } + } + + // user IDs -> user.ext.eids + if (bid.userIdAsEids && bid.userIdAsEids.length) { + if (!ortbRequest.user) ortbRequest.user = {}; + if (!ortbRequest.user.ext) ortbRequest.user.ext = {}; + ortbRequest.user.ext.eids = bid.userIdAsEids; + } + requests.push({ method: 'POST', - url: requestUrl, + url, options: { - withCredentials: false, + withCredentials: true, }, - data: JSON.stringify(requestParent), + data: JSON.stringify(ortbRequest), }); } return requests; }, - interpretResponse: function (serverResponse, bidRequest) { - const bidResponses = []; - const bidResponsesFromServer = serverResponse.body.bidResponses; - if (Array.isArray(bidResponsesFromServer) && bidResponsesFromServer.length === 0) { - return bidResponses - } - const firstBid = bidResponsesFromServer[0] - if (!firstBid) { - return bidResponses + interpretResponse: function (serverResponse, request) { + const body = + serverResponse && serverResponse.body ? serverResponse.body : {}; + + // PBS OpenRTB: seatbid[].bid[] + if ( + !body.seatbid || + !Array.isArray(body.seatbid) || + body.seatbid.length === 0 + ) { + return []; } - const firstSeat = firstBid.ads[0] - if (!firstSeat) { - return bidResponses + + const currency = body.cur || 'DKK'; + const bids = []; + + // recover referrer (site.page) from original request + let referrer = ''; + try { + if (request && request.data) { + const originalReq = + typeof request.data === 'string' + ? JSON.parse(request.data) + : request.data; + if (originalReq && originalReq.site && originalReq.site.page) { + referrer = originalReq.site.page; + } + } + } catch (_) {} + + for (let i = 0; i < body.seatbid.length; i++) { + const seatbid = body.seatbid[i]; + if (!seatbid.bid || !Array.isArray(seatbid.bid)) continue; + + for (let j = 0; j < seatbid.bid.length; j++) { + const b = seatbid.bid[j]; + + if (!b || typeof b.price !== 'number' || !b.adm) continue; + + bids.push({ + requestId: b.impid || b.id, + cpm: b.price, + width: b.w, + height: b.h, + creativeId: b.crid || b.id || '', + dealId: b.dealid || b.dealId || undefined, + currency, + netRevenue: true, + ttl: 300, + referrer, + ad: b.adm, + }); + } } - const bidResponse = { - requestId: firstSeat.requestId, - cpm: firstSeat.cpm, - width: firstSeat.width, - height: firstSeat.height, - creativeId: firstSeat.creativeId, - dealId: firstSeat.dealId, - currency: firstSeat.currency, - netRevenue: true, - ttl: firstSeat.ttl, - referrer: firstSeat.referrer, - ad: firstSeat.html - }; - bidResponses.push(bidResponse); - return bidResponses; + + return bids; + }, + + /** + * Cookie sync for conceptx is handled by the enrichment script's runPbsCookieSync, + * which calls https://cxba-s2s.cncpt.dk/cookie_sync with bidders. The PBS returns + * bidder_status with usersync URLs, and the script runs iframe/image syncs. + * The adapter does not return sync URLs here since those come from the cookie_sync + * endpoint, not the auction response. + */ + getUserSyncs: function () { + return []; }, +}; -} registerBidder(spec); diff --git a/modules/concertAnalyticsAdapter.js b/modules/concertAnalyticsAdapter.js index 75c0c33966c..99b6f8b8f20 100644 --- a/modules/concertAnalyticsAdapter.js +++ b/modules/concertAnalyticsAdapter.js @@ -1,5 +1,5 @@ import { logMessage } from '../src/utils.js'; -import {ajax} from '../src/ajax.js'; +import { ajax } from '../src/ajax.js'; import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import { EVENTS } from '../src/constants.js'; import adapterManager from '../src/adapterManager.js'; @@ -20,7 +20,7 @@ const { let queue = []; -const concertAnalytics = Object.assign(adapter({url, analyticsType}), { +const concertAnalytics = Object.assign(adapter({ url, analyticsType }), { track({ eventType, args }) { switch (eventType) { case BID_RESPONSE: diff --git a/modules/concertBidAdapter.js b/modules/concertBidAdapter.js index a83c078ccef..e9d463ca937 100644 --- a/modules/concertBidAdapter.js +++ b/modules/concertBidAdapter.js @@ -4,6 +4,7 @@ import { getStorageManager } from '../src/storageManager.js'; import { hasPurpose1Consent } from '../src/utils/gdpr.js'; import { getBoundingClientRect } from '../libraries/boundingClientRect/boundingClientRect.js'; import { getViewportCoordinates } from '../libraries/viewport/viewport.js'; +import { getAdUnitElement } from '../src/utils/adUnits.js'; /** * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest @@ -70,7 +71,7 @@ export const spec = { payload.slots = validBidRequests.map((bidRequest) => { eids.push(...(bidRequest.userIdAsEids || [])); - const adUnitElement = document.getElementById(bidRequest.adUnitCode); + const adUnitElement = getAdUnitElement(bidRequest); const coordinates = getOffset(adUnitElement); const slot = { diff --git a/modules/connatixBidAdapter.js b/modules/connatixBidAdapter.js index 4ccb75bdc97..c8364b97daa 100644 --- a/modules/connatixBidAdapter.js +++ b/modules/connatixBidAdapter.js @@ -3,7 +3,7 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { percentInView } from '../libraries/percentInView/percentInView.js'; +import { getViewability, isViewabilityMeasurable } from '../libraries/percentInView/percentInView.js'; import { ajax } from '../src/ajax.js'; import { config } from '../src/config.js'; @@ -25,6 +25,7 @@ import { BANNER, VIDEO, } from '../src/mediaTypes.js'; +import { getAdUnitElement } from '../src/utils/adUnits.js'; const BIDDER_CODE = 'connatix'; @@ -65,6 +66,7 @@ export function getBidFloor(bid) { export function validateBanner(mediaTypes) { if (!mediaTypes[BANNER]) { + // Undefined banner means no banner ads, which is a valid option return true; } @@ -75,6 +77,7 @@ export function validateBanner(mediaTypes) { export function validateVideo(mediaTypes) { const video = mediaTypes[VIDEO]; if (!video) { + // Undefined video means no video ads, which is a valid option return true; } @@ -82,7 +85,10 @@ export function validateVideo(mediaTypes) { } export function _getMinSize(sizes) { - if (!sizes || sizes.length === 0) return undefined; + if (!sizes || sizes.length === 0) { + return; + } + return sizes.reduce((minSize, currentSize) => { const minArea = minSize.w * minSize.h; const currentArea = currentSize.w * currentSize.h; @@ -90,28 +96,36 @@ export function _getMinSize(sizes) { }); } -export function _canSelectViewabilityContainer() { - try { - window.top.document.querySelector('#viewability-container'); - return true; - } catch (e) { - return false; - } -} +function getDomElement(elementId) { + const getElementFromDoc = doc => { + let element; -export function _isViewabilityMeasurable(element) { - if (!element) return false; - return _canSelectViewabilityContainer(element); -} + try { + element = doc.querySelector(elementId); + } catch (e) { + /* noop */ + } -export function _getViewability(element, topWin, { w, h } = {}) { - return topWin.document.visibilityState === 'visible' - ? percentInView(element, { w, h }) - : 0; + if (!element) { + element = doc.getElementById(elementId); + } + + return element; + }; + + let viewabilityContainer = getElementFromDoc(document); + if (viewabilityContainer) { + return viewabilityContainer; + } + + const topDocument = window.top.document; + if (document !== topDocument) { + return getElementFromDoc(topDocument); + } } export function detectViewability(bid) { - const { params, adUnitCode } = bid; + const { params } = bid; const viewabilityContainerIdentifier = params.viewabilityContainerIdentifier; @@ -121,7 +135,8 @@ export function detectViewability(bid) { if (isStr(viewabilityContainerIdentifier)) { try { - element = document.querySelector(viewabilityContainerIdentifier) || window.top.document.querySelector(viewabilityContainerIdentifier); + element = getDomElement(viewabilityContainerIdentifier); + if (element) { bidParamSizes = [element.offsetWidth, element.offsetHeight]; minSize = _getMinSize(bidParamSizes) @@ -137,15 +152,15 @@ export function detectViewability(bid) { bidParamSizes = typeof bidParamSizes === 'undefined' && bid.mediaType && bid.mediaType.video && bid.mediaType.video.playerSize ? bid.mediaType.video.playerSize : bidParamSizes; bidParamSizes = typeof bidParamSizes === 'undefined' && bid.mediaType && bid.mediaType.video && isNumber(bid.mediaType.video.w) && isNumber(bid.mediaType.h) ? [bid.mediaType.video.w, bid.mediaType.video.h] : bidParamSizes; minSize = _getMinSize(bidParamSizes ?? []) - element = document.getElementById(adUnitCode); + element = getAdUnitElement(bid); } - if (_isViewabilityMeasurable(element)) { + if (isViewabilityMeasurable(element)) { const minSizeObj = { w: minSize[0], h: minSize[1] } - return Math.round(_getViewability(element, getWindowTop(), minSizeObj)) + return Math.round(getViewability(element, getWindowTop(), minSizeObj)) } return null; @@ -342,6 +357,15 @@ export const spec = { params['us_privacy'] = encodeURIComponent(uspConsent); } + if (gppConsent?.gppString && gppConsent?.applicableSections?.length) { + params['gpp'] = encodeURIComponent(gppConsent.gppString); + params['gpp_sid'] = gppConsent.applicableSections.join(','); + } + + if (config.getConfig('coppa') === true) { + params['coppa'] = 1; + } + window.addEventListener('message', function handler(event) { if (!event.data || event.origin !== 'https://cds.connatix.com' || !event.data.cnx) { return; @@ -397,7 +421,7 @@ export const spec = { } const requestTimeout = connatixBidRequestTimeout.timeout; const timeout = isNumber(requestTimeout) ? requestTimeout : config.getConfig('bidderTimeout'); - spec.triggerEvent({type: 'Timeout', timeout, context}); + spec.triggerEvent({ type: 'Timeout', timeout, context }); }, /** @@ -407,9 +431,9 @@ export const spec = { if (bidWinData == null) { return; } - const {bidder, cpm, requestId, bidId, adUnitCode, timeToRespond, auctionId} = bidWinData; + const { bidder, cpm, requestId, bidId, adUnitCode, timeToRespond, auctionId } = bidWinData; - spec.triggerEvent({type: 'BidWon', bestBidBidder: bidder, bestBidPrice: cpm, requestId, bidId, adUnitCode, timeToRespond, auctionId, context}); + spec.triggerEvent({ type: 'BidWon', bestBidBidder: bidder, bestBidPrice: cpm, requestId, bidId, adUnitCode, timeToRespond, auctionId, context }); }, triggerEvent(data) { diff --git a/modules/connectIdSystem.js b/modules/connectIdSystem.js index 006cb06d005..b0f8e4836ef 100644 --- a/modules/connectIdSystem.js +++ b/modules/connectIdSystem.js @@ -5,13 +5,13 @@ * @requires module:modules/userId */ -import {ajax} from '../src/ajax.js'; -import {submodule} from '../src/hook.js'; +import { ajax } from '../src/ajax.js'; +import { submodule } from '../src/hook.js'; -import {getRefererInfo} from '../src/refererDetection.js'; -import {getStorageManager, STORAGE_TYPE_COOKIES, STORAGE_TYPE_LOCALSTORAGE} from '../src/storageManager.js'; -import {formatQS, isNumber, isPlainObject, logError, parseUrl} from '../src/utils.js'; -import {MODULE_TYPE_UID} from '../src/activities/modules.js'; +import { getRefererInfo } from '../src/refererDetection.js'; +import { getStorageManager, STORAGE_TYPE_COOKIES, STORAGE_TYPE_LOCALSTORAGE } from '../src/storageManager.js'; +import { formatQS, isNumber, isPlainObject, logError, parseUrl } from '../src/utils.js'; +import { MODULE_TYPE_UID } from '../src/activities/modules.js'; /** * @typedef {import('../modules/userId/index.js').Submodule} Submodule @@ -42,7 +42,7 @@ const O_AND_O_DOMAINS = [ 'techcrunch.com', 'autoblog.com', ]; -export const storage = getStorageManager({moduleType: MODULE_TYPE_UID, moduleName: MODULE_NAME}); +export const storage = getStorageManager({ moduleType: MODULE_TYPE_UID, moduleName: MODULE_NAME }); /** * Stores the ConnectID object in browser storage according to storage configuration @@ -201,7 +201,7 @@ export const connectIdSubmodule = { return undefined; } return (isPlainObject(value) && (value.connectId || value.connectid)) - ? {connectId: value.connectId || value.connectid} : undefined; + ? { connectId: value.connectId || value.connectid } : undefined; }, /** * Gets the Yahoo ConnectID @@ -239,7 +239,7 @@ export const connectIdSubmodule = { if (!shouldResync) { storedId.lastUsed = Date.now(); storeObject(storedId, storageConfig); - return {id: storedId}; + return { id: storedId }; } } @@ -316,9 +316,9 @@ export const connectIdSubmodule = { }; const endpoint = UPS_ENDPOINT.replace(PLACEHOLDER, params.pixelId); const url = `${params.endpoint || endpoint}?${formatQS(data)}`; - connectIdSubmodule.getAjaxFn()(url, callbacks, null, {method: 'GET', withCredentials: true}); + connectIdSubmodule.getAjaxFn()(url, callbacks, null, { method: 'GET', withCredentials: true }); }; - const result = {callback: resp}; + const result = { callback: resp }; if (shouldResync && storedId) { result.id = storedId; } diff --git a/modules/connectadBidAdapter.js b/modules/connectadBidAdapter.js index 464499b5f7d..d1fec831b50 100644 --- a/modules/connectadBidAdapter.js +++ b/modules/connectadBidAdapter.js @@ -1,9 +1,9 @@ -import {getDNT} from '../libraries/dnt/index.js'; import { deepAccess, deepSetValue, mergeDeep, logWarn, generateUUID } from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER } from '../src/mediaTypes.js' -import {config} from '../src/config.js'; -import {tryAppendQueryString} from '../libraries/urlUtils/urlUtils.js'; +import { config } from '../src/config.js'; +import { tryAppendQueryString } from '../libraries/urlUtils/urlUtils.js'; +import { getDNT } from '../libraries/dnt/index.js'; const BIDDER_CODE = 'connectad'; const BIDDER_CODE_ALIAS = 'connectadrealtime'; @@ -13,7 +13,7 @@ const SUPPORTED_MEDIA_TYPES = [BANNER]; export const spec = { code: BIDDER_CODE, gvlid: 138, - aliases: [ BIDDER_CODE_ALIAS ], + aliases: [BIDDER_CODE_ALIAS], supportedMediaTypes: SUPPORTED_MEDIA_TYPES, isBidRequestValid: function(bid) { diff --git a/modules/consentManagementGpp.ts b/modules/consentManagementGpp.ts index fcbcaeb664c..0f7c5b38550 100644 --- a/modules/consentManagementGpp.ts +++ b/modules/consentManagementGpp.ts @@ -4,15 +4,15 @@ * and make it available for any GPP supported adapters to read/pass this information to * their system and for various other features/modules in Prebid.js. */ -import {deepSetValue, isEmpty, isPlainObject, isStr, logInfo, logWarn} from '../src/utils.js'; -import {config} from '../src/config.js'; -import {gppDataHandler} from '../src/adapterManager.js'; -import {enrichFPD} from '../src/fpd/enrichment.js'; -import {cmpClient, MODE_CALLBACK} from '../libraries/cmp/cmpClient.js'; -import {PbPromise, defer} from '../src/utils/promise.js'; -import {type CMConfig, configParser} from '../libraries/consentManagement/cmUtils.js'; -import {createCmpEventManager, type CmpEventManager} from '../libraries/cmp/cmpEventUtils.js'; -import {CONSENT_GPP} from "../src/consentHandler.ts"; +import { deepSetValue, isEmpty, isPlainObject, isStr, logInfo, logWarn } from '../src/utils.js'; +import { config } from '../src/config.js'; +import { gppDataHandler } from '../src/adapterManager.js'; +import { enrichFPD } from '../src/fpd/enrichment.js'; +import { cmpClient, MODE_CALLBACK } from '../libraries/cmp/cmpClient.js'; +import { PbPromise, defer } from '../src/utils/promise.js'; +import { type CMConfig, configParser } from '../libraries/consentManagement/cmUtils.js'; +import { createCmpEventManager, type CmpEventManager } from '../libraries/cmp/cmpEventUtils.js'; +import { CONSENT_GPP } from "../src/consentHandler.ts"; export let consentConfig = {} as any; @@ -142,7 +142,7 @@ export class GPPClient { } refresh() { - return this.cmp({command: 'ping'}).then(this.init.bind(this)); + return this.cmp({ command: 'ping' }).then(this.init.bind(this)); } /** diff --git a/modules/consentManagementTcf.ts b/modules/consentManagementTcf.ts index 673a2d6f269..48fe05e1723 100644 --- a/modules/consentManagementTcf.ts +++ b/modules/consentManagementTcf.ts @@ -4,16 +4,16 @@ * and make it available for any GDPR supported adapters to read/pass this information to * their system. */ -import {deepSetValue, isStr, logInfo} from '../src/utils.js'; -import {config} from '../src/config.js'; -import {gdprDataHandler} from '../src/adapterManager.js'; -import {registerOrtbProcessor, REQUEST} from '../src/pbjsORTB.js'; -import {enrichFPD} from '../src/fpd/enrichment.js'; -import {cmpClient} from '../libraries/cmp/cmpClient.js'; -import {configParser} from '../libraries/consentManagement/cmUtils.js'; -import {createCmpEventManager, type CmpEventManager} from '../libraries/cmp/cmpEventUtils.js'; -import {CONSENT_GDPR} from "../src/consentHandler.ts"; -import type {CMConfig} from "../libraries/consentManagement/cmUtils.ts"; +import { deepSetValue, isStr, logInfo } from '../src/utils.js'; +import { config } from '../src/config.js'; +import { gdprDataHandler } from '../src/adapterManager.js'; +import { registerOrtbProcessor, REQUEST } from '../src/pbjsORTB.js'; +import { enrichFPD } from '../src/fpd/enrichment.js'; +import { cmpClient } from '../libraries/cmp/cmpClient.js'; +import { configParser } from '../libraries/consentManagement/cmUtils.js'; +import { createCmpEventManager, type CmpEventManager } from '../libraries/cmp/cmpEventUtils.js'; +import { CONSENT_GDPR } from "../src/consentHandler.ts"; +import type { CMConfig } from "../libraries/consentManagement/cmUtils.ts"; export let consentConfig: any = {}; export let gdprScope; @@ -145,7 +145,7 @@ function parseConsentData(consentObject): TCFConsentData { } if (checkData()) { - throw Object.assign(new Error(`CMP returned unexpected value during lookup process.`), {args: [consentObject]}) + throw Object.assign(new Error(`CMP returned unexpected value during lookup process.`), { args: [consentObject] }) } else { return toConsentData(consentObject); } @@ -203,7 +203,7 @@ export function setConsentConfig(config) { } gdprScope = tcfConfig?.defaultGdprScope === true; dsaPlatform = !!tcfConfig?.dsaPlatform; - consentConfig = parseConfig({gdpr: tcfConfig}); + consentConfig = parseConfig({ gdpr: tcfConfig }); return consentConfig.loadConsentData?.()?.catch?.(() => null); } config.getConfig('consentManagement', config => setConsentConfig(config.consentManagement)); @@ -234,4 +234,4 @@ export function setOrtbAdditionalConsent(ortbRequest, bidderRequest) { } } -registerOrtbProcessor({type: REQUEST, name: 'gdprAddtlConsent', fn: setOrtbAdditionalConsent}) +registerOrtbProcessor({ type: REQUEST, name: 'gdprAddtlConsent', fn: setOrtbAdditionalConsent }) diff --git a/modules/consentManagementUsp.ts b/modules/consentManagementUsp.ts index 2485885e476..dee29fff6fb 100644 --- a/modules/consentManagementUsp.ts +++ b/modules/consentManagementUsp.ts @@ -4,15 +4,15 @@ * information and make it available for any USP (CCPA) supported adapters to * read/pass this information to their system. */ -import {deepSetValue, isNumber, isPlainObject, isStr, logError, logInfo, logWarn} from '../src/utils.js'; -import {config} from '../src/config.js'; -import adapterManager, {uspDataHandler} from '../src/adapterManager.js'; -import {timedAuctionHook} from '../src/utils/perfMetrics.js'; -import {getHook} from '../src/hook.js'; -import {enrichFPD} from '../src/fpd/enrichment.js'; -import {cmpClient} from '../libraries/cmp/cmpClient.js'; -import type {IABCMConfig, StaticCMConfig} from "../libraries/consentManagement/cmUtils.ts"; -import type {CONSENT_USP} from "../src/consentHandler.ts"; +import { deepSetValue, isNumber, isPlainObject, isStr, logError, logInfo, logWarn } from '../src/utils.js'; +import { config } from '../src/config.js'; +import adapterManager, { uspDataHandler } from '../src/adapterManager.js'; +import { timedAuctionHook } from '../src/utils/perfMetrics.js'; +import { getHook } from '../src/hook.js'; +import { enrichFPD } from '../src/fpd/enrichment.js'; +import { cmpClient } from '../libraries/cmp/cmpClient.js'; +import type { IABCMConfig, StaticCMConfig } from "../libraries/consentManagement/cmUtils.ts"; +import type { CONSENT_USP } from "../src/consentHandler.ts"; const DEFAULT_CONSENT_API = 'iab'; const DEFAULT_CONSENT_TIMEOUT = 50; @@ -59,8 +59,8 @@ const uspCallMap = { /** * This function reads the consent string from the config to obtain the consent information of the user. */ -function lookupStaticConsentData({onSuccess, onError}) { - processUspData(staticConsentData, {onSuccess, onError}); +function lookupStaticConsentData({ onSuccess, onError }) { + processUspData(staticConsentData, { onSuccess, onError }); } /** @@ -68,13 +68,13 @@ function lookupStaticConsentData({onSuccess, onError}) { * Given the async nature of the USP's API, we pass in acting success/error callback functions to exit this function * based on the appropriate result. */ -function lookupUspConsent({onSuccess, onError}) { +function lookupUspConsent({ onSuccess, onError }) { function handleUspApiResponseCallbacks() { const uspResponse = {} as any; function afterEach() { if (uspResponse.usPrivacy) { - processUspData(uspResponse, {onSuccess, onError}) + processUspData(uspResponse, { onSuccess, onError }) } else { onError('Unable to get USP consent string.'); } @@ -197,7 +197,7 @@ export const requestBidsHook = timedAuctionHook('usp', function requestBidsHook( * @param {function(string): void} callbacks.onSuccess - Callback accepting the resolved USP consent string. * @param {function(string, ...Object?): void} callbacks.onError - Callback accepting an error message and any extra error arguments (used purely for logging). */ -function processUspData(consentObject, {onSuccess, onError}) { +function processUspData(consentObject, { onSuccess, onError }) { const valid = !!(consentObject && consentObject.usPrivacy); if (!valid) { onError(`USPAPI returned unexpected value during lookup process.`, consentObject); diff --git a/modules/consumableBidAdapter.js b/modules/consumableBidAdapter.js index 8a565788764..b8a19ab3141 100644 --- a/modules/consumableBidAdapter.js +++ b/modules/consumableBidAdapter.js @@ -1,5 +1,5 @@ import { logWarn, deepAccess, isArray, deepSetValue, isFn, isPlainObject } from '../src/utils.js'; -import {config} from '../src/config.js'; +import { config } from '../src/config.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; diff --git a/modules/contxtfulRtdProvider.js b/modules/contxtfulRtdProvider.js index 4c319c9b48a..f711c300b8b 100644 --- a/modules/contxtfulRtdProvider.js +++ b/modules/contxtfulRtdProvider.js @@ -292,6 +292,7 @@ function getDivIdPosition(divId) { let domElement; + // TODO: this should use getAdUnitElement if (inIframe() === true) { const ws = getWindowSelf(); const currentElement = ws.document.getElementById(divId); @@ -335,6 +336,7 @@ function tryGetDivIdPosition(divIdMethod) { return undefined; } +// TODO unified adUnit/element association in 11 function tryMultipleDivIdPositions(adUnit) { const divMethods = [ // ortb2\ diff --git a/modules/conversantBidAdapter.ts b/modules/conversantBidAdapter.ts index 8563611b337..7d6d56788af 100644 --- a/modules/conversantBidAdapter.ts +++ b/modules/conversantBidAdapter.ts @@ -10,10 +10,10 @@ import { mergeDeep, parseUrl, } from '../src/utils.js'; -import {type BidderSpec, registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js'; -import {ortbConverter} from '../libraries/ortbConverter/converter.js'; -import {ORTB_MTYPES} from '../libraries/ortbConverter/processors/mediaType.js'; +import { type BidderSpec, registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; +import { ortbConverter } from '../libraries/ortbConverter/converter.js'; +import { ORTB_MTYPES } from '../libraries/ortbConverter/processors/mediaType.js'; // Maintainer: mediapsr@epsilon.com @@ -126,7 +126,7 @@ const converter = ortbConverter({ if (bidRequest.mediaTypes && !bidRequest.mediaTypes.banner) return; if (bidRequest.params.position) { // fillBannerImp looks for mediaTypes.banner.pos so put it under the right name here - mergeDeep(bidRequest, {mediaTypes: {banner: {pos: bidRequest.params.position}}}); + mergeDeep(bidRequest, { mediaTypes: { banner: { pos: bidRequest.params.position } } }); } fillBannerImp(imp, bidRequest, context); }, @@ -183,7 +183,7 @@ export const spec: BidderSpec = { }, buildRequests: function(bidRequests, bidderRequest) { - const payload = converter.toORTB({bidderRequest, bidRequests}); + const payload = converter.toORTB({ bidderRequest, bidRequests }); return { method: 'POST', url: makeBidUrl(bidRequests[0]), @@ -198,7 +198,7 @@ export const spec: BidderSpec = { * @return {Bid[]} An array of bids which were nested inside the server. */ interpretResponse: function(serverResponse, bidRequest) { - return converter.fromORTB({request: bidRequest.data, response: serverResponse.body}); + return converter.fromORTB({ request: bidRequest.data, response: serverResponse.body }); }, /** @@ -228,7 +228,7 @@ export const spec: BidderSpec = { responses.forEach(response => { if (response?.body?.ext) { const ext = response.body.ext; - const pixels = [{urls: ext.fsyncs, type: 'iframe'}, {urls: ext.psyncs, type: 'image'}] + const pixels = [{ urls: ext.fsyncs, type: 'iframe' }, { urls: ext.psyncs, type: 'image' }] .filter((entry) => { return entry.urls && Array.isArray(entry.urls) && entry.urls.length > 0 && @@ -242,7 +242,7 @@ export const spec: BidderSpec = { if (Object.keys(urlInfo.search).length === 0) { delete urlInfo.search; } - return {type: entry.type, url: buildUrl(urlInfo)}; + return { type: entry.type, url: buildUrl(urlInfo) }; }) .reduce((x, y) => x.concat(y), []); }) diff --git a/modules/craftBidAdapter.js b/modules/craftBidAdapter.js index cbb3d047e0b..34d2bb35f17 100644 --- a/modules/craftBidAdapter.js +++ b/modules/craftBidAdapter.js @@ -1,17 +1,17 @@ -import {getBidRequest} from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js'; -import {getStorageManager} from '../src/storageManager.js'; -import {ajax} from '../src/ajax.js'; -import {hasPurpose1Consent} from '../src/utils/gdpr.js'; -import {convertOrtbRequestToProprietaryNative} from '../src/native.js'; -import {getANKeywordParam} from '../libraries/appnexusUtils/anKeywords.js'; -import {interpretResponseUtil} from '../libraries/interpretResponseUtils/index.js'; +import { getBidRequest } from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; +import { getStorageManager } from '../src/storageManager.js'; +import { ajax } from '../src/ajax.js'; +import { hasPurpose1Consent } from '../src/utils/gdpr.js'; +import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; +import { getANKeywordParam } from '../libraries/appnexusUtils/anKeywords.js'; +import { interpretResponseUtil } from '../libraries/interpretResponseUtils/index.js'; const BIDDER_CODE = 'craft'; const URL_BASE = 'https://gacraft.jp/prebid-v3'; const TTL = 360; -const storage = getStorageManager({bidderCode: BIDDER_CODE}); +const storage = getStorageManager({ bidderCode: BIDDER_CODE }); export const spec = { code: BIDDER_CODE, @@ -54,7 +54,8 @@ export const spec = { // TODO: this collects everything it finds, except for the canonical URL rd_ref: bidderRequest.refererInfo.topmostLocation, rd_top: bidderRequest.refererInfo.reachedTop, - rd_ifs: bidderRequest.refererInfo.numIframes}; + rd_ifs: bidderRequest.refererInfo.numIframes + }; if (bidderRequest.refererInfo.stack) { refererinfo.rd_stk = bidderRequest.refererInfo.stack.join(','); } @@ -68,9 +69,9 @@ export const spec = { return request; }, - interpretResponse: function(serverResponse, {bidderRequest}) { + interpretResponse: function(serverResponse, { bidderRequest }) { try { - const bids = interpretResponseUtil(serverResponse, {bidderRequest}, serverBid => { + const bids = interpretResponseUtil(serverResponse, { bidderRequest }, serverBid => { const rtbBid = getRtbBid(serverBid); if (rtbBid && rtbBid.cpm !== 0 && this.supportedMediaTypes.includes(rtbBid.ad_type)) { const bid = newBid(serverBid, rtbBid, bidderRequest); diff --git a/modules/criteoBidAdapter.js b/modules/criteoBidAdapter.js index 9965cd1cb2b..56fc19ef7be 100644 --- a/modules/criteoBidAdapter.js +++ b/modules/criteoBidAdapter.js @@ -1,15 +1,15 @@ -import {deepSetValue, isArray, logError, logWarn, parseUrl, triggerPixel, deepAccess, logInfo} from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js'; -import {getStorageManager} from '../src/storageManager.js'; -import {getRefererInfo} from '../src/refererDetection.js'; -import {hasPurpose1Consent} from '../src/utils/gdpr.js'; -import {Renderer} from '../src/Renderer.js'; -import {OUTSTREAM} from '../src/video.js'; -import {ajax} from '../src/ajax.js'; -import {ortbConverter} from '../libraries/ortbConverter/converter.js'; -import {ortb25Translator} from '../libraries/ortb2.5Translator/translator.js'; -import {config} from '../src/config.js'; +import { deepAccess, deepSetValue, logError, logInfo, logWarn, parseUrl, triggerPixel } from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; +import { getStorageManager } from '../src/storageManager.js'; +import { getRefererInfo } from '../src/refererDetection.js'; +import { hasPurpose1Consent } from '../src/utils/gdpr.js'; +import { Renderer } from '../src/Renderer.js'; +import { OUTSTREAM } from '../src/video.js'; +import { ajax } from '../src/ajax.js'; +import { ortbConverter } from '../libraries/ortbConverter/converter.js'; +import { ortb25Translator } from '../libraries/ortb2.5Translator/translator.js'; +import { config } from '../src/config.js'; /** * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest @@ -76,10 +76,6 @@ function imp(buildImp, bidRequest, context) { delete imp.rwdd // oRTB 2.6 field moved to ext - if (!context.fledgeEnabled && imp.ext.igs?.ae) { - delete imp.ext.igs.ae; - } - if (hasVideoMediaType(bidRequest)) { const paramsVideo = bidRequest.params.video; if (paramsVideo !== undefined) { @@ -165,7 +161,7 @@ function bidResponse(buildBidResponse, bid, context) { } const bidResponse = buildBidResponse(bid, context); - const {bidRequest} = context; + const { bidRequest } = context; bidResponse.currency = bid?.ext?.cur; @@ -374,7 +370,7 @@ export const spec = { const context = buildContext(bidRequests, bidderRequest); const url = buildCdbUrl(context); - const data = CONVERTER.toORTB({bidderRequest, bidRequests, context}); + const data = CONVERTER.toORTB({ bidderRequest, bidRequests, context }); if (data) { return { @@ -392,26 +388,15 @@ export const spec = { /** * @param {*} response * @param {ServerRequest} request - * @return {Bid[] | {bids: Bid[], fledgeAuctionConfigs: object[]}} + * @return {Bid[] | {bids: Bid[]}} */ interpretResponse: (response, request) => { if (typeof response?.body === 'undefined') { return []; // no bid } - const interpretedResponse = CONVERTER.fromORTB({response: response.body, request: request.data}); - const bids = interpretedResponse.bids || []; - - const fledgeAuctionConfigs = response.body?.ext?.igi?.filter(igi => isArray(igi?.igs)) - .flatMap(igi => igi.igs); - if (fledgeAuctionConfigs?.length) { - return { - bids, - paapi: fledgeAuctionConfigs, - }; - } - - return bids; + const interpretedResponse = CONVERTER.fromORTB({ response: response.body, request: request.data }); + return interpretedResponse.bids || []; }, /** @@ -503,7 +488,6 @@ function buildContext(bidRequests, bidderRequest) { url: bidderRequest?.refererInfo?.page || '', debug: queryString['pbt_debug'] === '1', noLog: queryString['pbt_nolog'] === '1', - fledgeEnabled: bidderRequest.paapi?.enabled, amp: bidRequests.some(bidRequest => bidRequest.params.integrationMode === 'amp'), networkId: bidRequests.find(bidRequest => bidRequest.params?.networkId)?.params.networkId, publisherId: bidRequests.find(bidRequest => bidRequest.params?.pubid)?.params.pubid, diff --git a/modules/currency.ts b/modules/currency.ts index 34b29d94047..a45c79ef9ec 100644 --- a/modules/currency.ts +++ b/modules/currency.ts @@ -1,17 +1,17 @@ -import {deepSetValue, logError, logInfo, logMessage, logWarn} from '../src/utils.js'; -import {getGlobal} from '../src/prebidGlobal.js'; +import { deepSetValue, logError, logInfo, logMessage, logWarn } from '../src/utils.js'; +import { getGlobal } from '../src/prebidGlobal.js'; import { EVENTS, REJECTION_REASON } from '../src/constants.js'; -import {ajax} from '../src/ajax.js'; -import {config} from '../src/config.js'; -import {getHook} from '../src/hook.js'; -import {defer} from '../src/utils/promise.js'; -import {registerOrtbProcessor, REQUEST} from '../src/pbjsORTB.js'; -import {timedAuctionHook, timedBidResponseHook} from '../src/utils/perfMetrics.js'; -import {on as onEvent, off as offEvent} from '../src/events.js'; +import { ajax } from '../src/ajax.js'; +import { config } from '../src/config.js'; +import { getHook } from '../src/hook.js'; +import { defer } from '../src/utils/promise.js'; +import { registerOrtbProcessor, REQUEST } from '../src/pbjsORTB.js'; +import { timedAuctionHook, timedBidResponseHook } from '../src/utils/perfMetrics.js'; +import { on as onEvent, off as offEvent } from '../src/events.js'; import { enrichFPD } from '../src/fpd/enrichment.js'; import { timeoutQueue } from '../libraries/timeoutQueue/timeoutQueue.js'; -import type {Currency, BidderCode} from "../src/types/common.d.ts"; -import {addApiMethod} from "../src/prebid.ts"; +import type { Currency, BidderCode } from "../src/types/common.d.ts"; +import { addApiMethod } from "../src/prebid.ts"; const DEFAULT_CURRENCY_RATE_URL = 'https://cdn.jsdelivr.net/gh/prebid/currency-file@1/latest.json?date=$$TODAY$$'; const CURRENCY_RATE_PRECISION = 4; @@ -218,10 +218,10 @@ function initCurrency() { export function resetCurrency() { if (currencySupportEnabled) { - getHook('addBidResponse').getHooks({hook: addBidResponseHook}).remove(); - getHook('responsesReady').getHooks({hook: responsesReadyHook}).remove(); - enrichFPD.getHooks({hook: enrichFPDHook}).remove(); - getHook('requestBids').getHooks({hook: requestBidsHook}).remove(); + getHook('addBidResponse').getHooks({ hook: addBidResponseHook }).remove(); + getHook('responsesReady').getHooks({ hook: responsesReadyHook }).remove(); + enrichFPD.getHooks({ hook: enrichFPDHook }).remove(); + getHook('requestBids').getHooks({ hook: requestBidsHook }).remove(); offEvent(EVENTS.AUCTION_TIMEOUT, rejectOnAuctionTimeout); offEvent(EVENTS.AUCTION_INIT, loadRates); delete getGlobal().convertCurrency; @@ -287,7 +287,7 @@ export const addBidResponseHook = timedBidResponseHook('currency', function addB } }); -function rejectOnAuctionTimeout({auctionId}) { +function rejectOnAuctionTimeout({ auctionId }) { bidResponseQueue = bidResponseQueue.filter(([fn, ctx, adUnitCode, bid, reject]) => { if (bid.auctionId === auctionId) { reject(REJECTION_REASON.CANNOT_CONVERT_CURRENCY) @@ -395,7 +395,7 @@ export function setOrtbCurrency(ortbRequest, bidderRequest, context) { } } -registerOrtbProcessor({type: REQUEST, name: 'currency', fn: setOrtbCurrency}); +registerOrtbProcessor({ type: REQUEST, name: 'currency', fn: setOrtbCurrency }); function enrichFPDHook(next, fpd) { return next(fpd.then(ortb2 => { diff --git a/modules/cwireBidAdapter.js b/modules/cwireBidAdapter.js index 875dfdedf67..46647b31c58 100644 --- a/modules/cwireBidAdapter.js +++ b/modules/cwireBidAdapter.js @@ -11,6 +11,7 @@ import { getBoundingClientRect } from "../libraries/boundingClientRect/boundingC import { hasPurpose1Consent } from "../src/utils/gdpr.js"; import { sendBeacon } from "../src/ajax.js"; import { isAutoplayEnabled } from "../libraries/autoplayDetection/autoplay.js"; +import { getAdUnitElement } from '../src/utils/adUnits.js'; /** * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest @@ -35,7 +36,7 @@ export const storage = getStorageManager({ bidderCode: BIDDER_CODE }); */ function slotDimensions(bid) { const adUnitCode = bid.adUnitCode; - const slotEl = document.getElementById(adUnitCode); + const slotEl = getAdUnitElement(bid); if (slotEl) { logInfo(`Slot element found: ${adUnitCode}`); diff --git a/modules/czechAdIdSystem.js b/modules/czechAdIdSystem.js index 62141dd7d62..854d16de73a 100644 --- a/modules/czechAdIdSystem.js +++ b/modules/czechAdIdSystem.js @@ -6,8 +6,8 @@ */ import { submodule } from '../src/hook.js' -import {getStorageManager} from '../src/storageManager.js'; -import {MODULE_TYPE_UID} from '../src/activities/modules.js'; +import { getStorageManager } from '../src/storageManager.js'; +import { MODULE_TYPE_UID } from '../src/activities/modules.js'; /** * @typedef {import('../modules/userId/index.js').Submodule} Submodule diff --git a/modules/dacIdSystem.js b/modules/dacIdSystem.js index ffdadef18e8..c2ebe29c5ad 100644 --- a/modules/dacIdSystem.js +++ b/modules/dacIdSystem.js @@ -19,9 +19,9 @@ import { import { getStorageManager } from '../src/storageManager.js'; -import {MODULE_TYPE_UID} from '../src/activities/modules.js'; +import { MODULE_TYPE_UID } from '../src/activities/modules.js'; const MODULE_NAME = 'dacId'; -export const storage = getStorageManager({moduleType: MODULE_TYPE_UID, moduleName: MODULE_NAME}); +export const storage = getStorageManager({ moduleType: MODULE_TYPE_UID, moduleName: MODULE_NAME }); export const FUUID_COOKIE_NAME = '_a1_f'; export const AONEID_COOKIE_NAME = '_a1_d'; diff --git a/modules/dailyhuntBidAdapter.js b/modules/dailyhuntBidAdapter.js index 7337c5417d3..6082cd3ee1d 100644 --- a/modules/dailyhuntBidAdapter.js +++ b/modules/dailyhuntBidAdapter.js @@ -1,10 +1,10 @@ -import {registerBidder} from '../src/adapters/bidderFactory.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; import * as mediaTypes from '../src/mediaTypes.js'; -import {_map, deepAccess, isEmpty} from '../src/utils.js'; -import {ajax} from '../src/ajax.js'; -import {INSTREAM, OUTSTREAM} from '../src/video.js'; -import {convertOrtbRequestToProprietaryNative} from '../src/native.js'; -import {parseNativeResponse, getBidFloor} from '../libraries/nexverseUtils/index.js'; +import { _map, deepAccess, isEmpty } from '../src/utils.js'; +import { ajax } from '../src/ajax.js'; +import { INSTREAM, OUTSTREAM } from '../src/video.js'; +import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; +import { parseNativeResponse, getBidFloor } from '../libraries/nexverseUtils/index.js'; const BIDDER_CODE = 'dailyhunt'; const BIDDER_ALIAS = 'dh'; @@ -47,7 +47,8 @@ const ORTB_NATIVE_PARAMS = { id: 4, name: 'data', type: 10 - }}; + } +}; // Extract key from collections. const extractKeyInfo = (collection, key) => { @@ -212,17 +213,17 @@ const createOrtbImpVideoObj = (bid, videoObj) => { return obj; } -export function getProtocols({protocols}) { +export function getProtocols({ protocols }) { const defaultValue = [2, 3, 5, 6, 7, 8]; const listProtocols = [ - {key: 'VAST_1_0', value: 1}, - {key: 'VAST_2_0', value: 2}, - {key: 'VAST_3_0', value: 3}, - {key: 'VAST_1_0_WRAPPER', value: 4}, - {key: 'VAST_2_0_WRAPPER', value: 5}, - {key: 'VAST_3_0_WRAPPER', value: 6}, - {key: 'VAST_4_0', value: 7}, - {key: 'VAST_4_0_WRAPPER', value: 8} + { key: 'VAST_1_0', value: 1 }, + { key: 'VAST_2_0', value: 2 }, + { key: 'VAST_3_0', value: 3 }, + { key: 'VAST_1_0_WRAPPER', value: 4 }, + { key: 'VAST_2_0_WRAPPER', value: 5 }, + { key: 'VAST_3_0_WRAPPER', value: 6 }, + { key: 'VAST_4_0', value: 7 }, + { key: 'VAST_4_0_WRAPPER', value: 8 } ]; if (protocols) { return listProtocols.filter(p => { diff --git a/modules/dataControllerModule/index.js b/modules/dataControllerModule/index.js index 7c88eae029a..9131ada680c 100644 --- a/modules/dataControllerModule/index.js +++ b/modules/dataControllerModule/index.js @@ -2,11 +2,11 @@ * This module validates the configuration and filters data accordingly * @module modules/dataController */ -import {config} from '../../src/config.js'; -import {getHook, module} from '../../src/hook.js'; -import {deepAccess, deepSetValue, prefixLog} from '../../src/utils.js'; -import {startAuction} from '../../src/prebid.js'; -import {timedAuctionHook} from '../../src/utils/perfMetrics.js'; +import { config } from '../../src/config.js'; +import { getHook, module } from '../../src/hook.js'; +import { deepAccess, deepSetValue, prefixLog } from '../../src/utils.js'; +import { startAuction } from '../../src/prebid.js'; +import { timedAuctionHook } from '../../src/utils/perfMetrics.js'; const LOG_PRE_FIX = 'Data_Controller : '; const ALL = '*'; @@ -163,13 +163,13 @@ export function init() { const dataController = dataControllerConfig && dataControllerConfig.dataController; if (!dataController) { _logger.logInfo(`Data Controller is not configured`); - startAuction.getHooks({hook: filterBidData}).remove(); + startAuction.getHooks({ hook: filterBidData }).remove(); return; } if (dataController.filterEIDwhenSDA && dataController.filterSDAwhenEID) { _logger.logInfo(`Data Controller can be configured with either filterEIDwhenSDA or filterSDAwhenEID`); - startAuction.getHooks({hook: filterBidData}).remove(); + startAuction.getHooks({ hook: filterBidData }).remove(); return; } confListener(); // unsubscribe config listener diff --git a/modules/datablocksBidAdapter.js b/modules/datablocksBidAdapter.js index 7f5a4bedd62..fdd4a14342b 100644 --- a/modules/datablocksBidAdapter.js +++ b/modules/datablocksBidAdapter.js @@ -1,16 +1,16 @@ -import {getDevicePixelRatio} from '../libraries/devicePixelRatio/devicePixelRatio.js'; -import {deepAccess, getWinDimensions, getWindowTop, isGptPubadsDefined} from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {config} from '../src/config.js'; -import {BANNER, NATIVE} from '../src/mediaTypes.js'; -import {getStorageManager} from '../src/storageManager.js'; -import {ajax} from '../src/ajax.js'; -import {convertOrtbRequestToProprietaryNative} from '../src/native.js'; -import {getAdUnitSizes} from '../libraries/sizeUtils/sizeUtils.js'; -import {isWebdriverEnabled} from '../libraries/webdriver/webdriver.js'; +import { getDevicePixelRatio } from '../libraries/devicePixelRatio/devicePixelRatio.js'; +import { deepAccess, getWinDimensions, getWindowTop, isGptPubadsDefined } from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { config } from '../src/config.js'; +import { BANNER, NATIVE } from '../src/mediaTypes.js'; +import { getStorageManager } from '../src/storageManager.js'; +import { ajax } from '../src/ajax.js'; +import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; +import { getAdUnitSizes } from '../libraries/sizeUtils/sizeUtils.js'; +import { isWebdriverEnabled, isSeleniumDetected } from '../libraries/webdriver/webdriver.js'; import { buildNativeRequest, parseNativeResponse } from '../libraries/nativeAssetsUtils.js'; -export const storage = getStorageManager({bidderCode: 'datablocks'}); +export const storage = getStorageManager({ bidderCode: 'datablocks' }); // DEFINE THE PREBID BIDDER SPEC export const spec = { @@ -18,7 +18,7 @@ export const spec = { code: 'datablocks', // DATABLOCKS SCOPED OBJECT - db_obj: {metrics_host: 'prebid.dblks.net', metrics: [], metrics_timer: null, metrics_queue_time: 1000, vis_optout: false, source_id: 0}, + db_obj: { metrics_host: 'prebid.dblks.net', metrics: [], metrics_timer: null, metrics_queue_time: 1000, vis_optout: false, source_id: 0 }, // STORE THE DATABLOCKS BUYERID IN STORAGE store_dbid: function(dbid) { @@ -112,7 +112,7 @@ export const spec = { // POST CONSOLIDATED METRICS BACK TO SERVER send_metrics: function() { // POST TO SERVER - ajax(`https://${this.db_obj.metrics_host}/a/pb/`, null, JSON.stringify(this.db_obj.metrics), {method: 'POST', withCredentials: true}); + ajax(`https://${this.db_obj.metrics_host}/a/pb/`, null, JSON.stringify(this.db_obj.metrics), { method: 'POST', withCredentials: true }); // RESET THE QUEUE OF METRIC DATA this.db_obj.metrics = []; @@ -155,10 +155,10 @@ export const spec = { if (typeof window['googletag'].pubads().addEventListener === 'function') { // TODO: fix auctionId leak: https://github.com/prebid/Prebid.js/issues/9781 window['googletag'].pubads().addEventListener('impressionViewable', function(event) { - scope.queue_metric({type: 'slot_view', source_id: scope.db_obj.source_id, auction_id: bid.auctionId, div_id: event.slot.getSlotElementId(), slot_id: event.slot.getSlotId().getAdUnitPath()}); + scope.queue_metric({ type: 'slot_view', source_id: scope.db_obj.source_id, auction_id: bid.auctionId, div_id: event.slot.getSlotElementId(), slot_id: event.slot.getSlotId().getAdUnitPath() }); }); window['googletag'].pubads().addEventListener('slotRenderEnded', function(event) { - scope.queue_metric({type: 'slot_render', source_id: scope.db_obj.source_id, auction_id: bid.auctionId, div_id: event.slot.getSlotElementId(), slot_id: event.slot.getSlotId().getAdUnitPath()}); + scope.queue_metric({ type: 'slot_render', source_id: scope.db_obj.source_id, auction_id: bid.auctionId, div_id: event.slot.getSlotElementId(), slot_id: event.slot.getSlotId().getAdUnitPath() }); }) } } @@ -386,7 +386,7 @@ export const spec = { // DATABLOCKS WON THE AUCTION - REPORT SUCCESS onBidWon: function(bid) { - this.queue_metric({type: 'bid_won', source_id: bid.params[0].source_id, req_id: bid.requestId, slot_id: bid.adUnitCode, auction_id: bid.auctionId, size: bid.size, cpm: bid.cpm, pb: bid.adserverTargeting.hb_pb, rt: bid.timeToRespond, ttl: bid.ttl}); + this.queue_metric({ type: 'bid_won', source_id: bid.params[0].source_id, req_id: bid.requestId, slot_id: bid.adUnitCode, auction_id: bid.auctionId, size: bid.size, cpm: bid.cpm, pb: bid.adserverTargeting.hb_pb, rt: bid.timeToRespond, ttl: bid.ttl }); }, // TARGETING HAS BEEN SET @@ -400,17 +400,17 @@ export const spec = { const bids = []; const resBids = deepAccess(rtbResponse, 'body.seatbid') || []; resBids.forEach(bid => { - const resultItem = {requestId: bid.id, cpm: bid.price, creativeId: bid.crid, currency: bid.currency || 'USD', netRevenue: true, ttl: bid.ttl || 360, meta: {advertiserDomains: bid.adomain}}; + const resultItem = { requestId: bid.id, cpm: bid.price, creativeId: bid.crid, currency: bid.currency || 'USD', netRevenue: true, ttl: bid.ttl || 360, meta: { advertiserDomains: bid.adomain } }; const mediaType = deepAccess(bid, 'ext.mtype') || ''; switch (mediaType) { case 'banner': - bids.push(Object.assign({}, resultItem, {mediaType: BANNER, width: bid.w, height: bid.h, ad: bid.adm})); + bids.push(Object.assign({}, resultItem, { mediaType: BANNER, width: bid.w, height: bid.h, ad: bid.adm })); break; case 'native': const nativeResult = JSON.parse(bid.adm); - bids.push(Object.assign({}, resultItem, {mediaType: NATIVE, native: parseNativeResponse(nativeResult.native)})); + bids.push(Object.assign({}, resultItem, { mediaType: NATIVE, native: parseNativeResponse(nativeResult.native) })); break; default: @@ -432,40 +432,11 @@ export class BotClientTests { }, selenium: function () { - let response = false; - - if (window && document) { - const results = [ - 'webdriver' in window, - '_Selenium_IDE_Recorder' in window, - 'callSelenium' in window, - '_selenium' in window, - '__webdriver_script_fn' in document, - '__driver_evaluate' in document, - '__webdriver_evaluate' in document, - '__selenium_evaluate' in document, - '__fxdriver_evaluate' in document, - '__driver_unwrapped' in document, - '__webdriver_unwrapped' in document, - '__selenium_unwrapped' in document, - '__fxdriver_unwrapped' in document, - '__webdriver_script_func' in document, - document.documentElement.getAttribute('selenium') !== null, - document.documentElement.getAttribute('webdriver') !== null, - document.documentElement.getAttribute('driver') !== null - ]; - - results.forEach(result => { - if (result === true) { - response = true; - } - }) - } - - return response; + return isSeleniumDetected(window, document); }, } } + doTests() { let response = false; for (const i of Object.keys(this.tests)) { diff --git a/modules/datawrkzBidAdapter.js b/modules/datawrkzBidAdapter.js index b75af264935..f09f824486a 100644 --- a/modules/datawrkzBidAdapter.js +++ b/modules/datawrkzBidAdapter.js @@ -176,7 +176,7 @@ function buildNativeRequest(bidRequest, bidderRequest) { assets.push(generateNativeDataObj(body, 'desc', ++counter)); } - const request = JSON.stringify({assets: assets}); + const request = JSON.stringify({ assets: assets }); const native = { request: request }; @@ -277,7 +277,7 @@ function getVideoAdUnitSize(bidRequest) { adH = parseInt(playerSize[0][1]); } } - return {adH: adH, adW: adW} + return { adH: adH, adW: adW } } /* Get mediatype of the adunit from request */ @@ -306,7 +306,7 @@ function generatePayload(imp, bidderRequest) { publisher: {} }; - const regs = {ext: {}}; + const regs = { ext: {} }; if (bidderRequest.uspConsent) { regs.ext.us_privacy = bidderRequest.uspConsent; diff --git a/modules/dchain.ts b/modules/dchain.ts index d2a3199f983..66ef00783e4 100644 --- a/modules/dchain.ts +++ b/modules/dchain.ts @@ -1,8 +1,8 @@ -import {config} from '../src/config.js'; -import {getHook} from '../src/hook.js'; -import {_each, deepAccess, deepClone, isArray, isPlainObject, isStr, logError, logWarn} from '../src/utils.js'; -import {timedBidResponseHook} from '../src/utils/perfMetrics.js'; -import type {DemandChain} from "../src/types/ortb/ext/dchain.d.ts"; +import { config } from '../src/config.js'; +import { getHook } from '../src/hook.js'; +import { _each, deepAccess, deepClone, isArray, isPlainObject, isStr, logError, logWarn } from '../src/utils.js'; +import { timedBidResponseHook } from '../src/utils/perfMetrics.js'; +import type { DemandChain } from "../src/types/ortb/ext/dchain.d.ts"; const shouldBeAString = ' should be a string'; const shouldBeAnObject = ' should be an object'; diff --git a/modules/debugging/bidInterceptor.js b/modules/debugging/bidInterceptor.js index 928fba3f10b..afd644684d7 100644 --- a/modules/debugging/bidInterceptor.js +++ b/modules/debugging/bidInterceptor.js @@ -4,11 +4,11 @@ import makeResponseResolvers from './responses.js'; * @typedef {Number|String|boolean|null|undefined} Scalar */ -export function makebidInterceptor({utils, BANNER, NATIVE, VIDEO, Renderer}) { - const {deepAccess, deepClone, delayExecution, hasNonSerializableProperty, mergeDeep} = utils; - const responseResolvers = makeResponseResolvers({Renderer, BANNER, NATIVE, VIDEO}); +export function makebidInterceptor({ utils, BANNER, NATIVE, VIDEO, Renderer }) { + const { deepAccess, deepClone, delayExecution, hasNonSerializableProperty, mergeDeep } = utils; + const responseResolvers = makeResponseResolvers({ Renderer, BANNER, NATIVE, VIDEO }); function BidInterceptor(opts = {}) { - ({setTimeout: this.setTimeout = window.setTimeout.bind(window)} = opts); + ({ setTimeout: this.setTimeout = window.setTimeout.bind(window) } = opts); this.logger = opts.logger; this.rules = []; } @@ -53,7 +53,6 @@ export function makebidInterceptor({utils, BANNER, NATIVE, VIDEO, Renderer}) { match: this.matcher(ruleDef.when, ruleNo), replace: this.replacer(ruleDef.then, ruleNo), options: Object.assign({}, this.DEFAULT_RULE_OPTIONS, ruleDef.options), - paapi: this.paapiReplacer(ruleDef.paapi || [], ruleNo) } }, /** @@ -78,7 +77,7 @@ export function makebidInterceptor({utils, BANNER, NATIVE, VIDEO, Renderer}) { this.logger.logError(`Invalid 'when' definition for debug bid interceptor (in rule #${ruleNo})`); return () => false; } - function matches(candidate, {ref = matchDef, args = []}) { + function matches(candidate, { ref = matchDef, args = [] }) { return Object.entries(ref).map(([key, val]) => { const cVal = candidate[key]; if (val instanceof RegExp) { @@ -88,12 +87,12 @@ export function makebidInterceptor({utils, BANNER, NATIVE, VIDEO, Renderer}) { return !!val(cVal, ...args); } if (typeof val === 'object') { - return matches(cVal, {ref: val, args}); + return matches(cVal, { ref: val, args }); } return cVal === val; }).every((i) => i); } - return (candidate, ...args) => matches(candidate, {args}); + return (candidate, ...args) => matches(candidate, { args }); }, /** * @typedef {Function} ReplacerFn @@ -116,18 +115,18 @@ export function makebidInterceptor({utils, BANNER, NATIVE, VIDEO, Renderer}) { replDef = replDef || {}; let replFn; if (typeof replDef === 'function') { - replFn = ({args}) => replDef(...args); + replFn = ({ args }) => replDef(...args); } else if (typeof replDef !== 'object') { this.logger.logError(`Invalid 'then' definition for debug bid interceptor (in rule #${ruleNo})`); replFn = () => ({}); } else { - replFn = ({args, ref = replDef}) => { + replFn = ({ args, ref = replDef }) => { const result = Array.isArray(ref) ? [] : {}; Object.entries(ref).forEach(([key, val]) => { if (typeof val === 'function') { result[key] = val(...args); } else if (val != null && typeof val === 'object') { - result[key] = replFn({args, ref: val}) + result[key] = replFn({ args, ref: val }) } else { result[key] = val; } @@ -137,31 +136,13 @@ export function makebidInterceptor({utils, BANNER, NATIVE, VIDEO, Renderer}) { } return (bid, ...args) => { const response = this.responseDefaults(bid); - mergeDeep(response, replFn({args: [bid, ...args]})); + mergeDeep(response, replFn({ args: [bid, ...args] })); const resolver = responseResolvers[response.mediaType]; resolver && resolver(bid, response); response.isDebug = true; return response; } }, - - paapiReplacer(paapiDef, ruleNo) { - function wrap(configs = []) { - return configs.map(config => { - return Object.keys(config).some(k => !['config', 'igb'].includes(k)) - ? {config} - : config - }); - } - if (Array.isArray(paapiDef)) { - return () => wrap(paapiDef); - } else if (typeof paapiDef === 'function') { - return (...args) => wrap(paapiDef(...args)) - } else { - this.logger.logError(`Invalid 'paapi' definition for debug bid interceptor (in rule #${ruleNo})`); - } - }, - responseDefaults(bid) { const response = { requestId: bid.bidId, @@ -210,7 +191,7 @@ export function makebidInterceptor({utils, BANNER, NATIVE, VIDEO, Renderer}) { bids.forEach((bid) => { const rule = this.match(bid, bidRequest); if (rule != null) { - matches.push({rule: rule, bid: bid}); + matches.push({ rule: rule, bid: bid }); } else { remainder.push(bid); } @@ -224,12 +205,11 @@ export function makebidInterceptor({utils, BANNER, NATIVE, VIDEO, Renderer}) { * {{}[]} bids? * {*} bidRequest * {function(*)} addBid called once for each mock response - * addPaapiConfig called once for each mock PAAPI config * {function()} done called once after all mock responses have been run through `addBid` * returns {{bids: {}[], bidRequest: {}} remaining bids that did not match any rule (this applies also to * bidRequest.bids) */ - intercept({bids, bidRequest, addBid, addPaapiConfig, done}) { + intercept({ bids, bidRequest, addBid, done }) { if (bids == null) { bids = bidRequest.bids; } @@ -238,12 +218,10 @@ export function makebidInterceptor({utils, BANNER, NATIVE, VIDEO, Renderer}) { const callDone = delayExecution(done, matches.length); matches.forEach((match) => { const mockResponse = match.rule.replace(match.bid, bidRequest); - const mockPaapi = match.rule.paapi(match.bid, bidRequest); const delay = match.rule.options.delay; - this.logger.logMessage(`Intercepted bid request (matching rule #${match.rule.no}), mocking response in ${delay}ms. Request, response, PAAPI configs:`, match.bid, mockResponse, mockPaapi) + this.logger.logMessage(`Intercepted bid request (matching rule #${match.rule.no}), mocking response in ${delay}ms. Request, response:`, match.bid, mockResponse) this.setTimeout(() => { mockResponse && addBid(mockResponse, match.bid); - mockPaapi.forEach(cfg => addPaapiConfig(cfg, match.bid, bidRequest)); callDone(); }, delay) }); @@ -252,7 +230,7 @@ export function makebidInterceptor({utils, BANNER, NATIVE, VIDEO, Renderer}) { } else { this.setTimeout(done, 0); } - return {bids, bidRequest}; + return { bids, bidRequest }; } }); return BidInterceptor; diff --git a/modules/debugging/debugging.js b/modules/debugging/debugging.js index d5bbc895ae1..022d901264d 100644 --- a/modules/debugging/debugging.js +++ b/modules/debugging/debugging.js @@ -1,29 +1,29 @@ -import {makebidInterceptor} from './bidInterceptor.js'; -import {makePbsInterceptor} from './pbsInterceptor.js'; -import {addHooks, removeHooks} from './legacy.js'; +import { makebidInterceptor } from './bidInterceptor.js'; +import { makePbsInterceptor } from './pbsInterceptor.js'; +import { addHooks, removeHooks } from './legacy.js'; const interceptorHooks = []; let bidInterceptor; let enabled = false; -function enableDebugging(debugConfig, {fromSession = false, config, hook, logger}) { - config.setConfig({debug: true}); +function enableDebugging(debugConfig, { fromSession = false, config, hook, logger }) { + config.setConfig({ debug: true }); bidInterceptor.updateConfig(debugConfig); resetHooks(true); // also enable "legacy" overrides - removeHooks({hook}); - addHooks(debugConfig, {hook, logger}); + removeHooks({ hook }); + addHooks(debugConfig, { hook, logger }); if (!enabled) { enabled = true; logger.logMessage(`Debug overrides enabled${fromSession ? ' from session' : ''}`); } } -export function disableDebugging({hook, logger}) { +export function disableDebugging({ hook, logger }) { bidInterceptor.updateConfig(({})); resetHooks(false); // also disable "legacy" overrides - removeHooks({hook}); + removeHooks({ hook }); if (enabled) { enabled = false; logger.logMessage('Debug overrides disabled'); @@ -31,8 +31,8 @@ export function disableDebugging({hook, logger}) { } // eslint-disable-next-line no-restricted-properties -function saveDebuggingConfig(debugConfig, {sessionStorage = window.sessionStorage, DEBUG_KEY, utils} = {}) { - const {deepClone} = utils; +function saveDebuggingConfig(debugConfig, { sessionStorage = window.sessionStorage, DEBUG_KEY, utils } = {}) { + const { deepClone } = utils; if (!debugConfig.enabled) { try { sessionStorage.removeItem(DEBUG_KEY); @@ -51,7 +51,7 @@ function saveDebuggingConfig(debugConfig, {sessionStorage = window.sessionStorag } // eslint-disable-next-line no-restricted-properties -export function getConfig(debugging, {getStorage = () => window.sessionStorage, DEBUG_KEY, config, hook, logger, utils} = {}) { +export function getConfig(debugging, { getStorage = () => window.sessionStorage, DEBUG_KEY, config, hook, logger, utils } = {}) { if (debugging == null) return; let sessionStorage; try { @@ -60,16 +60,16 @@ export function getConfig(debugging, {getStorage = () => window.sessionStorage, logger.logError(`sessionStorage is not available: debugging configuration will not persist on page reload`, e); } if (sessionStorage != null) { - saveDebuggingConfig(debugging, {sessionStorage, DEBUG_KEY, utils}); + saveDebuggingConfig(debugging, { sessionStorage, DEBUG_KEY, utils }); } if (!debugging.enabled) { - disableDebugging({hook, logger}); + disableDebugging({ hook, logger }); } else { - enableDebugging(debugging, {config, hook, logger}); + enableDebugging(debugging, { config, hook, logger }); } } -export function sessionLoader({DEBUG_KEY, storage, config, hook, logger}) { +export function sessionLoader({ DEBUG_KEY, storage, config, hook, logger }) { let overrides; try { // eslint-disable-next-line no-restricted-properties @@ -78,13 +78,13 @@ export function sessionLoader({DEBUG_KEY, storage, config, hook, logger}) { } catch (e) { } if (overrides) { - enableDebugging(overrides, {fromSession: true, config, hook, logger}); + enableDebugging(overrides, { fromSession: true, config, hook, logger }); } } function resetHooks(enable) { interceptorHooks.forEach(([getHookFn, interceptor]) => { - getHookFn().getHooks({hook: interceptor}).remove(); + getHookFn().getHooks({ hook: interceptor }).remove(); }); if (enable) { interceptorHooks.forEach(([getHookFn, interceptor]) => { @@ -100,32 +100,31 @@ function registerBidInterceptor(getHookFn, interceptor) { }]); } -export function makeBidderBidInterceptor({utils}) { - const {delayExecution} = utils; +export function makeBidderBidInterceptor({ utils }) { + const { delayExecution } = utils; return function bidderBidInterceptor(next, interceptBids, spec, bids, bidRequest, ajax, wrapCallback, cbs) { const done = delayExecution(cbs.onCompletion, 2); - ({bids, bidRequest} = interceptBids({ + ({ bids, bidRequest } = interceptBids({ bids, bidRequest, addBid: wrapCallback(cbs.onBid), - addPaapiConfig: wrapCallback((config, bidRequest) => cbs.onPaapi({bidId: bidRequest.bidId, ...config})), done })); if (bids.length === 0) { cbs.onResponse?.({}); // trigger onResponse so that the bidder may be marked as "timely" if necessary done(); } else { - next(spec, bids, bidRequest, ajax, wrapCallback, {...cbs, onCompletion: done}); + next(spec, bids, bidRequest, ajax, wrapCallback, { ...cbs, onCompletion: done }); } } } -export function install({DEBUG_KEY, config, hook, createBid, logger, utils, BANNER, NATIVE, VIDEO, Renderer}) { - const BidInterceptor = makebidInterceptor({utils, BANNER, NATIVE, VIDEO, Renderer}); - bidInterceptor = new BidInterceptor({logger}); - const pbsBidInterceptor = makePbsInterceptor({createBid, utils}); - registerBidInterceptor(() => hook.get('processBidderRequests'), makeBidderBidInterceptor({utils})); +export function install({ DEBUG_KEY, config, hook, createBid, logger, utils, BANNER, NATIVE, VIDEO, Renderer }) { + const BidInterceptor = makebidInterceptor({ utils, BANNER, NATIVE, VIDEO, Renderer }); + bidInterceptor = new BidInterceptor({ logger }); + const pbsBidInterceptor = makePbsInterceptor({ createBid, utils }); + registerBidInterceptor(() => hook.get('processBidderRequests'), makeBidderBidInterceptor({ utils })); registerBidInterceptor(() => hook.get('processPBSRequest'), pbsBidInterceptor); - sessionLoader({DEBUG_KEY, config, hook, logger}); - config.getConfig('debugging', ({debugging}) => getConfig(debugging, {DEBUG_KEY, config, hook, logger, utils}), {init: true}); + sessionLoader({ DEBUG_KEY, config, hook, logger }); + config.getConfig('debugging', ({ debugging }) => getConfig(debugging, { DEBUG_KEY, config, hook, logger, utils }), { init: true }); } diff --git a/modules/debugging/index.js b/modules/debugging/index.js index 728c3841687..87d80ddc291 100644 --- a/modules/debugging/index.js +++ b/modules/debugging/index.js @@ -1,14 +1,14 @@ /* eslint prebid/validate-imports: 0 */ -import {config} from '../../src/config.js'; -import {hook} from '../../src/hook.js'; -import {install} from './debugging.js'; -import {prefixLog} from '../../src/utils.js'; -import {createBid} from '../../src/bidfactory.js'; -import {DEBUG_KEY} from '../../src/debugging.js'; +import { config } from '../../src/config.js'; +import { hook } from '../../src/hook.js'; +import { install } from './debugging.js'; +import { prefixLog } from '../../src/utils.js'; +import { createBid } from '../../src/bidfactory.js'; +import { DEBUG_KEY } from '../../src/debugging.js'; import * as utils from '../../src/utils.js'; -import {BANNER, NATIVE, VIDEO} from '../../src/mediaTypes.js'; -import {Renderer} from '../../src/Renderer.js'; +import { BANNER, NATIVE, VIDEO } from '../../src/mediaTypes.js'; +import { Renderer } from '../../src/Renderer.js'; install({ DEBUG_KEY, diff --git a/modules/debugging/legacy.js b/modules/debugging/legacy.js index e83b99c5194..9550e7a744b 100644 --- a/modules/debugging/legacy.js +++ b/modules/debugging/legacy.js @@ -1,17 +1,17 @@ export let addBidResponseBound; export let addBidderRequestsBound; -export function addHooks(overrides, {hook, logger}) { - addBidResponseBound = addBidResponseHook.bind({overrides, logger}); +export function addHooks(overrides, { hook, logger }) { + addBidResponseBound = addBidResponseHook.bind({ overrides, logger }); hook.get('addBidResponse').before(addBidResponseBound, 5); - addBidderRequestsBound = addBidderRequestsHook.bind({overrides, logger}); + addBidderRequestsBound = addBidderRequestsHook.bind({ overrides, logger }); hook.get('addBidderRequests').before(addBidderRequestsBound, 5); } -export function removeHooks({hook}) { - hook.get('addBidResponse').getHooks({hook: addBidResponseBound}).remove(); - hook.get('addBidderRequests').getHooks({hook: addBidderRequestsBound}).remove(); +export function removeHooks({ hook }) { + hook.get('addBidResponse').getHooks({ hook: addBidResponseBound }).remove(); + hook.get('addBidderRequests').getHooks({ hook: addBidderRequestsBound }).remove(); } /** @@ -55,7 +55,7 @@ export function applyBidOverrides(overrideObj, bidObj, bidType, logger) { } export function addBidResponseHook(next, adUnitCode, bid, reject) { - const {overrides, logger} = this; + const { overrides, logger } = this; if (bidderExcluded(overrides.bidders, bid.bidderCode)) { logger.logWarn(`bidder '${bid.bidderCode}' excluded from auction by bidder overrides`); @@ -74,7 +74,7 @@ export function addBidResponseHook(next, adUnitCode, bid, reject) { } export function addBidderRequestsHook(next, bidderRequests) { - const {overrides, logger} = this; + const { overrides, logger } = this; const includedBidderRequests = bidderRequests.filter(function (bidderRequest) { if (bidderExcluded(overrides.bidders, bidderRequest.bidderCode)) { diff --git a/modules/debugging/pbsInterceptor.js b/modules/debugging/pbsInterceptor.js index 753f502002d..da246f30511 100644 --- a/modules/debugging/pbsInterceptor.js +++ b/modules/debugging/pbsInterceptor.js @@ -1,10 +1,9 @@ -export function makePbsInterceptor({createBid, utils}) { - const {deepClone, delayExecution} = utils; +export function makePbsInterceptor({ createBid, utils }) { + const { deepClone, delayExecution } = utils; return function pbsBidInterceptor(next, interceptBids, s2sBidRequest, bidRequests, ajax, { onResponse, onError, onBid, - onFledge, }) { let responseArgs; const done = delayExecution(() => onResponse(...responseArgs), bidRequests.length + 1) @@ -15,21 +14,13 @@ export function makePbsInterceptor({createBid, utils}) { function addBid(bid, bidRequest) { onBid({ adUnit: bidRequest.adUnitCode, - bid: Object.assign(createBid(bidRequest), {requestBidder: bidRequest.bidder}, bid) + bid: Object.assign(createBid(bidRequest), { requestBidder: bidRequest.bidder }, bid) }) } bidRequests = bidRequests .map((req) => interceptBids({ bidRequest: req, addBid, - addPaapiConfig(config, bidRequest, bidderRequest) { - onFledge({ - adUnitCode: bidRequest.adUnitCode, - ortb2: bidderRequest.ortb2, - ortb2Imp: bidRequest.ortb2Imp, - ...config - }) - }, done }).bidRequest) .filter((req) => req.bids.length > 0) @@ -42,7 +33,7 @@ export function makePbsInterceptor({createBid, utils}) { unit.bids = unit.bids.filter((bid) => bidIds.has(bid.bid_id)); }) s2sBidRequest.ad_units = s2sBidRequest.ad_units.filter((unit) => unit.bids.length > 0); - next(s2sBidRequest, bidRequests, ajax, {onResponse: signalResponse, onError, onBid}); + next(s2sBidRequest, bidRequests, ajax, { onResponse: signalResponse, onError, onBid }); } else { signalResponse(true, []); } diff --git a/modules/debugging/responses.js b/modules/debugging/responses.js index d30ffdeb8d7..dde386934d1 100644 --- a/modules/debugging/responses.js +++ b/modules/debugging/responses.js @@ -7,7 +7,7 @@ function getSlotDivid(adUnitCode) { return slot?.getSlotElementId(); } -export default function ({Renderer, BANNER, NATIVE, VIDEO}) { +export default function ({ Renderer, BANNER, NATIVE, VIDEO }) { return { [BANNER]: (bid, bidResponse) => { if (!bidResponse.hasOwnProperty('ad') && !bidResponse.hasOwnProperty('adUrl')) { diff --git a/modules/debugging/standalone.js b/modules/debugging/standalone.js index b3b539f5aa2..f715a60ccc4 100644 --- a/modules/debugging/standalone.js +++ b/modules/debugging/standalone.js @@ -1,4 +1,4 @@ -import {install} from './debugging.js'; +import { install } from './debugging.js'; window._pbjsGlobals.forEach((name) => { if (window[name] && window[name]._installDebugging === true) { diff --git a/modules/deepintentBidAdapter.js b/modules/deepintentBidAdapter.js index c5acc942218..47602bd218f 100644 --- a/modules/deepintentBidAdapter.js +++ b/modules/deepintentBidAdapter.js @@ -1,9 +1,9 @@ -import {getDNT} from '../libraries/dnt/index.js'; import { generateUUID, deepSetValue, deepAccess, isArray, isFn, isPlainObject, logError, logWarn } from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; import { COMMON_ORTB_VIDEO_PARAMS, formatResponse } from '../libraries/deepintentUtils/index.js'; import { addDealCustomTargetings, addPMPDeals } from '../libraries/dealUtils/dealUtils.js'; +import { getDNT } from '../libraries/dnt/index.js'; const LOG_WARN_PREFIX = 'DeepIntent: '; const BIDDER_CODE = 'deepintent'; @@ -98,6 +98,16 @@ export const spec = { deepSetValue(openRtbBidRequest, 'regs.coppa', 1); } + // ortb2 blocking: bcat, badv (with optional params fallback) + const bcat = bidderRequest?.ortb2?.bcat || deepAccess(validBidRequests, '0.params.bcat'); + const badv = bidderRequest?.ortb2?.badv || deepAccess(validBidRequests, '0.params.badv'); + if (isArray(bcat) && bcat.length > 0) { + openRtbBidRequest.bcat = bcat; + } + if (isArray(badv) && badv.length > 0) { + openRtbBidRequest.badv = badv; + } + injectEids(openRtbBidRequest, validBidRequests); return { diff --git a/modules/deepintentDpesIdSystem.js b/modules/deepintentDpesIdSystem.js index a1f1e29a4ce..90baadee34c 100644 --- a/modules/deepintentDpesIdSystem.js +++ b/modules/deepintentDpesIdSystem.js @@ -6,9 +6,9 @@ */ import { submodule } from '../src/hook.js'; -import {getStorageManager} from '../src/storageManager.js'; -import {MODULE_TYPE_UID} from '../src/activities/modules.js'; -import {isPlainObject} from '../src/utils.js'; +import { getStorageManager } from '../src/storageManager.js'; +import { MODULE_TYPE_UID } from '../src/activities/modules.js'; +import { isPlainObject } from '../src/utils.js'; /** * @typedef {import('../modules/userId/index.js').Submodule} Submodule @@ -17,7 +17,7 @@ import {isPlainObject} from '../src/utils.js'; */ const MODULE_NAME = 'deepintentId'; -export const storage = getStorageManager({moduleType: MODULE_TYPE_UID, moduleName: MODULE_NAME}); +export const storage = getStorageManager({ moduleType: MODULE_TYPE_UID, moduleName: MODULE_NAME }); /** @type {Submodule} */ export const deepintentDpesSubmodule = { diff --git a/modules/defineMediaBidAdapter.js b/modules/defineMediaBidAdapter.js index 937ad9fb8d8..3ec6cf4f8cc 100644 --- a/modules/defineMediaBidAdapter.js +++ b/modules/defineMediaBidAdapter.js @@ -9,10 +9,10 @@ * @version 1.0.0 */ -import {logInfo, logError, logWarn } from "../src/utils.js"; +import { logInfo, logError, logWarn } from "../src/utils.js"; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER } from '../src/mediaTypes.js'; -import {ortbConverter} from '../libraries/ortbConverter/converter.js' +import { ortbConverter } from '../libraries/ortbConverter/converter.js' import { ajax } from '../src/ajax.js'; // Bidder identification and compliance constants @@ -181,7 +181,7 @@ export const spec = { try { // Use the converter from the request if available (with custom TTL), otherwise use default const responseConverter = request.converter || converter; - const bids = responseConverter.fromORTB({response: serverResponse.body, request: request.data}).bids; + const bids = responseConverter.fromORTB({ response: serverResponse.body, request: request.data }).bids; logInfo(`[${BIDDER_CODE}] Successfully parsed ${bids.length} bids`); return bids; } catch (error) { diff --git a/modules/deltaprojectsBidAdapter.js b/modules/deltaprojectsBidAdapter.js index 1a9f2b46b9e..0904c018d31 100644 --- a/modules/deltaprojectsBidAdapter.js +++ b/modules/deltaprojectsBidAdapter.js @@ -227,7 +227,7 @@ function getUserSyncs(syncOptions, serverResponses, gdprConsent) { export function getBidFloor(bid, mediaType, size, currency) { if (isFn(bid.getFloor)) { const bidFloorCurrency = currency || 'USD'; - const bidFloor = bid.getFloor({currency: bidFloorCurrency, mediaType: mediaType, size: size}); + const bidFloor = bid.getFloor({ currency: bidFloorCurrency, mediaType: mediaType, size: size }); if (isNumber(bidFloor?.floor)) { return bidFloor; } diff --git a/modules/dfpAdServerVideo.js b/modules/dfpAdServerVideo.js deleted file mode 100644 index a7053622102..00000000000 --- a/modules/dfpAdServerVideo.js +++ /dev/null @@ -1,11 +0,0 @@ -/* eslint prebid/validate-imports: "off" */ -import {registerVideoSupport} from '../src/adServerManager.js'; -import {buildGamVideoUrl, getVastXml, notifyTranslationModule, dep, VAST_TAG_URI_TAGNAME, getBase64BlobContent} from './gamAdServerVideo.js'; - -export const buildDfpVideoUrl = buildGamVideoUrl; -export { getVastXml, notifyTranslationModule, dep, VAST_TAG_URI_TAGNAME, getBase64BlobContent }; - -registerVideoSupport('dfp', { - buildVideoUrl: buildDfpVideoUrl, - getVastXml -}); diff --git a/modules/dfpAdpod.js b/modules/dfpAdpod.js deleted file mode 100644 index 831507dcc5c..00000000000 --- a/modules/dfpAdpod.js +++ /dev/null @@ -1,10 +0,0 @@ -/* eslint prebid/validate-imports: "off" */ -import {registerVideoSupport} from '../src/adServerManager.js'; -import {buildAdpodVideoUrl, adpodUtils} from './gamAdpod.js'; - -export { buildAdpodVideoUrl, adpodUtils }; - -registerVideoSupport('dfp', { - buildAdpodVideoUrl, - getAdpodTargeting: (args) => adpodUtils.getTargeting(args) -}); diff --git a/modules/dianomiBidAdapter.js b/modules/dianomiBidAdapter.js index 55ee192461f..1d369800adf 100644 --- a/modules/dianomiBidAdapter.js +++ b/modules/dianomiBidAdapter.js @@ -17,7 +17,7 @@ import { config } from '../src/config.js'; import { Renderer } from '../src/Renderer.js'; import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; import { getCurrencyFromBidderRequest } from '../libraries/ortb2Utils/currency.js'; -import {getUserSyncParams} from '../libraries/userSyncUtils/userSyncUtils.js'; +import { getUserSyncParams } from '../libraries/userSyncUtils/userSyncUtils.js'; const { getConfig } = config; diff --git a/modules/digitalMatterBidAdapter.js b/modules/digitalMatterBidAdapter.js index 37c01e6a9d9..0c56118dc71 100644 --- a/modules/digitalMatterBidAdapter.js +++ b/modules/digitalMatterBidAdapter.js @@ -1,9 +1,9 @@ -import {getDNT} from '../libraries/dnt/index.js'; -import {deepAccess, deepSetValue, getWinDimensions, inIframe, logWarn, parseSizesInput} from '../src/utils.js'; -import {config} from '../src/config.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER} from '../src/mediaTypes.js'; -import {hasPurpose1Consent} from '../src/utils/gdpr.js'; +import { deepAccess, deepSetValue, getWinDimensions, inIframe, logWarn, parseSizesInput } from '../src/utils.js'; +import { config } from '../src/config.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER } from '../src/mediaTypes.js'; +import { hasPurpose1Consent } from '../src/utils/gdpr.js'; +import { getDNT } from '../libraries/dnt/index.js'; const BIDDER_CODE = 'digitalMatter'; const GVLID = 1345; @@ -30,7 +30,7 @@ export const spec = { const common = bidderRequest.ortb2 || {}; const site = common.site; const tid = common?.source?.tid; - const {user} = common || {}; + const { user } = common || {}; if (!site.page) { site.page = bidderRequest.refererInfo.page; @@ -43,7 +43,7 @@ export const spec = { const cur = currency && [currency]; const imp = validBidRequests.map((bid, id) => { - const {accountId, siteId} = bid.params; + const { accountId, siteId } = bid.params; const bannerParams = deepAccess(bid, 'mediaTypes.banner'); const position = deepAccess(bid, 'mediaTypes.banner.pos') ?? 0; @@ -110,7 +110,7 @@ export const spec = { }, interpretResponse: function (serverResponse) { const body = serverResponse.body || serverResponse; - const {cur} = body; + const { cur } = body; const bids = []; if (body && body.bids && Array.isArray(body.bids)) { @@ -162,9 +162,9 @@ export const spec = { if (url) { if ((type === 'image' || type === 'redirect') && syncOptions.pixelEnabled) { - userSyncs.push({type: 'image', url: url}); + userSyncs.push({ type: 'image', url: url }); } else if (type === 'iframe' && syncOptions.iframeEnabled) { - userSyncs.push({type: 'iframe', url: url}); + userSyncs.push({ type: 'iframe', url: url }); } } }) diff --git a/modules/digitalcaramelBidAdapter.js b/modules/digitalcaramelBidAdapter.js index 1f045fb352d..8b724ae6515 100644 --- a/modules/digitalcaramelBidAdapter.js +++ b/modules/digitalcaramelBidAdapter.js @@ -14,8 +14,8 @@ export const spec = { isBidRequestValid: sspValidRequest, buildRequests: sspBuildRequests(DEFAULT_ENDPOINT), interpretResponse: sspInterpretResponse(TIME_TO_LIVE, ADOMAIN), - getUserSyncs: getUserSyncs(SYNC_ENDPOINT, {usp: 'usp', consent: 'consent'}), - supportedMediaTypes: [ BANNER, VIDEO ] + getUserSyncs: getUserSyncs(SYNC_ENDPOINT, { usp: 'usp', consent: 'consent' }), + supportedMediaTypes: [BANNER, VIDEO] } registerBidder(spec); diff --git a/modules/discoveryBidAdapter.js b/modules/discoveryBidAdapter.js index 8992efa9829..ba2147e6412 100644 --- a/modules/discoveryBidAdapter.js +++ b/modules/discoveryBidAdapter.js @@ -18,7 +18,7 @@ import { cookieSync } from '../libraries/cookieSync/cookieSync.js'; const BIDDER_CODE = 'discovery'; const ENDPOINT_URL = 'https://rtb-jp.mediago.io/api/bid?tn='; const TIME_TO_LIVE = 500; -export const storage = getStorageManager({bidderCode: BIDDER_CODE}); +export const storage = getStorageManager({ bidderCode: BIDDER_CODE }); const globals = {}; const itemMaps = {}; const MEDIATYPE = [BANNER, NATIVE]; diff --git a/modules/displayioBidAdapter.js b/modules/displayioBidAdapter.js index 69ff56621fd..66b4716dee7 100644 --- a/modules/displayioBidAdapter.js +++ b/modules/displayioBidAdapter.js @@ -1,10 +1,10 @@ -import {getDNT} from '../libraries/dnt/index.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER, VIDEO} from '../src/mediaTypes.js'; -import {Renderer} from '../src/Renderer.js'; -import {logWarn} from '../src/utils.js'; -import {getStorageManager} from '../src/storageManager.js'; -import {getAllOrtbKeywords} from '../libraries/keywords/keywords.js'; +import { getDNT } from '../libraries/dnt/index.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; +import { Renderer } from '../src/Renderer.js'; +import { logWarn } from '../src/utils.js'; +import { getStorageManager } from '../src/storageManager.js'; +import { getAllOrtbKeywords } from '../libraries/keywords/keywords.js'; import { getConnectionInfo } from '../libraries/connectionInfo/connectionUtils.js'; const ADAPTER_VERSION = '1.1.0'; @@ -28,7 +28,7 @@ export const spec = { const data = getPayload(bid, bidderRequest); return { method: 'POST', - headers: {'Content-Type': 'application/json;charset=utf-8'}, + headers: { 'Content-Type': 'application/json;charset=utf-8' }, url, data }; @@ -72,7 +72,7 @@ export const spec = { function getPayload (bid, bidderRequest) { const connection = getConnectionInfo(); - const storage = getStorageManager({bidderCode: BIDDER_CODE}); + const storage = getStorageManager({ bidderCode: BIDDER_CODE }); const userSession = (() => { let us = storage.getDataFromLocalStorage(US_KEY); if (!us) { @@ -88,7 +88,7 @@ function getPayload (bid, bidderRequest) { const { params, adUnitCode, bidId } = bid; const { siteId, placementId, renderURL, pageCategory, keywords } = params; const { refererInfo, uspConsent, gdprConsent } = bidderRequest; - const mediation = {gdprConsent: '', gdpr: '-1'}; + const mediation = { gdprConsent: '', gdpr: '-1' }; if (gdprConsent && 'gdprApplies' in gdprConsent) { if (gdprConsent.consentString !== undefined) { mediation.gdprConsent = gdprConsent.consentString; diff --git a/modules/distroscaleBidAdapter.js b/modules/distroscaleBidAdapter.js index 4737c8bf969..a2eea9e9130 100644 --- a/modules/distroscaleBidAdapter.js +++ b/modules/distroscaleBidAdapter.js @@ -1,8 +1,8 @@ -import {getDNT} from '../libraries/dnt/index.js'; import { logWarn, isPlainObject, isStr, isArray, isFn, inIframe, mergeDeep, deepSetValue, logError, deepClone } from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { config } from '../src/config.js'; import { BANNER } from '../src/mediaTypes.js'; +import { getDNT } from '../libraries/dnt/index.js'; const BIDDER_CODE = 'distroscale'; const SHORT_CODE = 'ds'; const LOG_WARN_PREFIX = 'DistroScale: '; @@ -12,7 +12,7 @@ const AUCTION_TYPE = 1; const GVLID = 754; const UNDEF = undefined; -const SUPPORTED_MEDIATYPES = [ BANNER ]; +const SUPPORTED_MEDIATYPES = [BANNER]; function _getHost(url) { const a = document.createElement('a'); @@ -222,10 +222,10 @@ export const spec = { // First Party Data const commonFpd = bidderRequest.ortb2 || {}; if (commonFpd.site) { - mergeDeep(payload, {site: commonFpd.site}); + mergeDeep(payload, { site: commonFpd.site }); } if (commonFpd.user) { - mergeDeep(payload, {user: commonFpd.user}); + mergeDeep(payload, { user: commonFpd.user }); } // User IDs diff --git a/modules/djaxBidAdapter.js b/modules/djaxBidAdapter.js index 775ae146b88..15ba614f449 100644 --- a/modules/djaxBidAdapter.js +++ b/modules/djaxBidAdapter.js @@ -1,8 +1,8 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import * as utils from '../src/utils.js'; -import {BANNER, VIDEO} from '../src/mediaTypes.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; import { ajax } from '../src/ajax.js'; -import {Renderer} from '../src/Renderer.js'; +import { Renderer } from '../src/Renderer.js'; const SUPPORTED_AD_TYPES = [BANNER, VIDEO]; const BIDDER_CODE = 'djax'; @@ -30,7 +30,7 @@ function createRenderer(bidAd, rendererParams, adUnitCode) { id: rendererParams.id, url: rendererParams.url, loaded: false, - config: {'player_height': bidAd.height, 'player_width': bidAd.width}, + config: { 'player_height': bidAd.height, 'player_width': bidAd.width }, adUnitCode }); try { diff --git a/modules/dmdIdSystem.js b/modules/dmdIdSystem.js deleted file mode 100644 index 4fc986bd1fc..00000000000 --- a/modules/dmdIdSystem.js +++ /dev/null @@ -1,104 +0,0 @@ -/** - * This module adds dmdId to the User ID module - * The {@link module:modules/userId} module is required - * @module modules/dmdIdSystem - * @requires module:modules/userId - */ - -import { logError, getWindowLocation } from '../src/utils.js'; -import { submodule } from '../src/hook.js'; -import { ajax } from '../src/ajax.js'; - -/** - * @typedef {import('../modules/userId/index.js').Submodule} Submodule - * @typedef {import('../modules/userId/index.js').SubmoduleConfig} SubmoduleConfig - * @typedef {import('../modules/userId/index.js').ConsentData} ConsentData - * @typedef {import('../modules/userId/index.js').IdResponse} IdResponse - */ - -const MODULE_NAME = 'dmdId'; - -/** @type {Submodule} */ -export const dmdIdSubmodule = { - /** - * used to link submodule with config - * @type {string} - */ - name: MODULE_NAME, - - /** - * decode the stored id value for passing to bid requests - * @function decode - * @param {(Object|string)} value - * @returns {(Object|undefined)} - */ - decode(value) { - return value && typeof value === 'string' - ? { 'dmdId': value } - : undefined; - }, - - /** - * performs action to obtain id and return a value in the callback's response argument - * @function getId - * @param {SubmoduleConfig} [config] - * @param {ConsentData} consentData - * @param {Object} cacheIdObj - existing id, if any - * @returns {IdResponse|undefined} - */ - getId(config, consentData, cacheIdObj) { - const configParams = (config && config.params) || {}; - if ( - !configParams || - !configParams.api_key || - typeof configParams.api_key !== 'string' - ) { - logError('dmd submodule requires an api_key.'); - return; - } - // If cahceIdObj is null or undefined - calling AIX-API - if (cacheIdObj) { - return cacheIdObj; - } else { - const url = configParams && configParams.api_url - ? configParams.api_url - : `https://aix.hcn.health/api/v1/auths`; - // Setting headers - const headers = {}; - headers['x-api-key'] = configParams.api_key; - headers['x-domain'] = getWindowLocation(); - // Response callbacks - const resp = function (callback) { - const callbacks = { - success: response => { - let responseObj; - let responseId; - try { - responseObj = JSON.parse(response); - if (responseObj && responseObj.dgid) { - responseId = responseObj.dgid; - } - } catch (error) { - logError(error); - } - callback(responseId); - }, - error: error => { - logError(`${MODULE_NAME}: ID fetch encountered an error`, error); - callback(); - } - }; - ajax(url, callbacks, undefined, { method: 'GET', withCredentials: true, customHeaders: headers }); - }; - return { callback: resp }; - } - }, - eids: { - 'dmdId': { - source: 'hcn.health', - atype: 3 - }, - } -}; - -submodule('userId', dmdIdSubmodule); diff --git a/modules/dmdIdSystem.md b/modules/dmdIdSystem.md deleted file mode 100644 index f2a5b76ade7..00000000000 --- a/modules/dmdIdSystem.md +++ /dev/null @@ -1,26 +0,0 @@ -pbjs.setConfig({ - userSync: { - userIds: [{ - name: 'dmdId', - storage: { - name: 'dmd-dgid', - type: 'cookie', - expires: 30 - }, - params: { - api_key: '3fdbe297-3690-4f5c-9e11-ee9186a6d77c', // provided by DMD - } - }] - } -}); - -#### DMD ID Configuration - -{: .table .table-bordered .table-striped } -| Param under userSync.userIds[] | Scope | Type | Description | Example | -| --- | --- | --- | --- | --- | -| name | Required | String | The name of Module | `"dmdId"` | -| storage | Required | Object | | -| storage.name | Required | String | `dmd-dgid` | -| params | Required | Object | Container of all module params. | | -| params.api_key | Required | String | This is your `api_key` as provided by DMD Marketing Corp. | `3fdbe297-3690-4f5c-9e11-ee9186a6d77c` | \ No newline at end of file diff --git a/modules/docereeBidAdapter.js b/modules/docereeBidAdapter.js index 849aa73bde6..c21a18edd36 100644 --- a/modules/docereeBidAdapter.js +++ b/modules/docereeBidAdapter.js @@ -2,7 +2,7 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { triggerPixel } from '../src/utils.js'; import { config } from '../src/config.js'; import { BANNER } from '../src/mediaTypes.js'; -import {tryAppendQueryString} from '../libraries/urlUtils/urlUtils.js'; +import { tryAppendQueryString } from '../libraries/urlUtils/urlUtils.js'; const BIDDER_CODE = 'doceree'; const GVLID = 1063; const END_POINT = 'https://bidder.doceree.com' @@ -12,7 +12,7 @@ export const spec = { code: BIDDER_CODE, gvlid: GVLID, url: '', - supportedMediaTypes: [ BANNER ], + supportedMediaTypes: [BANNER], isBidRequestValid: (bid) => { const { placementId } = bid.params; diff --git a/modules/dpaiBidAdapter.js b/modules/dpaiBidAdapter.js new file mode 100644 index 00000000000..4cf359b196b --- /dev/null +++ b/modules/dpaiBidAdapter.js @@ -0,0 +1,19 @@ +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; +import { isBidRequestValid, buildRequests, interpretResponse, getUserSyncs } from '../libraries/teqblazeUtils/bidderUtils.js'; + +const BIDDER_CODE = 'dpai'; +const AD_URL = 'https://ssp.drift-pixel.ai/pbjs'; +const SYNC_URL = 'https://sync.drift-pixel.ai'; + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [BANNER, VIDEO, NATIVE], + + isBidRequestValid: isBidRequestValid(), + buildRequests: buildRequests(AD_URL), + interpretResponse, + getUserSyncs: getUserSyncs(SYNC_URL) +}; + +registerBidder(spec); diff --git a/modules/dpaiBidAdapter.md b/modules/dpaiBidAdapter.md new file mode 100644 index 00000000000..4882abdacc4 --- /dev/null +++ b/modules/dpaiBidAdapter.md @@ -0,0 +1,79 @@ +# Overview + +``` +Module Name: DPAI Bidder Adapter +Module Type: DPAI Bidder Adapter +Maintainer: adops@driftpixel.ai +``` + +# Description + +Connects to DPAI exchange for bids. +DPAI bid adapter supports Banner, Video (instream and outstream) and Native. + +# Test Parameters +``` + var adUnits = [ + // Will return static test banner + { + code: 'adunit1', + mediaTypes: { + banner: { + sizes: [ [300, 250], [320, 50] ], + } + }, + bids: [ + { + bidder: 'dpai', + params: { + placementId: 'testBanner', + } + } + ] + }, + { + code: 'addunit2', + mediaTypes: { + video: { + playerSize: [ [640, 480] ], + context: 'instream', + minduration: 5, + maxduration: 60, + } + }, + bids: [ + { + bidder: 'dpai', + params: { + placementId: 'testVideo', + } + } + ] + }, + { + code: 'addunit3', + mediaTypes: { + native: { + title: { + required: true + }, + body: { + required: true + }, + icon: { + required: true, + size: [64, 64] + } + } + }, + bids: [ + { + bidder: 'dpai', + params: { + placementId: 'testNative', + } + } + ] + } + ]; +``` diff --git a/modules/driftpixelBidAdapter.js b/modules/driftpixelBidAdapter.js index 5dd0d3a5835..78c23c75cf6 100644 --- a/modules/driftpixelBidAdapter.js +++ b/modules/driftpixelBidAdapter.js @@ -1,6 +1,6 @@ -import {BANNER, VIDEO} from '../src/mediaTypes.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {buildRequests, getUserSyncs, interpretResponse, isBidRequestValid} from '../libraries/xeUtils/bidderUtils.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { buildRequests, getUserSyncs, interpretResponse, isBidRequestValid } from '../libraries/xeUtils/bidderUtils.js'; const BIDDER_CODE = 'driftpixel'; const ENDPOINT = 'https://pbjs.driftpixel.live'; diff --git a/modules/dsaControl.js b/modules/dsaControl.js index 73a1dd19cd4..bb87ef7ad3f 100644 --- a/modules/dsaControl.js +++ b/modules/dsaControl.js @@ -1,9 +1,9 @@ -import {config} from '../src/config.js'; -import {auctionManager} from '../src/auctionManager.js'; -import {timedBidResponseHook} from '../src/utils/perfMetrics.js'; +import { config } from '../src/config.js'; +import { auctionManager } from '../src/auctionManager.js'; +import { timedBidResponseHook } from '../src/utils/perfMetrics.js'; import { REJECTION_REASON } from '../src/constants.js'; -import {getHook} from '../src/hook.js'; -import {logInfo, logWarn} from '../src/utils.js'; +import { getHook } from '../src/hook.js'; +import { logInfo, logWarn } from '../src/utils.js'; let expiryHandle; let dsaAuctions = {}; @@ -48,7 +48,7 @@ function toggleHooks(enabled) { }); logInfo('dsaControl: DSA bid validation is enabled') } else if (!enabled && expiryHandle != null) { - getHook('addBidResponse').getHooks({hook: addBidResponseHook}).remove(); + getHook('addBidResponse').getHooks({ hook: addBidResponseHook }).remove(); expiryHandle(); expiryHandle = null; logInfo('dsaControl: DSA bid validation is disabled') diff --git a/modules/dspxBidAdapter.js b/modules/dspxBidAdapter.js index 19419ba70e8..e6bc012374f 100644 --- a/modules/dspxBidAdapter.js +++ b/modules/dspxBidAdapter.js @@ -1,6 +1,6 @@ -import {deepAccess, logMessage, getBidIdParameter, logError, logWarn} from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER, VIDEO} from '../src/mediaTypes.js'; +import { deepAccess, logMessage, getBidIdParameter, logError, logWarn } from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; import { fillUsersIds, @@ -16,7 +16,7 @@ import { extractUserSegments, interpretResponse } from '../libraries/dspxUtils/bidderUtils.js'; -import {Renderer} from '../src/Renderer.js'; +import { Renderer } from '../src/Renderer.js'; /** * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest diff --git a/modules/dvgroupBidAdapter.js b/modules/dvgroupBidAdapter.js index bf63dd0bbe0..fce3dfd3396 100644 --- a/modules/dvgroupBidAdapter.js +++ b/modules/dvgroupBidAdapter.js @@ -1,7 +1,7 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; import { hasPurpose1Consent } from '../src/utils/gdpr.js'; -import {deepAccess, deepClone, replaceAuctionPrice} from '../src/utils.js'; +import { deepAccess, deepClone, replaceAuctionPrice } from '../src/utils.js'; import { ortbConverter } from '../libraries/ortbConverter/converter.js'; const BIDDER_CODE = 'dvgroup'; @@ -57,7 +57,7 @@ export const spec = { return []; } - const bids = converter.fromORTB({response: response.body, request: request.data}).bids; + const bids = converter.fromORTB({ response: response.body, request: request.data }).bids; bids.forEach((bid) => { bid.meta = bid.meta || {}; bid.ttl = bid.ttl || TIME_TO_LIVE; @@ -92,7 +92,7 @@ export const spec = { return syncs; }, - supportedMediaTypes: [ BANNER, VIDEO ] + supportedMediaTypes: [BANNER, VIDEO] } registerBidder(spec); diff --git a/modules/dxkultureBidAdapter.js b/modules/dxkultureBidAdapter.js index 78b37a67602..0a1e3229d3e 100644 --- a/modules/dxkultureBidAdapter.js +++ b/modules/dxkultureBidAdapter.js @@ -7,10 +7,10 @@ import { deepSetValue, mergeDeep } from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER, VIDEO} from '../src/mediaTypes.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; import { Renderer } from '../src/Renderer.js'; -import {ortbConverter} from '../libraries/ortbConverter/converter.js' +import { ortbConverter } from '../libraries/ortbConverter/converter.js' /** * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest @@ -62,7 +62,7 @@ const converter = ortbConverter({ }, bidResponse(buildBidResponse, bid, context) { let resMediaType; - const {bidRequest} = context; + const { bidRequest } = context; if (bid.adm?.trim().startsWith(' - !!(bid && bid.params && bid.params.zone && bid.bidder === BIDDER_CODE), - - buildRequests: (bidRequests, bidderRequest) => { - const currencyObj = config.getConfig("currency"); - const currency = (currencyObj && currencyObj.adServerCurrency) || "USD"; - - const request = { - id: bidRequests[0].bidderRequestId, - at: 1, - imp: bidRequests.map((slot) => impression(slot, currency)), - site: { - page: bidderRequest.refererInfo.page, - domain: bidderRequest.refererInfo.domain, - ref: bidderRequest.refererInfo.ref, - publisher: { domain: bidderRequest.refererInfo.domain }, - }, - device: { - ua: navigator.userAgent, - js: 1, - dnt: - navigator.doNotTrack === "yes" || - navigator.doNotTrack === "1" || - navigator.msDoNotTrack === "1" - ? 1 - : 0, - h: screen.height, - w: screen.width, - language: navigator.language, - connectiontype: getConnectionType(), - }, - cur: [currency], - source: { - fd: 1, - tid: bidderRequest.ortb2?.source?.tid, - ext: { - prebid: "$prebid.version$", - }, - }, - user: {}, - regs: {}, - ext: {}, - }; - - if (bidderRequest.gdprConsent) { - request.user = { - ext: { - consent: bidderRequest.gdprConsent.consentString || "", - }, - }; - request.regs = { - ext: { - gdpr: - bidderRequest.gdprConsent.gdprApplies !== undefined - ? bidderRequest.gdprConsent.gdprApplies - : true, - }, - }; - } - - if (bidderRequest.ortb2?.source?.ext?.schain) { - request.schain = bidderRequest.ortb2.source.ext.schain; - } - - let bidUserIdAsEids = deepAccess(bidRequests, "0.userIdAsEids"); - if (isArray(bidUserIdAsEids) && bidUserIdAsEids.length > 0) { - deepSetValue(request, "user.eids", bidUserIdAsEids); - } - - const commonFpd = bidderRequest.ortb2 || {}; - const { user, device, site, bcat, badv } = commonFpd; - if (site) { - mergeDeep(request, { site: site }); - } - if (user) { - mergeDeep(request, { user: user }); - } - if (badv) { - mergeDeep(request, { badv: badv }); - } - if (bcat) { - mergeDeep(request, { bcat: bcat }); - } - - if (user?.geo && device?.geo) { - request.device.geo = { ...request.device.geo, ...device.geo }; - request.user.geo = { ...request.user.geo, ...user.geo }; - } else { - if (user?.geo || device?.geo) { - request.user.geo = request.device.geo = user?.geo - ? { ...request.user.geo, ...user.geo } - : { ...request.user.geo, ...device.geo }; - } - } - - if (bidderRequest.ortb2?.device) { - mergeDeep(request.device, bidderRequest.ortb2.device); - } - - return { - method: "POST", - url: ENDPOINT, - data: JSON.stringify(request), - }; - }, - - interpretResponse: (bidResponse, bidRequest) => { - const idToImpMap = {}; - const idToBidMap = {}; - - if (!bidResponse["body"]) { - return []; - } - if (!bidRequest.data) { - return []; - } - const requestImps = parse(bidRequest.data); - if (!requestImps) { - return []; - } - requestImps.imp.forEach((imp) => { - idToImpMap[imp.id] = imp; - }); - bidResponse = bidResponse.body; - if (bidResponse) { - bidResponse.seatbid.forEach((seatBid) => - seatBid.bid.forEach((bid) => { - idToBidMap[bid.impid] = bid; - }) - ); - } - const bids = []; - Object.keys(idToImpMap).forEach((id) => { - const imp = idToImpMap[id]; - const result = idToBidMap[id]; - - if (result) { - const bid = { - requestId: id, - cpm: result.price, - creativeId: result.crid, - ttl: 300, - netRevenue: true, - mediaType: imp.video ? VIDEO : BANNER, - currency: bidResponse.cur, - }; - if (imp.video) { - bid.vastXml = result.adm; - } else if (imp.banner) { - bid.ad = result.adm; - } - bid.width = result.w; - bid.height = result.h; - if (result.burl) bid.burl = result.burl; - if (result.nurl) bid.nurl = result.nurl; - if (result.adomain) { - bid.meta = { - advertiserDomains: result.adomain, - }; - } - bids.push(bid); - } - }); - return bids; - }, - - onBidWon: (bid) => { - if (bid.nurl && isStr(bid.nurl)) { - bid.nurl = replaceMacros(bid.nurl, { - AUCTION_PRICE: bid.cpm, - AUCTION_CURRENCY: bid.cur, - }); - triggerPixel(bid.nurl); - } - }, -}; - -function impression(slot, currency) { - let bidFloorFromModule; - if (typeof slot.getFloor === "function") { - const floorInfo = slot.getFloor({ - currency: "USD", - mediaType: "*", - size: "*", - }); - bidFloorFromModule = - floorInfo?.currency === "USD" ? floorInfo?.floor : undefined; - } - const imp = { - id: slot.bidId, - bidfloor: bidFloorFromModule || slot.params.bidfloor || 0, - bidfloorcur: - (bidFloorFromModule && "USD") || - slot.params.bidfloorcur || - currency || - "USD", - tagid: "" + (slot.params.zone || ""), - }; - - if (slot.mediaTypes.banner) { - imp.banner = bannerImpression(slot); - } else if (slot.mediaTypes.video) { - imp.video = deepAccess(slot, "mediaTypes.video"); - } - imp.ext = slot.params || {}; - const { innerWidth, innerHeight } = getWinDimensions(); - imp.ext.ww = innerWidth || ""; - imp.ext.wh = innerHeight || ""; - return imp; -} - -function bannerImpression(slot) { - const sizes = slot.mediaTypes.banner.sizes || slot.sizes; - return { - format: sizes.map((s) => ({ w: s[0], h: s[1] })), - w: sizes[0][0], - h: sizes[0][1], - }; -} - -function parse(rawResponse) { - try { - if (rawResponse) { - if (typeof rawResponse === "object") { - return rawResponse; - } else { - return JSON.parse(rawResponse); - } - } - } catch (ex) { - logError("empowerBidAdapter", "ERROR", ex); - } - return null; -} - -registerBidder(spec); +import { + deepAccess, + mergeDeep, + logError, + replaceMacros, + triggerPixel, + deepSetValue, + isStr, + isArray, + getWinDimensions, +} from "../src/utils.js"; +import { registerBidder } from "../src/adapters/bidderFactory.js"; +import { config } from "../src/config.js"; +import { VIDEO, BANNER } from "../src/mediaTypes.js"; +import { getConnectionType } from "../libraries/connectionInfo/connectionUtils.js"; +import { getDNT } from "../libraries/dnt/index.js"; + +export const ENDPOINT = "https://bid.virgul.com/prebid"; + +const BIDDER_CODE = "empower"; +const GVLID = 1248; + +export const spec = { + code: BIDDER_CODE, + gvlid: GVLID, + supportedMediaTypes: [VIDEO, BANNER], + + isBidRequestValid: (bid) => + !!(bid && bid.params && bid.params.zone && bid.bidder === BIDDER_CODE), + + buildRequests: (bidRequests, bidderRequest) => { + const currencyObj = config.getConfig("currency"); + const currency = (currencyObj && currencyObj.adServerCurrency) || "USD"; + + const request = { + id: bidRequests[0].bidderRequestId, + at: 1, + imp: bidRequests.map((slot) => impression(slot, currency)), + site: { + page: bidderRequest.refererInfo.page, + domain: bidderRequest.refererInfo.domain, + ref: bidderRequest.refererInfo.ref, + publisher: { domain: bidderRequest.refererInfo.domain }, + }, + device: { + ua: navigator.userAgent, + js: 1, + dnt: getDNT() ? 1 : 0, + h: screen.height, + w: screen.width, + language: navigator.language, + connectiontype: getConnectionType(), + }, + cur: [currency], + source: { + fd: 1, + tid: bidderRequest.ortb2?.source?.tid, + ext: { + prebid: "$prebid.version$", + }, + }, + user: {}, + regs: {}, + ext: {}, + }; + + if (bidderRequest.gdprConsent) { + request.user = { + ext: { + consent: bidderRequest.gdprConsent.consentString || "", + }, + }; + request.regs = { + ext: { + gdpr: + bidderRequest.gdprConsent.gdprApplies !== undefined + ? bidderRequest.gdprConsent.gdprApplies + : true, + }, + }; + } + + if (bidderRequest.ortb2?.source?.ext?.schain) { + request.schain = bidderRequest.ortb2.source.ext.schain; + } + + let bidUserIdAsEids = deepAccess(bidRequests, "0.userIdAsEids"); + if (isArray(bidUserIdAsEids) && bidUserIdAsEids.length > 0) { + deepSetValue(request, "user.eids", bidUserIdAsEids); + } + + const commonFpd = bidderRequest.ortb2 || {}; + const { user, device, site, bcat, badv } = commonFpd; + if (site) { + mergeDeep(request, { site: site }); + } + if (user) { + mergeDeep(request, { user: user }); + } + if (badv) { + mergeDeep(request, { badv: badv }); + } + if (bcat) { + mergeDeep(request, { bcat: bcat }); + } + + if (user?.geo && device?.geo) { + request.device.geo = { ...request.device.geo, ...device.geo }; + request.user.geo = { ...request.user.geo, ...user.geo }; + } else { + if (user?.geo || device?.geo) { + request.user.geo = request.device.geo = user?.geo + ? { ...request.user.geo, ...user.geo } + : { ...request.user.geo, ...device.geo }; + } + } + + if (bidderRequest.ortb2?.device) { + mergeDeep(request.device, bidderRequest.ortb2.device); + } + + return { + method: "POST", + url: ENDPOINT, + data: JSON.stringify(request), + }; + }, + + interpretResponse: (bidResponse, bidRequest) => { + const idToImpMap = {}; + const idToBidMap = {}; + + if (!bidResponse["body"]) { + return []; + } + if (!bidRequest.data) { + return []; + } + const requestImps = parse(bidRequest.data); + if (!requestImps) { + return []; + } + requestImps.imp.forEach((imp) => { + idToImpMap[imp.id] = imp; + }); + bidResponse = bidResponse.body; + if (bidResponse) { + bidResponse.seatbid.forEach((seatBid) => + seatBid.bid.forEach((bid) => { + idToBidMap[bid.impid] = bid; + }) + ); + } + const bids = []; + Object.keys(idToImpMap).forEach((id) => { + const imp = idToImpMap[id]; + const result = idToBidMap[id]; + + if (result) { + const bid = { + requestId: id, + cpm: result.price, + creativeId: result.crid, + ttl: 300, + netRevenue: true, + mediaType: imp.video ? VIDEO : BANNER, + currency: bidResponse.cur, + }; + if (imp.video) { + bid.vastXml = result.adm; + } else if (imp.banner) { + bid.ad = result.adm; + } + bid.width = result.w; + bid.height = result.h; + if (result.burl) bid.burl = result.burl; + if (result.nurl) bid.nurl = result.nurl; + if (result.adomain) { + bid.meta = { + advertiserDomains: result.adomain, + }; + } + bids.push(bid); + } + }); + return bids; + }, + + onBidWon: (bid) => { + if (bid.nurl && isStr(bid.nurl)) { + bid.nurl = replaceMacros(bid.nurl, { + AUCTION_PRICE: bid.cpm, + AUCTION_CURRENCY: bid.cur, + }); + triggerPixel(bid.nurl); + } + }, +}; + +function impression(slot, currency) { + let bidFloorFromModule; + if (typeof slot.getFloor === "function") { + const floorInfo = slot.getFloor({ + currency: "USD", + mediaType: "*", + size: "*", + }); + bidFloorFromModule = + floorInfo?.currency === "USD" ? floorInfo?.floor : undefined; + } + const imp = { + id: slot.bidId, + bidfloor: bidFloorFromModule || slot.params.bidfloor || 0, + bidfloorcur: + (bidFloorFromModule && "USD") || + slot.params.bidfloorcur || + currency || + "USD", + tagid: "" + (slot.params.zone || ""), + }; + + if (slot.mediaTypes.banner) { + imp.banner = bannerImpression(slot); + } else if (slot.mediaTypes.video) { + imp.video = deepAccess(slot, "mediaTypes.video"); + } + imp.ext = slot.params || {}; + const { innerWidth, innerHeight } = getWinDimensions(); + imp.ext.ww = innerWidth || ""; + imp.ext.wh = innerHeight || ""; + return imp; +} + +function bannerImpression(slot) { + const sizes = slot.mediaTypes.banner.sizes || slot.sizes; + return { + format: sizes.map((s) => ({ w: s[0], h: s[1] })), + w: sizes[0][0], + h: sizes[0][1], + }; +} + +function parse(rawResponse) { + try { + if (rawResponse) { + if (typeof rawResponse === "object") { + return rawResponse; + } else { + return JSON.parse(rawResponse); + } + } + } catch (ex) { + logError("empowerBidAdapter", "ERROR", ex); + } + return null; +} + +registerBidder(spec); diff --git a/modules/engageyaBidAdapter.js b/modules/engageyaBidAdapter.js index c0889c90981..9602a0273c0 100644 --- a/modules/engageyaBidAdapter.js +++ b/modules/engageyaBidAdapter.js @@ -1,6 +1,6 @@ import { BANNER, NATIVE } from '../src/mediaTypes.js'; import { createTrackPixelHtml } from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; const BIDDER_CODE = 'engageya'; diff --git a/modules/enrichmentLiftMeasurement/index.js b/modules/enrichmentLiftMeasurement/index.js index 0486772b540..0578bcaffee 100644 --- a/modules/enrichmentLiftMeasurement/index.js +++ b/modules/enrichmentLiftMeasurement/index.js @@ -28,7 +28,7 @@ let rules = []; export function init(storageManager = getStorageManager({ moduleType: MODULE_TYPE, moduleName: MODULE_NAME })) { moduleConfig = config.getConfig(MODULE_NAME) || {}; - const {suppression, testRun, storeSplits} = moduleConfig; + const { suppression, testRun, storeSplits } = moduleConfig; let modules; if (testRun && storeSplits && storeSplits !== storeSplitsMethod.MEMORY) { @@ -43,14 +43,14 @@ export function init(storageManager = getStorageManager({ moduleType: MODULE_TYP modules = modules ?? internals.getCalculatedSubmodules(); - const bannedModules = new Set(modules.filter(({enabled}) => !enabled).map(({name}) => name)); + const bannedModules = new Set(modules.filter(({ enabled }) => !enabled).map(({ name }) => name)); if (bannedModules.size) { const init = suppression === suppressionMethod.SUBMODULES; rules.push(registerActivityControl(ACTIVITY_ENRICH_EIDS, MODULE_NAME, userIdSystemBlockRule(bannedModules, init))); } if (testRun) { - setAnalyticLabels({[testRun]: modules}); + setAnalyticLabels({ [testRun]: modules }); } } @@ -61,10 +61,10 @@ export function reset() { } export function compareConfigs(old, current) { - const {modules: newModules, testRun: newTestRun} = current; - const {modules: oldModules, testRun: oldTestRun} = old; + const { modules: newModules, testRun: newTestRun } = current; + const { modules: oldModules, testRun: oldTestRun } = old; - const getModulesObject = (modules) => modules.reduce((acc, curr) => ({...acc, [curr.name]: curr.percentage}), {}); + const getModulesObject = (modules) => modules.reduce((acc, curr) => ({ ...acc, [curr.name]: curr.percentage }), {}); const percentageEqual = deepEqual( getModulesObject(oldModules), @@ -78,16 +78,16 @@ export function compareConfigs(old, current) { function userIdSystemBlockRule(bannedModules, init) { return (params) => { if ((params.init ?? true) === init && params[ACTIVITY_PARAM_COMPONENT_TYPE] === MODULE_TYPE_UID && bannedModules.has(params[ACTIVITY_PARAM_COMPONENT_NAME])) { - return {allow: false, reason: 'disabled due to AB testing'}; + return { allow: false, reason: 'disabled due to AB testing' }; } } }; export function getCalculatedSubmodules(modules = moduleConfig.modules) { return (modules || []) - .map(({name, percentage}) => { + .map(({ name, percentage }) => { const enabled = Math.random() < percentage; - return {name, percentage, enabled} + return { name, percentage, enabled } }); }; @@ -120,7 +120,7 @@ export function storeTestConfig(testRun, modules, storeSplits, storageManager) { return; } - const configToStore = {testRun, modules}; + const configToStore = { testRun, modules }; storeMethod(STORAGE_KEY, JSON.stringify(configToStore)); logInfo(`${MODULE_NAME}: AB test config successfully saved to ${storeSplits} storage`); }; diff --git a/modules/eplanningBidAdapter.js b/modules/eplanningBidAdapter.js index 7ef55e31784..27f5171e724 100644 --- a/modules/eplanningBidAdapter.js +++ b/modules/eplanningBidAdapter.js @@ -1,14 +1,14 @@ -import {isEmpty, parseSizesInput, isGptPubadsDefined, getWinDimensions} from '../src/utils.js'; -import {getGlobal} from '../src/prebidGlobal.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {getStorageManager} from '../src/storageManager.js'; -import {BANNER, VIDEO} from '../src/mediaTypes.js'; -import {isSlotMatchingAdUnitCode} from '../libraries/gptUtils/gptUtils.js'; -import {serializeSupplyChain} from '../libraries/schainSerializer/schainSerializer.js'; +import { isEmpty, parseSizesInput, isGptPubadsDefined, getWinDimensions } from '../src/utils.js'; +import { getGlobal } from '../src/prebidGlobal.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { getStorageManager } from '../src/storageManager.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; +import { isSlotMatchingAdUnitCode } from '../libraries/gptUtils/gptUtils.js'; +import { serializeSupplyChain } from '../libraries/schainSerializer/schainSerializer.js'; import { getBoundingClientRect } from '../libraries/boundingClientRect/boundingClientRect.js'; const BIDDER_CODE = 'eplanning'; -export const storage = getStorageManager({bidderCode: BIDDER_CODE}); +export const storage = getStorageManager({ bidderCode: BIDDER_CODE }); const rnd = Math.random(); const DEFAULT_SV = 'pbjs.e-planning.net'; const DEFAULT_ISV = 'i.e-planning.net'; @@ -298,7 +298,7 @@ function getSpaces(bidRequests, ml) { } const spacesStruct = getSpacesStruct(bidRequests); - const es = {str: '', vs: '', map: {}, impType: impType}; + const es = { str: '', vs: '', map: {}, impType: impType }; es.str = Object.keys(spacesStruct).map(size => spacesStruct[size].map((bid, i) => { es.vs += getVs(bid); @@ -420,6 +420,7 @@ function _mapAdUnitPathToElementId(adUnitCode) { } function _getAdSlotHTMLElement(adUnitCode) { + // TODO: this should use getAdUnitElement return document.getElementById(adUnitCode) || document.getElementById(_mapAdUnitPathToElementId(adUnitCode)); } diff --git a/modules/eskimiBidAdapter.js b/modules/eskimiBidAdapter.js index b345bf3b9af..118910827fd 100644 --- a/modules/eskimiBidAdapter.js +++ b/modules/eskimiBidAdapter.js @@ -1,8 +1,8 @@ -import {ortbConverter} from '../libraries/ortbConverter/converter.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER, VIDEO} from '../src/mediaTypes.js'; +import { ortbConverter } from '../libraries/ortbConverter/converter.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; import * as utils from '../src/utils.js'; -import {getBidIdParameter, logInfo, mergeDeep} from '../src/utils.js'; +import { getBidIdParameter, logInfo, mergeDeep } from '../src/utils.js'; import { getTimeZone } from '../libraries/timezone/timezone.js'; /** @@ -66,7 +66,7 @@ export const spec = { onTimeout: function (timeoutData) { logInfo('Timeout: ', timeoutData); }, - onBidderError: function ({error, bidderRequest}) { + onBidderError: function ({ error, bidderRequest }) { logInfo('Error: ', error, bidderRequest); }, } @@ -158,14 +158,14 @@ function buildRequests(validBidRequests, bidderRequest) { } function interpretResponse(response, request) { - return CONVERTER.fromORTB({request: request.data, response: response.body}).bids; + return CONVERTER.fromORTB({ request: request.data, response: response.body }).bids; } function buildVideoImp(bidRequest, imp) { const videoAdUnitParams = utils.deepAccess(bidRequest, `mediaTypes.${VIDEO}`, {}); const videoBidderParams = utils.deepAccess(bidRequest, `params.${VIDEO}`, {}); - const videoParams = {...videoAdUnitParams, ...videoBidderParams}; + const videoParams = { ...videoAdUnitParams, ...videoBidderParams }; const videoSizes = (videoAdUnitParams && videoAdUnitParams.playerSize) || []; @@ -184,14 +184,14 @@ function buildVideoImp(bidRequest, imp) { imp.video.plcmt = imp.video.plcmt || 4; } - return {...imp}; + return { ...imp }; } function buildBannerImp(bidRequest, imp) { const bannerAdUnitParams = utils.deepAccess(bidRequest, `mediaTypes.${BANNER}`, {}); const bannerBidderParams = utils.deepAccess(bidRequest, `params.${BANNER}`, {}); - const bannerParams = {...bannerAdUnitParams, ...bannerBidderParams}; + const bannerParams = { ...bannerAdUnitParams, ...bannerBidderParams }; const sizes = bidRequest.mediaTypes.banner.sizes; @@ -206,15 +206,15 @@ function buildBannerImp(bidRequest, imp) { } }); - return {...imp}; + return { ...imp }; } function createRequest(bidRequests, bidderRequest, mediaType) { - const data = CONVERTER.toORTB({bidRequests, bidderRequest, context: {mediaType}}) + const data = CONVERTER.toORTB({ bidRequests, bidderRequest, context: { mediaType } }) const bid = bidRequests.find((b) => b.params.placementId) if (!data.site) data.site = {} - data.site.ext = {placementId: parseInt(bid.params.placementId)} + data.site.ext = { placementId: parseInt(bid.params.placementId) } if (bidderRequest.gdprConsent) { if (!data.user) data.user = {}; diff --git a/modules/etargetBidAdapter.js b/modules/etargetBidAdapter.js index 89ec415b173..0ce137e519c 100644 --- a/modules/etargetBidAdapter.js +++ b/modules/etargetBidAdapter.js @@ -1,5 +1,5 @@ import { deepClone, deepSetValue, isFn, isPlainObject } from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; const BIDDER_CODE = 'etarget'; @@ -21,7 +21,7 @@ const countryMap = { export const spec = { code: BIDDER_CODE, gvlid: GVL_ID, - supportedMediaTypes: [ BANNER, VIDEO ], + supportedMediaTypes: [BANNER, VIDEO], isBidRequestValid: function (bid) { return !!(bid.params.refid && bid.params.country); }, diff --git a/modules/euidIdSystem.js b/modules/euidIdSystem.js index 9070c7efd3e..fb984a939ae 100644 --- a/modules/euidIdSystem.js +++ b/modules/euidIdSystem.js @@ -6,9 +6,9 @@ */ import { logInfo, logWarn, deepAccess } from '../src/utils.js'; -import {submodule} from '../src/hook.js'; -import {getStorageManager} from '../src/storageManager.js'; -import {MODULE_TYPE_UID} from '../src/activities/modules.js'; +import { submodule } from '../src/hook.js'; +import { getStorageManager } from '../src/storageManager.js'; +import { MODULE_TYPE_UID } from '../src/activities/modules.js'; import { Uid2GetId, Uid2CodeVersion, extractIdentityFromParams } from '../libraries/uid2IdSystemShared/uid2IdSystem_shared.js'; @@ -40,7 +40,7 @@ function createLogger(logger, prefix) { const _logInfo = createLogger(logInfo, LOG_PRE_FIX); const _logWarn = createLogger(logWarn, LOG_PRE_FIX); -export const storage = getStorageManager({moduleType: MODULE_TYPE_UID, moduleName: MODULE_NAME}); +export const storage = getStorageManager({ moduleType: MODULE_TYPE_UID, moduleName: MODULE_NAME }); function hasWriteToDeviceConsent(consentData) { const gdprApplies = consentData?.gdprApplies === true; diff --git a/modules/excoBidAdapter.js b/modules/excoBidAdapter.js index 5123120be88..3b5d6d4bc93 100644 --- a/modules/excoBidAdapter.js +++ b/modules/excoBidAdapter.js @@ -45,7 +45,7 @@ export class AdapterHelpers { createSyncUrl({ consentString, gppString, applicableSections, gdprApplies }, network) { try { const url = new URL(SYNC_URL); - const networks = [ '368531133' ]; + const networks = ['368531133']; if (network) { networks.push(network); @@ -392,7 +392,7 @@ export const spec = { */ interpretResponse: function (response, request) { const body = response?.body?.Result || response?.body || {}; - const converted = converter.fromORTB({response: body, request: request?.data}); + const converted = converter.fromORTB({ response: body, request: request?.data }); const bids = converted.bids || []; if (bids.length && !EVENTS.subscribed) { diff --git a/modules/experianRtdProvider.js b/modules/experianRtdProvider.js index cd415d4b32c..7200081869a 100644 --- a/modules/experianRtdProvider.js +++ b/modules/experianRtdProvider.js @@ -96,10 +96,10 @@ export const experianRtdObj = { if (userConsent != null) { if (userConsent.gdpr != null) { const { gdprApplies, consentString } = userConsent.gdpr; - mergeDeep(queryObj, {gdpr: gdprApplies, gdpr_consent: consentString}) + mergeDeep(queryObj, { gdpr: gdprApplies, gdpr_consent: consentString }) } if (userConsent.uspConsent != null) { - mergeDeep(queryObj, {us_privacy: userConsent.uspConsent}) + mergeDeep(queryObj, { us_privacy: userConsent.uspConsent }) } } const consentQueryString = Object.entries(queryObj).map(([key, val]) => `${key}=${val}`).join('&'); diff --git a/modules/express.js b/modules/express.js deleted file mode 100644 index a2998baed07..00000000000 --- a/modules/express.js +++ /dev/null @@ -1,210 +0,0 @@ -import { logMessage, logWarn, logError, logInfo } from '../src/utils.js'; -import {getGlobal} from '../src/prebidGlobal.js'; - -const MODULE_NAME = 'express'; -const pbjsInstance = getGlobal(); - -/** - * Express Module - * - * The express module allows the initiation of Prebid.js auctions automatically based on calls such as gpt.defineSlot. - * It works by monkey-patching the gpt methods and overloading their functionality. In order for this module to be - * used gpt must be included in the page, this module must be included in the Prebid.js bundle, and a call to - * pbjs.express() must be made. - * - * @param {Object[]} [adUnits = pbjs.adUnits] - an array of adUnits for express to operate on. - */ -pbjsInstance.express = function(adUnits = pbjsInstance.adUnits) { - logMessage('loading ' + MODULE_NAME); - - if (adUnits.length === 0) { - logWarn('no valid adUnits found, not loading ' + MODULE_NAME); - } - - // store gpt slots in a more performant hash lookup by elementId (adUnit code) - var gptSlotCache = {}; - // put adUnits in a more performant hash lookup by code. - var adUnitsCache = adUnits.reduce(function (cache, adUnit) { - if (adUnit.code && adUnit.bids) { - cache[adUnit.code] = adUnit; - } else { - logError('misconfigured adUnit', null, adUnit); - } - return cache; - }, {}); - - window.googletag = window.googletag || {}; - window.googletag.cmd = window.googletag.cmd || []; - window.googletag.cmd.push(function () { - // verify all necessary gpt functions exist - var gpt = window.googletag; - var pads = gpt.pubads; - if (!gpt.display || !gpt.enableServices || typeof pads !== 'function' || !pads().refresh || !pads().disableInitialLoad || !pads().getSlots || !pads().enableSingleRequest) { - logError('could not bind to gpt googletag api'); - return; - } - logMessage('running'); - - // function to convert google tag slot sizes to [[w,h],...] - function mapGptSlotSizes(aGPTSlotSizes) { - var aSlotSizes = []; - for (var i = 0; i < aGPTSlotSizes.length; i++) { - try { - aSlotSizes.push([aGPTSlotSizes[i].getWidth(), aGPTSlotSizes[i].getHeight()]); - } catch (e) { - logWarn('slot size ' + aGPTSlotSizes[i].toString() + ' not supported by' + MODULE_NAME); - } - } - return aSlotSizes; - } - - // a helper function to verify slots or get slots if not present - function defaultSlots(slots) { - return Array.isArray(slots) - ? slots.slice() - // eslint-disable-next-line no-undef - : googletag.pubads().getSlots().slice(); - } - - // maps gpt slots to adUnits, matches are copied to new array and removed from passed array. - function pickAdUnits(gptSlots) { - var adUnits = []; - // traverse backwards (since gptSlots is mutated) to find adUnits in cache and remove non-mapped slots - for (var i = gptSlots.length - 1; i > -1; i--) { - const gptSlot = gptSlots[i]; - const elemId = gptSlot.getSlotElementId(); - const adUnit = adUnitsCache[elemId]; - - if (adUnit) { - gptSlotCache[elemId] = gptSlot; // store by elementId - adUnit.sizes = adUnit.sizes || mapGptSlotSizes(gptSlot.getSizes()); - adUnits.push(adUnit); - gptSlots.splice(i, 1); - } - } - - return adUnits; - } - - // store original gpt functions that will be overridden - var fGptDisplay = gpt.display; - var fGptEnableServices = gpt.enableServices; - var fGptRefresh = pads().refresh; - var fGptDisableInitialLoad = pads().disableInitialLoad; - var fGptEnableSingleRequest = pads().enableSingleRequest; - - // override googletag.enableServices() - // - make sure fGptDisableInitialLoad() has been called so we can - // better control when slots are displayed, then call original - // fGptEnableServices() - gpt.enableServices = function () { - if (!bInitialLoadDisabled) { - fGptDisableInitialLoad.apply(pads()); - } - return fGptEnableServices.apply(gpt, arguments); - }; - - // override googletag.display() - // - call the real fGptDisplay(). this won't initiate auctions because we've disabled initial load - // - define all corresponding rubicon slots - // - if disableInitialLoad() has been called by the pub, done - // - else run an auction and call the real fGptRefresh() to - // initiate the DFP request - gpt.display = function (sElementId) { - logInfo('display:', sElementId); - // call original gpt display() function - fGptDisplay.apply(gpt, arguments); - - // if not SRA mode, get only the gpt slot corresponding to sEementId - var aGptSlots; - if (!bEnabledSRA) { - // eslint-disable-next-line no-undef - aGptSlots = googletag.pubads().getSlots().filter(function (oGptSlot) { - return oGptSlot.getSlotElementId() === sElementId; - }); - } - - aGptSlots = defaultSlots(aGptSlots).filter(function (gptSlot) { - return !gptSlot._displayed; - }); - - aGptSlots.forEach(function (gptSlot) { - gptSlot._displayed = true; - }); - - var adUnits = pickAdUnits(/* mutated: */ aGptSlots); - - if (!bInitialLoadDisabled) { - if (aGptSlots.length) { - fGptRefresh.apply(pads(), [aGptSlots]); - } - - if (adUnits.length) { - pbjsInstance.requestBids({ - adUnits: adUnits, - bidsBackHandler: function () { - pbjsInstance.setTargetingForGPTAsync(); - fGptRefresh.apply(pads(), [ - adUnits.map(function (adUnit) { - return gptSlotCache[adUnit.code]; - }) - ]); - } - }); - } - } - }; - - // override gpt refresh() function - // - run auctions for provided gpt slots, then initiate ad-server call - pads().refresh = function (aGptSlots, options) { - logInfo('refresh:', aGptSlots); - // get already displayed adUnits from aGptSlots if provided, else all defined gptSlots - aGptSlots = defaultSlots(aGptSlots); - var adUnits = pickAdUnits(/* mutated: */ aGptSlots).filter(function (adUnit) { - return gptSlotCache[adUnit.code]._displayed; - }); - - if (aGptSlots.length) { - fGptRefresh.apply(pads(), [aGptSlots, options]); - } - - if (adUnits.length) { - pbjsInstance.requestBids({ - adUnits: adUnits, - bidsBackHandler: function () { - pbjsInstance.setTargetingForGPTAsync(); - fGptRefresh.apply(pads(), [ - adUnits.map(function (adUnit) { - return gptSlotCache[adUnit.code]; - }), - options - ]); - } - }); - } - }; - - // override gpt disableInitialLoad function - // Register that initial load was called, meaning calls to display() - // should not initiate an ad-server request. Instead a call to - // refresh() will be needed to iniate the request. - // We will assume the pub is using this the correct way, calling it - // before enableServices() - var bInitialLoadDisabled = false; - pads().disableInitialLoad = function () { - bInitialLoadDisabled = true; - return fGptDisableInitialLoad.apply(window.googletag.pubads(), arguments); - }; - - // override gpt useSingleRequest function - // Register that SRA has been turned on - // We will assume the pub is using this the correct way, calling it - // before enableServices() - var bEnabledSRA = false; - pads().enableSingleRequest = function () { - bEnabledSRA = true; - return fGptEnableSingleRequest.apply(window.googletag.pubads(), arguments); - }; - }); -}; diff --git a/modules/fabrickIdSystem.js b/modules/fabrickIdSystem.js index 34fa990c080..c946667fcc3 100644 --- a/modules/fabrickIdSystem.js +++ b/modules/fabrickIdSystem.js @@ -115,9 +115,9 @@ export const fabrickIdSubmodule = { callback(); } }; - ajax(url, callbacks, null, {method: 'GET', withCredentials: true}); + ajax(url, callbacks, null, { method: 'GET', withCredentials: true }); }; - return {callback: resp}; + return { callback: resp }; } catch (e) { logError(`fabrickIdSystem encountered an error`, e); } diff --git a/modules/fanBidAdapter.js b/modules/fanBidAdapter.js index 04247011751..a3690cd6098 100644 --- a/modules/fanBidAdapter.js +++ b/modules/fanBidAdapter.js @@ -6,6 +6,7 @@ import { getBidFloor } from '../libraries/currencyUtils/floor.js'; import { getStorageManager } from '../src/storageManager.js'; import { Renderer } from '../src/Renderer.js'; import { getGptSlotInfoForAdUnitCode } from '../libraries/gptUtils/gptUtils.js'; +import { getAdUnitElement } from '../src/utils/adUnits.js'; const BIDDER_CODE = 'freedomadnetwork'; const BIDDER_VERSION = '0.2.0'; @@ -19,7 +20,7 @@ const DEFAULT_ENDPOINT = NETWORK_ENDPOINTS['fan']; const DEFAULT_CURRENCY = 'USD'; const DEFAULT_TTL = 300; -export const storage = getStorageManager({bidderCode: BIDDER_CODE}); +export const storage = getStorageManager({ bidderCode: BIDDER_CODE }); const converter = ortbConverter({ context: { @@ -349,8 +350,7 @@ function createRenderer(bid, videoPlayerUrl) { try { renderer.setRender(function (bidResponse) { - const divId = document.getElementById(bid.adUnitCode) ? bid.adUnitCode : getGptSlotInfoForAdUnitCode(bid.adUnitCode).divId; - const adUnit = document.getElementById(divId); + const adUnit = getAdUnitElement(bidResponse) ?? document.getElementById(getGptSlotInfoForAdUnitCode(bid.adUnitCode).divId) if (!window.createOutstreamPlayer) { logWarn('Renderer error: outstream player is not available'); diff --git a/modules/feedadBidAdapter.js b/modules/feedadBidAdapter.js index e6200bb3561..b382d34082b 100644 --- a/modules/feedadBidAdapter.js +++ b/modules/feedadBidAdapter.js @@ -1,7 +1,7 @@ -import {deepAccess, isArray, logWarn} from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER} from '../src/mediaTypes.js'; -import {ajax} from '../src/ajax.js'; +import { deepAccess, isArray, logWarn } from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER } from '../src/mediaTypes.js'; +import { ajax } from '../src/ajax.js'; /** * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest @@ -291,7 +291,7 @@ function createTrackingParams(data, klass) { if (!BID_METADATA.hasOwnProperty(bidId)) { return null; } - const {referer, transactionId} = BID_METADATA[bidId]; + const { referer, transactionId } = BID_METADATA[bidId]; delete BID_METADATA[bidId]; return { app_hybrid: false, diff --git a/modules/finativeBidAdapter.js b/modules/finativeBidAdapter.js index a1dbfb3ee0c..c744fc6b637 100644 --- a/modules/finativeBidAdapter.js +++ b/modules/finativeBidAdapter.js @@ -1,17 +1,17 @@ // jshint esversion: 6, es3: false, node: true 'use strict'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {NATIVE} from '../src/mediaTypes.js'; -import {_map, deepSetValue, isEmpty, setOnAny} from '../src/utils.js'; -import {convertOrtbRequestToProprietaryNative} from '../src/native.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { NATIVE } from '../src/mediaTypes.js'; +import { _map, deepSetValue, isEmpty, setOnAny } from '../src/utils.js'; +import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; import { getCurrencyFromBidderRequest } from '../libraries/ortb2Utils/currency.js'; const BIDDER_CODE = 'finative'; const DEFAULT_CUR = 'EUR'; const ENDPOINT_URL = 'https://b.finative.cloud/cds/rtb/bid?format=openrtb2.5&ssp=pb'; -const NATIVE_ASSET_IDS = {0: 'title', 1: 'body', 2: 'sponsoredBy', 3: 'image', 4: 'cta', 5: 'icon'}; +const NATIVE_ASSET_IDS = { 0: 'title', 1: 'body', 2: 'sponsoredBy', 3: 'image', 4: 'cta', 5: 'icon' }; const NATIVE_PARAMS = { title: { @@ -190,7 +190,7 @@ export const spec = { registerBidder(spec); function parseNative(bid) { - const {assets, link, imptrackers} = bid.adm.native; + const { assets, link, imptrackers } = bid.adm.native; const clickUrl = link.url.replace(/\$\{AUCTION_PRICE\}/g, bid.price); diff --git a/modules/fintezaAnalyticsAdapter.js b/modules/fintezaAnalyticsAdapter.js index 3f22fe444bd..71c74986a6f 100644 --- a/modules/fintezaAnalyticsAdapter.js +++ b/modules/fintezaAnalyticsAdapter.js @@ -2,12 +2,12 @@ import { parseUrl, logError } from '../src/utils.js'; import { ajax } from '../src/ajax.js'; import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; -import {getStorageManager} from '../src/storageManager.js'; +import { getStorageManager } from '../src/storageManager.js'; import { EVENTS } from '../src/constants.js'; -import {MODULE_TYPE_ANALYTICS} from '../src/activities/modules.js'; +import { MODULE_TYPE_ANALYTICS } from '../src/activities/modules.js'; const MODULE_CODE = 'finteza'; -const storage = getStorageManager({moduleType: MODULE_TYPE_ANALYTICS, moduleName: MODULE_CODE}); +const storage = getStorageManager({ moduleType: MODULE_TYPE_ANALYTICS, moduleName: MODULE_CODE }); const ANALYTICS_TYPE = 'endpoint'; const FINTEZA_HOST = 'https://content.mql5.com/tr'; @@ -76,7 +76,7 @@ function initFirstVisit() { cookies = {}; } - visitDate = cookies[ FIRST_VISIT_DATE ]; + visitDate = cookies[FIRST_VISIT_DATE]; if (!visitDate) { now = new Date(); @@ -181,7 +181,7 @@ function initSession() { cookies = {}; } - sessionId = cookies[ SESSION_ID ]; + sessionId = cookies[SESSION_ID]; if (!sessionId || !checkSessionByExpires() || @@ -269,7 +269,7 @@ function getTrackRequestLastTime() { // TODO: commented out because of rule violations cookie = {} // parseCookies(document.cookie); - cookie = cookie[ TRACK_TIME_KEY ]; + cookie = cookie[TRACK_TIME_KEY]; if (cookie) { return parseInt(cookie, 10); } @@ -282,7 +282,7 @@ function getAntiCacheParam() { const date = new Date(); const rand = (Math.random() * 99999 + 1) >>> 0; - return ([ date.getTime(), rand ].join('')); + return ([date.getTime(), rand].join('')); } function replaceBidder(str, bidder) { diff --git a/modules/flippBidAdapter.js b/modules/flippBidAdapter.js index d1e8048be85..fc1d8eaa27e 100644 --- a/modules/flippBidAdapter.js +++ b/modules/flippBidAdapter.js @@ -1,7 +1,7 @@ -import {isEmpty, parseUrl} from '../src/utils.js'; +import { isEmpty, parseUrl } from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER } from '../src/mediaTypes.js'; -import {getStorageManager} from '../src/storageManager.js'; +import { getStorageManager } from '../src/storageManager.js'; /** * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest @@ -28,7 +28,7 @@ const COMPACT_DEFAULT_HEIGHT = 600; const STANDARD_DEFAULT_HEIGHT = 1800; let userKey = null; -export const storage = getStorageManager({bidderCode: BIDDER_CODE}); +export const storage = getStorageManager({ bidderCode: BIDDER_CODE }); export function getUserKey(options = {}) { if (userKey) { @@ -127,9 +127,9 @@ export const spec = { siteId: bid.params.siteId, adTypes: getAdTypes(bid.params.creativeType), count: 1, - ...(!isEmpty(bid.params.zoneIds) && {zoneIds: bid.params.zoneIds}), + ...(!isEmpty(bid.params.zoneIds) && { zoneIds: bid.params.zoneIds }), properties: { - ...(!isEmpty(contentCode) && {contentCode: contentCode.slice(0, 32)}), + ...(!isEmpty(contentCode) && { contentCode: contentCode.slice(0, 32) }), }, options, prebid: { diff --git a/modules/floxisBidAdapter.js b/modules/floxisBidAdapter.js new file mode 100644 index 00000000000..c677f054a46 --- /dev/null +++ b/modules/floxisBidAdapter.js @@ -0,0 +1,149 @@ +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; +import { ortbConverter } from '../libraries/ortbConverter/converter.js'; +import { triggerPixel, mergeDeep } from '../src/utils.js'; + +const BIDDER_CODE = 'floxis'; +const DEFAULT_BID_TTL = 300; +const DEFAULT_CURRENCY = 'USD'; +const DEFAULT_NET_REVENUE = true; +const DEFAULT_REGION = 'us-e'; +const DEFAULT_PARTNER = BIDDER_CODE; +const PARTNER_REGION_WHITELIST = { + [DEFAULT_PARTNER]: [DEFAULT_REGION], +}; + +function isAllowedPartnerRegion(partner, region) { + return PARTNER_REGION_WHITELIST[partner]?.includes(region) || false; +} + +function getEndpointUrl(seat, region, partner) { + if (!isAllowedPartnerRegion(partner, region)) return null; + const host = partner === BIDDER_CODE + ? `${region}.floxis.tech` + : `${partner}-${region}.floxis.tech`; + return `https://${host}/pbjs?seat=${encodeURIComponent(seat)}`; +} + +function normalizeBidParams(params = {}) { + return { + seat: params.seat, + region: params.region ?? DEFAULT_REGION, + partner: params.partner ?? DEFAULT_PARTNER + }; +} + +const CONVERTER = ortbConverter({ + context: { + netRevenue: DEFAULT_NET_REVENUE, + ttl: DEFAULT_BID_TTL, + currency: DEFAULT_CURRENCY + }, + imp(buildImp, bidRequest, context) { + const imp = buildImp(bidRequest, context); + imp.secure = bidRequest.ortb2Imp?.secure ?? 1; + + let floorInfo; + if (typeof bidRequest.getFloor === 'function') { + try { + floorInfo = bidRequest.getFloor({ + currency: DEFAULT_CURRENCY, + mediaType: '*', + size: '*' + }); + } catch (e) { } + } + const floor = floorInfo?.floor; + const floorCur = floorInfo?.currency || DEFAULT_CURRENCY; + if (typeof floor === 'number' && !isNaN(floor)) { + imp.bidfloor = floor; + imp.bidfloorcur = floorCur; + } + + return imp; + }, + request(buildRequest, imps, bidderRequest, context) { + const req = buildRequest(imps, bidderRequest, context); + mergeDeep(req, { + at: 1, + ext: { + prebid: { + adapter: BIDDER_CODE, + version: '$prebid.version$' + } + } + }); + return req; + } +}); + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [BANNER, VIDEO, NATIVE], + + isBidRequestValid(bid) { + const params = bid?.params; + if (!params) return false; + const { seat, region, partner } = normalizeBidParams(params); + if (typeof seat !== 'string' || !seat.length) return false; + if (!isAllowedPartnerRegion(partner, region)) return false; + return true; + }, + + buildRequests(validBidRequests = [], bidderRequest = {}) { + if (!validBidRequests.length) return []; + const filteredBidRequests = validBidRequests.filter((bidRequest) => spec.isBidRequestValid(bidRequest)); + if (!filteredBidRequests.length) return []; + + const bidRequestsByParams = filteredBidRequests.reduce((groups, bidRequest) => { + const { seat, region, partner } = normalizeBidParams(bidRequest.params); + const key = `${seat}|${region}|${partner}`; + groups[key] = groups[key] || []; + groups[key].push({ + ...bidRequest, + params: { + ...bidRequest.params, + seat, + region, + partner + } + }); + return groups; + }, {}); + + return Object.values(bidRequestsByParams).map((groupedBidRequests) => { + const { seat, region, partner } = groupedBidRequests[0].params; + const url = getEndpointUrl(seat, region, partner); + if (!url) return null; + return { + method: 'POST', + url, + data: CONVERTER.toORTB({ bidRequests: groupedBidRequests, bidderRequest }), + options: { + withCredentials: true, + contentType: 'text/plain' + } + }; + }).filter(Boolean); + }, + + interpretResponse(response, request) { + if (!response?.body || !request?.data) return []; + return CONVERTER.fromORTB({ request: request.data, response: response.body })?.bids || []; + }, + + getUserSyncs() { + return []; + }, + + onBidWon(bid) { + if (bid.burl) { + triggerPixel(bid.burl); + } + if (bid.nurl) { + triggerPixel(bid.nurl); + } + } +}; + +registerBidder(spec); diff --git a/modules/floxisBidAdapter.md b/modules/floxisBidAdapter.md new file mode 100644 index 00000000000..f36db0c6577 --- /dev/null +++ b/modules/floxisBidAdapter.md @@ -0,0 +1,59 @@ +# Overview + +``` +Module Name: Floxis Bidder Adapter +Module Type: Bidder Adapter +Maintainer: admin@floxis.tech +``` + +# Description + +The Floxis Bid Adapter enables integration with the Floxis programmatic advertising platform via Prebid.js. It supports banner, video (instream and outstream), and native formats. + +**Key Features:** +- Banner, Video and Native ad support +- OpenRTB 2.x compliant +- Privacy regulation compliance (GDPR, USP, GPP, COPPA) +- Prebid.js Floors Module support + +## Supported Media Types +- Banner +- Video +- Native + +## Floors Module Support +The Floxis Bid Adapter supports the Prebid.js [Floors Module](https://docs.prebid.org/dev-docs/modules/floors.html). Floor values are automatically included in the OpenRTB request as `imp.bidfloor` and `imp.bidfloorcur`. + +## Privacy +Privacy fields (GDPR, USP, GPP, COPPA) are handled by Prebid.js core and automatically included in the OpenRTB request. + +## Example Usage +```javascript +pbjs.addAdUnits([ + { + code: 'adunit-1', + mediaTypes: { banner: { sizes: [[300, 250]] } }, + bids: [{ + bidder: 'floxis', + params: { + seat: 'testSeat', + region: 'us-e', + partner: 'floxis' + } + }] + } +]); +``` + +# Configuration + +## Parameters + +| Name | Scope | Description | Example | Type | +| --- | --- | --- | --- | --- | +| `seat` | required | Seat identifier | `'testSeat'` | `string` | +| `region` | required | Region identifier for routing | `'us-e'` | `string` | +| `partner` | required | Partner identifier | `'floxis'` | `string` | + +## Testing +Unit tests are provided in `test/spec/modules/floxisBidAdapter_spec.js` and cover validation, request building, response interpretation, and bid-won notifications. diff --git a/modules/fpdModule/index.js b/modules/fpdModule/index.js index 4beb07b5656..6d18251d3d8 100644 --- a/modules/fpdModule/index.js +++ b/modules/fpdModule/index.js @@ -4,9 +4,9 @@ */ import { config } from '../../src/config.js'; import { module, getHook } from '../../src/hook.js'; -import {logError} from '../../src/utils.js'; -import {PbPromise} from '../../src/utils/promise.js'; -import {timedAuctionHook} from '../../src/utils/perfMetrics.js'; +import { logError } from '../../src/utils.js'; +import { PbPromise } from '../../src/utils/promise.js'; +import { timedAuctionHook } from '../../src/utils/perfMetrics.js'; const submodules = []; @@ -18,19 +18,19 @@ export function reset() { submodules.length = 0; } -export function processFpd({global = {}, bidder = {}} = {}) { +export function processFpd({ global = {}, bidder = {} } = {}) { const modConf = config.getConfig('firstPartyData') || {}; - let result = PbPromise.resolve({global, bidder}); + let result = PbPromise.resolve({ global, bidder }); submodules.sort((a, b) => { return ((a.queue || 1) - (b.queue || 1)); }).forEach(submodule => { result = result.then( - ({global, bidder}) => PbPromise.resolve(submodule.processFpd(modConf, {global, bidder})) + ({ global, bidder }) => PbPromise.resolve(submodule.processFpd(modConf, { global, bidder })) .catch((err) => { logError(`Error in FPD module ${submodule.name}`, err); return {}; }) - .then((result) => ({global: result.global || global, bidder: result.bidder || bidder})) + .then((result) => ({ global: result.global || global, bidder: result.bidder || bidder })) ); }); return result; diff --git a/modules/freeWheelAdserverVideo.js b/modules/freeWheelAdserverVideo.js deleted file mode 100644 index cb4bd938373..00000000000 --- a/modules/freeWheelAdserverVideo.js +++ /dev/null @@ -1,19 +0,0 @@ -/** - * This module adds Freewheel support for Video to Prebid. - */ - -import { registerVideoSupport } from '../src/adServerManager.js'; -import { getHook, submodule } from '../src/hook.js'; - -export const adpodUtils = {}; -export function notifyTranslationModule(fn) { - fn.call(this, 'freewheel'); -} - -getHook('registerAdserver').before(notifyTranslationModule); - -registerVideoSupport('freewheel', { - getTargeting: (args) => adpodUtils.getTargeting(args) -}); - -submodule('adpod', adpodUtils); diff --git a/modules/freepassBidAdapter.js b/modules/freepassBidAdapter.js index a765b5f5521..69373be46fc 100644 --- a/modules/freepassBidAdapter.js +++ b/modules/freepassBidAdapter.js @@ -1,7 +1,7 @@ -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {logMessage} from '../src/utils.js'; -import {BANNER} from '../src/mediaTypes.js'; -import {ortbConverter} from '../libraries/ortbConverter/converter.js' +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { logMessage } from '../src/utils.js'; +import { BANNER } from '../src/mediaTypes.js'; +import { ortbConverter } from '../libraries/ortbConverter/converter.js' const BIDDER_SERVICE_URL = 'https://bidding-dsp.ad-m.asia/dsp/api/bid/s/s/freepass'; @@ -108,7 +108,7 @@ export const spec = { interpretResponse(serverResponse, bidRequest) { logMessage('FreePass BidAdapter is interpreting server response: ', serverResponse); logMessage('FreePass BidAdapter is using bid request: ', bidRequest); - const bids = converter.fromORTB({response: serverResponse.body, request: bidRequest.data}).bids; + const bids = converter.fromORTB({ response: serverResponse.body, request: bidRequest.data }).bids; logMessage('FreePass BidAdapter interpreted ORTB bids as ', bids); return bids; diff --git a/modules/freepassIdSystem.js b/modules/freepassIdSystem.js index f00b2d6e629..1e5ccf46f20 100644 --- a/modules/freepassIdSystem.js +++ b/modules/freepassIdSystem.js @@ -50,7 +50,7 @@ export const freepassIdSubmodule = { idObject.freepassId = freepassData.commonId; } - return {id: idObject}; + return { id: idObject }; }, extendId: function (config, _, storedId) { diff --git a/modules/ftrackIdSystem.js b/modules/ftrackIdSystem.js index d22d7b28a35..f5612996c9c 100644 --- a/modules/ftrackIdSystem.js +++ b/modules/ftrackIdSystem.js @@ -6,10 +6,10 @@ */ import * as utils from '../src/utils.js'; -import {submodule} from '../src/hook.js'; -import {getStorageManager} from '../src/storageManager.js'; -import {loadExternalScript} from '../src/adloader.js'; -import {MODULE_TYPE_UID} from '../src/activities/modules.js'; +import { submodule } from '../src/hook.js'; +import { getStorageManager } from '../src/storageManager.js'; +import { loadExternalScript } from '../src/adloader.js'; +import { MODULE_TYPE_UID } from '../src/activities/modules.js'; /** * @typedef {import('../modules/userId/index.js').Submodule} Submodule @@ -24,7 +24,7 @@ const LOCAL_STORAGE_EXP_DAYS = 30; const LOCAL_STORAGE = 'html5'; const FTRACK_STORAGE_NAME = 'ftrackId'; const FTRACK_PRIVACY_STORAGE_NAME = `${FTRACK_STORAGE_NAME}_privacy`; -const storage = getStorageManager({moduleType: MODULE_TYPE_UID, moduleName: MODULE_NAME}); +const storage = getStorageManager({ moduleType: MODULE_TYPE_UID, moduleName: MODULE_NAME }); const consentInfo = { gdpr: { @@ -191,7 +191,7 @@ export const ftrackIdSubmodule = { isThereConsent: function(consentData) { let consentValue = true; - const {gdpr, usp} = consentData ?? {}; + const { gdpr, usp } = consentData ?? {}; /* * Scenario 1: GDPR * if GDPR Applies is true|1, we do not have consent diff --git a/modules/fwsspBidAdapter.js b/modules/fwsspBidAdapter.js index f64c21e71c4..efd10a5c75c 100644 --- a/modules/fwsspBidAdapter.js +++ b/modules/fwsspBidAdapter.js @@ -13,7 +13,7 @@ export const spec = { code: BIDDER_CODE, gvlid: GVL_ID, supportedMediaTypes: [BANNER, VIDEO], - aliases: [ 'freewheel-mrm'], // aliases for fwssp + aliases: ['freewheel-mrm'], // aliases for fwssp /** * Determines whether or not the given bid request is valid. diff --git a/modules/gamAdServerVideo.js b/modules/gamAdServerVideo.js index d5541c16424..3fc5dddd678 100644 --- a/modules/gamAdServerVideo.js +++ b/modules/gamAdServerVideo.js @@ -9,7 +9,6 @@ import { auctionManager } from '../src/auctionManager.js'; import { config } from '../src/config.js'; import { EVENTS } from '../src/constants.js'; import * as events from '../src/events.js'; -import { getHook } from '../src/hook.js'; import { getRefererInfo } from '../src/refererDetection.js'; import { targeting } from '../src/targeting.js'; import { @@ -22,12 +21,13 @@ import { parseSizesInput, parseUrl } from '../src/utils.js'; -import {DEFAULT_GAM_PARAMS, GAM_ENDPOINT, gdprParams} from '../libraries/gamUtils/gamUtils.js'; +import { DEFAULT_GAM_PARAMS, GAM_ENDPOINT, gdprParams } from '../libraries/gamUtils/gamUtils.js'; import { vastLocalCache } from '../src/videoCache.js'; import { fetch } from '../src/ajax.js'; import XMLUtil from '../libraries/xmlUtils/xmlUtils.js'; -import {getGlobalVarName} from '../src/buildOptions.js'; +import { getGlobalVarName } from '../src/buildOptions.js'; +import { gppDataHandler, uspDataHandler } from '../src/consentHandler.js'; /** * @typedef {Object} DfpVideoParams * @@ -88,7 +88,7 @@ export function buildGamVideoUrl(options) { if (options.url) { // when both `url` and `params` are given, parsed url will be overwriten // with any matching param components - urlComponents = parseUrl(options.url, {noDecodeWholeURL: true}); + urlComponents = parseUrl(options.url, { noDecodeWholeURL: true }); if (isEmpty(options.params)) { return buildUrlFromAdserverUrlComponents(urlComponents, bid, options); @@ -118,6 +118,22 @@ export function buildGamVideoUrl(options) { gdprParams() ); + // The IMA player adds usp info, but not gpp info + // For cases where the CMP only exposes gpp but not usp, + // it is better to derive an usp string from the gpp info and include it in the url + if (window.google?.ima) { + const usPrivacy = uspDataHandler.getConsentData?.(); + const gpp = gppDataHandler.getConsentData?.(); + + if (!usPrivacy && gpp) { + // Extract an usPrivacy string from the GPP string if possible + const uspFromGpp = retrieveUspInfoFromGpp(gpp); + if (uspFromGpp) { + queryParams['us_privacy'] = uspFromGpp; + } + } + } + const descriptionUrl = getDescriptionUrl(bid, options, 'params'); if (descriptionUrl) { queryParams.description_url = descriptionUrl; } @@ -182,12 +198,6 @@ export function buildGamVideoUrl(options) { return buildUrl(Object.assign({}, GAM_ENDPOINT, urlComponents, { search: queryParams })); } -export function notifyTranslationModule(fn) { - fn.call(this, 'dfp'); -} - -if (config.getConfig('brandCategoryTranslation.translationFile')) { getHook('registerAdserver').before(notifyTranslationModule); } - /** * Builds a video url from a base dfp video url and a winning bid, appending * Prebid-specific key-values. @@ -244,7 +254,7 @@ function getCustParams(bid, options, urlCustParams) { ); // TODO: WTF is this? just firing random events, guessing at the argument, hoping noone notices? - events.emit(EVENTS.SET_TARGETING, {[adUnit.code]: prebidTargetingSet}); + events.emit(EVENTS.SET_TARGETING, { [adUnit.code]: prebidTargetingSet }); // merge the prebid + publisher targeting sets const publisherTargetingSet = options?.params?.cust_params; @@ -292,7 +302,27 @@ async function getVastForLocallyCachedBids(gamVastWrapper, localCacheMap) { }; export async function getVastXml(options, localCacheMap = vastLocalCache) { - const vastUrl = buildGamVideoUrl(options); + let vastUrl = buildGamVideoUrl(options); + + const adUnit = options.adUnit; + const video = adUnit?.mediaTypes?.video; + const sdkApis = (video?.api || []).join(','); + const usPrivacy = uspDataHandler.getConsentData?.(); + // Adding parameters required by ima + if (config.getConfig('cache.useLocal') && window.google?.ima) { + vastUrl = new URL(vastUrl); + const imaSdkVersion = `h.${window.google.ima.VERSION}`; + vastUrl.searchParams.set('omid_p', `Google1/${imaSdkVersion}`); + vastUrl.searchParams.set('sdkv', imaSdkVersion); + if (sdkApis) { + vastUrl.searchParams.set('sdk_apis', sdkApis); + } + if (usPrivacy) { + vastUrl.searchParams.set('us_privacy', usPrivacy); + } + vastUrl = vastUrl.toString(); + } + const response = await fetch(vastUrl); if (!response.ok) { throw new Error('Unable to fetch GAM VAST wrapper'); @@ -307,6 +337,41 @@ export async function getVastXml(options, localCacheMap = vastLocalCache) { return gamVastWrapper; } +/** + * Extract a US Privacy string from the GPP data + */ +function retrieveUspInfoFromGpp(gpp) { + if (!gpp) { + return undefined; + } + const parsedSections = gpp.gppData?.parsedSections; + if (parsedSections) { + if (parsedSections.uspv1) { + const usp = parsedSections.uspv1; + return `${usp.Version}${usp.Notice}${usp.OptOutSale}${usp.LspaCovered}` + } else { + let saleOptOut; + let saleOptOutNotice; + Object.values(parsedSections).forEach(parsedSection => { + (Array.isArray(parsedSection) ? parsedSection : [parsedSection]).forEach(ps => { + const sectionSaleOptOut = ps.SaleOptOut; + const sectionSaleOptOutNotice = ps.SaleOptOutNotice; + if (saleOptOut === undefined && saleOptOutNotice === undefined && sectionSaleOptOut != null && sectionSaleOptOutNotice != null) { + saleOptOut = sectionSaleOptOut; + saleOptOutNotice = sectionSaleOptOutNotice; + } + }); + }); + if (saleOptOut !== undefined && saleOptOutNotice !== undefined) { + const uspOptOutSale = saleOptOut === 0 ? '-' : saleOptOut === 1 ? 'Y' : 'N'; + const uspOptOutNotice = saleOptOutNotice === 0 ? '-' : saleOptOutNotice === 1 ? 'Y' : 'N'; + const uspLspa = uspOptOutSale === '-' && uspOptOutNotice === '-' ? '-' : 'Y'; + return `1${uspOptOutNotice}${uspOptOutSale}${uspLspa}`; + } + } + } + return undefined +} export async function getBase64BlobContent(blobUrl) { const response = await fetch(blobUrl); diff --git a/modules/gamAdpod.js b/modules/gamAdpod.js deleted file mode 100644 index c21c71c0c3c..00000000000 --- a/modules/gamAdpod.js +++ /dev/null @@ -1,95 +0,0 @@ -import {submodule} from '../src/hook.js'; -import {buildUrl, deepAccess, formatQS, logError, parseSizesInput} from '../src/utils.js'; -import {auctionManager} from '../src/auctionManager.js'; -import {DEFAULT_GAM_PARAMS, GAM_ENDPOINT, gdprParams} from '../libraries/gamUtils/gamUtils.js'; -import {registerVideoSupport} from '../src/adServerManager.js'; - -export const adpodUtils = {}; - -/** - * @typedef {Object} DfpAdpodOptions - * - * @param {string} code Ad Unit code - * @param {Object} params Query params which should be set on the DFP request. - * These will override this module's defaults whenever they conflict. - * @param {function} callback Callback function to execute when master tag is ready - */ - -/** - * Creates master tag url for long-form - * @param {DfpAdpodOptions} options - * @returns {string} A URL which calls DFP with custom adpod targeting key values to compete with rest of the demand in DFP - */ -export function buildAdpodVideoUrl({code, params, callback} = {}) { - // TODO: the public API for this does not take in enough info to fill all DFP params (adUnit/bid), - // and is marked "alpha": https://docs.prebid.org/dev-docs/publisher-api-reference/adServers.gam.buildAdpodVideoUrl.html - if (!params || !callback) { - logError(`A params object and a callback is required to use pbjs.adServers.gam.buildAdpodVideoUrl`); - return; - } - - const derivedParams = { - correlator: Date.now(), - sz: getSizeForAdUnit(code), - url: encodeURIComponent(location.href), - }; - - function getSizeForAdUnit(code) { - const adUnit = auctionManager.getAdUnits() - .filter((adUnit) => adUnit.code === code) - const sizes = deepAccess(adUnit[0], 'mediaTypes.video.playerSize'); - return parseSizesInput(sizes).join('|'); - } - - adpodUtils.getTargeting({ - 'codes': [code], - 'callback': createMasterTag - }); - - function createMasterTag(err, targeting) { - if (err) { - callback(err, null); - return; - } - - const initialValue = { - [adpodUtils.TARGETING_KEY_PB_CAT_DUR]: undefined, - [adpodUtils.TARGETING_KEY_CACHE_ID]: undefined - }; - let customParams = {}; - if (targeting[code]) { - customParams = targeting[code].reduce((acc, curValue) => { - if (Object.keys(curValue)[0] === adpodUtils.TARGETING_KEY_PB_CAT_DUR) { - acc[adpodUtils.TARGETING_KEY_PB_CAT_DUR] = (typeof acc[adpodUtils.TARGETING_KEY_PB_CAT_DUR] !== 'undefined') ? acc[adpodUtils.TARGETING_KEY_PB_CAT_DUR] + ',' + curValue[adpodUtils.TARGETING_KEY_PB_CAT_DUR] : curValue[adpodUtils.TARGETING_KEY_PB_CAT_DUR]; - } else if (Object.keys(curValue)[0] === adpodUtils.TARGETING_KEY_CACHE_ID) { - acc[adpodUtils.TARGETING_KEY_CACHE_ID] = curValue[adpodUtils.TARGETING_KEY_CACHE_ID] - } - return acc; - }, initialValue); - } - - const encodedCustomParams = encodeURIComponent(formatQS(customParams)); - - const queryParams = Object.assign({}, - DEFAULT_GAM_PARAMS, - derivedParams, - params, - { cust_params: encodedCustomParams }, - gdprParams(), - ); - - const masterTag = buildUrl({ - ...GAM_ENDPOINT, - search: queryParams - }); - - callback(null, masterTag); - } -} - -registerVideoSupport('gam', { - buildAdpodVideoUrl: buildAdpodVideoUrl, - getAdpodTargeting: (args) => adpodUtils.getTargeting(args) -}); - -submodule('adpod', adpodUtils); diff --git a/modules/gamoshiBidAdapter.js b/modules/gamoshiBidAdapter.js index 66e74badf5e..f1ae1d1aab4 100644 --- a/modules/gamoshiBidAdapter.js +++ b/modules/gamoshiBidAdapter.js @@ -10,12 +10,13 @@ import { logWarn, mergeDeep } from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {Renderer} from '../src/Renderer.js'; -import {BANNER, VIDEO} from '../src/mediaTypes.js'; -import {ortbConverter} from '../libraries/ortbConverter/converter.js'; -import {ortb25Translator} from '../libraries/ortb2.5Translator/translator.js'; -import {getCurrencyFromBidderRequest} from '../libraries/ortb2Utils/currency.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { Renderer } from '../src/Renderer.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; +import { ortbConverter } from '../libraries/ortbConverter/converter.js'; +import { ortb25Translator } from '../libraries/ortb2.5Translator/translator.js'; +import { getCurrencyFromBidderRequest } from '../libraries/ortb2Utils/currency.js'; +import { getAdUnitElement } from '../src/utils/adUnits.js'; const ENDPOINTS = { 'gamoshi': 'https://rtb.gamoshi.io', 'cleanmedianet': 'https://bidder.cleanmediaads.com' @@ -208,7 +209,7 @@ export const spec = { bidResponse.ext['utrk'] .forEach(pixel => { const url = helper.replaceMacros(pixel.url, params); - syncs.push({type: pixel.type, url}); + syncs.push({ type: pixel.type, url }); }); } if (Array.isArray(bidResponse.seatbid)) { @@ -219,7 +220,7 @@ export const spec = { bid.ext['utrk'] .forEach(pixel => { const url = helper.replaceMacros(pixel.url, params); - syncs.push({type: pixel.type, url}); + syncs.push({ type: pixel.type, url }); }); } }); @@ -250,7 +251,7 @@ function renderOutstream(bid) { window['GamoshiPlayer'].renderAd({ id: unitId, debug: window.location.href.indexOf('pbjsDebug') >= 0, - placement: document.getElementById(bid.adUnitCode), + placement: getAdUnitElement(bid), width: bid.width, height: bid.height, events: { diff --git a/modules/gemiusIdSystem.ts b/modules/gemiusIdSystem.ts index 08b4cf1b053..2894eeaab9b 100644 --- a/modules/gemiusIdSystem.ts +++ b/modules/gemiusIdSystem.ts @@ -60,10 +60,10 @@ function retrieveId(primaryScriptWindow: PrimaryScriptWindow, callback: (id: Ser try { primaryScriptWindow.gemius_cmd('get_ruid', function (ruid, desc) { if (desc.status === 'ok') { - setResult({id: ruid}); + setResult({ id: ruid }); } else if (desc.status === 'no-consent') { logInfo(LOG_PREFIX + 'failed to get id, no consent'); - setResult({id: null}); + setResult({ id: null }); } else { logError(LOG_PREFIX + 'failed to get id, response: ' + desc.status); setResult(); @@ -80,15 +80,15 @@ export const gemiusIdSubmodule: IdProviderSpec = { gvlid: GVLID, decode(value) { if (isStr(value?.['id'])) { - return {[MODULE_NAME]: value['id']}; + return { [MODULE_NAME]: value['id'] }; } return undefined; }, - getId(_, {gdpr: consentData}: Partial = {}) { + getId(_, { gdpr: consentData }: Partial = {}) { if (consentData && typeof consentData.gdprApplies === 'boolean' && consentData.gdprApplies) { if (REQUIRED_PURPOSES.some(purposeId => !(consentData.vendorData?.purpose as any)?.consents?.[purposeId])) { logInfo(LOG_PREFIX + 'getId, no consent'); - return {id: {id: null}}; + return { id: { id: null } }; } } diff --git a/modules/genericAnalyticsAdapter.ts b/modules/genericAnalyticsAdapter.ts index eca61dce170..0b22b529ef0 100644 --- a/modules/genericAnalyticsAdapter.ts +++ b/modules/genericAnalyticsAdapter.ts @@ -1,11 +1,11 @@ -import AnalyticsAdapter, {type DefaultOptions} from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; -import {prefixLog, isPlainObject} from '../src/utils.js'; -import {type Events, has as hasEvent} from '../src/events.js'; +import AnalyticsAdapter, { type DefaultOptions } from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; +import { prefixLog, isPlainObject } from '../src/utils.js'; +import { type Events, has as hasEvent } from '../src/events.js'; import adapterManager from '../src/adapterManager.js'; -import {ajaxBuilder} from '../src/ajax.js'; -import type {AnyFunction} from "../src/types/functions"; +import { ajaxBuilder } from '../src/ajax.js'; +import type { AnyFunction } from "../src/types/functions"; -type EventMapping = {[E in keyof Events]?: (payload: Events[E][0]) => any}; +type EventMapping = { [E in keyof Events]?: (payload: Events[E][0]) => any }; type BaseOptions = { /** @@ -92,8 +92,8 @@ const TYPES = { const MAX_CALL_DEPTH = 20; export function GenericAnalytics() { - const parent = AnalyticsAdapter<'generic'>({analyticsType: 'endpoint'}); - const {logError, logWarn} = prefixLog('Generic analytics:'); + const parent = AnalyticsAdapter<'generic'>({ analyticsType: 'endpoint' }); + const { logError, logWarn } = prefixLog('Generic analytics:'); let batch = []; let callDepth = 0; let options, handler, timer, translate; @@ -161,7 +161,7 @@ export function GenericAnalytics() { if (!eventHandlers) { return (data) => data; } - return function ({eventType, args}) { + return function ({ eventType, args }) { if (eventHandlers.hasOwnProperty(eventType)) { try { return eventHandlers[eventType](args); @@ -205,16 +205,16 @@ export function GenericAnalytics() { ) } -export function defaultHandler({url, method, batchSize, ajax = ajaxBuilder()}) { +export function defaultHandler({ url, method, batchSize, ajax = ajaxBuilder() }) { const callbacks = { success() {}, error() {} } const extract = batchSize > 1 ? (events) => events : (events) => events[0]; - const serialize = method === 'GET' ? (data) => ({data: JSON.stringify(data)}) : (data) => JSON.stringify(data); + const serialize = method === 'GET' ? (data) => ({ data: JSON.stringify(data) }) : (data) => JSON.stringify(data); return function (events) { - ajax(url, callbacks, serialize(extract(events)), {method, keepalive: true}) + ajax(url, callbacks, serialize(extract(events)), { method, keepalive: true }) } } diff --git a/modules/geolocationRtdProvider.ts b/modules/geolocationRtdProvider.ts index 5283a33a1a1..9a8357a289b 100644 --- a/modules/geolocationRtdProvider.ts +++ b/modules/geolocationRtdProvider.ts @@ -1,11 +1,11 @@ -import {submodule} from '../src/hook.js'; -import {isFn, logError, deepAccess, deepSetValue, logInfo, logWarn, timestamp} from '../src/utils.js'; +import { submodule } from '../src/hook.js'; +import { isFn, logError, deepAccess, deepSetValue, logInfo, logWarn, timestamp } from '../src/utils.js'; import { ACTIVITY_TRANSMIT_PRECISE_GEO } from '../src/activities/activities.js'; import { MODULE_TYPE_RTD } from '../src/activities/modules.js'; import { isActivityAllowed } from '../src/activities/rules.js'; import { activityParams } from '../src/activities/activityParams.js'; -import {VENDORLESS_GVLID} from '../src/consentHandler.js'; -import type {RtdProviderSpec} from "./rtdModule/spec.ts"; +import { VENDORLESS_GVLID } from '../src/consentHandler.js'; +import type { RtdProviderSpec } from "./rtdModule/spec.ts"; let permissionsAvailable = true; let geolocation; diff --git a/modules/getintentBidAdapter.js b/modules/getintentBidAdapter.js index e85fd4b6f28..5d6499b418c 100644 --- a/modules/getintentBidAdapter.js +++ b/modules/getintentBidAdapter.js @@ -1,4 +1,4 @@ -import {getBidIdParameter, isFn, isInteger, logError} from '../src/utils.js'; +import { getBidIdParameter, isFn, isInteger, logError } from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; /** diff --git a/modules/gjirafaBidAdapter.js b/modules/gjirafaBidAdapter.js index 3218b32732a..2c1a4f87131 100644 --- a/modules/gjirafaBidAdapter.js +++ b/modules/gjirafaBidAdapter.js @@ -17,7 +17,7 @@ const SIZE_SEPARATOR = ';'; const BISKO_ID = 'biskoId'; const STORAGE_ID = 'bisko-sid'; const SEGMENTS = 'biskoSegments'; -const storage = getStorageManager({bidderCode: BIDDER_CODE}); +const storage = getStorageManager({ bidderCode: BIDDER_CODE }); export const spec = { code: BIDDER_CODE, diff --git a/modules/glomexBidAdapter.js b/modules/glomexBidAdapter.js index 8a1ae7292f8..88bb43fadc6 100644 --- a/modules/glomexBidAdapter.js +++ b/modules/glomexBidAdapter.js @@ -1,5 +1,5 @@ -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER} from '../src/mediaTypes.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER } from '../src/mediaTypes.js'; const ENDPOINT = 'https://prebid.mes.glomex.cloud/request-bid' const BIDDER_CODE = 'glomex' @@ -32,7 +32,8 @@ export const spec = { isAmp: refererInfo.isAmp, numIframes: refererInfo.numIframes, reachedTop: refererInfo.reachedTop, - referer: refererInfo.topmostLocation}, + referer: refererInfo.topmostLocation + }, gdprConsent: { consentString: gdprConsent.consentString, gdprApplies: gdprConsent.gdprApplies diff --git a/modules/gmosspBidAdapter.js b/modules/gmosspBidAdapter.js index 7bff19bb2c0..28b2d7cb7b8 100644 --- a/modules/gmosspBidAdapter.js +++ b/modules/gmosspBidAdapter.js @@ -1,6 +1,5 @@ import { getCurrencyFromBidderRequest } from '../libraries/ortb2Utils/currency.js'; import { tryAppendQueryString } from '../libraries/urlUtils/urlUtils.js'; -import {getDNT} from '../libraries/dnt/index.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER } from '../src/mediaTypes.js'; import { @@ -51,7 +50,6 @@ export const spec = { const urlInfo = getUrlInfo(bidderRequest.refererInfo); const cur = getCurrencyType(bidderRequest); - const dnt = getDNT() ? '1' : '0'; for (let i = 0; i < validBidRequests.length; i++) { let queryString = ''; @@ -76,7 +74,6 @@ export const spec = { queryString = tryAppendQueryString(queryString, 'meta_url', urlInfo.canonicalLink); queryString = tryAppendQueryString(queryString, 'ref', urlInfo.ref); queryString = tryAppendQueryString(queryString, 'cur', cur); - queryString = tryAppendQueryString(queryString, 'dnt', dnt); bidRequests.push({ method: 'GET', diff --git a/modules/gnetBidAdapter.js b/modules/gnetBidAdapter.js index 1bcc774e351..da99983ea0c 100644 --- a/modules/gnetBidAdapter.js +++ b/modules/gnetBidAdapter.js @@ -2,7 +2,7 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { _each, isEmpty, parseSizesInput } from '../src/utils.js'; import { BANNER } from '../src/mediaTypes.js'; import { getStorageManager } from '../src/storageManager.js'; -import {ajax} from '../src/ajax.js'; +import { ajax } from '../src/ajax.js'; /** * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest @@ -13,7 +13,7 @@ import {ajax} from '../src/ajax.js'; const BIDDER_CODE = 'gnet'; const ENDPOINT = 'https://service.gnetrtb.com/api'; -const storage = getStorageManager({bidderCode: BIDDER_CODE}); +const storage = getStorageManager({ bidderCode: BIDDER_CODE }); export const spec = { code: BIDDER_CODE, diff --git a/modules/goldbachBidAdapter.js b/modules/goldbachBidAdapter.js index 3912df96615..84cca2ebbb3 100644 --- a/modules/goldbachBidAdapter.js +++ b/modules/goldbachBidAdapter.js @@ -184,7 +184,7 @@ const converter = ortbConverter({ /* Logging */ const sendLog = (data, percentage = 0.0001) => { if (Math.random() > percentage) return; - const encodedData = `data=${window.btoa(JSON.stringify({...data, source: 'goldbach_pbjs', projectedAmount: (1 / percentage)}))}`; + const encodedData = `data=${window.btoa(JSON.stringify({ ...data, source: 'goldbach_pbjs', projectedAmount: (1 / percentage) }))}`; ajax(URL_LOGGING, null, encodedData, { withCredentials: false, method: METHOD, @@ -214,7 +214,7 @@ export const spec = { }; }, interpretResponse: function (ortbResponse, request) { - const bids = converter.fromORTB({response: ortbResponse.body, request: request.data}).bids; + const bids = converter.fromORTB({ response: ortbResponse.body, request: request.data }).bids; return bids }, getUserSyncs: function(syncOptions, serverResponses, gdprConsent, uspConsent) { diff --git a/modules/gppControl_usnat.js b/modules/gppControl_usnat.js index b38fc1a9d29..4aec1c11d9a 100644 --- a/modules/gppControl_usnat.js +++ b/modules/gppControl_usnat.js @@ -1,5 +1,5 @@ -import {config} from '../src/config.js'; -import {setupRules} from '../libraries/mspa/activityControls.js'; +import { config } from '../src/config.js'; +import { setupRules } from '../libraries/mspa/activityControls.js'; let setupDone = false; diff --git a/modules/gppControl_usstates.ts b/modules/gppControl_usstates.ts index 826fd74369d..3297d0158b2 100644 --- a/modules/gppControl_usstates.ts +++ b/modules/gppControl_usstates.ts @@ -1,6 +1,6 @@ -import {config} from '../src/config.js'; -import {setupRules} from '../libraries/mspa/activityControls.js'; -import {deepSetValue, prefixLog} from '../src/utils.js'; +import { config } from '../src/config.js'; +import { setupRules } from '../libraries/mspa/activityControls.js'; +import { deepSetValue, prefixLog } from '../src/utils.js'; const FIELDS = { Version: 0, @@ -29,7 +29,7 @@ const FIELDS = { * List fields are also copied, but forced to the "correct" length (by truncating or padding with nulls); * additionally, elements within them can be moved around using the `move` argument. */ -export function normalizer({nullify = [], move = {}, fn}: { +export function normalizer({ nullify = [], move = {}, fn }: { /** * list of fields to force to null */ @@ -108,8 +108,8 @@ export const NORMALIZATIONS = { } } }), - 9: normalizer({fn: scalarMinorsAreChildren}), - 10: normalizer({fn: scalarMinorsAreChildren}), + 9: normalizer({ fn: scalarMinorsAreChildren }), + 10: normalizer({ fn: scalarMinorsAreChildren }), 11: normalizer({ move: { SensitiveDataProcessing: { @@ -146,7 +146,7 @@ export const DEFAULT_SID_MAPPING = { export const getSections = (() => { const allSIDs = Object.keys(DEFAULT_SID_MAPPING).map(Number); - return function ({sections = {}, sids = allSIDs} = {}) { + return function ({ sections = {}, sids = allSIDs } = {}) { return sids.map(sid => { const logger = prefixLog(`Cannot set up MSPA controls for SID ${sid}:`); const ov = sections[sid] || {}; diff --git a/modules/gptPreAuction.ts b/modules/gptPreAuction.ts index db2978e1302..5bfb9d243a0 100644 --- a/modules/gptPreAuction.ts +++ b/modules/gptPreAuction.ts @@ -13,9 +13,7 @@ import { pick, uniques } from '../src/utils.js'; -import type {SlotMatchingFn} from '../src/targeting.ts'; -import type {AdUnitCode} from '../src/types/common.d.ts'; -import type {AdUnit} from '../src/adUnits.ts'; +import type { AdUnit } from '../src/adUnits.ts'; const MODULE_NAME = 'GPT Pre-Auction'; export let _currentConfig: any = {}; @@ -66,8 +64,6 @@ export function getAuctionsIdsFromTargeting(targeting, am = auctionManager) { } export const appendGptSlots = adUnits => { - const { customGptSlotMatching } = _currentConfig; - if (!isGptPubadsDefined()) { return; } @@ -81,9 +77,7 @@ export const appendGptSlots = adUnits => { const adUnitPaths = {}; window.googletag.pubads().getSlots().forEach((slot: googletag.Slot) => { - const matchingAdUnitCode = Object.keys(adUnitMap).find(customGptSlotMatching - ? customGptSlotMatching(slot) - : isAdUnitCodeMatchingSlot(slot)); + const matchingAdUnitCode = Object.keys(adUnitMap).find(isAdUnitCodeMatchingSlot(slot)); if (matchingAdUnitCode) { const path = adUnitPaths[matchingAdUnitCode] = slot.getAdUnitPath(); @@ -164,7 +158,7 @@ const setPpsConfigFromTargetingSet = (next, targetingSet) => { // set gpt config const auctionsIds = getAuctionsIdsFromTargeting(targetingSet); const signals = getSignalsIntersection(getSignalsArrayByAuctionsIds(auctionsIds)); - window.googletag.setConfig && window.googletag.setConfig({pps: { taxonomies: signals }}); + window.googletag.setConfig && window.googletag.setConfig({ pps: { taxonomies: signals } }); next(targetingSet); }; @@ -174,16 +168,9 @@ type GPTPreAuctionConfig = { */ enabled?: boolean; /** - * If true, use default behavior for determining GPID and PbAdSlot. Defaults to false. + * If true, use default behavior for determining GPID. Defaults to false. */ useDefaultPreAuction?: boolean; - customGptSlotMatching?: SlotMatchingFn; - /** - * @param adUnitCode Ad unit code - * @param adServerAdSlot The value of that ad unit's `ortb2Imp.ext.data.adserver.adslot` - * @returns pbadslot for the ad unit - */ - customPbAdSlot?: (adUnitCode: AdUnitCode, adServerAdSlot: string) => string; /** * @param adUnit An ad unit object * @param adServerAdSlot The value of that ad unit's `ortb2Imp.ext.data.adserver.adslot` @@ -206,8 +193,6 @@ declare module '../src/config' { const handleSetGptConfig = moduleConfig => { _currentConfig = pick(moduleConfig, [ 'enabled', enabled => enabled !== false, - 'customGptSlotMatching', customGptSlotMatching => - typeof customGptSlotMatching === 'function' && customGptSlotMatching, 'customPreAuction', customPreAuction => typeof customPreAuction === 'function' && customPreAuction, 'useDefaultPreAuction', useDefaultPreAuction => useDefaultPreAuction ?? true, ]); @@ -221,8 +206,8 @@ const handleSetGptConfig = moduleConfig => { } else { logInfo(`${MODULE_NAME}: Turning off module`); _currentConfig = {}; - getHook('makeBidRequests').getHooks({hook: makeBidRequestsHook}).remove(); - getHook('targetingDone').getHooks({hook: setPpsConfigFromTargetingSet}).remove(); + getHook('makeBidRequests').getHooks({ hook: makeBidRequestsHook }).remove(); + getHook('targetingDone').getHooks({ hook: setPpsConfigFromTargetingSet }).remove(); hooksAdded = false; } }; diff --git a/modules/gravitoIdSystem.js b/modules/gravitoIdSystem.js index cc02c6a103e..8f2e7e4b4df 100644 --- a/modules/gravitoIdSystem.js +++ b/modules/gravitoIdSystem.js @@ -6,11 +6,11 @@ */ import { submodule } from '../src/hook.js'; -import {getStorageManager} from '../src/storageManager.js'; -import {MODULE_TYPE_UID} from '../src/activities/modules.js'; +import { getStorageManager } from '../src/storageManager.js'; +import { MODULE_TYPE_UID } from '../src/activities/modules.js'; const MODULE_NAME = 'gravitompId'; -export const storage = getStorageManager({moduleType: MODULE_TYPE_UID, moduleName: MODULE_NAME}); +export const storage = getStorageManager({ moduleType: MODULE_TYPE_UID, moduleName: MODULE_NAME }); export const cookieKey = 'gravitompId'; @@ -34,7 +34,7 @@ export const gravitoIdSystemSubmodule = { const result = { gravitompId: newId } - return {id: result}; + return { id: result }; }, /** @@ -49,7 +49,7 @@ export const gravitoIdSystemSubmodule = { if (value.gravitompId) { result = value.gravitompId } - return {gravitompId: result}; + return { gravitompId: result }; } return undefined; }, diff --git a/modules/greenbidsAnalyticsAdapter.js b/modules/greenbidsAnalyticsAdapter.js index 99ce89ee4d1..151b97553ed 100644 --- a/modules/greenbidsAnalyticsAdapter.js +++ b/modules/greenbidsAnalyticsAdapter.js @@ -1,8 +1,8 @@ -import {ajax} from '../src/ajax.js'; +import { ajax } from '../src/ajax.js'; import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import { EVENTS } from '../src/constants.js'; import adapterManager from '../src/adapterManager.js'; -import {deepClone, generateUUID, logError, logInfo, logWarn, getParameterByName} from '../src/utils.js'; +import { deepClone, generateUUID, logError, logInfo, logWarn, getParameterByName } from '../src/utils.js'; /** * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid @@ -52,7 +52,7 @@ export const isSampled = function(greenbidsId, samplingRate, exploratorySampling return isExtraSampled; } -export const greenbidsAnalyticsAdapter = Object.assign(adapter({ANALYTICS_SERVER, analyticsType}), { +export const greenbidsAnalyticsAdapter = Object.assign(adapter({ ANALYTICS_SERVER, analyticsType }), { cachedAuctions: {}, exploratorySamplingSplit: 0.9, @@ -171,7 +171,7 @@ export const greenbidsAnalyticsAdapter = Object.assign(adapter({ANALYTICS_SERVER } }, createBidMessage(auctionEndArgs) { - const {auctionId, timestamp, auctionEnd, adUnits, bidsReceived, noBids} = auctionEndArgs; + const { auctionId, timestamp, auctionEnd, adUnits, bidsReceived, noBids } = auctionEndArgs; const cachedAuction = this.getCachedAuction(auctionId); const message = this.createCommonMessage(auctionId); const timeoutBids = cachedAuction.timeoutBids || []; @@ -183,9 +183,9 @@ export const greenbidsAnalyticsAdapter = Object.assign(adapter({ANALYTICS_SERVER message.adUnits.push({ code: adUnitCode, mediaTypes: { - ...(adUnit.mediaTypes?.banner !== undefined) && {banner: adUnit.mediaTypes.banner}, - ...(adUnit.mediaTypes?.video !== undefined) && {video: adUnit.mediaTypes.video}, - ...(adUnit.mediaTypes?.native !== undefined) && {native: adUnit.mediaTypes.native} + ...(adUnit.mediaTypes?.banner !== undefined) && { banner: adUnit.mediaTypes.banner }, + ...(adUnit.mediaTypes?.video !== undefined) && { video: adUnit.mediaTypes.video }, + ...(adUnit.mediaTypes?.native !== undefined) && { native: adUnit.mediaTypes.native } }, ortb2Imp: adUnit.ortb2Imp || {}, bidders: [], @@ -243,7 +243,7 @@ export const greenbidsAnalyticsAdapter = Object.assign(adapter({ANALYTICS_SERVER cachedAuction.billingId = billableArgs.billingId || 'unknown_billing_id'; } }, - track({eventType, args}) { + track({ eventType, args }) { try { if (eventType === AUCTION_INIT) { this.handleAuctionInit(args); diff --git a/modules/gridBidAdapter.js b/modules/gridBidAdapter.js index e894154e6d1..31a592cf6b6 100644 --- a/modules/gridBidAdapter.js +++ b/modules/gridBidAdapter.js @@ -60,7 +60,7 @@ export const spec = { code: BIDDER_CODE, gvlid: GVLID, aliases: ['playwire', 'adlivetech', 'gridNM'], - supportedMediaTypes: [ BANNER, VIDEO ], + supportedMediaTypes: [BANNER, VIDEO], /** * Determines whether or not the given bid request is valid. * @@ -89,7 +89,7 @@ export const spec = { let userExt = null; let endpoint = null; let forceBidderName = false; - let {bidderRequestId, gdprConsent, uspConsent, timeout, refererInfo, gppConsent} = bidderRequest || {}; + let { bidderRequestId, gdprConsent, uspConsent, timeout, refererInfo, gppConsent } = bidderRequest || {}; const referer = refererInfo ? encodeURIComponent(refererInfo.page) : ''; const tmax = parseInt(timeout) || null; @@ -262,7 +262,7 @@ export const spec = { } if (gdprConsent && gdprConsent.consentString) { - userExt = {consent: gdprConsent.consentString}; + userExt = { consent: gdprConsent.consentString }; } const ortb2UserExtDevice = deepAccess(bidderRequest, 'ortb2.user.ext.device'); @@ -348,7 +348,7 @@ export const spec = { if (uspConsent) { if (!request.regs) { - request.regs = {ext: {}}; + request.regs = { ext: {} }; } if (!request.regs.ext) { request.regs.ext = {}; @@ -365,7 +365,7 @@ export const spec = { if (ortb2Regs?.ext?.dsa) { if (!request.regs) { - request.regs = {ext: {}}; + request.regs = { ext: {} }; } if (!request.regs.ext) { request.regs.ext = {}; @@ -383,7 +383,7 @@ export const spec = { } const genre = deepAccess(site, 'content.genre'); if (genre && typeof genre === 'string') { - request.site.content = {...request.site.content, genre}; + request.site.content = { ...request.site.content, genre }; } const data = deepAccess(site, 'content.data'); if (data && data.length) { @@ -392,7 +392,7 @@ export const spec = { } const id = deepAccess(site, 'content.id'); if (id) { - request.site.content = {...request.site.content, id}; + request.site.content = { ...request.site.content, id }; } } }); @@ -483,7 +483,7 @@ export const spec = { }, onDataDeletionRequest: function(data) { - spec.ajaxCall(USP_DELETE_DATA_HANDLER, null, null, {method: 'GET'}); + spec.ajaxCall(USP_DELETE_DATA_HANDLER, null, null, { method: 'GET' }); } }; @@ -501,7 +501,7 @@ function _getFloor (mediaTypes, bid) { const floorInfo = bid.getFloor({ currency: 'USD', mediaType: curMediaType, - size: bid.sizes.map(([w, h]) => ({w, h})) + size: bid.sizes.map(([w, h]) => ({ w, h })) }); if (isPlainObject(floorInfo) && diff --git a/modules/growadsBidAdapter.js b/modules/growadsBidAdapter.js index 4b5b97f965a..42f02625db8 100644 --- a/modules/growadsBidAdapter.js +++ b/modules/growadsBidAdapter.js @@ -1,8 +1,8 @@ 'use strict'; -import {deepAccess, _each, triggerPixel, getBidIdParameter} from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER, NATIVE} from '../src/mediaTypes.js'; +import { deepAccess, _each, triggerPixel, getBidIdParameter } from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, NATIVE } from '../src/mediaTypes.js'; import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; const BIDDER_CODE = 'growads'; diff --git a/modules/growthCodeAnalyticsAdapter.js b/modules/growthCodeAnalyticsAdapter.js index 5c936767cdf..e49c23bbf27 100644 --- a/modules/growthCodeAnalyticsAdapter.js +++ b/modules/growthCodeAnalyticsAdapter.js @@ -6,16 +6,16 @@ import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; import * as utils from '../src/utils.js'; import { EVENTS } from '../src/constants.js'; -import {getStorageManager} from '../src/storageManager.js'; -import {getRefererInfo} from '../src/refererDetection.js'; -import {logError, logInfo} from '../src/utils.js'; -import {MODULE_TYPE_ANALYTICS} from '../src/activities/modules.js'; +import { getStorageManager } from '../src/storageManager.js'; +import { getRefererInfo } from '../src/refererDetection.js'; +import { logError, logInfo } from '../src/utils.js'; +import { MODULE_TYPE_ANALYTICS } from '../src/activities/modules.js'; const MODULE_NAME = 'growthCodeAnalytics'; const DEFAULT_PID = 'INVALID_PID' const ENDPOINT_URL = 'https://analytics.gcprivacy.com/v3/pb/analytics' -export const storage = getStorageManager({moduleType: MODULE_TYPE_ANALYTICS, moduleName: MODULE_NAME}); +export const storage = getStorageManager({ moduleType: MODULE_TYPE_ANALYTICS, moduleName: MODULE_NAME }); const sessionId = utils.generateUUID(); @@ -29,8 +29,8 @@ let startAuction = 0; let bidRequestTimeout = 0; const analyticsType = 'endpoint'; -const growthCodeAnalyticsAdapter = Object.assign(adapter({url: url, analyticsType}), { - track({eventType, args}) { +const growthCodeAnalyticsAdapter = Object.assign(adapter({ url: url, analyticsType }), { + track({ eventType, args }) { const eventData = args ? utils.deepClone(args) : {}; let data = {}; if (!trackEvents.includes(eventType)) return; @@ -93,11 +93,6 @@ const growthCodeAnalyticsAdapter = Object.assign(adapter({url: url, analyticsTyp break; } - case EVENTS.ADD_AD_UNITS: { - data = eventData; - break; - } - case EVENTS.NO_BID: { data = eventData break; @@ -159,7 +154,7 @@ function logToServer() { error: error => { logInfo(MODULE_NAME + ' Problem Send Data to Server: ' + error) } - }, JSON.stringify(data), {method: 'POST', withCredentials: true}) + }, JSON.stringify(data), { method: 'POST', withCredentials: true }) eventQueue = [ ]; diff --git a/modules/growthCodeIdSystem.js b/modules/growthCodeIdSystem.js index 2da339e1b4a..4c6d48f4932 100644 --- a/modules/growthCodeIdSystem.js +++ b/modules/growthCodeIdSystem.js @@ -6,8 +6,8 @@ */ import { submodule } from '../src/hook.js' -import {getStorageManager} from '../src/storageManager.js'; -import {MODULE_TYPE_UID} from '../src/activities/modules.js'; +import { getStorageManager } from '../src/storageManager.js'; +import { MODULE_TYPE_UID } from '../src/activities/modules.js'; /** * @typedef {import('../modules/userId/index.js').Submodule} Submodule @@ -67,7 +67,7 @@ export const growthCodeIdSubmodule = { ids = ids.concat(data) } - return {id: ids} + return { id: ids } }, }; diff --git a/modules/growthCodeRtdProvider.js b/modules/growthCodeRtdProvider.js index 807b17f351d..3e5a5b9119e 100644 --- a/modules/growthCodeRtdProvider.js +++ b/modules/growthCodeRtdProvider.js @@ -9,7 +9,7 @@ import { } from '../src/utils.js'; import * as ajax from '../src/ajax.js'; import { MODULE_TYPE_RTD } from '../src/activities/modules.js'; -import {tryAppendQueryString} from '../libraries/urlUtils/urlUtils.js'; +import { tryAppendQueryString } from '../libraries/urlUtils/urlUtils.js'; const MODULE_NAME = 'growthCodeRtd'; const LOG_PREFIX = 'GrowthCodeRtd: '; @@ -99,7 +99,7 @@ function callServer(configParams, items, expiresAt, userConsent) { error: error => { logError(LOG_PREFIX + 'ID fetch encountered an error', error); } - }, undefined, {method: 'GET', withCredentials: true}) + }, undefined, { method: 'GET', withCredentials: true }) } return true; diff --git a/modules/gumgumBidAdapter.js b/modules/gumgumBidAdapter.js index 3f7339a4f08..6aca8a36728 100644 --- a/modules/gumgumBidAdapter.js +++ b/modules/gumgumBidAdapter.js @@ -1,12 +1,13 @@ -import {BANNER, VIDEO} from '../src/mediaTypes.js'; -import {_each, deepAccess, getWinDimensions, logError, logWarn, parseSizesInput} from '../src/utils.js'; -import {getDevicePixelRatio} from '../libraries/devicePixelRatio/devicePixelRatio.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; +import { _each, deepAccess, getWinDimensions, logError, logWarn, parseSizesInput } from '../src/utils.js'; +import { getDevicePixelRatio } from '../libraries/devicePixelRatio/devicePixelRatio.js'; -import {config} from '../src/config.js'; -import {getStorageManager} from '../src/storageManager.js'; +import { config } from '../src/config.js'; +import { getStorageManager } from '../src/storageManager.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; import { getConnectionInfo } from '../libraries/connectionInfo/connectionUtils.js'; +import { getDNT } from '../libraries/dnt/index.js'; /** * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest @@ -18,7 +19,7 @@ import { getConnectionInfo } from '../libraries/connectionInfo/connectionUtils.j */ const BIDDER_CODE = 'gumgum'; -const storage = getStorageManager({bidderCode: BIDDER_CODE}); +const storage = getStorageManager({ bidderCode: BIDDER_CODE }); const ALIAS_BIDDER_CODE = ['gg']; const BID_ENDPOINT = `https://g2.gumgum.com/hbid/imp`; const JCSI = { t: 0, rq: 8, pbv: '$prebid.version$' } @@ -280,7 +281,7 @@ function _getDeviceData(ortb2Data) { ipv6: _device.ipv6, ua: _device.ua, sua: _device.sua ? JSON.stringify(_device.sua) : undefined, - dnt: _device.dnt, + dnt: getDNT() ? 1 : 0, os: _device.os, osv: _device.osv, dt: _device.devicetype, @@ -289,6 +290,8 @@ function _getDeviceData(ortb2Data) { model: _device.model, ppi: _device.ppi, pxratio: _device.pxratio, + lmt: _device.lmt, + ifa: _device.lmt !== 1 ? _device.ifa : undefined, foddid: _device?.ext?.fiftyonedegrees_deviceId, }; @@ -376,7 +379,6 @@ function buildRequests(validBidRequests, bidderRequest) { const { currency, floor } = _getFloor(mediaTypes, params.bidfloor, bidRequest); const eids = getEids(userId); const gpid = deepAccess(ortb2Imp, 'ext.gpid'); - const paapiEligible = deepAccess(ortb2Imp, 'ext.ae') === 1 let sizes = [1, 1]; let data = {}; data.displaymanager = 'Prebid.js - gumgum'; @@ -465,9 +467,6 @@ function buildRequests(validBidRequests, bidderRequest) { } else { // legacy params data = { ...data, ...handleLegacyParams(params, sizes) }; } - if (paapiEligible) { - data.ae = paapiEligible - } if (gdprConsent) { data.gdprApplies = gdprConsent.gdprApplies ? 1 : 0; } @@ -651,7 +650,7 @@ function interpretResponse(serverResponse, bidRequest) { // added logic for in-slot multi-szie } else if ((product === 2 && sizes.includes('1x1')) || product === 3) { const requestSizesThatMatchResponse = (bidRequest.sizes && bidRequest.sizes.reduce((result, current) => { - const [ width, height ] = current; + const [width, height] = current; if (responseWidth === width && responseHeight === height) result.push(current.join('x')); return result }, [])) || []; diff --git a/modules/h12mediaBidAdapter.js b/modules/h12mediaBidAdapter.js index 963ae660e57..90b284a485c 100644 --- a/modules/h12mediaBidAdapter.js +++ b/modules/h12mediaBidAdapter.js @@ -2,6 +2,7 @@ import { inIframe, logError, logMessage, deepAccess, getWinDimensions } from '.. import { registerBidder } from '../src/adapters/bidderFactory.js'; import { getBoundingClientRect } from '../libraries/boundingClientRect/boundingClientRect.js'; import { getViewportSize } from '../libraries/viewport/viewport.js'; +import { getAdUnitElement } from '../src/utils/adUnits.js'; const BIDDER_CODE = 'h12media'; const DEFAULT_URL = 'https://bidder.h12-media.com/prebid/'; const DEFAULT_CURRENCY = 'USD'; @@ -30,7 +31,7 @@ export const spec = { pubsubid = ''; } const pubcontainerid = bidderParams.pubcontainerid; - const adUnitElement = document.getElementById(pubcontainerid || bidRequest.adUnitCode); + const adUnitElement = pubcontainerid ? document.getElementById(pubcontainerid) : getAdUnitElement(bidRequest); const ishidden = !isVisible(adUnitElement); const framePos = getFramePos(); const coords = isiframe ? { @@ -66,7 +67,7 @@ export const spec = { return { method: 'POST', url: requestUrl, - options: {withCredentials: true}, + options: { withCredentials: true }, data: { gdpr: !!deepAccess(bidderRequest, 'gdprConsent.gdprApplies', false), gdpr_cs: deepAccess(bidderRequest, 'gdprConsent.consentString', ''), @@ -219,7 +220,7 @@ function getClientDimensions() { function getDocumentDimensions() { try { - const {document: {documentElement, body}} = getWinDimensions(); + const { document: { documentElement, body } } = getWinDimensions(); const width = body.clientWidth; const height = Math.max(body.scrollHeight, body.offsetHeight, documentElement.clientHeight, documentElement.scrollHeight, documentElement.offsetHeight); return [width, height]; diff --git a/modules/hadronAnalyticsAdapter.js b/modules/hadronAnalyticsAdapter.js index c01e33bc6a2..dae64cd0e5b 100644 --- a/modules/hadronAnalyticsAdapter.js +++ b/modules/hadronAnalyticsAdapter.js @@ -3,9 +3,9 @@ import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; import * as utils from '../src/utils.js'; import { EVENTS } from '../src/constants.js'; -import {getStorageManager} from '../src/storageManager.js'; -import {getRefererInfo} from '../src/refererDetection.js'; -import {MODULE_TYPE_ANALYTICS} from '../src/activities/modules.js'; +import { getStorageManager } from '../src/storageManager.js'; +import { getRefererInfo } from '../src/refererDetection.js'; +import { MODULE_TYPE_ANALYTICS } from '../src/activities/modules.js'; import { getViewportSize } from '../libraries/viewport/viewport.js'; /** @@ -18,7 +18,7 @@ const DEFAULT_PARTNER_ID = 0; const AU_GVLID = 561; const MODULE_CODE = 'hadronAnalytics'; -export const storage = getStorageManager({moduleType: MODULE_TYPE_ANALYTICS, moduleName: MODULE_CODE}); +export const storage = getStorageManager({ moduleType: MODULE_TYPE_ANALYTICS, moduleName: MODULE_CODE }); var viewId = utils.generateUUID(); @@ -47,8 +47,8 @@ var startAuction = 0; var bidRequestTimeout = 0; const analyticsType = 'endpoint'; -const hadronAnalyticsAdapter = Object.assign(adapter({url: HADRON_ANALYTICS_URL, analyticsType}), { - track({eventType, args}) { +const hadronAnalyticsAdapter = Object.assign(adapter({ url: HADRON_ANALYTICS_URL, analyticsType }), { + track({ eventType, args }) { args = args ? utils.deepClone(args) : {}; var data = {}; if (!eventsToTrack.includes(eventType)) return; @@ -111,11 +111,6 @@ const hadronAnalyticsAdapter = Object.assign(adapter({url: HADRON_ANALYTICS_URL, break; } - case EVENTS.ADD_AD_UNITS: { - data = args; - break; - } - case EVENTS.AD_RENDER_FAILED: { data = args; break; diff --git a/modules/hadronIdSystem.js b/modules/hadronIdSystem.js index ccd63bc0184..a87b75b6838 100644 --- a/modules/hadronIdSystem.js +++ b/modules/hadronIdSystem.js @@ -5,12 +5,12 @@ * @requires module:modules/userId */ -import {ajax} from '../src/ajax.js'; -import {getStorageManager} from '../src/storageManager.js'; -import {submodule} from '../src/hook.js'; -import {isFn, isStr, isPlainObject, logError, logInfo} from '../src/utils.js'; +import { ajax } from '../src/ajax.js'; +import { getStorageManager } from '../src/storageManager.js'; +import { submodule } from '../src/hook.js'; +import { isFn, isStr, isPlainObject, logError, logInfo } from '../src/utils.js'; import { config } from '../src/config.js'; -import {MODULE_TYPE_UID} from '../src/activities/modules.js'; +import { MODULE_TYPE_UID } from '../src/activities/modules.js'; import { gdprDataHandler, uspDataHandler, gppDataHandler } from '../src/adapterManager.js'; /** @@ -25,7 +25,7 @@ export const LS_TAM_KEY = 'auHadronId'; const AU_GVLID = 561; const DEFAULT_HADRON_URL_ENDPOINT = 'https://id.hadron.ad.gt/api/v1/pbhid'; -export const storage = getStorageManager({moduleType: MODULE_TYPE_UID, moduleName: MODULE_NAME}); +export const storage = getStorageManager({ moduleType: MODULE_TYPE_UID, moduleName: MODULE_NAME }); /** * Param or default. @@ -89,7 +89,7 @@ export const hadronIdSubmodule = { if (isStr(hadronId) && hadronId.length > 0) { logInfo(LOG_PREFIX, `${LS_TAM_KEY} found in localStorage = ${hadronId}`) // return {callback: function(cb) { cb(hadronId) }}; - return {id: hadronId} + return { id: hadronId } } const partnerId = config.params.partnerId | 0; const resp = function (callback) { @@ -144,9 +144,9 @@ export const hadronIdSubmodule = { logInfo(LOG_PREFIX, `${MODULE_NAME} not found, calling home (${url})`); - ajax(url, callbacks, undefined, {method: 'GET'}); + ajax(url, callbacks, undefined, { method: 'GET' }); }; - return {callback: resp}; + return { callback: resp }; }, eids: { 'hadronId': { diff --git a/modules/hadronRtdProvider.js b/modules/hadronRtdProvider.js index 0ff11de1a3e..216ef1336f6 100644 --- a/modules/hadronRtdProvider.js +++ b/modules/hadronRtdProvider.js @@ -5,13 +5,13 @@ * @module modules/hadronRtdProvider * @requires module:modules/realTimeData */ -import {config} from '../src/config.js'; -import {getGlobal} from '../src/prebidGlobal.js'; -import {getStorageManager} from '../src/storageManager.js'; -import {submodule} from '../src/hook.js'; -import {isFn, isStr, isArray, deepEqual, isPlainObject, logInfo} from '../src/utils.js'; -import {loadExternalScript} from '../src/adloader.js'; -import {MODULE_TYPE_RTD} from '../src/activities/modules.js'; +import { config } from '../src/config.js'; +import { getGlobal } from '../src/prebidGlobal.js'; +import { getStorageManager } from '../src/storageManager.js'; +import { submodule } from '../src/hook.js'; +import { isFn, isStr, isArray, deepEqual, isPlainObject, logInfo } from '../src/utils.js'; +import { loadExternalScript } from '../src/adloader.js'; +import { MODULE_TYPE_RTD } from '../src/activities/modules.js'; /** * @typedef {import('../modules/rtdModule/index.js').RtdSubmodule} RtdSubmodule @@ -24,7 +24,7 @@ const AU_GVLID = 561; const HADRON_JS_URL = 'https://cdn.hadronid.net/hadron.js'; const LS_TAM_KEY = 'auHadronId'; const RTD_LOCAL_NAME = 'auHadronRtd'; -export const storage = getStorageManager({moduleType: MODULE_TYPE_RTD, moduleName: SUBMODULE_NAME}); +export const storage = getStorageManager({ moduleType: MODULE_TYPE_RTD, moduleName: SUBMODULE_NAME }); /** * @param {string} url @@ -47,11 +47,11 @@ function mergeDeep(target, ...sources) { if (isPlainObject(target) && isPlainObject(source)) { for (const key in source) { if (isPlainObject(source[key])) { - if (!target[key]) Object.assign(target, {[key]: {}}); + if (!target[key]) Object.assign(target, { [key]: {} }); mergeDeep(target[key], source[key]); } else if (isArray(source[key])) { if (!target[key]) { - Object.assign(target, {[key]: source[key]}); + Object.assign(target, { [key]: source[key] }); } else if (isArray(target[key])) { source[key].forEach(obj => { let e = 1; @@ -67,7 +67,7 @@ function mergeDeep(target, ...sources) { }); } } else { - Object.assign(target, {[key]: source[key]}); + Object.assign(target, { [key]: source[key] }); } } } diff --git a/modules/harionBidAdapter.js b/modules/harionBidAdapter.js new file mode 100644 index 00000000000..40b1b8282b3 --- /dev/null +++ b/modules/harionBidAdapter.js @@ -0,0 +1,25 @@ +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; +import { isBidRequestValid, buildRequests, interpretResponse } from '../libraries/teqblazeUtils/bidderUtils.js'; + +const BIDDER_CODE = 'harion'; +const GVLID = 1406; +const AD_URL = 'https://east-api.harion-ma.com/pbjs'; + +export const spec = { + code: BIDDER_CODE, + gvlid: GVLID, + supportedMediaTypes: [BANNER, VIDEO, NATIVE], + + isBidRequestValid: isBidRequestValid(), + buildRequests: buildRequests(AD_URL), + interpretResponse: (serverResponse) => { + if (!serverResponse || !Array.isArray(serverResponse.body)) { + return []; + } + + return interpretResponse(serverResponse); + } +}; + +registerBidder(spec); diff --git a/modules/harionBidAdapter.md b/modules/harionBidAdapter.md new file mode 100644 index 00000000000..a2ec196eeab --- /dev/null +++ b/modules/harionBidAdapter.md @@ -0,0 +1,79 @@ +# Overview + +``` +Module Name: Harion Bidder Adapter +Module Type: Harion Bidder Adapter +Maintainer: adtech@markappmedia.site +``` + +# Description + +Connects to Harion exchange for bids. +Harion bid adapter supports Banner, Video (instream and outstream) and Native. + +# Test Parameters +``` + var adUnits = [ + // Will return static test banner + { + code: 'adunit1', + mediaTypes: { + banner: { + sizes: [ [300, 250], [320, 50] ], + } + }, + bids: [ + { + bidder: 'harion', + params: { + placementId: 'testBanner', + } + } + ] + }, + { + code: 'addunit2', + mediaTypes: { + video: { + playerSize: [ [640, 480] ], + context: 'instream', + minduration: 5, + maxduration: 60, + } + }, + bids: [ + { + bidder: 'harion', + params: { + placementId: 'testVideo', + } + } + ] + }, + { + code: 'addunit3', + mediaTypes: { + native: { + title: { + required: true + }, + body: { + required: true + }, + icon: { + required: true, + size: [64, 64] + } + } + }, + bids: [ + { + bidder: 'harion', + params: { + placementId: 'testNative', + } + } + ] + } + ]; +``` diff --git a/modules/holidBidAdapter.js b/modules/holidBidAdapter.js index 06e0e7a149e..88f39a4fe79 100644 --- a/modules/holidBidAdapter.js +++ b/modules/holidBidAdapter.js @@ -11,12 +11,91 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; const BIDDER_CODE = 'holid'; const GVLID = 1177; + const ENDPOINT = 'https://helloworld.holid.io/openrtb2/auction'; const COOKIE_SYNC_ENDPOINT = 'https://null.holid.io/sync.html'; + const TIME_TO_LIVE = 300; -const TMAX = 500; + +// Keep win URLs in-memory (per page-load) const wurlMap = {}; +/** + * Resolve tmax for the outgoing ORTB request. + * Goal: respect publisher's Prebid timeout (bidderRequest.timeout) and allow an optional per-bid override, + * without hard-forcing an arbitrary 500ms. + * + * Rules: + * - If bid.params.tmax is a positive number, use it, but never exceed bidderRequest.timeout when available. + * - Else if bidderRequest.timeout is a positive number, use it. + * - Else omit tmax entirely (PBS will apply its own default / config). + */ +function resolveTmax(bid, bidderRequest) { + const auctionTimeout = Number(bidderRequest?.timeout); + const paramTmax = Number(bid?.params?.tmax); + + const hasAuctionTimeout = Number.isFinite(auctionTimeout) && auctionTimeout > 0; + const hasParamTmax = Number.isFinite(paramTmax) && paramTmax > 0; + + if (hasParamTmax && hasAuctionTimeout) { + return Math.min(paramTmax, auctionTimeout); + } + if (hasParamTmax) { + return paramTmax; + } + if (hasAuctionTimeout) { + return auctionTimeout; + } + return undefined; +} + +/** + * Merge stored request ID into request.ext.prebid.storedrequest.id (without clobbering other ext fields). + * Keeps behavior consistent with the existing adapter expectation of bid.params.adUnitID. + */ +function mergeStoredRequest(ortbRequest, bid) { + const storedId = getBidIdParameter('adUnitID', bid.params); + if (storedId) { + deepSetValue(ortbRequest, 'ext.prebid.storedrequest.id', storedId); + } +} + +/** + * Merge schain into request.source.ext.schain (without overwriting request.source / request.ext). + */ +function mergeSchain(ortbRequest, bid) { + const schain = deepAccess(bid, 'ortb2.source.ext.schain'); + if (schain) { + deepSetValue(ortbRequest, 'source.ext.schain', schain); + } +} + +/** + * Build a sync URL for our sync endpoint. + */ +function buildSyncUrl({ bidders, gdprConsent, uspConsent, type }) { + const queryParams = []; + + queryParams.push('bidders=' + bidders); + + if (gdprConsent) { + queryParams.push('gdpr=' + (gdprConsent.gdprApplies ? 1 : 0)); + queryParams.push( + 'gdpr_consent=' + encodeURIComponent(gdprConsent.consentString || '') + ); + } else { + queryParams.push('gdpr=0'); + } + + if (typeof uspConsent !== 'undefined') { + queryParams.push('us_privacy=' + encodeURIComponent(uspConsent)); + } + + queryParams.push('type=' + encodeURIComponent(type)); + + return COOKIE_SYNC_ENDPOINT + '?' + queryParams.join('&'); +} + export const spec = { code: BIDDER_CODE, gvlid: GVLID, @@ -30,19 +109,23 @@ export const spec = { // Build request payload including GDPR, GPP, and US Privacy data if available buildRequests: function (validBidRequests, bidderRequest) { return validBidRequests.map((bid) => { + // Start from ortb2 (publisher modules may have populated site/user/device/ext/etc) const requestData = { ...bid.ortb2, - source: { - ext: { - schain: bid?.ortb2?.source?.ext?.schain - } - }, id: bidderRequest.bidderRequestId, imp: [getImp(bid)], - tmax: TMAX, - ...buildStoredRequest(bid), }; + // Merge (don’t overwrite) schain + storedrequest + mergeSchain(requestData, bid); + mergeStoredRequest(requestData, bid); + + // Resolve and set tmax (don’t hard-force) + const tmax = resolveTmax(bid, bidderRequest); + if (tmax) { + requestData.tmax = tmax; + } + // GDPR if (bidderRequest && bidderRequest.gdprConsent) { deepSetValue( @@ -67,7 +150,11 @@ export const spec = { // US Privacy if (bidderRequest && bidderRequest.usPrivacy) { - deepSetValue(requestData, 'regs.ext.us_privacy', bidderRequest.usPrivacy); + deepSetValue( + requestData, + 'regs.ext.us_privacy', + bidderRequest.usPrivacy + ); } // User IDs @@ -87,6 +174,7 @@ export const spec = { // Interpret response: group bids by unique impid and select the highest CPM bid per imp interpretResponse: function (serverResponse, bidRequest) { const bidResponsesMap = {}; // Maps impid -> highest bid object + if (!serverResponse.body || !serverResponse.body.seatbid) { return []; } @@ -98,20 +186,30 @@ export const spec = { // --- MINIMAL CHANGE START --- // Build meta object and propagate advertiser domains for hb_adomain const meta = deepAccess(bid, 'ext.prebid.meta', {}) || {}; + // Read ORTB adomain; normalize to array of clean strings let advertiserDomains = deepAccess(bid, 'adomain', []); advertiserDomains = Array.isArray(advertiserDomains) ? advertiserDomains .filter(Boolean) - .map(d => String(d).toLowerCase().replace(/^https?:\/\//, '').replace(/^www\./, '').trim()) + .map((d) => + String(d) + .toLowerCase() + .replace(/^https?:\/\//, '') + .replace(/^www\./, '') + .trim() + ) : []; + if (advertiserDomains.length > 0) { meta.advertiserDomains = advertiserDomains; // <-- Prebid uses this to set hb_adomain } + const networkId = deepAccess(bid, 'ext.prebid.meta.networkId'); if (networkId) { meta.networkId = networkId; } + // Keep writing back for completeness (preserves existing behavior) deepSetValue(bid, 'ext.prebid.meta', meta); // --- MINIMAL CHANGE END --- @@ -157,39 +255,41 @@ export const spec = { }, ]; - if (!serverResponse || (Array.isArray(serverResponse) && serverResponse.length === 0)) { + if ( + !serverResponse || + (Array.isArray(serverResponse) && serverResponse.length === 0) + ) { return syncs; } - const responses = Array.isArray(serverResponse) - ? serverResponse - : [serverResponse]; + const responses = Array.isArray(serverResponse) ? serverResponse : [serverResponse]; const bidders = getBidders(responses); + // Prefer iframe when allowed if (optionsType.iframeEnabled && bidders) { - const queryParams = []; - queryParams.push('bidders=' + bidders); - - if (gdprConsent) { - queryParams.push('gdpr=' + (gdprConsent.gdprApplies ? 1 : 0)); - queryParams.push( - 'gdpr_consent=' + - encodeURIComponent(gdprConsent.consentString || '') - ); - } else { - queryParams.push('gdpr=0'); - } - - if (typeof uspConsent !== 'undefined') { - queryParams.push('us_privacy=' + encodeURIComponent(uspConsent)); - } - - queryParams.push('type=iframe'); - const strQueryParams = '?' + queryParams.join('&'); - syncs.push({ type: 'iframe', - url: COOKIE_SYNC_ENDPOINT + strQueryParams, + url: buildSyncUrl({ + bidders, + gdprConsent, + uspConsent, + type: 'iframe', + }), + }); + return syncs; + } + + // Fallback: if iframe is disabled but pixels are enabled, attempt a pixel-based sync call + // (Your sync endpoint must support this mode for it to be effective.) + if (optionsType.pixelEnabled && bidders) { + syncs.push({ + type: 'image', + url: buildSyncUrl({ + bidders, + gdprConsent, + uspConsent, + type: 'image', + }), }); } @@ -211,8 +311,8 @@ export const spec = { function getImp(bid) { const imp = buildStoredRequest(bid); imp.id = bid.bidId; // Ensure imp.id is unique to match the bid response correctly - const sizes = - bid.sizes && !Array.isArray(bid.sizes[0]) ? [bid.sizes] : bid.sizes; + + const sizes = bid.sizes && !Array.isArray(bid.sizes[0]) ? [bid.sizes] : bid.sizes; if (deepAccess(bid, 'mediaTypes.banner')) { imp.banner = { @@ -244,13 +344,26 @@ function buildStoredRequest(bid) { } // Helper: Extract unique bidders from responses for user syncs +// Primary source: ext.responsetimemillis (PBS), fallback: seatbid[].seat function getBidders(responses) { - const bidders = responses - .map((res) => Object.keys(res.body.ext?.responsetimemillis || {})) - .flat(); + const bidderSet = new Set(); + + responses.forEach((res) => { + const rtm = deepAccess(res, 'body.ext.responsetimemillis'); + if (rtm && typeof rtm === 'object') { + Object.keys(rtm).forEach((k) => bidderSet.add(k)); + } + + const seatbid = deepAccess(res, 'body.seatbid', []); + if (Array.isArray(seatbid)) { + seatbid.forEach((sb) => { + if (sb && sb.seat) bidderSet.add(sb.seat); + }); + } + }); - if (bidders.length) { - return encodeURIComponent(JSON.stringify([...new Set(bidders)])); + if (bidderSet.size) { + return encodeURIComponent(JSON.stringify([...bidderSet])); } } diff --git a/modules/humansecurityRtdProvider.js b/modules/humansecurityRtdProvider.js deleted file mode 100644 index aeb872beb8d..00000000000 --- a/modules/humansecurityRtdProvider.js +++ /dev/null @@ -1,180 +0,0 @@ -/** - * This module adds humansecurity provider to the real time data module - * - * The {@link module:modules/realTimeData} module is required - * The module will inject the HUMAN Security script into the context where Prebid.js is initialized, enriching bid requests with specific data to provide advanced protection against ad fraud and spoofing. - * @module modules/humansecurityRtdProvider - * @requires module:modules/realTimeData - */ - -import { submodule } from '../src/hook.js'; -import { - prefixLog, - mergeDeep, - generateUUID, - getWindowSelf, -} from '../src/utils.js'; -import { getRefererInfo } from '../src/refererDetection.js'; -import { getGlobal } from '../src/prebidGlobal.js'; -import { loadExternalScript } from '../src/adloader.js'; -import { MODULE_TYPE_RTD } from '../src/activities/modules.js'; - -/** - * @typedef {import('../modules/rtdModule/index.js').RtdSubmodule} RtdSubmodule - * @typedef {import('../modules/rtdModule/index.js').SubmoduleConfig} SubmoduleConfig - * @typedef {import('../modules/rtdModule/index.js').UserConsentData} UserConsentData - */ - -const SUBMODULE_NAME = 'humansecurity'; -const SCRIPT_URL = 'https://sonar.script.ac/prebid/rtd.js'; - -const { logInfo, logWarn, logError } = prefixLog(`[${SUBMODULE_NAME}]:`); - -/** @type {string} */ -let clientId = ''; - -/** @type {boolean} */ -let verbose = false; - -/** @type {string} */ -let sessionId = ''; - -/** @type {Object} */ -let hmnsData = { }; - -/** - * Submodule registration - */ -function main() { - submodule('realTimeData', /** @type {RtdSubmodule} */ ({ - name: SUBMODULE_NAME, - - // - init: (config, userConsent) => { - try { - load(config); - return true; - } catch (err) { - logError('init', err.message); - return false; - } - }, - - getBidRequestData: onGetBidRequestData - })); -} - -/** - * Injects HUMAN Security script on the page to facilitate pre-bid signal collection. - * @param {SubmoduleConfig} config - */ -function load(config) { - // By default, this submodule loads the generic implementation script - // only identified by the referrer information. In the future, if publishers - // want to have analytics where their websites are grouped, they can request - // Client ID from HUMAN, pass it here, and it will enable advanced reporting - clientId = config?.params?.clientId || ''; - if (clientId && (typeof clientId !== 'string' || !/^\w{3,16}$/.test(clientId))) { - throw new Error(`The 'clientId' parameter must be a short alphanumeric string`); - } - - // Load/reset the state - verbose = !!config?.params?.verbose; - sessionId = generateUUID(); - hmnsData = {}; - - // We rely on prebid implementation to get the best domain possible here - // In some cases, it still might be null, though - const refDomain = getRefererInfo().domain || ''; - - // Once loaded, the implementation script will publish an API using - // the session ID value it was given in data attributes - const scriptAttrs = { 'data-sid': sessionId }; - const scriptUrl = `${SCRIPT_URL}?r=${refDomain}${clientId ? `&c=${clientId}` : ''}`; - - loadExternalScript(scriptUrl, MODULE_TYPE_RTD, SUBMODULE_NAME, onImplLoaded, null, scriptAttrs); -} - -/** - * The callback to loadExternalScript - * Establishes the bridge between this RTD submodule and the loaded implementation - */ -function onImplLoaded() { - // We then get a hold on this script using the knowledge of this session ID - const wnd = getWindowSelf(); - const impl = wnd[`sonar_${sessionId}`]; - if (typeof impl !== 'object' || typeof impl.connect !== 'function') { - verbose && logWarn('onload', 'Unable to access the implementation script'); - return; - } - - // And set up a bridge between the RTD submodule and the implementation. - // The first argument is used to identify the caller. - // The callback might be called multiple times to update the signals - // once more precise information is available. - impl.connect(getGlobal(), onImplMessage); -} - -/** - * The bridge function will be called by the implementation script - * to update the token information or report errors - * @param {Object} msg - */ -function onImplMessage(msg) { - if (typeof msg !== 'object') { - return; - } - - switch (msg.type) { - case 'hmns': { - hmnsData = mergeDeep({}, msg.data || {}); - break; - } - case 'error': { - logError('impl', msg.data || ''); - break; - } - case 'warn': { - verbose && logWarn('impl', msg.data || ''); - break; - } - case 'info': { - verbose && logInfo('impl', msg.data || ''); - break; - } - } -} - -/** - * onGetBidRequestData is called once per auction. - * Update the `ortb2Fragments` object with the data from the injected script. - * - * @param {Object} reqBidsConfigObj - * @param {function} callback - * @param {SubmoduleConfig} config - * @param {UserConsentData} userConsent - */ -function onGetBidRequestData(reqBidsConfigObj, callback, config, userConsent) { - // Add device.ext.hmns to the global ORTB data for all vendors to use - // At the time of writing this submodule, "hmns" is an object defined - // internally by humansecurity, and it currently contains "v1" field - // with a token that contains collected signals about this session. - mergeDeep(reqBidsConfigObj.ortb2Fragments.global, { device: { ext: { hmns: hmnsData } } }); - callback(); -} - -/** - * Exporting local (and otherwise encapsulated to this module) functions - * for testing purposes - */ -export const __TEST__ = { - SUBMODULE_NAME, - SCRIPT_URL, - main, - load, - onImplLoaded, - onImplMessage, - onGetBidRequestData -}; - -main(); diff --git a/modules/humansecurityRtdProvider.md b/modules/humansecurityRtdProvider.md index 6722319cbb5..fceb012e27c 100644 --- a/modules/humansecurityRtdProvider.md +++ b/modules/humansecurityRtdProvider.md @@ -23,7 +23,7 @@ sent within bid requests, and used for bot detection on the backend. * No incremental signals collected beyond existing HUMAN post-bid solution * Offsets negative impact from loss of granularity in IP and User Agent at bid time * Does not expose collected IVT signal to any party who doesn’t otherwise already have access to the same signal collected post-bid -* Does not introduce meaningful latency, as demonstrated in the Latency section +* Does not introduce meaningful latency * Comes at no additional cost to collect IVT signal and make it available at bid time * Leveraged to differentiate the invalid bid requests at device level, and cannot be used to identify a user or a device, thus preserving privacy. @@ -56,8 +56,8 @@ pbjs.setConfig({ }); ``` -It can be optionally parameterized, for example, to include client ID obtained from HUMAN, -should any advanced reporting be needed, or to have verbose output for troubleshooting: +Other parameters can also be provided. For example, a client ID obtained from HUMAN can +optionally be provided, or verbose output can be enabled for troubleshooting purposes: ```javascript pbjs.setConfig({ @@ -79,6 +79,7 @@ pbjs.setConfig({ | :--------------- | :------------ | :------------------------------------------------------------------ |:---------| | `clientId` | String | Should you need advanced reporting, contact [prebid@humansecurity.com](prebid@humansecurity.com) to receive client ID. | No | | `verbose` | Boolean | Only set to `true` if troubleshooting issues. | No | +| `perBidderOptOut` | string[] | Pass any bidder alias to opt-out from per-bidder signal generation. | No | ## Logging, latency and troubleshooting @@ -89,9 +90,6 @@ of type `ERROR`. With `verbose` parameter set to `true`, it may additionally: * Call `logWarning`, resulting in `auctionDebug` events of type `WARNING`, * Call `logInfo` with latency information. - * To observe these messages in console, Prebid.js must be run in - [debug mode](https://docs.prebid.org/dev-docs/publisher-api-reference/setConfig.html#debugging) - - either by adding `?pbjs_debug=true` to your page's URL, or by configuring with `pbjs.setConfig({ debug: true });` Example output of the latency information: @@ -139,6 +137,7 @@ There are a few points that are worth being mentioned separately, to avoid confu the ever-evolving nature of the threat landscape without the publishers having to rebuild their Prebid.js frequently. * The signal collection script is also obfuscated, as a defense-in-depth measure in order to complicate tampering by bad actors, as are all similar scripts in the industry, which is something that cannot be accommodated by Prebid.js itself. +* The collected signals are encrypted before they are passed to bid adapters and can only be interpreted by HUMAN backend systems. ## Why is this approach an innovation? @@ -199,10 +198,14 @@ ensuring the value of the inventory. ## FAQ +### Is partnership with HUMAN required to use the submodule? + +No. Using this submodule does not require any prior communication with HUMAN or being a client of HUMAN. +It is free and usage of the submodule doesn’t automatically make a Publisher HUMAN client. + ### Is latency an issue? The HUMAN Security RTD submodule is designed to minimize any latency in the auction within normal SLAs. - ### Do publishers get any insight into how the measurement is judged? Having the The HUMAN Security RTD submodule be part of the prebid process will allow the publisher to have insight @@ -211,13 +214,10 @@ inventory to the buyer. ### How are privacy concerns addressed? -The HUMAN Security RTD submodule seeks to reduce the impacts from signal deprecation that are inevitable without -compromising privacy by avoiding re-identification. Each bid request is enriched with just enough signal -to identify if the traffic is invalid or not. - -By having the The HUMAN Security RTD submodule operate at the Prebid level, data can be controlled -and not as freely passed through the bidstream where it may be accessible to various unknown parties. +The HUMAN Security RTD submodule seeks to reduce the impacts of signal deprecation without compromising privacy. +Each bid request is enriched with just enough signal to identify if the traffic is invalid or not, and these +signals are encrypted before being included in bid requests to prevent misuse. Note: anti-fraud use cases typically have carve outs in laws and regulations to permit data collection essential for effective fraud mitigation, but this does not constitute legal advice and you should -consult your attorney when making data access decisions. +consult your attorney when making data access decisions. \ No newline at end of file diff --git a/modules/humansecurityRtdProvider.ts b/modules/humansecurityRtdProvider.ts new file mode 100644 index 00000000000..96b99eadfb7 --- /dev/null +++ b/modules/humansecurityRtdProvider.ts @@ -0,0 +1,204 @@ +/** + * This module adds humansecurity provider to the real time data module + * + * The {@link module:modules/realTimeData} module is required + * The module will inject the HUMAN Security script into the context where Prebid.js is initialized, enriching bid requests with specific + * data to provide advanced protection against ad fraud and spoofing. + * @module modules/humansecurityRtdProvider + * @requires module:modules/realTimeData + */ +import { submodule } from '../src/hook.js'; +import { prefixLog, generateUUID, getWindowSelf } from '../src/utils.js'; +import { getRefererInfo } from '../src/refererDetection.js'; +import { getGlobal, PrebidJS } from '../src/prebidGlobal.js'; +import { loadExternalScript } from '../src/adloader.js'; +import { MODULE_TYPE_RTD } from '../src/activities/modules.js'; +import { AllConsentData } from '../src/consentHandler.ts'; +import type { RTDProvider, RTDProviderConfig, RtdProviderSpec } from './rtdModule/spec.ts'; +import type { StartAuctionOptions } from '../src/prebid.ts'; +import type { AuctionProperties } from '../src/auction.ts'; + +declare module './rtdModule/spec.ts' { + interface ProviderConfig { + humansecurity: { + params?: { + clientId?: string; + verbose?: boolean; + perBidderOptOut?: string[]; + }; + }; + } +} + +interface HumanSecurityImpl { + connect( + pbjs: PrebidJS, + callback: (m: string) => void | null, + config: RTDProviderConfig<'humansecurity'> + ): void; + + getBidRequestData( + reqBidsConfigObj: StartAuctionOptions, + callback: () => void, + config: RTDProviderConfig<'humansecurity'>, + userConsent: AllConsentData + ): void; + + onAuctionInitEvent( + pbjs: PrebidJS, + auctionDetails: AuctionProperties, + config: RTDProviderConfig<'humansecurity'>, + userConsent: AllConsentData + ): void; +} + +const SUBMODULE_NAME = 'humansecurity' as const; +const SCRIPT_URL = 'https://sonar.script.ac/prebid/rtd.js'; +const MODULE_VERSION = 1; + +const { logWarn, logError } = prefixLog(`[${SUBMODULE_NAME}]:`); + +let implRef: HumanSecurityImpl | null = null; +let clientId: string = ''; +let verbose: boolean = false; +let sessionId: string = ''; + +/** + * Injects HUMAN Security script on the page to facilitate pre-bid signal collection. + */ + +const load = (config: RTDProviderConfig<'humansecurity'>) => { + // Load implementation script and pass configuration parameters via data attributes + clientId = config?.params?.clientId || ''; + if (clientId && (typeof clientId !== 'string' || !/^\w{3,16}$/.test(clientId))) { + throw new Error(`The 'clientId' parameter must be a short alphanumeric string`); + } + + // Load/reset the state + verbose = !!config?.params?.verbose; + implRef = null; + sessionId = generateUUID(); + + // Get the best domain possible here, it still might be null + const refDomain = getRefererInfo().domain || ''; + + // Once loaded, the implementation script will publish an API using + // the session ID value it was given in data attributes + const scriptAttrs = { 'data-sid': sessionId }; + const scriptUrl = `${SCRIPT_URL}?r=${refDomain}${clientId ? `&c=${clientId}` : ''}&mv=${MODULE_VERSION}`; + + loadExternalScript(scriptUrl, MODULE_TYPE_RTD, SUBMODULE_NAME, () => onImplLoaded(config), null, scriptAttrs); +} + +/** + * Retrieves the implementation object created by the loaded script + * using the session ID as a key + */ + +const getImpl = () => { + // Use cached reference if already resolved + if (implRef && typeof implRef === 'object' && typeof implRef.connect === 'function') return implRef; + + // Attempt to resolve from window by session ID + const wnd = getWindowSelf(); + const impl: HumanSecurityImpl = wnd[`sonar_${sessionId}`]; + + if (typeof impl !== 'object' || typeof impl.connect !== 'function') { + verbose && logWarn('onload', 'Unable to access the implementation script'); + return; + } + implRef = impl; + return impl; +} + +/** + * The callback to loadExternalScript + * Establishes the bridge between this RTD submodule and the loaded implementation + */ + +const onImplLoaded = (config: RTDProviderConfig<'humansecurity'>) => { + const impl = getImpl(); + if (!impl) return; + + // And set up a bridge between the RTD submodule and the implementation. + impl.connect(getGlobal(), null, config); +} + +/** + * The bridge function will be called by the implementation script + * to update the token information or report errors + */ + +/** + * https://docs.prebid.org/dev-docs/add-rtd-submodule.html#getbidrequestdata + */ + +const getBidRequestData = ( + reqBidsConfigObj: StartAuctionOptions, + callback: () => void, + config: RtdProviderSpec<'humansecurity'>, + userConsent: AllConsentData +) => { + const impl = getImpl(); + if (!impl || typeof impl.getBidRequestData !== 'function') { + // Implementation not available; continue auction by invoking the callback. + callback(); + return; + } + + impl.getBidRequestData(reqBidsConfigObj, callback, config, userConsent); +} + +/** + * Event hooks + * https://docs.prebid.org/dev-docs/add-rtd-submodule.html#using-event-listeners + */ + +const onAuctionInitEvent = (auctionDetails: AuctionProperties, config: RTDProviderConfig<'humansecurity'>, userConsent: AllConsentData) => { + const impl = getImpl(); + if (!impl || typeof impl.onAuctionInitEvent !== 'function') return; + impl.onAuctionInitEvent(getGlobal(), auctionDetails, config, userConsent); +} + +/** + * Submodule registration + */ + +type RtdProviderSpecWithHooks

= RtdProviderSpec

& { + onAuctionInitEvent?: (auctionDetails: AuctionProperties, config: RTDProviderConfig

, userConsent: AllConsentData) => void; +}; + +const subModule: RtdProviderSpecWithHooks<'humansecurity'> = ({ + name: SUBMODULE_NAME, + init: (config, _userConsent) => { + try { + load(config); + return true; + } catch (err) { + const message = (err && typeof err === 'object' && 'message' in err) + ? (err as any).message + : String(err); + logError('init', message); + return false; + } + }, + getBidRequestData, + onAuctionInitEvent, +}); + +const registerSubModule = () => { submodule('realTimeData', subModule); } +registerSubModule(); + +/** + * Exporting local (and otherwise encapsulated to this module) functions + * for testing purposes + */ + +export const __TEST__ = { + SUBMODULE_NAME, + SCRIPT_URL, + main: registerSubModule, + load, + onImplLoaded, + getBidRequestData +}; diff --git a/modules/hybridBidAdapter.js b/modules/hybridBidAdapter.js index 1a9552c1754..24168e625d3 100644 --- a/modules/hybridBidAdapter.js +++ b/modules/hybridBidAdapter.js @@ -1,7 +1,7 @@ -import {_map, isArray} from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER, VIDEO} from '../src/mediaTypes.js'; -import {createRenderer, getMediaTypeFromBid, hasVideoMandatoryParams} from '../libraries/hybridVoxUtils/index.js'; +import { _map, isArray } from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; +import { createRenderer, getMediaTypeFromBid, hasVideoMandatoryParams } from '../libraries/hybridVoxUtils/index.js'; /** * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest @@ -52,8 +52,7 @@ function buildBid(bidData) { currency: bidData.currency, netRevenue: true, ttl: TTL, - meta: { - advertiserDomains: bidData.advertiserDomains || []} + meta: { advertiserDomains: bidData.advertiserDomains || [] } }; if (bidData.placement === PLACEMENT_TYPE_VIDEO) { diff --git a/modules/hypelabBidAdapter.js b/modules/hypelabBidAdapter.js index bc562b84cb3..2112d93b9e5 100644 --- a/modules/hypelabBidAdapter.js +++ b/modules/hypelabBidAdapter.js @@ -5,6 +5,7 @@ import { getDevicePixelRatio } from '../libraries/devicePixelRatio/devicePixelRa import { ajax } from '../src/ajax.js'; import { getBoundingClientRect } from '../libraries/boundingClientRect/boundingClientRect.js'; import { getWalletPresence, getWalletProviderFlags } from '../libraries/hypelabUtils/hypelabUtils.js'; +import { getAdUnitElement } from '../src/utils/adUnits.js'; export const BIDDER_CODE = 'hypelab'; export const ENDPOINT_URL = 'https://api.hypelab.com'; @@ -55,7 +56,7 @@ function buildRequests(validBidRequests, bidderRequest) { winDimensions?.innerHeight || 0 ), ]; - const pp = getPosition(request.adUnitCode); + const pp = getPosition(request); const payload = { property_slug: request.params.property_slug, @@ -121,8 +122,8 @@ function getBidFloor(bid, sizes) { return floor; } -function getPosition(id) { - const element = document.getElementById(id); +function getPosition(bidRequest) { + const element = getAdUnitElement(bidRequest); if (!element) return null; const rect = getBoundingClientRect(element); return [rect.left, rect.top]; diff --git a/modules/iasRtdProvider.js b/modules/iasRtdProvider.js index 3f0b8513299..9205d6c4561 100644 --- a/modules/iasRtdProvider.js +++ b/modules/iasRtdProvider.js @@ -1,9 +1,9 @@ -import {submodule} from '../src/hook.js'; +import { submodule } from '../src/hook.js'; import * as utils from '../src/utils.js'; -import {ajax} from '../src/ajax.js'; -import {getGlobal} from '../src/prebidGlobal.js'; -import {getAdUnitSizes} from '../libraries/sizeUtils/sizeUtils.js'; -import {getGptSlotInfoForAdUnitCode} from '../libraries/gptUtils/gptUtils.js'; +import { ajax } from '../src/ajax.js'; +import { getGlobal } from '../src/prebidGlobal.js'; +import { getAdUnitSizes } from '../libraries/sizeUtils/sizeUtils.js'; +import { getGptSlotInfoForAdUnitCode } from '../libraries/gptUtils/gptUtils.js'; import { mergeDeep } from '../src/utils.js'; /** diff --git a/modules/id5AnalyticsAdapter.js b/modules/id5AnalyticsAdapter.js index dca244bb317..033483157b4 100644 --- a/modules/id5AnalyticsAdapter.js +++ b/modules/id5AnalyticsAdapter.js @@ -1,8 +1,8 @@ import buildAdapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; -import {EVENTS} from '../src/constants.js'; +import { EVENTS } from '../src/constants.js'; import adapterManager from '../src/adapterManager.js'; -import {ajax} from '../src/ajax.js'; -import {compressDataWithGZip, isGzipCompressionSupported, logError, logInfo} from '../src/utils.js'; +import { ajax } from '../src/ajax.js'; +import { compressDataWithGZip, isGzipCompressionSupported, logError, logInfo } from '../src/utils.js'; import * as events from '../src/events.js'; const { @@ -26,7 +26,7 @@ const PBJS_VERSION = 'v' + '$prebid.version$'; const ID5_REDACTED = '__ID5_REDACTED__'; const isArray = Array.isArray; -const id5Analytics = Object.assign(buildAdapter({analyticsType: 'endpoint'}), { +const id5Analytics = Object.assign(buildAdapter({ analyticsType: 'endpoint' }), { eventsToTrack: STANDARD_EVENTS_TO_TRACK, track: (event) => { diff --git a/modules/id5IdSystem.js b/modules/id5IdSystem.js index d8f553c6ae0..bce8e5bcb6e 100644 --- a/modules/id5IdSystem.js +++ b/modules/id5IdSystem.js @@ -8,6 +8,7 @@ import { deepAccess, deepClone, + deepEqual, deepSetValue, isEmpty, isEmptyStr, @@ -16,13 +17,13 @@ import { logInfo, logWarn } from '../src/utils.js'; -import {fetch} from '../src/ajax.js'; -import {submodule} from '../src/hook.js'; -import {getRefererInfo} from '../src/refererDetection.js'; -import {getStorageManager} from '../src/storageManager.js'; -import {MODULE_TYPE_UID} from '../src/activities/modules.js'; -import {PbPromise} from '../src/utils/promise.js'; -import {loadExternalScript} from '../src/adloader.js'; +import { fetch } from '../src/ajax.js'; +import { submodule } from '../src/hook.js'; +import { getRefererInfo } from '../src/refererDetection.js'; +import { getStorageManager } from '../src/storageManager.js'; +import { MODULE_TYPE_UID } from '../src/activities/modules.js'; +import { PbPromise } from '../src/utils/promise.js'; +import { loadExternalScript } from '../src/adloader.js'; /** * @typedef {import('../modules/userId/spec.ts').IdProviderSpec} Submodule @@ -39,7 +40,7 @@ const ID5_API_CONFIG_URL = 'https://id5-sync.com/api/config/prebid'; const ID5_DOMAIN = 'id5-sync.com'; const TRUE_LINK_SOURCE = 'true-link-id5-sync.com'; -export const storage = getStorageManager({moduleType: MODULE_TYPE_UID, moduleName: MODULE_NAME}); +export const storage = getStorageManager({ moduleType: MODULE_TYPE_UID, moduleName: MODULE_NAME }); /** * @typedef {Object} Id5Response @@ -118,6 +119,7 @@ export const storage = getStorageManager({moduleType: MODULE_TYPE_UID, moduleNam * @property {Array} [segments] - A list of segments to push to partners. Supported only in multiplexing. * @property {boolean} [disableUaHints] - When true, look up of high entropy values through user agent hints is disabled. * @property {string} [gamTargetingPrefix] - When set, the GAM targeting tags will be set and use the specified prefix, for example 'id5'. + * @property {boolean} [exposeTargeting] - When set, the ID5 targeting consumer mechanism will be enabled. */ /** @@ -245,7 +247,7 @@ export const id5IdSubmodule = { responseObj.euid = { uid: ext.euid.uids[0].id, source: ext.euid.source, - ext: {provider: ID5_DOMAIN} + ext: { provider: ID5_DOMAIN } }; } @@ -308,7 +310,7 @@ export const id5IdSubmodule = { cbFunction(); }); }; - return {callback: resp}; + return { callback: resp }; }, /** @@ -325,14 +327,14 @@ export const id5IdSubmodule = { extendId(config, consentData, cacheIdObj) { if (!hasWriteConsentToLocalStorage(consentData?.gdpr)) { logInfo(LOG_PREFIX + 'No consent given for ID5 local storage writing, skipping nb increment.'); - return {id: cacheIdObj}; + return { id: cacheIdObj }; } if (getPartnerResponse(cacheIdObj, config.params)) { // response for partner is present logInfo(LOG_PREFIX + 'using cached ID', cacheIdObj); const updatedObject = deepClone(cacheIdObj); const responseToUpdate = getPartnerResponse(updatedObject, config.params); responseToUpdate.nbPage = incrementNb(responseToUpdate); - return {id: updatedObject}; + return { id: updatedObject }; } else { logInfo(LOG_PREFIX + ' refreshing ID. Cached object does not have ID for partner', cacheIdObj); return this.getId(config, consentData, cacheIdObj); @@ -419,7 +421,7 @@ export class IdFetchFlow { const extensionsUrl = extensionsCallConfig.url; const method = extensionsCallConfig.method || 'GET'; const body = method === 'GET' ? undefined : JSON.stringify(extensionsCallConfig.body || {}); - const response = await fetch(extensionsUrl, {method, body}); + const response = await fetch(extensionsUrl, { method, body }); if (!response.ok) { throw new Error('Error while calling extensions endpoint: ', response); } @@ -436,7 +438,7 @@ export class IdFetchFlow { ...additionalData, extensions: extensionsData }); - const response = await fetch(fetchUrl, {method: 'POST', body, credentials: 'include'}); + const response = await fetch(fetchUrl, { method: 'POST', body, credentials: 'include' }); if (!response.ok) { throw new Error('Error while calling fetch endpoint: ', response); } @@ -451,7 +453,7 @@ export class IdFetchFlow { const referer = getRefererInfo(); const signature = this.cacheIdObj ? this.cacheIdObj.signature : undefined; const nbPage = incrementNb(this.cacheIdObj); - const trueLinkInfo = window.id5Bootstrap ? window.id5Bootstrap.getTrueLinkInfo() : {booted: false}; + const trueLinkInfo = window.id5Bootstrap ? window.id5Bootstrap.getTrueLinkInfo() : { booted: false }; const data = { 'partner': params.partner, @@ -490,7 +492,7 @@ export class IdFetchFlow { if (params.provider !== undefined && !isEmptyStr(params.provider)) { data.provider = params.provider; } - const abTestingConfig = params.abTesting || {enabled: false}; + const abTestingConfig = params.abTesting || { enabled: false }; if (abTestingConfig.enabled) { data.ab_testing = { @@ -569,17 +571,30 @@ function incrementNb(cachedObj) { } function updateTargeting(fetchResponse, config) { - if (config.params.gamTargetingPrefix) { - const tags = fetchResponse.tags; - if (tags) { - window.googletag = window.googletag || {cmd: []}; + const tags = fetchResponse.tags; + if (tags) { + if (config.params.gamTargetingPrefix) { + window.googletag = window.googletag || { cmd: [] }; window.googletag.cmd = window.googletag.cmd || []; window.googletag.cmd.push(() => { for (const tag in tags) { - window.googletag.pubads().setTargeting(config.params.gamTargetingPrefix + '_' + tag, tags[tag]); + window.googletag.setConfig({ targeting: { [config.params.gamTargetingPrefix + '_' + tag]: tags[tag] } }); } }); } + + if (config.params.exposeTargeting && !deepEqual(window.id5tags?.tags, tags)) { + window.id5tags = window.id5tags || { cmd: [] }; + window.id5tags.cmd = window.id5tags.cmd || []; + window.id5tags.cmd.forEach(tagsCallback => { + setTimeout(() => tagsCallback(tags), 0); + }); + window.id5tags.cmd.push = function (tagsCallback) { + tagsCallback(tags) + Array.prototype.push.call(window.id5tags.cmd, tagsCallback); + }; + window.id5tags.tags = tags + } } } diff --git a/modules/id5IdSystem.md b/modules/id5IdSystem.md index 363dd02e831..aaf34fa4054 100644 --- a/modules/id5IdSystem.md +++ b/modules/id5IdSystem.md @@ -33,7 +33,8 @@ pbjs.setConfig({ }, disableExtensions: false,// optional canCookieSync: true, // optional, has effect only when externalModuleUrl is used - gamTargetingPrefix: "id5" // optional, when set the ID5 module will set gam targeting paramaters with this prefix + gamTargetingPrefix: "id5", // optional, when set the ID5 module will set gam targeting paramaters with this prefix + exposeTargeting: false // optional, when set the ID5 module will execute `window.id5tags.cmd` callbacks for custom targeting tags }, storage: { type: 'html5', // "html5" is the required storage type @@ -47,25 +48,26 @@ pbjs.setConfig({ }); ``` -| Param under userSync.userIds[] | Scope | Type | Description | Example | -| --- | --- | --- |------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| --- | -| name | Required | String | The name of this module: `"id5Id"` | `"id5Id"` | -| params | Required | Object | Details for the ID5 ID. | | -| params.partner | Required | Number | This is the ID5 Partner Number obtained from registering with ID5. | `173` | -| params.externalModuleUrl | Optional | String | The URL for the id5-prebid external module. It is recommended to use the latest version at the URL in the example. Source code available [here](https://github.com/id5io/id5-api.js/blob/master/src/id5PrebidModule.js). | https://cdn.id5-sync.com/api/1.0/id5PrebidModule.js -| params.pd | Optional | String | Partner-supplied data used for linking ID5 IDs across domains. See [our documentation](https://wiki.id5.io/en/identitycloud/retrieve-id5-ids/passing-partner-data-to-id5) for details on generating the string. Omit the parameter or leave as an empty string if no data to supply | `"MT1iNTBjY..."` | -| params.provider | Optional | String | An identifier provided by ID5 to technology partners who manage Prebid setups on behalf of publishers. Reach out to [ID5](mailto:prebid@id5.io) if you have questions about this parameter | `pubmatic-identity-hub` | -| params.abTesting | Optional | Object | Allows publishers to easily run an A/B Test. If enabled and the user is in the Control Group, the ID5 ID will NOT be exposed to bid adapters for that request | Disabled by default | -| params.abTesting.enabled | Optional | Boolean | Set this to `true` to turn on this feature | `true` or `false` | -| params.abTesting.controlGroupPct | Optional | Number | Must be a number between `0.0` and `1.0` (inclusive) and is used to determine the percentage of requests that fall into the control group (and thus not exposing the ID5 ID). For example, a value of `0.20` will result in 20% of requests without an ID5 ID and 80% with an ID. | `0.1` | -| params.disableExtensions | Optional | Boolean | Set this to `true` to force turn off extensions call. Default `false` | `true` or `false` | -| params.canCookieSync | Optional | Boolean | Set this to `true` to enable cookie syncing with other ID5 partners. See [our documentation](https://wiki.id5.io/docs/initiate-cookie-sync-to-id5) for details. Default `false` | `true` or `false` | +| Param under userSync.userIds[] | Scope | Type | Description | Example | +| --- | --- | --- |-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------| +| name | Required | String | The name of this module: `"id5Id"` | `"id5Id"` | +| params | Required | Object | Details for the ID5 ID. | | +| params.partner | Required | Number | This is the ID5 Partner Number obtained from registering with ID5. | `173` | +| params.externalModuleUrl | Optional | String | The URL for the id5-prebid external module. It is recommended to use the latest version at the URL in the example. Source code available [here](https://github.com/id5io/id5-api.js/blob/master/src/id5PrebidModule.js). | https://cdn.id5-sync.com/api/1.0/id5PrebidModule.js +| params.pd | Optional | String | Partner-supplied data used for linking ID5 IDs across domains. See [our documentation](https://wiki.id5.io/en/identitycloud/retrieve-id5-ids/passing-partner-data-to-id5) for details on generating the string. Omit the parameter or leave as an empty string if no data to supply | `"MT1iNTBjY..."` | +| params.provider | Optional | String | An identifier provided by ID5 to technology partners who manage Prebid setups on behalf of publishers. Reach out to [ID5](mailto:prebid@id5.io) if you have questions about this parameter | `pubmatic-identity-hub` | +| params.abTesting | Optional | Object | Allows publishers to easily run an A/B Test. If enabled and the user is in the Control Group, the ID5 ID will NOT be exposed to bid adapters for that request | Disabled by default | +| params.abTesting.enabled | Optional | Boolean | Set this to `true` to turn on this feature | `true` or `false` | +| params.abTesting.controlGroupPct | Optional | Number | Must be a number between `0.0` and `1.0` (inclusive) and is used to determine the percentage of requests that fall into the control group (and thus not exposing the ID5 ID). For example, a value of `0.20` will result in 20% of requests without an ID5 ID and 80% with an ID. | `0.1` | +| params.disableExtensions | Optional | Boolean | Set this to `true` to force turn off extensions call. Default `false` | `true` or `false` | +| params.canCookieSync | Optional | Boolean | Set this to `true` to enable cookie syncing with other ID5 partners. See [our documentation](https://wiki.id5.io/docs/initiate-cookie-sync-to-id5) for details. Default `false` | `true` or `false` | | params.gamTargetingPrefix | Optional | String | When this parameter is set the ID5 module will set appropriate GAM pubads targeting tags | `id5` | -| storage | Required | Object | Storage settings for how the User ID module will cache the ID5 ID locally | | -| storage.type | Required | String | This is where the results of the user ID will be stored. ID5 **requires** `"html5"`. | `"html5"` | -| storage.name | Required | String | The name of the local storage where the user ID will be stored. ID5 **requires** `"id5id"`. | `"id5id"` | -| storage.expires | Optional | Integer | How long (in days) the user ID information will be stored. ID5 recommends `90`. | `90` | -| storage.refreshInSeconds | Optional | Integer | How many seconds until the ID5 ID will be refreshed. ID5 strongly recommends 2 hours between refreshes | `7200` | +| params.exposeTargeting | Optional | Boolean | When this parameter is set the ID5 module will execute `window.id5tags.cmd` callbacks for custom targeting tags | `false` | +| storage | Required | Object | Storage settings for how the User ID module will cache the ID5 ID locally | | +| storage.type | Required | String | This is where the results of the user ID will be stored. ID5 **requires** `"html5"`. | `"html5"` | +| storage.name | Required | String | The name of the local storage where the user ID will be stored. ID5 **requires** `"id5id"`. | `"id5id"` | +| storage.expires | Optional | Integer | How long (in days) the user ID information will be stored. ID5 recommends `90`. | `90` | +| storage.refreshInSeconds | Optional | Integer | How many seconds until the ID5 ID will be refreshed. ID5 strongly recommends 2 hours between refreshes | `7200` | **ATTENTION:** As of Prebid.js v4.14.0, ID5 requires `storage.type` to be `"html5"` and `storage.name` to be `"id5id"`. Using other values will display a warning today, but in an upcoming release, it will prevent the ID5 module from loading. This change is to ensure the ID5 module in Prebid.js interoperates properly with the [ID5 API](https://github.com/id5io/id5-api.js) and to reduce the size of publishers' first-party cookies that are sent to their web servers. If you have any questions, please reach out to us at [prebid@id5.io](mailto:prebid@id5.io). diff --git a/modules/idImportLibrary.js b/modules/idImportLibrary.js index b3e0be4505c..248f999f5bd 100644 --- a/modules/idImportLibrary.js +++ b/modules/idImportLibrary.js @@ -235,7 +235,7 @@ function postData() { syncPayload.uids = userIds; const payloadString = JSON.stringify(syncPayload); _logInfo(payloadString); - ajax(conf.url, syncCallback(), payloadString, {method: 'POST', withCredentials: true}); + ajax(conf.url, syncCallback(), payloadString, { method: 'POST', withCredentials: true }); } function associateIds() { diff --git a/modules/identityLinkIdSystem.js b/modules/identityLinkIdSystem.js index 0f02e40a383..6d4442f2fc9 100644 --- a/modules/identityLinkIdSystem.js +++ b/modules/identityLinkIdSystem.js @@ -8,8 +8,8 @@ import * as utils from '../src/utils.js' import { ajax } from '../src/ajax.js'; import { submodule } from '../src/hook.js'; -import {getStorageManager} from '../src/storageManager.js'; -import {MODULE_TYPE_UID} from '../src/activities/modules.js'; +import { getStorageManager } from '../src/storageManager.js'; +import { MODULE_TYPE_UID } from '../src/activities/modules.js'; /** * @typedef {import('../modules/userId/index.js').Submodule} Submodule @@ -20,7 +20,7 @@ import {MODULE_TYPE_UID} from '../src/activities/modules.js'; const MODULE_NAME = 'identityLink'; -export const storage = getStorageManager({moduleType: MODULE_TYPE_UID, moduleName: MODULE_NAME}); +export const storage = getStorageManager({ moduleType: MODULE_TYPE_UID, moduleName: MODULE_NAME }); const liverampEnvelopeName = '_lr_env'; @@ -58,7 +58,7 @@ export const identityLinkSubmodule = { utils.logError('identityLink: requires partner id to be defined'); return; } - const {gdpr, gpp: gppData} = consentData ?? {}; + const { gdpr, gpp: gppData } = consentData ?? {}; const hasGdpr = (gdpr && typeof gdpr.gdprApplies === 'boolean' && gdpr.gdprApplies) ? 1 : 0; const gdprConsentString = hasGdpr ? gdpr.consentString : ''; // use protocol relative urls for http or https diff --git a/modules/idxBidAdapter.js b/modules/idxBidAdapter.js index 12adb4058ae..af2e9c31210 100644 --- a/modules/idxBidAdapter.js +++ b/modules/idxBidAdapter.js @@ -5,7 +5,7 @@ import { interpretResponse } from '../libraries/precisoUtils/bidUtils.js'; const BIDDER_CODE = 'idx' const ENDPOINT_URL = 'https://dev-event.dxmdp.com/rest/api/v1/bid' -const SUPPORTED_MEDIA_TYPES = [ BANNER ] +const SUPPORTED_MEDIA_TYPES = [BANNER] export const spec = { code: BIDDER_CODE, diff --git a/modules/idxIdSystem.js b/modules/idxIdSystem.js index da8230dbe29..55624faac0e 100644 --- a/modules/idxIdSystem.js +++ b/modules/idxIdSystem.js @@ -6,8 +6,8 @@ */ import { isStr, isPlainObject, logError } from '../src/utils.js'; import { submodule } from '../src/hook.js'; -import {getStorageManager} from '../src/storageManager.js'; -import {MODULE_TYPE_UID} from '../src/activities/modules.js'; +import { getStorageManager } from '../src/storageManager.js'; +import { MODULE_TYPE_UID } from '../src/activities/modules.js'; /** * @typedef {import('../modules/userId/index.js').Submodule} Submodule @@ -16,7 +16,7 @@ import {MODULE_TYPE_UID} from '../src/activities/modules.js'; const IDX_MODULE_NAME = 'idx'; const IDX_COOKIE_NAME = '_idx'; -export const storage = getStorageManager({moduleType: MODULE_TYPE_UID, moduleName: IDX_MODULE_NAME}); +export const storage = getStorageManager({ moduleType: MODULE_TYPE_UID, moduleName: IDX_MODULE_NAME }); function readIDxFromCookie() { return storage.cookiesAreEnabled ? storage.getCookie(IDX_COOKIE_NAME) : null; diff --git a/modules/illuminBidAdapter.js b/modules/illuminBidAdapter.js index 0e76e471f4b..37d48e1626f 100644 --- a/modules/illuminBidAdapter.js +++ b/modules/illuminBidAdapter.js @@ -1,6 +1,6 @@ -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER, VIDEO} from '../src/mediaTypes.js'; -import {getStorageManager} from '../src/storageManager.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; +import { getStorageManager } from '../src/storageManager.js'; import { isBidRequestValid, createUserSyncGetter, createInterpretResponseFn, createBuildRequestsFn } from '../libraries/vidazooUtils/bidderUtils.js'; @@ -9,7 +9,7 @@ const DEFAULT_SUB_DOMAIN = 'exchange'; const BIDDER_CODE = 'illumin'; const BIDDER_VERSION = '1.0.0'; const GVLID = 149; -export const storage = getStorageManager({bidderCode: BIDDER_CODE}); +export const storage = getStorageManager({ bidderCode: BIDDER_CODE }); export function createDomain(subDomain = DEFAULT_SUB_DOMAIN) { return `https://${subDomain}.illumin.com`; diff --git a/modules/imRtdProvider.js b/modules/imRtdProvider.js index 46573a81c15..5052b4c12a6 100644 --- a/modules/imRtdProvider.js +++ b/modules/imRtdProvider.js @@ -4,10 +4,10 @@ * @module modules/imRtdProvider * @requires module:modules/realTimeData */ -import {ajax} from '../src/ajax.js'; -import {config} from '../src/config.js'; -import {getGlobal} from '../src/prebidGlobal.js' -import {getStorageManager} from '../src/storageManager.js'; +import { ajax } from '../src/ajax.js'; +import { config } from '../src/config.js'; +import { getGlobal } from '../src/prebidGlobal.js' +import { getStorageManager } from '../src/storageManager.js'; import { deepSetValue, deepAccess, @@ -17,8 +17,8 @@ import { logInfo, isFn } from '../src/utils.js' -import {submodule} from '../src/hook.js'; -import {MODULE_TYPE_RTD} from '../src/activities/modules.js'; +import { submodule } from '../src/hook.js'; +import { MODULE_TYPE_RTD } from '../src/activities/modules.js'; /** * @typedef {import('../modules/rtdModule/index.js').RtdSubmodule} RtdSubmodule @@ -32,7 +32,7 @@ const segmentsMaxAge = 3600000; // 1 hour (30 * 60 * 1000) const uidMaxAge = 1800000; // 30 minites (30 * 60 * 1000) const vidMaxAge = 97200000000; // 37 months ((365 * 3 + 30) * 24 * 60 * 60 * 1000) -export const storage = getStorageManager({moduleType: MODULE_TYPE_RTD, moduleName: submoduleName}); +export const storage = getStorageManager({ moduleType: MODULE_TYPE_RTD, moduleName: submoduleName }); function setImDataInCookie(value) { storage.setCookie( @@ -103,7 +103,7 @@ export function getCustomBidderFunction(config, bidder) { */ export function setRealTimeData(bidConfig, moduleConfig, data) { const adUnits = bidConfig.adUnits || getGlobal().adUnits; - const utils = {deepSetValue, deepAccess, logInfo, logError, mergeDeep}; + const utils = { deepSetValue, deepAccess, logInfo, logError, mergeDeep }; if (data.im_segments) { const segments = getSegments(data.im_segments, moduleConfig); @@ -112,7 +112,7 @@ export function setRealTimeData(bidConfig, moduleConfig, data) { deepSetValue(ortb2, 'user.ext.data.im_uid', data.im_uid); if (moduleConfig.params.setGptKeyValues || !moduleConfig.params.hasOwnProperty('setGptKeyValues')) { - window.googletag = window.googletag || {cmd: []}; + window.googletag = window.googletag || { cmd: [] }; window.googletag.cmd = window.googletag.cmd || []; window.googletag.cmd.push(() => { window.googletag.pubads().setTargeting('im_segments', segments); @@ -165,7 +165,7 @@ export function getRealTimeData(reqBidsConfigObj, onDone, moduleConfig) { } if (sids !== null) { - setRealTimeData(reqBidsConfigObj, moduleConfig, {im_uid: uid, im_segments: parsedSids}); + setRealTimeData(reqBidsConfigObj, moduleConfig, { im_uid: uid, im_segments: parsedSids }); onDone(); alreadyDone = true; } @@ -175,7 +175,7 @@ export function getRealTimeData(reqBidsConfigObj, onDone, moduleConfig) { apiUrl, getApiCallback(reqBidsConfigObj, alreadyDone ? undefined : onDone, moduleConfig), undefined, - {method: 'GET', withCredentials: true} + { method: 'GET', withCredentials: true } ); } } @@ -212,7 +212,7 @@ export function getApiCallback(reqBidsConfigObj, onDone, moduleConfig) { } if (parsedResponse.segments) { - setRealTimeData(reqBidsConfigObj, moduleConfig, {im_uid: parsedResponse.uid, im_segments: parsedResponse.segments}); + setRealTimeData(reqBidsConfigObj, moduleConfig, { im_uid: parsedResponse.uid, im_segments: parsedResponse.segments }); storage.setDataInLocalStorage(imRtdLocalName, parsedResponse.segments); storage.setDataInLocalStorage(`${imRtdLocalName}_mt`, new Date(timestamp()).toUTCString()); } diff --git a/modules/impactifyBidAdapter.js b/modules/impactifyBidAdapter.js index 49ffe99245d..14a35d318f5 100644 --- a/modules/impactifyBidAdapter.js +++ b/modules/impactifyBidAdapter.js @@ -1,11 +1,11 @@ 'use strict'; -import {getDNT} from '../libraries/dnt/index.js'; import { deepAccess, deepSetValue, generateUUID, getWinDimensions, isPlainObject } from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { config } from '../src/config.js'; import { ajax } from '../src/ajax.js'; import { getStorageManager } from '../src/storageManager.js'; +import { getDNT } from '../libraries/dnt/index.js'; /** * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest diff --git a/modules/improvedigitalBidAdapter.js b/modules/improvedigitalBidAdapter.js index 3846794f7cc..2fbff040159 100644 --- a/modules/improvedigitalBidAdapter.js +++ b/modules/improvedigitalBidAdapter.js @@ -1,11 +1,11 @@ -import {deepAccess, deepSetValue, getBidIdParameter, getUniqueIdentifierStr, logWarn, mergeDeep} from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {config} from '../src/config.js'; -import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js'; -import {Renderer} from '../src/Renderer.js'; -import {hasPurpose1Consent} from '../src/utils/gdpr.js'; -import {ortbConverter} from '../libraries/ortbConverter/converter.js'; -import {convertCurrency} from '../libraries/currencyUtils/currency.js'; +import { deepAccess, deepSetValue, getBidIdParameter, getUniqueIdentifierStr, logWarn, mergeDeep } from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { config } from '../src/config.js'; +import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; +import { Renderer } from '../src/Renderer.js'; +import { hasPurpose1Consent } from '../src/utils/gdpr.js'; +import { ortbConverter } from '../libraries/ortbConverter/converter.js'; +import { convertCurrency } from '../libraries/currencyUtils/currency.js'; /** * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest @@ -67,7 +67,7 @@ export const spec = { * @return {Array} An array of bids which were nested inside the server. */ interpretResponse(serverResponse, { ortbRequest }) { - return CONVERTER.fromORTB({request: ortbRequest, response: serverResponse.body}).bids; + return CONVERTER.fromORTB({ request: ortbRequest, response: serverResponse.body }).bids; }, /** @@ -139,7 +139,7 @@ export const CONVERTER = ortbConverter({ ttl: CREATIVE_TTL, nativeRequest: { eventtrackers: [ - {event: 1, methods: [1, 2]}, + { event: 1, methods: [1, 2] }, ] } }, @@ -190,7 +190,7 @@ export const CONVERTER = ortbConverter({ if (!bid.adm || !bid.price || bid.hasOwnProperty('errorCode')) { return; } - const {bidRequest} = context; + const { bidRequest } = context; context.mediaType = (() => { const requestMediaTypes = Object.keys(bidRequest.mediaTypes); if (requestMediaTypes.length === 1) return requestMediaTypes[0]; @@ -227,8 +227,8 @@ export const CONVERTER = ortbConverter({ // override to disregard banner.sizes if usePrebidSizes is false if (!bidRequest.mediaTypes[BANNER]) return; if (config.getConfig('improvedigital.usePrebidSizes') === false) { - const banner = Object.assign({}, bidRequest.mediaTypes[BANNER], {sizes: null}); - bidRequest = {...bidRequest, mediaTypes: {[BANNER]: banner}} + const banner = Object.assign({}, bidRequest.mediaTypes[BANNER], { sizes: null }); + bidRequest = { ...bidRequest, mediaTypes: { [BANNER]: banner } } } fillImpBanner(imp, bidRequest, context); }, @@ -236,13 +236,13 @@ export const CONVERTER = ortbConverter({ // override to use video params from both mediaTypes.video and bidRequest.params.video if (!bidRequest.mediaTypes[VIDEO]) return; const video = Object.assign( - {mimes: VIDEO_PARAMS.DEFAULT_MIMES}, + { mimes: VIDEO_PARAMS.DEFAULT_MIMES }, bidRequest.mediaTypes[VIDEO], bidRequest.params?.video ) fillImpVideo( imp, - {...bidRequest, mediaTypes: {[VIDEO]: video}}, + { ...bidRequest, mediaTypes: { [VIDEO]: video } }, context ); deepSetValue(imp, 'ext.is_rewarded_inventory', (video.rewarded === 1 || deepAccess(video, 'ext.rewarded') === 1) || undefined); @@ -274,7 +274,7 @@ const ID_REQUEST = { } function formatRequest(bidRequests, publisherId, extendMode) { - const ortbRequest = CONVERTER.toORTB({bidRequests, bidderRequest, context: {extendMode}}); + const ortbRequest = CONVERTER.toORTB({ bidRequests, bidderRequest, context: { extendMode } }); return { method: 'POST', url: adServerUrl(extendMode, publisherId), diff --git a/modules/imuIdSystem.js b/modules/imuIdSystem.js index 52fdb099e31..c9f57cbfaae 100644 --- a/modules/imuIdSystem.js +++ b/modules/imuIdSystem.js @@ -19,7 +19,7 @@ import { getRefererInfo } from '../src/refererDetection.js'; const MODULE_NAME = 'imuid'; -export const storage = getStorageManager({moduleType: MODULE_TYPE_UID, moduleName: MODULE_NAME}); +export const storage = getStorageManager({ moduleType: MODULE_TYPE_UID, moduleName: MODULE_NAME }); export const storageKey = '__im_uid'; export const storagePpKey = '__im_ppid'; @@ -112,7 +112,7 @@ export function getApiCallback(callback) { export function callImuidApi(apiUrl) { return function (callback) { - ajax(apiUrl, getApiCallback(callback), undefined, {method: 'GET', withCredentials: true}); + ajax(apiUrl, getApiCallback(callback), undefined, { method: 'GET', withCredentials: true }); }; } @@ -156,7 +156,7 @@ export const imuIdSubmodule = { } if (!localData.id) { - return {callback: callImuidApi(apiUrl)}; + return { callback: callImuidApi(apiUrl) }; } if (localData.expired) { callImuidApi(apiUrl)(); diff --git a/modules/insticatorBidAdapter.js b/modules/insticatorBidAdapter.js index 447947e43a4..2455d96fed2 100644 --- a/modules/insticatorBidAdapter.js +++ b/modules/insticatorBidAdapter.js @@ -1,11 +1,11 @@ -import {config} from '../src/config.js'; -import {BANNER, VIDEO} from '../src/mediaTypes.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {deepAccess, generateUUID, logError, isArray, isInteger, isArrayOfNums, deepSetValue, isFn, logWarn, getWinDimensions} from '../src/utils.js'; -import {getStorageManager} from '../src/storageManager.js'; +import { config } from '../src/config.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { deepAccess, generateUUID, logError, isArray, isInteger, isArrayOfNums, deepSetValue, isFn, logWarn, getWinDimensions } from '../src/utils.js'; +import { getStorageManager } from '../src/storageManager.js'; const BIDDER_CODE = 'insticator'; -const ENDPOINT = 'https://ex.ingage.tech/v1/openrtb'; // production endpoint +const ENDPOINT = 'https://ex.ingage.tech/v1/openrtb'; const USER_ID_KEY = 'hb_insticator_uid'; const USER_ID_COOKIE_EXP = 2592000000; // 30 days const BID_TTL = 300; // 5 minutes @@ -29,7 +29,16 @@ export const OPTIONAL_VIDEO_PARAMS = { 'playbackend': (value) => isInteger(value) && [1, 2, 3].includes(value), 'delivery': (value) => isArrayOfNums(value), 'pos': (value) => isInteger(value) && [0, 1, 2, 3, 4, 5, 6, 7].includes(value), - 'api': (value) => isArrayOfNums(value)}; + 'api': (value) => isArrayOfNums(value), + // ORTB 2.6 video parameters + 'podid': (value) => typeof value === 'string' && value.length > 0, + 'podseq': (value) => isInteger(value) && value >= 0, + 'poddur': (value) => isInteger(value) && value > 0, + 'slotinpod': (value) => isInteger(value) && [-1, 0, 1, 2].includes(value), + 'mincpmpersec': (value) => typeof value === 'number' && value > 0, + 'maxseq': (value) => isInteger(value) && value > 0, + 'rqddurs': (value) => isArrayOfNums(value) && value.every(v => v > 0), +}; const ORTB_SITE_FIRST_PARTY_DATA = { 'cat': v => Array.isArray(v) && v.every(c => typeof c === 'string'), @@ -41,7 +50,7 @@ const ORTB_SITE_FIRST_PARTY_DATA = { 'keywords': v => typeof v === 'string', } -export const storage = getStorageManager({bidderCode: BIDDER_CODE}); +export const storage = getStorageManager({ bidderCode: BIDDER_CODE }); config.setDefaults({ insticator: { @@ -106,7 +115,7 @@ function buildVideo(bidRequest) { const context = deepAccess(bidRequest, 'mediaTypes.video.context'); if (!h && !w && playerSize) { - ({w, h} = parsePlayerSizeToWidthHeight(playerSize, w, h)); + ({ w, h } = parsePlayerSizeToWidthHeight(playerSize, w, h)); } const bidRequestVideo = deepAccess(bidRequest, 'mediaTypes.video'); @@ -114,11 +123,11 @@ function buildVideo(bidRequest) { const optionalParams = {}; for (const param in OPTIONAL_VIDEO_PARAMS) { - if (bidRequestVideo[param] && OPTIONAL_VIDEO_PARAMS[param](bidRequestVideo[param])) { + if (bidRequestVideo[param] != null && OPTIONAL_VIDEO_PARAMS[param](bidRequestVideo[param])) { optionalParams[param] = bidRequestVideo[param]; } // remove invalid optional params from bidder specific overrides - if (videoBidderParams[param] && !OPTIONAL_VIDEO_PARAMS[param](videoBidderParams[param])) { + if (videoBidderParams[param] != null && !OPTIONAL_VIDEO_PARAMS[param](videoBidderParams[param])) { delete videoBidderParams[param]; } } @@ -205,7 +214,7 @@ function buildImpression(bidRequest) { } else { const sizes = deepAccess(bidRequest, 'mediaTypes.banner.format'); if (sizes && sizes.length > 0) { - const {w: width, h: height} = sizes[0]; + const { w: width, h: height } = sizes[0]; _size = [width, height]; } } @@ -405,7 +414,7 @@ function buildRequest(validBidRequests, bidderRequest) { if (params) { req.ext = { - insticator: {...req.ext.insticator, ...params}, + insticator: { ...req.ext.insticator, ...params }, }; } @@ -442,8 +451,9 @@ function buildRequest(validBidRequests, bidderRequest) { return req; } -function buildBid(bid, bidderRequest) { +function buildBid(bid, bidderRequest, seatbid) { const originalBid = ((bidderRequest.bids) || []).find((b) => b.bidId === bid.impid); + let meta = {} if (bid.ext && bid.ext.meta) { @@ -454,27 +464,79 @@ function buildBid(bid, bidderRequest) { meta.advertiserDomains = bid.adomain } + // ORTB 2.6: Add category support + if (bid.cat && Array.isArray(bid.cat) && bid.cat.length > 0) { + meta.primaryCatId = bid.cat[0]; + if (bid.cat.length > 1) { + meta.secondaryCatIds = bid.cat.slice(1); + } + } + + // ORTB 2.6: Add seat from seatbid + if (seatbid && seatbid.seat) { + meta.seat = seatbid.seat; + } + + // ORTB 2.6: Add creative attributes + if (bid.attr && Array.isArray(bid.attr)) { + meta.attr = bid.attr; + } + + // Determine media type using multiple signals let mediaType = 'banner'; - if (bid.adm && bid.adm.includes(' 0 ? Math.min(bid.exp, configTTL) : configTTL; + const bidResponse = { requestId: bid.impid, creativeId: bid.crid, cpm: bid.price, currency: 'USD', netRevenue: true, - ttl: bid.exp || config.getConfig('insticator.bidTTL') || BID_TTL, + ttl: ttl, width: bid.w, height: bid.h, mediaType: mediaType, ad: bid.adm, - adUnitCode: originalBid.adUnitCode, - ...(Object.keys(meta).length > 0 ? {meta} : {}) + adUnitCode: originalBid?.adUnitCode, + ...(Object.keys(meta).length > 0 ? { meta } : {}) }; + // ORTB 2.6: Add deal ID + if (bid.dealid) { + bidResponse.dealId = bid.dealid; + } + + // ORTB 2.6: Add billing URL for billing notification + if (bid.burl) { + bidResponse.burl = bid.burl; + } + + // ORTB 2.6: Add notice URL for win notification + if (bid.nurl) { + bidResponse.nurl = bid.nurl; + } + if (mediaType === 'video') { bidResponse.vastXml = bid.adm; + + // ORTB 2.6: Add video duration for adpod support + if (bid.dur && isInteger(bid.dur) && bid.dur > 0) { + bidResponse.video = bidResponse.video || {}; + bidResponse.video.durationSeconds = bid.dur; + } } // Inticator bid adaptor only returns `vastXml` for video bids. No VastUrl or videoCache. @@ -493,7 +555,7 @@ function buildBid(bid, bidderRequest) { } function buildBidSet(seatbid, bidderRequest) { - return seatbid.bid.map((bid) => buildBid(bid, bidderRequest)); + return seatbid.bid.map((bid) => buildBid(bid, bidderRequest, seatbid)); } function validateSize(size) { @@ -567,7 +629,7 @@ function validateVideo(bid) { const playerSize = deepAccess(bid, 'mediaTypes.video.playerSize'); if (!h && !w && playerSize) { - ({w, h} = parsePlayerSizeToWidthHeight(playerSize, w, h)); + ({ w, h } = parsePlayerSizeToWidthHeight(playerSize, w, h)); } const videoSize = [w, h]; @@ -631,7 +693,7 @@ function parsePlayerSizeToWidthHeight(playerSize, w, h) { export const spec = { code: BIDDER_CODE, gvlid: GVLID, - supportedMediaTypes: [ BANNER, VIDEO ], + supportedMediaTypes: [BANNER, VIDEO], isBidRequestValid: function (bid) { return ( @@ -652,9 +714,19 @@ export const spec = { if (deepAccess(validBidRequests[0], 'params.bid_endpoint_request_url')) { endpointUrl = deepAccess(validBidRequests[0], 'params.bid_endpoint_request_url').replace(/^http:/, 'https:'); } + + // Add publisherId as query parameter if present and non-empty + const publisherId = deepAccess(validBidRequests[0], 'params.publisherId'); + if (publisherId && publisherId.trim() !== '') { + const urlObj = new URL(endpointUrl); + urlObj.searchParams.set('publisherId', publisherId); + endpointUrl = urlObj.toString(); + } } if (validBidRequests.length > 0) { + const ortbRequest = buildRequest(validBidRequests, bidderRequest); + requests.push({ method: 'POST', url: endpointUrl, @@ -662,7 +734,7 @@ export const spec = { contentType: 'application/json', withCredentials: true, }, - data: JSON.stringify(buildRequest(validBidRequests, bidderRequest)), + data: JSON.stringify(ortbRequest), bidderRequest, }); } @@ -673,11 +745,22 @@ export const spec = { interpretResponse: function (serverResponse, request) { const bidderRequest = request.bidderRequest; const body = serverResponse.body; - if (!body || body.id !== bidderRequest.bidderRequestId) { - logError('insticator: response id does not match bidderRequestId'); + + // Handle 204 No Content or empty response body (valid "no bid" scenario) + if (!body || !body.id) { return []; } + // Validate response ID matches request ID + if (body.id !== bidderRequest.bidderRequestId) { + logError('insticator: response id does not match bidderRequestId', { + responseId: body.id, + bidderRequestId: bidderRequest.bidderRequestId + }); + return []; + } + + // No seatbid means no bids (valid scenario) if (!body.seatbid) { return []; } @@ -686,7 +769,9 @@ export const spec = { buildBidSet(seatbid, bidderRequest) ); - return bidsets.reduce((a, b) => a.concat(b), []); + const finalBids = bidsets.reduce((a, b) => a.concat(b), []); + + return finalBids; }, getUserSyncs: function (options, responses) { diff --git a/modules/instreamTracking.js b/modules/instreamTracking.js index 909c21b29bd..b63bb860d23 100644 --- a/modules/instreamTracking.js +++ b/modules/instreamTracking.js @@ -48,7 +48,7 @@ const whitelistedResources = /video|fetch|xmlhttprequest|other/; * * @return {boolean} returns TRUE if tracking started */ -export function trackInstreamDeliveredImpressions({adUnits, bidsReceived, bidderRequests}) { +export function trackInstreamDeliveredImpressions({ adUnits, bidsReceived, bidderRequests }) { const instreamTrackingConfig = config.getConfig('instreamTracking') || {}; // check if instreamTracking is enabled and performance api is available if (!instreamTrackingConfig.enabled || !window.performance || !window.performance.getEntriesByType) { @@ -74,7 +74,7 @@ export function trackInstreamDeliveredImpressions({adUnits, bidsReceived, bidder const instreamAdUnitsCount = Object.keys(instreamAdUnitMap).length; const start = Date.now(); - const {maxWindow, pollingFreq, urlPattern} = instreamTrackingConfig; + const { maxWindow, pollingFreq, urlPattern } = instreamTrackingConfig; let instreamWinningBidsCount = 0; let lastRead = 0; // offset for performance.getEntriesByType diff --git a/modules/insuradsBidAdapter.md b/modules/insuradsBidAdapter.md new file mode 100644 index 00000000000..11c0bc248e7 --- /dev/null +++ b/modules/insuradsBidAdapter.md @@ -0,0 +1,55 @@ +# Overview + +``` +Module Name: InsurAds Bid Adapter +Module Type: Bidder Adapter +Maintainer: jclimaco@insurads.com +``` + +# Description + +Connects to InsurAds network for bids. + +# Test Parameters + +## Web + +### Display +``` +var adUnits = [ + // Banner adUnit + { + code: 'banner-div', + mediaTypes: { + banner: { + sizes: [[300, 250], [300,600]] + } + }, + bids: [{ + bidder: 'insurads', + params: { + tagId: 'ToBeSupplied' + } + }] + }, +]; +``` + +### Video Instream +``` + var videoAdUnit = { + code: 'video1', + mediaTypes: { + video: { + playerSize: [640, 480], + context: 'instream' + } + }, + bids: [{ + bidder: 'insurads', + params: { + tagId: 'ToBeSupplied' + } + }] + }; +``` diff --git a/modules/insuradsBidAdapter.ts b/modules/insuradsBidAdapter.ts new file mode 100644 index 00000000000..30f506118b7 --- /dev/null +++ b/modules/insuradsBidAdapter.ts @@ -0,0 +1,149 @@ +import { deepSetValue, generateUUID, logError } from '../src/utils.js'; +import { getStorageManager } from '../src/storageManager.js'; +import { AdapterRequest, BidderSpec, registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; +import { ortbConverter } from '../libraries/ortbConverter/converter.js' + +import { interpretResponse, enrichImp, enrichRequest, getAmxId, getLocalStorageFunctionGenerator, getUserSyncs } from '../libraries/nexx360Utils/index.js'; +import { getBoundingClientRect } from '../libraries/boundingClientRect/boundingClientRect.js'; +import { BidRequest, ClientBidderRequest } from '../src/adapterManager.js'; +import { ORTBImp, ORTBRequest } from '../src/prebid.public.js'; +import { config } from '../src/config.js'; + +const BIDDER_CODE = 'insurads'; +const REQUEST_URL = 'https://fast.nexx360.io/booster'; +const PAGE_VIEW_ID = generateUUID(); +const BIDDER_VERSION = '7.1'; +const GVLID = 596; +const ALT_KEY = 'nexx360_storage'; + +const DEFAULT_GZIP_ENABLED = false; + +type RequireAtLeastOne = + Omit & { + [K in Keys]-?: Required> & + Partial>> + }[Keys]; + +type InsurAdsBidParams = RequireAtLeastOne<{ + tagId?: string; + placement?: string; + videoTagId?: string; + nativeTagId?: string; + adUnitPath?: string; + adUnitName?: string; + divId?: string; + allBids?: boolean; + customId?: string; + bidders?: Record; +}, "tagId" | "placement">; + +declare module '../src/adUnits' { + interface BidderParams { + ['nexx360']: InsurAdsBidParams; + } +} + +export const STORAGE = getStorageManager({ + bidderCode: BIDDER_CODE, +}); + +export const getInsurAdsLocalStorage = getLocalStorageFunctionGenerator<{ nexx360Id: string }>( + STORAGE, + BIDDER_CODE, + ALT_KEY, + 'nexx360Id' +); + +export const getGzipSetting = (): boolean => { + const bidderConfig = config.getBidderConfig(); + const gzipEnabled = bidderConfig.insurads?.gzipEnabled; + + if (gzipEnabled === true || gzipEnabled === 'true') { + return true; + } + return DEFAULT_GZIP_ENABLED; +} + +const converter = ortbConverter({ + context: { + netRevenue: true, // or false if your adapter should set bidResponse.netRevenue = false + ttl: 90, // default bidResponse.ttl (when not specified in ORTB response.seatbid[].bid[].exp) + }, + imp(buildImp, bidRequest: BidRequest, context) { + let imp: ORTBImp = buildImp(bidRequest, context); + imp = enrichImp(imp, bidRequest); + const divId = bidRequest.params.divId || bidRequest.adUnitCode; + const slotEl: HTMLElement | null = typeof divId === 'string' ? document.getElementById(divId) : null; + if (slotEl) { + const { width, height } = getBoundingClientRect(slotEl); + deepSetValue(imp, 'ext.dimensions.slotW', width); + deepSetValue(imp, 'ext.dimensions.slotH', height); + deepSetValue(imp, 'ext.dimensions.cssMaxW', slotEl.style?.maxWidth); + deepSetValue(imp, 'ext.dimensions.cssMaxH', slotEl.style?.maxHeight); + } + deepSetValue(imp, 'ext.nexx360', bidRequest.params); + deepSetValue(imp, 'ext.nexx360.divId', divId); + if (bidRequest.params.adUnitPath) deepSetValue(imp, 'ext.adUnitPath', bidRequest.params.adUnitPath); + if (bidRequest.params.adUnitName) deepSetValue(imp, 'ext.adUnitName', bidRequest.params.adUnitName); + return imp; + }, + request(buildRequest, imps, bidderRequest, context) { + let request: ORTBRequest = buildRequest(imps, bidderRequest, context); + const amxId = getAmxId(STORAGE, BIDDER_CODE); + request = enrichRequest(request, amxId, PAGE_VIEW_ID, BIDDER_VERSION); + return request; + }, +}); + +const isBidRequestValid = (bid: BidRequest): boolean => { + if (bid.params.adUnitName && (typeof bid.params.adUnitName !== 'string' || bid.params.adUnitName === '')) { + logError('bid.params.adUnitName needs to be a string'); + return false; + } + if (bid.params.adUnitPath && (typeof bid.params.adUnitPath !== 'string' || bid.params.adUnitPath === '')) { + logError('bid.params.adUnitPath needs to be a string'); + return false; + } + if (bid.params.divId && (typeof bid.params.divId !== 'string' || bid.params.divId === '')) { + logError('bid.params.divId needs to be a string'); + return false; + } + if (bid.params.allBids && typeof bid.params.allBids !== 'boolean') { + logError('bid.params.allBids needs to be a boolean'); + return false; + } + if (!bid.params.tagId && !bid.params.videoTagId && !bid.params.nativeTagId && !bid.params.placement) { + logError('bid.params.tagId or bid.params.videoTagId or bid.params.nativeTagId or bid.params.placement must be defined'); + return false; + } + return true; +}; + +const buildRequests = ( + bidRequests: BidRequest[], + bidderRequest: ClientBidderRequest, +): AdapterRequest => { + const data: ORTBRequest = converter.toORTB({ bidRequests, bidderRequest }) + const adapterRequest: AdapterRequest = { + method: 'POST', + url: REQUEST_URL, + data, + options: { + endpointCompression: getGzipSetting() + }, + } + return adapterRequest; +} + +export const spec: BidderSpec = { + code: BIDDER_CODE, + gvlid: GVLID, + supportedMediaTypes: [BANNER, VIDEO, NATIVE], + isBidRequestValid, + buildRequests, + interpretResponse, + getUserSyncs, +}; + +registerBidder(spec); diff --git a/modules/integr8BidAdapter.js b/modules/integr8BidAdapter.js index 4505bd98147..0f521cd94f7 100644 --- a/modules/integr8BidAdapter.js +++ b/modules/integr8BidAdapter.js @@ -17,7 +17,7 @@ const SIZE_SEPARATOR = ';'; const BISKO_ID = 'integr8Id'; const STORAGE_ID = 'bisko-sid'; const SEGMENTS = 'integr8Segments'; -const storage = getStorageManager({bidderCode: BIDDER_CODE}); +const storage = getStorageManager({ bidderCode: BIDDER_CODE }); export const spec = { code: BIDDER_CODE, diff --git a/modules/intentIqAnalyticsAdapter.js b/modules/intentIqAnalyticsAdapter.js index 946a13ae174..3345f9d6ecd 100644 --- a/modules/intentIqAnalyticsAdapter.js +++ b/modules/intentIqAnalyticsAdapter.js @@ -5,8 +5,9 @@ import { ajax } from '../src/ajax.js'; import { EVENTS } from '../src/constants.js'; import { detectBrowser } from '../libraries/intentIqUtils/detectBrowserUtils.js'; import { appendSPData } from '../libraries/intentIqUtils/urlUtils.js'; -import { appendVrrefAndFui, getReferrer } from '../libraries/intentIqUtils/getRefferer.js'; +import { appendVrrefAndFui, getCurrentUrl, getRelevantRefferer } from '../libraries/intentIqUtils/getRefferer.js'; import { getCmpData } from '../libraries/intentIqUtils/getCmpData.js'; +import { getUnitPosition } from '../libraries/intentIqUtils/getUnitPosition.js'; import { VERSION, PREBID, @@ -16,10 +17,12 @@ import { reportingServerAddress } from '../libraries/intentIqUtils/intentIqConfi import { handleAdditionalParams } from '../libraries/intentIqUtils/handleAdditionalParams.js'; import { gamPredictionReport } from '../libraries/intentIqUtils/gamPredictionReport.js'; import { defineABTestingGroup } from '../libraries/intentIqUtils/defineABTestingGroupUtils.js'; +import { getGlobal } from '../src/prebidGlobal.js'; const MODULE_NAME = 'iiqAnalytics'; const analyticsType = 'endpoint'; const prebidVersion = '$prebid.version$'; +const pbjs = getGlobal(); export const REPORTER_ID = Date.now() + '_' + getRandom(0, 1000); let globalName; let identityGlobalName; @@ -70,10 +73,7 @@ const PARAMS_NAMES = { const DEFAULT_URL = 'https://reports.intentiq.com/report'; const getDataForDefineURL = () => { - const cmpData = getCmpData(); - const gdprDetected = cmpData.gdprString; - - return [iiqAnalyticsAnalyticsAdapter.initOptions.reportingServerAddress, gdprDetected]; + return [iiqAnalyticsAnalyticsAdapter.initOptions.reportingServerAddress, iiqAnalyticsAnalyticsAdapter.initOptions.region]; }; const getDefaultInitOptions = () => { @@ -92,7 +92,8 @@ const getDefaultInitOptions = () => { abPercentage: null, abTestUuid: null, additionalParams: null, - reportingServerAddress: '' + reportingServerAddress: '', + region: '' } } @@ -123,12 +124,13 @@ function initAdapterConfig(config) { const options = config?.options || {} iiqConfig = options - const { manualWinReportEnabled, gamPredictReporting, reportMethod, reportingServerAddress, adUnitConfig, partner, ABTestingConfigurationSource, browserBlackList, domainName, additionalParams } = options + const { manualWinReportEnabled, gamPredictReporting, reportMethod, reportingServerAddress, region, adUnitConfig, partner, ABTestingConfigurationSource, browserBlackList, domainName, additionalParams } = options iiqAnalyticsAnalyticsAdapter.initOptions.manualWinReportEnabled = manualWinReportEnabled || false; iiqAnalyticsAnalyticsAdapter.initOptions.reportMethod = parseReportingMethod(reportMethod); iiqAnalyticsAnalyticsAdapter.initOptions.gamPredictReporting = typeof gamPredictReporting === 'boolean' ? gamPredictReporting : false; iiqAnalyticsAnalyticsAdapter.initOptions.reportingServerAddress = typeof reportingServerAddress === 'string' ? reportingServerAddress : ''; + iiqAnalyticsAnalyticsAdapter.initOptions.region = typeof region === 'string' ? region : ''; iiqAnalyticsAnalyticsAdapter.initOptions.adUnitConfig = typeof adUnitConfig === 'number' ? adUnitConfig : 1; iiqAnalyticsAnalyticsAdapter.initOptions.configSource = ABTestingConfigurationSource; iiqAnalyticsAnalyticsAdapter.initOptions.currentGroup = defineABTestingGroup(options); @@ -155,7 +157,7 @@ function receivePartnerData() { return false } iiqAnalyticsAnalyticsAdapter.initOptions.fpid = FPD - const { partnerData, clientsHints = '', actualABGroup } = window[identityGlobalName] + const { partnerData, clientHints = '', actualABGroup } = window[identityGlobalName] if (partnerData) { iiqAnalyticsAnalyticsAdapter.initOptions.dataIdsInitialized = true; @@ -172,7 +174,7 @@ function receivePartnerData() { if (actualABGroup) { iiqAnalyticsAnalyticsAdapter.initOptions.currentGroup = actualABGroup; } - iiqAnalyticsAnalyticsAdapter.initOptions.clientsHints = clientsHints; + iiqAnalyticsAnalyticsAdapter.initOptions.clientHints = clientHints; } catch (e) { logError(e); return false; @@ -268,9 +270,10 @@ function getRandom(start, end) { export function preparePayload(data) { const result = getDefaultDataObject(); + const fullUrl = getCurrentUrl(); result[PARAMS_NAMES.partnerId] = iiqAnalyticsAnalyticsAdapter.initOptions.partner; result[PARAMS_NAMES.prebidVersion] = prebidVersion; - result[PARAMS_NAMES.referrer] = getReferrer(); + result[PARAMS_NAMES.referrer] = getRelevantRefferer(iiqAnalyticsAnalyticsAdapter.initOptions.domainName, fullUrl); result[PARAMS_NAMES.terminationCause] = iiqAnalyticsAnalyticsAdapter.initOptions.terminationCause; result[PARAMS_NAMES.clientType] = iiqAnalyticsAnalyticsAdapter.initOptions.clientType; result[PARAMS_NAMES.siteId] = iiqAnalyticsAnalyticsAdapter.initOptions.siteId; @@ -327,30 +330,32 @@ function fillEidsData(result) { function prepareData(data, result) { const adTypeValue = data.adType || data.mediaType; - if (data.bidderCode) { - result.bidderCode = data.bidderCode; - } - if (data.cpm) { - result.cpm = data.cpm; - } - if (data.currency) { - result.currency = data.currency; + if (data.bidderCode) result.bidderCode = data.bidderCode; + if (data.cpm) result.cpm = data.cpm; + if (data.currency) result.currency = data.currency; + if (data.originalCpm) result.originalCpm = data.originalCpm; + if (data.originalCurrency) result.originalCurrency = data.originalCurrency; + if (data.status) result.status = data.status; + if (data.size) result.size = data.size; + if (typeof data.pos === 'number') { + result.pos = data.pos; + } else if (data.adUnitCode) { + const pos = getUnitPosition(pbjs, data.adUnitCode); + if (typeof pos === 'number') result.pos = pos; } - if (data.originalCpm) { - result.originalCpm = data.originalCpm; + if (data.size) { + result.size = data.size; } - if (data.originalCurrency) { - result.originalCurrency = data.originalCurrency; - } - if (data.status) { - result.status = data.status; + if (typeof data.pos === 'number') { + result.pos = data.pos; + } else if (data.adUnitCode) { + const pos = getUnitPosition(pbjs, data.adUnitCode); + if (typeof pos === 'number') result.pos = pos; } result.prebidAuctionId = data.auctionId || data.prebidAuctionId; - if (adTypeValue) { - result[PARAMS_NAMES.adType] = adTypeValue; - } + if (adTypeValue) result[PARAMS_NAMES.adType] = adTypeValue; switch (iiqAnalyticsAnalyticsAdapter.initOptions.adUnitConfig) { case 1: @@ -375,7 +380,8 @@ function prepareData(data, result) { } result.biddingPlatformId = data.biddingPlatformId || 1; - result.partnerAuctionId = 'BW'; + + if (data?.partnerAuctionId) result.partnerAuctionId = data.partnerAuctionId; } function extractPlacementId(data) { @@ -396,7 +402,6 @@ function getDefaultDataObject() { return { inbbl: false, pbjsver: prebidVersion, - partnerAuctionId: 'BW', reportSource: 'pbjs', jsversion: VERSION, partnerId: -1, @@ -410,7 +415,9 @@ function getDefaultDataObject() { function constructFullUrl(data) { const report = []; const reportMethod = iiqAnalyticsAnalyticsAdapter.initOptions.reportMethod; + const partnerData = window[identityGlobalName]?.partnerData; const currentBrowserLowerCase = detectBrowser(); + const partnerAuctionId = data?.partnerAuctionId; data = btoa(JSON.stringify(data)); report.push(data); @@ -420,8 +427,13 @@ function constructFullUrl(data) { let url = baseUrl + '?pid=' + - iiqAnalyticsAnalyticsAdapter.initOptions.partner + - '&mct=1' + + iiqAnalyticsAnalyticsAdapter.initOptions.partner; + if (partnerAuctionId) { + url += + '&paucid=' + + encodeURIComponent(JSON.stringify([partnerAuctionId])); + } + url += '&mct=1' + (iiqAnalyticsAnalyticsAdapter.initOptions?.fpid ? '&iiqid=' + encodeURIComponent(iiqAnalyticsAnalyticsAdapter.initOptions.fpid.pcid) : '') + @@ -432,24 +444,29 @@ function constructFullUrl(data) { '&source=' + PREBID + '&uh=' + - encodeURIComponent(iiqAnalyticsAnalyticsAdapter.initOptions.clientsHints) + + encodeURIComponent(iiqAnalyticsAnalyticsAdapter.initOptions.clientHints) + (cmpData.uspString ? '&us_privacy=' + encodeURIComponent(cmpData.uspString) : '') + (cmpData.gppString ? '&gpp=' + encodeURIComponent(cmpData.gppString) : '') + (cmpData.gdprString ? '&gdpr_consent=' + encodeURIComponent(cmpData.gdprString) + '&gdpr=1' : '&gdpr=0'); - url = appendSPData(url, iiqAnalyticsAnalyticsAdapter.initOptions.fpid); + + url = appendSPData(url, partnerData); url = appendVrrefAndFui(url, iiqAnalyticsAnalyticsAdapter.initOptions.domainName); - if (reportMethod === 'POST') { - return { url, method: 'POST', payload: JSON.stringify(report) }; + if (reportMethod !== 'POST') { + url += '&payload=' + encodeURIComponent(JSON.stringify(report)); } - url += '&payload=' + encodeURIComponent(JSON.stringify(report)); + url = handleAdditionalParams( currentBrowserLowerCase, url, 2, iiqAnalyticsAnalyticsAdapter.initOptions.additionalParams ); - return { url, method: 'GET' }; + + if (reportMethod === 'POST') { + return { url, method: 'POST', payload: JSON.stringify(report) }; + } + return { url }; } iiqAnalyticsAnalyticsAdapter.originEnableAnalytics = iiqAnalyticsAnalyticsAdapter.enableAnalytics; diff --git a/modules/intentIqAnalyticsAdapter.md b/modules/intentIqAnalyticsAdapter.md index 42f6167c33b..ee1667e62ae 100644 --- a/modules/intentIqAnalyticsAdapter.md +++ b/modules/intentIqAnalyticsAdapter.md @@ -21,36 +21,33 @@ No registration for this module is required. {: .table .table-bordered .table-striped } | Parameter | Scope | Type | Description | Example | | --- | --- | --- | --- | --- | -| options.partner| Required | Number | This is the partner ID value obtained from registering with IntentIQ. | `1177538` | +| options.partner| Required | Number | This is the partner ID value obtained from registering with IntentIQ. | `1177538` | | options.manualWinReportEnabled | Optional | Boolean | This variable determines whether the bidWon event is triggered automatically. If set to false, the event will occur automatically, and manual reporting with reportExternalWin will be disabled. If set to true, the event will not occur automatically, allowing manual reporting through reportExternalWin. The default value is false. | `false` | | options.reportMethod | Optional | String | Defines the HTTP method used to send the analytics report. If set to `"POST"`, the report payload will be sent in the body of the request. If set to `"GET"` (default), the payload will be included as a query parameter in the request URL. | `"GET"` | | options.reportingServerAddress | Optional | String | The base URL for the IntentIQ reporting server. If parameter is provided in `configParams`, it will be used. | `"https://domain.com"` | -| options.adUnitConfig | Optional | Number | Determines how the `placementId` parameter is extracted in the report (default is 1). Possible values: 1 – adUnitCode first, 2 – placementId first, 3 – only adUnitCode, 4 – only placementId. | `1` | -| options.gamPredictReporting | Optional | Boolean | This variable controls whether the GAM prediction logic is enabled or disabled. The main purpose of this logic is to extract information from a rendered GAM slot when no Prebid bidWon event is available. In that case, we take the highest CPM from the current auction and add 0.01 to that value. | `false` | +| options.adUnitConfig | Optional | Number | Determines how the `placementId` parameter is extracted in the report (default is 1). Possible values: 1 – adUnitCode first, 2 – placementId first, 3 – only adUnitCode, 4 – only placementId. | `1` | +| options.gamPredictReporting | Optional | Boolean | This variable controls whether the GAM prediction logic is enabled or disabled. The main purpose of this logic is to extract information from a rendered GAM slot when no Prebid bidWon event is available. In that case, we take the highest CPM from the current auction and add 0.01 to that value. | `false` | | options.ABTestingConfigurationSource | Optional | String | Determines how AB group will be defined. Possible values: `"IIQServer"` – group defined by IIQ server, `"percentage"` – generated group based on abPercentage, `"group"` – define group based on value provided by partner. | `IIQServer` | | options.abPercentage | Optional | Number | Percentage for A/B testing group. Default value is `95` | `95` | | options.group | Optional | String | Define group provided by partner, possible values: `"A"`, `"B"` | `"A"` | -| options.gamObjectReference | Optional | Object | This is a reference to the Google Ad Manager (GAM) object, which will be used to set targeting. If this parameter is not provided, the group reporting will not be configured.| `googletag`| -| options.browserBlackList | Optional | String | This is the name of a browser that can be added to a blacklist.| `"chrome"`| -| options.domainName | Optional | String | Specifies the domain of the page in which the IntentIQ object is currently running and serving the impression. This domain will be used later in the revenue reporting breakdown by domain. For example, cnn.com. It identifies the primary source of requests to the IntentIQ servers, even within nested web pages.| `"currentDomain.com"`| +| options.gamObjectReference | Optional | Object | This is a reference to the Google Ad Manager (GAM) object, which will be used to set targeting. If this parameter is not provided, the group reporting will not be configured.| `googletag`| +| options.browserBlackList | Optional | String | This is the name of a browser that can be added to a blacklist.| `"chrome"`| +| options.domainName | Optional | String | Specifies the domain of the page in which the IntentIQ object is currently running and serving the impression. This domain will be used later in the revenue reporting breakdown by domain. For example, cnn.com. It identifies the primary source of requests to the IntentIQ servers, even within nested web pages.| `"currentDomain.com"`| | options. additionalParams | Optional | Array | This parameter allows sending additional custom key-value parameters with specific destination logic (sync, VR, winreport). Each custom parameter is defined as an object in the array. | `[ { parameterName: “abc”, parameterValue: 123, destination: [1,1,0] } ]` | -| options. additionalParams[0].parameterName | Required | String | Name of the custom parameter. This will be sent as a query parameter. | `"abc"` | -| options. additionalParams[0].parameterValue | Required | String / Number | Value to assign to the parameter. | `123` | -| options. additionalParams[0].destination | Required | Array | Array of numbers either `1` or `0`. Controls where this parameter is sent `[sendWithSync, sendWithVr, winreport]`. | `[1, 0, 0]` | +| options. additionalParams[0].parameterName | Optional | String | Name of the custom parameter. This will be sent as a query parameter. | `"abc"` | +| options. additionalParams[0].parameterValue | Optional | String / Number | Value to assign to the parameter. | `123` | +| options. additionalParams[0].destination | Optional | Array | Array of numbers either `1` or `0`. Controls where this parameter is sent `[sendWithSync, sendWithVr, winreport]`. | `[1, 0, 0]` | #### Example Configuration ```js pbjs.enableAnalytics({ - provider: 'iiqAnalytics', - options: { - partner: 1177538, - manualWinReportEnabled: false, - reportMethod: "GET", - adUnitConfig: 1, - domainName: "currentDomain.com", - gamPredictReporting: false - } + provider: "iiqAnalytics", + options: { + partner: 1177538, + ABTestingConfigurationSource: "IIQServer", + domainName: "currentDomain.com", + }, }); ``` @@ -65,13 +62,13 @@ To enable this manual reporting functionality, you must set the manualWinReportE To call the reportExternalWin function, you need to pass the partner_id parameter as shown in the example below: ```js -window.intentIqAnalyticsAdapter_[partner_id].reportExternalWin(reportData) +window.intentIqAnalyticsAdapter_[partner_id].reportExternalWin(reportData); ``` Example use with Partner ID = 123455 ```js -window.intentIqAnalyticsAdapter_123455.reportExternalWin(reportData) +window.intentIqAnalyticsAdapter_123455.reportExternalWin(reportData); ``` ### Function Parameters @@ -90,27 +87,29 @@ originalCpm: 1.5, // Original CPM value. originalCurrency: 'USD', // Original currency. status: 'rendered', // Auction status, e.g., 'rendered'. placementId: 'div-1' // ID of the ad placement. -adType: 'banner' // Specifies the type of ad served +adType: 'banner', // Specifies the type of ad served, +size: '320x250', // Size of adUnit item, +pos: 0 // The following values are defined in the ORTB 2.5 spec } ``` {: .table .table-bordered .table-striped } -| Field | Data Type | Description | Example | Mandatory | +| Field | Data Type | Description | Example | Mandatory | |--------------------|-----------|--------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------|-----------| -| biddingPlatformId | Integer | Specify the platform in which this ad impression was rendered – 1 – Prebid, 2 – Amazon, 3 – Google, 4 – Open RTB (including your local Prebid server) | 1 | Yes | -| partnerAuctionId | String | Use this when you are running multiple auction solutions across your assets and have a unified identifier for auctions | 3d44542d-xx-4662-xxxx-4xxxx3d8e | No | -| bidderCode | String | Specifies the name of the bidder that won the auction as reported by Prebid and all other bidding platforms | newAppnexus | Yes | -| prebidAuctionId | String | Specifies the identifier of the Prebid auction. Leave empty or undefined if Prebid is not the bidding platform | | | -| cpm | Decimal | Cost per mille of the impression as received from the demand-side auction (without modifications or reductions) | 5.62 | Yes | -| currency | String | Currency of the auction | USD | Yes | -| originalCpm | Decimal | Leave empty or undefined if Prebid is not the bidding platform | 5.5 | No | -| originalCurrency | String | Currency of the original auction | USD | No | -| status | String | Status of the impression. Leave empty or undefined if Prebid is not the bidding platform | rendered | No | -| placementId | String | Unique identifier of the ad unit on the webpage that showed this ad | div-1 | No | -| adType | String | Specifies the type of ad served. Possible values: “banner“, “video“, “native“, “audio“. | banner | No | +| biddingPlatformId | Integer | Specify the platform in which this ad impression was rendered – 1 – Prebid, 2 – Amazon, 3 – Google, 4 – Open RTB (including your local Prebid server) | 1 | Yes | +| partnerAuctionId | String | Use this when you are running multiple auction solutions across your assets and have a unified identifier for auctions | 3d44542d-xx-4662-xxxx-4xxxx3d8e | No | +| bidderCode | String | Specifies the name of the bidder that won the auction as reported by Prebid and all other bidding platforms | newAppnexus | Yes | +| prebidAuctionId | String | Specifies the identifier of the Prebid auction. Leave empty or undefined if Prebid is not the bidding platform | 3513ce01-de02-490b-9d87-bfc137697f82 | No | +| cpm | Decimal | Cost per mille of the impression as received from the demand-side auction (without modifications or reductions) | 5.62 | Yes | +| currency | String | Currency of the auction | USD | Yes | +| originalCpm | Decimal | Leave empty or undefined if Prebid is not the bidding platform | 5.5 | No | +| originalCurrency | String | Currency of the original auction | USD | No | +| status | String | Status of the impression. Leave empty or undefined if Prebid is not the bidding platform | rendered | No | +| placementId | String | Unique identifier of the ad unit on the webpage that showed this ad | div-1 | No | +| adType | String | Specifies the type of ad served. Possible values: “banner“, “video“, “native“, “audio“. | banner | No | To report the auction win, call the function as follows: ```js -window.intentIqAnalyticsAdapter_[partner_id].reportExternalWin(reportData) +window.intentIqAnalyticsAdapter_[partner_id].reportExternalWin(reportData); ``` diff --git a/modules/intentIqIdSystem.js b/modules/intentIqIdSystem.js index e10c19f1888..c064719ae88 100644 --- a/modules/intentIqIdSystem.js +++ b/modules/intentIqIdSystem.js @@ -5,25 +5,25 @@ * @requires module:modules/userId */ -import {logError, isPlainObject, isStr, isNumber} from '../src/utils.js'; -import {ajax} from '../src/ajax.js'; -import {submodule} from '../src/hook.js' -import {detectBrowser} from '../libraries/intentIqUtils/detectBrowserUtils.js'; -import {appendSPData} from '../libraries/intentIqUtils/urlUtils.js'; +import { logError, isPlainObject, isStr, isNumber } from '../src/utils.js'; +import { ajax } from '../src/ajax.js'; +import { submodule } from '../src/hook.js' +import { detectBrowser } from '../libraries/intentIqUtils/detectBrowserUtils.js'; +import { appendSPData } from '../libraries/intentIqUtils/urlUtils.js'; import { isCHSupported } from '../libraries/intentIqUtils/chUtils.js' -import {appendVrrefAndFui} from '../libraries/intentIqUtils/getRefferer.js'; +import { appendVrrefAndFui } from '../libraries/intentIqUtils/getRefferer.js'; import { getCmpData } from '../libraries/intentIqUtils/getCmpData.js'; -import {readData, storeData, defineStorageType, removeDataByKey, tryParse} from '../libraries/intentIqUtils/storageUtils.js'; +import { readData, storeData, defineStorageType, removeDataByKey, tryParse } from '../libraries/intentIqUtils/storageUtils.js'; import { FIRST_PARTY_KEY, CLIENT_HINTS_KEY, EMPTY, GVLID, VERSION, INVALID_ID, SYNC_REFRESH_MILL, META_DATA_CONSTANT, PREBID, - HOURS_24, CH_KEYS + HOURS_72, CH_KEYS } from '../libraries/intentIqConstants/intentIqConstants.js'; -import {SYNC_KEY} from '../libraries/intentIqUtils/getSyncKey.js'; -import {iiqPixelServerAddress, iiqServerAddress} from '../libraries/intentIqUtils/intentIqConfig.js'; +import { SYNC_KEY } from '../libraries/intentIqUtils/getSyncKey.js'; +import { iiqPixelServerAddress, getIiqServerAddress } from '../libraries/intentIqUtils/intentIqConfig.js'; import { handleAdditionalParams } from '../libraries/intentIqUtils/handleAdditionalParams.js'; import { decryptData, encryptData } from '../libraries/intentIqUtils/cryptionUtils.js'; import { defineABTestingGroup } from '../libraries/intentIqUtils/defineABTestingGroupUtils.js'; @@ -81,7 +81,7 @@ function addUniquenessToUrl(url) { return url; } -function appendFirstPartyData (url, firstPartyData, partnerData) { +function appendFirstPartyData(url, firstPartyData, partnerData) { url += firstPartyData.pid ? '&pid=' + encodeURIComponent(firstPartyData.pid) : ''; url += firstPartyData.pcid ? '&iiqidtype=2&iiqpcid=' + encodeURIComponent(firstPartyData.pcid) : ''; url += firstPartyData.pcidDate ? '&iiqpciddate=' + encodeURIComponent(firstPartyData.pcidDate) : ''; @@ -93,7 +93,7 @@ function verifyIdType(value) { return -1; } -function appendPartnersFirstParty (url, configParams) { +function appendPartnersFirstParty(url, configParams) { const partnerClientId = typeof configParams.partnerClientId === 'string' ? encodeURIComponent(configParams.partnerClientId) : ''; const partnerClientIdType = typeof configParams.partnerClientIdType === 'number' ? verifyIdType(configParams.partnerClientIdType) : -1; @@ -105,7 +105,7 @@ function appendPartnersFirstParty (url, configParams) { return url; } -function appendCMPData (url, cmpData) { +function appendCMPData(url, cmpData) { url += cmpData.uspString ? '&us_privacy=' + encodeURIComponent(cmpData.uspString) : ''; url += cmpData.gppString ? '&gpp=' + encodeURIComponent(cmpData.gppString) : ''; url += cmpData.gdprApplies @@ -114,7 +114,7 @@ function appendCMPData (url, cmpData) { return url } -function appendCounters (url) { +function appendCounters(url) { url += '&jaesc=' + encodeURIComponent(callCount); url += '&jafc=' + encodeURIComponent(failCount); url += '&jaensc=' + encodeURIComponent(noDataCount); @@ -146,7 +146,7 @@ function addMetaData(url, data) { return url + '&fbp=' + data; } -export function initializeGlobalIIQ (partnerId) { +export function initializeGlobalIIQ(partnerId) { if (!globalName || !window[globalName]) { globalName = `iiq_identity_${partnerId}` window[globalName] = {} @@ -171,7 +171,7 @@ export function createPixelUrl(firstPartyData, clientHints, configParams, partne url = appendCMPData(url, cmpData); url = addMetaData(url, sourceMetaDataExternal || sourceMetaData); url = handleAdditionalParams(browser, url, 0, configParams.additionalParams); - url = appendSPData(url, firstPartyData) + url = appendSPData(url, partnerData); url += '&source=' + PREBID; return url; } @@ -184,7 +184,7 @@ function sendSyncRequest(allowedStorage, url, partner, firstPartyData, newUser) const needToDoSync = (Date.now() - (firstPartyData?.date || firstPartyData?.sCal || Date.now())) > SYNC_REFRESH_MILL if (newUser || needToDoSync) { ajax(url, () => { - }, undefined, {method: 'GET', withCredentials: true}); + }, undefined, { method: 'GET', withCredentials: true }); if (firstPartyData?.date) { firstPartyData.date = Date.now() storeData(FIRST_PARTY_KEY_FINAL, JSON.stringify(firstPartyData), allowedStorage, firstPartyData); @@ -193,7 +193,7 @@ function sendSyncRequest(allowedStorage, url, partner, firstPartyData, newUser) } else if (!lastSyncDate || lastSyncElapsedTime > SYNC_REFRESH_MILL) { storeData(SYNC_KEY(partner), Date.now() + '', allowedStorage); ajax(url, () => { - }, undefined, {method: 'GET', withCredentials: true}); + }, undefined, { method: 'GET', withCredentials: true }); } } @@ -208,9 +208,16 @@ export function setGamReporting(gamObjectReference, gamParameterName, userGroup, if (isBlacklisted) return; if (isPlainObject(gamObjectReference) && gamObjectReference.cmd) { gamObjectReference.cmd.push(() => { - gamObjectReference - .pubads() - .setTargeting(gamParameterName, userGroup); + if (typeof gamObjectReference.setConfig === 'function') { + gamObjectReference.setConfig({ + targeting: { + [gamParameterName]: userGroup + } + }); + return; + } + // Fallback in case an older version of Google Publisher Tag is used. + gamObjectReference?.pubads?.()?.setTargeting?.(gamParameterName, userGroup); }); } } @@ -285,7 +292,7 @@ export const intentIqIdSubmodule = { * @returns {{intentIqId: {string}}|undefined} */ decode(value) { - return value && INVALID_ID !== value ? {'intentIqId': value} : undefined; + return value && INVALID_ID !== value ? { 'intentIqId': value } : undefined; }, /** @@ -431,7 +438,7 @@ export const intentIqIdSubmodule = { } } - function updateGlobalObj () { + function updateGlobalObj() { if (globalName) { window[globalName].partnerData = partnerData window[globalName].firstPartyData = firstPartyData @@ -442,8 +449,8 @@ export const intentIqIdSubmodule = { let hasPartnerData = !!Object.keys(partnerData).length; if (!isCMPStringTheSame(firstPartyData, cmpData) || - !firstPartyData.sCal || - (hasPartnerData && (!partnerData.cttl || !partnerData.date || Date.now() - partnerData.date > partnerData.cttl))) { + !firstPartyData.sCal || + (hasPartnerData && (!partnerData.cttl || !partnerData.date || Date.now() - partnerData.date > partnerData.cttl))) { firstPartyData.uspString = cmpData.uspString; firstPartyData.gppString = cmpData.gppString; firstPartyData.gdprString = cmpData.gdprString; @@ -454,7 +461,7 @@ export const intentIqIdSubmodule = { if (!shouldCallServer) { if (!hasPartnerData && !firstPartyData.isOptedOut) { shouldCallServer = true; - } else shouldCallServer = Date.now() > firstPartyData.sCal + HOURS_24; + } else shouldCallServer = Date.now() > firstPartyData.sCal + HOURS_72; } if (firstPartyData.isOptedOut) { @@ -498,7 +505,7 @@ export const intentIqIdSubmodule = { updateGlobalObj() // update global object before server request, to make sure analytical adapter will have it even if the server is "not in time" // use protocol relative urls for http or https - let url = `${iiqServerAddress(configParams, gdprDetected)}/profiles_engine/ProfilesEngineServlet?at=39&mi=10&dpi=${configParams.partner}&pt=17&dpn=1`; + let url = `${getIiqServerAddress(configParams)}/profiles_engine/ProfilesEngineServlet?at=39&mi=10&dpi=${configParams.partner}&pt=17&dpn=1`; url += configParams.pai ? '&pai=' + encodeURIComponent(configParams.pai) : ''; url = appendFirstPartyData(url, firstPartyData, partnerData); url = appendPartnersFirstParty(url, configParams); @@ -511,7 +518,7 @@ export const intentIqIdSubmodule = { url += actualABGroup ? '&testGroup=' + encodeURIComponent(actualABGroup) : ''; url = addMetaData(url, sourceMetaDataExternal || sourceMetaData); url = handleAdditionalParams(currentBrowserLowerCase, url, 1, additionalParams); - url = appendSPData(url, firstPartyData) + url = appendSPData(url, partnerData) url += '&source=' + PREBID; url += '&ABTestingConfigurationSource=' + configParams.ABTestingConfigurationSource url += '&abtg=' + encodeURIComponent(actualABGroup) @@ -528,6 +535,9 @@ export const intentIqIdSubmodule = { const resp = function (callback) { const callbacks = { success: response => { + if (rrttStrtTime && rrttStrtTime > 0) { + partnerData.rrtt = Date.now() - rrttStrtTime; + } const respJson = tryParse(response); // If response is a valid json and should save is true if (respJson) { @@ -542,7 +552,7 @@ export const intentIqIdSubmodule = { if (callbackTimeoutID) clearTimeout(callbackTimeoutID) if ('cttl' in respJson) { partnerData.cttl = respJson.cttl; - } else partnerData.cttl = HOURS_24; + } else partnerData.cttl = HOURS_72; if ('tc' in respJson) { partnerData.terminationCause = respJson.tc; @@ -588,7 +598,7 @@ export const intentIqIdSubmodule = { } else { // If data is a single string, assume it is an id with source intentiq.com if (respJson.data && typeof respJson.data === 'string') { - respJson.data = {eids: [respJson.data]} + respJson.data = { eids: [respJson.data] } } } partnerData.data = respJson.data; @@ -604,7 +614,7 @@ export const intentIqIdSubmodule = { if ('spd' in respJson) { // server provided data - firstPartyData.spd = respJson.spd; + partnerData.spd = respJson.spd; } if ('abTestUuid' in respJson) { @@ -620,10 +630,6 @@ export const intentIqIdSubmodule = { delete partnerData.gpr // remove prediction flag in case server doesn't provide it } - if (rrttStrtTime && rrttStrtTime > 0) { - partnerData.rrtt = Date.now() - rrttStrtTime; - } - if (respJson.data?.eids) { runtimeEids = respJson.data callback(respJson.data.eids); @@ -648,12 +654,13 @@ export const intentIqIdSubmodule = { callback(runtimeEids); } }; - rrttStrtTime = Date.now(); partnerData.wsrvcll = true; storeData(PARTNER_DATA_KEY, JSON.stringify(partnerData), allowedStorage, firstPartyData); clearCountersAndStore(allowedStorage, partnerData); + rrttStrtTime = Date.now(); + const sendAjax = uh => { if (uh) url += '&uh=' + encodeURIComponent(uh); ajax(url, callbacks, undefined, { method: 'GET', withCredentials: true }); @@ -675,7 +682,7 @@ export const intentIqIdSubmodule = { sendAjax(''); } }; - const respObj = {callback: resp}; + const respObj = { callback: resp }; if (runtimeEids?.eids?.length) respObj.id = runtimeEids.eids; return respObj diff --git a/modules/intentIqIdSystem.md b/modules/intentIqIdSystem.md index 7597ba90fbf..ee002d90c0c 100644 --- a/modules/intentIqIdSystem.md +++ b/modules/intentIqIdSystem.md @@ -56,10 +56,11 @@ Please find below list of parameters that could be used in configuring Intent IQ | params. ABTestingConfigurationSource| Optional | String | Determines how AB group will be defined. Possible values: `"IIQServer"` – group defined by IIQ server, `"percentage"` – generated group based on abPercentage, `"group"` – define group based on value provided by partner. | `IIQServer` | | params.abPercentage | Optional | Number | Percentage for A/B testing group. Default value is `95` | `95` | | params.group | Optional | String | Define group provided by partner, possible values: `"A"`, `"B"` | `"A"` | +| params.region | Optional | String | Optional region identifier used to automatically build server endpoints. When specified, region-specific endpoints will be used. (`gdpr`,`emea`, `apac`) | `"gdpr"` | | params.additionalParams | Optional | Array | This parameter allows sending additional custom key-value parameters with specific destination logic (sync, VR, winreport). Each custom parameter is defined as an object in the array. | `[ { parameterName: “abc”, parameterValue: 123, destination: [1,1,0] } ]` | -| params.additionalParams [0].parameterName | Required | String | Name of the custom parameter. This will be sent as a query parameter. | `"abc"` | -| params.additionalParams [0].parameterValue | Required | String / Number | Value to assign to the parameter. | `123` | -| params.additionalParams [0].destination | Required | Array | Array of numbers either `1` or `0`. Controls where this parameter is sent `[sendWithSync, sendWithVr, winreport]`. | `[1, 0, 0]` | +| params.additionalParams [0].parameterName | Optional | String | Name of the custom parameter. This will be sent as a query parameter. | `"abc"` | +| params.additionalParams [0].parameterValue | Optional | String / Number | Value to assign to the parameter. | `123` | +| params.additionalParams [0].destination | Optional | Array | Array of numbers either `1` or `0`. Controls where this parameter is sent `[sendWithSync, sendWithVr, winreport]`. | `[1, 0, 0]` | ### Configuration example @@ -72,6 +73,7 @@ pbjs.setConfig({ partner: 123456, // valid partner id timeoutInMillis: 500, browserBlackList: "chrome", + ABTestingConfigurationSource: 'IIQServer', callback: (data) => {...}, // your logic here groupChanged: (group) => console.log('Group is', group), domainName: "currentDomain.com", @@ -80,7 +82,8 @@ pbjs.setConfig({ sourceMetaData: "123.123.123.123", // Optional parameter sourceMetaDataExternal: 123456, // Optional parameter chTimeout: 10, // Optional parameter - abPercentage: 95 //Optional parameter + abPercentage: 95, // Optional parameter + region: "gdpr", // Optional parameter additionalParams: [ // Optional parameter { parameterName: "abc", diff --git a/modules/interactiveOffersBidAdapter.js b/modules/interactiveOffersBidAdapter.js index b7104776190..a7bca0b8edb 100644 --- a/modules/interactiveOffersBidAdapter.js +++ b/modules/interactiveOffersBidAdapter.js @@ -1,6 +1,6 @@ -import {deepClone, isNumber, logWarn} from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER} from '../src/mediaTypes.js'; +import { deepClone, isNumber, logWarn } from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER } from '../src/mediaTypes.js'; const BIDDER_CODE = 'interactiveOffers'; const ENDPOINT = 'https://prebid.ioadx.com/bidRequest/?partnerId='; @@ -139,7 +139,7 @@ function parseRequestPrebidjsToOpenRTB(prebidRequest, bidderRequest) { imp.banner.w = adSize[0]; imp.banner.h = adSize[1]; } - imp.banner.format.push({w: adSize[0], h: adSize[1]}); + imp.banner.format.push({ w: adSize[0], h: adSize[1] }); }); } }); diff --git a/modules/intersectionRtdProvider.js b/modules/intersectionRtdProvider.js deleted file mode 100644 index e1ef87f48e2..00000000000 --- a/modules/intersectionRtdProvider.js +++ /dev/null @@ -1,118 +0,0 @@ -import {submodule} from '../src/hook.js'; -import {isFn, logError} from '../src/utils.js'; -import {config} from '../src/config.js'; -import {getGlobal} from '../src/prebidGlobal.js'; - -import '../src/adapterManager.js'; - -let observerAvailable = true; -function getIntersectionData(requestBidsObject, onDone, providerConfig, userConsent) { - const intersectionMap = {}; - const placeholdersMap = {}; - let done = false; - if (!observerAvailable) return complete(); - const observer = new IntersectionObserver(observerCallback, {threshold: 0.5}); - const adUnitCodes = requestBidsObject.adUnitCodes || []; - const auctionDelay = config.getConfig('realTimeData.auctionDelay') || 0; - const waitForIt = providerConfig.waitForIt; - let adUnits = requestBidsObject.adUnits || getGlobal().adUnits || []; - if (adUnitCodes.length) { - adUnits = adUnits.filter(unit => adUnitCodes.includes(unit.code)); - } - let checkTimeoutId; - findAndObservePlaceholders(); - if (auctionDelay > 0) { - setTimeout(complete, auctionDelay); - } - function findAndObservePlaceholders() { - const observed = adUnits.filter((unit) => { - const code = unit.code; - if (placeholdersMap[code]) return true; - const ph = document.getElementById(code); - if (ph) { - placeholdersMap[code] = ph; - observer.observe(ph); - return true; - } - return false; - }); - if ( - observed.length === adUnits.length || - !waitForIt || - auctionDelay <= 0 - ) { - return; - } - checkTimeoutId = setTimeout(findAndObservePlaceholders); - } - function observerCallback(entries) { - let entry = entries.pop(); - while (entry) { - const target = entry.target; - const id = target.getAttribute('id'); - if (id) { - const intersection = intersectionMap[id]; - if (!intersection || intersection.time < entry.time) { - intersectionMap[id] = { - 'boundingClientRect': cloneRect(entry.boundingClientRect), - 'intersectionRect': cloneRect(entry.intersectionRect), - 'rootRect': cloneRect(entry.rootRect), - 'intersectionRatio': entry.intersectionRatio, - 'isIntersecting': entry.isIntersecting, - 'time': entry.time - }; - if (adUnits.every(unit => !!intersectionMap[unit.code])) { - complete(); - } - } - } - entry = entries.pop(); - } - } - function complete() { - if (done) return; - if (checkTimeoutId) clearTimeout(checkTimeoutId); - done = true; - checkTimeoutId = null; - observer && observer.disconnect(); - adUnits && adUnits.forEach((unit) => { - const intersection = intersectionMap[unit.code]; - if (intersection && unit.bids) { - unit.bids.forEach(bid => { - bid.intersection = intersection; - }); - } - }); - onDone(); - } -} -function init(moduleConfig) { - if (!isFn(window.IntersectionObserver)) { - logError('IntersectionObserver is not defined'); - observerAvailable = false; - } else { - observerAvailable = true; - } - return observerAvailable; -} -function cloneRect(rect) { - return rect ? { - 'left': rect.left, - 'top': rect.top, - 'right': rect.right, - 'bottom': rect.bottom, - 'width': rect.width, - 'height': rect.height, - 'x': rect.x, - 'y': rect.y, - } : rect; -} -export const intersectionSubmodule = { - name: 'intersection', - getBidRequestData: getIntersectionData, - init: init, -}; -function registerSubModule() { - submodule('realTimeData', intersectionSubmodule); -} -registerSubModule(); diff --git a/modules/invamiaBidAdapter.js b/modules/invamiaBidAdapter.js index 7f9a40e1473..f9cffcd30dd 100644 --- a/modules/invamiaBidAdapter.js +++ b/modules/invamiaBidAdapter.js @@ -1,5 +1,5 @@ -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER} from '../src/mediaTypes.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER } from '../src/mediaTypes.js'; import { buildBannerRequests, interpretBannerResponse } from '../libraries/biddoInvamiaUtils/index.js'; /** diff --git a/modules/invibesBidAdapter.js b/modules/invibesBidAdapter.js index 69bb7cae6eb..1f4e51967b8 100644 --- a/modules/invibesBidAdapter.js +++ b/modules/invibesBidAdapter.js @@ -1,6 +1,6 @@ -import {getWinDimensions, logInfo} from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {getStorageManager} from '../src/storageManager.js'; +import { getWinDimensions, logInfo } from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { getStorageManager } from '../src/storageManager.js'; /** * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest @@ -22,7 +22,7 @@ const CONSTANTS = { DISABLE_USER_SYNC: true }; -export const storage = getStorageManager({bidderCode: CONSTANTS.BIDDER_CODE}); +export const storage = getStorageManager({ bidderCode: CONSTANTS.BIDDER_CODE }); export const spec = { code: CONSTANTS.BIDDER_CODE, @@ -225,7 +225,7 @@ function buildRequest(bidRequests, bidderRequest) { method: CONSTANTS.METHOD, url: endpoint, data: data, - options: {withCredentials: true}, + options: { withCredentials: true }, // for POST: { contentType: 'application/json', withCredentials: true } bidRequests: bidRequests }; diff --git a/modules/invisiblyAnalyticsAdapter.js b/modules/invisiblyAnalyticsAdapter.js index d6b5fc3efef..e835df6ce47 100644 --- a/modules/invisiblyAnalyticsAdapter.js +++ b/modules/invisiblyAnalyticsAdapter.js @@ -27,7 +27,6 @@ const { BIDDER_DONE, SET_TARGETING, REQUEST_BIDS, - ADD_AD_UNITS, AD_RENDER_FAILED, } = EVENTS; @@ -187,10 +186,6 @@ function handleEvent(eventType, eventArgs) { invisiblyEvent = eventArgs; break; } - case ADD_AD_UNITS: { - invisiblyEvent = eventArgs; - break; - } case AD_RENDER_FAILED: { invisiblyEvent = eventArgs; break; diff --git a/modules/iqxBidAdapter.js b/modules/iqxBidAdapter.js index e859dfd2c01..49362a2e0ae 100644 --- a/modules/iqxBidAdapter.js +++ b/modules/iqxBidAdapter.js @@ -1,6 +1,6 @@ -import {BANNER, VIDEO} from '../src/mediaTypes.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {buildRequests, getUserSyncs, interpretResponse, isBidRequestValid} from '../libraries/xeUtils/bidderUtils.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { buildRequests, getUserSyncs, interpretResponse, isBidRequestValid } from '../libraries/xeUtils/bidderUtils.js'; const BIDDER_CODE = 'iqx'; const ENDPOINT = 'https://pbjs.iqzonertb.live'; diff --git a/modules/ivsBidAdapter.js b/modules/ivsBidAdapter.js index f6baedec2ee..bbe1ad4ede4 100644 --- a/modules/ivsBidAdapter.js +++ b/modules/ivsBidAdapter.js @@ -1,5 +1,5 @@ import { ortbConverter } from '../libraries/ortbConverter/converter.js'; -import {deepAccess, deepSetValue, getBidIdParameter, logError} from '../src/utils.js'; +import { deepAccess, deepSetValue, getBidIdParameter, logError } from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { VIDEO } from '../src/mediaTypes.js'; import { INSTREAM } from '../src/video.js'; @@ -64,7 +64,7 @@ export const spec = { * @return {Object} Info describing the request to the server. */ buildRequests: function(validBidRequests, bidderRequest) { - const data = converter.toORTB({ bidderRequest, validBidRequests, context: {mediaType: 'video'} }); + const data = converter.toORTB({ bidderRequest, validBidRequests, context: { mediaType: 'video' } }); deepSetValue(data.site, 'publisher.id', validBidRequests[0].params.publisherId); return { diff --git a/modules/ixBidAdapter.js b/modules/ixBidAdapter.js index 1df9570aab8..b2da2f3c077 100644 --- a/modules/ixBidAdapter.js +++ b/modules/ixBidAdapter.js @@ -22,15 +22,17 @@ import { getStorageManager } from '../src/storageManager.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { INSTREAM, OUTSTREAM } from '../src/video.js'; import { Renderer } from '../src/Renderer.js'; -import {getGptSlotInfoForAdUnitCode} from '../libraries/gptUtils/gptUtils.js'; +import { getGptSlotInfoForAdUnitCode } from '../libraries/gptUtils/gptUtils.js'; +import { getAdUnitElement } from '../src/utils/adUnits.js'; const divIdCache = {}; -export function getDivIdFromAdUnitCode(adUnitCode) { +export function getDivIdFromAdUnit(adUnitCode, target) { if (divIdCache[adUnitCode]) { return divIdCache[adUnitCode]; } - const divId = document.getElementById(adUnitCode) ? adUnitCode : getGptSlotInfoForAdUnitCode(adUnitCode).divId; + const element = getAdUnitElement(target); + const divId = element?.id ? element.id : getGptSlotInfoForAdUnitCode(adUnitCode).divId; divIdCache[adUnitCode] = divId; return divId; } @@ -686,8 +688,7 @@ function buildRequest(validBidRequests, bidderRequest, impressions, version) { r = addRequestedFeatureToggles(r, FEATURE_TOGGLES.REQUESTED_FEATURE_TOGGLES) // getting ixdiags for adunits of the video, outstream & multi format (MF) style - const fledgeEnabled = deepAccess(bidderRequest, 'paapi.enabled') - const ixdiag = buildIXDiag(validBidRequests, fledgeEnabled); + const ixdiag = buildIXDiag(validBidRequests); for (const key in ixdiag) { r.ext.ixdiag[key] = ixdiag[key]; } @@ -954,8 +955,6 @@ function addImpressions(impressions, impKeys, r, adUnitIndex) { const dfpAdUnitCode = impressions[impKeys[adUnitIndex]].dfp_ad_unit_code; const tid = impressions[impKeys[adUnitIndex]].tid; const sid = impressions[impKeys[adUnitIndex]].sid; - const auctionEnvironment = impressions[impKeys[adUnitIndex]].ae; - const paapi = impressions[impKeys[adUnitIndex]].paapi; const bannerImpressions = impressionObjects.filter(impression => BANNER in impression); const otherImpressions = impressionObjects.filter(impression => !(BANNER in impression)); @@ -976,7 +975,8 @@ function addImpressions(impressions, impKeys, r, adUnitIndex) { banner: { topframe, format: bannerImps.map(({ banner: { w, h }, ext }) => ({ w, h, ext })) - }}; + } + }; for (let i = 0; i < _bannerImpression.banner.format.length; i++) { // We add sid and externalID in imp.ext therefore, remove from banner.format[].ext @@ -1005,7 +1005,7 @@ function addImpressions(impressions, impKeys, r, adUnitIndex) { _bannerImpression.banner.pos = position; } - if (dfpAdUnitCode || gpid || tid || sid || auctionEnvironment || externalID || paapi) { + if (dfpAdUnitCode || gpid || tid || sid || externalID) { _bannerImpression.ext = {}; _bannerImpression.ext.dfp_ad_unit_code = dfpAdUnitCode; @@ -1013,12 +1013,6 @@ function addImpressions(impressions, impKeys, r, adUnitIndex) { _bannerImpression.ext.tid = tid; _bannerImpression.ext.sid = sid; _bannerImpression.ext.externalID = externalID; - - // enable fledge auction - if (Number(auctionEnvironment) === 1) { - _bannerImpression.ext.ae = 1; - _bannerImpression.ext.paapi = paapi; - } } if ('bidfloor' in bannerImps[0]) { @@ -1172,7 +1166,7 @@ function addFPD(bidderRequest, r, fpd, site, user) { }); if (fpd.device) { - const sua = {...fpd.device.sua}; + const sua = { ...fpd.device.sua }; if (!isEmpty(sua)) { deepSetValue(r, 'device.sua', sua); } @@ -1272,10 +1266,9 @@ function addIdentifiersInfo(impressions, r, impKeys, adUnitIndex, payload, baseU * Calculates IX diagnostics values and packages them into an object * * @param {Array} validBidRequests - The valid bid requests from prebid - * @param {boolean} fledgeEnabled - Flag indicating if protected audience (fledge) is enabled * @return {Object} IX diag values for ad units */ -function buildIXDiag(validBidRequests, fledgeEnabled) { +function buildIXDiag(validBidRequests) { var adUnitMap = validBidRequests .map(bidRequest => bidRequest.adUnitCode) .filter((value, index, arr) => arr.indexOf(value) === index); @@ -1292,7 +1285,6 @@ function buildIXDiag(validBidRequests, fledgeEnabled) { version: '$prebid.version$', url: window.location.href.split('?')[0], vpd: defaultVideoPlacement, - ae: fledgeEnabled, eidLength: allEids.length }; @@ -1368,7 +1360,7 @@ function createNativeImps(validBidRequest, nativeImps) { nativeImps[validBidRequest.adUnitCode].tagId = deepAccess(validBidRequest, 'params.tagId'); const adUnitCode = validBidRequest.adUnitCode; - const divId = getDivIdFromAdUnitCode(adUnitCode); + const divId = getDivIdFromAdUnit(adUnitCode, validBidRequest); nativeImps[validBidRequest.adUnitCode].adUnitCode = adUnitCode; nativeImps[validBidRequest.adUnitCode].divId = divId; } @@ -1390,7 +1382,7 @@ function createVideoImps(validBidRequest, videoImps) { videoImps[validBidRequest.adUnitCode].tagId = deepAccess(validBidRequest, 'params.tagId'); const adUnitCode = validBidRequest.adUnitCode; - const divId = getDivIdFromAdUnitCode(adUnitCode); + const divId = getDivIdFromAdUnit(adUnitCode, validBidRequest); videoImps[validBidRequest.adUnitCode].adUnitCode = adUnitCode; videoImps[validBidRequest.adUnitCode].divId = divId; } @@ -1417,23 +1409,6 @@ function createBannerImps(validBidRequest, missingBannerSizes, bannerImps, bidde bannerImps[validBidRequest.adUnitCode].tagId = deepAccess(validBidRequest, 'params.tagId'); bannerImps[validBidRequest.adUnitCode].pos = deepAccess(validBidRequest, 'mediaTypes.banner.pos'); - // Add Fledge flag if enabled - const fledgeEnabled = deepAccess(bidderRequest, 'paapi.enabled') - if (fledgeEnabled) { - const auctionEnvironment = deepAccess(validBidRequest, 'ortb2Imp.ext.ae') - const paapi = deepAccess(validBidRequest, 'ortb2Imp.ext.paapi') - if (paapi) { - bannerImps[validBidRequest.adUnitCode].paapi = paapi - } - if (auctionEnvironment) { - if (isInteger(auctionEnvironment)) { - bannerImps[validBidRequest.adUnitCode].ae = auctionEnvironment; - } else { - logWarn('error setting auction environment flag - must be an integer') - } - } - } - // AdUnit-Specific First Party Data const adUnitFPD = deepAccess(validBidRequest, 'ortb2Imp.ext.data'); if (adUnitFPD) { @@ -1446,7 +1421,7 @@ function createBannerImps(validBidRequest, missingBannerSizes, bannerImps, bidde } const adUnitCode = validBidRequest.adUnitCode; - const divId = getDivIdFromAdUnitCode(adUnitCode); + const divId = getDivIdFromAdUnit(adUnitCode, validBidRequest); bannerImps[validBidRequest.adUnitCode].adUnitCode = adUnitCode; bannerImps[validBidRequest.adUnitCode].divId = divId; @@ -1514,7 +1489,7 @@ function createMissingBannerImp(bid, imp, newSize) { function outstreamRenderer(bid) { bid.renderer.push(function () { const adUnitCode = bid.adUnitCode; - const divId = getDivIdFromAdUnitCode(adUnitCode); + const divId = getDivIdFromAdUnit(adUnitCode, bid); if (!divId) { logWarn(`IX Bid Adapter: adUnitCode: ${divId} not found on page.`); return; @@ -1766,9 +1741,6 @@ export const spec = { const bids = []; let bid = null; - // Extract the FLEDGE auction configuration list from the response - let fledgeAuctionConfigs = deepAccess(serverResponse, 'body.ext.protectedAudienceAuctionConfigs') || []; - FEATURE_TOGGLES.setFeatureToggles(serverResponse); if (!serverResponse.hasOwnProperty('body')) { @@ -1812,29 +1784,7 @@ export const spec = { } } } - - if (Array.isArray(fledgeAuctionConfigs) && fledgeAuctionConfigs.length > 0) { - // Validate and filter fledgeAuctionConfigs - fledgeAuctionConfigs = fledgeAuctionConfigs.filter(config => { - if (!isValidAuctionConfig(config)) { - logWarn('Malformed auction config detected:', config); - return false; - } - return true; - }); - - try { - return { - bids, - paapi: fledgeAuctionConfigs, - }; - } catch (error) { - logWarn('Error attaching AuctionConfigs', error); - return bids; - } - } else { - return bids; - } + return bids; }, /** @@ -2044,15 +1994,6 @@ function getFormatCount(imp) { return formatCount; } -/** - * Checks if auction config is valid - * @param {object} config - * @returns bool - */ -function isValidAuctionConfig(config) { - return typeof config === 'object' && config !== null; -} - /** * Adds device.w / device.h info * @param {object} r diff --git a/modules/ixBidAdapter.md b/modules/ixBidAdapter.md index e6f6f8dc320..14a013fd6be 100644 --- a/modules/ixBidAdapter.md +++ b/modules/ixBidAdapter.md @@ -468,11 +468,6 @@ pbjs.setConfig({ The timeout value must be a positive whole number in milliseconds. -Protected Audience API (FLEDGE) -=========================== - -In order to enable receiving [Protected Audience API](https://developer.chrome.com/en/docs/privacy-sandbox/fledge/) traffic, follow Prebid's documentation on [paapiForGpt](https://docs.prebid.org/dev-docs/modules/paapiForGpt.html) module to build and enable Fledge. - Additional Information ====================== diff --git a/modules/jixieBidAdapter.js b/modules/jixieBidAdapter.js index 3a4df75762b..67895347e73 100644 --- a/modules/jixieBidAdapter.js +++ b/modules/jixieBidAdapter.js @@ -1,18 +1,18 @@ -import {getDNT} from '../libraries/dnt/index.js'; -import {deepAccess, isArray, logWarn, isFn, isPlainObject, logError, logInfo, getWinDimensions} from '../src/utils.js'; -import {config} from '../src/config.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {getStorageManager} from '../src/storageManager.js'; -import {BANNER, VIDEO} from '../src/mediaTypes.js'; -import {ajax} from '../src/ajax.js'; -import {getRefererInfo} from '../src/refererDetection.js'; -import {Renderer} from '../src/Renderer.js'; +import { deepAccess, isArray, logWarn, isFn, isPlainObject, logError, logInfo, getWinDimensions } from '../src/utils.js'; +import { config } from '../src/config.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { getStorageManager } from '../src/storageManager.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; +import { ajax } from '../src/ajax.js'; +import { getRefererInfo } from '../src/refererDetection.js'; +import { Renderer } from '../src/Renderer.js'; +import { getDNT } from '../libraries/dnt/index.js'; const ADAPTER_VERSION = '2.1.0'; const PREBID_VERSION = '$prebid.version$'; const BIDDER_CODE = 'jixie'; -export const storage = getStorageManager({bidderCode: BIDDER_CODE}); +export const storage = getStorageManager({ bidderCode: BIDDER_CODE }); const JX_OUTSTREAM_RENDERER_URL = 'https://scripts.jixie.media/jxhbrenderer.1.1.min.js'; const REQUESTS_URL = 'https://hb.jixie.io/v2/hbpost'; const sidTTLMins_ = 30; @@ -130,7 +130,7 @@ function createRenderer_(bidAd, scriptUrl, createFcn) { id: bidAd.adUnitCode, url: scriptUrl, loaded: false, - config: {'player_height': bidAd.height, 'player_width': bidAd.width}, + config: { 'player_height': bidAd.height, 'player_width': bidAd.width }, adUnitCode: bidAd.adUnitCode }); try { @@ -307,7 +307,7 @@ export const spec = { if (syncOptions.iframeEnabled) { syncs.push(sync.uf ? { url: sync.uf, type: 'iframe' } : { url: sync.up, type: 'image' }); } else if (syncOptions.pixelEnabled && sync.up) { - syncs.push({url: sync.up, type: 'image'}) + syncs.push({ url: sync.up, type: 'image' }) } }) return syncs; diff --git a/modules/justIdSystem.js b/modules/justIdSystem.js index 4ced4025ef6..eade64521e9 100644 --- a/modules/justIdSystem.js +++ b/modules/justIdSystem.js @@ -53,7 +53,7 @@ export const justIdSubmodule = { decode(value) { utils.logInfo(LOG_PREFIX, 'decode', value); const justId = value && value.uid; - return justId && {justId: justId}; + return justId && { justId: justId }; }, /** @@ -89,7 +89,7 @@ export const justIdSubmodule = { cbFun(); return; } - cbFun({uid: justId}); + cbFun({ uid: justId }); }, err => { utils.logError(LOG_PREFIX, 'error during fetching', err); cbFun(); diff --git a/modules/jwplayerBidAdapter.js b/modules/jwplayerBidAdapter.js index 9ee7312bb84..3ff51fce8c1 100644 --- a/modules/jwplayerBidAdapter.js +++ b/modules/jwplayerBidAdapter.js @@ -1,9 +1,9 @@ -import {getDNT} from '../libraries/dnt/index.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { VIDEO } from '../src/mediaTypes.js'; import { isArray, isFn, deepAccess, deepSetValue, logError, logWarn } from '../src/utils.js'; import { config } from '../src/config.js'; import { hasPurpose1Consent } from '../src/utils/gdpr.js'; +import { getDNT } from '../libraries/dnt/index.js'; const BIDDER_CODE = 'jwplayer'; const BASE_URL = 'https://vpb-server.jwplayer.com/'; diff --git a/modules/jwplayerRtdProvider.js b/modules/jwplayerRtdProvider.js index 860d31688bf..48570d6d43d 100644 --- a/modules/jwplayerRtdProvider.js +++ b/modules/jwplayerRtdProvider.js @@ -9,11 +9,11 @@ * @requires module:modules/realTimeData */ -import {submodule} from '../src/hook.js'; -import {config} from '../src/config.js'; -import {ajaxBuilder} from '../src/ajax.js'; +import { submodule } from '../src/hook.js'; +import { config } from '../src/config.js'; +import { ajaxBuilder } from '../src/ajax.js'; import { deepAccess, logError, logWarn } from '../src/utils.js' -import {getGlobal} from '../src/prebidGlobal.js'; +import { getGlobal } from '../src/prebidGlobal.js'; /** * @typedef {import('../modules/rtdModule/index.js').RtdSubmodule} RtdSubmodule @@ -52,7 +52,7 @@ export const jwplayerSubmodule = { init }; -config.getConfig('realTimeData', ({realTimeData}) => { +config.getConfig('realTimeData', ({ realTimeData }) => { const providers = realTimeData.dataProviders; const jwplayerProvider = providers && ((providers) || []).find(pr => pr.name && pr.name.toLowerCase() === SUBMODULE_NAME); const params = jwplayerProvider && jwplayerProvider.params; diff --git a/modules/jwplayerVideoProvider.js b/modules/jwplayerVideoProvider.js index ee8bd15f9d6..6516b3d3cea 100644 --- a/modules/jwplayerVideoProvider.js +++ b/modules/jwplayerVideoProvider.js @@ -127,7 +127,7 @@ export function JWPlayerProvider(config, jwplayer_, adState_, timeState_, callba battr: adConfig.battr, maxextended: -1, // extension is allowed, and there is no time limit imposed. boxingallowed: 1, - playbackmethod: [ utils.getPlaybackMethod(config) ], + playbackmethod: [utils.getPlaybackMethod(config)], playbackend: 1, // TODO: need to account for floating player - https://developer.jwplayer.com/jwplayer/docs/jw8-embed-an-outstream-player , https://developer.jwplayer.com/jwplayer/docs/jw8-player-configuration-reference#section-float-on-scroll // companionad - TODO add in future version // companiontype - TODO add in future version diff --git a/modules/kargoBidAdapter.js b/modules/kargoBidAdapter.js index 1909112d36d..33355868d19 100644 --- a/modules/kargoBidAdapter.js +++ b/modules/kargoBidAdapter.js @@ -16,7 +16,7 @@ const BIDDER = Object.freeze({ SUPPORTED_MEDIA_TYPES: [BANNER, VIDEO], }); -const STORAGE = getStorageManager({bidderCode: BIDDER.CODE}); +const STORAGE = getStorageManager({ bidderCode: BIDDER.CODE }); const CURRENCY = Object.freeze({ KEY: 'currency', @@ -199,7 +199,6 @@ function buildRequests(validBidRequests, bidderRequest) { function interpretResponse(response, bidRequest) { const bids = response.body; - const fledgeAuctionConfigs = []; const bidResponses = []; if (isEmpty(bids) || typeof bids !== 'object') { @@ -241,23 +240,9 @@ function interpretResponse(response, bidRequest) { } bidResponses.push(bidResponse); - - if (adUnit.auctionConfig) { - fledgeAuctionConfigs.push({ - bidId: bidID, - config: adUnit.auctionConfig - }) - } } - if (fledgeAuctionConfigs.length > 0) { - return { - bids: bidResponses, - paapi: fledgeAuctionConfigs - } - } else { - return bidResponses; - } + return bidResponses; } function getUserSyncs(syncOptions, _, gdprConsent, usPrivacy, gppConsent) { diff --git a/modules/kimberliteBidAdapter.js b/modules/kimberliteBidAdapter.js index 637afe328da..f38696779a8 100644 --- a/modules/kimberliteBidAdapter.js +++ b/modules/kimberliteBidAdapter.js @@ -2,7 +2,7 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; import { ortbConverter } from '../libraries/ortbConverter/converter.js' import { deepSetValue, replaceMacros } from '../src/utils.js'; -import {ORTB_MTYPES} from '../libraries/ortbConverter/processors/mediaType.js'; +import { ORTB_MTYPES } from '../libraries/ortbConverter/processors/mediaType.js'; const VERSION = '1.1.0'; @@ -86,7 +86,7 @@ export const spec = { }, interpretResponse(serverResponse, bidRequest) { - const bids = converter.fromORTB({response: serverResponse.body, request: bidRequest.data}).bids; + const bids = converter.fromORTB({ response: serverResponse.body, request: bidRequest.data }).bids; return bids; } }; @@ -96,7 +96,7 @@ export function expandAuctionMacros(str, price, currency) { const defaultCurrency = 'RUB'; - return replaceMacros(str, {AUCTION_PRICE: price, AUCTION_CURRENCY: currency || defaultCurrency}); + return replaceMacros(str, { AUCTION_PRICE: price, AUCTION_CURRENCY: currency || defaultCurrency }); }; registerBidder(spec); diff --git a/modules/kinessoIdSystem.js b/modules/kinessoIdSystem.js index 1a86c07ebba..8653a7a6737 100644 --- a/modules/kinessoIdSystem.js +++ b/modules/kinessoIdSystem.js @@ -6,8 +6,8 @@ */ import { logError, logInfo } from '../src/utils.js'; -import {ajax} from '../src/ajax.js'; -import {submodule} from '../src/hook.js'; +import { ajax } from '../src/ajax.js'; +import { submodule } from '../src/hook.js'; /** * @typedef {import('../modules/userId/index.js').Submodule} Submodule @@ -182,7 +182,7 @@ function encodeId(value) { * @return {string} */ function kinessoSyncUrl(accountId, consentData) { - const {gdpr, usp: usPrivacyString} = consentData ?? {}; + const { gdpr, usp: usPrivacyString } = consentData ?? {}; let kinessoSyncUrl = `${ID_SVC}?accountid=${accountId}`; if (usPrivacyString) { kinessoSyncUrl = `${kinessoSyncUrl}&us_privacy=${usPrivacyString}`; @@ -235,8 +235,8 @@ export const kinessoIdSubmodule = { const kinessoIdPayload = {}; kinessoIdPayload.id = knnsoId; const payloadString = JSON.stringify(kinessoIdPayload); - ajax(kinessoSyncUrl(accountId, consentData), syncId(knnsoId), payloadString, {method: 'POST', withCredentials: true}); - return {'id': knnsoId}; + ajax(kinessoSyncUrl(accountId, consentData), syncId(knnsoId), payloadString, { method: 'POST', withCredentials: true }); + return { 'id': knnsoId }; }, eids: { 'kpuid': { diff --git a/modules/koblerBidAdapter.js b/modules/koblerBidAdapter.js index 2b0c55b2fb9..a4b83df1359 100644 --- a/modules/koblerBidAdapter.js +++ b/modules/koblerBidAdapter.js @@ -7,9 +7,9 @@ import { replaceAuctionPrice, triggerPixel } from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER} from '../src/mediaTypes.js'; -import {getRefererInfo} from '../src/refererDetection.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER } from '../src/mediaTypes.js'; +import { getRefererInfo } from '../src/refererDetection.js'; import { getCurrencyFromBidderRequest } from '../libraries/ortb2Utils/currency.js'; const additionalData = new WeakMap(); @@ -204,7 +204,12 @@ function buildOpenRtbImpObject(validBidRequest) { }, bidfloor: floorInfo.floor, bidfloorcur: floorInfo.currency, - pmp: buildPmpObject(validBidRequest) + pmp: buildPmpObject(validBidRequest), + ext: { + prebid: { + adunitcode: validBidRequest.adUnitCode + } + } }; } diff --git a/modules/kubientBidAdapter.js b/modules/kubientBidAdapter.js index 425dbbcf4b9..9a8a1253c31 100644 --- a/modules/kubientBidAdapter.js +++ b/modules/kubientBidAdapter.js @@ -1,6 +1,6 @@ import { isArray, deepAccess, isPlainObject } from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER, VIDEO} from '../src/mediaTypes.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; import { config } from '../src/config.js'; const BIDDER_CODE = 'kubient'; @@ -9,7 +9,7 @@ const VERSION = '1.1'; const VENDOR_ID = 794; export const spec = { code: BIDDER_CODE, - supportedMediaTypes: [ BANNER, VIDEO ], + supportedMediaTypes: [BANNER, VIDEO], isBidRequestValid: function (bid) { return !!( bid && @@ -31,7 +31,7 @@ export const spec = { if (typeof bid.getFloor === 'function') { const mediaType = (Object.keys(bid.mediaTypes).length === 1) ? Object.keys(bid.mediaTypes)[0] : '*'; const sizes = bid.sizes || '*'; - const floorInfo = bid.getFloor({currency: 'USD', mediaType: mediaType, size: sizes}); + const floorInfo = bid.getFloor({ currency: 'USD', mediaType: mediaType, size: sizes }); if (isPlainObject(floorInfo) && floorInfo.currency === 'USD') { const floor = parseFloat(floorInfo.floor) if (!isNaN(floor) && floor > 0) { diff --git a/modules/kueezRtbBidAdapter.js b/modules/kueezRtbBidAdapter.js index 60eced6a490..aa42e11455e 100644 --- a/modules/kueezRtbBidAdapter.js +++ b/modules/kueezRtbBidAdapter.js @@ -1,6 +1,6 @@ -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER, VIDEO} from '../src/mediaTypes.js'; -import {getStorageManager} from '../src/storageManager.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; +import { getStorageManager } from '../src/storageManager.js'; import { createBuildRequestsFn, createInterpretResponseFn, @@ -13,7 +13,7 @@ const GVLID = 1165; const DEFAULT_SUB_DOMAIN = 'exchange'; const BIDDER_CODE = 'kueezrtb'; const BIDDER_VERSION = '1.0.0'; -export const storage = getStorageManager({bidderCode: BIDDER_CODE}); +export const storage = getStorageManager({ bidderCode: BIDDER_CODE }); export const spec = { code: BIDDER_CODE, @@ -62,7 +62,7 @@ function getFirstPartyUUID() { }; function createUniqueRequestData(hashUrl, bid) { - const {auctionId, transactionId} = bid; + const { auctionId, transactionId } = bid; const fdata = getAndSetFirstPartyData(); return { auctionId, diff --git a/modules/lassoBidAdapter.js b/modules/lassoBidAdapter.js index 16e714823a4..1a4e97fb19b 100644 --- a/modules/lassoBidAdapter.js +++ b/modules/lassoBidAdapter.js @@ -9,7 +9,7 @@ const BIDDER_CODE = 'lasso'; const ENDPOINT_URL = 'https://trc.lhmos.com/prebid'; const GET_IUD_URL = 'https://secure.adnxs.com/getuid?'; const COOKIE_NAME = 'aim-xr'; -const storage = getStorageManager({bidderCode: BIDDER_CODE}); +const storage = getStorageManager({ bidderCode: BIDDER_CODE }); export const spec = { code: BIDDER_CODE, diff --git a/modules/leagueMBidAdapter.js b/modules/leagueMBidAdapter.js new file mode 100644 index 00000000000..9171ee50705 --- /dev/null +++ b/modules/leagueMBidAdapter.js @@ -0,0 +1,17 @@ +import { BANNER, VIDEO } from '../src/mediaTypes.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { buildRequests, getUserSyncs, interpretResponse, isBidRequestValid } from '../libraries/xeUtils/bidderUtils.js'; + +const BIDDER_CODE = 'leagueM'; +const ENDPOINT = 'https://pbjs.league-m.media'; + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [BANNER, VIDEO], + isBidRequestValid: bid => isBidRequestValid(bid, ['pid']), + buildRequests: (validBidRequests, bidderRequest) => buildRequests(validBidRequests, bidderRequest, ENDPOINT), + interpretResponse, + getUserSyncs +} + +registerBidder(spec); diff --git a/modules/leagueMBidAdapter.md b/modules/leagueMBidAdapter.md new file mode 100644 index 00000000000..5ef96b0d66b --- /dev/null +++ b/modules/leagueMBidAdapter.md @@ -0,0 +1,54 @@ +# Overview + +``` +Module Name: LeagueM Bidder Adapter +Module Type: LeagueM Bidder Adapter +Maintainer: prebid@league-m.com +``` + +# Description + +Module that connects to league-m.media demand sources + +# Test Parameters +``` +var adUnits = [ + { + code: 'test-banner', + mediaTypes: { + banner: { + sizes: [[300, 250]], + } + }, + bids: [ + { + bidder: 'leagueM', + params: { + env: 'leagueM', + pid: 'aa8217e20131c095fe9dba67981040b0', + ext: {} + } + } + ] + }, + { + code: 'test-video', + sizes: [ [ 640, 480 ] ], + mediaTypes: { + video: { + playerSize: [640, 480], + context: 'instream', + skippable: true + } + }, + bids: [{ + bidder: 'leagueM', + params: { + env: 'leagueM', + pid: 'aa8217e20131c095fe9dba67981040b0', + ext: {} + } + }] + } +]; +``` diff --git a/modules/lemmaDigitalBidAdapter.js b/modules/lemmaDigitalBidAdapter.js index 7447d893217..8c9eaeb925c 100644 --- a/modules/lemmaDigitalBidAdapter.js +++ b/modules/lemmaDigitalBidAdapter.js @@ -1,8 +1,8 @@ import * as utils from '../src/utils.js'; -import { getDNT } from '../libraries/dnt/index.js'; import { config } from '../src/config.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; +import { getDNT } from '../libraries/dnt/index.js'; /** * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest diff --git a/modules/lifestreetBidAdapter.js b/modules/lifestreetBidAdapter.js index 16e17ac50b3..b6db322804a 100644 --- a/modules/lifestreetBidAdapter.js +++ b/modules/lifestreetBidAdapter.js @@ -39,8 +39,8 @@ function template(strings, ...keys) { * @param {BidRequest} bid The bid params to use for formatting a request */ function formatBidRequest(bid, bidderRequest = {}) { - const {params} = bid; - const {referer} = (bidderRequest.refererInfo || {}); + const { params } = bid; + const { referer } = (bidderRequest.refererInfo || {}); let url = urlTemplate({ adapter: 'prebid', slot: params.slot, @@ -90,7 +90,7 @@ export const spec = { supportedMediaTypes: [BANNER, VIDEO], isBidRequestValid: (bid = {}) => { - const {params = {}} = bid; + const { params = {} } = bid; return !!(params.slot && params.adkey && params.ad_size); }, diff --git a/modules/limelightDigitalBidAdapter.js b/modules/limelightDigitalBidAdapter.js index 283a89c5a3f..102aca146bf 100644 --- a/modules/limelightDigitalBidAdapter.js +++ b/modules/limelightDigitalBidAdapter.js @@ -1,7 +1,8 @@ -import { logMessage, groupBy, flatten, uniques } from '../src/utils.js'; +import { uniques, flatten, deepSetValue } from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; import { ajax } from '../src/ajax.js'; +import { ortbConverter } from '../libraries/ortbConverter/converter.js'; /** * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest @@ -10,6 +11,9 @@ import { ajax } from '../src/ajax.js'; */ const BIDDER_CODE = 'limelightDigital'; +const DEFAULT_NET_REVENUE = true; +const DEFAULT_TTL = 300; +const MTYPE_MAP = { 1: BANNER, 2: VIDEO }; /** * Determines whether or not the given bid response is valid. @@ -21,7 +25,7 @@ function isBidResponseValid(bid) { if (!bid.requestId || !bid.cpm || !bid.creativeId || !bid.ttl || !bid.currency || !bid.meta.advertiserDomains) { return false; } - switch (bid.meta.mediaType) { + switch (bid.mediaType) { case BANNER: return Boolean(bid.width && bid.height && bid.ad); case VIDEO: @@ -30,12 +34,46 @@ function isBidResponseValid(bid) { return false; } +const converter = ortbConverter({ + context: { + netRevenue: DEFAULT_NET_REVENUE, + ttl: DEFAULT_TTL + }, + imp(buildImp, bidRequest, context) { + const imp = buildImp(bidRequest, context); + for (let i = 1; i <= 5; i++) { + const customValue = bidRequest.params[`custom${i}`]; + if (customValue !== undefined) { + deepSetValue(imp, `ext.c${i}`, customValue); + } + } + deepSetValue(imp, `ext.adUnitId`, bidRequest.params.adUnitId); + return imp; + }, + bidResponse(buildBidResponse, bid, context) { + let mediaType; + if (bid.mtype) { + mediaType = MTYPE_MAP[bid.mtype]; + } + if (!mediaType && bid.ext?.mediaType) { + mediaType = bid.ext.mediaType; + } + if (!mediaType && context.imp) { + if (context.imp.banner) mediaType = BANNER; + else if (context.imp.video) mediaType = VIDEO; + } + if (mediaType) { + context.mediaType = mediaType; + } + return buildBidResponse(bid, context); + } +}); + export const spec = { code: BIDDER_CODE, aliases: [ { code: 'pll' }, { code: 'iionads', gvlid: 1358 }, - { code: 'apester' }, { code: 'adsyield' }, { code: 'tgm' }, { code: 'adtg_org' }, @@ -45,12 +83,12 @@ export const spec = { { code: 'stellorMediaRtb' }, { code: 'smootai' }, { code: 'anzuExchange' }, - { code: 'adnimation' }, { code: 'rtbdemand' }, { code: 'altstar' }, { code: 'vaayaMedia' }, { code: 'performist' }, - { code: 'oveeo' } + { code: 'oveeo' }, + { code: 'embimedia' } ], supportedMediaTypes: [BANNER, VIDEO], @@ -66,22 +104,62 @@ export const spec = { }, /** - * Make a server request from the list of BidRequests. + * Make a server request from the list of BidRequests using OpenRTB format. * - * @return ServerRequest Info describing the request to the server. + * @param {BidRequest[]} validBidRequests - Array of valid bid requests + * @param {Object} bidderRequest - The bidder request object + * @return {Object[]} Array of server requests */ buildRequests: (validBidRequests, bidderRequest) => { - let winTop; - try { - winTop = window.top; - winTop.location.toString(); - } catch (e) { - logMessage(e); - winTop = window; - } - const placements = groupBy(validBidRequests.map(bidRequest => buildPlacement(bidRequest)), 'host') - return Object.keys(placements) - .map(host => buildRequest(winTop, host, placements[host].map(placement => placement.adUnit), bidderRequest)); + const normalizedBids = validBidRequests.map(bid => { + const adUnitType = bid.params.adUnitType || BANNER + if (!bid.mediaTypes && bid.sizes) { + if (adUnitType === BANNER) { + return { ...bid, mediaTypes: { banner: { sizes: bid.sizes } } }; + } else { + return { ...bid, mediaTypes: { video: { playerSize: bid.sizes } } }; + } + } + if (bid.mediaTypes && bid.sizes) { + const mediaTypes = { ...bid.mediaTypes }; + if (adUnitType === BANNER && mediaTypes.banner) { + mediaTypes.banner = { + ...mediaTypes.banner, + sizes: (mediaTypes.banner.sizes || []).concat(bid.sizes) + }; + } + if (adUnitType === VIDEO && mediaTypes.video) { + mediaTypes.video = { + ...mediaTypes.video, + playerSize: (mediaTypes.video.playerSize || []).concat(bid.sizes) + }; + } + return { ...bid, mediaTypes }; + } + return bid; + }); + const bidRequestsByHost = normalizedBids.reduce((groups, bid) => { + const host = bid.params.host; + groups[host] = groups[host] || []; + groups[host].push(bid); + return groups; + }, {}); + const enrichedBidderRequest = { + ...bidderRequest, + ortb2: { + ...bidderRequest.ortb2, + site: { + ...bidderRequest.ortb2?.site, + page: bidderRequest.ortb2?.site?.page || bidderRequest.refererInfo?.page + } + } + }; + + return Object.entries(bidRequestsByHost).map(([host, bids]) => ({ + method: 'POST', + url: `https://${host}/ortbhb`, + data: converter.toORTB({ bidRequests: bids, bidderRequest: enrichedBidderRequest }) + })); }, /** @@ -100,22 +178,20 @@ export const spec = { }, /** - * Unpack the response from the server into a list of bids. + * Unpack the OpenRTB response from the server into a list of bids. * - * @param {ServerResponse} serverResponse A successful response from the server. - * @return {Bid[]} An array of bids which were nested inside the server. + * @param {ServerResponse} response - A successful response from the server + * @param {Object} request - The request object that was sent + * @return {Bid[]} An array of bids */ - interpretResponse: (serverResponse, bidRequest) => { - const bidResponses = []; - const serverBody = serverResponse.body; - const len = serverBody.length; - for (let i = 0; i < len; i++) { - const bidResponse = serverBody[i]; - if (isBidResponseValid(bidResponse)) { - bidResponses.push(bidResponse); - } + interpretResponse: (response, request) => { + if (!response.body) { + return []; } - return bidResponses; + return converter.fromORTB({ + response: response.body, + request: request.data + }).bids.filter(bid => isBidResponseValid(bid)); }, getUserSyncs: (syncOptions, serverResponses, gdprConsent, uspConsent) => { @@ -137,74 +213,3 @@ export const spec = { }; registerBidder(spec); - -function buildRequest(winTop, host, adUnits, bidderRequest) { - return { - method: 'POST', - url: `https://${host}/hb`, - data: { - secure: (location.protocol === 'https:'), - deviceWidth: winTop.screen.width, - deviceHeight: winTop.screen.height, - adUnits: adUnits, - ortb2: bidderRequest?.ortb2, - refererInfo: bidderRequest?.refererInfo, - sua: bidderRequest?.ortb2?.device?.sua, - page: bidderRequest?.ortb2?.site?.page || bidderRequest?.refererInfo?.page - } - } -} - -function buildPlacement(bidRequest) { - let sizes; - if (bidRequest.mediaTypes) { - switch (bidRequest.params.adUnitType) { - case BANNER: - if (bidRequest.mediaTypes.banner && bidRequest.mediaTypes.banner.sizes) { - sizes = bidRequest.mediaTypes.banner.sizes; - } - break; - case VIDEO: - if (bidRequest.mediaTypes.video && bidRequest.mediaTypes.video.playerSize) { - sizes = [bidRequest.mediaTypes.video.playerSize]; - } - break; - } - } - sizes = (sizes || []).concat(bidRequest.sizes || []); - return { - host: bidRequest.params.host, - adUnit: { - id: bidRequest.params.adUnitId, - bidId: bidRequest.bidId, - transactionId: bidRequest.ortb2Imp?.ext?.tid, - sizes: sizes.map(size => { - let floorInfo = null; - if (typeof bidRequest.getFloor === 'function') { - try { - floorInfo = bidRequest.getFloor({ - currency: 'USD', - mediaType: bidRequest.params.adUnitType, - size: [size[0], size[1]] - }); - } catch (e) {} - } - return { - width: size[0], - height: size[1], - floorInfo: floorInfo - }; - }), - type: bidRequest.params.adUnitType.toUpperCase(), - ortb2Imp: bidRequest.ortb2Imp, - publisherId: bidRequest.params.publisherId, - userIdAsEids: bidRequest.userIdAsEids, - supplyChain: bidRequest?.ortb2?.source?.ext?.schain, - custom1: bidRequest.params.custom1, - custom2: bidRequest.params.custom2, - custom3: bidRequest.params.custom3, - custom4: bidRequest.params.custom4, - custom5: bidRequest.params.custom5 - } - } -} diff --git a/modules/liveIntentAnalyticsAdapter.js b/modules/liveIntentAnalyticsAdapter.js index 1a548bfcd9c..6733f441159 100644 --- a/modules/liveIntentAnalyticsAdapter.js +++ b/modules/liveIntentAnalyticsAdapter.js @@ -7,7 +7,7 @@ import { getRefererInfo } from '../src/refererDetection.js'; import { config as prebidConfig } from '../src/config.js'; import { auctionManager } from '../src/auctionManager.js'; -import {getGlobalVarName} from '../src/buildOptions.js'; +import { getGlobalVarName } from '../src/buildOptions.js'; const ANALYTICS_TYPE = 'endpoint'; const URL = 'https://wba.liadm.com/analytic-events'; @@ -19,7 +19,7 @@ const INTEGRATION_ID = getGlobalVarName(); let partnerIdFromUserIdConfig; let sendAuctionInitEvents; -const liAnalytics = Object.assign(adapter({URL, ANALYTICS_TYPE}), { +const liAnalytics = Object.assign(adapter({ URL, ANALYTICS_TYPE }), { track({ eventType, args }) { switch (eventType) { case AUCTION_INIT: @@ -66,7 +66,7 @@ function handleAuctionInitEvent(auctionInitEvent) { } function handleBidWonEvent(bidWonEvent) { - const auction = auctionManager.index.getAuction({auctionId: bidWonEvent.auctionId}); + const auction = auctionManager.index.getAuction({ auctionId: bidWonEvent.auctionId }); const liveIntentIdsPresent = checkLiveIntentIdsPresent(auction?.getBidRequests()) // This is for old integration that enable or disable the user id module diff --git a/modules/liveIntentRtdProvider.js b/modules/liveIntentRtdProvider.js index 92cd09ae346..affe336d1ca 100644 --- a/modules/liveIntentRtdProvider.js +++ b/modules/liveIntentRtdProvider.js @@ -2,7 +2,7 @@ * This module adds the LiveIntent provider to the Real Time Data module (rtdModule). */ import { submodule } from '../src/hook.js'; -import {deepAccess, deepSetValue} from '../src/utils.js' +import { deepAccess, deepSetValue } from '../src/utils.js' /** * @typedef {import('../modules/rtdModule/index.js').RtdSubmodule} RtdSubmodule diff --git a/modules/livewrappedAnalyticsAdapter.js b/modules/livewrappedAnalyticsAdapter.js index 37b73368fdc..686d90b7cc7 100644 --- a/modules/livewrappedAnalyticsAdapter.js +++ b/modules/livewrappedAnalyticsAdapter.js @@ -1,9 +1,10 @@ import { timestamp, logInfo } from '../src/utils.js'; -import {ajax} from '../src/ajax.js'; +import { ajax } from '../src/ajax.js'; import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import { EVENTS } from '../src/constants.js'; import adapterManager from '../src/adapterManager.js'; import { getGlobal } from '../src/prebidGlobal.js'; +import { getAdUnitElement } from '../src/utils/adUnits.js'; const ANALYTICSTYPE = 'endpoint'; const URL = 'https://lwadm.com/analytics/10'; @@ -23,15 +24,15 @@ const cache = { auctions: {} }; -const livewrappedAnalyticsAdapter = Object.assign(adapter({EMPTYURL, ANALYTICSTYPE}), { - track({eventType, args}) { +const livewrappedAnalyticsAdapter = Object.assign(adapter({ EMPTYURL, ANALYTICSTYPE }), { + track({ eventType, args }) { const time = timestamp(); logInfo('LIVEWRAPPED_EVENT:', [eventType, args]); switch (eventType) { case EVENTS.AUCTION_INIT: logInfo('LIVEWRAPPED_AUCTION_INIT:', args); - cache.auctions[args.auctionId] = {bids: {}, bidAdUnits: {}}; + cache.auctions[args.auctionId] = { bids: {}, bidAdUnits: {} }; break; case EVENTS.BID_REQUESTED: logInfo('LIVEWRAPPED_BID_REQUESTED:', args); @@ -41,7 +42,7 @@ const livewrappedAnalyticsAdapter = Object.assign(adapter({EMPTYURL, ANALYTICSTY cache.auctions[args.auctionId].gdprApplies = args.gdprConsent ? args.gdprConsent.gdprApplies : undefined; cache.auctions[args.auctionId].gdprConsent = args.gdprConsent ? args.gdprConsent.consentString : undefined; let lwFloor; - const container = document.getElementById(bidRequest.adUnitCode); + const container = getAdUnitElement(bidRequest); let adUnitId = container ? container.getAttribute('data-adunitid') : undefined; adUnitId = adUnitId != null ? adUnitId : undefined; @@ -189,7 +190,7 @@ livewrappedAnalyticsAdapter.sendEvents = function() { return; } - ajax(initOptions.endpoint || URL, undefined, JSON.stringify(events), {method: 'POST'}); + ajax(initOptions.endpoint || URL, undefined, JSON.stringify(events), { method: 'POST' }); setTimeout(() => { sentRequests.auctionIds.forEach(id => { @@ -233,7 +234,7 @@ function getSentRequests() { }); }); - return {gdpr: gdpr, auctionIds: auctionIds, sentRequests: sentRequests}; + return { gdpr: gdpr, auctionIds: auctionIds, sentRequests: sentRequests }; } function getResponses(gdpr, auctionIds) { @@ -309,7 +310,7 @@ function getGdprPos(gdpr, auction) { } if (gdprPos === gdpr.length) { - gdpr[gdprPos] = {gdprApplies: auction.gdprApplies, gdprConsent: auction.gdprConsent}; + gdpr[gdprPos] = { gdprApplies: auction.gdprApplies, gdprConsent: auction.gdprConsent }; } return gdprPos; diff --git a/modules/livewrappedBidAdapter.js b/modules/livewrappedBidAdapter.js index 615c81fb098..922b0d46ef0 100644 --- a/modules/livewrappedBidAdapter.js +++ b/modules/livewrappedBidAdapter.js @@ -1,8 +1,8 @@ -import {deepAccess, getWindowTop, isSafariBrowser, mergeDeep, isFn, isPlainObject, getWinDimensions} from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {config} from '../src/config.js'; -import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js'; -import {getStorageManager} from '../src/storageManager.js'; +import { deepAccess, getWindowTop, isSafariBrowser, mergeDeep, isFn, isPlainObject, getWinDimensions } from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { config } from '../src/config.js'; +import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; +import { getStorageManager } from '../src/storageManager.js'; import { getCurrencyFromBidderRequest } from '../libraries/ortb2Utils/currency.js'; /** @@ -11,7 +11,7 @@ import { getCurrencyFromBidderRequest } from '../libraries/ortb2Utils/currency.j */ const BIDDER_CODE = 'livewrapped'; -export const storage = getStorageManager({bidderCode: BIDDER_CODE}); +export const storage = getStorageManager({ bidderCode: BIDDER_CODE }); export const URL = 'https://lwadm.com/ad'; const VERSION = '1.4'; @@ -112,7 +112,8 @@ export const spec = { return { method: 'POST', url: bidUrl, - data: payloadString}; + data: payloadString + }; }, /** @@ -174,11 +175,11 @@ export const spec = { userSync.forEach(function(sync) { if (syncOptions.pixelEnabled && sync.type === 'Redirect') { - syncList.push({type: 'image', url: sync.url}); + syncList.push({ type: 'image', url: sync.url }); } if (syncOptions.iframeEnabled && sync.type === 'Iframe') { - syncList.push({type: 'iframe', url: sync.url}); + syncList.push({ type: 'iframe', url: sync.url }); } }); @@ -301,7 +302,7 @@ function getAdblockerRecovered() { function handleEids(bidRequests) { const bidRequest = bidRequests[0]; if (bidRequest && bidRequest.userIdAsEids) { - return {user: {ext: {eids: bidRequest.userIdAsEids}}}; + return { user: { ext: { eids: bidRequest.userIdAsEids } } }; } return undefined; diff --git a/modules/lkqdBidAdapter.js b/modules/lkqdBidAdapter.js index 535dbbf759b..a017b3c2eb3 100644 --- a/modules/lkqdBidAdapter.js +++ b/modules/lkqdBidAdapter.js @@ -46,7 +46,6 @@ export const spec = { const DOMAIN = bid.params.pageurl || REFERER; const GDPR = BIDDER_GDPR || bid.params.gdpr || null; const GDPRS = BIDDER_GDPRS || bid.params.gdprs || null; - const DNT = bid.params.dnt || null; const BID_FLOOR = 0; const VIDEO_BID = bid.video ? bid.video : {}; @@ -76,10 +75,6 @@ export const spec = { } }; - if (isSet(DNT)) { - requestData.device.dnt = DNT; - } - if (isSet(config.getConfig('coppa'))) { requestData.regs.coppa = config.getConfig('coppa') === true ? 1 : 0; } diff --git a/modules/lm_kiviadsBidAdapter.js b/modules/lm_kiviadsBidAdapter.js index 7295eb33258..01eab4a86c3 100644 --- a/modules/lm_kiviadsBidAdapter.js +++ b/modules/lm_kiviadsBidAdapter.js @@ -1,6 +1,6 @@ -import {BANNER, VIDEO} from '../src/mediaTypes.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {buildRequests, getUserSyncs, interpretResponse, isBidRequestValid} from '../libraries/xeUtils/bidderUtils.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { buildRequests, getUserSyncs, interpretResponse, isBidRequestValid } from '../libraries/xeUtils/bidderUtils.js'; const BIDDER_CODE = 'lm_kiviads'; const ENDPOINT = 'https://pbjs.kiviads.live'; diff --git a/modules/locIdSystem.js b/modules/locIdSystem.js new file mode 100644 index 00000000000..5e06e29b302 --- /dev/null +++ b/modules/locIdSystem.js @@ -0,0 +1,676 @@ +/** + * This file is licensed under the Apache 2.0 license. + * http://www.apache.org/licenses/LICENSE-2.0 + */ + +/** + * This module adds LocID to the User ID module + * The {@link module:modules/userId} module is required. + * @module modules/locIdSystem + * @requires module:modules/userId + */ + +import { logWarn, logError } from '../src/utils.js'; +import { submodule } from '../src/hook.js'; +import { gppDataHandler, uspDataHandler } from '../src/adapterManager.js'; +import { ajaxBuilder } from '../src/ajax.js'; +import { getStorageManager } from '../src/storageManager.js'; +import { VENDORLESS_GVLID } from '../src/consentHandler.js'; +import { MODULE_TYPE_UID } from '../src/activities/modules.js'; + +const MODULE_NAME = 'locId'; +const LOG_PREFIX = 'LocID:'; +const DEFAULT_TIMEOUT_MS = 800; +const DEFAULT_EID_SOURCE = 'locid.com'; +// EID atype: 1 = AdCOM AgentTypeWeb (agent type for web environments) +const DEFAULT_EID_ATYPE = 1; +const MAX_ID_LENGTH = 512; +const MAX_CONNECTION_IP_LENGTH = 64; +const DEFAULT_IP_CACHE_TTL_MS = 4 * 60 * 60 * 1000; // 4 hours +const IP_CACHE_SUFFIX = '_ip'; + +export const storage = getStorageManager({ moduleType: MODULE_TYPE_UID, moduleName: MODULE_NAME }); + +/** + * Normalizes privacy mode config to a boolean flag. + * Supports both requirePrivacySignals (boolean) and privacyMode (string enum). + * + * Precedence: requirePrivacySignals (if true) takes priority over privacyMode. + * - privacyMode is the preferred high-level setting for new integrations. + * - requirePrivacySignals exists for backwards compatibility with integrators + * who prefer a simple boolean. + * + * @param {Object} params - config.params + * @returns {boolean} true if privacy signals are required, false otherwise + */ +function shouldRequirePrivacySignals(params) { + // requirePrivacySignals=true takes precedence (backwards compatibility) + if (params?.requirePrivacySignals === true) { + return true; + } + if (params?.privacyMode === 'requireSignals') { + return true; + } + // Default: allowWithoutSignals + return false; +} + +function getUspConsent(consentData) { + if (consentData && consentData.usp != null) { + return consentData.usp; + } + return consentData?.uspConsent; +} + +function getGppConsent(consentData) { + if (consentData && consentData.gpp != null) { + return consentData.gpp; + } + return consentData?.gppConsent; +} + +/** + * Checks if any privacy signals are present in consentData or data handlers. + * + * IMPORTANT: gdprApplies alone does NOT count as a privacy signal. + * A publisher may set gdprApplies=true without having a CMP installed. + * We only consider GDPR signals "present" when actual consent framework + * artifacts exist (consentString, vendorData). This supports LI-based + * operation where no TCF consent string is required. + * + * "Signals present" means ANY of the following are available: + * - consentString or gdpr.consentString (indicates CMP provided framework data) + * - vendorData or gdpr.vendorData (indicates CMP provided vendor data) + * - usp or uspConsent (US Privacy string) + * - gpp or gppConsent (GPP consent data) + * - Data from gppDataHandler or uspDataHandler + * + * @param {Object} consentData - The consent data object passed to getId + * @returns {boolean} true if any privacy signals are present + */ +function hasPrivacySignals(consentData) { + // Check GDPR-related signals (flat and nested) + // NOTE: gdprApplies alone is NOT a signal - it just indicates jurisdiction. + // A signal requires actual CMP artifacts (consentString or vendorData). + if (consentData?.consentString || consentData?.gdpr?.consentString) { + return true; + } + if (consentData?.vendorData || consentData?.gdpr?.vendorData) { + return true; + } + + // Check USP consent + const uspConsent = getUspConsent(consentData); + if (uspConsent) { + return true; + } + + // Check GPP consent + const gppConsent = getGppConsent(consentData); + if (gppConsent) { + return true; + } + + // Check data handlers + const uspFromHandler = uspDataHandler.getConsentData(); + if (uspFromHandler) { + return true; + } + + const gppFromHandler = gppDataHandler.getConsentData(); + if (gppFromHandler) { + return true; + } + + return false; +} + +function isValidId(id) { + return typeof id === 'string' && id.trim().length > 0 && id.length <= MAX_ID_LENGTH; +} + +function isValidConnectionIp(ip) { + return typeof ip === 'string' && ip.length > 0 && ip.length <= MAX_CONNECTION_IP_LENGTH; +} + +function normalizeStoredId(storedId) { + if (!storedId) { + return null; + } + if (typeof storedId === 'string') { + return null; + } + if (typeof storedId === 'object') { + // Preserve explicit null for id (means "empty tx_cloc, valid cached response"). + // 'id' in storedId is needed because ?? treats null as nullish and would + // incorrectly fall through to tx_cloc. + const hasExplicitId = 'id' in storedId; + const id = hasExplicitId ? storedId.id : (storedId.tx_cloc ?? null); + const connectionIp = storedId.connectionIp ?? storedId.connection_ip; + return { ...storedId, id, connectionIp }; + } + return null; +} + +/** + * Checks privacy framework signals. Returns true if ID operations are allowed. + * + * LocID operates under Legitimate Interest and does not require a TCF consent + * string when no privacy framework is present. When privacy signals exist, + * framework processing restrictions are enforced. + * + * @param {Object} consentData - The consent data object from Prebid + * @param {Object} params - config.params for privacy mode settings + * @returns {boolean} true if ID operations are allowed + */ +function hasValidConsent(consentData, params) { + const requireSignals = shouldRequirePrivacySignals(params); + const signalsPresent = hasPrivacySignals(consentData); + + // B) If privacy signals are NOT present + if (!signalsPresent) { + if (requireSignals) { + logWarn(LOG_PREFIX, 'Privacy signals required but none present'); + return false; + } + // Default: allow operation without privacy signals (LI-based operation) + return true; + } + + // A) Privacy signals ARE present - enforce applicable restrictions + // + // Note: privacy signals can come from GDPR, USP, or GPP. GDPR checks only + // apply when GDPR is flagged AND CMP artifacts (consentString/vendorData) + // are present. gdprApplies alone does not trigger GDPR enforcement. + + // Check GDPR - support both flat and nested shapes + const gdprApplies = consentData?.gdprApplies === true || consentData?.gdpr?.gdprApplies === true; + const consentString = consentData?.consentString || consentData?.gdpr?.consentString; + const vendorData = consentData?.vendorData || consentData?.gdpr?.vendorData; + const gdprCmpArtifactsPresent = !!(consentString || vendorData); + + if (gdprApplies && gdprCmpArtifactsPresent) { + // When GDPR applies AND we have CMP signals, require consentString + if (!consentString || consentString.length === 0) { + logWarn(LOG_PREFIX, 'GDPR framework data missing consent string'); + return false; + } + } + + // Check USP for processing restriction + const uspData = getUspConsent(consentData) ?? uspDataHandler.getConsentData(); + if (uspData && uspData.length >= 3 && uspData.charAt(2) === 'Y') { + logWarn(LOG_PREFIX, 'US Privacy framework processing restriction detected'); + return false; + } + + // Check GPP for processing restriction + const gppData = getGppConsent(consentData) ?? gppDataHandler.getConsentData(); + if (gppData?.applicableSections?.includes(7) && + gppData?.parsedSections?.usnat?.KnownChildSensitiveDataConsents?.includes(1)) { + logWarn(LOG_PREFIX, 'GPP usnat KnownChildSensitiveDataConsents processing restriction detected'); + return false; + } + + return true; +} + +function parseEndpointResponse(response) { + if (!response) { + return null; + } + try { + return typeof response === 'string' ? JSON.parse(response) : response; + } catch (e) { + logError(LOG_PREFIX, 'Error parsing endpoint response:', e.message); + return null; + } +} + +/** + * Extracts LocID from endpoint response. + * Only tx_cloc is accepted. + */ +function extractLocIdFromResponse(parsed) { + if (!parsed) return null; + + if (isValidId(parsed.tx_cloc)) { + return parsed.tx_cloc; + } + + logWarn(LOG_PREFIX, 'Could not extract valid tx_cloc from response'); + return null; +} + +function extractConnectionIp(parsed) { + if (!parsed) { + return null; + } + const connectionIp = parsed.connection_ip ?? parsed.connectionIp; + return isValidConnectionIp(connectionIp) ? connectionIp : null; +} + +function getIpCacheKey(config) { + const baseName = config?.storage?.name || '_locid'; + return config?.params?.ipCacheName || (baseName + IP_CACHE_SUFFIX); +} + +function getIpCacheTtlMs(config) { + const ttl = config?.params?.ipCacheTtlMs; + return (typeof ttl === 'number' && ttl > 0) ? ttl : DEFAULT_IP_CACHE_TTL_MS; +} + +function readIpCache(config) { + try { + const key = getIpCacheKey(config); + const raw = storage.getDataFromLocalStorage(key); + if (!raw) return null; + const entry = JSON.parse(raw); + if (!entry || typeof entry !== 'object') return null; + if (!isValidConnectionIp(entry.ip)) return null; + if (typeof entry.expiresAt === 'number' && Date.now() > entry.expiresAt) return null; + return entry; + } catch (e) { + logWarn(LOG_PREFIX, 'Error reading IP cache:', e.message); + return null; + } +} + +function writeIpCache(config, ip) { + if (!isValidConnectionIp(ip)) return; + try { + const key = getIpCacheKey(config); + const nowMs = Date.now(); + const ttlMs = getIpCacheTtlMs(config); + const entry = { + ip: ip, + fetchedAt: nowMs, + expiresAt: nowMs + ttlMs + }; + storage.setDataInLocalStorage(key, JSON.stringify(entry)); + } catch (e) { + logWarn(LOG_PREFIX, 'Error writing IP cache:', e.message); + } +} + +/** + * Parses an IP response from an IP-only endpoint. + * Supports JSON ({ip: "..."}, {connection_ip: "..."}) and plain text IP. + */ +function parseIpResponse(response) { + if (!response) return null; + + if (typeof response === 'string') { + const trimmed = response.trim(); + if (trimmed.charAt(0) === '{') { + try { + const parsed = JSON.parse(trimmed); + const ip = parsed.ip || parsed.connection_ip || parsed.connectionIp; + return isValidConnectionIp(ip) ? ip : null; + } catch (e) { + // Not valid JSON, try as plain text + } + } + return isValidConnectionIp(trimmed) ? trimmed : null; + } + + if (typeof response === 'object') { + const ip = response.ip || response.connection_ip || response.connectionIp; + return isValidConnectionIp(ip) ? ip : null; + } + + return null; +} + +/** + * Checks if a stored tx_cloc entry is valid for reuse. + * Accepts both valid id strings AND null (empty tx_cloc is a valid cached result). + */ +function isStoredEntryReusable(normalizedStored, currentIp) { + if (!normalizedStored || !isValidConnectionIp(normalizedStored.connectionIp)) { + return false; + } + if (isExpired(normalizedStored)) { + return false; + } + if (currentIp && normalizedStored.connectionIp !== currentIp) { + return false; + } + // id must be either a valid string or explicitly null (empty tx_cloc) + return normalizedStored.id === null || isValidId(normalizedStored.id); +} + +function getExpiresAt(config, nowMs) { + const expiresDays = config?.storage?.expires; + if (typeof expiresDays !== 'number' || expiresDays <= 0) { + return undefined; + } + return nowMs + (expiresDays * 24 * 60 * 60 * 1000); +} + +function buildStoredId(id, connectionIp, config) { + const nowMs = Date.now(); + return { + id, + connectionIp, + createdAt: nowMs, + updatedAt: nowMs, + expiresAt: getExpiresAt(config, nowMs) + }; +} + +function isExpired(storedEntry) { + return typeof storedEntry?.expiresAt === 'number' && Date.now() > storedEntry.expiresAt; +} + +/** + * Builds the request URL, appending altId if configured. + * Preserves URL fragments by appending query params before the hash. + */ +function buildRequestUrl(endpoint, altId) { + if (!altId) { + return endpoint; + } + + // Split on hash to preserve fragment + const hashIndex = endpoint.indexOf('#'); + let base = endpoint; + let fragment = ''; + + if (hashIndex !== -1) { + base = endpoint.substring(0, hashIndex); + fragment = endpoint.substring(hashIndex); + } + + const separator = base.includes('?') ? '&' : '?'; + return `${base}${separator}alt_id=${encodeURIComponent(altId)}${fragment}`; +} + +/** + * Fetches LocID from the configured endpoint (GET only). + */ +function fetchLocIdFromEndpoint(config, callback) { + const params = config?.params || {}; + const endpoint = params.endpoint; + const timeoutMs = params.timeoutMs || DEFAULT_TIMEOUT_MS; + + if (!endpoint) { + logError(LOG_PREFIX, 'No endpoint configured'); + callback(undefined); + return; + } + + const requestUrl = buildRequestUrl(endpoint, params.altId); + + const requestOptions = { + method: 'GET', + contentType: 'application/json', + withCredentials: params.withCredentials === true + }; + + // Add x-api-key header if apiKey is configured + if (params.apiKey) { + requestOptions.customHeaders = { + 'x-api-key': params.apiKey + }; + } + + let callbackFired = false; + const safeCallback = (result) => { + if (!callbackFired) { + callbackFired = true; + callback(result); + } + }; + + const onSuccess = (response) => { + const parsed = parseEndpointResponse(response); + if (!parsed) { + safeCallback(undefined); + return; + } + const connectionIp = extractConnectionIp(parsed); + if (!connectionIp) { + logWarn(LOG_PREFIX, 'Missing or invalid connection_ip in response'); + safeCallback(undefined); + return; + } + // tx_cloc may be null (empty/missing for this IP) -- this is a valid cacheable result. + // connection_ip is always required. + const locId = extractLocIdFromResponse(parsed); + writeIpCache(config, connectionIp); + safeCallback(buildStoredId(locId, connectionIp, config)); + }; + + const onError = (error) => { + logWarn(LOG_PREFIX, 'Request failed:', error); + safeCallback(undefined); + }; + + try { + const ajax = ajaxBuilder(timeoutMs); + ajax(requestUrl, { success: onSuccess, error: onError }, null, requestOptions); + } catch (e) { + logError(LOG_PREFIX, 'Error initiating request:', e.message); + safeCallback(undefined); + } +} + +/** + * Fetches the connection IP from a separate lightweight endpoint (GET only). + * Callback receives the IP string on success or null on failure. + */ +function fetchIpFromEndpoint(config, callback) { + const params = config?.params || {}; + const ipEndpoint = params.ipEndpoint; + const timeoutMs = params.timeoutMs || DEFAULT_TIMEOUT_MS; + + if (!ipEndpoint) { + callback(null); + return; + } + + let callbackFired = false; + const safeCallback = (result) => { + if (!callbackFired) { + callbackFired = true; + callback(result); + } + }; + + const onSuccess = (response) => { + const ip = parseIpResponse(response); + safeCallback(ip); + }; + + const onError = (error) => { + logWarn(LOG_PREFIX, 'IP endpoint request failed:', error); + safeCallback(null); + }; + + try { + const ajax = ajaxBuilder(timeoutMs); + const requestOptions = { + method: 'GET', + withCredentials: params.withCredentials === true + }; + if (params.apiKey) { + requestOptions.customHeaders = { + 'x-api-key': params.apiKey + }; + } + ajax(ipEndpoint, { success: onSuccess, error: onError }, null, requestOptions); + } catch (e) { + logError(LOG_PREFIX, 'Error initiating IP request:', e.message); + safeCallback(null); + } +} + +export const locIdSubmodule = { + name: MODULE_NAME, + aliasName: 'locid', + gvlid: VENDORLESS_GVLID, + + /** + * Decode stored value into userId object. + */ + decode(value) { + if (!value || typeof value !== 'object') { + return undefined; + } + const id = value?.id ?? value?.tx_cloc; + const connectionIp = value?.connectionIp ?? value?.connection_ip; + if (isValidId(id) && isValidConnectionIp(connectionIp)) { + return { locId: id }; + } + return undefined; + }, + + /** + * Get the LocID from endpoint. + * Returns {id} for sync or {callback} for async per Prebid patterns. + * + * Two-tier cache: IP cache (4h default) and tx_cloc cache (7d default). + * IP is refreshed more frequently to detect network changes while keeping + * tx_cloc stable for its full cache period. + */ + getId(config, consentData, storedId) { + const params = config?.params || {}; + + // Check privacy restrictions first + if (!hasValidConsent(consentData, params)) { + return undefined; + } + + const normalizedStored = normalizeStoredId(storedId); + const cachedIp = readIpCache(config); + + // Step 1: IP cache is valid -- check if tx_cloc matches + if (cachedIp) { + if (isStoredEntryReusable(normalizedStored, cachedIp.ip)) { + return { id: normalizedStored }; + } + // IP cached but tx_cloc missing, expired, or IP mismatch -- full fetch + return { + callback: (callback) => { + fetchLocIdFromEndpoint(config, callback); + } + }; + } + + // Step 2: IP cache expired or missing + if (params.ipEndpoint) { + // Two-call optimization: lightweight IP check first + return { + callback: (callback) => { + fetchIpFromEndpoint(config, (freshIp) => { + if (!freshIp) { + // IP fetch failed; fall back to main endpoint + fetchLocIdFromEndpoint(config, callback); + return; + } + writeIpCache(config, freshIp); + // Check if stored tx_cloc matches the fresh IP + if (isStoredEntryReusable(normalizedStored, freshIp)) { + callback(normalizedStored); + return; + } + // IP changed or no valid tx_cloc -- full fetch + fetchLocIdFromEndpoint(config, callback); + }); + } + }; + } + + // Step 3: No ipEndpoint configured -- call main endpoint to refresh IP. + // Only update tx_cloc if IP changed or tx_cloc cache expired. + return { + callback: (callback) => { + fetchLocIdFromEndpoint(config, (freshEntry) => { + if (!freshEntry) { + callback(undefined); + return; + } + // Honor empty tx_cloc: if the server returned null, use the fresh + // entry so stale identifiers are cleared (cached as id: null). + if (freshEntry.id === null) { + callback(freshEntry); + return; + } + // IP is already cached by fetchLocIdFromEndpoint's onSuccess. + // Check if we should preserve the existing tx_cloc (avoid churning it). + if (normalizedStored?.id !== null && isStoredEntryReusable(normalizedStored, freshEntry.connectionIp)) { + callback(normalizedStored); + return; + } + // IP changed or tx_cloc expired/missing -- use fresh entry + callback(freshEntry); + }); + } + }; + }, + + /** + * Extend existing LocID. + * Accepts id: null (empty tx_cloc) as a valid cached result. + * If IP cache is missing/expired/mismatched, return a callback to refresh. + */ + extendId(config, consentData, storedId) { + const normalizedStored = normalizeStoredId(storedId); + if (!normalizedStored || !isValidConnectionIp(normalizedStored.connectionIp)) { + return undefined; + } + // Accept both valid id strings AND null (empty tx_cloc is a valid cached result) + if (normalizedStored.id !== null && !isValidId(normalizedStored.id)) { + return undefined; + } + if (isExpired(normalizedStored)) { + return undefined; + } + if (!hasValidConsent(consentData, config?.params)) { + return undefined; + } + const refreshInSeconds = config?.storage?.refreshInSeconds; + if (typeof refreshInSeconds === 'number' && refreshInSeconds > 0) { + const createdAt = normalizedStored.createdAt; + if (typeof createdAt !== 'number') { + return undefined; + } + const refreshAfterMs = refreshInSeconds * 1000; + if (Date.now() - createdAt >= refreshAfterMs) { + return undefined; + } + } + // Check IP cache -- if expired/missing or IP changed, trigger re-fetch + const cachedIp = readIpCache(config); + if (!cachedIp || cachedIp.ip !== normalizedStored.connectionIp) { + return { + callback: (callback) => { + fetchLocIdFromEndpoint(config, callback); + } + }; + } + return { id: normalizedStored }; + }, + + /** + * EID configuration following standard Prebid shape. + */ + eids: { + locId: { + source: DEFAULT_EID_SOURCE, + atype: DEFAULT_EID_ATYPE, + getValue: function (data) { + if (typeof data === 'string') { + return data; + } + if (!data || typeof data !== 'object') { + return undefined; + } + return data?.id ?? data?.tx_cloc ?? data?.locId ?? data?.locid; + } + } + } +}; + +submodule('userId', locIdSubmodule); diff --git a/modules/locIdSystem.md b/modules/locIdSystem.md new file mode 100644 index 00000000000..6ffe02fe841 --- /dev/null +++ b/modules/locIdSystem.md @@ -0,0 +1,250 @@ +# LocID User ID Submodule + +## Overview + +The LocID User ID submodule retrieves a LocID from a configured first-party endpoint, honors applicable privacy framework processing restrictions when present, persists the identifier using Prebid's storage framework, and exposes the ID to bidders via the standard EIDs interface. + +LocID is a geospatial identifier provided by Digital Envoy. The endpoint is a publisher-controlled, first-party or on-premises service operated by the publisher, GrowthCode, or Digital Envoy. The endpoint derives location information server-side. The browser module does not transmit IP addresses. + +## Configuration + +```javascript +pbjs.setConfig({ + userSync: { + userIds: [{ + name: 'locId', + params: { + endpoint: 'https://id.example.com/locid', + ipEndpoint: 'https://id.example.com/ip' // optional: lightweight IP-only check + }, + storage: { + type: 'html5', + name: '_locid', + expires: 7 + } + }] + } +}); +``` + +## Parameters + +| Parameter | Type | Required | Default | Description | +| ----------------------- | ------- | -------- | ----------------------- | ----------------------------------------------------------------------------- | +| `endpoint` | String | Yes | – | First-party LocID endpoint (see Endpoint Requirements below) | +| `ipEndpoint` | String | No | – | Separate endpoint returning the connection IP (see IP Change Detection below) | +| `ipCacheTtlMs` | Number | No | `14400000` (4h) | TTL for the IP cache entry in milliseconds | +| `ipCacheName` | String | No | `{storage.name}_ip` | localStorage key for the IP cache (auto-derived if not set) | +| `altId` | String | No | – | Alternative identifier appended as `?alt_id=` query param | +| `timeoutMs` | Number | No | `800` | Request timeout in milliseconds | +| `withCredentials` | Boolean | No | `false` | Whether to include credentials on the request | +| `apiKey` | String | No | – | API key sent as `x-api-key` header on `endpoint` and `ipEndpoint` requests | +| `requirePrivacySignals` | Boolean | No | `false` | If `true`, requires privacy signals to be present | +| `privacyMode` | String | No | `'allowWithoutSignals'` | `'allowWithoutSignals'` or `'requireSignals'` | + +**Note on privacy configuration:** `privacyMode` is the preferred high-level setting for new integrations. `requirePrivacySignals` exists for backwards compatibility with integrators who prefer a simple boolean. If `requirePrivacySignals: true` is set, it takes precedence. + +### Endpoint Requirements + +The `endpoint` parameter must point to a **first-party proxy** or **on-premises service**—not the raw LocID Encrypt API directly. + +The LocID Encrypt API (`GET /encrypt?ip=&alt_id=`) requires the client IP address as a parameter. Since browsers cannot reliably determine their own public IP, a server-side proxy is required to: + +1. Receive the request from the browser +2. Extract the client IP from the incoming connection +3. Forward the request to the LocID Encrypt API with the IP injected +4. Return the response with `tx_cloc` and `connection_ip` to the browser (any `stable_cloc` is ignored client-side) + +This architecture ensures the browser never transmits IP addresses and the LocID service receives accurate location data. + +If you configure `altId`, the module appends it as `?alt_id=` to the endpoint URL. Your proxy can then forward this to the LocID API. + +### CORS Configuration + +If your endpoint is on a different origin or you set `withCredentials: true`, ensure your server returns appropriate CORS headers: + +```http +Access-Control-Allow-Origin: +Access-Control-Allow-Credentials: true +``` + +When using `withCredentials`, the server cannot use `Access-Control-Allow-Origin: *`; it must specify the exact origin. + +### Storage Configuration + +| Parameter | Required | Description | +| --------- | -------- | ---------------- | +| `type` | Yes | `'html5'` | +| `name` | Yes | Storage key name | +| `expires` | No | TTL in days | + +### Stored Value Format + +The module stores a structured object (rather than a raw string) so it can track IP-aware metadata: + +```json +{ + "id": "", + "connectionIp": "203.0.113.42", + "createdAt": 1738147200000, + "updatedAt": 1738147200000, + "expiresAt": 1738752000000 +} +``` + +When the endpoint returns a valid `connection_ip` but no usable `tx_cloc` (`null`, missing, empty, or whitespace-only), `id` is stored as `null`. This caches the "no location for this IP" result for the full cache period without re-fetching. The `decode()` function returns `undefined` for `null` IDs, so no EID is emitted in bid requests. + +**Important:** String-only stored values are treated as invalid and are not emitted. + +### IP Cache Format + +The module maintains a separate IP cache entry in localStorage (default key: `{storage.name}_ip`) with a shorter TTL (default 4 hours): + +```json +{ + "ip": "203.0.113.42", + "fetchedAt": 1738147200000, + "expiresAt": 1738161600000 +} +``` + +This entry is managed by the module directly via Prebid's `storageManager` and is independent of the framework-managed tx_cloc cache. + +## Operation Flow + +The module uses a two-tier cache: an IP cache (default 4-hour TTL) and a tx_cloc cache (TTL defined by `storage.expires`). The IP is refreshed more frequently to detect network changes while keeping tx_cloc stable for its full cache period. + +1. The module checks the IP cache for a current connection IP. +2. If the IP cache is valid, the module compares it against the stored tx_cloc entry's `connectionIp`. +3. If the IPs match and the tx_cloc entry is not expired, the cached tx_cloc is reused (even if `null`). +4. If the IP cache is expired or missing and `ipEndpoint` is configured, the module calls `ipEndpoint` to get the current IP, then compares with the stored tx_cloc. If the IPs match, the tx_cloc is reused without calling the main endpoint. +5. If the IPs differ, or the tx_cloc is expired/missing, or `ipEndpoint` is not configured, the module calls the main endpoint to get a fresh tx_cloc and connection IP. +6. The endpoint response may include a `null`, empty, whitespace-only, or missing `tx_cloc` (indicating no location for this IP). This is cached as `id: null` for the full cache period, and overrides any previously stored non-null ID for that same IP. +7. Both the IP cache and tx_cloc cache are updated after each endpoint call. +8. The ID is included in bid requests via the EIDs array. Entries with `null` tx_cloc are omitted from bid requests. + +## Endpoint Response Requirements + +The proxy must return: + +```json +{ + "tx_cloc": "", + "connection_ip": "203.0.113.42" +} +``` + +Notes: + +- `connection_ip` is always required. If missing, the entire response is treated as a failure. +- `tx_cloc` may be `null`, missing, empty, or whitespace-only when no location is available for the IP. This is a valid response and will be cached as `id: null` for the configured cache period. +- `tx_cloc` is the only value the browser module will store/transmit. +- `stable_cloc` may exist in proxy responses for server-side caching, but the client ignores it. + +## IP Change Detection + +The module uses a two-tier cache to detect IP changes without churning the tx_cloc identifier: + +- **IP cache** (default 4-hour TTL): Tracks the current connection IP. Stored in a separate localStorage key (`{storage.name}_ip`). +- **tx_cloc cache** (`storage.expires`): Stores the LocID. Managed by Prebid's userId framework. + +When the IP cache expires, the module refreshes the IP. If the IP is unchanged and the tx_cloc cache is still valid, the existing tx_cloc is reused without calling the main endpoint. + +### ipEndpoint (optional) + +When `ipEndpoint` is configured, the module calls it for lightweight IP-only checks. This avoids a full tx_cloc API call when only the IP needs refreshing. The endpoint should return the connection IP in one of these formats: + +- JSON: `{"ip": "203.0.113.42"}` or `{"connection_ip": "203.0.113.42"}` +- Plain text: `203.0.113.42` + +If `apiKey` is configured, the `x-api-key` header is included on `ipEndpoint` requests using the same `customHeaders` mechanism as the main endpoint. + +When `ipEndpoint` is not configured, the module falls back to calling the main endpoint to refresh the IP, but only updates the stored tx_cloc when the IP has changed or the tx_cloc cache has expired. In this mode, IP changes are only detected when the IP cache is refreshed (for example when it expires and `extendId()` returns a refresh callback); there is no separate lightweight proactive IP probe. + +### Prebid Refresh Triggers + +When `storage.refreshInSeconds` is set, the module will reuse the cached ID until `createdAt + refreshInSeconds`; once due (or if `createdAt` is missing), `extendId()` returns `undefined` to indicate the cached ID should not be reused. The `extendId()` method also checks the IP cache: if the IP cache is expired or missing, or if the cached IP differs from the stored tx_cloc's IP, it returns a callback that refreshes via the main endpoint. This enforces the IP cache TTL (`ipCacheTtlMs`) even when the tx_cloc cache has not expired. + +## Consent Handling + +LocID operates under Legitimate Interest (LI). By default, the module proceeds when no privacy framework signals are present. When privacy signals exist, they are enforced. Privacy frameworks can only stop LocID via global processing restrictions; they do not enable it. +For TCF integration, the module declares Prebid's vendorless GVL marker so purpose-level enforcement applies without vendor-ID checks. + +### Legal Basis and IP-Based Identifiers + +LocID is derived from IP-based geolocation. Because IP addresses are transient and shared, there is no meaningful IP-level choice to express. Privacy frameworks are only consulted to honor rare, publisher- or regulator-level instructions to stop all processing. When such a global processing restriction is signaled, LocID respects it by returning `undefined`. + +### Default Behavior (allowWithoutSignals) + +- **No privacy signals present**: Module proceeds and fetches the ID +- **Privacy signals present**: Enforcement rules apply (see below) + +### Strict Mode (requireSignals) + +Set `requirePrivacySignals: true` or `privacyMode: 'requireSignals'` to require privacy signals: + +```javascript +params: { + endpoint: 'https://id.example.com/locid', + requirePrivacySignals: true +} +``` + +In strict mode, the module returns `undefined` if no privacy signals are present. + +### Privacy Signal Enforcement + +When privacy signals **are** present, the module does not fetch or return an ID if any of the following apply: + +- GDPR applies and vendorData is present, but consentString is missing or empty +- The US Privacy string indicates a global processing restriction (third character is 'Y') +- GPP signals indicate an applicable processing restriction + +When GDPR applies and `consentString` is present, the module proceeds unless a framework processing restriction is signaled. + +### Privacy Signals Detection + +The module considers privacy signals "present" if any of the following exist: + +- `consentString` (TCF consent string from CMP) +- `vendorData` (TCF vendor data from CMP) +- `usp` or `uspConsent` (US Privacy string) +- `gpp` or `gppConsent` (GPP consent data) +- Data from `uspDataHandler` or `gppDataHandler` + +**Important:** `gdprApplies` alone does NOT constitute a privacy signal. A publisher may indicate GDPR jurisdiction without having a CMP installed. TCF framework data is only required when actual CMP artifacts (`consentString` or `vendorData`) are present. This supports Legitimate Interest-based operation in deployments without a full TCF implementation. + +## EID Output + +When available, the LocID is exposed as: + +```javascript +{ + source: "locid.com", + uids: [{ + id: "", + atype: 1 // AdCOM AgentTypeWeb + }] +} +``` + +## Identifier Type + +- **`atype: 1`** — The AdCOM agent type for web (`AgentTypeWeb`). This is used in EID emission. + +`atype` is an OpenRTB agent type (environment), not an IAB GVL vendor ID. + +## Debugging + +```javascript +pbjs.getUserIds().locId +pbjs.refreshUserIds() +localStorage.getItem('_locid') +localStorage.getItem('_locid_ip') // IP cache entry +``` + +## Validation Checklist + +- [ ] EID is present in bid requests when no processing restriction is signaled +- [ ] No network request occurs when a global processing restriction is signaled +- [ ] Stored IDs are reused across page loads diff --git a/modules/lockerdomeBidAdapter.js b/modules/lockerdomeBidAdapter.js index e0be50e6d1c..59c90e3f532 100644 --- a/modules/lockerdomeBidAdapter.js +++ b/modules/lockerdomeBidAdapter.js @@ -1,6 +1,6 @@ -import {BANNER} from '../src/mediaTypes.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {getBidIdParameter} from '../src/utils.js'; +import { BANNER } from '../src/mediaTypes.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { getBidIdParameter } from '../src/utils.js'; export const spec = { code: 'lockerdome', diff --git a/modules/logicadBidAdapter.js b/modules/logicadBidAdapter.js index 64a848c157e..aeff148880f 100644 --- a/modules/logicadBidAdapter.js +++ b/modules/logicadBidAdapter.js @@ -1,5 +1,5 @@ -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER, NATIVE} from '../src/mediaTypes.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, NATIVE } from '../src/mediaTypes.js'; import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; import { deepAccess } from '../src/utils.js'; @@ -42,14 +42,6 @@ export const spec = { bids.push(seatbid.bid); }) - const fledgeAuctionConfigs = deepAccess(serverResponse, 'ext.fledgeAuctionConfigs') || []; - if (fledgeAuctionConfigs.length) { - return { - bids, - paapi: fledgeAuctionConfigs, - }; - } - return bids; }, getUserSyncs: function (syncOptions, serverResponses) { @@ -74,14 +66,6 @@ function newBidRequest(bidRequest, bidderRequest) { mediaTypes: bidRequest.mediaTypes, } - const fledgeEnabled = deepAccess(bidderRequest, 'paapi.enabled') - if (fledgeEnabled) { - const ae = deepAccess(bidRequest, 'ortb2Imp.ext.ae'); - if (ae) { - bid.ae = ae; - } - } - const data = { // TODO: fix auctionId leak: https://github.com/prebid/Prebid.js/issues/9781 auctionId: bidRequest.auctionId, diff --git a/modules/loopmeBidAdapter.js b/modules/loopmeBidAdapter.js index 0cdf03b5f56..8edb980033d 100644 --- a/modules/loopmeBidAdapter.js +++ b/modules/loopmeBidAdapter.js @@ -16,7 +16,7 @@ export const converter = ortbConverter({ }, imp(buildImp, bidRequest, context) { const imp = buildImp(bidRequest, context); - deepSetValue(imp, 'ext.bidder', {...bidRequest.params}); + deepSetValue(imp, 'ext.bidder', { ...bidRequest.params }); return imp; }, request(buildRequest, imps, bidderRequest, context) { @@ -31,19 +31,19 @@ export const spec = { code: BIDDER_CODE, gvlid: GVLID, - isBidRequestValid: ({params = {}}) => Boolean(params.publisherId), + isBidRequestValid: ({ params = {} }) => Boolean(params.publisherId), buildRequests: (bidRequests, bidderRequest) => - ({url, method: 'POST', data: converter.toORTB({bidRequests, bidderRequest})}), + ({ url, method: 'POST', data: converter.toORTB({ bidRequests, bidderRequest }) }), - interpretResponse: ({body}, {data}) => converter.fromORTB({ request: data, response: body }).bids, + interpretResponse: ({ body }, { data }) => converter.fromORTB({ request: data, response: body }).bids, getUserSyncs: (syncOptions, serverResponses) => - serverResponses.flatMap(({body}) => + serverResponses.flatMap(({ body }) => (body.ext?.usersyncs || []) - .filter(({type}) => type === 'image' || type === 'iframe') - .filter(({url}) => url && (url.startsWith('http://') || url.startsWith('https://') || url.startsWith('//'))) - .filter(({type}) => (type === 'image' && syncOptions.pixelEnabled) || (type === 'iframe' && syncOptions.iframeEnabled)) + .filter(({ type }) => type === 'image' || type === 'iframe') + .filter(({ url }) => url && (url.startsWith('http://') || url.startsWith('https://') || url.startsWith('//'))) + .filter(({ type }) => (type === 'image' && syncOptions.pixelEnabled) || (type === 'iframe' && syncOptions.iframeEnabled)) ) } registerBidder(spec); diff --git a/modules/lotamePanoramaIdSystem.js b/modules/lotamePanoramaIdSystem.js index b3a356dbc66..aeccc5ac6b5 100644 --- a/modules/lotamePanoramaIdSystem.js +++ b/modules/lotamePanoramaIdSystem.js @@ -15,8 +15,8 @@ import { } from '../src/utils.js'; import { ajax } from '../src/ajax.js'; import { submodule } from '../src/hook.js'; -import {getStorageManager} from '../src/storageManager.js'; -import {MODULE_TYPE_UID} from '../src/activities/modules.js'; +import { getStorageManager } from '../src/storageManager.js'; +import { MODULE_TYPE_UID } from '../src/activities/modules.js'; /** * @typedef {import('../modules/userId/index.js').Submodule} Submodule @@ -38,7 +38,7 @@ const ID_HOST = 'id.crwdcntrl.net'; const ID_HOST_COOKIELESS = 'c.ltmsphrcl.net'; const DO_NOT_HONOR_CONFIG = false; -export const storage = getStorageManager({moduleType: MODULE_TYPE_UID, moduleName: MODULE_NAME}); +export const storage = getStorageManager({ moduleType: MODULE_TYPE_UID, moduleName: MODULE_NAME }); let cookieDomain; const appliedConfig = { name: 'lotamePanoramaId', diff --git a/modules/luceadBidAdapter.js b/modules/luceadBidAdapter.js index 134b6e505eb..52f7718a7ab 100755 --- a/modules/luceadBidAdapter.js +++ b/modules/luceadBidAdapter.js @@ -2,10 +2,10 @@ * @module modules/luceadBidAdapter */ -import {ortbConverter} from '../libraries/ortbConverter/converter.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {getUniqueIdentifierStr, deepSetValue, logInfo} from '../src/utils.js'; -import {fetch} from '../src/ajax.js'; +import { ortbConverter } from '../libraries/ortbConverter/converter.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { getUniqueIdentifierStr, deepSetValue, logInfo } from '../src/utils.js'; +import { fetch } from '../src/ajax.js'; const bidderCode = 'lucead'; const defaultCurrency = 'EUR'; @@ -105,35 +105,8 @@ function interpretResponse(serverResponse, bidRequest) { }, })); - logInfo('interpretResponse', {serverResponse, bidRequest, bidRequestData, bids}); - - if (response?.enable_pa === false) { return bids; } - - const fledgeAuctionConfigs = (response.bids || []).map(bid => ({ - bidId: bid?.bid_id, - config: { - seller: baseUrl, - decisionLogicUrl: `${baseUrl}/js/ssp.js`, - interestGroupBuyers: [baseUrl], - requestedSize: bid?.size, - auctionSignals: { - size: bid?.size, - }, - perBuyerSignals: { - [baseUrl]: { - prebid_paapi: true, - prebid_bid_id: bid?.bid_id, - prebid_request_id: bidRequestData.request_id, - placement_id: bid.placement_id, - // floor, - is_sra: true, - endpoint_url: endpointUrl, - }, - } - } - })); - - return {bids, paapi: fledgeAuctionConfigs}; + logInfo('interpretResponse', { serverResponse, bidRequest, bidRequestData, bids }); + return { bids }; } function report(type, data) { diff --git a/modules/luponmediaBidAdapter.js b/modules/luponmediaBidAdapter.js index 2e22e10d1cd..a208ed3f831 100755 --- a/modules/luponmediaBidAdapter.js +++ b/modules/luponmediaBidAdapter.js @@ -1,8 +1,8 @@ -import {logError, logMessage, logWarn, deepSetValue} from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER} from '../src/mediaTypes.js'; -import {ortbConverter} from '../libraries/ortbConverter/converter.js'; -import {config} from '../src/config.js'; +import { logError, logMessage, logWarn, deepSetValue } from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER } from '../src/mediaTypes.js'; +import { ortbConverter } from '../libraries/ortbConverter/converter.js'; +import { config } from '../src/config.js'; const BIDDER_CODE = 'luponmedia'; const GVLID = 1132; @@ -102,7 +102,7 @@ export const spec = { }; }, interpretResponse: (response, request) => { - return converter.fromORTB({response: response.body, request: request.data}).bids; + return converter.fromORTB({ response: response.body, request: request.data }).bids; }, getUserSyncs: function (syncOptions, responses) { const allUserSyncs = []; diff --git a/modules/madvertiseBidAdapter.js b/modules/madvertiseBidAdapter.js index 183fca096c3..2d0c0f7ad3a 100644 --- a/modules/madvertiseBidAdapter.js +++ b/modules/madvertiseBidAdapter.js @@ -1,5 +1,5 @@ import { parseSizesInput, _each } from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; /** * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest @@ -65,7 +65,7 @@ export const spec = { return { method: 'GET', url: MADVERTISE_ENDPOINT + src, - options: {withCredentials: false}, + options: { withCredentials: false }, bidId: bidRequest.bidId }; }); diff --git a/modules/magniteAnalyticsAdapter.js b/modules/magniteAnalyticsAdapter.js index cc275bd59f0..bf28dab4fea 100644 --- a/modules/magniteAnalyticsAdapter.js +++ b/modules/magniteAnalyticsAdapter.js @@ -20,11 +20,11 @@ import { import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; import { EVENTS, REJECTION_REASON } from '../src/constants.js'; -import {ajax} from '../src/ajax.js'; -import {config} from '../src/config.js'; -import {getGlobal} from '../src/prebidGlobal.js'; -import {getStorageManager} from '../src/storageManager.js'; -import {MODULE_TYPE_ANALYTICS} from '../src/activities/modules.js'; +import { ajax } from '../src/ajax.js'; +import { config } from '../src/config.js'; +import { getGlobal } from '../src/prebidGlobal.js'; +import { getStorageManager } from '../src/storageManager.js'; +import { MODULE_TYPE_ANALYTICS } from '../src/activities/modules.js'; import { getHook } from '../src/hook.js'; const RUBICON_GVL_ID = 52; @@ -1021,7 +1021,7 @@ magniteAdapter.track = ({ eventType, args }) => { }; const handlePbsAnalytics = function (args) { - const {seatnonbid, auctionId, atag} = args; + const { seatnonbid, auctionId, atag } = args; if (seatnonbid) { handleNonBidEvent(seatnonbid, auctionId); } @@ -1049,10 +1049,10 @@ const handleNonBidEvent = function(seatnonbid, auctionId) { } const adUnits = auction.adUnits; seatnonbid.forEach(seatnonbid => { - const {seat} = seatnonbid; + const { seat } = seatnonbid; seatnonbid.nonbid.forEach(nonbid => { try { - const {status, impid} = nonbid; + const { status, impid } = nonbid; const matchingTid = Object.keys(adUnits).find(tid => adUnits[tid].adUnitCode === impid); const adUnit = adUnits[matchingTid]; const statusInfo = statusMap[status] || { status: 'no-bid' }; diff --git a/modules/magniteBidAdapter.js b/modules/magniteBidAdapter.js new file mode 100644 index 00000000000..7aaa4b76981 --- /dev/null +++ b/modules/magniteBidAdapter.js @@ -0,0 +1,363 @@ +import { config } from '../src/config.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; +import { ortbConverter } from '../libraries/ortbConverter/converter.js'; +import { pbsExtensions } from '../libraries/pbsExtensions/pbsExtensions.js'; +import { + deepSetValue, + formatQS, + mergeDeep, + isPlainObject +} from '../src/utils.js'; +import { + outstreamRenderer, + bidShouldUsePlayerWidthAndHeight +} from '../libraries/magniteUtils/outstream.js'; + +const GVL_ID = 52; +export const REQUEST_URL = 'https://fastlane.rubiconproject.com/a/api/prebid-exchange.json'; +export const SYNC_URL = 'https://eus.rubiconproject.com/usync.html'; +const DEFAULT_INTEGRATION = 'pbjs'; + +let mgniConf = {}; + +// For transition period we need to listen to both rubicon and magnite configs +['magnite', 'rubicon'].forEach(confName => { + // get anything set as of now + mergeDeep(mgniConf, config.getConfig(confName) || {}); + + // listen for future things + config.getConfig(confName, config => { + mergeDeep(mgniConf, config[confName]); + }); +}); +export function resetMgniConf() { + mgniConf = {}; +} + +export const spec = { + code: 'magnite', + gvlid: GVL_ID, + supportedMediaTypes: [BANNER, NATIVE, VIDEO], + isBidRequestValid, + buildRequests, + interpretResponse, + getUserSyncs, + alwaysHasCapacity: true +}; + +registerBidder(spec); + +/** + * Lets Prebid-Core know if the bid is valid before sending it to the adapter + * @param {object} bid + */ +function isBidRequestValid(bid) { + return ['accountId', 'siteId', 'zoneId'].every(param => !Number.isNaN(Number.parseInt(bid?.params?.[param]))); +} + +const posMap = { + atf: 1, + btf: 3 +}; + +export function masSizeOrdering(sizes) { + const MAS_SIZE_PRIORITY = [ + { w: 300, h: 250 }, + { w: 728, h: 90 }, + { w: 160, h: 600 } + ]; + + const compareSizes = (left, right) => left.w === right.w && left.h === right.h; + + return sizes.sort((first, second) => { + // sort by MAS_SIZE_PRIORITY priority order + const firstPriority = MAS_SIZE_PRIORITY.findIndex(masSize => compareSizes(masSize, first)); + const secondPriority = MAS_SIZE_PRIORITY.findIndex(masSize => compareSizes(masSize, second)); + + // Handle cases where only one size is in the priority list + if (firstPriority !== -1 || secondPriority !== -1) { + if (firstPriority === -1) return 1; + if (secondPriority === -1) return -1; + return firstPriority - secondPriority; + } + + // If neither size is in priority list, maintain original order + return 0; + }); +} + +function getPpuidFromEids(eids) { + for (const eid of eids) { + const ppId = eid.uids.find(uid => uid?.ext?.stype === 'ppuid' && uid?.id); + if (ppId) { + return ppId.id; + } + } +} + +function getPpuid(req) { + const user = req.user; + if (user?.id) { + return user.id; + } + + const userConfigId = config.getConfig('user.id'); + if (userConfigId) { + return userConfigId; + } + + const eids = user?.ext?.eids || []; + return getPpuidFromEids(eids); +} + +function cleanFpd(fpdObj) { + // DV+ wants first party data as object of keys / val where val is array + Object.entries(fpdObj || {}).forEach(([key, val]) => { + // if not array, wrap in array + if (!Array.isArray(val)) { + fpdObj[key] = [val]; + } + }); +} + +const converter = ortbConverter({ + context: { + netRevenue: true, + ttl: 300, + currency: 'USD' + }, + processors: pbsExtensions, + imp(buildImp, bidRequest, context) { + // Building imps of request + const imp = buildImp(bidRequest, context); + + // remove any mediaTypes on imp that are not in our context + [BANNER, NATIVE, VIDEO].forEach(mediaType => { + if (mediaType !== context.mediaType) { + delete imp[mediaType]; + } + }); + + // move params to new location and delete old + imp.ext.prebid.bidder.dvplus = imp.ext.prebid.bidder[bidRequest.bidder]; + delete imp.ext?.prebid?.bidder?.[bidRequest.bidder]; + + // Need to convert bad input for visitor and inventory objects (convert all to array of string / numbers) + ['visitor', 'inventory'].forEach(fpdParam => { + if (isPlainObject(imp.ext.prebid.bidder.dvplus[fpdParam])) { + cleanFpd(imp.ext.prebid.bidder.dvplus[fpdParam]); + } + }); + + // Signal which formats are in the bid request (if more than one) + if (Object.keys(bidRequest.mediaTypes).length > 1) { + deepSetValue(imp, 'ext.rp.rtb.formats', Object.keys(bidRequest.mediaTypes)); + } + + // If params has pos and not already set by ORTB Converter => set it + const mappedPos = posMap[bidRequest.params.position]; + if (mappedPos && typeof imp[context.mediaType].pos !== 'number') { + imp[context.mediaType].pos = mappedPos; + } + + // Sort banner sizes + if (context.mediaType === BANNER && imp[context.mediaType]?.format?.length > 1) { + imp[context.mediaType].format = masSizeOrdering(imp[context.mediaType].format); + } + + // Set secure + imp.secure = 1; + + return imp; + }, + request(buildRequest, imps, bidderRequest, context) { + const req = buildRequest(imps, bidderRequest, context); + + // Do not send in tmax + delete req.tmax; + + // we always deal in USD so just remove + delete req.cur; + + // Set to channel to int_type if avail + deepSetValue(req, 'ext.prebid.channel.name', mgniConf.int_type || DEFAULT_INTEGRATION); + + // we do not need to send addtlConsent to DV+ + delete req?.ext?.ConsentedProvidersSettings?.consented_providers; + + // If user.id is not set try pub conf user.id else undefined + const ppuid = getPpuid(req); + if (ppuid) { + deepSetValue(req, 'user.id', ppuid); + } else { + delete req.user?.id; + } + + // delete buyeruid always + delete req.user?.buyeruid; + + // delete device ifa if falsy + if (!req.device?.ifa) { + delete req.device?.ifa; + } + + // let AE determine dnt from headers not payload + delete req.device?.dnt; + + return req; + }, + bidResponse(buildBidResponse, bid, context) { + // Move adm_native to adm for native responses so the ortbConverter can process it + if (context.mediaType === NATIVE && bid.adm_native) { + bid.adm = bid.adm_native; + delete bid.adm_native; + } + + const bidResponse = buildBidResponse(bid, context); + + bidResponse.bidderCode = context.bidRequest.bidder; + + // If it is a video response up the ttl + if (bidResponse.mediaType === VIDEO) { + bidResponse.ttl = 900; + } + + // Attach renderer and w and h if outstream + if (bidResponse.mediaType === VIDEO && context.bidRequest?.mediaTypes?.video?.context === 'outstream') { + bidResponse.renderer = outstreamRenderer(bidResponse, mgniConf.rendererUrl, mgniConf.rendererConfig); + // generate local vastUrl using createObjectURL + bidResponse.vastUrl = URL.createObjectURL(new Blob([bidResponse.vastXml], { type: 'text/xml' })); + setTimeout(() => { + URL.revokeObjectURL(bidResponse.vastUrl); + }, (bidResponse.ttl + 60) * 1000); + } + + // If its video and the width and height are not set + if (bidResponse.mediaType === VIDEO && bidShouldUsePlayerWidthAndHeight(bidResponse)) { + bidResponse.width = bidResponse.playerWidth; + bidResponse.height = bidResponse.playerHeight; + } + + return bidResponse; + }, + response(buildResponse, bidResponses, ortbResponse, context) { + const response = buildResponse(bidResponses, ortbResponse, context); + return response; + }, + overrides: { + imp: { + bidfloor(setBidFloor, imp, bidRequest, context) { + // Floors should always be in USD + const floor = {}; + setBidFloor(floor, bidRequest, { ...context, currency: 'USD' }); + if (floor.bidfloorcur === 'USD') { + Object.assign(imp, floor); + } + }, + } + } +}); + +function shouldAddBid(bid, mediaType) { + const enabledTypes = bid.params?.enabledMediaTypes; + return !Array.isArray(enabledTypes) || enabledTypes.includes(mediaType); +} + +/** + * We must split our bids into different requests with the following criteria + * - Each accountId - siteId - mediaType combo gets own request + * - Max of 10 imps per request + * @param bids + * @param bidderRequest + * @returns Array of HTTP Request Objects + */ +function buildRequests(bids, bidderRequest) { + const bidsMap = {}; + + // Loop through all bids and group them by accountId, siteId, and mediaType + for (const bid of bids) { + const { accountId, siteId } = bid.params; + for (const mediaType of Object.keys(bid.mediaTypes)) { + if (shouldAddBid(bid, mediaType)) { + const key = `${accountId}|${siteId}|${mediaType}`; + if (!bidsMap[key]) { + bidsMap[key] = []; + } + bidsMap[key].push(bid); + } + } + } + + const impLimit = mgniConf.impLimit ?? 10; + const requests = []; + + // Loop through the grouped bids and create requests + // We need to split the bids into chunks of impLimit + // and create a request for each chunk + for (const [key, groupBids] of Object.entries(bidsMap)) { + const [accountId, siteId, mediaType] = key.split('|'); + + for (let i = 0; i < groupBids.length; i += impLimit) { + const chunk = groupBids.slice(i, i + impLimit); + requests.push(createRequest(chunk, bidderRequest, `${accountId}-${siteId}`, mediaType)); + } + } + + return requests; +} + +function createRequest(bidRequests, bidderRequest, acctSite, mediaType) { + return { + method: 'POST', + url: `${(mgniConf.bidEndpoint || REQUEST_URL)}?as=${acctSite}&m=${mediaType}&s=${bidRequests.length}`, + data: converter.toORTB({ bidRequests, bidderRequest, context: { mediaType } }) + } +} + +function interpretResponse(resp, req) { + if (!resp.body) { + resp.body = { nbr: 0 }; + } + return converter.fromORTB({ request: req.data, response: resp.body })?.bids; +} + +/** + * @param syncOptions + * @param responses + * @param gdprConsent + * @param uspConsent + * @param gppConsent + * @return {{type: (string), url: (*|string)}[]} + */ +function getUserSyncs(syncOptions, responses, gdprConsent, uspConsent, gppConsent) { + if (!syncOptions.iframeEnabled) { + return; + } + + const params = {}; + + if (gdprConsent && typeof gdprConsent.gdprApplies === 'boolean') { + params['gdpr'] = Number(gdprConsent.gdprApplies); + } + if (gdprConsent && typeof gdprConsent.consentString === 'string') { + params['gdpr_consent'] = gdprConsent.consentString; + } + + if (uspConsent) { + params['us_privacy'] = encodeURIComponent(uspConsent); + } + + if (gppConsent?.gppString) { + params['gpp'] = gppConsent.gppString; + params['gpp_sid'] = gppConsent.applicableSections?.toString(); + } + + const queryString = Object.keys(params).length ? `?${formatQS(params)}` : ''; + const syncEndpoint = mgniConf.syncEndpoint || SYNC_URL; + + return { + type: 'iframe', + url: syncEndpoint + queryString + }; +} diff --git a/modules/magniteBidAdapter.md b/modules/magniteBidAdapter.md new file mode 100644 index 00000000000..88b5cf39551 --- /dev/null +++ b/modules/magniteBidAdapter.md @@ -0,0 +1,85 @@ +# Overview + +``` +Module Name: Magnite Bid Adapter +Module Type: Bidder Adapter +Maintainer: header-bidding@magnite.com +``` + +# Description + +Connect to Magnite's exchange for bids via a full OpenRTB integration. + +The Magnite adapter requires setup and approval from the +Magnite team. Please reach out to your account team or +header-bidding@magnite.com for more information. + +# Bid Parameters + +| Name | Scope | Type | Description | Example | +| ---- | ----- | ---- | ----------- | ------- | +| `accountId` | required | Number | Magnite account ID. | `14062` | +| `siteId` | required | Number | Magnite site ID. | `70608` | +| `zoneId` | required | Number | Magnite zone ID. | `498816` | + +# Test Parameters + +``` +var adUnits = [ + { + code: 'test-div', + mediaTypes: { + banner: { + sizes: [[300, 250]] + } + }, + bids: [ + { + bidder: "magnite", + params: { + accountId: 14062, + siteId: 70608, + zoneId: 498816 + } + } + ] + } +]; + +var videoAdUnit = { + code: 'myVideoAdUnit', + mediaTypes: { + video: { + context: 'instream', + playerSize: [640, 480], + mimes: ['video/mp4', 'video/x-ms-wmv'], + protocols: [2, 5], + maxduration: 30, + linearity: 1, + api: [2] + } + }, + bids: [ + { + bidder: 'magnite', + params: { + accountId: 14062, + siteId: 70608, + zoneId: 498816 + } + } + ] +}; +``` + +# Configuration + +Add the following code to enable user syncing. By default, Prebid.js turns off user syncing through iframes. Magnite recommends enabling iframe-based user syncing to improve match rates and bid performance. + +```javascript +pbjs.setConfig({ + userSync: { + iframeEnabled: true + } +}); +``` diff --git a/modules/malltvAnalyticsAdapter.js b/modules/malltvAnalyticsAdapter.js index 29936d18a0b..98aa989cc15 100644 --- a/modules/malltvAnalyticsAdapter.js +++ b/modules/malltvAnalyticsAdapter.js @@ -1,9 +1,9 @@ -import {ajax} from '../src/ajax.js' +import { ajax } from '../src/ajax.js' import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js' import { EVENTS } from '../src/constants.js' import adapterManager from '../src/adapterManager.js' -import {getGlobal} from '../src/prebidGlobal.js' -import {logInfo, logError, deepClone} from '../src/utils.js' +import { getGlobal } from '../src/prebidGlobal.js' +import { logInfo, logError, deepClone } from '../src/utils.js' const analyticsType = 'endpoint' export const ANALYTICS_VERSION = '1.0.0' @@ -40,7 +40,7 @@ export const parseAdUnitCode = function (bidResponse) { return bidResponse.adUnitCode.toLowerCase() } -export const malltvAnalyticsAdapter = Object.assign(adapter({DEFAULT_SERVER, analyticsType}), { +export const malltvAnalyticsAdapter = Object.assign(adapter({ DEFAULT_SERVER, analyticsType }), { cachedAuctions: {}, @@ -62,7 +62,7 @@ export const malltvAnalyticsAdapter = Object.assign(adapter({DEFAULT_SERVER, ana return true }, - track({eventType, args}) { + track({ eventType, args }) { switch (eventType) { case BID_TIMEOUT: this.handleBidTimeout(args) @@ -86,7 +86,7 @@ export const malltvAnalyticsAdapter = Object.assign(adapter({DEFAULT_SERVER, ana ) }, createBidMessage(auctionEndArgs, winningBids, timeoutBids) { - const {auctionId, timestamp, timeout, auctionEnd, adUnitCodes, bidsReceived, noBids} = auctionEndArgs + const { auctionId, timestamp, timeout, auctionEnd, adUnitCodes, bidsReceived, noBids } = auctionEndArgs const message = this.createCommonMessage(auctionId) message.auctionElapsed = (auctionEnd - timestamp) diff --git a/modules/malltvBidAdapter.js b/modules/malltvBidAdapter.js index ef71f367142..5ad83875a7a 100644 --- a/modules/malltvBidAdapter.js +++ b/modules/malltvBidAdapter.js @@ -16,7 +16,7 @@ const SIZE_SEPARATOR = ';'; const BISKO_ID = 'biskoId'; const STORAGE_ID = 'bisko-sid'; const SEGMENTS = 'biskoSegments'; -const storage = getStorageManager({bidderCode: BIDDER_CODE}); +const storage = getStorageManager({ bidderCode: BIDDER_CODE }); export const spec = { code: BIDDER_CODE, diff --git a/modules/mantisBidAdapter.js b/modules/mantisBidAdapter.js index 5d9ccc9419b..a65225b0724 100644 --- a/modules/mantisBidAdapter.js +++ b/modules/mantisBidAdapter.js @@ -1,10 +1,10 @@ -import {registerBidder} from '../src/adapters/bidderFactory.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; import { getStorageManager } from '../src/storageManager.js'; import { ajax } from '../src/ajax.js'; import { getBoundingClientRect } from '../libraries/boundingClientRect/boundingClientRect.js'; import { getWinDimensions } from '../src/utils.js'; -export const storage = getStorageManager({bidderCode: 'mantis'}); +export const storage = getStorageManager({ bidderCode: 'mantis' }); function inIframe() { try { @@ -220,7 +220,7 @@ export const spec = { bidId: bid.bidId, config: bid.params, sizes: bid.sizes.map(function (size) { - return {width: size[0], height: size[1]}; + return { width: size[0], height: size[1] }; }) }; }), @@ -261,13 +261,13 @@ export const spec = { if (syncOptions.iframeEnabled) { return [{ type: 'iframe', - url: buildMantisUrl('/prebid/iframe', {gdpr: gdprConsent, uspConsent: uspConsent}) + url: buildMantisUrl('/prebid/iframe', { gdpr: gdprConsent, uspConsent: uspConsent }) }]; } if (syncOptions.pixelEnabled) { return [{ type: 'image', - url: buildMantisUrl('/prebid/pixel', {gdpr: gdprConsent, uspConsent: uspConsent}) + url: buildMantisUrl('/prebid/pixel', { gdpr: gdprConsent, uspConsent: uspConsent }) }]; } } diff --git a/modules/marsmediaBidAdapter.js b/modules/marsmediaBidAdapter.js index 3a7039d7899..074a96b6268 100644 --- a/modules/marsmediaBidAdapter.js +++ b/modules/marsmediaBidAdapter.js @@ -1,11 +1,12 @@ 'use strict'; -import {getDNT} from '../libraries/dnt/index.js'; import { deepAccess, parseSizesInput, isArray, getWindowTop, deepSetValue, triggerPixel, getWindowSelf, isPlainObject } from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; -import {config} from '../src/config.js'; +import { config } from '../src/config.js'; import { percentInView } from '../libraries/percentInView/percentInView.js'; -import {getMinSize} from '../libraries/sizeUtils/sizeUtils.js'; +import { getMinSize } from '../libraries/sizeUtils/sizeUtils.js'; +import { getAdUnitElement } from '../src/utils/adUnits.js'; +import { getDNT } from '../libraries/dnt/index.js'; function MarsmediaAdapter() { this.code = 'marsmedia'; @@ -165,9 +166,9 @@ function MarsmediaAdapter() { let bidSizes = (bid.mediaTypes && bid.mediaTypes.banner && bid.mediaTypes.banner.sizes) || bid.sizes; bidSizes = ((isArray(bidSizes) && isArray(bidSizes[0])) ? bidSizes : [bidSizes]); bidSizes = bidSizes.filter(size => isArray(size)); - const processedSizes = bidSizes.map(size => ({w: parseInt(size[0], 10), h: parseInt(size[1], 10)})); + const processedSizes = bidSizes.map(size => ({ w: parseInt(size[0], 10), h: parseInt(size[1], 10) })); - const element = document.getElementById(bid.adUnitCode); + const element = getAdUnitElement(bid); const minSize = getMinSize(processedSizes); const viewabilityAmount = _isViewabilityMeasurable(element) ? _getViewability(element, getWindowTop(), minSize) diff --git a/modules/mediaeyesBidAdapter.js b/modules/mediaeyesBidAdapter.js index ae43af2b50d..78d26517308 100644 --- a/modules/mediaeyesBidAdapter.js +++ b/modules/mediaeyesBidAdapter.js @@ -1,5 +1,6 @@ import { - BANNER + BANNER, + VIDEO } from '../src/mediaTypes.js'; import { registerBidder @@ -10,7 +11,7 @@ const ENDPOINT_URL = 'https://delivery.upremium.asia/ortb/open/auction'; export const spec = { code: 'mediaeyes', - supportedMediaTypes: BANNER, + supportedMediaTypes: [BANNER, VIDEO], isBidRequestValid: (bid) => { return !!(bid.params.itemId); @@ -20,7 +21,7 @@ export const spec = { const requests = []; bidRequests.forEach(bidRequest => { - const {itemId} = bidRequest.params; + const { itemId } = bidRequest.params; const requestData = { id: generateUUID(), imp: [cookingImp(bidRequest)], @@ -57,10 +58,24 @@ export const spec = { netRevenue: true }; - prBid.mediaType = BANNER; + let mediaType = rtbBid.ext?.mediaType; + if (!mediaType) { + if (rtbBid.adm && rtbBid.adm.includes(' { } }; +function cookImpVideo({ mediaTypes }) { + const video = mediaTypes.video; + + const size = Array.isArray(video.playerSize[0]) + ? video.playerSize[0] + : video.playerSize; + + const [w, h] = size; + + let placement = video.placement; + if (!placement && video.context) { + if (video.context === 'outstream') { + placement = 4; + } else if (video.context === 'instream') { + placement = 1; + } + } + + return { + w, + h, + mimes: video.mimes || ['video/mp4'], + protocols: video.protocols || [2, 3, 5, 6], + placement: video.placement || 1 + }; +} + function getBidFloor(bidRequest) { let bidfloor = deepAccess(bidRequest, 'params.bidFloor', 0) diff --git a/modules/mediaeyesBidAdapter.md b/modules/mediaeyesBidAdapter.md index 40e1eb77d67..5d49d499367 100644 --- a/modules/mediaeyesBidAdapter.md +++ b/modules/mediaeyesBidAdapter.md @@ -31,3 +31,24 @@ Module that connects to MediaEyes Bidder System }, ]; ``` + +``` + var adUnits = [ + { + code: "video-zone", + mediaTypes: { + video: { + context: "instream", + playerSize: [640, 480] + } + }, + bids: [{ + bidder: "mediaeyes", + params: { + itemId: "4d27f3cc8bbd5bd153045e", + bidFloor: 0.01 + } + }] + } + ]; +``` \ No newline at end of file diff --git a/modules/mediaforceBidAdapter.js b/modules/mediaforceBidAdapter.js index a1d333ab0af..2fda15971ea 100644 --- a/modules/mediaforceBidAdapter.js +++ b/modules/mediaforceBidAdapter.js @@ -1,9 +1,9 @@ -import {getDNT} from '../libraries/dnt/index.js'; import { deepAccess, isStr, replaceAuctionPrice, triggerPixel, parseGPTSingleSizeArrayToRtbSize } from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; import { buildNativeRequest, parseNativeResponse } from '../libraries/nativeAssetsUtils.js'; +import { getDNT } from '../libraries/dnt/index.js'; /** * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest diff --git a/modules/mediafuseBidAdapter.js b/modules/mediafuseBidAdapter.js index 0bffb9219ce..7fe6538c7ad 100644 --- a/modules/mediafuseBidAdapter.js +++ b/modules/mediafuseBidAdapter.js @@ -1,8 +1,13 @@ +import { ortbConverter } from '../libraries/ortbConverter/converter.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; +import { Renderer } from '../src/Renderer.js'; +import { getStorageManager } from '../src/storageManager.js'; +import { hasPurpose1Consent } from '../src/utils/gdpr.js'; import { createTrackPixelHtml, deepAccess, - deepClone, - getBidRequest, + deepSetValue, getParameterByName, isArray, isArrayOfNums, @@ -16,495 +21,670 @@ import { logMessage, logWarn } from '../src/utils.js'; -import {Renderer} from '../src/Renderer.js'; -import {config} from '../src/config.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {ADPOD, BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js'; -import {INSTREAM, OUTSTREAM} from '../src/video.js'; -import {getStorageManager} from '../src/storageManager.js'; -import {bidderSettings} from '../src/bidderSettings.js'; -import {hasPurpose1Consent} from '../src/utils/gdpr.js'; -import {convertOrtbRequestToProprietaryNative} from '../src/native.js'; -import {APPNEXUS_CATEGORY_MAPPING} from '../libraries/categoryTranslationMapping/index.js'; +import { config } from '../src/config.js'; import { getANKewyordParamFromMaps, getANKeywordParam } from '../libraries/appnexusUtils/anKeywords.js'; -import {convertCamelToUnderscore, fill} from '../libraries/appnexusUtils/anUtils.js'; -import {chunk} from '../libraries/chunk/chunk.js'; - -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - */ +import { convertCamelToUnderscore } from '../libraries/appnexusUtils/anUtils.js'; +import { chunk } from '../libraries/chunk/chunk.js'; +import { getAdUnitElement } from '../src/utils/adUnits.js'; const BIDDER_CODE = 'mediafuse'; -const URL = 'https://ib.adnxs.com/ut/v3/prebid'; -const URL_SIMPLE = 'https://ib.adnxs-simple.com/ut/v3/prebid'; -const VIDEO_TARGETING = ['id', 'minduration', 'maxduration', - 'skippable', 'playback_method', 'frameworks', 'context', 'skipoffset']; -const VIDEO_RTB_TARGETING = ['minduration', 'maxduration', 'skip', 'skipafter', 'playbackmethod', 'api']; -const USER_PARAMS = ['age', 'externalUid', 'segments', 'gender', 'dnt', 'language']; -const APP_DEVICE_PARAMS = ['device_id']; // appid is collected separately +const GVLID = 32; +const ENDPOINT_URL_NORMAL = 'https://ib.adnxs.com/openrtb2/prebidjs'; +const ENDPOINT_URL_SIMPLE = 'https://ib.adnxs-simple.com/openrtb2/prebidjs'; +const SOURCE = 'pbjs'; +const MAX_IMPS_PER_REQUEST = 15; const DEBUG_PARAMS = ['enabled', 'dongle', 'member_id', 'debug_timeout']; -const VIDEO_MAPPING = { - playback_method: { - 'unknown': 0, - 'auto_play_sound_on': 1, - 'auto_play_sound_off': 2, - 'click_to_play': 3, - 'mouse_over': 4, - 'auto_play_sound_unknown': 5 - }, - context: { - 'unknown': 0, - 'pre_roll': 1, - 'mid_roll': 2, - 'post_roll': 3, - 'outstream': 4, - 'in-banner': 5 - } +const DEBUG_QUERY_PARAM_MAP = { + 'apn_debug_enabled': 'enabled', + 'apn_debug_dongle': 'dongle', + 'apn_debug_member_id': 'member_id', + 'apn_debug_timeout': 'debug_timeout' }; -const NATIVE_MAPPING = { - body: 'description', - body2: 'desc2', - cta: 'ctatext', - image: { - serverName: 'main_image', - requiredParams: { required: true } - }, - icon: { - serverName: 'icon', - requiredParams: { required: true } - }, - sponsoredBy: 'sponsored_by', - privacyLink: 'privacy_link', - salePrice: 'saleprice', - displayUrl: 'displayurl' +const RESPONSE_MEDIA_TYPE_MAP = { + 0: BANNER, + 1: VIDEO, + 3: NATIVE }; -const SOURCE = 'pbjs'; -const MAX_IMPS_PER_REQUEST = 15; -const SCRIPT_TAG_START = ' USER_PARAMS.includes(param)) - .forEach((param) => { - const uparam = convertCamelToUnderscore(param); - if (param === 'segments' && isArray(userObjBid.params.user[param])) { - const segs = []; - userObjBid.params.user[param].forEach(val => { - if (isNumber(val)) { - segs.push({'id': val}); - } else if (isPlainObject(val)) { - segs.push(val); - } - }); - userObj[uparam] = segs; - } else if (param !== 'segments') { - userObj[uparam] = userObjBid.params.user[param]; - } - }); + if (!imp.banner && deepAccess(bidRequest, 'mediaTypes.banner')) { + const sizes = deepAccess(bidRequest, 'mediaTypes.banner.sizes'); + if (isArray(sizes) && sizes.length > 0) { + const size = isArray(sizes[0]) ? sizes[0] : sizes; + imp.banner = { + w: size[0], + h: size[1], + format: sizes.map(s => { + const sz = isArray(s) ? s : [s[0], s[1]]; + return { w: sz[0], h: sz[1] }; + }) + }; + } + } + const bidderParams = bidRequest.params; + const extANData = { + disable_psa: true + }; + // Legacy support for placement_id vs placementId + const placementId = bidderParams.placement_id || bidderParams.placementId; + if (placementId) { + extANData.placement_id = parseInt(placementId, 10); + } else { + const invCode = bidderParams.inv_code || bidderParams.invCode; + if (invCode) { + deepSetValue(imp, 'tagid', invCode); + } } - const appDeviceObjBid = ((bidRequests) || []).find(hasAppDeviceInfo); - let appDeviceObj; - if (appDeviceObjBid && appDeviceObjBid.params && appDeviceObjBid.params.app) { - appDeviceObj = {}; - Object.keys(appDeviceObjBid.params.app) - .filter(param => APP_DEVICE_PARAMS.includes(param)) - .forEach(param => { - appDeviceObj[param] = appDeviceObjBid.params.app[param]; - }); + if (imp.banner) { + // primary_size for legacy support + const firstFormat = deepAccess(imp, 'banner.format.0'); + if (firstFormat) { + extANData.primary_size = firstFormat; + } + if (!imp.banner.api) { + const bannerFrameworks = bidderParams.banner_frameworks || bidderParams.frameworks; + if (isArrayOfNums(bannerFrameworks)) { + extANData.banner_frameworks = bannerFrameworks; + } + } + if (bidderParams.position === 'above') { + imp.banner.pos = 1; + } else if (bidderParams.position === 'below') { + imp.banner.pos = 3; + } } - const appIdObjBid = ((bidRequests) || []).find(hasAppId); - let appIdObj; - if (appIdObjBid && appIdObjBid.params && appDeviceObjBid.params.app && appDeviceObjBid.params.app.id) { - appIdObj = { - appid: appIdObjBid.params.app.id - }; + const gpid = deepAccess(bidRequest, 'ortb2Imp.ext.gpid'); + if (gpid) { + extANData.gpid = gpid; } - let debugObj = {}; - const debugObjParams = {}; - const debugCookieName = 'apn_prebid_debug'; - const debugCookie = storage.getCookie(debugCookieName) || null; + if (FEATURES.VIDEO && imp.video) { + if (deepAccess(bidRequest, 'mediaTypes.video.context') === 'instream') { + extANData.require_asset_url = true; + } - if (debugCookie) { - try { - debugObj = JSON.parse(debugCookie); - } catch (e) { - logError('MediaFuse Debug Auction Cookie Error:\n\n' + e); + const videoParams = bidderParams.video; + if (videoParams) { + Object.keys(videoParams) + .filter(param => VIDEO_TARGETING.includes(param)) + .forEach(param => { + if (param === 'frameworks') { + if (isArray(videoParams.frameworks)) { + extANData.video_frameworks = videoParams.frameworks; + } + } else { + imp.video[param] = videoParams[param]; + } + }); } - } else { - const debugBidRequest = ((bidRequests) || []).find(hasDebug); - if (debugBidRequest && debugBidRequest.debug) { - debugObj = debugBidRequest.debug; + + const videoMediaType = deepAccess(bidRequest, 'mediaTypes.video'); + if (videoMediaType && imp.video) { + Object.keys(videoMediaType) + .filter(param => VIDEO_RTB_TARGETING.includes(param)) + .forEach(param => { + switch (param) { + case 'minduration': + case 'maxduration': + if (typeof imp.video[param] !== 'number') imp.video[param] = videoMediaType[param]; + break; + case 'skip': + if (typeof imp.video['skippable'] !== 'boolean') imp.video['skippable'] = (videoMediaType[param] === 1); + break; + case 'skipafter': + if (typeof imp.video['skipoffset'] !== 'number') imp.video['skipoffset'] = videoMediaType[param]; + break; + case 'playbackmethod': + if (typeof imp.video['playback_method'] !== 'number' && isArray(videoMediaType[param])) { + const type = videoMediaType[param][0]; + if (type >= 1 && type <= 4) { + imp.video['playback_method'] = type; + } + } + break; + case 'api': + if (!extANData.video_frameworks && isArray(videoMediaType[param])) { + const apiTmp = videoMediaType[param].map(val => { + const v = (val === 4) ? 5 : (val === 5) ? 4 : val; + return (v >= 1 && v <= 5) ? v : undefined; + }).filter(v => v !== undefined); + extANData.video_frameworks = apiTmp; + } + break; + } + }); } - } - if (debugObj && debugObj.enabled) { - Object.keys(debugObj) - .filter(param => DEBUG_PARAMS.includes(param)) - .forEach(param => { - debugObjParams[param] = debugObj[param]; - }); + if (deepAccess(bidRequest, 'mediaTypes.video.context') === 'outstream') { + imp.video.placement = imp.video.placement || 4; + } + + const xandrVideoContext = VIDEO_CONTEXT_MAP[deepAccess(bidRequest, 'mediaTypes.video.context')]; + if (xandrVideoContext !== undefined) { + deepSetValue(imp, 'video.ext.appnexus.context', xandrVideoContext); + } + } + if (bidRequest.renderer) { + extANData.custom_renderer_present = true; } - const memberIdBid = ((bidRequests) || []).find(hasMemberId); - const member = memberIdBid ? parseInt(memberIdBid.params.member, 10) : 0; - const schain = bidRequests[0]?.ortb2?.source?.ext?.schain; - const omidSupport = ((bidRequests) || []).find(hasOmidSupport); + Object.entries(OPTIONAL_PARAMS_MAP).forEach(([paramName, ortbName]) => { + if (bidderParams[paramName] !== undefined) { + extANData[ortbName] = bidderParams[paramName]; + } + }); - const payload = { - tags: [...tags], - user: userObj, - sdk: { - source: SOURCE, - version: '$prebid.version$' - }, - schain: schain - }; + Object.keys(bidderParams) + .filter(param => !KNOWN_PARAMS.has(param)) + .forEach(param => { + extANData[convertCamelToUnderscore(param)] = bidderParams[param]; + }); - if (omidSupport) { - payload['iab_support'] = { - omidpn: 'Mediafuse', - omidpv: '$prebid.version$' - }; + // Keywords + if (!isEmpty(bidderParams.keywords)) { + const keywords = getANKewyordParamFromMaps(bidderParams.keywords); + if (keywords && keywords.length > 0) { + extANData.keywords = keywords.map(kw => kw.key + (kw.value ? '=' + kw.value.join(',') : '')).join(','); + } } - if (member > 0) { - payload.member_id = member; + // Floor + const bidFloor = getBidFloor(bidRequest); + if (bidFloor) { + imp.bidfloor = bidFloor; + imp.bidfloorcur = 'USD'; + } else { + delete imp.bidfloor; + delete imp.bidfloorcur; } - if (appDeviceObjBid) { - payload.device = appDeviceObj; + if (Object.keys(extANData).length > 0) { + deepSetValue(imp, 'ext.appnexus', extANData); } - if (appIdObjBid) { - payload.app = appIdObj; + + return imp; + }, + request(buildRequest, imps, bidderRequest, context) { + const request = buildRequest(imps, bidderRequest, context); + + // Ensure EIDs from the userId module are included when ortbConverter hasn't already + // populated user.ext.eids (e.g. when ortb2.user.ext.eids is not pre-set by the page). + // All bids in a request share the same user EIDs, so reading from bids[0] is correct. + if (!deepAccess(request, 'user.ext.eids')) { + const bidEids = bidderRequest.bids?.[0]?.userIdAsEids; + if (isArray(bidEids) && bidEids.length > 0) { + deepSetValue(request, 'user.ext.eids', bidEids); + } } - const mfKeywords = config.getConfig('mediafuseAuctionKeywords'); - payload.keywords = getANKeywordParam(bidderRequest?.ortb2, mfKeywords); + if (request.user && request.user.ext && isArray(request.user.ext.eids)) { + request.user.ext.eids.forEach(eid => { + let rtiPartner; + if (eid.source === 'adserver.org') { + rtiPartner = 'TDID'; + } else if (eid.source === 'uidapi.com') { + rtiPartner = 'UID2'; + } - if (config.getConfig('adpod.brandCategoryExclusion')) { - payload.brand_category_uniqueness = true; + if (rtiPartner) { + // Set rtiPartner on the first uid's ext object + if (isArray(eid.uids) && eid.uids[0]) { + eid.uids[0] = Object.assign({}, eid.uids[0], { ext: Object.assign({}, eid.uids[0].ext, { rtiPartner }) }); + } + } + }); } - if (debugObjParams.enabled) { - payload.debug = debugObjParams; - logInfo('MediaFuse Debug Auction Settings:\n\n' + JSON.stringify(debugObjParams, null, 4)); + const extANData = { + prebid: true, + hb_source: 1, // 1 = client/web-originated header bidding request (Xandr source enum) + sdk: { + version: '$prebid.version$', + source: SOURCE + } + }; + + if (bidderRequest?.refererInfo) { + const refererinfo = { + rd_ref: bidderRequest.refererInfo.topmostLocation ? encodeURIComponent(bidderRequest.refererInfo.topmostLocation) : '', + rd_top: bidderRequest.refererInfo.reachedTop, + rd_ifs: bidderRequest.refererInfo.numIframes, + rd_stk: bidderRequest.refererInfo.stack?.map((url) => encodeURIComponent(url)).join(',') + }; + if (bidderRequest.refererInfo.canonicalUrl) { + refererinfo.rd_can = bidderRequest.refererInfo.canonicalUrl; + } + extANData.referrer_detection = refererinfo; + } + + // App/Device parameters + const expandedBids = bidderRequest?.bids || []; + const memberBid = expandedBids.find(bid => bid.params && bid.params.member); + const commonBidderParams = memberBid ? memberBid.params : (expandedBids[0] && expandedBids[0].params); + + if (commonBidderParams) { + if (commonBidderParams.member) { + // member_id in the request body routes bids to the correct Xandr seat + extANData.member_id = parseInt(commonBidderParams.member, 10); + } + if (commonBidderParams.publisherId) { + deepSetValue(request, 'site.publisher.id', commonBidderParams.publisherId.toString()); + } } - if (bidderRequest && bidderRequest.gdprConsent) { - // note - objects for impbus use underscore instead of camelCase - payload.gdpr_consent = { - consent_string: bidderRequest.gdprConsent.consentString, - consent_required: bidderRequest.gdprConsent.gdprApplies + if (bidderRequest.bids?.some(bid => hasOmidSupport(bid))) { + extANData.iab_support = { + omidpn: 'Mediafuse', + omidpv: '$prebid.version$' }; + } + + deepSetValue(request, 'ext.appnexus', extANData); + + // GDPR / Consent + if (bidderRequest.gdprConsent) { + deepSetValue(request, 'regs.ext.gdpr', bidderRequest.gdprConsent.gdprApplies ? 1 : 0); + deepSetValue(request, 'user.ext.consent', bidderRequest.gdprConsent.consentString); if (bidderRequest.gdprConsent.addtlConsent && bidderRequest.gdprConsent.addtlConsent.indexOf('~') !== -1) { const ac = bidderRequest.gdprConsent.addtlConsent; - // pull only the ids from the string (after the ~) and convert them to an array of ints const acStr = ac.substring(ac.indexOf('~') + 1); - payload.gdpr_consent.addtl_consent = acStr.split('.').map(id => parseInt(id, 10)); + const addtlConsent = acStr.split('.').map(id => parseInt(id, 10)).filter(id => !isNaN(id)); + if (addtlConsent.length > 0) { + deepSetValue(request, 'user.ext.addtl_consent', addtlConsent); + } } } - if (bidderRequest && bidderRequest.uspConsent) { - payload.us_privacy = bidderRequest.uspConsent; + if (bidderRequest.uspConsent) { + deepSetValue(request, 'regs.ext.us_privacy', bidderRequest.uspConsent); } - if (bidderRequest && bidderRequest.refererInfo) { - const refererinfo = { - // TODO: this collects everything it finds, except for canonicalUrl - rd_ref: encodeURIComponent(bidderRequest.refererInfo.topmostLocation), - rd_top: bidderRequest.refererInfo.reachedTop, - rd_ifs: bidderRequest.refererInfo.numIframes, - rd_stk: bidderRequest.refererInfo.stack.map((url) => encodeURIComponent(url)).join(',') - }; - payload.referrer_detection = refererinfo; + if (bidderRequest.gppConsent) { + deepSetValue(request, 'regs.gpp', bidderRequest.gppConsent.gppString); + deepSetValue(request, 'regs.gpp_sid', bidderRequest.gppConsent.applicableSections); } - const hasAdPodBid = ((bidRequests) || []).find(hasAdPod); - if (hasAdPodBid) { - bidRequests.filter(hasAdPod).forEach(adPodBid => { - const adPodTags = createAdPodRequest(tags, adPodBid); - // don't need the original adpod placement because it's in adPodTags - const nonPodTags = payload.tags.filter(tag => tag.uuid !== adPodBid.bidId); - payload.tags = [...nonPodTags, ...adPodTags]; - }); + if (config.getConfig('coppa') === true) { + deepSetValue(request, 'regs.coppa', 1); } - if (bidRequests[0].userIdAsEids?.length > 0) { - const eids = []; - bidRequests[0].userIdAsEids.forEach(eid => { - if (!eid || !eid.uids || eid.uids.length < 1) { return; } - eid.uids.forEach(uid => { - const tmp = {'source': eid.source, 'id': uid.id}; - if (eid.source === 'adserver.org') { - tmp.rti_partner = 'TDID'; - } else if (eid.source === 'uidapi.com') { - tmp.rti_partner = 'UID2'; + // Legacy Xandr-specific user params (externalUid, segments, age, gender, dnt, language). + // These are not part of standard OpenRTB; kept for backwards compatibility with existing + // publisher configs. Standard OpenRTB user fields flow via bidderRequest.ortb2.user. + const userObjBid = ((bidderRequest?.bids) || []).find(bid => bid.params?.user); + if (userObjBid) { + const userObj = request.user || {}; + Object.keys(userObjBid.params.user) + .filter(param => USER_PARAMS.includes(param)) + .forEach((param) => { + const uparam = convertCamelToUnderscore(param); + if (param === 'segments' && isArray(userObjBid.params.user[param])) { + const segs = userObjBid.params.user[param].map(val => { + if (isNumber(val)) return { 'id': val }; + if (isPlainObject(val)) return val; + return undefined; + }).filter(s => s); + userObj.ext = userObj.ext || {}; + userObj.ext[uparam] = segs; + } else if (param !== 'segments') { + userObj[uparam] = userObjBid.params.user[param]; } - eids.push(tmp); }); - }); + request.user = userObj; + } - if (eids.length) { - payload.eids = eids; - } + // Legacy app object from bid.params.app; backwards compatibility for publishers who pre-date + // the standard bidderRequest.ortb2.app first-party data path. + const appObjBid = ((bidderRequest?.bids) || []).find(bid => bid.params?.app); + if (appObjBid) { + request.app = Object.assign({}, request.app, appObjBid.params.app); } - if (tags[0].publisher_id) { - payload.publisher_id = tags[0].publisher_id; + // Global Keywords — set via pbjs.setConfig({ mediafuseAuctionKeywords: { key: ['val'] } }) + const mfKeywords = config.getConfig('mediafuseAuctionKeywords'); + if (mfKeywords) { + const keywords = getANKeywordParam(bidderRequest?.ortb2, mfKeywords); + if (keywords && keywords.length > 0) { + const kwString = keywords.map(kw => kw.key + (kw.value ? '=' + kw.value.join(',') : '')).join(','); + deepSetValue(request, 'ext.appnexus.keywords', kwString); + } } - const request = formatRequest(payload, bidderRequest); return request; }, - - /** - * Unpack the response from the server into a list of bids. - * - * @param {*} serverResponse A successful response from the server. - * @return {Bid[]} An array of bids which were nested inside the server. - */ - interpretResponse: function (serverResponse, { bidderRequest }) { - serverResponse = serverResponse.body; - const bids = []; - if (!serverResponse || serverResponse.error) { - let errorMessage = `in response for ${bidderRequest.bidderCode} adapter`; - if (serverResponse && serverResponse.error) { errorMessage += `: ${serverResponse.error}`; } - logError(errorMessage); - return bids; + bidResponse(buildBidResponse, bid, context) { + const { bidRequest } = context; + const bidAdType = bid?.ext?.appnexus?.bid_ad_type; + const mediaType = RESPONSE_MEDIA_TYPE_MAP[bidAdType]; + const extANData = deepAccess(bid, 'ext.appnexus'); + // Set mediaType for all bids to help ortbConverter determine the correct parser + if (mediaType) { + context.mediaType = mediaType; + } + let bidResponse; + try { + bidResponse = buildBidResponse(bid, context); + } catch (e) { + if (bidAdType !== 3 && mediaType !== 'native') { + logError('Mediafuse: buildBidResponse hook crash', e); + } else { + logWarn('Mediafuse: buildBidResponse native parse error', e); + } + } + if (!bidResponse) { + if (mediaType) { + bidResponse = { + requestId: bidRequest?.bidId || bid.impid, + cpm: bid.price || 0, + width: bid.w, + height: bid.h, + creativeId: bid.crid, + dealId: bid.dealid, + currency: 'USD', + netRevenue: true, + ttl: 300, + mediaType, + ad: bid.adm + }; + } else { + logWarn('Mediafuse: Could not build bidResponse for unknown mediaType', { bidAdType, mediaType }); + return null; + } } - if (serverResponse.tags) { - serverResponse.tags.forEach(serverBid => { - const rtbBid = getRtbBid(serverBid); - if (rtbBid) { - const cpmCheck = (bidderSettings.get(bidderRequest.bidderCode, 'allowZeroCpmBids') === true) ? rtbBid.cpm >= 0 : rtbBid.cpm > 0; - if (cpmCheck && this.supportedMediaTypes.includes(rtbBid.ad_type)) { - const bid = newBid(serverBid, rtbBid, bidderRequest); - bid.mediaType = parseMediaType(rtbBid); - bids.push(bid); - } - } + if (extANData) { + bidResponse.meta = Object.assign({}, bidResponse.meta, { + advertiserId: extANData.advertiser_id, + brandId: extANData.brand_id, + buyerMemberId: extANData.buyer_member_id, + dealPriority: extANData.deal_priority, + dealCode: extANData.deal_code }); - } - if (serverResponse.debug && serverResponse.debug.debug_info) { - const debugHeader = 'MediaFuse Debug Auction for Prebid\n\n' - let debugText = debugHeader + serverResponse.debug.debug_info - debugText = debugText - .replace(/(|)/gm, '\t') // Tables - .replace(/(<\/td>|<\/th>)/gm, '\n') // Tables - .replace(/^
/gm, '') // Remove leading
- .replace(/(
\n|
)/gm, '\n') //
- .replace(/

(.*)<\/h1>/gm, '\n\n===== $1 =====\n\n') // Header H1 - .replace(/(.*)<\/h[2-6]>/gm, '\n\n*** $1 ***\n\n') // Headers - .replace(/(<([^>]+)>)/igm, ''); // Remove any other tags - // logMessage('https://console.appnexus.com/docs/understanding-the-debug-auction'); - logMessage(debugText); + if (extANData.buyer_member_id) { + bidResponse.meta.dchain = { + ver: '1.0', + complete: 0, + nodes: [{ + bsid: extANData.buyer_member_id.toString() + }] + }; + } } - return bids; - }, - - getUserSyncs: function (syncOptions, responses, gdprConsent) { - if (syncOptions.iframeEnabled && hasPurpose1Consent({gdprConsent})) { - return [{ - type: 'iframe', - url: 'https://acdn.adnxs.com/dmp/async_usersync.html' - }]; + if (bid.adomain) { + const adomain = isArray(bid.adomain) ? bid.adomain : [bid.adomain]; + if (adomain.length > 0) { + bidResponse.meta = bidResponse.meta || {}; + bidResponse.meta.advertiserDomains = adomain; + } } - }, - /** - * Add element selector to javascript tracker to improve native viewability - * @param {Bid} bid - */ - onBidWon: function (bid) { - if (bid.native) { - reloadViewabilityScriptWithCorrectParameters(bid); - } - } -}; - -function reloadViewabilityScriptWithCorrectParameters(bid) { - const viewJsPayload = getMediafuseViewabilityScriptFromJsTrackers(bid.native.javascriptTrackers); - - if (viewJsPayload) { - const prebidParams = 'pbjs_adid=' + bid.adId + ';pbjs_auc=' + bid.adUnitCode; - - const jsTrackerSrc = getViewabilityScriptUrlFromPayload(viewJsPayload); - - const newJsTrackerSrc = jsTrackerSrc.replace('dom_id=%native_dom_id%', prebidParams); - - // find iframe containing script tag - const frameArray = document.getElementsByTagName('iframe'); + // Video + if (FEATURES.VIDEO && mediaType === VIDEO) { + bidResponse.ttl = 3600; + if (bid.nurl) { + bidResponse.vastImpUrl = bid.nurl; + } - // boolean var to modify only one script. That way if there are muliple scripts, - // they won't all point to the same creative. - let modifiedAScript = false; + if (extANData?.renderer_url && extANData?.renderer_id) { + const rendererOptions = deepAccess(bidRequest, 'mediaTypes.video.renderer.options') || deepAccess(bidRequest, 'renderer.options'); + bidResponse.adResponse = { + ad: { + notify_url: bid.nurl || '', + renderer_config: extANData.renderer_config || '', + }, + auction_id: extANData.auction_id, + content: bidResponse.vastXml, + tag_id: extANData.tag_id, + uuid: bidResponse.requestId + }; + bidResponse.renderer = newRenderer(bidRequest, { + renderer_url: extANData.renderer_url, + renderer_id: extANData.renderer_id, + }, rendererOptions); + } else if (extANData?.asset_url) { + bidResponse.vastUrl = extANData.asset_url; + } + } - // first, loop on all ifames - for (let i = 0; i < frameArray.length && !modifiedAScript; i++) { - const currentFrame = frameArray[i]; + // Native processing: viewability macro replacement and manual asset mapping + if (FEATURES.NATIVE && (bidAdType === 3 || mediaType === 'native')) { + bidResponse.mediaType = 'native'; try { - // IE-compatible, see https://stackoverflow.com/a/3999191/2112089 - const nestedDoc = currentFrame.contentDocument || currentFrame.contentWindow.document; - - if (nestedDoc) { - // if the doc is present, we look for our jstracker - const scriptArray = nestedDoc.getElementsByTagName('script'); - for (let j = 0; j < scriptArray.length && !modifiedAScript; j++) { - const currentScript = scriptArray[j]; - if (currentScript.getAttribute('data-src') === jsTrackerSrc) { - currentScript.setAttribute('src', newJsTrackerSrc); - currentScript.setAttribute('data-src', ''); - if (currentScript.removeAttribute) { - currentScript.removeAttribute('data-src'); - } - modifiedAScript = true; + const adm = bid.adm; + const nativeAdm = isStr(adm) ? JSON.parse(adm) : adm || {}; + + // 1. Viewability macro replacement + const eventtrackers = nativeAdm.native?.eventtrackers || nativeAdm.eventtrackers; + if (eventtrackers && isArray(eventtrackers)) { + eventtrackers.forEach(trackCfg => { + if (trackCfg.url && trackCfg.url.includes('dom_id=%native_dom_id%')) { + const prebidParams = 'pbjs_adid=' + (bidResponse.adId || bidResponse.requestId) + ';pbjs_auc=' + (bidRequest?.adUnitCode || ''); + trackCfg.url = trackCfg.url.replace('dom_id=%native_dom_id%', prebidParams); } + }); + if (nativeAdm.native) { + nativeAdm.native.eventtrackers = eventtrackers; + } else { + nativeAdm.eventtrackers = eventtrackers; } } - } catch (exception) { - // trying to access a cross-domain iframe raises a SecurityError - // this is expected and ignored - if (!(exception instanceof DOMException && exception.name === 'SecurityError')) { - // all other cases are raised again to be treated by the calling function - throw exception; - } - } - } - } -} + // Stringify native ADM to ensure 'ad' field is available for tracking + bidResponse.ad = JSON.stringify(nativeAdm); + + // 2. Manual Mapping - OpenRTB 1.2 asset array format + const nativeAd = nativeAdm.native || nativeAdm; + const native = { + clickUrl: nativeAd.link?.url, + clickTrackers: nativeAd.link?.clicktrackers || nativeAd.link?.click_trackers || [], + impressionTrackers: nativeAd.imptrackers || nativeAd.impression_trackers || [], + privacyLink: nativeAd.privacy || nativeAd.privacy_link, + }; -function strIsMediafuseViewabilityScript(str) { - const regexMatchUrlStart = str.match(VIEWABILITY_URL_START); - const viewUrlStartInStr = regexMatchUrlStart != null && regexMatchUrlStart.length >= 1; + const nativeDataTypeById = {}; + const nativeImgTypeById = {}; + try { + const ortbImp = context.imp || (context.request ?? context.ortbRequest)?.imp?.find(i => i.id === bid.impid); + if (ortbImp) { + const reqStr = ortbImp.native?.request; + const nativeReq = reqStr ? (isStr(reqStr) ? JSON.parse(reqStr) : reqStr) : null; + (nativeReq?.assets || []).forEach(a => { + if (a.data?.type) nativeDataTypeById[a.id] = a.data.type; + if (a.img?.type) nativeImgTypeById[a.id] = a.img.type; + }); + } + } catch (e) { + logError('Mediafuse Native fallback error', e); + } - const regexMatchFileName = str.match(VIEWABILITY_FILE_NAME); - const fileNameInStr = regexMatchFileName != null && regexMatchFileName.length >= 1; + try { + (nativeAd.assets || []).forEach(asset => { + if (asset.title) { + native.title = asset.title.text; + } else if (asset.img) { + const imgType = asset.img.type ?? nativeImgTypeById[asset.id]; + if (imgType === 1) { + native.icon = { url: asset.img.url, width: asset.img.w || asset.img.width, height: asset.img.h || asset.img.height }; + } else { + native.image = { url: asset.img.url, width: asset.img.w || asset.img.width, height: asset.img.h || asset.img.height }; + } + } else if (asset.data) { + switch (asset.data.type ?? nativeDataTypeById[asset.id]) { + case 1: native.sponsoredBy = asset.data.value; break; + case 2: native.body = asset.data.value; break; + case 3: native.rating = asset.data.value; break; + case 4: native.likes = asset.data.value; break; + case 5: native.downloads = asset.data.value; break; + case 6: native.price = asset.data.value; break; + case 7: native.salePrice = asset.data.value; break; + case 8: native.phone = asset.data.value; break; + case 9: native.address = asset.data.value; break; + case 10: native.body2 = asset.data.value; break; + case 11: native.displayUrl = asset.data.value; break; + case 12: native.cta = asset.data.value; break; + } + } + }); - return str.startsWith(SCRIPT_TAG_START) && fileNameInStr && viewUrlStartInStr; -} + // Fallback for non-asset based native response (AppNexus legacy format) + if (!native.title && nativeAd.title) { + native.title = (isStr(nativeAd.title)) ? nativeAd.title : nativeAd.title.text; + } + if (!native.body && nativeAd.desc) { + native.body = nativeAd.desc; + } + if (!native.body2 && nativeAd.desc2) native.body2 = nativeAd.desc2; + if (!native.cta && nativeAd.ctatext) native.cta = nativeAd.ctatext; + if (!native.rating && nativeAd.rating) native.rating = nativeAd.rating; + if (!native.sponsoredBy && nativeAd.sponsored) native.sponsoredBy = nativeAd.sponsored; + if (!native.displayUrl && nativeAd.displayurl) native.displayUrl = nativeAd.displayurl; + if (!native.address && nativeAd.address) native.address = nativeAd.address; + if (!native.downloads && nativeAd.downloads) native.downloads = nativeAd.downloads; + if (!native.likes && nativeAd.likes) native.likes = nativeAd.likes; + if (!native.phone && nativeAd.phone) native.phone = nativeAd.phone; + if (!native.price && nativeAd.price) native.price = nativeAd.price; + if (!native.salePrice && nativeAd.saleprice) native.salePrice = nativeAd.saleprice; + + if (!native.image && nativeAd.main_img) { + native.image = { url: nativeAd.main_img.url, width: nativeAd.main_img.width, height: nativeAd.main_img.height }; + } + if (!native.icon && nativeAd.icon) { + native.icon = { url: nativeAd.icon.url, width: nativeAd.icon.width, height: nativeAd.icon.height }; + } -function getMediafuseViewabilityScriptFromJsTrackers(jsTrackerArray) { - let viewJsPayload; - if (isStr(jsTrackerArray) && strIsMediafuseViewabilityScript(jsTrackerArray)) { - viewJsPayload = jsTrackerArray; - } else if (isArray(jsTrackerArray)) { - for (let i = 0; i < jsTrackerArray.length; i++) { - const currentJsTracker = jsTrackerArray[i]; - if (strIsMediafuseViewabilityScript(currentJsTracker)) { - viewJsPayload = currentJsTracker; + bidResponse.native = native; + + let jsTrackers = nativeAd.javascript_trackers; + const viewabilityConfig = deepAccess(bid, 'ext.appnexus.viewability.config'); + if (viewabilityConfig) { + const jsTrackerDisarmed = viewabilityConfig.replace(/src=/g, 'data-src='); + if (jsTrackers == null) { + jsTrackers = [jsTrackerDisarmed]; + } else if (isStr(jsTrackers)) { + jsTrackers = [jsTrackers, jsTrackerDisarmed]; + } else if (isArray(jsTrackers)) { + jsTrackers = [...jsTrackers, jsTrackerDisarmed]; + } + } else if (isArray(nativeAd.eventtrackers)) { + const trackers = nativeAd.eventtrackers + .filter(t => t.method === 1) + .map(t => (t.url && t.url.match(VIEWABILITY_URL_START) && t.url.indexOf(VIEWABILITY_FILE_NAME) > -1) + ? t.url.replace(/src=/g, 'data-src=') + : t.url + ).filter(url => url); + + if (jsTrackers == null) { + jsTrackers = trackers; + } else if (isStr(jsTrackers)) { + jsTrackers = [jsTrackers, ...trackers]; + } else if (isArray(jsTrackers)) { + jsTrackers = [...jsTrackers, ...trackers]; + } + } + if (bidResponse.native) { + bidResponse.native.javascriptTrackers = jsTrackers; + } + } catch (e) { + logError('Mediafuse Native mapping error', e); + } + } catch (e) { + logError('Mediafuse Native JSON parse error', e); } } - } - return viewJsPayload; -} - -function getViewabilityScriptUrlFromPayload(viewJsPayload) { - // extracting the content of the src attribute - // -> substring between src=" and " - const indexOfFirstQuote = viewJsPayload.indexOf('src="') + 5; // offset of 5: the length of 'src=' + 1 - const indexOfSecondQuote = viewJsPayload.indexOf('"', indexOfFirstQuote); - const jsTrackerSrc = viewJsPayload.substring(indexOfFirstQuote, indexOfSecondQuote); - return jsTrackerSrc; -} -function formatRequest(payload, bidderRequest) { - let request = []; - const options = { - withCredentials: true - }; - - let endpointUrl = URL; + // Banner Trackers + if (mediaType === BANNER && extANData?.trackers) { + extANData.trackers.forEach(tracker => { + if (tracker.impression_urls) { + tracker.impression_urls.forEach(url => { + bidResponse.ad = (bidResponse.ad || '') + createTrackPixelHtml(url); + }); + } + }); + } - if (!hasPurpose1Consent(bidderRequest?.gdprConsent)) { - endpointUrl = URL_SIMPLE; + return bidResponse; } +}); - if (getParameterByName('apn_test').toUpperCase() === 'TRUE' || config.getConfig('apn_test') === true) { - options.customHeaders = { - 'X-Is-Test': 1 - }; +function getBidFloor(bid) { + if (!isFn(bid.getFloor)) { + return (bid.params.reserve != null) ? bid.params.reserve : null; } - - if (payload.tags.length > MAX_IMPS_PER_REQUEST) { - const clonedPayload = deepClone(payload); - - chunk(payload.tags, MAX_IMPS_PER_REQUEST).forEach(tags => { - clonedPayload.tags = tags; - const payloadString = JSON.stringify(clonedPayload); - request.push({ - method: 'POST', - url: endpointUrl, - data: payloadString, - bidderRequest, - options - }); - }); - } else { - const payloadString = JSON.stringify(payload); - request = { - method: 'POST', - url: endpointUrl, - data: payloadString, - bidderRequest, - options - }; + // Mediafuse/AppNexus generally expects USD for its RTB endpoints + let floor = bid.getFloor({ + currency: 'USD', + mediaType: '*', + size: '*' + }); + if (isPlainObject(floor) && !isNaN(floor.floor) && floor.currency === 'USD') { + return floor.floor; } - - return request; + return null; } -function newRenderer(adUnitCode, rtbBid, rendererOptions = {}) { +function newRenderer(bidRequest, rtbBid, rendererOptions = {}) { const renderer = Renderer.install({ id: rtbBid.renderer_id, url: rtbBid.renderer_url, config: rendererOptions, loaded: false, - adUnitCode + adUnitCode: bidRequest.adUnitCode, }); try { @@ -514,588 +694,312 @@ function newRenderer(adUnitCode, rtbBid, rendererOptions = {}) { } renderer.setEventHandlers({ - impression: () => logMessage('MediaFuse outstream video impression event'), - loaded: () => logMessage('MediaFuse outstream video loaded event'), + impression: () => logMessage('Mediafuse outstream video impression event'), + loaded: () => logMessage('Mediafuse outstream video loaded event'), ended: () => { - logMessage('MediaFuse outstream renderer video event'); - document.querySelector(`#${adUnitCode}`).style.display = 'none'; - } + logMessage('Mediafuse outstream renderer video event'); + const el = getAdUnitElement(bidRequest); + if (el) { + el.style.display = 'none'; + } + }, }); return renderer; } -/** - * Unpack the Server's Bid into a Prebid-compatible one. - * @param serverBid - * @param rtbBid - * @param bidderRequest - * @return Bid - */ -function newBid(serverBid, rtbBid, bidderRequest) { - const bidRequest = getBidRequest(serverBid.uuid, [bidderRequest]); - const bid = { - requestId: serverBid.uuid, - cpm: rtbBid.cpm, - creativeId: rtbBid.creative_id, - dealId: rtbBid.deal_id, - currency: 'USD', - netRevenue: true, - ttl: 300, - adUnitCode: bidRequest.adUnitCode, - mediafuse: { - buyerMemberId: rtbBid.buyer_member_id, - dealPriority: rtbBid.deal_priority, - dealCode: rtbBid.deal_code +function hidedfpContainer(container) { + try { + const el = container.querySelectorAll("div[id^='google_ads']"); + if (el[0]) { + el[0].style.setProperty('display', 'none'); } - }; - - // WE DON'T FULLY SUPPORT THIS ATM - future spot for adomain code; creating a stub for 5.0 compliance - if (rtbBid.adomain) { - bid.meta = Object.assign({}, bid.meta, { advertiserDomains: [] }); - } - - if (rtbBid.advertiser_id) { - bid.meta = Object.assign({}, bid.meta, { advertiserId: rtbBid.advertiser_id }); - } - - // temporary function; may remove at later date if/when adserver fully supports dchain - function setupDChain(rtbBid) { - const dchain = { - ver: '1.0', - complete: 0, - nodes: [{ - bsid: rtbBid.buyer_member_id.toString() - }]}; - - return dchain; - } - if (rtbBid.buyer_member_id) { - bid.meta = Object.assign({}, bid.meta, {dchain: setupDChain(rtbBid)}); - } - - if (rtbBid.brand_id) { - bid.meta = Object.assign({}, bid.meta, { brandId: rtbBid.brand_id }); + } catch (e) { + logWarn('Mediafuse: hidedfpContainer error', e); } +} - if (rtbBid.rtb.video) { - // shared video properties used for all 3 contexts - Object.assign(bid, { - width: rtbBid.rtb.video.player_width, - height: rtbBid.rtb.video.player_height, - vastImpUrl: rtbBid.notify_url, - ttl: 3600 - }); - - const videoContext = deepAccess(bidRequest, 'mediaTypes.video.context'); - switch (videoContext) { - case ADPOD: - const primaryCatId = (APPNEXUS_CATEGORY_MAPPING[rtbBid.brand_category_id]) ? APPNEXUS_CATEGORY_MAPPING[rtbBid.brand_category_id] : null; - bid.meta = Object.assign({}, bid.meta, { primaryCatId }); - const dealTier = rtbBid.deal_priority; - bid.video = { - context: ADPOD, - durationSeconds: Math.floor(rtbBid.rtb.video.duration_ms / 1000), - dealTier - }; - bid.vastUrl = rtbBid.rtb.video.asset_url; - break; - case OUTSTREAM: - bid.adResponse = serverBid; - bid.adResponse.ad = bid.adResponse.ads[0]; - bid.adResponse.ad.video = bid.adResponse.ad.rtb.video; - bid.vastXml = rtbBid.rtb.video.content; - - if (rtbBid.renderer_url) { - const videoBid = ((bidderRequest.bids) || []).find(bid => bid.bidId === serverBid.uuid); - const rendererOptions = deepAccess(videoBid, 'renderer.options'); - bid.renderer = newRenderer(bid.adUnitCode, rtbBid, rendererOptions); - } - break; - case INSTREAM: - bid.vastUrl = rtbBid.notify_url + '&redir=' + encodeURIComponent(rtbBid.rtb.video.asset_url); - break; - } - } else if (rtbBid.rtb[NATIVE]) { - const nativeAd = rtbBid.rtb[NATIVE]; - - // setting up the jsTracker: - // we put it as a data-src attribute so that the tracker isn't called - // until we have the adId (see onBidWon) - const jsTrackerDisarmed = rtbBid.viewability.config.replace('src=', 'data-src='); - - let jsTrackers = nativeAd.javascript_trackers; - - if (jsTrackers === undefined || jsTrackers === null) { - jsTrackers = jsTrackerDisarmed; - } else if (isStr(jsTrackers)) { - jsTrackers = [jsTrackers, jsTrackerDisarmed]; - } else { - jsTrackers.push(jsTrackerDisarmed); +function hideSASIframe(container) { + try { + const el = container.querySelectorAll("script[id^='sas_script']"); + if (el[0]?.nextSibling?.localName === 'iframe') { + el[0].nextSibling.style.setProperty('display', 'none'); } + } catch (e) { + logWarn('Mediafuse: hideSASIframe error', e); + } +} - bid[NATIVE] = { - title: nativeAd.title, - body: nativeAd.desc, - body2: nativeAd.desc2, - cta: nativeAd.ctatext, - rating: nativeAd.rating, - sponsoredBy: nativeAd.sponsored, - privacyLink: nativeAd.privacy_link, - address: nativeAd.address, - downloads: nativeAd.downloads, - likes: nativeAd.likes, - phone: nativeAd.phone, - price: nativeAd.price, - salePrice: nativeAd.saleprice, - clickUrl: nativeAd.link.url, - displayUrl: nativeAd.displayurl, - clickTrackers: nativeAd.link.click_trackers, - impressionTrackers: nativeAd.impression_trackers, - javascriptTrackers: jsTrackers - }; - if (nativeAd.main_img) { - bid['native'].image = { - url: nativeAd.main_img.url, - height: nativeAd.main_img.height, - width: nativeAd.main_img.width, - }; - } - if (nativeAd.icon) { - bid['native'].icon = { - url: nativeAd.icon.url, - height: nativeAd.icon.height, - width: nativeAd.icon.width, - }; - } - } else { - Object.assign(bid, { - width: rtbBid.rtb.banner.width, - height: rtbBid.rtb.banner.height, - ad: rtbBid.rtb.banner.content +function handleOutstreamRendererEvents(bid, id, eventName) { + try { + bid.renderer.handleVideoEvent({ + id, + eventName, }); - try { - if (rtbBid.rtb.trackers) { - for (let i = 0; i < rtbBid.rtb.trackers[0].impression_urls.length; i++) { - const url = rtbBid.rtb.trackers[0].impression_urls[i]; - const tracker = createTrackPixelHtml(url); - bid.ad += tracker; - } - } - } catch (error) { - logError('Error appending tracking pixel', error); - } + } catch (err) { + logWarn(`Mediafuse: handleOutstreamRendererEvents error for ${eventName}`, err); } - - return bid; } -function bidToTag(bid) { - const tag = {}; - tag.sizes = transformSizes(bid.sizes); - tag.primary_size = tag.sizes[0]; - tag.ad_types = []; - tag.uuid = bid.bidId; - if (bid.params.placementId) { - tag.id = parseInt(bid.params.placementId, 10); - } else { - tag.code = bid.params.invCode; - } - tag.allow_smaller_sizes = bid.params.allowSmallerSizes || false; - tag.use_pmt_rule = bid.params.usePaymentRule || false; - tag.prebid = true; - tag.disable_psa = true; - const bidFloor = getBidFloor(bid); - if (bidFloor) { - tag.reserve = bidFloor; - } - if (bid.params.position) { - tag.position = { 'above': 1, 'below': 2 }[bid.params.position] || 0; - } - if (bid.params.trafficSourceCode) { - tag.traffic_source_code = bid.params.trafficSourceCode; - } - if (bid.params.privateSizes) { - tag.private_sizes = transformSizes(bid.params.privateSizes); - } - if (bid.params.supplyType) { - tag.supply_type = bid.params.supplyType; - } - if (bid.params.pubClick) { - tag.pubclick = bid.params.pubClick; - } - if (bid.params.extInvCode) { - tag.ext_inv_code = bid.params.extInvCode; - } - if (bid.params.publisherId) { - tag.publisher_id = parseInt(bid.params.publisherId, 10); - } - if (bid.params.externalImpId) { - tag.external_imp_id = bid.params.externalImpId; - } - if (!isEmpty(bid.params.keywords)) { - tag.keywords = getANKewyordParamFromMaps(bid.params.keywords); - } - const gpid = deepAccess(bid, 'ortb2Imp.ext.gpid'); - if (gpid) { - tag.gpid = gpid; - } +function outstreamRender(bid, doc) { + const container = getAdUnitElement(bid); + hidedfpContainer(container); + hideSASIframe(container); + bid.renderer.push(() => { + const win = doc?.defaultView || window; + if (win.ANOutstreamVideo) { + let sizes = bid.getSize(); + if (typeof sizes === 'string' && sizes.indexOf('x') > -1) { + sizes = [sizes.split('x').map(Number)]; + } else if (!isArray(sizes) || !isArray(sizes[0])) { + sizes = [sizes]; + } - if (bid.mediaType === NATIVE || deepAccess(bid, `mediaTypes.${NATIVE}`)) { - tag.ad_types.push(NATIVE); - if (tag.sizes.length === 0) { - tag.sizes = transformSizes([1, 1]); + win.ANOutstreamVideo.renderAd({ + tagId: bid.adResponse.tag_id, + sizes: sizes, + targetId: bid.adUnitCode, + uuid: bid.requestId, + adResponse: bid.adResponse, + rendererOptions: bid.renderer.getConfig(), + }, + handleOutstreamRendererEvents.bind(null, bid) + ); } + }); +} - if (bid.nativeParams) { - const nativeRequest = buildNativeRequest(bid.nativeParams); - tag[NATIVE] = { layouts: [nativeRequest] }; - } +function hasOmidSupport(bid) { + let hasOmid = false; + const bidderParams = bid?.params; + const videoParams = bid?.mediaTypes?.video?.api; + if (bidderParams?.frameworks && isArray(bidderParams.frameworks)) { + hasOmid = bidderParams.frameworks.includes(OMID_FRAMEWORK); } - - const videoMediaType = deepAccess(bid, `mediaTypes.${VIDEO}`); - const context = deepAccess(bid, 'mediaTypes.video.context'); - - if (videoMediaType && context === 'adpod') { - tag.hb_source = 7; - } else { - tag.hb_source = 1; + if (!hasOmid && isArray(bidderParams?.video?.frameworks)) { + hasOmid = bidderParams.video.frameworks.includes(OMID_FRAMEWORK); } - if (bid.mediaType === VIDEO || videoMediaType) { - tag.ad_types.push(VIDEO); + if (!hasOmid && isArray(videoParams)) { + hasOmid = videoParams.includes(OMID_API); } + return hasOmid; +} - // instream gets vastUrl, outstream gets vastXml - if (bid.mediaType === VIDEO || (videoMediaType && context !== 'outstream')) { - tag.require_asset_url = true; - } +export const spec = { + code: BIDDER_CODE, + gvlid: GVLID, + maintainer: { email: 'indrajit@oncoredigital.com' }, + supportedMediaTypes: [BANNER, VIDEO, NATIVE], + isBidRequestValid: function (bid) { + const params = bid?.params; + if (!params) return false; + return !!(params.placementId || params.placement_id || (params.member && (params.invCode || params.inv_code))); + }, - if (bid.params.video) { - tag.video = {}; - // place any valid video params on the tag - Object.keys(bid.params.video) - .filter(param => VIDEO_TARGETING.includes(param)) - .forEach(param => { - switch (param) { - case 'context': - case 'playback_method': - let type = bid.params.video[param]; - type = (isArray(type)) ? type[0] : type; - tag.video[param] = VIDEO_MAPPING[param][type]; - break; - // Deprecating tags[].video.frameworks in favor of tags[].video_frameworks - case 'frameworks': - break; - default: - tag.video[param] = bid.params.video[param]; - } - }); + buildRequests: function (bidRequests, bidderRequest) { + const options = { + withCredentials: true + }; - if (bid.params.video.frameworks && isArray(bid.params.video.frameworks)) { - tag['video_frameworks'] = bid.params.video.frameworks; + if (getParameterByName('apn_test')?.toUpperCase() === 'TRUE' || config.getConfig('apn_test') === true) { + options.customHeaders = { 'X-Is-Test': 1 }; } - } - // use IAB ORTB values if the corresponding values weren't already set by bid.params.video - if (videoMediaType) { - tag.video = tag.video || {}; - Object.keys(videoMediaType) - .filter(param => VIDEO_RTB_TARGETING.includes(param)) - .forEach(param => { - switch (param) { - case 'minduration': - case 'maxduration': - if (typeof tag.video[param] !== 'number') tag.video[param] = videoMediaType[param]; - break; - case 'skip': - if (typeof tag.video['skippable'] !== 'boolean') tag.video['skippable'] = (videoMediaType[param] === 1); - break; - case 'skipafter': - if (typeof tag.video['skipoffset'] !== 'number') tag.video['skippoffset'] = videoMediaType[param]; - break; - case 'playbackmethod': - if (typeof tag.video['playback_method'] !== 'number') { - let type = videoMediaType[param]; - type = (isArray(type)) ? type[0] : type; - - // we only support iab's options 1-4 at this time. - if (type >= 1 && type <= 4) { - tag.video['playback_method'] = type; - } - } - break; - case 'api': - if (!tag['video_frameworks'] && isArray(videoMediaType[param])) { - // need to read thru array; remove 6 (we don't support it), swap 4 <> 5 if found (to match our adserver mapping for these specific values) - const apiTmp = videoMediaType[param].map(val => { - const v = (val === 4) ? 5 : (val === 5) ? 4 : val; - - if (v >= 1 && v <= 5) { - return v; - } - return undefined; - }).filter(v => v); - tag['video_frameworks'] = apiTmp; - } - break; - } - }); - } - - if (bid.renderer) { - tag.video = Object.assign({}, tag.video, { custom_renderer_present: true }); - } - - if (bid.params.frameworks && isArray(bid.params.frameworks)) { - tag['banner_frameworks'] = bid.params.frameworks; - } - - if (bid.mediaTypes?.banner) { - tag.ad_types.push(BANNER); - } + const requests = []; + const chunkedRequests = chunk(bidRequests, MAX_IMPS_PER_REQUEST); - if (tag.ad_types.length === 0) { - delete tag.ad_types; - } + chunkedRequests.forEach(batch => { + const data = converter.toORTB({ bidRequests: batch, bidderRequest }); - return tag; -} + let endpointUrl = ENDPOINT_URL_NORMAL; + if (!hasPurpose1Consent(bidderRequest.gdprConsent)) { + endpointUrl = ENDPOINT_URL_SIMPLE; + } -/* Turn bid request sizes into ut-compatible format */ -function transformSizes(requestSizes) { - const sizes = []; - let sizeObj = {}; - - if (isArray(requestSizes) && requestSizes.length === 2 && - !isArray(requestSizes[0])) { - sizeObj.width = parseInt(requestSizes[0], 10); - sizeObj.height = parseInt(requestSizes[1], 10); - sizes.push(sizeObj); - } else if (typeof requestSizes === 'object') { - for (let i = 0; i < requestSizes.length; i++) { - const size = requestSizes[i]; - sizeObj = {}; - sizeObj.width = parseInt(size[0], 10); - sizeObj.height = parseInt(size[1], 10); - sizes.push(sizeObj); - } - } + // Debug logic + let debugObj = {}; + const debugCookie = storage.getCookie('apn_prebid_debug'); + if (debugCookie) { + try { + debugObj = JSON.parse(debugCookie); + } catch (e) { + logWarn('Mediafuse: failed to parse debug cookie', e); + } + } else { + Object.keys(DEBUG_QUERY_PARAM_MAP).forEach(qparam => { + const qval = getParameterByName(qparam); + if (qval) debugObj[DEBUG_QUERY_PARAM_MAP[qparam]] = qval; + }); + if (Object.keys(debugObj).length > 0 && !('enabled' in debugObj)) debugObj.enabled = true; + } - return sizes; -} + if (debugObj.enabled) { + logInfo('MediaFuse Debug Auction Settings:\n\n' + JSON.stringify(debugObj, null, 4)); + endpointUrl += (endpointUrl.indexOf('?') === -1 ? '?' : '&') + + Object.keys(debugObj).filter(p => DEBUG_PARAMS.includes(p)) + .map(p => (p === 'enabled') ? `debug=1` : `${p}=${encodeURIComponent(debugObj[p])}`).join('&'); + } -function hasUserInfo(bid) { - return !!bid.params.user; -} + // member_id on the URL enables Xandr server-side routing to the correct seat; + // it is also present in ext.appnexus.member_id in the request body for exchange logic. + const memberBid = batch.find(bid => bid.params && bid.params.member); + const member = memberBid && memberBid.params.member; + if (member) { + endpointUrl += (endpointUrl.indexOf('?') === -1 ? '?' : '&') + 'member_id=' + member; + } -function hasMemberId(bid) { - return !!parseInt(bid.params.member, 10); -} + requests.push({ + method: 'POST', + url: endpointUrl, + data, + bidderRequest, + options + }); + }); -function hasAppDeviceInfo(bid) { - if (bid.params) { - return !!bid.params.app - } -} + return requests; + }, -function hasAppId(bid) { - if (bid.params && bid.params.app) { - return !!bid.params.app.id - } - return !!bid.params.app -} + interpretResponse: function (serverResponse, request) { + const bids = converter.fromORTB({ + response: serverResponse.body, + request: request.data, + context: { + ortbRequest: request.data + } + }).bids; -function hasDebug(bid) { - return !!bid.debug -} + // Debug logging + if (serverResponse.body?.debug?.debug_info) { + const debugHeader = 'MediaFuse Debug Auction for Prebid\n\n'; + let debugText = debugHeader + serverResponse.body.debug.debug_info; + debugText = debugText + .replace(/(|)/gm, '\t') + .replace(/(<\/td>|<\/th>)/gm, '\n') + .replace(/^
/gm, '') + .replace(/(
\n|
)/gm, '\n') + .replace(/

(.*)<\/h1>/gm, '\n\n===== $1 =====\n\n') + .replace(/(.*)<\/h[2-6]>/gm, '\n\n*** $1 ***\n\n') + .replace(/(<([^>]+)>)/igm, ''); + logMessage(debugText); + } -function hasAdPod(bid) { - return ( - bid.mediaTypes && - bid.mediaTypes.video && - bid.mediaTypes.video.context === ADPOD - ); -} + return bids; + }, -function hasOmidSupport(bid) { - let hasOmid = false; - const bidderParams = bid.params; - const videoParams = bid.params.video; - if (bidderParams.frameworks && isArray(bidderParams.frameworks)) { - hasOmid = bid.params.frameworks.includes(6); - } - if (!hasOmid && videoParams && videoParams.frameworks && isArray(videoParams.frameworks)) { - hasOmid = bid.params.video.frameworks.includes(6); - } - return hasOmid; -} + getUserSyncs: function (syncOptions, serverResponses, gdprConsent, uspConsent, gppConsent) { + const syncs = []; + let gdprParams = ''; -/** - * Expand an adpod placement into a set of request objects according to the - * total adpod duration and the range of duration seconds. Sets minduration/ - * maxduration video property according to requireExactDuration configuration - */ -function createAdPodRequest(tags, adPodBid) { - const { durationRangeSec, requireExactDuration } = adPodBid.mediaTypes.video; + if (gdprConsent) { + if (typeof gdprConsent.gdprApplies === 'boolean') { + gdprParams = `?gdpr=${Number(gdprConsent.gdprApplies)}&gdpr_consent=${gdprConsent.consentString}`; + } else { + gdprParams = `?gdpr_consent=${gdprConsent.consentString}`; + } + } - const numberOfPlacements = getAdPodPlacementNumber(adPodBid.mediaTypes.video); - const maxDuration = Math.max(...durationRangeSec); + if (syncOptions.iframeEnabled && hasPurpose1Consent(gdprConsent)) { + syncs.push({ + type: 'iframe', + url: 'https://acdn.adnxs.com/dmp/async_usersync.html' + gdprParams + }); + } - const tagToDuplicate = tags.filter(tag => tag.uuid === adPodBid.bidId); - const request = fill(...tagToDuplicate, numberOfPlacements); + if (syncOptions.pixelEnabled && serverResponses.length > 0) { + const userSync = deepAccess(serverResponses[0], 'body.ext.appnexus.userSync'); + if (userSync && userSync.url) { + let url = userSync.url; + if (gdprParams) { + url += (url.indexOf('?') === -1 ? '?' : '&') + gdprParams.substring(1); + } + syncs.push({ + type: 'image', + url: url + }); + } + } + return syncs; + }, - if (requireExactDuration) { - const divider = Math.ceil(numberOfPlacements / durationRangeSec.length); - const chunked = chunk(request, divider); + onBidWon: function (bid) { + if (bid.native) { + reloadViewabilityScriptWithCorrectParameters(bid); + } + }, - // each configured duration is set as min/maxduration for a subset of requests - durationRangeSec.forEach((duration, index) => { - chunked[index].forEach(tag => { - setVideoProperty(tag, 'minduration', duration); - setVideoProperty(tag, 'maxduration', duration); - }); - }); - } else { - // all maxdurations should be the same - request.forEach(tag => setVideoProperty(tag, 'maxduration', maxDuration)); + onBidderError: function ({ error, bidderRequest }) { + logError(`Mediafuse Bidder Error: ${error.message || error}`, bidderRequest); } +}; - return request; -} - -function getAdPodPlacementNumber(videoParams) { - const { adPodDurationSec, durationRangeSec, requireExactDuration } = videoParams; - const minAllowedDuration = Math.min(...durationRangeSec); - const numberOfPlacements = Math.floor(adPodDurationSec / minAllowedDuration); +function reloadViewabilityScriptWithCorrectParameters(bid) { + const viewJsPayload = getMediafuseViewabilityScriptFromJsTrackers(bid.native.javascriptTrackers); - return requireExactDuration - ? Math.max(numberOfPlacements, durationRangeSec.length) - : numberOfPlacements; -} + if (viewJsPayload) { + const prebidParams = 'pbjs_adid=' + (bid.adId || bid.requestId) + ';pbjs_auc=' + bid.adUnitCode; + const jsTrackerSrc = getViewabilityScriptUrlFromPayload(viewJsPayload); + const newJsTrackerSrc = jsTrackerSrc.replace('dom_id=%native_dom_id%', prebidParams); -function setVideoProperty(tag, key, value) { - if (isEmpty(tag.video)) { tag.video = {}; } - tag.video[key] = value; -} + // find iframe containing script tag + const frameArray = document.getElementsByTagName('iframe'); -function getRtbBid(tag) { - return tag && tag.ads && tag.ads.length && ((tag.ads) || []).find(ad => ad.rtb); -} + // flag to modify only one script — prevents multiple scripts from pointing to the same creative + let modifiedAScript = false; -function buildNativeRequest(params) { - const request = {}; - - // map standard prebid native asset identifier to /ut parameters - // e.g., tag specifies `body` but /ut only knows `description`. - // mapping may be in form {tag: ''} or - // {tag: {serverName: '', requiredParams: {...}}} - Object.keys(params).forEach(key => { - // check if one of the forms is used, otherwise - // a mapping wasn't specified so pass the key straight through - const requestKey = - (NATIVE_MAPPING[key] && NATIVE_MAPPING[key].serverName) || - NATIVE_MAPPING[key] || - key; - - // required params are always passed on request - const requiredParams = NATIVE_MAPPING[key] && NATIVE_MAPPING[key].requiredParams; - request[requestKey] = Object.assign({}, requiredParams, params[key]); - - // convert the sizes of image/icon assets to proper format (if needed) - const isImageAsset = !!(requestKey === NATIVE_MAPPING.image.serverName || requestKey === NATIVE_MAPPING.icon.serverName); - if (isImageAsset && request[requestKey].sizes) { - const sizes = request[requestKey].sizes; - if (isArrayOfNums(sizes) || (isArray(sizes) && sizes.length > 0 && sizes.every(sz => isArrayOfNums(sz)))) { - request[requestKey].sizes = transformSizes(request[requestKey].sizes); + // loop on all iframes + for (let i = 0; i < frameArray.length && !modifiedAScript; i++) { + const currentFrame = frameArray[i]; + try { + const nestedDoc = currentFrame.contentDocument || currentFrame.contentWindow.document; + if (nestedDoc) { + const scriptArray = nestedDoc.getElementsByTagName('script'); + for (let j = 0; j < scriptArray.length && !modifiedAScript; j++) { + const currentScript = scriptArray[j]; + if (currentScript.getAttribute('data-src') === jsTrackerSrc) { + currentScript.setAttribute('src', newJsTrackerSrc); + currentScript.removeAttribute('data-src'); + modifiedAScript = true; + } + } + } + } catch (exception) { + if (!(exception instanceof DOMException && exception.name === 'SecurityError')) { + throw exception; + } } } - - if (requestKey === NATIVE_MAPPING.privacyLink) { - request.privacy_supported = true; - } - }); - - return request; -} - -/** - * This function hides google div container for outstream bids to remove unwanted space on page. Mediafuse renderer creates a new iframe outside of google iframe to render the outstream creative. - * @param {string} elementId element id - */ -function hidedfpContainer(elementId) { - var el = document.getElementById(elementId).querySelectorAll("div[id^='google_ads']"); - if (el[0]) { - el[0].style.setProperty('display', 'none'); - } -} - -function hideSASIframe(elementId) { - try { - // find script tag with id 'sas_script'. This ensures it only works if you're using Smart Ad Server. - const el = document.getElementById(elementId).querySelectorAll("script[id^='sas_script']"); - if (el[0].nextSibling && el[0].nextSibling.localName === 'iframe') { - el[0].nextSibling.style.setProperty('display', 'none'); - } - } catch (e) { - // element not found! } } -function outstreamRender(bid) { - hidedfpContainer(bid.adUnitCode); - hideSASIframe(bid.adUnitCode); - // push to render queue because ANOutstreamVideo may not be loaded - bid.renderer.push(() => { - window.ANOutstreamVideo.renderAd({ - tagId: bid.adResponse.tag_id, - sizes: [bid.getSize().split('x')], - targetId: bid.adUnitCode, // target div id to render video - uuid: bid.adResponse.uuid, - adResponse: bid.adResponse, - rendererOptions: bid.renderer.getConfig() - }, handleOutstreamRendererEvents.bind(null, bid)); - }); -} - -function handleOutstreamRendererEvents(bid, id, eventName) { - bid.renderer.handleVideoEvent({ id, eventName }); -} +function strIsMediafuseViewabilityScript(str) { + const regexMatchUrlStart = str.match(VIEWABILITY_URL_START); + const viewUrlStartInStr = regexMatchUrlStart != null && regexMatchUrlStart.length >= 1; + const regexMatchFileName = str.match(VIEWABILITY_FILE_NAME); + const fileNameInStr = regexMatchFileName != null && regexMatchFileName.length >= 1; -function parseMediaType(rtbBid) { - const adType = rtbBid.ad_type; - if (adType === VIDEO) { - return VIDEO; - } else if (adType === NATIVE) { - return NATIVE; - } else { - return BANNER; - } + return str.startsWith(' { /* ----- pmguid:end ------ */ /** - * 获取一个对象的某个值,如果没有则返回空字符串 + * Get a nested property value from object, return empty string if not found * - * @param {Object} obj 对象 - * @param {...string} keys 键名 + * @param {Object} obj object + * @param {...string} keys key path * @return {any} */ function getProperty(obj, ...keys) { @@ -84,7 +84,21 @@ function getProperty(obj, ...keys) { } /** - * 获取底价 + * Retrieve device platform/OS, priority order: userAgentData.platform > navigator.platform > UA parsing + * @returns {string} + */ +function getDeviceOs() { + if (navigator.userAgentData?.platform) { + return navigator.userAgentData.platform; + } + if (navigator.platform) { + return navigator.platform; + } + return getOsInfo().os || ''; +} + +/** + * Get bid floor * @param {*} bid * @param {*} mediaType * @param {*} sizes @@ -106,11 +120,11 @@ function getProperty(obj, ...keys) { // return floor; // } -// 支持的广告尺寸 +// Supported ad sizes const mediagoAdSize = normalAdSize; /** - * 获取广告位配置 + * Get ad slot config * @param {Array} validBidRequests an an array of bids * @param {Object} bidderRequest The master bidRequest object * @return {Object} @@ -124,7 +138,7 @@ function getItems(validBidRequests, bidderRequest) { const sizes = transformSizes(getProperty(req, 'sizes')); let matchSize; - // 确认尺寸是否符合我们要求 + // Validate size meets requirements for (const size of sizes) { matchSize = mediagoAdSize.find(item => size.width === item.w && size.height === item.h); if (matchSize) { @@ -153,7 +167,7 @@ function getItems(validBidRequests, bidderRequest) { } // if (mediaTypes.native) {} - // banner广告类型 + // Banner ad type if (mediaTypes.banner) { // fix id is not unique where there are multiple requests in the same page const id = getProperty(req, 'bidId') || ('' + (i + 1) + Math.random().toString(36).substring(2, 15)); @@ -169,10 +183,11 @@ function getItems(validBidRequests, bidderRequest) { ext: { adUnitCode: req.adUnitCode, referrer: getReferrer(req, bidderRequest), - ortb2Imp: utils.deepAccess(req, 'ortb2Imp'), // 传入完整对象,分析日志数据 - gpid: gpid, // 加入后无法返回广告 + ortb2Imp: utils.deepAccess(req, 'ortb2Imp'), + gpid: gpid, adslot: utils.deepAccess(req, 'ortb2Imp.ext.data.adserver.adslot', '', ''), publisher: req.params.publisher || '', + transactionId: utils.deepAccess(req, 'ortb2Imp.ext.tid') || req.transactionId || '', ...gdprConsent // gdpr }, tagid: req.params && req.params.tagid @@ -195,7 +210,7 @@ export function getCurrentTimeToUTCString() { } /** - * 获取rtb请求参数 + * Get RTB request params * * @param {Array} validBidRequests an an array of bids * @param {Object} bidderRequest The master bidRequest object @@ -240,11 +255,12 @@ function getParam(validBidRequests, bidderRequest) { // language: 'en', // os: 'Microsoft Windows', // ua: 'Mozilla/5.0 (Linux; Android 12; SM-G970U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Mobile Safari/537.36', - os: navigator.platform || '', + os: getDeviceOs(), ua: navigator.userAgent, language: /en/.test(navigator.language) ? 'en' : navigator.language }, ext: { + pbjsversion: '$prebid.version$', eids, bidsUserIdAsEids, firstPartyData, diff --git a/modules/mediakeysBidAdapter.js b/modules/mediakeysBidAdapter.js index a4eeb591d90..c7065a95f2b 100644 --- a/modules/mediakeysBidAdapter.js +++ b/modules/mediakeysBidAdapter.js @@ -1,4 +1,3 @@ -import {getDNT} from '../libraries/dnt/index.js'; import { cleanObj, deepAccess, @@ -17,10 +16,11 @@ import { mergeDeep, triggerPixel } from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {config} from '../src/config.js'; -import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js'; -import {convertOrtbRequestToProprietaryNative} from '../src/native.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { config } from '../src/config.js'; +import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; +import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; +import { getDNT } from '../libraries/dnt/index.js'; const AUCTION_TYPE = 1; const BIDDER_CODE = 'mediakeys'; @@ -81,7 +81,8 @@ const ORTB_VIDEO_PARAMS = { playbackend: value => [1, 2, 3].indexOf(value) !== -1, delivery: value => [1, 2, 3].indexOf(value) !== -1, pos: value => [0, 1, 2, 3, 4, 5, 6, 7].indexOf(value) !== -1, - api: value => Array.isArray(value) && value.every(v => [1, 2, 3, 4, 5, 6].indexOf(v) !== -1)}; + api: value => Array.isArray(value) && value.every(v => [1, 2, 3, 4, 5, 6].indexOf(v) !== -1) +}; /** * Returns the OpenRtb deviceType id detected from User Agent @@ -226,7 +227,7 @@ function createBannerImp(bid) { const format = []; sizes.forEach(function (size) { if (size.length && size.length > 1) { - format.push({w: size[0], h: size[1]}); + format.push({ w: size[0], h: size[1] }); } }); banner.format = format; diff --git a/modules/medianetAnalyticsAdapter.js b/modules/medianetAnalyticsAdapter.js index 734e2120b7b..56bef3e6a00 100644 --- a/modules/medianetAnalyticsAdapter.js +++ b/modules/medianetAnalyticsAdapter.js @@ -9,15 +9,15 @@ import { parseUrl, safeJSONEncode, } from '../src/utils.js'; -import {config} from '../src/config.js'; +import { config } from '../src/config.js'; import adapterManager from '../src/adapterManager.js'; import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; -import {BID_STATUS, EVENTS, REJECTION_REASON, S2S, TARGETING_KEYS} from '../src/constants.js'; -import {getRefererInfo} from '../src/refererDetection.js'; -import {ajax} from '../src/ajax.js'; -import {getPriceByGranularity} from '../src/auction.js'; -import {MODULE_TYPE_ANALYTICS} from '../src/activities/modules.js'; -import {registerVastTrackers} from '../libraries/vastTrackers/vastTrackers.js'; +import { BID_STATUS, EVENTS, REJECTION_REASON, S2S, TARGETING_KEYS } from '../src/constants.js'; +import { getRefererInfo } from '../src/refererDetection.js'; +import { ajax } from '../src/ajax.js'; +import { getPriceByGranularity } from '../src/auction.js'; +import { MODULE_TYPE_ANALYTICS } from '../src/activities/modules.js'; +import { registerVastTrackers } from '../libraries/vastTrackers/vastTrackers.js'; import { filterBidsListByFilters, findBidObj, @@ -33,7 +33,7 @@ import { getLoggingPayload, shouldLogAPPR } from '../libraries/medianetUtils/logger.js'; -import {KeysMap} from '../libraries/medianetUtils/logKeys.js'; +import { KeysMap } from '../libraries/medianetUtils/logKeys.js'; import { LOGGING_DELAY, BID_FLOOR_REJECTED, @@ -64,7 +64,7 @@ import { WINNING_AUCTION_MISSING_ERROR, WINNING_BID_ABSENT_ERROR, ERROR_IWB_BID_MISSING, POST_ENDPOINT_RA } from '../libraries/medianetUtils/constants.js'; -import {getGlobal} from '../src/prebidGlobal.js'; +import { getGlobal } from '../src/prebidGlobal.js'; // General Constants const ADAPTER_CODE = 'medianetAnalytics'; @@ -393,7 +393,7 @@ function addS2sInfo(auctionObj, bidderRequests) { bidderRequest.bids.forEach((bidRequest) => { if (bidRequest.src !== S2S.SRC) return; - const bidObjs = filterBidsListByFilters(auctionObj.bidsReceived, {bidId: bidRequest.bidId}); + const bidObjs = filterBidsListByFilters(auctionObj.bidsReceived, { bidId: bidRequest.bidId }); bidObjs.forEach((bidObj) => { bidObj.serverLatencyMillis = bidderRequest.serverResponseTimeMs; @@ -785,11 +785,14 @@ function bidderDoneHandler(eventType, args) { } function adRenderFailedHandler(eventType, args) { - const {reason, message, bid: { - auctionId, - adUnitCode, - bidder, - creativeId}} = args; + const { + reason, message, bid: { + auctionId, + adUnitCode, + bidder, + creativeId + } + } = args; errorLogger(eventType, { reason, message, @@ -801,7 +804,7 @@ function adRenderFailedHandler(eventType, args) { } function adRenderSucceededHandler(eventType, args) { - const {bid: {auctionId, adUnitCode, bidder, creativeId}} = args; + const { bid: { auctionId, adUnitCode, bidder, creativeId } } = args; errorLogger(eventType, { auctionId, adUnitCode, diff --git a/modules/medianetBidAdapter.js b/modules/medianetBidAdapter.js index cec3880a6d7..f8ff33ddc81 100644 --- a/modules/medianetBidAdapter.js +++ b/modules/medianetBidAdapter.js @@ -10,19 +10,20 @@ import { deepClone, deepSetValue, getWindowTop } from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {config} from '../src/config.js'; -import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js'; -import {Renderer} from '../src/Renderer.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { config } from '../src/config.js'; +import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; +import { Renderer } from '../src/Renderer.js'; import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; -import {getGptSlotInfoForAdUnitCode} from '../libraries/gptUtils/gptUtils.js'; -import {getViewportCoordinates} from '../libraries/viewport/viewport.js'; -import {filterBidsListByFilters, getTopWindowReferrer} from '../libraries/medianetUtils/utils.js'; -import {errorLogger} from '../libraries/medianetUtils/logger.js'; -import {GLOBAL_VENDOR_ID, MEDIANET} from '../libraries/medianetUtils/constants.js'; -import {getGlobal} from '../src/prebidGlobal.js'; -import {getBoundingClientRect} from '../libraries/boundingClientRect/boundingClientRect.js'; -import {getMinSize} from '../libraries/sizeUtils/sizeUtils.js'; +import { getGptSlotInfoForAdUnitCode } from '../libraries/gptUtils/gptUtils.js'; +import { getViewportCoordinates } from '../libraries/viewport/viewport.js'; +import { filterBidsListByFilters, getTopWindowReferrer } from '../libraries/medianetUtils/utils.js'; +import { errorLogger } from '../libraries/medianetUtils/logger.js'; +import { GLOBAL_VENDOR_ID, MEDIANET } from '../libraries/medianetUtils/constants.js'; +import { getGlobal } from '../src/prebidGlobal.js'; +import { getBoundingClientRect } from '../libraries/boundingClientRect/boundingClientRect.js'; +import { getMinSize } from '../libraries/sizeUtils/sizeUtils.js'; +import { getAdUnitElement } from '../src/utils/adUnits.js'; /** * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest @@ -133,11 +134,11 @@ function getWindowSize() { } } -function getCoordinates(adUnitCode) { - let element = document.getElementById(adUnitCode); - if (!element && adUnitCode.indexOf('/') !== -1) { +function getCoordinates(bidRequest) { + let element = getAdUnitElement(bidRequest); + if (!element && bidRequest.adUnitCode.indexOf('/') !== -1) { // now it means that adUnitCode is GAM AdUnitPath - const {divId} = getGptSlotInfoForAdUnitCode(adUnitCode); + const { divId } = getGptSlotInfoForAdUnitCode(bidRequest.adUnitCode); if (isStr(divId)) { element = document.getElementById(divId); } @@ -168,7 +169,7 @@ function extParams(bidRequest, bidderRequests) { const gdprApplies = !!(gdpr && gdpr.gdprApplies); const uspApplies = !!(uspConsent); const coppaApplies = !!(config.getConfig('coppa')); - const {top = -1, right = -1, bottom = -1, left = -1} = getViewportCoordinates(); + const { top = -1, right = -1, bottom = -1, left = -1 } = getViewportCoordinates(); return Object.assign({}, { customer_id: params.cid }, { prebid_version: 'v' + '$prebid.version$' }, @@ -176,11 +177,11 @@ function extParams(bidRequest, bidderRequests) { (gdprApplies) && { gdpr_consent_string: gdpr.consentString || '' }, { usp_applies: uspApplies }, uspApplies && { usp_consent_string: uspConsent || '' }, - {coppa_applies: coppaApplies}, + { coppa_applies: coppaApplies }, windowSize.w !== -1 && windowSize.h !== -1 && { screen: windowSize }, userId && { user_id: userId }, getGlobal().medianetGlobals.analyticsEnabled && { analytics: true }, - !isEmpty(sChain) && {schain: sChain}, + !isEmpty(sChain) && { schain: sChain }, { vcoords: { top_left: { x: left, y: top }, @@ -239,7 +240,7 @@ function slotParams(bidRequest, bidderRequests) { if (bidFloor) { params.bidfloor = bidFloor; } - const coordinates = getCoordinates(bidRequest.adUnitCode); + const coordinates = getCoordinates(bidRequest); if (coordinates && params.banner && params.banner.length !== 0) { const normCoordinates = normalizeCoordinates(coordinates); params.ext.coordinates = normCoordinates; @@ -256,9 +257,6 @@ function slotParams(bidRequest, bidderRequests) { if (floorInfo && floorInfo.length > 0) { params.bidfloors = floorInfo; } - if (bidderRequests.paapi?.enabled) { - params.ext.ae = bidRequest?.ortb2Imp?.ext?.ae; - } return params; } @@ -282,7 +280,7 @@ function getBidFloorByType(bidRequest) { return floorInfo; } function setFloorInfo(bidRequest, mediaType, size, floorInfo) { - const floor = bidRequest.getFloor({currency: 'USD', mediaType: mediaType, size: size}) || {}; + const floor = bidRequest.getFloor({ currency: 'USD', mediaType: mediaType, size: size }) || {}; if (size.length > 1) floor.size = size; floor.mediaType = mediaType; floorInfo.push(floor); @@ -299,7 +297,7 @@ function getSlotVisibility(topLeft, size) { return 0; } - return getOverlapArea(topLeft, bottomRight, {x: 0, y: 0}, {x: windowSize.w, y: windowSize.h}) / maxArea; + return getOverlapArea(topLeft, bottomRight, { x: 0, y: 0 }, { x: windowSize.w, y: windowSize.h }) / maxArea; } // find the overlapping area between two rectangles @@ -313,7 +311,7 @@ function getOverlapArea(topLeft1, bottomRight1, topLeft2, bottomRight2) { } function normalizeCoordinates(coordinates) { - const {scrollX, scrollY} = window; + const { scrollX, scrollY } = window; return { top_left: { x: coordinates.top_left.x + scrollX, @@ -487,7 +485,7 @@ export const spec = { * Unpack the response from the server into a list of bids. * * @param {*} serverResponse A successful response from the server. - * @returns {{bids: *[], fledgeAuctionConfigs: *[]} | *[]} An object containing bids and fledgeAuctionConfigs if present, otherwise an array of bids. + * @returns {*[]} An array of bids. */ interpretResponse: function(serverResponse, request) { let validBids = []; @@ -502,28 +500,17 @@ export const spec = { validBids = bids.filter(bid => isValidBid(bid)); validBids.forEach(addRenderer); } - const fledgeAuctionConfigs = deepAccess(serverResponse, 'body.ext.paApiAuctionConfigs') || []; - const ortbAuctionConfigs = deepAccess(serverResponse, 'body.ext.igi') || []; - if (fledgeAuctionConfigs.length === 0 && ortbAuctionConfigs.length === 0) { - return validBids; - } - if (ortbAuctionConfigs.length > 0) { - fledgeAuctionConfigs.push(...ortbAuctionConfigs.map(({igs}) => igs || []).flat()); - } - return { - bids: validBids, - paapi: fledgeAuctionConfigs, - } + return validBids; }, getUserSyncs: function(syncOptions, serverResponses) { const cookieSyncUrls = fetchCookieSyncUrls(serverResponses); if (syncOptions.iframeEnabled) { - return filterBidsListByFilters(cookieSyncUrls, {type: 'iframe'}); + return filterBidsListByFilters(cookieSyncUrls, { type: 'iframe' }); } if (syncOptions.pixelEnabled) { - return filterBidsListByFilters(cookieSyncUrls, {type: 'image'}); + return filterBidsListByFilters(cookieSyncUrls, { type: 'image' }); } }, @@ -567,7 +554,7 @@ export const spec = { } catch (e) {} }, - onBidderError: ({error, bidderRequest}) => { + onBidderError: ({ error, bidderRequest }) => { try { const eventData = { name: EVENTS.BIDDER_ERROR, diff --git a/modules/medianetBidAdapter.md b/modules/medianetBidAdapter.md index 500c9f3f12b..1af49e5b6dc 100644 --- a/modules/medianetBidAdapter.md +++ b/modules/medianetBidAdapter.md @@ -181,23 +181,3 @@ var adUnits = [{ ``` - -# Protected Audience API (FLEDGE) - -In order to enable PAAPI auctions follow the instructions below: - -1. Add the paapiForGpt and paapi modules to your prebid bundle. -2. Add the following configuration for the module -``` -pbjs.que.push(function() { - pbjs.setConfig({ - paapi: { - enabled: true, - bidders: ['medianet'], - defaultForSlots: 1 - } - }); -}); -``` - -For a detailed guide to enabling PAAPI auctions follow Prebid's documentation on [paapiForGpt](https://docs.prebid.org/dev-docs/modules/paapiForGpt.html) diff --git a/modules/medianetRtdProvider.js b/modules/medianetRtdProvider.js index a9a0bb47d63..b22b46625a7 100644 --- a/modules/medianetRtdProvider.js +++ b/modules/medianetRtdProvider.js @@ -1,7 +1,7 @@ -import {isEmptyStr, isFn, isStr, logError, mergeDeep} from '../src/utils.js'; -import {loadExternalScript} from '../src/adloader.js'; -import {submodule} from '../src/hook.js'; -import {getGlobal} from '../src/prebidGlobal.js'; +import { isEmptyStr, isFn, isStr, logError, mergeDeep } from '../src/utils.js'; +import { loadExternalScript } from '../src/adloader.js'; +import { submodule } from '../src/hook.js'; +import { getGlobal } from '../src/prebidGlobal.js'; import { MODULE_TYPE_RTD } from '../src/activities/modules.js'; @@ -26,7 +26,7 @@ function init(config) { executeCommand(() => window.mnjs.setData({ module: 'iref', name: 'initIRefresh', - data: {config, prebidGlobal: getGlobal()}, + data: { config, prebidGlobal: getGlobal() }, }, SOURCE)); return true; } @@ -34,7 +34,7 @@ function init(config) { function getBidRequestData(requestBidsProps, callback, config, userConsent) { executeCommand(() => { const adUnits = getAdUnits(requestBidsProps.adUnits, requestBidsProps.adUnitCodes); - const request = window.mnjs.onPrebidRequestBid({requestBidsProps, config, userConsent}); + const request = window.mnjs.onPrebidRequestBid({ requestBidsProps, config, userConsent }); if (!request) { callback(); return; @@ -56,7 +56,7 @@ function onAuctionInitEvent(auctionInit) { executeCommand(() => window.mnjs.setData({ module: 'iref', name: 'auctionInit', - data: {auction: auctionInit}, + data: { auction: auctionInit }, }, SOURCE)); } diff --git a/modules/mediasniperBidAdapter.js b/modules/mediasniperBidAdapter.js index eaf8e0606b2..2cf49393d17 100644 --- a/modules/mediasniperBidAdapter.js +++ b/modules/mediasniperBidAdapter.js @@ -15,8 +15,8 @@ import { triggerPixel, } from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER} from '../src/mediaTypes.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER } from '../src/mediaTypes.js'; const BIDDER_CODE = 'mediasniper'; const DEFAULT_BID_TTL = 360; diff --git a/modules/mediasquareBidAdapter.js b/modules/mediasquareBidAdapter.js index cb358ec4687..dd91723af75 100644 --- a/modules/mediasquareBidAdapter.js +++ b/modules/mediasquareBidAdapter.js @@ -1,9 +1,9 @@ -import {ajax} from '../src/ajax.js'; -import {config} from '../src/config.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js'; +import { ajax } from '../src/ajax.js'; +import { config } from '../src/config.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; -import {Renderer} from '../src/Renderer.js'; +import { Renderer } from '../src/Renderer.js'; import { getRefererInfo } from '../src/refererDetection.js'; /** @@ -65,11 +65,11 @@ export const spec = { if (typeof adunitValue.getFloor === 'function') { if (Array.isArray(adunitValue.sizes)) { adunitValue.sizes.forEach(value => { - const tmpFloor = adunitValue.getFloor({currency: 'USD', mediaType: '*', size: value}); + const tmpFloor = adunitValue.getFloor({ currency: 'USD', mediaType: '*', size: value }); if (tmpFloor !== null && tmpFloor !== undefined && Object.keys(tmpFloor).length !== 0) { code.floor[value.join('x')] = tmpFloor; } }); } - const tmpFloor = adunitValue.getFloor({currency: 'USD', mediaType: '*', size: '*'}); + const tmpFloor = adunitValue.getFloor({ currency: 'USD', mediaType: '*', size: '*' }); if (tmpFloor !== null && tmpFloor !== undefined && Object.keys(tmpFloor).length !== 0) { code.floor['*'] = tmpFloor; } } if (adunitValue.ortb2Imp) { code.ortb2Imp = adunitValue.ortb2Imp } @@ -139,6 +139,9 @@ export const spec = { bidResponse['mediasquare'][param] = value[param]; } }); + if ('burls' in value) { + bidResponse['mediasquare']['burls'] = value['burls']; + } if ('native' in value) { bidResponse['native'] = value['native']; bidResponse['mediaType'] = 'native'; @@ -182,9 +185,22 @@ export const spec = { } const params = { pbjs: '$prebid.version$', referer: encodeURIComponent(getRefererInfo().page || getRefererInfo().topmostLocation) }; const endpoint = document.location.search.match(/msq_test=true/) ? BIDDER_URL_TEST : BIDDER_URL_PROD; - let paramsToSearchFor = ['bidder', 'code', 'match', 'hasConsent', 'context', 'increment', 'ova']; + if (bid.hasOwnProperty('mediasquare')) { - paramsToSearchFor.forEach(param => { + // if burls then fire tracking pixels and exit + if (bid.mediasquare.hasOwnProperty('burls') && Array.isArray(bid.mediasquare.burls) && bid.mediasquare.burls.length > 0) { + bid.mediasquare.burls.forEach(burl => { + const url = burl && burl.url; + if (!url) return; + const method = (burl.method ?? "GET").toUpperCase(); + const data = (method === "POST" && burl.data ? burl.data : null); + ajax(url, null, data ? JSON.stringify(data) : null, { method: method, withCredentials: true }); + }); + return true; + } + // no burl so checking for other mediasquare params + let msqParamsToSearchFor = ['bidder', 'code', 'match', 'hasConsent', 'context', 'increment', 'ova']; + msqParamsToSearchFor.forEach(param => { if (bid['mediasquare'].hasOwnProperty(param)) { params[param] = bid['mediasquare'][param]; if (typeof params[param] === 'number') { @@ -193,7 +209,8 @@ export const spec = { } }); }; - paramsToSearchFor = ['cpm', 'size', 'mediaType', 'currency', 'creativeId', 'adUnitCode', 'timeToRespond', 'requestId', 'auctionId', 'originalCpm', 'originalCurrency']; + + let paramsToSearchFor = ['cpm', 'size', 'mediaType', 'currency', 'creativeId', 'adUnitCode', 'timeToRespond', 'requestId', 'auctionId', 'originalCpm', 'originalCurrency']; paramsToSearchFor.forEach(param => { if (bid.hasOwnProperty(param)) { params[param] = bid[param]; @@ -202,7 +219,7 @@ export const spec = { } } }); - ajax(endpoint + BIDDER_ENDPOINT_WINNING, null, JSON.stringify(params), {method: 'POST', withCredentials: true}); + ajax(endpoint + BIDDER_ENDPOINT_WINNING, null, JSON.stringify(params), { method: 'POST', withCredentials: true }); return true; } diff --git a/modules/merkleIdSystem.js b/modules/merkleIdSystem.js index cc4bfbf5e98..166bb84b7e1 100644 --- a/modules/merkleIdSystem.js +++ b/modules/merkleIdSystem.js @@ -7,9 +7,9 @@ import { logInfo, logError, logWarn } from '../src/utils.js'; import * as ajaxLib from '../src/ajax.js'; -import {submodule} from '../src/hook.js' -import {getStorageManager} from '../src/storageManager.js'; -import {MODULE_TYPE_UID} from '../src/activities/modules.js'; +import { submodule } from '../src/hook.js' +import { getStorageManager } from '../src/storageManager.js'; +import { MODULE_TYPE_UID } from '../src/activities/modules.js'; /** * @typedef {import('../modules/userId/index.js').Submodule} Submodule @@ -23,7 +23,7 @@ const ID_URL = 'https://prebid.sv.rkdms.com/identity/'; const DEFAULT_REFRESH = 7 * 3600; const SESSION_COOKIE_NAME = '_svsid'; -export const storage = getStorageManager({moduleType: MODULE_TYPE_UID, moduleName: MODULE_NAME}); +export const storage = getStorageManager({ moduleType: MODULE_TYPE_UID, moduleName: MODULE_NAME }); function getSession(configParams) { let session = null; @@ -85,7 +85,7 @@ function generateId(configParams, configStorage) { logError(`${MODULE_NAME}: merkleId fetch encountered an error`, error); callback(); }, - {method: 'GET', withCredentials: true} + { method: 'GET', withCredentials: true } ); }; return resp; @@ -111,14 +111,14 @@ export const merkleIdSubmodule = { logInfo('Merkle id ' + JSON.stringify(id)); if (id) { - return {'merkleId': id} + return { 'merkleId': id } } // Supports multiple IDs for different SSPs const merkleIds = (value && value?.merkleId && Array.isArray(value.merkleId)) ? value.merkleId : undefined; logInfo('merkleIds: ' + JSON.stringify(merkleIds)); - return merkleIds ? {'merkleId': merkleIds} : undefined; + return merkleIds ? { 'merkleId': merkleIds } : undefined; }, /** @@ -159,7 +159,7 @@ export const merkleIdSubmodule = { const configStorage = (config && config.storage) || {}; const resp = generateId(configParams, configStorage) - return {callback: resp}; + return { callback: resp }; }, extendId: function (config = {}, consentData, storedId) { logInfo('User ID - stored id ' + storedId); @@ -181,7 +181,7 @@ export const merkleIdSubmodule = { const configStorage = (config && config.storage) || {}; if (configStorage && configStorage.refreshInSeconds && typeof configParams.refreshInSeconds === 'number') { - return {id: storedId}; + return { id: storedId }; } let refreshInSeconds = DEFAULT_REFRESH; @@ -197,12 +197,12 @@ export const merkleIdSubmodule = { if (refreshNeeded) { logInfo('User ID - merkleId needs refreshing id'); const resp = generateId(configParams, configStorage); - return {callback: resp}; + return { callback: resp }; } } logInfo('User ID - merkleId not refreshed'); - return {id: storedId}; + return { id: storedId }; }, eids: { 'merkleId': { diff --git a/modules/mgidBidAdapter.js b/modules/mgidBidAdapter.js index 8044768bdad..f370d220464 100644 --- a/modules/mgidBidAdapter.js +++ b/modules/mgidBidAdapter.js @@ -1,4 +1,3 @@ -import {getDNT} from '../libraries/dnt/index.js'; import { _each, deepAccess, @@ -18,13 +17,14 @@ import { isInteger, deepSetValue, getBidIdParameter, setOnAny, getWinDimensions } from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER, NATIVE} from '../src/mediaTypes.js'; -import {config} from '../src/config.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, NATIVE } from '../src/mediaTypes.js'; +import { config } from '../src/config.js'; import { getStorageManager } from '../src/storageManager.js'; import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; import { getUserSyncs } from '../libraries/mgidUtils/mgidUtils.js' import { getCurrencyFromBidderRequest } from '../libraries/ortb2Utils/currency.js' +import { getDNT } from '../libraries/dnt/index.js'; /** * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest @@ -35,7 +35,7 @@ import { getCurrencyFromBidderRequest } from '../libraries/ortb2Utils/currency.j const GVLID = 358; const DEFAULT_CUR = 'USD'; const BIDDER_CODE = 'mgid'; -export const storage = getStorageManager({bidderCode: BIDDER_CODE}); +export const storage = getStorageManager({ bidderCode: BIDDER_CODE }); const ENDPOINT_URL = 'https://prebid.mgid.com/prebid/'; const LOG_WARN_PREFIX = '[MGID warn]: '; const LOG_INFO_PREFIX = '[MGID info]: '; @@ -210,7 +210,7 @@ export const spec = { id: deepAccess(bidderRequest, 'bidderRequestId'), site: ortb2Data?.site || {}, cur: [cur], - geo: {utcoffset: info.timeOffset}, + geo: { utcoffset: info.timeOffset }, device: ortb2Data?.device || {}, ext: { mgid_ver: spec.VERSION, @@ -455,7 +455,7 @@ function createBannerRequest(bid) { if (sizes.length > 1) { for (let f = 0; f < sizes.length; f++) { if (sizes[f].length === 2) { - format.push({w: sizes[f][0], h: sizes[f][1]}); + format.push({ w: sizes[f][0], h: sizes[f][1] }); } } } @@ -692,7 +692,7 @@ function getBidFloor(bid, cur) { if (reqCur === cur) { cur = '' } - return {floor: bidFloor, cur: cur} + return { floor: bidFloor, cur: cur } } function copyFromAdmAsset(asset) { diff --git a/modules/mgidRtdProvider.js b/modules/mgidRtdProvider.js index 1ba75d3c343..f3b226182ae 100644 --- a/modules/mgidRtdProvider.js +++ b/modules/mgidRtdProvider.js @@ -1,9 +1,9 @@ import { submodule } from '../src/hook.js'; -import {ajax} from '../src/ajax.js'; -import {deepAccess, logError, logInfo, mergeDeep} from '../src/utils.js'; -import {getStorageManager} from '../src/storageManager.js'; -import {getRefererInfo} from '../src/refererDetection.js'; -import {MODULE_TYPE_RTD} from '../src/activities/modules.js'; +import { ajax } from '../src/ajax.js'; +import { deepAccess, logError, logInfo, mergeDeep } from '../src/utils.js'; +import { getStorageManager } from '../src/storageManager.js'; +import { getRefererInfo } from '../src/refererDetection.js'; +import { MODULE_TYPE_RTD } from '../src/activities/modules.js'; /** * @typedef {import('../modules/rtdModule/index.js').RtdSubmodule} RtdSubmodule diff --git a/modules/michaoBidAdapter.ts b/modules/michaoBidAdapter.ts index 36ce72b7453..b16ac379aa8 100644 --- a/modules/michaoBidAdapter.ts +++ b/modules/michaoBidAdapter.ts @@ -1,5 +1,5 @@ import { ortbConverter } from '../libraries/ortbConverter/converter.js'; -import {type BidderSpec, registerBidder} from '../src/adapters/bidderFactory.js'; +import { type BidderSpec, registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; import { Renderer } from '../src/Renderer.js'; import { diff --git a/modules/microadBidAdapter.js b/modules/microadBidAdapter.js index 4e292afebd2..b7564b597a5 100644 --- a/modules/microadBidAdapter.js +++ b/modules/microadBidAdapter.js @@ -23,15 +23,15 @@ const NATIVE_CODE = 2; const VIDEO_CODE = 4; const AUDIENCE_IDS = [ - {type: 6, bidKey: 'userId.imuid', source: 'intimatemerger.com'}, - {type: 8, bidKey: 'userId.id5id.uid', source: 'id5-sync.com'}, - {type: 9, bidKey: 'userId.tdid', source: 'adserver.org'}, - {type: 10, bidKey: 'userId.novatiq.snowflake', source: 'novatiq.com'}, - {type: 12, bidKey: 'userId.dacId.id', source: 'dac.co.jp'}, - {type: 13, bidKey: 'userId.idl_env', source: 'liveramp.com'}, - {type: 14, bidKey: 'userId.criteoId', source: 'criteo.com'}, - {type: 15, bidKey: 'userId.pubcid', source: 'pubcid.org'}, - {type: 17, bidKey: 'userId.uid2.id', source: 'uidapi.com'} + { type: 6, bidKey: 'userId.imuid', source: 'intimatemerger.com' }, + { type: 8, bidKey: 'userId.id5id.uid', source: 'id5-sync.com' }, + { type: 9, bidKey: 'userId.tdid', source: 'adserver.org' }, + { type: 10, bidKey: 'userId.novatiq.snowflake', source: 'novatiq.com' }, + { type: 12, bidKey: 'userId.dacId.id', source: 'dac.co.jp' }, + { type: 13, bidKey: 'userId.idl_env', source: 'liveramp.com' }, + { type: 14, bidKey: 'userId.criteoId', source: 'criteo.com' }, + { type: 15, bidKey: 'userId.pubcid', source: 'pubcid.org' }, + { type: 17, bidKey: 'userId.uid2.id', source: 'uidapi.com' } ]; function createCBT() { diff --git a/modules/mileBidAdapter.md b/modules/mileBidAdapter.md new file mode 100644 index 00000000000..0124c5f4327 --- /dev/null +++ b/modules/mileBidAdapter.md @@ -0,0 +1,72 @@ +# Overview + +``` +Module Name: Mile Bid Adapter +Module Type: Bidder Adapter +Maintainer: tech@mile.tech +``` + +# Description + +This bidder adapter connects to Mile demand sources. + +# Bid Params + +| Name | Scope | Description | Example | Type | +|---------------|----------|-----------------------------------|------------------|--------| +| `placementId` | required | The placement ID for the ad unit | `'12345'` | string | +| `siteId` | required | The site ID for the publisher | `'site123'` | string | +| `publisherId` | required | The publisher ID | `'pub456'` | string | + +# Example Configuration + +```javascript +var adUnits = [{ + code: 'test-banner', + mediaTypes: { + banner: { + sizes: [[300, 250]] + } + }, + bids: [{ + bidder: 'mile', + params: { + placementId: 'test-placement', + siteId: 'test-site', + publisherId: 'test-pub' + } + }] +}]; +``` + +# User Sync Configuration + +To enable user syncing, configure Prebid.js with: + +```javascript +pbjs.setConfig({ + userSync: { + iframeEnabled: true, // Enable iframe syncs + filterSettings: { + iframe: { + bidders: ['mile'], + filter: 'include' + } + } + } +}); +``` + +# Test Parameters + +```javascript +{ + bidder: 'mile', + params: { + placementId: 'test-placement', + siteId: 'test-site', + publisherId: 'test-publisher-id' + } +} +``` + diff --git a/modules/mileBidAdapter.ts b/modules/mileBidAdapter.ts new file mode 100644 index 00000000000..28728615051 --- /dev/null +++ b/modules/mileBidAdapter.ts @@ -0,0 +1,428 @@ +import { type BidderSpec, registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER } from '../src/mediaTypes.js'; +import { deepAccess, deepSetValue, generateUUID, logInfo, logError } from '../src/utils.js'; +import { ajax } from '../src/ajax.js'; +import { getDNT } from '../libraries/dnt/index.js'; + +/** + * Mile Bid Adapter + * + * This adapter handles: + * 1. User syncs by sending requests to the prebid server cookie sync endpoint + * 2. Bid requests by parsing necessary parameters from the prebid auction + */ + +const BIDDER_CODE = 'mile'; + +const MILE_BIDDER_HOST = 'https://pbs.atmtd.com'; +const ENDPOINT_URL = `${MILE_BIDDER_HOST}/mile/v1/request`; +const USER_SYNC_ENDPOINT = `https://scripts.atmtd.com/user-sync/load-cookie.html`; + +const MILE_ANALYTICS_ENDPOINT = `https://e01.atmtd.com/bidanalytics-event/json`; + +type MileBidParams = { + placementId: string; + siteId: string; + publisherId: string; +}; + +declare module '../src/adUnits' { + interface BidderParams { + [BIDDER_CODE]: MileBidParams; + } +} + +export let siteIdTracker : string | undefined; +export let publisherIdTracker : string | undefined; + +export function getLowestFloorPrice(bid) { + let floorPrice: number; + + if (typeof bid.getFloor === 'function') { + let sizes = [] + // Get floor prices for each banner size in the bid request + if (deepAccess(bid, 'mediaTypes.banner.sizes')) { + sizes = deepAccess(bid, 'mediaTypes.banner.sizes') + } else if (bid.sizes) { + sizes = bid.sizes + } + + sizes.forEach((size: string | number[]) => { + const [w, h] = typeof size === 'string' ? size.split('x') : size as number[]; + const floor = bid.getFloor({ currency: 'USD', mediaType: '*', size: [Number(w), Number(h)] }); + if (floor && floor.floor) { + if (floorPrice === undefined) { + floorPrice = floor.floor; + } else { + floorPrice = Math.min(floorPrice, floor.floor); + } + } + }); + } else { + floorPrice = 0 + } + + return floorPrice +} + +export const spec: BidderSpec = { + code: BIDDER_CODE, + supportedMediaTypes: [BANNER], + isBidRequestValid: function (bid) { + const params = bid.params; + + if (!params?.placementId) { + logError(`${BIDDER_CODE}: Missing required param: placementId`); + return false; + } + + if (!params?.siteId) { + logError(`${BIDDER_CODE}: Missing required param: siteId`); + return false; + } + + if (!params?.publisherId) { + logError(`${BIDDER_CODE}: Missing required param: publisherId`); + return false; + } + + if (siteIdTracker === undefined) { + siteIdTracker = params.siteId; + } else if (siteIdTracker !== params.siteId) { + logError(`${BIDDER_CODE}: Site ID mismatch: ${siteIdTracker} !== ${params.siteId}`); + return false; + } + + if (publisherIdTracker === undefined) { + publisherIdTracker = params.publisherId; + } else if (publisherIdTracker !== params.publisherId) { + logError(`${BIDDER_CODE}: Publisher ID mismatch: ${publisherIdTracker} !== ${params.publisherId}`); + return false; + } + + return true; + }, + + /** + * Make a server request from the list of BidRequests. + * Builds an OpenRTB 2.5 compliant bid request. + * + * @param validBidRequests - An array of valid bids + * @param bidderRequest - The master bidder request object + * @returns ServerRequest info describing the request to the server + */ + buildRequests: function (validBidRequests, bidderRequest) { + logInfo(`${BIDDER_CODE}: Building batched request for ${validBidRequests.length} bids`); + + // Build imp[] array - one impression object per bid request + const imps = validBidRequests.map((bid) => { + const sizes = deepAccess(bid, 'mediaTypes.banner.sizes') || []; + const floorPrice = getLowestFloorPrice(bid); + + const imp: any = { + id: bid.bidId, + tagid: bid.params.placementId, + secure: 1, + banner: { + format: sizes.map((size: number[]) => ({ + w: size[0], + h: size[1], + })) + }, + ext: { + adUnitCode: bid.adUnitCode, + placementId: bid.params.placementId, + gpid: deepAccess(bid, 'ortb2Imp.ext.gpid') || deepAccess(bid, 'ortb2Imp.ext.data.pbadslot'), + }, + }; + + // Add bidfloor if available + if (floorPrice > 0) { + imp.bidfloor = floorPrice; + imp.bidfloorcur = 'USD'; + } + + return imp; + }); + + // Build the OpenRTB 2.5 BidRequest object + const openRtbRequest: any = { + id: bidderRequest.bidderRequestId || generateUUID(), + imp: imps, + tmax: bidderRequest.timeout, + cur: ['USD'], + site: { + id: siteIdTracker, + page: deepAccess(bidderRequest, 'refererInfo.page') || '', + domain: deepAccess(bidderRequest, 'refererInfo.domain') || '', + ref: deepAccess(bidderRequest, 'refererInfo.ref') || '', + publisher: { + id: publisherIdTracker, + }, + }, + user: {}, + // Device object + device: { + ua: navigator.userAgent, + language: navigator.language?.split('-')[0] || 'en', + dnt: getDNT() ? 1 : 0, + w: window.screen?.width, + h: window.screen?.height, + }, + + // Source object with supply chain + source: { + tid: deepAccess(bidderRequest, 'ortb2.source.tid') + }, + }; + + // Add schain if available + const schain = deepAccess(validBidRequests, '0.ortb2.source.ext.schain'); + if (schain) { + deepSetValue(openRtbRequest, 'source.ext.schain', schain); + } + + // User object + const eids = deepAccess(validBidRequests, '0.userIdAsEids'); + if (eids && eids.length) { + deepSetValue(openRtbRequest, 'user.ext.eids', eids); + } + + // Regs object for privacy/consent + const regs: any = { ext: {} }; + + // GDPR + if (bidderRequest.gdprConsent) { + regs.ext.gdpr = bidderRequest.gdprConsent.gdprApplies ? 1 : 0; + deepSetValue(openRtbRequest, 'user.ext.consent', bidderRequest.gdprConsent.consentString || ''); + } + + // US Privacy (CCPA) + if (bidderRequest.uspConsent) { + regs.ext.us_privacy = bidderRequest.uspConsent; + } + + // GPP + if (bidderRequest.gppConsent) { + regs.gpp = bidderRequest.gppConsent.gppString || ''; + regs.gpp_sid = bidderRequest.gppConsent.applicableSections || []; + } + + if (Object.keys(regs.ext).length > 0 || regs.gpp) { + openRtbRequest.regs = regs; + } + + // Merge any first-party data from ortb2 + if (bidderRequest.ortb2) { + if (bidderRequest.ortb2.site) { + openRtbRequest.site = { ...openRtbRequest.site, ...bidderRequest.ortb2.site }; + // Preserve publisher ID + openRtbRequest.site.publisher = { id: publisherIdTracker, ...bidderRequest.ortb2.site.publisher }; + } + if (bidderRequest.ortb2.user) { + openRtbRequest.user = { ...openRtbRequest.user, ...bidderRequest.ortb2.user }; + } + if (bidderRequest.ortb2.device) { + openRtbRequest.device = { ...openRtbRequest.device, ...bidderRequest.ortb2.device }; + } + } + + // Add prebid adapter version info + deepSetValue(openRtbRequest, 'ext.prebid.channel', { + name: 'pbjs', + version: '$prebid.version$', + }); + + return { + method: 'POST', + url: ENDPOINT_URL, + data: openRtbRequest + }; + }, + + /** + * Unpack the OpenRTB 2.5 response from the server into a list of bids. + * + * @param serverResponse - A successful response from the server. + * @param request - The request that was sent to the server. + * @returns An array of bids which were nested inside the server. + */ + interpretResponse: function (serverResponse, request) { + const bidResponses: any[] = []; + + if (!serverResponse?.body) { + logInfo(`${BIDDER_CODE}: Empty server response`); + return bidResponses; + } + + const response = serverResponse.body; + const currency = response.cur || 'USD'; + + // OpenRTB 2.5 response format: seatbid[] contains bid[] + const seatbids = response.bids || []; + + seatbids.forEach((bid: any) => { + if (!bid || !bid.cpm) { + return; + } + + const bidResponse = { + requestId: bid.requestId, + cpm: parseFloat(bid.cpm), + width: parseInt(bid.width || bid.w, 10), + height: parseInt(bid.height || bid.h, 10), + creativeId: bid.creativeId || bid.crid || bid.id, + currency: currency, + netRevenue: true, + bidder: BIDDER_CODE, + ttl: bid.ttl || 300, + ad: bid.ad, + mediaType: BANNER, + meta: { + advertiserDomains: bid.adomain || [], + upstreamBidder: bid.upstreamBidder || '', + siteUID: deepAccess(response, 'site.id') || '', + publisherID: deepAccess(response, 'site.publisher.id') || '', + page: deepAccess(response, 'site.page') || '', + domain: deepAccess(response, 'site.domain') || '', + } + }; + + // Handle nurl (win notice URL) if present + if (bid.nurl) { + (bidResponse as any).nurl = bid.nurl; + } + + // Handle burl (billing URL) if present + if (bid.burl) { + (bidResponse as any).burl = bid.burl; + } + + bidResponses.push(bidResponse); + }); + + return bidResponses; + }, + + /** + * Register user sync pixels or iframes to be dropped after the auction. + * + * @param syncOptions - Which sync types are allowed (iframe, image/pixel) + * @param serverResponses - Array of server responses from the auction + * @param gdprConsent - GDPR consent data + * @param uspConsent - US Privacy consent string + * @param gppConsent - GPP consent data + * @returns Array of user sync objects to be executed + */ + getUserSyncs: function ( + syncOptions, + serverResponses, + gdprConsent, + uspConsent, + gppConsent + ) { + logInfo(`${BIDDER_CODE}: getUserSyncs called`, { + iframeEnabled: syncOptions.iframeEnabled + }); + + const syncs = []; + + // Build query parameters for consent + const queryParams: string[] = []; + + // GDPR consent + if (gdprConsent) { + queryParams.push(`gdpr=${gdprConsent.gdprApplies ? 1 : 0}`); + queryParams.push(`gdpr_consent=${encodeURIComponent(gdprConsent.consentString || '')}`); + } + + // US Privacy / CCPA + if (uspConsent) { + queryParams.push(`us_privacy=${encodeURIComponent(uspConsent)}`); + } + + // GPP consent + if (gppConsent?.gppString) { + queryParams.push(`gpp=${encodeURIComponent(gppConsent.gppString)}`); + if (gppConsent.applicableSections?.length) { + queryParams.push(`gpp_sid=${encodeURIComponent(gppConsent.applicableSections.join(','))}`); + } + } + + const queryString = queryParams.length > 0 ? `?${queryParams.join('&')}` : ''; + + if (syncOptions.iframeEnabled) { + syncs.push({ + type: 'iframe' as const, + url: `${USER_SYNC_ENDPOINT}${queryString}`, + }); + } + + return syncs; + }, + + /** + * Called when a bid from this adapter wins the auction. + * Sends an XHR POST request to the bid's nurl (win notification URL). + * + * @param bid - The winning bid object + */ + onBidWon: function (bid) { + logInfo(`${BIDDER_CODE}: Bid won`, bid); + + const winNotificationData = { + adUnitCode: bid.adUnitCode, + metaData: { + impressionID: [bid.requestId], + }, + ua: navigator.userAgent, + timestamp: Date.now(), + winningSize: `${bid.width}x${bid.height}`, + cpm: bid.cpm, + eventType: 'mile-bidder-win-notify', + winningBidder: deepAccess(bid, 'meta.upstreamBidder') || '', + siteUID: deepAccess(bid, 'meta.siteUID') || '', + yetiPublisherID: deepAccess(bid, 'meta.publisherID') || '', + page: deepAccess(bid, 'meta.page') || '', + site: deepAccess(bid, 'meta.domain') || '', + } + + ajax(MILE_ANALYTICS_ENDPOINT, null, JSON.stringify([winNotificationData]), { method: 'POST' }); + + // @ts-expect-error - bid.nurl is not defined + if (bid.nurl) ajax(bid.nurl, null, null, { method: 'GET' }); + }, + + /** + * Called when bid requests timeout. + * Sends analytics notification for timed out bids. + * + * @param timeoutData - Array of bid requests that timed out + */ + onTimeout: function (timeoutData) { + logInfo(`${BIDDER_CODE}: Timeout for ${timeoutData.length} bid(s)`, timeoutData); + + if (timeoutData.length === 0) return; + + const timedOutBids = []; + + timeoutData.forEach((bid) => { + const timeoutNotificationData = { + adUnitCode: bid.adUnitCode, + metaData: { + impressionID: [bid.bidId], + configuredTimeout: [bid.timeout.toString()], + }, + ua: navigator.userAgent, + timestamp: Date.now(), + eventType: 'mile-bidder-timeout' + }; + + timedOutBids.push(timeoutNotificationData); + }); + + ajax(MILE_ANALYTICS_ENDPOINT, null, JSON.stringify(timedOutBids), { method: 'POST' }); + }, +}; + +registerBidder(spec); diff --git a/modules/minutemediaBidAdapter.js b/modules/minutemediaBidAdapter.js index d5aae8a84d0..3c009ff68ad 100644 --- a/modules/minutemediaBidAdapter.js +++ b/modules/minutemediaBidAdapter.js @@ -1,6 +1,6 @@ -import {logWarn} from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {makeBaseSpec} from '../libraries/riseUtils/index.js'; +import { logWarn } from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { makeBaseSpec } from '../libraries/riseUtils/index.js'; const BIDDER_CODE = 'minutemedia'; const BASE_URL = 'https://hb.minutemedia-prebid.com/'; diff --git a/modules/missenaBidAdapter.js b/modules/missenaBidAdapter.js index 573f20fb302..dba7a723693 100644 --- a/modules/missenaBidAdapter.js +++ b/modules/missenaBidAdapter.js @@ -157,6 +157,7 @@ export const spec = { serverResponses, gdprConsent = {}, uspConsent, + gppConsent, ) { if (!syncOptions.iframeEnabled || !this.msnaApiKey) { return []; @@ -172,6 +173,13 @@ export const spec = { if (uspConsent) { url.searchParams.append('us_privacy', uspConsent); } + if (gppConsent?.gppString) { + url.searchParams.append('gpp', gppConsent.gppString); + url.searchParams.append( + 'gpp_sid', + (gppConsent.applicableSections || []).join(','), + ); + } return [{ type: 'iframe', url: url.href }]; }, diff --git a/modules/mobianRtdProvider.js b/modules/mobianRtdProvider.js index d0eb5880bac..71145a25c0e 100644 --- a/modules/mobianRtdProvider.js +++ b/modules/mobianRtdProvider.js @@ -59,25 +59,48 @@ export const CONTEXT_KEYS = [ const AP_KEYS = ['a0', 'a1', 'p0', 'p1']; +export const MAX_CACHE_SIZE = 10; + // eslint-disable-next-line no-restricted-syntax const logMessage = (...args) => { _logMessage('Mobian', ...args); }; -function makeMemoizedFetch() { - let cachedResponse = null; - return async function () { - if (cachedResponse) { - return Promise.resolve(cachedResponse); +function getNormalizedPageUrl() { + try { + const { origin, pathname } = window.location; + return origin + pathname; + } catch (e) { + // Fallback to href if origin/pathname are not available, but keep normalization consistent + const href = window.location && window.location.href; + if (typeof href === 'string') { + // Strip query string and hash to match origin + pathname behavior + return href.split(/[?#]/)[0]; + } + return ''; + } +} + +export function makeMemoizedFetch(maxSize = MAX_CACHE_SIZE) { + const sanitizedMaxSize = (Number.isFinite(maxSize) && maxSize >= 1) ? Math.floor(maxSize) : MAX_CACHE_SIZE; + const cache = new Map(); + return function () { + const pageUrl = getNormalizedPageUrl(); + if (cache.has(pageUrl)) { + return cache.get(pageUrl); } - try { - const response = await fetchContextData(); - cachedResponse = makeDataFromResponse(response); - return cachedResponse; - } catch (error) { - logMessage('error', error); - return Promise.resolve({}); + if (cache.size >= sanitizedMaxSize) { + cache.delete(cache.keys().next().value); } + const pending = fetchContextData() + .then((response) => makeDataFromResponse(response)) + .catch((error) => { + logMessage('error', error); + cache.delete(pageUrl); + return {}; + }); + cache.set(pageUrl, pending); + return pending; } } diff --git a/modules/mobianRtdProvider.md b/modules/mobianRtdProvider.md index ccfc43e3aab..dee9cc12773 100644 --- a/modules/mobianRtdProvider.md +++ b/modules/mobianRtdProvider.md @@ -160,6 +160,36 @@ p1 = Advertisers (via Campaign IDs) should target these personas *AP Values is in the early stages of testing and is subject to change. +------------------ + +Additional Results Fields (API response) + +The fields below are present in the Mobian Contextual API `results` schema and are useful for downstream interpretation of content maturity and taxonomy. + +mobianMpaaRating: + +Type: integer | null + +Description: MPAA-style maturity rating score represented as an integer value in the API response. + +Behavior when unavailable: omitted when null. + +mobianEsrbRating: + +Type: integer | null + +Description: ESRB-style maturity rating score represented as an integer value in the API response. + +Behavior when unavailable: omitted when null. + +mobianContentTaxonomy: + +Type: string[] + +Description: IAB content taxonomy categories (broad topic buckets such as "News" or "Health"). + +Behavior when unavailable: may be returned as an empty array. + ## GAM Targeting: On each page load, the Mobian RTD module finds each ad slot on the page and performs the following function: diff --git a/modules/mobilefuseBidAdapter.js b/modules/mobilefuseBidAdapter.js index d92d03672a9..c84db034bd3 100644 --- a/modules/mobilefuseBidAdapter.js +++ b/modules/mobilefuseBidAdapter.js @@ -1,8 +1,8 @@ -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER, VIDEO} from '../src/mediaTypes.js'; -import {deepAccess, deepSetValue} from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; +import { deepAccess, deepSetValue } from '../src/utils.js'; import { config } from '../src/config.js'; -import {ortbConverter} from '../libraries/ortbConverter/converter.js'; +import { ortbConverter } from '../libraries/ortbConverter/converter.js'; import { userSync } from '../src/userSync.js'; const ADAPTER_VERSION = '1.0.0'; @@ -76,7 +76,7 @@ function buildRequests(validBidRequests, bidderRequest) { return { method: 'POST', url: ENDPOINT_URL, - data: converter.toORTB({validBidRequests, bidderRequest}), + data: converter.toORTB({ validBidRequests, bidderRequest }), }; } @@ -106,7 +106,7 @@ function getUserSyncs(syncOptions, serverResponses, gdprConsent, uspConsent, gpp const querystring = params.length ? `?${params.join('&')}` : ''; - return [{type: 'iframe', url: `${SYNC_URL}${querystring}`}]; + return [{ type: 'iframe', url: `${SYNC_URL}${querystring}` }]; } const pixels = []; @@ -114,7 +114,7 @@ function getUserSyncs(syncOptions, serverResponses, gdprConsent, uspConsent, gpp serverResponses.forEach(response => { if (response.body.ext && response.body.ext.syncs) { response.body.ext.syncs.forEach(url => { - pixels.push({type: 'image', url: url}); + pixels.push({ type: 'image', url: url }); }); } }); diff --git a/modules/mobkoiAnalyticsAdapter.js b/modules/mobkoiAnalyticsAdapter.js index a8d373e3a3b..4d35149bf01 100644 --- a/modules/mobkoiAnalyticsAdapter.js +++ b/modules/mobkoiAnalyticsAdapter.js @@ -76,6 +76,7 @@ export class LocalContext { _impressionPayloadCache = { // [impid]: { ... } }; + /** * The payload that is common to all bid contexts. The payload will be * submitted to the server along with the debug events. @@ -87,6 +88,7 @@ export class LocalContext { return this._impressionPayloadCache[impid] || {}; } + /** * Update the payload for all impressions. The new values will be merged to * the existing payload. @@ -253,7 +255,7 @@ export class LocalContext { * @param {*} params.subPayloads Objects containing additional data that are * obtain from to the Prebid events indexed by SUB_PAYLOAD_TYPES. */ - pushEventToAllBidContexts({eventType, level, timestamp, note, subPayloads}) { + pushEventToAllBidContexts({ eventType, level, timestamp, note, subPayloads }) { // Create one event for each impression ID _each(this.getAllBidderRequestImpIds(), impid => { const eventClone = new Event({ @@ -466,7 +468,7 @@ function pickKeyFields(objType, eventArgs) { } } -const mobkoiAnalytics = Object.assign(adapter({analyticsType}), { +const mobkoiAnalytics = Object.assign(adapter({ analyticsType }), { localContext: new LocalContext(), async track({ eventType, @@ -597,7 +599,7 @@ const mobkoiAnalytics = Object.assign(adapter({analyticsType}), { case AD_RENDER_FAILED: { utils.logTrackEvent(eventType, prebidEventArgs); const argsType = utils.determineObjType(prebidEventArgs); - const {bid: prebidBid} = prebidEventArgs; + const { bid: prebidBid } = prebidEventArgs; const bidContext = this.localContext.retrieveBidContext(prebidBid); bidContext.pushEvent({ eventInstance: new Event({ @@ -775,6 +777,7 @@ class BidContext { get subPayloads() { return this._subPayloads; } + /** * To avoid overriding the subPayloads object, we merge the new values to the * existing subPayloads object. Identity fields will automatically added to the @@ -868,7 +871,7 @@ class BidContext { * @param {Event} params.eventInstance - DebugEvent object. If it does not contain the same impid as the BidContext, the event will be ignored. * @param {Object|null} params.subPayloads - Object containing various payloads obtained from the Prebid Event args. The payloads will be merged into the existing subPayloads. */ - pushEvent({eventInstance, subPayloads}) { + pushEvent({ eventInstance, subPayloads }) { if (!(eventInstance instanceof Event)) { throw new Error('bugEvent must be an instance of DebugEvent'); } @@ -919,7 +922,7 @@ class Event { */ timestamp = null; - constructor({eventType, impid, publisherId, level, timestamp, note = undefined}) { + constructor({ eventType, impid, publisherId, level, timestamp, note = undefined }) { if (!eventType) { throw new Error('eventType is required'); } diff --git a/modules/mobkoiBidAdapter.js b/modules/mobkoiBidAdapter.js index ff5be0293ac..3ec4861ee4b 100644 --- a/modules/mobkoiBidAdapter.js +++ b/modules/mobkoiBidAdapter.js @@ -91,7 +91,7 @@ export const spec = { interpretResponse(serverResponse, customBidRequest) { if (!serverResponse.body) return []; - const responseBody = {...serverResponse.body, seatbid: serverResponse.body.seatbid}; + const responseBody = { ...serverResponse.body, seatbid: serverResponse.body.seatbid }; const prebidBidResponse = converter.fromORTB({ request: customBidRequest.data, response: responseBody, @@ -182,7 +182,7 @@ export const utils = { 'Failed to obtain placement ID from the given object. ' + `Please set it via the "${paramPath}" field in the bid configuration.\n` + 'Given object:\n' + - JSON.stringify({functionParam: prebidBidRequestOrOrtbBidRequest}, null, 3) + JSON.stringify({ functionParam: prebidBidRequestOrOrtbBidRequest }, null, 3) ); } diff --git a/modules/msftBidAdapter.js b/modules/msftBidAdapter.js index 6155b91e778..e9539cce6fc 100644 --- a/modules/msftBidAdapter.js +++ b/modules/msftBidAdapter.js @@ -31,6 +31,7 @@ import { logWarn, mergeDeep } from "../src/utils.js"; +import { getAdUnitElement } from '../src/utils/adUnits.js'; const BIDDER_CODE = "msft"; const DEBUG_PARAMS = ['enabled', 'dongle', 'member_id', 'debug_timeout']; @@ -376,7 +377,7 @@ export const spec = { return [{ type: "iframe", url: "https://acdn.adnxs.com/dmp/async_usersync.html", - }, ]; + },]; } if (syncOptions.pixelEnabled) { @@ -502,12 +503,10 @@ function newRenderer(adUnitCode, rtbBid, rendererOptions = {}) { /** * This function hides google div container for outstream bids to remove unwanted space on page. Appnexus renderer creates a new iframe outside of google iframe to render the outstream creative. - * @param {string} elementId element id */ -function hidedfpContainer(elementId) { +function hidedfpContainer(container) { try { - const el = document - .getElementById(elementId) + const el = container .querySelectorAll("div[id^='google_ads']"); if (el[0]) { el[0].style.setProperty("display", "none"); @@ -517,11 +516,10 @@ function hidedfpContainer(elementId) { } } -function hideSASIframe(elementId) { +function hideSASIframe(container) { try { // find script tag with id 'sas_script'. This ensures it only works if you're using Smart Ad Server. - const el = document - .getElementById(elementId) + const el = container .querySelectorAll("script[id^='sas_script']"); if (el[0]?.nextSibling?.localName === "iframe") { el[0].nextSibling.style.setProperty("display", "none"); @@ -539,8 +537,9 @@ function handleOutstreamRendererEvents(bid, id, eventName) { } function outstreamRender(bid, doc) { - hidedfpContainer(bid.adUnitCode); - hideSASIframe(bid.adUnitCode); + const container = getAdUnitElement(bid); + hidedfpContainer(container); + hideSASIframe(container); // push to render queue because ANOutstreamVideo may not be loaded yet bid.renderer.push(() => { const win = doc?.defaultView || window; diff --git a/modules/msftBidAdapter.md b/modules/msftBidAdapter.md index df5be0a1fb6..3d5c9f4ff5b 100644 --- a/modules/msftBidAdapter.md +++ b/modules/msftBidAdapter.md @@ -10,7 +10,7 @@ Maintainer: prebid@microsoft.com The Microsoft Bid Adapter connects to Microsoft's advertising exchange for bids. This adapter supports banner, video (instream and outstream), and native ad formats using OpenRTB 2.5 standards. -The Microsoft adapter requires setup and approval from the Microsoft Advertising team. Please reach out to your account team for more information. +If you are transitioning from the AppNexus bid adapter and you have any questions or concerns, please reach out to your account team for assistance. # Migration from AppNexus Bid Adapter diff --git a/modules/multibid/index.ts b/modules/multibid/index.ts index d7f6b0cbb38..7b1f137ad3a 100644 --- a/modules/multibid/index.ts +++ b/modules/multibid/index.ts @@ -3,18 +3,18 @@ * @module modules/multibid */ -import {config} from '../../src/config.js'; -import {setupBeforeHookFnOnce, getHook} from '../../src/hook.js'; +import { config } from '../../src/config.js'; +import { setupBeforeHookFnOnce, getHook } from '../../src/hook.js'; import { logWarn, deepAccess, getUniqueIdentifierStr, deepSetValue, groupBy } from '../../src/utils.js'; import * as events from '../../src/events.js'; import { EVENTS } from '../../src/constants.js'; -import {addBidderRequests} from '../../src/auction.js'; -import {getHighestCpmBidsFromBidPool, sortByDealAndPriceBucketOrCpm} from '../../src/targeting.js'; -import {PBS, registerOrtbProcessor, REQUEST} from '../../src/pbjsORTB.js'; -import {timedBidResponseHook} from '../../src/utils/perfMetrics.js'; -import type {BidderCode} from "../../src/types/common.d.ts"; +import { addBidderRequests } from '../../src/auction.js'; +import { getHighestCpmBidsFromBidPool, sortByDealAndPriceBucketOrCpm } from '../../src/targeting.js'; +import { PBS, registerOrtbProcessor, REQUEST } from '../../src/pbjsORTB.js'; +import { timedBidResponseHook } from '../../src/utils/perfMetrics.js'; +import type { BidderCode } from "../../src/types/common.d.ts"; const MODULE_NAME = 'multibid'; let hasMultibid = false; @@ -102,7 +102,7 @@ export function validateMultibid(conf) { return entry; }); - if (!check) config.setConfig({multibid: duplicate}); + if (!check) config.setConfig({ multibid: duplicate }); return check; } @@ -175,9 +175,9 @@ export const addBidResponseHook = timedBidResponseHook('multibid', function addB logWarn(`Filtering multibid received from bidder ${bid.bidderCode}: ` + ((multibidUnits[adUnitCode][bid.bidderCode].maxReached) ? `Maximum bid limit reached for ad unit code ${adUnitCode}` : 'Bid cpm under floors value.')); } } else { - if (deepAccess(bid, 'floorData.floorValue')) deepSetValue(multibidUnits, [adUnitCode, bid.bidderCode], {floor: deepAccess(bid, 'floorData.floorValue')}); + if (deepAccess(bid, 'floorData.floorValue')) deepSetValue(multibidUnits, [adUnitCode, bid.bidderCode], { floor: deepAccess(bid, 'floorData.floorValue') }); - deepSetValue(multibidUnits, [adUnitCode, bid.bidderCode], {ads: [bid]}); + deepSetValue(multibidUnits, [adUnitCode, bid.bidderCode], { ads: [bid] }); if (multibidUnits[adUnitCode][bid.bidderCode].ads.length === multiConfig[bid.bidderCode].maxbids) multibidUnits[adUnitCode][bid.bidderCode].maxReached = true; fn.call(this, adUnitCode, bid, reject); @@ -291,4 +291,4 @@ export function setOrtbExtPrebidMultibid(ortbRequest) { } } -registerOrtbProcessor({type: REQUEST, name: 'extPrebidMultibid', fn: setOrtbExtPrebidMultibid, dialects: [PBS]}); +registerOrtbProcessor({ type: REQUEST, name: 'extPrebidMultibid', fn: setOrtbExtPrebidMultibid, dialects: [PBS] }); diff --git a/modules/mwOpenLinkIdSystem.js b/modules/mwOpenLinkIdSystem.js index f638f955fd0..af0a1701e23 100644 --- a/modules/mwOpenLinkIdSystem.js +++ b/modules/mwOpenLinkIdSystem.js @@ -8,8 +8,8 @@ import { timestamp, logError, deepClone, generateUUID, isPlainObject } from '../src/utils.js'; import { ajax } from '../src/ajax.js'; import { submodule } from '../src/hook.js'; -import {getStorageManager} from '../src/storageManager.js'; -import {MODULE_TYPE_UID} from '../src/activities/modules.js'; +import { getStorageManager } from '../src/storageManager.js'; +import { MODULE_TYPE_UID } from '../src/activities/modules.js'; /** * @typedef {import('../modules/userId/index.js').Submodule} Submodule @@ -22,7 +22,7 @@ const openLinkID = { cookie_expiration: (86400 * 1000 * 365 * 1) // 1 year } -const storage = getStorageManager({moduleType: MODULE_TYPE_UID, moduleName: openLinkID.name}); +const storage = getStorageManager({ moduleType: MODULE_TYPE_UID, moduleName: openLinkID.name }); function getExpirationDate() { return (new Date(timestamp() + openLinkID.cookie_expiration)).toGMTString(); @@ -103,7 +103,7 @@ function register(configParams, olid) { function setID(configParams) { if (!isValidConfig(configParams)) return undefined; const mwOlId = readCookie(); - const newMwOlId = mwOlId ? deepClone(mwOlId) : {eid: generateUUID()}; + const newMwOlId = mwOlId ? deepClone(mwOlId) : { eid: generateUUID() }; writeCookie(newMwOlId); register(configParams, newMwOlId.eid); return { diff --git a/modules/my6senseBidAdapter.js b/modules/my6senseBidAdapter.js index 9823783a557..93c3bb75e2a 100644 --- a/modules/my6senseBidAdapter.js +++ b/modules/my6senseBidAdapter.js @@ -1,5 +1,5 @@ import { BANNER, NATIVE } from '../src/mediaTypes.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; const BIDDER_CODE = 'my6sense'; diff --git a/modules/nativeRendering.js b/modules/nativeRendering.js index b3a382a0483..e90bf4e42a9 100644 --- a/modules/nativeRendering.js +++ b/modules/nativeRendering.js @@ -1,9 +1,9 @@ -import {getRenderingData} from '../src/adRendering.js'; -import {getNativeRenderingData, isNativeResponse} from '../src/native.js'; -import {auctionManager} from '../src/auctionManager.js'; +import { getRenderingData } from '../src/adRendering.js'; +import { getNativeRenderingData, isNativeResponse } from '../src/native.js'; +import { auctionManager } from '../src/auctionManager.js'; // eslint-disable-next-line prebid/validate-imports -import {RENDERER} from '../creative-renderers/native.js'; // autogenerated during precompilation -import {getCreativeRendererSource} from '../src/creativeRenderers.js'; +import { RENDERER } from '../creative-renderers/native.js'; // autogenerated during precompilation +import { getCreativeRendererSource } from '../src/creativeRenderers.js'; function getRenderingDataHook(next, bidResponse, options) { if (isNativeResponse(bidResponse)) { diff --git a/modules/nativoBidAdapter.js b/modules/nativoBidAdapter.js index 3a981d65812..1fd6a8fca1d 100644 --- a/modules/nativoBidAdapter.js +++ b/modules/nativoBidAdapter.js @@ -542,6 +542,7 @@ export class BidRequestDataSource { constructor() { this.type = 'BidRequestDataSource' } + processBidRequestData(bidRequest, bidderRequest) {} getRequestQueryString() { return '' diff --git a/modules/naveggIdSystem.js b/modules/naveggIdSystem.js index 6f1964b11df..feb853cbabf 100644 --- a/modules/naveggIdSystem.js +++ b/modules/naveggIdSystem.js @@ -20,7 +20,7 @@ const OLD_NAVEGG_ID = 'nid'; const NAVEGG_ID = 'nvggid'; const BASE_URL = 'https://id.navegg.com/uid/'; -export const storage = getStorageManager({moduleType: MODULE_TYPE_UID, moduleName: MODULE_NAME}); +export const storage = getStorageManager({ moduleType: MODULE_TYPE_UID, moduleName: MODULE_NAME }); function getIdFromAPI() { const resp = function (callback) { @@ -50,7 +50,7 @@ function getIdFromAPI() { const fallbackValue = getNaveggIdFromLocalStorage() || getOldCookie(); callback(fallbackValue); }, - {method: 'GET', withCredentials: false}); + { method: 'GET', withCredentials: false }); }; return resp; } @@ -115,7 +115,7 @@ export const naveggIdSubmodule = { */ getId(config, consentData) { const resp = getIdFromAPI() - return {callback: resp} + return { callback: resp } }, eids: { 'naveggId': { diff --git a/modules/netIdSystem.js b/modules/netIdSystem.js index 4482013b46c..bb5a0421ae9 100644 --- a/modules/netIdSystem.js +++ b/modules/netIdSystem.js @@ -5,7 +5,7 @@ * @requires module:modules/userId */ -import {submodule} from '../src/hook.js'; +import { submodule } from '../src/hook.js'; /** * @typedef {import('../modules/userId/index.js').Submodule} Submodule diff --git a/modules/neuwoRtdProvider.js b/modules/neuwoRtdProvider.js index 3255547aa47..8371c20fb88 100644 --- a/modules/neuwoRtdProvider.js +++ b/modules/neuwoRtdProvider.js @@ -1,5 +1,6 @@ /** * @module neuwoRtdProvider + * @version 2.2.6 * @author Grzegorz Malisz * @see {project-root-directory}/integrationExamples/gpt/neuwoRtdProvider_example.html for an example/testing page. * @see {project-root-directory}/test/spec/modules/neuwoRtdProvider_spec.js for unit tests. @@ -7,8 +8,10 @@ * This module is a Prebid.js Real-Time Data (RTD) provider that integrates with the Neuwo API. * * It fetches contextual marketing categories (IAB content and audience) for the current page from the Neuwo API. - * The retrieved data is then injected into the bid request as OpenRTB (ORTB2)`site.content.data` + * The retrieved data is then injected into the bid request as OpenRTB (ORTB2) `site.content.data` * and `user.data` fragments, making it available for bidders to use in their decisioning process. + * Additionally, when enabled, the module populates OpenRTB 2.5 category fields (`ortb2.site.cat`, + * `ortb2.site.sectioncat`, `ortb2.site.pagecat`, `ortb2.site.content.cat`) with IAB Content Taxonomy 1.0 segments. * * @see {@link https://docs.prebid.org/dev-docs/add-rtd-submodule.html} for more information on development of Prebid.js RTD modules. * @see {@link https://docs.prebid.org/features/firstPartyData.html} for more information on Prebid.js First Party Data. @@ -18,24 +21,40 @@ import { ajax } from "../src/ajax.js"; import { submodule } from "../src/hook.js"; import { getRefererInfo } from "../src/refererDetection.js"; -import { deepSetValue, logError, logInfo, mergeDeep } from "../src/utils.js"; +import { + deepSetValue, + logError, + logInfo, + logWarn, + mergeDeep, +} from "../src/utils.js"; const MODULE_NAME = "NeuwoRTDModule"; +const MODULE_VERSION = "2.2.6"; export const DATA_PROVIDER = "www.neuwo.ai"; -// Cached API response to avoid redundant requests. -let globalCachedResponse; +// Default IAB Content Taxonomy version +const DEFAULT_IAB_CONTENT_TAXONOMY_VERSION = "2.2"; + +// Maximum number of cached API responses to keep. Oldest entries are evicted when exceeded. +const MAX_CACHE_ENTRIES = 10; +// Cached API responses keyed by full API URL to avoid redundant requests. +let cachedResponses = {}; +// In-flight request promises keyed by full API URL to prevent duplicate API calls during the same request cycle. +let pendingRequests = {}; /** - * Clears the cached API response. Primarily used for testing. + * Clears the cached API responses and pending requests. Primarily used for testing. * @private */ export function clearCache() { - globalCachedResponse = undefined; + cachedResponses = {}; + pendingRequests = {}; } // Maps the IAB Content Taxonomy version string to the corresponding segtax ID. // Based on https://github.com/InteractiveAdvertisingBureau/AdCOM/blob/main/AdCOM%20v1.0%20FINAL.md#list--category-taxonomies- +// prettier-ignore const IAB_CONTENT_TAXONOMY_MAP = { "1.0": 1, "2.0": 2, @@ -53,14 +72,14 @@ const IAB_CONTENT_TAXONOMY_MAP = { * @returns {boolean} `true` if the module is configured correctly, otherwise `false`. */ function init(config, userConsent) { - logInfo(MODULE_NAME, "init:", config, userConsent); + logInfo(MODULE_NAME, "init():", "Version " + MODULE_VERSION, config, userConsent); const params = config?.params || {}; if (!params.neuwoApiUrl) { - logError(MODULE_NAME, "init:", "Missing Neuwo Edge API Endpoint URL"); + logError(MODULE_NAME, "init():", "Missing Neuwo Edge API Endpoint URL"); return false; } if (!params.neuwoApiToken) { - logError(MODULE_NAME, "init:", "Missing Neuwo API Token missing"); + logError(MODULE_NAME, "init():", "Missing Neuwo API Token"); return false; } return true; @@ -69,6 +88,9 @@ function init(config, userConsent) { /** * Fetches contextual data from the Neuwo API and enriches the bid request object with IAB categories. * Uses cached response if available to avoid redundant API calls. + * Automatically detects API capabilities from the endpoint URL format: + * - URLs containing "/v1/iab" use GET requests with server-side filtering + * - Other URLs use GET requests with client-side filtering (legacy support) * * @param {Object} reqBidsConfigObj The bid request configuration object. * @param {function} callback The callback function to continue the auction. @@ -77,70 +99,272 @@ function init(config, userConsent) { * @param {string} config.params.neuwoApiUrl The Neuwo API endpoint URL. * @param {string} config.params.neuwoApiToken The Neuwo API authentication token. * @param {string} [config.params.websiteToAnalyseUrl] Optional URL to analyze instead of current page. - * @param {string} [config.params.iabContentTaxonomyVersion] IAB content taxonomy version (default: "3.0"). - * @param {boolean} [config.params.enableCache=true] If true, caches API responses to avoid redundant requests (default: true). + * @param {string} [config.params.iabContentTaxonomyVersion="2.2"] IAB Content Taxonomy version. + * @param {boolean} [config.params.enableCache=true] If true, caches API responses to avoid redundant requests. + * @param {boolean} [config.params.enableOrtb25Fields=true] If true, populates OpenRTB 2.5 category fields (site.cat, site.sectioncat, site.pagecat, site.content.cat) with IAB Content Taxonomy 1.0 segments. * @param {boolean} [config.params.stripAllQueryParams] If true, strips all query parameters from the URL. * @param {string[]} [config.params.stripQueryParamsForDomains] List of domains for which to strip all query params. * @param {string[]} [config.params.stripQueryParams] List of specific query parameter names to strip. * @param {boolean} [config.params.stripFragments] If true, strips URL fragments (hash). + * @param {Object} [config.params.iabTaxonomyFilters] Per-tier filtering configuration for IAB Taxonomies. * @param {Object} userConsent The user consent object. */ -export function getBidRequestData(reqBidsConfigObj, callback, config, userConsent) { - logInfo(MODULE_NAME, "getBidRequestData:", "starting getBidRequestData", config); +export function getBidRequestData( + reqBidsConfigObj, + callback, + config, + userConsent +) { + logInfo( + MODULE_NAME, + "getBidRequestData():", + "starting getBidRequestData", + config + ); const { websiteToAnalyseUrl, neuwoApiUrl, neuwoApiToken, - iabContentTaxonomyVersion, + iabContentTaxonomyVersion = DEFAULT_IAB_CONTENT_TAXONOMY_VERSION, enableCache = true, + enableOrtb25Fields = true, stripAllQueryParams, stripQueryParamsForDomains, stripQueryParams, stripFragments, + iabTaxonomyFilters, } = config.params; const rawUrl = websiteToAnalyseUrl || getRefererInfo().page; + if (!rawUrl) { + logError(MODULE_NAME, "getBidRequestData():", "No URL available to analyse"); + callback(); + return; + } const processedUrl = cleanUrl(rawUrl, { stripAllQueryParams, stripQueryParamsForDomains, stripQueryParams, - stripFragments + stripFragments, }); const pageUrl = encodeURIComponent(processedUrl); - // Adjusted for pages api.url?prefix=test (to add params with '&') as well as api.url (to add params with '?') - const joiner = neuwoApiUrl.indexOf("?") < 0 ? "?" : "&"; - const neuwoApiUrlFull = - neuwoApiUrl + joiner + ["token=" + neuwoApiToken, "url=" + pageUrl].join("&"); + const contentSegtax = + IAB_CONTENT_TAXONOMY_MAP[iabContentTaxonomyVersion] || + IAB_CONTENT_TAXONOMY_MAP[DEFAULT_IAB_CONTENT_TAXONOMY_VERSION]; - const success = (response) => { - logInfo(MODULE_NAME, "getBidRequestData:", "Neuwo API raw response:", response); - try { - const responseParsed = JSON.parse(response); + // Detect whether the endpoint supports multi-taxonomy responses and server-side filtering. + // Use URL pathname to avoid false positives when "/v1/iab" appears in query params. + let isIabEndpoint = false; + try { + isIabEndpoint = new URL(neuwoApiUrl).pathname.includes("/v1/iab"); + } catch (e) { + isIabEndpoint = neuwoApiUrl.split("?")[0].includes("/v1/iab"); + } - if (enableCache) { - globalCachedResponse = responseParsed; - } + // Warn if OpenRTB 2.5 feature enabled with legacy endpoint + if (enableOrtb25Fields && !isIabEndpoint) { + logWarn( + MODULE_NAME, + "getBidRequestData():", + "OpenRTB 2.5 category fields require the /v1/iab endpoint" + ); + } - injectIabCategories(responseParsed, reqBidsConfigObj, iabContentTaxonomyVersion); - } catch (ex) { - logError(MODULE_NAME, "getBidRequestData:", "Error while processing Neuwo API response", ex); + const joiner = neuwoApiUrl.indexOf("?") < 0 ? "?" : "&"; + const urlParams = [ + "token=" + neuwoApiToken, + "url=" + pageUrl, + "_neuwo_prod=PrebidModule", + ]; + + // Request both IAB Content Taxonomy (based on config) and IAB Audience Taxonomy (segtax 4) + if (isIabEndpoint) { + urlParams.push("iabVersions=" + contentSegtax); + urlParams.push("iabVersions=4"); // IAB Audience 1.1 + + // Request IAB 1.0 for OpenRTB 2.5 fields if feature enabled. + // Skip when contentSegtax is already 1 -- already requested above. + if (enableOrtb25Fields && contentSegtax !== 1) { + urlParams.push("iabVersions=1"); // IAB Content 1.0 } - callback(); - }; - const error = (err) => { - logError(MODULE_NAME, "getBidRequestData:", "AJAX error:", err); - callback(); - }; + // Add flattened filter parameters to URL for GET request + const filterParams = buildFilterQueryParams( + iabTaxonomyFilters, + contentSegtax, + enableOrtb25Fields + ); + if (filterParams.length > 0) { + urlParams.push(...filterParams); + } + } - if (enableCache && globalCachedResponse) { - logInfo(MODULE_NAME, "getBidRequestData:", "Using cached response:", globalCachedResponse); - injectIabCategories(globalCachedResponse, reqBidsConfigObj, iabContentTaxonomyVersion); + const neuwoApiUrlFull = neuwoApiUrl + joiner + urlParams.join("&"); + + // For /v1/iab endpoints the full URL already encodes all config (iabVersions, filters). + // For legacy endpoints the URL only carries token + page URL, so append config-dependent + // values to the cache key to prevent different configs sharing a response that was + // transformed/filtered for a different taxonomy version or filter set. + let cacheKey = neuwoApiUrlFull; + if (!isIabEndpoint) { + cacheKey += "&_segtax=" + contentSegtax; + if (iabTaxonomyFilters && Object.keys(iabTaxonomyFilters).length > 0) { + cacheKey += "&_filters=" + JSON.stringify(iabTaxonomyFilters); + } + } + + // Cache flow: cached response -> pending request -> new request + // Each caller gets their own callback invoked when data is ready. + // Keyed by cacheKey to ensure different parameters never share cached data. + if (enableCache && cachedResponses[cacheKey]) { + // Previous request succeeded - use cached response immediately + logInfo( + MODULE_NAME, + "getBidRequestData():", + "Cache System:", + "Using cached response for:", + cacheKey + ); + injectIabCategories( + cachedResponses[cacheKey], + reqBidsConfigObj, + iabContentTaxonomyVersion, + enableOrtb25Fields + ); callback(); + } else if (enableCache && pendingRequests[cacheKey]) { + // Another caller started a request with the same params - wait for it + logInfo( + MODULE_NAME, + "getBidRequestData():", + "Cache System:", + "Waiting for pending request for:", + cacheKey + ); + pendingRequests[cacheKey] + .then((responseParsed) => { + if (responseParsed) { + injectIabCategories( + responseParsed, + reqBidsConfigObj, + iabContentTaxonomyVersion, + enableOrtb25Fields + ); + } + }) + .finally(() => callback()); } else { - logInfo(MODULE_NAME, "getBidRequestData:", "Calling Neuwo API Endpoint: ", neuwoApiUrlFull); - ajax(neuwoApiUrlFull, { success, error }, null); + // First request or cache disabled - make the API call + logInfo( + MODULE_NAME, + "getBidRequestData():", + "Cache System:", + "Calling Neuwo API Endpoint:", + neuwoApiUrlFull + ); + + const requestPromise = new Promise((resolve) => { + ajax( + neuwoApiUrlFull, + { + success: (response) => { + logInfo( + MODULE_NAME, + "getBidRequestData():", + "success():", + "Neuwo API raw response:", + response + ); + + let responseParsed; + try { + responseParsed = JSON.parse(response); + } catch (ex) { + logError( + MODULE_NAME, + "getBidRequestData():", + "success():", + "Error parsing Neuwo API response JSON:", + ex + ); + resolve(null); + return; + } + + try { + if (!isIabEndpoint) { + // Apply per-tier filtering to V1 format + const filteredMarketingCategories = filterIabTaxonomies( + responseParsed.marketing_categories, + iabTaxonomyFilters + ); + + // Transform filtered V1 response to unified internal format + responseParsed = transformV1ResponseToV2( + { marketing_categories: filteredMarketingCategories }, + contentSegtax + ); + } + + // Cache response, evicting oldest entry if at capacity. + // Only cache valid responses so failed requests can be retried. + if ( + enableCache && + responseParsed && + typeof responseParsed === "object" + ) { + // Object.keys() preserves string insertion order in modern JS engines. + const keys = Object.keys(cachedResponses); + if (keys.length >= MAX_CACHE_ENTRIES) { + delete cachedResponses[keys[0]]; + } + cachedResponses[cacheKey] = responseParsed; + } + + injectIabCategories( + responseParsed, + reqBidsConfigObj, + iabContentTaxonomyVersion, + enableOrtb25Fields + ); + resolve(responseParsed); + } catch (ex) { + logError( + MODULE_NAME, + "getBidRequestData():", + "success():", + "Error processing Neuwo API response:", + ex + ); + resolve(null); + } + }, + error: (err) => { + logError( + MODULE_NAME, + "getBidRequestData():", + "error():", + "AJAX error:", + err + ); + resolve(null); + }, + } + ); + }); + + if (enableCache) { + // Store promise so concurrent callers with same params can wait on it + pendingRequests[cacheKey] = requestPromise; + // Clear after settling so failed requests can be retried + requestPromise.finally(() => { + delete pendingRequests[cacheKey]; + }); + } + + // Signal this caller's auction to proceed once request completes + requestPromise.finally(() => callback()); } } @@ -160,14 +384,23 @@ export function getBidRequestData(reqBidsConfigObj, callback, config, userConsen * @returns {string} The cleaned URL. */ export function cleanUrl(url, options = {}) { - const { stripAllQueryParams, stripQueryParamsForDomains, stripQueryParams, stripFragments } = options; + const { + stripAllQueryParams, + stripQueryParamsForDomains, + stripQueryParams, + stripFragments, + } = options; if (!url) { - logInfo(MODULE_NAME, "cleanUrl:", "Empty or null URL provided, returning as-is"); + logInfo( + MODULE_NAME, + "cleanUrl():", + "Empty or null URL provided, returning as-is" + ); return url; } - logInfo(MODULE_NAME, "cleanUrl:", "Input URL:", url, "Options:", options); + logInfo(MODULE_NAME, "cleanUrl():", "Input URL:", url, "Options:", options); try { const urlObj = new URL(url); @@ -175,21 +408,24 @@ export function cleanUrl(url, options = {}) { // Strip fragments if requested if (stripFragments === true) { urlObj.hash = ""; - logInfo(MODULE_NAME, "cleanUrl:", "Stripped fragment from URL"); + logInfo(MODULE_NAME, "cleanUrl():", "Stripped fragment from URL"); } // Option 1: Strip all query params unconditionally if (stripAllQueryParams === true) { urlObj.search = ""; const cleanedUrl = urlObj.toString(); - logInfo(MODULE_NAME, "cleanUrl:", "Output URL:", cleanedUrl); + logInfo(MODULE_NAME, "cleanUrl():", "Output URL:", cleanedUrl); return cleanedUrl; } // Option 2: Strip all query params for specific domains - if (Array.isArray(stripQueryParamsForDomains) && stripQueryParamsForDomains.length > 0) { + if ( + Array.isArray(stripQueryParamsForDomains) && + stripQueryParamsForDomains.length > 0 + ) { const hostname = urlObj.hostname; - const shouldStripForDomain = stripQueryParamsForDomains.some(domain => { + const shouldStripForDomain = stripQueryParamsForDomains.some((domain) => { // Support exact match or subdomain match return hostname === domain || hostname.endsWith("." + domain); }); @@ -197,7 +433,7 @@ export function cleanUrl(url, options = {}) { if (shouldStripForDomain) { urlObj.search = ""; const cleanedUrl = urlObj.toString(); - logInfo(MODULE_NAME, "cleanUrl:", "Output URL:", cleanedUrl); + logInfo(MODULE_NAME, "cleanUrl():", "Output URL:", cleanedUrl); return cleanedUrl; } } @@ -208,21 +444,25 @@ export function cleanUrl(url, options = {}) { // - "??" is treated as query parameter with key "?" and value "" if (Array.isArray(stripQueryParams) && stripQueryParams.length > 0) { const queryParams = urlObj.searchParams; - logInfo(MODULE_NAME, "cleanUrl:", `Query parameters to strip: ${stripQueryParams}`); - stripQueryParams.forEach(param => { + logInfo( + MODULE_NAME, + "cleanUrl():", + `Query parameters to strip: ${stripQueryParams}` + ); + stripQueryParams.forEach((param) => { queryParams.delete(param); }); urlObj.search = queryParams.toString(); const cleanedUrl = urlObj.toString(); - logInfo(MODULE_NAME, "cleanUrl:", "Output URL:", cleanedUrl); + logInfo(MODULE_NAME, "cleanUrl():", "Output URL:", cleanedUrl); return cleanedUrl; } const finalUrl = urlObj.toString(); - logInfo(MODULE_NAME, "cleanUrl:", "Output URL:", finalUrl); + logInfo(MODULE_NAME, "cleanUrl():", "Output URL:", finalUrl); return finalUrl; } catch (e) { - logError(MODULE_NAME, "cleanUrl:", "Error cleaning URL:", e); + logError(MODULE_NAME, "cleanUrl():", "Error cleaning URL:", e); return url; } } @@ -241,72 +481,435 @@ export function injectOrtbData(reqBidsConfigObj, path, data) { } /** - * Builds an IAB category data object for use in OpenRTB. + * Extracts all segment IDs from tier data into a flat array. + * Used for populating OpenRTB 2.5 category fields and building IAB data objects. + * + * @param {Object} tierData The tier data keyed by tier numbers (e.g., {"1": [{id: "IAB12"}], "2": [...]}). + * @returns {Array} Flat array of segment IDs (e.g., ["IAB12", "IAB12-3", "IAB12-5"]). + */ +export function extractCategoryIds(tierData) { + const ids = []; + + // Handle null, undefined, non-object, or array tierData + if (!tierData || typeof tierData !== "object" || Array.isArray(tierData)) { + return ids; + } + + // Process ALL tier keys present in tierData + Object.keys(tierData).forEach((tierKey) => { + const segments = tierData[tierKey]; + if (Array.isArray(segments)) { + segments.forEach((item) => { + if (item?.id) { + ids.push(item.id); + } + }); + } + }); + + return ids; +} + +/** + * Builds an IAB category data object for OpenRTB injection. + * Dynamically processes all tiers present in the response data. * - * @param {Object} marketingCategories Marketing Categories returned by Neuwo API. - * @param {string[]} tiers The tier keys to extract from marketingCategories. - * @param {number} segtax The IAB taxonomy version Id. - * @returns {Object} The constructed data object. + * @param {Object} tierData The tier data keyed by tier numbers (e.g., {"1": [...], "2": [...], "3": [...]}). + * @param {number} segtax The IAB Taxonomy segtax ID. + * @returns {Object} The OpenRTB data object with name, segment array, and ext.segtax. */ -export function buildIabData(marketingCategories, tiers, segtax) { - const data = { +export function buildIabData(tierData, segtax) { + const ids = extractCategoryIds(tierData); + return { name: DATA_PROVIDER, - segment: [], + segment: ids.map((id) => ({ id })), ext: { segtax }, }; +} + +/** + * v1 API specific + * Filters and limits a single tier's taxonomies based on relevance score and count. + * Used for client-side filtering with legacy endpoints. + * + * @param {Array} iabTaxonomies Array of IAB Taxonomy Segments objects with ID, label, and relevance. + * @param {Object} filter Filter configuration with optional threshold and limit properties. + * @returns {Array} Filtered and limited array of taxonomies, sorted by relevance (highest first). + */ +export function filterIabTaxonomyTier(iabTaxonomies, filter = {}) { + if (!Array.isArray(iabTaxonomies)) { + return []; + } + if (iabTaxonomies.length === 0) { + return iabTaxonomies; + } - tiers.forEach((tier) => { - const tierData = marketingCategories?.[tier]; + const { threshold, limit } = filter; + const hasThreshold = typeof threshold === "number" && threshold > 0; + const hasLimit = typeof limit === "number" && limit >= 0; + + // No effective filter configured -- return original order unchanged + if (!hasThreshold && !hasLimit) { + return iabTaxonomies; + } + + let filtered = [...iabTaxonomies]; // Create copy to avoid mutating original + + // Filter by minimum relevance score + if (hasThreshold) { + filtered = filtered.filter((item) => { + const relevance = parseFloat(item?.relevance); + return !isNaN(relevance) && relevance >= threshold; + }); + } + + // Sort by relevance (highest first) so limit keeps the most relevant items + if (hasLimit) { + filtered = filtered.sort((a, b) => { + const relA = parseFloat(a?.relevance) || 0; + const relB = parseFloat(b?.relevance) || 0; + return relB - relA; // Descending order + }); + filtered = filtered.slice(0, limit); + } + + return filtered; +} + +/** + * Maps tier configuration keys to API response keys. + */ +const TIER_KEY_MAP = { + ContentTier1: "iab_tier_1", + ContentTier2: "iab_tier_2", + ContentTier3: "iab_tier_3", + AudienceTier3: "iab_audience_tier_3", + AudienceTier4: "iab_audience_tier_4", + AudienceTier5: "iab_audience_tier_5", +}; + +/** + * v1 API specific + * Applies per-tier filtering to IAB taxonomies (client-side filtering for legacy endpoints). + * Filters taxonomies by relevance score and limits the count per tier. + * + * @param {Object} marketingCategories Marketing categories from legacy API response. + * @param {Object} [tierFilters] Per-tier filter configuration with human-readable tier names (e.g., {ContentTier1: {limit: 3, threshold: 0.75}}). + * @returns {Object} Filtered marketing categories with the same structure as input. + */ +export function filterIabTaxonomies(marketingCategories, tierFilters = {}) { + if (!marketingCategories || typeof marketingCategories !== "object") { + return marketingCategories; + } + + // If no filters provided, return original data + if (!tierFilters || Object.keys(tierFilters).length === 0) { + logInfo( + MODULE_NAME, + "filterIabTaxonomies():", + "No filters provided, returning original data" + ); + return marketingCategories; + } + + const filtered = {}; + + // Iterate through all tiers in the API response + Object.keys(marketingCategories).forEach((apiTierKey) => { + const tierData = marketingCategories[apiTierKey]; + + // Find the corresponding config key for this API tier + const configTierKey = Object.keys(TIER_KEY_MAP).find( + (key) => TIER_KEY_MAP[key] === apiTierKey + ); + + // Get filter for this tier (if configured) + const filter = configTierKey ? tierFilters[configTierKey] : {}; + + // Apply filter if this tier has data if (Array.isArray(tierData)) { - tierData.forEach((item) => { - const ID = item?.ID; - const label = item?.label; + filtered[apiTierKey] = filterIabTaxonomyTier(tierData, filter); + } else { + // Preserve non-array data as-is + filtered[apiTierKey] = tierData; + } + }); + + logInfo( + MODULE_NAME, + "filterIabTaxonomies():", + "Filtering results:", + "Original:", + marketingCategories, + "Filtered:", + filtered + ); + + return filtered; +} + +/** + * v1 API specific + * Transforms legacy API response format to unified internal format. + * Converts marketing_categories structure to segtax-based structure for consistent processing. + * + * Legacy format: { marketing_categories: { iab_tier_1: [...], iab_audience_tier_3: [...] } } + * Unified format: { "6": { "1": [...], "2": [...] }, "4": { "3": [...], "4": [...] } } + * + * @param {Object} v1Response The legacy API response with marketing_categories structure. + * @param {number} contentSegtax The segtax ID for content taxonomies (determined by iabContentTaxonomyVersion). + * @returns {Object} Unified format response keyed by segtax and tier numbers. + */ +export function transformV1ResponseToV2(v1Response, contentSegtax) { + const marketingCategories = v1Response?.marketing_categories || {}; + const contentSegtaxStr = String(contentSegtax); + const result = {}; + + // Content tiers: keyed by segtax from config + result[contentSegtaxStr] = {}; + if (marketingCategories.iab_tier_1) { + result[contentSegtaxStr]["1"] = transformSegmentsV1ToV2( + marketingCategories.iab_tier_1 + ); + } + if (marketingCategories.iab_tier_2) { + result[contentSegtaxStr]["2"] = transformSegmentsV1ToV2( + marketingCategories.iab_tier_2 + ); + } + if (marketingCategories.iab_tier_3) { + result[contentSegtaxStr]["3"] = transformSegmentsV1ToV2( + marketingCategories.iab_tier_3 + ); + } - if (ID && label) { - data.segment.push({ id: ID, name: label }); + // Audience tiers: segtax 4 + result["4"] = {}; + if (marketingCategories.iab_audience_tier_3) { + result["4"]["3"] = transformSegmentsV1ToV2( + marketingCategories.iab_audience_tier_3 + ); + } + if (marketingCategories.iab_audience_tier_4) { + result["4"]["4"] = transformSegmentsV1ToV2( + marketingCategories.iab_audience_tier_4 + ); + } + if (marketingCategories.iab_audience_tier_5) { + result["4"]["5"] = transformSegmentsV1ToV2( + marketingCategories.iab_audience_tier_5 + ); + } + + return result; +} + +/** + * v1 API specific + * Transforms segment objects from legacy format to unified format. + * Maps field names from legacy API response to unified internal representation. + * + * Legacy format: { ID: "123", label: "Category Name", relevance: "0.95" } + * Unified format: { id: "123", name: "Category Name", relevance: "0.95" } + * + * @param {Array} segments Array of legacy segment objects with ID, label, relevance. + * @returns {Array} Array of unified format segment objects with id, name, relevance. + */ +export function transformSegmentsV1ToV2(segments) { + if (!Array.isArray(segments)) return []; + return segments.map((seg) => ({ + id: seg.ID, + name: seg.label, + relevance: seg.relevance, + })); +} + +/** + * Builds flattened query parameters from IAB taxonomy filters. + * Converts human-readable tier names directly to query parameter format for GET requests. + * + * @param {Object} iabTaxonomyFilters Publisher's tier filter configuration using human-readable tier names. + * @param {number} contentSegtax The segtax ID for content taxonomies (determined by iabContentTaxonomyVersion). + * @param {boolean} [enableOrtb25Fields=true] If true, also applies filters to IAB Content Taxonomy 1.0 (segtax 1) for OpenRTB 2.5 category fields. + * @returns {Array} Array of query parameter strings (e.g., ["filter_6_1_limit=3", "filter_6_1_threshold=0.5"]). + * + * @example + * Input: { ContentTier1: { limit: 3, threshold: 0.5 }, AudienceTier3: { limit: 2 } }, contentSegtax=6 + * Output: ["filter_6_1_limit=3", "filter_6_1_threshold=0.5", "filter_4_3_limit=2"] + */ +export function buildFilterQueryParams( + iabTaxonomyFilters, + contentSegtax, + enableOrtb25Fields = true +) { + const params = []; + + if (!iabTaxonomyFilters || typeof iabTaxonomyFilters !== "object") { + return params; + } + + const TIER_TO_SEGTAX = { + ContentTier1: { segtax: contentSegtax, tier: "1" }, + ContentTier2: { segtax: contentSegtax, tier: "2" }, + ContentTier3: { segtax: contentSegtax, tier: "3" }, + AudienceTier3: { segtax: 4, tier: "3" }, + AudienceTier4: { segtax: 4, tier: "4" }, + AudienceTier5: { segtax: 4, tier: "5" }, + }; + + // Build query params from tier mappings + Object.entries(iabTaxonomyFilters).forEach(([tierName, filter]) => { + const mapping = TIER_TO_SEGTAX[tierName]; + if (mapping && filter && typeof filter === "object") { + const segtax = mapping.segtax; + const tier = mapping.tier; + + // Add each filter property (limit, threshold) as a query parameter + Object.keys(filter).forEach((prop) => { + const value = filter[prop]; + if (value !== undefined && value !== null) { + params.push(`filter_${segtax}_${tier}_${prop}=${value}`); } }); } }); - return data; + // Apply same filters to IAB 1.0 (segtax 1) for OpenRTB 2.5 fields. + // Skip when contentSegtax is already 1 -- the first loop already emitted filter_1_* params. + // Note: IAB 1.0 only has tiers 1 and 2 (tier 3 will be ignored if configured) + if (enableOrtb25Fields && contentSegtax !== 1) { + if (iabTaxonomyFilters.ContentTier1) { + Object.keys(iabTaxonomyFilters.ContentTier1).forEach((prop) => { + const value = iabTaxonomyFilters.ContentTier1[prop]; + if (value !== undefined && value !== null) { + params.push(`filter_1_1_${prop}=${value}`); + } + }); + } + + if (iabTaxonomyFilters.ContentTier2) { + Object.keys(iabTaxonomyFilters.ContentTier2).forEach((prop) => { + const value = iabTaxonomyFilters.ContentTier2[prop]; + if (value !== undefined && value !== null) { + params.push(`filter_1_2_${prop}=${value}`); + } + }); + } + } + + return params; } /** - * Processes the Neuwo API response to build and inject IAB content and audience categories - * into the bid request object. + * Processes the Neuwo API response and injects IAB Content and Audience Segments into the bid request. + * Extracts Segments from the response and injects them into ORTB2 structure. + * + * Response format: { "6": { "1": [{id, name}], "2": [...] }, "4": { "3": [...], "4": [...] } } + * - Content taxonomies are injected into ortb2.site.content.data + * - Audience taxonomies are injected into ortb2.user.data + * - If enableOrtb25Fields is true, IAB 1.0 segments are injected into OpenRTB 2.5 category fields * - * @param {Object} responseParsed The parsed JSON response from the Neuwo API. - * @param {Object} reqBidsConfigObj The bid request configuration object to be modified. - * @param {string} iabContentTaxonomyVersion The version of the IAB content taxonomy to use for segtax mapping. + * Only injects data if segments exist to avoid adding empty data structures. + * + * @param {Object} responseParsed The parsed API response. + * @param {Object} reqBidsConfigObj The bid request configuration object to be enriched. + * @param {string} iabContentTaxonomyVersion The IAB Content Taxonomy version for segtax mapping. + * @param {boolean} [enableOrtb25Fields=true] If true, populates OpenRTB 2.5 category fields with IAB Content Taxonomy 1.0 segments. */ -function injectIabCategories(responseParsed, reqBidsConfigObj, iabContentTaxonomyVersion) { - const marketingCategories = responseParsed.marketing_categories; - - if (!marketingCategories) { - logError(MODULE_NAME, "injectIabCategories:", "No Marketing Categories in Neuwo API response."); - return +export function injectIabCategories( + responseParsed, + reqBidsConfigObj, + iabContentTaxonomyVersion, + enableOrtb25Fields = true +) { + if (!responseParsed || typeof responseParsed !== "object") { + logError(MODULE_NAME, "injectIabCategories():", "Invalid response format"); + return; } - // Process content categories - const contentTiers = ["iab_tier_1", "iab_tier_2", "iab_tier_3"]; - const contentData = buildIabData( - marketingCategories, - contentTiers, - IAB_CONTENT_TAXONOMY_MAP[iabContentTaxonomyVersion] || IAB_CONTENT_TAXONOMY_MAP["3.0"] - ); + const contentSegtax = + IAB_CONTENT_TAXONOMY_MAP[iabContentTaxonomyVersion] || + IAB_CONTENT_TAXONOMY_MAP[DEFAULT_IAB_CONTENT_TAXONOMY_VERSION]; + const contentSegtaxStr = String(contentSegtax); + + // Extract IAB Content Taxonomy data for the configured version + const contentTiers = responseParsed[contentSegtaxStr] || {}; + const contentData = buildIabData(contentTiers, contentSegtax); + + // Extract IAB Audience Taxonomy data + const audienceTiers = responseParsed["4"] || {}; + const audienceData = buildIabData(audienceTiers, 4); - // Process audience categories - const audienceTiers = ["iab_audience_tier_3", "iab_audience_tier_4", "iab_audience_tier_5"]; - const audienceData = buildIabData(marketingCategories, audienceTiers, 4); + logInfo( + MODULE_NAME, + "injectIabCategories():", + "contentData structure:", + contentData + ); + logInfo( + MODULE_NAME, + "injectIabCategories():", + "audienceData structure:", + audienceData + ); - logInfo(MODULE_NAME, "injectIabCategories:", "contentData structure:", contentData); - logInfo(MODULE_NAME, "injectIabCategories:", "audienceData structure:", audienceData); + // Inject content and audience data independently to avoid sending empty structures + if (contentData.segment.length > 0) { + injectOrtbData(reqBidsConfigObj, "site.content.data", [contentData]); + logInfo( + MODULE_NAME, + "injectIabCategories():", + "Injected content data into site.content.data" + ); + } else { + logInfo( + MODULE_NAME, + "injectIabCategories():", + "No content segments to inject, skipping site.content.data" + ); + } - injectOrtbData(reqBidsConfigObj, "site.content.data", [contentData]); - injectOrtbData(reqBidsConfigObj, "user.data", [audienceData]); + if (audienceData.segment.length > 0) { + injectOrtbData(reqBidsConfigObj, "user.data", [audienceData]); + logInfo( + MODULE_NAME, + "injectIabCategories():", + "Injected audience data into user.data" + ); + } else { + logInfo( + MODULE_NAME, + "injectIabCategories():", + "No audience segments to inject, skipping user.data" + ); + } - logInfo(MODULE_NAME, "injectIabCategories:", "post-injection bidsConfig", reqBidsConfigObj); + // Inject OpenRTB 2.5 category fields if feature enabled + if (enableOrtb25Fields) { + const iab10Tiers = responseParsed["1"] || {}; // Segtax 1 = IAB Content 1.0 + const categoryIds = extractCategoryIds(iab10Tiers); // ["IAB12", "IAB12-3", ...] + + if (categoryIds.length > 0) { + // Inject same array into all four OpenRTB 2.5 category fields + injectOrtbData(reqBidsConfigObj, "site.cat", categoryIds); + injectOrtbData(reqBidsConfigObj, "site.sectioncat", categoryIds); + injectOrtbData(reqBidsConfigObj, "site.pagecat", categoryIds); + injectOrtbData(reqBidsConfigObj, "site.content.cat", categoryIds); + + logInfo( + MODULE_NAME, + "injectIabCategories():", + "Injected OpenRTB 2.5 category fields:", + categoryIds + ); + } else { + logInfo( + MODULE_NAME, + "injectIabCategories():", + "No IAB 1.0 segments available for OpenRTB 2.5 fields" + ); + } + } } export const neuwoRtdModule = { diff --git a/modules/neuwoRtdProvider.md b/modules/neuwoRtdProvider.md index 804130be1e6..e6c2798a1ad 100644 --- a/modules/neuwoRtdProvider.md +++ b/modules/neuwoRtdProvider.md @@ -10,55 +10,47 @@ The Neuwo RTD provider fetches real-time contextual data from the Neuwo API. Whe This data is then added to the bid request by populating the OpenRTB 2.x objects `ortb2.site.content.data` (for IAB Content Taxonomy) and `ortb2.user.data` (for IAB Audience Taxonomy). This enrichment allows bidders to leverage Neuwo's contextual analysis for more precise targeting and decision-making. +Additionally, when enabled, the module populates OpenRTB 2.5 category fields (`ortb2.site.cat`, `ortb2.site.sectioncat`, `ortb2.site.pagecat`, `ortb2.site.content.cat`) with IAB Content Taxonomy 1.0 segments. + Here is an example scheme of the data injected into the `ortb2` object by our module: ```javascript ortb2: { site: { + // OpenRTB 2.5 category fields (IAB Content Taxonomy 1.0) + cat: ["IAB12", "IAB12-3", "IAB12-5"], + sectioncat: ["IAB12", "IAB12-3", "IAB12-5"], + pagecat: ["IAB12", "IAB12-3", "IAB12-5"], content: { + // OpenRTB 2.5 category field (IAB Content Taxonomy 1.0) + cat: ["IAB12", "IAB12-3", "IAB12-5"], // IAB Content Taxonomy data is injected here data: [{ name: "www.neuwo.ai", - segment: [{ - id: "274", - name: "Home & Garden", - }, - { - id: "42", - name: "Books and Literature", - }, - { - id: "210", - name: "Food & Drink", - }, + segment: [ + { id: "274" }, + { id: "42" }, + { id: "210" }, ], ext: { - segtax: 7, + segtax: 6, }, - }, ], + }], }, }, user: { // IAB Audience Taxonomy data is injected here data: [{ name: "www.neuwo.ai", - segment: [{ - id: "49", - name: "Demographic | Gender | Female |", - }, - { - id: "161", - name: "Demographic | Marital Status | Married |", - }, - { - id: "6", - name: "Demographic | Age Range | 30-34 |", - }, + segment: [ + { id: "49" }, + { id: "161" }, + { id: "6" }, ], ext: { segtax: 4, }, - }, ], + }], }, } ``` @@ -74,16 +66,17 @@ This module is configured as part of the `realTimeData.dataProviders` object. ```javascript pbjs.setConfig({ realTimeData: { - auctionDelay: 500, // Value can be adjusted based on the needs + auctionDelay: 500, // Value can be adjusted based on the needs. Recommended to start with value `500` dataProviders: [ { name: "NeuwoRTDModule", - waitForIt: true, + waitForIt: true, // Recommended to be set to `true` params: { neuwoApiUrl: "", neuwoApiToken: "", - iabContentTaxonomyVersion: "3.0", + iabContentTaxonomyVersion: "2.2", enableCache: true, // Default: true. Caches API responses to avoid redundant requests + enableOrtb25Fields: true, // Default: true. }, }, ], @@ -93,23 +86,61 @@ pbjs.setConfig({ **Parameters** -| Name | Type | Required | Default | Description | -| :---------------------------------- | :------- | :------- | :------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `name` | String | Yes | | The name of the module, which is `NeuwoRTDModule`. | -| `params` | Object | Yes | | Container for module-specific parameters. | -| `params.neuwoApiUrl` | String | Yes | | The endpoint URL for the Neuwo Edge API. | -| `params.neuwoApiToken` | String | Yes | | Your unique API token provided by Neuwo. | -| `params.iabContentTaxonomyVersion` | String | No | `'3.0'` | Specifies the version of the IAB Content Taxonomy to be used. Supported values: `'2.2'`, `'3.0'`. | -| `params.enableCache` | Boolean | No | `true` | If `true`, caches API responses to avoid redundant requests for the same page during the session. Set to `false` to disable caching and make a fresh API call on every bid request. | -| `params.stripAllQueryParams` | Boolean | No | `false` | If `true`, strips all query parameters from the URL before analysis. Takes precedence over other stripping options. | -| `params.stripQueryParamsForDomains` | String[] | No | `[]` | List of domains for which to strip **all** query parameters. When a domain matches, all query params are removed for that domain and all its subdomains (e.g., `'example.com'` strips params for both `'example.com'` and `'sub.example.com'`). This option takes precedence over `stripQueryParams` for matching domains. | -| `params.stripQueryParams` | String[] | No | `[]` | List of specific query parameter names to strip from the URL (e.g., `['utm_source', 'fbclid']`). Other parameters are preserved. Only applies when the domain does not match `stripQueryParamsForDomains`. | -| `params.stripFragments` | Boolean | No | `false` | If `true`, strips URL fragments (hash, e.g., `#section`) from the URL before analysis. | +| Name | Type | Required | Default | Description | +| :---------------------------------- | :------- | :------- | :------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `name` | String | Yes | | The name of the module, which is `NeuwoRTDModule`. | +| `params` | Object | Yes | | Container for module-specific parameters. | +| `params.neuwoApiUrl` | String | Yes | | The endpoint URL for the Neuwo Edge API. | +| `params.neuwoApiToken` | String | Yes | | Your unique API token provided by Neuwo. | +| `params.iabContentTaxonomyVersion` | String | No | `'2.2'` | Specifies the version of the IAB Content Taxonomy to be used. Supported values: `'1.0'`, `'2.2'`, `'3.0'`. | +| `params.enableCache` | Boolean | No | `true` | If `true`, caches API responses to avoid redundant requests for the same page during the session. Set to `false` to disable caching and make a fresh API call on every bid request. | +| `params.enableOrtb25Fields` | Boolean | No | `true` | If `true`, populates OpenRTB 2.5 category fields (`ortb2.site.cat`, `ortb2.site.sectioncat`, `ortb2.site.pagecat`, `ortb2.site.content.cat`) with IAB Content Taxonomy 1.0 segments. See [OpenRTB 2.5 Category Fields](#openrtb-25-category-fields) section below for details. | +| `params.stripAllQueryParams` | Boolean | No | `false` | If `true`, strips all query parameters from the URL before analysis. Takes precedence over other stripping options. | +| `params.stripQueryParamsForDomains` | String[] | No | `[]` | List of domains for which to strip **all** query parameters. When a domain matches, all query params are removed for that domain and all its subdomains (e.g., `'example.com'` strips params for both `'example.com'` and `'sub.example.com'`). This option takes precedence over `stripQueryParams` for matching domains. | +| `params.stripQueryParams` | String[] | No | `[]` | List of specific query parameter names to strip from the URL (e.g., `['utm_source', 'fbclid']`). Other parameters are preserved. Only applies when the domain does not match `stripQueryParamsForDomains`. | +| `params.stripFragments` | Boolean | No | `false` | If `true`, strips URL fragments (hash, e.g., `#section`) from the URL before analysis. | +| `params.iabTaxonomyFilters` | Object | No | | Per-tier filtering configuration for IAB taxonomies. Allows filtering by relevance threshold and limiting the count of categories per tier. Filters configured for `ContentTier1` and `ContentTier2` are automatically applied to IAB Content Taxonomy 1.0 when `enableOrtb25Fields` is `true`. See [IAB Taxonomy Filtering](#iab-taxonomy-filtering) section for details. | ### API Response Caching By default, the module caches API responses during the page session to optimise performance and reduce redundant API calls. This behaviour can be disabled by setting `enableCache: false` if needed for dynamic content scenarios. +### OpenRTB 2.5 Category Fields + +The module supports populating OpenRTB 2.5 category fields with IAB Content Taxonomy 1.0 segments. This feature is enabled by default and provides additional contextual signals to bidders through standard OpenRTB fields. + +**Category Fields Populated:** + +- `ortb2.site.cat` - Array of IAB Content Taxonomy 1.0 category IDs +- `ortb2.site.sectioncat` - Array of IAB Content Taxonomy 1.0 category IDs +- `ortb2.site.pagecat` - Array of IAB Content Taxonomy 1.0 category IDs +- `ortb2.site.content.cat` - Array of IAB Content Taxonomy 1.0 category IDs + +**Result Example:** + +With `enableOrtb25Fields: true`, the module injects: + +```javascript +ortb2: { + site: { + // OpenRTB 2.5 category fields + cat: ["IAB12", "IAB12-3", "IAB12-5"], + sectioncat: ["IAB12", "IAB12-3", "IAB12-5"], + pagecat: ["IAB12", "IAB12-3", "IAB12-5"], + content: { + // OpenRTB 2.5 category field + cat: ["IAB12", "IAB12-3", "IAB12-5"], + // Standard content data + data: [{ + name: "www.neuwo.ai", + segment: [{ id: "274" }, { id: "42" }], + ext: { segtax: 6 } + }] + } + } +} +``` + ### URL Cleaning Options The module provides optional URL cleaning capabilities to strip query parameters and/or fragments from the analysed URL before sending it to the Neuwo API. This can be useful for privacy, caching, or analytics purposes. @@ -119,15 +150,15 @@ The module provides optional URL cleaning capabilities to strip query parameters ```javascript pbjs.setConfig({ realTimeData: { - auctionDelay: 500, // Value can be adjusted based on the needs + auctionDelay: 500, // Value can be adjusted based on the needs. Recommended to start with value `500` dataProviders: [ { name: "NeuwoRTDModule", - waitForIt: true, + waitForIt: true, // Recommended to be set to `true` params: { neuwoApiUrl: "", neuwoApiToken: "", - iabContentTaxonomyVersion: "3.0", + iabContentTaxonomyVersion: "2.2", // Option 1: Strip all query parameters from the URL stripAllQueryParams: true, @@ -147,6 +178,133 @@ pbjs.setConfig({ }); ``` +### IAB Taxonomy Filtering + +The module provides optional per-tier filtering for IAB taxonomies to control the quantity and quality of categories injected into bid requests. This allows you to limit categories based on their relevance score and restrict the maximum number of categories per tier. Filtering is performed server-side, which means only the filtered categories are returned in the response. This reduces bandwidth and improves performance. + +**Filter Configuration:** + +Each tier can have two optional parameters: + +- `threshold` (Number): Minimum relevance score (0.0 to 1.0). Categories below this threshold are excluded. +- `limit` (Number): Maximum number of categories to include for this tier (after filtering and sorting by relevance). + +**Available Tiers:** + +| Tier Name | Description | IAB Taxonomy | +| :-------------- | :------------------ | :----------------------------------- | +| `ContentTier1` | IAB Content Tier 1 | Based on configured taxonomy version | +| `ContentTier2` | IAB Content Tier 2 | Based on configured taxonomy version | +| `ContentTier3` | IAB Content Tier 3 | Based on configured taxonomy version | +| `AudienceTier3` | IAB Audience Tier 3 | IAB Audience Taxonomy 1.1 (segtax 4) | +| `AudienceTier4` | IAB Audience Tier 4 | IAB Audience Taxonomy 1.1 (segtax 4) | +| `AudienceTier5` | IAB Audience Tier 5 | IAB Audience Taxonomy 1.1 (segtax 4) | + +**Example with IAB taxonomy filtering:** + +```javascript +pbjs.setConfig({ + realTimeData: { + auctionDelay: 500, // Value can be adjusted based on the needs. Recommended to start with value `500` + dataProviders: [ + { + name: "NeuwoRTDModule", + waitForIt: true, // Recommended to be set to `true` + params: { + neuwoApiUrl: "", + neuwoApiToken: "", + iabContentTaxonomyVersion: "2.2", + + // Filter IAB taxonomies by tier + iabTaxonomyFilters: { + // Content Tier 1: Keep only the top category with at least 10% relevance + ContentTier1: { limit: 1, threshold: 0.1 }, + + // Content Tier 2: Keep top 2 categories with at least 10% relevance + ContentTier2: { limit: 2, threshold: 0.1 }, + + // Content Tier 3: Keep top 3 categories with at least 15% relevance + ContentTier3: { limit: 3, threshold: 0.15 }, + + // Audience Tier 3: Keep top 3 categories with at least 20% relevance + AudienceTier3: { limit: 3, threshold: 0.2 }, + + // Audience Tier 4: Keep top 5 categories with at least 20% relevance + AudienceTier4: { limit: 5, threshold: 0.2 }, + + // Audience Tier 5: Keep top 7 categories with at least 30% relevance + AudienceTier5: { limit: 7, threshold: 0.3 }, + }, + }, + }, + ], + }, +}); +``` + +**OpenRTB 2.5 Category Fields Filtering** + +When `iabTaxonomyFilters` are configured, the same filters applied to `ContentTier1` and `ContentTier2` are automatically applied to IAB Content Taxonomy 1.0 data used for these fields. Note that IAB Content Taxonomy 1.0 only has tiers 1 and 2, so `ContentTier3` filters are ignored for these fields. + +## Accessing Neuwo Data Outside Prebid.js + +The Neuwo RTD module enriches bid requests with contextual data that can be accessed in application code for analytics, targeting, integration with Google Ad Manager as [Publisher Provided Signals (PPS)](https://support.google.com/admanager/answer/15287325) or other purposes. The enriched data is available through Prebid.js events. + +### Example of Using the `bidRequested` Event + +Listen to the `bidRequested` event to access the enriched ORTB2 data. This event fires early in the auction lifecycle and provides direct access to the Neuwo data: + +```javascript +pbjs.que.push(function () { + pbjs.onEvent("bidRequested", function (bidRequest) { + // The ortb2 data is available directly on the bidRequest + const ortb2 = bidRequest.ortb2; + + // Extract Neuwo-specific data (from www.neuwo.ai provider) + const neuwoSiteData = ortb2?.site?.content?.data?.find( + (d) => d.name === "www.neuwo.ai" + ); + const neuwoUserData = ortb2?.user?.data?.find( + (d) => d.name === "www.neuwo.ai" + ); + + // Extract OpenRTB 2.5 category fields (if enableOrtb25Fields is true) + const categoryFields = { + siteCat: ortb2?.site?.cat, + siteSectioncat: ortb2?.site?.sectioncat, + sitePagecat: ortb2?.site?.pagecat, + contentCat: ortb2?.site?.content?.cat, + }; + + // Use the data in the application + console.log("Neuwo Site Content:", neuwoSiteData); + console.log("Neuwo User Data:", neuwoUserData); + console.log("OpenRTB 2.5 Category Fields:", categoryFields); + + // Example: Store in a global variable for later use + window.neuwoData = { + siteContent: neuwoSiteData, + user: neuwoUserData, + categoryFields: categoryFields, + }; + }); +}); +``` + +### Other Prebid.js Events + +The Neuwo data is also available in other Prebid.js events: + +| Order | Event | Fires Once Per | Data Location | +| :---- | :----------------- | :------------- | :----------------------------------- | +| 1 | `auctionInit` | Auction | `auctionData.bidderRequests[].ortb2` | +| 2 | `bidRequested` | Bidder | `bidRequest.ortb2` | +| 3 | `beforeBidderHttp` | Bidder | `bidRequests[].ortb2` | +| 4 | `bidResponse` | Bidder | `bidResponse.ortb2` | +| 5 | `auctionEnd` | Auction | `auctionData.bidderRequests[].ortb2` | + +For more information on Prebid.js events, see the [Prebid.js Event API documentation](https://docs.prebid.org/dev-docs/publisher-api-reference/getEvents.html). + ## Local Development Install the exact versions of packages specified in the lockfile: @@ -194,7 +352,7 @@ npx eslint 'modules/neuwoRtdProvider.js' --cache --cache-strategy content To run the module-specific tests: ```bash -npx gulp test-only --modules=rtdModule,neuwoRtdProvider,appnexusBidAdapter --file=test/spec/modules/euwoRtdProvider_spec.js +npx gulp test-only --modules=rtdModule,neuwoRtdProvider,appnexusBidAdapter --file=test/spec/modules/neuwoRtdProvider_spec.js ``` Skip building, if the project has already been built: @@ -202,3 +360,32 @@ Skip building, if the project has already been built: ```bash npx gulp test-only-nobuild --file=test/spec/modules/neuwoRtdProvider_spec.js ``` + +To generate test coverage report for the Neuwo RTD Module: + +```bash +npx gulp test-coverage --file=test/spec/modules/neuwoRtdProvider_spec.js +``` + +After running the coverage command, you can view the HTML report: + +```bash +# Open the coverage report in your browser +firefox build/coverage/lcov-report/index.html +# or +google-chrome build/coverage/lcov-report/index.html +``` + +Navigate to `modules/neuwoRtdProvider.js` in the report to see detailed line-by-line coverage with highlighted covered/uncovered lines. + +## Building for Production + +To generate minified code for production use: + +```bash +npx gulp build --modules=rtdModule,neuwoRtdProvider +``` + +This command creates optimised, minified code typically used on websites. + +> Version **2.2.6** diff --git a/modules/nextMillenniumBidAdapter.js b/modules/nextMillenniumBidAdapter.js index ec089e151aa..b5766b30607 100644 --- a/modules/nextMillenniumBidAdapter.js +++ b/modules/nextMillenniumBidAdapter.js @@ -13,14 +13,14 @@ import { triggerPixel, } from '../src/utils.js'; -import {getAd} from '../libraries/targetVideoUtils/bidderUtils.js'; +import { getAd } from '../libraries/targetVideoUtils/bidderUtils.js'; import { EVENTS } from '../src/constants.js'; -import {BANNER, VIDEO} from '../src/mediaTypes.js'; -import {config} from '../src/config.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; +import { config } from '../src/config.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {getRefererInfo} from '../src/refererDetection.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { getRefererInfo } from '../src/refererDetection.js'; import { getViewportSize } from '../libraries/viewport/viewport.js'; import { getConnectionInfo } from '../libraries/connectionInfo/connectionUtils.js'; @@ -180,7 +180,7 @@ export const spec = { _each(validBidRequests, (bid, i) => { window.nmmRefreshCounts[bid.adUnitCode] = window.nmmRefreshCounts[bid.adUnitCode] || 0; const id = getPlacementId(bid); - const {cur, mediaTypes} = getCurrency(bid); + const { cur, mediaTypes } = getCurrency(bid); if (i === 0) postBody.cur = cur; const impId = String(i + 1) @@ -218,7 +218,7 @@ export const spec = { _each(resp.bid, (bid) => { const requestId = bidRequest.bidIds.get(bid.impid); - const {ad, adUrl, vastUrl, vastXml} = getAd(bid); + const { ad, adUrl, vastUrl, vastXml } = getAd(bid); const bidResponse = { requestId, @@ -259,7 +259,7 @@ export const spec = { if (!syncOptions.iframeEnabled && !syncOptions.pixelEnabled) return []; const pixels = []; - const getSetPixelFunc = type => url => { pixels.push({type, url: replaceUsersyncMacros(url, gdprConsent, uspConsent, gppConsent, type)}) }; + const getSetPixelFunc = type => url => { pixels.push({ type, url: replaceUsersyncMacros(url, gdprConsent, uspConsent, gppConsent, type) }) }; const getSetPixelsFunc = type => response => { deepAccess(response, `body.ext.sync.${type}`, []).forEach(getSetPixelFunc(type)) }; const setPixel = (type, url) => { (getSetPixelFunc(type))(url) }; @@ -337,7 +337,7 @@ export const spec = { export function getExtNextMilImp(impId, bid) { if (typeof window?.nmmRefreshCounts[bid.adUnitCode] === 'number') ++window.nmmRefreshCounts[bid.adUnitCode]; - const {adSlots, allowedAds} = bid.params + const { adSlots, allowedAds } = bid.params const nextMilImp = { impId, nextMillennium: { @@ -355,7 +355,7 @@ export function getExtNextMilImp(impId, bid) { } export function getImp(impId, bid, id, mediaTypes) { - const {banner, video} = mediaTypes; + const { banner, video } = mediaTypes; const imp = { id: impId, ext: { @@ -382,8 +382,8 @@ export function getImpBanner(imp, banner) { if (banner.bidfloorcur) imp.bidfloorcur = banner.bidfloorcur; if (banner.bidfloor) imp.bidfloor = banner.bidfloor; - const format = (banner.data?.sizes || []).map(s => { return {w: s[0], h: s[1]} }); - const {w, h} = (format[0] || {}) + const format = (banner.data?.sizes || []).map(s => { return { w: s[0], h: s[1] } }); + const { w, h } = (format[0] || {}) imp.banner = { w, h, @@ -499,13 +499,13 @@ function getCurrency(bid = {}) { for (const mediaType of types) { const mediaTypeData = deepAccess(bid, `mediaTypes.${mediaType}`); if (mediaTypeData) { - mediaTypes[mediaType] = {data: mediaTypeData}; + mediaTypes[mediaType] = { data: mediaTypeData }; } else { continue; }; if (typeof bid.getFloor === 'function') { - const floorInfo = bid.getFloor({currency, mediaType, size: '*'}); + const floorInfo = bid.getFloor({ currency, mediaType, size: '*' }); mediaTypes[mediaType].bidfloorcur = floorInfo?.currency; mediaTypes[mediaType].bidfloor = floorInfo?.floor; } else { @@ -517,7 +517,7 @@ function getCurrency(bid = {}) { if (!cur.length) cur.push(DEFAULT_CURRENCY); - return {cur, mediaTypes}; + return { cur, mediaTypes }; } export function getPlacementId(bid) { @@ -611,13 +611,13 @@ export function getSourceObj(validBidRequests, bidderRequest) { } function getSua() { - const {brands, mobile, platform} = (window?.navigator?.userAgentData || {}); + const { brands, mobile, platform } = (window?.navigator?.userAgentData || {}); if (!(brands && platform)) return undefined; return { browsers: brands, mobile: Number(!!mobile), - platform: (platform && {brand: platform}) || undefined, + platform: (platform && { brand: platform }) || undefined, }; } diff --git a/modules/nextrollBidAdapter.js b/modules/nextrollBidAdapter.js index 92fc2222fb2..8ac2b977a32 100644 --- a/modules/nextrollBidAdapter.js +++ b/modules/nextrollBidAdapter.js @@ -6,9 +6,10 @@ import { isPlainObject, isStr, parseUrl, - replaceAuctionPrice} from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER, NATIVE} from '../src/mediaTypes.js'; + replaceAuctionPrice +} from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, NATIVE } from '../src/mediaTypes.js'; import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; import { getOsVersion } from '../libraries/advangUtils/index.js'; @@ -104,7 +105,7 @@ export const spec = { function _getBanner(bidRequest) { const sizes = _getSizes(bidRequest); if (sizes === undefined) return undefined; - return {format: sizes}; + return { format: sizes }; } function _getNative(mediaTypeNative) { @@ -128,12 +129,12 @@ function _getNative(mediaTypeNative) { required: Overrides the asset required field configured, only overrides when is true. */ const NATIVE_ASSET_MAP = [ - {id: 1, kind: 'title', key: 'title', required: true}, - {id: 2, kind: 'img', key: 'image', type: 3, required: true}, - {id: 3, kind: 'img', key: 'icon', type: 1}, - {id: 4, kind: 'img', key: 'logo', type: 2}, - {id: 5, kind: 'data', key: 'sponsoredBy', type: 1}, - {id: 6, kind: 'data', key: 'body', type: 2} + { id: 1, kind: 'title', key: 'title', required: true }, + { id: 2, kind: 'img', key: 'image', type: 3, required: true }, + { id: 3, kind: 'img', key: 'icon', type: 1 }, + { id: 4, kind: 'img', key: 'logo', type: 2 }, + { id: 5, kind: 'data', key: 'sponsoredBy', type: 1 }, + { id: 6, kind: 'data', key: 'body', type: 2 } ]; const ASSET_KIND_MAP = { @@ -154,7 +155,7 @@ function _getAsset(mediaTypeNative, assetMap) { } function _getTitleAsset(title, _assetMap) { - return {len: title.len || 0}; + return { len: title.len || 0 }; } function _getMinAspectRatio(aspectRatio, property) { diff --git a/modules/nexverseBidAdapter.js b/modules/nexverseBidAdapter.js index 25d52000a82..4e65a98066c 100644 --- a/modules/nexverseBidAdapter.js +++ b/modules/nexverseBidAdapter.js @@ -1,14 +1,15 @@ -import {getDNT} from '../libraries/dnt/index.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER, VIDEO, NATIVE } from '../src/mediaTypes.js'; import { isArray, generateUUID, getWinDimensions, isNumber } from '../src/utils.js'; import { getBoundingClientRect } from '../libraries/boundingClientRect/boundingClientRect.js'; -import {getConnectionType} from '../libraries/connectionInfo/connectionUtils.js' +import { getConnectionType } from '../libraries/connectionInfo/connectionUtils.js' import { getDeviceType } from '../libraries/userAgentUtils/index.js'; import { getDeviceModel, buildEndpointUrl, isBidRequestValid, parseNativeResponse, printLog, getUid, getBidFloor, getOsInfo } from '../libraries/nexverseUtils/index.js'; -import {getStorageManager} from '../src/storageManager.js'; -import {MODULE_TYPE_UID} from '../src/activities/modules.js'; +import { getStorageManager } from '../src/storageManager.js'; +import { MODULE_TYPE_UID } from '../src/activities/modules.js'; import { config } from '../src/config.js'; +import { getAdUnitElement } from '../src/utils/adUnits.js'; +import { getDNT } from '../libraries/dnt/index.js'; const BIDDER_CODE = 'nexverse'; const BIDDER_ENDPOINT = 'https://rtb.nexverse.ai'; @@ -17,7 +18,7 @@ const DEFAULT_CURRENCY = 'USD'; const BID_TTL = 300; const DEFAULT_LANG = 'en'; -export const storage = getStorageManager({moduleType: MODULE_TYPE_UID, moduleName: BIDDER_CODE}); +export const storage = getStorageManager({ moduleType: MODULE_TYPE_UID, moduleName: BIDDER_CODE }); export const spec = { code: BIDDER_CODE, @@ -191,7 +192,7 @@ function buildOpenRtbRequest(bid, bidderRequest) { const imps = []; // Calculate viewability percentage for the ad unit - const adUnitElement = document.getElementById(bid.adUnitCode); + const adUnitElement = getAdUnitElement(bid); let viewabilityPercentage = 0; if (adUnitElement) { const rect = getBoundingClientRect(adUnitElement); diff --git a/modules/nexx360BidAdapter.ts b/modules/nexx360BidAdapter.ts index a5aa9e92dd7..513efd89100 100644 --- a/modules/nexx360BidAdapter.ts +++ b/modules/nexx360BidAdapter.ts @@ -1,8 +1,8 @@ import { deepSetValue, generateUUID, logError } from '../src/utils.js'; -import {getStorageManager} from '../src/storageManager.js'; -import {AdapterRequest, BidderSpec, registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js'; -import {ortbConverter} from '../libraries/ortbConverter/converter.js' +import { getStorageManager } from '../src/storageManager.js'; +import { AdapterRequest, BidderSpec, registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; +import { ortbConverter } from '../libraries/ortbConverter/converter.js' import { interpretResponse, enrichImp, enrichRequest, getAmxId, getLocalStorageFunctionGenerator, getUserSyncs } from '../libraries/nexx360Utils/index.js'; import { getBoundingClientRect } from '../libraries/boundingClientRect/boundingClientRect.js'; @@ -142,7 +142,7 @@ const buildRequests = ( bidRequests: BidRequest[], bidderRequest: ClientBidderRequest, ): AdapterRequest => { - const data:ORTBRequest = converter.toORTB({bidRequests, bidderRequest}) + const data:ORTBRequest = converter.toORTB({ bidRequests, bidderRequest }) const adapterRequest:AdapterRequest = { method: 'POST', url: REQUEST_URL, diff --git a/modules/nobidAnalyticsAdapter.js b/modules/nobidAnalyticsAdapter.js index 37064938b2a..484e4dc2b2f 100644 --- a/modules/nobidAnalyticsAdapter.js +++ b/modules/nobidAnalyticsAdapter.js @@ -1,10 +1,10 @@ -import {deepClone, logError, getParameterByName, logMessage} from '../src/utils.js'; -import {ajax} from '../src/ajax.js'; -import {getStorageManager} from '../src/storageManager.js'; +import { deepClone, logError, getParameterByName, logMessage } from '../src/utils.js'; +import { ajax } from '../src/ajax.js'; +import { getStorageManager } from '../src/storageManager.js'; import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import { EVENTS } from '../src/constants.js'; import adapterManager from '../src/adapterManager.js'; -import {MODULE_TYPE_ANALYTICS} from '../src/activities/modules.js'; +import { MODULE_TYPE_ANALYTICS } from '../src/activities/modules.js'; const VERSION = '2.0.2'; const MODULE_NAME = 'nobidAnalyticsAdapter'; @@ -15,7 +15,7 @@ window.nobidAnalyticsVersion = VERSION; const analyticsType = 'endpoint'; const url = 'localhost:8383/event'; const GVLID = 816; -const storage = getStorageManager({gvlid: GVLID, moduleName: MODULE_NAME, moduleType: MODULE_TYPE_ANALYTICS}); +const storage = getStorageManager({ gvlid: GVLID, moduleName: MODULE_NAME, moduleType: MODULE_TYPE_ANALYTICS }); const { AUCTION_INIT, BID_REQUESTED, @@ -113,7 +113,7 @@ function auctionInit (event) { nobidAnalytics.topLocation = event.bidderRequests[0].refererInfo.topmostLocation; } } -let nobidAnalytics = Object.assign(adapter({url, analyticsType}), { +let nobidAnalytics = Object.assign(adapter({ url, analyticsType }), { track({ eventType, args }) { switch (eventType) { case AUCTION_INIT: diff --git a/modules/nobidBidAdapter.js b/modules/nobidBidAdapter.js index c63bad76aba..7474fb6def6 100644 --- a/modules/nobidBidAdapter.js +++ b/modules/nobidBidAdapter.js @@ -16,7 +16,7 @@ import { hasPurpose1Consent } from '../src/utils/gdpr.js'; const GVLID = 816; const BIDDER_CODE = 'nobid'; -const storage = getStorageManager({bidderCode: BIDDER_CODE}); +const storage = getStorageManager({ bidderCode: BIDDER_CODE }); window.nobidVersion = '1.3.4'; window.nobid = window.nobid || {}; window.nobid.bidResponses = window.nobid.bidResponses || {}; @@ -94,7 +94,7 @@ function nobidBuildRequests(bids, bidderRequest) { } var coppa = function() { if (config.getConfig('coppa') === true) { - return {'coppa': true}; + return { 'coppa': true }; } if (bids && bids.length > 0) { return bids[0].coppa @@ -139,11 +139,11 @@ function nobidBuildRequests(bids, bidderRequest) { const ids = []; if (eid.uids) { eid.uids.forEach(value => { - ids.push({'id': value.id + ''}); + ids.push({ 'id': value.id + '' }); }); } if (eid.source && ids.length > 0) { - src.push({source: eid.source, uids: ids}); + src.push({ source: eid.source, uids: ids }); } }); return src; @@ -375,7 +375,7 @@ export const spec = { code: BIDDER_CODE, gvlid: GVLID, aliases: [ - { code: 'duration'} + { code: 'duration' } ], supportedMediaTypes: [BANNER, VIDEO], /** diff --git a/modules/nodalsAiRtdProvider.js b/modules/nodalsAiRtdProvider.js index 104e91ab851..17186f0a151 100644 --- a/modules/nodalsAiRtdProvider.js +++ b/modules/nodalsAiRtdProvider.js @@ -116,7 +116,7 @@ class NodalsAiRtdProvider { } const engine = this.#initialiseEngine(config); if (!engine) { - this.#addToCommandQueue('getBidRequestData', {config, reqBidsConfigObj, callback, userConsent, storedData }); + this.#addToCommandQueue('getBidRequestData', { config, reqBidsConfigObj, callback, userConsent, storedData }); } else { try { engine.getBidRequestData( @@ -143,7 +143,7 @@ class NodalsAiRtdProvider { } const engine = this.#initialiseEngine(config); if (!engine) { - this.#addToCommandQueue('onBidResponseEvent', {config, bidResponse, userConsent, storedData }) + this.#addToCommandQueue('onBidResponseEvent', { config, bidResponse, userConsent, storedData }) return; } try { @@ -164,7 +164,7 @@ class NodalsAiRtdProvider { } const engine = this.#initialiseEngine(config); if (!engine) { - this.#addToCommandQueue('onAuctionEndEvent', {config, auctionDetails, userConsent, storedData }); + this.#addToCommandQueue('onAuctionEndEvent', { config, auctionDetails, userConsent, storedData }); return; } try { diff --git a/modules/novatiqIdSystem.js b/modules/novatiqIdSystem.js index 50478183753..edbdf6cc293 100644 --- a/modules/novatiqIdSystem.js +++ b/modules/novatiqIdSystem.js @@ -8,8 +8,8 @@ import { logInfo, getWindowLocation } from '../src/utils.js'; import { ajax } from '../src/ajax.js'; import { submodule } from '../src/hook.js'; -import {getStorageManager} from '../src/storageManager.js'; -import {MODULE_TYPE_UID} from '../src/activities/modules.js'; +import { getStorageManager } from '../src/storageManager.js'; +import { MODULE_TYPE_UID } from '../src/activities/modules.js'; /** * @typedef {import('../modules/userId/index.js').Submodule} Submodule @@ -92,8 +92,10 @@ export const novatiqIdSubmodule = { } else { this.sendSimpleSyncRequest(novatiqId, url); - return { 'id': novatiqId, - 'sharedStatus': sharedStatus } + return { + 'id': novatiqId, + 'sharedStatus': sharedStatus + } } }, @@ -124,7 +126,7 @@ export const novatiqIdSubmodule = { undefined, { method: 'GET', withCredentials: false }); } - return {callback: resp}; + return { callback: resp }; }, sendSimpleSyncRequest(novatiqId, url) { @@ -225,7 +227,7 @@ export const novatiqIdSubmodule = { let sharedId = null; if (this.useSharedId(configParams)) { const cookieOrStorageID = this.getCookieOrStorageID(configParams); - const storage = getStorageManager({moduleType: MODULE_TYPE_UID, moduleName: MODULE_NAME}); + const storage = getStorageManager({ moduleType: MODULE_TYPE_UID, moduleName: MODULE_NAME }); // first check local storage if (storage.hasLocalStorage()) { diff --git a/modules/oguryBidAdapter.js b/modules/oguryBidAdapter.js index 00c8bfb77ef..48cfa78e186 100644 --- a/modules/oguryBidAdapter.js +++ b/modules/oguryBidAdapter.js @@ -109,18 +109,18 @@ function getUserSyncs(syncOptions, serverResponses, gdprConsent, uspConsent, gpp } function buildRequests(bidRequests, bidderRequest) { - const data = converter.toORTB({bidRequests, bidderRequest}); + const data = converter.toORTB({ bidRequests, bidderRequest }); return { method: 'POST', url: BID_HOST, data, - options: {contentType: 'application/json'}, + options: { contentType: 'application/json' }, }; } function interpretResponse(response, request) { - return converter.fromORTB({response: response.body, request: request.data}).bids; + return converter.fromORTB({ response: response.body, request: request.data }).bids; } function getFloor(bid) { @@ -153,7 +153,7 @@ function onBidWon(bid) { } function onTimeout(timeoutData) { - ajax(`${TIMEOUT_MONITORING_HOST}/bid_timeout`, null, JSON.stringify({...timeoutData[0], location: window.location.href}), { + ajax(`${TIMEOUT_MONITORING_HOST}/bid_timeout`, null, JSON.stringify({ ...timeoutData[0], location: window.location.href }), { method: 'POST', contentType: 'application/json' }); diff --git a/modules/omnidexBidAdapter.js b/modules/omnidexBidAdapter.js index 6fd1f34cf21..d83449edb73 100644 --- a/modules/omnidexBidAdapter.js +++ b/modules/omnidexBidAdapter.js @@ -1,6 +1,6 @@ -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER, VIDEO} from '../src/mediaTypes.js'; -import {getStorageManager} from '../src/storageManager.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; +import { getStorageManager } from '../src/storageManager.js'; import { isBidRequestValid, onBidWon, @@ -13,14 +13,14 @@ const DEFAULT_SUB_DOMAIN = 'exchange'; const BIDDER_CODE = 'omnidex'; const BIDDER_VERSION = '1.0.0'; const GVLID = 1463; -export const storage = getStorageManager({bidderCode: BIDDER_CODE}); +export const storage = getStorageManager({ bidderCode: BIDDER_CODE }); export function createDomain(subDomain = DEFAULT_SUB_DOMAIN) { return `https://${subDomain}.omni-dex.io`; } function createUniqueRequestData(hashUrl, bid) { - const {auctionId, transactionId} = bid; + const { auctionId, transactionId } = bid; return { auctionId, transactionId diff --git a/modules/omsBidAdapter.js b/modules/omsBidAdapter.js index fd7be06409e..d9f1e8091db 100644 --- a/modules/omsBidAdapter.js +++ b/modules/omsBidAdapter.js @@ -8,14 +8,16 @@ import { getBidIdParameter, getUniqueIdentifierStr, formatQS, + deepAccess, } from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; -import {ajax} from '../src/ajax.js'; -import {percentInView} from '../libraries/percentInView/percentInView.js'; -import {getUserSyncParams} from '../libraries/userSyncUtils/userSyncUtils.js'; -import {getMinSize} from '../libraries/sizeUtils/sizeUtils.js'; -import {getBidFloor, isIframe} from '../libraries/omsUtils/index.js'; +import { ajax } from '../src/ajax.js'; +import { percentInView } from '../libraries/percentInView/percentInView.js'; +import { getUserSyncParams } from '../libraries/userSyncUtils/userSyncUtils.js'; +import { getMinSize } from '../libraries/sizeUtils/sizeUtils.js'; +import { getBidFloor, isIframe } from '../libraries/omsUtils/index.js'; +import { getAdUnitElement } from '../src/utils/adUnits.js'; const BIDDER_CODE = 'oms'; const URL = 'https://rt.marphezis.com/hb'; @@ -41,9 +43,9 @@ function buildRequests(bidReqs, bidderRequest) { let bidSizes = bid?.mediaTypes?.banner?.sizes || bid.sizes || []; bidSizes = ((isArray(bidSizes) && isArray(bidSizes[0])) ? bidSizes : [bidSizes]); bidSizes = bidSizes.filter(size => isArray(size)); - const processedSizes = bidSizes.map(size => ({w: parseInt(size[0], 10), h: parseInt(size[1], 10)})); + const processedSizes = bidSizes.map(size => ({ w: parseInt(size[0], 10), h: parseInt(size[1], 10) })); - const element = document.getElementById(bid.adUnitCode); + const element = getAdUnitElement(bid); const minSize = getMinSize(processedSizes); const viewabilityAmount = _isViewabilityMeasurable(element) ? _getViewability(element, getWindowTop(), minSize) : 'na'; const viewabilityAmountRounded = isNaN(viewabilityAmount) ? viewabilityAmount : Math.round(viewabilityAmount); @@ -51,6 +53,7 @@ function buildRequests(bidReqs, bidderRequest) { const imp = { id: bid.bidId, + displaymanagerver: '$prebid.version$', ext: { ...gpidData }, @@ -72,6 +75,10 @@ function buildRequests(bidReqs, bidderRequest) { } } + if (deepAccess(bid, 'ortb2Imp.instl') === 1) { + imp.instl = 1; + } + const bidFloor = getBidFloor(bid); if (bidFloor) { @@ -143,7 +150,7 @@ function buildRequests(bidReqs, bidderRequest) { data: JSON.stringify(payload), }; } catch (e) { - logError(e, {bidReqs, bidderRequest}); + logError(e, { bidReqs, bidderRequest }); } } @@ -162,7 +169,7 @@ function interpretResponse(serverResponse) { return response; } - const {body: {id, seatbid}} = serverResponse; + const { body: { id, seatbid } } = serverResponse; try { if (id && seatbid && seatbid.length > 0 && seatbid[0].bid && seatbid[0].bid.length > 0) { @@ -193,7 +200,7 @@ function interpretResponse(serverResponse) { }); } } catch (e) { - logError(e, {id, seatbid}); + logError(e, { id, seatbid }); } return response; @@ -272,8 +279,8 @@ function _isViewabilityMeasurable(element) { return !isIframe() && element !== null; } -function _getViewability(element, topWin, {w, h} = {}) { - return getWindowTop().document.visibilityState === 'visible' ? percentInView(element, {w, h}) : 0; +function _getViewability(element, topWin, { w, h } = {}) { + return getWindowTop().document.visibilityState === 'visible' ? percentInView(element, { w, h }) : 0; } function _extractGpidData(bid) { diff --git a/modules/oneKeyIdSystem.js b/modules/oneKeyIdSystem.js index 5528212531d..5e8ef101bd3 100644 --- a/modules/oneKeyIdSystem.js +++ b/modules/oneKeyIdSystem.js @@ -5,7 +5,7 @@ * @requires module:modules/userId */ -import {submodule} from '../src/hook.js'; +import { submodule } from '../src/hook.js'; import { logError, logMessage } from '../src/utils.js'; /** @@ -89,7 +89,7 @@ export const oneKeyIdSubmodule = { atype: 1, getEidExt: function(data) { if (data && data.preferences) { - return {preferences: data.preferences}; + return { preferences: data.preferences }; } }, getUidExt: function(data) { diff --git a/modules/onetagBidAdapter.js b/modules/onetagBidAdapter.js index d919d1398b7..3d70c233805 100644 --- a/modules/onetagBidAdapter.js +++ b/modules/onetagBidAdapter.js @@ -9,6 +9,7 @@ import { deepClone, logError, deepAccess, getWinDimensions } from '../src/utils. import { getBoundingClientRect } from '../libraries/boundingClientRect/boundingClientRect.js'; import { toOrtbNativeRequest } from '../src/native.js'; import { getConnectionInfo } from '../libraries/connectionInfo/connectionUtils.js'; +import { getAdUnitElement } from '../src/utils/adUnits.js'; /** * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest @@ -145,7 +146,7 @@ function buildRequests(validBidRequests, bidderRequest) { const connection = getConnectionInfo(); payload.networkConnectionType = connection?.type || null; payload.networkEffectiveConnectionType = connection?.effectiveType || null; - payload.fledgeEnabled = Boolean(bidderRequest?.paapi?.enabled) + payload.fledgeEnabled = false; return { method: 'POST', url: ENDPOINT, @@ -160,7 +161,7 @@ function interpretResponse(serverResponse, bidderRequest) { if (!body || (body.nobid && body.nobid === true)) { return bids; } - if (!body.fledgeAuctionConfigs && (!body.bids || !Array.isArray(body.bids) || body.bids.length === 0)) { + if (!body.bids || !Array.isArray(body.bids) || body.bids.length === 0) { return bids; } Array.isArray(body.bids) && body.bids.forEach(bid => { @@ -206,15 +207,7 @@ function interpretResponse(serverResponse, bidderRequest) { bids.push(responseBid); }); - if (body.fledgeAuctionConfigs && Array.isArray(body.fledgeAuctionConfigs)) { - const fledgeAuctionConfigs = body.fledgeAuctionConfigs - return { - bids, - paapi: fledgeAuctionConfigs - } - } else { - return bids; - } + return bids; } function createRenderer(bid, rendererOptions = {}) { @@ -366,14 +359,14 @@ function setGeneralInfo(bidRequest) { if (params.dealId) { this['dealId'] = params.dealId; } - const coords = getSpaceCoords(bidRequest.adUnitCode); + const coords = getSpaceCoords(bidRequest); if (coords) { this['coords'] = coords; } } -function getSpaceCoords(id) { - const space = document.getElementById(id); +function getSpaceCoords(bidRequest) { + const space = getAdUnitElement(bidRequest); try { const { top, left, width, height } = getBoundingClientRect(space); let window = space.ownerDocument.defaultView; diff --git a/modules/onomagicBidAdapter.js b/modules/onomagicBidAdapter.js index c3176d7abcc..1dade3668ea 100644 --- a/modules/onomagicBidAdapter.js +++ b/modules/onomagicBidAdapter.js @@ -7,11 +7,12 @@ import { logError, logWarn } from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER} from '../src/mediaTypes.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER } from '../src/mediaTypes.js'; import { percentInView } from '../libraries/percentInView/percentInView.js'; -import {getMinSize} from '../libraries/sizeUtils/sizeUtils.js'; -import {getBidFloor, isIframe} from '../libraries/omsUtils/index.js'; +import { getMinSize } from '../libraries/sizeUtils/sizeUtils.js'; +import { getBidFloor, isIframe } from '../libraries/omsUtils/index.js'; +import { getAdUnitElement } from '../src/utils/adUnits.js'; const BIDDER_CODE = 'onomagic'; const URL = 'https://bidder.onomagic.com/hb'; @@ -37,9 +38,9 @@ function buildRequests(bidReqs, bidderRequest) { let bidSizes = (bid.mediaTypes && bid.mediaTypes.banner && bid.mediaTypes.banner.sizes) || bid.sizes; bidSizes = ((isArray(bidSizes) && isArray(bidSizes[0])) ? bidSizes : [bidSizes]); bidSizes = bidSizes.filter(size => isArray(size)); - const processedSizes = bidSizes.map(size => ({w: parseInt(size[0], 10), h: parseInt(size[1], 10)})); + const processedSizes = bidSizes.map(size => ({ w: parseInt(size[0], 10), h: parseInt(size[1], 10) })); - const element = document.getElementById(bid.adUnitCode); + const element = getAdUnitElement(bid); const minSize = getMinSize(processedSizes); const viewabilityAmount = _isViewabilityMeasurable(element) ? _getViewability(element, getWindowTop(), minSize) @@ -85,10 +86,10 @@ function buildRequests(bidReqs, bidderRequest) { method: 'POST', url: URL, data: JSON.stringify(onomagicBidReq), - options: {contentType: 'text/plain', withCredentials: false} + options: { contentType: 'text/plain', withCredentials: false } }; } catch (e) { - logError(e, {bidReqs, bidderRequest}); + logError(e, { bidReqs, bidderRequest }); } } @@ -109,7 +110,7 @@ function interpretResponse(serverResponse) { logWarn('Onomagic server returned empty/non-json response: ' + JSON.stringify(serverResponse.body)); return []; } - const { body: {id, seatbid} } = serverResponse; + const { body: { id, seatbid } } = serverResponse; try { const onomagicBidResponses = []; if (id && @@ -137,7 +138,7 @@ function interpretResponse(serverResponse) { } return onomagicBidResponses; } catch (e) { - logError(e, {id, seatbid}); + logError(e, { id, seatbid }); } } diff --git a/modules/opaMarketplaceBidAdapter.js b/modules/opaMarketplaceBidAdapter.js index c3948a93cf1..858605ea17d 100644 --- a/modules/opaMarketplaceBidAdapter.js +++ b/modules/opaMarketplaceBidAdapter.js @@ -1,6 +1,6 @@ -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER, VIDEO} from '../src/mediaTypes.js'; -import {getStorageManager} from '../src/storageManager.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; +import { getStorageManager } from '../src/storageManager.js'; import { isBidRequestValid, onBidWon, @@ -12,14 +12,14 @@ import { const DEFAULT_SUB_DOMAIN = 'exchange'; const BIDDER_CODE = 'opamarketplace'; const BIDDER_VERSION = '1.0.0'; -export const storage = getStorageManager({bidderCode: BIDDER_CODE}); +export const storage = getStorageManager({ bidderCode: BIDDER_CODE }); export function createDomain(subDomain = DEFAULT_SUB_DOMAIN) { return `https://${subDomain}.opamarketplace.com`; } function createUniqueRequestData(hashUrl, bid) { - const {auctionId, transactionId} = bid; + const { auctionId, transactionId } = bid; return { auctionId, transactionId diff --git a/modules/open8BidAdapter.js b/modules/open8BidAdapter.js index 49523926c0e..56dc246ace0 100644 --- a/modules/open8BidAdapter.js +++ b/modules/open8BidAdapter.js @@ -1,9 +1,9 @@ import { Renderer } from '../src/Renderer.js'; -import {ajax} from '../src/ajax.js'; -import {createTrackPixelHtml, getBidIdParameter, logError, logWarn} from '../src/utils.js'; +import { ajax } from '../src/ajax.js'; +import { createTrackPixelHtml, getBidIdParameter, logError, logWarn } from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { VIDEO, BANNER } from '../src/mediaTypes.js'; -import {tryAppendQueryString} from '../libraries/urlUtils/urlUtils.js'; +import { tryAppendQueryString } from '../libraries/urlUtils/urlUtils.js'; const BIDDER_CODE = 'open8'; const URL = 'https://as.vt.open8.com/v1/control/prebid'; diff --git a/modules/openPairIdSystem.js b/modules/openPairIdSystem.js index 81f28734d2e..a4407dd0363 100644 --- a/modules/openPairIdSystem.js +++ b/modules/openPairIdSystem.js @@ -5,11 +5,11 @@ * @requires module:modules/userId */ -import {submodule} from '../src/hook.js'; -import {getStorageManager} from '../src/storageManager.js' -import {logInfo} from '../src/utils.js'; -import {MODULE_TYPE_UID} from '../src/activities/modules.js'; -import {VENDORLESS_GVLID} from '../src/consentHandler.js'; +import { submodule } from '../src/hook.js'; +import { getStorageManager } from '../src/storageManager.js' +import { logInfo } from '../src/utils.js'; +import { MODULE_TYPE_UID } from '../src/activities/modules.js'; +import { VENDORLESS_GVLID } from '../src/consentHandler.js'; /** * @typedef {import('../modules/userId/index.js').Submodule} Submodule @@ -27,7 +27,7 @@ const DEFAULT_SOURCE = 'pair-protocol.com'; const MATCH_METHOD = 3; -export const storage = getStorageManager({moduleType: MODULE_TYPE_UID, moduleName: MODULE_NAME}); +export const storage = getStorageManager({ moduleType: MODULE_TYPE_UID, moduleName: MODULE_NAME }); function publisherIdFromLocalStorage(key) { return storage.localStorageIsEnabled() ? storage.getDataFromLocalStorage(key) : null; @@ -56,7 +56,7 @@ export const openPairIdSubmodule = { * @returns {{pairId:string} | undefined } */ decode(value) { - return value && Array.isArray(value) ? {'openPairId': value} : undefined; + return value && Array.isArray(value) ? { 'openPairId': value } : undefined; }, /** * Performs action to obtain ID and return a value in the callback's response argument. @@ -115,7 +115,7 @@ export const openPairIdSubmodule = { return undefined; } - return {'id': ids}; + return { 'id': ids }; }, eids: { openPairId: function(values, config = {}) { diff --git a/modules/openwebBidAdapter.js b/modules/openwebBidAdapter.js index 0dff314ce17..777c20d6f89 100644 --- a/modules/openwebBidAdapter.js +++ b/modules/openwebBidAdapter.js @@ -1,6 +1,6 @@ -import {logWarn} from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {makeBaseSpec} from '../libraries/riseUtils/index.js'; +import { logWarn } from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { makeBaseSpec } from '../libraries/riseUtils/index.js'; const BIDDER_CODE = 'openweb'; const BASE_URL = 'https://hb.openwebmp.com/'; diff --git a/modules/openxBidAdapter.js b/modules/openxBidAdapter.js index 2da04ad3e38..9627223b221 100644 --- a/modules/openxBidAdapter.js +++ b/modules/openxBidAdapter.js @@ -1,9 +1,9 @@ -import {config} from '../src/config.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; +import { config } from '../src/config.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; import * as utils from '../src/utils.js'; -import {mergeDeep} from '../src/utils.js'; -import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js'; -import {ortbConverter} from '../libraries/ortbConverter/converter.js'; +import { mergeDeep } from '../src/utils.js'; +import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; +import { ortbConverter } from '../libraries/ortbConverter/converter.js'; const bidderConfig = 'hb_pb_ortb'; const bidderVersion = '2.0'; @@ -28,7 +28,7 @@ const converter = ortbConverter({ ttl: 300, nativeRequest: { eventtrackers: [ - {event: 1, methods: [1, 2]}, + { event: 1, methods: [1, 2] }, ] } }, @@ -61,9 +61,7 @@ const converter = ortbConverter({ if (bid.params.coppa) { utils.deepSetValue(req, 'regs.coppa', 1); } - if (bid.params.doNotTrack) { - utils.deepSetValue(req, 'device.dnt', 1); - } + utils.deepSetValue(req, 'device.dnt', 0); if (bid.params.platform) { utils.deepSetValue(req, 'ext.platform', bid.params.platform); } @@ -89,7 +87,7 @@ const converter = ortbConverter({ }, response(buildResponse, bidResponses, ortbResponse, context) { // pass these from request to the responses for use in userSync - const {ortbRequest} = context; + const { ortbRequest } = context; if (ortbRequest.ext) { if (ortbRequest.ext.delDomain) { utils.deepSetValue(ortbResponse, 'ext.delDomain', ortbRequest.ext.delDomain); @@ -98,27 +96,7 @@ const converter = ortbConverter({ utils.deepSetValue(ortbResponse, 'ext.platform', ortbRequest.ext.platform); } } - const response = buildResponse(bidResponses, ortbResponse, context); - // TODO: we may want to standardize this and move fledge logic to ortbConverter - let fledgeAuctionConfigs = utils.deepAccess(ortbResponse, 'ext.fledge_auction_configs'); - if (fledgeAuctionConfigs) { - fledgeAuctionConfigs = Object.entries(fledgeAuctionConfigs).map(([bidId, cfg]) => { - return { - bidId, - config: mergeDeep(Object.assign({}, cfg), { - auctionSignals: { - ortb2Imp: context.impContext[bidId]?.imp, - }, - }), - } - }); - return { - bids: response.bids, - paapi: fledgeAuctionConfigs, - } - } else { - return response - } + return buildResponse(bidResponses, ortbResponse, context); }, overrides: { imp: { @@ -126,7 +104,7 @@ const converter = ortbConverter({ // enforce floors should always be in USD // TODO: does it make sense that request.cur can be any currency, but request.imp[].bidfloorcur must be USD? const floor = {}; - setBidFloor(floor, bidRequest, {...context, currency: 'USD'}); + setBidFloor(floor, bidRequest, { ...context, currency: 'USD' }); if (floor.bidfloorcur === 'USD') { Object.assign(imp, floor); } @@ -139,7 +117,7 @@ const converter = ortbConverter({ let videoParams = bidRequest.mediaTypes[VIDEO]; if (videoParams) { videoParams = Object.assign({}, videoParams, bidRequest.params.video); - bidRequest = {...bidRequest, mediaTypes: {[VIDEO]: videoParams}} + bidRequest = { ...bidRequest, mediaTypes: { [VIDEO]: videoParams } } } orig(imp, bidRequest, context); } @@ -165,7 +143,7 @@ function buildRequests(bidRequests, bidderRequest) { const videoRequests = bidRequests.filter(bidRequest => isVideoBidRequest(bidRequest)); const bannerAndNativeRequests = bidRequests.filter(bidRequest => isBannerBidRequest(bidRequest) || isNativeBidRequest(bidRequest)) // In case of multi-format bids remove `video` from mediaTypes as for video a separate bid request is built - .map(bid => ({...bid, mediaTypes: {...bid.mediaTypes, video: undefined}})); + .map(bid => ({ ...bid, mediaTypes: { ...bid.mediaTypes, video: undefined } })); const requests = bannerAndNativeRequests.length ? [createRequest(bannerAndNativeRequests, bidderRequest, null)] : []; videoRequests.forEach(bid => { @@ -178,7 +156,7 @@ function createRequest(bidRequests, bidderRequest, mediaType) { return { method: 'POST', url: config.getConfig('openxOrtbUrl') || REQUEST_URL, - data: converter.toORTB({bidRequests, bidderRequest, context: {mediaType}}) + data: converter.toORTB({ bidRequests, bidderRequest, context: { mediaType } }) } } @@ -197,9 +175,9 @@ function isBannerBidRequest(bidRequest) { function interpretResponse(resp, req) { if (!resp.body) { - resp.body = {nbr: 0}; + resp.body = { nbr: 0 }; } - return converter.fromORTB({request: req.data, response: resp.body}); + return converter.fromORTB({ request: req.data, response: resp.body }); } /** diff --git a/modules/operaadsBidAdapter.js b/modules/operaadsBidAdapter.js index 8645196d07d..4a8e22aaa6a 100644 --- a/modules/operaadsBidAdapter.js +++ b/modules/operaadsBidAdapter.js @@ -1,4 +1,3 @@ -import {getDNT} from '../libraries/dnt/index.js'; import { deepAccess, deepSetValue, @@ -11,12 +10,13 @@ import { logWarn, triggerPixel } from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {config} from '../src/config.js'; -import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js'; -import {Renderer} from '../src/Renderer.js'; -import {OUTSTREAM} from '../src/video.js'; -import {convertOrtbRequestToProprietaryNative} from '../src/native.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { config } from '../src/config.js'; +import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; +import { Renderer } from '../src/Renderer.js'; +import { OUTSTREAM } from '../src/video.js'; +import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; +import { getDNT } from '../libraries/dnt/index.js'; /** * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest @@ -709,7 +709,7 @@ function getUserId(bidRequest) { * @param {*} params.size * @returns {Object} floor price */ -function getBidFloor(bid, {mediaType = '*', size = '*'}) { +function getBidFloor(bid, { mediaType = '*', size = '*' }) { if (isFn(bid.getFloor)) { const floorInfo = bid.getFloor({ currency: DEFAULT_CURRENCY, diff --git a/modules/operaadsIdSystem.js b/modules/operaadsIdSystem.js index 1041237e13c..36c9f9a72d7 100644 --- a/modules/operaadsIdSystem.js +++ b/modules/operaadsIdSystem.js @@ -18,7 +18,7 @@ const ID_KEY = MODULE_NAME; const version = '1.0'; const SYNC_URL = 'https://t.adx.opera.com/identity/'; const AJAX_TIMEOUT = 300; -const AJAX_OPTIONS = {method: 'GET', withCredentials: true, contentType: 'application/json'}; +const AJAX_OPTIONS = { method: 'GET', withCredentials: true, contentType: 'application/json' }; function constructUrl(pairs) { const queries = []; diff --git a/modules/oprxBidAdapter.js b/modules/oprxBidAdapter.js index 242e95d282f..566232c4e92 100644 --- a/modules/oprxBidAdapter.js +++ b/modules/oprxBidAdapter.js @@ -51,7 +51,7 @@ const defaultConverter = ortbConverter({ }, imp(buildImp, bidRequest, context) { const imp = buildImp(bidRequest, context); - imp.ext = {bidder: bidRequest.params}; + imp.ext = { bidder: bidRequest.params }; if (bidRequest.params.bid_floor) { imp.bidfloor = bidRequest.params.bid_floor; } diff --git a/modules/opscoBidAdapter.js b/modules/opscoBidAdapter.js index 835387d9bc1..b331b9a212a 100644 --- a/modules/opscoBidAdapter.js +++ b/modules/opscoBidAdapter.js @@ -1,8 +1,8 @@ -import {deepAccess, deepSetValue, isArray, logInfo} from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER} from '../src/mediaTypes.js'; -import {ortbConverter} from '../libraries/ortbConverter/converter.js' -import {pbsExtensions} from '../libraries/pbsExtensions/pbsExtensions.js'; +import { deepAccess, deepSetValue, isArray, logInfo } from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER } from '../src/mediaTypes.js'; +import { ortbConverter } from '../libraries/ortbConverter/converter.js' +import { pbsExtensions } from '../libraries/pbsExtensions/pbsExtensions.js'; const ENDPOINT = 'https://exchange.ops.co/openrtb2/auction'; const BIDDER_CODE = 'opsco'; @@ -12,10 +12,10 @@ const DEFAULT_NET_REVENUE = true; const converter = ortbConverter({ request(buildRequest, imps, bidderRequest, context) { - const {bidRequests} = context; + const { bidRequests } = context; const data = buildRequest(imps, bidderRequest, context); - const {publisherId, siteId} = bidRequests[0].params; + const { publisherId, siteId } = bidRequests[0].params; data.site = data.site || {}; data.site.id = siteId; @@ -120,7 +120,7 @@ export const spec = { creativeId: bid.crid, netRevenue: DEFAULT_NET_REVENUE, currency: DEFAULT_CURRENCY, - meta: {advertiserDomains: bid?.adomain || []}, + meta: { advertiserDomains: bid?.adomain || [] }, mediaType: bid.mediaType || bid.mtype })) || []; @@ -144,7 +144,7 @@ export const spec = { syncDetails.forEach(syncDetail => { const type = syncDetail.type === 'iframe' ? 'iframe' : 'image'; if ((type === 'iframe' && syncOptions.iframeEnabled) || (type === 'image' && syncOptions.pixelEnabled)) { - syncs.push({type, url: syncDetail.url}); + syncs.push({ type, url: syncDetail.url }); } }); } diff --git a/modules/optableBidAdapter.js b/modules/optableBidAdapter.js deleted file mode 100644 index d2dae252e6c..00000000000 --- a/modules/optableBidAdapter.js +++ /dev/null @@ -1,67 +0,0 @@ -import * as utils from '../src/utils.js'; -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { config } from '../src/config.js'; -import { BANNER } from '../src/mediaTypes.js'; -import { ortbConverter } from '../libraries/ortbConverter/converter.js' -const converter = ortbConverter({ - context: { netRevenue: true, ttl: 300 }, - imp(buildImp, bidRequest, context) { - const imp = buildImp(bidRequest, context); - utils.mergeDeep(imp, { - tagid: bidRequest.params.site, - }); - return imp; - } -}); -const BIDDER_CODE = 'optable'; -const DEFAULT_REGION = 'ca' -const DEFAULT_ORIGIN = 'https://ads.optable.co' - -function getOrigin() { - return config.getConfig('optable.origin') ?? DEFAULT_ORIGIN; -} - -function getBaseUrl() { - const region = config.getConfig('optable.region') ?? DEFAULT_REGION; - return `${getOrigin()}/${region}` -} - -export const spec = { - code: BIDDER_CODE, - isBidRequestValid: function(bid) { return !!bid.params?.site }, - buildRequests: function(bidRequests, bidderRequest) { - const requestURL = `${getBaseUrl()}/ortb2/v1/ssp/bid` - const data = converter.toORTB({ bidRequests, bidderRequest, context: { mediaType: BANNER } }); - return { method: 'POST', url: requestURL, data } - }, - buildPAAPIConfigs: function(bidRequests) { - const origin = getOrigin(); - return bidRequests - .filter(req => req.ortb2Imp?.ext?.ae) - .map(bid => ({ - bidId: bid.bidId, - config: { - seller: origin, - decisionLogicURL: `${getBaseUrl()}/paapi/v1/ssp/decision-logic.js?origin=${bid.params.site}`, - interestGroupBuyers: [origin], - perBuyerMultiBidLimits: { - [origin]: 100 - }, - perBuyerCurrencies: { - [origin]: 'USD' - } - } - })) - }, - interpretResponse: function(response, request) { - const bids = converter.fromORTB({ response: response.body, request: request.data }).bids - const auctionConfigs = (response.body.ext?.optable?.fledge?.auctionconfigs ?? []).map((cfg) => { - const { impid, ...config } = cfg; - return { bidId: impid, config } - }) - - return { bids, paapi: auctionConfigs } - }, - supportedMediaTypes: [BANNER] -} -registerBidder(spec); diff --git a/modules/optableBidAdapter.md b/modules/optableBidAdapter.md deleted file mode 100644 index a7c4829fe63..00000000000 --- a/modules/optableBidAdapter.md +++ /dev/null @@ -1,41 +0,0 @@ -# Overview - -``` -Module Name: Optable Bidder Adapter -Module Type: Bidder Adapter -Maintainer: prebid@optable.co -``` - -# Description - -Module that connects to Optable's demand sources. - -# Bid Parameters -## Banner - -| Name | Scope | Type | Description | Example -| ---- | ----- | ---- | ----------- | ------- -| `site` | required | String | Optable site ID provided by your Optable representative. | "aaaaaaaa" - -## Video - -Not supported at the moment. - -# Example -```javascript -var adUnits = [ - { - code: 'test-div', - sizes: [[728, 90]], // a display size - mediaTypes: {'banner': {}}, - bids: [ - { - bidder: 'optable', - params: { - site: 'aaaaaaaa', - }, - }, - ], - }, -]; -``` diff --git a/modules/optableRtdProvider.js b/modules/optableRtdProvider.js index 29638ba3a94..290c0947ee6 100644 --- a/modules/optableRtdProvider.js +++ b/modules/optableRtdProvider.js @@ -1,13 +1,13 @@ -import {MODULE_TYPE_RTD} from '../src/activities/modules.js'; -import {loadExternalScript} from '../src/adloader.js'; -import {config} from '../src/config.js'; -import {submodule} from '../src/hook.js'; -import {deepAccess, mergeDeep, prefixLog} from '../src/utils.js'; +import { MODULE_TYPE_RTD } from '../src/activities/modules.js'; +import { loadExternalScript } from '../src/adloader.js'; +import { config } from '../src/config.js'; +import { submodule } from '../src/hook.js'; +import { deepAccess, mergeDeep, prefixLog } from '../src/utils.js'; const MODULE_NAME = 'optable'; export const LOG_PREFIX = `[${MODULE_NAME} RTD]:`; const optableLog = prefixLog(LOG_PREFIX); -const {logMessage, logWarn, logError} = optableLog; +const { logMessage, logWarn, logError } = optableLog; /** * Extracts the parameters for Optable RTD module from the config object passed at instantiation @@ -27,15 +27,15 @@ export const parseConfig = (moduleConfig) => { // Verify that bundleUrl is a valid URL: only secure (HTTPS) URLs are allowed if (typeof bundleUrl === 'string' && bundleUrl.length && !bundleUrl.startsWith('https://')) { logError('Invalid URL format for bundleUrl in moduleConfig. Only HTTPS URLs are allowed.'); - return {bundleUrl: null, adserverTargeting, handleRtd: null}; + return { bundleUrl: null, adserverTargeting, handleRtd: null }; } if (handleRtd && typeof handleRtd !== 'function') { logError('handleRtd must be a function'); - return {bundleUrl, adserverTargeting, handleRtd: null}; + return { bundleUrl, adserverTargeting, handleRtd: null }; } - const result = {bundleUrl, adserverTargeting, handleRtd}; + const result = { bundleUrl, adserverTargeting, handleRtd }; if (instance !== null) { result.instance = instance; } @@ -118,7 +118,7 @@ export const mergeOptableData = async (handleRtdFn, reqBidsConfigObj, optableExt export const getBidRequestData = (reqBidsConfigObj, callback, moduleConfig, userConsent) => { try { // Extract the bundle URL from the module configuration - const {bundleUrl, handleRtd} = parseConfig(moduleConfig); + const { bundleUrl, handleRtd } = parseConfig(moduleConfig); const handleRtdFn = handleRtd || defaultHandleRtd; const optableExtraData = config.getConfig('optableRtdConfig') || {}; @@ -162,7 +162,7 @@ export const getBidRequestData = (reqBidsConfigObj, callback, moduleConfig, user */ export const getTargetingData = (adUnits, moduleConfig, userConsent, auction) => { // Extract `adserverTargeting` and `instance` from the module configuration - const {adserverTargeting, instance} = parseConfig(moduleConfig); + const { adserverTargeting, instance } = parseConfig(moduleConfig); logMessage('Ad Server targeting: ', adserverTargeting); if (!adserverTargeting) { diff --git a/modules/optidigitalBidAdapter.js b/modules/optidigitalBidAdapter.js index 1330bf2054f..b32a0495c8e 100755 --- a/modules/optidigitalBidAdapter.js +++ b/modules/optidigitalBidAdapter.js @@ -1,7 +1,7 @@ -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER} from '../src/mediaTypes.js'; -import {deepAccess, isPlainObject, parseSizesInput} from '../src/utils.js'; -import {getAdUnitSizes} from '../libraries/sizeUtils/sizeUtils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER } from '../src/mediaTypes.js'; +import { deepAccess, isPlainObject, parseSizesInput } from '../src/utils.js'; +import { getAdUnitSizes } from '../libraries/sizeUtils/sizeUtils.js'; /** * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest @@ -63,7 +63,8 @@ export const spec = { imp: validBidRequests.map(bidRequest => buildImp(bidRequest, ortb2)), badv: ortb2.badv || deepAccess(validBidRequests[0], 'params.badv') || [], bcat: ortb2.bcat || deepAccess(validBidRequests[0], 'params.bcat') || [], - bapp: deepAccess(validBidRequests[0], 'params.bapp') || [] + bapp: deepAccess(validBidRequests[0], 'params.bapp') || [], + device: ortb2.device || {} } if (validBidRequests[0].auctionId) { @@ -82,10 +83,14 @@ export const spec = { const gdpr = deepAccess(bidderRequest, 'gdprConsent'); if (bidderRequest && gdpr) { const isConsentString = typeof gdpr.consentString === 'string'; + const isGdprApplies = typeof gdpr.gdprApplies === 'boolean'; payload.gdpr = { consent: isConsentString ? gdpr.consentString : '', - required: true + required: isGdprApplies ? gdpr.gdprApplies : false }; + if (gdpr?.addtlConsent) { + payload.gdpr.addtlConsent = gdpr.addtlConsent; + } } if (bidderRequest && !gdpr) { payload.gdpr = { @@ -120,10 +125,16 @@ export const spec = { } } + const ortb2SiteKeywords = (bidderRequest?.ortb2?.site?.keywords || '')?.split(',').map(k => k.trim()).filter(k => k !== '').join(','); + if (ortb2SiteKeywords) { + payload.site = payload.site || {}; + payload.site.keywords = ortb2SiteKeywords; + } + const payloadObject = JSON.stringify(payload); return { method: 'POST', - url: ENDPOINT_URL, + url: `${ENDPOINT_URL}/${payload.publisherId}`, data: payloadObject }; }, @@ -230,6 +241,11 @@ function buildImp(bidRequest, ortb2) { imp.battr = battr; } + const gpid = deepAccess(bidRequest, 'ortb2Imp.ext.gpid'); + if (gpid) { + imp.gpid = gpid; + } + return imp; } diff --git a/modules/optoutBidAdapter.js b/modules/optoutBidAdapter.js index 03cd420b0be..f338983b584 100644 --- a/modules/optoutBidAdapter.js +++ b/modules/optoutBidAdapter.js @@ -1,78 +1,314 @@ -import { deepAccess } from '../src/utils.js'; -import {config} from '../src/config.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {hasPurpose1Consent} from '../src/utils/gdpr.js'; +import { deepAccess, logWarn } from '../src/utils.js'; +import { config } from '../src/config.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { hasPurpose1Consent } from '../src/utils/gdpr.js'; +import { BANNER } from '../src/mediaTypes.js'; const BIDDER_CODE = 'optout'; const GVLID = 227; +const DEFAULT_TTL = 300; +const DEFAULT_CURRENCY = 'EUR'; +/** + * Sanitizes a URL by removing query parameters and fragments to prevent data leakage + * @param {string} rawUrl - The URL to sanitize + * @returns {string} Sanitized URL (origin + pathname) or empty string if invalid + */ +function sanitizeUrl(rawUrl) { + if (!rawUrl) return ''; + try { + const u = new URL(rawUrl, deepAccess(window, 'location.href')); + // Avoid leaking query params / fragments + const sanitized = `${u.origin}${u.pathname}`; + // Ensure we never return null or undefined as a string + return sanitized && sanitized !== 'null' && sanitized !== 'undefined' ? sanitized : ''; + } catch (e) { + // If it's not a valid URL, return an empty string to avoid leaking potentially sensitive data + logWarn(`${BIDDER_CODE}: Invalid URL provided: ${rawUrl}`); + return ''; + } +} + +/** + * Gets the domain/URL from bidderRequest with fallbacks + * Priority: canonicalUrl > page > window.location.href + * @param {Object} bidderRequest - The bidder request object + * @returns {string} Sanitized domain URL + */ function getDomain(bidderRequest) { - return deepAccess(bidderRequest, 'refererInfo.canonicalUrl') || deepAccess(window, 'location.href'); + const fromCanonical = deepAccess(bidderRequest, 'refererInfo.canonicalUrl'); + if (fromCanonical) return sanitizeUrl(fromCanonical); + + const fromPage = deepAccess(bidderRequest, 'refererInfo.page'); + if (fromPage) return sanitizeUrl(fromPage); + + const href = deepAccess(window, 'location.href'); + return sanitizeUrl(href); } +/** + * Gets currency configuration from Prebid config + * @returns {Object} Currency config object with adServerCurrency and granularityMultiplier + */ function getCurrency() { - let cur = config.getConfig('currency'); - if (cur === undefined) { - cur = { - adServerCurrency: 'EUR', - granularityMultiplier: 1 - }; + const cur = config.getConfig('currency'); + if (!cur) { + return { adServerCurrency: DEFAULT_CURRENCY, granularityMultiplier: 1 }; } return cur; } +/** + * Normalize customs: + * - arrays -> CSV string + * - null/undefined -> removed + * - objects -> JSON string (removed if not serializable / circular) + * - primitives -> String(value) + * + * Returns a new object (never mutates input). + */ +function normalizeCustoms(input) { + if (!input || typeof input !== 'object' || Array.isArray(input)) { + return {}; + } + + const out = Object.assign({}, input); + + Object.entries(out).forEach(([key, value]) => { + if (Array.isArray(value)) { + out[key] = value.join(','); + return; + } + + if (value === null || value === undefined) { + delete out[key]; + return; + } + + if (typeof value === 'object') { + try { + const str = JSON.stringify(value); + if (str === undefined) { + delete out[key]; + } else { + out[key] = str; + } + } catch (e) { + // e.g. circular structure + delete out[key]; + } + return; + } + + out[key] = String(value); + }); + + return out; +} + export const spec = { code: BIDDER_CODE, gvlid: GVLID, + supportedMediaTypes: [BANNER], - isBidRequestValid: function(bid) { - return !!bid.params.publisher && !!bid.params.adslot; + /** + * Determines if a bid request is valid + * @param {Object} bid - The bid to validate + * @returns {boolean} True if valid, false otherwise + */ + isBidRequestValid: function (bid) { + const params = bid && bid.params; + const adSlot = params && (params.adSlot || params.adslot); + return !!(params && params.publisher && adSlot); }, - buildRequests: function(validBidRequests, bidderRequest) { - return validBidRequests.map(bidRequest => { - let endPoint = 'https://adscience-nocookie.nl/prebid/display'; - let consentString = ''; - let gdpr = 0; - if (bidRequest.gdprConsent) { - gdpr = (typeof bidRequest.gdprConsent.gdprApplies === 'boolean') ? Number(bidRequest.gdprConsent.gdprApplies) : 0; - consentString = bidRequest.gdprConsent.consentString; - if (!gdpr || hasPurpose1Consent(bidRequest.gdprConsent)) { - endPoint = 'https://prebid.adscience.nl/prebid/display'; - } + /** + * Builds bid requests from valid bid requests + * @param {Array} validBidRequests - Array of valid bid requests + * @param {Object} bidderRequest - The bidder request object + * @returns {Array} Array containing the bid request object + */ + buildRequests: function (validBidRequests, bidderRequest) { + if (!Array.isArray(validBidRequests) || validBidRequests.length === 0) { + return []; + } + + const firstBid = validBidRequests[0]; + + let endPoint = 'https://prebid.optoutadserving.com/prebid/display'; + + const gdprConsent = + bidderRequest && typeof bidderRequest === 'object' ? bidderRequest.gdprConsent : null; + + let consentString = ''; + let gdpr = 0; + + if (gdprConsent && typeof gdprConsent === 'object') { + gdpr = + typeof gdprConsent.gdprApplies === 'boolean' + ? Number(gdprConsent.gdprApplies) + : 0; + + consentString = gdprConsent.consentString || ''; + + if (!gdpr || hasPurpose1Consent(gdprConsent)) { + endPoint = 'https://prebid.optinadserving.com/prebid/display'; } - return { - method: 'POST', - url: endPoint, - data: { - requestId: bidRequest.bidId, - publisher: bidRequest.params.publisher, - adSlot: bidRequest.params.adslot, - cur: getCurrency(), - url: getDomain(bidRequest), - ortb2: bidderRequest.ortb2, - consent: consentString, - gdpr: gdpr - - }, + } + + const shouldIncludeOrtb2 = validBidRequests.some((b) => !!b?.params?.includeOrtb2); + + const slots = validBidRequests.map((b) => { + const slotCustoms = normalizeCustoms(b?.params?.customs); + const adSlotValue = b.params.adSlot || b.params.adslot; + + const slot = { + adSlot: adSlotValue, + requestId: b.bidId, }; + + // Use explicit id if provided, otherwise use adSlot value + if (b.params && b.params.id != null) { + slot.id = String(b.params.id); + } else { + slot.id = adSlotValue; + } + + if (Object.keys(slotCustoms).length) slot.customs = slotCustoms; + return slot; }); + + const mergedCustoms = Object.assign( + {}, + firstBid?.params?.customs, + bidderRequest?.ortb2?.ext?.data, + bidderRequest?.ortb2?.site?.ext?.data, + bidderRequest?.ortb2?.app?.ext?.data, + bidderRequest?.ortb2?.user?.ext?.data + ); + + const customs = normalizeCustoms(mergedCustoms); + + const data = { + publisher: firstBid.params.publisher, // intentionally uses the first bid's publisher when batching + slots, + cur: getCurrency(), + url: getDomain(bidderRequest), + sdk_version: 'prebid', + consent: consentString, + gdpr + }; + + if (Object.keys(customs).length) data.customs = customs; + + if (shouldIncludeOrtb2 && bidderRequest?.ortb2) { + data.ortb2 = JSON.stringify(bidderRequest.ortb2); + } + + return [ + { + method: 'POST', + url: endPoint, + data + } + ]; }, + /** + * Interprets the server response and returns valid bids + * @param {Object} serverResponse - The server response object + * @param {Object} bidRequest - The original bid request + * @returns {Array} Array of valid bid objects + */ interpretResponse: function (serverResponse, bidRequest) { - return serverResponse.body; + const body = serverResponse?.body; + + const bids = Array.isArray(body) + ? body + : Array.isArray(body?.bids) + ? body.bids + : []; + + const sentSlots = bidRequest?.data?.slots || []; + + const slotIdToPrebidId = new Map( + sentSlots.map((s) => [String(s.id), String(s.requestId)]) + ); + + const prebidIds = new Set(sentSlots.map((s) => String(s.requestId))); + + return bids + .map((bid) => { + // Defensive handling of malformed bids + if (!bid || !bid.requestId) return null; + if (!bid.currency || !bid.ad || bid.width == null || bid.height == null) return null; + + const cpmNum = Number(bid.cpm); + if (bid.cpm == null || Number.isNaN(cpmNum) || cpmNum <= 0) return null; + + const w = Number(bid.width); + const h = Number(bid.height); + if (!Number.isFinite(w) || w <= 0 || !Number.isFinite(h) || h <= 0) return null; + + const serverSlotOrReq = String(bid.requestId); + + const prebidRequestId = prebidIds.has(serverSlotOrReq) + ? serverSlotOrReq + : slotIdToPrebidId.get(serverSlotOrReq); + + if (!prebidRequestId) return null; + + return { + requestId: prebidRequestId, + cpm: cpmNum, + currency: bid.currency, + width: w, + height: h, + ad: bid.ad, + ttl: Number(bid.ttl) || DEFAULT_TTL, + creativeId: bid.creativeId || String(bid.requestId), + netRevenue: true, + optOutExt: bid.optOutExt, + meta: bid.meta + }; + }) + .filter(Boolean); }, + /** + * Returns user sync pixels/iframes based on consent + * @param {Object} syncOptions - Sync options from Prebid + * @param {Array} responses - Server responses + * @param {Object} gdprConsent - GDPR consent data + * @returns {Array} Array of user sync objects + */ getUserSyncs: function (syncOptions, responses, gdprConsent) { - if (gdprConsent) { - const gdpr = (typeof gdprConsent.gdprApplies === 'boolean') ? Number(gdprConsent.gdprApplies) : 0; - if (syncOptions.iframeEnabled && (!gdprConsent.gdprApplies || hasPurpose1Consent(gdprConsent))) { - return [{ + if (!gdprConsent || typeof gdprConsent !== 'object') return []; + + const gdprApplies = typeof gdprConsent.gdprApplies === 'boolean' + ? gdprConsent.gdprApplies + : false; + + const gdpr = gdprApplies ? 1 : 0; + + if ( + syncOptions.iframeEnabled && + (!gdprApplies || hasPurpose1Consent(gdprConsent)) + ) { + return [ + { type: 'iframe', - url: 'https://umframe.adscience.nl/matching/iframe?gdpr=' + gdpr + '&gdpr_consent=' + gdprConsent.consentString - }]; - } + url: + 'https://umframe.optinadserving.com/matching/iframe?gdpr=' + + gdpr + + '&gdpr_consent=' + + encodeURIComponent(gdprConsent.consentString || '') + } + ]; } - }, + + return []; + } }; + registerBidder(spec); diff --git a/modules/orbitsoftBidAdapter.js b/modules/orbitsoftBidAdapter.js index 7ae2195294d..94648f92032 100644 --- a/modules/orbitsoftBidAdapter.js +++ b/modules/orbitsoftBidAdapter.js @@ -1,6 +1,6 @@ import * as utils from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {getBidIdParameter} from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { getBidIdParameter } from '../src/utils.js'; const BIDDER_CODE = 'orbitsoft'; const styleParamsMap = { @@ -98,7 +98,7 @@ export const spec = { method: 'POST', url: requestUrl, data: requestData, - options: {withCredentials: false}, + options: { withCredentials: false }, bidRequest: bidRequest }); } diff --git a/modules/otmBidAdapter.js b/modules/otmBidAdapter.js index 7d4049e3054..4f9928b8323 100644 --- a/modules/otmBidAdapter.js +++ b/modules/otmBidAdapter.js @@ -20,7 +20,7 @@ export const spec = { code: BIDDER_CODE, url: OTM_BID_URL, - supportedMediaTypes: [ BANNER ], + supportedMediaTypes: [BANNER], /** * Determines whether or not the given bid request is valid. diff --git a/modules/outbrainBidAdapter.js b/modules/outbrainBidAdapter.js index f04ce0886d8..1b08f326c32 100644 --- a/modules/outbrainBidAdapter.js +++ b/modules/outbrainBidAdapter.js @@ -1,15 +1,15 @@ // jshint esversion: 6, es3: false, node: true 'use strict'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; import { getStorageManager } from '../src/storageManager.js'; -import {OUTSTREAM} from '../src/video.js'; -import {_map, deepAccess, deepSetValue, logWarn, replaceAuctionPrice, setOnAny, parseGPTSingleSizeArrayToRtbSize, isPlainObject} from '../src/utils.js'; -import {ajax} from '../src/ajax.js'; -import {config} from '../src/config.js'; -import {convertOrtbRequestToProprietaryNative} from '../src/native.js'; -import {Renderer} from '../src/Renderer.js'; +import { OUTSTREAM } from '../src/video.js'; +import { _map, deepAccess, deepSetValue, logWarn, replaceAuctionPrice, setOnAny, parseGPTSingleSizeArrayToRtbSize, isPlainObject } from '../src/utils.js'; +import { ajax } from '../src/ajax.js'; +import { config } from '../src/config.js'; +import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; +import { Renderer } from '../src/Renderer.js'; const BIDDER_CODE = 'outbrain'; const GVLID = 164; @@ -29,12 +29,12 @@ const NATIVE_ASSET_IDS = Object.entries(NATIVE_PARAMS).reduce((acc, [key, value] const OUTSTREAM_RENDERER_URL = 'https://acdn.adnxs.com/video/outstream/ANOutstreamVideo.js'; const OB_USER_TOKEN_KEY = 'OB-USER-TOKEN'; -export const storage = getStorageManager({bidderCode: BIDDER_CODE}); +export const storage = getStorageManager({ bidderCode: BIDDER_CODE }); export const spec = { code: BIDDER_CODE, gvlid: GVLID, - supportedMediaTypes: [ NATIVE, BANNER, VIDEO ], + supportedMediaTypes: [NATIVE, BANNER, VIDEO], isBidRequestValid: (bid) => { if (typeof bid.params !== 'object') { return false; diff --git a/modules/oxxionAnalyticsAdapter.js b/modules/oxxionAnalyticsAdapter.js index 63776f43a40..223634d28f0 100644 --- a/modules/oxxionAnalyticsAdapter.js +++ b/modules/oxxionAnalyticsAdapter.js @@ -196,7 +196,7 @@ function handleBidWon(args) { args['cpmIncrement'] = increment; args['referer'] = encodeURIComponent(getRefererInfo().page || getRefererInfo().topmostLocation); if (typeof saveEvents.bidRequested === 'object' && saveEvents.bidRequested.length > 0 && saveEvents.bidRequested[0].gdprConsent) { args.gdpr = saveEvents.bidRequested[0].gdprConsent; } - ajax(endpoint + '.oxxion.io/analytics/bid_won', null, JSON.stringify(args), {method: 'POST', withCredentials: true}); + ajax(endpoint + '.oxxion.io/analytics/bid_won', null, JSON.stringify(args), { method: 'POST', withCredentials: true }); } function handleAuctionEnd() { @@ -208,16 +208,16 @@ function handleAuctionEnd() { const tmpId = bidResponse['originalBidder'] + '_' + bidResponse['creativeId']; if (list.includes(tmpId) && !alreadyCalled.includes(tmpId)) { alreadyCalled.push(tmpId); - ajax(endpoint + '.oxxion.io/analytics/creatives', null, JSON.stringify(bidResponse), {method: 'POST', withCredentials: true}); + ajax(endpoint + '.oxxion.io/analytics/creatives', null, JSON.stringify(bidResponse), { method: 'POST', withCredentials: true }); } }); } allEvents = {}; - }, JSON.stringify(auctionEnd), {method: 'POST', withCredentials: true}); + }, JSON.stringify(auctionEnd), { method: 'POST', withCredentials: true }); auctionEnd = {}; } -const oxxionAnalytics = Object.assign(adapter({url, analyticsType}), { +const oxxionAnalytics = Object.assign(adapter({ url, analyticsType }), { track({ eventType, args diff --git a/modules/oxxionRtdProvider.js b/modules/oxxionRtdProvider.js index 238a1f4d6eb..a522d507f27 100644 --- a/modules/oxxionRtdProvider.js +++ b/modules/oxxionRtdProvider.js @@ -46,7 +46,7 @@ function getAdUnits(reqBidsConfigObj, callback, config, userConsent) { [reqBidsConfigObj.adUnits, filteredBids] = getFilteredAdUnitsOnBidRates(bidsRateInterests, reqBidsConfigObj.adUnits, config.params, true); } if (filteredBids.length > 0) { - getPromisifiedAjax('https://' + config.params.domain + '.oxxion.io/analytics/request_rejecteds', JSON.stringify({'bids': filteredBids, 'gdpr': gdpr}), { + getPromisifiedAjax('https://' + config.params.domain + '.oxxion.io/analytics/request_rejecteds', JSON.stringify({ 'bids': filteredBids, 'gdpr': gdpr }), { method: 'POST', withCredentials: true }); @@ -55,7 +55,7 @@ function getAdUnits(reqBidsConfigObj, callback, config, userConsent) { const timeToRun = new Date() - moduleStarted; logInfo(LOG_PREFIX + ' time to run: ' + timeToRun); if (getRandomNumber(50) === 1) { - ajax('https://' + config.params.domain + '.oxxion.io/ova/time', null, JSON.stringify({'duration': timeToRun, 'auctionId': reqBidsConfigObj.auctionId}), {method: 'POST', withCredentials: true}); + ajax('https://' + config.params.domain + '.oxxion.io/ova/time', null, JSON.stringify({ 'duration': timeToRun, 'auctionId': reqBidsConfigObj.auctionId }), { method: 'POST', withCredentials: true }); } }).catch(error => logError(LOG_PREFIX, 'bidInterestError', error)); } diff --git a/modules/ozoneBidAdapter.js b/modules/ozoneBidAdapter.js index e804eef164e..ee631309610 100644 --- a/modules/ozoneBidAdapter.js +++ b/modules/ozoneBidAdapter.js @@ -1,21 +1,24 @@ import { - logInfo, - logError, deepAccess, - logWarn, + deepClone, deepSetValue, + generateUUID, + getBidIdParameter, isArray, + logError, + logInfo, + logWarn, mergeDeep, - parseUrl, - generateUUID, isInteger, deepClone, getBidIdParameter + parseUrl } from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; -import {config} from '../src/config.js'; -import {getPriceBucketString} from '../src/cpmBucketManager.js'; +import { config } from '../src/config.js'; +import { getPriceBucketString } from '../src/cpmBucketManager.js'; import { Renderer } from '../src/Renderer.js'; -import {getRefererInfo} from '../src/refererDetection.js'; -import {toOrtb25} from '../libraries/ortb2.5Translator/translator.js'; +import { getRefererInfo } from '../src/refererDetection.js'; +import { toOrtb25 } from '../libraries/ortb2.5Translator/translator.js'; + const BIDDER_CODE = 'ozone'; const ORIGIN = 'https://elb.the-ozone-project.com'; const AUCTIONURI = '/openrtb2/auction'; @@ -28,8 +31,8 @@ export const spec = { version: OZONEVERSION, code: BIDDER_CODE, supportedMediaTypes: [VIDEO, BANNER], - cookieSyncBag: {publisherId: null, siteId: null, userIdObject: {}}, - propertyBag: {pageId: null, buildRequestsStart: 0, buildRequestsEnd: 0}, + cookieSyncBag: { publisherId: null, siteId: null, userIdObject: {} }, + propertyBag: { pageId: null, buildRequestsStart: 0, buildRequestsEnd: 0 }, getAuctionUrl() { const ep = config.getConfig('ozone.endpointOverride') || {}; if (ep.auctionUrl) return ep.auctionUrl; @@ -137,8 +140,7 @@ export const spec = { if (this.blockTheRequest()) { return []; } - const fledgeEnabled = !!bidderRequest.fledgeEnabled; - let htmlParams = {'publisherId': '', 'siteId': ''}; + let htmlParams = { 'publisherId': '', 'siteId': '' }; if (validBidRequests.length > 0) { Object.assign(this.cookieSyncBag.userIdObject, this.findAllUserIdsFromEids(validBidRequests[0])); this.cookieSyncBag.siteId = deepAccess(validBidRequests[0], 'params.siteId'); @@ -148,9 +150,9 @@ export const spec = { logInfo('cookie sync bag', this.cookieSyncBag); let singleRequest = config.getConfig('ozone.singleRequest'); singleRequest = singleRequest !== false; - const ozoneRequest = {site: {}, regs: {}, user: {}}; + const ozoneRequest = { site: {}, regs: {}, user: {} }; const fpd = deepAccess(bidderRequest, 'ortb2', {}); - const fpdPruned = this.pruneToExtPaths(fpd, {maxTestDepth: 2}); + const fpdPruned = this.pruneToExtPaths(fpd, { maxTestDepth: 2 }); logInfo('got ortb2 fpd: ', fpd); logInfo('got ortb2 fpdPruned: ', fpdPruned); logInfo('going to assign the pruned (ext only) FPD ortb2 object to ozoneRequest, wholesale'); @@ -159,7 +161,7 @@ export const spec = { const getParams = this.getGetParametersAsObject(); const wlOztestmodeKey = 'oztestmode'; const isTestMode = getParams[wlOztestmodeKey] || null; - mergeDeep(ozoneRequest, {device: bidderRequest?.ortb2?.device || {}}); + mergeDeep(ozoneRequest, { device: bidderRequest?.ortb2?.device || {} }); const placementIdOverrideFromGetParam = this.getPlacementIdOverrideFromGetParam(); let schain = null; var auctionId = deepAccess(validBidRequests, '0.ortb2.source.tid'); @@ -168,7 +170,7 @@ export const spec = { } const tosendtags = validBidRequests.map(ozoneBidRequest => { var obj = {}; - let prunedImp = this.pruneToExtPaths(ozoneBidRequest.ortb2Imp, {maxTestDepth: 2}); + let prunedImp = this.pruneToExtPaths(ozoneBidRequest.ortb2Imp, { maxTestDepth: 2 }); logInfo('merging into bid[] from pruned ozoneBidRequest.ortb2Imp (this includes adunits ortb2imp and gpid & tid from gptPreAuction if included', prunedImp); mergeDeep(obj, prunedImp); const placementId = placementIdOverrideFromGetParam || this.getPlacementId(ozoneBidRequest); @@ -229,12 +231,12 @@ export const spec = { w: arrBannerSizes[0][0] || 0, h: arrBannerSizes[0][1] || 0, format: arrBannerSizes.map(s => { - return {w: s[0], h: s[1]}; + return { w: s[0], h: s[1] }; }) }; } obj.placementId = placementId; - mergeDeep(obj, {ext: {prebid: {'storedrequest': {'id': placementId}}}}); + mergeDeep(obj, { ext: { prebid: { 'storedrequest': { 'id': placementId } } } }); obj.ext[bidderKey] = obj.ext[bidderKey] || {}; obj.ext[bidderKey].adUnitCode = ozoneBidRequest.adUnitCode; if (ozoneBidRequest.params.hasOwnProperty('customData')) { @@ -256,7 +258,7 @@ export const spec = { obj.ext[bidderKey].customData[i]['targeting'][wlOztestmodeKey] = isTestMode; } } else { - obj.ext[bidderKey].customData = [{'settings': {}, 'targeting': {}}]; + obj.ext[bidderKey].customData = [{ 'settings': {}, 'targeting': {} }]; obj.ext[bidderKey].customData[0].targeting[wlOztestmodeKey] = isTestMode; } } @@ -274,14 +276,6 @@ export const spec = { if (auctionId) { obj.ext.auctionId = auctionId; } - if (fledgeEnabled) { - const auctionEnvironment = deepAccess(ozoneBidRequest, 'ortb2Imp.ext.ae'); - if (isInteger(auctionEnvironment)) { - deepSetValue(obj, 'ext.ae', auctionEnvironment); - } else { - logError(`ignoring ortb2Imp.ext.ae - not an integer for obj.id=${obj.id}`); - } - } return obj; }); const extObj = {}; @@ -311,7 +305,7 @@ export const spec = { } const userExtEids = deepAccess(validBidRequests, '0.userIdAsEids', []); mergeDeep(ozoneRequest.site, { - 'publisher': {'id': htmlParams.publisherId}, + 'publisher': { 'id': htmlParams.publisherId }, 'page': getRefererInfo().page, 'id': htmlParams.siteId }); @@ -319,7 +313,7 @@ export const spec = { if (bidderRequest && bidderRequest.gdprConsent) { logInfo('ADDING GDPR'); const apiVersion = deepAccess(bidderRequest, 'gdprConsent.apiVersion', 1); - mergeDeep(ozoneRequest.regs, {ext: {gdpr: bidderRequest.gdprConsent.gdprApplies ? 1 : 0, apiVersion: apiVersion}}); + mergeDeep(ozoneRequest.regs, { ext: { gdpr: bidderRequest.gdprConsent.gdprApplies ? 1 : 0, apiVersion: apiVersion } }); if (bidderRequest.gdprConsent.gdprApplies) { deepSetValue(ozoneRequest, 'user.ext.consent', bidderRequest.gdprConsent.consentString); } else { @@ -353,12 +347,12 @@ export const spec = { const arrRet = []; for (let i = 0; i < tosendtags.length; i += batchRequestsVal) { ozoneRequest.id = generateUUID(); - mergeDeep(ozoneRequest, {user: {ext: {eids: userExtEids}}}); + mergeDeep(ozoneRequest, { user: { ext: { eids: userExtEids } } }); if (auctionId) { deepSetValue(ozoneRequest, 'source.tid', auctionId); } ozoneRequest.imp = tosendtags.slice(i, i + batchRequestsVal); - mergeDeep(ozoneRequest, {ext: extObj}); + mergeDeep(ozoneRequest, { ext: extObj }); toOrtb25(ozoneRequest); if (ozoneRequest.imp.length > 0) { arrRet.push({ @@ -377,9 +371,9 @@ export const spec = { logInfo('single request starting'); ozoneRequest.id = generateUUID(); ozoneRequest.imp = tosendtags; - mergeDeep(ozoneRequest, {ext: extObj}); + mergeDeep(ozoneRequest, { ext: extObj }); toOrtb25(ozoneRequest); - mergeDeep(ozoneRequest, {user: {ext: {eids: userExtEids}}}); + mergeDeep(ozoneRequest, { user: { ext: { eids: userExtEids } } }); if (auctionId) { deepSetValue(ozoneRequest, 'source.tid', auctionId); } @@ -398,8 +392,8 @@ export const spec = { const ozoneRequestSingle = Object.assign({}, ozoneRequest); ozoneRequestSingle.id = generateUUID(); ozoneRequestSingle.imp = [imp]; - mergeDeep(ozoneRequestSingle, {ext: extObj}); - mergeDeep(ozoneRequestSingle, {user: {ext: {eids: userExtEids}}}); + mergeDeep(ozoneRequestSingle, { ext: extObj }); + mergeDeep(ozoneRequestSingle, { user: { ext: { eids: userExtEids } } }); if (auctionId) { deepSetValue(ozoneRequestSingle, 'source.tid', auctionId); } @@ -424,13 +418,13 @@ export const spec = { logInfo('getFloorObjectForAuction mediaTypesSizes : ', mediaTypesSizes); const ret = {}; if (mediaTypesSizes.banner) { - ret.banner = bidRequestRef.getFloor({mediaType: 'banner', currency: 'USD', size: mediaTypesSizes.banner[0]}); + ret.banner = bidRequestRef.getFloor({ mediaType: 'banner', currency: 'USD', size: mediaTypesSizes.banner[0] }); } if (mediaTypesSizes.video) { - ret.video = bidRequestRef.getFloor({mediaType: 'video', currency: 'USD', size: mediaTypesSizes.video[0]}); + ret.video = bidRequestRef.getFloor({ mediaType: 'video', currency: 'USD', size: mediaTypesSizes.video[0] }); } if (mediaTypesSizes.native) { - ret.native = bidRequestRef.getFloor({mediaType: 'native', currency: 'USD', size: mediaTypesSizes.native[0]}); + ret.native = bidRequestRef.getFloor({ mediaType: 'native', currency: 'USD', size: mediaTypesSizes.native[0] }); } logInfo('getFloorObjectForAuction returning : ', deepClone(ret)); return ret; @@ -468,9 +462,9 @@ export const spec = { for (let j = 0; j < sb.bid.length; j++) { const thisRequestBid = this.getBidRequestForBidId(sb.bid[j].impid, request.bidderRequest.bids); logInfo(`seatbid:${i}, bid:${j} Going to set default w h for seatbid/bidRequest`, sb.bid[j], thisRequestBid); - const {defaultWidth, defaultHeight} = defaultSize(thisRequestBid); + const { defaultWidth, defaultHeight } = defaultSize(thisRequestBid); const thisBid = ozoneAddStandardProperties(sb.bid[j], defaultWidth, defaultHeight); - thisBid.meta = {advertiserDomains: thisBid.adomain || []}; + thisBid.meta = { advertiserDomains: thisBid.adomain || [] }; let videoContext = null; let isVideo = false; const bidType = deepAccess(thisBid, 'ext.prebid.type'); @@ -544,7 +538,7 @@ export const spec = { logInfo(perBidInfo); } } - let {seat: winningSeat, bid: winningBid} = ozoneGetWinnerForRequestBid(thisBid.bidId, serverResponse.seatbid); + let { seat: winningSeat, bid: winningBid } = ozoneGetWinnerForRequestBid(thisBid.bidId, serverResponse.seatbid); winningBid = ozoneAddStandardProperties(winningBid, defaultWidth, defaultHeight); adserverTargeting[prefix + '_auc_id'] = String(aucId); adserverTargeting[prefix + '_winner'] = String(winningSeat); @@ -572,20 +566,6 @@ export const spec = { } } let ret = arrAllBids; - let fledgeAuctionConfigs = deepAccess(serverResponse, 'ext.igi') || []; - if (isArray(fledgeAuctionConfigs) && fledgeAuctionConfigs.length > 0) { - fledgeAuctionConfigs = fledgeAuctionConfigs.filter(cfg => { - if (typeof cfg !== 'object' || cfg === null) { - logWarn('Removing malformed fledge auction config:', cfg); - return false; - } - return true; - }); - ret = { - bids: arrAllBids, - fledgeAuctionConfigs, - }; - } const endTime = new Date().getTime(); logInfo(`interpretResponse going to return at time ${endTime} (took ${endTime - startTime}ms) Time from buildRequests Start -> interpretRequests End = ${endTime - this.propertyBag.buildRequestsStart}ms`); logInfo('will return: ', deepClone(ret)); @@ -603,7 +583,7 @@ export const spec = { var ret = []; for (let i = 0; i < seatbid.length; i++) { const sb = seatbid[i]; - var retSeatbid = {'seat': sb.seat, 'bid': []}; + var retSeatbid = { 'seat': sb.seat, 'bid': [] }; var bidIds = []; for (let j = 0; j < sb.bid.length; j++) { var candidate = sb.bid[j]; @@ -727,7 +707,7 @@ export const spec = { return this.propertyBag.pageId; }, unpackVideoConfigIntoIABformat(videoConfig, childConfig) { - let ret = {'ext': {}}; + let ret = { 'ext': {} }; ret = this._unpackVideoConfigIntoIABformat(ret, videoConfig); ret = this._unpackVideoConfigIntoIABformat(ret, childConfig); return ret; @@ -886,7 +866,7 @@ export function ozoneGetWinnerForRequestBid(requestBidId, serverResponseSeatBid) } } } - return {'seat': winningSeat, 'bid': thisBidWinner}; + return { 'seat': winningSeat, 'bid': thisBidWinner }; } export function ozoneGetAllBidsForBidId(matchBidId, serverResponseSeatBid, defaultWidth, defaultHeight) { const objBids = {}; @@ -926,7 +906,7 @@ export function getRoundedBid(price, mediaType) { key = 'custom'; } } - const mapping = {medium: 'med', custom: 'custom', high: 'high', low: 'low', dense: 'dense'}; + const mapping = { medium: 'med', custom: 'custom', high: 'high', low: 'low', dense: 'dense' }; const priceStrings = getPriceBucketString(price, buckets, config.getConfig('currency.granularityMultiplier')); logInfo('getRoundedBid price:', price, 'mediaType:', mediaType, 'bucketKey:', key); return priceStrings[mapping[key] || 'auto']; @@ -961,7 +941,7 @@ export function getWidthAndHeightFromVideoObject(objVideo) { logError('getWidthAndHeightFromVideoObject found playerSize with length of ' + playerSize.length + '. This is totally wrong - cannot continue.'); return null; } - return ({'w': playerSize[0], 'h': playerSize[1]}); + return ({ 'w': playerSize[0], 'h': playerSize[1] }); } function getPlayerSizeFromObject(objVideo) { logInfo('getPlayerSizeFromObject received object', objVideo); diff --git a/modules/paapi.js b/modules/paapi.js deleted file mode 100644 index e67b24bdcfc..00000000000 --- a/modules/paapi.js +++ /dev/null @@ -1,808 +0,0 @@ -/** - * Collect PAAPI component auction configs from bid adapters and make them available through `pbjs.getPAAPIConfig()` - */ -import {config} from '../src/config.js'; -import {getHook, hook, module} from '../src/hook.js'; -import { - deepAccess, - deepEqual, - deepSetValue, - logError, - logInfo, - logWarn, - mergeDeep, - sizesToSizeTuples -} from '../src/utils.js'; -import {IMP, PBS, registerOrtbProcessor, RESPONSE} from '../src/pbjsORTB.js'; -import * as events from '../src/events.js'; -import {EVENTS} from '../src/constants.js'; -import {currencyCompare} from '../libraries/currencyUtils/currency.js'; -import {keyCompare, maximum, minimum} from '../src/utils/reducers.js'; -import {getGlobal} from '../src/prebidGlobal.js'; -import {auctionStore} from '../libraries/weakStore/weakStore.js'; -import {adapterMetrics, guardTids} from '../src/adapters/bidderFactory.js'; -import {defer, PbPromise} from '../src/utils/promise.js'; -import {auctionManager} from '../src/auctionManager.js'; - -const MODULE = 'PAAPI'; - -const submodules = []; -const USED = new WeakSet(); - -export function registerSubmodule(submod) { - submodules.push(submod); - submod.init && submod.init({ - getPAAPIConfig, - expandFilters - }); -} - -module('paapi', registerSubmodule); - -/* auction configs as returned by getPAAPIConfigs */ -const configsForAuction = auctionStore(); - -/* auction configs returned by adapters, but waiting for end-of-auction signals before they're added to configsForAuction */ -const pendingConfigsForAuction = auctionStore(); - -/* igb returned by adapters, waiting for end-of-auction signals before they're merged into configForAuctions */ -const pendingBuyersForAuction = auctionStore(); - -/* for auction configs that were generated in parallel with auctions (and contain promises), their resolve/reject methods */ -const deferredConfigsForAuction = auctionStore(); - -let latestAuctionForAdUnit = {}; -let moduleConfig = {}; - -config.getConfig('paapi', config => { - init(config.paapi); -}); - -export function reset() { - submodules.splice(0, submodules.length); - latestAuctionForAdUnit = {}; -} - -export function init(cfg) { - if (cfg && cfg.enabled === true) { - if (!moduleConfig.enabled) { - attachHandlers(); - } - moduleConfig = cfg; - logInfo(`${MODULE} enabled (browser ${isFledgeSupported() ? 'supports' : 'does NOT support'} runAdAuction)`, cfg); - } else { - if (moduleConfig.enabled) { - detachHandlers(); - } - moduleConfig = {}; - logInfo(`${MODULE} disabled`, cfg); - } -} - -function attachHandlers() { - getHook('addPaapiConfig').before(addPaapiConfigHook); - getHook('makeBidRequests').before(addPaapiData); - getHook('makeBidRequests').after(markForFledge); - getHook('processBidderRequests').before(parallelPaapiProcessing, 9); - // resolve params before parallel processing - getHook('processBidderRequests').before(buildPAAPIParams, 10); - getHook('processBidderRequests').before(adAuctionHeadersHook); - events.on(EVENTS.AUCTION_INIT, onAuctionInit); - events.on(EVENTS.AUCTION_END, onAuctionEnd); -} - -function detachHandlers() { - getHook('addPaapiConfig').getHooks({hook: addPaapiConfigHook}).remove(); - getHook('makeBidRequests').getHooks({hook: addPaapiData}).remove(); - getHook('makeBidRequests').getHooks({hook: markForFledge}).remove(); - getHook('processBidderRequests').getHooks({hook: parallelPaapiProcessing}).remove(); - getHook('processBidderRequests').getHooks({hook: buildPAAPIParams}).remove(); - getHook('processBidderRequests').getHooks({hook: adAuctionHeadersHook}).remove(); - events.off(EVENTS.AUCTION_INIT, onAuctionInit); - events.off(EVENTS.AUCTION_END, onAuctionEnd); -} - -export function adAuctionHeadersHook(next, spec, bids, bidderRequest, ajax, ...args) { - if (bidderRequest.paapi?.enabled) { - ajax = ((orig) => { - return function (url, callback, data, options) { - options = options ?? {}; - options.adAuctionHeaders = options.adAuctionHeaders ?? true; - return orig.call(this, url, callback, data, options); - } - })(ajax); - } - return next.call(this, spec, bids, bidderRequest, ajax, ...args); -} - -function getStaticSignals(adUnit = {}) { - const cfg = {}; - const requestedSize = getRequestedSize(adUnit); - if (requestedSize) { - cfg.requestedSize = requestedSize; - } - return cfg; -} - -function getSlotSignals(bidsReceived = [], bidRequests = []) { - let bidfloor, bidfloorcur; - if (bidsReceived.length > 0) { - const bestBid = bidsReceived.reduce(maximum(currencyCompare(bid => [bid.cpm, bid.currency]))); - bidfloor = bestBid.cpm; - bidfloorcur = bestBid.currency; - } else { - const floors = bidRequests.map(bid => typeof bid.getFloor === 'function' && bid.getFloor()).filter(f => f); - const minFloor = floors.length && floors.reduce(minimum(currencyCompare(floor => [floor.floor, floor.currency]))); - bidfloor = minFloor?.floor; - bidfloorcur = minFloor?.currency; - } - const cfg = {}; - if (bidfloor) { - deepSetValue(cfg, 'auctionSignals.prebid.bidfloor', bidfloor); - bidfloorcur && deepSetValue(cfg, 'auctionSignals.prebid.bidfloorcur', bidfloorcur); - } - return cfg; -} - -export function buyersToAuctionConfigs(igbRequests, merge = mergeBuyers, config = moduleConfig?.componentSeller ?? {}, partitioners = { - compact: (igbRequests) => partitionBuyers(igbRequests.map(req => req[1])).map(part => [{}, part]), - expand: partitionBuyersByBidder -}) { - if (!config.auctionConfig) { - logWarn(MODULE, 'Cannot use IG buyers: paapi.componentSeller.auctionConfig not set', igbRequests.map(req => req[1])); - return []; - } - const partition = partitioners[config.separateAuctions ? 'expand' : 'compact']; - return partition(igbRequests) - .map(([request, igbs]) => { - const auctionConfig = mergeDeep(merge(igbs), config.auctionConfig); - auctionConfig.auctionSignals = setFPD(auctionConfig.auctionSignals || {}, request); - return [request, auctionConfig]; - }); -} - -function onAuctionEnd({auctionId, bidsReceived, bidderRequests, adUnitCodes, adUnits}) { - const adUnitsByCode = Object.fromEntries(adUnits?.map(au => [au.code, au]) || []); - const allReqs = bidderRequests?.flatMap(br => br.bids); - const paapiConfigs = configsForAuction(auctionId); - (adUnitCodes || []).forEach(au => { - if (!paapiConfigs.hasOwnProperty(au)) { - paapiConfigs[au] = null; - } - !latestAuctionForAdUnit.hasOwnProperty(au) && (latestAuctionForAdUnit[au] = null); - }); - - const pendingConfigs = pendingConfigsForAuction(auctionId); - const pendingBuyers = pendingBuyersForAuction(auctionId); - - if (pendingConfigs && pendingBuyers) { - Object.entries(pendingBuyers).forEach(([adUnitCode, igbRequests]) => { - buyersToAuctionConfigs(igbRequests).forEach(([{bidder}, auctionConfig]) => append(pendingConfigs, adUnitCode, {id: getComponentSellerConfigId(bidder), config: auctionConfig})) - }) - } - - const deferredConfigs = deferredConfigsForAuction(auctionId); - - const adUnitsWithConfigs = Array.from(new Set(Object.keys(pendingConfigs).concat(Object.keys(deferredConfigs)))); - const signals = Object.fromEntries( - adUnitsWithConfigs.map(adUnitCode => { - latestAuctionForAdUnit[adUnitCode] = auctionId; - const forThisAdUnit = (bid) => bid.adUnitCode === adUnitCode; - return [adUnitCode, { - ...getStaticSignals(adUnitsByCode[adUnitCode]), - ...getSlotSignals(bidsReceived?.filter(forThisAdUnit), allReqs?.filter(forThisAdUnit)) - }] - }) - ) - - const configsById = {}; - Object.entries(pendingConfigs || {}).forEach(([adUnitCode, auctionConfigs]) => { - auctionConfigs.forEach(({id, config}) => append(configsById, id, { - adUnitCode, - config: mergeDeep({}, signals[adUnitCode], config) - })); - }); - - function resolveSignals(signals, deferrals) { - Object.entries(deferrals).forEach(([signal, {resolve, default: defaultValue}]) => { - let value = signals.hasOwnProperty(signal) ? signals[signal] : null; - if (value == null && defaultValue == null) { - value = undefined; - } else if (typeof defaultValue === 'object' && typeof value === 'object') { - value = mergeDeep({}, defaultValue, value); - } else { - value = value ?? defaultValue - } - resolve(value); - }) - } - - Object.entries(deferredConfigs).forEach(([adUnitCode, {top, components}]) => { - resolveSignals(signals[adUnitCode], top); - Object.entries(components).forEach(([configId, {deferrals}]) => { - const matchingConfigs = configsById.hasOwnProperty(configId) ? configsById[configId] : []; - if (matchingConfigs.length > 1) { - logWarn(`Received multiple PAAPI configs for the same bidder and seller (${configId}), active PAAPI auctions will only see the first`); - } - const {config} = matchingConfigs.shift() ?? {config: {...signals[adUnitCode]}} - resolveSignals(config, deferrals); - }) - }); - - const newConfigs = Object.values(configsById).flatMap(configs => configs); - const hasDeferredConfigs = Object.keys(deferredConfigs).length > 0; - - if (moduleConfig.parallel && hasDeferredConfigs && newConfigs.length > 0) { - logError(`Received PAAPI configs after PAAPI auctions were already started in parallel with their contextual auction`, newConfigs) - } - - newConfigs.forEach(({adUnitCode, config}) => { - if (paapiConfigs[adUnitCode] == null) { - paapiConfigs[adUnitCode] = { - ...signals[adUnitCode], - componentAuctions: [] - } - } - paapiConfigs[adUnitCode].componentAuctions.push(mergeDeep({}, signals[adUnitCode], config)); - }); - - if (!moduleConfig.parallel || !hasDeferredConfigs) { - submodules.forEach(submod => submod.onAuctionConfig?.(auctionId, paapiConfigs)); - } -} - -function append(target, key, value) { - !target.hasOwnProperty(key) && (target[key] = []); - target[key].push(value); -} - -function setFPD(target, {ortb2, ortb2Imp}) { - ortb2 != null && deepSetValue(target, 'prebid.ortb2', mergeDeep({}, ortb2, target.prebid?.ortb2)); - ortb2Imp != null && deepSetValue(target, 'prebid.ortb2Imp', mergeDeep({}, ortb2Imp, target.prebid?.ortb2Imp)); - return target; -} - -function getConfigId(bidderCode, seller) { - return `${bidderCode}::${seller}`; -} - -function getComponentSellerConfigId(bidderCode) { - return moduleConfig.componentSeller.separateAuctions ? `igb::${bidderCode}` : 'igb'; -} - -export function addPaapiConfigHook(next, request, paapiConfig) { - if (getFledgeConfig(config.getCurrentBidder()).enabled) { - const {adUnitCode, auctionId, bidder} = request; - - function storePendingData(store, data) { - const target = store(auctionId); - if (target != null) { - append(target, adUnitCode, data) - } else { - logWarn(MODULE, `Received PAAPI config for auction that has closed (auction '${auctionId}', adUnit '${adUnitCode}')`, data); - } - } - - const {config, igb} = paapiConfig; - if (config) { - config.auctionSignals = setFPD(config.auctionSignals || {}, request); - const pbs = config.perBuyerSignals = config.perBuyerSignals ?? {}; - (config.interestGroupBuyers || []).forEach(buyer => { - pbs[buyer] = setFPD(pbs[buyer] ?? {}, request); - }) - storePendingData(pendingConfigsForAuction, {id: getConfigId(bidder, config.seller), config}); - } - if (igb && checkOrigin(igb)) { - igb.pbs = setFPD(igb.pbs || {}, request); - storePendingData(pendingBuyersForAuction, [request, igb]) - } - } - next(request, paapiConfig); -} - -export const IGB_TO_CONFIG = { - cur: 'perBuyerCurrencies', - pbs: 'perBuyerSignals', - ps: 'perBuyerPrioritySignals', - maxbid: 'auctionSignals.prebid.perBuyerMaxbid', -} - -function checkOrigin(igb) { - if (igb.origin) return true; - logWarn('PAAPI buyer does not specify origin and will be ignored', igb); -} - -/** - * Convert a list of InterestGroupBuyer (igb) objects into a partial auction config. - * https://github.com/InteractiveAdvertisingBureau/openrtb/blob/main/extensions/community_extensions/Protected%20Audience%20Support.md - */ -export function mergeBuyers(igbs) { - const buyers = new Set(); - return Object.assign( - igbs.reduce((config, igb) => { - if (checkOrigin(igb)) { - if (!buyers.has(igb.origin)) { - buyers.add(igb.origin); - Object.entries(IGB_TO_CONFIG).forEach(([igbField, configField]) => { - if (igb[igbField] != null) { - const entry = deepAccess(config, configField) || {} - entry[igb.origin] = igb[igbField]; - deepSetValue(config, configField, entry); - } - }); - } else { - logWarn(MODULE, `Duplicate buyer: ${igb.origin}. All but the first will be ignored`, igbs); - } - } - return config; - }, {}), - { - interestGroupBuyers: Array.from(buyers.keys()) - } - ); -} - -/** - * Partition a list of InterestGroupBuyer (igb) object into sets that can each be merged into a single auction. - * If the same buyer (origin) appears more than once, it will be split across different partition unless the igb objects - * are identical. - */ -export function partitionBuyers(igbs) { - return igbs.reduce((partitions, igb) => { - if (checkOrigin(igb)) { - let partition = partitions.find(part => !part.hasOwnProperty(igb.origin) || deepEqual(part[igb.origin], igb)); - if (!partition) { - partition = {}; - partitions.push(partition); - } - partition[igb.origin] = igb; - } - return partitions; - }, []).map(part => Object.values(part)); -} - -export function partitionBuyersByBidder(igbRequests) { - const requests = {}; - const igbs = {}; - igbRequests.forEach(([request, igb]) => { - !requests.hasOwnProperty(request.bidder) && (requests[request.bidder] = request); - append(igbs, request.bidder, igb); - }) - return Object.entries(igbs).map(([bidder, igbs]) => [requests[bidder], igbs]) -} - -/** - * Expand PAAPI api filters into a map from ad unit code to auctionId. - * - * auctionId when specified, the result will have this as the value for each entry. - * when not specified, each ad unit will map to the latest auction that involved that ad unit. - * adUnitCode when specified, the result will contain only one entry (for this ad unit) or be empty (if this ad - * unit was never involved in an auction). - * when not specified, the result will contain an entry for every ad unit that was involved in any auction. - * @return {{[adUnitCode: string]: string}} - */ -function expandFilters({auctionId, adUnitCode} = {}) { - let adUnitCodes = []; - if (adUnitCode == null) { - adUnitCodes = Object.keys(latestAuctionForAdUnit); - } else if (latestAuctionForAdUnit.hasOwnProperty(adUnitCode)) { - adUnitCodes = [adUnitCode]; - } - return Object.fromEntries( - adUnitCodes.map(au => [au, auctionId ?? latestAuctionForAdUnit[au]]) - ); -} - -/** - * Get PAAPI auction configuration. - * - * @param {Object} [filters] - Filters object - * @param {string} [filters.auctionId] optional auction filter; if omitted, the latest auction for each ad unit is used - * @param {string} [filters.adUnitCode] optional ad unit filter - * @param {boolean} [includeBlanks=false] if true, include null entries for ad units that match the given filters but do not have any available auction configs. - * @returns {Object} a map from ad unit code to auction config for the ad unit. - */ -export function getPAAPIConfig(filters = {}, includeBlanks = false) { - const output = {}; - Object.entries(expandFilters(filters)).forEach(([au, auctionId]) => { - const auctionConfigs = configsForAuction(auctionId); - if (auctionConfigs?.hasOwnProperty(au)) { - // ad unit was involved in a PAAPI auction - const candidate = auctionConfigs[au]; - if (candidate && !USED.has(candidate)) { - output[au] = candidate; - USED.add(candidate); - } else if (includeBlanks) { - output[au] = null; - } - } else if (auctionId == null && includeBlanks) { - // ad unit was involved in a non-PAAPI auction - output[au] = null; - } - }); - return output; -} - -getGlobal().getPAAPIConfig = (filters) => getPAAPIConfig(filters); - -function isFledgeSupported() { - return 'runAdAuction' in navigator && 'joinAdInterestGroup' in navigator; -} - -function getFledgeConfig(bidder) { - const enabled = moduleConfig.enabled && (bidder == null || !moduleConfig.bidders?.length || moduleConfig.bidders?.includes(bidder)); - return { - enabled, - ae: enabled ? moduleConfig.defaultForSlots : undefined - }; -} - -/** - * Given an array of size tuples, return the one that should be used for PAAPI. - */ -export const getPAAPISize = hook('sync', function (sizes) { - sizes = sizes - ?.filter(([w, h]) => !(w === h && w <= 5)); - - if (sizes?.length) { - return sizes - .reduce(maximum(keyCompare(([w, h]) => w * h))); - } -}, 'getPAAPISize'); - -function getRequestedSize(adUnit) { - return adUnit.ortb2Imp?.ext?.paapi?.requestedSize || (() => { - const size = getPAAPISize(sizesToSizeTuples(adUnit.mediaTypes?.banner?.sizes)); - if (size) { - return { - width: size[0], - height: size[1] - }; - } - })(); -} - -export function addPaapiData(next, adUnits, ...args) { - if (isFledgeSupported() && moduleConfig.enabled) { - adUnits.forEach(adUnit => { - // https://github.com/InteractiveAdvertisingBureau/openrtb/blob/main/extensions/community_extensions/Protected%20Audience%20Support.md - const igsAe = adUnit.ortb2Imp?.ext?.igs != null - ? adUnit.ortb2Imp.ext.igs.ae || 1 - : null; - const extAe = adUnit.ortb2Imp?.ext?.ae; - if (igsAe !== extAe && igsAe != null && extAe != null) { - logWarn(MODULE, `Ad unit defines conflicting ortb2Imp.ext.ae and ortb2Imp.ext.igs, using the latter`, adUnit); - } - const ae = igsAe ?? extAe ?? moduleConfig.defaultForSlots; - if (ae) { - deepSetValue(adUnit, 'ortb2Imp.ext.ae', ae); - adUnit.ortb2Imp.ext.igs = Object.assign({ - ae: ae, - biddable: 1 - }, adUnit.ortb2Imp.ext.igs); - const requestedSize = getRequestedSize(adUnit); - if (requestedSize) { - deepSetValue(adUnit, 'ortb2Imp.ext.paapi.requestedSize', requestedSize); - } - adUnit.bids.forEach(bidReq => { - if (!getFledgeConfig(bidReq.bidder).enabled) { - deepSetValue(bidReq, 'ortb2Imp.ext.ae', 0); - bidReq.ortb2Imp.ext.igs = {ae: 0, biddable: 0}; - } - }) - } - }) - } - next(adUnits, ...args); -} - -export const NAVIGATOR_APIS = ['createAuctionNonce', 'getInterestGroupAdAuctionData']; - -export function markForFledge(next, bidderRequests) { - if (isFledgeSupported()) { - bidderRequests.forEach((bidderReq) => { - const {enabled} = getFledgeConfig(bidderReq.bidderCode); - Object.assign(bidderReq, { - paapi: { - enabled, - componentSeller: !!moduleConfig.componentSeller?.auctionConfig - } - }); - if (enabled) { - NAVIGATOR_APIS.forEach(method => { - bidderReq.paapi[method] = (...args) => new AsyncPAAPIParam(() => navigator[method](...args)) - }) - } - }); - } - next(bidderRequests); -} - -export const ASYNC_SIGNALS = [ - 'auctionSignals', - 'sellerSignals', - 'perBuyerSignals', - 'perBuyerTimeouts', - 'directFromSellerSignals', - 'perBuyerCurrencies', - 'perBuyerCumulativeTimeouts', - 'serverResponse' -]; - -const validatePartialConfig = (() => { - const REQUIRED_SYNC_SIGNALS = [ - { - props: ['seller'], - validate: (val) => typeof val === 'string' - }, - { - props: ['interestGroupBuyers'], - validate: (val) => Array.isArray(val) && val.length > 0 - }, - { - props: ['decisionLogicURL', 'decisionLogicUrl'], - validate: (val) => typeof val === 'string' - } - ]; - - return function (config) { - const invalid = REQUIRED_SYNC_SIGNALS.find(({props, validate}) => props.every(prop => !config.hasOwnProperty(prop) || !config[prop] || !validate(config[prop]))); - if (invalid) { - logError(`Partial PAAPI config has missing or invalid property "${invalid.props[0]}"`, config) - return false; - } - return true; - } -})() - -function callAdapterApi(spec, method, bids, bidderRequest) { - const metrics = adapterMetrics(bidderRequest); - const tidGuard = guardTids(bidderRequest); - let result; - metrics.measureTime(method, () => { - try { - result = spec[method](bids.map(tidGuard.bidRequest), tidGuard.bidderRequest(bidderRequest)) - } catch (e) { - logError(`Error invoking "${method}":`, e); - } - }); - return result; -} - -/** - * Adapters can provide a `spec.buildPAAPIConfigs(validBidRequests, bidderRequest)` to be included in PAAPI auctions - * that can be started in parallel with contextual auctions. - * - * If PAAPI is enabled, and an adapter provides `buildPAAPIConfigs`, it is invoked just before `buildRequests`, - * and takes the same arguments. It should return an array of PAAPI configuration objects with the same format - * as in `interpretResponse` (`{bidId, config?, igb?}`). - * - * Everything returned by `buildPAAPIConfigs` is treated in the same way as if it was returned by `interpretResponse` - - * except for signals that can be provided asynchronously (cfr. `ASYNC_SIGNALS`), which are replaced by promises. - * When the (contextual) auction ends, the promises are resolved. - * - * If during the auction the adapter's `interpretResponse` returned matching configurations (same `bidId`, - * and a `config` with the same `seller`, or an `igb` with the same `origin`), the promises resolve to their contents. - * Otherwise, they resolve to the values provided by `buildPAAPIConfigs`, or an empty object if no value was provided. - * - * Promisified auction configs are available from `getPAAPIConfig` immediately after `requestBids`. - * If the `paapi.parallel` config flag is set, PAAPI submodules are also triggered at the same time - * (instead of when the auction ends). - */ -export function parallelPaapiProcessing(next, spec, bids, bidderRequest, ...args) { - function makeDeferrals(defaults = {}) { - const promises = {}; - const deferrals = Object.fromEntries(ASYNC_SIGNALS.map(signal => { - const def = defer({promiseFactory: (resolver) => new Promise(resolver)}); - def.default = defaults.hasOwnProperty(signal) ? defaults[signal] : null; - promises[signal] = def.promise; - return [signal, def] - })) - return [deferrals, promises]; - } - - const {auctionId, paapi: {enabled, componentSeller} = {}} = bidderRequest; - const auctionConfigs = configsForAuction(auctionId); - bids.map(bid => bid.adUnitCode).forEach(adUnitCode => { - latestAuctionForAdUnit[adUnitCode] = auctionId; - if (!auctionConfigs.hasOwnProperty(adUnitCode)) { - auctionConfigs[adUnitCode] = null; - } - }); - - if (enabled && spec.buildPAAPIConfigs) { - const partialConfigs = callAdapterApi(spec, 'buildPAAPIConfigs', bids, bidderRequest) - const requestsById = Object.fromEntries(bids.map(bid => [bid.bidId, bid])); - (partialConfigs ?? []).forEach(({bidId, config, igb}) => { - const bidRequest = requestsById.hasOwnProperty(bidId) && requestsById[bidId]; - if (!bidRequest) { - logError(`Received partial PAAPI config for unknown bidId`, {bidId, config}); - } else { - const adUnitCode = bidRequest.adUnitCode; - latestAuctionForAdUnit[adUnitCode] = auctionId; - const deferredConfigs = deferredConfigsForAuction(auctionId); - - const getDeferredConfig = () => { - if (!deferredConfigs.hasOwnProperty(adUnitCode)) { - const [deferrals, promises] = makeDeferrals(); - auctionConfigs[adUnitCode] = { - ...getStaticSignals(auctionManager.index.getAdUnit(bidRequest)), - ...promises, - componentAuctions: [] - } - deferredConfigs[adUnitCode] = { - top: deferrals, - components: {}, - auctionConfig: auctionConfigs[adUnitCode] - } - } - return deferredConfigs[adUnitCode]; - } - - if (config && validatePartialConfig(config)) { - const configId = getConfigId(bidRequest.bidder, config.seller); - const deferredConfig = getDeferredConfig(); - if (deferredConfig.components.hasOwnProperty(configId)) { - logWarn(`Received multiple PAAPI configs for the same bidder and seller; config will be ignored`, { - config, - bidder: bidRequest.bidder - }) - } else { - const [deferrals, promises] = makeDeferrals(config); - const auctionConfig = { - ...getStaticSignals(bidRequest), - ...config, - ...promises - } - deferredConfig.auctionConfig.componentAuctions.push(auctionConfig) - deferredConfig.components[configId] = {auctionConfig, deferrals}; - } - } - if (componentSeller && igb && checkOrigin(igb)) { - const configId = getComponentSellerConfigId(spec.code); - const deferredConfig = getDeferredConfig(); - const partialConfig = buyersToAuctionConfigs([[bidRequest, igb]])[0][1]; - if (deferredConfig.components.hasOwnProperty(configId)) { - const {auctionConfig, deferrals} = deferredConfig.components[configId]; - if (!auctionConfig.interestGroupBuyers.includes(igb.origin)) { - const immediate = {}; - Object.entries(partialConfig).forEach(([key, value]) => { - if (deferrals.hasOwnProperty(key)) { - mergeDeep(deferrals[key], {default: value}); - } else { - immediate[key] = value; - } - }) - mergeDeep(auctionConfig, immediate); - } else { - logWarn(`Received the same PAAPI buyer multiple times for the same PAAPI auction. Consider setting paapi.componentSeller.separateAuctions: true`, igb) - } - } else { - const [deferrals, promises] = makeDeferrals(partialConfig); - const auctionConfig = { - ...partialConfig, - ...getStaticSignals(bidRequest), - ...promises, - } - deferredConfig.components[configId] = {auctionConfig, deferrals}; - deferredConfig.auctionConfig.componentAuctions.push(auctionConfig); - } - } - } - }) - } - return next.call(this, spec, bids, bidderRequest, ...args); -} - -export class AsyncPAAPIParam { - constructor(resolve) { - this.resolve = resolve; - } -} - -export function buildPAAPIParams(next, spec, bids, bidderRequest, ...args) { - if (bidderRequest.paapi?.enabled && spec.paapiParameters) { - const params = callAdapterApi(spec, 'paapiParameters', bids, bidderRequest); - return PbPromise.all( - Object.entries(params ?? {}).map(([key, value]) => - value instanceof AsyncPAAPIParam - ? value.resolve().then(result => [key, result]) - : Promise.resolve([key, value])) - ).then(resolved => { - bidderRequest.paapi.params = Object.fromEntries(resolved); - }).catch(err => { - logError(`Could not resolve PAAPI parameters`, err); - }).then(() => { - next.call(this, spec, bids, bidderRequest, ...args); - }) - } else { - next.call(this, spec, bids, bidderRequest, ...args); - } -} - -export function onAuctionInit({auctionId}) { - if (moduleConfig.parallel) { - auctionManager.index.getAuction({auctionId}).requestsDone.then(() => { - if (Object.keys(deferredConfigsForAuction(auctionId)).length > 0) { - submodules.forEach(submod => submod.onAuctionConfig?.(auctionId, configsForAuction(auctionId))); - } - }) - } -} - -export function setImpExtAe(imp, bidRequest, context) { - if (!context.bidderRequest.paapi?.enabled) { - delete imp.ext?.ae; - delete imp.ext?.igs; - } -} - -registerOrtbProcessor({type: IMP, name: 'impExtAe', fn: setImpExtAe}); - -export function parseExtIgi(response, ortbResponse, context) { - paapiResponseParser( - (ortbResponse.ext?.igi || []).flatMap(igi => { - return (igi?.igs || []).map(igs => { - if (igs.impid !== igi.impid && igs.impid != null && igi.impid != null) { - logWarn(MODULE, 'ORTB response ext.igi.igs.impid conflicts with parent\'s impid', igi); - } - return { - config: igs.config, - impid: igs.impid ?? igi.impid - } - }).concat((igi?.igb || []).map(igb => ({ - igb, - impid: igi.impid - }))) - }), - response, - context - ) -} - -function paapiResponseParser(configs, response, context) { - configs.forEach((config) => { - const impCtx = context.impContext[config.impid]; - if (!impCtx?.imp?.ext?.ae) { - logWarn(MODULE, 'Received auction configuration for an impression that was not in the request or did not ask for it', config, impCtx?.imp); - } else { - impCtx.paapiConfigs = impCtx.paapiConfigs || []; - impCtx.paapiConfigs.push(config); - } - }); -} - -// to make it easier to share code between the PBS adapter and adapters whose backend is PBS, break up -// fledge response processing in two steps: first aggregate all the auction configs by their imp... - -export function parseExtPrebidFledge(response, ortbResponse, context) { - paapiResponseParser( - (ortbResponse.ext?.prebid?.fledge?.auctionconfigs || []), - response, - context - ) -} - -registerOrtbProcessor({type: RESPONSE, name: 'extPrebidFledge', fn: parseExtPrebidFledge, dialects: [PBS]}); -registerOrtbProcessor({type: RESPONSE, name: 'extIgiIgs', fn: parseExtIgi}); - -// ...then, make them available in the adapter's response. This is the client side version, for which the -// interpretResponse api is {fledgeAuctionConfigs: [{bidId, config}]} - -export function setResponsePaapiConfigs(response, ortbResponse, context) { - const configs = Object.values(context.impContext) - .flatMap((impCtx) => (impCtx.paapiConfigs || []).map(cfg => ({ - bidId: impCtx.bidRequest.bidId, - ...cfg - }))); - if (configs.length > 0) { - response.paapi = configs; - } -} - -registerOrtbProcessor({ - type: RESPONSE, - name: 'paapiConfigs', - priority: -1, - fn: setResponsePaapiConfigs, -}); diff --git a/modules/paapiForGpt.js b/modules/paapiForGpt.js deleted file mode 100644 index 363c83ada03..00000000000 --- a/modules/paapiForGpt.js +++ /dev/null @@ -1,166 +0,0 @@ -/** - * GPT-specific slot configuration logic for PAAPI. - */ -import {getHook, submodule} from '../src/hook.js'; -import {deepAccess, logInfo, logWarn, sizeTupleToSizeString} from '../src/utils.js'; -import {config} from '../src/config.js'; -import {getGlobal} from '../src/prebidGlobal.js'; - -import {keyCompare} from '../src/utils/reducers.js'; -import {getGPTSlotsForAdUnits, targeting} from '../src/targeting.js'; - -const MODULE = 'paapiForGpt'; - -let getPAAPIConfig; - -config.getConfig('paapi', (cfg) => { - if (deepAccess(cfg, 'paapi.gpt.configWithTargeting', true)) { - logInfo(MODULE, 'enabling PAAPI configuration with setTargetingForGPTAsync') - targeting.setTargetingForGPT.before(setTargetingHook); - } else { - targeting.setTargetingForGPT.getHooks({hook: setTargetingHook}).remove(); - } -}); - -export function setTargetingHookFactory(setPaapiConfig = getGlobal().setPAAPIConfigForGPT) { - return function(next, adUnit, customSlotMatching) { - const adUnitCodes = Array.isArray(adUnit) ? adUnit : [adUnit] - adUnitCodes - .map(adUnitCode => adUnitCode == null ? undefined : {adUnitCode}) - .forEach(filters => setPaapiConfig(filters, customSlotMatching)) - next(adUnit, customSlotMatching); - } -} - -export function slotConfigurator() { - const PREVIOUSLY_SET = {}; - return function setComponentAuction(adUnitCode, gptSlots, auctionConfigs, reset = true) { - if (gptSlots.length > 0) { - let previous = PREVIOUSLY_SET[adUnitCode] ?? {}; - let configsBySeller = Object.fromEntries(auctionConfigs.map(cfg => [cfg.seller, cfg])); - const sellers = Object.keys(configsBySeller); - if (reset) { - configsBySeller = Object.assign(previous, configsBySeller); - previous = Object.fromEntries(sellers.map(seller => [seller, null])); - } else { - sellers.forEach(seller => { - previous[seller] = null; - }); - } - Object.keys(previous).length ? PREVIOUSLY_SET[adUnitCode] = previous : delete PREVIOUSLY_SET[adUnitCode]; - const componentAuction = Object.entries(configsBySeller) - .map(([configKey, auctionConfig]) => ({configKey, auctionConfig})); - if (componentAuction.length > 0) { - gptSlots.forEach(gptSlot => { - gptSlot.setConfig({componentAuction}); - logInfo(MODULE, `register component auction configs for: ${adUnitCode}: ${gptSlot.getAdUnitPath()}`, auctionConfigs); - // reference https://developers.google.com/publisher-tag/reference#googletag.config.ComponentAuctionConfig - }); - } - } else if (auctionConfigs.length > 0) { - logWarn(MODULE, `unable to register component auction config for ${adUnitCode}`, auctionConfigs); - } - }; -} - -const setComponentAuction = slotConfigurator(); - -export const getPAAPISizeHook = (() => { - /* - https://github.com/google/ads-privacy/tree/master/proposals/fledge-multiple-seller-testing#faq - https://support.google.com/admanager/answer/1100453?hl=en - - Ignore any placeholder sizes, where placeholder is defined as a square creative with a side of <= 5 pixels - Look if there are any sizes that are part of the set of supported ad sizes defined here. If there are, choose the largest supported size by area (width * height) - For clarity, the set of supported ad sizes includes all of the ad sizes listed under “Top-performing ad sizes”, “Other supported ad sizes”, and “Regional ad sizes”. - If not, choose the largest remaining size (i.e. that isn’t in the list of supported ad sizes) by area (width * height) - */ - const SUPPORTED_SIZES = [ - [728, 90], - [336, 280], - [300, 250], - [300, 50], - [160, 600], - [1024, 768], - [970, 250], - [970, 90], - [768, 1024], - [480, 320], - [468, 60], - [320, 480], - [320, 100], - [320, 50], - [300, 600], - [300, 100], - [250, 250], - [234, 60], - [200, 200], - [180, 150], - [125, 125], - [120, 600], - [120, 240], - [120, 60], - [88, 31], - [980, 120], - [980, 90], - [950, 90], - [930, 180], - [750, 300], - [750, 200], - [750, 100], - [580, 400], - [250, 360], - [240, 400], - ].sort(keyCompare(([w, h]) => -(w * h))) - .map(size => [size, sizeTupleToSizeString(size)]); - - return function(next, sizes) { - if (sizes?.length) { - const sizeStrings = new Set(sizes.map(sizeTupleToSizeString)); - const preferredSize = SUPPORTED_SIZES.find(([_, sizeStr]) => sizeStrings.has(sizeStr)); - if (preferredSize) { - next.bail(preferredSize[0]); - return; - } - } - next(sizes); - } -})(); - -export function setPAAPIConfigFactory( - getConfig = (filters) => getPAAPIConfig(filters, true), - setGptConfig = setComponentAuction, - getSlots = getGPTSlotsForAdUnits) { - /** - * Configure GPT slots with PAAPI auction configs. - * `filters` are the same filters accepted by `pbjs.getPAAPIConfig`; - */ - return function(filters = {}, customSlotMatching) { - let some = false; - const cfg = getConfig(filters) || {}; - const auToSlots = getSlots(Object.keys(cfg), customSlotMatching); - - Object.entries(cfg).forEach(([au, config]) => { - if (config != null) { - some = true; - } - setGptConfig(au, auToSlots[au], config?.componentAuctions || [], true); - }) - if (!some) { - logInfo(`${MODULE}: No component auctions available to set`); - } - } -} -/** - * Configure GPT slots with PAAPI component auctions. Accepts the same filter arguments as `pbjs.getPAAPIConfig`. - */ -getGlobal().setPAAPIConfigForGPT = setPAAPIConfigFactory(); -const setTargetingHook = setTargetingHookFactory(); - -submodule('paapi', { - name: 'gpt', - init(params) { - getPAAPIConfig = params.getPAAPIConfig; - getHook('getPAAPISize').before(getPAAPISizeHook); - } -}); diff --git a/modules/paapiForGpt.md b/modules/paapiForGpt.md deleted file mode 100644 index 8565987eb5b..00000000000 --- a/modules/paapiForGpt.md +++ /dev/null @@ -1,123 +0,0 @@ -# Overview -This module allows Prebid.js to support PAAPI by integrating it with GPT's [experimental PAAPI -support](https://github.com/google/ads-privacy/tree/master/proposals/fledge-multiple-seller-testing). - -To learn more about PAAPI in general, go [here](https://github.com/WICG/turtledove/blob/main/PAAPI.md). - -This document covers the steps necessary for publishers to enable PAAPI on their inventory. It also describes -the changes Bid Adapters need to implement in order to support PAAPI. - -## Publisher Integration -Publishers wishing to enable PAAPI support must do two things. First, they must compile Prebid.js with support for this module. -This is accomplished by adding the `paapiForGpt` module to the list of modules they are already using: - -``` -gulp build --modules=paapiForGpt,... -``` - -Second, they must enable PAAPI in their Prebid.js configuration. -This is done through module level configuration, but to provide a high degree of flexiblity for testing, PAAPI settings also exist the slot level. - -### Module Configuration -This module exposes the following settings: - -|Name |Type |Description |Notes | -| :------------ | :------------ | :------------ |:------------ | -|enabled | Boolean |Enable/disable the module |Defaults to `false` | -|bidders | Array[String] |Optional list of bidders |Defaults to all bidders | -|defaultForSlots | Number |Default value for `imp.ext.ae` in requests for specified bidders |Should be 1 | - -As noted above, PAAPI support is disabled by default. To enable it, set the `enabled` value to `true` for this module and configure `defaultForSlots` to be `1` (meaning _Client-side auction_). -using the `setConfig` method of Prebid.js. Optionally, a list of bidders to apply these settings to may be provided: - -```js -pbjs.que.push(function() { - pbjs.setConfig({ - paapi: { - enabled: true, - bidders: ['openx', 'rtbhouse'], - defaultForSlots: 1 - } - }); -}); -``` - -### AdUnit Configuration -All adunits can be opted-in to PAAPI in the global config via the `defaultForSlots` parameter. -If needed, adunits can be configured individually by setting an attribute of the `ortb2Imp` object for that -adunit. This attribute will take precedence over `defaultForSlots` setting. - -|Name |Type |Description |Notes | -| :------------ | :------------ | :------------ |:------------ | -| ortb2Imp.ext.ae | Integer | Auction Environment: 1 indicates PAAPI eligible, 0 indicates it is not | Absence indicates this is not PAAPI eligible | - -The `ae` field stands for Auction Environment and was chosen to be consistent with the field that GAM passes to bidders -in their Open Bidding and Exchange Bidding APIs. More details on that can be found -[here](https://github.com/google/ads-privacy/tree/master/proposals/fledge-rtb#bid-request-changes-indicating-interest-group-auction-support) -In practice, this looks as follows: - -```js -pbjs.addAdUnits({ - code: "my-adunit-div", - // other config here - ortb2Imp: { - ext: { - ae: 1 - } - } -}); -``` - -## Bid Adapter Integration -Chrome has enabled a two-tier auction in PAAPI. This allows multiple sellers (frequently SSPs) to act on behalf of the publisher with -a single entity serving as the final decision maker. In their [current approach](https://github.com/google/ads-privacy/tree/master/proposals/fledge-multiple-seller-testing), -GPT has opted to run the final auction layer while allowing other SSPs/sellers to participate as -[Component Auctions](https://github.com/WICG/turtledove/blob/main/PAAPI.md#21-initiating-an-on-device-auction) which feed their -bids to the final layer. To learn more about Component Auctions, go [here](https://github.com/WICG/turtledove/blob/main/PAAPI.md#24-scoring-bids-in-component-auctions). - -The PAAPI auction, including Component Auctions, are configured via an `AuctionConfig` object that defines the parameters of the auction for a given -seller. This module enables PAAPI support by allowing bid adaptors to return `AuctionConfig` objects in addition to bids. If a bid adaptor returns an -`AuctionConfig` object, Prebid.js will register it with the appropriate GPT ad slot so the bidder can participate as a Component Auction in the overall -PAAPI auction for that slot. More details on the GPT API can be found [here](https://developers.google.com/publisher-tag/reference#googletag.config.componentauctionconfig). - -Modifying a bid adapter to support PAAPI is a straightforward process and consists of the following steps: -1. Detecting when a bid request is PAAPI eligible -2. Responding with AuctionConfig - -PAAPI eligibility is made available to bid adapters through the `bidderRequest.paapi.enabled` field. -The [`bidderRequest`](https://docs.prebid.org/dev-docs/bidder-adaptor.html#bidderrequest-parameters) object is passed to -the [`buildRequests`](https://docs.prebid.org/dev-docs/bidder-adaptor.html#building-the-request) method of an adapter. Bid adapters -who wish to participate should read this flag and pass it to their server. PAAPI eligibility depends on a number of parameters: - -1. Chrome enablement -2. Publisher participatipon in the [Origin Trial](https://developer.chrome.com/docs/privacy-sandbox/unified-origin-trial/#configure) -3. Publisher Prebid.js configuration (detailed above) - -When a bid request is PAAPI enabled, a bid adapter can return a tuple consisting of bids and AuctionConfig objects rather than just a list of bids: - -```js -function interpretResponse(resp, req) { - // Load the bids from the response - this is adapter specific - const bids = parseBids(resp); - - // Load the auctionConfigs from the response - also adapter specific - const auctionConfigs = parseAuctionConfigs(resp); - - if (auctionConfigs) { - // Return a tuple of bids and auctionConfigs. It is possible that bids could be null. - return {bids, auctionConfigs}; - } else { - return bids; - } -} -``` - -An AuctionConfig must be associated with an adunit and auction, and this is accomplished using the value in the `bidId` field from the objects in the -`validBidRequests` array passed to the `buildRequests` function - see [here](https://docs.prebid.org/dev-docs/bidder-adaptor.html#ad-unit-params-in-the-validbidrequests-array) -for more details. This means that the AuctionConfig objects returned from `interpretResponse` must contain a `bidId` field whose value corresponds to -the request it should be associated with. This may raise the question: why isn't the AuctionConfig object returned as part of the bid? The -answer is that it's possible to participate in the PAAPI auction without returning a contextual bid. - -An example of this can be seen in the OpenX OpenRTB bid adapter [here](https://github.com/prebid/Prebid.js/blob/master/modules/openxOrtbBidAdapter.js#L327). - -Other than the addition of the `bidId` field, the AuctionConfig object should adhere to the requirements set forth in PAAPI. The details of creating an AuctionConfig object are beyond the scope of this document. diff --git a/modules/padsquadBidAdapter.js b/modules/padsquadBidAdapter.js index 48471fc98e3..0a6afd6b448 100644 --- a/modules/padsquadBidAdapter.js +++ b/modules/padsquadBidAdapter.js @@ -1,6 +1,6 @@ -import {deepAccess, logInfo} from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER} from '../src/mediaTypes.js'; +import { deepAccess, logInfo } from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER } from '../src/mediaTypes.js'; const ENDPOINT_URL = 'https://x.padsquad.com/auction'; @@ -57,8 +57,8 @@ export const spec = { // apply gdpr if (bidderRequest.gdprConsent) { - openrtbRequest.regs = {ext: {gdpr: bidderRequest.gdprConsent.gdprApplies ? 1 : 0}}; - openrtbRequest.user = {ext: {consent: bidderRequest.gdprConsent.consentString}}; + openrtbRequest.regs = { ext: { gdpr: bidderRequest.gdprConsent.gdprApplies ? 1 : 0 } }; + openrtbRequest.user = { ext: { consent: bidderRequest.gdprConsent.consentString } }; } const payloadString = JSON.stringify(openrtbRequest); diff --git a/modules/pairIdSystem.js b/modules/pairIdSystem.js index 4b1e287e957..fe4e761740e 100644 --- a/modules/pairIdSystem.js +++ b/modules/pairIdSystem.js @@ -6,9 +6,9 @@ */ import { submodule } from '../src/hook.js'; -import {getStorageManager} from '../src/storageManager.js' +import { getStorageManager } from '../src/storageManager.js' import { logInfo } from '../src/utils.js'; -import {MODULE_TYPE_UID} from '../src/activities/modules.js'; +import { MODULE_TYPE_UID } from '../src/activities/modules.js'; /** * @typedef {import('../modules/userId/index.js').Submodule} Submodule @@ -18,7 +18,7 @@ const MODULE_NAME = 'pairId'; const PAIR_ID_KEY = 'pairId'; const DEFAULT_LIVERAMP_PAIR_ID_KEY = '_lr_pairId'; -export const storage = getStorageManager({moduleType: MODULE_TYPE_UID, moduleName: MODULE_NAME}); +export const storage = getStorageManager({ moduleType: MODULE_TYPE_UID, moduleName: MODULE_NAME }); function pairIdFromLocalStorage(key) { return storage.localStorageIsEnabled() ? storage.getDataFromLocalStorage(key) : null; @@ -47,7 +47,7 @@ export const pairIdSubmodule = { * @returns {{pairId:string} | undefined } */ decode(value) { - return value && Array.isArray(value) ? {'pairId': value} : undefined + return value && Array.isArray(value) ? { 'pairId': value } : undefined }, /** * Performs action to obtain ID and return a value in the callback's response argument. @@ -98,7 +98,7 @@ export const pairIdSubmodule = { logInfo('PairId not found.') return undefined; } - return {'id': ids}; + return { 'id': ids }; }, eids: { 'pairId': { diff --git a/modules/pangleBidAdapter.js b/modules/pangleBidAdapter.js index c22a44687a2..a92b2a13910 100644 --- a/modules/pangleBidAdapter.js +++ b/modules/pangleBidAdapter.js @@ -114,7 +114,7 @@ const converter = ortbConverter({ context.mediaType = VIDEO; bidResponse = buildBidResponse(bid, context); if (bidRequest.mediaTypes.video?.context === 'outstream') { - const renderer = Renderer.install({id: bid.bidId, url: OUTSTREAM_RENDERER_URL, adUnitCode: bid.adUnitCode}); + const renderer = Renderer.install({ id: bid.bidId, url: OUTSTREAM_RENDERER_URL, adUnitCode: bid.adUnitCode }); renderer.setRender(renderOutstream); bidResponse.renderer = renderer; } diff --git a/modules/panxoBidAdapter.js b/modules/panxoBidAdapter.js new file mode 100644 index 00000000000..5de64af33ff --- /dev/null +++ b/modules/panxoBidAdapter.js @@ -0,0 +1,358 @@ +/** + * @module modules/panxoBidAdapter + * @description Bid Adapter for Prebid.js - AI-referred traffic monetization + * @see https://docs.panxo.ai for Signal script installation + */ + +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER } from '../src/mediaTypes.js'; +import { deepAccess, logWarn, isFn, isPlainObject } from '../src/utils.js'; +import { getStorageManager } from '../src/storageManager.js'; +import { getDNT } from '../libraries/dnt/index.js'; + +const BIDDER_CODE = 'panxo'; +const ENDPOINT_URL = 'https://panxo-sys.com/openrtb/2.5/bid'; +const USER_ID_KEY = 'panxo_uid'; +const SYNC_URL = 'https://panxo-sys.com/usersync'; +const DEFAULT_CURRENCY = 'USD'; +const TTL = 300; +const NET_REVENUE = true; + +export const storage = getStorageManager({ bidderCode: BIDDER_CODE }); + +export function getPanxoUserId() { + try { + return storage.getDataFromLocalStorage(USER_ID_KEY); + } catch (e) { + // storageManager handles errors internally + } + return null; +} + +function buildBanner(bid) { + const sizes = deepAccess(bid, 'mediaTypes.banner.sizes') || bid.sizes || []; + if (sizes.length === 0) return null; + + return { + format: sizes.map(size => ({ w: size[0], h: size[1] })), + w: sizes[0][0], + h: sizes[0][1] + }; +} + +function getFloorPrice(bid, size) { + if (isFn(bid.getFloor)) { + try { + const floorInfo = bid.getFloor({ + currency: DEFAULT_CURRENCY, + mediaType: BANNER, + size: size + }); + if (floorInfo && floorInfo.floor) { + return floorInfo.floor; + } + } catch (e) { + // Floor module error + } + } + return deepAccess(bid, 'params.floor') || 0; +} + +function buildUser(panxoUid, bidderRequest) { + const user = { buyeruid: panxoUid }; + + // GDPR consent + const gdprConsent = deepAccess(bidderRequest, 'gdprConsent'); + if (gdprConsent && gdprConsent.consentString) { + user.ext = { consent: gdprConsent.consentString }; + } + + // First Party Data - user + const fpd = deepAccess(bidderRequest, 'ortb2.user'); + if (isPlainObject(fpd)) { + user.ext = { ...user.ext, ...fpd.ext }; + if (fpd.data) user.data = fpd.data; + } + + return user; +} + +function buildRegs(bidderRequest) { + const regs = { ext: {} }; + + // GDPR - only set when gdprApplies is explicitly true or false, not undefined + const gdprConsent = deepAccess(bidderRequest, 'gdprConsent'); + if (gdprConsent && typeof gdprConsent.gdprApplies === 'boolean') { + regs.ext.gdpr = gdprConsent.gdprApplies ? 1 : 0; + } + + // CCPA / US Privacy + const uspConsent = deepAccess(bidderRequest, 'uspConsent'); + if (uspConsent) { + regs.ext.us_privacy = uspConsent; + } + + // GPP + const gppConsent = deepAccess(bidderRequest, 'gppConsent'); + if (gppConsent) { + regs.ext.gpp = gppConsent.gppString; + regs.ext.gpp_sid = gppConsent.applicableSections; + } + + // COPPA + const coppa = deepAccess(bidderRequest, 'ortb2.regs.coppa'); + if (coppa) { + regs.coppa = 1; + } + + return regs; +} + +function buildDevice() { + const device = { + ua: navigator.userAgent, + language: navigator.language, + js: 1, + dnt: getDNT() ? 1 : 0 + }; + + if (typeof screen !== 'undefined') { + device.w = screen.width; + device.h = screen.height; + } + + return device; +} + +function buildSite(bidderRequest) { + const site = { + page: deepAccess(bidderRequest, 'refererInfo.page') || '', + domain: deepAccess(bidderRequest, 'refererInfo.domain') || '', + ref: deepAccess(bidderRequest, 'refererInfo.ref') || '' + }; + + // First Party Data - site + const fpd = deepAccess(bidderRequest, 'ortb2.site'); + if (isPlainObject(fpd)) { + Object.assign(site, { + name: fpd.name, + cat: fpd.cat, + sectioncat: fpd.sectioncat, + pagecat: fpd.pagecat, + content: fpd.content + }); + if (fpd.ext) site.ext = fpd.ext; + } + + return site; +} + +function buildSource(bidderRequest) { + const source = { + tid: deepAccess(bidderRequest, 'ortb2.source.tid') || bidderRequest.auctionId + }; + + // Supply Chain (schain) - read from ortb2 where Prebid normalizes it + const schain = deepAccess(bidderRequest, 'ortb2.source.ext.schain'); + if (isPlainObject(schain)) { + source.ext = { schain: schain }; + } + + return source; +} + +export const spec = { + code: BIDDER_CODE, + gvlid: 1527, // IAB TCF Global Vendor List ID + supportedMediaTypes: [BANNER], + + isBidRequestValid(bid) { + const propertyKey = deepAccess(bid, 'params.propertyKey'); + if (!propertyKey) { + logWarn('Panxo: Missing required param "propertyKey"'); + return false; + } + if (!deepAccess(bid, 'mediaTypes.banner')) { + logWarn('Panxo: Only banner mediaType is supported'); + return false; + } + return true; + }, + + buildRequests(validBidRequests, bidderRequest) { + const panxoUid = getPanxoUserId(); + if (!panxoUid) { + logWarn('Panxo: panxo_uid not found. Ensure Signal script is loaded before Prebid.'); + return []; + } + + // Group bids by propertyKey to handle multiple properties on same page + const bidsByPropertyKey = {}; + validBidRequests.forEach(bid => { + const key = deepAccess(bid, 'params.propertyKey'); + if (!bidsByPropertyKey[key]) { + bidsByPropertyKey[key] = []; + } + bidsByPropertyKey[key].push(bid); + }); + + // Build one request per propertyKey + const requests = []; + Object.keys(bidsByPropertyKey).forEach(propertyKey => { + const bidsForKey = bidsByPropertyKey[propertyKey]; + + const impressions = bidsForKey.map((bid) => { + const banner = buildBanner(bid); + if (!banner) return null; + + const sizes = deepAccess(bid, 'mediaTypes.banner.sizes') || []; + const primarySize = sizes[0] || [300, 250]; + + // Build impression object + const imp = { + id: bid.bidId, + banner: banner, + bidfloor: getFloorPrice(bid, primarySize), + bidfloorcur: DEFAULT_CURRENCY, + secure: 1, + tagid: bid.adUnitCode + }; + + // Merge full ortb2Imp object (instl, pmp, ext, etc.) + const ortb2Imp = deepAccess(bid, 'ortb2Imp'); + if (isPlainObject(ortb2Imp)) { + Object.keys(ortb2Imp).forEach(key => { + if (key === 'ext') { + imp.ext = { ...imp.ext, ...ortb2Imp.ext }; + } else if (imp[key] === undefined) { + imp[key] = ortb2Imp[key]; + } + }); + } + + return imp; + }).filter(Boolean); + + if (impressions.length === 0) return; + + const openrtbRequest = { + id: bidderRequest.bidderRequestId, + imp: impressions, + site: buildSite(bidderRequest), + device: buildDevice(), + user: buildUser(panxoUid, bidderRequest), + regs: buildRegs(bidderRequest), + source: buildSource(bidderRequest), + at: 1, + cur: [DEFAULT_CURRENCY], + tmax: bidderRequest.timeout || 1000 + }; + + requests.push({ + method: 'POST', + url: `${ENDPOINT_URL}?key=${encodeURIComponent(propertyKey)}&source=prebid`, + data: openrtbRequest, + options: { contentType: 'text/plain', withCredentials: false }, + bidderRequest: bidderRequest + }); + }); + + return requests; + }, + + interpretResponse(serverResponse, request) { + const bids = []; + const response = serverResponse.body; + + if (!response || !response.seatbid) return bids; + + const bidRequestMap = {}; + if (request.bidderRequest && request.bidderRequest.bids) { + request.bidderRequest.bids.forEach(bid => { + bidRequestMap[bid.bidId] = bid; + }); + } + + response.seatbid.forEach(seatbid => { + if (!seatbid.bid) return; + + seatbid.bid.forEach(bid => { + const originalBid = bidRequestMap[bid.impid]; + if (!originalBid) return; + + bids.push({ + requestId: bid.impid, + cpm: bid.price, + currency: response.cur || DEFAULT_CURRENCY, + width: bid.w, + height: bid.h, + creativeId: bid.crid || bid.id, + dealId: bid.dealid || null, + netRevenue: NET_REVENUE, + ttl: bid.exp || TTL, + ad: bid.adm, + nurl: bid.nurl, + meta: { + advertiserDomains: bid.adomain || [], + mediaType: BANNER + } + }); + }); + }); + + return bids; + }, + + getUserSyncs(syncOptions, serverResponses, gdprConsent, uspConsent, gppConsent) { + const syncs = []; + + if (syncOptions.pixelEnabled) { + let syncUrl = SYNC_URL + '?source=prebid'; + + // GDPR - only include when gdprApplies is explicitly true or false + if (gdprConsent) { + if (typeof gdprConsent.gdprApplies === 'boolean') { + syncUrl += `&gdpr=${gdprConsent.gdprApplies ? 1 : 0}`; + } + if (gdprConsent.consentString) { + syncUrl += `&gdpr_consent=${encodeURIComponent(gdprConsent.consentString)}`; + } + } + + // US Privacy + if (uspConsent) { + syncUrl += `&us_privacy=${encodeURIComponent(uspConsent)}`; + } + + // GPP + if (gppConsent) { + if (gppConsent.gppString) { + syncUrl += `&gpp=${encodeURIComponent(gppConsent.gppString)}`; + } + if (gppConsent.applicableSections) { + syncUrl += `&gpp_sid=${encodeURIComponent(gppConsent.applicableSections.join(','))}`; + } + } + + syncs.push({ type: 'image', url: syncUrl }); + } + + return syncs; + }, + + onBidWon(bid) { + if (bid.nurl) { + const winUrl = bid.nurl.replace(/\$\{AUCTION_PRICE\}/g, bid.cpm); + const img = document.createElement('img'); + img.src = winUrl; + img.style.display = 'none'; + document.body.appendChild(img); + } + }, + + onTimeout(timeoutData) { + logWarn('Panxo: Bid timeout', timeoutData); + } +}; + +registerBidder(spec); diff --git a/modules/panxoBidAdapter.md b/modules/panxoBidAdapter.md new file mode 100644 index 00000000000..17a1df185f9 --- /dev/null +++ b/modules/panxoBidAdapter.md @@ -0,0 +1,229 @@ +# Overview + +``` +Module Name: Panxo Bid Adapter +Module Type: Bidder Adapter +Maintainer: tech@panxo.ai +``` + +# Description + +Panxo is a specialized SSP for AI-referred traffic monetization. This adapter enables publishers to monetize traffic coming from AI assistants like ChatGPT, Perplexity, Claude, and Gemini through Prebid.js header bidding. + +**Important**: This adapter requires the Panxo Signal script to be installed on the publisher's page. The Signal script must load before Prebid.js to ensure proper user identification and AI traffic detection. + +# Prerequisites + +1. Register your property at [app.panxo.ai](https://app.panxo.ai) +2. Obtain your `propertyKey` from the Panxo dashboard +3. Install the Panxo Signal script in your page's ``: + +```html + +``` + +# Bid Params + +| Name | Scope | Description | Example | Type | +|------|-------|-------------|---------|------| +| `propertyKey` | required | Your unique property identifier from Panxo dashboard | `'abc123def456'` | `string` | +| `floor` | optional | Minimum CPM floor price in USD | `0.50` | `number` | + +# Configuration Example + +```javascript +var adUnits = [{ + code: 'banner-ad', + mediaTypes: { + banner: { + sizes: [[300, 250], [728, 90]] + } + }, + bids: [{ + bidder: 'panxo', + params: { + propertyKey: 'your-property-key-here' + } + }] +}]; +``` + +# Full Page Example + +```html + + + + + + + + + + + + +
+ + +``` + +# Supported Media Types + +| Type | Support | +|------|---------| +| Banner | Yes | +| Video | No | +| Native | No | + +# Privacy & Consent + +**IAB TCF Global Vendor List ID: 1527** + +This adapter is registered with the IAB Europe Transparency and Consent Framework. Publishers using a CMP (Consent Management Platform) should ensure Panxo (Vendor ID 1527) is included in their vendor list. + +This adapter supports: + +- **GDPR/TCF 2.0**: Consent string is passed in bid requests. GVL ID: 1527 +- **CCPA/US Privacy**: USP string is passed in bid requests +- **GPP**: Global Privacy Platform strings are supported +- **COPPA**: Child-directed content flags are respected + +## CMP Configuration + +If you use a Consent Management Platform (Cookiebot, OneTrust, Quantcast Choice, etc.), ensure that: + +1. Panxo (Vendor ID: 1527) is included in your vendor list +2. Users can grant/deny consent specifically for Panxo +3. The CMP loads before Prebid.js to ensure consent is available + +Example TCF configuration with Prebid: + +```javascript +pbjs.setConfig({ + consentManagement: { + gdpr: { + cmpApi: 'iab', + timeout: 8000, + defaultGdprScope: true + }, + usp: { + cmpApi: 'iab', + timeout: 1000 + } + } +}); +``` + +# User Sync + +Panxo supports pixel-based user sync. Enable it in your Prebid configuration: + +```javascript +pbjs.setConfig({ + userSync: { + filterSettings: { + pixel: { + bidders: ['panxo'], + filter: 'include' + } + } + } +}); +``` + +# First Party Data + +This adapter supports First Party Data via the `ortb2` configuration: + +```javascript +pbjs.setConfig({ + ortb2: { + site: { + name: 'Example Site', + cat: ['IAB1'], + content: { + keywords: 'technology, ai' + } + }, + user: { + data: [{ + name: 'example-data-provider', + segment: [{ id: 'segment-1' }] + }] + } + } +}); +``` + +# Supply Chain (schain) + +Supply chain information is automatically passed when configured: + +```javascript +pbjs.setConfig({ + schain: { + validation: 'relaxed', + config: { + ver: '1.0', + complete: 1, + nodes: [{ + asi: 'publisher-domain.com', + sid: '12345', + hp: 1 + }] + } + } +}); +``` + +# Floor Prices + +This adapter supports the Prebid Price Floors Module. Configure floors as needed: + +```javascript +pbjs.setConfig({ + floors: { + enforcement: { floorDeals: true }, + data: { + default: 0.50, + schema: { fields: ['mediaType'] }, + values: { 'banner': 0.50 } + } + } +}); +``` + +# Win Notifications + +This adapter automatically fires win notification URLs (nurl) when a bid wins the auction. No additional configuration is required. + +# Contact + +For support or questions: +- Email: tech@panxo.ai +- Documentation: https://docs.panxo.ai diff --git a/modules/panxoRtdProvider.js b/modules/panxoRtdProvider.js new file mode 100644 index 00000000000..5865106e564 --- /dev/null +++ b/modules/panxoRtdProvider.js @@ -0,0 +1,227 @@ +/** + * This module adds Panxo AI traffic classification to the real time data module. + * + * The {@link module:modules/realTimeData} module is required. + * The module injects the Panxo signal collection script, enriching bid requests + * with AI traffic classification data and contextual signals for improved targeting. + * @module modules/panxoRtdProvider + * @requires module:modules/realTimeData + */ + +import { submodule } from '../src/hook.js'; +import { + prefixLog, + mergeDeep, + generateUUID, + getWindowSelf, +} from '../src/utils.js'; +import { getRefererInfo } from '../src/refererDetection.js'; +import { getGlobal } from '../src/prebidGlobal.js'; +import { loadExternalScript } from '../src/adloader.js'; +import { MODULE_TYPE_RTD } from '../src/activities/modules.js'; + +/** + * @typedef {import('../modules/rtdModule/index.js').RtdSubmodule} RtdSubmodule + * @typedef {import('../modules/rtdModule/index.js').SubmoduleConfig} SubmoduleConfig + * @typedef {import('../modules/rtdModule/index.js').UserConsentData} UserConsentData + */ + +const SUBMODULE_NAME = 'panxo'; +const SCRIPT_URL = 'https://api.idsequoia.ai/rtd.js'; + +const { logWarn, logError } = prefixLog(`[${SUBMODULE_NAME}]:`); + +/** @type {string} */ +let siteId = ''; + +/** @type {boolean} */ +let verbose = false; + +/** @type {string} */ +let sessionId = ''; + +/** @type {Object} */ +let panxoData = {}; + +/** @type {boolean} */ +let implReady = false; + +/** @type {Array} */ +let pendingCallbacks = []; + +/** + * Submodule registration + */ +function main() { + submodule('realTimeData', /** @type {RtdSubmodule} */ ({ + name: SUBMODULE_NAME, + + init: (config, userConsent) => { + try { + load(config); + return true; + } catch (err) { + logError('init', err.message); + return false; + } + }, + + getBidRequestData: onGetBidRequestData + })); +} + +/** + * Validates configuration and loads the Panxo signal collection script. + * @param {SubmoduleConfig} config + */ +function load(config) { + siteId = config?.params?.siteId || ''; + if (!siteId || typeof siteId !== 'string') { + throw new Error(`The 'siteId' parameter is required and must be a string`); + } + + // siteId is a 16-character hex hash identifying the publisher property + if (!/^[a-f0-9]{16}$/.test(siteId)) { + throw new Error(`The 'siteId' parameter must be a valid 16-character hex identifier`); + } + + // Load/reset the state + verbose = !!config?.params?.verbose; + sessionId = generateUUID(); + panxoData = {}; + implReady = false; + pendingCallbacks = []; + + const refDomain = getRefererInfo().domain || ''; + + // The implementation script uses the session parameter to register + // a bridge API on window['panxo_' + sessionId] + const scriptUrl = `${SCRIPT_URL}?siteId=${siteId}&session=${sessionId}&r=${refDomain}`; + + loadExternalScript(scriptUrl, MODULE_TYPE_RTD, SUBMODULE_NAME, onImplLoaded); +} + +/** + * Callback invoked when the external script finishes loading. + * Establishes the bridge between this RTD submodule and the implementation. + */ +function onImplLoaded() { + const wnd = getWindowSelf(); + const impl = wnd[`panxo_${sessionId}`]; + if (typeof impl !== 'object' || typeof impl.connect !== 'function') { + if (verbose) logWarn('onload', 'Unable to access the implementation script'); + if (!implReady) { + implReady = true; + flushPendingCallbacks(); + } + return; + } + + // Set up the bridge. The callback may be called multiple times as + // more precise signal data becomes available. + impl.connect(getGlobal(), onImplMessage); +} + +/** + * Bridge callback invoked by the implementation script to update signal data. + * When the first signal arrives, flushes any pending auction callbacks so + * the auction can proceed with enriched data. + * @param {Object} msg + */ +function onImplMessage(msg) { + if (!msg || typeof msg !== 'object') { + return; + } + + switch (msg.type) { + case 'signal': { + panxoData = mergeDeep({}, msg.data || {}); + if (!implReady) { + implReady = true; + flushPendingCallbacks(); + } + break; + } + case 'error': { + logError('impl', msg.data || ''); + if (!implReady) { + implReady = true; + flushPendingCallbacks(); + } + break; + } + } +} + +/** + * Flush all pending getBidRequestData callbacks. + * Called when the implementation script sends its first signal. + */ +function flushPendingCallbacks() { + const cbs = pendingCallbacks.splice(0); + cbs.forEach(cb => cb()); +} + +/** + * Called once per auction to enrich bid request ORTB data. + * + * If the implementation script has already sent signal data, enrichment + * happens synchronously and the callback fires immediately. Otherwise the + * callback is deferred until the first signal arrives. The Prebid RTD + * framework enforces `auctionDelay` as the upper bound on this wait, so + * the auction is never blocked indefinitely. + * + * Adds the following fields: + * - device.ext.panxo: session signal token for traffic verification + * - site.ext.data.panxo: contextual AI traffic classification data + * + * @param {Object} reqBidsConfigObj + * @param {function} callback + * @param {SubmoduleConfig} config + * @param {UserConsentData} userConsent + */ +function onGetBidRequestData(reqBidsConfigObj, callback, config, userConsent) { + function enrichAndDone() { + const ortb2 = {}; + + // Add device-level signal (opaque session token) + if (panxoData.device) { + mergeDeep(ortb2, { device: { ext: { panxo: panxoData.device } } }); + } + + // Add site-level contextual data (AI classification) + if (panxoData.site && Object.keys(panxoData.site).length > 0) { + mergeDeep(ortb2, { site: { ext: { data: { panxo: panxoData.site } } } }); + } + + mergeDeep(reqBidsConfigObj.ortb2Fragments.global, ortb2); + callback(); + } + + // If data already arrived, proceed immediately + if (implReady) { + enrichAndDone(); + return; + } + + // Otherwise, wait for the implementation script to send its first signal. + // The auctionDelay configured by the publisher (e.g. 1500ms) acts as the + // maximum wait time -- Prebid will call our callback when it expires. + pendingCallbacks.push(enrichAndDone); +} + +/** + * Exporting local functions for testing purposes. + */ +export const __TEST__ = { + SUBMODULE_NAME, + SCRIPT_URL, + main, + load, + onImplLoaded, + onImplMessage, + onGetBidRequestData, + flushPendingCallbacks +}; + +main(); diff --git a/modules/panxoRtdProvider.md b/modules/panxoRtdProvider.md new file mode 100644 index 00000000000..bc9be90d152 --- /dev/null +++ b/modules/panxoRtdProvider.md @@ -0,0 +1,45 @@ +# Overview + +``` +Module Name: Panxo RTD Provider +Module Type: RTD Provider +Maintainer: prebid@panxo.ai +``` + +# Description + +The Panxo RTD module enriches OpenRTB bid requests with real-time AI traffic classification signals. It detects visits originating from AI assistants and provides contextual data through `device.ext.panxo` and `site.ext.data.panxo`, enabling the Panxo Bid Adapter and other demand partners to apply differentiated bidding on AI-referred inventory. + +To use this module, contact [publishers@panxo.ai](mailto:publishers@panxo.ai) or sign up at [app.panxo.com](https://app.panxo.com) to receive your property identifier. + +# Build + +```bash +gulp build --modules=rtdModule,panxoRtdProvider,... +``` + +> `rtdModule` is required to use the Panxo RTD module. + +# Configuration + +```javascript +pbjs.setConfig({ + realTimeData: { + auctionDelay: 300, + dataProviders: [{ + name: 'panxo', + waitForIt: true, + params: { + siteId: 'a1b2c3d4e5f67890' + } + }] + } +}); +``` + +## Parameters + +| Name | Type | Description | Required | +| :-------- | :------ | :----------------------------------------------------- | :------- | +| `siteId` | String | 16-character hex property identifier provided by Panxo | Yes | +| `verbose` | Boolean | Enable verbose logging for troubleshooting | No | diff --git a/modules/performaxBidAdapter.js b/modules/performaxBidAdapter.js index 48dd4366f1d..04208811f1b 100644 --- a/modules/performaxBidAdapter.js +++ b/modules/performaxBidAdapter.js @@ -1,12 +1,95 @@ -import { deepSetValue, deepAccess } from '../src/utils.js'; +import { logWarn, logError, deepSetValue, deepAccess, safeJSONEncode, debugTurnedOn } from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER } from '../src/mediaTypes.js' import { ortbConverter } from '../libraries/ortbConverter/converter.js'; +import { getStorageManager } from '../src/storageManager.js'; +import { ajax } from '../src/ajax.js'; const BIDDER_CODE = 'performax'; const BIDDER_SHORT_CODE = 'px'; const GVLID = 732 const ENDPOINT = 'https://dale.performax.cz/ortb' +const USER_SYNC_URL = 'https://cdn.performax.cz/px2/cookie_sync_bundle.html'; +const USER_SYNC_ORIGIN = 'https://cdn.performax.cz'; +const UIDS_STORAGE_KEY = BIDDER_SHORT_CODE + '_uids'; +const LOG_EVENT_URL = 'https://chip.performax.cz/error'; +const LOG_EVENT_SAMPLE_RATE = 1; +const LOG_EVENT_TYPE_BIDDER_ERROR = 'bidderError'; +const LOG_EVENT_TYPE_INTERVENTION = 'intervention'; +const LOG_EVENT_TYPE_TIMEOUT = 'timeout'; + +let isUserSyncsInit = false; + +export const storage = getStorageManager({ bidderCode: BIDDER_CODE }); + +/** + * Sends diagnostic events. + * @param {string} type - The category of the event + * @param {Object|Array|string} payload - The data to be logged + * @param {number} [sampleRate=LOG_EVENT_SAMPLE_RATE] - The probability of logging the event + * @returns {void} + */ +function logEvent(type, payload, sampleRate = LOG_EVENT_SAMPLE_RATE) { + if (sampleRate <= Math.random()) { + return; + } + + const data = { type, payload }; + const options = { method: 'POST', withCredentials: true, contentType: 'application/json' }; + + ajax(LOG_EVENT_URL, undefined, safeJSONEncode(data), options); +} + +/** + * Serializes and stores data. + * @param {string} key - The unique identifier + * @param {any} value - The data to store + * @returns {void} + */ +export function storeData(key, value) { + if (!storage.localStorageIsEnabled()) { + if (debugTurnedOn()) logWarn('Local Storage is not enabled'); + return; + } + + try { + storage.setDataInLocalStorage(key, JSON.stringify(value)); + } catch (err) { + logError('Failed to store data: ', err); + } +} + +/** + * Retrieves and parses data. + * @param {string} key - The unique identifier + * @param {any} defaultValue - The value to return if the key is missing or parsing fails. + * @returns {any} The parsed data + */ +export function readData(key, defaultValue) { + if (!storage.localStorageIsEnabled()) { + if (debugTurnedOn()) logWarn('Local Storage is not enabled'); + return defaultValue; + } + + let rawData = storage.getDataFromLocalStorage(key); + + if (rawData === null) { + return defaultValue; + } + + try { + const parsed = JSON.parse(rawData); + return (parsed !== null && typeof parsed === 'object' && !Array.isArray(parsed)) ? parsed : defaultValue; + } catch (err) { + logError(`Error parsing data for key "${key}": `, err); + return defaultValue; + } +} + +export function resetUserSyncsInit() { + isUserSyncsInit = false; +} + export const converter = ortbConverter({ imp(buildImp, bidRequest, context) { @@ -39,11 +122,28 @@ export const spec = { }, buildRequests: function (bidRequests, bidderRequest) { - const data = converter.toORTB({bidderRequest, bidRequests}) + const data = converter.toORTB({ bidderRequest, bidRequests }) + + const uids = readData(UIDS_STORAGE_KEY, {}); + if (Object.keys(uids).length > 0) { + if (!data.user) { + data.user = {}; + } + + if (!data.user.ext) { + data.user.ext = {}; + } + + data.user.ext.uids = { + ...uids, + ...(data.user.ext.uids ?? {}) + }; + } + return [{ method: 'POST', url: ENDPOINT, - options: {'contentType': 'application/json'}, + options: { 'contentType': 'application/json' }, data: data }] }, @@ -71,7 +171,61 @@ export const spec = { }; return converter.fromORTB({ response: data, request: request.data }).bids }, + getUserSyncs: function(syncOptions, serverResponses, gdprConsent) { + const syncs = []; + + if (!syncOptions.iframeEnabled) { + if (debugTurnedOn()) { + logWarn('User sync is supported only via iframe'); + } + return syncs; + } + + let url = USER_SYNC_URL; + + if (gdprConsent) { + if (typeof gdprConsent.gdprApplies === 'boolean') { + url += `?gdpr=${Number(gdprConsent.gdprApplies)}&gdpr_consent=${gdprConsent.consentString}`; + } else { + url += `?gdpr_consent=${gdprConsent.consentString}`; + } + } + syncs.push({ + type: 'iframe', + url: url + }); + + if (!isUserSyncsInit) { + window.addEventListener('message', function (event) { + if (!event.data || event.origin !== USER_SYNC_ORIGIN || !event.data.flexo_sync_cookie) { + return; + } + + const { uid, vendor } = event.data.flexo_sync_cookie; + + if (!uid || !vendor) { + return; + } + + const uids = readData(UIDS_STORAGE_KEY, {}); + uids[vendor] = uid; + storeData(UIDS_STORAGE_KEY, uids); + }); + isUserSyncsInit = true; + } + + return syncs; + }, + onTimeout: function(timeoutData) { + logEvent(LOG_EVENT_TYPE_TIMEOUT, timeoutData); + }, + onBidderError: function({ bidderRequest }) { + logEvent(LOG_EVENT_TYPE_BIDDER_ERROR, bidderRequest); + }, + onIntervention: function({ bid }) { + logEvent(LOG_EVENT_TYPE_INTERVENTION, bid); + } } registerBidder(spec); diff --git a/modules/permutiveIdentityManagerIdSystem.js b/modules/permutiveIdentityManagerIdSystem.js index cbd2a1b0d2b..4f6404d842a 100644 --- a/modules/permutiveIdentityManagerIdSystem.js +++ b/modules/permutiveIdentityManagerIdSystem.js @@ -1,9 +1,9 @@ -import {MODULE_TYPE_UID} from '../src/activities/modules.js' -import {submodule} from '../src/hook.js' -import {getStorageManager} from '../src/storageManager.js' -import {deepAccess, prefixLog, safeJSONParse} from '../src/utils.js' -import {hasPurposeConsent} from '../libraries/permutiveUtils/index.js' -import {VENDORLESS_GVLID} from "../src/consentHandler.js"; +import { MODULE_TYPE_UID } from '../src/activities/modules.js' +import { submodule } from '../src/hook.js' +import { getStorageManager } from '../src/storageManager.js' +import { deepAccess, prefixLog, safeJSONParse } from '../src/utils.js' +import { hasPurposeConsent } from '../libraries/permutiveUtils/index.js' +import { VENDORLESS_GVLID } from "../src/consentHandler.js"; /** * @typedef {import('../modules/userId/index.js').Submodule} Submodule * @typedef {import('../modules/userId/index.js').SubmoduleConfig} SubmoduleConfig @@ -21,7 +21,7 @@ const GOOGLE_DOMAIN = 'google.com' const PRIMARY_IDS = ['id5id', 'idl_env', 'uid2', 'pairId'] -export const storage = getStorageManager({moduleType: MODULE_TYPE_UID, moduleName: MODULE_NAME}) +export const storage = getStorageManager({ moduleType: MODULE_TYPE_UID, moduleName: MODULE_NAME }) const logger = prefixLog('[PermutiveID]') @@ -107,7 +107,7 @@ export const permutiveIdentityManagerIdSubmodule = { } catch (e) { logger.logInfo('Error parsing pairId') } - return pairId === undefined ? value : {...value, pairId} + return pairId === undefined ? value : { ...value, pairId } }, /** diff --git a/modules/permutiveRtdProvider.js b/modules/permutiveRtdProvider.js index 972154e3933..09697b6f906 100644 --- a/modules/permutiveRtdProvider.js +++ b/modules/permutiveRtdProvider.js @@ -5,14 +5,14 @@ * @module modules/permutiveRtdProvider * @requires module:modules/realTimeData */ -import {getGlobal} from '../src/prebidGlobal.js'; -import {submodule} from '../src/hook.js'; -import {getStorageManager} from '../src/storageManager.js'; -import {deepAccess, deepSetValue, isFn, logError, mergeDeep, isPlainObject, safeJSONParse, prefixLog} from '../src/utils.js'; -import {VENDORLESS_GVLID} from '../src/consentHandler.js'; -import {hasPurposeConsent} from '../libraries/permutiveUtils/index.js'; +import { getGlobal } from '../src/prebidGlobal.js'; +import { submodule } from '../src/hook.js'; +import { getStorageManager } from '../src/storageManager.js'; +import { deepAccess, deepSetValue, isFn, logError, mergeDeep, isPlainObject, safeJSONParse, prefixLog } from '../src/utils.js'; +import { VENDORLESS_GVLID } from '../src/consentHandler.js'; +import { hasPurposeConsent } from '../libraries/permutiveUtils/index.js'; -import {MODULE_TYPE_RTD} from '../src/activities/modules.js'; +import { MODULE_TYPE_RTD } from '../src/activities/modules.js'; /** * @typedef {import('../modules/rtdModule/index.js').RtdSubmodule} RtdSubmodule @@ -27,7 +27,7 @@ export const PERMUTIVE_STANDARD_KEYWORD = 'p_standard' export const PERMUTIVE_CUSTOM_COHORTS_KEYWORD = 'permutive' export const PERMUTIVE_STANDARD_AUD_KEYWORD = 'p_standard_aud' -export const storage = getStorageManager({moduleType: MODULE_TYPE_RTD, moduleName: MODULE_NAME}) +export const storage = getStorageManager({ moduleType: MODULE_TYPE_RTD, moduleName: MODULE_NAME }) function init(moduleConfig, userConsent) { readPermutiveModuleConfigFromCache() diff --git a/modules/prebidServerBidAdapter/bidderConfig.js b/modules/prebidServerBidAdapter/bidderConfig.js index b009bb9bcfa..f860f0337b4 100644 --- a/modules/prebidServerBidAdapter/bidderConfig.js +++ b/modules/prebidServerBidAdapter/bidderConfig.js @@ -1,5 +1,5 @@ -import {mergeDeep, deepEqual, deepAccess, deepSetValue, deepClone} from '../../src/utils.js'; -import {ORTB_EIDS_PATHS} from '../../src/activities/redactor.js'; +import { mergeDeep, deepEqual, deepAccess, deepSetValue, deepClone } from '../../src/utils.js'; +import { ORTB_EIDS_PATHS } from '../../src/activities/redactor.js'; /** * Perform a partial pre-merge of bidder config for PBS. @@ -11,7 +11,7 @@ import {ORTB_EIDS_PATHS} from '../../src/activities/redactor.js'; * This returns bidder config (from `bidder`) where arrays are replaced with what you get from merging them with `global`, * so that the result of merging in PBS is the same as in JS. */ -export function getPBSBidderConfig({global, bidder}) { +export function getPBSBidderConfig({ global, bidder }) { return Object.fromEntries( Object.entries(bidder).map(([bidderCode, bidderConfig]) => { return [bidderCode, replaceArrays(bidderConfig, mergeDeep({}, global, bidderConfig))] @@ -44,7 +44,7 @@ function replaceArrays(config, mergedConfig) { * `bidders` is a list of all the bidders that refer to that specific EID object, or false if that EID object is defined globally. * - `conflicts` is a set containing all EID sources that appear in multiple, otherwise different, EID objects. */ -export function extractEids({global, bidder}) { +export function extractEids({ global, bidder }) { const entries = []; const bySource = {}; const conflicts = new Set() @@ -52,7 +52,7 @@ export function extractEids({global, bidder}) { function getEntry(eid) { let entry = entries.find((candidate) => deepEqual(candidate.eid, eid)); if (entry == null) { - entry = {eid, bidders: new Set()} + entry = { eid, bidders: new Set() } entries.push(entry); } if (bySource[eid.source] == null) { @@ -79,7 +79,7 @@ export function extractEids({global, bidder}) { }) }) }) - return {eids: entries.map(({eid, bidders}) => ({eid, bidders: bidders && Array.from(bidders)})), conflicts}; + return { eids: entries.map(({ eid, bidders }) => ({ eid, bidders: bidders && Array.from(bidders) })), conflicts }; } /** @@ -95,16 +95,16 @@ export function extractEids({global, bidder}) { * - `bidder` is a map from bidder code to EID objects that are specific to that bidder, and cannot be restricted through `permissions` * - `permissions` is a list of EID permissions as expected by PBS. */ -export function consolidateEids({eids, conflicts = new Set()}) { +export function consolidateEids({ eids, conflicts = new Set() }) { const globalEntries = []; const bidderEntries = []; const byBidder = {}; eids.forEach(eid => { (eid.bidders === false ? globalEntries : bidderEntries).push(eid); }); - bidderEntries.forEach(({eid, bidders}) => { + bidderEntries.forEach(({ eid, bidders }) => { if (!conflicts.has(eid.source)) { - globalEntries.push({eid, bidders}) + globalEntries.push({ eid, bidders }) } else { bidders.forEach(bidderCode => { (byBidder[bidderCode] = byBidder[bidderCode] || []).push(eid) @@ -112,8 +112,8 @@ export function consolidateEids({eids, conflicts = new Set()}) { } }); return { - global: globalEntries.map(({eid}) => eid), - permissions: globalEntries.filter(({bidders}) => bidders !== false).map(({eid, bidders}) => ({ + global: globalEntries.map(({ eid }) => eid), + permissions: globalEntries.filter(({ bidders }) => bidders !== false).map(({ eid, bidders }) => ({ source: eid.source, bidders })), @@ -121,8 +121,8 @@ export function consolidateEids({eids, conflicts = new Set()}) { } } -function replaceEids({global, bidder}, requestedBidders) { - const consolidated = consolidateEids(extractEids({global, bidder})); +function replaceEids({ global, bidder }, requestedBidders) { + const consolidated = consolidateEids(extractEids({ global, bidder })); global = deepClone(global); bidder = deepClone(bidder); function removeEids(target) { @@ -147,7 +147,7 @@ function replaceEids({global, bidder}, requestedBidders) { deepSetValue(bidder[bidderCode], 'user.ext.eids', bidderEids); } }) - return {global, bidder} + return { global, bidder } } export function premergeFpd(ortb2Fragments, requestedBidders) { diff --git a/modules/prebidServerBidAdapter/index.ts b/modules/prebidServerBidAdapter/index.ts index 71abec10cee..056eca786a1 100644 --- a/modules/prebidServerBidAdapter/index.ts +++ b/modules/prebidServerBidAdapter/index.ts @@ -18,23 +18,23 @@ import { triggerPixel, uniques, } from '../../src/utils.js'; -import {DEBUG_MODE, EVENTS, REJECTION_REASON, S2S} from '../../src/constants.js'; -import adapterManager, {s2sActivityParams} from '../../src/adapterManager.js'; -import {config} from '../../src/config.js'; -import {addPaapiConfig, isValid} from '../../src/adapters/bidderFactory.js'; +import { DEBUG_MODE, EVENTS, REJECTION_REASON, S2S } from '../../src/constants.js'; +import adapterManager, { s2sActivityParams } from '../../src/adapterManager.js'; +import { config } from '../../src/config.js'; +import { isValid } from '../../src/adapters/bidderFactory.js'; import * as events from '../../src/events.js'; -import {ajax} from '../../src/ajax.js'; -import {hook} from '../../src/hook.js'; -import {hasPurpose1Consent} from '../../src/utils/gdpr.js'; -import {buildPBSRequest, interpretPBSResponse} from './ortbConverter.js'; -import {useMetrics} from '../../src/utils/perfMetrics.js'; -import {isActivityAllowed} from '../../src/activities/rules.js'; -import {ACTIVITY_TRANSMIT_UFPD} from '../../src/activities/activities.js'; -import type {Identifier, BidderCode} from '../../src/types/common.d.ts'; -import type {Metrics} from "../../src/utils/perfMetrics.ts"; -import type {ORTBResponse} from "../../src/types/ortb/response.d.ts"; -import type {NativeRequest} from '../../src/types/ortb/native.d.ts'; -import type {SyncType} from "../../src/userSync.ts"; +import { ajax } from '../../src/ajax.js'; +import { hook } from '../../src/hook.js'; +import { hasPurpose1Consent } from '../../src/utils/gdpr.js'; +import { buildPBSRequest, interpretPBSResponse } from './ortbConverter.js'; +import { useMetrics } from '../../src/utils/perfMetrics.js'; +import { isActivityAllowed } from '../../src/activities/rules.js'; +import { ACTIVITY_TRANSMIT_UFPD } from '../../src/activities/activities.js'; +import type { Identifier, BidderCode } from '../../src/types/common.d.ts'; +import type { Metrics } from "../../src/utils/perfMetrics.ts"; +import type { ORTBResponse } from "../../src/types/ortb/response.d.ts"; +import type { NativeRequest } from '../../src/types/ortb/native.d.ts'; +import type { SyncType } from "../../src/userSync.ts"; const getConfig = config.getConfig; @@ -155,7 +155,7 @@ export const s2sDefaultConfig: Partial = { syncUrlModifier: {}, ortbNative: { eventtrackers: [ - {event: 1, methods: [1, 2]} + { event: 1, methods: [1, 2] } ], }, maxTimeout: 1500, @@ -247,7 +247,7 @@ function setS2sConfig(options) { _s2sConfigs = options; } } -getConfig('s2sConfig', ({s2sConfig}) => setS2sConfig(s2sConfig)); +getConfig('s2sConfig', ({ s2sConfig }) => setS2sConfig(s2sConfig)); /** * resets the _synced variable back to false, primiarily used for testing purposes @@ -459,7 +459,6 @@ export type PbsAnalytics = SeatNonBid & { declare module '../../src/events' { interface Events { - [EVENTS.SEAT_NON_BID]: [SeatNonBid]; [EVENTS.PBS_ANALYTICS]: [PbsAnalytics]; [EVENTS.BEFORE_PBS_HTTP]: [PbsRequestData]; } @@ -477,7 +476,7 @@ export function PrebidServer() { .newMetrics() .renameWith((n) => [`adapter.s2s.${n}`, `adapters.s2s.${s2sBidRequest.s2sConfig.defaultVendor}.${n}`]) done = adapterMetrics.startTiming('total').stopBefore(done); - bidRequests.forEach(req => useMetrics(req.metrics).join(adapterMetrics, {stopPropagation: true})); + bidRequests.forEach(req => useMetrics(req.metrics).join(adapterMetrics, { stopPropagation: true })); const { gdprConsent, uspConsent, gppConsent } = getConsentData(bidRequests); @@ -497,15 +496,6 @@ export function PrebidServer() { bidRequests.forEach(bidderRequest => events.emit(EVENTS.BIDDER_DONE, bidderRequest)); } const { seatNonBidData, atagData } = getAnalyticsFlags(s2sBidRequest.s2sConfig, response) - if (seatNonBidData) { - events.emit(EVENTS.SEAT_NON_BID, { - seatnonbid: response.ext.seatnonbid, - auctionId: bidRequests[0].auctionId, - requestedBidders, - response, - adapterMetrics - }); - } // pbs analytics event if (seatNonBidData || atagData) { const data: PbsAnalytics = { @@ -522,7 +512,7 @@ export function PrebidServer() { doClientSideSyncs(requestedBidders, gdprConsent, uspConsent, gppConsent); }, onError(msg, error) { - const {p1Consent = '', noP1Consent = ''} = s2sBidRequest?.s2sConfig?.endpoint || {}; + const { p1Consent = '', noP1Consent = '' } = s2sBidRequest?.s2sConfig?.endpoint || {}; if (p1Consent === noP1Consent) { logError(`Prebid server call failed: '${msg}'. Endpoint: "${p1Consent}"}`, error); } else { @@ -531,7 +521,7 @@ export function PrebidServer() { bidRequests.forEach(bidderRequest => events.emit(EVENTS.BIDDER_ERROR, { error, bidderRequest })); done(error.timedOut); }, - onBid: function ({adUnit, bid}) { + onBid: function ({ adUnit, bid }) { const metrics = bid.metrics = s2sBidRequest.metrics.fork().renameWith(); metrics.checkpoint('addBidResponse'); if ((bid.requestId == null || bid.requestBidder == null) && !s2sBidRequest.s2sConfig.allowUnknownBidderCodes) { @@ -544,11 +534,6 @@ export function PrebidServer() { addBidResponse.reject(adUnit, bid, REJECTION_REASON.INVALID); } } - }, - onFledge: (params) => { - config.runWithBidder(params.bidder, () => { - addPaapiConfig({auctionId: bidRequests[0].auctionId, ...params}, {config: params.config}); - }) } }) } @@ -577,7 +562,7 @@ type PbsRequestData = { * @param onError {function(String, {})} invoked on HTTP failure - with status message and XHR error * @param onBid {function({})} invoked once for each bid in the response - with the bid as returned by interpretResponse */ -export const processPBSRequest = hook('async', function (s2sBidRequest, bidRequests, ajax, {onResponse, onError, onBid, onFledge}) { +export const processPBSRequest = hook('async', function (s2sBidRequest, bidRequests, ajax, { onResponse, onError, onBid }) { const { gdprConsent } = getConsentData(bidRequests); const adUnits = deepClone(s2sBidRequest.ad_units); @@ -606,11 +591,8 @@ export const processPBSRequest = hook('async', function (s2sBidRequest, bidReque let result; try { result = JSON.parse(response); - const {bids, paapi} = s2sBidRequest.metrics.measureTime('interpretResponse', () => interpretPBSResponse(result, request)); + const { bids } = s2sBidRequest.metrics.measureTime('interpretResponse', () => interpretPBSResponse(result, request)); bids.forEach(onBid); - if (paapi) { - paapi.forEach(onFledge); - } } catch (error) { logError(error); } diff --git a/modules/prebidServerBidAdapter/ortbConverter.js b/modules/prebidServerBidAdapter/ortbConverter.js index ab9e18e6837..47edd70eed2 100644 --- a/modules/prebidServerBidAdapter/ortbConverter.js +++ b/modules/prebidServerBidAdapter/ortbConverter.js @@ -1,23 +1,23 @@ -import {ortbConverter} from '../../libraries/ortbConverter/converter.js'; -import {deepClone, deepSetValue, getBidRequest, logError, logWarn, mergeDeep, timestamp} from '../../src/utils.js'; -import {config} from '../../src/config.js'; -import {S2S} from '../../src/constants.js'; -import {createBid} from '../../src/bidfactory.js'; -import {pbsExtensions} from '../../libraries/pbsExtensions/pbsExtensions.js'; -import {setImpBidParams} from '../../libraries/pbsExtensions/processors/params.js'; -import {SUPPORTED_MEDIA_TYPES} from '../../libraries/pbsExtensions/processors/mediaType.js'; -import {IMP, REQUEST, RESPONSE} from '../../src/pbjsORTB.js'; -import {redactor} from '../../src/activities/redactor.js'; -import {s2sActivityParams} from '../../src/adapterManager.js'; -import {activityParams} from '../../src/activities/activityParams.js'; -import {MODULE_TYPE_BIDDER} from '../../src/activities/modules.js'; -import {isActivityAllowed} from '../../src/activities/rules.js'; -import {ACTIVITY_TRANSMIT_TID} from '../../src/activities/activities.js'; -import {currencyCompare} from '../../libraries/currencyUtils/currency.js'; -import {minimum} from '../../src/utils/reducers.js'; -import {s2sDefaultConfig} from './index.js'; -import {premergeFpd} from './bidderConfig.js'; -import {ALL_MEDIATYPES, BANNER} from '../../src/mediaTypes.js'; +import { ortbConverter } from '../../libraries/ortbConverter/converter.js'; +import { deepClone, deepSetValue, getBidRequest, logError, logWarn, mergeDeep, timestamp } from '../../src/utils.js'; +import { config } from '../../src/config.js'; +import { S2S } from '../../src/constants.js'; +import { createBid } from '../../src/bidfactory.js'; +import { pbsExtensions } from '../../libraries/pbsExtensions/pbsExtensions.js'; +import { setImpBidParams } from '../../libraries/pbsExtensions/processors/params.js'; +import { SUPPORTED_MEDIA_TYPES } from '../../libraries/pbsExtensions/processors/mediaType.js'; +import { IMP, REQUEST, RESPONSE } from '../../src/pbjsORTB.js'; +import { redactor } from '../../src/activities/redactor.js'; +import { s2sActivityParams } from '../../src/adapterManager.js'; +import { activityParams } from '../../src/activities/activityParams.js'; +import { MODULE_TYPE_BIDDER } from '../../src/activities/modules.js'; +import { isActivityAllowed } from '../../src/activities/rules.js'; +import { ACTIVITY_TRANSMIT_TID } from '../../src/activities/activities.js'; +import { currencyCompare } from '../../libraries/currencyUtils/currency.js'; +import { minimum } from '../../src/utils/reducers.js'; +import { s2sDefaultConfig } from './index.js'; +import { premergeFpd } from './bidderConfig.js'; +import { ALL_MEDIATYPES, BANNER } from '../../src/mediaTypes.js'; const DEFAULT_S2S_TTL = 60; const DEFAULT_S2S_CURRENCY = 'USD'; @@ -59,7 +59,7 @@ const PBS_CONVERTER = ortbConverter({ if (!imps.length) { logError('Request to Prebid Server rejected due to invalid media type(s) in adUnit.'); } else { - const {s2sBidRequest} = context; + const { s2sBidRequest } = context; const request = buildRequest(imps, proxyBidderRequest, context); request.tmax = Math.floor(s2sBidRequest.s2sConfig.timeout ?? Math.min(s2sBidRequest.requestBidsTimeout * 0.75, s2sBidRequest.s2sConfig.maxTimeout ?? s2sDefaultConfig.maxTimeout)); @@ -200,7 +200,7 @@ const PBS_CONVERTER = ortbConverter({ }).map(([bidder, ortb2]) => ({ // ... but for bidder specific FPD we can use the actual bidder bidders: [bidder], - config: {ortb2: context.getRedactor(bidder).ortb2(ortb2)} + config: { ortb2: context.getRedactor(bidder).ortb2(ortb2) } })); if (fpdConfigs.length) { deepSetValue(ortbRequest, 'ext.prebid.bidderconfig', fpdConfigs); @@ -219,16 +219,16 @@ const PBS_CONVERTER = ortbConverter({ bidders: [req.bidderCode], schain: req?.bids?.[0]?.ortb2?.source?.ext?.schain }))) - .filter(({bidders, schain}) => bidders?.length > 0 && schain) - .reduce((chains, {bidders, schain}) => { + .filter(({ bidders, schain }) => bidders?.length > 0 && schain) + .reduce((chains, { bidders, schain }) => { const key = JSON.stringify(schain); if (!chains.hasOwnProperty(key)) { - chains[key] = {bidders: new Set(), schain}; + chains[key] = { bidders: new Set(), schain }; } bidders.forEach((bidder) => chains[key].bidders.add(bidder)); return chains; }, {}) - ).map(({bidders, schain}) => ({bidders: Array.from(bidders), schain})); + ).map(({ bidders, schain }) => ({ bidders: Array.from(bidders), schain })); if (chains.length) { deepSetValue(ortbRequest, 'ext.prebid.schains', chains); @@ -246,25 +246,8 @@ const PBS_CONVERTER = ortbConverter({ [RESPONSE]: { serverSideStats(orig, response, ortbResponse, context) { // override to process each request - context.actualBidderRequests.forEach(req => orig(response, ortbResponse, {...context, bidderRequest: req, bidRequests: req.bids})); + context.actualBidderRequests.forEach(req => orig(response, ortbResponse, { ...context, bidderRequest: req, bidRequests: req.bids })); }, - paapiConfigs(orig, response, ortbResponse, context) { - const configs = Object.values(context.impContext) - .flatMap((impCtx) => (impCtx.paapiConfigs || []).map(cfg => { - const bidderReq = impCtx.actualBidderRequests.find(br => br.bidderCode === cfg.bidder); - const bidReq = impCtx.actualBidRequests.get(cfg.bidder); - return { - adUnitCode: impCtx.adUnit.code, - ortb2: bidderReq?.ortb2, - ortb2Imp: bidReq?.ortb2Imp, - bidder: cfg.bidder, - config: cfg.config - }; - })); - if (configs.length > 0) { - response.paapi = configs; - } - } } }, }); @@ -311,15 +294,12 @@ export function buildPBSRequest(s2sBidRequest, bidderRequests, adUnits, requeste proxyBidRequests.push({ ...adUnit, adUnitCode: adUnit.code, - pbsData: {impId, actualBidRequests, adUnit}, + pbsData: { impId, actualBidRequests, adUnit }, }); }); const proxyBidderRequest = { ...Object.fromEntries(Object.entries(bidderRequests[0]).filter(([k]) => !BIDDER_SPECIFIC_REQUEST_PROPS.has(k))), - paapi: { - enabled: bidderRequests.some(br => br.paapi?.enabled) - } } return PBS_CONVERTER.toORTB({ @@ -343,5 +323,5 @@ export function buildPBSRequest(s2sBidRequest, bidderRequests, adUnits, requeste } export function interpretPBSResponse(response, request) { - return PBS_CONVERTER.fromORTB({response, request}); + return PBS_CONVERTER.fromORTB({ response, request }); } diff --git a/modules/previousAuctionInfo/index.js b/modules/previousAuctionInfo/index.js index 3846a46812a..179d25e555e 100644 --- a/modules/previousAuctionInfo/index.js +++ b/modules/previousAuctionInfo/index.js @@ -1,8 +1,8 @@ -import {on as onEvent, off as offEvent} from '../../src/events.js'; +import { on as onEvent, off as offEvent } from '../../src/events.js'; import { EVENTS } from '../../src/constants.js'; import { config } from '../../src/config.js'; -import {deepSetValue} from '../../src/utils.js'; -import {startAuction} from '../../src/prebid.js'; +import { deepSetValue } from '../../src/utils.js'; +import { startAuction } from '../../src/prebid.js'; export const CONFIG_NS = 'previousAuctionInfo'; export let previousAuctionInfoEnabled = false; let enabledBidders = []; @@ -19,7 +19,7 @@ export function resetPreviousAuctionInfo() { } function initPreviousAuctionInfo() { - config.getConfig('previousAuctionInfo', ({[CONFIG_NS]: config = {}}) => { + config.getConfig('previousAuctionInfo', ({ [CONFIG_NS]: config = {} }) => { if (!config?.enabled) { resetPreviousAuctionInfo(); return; @@ -46,7 +46,7 @@ const deinitHandlers = () => { if (handlersAttached) { offEvent(EVENTS.AUCTION_END, onAuctionEndHandler); offEvent(EVENTS.BID_WON, onBidWonHandler); - startAuction.getHooks({hook: startAuctionHook}).remove(); + startAuction.getHooks({ hook: startAuctionHook }).remove(); handlersAttached = false; } } diff --git a/modules/priceFloors.ts b/modules/priceFloors.ts index 45460c66425..2eacebdd575 100644 --- a/modules/priceFloors.ts +++ b/modules/priceFloors.ts @@ -15,25 +15,25 @@ import { deepEqual, generateUUID } from '../src/utils.js'; -import {getGlobal} from '../src/prebidGlobal.js'; -import {config} from '../src/config.js'; -import {ajaxBuilder} from '../src/ajax.js'; +import { getGlobal } from '../src/prebidGlobal.js'; +import { config } from '../src/config.js'; +import { ajaxBuilder } from '../src/ajax.js'; import * as events from '../src/events.js'; import { EVENTS, REJECTION_REASON } from '../src/constants.js'; -import {getHook} from '../src/hook.js'; -import {getRefererInfo} from '../src/refererDetection.js'; -import {bidderSettings} from '../src/bidderSettings.js'; -import {auctionManager} from '../src/auctionManager.js'; -import {IMP, PBS, registerOrtbProcessor, REQUEST} from '../src/pbjsORTB.js'; -import {timedAuctionHook, timedBidResponseHook} from '../src/utils/perfMetrics.js'; -import {adjustCpm} from '../src/utils/cpm.js'; -import {getGptSlotInfoForAdUnitCode} from '../libraries/gptUtils/gptUtils.js'; -import {convertCurrency} from '../libraries/currencyUtils/currency.js'; +import { getHook } from '../src/hook.js'; +import { getRefererInfo } from '../src/refererDetection.js'; +import { bidderSettings } from '../src/bidderSettings.js'; +import { auctionManager } from '../src/auctionManager.js'; +import { IMP, PBS, registerOrtbProcessor, REQUEST } from '../src/pbjsORTB.js'; +import { timedAuctionHook, timedBidResponseHook } from '../src/utils/perfMetrics.js'; +import { adjustCpm } from '../src/utils/cpm.js'; +import { getGptSlotInfoForAdUnitCode } from '../libraries/gptUtils/gptUtils.js'; +import { convertCurrency } from '../libraries/currencyUtils/currency.js'; import { timeoutQueue } from '../libraries/timeoutQueue/timeoutQueue.js'; -import {ALL_MEDIATYPES, BANNER, type MediaType} from '../src/mediaTypes.js'; -import type {Currency, Size, BidderCode} from "../src/types/common.d.ts"; -import type {BidRequest} from '../src/adapterManager.ts'; -import type {Bid} from "../src/bidfactory.ts"; +import { ALL_MEDIATYPES, BANNER, type MediaType } from '../src/mediaTypes.js'; +import type { Currency, Size, BidderCode } from "../src/types/common.d.ts"; +import type { BidRequest } from '../src/adapterManager.ts'; +import type { Bid } from "../src/bidfactory.ts"; export const FLOOR_SKIPPED_REASON = { NOT_FOUND: 'not_found', @@ -57,7 +57,7 @@ const SYN_FIELD = Symbol(); * @summary Allowed fields for rules to have */ export const allowedFields = [SYN_FIELD, 'gptSlot', 'adUnitCode', 'size', 'domain', 'mediaType'] as const; -type DefaultField = { [K in (typeof allowedFields)[number]]: K extends string ? K : never}[(typeof allowedFields)[number]]; +type DefaultField = { [K in (typeof allowedFields)[number]]: K extends string ? K : never }[(typeof allowedFields)[number]]; /** * @summary Global set to track valid userId tier fields @@ -115,7 +115,7 @@ const getHostname = (() => { let domain; return function() { if (domain == null) { - domain = parseUrl(getRefererInfo().topmostLocation, {noDecodeWholeURL: true}).hostname; + domain = parseUrl(getRefererInfo().topmostLocation, { noDecodeWholeURL: true }).hostname; } return domain; } @@ -148,13 +148,13 @@ export function resolveTierUserIds(tiers, bidRequest) { }, {}); } -function getGptSlotFromAdUnit(adUnitId, {index = auctionManager.index} = {}) { - const adUnit = index.getAdUnit({adUnitId}); +function getGptSlotFromAdUnit(adUnitId, { index = auctionManager.index } = {}) { + const adUnit = index.getAdUnit({ adUnitId }); const isGam = deepAccess(adUnit, 'ortb2Imp.ext.data.adserver.name') === 'gam'; return isGam && adUnit.ortb2Imp.ext.data.adserver.adslot; } -function getAdUnitCode(request, response, {index = auctionManager.index} = {}) { +function getAdUnitCode(request, response, { index = auctionManager.index } = {}) { return request?.adUnitCode || index.getAdUnit(response).code; } @@ -209,7 +209,7 @@ function enumeratePossibleFieldValues(floorFields, bidObject, responseObject) { export function getFirstMatchingFloor(floorData, bidObject, responseObject = {}) { const fieldValues = enumeratePossibleFieldValues(deepAccess(floorData, 'schema.fields') || [], bidObject, responseObject); if (!fieldValues.length) { - return {matchingFloor: undefined} + return { matchingFloor: undefined } } // look to see if a request for this context was made already @@ -217,7 +217,7 @@ export function getFirstMatchingFloor(floorData, bidObject, responseObject = {}) // if we already have gotten the matching rule from this matching input then use it! No need to look again const previousMatch = deepAccess(floorData, `matchingInputs.${matchingInput}`); if (previousMatch) { - return {...previousMatch}; + return { ...previousMatch }; } const allPossibleMatches = generatePossibleEnumerations(fieldValues, deepAccess(floorData, 'schema.delimiter') || '|'); const matchingRule = ((allPossibleMatches) || []).find(hashValue => floorData.values.hasOwnProperty(hashValue)); @@ -235,7 +235,7 @@ export function getFirstMatchingFloor(floorData, bidObject, responseObject = {}) } matchingData.matchingFloor = Math.max(matchingData.floorMin, matchingData.floorRuleValue); // save for later lookup if needed - deepSetValue(floorData, `matchingInputs.${matchingInput}`, {...matchingData}); + deepSetValue(floorData, `matchingInputs.${matchingInput}`, { ...matchingData }); return matchingData; } @@ -260,7 +260,7 @@ function generatePossibleEnumerations(arrayOfFields, delimiter) { * @summary If a the input bidder has a registered cpmadjustment it returns the input CPM after being adjusted */ export function getBiddersCpmAdjustment(inputCpm, bid, bidRequest) { - return parseFloat(adjustCpm(inputCpm, {...bid, cpm: inputCpm}, bidRequest)); + return parseFloat(adjustCpm(inputCpm, { ...bid, cpm: inputCpm }, bidRequest)); } /** @@ -316,7 +316,7 @@ declare module '../src/bidderSettings' { /** * Inverse of bidCpmAdjustment */ - inverseBidAdjustment?: (floor: number, bidRequest: BidRequest, params: {[K in keyof GetFloorParams]?: Exclude}) => number; + inverseBidAdjustment?: (floor: number, bidRequest: BidRequest, params: { [K in keyof GetFloorParams]?: Exclude }) => number; } } @@ -324,14 +324,14 @@ declare module '../src/bidderSettings' { * @summary This is the function which will return a single floor based on the input requests * and matching it to a rule for the current auction */ -export function getFloor(requestParams: GetFloorParams = {currency: 'USD', mediaType: '*', size: '*'}) { +export function getFloor(requestParams: GetFloorParams = { currency: 'USD', mediaType: '*', size: '*' }) { // eslint-disable-next-line @typescript-eslint/no-this-alias const bidRequest = this; const floorData = _floorDataForAuction[bidRequest.auctionId]; if (!floorData || floorData.skipped) return {}; requestParams = updateRequestParamsFromContext(bidRequest, requestParams); - const floorInfo = getFirstMatchingFloor(floorData.data, {...bidRequest}, {mediaType: requestParams.mediaType, size: requestParams.size}); + const floorInfo = getFirstMatchingFloor(floorData.data, { ...bidRequest }, { mediaType: requestParams.mediaType, size: requestParams.size }); let currency = requestParams.currency || floorData.data.currency; // if bidder asked for a currency which is not what floors are set in convert @@ -367,7 +367,8 @@ export function getFloor(requestParams: GetFloorParams = {currency: 'USD', media if (floorInfo.matchingFloor) { return { floor: roundUp(floorInfo.matchingFloor, 4), - currency}; + currency + }; } return {}; } @@ -412,7 +413,7 @@ export function getFloorDataFromAdUnits(adUnits) { logError(`${MODULE_NAME}: adUnit '${adUnit.code}' declares a different schema from one previously declared by adUnit '${schemaAu.code}'. Floor config for '${adUnit.code}' will be ignored.`) return accum; } - const floors = Object.assign({}, schemaAu?.floors, {values: undefined}, adUnit.floors) + const floors = Object.assign({}, schemaAu?.floors, { values: undefined }, adUnit.floors) if (isFloorsDataValid(floors)) { // if values already exist we want to not overwrite them if (!accum.values) { @@ -585,7 +586,7 @@ export function normalizeDefault(model) { model.values = model.values || {}; if (model.values[defaultRule] == null) { model.values[defaultRule] = model.default; - model.meta = {defaultRule}; + model.meta = { defaultRule }; } } return model; @@ -843,6 +844,11 @@ export type FloorsConfig = Pick * If set to false, the Price Floors Module will still provide floors for bid adapters, there will be no floor enforcement. */ enforceJS?: boolean; + /** + * Array of bidders to enforce JS floors on when enforceJS is true. + * Defaults to ['*'] (all bidders). + */ + enforceBidders?: (BidderCode | '*')[]; /** * If set to true (the default), the Price Floors Module will signal to Prebid Server to pass floors to it’s bid * adapters and enforce floors. @@ -901,6 +907,7 @@ export function handleSetFloorsConfig(config) { 'userIds', validateUserIdsConfig, 'enforcement', enforcement => pick(enforcement || {}, [ 'enforceJS', enforceJS => enforceJS !== false, // defaults to true + 'enforceBidders', enforceBidders => Array.isArray(enforceBidders) && enforceBidders.length > 0 ? enforceBidders : ['*'], 'enforcePBS', enforcePBS => enforcePBS === true, // defaults to false 'floorDeals', floorDeals => floorDeals === true, // defaults to false 'bidAdjustment', bidAdjustment => bidAdjustment !== false, // defaults to true, @@ -935,8 +942,8 @@ export function handleSetFloorsConfig(config) { _floorsConfig = {}; _floorDataForAuction = {}; - getHook('addBidResponse').getHooks({hook: addBidResponseHook}).remove(); - getHook('requestBids').getHooks({hook: requestBidsHook}).remove(); + getHook('addBidResponse').getHooks({ hook: addBidResponseHook }).remove(); + getHook('requestBids').getHooks({ hook: requestBidsHook }).remove(); addedFloorsHook = false; } @@ -969,7 +976,7 @@ function addFloorDataToBid(floorData, floorInfo, bid: Partial, adjustedCpm) floorRuleValue: floorInfo.floorRuleValue, floorCurrency: floorData.data.currency, cpmAfterAdjustments: adjustedCpm, - enforcements: {...floorData.enforcement}, + enforcements: { ...floorData.enforcement }, matchedFields: {} }; floorData.data.schema.fields.forEach((field, index) => { @@ -983,9 +990,12 @@ function addFloorDataToBid(floorData, floorInfo, bid: Partial, adjustedCpm) */ function shouldFloorBid(floorData, floorInfo, bid) { const enforceJS = deepAccess(floorData, 'enforcement.enforceJS') !== false; + const enforceBidders = deepAccess(floorData, 'enforcement.enforceBidders') || ['*']; + const bidderCode = bid?.adapterCode || bid?.bidderCode || bid?.bidder; + const shouldEnforceBidder = enforceBidders.includes('*') || (bidderCode != null && enforceBidders.includes(bidderCode)); const shouldFloorDeal = deepAccess(floorData, 'enforcement.floorDeals') === true || !bid.dealId; const bidBelowFloor = bid.floorData.cpmAfterAdjustments < floorInfo.matchingFloor; - return enforceJS && (bidBelowFloor && shouldFloorDeal); + return enforceJS && shouldEnforceBidder && (bidBelowFloor && shouldFloorDeal); } /** @@ -1002,7 +1012,7 @@ export const addBidResponseHook = timedBidResponseHook('priceFloors', function a const matchingBidRequest = auctionManager.index.getBidRequest(bid); // get the matching rule - const floorInfo = getFirstMatchingFloor(floorData.data, matchingBidRequest, {...bid, size: [bid.width, bid.height]}); + const floorInfo = getFirstMatchingFloor(floorData.data, matchingBidRequest, { ...bid, size: [bid.width, bid.height] }); if (!floorInfo.matchingFloor) { if (floorInfo.matchingFloor !== 0) logWarn(`${MODULE_NAME}: unable to determine a matching price floor for bidResponse`, bid); @@ -1044,7 +1054,7 @@ export const addBidResponseHook = timedBidResponseHook('priceFloors', function a config.getConfig('floors', config => handleSetFloorsConfig(config.floors)); -function tryGetFloor(bidRequest, {currency = config.getConfig('currency.adServerCurrency') || 'USD', mediaType = '*', size = '*'}: GetFloorParams, fn) { +function tryGetFloor(bidRequest, { currency = config.getConfig('currency.adServerCurrency') || 'USD', mediaType = '*', size = '*' }: GetFloorParams, fn) { if (typeof bidRequest.getFloor === 'function') { let floor; try { @@ -1100,7 +1110,7 @@ export function setGranularBidfloors(imp, bidRequest, context) { }, setIfDifferent.bind(imp[mediaType])) }); (imp[BANNER]?.format || []) - .filter(({w, h}) => w != null && h != null) + .filter(({ w, h }) => w != null && h != null) .forEach(format => { tryGetFloor(bidRequest, { currency: imp.bidfloorcur || context?.currency, @@ -1119,7 +1129,7 @@ export function setImpExtPrebidFloors(imp, bidRequest, context) { // 4. set req wide floorMin and floorMinCur values for pbs after iterations are done if (imp.bidfloor != null) { - let {floorMinCur, floorMin} = context.reqContext.floorMin || {}; + let { floorMinCur, floorMin } = context.reqContext.floorMin || {}; if (floorMinCur == null) { floorMinCur = imp.bidfloorcur } const ortb2ImpFloorCur = imp.ext?.prebid?.floors?.floorMinCur || imp.ext?.prebid?.floorMinCur || floorMinCur; @@ -1133,7 +1143,7 @@ export function setImpExtPrebidFloors(imp, bidRequest, context) { deepSetValue(imp, 'ext.prebid.floors.floorMin', lowestImpFloorMin); if (floorMin == null || floorMin > lowestImpFloorMin) { floorMin = lowestImpFloorMin } - context.reqContext.floorMin = {floorMin, floorMinCur}; + context.reqContext.floorMin = { floorMin, floorMinCur }; } } @@ -1145,15 +1155,15 @@ export function setOrtbExtPrebidFloors(ortbRequest, bidderRequest, context) { deepSetValue(ortbRequest, 'ext.prebid.floors.enabled', ortbRequest.ext?.prebid?.floors?.enabled || false); } if (context?.floorMin) { - mergeDeep(ortbRequest, {ext: {prebid: {floors: context.floorMin}}}) + mergeDeep(ortbRequest, { ext: { prebid: { floors: context.floorMin } } }) } } -registerOrtbProcessor({type: IMP, name: 'bidfloor', fn: setOrtbImpBidFloor}); +registerOrtbProcessor({ type: IMP, name: 'bidfloor', fn: setOrtbImpBidFloor }); // granular floors should be set after both "normal" bidfloors and mediaypes -registerOrtbProcessor({type: IMP, name: 'extBidfloor', fn: setGranularBidfloors, priority: -10}) -registerOrtbProcessor({type: IMP, name: 'extPrebidFloors', fn: setImpExtPrebidFloors, dialects: [PBS], priority: -1}); -registerOrtbProcessor({type: REQUEST, name: 'extPrebidFloors', fn: setOrtbExtPrebidFloors, dialects: [PBS]}); +registerOrtbProcessor({ type: IMP, name: 'extBidfloor', fn: setGranularBidfloors, priority: -10 }) +registerOrtbProcessor({ type: IMP, name: 'extPrebidFloors', fn: setImpExtPrebidFloors, dialects: [PBS], priority: -1 }); +registerOrtbProcessor({ type: REQUEST, name: 'extPrebidFloors', fn: setOrtbExtPrebidFloors, dialects: [PBS] }); /** * Validate userIds config: must be an object with array values diff --git a/modules/prismaBidAdapter.js b/modules/prismaBidAdapter.js index 8506d29f82b..b2b7682f878 100644 --- a/modules/prismaBidAdapter.js +++ b/modules/prismaBidAdapter.js @@ -1,9 +1,9 @@ -import {ajax} from '../src/ajax.js'; -import {config} from '../src/config.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER, VIDEO} from '../src/mediaTypes.js'; -import {getANKeywordParam} from '../libraries/appnexusUtils/anKeywords.js'; -import {getConnectionType} from '../libraries/connectionInfo/connectionUtils.js' +import { ajax } from '../src/ajax.js'; +import { config } from '../src/config.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; +import { getANKeywordParam } from '../libraries/appnexusUtils/anKeywords.js'; +import { getConnectionType } from '../libraries/connectionInfo/connectionUtils.js' /** * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest @@ -177,7 +177,7 @@ export const spec = { }; params.price = bid.cpm; const url = `${METRICS_TRACKER_URL}?${new URLSearchParams(params).toString()}`; - ajax(url, null, undefined, {method: 'GET', withCredentials: true}); + ajax(url, null, undefined, { method: 'GET', withCredentials: true }); return true; } diff --git a/modules/programmaticXBidAdapter.js b/modules/programmaticXBidAdapter.js index 1f0333b9a3c..413210beafa 100644 --- a/modules/programmaticXBidAdapter.js +++ b/modules/programmaticXBidAdapter.js @@ -1,6 +1,6 @@ -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER, VIDEO} from '../src/mediaTypes.js'; -import {getStorageManager} from '../src/storageManager.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; +import { getStorageManager } from '../src/storageManager.js'; import { createBuildRequestsFn, createInterpretResponseFn, @@ -13,7 +13,7 @@ const BIDDER_CODE = 'programmaticX'; const BIDDER_VERSION = '1.0.0'; const GVLID = 1344; -export const storage = getStorageManager({bidderCode: BIDDER_CODE}); +export const storage = getStorageManager({ bidderCode: BIDDER_CODE }); export function createDomain(subDomain = DEFAULT_SUB_DOMAIN) { return `https://${subDomain}.programmaticx.ai`; diff --git a/modules/programmaticaBidAdapter.js b/modules/programmaticaBidAdapter.js index 7ae2ae8404a..525f495f525 100644 --- a/modules/programmaticaBidAdapter.js +++ b/modules/programmaticaBidAdapter.js @@ -13,8 +13,8 @@ export const spec = { isBidRequestValid: sspValidRequest, buildRequests: sspBuildRequests(DEFAULT_ENDPOINT), interpretResponse: sspInterpretResponse(TIME_TO_LIVE, ADOMAIN), - getUserSyncs: getUserSyncs(SYNC_ENDPOINT, {usp: 'usp', consent: 'consent'}), - supportedMediaTypes: [ BANNER, VIDEO ] + getUserSyncs: getUserSyncs(SYNC_ENDPOINT, { usp: 'usp', consent: 'consent' }), + supportedMediaTypes: [BANNER, VIDEO] } registerBidder(spec); diff --git a/modules/proxistoreBidAdapter.js b/modules/proxistoreBidAdapter.js index 5c66be10804..0d63c00eb75 100644 --- a/modules/proxistoreBidAdapter.js +++ b/modules/proxistoreBidAdapter.js @@ -1,185 +1,160 @@ -import { isFn, isPlainObject } from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { ortbConverter } from '../libraries/ortbConverter/converter.js'; +import { BANNER } from '../src/mediaTypes.js'; +import { deepSetValue } from '../src/utils.js'; const BIDDER_CODE = 'proxistore'; const PROXISTORE_VENDOR_ID = 418; -const COOKIE_BASE_URL = 'https://abs.proxistore.com/v3/rtb/prebid/multi'; -const COOKIE_LESS_URL = - 'https://abs.cookieless-proxistore.com/v3/rtb/prebid/multi'; - -function _createServerRequest(bidRequests, bidderRequest) { - var sizeIds = []; - bidRequests.forEach(function (bid) { - var sizeId = { - id: bid.bidId, - sizes: bid.sizes.map(function (size) { - return { - width: size[0], - height: size[1], - }; - }), - floor: _assignFloor(bid), - segments: _assignSegments(bid), - }; - sizeIds.push(sizeId); - }); - var payload = { - // TODO: fix auctionId leak: https://github.com/prebid/Prebid.js/issues/9781 - auctionId: bidRequests[0].auctionId, - transactionId: bidRequests[0].ortb2Imp?.ext?.tid, - bids: sizeIds, - website: bidRequests[0].params.website, - language: bidRequests[0].params.language, - gdpr: { - applies: false, - consentGiven: false, - }, - }; +const COOKIE_BASE_URL = 'https://abs.proxistore.com/v3/rtb/openrtb'; +const COOKIE_LESS_URL = 'https://abs.cookieless-proxistore.com/v3/rtb/openrtb'; +const SYNC_BASE_URL = 'https://abs.proxistore.com/v3/rtb/sync'; + +const converter = ortbConverter({ + context: { + mediaType: BANNER, + netRevenue: true, + ttl: 30, + currency: 'EUR', + }, + request(buildRequest, imps, bidderRequest, context) { + const request = buildRequest(imps, bidderRequest, context); + const bidRequests = context.bidRequests; + if (bidRequests && bidRequests.length > 0) { + const params = bidRequests[0].params; + if (params.website) { + deepSetValue(request, 'ext.proxistore.website', params.website); + } + if (params.language) { + deepSetValue(request, 'ext.proxistore.language', params.language); + } + } + return request; + } +}); - if (bidderRequest && bidderRequest.gdprConsent) { - var gdprConsent = bidderRequest.gdprConsent; +/** + * Determines whether or not the given bid request is valid. + * + * @param bid The bid params to validate. + * @return boolean True if this is a valid bid, and false otherwise. + */ +function isBidRequestValid(bid) { + return !!(bid.params.website && bid.params.language); +} - if ( - typeof gdprConsent.gdprApplies === 'boolean' && - gdprConsent.gdprApplies - ) { - payload.gdpr.applies = true; - } +/** + * Make a server request from the list of BidRequests. + * + * @param bidRequests - an array of bids + * @param bidderRequest + * @return ServerRequest Info describing the request to the server. + */ +function buildRequests(bidRequests, bidderRequest) { + let gdprApplies = false; + let consentGiven = false; + + if (bidderRequest && bidderRequest.gdprConsent) { + const gdprConsent = bidderRequest.gdprConsent; - if ( - typeof gdprConsent.consentString === 'string' && - gdprConsent.consentString - ) { - payload.gdpr.consentString = bidderRequest.gdprConsent.consentString; + if (typeof gdprConsent.gdprApplies === 'boolean' && gdprConsent.gdprApplies) { + gdprApplies = true; } if (gdprConsent.vendorData) { - var vendorData = gdprConsent.vendorData; - + const vendorData = gdprConsent.vendorData; if ( vendorData.vendor && vendorData.vendor.consents && - typeof vendorData.vendor.consents[PROXISTORE_VENDOR_ID.toString(10)] !== - 'undefined' + vendorData.vendor.consents[PROXISTORE_VENDOR_ID.toString(10)] !== 'undefined' ) { - payload.gdpr.consentGiven = - !!vendorData.vendor.consents[PROXISTORE_VENDOR_ID.toString(10)]; + consentGiven = !!vendorData.vendor.consents[PROXISTORE_VENDOR_ID.toString(10)]; } } } - var options = { + const options = { contentType: 'application/json', - withCredentials: payload.gdpr.consentGiven, + withCredentials: consentGiven, customHeaders: { - version: '1.0.4', + version: '2.0.0', }, }; - var endPointUri = - payload.gdpr.consentGiven || !payload.gdpr.applies - ? COOKIE_BASE_URL - : COOKIE_LESS_URL; + + const endPointUri = consentGiven || !gdprApplies ? COOKIE_BASE_URL : COOKIE_LESS_URL; return { method: 'POST', url: endPointUri, - data: JSON.stringify(payload), + data: converter.toORTB({ bidRequests, bidderRequest }), options: options, }; } -function _assignSegments(bid) { - var segs = (bid.ortb2 && bid.ortb2.user && bid.ortb2.user.ext && bid.ortb2.user.ext.data && bid.ortb2.user.ext.data.sd_rtd && bid.ortb2.user.ext.data.sd_rtd.segments ? bid.ortb2.user.ext.data.sd_rtd.segments : []); - var cats = {}; - if (bid.ortb2 && bid.ortb2.site && bid.ortb2.site.ext && bid.ortb2.site.ext.data && bid.ortb2.site.ext.data.sd_rtd) { - if (bid.ortb2.site.ext.data.sd_rtd.categories) { - segs = segs.concat(bid.ortb2.site.ext.data.sd_rtd.categories); - } - if (bid.ortb2.site.ext.data.sd_rtd.categories_score) { - cats = bid.ortb2.site.ext.data.sd_rtd.categories_score; - } - } - - return { - segments: segs, - contextual_categories: cats - }; -} - -function _createBidResponse(response) { - return { - requestId: response.requestId, - cpm: response.cpm, - width: response.width, - height: response.height, - ad: response.ad, - ttl: response.ttl, - creativeId: response.creativeId, - currency: response.currency, - netRevenue: response.netRevenue, - vastUrl: response.vastUrl, - vastXml: response.vastXml, - dealId: response.dealId, - meta: response.meta, - }; -} /** - * Determines whether or not the given bid request is valid. + * Unpack the response from the server into a list of bids. * - * @param bid The bid params to validate. - * @return boolean True if this is a valid bid, and false otherwise. + * @param response + * @param request + * @return An array of bids which were nested inside the server. */ - -function isBidRequestValid(bid) { - return !!(bid.params.website && bid.params.language); +function interpretResponse(response, request) { + if (response.body) { + return converter.fromORTB({ response: response.body, request: request.data }).bids; + } + return []; } -/** - * Make a server request from the list of BidRequests. - * - * @param bidRequests - an array of bids - * @param bidderRequest - * @return ServerRequest Info describing the request to the server. - */ - -function buildRequests(bidRequests, bidderRequest) { - var request = _createServerRequest(bidRequests, bidderRequest); - return request; -} /** - * Unpack the response from the server into a list of bids. + * Register user sync pixels and iframes. * - * @param serverResponse A successful response from the server. - * @param bidRequest Request original server request - * @return An array of bids which were nested inside the server. + * @param syncOptions - which sync types are enabled + * @param responses - server responses + * @param gdprConsent - GDPR consent data + * @return Array of sync objects */ +function getUserSyncs(syncOptions, responses, gdprConsent) { + const syncs = []; -function interpretResponse(serverResponse, bidRequest) { - return serverResponse.body.map(_createBidResponse); -} + // Only sync if consent given or GDPR doesn't apply + const consentGiven = gdprConsent?.vendorData?.vendor?.consents?.[PROXISTORE_VENDOR_ID]; + if (gdprConsent?.gdprApplies && !consentGiven) { + return syncs; + } -function _assignFloor(bid) { - if (!isFn(bid.getFloor)) { - return bid.params.bidFloor ? bid.params.bidFloor : null; + const params = new URLSearchParams(); + if (gdprConsent) { + params.set('gdpr', gdprConsent.gdprApplies ? '1' : '0'); + if (gdprConsent.consentString) { + params.set('gdpr_consent', gdprConsent.consentString); + } + } + + if (syncOptions.pixelEnabled) { + syncs.push({ + type: 'image', + url: `${SYNC_BASE_URL}/image?${params}` + }); } - const floor = bid.getFloor({ - currency: 'EUR', - mediaType: 'banner', - size: '*', - }); - if (isPlainObject(floor) && !isNaN(floor.floor) && floor.currency === 'EUR') { - return floor.floor; + if (syncOptions.iframeEnabled) { + syncs.push({ + type: 'iframe', + url: `${SYNC_BASE_URL}/iframe?${params}` + }); } - return null; + + return syncs; } export const spec = { code: BIDDER_CODE, + gvlid: PROXISTORE_VENDOR_ID, isBidRequestValid: isBidRequestValid, buildRequests: buildRequests, interpretResponse: interpretResponse, - gvlid: PROXISTORE_VENDOR_ID, + getUserSyncs: getUserSyncs, + supportedMediaTypes: [BANNER], + browsingTopics: true, }; registerBidder(spec); diff --git a/modules/pubProvidedIdSystem.js b/modules/pubProvidedIdSystem.js index d23d992e495..bb802fce8c3 100644 --- a/modules/pubProvidedIdSystem.js +++ b/modules/pubProvidedIdSystem.js @@ -5,9 +5,9 @@ * @requires module:modules/userId */ -import {submodule} from '../src/hook.js'; +import { submodule } from '../src/hook.js'; import { logInfo, isArray } from '../src/utils.js'; -import {VENDORLESS_GVLID} from '../src/consentHandler.js'; +import { VENDORLESS_GVLID } from '../src/consentHandler.js'; /** * @typedef {import('../modules/userId/index.js').Submodule} Submodule @@ -33,7 +33,7 @@ export const pubProvidedIdSubmodule = { * @returns {{pubProvidedId: Array}} or undefined if value doesn't exists */ decode(value) { - const res = value ? {pubProvidedId: value} : undefined; + const res = value ? { pubProvidedId: value } : undefined; logInfo('PubProvidedId: Decoded value ' + JSON.stringify(res)); return res; }, @@ -53,7 +53,7 @@ export const pubProvidedIdSubmodule = { if (typeof configParams.eidsFunction === 'function') { res = res.concat(configParams.eidsFunction()); } - return {id: res}; + return { id: res }; } }; diff --git a/modules/pubgeniusBidAdapter.js b/modules/pubgeniusBidAdapter.js index 6267d0c9225..bfdca074e2c 100644 --- a/modules/pubgeniusBidAdapter.js +++ b/modules/pubgeniusBidAdapter.js @@ -1,7 +1,7 @@ -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {ajax} from '../src/ajax.js'; -import {config} from '../src/config.js'; -import {BANNER, VIDEO} from '../src/mediaTypes.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { ajax } from '../src/ajax.js'; +import { config } from '../src/config.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; import { deepAccess, deepSetValue, @@ -21,7 +21,7 @@ const BASE_URL = 'https://auction.adpearl.io'; export const spec = { code: 'pubgenius', - supportedMediaTypes: [ BANNER, VIDEO ], + supportedMediaTypes: [BANNER, VIDEO], isBidRequestValid(bid) { const adUnitId = bid.params.adUnitId; diff --git a/modules/publicGoodBidAdapter.js b/modules/publicGoodBidAdapter.js index b5fa56d7a53..d968cf93c5c 100644 --- a/modules/publicGoodBidAdapter.js +++ b/modules/publicGoodBidAdapter.js @@ -1,7 +1,7 @@ 'use strict'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER, NATIVE} from '../src/mediaTypes.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, NATIVE } from '../src/mediaTypes.js'; const BIDDER_CODE = 'publicgood'; const PUBLIC_GOOD_ENDPOINT = 'https://advice.pgs.io'; @@ -67,7 +67,7 @@ export const spec = { bidResponse.currency = 'USD'; bidResponse.netRevenue = true; bidResponse.ttl = 360; - bidResponse.meta = {advertiserDomains: []}; + bidResponse.meta = { advertiserDomains: [] }; bidResponses.push(bidResponse); } diff --git a/modules/publinkIdSystem.js b/modules/publinkIdSystem.js index 09981498877..7554a0e9c38 100644 --- a/modules/publinkIdSystem.js +++ b/modules/publinkIdSystem.js @@ -5,11 +5,11 @@ * @requires module:modules/userId */ -import {submodule} from '../src/hook.js'; -import {getStorageManager} from '../src/storageManager.js'; -import {ajax} from '../src/ajax.js'; +import { submodule } from '../src/hook.js'; +import { getStorageManager } from '../src/storageManager.js'; +import { ajax } from '../src/ajax.js'; import { parseUrl, buildUrl, logError } from '../src/utils.js'; -import {MODULE_TYPE_UID} from '../src/activities/modules.js'; +import { MODULE_TYPE_UID } from '../src/activities/modules.js'; /** * @typedef {import('../modules/userId/index.js').Submodule} Submodule @@ -25,7 +25,7 @@ const PUBLINK_S2S_COOKIE = '_publink_srv'; const PUBLINK_REQUEST_PATH = '/cvx/client/sync/publink'; const PUBLINK_REFRESH_PATH = '/cvx/client/sync/publink/refresh'; -export const storage = getStorageManager({moduleType: MODULE_TYPE_UID, moduleName: MODULE_NAME}); +export const storage = getStorageManager({ moduleType: MODULE_TYPE_UID, moduleName: MODULE_NAME }); function isHex(s) { return /^[A-F0-9]+$/i.test(s); @@ -69,7 +69,7 @@ function publinkIdUrl(params, consentData, storedId) { function makeCallback(config = {}, consentData, storedId) { return function(prebidCallback) { - const options = {method: 'GET', withCredentials: true}; + const options = { method: 'GET', withCredentials: true }; const handleResponse = function(responseText, xhr) { if (xhr.status === 200) { const response = JSON.parse(responseText); @@ -136,7 +136,7 @@ export const publinkIdSubmodule = { * @returns {{publinkId: string} | undefined} */ decode(publinkId) { - return {publinkId: publinkId}; + return { publinkId: publinkId }; }, /** @@ -151,9 +151,9 @@ export const publinkIdSubmodule = { getId: function(config, consentData, storedId) { const localValue = getlocalValue(); if (localValue) { - return {id: localValue}; + return { id: localValue }; } - return {callback: makeCallback(config, consentData, storedId)}; + return { callback: makeCallback(config, consentData, storedId) }; }, eids: { 'publinkId': { diff --git a/modules/pubmaticAnalyticsAdapter.js b/modules/pubmaticAnalyticsAdapter.js index 19b1c11ffe9..04838ef6989 100755 --- a/modules/pubmaticAnalyticsAdapter.js +++ b/modules/pubmaticAnalyticsAdapter.js @@ -524,7 +524,7 @@ const eventHandlers = { /// /////////// ADAPTER DEFINITION ////////////// -const baseAdapter = adapter({analyticsType: 'endpoint'}); +const baseAdapter = adapter({ analyticsType: 'endpoint' }); const pubmaticAdapter = Object.assign({}, baseAdapter, { enableAnalytics(conf = {}) { diff --git a/modules/pubmaticBidAdapter.js b/modules/pubmaticBidAdapter.js index f23665670e9..4a1161fca73 100644 --- a/modules/pubmaticBidAdapter.js +++ b/modules/pubmaticBidAdapter.js @@ -1,6 +1,6 @@ import { logWarn, isStr, isArray, deepAccess, deepSetValue, isBoolean, isInteger, logInfo, logError, deepClone, uniques, generateUUID, isPlainObject, isFn, getWindowTop } from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { BANNER, VIDEO, NATIVE, ADPOD } from '../src/mediaTypes.js'; +import { BANNER, VIDEO, NATIVE } from '../src/mediaTypes.js'; import { config } from '../src/config.js'; import { Renderer } from '../src/Renderer.js'; import { isViewabilityMeasurable, getViewability } from '../libraries/percentInView/percentInView.js'; @@ -9,6 +9,7 @@ import { ortbConverter } from '../libraries/ortbConverter/converter.js'; import { NATIVE_ASSET_TYPES, NATIVE_IMAGE_TYPES, PREBID_NATIVE_DATA_KEYS_TO_ORTB, NATIVE_KEYS_THAT_ARE_NOT_ASSETS, NATIVE_KEYS } from '../src/constants.js'; import { addDealCustomTargetings, addPMPDeals } from '../libraries/dealUtils/dealUtils.js'; import { getConnectionType } from '../libraries/connectionInfo/connectionUtils.js'; +import { getAdUnitElement } from '../src/utils/adUnits.js'; /** * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest @@ -95,7 +96,7 @@ const converter = ortbConverter({ if (imp.hasOwnProperty('banner')) updateBannerImp(imp.banner, adSlot); if (imp.hasOwnProperty('video')) updateVideoImp(mediaTypes?.video, adUnitCode, imp); if (imp.hasOwnProperty('native')) updateNativeImp(imp, mediaTypes?.native); - if (imp.hasOwnProperty('banner') || imp.hasOwnProperty('video')) addViewabilityToImp(imp, adUnitCode, bidRequest?.sizes); + if (imp.hasOwnProperty('banner') || imp.hasOwnProperty('video')) addViewabilityToImp(imp, bidRequest, bidRequest?.sizes); if (pmzoneid) imp.ext.pmZoneId = pmzoneid; setImpTagId(imp, adSlot.trim(), hashedKey); setImpFields(imp); @@ -142,12 +143,11 @@ const converter = ortbConverter({ if (mediaType === VIDEO) { if (!bidResponse.width) bidResponse.width = playerWidth; if (!bidResponse.height) bidResponse.height = playerHeight; - const { context, maxduration } = mediaTypes[mediaType]; + const { context } = mediaTypes[mediaType]; if (context === 'outstream' && params.outstreamAU && adUnitCode) { bidResponse.rendererCode = params.outstreamAU; bidResponse.renderer = BB_RENDERER.newRenderer(bidResponse.rendererCode, adUnitCode); } - assignDealTier(bidResponse, context, maxduration); } if (mediaType === NATIVE && bid.adm) { try { @@ -256,6 +256,11 @@ const toOrtbNativeRequest = legacyNativeAssets => { continue; } + if (key === 'privacyLink') { + ortb.privacy = 1; + continue; + } + const asset = legacyNativeAssets[key]; const required = asset.required && isBoolean(asset.required) ? 1 : 0; const ortbAsset = { id: ortb.assets.length, required }; @@ -315,7 +320,7 @@ const setFloorInImp = (imp, bid) => { const mediaTypeFloor = parseFloat(floorInfo.floor); if (isMultiFormatRequest && mediaType !== BANNER) { logInfo(LOG_WARN_PREFIX, 'floor from floor module returned for mediatype:', mediaType, 'is : ', mediaTypeFloor, 'with currency :', imp.bidfloorcur); - imp[mediaType]['ext'] = {'bidfloor': mediaTypeFloor, 'bidfloorcur': imp.bidfloorcur}; + imp[mediaType]['ext'] = { 'bidfloor': mediaTypeFloor, 'bidfloorcur': imp.bidfloorcur }; } logInfo(LOG_WARN_PREFIX, 'floor from floor module:', mediaTypeFloor, 'previous floor value', bidFloor, 'Min:', Math.min(mediaTypeFloor, bidFloor)); bidFloor = bidFloor === -1 ? mediaTypeFloor : Math.min(mediaTypeFloor, bidFloor); @@ -323,7 +328,7 @@ const setFloorInImp = (imp, bid) => { } }); if (isMultiFormatRequest && mediaType === BANNER) { - imp[mediaType]['ext'] = {'bidfloor': bidFloor, 'bidfloorcur': imp.bidfloorcur}; + imp[mediaType]['ext'] = { 'bidfloor': bidFloor, 'bidfloorcur': imp.bidfloorcur }; } }); } @@ -524,27 +529,6 @@ const addExtenstionParams = (req, bidderRequest) => { } } -/** - * In case of adpod video context, assign prebiddealpriority to the dealtier property of adpod-video bid, - * so that adpod module can set the hb_pb_cat_dur targetting key. - * @param {*} bid - * @param {*} context - * @param {*} maxduration - * @returns - */ -const assignDealTier = (bid, context, maxduration) => { - if (!bid?.ext?.prebiddealpriority || !FEATURES.VIDEO) return; - if (context !== ADPOD) return; - - const duration = bid?.ext?.video?.duration || maxduration; - // if (!duration) return; - bid.video = { - context: ADPOD, - durationSeconds: duration, - dealTier: bid.ext.prebiddealpriority - }; -} - const validateAllowedCategories = (acat) => { return [...new Set( acat @@ -621,7 +605,7 @@ const BB_RENDERER = { } const rendererId = BB_RENDERER.getRendererId(PUBLICATION, bid.rendererCode); - const ele = document.getElementById(bid.adUnitCode); // NB convention + const ele = getAdUnitElement(bid); const renderer = window.bluebillywig.renderers.find(r => r._id === rendererId); if (renderer) renderer.bootstrap(config, ele); @@ -723,10 +707,10 @@ function _getMinSize(sizes) { /** * Measures viewability for an element and adds it to the imp object at the ext level * @param {Object} imp - The impression object - * @param {string} adUnitCode - The ad unit code for element identification + * @param {Object} bidRequest - The bid request for element identification * @param {Object} sizes - Sizes object with width and height properties */ -export const addViewabilityToImp = (imp, adUnitCode, sizes) => { +export const addViewabilityToImp = (imp, bidRequest, sizes) => { let elementSize = { w: 0, h: 0 }; if (imp.video?.w > 0 && imp.video?.h > 0) { @@ -735,7 +719,7 @@ export const addViewabilityToImp = (imp, adUnitCode, sizes) => { } else { elementSize = _getMinSize(sizes); } - const element = document.getElementById(adUnitCode); + const element = getAdUnitElement(bidRequest); if (!element) return; const viewabilityAmount = isViewabilityMeasurable(element) @@ -756,6 +740,7 @@ export const spec = { code: BIDDER_CODE, gvlid: 76, supportedMediaTypes: [BANNER, VIDEO, NATIVE], + alwaysHasCapacity: true, /** * Determines whether or not the given bid request is valid. Valid bid request must have placementId and hbid * diff --git a/modules/pubstackBidAdapter.md b/modules/pubstackBidAdapter.md new file mode 100644 index 00000000000..dc3df3b8ee5 --- /dev/null +++ b/modules/pubstackBidAdapter.md @@ -0,0 +1,30 @@ +# Overview +``` +Module Name: Pubstack Bidder Adapter +Module Type: Bidder Adapter +Maintainer: prebid@pubstack.io +``` + +# Description +Connects to Pubstack exchange for bids. + +Pubstack bid adapter supports all media type including video, banner and native. + +# Test Parameters +``` +var adUnits = [{ + code: 'adunit-1', + mediaTypes: { + banner: { + sizes: [[300, 250]] + } + }, + bids: [{ + bidder: 'pubstack', + params: { + siteId: 'your-site-id', + adUnitName: 'adunit-1' + } + }] +}]; +``` \ No newline at end of file diff --git a/modules/pubstackBidAdapter.ts b/modules/pubstackBidAdapter.ts new file mode 100644 index 00000000000..3ef04f138e1 --- /dev/null +++ b/modules/pubstackBidAdapter.ts @@ -0,0 +1,156 @@ +import { canAccessWindowTop, deepSetValue, getWindowSelf, getWindowTop, logError } from '../src/utils.js'; +import { AdapterRequest, BidderSpec, registerBidder, ServerResponse } from '../src/adapters/bidderFactory.js'; +import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; +import { ortbConverter } from '../libraries/ortbConverter/converter.js'; +import { getPlacementPositionUtils } from '../libraries/placementPositionInfo/placementPositionInfo.js'; +import { getGptSlotInfoForAdUnitCode } from '../libraries/gptUtils/gptUtils.js'; +import { BidRequest, ClientBidderRequest } from '../src/adapterManager.js'; +import { ORTBRequest } from '../src/prebid.public.js'; +import { config } from '../src/config.js'; +import { SyncType } from '../src/userSync.js'; +import { ConsentData, CONSENT_GDPR, CONSENT_USP, CONSENT_GPP } from '../src/consentHandler.js'; +import { getGlobal } from '../src/prebidGlobal.js'; + +const BIDDER_CODE = 'pubstack'; +const GVLID = 1408; +const REQUEST_URL = 'https://node.pbstck.com/openrtb2/auction'; +const COOKIESYNC_IFRAME_URL = 'https://cdn.pbstck.com/async_usersync.html'; +const COOKIESYNC_PIXEL_URL = 'https://cdn.pbstck.com/async_usersync.png'; + +declare module '../src/adUnits' { + interface BidderParams { + [BIDDER_CODE]: { + siteId: string; + adUnitName: string; + }; + } +} + +type GetUserSyncFn = ( + syncOptions: { + iframeEnabled: boolean; + pixelEnabled: boolean; + }, + responses: ServerResponse[], + gdprConsent: null | ConsentData[typeof CONSENT_GDPR], + uspConsent: null | ConsentData[typeof CONSENT_USP], + gppConsent: null | ConsentData[typeof CONSENT_GPP]) => ({ type: SyncType, url: string })[] + +const siteIds: Set = new Set(); +let cntRequest = 0; +let cntTimeouts = 0; +const { getPlacementEnv, getPlacementInfo } = getPlacementPositionUtils(); + +const getElementForAdUnitCode = (adUnitCode: string): HTMLElement | undefined => { + if (!adUnitCode) return; + const win = canAccessWindowTop() ? getWindowTop() : getWindowSelf(); + const doc = win?.document; + let element = doc?.getElementById(adUnitCode) as HTMLElement | null; + if (element) return element; + const divId = getGptSlotInfoForAdUnitCode(adUnitCode)?.divId; + element = divId ? doc?.getElementById(divId) as HTMLElement | null : null; + if (element) return element; +}; + +const converter = ortbConverter({ + imp(buildImp, bidRequest: BidRequest, context) { + const element = getElementForAdUnitCode(bidRequest.adUnitCode); + const placementInfo = getPlacementInfo(bidRequest); + const imp = buildImp(bidRequest, context); + deepSetValue(imp, `ext.prebid.bidder.${BIDDER_CODE}.adUnitName`, bidRequest.params.adUnitName); + deepSetValue(imp, `ext.prebid.placement.code`, bidRequest.adUnitCode); + deepSetValue(imp, `ext.prebid.placement.domId`, element?.id); + deepSetValue(imp, `ext.prebid.placement.viewability`, placementInfo.PlacementPercentView); + deepSetValue(imp, `ext.prebid.placement.viewportDistance`, placementInfo.DistanceToView); + deepSetValue(imp, `ext.prebid.placement.height`, placementInfo.ElementHeight); + deepSetValue(imp, `ext.prebid.placement.auctionsCount`, placementInfo.AuctionsCount); + return imp; + }, + request(buildRequest, imps, bidderRequest, context) { + cntRequest++; + const placementEnv = getPlacementEnv(); + const request = buildRequest(imps, bidderRequest, context) + const siteId = bidderRequest.bids[0].params.siteId + siteIds.add(siteId); + deepSetValue(request, 'site.publisher.id', siteId); + deepSetValue(request, 'test', config.getConfig('debug') ? 1 : 0); + deepSetValue(request, 'ext.prebid.version', getGlobal()?.version ?? 'unknown'); + deepSetValue(request, `ext.prebid.request.count`, cntRequest); + deepSetValue(request, `ext.prebid.request.timeoutCount`, cntTimeouts); + deepSetValue(request, `ext.prebid.page.tabActive`, placementEnv.TabActive); + deepSetValue(request, `ext.prebid.page.height`, placementEnv.PageHeight); + deepSetValue(request, `ext.prebid.page.viewportHeight`, placementEnv.ViewportHeight); + deepSetValue(request, `ext.prebid.page.timeFromNavigation`, placementEnv.TimeFromNavigation); + return request; + }, +}); + +const isBidRequestValid = (bid: BidRequest): boolean => { + if (!bid.params.siteId || typeof bid.params.siteId !== 'string') { + logError('bid.params.siteId needs to be a string'); + if (config.getConfig('debug') === false) return false; + } + if (!bid.params.adUnitName || typeof bid.params.adUnitName !== 'string') { + logError('bid.params.adUnitName needs to be a string'); + if (config.getConfig('debug') === false) return false; + } + return true; +}; + +const buildRequests = ( + bidRequests: BidRequest[], + bidderRequest: ClientBidderRequest, +): AdapterRequest => { + const data: ORTBRequest = converter.toORTB({ bidRequests, bidderRequest }); + const siteId = data.site.publisher.id; + return { + method: 'POST', + url: `${REQUEST_URL}?siteId=${siteId}`, + data, + }; +}; + +const interpretResponse = (serverResponse, bidRequest) => { + if (!serverResponse?.body) { + return []; + } + return converter.fromORTB({ request: bidRequest.data, response: serverResponse.body }); +}; + +const getUserSyncs: GetUserSyncFn = (syncOptions, _serverResponses, gdprConsent, uspConsent, gppConsent) => { + const isIframeEnabled = syncOptions.iframeEnabled; + const isPixelEnabled = syncOptions.pixelEnabled; + + if (!isIframeEnabled && !isPixelEnabled) { + return []; + } + + const payload = btoa(JSON.stringify({ + gdprConsentString: gdprConsent?.consentString, + gdprApplies: gdprConsent?.gdprApplies, + uspConsent, + gpp: gppConsent?.gppString, + gpp_sid: gppConsent?.applicableSections + + })); + const syncUrl = isIframeEnabled ? COOKIESYNC_IFRAME_URL : COOKIESYNC_PIXEL_URL; + + return Array.from(siteIds).map(siteId => ({ + type: isIframeEnabled ? 'iframe' : 'image', + url: `${syncUrl}?consent=${payload}&siteId=${siteId}`, + })); +}; + +export const spec: BidderSpec = { + code: BIDDER_CODE, + aliases: [{ code: `${BIDDER_CODE}_server`, gvlid: GVLID }], + gvlid: GVLID, + supportedMediaTypes: [BANNER, VIDEO, NATIVE], + isBidRequestValid, + buildRequests, + interpretResponse, + getUserSyncs, + onTimeout: () => cntTimeouts++, +}; + +registerBidder(spec); diff --git a/modules/pubwiseAnalyticsAdapter.js b/modules/pubwiseAnalyticsAdapter.js index f92c61a53dd..51cc3b413e5 100644 --- a/modules/pubwiseAnalyticsAdapter.js +++ b/modules/pubwiseAnalyticsAdapter.js @@ -1,12 +1,12 @@ import { getParameterByName, logInfo, generateUUID, debugTurnedOn } from '../src/utils.js'; -import {ajax} from '../src/ajax.js'; +import { ajax } from '../src/ajax.js'; import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; import { EVENTS } from '../src/constants.js'; -import {getStorageManager} from '../src/storageManager.js'; -import {MODULE_TYPE_ANALYTICS} from '../src/activities/modules.js'; +import { getStorageManager } from '../src/storageManager.js'; +import { MODULE_TYPE_ANALYTICS } from '../src/activities/modules.js'; const MODULE_CODE = 'pubwise'; -const storage = getStorageManager({moduleType: MODULE_TYPE_ANALYTICS, moduleName: MODULE_CODE}); +const storage = getStorageManager({ moduleType: MODULE_TYPE_ANALYTICS, moduleName: MODULE_CODE }); /**** * PubWise.io Analytics @@ -32,10 +32,10 @@ const analyticsType = 'endpoint'; const analyticsName = 'PubWise:'; const prebidVersion = '$prebid.version$'; const pubwiseVersion = '4.0.1'; -let configOptions = {site: '', endpoint: 'https://api.pubwise.io/api/v5/event/add/', debug: null}; +let configOptions = { site: '', endpoint: 'https://api.pubwise.io/api/v5/event/add/', debug: null }; let pwAnalyticsEnabled = false; -const utmKeys = {utm_source: '', utm_medium: '', utm_campaign: '', utm_term: '', utm_content: ''}; -const sessionData = {sessionId: '', activationId: ''}; +const utmKeys = { utm_source: '', utm_medium: '', utm_campaign: '', utm_term: '', utm_content: '' }; +const sessionData = { sessionId: '', activationId: '' }; const pwNamespace = 'pubwise'; const pwEvents = []; let metaData = {}; @@ -168,7 +168,7 @@ function sessionExpired() { function flushEvents() { if (pwEvents.length > 0) { - const dataBag = {metaData: metaData, eventList: pwEvents.splice(0)}; // put all the events together with the metadata and send + const dataBag = { metaData: metaData, eventList: pwEvents.splice(0) }; // put all the events together with the metadata and send ajax(configOptions.endpoint, (result) => pwInfo(`Result`, result), JSON.stringify(dataBag)); } } @@ -254,9 +254,9 @@ function filterAuctionInit(data) { return modified; } -const pubwiseAnalytics = Object.assign(adapter({analyticsType}), { +const pubwiseAnalytics = Object.assign(adapter({ analyticsType }), { // Override AnalyticsAdapter functions by supplying custom methods - track({eventType, args}) { + track({ eventType, args }) { this.handleEvent(eventType, args); } }); diff --git a/modules/pulsepointBidAdapter.js b/modules/pulsepointBidAdapter.js index 9619488f261..2d4bcc1abf9 100644 --- a/modules/pulsepointBidAdapter.js +++ b/modules/pulsepointBidAdapter.js @@ -29,7 +29,7 @@ export const spec = { ), buildRequests: (bidRequests, bidderRequest) => { - const data = converter.toORTB({bidRequests, bidderRequest}); + const data = converter.toORTB({ bidRequests, bidderRequest }); return { method: 'POST', url: 'https://bid.contextweb.com/header/ortb?src=prebid', @@ -40,7 +40,7 @@ export const spec = { interpretResponse: (response, request) => { if (response.body) { - return converter.fromORTB({response: response.body, request: request.data}).bids; + return converter.fromORTB({ response: response.body, request: request.data }).bids; } return []; }, diff --git a/modules/pwbidBidAdapter.js b/modules/pwbidBidAdapter.js index cb383fee630..1d2fa182b79 100644 --- a/modules/pwbidBidAdapter.js +++ b/modules/pwbidBidAdapter.js @@ -1,10 +1,10 @@ -import {getDNT} from '../libraries/dnt/index.js'; import { _each, isBoolean, isNumber, isStr, deepClone, isArray, deepSetValue, inIframe, mergeDeep, deepAccess, logMessage, logInfo, logWarn, logError, isPlainObject } from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; import { config } from '../src/config.js'; import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; import { OUTSTREAM, INSTREAM } from '../src/video.js'; +import { getDNT } from '../libraries/dnt/index.js'; /** * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest @@ -275,7 +275,7 @@ export const spec = { deepSetValue(payload, 'regs.coppa', 1); } - var options = {contentType: 'text/plain'}; + var options = { contentType: 'text/plain' }; _logInfo('buildRequests payload', payload); _logInfo('buildRequests bidderRequest', bidderRequest); diff --git a/modules/pxyzBidAdapter.js b/modules/pxyzBidAdapter.js index 12bd04c744d..681055d78b9 100644 --- a/modules/pxyzBidAdapter.js +++ b/modules/pxyzBidAdapter.js @@ -1,6 +1,6 @@ -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER} from '../src/mediaTypes.js'; -import {isArray, logError, logInfo} from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER } from '../src/mediaTypes.js'; +import { isArray, logError, logInfo } from '../src/utils.js'; /** * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest diff --git a/modules/qortexRtdProvider.js b/modules/qortexRtdProvider.js index 8049f02be81..4b2ad2447e3 100644 --- a/modules/qortexRtdProvider.js +++ b/modules/qortexRtdProvider.js @@ -83,7 +83,7 @@ export function addContextToRequests (reqBidsConfig) { if (checkPercentageOutcome(qortexSessionInfo.groupConfig?.prebidBidEnrichmentPercentage)) { const fragment = qortexSessionInfo.currentSiteContext if (qortexSessionInfo.bidderArray?.length > 0) { - qortexSessionInfo.bidderArray.forEach(bidder => mergeDeep(reqBidsConfig.ortb2Fragments.bidder, {[bidder]: fragment})); + qortexSessionInfo.bidderArray.forEach(bidder => mergeDeep(reqBidsConfig.ortb2Fragments.bidder, { [bidder]: fragment })); } else if (!qortexSessionInfo.bidderArray) { mergeDeep(reqBidsConfig.ortb2Fragments.global, fragment); } else { @@ -101,7 +101,7 @@ export function loadScriptTag(config) { const code = 'qortex'; const groupId = config.params.groupId; const src = 'https://tags.qortex.ai/bootstrapper' - const attr = {'data-group-id': groupId} + const attr = { 'data-group-id': groupId } const tc = config.params.tagConfig Object.keys(tc).forEach(p => { @@ -117,7 +117,7 @@ export function loadScriptTag(config) { } switch (e?.detail?.type) { case 'qx-impression': - const {uid} = e.detail; + const { uid } = e.detail; if (!uid || qortexSessionInfo.impressionIds.has(uid)) { logWarn(`Received invalid billable event due to ${!uid ? 'missing' : 'duplicate'} uid: qx-impression`) return; @@ -162,7 +162,7 @@ export function requestContextData() { * @param {Object} config module config obtained during init */ export function initializeModuleData(config) { - const {groupId, bidders, enableBidEnrichment} = config.params; + const { groupId, bidders, enableBidEnrichment } = config.params; qortexSessionInfo.bidEnrichmentDisabled = enableBidEnrichment !== null ? !enableBidEnrichment : true; qortexSessionInfo.bidderArray = bidders; qortexSessionInfo.impressionIds = new Set(); diff --git a/modules/quantcastBidAdapter.js b/modules/quantcastBidAdapter.js deleted file mode 100644 index 0ee58a927d1..00000000000 --- a/modules/quantcastBidAdapter.js +++ /dev/null @@ -1,297 +0,0 @@ -import {deepAccess, isArray, isEmpty, logError, logInfo} from '../src/utils.js'; -import {ajax} from '../src/ajax.js'; -import {config} from '../src/config.js'; -import {getStorageManager} from '../src/storageManager.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {parseDomain} from '../src/refererDetection.js'; - -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - */ - -const BIDDER_CODE = 'quantcast'; -const DEFAULT_BID_FLOOR = 0.0000000001; - -const QUANTCAST_VENDOR_ID = '11'; -// Check other required purposes on server -const PURPOSE_DATA_COLLECT = '1'; - -export const QUANTCAST_DOMAIN = 'qcx.quantserve.com'; -export const QUANTCAST_TEST_DOMAIN = 's2s-canary.quantserve.com'; -export const QUANTCAST_NET_REVENUE = true; -export const QUANTCAST_TEST_PUBLISHER = 'test-publisher'; -export const QUANTCAST_TTL = 4; -export const QUANTCAST_PROTOCOL = 'https'; -export const QUANTCAST_PORT = '8443'; -export const QUANTCAST_FPA = '__qca'; - -export const storage = getStorageManager({bidderCode: BIDDER_CODE}); - -function makeVideoImp(bid) { - const videoInMediaType = deepAccess(bid, 'mediaTypes.video') || {}; - const videoInParams = deepAccess(bid, 'params.video') || {}; - const video = Object.assign({}, videoInParams, videoInMediaType); - - if (video.playerSize) { - video.w = video.playerSize[0]; - video.h = video.playerSize[1]; - } - const videoCopy = { - mimes: video.mimes, - minduration: video.minduration, - maxduration: video.maxduration, - protocols: video.protocols, - startdelay: video.startdelay, - linearity: video.linearity, - battr: video.battr, - maxbitrate: video.maxbitrate, - playbackmethod: video.playbackmethod, - delivery: video.delivery, - api: video.api, - w: video.w, - h: video.h - } - - return { - video: videoCopy, - placementCode: bid.placementCode, - bidFloor: DEFAULT_BID_FLOOR - }; -} - -function makeBannerImp(bid) { - const sizes = bid.sizes || bid.mediaTypes.banner.sizes; - - return { - banner: { - battr: bid.params.battr, - sizes: sizes.map(size => { - return { - width: size[0], - height: size[1] - }; - }) - }, - placementCode: bid.placementCode, - bidFloor: DEFAULT_BID_FLOOR - }; -} - -function checkTCF(tcData) { - const restrictions = tcData.publisher ? tcData.publisher.restrictions : {}; - const qcRestriction = restrictions && restrictions[PURPOSE_DATA_COLLECT] - ? restrictions[PURPOSE_DATA_COLLECT][QUANTCAST_VENDOR_ID] - : null; - - if (qcRestriction === 0 || qcRestriction === 2) { - // Not allowed by publisher, or requires legitimate interest - return false; - } - - const vendorConsent = tcData.vendor && tcData.vendor.consents && tcData.vendor.consents[QUANTCAST_VENDOR_ID]; - const purposeConsent = tcData.purpose && tcData.purpose.consents && tcData.purpose.consents[PURPOSE_DATA_COLLECT]; - - return !!(vendorConsent && purposeConsent); -} - -function getQuantcastFPA() { - const fpa = storage.getCookie(QUANTCAST_FPA) - return fpa || '' -} - -let hasUserSynced = false; - -/** - * The documentation for Prebid.js Adapter 1.0 can be found at link below, - * http://prebid.org/dev-docs/bidder-adapter-1.html - */ -export const spec = { - code: BIDDER_CODE, - gvlid: QUANTCAST_VENDOR_ID, - supportedMediaTypes: ['banner', 'video'], - - /** - * Verify the `AdUnits.bids` response with `true` for valid request and `false` - * for invalid request. - * - * @param {object} bid - * @return boolean `true` is this is a valid bid, and `false` otherwise - */ - isBidRequestValid(bid) { - return !!bid.params.publisherId; - }, - - /** - * Make a server request when the page asks Prebid.js for bids from a list of - * `BidRequests`. - * - * @param {BidRequest[]} bidRequests A non-empty list of bid requests which should be send to Quantcast server - * @param bidderRequest - * @return ServerRequest information describing the request to the server. - */ - buildRequests(bidRequests, bidderRequest) { - const bids = bidRequests || []; - const gdprConsent = deepAccess(bidderRequest, 'gdprConsent') || {}; - const uspConsent = deepAccess(bidderRequest, 'uspConsent'); - const referrer = deepAccess(bidderRequest, 'refererInfo.ref'); - const page = deepAccess(bidderRequest, 'refererInfo.page') || deepAccess(window, 'location.href'); - const domain = parseDomain(page, {noLeadingWww: true}); - - // Check for GDPR consent for purpose 1, and drop request if consent has not been given - // Remaining consent checks are performed server-side. - if (gdprConsent.gdprApplies) { - if (gdprConsent.vendorData) { - if (!checkTCF(gdprConsent.vendorData)) { - logInfo(`${BIDDER_CODE}: No purpose 1 consent for TCF v2`); - return; - } - } - } - - const bidRequestsList = []; - - bids.forEach(bid => { - let imp; - if (bid.mediaTypes) { - if (bid.mediaTypes.video && bid.mediaTypes.video.context === 'instream') { - imp = makeVideoImp(bid); - } else if (bid.mediaTypes.banner) { - imp = makeBannerImp(bid); - } else { - // Unsupported mediaType - logInfo(`${BIDDER_CODE}: No supported mediaTypes found in ${JSON.stringify(bid.mediaTypes)}`); - return; - } - } else { - // Parse as banner by default - imp = makeBannerImp(bid); - } - - // Request Data Format can be found at https://wiki.corp.qc/display/adinf/QCX - const requestData = { - publisherId: bid.params.publisherId, - requestId: bid.bidId, - imp: [imp], - site: { - page, - referrer, - domain - }, - bidId: bid.bidId, - gdprSignal: gdprConsent.gdprApplies ? 1 : 0, - gdprConsent: gdprConsent.consentString, - uspSignal: uspConsent ? 1 : 0, - uspConsent, - coppa: config.getConfig('coppa') === true ? 1 : 0, - prebidJsVersion: '$prebid.version$', - fpa: getQuantcastFPA() - }; - - const data = JSON.stringify(requestData); - const qcDomain = bid.params.publisherId === QUANTCAST_TEST_PUBLISHER - ? QUANTCAST_TEST_DOMAIN - : QUANTCAST_DOMAIN; - const url = `${QUANTCAST_PROTOCOL}://${qcDomain}:${QUANTCAST_PORT}/qchb`; - - bidRequestsList.push({ - data, - method: 'POST', - url - }); - }); - - return bidRequestsList; - }, - - /** - * Function get called when the browser has received the response from Quantcast server. - * The function parse the response and create a `bidResponse` object containing one/more bids. - * Returns an empty array if no valid bids - * - * Response Data Format can be found at https://wiki.corp.qc/display/adinf/QCX - * - * @param {*} serverResponse A successful response from Quantcast server. - * @return {Bid[]} An array of bids which were nested inside the server. - * - */ - interpretResponse(serverResponse) { - if (serverResponse === undefined) { - logError('Server Response is undefined'); - return []; - } - - const response = serverResponse['body']; - - if (response === undefined || !response.hasOwnProperty('bids')) { - logError('Sub-optimal JSON received from Quantcast server'); - return []; - } - - if (isEmpty(response.bids)) { - // Shortcut response handling if no bids are present - return []; - } - - const bidResponsesList = response.bids.map(bid => { - const { ad, cpm, width, height, creativeId, currency, videoUrl, dealId, meta } = bid; - - const result = { - requestId: response.requestId, - cpm, - width, - height, - ad, - ttl: QUANTCAST_TTL, - creativeId, - netRevenue: QUANTCAST_NET_REVENUE, - currency - }; - - if (videoUrl !== undefined && videoUrl) { - result['vastUrl'] = videoUrl; - result['mediaType'] = 'video'; - } - - if (dealId !== undefined && dealId) { - result['dealId'] = dealId; - } - - if (meta !== undefined && meta.advertiserDomains && isArray(meta.advertiserDomains)) { - result.meta = {}; - result.meta.advertiserDomains = meta.advertiserDomains; - } - - return result; - }); - - return bidResponsesList; - }, - onTimeout(timeoutData) { - const url = `${QUANTCAST_PROTOCOL}://${QUANTCAST_DOMAIN}:${QUANTCAST_PORT}/qchb_notify?type=timeout`; - ajax(url, null, null); - }, - getUserSyncs(syncOptions, serverResponses) { - const syncs = [] - if (!hasUserSynced && syncOptions.pixelEnabled) { - const responseWithUrl = ((serverResponses) || []).find(serverResponse => - deepAccess(serverResponse.body, 'userSync.url') - ); - - if (responseWithUrl) { - const url = deepAccess(responseWithUrl.body, 'userSync.url') - syncs.push({ - type: 'image', - url: url - }); - } - hasUserSynced = true; - } - return syncs; - }, - resetUserSync() { - hasUserSynced = false; - } -}; - -registerBidder(spec); diff --git a/modules/quantcastBidAdapter.md b/modules/quantcastBidAdapter.md deleted file mode 100644 index edbbc538b65..00000000000 --- a/modules/quantcastBidAdapter.md +++ /dev/null @@ -1,74 +0,0 @@ -# Overview - -``` -Module Name: Quantcast Bidder Adapter -Module Type: Bidder Adapter -Maintainer: inventoryteam@quantcast.com -``` - -# Description - -Module that connects to Quantcast demand sources to fetch bids. - -# Test Parameters - -## Sample Banner Ad Unit -```js -const adUnits = [{ - code: 'banner', - sizes: [ - [300, 250] - ], - bids: [ - { - bidder: 'quantcast', - params: { - publisherId: 'test-publisher', // REQUIRED - Publisher ID provided by Quantcast - battr: [1, 2] // OPTIONAL - Array of blocked creative attributes as per OpenRTB Spec List 5.3 - } - } - ], - userSync: { - url: 'https://quantcast.com/pixelUrl' - } -}]; -``` - -## Sample Video Ad Unit -```js -var adUnits = [{ - code: 'video', - mediaTypes: { - video: { - context: 'instream', // required - playerSize: [600, 300] // required - } - }, - bids: [ - { - bidder: 'quantcast', - params: { - publisherId: 'test-publisher', // REQUIRED - Publisher ID provided by Quantcast - // Video object as specified in OpenRTB 2.5 - video: { - 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 - } - } - } - ], - userSync: { - url: 'https://quantcast.com/pixelUrl' - } -}]; -``` diff --git a/modules/quantcastIdSystem.js b/modules/quantcastIdSystem.js deleted file mode 100644 index aeccceb0d10..00000000000 --- a/modules/quantcastIdSystem.js +++ /dev/null @@ -1,230 +0,0 @@ -/** - * This module adds QuantcastID to the User ID module - * The {@link module:modules/userId} module is required - * @module modules/quantcastIdSystem - * @requires module:modules/userId - */ - -import {submodule} from '../src/hook.js' -import {getStorageManager} from '../src/storageManager.js'; -import { triggerPixel, logInfo } from '../src/utils.js'; -import { uspDataHandler, coppaDataHandler, gdprDataHandler } from '../src/adapterManager.js'; -import {MODULE_TYPE_UID} from '../src/activities/modules.js'; - -/** - * @typedef {import('../modules/userId/index.js').Submodule} Submodule - */ - -const QUANTCAST_FPA = '__qca'; -const DEFAULT_COOKIE_EXP_DAYS = 392; // (13 months - 2 days) -const DAY_MS = 86400000; -const PREBID_PCODE = 'p-KceJUEvXN48CE'; -const QSERVE_URL = 'https://pixel.quantserve.com/pixel'; -const QUANTCAST_VENDOR_ID = '11'; -const PURPOSE_DATA_COLLECT = '1'; -const PURPOSE_PRODUCT_IMPROVEMENT = '10'; -const QC_TCF_REQUIRED_PURPOSES = [PURPOSE_DATA_COLLECT, PURPOSE_PRODUCT_IMPROVEMENT]; -const QC_TCF_CONSENT_FIRST_PURPOSES = [PURPOSE_DATA_COLLECT]; -const QC_TCF_CONSENT_ONLY_PUPROSES = [PURPOSE_DATA_COLLECT]; -const GDPR_PRIVACY_STRING = gdprDataHandler.getConsentData(); -const US_PRIVACY_STRING = uspDataHandler.getConsentData(); -const MODULE_NAME = 'quantcastId'; - -export const storage = getStorageManager({moduleType: MODULE_TYPE_UID, moduleName: MODULE_NAME}); - -export function firePixel(clientId, cookieExpDays = DEFAULT_COOKIE_EXP_DAYS) { - // check for presence of Quantcast Measure tag _qevent obj and publisher provided clientID - if (!window._qevents && clientId) { - var fpa = storage.getCookie(QUANTCAST_FPA); - var fpan = '0'; - var domain = quantcastIdSubmodule.findRootDomain(); - var now = new Date(); - var usPrivacyParamString = ''; - var firstPartyParamStrings; - var gdprParamStrings; - - if (!fpa) { - var et = now.getTime(); - var expires = new Date(et + (cookieExpDays * DAY_MS)).toGMTString(); - var rand = Math.round(Math.random() * 2147483647); - fpa = `B0-${rand}-${et}`; - fpan = '1'; - storage.setCookie(QUANTCAST_FPA, fpa, expires, '/', domain, null); - } - - firstPartyParamStrings = `&fpan=${fpan}&fpa=${fpa}`; - gdprParamStrings = '&gdpr=0'; - if (GDPR_PRIVACY_STRING && typeof GDPR_PRIVACY_STRING.gdprApplies === 'boolean' && GDPR_PRIVACY_STRING.gdprApplies) { - gdprParamStrings = `gdpr=1&gdpr_consent=${GDPR_PRIVACY_STRING.consentString}`; - } - if (US_PRIVACY_STRING && typeof US_PRIVACY_STRING === 'string') { - usPrivacyParamString = `&us_privacy=${US_PRIVACY_STRING}`; - } - - const url = QSERVE_URL + - '?d=' + domain + - '&client_id=' + clientId + - '&a=' + PREBID_PCODE + - usPrivacyParamString + - gdprParamStrings + - firstPartyParamStrings; - - triggerPixel(url); - } -}; - -export function hasGDPRConsent(gdprConsent) { - // Check for GDPR consent for purpose 1 and 10, and drop request if consent has not been given - // Remaining consent checks are performed server-side. - if (gdprConsent && typeof gdprConsent.gdprApplies === 'boolean' && gdprConsent.gdprApplies) { - if (!gdprConsent.vendorData) { - return false; - } - return checkTCFv2(gdprConsent.vendorData); - } - return true; -} - -export function checkTCFv2(vendorData, requiredPurposes = QC_TCF_REQUIRED_PURPOSES) { - var gdprApplies = vendorData.gdprApplies; - var purposes = vendorData.purpose; - var vendors = vendorData.vendor; - var qcConsent = vendors && vendors.consents && vendors.consents[QUANTCAST_VENDOR_ID]; - var qcInterest = vendors && vendors.legitimateInterests && vendors.legitimateInterests[QUANTCAST_VENDOR_ID]; - var restrictions = vendorData.publisher ? vendorData.publisher.restrictions : {}; - - if (!gdprApplies) { - return true; - } - - return requiredPurposes.map(function(purpose) { - var purposeConsent = purposes.consents ? purposes.consents[purpose] : false; - var purposeInterest = purposes.legitimateInterests ? purposes.legitimateInterests[purpose] : false; - - var qcRestriction = restrictions && restrictions[purpose] - ? restrictions[purpose][QUANTCAST_VENDOR_ID] - : null; - - if (qcRestriction === 0) { - return false; - } - - // Seek consent or legitimate interest based on our default legal - // basis for the purpose, falling back to the other if possible. - if ( - // we have positive vendor consent - qcConsent && - // there is positive purpose consent - purposeConsent && - // publisher does not require legitimate interest - qcRestriction !== 2 && - // purpose is a consent-first purpose or publisher has explicitly restricted to consent - (QC_TCF_CONSENT_FIRST_PURPOSES.indexOf(purpose) !== -1 || qcRestriction === 1) - ) { - return true; - } else if ( - // publisher does not require consent - qcRestriction !== 1 && - // we have legitimate interest for vendor - qcInterest && - // there is legitimate interest for purpose - purposeInterest && - // purpose's legal basis does not require consent - QC_TCF_CONSENT_ONLY_PUPROSES.indexOf(purpose) === -1 && - // purpose is a legitimate-interest-first purpose or publisher has explicitly restricted to legitimate interest - (QC_TCF_CONSENT_FIRST_PURPOSES.indexOf(purpose) === -1 || qcRestriction === 2) - ) { - return true; - } - - return false; - }).reduce(function(a, b) { - return a && b; - }, true); -} - -/** - * tests if us_privacy consent string is present, us_privacy applies, and notice_given / do-not-sell is set to yes - * @returns {boolean} - */ -export function hasCCPAConsent(usPrivacyConsent) { - if ( - usPrivacyConsent && - typeof usPrivacyConsent === 'string' && - usPrivacyConsent.length === 4 && - usPrivacyConsent.charAt(1) === 'Y' && - usPrivacyConsent.charAt(2) === 'Y' - ) { - return false - } - return true; -} - -/** @type {Submodule} */ -export const quantcastIdSubmodule = { - /** - * used to link submodule with config - * @type {string} - */ - name: MODULE_NAME, - - /** - * Vendor id of Quantcast - * @type {Number} - */ - gvlid: QUANTCAST_VENDOR_ID, - - /** - * decode the stored id value for passing to bid requests - * @function - * @returns {{quantcastId: string} | undefined} - */ - decode(value) { - return value; - }, - - /** - * read Quantcast first party cookie and pass it along in quantcastId - * @function - * @returns {{id: {quantcastId: string} | undefined}}} - */ - getId(config) { - // Consent signals are currently checked on the server side. - const fpa = storage.getCookie(QUANTCAST_FPA); - - const coppa = coppaDataHandler.getCoppa(); - - if (coppa || !hasCCPAConsent(US_PRIVACY_STRING) || !hasGDPRConsent(GDPR_PRIVACY_STRING)) { - var expired = new Date(0).toUTCString(); - var domain = quantcastIdSubmodule.findRootDomain(); - logInfo('QuantcastId: Necessary consent not present for Id, exiting QuantcastId'); - storage.setCookie(QUANTCAST_FPA, '', expired, '/', domain, null); - return undefined; - } - - const configParams = (config && config.params) || {}; - const storageParams = (config && config.storage) || {}; - - var clientId = configParams.clientId || ''; - var cookieExpDays = storageParams.expires || DEFAULT_COOKIE_EXP_DAYS; - - // Callbacks on Event Listeners won't trigger if the event is already complete so this check is required - if (document.readyState === 'complete') { - firePixel(clientId, cookieExpDays); - } else { - window.addEventListener('load', function () { - firePixel(clientId, cookieExpDays); - }); - } - - return { id: fpa ? { quantcastId: fpa } : undefined }; - }, - eids: { - 'quantcastId': { - source: 'quantcast.com', - atype: 1 - }, - } -}; - -submodule('userId', quantcastIdSubmodule); diff --git a/modules/quantcastIdSystem.md b/modules/quantcastIdSystem.md deleted file mode 100644 index 7e90764432b..00000000000 --- a/modules/quantcastIdSystem.md +++ /dev/null @@ -1,46 +0,0 @@ -#### Overview - -``` -Module Name: Quantcast Id System -Module Type: Id System -Maintainer: asig@quantcast.com -``` - -#### Description - - The Prebid Quantcast ID module stores a Quantcast ID in a first party cookie. The ID is then made available in the bid request. The ID from the cookie added in the bidstream allows Quantcast to more accurately bid on publisher inventories without third party cookies, which can result in better monetization across publisher sites from Quantcast. And, it’s free to use! For easier integration, you can work with one of our SSP partners, like PubMatic, who can facilitate the legal process as well as the software integration for you. - - Add it to your Prebid.js package with: - - `gulp build --modules=userId,quantcastIdSystem` - - Quantcast’s privacy policies for the services rendered can be found at - https://www.quantcast.com/privacy/ - - Publishers deploying the module are responsible for ensuring legally required notices and choices for users. - - The Quantcast ID module will only perform any action and return an ID in situations where: - 1. the publisher has not set a ‘coppa' flag on the prebid configuration on their site (see [pbjs.setConfig.coppa](https://docs.prebid.org/dev-docs/publisher-api-reference/setConfig.html#setConfig-coppa)) - 2. there is not a IAB us-privacy string indicating the digital property has provided user notice and the user has made a choice to opt out of sale - 3. if GDPR applies, an IAB TCF v2 string exists indicating that Quantcast does not have consent for purpose 1 (cookies, device identifiers, or other information can be stored or accessed on your device for the purposes presented to you), or an established legal basis (by default legitimate interest) for purpose 10 (your data can be used to improve existing systems and software, and to develop new products). - - #### Quantcast ID Configuration - - | Param under userSync.userIds[] | Scope | Type | Description | Example | - | --- | --- | --- | --- | --- | - | name | Required | String | `"quantcastId"` | `"quantcastId"` | - | params | Optional | Object | Details for Quantcast initialization. | | - | params.ClientID | Optional | String | Optional parameter for Quantcast prebid managed service partners. The parameter is not required for websites with Quantcast Measure tag. Reach out to Quantcast for ClientID if you are not an existing Quantcast prebid managed service partner: quantcast-idsupport@quantcast.com. | | - - - #### Quantcast ID Example - -```js - pbjs.setConfig({ - userSync: { - userIds: [{ - name: "quantcastId" - }] - } - }); -``` diff --git a/modules/r2b2AnalyticsAdapter.js b/modules/r2b2AnalyticsAdapter.js index f8953232982..f452e97e094 100644 --- a/modules/r2b2AnalyticsAdapter.js +++ b/modules/r2b2AnalyticsAdapter.js @@ -1,11 +1,11 @@ -import {ajax} from '../src/ajax.js'; +import { ajax } from '../src/ajax.js'; import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; -import {EVENTS} from '../src/constants.js'; +import { EVENTS } from '../src/constants.js'; import adapterManager from '../src/adapterManager.js'; -import {getGlobal} from '../src/prebidGlobal.js'; -import {isNumber, isPlainObject, isStr, logError, logWarn} from '../src/utils.js'; -import {getRefererInfo} from '../src/refererDetection.js'; -import {config} from '../src/config.js'; +import { getGlobal } from '../src/prebidGlobal.js'; +import { isNumber, isPlainObject, isStr, logError, logWarn } from '../src/utils.js'; +import { getRefererInfo } from '../src/refererDetection.js'; +import { config } from '../src/config.js'; const ADAPTER_VERSION = '1.1.0'; const ADAPTER_CODE = 'r2b2'; @@ -357,9 +357,9 @@ function handleBidderDone (args) { } function getAuctionUnitsData (auctionObject) { let unitsData = {}; - const {bidsReceived, bidsRejected} = auctionObject; + const { bidsReceived, bidsRejected } = auctionObject; const _unitsDataBidReducer = function(data, bid, key) { - const {adUnitCode, bidder} = bid; + const { adUnitCode, bidder } = bid; data[adUnitCode] = data[adUnitCode] || {}; data[adUnitCode][key] = data[adUnitCode][key] || {}; data[adUnitCode][key][bidder] = (data[adUnitCode][key][bidder] || 0) + 1; @@ -472,7 +472,7 @@ function handleStaleRender (args) { } function handleRenderSuccess (args) { // console.log('render success:', arguments); - const {bid} = args; + const { bid } = args; bidsData[bid.adId].renderTime = Date.now(); const data = { b: bid.bidder, @@ -488,7 +488,7 @@ function handleRenderSuccess (args) { } function handleRenderFailed (args) { // console.log('render failed:', arguments); - const {bid, reason} = args; + const { bid, reason } = args; const data = { b: bid.bidder, u: bid.adUnitCode, @@ -513,7 +513,7 @@ function handleBidViewable (args) { processEvent(event); } -const baseAdapter = adapter({analyticsType}); +const baseAdapter = adapter({ analyticsType }); const r2b2Analytics = Object.assign({}, baseAdapter, { getUrl() { return `${DEFAULT_PROTOCOL}://${LOG_SERVER}/${DEFAULT_EVENT_PATH}` @@ -523,7 +523,7 @@ const r2b2Analytics = Object.assign({}, baseAdapter, { }, enableAnalytics(conf = {}) { if (isPlainObject(conf.options)) { - const {domain, configId, configVer, server} = conf.options; + const { domain, configId, configVer, server } = conf.options; if (!domain || !isStr(domain)) { logWarn(`${MODULE_NAME}: Mandatory parameter 'domain' not configured, analytics disabled`); return @@ -554,7 +554,7 @@ const r2b2Analytics = Object.assign({}, baseAdapter, { baseAdapter.enableAnalytics.call(this, conf); }, track(event) { - const {eventType, args} = event; + const { eventType, args } = event; try { if (!adServerCurrency) { const currencyObj = config.getConfig('currency'); diff --git a/modules/r2b2BidAdapter.js b/modules/r2b2BidAdapter.js index bb645d49e8c..2acca14cc78 100644 --- a/modules/r2b2BidAdapter.js +++ b/modules/r2b2BidAdapter.js @@ -1,10 +1,10 @@ -import {logWarn, logError, triggerPixel, deepSetValue, getParameterByName} from '../src/utils.js'; -import {ortbConverter} from '../libraries/ortbConverter/converter.js' -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {Renderer} from '../src/Renderer.js'; -import {BANNER, VIDEO, NATIVE} from '../src/mediaTypes.js'; -import {pbsExtensions} from '../libraries/pbsExtensions/pbsExtensions.js'; -import {bidderSettings} from '../src/bidderSettings.js'; +import { logWarn, logError, triggerPixel, deepSetValue, getParameterByName } from '../src/utils.js'; +import { ortbConverter } from '../libraries/ortbConverter/converter.js' +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { Renderer } from '../src/Renderer.js'; +import { BANNER, VIDEO, NATIVE } from '../src/mediaTypes.js'; +import { pbsExtensions } from '../libraries/pbsExtensions/pbsExtensions.js'; +import { bidderSettings } from '../src/bidderSettings.js'; const ADAPTER_VERSION = '1.0.0'; const BIDDER_CODE = 'r2b2'; @@ -243,14 +243,14 @@ export const spec = { const responseImpId = responseBid.impid; const requestCurrentImp = requestImps.find((requestImp) => requestImp.id === responseImpId); if (!requestCurrentImp) { - r2b2Error('Cant match bid response.', {impid: Boolean(responseBid.impid)}); + r2b2Error('Cant match bid response.', { impid: Boolean(responseBid.impid) }); continue;// Skip this iteration if there's no match } prebidResponses.push(createPrebidResponseBid(requestCurrentImp, responseBid, response, request.bids)); } }) } catch (e) { - r2b2Error('Error while interpreting response:', {msg: e.message}); + r2b2Error('Error while interpreting response:', { msg: e.message }); } return prebidResponses; }, diff --git a/modules/raveltechRtdProvider.js b/modules/raveltechRtdProvider.js index adac49c3258..72110c20603 100644 --- a/modules/raveltechRtdProvider.js +++ b/modules/raveltechRtdProvider.js @@ -1,6 +1,6 @@ -import {submodule, getHook} from '../src/hook.js'; +import { submodule, getHook } from '../src/hook.js'; import adapterManager from '../src/adapterManager.js'; -import {logInfo, deepClone, isArray, isStr, isPlainObject, logError} from '../src/utils.js'; +import { logInfo, deepClone, isArray, isStr, isPlainObject, logError } from '../src/utils.js'; // Constants const MODULE_NAME = 'raveltech'; @@ -24,10 +24,10 @@ const getAnonymizedEids = (eids) => { return []; } logInfo('Anonymized as byte array of length=', id.length); - return [ { + return [{ ...uid, id - } ]; + }]; }) }) @@ -57,7 +57,7 @@ const wrapBuildRequests = (aliasName, preserveOriginalBid, buildRequests) => { } let requests = preserveOriginalBid ? buildRequests(validBidRequests, ...rest) : []; if (!isArray(requests)) { - requests = [ requests ]; + requests = [requests]; } try { @@ -73,7 +73,7 @@ const wrapBuildRequests = (aliasName, preserveOriginalBid, buildRequests) => { let ravelRequests = buildRequests(ravelBidRequests, ...rest); if (!isArray(ravelRequests) && ravelRequests) { - ravelRequests = [ ravelRequests ]; + ravelRequests = [ravelRequests]; } if (ravelRequests) { ravelRequests.forEach(request => { @@ -84,7 +84,7 @@ const wrapBuildRequests = (aliasName, preserveOriginalBid, buildRequests) => { }) } - return [ ...requests ?? [], ...ravelRequests ?? [] ]; + return [...requests ?? [], ...ravelRequests ?? []]; } catch (e) { logError('Error while generating ravel requests :', e); return requests; diff --git a/modules/readpeakBidAdapter.js b/modules/readpeakBidAdapter.js index 7cb97579394..d8028b6c1c9 100644 --- a/modules/readpeakBidAdapter.js +++ b/modules/readpeakBidAdapter.js @@ -294,12 +294,12 @@ function app(bidderRequest) { } function isMobile() { - return /(ios|ipod|ipad|iphone|android)/i.test(global.navigator.userAgent); + return /(ios|ipod|ipad|iphone|android)/i.test(window.navigator.userAgent); } function isConnectedTV() { return /(smart[-]?tv|hbbtv|appletv|googletv|hdmi|netcast\.tv|viera|nettv|roku|\bdtv\b|sonydtv|inettvbrowser|\btv\b)/i.test( - global.navigator.userAgent + window.navigator.userAgent ); } diff --git a/modules/reconciliationRtdProvider.js b/modules/reconciliationRtdProvider.js index 46486923c0a..11074b9a286 100644 --- a/modules/reconciliationRtdProvider.js +++ b/modules/reconciliationRtdProvider.js @@ -16,9 +16,9 @@ * @property {?boolean} allowAccess */ -import {submodule} from '../src/hook.js'; -import {ajaxBuilder} from '../src/ajax.js'; -import {generateUUID, isGptPubadsDefined, logError, timestamp} from '../src/utils.js'; +import { submodule } from '../src/hook.js'; +import { ajaxBuilder } from '../src/ajax.js'; +import { generateUUID, isGptPubadsDefined, logError, timestamp } from '../src/utils.js'; /** * @typedef {import('../modules/rtdModule/index.js').RtdSubmodule} RtdSubmodule @@ -27,7 +27,8 @@ import {generateUUID, isGptPubadsDefined, logError, timestamp} from '../src/util /** @type {Object} */ const MessageType = { IMPRESSION_REQUEST: 'rsdk:impression:req', - IMPRESSION_RESPONSE: 'rsdk:impression:res'}; + IMPRESSION_RESPONSE: 'rsdk:impression:res' +}; /** @type {ModuleParams} */ const DEFAULT_PARAMS = { initUrl: 'https://confirm.fiduciadlt.com/init', diff --git a/modules/relaidoBidAdapter.js b/modules/relaidoBidAdapter.js index 1ef3be58798..2423bf69149 100644 --- a/modules/relaidoBidAdapter.js +++ b/modules/relaidoBidAdapter.js @@ -23,7 +23,7 @@ const ADAPTER_VERSION = '1.2.2'; const DEFAULT_TTL = 300; const UUID_KEY = 'relaido_uuid'; -const storage = getStorageManager({bidderCode: BIDDER_CODE}); +const storage = getStorageManager({ bidderCode: BIDDER_CODE }); function isBidRequestValid(bid) { if (!deepAccess(bid, 'params.placementId')) { diff --git a/modules/relevadRtdProvider.js b/modules/relevadRtdProvider.js index 41b2ee797e5..eba2fdde48a 100644 --- a/modules/relevadRtdProvider.js +++ b/modules/relevadRtdProvider.js @@ -6,11 +6,11 @@ * @requires module:modules/realTimeData */ -import {deepSetValue, isEmpty, logError, mergeDeep} from '../src/utils.js'; -import {submodule} from '../src/hook.js'; -import {ajax} from '../src/ajax.js'; -import {config} from '../src/config.js'; -import {getRefererInfo} from '../src/refererDetection.js'; +import { deepSetValue, isEmpty, logError, mergeDeep } from '../src/utils.js'; +import { submodule } from '../src/hook.js'; +import { ajax } from '../src/ajax.js'; +import { config } from '../src/config.js'; +import { getRefererInfo } from '../src/refererDetection.js'; const MODULE_NAME = 'realTimeData'; const SUBMODULE_NAME = 'RelevadRTDModule'; @@ -116,7 +116,7 @@ function composeOrtb2Data(rtdData, prefix) { const contentSegments = { name: 'relevad', ext: { segtax: content.segtax }, - segment: content.segs.map(x => { return {id: x}; }) + segment: content.segs.map(x => { return { id: x }; }) }; deepSetValue(addOrtb2, prefix + '.content.data', [contentSegments]); } @@ -174,7 +174,7 @@ function filterByScore(dict, minscore) { * @return {object} Filtered RTD */ function getFiltered(data, minscore) { - const relevadData = {'segments': []}; + const relevadData = { 'segments': [] }; minscore = minscore && typeof minscore === 'number' ? minscore : 30; @@ -182,11 +182,11 @@ function getFiltered(data, minscore) { const pcats = filterByScore(data.pcats, minscore) || cats; const scats = filterByScore(data.scats, minscore) || pcats; const cattax = (data.cattax || data.cattax === undefined) ? data.cattax : CATTAX_IAB; - relevadData.categories = {cat: cats, pagecat: pcats, sectioncat: scats, cattax: cattax}; + relevadData.categories = { cat: cats, pagecat: pcats, sectioncat: scats, cattax: cattax }; const contsegs = filterByScore(data.contsegs, minscore); const segtax = data.segtax ? data.segtax : SEGTAX_IAB; - relevadData.content = {segs: contsegs, segtax: segtax}; + relevadData.content = { segs: contsegs, segtax: segtax }; try { if (data && data.segments) { @@ -283,7 +283,7 @@ export function addRtdData(reqBids, data, moduleConfig) { }); }); - serverData = {...serverData, ...relevadData}; + serverData = { ...serverData, ...relevadData }; return adUnits; } @@ -329,7 +329,7 @@ function onAuctionEnd(auctionDetails, config, userConsent) { }); entries(adunitObj).forEach(([adunitCode, bidsReceived]) => { - adunits.push({code: adunitCode, bids: bidsReceived}); + adunits.push({ code: adunitCode, bids: bidsReceived }); }); const data = { diff --git a/modules/relevantdigitalBidAdapter.js b/modules/relevantdigitalBidAdapter.js index c776022749d..6e9a1a442d3 100644 --- a/modules/relevantdigitalBidAdapter.js +++ b/modules/relevantdigitalBidAdapter.js @@ -1,9 +1,9 @@ -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {ortbConverter} from '../libraries/ortbConverter/converter.js' -import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js'; -import {config} from '../src/config.js'; -import {pbsExtensions} from '../libraries/pbsExtensions/pbsExtensions.js' -import {deepSetValue, isEmpty, deepClone, shuffle, triggerPixel, deepAccess} from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { ortbConverter } from '../libraries/ortbConverter/converter.js' +import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; +import { config } from '../src/config.js'; +import { pbsExtensions } from '../libraries/pbsExtensions/pbsExtensions.js' +import { deepSetValue, isEmpty, deepClone, shuffle, triggerPixel, deepAccess } from '../src/utils.js'; const BIDDER_CODE = 'relevantdigital'; @@ -111,7 +111,7 @@ export const spec = { buildRequests(bidRequests, bidderRequest) { const { bidder } = bidRequests[0]; const cfg = getBidderConfig(bidRequests); - const data = converter.toORTB({bidRequests, bidderRequest}); + const data = converter.toORTB({ bidRequests, bidderRequest }); /** Set tmax, in general this will be timeout - pbsBufferMs */ const pbjsTimeout = bidderRequest.timeout || 1000; @@ -147,11 +147,11 @@ export const spec = { Object.entries(MODIFIERS).forEach(([field, combineFn]) => { const obj = resp.ext?.[field]; if (!isEmpty(obj)) { - resp.ext[field] = {[bidder]: combineFn(Object.values(obj))}; + resp.ext[field] = { [bidder]: combineFn(Object.values(obj)) }; } }); - const bids = converter.fromORTB({response: resp, request: request.data}).bids; + const bids = converter.fromORTB({ response: resp, request: request.data }).bids; return bids; }, diff --git a/modules/responsiveAdsBidAdapter.js b/modules/responsiveAdsBidAdapter.js index a33a52f5644..b9e366958c4 100644 --- a/modules/responsiveAdsBidAdapter.js +++ b/modules/responsiveAdsBidAdapter.js @@ -1,4 +1,4 @@ -import {registerBidder} from '../src/adapters/bidderFactory.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER } from '../src/mediaTypes.js'; import { ortbConverter } from '../libraries/ortbConverter/converter.js' import { diff --git a/modules/retailspotBidAdapter.js b/modules/retailspotBidAdapter.js index 5e11e95787e..0b031d0e85d 100644 --- a/modules/retailspotBidAdapter.js +++ b/modules/retailspotBidAdapter.js @@ -1,6 +1,6 @@ -import {buildUrl, deepAccess, parseSizesInput} from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER, VIDEO} from '../src/mediaTypes.js'; +import { buildUrl, deepAccess, parseSizesInput } from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; /** * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest diff --git a/modules/revantageBidAdapter.js b/modules/revantageBidAdapter.js new file mode 100644 index 00000000000..0a0186d5b59 --- /dev/null +++ b/modules/revantageBidAdapter.js @@ -0,0 +1,407 @@ +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { deepClone, deepAccess, logWarn, logError, triggerPixel } from '../src/utils.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; + +const BIDDER_CODE = 'revantage'; +const ENDPOINT_URL = 'https://bid.revantage.io/bid'; +const SYNC_URL = 'https://sync.revantage.io/sync'; + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [BANNER, VIDEO], + + isBidRequestValid: function(bid) { + return !!(bid && bid.params && bid.params.feedId); + }, + + buildRequests: function(validBidRequests, bidderRequest) { + // Handle null/empty bid requests + if (!validBidRequests || validBidRequests.length === 0) { + return []; + } + + // All bid requests in a batch must have the same feedId + // If not, we log a warning and return an empty array + const feedId = validBidRequests[0]?.params?.feedId; + const allSameFeedId = validBidRequests.every(bid => bid.params.feedId === feedId); + if (!allSameFeedId) { + logWarn('Revantage: All bid requests in a batch must have the same feedId'); + return []; + } + + try { + const openRtbBidRequest = makeOpenRtbRequest(validBidRequests, bidderRequest); + return { + method: 'POST', + url: ENDPOINT_URL + '?feed=' + encodeURIComponent(feedId), + data: JSON.stringify(openRtbBidRequest), + options: { + contentType: 'text/plain', + withCredentials: false + }, + bidRequests: validBidRequests + }; + } catch (e) { + logError('Revantage: buildRequests failed', e); + return []; + } + }, + + interpretResponse: function(serverResponse, request) { + const bids = []; + const resp = serverResponse.body; + const originalBids = request.bidRequests || []; + const bidIdMap = {}; + originalBids.forEach(b => { bidIdMap[b.bidId] = b; }); + + if (!resp || !Array.isArray(resp.seatbid)) return bids; + + resp.seatbid.forEach(seatbid => { + if (Array.isArray(seatbid.bid)) { + seatbid.bid.forEach(rtbBid => { + const originalBid = bidIdMap[rtbBid.impid]; + if (!originalBid || !rtbBid.price || rtbBid.price <= 0) return; + + // Check for ad markup + const hasAdMarkup = !!(rtbBid.adm || rtbBid.vastXml || rtbBid.vastUrl); + if (!hasAdMarkup) { + logWarn('Revantage: No ad markup in bid'); + return; + } + + const bidResponse = { + requestId: originalBid.bidId, + cpm: rtbBid.price, + width: rtbBid.w || getFirstSize(originalBid, 0, 300), + height: rtbBid.h || getFirstSize(originalBid, 1, 250), + creativeId: rtbBid.crid || rtbBid.id || rtbBid.adid || 'revantage-' + Date.now(), + dealId: rtbBid.dealid, + currency: resp.cur || 'USD', + netRevenue: true, + ttl: 300, + meta: { + advertiserDomains: rtbBid.adomain || [], + dsp: seatbid.seat || 'unknown', + networkName: 'Revantage' + } + }; + + // Add burl for server-side win notification + if (rtbBid.burl) { + bidResponse.burl = rtbBid.burl; + } + + // Determine if this is a video bid + // FIX: Check for VAST content in adm even for multi-format ad units + const isVideo = (rtbBid.ext && rtbBid.ext.mediaType === 'video') || + rtbBid.vastXml || rtbBid.vastUrl || + isVastAdm(rtbBid.adm) || + (originalBid.mediaTypes && originalBid.mediaTypes.video && + !originalBid.mediaTypes.banner); + + if (isVideo) { + bidResponse.mediaType = VIDEO; + bidResponse.vastXml = rtbBid.vastXml || rtbBid.adm; + bidResponse.vastUrl = rtbBid.vastUrl; + + if (!bidResponse.vastUrl && !bidResponse.vastXml) { + logWarn('Revantage: Video bid missing VAST content'); + return; + } + } else { + bidResponse.mediaType = BANNER; + bidResponse.ad = rtbBid.adm; + + if (!bidResponse.ad) { + logWarn('Revantage: Banner bid missing ad markup'); + return; + } + } + + // Add DSP price if available + if (rtbBid.ext && rtbBid.ext.dspPrice) { + bidResponse.meta.dspPrice = rtbBid.ext.dspPrice; + } + + bids.push(bidResponse); + }); + } + }); + + return bids; + }, + + getUserSyncs: function(syncOptions, serverResponses, gdprConsent, uspConsent, gppConsent) { + const syncs = []; + let params = '?cb=' + new Date().getTime(); + + if (gdprConsent) { + if (typeof gdprConsent.gdprApplies === 'boolean') { + params += '&gdpr=' + (gdprConsent.gdprApplies ? 1 : 0); + } + if (typeof gdprConsent.consentString === 'string') { + params += '&gdpr_consent=' + encodeURIComponent(gdprConsent.consentString); + } + } + + if (uspConsent && typeof uspConsent === 'string') { + params += '&us_privacy=' + encodeURIComponent(uspConsent); + } + + if (gppConsent) { + if (gppConsent.gppString) { + params += '&gpp=' + encodeURIComponent(gppConsent.gppString); + } + if (gppConsent.applicableSections) { + params += '&gpp_sid=' + encodeURIComponent(gppConsent.applicableSections.join(',')); + } + } + + if (syncOptions.iframeEnabled) { + syncs.push({ type: 'iframe', url: SYNC_URL + params }); + } + if (syncOptions.pixelEnabled) { + syncs.push({ type: 'image', url: SYNC_URL + params + '&tag=img' }); + } + + return syncs; + }, + + onBidWon: function(bid) { + if (bid.burl) { + triggerPixel(bid.burl); + } + } +}; + +// === MAIN RTB BUILDER === +function makeOpenRtbRequest(validBidRequests, bidderRequest) { + const imp = validBidRequests.map(bid => { + const sizes = getSizes(bid); + const floor = getBidFloorEnhanced(bid); + + const impression = { + id: bid.bidId, + tagid: bid.adUnitCode, + bidfloor: floor, + ext: { + feedId: deepAccess(bid, 'params.feedId'), + bidder: { + placementId: deepAccess(bid, 'params.placementId'), + publisherId: deepAccess(bid, 'params.publisherId') + } + } + }; + + // Add banner specs + if (bid.mediaTypes && bid.mediaTypes.banner) { + impression.banner = { + w: sizes[0][0], + h: sizes[0][1], + format: sizes.map(size => ({ w: size[0], h: size[1] })) + }; + } + + // Add video specs + if (bid.mediaTypes && bid.mediaTypes.video) { + const video = bid.mediaTypes.video; + impression.video = { + mimes: video.mimes || ['video/mp4', 'video/webm'], + minduration: video.minduration || 0, + maxduration: video.maxduration || 60, + protocols: video.protocols || [2, 3, 5, 6], + w: getVideoSize(video.playerSize, 0, 640), + h: getVideoSize(video.playerSize, 1, 360), + placement: video.placement || 1, + playbackmethod: video.playbackmethod || [1, 2], + api: video.api || [1, 2], + skip: video.skip || 0, + skipmin: video.skipmin || 0, + skipafter: video.skipafter || 0, + pos: video.pos || 0, + startdelay: video.startdelay || 0, + linearity: video.linearity || 1 + }; + } + + return impression; + }); + + let user = {}; + if (validBidRequests[0] && validBidRequests[0].userIdAsEids) { + user.eids = deepClone(validBidRequests[0].userIdAsEids); + } + + const ortb2 = bidderRequest.ortb2 || {}; + const site = { + domain: typeof window !== 'undefined' ? window.location.hostname : '', + page: typeof window !== 'undefined' ? window.location.href : '', + ref: typeof document !== 'undefined' ? document.referrer : '' + }; + + // Merge ortb2 site data + if (ortb2.site) { + Object.assign(site, deepClone(ortb2.site)); + } + + const device = deepClone(ortb2.device) || {}; + // Add basic device info if not present + if (!device.ua) { + device.ua = typeof navigator !== 'undefined' ? navigator.userAgent : ''; + } + if (!device.language) { + device.language = typeof navigator !== 'undefined' ? navigator.language : ''; + } + if (!device.w) { + device.w = typeof screen !== 'undefined' ? screen.width : 0; + } + if (!device.h) { + device.h = typeof screen !== 'undefined' ? screen.height : 0; + } + if (!device.devicetype) { + device.devicetype = getDeviceType(); + } + + const regs = { ext: {} }; + if (bidderRequest.gdprConsent) { + regs.ext.gdpr = bidderRequest.gdprConsent.gdprApplies ? 1 : 0; + user.ext = { consent: bidderRequest.gdprConsent.consentString }; + } + if (bidderRequest.uspConsent) { + regs.ext.us_privacy = bidderRequest.uspConsent; + } + + // Add GPP consent + if (bidderRequest.gppConsent) { + if (bidderRequest.gppConsent.gppString) { + regs.ext.gpp = bidderRequest.gppConsent.gppString; + } + if (bidderRequest.gppConsent.applicableSections) { + // Send as array, not comma-separated string + regs.ext.gpp_sid = bidderRequest.gppConsent.applicableSections; + } + } + + // Get supply chain + const schain = bidderRequest.schain || (validBidRequests[0] && validBidRequests[0].schain); + + return { + id: bidderRequest.auctionId, + imp: imp, + site: site, + device: device, + user: user, + regs: regs, + schain: schain, + tmax: bidderRequest.timeout || 1000, + cur: ['USD'], + ext: { + prebid: { + version: '$prebid.version$' + } + } + }; +} + +// === UTILS === +function getSizes(bid) { + if (bid.mediaTypes && bid.mediaTypes.banner && Array.isArray(bid.mediaTypes.banner.sizes) && bid.mediaTypes.banner.sizes.length > 0) { + return bid.mediaTypes.banner.sizes; + } + if (bid.mediaTypes && bid.mediaTypes.video && bid.mediaTypes.video.playerSize && bid.mediaTypes.video.playerSize.length > 0) { + return bid.mediaTypes.video.playerSize; + } + if (bid.sizes && bid.sizes.length > 0) { + return bid.sizes; + } + return [[300, 250]]; +} + +function getFirstSize(bid, index, defaultVal) { + const sizes = getSizes(bid); + return (sizes && sizes[0] && sizes[0][index]) || defaultVal; +} + +/** + * Safely extract video dimensions from playerSize. + * Handles both nested [[640, 480]] and flat [640, 480] formats. + * @param {Array} playerSize - video.playerSize from mediaTypes config + * @param {number} index - 0 for width, 1 for height + * @param {number} defaultVal - fallback value + * @returns {number} + */ +function getVideoSize(playerSize, index, defaultVal) { + if (!playerSize || !Array.isArray(playerSize) || playerSize.length === 0) { + return defaultVal; + } + // Nested: [[640, 480]] or [[640, 480], [320, 240]] + if (Array.isArray(playerSize[0])) { + return playerSize[0][index] || defaultVal; + } + // Flat: [640, 480] + if (typeof playerSize[0] === 'number') { + return playerSize[index] || defaultVal; + } + return defaultVal; +} + +/** + * Detect if adm content is VAST XML (for multi-format video detection). + * @param {string} adm - ad markup string + * @returns {boolean} + */ +function isVastAdm(adm) { + if (typeof adm !== 'string') return false; + const trimmed = adm.trim(); + return trimmed.startsWith(' floor && floorInfo.currency === 'USD' && !isNaN(floorInfo.floor)) { + floor = floorInfo.floor; + } + } catch (e) { + // Continue to next size + } + } + + // Fallback to general floor + if (floor === 0) { + try { + const floorInfo = bid.getFloor({ currency: 'USD', mediaType: mediaType, size: '*' }); + if (typeof floorInfo === 'object' && floorInfo.currency === 'USD' && !isNaN(floorInfo.floor)) { + floor = floorInfo.floor; + } + } catch (e) { + logWarn('Revantage: getFloor threw error', e); + } + } + } + return floor; +} + +function getDeviceType() { + if (typeof screen === 'undefined') return 1; + const width = screen.width; + const ua = typeof navigator !== 'undefined' ? navigator.userAgent : ''; + + if (/iPhone|iPod/i.test(ua) || (width < 768 && /Mobile/i.test(ua))) return 2; // Mobile + if (/iPad/i.test(ua) || (width >= 768 && width < 1024)) return 5; // Tablet + return 1; // Desktop/PC +} + +// === REGISTER === +registerBidder(spec); diff --git a/modules/revantageBidAdapter.md b/modules/revantageBidAdapter.md new file mode 100644 index 00000000000..42a4ef4198d --- /dev/null +++ b/modules/revantageBidAdapter.md @@ -0,0 +1,34 @@ +# Overview + +``` +Module Name: ReVantage Bidder Adapter +Module Type: ReVantage Bidder Adapter +Maintainer: bern@revantage.io +``` + +# Description + +Connects to ReVantage exchange for bids. +ReVantage bid adapter supports Banner and Video. + +# Test Parameters +``` + var adUnits = [ + // Will return static test banner + { + code: 'adunit', + mediaTypes: { + banner: { + sizes: [ [300, 250], [320, 50] ], + } + }, + bids: [ + { + bidder: 'revantage', + params: { + feedId: 'testfeed', + } + } + ] + } +``` diff --git a/modules/revcontentBidAdapter.js b/modules/revcontentBidAdapter.js index 258f2fc6fb0..81c8a6907bf 100644 --- a/modules/revcontentBidAdapter.js +++ b/modules/revcontentBidAdapter.js @@ -1,12 +1,12 @@ // jshint esversion: 6, es3: false, node: true 'use strict'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER, NATIVE} from '../src/mediaTypes.js'; -import {_map, deepAccess, isFn, parseGPTSingleSizeArrayToRtbSize, triggerPixel} from '../src/utils.js'; -import {parseDomain} from '../src/refererDetection.js'; -import {convertOrtbRequestToProprietaryNative} from '../src/native.js'; -import {getAdUnitSizes} from '../libraries/sizeUtils/sizeUtils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, NATIVE } from '../src/mediaTypes.js'; +import { _map, deepAccess, isFn, parseGPTSingleSizeArrayToRtbSize, triggerPixel } from '../src/utils.js'; +import { parseDomain } from '../src/refererDetection.js'; +import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; +import { getAdUnitSizes } from '../libraries/sizeUtils/sizeUtils.js'; const BIDDER_CODE = 'revcontent'; const GVLID = 203; @@ -56,7 +56,7 @@ export const spec = { } if (typeof domain === 'undefined') { - domain = parseDomain(refererInfo, {noPort: true}); + domain = parseDomain(refererInfo, { noPort: true }); } var endpoint = 'https://' + host + '/rtb?apiKey=' + apiKey + '&userId=' + userId; diff --git a/modules/revnewBidAdapter.ts b/modules/revnewBidAdapter.ts index ecdcdb5e845..c7391b3e485 100644 --- a/modules/revnewBidAdapter.ts +++ b/modules/revnewBidAdapter.ts @@ -1,8 +1,8 @@ import { deepSetValue, generateUUID, logError } from '../src/utils.js'; -import {getStorageManager} from '../src/storageManager.js'; -import {AdapterRequest, BidderSpec, registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js'; -import {ortbConverter} from '../libraries/ortbConverter/converter.js' +import { getStorageManager } from '../src/storageManager.js'; +import { AdapterRequest, BidderSpec, registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; +import { ortbConverter } from '../libraries/ortbConverter/converter.js' import { interpretResponse, enrichImp, enrichRequest, getAmxId, getLocalStorageFunctionGenerator, getUserSyncs } from '../libraries/nexx360Utils/index.js'; import { BidRequest, ClientBidderRequest } from '../src/adapterManager.js'; @@ -75,7 +75,7 @@ const buildRequests = ( bidRequests: BidRequest[], bidderRequest: ClientBidderRequest, ): AdapterRequest => { - const data:ORTBRequest = converter.toORTB({bidRequests, bidderRequest}) + const data:ORTBRequest = converter.toORTB({ bidRequests, bidderRequest }) const adapterRequest:AdapterRequest = { method: 'POST', url: REQUEST_URL, diff --git a/modules/rewardedInterestIdSystem.js b/modules/rewardedInterestIdSystem.js index 8cf514f372b..54fa2bb401f 100644 --- a/modules/rewardedInterestIdSystem.js +++ b/modules/rewardedInterestIdSystem.js @@ -28,8 +28,8 @@ * @return {Promise} */ -import {submodule} from '../src/hook.js'; -import {logError} from '../src/utils.js'; +import { submodule } from '../src/hook.js'; +import { logError } from '../src/utils.js'; export const MODULE_NAME = 'rewardedInterestId'; export const SOURCE = 'rewardedinterest.com'; @@ -103,7 +103,7 @@ export const rewardedInterestIdSubmodule = { * @returns {{rewardedInterestId: string}|undefined} */ decode(value) { - return value ? {[MODULE_NAME]: value} : undefined; + return value ? { [MODULE_NAME]: value } : undefined; }, /** diff --git a/modules/rhythmoneBidAdapter.js b/modules/rhythmoneBidAdapter.js index e3d4b15aebc..f7518d0d916 100644 --- a/modules/rhythmoneBidAdapter.js +++ b/modules/rhythmoneBidAdapter.js @@ -1,9 +1,9 @@ 'use strict'; -import {getDNT} from '../libraries/dnt/index.js'; import { deepAccess, parseSizesInput, isArray } from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; +import { getDNT } from '../libraries/dnt/index.js'; function RhythmOneBidAdapter() { this.code = 'rhythmone'; diff --git a/modules/richaudienceBidAdapter.js b/modules/richaudienceBidAdapter.js index cb551bb0b62..5821e8e89c9 100644 --- a/modules/richaudienceBidAdapter.js +++ b/modules/richaudienceBidAdapter.js @@ -1,8 +1,8 @@ -import {deepAccess, triggerPixel} from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {config} from '../src/config.js'; -import {BANNER, VIDEO} from '../src/mediaTypes.js'; -import {Renderer} from '../src/Renderer.js'; +import { deepAccess, triggerPixel } from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { config } from '../src/config.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; +import { Renderer } from '../src/Renderer.js'; import { getCurrencyFromBidderRequest } from '../libraries/ortb2Utils/currency.js'; const BIDDER_CODE = 'richaudience'; @@ -11,7 +11,7 @@ let REFERER = ''; export const spec = { code: BIDDER_CODE, gvlid: 108, - aliases: [{code: 'ra', gvlid: 108}], + aliases: [{ code: 'ra', gvlid: 108 }], supportedMediaTypes: [BANNER, VIDEO], /*** @@ -339,7 +339,7 @@ function raiGetFloor(bid, config) { } function raiGetTimeoutURL(data) { - const {params, timeout} = data[0] + const { params, timeout } = data[0] let url = 'https://s.richaudience.com/err/?ec=6&ev=[timeout_publisher]&pla=[placement_hash]&int=PREBID&pltfm=&node=&dm=[domain]'; url = url.replace('[timeout_publisher]', timeout) diff --git a/modules/ringieraxelspringerBidAdapter.js b/modules/ringieraxelspringerBidAdapter.js deleted file mode 100644 index c5b7e000f87..00000000000 --- a/modules/ringieraxelspringerBidAdapter.js +++ /dev/null @@ -1,8 +0,0 @@ -/** - * Backward-compatibility shim for ringieraxelspringer bidder. - * This bidder has been renamed to 'das'. - * - * This file will be removed in Prebid 11. - * See dasBidAdapter.js for implementation. - */ -export { spec } from './dasBidAdapter.js'; // eslint-disable-line prebid/validate-imports diff --git a/modules/ringieraxelspringerBidAdapter.md b/modules/ringieraxelspringerBidAdapter.md deleted file mode 100644 index 4527d9f8c6d..00000000000 --- a/modules/ringieraxelspringerBidAdapter.md +++ /dev/null @@ -1,8 +0,0 @@ -# Overview - -The `ringieraxelspringer` bidder has been renamed to `das`. -Please use the `das` bidder code instead. - -See [dasBidAdapter.md](./dasBidAdapter.md) for documentation. - -This adapter will be removed in Prebid 11. diff --git a/modules/riseBidAdapter.js b/modules/riseBidAdapter.js index a6970e959ce..ecd4711a51b 100644 --- a/modules/riseBidAdapter.js +++ b/modules/riseBidAdapter.js @@ -1,6 +1,6 @@ -import {logWarn} from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {makeBaseSpec} from '../libraries/riseUtils/index.js'; +import { logWarn } from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { makeBaseSpec } from '../libraries/riseUtils/index.js'; import { ALIASES, BASE_URL, diff --git a/modules/rivrAnalyticsAdapter.js b/modules/rivrAnalyticsAdapter.js index 476d3d21337..51772629d5a 100644 --- a/modules/rivrAnalyticsAdapter.js +++ b/modules/rivrAnalyticsAdapter.js @@ -1,12 +1,12 @@ -import {ajax} from '../src/ajax.js'; +import { ajax } from '../src/ajax.js'; import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; import * as utils from '../src/utils.js'; -import {getGlobal} from '../src/prebidGlobal.js'; +import { getGlobal } from '../src/prebidGlobal.js'; const analyticsType = 'endpoint'; -const rivrAnalytics = Object.assign(adapter({analyticsType}), { +const rivrAnalytics = Object.assign(adapter({ analyticsType }), { track({ eventType, args }) { if (window.rivraddon && window.rivraddon.analytics && window.rivraddon.analytics.getContext() && window.rivraddon.analytics.trackPbjsEvent) { utils.logInfo(`ARGUMENTS FOR TYPE: ============= ${eventType}`, args); @@ -21,7 +21,7 @@ rivrAnalytics.originEnableAnalytics = rivrAnalytics.enableAnalytics; // override enableAnalytics so we can get access to the config passed in from the page rivrAnalytics.enableAnalytics = (config) => { if (window.rivraddon && window.rivraddon.analytics) { - window.rivraddon.analytics.enableAnalytics(config, {utils, ajax, pbjsGlobalVariable: getGlobal()}); + window.rivraddon.analytics.enableAnalytics(config, { utils, ajax, pbjsGlobalVariable: getGlobal() }); rivrAnalytics.originEnableAnalytics(config); } }; diff --git a/modules/robustAppsBidAdapter.js b/modules/robustAppsBidAdapter.js index 8331433d222..4d242e0040c 100644 --- a/modules/robustAppsBidAdapter.js +++ b/modules/robustAppsBidAdapter.js @@ -1,6 +1,6 @@ -import {BANNER, VIDEO} from '../src/mediaTypes.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {buildRequests, getUserSyncs, interpretResponse, isBidRequestValid} from '../libraries/xeUtils/bidderUtils.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { buildRequests, getUserSyncs, interpretResponse, isBidRequestValid } from '../libraries/xeUtils/bidderUtils.js'; const BIDDER_CODE = 'robustApps'; const ENDPOINT = 'https://pbjs.rbstsystems.live'; diff --git a/modules/roxotAnalyticsAdapter.js b/modules/roxotAnalyticsAdapter.js index 9cd2bf72b8e..4aca8ef0733 100644 --- a/modules/roxotAnalyticsAdapter.js +++ b/modules/roxotAnalyticsAdapter.js @@ -1,15 +1,15 @@ -import {deepClone, getParameterByName, logError, logInfo} from '../src/utils.js'; +import { deepClone, getParameterByName, logError, logInfo } from '../src/utils.js'; import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import { EVENTS } from '../src/constants.js'; import adapterManager from '../src/adapterManager.js'; -import {ajaxBuilder} from '../src/ajax.js'; -import {getStorageManager} from '../src/storageManager.js'; -import {MODULE_TYPE_ANALYTICS} from '../src/activities/modules.js'; +import { ajaxBuilder } from '../src/ajax.js'; +import { getStorageManager } from '../src/storageManager.js'; +import { MODULE_TYPE_ANALYTICS } from '../src/activities/modules.js'; const MODULE_CODE = 'roxot'; -const storage = getStorageManager({moduleType: MODULE_TYPE_ANALYTICS, moduleName: MODULE_CODE}); +const storage = getStorageManager({ moduleType: MODULE_TYPE_ANALYTICS, moduleName: MODULE_CODE }); const ajax = ajaxBuilder(0); @@ -310,8 +310,8 @@ function handleOtherEvents(eventType, args) { registerEvent(eventType, eventType, args); } -const roxotAdapter = Object.assign(adapter({url: DEFAULT_EVENT_URL, analyticsType}), { - track({eventType, args}) { +const roxotAdapter = Object.assign(adapter({ url: DEFAULT_EVENT_URL, analyticsType }), { + track({ eventType, args }) { switch (eventType) { case AUCTION_INIT: handleAuctionInit(args); diff --git a/modules/rtbhouseBidAdapter.js b/modules/rtbhouseBidAdapter.js index 9aec645b715..87ba9c0fef8 100644 --- a/modules/rtbhouseBidAdapter.js +++ b/modules/rtbhouseBidAdapter.js @@ -1,9 +1,9 @@ -import {deepAccess, deepClone, isArray, logError, mergeDeep, isEmpty, isPlainObject, isNumber, isStr, deepSetValue} from '../src/utils.js'; -import {getOrigin} from '../libraries/getOrigin/index.js'; -import {BANNER, NATIVE} from '../src/mediaTypes.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; +import { deepAccess, deepClone, isArray, logError, mergeDeep, isEmpty, isPlainObject, isNumber, isStr, deepSetValue } from '../src/utils.js'; +import { getOrigin } from '../libraries/getOrigin/index.js'; +import { BANNER, NATIVE } from '../src/mediaTypes.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; -import {convertOrtbRequestToProprietaryNative} from '../src/native.js'; +import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; import { interpretNativeBid, OPENRTB } from '../libraries/precisoUtils/bidNativeUtils.js'; const BIDDER_CODE = 'rtbhouse'; @@ -46,8 +46,8 @@ export const spec = { const consentStr = (bidderRequest.gdprConsent.consentString) ? bidderRequest.gdprConsent.consentString.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '') : ''; const gdpr = bidderRequest.gdprConsent.gdprApplies ? 1 : 0; - request.regs = {ext: {gdpr: gdpr}}; - request.user = {ext: {consent: consentStr}}; + request.regs = { ext: { gdpr: gdpr } }; + request.user = { ext: { consent: consentStr } }; } const bidSchain = validBidRequests[0]?.ortb2?.source?.ext?.schain; if (bidSchain) { @@ -64,7 +64,7 @@ export const spec = { if (request.user && request.user.ext) { request.user.ext = { ...request.user.ext, ...eids }; } else { - request.user = {ext: eids}; + request.user = { ext: eids }; } } @@ -372,7 +372,7 @@ function mapNativeAssets(slot) { * @returns {object} Request Image by OpenRTB Native Ads 1.1 §4.4 */ function mapNativeImage(image, type) { - const img = {type: type}; + const img = { type: type }; if (image.aspect_ratios) { const ratio = image.aspect_ratios[0]; const minWidth = ratio.min_width || 100; diff --git a/modules/rtbhouseBidAdapter.md b/modules/rtbhouseBidAdapter.md index 7fcae1299b2..b8b59aa9edc 100644 --- a/modules/rtbhouseBidAdapter.md +++ b/modules/rtbhouseBidAdapter.md @@ -65,46 +65,3 @@ Please reach out to pmp@rtbhouse.com to receive your own } ]; ``` - -# Protected Audience API (FLEDGE) support -There’s an option to receive demand for Protected Audience API (FLEDGE/PAAPI) -ads using RTB House bid adapter. -Prebid’s [paapiForGpt](https://docs.prebid.org/dev-docs/modules/paapiForGpt.html) -module and Google Ad Manager is currently required. - -The following steps should be taken to setup Protected Audience for RTB House: - -1. Reach out to your RTB House representative for setup coordination. - -2. Build and enable FLEDGE module as described in -[paapiForGpt](https://docs.prebid.org/dev-docs/modules/paapiForGpt.html) -module documentation. - - a. Make sure to enable RTB House bidder to participate in FLEDGE. If there are any other bidders to be allowed for that, add them to the **bidders** array: - ```javascript - pbjs.setConfig({ - paapi: { - bidders: ["rtbhouse"], - enabled: true - } - }); - ``` - - b. If you as a publisher have your own [decisionLogicUrl](https://github.com/WICG/turtledove/blob/main/FLEDGE.md#21-initiating-an-on-device-auction) - you may utilize it by setting up a dedicated `fledgeConfig` object: - ```javascript - pbjs.setConfig({ - paapi: { - bidders: ["rtbhouse"], - enabled: true - }, - fledgeConfig: { - seller: 'https://seller.domain', - decisionLogicUrl: 'https://seller.domain/decisionLogicFile.js', - sellerTimeout: 100 - } - }); - ``` - The `decisionLogicUrl` must be in the same domain as `seller` and has to respond with `X-Allow-FLEDGE: true` http header. - - `sellerTimeout` is optional, defaults to 50 as per spec, will be clamped to 500 if greater. diff --git a/modules/rtbsapeBidAdapter.js b/modules/rtbsapeBidAdapter.js index 7db9c574521..803a7a5c6a7 100644 --- a/modules/rtbsapeBidAdapter.js +++ b/modules/rtbsapeBidAdapter.js @@ -1,8 +1,8 @@ import { deepAccess, triggerPixel } from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER, VIDEO} from '../src/mediaTypes.js'; -import {OUTSTREAM} from '../src/video.js'; -import {Renderer} from '../src/Renderer.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; +import { OUTSTREAM } from '../src/video.js'; +import { Renderer } from '../src/Renderer.js'; /** * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest @@ -145,7 +145,7 @@ function setOutstreamRenderer(bid) { props.xml = bid.vastXml; } bid.renderer.push(() => { - const player = window.sapeRtbPlayerHandler(bid.adUnitCode, bid.width, bid.height, bid.playerMuted, {singleton: true}); + const player = window.sapeRtbPlayerHandler(bid.adUnitCode, bid.width, bid.height, bid.playerMuted, { singleton: true }); props.onComplete = () => player.destroy(); props.onError = () => player.destroy(); player.addSlot(props); diff --git a/modules/rtdModule/index.ts b/modules/rtdModule/index.ts index 1b3bff0baf3..8a358aead21 100644 --- a/modules/rtdModule/index.ts +++ b/modules/rtdModule/index.ts @@ -1,16 +1,16 @@ -import {config} from '../../src/config.js'; -import {getHook, module} from '../../src/hook.js'; -import {logError, logInfo, logWarn, mergeDeep} from '../../src/utils.js'; +import { config } from '../../src/config.js'; +import { getHook, module } from '../../src/hook.js'; +import { logError, logInfo, logWarn, mergeDeep } from '../../src/utils.js'; import * as events from '../../src/events.js'; -import {EVENTS, JSON_MAPPING} from '../../src/constants.js'; -import adapterManager, {gdprDataHandler, gppDataHandler, uspDataHandler} from '../../src/adapterManager.js'; -import {timedAuctionHook} from '../../src/utils/perfMetrics.js'; -import {GDPR_GVLIDS} from '../../src/consentHandler.js'; -import {MODULE_TYPE_RTD} from '../../src/activities/modules.js'; -import {guardOrtb2Fragments} from '../../libraries/objectGuard/ortbGuard.js'; -import {activityParamsBuilder} from '../../src/activities/params.js'; -import type {StartAuctionOptions} from "../../src/prebid.ts"; -import type {ProviderConfig, RTDProvider, RTDProviderConfig} from "./spec.ts"; +import { EVENTS, JSON_MAPPING } from '../../src/constants.js'; +import adapterManager, { gdprDataHandler, gppDataHandler, uspDataHandler } from '../../src/adapterManager.js'; +import { timedAuctionHook } from '../../src/utils/perfMetrics.js'; +import { GDPR_GVLIDS } from '../../src/consentHandler.js'; +import { MODULE_TYPE_RTD } from '../../src/activities/modules.js'; +import { guardOrtb2Fragments } from '../../libraries/objectGuard/ortbGuard.js'; +import { activityParamsBuilder } from '../../src/activities/params.js'; +import type { StartAuctionOptions } from "../../src/prebid.ts"; +import type { ProviderConfig, RTDProvider, RTDProviderConfig } from "./spec.ts"; const activityParams = activityParamsBuilder((al) => adapterManager.resolveAlias(al)); @@ -53,8 +53,7 @@ const setEventsListeners = (function () { [EVENTS.AUCTION_INIT]: ['onAuctionInitEvent'], [EVENTS.AUCTION_END]: ['onAuctionEndEvent', getAdUnitTargeting], [EVENTS.BID_RESPONSE]: ['onBidResponseEvent'], - [EVENTS.BID_REQUESTED]: ['onBidRequestEvent'], - [EVENTS.BID_ACCEPTED]: ['onBidAcceptedEvent'] + [EVENTS.BID_REQUESTED]: ['onBidRequestEvent'] }).forEach(([ev, [handler, preprocess]]) => { events.on(ev as any, (args) => { preprocess && (preprocess as any)(args); @@ -87,7 +86,7 @@ declare module '../../src/config' { } export function init(config) { - const confListener = config.getConfig(MODULE_NAME, ({realTimeData}) => { + const confListener = config.getConfig(MODULE_NAME, ({ realTimeData }) => { if (!realTimeData.dataProviders) { logError('missing parameters for real time module'); return; @@ -122,7 +121,7 @@ function initSubModules() { const sm = ((registeredSubModules) || []).find(s => s.name === provider.name); const initResponse = sm && sm.init && sm.init(provider, _userConsent); if (initResponse) { - subModulesByOrder.push(Object.assign(sm, {config: provider})); + subModulesByOrder.push(Object.assign(sm, { config: provider })); } }); subModules = subModulesByOrder; diff --git a/modules/rtdModule/spec.ts b/modules/rtdModule/spec.ts index 7abf38e1247..0fde369867d 100644 --- a/modules/rtdModule/spec.ts +++ b/modules/rtdModule/spec.ts @@ -1,9 +1,9 @@ -import type {AllConsentData} from "../../src/consentHandler.ts"; -import type {AdUnitCode, ByAdUnit, StorageDisclosure} from "../../src/types/common"; -import {EVENTS} from '../../src/constants.ts'; -import type {EventPayload} from "../../src/events.ts"; -import type {TargetingMap} from "../../src/targeting.ts"; -import type {StartAuctionOptions} from "../../src/prebid.ts"; +import type { AllConsentData } from "../../src/consentHandler.ts"; +import type { AdUnitCode, ByAdUnit, StorageDisclosure } from "../../src/types/common"; +import { EVENTS } from '../../src/constants.ts'; +import type { EventPayload } from "../../src/events.ts"; +import type { TargetingMap } from "../../src/targeting.ts"; +import type { StartAuctionOptions } from "../../src/prebid.ts"; export type RTDProvider = string; @@ -32,8 +32,7 @@ export type RTDProviderConfig

= BaseConfig

& ( type RTDEvent = typeof EVENTS.AUCTION_INIT | typeof EVENTS.AUCTION_END | typeof EVENTS.BID_RESPONSE | - typeof EVENTS.BID_REQUESTED | - typeof EVENTS.BID_ACCEPTED; + typeof EVENTS.BID_REQUESTED; type EventHandlers

= { [EV in RTDEvent]: (payload: EventPayload, config: RTDProviderConfig

, consent: AllConsentData) => void; diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index 47c311ceb9a..699f4654c1f 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -4,7 +4,6 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { config } from '../src/config.js'; import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; import { getGlobal } from '../src/prebidGlobal.js'; -import { Renderer } from '../src/Renderer.js'; import { deepAccess, deepSetValue, @@ -21,8 +20,9 @@ import { _each, isPlainObject } from '../src/utils.js'; -import {getAllOrtbKeywords} from '../libraries/keywords/keywords.js'; -import {getUserSyncParams} from '../libraries/userSyncUtils/userSyncUtils.js'; +import { getAllOrtbKeywords } from '../libraries/keywords/keywords.js'; +import { getUserSyncParams } from '../libraries/userSyncUtils/userSyncUtils.js'; +import { outstreamRenderer } from '../libraries/magniteUtils/outstream.js'; /** * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest @@ -30,8 +30,6 @@ import {getUserSyncParams} from '../libraries/userSyncUtils/userSyncUtils.js'; const DEFAULT_INTEGRATION = 'pbjs_lite'; const DEFAULT_PBS_INTEGRATION = 'pbjs'; -const DEFAULT_RENDERER_URL = 'https://video-outstream.rubiconproject.com/apex-2.2.1.js'; -// renderer code at https://github.com/rubicon-project/apex2 let rubiConf = config.getConfig('rubicon') || {}; // we are saving these as global to this module so that if a pub accidentally overwrites the entire @@ -113,8 +111,10 @@ var sizeMap = { 195: '600x300', 198: '640x360', 199: '640x200', + 210: '1080x1920', 213: '1030x590', 214: '980x360', + 219: '1920x1080', 221: '1x1', 229: '320x180', 230: '2000x1400', @@ -127,7 +127,6 @@ var sizeMap = { 259: '998x200', 261: '480x480', 264: '970x1000', - 265: '1920x1080', 274: '1800x200', 278: '320x500', 282: '320x400', @@ -167,7 +166,7 @@ _each(sizeMap, (item, key) => { export const converter = ortbConverter({ request(buildRequest, imps, bidderRequest, context) { - const {bidRequests} = context; + const { bidRequests } = context; const data = buildRequest(imps, bidderRequest, context); data.cur = ['USD']; data.test = config.getConfig('debug') ? 1 : 0; @@ -187,7 +186,7 @@ export const converter = ortbConverter({ const modules = (getGlobal()).installedModules; if (modules && (!modules.length || modules.indexOf('rubiconAnalyticsAdapter') !== -1)) { - deepSetValue(data, 'ext.prebid.analytics', {'rubicon': {'client-analytics': true}}); + deepSetValue(data, 'ext.prebid.analytics', { 'rubicon': { 'client-analytics': true } }); } addOrtbFirstPartyData(data, bidRequests, bidderRequest.ortb2); @@ -232,7 +231,7 @@ export const converter = ortbConverter({ bidResponse(buildBidResponse, bid, context) { const bidResponse = buildBidResponse(bid, context); bidResponse.meta.mediaType = deepAccess(bid, 'ext.prebid.type'); - const {bidRequest} = context; + const { bidRequest } = context; const [parseSizeWidth, parseSizeHeight] = bidRequest.mediaTypes.video?.context === 'outstream' ? parseSizes(bidRequest, VIDEO) : [undefined, undefined]; // 0 by default to avoid undefined size @@ -240,7 +239,7 @@ export const converter = ortbConverter({ bidResponse.height = bid.h || parseSizeHeight || bidResponse.playerHeight || 0; if (bidResponse.mediaType === VIDEO && bidRequest.mediaTypes.video.context === 'outstream') { - bidResponse.renderer = outstreamRenderer(bidResponse); + bidResponse.renderer = outstreamRenderer(bidResponse, rubiConf.rendererUrl, rubiConf.rendererConfig); } if (deepAccess(bid, 'ext.bidder.rp.advid')) { @@ -316,7 +315,7 @@ export const spec = { }); if (filteredRequests && filteredRequests.length) { - const data = converter.toORTB({bidRequests: filteredRequests, bidderRequest}); + const data = converter.toORTB({ bidRequests: filteredRequests, bidderRequest }); resetImpIdMap(); filteredHttpRequest.push({ @@ -329,7 +328,7 @@ export const spec = { const bannerBidRequests = bidRequests.filter((req) => { const mediaTypes = bidType(req) || []; - const {bidonmultiformat, video} = req.params || {}; + const { bidonmultiformat, video } = req.params || {}; return ( // Send to fastlane if: it must include BANNER and... mediaTypes.includes(BANNER) && ( @@ -534,7 +533,7 @@ export const spec = { // add p_pos only if specified and valid // For SRA we need to explicitly put empty semi colons so AE treats it as empty, instead of copying the latter value - const posMapping = {1: 'atf', 3: 'btf'}; + const posMapping = { 1: 'atf', 3: 'btf' }; const pos = posMapping[deepAccess(bidRequest, 'mediaTypes.banner.pos')] || ''; data['p_pos'] = (params.position === 'atf' || params.position === 'btf') ? params.position : pos; @@ -654,7 +653,7 @@ export const spec = { */ interpretResponse: function (responseObj, request) { responseObj = responseObj.body; - const {data} = request; + const { data } = request; // check overall response if (!responseObj || typeof responseObj !== 'object') { @@ -666,14 +665,14 @@ export const spec = { if (Array.isArray(responseErrors) && responseErrors.length > 0) { logWarn('Rubicon: Error in video response'); } - const bids = converter.fromORTB({request: data, response: responseObj}).bids; + const bids = converter.fromORTB({ request: data, response: responseObj }).bids; return bids; } let ads = responseObj.ads; let lastImpId; let multibid = 0; - const {bidRequest} = request; + const { bidRequest } = request; // video ads array is wrapped in an object if (typeof bidRequest === 'object' && !Array.isArray(bidRequest) && bidType(bidRequest).includes(VIDEO) && typeof ads === 'object') { @@ -739,12 +738,19 @@ export const spec = { [bid.width, bid.height] = sizeMap[ad.size_id].split('x').map(num => Number(num)); } + if (ad.bid_cat && ad.bid_cat.length) { + bid.meta.primaryCatId = ad.bid_cat[0]; + if (ad.bid_cat.length > 1) { + bid.meta.secondaryCatIds = ad.bid_cat.slice(1); + } + } + // add server-side targeting bid.rubiconTargeting = (Array.isArray(ad.targeting) ? ad.targeting : []) .reduce((memo, item) => { memo[item.key] = item.values[0]; return memo; - }, {'rpfl_elemid': associatedBidRequest.adUnitCode}); + }, { 'rpfl_elemid': associatedBidRequest.adUnitCode }); bids.push(bid); } else { @@ -802,71 +808,6 @@ function _renderCreative(script, impId) { `; } -function hideGoogleAdsDiv(adUnit) { - const el = adUnit.querySelector("div[id^='google_ads']"); - if (el) { - el.style.setProperty('display', 'none'); - } -} - -function hideSmartAdServerIframe(adUnit) { - const el = adUnit.querySelector("script[id^='sas_script']"); - const nextSibling = el && el.nextSibling; - if (nextSibling && nextSibling.localName === 'iframe') { - nextSibling.style.setProperty('display', 'none'); - } -} - -function renderBid(bid) { - // hide existing ad units - const adUnitElement = document.getElementById(bid.adUnitCode); - hideGoogleAdsDiv(adUnitElement); - hideSmartAdServerIframe(adUnitElement); - - // configure renderer - const defaultConfig = { - align: 'center', - position: 'append', - closeButton: false, - label: undefined, - collapse: true - }; - const config = { ...defaultConfig, ...bid.renderer.getConfig() }; - bid.renderer.push(() => { - window.MagniteApex.renderAd({ - width: bid.width, - height: bid.height, - vastUrl: bid.vastUrl, - placement: { - attachTo: `#${bid.adUnitCode}`, - align: config.align, - position: config.position - }, - closeButton: config.closeButton, - label: config.label, - collapse: config.collapse - }); - }); -} - -function outstreamRenderer(rtbBid) { - const renderer = Renderer.install({ - id: rtbBid.adId, - url: rubiConf.rendererUrl || DEFAULT_RENDERER_URL, - config: rubiConf.rendererConfig || {}, - loaded: false, - adUnitCode: rtbBid.adUnitCode - }); - - try { - renderer.setRender(renderBid); - } catch (err) { - logWarn('Prebid Error calling setRender on renderer', err); - } - - return renderer; -} - function parseSizes(bid, mediaType) { const params = bid.params; if (mediaType === VIDEO) { @@ -901,8 +842,8 @@ function parseSizes(bid, mediaType) { function applyFPD(bidRequest, mediaType, data) { const BID_FPD = { - user: {ext: {data: {...bidRequest.params.visitor}}}, - site: {ext: {data: {...bidRequest.params.inventory}}} + user: { ext: { data: { ...bidRequest.params.visitor } } }, + site: { ext: { data: { ...bidRequest.params.inventory } } } }; if (bidRequest.params.keywords) BID_FPD.site.keywords = (isArray(bidRequest.params.keywords)) ? bidRequest.params.keywords.join(',') : bidRequest.params.keywords; @@ -913,8 +854,8 @@ function applyFPD(bidRequest, mediaType, data) { const gpid = deepAccess(bidRequest, 'ortb2Imp.ext.gpid'); const dsa = deepAccess(fpd, 'regs.ext.dsa'); - const SEGTAX = {user: [4], site: [1, 2, 5, 6, 7]}; - const MAP = {user: 'tg_v.', site: 'tg_i.', adserver: 'tg_i.dfp_ad_unit_code', pbadslot: 'tg_i.pbadslot', keywords: 'kw'}; + const SEGTAX = { user: [4], site: [1, 2, 5, 6, 7] }; + const MAP = { user: 'tg_v.', site: 'tg_i.', adserver: 'tg_i.dfp_ad_unit_code', pbadslot: 'tg_i.pbadslot', keywords: 'kw' }; const validate = function(prop, key, parentName) { if (key === 'data' && Array.isArray(prop)) { return prop.filter(name => name.segment && deepAccess(name, 'ext.segtax') && SEGTAX[parentName] && @@ -1196,18 +1137,18 @@ export function determineRubiconVideoSizeId(bid) { export function getPriceGranularity(config) { return { ranges: { - low: [{max: 5.00, increment: 0.50}], - medium: [{max: 20.00, increment: 0.10}], - high: [{max: 20.00, increment: 0.01}], + low: [{ max: 5.00, increment: 0.50 }], + medium: [{ max: 20.00, increment: 0.10 }], + high: [{ max: 20.00, increment: 0.01 }], auto: [ - {max: 5.00, increment: 0.05}, - {min: 5.00, max: 10.00, increment: 0.10}, - {min: 10.00, max: 20.00, increment: 0.50} + { max: 5.00, increment: 0.05 }, + { min: 5.00, max: 10.00, increment: 0.10 }, + { min: 10.00, max: 20.00, increment: 0.50 } ], dense: [ - {max: 3.00, increment: 0.01}, - {min: 3.00, max: 8.00, increment: 0.05}, - {min: 8.00, max: 20.00, increment: 0.50} + { max: 3.00, increment: 0.01 }, + { min: 3.00, max: 8.00, increment: 0.05 }, + { min: 8.00, max: 20.00, increment: 0.50 } ], custom: config.getConfig('customPriceBucket') && config.getConfig('customPriceBucket').buckets }[config.getConfig('priceGranularity')] @@ -1305,8 +1246,8 @@ function addOrtbFirstPartyData(data, nonBannerRequests, ortb2) { const keywords = getAllOrtbKeywords(ortb2, ...nonBannerRequests.map(req => req.params.keywords)) nonBannerRequests.forEach(bidRequest => { const bidFirstPartyData = { - user: {ext: {data: {...bidRequest.params.visitor}}}, - site: {ext: {data: {...bidRequest.params.inventory}}} + user: { ext: { data: { ...bidRequest.params.visitor } } }, + site: { ext: { data: { ...bidRequest.params.inventory } } } }; // add site.content.language diff --git a/modules/rules/index.ts b/modules/rules/index.ts new file mode 100644 index 00000000000..d9dccd6a86b --- /dev/null +++ b/modules/rules/index.ts @@ -0,0 +1,506 @@ +import { setLabels } from "../../libraries/analyticsAdapter/AnalyticsAdapter.ts"; +import { timeoutQueue } from "../../libraries/timeoutQueue/timeoutQueue.ts"; +import { ACTIVITY_ADD_BID_RESPONSE, ACTIVITY_FETCH_BIDS } from "../../src/activities/activities.js"; +import { MODULE_TYPE_BIDDER } from "../../src/activities/modules.ts"; +import { ACTIVITY_PARAM_COMPONENT_NAME, ACTIVITY_PARAM_COMPONENT_TYPE } from "../../src/activities/params.js"; +import { registerActivityControl } from "../../src/activities/rules.js"; +import { ajax } from "../../src/ajax.ts"; +import { AuctionIndex } from "../../src/auctionIndex.js"; +import { auctionManager } from "../../src/auctionManager.js"; +import { config } from "../../src/config.ts"; +import { getHook } from "../../src/hook.ts"; +import { generateUUID, logInfo, logWarn } from "../../src/utils.ts"; +import { timedAuctionHook } from "../../src/utils/perfMetrics.ts"; + +/** + * Configuration interface for the shaping rules module. + */ +interface ShapingRulesConfig { + /** + * Endpoint configuration for fetching rules from a remote server. + * If not provided, rules must be provided statically via the `rules` property. + */ + endpoint?: { + /** URL endpoint to fetch rules configuration from */ + url: string; + /** HTTP method to use for fetching rules (currently only 'GET' is supported) */ + method: string; + }; + /** + * Static rules configuration object. + * If provided, rules will be used directly without fetching from endpoint. + * Takes precedence over endpoint configuration. + */ + rules?: RulesConfig; + /** + * Delay in milliseconds to wait for rules to be fetched before starting the auction. + * If rules are not loaded within this delay, the auction will proceed anyway. + * Default: 0 (no delay) + */ + auctionDelay?: number; + /** + * Custom schema evaluator functions to extend the default set of evaluators. + * Keys are function names, values are evaluator functions that take args and context, + * and return a function that evaluates to a value when called. + */ + extraSchemaEvaluators?: { + [key: string]: (args: any[], context: any) => () => any; + }; +} + +/** + * Schema function definition used to compute values. + */ +interface ModelGroupSchema { + /** Function name inside the schema */ + function: string; + /** Arguments for the schema function */ + args?: any[]; +} + +/** + * Model group configuration for A/B testing with different rule configurations. + * Only one object within the group is chosen based on weight. + */ +interface ModelGroup { + /** Determines selection probability; only one object within the group is chosen */ + weight: number; + /** Indicates whether this model group is selected (set automatically based on weight) */ + selected?: boolean; + /** Optional key used to produce aTags, identifying experiments or optimization targets */ + analyticsKey: string; + /** Version identifier for analytics */ + version: string; + /** + * Optional array of functions used to compute values. + * Without it, only the default rule is applied. + */ + schema: ModelGroupSchema[]; + /** + * Optional rule array; if absent, only the default rule is used. + * Each rule has conditions that must be met and results that are triggered. + */ + rules: [{ + /** Conditions that must be met for the rule to apply */ + conditions: string[]; + /** Resulting actions triggered when conditions are met */ + results: [ + { + /** Function defining the result action */ + function: string; + /** Arguments for the result function */ + args: any[]; + } + ]; + }]; + /** + * Default results object used if errors occur or when no schema or rules are defined. + * Exists outside the rules array for structural clarity. + */ + default?: Array<{ + /** Function defining the default result action */ + function: string; + /** Arguments for the default result function */ + args: any; + }>; +} + +/** + * Independent set of rules that can be applied to a specific stage of the auction. + */ +interface RuleSet { + /** Human-readable name of the ruleset */ + name: string; + /** + * Indicates which module stage the ruleset applies to. + * Can be either `processed-auction-request` or `processed-auction` + */ + stage: string; + /** Version identifier for the ruleset */ + version: string; + /** + * Optional timestamp of the last update (ISO 8601 format: `YYYY-MM-DDThh:mm:ss[.sss][Z or ±hh:mm]`) + */ + timestamp?: string; + /** + * One or more model groups for A/B testing with different rule configurations. + * Allows A/B testing with different rule configurations. + */ + modelGroups: ModelGroup[]; +} + +/** + * Main configuration object for the shaping rules module. + */ +interface RulesConfig { + /** Version identifier for the rules configuration */ + version: string; + /** One or more independent sets of rules */ + ruleSets: RuleSet[]; + /** Optional timestamp of the last update (ISO 8601 format: `YYYY-MM-DDThh:mm:ss[.sss][Z or ±hh:mm]`) */ + timestamp?: string; + /** Enables or disables the module. Default: `true` */ + enabled: boolean; +} + +declare module '../../src/config' { + interface Config { + shapingRules?: ShapingRulesConfig; + } +} + +const MODULE_NAME = 'shapingRules'; + +const globalRandomStore = new WeakMap<{ auctionId: string }, number>(); + +let auctionConfigStore = new Map(); + +export const dep = { + getGlobalRandom: getGlobalRandom +}; + +function getGlobalRandom(auctionId: string, auctionIndex: AuctionIndex = auctionManager.index) { + if (!auctionId) { + return Math.random(); + } + const auction = auctionIndex.getAuction({ auctionId }); + if (!globalRandomStore.has(auction)) { + globalRandomStore.set(auction, Math.random()); + } + return globalRandomStore.get(auction); +} + +const unregisterFunctions: Array<() => void> = [] + +let moduleConfig: ShapingRulesConfig = { + endpoint: { + method: 'GET', + url: '' + }, + auctionDelay: 0, + extraSchemaEvaluators: {} +}; + +let fetching = false; + +let rulesLoaded = false; + +const delayedAuctions = timeoutQueue(); + +let rulesConfig: RulesConfig = null; + +export function evaluateConfig(config: RulesConfig, auctionId: string) { + if (!config || !config.ruleSets) { + logWarn(`${MODULE_NAME}: Invalid structure for rules engine`); + return; + } + + if (!config.enabled) { + logInfo(`${MODULE_NAME}: Rules engine is disabled in the configuration.`); + return; + } + + const stageRules = config.ruleSets; + + const modelGroupsWithStage = getAssignedModelGroups(stageRules || []); + + for (const { modelGroups, stage } of modelGroupsWithStage) { + const modelGroup = modelGroups.find(group => group.selected); + if (!modelGroup) continue; + evaluateRules(modelGroup.rules || [], modelGroup.schema || [], stage, modelGroup.analyticsKey, auctionId, modelGroup.default); + } +} + +export function getAssignedModelGroups(rulesets: RuleSet[]): Array<{ modelGroups: ModelGroup[], stage: string }> { + return rulesets.flatMap(ruleset => { + const { modelGroups, stage } = ruleset; + if (!modelGroups?.length) { + return []; + } + + // Calculate cumulative weights for proper weighted random selection + let cumulativeWeight = 0; + const groupsWithCumulativeWeights = modelGroups.map(group => { + const groupWeight = group.weight ?? 100; + cumulativeWeight += groupWeight; + return { + group, + cumulativeWeight + }; + }); + + const weightSum = cumulativeWeight; + // Generate random value in range [0, weightSum) + // This ensures each group gets probability proportional to its weight + const randomValue = Math.random() * weightSum; + + // Find first group where cumulative weight >= randomValue + let selectedIndex = groupsWithCumulativeWeights.findIndex(({ cumulativeWeight }) => randomValue < cumulativeWeight); + + // Fallback: if no group was selected (shouldn't happen, but safety check) + if (selectedIndex === -1) { + selectedIndex = modelGroups.length - 1; + } + + // Create new model groups array with selected flag + const newModelGroups = modelGroups.map((group, index) => ({ + ...group, + selected: index === selectedIndex + })); + + return { + modelGroups: newModelGroups, + stage + }; + }); +} + +function evaluateRules(rules, schema, stage, analyticsKey, auctionId: string, defaultResults?) { + const modelGroupConfig = auctionConfigStore.get(auctionId) || []; + modelGroupConfig.push({ + rules, + schema, + stage, + analyticsKey, + defaultResults, + }); + auctionConfigStore.set(auctionId, modelGroupConfig); +} + +const schemaEvaluators = { + percent: (args, context) => () => { + const auctionId = context.auctiondId || context.bid?.auctionId; + return dep.getGlobalRandom(auctionId) * 100 < args[0] + }, + adUnitCode: (args, context) => () => context.adUnit.code, + adUnitCodeIn: (args, context) => () => args[0].includes(context.adUnit.code), + deviceCountry: (args, context) => () => context.ortb2?.device?.geo?.country, + deviceCountryIn: (args, context) => () => args[0].includes(context.ortb2?.device?.geo?.country), + channel: (args, context) => () => 'web', + eidAvailable: (args, context) => () => { + const eids = context.ortb2?.user?.eids || []; + return eids.length > 0; + }, + userFpdAvailable: (args, context) => () => { + const fpd = context.ortb2?.user?.data || {}; + const extFpd = context.ortb2?.user?.ext?.data || {}; + const mergedFpd = { ...fpd, ...extFpd }; + return Object.keys(mergedFpd).length > 0; + }, + fpdAvailable: (args, context) => () => { + const extData = context.ortb2?.user?.ext?.data || {}; + const usrData = context.ortb2?.user?.data || {}; + const siteExtData = context.ortb2?.site?.ext?.data || {}; + const siteContentData = context.ortb2?.site?.content?.data || {}; + const appExtData = context.ortb2?.app?.ext?.data || {}; + const appContentData = context.ortb2?.app?.content?.data || {}; + const mergedFpd = { ...extData, ...usrData, ...siteExtData, ...siteContentData, ...appExtData, ...appContentData }; + return Object.keys(mergedFpd).length > 0; + }, + gppSidIn: (args, context) => () => { + const gppSids = context.ortb2?.regs?.gpp_sid || []; + return args[0].some((sid) => gppSids.includes(sid)); + }, + tcfInScope: (args, context) => () => context.ortb2?.regs?.ext?.gdpr === 1, + domain: (args, context) => () => { + const domain = context.ortb2?.site?.domain || context.ortb2?.app?.domain || ''; + return domain; + }, + domainIn: (args, context) => () => { + const domain = context.ortb2?.site?.domain || context.ortb2?.app?.domain || ''; + return args[0].includes(domain); + }, + bundle: (args, context) => () => { + const bundle = context.ortb2?.app?.bundle || ''; + return bundle; + }, + bundleIn: (args, context) => () => { + const bundle = context.ortb2?.app?.bundle || ''; + return args[0].includes(bundle); + }, + mediaTypeIn: (args, context) => () => { + const mediaTypes = Object.keys(context.adUnit?.mediaTypes) || []; + return args[0].some((type) => mediaTypes.includes(type)); + }, + deviceTypeIn: (args, context) => () => { + const deviceType = context.ortb2?.device?.devicetype; + return args[0].includes(deviceType); + }, + bidPrice: (args, context) => () => { + const [operator, currency, value] = args || []; + const { cpm: bidPrice, currency: bidCurrency } = context.bid || {}; + if (bidCurrency !== currency) { + return false; + } + if (operator === 'gt') { + return bidPrice > value; + } else if (operator === 'gte') { + return bidPrice >= value; + } else if (operator === 'lt') { + return bidPrice < value; + } else if (operator === 'lte') { + return bidPrice <= value; + } + return false; + } +}; + +export function evaluateSchema(func, args, context) { + const extraEvaluators = moduleConfig.extraSchemaEvaluators || {}; + const evaluators = { ...schemaEvaluators, ...extraEvaluators }; + const evaluator = evaluators[func]; + if (evaluator) { + return evaluator(args, context); + } + return () => null; +} + +function evaluateCondition(condition, func) { + switch (condition) { + case '*': + return true + case 'true': + return func() === true; + case 'false': + return func() === false; + default: + return func() === condition; + } +} + +export function fetchRules(endpoint = moduleConfig.endpoint) { + if (fetching) { + logWarn(`${MODULE_NAME}: A fetch is already occurring. Skipping.`); + return; + } + + if (!endpoint?.url || endpoint?.method !== 'GET') return; + + fetching = true; + ajax(endpoint.url, { + success: (response: any) => { + fetching = false; + rulesLoaded = true; + rulesConfig = JSON.parse(response); + delayedAuctions.resume(); + logInfo(`${MODULE_NAME}: Rules configuration fetched successfully.`); + }, + error: () => { + fetching = false; + } + }, null, { method: 'GET' }); +} + +export function registerActivities() { + const stages = { + [ACTIVITY_FETCH_BIDS]: 'processed-auction-request', + [ACTIVITY_ADD_BID_RESPONSE]: 'processed-auction', + }; + + [ACTIVITY_FETCH_BIDS, ACTIVITY_ADD_BID_RESPONSE].forEach(activity => { + unregisterFunctions.push( + registerActivityControl(activity, MODULE_NAME, (params) => { + const auctionId = params.auctionId || params.bid?.auctionId; + if (params[ACTIVITY_PARAM_COMPONENT_TYPE] !== MODULE_TYPE_BIDDER) return; + if (!auctionId) return; + + const checkConditions = ({ schema, conditions, stage }) => { + for (const [index, schemaEntry] of schema.entries()) { + const schemaFunction = evaluateSchema(schemaEntry.function, schemaEntry.args || [], params); + if (evaluateCondition(conditions[index], schemaFunction)) { + return true; + } + } + return false; + } + + const results = []; + let modelGroups = auctionConfigStore.get(auctionId) || []; + modelGroups = modelGroups.filter(modelGroup => modelGroup.stage === stages[activity]); + + // evaluate applicable results for each model group + for (const modelGroup of modelGroups) { + // find first rule that matches conditions + const selectedRule = modelGroup.rules.find(rule => checkConditions({ ...rule, schema: modelGroup.schema })); + if (selectedRule) { + results.push(...selectedRule.results); + } else if (Array.isArray(modelGroup.defaultResults)) { + const defaults = modelGroup.defaultResults.map(result => ({ ...result, analyticsKey: modelGroup.analyticsKey })); + results.push(...defaults); + } + } + + // set analytics labels for logAtag results + results + .filter(result => result.function === 'logAtag') + .forEach((result) => { + setLabels({ [auctionId + '-' + result.analyticsKey]: result.args.analyticsValue }); + }); + + // verify current bidder against applicable rules + const allow = results + .filter(result => ['excludeBidders', 'includeBidders'].includes(result.function)) + .every((result) => { + return result.args.every(({ bidders }) => { + const bidderIncluded = bidders.includes(params[ACTIVITY_PARAM_COMPONENT_NAME]); + return result.function === 'excludeBidders' ? !bidderIncluded : bidderIncluded; + }); + }); + + if (!allow) { + return { allow, reason: `Bidder ${params.bid?.bidder} excluded by rules module` }; + } + }) + ); + }); +} + +export const startAuctionHook = timedAuctionHook('rules', function startAuctionHook(fn, req) { + req.auctionId = req.auctionId || generateUUID(); + evaluateConfig(rulesConfig, req.auctionId); + fn.call(this, req); +}); + +export const requestBidsHook = timedAuctionHook('rules', function requestBidsHook(fn, reqBidsConfigObj) { + const { auctionDelay = 0 } = moduleConfig; + const continueAuction = ((that) => () => fn.call(that, reqBidsConfigObj))(this); + + if (!rulesLoaded && auctionDelay > 0) { + delayedAuctions.submit(auctionDelay, continueAuction, () => { + logWarn(`${MODULE_NAME}: Fetch attempt did not return in time for auction ${reqBidsConfigObj.auctionId}`) + continueAuction(); + }); + } else { + continueAuction(); + } +}); + +function init(config: ShapingRulesConfig) { + moduleConfig = config; + registerActivities(); + auctionManager.onExpiry(auction => { + auctionConfigStore.delete(auction.getAuctionId()); + }); + // use static config if provided + if (config.rules) { + rulesConfig = config.rules; + } else { + fetchRules(); + } + getHook('requestBids').before(requestBidsHook, 50); + getHook('startAuction').before(startAuctionHook, 50); +} + +export function reset() { + try { + getHook('requestBids').getHooks({ hook: requestBidsHook }).remove(); + getHook('startAuction').getHooks({ hook: startAuctionHook }).remove(); + unregisterFunctions.forEach(unregister => unregister()); + unregisterFunctions.length = 0; + auctionConfigStore.clear(); + } catch (e) { + } + setLabels({}); +} + +config.getConfig(MODULE_NAME, config => init(config[MODULE_NAME])); diff --git a/modules/rumbleBidAdapter.js b/modules/rumbleBidAdapter.js index b4be549d394..0f5317d5511 100644 --- a/modules/rumbleBidAdapter.js +++ b/modules/rumbleBidAdapter.js @@ -106,13 +106,13 @@ export const spec = { return { url: endpoint, method: 'POST', - data: converter.toORTB({bidRequests: [bid], bidderRequest}), + data: converter.toORTB({ bidRequests: [bid], bidderRequest }), bidRequest: bid, }; }) }, interpretResponse(response, request) { - return converter.fromORTB({response: response.body, request: request.data}).bids; + return converter.fromORTB({ response: response.body, request: request.data }).bids; }, onBidWon: function(bid) { if (bid.burl) { diff --git a/modules/s2sTesting.js b/modules/s2sTesting.js index 18079118ffa..8f7b2b70dd7 100644 --- a/modules/s2sTesting.js +++ b/modules/s2sTesting.js @@ -1,7 +1,7 @@ -import {PARTITIONS, partitionBidders, filterBidsForAdUnit, getS2SBidderSet} from '../src/adapterManager.js'; -import {getBidderCodes, logWarn} from '../src/utils.js'; +import { PARTITIONS, partitionBidders, filterBidsForAdUnit, getS2SBidderSet } from '../src/adapterManager.js'; +import { getBidderCodes, logWarn } from '../src/utils.js'; -const {CLIENT, SERVER} = PARTITIONS; +const { CLIENT, SERVER } = PARTITIONS; export const s2sTesting = { ...PARTITIONS, clientTestBidders: new Set() @@ -11,7 +11,7 @@ s2sTesting.bidSource = {}; // store bidder sources determined from s2sConfig bid s2sTesting.globalRand = Math.random(); // if 10% of bidderA and 10% of bidderB should be server-side, make it the same 10% s2sTesting.getSourceBidderMap = function(adUnits = [], allS2SBidders = []) { - var sourceBidders = {[SERVER]: {}, [CLIENT]: {}}; + var sourceBidders = { [SERVER]: {}, [CLIENT]: {} }; adUnits.forEach((adUnit) => { // if any adUnit bidders specify a bidSource, include them @@ -117,7 +117,7 @@ partitionBidders.before(function (next, adUnits, s2sConfigs) { memo[CLIENT].push(bidder); } return memo; - }, {[CLIENT]: [], [SERVER]: []})); + }, { [CLIENT]: [], [SERVER]: [] })); }); filterBidsForAdUnit.before(function(next, bids, s2sConfig) { diff --git a/modules/scaleableAnalyticsAdapter.js b/modules/scaleableAnalyticsAdapter.js index cb2fc34737a..dda050f660f 100644 --- a/modules/scaleableAnalyticsAdapter.js +++ b/modules/scaleableAnalyticsAdapter.js @@ -74,7 +74,7 @@ const sendDataToServer = data => ajax(URL, () => {}, JSON.stringify(data)); // Track auction initiated const onAuctionInit = args => { - const config = scaleableAnalytics.config || {options: {}}; + const config = scaleableAnalytics.config || { options: {} }; const adunitObj = {}; const adunits = []; @@ -114,7 +114,7 @@ const onAuctionInit = args => { // Handle all events besides requests and wins const onAuctionEnd = args => { - const config = scaleableAnalytics.config || {options: {}}; + const config = scaleableAnalytics.config || { options: {} }; const adunitObj = {}; const adunits = []; @@ -167,7 +167,7 @@ const onAuctionEnd = args => { // Bid Win Events occur after auction end const onBidWon = args => { - const config = scaleableAnalytics.config || {options: {}}; + const config = scaleableAnalytics.config || { options: {} }; const data = { event: 'win', diff --git a/modules/scaliburBidAdapter.js b/modules/scaliburBidAdapter.js index b8b05c3ac61..71613297001 100644 --- a/modules/scaliburBidAdapter.js +++ b/modules/scaliburBidAdapter.js @@ -1,8 +1,9 @@ -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {config} from '../src/config.js'; -import {BANNER, VIDEO} from '../src/mediaTypes.js'; -import {getStorageManager} from '../src/storageManager.js'; -import {sizesToSizeTuples} from "../src/utils.js"; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { config } from '../src/config.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; +import { getStorageManager } from '../src/storageManager.js'; +import { sizesToSizeTuples } from "../src/utils.js"; +import { getDNT } from '../libraries/dnt/index.js'; const BIDDER_CODE = 'scalibur'; const ENDPOINT_SERVER = new URLSearchParams(window.location.search).get('sclServer') || 'srv'; @@ -14,7 +15,7 @@ const BIDDER_VERSION = '1.0.0'; const IFRAME_TYPE_Q_PARAM = 'iframe'; const IMAGE_TYPE_Q_PARAM = 'img'; const GVLID = 1471; -const storage = getStorageManager({bidderCode: BIDDER_CODE}); +const storage = getStorageManager({ bidderCode: BIDDER_CODE }); const STORAGE_KEY = `${BIDDER_CODE}_fp_data`; export const spec = { @@ -92,7 +93,7 @@ export const spec = { } // Floor Price - const floor = bid.getFloor ? bid.getFloor({currency: DEFAULT_CURRENCY, mediaType: '*', size: '*'}) : {}; + const floor = bid.getFloor ? bid.getFloor({ currency: DEFAULT_CURRENCY, mediaType: '*', size: '*' }) : {}; imp.bidfloor = floor.floor || bid.params.bidfloor || 0; imp.bidfloorcur = floor.currency || bid.params.bidfloorcur || DEFAULT_CURRENCY; @@ -115,7 +116,7 @@ export const spec = { ua: ortb2Device.ua, language: ortb2Device.language, sua: ortb2Device.sua || {}, - dnt: ortb2Device.dnt ?? 0, + dnt: getDNT() ? 1 : 0, }, user: { eids, @@ -212,10 +213,10 @@ export const spec = { const syncs = []; if (syncOptions.iframeEnabled) { - syncs.push({type: 'iframe', url: `${SYNC_IFRAME_URL}?${queryParams}`}); + syncs.push({ type: 'iframe', url: `${SYNC_IFRAME_URL}?${queryParams}` }); } if (syncOptions.pixelEnabled) { - syncs.push({type: 'image', url: `${SYNC_PIXEL_URL}?${queryParams}`}); + syncs.push({ type: 'image', url: `${SYNC_PIXEL_URL}?${queryParams}` }); } return syncs; }, diff --git a/modules/schain.ts b/modules/schain.ts index 6b864e4d70a..941115b9e23 100644 --- a/modules/schain.ts +++ b/modules/schain.ts @@ -1,7 +1,7 @@ -import {config} from '../src/config.js'; -import {deepClone, logWarn} from '../src/utils.js'; -import {normalizeFPD} from '../src/fpd/normalize.js'; -import type {ORTBRequest} from "../src/types/ortb/request"; +import { config } from '../src/config.js'; +import { deepClone, logWarn } from '../src/utils.js'; +import { normalizeFPD } from '../src/fpd/normalize.js'; +import type { ORTBRequest } from "../src/types/ortb/request"; export type SchainConfig = { config: ORTBRequest['source']['schain']; diff --git a/modules/scope3RtdProvider.md b/modules/scope3RtdProvider.md index 20dff06c967..f6f63942be3 100644 --- a/modules/scope3RtdProvider.md +++ b/modules/scope3RtdProvider.md @@ -7,6 +7,7 @@ The Scope3 RTD module enables real-time agentic execution for programmatic adver ### What It Does This module: + 1. Captures the **complete OpenRTB request** including all user IDs, geo data, device info, and site context 2. Sends it to Scope3's AEE for real-time analysis 3. Receives back targeting instructions: which line items to include/exclude this impression from @@ -30,34 +31,39 @@ The AEE returns opaque codes (e.g., "x82s") that instruct GAM which line items s pbjs.setConfig({ realTimeData: { auctionDelay: 1000, - dataProviders: [{ - name: 'scope3', - params: { - orgId: 'YOUR_ORG_ID', // Required - your Scope3 organization identifier - - // Optional - customize targeting keys (defaults shown) - includeKey: 's3i', // Key for include segments - excludeKey: 's3x', // Key for exclude segments - macroKey: 's3m', // Key for macro blob - - // Optional - other settings - endpoint: 'https://prebid.scope3.com/prebid', // API endpoint (default) - timeout: 1000, // Milliseconds (default: 1000) - publisherTargeting: true, // Set GAM targeting keys (default: true) - advertiserTargeting: true, // Enrich bid requests (default: true) - bidders: [], // Specific bidders to get data for (empty = all bidders in auction) - cacheEnabled: true, // Enable response caching (default: true) - debugMode: false // Enable debug logging (default: false) - } - }] - } + dataProviders: [ + { + name: "scope3", + waitForIt: true, + params: { + orgId: "YOUR_ORG_ID", // Required - your Scope3 organization identifier + + // Optional - customize targeting keys (defaults shown) + includeKey: "axei", // Key for include segments + excludeKey: "axex", // Key for exclude segments + macroKey: "axem", // Key for macro blob + + // Optional - other settings + endpoint: "https://prebid.scope3.com/prebid", // API endpoint (default) + timeout: 1000, // Milliseconds (default: 1000) + publisherTargeting: true, // Set GAM targeting keys (default: true) + advertiserTargeting: true, // Enrich bid requests (default: true) + bidders: [], // Specific bidders to get data for (empty = all bidders in auction) + cacheEnabled: true, // Enable response caching (default: true) + debugMode: false, // Enable debug logging (default: false) + }, + }, + ], + }, }); ``` ### Advanced Configuration Examples #### Custom Targeting Keys + Use your own naming convention for targeting keys: + ```javascript params: { orgId: 'YOUR_ORG_ID', @@ -68,7 +74,9 @@ params: { ``` #### Specific Bidders Only + Apply AEE signals only to certain bidders: + ```javascript params: { orgId: 'YOUR_ORG_ID', @@ -79,6 +87,7 @@ params: { ``` #### Development/Testing + ```javascript params: { orgId: 'YOUR_ORG_ID', @@ -91,7 +100,9 @@ params: { ## Data Flow ### 1. Complete OpenRTB Capture + The module captures ALL available OpenRTB data: + - **Site**: page URL, domain, referrer, keywords, content, categories - **Device**: user agent, geo location, IP, device type, screen size - **User**: ID, buyer UIDs, year of birth, gender, keywords, data segments, **all extended IDs (eids)** @@ -100,18 +111,20 @@ The module captures ALL available OpenRTB data: - **App**: if in-app, all app details ### 2. Request to AEE + Sends the complete OpenRTB request with list of bidders: + ```json { "orgId": "YOUR_ORG_ID", "ortb2": { - "site": { + "site": { "page": "https://example.com/page", "domain": "example.com", "cat": ["IAB1-1"], "keywords": "news,sports" }, - "device": { + "device": { "ua": "Mozilla/5.0...", "geo": { "country": "USA", @@ -128,7 +141,7 @@ Sends the complete OpenRTB request with list of bidders: "uids": [{"id": "XY123456"}] }, { - "source": "id5-sync.com", + "source": "id5-sync.com", "uids": [{"id": "ID5*abc"}] } ], @@ -144,7 +157,9 @@ Sends the complete OpenRTB request with list of bidders: ``` ### 3. AEE Response + Receives targeting instructions with opaque codes (e.g., 'x82s', 'a91k') that tell GAM which line items to include/exclude. These are NOT audience segments or IAB taxonomy: + ```json { "aee_signals": { @@ -172,13 +187,17 @@ Receives targeting instructions with opaque codes (e.g., 'x82s', 'a91k') that te ### 4. Signal Application #### Publisher Targeting (GAM) + Sets the configured targeting keys (GAM automatically converts to lowercase): -- `s3i` (or your includeKey): ["x82s", "a91k", "p2m7"] - line items to include -- `s3x` (or your excludeKey): ["c4x9", "f7r2"] - line items to exclude -- `s3m` (or your macroKey): "ctx9h3v8s5" - opaque context data + +- `axei` (or your includeKey): ["x82s", "a91k", "p2m7"] - line items to include +- `axex` (or your excludeKey): ["c4x9", "f7r2"] - line items to exclude +- `axem` (or your macroKey): "ctx9h3v8s5" - opaque context data #### Advertiser Data (OpenRTB) + Enriches bid requests with AEE signals: + ```javascript ortb2: { site: { @@ -203,20 +222,22 @@ Create line items that respond to agent targeting instructions. The codes (e.g., ``` Include impression in this line item: -s3i contains "x82s" +axei contains "x82s" Exclude impression from this line item: -s3x does not contain "f7r2" +axex does not contain "f7r2" Multiple targeting conditions: -s3i contains "a91k" AND s3x does not contain "c4x9" +axei contains "a91k" AND axex does not contain "c4x9" Macro data for creative: -s3m is present +axem is present ``` ### Custom Key Configuration + If you use custom keys: + ```javascript // Configuration params: { @@ -239,14 +260,14 @@ Bidders can access AEE signals in their adapters: ```javascript buildRequests: function(validBidRequests, bidderRequest) { const aeeSignals = bidderRequest.ortb2?.site?.ext?.data?.scope3_aee; - + if (aeeSignals) { // Use include segments for targeting payload.targeting_segments = aeeSignals.include; - + // Respect exclude segments payload.exclude_segments = aeeSignals.exclude; - + // Include macro data as opaque string if (aeeSignals.macro) { payload.context_code = aeeSignals.macro; @@ -258,12 +279,15 @@ buildRequests: function(validBidRequests, bidderRequest) { ## Performance Considerations ### Caching + - Responses are cached for 30 seconds by default - Cache key includes: page, user agent, geo, user IDs, and ad units - Reduces redundant API calls for similar contexts ### Data Completeness + The module sends ALL available OpenRTB data to maximize AEE intelligence: + - Extended user IDs (LiveRamp, ID5, UID2, etc.) - Geo location data - Device characteristics @@ -272,6 +296,7 @@ The module sends ALL available OpenRTB data to maximize AEE intelligence: - Regulatory consent status ### Timeout Handling + - Default timeout: 1000ms - Auction continues if AEE doesn't respond in time - No blocking - graceful degradation @@ -287,6 +312,7 @@ The module sends ALL available OpenRTB data to maximize AEE intelligence: ## Troubleshooting ### Enable Debug Mode + ```javascript params: { orgId: 'YOUR_ORG_ID', @@ -297,12 +323,14 @@ params: { ### Common Issues 1. **No signals appearing** + - Verify orgId is correct - Check endpoint is accessible - Ensure timeout is sufficient - Look for console errors in debug mode 2. **Targeting keys not in GAM** + - Verify `publisherTargeting: true` - Check key names match GAM setup - Ensure AEE is returning signals @@ -319,17 +347,21 @@ Minimal setup with defaults: ```javascript pbjs.setConfig({ realTimeData: { - dataProviders: [{ - name: 'scope3', - params: { - orgId: 'YOUR_ORG_ID' // Only required parameter - } - }] - } + dataProviders: [ + { + name: "scope3", + waitForIt: true, + params: { + orgId: "YOUR_ORG_ID", // Only required parameter + }, + }, + ], + }, }); ``` This will: + - Send complete OpenRTB data to Scope3's AEE - Set targeting keys: `s3i` (include), `s3x` (exclude), `s3m` (macro) - Enrich all bidders with AEE signals @@ -338,6 +370,7 @@ This will: ## About the Agentic Execution Engine Scope3's AEE implements the [Ad Context Protocol](https://adcontextprotocol.org) to analyze the complete context of each bid opportunity. By processing the full OpenRTB request including all user IDs, geo data, and site context, the AEE can: + - Identify optimal audience segments in real-time - Detect and prevent unwanted targeting scenarios - Apply complex business rules at scale @@ -346,6 +379,7 @@ Scope3's AEE implements the [Ad Context Protocol](https://adcontextprotocol.org) ## Support For technical support and AEE configuration: + - Documentation: https://docs.scope3.com - Ad Context Protocol: https://adcontextprotocol.org -- Support: support@scope3.com \ No newline at end of file +- Support: support@scope3.com diff --git a/modules/scope3RtdProvider.ts b/modules/scope3RtdProvider.ts index 620ae36108c..798a638559b 100644 --- a/modules/scope3RtdProvider.ts +++ b/modules/scope3RtdProvider.ts @@ -112,9 +112,9 @@ function initModule(config: any): boolean { // Set defaults moduleConfig.endpoint = moduleConfig.endpoint || DEFAULT_ENDPOINT; moduleConfig.timeout = moduleConfig.timeout || DEFAULT_TIMEOUT; - moduleConfig.includeKey = moduleConfig.includeKey || 'scope3_include'; - moduleConfig.excludeKey = moduleConfig.excludeKey || 'scope3_exclude'; - moduleConfig.macroKey = moduleConfig.macroKey || 'scope3_macro'; + moduleConfig.includeKey = moduleConfig.includeKey || 'axei'; + moduleConfig.excludeKey = moduleConfig.excludeKey || 'axex'; + moduleConfig.macroKey = moduleConfig.macroKey || 'axem'; moduleConfig.publisherTargeting = moduleConfig.publisherTargeting !== false; moduleConfig.advertiserTargeting = moduleConfig.advertiserTargeting !== false; moduleConfig.cacheEnabled = moduleConfig.cacheEnabled !== false; diff --git a/modules/screencoreBidAdapter.js b/modules/screencoreBidAdapter.js index ccf59b28ce7..e7c38c66a3a 100644 --- a/modules/screencoreBidAdapter.js +++ b/modules/screencoreBidAdapter.js @@ -25,7 +25,8 @@ const REGION_SUBDOMAIN_SUFFIX = { */ function getRegionSubdomainSuffix() { try { - const region = getTimeZone().split('/')[0]; + const tz = getTimeZone(); + const region = tz.split('/')[0]; switch (region) { case 'Asia': @@ -40,6 +41,8 @@ function getRegionSubdomainSuffix() { case 'Arctic': return REGION_SUBDOMAIN_SUFFIX['EU']; case 'America': + case 'US': + case 'Canada': return REGION_SUBDOMAIN_SUFFIX['US']; default: return REGION_SUBDOMAIN_SUFFIX['EU']; @@ -55,11 +58,10 @@ export function createDomain() { return `https://${subDomain}.screencore.io`; } -const AD_URL = `${createDomain()}/prebid`; - const placementProcessingFunction = buildPlacementProcessingFunction(); const buildRequests = (validBidRequests = [], bidderRequest = {}) => { + const AD_URL = `${createDomain()}/pbjs`; return buildRequestsBase({ adUrl: AD_URL, validBidRequests, bidderRequest, placementProcessingFunction }); }; diff --git a/modules/seedingAllianceBidAdapter.js b/modules/seedingAllianceBidAdapter.js index b22641cfb39..6a738e07299 100755 --- a/modules/seedingAllianceBidAdapter.js +++ b/modules/seedingAllianceBidAdapter.js @@ -1,12 +1,12 @@ // jshint esversion: 6, es3: false, node: true 'use strict'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER, NATIVE} from '../src/mediaTypes.js'; -import {generateUUID, deepSetValue, isEmpty, replaceAuctionPrice} from '../src/utils.js'; -import {config} from '../src/config.js'; -import {getStorageManager} from '../src/storageManager.js'; -import {ortbConverter} from '../libraries/ortbConverter/converter.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, NATIVE } from '../src/mediaTypes.js'; +import { generateUUID, deepSetValue, isEmpty, replaceAuctionPrice } from '../src/utils.js'; +import { config } from '../src/config.js'; +import { getStorageManager } from '../src/storageManager.js'; +import { ortbConverter } from '../libraries/ortbConverter/converter.js'; const GVL_ID = 371; const BIDDER_CODE = 'seedingAlliance'; @@ -14,7 +14,7 @@ const DEFAULT_CUR = 'EUR'; const ENDPOINT_URL = 'https://b.nativendo.de/cds/rtb/bid?format=openrtb2.5&ssp=pb'; const NATIVENDO_KEY = 'nativendo_id'; -export const storage = getStorageManager({bidderCode: BIDDER_CODE}); +export const storage = getStorageManager({ bidderCode: BIDDER_CODE }); const converter = ortbConverter({ context: { @@ -52,7 +52,7 @@ export const spec = { }, buildRequests: (validBidRequests = [], bidderRequest) => { - const oRtbRequest = converter.toORTB({bidRequests: validBidRequests, bidderRequest}); + const oRtbRequest = converter.toORTB({ bidRequests: validBidRequests, bidderRequest }); const eids = getEids(validBidRequests[0]); // check for url in params and set in site object diff --git a/modules/seedtagBidAdapter.js b/modules/seedtagBidAdapter.js index f93ed814a0a..c38325ece0a 100644 --- a/modules/seedtagBidAdapter.js +++ b/modules/seedtagBidAdapter.js @@ -5,6 +5,7 @@ import { BANNER, VIDEO } from '../src/mediaTypes.js'; import { _map, getWinDimensions, isArray, triggerPixel } from '../src/utils.js'; import { getViewportCoordinates } from '../libraries/viewport/viewport.js'; import { getConnectionInfo } from '../libraries/connectionInfo/connectionUtils.js'; +import { getAdUnitElement } from '../src/utils/adUnits.js'; /** * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest @@ -88,8 +89,7 @@ function hasBannerMediaType(bid) { function hasMandatoryDisplayParams(bid) { const p = bid.params; return ( - !!p.publisherId && - !!p.adUnitId + !!p.publisherId ); } @@ -98,7 +98,6 @@ function hasMandatoryVideoParams(bid) { const isValid = !!bid.params.publisherId && - !!bid.params.adUnitId && hasVideoMediaType(bid) && !!videoParams.playerSize && isArray(videoParams.playerSize) && @@ -123,7 +122,7 @@ function buildBidRequest(validBidRequest) { supplyTypes: mediaTypes, adUnitId: params.adUnitId, adUnitCode: validBidRequest.adUnitCode, - geom: geom(validBidRequest.adUnitCode), + geom: geom(validBidRequest), placement: params.placement, requestCount: validBidRequest.bidderRequestsCount || 1, }; @@ -217,8 +216,8 @@ function ttfb() { return ttfb >= 0 && ttfb <= performance.now() ? ttfb : 0; } -function geom(adunitCode) { - const slot = document.getElementById(adunitCode); +function geom(bidRequest) { + const slot = getAdUnitElement(bidRequest); if (slot) { const { top, left, width, height } = getBoundingClientRect(slot); const viewport = { @@ -249,13 +248,14 @@ export function getTimeoutUrl(data) { const params = data[0].params[0]; const timeout = data[0].timeout; - queryParams = - '?publisherToken=' + - params.publisherId + - '&adUnitId=' + - params.adUnitId + - '&timeout=' + - timeout; + const qsParams = [ + 'publisherToken=' + params.publisherId, + 'timeout=' + timeout + ]; + if (params.adUnitId) { + qsParams.push('adUnitId=' + params.adUnitId); + } + queryParams = '?' + qsParams.join('&'); } return SEEDTAG_SSP_ONTIMEOUT_ENDPOINT + queryParams; } @@ -295,9 +295,12 @@ export const spec = { * @return ServerRequest Info describing the request to the server. */ buildRequests(validBidRequests, bidderRequest) { + const publisherId = validBidRequests[0].params.publisherId; + const integrationType = validBidRequests[0].params.integrationType || 'publisherToken'; + const payload = { url: bidderRequest.refererInfo.page, - publisherToken: validBidRequests[0].params.publisherId, + publisherToken: publisherId, cmp: !!bidderRequest.gdprConsent, timeout: bidderRequest.timeout, version: '$prebid.version$', @@ -306,7 +309,8 @@ export const spec = { ttfb: ttfb(), bidRequests: _map(validBidRequests, buildBidRequest), user: { topics: [], eids: [] }, - site: {} + site: {}, + integrationType: integrationType }; if (payload.cmp) { @@ -371,6 +375,10 @@ export const spec = { payload.site.pagecat = bidderRequest.ortb2.site.pagecat } + if (bidderRequest.ortb2) { + payload.ortb = bidderRequest.ortb2; + } + const payloadString = JSON.stringify(payload); return { diff --git a/modules/sevioBidAdapter.js b/modules/sevioBidAdapter.js index 66bfca681d9..bb3f5f8634a 100644 --- a/modules/sevioBidAdapter.js +++ b/modules/sevioBidAdapter.js @@ -1,9 +1,9 @@ import * as utils from "../src/utils.js"; -import { detectWalletsPresence} from "../libraries/cryptoUtils/wallets.js"; +import { detectWalletsPresence } from "../libraries/cryptoUtils/wallets.js"; import { registerBidder } from "../src/adapters/bidderFactory.js"; import { BANNER, NATIVE } from "../src/mediaTypes.js"; import { config } from "../src/config.js"; -import {getDomComplexity, getPageDescription, getPageTitle} from "../libraries/fpdUtils/pageInfo.js"; +import { getDomComplexity, getPageDescription, getPageTitle } from "../libraries/fpdUtils/pageInfo.js"; import * as converter from '../libraries/ortbConverter/converter.js'; const PREBID_VERSION = '$prebid.version$'; @@ -43,6 +43,39 @@ const normalizeKeywords = (input) => { return []; }; +function resolveDataType(asset) { + if (typeof asset?.data?.type === 'number') { + return asset.data.type; + } + + if (typeof asset?.id === 'number') { + return asset.id; + } + + return null; +} + +// Helper: resolve the "image type" for an asset +// Returns 1 (icon), 3 (image) or null if unknown +function resolveImageType(asset) { + if (!asset) return null; + + // 1) explicit image type in the img block (preferred) + if (typeof asset.img?.type === 'number') return asset.img.type; + + // 2) fallback to data.type (some bidders put the type here) + if (typeof asset.data?.type === 'number') return asset.data.type; + + // 3) last resort: map legacy asset.id values to image types + // (13 -> icon, 14 -> image) — keep this mapping isolated here + if (typeof asset.id === 'number') { + if (asset.id === 13) return 1; // icon + if (asset.id === 14) return 3; // image + } + + return null; +} + const parseNativeAd = function (bid) { try { const nativeAd = JSON.parse(bid.ad); @@ -54,7 +87,8 @@ const parseNativeAd = function (bid) { } if (asset.data) { const value = asset.data.value; - switch (asset.data.type) { + const type = resolveDataType(asset); + switch (type) { case 1: if (value) native.sponsored = value; break; case 2: if (value) native.desc = value; break; case 3: if (value) native.rating = value; break; @@ -71,13 +105,14 @@ const parseNativeAd = function (bid) { } } if (asset.img) { - const { url, w = 0, h = 0, type } = asset.img; + const { url, w = 0, h = 0 } = asset.img; + const imgType = resolveImageType(asset); - if (type === 1 && url) { + if (imgType === 1 && url) { native.icon = url; native.icon_width = w; native.icon_height = h; - } else if (type === 3 && url) { + } else if (imgType === 3 && url) { native.image = url; native.image_width = w; native.image_height = h; @@ -242,7 +277,7 @@ export const spec = { referenceId: bidRequest.params.referenceId, tagId: bidRequest.params.zone, type: detectAdType(bidRequest), - ...(isNative && { nativeRequest: { ver: "1.2", assets: processedAssets || {}} }) + ...(isNative && { nativeRequest: { ver: "1.2", assets: processedAssets || {} } }) }, ], keywords: { diff --git a/modules/sharedIdSystem.ts b/modules/sharedIdSystem.ts index 9405e6c05fb..48875b0fd4d 100644 --- a/modules/sharedIdSystem.ts +++ b/modules/sharedIdSystem.ts @@ -5,16 +5,16 @@ * @requires module:modules/userId */ -import {parseUrl, buildUrl, triggerPixel, logInfo, hasDeviceAccess, generateUUID} from '../src/utils.js'; -import {submodule} from '../src/hook.js'; -import {getStorageManager} from '../src/storageManager.js'; -import {VENDORLESS_GVLID} from '../src/consentHandler.js'; -import {MODULE_TYPE_UID} from '../src/activities/modules.js'; -import {domainOverrideToRootDomain} from '../libraries/domainOverrideToRootDomain/index.js'; +import { parseUrl, buildUrl, triggerPixel, logInfo, hasDeviceAccess, generateUUID } from '../src/utils.js'; +import { submodule } from '../src/hook.js'; +import { getStorageManager } from '../src/storageManager.js'; +import { VENDORLESS_GVLID } from '../src/consentHandler.js'; +import { MODULE_TYPE_UID } from '../src/activities/modules.js'; +import { domainOverrideToRootDomain } from '../libraries/domainOverrideToRootDomain/index.js'; -import type {IdProviderSpec} from "./userId/spec.ts"; +import type { IdProviderSpec } from "./userId/spec.ts"; -export const storage = getStorageManager({moduleType: MODULE_TYPE_UID, moduleName: 'sharedId'}); +export const storage = getStorageManager({ moduleType: MODULE_TYPE_UID, moduleName: 'sharedId' }); const COOKIE = 'cookie'; const LOCAL_STORAGE = 'html5'; const OPTOUT_NAME = '_pubcid_optout'; @@ -129,7 +129,7 @@ export const sharedIdSystemSubmodule: IdProviderSpec<'sharedId'> = { return undefined; } logInfo(' Decoded value PubCommonId ' + value); - const idObj = {'pubcid': value as string}; + const idObj = { 'pubcid': value as string }; return idObj; }, getId: function (config = {} as any, consentData, storedId) { @@ -141,7 +141,7 @@ export const sharedIdSystemSubmodule: IdProviderSpec<'sharedId'> = { logInfo('PubCommonId: IDs not provided for coppa requests, exiting PubCommonId'); return; } - const {params: {create = true, pixelUrl} = {}} = config; + const { params: { create = true, pixelUrl } = {} } = config; let newId = storedId; if (!newId) { try { @@ -155,7 +155,7 @@ export const sharedIdSystemSubmodule: IdProviderSpec<'sharedId'> = { if (!newId) newId = (create && hasDeviceAccess()) ? generateUUID() : undefined; } - return {id: newId, callback: getIdCallback(newId, pixelUrl)}; + return { id: newId, callback: getIdCallback(newId, pixelUrl) }; }, /** * performs action to extend an id. There are generally two ways to extend the expiration time @@ -173,20 +173,20 @@ export const sharedIdSystemSubmodule: IdProviderSpec<'sharedId'> = { extendId: function(config = {} as any, consentData, storedId) { if (hasOptedOut()) { logInfo('PubCommonId: Has opted-out'); - return {id: undefined}; + return { id: undefined }; } if (consentData?.coppa) { logInfo('PubCommonId: IDs not provided for coppa requests, exiting PubCommonId'); return; } - const {params: {extend = false, pixelUrl} = {}} = config; + const { params: { extend = false, pixelUrl } = {} } = config; if (extend) { if (pixelUrl) { const callback = queuePixelCallback(pixelUrl, storedId as string); - return {callback: callback}; + return { callback: callback }; } else { - return {id: storedId}; + return { id: storedId }; } } }, @@ -195,7 +195,7 @@ export const sharedIdSystemSubmodule: IdProviderSpec<'sharedId'> = { 'pubcid'(values, config) { const eid: any = { source: 'pubcid.org', - uids: values.map(id => ({id, atype: 1})) + uids: values.map(id => ({ id, atype: 1 })) } if (config?.params?.inserter != null) { eid.inserter = config.params.inserter; diff --git a/modules/sharethroughAnalyticsAdapter.js b/modules/sharethroughAnalyticsAdapter.js index 0857b600b49..68bd577945e 100644 --- a/modules/sharethroughAnalyticsAdapter.js +++ b/modules/sharethroughAnalyticsAdapter.js @@ -1,6 +1,6 @@ import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; -import {tryAppendQueryString} from '../libraries/urlUtils/urlUtils.js'; +import { tryAppendQueryString } from '../libraries/urlUtils/urlUtils.js'; const emptyUrl = ''; const analyticsType = 'endpoint'; diff --git a/modules/sharethroughBidAdapter.js b/modules/sharethroughBidAdapter.js index a11820b5897..406ed77fc2b 100644 --- a/modules/sharethroughBidAdapter.js +++ b/modules/sharethroughBidAdapter.js @@ -1,4 +1,3 @@ -import { getDNT } from '../libraries/dnt/index.js'; import { handleCookieSync, PID_STORAGE_NAME, prepareSplitImps } from '../libraries/equativUtils/equativUtils.js'; import { ortbConverter } from '../libraries/ortbConverter/converter.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; @@ -6,6 +5,7 @@ import { config } from '../src/config.js'; import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; import { getStorageManager } from '../src/storageManager.js'; import { deepAccess, generateUUID, inIframe, isPlainObject, logWarn, mergeDeep } from '../src/utils.js'; +import { getDNT } from '../libraries/dnt/index.js'; const VERSION = '4.3.0'; const BIDDER_CODE = 'sharethrough'; @@ -159,10 +159,6 @@ export const sharethroughAdapterSpec = { const nativeRequest = deepAccess(bidReq, 'mediaTypes.native'); const videoRequest = deepAccess(bidReq, 'mediaTypes.video'); - if (bidderRequest.paapi?.enabled && bidReq.mediaTypes.banner) { - mergeDeep(impression, { ext: { ae: 1 } }); // ae = auction environment; if this is 1, ad server knows we have a fledge auction - } - if (videoRequest) { // default playerSize, only change this if we know width and height are properly defined in the request let [w, h] = [640, 360]; @@ -292,8 +288,6 @@ export const sharethroughAdapterSpec = { return []; } - const fledgeAuctionEnabled = body.ext?.auctionConfigs; - const imp = req.data.imp[0]; const bidsFromExchange = body.seatbid[0].bid.map((bid) => { @@ -340,15 +334,7 @@ export const sharethroughAdapterSpec = { return response; }); - - if (fledgeAuctionEnabled && !isEqtvTest) { - return { - bids: bidsFromExchange, - paapi: body.ext?.auctionConfigs || {}, - }; - } else { - return bidsFromExchange; - } + return bidsFromExchange; }, getUserSyncs: (syncOptions, serverResponses, gdprConsent) => { diff --git a/modules/shinezBidAdapter.js b/modules/shinezBidAdapter.js index f88360c4c38..d7aa86f12f7 100644 --- a/modules/shinezBidAdapter.js +++ b/modules/shinezBidAdapter.js @@ -1,6 +1,6 @@ -import {logWarn} from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {makeBaseSpec} from '../libraries/riseUtils/index.js'; +import { logWarn } from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { makeBaseSpec } from '../libraries/riseUtils/index.js'; const BIDDER_CODE = 'shinez'; const BASE_URL = 'https://hb.sweetgum.io/'; diff --git a/modules/shinezRtbBidAdapter.js b/modules/shinezRtbBidAdapter.js index f2034bd1992..4003565bfd9 100644 --- a/modules/shinezRtbBidAdapter.js +++ b/modules/shinezRtbBidAdapter.js @@ -1,6 +1,6 @@ -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER, VIDEO} from '../src/mediaTypes.js'; -import {getStorageManager} from '../src/storageManager.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; +import { getStorageManager } from '../src/storageManager.js'; import { isBidRequestValid, createBuildRequestsFn, @@ -10,7 +10,7 @@ import { const DEFAULT_SUB_DOMAIN = 'exchange'; const BIDDER_CODE = 'shinezRtb'; const BIDDER_VERSION = '1.0.0'; -export const storage = getStorageManager({bidderCode: BIDDER_CODE}); +export const storage = getStorageManager({ bidderCode: BIDDER_CODE }); export function createDomain(subDomain = DEFAULT_SUB_DOMAIN) { return `https://${subDomain}.sweetgum.io`; diff --git a/modules/showheroes-bsBidAdapter.js b/modules/showheroes-bsBidAdapter.js index 71a017120f0..99b5846e9cf 100644 --- a/modules/showheroes-bsBidAdapter.js +++ b/modules/showheroes-bsBidAdapter.js @@ -96,7 +96,7 @@ export const spec = { return []; } - return converter.fromORTB({response: response.body, request: request.data}).bids; + return converter.fromORTB({ response: response.body, request: request.data }).bids; }, getUserSyncs: (syncOptions, serverResponses) => { const syncs = []; diff --git a/modules/silvermobBidAdapter.js b/modules/silvermobBidAdapter.js index 340dc9c70ac..0f283ecc213 100644 --- a/modules/silvermobBidAdapter.js +++ b/modules/silvermobBidAdapter.js @@ -1,8 +1,8 @@ // import { logMessage } from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; -import {ortbConverter} from '../libraries/ortbConverter/converter.js' +import { ortbConverter } from '../libraries/ortbConverter/converter.js' import { config } from '../src/config.js'; const BIDDER_CODE = 'silvermob'; diff --git a/modules/silverpushBidAdapter.js b/modules/silverpushBidAdapter.js index 46aed11c4ac..c579619c8db 100644 --- a/modules/silverpushBidAdapter.js +++ b/modules/silverpushBidAdapter.js @@ -42,7 +42,7 @@ export const spec = { interpretResponse, onBidWon, getRequest: function(endpoint) { - ajax(endpoint, null, undefined, {method: 'GET'}); + ajax(endpoint, null, undefined, { method: 'GET' }); }, getOS: function(ua) { if (ua.indexOf('Windows') !== -1) { return 'Windows'; } else if (ua.match(/(iPhone|iPod|iPad)/)) { return 'iOS'; } else if (ua.indexOf('Mac OS X') !== -1) { return 'macOS'; } else if (ua.match(/Android/)) { return 'Android'; } else if (ua.indexOf('Linux') !== -1) { return 'Linux'; } else { return 'Unknown'; } @@ -117,22 +117,7 @@ export const CONVERTER = ortbConverter({ }, response(buildResponse, bidResponses, ortbResponse, context) { const response = buildResponse(bidResponses, ortbResponse, context); - - let fledgeAuctionConfigs = utils.deepAccess(ortbResponse, 'ext.fledge_auction_configs'); - if (fledgeAuctionConfigs) { - fledgeAuctionConfigs = Object.entries(fledgeAuctionConfigs).map(([bidId, cfg]) => { - return Object.assign({ - bidId, - auctionSignals: {} - }, cfg); - }); - return { - bids: response.bids, - paapi: fledgeAuctionConfigs, - } - } else { - return response.bids - } + return response.bids } }); @@ -209,7 +194,7 @@ function buildBannerImp(bidRequest, imp) { utils.deepSetValue(imp, 'banner.h', bannerSizes[0][1]); } - return {...imp}; + return { ...imp }; } function createRequest(bidRequests, bidderRequest, mediaType) { @@ -245,7 +230,7 @@ function buildVideoOutstreamResponse(bidResponse, context) { bidResponse.renderer.render(bidResponse); } - return {...bidResponse}; + return { ...bidResponse }; } function getBidFloor(bid, bidderRequest) { diff --git a/modules/sizeMapping.js b/modules/sizeMapping.js index ea9e8e2466a..b796015dc90 100644 --- a/modules/sizeMapping.js +++ b/modules/sizeMapping.js @@ -1,8 +1,8 @@ -import {config} from '../src/config.js'; -import {deepAccess, deepClone, deepSetValue, getWindowTop, logInfo, logWarn} from '../src/utils.js'; +import { config } from '../src/config.js'; +import { deepAccess, deepClone, deepSetValue, getWindowTop, logInfo, logWarn } from '../src/utils.js'; -import {BANNER, VIDEO} from '../src/mediaTypes.js'; -import {setupAdUnitMediaTypes} from '../src/adapterManager.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; +import { setupAdUnitMediaTypes } from '../src/adapterManager.js'; let sizeConfig = []; @@ -36,9 +36,9 @@ config.getConfig('sizeConfig', config => setSizeConfig(config.sizeConfig)); */ export function getLabels(bidOrAdUnit, activeLabels) { if (bidOrAdUnit.labelAll) { - return {labelAll: true, labels: bidOrAdUnit.labelAll, activeLabels}; + return { labelAll: true, labels: bidOrAdUnit.labelAll, activeLabels }; } - return {labelAll: false, labels: bidOrAdUnit.labelAny, activeLabels}; + return { labelAll: false, labels: bidOrAdUnit.labelAny, activeLabels }; } /** @@ -76,12 +76,12 @@ if (FEATURES.VIDEO) { * @returns {Object} return.mediaTypes - The media types object. * @returns {Object} [return.filterResults] - The filter results before and after applying size filtering. */ -export function resolveStatus({labels = [], labelAll = false, activeLabels = []} = {}, mediaTypes, configs = sizeConfig) { +export function resolveStatus({ labels = [], labelAll = false, activeLabels = [] } = {}, mediaTypes, configs = sizeConfig) { const maps = evaluateSizeConfig(configs); let filtered = false; let hasSize = false; - const filterResults = {before: {}, after: {}}; + const filterResults = { before: {}, after: {} }; if (maps.shouldFilter) { Object.entries(SIZE_PROPS).forEach(([mediaType, sizeProp]) => { diff --git a/modules/sizeMappingV2.js b/modules/sizeMappingV2.js index c234c039ac2..70b848a1419 100644 --- a/modules/sizeMappingV2.js +++ b/modules/sizeMappingV2.js @@ -15,8 +15,8 @@ import { logWarn } from '../src/utils.js'; -import {getHook} from '../src/hook.js'; -import {adUnitSetupChecks} from '../src/prebid.js'; +import { getHook } from '../src/hook.js'; +import { adUnitSetupChecks } from '../src/prebid.js'; /** * @typedef {import('../src/adapters/bidderFactory.js').AdUnit} AdUnit diff --git a/modules/slimcutBidAdapter.js b/modules/slimcutBidAdapter.js index e62d7ac5871..0604e3c8eae 100644 --- a/modules/slimcutBidAdapter.js +++ b/modules/slimcutBidAdapter.js @@ -1,4 +1,4 @@ -import {getBidIdParameter, getValue, parseSizesInput} from '../src/utils.js'; +import { getBidIdParameter, getValue, parseSizesInput } from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; @@ -16,7 +16,7 @@ const BIDDER_CODE = 'slimcut'; const ENDPOINT_URL = 'https://sb.freeskreen.com/pbr'; export const spec = { code: BIDDER_CODE, - aliases: [{ code: 'scm'}], + aliases: [{ code: 'scm' }], supportedMediaTypes: ['video', 'banner'], /** * Determines whether or not the given bid request is valid. diff --git a/modules/smaatoBidAdapter.js b/modules/smaatoBidAdapter.js index 03199e99d52..99bb13ff3a7 100644 --- a/modules/smaatoBidAdapter.js +++ b/modules/smaatoBidAdapter.js @@ -1,13 +1,11 @@ -import {getDNT} from '../libraries/dnt/index.js'; -import {deepAccess, deepSetValue, isEmpty, isNumber, logError, logInfo} from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {config} from '../src/config.js'; -import {ADPOD, BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js'; -import {NATIVE_IMAGE_TYPES} from '../src/constants.js'; -import {getAdUnitSizes} from '../libraries/sizeUtils/sizeUtils.js'; -import {fill} from '../libraries/appnexusUtils/anUtils.js'; -import {chunk} from '../libraries/chunk/chunk.js'; -import {ortbConverter} from '../libraries/ortbConverter/converter.js'; +import { deepAccess, deepSetValue, isEmpty, isNumber, logError, logInfo } from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { config } from '../src/config.js'; +import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; +import { NATIVE_IMAGE_TYPES } from '../src/constants.js'; +import { getAdUnitSizes } from '../libraries/sizeUtils/sizeUtils.js'; +import { ortbConverter } from '../libraries/ortbConverter/converter.js'; +import { getDNT } from '../libraries/dnt/index.js'; /** * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest @@ -48,30 +46,14 @@ export const spec = { return false; } - if (deepAccess(bid, 'mediaTypes.video.context') === ADPOD) { - logInfo('[SMAATO] Verifying adpod bid request'); - - if (typeof bid.params.adbreakId !== 'string') { - logError('[SMAATO] Missing for adpod request mandatory adbreakId param'); - return false; - } - - if (bid.params.adspaceId) { - logError('[SMAATO] The adspaceId param is not allowed in an adpod bid request'); - return false; - } - } else { - logInfo('[SMAATO] Verifying a non adpod bid request'); - - if (typeof bid.params.adspaceId !== 'string') { - logError('[SMAATO] Missing mandatory adspaceId param'); - return false; - } + if (typeof bid.params.adspaceId !== 'string') { + logError('[SMAATO] Missing mandatory adspaceId param'); + return false; + } - if (bid.params.adbreakId) { - logError('[SMAATO] The adbreakId param is only allowed in an adpod bid request'); - return false; - } + if (bid.params.adbreakId) { + logError('[SMAATO] The adbreakId param is not supported'); + return false; } logInfo('[SMAATO] Verification done, all good'); @@ -86,14 +68,15 @@ export const spec = { // separate requests per mediaType SUPPORTED_MEDIA_TYPES.forEach(mediaType => { if ((bid.mediaTypes && bid.mediaTypes[mediaType]) || (mediaType === NATIVE && bid.nativeOrtbRequest)) { - const data = converter.toORTB({bidderRequest, bidRequests: [bid], context: {mediaType}}); + const data = converter.toORTB({ bidderRequest, bidRequests: [bid], context: { mediaType } }); requests.push({ method: 'POST', url: bid.params.endpoint || SMAATO_ENDPOINT, data: JSON.stringify(data), options: { withCredentials: true, - crossOrigin: true}, + crossOrigin: true + }, bidderRequest }) } @@ -143,43 +126,29 @@ export const spec = { advertiserDomains: bid.adomain, networkName: bid.bidderName, agencyId: seatbid.seat, - ...(bid.ext?.dsa && {dsa: bid.ext.dsa}) + ...(bid.ext?.dsa && { dsa: bid.ext.dsa }) } }; - const videoContext = deepAccess(JSON.parse(bidRequest.data).imp[0], 'video.ext.context'); - if (videoContext === ADPOD) { - resultingBid.vastXml = bid.adm; - resultingBid.mediaType = VIDEO; - if (config.getConfig('adpod.brandCategoryExclusion')) { - resultingBid.meta.primaryCatId = bid.cat[0]; - } - resultingBid.video = { - context: ADPOD, - durationSeconds: bid.ext.duration - }; - bids.push(resultingBid); - } else { - switch (smtAdType) { - case 'Img': - case 'Richmedia': - resultingBid.ad = createBannerAd(bid); - resultingBid.mediaType = BANNER; - bids.push(resultingBid); - break; - case 'Video': - resultingBid.vastXml = bid.adm; - resultingBid.mediaType = VIDEO; - bids.push(resultingBid); - break; - case 'Native': - resultingBid.native = createNativeAd(bid.adm); - resultingBid.mediaType = NATIVE; - bids.push(resultingBid); - break; - default: - logInfo('[SMAATO] Invalid ad type:', smtAdType); - } + switch (smtAdType) { + case 'Img': + case 'Richmedia': + resultingBid.ad = createBannerAd(bid); + resultingBid.mediaType = BANNER; + bids.push(resultingBid); + break; + case 'Video': + resultingBid.vastXml = bid.adm; + resultingBid.mediaType = VIDEO; + bids.push(resultingBid); + break; + case 'Native': + resultingBid.native = createNativeAd(bid.adm); + resultingBid.mediaType = NATIVE; + bids.push(resultingBid); + break; + default: + logInfo('[SMAATO] Invalid ad type:', smtAdType); } resultingBid.meta.mediaType = resultingBid.mediaType; }); @@ -247,15 +216,6 @@ const converter = ortbConverter({ const request = buildRequest(imps, bidderRequest, context); const bidRequest = context.bidRequests[0]; - let content; - const mediaType = context.mediaType; - if (mediaType === VIDEO) { - const videoParams = bidRequest.mediaTypes[VIDEO]; - if (videoParams.context === ADPOD) { - request.imp = createAdPodImp(request.imp[0], videoParams); - content = addOptionalAdpodParameters(videoParams); - } - } request.at = 1; @@ -275,15 +235,9 @@ const converter = ortbConverter({ if (request.site) { request.site.id = window.location.hostname - if (content) { - request.site.content = content; - } setPublisherId(request.site); } else if (request.dooh) { request.dooh.id = window.location.hostname - if (content) { - request.dooh.content = content; - } setPublisherId(request.dooh); } else { request.site = { @@ -291,7 +245,7 @@ const converter = ortbConverter({ domain: bidderRequest.refererInfo.domain || window.location.hostname, page: bidderRequest.refererInfo.page || window.location.href, ref: bidderRequest.refererInfo.ref, - content: content || null + content: null } setPublisherId(request.site); } @@ -362,7 +316,6 @@ const converter = ortbConverter({ imp: { banner(orig, imp, bidRequest, context) { const mediaType = context.mediaType; - if (mediaType === BANNER) { imp.bidfloor = getBidFloor(bidRequest, BANNER, getAdUnitSizes(bidRequest)); } @@ -375,11 +328,9 @@ const converter = ortbConverter({ if (mediaType === VIDEO) { const videoParams = bidRequest.mediaTypes[VIDEO]; imp.bidfloor = getBidFloor(bidRequest, VIDEO, videoParams.playerSize); - if (videoParams.context !== ADPOD) { - deepSetValue(imp, 'video.ext', { - rewarded: videoParams.ext && videoParams.ext.rewarded ? videoParams.ext.rewarded : 0 - }) - } + deepSetValue(imp, 'video.ext', { + rewarded: videoParams.ext && videoParams.ext.rewarded ? videoParams.ext.rewarded : 0 + }) } orig(imp, bidRequest, context); @@ -387,7 +338,6 @@ const converter = ortbConverter({ native(orig, imp, bidRequest, context) { const mediaType = context.mediaType; - if (mediaType === NATIVE) { imp.bidfloor = getBidFloor(bidRequest, NATIVE, getNativeMainImageSize(bidRequest.nativeOrtbRequest)); } @@ -431,85 +381,10 @@ function getNativeMainImageSize(nativeRequest) { return [] } -function createAdPodImp(imp, videoMediaType) { - const bce = config.getConfig('adpod.brandCategoryExclusion') - imp.video.ext = { - context: ADPOD, - brandcategoryexclusion: bce !== undefined && bce - }; - - const numberOfPlacements = getAdPodNumberOfPlacements(videoMediaType) - const imps = fill(imp, numberOfPlacements) - - const durationRangeSec = videoMediaType.durationRangeSec - if (videoMediaType.requireExactDuration) { - // equal distribution of numberOfPlacement over all available durations - const divider = Math.ceil(numberOfPlacements / durationRangeSec.length) - const chunked = chunk(imps, divider) - - // each configured duration is set as min/maxduration for a subset of requests - durationRangeSec.forEach((duration, index) => { - chunked[index].forEach(imp => { - const sequence = index + 1; - imp.video.minduration = duration - imp.video.maxduration = duration - imp.video.sequence = sequence - }); - }); - } else { - // all maxdurations should be the same - const maxDuration = Math.max(...durationRangeSec); - imps.forEach((imp, index) => { - const sequence = index + 1; - imp.video.maxduration = maxDuration - imp.video.sequence = sequence - }); - } - - return imps -} - -function getAdPodNumberOfPlacements(videoMediaType) { - const {adPodDurationSec, durationRangeSec, requireExactDuration} = videoMediaType - const minAllowedDuration = Math.min(...durationRangeSec) - const numberOfPlacements = Math.floor(adPodDurationSec / minAllowedDuration) - - return requireExactDuration - ? Math.max(numberOfPlacements, durationRangeSec.length) - : numberOfPlacements -} - -const addOptionalAdpodParameters = (videoMediaType) => { - const content = {} - - if (videoMediaType.tvSeriesName) { - content.series = videoMediaType.tvSeriesName - } - if (videoMediaType.tvEpisodeName) { - content.title = videoMediaType.tvEpisodeName - } - if (typeof videoMediaType.tvSeasonNumber === 'number') { - content.season = videoMediaType.tvSeasonNumber.toString() // conversion to string as in OpenRTB season is a string - } - if (typeof videoMediaType.tvEpisodeNumber === 'number') { - content.episode = videoMediaType.tvEpisodeNumber - } - if (typeof videoMediaType.contentLengthSec === 'number') { - content.len = videoMediaType.contentLengthSec - } - if (videoMediaType.contentMode && ['live', 'on-demand'].indexOf(videoMediaType.contentMode) >= 0) { - content.livestream = videoMediaType.contentMode === 'live' ? 1 : 0 - } - - if (!isEmpty(content)) { - return content - } -} - function getBidFloor(bidRequest, mediaType, sizes) { if (typeof bidRequest.getFloor === 'function') { const size = sizes.length === 1 ? sizes[0] : '*'; - const floor = bidRequest.getFloor({currency: CURRENCY, mediaType: mediaType, size: size}); + const floor = bidRequest.getFloor({ currency: CURRENCY, mediaType: mediaType, size: size }); if (floor && !isNaN(floor.floor) && (floor.currency === CURRENCY)) { return floor.floor; } diff --git a/modules/smarthubBidAdapter.js b/modules/smarthubBidAdapter.js index 2a896a92499..5b7a8fba1fe 100644 --- a/modules/smarthubBidAdapter.js +++ b/modules/smarthubBidAdapter.js @@ -1,5 +1,5 @@ -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; import { buildPlacementProcessingFunction, buildRequestsBase, @@ -15,19 +15,19 @@ const SYNC_URLS = { }; const ALIASES = { - 'attekmi': {area: '1', pid: '300'}, - 'markapp': {area: '4', pid: '360'}, - 'jdpmedia': {area: '1', pid: '382'}, - 'tredio': {area: '4', pid: '337'}, - 'felixads': {area: '1', pid: '406'}, - 'artechnology': {area: '1', pid: '420'}, - 'adinify': {area: '1', pid: '424'}, - 'addigi': {area: '1', pid: '425'}, - 'jambojar': {area: '1', pid: '426'}, - 'anzu': {area: '1', pid: '445'}, - 'amcom': {area: '1', pid: '397'}, - 'adastra': {area: '1', pid: '33'}, - 'radiantfusion': {area: '1', pid: '455'}, + 'attekmi': { area: '1', pid: '300' }, + 'markapp': { area: '4', pid: '360' }, + 'jdpmedia': { area: '1', pid: '382' }, + 'tredio': { area: '4', pid: '337' }, + 'felixads': { area: '1', pid: '406' }, + 'artechnology': { area: '1', pid: '420' }, + 'adinify': { area: '1', pid: '424' }, + 'addigi': { area: '1', pid: '425' }, + 'jambojar': { area: '1', pid: '426' }, + 'anzu': { area: '1', pid: '445' }, + 'amcom': { area: '1', pid: '397' }, + 'adastra': { area: '1', pid: '33' }, + 'radiantfusion': { area: '1', pid: '455' }, }; const BASE_URLS = { diff --git a/modules/smarticoBidAdapter.js b/modules/smarticoBidAdapter.js index cb4379263ff..0b81735cbe1 100644 --- a/modules/smarticoBidAdapter.js +++ b/modules/smarticoBidAdapter.js @@ -1,5 +1,5 @@ -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER} from '../src/mediaTypes.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER } from '../src/mediaTypes.js'; const SMARTICO_CONFIG = { bidRequestUrl: 'https://trmads.eu/preBidRequest', @@ -66,7 +66,7 @@ export const spec = { url: SMARTICO_CONFIG.bidRequestUrl, bids: validBidRequests, // TODO: fix auctionId leak: https://github.com/prebid/Prebid.js/issues/9781 - data: {bidParams: bidParams, auctionId: bidderRequest.auctionId} + data: { bidParams: bidParams, auctionId: bidderRequest.auctionId } } return ServerRequestObjects; }, diff --git a/modules/smartxBidAdapter.js b/modules/smartxBidAdapter.js index 00f8616a665..46765cf217c 100644 --- a/modules/smartxBidAdapter.js +++ b/modules/smartxBidAdapter.js @@ -1,4 +1,3 @@ -import {getDNT} from '../libraries/dnt/index.js'; import { logError, deepAccess, @@ -21,6 +20,7 @@ import { import { VIDEO } from '../src/mediaTypes.js'; +import { getDNT } from '../libraries/dnt/index.js'; /** * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest diff --git a/modules/smartyadsAnalyticsAdapter.js b/modules/smartyadsAnalyticsAdapter.js index eab9995d238..b666cc9294b 100644 --- a/modules/smartyadsAnalyticsAdapter.js +++ b/modules/smartyadsAnalyticsAdapter.js @@ -84,7 +84,7 @@ const auctionHandler = (eventType, data) => { } const bidHandler = (eventType, bid) => { - const bids = bid.length ? bid : [ bid ]; + const bids = bid.length ? bid : [bid]; for (const bidObj of bids) { let bidToSend; @@ -114,7 +114,7 @@ const onBidderError = (data) => { error: data.error, bidderRequests: data?.bidderRequests?.length ? data.bidderRequests.filter(request => request.bidderCode === BIDDER_CODE) - : [ data.bidderRequest ] + : [data.bidderRequest] }); } diff --git a/modules/smartytechBidAdapter.js b/modules/smartytechBidAdapter.js index 373faebf5a4..1fc3ec5801c 100644 --- a/modules/smartytechBidAdapter.js +++ b/modules/smartytechBidAdapter.js @@ -1,10 +1,10 @@ -import {buildUrl, deepAccess, isArray, generateUUID} from '../src/utils.js' +import { buildUrl, deepAccess, isArray, generateUUID } from '../src/utils.js' import { BANNER, VIDEO } from '../src/mediaTypes.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {config} from '../src/config.js'; -import {chunk} from '../libraries/chunk/chunk.js'; -import {getStorageManager} from '../src/storageManager.js'; -import {findRootDomain} from '../src/fpd/rootDomain.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { config } from '../src/config.js'; +import { chunk } from '../libraries/chunk/chunk.js'; +import { getStorageManager } from '../src/storageManager.js'; +import { findRootDomain } from '../src/fpd/rootDomain.js'; const BIDDER_CODE = 'smartytech'; export const ENDPOINT_PROTOCOL = 'https'; @@ -16,7 +16,7 @@ const AUID_COOKIE_NAME = '_smartytech_auid'; const AUID_COOKIE_EXPIRATION_DAYS = 1825; // 5 years // Storage manager for cookies -export const storage = getStorageManager({bidderCode: BIDDER_CODE}); +export const storage = getStorageManager({ bidderCode: BIDDER_CODE }); /** * Get or generate Alias User ID (auId) @@ -47,7 +47,7 @@ export function getAliasUserId() { } export const spec = { - supportedMediaTypes: [ BANNER, VIDEO ], + supportedMediaTypes: [BANNER, VIDEO], code: BIDDER_CODE, isBidRequestValid: function (bidRequest) { diff --git a/modules/smilewantedBidAdapter.js b/modules/smilewantedBidAdapter.js index 5e47fe3cf47..0bb4ba86eff 100644 --- a/modules/smilewantedBidAdapter.js +++ b/modules/smilewantedBidAdapter.js @@ -1,10 +1,10 @@ -import {deepAccess, deepClone, isArray, isFn, isPlainObject, logError, logWarn} from '../src/utils.js'; -import {Renderer} from '../src/Renderer.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js'; -import {INSTREAM, OUTSTREAM} from '../src/video.js'; -import {serializeSupplyChain} from '../libraries/schainSerializer/schainSerializer.js' -import {convertOrtbRequestToProprietaryNative, toOrtbNativeRequest, toLegacyResponse} from '../src/native.js'; +import { deepAccess, deepClone, isArray, isFn, isPlainObject, logError, logWarn } from '../src/utils.js'; +import { Renderer } from '../src/Renderer.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; +import { INSTREAM, OUTSTREAM } from '../src/video.js'; +import { serializeSupplyChain } from '../libraries/schainSerializer/schainSerializer.js' +import { convertOrtbRequestToProprietaryNative, toOrtbNativeRequest, toLegacyResponse } from '../src/native.js'; import { getCurrencyFromBidderRequest } from '../libraries/ortb2Utils/currency.js'; const BIDDER_CODE = 'smilewanted'; diff --git a/modules/snigelBidAdapter.js b/modules/snigelBidAdapter.js index 29534ae7318..44fa0e257c7 100644 --- a/modules/snigelBidAdapter.js +++ b/modules/snigelBidAdapter.js @@ -1,10 +1,10 @@ -import {getDNT} from '../libraries/dnt/index.js'; -import {config} from '../src/config.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER} from '../src/mediaTypes.js'; -import {deepAccess, isArray, isFn, isPlainObject, inIframe, generateUUID} from '../src/utils.js'; -import {getStorageManager} from '../src/storageManager.js'; +import { config } from '../src/config.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER } from '../src/mediaTypes.js'; +import { deepAccess, isArray, isFn, isPlainObject, inIframe, generateUUID } from '../src/utils.js'; +import { getStorageManager } from '../src/storageManager.js'; import { getViewportSize } from '../libraries/viewport/viewport.js'; +import { getDNT } from '../libraries/dnt/index.js'; const BIDDER_CODE = 'snigel'; const GVLID = 1076; @@ -15,7 +15,7 @@ const FLOOR_MATCH_ALL_SIZES = '*'; const SESSION_ID_KEY = '_sn_session_pba'; const getConfig = config.getConfig; -const storageManager = getStorageManager({bidderCode: BIDDER_CODE}); +const storageManager = getStorageManager({ bidderCode: BIDDER_CODE }); const refreshes = {}; const placementCounters = {}; const pageViewStart = new Date().getTime(); @@ -109,7 +109,7 @@ export const spec = { getUserSyncs: function (syncOptions, responses, gdprConsent, uspConsent, gppConsent) { const syncUrl = getSyncUrl(responses || []); if (syncUrl && syncOptions.iframeEnabled) { - return [{type: 'iframe', url: getSyncEndpoint(syncUrl, gdprConsent, uspConsent, gppConsent)}]; + return [{ type: 'iframe', url: getSyncEndpoint(syncUrl, gdprConsent, uspConsent, gppConsent) }]; } }, }; diff --git a/modules/sonaradsBidAdapter.js b/modules/sonaradsBidAdapter.js index b1699029f21..2754b3f6062 100644 --- a/modules/sonaradsBidAdapter.js +++ b/modules/sonaradsBidAdapter.js @@ -1,8 +1,8 @@ -import {deepSetValue, isFn, logWarn} from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER} from '../src/mediaTypes.js'; -import {ortbConverter} from '../libraries/ortbConverter/converter.js'; -import {tryAppendQueryString} from '../libraries/urlUtils/urlUtils.js'; +import { deepSetValue, isFn, logWarn } from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER } from '../src/mediaTypes.js'; +import { ortbConverter } from '../libraries/ortbConverter/converter.js'; +import { tryAppendQueryString } from '../libraries/urlUtils/urlUtils.js'; /** * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest diff --git a/modules/sovrnBidAdapter.js b/modules/sovrnBidAdapter.js index 2e38a88c2f4..285cd43d1b3 100644 --- a/modules/sovrnBidAdapter.js +++ b/modules/sovrnBidAdapter.js @@ -39,6 +39,7 @@ export const spec = { code: 'sovrn', supportedMediaTypes: [BANNER, VIDEO], gvlid: 13, + alwaysHasCapacity: true, /** * Check if the bid is a valid zone ID in either number or string form @@ -102,7 +103,7 @@ export const spec = { let bidSizes = deepAccess(bid, 'mediaTypes.banner.sizes') || bid.sizes bidSizes = (isArray(bidSizes) && isArray(bidSizes[0])) ? bidSizes : [bidSizes] bidSizes = bidSizes.filter(size => isArray(size)) - const processedSizes = bidSizes.map(size => ({w: parseInt(size[0], 10), h: parseInt(size[1], 10)})) + const processedSizes = bidSizes.map(size => ({ w: parseInt(size[0], 10), h: parseInt(size[1], 10) })) imp.banner = { format: processedSizes, @@ -195,7 +196,7 @@ export const spec = { method: 'POST', url: url, data: JSON.stringify(sovrnBidReq), - options: {contentType: 'text/plain'} + options: { contentType: 'text/plain' } } } catch (e) { logError('Could not build bidrequest, error deatils:', e); @@ -207,7 +208,7 @@ export const spec = { * @param {*} param0 A successful response from Sovrn. * @return {Bid[]} An array of formatted bids. */ - interpretResponse: function({ body: {id, seatbid} }) { + interpretResponse: function({ body: { id, seatbid } }) { if (!id || !seatbid || !Array.isArray(seatbid)) return [] try { diff --git a/modules/sparteoBidAdapter.js b/modules/sparteoBidAdapter.js index 13cb0198594..f7a6c472815 100644 --- a/modules/sparteoBidAdapter.js +++ b/modules/sparteoBidAdapter.js @@ -91,6 +91,7 @@ function createRenderer(rendererConfig) { } function outstreamRender(bid) { + // TODO this should use getAdUnitElement if (!document.getElementById(bid.adUnitCode)) { logError(`Sparteo Bid Adapter: Video renderer did not started. bidResponse.adUnitCode is probably not a DOM element : ${bid.adUnitCode}`); return; @@ -226,7 +227,7 @@ export const spec = { }, buildRequests: function (bidRequests, bidderRequest) { - const payload = converter.toORTB({bidRequests, bidderRequest}) + const payload = converter.toORTB({ bidRequests, bidderRequest }) const endpoint = bidRequests[0].params.endpoint ? bidRequests[0].params.endpoint : REQUEST_URL; const url = replaceMacros(payload, endpoint); @@ -239,7 +240,7 @@ export const spec = { }, interpretResponse: function (serverResponse, requests) { - const bids = converter.fromORTB({response: serverResponse.body, request: requests.data}).bids; + const bids = converter.fromORTB({ response: serverResponse.body, request: requests.data }).bids; return bids; }, diff --git a/modules/ssmasBidAdapter.js b/modules/ssmasBidAdapter.js index 29d3b787ad1..2c4a2bff135 100644 --- a/modules/ssmasBidAdapter.js +++ b/modules/ssmasBidAdapter.js @@ -2,7 +2,7 @@ import { BANNER } from '../src/mediaTypes.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { triggerPixel, deepSetValue } from '../src/utils.js'; import { ortbConverter } from '../libraries/ortbConverter/converter.js'; -import {config} from '../src/config.js'; +import { config } from '../src/config.js'; export const SSMAS_CODE = 'ssmas'; const SSMAS_SERVER = 'ads.ssmas.com'; diff --git a/modules/sspBCBidAdapter.js b/modules/sspBCBidAdapter.js index 5ce8fb34492..72caeb713e4 100644 --- a/modules/sspBCBidAdapter.js +++ b/modules/sspBCBidAdapter.js @@ -6,6 +6,7 @@ import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; import { getCurrencyFromBidderRequest } from '../libraries/ortb2Utils/currency.js'; +import { EVENT_TYPE_VIEWABLE, TRACKER_METHOD_IMG } from '../src/eventTrackers.js'; const BIDDER_CODE = 'sspBC'; const BIDDER_URL = 'https://ssp.wp.pl/bidder/'; @@ -671,8 +672,7 @@ const spec = { interpretResponse(serverResponse, request) { const { bidderRequest } = request; const { body: response = {} } = serverResponse; - const { seatbid: responseSeat, ext: responseExt = {} } = response; - const { paapi: fledgeAuctionConfigs = [] } = responseExt; + const { seatbid: responseSeat } = response; const bids = []; let site = JSON.parse(request.data).site; // get page and referer data from request site.sn = response.sn || 'mc_adapter'; // WPM site name (wp_sn) @@ -738,6 +738,7 @@ const spec = { }, netRevenue: true, vurls, + eventtrackers: vurls.map(url => ({ event: EVENT_TYPE_VIEWABLE, method: TRACKER_METHOD_IMG, url })), }; // mediaType and ad data for instream / native / banner @@ -788,12 +789,12 @@ const spec = { }); } - return fledgeAuctionConfigs.length ? { bids, fledgeAuctionConfigs } : bids; + return bids; }, getUserSyncs(syncOptions, _, gdprConsent = {}) { - const {iframeEnabled, pixelEnabled} = syncOptions; - const {gdprApplies, consentString = ''} = gdprConsent; + const { iframeEnabled, pixelEnabled } = syncOptions; + const { gdprApplies, consentString = '' } = gdprConsent; const mySyncs = []; if (iframeEnabled) { mySyncs.push({ diff --git a/modules/ssp_genieeBidAdapter.js b/modules/ssp_genieeBidAdapter.js index c4464f0f59a..409c81b3e54 100644 --- a/modules/ssp_genieeBidAdapter.js +++ b/modules/ssp_genieeBidAdapter.js @@ -16,12 +16,12 @@ const BIDDER_CODE = 'ssp_geniee'; export const BANNER_ENDPOINT = 'https://aladdin.genieesspv.jp/yie/ld/api/ad_call/v2'; export const USER_SYNC_ENDPOINT_IMAGE = 'https://cs.gssprt.jp/yie/ld/mcs'; export const USER_SYNC_ENDPOINT_IFRAME = 'https://aladdin.genieesspv.jp/yie/ld'; -const SUPPORTED_MEDIA_TYPES = [ BANNER ]; +const SUPPORTED_MEDIA_TYPES = [BANNER]; const DEFAULT_CURRENCY = 'JPY'; const ALLOWED_CURRENCIES = ['USD', 'JPY']; const NET_REVENUE = true; const MODULE_NAME = `ssp_geniee`; -export const storage = getStorageManager({moduleType: MODULE_TYPE_ANALYTICS, moduleName: MODULE_NAME}) +export const storage = getStorageManager({ moduleType: MODULE_TYPE_ANALYTICS, moduleName: MODULE_NAME }) /** * List of keys for geparams (parameters we use) @@ -124,7 +124,7 @@ function hasParamsNotBlankString(params, key) { ); } -export const buildExtuidQuery = ({id5, imuId}) => { +export const buildExtuidQuery = ({ id5, imuId }) => { const params = [ ...(id5 ? [`id5:${id5}`] : []), ...(imuId ? [`im:${imuId}`] : []), @@ -233,7 +233,7 @@ function makeCommonRequestData(bid, geparameter, refererInfo) { // imuid, id5 const id5 = utils.deepAccess(bid, 'userId.id5id.uid'); const imuId = utils.deepAccess(bid, 'userId.imuid'); - const extuidQuery = buildExtuidQuery({id5, imuId}); + const extuidQuery = buildExtuidQuery({ id5, imuId }); if (extuidQuery) data.extuid = extuidQuery; // makeUAQuery diff --git a/modules/stackadaptBidAdapter.js b/modules/stackadaptBidAdapter.js index 4f866c217d2..86303e17232 100644 --- a/modules/stackadaptBidAdapter.js +++ b/modules/stackadaptBidAdapter.js @@ -2,7 +2,7 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { ortbConverter } from '../libraries/ortbConverter/converter.js'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; import { deepSetValue, logWarn, parseSizesInput, isNumber, isInteger, replaceAuctionPrice, formatQS, isFn, isPlainObject } from '../src/utils.js'; -import {getUserSyncParams} from '../libraries/userSyncUtils/userSyncUtils.js'; +import { getUserSyncParams } from '../libraries/userSyncUtils/userSyncUtils.js'; const BIDDER_CODE = 'stackadapt'; const ENDPOINT_URL = 'https://pjs.srv.stackadapt.com/br'; diff --git a/modules/startioBidAdapter.js b/modules/startioBidAdapter.js index 74629f2cc9c..fe1d10bf9a2 100644 --- a/modules/startioBidAdapter.js +++ b/modules/startioBidAdapter.js @@ -112,7 +112,7 @@ export const spec = { const data = converter.toORTB({ bidRequests: [bidRequest], bidderRequest, - context: {mediaType, bidParams: bidRequest.params} + context: { mediaType, bidParams: bidRequest.params } }); return { diff --git a/modules/stnBidAdapter.js b/modules/stnBidAdapter.js index 62d82b8d4b2..0508d50e18c 100644 --- a/modules/stnBidAdapter.js +++ b/modules/stnBidAdapter.js @@ -1,6 +1,6 @@ -import {logWarn} from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {makeBaseSpec} from '../libraries/riseUtils/index.js'; +import { logWarn } from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { makeBaseSpec } from '../libraries/riseUtils/index.js'; const BIDDER_CODE = 'stn'; const BASE_URL = 'https://hb.stngo.com/'; diff --git a/modules/storageControl.ts b/modules/storageControl.ts index 93411fba3ac..9ee860883d2 100644 --- a/modules/storageControl.ts +++ b/modules/storageControl.ts @@ -1,5 +1,5 @@ -import {config} from '../src/config.js'; -import {metadata} from '../libraries/metadata/metadata.js'; +import { config } from '../src/config.js'; +import { metadata } from '../libraries/metadata/metadata.js'; import { ACTIVITY_PARAM_COMPONENT, ACTIVITY_PARAM_COMPONENT_NAME, @@ -13,14 +13,14 @@ import { STORAGE_TYPE_LOCALSTORAGE, type StorageDisclosure as Disclosure } from '../src/storageManager.js'; -import {logWarn, uniques} from '../src/utils.js'; -import {registerActivityControl} from '../src/activities/rules.js'; -import {ACTIVITY_ACCESS_DEVICE} from '../src/activities/activities.js'; -import {addApiMethod} from "../src/prebid.ts"; +import { logWarn, uniques } from '../src/utils.js'; +import { registerActivityControl } from '../src/activities/rules.js'; +import { ACTIVITY_ACCESS_DEVICE } from '../src/activities/activities.js'; +import { addApiMethod } from "../src/prebid.ts"; // @ts-expect-error the ts compiler is confused by build-time renaming of summary.mjs to summary.js, reassure it // eslint-disable-next-line prebid/validate-imports -import {getStorageDisclosureSummary} from "../libraries/storageDisclosure/summary.js"; -import {getGlobal} from "../src/prebidGlobal.ts"; +import { getStorageDisclosureSummary } from "../libraries/storageDisclosure/summary.js"; +import { getGlobal } from "../src/prebidGlobal.ts"; export const ENFORCE_STRICT = 'strict'; export const ENFORCE_ALIAS = 'allowAliases'; @@ -83,9 +83,9 @@ export function checkDisclosure(params, getMatchingDisclosures = getDisclosures) if (disclosures == null) { reason = `Cannot determine if storage key "${key}" is disclosed by "${component}" because the necessary metadata is missing - was it included in the build?` } else { - const {disclosureURLs, matches} = disclosures; + const { disclosureURLs, matches } = disclosures; const moduleName = params[ACTIVITY_PARAM_COMPONENT_NAME] - for (const {componentName} of matches) { + for (const { componentName } of matches) { if (componentName === moduleName) { disclosed = true; } else { @@ -113,11 +113,11 @@ export function checkDisclosure(params, getMatchingDisclosures = getDisclosures) export function storageControlRule(getEnforcement = () => enforcement, check = checkDisclosure) { return function (params) { - const {disclosed, parent, reason} = check(params); + const { disclosed, parent, reason } = check(params); if (disclosed === null) return; if (!disclosed) { - const enforcement = getEnforcement(); - if (enforcement === ENFORCE_STRICT || (enforcement === ENFORCE_ALIAS && !parent)) return {allow: false, reason}; + const enforcement = getEnforcement() ?? ENFORCE_STRICT; + if (enforcement === ENFORCE_STRICT || (enforcement === ENFORCE_ALIAS && !parent)) return { allow: false, reason }; if (reason) { logWarn('storageControl:', reason); } @@ -125,14 +125,19 @@ export function storageControlRule(getEnforcement = () => enforcement, check = c } } -registerActivityControl(ACTIVITY_ACCESS_DEVICE, 'storageControl', storageControlRule()); +const rule = registerActivityControl(ACTIVITY_ACCESS_DEVICE, 'storageControl', storageControlRule()); + +export function deactivate() { + // turn off this module; should only be used in testing + rule(); +} export type StorageControlConfig = { /** - * - 'off': logs a warning when an undisclosed storage key is used - * - 'strict': deny access to undisclosed storage keys + * - 'strict': deny access to undisclosed storage keys (default) * - 'allowAliases': deny access to undisclosed storage keys, unless the use is from an alias of a module that does * disclose them + * - 'off': logs a warning when an undisclosed storage key is used */ enforcement?: typeof ENFORCE_OFF | typeof ENFORCE_ALIAS | typeof ENFORCE_STRICT; } @@ -144,7 +149,7 @@ declare module '../src/config' { } config.getConfig('storageControl', (cfg) => { - enforcement = cfg?.storageControl?.enforcement ?? ENFORCE_OFF; + enforcement = cfg?.storageControl?.enforcement ?? ENFORCE_STRICT; }) export function dynamicDisclosureCollector() { @@ -185,7 +190,7 @@ export function dynamicDisclosureCollector() { } } -const {hook: discloseStorageHook, getDisclosures: dynamicDisclosures} = dynamicDisclosureCollector(); +const { hook: discloseStorageHook, getDisclosures: dynamicDisclosures } = dynamicDisclosureCollector(); discloseStorageUse.before(discloseStorageHook); export type StorageDisclosure = Disclosure & { diff --git a/modules/stroeerCoreBidAdapter.js b/modules/stroeerCoreBidAdapter.js index aa9f656aa24..9ece204613e 100644 --- a/modules/stroeerCoreBidAdapter.js +++ b/modules/stroeerCoreBidAdapter.js @@ -1,8 +1,8 @@ -import {buildUrl, deepAccess, deepSetValue, generateUUID, getWinDimensions, getWindowSelf, getWindowTop, isEmpty, isStr, logWarn} from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER, VIDEO} from '../src/mediaTypes.js'; -import {getBoundingClientRect} from '../libraries/boundingClientRect/boundingClientRect.js'; -import {getGlobal} from '../src/prebidGlobal.js'; +import { buildUrl, deepAccess, deepSetValue, generateUUID, getWinDimensions, getWindowSelf, getWindowTop, isEmpty, isStr, logWarn } from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; +import { getBoundingClientRect } from '../libraries/boundingClientRect/boundingClientRect.js'; +import { getGlobal } from '../src/prebidGlobal.js'; const GVL_ID = 136; const BIDDER_CODE = 'stroeerCore'; @@ -79,13 +79,8 @@ export const spec = { }; } - const ORTB2_KEYS = ['regs.ext.dsa', 'device.ext.cdep', 'site.ext']; - ORTB2_KEYS.forEach(key => { - const value = deepAccess(bidderRequest.ortb2, key); - if (value !== undefined) { - deepSetValue(basePayload, `ortb2.${key}`, value); - } - }); + const ORTB2_PATHS = ['regs.ext.dsa', 'device.ext.cdep', 'site.ext', 'source.tid']; + copyDeepPaths(basePayload, bidderRequest.ortb2, ORTB2_PATHS, 'ortb2'); const bannerBids = validBidRequests .filter(hasBanner) @@ -98,7 +93,7 @@ export const spec = { return { method: 'POST', url: buildEndpointUrl(anyBid.params), - data: {...basePayload, bids: [...bannerBids, ...videoBids]} + data: { ...basePayload, bids: [...bannerBids, ...videoBids] } }; }, @@ -118,7 +113,7 @@ export const spec = { currency: 'EUR', netRevenue: true, creativeId: '', - meta: {...bidResponse.meta}, + meta: { ...bidResponse.meta }, mediaType, }; @@ -156,6 +151,7 @@ const isMainPageAccessible = () => { } const elementInView = (elementId) => { + // TODO this should use getAdUnitElement const resolveElement = (elId) => { const win = getWindowSelf(); @@ -181,12 +177,12 @@ const elementInView = (elementId) => { return undefined; } -const buildEndpointUrl = ({host: hostname = DEFAULT_HOST, port = DEFAULT_PORT, securePort, path: pathname = DEFAULT_PATH}) => { +const buildEndpointUrl = ({ host: hostname = DEFAULT_HOST, port = DEFAULT_PORT, securePort, path: pathname = DEFAULT_PATH }) => { if (securePort) { port = securePort; } - return buildUrl({protocol: 'https', hostname, port, pathname}); + return buildUrl({ protocol: 'https', hostname, port, pathname }); } const getGdprParams = gdprConsent => { @@ -213,12 +209,17 @@ const hasVideo = bidReq => { ['instream', 'outstream'].indexOf(mediaTypes.video.context) > -1; }; -const mapToPayloadBaseBid = (bidRequest) => ({ - bid: bidRequest.bidId, - sid: bidRequest.params.sid, - viz: elementInView(bidRequest.adUnitCode), - sfp: bidRequest.params.sfp, -}); +const mapToPayloadBaseBid = (bidRequest) => { + const bid = { + bid: bidRequest.bidId, + sid: bidRequest.params.sid, + viz: elementInView(bidRequest.adUnitCode), + sfp: bidRequest.params.sfp, + tid: bidRequest.transactionId, + } + copyDeepPaths(bid, bidRequest.ortb2Imp, ['ext.gpid'], 'ortb2Imp'); + return bid; +}; const mapToPayloadBannerBid = (bidRequest) => { const sizes = deepAccess(bidRequest, 'mediaTypes.banner.sizes') || []; @@ -261,7 +262,7 @@ const createFloorPriceObject = (mediaType, sizes, bidRequest) => { mediaType: mediaType, size: [size[0], size[1]] }) || {}; - return {...floor, size}; + return { ...floor, size }; }); const floorWithCurrency = (([defaultFloor].concat(sizeFloors)) || []).find(floor => floor.currency); @@ -287,4 +288,17 @@ const createFloorPriceObject = (mediaType, sizes, bidRequest) => { }; } +const copyDeepPaths = (target, source, paths, targetPrefix = '') => { + paths.forEach(path => { + const value = deepAccess(source, path); + if (value !== undefined) { + const targetPath = targetPrefix + ? `${targetPrefix}.${path}` + : path; + + deepSetValue(target, targetPath, value); + } + }); +} + registerBidder(spec); diff --git a/modules/stvBidAdapter.js b/modules/stvBidAdapter.js index 634eff58c05..45fc0791be4 100644 --- a/modules/stvBidAdapter.js +++ b/modules/stvBidAdapter.js @@ -1,6 +1,6 @@ -import {deepAccess, logMessage} from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER, VIDEO} from '../src/mediaTypes.js'; +import { deepAccess, logMessage } from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; import { handleSyncUrls, diff --git a/modules/symitriAnalyticsAdapter.js b/modules/symitriAnalyticsAdapter.js index 4e2d1e070e6..6b4792e95c8 100644 --- a/modules/symitriAnalyticsAdapter.js +++ b/modules/symitriAnalyticsAdapter.js @@ -2,7 +2,7 @@ import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; import { EVENTS } from '../src/constants.js'; import { logMessage } from '../src/utils.js'; -import {ajax} from '../src/ajax.js'; +import { ajax } from '../src/ajax.js'; const analyticsType = 'endpoint'; const url = 'https://ProdSymPrebidEventhub1.servicebus.windows.net/prebid-said-1/messages'; @@ -11,7 +11,7 @@ const { BID_WON } = EVENTS; let initOptions; -const symitriAnalytics = Object.assign(adapter({url, analyticsType}), { +const symitriAnalytics = Object.assign(adapter({ url, analyticsType }), { track({ eventType, args }) { switch (eventType) { case BID_WON: @@ -41,7 +41,7 @@ function sendEvent(payload) { ajax(url, cb, body, { method: 'POST', - customHeaders: {'Content-Type': 'application/atom+xml;type=entry;charset=utf-8', 'Authorization': initOptions.apiAuthToken} + customHeaders: { 'Content-Type': 'application/atom+xml;type=entry;charset=utf-8', 'Authorization': initOptions.apiAuthToken } }); } } catch (err) { logMessage('##### symitriAnalytics :: error' + err) } diff --git a/modules/symitriDapRtdProvider.js b/modules/symitriDapRtdProvider.js index ace251872ff..099f428323b 100644 --- a/modules/symitriDapRtdProvider.js +++ b/modules/symitriDapRtdProvider.js @@ -5,12 +5,12 @@ * @module modules/symitriDapRtdProvider * @requires module:modules/realTimeData */ -import {ajax} from '../src/ajax.js'; -import {getStorageManager} from '../src/storageManager.js'; -import {submodule} from '../src/hook.js'; -import {isPlainObject, mergeDeep, logMessage, logInfo, logError} from '../src/utils.js'; +import { ajax } from '../src/ajax.js'; +import { getStorageManager } from '../src/storageManager.js'; +import { submodule } from '../src/hook.js'; +import { isPlainObject, mergeDeep, logMessage, logInfo, logError } from '../src/utils.js'; import { loadExternalScript } from '../src/adloader.js'; -import {MODULE_TYPE_RTD} from '../src/activities/modules.js'; +import { MODULE_TYPE_RTD } from '../src/activities/modules.js'; /** * @typedef {import('../modules/rtdModule/index.js').RtdSubmodule} RtdSubmodule @@ -28,7 +28,7 @@ export function createRtdProvider(moduleName, moduleCode, headerPrefix) { const DAP_MAX_RETRY_TOKENIZE = 1; const DAP_CLIENT_ENTROPY = 'dap_client_entropy' - const storage = getStorageManager({moduleType: MODULE_TYPE_RTD, moduleName: SUBMODULE_NAME}); + const storage = getStorageManager({ moduleType: MODULE_TYPE_RTD, moduleName: SUBMODULE_NAME }); let dapRetryTokenize = 0; /** @@ -186,7 +186,7 @@ export function createRtdProvider(moduleName, moduleCode, headerPrefix) { api_version: rtdConfig.params.apiVersion, domain: rtdConfig.params.domain, segtax: rtdConfig.params.segtax, - identity: {type: rtdConfig.params.identityType, value: rtdConfig.params.identityValue}, + identity: { type: rtdConfig.params.identityType, value: rtdConfig.params.identityValue }, }; let refreshMembership = true; const token = dapUtils.dapGetTokenFromLocalStorage(); @@ -229,7 +229,7 @@ export function createRtdProvider(moduleName, moduleCode, headerPrefix) { // Trigger a refresh const now = Math.round(Date.now() / 1000.0); // in seconds const item = {} - const configAsync = {...config}; + const configAsync = { ...config }; dapUtils.dapTokenize(configAsync, config.identity, onDone, function(token, status, xhr, onDone) { item.expires_at = now + DAP_DEFAULT_TOKEN_TTL; @@ -284,7 +284,7 @@ export function createRtdProvider(moduleName, moduleCode, headerPrefix) { dapRefreshMembership: function(ortb2, config, token, onDone) { const now = Math.round(Date.now() / 1000.0); // in seconds const item = {} - const configAsync = {...config}; + const configAsync = { ...config }; dapUtils.dapMembership(configAsync, token, onDone, function(membership, status, xhr, onDone) { item.expires_at = now + DAP_DEFAULT_TOKEN_TTL; @@ -332,7 +332,7 @@ export function createRtdProvider(moduleName, moduleCode, headerPrefix) { dapRefreshEncryptedMembership: function(ortb2, config, token, onDone) { const now = Math.round(Date.now() / 1000.0); // in seconds const item = {}; - const configAsync = {...config}; + const configAsync = { ...config }; dapUtils.dapEncryptedMembership(configAsync, token, onDone, function(encToken, status, xhr, onDone) { item.expires_at = now + DAP_DEFAULT_TOKEN_TTL; @@ -528,7 +528,7 @@ export function createRtdProvider(moduleName, moduleCode, headerPrefix) { if (config === null || config === undefined) { onError(null, 'Invalid config object', 'ClientError', onDone); - return [ config, true ]; + return [config, true]; } if (!('api_version' in config) || (typeof (config.api_version) === 'string' && config.api_version.length === 0)) { @@ -537,22 +537,22 @@ export function createRtdProvider(moduleName, moduleCode, headerPrefix) { if (typeof (config.api_version) !== 'string') { onError(null, "Invalid api_version: must be a string like 'x1', etc.", 'ClientError', onDone); - return [ config, true ]; + return [config, true]; } if (!(('api_hostname') in config) || typeof (config.api_hostname) !== 'string' || config.api_hostname.length === 0) { onError(null, 'Invalid api_hostname: must be a non-empty string', 'ClientError', onDone); - return [ config, true ]; + return [config, true]; } if (token) { if (typeof (token) !== 'string') { onError(null, 'Invalid token: must be a non-null string', 'ClientError', onDone); - return [ config, true ]; + return [config, true]; } } - return [ config, false ]; + return [config, false]; }, addIdentifier: async function(identity, apiParams) { @@ -606,7 +606,7 @@ export function createRtdProvider(moduleName, moduleCode, headerPrefix) { */ dapTokenize: function(config, identity, onDone, onSuccess = null, onError = null) { let hasTokenizeError; - [ config, hasTokenizeError ] = this.dapValidationHelper(config, onDone, null, onError); + [config, hasTokenizeError] = this.dapValidationHelper(config, onDone, null, onError); if (hasTokenizeError) { return; } if (typeof (config.domain) !== 'string') { @@ -739,7 +739,7 @@ export function createRtdProvider(moduleName, moduleCode, headerPrefix) { */ dapMembership: function(config, token, onDone, onSuccess = null, onError = null) { let hasMembershipError; - [ config, hasMembershipError ] = this.dapValidationHelper(config, onDone, token, onError); + [config, hasMembershipError] = this.dapValidationHelper(config, onDone, token, onError); if (hasMembershipError) { return; } if (typeof (config.domain) !== 'string') { @@ -803,7 +803,7 @@ export function createRtdProvider(moduleName, moduleCode, headerPrefix) { */ dapEncryptedMembership: function(config, token, onDone, onSuccess = null, onError = null) { let hasEncryptedMembershipError; - [ config, hasEncryptedMembershipError ] = this.dapValidationHelper(config, onDone, token, onError); + [config, hasEncryptedMembershipError] = this.dapValidationHelper(config, onDone, token, onError); if (hasEncryptedMembershipError) { return; } const cb = { diff --git a/modules/taboolaBidAdapter.js b/modules/taboolaBidAdapter.js index 3e7127f1ab7..b4b04d4123a 100644 --- a/modules/taboolaBidAdapter.js +++ b/modules/taboolaBidAdapter.js @@ -1,17 +1,30 @@ 'use strict'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER} from '../src/mediaTypes.js'; -import {config} from '../src/config.js'; -import {deepSetValue, getWindowSelf, replaceAuctionPrice, isArray, safeJSONParse, isPlainObject} from '../src/utils.js'; -import {getStorageManager} from '../src/storageManager.js'; -import {ajax} from '../src/ajax.js'; -import {ortbConverter} from '../libraries/ortbConverter/converter.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, NATIVE } from '../src/mediaTypes.js'; +import { config } from '../src/config.js'; +import { + deepSetValue, + getWinDimensions, + getWindowSelf, + isPlainObject, + replaceAuctionPrice, + safeJSONParse +} from '../src/utils.js'; +import { getStorageManager } from '../src/storageManager.js'; +import { ajax } from '../src/ajax.js'; +import { ortbConverter } from '../libraries/ortbConverter/converter.js'; +import { getConnectionType } from '../libraries/connectionInfo/connectionUtils.js'; +import { getViewportCoordinates } from '../libraries/viewport/viewport.js'; +import { percentInView } from '../libraries/percentInView/percentInView.js'; +import { getBoundingClientRect } from '../libraries/boundingClientRect/boundingClientRect.js'; +import { getAdUnitElement } from '../src/utils/adUnits.js'; const BIDDER_CODE = 'taboola'; const GVLID = 42; const CURRENCY = 'USD'; -export const END_POINT_URL = 'https://display.bidder.taboola.com/OpenRTB/TaboolaHB/auction'; +export const BANNER_ENDPOINT_URL = 'https://display.bidder.taboola.com/OpenRTB/TaboolaHB/auction'; +export const NATIVE_ENDPOINT_URL = 'https://native.bidder.taboola.com/OpenRTB/TaboolaHB/auction'; export const USER_SYNC_IMG_URL = 'https://trc.taboola.com/sg/prebidJS/1/cm'; export const USER_SYNC_IFRAME_URL = 'https://cdn.taboola.com/scripts/prebid_iframe_sync.html'; const USER_ID = 'user-id'; @@ -30,9 +43,9 @@ export const EVENT_ENDPOINT = 'https://beacon.bidder.taboola.com'; * 4. new user set it to 0 */ export const userData = { - storageManager: getStorageManager({bidderCode: BIDDER_CODE}), + storageManager: getStorageManager({ bidderCode: BIDDER_CODE }), getUserId: () => { - const {getFromLocalStorage, getFromCookie, getFromTRC} = userData; + const { getFromLocalStorage, getFromCookie, getFromTRC } = userData; try { return getFromLocalStorage() || getFromCookie() || getFromTRC(); @@ -41,7 +54,7 @@ export const userData = { } }, getFromCookie() { - const {cookiesAreEnabled, getCookie} = userData.storageManager; + const { cookiesAreEnabled, getCookie } = userData.storageManager; if (cookiesAreEnabled()) { const cookieData = getCookie(COOKIE_KEY); let userId; @@ -74,7 +87,7 @@ export const userData = { return value; }, getFromLocalStorage() { - const {hasLocalStorage, localStorageIsEnabled, getDataFromLocalStorage} = userData.storageManager; + const { hasLocalStorage, localStorageIsEnabled, getDataFromLocalStorage } = userData.storageManager; if (hasLocalStorage() && localStorageIsEnabled()) { return getDataFromLocalStorage(STORAGE_KEY); @@ -95,29 +108,104 @@ export const internal = { } } +export function detectBot() { + try { + return { + detected: !!( + window.__nightmare || + window.callPhantom || + window._phantom || + /HeadlessChrome/.test(navigator.userAgent) + ) + }; + } catch (e) { + return { detected: false }; + } +} + +export function getPageVisibility() { + try { + return { + hidden: document.hidden, + state: document.visibilityState, + hasFocus: document.hasFocus() + }; + } catch (e) { + return { hidden: false, state: 'visible', hasFocus: true }; + } +} + +export function getDeviceExtSignals(existingExt = {}) { + const viewport = getViewportCoordinates(); + return { + ...existingExt, + bot: detectBot(), + visibility: getPageVisibility(), + scroll: { + top: Math.round(viewport.top), + left: Math.round(viewport.left) + } + }; +} + +export function getElementSignals(bidRequest) { + try { + const element = getAdUnitElement(bidRequest); + if (!element) return null; + + const rect = getBoundingClientRect(element); + const winDimensions = getWinDimensions(); + const rawViewability = percentInView(element); + + const signals = { + placement: { + top: Math.round(rect.top), + left: Math.round(rect.left) + }, + fold: rect.top < winDimensions.innerHeight ? 'above' : 'below' + }; + + if (rawViewability !== null && !isNaN(rawViewability)) { + signals.viewability = Math.round(rawViewability); + } + + return signals; + } catch (e) { + return null; + } +} + const converter = ortbConverter({ context: { netRevenue: true, - mediaType: BANNER, ttl: 300 }, imp(buildImp, bidRequest, context) { const imp = buildImp(bidRequest, context); - fillTaboolaImpData(bidRequest, imp); + fillTaboolaImpData(bidRequest, imp, context); return imp; }, request(buildRequest, imps, bidderRequest, context) { const reqData = buildRequest(imps, bidderRequest, context); - fillTaboolaReqData(bidderRequest, context.bidRequests[0], reqData) + fillTaboolaReqData(bidderRequest, context.bidRequests[0], reqData, context); return reqData; }, bidResponse(buildBidResponse, bid, context) { + if (context.mediaType === NATIVE) { + const admObj = safeJSONParse(bid.adm); + if (admObj?.native) { + bid.adm = JSON.stringify(admObj.native); + } + } + const bidResponse = buildBidResponse(bid, context); bidResponse.nurl = bid.nurl; if (bid.burl) { bidResponse.burl = bid.burl; } - bidResponse.ad = replaceAuctionPrice(bid.adm, bid.price); + if (bidResponse.mediaType !== NATIVE) { + bidResponse.ad = replaceAuctionPrice(bid.adm, bid.price); + } if (bid.ext && bid.ext.dchain) { deepSetValue(bidResponse, 'meta.dchain', bid.ext.dchain); } @@ -126,30 +214,37 @@ const converter = ortbConverter({ }); export const spec = { - supportedMediaTypes: [BANNER], + supportedMediaTypes: [BANNER, NATIVE], gvlid: GVLID, code: BIDDER_CODE, isBidRequestValid: (bidRequest) => { - return !!(bidRequest.sizes && - bidRequest.params && + const hasPublisherAndTag = !!(bidRequest.params && bidRequest.params.publisherId && bidRequest.params.tagId); + if (!hasPublisherAndTag) { + return false; + } + const { hasBanner, hasNative } = getMediaType(bidRequest); + return hasBanner || hasNative; }, buildRequests: (validBidRequests, bidderRequest) => { - const [bidRequest] = validBidRequests; - const data = converter.toORTB({bidderRequest: bidderRequest, bidRequests: validBidRequests}); - const {publisherId} = bidRequest.params; - const url = END_POINT_URL + '?publisher=' + publisherId; + const bannerBids = []; + const nativeBids = []; - return { - url, - method: 'POST', - data: data, - bids: validBidRequests, - options: { - withCredentials: false - }, - }; + validBidRequests.forEach(bid => { + const { hasBanner, hasNative } = getMediaType(bid); + if (hasBanner) bannerBids.push(bid); + if (hasNative) nativeBids.push(bid); + }); + + const requests = []; + if (bannerBids.length) { + requests.push(createTaboolaRequest(bannerBids, bidderRequest, BANNER_ENDPOINT_URL, BANNER)); + } + if (nativeBids.length) { + requests.push(createTaboolaRequest(nativeBids, bidderRequest, NATIVE_ENDPOINT_URL, NATIVE)); + } + return requests; }, interpretResponse: (serverResponse, request) => { if (!request || !request.bids || !request.data) { @@ -160,57 +255,10 @@ export const spec = { return []; } const bids = []; - const fledgeAuctionConfigs = []; if (!serverResponse.body.seatbid || !serverResponse.body.seatbid.length || !serverResponse.body.seatbid[0].bid || !serverResponse.body.seatbid[0].bid.length) { - if (!serverResponse.body.ext || !serverResponse.body.ext.igbid || !serverResponse.body.ext.igbid.length) { - return []; - } + return []; } else { - bids.push(...converter.fromORTB({response: serverResponse.body, request: request.data}).bids); - } - if (isArray(serverResponse.body.ext?.igbid)) { - serverResponse.body.ext.igbid.forEach((igbid) => { - if (!igbid || !igbid.igbuyer || !igbid.igbuyer.length || !igbid.igbuyer[0].buyerdata) { - return; - } - const buyerdata = safeJSONParse(igbid.igbuyer[0]?.buyerdata) - if (!buyerdata) { - return; - } - const perBuyerSignals = {}; - igbid.igbuyer.forEach(buyerItem => { - if (!buyerItem || !buyerItem.buyerdata || !buyerItem.origin) { - return; - } - const parsedData = safeJSONParse(buyerItem.buyerdata) - if (!parsedData || !parsedData.perBuyerSignals || !(buyerItem.origin in parsedData.perBuyerSignals)) { - return; - } - perBuyerSignals[buyerItem.origin] = parsedData.perBuyerSignals[buyerItem.origin]; - }); - const impId = igbid?.impid; - fledgeAuctionConfigs.push({ - impId, - config: { - seller: buyerdata?.seller, - resolveToConfig: buyerdata?.resolveToConfig, - sellerSignals: {}, - sellerTimeout: buyerdata?.sellerTimeout, - perBuyerSignals, - auctionSignals: {}, - decisionLogicUrl: buyerdata?.decisionLogicUrl, - interestGroupBuyers: buyerdata?.interestGroupBuyers, - perBuyerTimeouts: buyerdata?.perBuyerTimeouts, - }, - }); - }); - } - - if (fledgeAuctionConfigs.length) { - return { - bids, - paapi: fledgeAuctionConfigs, - }; + bids.push(...converter.fromORTB({ response: serverResponse.body, request: request.data }).bids); } return bids; }, @@ -262,16 +310,38 @@ export const spec = { return syncs; }, onTimeout: (timeoutData) => { - ajax(EVENT_ENDPOINT + '/timeout', null, JSON.stringify(timeoutData), {method: 'POST'}); + ajax(EVENT_ENDPOINT + '/timeout', null, JSON.stringify(timeoutData), { method: 'POST' }); }, onBidderError: ({ error, bidderRequest }) => { - ajax(EVENT_ENDPOINT + '/bidError', null, JSON.stringify({error, bidderRequest}), {method: 'POST'}); + ajax(EVENT_ENDPOINT + '/bidError', null, JSON.stringify({ error, bidderRequest }), { method: 'POST' }); }, }; -function getSiteProperties({publisherId}, refererInfo, ortb2) { - const {getPageUrl, getReferrer} = internal; +function createTaboolaRequest(bidRequests, bidderRequest, endpointUrl, mediaType) { + const [bidRequest] = bidRequests; + const auctionId = bidderRequest.auctionId || bidRequests[0]?.auctionId; + const data = converter.toORTB({ + bidderRequest: bidderRequest, + bidRequests: bidRequests, + context: { auctionId, mediaType } + }); + const { publisherId } = bidRequest.params; + const url = endpointUrl + '?publisher=' + publisherId; + + return { + url, + method: 'POST', + data: data, + bids: bidRequests, + options: { + withCredentials: false + }, + }; +} + +function getSiteProperties({ publisherId }, refererInfo, ortb2) { + const { getPageUrl, getReferrer } = internal; return { id: publisherId, name: publisherId, @@ -287,10 +357,19 @@ function getSiteProperties({publisherId}, refererInfo, ortb2) { } } -function fillTaboolaReqData(bidderRequest, bidRequest, data) { - const {refererInfo, gdprConsent = {}, uspConsent} = bidderRequest; +function fillTaboolaReqData(bidderRequest, bidRequest, data, context) { + const { refererInfo, gdprConsent = {}, uspConsent } = bidderRequest; const site = getSiteProperties(bidRequest.params, refererInfo, bidderRequest.ortb2); - deepSetValue(data, 'device', bidderRequest?.ortb2?.device); + + const ortb2Device = bidderRequest?.ortb2?.device || {}; + const connectionType = getConnectionType(); + const device = { + ...ortb2Device, + js: 1, + ...(connectionType && { connectiontype: connectionType }), + ext: getDeviceExtSignals(ortb2Device.ext) + }; + deepSetValue(data, 'device', device); const extractedUserId = userData.getUserId(gdprConsent, uspConsent); if (data.user === undefined || data.user === null) { data.user = { @@ -340,16 +419,22 @@ function fillTaboolaReqData(bidderRequest, bidRequest, data) { data.wlang = ortb2.wlang || bidRequest.params.wlang || []; deepSetValue(data, 'ext.pageType', ortb2?.ext?.data?.pageType || ortb2?.ext?.data?.section || bidRequest.params.pageType); deepSetValue(data, 'ext.prebid.version', '$prebid.version$'); + const auctionId = context?.auctionId; + if (auctionId) { + deepSetValue(data, 'ext.prebid.auctionId', auctionId); + } } -function fillTaboolaImpData(bid, imp) { - const {tagId, position} = bid.params; - imp.banner = getBanners(bid, position); +function fillTaboolaImpData(bid, imp, context) { + const { tagId, position } = bid.params; + if (imp.banner && position) { + imp.banner.pos = position; + } imp.tagid = tagId; - if (typeof bid.getFloor === 'function') { const floorInfo = bid.getFloor({ currency: CURRENCY, + mediaType: context.mediaType, size: '*' }); if (isPlainObject(floorInfo) && floorInfo.currency === CURRENCY && !isNaN(parseFloat(floorInfo.floor))) { @@ -357,29 +442,44 @@ function fillTaboolaImpData(bid, imp) { imp.bidfloorcur = CURRENCY; } } else { - const {bidfloor = null, bidfloorcur = CURRENCY} = bid.params; + const { bidfloor = null, bidfloorcur = CURRENCY } = bid.params; imp.bidfloor = bidfloor; imp.bidfloorcur = bidfloorcur; } deepSetValue(imp, 'ext.gpid', bid?.ortb2Imp?.ext?.gpid); -} -function getBanners(bid, pos) { - return { - ...getSizes(bid.sizes), - pos: pos + if (bid.bidId) { + deepSetValue(imp, 'ext.prebid.bidId', bid.bidId); + } + if (bid.adUnitCode) { + deepSetValue(imp, 'ext.prebid.adUnitCode', bid.adUnitCode); + } + if (bid.adUnitId) { + deepSetValue(imp, 'ext.prebid.adUnitId', bid.adUnitId); + } + + deepSetValue(imp, 'ext.prebid.bidRequestsCount', bid.bidRequestsCount); + deepSetValue(imp, 'ext.prebid.bidderRequestsCount', bid.bidderRequestsCount); + deepSetValue(imp, 'ext.prebid.bidderWinsCount', bid.bidderWinsCount); + + const elementSignals = getElementSignals(bid); + if (elementSignals) { + if (elementSignals.viewability !== undefined) { + deepSetValue(imp, 'ext.viewability', elementSignals.viewability); + } + deepSetValue(imp, 'ext.placement', elementSignals.placement); + deepSetValue(imp, 'ext.fold', elementSignals.fold); } } -function getSizes(sizes) { +function getMediaType(bidRequest) { + const hasBanner = !!bidRequest?.mediaTypes?.banner?.sizes; + const hasNative = !!bidRequest?.mediaTypes?.native; return { - format: sizes.map(size => { - return { - w: size[0], - h: size[1] - } - }) - } + hasBanner, + hasNative, + mediaType: hasNative && !hasBanner ? NATIVE : BANNER + }; } registerBidder(spec); diff --git a/modules/taboolaIdSystem.js b/modules/taboolaIdSystem.js index a370af9752f..64b989e7159 100644 --- a/modules/taboolaIdSystem.js +++ b/modules/taboolaIdSystem.js @@ -4,12 +4,12 @@ * @requires module:modules/userId */ -import {submodule} from '../src/hook.js'; -import {ajax} from '../src/ajax.js'; -import {getStorageManager} from '../src/storageManager.js'; -import {logError} from '../src/utils.js'; -import {gdprDataHandler, gppDataHandler, uspDataHandler} from '../src/adapterManager.js'; -import {MODULE_TYPE_UID} from '../src/activities/modules.js'; +import { submodule } from '../src/hook.js'; +import { ajax } from '../src/ajax.js'; +import { getStorageManager } from '../src/storageManager.js'; +import { logError } from '../src/utils.js'; +import { gdprDataHandler, gppDataHandler, uspDataHandler } from '../src/adapterManager.js'; +import { MODULE_TYPE_UID } from '../src/activities/modules.js'; /** * The Taboola sync endpoint. @@ -46,7 +46,7 @@ const userData = { }, getFromLocalStorage() { - const {hasLocalStorage, localStorageIsEnabled, getDataFromLocalStorage} = sm; + const { hasLocalStorage, localStorageIsEnabled, getDataFromLocalStorage } = sm; if (hasLocalStorage() && localStorageIsEnabled()) { return getDataFromLocalStorage(STORAGE_KEY); } @@ -54,7 +54,7 @@ const userData = { }, getFromCookie() { - const {cookiesAreEnabled, getCookie} = sm; + const { cookiesAreEnabled, getCookie } = sm; if (cookiesAreEnabled()) { const mainCookieData = getCookie(COOKIE_KEY); if (mainCookieData) { @@ -168,7 +168,7 @@ function saveUserIdInLocalStorage(id) { function callTaboolaUserSync(submoduleConfig, currentId, callback) { const skipSync = submoduleConfig?.params?.shouldSkipSync ?? true; if (skipSync) { - callback(currentId ? {taboolaId: currentId} : undefined); + callback(currentId ? { taboolaId: currentId } : undefined); return; } const syncUrl = buildTaboolaSyncUrl(); @@ -180,21 +180,21 @@ function callTaboolaUserSync(submoduleConfig, currentId, callback) { const data = JSON.parse(response); if (data && data.user && data.user.id) { saveUserIdInLocalStorage(data.user.id); - callback(data.user.id ? {taboolaId: data.user.id} : undefined); + callback(data.user.id ? { taboolaId: data.user.id } : undefined); return; } } catch (err) { logError('Taboola user-sync: error parsing JSON response', err); } - callback(currentId ? {taboolaId: currentId} : undefined); + callback(currentId ? { taboolaId: currentId } : undefined); }, error: (err) => { logError('Taboola user-sync: network/endpoint error', err); - callback(currentId ? {taboolaId: currentId} : undefined); + callback(currentId ? { taboolaId: currentId } : undefined); } }, undefined, - {method: 'GET', withCredentials: true} + { method: 'GET', withCredentials: true } ); } diff --git a/modules/tadvertisingBidAdapter.js b/modules/tadvertisingBidAdapter.js index 2db4076f57f..f2bc9af4fe5 100644 --- a/modules/tadvertisingBidAdapter.js +++ b/modules/tadvertisingBidAdapter.js @@ -10,11 +10,11 @@ import { isPlainObject, isInteger } from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER, VIDEO} from "../src/mediaTypes.js"; -import {ortbConverter} from '../libraries/ortbConverter/converter.js'; -import {hasPurpose1Consent} from '../src/utils/gdpr.js'; -import {ajax, sendBeacon} from "../src/ajax.js"; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, VIDEO } from "../src/mediaTypes.js"; +import { ortbConverter } from '../libraries/ortbConverter/converter.js'; +import { hasPurpose1Consent } from '../src/utils/gdpr.js'; +import { ajax, sendBeacon } from "../src/ajax.js"; const BIDDER_CODE = 'tadvertising'; const GVL_ID = 213; @@ -185,7 +185,7 @@ export const spec = { }, buildRequests: function (validBidRequests, bidderRequest) { - let data = converter.toORTB({validBidRequests, bidderRequest}) + let data = converter.toORTB({ validBidRequests, bidderRequest }) deepSetValue(data, 'site.publisher.id', bidderRequest.bids[0].params.publisherId) const bidFloor = getBidFloor(bidderRequest.bids[0]) @@ -215,7 +215,7 @@ export const spec = { } deepSetValue(response, 'body.seatbid.0.bid.0.impid', deepAccess(serverRequest, 'data.imp.0.id')) - const bids = converter.fromORTB({response: response.body, request: serverRequest.data}).bids; + const bids = converter.fromORTB({ response: response.body, request: serverRequest.data }).bids; bids.forEach(bid => { bid.ttl = BID_TTL; @@ -270,7 +270,7 @@ export const spec = { sendNotification(spec.notify_url, 'timeout', payload) }, - onBidderError: function ({error, bidderRequest}) { + onBidderError: function ({ error, bidderRequest }) { const payload = buildErrorNotification(bidderRequest, error) sendNotification(spec.notify_url, 'error', payload) } diff --git a/modules/tagorasBidAdapter.js b/modules/tagorasBidAdapter.js index 6ce54a5a895..4e8132de3ff 100644 --- a/modules/tagorasBidAdapter.js +++ b/modules/tagorasBidAdapter.js @@ -1,6 +1,6 @@ -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER, VIDEO} from '../src/mediaTypes.js'; -import {getStorageManager} from '../src/storageManager.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; +import { getStorageManager } from '../src/storageManager.js'; import { createBuildRequestsFn, createInterpretResponseFn, @@ -11,7 +11,7 @@ import { const DEFAULT_SUB_DOMAIN = 'exchange'; const BIDDER_CODE = 'tagoras'; const BIDDER_VERSION = '1.0.0'; -export const storage = getStorageManager({bidderCode: BIDDER_CODE}); +export const storage = getStorageManager({ bidderCode: BIDDER_CODE }); export function createDomain(subDomain = DEFAULT_SUB_DOMAIN) { return `https://${subDomain}.tagoras.io`; diff --git a/modules/talkadsBidAdapter.js b/modules/talkadsBidAdapter.js index 60be578ed6e..15376d83731 100644 --- a/modules/talkadsBidAdapter.js +++ b/modules/talkadsBidAdapter.js @@ -1,7 +1,7 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { NATIVE, BANNER } from '../src/mediaTypes.js'; import * as utils from '../src/utils.js'; -import {ajax} from '../src/ajax.js'; +import { ajax } from '../src/ajax.js'; import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; const CURRENCY = 'EUR'; @@ -9,7 +9,7 @@ const BIDDER_CODE = 'talkads'; export const spec = { code: BIDDER_CODE, - supportedMediaTypes: [ NATIVE, BANNER ], + supportedMediaTypes: [NATIVE, BANNER], /** * Determines whether or not the given bid request is valid. diff --git a/modules/tappxBidAdapter.js b/modules/tappxBidAdapter.js index 6ca3adb4d54..266a0186e30 100644 --- a/modules/tappxBidAdapter.js +++ b/modules/tappxBidAdapter.js @@ -1,12 +1,12 @@ 'use strict'; -import {getDNT} from '../libraries/dnt/index.js'; import { logWarn, deepAccess, isFn, isPlainObject, isBoolean, isNumber, isStr, isArray } from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; import { config } from '../src/config.js'; import { Renderer } from '../src/Renderer.js'; import { parseDomain } from '../src/refererDetection.js'; +import { getDNT } from '../libraries/dnt/index.js'; /** * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest @@ -330,7 +330,7 @@ function buildOneRequest(validBidRequests, bidderRequest) { banner.api = api; - const formatArr = bannerMediaType.sizes.map(size => ({w: size[0], h: size[1]})) + const formatArr = bannerMediaType.sizes.map(size => ({ w: size[0], h: size[1] })) banner.format = Object.assign({}, formatArr); imp.banner = banner; @@ -622,7 +622,7 @@ export function _checkParamDataType(key, value, datatype) { export function _extractPageUrl(validBidRequests, bidderRequest) { const url = bidderRequest?.refererInfo?.page || bidderRequest?.refererInfo?.topmostLocation; - return parseDomain(url, {noLeadingWww: true}); + return parseDomain(url, { noLeadingWww: true }); } registerBidder(spec); diff --git a/modules/targetVideoAdServerVideo.js b/modules/targetVideoAdServerVideo.js index b38da6c9d2b..8b5bccfb7b9 100644 --- a/modules/targetVideoAdServerVideo.js +++ b/modules/targetVideoAdServerVideo.js @@ -38,9 +38,9 @@ export function buildVideoUrl(options) { const iu = options.params.iu; if (isURL.test(iu)) { - const urlComponents = parseUrl(iu, {noDecodeWholeURL: true}); + const urlComponents = parseUrl(iu, { noDecodeWholeURL: true }); - for (const [key, value] of Object.entries({...allTargetingData, ...bid.adserverTargeting, ...defaultParameters})) { + for (const [key, value] of Object.entries({ ...allTargetingData, ...bid.adserverTargeting, ...defaultParameters })) { if (!urlComponents.search.hasOwnProperty(key)) { urlComponents.search[key] = value; } diff --git a/modules/targetVideoBidAdapter.js b/modules/targetVideoBidAdapter.js index 84730231543..e2ddad17ac6 100644 --- a/modules/targetVideoBidAdapter.js +++ b/modules/targetVideoBidAdapter.js @@ -1,14 +1,30 @@ -import {_each, deepAccess, getDefinedParams, parseGPTSingleSizeArrayToRtbSize} from '../src/utils.js'; -import {BANNER, VIDEO} from '../src/mediaTypes.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {formatRequest, getRtbBid, getSiteObj, getSyncResponse, videoBid, bannerBid, createVideoTag} from '../libraries/targetVideoUtils/bidderUtils.js'; -import {SOURCE, GVLID, BIDDER_CODE, VIDEO_PARAMS, BANNER_ENDPOINT_URL, VIDEO_ENDPOINT_URL, MARGIN, TIME_TO_LIVE} from '../libraries/targetVideoUtils/constants.js'; +import { _each, deepAccess, getDefinedParams, isFn, isPlainObject, parseGPTSingleSizeArrayToRtbSize } from '../src/utils.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { formatRequest, getRtbBid, getSiteObj, getSyncResponse, videoBid, bannerBid, createVideoTag } from '../libraries/targetVideoUtils/bidderUtils.js'; +import { SOURCE, GVLID, BIDDER_CODE, VIDEO_PARAMS, BANNER_ENDPOINT_URL, VIDEO_ENDPOINT_URL, MARGIN, TIME_TO_LIVE } from '../libraries/targetVideoUtils/constants.js'; /** * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid */ +function getBidFloor(bid) { + if (!isFn(bid.getFloor)) { + return (bid.params.floor) ? bid.params.floor : null; + } + + const floor = bid.getFloor({ + currency: 'EUR', + mediaType: '*', + size: '*' + }); + if (isPlainObject(floor) && !isNaN(floor.floor) && floor.currency === 'EUR') { + return floor.floor; + } + return null; +} + export const spec = { code: BIDDER_CODE, @@ -38,13 +54,15 @@ export const spec = { version: '$prebid.version$' }; - for (let {params, bidId, sizes, mediaTypes, ...bid} of bidRequests) { + for (let { bidId, sizes, mediaTypes, ...bid } of bidRequests) { for (const mediaType in mediaTypes) { switch (mediaType) { case VIDEO: { + const params = bid.params; const video = mediaTypes[VIDEO]; const placementId = params.placementId; const site = getSiteObj(); + const floor = getBidFloor(bid); if (sizes && !Array.isArray(sizes[0])) sizes = [sizes]; @@ -71,6 +89,12 @@ export const spec = { video: getDefinedParams(video, VIDEO_PARAMS) } + const bidFloor = typeof floor === 'string' ? Number(floor.trim()) + : typeof floor === 'number' ? floor + : NaN; + + if (Number.isFinite(bidFloor) && bidFloor > 0) imp.bidfloor = bidFloor; + if (video.playerSize) { imp.video = Object.assign( imp.video, parseGPTSingleSizeArrayToRtbSize(video.playerSize[0]) || {} @@ -120,7 +144,7 @@ export const spec = { }; } - const {ortb2} = bid; + const { ortb2 } = bid; if (ortb2?.source?.tid) { if (!payload.source) { diff --git a/modules/targetVideoBidAdapter.md b/modules/targetVideoBidAdapter.md index a34ad0aff27..9a204a86991 100644 --- a/modules/targetVideoBidAdapter.md +++ b/modules/targetVideoBidAdapter.md @@ -43,6 +43,7 @@ var adUnits = [ bidder: 'targetVideo', params: { placementId: 12345, + floor: 2, reserve: 0, } }] diff --git a/modules/tcfControl.ts b/modules/tcfControl.ts index 81d5df3d802..d49e34e31a8 100644 --- a/modules/tcfControl.ts +++ b/modules/tcfControl.ts @@ -2,12 +2,12 @@ * This module gives publishers extra set of features to enforce individual purposes of TCF v2 */ -import {deepAccess, logError, logWarn} from '../src/utils.js'; -import {config} from '../src/config.js'; -import adapterManager, {gdprDataHandler} from '../src/adapterManager.js'; +import { deepAccess, logError, logWarn } from '../src/utils.js'; +import { config } from '../src/config.js'; +import adapterManager, { gdprDataHandler } from '../src/adapterManager.js'; import * as events from '../src/events.js'; -import {EVENTS} from '../src/constants.js'; -import {GDPR_GVLIDS, VENDORLESS_GVLID} from '../src/consentHandler.js'; +import { EVENTS } from '../src/constants.js'; +import { GDPR_GVLIDS, VENDORLESS_GVLID } from '../src/consentHandler.js'; import { MODULE_TYPE_ANALYTICS, MODULE_TYPE_BIDDER, @@ -20,7 +20,7 @@ import { ACTIVITY_PARAM_COMPONENT_NAME, ACTIVITY_PARAM_COMPONENT_TYPE } from '../src/activities/params.js'; -import {registerActivityControl} from '../src/activities/rules.js'; +import { registerActivityControl } from '../src/activities/rules.js'; import { ACTIVITY_ACCESS_DEVICE, ACTIVITY_ACCESS_REQUEST_CREDENTIALS, @@ -33,7 +33,7 @@ import { ACTIVITY_TRANSMIT_PRECISE_GEO, ACTIVITY_TRANSMIT_UFPD } from '../src/activities/activities.js'; -import {processRequestOptions} from '../src/ajax.js'; +import { processRequestOptions } from '../src/ajax.js'; export const STRICT_STORAGE_ENFORCEMENT = 'strictStorageEnforcement'; @@ -149,7 +149,7 @@ export function getGvlid(moduleType, moduleName, fallbackFn) { } else if (moduleType === MODULE_TYPE_PREBID) { return VENDORLESS_GVLID; } else { - let {gvlid, modules} = GDPR_GVLIDS.get(moduleName); + let { gvlid, modules } = GDPR_GVLIDS.get(moduleName); if (gvlid == null && Object.keys(modules).length > 0) { // this behavior is for backwards compatibility; if multiple modules with the same // name declare different GVL IDs, pick the bidder's first, then userId, then analytics @@ -238,7 +238,7 @@ export function validateRules(rule, consentData, currentModule, gvlId, params = } const vendorConsentRequred = rule.enforceVendor && !((gvlId === VENDORLESS_GVLID || (rule.softVendorExceptions || []).includes(currentModule))); const deferS2Sbidders = params['isS2S'] && rule.purpose === 'basicAds' && rule.deferS2Sbidders && !gvlId; - const {purpose, vendor} = getConsent(consentData, ruleOptions.type, ruleOptions.id, gvlId); + const { purpose, vendor } = getConsent(consentData, ruleOptions.type, ruleOptions.id, gvlId); return (!rule.enforcePurpose || purpose) && (!vendorConsentRequred || deferS2Sbidders || vendor); } @@ -252,7 +252,7 @@ function gdprRule(purposeNo, checkConsent, blocked = null, gvlidFallback: any = const allow = !!checkConsent(consentData, modName, gvlid, params); if (!allow) { blocked && blocked.add(modName); - return {allow}; + return { allow }; } } }; @@ -297,7 +297,7 @@ export const transmitEidsRule = exceptPrebidModules((() => { if (ACTIVE_RULES.purpose[pno]?.vendorExceptions?.includes(modName)) { return true; } - const {purpose, vendor} = getConsent(consentData, 'purpose', pno, gvlId); + const { purpose, vendor } = getConsent(consentData, 'purpose', pno, gvlId); if (purpose && (vendor || ACTIVE_RULES.purpose[pno]?.softVendorExceptions?.includes(modName))) { return true; } @@ -433,7 +433,7 @@ export function checkIfCredentialsAllowed(next, options: { withCredentials?: boo const consentData = gdprDataHandler.getConsentData(); const rule = ACTIVE_RULES.purpose[1]; const ruleOptions = CONFIGURABLE_RULES[rule.purpose]; - const {purpose} = getConsent(consentData, ruleOptions.type, ruleOptions.id, null); + const { purpose } = getConsent(consentData, ruleOptions.type, ruleOptions.id, null); if (!purpose && rule.enforcePurpose) { options.withCredentials = false; @@ -444,7 +444,7 @@ export function checkIfCredentialsAllowed(next, options: { withCredentials?: boo export function uninstall() { while (RULE_HANDLES.length) RULE_HANDLES.pop()(); - processRequestOptions.getHooks({hook: checkIfCredentialsAllowed}).remove(); + processRequestOptions.getHooks({ hook: checkIfCredentialsAllowed }).remove(); hooksAdded = false; } diff --git a/modules/teadsBidAdapter.js b/modules/teadsBidAdapter.js index dbdda501658..4e140cb0913 100644 --- a/modules/teadsBidAdapter.js +++ b/modules/teadsBidAdapter.js @@ -1,10 +1,10 @@ -import {logError, parseSizesInput, isArray, getBidIdParameter, getWinDimensions, getScreenOrientation} from '../src/utils.js'; -import {getDevicePixelRatio} from '../libraries/devicePixelRatio/devicePixelRatio.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {getStorageManager} from '../src/storageManager.js'; -import {isAutoplayEnabled} from '../libraries/autoplayDetection/autoplay.js'; -import {getHLen} from '../libraries/navigatorData/navigatorData.js'; -import {getTimeToFirstByte} from '../libraries/timeToFirstBytesUtils/timeToFirstBytesUtils.js'; +import { logError, parseSizesInput, isArray, getBidIdParameter, getWinDimensions, getScreenOrientation } from '../src/utils.js'; +import { getDevicePixelRatio } from '../libraries/devicePixelRatio/devicePixelRatio.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { getStorageManager } from '../src/storageManager.js'; +import { isAutoplayEnabled } from '../libraries/autoplayDetection/autoplay.js'; +import { getHLen } from '../libraries/navigatorData/navigatorData.js'; +import { getTimeToFirstByte } from '../libraries/timeToFirstBytesUtils/timeToFirstBytesUtils.js'; import { getConnectionInfo } from '../libraries/connectionInfo/connectionUtils.js'; /** @@ -25,7 +25,7 @@ const gdprStatus = { const FP_TEADS_ID_COOKIE_NAME = '_tfpvi'; const OB_USER_TOKEN_KEY = 'OB-USER-TOKEN'; -export const storage = getStorageManager({bidderCode: BIDDER_CODE}); +export const storage = getStorageManager({ bidderCode: BIDDER_CODE }); export const spec = { code: BIDDER_CODE, @@ -201,7 +201,6 @@ function getSharedViewerIdParameters(validBidRequests) { id5Id: 'id5-sync.com', // id5IdSystem criteoId: 'criteo.com', // criteoIdSystem yahooConnectId: 'yahoo.com', // connectIdSystem - quantcastId: 'quantcast.com', // quantcastIdSystem epsilonPublisherLinkId: 'epsilon.com', // publinkIdSystem publisherFirstPartyViewerId: 'pubcid.org', // sharedIdSystem merkleId: 'merkleinc.com', // merkleIdSystem @@ -346,14 +345,14 @@ function getFirstPartyTeadsIdParameter(validBidRequests) { const firstPartyTeadsIdFromUserIdModule = validBidRequests?.[0]?.userIdAsEids?.find(eid => eid.source === 'teads.com')?.uids?.[0].id; if (firstPartyTeadsIdFromUserIdModule) { - return {firstPartyCookieTeadsId: firstPartyTeadsIdFromUserIdModule}; + return { firstPartyCookieTeadsId: firstPartyTeadsIdFromUserIdModule }; } if (storage.cookiesAreEnabled(null)) { const firstPartyTeadsIdFromCookie = storage.getCookie(FP_TEADS_ID_COOKIE_NAME, null); if (firstPartyTeadsIdFromCookie) { - return {firstPartyCookieTeadsId: firstPartyTeadsIdFromCookie}; + return { firstPartyCookieTeadsId: firstPartyTeadsIdFromCookie }; } } diff --git a/modules/teadsIdSystem.js b/modules/teadsIdSystem.js index 9c9b07cf355..b7703630116 100644 --- a/modules/teadsIdSystem.js +++ b/modules/teadsIdSystem.js @@ -5,11 +5,11 @@ * @requires module:modules/userId */ -import {isStr, isNumber, logError, logInfo, isEmpty, timestamp} from '../src/utils.js' -import {ajax} from '../src/ajax.js'; -import {submodule} from '../src/hook.js'; -import {getStorageManager} from '../src/storageManager.js'; -import {MODULE_TYPE_UID} from '../src/activities/modules.js'; +import { isStr, isNumber, logError, logInfo, isEmpty, timestamp } from '../src/utils.js' +import { ajax } from '../src/ajax.js'; +import { submodule } from '../src/hook.js'; +import { getStorageManager } from '../src/storageManager.js'; +import { MODULE_TYPE_UID } from '../src/activities/modules.js'; /** * @typedef {import('../modules/userId/index.js').Submodule} Submodule @@ -35,7 +35,7 @@ export const gdprReason = { GDPR_APPLIES_PUBLISHER_CLASSIC: 120, }; -export const storage = getStorageManager({moduleType: MODULE_TYPE_UID, moduleName: MODULE_NAME}); +export const storage = getStorageManager({ moduleType: MODULE_TYPE_UID, moduleName: MODULE_NAME }); /** @type {Submodule} */ export const teadsIdSubmodule = { @@ -56,7 +56,7 @@ export const teadsIdSubmodule = { * @returns {{teadsId:string}} */ decode(value) { - return {teadsId: value} + return { teadsId: value } }, /** * performs action to obtain id and return a value in the callback's response argument @@ -92,9 +92,9 @@ export const teadsIdSubmodule = { } }; - ajax(url, callbacks, undefined, {method: 'GET'}); + ajax(url, callbacks, undefined, { method: 'GET' }); }; - return {callback: resp}; + return { callback: resp }; }, eids: { teadsId: { diff --git a/modules/tealBidAdapter.js b/modules/tealBidAdapter.js index 4374646b102..0cac4b7c243 100644 --- a/modules/tealBidAdapter.js +++ b/modules/tealBidAdapter.js @@ -1,8 +1,8 @@ -import {deepSetValue, deepAccess, triggerPixel, deepClone, isEmpty, logError, shuffle} from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {ortbConverter} from '../libraries/ortbConverter/converter.js' -import {BANNER} from '../src/mediaTypes.js'; -import {pbsExtensions} from '../libraries/pbsExtensions/pbsExtensions.js' +import { deepSetValue, deepAccess, triggerPixel, deepClone, isEmpty, logError, shuffle } from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { ortbConverter } from '../libraries/ortbConverter/converter.js' +import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; +import { pbsExtensions } from '../libraries/pbsExtensions/pbsExtensions.js' const BIDDER_CODE = 'teal'; const GVLID = 1378; const DEFAULT_ENDPOINT = 'https://a.bids.ws/openrtb2/auction'; @@ -43,7 +43,7 @@ const converter = ortbConverter({ export const spec = { code: BIDDER_CODE, gvlid: GVLID, - supportedMediaTypes: [BANNER], + supportedMediaTypes: [BANNER, NATIVE, VIDEO], aliases: [], isBidRequestValid: function(bid) { @@ -52,7 +52,7 @@ export const spec = { buildRequests: function(bidRequests, bidderRequest) { const { bidder } = bidRequests[0]; - const data = converter.toORTB({bidRequests, bidderRequest}); + const data = converter.toORTB({ bidRequests, bidderRequest }); const account = deepAccess(bidRequests[0], 'params.account', null); const subAccount = deepAccess(bidRequests[0], 'params.subAccount', null); deepSetValue(data, 'site.publisher.id', account); @@ -79,10 +79,10 @@ export const spec = { Object.entries(modifiers).forEach(([field, combineFn]) => { const obj = resp.ext?.[field]; if (!isEmpty(obj)) { - resp.ext[field] = {[bidder]: combineFn(Object.values(obj))}; + resp.ext[field] = { [bidder]: combineFn(Object.values(obj)) }; } }); - const bids = converter.fromORTB({response: resp, request: request.data}).bids; + const bids = converter.fromORTB({ response: resp, request: request.data }).bids; return bids; }, diff --git a/modules/teqBlazeSalesAgentBidAdapter.js b/modules/teqBlazeSalesAgentBidAdapter.js new file mode 100644 index 00000000000..f2cbf2d57db --- /dev/null +++ b/modules/teqBlazeSalesAgentBidAdapter.js @@ -0,0 +1,41 @@ +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; +import { + buildPlacementProcessingFunction, + buildRequestsBase, + interpretResponse, + isBidRequestValid +} from '../libraries/teqblazeUtils/bidderUtils.js'; + +const BIDDER_CODE = 'teqBlazeSalesAgent'; +const AD_URL = 'https://be-agent.teqblaze.io/pbjs'; + +const addCustomFieldsToPlacement = (bid, bidderRequest, placement) => { + const aeeSignals = bidderRequest.ortb2?.site?.ext?.data?.scope3_aee; + + if (aeeSignals) { + placement.axei = aeeSignals.include; + placement.axex = aeeSignals.exclude; + + if (aeeSignals.macro) { + placement.axem = aeeSignals.macro; + } + } +}; + +const placementProcessingFunction = buildPlacementProcessingFunction({ addCustomFieldsToPlacement }); + +const buildRequests = (validBidRequests = [], bidderRequest = {}) => { + return buildRequestsBase({ adUrl: AD_URL, validBidRequests, bidderRequest, placementProcessingFunction }); +}; + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [BANNER, VIDEO, NATIVE], + + isBidRequestValid: isBidRequestValid(['placementId']), + buildRequests, + interpretResponse +}; + +registerBidder(spec); diff --git a/modules/teqBlazeSalesAgentBidAdapter.md b/modules/teqBlazeSalesAgentBidAdapter.md new file mode 100644 index 00000000000..d0c1475643d --- /dev/null +++ b/modules/teqBlazeSalesAgentBidAdapter.md @@ -0,0 +1,79 @@ +# Overview + +``` +Module Name: TeqBlaze Sales Agent Bidder Adapter +Module Type: TeqBlaze Sales Agent Bidder Adapter +Maintainer: support@teqblaze.com +``` + +# Description + +Connects to TeqBlaze Sales Agent for bids. +TeqBlaze Sales Agent bid adapter supports Banner, Video (instream and outstream) and Native. + +# Test Parameters +``` + var adUnits = [ + // Will return static test banner + { + code: 'adunit1', + mediaTypes: { + banner: { + sizes: [ [300, 250], [320, 50] ], + } + }, + bids: [ + { + bidder: 'teqBlazeSalesAgent', + params: { + placementId: 'testBanner', + } + } + ] + }, + { + code: 'addunit2', + mediaTypes: { + video: { + playerSize: [ [640, 480] ], + context: 'instream', + minduration: 5, + maxduration: 60, + } + }, + bids: [ + { + bidder: 'teqBlazeSalesAgent', + params: { + placementId: 'testVideo', + } + } + ] + }, + { + code: 'addunit3', + mediaTypes: { + native: { + title: { + required: true + }, + body: { + required: true + }, + icon: { + required: true, + size: [64, 64] + } + } + }, + bids: [ + { + bidder: 'teqBlazeSalesAgent', + params: { + placementId: 'testNative', + } + } + ] + } + ]; +``` diff --git a/modules/terceptAnalyticsAdapter.js b/modules/terceptAnalyticsAdapter.js index 6bc86ba8c98..4d4cdf6ff4e 100644 --- a/modules/terceptAnalyticsAdapter.js +++ b/modules/terceptAnalyticsAdapter.js @@ -28,7 +28,7 @@ var terceptAnalyticsAdapter = Object.assign(adapter( if (eventType === EVENTS.BID_TIMEOUT) { args.forEach(item => { mapBidResponse(item, 'timeout'); }); } else if (eventType === EVENTS.AUCTION_INIT) { - Object.assign(events, {bids: []}); + Object.assign(events, { bids: [] }); events.auctionInit = args; auctionTimestamp = args.timestamp; adUnitMap.set(args.auctionId, args.adUnits); diff --git a/modules/theAdxBidAdapter.js b/modules/theAdxBidAdapter.js index d57e307c7e1..bac8645eb0b 100644 --- a/modules/theAdxBidAdapter.js +++ b/modules/theAdxBidAdapter.js @@ -1,4 +1,3 @@ -import {getDNT} from '../libraries/dnt/index.js'; import { logInfo, isEmpty, deepAccess, parseUrl, parseSizesInput, _map } from '../src/utils.js'; import { BANNER, @@ -10,6 +9,7 @@ import { } from '../src/adapters/bidderFactory.js'; import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; import { getConnectionInfo } from '../libraries/connectionInfo/connectionUtils.js'; +import { getDNT } from '../libraries/dnt/index.js'; /** * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest diff --git a/modules/tncIdSystem.js b/modules/tncIdSystem.js index 44fb7a1182f..fafbb9d1700 100644 --- a/modules/tncIdSystem.js +++ b/modules/tncIdSystem.js @@ -26,7 +26,7 @@ const TNC_PREBIDJS_PROVIDER_ID = 'c8549079-f149-4529-a34b-3fa91ef257d1'; const TNC_LOCAL_VALUE_KEY = 'tncid'; let moduleConfig = null; -export const storage = getStorageManager({moduleType: MODULE_TYPE_UID, moduleName: MODULE_NAME}); +export const storage = getStorageManager({ moduleType: MODULE_TYPE_UID, moduleName: MODULE_NAME }); function fixURL(config, ns) { config.params = (config && config.params) ? config.params : {}; diff --git a/modules/topLevelPaapi.js b/modules/topLevelPaapi.js deleted file mode 100644 index 960d7858117..00000000000 --- a/modules/topLevelPaapi.js +++ /dev/null @@ -1,214 +0,0 @@ -import {submodule} from '../src/hook.js'; -import {config} from '../src/config.js'; -import {logError, logInfo, logWarn, mergeDeep} from '../src/utils.js'; -import {auctionStore} from '../libraries/weakStore/weakStore.js'; -import {getGlobal} from '../src/prebidGlobal.js'; -import {emit} from '../src/events.js'; -import {BID_STATUS, EVENTS} from '../src/constants.js'; -import {PbPromise} from '../src/utils/promise.js'; -import {getBidToRender, getRenderingData, markWinningBid} from '../src/adRendering.js'; - -let getPAAPIConfig, expandFilters, moduleConfig; - -const paapiBids = auctionStore(); -const MODULE_NAME = 'topLevelPaapi'; - -config.getConfig('paapi', (cfg) => { - moduleConfig = cfg.paapi?.topLevelSeller; - if (moduleConfig) { - getBidToRender.before(renderPaapiHook); - getBidToRender.after(renderOverrideHook); - getRenderingData.before(getRenderingDataHook); - markWinningBid.before(markWinningBidHook); - } else { - getBidToRender.getHooks({hook: renderPaapiHook}).remove(); - getBidToRender.getHooks({hook: renderOverrideHook}).remove(); - getRenderingData.getHooks({hook: getRenderingDataHook}).remove(); - markWinningBid.getHooks({hook: markWinningBidHook}).remove(); - } -}); - -function isPaapiBid(bid) { - return bid?.source === 'paapi'; -} - -function bidIfRenderable(bid) { - if (bid && !bid.urn) { - logWarn(MODULE_NAME, 'rendering in fenced frames is not supported. Consider using resolveToConfig: false', bid); - return; - } - return bid; -} - -const forRenderStack = []; - -function renderPaapiHook(next, adId, forRender = true, override = PbPromise.resolve()) { - forRenderStack.push(forRender); - const ids = parsePaapiAdId(adId); - if (ids) { - override = override.then((bid) => { - if (bid) return bid; - const [auctionId, adUnitCode] = ids; - return paapiBids(auctionId)?.[adUnitCode]?.then(bid => { - if (!bid) { - logWarn(MODULE_NAME, `No PAAPI bid found for auctionId: "${auctionId}", adUnit: "${adUnitCode}"`); - } - return bidIfRenderable(bid); - }); - }); - } - next(adId, forRender, override); -} - -function renderOverrideHook(next, bidPm) { - const forRender = forRenderStack.pop(); - if (moduleConfig?.overrideWinner) { - bidPm = bidPm.then((bid) => { - if (isPaapiBid(bid) || bid?.status === BID_STATUS.RENDERED) return bid; - return getPAAPIBids({adUnitCode: bid.adUnitCode}).then(res => { - const paapiBid = bidIfRenderable(res[bid.adUnitCode]); - if (paapiBid) { - if (!forRender) return paapiBid; - if (forRender && paapiBid.status !== BID_STATUS.RENDERED) { - paapiBid.overriddenAdId = bid.adId; - logInfo(MODULE_NAME, 'overriding contextual bid with PAAPI bid', bid, paapiBid) - return paapiBid; - } - } - return bid; - }); - }); - } - next(bidPm); -} - -export function getRenderingDataHook(next, bid, options) { - if (isPaapiBid(bid)) { - next.bail({ - width: bid.width, - height: bid.height, - adUrl: bid.urn - }); - } else { - next(bid, options); - } -} - -export function markWinningBidHook(next, bid) { - if (isPaapiBid(bid)) { - emit(EVENTS.BID_WON, bid); - next.bail(); - } else { - next(bid); - } -} - -function getBaseAuctionConfig() { - if (moduleConfig?.auctionConfig) { - return Object.assign({ - resolveToConfig: false - }, moduleConfig.auctionConfig); - } -} - -function onAuctionConfig(auctionId, auctionConfigs) { - const base = getBaseAuctionConfig(); - if (base) { - Object.entries(auctionConfigs).forEach(([adUnitCode, auctionConfig]) => { - mergeDeep(auctionConfig, base); - if (moduleConfig.autorun ?? true) { - getPAAPIBids({adUnitCode, auctionId}); - } - }); - } -} - -export function parsePaapiSize(size) { - /* From https://github.com/WICG/turtledove/blob/main/FLEDGE.md#12-interest-group-attributes: - * Each size has the format {width: widthVal, height: heightVal}, - * where the values can have either pixel units (e.g. 100 or '100px') or screen dimension coordinates (e.g. 100sw or 100sh). - */ - if (typeof size === 'number') return size; - if (typeof size === 'string') { - const px = /^(\d+)(px)?$/.exec(size)?.[1]; - if (px) { - return parseInt(px, 10); - } - } - return null; -} - -export function getPaapiAdId(auctionId, adUnitCode) { - return `paapi:/${auctionId.replace(/:/g, '::')}/:/${adUnitCode.replace(/:/g, '::')}`; -} - -export function parsePaapiAdId(adId) { - const match = /^paapi:\/(.*)\/:\/(.*)$/.exec(adId); - if (match) { - return [match[1], match[2]].map(s => s.replace(/::/g, ':')); - } -} - -/** - * Returns the PAAPI runAdAuction result for the given filters, as a map from ad unit code to auction result - * (an object with `width`, `height`, and one of `urn` or `frameConfig`). - * - * @param filters - * @param raa - * @return {Promise<{[p: string]: any}>} - */ -export function getPAAPIBids(filters, raa = (...args) => navigator.runAdAuction(...args)) { - return Promise.all( - Object.entries(expandFilters(filters)) - .map(([adUnitCode, auctionId]) => { - const bids = paapiBids(auctionId); - if (bids && !bids.hasOwnProperty(adUnitCode)) { - const auctionConfig = getPAAPIConfig({adUnitCode, auctionId})[adUnitCode]; - if (auctionConfig) { - emit(EVENTS.RUN_PAAPI_AUCTION, { - auctionId, - adUnitCode, - auctionConfig - }); - bids[adUnitCode] = new Promise((resolve, reject) => raa(auctionConfig).then(resolve, reject)) - .then(result => { - if (result) { - const bid = { - source: 'paapi', - adId: getPaapiAdId(auctionId, adUnitCode), - width: parsePaapiSize(auctionConfig.requestedSize?.width), - height: parsePaapiSize(auctionConfig.requestedSize?.height), - adUnitCode, - auctionId, - [typeof result === 'string' ? 'urn' : 'frameConfig']: result, - auctionConfig, - }; - emit(EVENTS.PAAPI_BID, bid); - return bid; - } else { - emit(EVENTS.PAAPI_NO_BID, {auctionId, adUnitCode, auctionConfig}); - return null; - } - }).catch(error => { - logError(MODULE_NAME, `error (auction "${auctionId}", adUnit "${adUnitCode}"):`, error); - emit(EVENTS.PAAPI_ERROR, {auctionId, adUnitCode, error, auctionConfig}); - return null; - }); - } - } - return bids?.[adUnitCode]?.then(res => [adUnitCode, res]); - }).filter(e => e) - ).then(result => Object.fromEntries(result)); -} - -getGlobal().getPAAPIBids = (filters) => getPAAPIBids(filters); - -export const topLevelPAAPI = { - name: MODULE_NAME, - init(params) { - getPAAPIConfig = params.getPAAPIConfig; - expandFilters = params.expandFilters; - }, - onAuctionConfig -}; -submodule('paapi', topLevelPAAPI); diff --git a/modules/topicsFpdModule.js b/modules/topicsFpdModule.js index 49abc7a5aa3..364294d2837 100644 --- a/modules/topicsFpdModule.js +++ b/modules/topicsFpdModule.js @@ -1,14 +1,14 @@ -import {isEmpty, logError, logWarn, mergeDeep, safeJSONParse} from '../src/utils.js'; -import {getRefererInfo} from '../src/refererDetection.js'; -import {submodule} from '../src/hook.js'; -import {PbPromise} from '../src/utils/promise.js'; -import {config} from '../src/config.js'; -import {getCoreStorageManager} from '../src/storageManager.js'; +import { isEmpty, logError, logWarn, mergeDeep, safeJSONParse } from '../src/utils.js'; +import { getRefererInfo } from '../src/refererDetection.js'; +import { submodule } from '../src/hook.js'; +import { PbPromise } from '../src/utils/promise.js'; +import { config } from '../src/config.js'; +import { getCoreStorageManager } from '../src/storageManager.js'; -import {isActivityAllowed} from '../src/activities/rules.js'; -import {ACTIVITY_ENRICH_UFPD} from '../src/activities/activities.js'; -import {activityParams} from '../src/activities/activityParams.js'; -import {MODULE_TYPE_BIDDER} from '../src/activities/modules.js'; +import { isActivityAllowed } from '../src/activities/rules.js'; +import { ACTIVITY_ENRICH_UFPD } from '../src/activities/activities.js'; +import { activityParams } from '../src/activities/activityParams.js'; +import { MODULE_TYPE_BIDDER } from '../src/activities/modules.js'; const MODULE_NAME = 'topicsFpd'; const DEFAULT_EXPIRATION_DAYS = 21; @@ -72,7 +72,7 @@ export function getTopicsData(name, topics, taxonomies = TAXONOMIES) { segtax: taxonomies[taxonomyVersion], segclass: modelVersion }, - segment: topics.map((topic) => ({id: topic.topic.toString()})) + segment: topics.map((topic) => ({ id: topic.topic.toString() })) }; if (name != null) { datum.name = name; @@ -106,7 +106,7 @@ export function getTopics(doc = document) { const topicsData = getTopics().then((topics) => getTopicsData(getRefererInfo().domain, topics)); -export function processFpd(config, {global}, {data = topicsData} = {}) { +export function processFpd(config, { global }, { data = topicsData } = {}) { if (!LOAD_TOPICS_INITIALISE) { loadTopicsForBidders(); LOAD_TOPICS_INITIALISE = true; @@ -120,7 +120,7 @@ export function processFpd(config, {global}, {data = topicsData} = {}) { } }); } - return {global}; + return { global }; }); } @@ -134,7 +134,7 @@ export function getCachedTopics() { const storedSegments = new Map(safeJSONParse(coreStorage.getDataFromLocalStorage(topicStorageName))); storedSegments && storedSegments.forEach((value, cachedBidder) => { // Check bidder exist in config for cached bidder data and then only retrieve the cached data - const bidderConfigObj = bidderList.find(({bidder}) => cachedBidder === bidder) + const bidderConfigObj = bidderList.find(({ bidder }) => cachedBidder === bidder) if (bidderConfigObj && isActivityAllowed(ACTIVITY_ENRICH_UFPD, activityParams(MODULE_TYPE_BIDDER, cachedBidder))) { if (!isCachedDataExpired(value[lastUpdated], bidderConfigObj?.expiry || DEFAULT_EXPIRATION_DAYS)) { Object.keys(value).forEach((segData) => { @@ -159,7 +159,7 @@ export function receiveMessage(evt) { try { const data = safeJSONParse(evt.data); if (getLoadedIframeURL().includes(evt.origin) && data && data.segment && !isEmpty(data.segment.topics)) { - const {domain, topics, bidder} = data.segment; + const { domain, topics, bidder } = data.segment; const iframeTopicsData = getTopicsData(domain, topics); iframeTopicsData && storeInLocalStorage(bidder, iframeTopicsData); } @@ -241,13 +241,13 @@ export function loadTopicsForBidders(doc = document) { const bidderLsEntry = storedSegments.get(bidder); if (!bidderLsEntry || (bidderLsEntry && isCachedDataExpired(bidderLsEntry[lastUpdated], fetchRate || DEFAULT_FETCH_RATE_IN_DAYS))) { - window.fetch(`${fetchUrl}?bidder=${bidder}`, {browsingTopics: true}) + window.fetch(`${fetchUrl}?bidder=${bidder}`, { browsingTopics: true }) .then(response => { return response.json(); }) .then(data => { if (data && data.segment && !isEmpty(data.segment.topics)) { - const {domain, topics, bidder} = data.segment; + const { domain, topics, bidder } = data.segment; const fetchTopicsData = getTopicsData(domain, topics); fetchTopicsData && storeInLocalStorage(bidder, fetchTopicsData); } diff --git a/modules/tpmnBidAdapter.js b/modules/tpmnBidAdapter.js index 270a9fb3fdd..6e80e56a542 100644 --- a/modules/tpmnBidAdapter.js +++ b/modules/tpmnBidAdapter.js @@ -23,7 +23,7 @@ const COMMON_PARAMS = [ export const VIDEO_RENDERER_URL = 'https://acdn.adnxs.com/video/outstream/ANOutstreamVideo.js'; export const ADAPTER_VERSION = '2.0'; -export const storage = getStorageManager({bidderCode: BIDDER_CODE}); +export const storage = getStorageManager({ bidderCode: BIDDER_CODE }); export const spec = { code: BIDDER_CODE, supportedMediaTypes: SUPPORTED_AD_TYPES, @@ -142,7 +142,7 @@ const CONVERTER = ortbConverter({ return imp; }, bidResponse(buildBidResponse, bid, context) { - const {bidRequest} = context; + const { bidRequest } = context; const bidResponse = buildBidResponse(bid, context); if (bidResponse.mediaType === BANNER) { bidResponse.ad = bid.adm; @@ -160,7 +160,7 @@ const CONVERTER = ortbConverter({ let videoParams = bidRequest.mediaTypes[VIDEO]; if (videoParams) { videoParams = Object.assign({}, videoParams, bidRequest.params.video); - bidRequest = {...bidRequest, mediaTypes: {[VIDEO]: videoParams}} + bidRequest = { ...bidRequest, mediaTypes: { [VIDEO]: videoParams } } } orig(imp, bidRequest, context); }, diff --git a/modules/trafficgateBidAdapter.js b/modules/trafficgateBidAdapter.js index 47cf3e052ca..ab6db9c878d 100644 --- a/modules/trafficgateBidAdapter.js +++ b/modules/trafficgateBidAdapter.js @@ -1,7 +1,7 @@ -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER, VIDEO} from '../src/mediaTypes.js'; -import {ortbConverter} from '../libraries/ortbConverter/converter.js'; -import {deepAccess, mergeDeep} from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; +import { ortbConverter } from '../libraries/ortbConverter/converter.js'; +import { deepAccess, mergeDeep } from '../src/utils.js'; const BIDDER_CODE = 'trafficgate'; const URL = 'https://[HOST].bc-plugin.com/prebidjs' @@ -64,7 +64,7 @@ const converter = ortbConverter({ imp: { bidfloor(setBidFloor, imp, bidRequest, context) { const floor = {}; - setBidFloor(floor, bidRequest, {...context, currency: 'USD'}); + setBidFloor(floor, bidRequest, { ...context, currency: 'USD' }); if (floor.bidfloorcur === 'USD') { Object.assign(imp, floor); } @@ -74,7 +74,7 @@ const converter = ortbConverter({ let videoParams = bidRequest.mediaTypes[VIDEO]; if (videoParams) { videoParams = Object.assign({}, videoParams, bidRequest.params.video); - bidRequest = {...bidRequest, mediaTypes: {[VIDEO]: videoParams}} + bidRequest = { ...bidRequest, mediaTypes: { [VIDEO]: videoParams } } } orig(imp, bidRequest, context); if (imp.video && videoParams?.context === 'outstream') { @@ -111,7 +111,7 @@ function createRequest(bidRequests, bidderRequest, mediaType) { return { method: 'POST', url: URL.replace('[HOST]', bidRequests[0].params.host), - data: converter.toORTB({bidRequests, bidderRequest, context: {mediaType}}) + data: converter.toORTB({ bidRequests, bidderRequest, context: { mediaType } }) } } @@ -125,9 +125,9 @@ function isBannerBid(bid) { function interpretResponse(resp, req) { if (!resp.body) { - resp.body = {nbr: 0}; + resp.body = { nbr: 0 }; } - return converter.fromORTB({request: req.data, response: resp.body}); + return converter.fromORTB({ request: req.data, response: resp.body }); } export const spec2 = { diff --git a/modules/trionBidAdapter.js b/modules/trionBidAdapter.js index 4af2eb6a549..87c4fb65f62 100644 --- a/modules/trionBidAdapter.js +++ b/modules/trionBidAdapter.js @@ -1,14 +1,14 @@ -import {getBidIdParameter, parseSizesInput} from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; +import { getBidIdParameter, parseSizesInput } from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; import { getStorageManager } from '../src/storageManager.js'; -import {tryAppendQueryString} from '../libraries/urlUtils/urlUtils.js'; -import {isWebdriverEnabled} from '../libraries/webdriver/webdriver.js'; +import { tryAppendQueryString } from '../libraries/urlUtils/urlUtils.js'; +import { isWebdriverEnabled } from '../libraries/webdriver/webdriver.js'; const BID_REQUEST_BASE_URL = 'https://in-appadvertising.com/api/bidRequest'; const USER_SYNC_URL = 'https://in-appadvertising.com/api/userSync.html'; const BIDDER_CODE = 'trion'; const BASE_KEY = '_trion_'; -const storage = getStorageManager({bidderCode: BIDDER_CODE}); +const storage = getStorageManager({ bidderCode: BIDDER_CODE }); export const spec = { code: BIDDER_CODE, @@ -55,7 +55,7 @@ export const spec = { bid.currency = result.currency; bid.netRevenue = result.netRevenue; if (result.adomain) { - bid.meta = {advertiserDomains: result.adomain}; + bid.meta = { advertiserDomains: result.adomain }; } bidResponses.push(bid); } diff --git a/modules/tripleliftBidAdapter.js b/modules/tripleliftBidAdapter.js index 503edaa8864..a9284e08400 100644 --- a/modules/tripleliftBidAdapter.js +++ b/modules/tripleliftBidAdapter.js @@ -4,7 +4,7 @@ import { BANNER, VIDEO } from '../src/mediaTypes.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { config } from '../src/config.js'; import { getStorageManager } from '../src/storageManager.js'; -import {tryAppendQueryString} from '../libraries/urlUtils/urlUtils.js'; +import { tryAppendQueryString } from '../libraries/urlUtils/urlUtils.js'; const GVLID = 28; const BIDDER_CODE = 'triplelift'; @@ -13,7 +13,7 @@ const BANNER_TIME_TO_LIVE = 300; const VIDEO_TIME_TO_LIVE = 3600; let gdprApplies = null; let consentString = null; -export const storage = getStorageManager({bidderCode: BIDDER_CODE}); +export const storage = getStorageManager({ bidderCode: BIDDER_CODE }); export const tripleliftAdapterSpec = { gvlid: GVLID, @@ -58,10 +58,6 @@ export const tripleliftAdapterSpec = { tlCall = tryAppendQueryString(tlCall, 'us_privacy', bidderRequest.uspConsent); } - if (bidderRequest?.paapi?.enabled) { - tlCall = tryAppendQueryString(tlCall, 'fledge', bidderRequest.paapi.enabled); - } - if (config.getConfig('coppa') === true) { tlCall = tryAppendQueryString(tlCall, 'coppa', true); } @@ -79,28 +75,10 @@ export const tripleliftAdapterSpec = { }; }, - interpretResponse: function(serverResponse, {bidderRequest}) { + interpretResponse: function(serverResponse, { bidderRequest }) { let bids = serverResponse.body.bids || []; - const paapi = serverResponse.body.paapi || []; - - bids = bids.map(bid => _buildResponseObject(bidderRequest, bid)); - if (paapi.length > 0) { - const fledgeAuctionConfigs = paapi.map(config => { - return { - bidId: bidderRequest.bids[config.imp_id].bidId, - config: config.auctionConfig - }; - }); - - logMessage('Response with FLEDGE:', { bids, fledgeAuctionConfigs }); - return { - bids, - paapi: fledgeAuctionConfigs - }; - } else { - return bids; - } + return bids.map(bid => _buildResponseObject(bidderRequest, bid)); }, getUserSyncs: function(syncOptions, responses, gdprConsent, usPrivacy, gppConsent) { diff --git a/modules/trustxBidAdapter.js b/modules/trustxBidAdapter.js index 2b0f2c80331..cd4f549c819 100644 --- a/modules/trustxBidAdapter.js +++ b/modules/trustxBidAdapter.js @@ -6,11 +6,11 @@ import { deepSetValue, mergeDeep } from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER, VIDEO} from '../src/mediaTypes.js'; -import {Renderer} from '../src/Renderer.js'; -import {ortbConverter} from '../libraries/ortbConverter/converter.js'; -import {config} from '../src/config.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; +import { Renderer } from '../src/Renderer.js'; +import { ortbConverter } from '../libraries/ortbConverter/converter.js'; +import { config } from '../src/config.js'; /** * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest @@ -44,7 +44,7 @@ const ortbAdapterConverter = ortbConverter({ const floorInfo = bidRequest.getFloor({ currency: SUPPORTED_CURRENCY, mediaType: curMediaType, - size: bidRequest.sizes ? bidRequest.sizes.map(([w, h]) => ({w, h})) : '*' + size: bidRequest.sizes ? bidRequest.sizes.map(([w, h]) => ({ w, h })) : '*' }); if (floorInfo && typeof floorInfo === 'object' && @@ -146,7 +146,7 @@ const ortbAdapterConverter = ortbConverter({ return requestObj; }, bidResponse(buildBidResponse, bid, context) { - const {bidRequest} = context; + const { bidRequest } = context; let responseMediaType; if (bid.mtype === 2) { @@ -219,7 +219,7 @@ export const spec = { const requestData = ortbAdapterConverter.toORTB({ bidRequests: validBidRequests, bidderRequest, - context: {contextMediaType: adType} + context: { contextMediaType: adType } }); if (validBidRequests[0].params.test) { diff --git a/modules/ttdBidAdapter.js b/modules/ttdBidAdapter.js index 4598c3b8a7a..944c75ae616 100644 --- a/modules/ttdBidAdapter.js +++ b/modules/ttdBidAdapter.js @@ -3,8 +3,8 @@ import { config } from '../src/config.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; import { isNumber } from '../src/utils.js'; -import { getDNT } from '../libraries/dnt/index.js'; import { getConnectionType } from '../libraries/connectionInfo/connectionUtils.js' +import { getDNT } from '../libraries/dnt/index.js'; /** * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest @@ -21,6 +21,7 @@ const BIDDER_CODE_LONG = 'thetradedesk'; const BIDDER_ENDPOINT = 'https://direct.adsrvr.org/bid/bidder/'; const BIDDER_ENDPOINT_HTTP2 = 'https://d2.adsrvr.org/bid/bidder/'; const USER_SYNC_ENDPOINT = 'https://match.adsrvr.org'; +const TTL = 360; const MEDIA_TYPE = { BANNER: 1, @@ -143,6 +144,8 @@ function getImpression(bidRequest) { }; const gpid = utils.deepAccess(bidRequest, 'ortb2Imp.ext.gpid'); + const exp = TTL; + impression.exp = exp; const tagid = gpid || bidRequest.params.placementId; if (tagid) { impression.tagid = tagid; @@ -170,7 +173,7 @@ function getImpression(bidRequest) { const secure = utils.deepAccess(bidRequest, 'ortb2Imp.secure'); impression.secure = isNumber(secure) ? secure : 1 - const {video: _, ...ortb2ImpWithoutVideo} = bidRequest.ortb2Imp; // if enabled, video is already assigned above + const { video: _, ...ortb2ImpWithoutVideo } = bidRequest.ortb2Imp; // if enabled, video is already assigned above utils.mergeDeep(impression, ortb2ImpWithoutVideo) return impression; @@ -477,7 +480,7 @@ export const spec = { dealId: bid.dealid || null, currency: currency || 'USD', netRevenue: true, - ttl: bid.ttl || 360, + ttl: bid.ttl || TTL, meta: {}, }; diff --git a/modules/twistDigitalBidAdapter.js b/modules/twistDigitalBidAdapter.js index bed9e531a26..373ac79c26a 100644 --- a/modules/twistDigitalBidAdapter.js +++ b/modules/twistDigitalBidAdapter.js @@ -1,6 +1,6 @@ -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER, VIDEO} from '../src/mediaTypes.js'; -import {getStorageManager} from '../src/storageManager.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; +import { getStorageManager } from '../src/storageManager.js'; import { isBidRequestValid, createInterpretResponseFn, createUserSyncGetter, createBuildRequestsFn, onBidWon } from '../libraries/vidazooUtils/bidderUtils.js'; @@ -10,7 +10,7 @@ const DEFAULT_SUB_DOMAIN = 'exchange'; const BIDDER_CODE = 'twistdigital'; const BIDDER_VERSION = '1.0.0'; -export const storage = getStorageManager({bidderCode: BIDDER_CODE}); +export const storage = getStorageManager({ bidderCode: BIDDER_CODE }); export function createDomain(subDomain = DEFAULT_SUB_DOMAIN) { return `https://${subDomain}.twist.win`; diff --git a/modules/ucfunnelAnalyticsAdapter.js b/modules/ucfunnelAnalyticsAdapter.js index 20a412cdfdb..b4df46086bf 100644 --- a/modules/ucfunnelAnalyticsAdapter.js +++ b/modules/ucfunnelAnalyticsAdapter.js @@ -1,9 +1,9 @@ -import {ajax} from '../src/ajax.js'; +import { ajax } from '../src/ajax.js'; import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import { EVENTS } from '../src/constants.js'; import adapterManager from '../src/adapterManager.js'; -import {getGlobal} from '../src/prebidGlobal.js'; -import {logError, logInfo, deepClone} from '../src/utils.js'; +import { getGlobal } from '../src/prebidGlobal.js'; +import { logError, logInfo, deepClone } from '../src/utils.js'; const analyticsType = 'endpoint'; @@ -35,7 +35,7 @@ export const parseAdUnitCode = function (bidResponse) { return bidResponse.adUnitCode.toLowerCase(); }; -export const ucfunnelAnalyticsAdapter = Object.assign(adapter({ANALYTICS_SERVER, analyticsType}), { +export const ucfunnelAnalyticsAdapter = Object.assign(adapter({ ANALYTICS_SERVER, analyticsType }), { cachedAuctions: {}, @@ -107,7 +107,7 @@ export const ucfunnelAnalyticsAdapter = Object.assign(adapter({ANALYTICS_SERVER, message.adUnits[adUnitCode][bidder] = bidResponse; }, createBidMessage(auctionEndArgs, winningBids, timeoutBids) { - const {auctionId, timestamp, auctionEnd, adUnitCodes, bidsReceived, noBids} = auctionEndArgs; + const { auctionId, timestamp, auctionEnd, adUnitCodes, bidsReceived, noBids } = auctionEndArgs; const message = this.createCommonMessage(auctionId); message.auctionElapsed = (auctionEnd - timestamp); @@ -161,7 +161,7 @@ export const ucfunnelAnalyticsAdapter = Object.assign(adapter({ANALYTICS_SERVER, handleBidWon(bidWonArgs) { this.sendEventMessage('imp', this.createImpressionMessage(bidWonArgs)); }, - track({eventType, args}) { + track({ eventType, args }) { if (analyticsOptions.sampled) { switch (eventType) { case BID_WON: diff --git a/modules/ucfunnelBidAdapter.js b/modules/ucfunnelBidAdapter.js index f73bd470b14..1794de9ed0d 100644 --- a/modules/ucfunnelBidAdapter.js +++ b/modules/ucfunnelBidAdapter.js @@ -1,10 +1,10 @@ -import {getDNT} from '../libraries/dnt/index.js'; import { generateUUID, _each, deepAccess } from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER, VIDEO, NATIVE} from '../src/mediaTypes.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, VIDEO, NATIVE } from '../src/mediaTypes.js'; import { getStorageManager } from '../src/storageManager.js'; import { config } from '../src/config.js'; import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; +import { getDNT } from '../libraries/dnt/index.js'; /** * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest @@ -20,7 +20,7 @@ const VIDEO_CONTEXT = { INSTREAM: 0, OUSTREAM: 2 } -const storage = getStorageManager({bidderCode: BIDDER_CODE}); +const storage = getStorageManager({ bidderCode: BIDDER_CODE }); export const spec = { code: BIDDER_CODE, @@ -233,7 +233,7 @@ function getFloor(bid, size, mediaTypes) { var bidFloor = bid.getFloor({ currency: CURRENCY, mediaType: getMediaType(mediaTypes), - size: (size) ? [ size[0], size[1] ] : '*', + size: (size) ? [size[0], size[1]] : '*', }); if (bidFloor?.currency === CURRENCY) { return bidFloor.floor; diff --git a/modules/uid2IdSystem.js b/modules/uid2IdSystem.js index 0f052fd6e44..e20880332c7 100644 --- a/modules/uid2IdSystem.js +++ b/modules/uid2IdSystem.js @@ -7,11 +7,11 @@ import { logInfo, logWarn } from '../src/utils.js'; import { submodule } from '../src/hook.js'; -import {getStorageManager} from '../src/storageManager.js'; -import {MODULE_TYPE_UID} from '../src/activities/modules.js'; +import { getStorageManager } from '../src/storageManager.js'; +import { MODULE_TYPE_UID } from '../src/activities/modules.js'; import { Uid2GetId, Uid2CodeVersion, extractIdentityFromParams } from '../libraries/uid2IdSystemShared/uid2IdSystem_shared.js'; -import {UID2_EIDS} from '../libraries/uid2Eids/uid2Eids.js'; +import { UID2_EIDS } from '../libraries/uid2Eids/uid2Eids.js'; /** * @typedef {import('../modules/userId/index.js').Submodule} Submodule @@ -41,7 +41,7 @@ function createLogger(logger, prefix) { const _logInfo = createLogger(logInfo, LOG_PRE_FIX); const _logWarn = createLogger(logWarn, LOG_PRE_FIX); -export const storage = getStorageManager({moduleType: MODULE_TYPE_UID, moduleName: MODULE_NAME}); +export const storage = getStorageManager({ moduleType: MODULE_TYPE_UID, moduleName: MODULE_NAME }); /** @type {Submodule} */ export const uid2IdSubmodule = { diff --git a/modules/underdogmediaBidAdapter.js b/modules/underdogmediaBidAdapter.js index 45bac54e64c..d78782ad3f1 100644 --- a/modules/underdogmediaBidAdapter.js +++ b/modules/underdogmediaBidAdapter.js @@ -8,11 +8,12 @@ import { logWarn, parseSizesInput } from '../src/utils.js'; -import {config} from '../src/config.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {isSlotMatchingAdUnitCode} from '../libraries/gptUtils/gptUtils.js'; +import { config } from '../src/config.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { isSlotMatchingAdUnitCode } from '../libraries/gptUtils/gptUtils.js'; import { percentInView } from '../libraries/percentInView/percentInView.js'; -import {isIframe} from '../libraries/omsUtils/index.js'; +import { isIframe } from '../libraries/omsUtils/index.js'; +import { getAdUnitElement } from '../src/utils/adUnits.js'; const BIDDER_CODE = 'underdogmedia'; const UDM_ADAPTER_VERSION = '7.30V'; @@ -109,7 +110,7 @@ export const spec = { sizes = flatten(sizes, parseSizesInput(bidParamSizes)); siteId = +bidParam.params.siteId; const adUnitCode = bidParam.adUnitCode - const element = _getAdSlotHTMLElement(adUnitCode) + const element = _getAdSlotHTMLElement(bidParam) const minSize = _getMinSize(bidParamSizes) placementObject.sizes = parseSizesInput(bidParamSizes) @@ -234,9 +235,9 @@ function _getMinSize(bidParamSizes) { return bidParamSizes.reduce((min, size) => size.h * size.w < min.h * min.w ? size : min) } -function _getAdSlotHTMLElement(adUnitCode) { - return document.getElementById(adUnitCode) || - document.getElementById(_mapAdUnitPathToElementId(adUnitCode)); +function _getAdSlotHTMLElement(bidRequest) { + return getAdUnitElement(bidRequest) || + document.getElementById(_mapAdUnitPathToElementId(bidRequest.adUnitCode)); } function _mapAdUnitPathToElementId(adUnitCode) { diff --git a/modules/undertoneBidAdapter.js b/modules/undertoneBidAdapter.js index badc0911270..e1da10b1da0 100644 --- a/modules/undertoneBidAdapter.js +++ b/modules/undertoneBidAdapter.js @@ -2,11 +2,12 @@ * Adapter to send bids to Undertone */ -import {deepAccess, parseUrl, extractDomainFromHost, getWinDimensions} from '../src/utils.js'; +import { deepAccess, parseUrl, extractDomainFromHost, getWinDimensions } from '../src/utils.js'; import { getBoundingClientRect } from '../libraries/boundingClientRect/boundingClientRect.js'; import { getViewportCoordinates } from '../libraries/viewport/viewport.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER, VIDEO} from '../src/mediaTypes.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; +import { getAdUnitElement } from '../src/utils/adUnits.js'; const BIDDER_CODE = 'undertone'; const URL = 'https://hb.undertone.com/hb'; @@ -38,10 +39,10 @@ function getGdprQueryParams(gdprConsent) { return `gdpr=${gdpr}&gdprstr=${gdprstr}`; } -function getBannerCoords(id) { - const element = document.getElementById(id); +function getBannerCoords(bidRequest) { + const element = getAdUnitElement(bidRequest); if (element) { - const {left, top} = getBoundingClientRect(element); + const { left, top } = getBoundingClientRect(element); const viewport = getViewportCoordinates(); return [Math.round(left + (viewport.left || 0)), Math.round(top + (viewport.top || 0))]; } @@ -109,7 +110,7 @@ export const spec = { validBidRequests.forEach(bidReq => { const bid = { bidRequestId: bidReq.bidId, - coordinates: getBannerCoords(bidReq.adUnitCode), + coordinates: getBannerCoords(bidReq), hbadaptor: 'prebid', url: pageUrl, domain: domain, diff --git a/modules/unicornBidAdapter.js b/modules/unicornBidAdapter.js index 4f0c3e3696d..825bd26be23 100644 --- a/modules/unicornBidAdapter.js +++ b/modules/unicornBidAdapter.js @@ -1,7 +1,7 @@ import { logInfo, deepAccess, generateUUID } from '../src/utils.js'; -import {BANNER} from '../src/mediaTypes.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {getStorageManager} from '../src/storageManager.js'; +import { BANNER } from '../src/mediaTypes.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { getStorageManager } from '../src/storageManager.js'; /** * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest @@ -13,7 +13,7 @@ const UNICORN_ENDPOINT = 'https://ds.uncn.jp/pb/0/bid.json'; const UNICORN_DEFAULT_CURRENCY = 'JPY'; const UNICORN_PB_COOKIE_KEY = '__pb_unicorn_aud'; const UNICORN_PB_VERSION = '1.1'; -const storage = getStorageManager({bidderCode: BIDDER_CODE}); +const storage = getStorageManager({ bidderCode: BIDDER_CODE }); /** * Placement ID and Account ID are required. @@ -173,7 +173,7 @@ const getUid = () => { * @param {Array} arr */ const makeFormat = arr => arr.map((s) => { - return {w: s[0], h: s[1]}; + return { w: s[0], h: s[1] }; }); export const spec = { diff --git a/modules/unifiedIdSystem.js b/modules/unifiedIdSystem.js index 0ffe1b5009f..a6335b33b63 100644 --- a/modules/unifiedIdSystem.js +++ b/modules/unifiedIdSystem.js @@ -6,9 +6,9 @@ */ import { logError } from '../src/utils.js'; -import {ajax} from '../src/ajax.js'; -import {submodule} from '../src/hook.js' -import {UID1_EIDS} from '../libraries/uid1Eids/uid1Eids.js'; +import { ajax } from '../src/ajax.js'; +import { submodule } from '../src/hook.js' +import { UID1_EIDS } from '../libraries/uid1Eids/uid1Eids.js'; /** * @typedef {import('../modules/userId/index.js').Submodule} Submodule @@ -71,9 +71,9 @@ export const unifiedIdSubmodule = { callback(); } }; - ajax(url, callbacks, undefined, {method: 'GET', withCredentials: true}); + ajax(url, callbacks, undefined, { method: 'GET', withCredentials: true }); }; - return {callback: resp}; + return { callback: resp }; }, eids: { tdid: { diff --git a/modules/uniquestAnalyticsAdapter.js b/modules/uniquestAnalyticsAdapter.js index fff6abc56b9..608c20cb866 100644 --- a/modules/uniquestAnalyticsAdapter.js +++ b/modules/uniquestAnalyticsAdapter.js @@ -1,8 +1,8 @@ -import {logError} from '../src/utils.js'; -import {ajax} from '../src/ajax.js'; +import { logError } from '../src/utils.js'; +import { ajax } from '../src/ajax.js'; import adapterManager from '../src/adapterManager.js'; -import {EVENTS} from '../src/constants.js'; -import {getRefererInfo} from '../src/refererDetection.js'; +import { EVENTS } from '../src/constants.js'; +import { getRefererInfo } from '../src/refererDetection.js'; import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; const ADAPTER_CODE = 'uniquest'; @@ -68,7 +68,7 @@ function auctionEndHandler(eventType, args, pageUrl) { } } -const baseAdapter = adapter({analyticsType: 'endpoint'}); +const baseAdapter = adapter({ analyticsType: 'endpoint' }); const uniquestAdapter = Object.assign({}, baseAdapter, { enableAnalytics(config = {}) { @@ -85,7 +85,7 @@ const uniquestAdapter = Object.assign({}, baseAdapter, { baseAdapter.disableAnalytics.apply(this, arguments); }, - track({eventType, args}) { + track({ eventType, args }) { const refererInfo = getRefererInfo(); const pageUrl = refererInfo.page; diff --git a/modules/uniquestBidAdapter.js b/modules/uniquestBidAdapter.js index 7fad6df68f0..d2ba0abf8a2 100644 --- a/modules/uniquestBidAdapter.js +++ b/modules/uniquestBidAdapter.js @@ -1,8 +1,8 @@ -import {getBidIdParameter} from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER} from '../src/mediaTypes.js'; -import {tryAppendQueryString} from '../libraries/urlUtils/urlUtils.js'; -import {interpretResponse} from '../libraries/uniquestUtils/uniquestUtils.js'; +import { getBidIdParameter } from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER } from '../src/mediaTypes.js'; +import { tryAppendQueryString } from '../libraries/urlUtils/urlUtils.js'; +import { interpretResponse } from '../libraries/uniquestUtils/uniquestUtils.js'; /** * @typedef {import('../src/adapters/bidderFactory').Bid} Bid diff --git a/modules/uniquest_widgetBidAdapter.js b/modules/uniquest_widgetBidAdapter.js index f40c47b238c..a4412919330 100644 --- a/modules/uniquest_widgetBidAdapter.js +++ b/modules/uniquest_widgetBidAdapter.js @@ -1,8 +1,8 @@ -import {getBidIdParameter} from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER} from '../src/mediaTypes.js'; -import {tryAppendQueryString} from '../libraries/urlUtils/urlUtils.js'; -import {interpretResponse} from '../libraries/uniquestUtils/uniquestUtils.js'; +import { getBidIdParameter } from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER } from '../src/mediaTypes.js'; +import { tryAppendQueryString } from '../libraries/urlUtils/urlUtils.js'; +import { interpretResponse } from '../libraries/uniquestUtils/uniquestUtils.js'; /** * @typedef {import('../src/adapters/bidderFactory').Bid} Bid diff --git a/modules/unrulyBidAdapter.js b/modules/unrulyBidAdapter.js index 3f9c4dd1253..df0192079e0 100644 --- a/modules/unrulyBidAdapter.js +++ b/modules/unrulyBidAdapter.js @@ -1,7 +1,7 @@ import { deepAccess, logError } from '../src/utils.js'; -import {Renderer} from '../src/Renderer.js' -import {registerBidder} from '../src/adapters/bidderFactory.js' -import {VIDEO, BANNER} from '../src/mediaTypes.js' +import { Renderer } from '../src/Renderer.js' +import { registerBidder } from '../src/adapters/bidderFactory.js' +import { VIDEO, BANNER } from '../src/mediaTypes.js' function configureUniversalTag(exchangeRenderer, requestId) { if (!exchangeRenderer.config) throw new Error('UnrulyBidAdapter: Missing renderer config.'); @@ -56,14 +56,8 @@ const RemoveDuplicateSizes = (validBid) => { } }; -const ConfigureProtectedAudience = (validBid, protectedAudienceEnabled) => { - if (!protectedAudienceEnabled && validBid.ortb2Imp && validBid.ortb2Imp.ext) { - delete validBid.ortb2Imp.ext.ae; - } -} - const getRequests = (conf, validBidRequests, bidderRequest) => { - const {bids, bidderRequestId, bidderCode, ...bidderRequestData} = bidderRequest; + const { bids, bidderRequestId, bidderCode, ...bidderRequestData } = bidderRequest; const invalidBidsCount = bidderRequest.bids.length - validBidRequests.length; const requestBySiteId = {}; @@ -71,7 +65,6 @@ const getRequests = (conf, validBidRequests, bidderRequest) => { const currSiteId = validBid.params.siteId; addBidFloorInfo(validBid); RemoveDuplicateSizes(validBid); - ConfigureProtectedAudience(validBid, conf.protectedAudienceEnabled); requestBySiteId[currSiteId] = requestBySiteId[currSiteId] || []; requestBySiteId[currSiteId].push(validBid); }); @@ -90,7 +83,7 @@ const getRequests = (conf, validBidRequests, bidderRequest) => { ) }; - request.push(Object.assign({}, {data, ...conf})); + request.push(Object.assign({}, { data, ...conf })); }); return request; @@ -226,43 +219,18 @@ export const adapter = { 'options': { 'contentType': 'application/json' }, - 'protectedAudienceEnabled': bidderRequest.paapi?.enabled }, validBidRequests, bidderRequest); }, interpretResponse: function (serverResponse) { - if (!(serverResponse && serverResponse.body && (serverResponse.body.auctionConfigs || serverResponse.body.bids))) { + if (!(serverResponse && serverResponse.body && serverResponse.body.bids)) { return []; } const serverResponseBody = serverResponse.body; - let bids = []; - let fledgeAuctionConfigs = null; - if (serverResponseBody.bids.length) { - bids = handleBidResponseByMediaType(serverResponseBody.bids); - } - - if (serverResponseBody.auctionConfigs) { - const auctionConfigs = serverResponseBody.auctionConfigs; - const bidIdList = Object.keys(auctionConfigs); - if (bidIdList.length) { - bidIdList.forEach((bidId) => { - fledgeAuctionConfigs = [{ - 'bidId': bidId, - 'config': auctionConfigs[bidId] - }]; - }) - } - } + const bids = handleBidResponseByMediaType(serverResponseBody.bids); - if (!fledgeAuctionConfigs) { - return bids; - } - - return { - bids, - paapi: fledgeAuctionConfigs - }; + return bids; } }; diff --git a/modules/userId/eids.js b/modules/userId/eids.js index b35eea2fab8..61a1634f64f 100644 --- a/modules/userId/eids.js +++ b/modules/userId/eids.js @@ -1,4 +1,4 @@ -import {logError, deepClone, isFn, isStr} from '../../src/utils.js'; +import { logError, deepClone, isFn, isStr } from '../../src/utils.js'; export const EID_CONFIG = new Map(); @@ -73,9 +73,9 @@ export function createEidsArray(bidRequestUserId, eidConfigs = EID_CONFIG) { eids = [eids]; } eids.forEach(eid => { - eid.uids = eid.uids.filter(({id}) => isStr(id)) + eid.uids = eid.uids.filter(({ id }) => isStr(id)) }) - eids = eids.filter(({uids}) => uids?.length > 0); + eids = eids.filter(({ uids }) => uids?.length > 0); } catch (e) { logError(`Could not generate EID for "${name}"`, e); } diff --git a/modules/userId/eids.md b/modules/userId/eids.md index f6f62229f53..bdd8a0bb3e8 100644 --- a/modules/userId/eids.md +++ b/modules/userId/eids.md @@ -25,6 +25,14 @@ userIdAsEids = [ }] }, + { + source: 'locid.com', + uids: [{ + id: 'some-random-id-value', + atype: 1 + }] + }, + { source: 'adserver.org', uids: [{ diff --git a/modules/userId/index.ts b/modules/userId/index.ts index 006b0421d7d..4946e96f578 100644 --- a/modules/userId/index.ts +++ b/modules/userId/index.ts @@ -3,13 +3,13 @@ * @module modules/userId */ -import {config} from '../../src/config.js'; +import { config } from '../../src/config.js'; import * as events from '../../src/events.js'; -import {addApiMethod, startAuction, type StartAuctionOptions} from '../../src/prebid.js'; +import { addApiMethod, startAuction, type StartAuctionOptions } from '../../src/prebid.js'; import adapterManager from '../../src/adapterManager.js'; -import {EVENTS} from '../../src/constants.js'; -import {module, ready as hooksReady} from '../../src/hook.js'; -import {EID_CONFIG, getEids} from './eids.js'; +import { EVENTS } from '../../src/constants.js'; +import { module, ready as hooksReady } from '../../src/hook.js'; +import { EID_CONFIG, getEids } from './eids.js'; import { discloseStorageUse, getCoreStorageManager, @@ -33,26 +33,26 @@ import { logInfo, logWarn, mergeDeep } from '../../src/utils.js'; -import {getPPID as coreGetPPID} from '../../src/adserver.js'; -import {defer, delay, PbPromise} from '../../src/utils/promise.js'; -import {newMetrics, timedAuctionHook, useMetrics} from '../../src/utils/perfMetrics.js'; -import {findRootDomain} from '../../src/fpd/rootDomain.js'; -import {allConsent, GDPR_GVLIDS} from '../../src/consentHandler.js'; -import {MODULE_TYPE_UID} from '../../src/activities/modules.js'; -import {isActivityAllowed, registerActivityControl} from '../../src/activities/rules.js'; -import {ACTIVITY_ACCESS_DEVICE, ACTIVITY_ENRICH_EIDS} from '../../src/activities/activities.js'; -import {activityParams} from '../../src/activities/activityParams.js'; -import {USERSYNC_DEFAULT_CONFIG, type UserSyncConfig} from '../../src/userSync.js'; -import type {ORTBRequest} from "../../src/types/ortb/request.d.ts"; -import type {AnyFunction, Wraps} from "../../src/types/functions.d.ts"; -import type {ProviderParams, UserId, UserIdProvider, UserIdConfig, IdProviderSpec, ProviderResponse} from "./spec.ts"; +import { getPPID as coreGetPPID } from '../../src/adserver.js'; +import { defer, delay, PbPromise } from '../../src/utils/promise.js'; +import { newMetrics, timedAuctionHook, useMetrics } from '../../src/utils/perfMetrics.js'; +import { findRootDomain } from '../../src/fpd/rootDomain.js'; +import { allConsent, GDPR_GVLIDS } from '../../src/consentHandler.js'; +import { MODULE_TYPE_UID } from '../../src/activities/modules.js'; +import { isActivityAllowed, registerActivityControl } from '../../src/activities/rules.js'; +import { ACTIVITY_ACCESS_DEVICE, ACTIVITY_ENRICH_EIDS } from '../../src/activities/activities.js'; +import { activityParams } from '../../src/activities/activityParams.js'; +import { USERSYNC_DEFAULT_CONFIG, type UserSyncConfig } from '../../src/userSync.js'; +import type { ORTBRequest } from "../../src/types/ortb/request.d.ts"; +import type { AnyFunction, Wraps } from "../../src/types/functions.d.ts"; +import type { ProviderParams, UserId, UserIdProvider, UserIdConfig, IdProviderSpec, ProviderResponse } from "./spec.ts"; import { ACTIVITY_PARAM_COMPONENT_NAME, ACTIVITY_PARAM_COMPONENT_TYPE, ACTIVITY_PARAM_STORAGE_TYPE, ACTIVITY_PARAM_STORAGE_WRITE } from '../../src/activities/params.js'; -import {beforeInitAuction} from '../../src/auction.js'; +import { beforeInitAuction } from '../../src/auction.js'; const MODULE_NAME = 'User ID'; const COOKIE = STORAGE_TYPE_COOKIES; @@ -381,8 +381,8 @@ function mkPriorityMaps() { function activeModuleGetter(key, useGlobals, modules) { return function () { - for (const {allowed, bidders, module} of modules) { - if (!dep.isAllowed(ACTIVITY_ENRICH_EIDS, activityParams(MODULE_TYPE_UID, module?.config?.name, {init: false}))) { + for (const { allowed, bidders, module } of modules) { + if (!dep.isAllowed(ACTIVITY_ENRICH_EIDS, activityParams(MODULE_TYPE_UID, module?.config?.name, { init: false }))) { continue; } const value = module.idObj?.[key]; @@ -430,22 +430,22 @@ function mkPriorityMaps() { } }) if (!allNonGlobal) { - global[key] = activeModuleGetter(key, true, modules.map(({bidders, module}) => ({allowed: bidders == null, bidders, module}))); + global[key] = activeModuleGetter(key, true, modules.map(({ bidders, module }) => ({ allowed: bidders == null, bidders, module }))); } bidderFilters.forEach(bidderCode => { bidder[bidderCode] = bidder[bidderCode] ?? {}; - bidder[bidderCode][key] = activeModuleGetter(key, false, modules.map(({bidders, module}) => ({allowed: bidders?.includes(bidderCode), bidders, module}))); + bidder[bidderCode][key] = activeModuleGetter(key, false, modules.map(({ bidders, module }) => ({ allowed: bidders?.includes(bidderCode), bidders, module }))); }) }); const combined = Object.values(bidder).concat([global]).reduce((combo, map) => Object.assign(combo, map), {}); - Object.assign(map, {global, bidder, combined}); + Object.assign(map, { global, bidder, combined }); } return map; } export function enrichEids(ortb2Fragments) { - const {global: globalFpd, bidder: bidderFpd} = ortb2Fragments; - const {global: globalMods, bidder: bidderMods} = initializedSubmodules; + const { global: globalFpd, bidder: bidderFpd } = ortb2Fragments; + const { global: globalMods, bidder: bidderMods } = initializedSubmodules; const globalEids = getEids(globalMods); if (globalEids.length > 0) { deepSetValue(globalFpd, 'user.ext.eids', (globalFpd.user?.ext?.eids ?? []).concat(globalEids)); @@ -469,14 +469,14 @@ declare module '../../src/adapterManager' { } } -export function addIdData({ortb2Fragments}) { - ortb2Fragments = ortb2Fragments ?? {global: {}, bidder: {}} +export function addIdData({ ortb2Fragments }) { + ortb2Fragments = ortb2Fragments ?? { global: {}, bidder: {} } enrichEids(ortb2Fragments); } const INIT_CANCELED = {}; -function idSystemInitializer({mkDelay = delay} = {}) { +function idSystemInitializer({ mkDelay = delay } = {}) { const startInit = defer(); const startCallbacks = defer(); let cancel; @@ -531,7 +531,7 @@ function idSystemInitializer({mkDelay = delay} = {}) { * with `ready` = true, starts initialization; with `refresh` = true, reinitialize submodules (optionally * filtered by `submoduleNames`). */ - return function ({refresh = false, submoduleNames = null, ready = false} = {}) { + return function ({ refresh = false, submoduleNames = null, ready = false } = {}) { if (ready && !initialized) { initialized = true; startInit.resolve(); @@ -593,13 +593,13 @@ function getPPID(eids = getUserIdsAsEids() || []) { * @param {Object} reqBidsConfigObj required; This is the same param that's used in pbjs.requestBids. * @param {function} fn required; The next function in the chain, used by hook.ts */ -export const startAuctionHook = timedAuctionHook('userId', function requestBidsHook(fn, reqBidsConfigObj: StartAuctionOptions, {mkDelay = delay, getIds = getUserIdsAsync} = {}) { +export const startAuctionHook = timedAuctionHook('userId', function requestBidsHook(fn, reqBidsConfigObj: StartAuctionOptions, { mkDelay = delay, getIds = getUserIdsAsync } = {}) { PbPromise.race([ getIds().catch(() => null), mkDelay(auctionDelay) ]).then(() => { addIdData(reqBidsConfigObj); - uidMetrics().join(useMetrics(reqBidsConfigObj.metrics), {propagate: false, includeGroups: true}); + uidMetrics().join(useMetrics(reqBidsConfigObj.metrics), { propagate: false, includeGroups: true }); // calling fn allows prebid to continue processing fn.call(this, reqBidsConfigObj); }); @@ -635,9 +635,9 @@ export function adUnitEidsHook(next, auction) { if (bidderCode == null) return globalEids; if (!eidsByBidder.hasOwnProperty(bidderCode)) { eidsByBidder[bidderCode] = mergeDeep( - {eids: []}, - {eids: globalEids}, - {eids: auction.getFPD()?.bidder?.[bidderCode]?.user?.ext?.eids ?? []} + { eids: [] }, + { eids: globalEids }, + { eids: auction.getFPD()?.bidder?.[bidderCode]?.user?.ext?.eids ?? [] } ).eids; } return eidsByBidder[bidderCode]; @@ -657,7 +657,7 @@ export function adUnitEidsHook(next, auction) { * @returns {boolean} */ function addedStartAuctionHook() { - return !!startAuction.getHooks({hook: startAuctionHook}).length; + return !!startAuction.getHooks({ hook: startAuctionHook }).length; } /** @@ -776,10 +776,10 @@ function retryOnCancel(initParams?) { * submoduleNames submodules to refresh. If omitted, refresh all submodules. * callback called when the refresh is complete */ -function refreshUserIds({submoduleNames}: { +function refreshUserIds({ submoduleNames }: { submoduleNames?: string[] } = {}, callback?: () => void): Promise> { - return retryOnCancel({refresh: true, submoduleNames}) + return retryOnCancel({ refresh: true, submoduleNames }) .then((userIds) => { if (callback && isFn(callback)) { callback(); @@ -1062,10 +1062,10 @@ function updateEIDConfig(submodules) { } export function generateSubmoduleContainers(options, configs, prevSubmodules = submodules, registry = submoduleRegistry) { - const {autoRefresh, retainConfig} = options; + const { autoRefresh, retainConfig } = options; return registry .reduce((acc, submodule) => { - const {name, aliasName} = submodule; + const { name, aliasName } = submodule; const matchesName = (query) => [name, aliasName].some(value => value?.toLowerCase() === query.toLowerCase()); const submoduleConfig = configs.find((configItem) => matchesName(configItem.name)); @@ -1180,7 +1180,7 @@ export function attachIdSystem(submodule: IdProviderSpec) { updateSubmodules(); // TODO: a test case wants this to work even if called after init (the setConfig({userId})) // so we trigger a refresh. But is that even possible outside of tests? - initIdSystem({refresh: true, submoduleNames: [submodule.name]}); + initIdSystem({ refresh: true, submoduleNames: [submodule.name] }); } } @@ -1216,7 +1216,7 @@ const enforceStorageTypeRule = (userIdsConfig, enforceStorageType) => { if (params[ACTIVITY_PARAM_STORAGE_TYPE] !== submoduleConfig.storage.type) { const reason = `${submoduleConfig.name} attempts to store data in ${params[ACTIVITY_PARAM_STORAGE_TYPE]} while configuration allows ${submoduleConfig.storage.type}.`; if (enforceStorageType) { - return {allow: false, reason}; + return { allow: false, reason }; } else { logWarn(reason); } @@ -1229,12 +1229,12 @@ const enforceStorageTypeRule = (userIdsConfig, enforceStorageType) => { * so a callback is added to fire after the consentManagement module. * @param {{getConfig:function}} config */ -export function init(config, {mkDelay = delay} = {}) { +export function init(config, { mkDelay = delay } = {}) { ppidSource = undefined; submodules = []; configRegistry = []; initializedSubmodules = mkPriorityMaps(); - initIdSystem = idSystemInitializer({mkDelay}); + initIdSystem = idSystemInitializer({ mkDelay }); if (configListener != null) { configListener(); } @@ -1248,18 +1248,18 @@ export function init(config, {mkDelay = delay} = {}) { if (userSync) { ppidSource = userSync.ppid; if (userSync.userIds) { - const {autoRefresh = false, retainConfig = true, enforceStorageType} = userSync; + const { autoRefresh = false, retainConfig = true, enforceStorageType } = userSync; configRegistry = userSync.userIds; syncDelay = isNumber(userSync.syncDelay) ? userSync.syncDelay : USERSYNC_DEFAULT_CONFIG.syncDelay auctionDelay = isNumber(userSync.auctionDelay) ? userSync.auctionDelay : USERSYNC_DEFAULT_CONFIG.auctionDelay; - updateSubmodules({retainConfig, autoRefresh}); + updateSubmodules({ retainConfig, autoRefresh }); unregisterEnforceStorageTypeRule?.(); - unregisterEnforceStorageTypeRule = registerActivityControl(ACTIVITY_ACCESS_DEVICE, 'enforceStorageTypeRule', enforceStorageTypeRule(submodules.map(({config}) => config), enforceStorageType)); + unregisterEnforceStorageTypeRule = registerActivityControl(ACTIVITY_ACCESS_DEVICE, 'enforceStorageTypeRule', enforceStorageTypeRule(submodules.map(({ config }) => config), enforceStorageType)); updateIdPriority(userSync.idPriority, submoduleRegistry); - initIdSystem({ready: true}); + initIdSystem({ ready: true }); const submodulesToRefresh = submodules.filter(item => item.refreshIds); if (submodulesToRefresh.length) { - refreshUserIds({submoduleNames: submodulesToRefresh.map(item => item.submodule.name)}); + refreshUserIds({ submoduleNames: submodulesToRefresh.map(item => item.submodule.name) }); } } } @@ -1278,7 +1278,7 @@ export function init(config, {mkDelay = delay} = {}) { } export function resetUserIds() { - config.setConfig({userSync: {}}) + config.setConfig({ userSync: {} }) init(config); } diff --git a/modules/userId/spec.ts b/modules/userId/spec.ts index d3fbae835dd..6bcfaa6b288 100644 --- a/modules/userId/spec.ts +++ b/modules/userId/spec.ts @@ -1,8 +1,8 @@ -import type {BidderCode, StorageDisclosure} from "../../src/types/common"; -import {STORAGE_TYPE_COOKIES, STORAGE_TYPE_LOCALSTORAGE, type StorageType} from "../../src/storageManager.ts"; -import type {AllConsentData} from "../../src/consentHandler.ts"; -import type {Ext} from '../../src/types/ortb/common'; -import type {ORTBRequest} from "../../src/types/ortb/request"; +import type { BidderCode, StorageDisclosure } from "../../src/types/common"; +import { STORAGE_TYPE_COOKIES, STORAGE_TYPE_LOCALSTORAGE, type StorageType } from "../../src/storageManager.ts"; +import type { AllConsentData } from "../../src/consentHandler.ts"; +import type { Ext } from '../../src/types/ortb/common'; +import type { ORTBRequest } from "../../src/types/ortb/request"; export type UserIdProvider = string; diff --git a/modules/userId/userId.md b/modules/userId/userId.md index 8ffd8f83043..328e23d96b0 100644 --- a/modules/userId/userId.md +++ b/modules/userId/userId.md @@ -26,16 +26,6 @@ pbjs.setConfig({ name: "_pubcid", expires: 60 } - }, { - name: 'dmdId', - storage: { - name: 'dmd-dgid', - type: 'cookie', - expires: 30 - }, - params: { - api_key: '3fdbe297-3690-4f5c-9e11-ee9186a6d77c', // provided by DMD - } }, { name: "unifiedId", params: { @@ -91,6 +81,16 @@ pbjs.setConfig({ name: '_li_pbid', expires: 60 } + }, { + name: 'locId', + params: { + endpoint: 'https://id.example.com/locid' + }, + storage: { + type: 'html5', + name: '_locid', + expires: 7 + } }, { name: 'criteo', storage: { // It is best not to specify this parameter since the module needs to be called as many times as possible diff --git a/modules/validationFpdModule/index.ts b/modules/validationFpdModule/index.ts index 8b68540ed3d..2e964b2f6e7 100644 --- a/modules/validationFpdModule/index.ts +++ b/modules/validationFpdModule/index.ts @@ -2,10 +2,10 @@ * This module sets default values and validates ortb2 first part data * @module modules/firstPartyData */ -import {deepAccess, isEmpty, isNumber, logWarn} from '../../src/utils.js'; -import {ORTB_MAP} from './config.js'; -import {submodule} from '../../src/hook.js'; -import {getCoreStorageManager} from '../../src/storageManager.js'; +import { deepAccess, isEmpty, isNumber, logWarn } from '../../src/utils.js'; +import { ORTB_MAP } from './config.js'; +import { submodule } from '../../src/hook.js'; +import { getCoreStorageManager } from '../../src/storageManager.js'; // TODO: do FPD modules need their own namespace? const STORAGE = getCoreStorageManager('FPDValidation'); @@ -85,7 +85,7 @@ function typeValidation(data, mapping) { */ export function filterArrayData(arr, child, path, parent) { arr = arr.filter((index, i) => { - const check = typeValidation(index, {type: child.type, isArray: child.isArray}); + const check = typeValidation(index, { type: child.type, isArray: child.isArray }); if (check && Array.isArray(index) === Boolean(child.isArray)) { return true; @@ -157,7 +157,7 @@ export function validateFpd(fpd, path = '', parent = '') { }).filter(key => { const mapping = deepAccess(ORTB_MAP, path + key); // let typeBool = false; - const typeBool = (mapping) ? typeValidation(fpd[key], {type: mapping.type, isArray: mapping.isArray}) : true; + const typeBool = (mapping) ? typeValidation(fpd[key], { type: mapping.type, isArray: mapping.isArray }) : true; if (typeBool || !mapping) return key; diff --git a/modules/valuadBidAdapter.js b/modules/valuadBidAdapter.js index 6a32d80b806..31b320c2557 100644 --- a/modules/valuadBidAdapter.js +++ b/modules/valuadBidAdapter.js @@ -11,7 +11,8 @@ import { import { getGptSlotInfoForAdUnitCode } from '../libraries/gptUtils/gptUtils.js'; import { config } from '../src/config.js'; import { getBoundingBox, percentInView } from '../libraries/percentInView/percentInView.js'; -import {isIframe} from '../libraries/omsUtils/index.js'; +import { isIframe } from '../libraries/omsUtils/index.js'; +import { getAdUnitElement } from '../src/utils/adUnits.js'; const BIDDER_CODE = 'valuad'; const GVL_ID = 1478; @@ -104,9 +105,9 @@ const converter = ortbConverter({ if (!adSize) { adSize = [0, 0]; } - const size = {w: adSize[0], h: adSize[1]}; + const size = { w: adSize[0], h: adSize[1] }; - const element = document.getElementById(bid.adUnitCode) || document.getElementById(getGptSlotInfoForAdUnitCode(bid.adUnitCode)?.divId); + const element = getAdUnitElement(bid) || document.getElementById(getGptSlotInfoForAdUnitCode(bid.adUnitCode)?.divId); const viewabilityAmount = _isViewabilityMeasurable(element) ? _getViewability(element, getWindowTop(), size) : 0; const rect = element && getBoundingBox(element, size); @@ -197,7 +198,7 @@ function interpretResponse(response, request) { } // Restore original call, remove logging and safe navigation - const bidResponses = converter.fromORTB({response: response.body, request: request.data}).bids; + const bidResponses = converter.fromORTB({ response: response.body, request: request.data }).bids; return bidResponses; } diff --git a/modules/ventesBidAdapter.js b/modules/ventesBidAdapter.js index f79ed20514a..fa9f1557719 100644 --- a/modules/ventesBidAdapter.js +++ b/modules/ventesBidAdapter.js @@ -1,9 +1,9 @@ -import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js'; -import {isArray, isNumber, isPlainObject, isStr, replaceAuctionPrice} from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; +import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; +import { isArray, isNumber, isPlainObject, isStr, replaceAuctionPrice } from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; -import {convertCamelToUnderscore} from '../libraries/appnexusUtils/anUtils.js'; -import {hasUserInfo} from '../libraries/adrelevantisUtils/bidderUtils.js'; +import { convertCamelToUnderscore } from '../libraries/appnexusUtils/anUtils.js'; +import { hasUserInfo } from '../libraries/adrelevantisUtils/bidderUtils.js'; const BID_METHOD = 'POST'; const BIDDER_URL = 'https://ad.ventesavenues.in/va/ad'; @@ -96,7 +96,8 @@ function createServerRequestFromAdUnits(adUnits, bidRequestId, adUnitContext) { data: generateBidRequestsFromAdUnits(adUnits, bidRequestId, adUnitContext), options: { contentType: 'application/json', - withCredentials: false} + withCredentials: false + } } } diff --git a/modules/verbenBidAdapter.js b/modules/verbenBidAdapter.js new file mode 100644 index 00000000000..867f669e93e --- /dev/null +++ b/modules/verbenBidAdapter.js @@ -0,0 +1,17 @@ +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; +import { isBidRequestValid, buildRequests, interpretResponse } from '../libraries/teqblazeUtils/bidderUtils.js'; + +const BIDDER_CODE = 'verben'; +const AD_URL = 'https://east-node.verben.com/pbjs'; + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [BANNER, VIDEO, NATIVE], + + isBidRequestValid: isBidRequestValid(), + buildRequests: buildRequests(AD_URL), + interpretResponse +}; + +registerBidder(spec); diff --git a/modules/verbenBidAdapter.md b/modules/verbenBidAdapter.md new file mode 100644 index 00000000000..2d0978bc52f --- /dev/null +++ b/modules/verbenBidAdapter.md @@ -0,0 +1,79 @@ +# Overview + +``` +Module Name: Verben Bidder Adapter +Module Type: Verben Bidder Adapter +Maintainer: support_trading@verben.com +``` + +# Description + +Connects to Verben exchange for bids. +Verben bid adapter supports Banner, Video (instream and outstream) and Native. + +# Test Parameters +``` + var adUnits = [ + // Will return static test banner + { + code: 'adunit1', + mediaTypes: { + banner: { + sizes: [ [300, 250], [320, 50] ], + } + }, + bids: [ + { + bidder: 'verben', + params: { + placementId: 'testBanner', + } + } + ] + }, + { + code: 'addunit2', + mediaTypes: { + video: { + playerSize: [ [640, 480] ], + context: 'instream', + minduration: 5, + maxduration: 60, + } + }, + bids: [ + { + bidder: 'verben', + params: { + placementId: 'testVideo', + } + } + ] + }, + { + code: 'addunit3', + mediaTypes: { + native: { + title: { + required: true + }, + body: { + required: true + }, + icon: { + required: true, + size: [64, 64] + } + } + }, + bids: [ + { + bidder: 'verben', + params: { + placementId: 'testNative', + } + } + ] + } + ]; +``` diff --git a/modules/viantBidAdapter.js b/modules/viantBidAdapter.js index 7d621726adc..ee1be030ecb 100644 --- a/modules/viantBidAdapter.js +++ b/modules/viantBidAdapter.js @@ -1,8 +1,8 @@ -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; import * as utils from '../src/utils.js'; -import {ortbConverter} from '../libraries/ortbConverter/converter.js' -import {deepAccess, getBidIdParameter, logError} from '../src/utils.js'; +import { ortbConverter } from '../libraries/ortbConverter/converter.js' +import { deepAccess, getBidIdParameter, logError } from '../src/utils.js'; /** * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid @@ -44,9 +44,9 @@ export const spec = { interpretResponse(response, request) { if (!response.body) { - response.body = {nbr: 0}; + response.body = { nbr: 0 }; } - const bids = converter.fromORTB({request: request.data, response: response.body}).bids; + const bids = converter.fromORTB({ request: request.data, response: response.body }).bids; return bids; }, @@ -80,7 +80,7 @@ function buildRequests(bids, bidderRequest) { } function createRequest(bidRequests, bidderRequest, mediaType) { - const data = converter.toORTB({bidRequests, bidderRequest, context: {mediaType}}); + const data = converter.toORTB({ bidRequests, bidderRequest, context: { mediaType } }); if (bidderRequest.gdprConsent && typeof bidderRequest.gdprConsent.gdprApplies === 'boolean') { if (!data.regs) data.regs = {}; if (!data.regs.ext) data.regs.ext = {}; diff --git a/modules/vibrantmediaBidAdapter.js b/modules/vibrantmediaBidAdapter.js index 65c6eaa51c1..83f8eba34ff 100644 --- a/modules/vibrantmediaBidAdapter.js +++ b/modules/vibrantmediaBidAdapter.js @@ -7,10 +7,10 @@ import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; * Note: Only BANNER and VIDEO are currently supported by the prebid server. */ -import {getWinDimensions, logError, triggerPixel} from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js'; -import {OUTSTREAM} from '../src/video.js'; +import { getWinDimensions, logError, triggerPixel } from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; +import { OUTSTREAM } from '../src/video.js'; /** * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest diff --git a/modules/vidazooBidAdapter.js b/modules/vidazooBidAdapter.js index ed732a4814a..86044aef889 100644 --- a/modules/vidazooBidAdapter.js +++ b/modules/vidazooBidAdapter.js @@ -1,6 +1,6 @@ -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER, VIDEO} from '../src/mediaTypes.js'; -import {getStorageManager} from '../src/storageManager.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; +import { getStorageManager } from '../src/storageManager.js'; import { createSessionId, isBidRequestValid, @@ -12,13 +12,13 @@ import { createBuildRequestsFn, createInterpretResponseFn } from '../libraries/vidazooUtils/bidderUtils.js'; -import {OPT_CACHE_KEY, OPT_TIME_KEY} from '../libraries/vidazooUtils/constants.js'; +import { OPT_CACHE_KEY, OPT_TIME_KEY } from '../libraries/vidazooUtils/constants.js'; const GVLID = 744; const DEFAULT_SUB_DOMAIN = 'prebid'; const BIDDER_CODE = 'vidazoo'; const BIDDER_VERSION = '1.0.0'; -export const storage = getStorageManager({bidderCode: BIDDER_CODE}); +export const storage = getStorageManager({ bidderCode: BIDDER_CODE }); export const webSessionId = createSessionId(); export function createDomain(subDomain = DEFAULT_SUB_DOMAIN) { diff --git a/modules/videoModule/adQueue.js b/modules/videoModule/adQueue.js index a98bd742294..8c9ba15ea72 100644 --- a/modules/videoModule/adQueue.js +++ b/modules/videoModule/adQueue.js @@ -12,7 +12,7 @@ export function AdQueueCoordinator(videoCore, pbEvents) { function queueAd(adTagUrl, divId, options = {}) { const queue = storage[divId]; if (queue) { - queue.push({adTagUrl, options}); + queue.push({ adTagUrl, options }); triggerEvent(AUCTION_AD_LOAD_QUEUED, adTagUrl, options); } else { loadAd(divId, adTagUrl, options); diff --git a/modules/videoModule/index.ts b/modules/videoModule/index.ts index 938d95ae1fd..8d2daef5956 100644 --- a/modules/videoModule/index.ts +++ b/modules/videoModule/index.ts @@ -1,8 +1,8 @@ -import {config} from '../../src/config.js'; +import { config } from '../../src/config.js'; import * as events from '../../src/events.js'; -import {logError, logWarn, mergeDeep} from '../../src/utils.js'; -import {getGlobal} from '../../src/prebidGlobal.js'; -import {EVENTS} from '../../src/constants.js'; +import { logError, logWarn, mergeDeep } from '../../src/utils.js'; +import { getGlobal } from '../../src/prebidGlobal.js'; +import { EVENTS } from '../../src/constants.js'; import { AD_ERROR, AD_IMPRESSION, @@ -12,23 +12,23 @@ import { BID_IMPRESSION, videoEvents } from '../../libraries/video/constants/events.js' -import {PLACEMENT} from '../../libraries/video/constants/ortb.js'; -import {videoKey} from '../../libraries/video/constants/constants.js' -import {videoCoreFactory} from './coreVideo.js'; -import {gamSubmoduleFactory} from './gamAdServerSubmodule.js'; -import {videoImpressionVerifierFactory} from './videoImpressionVerifier.js'; -import {AdQueueCoordinator} from './adQueue.js'; -import {getExternalVideoEventName, getExternalVideoEventPayload} from '../../libraries/video/shared/helpers.js' -import {VIDEO} from '../../src/mediaTypes.js'; -import {auctionManager} from '../../src/auctionManager.js'; -import {doRender} from '../../src/adRendering.js'; -import {getHook} from '../../src/hook.js'; -import {type VideoBid} from '../../src/bidfactory.js'; -import type {BidderCode} from "../../src/types/common.d.ts"; -import type {ORTBImp, ORTBRequest} from "../../src/types/ortb/request.d.ts"; -import type {DeepPartial} from "../../src/types/objects.d.ts"; -import type {AdServerVendor} from "../../libraries/video/constants/vendorCodes.ts"; -import type {VideoEvent} from "../../libraries/video/constants/events.ts"; +import { PLACEMENT } from '../../libraries/video/constants/ortb.js'; +import { videoKey } from '../../libraries/video/constants/constants.js' +import { videoCoreFactory } from './coreVideo.js'; +import { gamSubmoduleFactory } from './gamAdServerSubmodule.js'; +import { videoImpressionVerifierFactory } from './videoImpressionVerifier.js'; +import { AdQueueCoordinator } from './adQueue.js'; +import { getExternalVideoEventName, getExternalVideoEventPayload } from '../../libraries/video/shared/helpers.js' +import { VIDEO } from '../../src/mediaTypes.js'; +import { auctionManager } from '../../src/auctionManager.js'; +import { doRender } from '../../src/adRendering.js'; +import { getHook } from '../../src/hook.js'; +import { type VideoBid } from '../../src/bidfactory.js'; +import type { BidderCode } from "../../src/types/common.d.ts"; +import type { ORTBImp, ORTBRequest } from "../../src/types/ortb/request.d.ts"; +import type { DeepPartial } from "../../src/types/objects.d.ts"; +import type { AdServerVendor } from "../../libraries/video/constants/vendorCodes.ts"; +import type { VideoEvent } from "../../libraries/video/constants/events.ts"; const allVideoEvents = Object.keys(videoEvents).map(eventKey => videoEvents[eventKey]); @@ -293,7 +293,7 @@ export function PbVideo(videoCore_, getConfig_, pbGlobal_, requestBids_, pbEvent function getWinningBid(adUnitCode) { const highestCpmBids = pbGlobal.getHighestCpmBids(adUnitCode); if (!highestCpmBids.length) { - pbEvents.emit(getExternalVideoEventName(AUCTION_AD_LOAD_ABORT), getExternalVideoEventPayload(AUCTION_AD_LOAD_ABORT, {adUnitCode})); + pbEvents.emit(getExternalVideoEventName(AUCTION_AD_LOAD_ABORT), getExternalVideoEventPayload(AUCTION_AD_LOAD_ABORT, { adUnitCode })); return; } return highestCpmBids.shift(); diff --git a/modules/videobyteBidAdapter.js b/modules/videobyteBidAdapter.js index e7eb6f8cd23..5e49b71ce7d 100644 --- a/modules/videobyteBidAdapter.js +++ b/modules/videobyteBidAdapter.js @@ -1,6 +1,6 @@ import { logMessage, logError, deepAccess, isFn, isPlainObject, isStr, isNumber, isArray, deepSetValue } from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {VIDEO} from '../src/mediaTypes.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { VIDEO } from '../src/mediaTypes.js'; /** * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest @@ -60,7 +60,7 @@ export const spec = { return; } return bidRequests.map(bidRequest => { - const {params} = bidRequest; + const { params } = bidRequest; let pubId = params.pubId; const placementId = params.placementId; const nId = params.nid; @@ -164,7 +164,7 @@ export const spec = { // BUILD REQUESTS: VIDEO function buildRequestData(bidRequest, bidderRequest) { - const {params} = bidRequest; + const { params } = bidRequest; const videoAdUnit = deepAccess(bidRequest, 'mediaTypes.video', {}); const videoBidderParams = deepAccess(bidRequest, 'params.video', {}); @@ -202,7 +202,7 @@ function buildRequestData(bidRequest, bidderRequest) { floorData = bidRequest.getFloor(bidFloorRequest); } else { if (params.bidfloor) { - floorData = {floor: params.bidfloor, currency: params.currency || 'USD'}; + floorData = { floor: params.bidfloor, currency: params.currency || 'USD' }; } } diff --git a/modules/videojsVideoProvider.js b/modules/videojsVideoProvider.js index 8a214f07a91..62cb6a9d730 100644 --- a/modules/videojsVideoProvider.js +++ b/modules/videojsVideoProvider.js @@ -50,7 +50,7 @@ export function VideojsProvider(providerConfig, vjs_, adState_, timeState_, call let player = null; let playerVersion = null; let playerIsSetup = false; - const {playerConfig, divId} = providerConfig; + const { playerConfig, divId } = providerConfig; let isMuted; let previousLastTimePosition = 0; let lastTimePosition = 0; @@ -141,7 +141,7 @@ export function VideojsProvider(providerConfig, vjs_, adState_, timeState_, call // sequence - TODO not yet supported maxextended: -1, boxingallowed: 1, - playbackmethod: [ playBackMethod ], + playbackmethod: [playBackMethod], playbackend: PLAYBACK_END.VIDEO_COMPLETION, // Per ortb 7.4 skip is omitted since neither the player nor ima plugin imposes a skip button, or a skipmin/max }; @@ -614,7 +614,7 @@ export const utils = { return params.adPluginConfig || {}; // TODO: add adPluginConfig to spec }, - getPositionCode: function({left, top, width, height}) { + getPositionCode: function({ left, top, width, height }) { const bottom = getWinDimensions().innerHeight - top - height; const right = getWinDimensions().innerWidth - left - width; diff --git a/modules/videonowBidAdapter.js b/modules/videonowBidAdapter.js index 563f692693a..107f9bd2643 100644 --- a/modules/videonowBidAdapter.js +++ b/modules/videonowBidAdapter.js @@ -1,6 +1,6 @@ -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER} from '../src/mediaTypes.js'; -import {_each, getBidIdParameter, getValue, logError, logInfo} from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER } from '../src/mediaTypes.js'; +import { _each, getBidIdParameter, getValue, logError, logInfo } from '../src/utils.js'; /** * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest @@ -18,7 +18,7 @@ export const spec = { code: BIDDER_CODE, url: RTB_URL, - supportedMediaTypes: [ BANNER ], + supportedMediaTypes: [BANNER], /** * Determines whether or not the given bid request is valid. diff --git a/modules/videoreachBidAdapter.js b/modules/videoreachBidAdapter.js index 29c74c0dc6a..8c77988652a 100644 --- a/modules/videoreachBidAdapter.js +++ b/modules/videoreachBidAdapter.js @@ -1,5 +1,5 @@ -import {getBidIdParameter, getValue} from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; +import { getBidIdParameter, getValue } from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; const BIDDER_CODE = 'videoreach'; const ENDPOINT_URL = 'https://a.videoreach.com/hb/'; diff --git a/modules/vidoomyBidAdapter.js b/modules/vidoomyBidAdapter.js index 92e11a4dece..94aeb2f3798 100644 --- a/modules/vidoomyBidAdapter.js +++ b/modules/vidoomyBidAdapter.js @@ -1,9 +1,9 @@ -import {deepAccess, isPlainObject, logError, parseSizesInput} from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER, VIDEO} from '../src/mediaTypes.js'; -import {config} from '../src/config.js'; -import {Renderer} from '../src/Renderer.js'; -import {INSTREAM, OUTSTREAM} from '../src/video.js'; +import { deepAccess, isPlainObject, logError, parseSizesInput } from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; +import { config } from '../src/config.js'; +import { Renderer } from '../src/Renderer.js'; +import { INSTREAM, OUTSTREAM } from '../src/video.js'; const ENDPOINT = `https://d.vidoomy.com/api/rtbserver/prebid/`; const BIDDER_CODE = 'vidoomy'; @@ -87,7 +87,7 @@ function getBidFloor(bid, mediaType, sizes, bidfloor) { let floor = bidfloor; var size = sizes && sizes.length > 0 ? sizes[0] : '*'; if (typeof bid.getFloor === 'function') { - const floorInfo = bid.getFloor({currency: 'USD', mediaType, size}); + const floorInfo = bid.getFloor({ currency: 'USD', mediaType, size }); if (isPlainObject(floorInfo) && floorInfo.currency === 'USD' && !isNaN(parseFloat(floorInfo.floor))) { floor = Math.max(bidfloor, parseFloat(floorInfo.floor)); } diff --git a/modules/viewdeosDXBidAdapter.js b/modules/viewdeosDXBidAdapter.js index 813072a1dbe..6595f9e9a2c 100644 --- a/modules/viewdeosDXBidAdapter.js +++ b/modules/viewdeosDXBidAdapter.js @@ -1,7 +1,7 @@ -import {deepAccess, flatten, isArray, logError, parseSizesInput} from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {VIDEO} from '../src/mediaTypes.js'; -import {Renderer} from '../src/Renderer.js'; +import { deepAccess, flatten, isArray, logError, parseSizesInput } from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { VIDEO } from '../src/mediaTypes.js'; +import { Renderer } from '../src/Renderer.js'; import { getUserSyncsFn, isBidRequestValid, @@ -48,7 +48,7 @@ export const spec = { * @param {BidderRequest} bidderRequest * @return {Bid[]} An array of bids which were nested inside the server */ - interpretResponse: function (serverResponse, {bidderRequest}) { + interpretResponse: function (serverResponse, { bidderRequest }) { serverResponse = serverResponse.body; let bids = []; diff --git a/modules/vistarsBidAdapter.js b/modules/vistarsBidAdapter.js index 07fb10184c7..33ada9c3266 100644 --- a/modules/vistarsBidAdapter.js +++ b/modules/vistarsBidAdapter.js @@ -62,7 +62,7 @@ export const spec = { return []; } - const bids = converter.fromORTB({response: response.body, request: request.data}).bids; + const bids = converter.fromORTB({ response: response.body, request: request.data }).bids; bids.forEach((bid) => { bid.meta = bid.meta || {}; bid.meta.advertiserDomains = bid.meta.advertiserDomains || []; @@ -78,7 +78,7 @@ export const spec = { getUserSyncs: getUserSyncs(SYNC_ENDPOINT), - supportedMediaTypes: [ BANNER, VIDEO ] + supportedMediaTypes: [BANNER, VIDEO] } registerBidder(spec); diff --git a/modules/visxBidAdapter.js b/modules/visxBidAdapter.js index 801c68acc2d..bc7f72833f3 100644 --- a/modules/visxBidAdapter.js +++ b/modules/visxBidAdapter.js @@ -1,12 +1,13 @@ -import {deepAccess, logError, mergeDeep, parseSizesInput, sizeTupleToRtbSize, sizesToSizeTuples, triggerPixel} from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {config} from '../src/config.js'; -import {BANNER, VIDEO} from '../src/mediaTypes.js'; -import {INSTREAM as VIDEO_INSTREAM} from '../src/video.js'; -import {getStorageManager} from '../src/storageManager.js'; -import {getGptSlotInfoForAdUnitCode} from '../libraries/gptUtils/gptUtils.js'; +import { deepAccess, logError, mergeDeep, parseSizesInput, sizeTupleToRtbSize, sizesToSizeTuples, triggerPixel } from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { config } from '../src/config.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; +import { INSTREAM as VIDEO_INSTREAM } from '../src/video.js'; +import { getStorageManager } from '../src/storageManager.js'; +import { getGptSlotInfoForAdUnitCode } from '../libraries/gptUtils/gptUtils.js'; import { getBidFromResponse } from '../libraries/processResponse/index.js'; import { getCurrencyFromBidderRequest } from '../libraries/ortb2Utils/currency.js'; +import { getAdUnitElement } from '../src/utils/adUnits.js'; const BIDDER_CODE = 'visx'; const GVLID = 154; @@ -35,7 +36,7 @@ const LOG_ERROR_MESS = { videoMissing: 'Bid request videoType property is missing - ' }; const currencyWhiteList = ['EUR', 'USD', 'GBP', 'PLN', 'CHF', 'SEK']; -export const storage = getStorageManager({bidderCode: BIDDER_CODE}); +export const storage = getStorageManager({ bidderCode: BIDDER_CODE }); const _bidResponseTimeLogged = []; export const spec = { code: BIDDER_CODE, @@ -178,7 +179,7 @@ export const spec = { cur: [currency], source, ...(payloadUser && { user: payloadUser }), - ...(payloadRegs && {regs: payloadRegs}), + ...(payloadRegs && { regs: payloadRegs }), ...(payloadDevice && { device: payloadDevice }), ...(payloadSite && { site: payloadSite }), ...(payloadContent && { content: payloadContent }), @@ -295,7 +296,7 @@ function makeVideo(videoParams = {}) { } function buildImpObject(bid) { - const { params: { uid }, bidId, mediaTypes, sizes, adUnitCode } = bid; + const { params: { uid }, bidId, mediaTypes, sizes } = bid; const video = mediaTypes && _isVideoBid(bid) && _isValidVideoBid(bid) && makeVideo(mediaTypes.video); const banner = makeBanner((mediaTypes && mediaTypes.banner) || (!video && { sizes })); const impObject = { @@ -308,7 +309,7 @@ function buildImpObject(bid) { }; if (impObject.banner) { - impObject.ext.bidder.adslotExists = _isAdSlotExists(adUnitCode); + impObject.ext.bidder.adslotExists = _isAdSlotExists(bid); } if (bid.ortb2Imp?.ext?.gpid) { @@ -410,12 +411,12 @@ function _isValidVideoBid(bid, logErrors = false) { return result; } -function _isAdSlotExists(adUnitCode) { - if (document.getElementById(adUnitCode)) { +function _isAdSlotExists(bidRequest) { + if (getAdUnitElement(bidRequest)) { return true; } - const gptAdSlot = getGptSlotInfoForAdUnitCode(adUnitCode); + const gptAdSlot = getGptSlotInfoForAdUnitCode(bidRequest.adUnitCode); if (gptAdSlot.divId && document.getElementById(gptAdSlot.divId)) { return true; } diff --git a/modules/voxBidAdapter.js b/modules/voxBidAdapter.js index 8fb6574548c..e7e03172a28 100644 --- a/modules/voxBidAdapter.js +++ b/modules/voxBidAdapter.js @@ -1,8 +1,8 @@ -import {_map, isArray} from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER, VIDEO} from '../src/mediaTypes.js'; +import { _map, isArray } from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; import { getCurrencyFromBidderRequest } from '../libraries/ortb2Utils/currency.js'; -import {createRenderer, getMediaTypeFromBid, hasVideoMandatoryParams} from '../libraries/hybridVoxUtils/index.js'; +import { createRenderer, getMediaTypeFromBid, hasVideoMandatoryParams } from '../libraries/hybridVoxUtils/index.js'; /** * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest @@ -54,8 +54,7 @@ function buildBid(bidData) { mediaType: BANNER, ttl: TTL, content: bidData.content, - meta: { - advertiserDomains: bidData.advertiserDomains || []} + meta: { advertiserDomains: bidData.advertiserDomains || [] } }; if (bidData.placement === 'video') { diff --git a/modules/vrtcalBidAdapter.js b/modules/vrtcalBidAdapter.js index 07dc35525c3..bd0dd9dc243 100644 --- a/modules/vrtcalBidAdapter.js +++ b/modules/vrtcalBidAdapter.js @@ -1,8 +1,8 @@ -import {registerBidder} from '../src/adapters/bidderFactory.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER } from '../src/mediaTypes.js'; -import {ajax} from '../src/ajax.js'; +import { ajax } from '../src/ajax.js'; import { config } from '../src/config.js'; -import {deepAccess, isFn, isPlainObject} from '../src/utils.js'; +import { deepAccess, isFn, isPlainObject } from '../src/utils.js'; const GVLID = 706; const VRTCAL_USER_SYNC_URL_IFRAME = `https://usync.vrtcal.com/i?ssp=1804&synctype=iframe`; @@ -20,7 +20,7 @@ export const spec = { let floor = 0; if (isFn(bid.getFloor)) { - const floorInfo = bid.getFloor({ currency: 'USD', mediaType: 'banner', size: bid.sizes.map(([w, h]) => ({w, h})) }); + const floorInfo = bid.getFloor({ currency: 'USD', mediaType: 'banner', size: bid.sizes.map(([w, h]) => ({ w, h })) }); if (isPlainObject(floorInfo) && floorInfo.currency === 'USD' && !isNaN(parseFloat(floorInfo.floor))) { floor = Math.max(floor, parseFloat(floorInfo.floor)); @@ -104,7 +104,7 @@ export const spec = { params.regs.ext.gpp_sid = bid.ortb2.regs.gpp_sid; } - return {method: 'POST', url: 'https://rtb.vrtcal.com/bidder_prebid.vap?ssp=1804', data: JSON.stringify(params), options: {withCredentials: false, crossOrigin: true}}; + return { method: 'POST', url: 'https://rtb.vrtcal.com/bidder_prebid.vap?ssp=1804', data: JSON.stringify(params), options: { withCredentials: false, crossOrigin: true } }; }); return requests; diff --git a/modules/vuukleBidAdapter.js b/modules/vuukleBidAdapter.js index 6dd5709fe78..371cf002473 100644 --- a/modules/vuukleBidAdapter.js +++ b/modules/vuukleBidAdapter.js @@ -11,7 +11,7 @@ const VENDOR_ID = 1004; export const spec = { code: BIDDER_CODE, gvlid: VENDOR_ID, - supportedMediaTypes: [ BANNER ], + supportedMediaTypes: [BANNER], isBidRequestValid: function(bid) { return true @@ -56,7 +56,7 @@ export const spec = { method: 'GET', url: URL, data: params, - options: {withCredentials: false} + options: { withCredentials: false } } }); diff --git a/modules/waardexBidAdapter.js b/modules/waardexBidAdapter.js index 7846d12af32..138cbd54601 100644 --- a/modules/waardexBidAdapter.js +++ b/modules/waardexBidAdapter.js @@ -1,7 +1,7 @@ -import {deepAccess, getBidIdParameter, isArray, logError} from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER, VIDEO} from '../src/mediaTypes.js'; -import {config} from '../src/config.js'; +import { deepAccess, getBidIdParameter, isArray, logError } from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; +import { config } from '../src/config.js'; const ENDPOINT = `https://hb.justbidit2.xyz:8843/prebid`; const BIDDER_CODE = 'waardex'; @@ -58,13 +58,14 @@ const buildRequests = (validBidRequests, bidderRequest) => { zoneId = +validBidRequests[0].params.zoneId; } - return {method: 'POST', url: `${ENDPOINT}?pubId=${zoneId}`, data: dataToSend}; + return { method: 'POST', url: `${ENDPOINT}?pubId=${zoneId}`, data: dataToSend }; }; const getCommonBidsData = bidderRequest => { const payload = { ua: navigator.userAgent || '', - language: navigator.language && navigator.language.indexOf('-') !== -1 ? navigator.language.split('-')[0] : ''}; + language: navigator.language && navigator.language.indexOf('-') !== -1 ? navigator.language.split('-')[0] : '' + }; if (bidderRequest && bidderRequest.refererInfo) { // TODO: is 'page' the right value here? diff --git a/modules/weboramaRtdProvider.js b/modules/weboramaRtdProvider.js index bf7f7a043f9..ddda05699f5 100644 --- a/modules/weboramaRtdProvider.js +++ b/modules/weboramaRtdProvider.js @@ -188,6 +188,7 @@ class WeboramaRtdProvider { constructor(components) { this.#components = components; } + /** * Initialize module * @function diff --git a/modules/widespaceBidAdapter.js b/modules/widespaceBidAdapter.js index 7a8dec47a28..51bd244009d 100644 --- a/modules/widespaceBidAdapter.js +++ b/modules/widespaceBidAdapter.js @@ -1,9 +1,10 @@ -import {config} from '../src/config.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {deepClone, parseQueryStringParameters, parseSizesInput} from '../src/utils.js'; -import {getStorageManager} from '../src/storageManager.js'; +import { config } from '../src/config.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { deepClone, parseQueryStringParameters, parseSizesInput } from '../src/utils.js'; +import { getStorageManager } from '../src/storageManager.js'; import { getBoundingClientRect } from '../libraries/boundingClientRect/boundingClientRect.js'; import { getConnectionInfo } from '../libraries/connectionInfo/connectionUtils.js'; +import { getAdUnitElement } from '../src/utils/adUnits.js'; const BIDDER_CODE = 'widespace'; const WS_ADAPTER_VERSION = '2.0.1'; @@ -12,7 +13,7 @@ const LS_KEYS = { LC_UID: 'wsLcuid', CUST_DATA: 'wsCustomData' }; -export const storage = getStorageManager({bidderCode: BIDDER_CODE}); +export const storage = getStorageManager({ bidderCode: BIDDER_CODE }); let preReqTime = 0; @@ -53,7 +54,7 @@ export const spec = { 'inFrame': 1, 'sid': bid.params.sid, 'lcuid': LC_UID, - 'vol': isInHostileIframe ? '' : visibleOnLoad(document.getElementById(bid.adUnitCode)), + 'vol': isInHostileIframe ? '' : visibleOnLoad(getAdUnitElement(bid)), 'gdprCmp': bidderRequest && bidderRequest.gdprConsent ? 1 : 0, 'hb': '1', 'hb.cd': CUST_DATA ? encodedParamValue(CUST_DATA) : '', @@ -98,7 +99,7 @@ export const spec = { // GDPR Consent info if (data.gdprCmp) { - const {gdprApplies, consentString, vendorData} = bidderRequest.gdprConsent; + const { gdprApplies, consentString, vendorData } = bidderRequest.gdprConsent; const hasGlobalScope = vendorData && vendorData.hasGlobalScope; data.gdprApplies = gdprApplies ? 1 : gdprApplies === undefined ? '' : 0; data.gdprConsentData = consentString; @@ -162,7 +163,7 @@ export const spec = { userSyncs = serverResponses.reduce((allSyncPixels, response) => { if (response && response.body && response.body[0]) { (response.body[0].syncPixels || []).forEach((url) => { - allSyncPixels.push({type: 'image', url}); + allSyncPixels.push({ type: 'image', url }); }); } return allSyncPixels; diff --git a/modules/winrBidAdapter.js b/modules/winrBidAdapter.js index ee90307eb89..1d5cd60da70 100644 --- a/modules/winrBidAdapter.js +++ b/modules/winrBidAdapter.js @@ -7,15 +7,15 @@ import { isPlainObject, logError } from '../src/utils.js'; -import {config} from '../src/config.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER} from '../src/mediaTypes.js'; -import {getStorageManager} from '../src/storageManager.js'; -import {hasPurpose1Consent} from '../src/utils/gdpr.js'; -import {getANKeywordParam} from '../libraries/appnexusUtils/anKeywords.js'; -import {convertCamelToUnderscore} from '../libraries/appnexusUtils/anUtils.js'; +import { config } from '../src/config.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER } from '../src/mediaTypes.js'; +import { getStorageManager } from '../src/storageManager.js'; +import { hasPurpose1Consent } from '../src/utils/gdpr.js'; +import { getANKeywordParam } from '../libraries/appnexusUtils/anKeywords.js'; +import { convertCamelToUnderscore } from '../libraries/appnexusUtils/anUtils.js'; import { transformSizes } from '../libraries/sizeUtils/tranformSize.js'; -import {addUserId, hasUserInfo, hasAppDeviceInfo, hasAppId, getBidFloor} from '../libraries/adrelevantisUtils/bidderUtils.js'; +import { addUserId, hasUserInfo, hasAppDeviceInfo, hasAppId, getBidFloor } from '../libraries/adrelevantisUtils/bidderUtils.js'; /** * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest @@ -31,7 +31,7 @@ const SOURCE = 'pbjs'; const DEFAULT_CURRENCY = 'USD'; const GATE_COOKIE_NAME = 'wnr_gate'; -export const storage = getStorageManager({bidderCode: BIDDER_CODE}); +export const storage = getStorageManager({ bidderCode: BIDDER_CODE }); function buildBid(bidData) { const bid = bidData; diff --git a/modules/wipesBidAdapter.js b/modules/wipesBidAdapter.js index 56a4aeecd71..3a53638c908 100644 --- a/modules/wipesBidAdapter.js +++ b/modules/wipesBidAdapter.js @@ -1,6 +1,6 @@ -import {logWarn} from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER} from '../src/mediaTypes.js'; +import { logWarn } from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER } from '../src/mediaTypes.js'; const BIDDER_CODE = 'wipes'; const ALIAS_BIDDER_CODE = ['wi']; diff --git a/modules/wurflRtdProvider.js b/modules/wurflRtdProvider.js index dd88ea567eb..a87691c297b 100644 --- a/modules/wurflRtdProvider.js +++ b/modules/wurflRtdProvider.js @@ -13,7 +13,7 @@ import { getGlobal } from '../src/prebidGlobal.js'; // Constants const REAL_TIME_MODULE = 'realTimeData'; const MODULE_NAME = 'wurfl'; -const MODULE_VERSION = '2.4.0'; +const MODULE_VERSION = '2.7.0'; // WURFL_JS_HOST is the host for the WURFL service endpoints const WURFL_JS_HOST = 'https://prebid.wurflcloud.com'; @@ -50,6 +50,7 @@ const ORTB2_DEVICE_FIELDS = [ const ENRICHMENT_TYPE = { UNKNOWN: 'unknown', NONE: 'none', + NONE_LCE: 'none_lce', LCE: 'lce', LCE_ERROR: 'lcefailed', WURFL_PUB: 'wurfl_pub', @@ -76,8 +77,6 @@ const AB_TEST = { TREATMENT_GROUP: 'treatment', DEFAULT_SPLIT: 0.5, DEFAULT_NAME: 'unknown', - ENRICHMENT_TYPE_LCE: 'lce', - ENRICHMENT_TYPE_WURFL: 'wurfl' }; const logger = prefixLog('[WURFL RTD Submodule]'); @@ -106,6 +105,9 @@ let tier; // overQuota stores the over_quota flag from wurfl_pbjs data (possible values: 0, 1) let overQuota; +// cachedSUA holds the ORTB 2.6 SUA from Prebid's enrichment pipeline +let cachedSUA = null; + /** * Safely gets an object from localStorage with JSON parsing * @param {string} key The storage key @@ -286,22 +288,10 @@ function loadWurflJsAsync(config, bidders) { } }; - // Collect Client Hints if available, then load script - if (navigator?.userAgentData?.getHighEntropyValues) { - const hints = ['architecture', 'bitness', 'model', 'platformVersion', 'uaFullVersion', 'fullVersionList']; - navigator.userAgentData.getHighEntropyValues(hints) - .then(ch => { - if (ch !== null) { - url.searchParams.set('uach', JSON.stringify(ch)); - } - }) - .finally(() => { - loadWurflJs(url.toString()); - }); - } else { - // Load script immediately when Client Hints not available - loadWurflJs(url.toString()); + if (cachedSUA) { + url.searchParams.set('sua', JSON.stringify(cachedSUA)); } + loadWurflJs(url.toString()); } /** @@ -1023,8 +1013,6 @@ const ABTestManager = { _enabled: false, _name: null, _variant: null, - _excludeLCE: true, - _enrichmentType: null, /** * Initializes A/B test configuration @@ -1034,8 +1022,6 @@ const ABTestManager = { this._enabled = false; this._name = null; this._variant = null; - this._excludeLCE = true; - this._enrichmentType = null; const abTestEnabled = params?.abTest ?? false; if (!abTestEnabled) { @@ -1044,12 +1030,11 @@ const ABTestManager = { this._enabled = true; this._name = params?.abName ?? AB_TEST.DEFAULT_NAME; - this._excludeLCE = params?.abExcludeLCE ?? true; const split = params?.abSplit ?? AB_TEST.DEFAULT_SPLIT; this._variant = this._computeVariant(split); - logger.logMessage(`A/B test "${this._name}": user in ${this._variant} group (exclude_lce: ${this._excludeLCE})`); + logger.logMessage(`A/B test "${this._name}": user in ${this._variant} group`); }, /** @@ -1067,24 +1052,12 @@ const ABTestManager = { return Math.random() < split ? AB_TEST.TREATMENT_GROUP : AB_TEST.CONTROL_GROUP; }, - /** - * Sets the enrichment type encountered in current auction - * @param {string} enrichmentType 'lce' or 'wurfl' - */ - setEnrichmentType(enrichmentType) { - this._enrichmentType = enrichmentType; - }, - /** * Checks if A/B test is enabled for current auction * @returns {boolean} True if A/B test should be applied */ isEnabled() { - if (!this._enabled) return false; - if (this._enrichmentType === AB_TEST.ENRICHMENT_TYPE_LCE && this._excludeLCE) { - return false; - } - return true; + return this._enabled; }, /** @@ -1134,6 +1107,7 @@ const init = (config, userConsent) => { samplingRate = DEFAULT_SAMPLING_RATE; tier = ''; overQuota = DEFAULT_OVER_QUOTA; + cachedSUA = null; logger.logMessage('initialized', { version: MODULE_VERSION }); @@ -1163,19 +1137,29 @@ const getBidRequestData = (reqBidsConfigObj, callback, config, userConsent) => { }); }); + // Read SUA from Prebid's enrichment pipeline (already resolved, publisher-controlled hints) + cachedSUA = reqBidsConfigObj.ortb2Fragments?.global?.device?.sua || null; + // Determine enrichment type based on cache availability WurflDebugger.cacheReadStart(); const cachedWurflData = getObjectFromStorage(WURFL_RTD_STORAGE_KEY); WurflDebugger.cacheReadStop(); - const abEnrichmentType = cachedWurflData ? AB_TEST.ENRICHMENT_TYPE_WURFL : AB_TEST.ENRICHMENT_TYPE_LCE; - ABTestManager.setEnrichmentType(abEnrichmentType); - // A/B test: Skip enrichment for control group if (ABTestManager.isInControlGroup()) { logger.logMessage('A/B test control group: skipping enrichment'); - enrichmentType = ENRICHMENT_TYPE.NONE; - bidders.forEach(bidder => bidderEnrichment.set(bidder, ENRICHMENT_TYPE.NONE)); + const controlEnrichment = cachedWurflData ? ENRICHMENT_TYPE.NONE : ENRICHMENT_TYPE.NONE_LCE; + enrichmentType = controlEnrichment; + bidders.forEach(bidder => bidderEnrichment.set(bidder, controlEnrichment)); + + // Read cache metadata for beacon reporting (without enriching bid request) + if (cachedWurflData) { + wurflId = cachedWurflData.WURFL?.wurfl_id || ''; + samplingRate = cachedWurflData.wurfl_pbjs?.sampling_rate ?? DEFAULT_SAMPLING_RATE; + tier = cachedWurflData.wurfl_pbjs?.tier ?? ''; + overQuota = cachedWurflData.wurfl_pbjs?.over_quota ?? DEFAULT_OVER_QUOTA; + } + WurflDebugger.moduleExecutionStop(); callback(); return; @@ -1391,7 +1375,8 @@ function onAuctionEndEvent(auctionDetails, config, userConsent) { tier: tier, over_quota: overQuota, consent_class: consentClass, - ad_units: adUnits + ad_units: adUnits, + sua: cachedSUA }; // Add A/B test fields if enabled @@ -1433,5 +1418,10 @@ export const wurflSubmodule = { onAuctionEndEvent, } +// Exported for testing only +export const __testing__ = { + setCachedSUA: (value) => { cachedSUA = value; }, +}; + // Register the WURFL submodule as submodule of realTimeData submodule(REAL_TIME_MODULE, wurflSubmodule); diff --git a/modules/wurflRtdProvider.md b/modules/wurflRtdProvider.md index 30651a6ddea..4bc088303e0 100644 --- a/modules/wurflRtdProvider.md +++ b/modules/wurflRtdProvider.md @@ -8,10 +8,11 @@ ## Description -The WURFL RTD module enriches the OpenRTB 2.0 device data with [WURFL data](https://www.scientiamobile.com/wurfl-js-business-edition-at-the-intersection-of-javascript-and-enterprise/). -The module sets the WURFL data in `device.ext.wurfl` and all the bidder adapters will always receive the low entry capabilities like `is_mobile`, `complete_device_name` and `form_factor`, and the `wurfl_id`. +The WURFL RTD module enriches Prebid.js bid requests with comprehensive device detection data. -For a more detailed analysis bidders can subscribe to detect iPhone and iPad models and receive additional [WURFL device capabilities](https://www.scientiamobile.com/capabilities/?products%5B%5D=wurfl-js). +The WURFL RTD module relies on localStorage caching and local client-side detection, providing instant device enrichment on every page load. + +The module enriches `ortb2.device` with complete device information and adds extended WURFL capabilities to `device.ext.wurfl`, ensuring all bidder adapters have immediate access to enriched device data. **Note:** This module loads a dynamically generated JavaScript from prebid.wurflcloud.com @@ -34,10 +35,8 @@ Use `setConfig` to instruct Prebid.js to initilize the WURFL RTD module, as spec This module is configured as part of the `realTimeData.dataProviders` ```javascript -var TIMEOUT = 1000; pbjs.setConfig({ realTimeData: { - auctionDelay: TIMEOUT, dataProviders: [ { name: "wurfl", @@ -49,16 +48,14 @@ pbjs.setConfig({ ### Parameters -| Name | Type | Description | Default | -| :------------------ | :------ | :--------------------------------------------------------------- | :------------- | -| name | String | Real time data module name | Always 'wurfl' | -| waitForIt | Boolean | Should be `true` if there's an `auctionDelay` defined (optional) | `false` | -| params | Object | | | -| params.altHost | String | Alternate host to connect to WURFL.js | | -| params.abTest | Boolean | Enable A/B testing mode | `false` | -| params.abName | String | A/B test name identifier | `'unknown'` | -| params.abSplit | Number | Fraction of users in treatment group (0-1) | `0.5` | -| params.abExcludeLCE | Boolean | Don't apply A/B testing to LCE bids | `true` | +| Name | Type | Description | Default | +| :------------- | :------ | :----------------------------------------- | :------------- | +| name | String | Real time data module name | Always 'wurfl' | +| params | Object | | | +| params.altHost | String | Alternate host to connect to WURFL.js | | +| params.abTest | Boolean | Enable A/B testing mode | `false` | +| params.abName | String | A/B test name identifier | `'unknown'` | +| params.abSplit | Number | Fraction of users in treatment group (0-1) | `0.5` | ### A/B Testing @@ -67,15 +64,13 @@ The WURFL RTD module supports A/B testing to measure the impact of WURFL enrichm ```javascript pbjs.setConfig({ realTimeData: { - auctionDelay: 1000, dataProviders: [ { name: "wurfl", - waitForIt: true, params: { abTest: true, - abName: "pub_test_sept23", - abSplit: 0.5, // 50% treatment, 50% control + abName: "pub_test", + abSplit: 0.75, // 75% treatment, 25% control }, }, ], diff --git a/modules/xeBidAdapter.js b/modules/xeBidAdapter.js index 9bd6f8adbac..8589a851c8b 100644 --- a/modules/xeBidAdapter.js +++ b/modules/xeBidAdapter.js @@ -1,6 +1,6 @@ -import {BANNER, VIDEO} from '../src/mediaTypes.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {buildRequests, getUserSyncs, interpretResponse, isBidRequestValid} from '../libraries/xeUtils/bidderUtils.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { buildRequests, getUserSyncs, interpretResponse, isBidRequestValid } from '../libraries/xeUtils/bidderUtils.js'; const BIDDER_CODE = 'xe'; const ENDPOINT = 'https://pbjs.xe.works/bid'; diff --git a/modules/yahooAdsBidAdapter.js b/modules/yahooAdsBidAdapter.js index b2b92c6ba95..45f6726633c 100644 --- a/modules/yahooAdsBidAdapter.js +++ b/modules/yahooAdsBidAdapter.js @@ -3,7 +3,7 @@ import { BANNER, VIDEO } from '../src/mediaTypes.js'; import { deepAccess, isFn, isStr, isNumber, isArray, isEmpty, isPlainObject, generateUUID, logInfo, logWarn } from '../src/utils.js'; import { config } from '../src/config.js'; import { Renderer } from '../src/Renderer.js'; -import {hasPurpose1Consent} from '../src/utils/gdpr.js'; +import { hasPurpose1Consent } from '../src/utils/gdpr.js'; const INTEGRATION_METHOD = 'prebid.js'; const BIDDER_CODE = 'yahooAds'; @@ -71,7 +71,7 @@ function getSize(size) { function transformSizes(sizes) { if (isArray(sizes) && sizes.length === 2 && !isArray(sizes[0])) { - return [ getSize(sizes) ]; + return [getSize(sizes)]; } return sizes.map(getSize); } @@ -288,6 +288,7 @@ function generateOpenRtbObject(bidderRequest, bid) { } }, source: { + tid: bidderRequest.ortb2?.source?.tid, ext: { hb: 1, adapterver: ADAPTER_VERSION, @@ -505,7 +506,7 @@ function appendFirstPartyData(outBoundBidRequest, bid) { return outBoundBidRequest; }; -function generateServerRequest({payload, requestOptions, bidderRequest}) { +function generateServerRequest({ payload, requestOptions, bidderRequest }) { const pubIdMode = getPubIdMode(bidderRequest); const overrideEndpoint = getConfigValue(bidderRequest, 'endpoint'); let sspEndpoint = overrideEndpoint || SSP_ENDPOINT_DCN_POS; @@ -612,13 +613,13 @@ export const spec = { filteredBidRequests.forEach(bid => { appendImpObject(bid, payload); }); - return [generateServerRequest({payload, requestOptions, bidderRequest})]; + return [generateServerRequest({ payload, requestOptions, bidderRequest })]; } return filteredBidRequests.map(bid => { const payloadClone = generateOpenRtbObject(bidderRequest, bid); appendImpObject(bid, payloadClone); - return generateServerRequest({payload: payloadClone, requestOptions, bidderRequest: bid}); + return generateServerRequest({ payload: payloadClone, requestOptions, bidderRequest: bid }); }); }, diff --git a/modules/yahooAdsBidAdapter.md b/modules/yahooAdsBidAdapter.md index df9b71b2314..a26d24338c5 100644 --- a/modules/yahooAdsBidAdapter.md +++ b/modules/yahooAdsBidAdapter.md @@ -18,6 +18,7 @@ The Yahoo Advertising Bid Adapter is an OpenRTB interface that consolidates all * User ID Modules - ConnectId and others * First Party Data (ortb2 & ortb2Imp) * Custom TTL (time to live) +* Transaction ID (TID) support via ortb2.source.tid # Adapter Aliases Whilst the primary bidder code for this bid adapter is `yahooAds`, the aliases `yahoossp` and `yahooAdvertising` can be used to enable this adapter. If you wish to set Prebid configuration specifically for this bid adapter, then the configuration key _must_ match the used bidder code. All examples in this documentation use the primiry bidder code, but switching `yahooAds` with one of the relevant aliases may be required for your setup. Let's take [setting the request mode](#adapter-request-mode) as an example; if you used the `yahoossp` alias, then the corresponding `setConfig` API call would look like this: @@ -562,6 +563,40 @@ const adUnits = [{ ] ``` +## Transaction ID (TID) Support +The Yahoo Advertising bid adapter supports reading publisher-provided transaction IDs from `ortb2.source.tid` and including them in the OpenRTB request. This enables better bid request tracking and deduplication across the supply chain. + +**Important:** To use transaction IDs, you must enable TIDs in your Prebid configuration by setting `enableTIDs: true`. + +### Global Transaction ID (applies to all bidders) +```javascript +pbjs.setConfig({ + enableTIDs: true, // Required for TID support + ortb2: { + source: { + tid: "transaction-id-12345" + } + } +}); +``` + +### Bidder-Specific Transaction ID (Yahoo Ads only) +```javascript +pbjs.setBidderConfig({ + bidders: ['yahooAds'], + config: { + enableTIDs: true, // Required for TID support + ortb2: { + source: { + tid: "yahoo-specific-tid-67890" + } + } + } +}); +``` + +**Note:** If `enableTIDs` is not set to `true`, the transaction ID will not be available to the adapter, even if `ortb2.source.tid` is configured. When TID is not provided or not enabled, the adapter will not include the `source.tid` field in the OpenRTB request. + # Optional: Bidder bidOverride Parameters The Yahoo Advertising bid adapter allows passing override data to the outbound bid-request that overrides First Party Data. **Important!** We highly recommend using prebid modules to pass data instead of bidder speicifc overrides. diff --git a/modules/yaleoBidAdapter.md b/modules/yaleoBidAdapter.md new file mode 100644 index 00000000000..505d3617fd5 --- /dev/null +++ b/modules/yaleoBidAdapter.md @@ -0,0 +1,39 @@ +# Yaleo Bid Adapter + +# Overview + +``` +Module name: Yaleo Bid Adapter +Module Type: Bidder Adapter +Maintainer: alexandr.kim@audienzz.com +``` + +# Description + +Module that connects to Yaleo's demand sources. + +**Note:** the bid adapter requires correct setup and approval. For more information visit [yaleo.com](https://www.yaleo.com) or contact [hola@yaleo.com](mailto:hola@yaleo.com). + +# Test parameters + +**Note:** to receive bids when testing without proper integration with the demand source, enable Prebid.js debug mode. See [how to enable debug mode](https://docs.prebid.org/dev-docs/publisher-api-reference/setConfig.html#debugging) for details. + +```js +const adUnits = [ + { + code: "test-div-1", + mediaTypes: { + banner: { + sizes: [[300, 300], [300, 600]], + } + }, + bids: [{ + bidder: "yaleo", + params: { + placementId: "95a09f24-afb8-441c-977b-08b4039cb88e", + } + }] + } +]; +``` + diff --git a/modules/yaleoBidAdapter.ts b/modules/yaleoBidAdapter.ts new file mode 100755 index 00000000000..980d5634df7 --- /dev/null +++ b/modules/yaleoBidAdapter.ts @@ -0,0 +1,80 @@ +import { ortbConverter } from '../libraries/ortbConverter/converter.js'; +import { pbsExtensions } from '../libraries/pbsExtensions/pbsExtensions.js'; +import { BidderSpec, registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER } from '../src/mediaTypes.js'; + +interface YaleoBidParams { + /** + * Yaleo placement ID. + */ + placementId: string; + /** + * Member ID. + * @default 3927 + */ + memberId?: number; + /** + * Maximum CPM value. Bids with a CPM higher than the specified value will be rejected. + */ + maxCpm?: number; +} + +declare module '../src/adUnits' { + interface BidderParams { + [BIDDER_CODE]: YaleoBidParams; + } +} + +const BIDDER_CODE = 'yaleo'; +const AUDIENZZ_VENDOR_ID = 783; +const PREBID_URL = 'https://bidder.yaleo.com/prebid'; +const DEFAULT_TTL = 300; + +const converter = ortbConverter({ + context: { + netRevenue: true, + ttl: DEFAULT_TTL, + }, + processors: pbsExtensions, +}); + +const isBidRequestValid: BidderSpec['isBidRequestValid'] = (request) => { + if (!request.params || typeof request.params.placementId !== 'string') { + return false; + } + + return !!request.params.placementId; +}; + +const buildRequests: BidderSpec['buildRequests'] = (validBidRequests, bidderRequest) => { + const ortbRequest = converter.toORTB({ + bidRequests: validBidRequests, + bidderRequest, + }); + + return { + url: PREBID_URL, + method: 'POST', + data: ortbRequest, + }; +} + +const interpretResponse: BidderSpec['interpretResponse'] = (serverResponse, bidderRequest) => { + const response = converter.fromORTB({ + response: serverResponse.body, + request: bidderRequest.data, + }); + + return response; +}; + +export const spec: BidderSpec = { + buildRequests, + code: BIDDER_CODE, + gvlid: AUDIENZZ_VENDOR_ID, + interpretResponse, + isBidRequestValid, + supportedMediaTypes: [BANNER], +}; + +registerBidder(spec); diff --git a/modules/yandexBidAdapter.js b/modules/yandexBidAdapter.js index 901bbcf29de..910a8e75a87 100644 --- a/modules/yandexBidAdapter.js +++ b/modules/yandexBidAdapter.js @@ -7,6 +7,7 @@ import { getBoundingClientRect } from '../libraries/boundingClientRect/boundingC import { ajax } from '../src/ajax.js'; import { config as pbjsConfig } from '../src/config.js'; import { isWebdriverEnabled } from '../libraries/webdriver/webdriver.js'; +import { getAdUnitElement } from '../src/utils/adUnits.js'; /** * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid @@ -183,7 +184,7 @@ export const spec = { queryParams['tcf-consent'] = consentString; } - const adUnitElement = document.getElementById(bidRequest.params.pubcontainerid || bidRequest.adUnitCode); + const adUnitElement = bidRequest.params.pubcontainerid ? document.getElementById(bidRequest.params.pubcontainerid) : getAdUnitElement(bidRequest); const windowContext = getContext(adUnitElement); const isIframe = inIframe(); const coords = isIframe ? getFramePosition() : { diff --git a/modules/yandexBidAdapter.md b/modules/yandexBidAdapter.md index ac454285806..9a7684d2644 100644 --- a/modules/yandexBidAdapter.md +++ b/modules/yandexBidAdapter.md @@ -8,40 +8,42 @@ Maintainer: prebid@yandex-team.com # Description -The Yandex Prebid Adapter is designed for seamless integration with Yandex's advertising services. It facilitates effective bidding by leveraging Yandex's robust ad-serving technology, ensuring publishers can maximize their ad revenue through efficient and targeted ad placements. +The Yandex Prebid Adapter is designed for seamless integration with Yandex's advertising services. It facilitates effective bidding by leveraging Yandex's robust ad-serving technology, ensuring publishers can maximize their ad revenue through efficient and targeted ad placements. Please reach out to for the integration guide and more details. For comprehensive auction analytics, consider using the [Yandex Analytics Adapter](https://docs.prebid.org/dev-docs/analytics/yandex.html). This tool provides essential insights into auction dynamics and user interactions, empowering publishers to fine-tune their strategies for optimal ad performance. # Parameters -| Name | Required? | Description | Example | Type | -|---------------|--------------------------------------------|-------------|---------|-----------| -| `placementId` | Yes | Block ID | `123-1` | `String` | -| `pageId` | No
Deprecated. Please use `placementId` | Page ID | `123` | `Integer` | -| `impId` | No
Deprecated. Please use `placementId` | Imp ID | `1` | `Integer` | +| Name | Scope | Description | Example | Type | +|---------------|----------------------------------------|--------------|------------------|-----------| +| `placementId` | Required | Placement ID | `'R-X-123456-1'` | `String` | +| `cur` | Optional. Default value is `'EUR'` | Bid Currency | `'USD'` | `String` | +| `pageId` | `Deprecated`. Please use `placementId` | Page ID | `123` | `Integer` | +| `impId` | `Deprecated`. Please use `placementId` | Imp ID | `1` | `Integer` | # Test Parameters ```javascript var adUnits = [ - { // banner + { // banner example. please check if the 'placementId' is active in Yandex UI code: 'banner-1', mediaTypes: { banner: { - sizes: [[240, 400], [300, 600]], + sizes: [[300, 250], [300, 600]], } }, bids: [ { bidder: 'yandex', params: { - placementId: '346580-1' + placementId: 'R-A-346580-1', + cur: 'USD' }, } ], }, - { // video - code: 'banner-2', + { // video example. please check if the 'placementId' is active in Yandex UI + code: 'video-1', mediaTypes: { video: { sizes: [[640, 480]], @@ -57,13 +59,14 @@ var adUnits = [ { bidder: 'yandex', params: { - placementId: '346580-1' + placementId: 'R-V-346580-1', + cur: 'USD' }, } ], }, - { // native - code: 'banner-3',, + { // native example. please check if the 'placementId' is active in Yandex UI + code: 'native-1', mediaTypes: { native: { title: { @@ -84,7 +87,7 @@ var adUnits = [ len: 90 }, sponsoredBy: { - len: 25, + len: 25 } }, }, @@ -92,7 +95,8 @@ var adUnits = [ { bidder: 'yandex', params: { - placementId: '346580-1' + placementId: 'R-A-346580-2', + cur: 'USD' }, } ], diff --git a/modules/yieldlabBidAdapter.js b/modules/yieldlabBidAdapter.js index e4edb320f86..227e7ea8a12 100644 --- a/modules/yieldlabBidAdapter.js +++ b/modules/yieldlabBidAdapter.js @@ -52,7 +52,8 @@ export const spec = { const timestamp = Date.now(); const query = { ts: timestamp, - json: true}; + json: true + }; _each(validBidRequests, function (bid) { adslotIds.push(bid.params.adslotId); diff --git a/modules/yieldliftBidAdapter.js b/modules/yieldliftBidAdapter.js index ba53f2a6340..a3fdc5bdb5a 100644 --- a/modules/yieldliftBidAdapter.js +++ b/modules/yieldliftBidAdapter.js @@ -1,6 +1,6 @@ -import {deepAccess, deepSetValue, logInfo} from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER} from '../src/mediaTypes.js'; +import { deepAccess, deepSetValue, logInfo } from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER } from '../src/mediaTypes.js'; const ENDPOINT_URL = 'https://x.yieldlift.com/pbjs'; diff --git a/modules/yieldloveBidAdapter.js b/modules/yieldloveBidAdapter.js index fadcf51cc85..d7743b6580c 100644 --- a/modules/yieldloveBidAdapter.js +++ b/modules/yieldloveBidAdapter.js @@ -1,6 +1,6 @@ -import {registerBidder} from '../src/adapters/bidderFactory.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; import * as utils from '../src/utils.js'; -import {BANNER} from '../src/mediaTypes.js'; +import { BANNER } from '../src/mediaTypes.js'; const ENDPOINT_URL = 'https://s2s.yieldlove-ad-serving.net/openrtb2/auction'; diff --git a/modules/yieldmoBidAdapter.js b/modules/yieldmoBidAdapter.js index d249a5e3bd3..89502f490c3 100644 --- a/modules/yieldmoBidAdapter.js +++ b/modules/yieldmoBidAdapter.js @@ -1,4 +1,3 @@ -import {getDNT} from '../libraries/dnt/index.js'; import { deepAccess, deepSetValue, @@ -18,6 +17,7 @@ import { import { BANNER, VIDEO } from '../src/mediaTypes.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { Renderer } from '../src/Renderer.js'; +import { getDNT } from '../libraries/dnt/index.js'; /** * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest @@ -735,7 +735,7 @@ function canAccessTopWindow() { } function isStage(bidderRequest) { - return !!bidderRequest.refererInfo?.referer?.includes('pb_force_a'); + return !!bidderRequest.refererInfo?.page?.includes('pb_force_a'); } function getAdserverUrl(path, stage) { diff --git a/modules/yieldoneAnalyticsAdapter.js b/modules/yieldoneAnalyticsAdapter.js index 23fa0e0eec9..460e8ead751 100644 --- a/modules/yieldoneAnalyticsAdapter.js +++ b/modules/yieldoneAnalyticsAdapter.js @@ -1,5 +1,5 @@ import { isArray, deepClone } from '../src/utils.js'; -import {ajax} from '../src/ajax.js'; +import { ajax } from '../src/ajax.js'; import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import { EVENTS } from '../src/constants.js'; import adapterManager from '../src/adapterManager.js'; @@ -66,9 +66,9 @@ function addAdUnitName(params, map) { }); } -const yieldoneAnalytics = Object.assign(adapter({analyticsType}), { +const yieldoneAnalytics = Object.assign(adapter({ analyticsType }), { getUrl() { return url; }, - track({eventType, args = {}}) { + track({ eventType, args = {} }) { if (eventType === EVENTS.BID_REQUESTED) { const reqBidderId = `${args.bidderCode}_${args.auctionId}`; requestedBidders[reqBidderId] = deepClone(args); @@ -83,11 +83,11 @@ const yieldoneAnalytics = Object.assign(adapter({analyticsType}), { args.forEach((bid) => { const reqBidId = `${bid.bidId}_${bid.auctionId}`; const reqBidderId = `${bid.bidder}_${bid.auctionId}`; - if (!eventsStorage[bid.auctionId]) eventsStorage[bid.auctionId] = {events: []}; + if (!eventsStorage[bid.auctionId]) eventsStorage[bid.auctionId] = { events: [] }; if ((requestedBidders[reqBidderId] || reqBidders[bid.bidder]) && requestedBids[reqBidId]) { if (!reqBidders[bid.bidder]) { reqBidders[bid.bidder] = requestedBidders[reqBidderId]; - eventsStorage[bid.auctionId].events.push({eventType, params: reqBidders[bid.bidder]}); + eventsStorage[bid.auctionId].events.push({ eventType, params: reqBidders[bid.bidder] }); delete requestedBidders[reqBidderId]; } reqBidders[bid.bidder].bids.push(requestedBids[reqBidId]); @@ -98,7 +98,7 @@ const yieldoneAnalytics = Object.assign(adapter({analyticsType}), { currentAuctionId = args.auctionId || currentAuctionId; if (currentAuctionId) { const eventsStorage = yieldoneAnalytics.eventsStorage; - if (!eventsStorage[currentAuctionId]) eventsStorage[currentAuctionId] = {events: []}; + if (!eventsStorage[currentAuctionId]) eventsStorage[currentAuctionId] = { events: [] }; // TODO: is 'page' the right value here? const referrer = args.refererInfo && args.refererInfo.page; if (referrer && referrers[currentAuctionId] !== referrer) { @@ -114,7 +114,7 @@ const yieldoneAnalytics = Object.assign(adapter({analyticsType}), { }); } if (!ignoredEvents[eventType]) { - eventsStorage[currentAuctionId].events.push({eventType, params}); + eventsStorage[currentAuctionId].events.push({ eventType, params }); } if ( @@ -125,7 +125,7 @@ const yieldoneAnalytics = Object.assign(adapter({analyticsType}), { auctionManager.getBidsReceived() ); if (yieldoneAnalytics.eventsStorage[currentAuctionId] && yieldoneAnalytics.eventsStorage[currentAuctionId].events.length) { - yieldoneAnalytics.eventsStorage[currentAuctionId].page = {url: referrers[currentAuctionId]}; + yieldoneAnalytics.eventsStorage[currentAuctionId].page = { url: referrers[currentAuctionId] }; yieldoneAnalytics.eventsStorage[currentAuctionId].pubId = pubId; yieldoneAnalytics.eventsStorage[currentAuctionId].wrapper_version = '$prebid.version$'; const adUnitNameMap = makeAdUnitNameMap(); diff --git a/modules/yieldoneBidAdapter.js b/modules/yieldoneBidAdapter.js index c71dadd1573..8c91ef18057 100644 --- a/modules/yieldoneBidAdapter.js +++ b/modules/yieldoneBidAdapter.js @@ -1,10 +1,10 @@ -import {deepAccess, isEmpty, isStr, logWarn, parseSizesInput} from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {Renderer} from '../src/Renderer.js'; -import {BANNER, VIDEO} from '../src/mediaTypes.js'; -import {getBrowser, getOS} from '../libraries/userAgentUtils/index.js'; -import {browserTypes, osTypes} from '../libraries/userAgentUtils/userAgentTypes.enums.js'; -import {BOL_LIKE_USER_AGENTS} from '../libraries/userAgentUtils/constants.js'; +import { deepAccess, isEmpty, isStr, logWarn, parseSizesInput } from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { Renderer } from '../src/Renderer.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; +import { getBrowser, getOS } from '../libraries/userAgentUtils/index.js'; +import { browserTypes, osTypes } from '../libraries/userAgentUtils/userAgentTypes.enums.js'; +import { BOL_LIKE_USER_AGENTS } from '../libraries/userAgentUtils/constants.js'; /** * @typedef {import('../src/adapters/bidderFactory').Bid} Bid @@ -24,7 +24,7 @@ const VIDEO_PLAYER_URL = 'https://img.ak.impact-ad.jp/ic/pone/ivt/firstview/js/d const CMER_PLAYER_URL = 'https://an.cmertv.com/hb/renderer/cmertv-video-yone-prebid.min.js'; const VIEWABLE_PERCENTAGE_URL = 'https://img.ak.impact-ad.jp/ic/pone/ivt/firstview/js/prebid-adformat-config.js'; -const DEFAULT_VIDEO_SIZE = {w: 640, h: 360}; +const DEFAULT_VIDEO_SIZE = { w: 640, h: 360 }; /** @type {BidderSpec} */ export const spec = { @@ -126,6 +126,12 @@ export const spec = { payload.gpid = gpid; } + // instl + const instl = deepAccess(bidRequest, 'ortb2Imp.instl'); + if (instl === 1 || instl === '1') { + payload.instl = 1; + } + return { method: 'GET', url: ENDPOINT_URL, @@ -321,7 +327,7 @@ function getVideoSize(bidRequest, enabledOldFormat = true, enabled1x1 = true) { } const splited = size.split('x'); - const sizeObj = {w: parseInt(splited[0], 10), h: parseInt(splited[1], 10)}; + const sizeObj = { w: parseInt(splited[0], 10), h: parseInt(splited[1], 10) }; const _isValidPlayerSize = !(isEmpty(sizeObj)) && (isFinite(sizeObj.w) && isFinite(sizeObj.h)); if (!_isValidPlayerSize) { return result; diff --git a/modules/yuktamediaAnalyticsAdapter.js b/modules/yuktamediaAnalyticsAdapter.js index e95517faa62..5ff3f2edab6 100644 --- a/modules/yuktamediaAnalyticsAdapter.js +++ b/modules/yuktamediaAnalyticsAdapter.js @@ -1,15 +1,15 @@ -import {buildUrl, generateUUID, getWindowLocation, logError, logInfo, parseSizesInput, parseUrl} from '../src/utils.js'; -import {ajax, fetch} from '../src/ajax.js'; +import { buildUrl, generateUUID, getWindowLocation, logError, logInfo, parseSizesInput, parseUrl } from '../src/utils.js'; +import { ajax, fetch } from '../src/ajax.js'; import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; import { EVENTS } from '../src/constants.js'; -import {getStorageManager} from '../src/storageManager.js'; -import {getRefererInfo} from '../src/refererDetection.js'; +import { getStorageManager } from '../src/storageManager.js'; +import { getRefererInfo } from '../src/refererDetection.js'; -import {MODULE_TYPE_ANALYTICS} from '../src/activities/modules.js'; +import { MODULE_TYPE_ANALYTICS } from '../src/activities/modules.js'; const MODULE_CODE = 'yuktamedia'; -const storage = getStorageManager({moduleType: MODULE_TYPE_ANALYTICS, moduleName: MODULE_CODE}); +const storage = getStorageManager({ moduleType: MODULE_TYPE_ANALYTICS, moduleName: MODULE_CODE }); const yuktamediaAnalyticsVersion = 'v3.1.0'; let initOptions; diff --git a/modules/zeotapIdPlusIdSystem.js b/modules/zeotapIdPlusIdSystem.js index ca48fde30b3..cbecfb078fe 100644 --- a/modules/zeotapIdPlusIdSystem.js +++ b/modules/zeotapIdPlusIdSystem.js @@ -5,9 +5,9 @@ * @requires module:modules/userId */ import { isStr, isPlainObject } from '../src/utils.js'; -import {submodule} from '../src/hook.js'; -import {getStorageManager} from '../src/storageManager.js'; -import {MODULE_TYPE_UID} from '../src/activities/modules.js'; +import { submodule } from '../src/hook.js'; +import { getStorageManager } from '../src/storageManager.js'; +import { MODULE_TYPE_UID } from '../src/activities/modules.js'; /** * @typedef {import('../modules/userId/index.js').Submodule} Submodule @@ -27,7 +27,7 @@ function readFromLocalStorage() { } export function getStorage() { - return getStorageManager({moduleType: MODULE_TYPE_UID, moduleName: ZEOTAP_MODULE_NAME}); + return getStorageManager({ moduleType: MODULE_TYPE_UID, moduleName: ZEOTAP_MODULE_NAME }); } export const storage = getStorage(); diff --git a/modules/zeta_globalBidAdapter.js b/modules/zeta_globalBidAdapter.js index f9fb6f806f5..f5c2952a22b 100644 --- a/modules/zeta_globalBidAdapter.js +++ b/modules/zeta_globalBidAdapter.js @@ -1,6 +1,6 @@ -import {deepAccess, logWarn} from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER} from '../src/mediaTypes.js'; +import { deepAccess, logWarn } from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER } from '../src/mediaTypes.js'; /** * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest @@ -112,13 +112,13 @@ export const spec = { if (request.gdprConsent) { payload.regs.ext = Object.assign( payload.regs.ext, - {gdpr: request.gdprConsent.gdprApplies === true ? 1 : 0} + { gdpr: request.gdprConsent.gdprApplies === true ? 1 : 0 } ); } if (request.gdprConsent && request.gdprConsent.gdprApplies) { payload.user.ext = Object.assign( payload.user.ext, - {consent: request.gdprConsent.consentString} + { consent: request.gdprConsent.consentString } ); } const postUrl = params.definerId !== PREBID_DEFINER_ID ? ENDPOINT_URL.concat('/', params.definerId) : ENDPOINT_URL; diff --git a/modules/zeta_global_sspAnalyticsAdapter.js b/modules/zeta_global_sspAnalyticsAdapter.js index ed4971d39a7..01d6a4a5a77 100644 --- a/modules/zeta_global_sspAnalyticsAdapter.js +++ b/modules/zeta_global_sspAnalyticsAdapter.js @@ -1,11 +1,12 @@ -import {logError} from '../src/utils.js'; -import {ajax} from '../src/ajax.js'; +import { logError } from '../src/utils.js'; +import { ajax } from '../src/ajax.js'; import adapterManager from '../src/adapterManager.js'; -import {EVENTS} from '../src/constants.js'; +import { EVENTS } from '../src/constants.js'; import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; -import {config} from '../src/config.js'; -import {parseDomain} from '../src/refererDetection.js'; +import { config } from '../src/config.js'; +import { parseDomain } from '../src/refererDetection.js'; +import { BANNER, VIDEO } from "../src/mediaTypes.js"; const ZETA_GVL_ID = 833; const ADAPTER_CODE = 'zeta_global_ssp'; @@ -32,7 +33,7 @@ function adRenderSucceededHandler(args) { const page = config.getConfig('pageUrl') || args.doc?.location?.host + args.doc?.location?.pathname; const event = { zetaParams: zetaParams, - domain: parseDomain(page, {noLeadingWww: true}), + domain: parseDomain(page, { noLeadingWww: true }), page: page, bid: { adId: args.bid?.adId, @@ -40,12 +41,14 @@ function adRenderSucceededHandler(args) { auctionId: args.bid?.auctionId, creativeId: args.bid?.creativeId, bidder: args.bid?.bidderCode, + dspId: args.bid?.dspId, mediaType: args.bid?.mediaType, size: args.bid?.size, adomain: args.bid?.adserverTargeting?.hb_adomain, timeToRespond: args.bid?.timeToRespond, cpm: args.bid?.cpm, - adUnitCode: args.bid?.adUnitCode + adUnitCode: args.bid?.adUnitCode, + floorData: args.bid?.floorData }, device: { ua: navigator.userAgent @@ -61,15 +64,35 @@ function auctionEndHandler(args) { bidderCode: br?.bidderCode, domain: br?.refererInfo?.domain, page: br?.refererInfo?.page, - bids: br?.bids?.map(b => ({ - bidId: b?.bidId, - auctionId: b?.auctionId, - bidder: b?.bidder, - mediaType: b?.mediaTypes?.video ? 'VIDEO' : (b?.mediaTypes?.banner ? 'BANNER' : undefined), - size: b?.sizes?.filter(s => s && s.length === 2).filter(s => Number.isInteger(s[0]) && Number.isInteger(s[1])).map(s => s[0] + 'x' + s[1]).find(s => s), - device: b?.ortb2?.device, - adUnitCode: b?.adUnitCode - })) + bids: br?.bids?.map(b => { + const mediaType = b?.mediaTypes?.video ? VIDEO : (b?.mediaTypes?.banner ? BANNER : undefined); + let floor; + if (typeof b?.getFloor === 'function') { + try { + const floorInfo = b.getFloor({ + currency: 'USD', + mediaType: mediaType, + size: '*' + }); + if (floorInfo && !isNaN(parseFloat(floorInfo.floor))) { + floor = parseFloat(floorInfo.floor); + } + } catch (e) { + // ignore floor lookup errors + } + } + + return { + bidId: b?.bidId, + auctionId: b?.auctionId, + bidder: b?.bidder, + mediaType: mediaType, + sizes: b?.sizes, + device: b?.ortb2?.device, + adUnitCode: b?.adUnitCode, + floor: floor + }; + }) })), bidsReceived: args.bidsReceived?.map(br => ({ adId: br?.adId, @@ -81,7 +104,8 @@ function auctionEndHandler(args) { adomain: br?.adserverTargeting?.hb_adomain, timeToRespond: br?.timeToRespond, cpm: br?.cpm, - adUnitCode: br?.adUnitCode + adUnitCode: br?.adUnitCode, + dspId: br?.dspId })) } sendEvent(EVENTS.AUCTION_END, event); @@ -92,23 +116,42 @@ function bidTimeoutHandler(args) { zetaParams: zetaParams, domain: args.find(t => t?.ortb2?.site?.domain)?.ortb2?.site?.domain, page: args.find(t => t?.ortb2?.site?.page)?.ortb2?.site?.page, - timeouts: args.map(t => ({ - bidId: t?.bidId, - auctionId: t?.auctionId, - bidder: t?.bidder, - mediaType: t?.mediaTypes?.video ? 'VIDEO' : (t?.mediaTypes?.banner ? 'BANNER' : undefined), - size: t?.sizes?.filter(s => s && s.length === 2).filter(s => Number.isInteger(s[0]) && Number.isInteger(s[1])).map(s => s[0] + 'x' + s[1]).find(s => s), - timeout: t?.timeout, - device: t?.ortb2?.device, - adUnitCode: t?.adUnitCode - })) + timeouts: args.map(t => { + const mediaType = t?.mediaTypes?.video ? VIDEO : (t?.mediaTypes?.banner ? BANNER : undefined); + let floor; + if (typeof t?.getFloor === 'function') { + try { + const floorInfo = t.getFloor({ + currency: 'USD', + mediaType: mediaType, + size: '*' + }); + if (floorInfo && !isNaN(parseFloat(floorInfo.floor))) { + floor = parseFloat(floorInfo.floor); + } + } catch (e) { + // ignore floor lookup errors + } + } + return { + bidId: t?.bidId, + auctionId: t?.auctionId, + bidder: t?.bidder, + mediaType: mediaType, + sizes: t?.sizes, + timeout: t?.timeout, + device: t?.ortb2?.device, + adUnitCode: t?.adUnitCode, + floor: floor + } + }) } sendEvent(EVENTS.BID_TIMEOUT, event); } /// /////////// ADAPTER DEFINITION /////////////////////////// -const baseAdapter = adapter({analyticsType: 'endpoint'}); +const baseAdapter = adapter({ analyticsType: 'endpoint' }); const zetaAdapter = Object.assign({}, baseAdapter, { enableAnalytics(config = {}) { @@ -126,7 +169,7 @@ const zetaAdapter = Object.assign({}, baseAdapter, { baseAdapter.disableAnalytics.apply(this, arguments); }, - track({eventType, args}) { + track({ eventType, args }) { switch (eventType) { case EVENTS.AD_RENDER_SUCCEEDED: adRenderSucceededHandler(args); diff --git a/modules/zeta_global_sspBidAdapter.js b/modules/zeta_global_sspBidAdapter.js index bd0aac1d502..f69a8b4a18d 100644 --- a/modules/zeta_global_sspBidAdapter.js +++ b/modules/zeta_global_sspBidAdapter.js @@ -1,8 +1,8 @@ -import {deepAccess, deepSetValue, isArray, isBoolean, isNumber, isStr, logWarn} from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER, VIDEO} from '../src/mediaTypes.js'; -import {config} from '../src/config.js'; -import {parseDomain} from '../src/refererDetection.js'; +import { deepAccess, deepSetValue, isArray, isBoolean, isNumber, isStr, logWarn } from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; +import { config } from '../src/config.js'; +import { parseDomain } from '../src/refererDetection.js'; /** * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest @@ -108,7 +108,7 @@ export const spec = { const floorInfo = request.getFloor({ currency: 'USD', mediaType: impData.video ? 'video' : 'banner', - size: [ impData.video ? impData.video.w : impData.banner.w, impData.video ? impData.video.h : impData.banner.h ] + size: [impData.video ? impData.video.w : impData.banner.w, impData.video ? impData.video.h : impData.banner.h] }); if (floorInfo && floorInfo.floor) { impData.bidfloor = floorInfo.floor; @@ -128,8 +128,8 @@ export const spec = { id: bidderRequest.bidderRequestId, cur: [DEFAULT_CUR], imp: imps, - site: {...bidderRequest?.ortb2?.site, ...params?.site}, - device: {...bidderRequest?.ortb2?.device, ...params?.device}, + site: { ...bidderRequest?.ortb2?.site, ...params?.site }, + device: { ...bidderRequest?.ortb2?.device, ...params?.device }, user: params.user ? params.user : {}, app: params.app ? params.app : {}, ext: { @@ -140,7 +140,7 @@ export const spec = { const rInfo = bidderRequest.refererInfo; if (rInfo) { payload.site.page = cropPage(rInfo.page || rInfo.topmostLocation); - payload.site.domain = parseDomain(payload.site.page, {noLeadingWww: true}); + payload.site.domain = parseDomain(payload.site.page, { noLeadingWww: true }); } payload.device.ua = navigator.userAgent; diff --git a/modules/zmaticooBidAdapter.js b/modules/zmaticooBidAdapter.js index a8a5bc07461..e84c83fc42c 100644 --- a/modules/zmaticooBidAdapter.js +++ b/modules/zmaticooBidAdapter.js @@ -1,6 +1,6 @@ -import {deepAccess, isArray, isBoolean, isNumber, isStr, logWarn, triggerPixel} from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER, VIDEO} from '../src/mediaTypes.js'; +import { deepAccess, isArray, isBoolean, isNumber, isStr, logWarn, triggerPixel } from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; /** * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest @@ -149,10 +149,10 @@ export const spec = { payload.test = params.test; } if (bidderRequest.gdprConsent) { - payload.regs.ext = Object.assign(payload.regs.ext, {gdpr: Number(bidderRequest.gdprConsent.gdprApplies) === 1 ? 1 : 0}); + payload.regs.ext = Object.assign(payload.regs.ext, { gdpr: Number(bidderRequest.gdprConsent.gdprApplies) === 1 ? 1 : 0 }); } if (bidderRequest.gdprConsent && bidderRequest.gdprConsent.gdprApplies) { - payload.user.ext = Object.assign(payload.user.ext, {consent: bidderRequest.gdprConsent.consentString}); + payload.user.ext = Object.assign(payload.user.ext, { consent: bidderRequest.gdprConsent.consentString }); } const postUrl = ENDPOINT_URL; return { diff --git a/package-lock.json b/package-lock.json index 4d170942396..dc91f890c2a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "prebid.js", - "version": "10.23.0-pre", + "version": "11.2.0-pre", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "prebid.js", - "version": "10.23.0-pre", + "version": "11.2.0-pre", "license": "Apache-2.0", "dependencies": { "@babel/core": "^7.28.4", @@ -2967,29 +2967,6 @@ } } }, - "node_modules/@isaacs/balanced-match": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", - "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/@isaacs/brace-expansion": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz", - "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@isaacs/balanced-match": "^4.0.1" - }, - "engines": { - "node": "20 || >=22" - } - }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "dev": true, @@ -3981,24 +3958,34 @@ "typescript": ">=4.8.4 <6.0.0" } }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "engines": { + "node": "18 || 20 || >=22" + } + }, "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.3.tgz", + "integrity": "sha512-fy6KJm2RawA5RcHkLa1z/ScpBeA762UF9KmZQxwIbDtRJrgLzM10depAiEQ+CXYcoiqW1/m96OAAoke2nE9EeA==", "dev": true, - "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0" + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "version": "9.0.7", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.7.tgz", + "integrity": "sha512-MOwgjc8tfrpn5QQEvjijjmDVtMw2oL88ugTevzxQnzRLm6l3fVEF2gzU0kYeYYKD8C66+IdGX6peJ4MyUlUnPg==", "dev": true, - "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^5.0.2" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -6534,11 +6521,10 @@ } }, "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", + "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", "dev": true, - "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -6567,9 +6553,9 @@ } }, "node_modules/ajv-formats/node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -7194,8 +7180,9 @@ }, "node_modules/asynckit": { "version": "0.4.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true }, "node_modules/atob": { "version": "2.1.2", @@ -7223,14 +7210,13 @@ } }, "node_modules/axios": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.2.tgz", - "integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==", + "version": "1.13.5", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.5.tgz", + "integrity": "sha512-cz4ur7Vb0xS4/KUN0tPWe44eqxrIu31me+fbang3ijiNscE129POzipJJA6zniq2C/Z6sJCjMimjS8Lc/GAs8Q==", "dev": true, - "license": "MIT", "dependencies": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.4", + "follow-redirects": "^1.15.11", + "form-data": "^4.0.5", "proxy-from-env": "^1.1.0" } }, @@ -7443,11 +7429,14 @@ } }, "node_modules/baseline-browser-mapping": { - "version": "2.9.17", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.17.tgz", - "integrity": "sha512-agD0MgJFUP/4nvjqzIB29zRPUuCF7Ge6mEv9s8dHrtYD7QWXRcx75rOADE/d5ah1NI+0vkDl0yorDd5U852IQQ==", + "version": "2.10.7", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.7.tgz", + "integrity": "sha512-1ghYO3HnxGec0TCGBXiDLVns4eCSx4zJpxnHrlqFQajmhfKMQBzUGDdkMK7fUW7PTHTeLf+j87aTuKuuwWzMGw==", "bin": { - "baseline-browser-mapping": "dist/cli.js" + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" } }, "node_modules/basic-auth": { @@ -7467,9 +7456,10 @@ "license": "MIT" }, "node_modules/basic-ftp": { - "version": "5.0.5", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.2.0.tgz", + "integrity": "sha512-VoMINM2rqJwJgfdHq6RiUudKt2BV+FY5ZFezP/ypmwayk68+NzzAQy4XXLlqsGD4MCzq3DrmNFD/uUmBJuGoXw==", "dev": true, - "license": "MIT", "engines": { "node": ">=10.0.0" } @@ -7725,15 +7715,15 @@ } }, "node_modules/browserstack-local": { - "version": "1.5.5", + "version": "1.5.11", + "resolved": "https://registry.npmjs.org/browserstack-local/-/browserstack-local-1.5.11.tgz", + "integrity": "sha512-RNq0yrezPq7BXXxl/cvsbORfswUQi744po6ECkTEC2RkqNbdPyzewdy4VR9k4QHSzPHTkZx8PeH08veRtfFI8A==", "dev": true, - "license": "MIT", "dependencies": { "agent-base": "^6.0.2", "https-proxy-agent": "^5.0.1", "is-running": "^2.1.0", - "ps-tree": "=1.2.0", - "temp-fs": "^0.9.9" + "tree-kill": "^1.2.2" } }, "node_modules/browserstack/node_modules/agent-base": { @@ -7879,9 +7869,9 @@ "license": "MIT" }, "node_modules/caniuse-lite": { - "version": "1.0.30001765", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001765.tgz", - "integrity": "sha512-LWcNtSyZrakjECqmpP4qdg0MMGdN368D7X8XvvAqOcqMv0RxnlqVKZl2V6/mBR68oYMxOZPLw/gO7DuisMHUvQ==", + "version": "1.0.30001778", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001778.tgz", + "integrity": "sha512-PN7uxFL+ExFJO61aVmP1aIEG4i9whQd4eoSCebav62UwDyp5OHh06zN4jqKSMePVgxHifCw1QJxdRkA1Pisekg==", "funding": [ { "type": "opencollective", @@ -8220,8 +8210,9 @@ }, "node_modules/combined-stream": { "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "dev": true, - "license": "MIT", "dependencies": { "delayed-stream": "~1.0.0" }, @@ -9280,8 +9271,9 @@ }, "node_modules/delayed-stream": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.4.0" } @@ -10509,6 +10501,27 @@ } } }, + "node_modules/eslint-plugin-import-x/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/eslint-plugin-import-x/node_modules/brace-expansion": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.3.tgz", + "integrity": "sha512-fy6KJm2RawA5RcHkLa1z/ScpBeA762UF9KmZQxwIbDtRJrgLzM10depAiEQ+CXYcoiqW1/m96OAAoke2nE9EeA==", + "dev": true, + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, "node_modules/eslint-plugin-import-x/node_modules/debug": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", @@ -10528,16 +10541,15 @@ } }, "node_modules/eslint-plugin-import-x/node_modules/minimatch": { - "version": "10.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.3.tgz", - "integrity": "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==", + "version": "10.2.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.3.tgz", + "integrity": "sha512-Rwi3pnapEqirPSbWbrZaa6N3nmqq4Xer/2XooiOKyV3q12ML06f7MOuc5DVH8ONZIFhwIYQ3yzPH4nt7iWHaTg==", "dev": true, - "license": "ISC", "dependencies": { - "@isaacs/brace-expansion": "^5.0.0" + "brace-expansion": "^5.0.2" }, "engines": { - "node": "20 || >=22" + "node": "18 || 20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -11097,24 +11109,6 @@ "es5-ext": "~0.10.14" } }, - "node_modules/event-stream": { - "version": "3.3.4", - "dev": true, - "license": "MIT", - "dependencies": { - "duplexer": "~0.1.1", - "from": "~0", - "map-stream": "~0.1.0", - "pause-stream": "0.0.11", - "split": "0.3", - "stream-combiner": "~0.0.4", - "through": "~2.3.1" - } - }, - "node_modules/event-stream/node_modules/map-stream": { - "version": "0.1.0", - "dev": true - }, "node_modules/event-target-shim": { "version": "5.0.1", "dev": true, @@ -11447,10 +11441,25 @@ } ] }, + "node_modules/fast-xml-builder": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.1.4.tgz", + "integrity": "sha512-f2jhpN4Eccy0/Uz9csxh3Nu6q4ErKxf0XIsasomfOihuSUa3/xw6w8dnOtCDgEItQFJG8KyXPzQXzcODDrrbOg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "dependencies": { + "path-expression-matcher": "^1.1.3" + } + }, "node_modules/fast-xml-parser": { - "version": "5.2.5", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.2.5.tgz", - "integrity": "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ==", + "version": "5.5.7", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.5.7.tgz", + "integrity": "sha512-LteOsISQ2GEiDHZch6L9hB0+MLoYVLToR7xotrzU0opCICBkxOPgHAy1HxAvtxfJNXDJpgAsQN30mkrfpO2Prg==", "dev": true, "funding": [ { @@ -11458,9 +11467,10 @@ "url": "https://github.com/sponsors/NaturalIntelligence" } ], - "license": "MIT", "dependencies": { - "strnum": "^2.1.0" + "fast-xml-builder": "^1.1.4", + "path-expression-matcher": "^1.1.3", + "strnum": "^2.2.0" }, "bin": { "fxparser": "src/cli/cli.js" @@ -11584,11 +11594,10 @@ } }, "node_modules/filelist/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.8.tgz", + "integrity": "sha512-7RN35vit8DeBclkofOVmBY0eDAZZQd1HzmukRdSyz95CRh8FT54eqnbj0krQr3mrHR6sfRyYkyhwBWjoV5uqlQ==", "dev": true, - "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -11727,12 +11736,15 @@ } }, "node_modules/flatted": { - "version": "3.3.1", - "dev": true, - "license": "ISC" + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.1.tgz", + "integrity": "sha512-IxfVbRFVlV8V/yRaGzk0UVIcsKKHMSfYw66T/u4nTwlWteQePsxe//LjudR1AMX4tZW3WFCh3Zqa/sjlqpbURQ==", + "dev": true }, "node_modules/follow-redirects": { - "version": "1.15.6", + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", "dev": true, "funding": [ { @@ -11740,7 +11752,6 @@ "url": "https://github.com/sponsors/RubenVerborgh" } ], - "license": "MIT", "engines": { "node": ">=4.0" }, @@ -11815,11 +11826,10 @@ "license": "BSD" }, "node_modules/form-data": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", - "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", "dev": true, - "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", @@ -11868,11 +11878,6 @@ "node": ">= 0.6" } }, - "node_modules/from": { - "version": "0.1.7", - "dev": true, - "license": "MIT" - }, "node_modules/fs-extra": { "version": "11.3.1", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.1.tgz", @@ -12270,22 +12275,34 @@ "node": ">= 10.13.0" } }, + "node_modules/glob/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "engines": { + "node": "18 || 20 || >=22" + } + }, "node_modules/glob/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.3.tgz", + "integrity": "sha512-fy6KJm2RawA5RcHkLa1z/ScpBeA762UF9KmZQxwIbDtRJrgLzM10depAiEQ+CXYcoiqW1/m96OAAoke2nE9EeA==", "dev": true, - "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0" + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" } }, "node_modules/glob/node_modules/minimatch": { - "version": "9.0.5", + "version": "9.0.7", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.7.tgz", + "integrity": "sha512-MOwgjc8tfrpn5QQEvjijjmDVtMw2oL88ugTevzxQnzRLm6l3fVEF2gzU0kYeYYKD8C66+IdGX6peJ4MyUlUnPg==", "dev": true, - "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^5.0.2" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -15481,6 +15498,15 @@ "webpack": "^5.0.0" } }, + "node_modules/karma-webpack/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "engines": { + "node": "18 || 20 || >=22" + } + }, "node_modules/karma-webpack/node_modules/glob": { "version": "7.2.3", "dev": true, @@ -15501,9 +15527,10 @@ } }, "node_modules/karma-webpack/node_modules/glob/node_modules/minimatch": { - "version": "3.1.2", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.4.tgz", + "integrity": "sha512-twmL+S8+7yIsE9wsqgzU3E8/LumN3M3QELrBZ20OdmQ9jB2JvW5oZtBEmft84k/Gs5CG9mqtWc6Y9vW+JEzGxw==", "dev": true, - "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -15512,11 +15539,12 @@ } }, "node_modules/karma-webpack/node_modules/minimatch": { - "version": "9.0.4", + "version": "9.0.7", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.7.tgz", + "integrity": "sha512-MOwgjc8tfrpn5QQEvjijjmDVtMw2oL88ugTevzxQnzRLm6l3fVEF2gzU0kYeYYKD8C66+IdGX6peJ4MyUlUnPg==", "dev": true, - "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^5.0.2" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -15526,13 +15554,15 @@ } }, "node_modules/karma-webpack/node_modules/minimatch/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.3.tgz", + "integrity": "sha512-fy6KJm2RawA5RcHkLa1z/ScpBeA762UF9KmZQxwIbDtRJrgLzM10depAiEQ+CXYcoiqW1/m96OAAoke2nE9EeA==", "dev": true, - "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0" + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" } }, "node_modules/karma/node_modules/ansi-styles": { @@ -15839,8 +15869,9 @@ } }, "node_modules/lodash": { - "version": "4.17.21", - "license": "MIT" + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", + "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==" }, "node_modules/lodash.clone": { "version": "4.5.0", @@ -16167,9 +16198,10 @@ } }, "node_modules/minimatch": { - "version": "3.1.2", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.4.tgz", + "integrity": "sha512-twmL+S8+7yIsE9wsqgzU3E8/LumN3M3QELrBZ20OdmQ9jB2JvW5oZtBEmft84k/Gs5CG9mqtWc6Y9vW+JEzGxw==", "dev": true, - "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -16433,9 +16465,10 @@ } }, "node_modules/mocha/node_modules/minimatch": { - "version": "5.1.6", + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.8.tgz", + "integrity": "sha512-7RN35vit8DeBclkofOVmBY0eDAZZQd1HzmukRdSyz95CRh8FT54eqnbj0krQr3mrHR6sfRyYkyhwBWjoV5uqlQ==", "dev": true, - "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -16645,22 +16678,34 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/multimatch/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "engines": { + "node": "18 || 20 || >=22" + } + }, "node_modules/multimatch/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.3.tgz", + "integrity": "sha512-fy6KJm2RawA5RcHkLa1z/ScpBeA762UF9KmZQxwIbDtRJrgLzM10depAiEQ+CXYcoiqW1/m96OAAoke2nE9EeA==", "dev": true, "dependencies": { - "balanced-match": "^1.0.0" + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" } }, "node_modules/multimatch/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "version": "9.0.7", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.7.tgz", + "integrity": "sha512-MOwgjc8tfrpn5QQEvjijjmDVtMw2oL88ugTevzxQnzRLm6l3fVEF2gzU0kYeYYKD8C66+IdGX6peJ4MyUlUnPg==", "dev": true, "dependencies": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^5.0.2" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -17524,6 +17569,21 @@ "node": ">=8" } }, + "node_modules/path-expression-matcher": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.1.3.tgz", + "integrity": "sha512-qdVgY8KXmVdJZRSS1JdEPOKPdTiEK/pi0RkcT2sw1RhXxohdujUlJFPuS1TSkevZ9vzd3ZlL7ULl1MHGTApKzQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/path-is-absolute": { "version": "1.0.1", "dev": true, @@ -17600,17 +17660,6 @@ "node": "*" } }, - "node_modules/pause-stream": { - "version": "0.0.11", - "dev": true, - "license": [ - "MIT", - "Apache2" - ], - "dependencies": { - "through": "~2.3" - } - }, "node_modules/pend": { "version": "1.2.0", "dev": true, @@ -17886,20 +17935,6 @@ "dev": true, "license": "MIT" }, - "node_modules/ps-tree": { - "version": "1.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "event-stream": "=3.3.4" - }, - "bin": { - "ps-tree": "bin/ps-tree.js" - }, - "engines": { - "node": ">= 0.10" - } - }, "node_modules/pump": { "version": "3.0.0", "dev": true, @@ -18017,9 +18052,9 @@ } }, "node_modules/qs": { - "version": "6.14.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz", - "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==", + "version": "6.14.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz", + "integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==", "dependencies": { "side-channel": "^1.1.0" }, @@ -18260,9 +18295,10 @@ } }, "node_modules/readdir-glob/node_modules/minimatch": { - "version": "5.1.6", + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.8.tgz", + "integrity": "sha512-7RN35vit8DeBclkofOVmBY0eDAZZQd1HzmukRdSyz95CRh8FT54eqnbj0krQr3mrHR6sfRyYkyhwBWjoV5uqlQ==", "dev": true, - "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -18764,9 +18800,9 @@ } }, "node_modules/schema-utils/node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -19241,17 +19277,41 @@ } }, "node_modules/socket.io-parser": { - "version": "4.2.4", + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.6.tgz", + "integrity": "sha512-asJqbVBDsBCJx0pTqw3WfesSY0iRX+2xzWEWzrpcH7L6fLzrhyF8WPI8UaeM4YCuDfpwA/cgsdugMsmtz8EJeg==", "dev": true, - "license": "MIT", "dependencies": { "@socket.io/component-emitter": "~3.1.0", - "debug": "~4.3.1" + "debug": "~4.4.1" }, "engines": { "node": ">=10.0.0" } }, + "node_modules/socket.io-parser/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io-parser/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, "node_modules/socks": { "version": "2.8.4", "dev": true, @@ -19411,17 +19471,6 @@ "dev": true, "license": "CC0-1.0" }, - "node_modules/split": { - "version": "0.3.3", - "dev": true, - "license": "MIT", - "dependencies": { - "through": "2" - }, - "engines": { - "node": "*" - } - }, "node_modules/split2": { "version": "4.2.0", "dev": true, @@ -19496,14 +19545,6 @@ "node": ">= 0.10.0" } }, - "node_modules/stream-combiner": { - "version": "0.0.4", - "dev": true, - "license": "MIT", - "dependencies": { - "duplexer": "~0.1.1" - } - }, "node_modules/stream-composer": { "version": "1.0.2", "dev": true, @@ -19851,17 +19892,16 @@ } }, "node_modules/strnum": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.1.1.tgz", - "integrity": "sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.2.1.tgz", + "integrity": "sha512-BwRvNd5/QoAtyW1na1y1LsJGQNvRlkde6Q/ipqqEaivoMdV+B1OMOTVdwR+N/cwVUcIt9PYyHmV8HyexCZSupg==", "dev": true, "funding": [ { "type": "github", "url": "https://github.com/sponsors/NaturalIntelligence" } - ], - "license": "MIT" + ] }, "node_modules/supports-color": { "version": "5.5.0", @@ -19954,47 +19994,6 @@ "streamx": "^2.12.5" } }, - "node_modules/temp-fs": { - "version": "0.9.9", - "dev": true, - "license": "MIT", - "dependencies": { - "rimraf": "~2.5.2" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/temp-fs/node_modules/glob": { - "version": "7.2.3", - "dev": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/temp-fs/node_modules/rimraf": { - "version": "2.5.4", - "dev": true, - "license": "ISC", - "dependencies": { - "glob": "^7.0.5" - }, - "bin": { - "rimraf": "bin.js" - } - }, "node_modules/ternary-stream": { "version": "3.0.0", "dev": true, @@ -20304,6 +20303,15 @@ "node": ">=6" } }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, + "bin": { + "tree-kill": "cli.js" + } + }, "node_modules/triple-beam": { "version": "1.4.1", "dev": true, @@ -20678,9 +20686,9 @@ } }, "node_modules/undici": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-6.23.0.tgz", - "integrity": "sha512-VfQPToRA5FZs/qJxLIinmU59u0r7LXqoJkCzinq3ckNJp3vKEh7jTWN589YQ5+aoAC/TGRLyJLCPKcLQbM8r9g==", + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.24.1.tgz", + "integrity": "sha512-sC+b0tB1whOCzbtlx20fx3WgCXwkW627p4EA9uM+/tNNPkSS+eSEld6pAs9nDv7WbY1UUljBMYPtu9BCOrCWKA==", "dev": true, "engines": { "node": ">=18.17" @@ -24084,21 +24092,6 @@ "dev": true, "requires": {} }, - "@isaacs/balanced-match": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", - "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", - "dev": true - }, - "@isaacs/brace-expansion": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz", - "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==", - "dev": true, - "requires": { - "@isaacs/balanced-match": "^4.0.1" - } - }, "@isaacs/cliui": { "version": "8.0.2", "dev": true, @@ -24771,22 +24764,28 @@ "ts-api-utils": "^2.1.0" }, "dependencies": { + "balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true + }, "brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.3.tgz", + "integrity": "sha512-fy6KJm2RawA5RcHkLa1z/ScpBeA762UF9KmZQxwIbDtRJrgLzM10depAiEQ+CXYcoiqW1/m96OAAoke2nE9EeA==", "dev": true, "requires": { - "balanced-match": "^1.0.0" + "balanced-match": "^4.0.2" } }, "minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "version": "9.0.7", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.7.tgz", + "integrity": "sha512-MOwgjc8tfrpn5QQEvjijjmDVtMw2oL88ugTevzxQnzRLm6l3fVEF2gzU0kYeYYKD8C66+IdGX6peJ4MyUlUnPg==", "dev": true, "requires": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^5.0.2" } }, "semver": { @@ -26481,9 +26480,9 @@ } }, "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", + "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", "dev": true, "requires": { "fast-deep-equal": "^3.1.1", @@ -26501,9 +26500,9 @@ }, "dependencies": { "ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", "requires": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -26895,6 +26894,8 @@ }, "asynckit": { "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", "dev": true }, "atob": { @@ -26909,13 +26910,13 @@ } }, "axios": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.2.tgz", - "integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==", + "version": "1.13.5", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.5.tgz", + "integrity": "sha512-cz4ur7Vb0xS4/KUN0tPWe44eqxrIu31me+fbang3ijiNscE129POzipJJA6zniq2C/Z6sJCjMimjS8Lc/GAs8Q==", "dev": true, "requires": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.4", + "follow-redirects": "^1.15.11", + "form-data": "^4.0.5", "proxy-from-env": "^1.1.0" } }, @@ -27043,9 +27044,9 @@ "dev": true }, "baseline-browser-mapping": { - "version": "2.9.17", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.17.tgz", - "integrity": "sha512-agD0MgJFUP/4nvjqzIB29zRPUuCF7Ge6mEv9s8dHrtYD7QWXRcx75rOADE/d5ah1NI+0vkDl0yorDd5U852IQQ==" + "version": "2.10.7", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.7.tgz", + "integrity": "sha512-1ghYO3HnxGec0TCGBXiDLVns4eCSx4zJpxnHrlqFQajmhfKMQBzUGDdkMK7fUW7PTHTeLf+j87aTuKuuwWzMGw==" }, "basic-auth": { "version": "2.0.1", @@ -27061,7 +27062,9 @@ } }, "basic-ftp": { - "version": "5.0.5", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.2.0.tgz", + "integrity": "sha512-VoMINM2rqJwJgfdHq6RiUudKt2BV+FY5ZFezP/ypmwayk68+NzzAQy4XXLlqsGD4MCzq3DrmNFD/uUmBJuGoXw==", "dev": true }, "batch": { @@ -27255,14 +27258,15 @@ } }, "browserstack-local": { - "version": "1.5.5", + "version": "1.5.11", + "resolved": "https://registry.npmjs.org/browserstack-local/-/browserstack-local-1.5.11.tgz", + "integrity": "sha512-RNq0yrezPq7BXXxl/cvsbORfswUQi744po6ECkTEC2RkqNbdPyzewdy4VR9k4QHSzPHTkZx8PeH08veRtfFI8A==", "dev": true, "requires": { "agent-base": "^6.0.2", "https-proxy-agent": "^5.0.1", "is-running": "^2.1.0", - "ps-tree": "=1.2.0", - "temp-fs": "^0.9.9" + "tree-kill": "^1.2.2" } }, "buffer-crc32": { @@ -27338,9 +27342,9 @@ "dev": true }, "caniuse-lite": { - "version": "1.0.30001765", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001765.tgz", - "integrity": "sha512-LWcNtSyZrakjECqmpP4qdg0MMGdN368D7X8XvvAqOcqMv0RxnlqVKZl2V6/mBR68oYMxOZPLw/gO7DuisMHUvQ==" + "version": "1.0.30001778", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001778.tgz", + "integrity": "sha512-PN7uxFL+ExFJO61aVmP1aIEG4i9whQd4eoSCebav62UwDyp5OHh06zN4jqKSMePVgxHifCw1QJxdRkA1Pisekg==" }, "chai": { "version": "4.4.1", @@ -27558,6 +27562,8 @@ }, "combined-stream": { "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "dev": true, "requires": { "delayed-stream": "~1.0.0" @@ -28237,6 +28243,8 @@ }, "delayed-stream": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", "dev": true }, "depd": { @@ -29175,6 +29183,21 @@ "unrs-resolver": "^1.9.2" }, "dependencies": { + "balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true + }, + "brace-expansion": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.3.tgz", + "integrity": "sha512-fy6KJm2RawA5RcHkLa1z/ScpBeA762UF9KmZQxwIbDtRJrgLzM10depAiEQ+CXYcoiqW1/m96OAAoke2nE9EeA==", + "dev": true, + "requires": { + "balanced-match": "^4.0.2" + } + }, "debug": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", @@ -29185,12 +29208,12 @@ } }, "minimatch": { - "version": "10.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.3.tgz", - "integrity": "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==", + "version": "10.2.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.3.tgz", + "integrity": "sha512-Rwi3pnapEqirPSbWbrZaa6N3nmqq4Xer/2XooiOKyV3q12ML06f7MOuc5DVH8ONZIFhwIYQ3yzPH4nt7iWHaTg==", "dev": true, "requires": { - "@isaacs/brace-expansion": "^5.0.0" + "brace-expansion": "^5.0.2" } }, "ms": { @@ -29414,25 +29437,6 @@ "es5-ext": "~0.10.14" } }, - "event-stream": { - "version": "3.3.4", - "dev": true, - "requires": { - "duplexer": "~0.1.1", - "from": "~0", - "map-stream": "~0.1.0", - "pause-stream": "0.0.11", - "split": "0.3", - "stream-combiner": "~0.0.4", - "through": "~2.3.1" - }, - "dependencies": { - "map-stream": { - "version": "0.1.0", - "dev": true - } - } - }, "event-target-shim": { "version": "5.0.1", "dev": true @@ -29664,13 +29668,24 @@ "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==" }, + "fast-xml-builder": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.1.4.tgz", + "integrity": "sha512-f2jhpN4Eccy0/Uz9csxh3Nu6q4ErKxf0XIsasomfOihuSUa3/xw6w8dnOtCDgEItQFJG8KyXPzQXzcODDrrbOg==", + "dev": true, + "requires": { + "path-expression-matcher": "^1.1.3" + } + }, "fast-xml-parser": { - "version": "5.2.5", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.2.5.tgz", - "integrity": "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ==", + "version": "5.5.7", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.5.7.tgz", + "integrity": "sha512-LteOsISQ2GEiDHZch6L9hB0+MLoYVLToR7xotrzU0opCICBkxOPgHAy1HxAvtxfJNXDJpgAsQN30mkrfpO2Prg==", "dev": true, "requires": { - "strnum": "^2.1.0" + "fast-xml-builder": "^1.1.4", + "path-expression-matcher": "^1.1.3", + "strnum": "^2.2.0" } }, "fastest-levenshtein": { @@ -29751,9 +29766,9 @@ } }, "minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.8.tgz", + "integrity": "sha512-7RN35vit8DeBclkofOVmBY0eDAZZQd1HzmukRdSyz95CRh8FT54eqnbj0krQr3mrHR6sfRyYkyhwBWjoV5uqlQ==", "dev": true, "requires": { "brace-expansion": "^2.0.1" @@ -29849,11 +29864,15 @@ } }, "flatted": { - "version": "3.3.1", + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.1.tgz", + "integrity": "sha512-IxfVbRFVlV8V/yRaGzk0UVIcsKKHMSfYw66T/u4nTwlWteQePsxe//LjudR1AMX4tZW3WFCh3Zqa/sjlqpbURQ==", "dev": true }, "follow-redirects": { - "version": "1.15.6", + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", "dev": true }, "for-each": { @@ -29893,9 +29912,9 @@ "dev": true }, "form-data": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", - "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", "dev": true, "requires": { "asynckit": "^0.4.0", @@ -29926,10 +29945,6 @@ "fresh": { "version": "0.5.2" }, - "from": { - "version": "0.1.7", - "dev": true - }, "fs-extra": { "version": "11.3.1", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.1.tgz", @@ -30144,20 +30159,28 @@ "path-scurry": "^1.11.1" }, "dependencies": { + "balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true + }, "brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.3.tgz", + "integrity": "sha512-fy6KJm2RawA5RcHkLa1z/ScpBeA762UF9KmZQxwIbDtRJrgLzM10depAiEQ+CXYcoiqW1/m96OAAoke2nE9EeA==", "dev": true, "requires": { - "balanced-match": "^1.0.0" + "balanced-match": "^4.0.2" } }, "minimatch": { - "version": "9.0.5", + "version": "9.0.7", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.7.tgz", + "integrity": "sha512-MOwgjc8tfrpn5QQEvjijjmDVtMw2oL88ugTevzxQnzRLm6l3fVEF2gzU0kYeYYKD8C66+IdGX6peJ4MyUlUnPg==", "dev": true, "requires": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^5.0.2" } } } @@ -32351,6 +32374,12 @@ "webpack-merge": "^4.1.5" }, "dependencies": { + "balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true + }, "glob": { "version": "7.2.3", "dev": true, @@ -32364,7 +32393,9 @@ }, "dependencies": { "minimatch": { - "version": "3.1.2", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.4.tgz", + "integrity": "sha512-twmL+S8+7yIsE9wsqgzU3E8/LumN3M3QELrBZ20OdmQ9jB2JvW5oZtBEmft84k/Gs5CG9mqtWc6Y9vW+JEzGxw==", "dev": true, "requires": { "brace-expansion": "^1.1.7" @@ -32373,19 +32404,21 @@ } }, "minimatch": { - "version": "9.0.4", + "version": "9.0.7", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.7.tgz", + "integrity": "sha512-MOwgjc8tfrpn5QQEvjijjmDVtMw2oL88ugTevzxQnzRLm6l3fVEF2gzU0kYeYYKD8C66+IdGX6peJ4MyUlUnPg==", "dev": true, "requires": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^5.0.2" }, "dependencies": { "brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.3.tgz", + "integrity": "sha512-fy6KJm2RawA5RcHkLa1z/ScpBeA762UF9KmZQxwIbDtRJrgLzM10depAiEQ+CXYcoiqW1/m96OAAoke2nE9EeA==", "dev": true, "requires": { - "balanced-match": "^1.0.0" + "balanced-match": "^4.0.2" } } } @@ -32509,7 +32542,9 @@ } }, "lodash": { - "version": "4.17.21" + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", + "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==" }, "lodash.clone": { "version": "4.5.0", @@ -32733,7 +32768,9 @@ } }, "minimatch": { - "version": "3.1.2", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.4.tgz", + "integrity": "sha512-twmL+S8+7yIsE9wsqgzU3E8/LumN3M3QELrBZ20OdmQ9jB2JvW5oZtBEmft84k/Gs5CG9mqtWc6Y9vW+JEzGxw==", "dev": true, "requires": { "brace-expansion": "^1.1.7" @@ -32901,7 +32938,9 @@ } }, "minimatch": { - "version": "5.1.6", + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.8.tgz", + "integrity": "sha512-7RN35vit8DeBclkofOVmBY0eDAZZQd1HzmukRdSyz95CRh8FT54eqnbj0krQr3mrHR6sfRyYkyhwBWjoV5uqlQ==", "dev": true, "requires": { "brace-expansion": "^2.0.1" @@ -33036,22 +33075,28 @@ "minimatch": "^9.0.3" }, "dependencies": { + "balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true + }, "brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.3.tgz", + "integrity": "sha512-fy6KJm2RawA5RcHkLa1z/ScpBeA762UF9KmZQxwIbDtRJrgLzM10depAiEQ+CXYcoiqW1/m96OAAoke2nE9EeA==", "dev": true, "requires": { - "balanced-match": "^1.0.0" + "balanced-match": "^4.0.2" } }, "minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "version": "9.0.7", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.7.tgz", + "integrity": "sha512-MOwgjc8tfrpn5QQEvjijjmDVtMw2oL88ugTevzxQnzRLm6l3fVEF2gzU0kYeYYKD8C66+IdGX6peJ4MyUlUnPg==", "dev": true, "requires": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^5.0.2" } } } @@ -33580,6 +33625,12 @@ "version": "4.0.0", "dev": true }, + "path-expression-matcher": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.1.3.tgz", + "integrity": "sha512-qdVgY8KXmVdJZRSS1JdEPOKPdTiEK/pi0RkcT2sw1RhXxohdujUlJFPuS1TSkevZ9vzd3ZlL7ULl1MHGTApKzQ==", + "dev": true + }, "path-is-absolute": { "version": "1.0.1", "dev": true @@ -33627,13 +33678,6 @@ "version": "1.1.1", "dev": true }, - "pause-stream": { - "version": "0.0.11", - "dev": true, - "requires": { - "through": "~2.3" - } - }, "pend": { "version": "1.2.0", "dev": true @@ -33808,13 +33852,6 @@ "version": "1.0.1", "dev": true }, - "ps-tree": { - "version": "1.2.0", - "dev": true, - "requires": { - "event-stream": "=3.3.4" - } - }, "pump": { "version": "3.0.0", "dev": true, @@ -33888,9 +33925,9 @@ "dev": true }, "qs": { - "version": "6.14.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz", - "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==", + "version": "6.14.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz", + "integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==", "requires": { "side-channel": "^1.1.0" } @@ -34044,7 +34081,9 @@ } }, "minimatch": { - "version": "5.1.6", + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.8.tgz", + "integrity": "sha512-7RN35vit8DeBclkofOVmBY0eDAZZQd1HzmukRdSyz95CRh8FT54eqnbj0krQr3mrHR6sfRyYkyhwBWjoV5uqlQ==", "dev": true, "requires": { "brace-expansion": "^2.0.1" @@ -34355,9 +34394,9 @@ }, "dependencies": { "ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", "requires": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -34686,11 +34725,30 @@ } }, "socket.io-parser": { - "version": "4.2.4", + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.6.tgz", + "integrity": "sha512-asJqbVBDsBCJx0pTqw3WfesSY0iRX+2xzWEWzrpcH7L6fLzrhyF8WPI8UaeM4YCuDfpwA/cgsdugMsmtz8EJeg==", "dev": true, "requires": { "@socket.io/component-emitter": "~3.1.0", - "debug": "~4.3.1" + "debug": "~4.4.1" + }, + "dependencies": { + "debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "requires": { + "ms": "^2.1.3" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + } } }, "socks": { @@ -34801,13 +34859,6 @@ "version": "3.0.18", "dev": true }, - "split": { - "version": "0.3.3", - "dev": true, - "requires": { - "through": "2" - } - }, "split2": { "version": "4.2.0", "dev": true @@ -34854,13 +34905,6 @@ "version": "3.0.2", "dev": true }, - "stream-combiner": { - "version": "0.0.4", - "dev": true, - "requires": { - "duplexer": "~0.1.1" - } - }, "stream-composer": { "version": "1.0.2", "dev": true, @@ -35101,9 +35145,9 @@ "dev": true }, "strnum": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.1.1.tgz", - "integrity": "sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.2.1.tgz", + "integrity": "sha512-BwRvNd5/QoAtyW1na1y1LsJGQNvRlkde6Q/ipqqEaivoMdV+B1OMOTVdwR+N/cwVUcIt9PYyHmV8HyexCZSupg==", "dev": true }, "supports-color": { @@ -35165,34 +35209,6 @@ "streamx": "^2.12.5" } }, - "temp-fs": { - "version": "0.9.9", - "dev": true, - "requires": { - "rimraf": "~2.5.2" - }, - "dependencies": { - "glob": { - "version": "7.2.3", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "rimraf": { - "version": "2.5.4", - "dev": true, - "requires": { - "glob": "^7.0.5" - } - } - } - }, "ternary-stream": { "version": "3.0.0", "dev": true, @@ -35396,6 +35412,12 @@ "version": "3.0.1", "dev": true }, + "tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true + }, "triple-beam": { "version": "1.4.1", "dev": true @@ -35616,9 +35638,9 @@ "dev": true }, "undici": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-6.23.0.tgz", - "integrity": "sha512-VfQPToRA5FZs/qJxLIinmU59u0r7LXqoJkCzinq3ckNJp3vKEh7jTWN589YQ5+aoAC/TGRLyJLCPKcLQbM8r9g==", + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.24.1.tgz", + "integrity": "sha512-sC+b0tB1whOCzbtlx20fx3WgCXwkW627p4EA9uM+/tNNPkSS+eSEld6pAs9nDv7WbY1UUljBMYPtu9BCOrCWKA==", "dev": true }, "undici-types": { diff --git a/package.json b/package.json index 6f2cdf0ae0d..9ecb5bb3ad0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "10.23.0-pre", + "version": "11.2.0-pre", "description": "Header Bidding Management Library", "main": "dist/src/prebid.public.ts", "exports": { @@ -18,6 +18,7 @@ "./customize/*": "./customize/*.mjs" }, "scripts": { + "build": "gulp build", "serve": "gulp serve", "test": "gulp test", "lint": "gulp lint" diff --git a/plugins/buildOptions.js b/plugins/buildOptions.js index d1dbc59262b..aad89d1c9fa 100644 --- a/plugins/buildOptions.js +++ b/plugins/buildOptions.js @@ -40,7 +40,7 @@ function buildOptions(options) { const defineGlobal = typeof (options.defineGlobal) !== 'undefined' ? options.defineGlobal : prebid.defineGlobal; const features = featureMap(options.disableFeatures); const skipCalls = skipCallSet(features); // expression calls to skip entirely - const distUrlBase = options.prebidDistUrlBase || `https://cdn.jsdelivr.net/npm/prebid.js@${getNpmVersion(prebid.version)}/dist/chunks/`; + const distUrlBase = options.distUrlBase || `https://cdn.jsdelivr.net/npm/prebid.js@${getNpmVersion(prebid.version)}/dist/chunks/`; return { pbGlobal, diff --git a/plugins/eslint/approvedLoadExternalScriptPaths.js b/plugins/eslint/approvedLoadExternalScriptPaths.js new file mode 100644 index 00000000000..0eab8545924 --- /dev/null +++ b/plugins/eslint/approvedLoadExternalScriptPaths.js @@ -0,0 +1,48 @@ +// List of exact file paths or folder paths where loadExternalScript is allowed to be used. +// Folder paths (without file extension) allow all files in that folder. +const APPROVED_LOAD_EXTERNAL_SCRIPT_PATHS = [ + // Prebid maintained modules: + 'src/debugging.js', + 'src/Renderer.js', + // RTD modules: + 'modules/aaxBlockmeterRtdProvider.js', + 'modules/adagioRtdProvider.js', + 'modules/adlooxAnalyticsAdapter.js', + 'modules/arcspanRtdProvider.js', + 'modules/airgridRtdProvider.js', + 'modules/browsiRtdProvider.js', + 'modules/brandmetricsRtdProvider.js', + 'modules/cleanioRtdProvider.js', + 'modules/humansecurityMalvDefenseRtdProvider.js', + 'modules/humansecurityRtdProvider.ts', + 'modules/confiantRtdProvider.js', + 'modules/contxtfulRtdProvider.js', + 'modules/hadronRtdProvider.js', + 'modules/mediafilterRtdProvider.js', + 'modules/medianetRtdProvider.js', + 'modules/azerionedgeRtdProvider.js', + 'modules/a1MediaRtdProvider.js', + 'modules/geoedgeRtdProvider.js', + 'modules/qortexRtdProvider.js', + 'modules/dynamicAdBoostRtdProvider.js', + 'modules/51DegreesRtdProvider.js', + 'modules/symitriDapRtdProvider.js', + 'modules/wurflRtdProvider.js', + 'modules/nodalsAiRtdProvider.js', + 'modules/anonymisedRtdProvider.js', + 'modules/optableRtdProvider.js', + 'modules/oftmediaRtdProvider.js', + 'modules/panxoRtdProvider.js', + // UserId Submodules + 'modules/justIdSystem.js', + 'modules/tncIdSystem.js', + 'modules/ftrackIdSystem.js', + 'modules/id5IdSystem.js', + // Test files + '**/*spec.js', + '**/*spec.ts', + '**/test/**/*', +]; + +module.exports = APPROVED_LOAD_EXTERNAL_SCRIPT_PATHS; + diff --git a/scope3_segtax_pr.md b/scope3_segtax_pr.md deleted file mode 100644 index 7589507f8a4..00000000000 --- a/scope3_segtax_pr.md +++ /dev/null @@ -1,35 +0,0 @@ -# Proposed Addition to segtax.md - -Add this line to the "Vendor-specific Taxonomies" section (in numerical order): - -``` -604: Scope3 Agentic Execution Engine (AEE) Targeting Signals -``` - -## PR Description Template: - -**Title:** Add Scope3 AEE Targeting Signals Taxonomy (segtax ID 604) - -**Description:** - -This PR registers Scope3's Agentic Execution Engine (AEE) targeting signal taxonomy for use in OpenRTB segment data. - -**Details:** -- **Taxonomy ID:** 604 -- **Name:** Scope3 Agentic Execution Engine (AEE) Targeting Signals -- **Purpose:** Identifies proprietary targeting signals generated by Scope3's AEE for real-time programmatic optimization -- **Usage:** These are opaque targeting codes (e.g., "x82s", "a91k") used for line item targeting decisions, not traditional audience segments - -**Contact:** [Your email] - -cc: @bretg @slimkrazy (as listed approvers in the document) - -## Alternative Higher ID: - -If you want to avoid any potential conflicts with IDs in the 600s range, you could use: - -``` -1001: Scope3 Agentic Execution Engine (AEE) Targeting Signals -``` - -This would put you well clear of any existing entries while still in the vendor-specific range. \ No newline at end of file diff --git a/src/Renderer.js b/src/Renderer.js index c9d68b48993..fee6c865e88 100644 --- a/src/Renderer.js +++ b/src/Renderer.js @@ -2,7 +2,7 @@ import { loadExternalScript } from './adloader.js'; import { logError, logWarn, logMessage } from './utils.js'; -import {getGlobal} from './prebidGlobal.js'; +import { getGlobal } from './prebidGlobal.js'; import { MODULE_TYPE_PREBID } from './activities/modules.js'; const pbjsInstance = getGlobal(); diff --git a/src/activities/activities.js b/src/activities/activities.js index 53d73e26c3b..f436222603b 100644 --- a/src/activities/activities.js +++ b/src/activities/activities.js @@ -60,3 +60,8 @@ export const LOAD_EXTERNAL_SCRIPT = 'loadExternalScript'; * accessRequestCredentials: setting withCredentials flag in ajax request config */ export const ACTIVITY_ACCESS_REQUEST_CREDENTIALS = 'accessRequestCredentials'; + +/** + * acceptBid: a bid is about to be accepted. + */ +export const ACTIVITY_ADD_BID_RESPONSE = 'acceptBid'; diff --git a/src/activities/activityParams.js b/src/activities/activityParams.js index f33ceb2a9a4..9eb1ea2d69d 100644 --- a/src/activities/activityParams.js +++ b/src/activities/activityParams.js @@ -1,5 +1,5 @@ import adapterManager from '../adapterManager.js'; -import {activityParamsBuilder} from './params.js'; +import { activityParamsBuilder } from './params.js'; /** * Utility function for building common activity parameters - broken out to its own diff --git a/src/activities/params.js b/src/activities/params.js index 975d9b66cc4..d036a1de023 100644 --- a/src/activities/params.js +++ b/src/activities/params.js @@ -1,5 +1,5 @@ -import {MODULE_TYPE_BIDDER} from './modules.js'; -import {hook} from '../hook.js'; +import { MODULE_TYPE_BIDDER } from './modules.js'; +import { hook } from '../hook.js'; /** * Component ID - who is trying to perform the activity? diff --git a/src/activities/redactor.ts b/src/activities/redactor.ts index d58dd3588ce..4132c02e64d 100644 --- a/src/activities/redactor.ts +++ b/src/activities/redactor.ts @@ -1,6 +1,6 @@ -import {deepAccess} from '../utils.js'; -import {config} from '../config.js'; -import {isActivityAllowed, registerActivityControl} from './rules.js'; +import { deepAccess } from '../utils.js'; +import { config } from '../config.js'; +import { isActivityAllowed, registerActivityControl } from './rules.js'; import { ACTIVITY_TRANSMIT_EIDS, ACTIVITY_TRANSMIT_PRECISE_GEO, @@ -19,7 +19,7 @@ export const ORTB_UFPD_PATHS = [ 'id', 'buyeruid', 'customdata' -].map(f => `user.${f}`).concat('device.ext.cdep'); +].map(f => `user.${f}`).concat('device.ext.cdep', 'device.ifa'); export const ORTB_EIDS_PATHS = ['user.eids', 'user.ext.eids']; export const ORTB_GEO_PATHS = ['user.geo.lat', 'user.geo.lon', 'device.geo.lat', 'device.geo.lon']; export const ORTB_IPV4_PATHS = ['device.ip'] @@ -224,6 +224,6 @@ declare module '../config' { // by default, TIDs are off since version 8 registerActivityControl(ACTIVITY_TRANSMIT_TID, 'enableTIDs config', () => { if (!config.getConfig('enableTIDs')) { - return {allow: false, reason: 'TIDs are disabled'} + return { allow: false, reason: 'TIDs are disabled' } } }); diff --git a/src/activities/rules.js b/src/activities/rules.js index db4eeab7241..7902dadad5d 100644 --- a/src/activities/rules.js +++ b/src/activities/rules.js @@ -1,5 +1,5 @@ -import {prefixLog} from '../utils.js'; -import {ACTIVITY_PARAM_COMPONENT} from './params.js'; +import { prefixLog } from '../utils.js'; +import { ACTIVITY_PARAM_COMPONENT } from './params.js'; /** * @param logger @@ -19,16 +19,16 @@ export function ruleRegistry(logger = prefixLog('Activity control:')) { res = rule(params); } catch (e) { logger.logError(`Exception in rule ${name} for '${activity}'`, e); - res = {allow: false, reason: e}; + res = { allow: false, reason: e }; } - return res && Object.assign({activity, name, component: params[ACTIVITY_PARAM_COMPONENT]}, res); + return res && Object.assign({ activity, name, component: params[ACTIVITY_PARAM_COMPONENT] }, res); } const dupes = {}; const DEDUPE_INTERVAL = 1000; // eslint-disable-next-line no-restricted-syntax - function logResult({activity, name, allow, reason, component}) { + function logResult({ activity, name, allow, reason, component }) { const msg = `${name} ${allow ? 'allowed' : 'denied'} '${activity}' for '${component}'${reason ? ':' : ''}`; const deduping = dupes.hasOwnProperty(msg); if (deduping) { diff --git a/src/adRendering.ts b/src/adRendering.ts index b35959c75e0..c7b02468f7d 100644 --- a/src/adRendering.ts +++ b/src/adRendering.ts @@ -9,20 +9,21 @@ import { triggerPixel } from './utils.js'; import * as events from './events.js'; -import {AD_RENDER_FAILED_REASON, BID_STATUS, EVENTS, MESSAGES, PB_LOCATOR} from './constants.js'; -import {config} from './config.js'; -import {executeRenderer, isRendererRequired} from './Renderer.js'; -import {VIDEO} from './mediaTypes.js'; -import {auctionManager} from './auctionManager.js'; -import {getCreativeRenderer} from './creativeRenderers.js'; -import {hook} from './hook.js'; -import {fireNativeTrackers} from './native.js'; -import {PbPromise} from './utils/promise.js'; +import { AD_RENDER_FAILED_REASON, BID_STATUS, EVENTS, MESSAGES, PB_LOCATOR } from './constants.js'; +import { config } from './config.js'; +import { executeRenderer, isRendererRequired } from './Renderer.js'; +import { VIDEO } from './mediaTypes.js'; +import { auctionManager } from './auctionManager.js'; +import { getCreativeRenderer } from './creativeRenderers.js'; +import { hook } from './hook.js'; +import { fireNativeTrackers } from './native.js'; import adapterManager from './adapterManager.js'; -import {useMetrics} from './utils/perfMetrics.js'; -import {filters} from './targeting.js'; -import {EVENT_TYPE_WIN, parseEventTrackers, TRACKER_METHOD_IMG} from './eventTrackers.js'; -import type {Bid} from "./bidfactory.ts"; +import { useMetrics } from './utils/perfMetrics.js'; +import { filters } from './targeting.js'; +import { EVENT_TYPE_WIN, parseEventTrackers, TRACKER_METHOD_IMG } from './eventTrackers.js'; +import type { Bid } from "./bidfactory.ts"; +import { yieldsIf } from "./utils/yield.ts"; +import { PbPromise } from "./utils/promise.ts"; const { AD_RENDER_FAILED, AD_RENDER_SUCCEEDED, STALE_RENDER, BID_WON, EXPIRED_RENDER } = EVENTS; const { EXCEPTION } = AD_RENDER_FAILED_REASON; @@ -55,12 +56,6 @@ declare module './events' { } } -export const getBidToRender = hook('sync', function (adId, forRender = true, override = PbPromise.resolve()) { - return override - .then(bid => bid ?? auctionManager.findBidByAdId(adId)) - .catch(() => {}) -}) - export const markWinningBid = hook('sync', function (bid) { (parseEventTrackers(bid.eventtrackers)[EVENT_TYPE_WIN]?.[TRACKER_METHOD_IMG] || []) .forEach(url => triggerPixel(url)); @@ -122,7 +117,7 @@ type AdRenderSucceededData = { * (Note: Invocation of this function indicates that the render function did not generate an error, it does not guarantee that tracking for this event has occurred yet.) */ export function emitAdRenderSucceeded({ doc, bid, id }) { - const data: AdRenderSucceededData = { doc, bid, adId: id}; + const data: AdRenderSucceededData = { doc, bid, adId: id }; adapterManager.callAdRenderSucceededBidder(bid.adapterCode || bid.bidder, bid); @@ -176,7 +171,7 @@ export function handleCreativeEvent(data, bidResponse) { } } -export function handleNativeMessage(data, bidResponse, {resizeFn, fireTrackers = fireNativeTrackers}) { +export function handleNativeMessage(data, bidResponse, { resizeFn, fireTrackers = fireNativeTrackers }) { switch (data.action) { case 'resizeNativeHeight': resizeFn(data.width, data.height); @@ -207,7 +202,7 @@ type RenderOptions = { } export const getRenderingData = hook('sync', function (bidResponse: Bid, options?: RenderOptions): Record { - const {ad, adUrl, cpm, originalCpm, width, height, instl} = bidResponse + const { ad, adUrl, cpm, originalCpm, width, height, instl } = bidResponse const repl = { AUCTION_PRICE: originalCpm || cpm, CLICKTHROUGH: options?.clickUrl || '' @@ -221,7 +216,7 @@ export const getRenderingData = hook('sync', function (bidResponse: Bid, options }; }) -export const doRender = hook('sync', function({renderFn, resizeFn, bidResponse, options, doc, isMainDocument = doc === document && !inIframe()}) { +export const doRender = hook('sync', function({ renderFn, resizeFn, bidResponse, options, doc, isMainDocument = doc === document && !inIframe() }) { const videoBid = (FEATURES.VIDEO && bidResponse.mediaType === VIDEO) if (isMainDocument || videoBid) { emitAdRenderFail({ @@ -233,8 +228,8 @@ export const doRender = hook('sync', function({renderFn, resizeFn, bidResponse, return; } const data = getRenderingData(bidResponse, options); - renderFn(Object.assign({adId: bidResponse.adId}, data)); - const {width, height} = data; + renderFn(Object.assign({ adId: bidResponse.adId }, data)); + const { width, height } = data; if ((width ?? height) != null) { resizeFn(width, height); } @@ -242,17 +237,17 @@ export const doRender = hook('sync', function({renderFn, resizeFn, bidResponse, doRender.before(function (next, args) { // run renderers from a high priority hook to allow the video module to insert itself between this and "normal" rendering. - const {bidResponse, doc} = args; + const { bidResponse, doc } = args; if (isRendererRequired(bidResponse.renderer)) { executeRenderer(bidResponse.renderer, bidResponse, doc); - emitAdRenderSucceeded({doc, bid: bidResponse, id: bidResponse.adId}) + emitAdRenderSucceeded({ doc, bid: bidResponse, id: bidResponse.adId }) next.bail(); } else { next(args); } }, 100) -export function handleRender({renderFn, resizeFn, adId, options, bidResponse, doc}) { +export function handleRender({ renderFn, resizeFn, adId, options, bidResponse, doc }) { deferRendering(bidResponse, () => { if (bidResponse == null) { emitAdRenderFail({ @@ -278,7 +273,7 @@ export function handleRender({renderFn, resizeFn, adId, options, bidResponse, do } try { - doRender({renderFn, resizeFn, bidResponse, options, doc}); + doRender({ renderFn, resizeFn, bidResponse, options, doc }); } catch (e) { emitAdRenderFail({ reason: AD_RENDER_FAILED_REASON.EXCEPTION, @@ -331,10 +326,15 @@ export function renderIfDeferred(bidResponse) { } } -export function renderAdDirect(doc, adId, options) { +let legacyRender = false; +config.getConfig('auctionOptions', (opts) => { + legacyRender = opts.auctionOptions?.legacyRender ?? false +}); + +export const renderAdDirect = yieldsIf(() => !legacyRender, function renderAdDirect(doc, adId, options) { let bid; function fail(reason, message) { - emitAdRenderFail(Object.assign({id: adId, bid}, {reason, message})); + emitAdRenderFail(Object.assign({ id: adId, bid }, { reason, message })); } function resizeFn(width, height) { const frame = doc.defaultView?.frameElement; @@ -349,7 +349,7 @@ export function renderAdDirect(doc, adId, options) { } } } - const messageHandler = creativeMessageHandler({resizeFn}); + const messageHandler = creativeMessageHandler({ resizeFn }); function waitForDocumentReady(doc) { return new PbPromise((resolve) => { @@ -362,20 +362,26 @@ export function renderAdDirect(doc, adId, options) { } function renderFn(adData) { - PbPromise.all([ - getCreativeRenderer(bid), - waitForDocumentReady(doc) - ]).then(([render]) => render(adData, { - sendMessage: (type, data) => messageHandler(type, data, bid), - mkFrame: createIframe, - }, doc.defaultView)) - .then( - () => emitAdRenderSucceeded({doc, bid, id: bid.adId}), - (e) => { - fail(e?.reason || AD_RENDER_FAILED_REASON.EXCEPTION, e?.message) - e?.stack && logError(e); - } - ); + if (adData.ad && legacyRender) { + doc.write(adData.ad); + doc.close(); + emitAdRenderSucceeded({ doc, bid, id: bid.adId }); + } else { + PbPromise.all([ + getCreativeRenderer(bid), + waitForDocumentReady(doc) + ]).then(([render]) => render(adData, { + sendMessage: (type, data) => messageHandler(type, data, bid), + mkFrame: createIframe, + }, doc.defaultView)) + .then( + () => emitAdRenderSucceeded({ doc, bid, id: bid.adId }), + (e) => { + fail(e?.reason || AD_RENDER_FAILED_REASON.EXCEPTION, e?.message) + e?.stack && logError(e); + } + ); + } // TODO: this is almost certainly the wrong way to do this const creativeComment = document.createComment(`Creative ${bid.creativeId} served by ${bid.bidder} Prebid.js Header Bidding`); insertElement(creativeComment, doc, 'html'); @@ -384,15 +390,13 @@ export function renderAdDirect(doc, adId, options) { if (!adId || !doc) { fail(AD_RENDER_FAILED_REASON.MISSING_DOC_OR_ADID, `missing ${adId ? 'doc' : 'adId'}`); } else { - getBidToRender(adId).then(bidResponse => { - bid = bidResponse; - handleRender({renderFn, resizeFn, adId, options: {clickUrl: options?.clickThrough}, bidResponse, doc}); - }); + bid = auctionManager.findBidByAdId(adId) + handleRender({ renderFn, resizeFn, adId, options: { clickUrl: options?.clickThrough }, bidResponse: bid, doc }); } } catch (e) { fail(EXCEPTION, e.message); } -} +}); /** * Insert an invisible, named iframe that can be used by creatives to locate the window Prebid is running in diff --git a/src/adUnits.ts b/src/adUnits.ts index 2cb94f788e3..bdc86ee33b5 100644 --- a/src/adUnits.ts +++ b/src/adUnits.ts @@ -1,8 +1,8 @@ -import type {AdUnitCode, BidderCode, ContextIdentifiers, Size} from "./types/common.d.ts"; -import type {ORTBImp} from "./types/ortb/request.d.ts"; -import type {Bid} from "./bidfactory.ts"; -import type {MediaTypes} from "./mediaTypes.ts"; -import type {DeepPartial} from "./types/objects.d.ts"; +import type { AdUnitCode, BidderCode, ContextIdentifiers, Size } from "./types/common.d.ts"; +import type { ORTBImp } from "./types/ortb/request.d.ts"; +import type { Bid } from "./bidfactory.ts"; +import type { MediaTypes } from "./mediaTypes.ts"; +import type { DeepPartial } from "./types/objects.d.ts"; export interface RendererConfig { /** @@ -88,6 +88,11 @@ export interface AdUnitDefinition { * Used by setTargetingForGPTAsync() to match which auction is for which ad slot. */ code: AdUnitCode; + /** + * A DOM element corresponding to this ad unit. + * By default, this is `document.getElementById(adUnit.code)`. + */ + element?: HTMLElement; /** * Bid requests representing demand partners and associated parameters. */ diff --git a/src/adapterManager.ts b/src/adapterManager.ts index 1483899029f..328ab57335d 100644 --- a/src/adapterManager.ts +++ b/src/adapterManager.ts @@ -22,11 +22,11 @@ import { timestamp, uniques, } from './utils.js'; -import {decorateAdUnitsWithNativeParams, nativeAdapters} from './native.js'; -import {newBidder} from './adapters/bidderFactory.js'; -import {ajaxBuilder} from './ajax.js'; -import {config, RANDOM} from './config.js'; -import {hook} from './hook.js'; +import { decorateAdUnitsWithNativeParams, nativeAdapters } from './native.js'; +import { newBidder } from './adapters/bidderFactory.js'; +import { ajaxBuilder } from './ajax.js'; +import { config, RANDOM } from './config.js'; +import { hook } from './hook.js'; import { type AdUnit, type AdUnitBid, @@ -40,18 +40,18 @@ import { incrementBidderWinsCounter, incrementRequestsCounter } from './adUnits.js'; -import {getRefererInfo, type RefererInfo} from './refererDetection.js'; -import {GDPR_GVLIDS, gdprDataHandler, gppDataHandler, uspDataHandler,} from './consentHandler.js'; +import { getRefererInfo, type RefererInfo } from './refererDetection.js'; +import { GDPR_GVLIDS, gdprDataHandler, gppDataHandler, uspDataHandler, } from './consentHandler.js'; import * as events from './events.js'; -import {EVENTS, S2S} from './constants.js'; -import {type Metrics, useMetrics} from './utils/perfMetrics.js'; -import {auctionManager} from './auctionManager.js'; -import {MODULE_TYPE_ANALYTICS, MODULE_TYPE_BIDDER, MODULE_TYPE_PREBID} from './activities/modules.js'; -import {isActivityAllowed} from './activities/rules.js'; -import {ACTIVITY_FETCH_BIDS, ACTIVITY_REPORT_ANALYTICS} from './activities/activities.js'; -import {ACTIVITY_PARAM_ANL_CONFIG, ACTIVITY_PARAM_S2S_NAME, activityParamsBuilder} from './activities/params.js'; -import {redactor} from './activities/redactor.js'; -import {EVENT_TYPE_IMPRESSION, parseEventTrackers, TRACKER_METHOD_IMG} from './eventTrackers.js'; +import { EVENTS, S2S } from './constants.js'; +import { type Metrics, useMetrics } from './utils/perfMetrics.js'; +import { auctionManager } from './auctionManager.js'; +import { MODULE_TYPE_ANALYTICS, MODULE_TYPE_BIDDER, MODULE_TYPE_PREBID } from './activities/modules.js'; +import { isActivityAllowed } from './activities/rules.js'; +import { ACTIVITY_FETCH_BIDS, ACTIVITY_REPORT_ANALYTICS } from './activities/activities.js'; +import { ACTIVITY_PARAM_ANL_CONFIG, ACTIVITY_PARAM_S2S_NAME, activityParamsBuilder } from './activities/params.js'; +import { redactor } from './activities/redactor.js'; +import { EVENT_TYPE_IMPRESSION, parseEventTrackers, TRACKER_METHOD_IMG } from './eventTrackers.js'; import type { AdUnitCode, BidderCode, @@ -61,15 +61,15 @@ import type { ORTBFragments, Size, StorageDisclosure } from "./types/common.d.ts"; -import type {DeepPartial} from "./types/objects.d.ts"; -import type {ORTBRequest} from "./types/ortb/request.d.ts"; +import type { DeepPartial } from "./types/objects.d.ts"; +import type { ORTBRequest } from "./types/ortb/request.d.ts"; import type { AnalyticsConfig, AnalyticsProvider, AnalyticsProviderConfig, } from "../libraries/analyticsAdapter/AnalyticsAdapter.ts"; -import {getGlobal} from "./prebidGlobal.ts"; +import { getGlobal } from "./prebidGlobal.ts"; -export {gdprDataHandler, gppDataHandler, uspDataHandler, coppaDataHandler} from './consentHandler.js'; +export { gdprDataHandler, gppDataHandler, uspDataHandler, coppaDataHandler } from './consentHandler.js'; export const PBS_ADAPTER_NAME = 'pbsBidAdapter'; export const PARTITIONS = { @@ -84,7 +84,7 @@ export const dep = { const _bidderRegistry = {}; const _aliasRegistry: { [aliasCode: BidderCode]: BidderCode } = {}; -const _analyticsRegistry: { [P in AnalyticsProvider]?: { adapter: AnalyticsAdapter

, gvlid?: number }} = {}; +const _analyticsRegistry: { [P in AnalyticsProvider]?: { adapter: AnalyticsAdapter

, gvlid?: number } } = {}; let _s2sConfigs = []; config.getConfig('s2sConfig', config => { @@ -93,7 +93,7 @@ config.getConfig('s2sConfig', config => { } }); -const activityParams = activityParamsBuilder((alias) => adapterManager.resolveAlias(alias)); +export const activityParams = activityParamsBuilder((alias) => adapterManager.resolveAlias(alias)); function getConfigName(s2sConfig) { // According to our docs, "module" bid (stored impressions) @@ -155,7 +155,7 @@ export interface BaseBidRequest extends ContextIdentifiers, Pick; } -export interface StoredBidRequest extends BaseBidRequest, Omit<{[K in keyof AdUnitBidderBid]?: undefined}, keyof BaseBidRequest> { +export interface StoredBidRequest extends BaseBidRequest, Omit<{ [K in keyof AdUnitBidderBid]?: undefined }, keyof BaseBidRequest> { bidder: null; src: typeof S2S.SRC; } @@ -214,6 +214,7 @@ const ADUNIT_BID_PROPERTIES = [ 'nativeParams', 'nativeOrtbRequest', 'renderer', + 'element', ] as const; type GetBidsOptions = { @@ -247,11 +248,11 @@ export type AnalyticsAdapter

= StorageDisclosure & gvlid?: number | ((config: AnalyticsConfig

) => number); } -function getBids({bidderCode, auctionId, bidderRequestId, adUnits, src, metrics, getTid}: GetBidsOptions): BidRequest[] { +function getBids({ bidderCode, auctionId, bidderRequestId, adUnits, src, metrics, getTid }: GetBidsOptions): BidRequest[] { return adUnits.reduce((result, adUnit) => { const bids = adUnit.bids.filter(bid => bid.bidder === bidderCode); if (bidderCode == null && bids.length === 0 && (adUnit as PBSAdUnit).s2sBid != null) { - bids.push({bidder: null}); + bids.push({ bidder: null }); } result.push( bids.reduce((bids: BidRequest[], bid: BidRequest) => { @@ -262,7 +263,7 @@ function getBids({bidde {}, adUnit.ortb2Imp, bid.ortb2Imp, - {ext: {tid, tidSource}}) + { ext: { tid, tidSource } }) }, getDefinedParams(adUnit, ADUNIT_BID_PROPERTIES), ); @@ -313,7 +314,7 @@ function getBids({bidde * @param s2sConfig null if the adUnit is being routed to a client adapter; otherwise the s2s adapter's config * @returns the subset of `bids` that are pertinent for the given `s2sConfig` */ -export const filterBidsForAdUnit = hook('sync', function(bids, s2sConfig, {getS2SBidders = getS2SBidderSet} = {}) { +export const filterBidsForAdUnit = hook('sync', function(bids, s2sConfig, { getS2SBidders = getS2SBidderSet } = {}) { if (s2sConfig == null) { return bids; } else { @@ -367,7 +368,7 @@ function getAdUnitCopyForPrebidServer(adUnits: AdUnit[], s2sConfig) { }); // don't send empty requests - return {adUnits: adUnitsCopy, hasModuleBids}; + return { adUnits: adUnitsCopy, hasModuleBids }; } function getAdUnitCopyForClientAdapters(adUnits: AdUnit[]) { @@ -417,13 +418,13 @@ export function getS2SBidderSet(s2sConfigs) { * @returns {Object} return.client - Array of bidder codes that should be routed to client adapters. * @returns {Object} return.server - Array of bidder codes that should be routed to server adapters. */ -export function _partitionBidders (adUnits, s2sConfigs, {getS2SBidders = getS2SBidderSet} = {}) { +export function _partitionBidders (adUnits, s2sConfigs, { getS2SBidders = getS2SBidderSet } = {}) { const serverBidders = getS2SBidders(s2sConfigs); return getBidderCodes(adUnits).reduce((memo, bidder) => { const partition = serverBidders.has(bidder) ? PARTITIONS.SERVER : PARTITIONS.CLIENT; memo[partition].push(bidder); return memo; - }, {[PARTITIONS.CLIENT]: [], [PARTITIONS.SERVER]: []}) + }, { [PARTITIONS.CLIENT]: [], [PARTITIONS.SERVER]: [] }) } export const partitionBidders = hook('sync', _partitionBidders, 'partitionBidders'); @@ -506,17 +507,68 @@ const adapterManager = { .filter(uniques) .forEach(incrementAuctionsCounter); - let {[PARTITIONS.CLIENT]: clientBidders, [PARTITIONS.SERVER]: serverBidders} = partitionBidders(adUnits, _s2sConfigs); + const ortb2 = ortb2Fragments.global || {}; + const bidderOrtb2 = ortb2Fragments.bidder || {}; + + const getTid = tidFactory(); + + const getCacheKey = (bidderCode: BidderCode, s2sActivityParams?): string => { + const s2sName = s2sActivityParams != null ? s2sActivityParams[ACTIVITY_PARAM_S2S_NAME] : ''; + return s2sName ? `${bidderCode}:${s2sName}` : `${bidderCode}:`; + }; + + const mergeBidderFpd = (() => { + const fpdCache: any = {}; + return function(auctionId: string, bidderCode: BidderCode, s2sActivityParams?) { + const cacheKey = getCacheKey(bidderCode, s2sActivityParams); + const redact = dep.redact( + s2sActivityParams != null + ? s2sActivityParams + : activityParams(MODULE_TYPE_BIDDER, bidderCode) + ); + if (fpdCache[cacheKey] !== undefined) { + return [fpdCache[cacheKey], redact]; + } + const [tid, tidSource] = getTid(bidderCode, auctionId, bidderOrtb2[bidderCode]?.source?.tid ?? ortb2.source?.tid); + const fpd = Object.freeze(redact.ortb2(mergeDeep( + {}, + ortb2, + bidderOrtb2[bidderCode], + { + source: { + tid, + ext: { tidSource } + } + } + ))); + fpdCache[cacheKey] = fpd; + return [fpd, redact]; + } + })(); + + let { [PARTITIONS.CLIENT]: clientBidders, [PARTITIONS.SERVER]: serverBidders } = partitionBidders(adUnits, _s2sConfigs); const allowedBidders = new Set(); adUnits.forEach(au => { if (!isPlainObject(au.mediaTypes)) { au.mediaTypes = {}; } + // filter out bidders that cannot participate in the auction - au.bids = au.bids.filter((bid) => !bid.bidder || dep.isAllowed(ACTIVITY_FETCH_BIDS, activityParams(MODULE_TYPE_BIDDER, bid.bidder, { - isS2S: serverBidders.includes(bid.bidder) && !clientBidders.includes(bid.bidder) - }))) + au.bids = au.bids.filter((bid) => { + if (!bid.bidder) { + return true; + } + const [ortb2] = mergeBidderFpd(auctionId, bid.bidder); + const isS2S = serverBidders.includes(bid.bidder) && !clientBidders.includes(bid.bidder); + return dep.isAllowed(ACTIVITY_FETCH_BIDS, activityParams(MODULE_TYPE_BIDDER, bid.bidder, { + bid, + ortb2, + adUnit: au, + auctionId, + isS2S + })); + }); au.bids.forEach(bid => { allowedBidders.add(bid.bidder); }); @@ -535,29 +587,8 @@ const adapterManager = { const bidRequests: BidderRequest[] = []; - const ortb2 = ortb2Fragments.global || {}; - const bidderOrtb2 = ortb2Fragments.bidder || {}; - - const getTid = tidFactory(); - function addOrtb2>(bidderRequest: Partial, s2sActivityParams?): T { - const redact = dep.redact( - s2sActivityParams != null - ? s2sActivityParams - : activityParams(MODULE_TYPE_BIDDER, bidderRequest.bidderCode) - ); - const [tid, tidSource] = getTid(bidderRequest.bidderCode, bidderRequest.auctionId, bidderOrtb2[bidderRequest.bidderCode]?.source?.tid ?? ortb2.source?.tid); - const fpd = Object.freeze(redact.ortb2(mergeDeep( - {}, - ortb2, - bidderOrtb2[bidderRequest.bidderCode], - { - source: { - tid, - ext: {tidSource} - } - } - ))); + const [fpd, redact] = mergeBidderFpd(bidderRequest.auctionId, bidderRequest.bidderCode, s2sActivityParams); bidderRequest.ortb2 = fpd; bidderRequest.bids = bidderRequest.bids.map((bid) => { bid.ortb2 = fpd; @@ -578,7 +609,7 @@ const adapterManager = { _s2sConfigs.forEach(s2sConfig => { const s2sParams = s2sActivityParams(s2sConfig); if (s2sConfig && s2sConfig.enabled && dep.isAllowed(ACTIVITY_FETCH_BIDS, s2sParams)) { - const {adUnits: adUnitsS2SCopy, hasModuleBids} = getAdUnitCopyForPrebidServer(adUnits, s2sConfig); + const { adUnits: adUnitsS2SCopy, hasModuleBids } = getAdUnitCopyForPrebidServer(adUnits, s2sConfig); // uniquePbsTid is so we know which server to send which bids to during the callBids function const uniquePbsTid = generateUUID(); @@ -723,7 +754,7 @@ const adapterManager = { const uniqueServerRequests = serverBidderRequests.filter(serverBidRequest => serverBidRequest.uniquePbsTid === uniquePbsTid); if (s2sAdapter) { - const s2sBidRequest = {'ad_units': adUnitsS2SCopy, s2sConfig, ortb2Fragments, requestBidsTimeout}; + const s2sBidRequest = { 'ad_units': adUnitsS2SCopy, s2sConfig, ortb2Fragments, requestBidsTimeout }; if (s2sBidRequest.ad_units.length) { const doneCbs = uniqueServerRequests.map(bidRequest => { bidRequest.start = timestamp(); @@ -787,13 +818,13 @@ const adapterManager = { ) ); } catch (e) { - logError(`${bidderRequest.bidderCode} Bid Adapter emitted an uncaught error when parsing their bidRequest`, {e, bidRequest: bidderRequest}); + logError(`${bidderRequest.bidderCode} Bid Adapter emitted an uncaught error when parsing their bidRequest`, { e, bidRequest: bidderRequest }); adapterDone(); } }) }, videoAdapters: [], - registerBidAdapter(bidAdapter, bidderCode, {supportedMediaTypes = []} = {}) { + registerBidAdapter(bidAdapter, bidderCode, { supportedMediaTypes = [] } = {}) { if (bidAdapter && bidderCode) { if (typeof bidAdapter.callBids === 'function') { _bidderRegistry[bidderCode] = bidAdapter; @@ -874,7 +905,7 @@ const adapterManager = { } return code; }, - registerAnalyticsAdapter

({adapter, code, gvlid}: { + registerAnalyticsAdapter

({ adapter, code, gvlid }: { adapter: AnalyticsAdapter

, code: P, gvlid?: number @@ -904,7 +935,7 @@ const adapterManager = { config.forEach(adapterConfig => { const entry = _analyticsRegistry[adapterConfig.provider]; if (entry && entry.adapter) { - if (dep.isAllowed(ACTIVITY_REPORT_ANALYTICS, activityParams(MODULE_TYPE_ANALYTICS, adapterConfig.provider, {[ACTIVITY_PARAM_ANL_CONFIG]: adapterConfig}))) { + if (dep.isAllowed(ACTIVITY_REPORT_ANALYTICS, activityParams(MODULE_TYPE_ANALYTICS, adapterConfig.provider, { [ACTIVITY_PARAM_ANL_CONFIG]: adapterConfig }))) { entry.adapter.enableAnalytics(adapterConfig); } } else { diff --git a/src/adapters/bidderFactory.ts b/src/adapters/bidderFactory.ts index 60f21964f88..737c26117e1 100644 --- a/src/adapters/bidderFactory.ts +++ b/src/adapters/bidderFactory.ts @@ -4,12 +4,12 @@ import adapterManager, { type BidRequest, type ClientBidderRequest } from '../adapterManager.js'; -import {config} from '../config.js'; -import {BannerBid, Bid, BidResponse, createBid} from '../bidfactory.js'; -import {type SyncType, userSync} from '../userSync.js'; -import {nativeBidIsValid} from '../native.js'; -import {isValidVideoBid} from '../video.js'; -import {EVENTS, REJECTION_REASON, DEBUG_MODE} from '../constants.js'; +import { config } from '../config.js'; +import { BannerBid, Bid, BidResponse, createBid } from '../bidfactory.js'; +import { type SyncType, userSync } from '../userSync.js'; +import { nativeBidIsValid } from '../native.js'; +import { isValidVideoBid } from '../video.js'; +import { EVENTS, REJECTION_REASON, DEBUG_MODE } from '../constants.js'; import * as events from '../events.js'; import { @@ -28,20 +28,20 @@ import { getParameterByName, debugTurnedOn } from '../utils.js'; -import {hook} from '../hook.js'; -import {auctionManager} from '../auctionManager.js'; -import {bidderSettings} from '../bidderSettings.js'; -import {useMetrics} from '../utils/perfMetrics.js'; -import {isActivityAllowed} from '../activities/rules.js'; -import {activityParams} from '../activities/activityParams.js'; -import {MODULE_TYPE_BIDDER} from '../activities/modules.js'; -import {ACTIVITY_TRANSMIT_TID, ACTIVITY_TRANSMIT_UFPD} from '../activities/activities.js'; -import type {AnyFunction, Wraps} from "../types/functions.d.ts"; -import type {BidderCode, StorageDisclosure} from "../types/common.d.ts"; -import type {Ajax, AjaxOptions, XHR} from "../ajax.ts"; -import type {AddBidResponse} from "../auction.ts"; -import type {MediaType} from "../mediaTypes.ts"; -import {CONSENT_GDPR, CONSENT_GPP, CONSENT_USP, type ConsentData} from "../consentHandler.ts"; +import { hook } from '../hook.js'; +import { auctionManager } from '../auctionManager.js'; +import { bidderSettings } from '../bidderSettings.js'; +import { useMetrics } from '../utils/perfMetrics.js'; +import { isActivityAllowed } from '../activities/rules.js'; +import { activityParams } from '../activities/activityParams.js'; +import { MODULE_TYPE_BIDDER } from '../activities/modules.js'; +import { ACTIVITY_TRANSMIT_TID, ACTIVITY_TRANSMIT_UFPD } from '../activities/activities.js'; +import type { AnyFunction, Wraps } from "../types/functions.d.ts"; +import type { BidderCode, StorageDisclosure } from "../types/common.d.ts"; +import type { Ajax, AjaxOptions, XHR } from "../ajax.ts"; +import type { AddBidResponse } from "../auction.ts"; +import type { MediaType } from "../mediaTypes.ts"; +import { CONSENT_GDPR, CONSENT_GPP, CONSENT_USP, type ConsentData } from "../consentHandler.ts"; /** * This file aims to support Adapters during the Prebid 0.x -> 1.x transition. @@ -197,7 +197,7 @@ export function registerBidder(spec: BidderSpec) { } } -export const guardTids: any = memoize(({bidderCode}) => { +export const guardTids: any = memoize(({ bidderCode }) => { const tidsAllowed = isActivityAllowed(ACTIVITY_TRANSMIT_TID, activityParams(MODULE_TYPE_BIDDER, bidderCode)); function get(target, prop, receiver) { if (TIDS.hasOwnProperty(prop)) { @@ -215,7 +215,7 @@ export const guardTids: any = memoize(({bidderCode}) => { }); return proxy; } - const bidRequest = memoize((br) => privateAccessProxy(br, {get}), (arg) => arg.bidId); + const bidRequest = memoize((br) => privateAccessProxy(br, { get }), (arg) => arg.bidId); /** * Return a view on bidd(er) requests where auctionId/transactionId are nulled if the bidder is not allowed `transmitTid`. * @@ -279,11 +279,11 @@ export function newBidder(spec: BidderSpec) { const tidGuard = guardTids(bidderRequest); const adUnitCodesHandled = {}; - function addBidWithCode(adUnitCode: string, bid: Bid) { + function addBidWithCode(adUnitCode: string, bid: Bid, responseMediaType = null) { const metrics = useMetrics(bid.metrics); metrics.checkpoint('addBidResponse'); adUnitCodesHandled[adUnitCode] = true; - if (metrics.measureTime('addBidResponse.validate', () => isValid(adUnitCode, bid))) { + if (metrics.measureTime('addBidResponse.validate', () => isValid(adUnitCode, bid, { responseMediaType }))) { addBidResponse(adUnitCode, bid); } else { addBidResponse.reject(adUnitCode, bid, REJECTION_REASON.INVALID) @@ -319,14 +319,6 @@ export function newBidder(spec: BidderSpec) { onTimelyResponse(spec.code); responses.push(resp) }, - onPaapi: (paapiConfig: any) => { - const bidRequest = bidRequestMap[paapiConfig.bidId]; - if (bidRequest) { - addPaapiConfig(bidRequest, paapiConfig); - } else { - logWarn('Received fledge auction configuration for an unknown bidId', paapiConfig); - } - }, // If the server responds with an error, there's not much we can do beside logging. onError: (errorMessage, error) => { if (!error.timedOut) { @@ -334,7 +326,7 @@ export function newBidder(spec: BidderSpec) { } adapterManager.callBidderError(spec.code, error, bidderRequest) events.emit(EVENTS.BIDDER_ERROR, { error, bidderRequest }); - logError(`Server call for ${spec.code} failed: ${errorMessage} ${error.status}. Continuing without bids.`, {bidRequests: validBidRequests}); + logError(`Server call for ${spec.code} failed: ${errorMessage} ${error.status}. Continuing without bids.`, { bidRequests: validBidRequests }); }, onBid: (bidResponse) => { const bidRequest = bidRequestMap[bidResponse.requestId]; @@ -353,7 +345,10 @@ export function newBidder(spec: BidderSpec) { bid.deferBilling = bidRequest.deferBilling; bid.deferRendering = bid.deferBilling && (bidResponse.deferRendering ?? typeof spec.onBidBillable !== 'function'); const prebidBid: Bid = Object.assign(createBid(bidRequest), bid, pick(bidRequest, Object.keys(TIDS))); - addBidWithCode(bidRequest.adUnitCode, prebidBid); + const responseMediaType = Object.prototype.hasOwnProperty.call(bidResponse, 'mediaType') + ? bidResponse.mediaType + : null; + addBidWithCode(bidRequest.adUnitCode, prebidBid, responseMediaType); } else { logWarn(`Bidder ${spec.code} made bid for unknown request ID: ${bidResponse.requestId}. Ignoring.`); addBidResponse.reject(null, bidResponse, REJECTION_REASON.INVALID_REQUEST_ID); @@ -390,7 +385,12 @@ export function newBidder(spec: BidderSpec) { } } -const RESPONSE_PROPS = ['bids', 'paapi'] +const RESPONSE_PROPS = [ + 'bids', + // allow bid adapters to still reply with paapi (which will be ignored). + 'paapi', +] + /** * Run a set of bid requests - that entails converting them to HTTP requests, sending * them over the network, and parsing the responses. @@ -407,7 +407,7 @@ export const processBidderRequests = hook('async', function, ajax: Ajax, wrapCallback: (fn: T) => Wraps, - {onRequest, onResponse, onPaapi, onError, onBid, onCompletion}: { + { onRequest, onResponse, onError, onBid, onCompletion }: { /** * invoked once for each HTTP request built by the adapter - with the raw request */ @@ -424,10 +424,6 @@ export const processBidderRequests = hook('async', function void; - /** - * invoked once with each member of the adapter response's 'paapi' array. - */ - onPaapi: (paapi: unknown) => void; /** * invoked once when all bid requests have been processed */ @@ -483,16 +479,12 @@ export const processBidderRequests = hook('async', function !RESPONSE_PROPS.includes(key))) { bids = response.bids; - paapiConfigs = response.paapi; } else { bids = response; } - if (isArray(paapiConfigs)) { - paapiConfigs.forEach(onPaapi); - } if (bids) { if (isArray(bids)) { bids.forEach(addBid); @@ -616,9 +608,6 @@ export const registerSyncInner = hook('async', function(spec: BidderSpec { -}, 'addPaapiConfig'); - declare module '../bidfactory' { interface BannerBidProperties { width?: number; @@ -629,7 +618,7 @@ declare module '../bidfactory' { } // check that the bid has a width and height set -function validBidSize(adUnitCode, bid: BannerBid, {index = auctionManager.index} = {}) { +function validBidSize(adUnitCode, bid: BannerBid, { index = auctionManager.index } = {}) { if ((bid.width || parseInt(bid.width, 10) === 0) && (bid.height || parseInt(bid.height, 10) === 0)) { bid.width = parseInt(bid.width, 10); bid.height = parseInt(bid.height, 10); @@ -651,7 +640,7 @@ function validBidSize(adUnitCode, bid: BannerBid, {index = auctionManager.index} // if a banner impression has one valid size, we assign that size to any bid // response that does not explicitly set width or height if (parsedSizes.length === 1) { - const [ width, height ] = parsedSizes[0].split('x'); + const [width, height] = parsedSizes[0].split('x'); bid.width = parseInt(width, 10); bid.height = parseInt(height, 10); return true; @@ -661,7 +650,7 @@ function validBidSize(adUnitCode, bid: BannerBid, {index = auctionManager.index} } // Validate the arguments sent to us by the adapter. If this returns false, the bid should be totally ignored. -export function isValid(adUnitCode: string, bid: Bid, {index = auctionManager.index} = {}) { +export function isValid(adUnitCode: string, bid: Bid, { index = auctionManager.index, responseMediaType = bid.mediaType } = {}) { function hasValidKeys() { const bidKeys = Object.keys(bid); return COMMON_BID_RESPONSE_KEYS.every(key => bidKeys.includes(key) && ![undefined, null].includes(bid[key])); @@ -686,15 +675,30 @@ export function isValid(adUnitCode: string, bid: Bid, {index = auctionManager.in return false; } - if (FEATURES.NATIVE && bid.mediaType === 'native' && !nativeBidIsValid(bid, {index})) { + const auctionOptions = config.getConfig('auctionOptions') || {}; + const rejectUnknownMediaTypes = auctionOptions.rejectUnknownMediaTypes === true; + const rejectInvalidMediaTypes = auctionOptions.rejectInvalidMediaTypes !== false; + const mediaTypes = index.getMediaTypes(bid); + if (mediaTypes && Object.keys(mediaTypes).length > 0) { + if (responseMediaType == null && rejectUnknownMediaTypes) { + logError(errorMessage(`Bid mediaType is required. Allowed: ${Object.keys(mediaTypes).join(', ')}`)); + return false; + } + if (responseMediaType != null && rejectInvalidMediaTypes && !mediaTypes.hasOwnProperty(responseMediaType)) { + logError(errorMessage(`Bid mediaType '${responseMediaType}' is not supported by the ad unit. Allowed: ${Object.keys(mediaTypes).join(', ')}`)); + return false; + } + } + + if (FEATURES.NATIVE && bid.mediaType === 'native' && !nativeBidIsValid(bid, { index })) { logError(errorMessage('Native bid missing some required properties.')); return false; } - if (FEATURES.VIDEO && bid.mediaType === 'video' && !isValidVideoBid(bid, {index})) { + if (FEATURES.VIDEO && bid.mediaType === 'video' && !isValidVideoBid(bid, { index })) { logError(errorMessage(`Video bid does not have required vastUrl or renderer property`)); return false; } - if (bid.mediaType === 'banner' && !validBidSize(adUnitCode, bid, {index})) { + if (bid.mediaType === 'banner' && !validBidSize(adUnitCode, bid, { index })) { logError(errorMessage(`Banner bids require a width and height`)); return false; } diff --git a/src/adloader.js b/src/adloader.js index 098b78c211b..cd3587a716c 100644 --- a/src/adloader.js +++ b/src/adloader.js @@ -5,45 +5,6 @@ import { isActivityAllowed } from './activities/rules.js'; import { insertElement, logError, logWarn, setScriptAttributes } from './utils.js'; const _requestCache = new WeakMap(); -// The below list contains modules or vendors whom Prebid allows to load external JS. -const _approvedLoadExternalJSList = [ - // Prebid maintained modules: - 'debugging', - 'outstream', - // RTD modules: - 'aaxBlockmeter', - 'adagio', - 'adloox', - 'arcspan', - 'airgrid', - 'browsi', - 'brandmetrics', - 'clean.io', - 'humansecurityMalvDefense', - 'humansecurity', - 'confiant', - 'contxtful', - 'hadron', - 'mediafilter', - 'medianet', - 'azerionedge', - 'a1Media', - 'geoedge', - 'qortex', - 'dynamicAdBoost', - '51Degrees', - 'symitridap', - 'wurfl', - 'nodalsAi', - 'anonymised', - 'optable', - 'oftmedia', - // UserId Submodules - 'justtag', - 'tncId', - 'ftrackId', - 'id5', -]; /** * Loads external javascript. Can only be used if external JS is approved by Prebid. See https://github.com/prebid/prebid-js-external-js-template#policy @@ -64,20 +25,31 @@ export function loadExternalScript(url, moduleType, moduleCode, callback, doc, a logError('cannot load external script without url and moduleCode'); return; } - if (!_approvedLoadExternalJSList.includes(moduleCode)) { - logError(`${moduleCode} not whitelisted for loading external JavaScript`); - return; + + const hasCallback = typeof callback === 'function' || typeof callback?.success === 'function' || typeof callback?.error === 'function'; + + function runCallback(cb, err) { + if (err == null) { + if (typeof cb === 'function') { + cb() + } else { + cb.success?.(); + } + } else { + cb.error?.(err); + } } + if (!doc) { doc = document; // provide a "valid" key for the WeakMap } // only load each asset once const storedCachedObject = getCacheObject(doc, url); if (storedCachedObject) { - if (callback && typeof callback === 'function') { + if (hasCallback) { if (storedCachedObject.loaded) { // invokeCallbacks immediately - callback(); + runCallback(callback, storedCachedObject.error); } else { // queue the callback storedCachedObject.callbacks.push(callback); @@ -87,6 +59,7 @@ export function loadExternalScript(url, moduleType, moduleCode, callback, doc, a } const cachedDocObj = _requestCache.get(doc) || {}; const cacheObject = { + error: null, loaded: false, tag: null, callbacks: [] @@ -94,7 +67,7 @@ export function loadExternalScript(url, moduleType, moduleCode, callback, doc, a cachedDocObj[url] = cacheObject; _requestCache.set(doc, cachedDocObj); - if (callback && typeof callback === 'function') { + if (hasCallback) { cacheObject.callbacks.push(callback); } @@ -103,7 +76,7 @@ export function loadExternalScript(url, moduleType, moduleCode, callback, doc, a cacheObject.loaded = true; try { for (let i = 0; i < cacheObject.callbacks.length; i++) { - cacheObject.callbacks[i](); + runCallback(cacheObject.callbacks[i], cacheObject.error); } } catch (e) { logError('Error executing callback', 'adloader.js:loadExternalScript', e); @@ -123,16 +96,27 @@ export function loadExternalScript(url, moduleType, moduleCode, callback, doc, a cacheObject.tag = jptScript; } + function errorListener(e) { + cacheObject.error = e; + exit(); + } + jptScript.addEventListener('error', errorListener) + + function exit() { + jptScript.removeEventListener('error', errorListener); + callback(); + } + if (jptScript.readyState) { jptScript.onreadystatechange = function () { if (jptScript.readyState === 'loaded' || jptScript.readyState === 'complete') { jptScript.onreadystatechange = null; - callback(); + exit(); } }; } else { jptScript.onload = function () { - callback(); + exit(); }; } diff --git a/src/adserver.js b/src/adserver.js index db7aaaa1dc8..cd44b725e2f 100644 --- a/src/adserver.js +++ b/src/adserver.js @@ -1,4 +1,4 @@ -import {hook} from './hook.js'; +import { hook } from './hook.js'; /** * return the GAM PPID, if available (eid for the userID configured with `userSync.ppidSource`) diff --git a/src/ajax.ts b/src/ajax.ts index 58ed89b6e83..a1eda919d42 100644 --- a/src/ajax.ts +++ b/src/ajax.ts @@ -1,9 +1,9 @@ import { ACTIVITY_ACCESS_REQUEST_CREDENTIALS } from './activities/activities.js'; import { activityParams } from './activities/activityParams.js'; import { isActivityAllowed } from './activities/rules.js'; -import {config} from './config.js'; +import { config } from './config.js'; import { hook } from './hook.js'; -import {buildUrl, hasDeviceAccess, logError, parseUrl} from './utils.js'; +import { buildUrl, hasDeviceAccess, logError, parseUrl } from './utils.js'; export const dep = { fetch: window.fetch.bind(window), @@ -53,10 +53,6 @@ export interface AjaxOptions { * Whether chrome's `Sec-Browing-Topics` header should be sent */ browsingTopics?: boolean - /** - * Whether chrome's PAAPI headers should be sent. - */ - adAuctionHeaders?: boolean; /** * If true, suppress warnings */ @@ -94,8 +90,8 @@ export function toFetchRequest(url, data, options: AjaxOptions = {}) { rqOpts.credentials = 'include'; } if (isSecureContext) { - ['browsingTopics', 'adAuctionHeaders'].forEach(opt => { - // the Request constructor will throw an exception if the browser supports topics/fledge + ['browsingTopics'].forEach(opt => { + // the Request constructor will throw an exception if the browser supports topics // but we're not in a secure context if (options[opt]) { rqOpts[opt] = true; @@ -118,12 +114,12 @@ export function toFetchRequest(url, data, options: AjaxOptions = {}) { * `request` is invoked at the beginning of each request, and `done` at the end; both are passed its origin. * */ -export function fetcherFactory(timeout = 3000, {request, done}: any = {}, moduleType?: string, moduleName?: string): typeof window['fetch'] { +export function fetcherFactory(timeout = 3000, { request, done }: any = {}, moduleType?: string, moduleName?: string): typeof window['fetch'] { let fetcher = (resource, options) => { let to; if (timeout != null && options?.signal == null && !config.getConfig('disableAjaxTimeout')) { to = dep.timeout(timeout, resource); - options = Object.assign({signal: to.signal}, options); + options = Object.assign({ signal: to.signal }, options); } processRequestOptions(options, moduleType, moduleName); @@ -147,7 +143,7 @@ export function fetcherFactory(timeout = 3000, {request, done}: any = {}, module export type XHR = ReturnType; -function toXHR({status, statusText = '', headers, url}: { +function toXHR({ status, statusText = '', headers, url }: { status: number; statusText?: string; headers?: Response['headers']; @@ -179,7 +175,7 @@ function toXHR({status, statusText = '', headers, url}: { }, getResponseHeader: (header) => headers?.has(header) ? headers.get(header) : null, toJSON() { - return Object.assign({responseXML: getXML()}, this) + return Object.assign({ responseXML: getXML() }, this) }, timedOut: false } @@ -189,7 +185,7 @@ function toXHR({status, statusText = '', headers, url}: { * attach legacy `ajax` callbacks to a fetch promise. */ export function attachCallbacks(fetchPm: Promise, callback: AjaxCallback) { - const {success, error} = typeof callback === 'object' && callback != null ? callback : { + const { success, error } = typeof callback === 'object' && callback != null ? callback : { success: typeof callback === 'function' ? callback : () => null, error: (e, x) => logError('Network error', e, x) }; @@ -200,8 +196,8 @@ export function attachCallbacks(fetchPm: Promise, callback: AjaxCallba const xhr = toXHR(response, responseText); response.ok || response.status === 304 ? success(responseText, xhr) : error(response.statusText, xhr); }, (reason) => error('', Object.assign( - toXHR({status: 0}, ''), - {reason, timedOut: reason?.name === 'AbortError'})) + toXHR({ status: 0 }, ''), + { reason, timedOut: reason?.name === 'AbortError' })) ); } @@ -209,8 +205,8 @@ export type AjaxSuccessCallback = (responseText: string, xhr: XHR) => void; export type AjaxErrorCallback = (statusText: string, xhr: XHR) => void; export type AjaxCallback = AjaxSuccessCallback | { success?: AjaxErrorCallback; error?: AjaxSuccessCallback }; -export function ajaxBuilder(timeout = 3000, {request, done} = {} as any, moduleType?: string, moduleName?: string) { - const fetcher = fetcherFactory(timeout, {request, done}, moduleType, moduleName); +export function ajaxBuilder(timeout = 3000, { request, done } = {} as any, moduleType?: string, moduleName?: string) { + const fetcher = fetcherFactory(timeout, { request, done }, moduleType, moduleName); return function (url: string, callback?: AjaxCallback, data?: unknown, options: AjaxOptions = {}) { attachCallbacks(fetcher(toFetchRequest(url, data, options)), callback); }; diff --git a/src/auction.ts b/src/auction.ts index 7acd7b8cec9..6a7a9643938 100644 --- a/src/auction.ts +++ b/src/auction.ts @@ -10,32 +10,35 @@ import { parseUrl, timestamp } from './utils.js'; -import {getPriceBucketString} from './cpmBucketManager.js'; -import {isNativeResponse, setNativeResponseProperties} from './native.js'; -import {batchAndStore, storeLocally} from './videoCache.js'; -import {Renderer} from './Renderer.js'; -import {config} from './config.js'; -import {userSync} from './userSync.js'; -import {hook, ignoreCallbackArg} from './hook.js'; -import {OUTSTREAM} from './video.js'; -import {AUDIO, VIDEO} from './mediaTypes.js'; -import {auctionManager} from './auctionManager.js'; -import {bidderSettings} from './bidderSettings.js'; +import { getPriceBucketString } from './cpmBucketManager.js'; +import { isNativeResponse, setNativeResponseProperties } from './native.js'; +import { batchAndStore, storeLocally } from './videoCache.js'; +import { Renderer } from './Renderer.js'; +import { config } from './config.js'; +import { userSync } from './userSync.js'; +import { hook, ignoreCallbackArg } from './hook.js'; +import { OUTSTREAM } from './video.js'; +import { AUDIO, VIDEO } from './mediaTypes.js'; +import { auctionManager } from './auctionManager.js'; +import { bidderSettings } from './bidderSettings.js'; import * as events from './events.js'; -import adapterManager, {type BidderRequest, type BidRequest} from './adapterManager.js'; -import {EVENTS, GRANULARITY_OPTIONS, JSON_MAPPING, REJECTION_REASON, S2S, TARGETING_KEYS} from './constants.js'; -import {defer, PbPromise} from './utils/promise.js'; -import {type Metrics, useMetrics} from './utils/perfMetrics.js'; -import {adjustCpm} from './utils/cpm.js'; -import {getGlobal} from './prebidGlobal.js'; -import {ttlCollection} from './utils/ttlCollection.js'; -import {getMinBidCacheTTL, onMinBidCacheTTLChange} from './bidTTL.js'; -import type {Bid, BidResponse} from "./bidfactory.ts"; -import type {AdUnitCode, BidderCode, Identifier, ORTBFragments} from './types/common.d.ts'; -import type {TargetingMap} from "./targeting.ts"; -import type {AdUnit} from "./adUnits.ts"; -import type {MediaType} from "./mediaTypes.ts"; -import type {VideoContext} from "./video.ts"; +import adapterManager, { activityParams, type BidderRequest, type BidRequest } from './adapterManager.js'; +import { EVENTS, GRANULARITY_OPTIONS, JSON_MAPPING, REJECTION_REASON, S2S, TARGETING_KEYS } from './constants.js'; +import { defer, PbPromise } from './utils/promise.js'; +import { type Metrics, useMetrics } from './utils/perfMetrics.js'; +import { adjustCpm } from './utils/cpm.js'; +import { getGlobal } from './prebidGlobal.js'; +import { ttlCollection } from './utils/ttlCollection.js'; +import { getMinBidCacheTTL, onMinBidCacheTTLChange } from './bidTTL.js'; +import type { Bid, BidResponse } from "./bidfactory.ts"; +import type { AdUnitCode, BidderCode, Identifier, ORTBFragments } from './types/common.d.ts'; +import type { TargetingMap } from "./targeting.ts"; +import type { AdUnit, AdUnitDefinition } from "./adUnits.ts"; +import type { MediaType } from "./mediaTypes.ts"; +import type { VideoContext } from "./video.ts"; +import { isActivityAllowed } from './activities/rules.js'; +import { ACTIVITY_ADD_BID_RESPONSE } from './activities/activities.js'; +import { MODULE_TYPE_BIDDER } from './activities/modules.ts'; const { syncUsers } = userSync; @@ -97,10 +100,6 @@ declare module './events' { * Fired when an auction times out. */ [EVENTS.BID_TIMEOUT]: [BidRequest[]]; - /** - * Fired when a bid is received. - */ - [EVENTS.BID_ACCEPTED]: [Partial]; /** * Fired when a bid is rejected. */ @@ -135,6 +134,27 @@ export interface AuctionOptionsConfig { * When true, prevent bids from being rendered if TTL is reached. Default is false. */ suppressExpiredRender?: boolean; + + /** + * If true, use legacy rendering logic. + * + * Since Prebid 10.12, `pbjs.renderAd` wraps creatives in an additional iframe. This can cause problems for some creatives + * that try to reach the top window and do not expect to find the extra iframe. You may set `legacyRender: true` to revert + * to pre-10.12 rendering logic. + */ + legacyRender?: boolean; + + /** + * When true, reject bids without a response `mediaType` when the ad unit has an explicit mediaTypes list. + * Default is false to preserve legacy behavior for responses that omit mediaType. + */ + rejectUnknownMediaTypes?: boolean; + + /** + * When true, reject bids with a response `mediaType` that does not match the ad unit's explicit mediaTypes list. + * Default is true; set to false to keep mismatched mediaType responses. + */ + rejectInvalidMediaTypes?: boolean; } export interface PriceBucketConfig { @@ -156,13 +176,13 @@ declare module './config' { auctionOptions?: AuctionOptionsConfig; priceGranularity?: (typeof GRANULARITY_OPTIONS)[keyof typeof GRANULARITY_OPTIONS]; customPriceBucket?: PriceBucketConfig; - mediaTypePriceGranularity?: {[K in MediaType]?: PriceBucketConfig} & {[K in VideoContext as `${typeof VIDEO}-${K}`]?: PriceBucketConfig}; + mediaTypePriceGranularity?: { [K in MediaType]?: PriceBucketConfig } & { [K in VideoContext as `${typeof VIDEO}-${K}`]?: PriceBucketConfig }; } } export const beforeInitAuction = hook('sync', (auction) => {}) -export function newAuction({adUnits, adUnitCodes, callback, cbTimeout, labels, auctionId, ortb2Fragments, metrics}: AuctionOptions) { +export function newAuction({ adUnits, adUnitCodes, callback, cbTimeout, labels, auctionId, ortb2Fragments, metrics }: AuctionOptions) { metrics = useMetrics(metrics); const _adUnits = adUnits; const _labels = labels; @@ -243,7 +263,7 @@ export function newAuction({adUnits, adUnitCodes, callback, cbTimeout, labels, a done.resolve(); events.emit(EVENTS.AUCTION_END, getProperties()); - bidsBackCallback(_adUnits, function () { + bidsBackCallback(_adUnits, auctionId, function () { try { if (_callback != null) { const bids = _bidsReceived.toArray() @@ -416,8 +436,8 @@ export function newAuction({adUnits, adUnitCodes, callback, cbTimeout, labels, a adapterManager.callSetTargetingBidder(bid.adapterCode || bid.bidder, bid); } - events.on(EVENTS.SEAT_NON_BID, (event) => { - if (event.auctionId === _auctionId) { + events.on(EVENTS.PBS_ANALYTICS, (event) => { + if (event.auctionId === _auctionId && event.seatnonbid != null) { addNonBids(event.seatnonbid) } }); @@ -460,7 +480,13 @@ declare module './hook' { */ export const addBidResponse = ignoreCallbackArg(hook('async', function(adUnitCode: string, bid: Partial, reject: (reason: (typeof REJECTION_REASON)[keyof typeof REJECTION_REASON]) => void): void { if (!isValidPrice(bid)) { - reject(REJECTION_REASON.PRICE_TOO_HIGH) + reject(REJECTION_REASON.PRICE_TOO_HIGH); + } else if (!isActivityAllowed(ACTIVITY_ADD_BID_RESPONSE, activityParams(MODULE_TYPE_BIDDER, bid.bidder || bid.bidderCode, { + bid, + ortb2: auctionManager.index.getOrtb2(bid), + adUnit: auctionManager.index.getAdUnit(bid), + }))) { + reject(REJECTION_REASON.BIDDER_DISALLOWED); } else { this.dispatch.call(null, adUnitCode, bid); } @@ -478,7 +504,7 @@ export const addBidderRequests = hook('sync', function(bidderRequests) { this.dispatch.call(this.context, bidderRequests); }, 'addBidderRequests'); -export const bidsBackCallback = hook('async', function (adUnits, callback) { +export const bidsBackCallback = hook('async', function (adUnits, auctionId, callback) { if (callback) { callback(); } @@ -489,7 +515,7 @@ export type AddBidResponse = { reject(adUnitCode: AdUnitCode, bid: BidResponse, reason: typeof REJECTION_REASON[keyof typeof REJECTION_REASON]) : void; } -export function auctionCallbacks(auctionDone, auctionInstance, {index = auctionManager.index} = {}) { +export function auctionCallbacks(auctionDone, auctionInstance, { index = auctionManager.index } = {}) { let outstandingBidsAdded = 0; let allAdapterCalledDone = false; const bidderRequestsDone = new Set(); @@ -512,7 +538,6 @@ export function auctionCallbacks(auctionDone, auctionInstance, {index = auctionM function acceptBidResponse(adUnitCode: string, bid: Partial) { handleBidResponse(adUnitCode, bid, (done) => { const bidResponse = getPreparedBidForAuction(bid); - events.emit(EVENTS.BID_ACCEPTED, bidResponse); if ((FEATURES.VIDEO && bidResponse.mediaType === VIDEO) || (FEATURES.AUDIO && bidResponse.mediaType === AUDIO)) { tryAddVideoAudioBid(auctionInstance, bidResponse, done); } else { @@ -599,7 +624,7 @@ export function addBidToAuction(auctionInstance, bidResponse: Bid) { } // Video bids may fail if the cache is down, or there's trouble on the network. -function tryAddVideoAudioBid(auctionInstance, bidResponse, afterBidAdded, {index = auctionManager.index} = {}) { +function tryAddVideoAudioBid(auctionInstance, bidResponse, afterBidAdded, { index = auctionManager.index } = {}) { let addBid = true; const videoMediaType = index.getMediaTypes({ @@ -648,6 +673,7 @@ declare module './bidfactory' { } interface BaseBid { + element?: AdUnitDefinition['element']; /** * true if this bid is for an interstitial slot. */ @@ -715,7 +741,7 @@ declare module './bidfactory' { /** * Add timing properties to a bid response */ -function addBidTimingProperties(bidResponse: Partial, {index = auctionManager.index} = {}) { +function addBidTimingProperties(bidResponse: Partial, { index = auctionManager.index } = {}) { const bidderRequest = index.getBidderRequest(bidResponse); const start = (bidderRequest && bidderRequest.start) || bidResponse.requestTimestamp; @@ -729,10 +755,10 @@ function addBidTimingProperties(bidResponse: Partial, {index = auctionManag /** * Augment `bidResponse` with properties that are common across all bids - including rejected bids. */ -function addCommonResponseProperties(bidResponse: Partial, adUnitCode: string, {index = auctionManager.index} = {}) { +function addCommonResponseProperties(bidResponse: Partial, adUnitCode: string, { index = auctionManager.index } = {}) { const adUnit = index.getAdUnit(bidResponse); - addBidTimingProperties(bidResponse, {index}) + addBidTimingProperties(bidResponse, { index }) Object.assign(bidResponse, { cpm: parseFloat(bidResponse.cpm) || 0, @@ -748,7 +774,7 @@ function addCommonResponseProperties(bidResponse: Partial, adUnitCode: stri /** * Add additional bid response properties that are universal for all _accepted_ bids. */ -function getPreparedBidForAuction(bid: Partial, {index = auctionManager.index} = {}): Bid { +function getPreparedBidForAuction(bid: Partial, { index = auctionManager.index } = {}): Bid { // Let listeners know that now is the time to adjust the bid, if they want to. // // CAREFUL: Publishers rely on certain bid properties to be available (like cpm), @@ -757,6 +783,7 @@ function getPreparedBidForAuction(bid: Partial, {index = auctionManager.ind const adUnit = index.getAdUnit(bid); bid.instl = adUnit?.ortb2Imp?.instl === 1; + bid.element = adUnit?.element; // a publisher-defined renderer can be used to render bids const bidRenderer = index.getBidRequest(bid)?.renderer || adUnit.renderer; @@ -830,7 +857,7 @@ export function getMediaTypeGranularity(mediaType, mediaTypes, mediaTypePriceGra * @param {object} obj.index * @returns {string} granularity */ -export const getPriceGranularity = (bid, {index = auctionManager.index} = {}) => { +export const getPriceGranularity = (bid, { index = auctionManager.index } = {}) => { // Use the config value 'mediaTypeGranularity' if it has been set for mediaType, else use 'priceGranularity' const mediaTypeGranularity = getMediaTypeGranularity(bid.mediaType, index.getMediaTypes(bid), config.getConfig('mediaTypePriceGranularity')); const granularity = (typeof bid.mediaType === 'string' && mediaTypeGranularity) ? ((typeof mediaTypeGranularity === 'string') ? mediaTypeGranularity : 'custom') : config.getConfig('priceGranularity'); @@ -1035,7 +1062,7 @@ export function getStandardBidderSettings(mediaType, bidderCode) { return standardSettings; } -export function getKeyValueTargetingPairs(bidderCode, custBidObj: Bid, {index = auctionManager.index} = {}) { +export function getKeyValueTargetingPairs(bidderCode, custBidObj: Bid, { index = auctionManager.index } = {}) { if (!custBidObj) { return {}; } diff --git a/src/auctionIndex.js b/src/auctionIndex.js index 0bf0fb88943..58243aa4f49 100644 --- a/src/auctionIndex.js +++ b/src/auctionIndex.js @@ -11,6 +11,7 @@ * Bid responses are not guaranteed to have a corresponding request. * @property {function({ requestId?: string }): *} getBidRequest Returns bidRequest object for requestId. * Bid responses are not guaranteed to have a corresponding request. + * @property {function} getOrtb2 Returns ortb2 object for bid */ /** @@ -19,33 +20,33 @@ */ export function AuctionIndex(getAuctions) { Object.assign(this, { - getAuction({auctionId}) { + getAuction({ auctionId }) { if (auctionId != null) { return getAuctions() .find(auction => auction.getAuctionId() === auctionId); } }, - getAdUnit({adUnitId}) { + getAdUnit({ adUnitId }) { if (adUnitId != null) { return getAuctions() .flatMap(a => a.getAdUnits()) .find(au => au.adUnitId === adUnitId); } }, - getMediaTypes({adUnitId, requestId}) { + getMediaTypes({ adUnitId, requestId }) { if (requestId != null) { - const req = this.getBidRequest({requestId}); + const req = this.getBidRequest({ requestId }); if (req != null && (adUnitId == null || req.adUnitId === adUnitId)) { return req.mediaTypes; } } else if (adUnitId != null) { - const au = this.getAdUnit({adUnitId}); + const au = this.getAdUnit({ adUnitId }); if (au != null) { return au.mediaTypes; } } }, - getBidderRequest({requestId, bidderRequestId}) { + getBidderRequest({ requestId, bidderRequestId }) { if (requestId != null || bidderRequestId != null) { let bers = getAuctions().flatMap(a => a.getBidRequests()); if (bidderRequestId != null) { @@ -58,7 +59,7 @@ export function AuctionIndex(getAuctions) { } } }, - getBidRequest({requestId}) { + getBidRequest({ requestId }) { if (requestId != null) { return getAuctions() .flatMap(a => a.getBidRequests()) diff --git a/src/auctionManager.js b/src/auctionManager.js index 0e0d8af8f9c..40395d95f28 100644 --- a/src/auctionManager.js +++ b/src/auctionManager.js @@ -27,11 +27,11 @@ import { uniques, logWarn } from './utils.js'; import { newAuction, getStandardBidderSettings, AUCTION_COMPLETED } from './auction.js'; -import {AuctionIndex} from './auctionIndex.js'; +import { AuctionIndex } from './auctionIndex.js'; import { BID_STATUS, JSON_MAPPING } from './constants.js'; -import {useMetrics} from './utils/perfMetrics.js'; -import {ttlCollection} from './utils/ttlCollection.js'; -import {getMinBidCacheTTL, onMinBidCacheTTLChange} from './bidTTL.js'; +import { useMetrics } from './utils/perfMetrics.js'; +import { ttlCollection } from './utils/ttlCollection.js'; +import { getMinBidCacheTTL, onMinBidCacheTTLChange } from './bidTTL.js'; /** * Creates new instance of auctionManager. There will only be one instance of auctionManager but @@ -89,7 +89,7 @@ export function newAuctionManager() { getAdUnitCodes: { post: uniques, } - }).forEach(([mgrMethod, {name = mgrMethod, pre, post}]) => { + }).forEach(([mgrMethod, { name = mgrMethod, pre, post }]) => { const mapper = pre == null ? (auction) => auction[name]() : (auction) => pre(auction) ? auction[name]() : []; diff --git a/src/audio.ts b/src/audio.ts index 998531f1bf9..a2f8af10c52 100644 --- a/src/audio.ts +++ b/src/audio.ts @@ -1,34 +1,34 @@ -import {isArrayOfNums, isInteger, logError} from './utils.js'; -import {config} from './config.js'; -import {hook} from './hook.js'; -import {auctionManager} from './auctionManager.js'; -import type {AudioBid} from "./bidfactory.ts"; -import {type BaseMediaType} from "./mediaTypes.ts"; -import type {ORTBImp} from "./types/ortb/request"; -import type {AdUnitDefinition} from "./adUnits.ts"; -import {getGlobalVarName} from "./buildOptions.ts"; +import { isArrayOfNums, isInteger, logError } from './utils.js'; +import { config } from './config.js'; +import { hook } from './hook.js'; +import { auctionManager } from './auctionManager.js'; +import type { AudioBid } from "./bidfactory.ts"; +import { type BaseMediaType } from "./mediaTypes.ts"; +import type { ORTBImp } from "./types/ortb/request"; +import type { AdUnitDefinition } from "./adUnits.ts"; +import { getGlobalVarName } from "./buildOptions.ts"; export const OUTSTREAM = 'outstream'; export const INSTREAM = 'instream'; const ORTB_PARAMS = [ - [ 'mimes', value => Array.isArray(value) && value.length > 0 && value.every(v => typeof v === 'string') ], - [ 'minduration', isInteger ], - [ 'maxduration', isInteger ], - [ 'startdelay', isInteger ], - [ 'maxseq', isInteger ], - [ 'poddur', isInteger ], - [ 'protocols', isArrayOfNums ], - [ 'battr', isArrayOfNums ], - [ 'maxextended', isInteger ], - [ 'minbitrate', isInteger ], - [ 'maxbitrate', isInteger ], - [ 'delivery', isArrayOfNums ], - [ 'api', isArrayOfNums ], - [ 'companiontype', isArrayOfNums ], - [ 'feed', isInteger ], - [ 'stitched', isInteger ], - [ 'nvol', isInteger ], + ['mimes', value => Array.isArray(value) && value.length > 0 && value.every(v => typeof v === 'string')], + ['minduration', isInteger], + ['maxduration', isInteger], + ['startdelay', isInteger], + ['maxseq', isInteger], + ['poddur', isInteger], + ['protocols', isArrayOfNums], + ['battr', isArrayOfNums], + ['maxextended', isInteger], + ['minbitrate', isInteger], + ['maxbitrate', isInteger], + ['delivery', isArrayOfNums], + ['api', isArrayOfNums], + ['companiontype', isArrayOfNums], + ['feed', isInteger], + ['stitched', isInteger], + ['nvol', isInteger], ] as const; /** @@ -49,7 +49,7 @@ export function fillAudioDefaults(adUnit: AdUnitDefinition) {} /** * Validate that the assets required for audio context are present on the bid */ -export function isValidAudioBid(bid: AudioBid, {index = auctionManager.index} = {}): boolean { +export function isValidAudioBid(bid: AudioBid, { index = auctionManager.index } = {}): boolean { const audioMediaType = index.getMediaTypes(bid)?.audio; const context = audioMediaType && audioMediaType?.context; const useCacheKey = audioMediaType && audioMediaType?.useCacheKey; diff --git a/src/banner.ts b/src/banner.ts index 5b9520348aa..0f69254028b 100644 --- a/src/banner.ts +++ b/src/banner.ts @@ -1,21 +1,21 @@ import { isArrayOfNums, isInteger, isStr } from './utils.js'; -import type {Size} from "./types/common.d.ts"; -import type {ORTBImp} from "./types/ortb/request.d.ts"; -import type {BaseMediaType} from "./mediaTypes.ts"; +import type { Size } from "./types/common.d.ts"; +import type { ORTBImp } from "./types/ortb/request.d.ts"; +import type { BaseMediaType } from "./mediaTypes.ts"; const ORTB_PARAMS = [ - [ 'format', value => Array.isArray(value) && value.length > 0 && value.every(v => typeof v === 'object') ], - [ 'w', isInteger ], - [ 'h', isInteger ], - [ 'btype', isArrayOfNums ], - [ 'battr', isArrayOfNums ], - [ 'pos', isInteger ], - [ 'mimes', value => Array.isArray(value) && value.length > 0 && value.every(v => typeof v === 'string') ], - [ 'topframe', value => [1, 0].includes(value) ], - [ 'expdir', isArrayOfNums ], - [ 'api', isArrayOfNums ], - [ 'id', isStr ], - [ 'vcm', value => [1, 0].includes(value) ] + ['format', value => Array.isArray(value) && value.length > 0 && value.every(v => typeof v === 'object')], + ['w', isInteger], + ['h', isInteger], + ['btype', isArrayOfNums], + ['battr', isArrayOfNums], + ['pos', isInteger], + ['mimes', value => Array.isArray(value) && value.length > 0 && value.every(v => typeof v === 'string')], + ['topframe', value => [1, 0].includes(value)], + ['expdir', isArrayOfNums], + ['api', isArrayOfNums], + ['id', isStr], + ['vcm', value => [1, 0].includes(value)] ] as const; /** diff --git a/src/bidTTL.ts b/src/bidTTL.ts index c6e5db37eb8..2af2986311c 100644 --- a/src/bidTTL.ts +++ b/src/bidTTL.ts @@ -1,5 +1,5 @@ -import {config} from './config.js'; -import {logError} from './utils.js'; +import { config } from './config.js'; +import { logError } from './utils.js'; const CACHE_TTL_SETTING = 'minBidCacheTTL'; let TTL_BUFFER = 1; let minCacheTTL = null; diff --git a/src/bidderSettings.ts b/src/bidderSettings.ts index 0da7be81b0c..c9d68cdddf7 100644 --- a/src/bidderSettings.ts +++ b/src/bidderSettings.ts @@ -1,10 +1,10 @@ -import {deepAccess, mergeDeep} from './utils.js'; -import {getGlobal} from './prebidGlobal.js'; +import { deepAccess, mergeDeep } from './utils.js'; +import { getGlobal } from './prebidGlobal.js'; import { JSON_MAPPING } from './constants.js'; -import type {BidderCode} from "./types/common"; -import type {BidRequest} from "./adapterManager.ts"; -import type {Bid} from "./bidfactory.ts"; -import type {StorageType} from "./storageManager.ts"; +import type { BidderCode } from "./types/common"; +import type { BidRequest } from "./adapterManager.ts"; +import type { Bid } from "./bidfactory.ts"; +import type { StorageType } from "./storageManager.ts"; // eslint-disable-next-line @typescript-eslint/no-unused-vars export interface BidderSettings { diff --git a/src/bidfactory.ts b/src/bidfactory.ts index b87d3b14a26..576e97eb2a8 100644 --- a/src/bidfactory.ts +++ b/src/bidfactory.ts @@ -1,12 +1,12 @@ -import {getUniqueIdentifierStr} from './utils.js'; -import type {BidderCode, BidSource, ContextIdentifiers, Currency, Identifier} from "./types/common.d.ts"; -import {MediaType} from "./mediaTypes.ts"; -import type {DSAResponse} from "./types/ortb/ext/dsa.d.ts"; -import type {EventTrackerResponse} from "./types/ortb/native.d.ts"; -import {Metrics} from "./utils/perfMetrics.ts"; -import {Renderer} from './Renderer.js'; -import {type BID_STATUS} from "./constants.ts"; -import type {DemandChain} from "./types/ortb/ext/dchain.d.ts"; +import { getUniqueIdentifierStr } from './utils.js'; +import type { BidderCode, BidSource, ContextIdentifiers, Currency, Identifier } from "./types/common.d.ts"; +import { MediaType } from "./mediaTypes.ts"; +import type { DSAResponse } from "./types/ortb/ext/dsa.d.ts"; +import type { EventTrackerResponse } from "./types/ortb/native.d.ts"; +import { Metrics } from "./utils/perfMetrics.ts"; +import { Renderer } from './Renderer.js'; +import { type BID_STATUS } from "./constants.ts"; +import type { DemandChain } from "./types/ortb/ext/dchain.d.ts"; type BidIdentifiers = ContextIdentifiers & { src: BidSource; @@ -135,7 +135,6 @@ export interface BaseBid extends ContextIdentifiers, Required = {[K in keyof T]?: undefined}; +type NullProps = { [K in keyof T]?: undefined }; type NullBid = NullProps<_BannerBid> & NullProps<_VideoBid> & NullProps<_NativeBid>; type ExtendBid = B & Omit; @@ -194,7 +193,7 @@ export type AudioBid = VideoBid; export type Bid = BannerBid | VideoBid | NativeBid | AudioBid; // eslint-disable-next-line @typescript-eslint/no-redeclare -function Bid({src = 'client', bidder = '', bidId, transactionId, adUnitId, auctionId}: Partial = {}) { +function Bid({ src = 'client', bidder = '', bidId, transactionId, adUnitId, auctionId }: Partial = {}) { var _bidSrc = src; Object.assign(this, { diff --git a/src/config.ts b/src/config.ts index 00e87a9c62d..edb30c14c12 100644 --- a/src/config.ts +++ b/src/config.ts @@ -2,7 +2,7 @@ * Module for getting and setting Prebid configuration. */ -import {isValidPriceConfig} from './cpmBucketManager.js'; +import { isValidPriceConfig } from './cpmBucketManager.js'; import { deepAccess, deepClone, @@ -16,11 +16,12 @@ import { logWarn, mergeDeep } from './utils.js'; -import {DEBUG_MODE} from './constants.js'; -import type {UserSyncConfig} from "./userSync.ts"; -import type {DeepPartial, DeepProperty, DeepPropertyName, TypeOfDeepProperty} from "./types/objects.d.ts"; -import type {BidderCode} from "./types/common.d.ts"; -import type {ORTBRequest} from "./types/ortb/request.d.ts"; +import { DEBUG_MODE } from './constants.js'; +import type { UserSyncConfig } from "./userSync.ts"; +import type { DeepPartial, DeepProperty, DeepPropertyName, TypeOfDeepProperty } from "./types/objects.d.ts"; +import type { BidderCode } from "./types/common.d.ts"; +import type { ORTBRequest } from "./types/ortb/request.d.ts"; +import { Bid } from './bidfactory.ts'; const DEFAULT_DEBUG = getParameterByName(DEBUG_MODE).toUpperCase() === 'TRUE'; const DEFAULT_BIDDER_TIMEOUT = 3000; @@ -62,13 +63,47 @@ function attachProperties(config, useDefaultValues = true) { auctionOptions: {} } : {} + const validateauctionOptions = (() => { + const boolKeys = ['suppressStaleRender', 'suppressExpiredRender', 'legacyRender', 'rejectUnknownMediaTypes', 'rejectInvalidMediaTypes']; + const arrKeys = ['secondaryBidders'] + const allKeys = [].concat(boolKeys).concat(arrKeys); + + return function validateauctionOptions(val) { + if (!isPlainObject(val)) { + logWarn('Auction Options must be an object') + return false + } + + for (const k of Object.keys(val)) { + if (!allKeys.includes(k)) { + logWarn(`Auction Options given an incorrect param: ${k}`) + return false + } + if (arrKeys.includes(k)) { + if (!isArray(val[k])) { + logWarn(`Auction Options ${k} must be of type Array`); + return false + } else if (!val[k].every(isStr)) { + logWarn(`Auction Options ${k} must be only string`); + return false + } + } else if (boolKeys.includes(k)) { + if (!isBoolean(val[k])) { + logWarn(`Auction Options ${k} must be of type boolean`); + return false; + } + } + } + return true; + } + })(); function getProp(name) { return values[name]; } function setProp(name, val) { if (!values.hasOwnProperty(name)) { - Object.defineProperty(config, name, {enumerable: true}); + Object.defineProperty(config, name, { enumerable: true }); } values[name] = val; } @@ -164,35 +199,6 @@ function attachProperties(config, useDefaultValues = true) { } return true; } - - function validateauctionOptions(val) { - if (!isPlainObject(val)) { - logWarn('Auction Options must be an object') - return false - } - - for (const k of Object.keys(val)) { - if (k !== 'secondaryBidders' && k !== 'suppressStaleRender' && k !== 'suppressExpiredRender') { - logWarn(`Auction Options given an incorrect param: ${k}`) - return false - } - if (k === 'secondaryBidders') { - if (!isArray(val[k])) { - logWarn(`Auction Options ${k} must be of type Array`); - return false - } else if (!val[k].every(isStr)) { - logWarn(`Auction Options ${k} must be only string`); - return false - } - } else if (k === 'suppressStaleRender' || k === 'suppressExpiredRender') { - if (!isBoolean(val[k])) { - logWarn(`Auction Options ${k} must be of type boolean`); - return false; - } - } - } - return true; - } } export interface Config { @@ -249,6 +255,17 @@ export interface Config { * https://docs.prebid.org/features/firstPartyData.html */ ortb2?: DeepPartial; + /** + * When set, only bids for which this function returns a truthy value are included in setTargeting. + * The function is called with the bid and the initially filtered bids (bidsReceived) that passed adunit, zero cpm, and other filters for comparison purposes within the function. + * Return false to exclude a bid from targeting. + */ + bidTargetingExclusion?: (bid: Bid, bids: Bid[]) => boolean; + /** + * List of fingerprinting APIs to disable. When an API is listed, the corresponding library + * returns a safe default instead of reading the real value. Supported: 'devicepixelratio', 'webdriver', 'resolvedoptions'. + */ + disableFingerprintingApis?: Array<'devicepixelratio' | 'webdriver' | 'resolvedoptions'>; } type PartialConfig = Partial & { [setting: string]: unknown }; @@ -257,7 +274,7 @@ type BidderConfig = { config: PartialConfig; } -type TopicalConfig = {[K in DeepPropertyName]: S extends DeepProperty ? TypeOfDeepProperty : unknown}; +type TopicalConfig = { [K in DeepPropertyName]: S extends DeepProperty ? TypeOfDeepProperty : unknown }; type UnregistrationFn = () => void; type GetConfigOptions = { @@ -494,7 +511,7 @@ export function newConfig() { if (topic === ALL_TOPICS) { callback(getConfig()); } else { - callback({[topic]: getConfig(topic)}); + callback({ [topic]: getConfig(topic) }); } } diff --git a/src/consentHandler.ts b/src/consentHandler.ts index b3fa9594673..1c1ae7a0355 100644 --- a/src/consentHandler.ts +++ b/src/consentHandler.ts @@ -1,7 +1,7 @@ -import {cyrb53Hash, isStr, timestamp} from './utils.js'; -import {defer, PbPromise} from './utils/promise.js'; -import {config} from './config.js'; -import type {ModuleType} from "./activities/modules.ts"; +import { cyrb53Hash, isStr, timestamp } from './utils.js'; +import { defer, PbPromise } from './utils/promise.js'; +import { config } from './config.js'; +import type { ModuleType } from "./activities/modules.ts"; /** * Placeholder gvlid for when vendor consent is not required. When this value is used as gvlid, the gdpr @@ -212,7 +212,7 @@ export function gvlidRegistry() { * `gvlid` is the single GVL ID for this family of modules (only defined if all modules with this name declare the same ID). */ get(moduleName: string) { - const result: GVLIDResult = {modules: registry[moduleName] || {}}; + const result: GVLIDResult = { modules: registry[moduleName] || {} }; if (flat.hasOwnProperty(moduleName) && flat[moduleName] !== none) { result.gvlid = flat[moduleName]; } @@ -266,7 +266,7 @@ export type AllConsentData = { } interface MultiHandler extends Pick, 'promise' | 'hash' | 'getConsentData' | 'reset'> { - getConsentMeta(): {[K in keyof typeof ALL_HANDLERS]: ReturnType<(typeof ALL_HANDLERS)[K]['getConsentMeta']>} + getConsentMeta(): { [K in keyof typeof ALL_HANDLERS]: ReturnType<(typeof ALL_HANDLERS)[K]['getConsentMeta']> } } export function multiHandler(handlers = ALL_HANDLERS): MultiHandler { diff --git a/src/constants.ts b/src/constants.ts index 6380d89f712..5c4c0a6f00f 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -25,7 +25,6 @@ export const EVENTS = { BID_RESPONSE: 'bidResponse', BID_REJECTED: 'bidRejected', NO_BID: 'noBid', - SEAT_NON_BID: 'seatNonBid', BID_WON: 'bidWon', BIDDER_DONE: 'bidderDone', BIDDER_ERROR: 'bidderError', @@ -33,7 +32,6 @@ export const EVENTS = { BEFORE_REQUEST_BIDS: 'beforeRequestBids', BEFORE_BIDDER_HTTP: 'beforeBidderHttp', REQUEST_BIDS: 'requestBids', - ADD_AD_UNITS: 'addAdUnits', AD_RENDER_FAILED: 'adRenderFailed', AD_RENDER_SUCCEEDED: 'adRenderSucceeded', TCF2_ENFORCEMENT: 'tcf2Enforcement', @@ -42,12 +40,7 @@ export const EVENTS = { STALE_RENDER: 'staleRender', EXPIRED_RENDER: 'expiredRender', BILLABLE_EVENT: 'billableEvent', - BID_ACCEPTED: 'bidAccepted', - RUN_PAAPI_AUCTION: 'paapiRunAuction', PBS_ANALYTICS: 'pbsAnalytics', - PAAPI_BID: 'paapiBid', - PAAPI_NO_BID: 'paapiNoBid', - PAAPI_ERROR: 'paapiError', BEFORE_PBS_HTTP: 'beforePBSHttp', BROWSI_INIT: 'browsiInit', BROWSI_DATA: 'browsiData', diff --git a/src/creativeRenderers.js b/src/creativeRenderers.js index dcbfd2b40ba..5b65d4e6cb1 100644 --- a/src/creativeRenderers.js +++ b/src/creativeRenderers.js @@ -1,8 +1,8 @@ -import {PbPromise} from './utils/promise.js'; -import {createInvisibleIframe} from './utils.js'; +import { PbPromise } from './utils/promise.js'; +import { createInvisibleIframe } from './utils.js'; // eslint-disable-next-line prebid/validate-imports -import {RENDERER} from '../creative-renderers/display.js'; // autogenerated during precompilation -import {hook} from './hook.js'; +import { RENDERER } from '../creative-renderers/display.js'; // autogenerated during precompilation +import { hook } from './hook.js'; // the minimum rendererVersion that will be used by PUC export const PUC_MIN_VERSION = 3; diff --git a/src/debugging.js b/src/debugging.js index bd0baf3eddb..9fbbb61543a 100644 --- a/src/debugging.js +++ b/src/debugging.js @@ -1,16 +1,16 @@ -import {config} from './config.js'; -import {getHook, hook} from './hook.js'; -import {getGlobal} from './prebidGlobal.js'; -import {logMessage, prefixLog} from './utils.js'; -import {createBid} from './bidfactory.js'; -import {loadExternalScript} from './adloader.js'; -import {PbPromise} from './utils/promise.js'; +import { config } from './config.js'; +import { getHook, hook } from './hook.js'; +import { getGlobal } from './prebidGlobal.js'; +import { logError, logMessage, prefixLog } from './utils.js'; +import { createBid } from './bidfactory.js'; +import { loadExternalScript } from './adloader.js'; +import { PbPromise } from './utils/promise.js'; import { MODULE_TYPE_PREBID } from './activities/modules.js'; import * as utils from './utils.js'; -import {BANNER, NATIVE, VIDEO} from './mediaTypes.js'; -import {Renderer} from './Renderer.js'; +import { BANNER, NATIVE, VIDEO } from './mediaTypes.js'; +import { Renderer } from './Renderer.js'; -import {getDistUrlBase, getGlobalVarName} from './buildOptions.js'; +import { getDistUrlBase, getGlobalVarName } from './buildOptions.js'; export const DEBUG_KEY = `__${getGlobalVarName()}_debugging__`; @@ -19,12 +19,12 @@ function isDebuggingInstalled() { } function loadScript(url) { - return new PbPromise((resolve) => { - loadExternalScript(url, MODULE_TYPE_PREBID, 'debugging', resolve); + return new PbPromise((resolve, reject) => { + loadExternalScript(url, MODULE_TYPE_PREBID, 'debugging', { success: resolve, error: reject }); }); } -export function debuggingModuleLoader({alreadyInstalled = isDebuggingInstalled, script = loadScript} = {}) { +export function debuggingModuleLoader({ alreadyInstalled = isDebuggingInstalled, script = loadScript } = {}) { let loading = null; return function () { if (loading == null) { @@ -59,11 +59,15 @@ export function debuggingModuleLoader({alreadyInstalled = isDebuggingInstalled, } } -export function debuggingControls({load = debuggingModuleLoader(), hook = getHook('requestBids')} = {}) { +export function debuggingControls({ load = debuggingModuleLoader(), hook = getHook('requestBids') } = {}) { let promise = null; let enabled = false; function waitForDebugging(next, ...args) { - return (promise || PbPromise.resolve()).then(() => next.apply(this, args)) + return (promise || PbPromise.resolve()) + .catch((e) => { + logError(`Could not load debugging module`, e); + }) + .then(() => next.apply(this, args)) } function enable() { if (!enabled) { @@ -74,14 +78,14 @@ export function debuggingControls({load = debuggingModuleLoader(), hook = getHoo } } function disable() { - hook.getHooks({hook: waitForDebugging}).remove(); + hook.getHooks({ hook: waitForDebugging }).remove(); enabled = false; } function reset() { promise = null; disable(); } - return {enable, disable, reset}; + return { enable, disable, reset }; } const ctl = debuggingControls(); @@ -107,6 +111,6 @@ export function loadSession() { } } -config.getConfig('debugging', function ({debugging}) { +config.getConfig('debugging', function ({ debugging }) { debugging?.enabled ? ctl.enable() : ctl.disable(); }); diff --git a/src/eventTrackers.js b/src/eventTrackers.js index b0c06cf0f1b..306db1fe300 100644 --- a/src/eventTrackers.js +++ b/src/eventTrackers.js @@ -2,6 +2,7 @@ export const TRACKER_METHOD_IMG = 1; export const TRACKER_METHOD_JS = 2; export const EVENT_TYPE_IMPRESSION = 1; export const EVENT_TYPE_WIN = 500; +export const EVENT_TYPE_VIEWABLE = 2; /** * Returns a map from event type (EVENT_TYPE_*) @@ -13,7 +14,7 @@ export const EVENT_TYPE_WIN = 500; * @returns {{[type: string]: {[method: string]: string[]}}} */ export function parseEventTrackers(eventTrackers) { - return (eventTrackers ?? []).reduce((tally, {event, method, url}) => { + return (eventTrackers ?? []).reduce((tally, { event, method, url }) => { const trackersForType = tally[event] = tally[event] ?? {}; const trackersForMethod = trackersForType[method] = trackersForType[method] ?? []; trackersForMethod.push(url); diff --git a/src/events.ts b/src/events.ts index 6ea102ab105..2e9603f3e7b 100644 --- a/src/events.ts +++ b/src/events.ts @@ -3,10 +3,10 @@ */ import * as utils from './utils.js' import { EVENTS, EVENT_ID_PATHS } from './constants.js'; -import {ttlCollection} from './utils/ttlCollection.js'; -import {config} from './config.js'; +import { ttlCollection } from './utils/ttlCollection.js'; +import { config } from './config.js'; -type CoreEvent = {[K in keyof typeof EVENTS]: typeof EVENTS[K]}[keyof typeof EVENTS]; +type CoreEvent = { [K in keyof typeof EVENTS]: typeof EVENTS[K] }[keyof typeof EVENTS]; // hide video events (unless the video module is included) with this one weird trick @@ -191,7 +191,7 @@ const _public = (function () { utils._setEventEmitter(_public.emit.bind(_public)); -export const {on, off, get, getEvents, emit, addEvents, has} = _public; +export const { on, off, get, getEvents, emit, addEvents, has } = _public; export function clearEvents() { eventsFired.clear(); diff --git a/src/fpd/enrichment.ts b/src/fpd/enrichment.ts index 1ccaaaf6692..4d3f8327e63 100644 --- a/src/fpd/enrichment.ts +++ b/src/fpd/enrichment.ts @@ -1,6 +1,6 @@ -import {hook} from '../hook.js'; -import {getRefererInfo, parseDomain} from '../refererDetection.js'; -import {findRootDomain} from './rootDomain.js'; +import { hook } from '../hook.js'; +import { getRefererInfo, parseDomain } from '../refererDetection.js'; +import { findRootDomain } from './rootDomain.js'; import { deepSetValue, deepAccess, @@ -12,15 +12,14 @@ import { mergeDeep, memoize } from '../utils.js'; -import { getDNT } from '../../libraries/dnt/index.js'; -import {config} from '../config.js'; -import {getHighEntropySUA, getLowEntropySUA} from './sua.js'; -import {PbPromise} from '../utils/promise.js'; -import {CLIENT_SECTIONS, clientSectionChecker, hasSection} from './oneClient.js'; -import {isActivityAllowed} from '../activities/rules.js'; -import {activityParams} from '../activities/activityParams.js'; -import {ACTIVITY_ACCESS_DEVICE} from '../activities/activities.js'; -import {MODULE_TYPE_PREBID} from '../activities/modules.js'; +import { config } from '../config.js'; +import { getHighEntropySUA, getLowEntropySUA } from './sua.js'; +import { PbPromise } from '../utils/promise.js'; +import { CLIENT_SECTIONS, clientSectionChecker, hasSection } from './oneClient.js'; +import { isActivityAllowed } from '../activities/rules.js'; +import { activityParams } from '../activities/activityParams.js'; +import { ACTIVITY_ACCESS_DEVICE } from '../activities/activities.js'; +import { MODULE_TYPE_PREBID } from '../activities/modules.js'; import { getViewportSize } from '../../libraries/viewport/viewport.js'; export const dep = { @@ -153,12 +152,11 @@ const ENRICHMENTS = { const h = getWinDimensions().screen.height; // vpw and vph are the viewport dimensions of the browser window - const {width: vpw, height: vph} = getViewportSize(); + const { width: vpw, height: vph } = getViewportSize(); const device = { w, h, - dnt: getDNT() ? 1 : 0, ua: win.navigator.userAgent, language: win.navigator.language.split('-').shift(), ext: { @@ -220,7 +218,7 @@ export const getMetaTagKeywords = memoize(() => { // Enrichment of properties common across dooh, app and site - will be dropped into whatever // section is appropriate function clientEnrichment(ortb2, ri) { - const domain = parseDomain(ri.page, {noLeadingWww: true}); + const domain = parseDomain(ri.page, { noLeadingWww: true }); const keywords = new Set(); if (config.getConfig('firstPartyData.keywords.meta') ?? true) { (getMetaTagKeywords() ?? []).forEach(key => keywords.add(key)); diff --git a/src/fpd/normalize.js b/src/fpd/normalize.js index d28bdde3802..66a9dbb54f4 100644 --- a/src/fpd/normalize.js +++ b/src/fpd/normalize.js @@ -1,5 +1,5 @@ -import {deepEqual, deepSetValue, deepAccess, logWarn} from '../utils.js'; -import {hook} from '../hook.js'; +import { deepEqual, deepSetValue, deepAccess, logWarn } from '../utils.js'; +import { hook } from '../hook.js'; export const normalizeFPD = hook('sync', function(ortb2Fragments) { [ diff --git a/src/fpd/oneClient.js b/src/fpd/oneClient.js index 67f53c73bd8..4262367d1d5 100644 --- a/src/fpd/oneClient.js +++ b/src/fpd/oneClient.js @@ -1,4 +1,4 @@ -import {logWarn} from '../utils.js'; +import { logWarn } from '../utils.js'; // mutually exclusive ORTB sections in order of priority - 'dooh' beats 'app' & 'site' and 'app' beats 'site'; // if one is set, the others will be removed diff --git a/src/fpd/rootDomain.js b/src/fpd/rootDomain.js index 60497c8ff81..24256426610 100644 --- a/src/fpd/rootDomain.js +++ b/src/fpd/rootDomain.js @@ -1,5 +1,5 @@ -import {memoize} from '../utils.js'; -import {canSetCookie, getCoreStorageManager} from '../storageManager.js'; +import { memoize } from '../utils.js'; +import { canSetCookie, getCoreStorageManager } from '../storageManager.js'; export const coreStorage = getCoreStorageManager('fpdEnrichment'); diff --git a/src/fpd/sua.js b/src/fpd/sua.js index ac34951e347..aef2d840976 100644 --- a/src/fpd/sua.js +++ b/src/fpd/sua.js @@ -1,5 +1,5 @@ -import {isEmptyStr, isStr, isEmpty} from '../utils.js'; -import {PbPromise} from '../utils/promise.js'; +import { isEmptyStr, isStr, isEmpty } from '../utils.js'; +import { PbPromise } from '../utils/promise.js'; export const SUA_SOURCE_UNKNOWN = 0; export const SUA_SOURCE_LOW_ENTROPY = 1; @@ -77,19 +77,19 @@ export function highEntropySUAAccessor(uaData = window.navigator?.userAgentData) */ export function uaDataToSUA(source, uaData) { function toBrandVersion(brand, version) { - const bv = {brand}; + const bv = { brand }; if (isStr(version) && !isEmptyStr(version)) { bv.version = version.split('.'); } return bv; } - const sua = {source}; + const sua = { source }; if (uaData.platform) { sua.platform = toBrandVersion(uaData.platform, uaData.platformVersion); } if (uaData.fullVersionList || uaData.brands) { - sua.browsers = (uaData.fullVersionList || uaData.brands).map(({brand, version}) => toBrandVersion(brand, version)); + sua.browsers = (uaData.fullVersionList || uaData.brands).map(({ brand, version }) => toBrandVersion(brand, version)); } if (typeof uaData['mobile'] !== 'undefined') { sua.mobile = uaData.mobile ? 1 : 0; diff --git a/src/hook.ts b/src/hook.ts index 9aabc5b4ea3..1d78bb23dcc 100644 --- a/src/hook.ts +++ b/src/hook.ts @@ -1,7 +1,7 @@ import funHooks from 'fun-hooks/no-eval/index.js'; -import {defer} from './utils/promise.js'; -import type {AnyFunction, Wraps} from "./types/functions.d.ts"; -import type {AllExceptLast, Last} from "./types/tuples.d.ts"; +import { defer } from './utils/promise.js'; +import type { AnyFunction, Wraps } from "./types/functions.d.ts"; +import type { AllExceptLast, Last } from "./types/tuples.d.ts"; export type Next = { (...args: Parameters): unknown; @@ -66,14 +66,14 @@ export const ready: Promise = readyCtl.promise; export const getHook = hook.get; export function setupBeforeHookFnOnce(baseFn: Hookable, hookFn: BeforeHook, priority = 15) { - const result = baseFn.getHooks({hook: hookFn}); + const result = baseFn.getHooks({ hook: hookFn }); if (result.length === 0) { baseFn.before(hookFn, priority); } } const submoduleInstallMap = {}; -export function module(name, install, {postInstallAllowed = false} = {}) { +export function module(name, install, { postInstallAllowed = false } = {}) { hook('async', function (submodules) { submodules.forEach(args => install(...args)); if (postInstallAllowed) submoduleInstallMap[name] = install; @@ -99,7 +99,7 @@ export function submodule(name: N, ...args: Submodul export function wrapHook(hook: Hookable, wrapper: FN): Hookable { Object.defineProperties( wrapper, - Object.fromEntries(['before', 'after', 'getHooks', 'removeAll'].map((m) => [m, {get: () => hook[m]}])) + Object.fromEntries(['before', 'after', 'getHooks', 'removeAll'].map((m) => [m, { get: () => hook[m] }])) ); return (wrapper as unknown) as Hookable; } diff --git a/src/mediaTypes.ts b/src/mediaTypes.ts index ed7c4244878..01012985639 100644 --- a/src/mediaTypes.ts +++ b/src/mediaTypes.ts @@ -4,11 +4,11 @@ * All adapters are assumed to support banner ads. Other media types are specified by Adapters when they * register themselves with prebid-core. */ -import type {BannerMediaType} from "./banner.ts"; -import type {RendererConfig} from "./adUnits.ts"; -import type {VideoMediaType} from "./video.ts"; -import type {NativeMediaType} from "./native.ts"; -import {AudioMediaType} from "./audio.ts"; +import type { BannerMediaType } from "./banner.ts"; +import type { RendererConfig } from "./adUnits.ts"; +import type { VideoMediaType } from "./video.ts"; +import type { NativeMediaType } from "./native.ts"; +import { AudioMediaType } from "./audio.ts"; export type MediaType = typeof NATIVE | typeof VIDEO | typeof BANNER | typeof AUDIO; diff --git a/src/native.ts b/src/native.ts index 89a2bb8053d..c8e27fc6fcc 100644 --- a/src/native.ts +++ b/src/native.ts @@ -12,7 +12,7 @@ import { triggerPixel } from './utils.js'; -import {auctionManager} from './auctionManager.js'; +import { auctionManager } from './auctionManager.js'; import { NATIVE_ASSET_TYPES, NATIVE_IMAGE_TYPES, @@ -20,17 +20,17 @@ import { NATIVE_KEYS_THAT_ARE_NOT_ASSETS, PREBID_NATIVE_DATA_KEYS_TO_ORTB } from './constants.js'; -import {NATIVE} from './mediaTypes.js'; -import {getRenderingData} from './adRendering.js'; -import {getCreativeRendererSource, PUC_MIN_VERSION} from './creativeRenderers.js'; -import {EVENT_TYPE_IMPRESSION, parseEventTrackers, TRACKER_METHOD_IMG, TRACKER_METHOD_JS} from './eventTrackers.js'; -import type {Link, NativeRequest, NativeResponse} from "./types/ortb/native.d.ts"; -import type {Size} from "./types/common.d.ts"; -import type {Ext} from "./types/ortb/common.d.ts"; -import type {BidResponse, NativeBidResponse} from "./bidfactory.ts"; -import type {AdUnit} from "./adUnits.ts"; - -type LegacyAssets = Omit<{[K in keyof (typeof NATIVE_KEYS)]: unknown}, (typeof NATIVE_KEYS_THAT_ARE_NOT_ASSETS)[number]>; +import { NATIVE } from './mediaTypes.js'; +import { getRenderingData } from './adRendering.js'; +import { getCreativeRendererSource, PUC_MIN_VERSION } from './creativeRenderers.js'; +import { EVENT_TYPE_IMPRESSION, EVENT_TYPE_VIEWABLE, parseEventTrackers, TRACKER_METHOD_IMG, TRACKER_METHOD_JS } from './eventTrackers.js'; +import type { Link, NativeRequest, NativeResponse } from "./types/ortb/native.d.ts"; +import type { Size } from "./types/common.d.ts"; +import type { Ext } from "./types/ortb/common.d.ts"; +import type { BidResponse, NativeBidResponse } from "./bidfactory.ts"; +import type { AdUnit } from "./adUnits.ts"; + +type LegacyAssets = Omit<{ [K in keyof (typeof NATIVE_KEYS)]: unknown }, (typeof NATIVE_KEYS_THAT_ARE_NOT_ASSETS)[number]>; type LegacyImageAssets = { icon: unknown, image: unknown }; type LegacyImageAssetResponse = { @@ -293,7 +293,7 @@ export const hasNonNativeBidder = adUnit => * bid Native bid to validate * @return {Boolean} If object is valid */ -export function nativeBidIsValid(bid, {index = auctionManager.index} = {}) { +export function nativeBidIsValid(bid, { index = auctionManager.index } = {}) { const adUnit = index.getAdUnit(bid); if (!adUnit) { return false; } const ortbRequest = adUnit.nativeOrtbRequest @@ -350,14 +350,15 @@ export function fireNativeTrackers(message, bidResponse) { if (message.action === 'click') { fireClickTrackers(nativeResponse, message?.assetId); } else { - fireImpressionTrackers(nativeResponse); + fireImpressionTrackers(nativeResponse, bidResponse); } return message.action; } -export function fireImpressionTrackers(nativeResponse, {runMarkup = (mkup) => insertHtmlIntoIframe(mkup), fetchURL = triggerPixel} = {}) { - let {[TRACKER_METHOD_IMG]: img = [], [TRACKER_METHOD_JS]: js = []} = parseEventTrackers( - nativeResponse.eventtrackers || [] +export function fireImpressionTrackers(nativeResponse, bidResponse, { runMarkup = (mkup) => insertHtmlIntoIframe(mkup), fetchURL = triggerPixel } = {}) { + const filteredEventTrackers = filterEventTrackers(nativeResponse, bidResponse); + let { [TRACKER_METHOD_IMG]: img = [], [TRACKER_METHOD_JS]: js = [] } = parseEventTrackers( + filteredEventTrackers || [] )[EVENT_TYPE_IMPRESSION] || {}; if (nativeResponse.imptrackers) { @@ -375,7 +376,7 @@ export function fireImpressionTrackers(nativeResponse, {runMarkup = (mkup) => in } } -export function fireClickTrackers(nativeResponse, assetId = null, {fetchURL = triggerPixel} = {}) { +export function fireClickTrackers(nativeResponse, assetId = null, { fetchURL = triggerPixel } = {}) { // legacy click tracker if (!assetId) { (nativeResponse.link?.clicktrackers || []).forEach(url => fetchURL(url)); @@ -398,6 +399,20 @@ export function fireClickTrackers(nativeResponse, assetId = null, {fetchURL = tr } } +export function filterEventTrackers(nativeResponse, bid) { + const DEFAULT_ALLOWED_TRACKERS = [ + { event: EVENT_TYPE_IMPRESSION, methods: [TRACKER_METHOD_IMG, TRACKER_METHOD_JS] }, + { event: EVENT_TYPE_VIEWABLE, methods: [TRACKER_METHOD_IMG, TRACKER_METHOD_JS] }, + ]; + const mediaTypes = auctionManager.index.getMediaTypes(bid) || {}; + const nativeMediaType = mediaTypes.native || {}; + const requestEventTrackers = nativeMediaType.ortb?.eventtrackers || DEFAULT_ALLOWED_TRACKERS; + + const { eventtrackers = [] } = nativeResponse || {}; + + return eventtrackers.filter(tracker => requestEventTrackers.some(requestTracker => requestTracker.event === tracker.event && requestTracker.methods.includes(tracker.method))); +} + export function setNativeResponseProperties(bid, adUnit) { const nativeOrtbRequest = adUnit?.nativeOrtbRequest; const nativeOrtbResponse = bid.native?.ortb; @@ -423,7 +438,7 @@ function getNativeAssets(nativeProps, keys, ext = false) { if (ext === false && key === 'ext') { assets.push(...getNativeAssets(value, keys, true)); } else if (ext || NATIVE_KEYS.hasOwnProperty(key)) { - assets.push({key, value: getAssetValue(value)}); + assets.push({ key, value: getAssetValue(value) }); } }); return assets; @@ -443,7 +458,7 @@ export function getNativeRenderingData(bid, adUnit, keys) { return data; } -function assetsMessage(data, adObject, keys, {index = auctionManager.index} = {}) { +function assetsMessage(data, adObject, keys, { index = auctionManager.index } = {}) { const msg: any = { message: 'assetResponse', adId: data.adId, @@ -457,7 +472,7 @@ function assetsMessage(data, adObject, keys, {index = auctionManager.index} = {} msg.renderer = getCreativeRendererSource(adObject); msg.rendererVersion = PUC_MIN_VERSION; if (keys != null) { - renderData.assets = renderData.assets.filter(({key}) => keys.includes(key)) + renderData.assets = renderData.assets.filter(({ key }) => keys.includes(key)) } } else { renderData = getNativeRenderingData(adObject, index.getAdUnit(adObject), keys); diff --git a/src/pbjsORTB.ts b/src/pbjsORTB.ts index 9653fa82558..20656c7d85e 100644 --- a/src/pbjsORTB.ts +++ b/src/pbjsORTB.ts @@ -9,7 +9,7 @@ export function processorRegistry() { const processors = {}; return { - registerOrtbProcessor({type, name, fn, priority = 0, dialects = [DEFAULT]}) { + registerOrtbProcessor({ type, name, fn, priority = 0, dialects = [DEFAULT] }) { if (!types.has(type)) { throw new Error(`ORTB processor type must be one of: ${PROCESSOR_TYPES.join(', ')}`) } @@ -32,4 +32,4 @@ export function processorRegistry() { } } -export const {registerOrtbProcessor, getProcessors} = processorRegistry(); +export const { registerOrtbProcessor, getProcessors } = processorRegistry(); diff --git a/src/prebid.public.ts b/src/prebid.public.ts index 3404b32ef4d..5f30a10900b 100644 --- a/src/prebid.public.ts +++ b/src/prebid.public.ts @@ -3,5 +3,5 @@ // to get around this problem. import './types/summary/core.d.ts'; import './types/summary/global.d.ts'; -export {default} from './prebid.ts'; +export { default } from './prebid.ts'; export type * from './types/summary/exports.d.ts'; diff --git a/src/prebid.ts b/src/prebid.ts index c282155c8d1..c2d21b9c4eb 100644 --- a/src/prebid.ts +++ b/src/prebid.ts @@ -1,6 +1,6 @@ /** @module pbjs */ -import {getGlobal, type PrebidJS} from './prebidGlobal.js'; +import { getGlobal, type PrebidJS } from './prebidGlobal.js'; import { deepAccess, deepClone, @@ -24,27 +24,22 @@ import { uniques, unsupportedBidderMessage } from './utils.js'; -import {listenMessagesFromCreative} from './secureCreatives.js'; -import {userSync} from './userSync.js'; -import {config} from './config.js'; -import {auctionManager} from './auctionManager.js'; -import {isBidUsable, type SlotMatchingFn, targeting} from './targeting.js'; -import {hook, wrapHook} from './hook.js'; -import {loadSession} from './debugging.js'; -import {storageCallbacks} from './storageManager.js'; -import adapterManager, { - type AliasBidderOptions, - type BidRequest, - getS2SBidderSet -} from './adapterManager.js'; -import {BID_STATUS, EVENTS, NATIVE_KEYS} from './constants.js'; -import type {EventHandler, EventIDs, Event} from "./events.js"; +import { listenMessagesFromCreative } from './secureCreatives.js'; +import { userSync } from './userSync.js'; +import { config } from './config.js'; +import { auctionManager } from './auctionManager.js'; +import { isBidUsable, targeting } from './targeting.js'; +import { hook, wrapHook } from './hook.js'; +import { loadSession } from './debugging.js'; +import { storageCallbacks } from './storageManager.js'; +import adapterManager, { type AliasBidderOptions, type BidRequest, getS2SBidderSet } from './adapterManager.js'; +import { BID_STATUS, EVENTS, NATIVE_KEYS } from './constants.js'; +import type { Event, EventHandler, EventIDs } from "./events.js"; import * as events from './events.js'; -import {type Metrics, newMetrics, useMetrics} from './utils/perfMetrics.js'; -import {type Defer, defer, PbPromise} from './utils/promise.js'; -import {pbYield} from './utils/yield.js'; -import {enrichFPD} from './fpd/enrichment.js'; -import {allConsent} from './consentHandler.js'; +import { type Metrics, newMetrics, useMetrics } from './utils/perfMetrics.js'; +import { type Defer, defer, PbPromise } from './utils/promise.js'; +import { enrichFPD } from './fpd/enrichment.js'; +import { allConsent } from './consentHandler.js'; import { insertLocatorFrame, markBidAsRendered, @@ -52,29 +47,30 @@ import { renderAdDirect, renderIfDeferred } from './adRendering.js'; -import {getHighestCpm} from './utils/reducers.js'; -import {fillVideoDefaults, ORTB_VIDEO_PARAMS} from './video.js'; -import {ORTB_BANNER_PARAMS} from './banner.js'; -import {BANNER, VIDEO} from './mediaTypes.js'; -import {delayIfPrerendering} from './utils/prerendering.js'; -import {type BidAdapter, type BidderSpec, newBidder} from './adapters/bidderFactory.js'; -import {normalizeFPD} from './fpd/normalize.js'; -import type {Bid} from "./bidfactory.ts"; -import type {AdUnit, AdUnitDefinition, BidderParams} from "./adUnits.ts"; -import type {AdUnitCode, BidderCode, ByAdUnit, Identifier, ORTBFragments} from "./types/common.d.ts"; -import type {ORTBRequest} from "./types/ortb/request.d.ts"; -import type {DeepPartial} from "./types/objects.d.ts"; -import type {AnyFunction, Wraps} from "./types/functions.d.ts"; -import type {BidderScopedSettings, BidderSettings} from "./bidderSettings.ts"; -import {ORTB_AUDIO_PARAMS, fillAudioDefaults} from './audio.ts'; - -import {getGlobalVarName} from "./buildOptions.ts"; +import { getHighestCpm } from './utils/reducers.js'; +import { fillVideoDefaults, ORTB_VIDEO_PARAMS } from './video.js'; +import { ORTB_BANNER_PARAMS } from './banner.js'; +import { BANNER, VIDEO } from './mediaTypes.js'; +import { delayIfPrerendering } from './utils/prerendering.js'; +import { type BidAdapter, type BidderSpec, newBidder } from './adapters/bidderFactory.js'; +import { normalizeFPD } from './fpd/normalize.js'; +import type { Bid } from "./bidfactory.ts"; +import type { AdUnit, AdUnitDefinition, BidderParams } from "./adUnits.ts"; +import type { AdUnitCode, BidderCode, ByAdUnit, Identifier, ORTBFragments } from "./types/common.d.ts"; +import type { ORTBRequest } from "./types/ortb/request.d.ts"; +import type { DeepPartial } from "./types/objects.d.ts"; +import type { AnyFunction, Wraps } from "./types/functions.d.ts"; +import type { BidderScopedSettings, BidderSettings } from "./bidderSettings.ts"; +import { fillAudioDefaults, ORTB_AUDIO_PARAMS } from './audio.ts'; + +import { getGlobalVarName } from "./buildOptions.ts"; +import { yieldAll } from "./utils/yield.ts"; const pbjsInstance = getGlobal(); const { triggerUserSyncs } = userSync; /* private variables */ -const { ADD_AD_UNITS, REQUEST_BIDS, SET_TARGETING } = EVENTS; +const { REQUEST_BIDS, SET_TARGETING } = EVENTS; // initialize existing debugging sessions if present loadSession(); @@ -176,14 +172,14 @@ function validateBannerMediaType(adUnit: AdUnit) { banner.format = format; try { formatSizes = format - .filter(({w, h, wratio, hratio}) => { + .filter(({ w, h, wratio, hratio }) => { if ((w ?? h) != null && (wratio ?? hratio) != null) { logWarn(`Ad unit banner.format specifies both w/h and wratio/hratio`, adUnit); return false; } return (w != null && h != null) || (wratio != null && hratio != null); }) - .map(({w, h, wratio, hratio}) => [w ?? wratio, h ?? hratio]); + .map(({ w, h, wratio, hratio }) => [w ?? wratio, h ?? hratio]); } catch (e) { logError(`Invalid format definition on ad unit ${adUnit.code}`, format); } @@ -356,7 +352,7 @@ function validateAdUnit(adUnitDef: AdUnitDefinition): AdUnit { return null; } if (adUnit.ortb2Imp != null && (bids == null || bids.length === 0)) { - adUnit.bids = [{bidder: null}]; // the 'null' bidder is treated as an s2s-only placeholder by adapterManager + adUnit.bids = [{ bidder: null }]; // the 'null' bidder is treated as an s2s-only placeholder by adapterManager logMessage(msg(`defines 'adUnit.ortb2Imp' with no 'adUnit.bids'; it will be seen only by S2S adapters`)); } @@ -614,14 +610,13 @@ addApiMethod('getBidResponsesForAdUnitCode', getBidResponsesForAdUnitCode); /** * Set query string targeting on one or more GPT ad units. * @param adUnit a single `adUnit.code` or multiple. - * @param customSlotMatching gets a GoogleTag slot and returns a filter function for adUnitCode, so you can decide to match on either eg. return slot => { return adUnitCode => { return slot.getSlotElementId() === 'myFavoriteDivId'; } }; */ -function setTargetingForGPTAsync(adUnit?: AdUnitCode | AdUnitCode[], customSlotMatching?: SlotMatchingFn) { +function setTargetingForGPTAsync(adUnit?: AdUnitCode | AdUnitCode[]) { if (!isGptPubadsDefined()) { logError('window.googletag is not defined on the page'); return; } - targeting.setTargetingForGPT(adUnit, customSlotMatching); + targeting.setTargetingForGPT(adUnit); } addApiMethod('setTargetingForGPTAsync', setTargetingForGPTAsync); @@ -654,8 +649,7 @@ type RenderAdOptions = { * @param id adId of the bid to render * @param options */ -async function renderAd(doc: Document, id: Bid['adId'], options?: RenderAdOptions) { - await pbYield(); +function renderAd(doc: Document, id: Bid['adId'], options?: RenderAdOptions) { renderAdDirect(doc, id, options); } addApiMethod('renderAd', renderAd); @@ -791,17 +785,17 @@ export const requestBids = (function() { adUnitCodes = adUnitCodes.filter(uniques); return Object.assign({ adUnitCodes - }, adUnits.reduce(({included, excluded}, adUnit) => { + }, adUnits.reduce(({ included, excluded }, adUnit) => { (adUnitCodes.includes(adUnit.code) ? included : excluded).push(adUnit); - return {included, excluded}; - }, {included: [], excluded: []})) + return { included, excluded }; + }, { included: [], excluded: [] })) } } const delegate = hook('async', function (reqBidOptions: PrivRequestBidsOptions): void { let { bidsBackHandler, timeout, adUnits, adUnitCodes, labels, auctionId, ttlBuffer, ortb2, metrics, defer } = reqBidOptions ?? {}; const cbTimeout = timeout || config.getConfig('bidderTimeout'); - ({included: adUnits, adUnitCodes} = filterAdUnits(adUnits, adUnitCodes)); + ({ included: adUnits, adUnitCodes } = filterAdUnits(adUnits, adUnitCodes)); let ortb2Fragments = { global: mergeDeep({}, config.getAnyConfig('ortb2') || {}, ortb2 || {}), bidder: Object.fromEntries(Object.entries(config.getBidderConfig()).map(([bidder, cfg]) => [bidder, deepClone(cfg.ortb2)]).filter(([_, ortb2]) => ortb2 != null)) @@ -810,7 +804,7 @@ export const requestBids = (function() { enrichFPD(PbPromise.resolve(ortb2Fragments.global)).then(global => { ortb2Fragments.global = global; - return startAuction({bidsBackHandler, timeout: cbTimeout, adUnits, adUnitCodes, labels, auctionId, ttlBuffer, ortb2Fragments, metrics, defer}); + return startAuction({ bidsBackHandler, timeout: cbTimeout, adUnits, adUnitCodes, labels, auctionId, ttlBuffer, ortb2Fragments, metrics, defer }); }) }, 'requestBids'); @@ -827,7 +821,7 @@ export const requestBids = (function() { const metrics = newMetrics(); metrics.checkpoint('requestBids'); - const {included, excluded, adUnitCodes} = filterAdUnits(adUnits, options.adUnitCodes); + const { included, excluded, adUnitCodes } = filterAdUnits(adUnits, options.adUnitCodes); events.emit(REQUEST_BIDS, Object.assign(options, { adUnits: included, @@ -843,7 +837,7 @@ export const requestBids = (function() { // what it means for an event handler to modify adUnitCodes - so don't allow it adUnitCodes, metrics, - defer: defer({promiseFactory: (r) => new Promise(r)}) + defer: defer({ promiseFactory: (r) => new Promise(r) }) }); delegate.call(this, req); return req.defer.promise; @@ -959,21 +953,12 @@ export function executeCallbacks(fn, reqBidsConfigObj) { // This hook will execute all storage callbacks which were registered before gdpr enforcement hook was added. Some bidders, user id modules use storage functions when module is parsed but gdpr enforcement hook is not added at that stage as setConfig callbacks are yet to be called. Hence for such calls we execute all the stored callbacks just before requestBids. At this hook point we will know for sure that tcfControl module is added or not requestBids.before(executeCallbacks, 49); -declare module './events' { - interface Events { - /** - * Fired when `.addAdUniuts` is called. - */ - [ADD_AD_UNITS]: []; - } -} /** * Add ad unit(s) * @param adUnits */ function addAdUnits(adUnits: AdUnitDefinition | AdUnitDefinition[]) { pbjsInstance.adUnits.push(...(Array.isArray(adUnits) ? adUnits : [adUnits])) - events.emit(ADD_AD_UNITS); } addApiMethod('addAdUnits', addAdUnits); @@ -1166,7 +1151,7 @@ type MarkWinningBidAsUsedOptions = ({ /** * Mark the winning bid as used, should only be used in conjunction with video */ -function markWinningBidAsUsed({adId, adUnitCode, analytics = false, events = false}: MarkWinningBidAsUsedOptions) { +function markWinningBidAsUsed({ adId, adUnitCode, analytics = false, events = false }: MarkWinningBidAsUsedOptions) { let bids; if (adUnitCode && adId == null) { bids = targeting.getWinningBids(adUnitCode); @@ -1240,19 +1225,22 @@ function quePush(command) { }) } -async function _processQueue(queue) { - for (const cmd of queue) { - if (typeof cmd.called === 'undefined') { - try { - cmd.call(); - cmd.called = true; - } catch (e) { - logError('Error processing command :', 'prebid.js', e); - } +function runCommand(cmd) { + if (typeof cmd.called === 'undefined') { + try { + cmd.call(); + cmd.called = true; + } catch (e) { + logError('Error processing command :', 'prebid.js', e); } - await pbYield(); } } +function _processQueue(queue, cb?) { + yieldAll( + () => getGlobal().yield ?? true, + queue.map(cmd => () => runCommand(cmd)), cb + ); +} /** * Process the command queue, effectively booting up Prebid. @@ -1263,12 +1251,11 @@ const processQueue = delayIfPrerendering(() => pbjsInstance.delayPrerendering, a pbjsInstance.que.push = pbjsInstance.cmd.push = quePush; insertLocatorFrame(); hook.ready(); - try { - await _processQueue(pbjsInstance.que); - await _processQueue(pbjsInstance.cmd); - } finally { - queSetupComplete.resolve(); - } + _processQueue(pbjsInstance.que, () => { + _processQueue(pbjsInstance.cmd, () => { + queSetupComplete.resolve(); + }); + }); }) addApiMethod('processQueue', processQueue, false); @@ -1276,7 +1263,7 @@ addApiMethod('processQueue', processQueue, false); * Manually trigger billing for a winning bid, idendified either by ad ID or ad unit code. * Used in conjunction with `adUnit.deferBilling`. */ -function triggerBilling({adId, adUnitCode}: { +function triggerBilling({ adId, adUnitCode }: { adId?: string; adUnitCode?: AdUnitCode }) { diff --git a/src/prebidGlobal.ts b/src/prebidGlobal.ts index 749f5af85e7..7794f2a9f6c 100644 --- a/src/prebidGlobal.ts +++ b/src/prebidGlobal.ts @@ -1,4 +1,4 @@ -import {getGlobalVarName, shouldDefineGlobal} from "./buildOptions.ts"; +import { getGlobalVarName, shouldDefineGlobal } from "./buildOptions.ts"; interface Command { (): any; @@ -21,10 +21,6 @@ export interface PrebidJS { * Names of all installed modules. */ installedModules: string[] - /** - * Optional scheduler used by pbYield(). - */ - scheduler?: { yield: () => Promise } } // if the global already exists in global document scope, use it, if not, create the object diff --git a/src/refererDetection.ts b/src/refererDetection.ts index 43e6d890aa0..fb8e1ccc880 100644 --- a/src/refererDetection.ts +++ b/src/refererDetection.ts @@ -9,7 +9,7 @@ */ import { config } from './config.js'; -import {logWarn} from './utils.js'; +import { logWarn } from './utils.js'; /** * Prepend a URL with the page's protocol (http/https), if necessary. @@ -40,7 +40,7 @@ export function ensureProtocol(url, win = window) { * @param options.noPort - If true, do not include the ':[port]' portion. * @return The extracted domain or undefined if the URL is invalid. */ -export function parseDomain(url: string, {noLeadingWww = false, noPort = false} = {}): string | null { +export function parseDomain(url: string, { noLeadingWww = false, noPort = false } = {}): string | null { let target; try { target = new URL(ensureProtocol(url)); diff --git a/src/secureCreatives.js b/src/secureCreatives.js index 6ce564c95d0..f87b3af3ac4 100644 --- a/src/secureCreatives.js +++ b/src/secureCreatives.js @@ -3,19 +3,20 @@ access to a publisher page from creative payloads. */ -import {getAllAssetsMessage, getAssetMessage} from './native.js'; -import {BID_STATUS, MESSAGES} from './constants.js'; -import {isApnGetTagDefined, isGptPubadsDefined, logError, logWarn} from './utils.js'; +import { getAllAssetsMessage, getAssetMessage } from './native.js'; +import { BID_STATUS, MESSAGES } from './constants.js'; +import { isApnGetTagDefined, isGptPubadsDefined, logError, logWarn } from './utils.js'; import { deferRendering, - getBidToRender, handleCreativeEvent, handleNativeMessage, handleRender, markWinner } from './adRendering.js'; -import {getCreativeRendererSource, PUC_MIN_VERSION} from './creativeRenderers.js'; -import {PbPromise} from './utils/promise.js'; +import { getCreativeRendererSource, PUC_MIN_VERSION } from './creativeRenderers.js'; +import { PbPromise } from './utils/promise.js'; +import { getAdUnitElement } from './utils/adUnits.js'; +import { auctionManager } from './auctionManager.js'; const { REQUEST, RESPONSE, NATIVE, EVENT } = MESSAGES; @@ -56,11 +57,11 @@ export function getReplier(ev) { function ensureAdId(adId, reply) { return function (data, ...args) { - return reply(Object.assign({}, data, {adId}), ...args); + return reply(Object.assign({}, data, { adId }), ...args); } } -export function receiveMessage(ev) { +export function receiveMessage(ev, cb) { var key = ev.message ? 'message' : 'data'; var data = {}; try { @@ -70,9 +71,8 @@ export function receiveMessage(ev) { } if (data && data.adId && data.message && HANDLER_MAP.hasOwnProperty(data.message)) { - return getBidToRender(data.adId, data.message === MESSAGES.REQUEST).then(adObject => { - HANDLER_MAP[data.message](ensureAdId(data.adId, getReplier(ev)), data, adObject); - }) + HANDLER_MAP[data.message](ensureAdId(data.adId, getReplier(ev)), data, auctionManager.findBidByAdId(data.adId)); + cb && cb(); } } @@ -81,7 +81,7 @@ function getResizer(adId, bidResponse) { // the first is the one that was requested and is tied to the element // the second is the one that is being rendered (sometimes different, e.g. in some paapi setups) return function (width, height) { - resizeRemoteCreative({...bidResponse, width, height, adId}); + resizeRemoteCreative({ ...bidResponse, width, height, adId }); } } function handleRenderRequest(reply, message, bidResponse) { @@ -118,7 +118,7 @@ function handleNativeRequest(reply, data, adObject) { deferRendering(adObject, () => reply(getAllAssetsMessage(data, adObject))); break; default: - handleNativeMessage(data, adObject, {resizeFn: getResizer(data.adId, adObject)}); + handleNativeMessage(data, adObject, { resizeFn: getResizer(data.adId, adObject) }); markWinner(adObject); } } @@ -151,7 +151,7 @@ export function resizeAnchor(ins, width, height) { // wait until GPT has set dimensions on the ins, otherwise our changes will be overridden const resizer = setInterval(() => { let done = false; - Object.entries({width, height}) + Object.entries({ width, height }) .forEach(([dimension, newValue]) => { if (/\d+px/.test(ins.style[dimension])) { ins.style[dimension] = getDimension(newValue); @@ -166,7 +166,7 @@ export function resizeAnchor(ins, width, height) { }) } -export function resizeRemoteCreative({instl, adId, adUnitCode, width, height}) { +export function resizeRemoteCreative({ instl, element, adId, adUnitCode, width, height }) { // do not resize interstitials - the creative frame takes the full screen and sizing of the ad should // be handled within it. if (instl) return; @@ -189,7 +189,7 @@ export function resizeRemoteCreative({instl, adId, adUnitCode, width, height}) { function getElementByAdUnit(elmType) { const id = getElementIdBasedOnAdServer(adId, adUnitCode); - const parentDivEle = document.getElementById(id); + const parentDivEle = id == null ? getAdUnitElement({ element, adUnitCode }) : document.getElementById(id); return parentDivEle && parentDivEle.querySelector(elmType); } @@ -206,7 +206,6 @@ export function resizeRemoteCreative({instl, adId, adUnitCode, width, height}) { return apnId; } } - return adUnitCode; } function getDfpElementId(adId) { diff --git a/src/storageManager.ts b/src/storageManager.ts index c5c2862965f..998a3cbdbca 100644 --- a/src/storageManager.ts +++ b/src/storageManager.ts @@ -1,7 +1,7 @@ -import {checkCookieSupport, hasDeviceAccess, logError, memoize, timestamp} from './utils.js'; -import {bidderSettings} from './bidderSettings.js'; -import {MODULE_TYPE_BIDDER, MODULE_TYPE_PREBID, type ModuleType} from './activities/modules.js'; -import {isActivityAllowed, registerActivityControl} from './activities/rules.js'; +import { checkCookieSupport, hasDeviceAccess, logError, memoize, timestamp } from './utils.js'; +import { bidderSettings } from './bidderSettings.js'; +import { MODULE_TYPE_BIDDER, MODULE_TYPE_PREBID, type ModuleType } from './activities/modules.js'; +import { isActivityAllowed, registerActivityControl } from './activities/rules.js'; import { ACTIVITY_PARAM_ADAPTER_CODE, ACTIVITY_PARAM_COMPONENT_TYPE, @@ -10,13 +10,13 @@ import { ACTIVITY_PARAM_STORAGE_WRITE } from './activities/params.js'; -import {ACTIVITY_ACCESS_DEVICE, ACTIVITY_ACCESS_REQUEST_CREDENTIALS} from './activities/activities.js'; -import {config} from './config.js'; -import {hook} from "./hook.ts"; +import { ACTIVITY_ACCESS_DEVICE, ACTIVITY_ACCESS_REQUEST_CREDENTIALS } from './activities/activities.js'; +import { config } from './config.js'; +import { hook } from "./hook.ts"; import adapterManager from './adapterManager.js'; -import {activityParams} from './activities/activityParams.js'; -import type {AnyFunction} from "./types/functions.d.ts"; -import type {BidderCode} from "./types/common.d.ts"; +import { activityParams } from './activities/activityParams.js'; +import type { AnyFunction } from "./types/functions.d.ts"; +import type { BidderCode } from "./types/common.d.ts"; export const STORAGE_TYPE_LOCALSTORAGE = 'html5'; export const STORAGE_TYPE_COOKIES = 'cookie'; @@ -57,14 +57,14 @@ export type StorageManager = { /* * Storage manager constructor. Consumers should prefer one of `getStorageManager` or `getCoreStorageManager`. */ -export function newStorageManager({moduleName, moduleType, advertiseKeys = true}: { +export function newStorageManager({ moduleName, moduleType, advertiseKeys = true }: { moduleName: string; moduleType: ModuleType; /** * If false, do not pass the 'storageKey' to activity checks - turning off storageControl for this manager. */ advertiseKeys?: boolean; -} = {} as any, {isAllowed = isActivityAllowed} = {}) { +} = {} as any, { isAllowed = isActivityAllowed } = {}) { function isValid(cb, storageType, storageKey, isWrite) { let mod = moduleName; const curBidder = config.getCurrentBidder(); @@ -262,7 +262,7 @@ export function newStorageManager({moduleName, moduleType, advertiseKeys = true} * for `{moduleType: 'bidder', moduleName: bidderCode}`. * */ -export function getStorageManager({moduleType, moduleName, bidderCode}: { +export function getStorageManager({ moduleType, moduleName, bidderCode }: { moduleType?: ModuleType; moduleName?: string; bidderCode?: BidderCode; @@ -277,7 +277,7 @@ export function getStorageManager({moduleType, moduleName, bidderCode}: { } else if (!moduleName || !moduleType) { err() } - return newStorageManager({moduleType, moduleName}); + return newStorageManager({ moduleType, moduleName }); } /** @@ -286,7 +286,7 @@ export function getStorageManager({moduleType, moduleName, bidderCode}: { * @param {string} moduleName Module name */ export function getCoreStorageManager(moduleName) { - return newStorageManager({moduleName: moduleName, moduleType: MODULE_TYPE_PREBID}); + return newStorageManager({ moduleName: moduleName, moduleType: MODULE_TYPE_PREBID }); } export const canSetCookie = (() => { @@ -326,7 +326,7 @@ export const canSetCookie = (() => { */ export function deviceAccessRule() { if (!hasDeviceAccess()) { - return {allow: false} + return { allow: false } } } registerActivityControl(ACTIVITY_ACCESS_DEVICE, 'deviceAccess config', deviceAccessRule); @@ -351,7 +351,7 @@ export function storageAllowedRule(params, bs = bidderSettings) { allow = Array.isArray(allow) ? allow.some((e) => e === storageType) : allow === storageType; } if (!allow) { - return {allow}; + return { allow }; } } diff --git a/src/targeting.ts b/src/targeting.ts index 477ddaab7f0..1dcaef1b554 100644 --- a/src/targeting.ts +++ b/src/targeting.ts @@ -1,11 +1,10 @@ -import {auctionManager} from './auctionManager.js'; -import {getBufferedTTL} from './bidTTL.js'; -import {bidderSettings} from './bidderSettings.js'; -import {config} from './config.js'; -import {BID_STATUS, DEFAULT_TARGETING_KEYS, EVENTS, JSON_MAPPING, TARGETING_KEYS} from './constants.js'; +import { auctionManager } from './auctionManager.js'; +import { getBufferedTTL } from './bidTTL.js'; +import { bidderSettings } from './bidderSettings.js'; +import { config } from './config.js'; +import { BID_STATUS, DEFAULT_TARGETING_KEYS, EVENTS, JSON_MAPPING, TARGETING_KEYS } from './constants.js'; import * as events from './events.js'; -import {hook} from './hook.js'; -import {ADPOD} from './mediaTypes.js'; +import { hook } from './hook.js'; import { deepAccess, deepClone, @@ -22,11 +21,11 @@ import { timestamp, uniques, } from './utils.js'; -import {getHighestCpm, getOldestHighestCpmBid} from './utils/reducers.js'; -import type {Bid} from './bidfactory.ts'; -import type {AdUnitCode, ByAdUnit, Identifier} from './types/common.d.ts'; -import type {DefaultTargeting} from './auction.ts'; -import {lock} from "./targeting/lock.ts"; +import { getHighestCpm, getOldestHighestCpmBid } from './utils/reducers.js'; +import type { Bid } from './bidfactory.ts'; +import type { AdUnitCode, ByAdUnit, Identifier } from './types/common.d.ts'; +import type { DefaultTargeting } from './auction.ts'; +import { lock } from "./targeting/lock.ts"; var pbTargetingKeys = []; @@ -133,13 +132,12 @@ export function sortByDealAndPriceBucketOrCpm(useCpm = false) { * Return a map where each code in `adUnitCodes` maps to a list of GPT slots that match it. * * @param adUnitCodes - * @param customSlotMatching * @param getSlots */ -export function getGPTSlotsForAdUnits(adUnitCodes: AdUnitCode[], customSlotMatching, getSlots = () => window.googletag.pubads().getSlots()): ByAdUnit { +export function getGPTSlotsForAdUnits(adUnitCodes: AdUnitCode[], getSlots = () => window.googletag.pubads().getSlots()): ByAdUnit { return getSlots().reduce((auToSlots, slot) => { - const customMatch = isFn(customSlotMatching) && customSlotMatching(slot); - Object.keys(auToSlots).filter(isFn(customMatch) ? customMatch : isAdUnitCodeMatchingSlot(slot)).forEach(au => auToSlots[au].push(slot)); + Object.keys(auToSlots).filter(isAdUnitCodeMatchingSlot(slot)) + .forEach(au => auToSlots[au].push(slot)); return auToSlots; }, Object.fromEntries(adUnitCodes.map(au => [au, []]))); } @@ -306,13 +304,13 @@ export function newTargeting(auctionManager) { return flatTargeting; }, - setTargetingForGPT: hook('sync', function (adUnit?: AdUnitCode | AdUnitCode[], customSlotMatching?: SlotMatchingFn) { + setTargetingForGPT: hook('sync', function (adUnit?: AdUnitCode | AdUnitCode[]) { // get our ad unit codes const targetingSet: ByAdUnit = targeting.getAllTargeting(adUnit); const resetMap = Object.fromEntries(pbTargetingKeys.map(key => [key, null])); - Object.entries(getGPTSlotsForAdUnits(Object.keys(targetingSet), customSlotMatching)).forEach(([targetId, slots]) => { + Object.entries(getGPTSlotsForAdUnits(Object.keys(targetingSet))).forEach(([targetId, slots]) => { slots.forEach(slot => { // now set new targeting keys Object.keys(targetingSet[targetId]).forEach(key => { @@ -396,7 +394,7 @@ export function newTargeting(auctionManager) { // pt${n} keys should not be uppercased keywordsObj[key] = astTargeting[targetId][key]; } - window.apntag.setKeywords(targetId, keywordsObj, {overrideKeyValue: true}); + window.apntag.setKeywords(targetId, keywordsObj, { overrideKeyValue: true }); } }) } @@ -424,7 +422,7 @@ export function newTargeting(auctionManager) { (deals || allowedSendAllBidTargeting.indexOf(key) !== -1))); if (targetingValue) { - result.push({[bid.adUnitCode]: targetingValue}) + result.push({ [bid.adUnitCode]: targetingValue }) } } return result; @@ -513,12 +511,27 @@ export function newTargeting(auctionManager) { const customKeysByUnit = {}; const alwaysIncludeDeals = config.getConfig('targetingControls.alwaysIncludeDeals'); - bidsReceived.forEach(bid => { + const bidTargetingExclusion = config.getConfig('bidTargetingExclusion'); + + const initiallyFilteredBids = bidsReceived.filter(bid => { const adUnitIsEligible = adUnitCodes.includes(bid.adUnitCode); const cpmAllowed = bidderSettings.get(bid.bidderCode, 'allowZeroCpmBids') === true ? bid.cpm >= 0 : bid.cpm > 0; const isPreferredDeal = alwaysIncludeDeals && bid.dealId; + return adUnitIsEligible && (isPreferredDeal || cpmAllowed); + }); + + initiallyFilteredBids.forEach(bid => { + let notExcludedByConfig = true; + if (typeof bidTargetingExclusion === 'function') { + try { + notExcludedByConfig = bidTargetingExclusion(bid, initiallyFilteredBids); + } catch (e) { + logWarn(`Error in bidTargetingExclusion function - excluding bid ${bid.bidderCode} [${bid.adUnitCode}]`); + notExcludedByConfig = false; + } + } - if (adUnitIsEligible && (isPreferredDeal || cpmAllowed)) { + if (notExcludedByConfig) { filteredBids.push(bid); Object.keys(bid.adserverTargeting) .filter(getCustomKeys()) @@ -538,7 +551,7 @@ export function newTargeting(auctionManager) { } }); - return {filteredBids, customKeysByUnit}; + return { filteredBids, customKeysByUnit }; } // warn about conflicting configuration @@ -663,7 +676,7 @@ export function newTargeting(auctionManager) { const cacheFilter = bidCacheEnabled || isBidFromLastAuction; const bidFilter = cacheFilter && filterFunctionResult; - if (bidFilter && bid?.video?.context !== ADPOD && isBidUsable(bid)) { + if (bidFilter && isBidUsable(bid)) { bid.latestTargetedAuctionId = latestAuctionForAdUnit[bid.adUnitCode]; bids.push(bid) } @@ -733,11 +746,11 @@ export function newTargeting(auctionManager) { if (customKeysForUnit) { Object.keys(customKeysForUnit).forEach(key => { - if (key && customKeysForUnit[key]) targeting.push({[key]: customKeysForUnit[key]}); + if (key && customKeysForUnit[key]) targeting.push({ [key]: customKeysForUnit[key] }); }) } - acc.push({[newBid.adUnitCode]: targeting}); + acc.push({ [newBid.adUnitCode]: targeting }); return acc; }, []); @@ -747,7 +760,7 @@ export function newTargeting(auctionManager) { return keys.reduce((targeting, key) => { const value = bid.adserverTargeting[key]; if (value) { - targeting.push({[`${key}_${bid.bidderCode}`.substring(0, MAX_DFP_KEYLENGTH)]: [bid.adserverTargeting[key]]}) + targeting.push({ [`${key}_${bid.bidderCode}`.substring(0, MAX_DFP_KEYLENGTH)]: [bid.adserverTargeting[key]] }) } return targeting; }, []); @@ -756,7 +769,7 @@ export function newTargeting(auctionManager) { function getVersionTargeting(adUnitCodes) { let version = config.getConfig('targetingControls.version'); if (version === false) return []; - return adUnitCodes.map(au => ({[au]: [{[TARGETING_KEYS.VERSION]: [version ?? DEFAULT_HB_VER]}]})); + return adUnitCodes.map(au => ({ [au]: [{ [TARGETING_KEYS.VERSION]: [version ?? DEFAULT_HB_VER] }] })); } function getAdUnitTargeting(adUnitCodes) { @@ -770,7 +783,7 @@ export function newTargeting(auctionManager) { return Object.keys(aut) .map(function(key) { if (isStr(aut[key])) aut[key] = aut[key].split(',').map(s => s.trim()); - if (!isArray(aut[key])) aut[key] = [ aut[key] ]; + if (!isArray(aut[key])) aut[key] = [aut[key]]; return { [key]: aut[key] }; }); } @@ -780,7 +793,7 @@ export function newTargeting(auctionManager) { .reduce((result, adUnit) => { const targetingValues = getTargetingValues(adUnit); - if (targetingValues)result.push({[adUnit.code]: targetingValues}); + if (targetingValues)result.push({ [adUnit.code]: targetingValues }); return result; }, []); } diff --git a/src/targeting/lock.ts b/src/targeting/lock.ts index f59d7f1d890..f88a2b1c40c 100644 --- a/src/targeting/lock.ts +++ b/src/targeting/lock.ts @@ -1,7 +1,7 @@ -import type {TargetingMap} from "../targeting.ts"; -import {config} from "../config.ts"; -import {ttlCollection} from "../utils/ttlCollection.ts"; -import {isGptPubadsDefined} from "../utils.js"; +import type { TargetingMap } from "../targeting.ts"; +import { config } from "../config.ts"; +import { ttlCollection } from "../utils/ttlCollection.ts"; +import { isGptPubadsDefined } from "../utils.js"; import SlotRenderEndedEvent = googletag.events.SlotRenderEndedEvent; const DEFAULT_LOCK_TIMEOUT = 3000; @@ -34,7 +34,7 @@ export function targetingLock() { slack: 0, }); config.getConfig('targetingControls', (cfg) => { - ({lock: keys, lockTimeout: timeout = DEFAULT_LOCK_TIMEOUT} = cfg.targetingControls ?? {}); + ({ lock: keys, lockTimeout: timeout = DEFAULT_LOCK_TIMEOUT } = cfg.targetingControls ?? {}); if (keys != null && !Array.isArray(keys)) { keys = [keys]; } else if (keys == null) { @@ -44,7 +44,7 @@ export function targetingLock() { }) const [setupGpt, tearDownGpt] = (() => { let enabled = false; - function onGptRender({slot}: SlotRenderEndedEvent) { + function onGptRender({ slot }: SlotRenderEndedEvent) { keys?.forEach(key => slot.getTargeting(key)?.forEach(locked.delete)); } return [ diff --git a/src/types/common.d.ts b/src/types/common.d.ts index 3f385ab6868..a39b2c6d6c4 100644 --- a/src/types/common.d.ts +++ b/src/types/common.d.ts @@ -1,5 +1,5 @@ -import type {DeepPartial} from "./objects.d.ts"; -import type {ORTBRequest} from "./ortb/request.d.ts"; +import type { DeepPartial } from "./objects.d.ts"; +import type { ORTBRequest } from "./ortb/request.d.ts"; /** * Prebid-generated identifier. diff --git a/src/types/ortb/ext/dchain.d.ts b/src/types/ortb/ext/dchain.d.ts index 2db81a44c36..51ac0bf544f 100644 --- a/src/types/ortb/ext/dchain.d.ts +++ b/src/types/ortb/ext/dchain.d.ts @@ -1,6 +1,6 @@ // https://iabtechlab.com/wp-content/uploads/2021/03/DemandChainObject-1-0.pdf -import type {BooleanInt, Extensible} from "../common.d.ts"; +import type { BooleanInt, Extensible } from "../common.d.ts"; export type DemandChainNode = Extensible & { /** diff --git a/src/types/ortb/native.d.ts b/src/types/ortb/native.d.ts index ed099d10b11..c80ca986680 100644 --- a/src/types/ortb/native.d.ts +++ b/src/types/ortb/native.d.ts @@ -1,4 +1,4 @@ -import type {EventTrackerResponse as oEventTrackerResponse, NativeResponse as oNativeResponse, LinkResponse, NativeRequest as oNativeRequest} from 'iab-native'; +import type { EventTrackerResponse as oEventTrackerResponse, NativeResponse as oNativeResponse, LinkResponse, NativeRequest as oNativeRequest } from 'iab-native'; export type NativeRequest = oNativeRequest; export type EventTrackerResponse = oEventTrackerResponse; diff --git a/src/types/ortb/request.d.ts b/src/types/ortb/request.d.ts index 64cf842ccb1..87bbaa54ea1 100644 --- a/src/types/ortb/request.d.ts +++ b/src/types/ortb/request.d.ts @@ -1,9 +1,9 @@ /* eslint prebid/validate-imports: 0 */ -import type {Ext} from './common.d.ts'; -import type {DSARequest} from "./ext/dsa.d.ts"; +import type { Ext } from './common.d.ts'; +import type { DSARequest } from "./ext/dsa.d.ts"; -import type {BidRequest, Imp} from 'iab-openrtb/v26'; +import type { BidRequest, Imp } from 'iab-openrtb/v26'; type TidSource = 'pbjs' | 'pbjsStable' | 'pub'; diff --git a/src/types/ortb/response.d.ts b/src/types/ortb/response.d.ts index b5d7ffeecc3..f89cf7cdd99 100644 --- a/src/types/ortb/response.d.ts +++ b/src/types/ortb/response.d.ts @@ -1,7 +1,7 @@ -import type {BidResponse, SeatBid, Bid} from "iab-openrtb/v26"; -import type {Ext} from './common.d.ts'; -import type {DSAResponse} from "./ext/dsa.d.ts"; -import type {DemandChain} from "./ext/dchain.d.ts"; +import type { BidResponse, SeatBid, Bid } from "iab-openrtb/v26"; +import type { Ext } from './common.d.ts'; +import type { DSAResponse } from "./ext/dsa.d.ts"; +import type { DemandChain } from "./ext/dchain.d.ts"; export interface ORTBBid extends Bid { ext: Ext & { diff --git a/src/types/summary/exports.d.ts b/src/types/summary/exports.d.ts index f44277fe613..17d58c19a5b 100644 --- a/src/types/summary/exports.d.ts +++ b/src/types/summary/exports.d.ts @@ -1,10 +1,10 @@ -export type {PrebidJS} from '../../prebidGlobal.ts'; +export type { PrebidJS } from '../../prebidGlobal.ts'; // Type definitions (besides the prebid global) that may be useful to consumers, -export type {Bid, VideoBid, BannerBid, NativeBid} from '../../bidfactory.ts'; -export type {BidRequest, BidderRequest} from '../../adapterManager.ts'; -export type {Config} from '../../config.ts'; -export type {AdUnit, AdUnitDefinition, AdUnitBid} from '../../adUnits.ts' -export type {ORTBRequest, ORTBImp} from '../ortb/request.d.ts'; -export type {ORTBResponse} from '../ortb/response.d.ts'; -export type {NativeRequest as ORTBNativeRequest} from '../ortb/native.d.ts'; -export type {Event, EventRecord, EventPayload, EventHandler} from '../../events.ts'; +export type { Bid, VideoBid, BannerBid, NativeBid } from '../../bidfactory.ts'; +export type { BidRequest, BidderRequest } from '../../adapterManager.ts'; +export type { Config } from '../../config.ts'; +export type { AdUnit, AdUnitDefinition, AdUnitBid } from '../../adUnits.ts' +export type { ORTBRequest, ORTBImp } from '../ortb/request.d.ts'; +export type { ORTBResponse } from '../ortb/response.d.ts'; +export type { NativeRequest as ORTBNativeRequest } from '../ortb/native.d.ts'; +export type { Event, EventRecord, EventPayload, EventHandler } from '../../events.ts'; diff --git a/src/userSync.ts b/src/userSync.ts index 6c62fb36833..0d20e0f9a3a 100644 --- a/src/userSync.ts +++ b/src/userSync.ts @@ -5,23 +5,23 @@ import { import { config } from './config.js'; import { getCoreStorageManager } from './storageManager.js'; -import {isActivityAllowed, registerActivityControl} from './activities/rules.js'; -import {ACTIVITY_SYNC_USER} from './activities/activities.js'; +import { isActivityAllowed, registerActivityControl } from './activities/rules.js'; +import { ACTIVITY_SYNC_USER } from './activities/activities.js'; import { ACTIVITY_PARAM_COMPONENT_NAME, ACTIVITY_PARAM_COMPONENT_TYPE, ACTIVITY_PARAM_SYNC_TYPE, ACTIVITY_PARAM_SYNC_URL } from './activities/params.js'; -import {MODULE_TYPE_BIDDER} from './activities/modules.js'; -import {activityParams} from './activities/activityParams.js'; -import type {BidderCode} from "./types/common.d.ts"; +import { MODULE_TYPE_BIDDER } from './activities/modules.js'; +import { activityParams } from './activities/activityParams.js'; +import type { BidderCode } from "./types/common.d.ts"; export type SyncType = 'image' | 'iframe'; type SyncConfig = { bidders: '*' | BidderCode[]; filter: 'include' | 'exclude' } -type FilterSettings = {[K in SyncType | 'all']?: SyncConfig}; +type FilterSettings = { [K in SyncType | 'all']?: SyncConfig }; export interface UserSyncConfig { /** @@ -122,13 +122,13 @@ export function newUserSync(deps) { deps.regRule(ACTIVITY_SYNC_USER, 'userSync config', (params) => { if (!usConfig.syncEnabled) { - return {allow: false, reason: 'syncs are disabled'} + return { allow: false, reason: 'syncs are disabled' } } if (params[ACTIVITY_PARAM_COMPONENT_TYPE] === MODULE_TYPE_BIDDER) { const syncType = params[ACTIVITY_PARAM_SYNC_TYPE]; const bidder = params[ACTIVITY_PARAM_COMPONENT_NAME]; if (!publicApi.canBidderRegisterSync(syncType, bidder)) { - return {allow: false, reason: `${syncType} syncs are not enabled for ${bidder}`} + return { allow: false, reason: `${syncType} syncs are not enabled for ${bidder}` } } } }); diff --git a/src/utils.js b/src/utils.js index ec30b934a49..65cb3713cce 100644 --- a/src/utils.js +++ b/src/utils.js @@ -1,14 +1,14 @@ -import {config} from './config.js'; +import { config } from './config.js'; -import {EVENTS} from './constants.js'; -import {PbPromise} from './utils/promise.js'; +import { EVENTS } from './constants.js'; +import { PbPromise } from './utils/promise.js'; import deepAccess from 'dlv/index.js'; -import {isArray, isFn, isStr, isPlainObject} from './utils/objects.js'; +import { isArray, isFn, isStr, isPlainObject } from './utils/objects.js'; export { deepAccess }; export { dset as deepSetValue } from 'dset'; export * from './utils/objects.js' -export {getWinDimensions, resetWinDimensions, getScreenOrientation} from './utils/winDimensions.js'; +export { getWinDimensions, resetWinDimensions, getScreenOrientation } from './utils/winDimensions.js'; const consoleExists = Boolean(window.console); const consoleLogExists = Boolean(consoleExists && window.console.log); const consoleInfoExists = Boolean(consoleExists && window.console.info); @@ -164,7 +164,7 @@ export function parseGPTSingleSizeArray(singleSize) { } export function sizeTupleToRtbSize(size) { - return {w: size[0], h: size[1]}; + return { w: size[0], h: size[1] }; } // Parse a GPT style single size array, (i.e [300, 250]) @@ -206,6 +206,18 @@ export function canAccessWindowTop() { } } +/** + * Returns the window to use for fingerprinting reads: win if provided, otherwise top or self. + * @param {Window} [win] + * @returns {Window} + */ +export function getFallbackWindow(win) { + if (win) { + return win; + } + return canAccessWindowTop() ? internal.getWindowTop() : internal.getWindowSelf(); +} + /** * Wrappers to console.(log | info | warn | error). Takes N arguments, the same as the native methods */ @@ -650,7 +662,7 @@ export function replaceMacros(str, subs) { } export function replaceAuctionPrice(str, cpm) { - return replaceMacros(str, {AUCTION_PRICE: cpm}) + return replaceMacros(str, { AUCTION_PRICE: cpm }) } export function replaceClickThrough(str, clicktag) { @@ -760,7 +772,7 @@ export function groupBy(xs, key) { */ export function isValidMediaTypes(mediaTypes) { const SUPPORTED_MEDIA_TYPES = ['banner', 'native', 'video', 'audio']; - const SUPPORTED_STREAM_TYPES = ['instream', 'outstream', 'adpod']; + const SUPPORTED_STREAM_TYPES = ['instream', 'outstream']; const types = Object.keys(mediaTypes); @@ -798,7 +810,9 @@ export const compareCodeAndSlot = (slot, adUnitCode) => slot.getAdUnitPath() === * @return filter function */ export function isAdUnitCodeMatchingSlot(slot) { - return (adUnitCode) => compareCodeAndSlot(slot, adUnitCode); + const customGptSlotMatching = config.getConfig('customGptSlotMatching'); + const match = isFn(customGptSlotMatching) && customGptSlotMatching(slot); + return isFn(match) ? match : (adUnitCode) => compareCodeAndSlot(slot, adUnitCode); } /** @@ -808,7 +822,7 @@ export function isAdUnitCodeMatchingSlot(slot) { * @return {string} warning message to display when condition is met */ export function unsupportedBidderMessage(adUnit, bidder) { - const mediaType = Object.keys(adUnit.mediaTypes || {'banner': 'banner'}).join(', '); + const mediaType = Object.keys(adUnit.mediaTypes || { 'banner': 'banner' }).join(', '); return ` ${adUnit.code} is a ${mediaType} ad unit @@ -1125,7 +1139,7 @@ export function getUnixTimestampFromNow(timeValue = 0, timeUnit = 'd') { */ export function convertObjectToArray(obj) { return Object.keys(obj).map(key => { - return {[key]: obj[key]}; + return { [key]: obj[key] }; }); } diff --git a/src/utils/adUnits.ts b/src/utils/adUnits.ts new file mode 100644 index 00000000000..d9f4196e19c --- /dev/null +++ b/src/utils/adUnits.ts @@ -0,0 +1,21 @@ +import type { AdUnitDefinition } from "../adUnits.ts"; +import type { BidRequest } from "../adapterManager.ts"; +import type { Bid } from "../bidfactory.ts"; + +export function getAdUnitElement(bidRequest: BidRequest): HTMLElement +export function getAdUnitElement(bidResponse: Bid): HTMLElement +export function getAdUnitElement(adUnit: AdUnitDefinition): HTMLElement +export function getAdUnitElement(target: { + code?: string, + adUnitCode?: string, + element?: HTMLElement +}): HTMLElement | null { + if (target.element != null) { + return target.element; + } + const id = target.adUnitCode ?? target.code; + if (id) { + return document.getElementById(id); + } + return null; +} diff --git a/src/utils/cpm.js b/src/utils/cpm.js index 7dfabbe53be..f0206486186 100644 --- a/src/utils/cpm.js +++ b/src/utils/cpm.js @@ -1,8 +1,8 @@ -import {auctionManager} from '../auctionManager.js'; -import {bidderSettings} from '../bidderSettings.js'; -import {logError} from '../utils.js'; +import { auctionManager } from '../auctionManager.js'; +import { bidderSettings } from '../bidderSettings.js'; +import { logError } from '../utils.js'; -export function adjustCpm(cpm, bidResponse, bidRequest, {index = auctionManager.index, bs = bidderSettings} = {}) { +export function adjustCpm(cpm, bidResponse, bidRequest, { index = auctionManager.index, bs = bidderSettings } = {}) { bidRequest = bidRequest || index.getBidRequest(bidResponse); const adapterCode = bidResponse?.adapterCode; const bidderCode = bidResponse?.bidderCode || bidRequest?.bidder; diff --git a/src/utils/objects.ts b/src/utils/objects.ts index 90674d366d6..ca4ac3f41c8 100644 --- a/src/utils/objects.ts +++ b/src/utils/objects.ts @@ -1,6 +1,6 @@ -import {klona} from "klona/json"; -import type {AnyFunction} from "../types/functions.d.ts"; -import type {Repeat} from "../types/tuples.d.ts"; +import { klona } from "klona/json"; +import type { AnyFunction } from "../types/functions.d.ts"; +import type { Repeat } from "../types/tuples.d.ts"; export function deepClone(obj: T): T { return (klona(obj) || {}) as T; diff --git a/src/utils/perfMetrics.ts b/src/utils/perfMetrics.ts index 78b9cff2534..c8778f48e16 100644 --- a/src/utils/perfMetrics.ts +++ b/src/utils/perfMetrics.ts @@ -1,8 +1,8 @@ -import {config} from '../config.js'; -import type {AnyFunction, Wraps} from "../types/functions.d.ts"; -import {type BeforeHook, type BeforeHookParams, type HookType, Next} from "../hook.ts"; -import type {addBidResponse} from "../auction.ts"; -import type {PrivRequestBidsOptions, StartAuctionOptions} from "../prebid.ts"; +import { config } from '../config.js'; +import type { AnyFunction, Wraps } from "../types/functions.d.ts"; +import { type BeforeHook, type BeforeHookParams, type HookType, Next } from "../hook.ts"; +import type { addBidResponse } from "../auction.ts"; +import type { PrivRequestBidsOptions, StartAuctionOptions } from "../prebid.ts"; export const CONFIG_TOGGLE = 'performanceMetrics'; const getTime = window.performance && window.performance.now ? () => window.performance.now() : () => Date.now(); @@ -44,9 +44,9 @@ function wrapFn(fn: F, before?: () => void, after?: () => }; } -export function metricsFactory({now = getTime, mkNode = makeNode, mkTimer = makeTimer, mkRenamer = (rename) => rename, nodes = NODES} = {}) { +export function metricsFactory({ now = getTime, mkNode = makeNode, mkTimer = makeTimer, mkRenamer = (rename) => rename, nodes = NODES } = {}) { return function newMetrics() { - function makeMetrics(self, rename = (n) => ({forEach(fn) { fn(n); }})) { + function makeMetrics(self, rename = (n) => ({ forEach(fn) { fn(n); } })) { rename = mkRenamer(rename); function accessor(slot) { @@ -248,18 +248,18 @@ export function metricsFactory({now = getTime, mkNode = makeNode, mkTimer = make * } * ``` */ - function fork({propagate = true, stopPropagation = false, includeGroups = false}: PropagationOptions = {}): Metrics { - return makeMetrics(mkNode([[self, {propagate, stopPropagation, includeGroups}]]), rename); + function fork({ propagate = true, stopPropagation = false, includeGroups = false }: PropagationOptions = {}): Metrics { + return makeMetrics(mkNode([[self, { propagate, stopPropagation, includeGroups }]]), rename); } /** * Join `otherMetrics` with these; all metrics from `otherMetrics` will (by default) be propagated here, * and all metrics from here will be included in `otherMetrics`. */ - function join(otherMetrics: Metrics, {propagate = true, stopPropagation = false, includeGroups = false}: PropagationOptions = {}): void { + function join(otherMetrics: Metrics, { propagate = true, stopPropagation = false, includeGroups = false }: PropagationOptions = {}): void { const other = nodes.get(otherMetrics); if (other != null) { - other.addParent(self, {propagate, stopPropagation, includeGroups}); + other.addParent(self, { propagate, stopPropagation, includeGroups }); } } @@ -329,7 +329,7 @@ function makeNode(parents) { newSibling() { return makeNode(parents.slice()); }, - dfWalk({visit, follow = () => true, visited = new Set(), inEdge} = {} as any) { + dfWalk({ visit, follow = () => true, visited = new Set(), inEdge } = {} as any) { let res; if (!visited.has(this)) { visited.add(this); @@ -337,7 +337,7 @@ function makeNode(parents) { if (res != null) return res; for (const [parent, outEdge] of parents) { if (follow(inEdge, outEdge)) { - res = parent.dfWalk({visit, follow, visited, inEdge: outEdge}); + res = parent.dfWalk({ visit, follow, visited, inEdge: outEdge }); if (res != null) return res; } } @@ -349,19 +349,19 @@ function makeNode(parents) { const nullMetrics: Metrics = (() => { const nop = function () {}; const empty = () => ({}); - const none = {forEach: nop}; + const none = { forEach: nop }; const nullTimer = () => null; nullTimer.stopBefore = (fn) => fn; nullTimer.stopAfter = (fn) => fn; const nullNode = Object.defineProperties( - {dfWalk: nop, newSibling: () => nullNode, addParent: nop}, - Object.fromEntries(['metrics', 'timestamps', 'groups'].map(prop => [prop, {get: empty}]))); + { dfWalk: nop, newSibling: () => nullNode, addParent: nop }, + Object.fromEntries(['metrics', 'timestamps', 'groups'].map(prop => [prop, { get: empty }]))); return metricsFactory({ now: () => 0, mkNode: () => nullNode as any, mkRenamer: () => () => none, mkTimer: () => nullTimer, - nodes: {get: nop, set: nop} as unknown as Map + nodes: { get: nop, set: nop } as unknown as Map })(); })(); diff --git a/src/utils/prerendering.ts b/src/utils/prerendering.ts index 088f98f7a41..eab2bb14eda 100644 --- a/src/utils/prerendering.ts +++ b/src/utils/prerendering.ts @@ -1,6 +1,6 @@ -import {logInfo} from '../utils.js'; -import type {AnyFunction} from "../types/functions.d.ts"; -import type {UnwrapPromise, ToPromise} from "./promise.ts"; +import { logInfo } from '../utils.js'; +import type { AnyFunction } from "../types/functions.d.ts"; +import type { UnwrapPromise, ToPromise } from "./promise.ts"; /** * Returns a wrapper around fn that delays execution until the page if activated, if it was prerendered and isDelayEnabled returns true. @@ -13,7 +13,7 @@ export function delayIfPrerendering(isDelayEnabled: () => document.addEventListener('prerenderingchange', () => { logInfo(`Auctions were suspended while page was prerendering`) resolve(fn.apply(this, args)) - }, {once: true}) + }, { once: true }) }) } else { return Promise.resolve>>(fn.apply(this, args)); diff --git a/src/utils/promise.ts b/src/utils/promise.ts index 2da4ea53e2d..93bfb02e731 100644 --- a/src/utils/promise.ts +++ b/src/utils/promise.ts @@ -1,5 +1,5 @@ -import {GreedyPromise, greedySetTimeout} from '../../libraries/greedy/greedyPromise.js'; -import {getGlobal} from '../prebidGlobal.js'; +import { GreedyPromise, greedySetTimeout } from '../../libraries/greedy/greedyPromise.js'; +import { getGlobal } from '../prebidGlobal.js'; declare module '../prebidGlobal' { interface PrebidJS { @@ -35,7 +35,7 @@ export type ToPromise = Promise>; /** * @returns a {promise, resolve, reject} trio where `promise` is resolved by calling `resolve` or `reject`. */ -export function defer({promiseFactory = (resolver) => new PbPromise(resolver) as Promise}: { +export function defer({ promiseFactory = (resolver) => new PbPromise(resolver) as Promise }: { promiseFactory?: (...args: ConstructorParameters>) => Promise } = {}): Defer { function invoker(delegate) { diff --git a/src/utils/ttlCollection.ts b/src/utils/ttlCollection.ts index 8ec43f231e5..38e6ecaf435 100644 --- a/src/utils/ttlCollection.ts +++ b/src/utils/ttlCollection.ts @@ -1,6 +1,6 @@ -import {PbPromise} from './promise.js'; -import {binarySearch, logError, timestamp} from '../utils.js'; -import {setFocusTimeout} from './focusTimeout.js'; +import { PbPromise } from './promise.js'; +import { binarySearch, logError, timestamp } from '../utils.js'; +import { setFocusTimeout } from './focusTimeout.js'; export type TTLCollection = ReturnType>; diff --git a/src/utils/winDimensions.js b/src/utils/winDimensions.js index 2759e56eba6..af25f3b2b25 100644 --- a/src/utils/winDimensions.js +++ b/src/utils/winDimensions.js @@ -1,5 +1,5 @@ -import {canAccessWindowTop, internal as utilsInternals} from '../utils.js'; -import {CachedApiWrapper} from './cachedApiWrapper.js'; +import { canAccessWindowTop, internal as utilsInternals } from '../utils.js'; +import { CachedApiWrapper } from './cachedApiWrapper.js'; const CHECK_INTERVAL_MS = 20; diff --git a/src/utils/yield.ts b/src/utils/yield.ts index 049150c2520..0ac37b9d6b5 100644 --- a/src/utils/yield.ts +++ b/src/utils/yield.ts @@ -1,7 +1,63 @@ -import {getGlobal} from '../prebidGlobal.js'; -import {PbPromise} from './promise.js'; +import { PbPromise } from "./promise.ts"; -export function pbYield(): Promise { - const scheduler = getGlobal().scheduler ?? (window as any).scheduler; - return scheduler?.yield ? scheduler.yield() : PbPromise.resolve(); +declare module '../prebidGlobal' { + interface PrebidJS { + /** + * Enable yielding of the main thread. + */ + yield?: boolean; + } +} + +function doYield() { + const scheduler = (window as any).scheduler; + return typeof scheduler?.yield === 'function' ? scheduler.yield() : PbPromise.resolve() +} + +/** + * Runs `cb`, after yielding the main thread if `shouldYield` returns true. + */ +export function pbYield(shouldYield: () => boolean, cb: () => void) { + if (shouldYield()) { + doYield().then(cb); + } else { + cb(); + } +} + +/** + * Returns a wrapper around `fn` that yields the main thread if `shouldYield` returns true. + */ +export function yieldsIf void>(shouldYield: () => boolean, fn: T): (...args: Parameters) => void { + return function (...args) { + pbYield(shouldYield, () => { + fn.apply(this, args); + }) + } +} + +/** + * Runs each function in `fns`, yielding the main thread in between each one if `shouldYield` returns true. + * Runs `cb` after all functions have been run. + */ +export function yieldAll(shouldYield: () => boolean, fns: (() => void)[], cb?: () => void) { + serialize(fns.map(fn => (cb) => { + pbYield(shouldYield, () => { + fn(); + cb(); + }) + }), cb); +} + +export function serialize(fns: ((cb: () => void) => void)[], cb?: () => void) { + let i = 0; + function next() { + if (fns.length > i) { + i += 1; + fns[i - 1](next); + } else if (typeof cb === 'function') { + cb(); + } + } + next(); } diff --git a/src/video.ts b/src/video.ts index 020ee6a1bd2..32e9509cdaf 100644 --- a/src/video.ts +++ b/src/video.ts @@ -1,52 +1,52 @@ -import {isArrayOfNums, isInteger, isNumber, isStr, logError, logWarn} from './utils.js'; -import {config} from './config.js'; -import {hook} from './hook.js'; -import {auctionManager} from './auctionManager.js'; -import type {VideoBid} from "./bidfactory.ts"; -import {ADPOD, type BaseMediaType} from "./mediaTypes.ts"; -import type {ORTBImp} from "./types/ortb/request.d.ts"; -import type {Size} from "./types/common.d.ts"; -import type {AdUnitDefinition} from "./adUnits.ts"; +import { isArrayOfNums, isInteger, isNumber, isStr, logError, logWarn } from './utils.js'; +import { config } from './config.js'; +import { hook } from './hook.js'; +import { auctionManager } from './auctionManager.js'; +import type { VideoBid } from "./bidfactory.ts"; +import { ADPOD, type BaseMediaType } from "./mediaTypes.ts"; +import type { ORTBImp } from "./types/ortb/request.d.ts"; +import type { Size } from "./types/common.d.ts"; +import type { AdUnitDefinition } from "./adUnits.ts"; -import {getGlobalVarName} from "./buildOptions.ts"; +import { getGlobalVarName } from "./buildOptions.ts"; export const OUTSTREAM = 'outstream'; export const INSTREAM = 'instream'; const ORTB_PARAMS = [ - [ 'mimes', value => Array.isArray(value) && value.length > 0 && value.every(v => typeof v === 'string') ], - [ 'minduration', isInteger ], - [ 'maxduration', isInteger ], - [ 'startdelay', isInteger ], - [ 'maxseq', isInteger ], - [ 'poddur', isInteger ], - [ 'protocols', isArrayOfNums ], - [ 'w', isInteger ], - [ 'h', isInteger ], - [ 'podid', isStr ], - [ 'podseq', isInteger ], - [ 'rqddurs', isArrayOfNums ], - [ 'placement', isInteger ], // deprecated, see plcmt - [ 'plcmt', isInteger ], - [ 'linearity', isInteger ], - [ 'skip', value => [1, 0].includes(value) ], - [ 'skipmin', isInteger ], - [ 'skipafter', isInteger ], - [ 'sequence', isInteger ], // deprecated - [ 'slotinpod', isInteger ], - [ 'mincpmpersec', isNumber ], - [ 'battr', isArrayOfNums ], - [ 'maxextended', isInteger ], - [ 'minbitrate', isInteger ], - [ 'maxbitrate', isInteger ], - [ 'boxingallowed', isInteger ], - [ 'playbackmethod', isArrayOfNums ], - [ 'playbackend', isInteger ], - [ 'delivery', isArrayOfNums ], - [ 'pos', isInteger ], - [ 'api', isArrayOfNums ], - [ 'companiontype', isArrayOfNums ], - [ 'poddedupe', isArrayOfNums ] + ['mimes', value => Array.isArray(value) && value.length > 0 && value.every(v => typeof v === 'string')], + ['minduration', isInteger], + ['maxduration', isInteger], + ['startdelay', isInteger], + ['maxseq', isInteger], + ['poddur', isInteger], + ['protocols', isArrayOfNums], + ['w', isInteger], + ['h', isInteger], + ['podid', isStr], + ['podseq', isInteger], + ['rqddurs', isArrayOfNums], + ['placement', isInteger], // deprecated, see plcmt + ['plcmt', isInteger], + ['linearity', isInteger], + ['skip', value => [1, 0].includes(value)], + ['skipmin', isInteger], + ['skipafter', isInteger], + ['sequence', isInteger], // deprecated + ['slotinpod', isInteger], + ['mincpmpersec', isNumber], + ['battr', isArrayOfNums], + ['maxextended', isInteger], + ['minbitrate', isInteger], + ['maxbitrate', isInteger], + ['boxingallowed', isInteger], + ['playbackmethod', isArrayOfNums], + ['playbackend', isInteger], + ['delivery', isArrayOfNums], + ['pos', isInteger], + ['api', isArrayOfNums], + ['companiontype', isArrayOfNums], + ['poddedupe', isArrayOfNums] ] as const; /** @@ -104,7 +104,7 @@ export function fillVideoDefaults(adUnit: AdUnitDefinition) { /** * Validate that the assets required for video context are present on the bid */ -export function isValidVideoBid(bid: VideoBid, {index = auctionManager.index} = {}): boolean { +export function isValidVideoBid(bid: VideoBid, { index = auctionManager.index } = {}): boolean { const videoMediaType = index.getMediaTypes(bid)?.video; const context = videoMediaType && videoMediaType?.context; const useCacheKey = videoMediaType && videoMediaType?.useCacheKey; @@ -131,8 +131,12 @@ declare module './hook' { export const checkVideoBidSetup = hook('sync', function(bid: VideoBid, adUnit, videoMediaType, context, useCacheKey) { if (videoMediaType && (useCacheKey || context !== OUTSTREAM)) { // xml-only video bids require a prebid cache url - const { url, useLocal } = config.getConfig('cache') || {}; + const { url, useLocal, allowVastXmlOnly } = config.getConfig('cache') || {}; if ((!url && !useLocal) && bid.vastXml && !bid.vastUrl) { + if (allowVastXmlOnly === true) { + logWarn(`This bid contains only vastXml, and caching is disabled. Proceeding because cache.allowVastXmlOnly is enabled.`); + return true; + } logError(` This bid contains only vastXml and will not work when a prebid cache url is not specified. Try enabling either prebid cache with ${getGlobalVarName()}.setConfig({ cache: {url: "..."} }); diff --git a/src/videoCache.ts b/src/videoCache.ts index b63baf7dd43..9ca2ee84ddb 100644 --- a/src/videoCache.ts +++ b/src/videoCache.ts @@ -9,12 +9,12 @@ * This trickery helps integrate with ad servers, which set character limits on request params. */ -import {ajaxBuilder} from './ajax.js'; -import {config} from './config.js'; -import {auctionManager} from './auctionManager.js'; -import {generateUUID, logError, logWarn} from './utils.js'; -import {addBidToAuction} from './auction.js'; -import type {VideoBid} from "./bidfactory.ts"; +import { ajaxBuilder } from './ajax.js'; +import { config } from './config.js'; +import { auctionManager } from './auctionManager.js'; +import { generateUUID, logError, logWarn } from './utils.js'; +import { addBidToAuction } from './auction.js'; +import type { VideoBid } from "./bidfactory.ts"; /** * Might be useful to be configurable in the future @@ -78,6 +78,10 @@ export interface CacheConfig { * Flag determining whether to locally save VAST XML as a blob */ useLocal?: boolean; + /** + * When true, allows VAST XML-only bids to pass even without cache.url or cache.useLocal. + */ + allowVastXmlOnly?: boolean; /** * Timeout (in milliseconds) for network requests to the cache */ @@ -116,7 +120,7 @@ declare module './config' { * * @return {Object|null} - The payload to be sent to the prebid-server endpoints, or null if the bid can't be converted cleanly. */ -function toStorageRequest(bid, {index = auctionManager.index} = {}) { +function toStorageRequest(bid, { index = auctionManager.index } = {}) { const vastValue = getVastXml(bid); const auction = index.getAuction(bid); const ttlWithBuffer = Number(bid.ttl) + ttlBufferInSeconds; @@ -243,7 +247,7 @@ export function storeBatch(batch) { logError(`expected ${batch.length} cache IDs, got ${cacheIds.length} instead`) } else { cacheIds.forEach((cacheId, i) => { - const {auctionInstance, bidResponse, afterBidAdded} = batch[i]; + const { auctionInstance, bidResponse, afterBidAdded } = batch[i]; if (cacheId.uuid === '') { logWarn(`Supplied video cache key was already in use by Prebid Cache; caching attempt was rejected. Video bid must be discarded.`); } else { @@ -258,7 +262,7 @@ export function storeBatch(batch) { let batchSize, batchTimeout, cleanupHandler; if (FEATURES.VIDEO || FEATURES.AUDIO) { - config.getConfig('cache', ({cache}) => { + config.getConfig('cache', ({ cache }) => { batchSize = typeof cache.batchSize === 'number' && cache.batchSize > 0 ? cache.batchSize : 1; @@ -293,7 +297,7 @@ export const batchingCache = (timeout = setTimeout, cache = storeBatch) => { batches.push([]); } - batches[batches.length - 1].push({auctionInstance, bidResponse, afterBidAdded}); + batches[batches.length - 1].push({ auctionInstance, bidResponse, afterBidAdded }); if (!debouncing) { debouncing = true; diff --git a/test/build-logic/disclosure_spec.mjs b/test/build-logic/disclosure_spec.mjs index a6d7d38a5f3..a2e736325b9 100644 --- a/test/build-logic/disclosure_spec.mjs +++ b/test/build-logic/disclosure_spec.mjs @@ -1,6 +1,6 @@ -import {describe, it} from 'mocha'; -import {expect} from 'chai'; -import {getDisclosureUrl} from '../../metadata/storageDisclosure.mjs'; +import { describe, it } from 'mocha'; +import { expect } from 'chai'; +import { getDisclosureUrl } from '../../metadata/storageDisclosure.mjs'; describe('getDisclosureUrl', () => { let gvl; diff --git a/test/build-logic/gvl_spec.mjs b/test/build-logic/gvl_spec.mjs index 95085e68976..666dd3f2513 100644 --- a/test/build-logic/gvl_spec.mjs +++ b/test/build-logic/gvl_spec.mjs @@ -1,6 +1,6 @@ -import {expect} from 'chai'; -import {describe, it} from 'mocha'; -import {isValidGvlId} from '../../metadata/gvl.mjs'; +import { expect } from 'chai'; +import { describe, it } from 'mocha'; +import { isValidGvlId } from '../../metadata/gvl.mjs'; describe('gvl build time checks', () => { let gvl; diff --git a/test/build-logic/no_3384_spec.mjs b/test/build-logic/no_3384_spec.mjs new file mode 100644 index 00000000000..f5e726acf85 --- /dev/null +++ b/test/build-logic/no_3384_spec.mjs @@ -0,0 +1,47 @@ +import { describe, it } from 'mocha'; +import { expect } from 'chai'; +import { execFileSync } from 'node:child_process'; +import { fileURLToPath } from 'node:url'; +import path from 'node:path'; + +const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..'); + +describe('build hygiene checks', () => { + it('should not contain the forbidden legacy token in LocID files', () => { + const forbiddenToken = ['33', '84'].join(''); + const scopeArgs = [ + 'ls-files', + '--', + ':(glob)modules/**/locId*', + ':(glob)test/spec/**/locId*', + 'docs/modules/locid.md', + 'modules/locIdSystem.md' + ]; + const scopedPaths = execFileSync('git', scopeArgs, { cwd: repoRoot, encoding: 'utf8' }) + .split('\n') + .map(filePath => filePath.trim()) + .filter(Boolean); + + expect(scopedPaths.length, 'No LocID files were selected for the 3384 guard').to.be.greaterThan(0); + + const args = [ + 'grep', + '-n', + '-I', + '-E', + `\\b${forbiddenToken}\\b`, + '--', + ...scopedPaths + ]; + + try { + const output = execFileSync('git', args, { cwd: repoRoot, encoding: 'utf8' }); + expect(output.trim(), `Unexpected ${forbiddenToken} matches:\n${output}`).to.equal(''); + } catch (e) { + if (e?.status === 1) { + return; + } + throw e; + } + }); +}); diff --git a/test/fixtures/fixtures.js b/test/fixtures/fixtures.js index ccf40aee9ab..1722bdb9a14 100644 --- a/test/fixtures/fixtures.js +++ b/test/fixtures/fixtures.js @@ -1,6 +1,6 @@ // jscs:disable import { TARGETING_KEYS } from 'src/constants.js'; -import {createBid} from '../../src/bidfactory.js'; +import { createBid } from '../../src/bidfactory.js'; const utils = require('src/utils.js'); function convertTargetingsFromOldToNew(targetings) { @@ -1289,7 +1289,7 @@ export function getCurrencyRates() { }; } -export function createBidReceived({bidder, cpm, auctionId, responseTimestamp, adUnitCode, adId, status, ttl, requestId, mediaType}) { +export function createBidReceived({ bidder, cpm, auctionId, responseTimestamp, adUnitCode, adId, status, ttl, requestId, mediaType }) { const bid = { 'bidderCode': bidder, 'width': '300', diff --git a/test/helpers/analytics.js b/test/helpers/analytics.js index d36bcf44f64..a2d06d30ac1 100644 --- a/test/helpers/analytics.js +++ b/test/helpers/analytics.js @@ -9,7 +9,7 @@ export function fireEvents(events = [ EVENTS.BID_WON ]) { return events.map((ev, i) => { - ev = Array.isArray(ev) ? ev : [ev, {i: i}]; + ev = Array.isArray(ev) ? ev : [ev, { i: i }]; pbEvents.emit.apply(null, ev) return ev; }); @@ -21,7 +21,7 @@ export function expectEvents(events) { to: { beTrackedBy(trackFn) { events.forEach(([eventType, args]) => { - sinon.assert.calledWithMatch(trackFn, sinon.match({eventType, args})); + sinon.assert.calledWithMatch(trackFn, sinon.match({ eventType, args })); }); }, beBundledTo(bundleFn) { diff --git a/test/helpers/consentData.js b/test/helpers/consentData.js index 3ebb4506704..ec4c7654b3d 100644 --- a/test/helpers/consentData.js +++ b/test/helpers/consentData.js @@ -1,5 +1,5 @@ -import {gdprDataHandler, gppDataHandler} from 'src/adapterManager.js'; -import {PbPromise} from '../../src/utils/promise.js'; +import { gdprDataHandler, gppDataHandler } from 'src/adapterManager.js'; +import { PbPromise } from '../../src/utils/promise.js'; export function mockGdprConsent(sandbox, getConsentData = () => null) { sandbox.stub(gdprDataHandler, 'enabled').get(() => true) diff --git a/test/helpers/fpd.js b/test/helpers/fpd.js index a7afecbd28e..c9f7b8c5484 100644 --- a/test/helpers/fpd.js +++ b/test/helpers/fpd.js @@ -1,7 +1,7 @@ -import {dep, enrichFPD} from 'src/fpd/enrichment.js'; -import {PbPromise} from '../../src/utils/promise.js'; -import {deepClone} from '../../src/utils.js'; -import {gdprDataHandler, uspDataHandler} from '../../src/adapterManager.js'; +import { dep, enrichFPD } from 'src/fpd/enrichment.js'; +import { PbPromise } from '../../src/utils/promise.js'; +import { deepClone } from '../../src/utils.js'; +import { gdprDataHandler, uspDataHandler } from '../../src/adapterManager.js'; export function mockFpdEnrichments(sandbox, overrides = {}) { overrides = Object.assign({}, { diff --git a/test/helpers/global_hooks.js b/test/helpers/global_hooks.js index 3df3d27d032..2458bbb96e0 100644 --- a/test/helpers/global_hooks.js +++ b/test/helpers/global_hooks.js @@ -1,4 +1,4 @@ -import {clearEvents} from '../../src/events.js'; +import { clearEvents } from '../../src/events.js'; window.describe = window.context = ((orig) => { let level = 0; diff --git a/test/helpers/hookSetup.js b/test/helpers/hookSetup.js index 2de35bb1dd4..3df2a150fcb 100644 --- a/test/helpers/hookSetup.js +++ b/test/helpers/hookSetup.js @@ -1,4 +1,4 @@ -import {hook} from '../../src/hook.js'; +import { hook } from '../../src/hook.js'; before(() => { hook.ready(); diff --git a/test/helpers/indexStub.js b/test/helpers/indexStub.js index 5202106c9cf..5b81a817003 100644 --- a/test/helpers/indexStub.js +++ b/test/helpers/indexStub.js @@ -1,6 +1,6 @@ -import {AuctionIndex} from '../../src/auctionIndex.js'; +import { AuctionIndex } from '../../src/auctionIndex.js'; -export function stubAuctionIndex({bidRequests, bidderRequests, adUnits, auctionId = 'mock-auction'}) { +export function stubAuctionIndex({ bidRequests, bidderRequests, adUnits, auctionId = 'mock-auction' }) { if (adUnits == null) { adUnits = [] } diff --git a/test/helpers/index_adapter_utils.js b/test/helpers/index_adapter_utils.js index 6c2391e524a..66396cf38a5 100644 --- a/test/helpers/index_adapter_utils.js +++ b/test/helpers/index_adapter_utils.js @@ -221,7 +221,7 @@ exports.matchBidsOnSize = function(lhs, rhs) { } var lstore = createObjectFromArray(configured); - var rstore = createObjectFromArray(rhs.map(bid => [ bid.banner.w + 'x' + bid.banner.h, bid ])); + var rstore = createObjectFromArray(rhs.map(bid => [bid.banner.w + 'x' + bid.banner.h, bid])); var compared = compareOnKeys(lstore, rstore); var matched = compared.intersection.map(function(pair) { return { configured: pair.left, sent: pair.right, name: pair.name } }); @@ -253,7 +253,7 @@ exports.getBidResponse = function(configuredBids, urlJSON, optionalPriceLevel, o if (typeof optionalPassOnBid[i] !== 'undefined' && typeof optionalPassOnBid[i][j] !== 'undefined' && optionalPassOnBid[i][j]) continue; var bid = {}; - bid.adomain = [ (DefaultAdDoman + adCount).toString() ]; + bid.adomain = [(DefaultAdDoman + adCount).toString()]; bid.adid = (DefaultCreativeID + adCount).toString(); bid.impid = adCount.toString(); bid.id = adCount.toString(); @@ -314,7 +314,7 @@ exports.getExpectedAdaptorResponse = function(configuredBids, asResponse) { } if (typeof expectedResponse[placementCode] === 'undefined') { - expectedResponse[placementCode] = [ result ]; + expectedResponse[placementCode] = [result]; } else { expectedResponse[placementCode].push(result); } diff --git a/test/helpers/pbjs-test-only.js b/test/helpers/pbjs-test-only.js index 758940750a3..87212b4e65f 100644 --- a/test/helpers/pbjs-test-only.js +++ b/test/helpers/pbjs-test-only.js @@ -1,4 +1,4 @@ -import {getGlobal} from '../../src/prebidGlobal.js'; +import { getGlobal } from '../../src/prebidGlobal.js'; export const pbjsTestOnly = { diff --git a/test/helpers/prebidGlobal.js b/test/helpers/prebidGlobal.js index e80dff2577d..5c5a9351e24 100644 --- a/test/helpers/prebidGlobal.js +++ b/test/helpers/prebidGlobal.js @@ -1,4 +1,4 @@ -import {getGlobalVarName} from '../../src/buildOptions.js'; +import { getGlobalVarName } from '../../src/buildOptions.js'; window[getGlobalVarName()] = (window[getGlobalVarName()] || {}); window[getGlobalVarName()].installedModules = (window[getGlobalVarName()].installedModules || []); diff --git a/test/helpers/refererDetectionHelper.js b/test/helpers/refererDetectionHelper.js index 855574e64dd..133d4af8dea 100644 --- a/test/helpers/refererDetectionHelper.js +++ b/test/helpers/refererDetectionHelper.js @@ -67,7 +67,7 @@ export function buildWindowTree(urls, topReferrer = null, canonicalUrl = null, a } win.top = sameOriginAsTop ? topWindow : inaccessibles[0]; - const inWin = {parent: inaccessibles[inaccessibles.length - 1], top: inaccessibles[0]}; + const inWin = { parent: inaccessibles[inaccessibles.length - 1], top: inaccessibles[0] }; if (index === 0) { inWin.top = inWin; } diff --git a/test/helpers/testing-utils.js b/test/helpers/testing-utils.js index 368acfd2b11..2bafd658889 100644 --- a/test/helpers/testing-utils.js +++ b/test/helpers/testing-utils.js @@ -1,4 +1,4 @@ -const {expect} = require('chai'); +const { expect } = require('chai'); const DEFAULT_TIMEOUT = 10000; // allow more time for BrowserStack sessions const utils = { host: (process.env.TEST_SERVER_HOST) ? process.env.TEST_SERVER_HOST : 'localhost', @@ -8,7 +8,7 @@ const utils = { }, waitForElement: async function(elementRef, time = DEFAULT_TIMEOUT) { const element = $(elementRef); - await element.waitForExist({timeout: time}); + await element.waitForExist({ timeout: time }); }, switchFrame: async function(frameRef) { const iframe = await $(frameRef); @@ -27,7 +27,7 @@ const utils = { } } }, - setupTest({url, waitFor, expectGAMCreative = null, nestedIframe = true, pause = 5000, timeout = DEFAULT_TIMEOUT, retries = 3}, name, fn) { + setupTest({ url, waitFor, expectGAMCreative = null, nestedIframe = true, pause = 5000, timeout = DEFAULT_TIMEOUT, retries = 3 }, name, fn) { describe(name, function () { this.retries(retries); before(() => utils.loadAndWaitForElement(url, waitFor, pause, timeout, retries)); diff --git a/test/mocks/analyticsStub.js b/test/mocks/analyticsStub.js index 98e0f56688f..12b7de46792 100644 --- a/test/mocks/analyticsStub.js +++ b/test/mocks/analyticsStub.js @@ -1,4 +1,4 @@ -import {_internal, setDebounceDelay} from '../../libraries/analyticsAdapter/AnalyticsAdapter.js'; +import { _internal, setDebounceDelay } from '../../libraries/analyticsAdapter/AnalyticsAdapter.js'; before(() => { // stub out analytics networking to avoid random events polluting the global xhr mock diff --git a/test/mocks/ortbConverter.js b/test/mocks/ortbConverter.js index 446fac4629a..4adad568de6 100644 --- a/test/mocks/ortbConverter.js +++ b/test/mocks/ortbConverter.js @@ -1,5 +1,5 @@ -import {defaultProcessors} from '../../libraries/ortbConverter/converter.js'; -import {pbsExtensions} from '../../libraries/pbsExtensions/pbsExtensions.js'; +import { defaultProcessors } from '../../libraries/ortbConverter/converter.js'; +import { pbsExtensions } from '../../libraries/pbsExtensions/pbsExtensions.js'; beforeEach(() => { // disable caching of default processors so that tests do not freeze a subset for other tests diff --git a/test/mocks/videoCacheStub.js b/test/mocks/videoCacheStub.js index acae5cd6a32..f60da236cd0 100644 --- a/test/mocks/videoCacheStub.js +++ b/test/mocks/videoCacheStub.js @@ -1,4 +1,4 @@ -import {_internal as videoCache} from 'src/videoCache.js'; +import { _internal as videoCache } from 'src/videoCache.js'; /** * Function which can be called from unit tests to stub out the video cache. diff --git a/test/mocks/xhr.js b/test/mocks/xhr.js index de77fbc0b91..87678f72439 100644 --- a/test/mocks/xhr.js +++ b/test/mocks/xhr.js @@ -1,7 +1,7 @@ -import {getUniqueIdentifierStr} from '../../src/utils.js'; -import {GreedyPromise} from 'libraries/greedy/greedyPromise.js'; -import {fakeXhr} from 'nise'; -import {dep} from 'src/ajax.js'; +import { getUniqueIdentifierStr } from '../../src/utils.js'; +import { GreedyPromise } from 'libraries/greedy/greedyPromise.js'; +import { fakeXhr } from 'nise'; +import { dep } from 'src/ajax.js'; export const xhr = fakeXhr.useFakeXMLHttpRequest(); export const server = mockFetchServer(); @@ -13,7 +13,7 @@ function mockFetchServer() { const sandbox = sinon.createSandbox(); const bodies = new WeakMap(); const requests = []; - const {DONE, UNSENT} = XMLHttpRequest; + const { DONE, UNSENT } = XMLHttpRequest; function makeRequest(resource, options) { const requestBody = options?.body || bodies.get(resource); diff --git a/test/spec/AnalyticsAdapter_spec.js b/test/spec/AnalyticsAdapter_spec.js index 1e92d267838..f6c65d78dd6 100644 --- a/test/spec/AnalyticsAdapter_spec.js +++ b/test/spec/AnalyticsAdapter_spec.js @@ -1,15 +1,15 @@ -import {expect} from 'chai'; +import { expect } from 'chai'; import * as events from 'src/events.js'; import { EVENTS } from 'src/constants.js'; -import {server} from 'test/mocks/xhr.js'; -import {disableAjaxForAnalytics, enableAjaxForAnalytics} from '../mocks/analyticsStub.js'; -import {clearEvents} from 'src/events.js'; +import { server } from 'test/mocks/xhr.js'; +import { disableAjaxForAnalytics, enableAjaxForAnalytics } from '../mocks/analyticsStub.js'; +import { clearEvents } from 'src/events.js'; import { DEFAULT_EXCLUDE_EVENTS, DEFAULT_INCLUDE_EVENTS, setDebounceDelay } from '../../libraries/analyticsAdapter/AnalyticsAdapter.js'; -import {config} from 'src/config.js'; +import { config } from 'src/config.js'; const BID_WON = EVENTS.BID_WON; const NO_BID = EVENTS.NO_BID; @@ -48,17 +48,17 @@ FEATURE: Analytics Adapters API it(`SHOULD call the endpoint WHEN an event occurs that is to be tracked`, function () { const eventType = BID_WON; - const args = {some: 'data'}; + const args = { some: 'data' }; - adapter.track({eventType, args}); + adapter.track({ eventType, args }); const result = JSON.parse(server.requests[0].requestBody); - sinon.assert.match(result, {args: {some: 'data'}, eventType}) + sinon.assert.match(result, { args: { some: 'data' }, eventType }) }); it(`SHOULD queue the event first and then track it WHEN an event occurs before tracking library is available`, function () { const eventType = BID_WON; - const args = {wat: 'wot'}; + const args = { wat: 'wot' }; events.emit(eventType, args); adapter.enableAnalytics(); @@ -66,7 +66,7 @@ FEATURE: Analytics Adapters API // As now AUCTION_DEBUG is triggered for WARNINGS too, the BID_RESPONSE goes last in the array const index = server.requests.length - 1; const result = JSON.parse(server.requests[index].requestBody); - sinon.assert.match(result, {eventType, args: {wat: 'wot'}}) + sinon.assert.match(result, { eventType, args: { wat: 'wot' } }) }); describe('analyticsLabels', () => { @@ -82,11 +82,11 @@ FEATURE: Analytics Adapters API }) it('should be attached to payloads (type: endpoint)', () => { - events.emit(BID_WON, {foo: 'bar'}); + events.emit(BID_WON, { foo: 'bar' }); adapter.enableAnalytics(); server.requests .map(req => JSON.parse(req.requestBody)) - .forEach(payload => sinon.assert.match(payload, {labels: analyticsLabels, args: sinon.match({analyticsLabels})})) + .forEach(payload => sinon.assert.match(payload, { labels: analyticsLabels, args: sinon.match({ analyticsLabels }) })) }); it('should be attached payloads (type: bundle)', () => { @@ -96,9 +96,9 @@ FEATURE: Analytics Adapters API }) window.analytics = sinon.stub(); try { - events.emit(BID_WON, {foo: 'bar'}) + events.emit(BID_WON, { foo: 'bar' }) adapter.enableAnalytics(); - sinon.assert.calledWith(window.analytics, sinon.match.any, BID_WON, sinon.match({analyticsLabels})) + sinon.assert.calledWith(window.analytics, sinon.match.any, BID_WON, sinon.match({ analyticsLabels })) } finally { delete window.analytics; } @@ -108,32 +108,32 @@ FEATURE: Analytics Adapters API Object.assign(adapter, { track: sinon.stub() }); - events.emit(BID_WON, {foo: 'bar'}); + events.emit(BID_WON, { foo: 'bar' }); adapter.enableAnalytics(); sinon.assert.calledWith(adapter.track, sinon.match({ eventType: BID_WON, - args: sinon.match({analyticsLabels}), + args: sinon.match({ analyticsLabels }), labels: analyticsLabels })) }) it('should not override the "analyticsLabels" property an event payload may have', () => { adapter.track = sinon.stub(); - events.emit(BID_WON, {analyticsLabels: 'not these ones'}); + events.emit(BID_WON, { analyticsLabels: 'not these ones' }); adapter.enableAnalytics(); sinon.assert.calledWith(adapter.track, sinon.match({ - args: {analyticsLabels: 'not these ones'} + args: { analyticsLabels: 'not these ones' } })); }); it('should not modify event payloads when there are no labels', () => { config.resetConfig(); adapter.track = sinon.stub(); - events.emit(BID_WON, {'foo': 'bar'}); + events.emit(BID_WON, { 'foo': 'bar' }); adapter.enableAnalytics(); sinon.assert.calledWith(adapter.track, { labels: {}, - args: {foo: 'bar'}, + args: { foo: 'bar' }, eventType: BID_WON }) }) @@ -199,7 +199,7 @@ FEATURE: Analytics Adapters API Object.values(DEFAULT_INCLUDE_EVENTS).forEach(eventType => { it(`SHOULD call global when a ${eventType} event occurs`, () => { - const args = {more: 'info'}; + const args = { more: 'info' }; adapter.enableAnalytics(); events.emit(eventType, args); @@ -216,7 +216,7 @@ FEATURE: Analytics Adapters API it('SHOULD NOT call global again when adapter.enableAnalytics is called with previous timeout', function () { const eventType = BID_WON; - const args = {call: 'timeout'}; + const args = { call: 'timeout' }; events.emit(eventType, args); adapter.enableAnalytics(); @@ -227,7 +227,7 @@ FEATURE: Analytics Adapters API describe(`AND sampling is enabled\n`, function () { const eventType = BID_WON; - const args = {more: 'info'}; + const args = { more: 'info' }; beforeEach(function () { sinon.stub(Math, 'random').returns(0.5); @@ -247,7 +247,7 @@ FEATURE: Analytics Adapters API expect(server.requests.length).to.equal(1); const result = JSON.parse(server.requests[0].requestBody); - sinon.assert.match(result, {args: {more: 'info'}, eventType: 'bidWon'}) + sinon.assert.match(result, { args: { more: 'info' }, eventType: 'bidWon' }) }); it(`THEN should disable analytics when random number is outside sample range`, function () { @@ -286,16 +286,16 @@ describe('Analytics asynchronous event tracking', () => { }) it('does not call track as long as events are coming', () => { - events.emit(BID_WON, {i: 0}); + events.emit(BID_WON, { i: 0 }); sinon.assert.notCalled(adapter.track); clock.tick(10); - events.emit(BID_WON, {i: 1}); + events.emit(BID_WON, { i: 1 }); sinon.assert.notCalled(adapter.track); clock.tick(10); sinon.assert.notCalled(adapter.track); clock.tick(100); sinon.assert.calledTwice(adapter.track); - sinon.assert.calledWith(adapter.track.firstCall, sinon.match({eventType: BID_WON, args: {i: 0}})); - sinon.assert.calledWith(adapter.track.secondCall, sinon.match({eventType: BID_WON, args: {i: 1}})); + sinon.assert.calledWith(adapter.track.firstCall, sinon.match({ eventType: BID_WON, args: { i: 0 } })); + sinon.assert.calledWith(adapter.track.secondCall, sinon.match({ eventType: BID_WON, args: { i: 1 } })); }); }) diff --git a/test/spec/activities/allowActivites_spec.js b/test/spec/activities/allowActivites_spec.js index cc1c83ec4c9..5e06d46330f 100644 --- a/test/spec/activities/allowActivites_spec.js +++ b/test/spec/activities/allowActivites_spec.js @@ -1,7 +1,7 @@ -import {config} from 'src/config.js'; -import {ruleRegistry} from '../../../src/activities/rules.js'; -import {updateRulesFromConfig} from '../../../modules/allowActivities.js'; -import {activityParams} from '../../../src/activities/activityParams.js'; +import { config } from 'src/config.js'; +import { ruleRegistry } from '../../../src/activities/rules.js'; +import { updateRulesFromConfig } from '../../../modules/allowActivities.js'; +import { activityParams } from '../../../src/activities/activityParams.js'; describe('allowActivities config', () => { const MODULE_TYPE = 'test' @@ -41,7 +41,7 @@ describe('allowActivities config', () => { default: false, rules: [ { - condition({componentName}) { + condition({ componentName }) { return componentName === MODULE_NAME }, allow: true @@ -63,7 +63,7 @@ describe('allowActivities config', () => { it('are tested for their condition', () => { setupActivityConfig({ rules: [{ - condition({flag}) { return flag }, + condition({ flag }) { return flag }, allow: false }] }); @@ -74,7 +74,7 @@ describe('allowActivities config', () => { it('always apply if they have no condition', () => { setupActivityConfig({ - rules: [{allow: false}] + rules: [{ allow: false }] }); expect(isAllowed(ACTIVITY, params)).to.be.false; }); @@ -94,7 +94,7 @@ describe('allowActivities config', () => { it('does not pass private (underscored) parameters to condition', () => { setupActivityConfig({ rules: [{ - condition({_priv}) { return _priv }, + condition({ _priv }) { return _priv }, allow: false }] }); @@ -131,7 +131,7 @@ describe('allowActivities config', () => { setupActivityConfig({ allow: false }); - config.setConfig({allowActivities: {}}); + config.setConfig({ allowActivities: {} }); expect(isAllowed(ACTIVITY, params)).to.be.true; }); }); diff --git a/test/spec/activities/objectGuard_spec.js b/test/spec/activities/objectGuard_spec.js index 3f1474b0c46..7c680d82289 100644 --- a/test/spec/activities/objectGuard_spec.js +++ b/test/spec/activities/objectGuard_spec.js @@ -1,6 +1,6 @@ -import {objectGuard, writeProtectRule} from '../../../libraries/objectGuard/objectGuard.js'; -import {redactRule} from '../../../src/activities/redactor.js'; -import {mergeDeep} from 'src/utils.js'; +import { objectGuard, writeProtectRule } from '../../../libraries/objectGuard/objectGuard.js'; +import { redactRule } from '../../../src/activities/redactor.js'; +import { mergeDeep } from 'src/utils.js'; describe('objectGuard', () => { describe('read rule', () => { @@ -11,23 +11,25 @@ describe('objectGuard', () => { paths: ['foo', 'outer.inner.foo'], name: 'testRule', applies: sinon.stub().callsFake(() => applies), - get(val) { return `repl${val}` }, - } - }) + get(val) { + return `repl${val}`; + }, + }; + }); it('should reject conflicting rules', () => { - const crule = {...rule, paths: ['outer']}; + const crule = { ...rule, paths: ['outer'] }; expect(() => objectGuard([rule, crule])).to.throw(); expect(() => objectGuard([crule, rule])).to.throw(); }); it('should preserve object identity', () => { - const guard = objectGuard([rule])({outer: {inner: {foo: 'bar'}}}); + const guard = objectGuard([rule])({ outer: { inner: { foo: 'bar' } } }); expect(guard.outer).to.equal(guard.outer); expect(guard.outer.inner).to.equal(guard.outer.inner); - }) + }); it('can prevent top level read access', () => { - const obj = objectGuard([rule])({'foo': 1, 'other': 2}); + const obj = objectGuard([rule])({ 'foo': 1, 'other': 2 }); expect(obj).to.eql({ foo: 'repl1', other: 2 @@ -40,19 +42,19 @@ describe('objectGuard', () => { }); it('allows concurrent reads', () => { - const obj = {'foo': 'bar'}; + const obj = { 'foo': 'bar' }; const guarded = objectGuard([rule])(obj); obj.foo = 'baz'; expect(guarded.foo).to.eql('replbaz'); - }) + }); it('does not prevent access if applies returns false', () => { applies = false; - const obj = objectGuard([rule])({foo: 1}); + const obj = objectGuard([rule])({ foo: 1 }); expect(obj).to.eql({ foo: 1 }); - }) + }); it('can prevent nested property access', () => { const obj = objectGuard([rule])({ @@ -78,13 +80,13 @@ describe('objectGuard', () => { foo: 3 } } - }) + }); }); it('prevents nested property access when a parent property is protected', () => { - const guard = objectGuard([rule])({foo: {inner: 'value'}}); + const guard = objectGuard([rule])({ foo: { inner: 'value' } }); expect(guard.inner?.value).to.not.exist; - }) + }); it('does not call applies more than once', () => { JSON.stringify(objectGuard([rule])({ @@ -96,7 +98,7 @@ describe('objectGuard', () => { } })); expect(rule.applies.callCount).to.equal(1); - }) + }); }); describe('write protection', () => { @@ -114,70 +116,119 @@ describe('objectGuard', () => { applies = false; const obj = {}; const guard = objectGuard([rule])(obj); - mergeDeep(guard, {foo: {nested: 'item'}}); - expect(obj.foo).to.eql({nested: 'item'}); + mergeDeep(guard, { foo: { nested: 'item' } }); + expect(obj.foo).to.eql({ nested: 'item' }); + }); + + it('should handle circular references in guarded properties', () => { + applies = false; + const obj = { + foo: {} + }; + const guard = objectGuard([rule])(obj); + guard.foo.inner = guard.foo; + expect(guard).to.eql({ + foo: { + inner: guard.foo + } + }); + }); + + it('should handle circular references in unguarded properties', () => { + const obj = {}; + const guard = objectGuard([rule])(obj); + const val = {}; + val.circular = val; + guard.prop = val; + expect(guard).to.eql({ + prop: val + }) }); + it('should allow for deferred modification', () => { + const obj = {}; + const guard = objectGuard([rule])(obj); + const prop = {}; + guard.prop = prop; + prop.val = 'foo'; + expect(obj).to.eql({ + prop: { + val: 'foo' + } + }); + }); + + it('should not choke on immutable objects', () => { + const obj = {}; + const guard = objectGuard([rule])(obj); + guard.prop = Object.freeze({ val: 'foo' }); + expect(obj).to.eql({ + prop: { + val: 'foo' + } + }) + }) + it('should reject conflicting rules', () => { - const crule = {...rule, paths: ['outer']}; + const crule = { ...rule, paths: ['outer'] }; expect(() => objectGuard([rule, crule])).to.throw(); expect(() => objectGuard([crule, rule])).to.throw(); }); it('should preserve object identity', () => { - const guard = objectGuard([rule])({outer: {inner: {foo: 'bar'}}}); + const guard = objectGuard([rule])({ outer: { inner: { foo: 'bar' } } }); expect(guard.outer).to.equal(guard.outer); expect(guard.outer.inner).to.equal(guard.outer.inner); - }) + }); it('does not mess up array reads', () => { - const guard = objectGuard([rule])({foo: [{bar: 'baz'}]}); - expect(guard.foo).to.eql([{bar: 'baz'}]); - }) + const guard = objectGuard([rule])({ foo: [{ bar: 'baz' }] }); + expect(guard.foo).to.eql([{ bar: 'baz' }]); + }); it('prevents array modification', () => { - const obj = {foo: ['value']}; + const obj = { foo: ['value'] }; const guard = objectGuard([rule])(obj); guard.foo.pop(); guard.foo.push('test'); expect(obj.foo).to.eql(['value']); - }) + }); it('allows array modification when not applicable', () => { applies = false; - const obj = {foo: ['value']}; + const obj = { foo: ['value'] }; const guard = objectGuard([rule])(obj); guard.foo.pop(); guard.foo.push('test'); expect(obj.foo).to.eql(['test']); - }) + }); it('should prevent top-level writes', () => { - const obj = {bar: {nested: 'val'}, other: 'val'}; + const obj = { bar: { nested: 'val' }, other: 'val' }; const guard = objectGuard([rule])(obj); guard.foo = 'denied'; guard.bar.nested = 'denied'; guard.bar.other = 'denied'; guard.other = 'allowed'; - expect(guard).to.eql({bar: {nested: 'val'}, other: 'allowed'}); + expect(guard).to.eql({ bar: { nested: 'val' }, other: 'allowed' }); }); it('should not prevent no-op writes', () => { - const guard = objectGuard([rule])({foo: {some: 'value'}}); - guard.foo = {some: 'value'}; + const guard = objectGuard([rule])({ foo: { some: 'value' } }); + guard.foo = { some: 'value' }; sinon.assert.notCalled(rule.applies); - }) + }); it('should prevent top-level deletes', () => { - const obj = {foo: {nested: 'val'}, bar: 'val'}; + const obj = { foo: { nested: 'val' }, bar: 'val' }; const guard = objectGuard([rule])(obj); delete guard.foo.nested; delete guard.bar; - expect(guard).to.eql({foo: {nested: 'val'}, bar: 'val'}); - }) + expect(guard).to.eql({ foo: { nested: 'val' }, bar: 'val' }); + }); it('should prevent nested writes', () => { - const obj = {outer: {inner: {bar: {nested: 'val'}, other: 'val'}}}; + const obj = { outer: { inner: { bar: { nested: 'val' }, other: 'val' } } }; const guard = objectGuard([rule])(obj); guard.outer.inner.bar.other = 'denied'; guard.outer.inner.bar.nested = 'denied'; @@ -192,71 +243,71 @@ describe('objectGuard', () => { other: 'allowed' } } - }) + }); }); it('should prevent writes if upper levels are protected', () => { - const obj = {foo: {inner: {}}}; + const obj = { foo: { inner: {} } }; const guard = objectGuard([rule])(obj); guard.foo.inner.prop = 'value'; - expect(obj).to.eql({foo: {inner: {}}}); - }) + expect(obj).to.eql({ foo: { inner: {} } }); + }); it('should prevent deletes if a higher level property is protected', () => { - const obj = {foo: {inner: {prop: 'value'}}}; + const obj = { foo: { inner: { prop: 'value' } } }; const guard = objectGuard([rule])(obj); delete guard.foo.inner.prop; - expect(obj).to.eql({foo: {inner: {prop: 'value'}}}); - }) + expect(obj).to.eql({ foo: { inner: { prop: 'value' } } }); + }); it('should clean up top-level writes that would result in inner properties changing', () => { - const guard = objectGuard([rule])({outer: {inner: {bar: 'baz'}}}); - guard.outer = {inner: {bar: 'baz', foo: 'baz', prop: 'allowed'}}; - expect(guard).to.eql({outer: {inner: {bar: 'baz', prop: 'allowed'}}}); - }) + const guard = objectGuard([rule])({ outer: { inner: { bar: 'baz' } } }); + guard.outer = { inner: { bar: 'baz', foo: 'baz', prop: 'allowed' } }; + expect(guard).to.eql({ outer: { inner: { bar: 'baz', prop: 'allowed' } } }); + }); it('should not prevent writes that are not protected', () => { const obj = {}; const guard = objectGuard([rule])(obj); guard.outer = { test: 'value' - } + }; expect(obj.outer.test).to.eql('value'); - }) + }); it('should not choke on type mismatch: overwrite object with scalar', () => { - const obj = {outer: {inner: {}}}; + const obj = { outer: { inner: {} } }; const guard = objectGuard([rule])(obj); guard.outer = null; - expect(obj).to.eql({outer: {inner: {}}}); + expect(obj).to.eql({ outer: { inner: {} } }); }); it('should not choke on type mismatch: overwrite scalar with object', () => { - const obj = {outer: null}; + const obj = { outer: null }; const guard = objectGuard([rule])(obj); - guard.outer = {inner: {bar: 'denied', other: 'allowed'}}; - expect(obj).to.eql({outer: {inner: {other: 'allowed'}}}); - }) + guard.outer = { inner: { bar: 'denied', other: 'allowed' } }; + expect(obj).to.eql({ outer: { inner: { other: 'allowed' } } }); + }); it('should prevent nested deletes', () => { - const obj = {outer: {inner: {foo: {nested: 'val'}, bar: 'val'}}}; + const obj = { outer: { inner: { foo: { nested: 'val' }, bar: 'val' } } }; const guard = objectGuard([rule])(obj); delete guard.outer.inner.foo.nested; delete guard.outer.inner.bar; - expect(guard).to.eql({outer: {inner: {foo: {nested: 'val'}, bar: 'val'}}}) + expect(guard).to.eql({ outer: { inner: { foo: { nested: 'val' }, bar: 'val' } } }); }); it('should prevent higher level deletes that would result in inner properties changing', () => { - const guard = objectGuard([rule])({outer: {inner: {bar: 'baz'}}}); + const guard = objectGuard([rule])({ outer: { inner: { bar: 'baz' } } }); delete guard.outer.inner; - expect(guard).to.eql({outer: {inner: {bar: 'baz'}}}); - }) + expect(guard).to.eql({ outer: { inner: { bar: 'baz' } } }); + }); it('should work on null properties', () => { - const obj = {foo: null}; + const obj = { foo: null }; const guard = objectGuard([rule])(obj); guard.foo = 'denied'; - expect(guard).to.eql({foo: null}); + expect(guard).to.eql({ foo: null }); }); }); describe('multiple rules on the same path', () => { @@ -276,7 +327,7 @@ describe('objectGuard', () => { return '2' + val; } }) - ])({foo: 'bar'}); + ])({ foo: 'bar' }); expect(obj.foo).to.eql('21bar'); }); @@ -293,18 +344,18 @@ describe('objectGuard', () => { applies: () => true, }) ]; - }) + }); Object.entries({ 'simple value': 'val', - 'object value': {inner: 'val'} + 'object value': { inner: 'val' } }).forEach(([t, val]) => { it(`can apply them both (on ${t})`, () => { - const obj = objectGuard(rules)({foo: val}); + const obj = objectGuard(rules)({ foo: val }); expect(obj.foo).to.not.exist; - obj.foo = {other: 'val'}; + obj.foo = { other: 'val' }; expect(obj.foo).to.not.exist; - }) - }) - }) - }) + }); + }); + }); + }); }); diff --git a/test/spec/activities/ortbGuard_spec.js b/test/spec/activities/ortbGuard_spec.js index 1384a6c0674..ff2f961277b 100644 --- a/test/spec/activities/ortbGuard_spec.js +++ b/test/spec/activities/ortbGuard_spec.js @@ -1,14 +1,14 @@ -import {ortb2FragmentsGuardFactory, ortb2GuardFactory} from '../../../libraries/objectGuard/ortbGuard.js'; -import {ACTIVITY_PARAM_COMPONENT_NAME, ACTIVITY_PARAM_COMPONENT_TYPE} from '../../../src/activities/params.js'; +import { ortb2FragmentsGuardFactory, ortb2GuardFactory } from '../../../libraries/objectGuard/ortbGuard.js'; +import { ACTIVITY_PARAM_COMPONENT_NAME, ACTIVITY_PARAM_COMPONENT_TYPE } from '../../../src/activities/params.js'; import { ACTIVITY_ENRICH_EIDS, ACTIVITY_ENRICH_UFPD, ACTIVITY_TRANSMIT_EIDS, ACTIVITY_TRANSMIT_UFPD } from '../../../src/activities/activities.js'; -import {activityParams} from '../../../src/activities/activityParams.js'; -import {deepAccess, deepClone, deepSetValue, mergeDeep} from '../../../src/utils.js'; -import {ORTB_EIDS_PATHS, ORTB_UFPD_PATHS} from '../../../src/activities/redactor.js'; -import {objectGuard, writeProtectRule} from '../../../libraries/objectGuard/objectGuard.js'; +import { activityParams } from '../../../src/activities/activityParams.js'; +import { deepAccess, deepClone, deepSetValue, mergeDeep } from '../../../src/utils.js'; +import { ORTB_EIDS_PATHS, ORTB_UFPD_PATHS } from '../../../src/activities/redactor.js'; +import { objectGuard, writeProtectRule } from '../../../libraries/objectGuard/objectGuard.js'; describe('ortb2Guard', () => { const MOD_TYPE = 'test'; @@ -46,12 +46,12 @@ describe('ortb2Guard', () => { function testPropertiesAreProtected(properties, allowed) { properties.forEach(prop => { it(`should ${allowed ? 'keep' : 'prevent'} additions to ${prop}`, () => { - const orig = [{n: 'orig'}]; + const orig = [{ n: 'orig' }]; const ortb2 = {}; deepSetValue(ortb2, prop, deepClone(orig)); const guard = ortb2Guard(ortb2, activityParams(MOD_TYPE, MOD_NAME)); const mod = {}; - const insert = [{n: 'new'}]; + const insert = [{ n: 'new' }]; deepSetValue(mod, prop, insert); mergeDeep(guard, mod); const actual = deepAccess(ortb2, prop); @@ -63,16 +63,16 @@ describe('ortb2Guard', () => { }); it(`should ${allowed ? 'keep' : 'prevent'} modifications to ${prop}`, () => { - const orig = [{n: 'orig'}]; + const orig = [{ n: 'orig' }]; const ortb2 = {}; deepSetValue(ortb2, prop, orig); const guard = ortb2Guard(ortb2, activityParams(MOD_TYPE, MOD_NAME)); deepSetValue(guard, `${prop}.0.n`, 'new'); const actual = deepAccess(ortb2, prop); if (allowed) { - expect(actual).to.eql([{n: 'new'}]); + expect(actual).to.eql([{ n: 'new' }]); } else { - expect(actual).to.eql([{n: 'orig'}]); + expect(actual).to.eql([{ n: 'orig' }]); } }); }) @@ -103,12 +103,12 @@ describe('ortb2FragmentsGuard', () => { it('should prevent changes to global FPD', () => { const fragments = { global: { - foo: {inner: 'val'} + foo: { inner: 'val' } } } const guard = guardFragments(fragments); guard.global.foo = 'other'; - expect(fragments.global.foo).to.eql({inner: 'val'}); + expect(fragments.global.foo).to.eql({ inner: 'val' }); }); it('should prevent changes to bidder FPD', () => { @@ -121,7 +121,7 @@ describe('ortb2FragmentsGuard', () => { }; const guard = guardFragments(fragments); guard.bidder.A.foo = 'denied'; - expect(fragments.bidder.A).to.eql({foo: 'val'}); + expect(fragments.bidder.A).to.eql({ foo: 'val' }); }); it('should prevent changes to bidder FPD that was not initially there', () => { @@ -129,7 +129,7 @@ describe('ortb2FragmentsGuard', () => { bidder: {} }; const guard = guardFragments(fragments); - guard.bidder.A = {foo: 'denied', other: 'allowed'}; - expect(fragments.bidder.A).to.eql({other: 'allowed'}); + guard.bidder.A = { foo: 'denied', other: 'allowed' }; + expect(fragments.bidder.A).to.eql({ other: 'allowed' }); }); }) diff --git a/test/spec/activities/params_spec.js b/test/spec/activities/params_spec.js index d949cd41cb4..1fc00d7e578 100644 --- a/test/spec/activities/params_spec.js +++ b/test/spec/activities/params_spec.js @@ -4,12 +4,12 @@ import { ACTIVITY_PARAM_COMPONENT_TYPE } from '../../../src/activities/params.js'; import adapterManager from '../../../src/adapterManager.js'; -import {MODULE_TYPE_BIDDER} from '../../../src/activities/modules.js'; -import {activityParams} from '../../../src/activities/activityParams.js'; +import { MODULE_TYPE_BIDDER } from '../../../src/activities/modules.js'; +import { activityParams } from '../../../src/activities/activityParams.js'; describe('activityParams', () => { it('fills out component params', () => { - sinon.assert.match(activityParams('bidder', 'mockBidder', {foo: 'bar'}), { + sinon.assert.match(activityParams('bidder', 'mockBidder', { foo: 'bar' }), { [ACTIVITY_PARAM_COMPONENT]: 'bidder.mockBidder', [ACTIVITY_PARAM_COMPONENT_TYPE]: 'bidder', [ACTIVITY_PARAM_COMPONENT_NAME]: 'mockBidder', @@ -18,7 +18,7 @@ describe('activityParams', () => { }); it('fills out adapterCode', () => { - adapterManager.registerBidAdapter({callBids: sinon.stub(), getSpec: sinon.stub().returns({})}, 'mockBidder') + adapterManager.registerBidAdapter({ callBids: sinon.stub(), getSpec: sinon.stub().returns({}) }, 'mockBidder') adapterManager.aliasBidAdapter('mockBidder', 'mockAlias'); expect(activityParams(MODULE_TYPE_BIDDER, 'mockAlias')[ACTIVITY_PARAM_ADAPTER_CODE]).to.equal('mockBidder'); }); diff --git a/test/spec/activities/redactor_spec.js b/test/spec/activities/redactor_spec.js index e1d565d6d60..57ea96a6fd4 100644 --- a/test/spec/activities/redactor_spec.js +++ b/test/spec/activities/redactor_spec.js @@ -6,14 +6,14 @@ import { ORTB_UFPD_PATHS, redactorFactory, redactRule } from '../../../src/activities/redactor.js'; -import {ACTIVITY_PARAM_COMPONENT_NAME, ACTIVITY_PARAM_COMPONENT_TYPE} from '../../../src/activities/params.js'; +import { ACTIVITY_PARAM_COMPONENT_NAME, ACTIVITY_PARAM_COMPONENT_TYPE } from '../../../src/activities/params.js'; import { ACTIVITY_TRANSMIT_EIDS, ACTIVITY_TRANSMIT_PRECISE_GEO, ACTIVITY_TRANSMIT_TID, ACTIVITY_TRANSMIT_UFPD } from '../../../src/activities/activities.js'; -import {deepAccess, deepSetValue} from '../../../src/utils.js'; -import {activityParams} from '../../../src/activities/activityParams.js'; +import { deepAccess, deepSetValue } from '../../../src/utils.js'; +import { activityParams } from '../../../src/activities/activityParams.js'; describe('objectTransformer', () => { describe('using dummy rules', () => { @@ -30,7 +30,7 @@ describe('objectTransformer', () => { }); it('runs rule for each path', () => { - const obj = {foo: 'val'}; + const obj = { foo: 'val' }; objectTransformer([rule])({}, obj); sinon.assert.calledWith(run, obj, null, obj, 'foo'); sinon.assert.calledWith(run, obj, 'bar', undefined, 'baz'); @@ -56,15 +56,15 @@ describe('objectTransformer', () => { }); it('does not call apply if session already contains a result for the rule', () => { - objectTransformer([rule])({[rule.name]: false}, {}); + objectTransformer([rule])({ [rule.name]: false }, {}); expect(applies.callCount).to.equal(0); expect(run.callCount).to.equal(0); }) it('passes arguments to applies', () => { run.callsFake((_1, _2, _3, _4, applies) => applies()); - const arg1 = {n: 0}; - const arg2 = {n: 1}; + const arg1 = { n: 0 }; + const arg2 = { n: 1 }; objectTransformer([rule])({}, {}, arg1, arg2); sinon.assert.calledWith(applies, arg1, arg2); }); @@ -95,10 +95,10 @@ describe('objectTransformer', () => { expect(Object.keys(parent)).to.not.include.members([prop]); } } - }).forEach(([t, {get, expectation}]) => { + }).forEach(([t, { get, expectation }]) => { describe(`property ${t}`, () => { it('should work on top level properties', () => { - const obj = {foo: 1, bar: 2}; + const obj = { foo: 1, bar: 2 }; objectTransformer([ redactRule({ name: 'test', @@ -113,7 +113,7 @@ describe('objectTransformer', () => { expectation(obj, 'foo', get(1)); }); it('should work on nested properties', () => { - const obj = {outer: {inner: {foo: 'bar'}, baz: 0}}; + const obj = { outer: { inner: { foo: 'bar' }, baz: 0 } }; objectTransformer([ redactRule({ name: 'test', @@ -134,10 +134,10 @@ describe('objectTransformer', () => { describe('should not run rule if property is', () => { Object.entries({ 'missing': {}, - 'empty array': {foo: []}, - 'empty object': {foo: {}}, - 'null': {foo: null}, - 'undefined': {foo: undefined} + 'empty array': { foo: [] }, + 'empty object': { foo: {} }, + 'null': { foo: null }, + 'undefined': { foo: undefined } }).forEach(([t, obj]) => { it(t, () => { const get = sinon.stub(); @@ -160,14 +160,14 @@ describe('objectTransformer', () => { false: false }).forEach(([t, val]) => { it(t, () => { - const obj = {foo: val}; + const obj = { foo: val }; objectTransformer([redactRule({ name: 'test', paths: ['foo'], applies() { return true }, get(val) { return 'repl' }, })])({}, obj); - expect(obj).to.eql({foo: 'repl'}); + expect(obj).to.eql({ foo: 'repl' }); }) }) }); diff --git a/test/spec/activities/rules_spec.js b/test/spec/activities/rules_spec.js index 2acfae57980..59d16ad9c34 100644 --- a/test/spec/activities/rules_spec.js +++ b/test/spec/activities/rules_spec.js @@ -1,4 +1,4 @@ -import {ruleRegistry} from '../../../src/activities/rules.js'; +import { ruleRegistry } from '../../../src/activities/rules.js'; describe('Activity control rules', () => { const MOCK_ACTIVITY = 'mockActivity'; @@ -27,73 +27,73 @@ describe('Activity control rules', () => { }); it('denies if a rule denies', () => { - registerRule(MOCK_ACTIVITY, MOCK_RULE, () => ({allow: false})); + registerRule(MOCK_ACTIVITY, MOCK_RULE, () => ({ allow: false })); expect(isAllowed(MOCK_ACTIVITY, {})).to.be.false; }); it('partitions rules by activity', () => { - registerRule(MOCK_ACTIVITY, MOCK_RULE, () => ({allow: false})); + registerRule(MOCK_ACTIVITY, MOCK_RULE, () => ({ allow: false })); expect(isAllowed('other', {})).to.be.true; }); it('passes params to rules', () => { - registerRule(MOCK_ACTIVITY, MOCK_RULE, (params) => ({allow: params.foo !== 'bar'})); - expect(isAllowed(MOCK_ACTIVITY, {foo: 'notbar'})).to.be.true; - expect(isAllowed(MOCK_ACTIVITY, {foo: 'bar'})).to.be.false; + registerRule(MOCK_ACTIVITY, MOCK_RULE, (params) => ({ allow: params.foo !== 'bar' })); + expect(isAllowed(MOCK_ACTIVITY, { foo: 'notbar' })).to.be.true; + expect(isAllowed(MOCK_ACTIVITY, { foo: 'bar' })).to.be.false; }); it('allows if rules do not opine', () => { registerRule(MOCK_ACTIVITY, MOCK_RULE, () => null); - expect(isAllowed(MOCK_ACTIVITY, {foo: 'bar'})).to.be.true; + expect(isAllowed(MOCK_ACTIVITY, { foo: 'bar' })).to.be.true; }); it('denies if any rule denies', () => { registerRule(MOCK_ACTIVITY, MOCK_RULE, () => null); - registerRule(MOCK_ACTIVITY, MOCK_RULE, () => ({allow: true})); - registerRule(MOCK_ACTIVITY, MOCK_RULE, () => ({allow: false})); - expect(isAllowed(MOCK_ACTIVITY, {foo: 'bar'})).to.be.false; + registerRule(MOCK_ACTIVITY, MOCK_RULE, () => ({ allow: true })); + registerRule(MOCK_ACTIVITY, MOCK_RULE, () => ({ allow: false })); + expect(isAllowed(MOCK_ACTIVITY, { foo: 'bar' })).to.be.false; }); it('allows if higher priority allow rule trumps a lower priority deny rule', () => { - registerRule(MOCK_ACTIVITY, MOCK_RULE, () => ({allow: false})); - registerRule(MOCK_ACTIVITY, MOCK_RULE, () => ({allow: true}), 0); + registerRule(MOCK_ACTIVITY, MOCK_RULE, () => ({ allow: false })); + registerRule(MOCK_ACTIVITY, MOCK_RULE, () => ({ allow: true }), 0); expect(isAllowed(MOCK_ACTIVITY, {})).to.be.true; }); it('denies if a higher priority deny rule trumps a lower priority allow rule', () => { - registerRule(MOCK_ACTIVITY, MOCK_RULE, () => ({allow: true})); - registerRule(MOCK_ACTIVITY, MOCK_RULE, () => ({allow: false}), 0); + registerRule(MOCK_ACTIVITY, MOCK_RULE, () => ({ allow: true })); + registerRule(MOCK_ACTIVITY, MOCK_RULE, () => ({ allow: false }), 0); expect(isAllowed(MOCK_ACTIVITY, {})).to.be.false; }); it('can unregister rules', () => { - registerRule(MOCK_ACTIVITY, MOCK_RULE, () => ({allow: true})); - const r = registerRule(MOCK_ACTIVITY, MOCK_RULE, () => ({allow: false}), 0); + registerRule(MOCK_ACTIVITY, MOCK_RULE, () => ({ allow: true })); + const r = registerRule(MOCK_ACTIVITY, MOCK_RULE, () => ({ allow: false }), 0); expect(isAllowed(MOCK_ACTIVITY, {})).to.be.false; r(); expect(isAllowed(MOCK_ACTIVITY, {})).to.be.true; }) it('logs INFO when explicit allow is found', () => { - registerRule(MOCK_ACTIVITY, MOCK_RULE, () => ({allow: true})); + registerRule(MOCK_ACTIVITY, MOCK_RULE, () => ({ allow: true })); isAllowed(MOCK_ACTIVITY, {}); sinon.assert.calledWithMatch(logger.logInfo, new RegExp(MOCK_RULE)); }); it('logs INFO with reason if the rule provides one', () => { - registerRule(MOCK_ACTIVITY, MOCK_RULE, () => ({allow: true, reason: 'because'})); + registerRule(MOCK_ACTIVITY, MOCK_RULE, () => ({ allow: true, reason: 'because' })); isAllowed(MOCK_ACTIVITY, {}); sinon.assert.calledWithMatch(logger.logInfo, new RegExp(MOCK_RULE), /because/); }); it('logs WARN when a deny is found', () => { - registerRule(MOCK_ACTIVITY, MOCK_RULE, () => ({allow: false})); + registerRule(MOCK_ACTIVITY, MOCK_RULE, () => ({ allow: false })); isAllowed(MOCK_ACTIVITY, {}); sinon.assert.calledWithMatch(logger.logWarn, new RegExp(MOCK_RULE)); }); it('logs WARN with reason if the rule provides one', () => { - registerRule(MOCK_ACTIVITY, MOCK_RULE, () => ({allow: false, reason: 'fail'})); + registerRule(MOCK_ACTIVITY, MOCK_RULE, () => ({ allow: false, reason: 'fail' })); isAllowed(MOCK_ACTIVITY, {}); sinon.assert.calledWithMatch(logger.logWarn, new RegExp(MOCK_RULE), /fail/); }); diff --git a/test/spec/adUnits_spec.js b/test/spec/adUnits_spec.js index 1723a567c11..2a53f4095c1 100644 --- a/test/spec/adUnits_spec.js +++ b/test/spec/adUnits_spec.js @@ -1,7 +1,7 @@ import 'src/prebid.js'; -import {getGlobal} from '../../src/prebidGlobal.js'; +import { getGlobal } from '../../src/prebidGlobal.js'; -import {getGlobalVarName} from '../../src/buildOptions.js'; +import { getGlobalVarName } from '../../src/buildOptions.js'; describe('Publisher API _ AdUnits', function () { var assert = require('chai').assert; @@ -100,7 +100,7 @@ describe('Publisher API _ AdUnits', function () { it(`the second adUnits value should be same with the adUnits that is added by ${getGlobalVarName()}.addAdUnits();`, function () { assert.strictEqual(adUnit2.code, '/1996833/slot-2', 'adUnit2 code'); assert.deepEqual(adUnit2.sizes, [[468, 60]], 'adUnit2 sizes'); - assert.deepEqual(adUnit2['ortb2Imp'], {'ext': {'data': {'pbadslot': 'adSlotTest', 'inventory': [4], 'keywords': 'foo,bar', 'visitor': [1, 2, 3]}}}, 'adUnit2 ortb2Imp'); + assert.deepEqual(adUnit2['ortb2Imp'], { 'ext': { 'data': { 'pbadslot': 'adSlotTest', 'inventory': [4], 'keywords': 'foo,bar', 'visitor': [1, 2, 3] } } }, 'adUnit2 ortb2Imp'); assert.strictEqual(bids2[0].bidder, 'rubicon', 'adUnit2 bids1 bidder'); assert.strictEqual(bids2[0].params.rp_account, '4934', 'adUnit2 bids1 params.rp_account'); assert.strictEqual(bids2[0].params.rp_zonesize, '23948-15', 'adUnit2 bids1 params.rp_zonesize'); diff --git a/test/spec/adloader_spec.js b/test/spec/adloader_spec.js index f6574e21eda..155405cdfc9 100644 --- a/test/spec/adloader_spec.js +++ b/test/spec/adloader_spec.js @@ -5,12 +5,16 @@ import { registerActivityControl } from '../../src/activities/rules.js'; import { MODULE_TYPE_PREBID } from '../../src/activities/modules.js'; describe('adLoader', function () { + let sandbox; let utilsinsertElementStub; let utilsLogErrorStub; + let scriptEl; beforeEach(function () { - utilsinsertElementStub = sinon.stub(utils, 'insertElement'); - utilsLogErrorStub = sinon.stub(utils, 'logError'); + sandbox = sinon.createSandbox(); + scriptEl = null; + utilsinsertElementStub = sandbox.stub(utils, 'insertElement').callsFake((el) => { scriptEl = el }); + utilsLogErrorStub = sandbox.stub(utils, 'logError'); }); afterEach(function () { @@ -42,20 +46,46 @@ describe('adLoader', function () { expect(utilsinsertElementStub.called).to.be.true; }); + it('should run callback when script loads', () => { + const callback = sinon.stub(); + adLoader.loadExternalScript('test-1', MODULE_TYPE_PREBID, 'debugging', callback); + scriptEl.onload(); + sinon.assert.called(callback); + }); + + it('should run callback as an object', () => { + const callback = { + success: sinon.stub() + } + adLoader.loadExternalScript('test-2', MODULE_TYPE_PREBID, 'debugging', callback); + scriptEl.onload(); + sinon.assert.called(callback.success); + }); + + it('should run error callback once', () => { + const callback = { + error: sinon.stub() + } + adLoader.loadExternalScript('test-3', MODULE_TYPE_PREBID, 'debugging', callback); + const ev = new Event('error'); + scriptEl.dispatchEvent(ev); + scriptEl.dispatchEvent(ev); + sinon.assert.calledWith(callback.error, ev); + sinon.assert.calledOnce(callback.error); + }); + it('requires a url to be included once per document', function () { function getDocSpec() { return { createElement: function() { return { - + addEventListener() {} } }, getElementsByTagName: function() { return { firstChild: { - insertBefore: function() { - - } + insertBefore: function() {} } } } @@ -79,7 +109,8 @@ describe('adLoader', function () { return { setAttribute: function (key, value) { this[key] = value; - } + }, + addEventListener() {} } }, getElementsByTagName: function() { @@ -90,7 +121,7 @@ describe('adLoader', function () { } } }; - const attrs = {'z': 'A', 'y': 2}; + const attrs = { 'z': 'A', 'y': 2 }; const script = adLoader.loadExternalScript('someUrl', MODULE_TYPE_PREBID, 'debugging', undefined, doc, attrs); expect(script.z).to.equal('A'); expect(script.y).to.equal(2); @@ -99,7 +130,7 @@ describe('adLoader', function () { it('should disable loading external script for activity rule set', function () { let unregisterRule; try { - unregisterRule = registerActivityControl(LOAD_EXTERNAL_SCRIPT, 'loadExternalScript config', () => ({allow: false})); + unregisterRule = registerActivityControl(LOAD_EXTERNAL_SCRIPT, 'loadExternalScript config', () => ({ allow: false })); adLoader.loadExternalScript(null, 'debugging'); expect(utilsLogErrorStub.called).to.be.false; } finally { diff --git a/test/spec/adserver_spec.js b/test/spec/adserver_spec.js index 50fefaaaa13..b967af32691 100644 --- a/test/spec/adserver_spec.js +++ b/test/spec/adserver_spec.js @@ -19,7 +19,7 @@ describe('adserver', function() { try { expect(getPPID()).to.equal('hooked'); } finally { - getPPID.getHooks({hook: hookFn}).remove(); + getPPID.getHooks({ hook: hookFn }).remove(); } }); }); diff --git a/test/spec/aliasBidder_spec.js b/test/spec/aliasBidder_spec.js index 4ac830dae50..3657534d59e 100644 --- a/test/spec/aliasBidder_spec.js +++ b/test/spec/aliasBidder_spec.js @@ -1,5 +1,5 @@ import { pbjsTestOnly } from 'test/helpers/pbjs-test-only.js'; -import {getGlobal} from '../../src/prebidGlobal.js'; +import { getGlobal } from '../../src/prebidGlobal.js'; describe('Publisher API _ Alias Bidder', function () { var assert = require('chai').assert; diff --git a/test/spec/api_spec.js b/test/spec/api_spec.js index 21f4999b85a..77a0c4d9309 100755 --- a/test/spec/api_spec.js +++ b/test/spec/api_spec.js @@ -1,7 +1,7 @@ var assert = require('chai').assert; var prebid = require('../../src/prebid.js'); -const {getGlobalVarName} = require('../../src/buildOptions.js'); -const {getGlobal} = require('../../src/prebidGlobal.js'); +const { getGlobalVarName } = require('../../src/buildOptions.js'); +const { getGlobal } = require('../../src/prebidGlobal.js'); describe('Publisher API', function () { // var assert = chai.assert; diff --git a/test/spec/appnexusKeywords_spec.js b/test/spec/appnexusKeywords_spec.js index 53ffa87dd1a..635dc02f19e 100644 --- a/test/spec/appnexusKeywords_spec.js +++ b/test/spec/appnexusKeywords_spec.js @@ -1,5 +1,5 @@ -import {transformBidderParamKeywords} from '../../libraries/appnexusUtils/anKeywords.js'; -import {expect} from 'chai/index.js'; +import { transformBidderParamKeywords } from '../../libraries/appnexusUtils/anKeywords.js'; +import { expect } from 'chai/index.js'; import * as utils from '../../src/utils.js'; describe('transformBidderParamKeywords', function () { diff --git a/test/spec/auctionmanager_spec.js b/test/spec/auctionmanager_spec.js index 6ebb8666096..58151526325 100644 --- a/test/spec/auctionmanager_spec.js +++ b/test/spec/auctionmanager_spec.js @@ -12,24 +12,24 @@ import * as auctionModule from 'src/auction.js'; import { registerBidder } from 'src/adapters/bidderFactory.js'; import { createBid } from 'src/bidfactory.js'; import { config } from 'src/config.js'; -import {_internal as store} from 'src/videoCache.js'; +import { _internal as store } from 'src/videoCache.js'; import * as ajaxLib from 'src/ajax.js'; import { server } from 'test/mocks/xhr.js'; -import {hook} from '../../src/hook.js'; -import {auctionManager} from '../../src/auctionManager.js'; +import { hook } from '../../src/hook.js'; +import { auctionManager } from '../../src/auctionManager.js'; import 'modules/debugging/index.js' // some tests look for debugging side effects -import {AuctionIndex} from '../../src/auctionIndex.js'; -import {expect} from 'chai'; -import {deepClone} from '../../src/utils.js'; +import { AuctionIndex } from '../../src/auctionIndex.js'; +import { expect } from 'chai'; +import { deepClone } from '../../src/utils.js'; import { IMAGE as ortbNativeRequest } from 'src/native.js'; -import {PrebidServer} from '../../modules/prebidServerBidAdapter/index.js'; +import { PrebidServer } from '../../modules/prebidServerBidAdapter/index.js'; import { setConfig as setCurrencyConfig } from '../../modules/currency.js' import { REJECTION_REASON } from '../../src/constants.js'; import { setDocumentHidden } from './unit/utils/focusTimeout_spec.js'; -import {sandbox} from 'sinon'; -import {getMinBidCacheTTL, onMinBidCacheTTLChange} from '../../src/bidTTL.js'; -import {getGlobal} from '../../src/prebidGlobal.js'; +import { sandbox } from 'sinon'; +import { getMinBidCacheTTL, onMinBidCacheTTLChange } from '../../src/bidTTL.js'; +import { getGlobal } from '../../src/prebidGlobal.js'; /** * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest @@ -105,6 +105,10 @@ function mockBidRequest(bid, opts) { const defaultMediaType = { banner: { sizes: [[300, 250], [300, 600]] + }, + video: { + context: 'outstream', + renderer: {} } } const mediaType = (opts && opts.mediaType) ? opts.mediaType : defaultMediaType; @@ -149,7 +153,7 @@ function mockBidder(bidderCode, bids) { getUserSyncs: sinon.stub() }; - spec.buildRequests.returns([{'id': 123, 'method': 'POST'}]); + spec.buildRequests.returns([{ 'id': 123, 'method': 'POST' }]); spec.isBidRequestValid.returns(true); spec.interpretResponse.returns(bids); @@ -806,7 +810,7 @@ describe('auctionmanager.js', function () { code: ADUNIT_CODE, adUnitId: ADUNIT_CODE, bids: [ - {bidder: BIDDER_CODE}, + { bidder: BIDDER_CODE }, ] }]; }); @@ -822,7 +826,7 @@ describe('auctionmanager.js', function () { global: {}, bidder: {} } - const auction = auctionManager.createAuction({adUnits, ortb2Fragments}); + const auction = auctionManager.createAuction({ adUnits, ortb2Fragments }); auction.callBids(); const anyArgs = [...Array(7).keys()].map(() => sinon.match.any); sinon.assert.calledWith(stubMakeBidRequests, ...anyArgs.slice(0, 5).concat([sinon.match.same(ortb2Fragments)])); @@ -834,9 +838,9 @@ describe('auctionmanager.js', function () { global: {}, bidder: {} } - const auction = auctionManager.createAuction({adUnits, ortb2Fragments}); + const auction = auctionManager.createAuction({ adUnits, ortb2Fragments }); expect(auction.getNonBids()[0]).to.equal(undefined); - events.emit(EVENTS.SEAT_NON_BID, { + events.emit(EVENTS.PBS_ANALYTICS, { auctionId: auction.getAuctionId(), seatnonbid: ['test'] }); @@ -844,7 +848,7 @@ describe('auctionmanager.js', function () { }); it('resolves .requestsDone', async () => { - const auction = auctionManager.createAuction({adUnits}); + const auction = auctionManager.createAuction({ adUnits }); stubCallAdapters.resetHistory(); auction.callBids(); await auction.requestsDone; @@ -853,15 +857,15 @@ describe('auctionmanager.js', function () { describe('setConfig(minBidCacheTTL)', () => { it('should update getMinBidCacheTTL', () => { expect(getMinBidCacheTTL()).to.eql(null); - config.setConfig({minBidCacheTTL: 123}); + config.setConfig({ minBidCacheTTL: 123 }); expect(getMinBidCacheTTL()).to.eql(123); }); it('should run listeners registered with onMinBidCacheTTLChange', () => { - config.setConfig({minBidCacheTTL: 1}); + config.setConfig({ minBidCacheTTL: 1 }); let newTTL = null; onMinBidCacheTTLChange((ttl) => { newTTL = ttl; }); - config.setConfig({minBidCacheTTL: 2}); + config.setConfig({ minBidCacheTTL: 2 }); expect(newTTL).to.eql(2); }) }) @@ -870,7 +874,7 @@ describe('auctionmanager.js', function () { let clock, auction; beforeEach(() => { clock = sinon.useFakeTimers(); - auction = auctionManager.createAuction({adUnits}); + auction = auctionManager.createAuction({ adUnits }); indexAuctions.push(auction); }); afterEach(() => { @@ -991,7 +995,7 @@ describe('auctionmanager.js', function () { bd: [], entries: () => auctionManager.getNoBids() } - }).forEach(([t, {bd, entries}]) => { + }).forEach(([t, { bd, entries }]) => { it(`with ${t} are never dropped if minBidCacheTTL is not set`, () => { bids = bd; auction.callBids(); @@ -1036,13 +1040,13 @@ describe('auctionmanager.js', function () { code: ADUNIT_CODE, adUnitId: ADUNIT_CODE, bids: [ - {bidder: BIDDER_CODE, params: {placementId: 'id'}}, + { bidder: BIDDER_CODE, params: { placementId: 'id' } }, ] }]; adUnitCodes = [ADUNIT_CODE]; - auction = auctionModule.newAuction({adUnits, adUnitCodes, callback: function() {}, cbTimeout: 3000}); + auction = auctionModule.newAuction({ adUnits, adUnitCodes, callback: function() {}, cbTimeout: 3000 }); bids = TEST_BIDS.slice(); - bidderRequests = bids.map(b => mockBidRequest(b, {auctionId: auction.getAuctionId()})); + bidderRequests = bids.map(b => mockBidRequest(b, { auctionId: auction.getAuctionId() })); makeRequestsStub.returns(bidderRequests); indexAuctions = [auction]; createAuctionStub = sinon.stub(auctionModule, 'newAuction'); @@ -1130,7 +1134,7 @@ describe('auctionmanager.js', function () { mediaType: 'banner', } ); - Object.assign(getObj(), {renderer}); + Object.assign(getObj(), { renderer }); spec.interpretResponse.returns(bid); auction.callBids(); return auction.getBidsReceived().pop(); @@ -1153,13 +1157,13 @@ describe('auctionmanager.js', function () { backupOnly: true, render: (bid) => bid }; - Object.assign(adUnits[0], {renderer}); + Object.assign(adUnits[0], { renderer }); const bids1 = Object.assign({}, bids[0], { bidderCode: BIDDER_CODE, - mediaType: 'video-outstream', + mediaType: 'video', } ); spec.interpretResponse.returns(bids1); @@ -1236,11 +1240,11 @@ describe('auctionmanager.js', function () { url: 'renderer.js', render: (bid) => bid }; - Object.assign(adUnits[0], {renderer}); + Object.assign(adUnits[0], { renderer }); // make sure that if the renderer is only on the second ad unit, prebid // still correctly uses it const bid = mockBid(); - const bidRequests = [mockBidRequest(bid, {auctionId: auction.getAuctionId()})]; + const bidRequests = [mockBidRequest(bid, { auctionId: auction.getAuctionId() })]; bidRequests[0].bids[1] = Object.assign({ bidId: utils.getUniqueIdentifierStr() @@ -1278,6 +1282,12 @@ describe('auctionmanager.js', function () { expect(auction.getBidsReceived()[0].ttlBuffer).to.eql(0); }); + it('sets bidResponse.element from adUnit.element', () => { + adUnits[0].element = 'test'; + auction.callBids(); + expect(auction.getBidsReceived()[0].element).to.equal('test'); + }); + [ { request: 1, @@ -1295,9 +1305,9 @@ describe('auctionmanager.js', function () { request: undefined, response: false } - ].forEach(({request, response}) => { + ].forEach(({ request, response }) => { it(`sets bidResponse.instl to ${response} if adUnit.ortb2Imp.instl is ${request}`, () => { - adUnits[0].ortb2Imp = {instl: request}; + adUnits[0].ortb2Imp = { instl: request }; auction.callBids(); expect(auction.getBidsReceived()[0].instl).to.equal(response); }) @@ -1312,7 +1322,7 @@ describe('auctionmanager.js', function () { } function runAuction() { - const bidRequests = bids.map(bid => mockBidRequest(bid, {auctionId: auction.getAuctionId()})); + const bidRequests = bids.map(bid => mockBidRequest(bid, { auctionId: auction.getAuctionId() })); makeRequestsStub.returns(bidRequests); return new Promise((resolve) => { auctionDone = resolve; @@ -1326,8 +1336,8 @@ describe('auctionmanager.js', function () { transactionId: ADUNIT_CODE, adUnitId: ADUNIT_CODE, bids: [ - {bidder: BIDDER_CODE, params: {placementId: 'id'}}, - {bidder: BIDDER_CODE1, params: {placementId: 'id'}}, + { bidder: BIDDER_CODE, params: { placementId: 'id' } }, + { bidder: BIDDER_CODE1, params: { placementId: 'id' } }, ] }]; adUnitCodes = [ADUNIT_CODE]; @@ -1338,7 +1348,7 @@ describe('auctionmanager.js', function () { registerBidder(spec1); const spec2 = mockBidder(BIDDER_CODE1, [bids[1]]); registerBidder(spec2); - auction = auctionModule.newAuction({adUnits, adUnitCodes, callback: () => auctionDone(), cbTimeout: 20}); + auction = auctionModule.newAuction({ adUnits, adUnitCodes, callback: () => auctionDone(), cbTimeout: 20 }); indexAuctions = [auction]; }); @@ -1375,7 +1385,7 @@ describe('auctionmanager.js', function () { it(t, () => { const pm = runAuction().then(() => { if (shouldFire) { - sinon.assert.calledWith(handler, sinon.match({auctionId: auction.getAuctionId()})) + sinon.assert.calledWith(handler, sinon.match({ auctionId: auction.getAuctionId() })) } else { sinon.assert.notCalled(handler); } @@ -1460,7 +1470,7 @@ describe('auctionmanager.js', function () { }, }] }) - adUnits[0].bids.push({bidder: 'mock-s2s-1'}, {bidder: 'mock-s2s-2'}) + adUnits[0].bids.push({ bidder: 'mock-s2s-1' }, { bidder: 'mock-s2s-2' }) const s2sAdUnits = deepClone(adUnits); bids.unshift( mockBid({ bidderCode: 'mock-s2s-1', src: S2S.SRC, adUnits: s2sAdUnits, uniquePbsTid: '1' }), @@ -1516,18 +1526,18 @@ describe('auctionmanager.js', function () { transactionId: ADUNIT_CODE, adUnitId: ADUNIT_CODE, bids: [ - {bidder: BIDDER_CODE, params: {placementId: 'id'}}, + { bidder: BIDDER_CODE, params: { placementId: 'id' } }, ] }, { code: ADUNIT_CODE1, transactionId: ADUNIT_CODE1, adUnitId: ADUNIT_CODE1, bids: [ - {bidder: BIDDER_CODE1, params: {placementId: 'id'}}, + { bidder: BIDDER_CODE1, params: { placementId: 'id' } }, ] }]; adUnitCodes = adUnits.map(({ code }) => code); - auction = auctionModule.newAuction({adUnits, adUnitCodes, callback: function() {}, cbTimeout: 3000}); + auction = auctionModule.newAuction({ adUnits, adUnitCodes, callback: function() {}, cbTimeout: 3000 }); const bidRequests = [ mockBidRequest(bids[0], { auctionId: auction.getAuctionId() }), mockBidRequest(bids1[0], { auctionId: auction.getAuctionId(), adUnitCode: ADUNIT_CODE1 }) @@ -1574,11 +1584,11 @@ describe('auctionmanager.js', function () { }); it('should run auction after video bids have been cached', async function () { - sinon.stub(store, 'store').callsArgWith(1, null, [{uuid: 123}]); + sinon.stub(store, 'store').callsArgWith(1, null, [{ uuid: 123 }]); sinon.stub(config, 'getConfig').withArgs('cache.url').returns('cache-url'); - const bidsCopy = [Object.assign({}, bids[0], {mediaType: 'video'})]; - const bids1Copy = [Object.assign({}, bids1[0], {mediaType: 'video'})]; + const bidsCopy = [Object.assign({}, bids[0], { mediaType: 'video' })]; + const bids1Copy = [Object.assign({}, bids1[0], { mediaType: 'video' })]; spec.interpretResponse.returns(bidsCopy); spec1.interpretResponse.returns(bids1Copy); @@ -1594,16 +1604,16 @@ describe('auctionmanager.js', function () { }); it('runs auction after video responses with multiple bid objects have been cached', async function () { - sinon.stub(store, 'store').callsArgWith(1, null, [{uuid: 123}]); + sinon.stub(store, 'store').callsArgWith(1, null, [{ uuid: 123 }]); sinon.stub(config, 'getConfig').withArgs('cache.url').returns('cache-url'); const bidsCopy = [ - Object.assign({}, bids[0], {mediaType: 'video'}), - Object.assign({}, bids[0], {mediaType: 'banner'}), + Object.assign({}, bids[0], { mediaType: 'video' }), + Object.assign({}, bids[0], { mediaType: 'banner' }), ]; const bids1Copy = [ - Object.assign({}, bids1[0], {mediaType: 'video'}), - Object.assign({}, bids1[0], {mediaType: 'video'}), + Object.assign({}, bids1[0], { mediaType: 'video' }), + Object.assign({}, bids1[0], { mediaType: 'video' }), ]; spec.interpretResponse.returns(bidsCopy); @@ -1626,7 +1636,7 @@ describe('auctionmanager.js', function () { }); auction.callBids(); - sinon.assert.calledWith(auction.addBidRejected, sinon.match({rejectionReason: REJECTION_REASON.PRICE_TOO_HIGH})); + sinon.assert.calledWith(auction.addBidRejected, sinon.match({ rejectionReason: REJECTION_REASON.PRICE_TOO_HIGH })); }) }); @@ -1675,17 +1685,17 @@ describe('auctionmanager.js', function () { code: ADUNIT_CODE, transactionId: ADUNIT_CODE, bids: [ - {bidder: BIDDER_CODE, params: {placementId: 'id'}}, + { bidder: BIDDER_CODE, params: { placementId: 'id' } }, ] }, { code: ADUNIT_CODE1, transactionId: ADUNIT_CODE1, bids: [ - {bidder: BIDDER_CODE1, params: {placementId: 'id'}}, + { bidder: BIDDER_CODE1, params: { placementId: 'id' } }, ] }]; adUnitCodes = adUnits.map(({ code }) => code); - auction = auctionModule.newAuction({adUnits, adUnitCodes, callback: function() {}, cbTimeout: 3000}); + auction = auctionModule.newAuction({ adUnits, adUnitCodes, callback: function() {}, cbTimeout: 3000 }); createAuctionStub = sinon.stub(auctionModule, 'newAuction'); createAuctionStub.returns(auction); indexAuctions = [auction]; @@ -1743,12 +1753,12 @@ describe('auctionmanager.js', function () { transactionId: ADUNIT_CODE, adUnitId: ADUNIT_CODE, bids: [ - {bidder: BIDDER_CODE, params: {placementId: 'id'}}, + { bidder: BIDDER_CODE, params: { placementId: 'id' } }, ], nativeOrtbRequest: ortbNativeRequest.ortb, mediaTypes: { native: { type: 'image' } } }]; - auction = auctionModule.newAuction({adUnits, adUnitCodes: [ADUNIT_CODE], callback: function() {}, cbTimeout: 3000}); + auction = auctionModule.newAuction({ adUnits, adUnitCodes: [ADUNIT_CODE], callback: function() {}, cbTimeout: 3000 }); indexAuctions = [auction]; const createAuctionStub = sinon.stub(auctionModule, 'newAuction'); createAuctionStub.returns(auction); @@ -1806,7 +1816,7 @@ describe('auctionmanager.js', function () { describe('getMediaTypeGranularity', function () { if (FEATURES.VIDEO) { it('video', function () { - let mediaTypes = { video: {id: '1'} }; + let mediaTypes = { video: { id: '1' } }; // mediaType is video and video.context is undefined expect(getMediaTypeGranularity('video', mediaTypes, { @@ -1857,14 +1867,14 @@ describe('auctionmanager.js', function () { banner: 'low', video: 'medium' })).to.equal('medium'); - expect(getMediaTypeGranularity('video', {mediaTypes: {banner: {}}}, { + expect(getMediaTypeGranularity('video', { mediaTypes: { banner: {} } }, { banner: 'low', video: 'medium' })).to.equal('medium'); }); } it('native', function () { - expect(getMediaTypeGranularity('native', {native: {}}, { + expect(getMediaTypeGranularity('native', { native: {} }, { banner: 'low', video: 'medium', native: 'high' })).to.equal('high'); }); @@ -1876,8 +1886,8 @@ describe('auctionmanager.js', function () { sandbox = sinon.createSandbox(); sandbox.stub(adapterManager, 'callBidWonBidder'); sandbox.stub(adapterManager, 'triggerBilling') - adUnits = [{code: 'au1'}, {code: 'au2'}] - auction = newAuction({adUnits}); + adUnits = [{ code: 'au1' }, { code: 'au2' }] + auction = newAuction({ adUnits }); bid = { bidder: 'mock-bidder' }; @@ -1937,13 +1947,13 @@ describe('auctionmanager.js', function () { expect(gpbg({ mediaType: 'video', pbMg: 'medium' }, { - 'mediaTypes': {video: {id: '1'}} + 'mediaTypes': { video: { id: '1' } } })).to.equal('medium'); expect(gpbg({ mediaType: 'banner', pbLg: 'low' }, { - 'mediaTypes': {banner: {}} + 'mediaTypes': { banner: {} } })).to.equal('low'); }); }) @@ -1958,7 +1968,7 @@ describe('auctionmanager.js', function () { responsesReady.before(responsesReadyHook, 100); }); after(() => { - responsesReady.getHooks({hook: responsesReadyHook}).remove(); + responsesReady.getHooks({ hook: responsesReadyHook }).remove(); }); beforeEach(() => { @@ -1998,12 +2008,12 @@ describe('auctionmanager.js', function () { const ADUNIT_CODE2 = 'adUnitCode2'; const BIDDER_CODE2 = 'sampleBidder2'; - const bids1 = [mockBid({bidderCode: BIDDER_CODE1, adUnitId: ADUNIT_CODE1})]; - const bids2 = [mockBid({bidderCode: BIDDER_CODE2, adUnitId: ADUNIT_CODE2})]; + const bids1 = [mockBid({ bidderCode: BIDDER_CODE1, adUnitId: ADUNIT_CODE1 })]; + const bids2 = [mockBid({ bidderCode: BIDDER_CODE2, adUnitId: ADUNIT_CODE2 })]; bidRequests = [ mockBidRequest(bids[0]), - mockBidRequest(bids1[0], {adUnitCode: ADUNIT_CODE1}), - mockBidRequest(bids2[0], {adUnitCode: ADUNIT_CODE2}) + mockBidRequest(bids1[0], { adUnitCode: ADUNIT_CODE1 }), + mockBidRequest(bids2[0], { adUnitCode: ADUNIT_CODE2 }) ]; const cbs = auctionCallbacks(doneSpy, auction); const method = getMethod(cbs); @@ -2021,7 +2031,7 @@ describe('auctionmanager.js', function () { if (FEATURES.VIDEO) { it('should call auction done after prebid cache is complete for mediaType video', async function () { bids[0].mediaType = 'video'; - const bids1 = [mockBid({bidderCode: BIDDER_CODE1})]; + const bids1 = [mockBid({ bidderCode: BIDDER_CODE1 })]; const opts = { mediaType: { @@ -2033,7 +2043,7 @@ describe('auctionmanager.js', function () { }; bidRequests = [ mockBidRequest(bids[0], opts), - mockBidRequest(bids1[0], {adUnitCode: ADUNIT_CODE1}), + mockBidRequest(bids1[0], { adUnitCode: ADUNIT_CODE1 }), ]; const cbs = auctionCallbacks(doneSpy, auction); @@ -2045,7 +2055,7 @@ describe('auctionmanager.js', function () { assert.equal(doneSpy.callCount, 0); const uuid = 'c488b101-af3e-4a99-b538-00423e5a3371'; const responseBody = `{"responses":[{"uuid":"${uuid}"}]}`; - server.requests[0].respond(200, {'Content-Type': 'application/json'}, responseBody); + server.requests[0].respond(200, { 'Content-Type': 'application/json' }, responseBody); assert.equal(doneSpy.callCount, 1); }); } @@ -2053,10 +2063,10 @@ describe('auctionmanager.js', function () { it('should convert cpm to number', () => { auction.addBidReceived = sinon.spy(); const cbs = auctionCallbacks(doneSpy, auction); - const bid = {...bids[0], cpm: '1.23'} + const bid = { ...bids[0], cpm: '1.23' } bidRequests = [mockBidRequest(bid)]; cbs.addBidResponse.call(bidRequests[0], ADUNIT_CODE, bid); - sinon.assert.calledWith(auction.addBidReceived, sinon.match({cpm: 1.23})); + sinon.assert.calledWith(auction.addBidReceived, sinon.match({ cpm: 1.23 })); }) describe('when responsesReady defers', () => { @@ -2071,7 +2081,7 @@ describe('auctionmanager.js', function () { }); after(() => { - responsesReady.getHooks({hook}).remove(); + responsesReady.getHooks({ hook }).remove(); }); beforeEach(() => { @@ -2081,8 +2091,8 @@ describe('auctionmanager.js', function () { reject = rj; }); bids = [ - mockBid({bidderCode: BIDDER_CODE1}), - mockBid({bidderCode: BIDDER_CODE}) + mockBid({ bidderCode: BIDDER_CODE1 }), + mockBid({ bidderCode: BIDDER_CODE }) ] bidRequests = bids.map((b) => mockBidRequest(b)); callbacks = auctionCallbacks(doneSpy, auction); @@ -2126,13 +2136,13 @@ describe('auctionmanager.js', function () { }); after(() => { - addBidResponse.getHooks({hook: rejectHook}).remove(); + addBidResponse.getHooks({ hook: rejectHook }).remove(); events.off(EVENTS.BID_REJECTED, onBidRejected); }); beforeEach(() => { onBidRejected.resetHistory(); - bid = mockBid({bidderCode: BIDDER_CODE}); + bid = mockBid({ bidderCode: BIDDER_CODE }); bidRequests = [ mockBidRequest(bid), ]; @@ -2182,7 +2192,7 @@ describe('auctionmanager.js', function () { doneSpy = sinon.spy(); config.setConfig({ 'auctionOptions': { - secondaryBidders: [ secondaryBidder ] + secondaryBidders: [secondaryBidder] } }); @@ -2198,13 +2208,13 @@ describe('auctionmanager.js', function () { }); it('should not wait to call auction done for secondary bidders', async function () { - const bids1 = [mockBid({bidderCode: requiredBidder, transactionId: ADUNIT_CODE1})]; - const bids2 = [mockBid({bidderCode: requiredBidder1, transactionId: ADUNIT_CODE1})]; - const bids3 = [mockBid({bidderCode: secondaryBidder, transactionId: ADUNIT_CODE1})]; + const bids1 = [mockBid({ bidderCode: requiredBidder, transactionId: ADUNIT_CODE1 })]; + const bids2 = [mockBid({ bidderCode: requiredBidder1, transactionId: ADUNIT_CODE1 })]; + const bids3 = [mockBid({ bidderCode: secondaryBidder, transactionId: ADUNIT_CODE1 })]; bidRequests = [ - mockBidRequest(bids1[0], {adUnitCode: ADUNIT_CODE1}), - mockBidRequest(bids2[0], {adUnitCode: ADUNIT_CODE1}), - mockBidRequest(bids3[0], {adUnitCode: ADUNIT_CODE1}), + mockBidRequest(bids1[0], { adUnitCode: ADUNIT_CODE1 }), + mockBidRequest(bids2[0], { adUnitCode: ADUNIT_CODE1 }), + mockBidRequest(bids3[0], { adUnitCode: ADUNIT_CODE1 }), ]; const cbs = auctionCallbacks(doneSpy, auction); // required bidder responds immeaditely to auction @@ -2231,13 +2241,13 @@ describe('auctionmanager.js', function () { secondaryBidders: [requiredBidder, requiredBidder1, secondaryBidder] } }) - const bids1 = [mockBid({bidderCode: requiredBidder})]; - const bids2 = [mockBid({bidderCode: requiredBidder1})]; - const bids3 = [mockBid({bidderCode: secondaryBidder})]; + const bids1 = [mockBid({ bidderCode: requiredBidder })]; + const bids2 = [mockBid({ bidderCode: requiredBidder1 })]; + const bids3 = [mockBid({ bidderCode: secondaryBidder })]; bidRequests = [ - mockBidRequest(bids1[0], {adUnitCode: ADUNIT_CODE1}), - mockBidRequest(bids2[0], {adUnitCode: ADUNIT_CODE1}), - mockBidRequest(bids3[0], {adUnitCode: ADUNIT_CODE1}), + mockBidRequest(bids1[0], { adUnitCode: ADUNIT_CODE1 }), + mockBidRequest(bids2[0], { adUnitCode: ADUNIT_CODE1 }), + mockBidRequest(bids3[0], { adUnitCode: ADUNIT_CODE1 }), ]; const cbs = auctionCallbacks(doneSpy, auction); cbs.addBidResponse.call(bidRequests[0], ADUNIT_CODE1, bids1[0]); @@ -2259,13 +2269,13 @@ describe('auctionmanager.js', function () { }); it('should allow secondaryBidders to respond in auction before is is done', async function () { - const bids1 = [mockBid({bidderCode: requiredBidder})]; - const bids2 = [mockBid({bidderCode: requiredBidder1})]; - const bids3 = [mockBid({bidderCode: secondaryBidder})]; + const bids1 = [mockBid({ bidderCode: requiredBidder })]; + const bids2 = [mockBid({ bidderCode: requiredBidder1 })]; + const bids3 = [mockBid({ bidderCode: secondaryBidder })]; bidRequests = [ - mockBidRequest(bids1[0], {adUnitCode: ADUNIT_CODE1}), - mockBidRequest(bids2[0], {adUnitCode: ADUNIT_CODE1}), - mockBidRequest(bids3[0], {adUnitCode: ADUNIT_CODE1}), + mockBidRequest(bids1[0], { adUnitCode: ADUNIT_CODE1 }), + mockBidRequest(bids2[0], { adUnitCode: ADUNIT_CODE1 }), + mockBidRequest(bids3[0], { adUnitCode: ADUNIT_CODE1 }), ]; const cbs = auctionCallbacks(doneSpy, auction); // secondaryBidder is first to respond diff --git a/test/spec/banner_spec.js b/test/spec/banner_spec.js index f60f2023194..daf6aeecf77 100644 --- a/test/spec/banner_spec.js +++ b/test/spec/banner_spec.js @@ -23,7 +23,7 @@ describe('banner', () => { otherOne: 'test' }; - const expected = {...mt}; + const expected = { ...mt }; delete expected.api; const adUnit = { @@ -80,7 +80,7 @@ describe('banner', () => { const adUnit = { mediaTypes: { banner: { - format: [{w: 100, h: 100}], + format: [{ w: 100, h: 100 }], btype: [1, 2, 34], // should be overwritten with value from ortb2Imp battr: [3, 4], maxduration: 'omitted_value' // should be omitted during copying - not part of banner obj spec @@ -100,7 +100,7 @@ describe('banner', () => { const expected = { mediaTypes: { banner: { - format: [{w: 100, h: 100}], + format: [{ w: 100, h: 100 }], btype: [999, 999], pos: 5, battr: [3, 4], @@ -110,7 +110,7 @@ describe('banner', () => { }, ortb2Imp: { banner: { - format: [{w: 100, h: 100}], + format: [{ w: 100, h: 100 }], request: '{payload: true}', pos: 5, btype: [999, 999], @@ -131,12 +131,12 @@ describe('banner', () => { const adUnit = { mediaTypes: { banner: { - format: [{wratio: 1, hratio: 1}] + format: [{ wratio: 1, hratio: 1 }] } }, ortb2Imp: { banner: { - format: [{wratio: 1, hratio: 1}] + format: [{ wratio: 1, hratio: 1 }] } } } @@ -168,7 +168,7 @@ describe('banner', () => { const adUnit = { mediaTypes: { banner: { - format: [{w: 100, h: 100}], + format: [{ w: 100, h: 100 }], btype: [999, 999], pos: 5, battr: [3, 4], @@ -181,7 +181,7 @@ describe('banner', () => { const expected1 = { mediaTypes: { banner: { - format: [{w: 100, h: 100}], + format: [{ w: 100, h: 100 }], btype: [999, 999], pos: 5, battr: [3, 4], @@ -191,7 +191,7 @@ describe('banner', () => { }, ortb2Imp: { banner: { - format: [{w: 100, h: 100}], + format: [{ w: 100, h: 100 }], btype: [999, 999], pos: 5, battr: [3, 4], @@ -207,7 +207,7 @@ describe('banner', () => { mediaTypes: {}, ortb2Imp: { banner: { - format: [{w: 100, h: 100}], + format: [{ w: 100, h: 100 }], btype: [999, 999], pos: 5, battr: [3, 4], @@ -220,7 +220,7 @@ describe('banner', () => { const expected2 = { mediaTypes: { banner: { - format: [{w: 100, h: 100}], + format: [{ w: 100, h: 100 }], btype: [999, 999], pos: 5, battr: [3, 4], @@ -229,7 +229,7 @@ describe('banner', () => { }, ortb2Imp: { banner: { - format: [{w: 100, h: 100}], + format: [{ w: 100, h: 100 }], btype: [999, 999], pos: 5, battr: [3, 4], diff --git a/test/spec/config_spec.js b/test/spec/config_spec.js index 51ac5ca1291..b13e9a15967 100644 --- a/test/spec/config_spec.js +++ b/test/spec/config_spec.js @@ -46,7 +46,7 @@ describe('config API', function () { }); it('readConfig returns deepCopy of the internal config object', function () { - setConfig({ foo: {biz: 'bar'} }); + setConfig({ foo: { biz: 'bar' } }); const config1 = readConfig('foo'); config1.biz = 'buz'; const config2 = readConfig('foo'); @@ -108,16 +108,16 @@ describe('config API', function () { it('getConfig subscribers are called immediately if passed {init: true}', () => { const listener = sinon.spy(); - setConfig({foo: 'bar'}); - getConfig('foo', listener, {init: true}); - sinon.assert.calledWith(listener, {foo: 'bar'}); + setConfig({ foo: 'bar' }); + getConfig('foo', listener, { init: true }); + sinon.assert.calledWith(listener, { foo: 'bar' }); }); it('getConfig subscribers with no topic are called immediately if passed {init: true}', () => { const listener = sinon.spy(); - setConfig({foo: 'bar'}); - getConfig(listener, {init: true}); - sinon.assert.calledWith(listener, sinon.match({foo: 'bar'})); + setConfig({ foo: 'bar' }); + getConfig(listener, { init: true }); + sinon.assert.calledWith(listener, sinon.match({ foo: 'bar' })); }); it('sets and gets arbitrary configuration properties', function () { @@ -139,9 +139,9 @@ describe('config API', function () { }); it('overwrites existing config properties', function () { - setConfig({ foo: {biz: 'buz'} }); - setConfig({ foo: {baz: 'qux'} }); - expect(getConfig('foo')).to.eql({baz: 'qux'}); + setConfig({ foo: { biz: 'buz' } }); + setConfig({ foo: { baz: 'qux' } }); + expect(getConfig('foo')).to.eql({ baz: 'qux' }); }); it('sets debugging', function () { @@ -167,7 +167,7 @@ describe('config API', function () { syncDelay: 3000, auctionDelay: 0 }; - setDefaults({'userSync': DEFAULT_USERSYNC}); + setDefaults({ 'userSync': DEFAULT_USERSYNC }); expect(getConfig('userSync')).to.eql(DEFAULT_USERSYNC); }); @@ -258,10 +258,10 @@ describe('config API', function () { getter: () => config.getConfig }, 'using setBidderConfig': { - setter: () => (config) => setBidderConfig({bidders: ['mockBidder'], config}), + setter: () => (config) => setBidderConfig({ bidders: ['mockBidder'], config }), getter: () => (option) => config.runWithBidder('mockBidder', () => config.getConfig(option)) } - }).forEach(([t, {getter, setter}]) => { + }).forEach(([t, { getter, setter }]) => { describe(t, () => { let getConfig, setConfig; beforeEach(() => { @@ -284,8 +284,8 @@ describe('config API', function () { }); it('does not force defaults for bidder config', () => { - config.setConfig({bidderSequence: 'fixed'}); - config.setBidderConfig({bidders: ['mockBidder'], config: {other: 'config'}}) + config.setConfig({ bidderSequence: 'fixed' }); + config.setBidderConfig({ bidders: ['mockBidder'], config: { other: 'config' } }) expect(config.runWithBidder('mockBidder', () => config.getConfig('bidderSequence'))).to.eql('fixed'); }) @@ -361,36 +361,44 @@ describe('config API', function () { }); it('should log warning for invalid auctionOptions bidder values', function () { - setConfig({ auctionOptions: { - 'secondaryBidders': 'appnexus, rubicon', - }}); + setConfig({ + auctionOptions: { + 'secondaryBidders': 'appnexus, rubicon', + } + }); expect(logWarnSpy.calledOnce).to.equal(true); const warning = 'Auction Options secondaryBidders must be of type Array'; assert.ok(logWarnSpy.calledWith(warning), 'expected warning was logged'); }); it('should log warning for invalid auctionOptions suppress stale render', function () { - setConfig({ auctionOptions: { - 'suppressStaleRender': 'test', - }}); + setConfig({ + auctionOptions: { + 'suppressStaleRender': 'test', + } + }); expect(logWarnSpy.calledOnce).to.equal(true); const warning = 'Auction Options suppressStaleRender must be of type boolean'; assert.ok(logWarnSpy.calledWith(warning), 'expected warning was logged'); }); it('should log warning for invalid auctionOptions suppress expired render', function () { - setConfig({ auctionOptions: { - 'suppressExpiredRender': 'test', - }}); + setConfig({ + auctionOptions: { + 'suppressExpiredRender': 'test', + } + }); expect(logWarnSpy.calledOnce).to.equal(true); const warning = 'Auction Options suppressExpiredRender must be of type boolean'; assert.ok(logWarnSpy.calledWith(warning), 'expected warning was logged'); }); it('should log warning for invalid properties to auctionOptions', function () { - setConfig({ auctionOptions: { - 'testing': true - }}); + setConfig({ + auctionOptions: { + 'testing': true + } + }); expect(logWarnSpy.calledOnce).to.equal(true); const warning = 'Auction Options given an incorrect param: testing'; assert.ok(logWarnSpy.calledWith(warning), 'expected warning was logged'); @@ -408,16 +416,17 @@ describe('config API', function () { } } }; - setConfig({ ortb2: { - user: { - ext: { - data: { - registered: true, - interests: ['cars'] + setConfig({ + ortb2: { + user: { + ext: { + data: { + registered: true, + interests: ['cars'] + } } } } - } }); mergeConfig(obj); const expected = { @@ -453,15 +462,17 @@ describe('config API', function () { } } } - setConfig({ ortb2: { - user: { - ext: { - data: { - registered: false + setConfig({ + ortb2: { + user: { + ext: { + data: { + registered: false + } } } } - }}); + }); mergeConfig(input); const expected = { user: { diff --git a/test/spec/creative/crossDomainCreative_spec.js b/test/spec/creative/crossDomainCreative_spec.js index ac63bcf8f79..41dfd391569 100644 --- a/test/spec/creative/crossDomainCreative_spec.js +++ b/test/spec/creative/crossDomainCreative_spec.js @@ -1,4 +1,4 @@ -import {renderer} from '../../../creative/crossDomain.js'; +import { renderer } from '../../../creative/crossDomain.js'; import { ERROR_EXCEPTION, EVENT_AD_RENDER_FAILED, EVENT_AD_RENDER_SUCCEEDED, @@ -38,9 +38,9 @@ describe('cross-domain creative', () => { }, parent: { parent: top, - frames: {'__pb_locator__': {}}, + frames: { '__pb_locator__': {} }, postMessage: sinon.stub().callsFake((payload, targetOrigin, transfer) => { - messages.push({payload: JSON.parse(payload), targetOrigin, transfer}); + messages.push({ payload: JSON.parse(payload), targetOrigin, transfer }); }) } }; @@ -73,7 +73,7 @@ describe('cross-domain creative', () => { } it('derives postMessage target origin from pubUrl ', () => { - renderAd({pubUrl: 'https://domain.com:123/path'}); + renderAd({ pubUrl: 'https://domain.com:123/path' }); expect(messages[0].targetOrigin).to.eql('https://domain.com:123') }); @@ -88,7 +88,7 @@ describe('cross-domain creative', () => { ...target, parent: { top, - frames: {'__pb_locator__': {}}, + frames: { '__pb_locator__': {} }, parent: { top, frames: {} @@ -103,10 +103,10 @@ describe('cross-domain creative', () => { }).forEach(([t, getFrames]) => { describe(`when an ancestor ${t}`, () => { beforeEach(() => { - Object.defineProperty(win.parent.parent.parent.parent, 'frames', {get: getFrames}) + Object.defineProperty(win.parent.parent.parent.parent, 'frames', { get: getFrames }) }) it('posts message to the first ancestor with __pb_locator__ child', () => { - renderAd({pubUrl: 'https://www.example.com'}); + renderAd({ pubUrl: 'https://www.example.com' }); expect(messages.length).to.eql(1); }); }) @@ -117,13 +117,13 @@ describe('cross-domain creative', () => { throw new DOMException(); } }); - renderAd({pubUrl: 'https://www.example.com'}); + renderAd({ pubUrl: 'https://www.example.com' }); expect(messages.length).to.eql(1); }) }) it('generates request message with adId and clickUrl', () => { - renderAd({adId: '123', clickUrl: 'https://click-url.com', pubUrl: ORIGIN}); + renderAd({ adId: '123', clickUrl: 'https://click-url.com', pubUrl: ORIGIN }); expect(messages[0].payload).to.eql({ message: MESSAGE_REQUEST, adId: '123', @@ -149,15 +149,15 @@ describe('cross-domain creative', () => { } it('ignores messages that are not a prebid response message', () => { - renderAd({adId: '123', pubUrl: ORIGIN}); - reply({adId: '123', ad: 'markup'}); + renderAd({ adId: '123', pubUrl: ORIGIN }); + reply({ adId: '123', ad: 'markup' }); sinon.assert.notCalled(mkIframe); }) it('signals AD_RENDER_FAILED on exceptions', () => { mkIframe.callsFake(() => { throw new Error('error message') }); - renderAd({adId: '123', pubUrl: ORIGIN}); - reply({message: MESSAGE_RESPONSE, adId: '123', ad: 'markup'}); + renderAd({ adId: '123', pubUrl: ORIGIN }); + reply({ message: MESSAGE_RESPONSE, adId: '123', ad: 'markup' }); return waitFor(() => messages[1]?.payload).then(() => { expect(messages[1].payload).to.eql({ message: MESSAGE_EVENT, @@ -184,7 +184,7 @@ describe('cross-domain creative', () => { adId: '123', renderer: 'window.render = window.parent._render' } - renderAd({adId: '123', pubUrl: ORIGIN}); + renderAd({ adId: '123', pubUrl: ORIGIN }); reply(data); return waitFor(() => window._render.args.length).then(() => { sinon.assert.calledWith(window._render, data, sinon.match.any, win); @@ -201,7 +201,7 @@ describe('cross-domain creative', () => { 'rejects (w/reason)': ['window.render = function() { return Promise.reject({reason: "other", message: "msg"}) }', 'other'], }).forEach(([t, [renderer, reason = ERROR_EXCEPTION, message = 'msg']]) => { it(`signals AD_RENDER_FAILED on renderer that ${t}`, () => { - renderAd({adId: '123', pubUrl: ORIGIN}); + renderAd({ adId: '123', pubUrl: ORIGIN }); reply({ message: MESSAGE_RESPONSE, adId: '123', @@ -222,7 +222,7 @@ describe('cross-domain creative', () => { }); it('signals AD_RENDER_SUCCEEDED when renderer resolves', () => { - renderAd({adId: '123', pubUrl: ORIGIN}); + renderAd({ adId: '123', pubUrl: ORIGIN }); reply({ message: MESSAGE_RESPONSE, adId: '123', @@ -244,7 +244,7 @@ describe('cross-domain creative', () => { }) it('is provided a sendMessage that accepts replies', () => { - renderAd({adId: '123', pubUrl: ORIGIN}); + renderAd({ adId: '123', pubUrl: ORIGIN }); window._reply = sinon.stub(); reply({ adId: '123', @@ -255,7 +255,7 @@ describe('cross-domain creative', () => { reply('response', 1); return waitFor(() => window._reply.args.length) }).then(() => { - sinon.assert.calledWith(window._reply, sinon.match({data: JSON.stringify('response')})); + sinon.assert.calledWith(window._reply, sinon.match({ data: JSON.stringify('response') })); }).finally(() => { delete window._reply; }) diff --git a/test/spec/creative/displayRenderer_spec.js b/test/spec/creative/displayRenderer_spec.js index a269d5f4e33..95bc0ebab9e 100644 --- a/test/spec/creative/displayRenderer_spec.js +++ b/test/spec/creative/displayRenderer_spec.js @@ -1,10 +1,10 @@ -import {render} from 'creative/renderers/display/renderer.js'; -import {ERROR_NO_AD} from '../../../creative/renderers/display/constants.js'; +import { render } from 'creative/renderers/display/renderer.js'; +import { ERROR_NO_AD } from '../../../creative/renderers/display/constants.js'; describe('Creative renderer - display', () => { let doc, mkFrame, sendMessage, win; beforeEach(() => { - mkFrame = sinon.stub().callsFake((doc, attrs) => Object.assign({doc}, attrs)); + mkFrame = sinon.stub().callsFake((doc, attrs) => Object.assign({ doc }, attrs)); sendMessage = sinon.stub(); doc = { body: { @@ -17,7 +17,7 @@ describe('Creative renderer - display', () => { }); function runRenderer(data) { - return render(data, {sendMessage, mkFrame}, win); + return render(data, { sendMessage, mkFrame }, win); } it('throws when both ad and adUrl are missing', () => { @@ -57,7 +57,7 @@ describe('Creative renderer - display', () => { }) it('defaults width and height to 100%', () => { - runRenderer({ad: 'mock'}); + runRenderer({ ad: 'mock' }); sinon.assert.calledWith(doc.body.appendChild, sinon.match({ doc, width: '100%', @@ -72,13 +72,13 @@ describe('Creative renderer - display', () => { style: {} } }) - runRenderer({ad: 'mock'}); + runRenderer({ ad: 'mock' }); expect(doc.body.style.height).to.eql('100%'); expect(doc.body.parentElement.style.height).to.eql('100%'); }); it('sizes frame element if instl = true', () => { - win.frameElement = { style: {}}; + win.frameElement = { style: {} }; runRenderer({ ad: 'mock', width: 123, diff --git a/test/spec/creative/nativeRenderer_spec.js b/test/spec/creative/nativeRenderer_spec.js index 02971088a1e..6c149bec7af 100644 --- a/test/spec/creative/nativeRenderer_spec.js +++ b/test/spec/creative/nativeRenderer_spec.js @@ -1,5 +1,5 @@ -import {getAdMarkup, getReplacements, getReplacer} from '../../../creative/renderers/native/renderer.js'; -import {ACTION_CLICK, ACTION_IMP, ACTION_RESIZE, MESSAGE_NATIVE} from '../../../creative/renderers/native/constants.js'; +import { getAdMarkup, getReplacements, getReplacer } from '../../../creative/renderers/native/renderer.js'; +import { ACTION_CLICK, ACTION_IMP, ACTION_RESIZE, MESSAGE_NATIVE } from '../../../creative/renderers/native/constants.js'; describe('Native creative renderer', () => { let win; @@ -45,7 +45,7 @@ describe('Native creative renderer', () => { document.body.removeChild(frame); }) it('with adTemplate, if present', () => { - return getAdMarkup('123', {adTemplate: 'tpl'}, replacer, win).then((result) => { + return getAdMarkup('123', { adTemplate: 'tpl' }, replacer, win).then((result) => { expect(result).to.eql('markup'); sinon.assert.calledWith(replacer, 'tpl'); }); @@ -83,8 +83,8 @@ describe('Native creative renderer', () => { ortb: { assets: [{ id: 1, - link: {url: 'l1'}, - data: {value: 'v1'} + link: { url: 'l1' }, + data: { value: 'v1' } }] } }); @@ -99,7 +99,7 @@ describe('Native creative renderer', () => { it('replaces placeholders for for legacy assets', () => { const repl = getReplacer('123', { assets: [ - {key: 'k1', value: 'v1'}, {key: 'k2', value: 'v2'} + { key: 'k1', value: 'v1' }, { key: 'k2', value: 'v2' } ], nativeKeys: { k1: 'hb_native_k1', @@ -135,8 +135,8 @@ describe('Native creative renderer', () => { const repl = getReplacer('123', { ortb, assets: [ - {key: 'clickUrl', value: 'overridden'}, - {key: 'privacyLink', value: 'overridden'} + { key: 'clickUrl', value: 'overridden' }, + { key: 'privacyLink', value: 'overridden' } ], nativeKeys: { clickUrl: 'hb_native_linkurl', @@ -157,10 +157,10 @@ describe('Native creative renderer', () => { }); Object.entries({ - title: {text: 'val'}, - data: {value: 'val'}, - img: {url: 'val'}, - video: {vasttag: 'val'} + title: { text: 'val' }, + data: { value: 'val' }, + img: { url: 'val' }, + video: { vasttag: 'val' } }).forEach(([type, contents]) => { describe(`for ortb ${type} asset`, () => { let ortb; @@ -175,14 +175,14 @@ describe('Native creative renderer', () => { }; }); it('replaces placeholder', () => { - const repl = getReplacer('', {ortb}); + const repl = getReplacer('', { ortb }); expectReplacements(repl, { '##hb_native_asset_id_123##': 'val' }) }); it('replaces link placeholders', () => { - ortb.assets[0].link = {url: 'link'}; - const repl = getReplacer('', {ortb}); + ortb.assets[0].link = { url: 'link' }; + const repl = getReplacer('', { ortb }); expectReplacements(repl, { '##hb_native_asset_link_id_123##': 'link' }) @@ -209,7 +209,7 @@ describe('Native creative renderer', () => { }) function runRender() { - return render({adId, native: nativeData}, {sendMessage, exc}, win, getMarkup) + return render({ adId, native: nativeData }, { sendMessage, exc }, win, getMarkup) } it('replaces placeholders in head, if present', () => { @@ -217,7 +217,7 @@ describe('Native creative renderer', () => { win.document.head.innerHTML = '##hb_native_asset_id_1##'; nativeData.ortb = { assets: [ - {id: 1, data: {value: 'repl'}} + { id: 1, data: { value: 'repl' } } ] }; return runRender().then(() => { @@ -237,7 +237,7 @@ describe('Native creative renderer', () => { getMarkup.returns(Promise.resolve('markup')); return runRender().then(() => { expect(win.document.body.innerHTML).to.eql('markup'); - sinon.assert.calledWith(sendMessage, MESSAGE_NATIVE, {action: ACTION_IMP}); + sinon.assert.calledWith(sendMessage, MESSAGE_NATIVE, { action: ACTION_IMP }); }) }); @@ -289,24 +289,24 @@ describe('Native creative renderer', () => { it('immediately, if document is loaded', () => { win.document.readyState = 'complete'; return runRender().then(() => { - sinon.assert.calledWith(sendMessage, MESSAGE_NATIVE, {action: ACTION_RESIZE, height: 123, width: 321}) + sinon.assert.calledWith(sendMessage, MESSAGE_NATIVE, { action: ACTION_RESIZE, height: 123, width: 321 }) }) }); it('on document load otherwise', () => { return runRender().then(() => { - sinon.assert.neverCalledWith(sendMessage, MESSAGE_NATIVE, sinon.match({action: ACTION_RESIZE})); + sinon.assert.neverCalledWith(sendMessage, MESSAGE_NATIVE, sinon.match({ action: ACTION_RESIZE })); win.onload(); - sinon.assert.calledWith(sendMessage, MESSAGE_NATIVE, {action: ACTION_RESIZE, height: 123, width: 321}); + sinon.assert.calledWith(sendMessage, MESSAGE_NATIVE, { action: ACTION_RESIZE, height: 123, width: 321 }); }) }); it('uses scrollHeight if offsetHeight is 0', () => { win.document.body.offsetHeight = 0; - win.document.documentElement = {scrollHeight: 200}; + win.document.documentElement = { scrollHeight: 200 }; return runRender().then(() => { win.onload(); - sinon.assert.calledWith(sendMessage, MESSAGE_NATIVE, sinon.match({action: ACTION_RESIZE, height: 200})) + sinon.assert.calledWith(sendMessage, MESSAGE_NATIVE, sinon.match({ action: ACTION_RESIZE, height: 200 })) }) }) }) @@ -326,7 +326,7 @@ describe('Native creative renderer', () => { getMarkup.returns(Promise.resolve('

')); return runRender().then(() => { win.document.querySelector('#target').click(); - sinon.assert.calledWith(sendMessage, MESSAGE_NATIVE, sinon.match({action: ACTION_CLICK})); + sinon.assert.calledWith(sendMessage, MESSAGE_NATIVE, sinon.match({ action: ACTION_CLICK })); }) }); @@ -334,7 +334,7 @@ describe('Native creative renderer', () => { getMarkup.returns(Promise.resolve('
')); return runRender().then(() => { win.document.querySelector('#target').click(); - sinon.assert.calledWith(sendMessage, MESSAGE_NATIVE, {action: ACTION_CLICK, assetId: '123'}) + sinon.assert.calledWith(sendMessage, MESSAGE_NATIVE, { action: ACTION_CLICK, assetId: '123' }) }); }); }); diff --git a/test/spec/debugging_spec.js b/test/spec/debugging_spec.js index 8408ceec367..d707548cac6 100644 --- a/test/spec/debugging_spec.js +++ b/test/spec/debugging_spec.js @@ -1,6 +1,6 @@ -import {ready, loadSession, getConfig, reset, debuggingModuleLoader, debuggingControls} from '../../src/debugging.js'; -import {getGlobal} from '../../src/prebidGlobal.js'; -import {defer} from '../../src/utils/promise.js'; +import { ready, loadSession, getConfig, reset, debuggingModuleLoader, debuggingControls } from '../../src/debugging.js'; +import { getGlobal } from '../../src/prebidGlobal.js'; +import { defer } from '../../src/utils/promise.js'; import funHooks from 'fun-hooks/no-eval/index.js'; describe('Debugging', () => { @@ -20,7 +20,7 @@ describe('Debugging', () => { return scriptResult; }); alreadyInstalled = sinon.stub(); - loader = debuggingModuleLoader({alreadyInstalled, script}); + loader = debuggingModuleLoader({ alreadyInstalled, script }); }); afterEach(() => { @@ -70,7 +70,7 @@ describe('Debugging', () => { loader = defer(); hookRan = false; hook = funHooks()('sync', () => { hookRan = true }); - debugging = debuggingControls({load: sinon.stub().returns(loader.promise), hook}); + debugging = debuggingControls({ load: sinon.stub().returns(loader.promise), hook }); }) it('should delay execution of hook until module is loaded', () => { @@ -79,6 +79,8 @@ describe('Debugging', () => { expect(hookRan).to.be.false; loader.resolve(); return loader.promise.then(() => { + return Promise.resolve(); + }).then(() => { expect(hookRan).to.be.true; }); }); @@ -88,6 +90,17 @@ describe('Debugging', () => { debugging.disable(); hook(); expect(hookRan).to.be.true; + }); + + it('should continue when module cannot be loaded', () => { + debugging.enable(); + hook(); + loader.reject(); + return loader.promise.catch(() => { + return Promise.resolve(); + }).then(() => { + expect(hookRan).to.be.true; + }) }) }); }); diff --git a/test/spec/e2e/banner/basic_banner_ad.spec.js b/test/spec/e2e/banner/basic_banner_ad.spec.js index 91e7b16b1eb..f230c029ed9 100644 --- a/test/spec/e2e/banner/basic_banner_ad.spec.js +++ b/test/spec/e2e/banner/basic_banner_ad.spec.js @@ -1,5 +1,5 @@ const expect = require('chai').expect; -const {setupTest, testPageURL} = require('../../../helpers/testing-utils.js'); +const { setupTest, testPageURL } = require('../../../helpers/testing-utils.js'); const TEST_PAGE_URL = testPageURL('banner.html?pbjs_debug=true'); const SYNC_PAGE_URL = testPageURL('banner_sync.html?pbjs_debug=true'); diff --git a/test/spec/e2e/modules/e2e_bidderSettings.spec.js b/test/spec/e2e/modules/e2e_bidderSettings.spec.js index a27f2be0e72..f13ab309344 100644 --- a/test/spec/e2e/modules/e2e_bidderSettings.spec.js +++ b/test/spec/e2e/modules/e2e_bidderSettings.spec.js @@ -1,5 +1,5 @@ const expect = require('chai').expect; -const {testPageURL, setupTest} = require('../../../helpers/testing-utils.js'); +const { testPageURL, setupTest } = require('../../../helpers/testing-utils.js'); const TEST_PAGE_URL = testPageURL('bidderSettings.html?pbjs_debug=true'); const CREATIVE_IFRAME_CSS_SELECTOR = 'iframe[id="google_ads_iframe_/19968336/header-bid-tag-0_0"]'; diff --git a/test/spec/fingerprinting_spec.js b/test/spec/fingerprinting_spec.js new file mode 100644 index 00000000000..f1a810fc751 --- /dev/null +++ b/test/spec/fingerprinting_spec.js @@ -0,0 +1,60 @@ +import { expect } from 'chai'; + +import { config } from 'src/config.js'; +import { getDevicePixelRatio } from 'libraries/devicePixelRatio/devicePixelRatio.js'; +import { isWebdriverEnabled } from 'libraries/webdriver/webdriver.js'; +import { getTimeZone } from 'libraries/timezone/timezone.js'; + +describe('disableFingerprintingApis', function () { + after(function () { + config.resetConfig(); + }); + + it('when devicepixelratio is disabled, getDevicePixelRatio returns 1 without reading window.devicePixelRatio', function () { + const devicePixelRatioSpy = sinon.spy(); + const mockWin = { + get devicePixelRatio() { + devicePixelRatioSpy(); + return 2; + } + }; + config.setConfig({ disableFingerprintingApis: ['devicepixelratio'] }); + const result = getDevicePixelRatio(mockWin); + expect(result).to.equal(1); + sinon.assert.notCalled(devicePixelRatioSpy); + }); + + it('when webdriver is disabled, isWebdriverEnabled returns false without reading navigator.webdriver', function () { + const webdriverSpy = sinon.spy(); + const mockWin = { + navigator: { + get webdriver() { + webdriverSpy(); + return true; + } + } + }; + config.setConfig({ disableFingerprintingApis: ['webdriver'] }); + const result = isWebdriverEnabled(mockWin); + expect(result).to.equal(false); + sinon.assert.notCalled(webdriverSpy); + }); + + it('when resolvedoptions is disabled, getTimeZone returns safe default without calling Intl.DateTimeFormat', function () { + const resolvedOptionsSpy = sinon.spy(); + const dateTimeFormatStub = sinon.stub(Intl, 'DateTimeFormat').returns({ + resolvedOptions: function () { + resolvedOptionsSpy(); + return { timeZone: 'America/New_York' }; + } + }); + try { + config.setConfig({ disableFingerprintingApis: ['resolvedoptions'] }); + const result = getTimeZone(); + expect(result).to.equal(''); + sinon.assert.notCalled(resolvedOptionsSpy); + } finally { + dateTimeFormatStub.restore(); + } + }); +}); diff --git a/test/spec/fpd/enrichment_spec.js b/test/spec/fpd/enrichment_spec.js index 8c13da6f4e2..38fa8de6f33 100644 --- a/test/spec/fpd/enrichment_spec.js +++ b/test/spec/fpd/enrichment_spec.js @@ -1,13 +1,13 @@ -import {dep, enrichFPD, getJsonLdKeywords, getMetaTagKeywords} from '../../../src/fpd/enrichment.js'; -import {hook} from '../../../src/hook.js'; -import {expect} from 'chai/index.mjs'; -import {config} from 'src/config.js'; +import { dep, enrichFPD, getJsonLdKeywords, getMetaTagKeywords } from '../../../src/fpd/enrichment.js'; +import { hook } from '../../../src/hook.js'; +import { expect } from 'chai/index.mjs'; +import { config } from 'src/config.js'; import * as utils from 'src/utils.js'; import * as winDimensions from 'src/utils/winDimensions.js'; import * as activities from 'src/activities/rules.js' -import {CLIENT_SECTIONS} from '../../../src/fpd/oneClient.js'; -import {ACTIVITY_ACCESS_DEVICE} from '../../../src/activities/activities.js'; -import {ACTIVITY_PARAM_COMPONENT} from '../../../src/activities/params.js'; +import { CLIENT_SECTIONS } from '../../../src/fpd/oneClient.js'; +import { ACTIVITY_ACCESS_DEVICE } from '../../../src/activities/activities.js'; +import { ACTIVITY_PARAM_COMPONENT } from '../../../src/activities/params.js'; describe('FPD enrichment', () => { let sandbox; @@ -63,7 +63,7 @@ describe('FPD enrichment', () => { describe(`${section}, when set`, () => { let ortb2; beforeEach(() => { - ortb2 = {[section]: {ext: {}}} + ortb2 = { [section]: { ext: {} } } }) it('sets domain and publisher.domain', () => { @@ -188,7 +188,7 @@ describe('FPD enrichment', () => { ['dooh', 'app'].forEach(prop => { it(`should not be set when ${prop} is set`, () => { - return fpd({[prop]: {foo: 'bar'}}).then(ortb2 => { + return fpd({ [prop]: { foo: 'bar' } }).then(ortb2 => { expect(ortb2.site).to.not.exist; sinon.assert.notCalled(utils.logWarn); // make sure we don't generate "both site and app are set" warnings }) @@ -244,7 +244,7 @@ describe('FPD enrichment', () => { it('sets w/h', () => { const getWinDimensionsStub = sandbox.stub(winDimensions, 'getWinDimensions'); - getWinDimensionsStub.returns({screen: {width: 321, height: 123}}); + getWinDimensionsStub.returns({ screen: { width: 321, height: 123 } }); return fpd().then(ortb2 => { sinon.assert.match(ortb2.device, { w: 321, @@ -256,7 +256,7 @@ describe('FPD enrichment', () => { it('sets ext.vpw/vph', () => { const getWinDimensionsStub = sandbox.stub(winDimensions, 'getWinDimensions'); - getWinDimensionsStub.returns({innerWidth: 12, innerHeight: 21, screen: {}}); + getWinDimensionsStub.returns({ innerWidth: 12, innerHeight: 21, screen: {} }); return fpd().then(ortb2 => { sinon.assert.match(ortb2.device.ext, { vpw: 12, @@ -306,7 +306,7 @@ describe('FPD enrichment', () => { describe('coppa', () => { [[true, 1], [false, 0]].forEach(([cfgVal, regVal]) => { it(`is set to ${regVal} if config = ${cfgVal}`, () => { - config.setConfig({coppa: cfgVal}); + config.setConfig({ coppa: cfgVal }); return fpd().then(ortb2 => { expect(ortb2.regs.coppa).to.eql(regVal); }) @@ -335,25 +335,25 @@ describe('FPD enrichment', () => { }) }); it('uses low entropy values if uaHints is []', () => { - sandbox.stub(dep, 'getLowEntropySUA').callsFake(() => ({mock: 'sua'})); + sandbox.stub(dep, 'getLowEntropySUA').callsFake(() => ({ mock: 'sua' })); config.setConfig({ firstPartyData: { uaHints: [], } }) return fpd().then(ortb2 => { - expect(ortb2.device.sua).to.eql({mock: 'sua'}); + expect(ortb2.device.sua).to.eql({ mock: 'sua' }); }) }); it('uses high entropy values otherwise', () => { - sandbox.stub(dep, 'getHighEntropySUA').callsFake((hints) => Promise.resolve({hints})); + sandbox.stub(dep, 'getHighEntropySUA').callsFake((hints) => Promise.resolve({ hints })); config.setConfig({ firstPartyData: { uaHints: ['h1', 'h2'] } }); return fpd().then(ortb2 => { - expect(ortb2.device.sua).to.eql({hints: ['h1', 'h2']}) + expect(ortb2.device.sua).to.eql({ hints: ['h1', 'h2'] }) }) }); }); @@ -425,9 +425,9 @@ describe('FPD enrichment', () => { it('leaves only one of app, site, dooh', () => { return fpd({ - app: {p: 'val'}, - site: {p: 'val'}, - dooh: {p: 'val'} + app: { p: 'val' }, + site: { p: 'val' }, + dooh: { p: 'val' } }).then(ortb2 => { expect(ortb2.app).to.not.exist; expect(ortb2.site).to.not.exist; diff --git a/test/spec/fpd/gdpr_spec.js b/test/spec/fpd/gdpr_spec.js index a9d729e1c1e..5935bc5fd02 100644 --- a/test/spec/fpd/gdpr_spec.js +++ b/test/spec/fpd/gdpr_spec.js @@ -1,6 +1,6 @@ -import {gdprDataHandler} from '../../../src/adapterManager.js'; -import {enrichFPDHook} from '../../../modules/consentManagementTcf.js'; -import {config} from 'src/config.js'; +import { gdprDataHandler } from '../../../src/adapterManager.js'; +import { enrichFPDHook } from '../../../modules/consentManagementTcf.js'; +import { config } from 'src/config.js'; import 'src/prebid.js'; describe('GDPR FPD enrichment', () => { @@ -21,7 +21,7 @@ describe('GDPR FPD enrichment', () => { } it('sets gdpr properties from gdprDataHandler', () => { - consent = {gdprApplies: true, consentString: 'consent'}; + consent = { gdprApplies: true, consentString: 'consent' }; return callHook().then(ortb2 => { expect(ortb2.regs.ext.gdpr).to.eql(1); expect(ortb2.user.ext.consent).to.eql('consent'); @@ -35,7 +35,7 @@ describe('GDPR FPD enrichment', () => { }); it('sets user.ext.consent, but not regs.ext.gdpr, if gdprApplies is not a boolean', () => { - consent = {consentString: 'mock-consent'}; + consent = { consentString: 'mock-consent' }; return callHook().then(ortb2 => { expect(ortb2).to.eql({ user: { diff --git a/test/spec/fpd/normalize_spec.js b/test/spec/fpd/normalize_spec.js index 1f5dc1a51a1..92c92fdb303 100644 --- a/test/spec/fpd/normalize_spec.js +++ b/test/spec/fpd/normalize_spec.js @@ -1,6 +1,6 @@ -import {makeNormalizer, normalizeEIDs, normalizeFPD, normalizeSchain} from '../../../src/fpd/normalize.js'; +import { makeNormalizer, normalizeEIDs, normalizeFPD, normalizeSchain } from '../../../src/fpd/normalize.js'; import * as utils from '../../../src/utils.js'; -import {deepClone, deepSetValue} from '../../../src/utils.js'; +import { deepClone, deepSetValue } from '../../../src/utils.js'; import deepAccess from 'dlv/index.js'; describe('FPD normalization', () => { @@ -17,33 +17,33 @@ describe('FPD normalization', () => { it('should merge user.eids into user.ext.eids', () => { const fpd = { user: { - eids: [{source: 'idA'}], - ext: {eids: [{source: 'idB'}]} + eids: [{ source: 'idA' }], + ext: { eids: [{ source: 'idB' }] } } }; const result = normalizeEIDs(fpd); expect(result.user.eids).to.not.exist; expect(result.user.ext.eids).to.deep.have.members([ - {source: 'idA'}, - {source: 'idB'} + { source: 'idA' }, + { source: 'idB' } ]) }); it('should remove duplicates', () => { const fpd = { user: { - eids: [{source: 'id'}], - ext: {eids: [{source: 'id'}]} + eids: [{ source: 'id' }], + ext: { eids: [{ source: 'id' }] } } } expect(normalizeEIDs(fpd).user.ext.eids).to.eql([ - {source: 'id'} + { source: 'id' } ]) sinon.assert.called(utils.logWarn); }); it('should NOT remove duplicates if they come from the same place', () => { const fpd = { user: { - eids: [{source: 'id'}, {source: 'id'}] + eids: [{ source: 'id' }, { source: 'id' }] } } expect(normalizeEIDs(fpd).user.ext.eids.length).to.eql(2); @@ -83,7 +83,7 @@ describe('FPD normalization', () => { it('should do nothing if there is neither preferred nor fallback data', () => { ortb2.unrelated = ['data']; normalizer(ortb2); - expect(ortb2).to.eql({unrelated: ['data']}); + expect(ortb2).to.eql({ unrelated: ['data'] }); }) it(`should leave fpd unchanged if data is only in the ${t}`, () => { @@ -93,21 +93,21 @@ describe('FPD normalization', () => { }); it('should move data when it is in the fallback path', () => { - ortb2.fallback = {path: ['data']}; + ortb2.fallback = { path: ['data'] }; normalizer(ortb2); check(); }); it('should move data when it is in the preferred path', () => { - ortb2.preferred = {path: ['data']}; + ortb2.preferred = { path: ['data'] }; normalizer(ortb2); expect(deepAccess(ortb2, dest)).to.eql(deepAccess(expected, dest)); check(); }); it('should warn on conflict', () => { - ortb2.preferred = {path: ['data']}; - ortb2.fallback = {path: ['fallback']}; + ortb2.preferred = { path: ['data'] }; + ortb2.fallback = { path: ['fallback'] }; normalizer(ortb2); sinon.assert.called(utils.logWarn); check(); diff --git a/test/spec/fpd/oneClient_spec.js b/test/spec/fpd/oneClient_spec.js index 4ecde8d8a38..8c817fb9495 100644 --- a/test/spec/fpd/oneClient_spec.js +++ b/test/spec/fpd/oneClient_spec.js @@ -1,4 +1,4 @@ -import {clientSectionChecker} from '../../../src/fpd/oneClient.js'; +import { clientSectionChecker } from '../../../src/fpd/oneClient.js'; describe('onlyOneClientSection', () => { const oneClient = clientSectionChecker(); @@ -11,7 +11,7 @@ describe('onlyOneClientSection', () => { [['dooh', 'site'], 'dooh'] ].forEach(([sections, winner]) => { it(`should leave only ${winner} in request when it contains ${sections.join(', ')}`, () => { - const req = Object.fromEntries(sections.map(s => [s, {foo: 'bar'}])); + const req = Object.fromEntries(sections.map(s => [s, { foo: 'bar' }])); oneClient(req); expect(Object.keys(req)).to.eql([winner]); }) diff --git a/test/spec/fpd/rootDomain_spec.js b/test/spec/fpd/rootDomain_spec.js index b77f05d02c5..d21f4fea311 100644 --- a/test/spec/fpd/rootDomain_spec.js +++ b/test/spec/fpd/rootDomain_spec.js @@ -1,6 +1,6 @@ -import {expect} from 'chai/index.js'; -import {findRootDomain, coreStorage} from 'src/fpd/rootDomain.js'; -import {canSetCookie} from '../../../src/storageManager.js'; +import { expect } from 'chai/index.js'; +import { findRootDomain, coreStorage } from 'src/fpd/rootDomain.js'; +import { canSetCookie } from '../../../src/storageManager.js'; describe('findRootDomain', function () { let sandbox, cookies, rejectDomain; diff --git a/test/spec/fpd/sua_spec.js b/test/spec/fpd/sua_spec.js index 63e0068d0ef..e4aae2ea0c7 100644 --- a/test/spec/fpd/sua_spec.js +++ b/test/spec/fpd/sua_spec.js @@ -20,7 +20,7 @@ describe('uaDataToSUA', () => { it(`should not set ${suaKey} if ${uaKey} is missing from UAData`, () => { const example = { platform: 'Windows', - brands: [{brand: 'Mock', version: 'mk'}], + brands: [{ brand: 'Mock', version: 'mk' }], mobile: true, model: 'mockModel', bitness: '64', @@ -191,7 +191,7 @@ describe('lowEntropySUAAccessor', () => { }) it('should return mobile and source', () => { - expect(getSUA(new MockUserAgentData())).to.eql({mobile: 0, source: 1}) + expect(getSUA(new MockUserAgentData())).to.eql({ mobile: 0, source: 1 }) }) }); diff --git a/test/spec/fpd/usp_spec.js b/test/spec/fpd/usp_spec.js index ddffc5df1f8..0f8dd2dc24d 100644 --- a/test/spec/fpd/usp_spec.js +++ b/test/spec/fpd/usp_spec.js @@ -1,5 +1,5 @@ -import {enrichFPDHook} from '../../../modules/consentManagementUsp.js'; -import {uspDataHandler} from '../../../src/adapterManager.js'; +import { enrichFPDHook } from '../../../modules/consentManagementUsp.js'; +import { uspDataHandler } from '../../../src/adapterManager.js'; describe('FPD enrichment USP', () => { let sandbox, consent; diff --git a/test/spec/hook_spec.js b/test/spec/hook_spec.js index a42ffbb4bb6..ef826ed3da3 100644 --- a/test/spec/hook_spec.js +++ b/test/spec/hook_spec.js @@ -1,4 +1,4 @@ -import {hook as makeHook, ignoreCallbackArg} from '../../src/hook.js'; +import { hook as makeHook, ignoreCallbackArg } from '../../src/hook.js'; describe('hooks', () => { describe('ignoreCallbackArg', () => { diff --git a/test/spec/integration/faker/googletag.js b/test/spec/integration/faker/googletag.js index a8676b500a5..4b8016c0ef1 100644 --- a/test/spec/integration/faker/googletag.js +++ b/test/spec/integration/faker/googletag.js @@ -44,7 +44,7 @@ export function makeSlot() { } export function emitEvent(eventName, params) { - (window.googletag._callbackMap[eventName] || []).forEach(eventCb => eventCb({...params, eventName})); + (window.googletag._callbackMap[eventName] || []).forEach(eventCb => eventCb({ ...params, eventName })); } export function enable() { diff --git a/test/spec/keywords_spec.js b/test/spec/keywords_spec.js index e048ead3b98..db265d7e43c 100644 --- a/test/spec/keywords_spec.js +++ b/test/spec/keywords_spec.js @@ -1,4 +1,4 @@ -import {getAllOrtbKeywords, mergeKeywords} from '../../libraries/keywords/keywords.js'; +import { getAllOrtbKeywords, mergeKeywords } from '../../libraries/keywords/keywords.js'; describe('mergeKeywords', () => { Object.entries({ @@ -66,7 +66,7 @@ describe('mergeKeywords', () => { 'three' ] } - }).forEach(([t, {input, output}]) => { + }).forEach(([t, { input, output }]) => { it(`can merge ${t}`, () => { expect(mergeKeywords(...input)).to.have.members(output); }) diff --git a/test/spec/libraries/autoplayDetection_spec.js b/test/spec/libraries/autoplayDetection_spec.js index 88c55f2b7d7..51780a245f0 100644 --- a/test/spec/libraries/autoplayDetection_spec.js +++ b/test/spec/libraries/autoplayDetection_spec.js @@ -1,4 +1,4 @@ -import {expect} from 'chai'; +import { expect } from 'chai'; import sinon from 'sinon'; function loadAutoplay() { diff --git a/test/spec/libraries/bidViewabilityPixels_spec.js b/test/spec/libraries/bidViewabilityPixels_spec.js new file mode 100644 index 00000000000..05f248c30ba --- /dev/null +++ b/test/spec/libraries/bidViewabilityPixels_spec.js @@ -0,0 +1,178 @@ +import { fireViewabilityPixels, getViewabilityTrackersFromBid } from 'libraries/bidViewabilityPixels/index.js'; +import * as utils from 'src/utils.js'; +import * as sinon from 'sinon'; +import { expect } from 'chai'; +import { EVENT_TYPE_IMPRESSION, EVENT_TYPE_VIEWABLE, TRACKER_METHOD_IMG, TRACKER_METHOD_JS } from 'src/eventTrackers.js'; +import { auctionManager } from 'src/auctionManager.js'; + +const VIEWABILITY_PIXEL_URLS = [ + 'https://domain-1.com/end-point?a=1', + 'https://domain-2.com/end-point/', + 'https://domain-3.com/end-point?a=1' +]; + +const bidWithViewabilityTrackers = { + adUnitCode: 'test-unit', + bidder: 'test-bidder', + eventtrackers: VIEWABILITY_PIXEL_URLS.map(url => ({ + event: EVENT_TYPE_VIEWABLE, + method: TRACKER_METHOD_IMG, + url + })) +}; + +describe('bidViewabilityPixels library', function () { + let sandbox; + let triggerPixelSpy; + let insertHtmlIntoIframeSpy; + + beforeEach(function () { + sandbox = sinon.createSandbox(); + triggerPixelSpy = sandbox.spy(utils, 'triggerPixel'); + insertHtmlIntoIframeSpy = sandbox.spy(utils, 'insertHtmlIntoIframe'); + }); + + afterEach(function () { + sandbox.restore(); + }); + + describe('getViewabilityTrackersFromBid', function () { + it('should return { img: [], js: [] } when bid is null, undefined, or has no valid eventtrackers', function () { + const empty = { img: [], js: [] }; + expect(getViewabilityTrackersFromBid(null)).to.deep.equal(empty); + expect(getViewabilityTrackersFromBid(undefined)).to.deep.equal(empty); + expect(getViewabilityTrackersFromBid({ adUnitCode: 'x' })).to.deep.equal(empty); + expect(getViewabilityTrackersFromBid({ eventtrackers: {} })).to.deep.equal(empty); + expect(getViewabilityTrackersFromBid({ eventtrackers: 'trackers' })).to.deep.equal(empty); + expect(getViewabilityTrackersFromBid({ eventtrackers: [] })).to.deep.equal(empty); + }); + + it('should return img and js URLs for viewable trackers', function () { + const bid = { + eventtrackers: [ + { event: EVENT_TYPE_VIEWABLE, method: TRACKER_METHOD_IMG, url: 'https://img.com' }, + { event: EVENT_TYPE_VIEWABLE, method: TRACKER_METHOD_JS, url: 'https://js.com' } + ] + }; + expect(getViewabilityTrackersFromBid(bid)).to.deep.equal({ + img: ['https://img.com'], + js: ['https://js.com'] + }); + }); + + it('should return only viewable trackers when eventtrackers has mixed event types', function () { + const bid = { + eventtrackers: [ + { event: EVENT_TYPE_VIEWABLE, method: TRACKER_METHOD_IMG, url: 'https://viewable.com' }, + { event: EVENT_TYPE_IMPRESSION, method: TRACKER_METHOD_IMG, url: 'https://impression.com' } + ] + }; + expect(getViewabilityTrackersFromBid(bid)).to.deep.equal({ + img: ['https://viewable.com'], + js: [] + }); + }); + + it('should return only js when viewable trackers have JS method only', function () { + const bid = { + eventtrackers: [ + { event: EVENT_TYPE_VIEWABLE, method: TRACKER_METHOD_JS, url: 'https://viewable-js.com' } + ] + }; + expect(getViewabilityTrackersFromBid(bid)).to.deep.equal({ + img: [], + js: ['https://viewable-js.com'] + }); + }); + }); + + describe('fireViewabilityPixels', function () { + it('should not call triggerPixel or insertHtmlIntoIframe when bid has no eventtrackers', function () { + fireViewabilityPixels({ adUnitCode: 'x' }); + expect(triggerPixelSpy.callCount).to.equal(0); + expect(insertHtmlIntoIframeSpy.callCount).to.equal(0); + }); + + it('should not call triggerPixel or insertHtmlIntoIframe when eventtrackers is empty array', function () { + fireViewabilityPixels({ eventtrackers: [] }); + expect(triggerPixelSpy.callCount).to.equal(0); + expect(insertHtmlIntoIframeSpy.callCount).to.equal(0); + }); + + it('should fire one pixel per viewable img URL in eventtrackers', function () { + fireViewabilityPixels(bidWithViewabilityTrackers); + expect(triggerPixelSpy.callCount).to.equal(VIEWABILITY_PIXEL_URLS.length); + VIEWABILITY_PIXEL_URLS.forEach((url, i) => { + expect(triggerPixelSpy.getCall(i).args[0]).to.equal(url); + }); + }); + + it('should fire viewable JS trackers via insertHtmlIntoIframe with script tag', function () { + const bid = { + eventtrackers: [ + { event: EVENT_TYPE_VIEWABLE, method: TRACKER_METHOD_JS, url: 'https://viewable-js.com' } + ] + }; + fireViewabilityPixels(bid); + expect(triggerPixelSpy.callCount).to.equal(0); + expect(insertHtmlIntoIframeSpy.callCount).to.equal(1); + expect(insertHtmlIntoIframeSpy.getCall(0).args[0]).to.include('script async src="https://viewable-js.com"'); + }); + + it('should fire both img (triggerPixel) and js (insertHtmlIntoIframe) viewable trackers', function () { + const bid = { + eventtrackers: [ + { event: EVENT_TYPE_VIEWABLE, method: TRACKER_METHOD_IMG, url: 'https://img.com' }, + { event: EVENT_TYPE_VIEWABLE, method: TRACKER_METHOD_JS, url: 'https://js.com' } + ] + }; + fireViewabilityPixels(bid); + expect(triggerPixelSpy.callCount).to.equal(1); + expect(triggerPixelSpy.getCall(0).args[0]).to.equal('https://img.com'); + expect(insertHtmlIntoIframeSpy.callCount).to.equal(1); + expect(insertHtmlIntoIframeSpy.getCall(0).args[0]).to.include('script async src="https://js.com"'); + }); + + it('should only fire EVENT_TYPE_VIEWABLE URLs', function () { + const bid = { + eventtrackers: [ + { event: EVENT_TYPE_VIEWABLE, method: TRACKER_METHOD_IMG, url: 'https://viewable-img.com' }, + { event: EVENT_TYPE_IMPRESSION, method: TRACKER_METHOD_IMG, url: 'https://impression.com' } + ] + }; + fireViewabilityPixels(bid); + expect(triggerPixelSpy.callCount).to.equal(1); + expect(triggerPixelSpy.getCall(0).args[0]).to.equal('https://viewable-img.com'); + }); + + describe('when bid has native response with eventtrackers (viewable)', function () { + let indexStub; + let getMediaTypesStub; + + beforeEach(function () { + getMediaTypesStub = sinon.stub(); + indexStub = sandbox.stub(auctionManager, 'index').get(() => ({ getMediaTypes: getMediaTypesStub })); + }); + + it('should fire viewable trackers from bid.native.ortb.eventtrackers in addition to bid.eventtrackers', function () { + getMediaTypesStub.returns({}); + const bid = { + eventtrackers: [ + { event: EVENT_TYPE_VIEWABLE, method: TRACKER_METHOD_IMG, url: 'https://from-bid.com' } + ], + native: { + ortb: { + eventtrackers: [ + { event: EVENT_TYPE_VIEWABLE, method: TRACKER_METHOD_IMG, url: 'https://from-native.com' } + ] + } + } + }; + fireViewabilityPixels(bid); + expect(triggerPixelSpy.callCount).to.equal(2); + expect(triggerPixelSpy.getCall(0).args[0]).to.equal('https://from-bid.com'); + expect(triggerPixelSpy.getCall(1).args[0]).to.equal('https://from-native.com'); + }); + }); + }); +}); diff --git a/test/spec/libraries/boundingClientRect_spec.js b/test/spec/libraries/boundingClientRect_spec.js index ecf82876bb5..7f17a058520 100644 --- a/test/spec/libraries/boundingClientRect_spec.js +++ b/test/spec/libraries/boundingClientRect_spec.js @@ -39,7 +39,7 @@ describe('getBoundingClientRect', () => { adUnits: [], ttlBuffer: 1000, auctionId: '909090', - defer: {resolve: () => {}} + defer: { resolve: () => {} } }; getBoundingClientRect(element); diff --git a/test/spec/libraries/cmUtils_spec.js b/test/spec/libraries/cmUtils_spec.js index be2e31a8e18..4cd7276bd90 100644 --- a/test/spec/libraries/cmUtils_spec.js +++ b/test/spec/libraries/cmUtils_spec.js @@ -1,5 +1,5 @@ import * as utils from 'src/utils.js'; -import {lookupConsentData, consentManagementHook, configParser} from '../../../libraries/consentManagement/cmUtils.js'; +import { lookupConsentData, consentManagementHook, configParser } from '../../../libraries/consentManagement/cmUtils.js'; describe('consent management utils', () => { let sandbox, clock; @@ -28,7 +28,7 @@ describe('consent management utils', () => { } }, 'an error with args': { - error: Object.assign(new Error('mock-error'), {args: ['arg1', 'arg2']}), + error: Object.assign(new Error('mock-error'), { args: ['arg1', 'arg2'] }), check(logger) { sinon.assert.calledWith(logger, sinon.match('mock-error'), 'arg1', 'arg2'); } @@ -37,7 +37,7 @@ describe('consent management utils', () => { check() { } } - }).forEach(([errorDesc, {error, check: checkLogs}]) => { + }).forEach(([errorDesc, { error, check: checkLogs }]) => { describe(`when loadConsentData rejects with ${errorDesc}`, () => { beforeEach(async () => { loadResult = Promise.reject(error); @@ -47,7 +47,7 @@ describe('consent management utils', () => { }); it('should log an error and run bidsBackHandler', async () => { const bidsBackHandler = sinon.stub(); - cmHook(next, {bidsBackHandler}); + cmHook(next, { bidsBackHandler }); await loadResult.catch(() => null); sinon.assert.calledWith(utils.logError, sinon.match('Canceling auction')); sinon.assert.notCalled(next); @@ -61,10 +61,10 @@ describe('consent management utils', () => { }); describe(`when loadConsentData resolves with ${errorDesc}`, () => { function setupError() { - loadResult = Promise.resolve({error}); + loadResult = Promise.resolve({ error }); } function setupConsentAndError() { - loadResult = Promise.resolve({consentData: {'consent': 'data'}, error}); + loadResult = Promise.resolve({ consentData: { 'consent': 'data' }, error }); } Object.entries({ 'with': setupConsentAndError, @@ -73,9 +73,9 @@ describe('consent management utils', () => { describe(`${t} consent`, () => { beforeEach(setup); it('should log a warning and continue auction', async () => { - cmHook(next, {auction: 'args'}); + cmHook(next, { auction: 'args' }); await loadResult; - sinon.assert.calledWith(next, {auction: 'args'}); + sinon.assert.calledWith(next, { auction: 'args' }); checkLogs(utils.logWarn); }); }); @@ -157,14 +157,14 @@ describe('consent management utils', () => { beforeEach(() => { cmpTimeout = timeout; setupCmp.callsFake(() => { - consentDataHandler.getConsentData.returns({consent: 'data'}); + consentDataHandler.getConsentData.returns({ consent: 'data' }); return Promise.resolve(); }); }); it(`should resolve if cmp handler resolves`, async () => { - const {consentData, error} = await runLookup(); - expect(consentData).to.eql({consent: 'data'}); + const { consentData, error } = await runLookup(); + expect(consentData).to.eql({ consent: 'data' }); expect(error).to.not.exist; }); @@ -191,9 +191,9 @@ describe('consent management utils', () => { cmpTimeout = timeout; const lookup = runLookup(); clock.tick(timeout + 1); - const {consentData, error} = await lookup; - sinon.assert.calledWith(consentDataHandler.setConsentData, {consent: null}); - expect(consentData).to.eql({consent: null}) + const { consentData, error } = await lookup; + sinon.assert.calledWith(consentDataHandler.setConsentData, { consent: null }); + expect(consentData).to.eql({ consent: null }) expect(error.message).to.match(/.*CMP to load.*/) }); }); @@ -203,10 +203,10 @@ describe('consent management utils', () => { actionTimeout = timeout; const lookup = runLookup(); clock.tick(10); - setProvisionalConsent({consent: 'provisional'}); + setProvisionalConsent({ consent: 'provisional' }); clock.tick(timeout + 1); - const {consentData, error} = await lookup; - expect(consentData).to.eql({consent: 'provisional'}); + const { consentData, error } = await lookup; + expect(consentData).to.eql({ consent: 'provisional' }); expect(error.message).to.match(/.*action.*/) }); }); @@ -217,12 +217,12 @@ describe('consent management utils', () => { const lookup = runLookup().then((res) => { consentData = res.consentData; }); - setProvisionalConsent({consent: 1}); + setProvisionalConsent({ consent: 1 }); clock.tick(20); - setProvisionalConsent({consent: 2}); + setProvisionalConsent({ consent: 2 }); clock.tick(80); await lookup; - expect(consentData).to.eql({consent: 2}); + expect(consentData).to.eql({ consent: 2 }); }) }); }); @@ -242,7 +242,7 @@ describe('consent management utils', () => { setConsentData: sinon.stub() }; parseConsentData = sinon.stub().callsFake(data => data); - getNullConsent = sinon.stub().returns({consent: null}); + getNullConsent = sinon.stub().returns({ consent: null }); cmpHandlers = { iab: sinon.stub().returns(Promise.resolve()) }; @@ -269,21 +269,21 @@ describe('consent management utils', () => { }); it('should reset and return empty object when config is not an object', () => { - const result = getConsentConfig({[namespace]: 'not an object'}); + const result = getConsentConfig({ [namespace]: 'not an object' }); expect(result).to.deep.equal({}); sinon.assert.calledWith(utils.logWarn, sinon.match('config not defined')); }); describe('when module is explicitly disabled', () => { it('should reset consent data handler and return empty object when enabled is false', () => { - const result = getConsentConfig({[namespace]: {enabled: false}}); + const result = getConsentConfig({ [namespace]: { enabled: false } }); expect(result).to.deep.equal({}); sinon.assert.calledWith(utils.logWarn, sinon.match('config enabled is set to false')); }); it('should call cmpEventCleanup when enabled is false', () => { - getConsentConfig({[namespace]: {enabled: false}}); + getConsentConfig({ [namespace]: { enabled: false } }); sinon.assert.called(cmpEventCleanup); sinon.assert.calledWith(utils.logWarn, sinon.match('config enabled is set to false')); @@ -293,20 +293,20 @@ describe('consent management utils', () => { const cleanupError = new Error('Cleanup failed'); cmpEventCleanup.throws(cleanupError); - getConsentConfig({[namespace]: {enabled: false}}); + getConsentConfig({ [namespace]: { enabled: false } }); sinon.assert.called(cmpEventCleanup); sinon.assert.calledWith(utils.logError, sinon.match('Error during CMP event cleanup'), cleanupError); }); it('should not call cmpEventCleanup when enabled is true', () => { - getConsentConfig({[namespace]: {enabled: true, cmpApi: 'iab'}}); + getConsentConfig({ [namespace]: { enabled: true, cmpApi: 'iab' } }); sinon.assert.notCalled(cmpEventCleanup); }); it('should not call cmpEventCleanup when enabled is not specified', () => { - getConsentConfig({[namespace]: {cmpApi: 'iab'}}); + getConsentConfig({ [namespace]: { cmpApi: 'iab' } }); sinon.assert.notCalled(cmpEventCleanup); }); @@ -324,7 +324,7 @@ describe('consent management utils', () => { // No cmpEventCleanup provided }); - const result = configParserWithoutCleanup({[namespace]: {enabled: false}}); + const result = configParserWithoutCleanup({ [namespace]: { enabled: false } }); expect(result).to.deep.equal({}); // Should not throw error when cmpEventCleanup is undefined }); @@ -340,7 +340,7 @@ describe('consent management utils', () => { cmpEventCleanup: 'not a function' }); - const result = configParserWithNonFunction({[namespace]: {enabled: false}}); + const result = configParserWithNonFunction({ [namespace]: { enabled: false } }); expect(result).to.deep.equal({}); // Should not throw error when cmpEventCleanup is not a function }); diff --git a/test/spec/libraries/cmp/cmpClient_spec.js b/test/spec/libraries/cmp/cmpClient_spec.js index 7f78aa598fd..f647a1fa9b6 100644 --- a/test/spec/libraries/cmp/cmpClient_spec.js +++ b/test/spec/libraries/cmp/cmpClient_spec.js @@ -1,4 +1,4 @@ -import {cmpClient, MODE_CALLBACK, MODE_RETURN} from '../../../../libraries/cmp/cmpClient.js'; +import { cmpClient, MODE_CALLBACK, MODE_RETURN } from '../../../../libraries/cmp/cmpClient.js'; describe('cmpClient', () => { function mockWindow(props = {}) { @@ -15,7 +15,7 @@ describe('cmpClient', () => { } }), postMessage: sinon.stub().callsFake((msg) => { - listeners.forEach(ln => ln({data: msg})) + listeners.forEach(ln => ln({ data: msg })) }), ...props, }; @@ -24,13 +24,13 @@ describe('cmpClient', () => { } it('should return undefined when there is no CMP', () => { - expect(cmpClient({apiName: 'missing'}, mockWindow())).to.not.exist; + expect(cmpClient({ apiName: 'missing' }, mockWindow())).to.not.exist; }); it('should return undefined when parent is inaccessible', () => { const win = mockWindow(); win.top = mockWindow(); - expect(cmpClient({apiName: 'missing'}, win)).to.not.exist; + expect(cmpClient({ apiName: 'missing' }, win)).to.not.exist; }) describe('direct access', () => { @@ -39,14 +39,14 @@ describe('cmpClient', () => { mockApiFn = sinon.stub(); }) Object.entries({ - 'on same frame': () => mockWindow({mockApiFn}), - 'on parent frame': () => mockWindow({parent: mockWindow({parent: mockWindow({parent: mockWindow(), mockApiFn})})}), + 'on same frame': () => mockWindow({ mockApiFn }), + 'on parent frame': () => mockWindow({ parent: mockWindow({ parent: mockWindow({ parent: mockWindow(), mockApiFn }) }) }), }).forEach(([t, mkWindow]) => { describe(t, () => { let win, mkClient; beforeEach(() => { win = mkWindow(); - mkClient = (opts) => cmpClient(Object.assign({apiName: 'mockApiFn'}, opts), win) + mkClient = (opts) => cmpClient(Object.assign({ apiName: 'mockApiFn' }, opts), win) }); it('should mark client function as direct', () => { @@ -54,7 +54,7 @@ describe('cmpClient', () => { }); it('should find and call the CMP api function', () => { - mkClient()({command: 'mockCmd'}); + mkClient()({ command: 'mockCmd' }); sinon.assert.calledWith(mockApiFn, 'mockCmd'); }); @@ -85,14 +85,14 @@ describe('cmpClient', () => { }).forEach(([t, success]) => { it(`resolves to ${tResult} (${t})`, (done) => { cbResult = ['cbVal', success]; - mkClient({mode})({callback}).then((val) => { + mkClient({ mode })({ callback }).then((val) => { expect(val).to.equal(expectedResult); done(); }) }); it('should pass either a function or undefined as callback', () => { - mkClient({mode})({callback}); + mkClient({ mode })({ callback }); sinon.assert.calledWith(mockApiFn, sinon.match.any, sinon.match(arg => typeof arg === 'undefined' || typeof arg === 'function')) }) }); @@ -101,7 +101,7 @@ describe('cmpClient', () => { it('rejects to undefined when callback is provided and success = false', (done) => { cbResult = ['cbVal', false]; - mkClient()({callback: sinon.stub()}).catch(val => { + mkClient()({ callback: sinon.stub() }).catch(val => { expect(val).to.not.exist; done(); }) @@ -109,7 +109,7 @@ describe('cmpClient', () => { it('rejects to callback arg when callback is NOT provided, success = false, mode = MODE_CALLBACK', (done) => { cbResult = ['cbVal', false]; - mkClient({mode: MODE_CALLBACK})().catch(val => { + mkClient({ mode: MODE_CALLBACK })().catch(val => { expect(val).to.eql('cbVal'); done(); }) @@ -128,7 +128,7 @@ describe('cmpClient', () => { }) it('should use apiArgs to choose and order the arguments to pass to the API fn', () => { - mkClient({apiArgs: ['parameter', 'command']})({ + mkClient({ apiArgs: ['parameter', 'command'] })({ command: 'mockCmd', parameter: 'mockParam', callback() {} @@ -149,22 +149,22 @@ describe('cmpClient', () => { response = {}; messenger = sinon.stub().callsFake((msg) => { if (msg.mockApiCall) { - win.postMessage({mockApiReturn: {callId: msg.mockApiCall.callId, ...response}}); + win.postMessage({ mockApiReturn: { callId: msg.mockApiCall.callId, ...response } }); } }); }); function mkClient(options) { - return cmpClient(Object.assign({apiName: 'mockApi'}, options), win); + return cmpClient(Object.assign({ apiName: 'mockApi' }, options), win); } Object.entries({ 'on same frame': () => { - win = mockWindow({frames: {mockApiLocator: true}}); + win = mockWindow({ frames: { mockApiLocator: true } }); win.addEventListener('message', (evt) => messenger(evt.data)); }, 'on parent frame': () => { - win = mockWindow({parent: mockWindow({frames: {mockApiLocator: true}})}) + win = mockWindow({ parent: mockWindow({ frames: { mockApiLocator: true } }) }) win.parent.addEventListener('message', evt => messenger(evt.data)) } }).forEach(([t, setup]) => { @@ -176,7 +176,7 @@ describe('cmpClient', () => { }); it('should find and message the CMP frame', () => { - mkClient()({command: 'mockCmd', parameter: 'param'}); + mkClient()({ command: 'mockCmd', parameter: 'param' }); sinon.assert.calledWithMatch(messenger, { mockApiCall: { command: 'mockCmd', @@ -186,7 +186,7 @@ describe('cmpClient', () => { }); it('should use apiArgs to choose what to include in the message payload', () => { - mkClient({apiArgs: ['command']})({ + mkClient({ apiArgs: ['command'] })({ command: 'cmd', parameter: 'param' }); @@ -198,7 +198,7 @@ describe('cmpClient', () => { it('should not include callback in the payload, but still run it on response', () => { const cb = sinon.stub(); - mkClient({apiArgs: ['command', 'callback']})({ + mkClient({ apiArgs: ['command', 'callback'] })({ command: 'cmd', callback: cb }); @@ -208,14 +208,14 @@ describe('cmpClient', () => { it('should use callbackArgs to decide what to pass to callback', () => { const cb = sinon.stub(); - response = {a: 'one', b: 'two'}; - mkClient({callbackArgs: ['a', 'b']})({callback: cb}); + response = { a: 'one', b: 'two' }; + mkClient({ callbackArgs: ['a', 'b'] })({ callback: cb }); sinon.assert.calledWith(cb, 'one', 'two'); }) describe('should return a promise that', () => { beforeEach(() => { - response = {returnValue: 'val'} + response = { returnValue: 'val' } }) Object.entries({ 'callback': [sinon.stub(), 'undefined', undefined], @@ -228,11 +228,11 @@ describe('cmpClient', () => { describe(`when ${t} is provided`, () => { Object.entries({ 'no success flag': {}, - 'with success flag': {success: true} + 'with success flag': { success: true } }).forEach(([t, resp]) => { it(`resolves to ${tResult} (${t})`, () => { Object.assign(response, resp); - mkClient({mode})({callback}).then((val) => { + mkClient({ mode })({ callback }).then((val) => { expect(val).to.equal(expectedResult); }) }) @@ -241,7 +241,7 @@ describe('cmpClient', () => { if (mode !== MODE_RETURN) { // in return mode, the promise never rejects it(`rejects to ${tResult} when success = false`, (done) => { response.success = false; - mkClient()({mode, callback}).catch((err) => { + mkClient()({ mode, callback }).catch((err) => { expect(err).to.equal(expectedResult); done(); }); @@ -255,7 +255,7 @@ describe('cmpClient', () => { let callback, callId; function runCallback(returnValue) { - win.postMessage({mockApiReturn: {callId, returnValue}}); + win.postMessage({ mockApiReturn: { callId, returnValue } }); } beforeEach(() => { @@ -269,7 +269,7 @@ describe('cmpClient', () => { }); it('should re-use callback for messages with same callId', () => { - mkClient()({callback}); + mkClient()({ callback }); expect(callId).to.exist; runCallback('a'); runCallback('b'); @@ -278,7 +278,7 @@ describe('cmpClient', () => { }); it('should NOT re-use callback if once = true', () => { - mkClient()({callback}, true); + mkClient()({ callback }, true); expect(callId).to.exist; runCallback('a'); runCallback('b'); @@ -288,7 +288,7 @@ describe('cmpClient', () => { it('should NOT fire again after .close()', () => { const client = mkClient(); - client({callback}); + client({ callback }); runCallback('a'); client.close(); runCallback('b'); diff --git a/test/spec/libraries/currencyUtils_spec.js b/test/spec/libraries/currencyUtils_spec.js index 7eed7a5aacb..be114bfa5d4 100644 --- a/test/spec/libraries/currencyUtils_spec.js +++ b/test/spec/libraries/currencyUtils_spec.js @@ -1,5 +1,5 @@ -import {getGlobal} from 'src/prebidGlobal.js'; -import {convertCurrency, currencyCompare, currencyNormalizer} from 'libraries/currencyUtils/currency.js'; +import { getGlobal } from 'src/prebidGlobal.js'; +import { convertCurrency, currencyCompare, currencyNormalizer } from 'libraries/currencyUtils/currency.js'; describe('currency utils', () => { let sandbox; @@ -99,9 +99,9 @@ describe('currency utils', () => { compare = currencyCompare((val) => [val.amount, val.cur], currencyNormalizer(null, false, mockConvert)) }); [ - [{amount: 1, cur: 1}, {amount: 1, cur: 10}, 1], - [{amount: 10, cur: 1}, {amount: 0.1, cur: 100}, 1], - [{amount: 1, cur: 1}, {amount: 10, cur: 10}, 0], + [{ amount: 1, cur: 1 }, { amount: 1, cur: 10 }, 1], + [{ amount: 10, cur: 1 }, { amount: 0.1, cur: 100 }, 1], + [{ amount: 1, cur: 1 }, { amount: 10, cur: 10 }, 0], ].forEach(([a, b, expected]) => { it(`should compare ${a.amount}/${a.cur} and ${b.amount}/${b.cur}`, () => { expect(compare(a, b)).to.equal(expected); diff --git a/test/spec/libraries/dnt_spec.js b/test/spec/libraries/dnt_spec.js deleted file mode 100644 index 177542dca31..00000000000 --- a/test/spec/libraries/dnt_spec.js +++ /dev/null @@ -1,34 +0,0 @@ -import {getDNT} from '../../../libraries/dnt/index.js'; - -describe('dnt helper', () => { - let win; - beforeEach(() => { - win = {}; - }); - - [ - 'top', - 'doNotTrack', - 'navigator', - 'navigator.doNotTrack', - 'top.doNotTrack', - 'top.navigator.doNotTrack' - ].forEach(path => { - it(`should not choke if ${path} throws`, () => { - path = path.split('.'); - path.reduce((parent, name, i) => { - if (i === path.length - 1) { - Object.defineProperty(parent, name, { - get() { - throw new Error(); - } - }) - } else { - parent = parent[name] = {}; - } - return parent; - }, win); - expect(getDNT(win)).to.be.false; - }) - }) -}) diff --git a/test/spec/libraries/domainOverrideToRootDomain/index_spec.js b/test/spec/libraries/domainOverrideToRootDomain/index_spec.js index d06c725803f..6109b04d97c 100644 --- a/test/spec/libraries/domainOverrideToRootDomain/index_spec.js +++ b/test/spec/libraries/domainOverrideToRootDomain/index_spec.js @@ -1,6 +1,6 @@ -import {domainOverrideToRootDomain} from 'libraries/domainOverrideToRootDomain/index.js'; -import {getStorageManager} from 'src/storageManager.js'; -import {MODULE_TYPE_UID} from '../../../../src/activities/modules.js'; +import { domainOverrideToRootDomain } from 'libraries/domainOverrideToRootDomain/index.js'; +import { getStorageManager } from 'src/storageManager.js'; +import { MODULE_TYPE_UID } from '../../../../src/activities/modules.js'; const storage = getStorageManager({ moduleName: 'test', moduleType: MODULE_TYPE_UID }); const domainOverride = domainOverrideToRootDomain(storage, 'test'); diff --git a/test/spec/libraries/gamPredictionReport_spec.js b/test/spec/libraries/gamPredictionReport_spec.js new file mode 100644 index 00000000000..65690f9ccd3 --- /dev/null +++ b/test/spec/libraries/gamPredictionReport_spec.js @@ -0,0 +1,114 @@ +import { expect } from 'chai'; +import sinon from 'sinon'; +import * as events from 'src/events.js'; +import * as utils from 'src/utils.js'; +import { gamPredictionReport } from '../../../libraries/intentIqUtils/gamPredictionReport.js'; + +describe('gamPredictionReport', function () { + let getEventsStub; + let logErrorStub; + + beforeEach(() => { + getEventsStub = sinon.stub(events, 'getEvents').returns([]); + logErrorStub = sinon.stub(utils, 'logError'); + }); + + afterEach(() => { + getEventsStub.restore(); + logErrorStub.restore(); + }); + + function runWithSlot(slot, sendData) { + let handler; + const gamObjectReference = { + cmd: [], + pubads: () => ({ + addEventListener: (eventName, callback) => { + handler = callback; + } + }) + }; + + gamPredictionReport(gamObjectReference, sendData); + gamObjectReference.cmd.forEach((fn) => fn()); + handler({ isEmpty: false, slot }); + } + + it('reads targeting from slot.getConfig targeting wrapper', () => { + const sendData = sinon.spy(); + const slot = { + getConfig: sinon.stub().withArgs('targeting').returns({ targeting: { hb_bidder: ['test'] } }), + getTargetingKeys: sinon.stub().throws(new Error('deprecated')), + getTargeting: sinon.stub().throws(new Error('deprecated')), + getSlotElementId: () => 'div-1', + getAdUnitPath: () => '/123' + }; + + runWithSlot(slot, sendData); + + expect(sendData.calledOnce).to.equal(true); + expect(sendData.firstCall.args[0].bidderCode).to.equal('test'); + }); + + it('reads targeting from slot.getConfig flat object', () => { + const sendData = sinon.spy(); + const slot = { + getConfig: sinon.stub().withArgs('targeting').returns({ hb_bidder: ['flat'] }), + getSlotElementId: () => 'div-2', + getAdUnitPath: () => '/456' + }; + + runWithSlot(slot, sendData); + + expect(sendData.calledOnce).to.equal(true); + expect(sendData.firstCall.args[0].bidderCode).to.equal('flat'); + }); + + it('reads targeting from legacy slot.getTargeting APIs when getConfig is missing', () => { + const sendData = sinon.spy(); + const slot = { + getTargetingKeys: sinon.stub().returns(['hb_bidder']), + getTargeting: sinon.stub().withArgs('hb_bidder').returns(['legacy']), + getSlotElementId: () => 'div-3', + getAdUnitPath: () => '/789' + }; + + runWithSlot(slot, sendData); + + expect(sendData.calledOnce).to.equal(true); + expect(sendData.firstCall.args[0].bidderCode).to.equal('legacy'); + expect(slot.getTargetingKeys.calledOnce).to.equal(true); + expect(slot.getTargeting.calledOnce).to.equal(true); + }); + + it('coerces non-array targeting values to string arrays', () => { + const sendData = sinon.spy(); + const slot = { + getConfig: sinon.stub().withArgs('targeting').returns({ targeting: { hb_bidder: 42 } }), + getSlotElementId: () => 'div-4', + getAdUnitPath: () => '/101' + }; + + runWithSlot(slot, sendData); + + expect(sendData.calledOnce).to.equal(true); + expect(sendData.firstCall.args[0].bidderCode).to.equal('42'); + }); + + it('logs and recovers when legacy targeting APIs throw', () => { + const sendData = sinon.spy(); + const slot = { + getTargetingKeys: sinon.stub().throws(new Error('legacy broken')), + getTargeting: sinon.stub(), + getSlotElementId: () => 'div-5', + getAdUnitPath: () => '/202' + }; + + runWithSlot(slot, sendData); + + expect(sendData.calledOnce).to.equal(true); + expect(sendData.firstCall.args[0].bidderCode).to.equal(null); + expect(logErrorStub.called).to.equal(true); + expect(logErrorStub.firstCall.args[0]).to.match(/Failed to get slot targeting/); + }); +}); diff --git a/test/spec/libraries/greedy/greedyPromise_spec.js b/test/spec/libraries/greedy/greedyPromise_spec.js index c59f646ec5b..aa8c08f5411 100644 --- a/test/spec/libraries/greedy/greedyPromise_spec.js +++ b/test/spec/libraries/greedy/greedyPromise_spec.js @@ -1,5 +1,5 @@ -import {GreedyPromise, greedySetTimeout} from '../../../../libraries/greedy/greedyPromise.js'; -import {delay} from '../../../../src/utils/promise.js'; +import { GreedyPromise, greedySetTimeout } from '../../../../libraries/greedy/greedyPromise.js'; +import { delay } from '../../../../src/utils/promise.js'; describe('GreedyPromise', () => { it('throws when resolver is not a function', () => { @@ -113,7 +113,7 @@ describe('GreedyPromise', () => { const greedy = op(GreedyPromise); // note that we are not using `allSettled` & co to resolve our promises, // to avoid transformations those methods do under the hood - const {actual = {}, expected = {}} = {}; + const { actual = {}, expected = {} } = {}; return new Promise((resolve) => { let pending = 2; function collect(dest, slot) { diff --git a/test/spec/libraries/magniteUtils/outstream_spec.js b/test/spec/libraries/magniteUtils/outstream_spec.js new file mode 100644 index 00000000000..83752e27a75 --- /dev/null +++ b/test/spec/libraries/magniteUtils/outstream_spec.js @@ -0,0 +1,157 @@ +import { + outstreamRenderer, + renderBid, + bidShouldUsePlayerWidthAndHeight, + DEFAULT_RENDERER_URL +} from 'libraries/magniteUtils/outstream.js'; +import { Renderer } from 'src/Renderer.js'; +import * as utils from 'src/utils.js'; + +describe('Magnite Utils Outstream', function () { + let logWarnStub; + + beforeEach(function () { + logWarnStub = sinon.stub(utils, 'logWarn'); + }); + + afterEach(function () { + logWarnStub.restore(); + }); + + describe('bidShouldUsePlayerWidthAndHeight', function () { + it('should return true if bid has no dimensions but has player size', function () { + const bid = { + playerWidth: 640, + playerHeight: 480 + }; + expect(bidShouldUsePlayerWidthAndHeight(bid)).to.be.true; + }); + + it('should return false if bid has dimensions', function () { + const bid = { + width: 300, + height: 250, + playerWidth: 640, + playerHeight: 480 + }; + expect(bidShouldUsePlayerWidthAndHeight(bid)).to.be.false; + }); + + it('should return false if bid has no player size', function () { + const bid = { + width: undefined, + height: undefined + }; + expect(bidShouldUsePlayerWidthAndHeight(bid)).to.be.false; + }); + }); + + describe('outstreamRenderer', function () { + let rendererInstallStub; + let rendererSetRenderSpy; + + beforeEach(function () { + rendererSetRenderSpy = sinon.spy(); + rendererInstallStub = sinon.stub(Renderer, 'install').returns({ + setRender: rendererSetRenderSpy + }); + }); + + afterEach(function () { + rendererInstallStub.restore(); + }); + + it('should install renderer with default URL if not provided', function () { + const bid = { + adId: 'test-ad-id', + adUnitCode: 'test-ad-unit' + }; + outstreamRenderer(bid); + expect(rendererInstallStub.calledOnce).to.be.true; + expect(rendererInstallStub.firstCall.args[0].url).to.equal(DEFAULT_RENDERER_URL); + }); + + it('should install renderer with provided URL', function () { + const bid = { + adId: 'test-ad-id', + adUnitCode: 'test-ad-unit' + }; + const customUrl = 'https://custom.url/renderer.js'; + outstreamRenderer(bid, customUrl); + expect(rendererInstallStub.firstCall.args[0].url).to.equal(customUrl); + }); + + it('should set render function', function () { + const bid = { + adId: 'test-ad-id', + adUnitCode: 'test-ad-unit' + }; + outstreamRenderer(bid); + expect(rendererSetRenderSpy.calledOnce).to.be.true; + expect(rendererSetRenderSpy.firstCall.args[0]).to.equal(renderBid); + }); + }); + + describe('renderBid', function () { + let adUnitElement; + let bid; + let rendererPushSpy; + + beforeEach(function () { + adUnitElement = document.createElement('div'); + adUnitElement.id = 'test-ad-unit'; + document.body.appendChild(adUnitElement); + + rendererPushSpy = sinon.spy(); + bid = { + adUnitCode: 'test-ad-unit', + width: 640, + height: 480, + vastUrl: 'https://vast.url', + renderer: { + getConfig: () => ({}), + push: rendererPushSpy + } + }; + + globalThis.MagniteApex = { + renderAd: sinon.spy() + }; + }); + + afterEach(function () { + document.body.removeChild(adUnitElement); + delete globalThis.MagniteApex; + }); + + it('should log warning if ad unit element not found', function () { + bid.adUnitCode = 'missing-ad-unit'; + renderBid(bid); + expect(logWarnStub.calledWithMatch('Magnite: unable to find ad unit element')).to.be.true; + }); + + it('should push render function to renderer', function () { + renderBid(bid); + expect(rendererPushSpy.calledOnce).to.be.true; + const callback = rendererPushSpy.firstCall.args[0]; + callback(); + expect(globalThis.MagniteApex.renderAd.calledOnce).to.be.true; + const args = globalThis.MagniteApex.renderAd.firstCall.args[0]; + expect(args.width).to.equal(640); + expect(args.height).to.equal(480); + expect(args.vastUrl).to.equal('https://vast.url'); + expect(args.placement.attachTo).to.equal(adUnitElement); + }); + + it('should hide google ads div', function () { + const wrapper = document.createElement('div'); + adUnitElement.appendChild(wrapper); + const googleAdsDiv = document.createElement('div'); + googleAdsDiv.id = 'google_ads_iframe'; + wrapper.appendChild(googleAdsDiv); + + renderBid(bid); + expect(googleAdsDiv.style.display).to.equal('none'); + }); + }); +}); diff --git a/test/spec/libraries/metadata_spec.js b/test/spec/libraries/metadata_spec.js index 0cbf066e339..5333aca09d2 100644 --- a/test/spec/libraries/metadata_spec.js +++ b/test/spec/libraries/metadata_spec.js @@ -1,4 +1,4 @@ -import {metadataRepository} from '../../../libraries/metadata/metadata.js'; +import { metadataRepository } from '../../../libraries/metadata/metadata.js'; describe('metadata', () => { let metadata; @@ -20,7 +20,7 @@ describe('metadata', () => { expect(metadata.getMetadata('bidder', 'mock')).to.eql(meta); }); it('can register and return storage disclosures', () => { - const disclosure = {timestamp: 'mock', disclosures: ['foo', 'bar']}; + const disclosure = { timestamp: 'mock', disclosures: ['foo', 'bar'] }; metadata.register('mockModule', { disclosures: { 'mock.url': disclosure @@ -42,7 +42,7 @@ describe('metadata', () => { } ] const disclosures = { - 'mock.url': {disclosures: ['foo', 'bar']} + 'mock.url': { disclosures: ['foo', 'bar'] } }; metadata.register('mockModule', { components diff --git a/test/spec/libraries/mspa/activityControls_spec.js b/test/spec/libraries/mspa/activityControls_spec.js index dcbebf9974c..ddf57c2d440 100644 --- a/test/spec/libraries/mspa/activityControls_spec.js +++ b/test/spec/libraries/mspa/activityControls_spec.js @@ -1,5 +1,5 @@ -import {mspaRule, setupRules, isTransmitUfpdConsentDenied, isTransmitGeoConsentDenied, isBasicConsentDenied, sensitiveNoticeIs, isConsentDenied} from '../../../../libraries/mspa/activityControls.js'; -import {ruleRegistry} from '../../../../src/activities/rules.js'; +import { mspaRule, setupRules, isTransmitUfpdConsentDenied, isTransmitGeoConsentDenied, isBasicConsentDenied, sensitiveNoticeIs, isConsentDenied } from '../../../../libraries/mspa/activityControls.js'; +import { ruleRegistry } from '../../../../src/activities/rules.js'; describe('Consent interpretation', () => { function mkConsent(flags) { @@ -124,7 +124,7 @@ describe('Consent interpretation', () => { versions: [2] } - }).forEach(([t, {flagNo, consents, versions}]) => { + }).forEach(([t, { flagNo, consents, versions }]) => { describe(t, () => { Object.entries(consents).forEach(([flagValue, shouldBeDenied]) => { const flagDescription = ({ @@ -236,7 +236,7 @@ describe('mspaRule', () => { }); it('should deny when consent is using version other than 1/2', () => { - consent = {Version: 3}; + consent = { Version: 3 }; expect(mkRule()().allow).to.equal(false); }) @@ -246,7 +246,7 @@ describe('mspaRule', () => { }).forEach(([t, denied]) => { it(`should check if deny fn ${t}`, () => { denies.returns(denied); - consent = {mock: 'value', Version: 1}; + consent = { mock: 'value', Version: 1 }; const result = mkRule()(); sinon.assert.calledWith(denies, consent); if (denied) { @@ -290,7 +290,7 @@ describe('setupRules', () => { }); it('should accept already flattened section data', () => { - consent.parsedSections.mockApi = {flat: 'consent', Version: 1}; + consent.parsedSections.mockApi = { flat: 'consent', Version: 1 }; runSetup('mockApi', [1]); isAllowed('mockActivity', {}); sinon.assert.calledWith(rules.mockActivity, consent.parsedSections.mockApi) @@ -308,11 +308,11 @@ describe('setupRules', () => { }); it('should pass flattened consent through normalizeConsent', () => { - const normalize = sinon.stub().returns({normalized: 'consent', Version: 1}) + const normalize = sinon.stub().returns({ normalized: 'consent', Version: 1 }) runSetup('mockApi', [1], normalize); expect(isAllowed('mockActivity', {})).to.equal(false); - sinon.assert.calledWith(normalize, {mock: 'consent', Version: 1}); - sinon.assert.calledWith(rules.mockActivity, {normalized: 'consent', Version: 1}); + sinon.assert.calledWith(normalize, { mock: 'consent', Version: 1 }); + sinon.assert.calledWith(rules.mockActivity, { normalized: 'consent', Version: 1 }); }); it('should return a function that unregisters activity controls', () => { diff --git a/test/spec/libraries/percentInView_spec.js b/test/spec/libraries/percentInView_spec.js new file mode 100644 index 00000000000..fd3f9b5ea20 --- /dev/null +++ b/test/spec/libraries/percentInView_spec.js @@ -0,0 +1,272 @@ +import { + getViewportOffset, + intersections, + mkIntersectionHook, + percentInView, + viewportIntersections, +} from '../../../libraries/percentInView/percentInView.js'; +import * as bbox from 'libraries/boundingClientRect/boundingClientRect'; + +import { defer } from 'src/utils/promise.js'; + +describe('percentInView', () => { + let sandbox; + beforeEach(() => { + sandbox = sinon.createSandbox(); + }); + afterEach(() => { + sandbox.restore(); + }); + + describe('getViewportOffset', () => { + function mockWindow(offsets = []) { + let win, leaf, child; + win = leaf = {}; + for (const [x, y] of offsets) { + win.frameElement = { + getBoundingClientRect() { + return { left: x, top: y }; + } + }; + child = win; + win = {}; + child.parent = win; + } + return leaf; + } + + it('returns 0, 0 for the top window', () => { + expect(getViewportOffset(mockWindow())).to.eql({ x: 0, y: 0 }); + }); + + it('returns frame offset for a direct child', () => { + expect(getViewportOffset(mockWindow([[10, 20]]))).to.eql({ x: 10, y: 20 }); + }); + it('returns cumulative offests for descendants', () => { + expect(getViewportOffset(mockWindow([[10, 20], [20, 30]]))).to.eql({ x: 30, y: 50 }); + }); + it('does not choke when parent is not accessible', () => { + const win = mockWindow([[10, 20]]); + Object.defineProperty(win, 'frameElement', { + get() { + throw new Error(); + } + }); + expect(getViewportOffset(win)).to.eql({ x: 0, y: 0 }); + }); + }); + + async function delay(ms = 10) { + await new Promise(resolve => setTimeout(resolve, ms)); + } + + describe('intersections', () => { + let callback, obs, nakedObs, mkObserver, el; + beforeEach(() => { + el = document.createElement('div'); + nakedObs = sinon.stub(); + nakedObs.observe = sinon.stub(); + mkObserver = sinon.stub().callsFake((cb) => { + callback = cb; + return nakedObs; + }); + obs = intersections(mkObserver); + }); + describe('when mkObserver throws', () => { + beforeEach(() => { + mkObserver = sinon.stub().callsFake(() => { + throw new Error(); + }); + obs = intersections(mkObserver); + }); + it('getIntersection should return undef', () => { + expect(obs.getIntersection({})).to.not.exist; + }); + + it('observe should resolve', async () => { + await obs.observe({}); + }); + }); + + it('observe should reject if the element cannot be observed', async () => { + let err = new Error(); + nakedObs.observe.throws(err); + try { + await obs.observe(null); + } catch (e) { + expect(e).to.eql(err); + return; + } + sinon.assert.fail('promise should reject'); + }); + it('does not observe the same element more than once', () => { + obs.observe(el); + obs.observe(el); + sinon.assert.calledOnce(nakedObs.observe); + }); + it('getIntersection should return undefined if the element is not observed', () => { + expect(obs.getIntersection(el)).to.not.exist; + }); + it('observe should resolve to latest intersection entry', () => { + let pm = obs.observe(el); + let entry = { + target: el, + time: 100 + }; + callback([entry, { + target: el, + time: 50 + }]); + return pm.then(result => { + expect(result).to.eql(entry); + }); + }); + it('observe should resolve immediately if an entry is available', () => { + const entry = { + target: el, + time: 10 + }; + callback([entry]); + const pm = obs.observe(el); + callback([{ + target: el, + time: 20 + }]); + return pm.then((result) => { + expect(result).to.eql(entry); + }); + }); + it('should ignore stale entries', async () => { + const entry = { + target: el, + time: 100 + }; + obs.observe(el); + callback([entry]); + callback([{ + target: el, + time: 10 + }]); + expect(obs.getIntersection(el)).to.eql(entry); + }); + + it('should not resolve until the targeted element has intersected', async () => { + const entry = { + target: el, + time: 100 + }; + const pm = obs.observe(el); + callback([{ + target: {}, + time: 20 + }]); + await delay(); + callback([entry]); + expect(await pm).to.eql(entry); + }); + }); + + describe('intersection hook', () => { + let intersections, hook, next, request; + beforeEach(() => { + next = sinon.stub(); + intersections = { + observe: sinon.stub() + }; + hook = mkIntersectionHook(intersections); + request = {}; + }); + + it('should observe elements for every ad unit', async () => { + request.adUnits = [{ + element: 'el1' + }, { + code: 'el2' + }]; + sandbox.stub(document, 'getElementById').returns('el2'); + hook(next, request); + sinon.assert.calledWith(intersections.observe, 'el1'); + sinon.assert.calledWith(intersections.observe, 'el2'); + await delay(); + sinon.assert.calledWith(next, request); + }); + + describe('promise resolution', () => { + let adUnits; + beforeEach(() => { + adUnits = { + el1: { + element: 'el1', + df: defer() + }, + el2: { + element: 'el2', + df: defer() + } + }; + request.adUnits = Object.values(adUnits); + intersections.observe.callsFake((element) => adUnits[element].df.promise); + }); + it('should wait for all promises to resolve', async () => { + hook(next, request); + sinon.assert.notCalled(next); + adUnits.el1.df.resolve(); + await delay(); + sinon.assert.notCalled(next); + adUnits.el2.df.resolve(); + await delay(); + sinon.assert.calledWith(next, request); + }); + + it('should still continue if some promises reject', async () => { + hook(next, request); + adUnits.el1.df.reject(); + await delay(); + sinon.assert.notCalled(next); + adUnits.el2.df.resolve(); + await delay(); + sinon.assert.calledWith(next, request); + }); + + it('should continue if promises never resolve', async () => { + hook(next, request); + await delay(100); + sinon.assert.called(next); + }); + + it('should not delay if there are no elements to observe', async () => { + request.adUnits = []; + hook(next, request); + await delay(); + sinon.assert.called(next); + }) + }); + }); + + describe('percentInView', () => { + let intersection; + beforeEach(() => { + sandbox.stub(viewportIntersections, 'getIntersection').callsFake(() => intersection); + sandbox.stub(viewportIntersections, 'observe'); + sandbox.stub(bbox, 'getBoundingClientRect'); + }); + + it('does not use intersection if w/h are relevant', () => { + bbox.getBoundingClientRect.returns({ + width: 0, + height: 0, + left: -50, + top: -100, + }); + intersection = { + boundingClientRect: { + width: 0, + height: 0, + }, + isIntersecting: true, + intersectionRatio: 1 + }; + expect(percentInView({}, { w: 100, h: 200 })).to.not.eql(100); + }); + }); +}); diff --git a/test/spec/libraries/placementPositionInfo_spec.js b/test/spec/libraries/placementPositionInfo_spec.js new file mode 100644 index 00000000000..bfd886b8898 --- /dev/null +++ b/test/spec/libraries/placementPositionInfo_spec.js @@ -0,0 +1,459 @@ +import { getPlacementPositionUtils } from '../../../libraries/placementPositionInfo/placementPositionInfo.js'; +import * as utils from '../../../src/utils.js'; +import * as boundingClientRectLib from '../../../libraries/boundingClientRect/boundingClientRect.js'; +import * as percentInViewLib from '../../../libraries/percentInView/percentInView.js'; +import * as winDimensions from 'src/utils/winDimensions.js'; +import * as adUnits from 'src/utils/adUnits'; + +import assert from 'assert'; +import sinon from 'sinon'; + +describe('placementPositionInfo', function () { + let sandbox; + let canAccessWindowTopStub; + let getWindowTopStub; + let getWindowSelfStub; + let getBoundingClientRectStub; + let percentInViewStub; + let cleanObjStub; + + let mockDocument; + let mockWindow; + let viewportOffset + + beforeEach(function () { + sandbox = sinon.createSandbox(); + + mockDocument = { + getElementById: sandbox.stub().returns(null), + getElementsByTagName: sandbox.stub().returns([]), + body: { scrollHeight: 2000, offsetHeight: 1800 }, + documentElement: { clientHeight: 1900, scrollHeight: 2100, offsetHeight: 1950 }, + visibilityState: 'visible' + }; + + mockWindow = { + innerHeight: 800, + document: mockDocument + }; + + canAccessWindowTopStub = sandbox.stub(utils, 'canAccessWindowTop').returns(true); + getWindowTopStub = sandbox.stub(utils, 'getWindowTop').returns(mockWindow); + getWindowSelfStub = sandbox.stub(utils, 'getWindowSelf').returns(mockWindow); + getBoundingClientRectStub = sandbox.stub(boundingClientRectLib, 'getBoundingClientRect'); + percentInViewStub = sandbox.stub(percentInViewLib, 'getViewability'); + cleanObjStub = sandbox.stub(utils, 'cleanObj').callsFake(obj => obj); + sandbox.stub(winDimensions, 'getWinDimensions').returns(mockWindow); + viewportOffset = { x: 0, y: 0 }; + sandbox.stub(percentInViewLib, 'getViewportOffset').callsFake(() => viewportOffset); + }); + + afterEach(function () { + sandbox.restore(); + }); + + describe('getPlacementPositionUtils', function () { + it('should return an object with getPlacementInfo and getPlacementEnv functions', function () { + const result = getPlacementPositionUtils(); + + assert.strictEqual(typeof result.getPlacementInfo, 'function'); + assert.strictEqual(typeof result.getPlacementEnv, 'function'); + }); + + it('should use window top when accessible', function () { + canAccessWindowTopStub.returns(true); + getPlacementPositionUtils(); + + assert.ok(getWindowTopStub.called); + }); + + it('should use window self when top is not accessible', function () { + canAccessWindowTopStub.returns(false); + getPlacementPositionUtils(); + + assert.ok(getWindowSelfStub.called); + }); + }); + + describe('getPlacementInfo', function () { + let getPlacementInfo; + let mockElement; + + beforeEach(function () { + mockElement = { id: 'test-ad-unit' }; + sandbox.stub(adUnits, 'getAdUnitElement').returns(mockElement); + + getBoundingClientRectStub.returns({ + top: 100, + bottom: 200, + height: 100, + width: 300 + }); + + percentInViewStub.returns(50); + + const placementUtils = getPlacementPositionUtils(); + getPlacementInfo = placementUtils.getPlacementInfo; + }); + + it('should return placement info with all required fields', function () { + const bidReq = { + adUnitCode: 'test-ad-unit', + auctionsCount: 5, + sizes: [[300, 250]] + }; + + const result = getPlacementInfo(bidReq); + + assert.strictEqual(result.AuctionsCount, 5); + assert.strictEqual(typeof result.DistanceToView, 'number'); + assert.strictEqual(typeof result.PlacementPercentView, 'number'); + assert.strictEqual(typeof result.ElementHeight, 'number'); + }); + + it('should calculate distanceToView as 0 when element is in viewport', function () { + getBoundingClientRectStub.returns({ + top: 100, + bottom: 200, + height: 100 + }); + + const bidReq = { + adUnitCode: 'test-ad-unit', + sizes: [[300, 250]] + }; + + const result = getPlacementInfo(bidReq); + + assert.strictEqual(result.DistanceToView, 0); + }); + + it('should calculate positive distanceToView when element is below viewport', function () { + getBoundingClientRectStub.returns({ + top: 1000, + bottom: 1100, + height: 100 + }); + + const bidReq = { + adUnitCode: 'test-ad-unit', + sizes: [[300, 250]] + }; + + const result = getPlacementInfo(bidReq); + + assert.strictEqual(result.DistanceToView, 200); + }); + + it('should calculate negative distanceToView when element is above viewport', function () { + getBoundingClientRectStub.returns({ + top: -200, + bottom: -100, + height: 100 + }); + + const bidReq = { + adUnitCode: 'test-ad-unit', + sizes: [[300, 250]] + }; + + const result = getPlacementInfo(bidReq); + + assert.strictEqual(result.DistanceToView, -100); + }); + + it('should handle null element gracefully', function () { + adUnits.getAdUnitElement.returns(null); + + const bidReq = { + adUnitCode: 'non-existent-unit', + sizes: [[300, 250]] + }; + + const placementUtils = getPlacementPositionUtils(); + const result = placementUtils.getPlacementInfo(bidReq); + + assert.strictEqual(result.DistanceToView, 0); + assert.strictEqual(result.ElementHeight, 1); + }); + + it('should not call getViewability when element is null', function () { + adUnits.getAdUnitElement.returns(null); + + const bidReq = { + adUnitCode: 'non-existent-unit', + sizes: [[300, 250]] + }; + + const placementUtils = getPlacementPositionUtils(); + placementUtils.getPlacementInfo(bidReq); + + assert.ok(!percentInViewStub.called, 'getViewability should not be called with null element'); + }); + + it('should handle empty sizes array', function () { + const bidReq = { + adUnitCode: 'test-ad-unit', + sizes: [] + }; + + const result = getPlacementInfo(bidReq); + + assert.ok(!isNaN(result.PlacementPercentView), 'PlacementPercentView should not be NaN'); + }); + + it('should handle undefined sizes', function () { + const bidReq = { + adUnitCode: 'test-ad-unit' + }; + + const result = getPlacementInfo(bidReq); + + assert.ok(!isNaN(result.PlacementPercentView), 'PlacementPercentView should not be NaN'); + }); + + it('should select the smallest size by area', function () { + const bidReq = { + adUnitCode: 'test-ad-unit', + sizes: [[728, 90], [300, 250], [160, 600]] + }; + + getPlacementInfo(bidReq); + + const percentInViewCall = percentInViewStub.getCall(0); + const sizeArg = percentInViewCall.args[2]; + + assert.strictEqual(sizeArg.w, 728); + assert.strictEqual(sizeArg.h, 90); + }); + + it('should use ElementHeight from bounding rect', function () { + getBoundingClientRectStub.returns({ + top: 100, + bottom: 350, + height: 250 + }); + + const bidReq = { + adUnitCode: 'test-ad-unit', + sizes: [[300, 250]] + }; + + const result = getPlacementInfo(bidReq); + + assert.strictEqual(result.ElementHeight, 250); + }); + + it('should default ElementHeight to 1 when height is 0', function () { + getBoundingClientRectStub.returns({ + top: 100, + bottom: 100, + height: 0 + }); + + const bidReq = { + adUnitCode: 'test-ad-unit', + sizes: [[300, 250]] + }; + + const result = getPlacementInfo(bidReq); + + assert.strictEqual(result.ElementHeight, 1); + }); + }); + + describe('getPlacementEnv', function () { + let getPlacementEnv; + let performanceNowStub; + + beforeEach(function () { + performanceNowStub = sandbox.stub(performance, 'now').returns(1234.567); + + const placementUtils = getPlacementPositionUtils(); + getPlacementEnv = placementUtils.getPlacementEnv; + }); + + it('should return environment info with all required fields', function () { + const result = getPlacementEnv(); + + assert.strictEqual(typeof result.TimeFromNavigation, 'number'); + assert.strictEqual(typeof result.TabActive, 'boolean'); + assert.strictEqual(typeof result.PageHeight, 'number'); + assert.strictEqual(typeof result.ViewportHeight, 'number'); + }); + + it('should return TimeFromNavigation as floored performance.now()', function () { + performanceNowStub.returns(5678.999); + + const placementUtils = getPlacementPositionUtils(); + const result = placementUtils.getPlacementEnv(); + + assert.strictEqual(result.TimeFromNavigation, 5678); + }); + + it('should return TabActive as true when document is visible', function () { + const result = getPlacementEnv(); + + assert.strictEqual(result.TabActive, true); + }); + + it('should return TabActive as false when document is hidden', function () { + sandbox.restore(); + sandbox = sinon.createSandbox(); + + const hiddenMockDocument = { + getElementById: sandbox.stub().returns(null), + getElementsByTagName: sandbox.stub().returns([]), + body: { scrollHeight: 1000, offsetHeight: 1000 }, + documentElement: { clientHeight: 1000, scrollHeight: 1000, offsetHeight: 1000 }, + visibilityState: 'hidden' + }; + + const hiddenMockWindow = { + innerHeight: 800, + document: hiddenMockDocument + }; + + sandbox.stub(utils, 'canAccessWindowTop').returns(true); + sandbox.stub(utils, 'getWindowTop').returns(hiddenMockWindow); + sandbox.stub(utils, 'getWindowSelf').returns(hiddenMockWindow); + sandbox.stub(utils, 'cleanObj').callsFake(obj => obj); + sandbox.stub(performance, 'now').returns(1000); + + const freshUtils = getPlacementPositionUtils(); + const result = freshUtils.getPlacementEnv(); + + assert.strictEqual(result.TabActive, false); + }); + + it('should return ViewportHeight from window.innerHeight', function () { + const result = getPlacementEnv(); + + assert.strictEqual(result.ViewportHeight, 800); + }); + + it('should return max PageHeight from all document height properties', function () { + const result = getPlacementEnv(); + + assert.strictEqual(result.PageHeight, 2100); + }); + }); + + describe('getViewableDistance edge cases', function () { + let getPlacementInfo; + + beforeEach(function () { + mockDocument.getElementById.returns({ id: 'test' }); + percentInViewStub.returns(0); + + const placementUtils = getPlacementPositionUtils(); + getPlacementInfo = placementUtils.getPlacementInfo; + }); + + it('should handle getBoundingClientRect returning null', function () { + getBoundingClientRectStub.returns(null); + + const bidReq = { + adUnitCode: 'test', + sizes: [[300, 250]] + }; + + const result = getPlacementInfo(bidReq); + + assert.strictEqual(result.DistanceToView, 0); + assert.strictEqual(result.ElementHeight, 1); + }); + + it('should handle element exactly at viewport bottom edge', function () { + getBoundingClientRectStub.returns({ + top: 800, + bottom: 900, + height: 100 + }); + + const bidReq = { + adUnitCode: 'test', + sizes: [[300, 250]] + }; + + const result = getPlacementInfo(bidReq); + + assert.strictEqual(result.DistanceToView, 0); + }); + + it('should handle element exactly at viewport top edge', function () { + getBoundingClientRectStub.returns({ + top: 0, + bottom: 100, + height: 100 + }); + + const bidReq = { + adUnitCode: 'test', + sizes: [[300, 250]] + }; + + const result = getPlacementInfo(bidReq); + + assert.strictEqual(result.DistanceToView, 0); + }); + }); + + describe('iframe coordinate translation', function () { + beforeEach(() => { + sandbox.stub(adUnits, 'getAdUnitElement').returns({ id: 'test' }) + mockWindow.innerHeight = 1000; + mockDocument.body = { + scrollHeight: 2000, offsetHeight: 1800 + } + mockDocument.documentElement = { clientHeight: 1900, scrollHeight: 2100, offsetHeight: 1950 } + }); + it('should apply iframe offset when running inside a friendly iframe', function () { + viewportOffset = { y: 200 }; + + getBoundingClientRectStub.callsFake((el) => { + return { top: 100, bottom: 200, height: 100 }; + }); + + const placementUtils = getPlacementPositionUtils(); + const result = placementUtils.getPlacementInfo({ + adUnitCode: 'test', + sizes: [[300, 250]] + }); + + assert.strictEqual(result.DistanceToView, 0); + }); + + it('should calculate correct distance when element is below viewport with iframe offset', function () { + viewportOffset = { y: 500 }; + + getBoundingClientRectStub.callsFake((el) => { + return { top: 600, bottom: 700, height: 100 }; + }); + + const placementUtils = getPlacementPositionUtils(); + const result = placementUtils.getPlacementInfo({ + adUnitCode: 'test', + sizes: [[300, 250]] + }); + + assert.strictEqual(result.DistanceToView, 100); + }); + + it('should calculate negative distance when element is above viewport with iframe offset', function () { + viewportOffset = { y: -600 }; + + getBoundingClientRectStub.callsFake((el) => { + return { top: 100, bottom: 200, height: 100 }; + }); + + const placementUtils = getPlacementPositionUtils(); + const result = placementUtils.getPlacementInfo({ + adUnitCode: 'test', + sizes: [[300, 250]] + }); + + assert.strictEqual(result.DistanceToView, -400); + }); + }); +}); diff --git a/test/spec/libraries/precisoUtils/bidNativeUtils_spec.js b/test/spec/libraries/precisoUtils/bidNativeUtils_spec.js index 1993ab3665a..1b163b513c8 100644 --- a/test/spec/libraries/precisoUtils/bidNativeUtils_spec.js +++ b/test/spec/libraries/precisoUtils/bidNativeUtils_spec.js @@ -1,4 +1,4 @@ -import {expect} from 'chai'; +import { expect } from 'chai'; import { NATIVE } from '../../../../src/mediaTypes.js'; import { interpretNativeBid, OPENRTB } from '../../../../libraries/precisoUtils/bidNativeUtils.js'; diff --git a/test/spec/libraries/precisoUtils/bidUtilsCommon_spec.js b/test/spec/libraries/precisoUtils/bidUtilsCommon_spec.js index ad7243cb875..03b3b219cc5 100644 --- a/test/spec/libraries/precisoUtils/bidUtilsCommon_spec.js +++ b/test/spec/libraries/precisoUtils/bidUtilsCommon_spec.js @@ -1,4 +1,4 @@ -import {expect} from 'chai'; +import { expect } from 'chai'; import { BANNER } from '../../../../src/mediaTypes.js'; import * as utils from '../../../../src/utils.js'; diff --git a/test/spec/libraries/processResponse_spec.js b/test/spec/libraries/processResponse_spec.js index 99d20c69c77..9ca5844496e 100644 --- a/test/spec/libraries/processResponse_spec.js +++ b/test/spec/libraries/processResponse_spec.js @@ -1,5 +1,5 @@ import { getBidFromResponse } from '../../../libraries/processResponse/index.js'; -import {expect} from 'chai/index.js'; +import { expect } from 'chai/index.js'; describe('processResponse', function () { const respItem = { diff --git a/test/spec/libraries/pubmaticUtils/plugins/floorProvider_spec.js b/test/spec/libraries/pubmaticUtils/plugins/floorProvider_spec.js index 686a9b1ad30..2de026f86bc 100644 --- a/test/spec/libraries/pubmaticUtils/plugins/floorProvider_spec.js +++ b/test/spec/libraries/pubmaticUtils/plugins/floorProvider_spec.js @@ -2,7 +2,7 @@ import sinon from 'sinon'; import * as floorProvider from '../../../../../libraries/pubmaticUtils/plugins/floorProvider.js'; import * as priceFloors from '../../../../../modules/priceFloors.js'; import * as pubmaticUtils from '../../../../../libraries/pubmaticUtils/pubmaticUtils.js'; -import {expect} from 'chai'; +import { expect } from 'chai'; describe('FloorProvider', () => { const floorsobj = { @@ -91,7 +91,7 @@ describe('FloorProvider', () => { getConfigByName: () => floorsobj }); - const req = {err: 4}; + const req = { err: 4 }; const result = await floorProvider.processBidRequest(req); expect(result).to.equal(req); diff --git a/test/spec/libraries/sizeUtils_spec.js b/test/spec/libraries/sizeUtils_spec.js index 37e089f39ad..e2f69a6c826 100644 --- a/test/spec/libraries/sizeUtils_spec.js +++ b/test/spec/libraries/sizeUtils_spec.js @@ -1,5 +1,5 @@ -import {getAdUnitSizes} from '../../../libraries/sizeUtils/sizeUtils.js'; -import {expect} from 'chai/index.js'; +import { getAdUnitSizes } from '../../../libraries/sizeUtils/sizeUtils.js'; +import { expect } from 'chai/index.js'; describe('getAdUnitSizes', function () { it('returns an empty response when adUnits is undefined', function () { @@ -8,23 +8,23 @@ describe('getAdUnitSizes', function () { }); it('returns an empty array when invalid data is present in adUnit object', function () { - const sizes = getAdUnitSizes({sizes: 300}); + const sizes = getAdUnitSizes({ sizes: 300 }); expect(sizes).to.deep.equal([]); }); it('retuns an array of arrays when reading from adUnit.sizes', function () { - let sizes = getAdUnitSizes({sizes: [300, 250]}); + let sizes = getAdUnitSizes({ sizes: [300, 250] }); expect(sizes).to.deep.equal([[300, 250]]); - sizes = getAdUnitSizes({sizes: [[300, 250], [300, 600]]}); + sizes = getAdUnitSizes({ sizes: [[300, 250], [300, 600]] }); expect(sizes).to.deep.equal([[300, 250], [300, 600]]); }); it('returns an array of arrays when reading from adUnit.mediaTypes.banner.sizes', function () { - let sizes = getAdUnitSizes({mediaTypes: {banner: {sizes: [300, 250]}}}); + let sizes = getAdUnitSizes({ mediaTypes: { banner: { sizes: [300, 250] } } }); expect(sizes).to.deep.equal([[300, 250]]); - sizes = getAdUnitSizes({mediaTypes: {banner: {sizes: [[300, 250], [300, 600]]}}}); + sizes = getAdUnitSizes({ mediaTypes: { banner: { sizes: [[300, 250], [300, 600]] } } }); expect(sizes).to.deep.equal([[300, 250], [300, 600]]); }); }); diff --git a/test/spec/libraries/storageDisclosure_spec.js b/test/spec/libraries/storageDisclosure_spec.js index 1f848504ee2..98a98700484 100644 --- a/test/spec/libraries/storageDisclosure_spec.js +++ b/test/spec/libraries/storageDisclosure_spec.js @@ -1,5 +1,4 @@ -import {getStorageDisclosureSummary} from '../../../libraries/storageDisclosure/summary.js'; -import {dynamicDisclosureCollector} from '../../../modules/storageControl.js'; +import { getStorageDisclosureSummary } from '../../../libraries/storageDisclosure/summary.js'; describe('storageDisclosure', () => { let moduleMeta; @@ -38,7 +37,7 @@ describe('storageDisclosure', () => { disclosures: { url1: { disclosures: [ - {identifier: 'foo'} + { identifier: 'foo' } ] } } @@ -47,7 +46,7 @@ describe('storageDisclosure', () => { disclosures: { url2: { disclosures: [ - {identifier: 'bar'} + { identifier: 'bar' } ] } } @@ -71,7 +70,7 @@ describe('storageDisclosure', () => { const disclosures = { url: { disclosures: [ - {identifier: 'foo'} + { identifier: 'foo' } ] } } diff --git a/test/spec/libraries/teqblazeUtils/bidderUtils_spec.js b/test/spec/libraries/teqblazeUtils/bidderUtils_spec.js index 85ea1131db4..73055521549 100644 --- a/test/spec/libraries/teqblazeUtils/bidderUtils_spec.js +++ b/test/spec/libraries/teqblazeUtils/bidderUtils_spec.js @@ -516,7 +516,7 @@ describe('TeqBlazeBidderUtils', function () { const syncData = spec.getUserSyncs({}, {}, { consentString: 'ALL', gdprApplies: true, - }, {}); + }, undefined); expect(syncData).to.be.an('array').which.is.not.empty; expect(syncData[0]).to.be.an('object') expect(syncData[0].type).to.be.a('string') @@ -525,9 +525,7 @@ describe('TeqBlazeBidderUtils', function () { expect(syncData[0].url).to.equal(`https://${DOMAIN}/image?pbjs=1&gdpr=1&gdpr_consent=ALL&coppa=0`) }); it('Should return array of objects with proper sync config , include CCPA', function () { - const syncData = spec.getUserSyncs({}, {}, {}, { - consentString: '1---' - }); + const syncData = spec.getUserSyncs({}, {}, {}, '1---'); expect(syncData).to.be.an('array').which.is.not.empty; expect(syncData[0]).to.be.an('object') expect(syncData[0].type).to.be.a('string') @@ -536,7 +534,7 @@ describe('TeqBlazeBidderUtils', function () { expect(syncData[0].url).to.equal(`https://${DOMAIN}/image?pbjs=1&ccpa_consent=1---&coppa=0`) }); it('Should return array of objects with proper sync config , include GPP', function () { - const syncData = spec.getUserSyncs({}, {}, {}, {}, { + const syncData = spec.getUserSyncs({}, {}, {}, undefined, { gppString: 'abc123', applicableSections: [8] }); diff --git a/test/spec/libraries/timeoutQueue_spec.js b/test/spec/libraries/timeoutQueue_spec.js index ab8d20aa97d..b290325bcaa 100644 --- a/test/spec/libraries/timeoutQueue_spec.js +++ b/test/spec/libraries/timeoutQueue_spec.js @@ -1,5 +1,5 @@ -import {timeoutQueue} from '../../../libraries/timeoutQueue/timeoutQueue.js'; -import {expect} from 'chai/index.js'; +import { timeoutQueue } from '../../../libraries/timeoutQueue/timeoutQueue.js'; +import { expect } from 'chai/index.js'; describe('timeoutQueue', () => { let clock; diff --git a/test/spec/libraries/urlUtils_spec.js b/test/spec/libraries/urlUtils_spec.js index 9dd66b05407..43629a439ef 100644 --- a/test/spec/libraries/urlUtils_spec.js +++ b/test/spec/libraries/urlUtils_spec.js @@ -1,4 +1,4 @@ -import {tryAppendQueryString} from '../../../libraries/urlUtils/urlUtils.js'; +import { tryAppendQueryString } from '../../../libraries/urlUtils/urlUtils.js'; import assert from 'assert'; describe('tryAppendQueryString', function () { diff --git a/test/spec/libraries/vastTrackers_spec.js b/test/spec/libraries/vastTrackers_spec.js index ddd80e98f9d..e0e3db9cae4 100644 --- a/test/spec/libraries/vastTrackers_spec.js +++ b/test/spec/libraries/vastTrackers_spec.js @@ -7,8 +7,8 @@ import { reset, cacheVideoBidHook, disable } from 'libraries/vastTrackers/vastTrackers.js'; -import {MODULE_TYPE_ANALYTICS} from '../../../src/activities/modules.js'; -import {AuctionIndex} from '../../../src/auctionIndex.js'; +import { MODULE_TYPE_ANALYTICS } from '../../../src/activities/modules.js'; +import { AuctionIndex } from '../../../src/auctionIndex.js'; describe('vast trackers', () => { let sandbox, tracker, auction, bid, bidRequest, index; @@ -28,17 +28,17 @@ describe('vast trackers', () => { return 'aid'; }, getProperties() { - return {auction: 'props'}; + return { auction: 'props' }; }, getBidRequests() { - return [{bids: [bidRequest]}] + return [{ bids: [bidRequest] }] } }; sandbox = sinon.createSandbox(); index = new AuctionIndex(() => [auction]); tracker = sinon.stub().callsFake(function (bidResponse) { return [ - {'event': 'impressions', 'url': `https://vasttracking.mydomain.com/vast?cpm=${bidResponse.cpm}`} + { 'event': 'impressions', 'url': `https://vasttracking.mydomain.com/vast?cpm=${bidResponse.cpm}` } ]; }); registerVastTrackers(MODULE_TYPE_ANALYTICS, 'test', tracker); @@ -51,27 +51,27 @@ describe('vast trackers', () => { after(disable); it('insert into tracker list', function () { - const trackers = getVastTrackers(bid, {index}); + const trackers = getVastTrackers(bid, { index }); expect(trackers).to.be.a('map'); expect(trackers.get('impressions')).to.exists; expect(trackers.get('impressions').has('https://vasttracking.mydomain.com/vast?cpm=1')).to.be.true; }); it('insert trackers in vastXml', function () { - const trackers = getVastTrackers(bid, {index}); + const trackers = getVastTrackers(bid, { index }); let vastXml = ''; vastXml = insertVastTrackers(trackers, vastXml); expect(vastXml).to.equal(''); }); it('should pass request and auction properties to trackerFn', () => { - const bid = {requestId: 'bid', auctionId: 'aid'}; - getVastTrackers(bid, {index}); - sinon.assert.calledWith(tracker, bid, sinon.match({auction: auction.getProperties(), bidRequest})) + const bid = { requestId: 'bid', auctionId: 'aid' }; + getVastTrackers(bid, { index }); + sinon.assert.calledWith(tracker, bid, sinon.match({ auction: auction.getProperties(), bidRequest })) }) it('test addImpUrlToTrackers', function () { - const trackers = addImpUrlToTrackers({'vastImpUrl': 'imptracker.com'}, getVastTrackers(bid, {index})); + const trackers = addImpUrlToTrackers({ 'vastImpUrl': 'imptracker.com' }, getVastTrackers(bid, { index })); expect(trackers).to.be.a('map'); expect(trackers.get('impressions')).to.exists; expect(trackers.get('impressions').has('imptracker.com')).to.be.true; @@ -79,7 +79,7 @@ describe('vast trackers', () => { if (FEATURES.VIDEO) { it('should add trackers to bid response', () => { - cacheVideoBidHook({index})(sinon.stub(), 'au', bid); + cacheVideoBidHook({ index })(sinon.stub(), 'au', bid); expect(bid.vastImpUrl).to.eql([ 'https://vasttracking.mydomain.com/vast?cpm=1' ]) diff --git a/test/spec/libraries/weakStore_spec.js b/test/spec/libraries/weakStore_spec.js index 407b83391ef..f249fdf3f7d 100644 --- a/test/spec/libraries/weakStore_spec.js +++ b/test/spec/libraries/weakStore_spec.js @@ -1,4 +1,4 @@ -import {weakStore} from '../../../libraries/weakStore/weakStore.js'; +import { weakStore } from '../../../libraries/weakStore/weakStore.js'; describe('weakStore', () => { let targets, store; @@ -18,7 +18,7 @@ describe('weakStore', () => { }); it('inits to given value', () => { - expect(store('id', {initial: 'value'})).to.eql({'initial': 'value'}); + expect(store('id', { initial: 'value' })).to.eql({ 'initial': 'value' }); }); it('returns the same object as long as the target does not change', () => { @@ -26,7 +26,7 @@ describe('weakStore', () => { }); it('ignores init value if already initialized', () => { - store('id', {initial: 'value'}); - expect(store('id', {second: 'value'})).to.eql({initial: 'value'}); + store('id', { initial: 'value' }); + expect(store('id', { second: 'value' })).to.eql({ initial: 'value' }); }) }); diff --git a/test/spec/modules/1plusXRtdProvider_spec.js b/test/spec/modules/1plusXRtdProvider_spec.js index b971fda6521..00243c7ae38 100644 --- a/test/spec/modules/1plusXRtdProvider_spec.js +++ b/test/spec/modules/1plusXRtdProvider_spec.js @@ -1,5 +1,5 @@ import assert from 'assert'; -import {config} from 'src/config'; +import { config } from 'src/config'; import { buildOrtb2Updates, extractConfig, @@ -11,7 +11,7 @@ import { setTargetingDataToConfig, updateBidderConfig, } from 'modules/1plusXRtdProvider'; -import {deepClone} from '../../../src/utils.js'; +import { deepClone } from '../../../src/utils.js'; import { STORAGE_TYPE_COOKIES, STORAGE_TYPE_LOCALSTORAGE } from 'src/storageManager.js'; import { server } from 'test/mocks/xhr.js'; diff --git a/test/spec/modules/33acrossAnalyticsAdapter_spec.js b/test/spec/modules/33acrossAnalyticsAdapter_spec.js index b31117e00c4..03b6e7981fa 100644 --- a/test/spec/modules/33acrossAnalyticsAdapter_spec.js +++ b/test/spec/modules/33acrossAnalyticsAdapter_spec.js @@ -422,7 +422,7 @@ describe('33acrossAnalyticsAdapter:', function () { const timeout = 2000; this.enableAnalytics({ timeout }); - performStandardAuction({exclude: ['bidWon', 'slotRenderEnded', 'auctionEnd']}); + performStandardAuction({ exclude: ['bidWon', 'slotRenderEnded', 'auctionEnd'] }); sandbox.clock.tick(timeout + 1000); @@ -435,7 +435,7 @@ describe('33acrossAnalyticsAdapter:', function () { const request = getMockEvents().prebid[0].BID_REQUESTED[0]; const bidToTimeout = request.bids[0]; - performStandardAuction({exclude: ['bidWon', 'slotRenderEnded', 'auctionEnd']}); + performStandardAuction({ exclude: ['bidWon', 'slotRenderEnded', 'auctionEnd'] }); sandbox.clock.tick(1); events.emit(EVENTS.BID_TIMEOUT, [{ auctionId: request.auctionId, @@ -455,7 +455,7 @@ describe('33acrossAnalyticsAdapter:', function () { it('logs an error', function () { this.enableAnalytics(); - performStandardAuction({exclude: ['bidWon', 'slotRenderEnded', 'auctionEnd']}); + performStandardAuction({ exclude: ['bidWon', 'slotRenderEnded', 'auctionEnd'] }); sandbox.clock.tick(this.defaultTimeout + 1000); @@ -470,7 +470,7 @@ describe('33acrossAnalyticsAdapter:', function () { const timeout = POST_GAM_TIMEOUT + 2000; this.enableAnalytics({ timeout }); - performStandardAuction({exclude: ['bidWon', 'auctionEnd']}); + performStandardAuction({ exclude: ['bidWon', 'auctionEnd'] }); sandbox.clock.tick(POST_GAM_TIMEOUT + 1); assert.strictEqual(navigator.sendBeacon.callCount, 1); @@ -482,7 +482,7 @@ describe('33acrossAnalyticsAdapter:', function () { const timeout = POST_GAM_TIMEOUT + 2000; this.enableAnalytics({ timeout }); - performStandardAuction({exclude: ['bidWon', 'auctionEnd'], useSlotElementIds: true}); + performStandardAuction({ exclude: ['bidWon', 'auctionEnd'], useSlotElementIds: true }); sandbox.clock.tick(POST_GAM_TIMEOUT + 1); assert.strictEqual(navigator.sendBeacon.callCount, 1); @@ -493,7 +493,7 @@ describe('33acrossAnalyticsAdapter:', function () { const timeout = POST_GAM_TIMEOUT + 2000; this.enableAnalytics({ timeout }); - performStandardAuction({exclude: ['bidWon', 'auctionEnd']}); + performStandardAuction({ exclude: ['bidWon', 'auctionEnd'] }); sandbox.clock.tick(POST_GAM_TIMEOUT - 1); assert.strictEqual(navigator.sendBeacon.callCount, 0); @@ -526,7 +526,7 @@ describe('33acrossAnalyticsAdapter:', function () { this.enableAnalytics(); - performStandardAuction({exclude: ['auctionEnd']}); + performStandardAuction({ exclude: ['auctionEnd'] }); sandbox.clock.tick(this.defaultTimeout + 1000); const incompleteSentBid = JSON.parse(navigator.sendBeacon.firstCall.args[1]).auctions[0].adUnits[1].bids[0]; @@ -538,7 +538,7 @@ describe('33acrossAnalyticsAdapter:', function () { this.enableAnalytics(); - performStandardAuction({exclude: ['bidWon', 'auctionEnd']}); + performStandardAuction({ exclude: ['bidWon', 'auctionEnd'] }); sandbox.clock.tick(this.defaultTimeout + 1000); const incompleteSentBid = JSON.parse(navigator.sendBeacon.firstCall.args[1]).auctions[0].adUnits[1].bids[0]; @@ -551,7 +551,7 @@ describe('33acrossAnalyticsAdapter:', function () { const endpoint = faker.internet.url(); this.enableAnalytics({ endpoint }); - performStandardAuction({exclude: ['bidWon', 'auctionEnd']}); + performStandardAuction({ exclude: ['bidWon', 'auctionEnd'] }); sandbox.clock.tick(this.defaultTimeout + 1000); assert.calledWith(log.info, `Analytics report sent to ${endpoint}`); @@ -616,7 +616,7 @@ describe('33acrossAnalyticsAdapter:', function () { context('and the Google Ad Manager timeout has elapsed', function () { it('completes the transaction', function () { const timeout = POST_GAM_TIMEOUT + 2000; - this.enableAnalytics({timeout}); + this.enableAnalytics({ timeout }); const { prebid: [auction], gam } = getMockEvents(); const slotRenderEnded = gam.slotRenderEnded[0]; diff --git a/test/spec/modules/33acrossBidAdapter_spec.js b/test/spec/modules/33acrossBidAdapter_spec.js index 110a5655f40..93c7a742bfa 100644 --- a/test/spec/modules/33acrossBidAdapter_spec.js +++ b/test/spec/modules/33acrossBidAdapter_spec.js @@ -257,7 +257,7 @@ describe('33acrossBidAdapter:', function () { return Object.assign(fm, { ext: { ttx: { - bidfloors: [ floors[i] ] + bidfloors: [floors[i]] } } }) @@ -482,7 +482,7 @@ describe('33acrossBidAdapter:', function () { } this.buildBidderRequest = (bidRequests, additionalProps) => { - const [ bidRequest ] = bidRequests; + const [bidRequest] = bidRequests; return utils.mergeDeep({ ortb2: utils.mergeDeep({ @@ -775,7 +775,7 @@ describe('33acrossBidAdapter:', function () { it('returns false', function() { const bidRequests = ( new BidRequestsBuilder() - .withVideo({context: 'instream', protocols: [1, 2], mimes: ['foo', 'bar']}) + .withVideo({ context: 'instream', protocols: [1, 2], mimes: ['foo', 'bar'] }) .build() ); @@ -797,7 +797,7 @@ describe('33acrossBidAdapter:', function () { it('returns true', function() { const bidRequests = ( new BidRequestsBuilder() - .withVideo({context: 'outstream', protocols: [1, 2], mimes: ['foo', 'bar']}) + .withVideo({ context: 'outstream', protocols: [1, 2], mimes: ['foo', 'bar'] }) .build() ); @@ -833,7 +833,7 @@ describe('33acrossBidAdapter:', function () { const bidRequests = this.buildBannerBidRequests(); // Bids have the default zone ID configured const bidderRequest = this.buildBidderRequest(bidRequests); - const [ buildRequest ] = spec.buildRequests(bidRequests, bidderRequest); + const [buildRequest] = spec.buildRequests(bidRequests, bidderRequest); validateBuiltServerRequest(buildRequest, serverRequest); }) @@ -864,7 +864,7 @@ describe('33acrossBidAdapter:', function () { } }); - const [ buildRequest ] = spec.buildRequests(bidRequests, bidderRequest); + const [buildRequest] = spec.buildRequests(bidRequests, bidderRequest); validateBuiltServerRequest(buildRequest, serverRequest); }); @@ -876,7 +876,7 @@ describe('33acrossBidAdapter:', function () { new TtxRequestBuilder() .withBanner() .withProduct() - .withViewability({amount: 100}) + .withViewability({ amount: 100 }) .build() ); @@ -884,7 +884,7 @@ describe('33acrossBidAdapter:', function () { const bidRequests = this.buildBannerBidRequests(); const bidderRequest = this.buildBidderRequest(bidRequests); - const [ buildRequest ] = spec.buildRequests(bidRequests, bidderRequest); + const [buildRequest] = spec.buildRequests(bidRequests, bidderRequest); validateBuiltServerRequest(buildRequest, serverRequest); }); @@ -896,7 +896,7 @@ describe('33acrossBidAdapter:', function () { new TtxRequestBuilder() .withBanner() .withProduct() - .withViewability({amount: 0}) + .withViewability({ amount: 0 }) .build() ); const bidRequests = this.buildBannerBidRequests(); @@ -904,7 +904,7 @@ describe('33acrossBidAdapter:', function () { Object.assign(this.element, { x: -300, y: 0, width: 207, height: 320 }); - const [ buildRequest ] = spec.buildRequests(bidRequests, bidderRequest); + const [buildRequest] = spec.buildRequests(bidRequests, bidderRequest); validateBuiltServerRequest(buildRequest, serverRequest); }); @@ -916,7 +916,7 @@ describe('33acrossBidAdapter:', function () { new TtxRequestBuilder() .withBanner() .withProduct() - .withViewability({amount: 75}) + .withViewability({ amount: 75 }) .build() ) const bidRequests = this.buildBannerBidRequests(); @@ -924,7 +924,7 @@ describe('33acrossBidAdapter:', function () { Object.assign(this.element, { width: 800, height: 800 }); - const [ buildRequest ] = spec.buildRequests(bidRequests, bidderRequest); + const [buildRequest] = spec.buildRequests(bidRequests, bidderRequest); validateBuiltServerRequest(buildRequest, serverRequest); }); @@ -937,7 +937,7 @@ describe('33acrossBidAdapter:', function () { .withBanner() .withProduct() .withSizes([{ w: 800, h: 2400 }]) - .withViewability({amount: 25}) + .withViewability({ amount: 25 }) .build() ); const bidRequests = this.buildBannerBidRequests(); @@ -946,7 +946,7 @@ describe('33acrossBidAdapter:', function () { Object.assign(this.element, { width: 0, height: 0 }); bidRequests[0].mediaTypes.banner.sizes = [[800, 2400]]; - const [ buildRequest ] = spec.buildRequests(bidRequests, bidderRequest); + const [buildRequest] = spec.buildRequests(bidRequests, bidderRequest); validateBuiltServerRequest(buildRequest, serverRequest); }); @@ -958,7 +958,7 @@ describe('33acrossBidAdapter:', function () { new TtxRequestBuilder() .withBanner() .withProduct() - .withViewability({amount: spec.NON_MEASURABLE}) + .withViewability({ amount: spec.NON_MEASURABLE }) .build() ); const bidRequests = this.buildBannerBidRequests(); @@ -971,7 +971,7 @@ describe('33acrossBidAdapter:', function () { this.sandbox.stub(utils, 'getWindowTop').returns({}); this.sandbox.stub(utils, 'getWindowSelf').returns(this.win); - const [ buildRequest ] = spec.buildRequests(bidRequests, bidderRequest); + const [buildRequest] = spec.buildRequests(bidRequests, bidderRequest); validateBuiltServerRequest(buildRequest, serverRequest); }); @@ -1014,7 +1014,7 @@ describe('33acrossBidAdapter:', function () { } }); - const [ buildRequest ] = spec.buildRequests(bidRequests, bidderRequest); + const [buildRequest] = spec.buildRequests(bidRequests, bidderRequest); validateBuiltServerRequest(buildRequest, serverRequest); }); @@ -1064,7 +1064,7 @@ describe('33acrossBidAdapter:', function () { } }); - const [ buildRequest ] = spec.buildRequests(bidRequests, bidderRequest); + const [buildRequest] = spec.buildRequests(bidRequests, bidderRequest); validateBuiltServerRequest(buildRequest, serverRequest); }); @@ -1077,7 +1077,7 @@ describe('33acrossBidAdapter:', function () { new TtxRequestBuilder() .withBanner() .withProduct() - .withViewability({amount: 0}) + .withViewability({ amount: 0 }) .build() ); const bidRequests = this.buildBannerBidRequests(); @@ -1090,7 +1090,7 @@ describe('33acrossBidAdapter:', function () { this.sandbox.stub(utils, 'getWindowTop').returns(this.win); resetWinDimensions(); - const [ buildRequest ] = spec.buildRequests(bidRequests, bidderRequest); + const [buildRequest] = spec.buildRequests(bidRequests, bidderRequest); validateBuiltServerRequest(buildRequest, serverRequest); }); @@ -1118,7 +1118,7 @@ describe('33acrossBidAdapter:', function () { } }); - const [ builtServerRequest ] = spec.buildRequests(bidRequests, bidderRequest); + const [builtServerRequest] = spec.buildRequests(bidRequests, bidderRequest); validateBuiltServerRequest(builtServerRequest, serverRequest); }); @@ -1150,7 +1150,7 @@ describe('33acrossBidAdapter:', function () { } } }); - const [ builtServerRequest ] = spec.buildRequests(bidRequests, bidderRequest); + const [builtServerRequest] = spec.buildRequests(bidRequests, bidderRequest); validateBuiltServerRequest(builtServerRequest, serverRequest); }); @@ -1166,7 +1166,7 @@ describe('33acrossBidAdapter:', function () { ); const bidRequests = this.buildBannerBidRequests(); const bidderRequest = this.buildBidderRequest(bidRequests); - const [ builtServerRequest ] = spec.buildRequests(bidRequests, bidderRequest); + const [builtServerRequest] = spec.buildRequests(bidRequests, bidderRequest); validateBuiltServerRequest(builtServerRequest, serverRequest); }); @@ -1187,7 +1187,7 @@ describe('33acrossBidAdapter:', function () { ); const bidRequests = this.buildBannerBidRequests(); const bidderRequest = this.buildBidderRequest(bidRequests); - const [ builtServerRequest ] = spec.buildRequests(bidRequests, bidderRequest); + const [builtServerRequest] = spec.buildRequests(bidRequests, bidderRequest); validateBuiltServerRequest(builtServerRequest, serverRequest); }); @@ -1211,7 +1211,7 @@ describe('33acrossBidAdapter:', function () { } }); - const [ builtServerRequest ] = spec.buildRequests(bidRequests, bidderRequest); + const [builtServerRequest] = spec.buildRequests(bidRequests, bidderRequest); validateBuiltServerRequest(builtServerRequest, serverRequest); }); @@ -1239,7 +1239,7 @@ describe('33acrossBidAdapter:', function () { } } }); - const [ builtServerRequest ] = spec.buildRequests(bidRequests, bidderRequest); + const [builtServerRequest] = spec.buildRequests(bidRequests, bidderRequest); validateBuiltServerRequest(builtServerRequest, serverRequest); }); @@ -1255,7 +1255,7 @@ describe('33acrossBidAdapter:', function () { ); const bidRequests = this.buildBannerBidRequests(); const bidderRequest = this.buildBidderRequest(bidRequests); - const [ builtServerRequest ] = spec.buildRequests(bidRequests, bidderRequest); + const [builtServerRequest] = spec.buildRequests(bidRequests, bidderRequest); validateBuiltServerRequest(builtServerRequest, serverRequest); }); @@ -1276,7 +1276,7 @@ describe('33acrossBidAdapter:', function () { ); const bidRequests = this.buildBannerBidRequests(); const bidderRequest = this.buildBidderRequest(bidRequests); - const [ builtServerRequest ] = spec.buildRequests(bidRequests, bidderRequest); + const [builtServerRequest] = spec.buildRequests(bidRequests, bidderRequest); validateBuiltServerRequest(builtServerRequest, serverRequest); }); @@ -1303,7 +1303,7 @@ describe('33acrossBidAdapter:', function () { } } }); - const [ builtServerRequest ] = spec.buildRequests(bidRequests, bidderRequest); + const [builtServerRequest] = spec.buildRequests(bidRequests, bidderRequest); validateBuiltServerRequest(builtServerRequest, serverRequest); }); @@ -1330,7 +1330,7 @@ describe('33acrossBidAdapter:', function () { } } }); - const [ builtServerRequest ] = spec.buildRequests(bidRequests, bidderRequest); + const [builtServerRequest] = spec.buildRequests(bidRequests, bidderRequest); validateBuiltServerRequest(builtServerRequest, serverRequest); }); @@ -1355,7 +1355,7 @@ describe('33acrossBidAdapter:', function () { } }); - const [ builtServerRequest ] = spec.buildRequests(bidRequests, bidderRequest); + const [builtServerRequest] = spec.buildRequests(bidRequests, bidderRequest); validateBuiltServerRequest(builtServerRequest, serverRequest); }); @@ -1383,7 +1383,7 @@ describe('33acrossBidAdapter:', function () { } } }); - const [ builtServerRequest ] = spec.buildRequests(bidRequests, bidderRequest); + const [builtServerRequest] = spec.buildRequests(bidRequests, bidderRequest); validateBuiltServerRequest(builtServerRequest, serverRequest); }); @@ -1407,7 +1407,7 @@ describe('33acrossBidAdapter:', function () { } } }); - const [ builtServerRequest ] = spec.buildRequests(bidRequests, bidderRequest); + const [builtServerRequest] = spec.buildRequests(bidRequests, bidderRequest); validateBuiltServerRequest(builtServerRequest, serverRequest); }); @@ -1429,7 +1429,7 @@ describe('33acrossBidAdapter:', function () { } }); - const [ builtServerRequest ] = spec.buildRequests(bidRequests, bidderRequest); + const [builtServerRequest] = spec.buildRequests(bidRequests, bidderRequest); validateBuiltServerRequest(builtServerRequest, serverRequest); }); @@ -1460,7 +1460,7 @@ describe('33acrossBidAdapter:', function () { }; }); - const [ builtServerRequest ] = spec.buildRequests(bidRequestsWithGpid, bidderRequest); + const [builtServerRequest] = spec.buildRequests(bidRequestsWithGpid, bidderRequest); validateBuiltServerRequest(builtServerRequest, serverRequest); }); @@ -1479,7 +1479,7 @@ describe('33acrossBidAdapter:', function () { refererInfo: {} }); - const [ builtServerRequest ] = spec.buildRequests(bidRequests, bidderRequest); + const [builtServerRequest] = spec.buildRequests(bidRequests, bidderRequest); validateBuiltServerRequest(builtServerRequest, serverRequest); }); @@ -1533,7 +1533,7 @@ describe('33acrossBidAdapter:', function () { } }); - const [ builtServerRequest ] = spec.buildRequests(bidRequests, bidderRequest); + const [builtServerRequest] = spec.buildRequests(bidRequests, bidderRequest); validateBuiltServerRequest(builtServerRequest, serverRequest); }); @@ -1551,7 +1551,7 @@ describe('33acrossBidAdapter:', function () { const bidRequests = this.buildBannerBidRequests(); const bidderRequest = this.buildBidderRequest(bidRequests); - const [ builtServerRequest ] = spec.buildRequests(bidRequests, bidderRequest); + const [builtServerRequest] = spec.buildRequests(bidRequests, bidderRequest); validateBuiltServerRequest(builtServerRequest, serverRequest); }); @@ -1567,7 +1567,7 @@ describe('33acrossBidAdapter:', function () { ); const bidRequests = this.buildBannerBidRequests(); const bidderRequest = this.buildBidderRequest(bidRequests); - const [ builtServerRequest ] = spec.buildRequests(bidRequests, bidderRequest); + const [builtServerRequest] = spec.buildRequests(bidRequests, bidderRequest); validateBuiltServerRequest(builtServerRequest, serverRequest); }); @@ -1587,7 +1587,7 @@ describe('33acrossBidAdapter:', function () { bidRequests[0].getFloor = () => ({}); - const [ builtServerRequest ] = spec.buildRequests(bidRequests, bidderRequest); + const [builtServerRequest] = spec.buildRequests(bidRequests, bidderRequest); validateBuiltServerRequest(builtServerRequest, serverRequest); }); @@ -1599,13 +1599,13 @@ describe('33acrossBidAdapter:', function () { new TtxRequestBuilder() .withBanner() .withProduct() - .withFormatFloors('banner', [ 1.0, 0.10 ]) + .withFormatFloors('banner', [1.0, 0.10]) .build() ); const bidRequests = this.buildBannerBidRequests(); const bidderRequest = this.buildBidderRequest(bidRequests); - bidRequests[0].getFloor = ({size, currency, mediaType}) => { + bidRequests[0].getFloor = ({ size, currency, mediaType }) => { const floor = (size[0] === 300 && size[1] === 250) ? 1.0 : 0.10 return ( { @@ -1615,7 +1615,7 @@ describe('33acrossBidAdapter:', function () { ); }; - const [ builtServerRequest ] = spec.buildRequests(bidRequests, bidderRequest); + const [builtServerRequest] = spec.buildRequests(bidRequests, bidderRequest); validateBuiltServerRequest(builtServerRequest, serverRequest); }); @@ -1626,7 +1626,7 @@ describe('33acrossBidAdapter:', function () { context('and context is instream', function() { it('builds instream request with default params', function() { const bidRequests = new BidRequestsBuilder() - .withVideo({context: 'instream'}) + .withVideo({ context: 'instream' }) .build(); const ttxRequest = new TtxRequestBuilder() .withVideo() @@ -1636,7 +1636,7 @@ describe('33acrossBidAdapter:', function () { const serverRequest = this.buildServerRequest(ttxRequest); const bidderRequest = this.buildBidderRequest(bidRequests); - const [ builtServerRequest ] = spec.buildRequests(bidRequests, bidderRequest); + const [builtServerRequest] = spec.buildRequests(bidRequests, bidderRequest); validateBuiltServerRequest(builtServerRequest, serverRequest); }); @@ -1669,7 +1669,7 @@ describe('33acrossBidAdapter:', function () { }; const bidRequests = ( new BidRequestsBuilder() - .withVideo({context: 'instream', ...allowedParams}) + .withVideo({ context: 'instream', ...allowedParams }) .build() ); const ttxRequest = new TtxRequestBuilder() @@ -1678,7 +1678,7 @@ describe('33acrossBidAdapter:', function () { .build(); const serverRequest = this.buildServerRequest(ttxRequest); const bidderRequest = this.buildBidderRequest(bidRequests); - const [ builtServerRequest ] = spec.buildRequests(bidRequests, bidderRequest); + const [builtServerRequest] = spec.buildRequests(bidRequests, bidderRequest); validateBuiltServerRequest(builtServerRequest, serverRequest); }); @@ -1701,7 +1701,7 @@ describe('33acrossBidAdapter:', function () { const serverRequest = this.buildServerRequest(ttxRequest); const bidderRequest = this.buildBidderRequest(bidRequests); - const [ builtServerRequest ] = spec.buildRequests(bidRequests, bidderRequest); + const [builtServerRequest] = spec.buildRequests(bidRequests, bidderRequest); validateBuiltServerRequest(builtServerRequest, serverRequest); }); @@ -1719,12 +1719,12 @@ describe('33acrossBidAdapter:', function () { .build(); const serverRequest = this.buildServerRequest( new TtxRequestBuilder() - .withVideo({plcmt: 3, playbackmethod: [2]}) + .withVideo({ plcmt: 3, playbackmethod: [2] }) .withProduct('siab') .build() ); const bidderRequest = this.buildBidderRequest(bidRequests); - const [ builtServerRequest ] = spec.buildRequests(bidRequests, bidderRequest); + const [builtServerRequest] = spec.buildRequests(bidRequests, bidderRequest); validateBuiltServerRequest(builtServerRequest, serverRequest); }); @@ -1741,7 +1741,7 @@ describe('33acrossBidAdapter:', function () { ); const bidRequests = this.buildBannerBidRequests(); const bidderRequest = this.buildBidderRequest(bidRequests); - const [ builtServerRequest ] = spec.buildRequests(bidRequests, bidderRequest); + const [builtServerRequest] = spec.buildRequests(bidRequests, bidderRequest); validateBuiltServerRequest(builtServerRequest, serverRequest); }); @@ -1759,7 +1759,7 @@ describe('33acrossBidAdapter:', function () { .build() ); const bidderRequest = this.buildBidderRequest(bidRequests); - const [ builtServerRequest ] = spec.buildRequests(bidRequests, bidderRequest); + const [builtServerRequest] = spec.buildRequests(bidRequests, bidderRequest); validateBuiltServerRequest(builtServerRequest, serverRequest); }); @@ -1771,7 +1771,7 @@ describe('33acrossBidAdapter:', function () { const bidRequests = ( new BidRequestsBuilder() .withBanner() - .withVideo({context: 'outstream'}) + .withVideo({ context: 'outstream' }) .build() ); const serverRequest = this.buildServerRequest( @@ -1782,7 +1782,7 @@ describe('33acrossBidAdapter:', function () { .build() ); const bidderRequest = this.buildBidderRequest(bidRequests); - const [ builtServerRequest ] = spec.buildRequests(bidRequests, bidderRequest); + const [builtServerRequest] = spec.buildRequests(bidRequests, bidderRequest); validateBuiltServerRequest(builtServerRequest, serverRequest); }); @@ -1792,7 +1792,7 @@ describe('33acrossBidAdapter:', function () { const bidRequests = ( new BidRequestsBuilder() .withBanner() - .withVideo({context: 'instream', plcmt: 2}) + .withVideo({ context: 'instream', plcmt: 2 }) .build() ); @@ -1803,7 +1803,7 @@ describe('33acrossBidAdapter:', function () { .build(); const serverRequest = this.buildServerRequest(ttxRequest); const bidderRequest = this.buildBidderRequest(bidRequests); - const [ builtServerRequest ] = spec.buildRequests(bidRequests, bidderRequest); + const [builtServerRequest] = spec.buildRequests(bidRequests, bidderRequest); validateBuiltServerRequest(builtServerRequest, serverRequest); }); @@ -1815,7 +1815,7 @@ describe('33acrossBidAdapter:', function () { it('does not set any bidfloors in video', function() { const bidRequests = ( new BidRequestsBuilder() - .withVideo({context: 'outstream'}) + .withVideo({ context: 'outstream' }) .build() ); @@ -1828,7 +1828,7 @@ describe('33acrossBidAdapter:', function () { .build() ); const bidderRequest = this.buildBidderRequest(bidRequests); - const [ builtServerRequest ] = spec.buildRequests(bidRequests, bidderRequest); + const [builtServerRequest] = spec.buildRequests(bidRequests, bidderRequest); validateBuiltServerRequest(builtServerRequest, serverRequest); }); @@ -1838,11 +1838,11 @@ describe('33acrossBidAdapter:', function () { it('sets bidfloors in video', function() { const bidRequests = ( new BidRequestsBuilder() - .withVideo({context: 'outstream'}) + .withVideo({ context: 'outstream' }) .build() ); - bidRequests[0].getFloor = ({size, currency, mediaType}) => { + bidRequests[0].getFloor = ({ size, currency, mediaType }) => { const floor = (mediaType === 'video') ? 1.0 : 0.10 return ( { @@ -1855,11 +1855,11 @@ describe('33acrossBidAdapter:', function () { const ttxRequest = new TtxRequestBuilder() .withVideo() .withProduct() - .withFloors('video', [ 1.0 ]) + .withFloors('video', [1.0]) .build(); const serverRequest = this.buildServerRequest(ttxRequest); const bidderRequest = this.buildBidderRequest(bidRequests); - const [ builtServerRequest ] = spec.buildRequests(bidRequests, bidderRequest); + const [builtServerRequest] = spec.buildRequests(bidRequests, bidderRequest); validateBuiltServerRequest(builtServerRequest, serverRequest); }); @@ -1910,7 +1910,7 @@ describe('33acrossBidAdapter:', function () { } } }); - const [ builtServerRequest ] = spec.buildRequests(bidRequests, bidderRequest); + const [builtServerRequest] = spec.buildRequests(bidRequests, bidderRequest); validateBuiltServerRequest(builtServerRequest, serverRequest); }); @@ -1937,7 +1937,7 @@ describe('33acrossBidAdapter:', function () { } } }); - const [ builtServerRequest ] = spec.buildRequests(bidRequests, bidderRequest); + const [builtServerRequest] = spec.buildRequests(bidRequests, bidderRequest); validateBuiltServerRequest(builtServerRequest, serverRequest); }); @@ -1979,7 +1979,7 @@ describe('33acrossBidAdapter:', function () { .build() ); const bidderRequest = this.buildBidderRequest(bidRequests); - const [ builtServerRequest ] = spec.buildRequests(bidRequests, bidderRequest); + const [builtServerRequest] = spec.buildRequests(bidRequests, bidderRequest); validateBuiltServerRequest(builtServerRequest, serverRequest); }); @@ -2012,7 +2012,7 @@ describe('33acrossBidAdapter:', function () { } }) .withBanner() - .withVideo({context: 'outstream'}) + .withVideo({ context: 'outstream' }) .build(); const req1 = new TtxRequestBuilder() @@ -2066,7 +2066,7 @@ describe('33acrossBidAdapter:', function () { } }) .withBanner() - .withVideo({context: 'outstream'}) + .withVideo({ context: 'outstream' }) .build(); const req1 = new TtxRequestBuilder() @@ -2457,7 +2457,7 @@ describe('33acrossBidAdapter:', function () { it('returns sync urls with undefined consent string as param and gdpr=1', function() { spec.buildRequests(this.bidRequests); - const syncResults = spec.getUserSyncs(this.syncOptions, {}, {gdprApplies: true}); + const syncResults = spec.getUserSyncs(this.syncOptions, {}, { gdprApplies: true }); const expectedSyncs = [ { type: 'iframe', @@ -2477,7 +2477,7 @@ describe('33acrossBidAdapter:', function () { it('returns sync urls with gdpr_consent=consent string as param and gdpr=1', function() { spec.buildRequests(this.bidRequests); - const syncResults = spec.getUserSyncs(this.syncOptions, {}, {gdprApplies: true, consentString: 'consent123A'}); + const syncResults = spec.getUserSyncs(this.syncOptions, {}, { gdprApplies: true, consentString: 'consent123A' }); const expectedSyncs = [ { type: 'iframe', @@ -2497,7 +2497,7 @@ describe('33acrossBidAdapter:', function () { it('returns sync urls with undefined consent string as param and gdpr=0', function() { spec.buildRequests(this.bidRequests); - const syncResults = spec.getUserSyncs(this.syncOptions, {}, {gdprApplies: false}); + const syncResults = spec.getUserSyncs(this.syncOptions, {}, { gdprApplies: false }); const expectedSyncs = [ { type: 'iframe', @@ -2516,7 +2516,7 @@ describe('33acrossBidAdapter:', function () { it('returns sync urls with only consent string as param', function() { spec.buildRequests(this.bidRequests); - const syncResults = spec.getUserSyncs(this.syncOptions, {}, {consentString: 'consent123A'}); + const syncResults = spec.getUserSyncs(this.syncOptions, {}, { consentString: 'consent123A' }); const expectedSyncs = [ { type: 'iframe', @@ -2535,7 +2535,7 @@ describe('33acrossBidAdapter:', function () { it('returns sync urls with consent string as param and gdpr=0', function() { spec.buildRequests(this.bidRequests); - const syncResults = spec.getUserSyncs(this.syncOptions, {}, {gdprApplies: false, consentString: 'consent123A'}); + const syncResults = spec.getUserSyncs(this.syncOptions, {}, { gdprApplies: false, consentString: 'consent123A' }); const expectedSyncs = [ { type: 'iframe', diff --git a/test/spec/modules/33acrossIdSystem_spec.js b/test/spec/modules/33acrossIdSystem_spec.js index 3cf5995bb17..6c7c98d197c 100644 --- a/test/spec/modules/33acrossIdSystem_spec.js +++ b/test/spec/modules/33acrossIdSystem_spec.js @@ -3,9 +3,9 @@ import * as utils from 'src/utils.js'; import { server } from 'test/mocks/xhr.js'; import { uspDataHandler, coppaDataHandler, gppDataHandler } from 'src/adapterManager.js'; -import {createEidsArray} from '../../../modules/userId/eids.js'; -import {expect} from 'chai/index.mjs'; -import {attachIdSystem} from '../../../modules/userId/index.js'; +import { createEidsArray } from '../../../modules/userId/eids.js'; +import { expect } from 'chai/index.mjs'; +import { attachIdSystem } from '../../../modules/userId/index.js'; describe('33acrossIdSystem', () => { describe('name', () => { @@ -21,6 +21,10 @@ describe('33acrossIdSystem', () => { }); describe('getId', () => { + afterEach(() => { + sinon.restore(); + }); + it('should call endpoint', () => { const completeCallback = sinon.spy(); @@ -86,7 +90,7 @@ describe('33acrossIdSystem', () => { params: { pid: '12345' }, - enabledStorageTypes: [ 'html5' ], + enabledStorageTypes: ['html5'], storage: {} }); @@ -96,6 +100,7 @@ describe('33acrossIdSystem', () => { const removeDataFromLocalStorage = sinon.stub(storage, 'removeDataFromLocalStorage'); const setCookie = sinon.stub(storage, 'setCookie'); + sinon.stub(storage, 'cookiesAreEnabled').returns(true); sinon.stub(domainUtils, 'domainOverride').returns('foo.com'); request.respond(200, { @@ -110,10 +115,6 @@ describe('33acrossIdSystem', () => { expect(removeDataFromLocalStorage.calledWithExactly('33acrossIdHm')).to.be.false; expect(setCookie.calledWithExactly('33acrossIdHm', '', sinon.match.string, 'Lax', 'foo.com')).to.be.false; - - removeDataFromLocalStorage.restore(); - setCookie.restore(); - domainUtils.domainOverride.restore(); }); }); @@ -134,7 +135,7 @@ describe('33acrossIdSystem', () => { pid: '12345', ...opts }, - enabledStorageTypes: [ 'cookie' ], + enabledStorageTypes: ['cookie'], storage: { expires: 30 } }); @@ -172,7 +173,7 @@ describe('33acrossIdSystem', () => { pid: '12345', ...opts }, - enabledStorageTypes: [ 'html5' ], + enabledStorageTypes: ['html5'], storage: {} }); @@ -208,7 +209,7 @@ describe('33acrossIdSystem', () => { pid: '12345', ...opts }, - enabledStorageTypes: [ 'cookie', 'html5' ], + enabledStorageTypes: ['cookie', 'html5'], storage: {} }); @@ -250,7 +251,7 @@ describe('33acrossIdSystem', () => { pid: '12345', ...opts }, - enabledStorageTypes: [ 'html5' ], + enabledStorageTypes: ['html5'], storage: {} }); @@ -260,6 +261,7 @@ describe('33acrossIdSystem', () => { const removeDataFromLocalStorage = sinon.stub(storage, 'removeDataFromLocalStorage'); const setCookie = sinon.stub(storage, 'setCookie'); + const cookiesAreEnabled = sinon.stub(storage, 'cookiesAreEnabled').returns(true); sinon.stub(domainUtils, 'domainOverride').returns('foo.com'); request.respond(200, { @@ -277,6 +279,7 @@ describe('33acrossIdSystem', () => { removeDataFromLocalStorage.restore(); setCookie.restore(); + cookiesAreEnabled.restore(); domainUtils.domainOverride.restore(); }); }); @@ -293,7 +296,7 @@ describe('33acrossIdSystem', () => { pid: '12345', ...opts }, - enabledStorageTypes: [ 'cookie' ], + enabledStorageTypes: ['cookie'], storage: { expires: 30 } }); @@ -331,7 +334,7 @@ describe('33acrossIdSystem', () => { pid: '12345', ...opts }, - enabledStorageTypes: [ 'html5' ], + enabledStorageTypes: ['html5'], storage: {} }); @@ -367,7 +370,7 @@ describe('33acrossIdSystem', () => { pid: '12345', ...opts }, - enabledStorageTypes: [ 'cookie', 'html5' ], + enabledStorageTypes: ['cookie', 'html5'], storage: {} }); @@ -409,7 +412,7 @@ describe('33acrossIdSystem', () => { pid: '12345', ...opts }, - enabledStorageTypes: [ 'html5' ], + enabledStorageTypes: ['html5'], storage: {} }); @@ -419,6 +422,7 @@ describe('33acrossIdSystem', () => { const removeDataFromLocalStorage = sinon.stub(storage, 'removeDataFromLocalStorage'); const setCookie = sinon.stub(storage, 'setCookie'); + const cookiesAreEnabled = sinon.stub(storage, 'cookiesAreEnabled').returns(true); sinon.stub(domainUtils, 'domainOverride').returns('foo.com'); request.respond(200, { @@ -436,6 +440,7 @@ describe('33acrossIdSystem', () => { removeDataFromLocalStorage.restore(); setCookie.restore(); + cookiesAreEnabled.restore(); domainUtils.domainOverride.restore(); }); }); @@ -452,7 +457,7 @@ describe('33acrossIdSystem', () => { pid: '12345', storeFpid: false }, - enabledStorageTypes: [ 'cookie' ], + enabledStorageTypes: ['cookie'], storage: { expires: 30 } @@ -490,7 +495,7 @@ describe('33acrossIdSystem', () => { pid: '12345', storeFpid: false }, - enabledStorageTypes: [ 'html5' ], + enabledStorageTypes: ['html5'], storage: {} }); @@ -515,6 +520,40 @@ describe('33acrossIdSystem', () => { setDataInLocalStorage.restore(); }); + + it('should not store a publisher-provided hashed email in local storage', () => { + const completeCallback = () => {}; + + const { callback } = thirtyThreeAcrossIdSubmodule.getId({ + params: { + pid: '12345', + storeFpid: false, + hem: '33acrossIdHmValue+' + }, + enabledStorageTypes: ['html5'], + storage: {} + }); + + callback(completeCallback); + + const [request] = server.requests; + + const setDataInLocalStorage = sinon.stub(storage, 'setDataInLocalStorage'); + + request.respond(200, { + 'Content-Type': 'application/json' + }, JSON.stringify({ + succeeded: true, + data: { + envelope: 'foo' + }, + expires: 1645667805067 + })); + + expect(setDataInLocalStorage.calledWithExactly('33acrossIdHm', '33acrossIdHmValue+')).to.be.false; + + setDataInLocalStorage.restore(); + }); }); }); @@ -528,7 +567,7 @@ describe('33acrossIdSystem', () => { pid: '12345', storeTpid: false }, - enabledStorageTypes: [ 'cookie' ], + enabledStorageTypes: ['cookie'], storage: { expires: 30 } @@ -564,7 +603,7 @@ describe('33acrossIdSystem', () => { pid: '12345', storeTpid: false }, - enabledStorageTypes: [ 'html5' ], + enabledStorageTypes: ['html5'], storage: {} }); @@ -600,7 +639,7 @@ describe('33acrossIdSystem', () => { params: { pid: '12345' }, - enabledStorageTypes: [ 'html5' ], + enabledStorageTypes: ['html5'], storage: {} }); @@ -610,6 +649,7 @@ describe('33acrossIdSystem', () => { const removeDataFromLocalStorage = sinon.stub(storage, 'removeDataFromLocalStorage'); const setCookie = sinon.stub(storage, 'setCookie'); + sinon.stub(storage, 'cookiesAreEnabled').returns(true); sinon.stub(domainUtils, 'domainOverride').returns('foo.com'); request.respond(200, { @@ -626,10 +666,6 @@ describe('33acrossIdSystem', () => { expect(removeDataFromLocalStorage.calledWith(`33acrossId${suffix}`)).to.be.true; expect(setCookie.calledWithExactly(`33acrossId${suffix}`, '', sinon.match.string, 'Lax', 'foo.com')).to.be.true; }); - - removeDataFromLocalStorage.restore(); - setCookie.restore(); - domainUtils.domainOverride.restore(); }); }); @@ -642,7 +678,7 @@ describe('33acrossIdSystem', () => { pid: '12345' } }, { - gdpr: {gdprApplies: true} + gdpr: { gdprApplies: true } }); expect(logWarnSpy.calledOnceWithExactly('33acrossId: Submodule cannot be used where GDPR applies')).to.be.true; @@ -660,7 +696,7 @@ describe('33acrossIdSystem', () => { pid: '12345' } }, { - gdpr: {gdprApplies: false} + gdpr: { gdprApplies: false } }); callback(completeCallback); @@ -845,7 +881,7 @@ describe('33acrossIdSystem', () => { params: { pid: '12345' }, - enabledStorageTypes: [ 'html5' ], + enabledStorageTypes: ['html5'], storage: {} }); @@ -870,7 +906,7 @@ describe('33acrossIdSystem', () => { params: { pid: '12345' }, - enabledStorageTypes: [ 'cookie' ], + enabledStorageTypes: ['cookie'], storage: {} }); @@ -912,7 +948,7 @@ describe('33acrossIdSystem', () => { params: { pid: '12345' }, - enabledStorageTypes: [ 'html5' ], + enabledStorageTypes: ['html5'], storage: {} }); @@ -937,7 +973,7 @@ describe('33acrossIdSystem', () => { params: { pid: '12345' }, - enabledStorageTypes: [ 'cookie' ], + enabledStorageTypes: ['cookie'], storage: {} }); @@ -980,7 +1016,7 @@ describe('33acrossIdSystem', () => { pid: '12345', hem: '33acrossIdHmValue+' }, - enabledStorageTypes: [ 'html5' ], + enabledStorageTypes: ['html5'], storage: {} }); @@ -1007,7 +1043,7 @@ describe('33acrossIdSystem', () => { pid: '12345', hem: '33acrossIdHmValue+' }, - enabledStorageTypes: [ 'html5' ], + enabledStorageTypes: ['html5'], storage: {} }); @@ -1043,7 +1079,7 @@ describe('33acrossIdSystem', () => { pid: '12345', hem: '33acrossIdHmValue+' }, - enabledStorageTypes: [ 'cookie' ], + enabledStorageTypes: ['cookie'], storage: { expires: 30 } }); @@ -1094,7 +1130,7 @@ describe('33acrossIdSystem', () => { pid: '12345' // No hashed email via config option. }, - enabledStorageTypes: [ 'html5' ], + enabledStorageTypes: ['html5'], storage: {} }); @@ -1120,7 +1156,7 @@ describe('33acrossIdSystem', () => { params: { pid: '12345' }, - enabledStorageTypes: [ 'html5' ], + enabledStorageTypes: ['html5'], storage: {} }); @@ -1155,7 +1191,7 @@ describe('33acrossIdSystem', () => { params: { pid: '12345' }, - enabledStorageTypes: [ 'cookie' ], + enabledStorageTypes: ['cookie'], storage: { expires: 30 } }); @@ -1192,7 +1228,7 @@ describe('33acrossIdSystem', () => { params: { pid: '12345' }, - enabledStorageTypes: [ 'html5' ], + enabledStorageTypes: ['html5'], storage: {} }); @@ -1217,7 +1253,7 @@ describe('33acrossIdSystem', () => { params: { pid: '12345' }, - enabledStorageTypes: [ 'cookie' ], + enabledStorageTypes: ['cookie'], storage: {} }); @@ -1466,7 +1502,7 @@ describe('33acrossIdSystem', () => { params: { pid: '12345' }, - enabledStorageTypes: [ 'html5' ], + enabledStorageTypes: ['html5'], storage: {} }); @@ -1476,6 +1512,7 @@ describe('33acrossIdSystem', () => { const removeDataFromLocalStorage = sinon.stub(storage, 'removeDataFromLocalStorage'); const setCookie = sinon.stub(storage, 'setCookie'); + const cookiesAreEnabled = sinon.stub(storage, 'cookiesAreEnabled').returns(true); sinon.stub(domainUtils, 'domainOverride').returns('foo.com'); request.respond(200, { @@ -1493,8 +1530,42 @@ describe('33acrossIdSystem', () => { removeDataFromLocalStorage.restore(); setCookie.restore(); + cookiesAreEnabled.restore(); domainUtils.domainOverride.restore(); }); + + it('should not wipe any stored hashed email when first-party ID support is disabled', () => { + const completeCallback = () => {}; + + const { callback } = thirtyThreeAcrossIdSubmodule.getId({ + params: { + pid: '12345', + storeFpid: false + }, + enabledStorageTypes: ['html5'], + storage: {} + }); + + callback(completeCallback); + + const [request] = server.requests; + + const removeDataFromLocalStorage = sinon.stub(storage, 'removeDataFromLocalStorage'); + + request.respond(200, { + 'Content-Type': 'application/json' + }, JSON.stringify({ + succeeded: true, + data: { + // no envelope field + }, + expires: 1645667805067 + })); + + expect(removeDataFromLocalStorage.calledWithExactly('33acrossIdHm')).to.be.false; + + removeDataFromLocalStorage.restore(); + }); }); context('when the server returns an error status code', () => { diff --git a/test/spec/modules/360playvidBidAdapter_spec.js b/test/spec/modules/360playvidBidAdapter_spec.js index 393afbf4545..7718c0c2eca 100644 --- a/test/spec/modules/360playvidBidAdapter_spec.js +++ b/test/spec/modules/360playvidBidAdapter_spec.js @@ -483,7 +483,7 @@ describe('360PlayVidBidAdapter', function () { const syncData = spec.getUserSyncs({}, {}, { consentString: 'ALL', gdprApplies: true, - }, {}); + }, undefined); expect(syncData).to.be.an('array').which.is.not.empty; expect(syncData[0]).to.be.an('object') expect(syncData[0].type).to.be.a('string') @@ -492,9 +492,7 @@ describe('360PlayVidBidAdapter', function () { expect(syncData[0].url).to.equal('https://cookie.360playvid.com/image?pbjs=1&gdpr=1&gdpr_consent=ALL&coppa=0') }); it('Should return array of objects with proper sync config , include CCPA', function() { - const syncData = spec.getUserSyncs({}, {}, {}, { - consentString: '1---' - }); + const syncData = spec.getUserSyncs({}, {}, {}, '1---'); expect(syncData).to.be.an('array').which.is.not.empty; expect(syncData[0]).to.be.an('object') expect(syncData[0].type).to.be.a('string') @@ -503,7 +501,7 @@ describe('360PlayVidBidAdapter', function () { expect(syncData[0].url).to.equal('https://cookie.360playvid.com/image?pbjs=1&ccpa_consent=1---&coppa=0') }); it('Should return array of objects with proper sync config , include GPP', function() { - const syncData = spec.getUserSyncs({}, {}, {}, {}, { + const syncData = spec.getUserSyncs({}, {}, {}, undefined, { gppString: 'abc123', applicableSections: [8] }); diff --git a/test/spec/modules/51DegreesRtdProvider_spec.js b/test/spec/modules/51DegreesRtdProvider_spec.js index 4993f9097f6..7e1840e3dc7 100644 --- a/test/spec/modules/51DegreesRtdProvider_spec.js +++ b/test/spec/modules/51DegreesRtdProvider_spec.js @@ -8,7 +8,7 @@ import { getBidRequestData, fiftyOneDegreesSubmodule, } from 'modules/51DegreesRtdProvider'; -import {mergeDeep} from '../../../src/utils.js'; +import { mergeDeep } from '../../../src/utils.js'; const inject51DegreesMeta = () => { const meta = document.createElement('meta'); @@ -80,7 +80,7 @@ describe('51DegreesRtdProvider', function() { describe('extractConfig', function() { it('returns the resourceKey from the moduleConfig', function() { const reqBidsConfigObj = {}; - const moduleConfig = {params: {resourceKey: 'TEST_RESOURCE_KEY'}}; + const moduleConfig = { params: { resourceKey: 'TEST_RESOURCE_KEY' } }; expect(extractConfig(moduleConfig, reqBidsConfigObj)).to.deep.equal({ resourceKey: 'TEST_RESOURCE_KEY', onPremiseJSUrl: undefined, @@ -89,7 +89,7 @@ describe('51DegreesRtdProvider', function() { it('returns the onPremiseJSUrl from the moduleConfig', function() { const reqBidsConfigObj = {}; - const moduleConfig = {params: {onPremiseJSUrl: 'https://example.com/51Degrees.core.js'}}; + const moduleConfig = { params: { onPremiseJSUrl: 'https://example.com/51Degrees.core.js' } }; expect(extractConfig(moduleConfig, reqBidsConfigObj)).to.deep.equal({ onPremiseJSUrl: 'https://example.com/51Degrees.core.js', resourceKey: undefined, @@ -98,30 +98,34 @@ describe('51DegreesRtdProvider', function() { it('throws an error if neither resourceKey nor onPremiseJSUrl is provided', function() { const reqBidsConfigObj = {}; - const moduleConfig = {params: {}}; + const moduleConfig = { params: {} }; expect(() => extractConfig(moduleConfig, reqBidsConfigObj)).to.throw(); }); it('throws an error if both resourceKey and onPremiseJSUrl are provided', function() { const reqBidsConfigObj = {}; - const moduleConfig = {params: { - resourceKey: 'TEST_RESOURCE_KEY', - onPremiseJSUrl: 'https://example.com/51Degrees.core.js', - }}; + const moduleConfig = { + params: { + resourceKey: 'TEST_RESOURCE_KEY', + onPremiseJSUrl: 'https://example.com/51Degrees.core.js', + } + }; expect(() => extractConfig(moduleConfig, reqBidsConfigObj)).to.throw(); }); it('throws an error if the resourceKey is equal to "" from example', function() { const reqBidsConfigObj = {}; - const moduleConfig = {params: {resourceKey: ''}}; + const moduleConfig = { params: { resourceKey: '' } }; expect(() => extractConfig(moduleConfig, reqBidsConfigObj)).to.throw(); }); it('sets the resourceKey to undefined if it was set to "0"', function() { - const moduleConfig = {params: { - resourceKey: '0', - onPremiseJSUrl: 'https://example.com/51Degrees.core.js', - }}; + const moduleConfig = { + params: { + resourceKey: '0', + onPremiseJSUrl: 'https://example.com/51Degrees.core.js', + } + }; expect(extractConfig(moduleConfig, {})).to.deep.equal({ resourceKey: undefined, onPremiseJSUrl: 'https://example.com/51Degrees.core.js', @@ -129,10 +133,12 @@ describe('51DegreesRtdProvider', function() { }); it('sets the onPremiseJSUrl to undefined if it was set to "0"', function() { - const moduleConfig = {params: { - resourceKey: 'TEST_RESOURCE_KEY', - onPremiseJSUrl: '0', - }}; + const moduleConfig = { + params: { + resourceKey: 'TEST_RESOURCE_KEY', + onPremiseJSUrl: '0', + } + }; expect(extractConfig(moduleConfig, {})).to.deep.equal({ resourceKey: 'TEST_RESOURCE_KEY', onPremiseJSUrl: undefined, @@ -141,10 +147,10 @@ describe('51DegreesRtdProvider', function() { it('throws an error if the onPremiseJSUrl is not a valid URL', function() { expect(() => extractConfig({ - params: {onPremiseJSUrl: 'invalid URL'} + params: { onPremiseJSUrl: 'invalid URL' } }, {})).to.throw(); expect(() => extractConfig({ - params: {onPremiseJSUrl: 'www.example.com/51Degrees.core.js'} + params: { onPremiseJSUrl: 'www.example.com/51Degrees.core.js' } }, {})).to.throw(); }); @@ -158,7 +164,7 @@ describe('51DegreesRtdProvider', function() { VALID_URLS.forEach(url => { expect(() => extractConfig({ - params: {onPremiseJSUrl: url} + params: { onPremiseJSUrl: url } }, {})).to.not.throw(); }); }); @@ -209,7 +215,7 @@ describe('51DegreesRtdProvider', function() { }; it('returns the cloud URL if the resourceKey is provided', function() { - const config = {resourceKey: 'TEST_RESOURCE_KEY'}; + const config = { resourceKey: 'TEST_RESOURCE_KEY' }; expect(get51DegreesJSURL(config, mockWindow)).to.equal( 'https://cloud.51degrees.com/api/v4/TEST_RESOURCE_KEY.js?' + `51D_ScreenPixelsHeight=${mockWindow.screen.height}&` + @@ -219,7 +225,7 @@ describe('51DegreesRtdProvider', function() { }); it('returns the on-premise URL if the onPremiseJSUrl is provided', function () { - const config = {onPremiseJSUrl: 'https://example.com/51Degrees.core.js'}; + const config = { onPremiseJSUrl: 'https://example.com/51Degrees.core.js' }; expect(get51DegreesJSURL(config, mockWindow)).to.equal( `https://example.com/51Degrees.core.js?` + `51D_ScreenPixelsHeight=${mockWindow.screen.height}&` + @@ -229,7 +235,7 @@ describe('51DegreesRtdProvider', function() { }); it('doesn\'t override static query string parameters', function () { - const config = {onPremiseJSUrl: 'https://example.com/51Degrees.core.js?test=1'}; + const config = { onPremiseJSUrl: 'https://example.com/51Degrees.core.js?test=1' }; expect(get51DegreesJSURL(config, mockWindow)).to.equal( `https://example.com/51Degrees.core.js?test=1&` + `51D_ScreenPixelsHeight=${mockWindow.screen.height}&` + @@ -270,7 +276,7 @@ describe('51DegreesRtdProvider', function() { delete mockWindow.screen; delete mockWindow.devicePixelRatio; - const config = {onPremiseJSUrl: 'https://example.com/51Degrees.core.js'}; + const config = { onPremiseJSUrl: 'https://example.com/51Degrees.core.js' }; expect(get51DegreesJSURL(config, mockWindow)).to.equal('https://example.com/51Degrees.core.js'); expect(get51DegreesJSURL(config, window)).to.not.equal('https://example.com/51Degrees.core.js'); }); @@ -315,7 +321,7 @@ describe('51DegreesRtdProvider', function() { it('sets value of ORTB2 key if it is not empty', function() { const data = {}; deepSetNotEmptyValue(data, 'TEST_ORTB2_KEY', 'TEST_ORTB2_VALUE'); - expect(data).to.deep.equal({TEST_ORTB2_KEY: 'TEST_ORTB2_VALUE'}); + expect(data).to.deep.equal({ TEST_ORTB2_KEY: 'TEST_ORTB2_VALUE' }); deepSetNotEmptyValue(data, 'test2.TEST_ORTB2_KEY_2', 'TEST_ORTB2_VALUE_2'); expect(data).to.deep.equal({ TEST_ORTB2_KEY: 'TEST_ORTB2_VALUE', @@ -365,27 +371,63 @@ describe('51DegreesRtdProvider', function() { }); it('does not set the deviceid if it is not provided', function() { - const device = {...fiftyOneDegreesDevice}; + const device = { ...fiftyOneDegreesDevice }; delete device.deviceid; expect(convert51DegreesDeviceToOrtb2(device).device.ext).to.not.have.any.keys('fiftyonedegrees_deviceId'); expect(convert51DegreesDeviceToOrtb2(device).device.ext.fod).to.not.have.any.keys('deviceId'); }); it('sets the model to hardwarename if hardwaremodel is not provided', function() { - const device = {...fiftyOneDegreesDevice}; + const device = { ...fiftyOneDegreesDevice }; delete device.hardwaremodel; - expect(convert51DegreesDeviceToOrtb2(device).device).to.deep.include({model: 'Macintosh'}); + expect(convert51DegreesDeviceToOrtb2(device).device).to.deep.include({ model: 'Macintosh' }); }); it('does not set the model if hardwarename is empty', function() { - const device = {...fiftyOneDegreesDevice}; + const device = { ...fiftyOneDegreesDevice }; delete device.hardwaremodel; device.hardwarename = []; expect(convert51DegreesDeviceToOrtb2(device).device).to.not.have.any.keys('model'); }); + it('prefers hardwarenameprefix over hardwaremodel for model field', function() { + const device = { ...fiftyOneDegreesDevice, hardwarenameprefix: 'iPhone' }; + expect(convert51DegreesDeviceToOrtb2(device).device).to.deep.include({ model: 'iPhone' }); + }); + + it('falls back to hardwaremodel when hardwarenameprefix is not provided', function() { + const device = { ...fiftyOneDegreesDevice }; + delete device.hardwarenameprefix; + expect(convert51DegreesDeviceToOrtb2(device).device).to.deep.include({ model: 'Macintosh' }); + }); + + it('sets hwv from hardwarenameversion when provided', function() { + const device = { ...fiftyOneDegreesDevice, hardwarenameversion: '12 Pro Max' }; + expect(convert51DegreesDeviceToOrtb2(device).device).to.deep.include({ hwv: '12 Pro Max' }); + }); + + it('does not set hwv if hardwarenameversion is not provided', function() { + const device = { ...fiftyOneDegreesDevice }; + delete device.hardwarenameversion; + expect(convert51DegreesDeviceToOrtb2(device).device).to.not.have.any.keys('hwv'); + }); + + it('sets model from hardwarenameprefix independently of hwv from hardwarenameversion', function() { + const deviceWithPrefix = { ...fiftyOneDegreesDevice, hardwarenameprefix: 'iPhone' }; + delete deviceWithPrefix.hardwarenameversion; + const resultWithPrefix = convert51DegreesDeviceToOrtb2(deviceWithPrefix).device; + expect(resultWithPrefix).to.deep.include({ model: 'iPhone' }); + expect(resultWithPrefix).to.not.have.any.keys('hwv'); + + const deviceWithVersion = { ...fiftyOneDegreesDevice, hardwarenameversion: '12 Pro Max' }; + delete deviceWithVersion.hardwarenameprefix; + const resultWithVersion = convert51DegreesDeviceToOrtb2(deviceWithVersion).device; + expect(resultWithVersion).to.deep.include({ hwv: '12 Pro Max' }); + expect(resultWithVersion).to.deep.include({ model: 'Macintosh' }); + }); + it('does not set the ppi if screeninchesheight is not provided', function() { - const device = {...fiftyOneDegreesDevice}; + const device = { ...fiftyOneDegreesDevice }; delete device.screeninchesheight; expect(convert51DegreesDeviceToOrtb2(device).device).to.not.have.any.keys('ppi'); }); @@ -454,14 +496,14 @@ describe('51DegreesRtdProvider', function() { it('calls the callback even if submodule fails (wrong config)', function() { const callback = sinon.spy(); - const moduleConfig = {params: {}}; + const moduleConfig = { params: {} }; getBidRequestData(reqBidsConfigObj, callback, moduleConfig, {}); expect(callback.calledOnce).to.be.true; }); it('calls the callback even if submodule fails (on-premise, non-working URL)', async function() { const callback = sinon.spy(); - const moduleConfig = {params: {onPremiseJSUrl: 'http://localhost:12345/test/51Degrees.core.js'}}; + const moduleConfig = { params: { onPremiseJSUrl: 'http://localhost:12345/test/51Degrees.core.js' } }; getBidRequestData(reqBidsConfigObj, callback, moduleConfig, {}); await new Promise(resolve => setTimeout(resolve, 100)); @@ -470,7 +512,7 @@ describe('51DegreesRtdProvider', function() { it('calls the callback even if submodule fails (invalid resource key)', async function() { const callback = sinon.spy(); - const moduleConfig = {params: {resourceKey: 'INVALID_RESOURCE_KEY'}}; + const moduleConfig = { params: { resourceKey: 'INVALID_RESOURCE_KEY' } }; getBidRequestData(reqBidsConfigObj, callback, moduleConfig, {}); await new Promise(resolve => setTimeout(resolve, 100)); @@ -480,7 +522,7 @@ describe('51DegreesRtdProvider', function() { it('works with Delegate-CH meta tag', async function() { inject51DegreesMeta(); const callback = sinon.spy(); - const moduleConfig = {params: {resourceKey: 'INVALID_RESOURCE_KEY'}}; + const moduleConfig = { params: { resourceKey: 'INVALID_RESOURCE_KEY' } }; getBidRequestData(reqBidsConfigObj, callback, moduleConfig, {}); await new Promise(resolve => setTimeout(resolve, 100)); expect(callback.calledOnce).to.be.true; @@ -488,7 +530,7 @@ describe('51DegreesRtdProvider', function() { it('has the correct ORTB2 data', async function() { const callback = sinon.spy(); - const moduleConfig = {params: {resourceKey: 'INVALID_RESOURCE_KEY'}}; + const moduleConfig = { params: { resourceKey: 'INVALID_RESOURCE_KEY' } }; getBidRequestData(reqBidsConfigObj, callback, moduleConfig, {}); await new Promise(resolve => setTimeout(resolve, 100)); expect(callback.calledOnce).to.be.true; diff --git a/test/spec/modules/AsteriobidPbmAnalyticsAdapter_spec.js b/test/spec/modules/AsteriobidPbmAnalyticsAdapter_spec.js index d94368eeeb6..e148042ce09 100644 --- a/test/spec/modules/AsteriobidPbmAnalyticsAdapter_spec.js +++ b/test/spec/modules/AsteriobidPbmAnalyticsAdapter_spec.js @@ -1,8 +1,8 @@ -import prebidmanagerAnalytics, {storage} from 'modules/AsteriobidPbmAnalyticsAdapter.js'; -import {expect} from 'chai'; -import {server} from 'test/mocks/xhr.js'; +import prebidmanagerAnalytics, { storage } from 'modules/AsteriobidPbmAnalyticsAdapter.js'; +import { expect } from 'chai'; +import { server } from 'test/mocks/xhr.js'; import * as utils from 'src/utils.js'; -import {expectEvents} from '../../helpers/analytics.js'; +import { expectEvents } from '../../helpers/analytics.js'; import { EVENTS } from 'src/constants.js'; const events = require('src/events'); diff --git a/test/spec/modules/ViouslyBidAdapter_spec.js b/test/spec/modules/ViouslyBidAdapter_spec.js index 16f32e3d6ab..c7fbf9a4bd6 100644 --- a/test/spec/modules/ViouslyBidAdapter_spec.js +++ b/test/spec/modules/ViouslyBidAdapter_spec.js @@ -1,10 +1,10 @@ -import {expect} from 'chai'; +import { expect } from 'chai'; import { deepClone, mergeDeep } from 'src/utils'; import { BANNER, VIDEO } from 'src/mediaTypes'; import { createEidsArray } from 'modules/userId/eids.js'; -import {spec as adapter} from 'modules/viouslyBidAdapter'; +import { spec as adapter } from 'modules/viouslyBidAdapter'; import sinon from 'sinon'; import { config } from 'src/config.js'; diff --git a/test/spec/modules/a1MediaBidAdapter_spec.js b/test/spec/modules/a1MediaBidAdapter_spec.js index 3c5fa14c61d..361644701cb 100644 --- a/test/spec/modules/a1MediaBidAdapter_spec.js +++ b/test/spec/modules/a1MediaBidAdapter_spec.js @@ -6,7 +6,7 @@ import 'modules/priceFloors.js'; import { replaceAuctionPrice } from '../../../src/utils.js'; const ortbBlockParams = { - battr: [ 13 ], + battr: [13], bcat: ['IAB1-1'] }; const getBidderRequest = (isMulti = false) => { @@ -22,7 +22,7 @@ const getBidderRequest = (isMulti = false) => { mediaTypes: { banner: { sizes: [ - [ 320, 100 ], + [320, 100], ] }, ...(isMulti && { @@ -32,7 +32,8 @@ const getBidderRequest = (isMulti = false) => { native: { title: { required: true, - }} + } + } }) }, ...(isMulti && { diff --git a/test/spec/modules/a1MediaRtdProvider_spec.js b/test/spec/modules/a1MediaRtdProvider_spec.js index f313062b192..abadb4fd7cc 100644 --- a/test/spec/modules/a1MediaRtdProvider_spec.js +++ b/test/spec/modules/a1MediaRtdProvider_spec.js @@ -30,7 +30,7 @@ const a1TestOrtbObj = { ext: { segtax: 900 }, - segment: [{id: 'test'}] + segment: [{ id: 'test' }] } ], ext: { diff --git a/test/spec/modules/aaxBlockmeter_spec.js b/test/spec/modules/aaxBlockmeter_spec.js index 88b750274ee..3b1997901b5 100644 --- a/test/spec/modules/aaxBlockmeter_spec.js +++ b/test/spec/modules/aaxBlockmeter_spec.js @@ -1,6 +1,6 @@ -import {aaxBlockmeterRtdModule} from '../../../modules/aaxBlockmeterRtdProvider.js'; +import { aaxBlockmeterRtdModule } from '../../../modules/aaxBlockmeterRtdProvider.js'; import * as sinon from 'sinon'; -import {assert} from 'chai'; +import { assert } from 'chai'; let sandbox; let getTargetingDataSpy; @@ -31,11 +31,11 @@ describe('aaxBlockmeter realtime module', function () { }); it('init should return false when config.params id is empty', function () { - assert.equal(aaxBlockmeterRtdModule.init({params: {}}), false); + assert.equal(aaxBlockmeterRtdModule.init({ params: {} }), false); }); it('init should return true when config.params.pub is not string', function () { - assert.equal(aaxBlockmeterRtdModule.init({params: {pub: 12345}}), false); + assert.equal(aaxBlockmeterRtdModule.init({ params: { pub: 12345 } }), false); }); it('init should return true when config.params.pub id is passed and is string typed', function () { @@ -46,8 +46,8 @@ describe('aaxBlockmeter realtime module', function () { it('should return ad unit codes when ad units are present', function () { const codes = ['code1', 'code2']; assert.deepEqual(aaxBlockmeterRtdModule.getTargetingData(codes), { - code1: {'atk': 'code1'}, - code2: {'atk': 'code2'}, + code1: { 'atk': 'code1' }, + code2: { 'atk': 'code2' }, }); }); diff --git a/test/spec/modules/ablidaBidAdapter_spec.js b/test/spec/modules/ablidaBidAdapter_spec.js index f5e633369aa..da209859e2f 100644 --- a/test/spec/modules/ablidaBidAdapter_spec.js +++ b/test/spec/modules/ablidaBidAdapter_spec.js @@ -1,6 +1,6 @@ -import {assert, expect} from 'chai'; -import {spec} from 'modules/ablidaBidAdapter.js'; -import {newBidder} from 'src/adapters/bidderFactory.js'; +import { assert, expect } from 'chai'; +import { spec } from 'modules/ablidaBidAdapter.js'; +import { newBidder } from 'src/adapters/bidderFactory.js'; import * as utils from 'src/utils.js'; const ENDPOINT_URL = 'https://bidder.ablida.net/prebid'; @@ -17,7 +17,7 @@ describe('ablidaBidAdapter', function () { bidderRequestsCount: 1, bidderWinsCount: 0, bidId: '1234asdf1234', - mediaTypes: {banner: {sizes: [[300, 250]]}}, + mediaTypes: { banner: { sizes: [[300, 250]] } }, params: { placementId: 123 }, @@ -42,7 +42,7 @@ describe('ablidaBidAdapter', function () { bidderRequestId: '14d2939272a26a', bidderRequestsCount: 1, bidderWinsCount: 0, - mediaTypes: {banner: {sizes: [[300, 250]]}}, + mediaTypes: { banner: { sizes: [[300, 250]] } }, params: { placementId: 123 }, @@ -81,7 +81,7 @@ describe('ablidaBidAdapter', function () { device: 'desktop', gdprConsent: undefined, jaySupported: true, - mediaTypes: {banner: {sizes: [[300, 250]]}}, + mediaTypes: { banner: { sizes: [[300, 250]] } }, placementId: 'testPlacementId', width: 300, height: 200, diff --git a/test/spec/modules/aceexBidAdapter_spec.js b/test/spec/modules/aceexBidAdapter_spec.js new file mode 100644 index 00000000000..dd83280f78e --- /dev/null +++ b/test/spec/modules/aceexBidAdapter_spec.js @@ -0,0 +1,248 @@ +import { expect } from 'chai'; +import { spec } from '../../../modules/aceexBidAdapter.js'; + +describe('aceexBidAdapter', function () { + const makeBidderRequest = (ortb2 = {}) => ({ + bidderCode: 'aceex', + auctionId: 'auction-1', + bidderRequestId: 'br-1', + ortb2 + }); + + const makeBid = (overrides = {}) => ({ + bidId: overrides.bidId || 'bid-1', + bidder: 'aceex', + params: { + publisherId: 'pub-1', + trafficType: 'banner', + internalKey: 'ik-1', + bidfloor: 0.1, + ...overrides.params, + }, + mediaTypes: overrides.mediaTypes, + sizes: overrides.sizes, + ...overrides, + }); + + describe('isBidRequestValid', function () { + it('should return true when bidId, params.publisherId and params.trafficType are present', function () { + const bid = makeBid({ + bidId: 'bid-123', + params: { publisherId: 'pub-123', trafficType: 'banner' } + }); + + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return false when bidId is missing', function () { + const bid = makeBid({ bidId: undefined }); + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when params.publisherId is missing', function () { + const bid = makeBid({ params: { trafficType: 'banner' } }); + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when params.trafficType is missing', function () { + const bid = makeBid({ params: { publisherId: 'pub-1' } }); + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + }); + + describe('buildRequests', function () { + it('should build a single request object', function () { + const bidderRequest = makeBidderRequest(); + const validBidRequests = [makeBid({ bidId: 'bid-1' })]; + + const req = spec.buildRequests(validBidRequests, bidderRequest); + + expect(req).to.be.an('object'); + + expect(req).to.have.property('data'); + }); + + it('should map ortb2 fields into request.data (cat, keywords, badv, wseat, bseat)', function () { + const bidderRequest = makeBidderRequest({ + cat: ['IAB1', 'IAB1-1'], + keywords: { key1: ['v1', 'v2'] }, + badv: ['bad.com'], + wseat: ['seat1'], + bseat: ['seat2'], + }); + + const validBidRequests = [makeBid({ bidId: 'bid-1' })]; + const req = spec.buildRequests(validBidRequests, bidderRequest); + + expect(req.data.cat).to.deep.equal(['IAB1', 'IAB1-1']); + expect(req.data.keywords).to.deep.equal({ key1: ['v1', 'v2'] }); + expect(req.data.badv).to.deep.equal(['bad.com']); + expect(req.data.wseat).to.deep.equal(['seat1']); + expect(req.data.bseat).to.deep.equal(['seat2']); + }); + + it('should not throw if ortb2 fields are missing', function () { + const bidderRequest = makeBidderRequest(); + const validBidRequests = [makeBid({ bidId: 'bid-1' })]; + + expect(() => spec.buildRequests(validBidRequests, bidderRequest)).to.not.throw(); + }); + }); + + describe('interpretResponse', function () { + it('should return [] when serverResponse/body is missing', function () { + expect(spec.interpretResponse(null, {})).to.deep.equal([]); + expect(spec.interpretResponse({}, {})).to.deep.equal([]); + expect(spec.interpretResponse({ body: null }, {})).to.deep.equal([]); + }); + + it('should interpret banner bid', function () { + const bidRequest = { + data: { + placements: [ + { bidId: 'resp-bid-1', adFormat: 'banner' } + ] + } + }; + + const serverResponse = { + body: { + seatbid: [{ + bid: [{ + id: 'resp-bid-1', + price: 1.23, + crid: 'cr-1', + dealid: 'deal-1', + h: 250, + w: 300, + adm: '
price=1.23
', + nurl: 'https://win.example.com?c=1.23', + adomain: ['test.com'] + }] + }] + } + }; + + const out = spec.interpretResponse(serverResponse, bidRequest); + expect(out).to.have.lengthOf(1); + + const b = out[0]; + expect(b.requestId).to.equal('resp-bid-1'); + expect(b.cpm).to.equal(1.23); + expect(b.mediaType).to.equal('banner'); + expect(b.width).to.equal(300); + expect(b.height).to.equal(250); + expect(b.ad).to.include('1.23'); + expect(b.meta.advertiserDomains[0]).to.equal('test.com'); + }); + + it('should interpret video bid as vastXml', function () { + const bidRequest = { + data: { + placements: [ + { bidId: 'resp-bid-2', adFormat: 'video' } + ] + } + }; + + const serverResponse = { + body: { + seatbid: [{ + bid: [{ + id: 'resp-bid-2', + price: 5, + crid: 'cr-v', + dealid: 'deal-v', + h: 360, + w: 640, + adm: '', + nurl: 'https://win.example.com?c=5', + adomain: ['test.com'] + }] + }] + } + }; + + const out = spec.interpretResponse(serverResponse, bidRequest); + expect(out).to.have.lengthOf(1); + + const b = out[0]; + expect(b.mediaType).to.equal('video'); + expect(b.vastXml).to.equal(''); + expect(b.ad).to.equal(undefined); + }); + + it('should interpret native bid into native.ortb', function () { + const bidRequest = { + data: { + placements: [ + { bidId: 'resp-bid-3', adFormat: 'native' } + ] + } + }; + + const nativeAdm = JSON.stringify({ + native: { + assets: [{ id: 1, title: { text: 'Hello' } }], + imptrackers: ['https://imp.example.com/1'], + link: { url: 'https://click.example.com' } + } + }); + + const serverResponse = { + body: { + seatbid: [{ + bid: [{ + id: 'resp-bid-3', + price: 2.5, + crid: 'cr-n', + dealid: 'deal-n', + h: 1, + w: 1, + adm: nativeAdm, + nurl: 'https://win.example.com?c=5', + adomain: ['test.com'] + }] + }] + } + }; + + const out = spec.interpretResponse(serverResponse, bidRequest); + expect(out).to.have.lengthOf(1); + + const b = out[0]; + expect(b.mediaType).to.equal('native'); + expect(b).to.have.property('native'); + expect(b.native).to.have.property('ortb'); + + expect(b.native.ortb.assets).to.deep.equal([{ id: 1, title: { text: 'Hello' } }]); + expect(b.native.ortb.imptrackers).to.deep.equal(['https://imp.example.com/1']); + expect(b.native.ortb.link).to.deep.equal({ url: 'https://click.example.com' }); + }); + + it('should handle multiple seatbids and multiple bids', function () { + const bidRequest = { + data: { + placements: [ + { bidId: 'b1', adFormat: 'banner' }, + { bidId: 'b2', adFormat: 'video' } + ] + } + }; + + const serverResponse = { + body: { + seatbid: [ + { bid: [{ id: 'b1', price: 1, crid: 'c1', dealid: 'd1', h: 250, w: 300, adm: '
', nurl: '', adomain: ['test.com'] }] }, + { bid: [{ id: 'b2', price: 2, crid: 'c2', dealid: 'd2', h: 360, w: 640, adm: '', nurl: '', adomain: ['test.com'] }] } + ] + } + }; + + const out = spec.interpretResponse(serverResponse, bidRequest); + expect(out).to.have.lengthOf(2); + expect(out.find(x => x.requestId === 'b1').mediaType).to.equal('banner'); + expect(out.find(x => x.requestId === 'b2').mediaType).to.equal('video'); + }); + }); +}); diff --git a/test/spec/modules/acuityadsBidAdapter_spec.js b/test/spec/modules/acuityadsBidAdapter_spec.js index e37b6913ced..f7979c969df 100644 --- a/test/spec/modules/acuityadsBidAdapter_spec.js +++ b/test/spec/modules/acuityadsBidAdapter_spec.js @@ -531,7 +531,7 @@ describe('AcuityAdsBidAdapter', function () { const syncData = spec.getUserSyncs({}, {}, { consentString: 'ALL', gdprApplies: true, - }, {}); + }, undefined); expect(syncData).to.be.an('array').which.is.not.empty; expect(syncData[0]).to.be.an('object') expect(syncData[0].type).to.be.a('string') @@ -540,9 +540,7 @@ describe('AcuityAdsBidAdapter', function () { expect(syncData[0].url).to.equal('https://cs.admanmedia.com/image?pbjs=1&gdpr=1&gdpr_consent=ALL&coppa=0') }); it('Should return array of objects with proper sync config , include CCPA', function() { - const syncData = spec.getUserSyncs({}, {}, {}, { - consentString: '1---' - }); + const syncData = spec.getUserSyncs({}, {}, {}, '1---'); expect(syncData).to.be.an('array').which.is.not.empty; expect(syncData[0]).to.be.an('object') expect(syncData[0].type).to.be.a('string') @@ -551,7 +549,7 @@ describe('AcuityAdsBidAdapter', function () { expect(syncData[0].url).to.equal('https://cs.admanmedia.com/image?pbjs=1&ccpa_consent=1---&coppa=0') }); it('Should return array of objects with proper sync config , include GPP', function() { - const syncData = spec.getUserSyncs({}, {}, {}, {}, { + const syncData = spec.getUserSyncs({}, {}, {}, undefined, { gppString: 'abc123', applicableSections: [8] }); diff --git a/test/spec/modules/adWMGAnalyticsAdapter_spec.js b/test/spec/modules/adWMGAnalyticsAdapter_spec.js index d766a0f8ba3..112225e8019 100644 --- a/test/spec/modules/adWMGAnalyticsAdapter_spec.js +++ b/test/spec/modules/adWMGAnalyticsAdapter_spec.js @@ -1,8 +1,8 @@ import adWMGAnalyticsAdapter from 'modules/adWMGAnalyticsAdapter.js'; import { expect } from 'chai'; import { server } from 'test/mocks/xhr.js'; -import {expectEvents} from '../../helpers/analytics.js'; -import {EVENTS} from 'src/constants.js'; +import { expectEvents } from '../../helpers/analytics.js'; +import { EVENTS } from 'src/constants.js'; const adapterManager = require('src/adapterManager').default; const events = require('src/events'); @@ -142,7 +142,7 @@ describe('adWMG Analytics', function () { }); expectEvents([ - [EVENTS.AUCTION_INIT, {timestamp, auctionId, timeout, adUnits}], + [EVENTS.AUCTION_INIT, { timestamp, auctionId, timeout, adUnits }], [EVENTS.BID_REQUESTED, {}], [EVENTS.BID_RESPONSE, bidResponse], [EVENTS.NO_BID, {}], diff --git a/test/spec/modules/adagioAnalyticsAdapter_spec.js b/test/spec/modules/adagioAnalyticsAdapter_spec.js index d77a0fb8bd6..c493ef960ec 100644 --- a/test/spec/modules/adagioAnalyticsAdapter_spec.js +++ b/test/spec/modules/adagioAnalyticsAdapter_spec.js @@ -222,7 +222,7 @@ const AUCTION_INIT_ANOTHER = { 'auctionId': AUCTION_ID, 'timestamp': 1519767010567, 'auctionStatus': 'inProgress', - 'adUnits': [ { + 'adUnits': [{ 'code': '/19968336/header-bid-tag-1', 'mediaTypes': { 'banner': { @@ -244,7 +244,7 @@ const AUCTION_INIT_ANOTHER = { } }, 'sizes': [[640, 480]], - 'bids': [ { + 'bids': [{ 'bidder': 'another', 'params': { 'publisherId': '1001' @@ -264,7 +264,7 @@ const AUCTION_INIT_ANOTHER = { 'params': { 'publisherId': '1001' }, - }, ], + },], 'transactionId': 'ca4af27a-6d02-4f90-949d-d5541fa12014' }, { 'code': '/19968336/footer-bid-tag-1', @@ -279,12 +279,12 @@ const AUCTION_INIT_ANOTHER = { } }, 'sizes': [[640, 480]], - 'bids': [ { + 'bids': [{ 'bidder': 'another', 'params': { 'publisherId': '1001' }, - } ], + }], 'transactionId': 'ca4af27a-6d02-4f90-949d-d5541fa12014', 'ortb2Imp': { 'ext': { @@ -295,7 +295,7 @@ const AUCTION_INIT_ANOTHER = { } } }, - } ], + }], 'adUnitCodes': ['/19968336/header-bid-tag-1', '/19968336/footer-bid-tag-1'], 'bidderRequests': [ { @@ -462,7 +462,7 @@ const AUCTION_INIT_ANOTHER = { 'bidderCode': 'adagio', 'auctionId': AUCTION_ID, 'bidderRequestId': '1be65d7958826a', - 'bids': [ { + 'bids': [{ 'bidder': 'adagio', 'params': { ...PARAMS_ADG @@ -514,7 +514,7 @@ const AUCTION_INIT_CACHE = { 'auctionId': AUCTION_ID_CACHE, 'timestamp': 1519767010567, 'auctionStatus': 'inProgress', - 'adUnits': [ { + 'adUnits': [{ 'code': '/19968336/header-bid-tag-1', 'mediaTypes': { 'banner': { @@ -531,7 +531,7 @@ const AUCTION_INIT_CACHE = { } }, 'sizes': [[640, 480]], - 'bids': [ { + 'bids': [{ 'bidder': 'another', 'params': { 'publisherId': '1001' @@ -541,7 +541,7 @@ const AUCTION_INIT_CACHE = { 'params': { ...PARAMS_ADG }, - }, ], + },], 'transactionId': 'ca4af27a-6d02-4f90-949d-d5541fa12014', 'ortb2Imp': { 'ext': { @@ -561,20 +561,20 @@ const AUCTION_INIT_CACHE = { } }, 'sizes': [[640, 480]], - 'bids': [ { + 'bids': [{ 'bidder': 'another', 'params': { 'publisherId': '1001' }, - } ], + }], 'transactionId': 'ca4af27a-6d02-4f90-949d-d5541fa12014', - } ], + }], 'adUnitCodes': ['/19968336/header-bid-tag-1', '/19968336/footer-bid-tag-1'], - 'bidderRequests': [ { + 'bidderRequests': [{ 'bidderCode': 'another', 'auctionId': AUCTION_ID_CACHE, 'bidderRequestId': '1be65d7958826a', - 'bids': [ { + 'bids': [{ 'bidder': 'another', 'params': { 'publisherId': '1001', @@ -638,7 +638,7 @@ const AUCTION_INIT_CACHE = { 'bidderCode': 'adagio', 'auctionId': AUCTION_ID_CACHE, 'bidderRequestId': '1be65d7958826a', - 'bids': [ { + 'bids': [{ 'bidder': 'adagio', 'params': { ...PARAMS_ADG diff --git a/test/spec/modules/adagioBidAdapter_spec.js b/test/spec/modules/adagioBidAdapter_spec.js index 45788fe14a6..e774b49da0d 100644 --- a/test/spec/modules/adagioBidAdapter_spec.js +++ b/test/spec/modules/adagioBidAdapter_spec.js @@ -12,7 +12,7 @@ import { config } from '../../../src/config.js'; import { executeRenderer } from '../../../src/Renderer.js'; import { expect } from 'chai'; import { userSync } from '../../../src/userSync.js'; -import {getGlobal} from '../../../src/prebidGlobal.js'; +import { getGlobal } from '../../../src/prebidGlobal.js'; const BidRequestBuilder = function BidRequestBuilder(options) { const defaults = { @@ -197,12 +197,14 @@ describe('Adagio bid adapter', () => { }); it('should use adUnit code for adUnitElementId and placement params', function() { - const bid01 = new BidRequestBuilder({ params: { - organizationId: '1000', - site: 'site-name', - useAdUnitCodeAsPlacement: true, - useAdUnitCodeAsAdUnitElementId: true - }}).build(); + const bid01 = new BidRequestBuilder({ + params: { + organizationId: '1000', + site: 'site-name', + useAdUnitCodeAsPlacement: true, + useAdUnitCodeAsAdUnitElementId: true + } + }).build(); expect(spec.isBidRequestValid(bid01)).to.equal(true); expect(bid01.params.adUnitElementId).to.equal('adunit-code'); @@ -210,20 +212,26 @@ describe('Adagio bid adapter', () => { }); it('should return false when a required param is missing', function() { - const bid01 = new BidRequestBuilder({ params: { - organizationId: '1000', - placement: 'PAVE_ATF' - }}).build(); + const bid01 = new BidRequestBuilder({ + params: { + organizationId: '1000', + placement: 'PAVE_ATF' + } + }).build(); - const bid02 = new BidRequestBuilder({ params: { - organizationId: '1000', - site: 'SITE-NAME' - }}).build(); + const bid02 = new BidRequestBuilder({ + params: { + organizationId: '1000', + site: 'SITE-NAME' + } + }).build(); - const bid03 = new BidRequestBuilder({ params: { - placement: 'PAVE_ATF', - site: 'SITE-NAME' - }}).build(); + const bid03 = new BidRequestBuilder({ + params: { + placement: 'PAVE_ATF', + site: 'SITE-NAME' + } + }).build(); sandbox.stub(config, 'getConfig').withArgs('adagio').returns({ siteId: '1000' @@ -423,7 +431,9 @@ describe('Adagio bid adapter', () => { dom_loading: '111111111', } } - }}} + } + } + } }, ortb2Imp: { ext: { @@ -896,8 +906,8 @@ describe('Adagio bid adapter', () => { const requests = spec.buildRequests([bid01], bidderRequest); expect(requests[0].data.adUnits[0].mediaTypes.banner.sizes.length).to.equal(2); - expect(requests[0].data.adUnits[0].mediaTypes.banner.bannerSizes[0]).to.deep.equal({size: [300, 250], floor: 1}); - expect(requests[0].data.adUnits[0].mediaTypes.banner.bannerSizes[1]).to.deep.equal({size: [300, 600], floor: 1}); + expect(requests[0].data.adUnits[0].mediaTypes.banner.bannerSizes[0]).to.deep.equal({ size: [300, 250], floor: 1 }); + expect(requests[0].data.adUnits[0].mediaTypes.banner.bannerSizes[1]).to.deep.equal({ size: [300, 600], floor: 1 }); expect(requests[0].data.adUnits[0].mediaTypes.video.floor).to.equal(1); }); @@ -1060,12 +1070,12 @@ describe('Adagio bid adapter', () => { model: 'iPhone 12 Pro Max', os: 'iOS', osv: '17.4', - ext: {fiftyonedegrees_deviceId: '17595-133085-133468-18092'}, + ext: { fiftyonedegrees_deviceId: '17595-133085-133468-18092' }, }, }; const bid01 = new BidRequestBuilder().withParams().build(); - const bidderRequest = new BidderRequestBuilder({ortb2}).build(); + const bidderRequest = new BidderRequestBuilder({ ortb2 }).build(); const requests = spec.buildRequests([bid01], bidderRequest); const expectedData = { @@ -1361,7 +1371,7 @@ describe('Adagio bid adapter', () => { }); it('should logError if correct renderer is not defined', function() { - window.bluebillywig = { renderers: [ { _id: 'adagio-another_renderer' } ] }; + window.bluebillywig = { renderers: [{ _id: 'adagio-another_renderer' }] }; utilsMock.expects('logError').withExactArgs('Adagio: couldn\'t find a renderer with ID adagio-renderer').once(); diff --git a/test/spec/modules/adagioRtdProvider_spec.js b/test/spec/modules/adagioRtdProvider_spec.js index 3ac2d1fa73f..800590e8826 100644 --- a/test/spec/modules/adagioRtdProvider_spec.js +++ b/test/spec/modules/adagioRtdProvider_spec.js @@ -127,7 +127,7 @@ describe('Adagio Rtd Provider', function () { }; it('store new session data for further usage', function () { - const storageValue = JSON.stringify({abTest: {}}); + const storageValue = JSON.stringify({ abTest: {} }); sandbox.stub(storage, 'getDataFromLocalStorage').callsArgWith(1, storageValue); sandbox.stub(Date, 'now').returns(1714116520710); sandbox.stub(Math, 'random').returns(0.8); @@ -155,7 +155,7 @@ describe('Adagio Rtd Provider', function () { }); it('store existing session data for further usage', function () { - const storageValue = JSON.stringify({session: session, abTest: {}}); + const storageValue = JSON.stringify({ session: session, abTest: {} }); sandbox.stub(storage, 'getDataFromLocalStorage').callsArgWith(1, storageValue); sandbox.stub(Date, 'now').returns(1714116520710); sandbox.stub(Math, 'random').returns(0.8); @@ -179,7 +179,7 @@ describe('Adagio Rtd Provider', function () { }); it('store new session if old session has expired data for further usage', function () { - const storageValue = JSON.stringify({session: session, abTest: {}}); + const storageValue = JSON.stringify({ session: session, abTest: {} }); sandbox.stub(Date, 'now').returns(1715679344351); sandbox.stub(storage, 'getDataFromLocalStorage').callsArgWith(1, storageValue); sandbox.stub(Math, 'random').returns(0.8); @@ -422,8 +422,8 @@ describe('Adagio Rtd Provider', function () { ext: { geom() { return { - win: {t: 23, r: 1920, b: 1200, l: 0, w: 1920, h: 1177}, - self: {t: 210, r: 1159, b: 460, l: 859, w: 300, h: 250}, + win: { t: 23, r: 1920, b: 1200, l: 0, w: 1920, h: 1177 }, + self: { t: 210, r: 1159, b: 460, l: 859, w: 300, h: 250 }, } } } @@ -705,7 +705,8 @@ describe('Adagio Rtd Provider', function () { mediaTypes, params, auctionId, - bidderRequestsCount } = bidderRequestCopy.bids[0]; + bidderRequestsCount + } = bidderRequestCopy.bids[0]; const expected = { bidder, diff --git a/test/spec/modules/adbroBidAdapter_spec.js b/test/spec/modules/adbroBidAdapter_spec.js index 56fca4dbec1..f22b161e8c0 100644 --- a/test/spec/modules/adbroBidAdapter_spec.js +++ b/test/spec/modules/adbroBidAdapter_spec.js @@ -20,7 +20,7 @@ describe('adbroBidAdapter', function () { } }; - const validBid = makeBid({placementId: '1234'}, [[300, 250]]); + const validBid = makeBid({ placementId: '1234' }, [[300, 250]]); const invalidBid = makeBid({}, [[300, 250]]); const bidderRequest = { @@ -47,13 +47,13 @@ describe('adbroBidAdapter', function () { expect(spec.isBidRequestValid(makeBid({}, [[300, 250]]))).to.be.false; }); it('Should return false if banner sizes are not presented', function () { - expect(spec.isBidRequestValid(makeBid({placementId: '1234'}))).to.be.false; - expect(spec.isBidRequestValid(makeBid({placementId: '1234'}, []))).to.be.false; + expect(spec.isBidRequestValid(makeBid({ placementId: '1234' }))).to.be.false; + expect(spec.isBidRequestValid(makeBid({ placementId: '1234' }, []))).to.be.false; }); it('Should return true if placementId is an integer', function () { - expect(spec.isBidRequestValid(makeBid({placementId: '1234'}, [[300, 250]]))).to.be.true; - expect(spec.isBidRequestValid(makeBid({placementId: '1234'}, [[300, 250]]))).to.be.true; - expect(spec.isBidRequestValid(makeBid({placementId: 'abc'}, [[300, 250]]))).to.be.false; + expect(spec.isBidRequestValid(makeBid({ placementId: '1234' }, [[300, 250]]))).to.be.true; + expect(spec.isBidRequestValid(makeBid({ placementId: '1234' }, [[300, 250]]))).to.be.true; + expect(spec.isBidRequestValid(makeBid({ placementId: 'abc' }, [[300, 250]]))).to.be.false; }); }); @@ -121,11 +121,13 @@ describe('adbroBidAdapter', function () { h: 250, }; const bidRequest = spec.buildRequests([validBid], bidderRequest)[0]; - const bidResponse = {body: { - id: bidRequest.data.id, - bidid: utils.getUniqueIdentifierStr(), - seatbid: [{bid: [responseBid]}], - }}; + const bidResponse = { + body: { + id: bidRequest.data.id, + bidid: utils.getUniqueIdentifierStr(), + seatbid: [{ bid: [responseBid] }], + } + }; const responses = spec.interpretResponse(bidResponse, bidRequest); expect(responses).to.be.an('array').that.is.not.empty; const response = responses[0]; @@ -147,29 +149,33 @@ describe('adbroBidAdapter', function () { it('Should return an empty array if invalid banner response is passed', function () { const bidRequest = spec.buildRequests([validBid], bidderRequest)[0]; - const bidResponse = {body: { - id: bidRequest.data.id, - bidid: utils.getUniqueIdentifierStr(), - seatbid: [{ - bid: [{ - id: utils.getUniqueIdentifierStr(), - impid: invalidBid.bidId, - price: 0.4, - w: 300, - h: 250, + const bidResponse = { + body: { + id: bidRequest.data.id, + bidid: utils.getUniqueIdentifierStr(), + seatbid: [{ + bid: [{ + id: utils.getUniqueIdentifierStr(), + impid: invalidBid.bidId, + price: 0.4, + w: 300, + h: 250, + }], }], - }], - }}; + } + }; const responses = spec.interpretResponse(bidResponse, bidRequest); expect(responses).to.be.an('array').that.is.empty; }); it('Should return an empty array if no seat bids are passed', function () { const bidRequest = spec.buildRequests([validBid], bidderRequest)[0]; - const bidResponse = {body: { - id: bidRequest.data.id, - bidid: utils.getUniqueIdentifierStr(), - }}; + const bidResponse = { + body: { + id: bidRequest.data.id, + bidid: utils.getUniqueIdentifierStr(), + } + }; const responses = spec.interpretResponse(bidResponse, bidRequest); expect(responses).to.be.an('array').that.is.empty; }); @@ -190,7 +196,7 @@ describe('adbroBidAdapter', function () { }); it('Should trigger billing URL pixel', function () { - spec.onBidBillable({burl: pixel}); + spec.onBidBillable({ burl: pixel }); sinon.assert.calledOnce(triggerPixelStub); sinon.assert.calledWith(triggerPixelStub, pixel); }); diff --git a/test/spec/modules/adclusterBidAdapter_spec.js b/test/spec/modules/adclusterBidAdapter_spec.js new file mode 100644 index 00000000000..d068a0b5944 --- /dev/null +++ b/test/spec/modules/adclusterBidAdapter_spec.js @@ -0,0 +1,415 @@ +// test/spec/modules/adclusterBidAdapter_spec.js + +import { expect } from "chai"; +import { spec } from "modules/adclusterBidAdapter.js"; // adjust path if needed +import { BANNER, VIDEO } from "src/mediaTypes.js"; + +const BIDDER_CODE = "adcluster"; +const ENDPOINT = "https://core.adcluster.com.tr/bid"; + +describe("adclusterBidAdapter", function () { + // ---------- Test Fixtures (immutable) ---------- + const baseBid = Object.freeze({ + bidder: BIDDER_CODE, + bidId: "2f5d", + bidderRequestId: "breq-1", + auctionId: "auc-1", + transactionId: "txn-1", + adUnitCode: "div-1", + adUnitId: "adunit-1", + params: { unitId: "61884b5c-9420-4f15-871f-2dcc2fa1cff5" }, + }); + + const gdprConsent = Object.freeze({ + gdprApplies: true, + consentString: "BOJ/P2HOJ/P2HABABMAAAAAZ+A==", + }); + + const uspConsent = "1---"; + const gpp = "DBABLA.."; + const gppSid = [7, 8]; + + const bidderRequestBase = Object.freeze({ + auctionId: "auc-1", + bidderCode: BIDDER_CODE, + bidderRequestId: "breq-1", + auctionStart: 1111111111111, + timeout: 2000, + start: 1111111111112, + ortb2: { regs: { gpp, gpp_sid: gppSid } }, + gdprConsent, + uspConsent, + }); + + // helpers return fresh objects to avoid cross-test mutation + function mkBidBanner(extra = {}) { + return { + ...baseBid, + mediaTypes: { + banner: { + sizes: [ + [300, 250], + [300, 600], + ], + }, + }, + getFloor: ({ mediaType }) => { + if (mediaType === "banner") return { currency: "USD", floor: 0.5 }; + return { currency: "USD", floor: 0.0 }; + }, + userIdAsEids: [ + { source: "example.com", uids: [{ id: "abc", atype: 1 }] }, + ], + ortb2: { + source: { ext: { schain: { ver: "1.0", complete: 1, nodes: [] } } }, + }, + ...extra, + }; + } + + function mkBidVideo(extra = {}) { + return { + ...baseBid, + mediaTypes: { + video: { + context: "instream", + playerSize: [640, 360], + minduration: 5, + maxduration: 30, + }, + }, + getFloor: ({ mediaType, size }) => { + if (mediaType === "video" && Array.isArray(size)) { + return { currency: "USD", floor: 1.2 }; + } + return { currency: "USD", floor: 0.0 }; + }, + userIdAsEids: [ + { source: "example.com", uids: [{ id: "xyz", atype: 1 }] }, + ], + ortb2: { + source: { ext: { schain: { ver: "1.0", complete: 1, nodes: [] } } }, + }, + ...extra, + }; + } + + describe("isBidRequestValid", function () { + it("returns true when params.unitId is present", function () { + // Arrange + const bid = { ...baseBid }; + + // Act + const valid = spec.isBidRequestValid(bid); + + // Assert + expect(valid).to.equal(true); + }); + + it("returns false when params.unitId is missing", function () { + // Arrange + const bid = { ...baseBid, params: {} }; + + // Act + const valid = spec.isBidRequestValid(bid); + + // Assert + expect(valid).to.equal(false); + }); + + it("returns false when params is undefined", function () { + // Arrange + const bid = { ...baseBid, params: undefined }; + + // Act + const valid = spec.isBidRequestValid(bid); + + // Assert + expect(valid).to.equal(false); + }); + }); + + describe("buildRequests", function () { + it("builds a POST request with JSON body to the right endpoint", function () { + // Arrange + const br = { ...bidderRequestBase }; + const bids = [mkBidBanner(), mkBidVideo()]; + + // Act + const req = spec.buildRequests(bids, br); + + // Assert + expect(req.method).to.equal("POST"); + expect(req.url).to.equal(ENDPOINT); + expect(req.options).to.deep.equal({ contentType: "text/plain" }); + expect(req.data).to.be.an("object"); + expect(req.data.bidderCode).to.equal(BIDDER_CODE); + expect(req.data.auctionId).to.equal(br.auctionId); + expect(req.data.bids).to.be.an("array").with.length(2); + }); + + it("includes privacy signals (GDPR, USP, GPP) when present", function () { + // Arrange + const br = { ...bidderRequestBase }; + const bids = [mkBidBanner()]; + + // Act + const req = spec.buildRequests(bids, br); + + // Assert + const { regs, user } = req.data; + expect(regs).to.be.an("object"); + expect(regs.ext.gdpr).to.equal(1); + expect(user.ext.consent).to.equal(gdprConsent.consentString); + expect(regs.ext.us_privacy).to.equal(uspConsent); + expect(regs.ext.gpp).to.equal(gpp); + expect(regs.ext.gppSid).to.deep.equal(gppSid); + }); + + it("omits privacy fields when not provided", function () { + // Arrange + const minimalBR = { + auctionId: "auc-2", + bidderCode: BIDDER_CODE, + bidderRequestId: "breq-2", + auctionStart: 1, + timeout: 1000, + start: 2, + }; + const bids = [mkBidBanner()]; + + // Act + const req = spec.buildRequests(bids, minimalBR); + + // Assert + // regs.ext should exist but contain no privacy flags + expect(req.data.regs).to.be.an("object"); + expect(req.data.regs.ext).to.deep.equal({}); + // user.ext.consent must be undefined when no GDPR + expect(req.data.user).to.be.an("object"); + expect(req.data.user.ext).to.be.an("object"); + expect(req.data.user.ext.consent).to.be.undefined; + // allow eids to be present (they come from bids) + // don't assert deep-equality on user.ext, just ensure no privacy fields + expect(req.data.user.ext.gdpr).to.be.undefined; + }); + + it("passes userIdAsEids and schain when provided", function () { + // Arrange + const br = { ...bidderRequestBase }; + const bids = [mkBidBanner()]; + + // Act + const req = spec.buildRequests(bids, br); + + // Assert + expect(req.data.user.ext.eids).to.be.an("array").with.length(1); + expect(req.data.source.ext.schain).to.be.an("object"); + }); + + it("sets banner dimensions from first size and includes floors ext", function () { + // Arrange + const br = { ...bidderRequestBase }; + const bids = [mkBidBanner()]; + + // Act + const req = spec.buildRequests(bids, br); + + // Assert + const imp = req.data.bids[0]; + expect(imp.width).to.equal(300); + expect(imp.height).to.equal(250); + expect(imp.ext).to.have.property("floors"); + expect(imp.ext.floors.banner).to.equal(0.5); + }); + + it("sets video sizes from playerSize and includes video floors", function () { + // Arrange + const br = { ...bidderRequestBase }; + const bids = [mkBidVideo()]; + + // Act + const req = spec.buildRequests(bids, br); + + // Assert + const imp = req.data.bids[0]; + expect(imp.width).to.equal(640); + expect(imp.height).to.equal(360); + expect(imp.video).to.be.an("object"); + expect(imp.video.minduration).to.equal(5); + expect(imp.video.maxduration).to.equal(30); + expect(imp.video.ext.context).to.equal("instream"); + expect(imp.video.ext.floor).to.equal(1.2); + expect(imp.ext.floors.video).to.equal(1.2); + }); + + it("gracefully handles missing getFloor", function () { + // Arrange + const br = { ...bidderRequestBase }; + const bids = [mkBidBanner({ getFloor: undefined })]; + + // Act + const req = spec.buildRequests(bids, br); + + // Assert + expect(req.data.bids[0].ext.floors.banner).to.equal(null); + }); + + it("passes previewMediaId when provided", function () { + // Arrange + const br = { ...bidderRequestBase }; + const bids = [ + mkBidVideo({ params: { unitId: "x", previewMediaId: "media-123" } }), + ]; + + // Act + const req = spec.buildRequests(bids, br); + + // Assert + expect(req.data.bids[0].params.previewMediaId).to.equal("media-123"); + }); + }); + + describe("interpretResponse", function () { + it("returns empty array when body is missing or not an array", function () { + // Arrange + const missing = { body: null }; + const notArray = { body: {} }; + + // Act + const out1 = spec.interpretResponse(missing); + const out2 = spec.interpretResponse(notArray); + + // Assert + expect(out1).to.deep.equal([]); + expect(out2).to.deep.equal([]); + }); + + it("maps banner responses to Prebid bids", function () { + // Arrange + const serverBody = [ + { + requestId: "2f5d", + cpm: 1.23, + currency: "USD", + width: 300, + height: 250, + creativeId: "cr-1", + ttl: 300, + netRevenue: true, + mediaType: "banner", + ad: "
creative
", + meta: { advertiserDomains: ["advertiser.com"] }, + }, + ]; + + // Act + const out = spec.interpretResponse({ body: serverBody }); + + // Assert + expect(out).to.have.length(1); + const b = out[0]; + expect(b.requestId).to.equal("2f5d"); + expect(b.cpm).to.equal(1.23); + expect(b.mediaType).to.equal(BANNER); + expect(b.ad).to.be.a("string"); + expect(b.meta.advertiserDomains).to.deep.equal(["advertiser.com"]); + }); + + it("maps video responses to Prebid bids (vastUrl)", function () { + // Arrange + const serverBody = [ + { + requestId: "vid-1", + cpm: 2.5, + currency: "USD", + width: 640, + height: 360, + creativeId: "cr-v", + ttl: 300, + netRevenue: true, + mediaType: "video", + ad: "https://vast.tag/url.xml", + meta: { advertiserDomains: ["brand.com"] }, // mediaType hint optional + }, + ]; + + // Act + const out = spec.interpretResponse({ body: serverBody }); + + // Assert + expect(out).to.have.length(1); + const b = out[0]; + expect(b.requestId).to.equal("vid-1"); + expect(b.mediaType).to.equal(VIDEO); + expect(b.vastUrl).to.equal("https://vast.tag/url.xml"); + expect(b.ad).to.be.undefined; + }); + + it("handles missing meta.advertiserDomains safely", function () { + // Arrange + const serverBody = [ + { + requestId: "2f5d", + cpm: 0.2, + currency: "USD", + width: 300, + height: 250, + creativeId: "cr-2", + ttl: 120, + netRevenue: true, + ad: "
", + meta: {}, + }, + ]; + + // Act + const out = spec.interpretResponse({ body: serverBody }); + + // Assert + expect(out[0].meta.advertiserDomains).to.deep.equal([]); + }); + + it("supports multiple mixed responses", function () { + // Arrange + const serverBody = [ + { + requestId: "b-1", + cpm: 0.8, + currency: "USD", + width: 300, + height: 250, + creativeId: "cr-b", + ttl: 300, + netRevenue: true, + ad: "
banner
", + mediaType: "banner", + meta: { advertiserDomains: [] }, + }, + { + requestId: "v-1", + cpm: 3.1, + currency: "USD", + width: 640, + height: 360, + creativeId: "cr-v", + ttl: 300, + netRevenue: true, + mediaType: "video", + ad: "https://vast.example/vast.xml", + meta: { advertiserDomains: ["x.com"] }, + }, + ]; + + // Act + const out = spec.interpretResponse({ body: serverBody }); + + // Assert + expect(out).to.have.length(2); + const [b, v] = out; + expect(b.mediaType).to.equal(BANNER); + expect(v.mediaType).to.equal(VIDEO); + expect(v.vastUrl).to.match(/^https:\/\/vast\.example/); + }); + }); +}); diff --git a/test/spec/modules/addefendBidAdapter_spec.js b/test/spec/modules/addefendBidAdapter_spec.js index 7faa30c69be..f4449b4a9cf 100644 --- a/test/spec/modules/addefendBidAdapter_spec.js +++ b/test/spec/modules/addefendBidAdapter_spec.js @@ -1,6 +1,6 @@ -import {expect} from 'chai'; -import {spec} from 'modules/addefendBidAdapter.js'; -import {getGlobal} from '../../../src/prebidGlobal.js'; +import { expect } from 'chai'; +import { spec } from 'modules/addefendBidAdapter.js'; +import { getGlobal } from '../../../src/prebidGlobal.js'; describe('addefendBidAdapter', () => { const defaultBidRequest = { @@ -84,12 +84,12 @@ describe('addefendBidAdapter', () => { it('sends correct bid parameters', () => { const bidRequest = deepClone(defaultBidRequest); - expect(request.data.bids).to.deep.equal([ { + expect(request.data.bids).to.deep.equal([{ bidId: bidRequest.bidId, placementId: bidRequest.params.placementId, - sizes: [ '300x250', '300x600' ], + sizes: ['300x250', '300x600'], transactionId: 'd58851660c0c4461e4aa06344fc9c0c6' - } ]); + }]); }); it('handles empty gdpr object', () => { @@ -154,7 +154,7 @@ describe('addefendBidAdapter', () => { } ]; - const result = spec.interpretResponse({body: serverResponse}); + const result = spec.interpretResponse({ body: serverResponse }); expect(result.length).to.equal(expectedResponse.length); Object.keys(expectedResponse[0]).forEach((key) => { expect(result[0][key]).to.deep.equal(expectedResponse[0][key]); @@ -170,14 +170,14 @@ describe('addefendBidAdapter', () => { 'ttl': 60 } ]; - const result = spec.interpretResponse({body: serverResponse}); + const result = spec.interpretResponse({ body: serverResponse }); expect(result.length).to.equal(0); }); it('handles nobid responses', () => { const serverResponse = []; - const result = spec.interpretResponse({body: serverResponse}); + const result = spec.interpretResponse({ body: serverResponse }); expect(result.length).to.equal(0); }); diff --git a/test/spec/modules/adfBidAdapter_spec.js b/test/spec/modules/adfBidAdapter_spec.js index bc15ab40c7d..2743c2fb3cc 100644 --- a/test/spec/modules/adfBidAdapter_spec.js +++ b/test/spec/modules/adfBidAdapter_spec.js @@ -129,7 +129,7 @@ describe('Adf adapter', function () { }); it('should transfer DSA info', function () { - const validBidRequests = [ { bidId: 'bidId', params: { siteId: 'siteId' } } ]; + const validBidRequests = [{ bidId: 'bidId', params: { siteId: 'siteId' } }]; const request = JSON.parse( spec.buildRequests(validBidRequests, { @@ -201,8 +201,8 @@ describe('Adf adapter', function () { params: { siteId: 'siteId' }, }]; const request = JSON.parse(spec.buildRequests(validBidRequests, { - refererInfo: {page: 'page'}, - ortb2: {source: {tid: 'tid'}} + refererInfo: { page: 'page' }, + ortb2: { source: { tid: 'tid' } } }).data); assert.equal(request.source.tid, 'tid'); @@ -278,8 +278,8 @@ describe('Adf adapter', function () { bidId: 'bidId', params: {}, userIdAsEids: [ - { source: 'adserver.org', uids: [ { id: 'TTD_ID_FROM_USER_ID_MODULE', atype: 1, ext: { rtiPartner: 'TDID' } } ] }, - { source: 'pubcid.org', uids: [ { id: 'pubCommonId_FROM_USER_ID_MODULE', atype: 1 } ] } + { source: 'adserver.org', uids: [{ id: 'TTD_ID_FROM_USER_ID_MODULE', atype: 1, ext: { rtiPartner: 'TDID' } }] }, + { source: 'pubcid.org', uids: [{ id: 'pubCommonId_FROM_USER_ID_MODULE', atype: 1 }] } ] }]; @@ -294,7 +294,7 @@ describe('Adf adapter', function () { setCurrencyConfig({ adServerCurrency: 'EUR' }) return addFPDToBidderRequest(bidderRequest).then(res => { const request = JSON.parse(spec.buildRequests(validBidRequests, res).data); - assert.deepEqual(request.cur, [ 'EUR' ]); + assert.deepEqual(request.cur, ['EUR']); setCurrencyConfig({}); }); }); @@ -364,15 +364,15 @@ describe('Adf adapter', function () { const validBidRequests = [{ bidId: 'bidId', params: { mid: '1000' }, - mediaTypes: {video: {}} + mediaTypes: { video: {} } }, { bidId: 'bidId2', params: { mid: '1000' }, - mediaTypes: {video: {}} + mediaTypes: { video: {} } }, { bidId: 'bidId3', params: { mid: '1000' }, - mediaTypes: {video: {}} + mediaTypes: { video: {} } }]; const imps = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } }).data).imp; @@ -382,9 +382,9 @@ describe('Adf adapter', function () { }); it('should add mid', function () { - const validBidRequests = [{ bidId: 'bidId', params: {mid: 1000}, mediaTypes: {video: {}} }, - { bidId: 'bidId2', params: {mid: 1001}, mediaTypes: {video: {}} }, - { bidId: 'bidId3', params: {mid: 1002}, mediaTypes: {video: {}} }]; + const validBidRequests = [{ bidId: 'bidId', params: { mid: 1000 }, mediaTypes: { video: {} } }, + { bidId: 'bidId2', params: { mid: 1001 }, mediaTypes: { video: {} } }, + { bidId: 'bidId3', params: { mid: 1002 }, mediaTypes: { video: {} } }]; const imps = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } }).data).imp; for (let i = 0; i < 3; i++) { assert.equal(imps[i].tagid, validBidRequests[i].params.mid); @@ -411,11 +411,11 @@ describe('Adf adapter', function () { describe('dynamic placement tag', function () { it('should add imp parameters correctly', function () { const validBidRequests = [ - { bidId: 'bidId', params: { inv: 1000, mname: 'placement' }, mediaTypes: {video: {}} }, - { bidId: 'bidId', params: { mid: 1234, inv: 1002, mname: 'placement2' }, mediaTypes: {video: {}} }, - { bidId: 'bidId', params: { mid: 1234 }, mediaTypes: {video: {}} } + { bidId: 'bidId', params: { inv: 1000, mname: 'placement' }, mediaTypes: { video: {} } }, + { bidId: 'bidId', params: { mid: 1234, inv: 1002, mname: 'placement2' }, mediaTypes: { video: {} } }, + { bidId: 'bidId', params: { mid: 1234 }, mediaTypes: { video: {} } } ]; - const [ imp1, imp2, imp3 ] = getRequestImps(validBidRequests); + const [imp1, imp2, imp3] = getRequestImps(validBidRequests); assert.equal(imp1.ext.bidder.inv, 1000); assert.equal(imp1.ext.bidder.mname, 'placement'); @@ -434,7 +434,7 @@ describe('Adf adapter', function () { describe('price floors', function () { it('should not add if floors module not configured', function () { - const validBidRequests = [{ bidId: 'bidId', params: {mid: 1000}, mediaTypes: {video: {}} }]; + const validBidRequests = [{ bidId: 'bidId', params: { mid: 1000 }, mediaTypes: { video: {} } }]; const imp = getRequestImps(validBidRequests)[0]; assert.equal(imp.bidfloor, undefined); @@ -442,7 +442,7 @@ describe('Adf adapter', function () { }); it('should not add if floor price not defined', function () { - const validBidRequests = [ getBidWithFloor() ]; + const validBidRequests = [getBidWithFloor()]; const imp = getRequestImps(validBidRequests)[0]; assert.equal(imp.bidfloor, undefined); @@ -451,7 +451,7 @@ describe('Adf adapter', function () { it('should request floor price in adserver currency', function () { setCurrencyConfig({ adServerCurrency: 'DKK' }) - const validBidRequests = [ getBidWithFloor() ]; + const validBidRequests = [getBidWithFloor()]; return addFPDToBidderRequest(validBidRequests[0]).then(res => { const imp = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' }, ...res }).data).imp[0]; assert.equal(imp.bidfloor, undefined); @@ -461,7 +461,7 @@ describe('Adf adapter', function () { }); it('should add correct floor values', function () { - const expectedFloors = [ 1, 1.3, 0.5 ]; + const expectedFloors = [1, 1.3, 0.5]; const validBidRequests = expectedFloors.map(getBidWithFloor); const imps = getRequestImps(validBidRequests); @@ -473,18 +473,22 @@ describe('Adf adapter', function () { it('should add correct params to getFloor', function () { let result; - let mediaTypes = { video: { - playerSize: [ 100, 200 ] - } }; - const expectedFloors = [ 1, 1.3, 0.5 ]; + let mediaTypes = { + video: { + playerSize: [100, 200] + } + }; + const expectedFloors = [1, 1.3, 0.5]; setCurrencyConfig({ adServerCurrency: 'DKK' }); const validBidRequests = expectedFloors.map(getBidWithFloorTest); return addFPDToBidderRequest(validBidRequests[0]).then(res => { getRequestImps(validBidRequests, res); assert.deepEqual(result, { currency: 'DKK', size: '*', mediaType: '*' }) - mediaTypes = { banner: { - sizes: [ [100, 200], [300, 400] ] - }}; + mediaTypes = { + banner: { + sizes: [[100, 200], [300, 400]] + } + }; getRequestImps(validBidRequests, res); assert.deepEqual(result, { currency: 'DKK', size: '*', mediaType: '*' }); @@ -573,7 +577,7 @@ describe('Adf adapter', function () { video: {} } }]; - const [ first, second, third ] = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } }).data).imp; + const [first, second, third] = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } }).data).imp; assert.ok(first.banner); assert.ok(first.video); @@ -602,7 +606,7 @@ describe('Adf adapter', function () { }]; const { banner } = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } }).data).imp[0]; assert.deepEqual(banner, { - format: [ { w: 100, h: 100 }, { w: 200, h: 300 } ] + format: [{ w: 100, h: 100 }, { w: 200, h: 300 }] }); }); }); @@ -709,7 +713,7 @@ describe('Adf adapter', function () { hmin: 627, w: 325, h: 300, - mimes: [ 'image/jpg', 'image/gif' ] + mimes: ['image/jpg', 'image/gif'] } }, { @@ -900,9 +904,9 @@ describe('Adf adapter', function () { const serverResponse = { body: { seatbid: [{ - bid: [{impid: '1', native: {ver: '1.1', link: { url: 'link' }, assets: [{id: 0, title: {text: 'Asset title text'}}]}}] + bid: [{ impid: '1', native: { ver: '1.1', link: { url: 'link' }, assets: [{ id: 0, title: { text: 'Asset title text' } }] } }] }, { - bid: [{impid: '2', native: {ver: '1.1', link: { url: 'link' }, assets: [{id: 1, data: {value: 'Asset title text'}}]}}] + bid: [{ impid: '2', native: { ver: '1.1', link: { url: 'link' }, assets: [{ id: 1, data: { value: 'Asset title text' } }] } }] }] } }; @@ -943,11 +947,11 @@ describe('Adf adapter', function () { body: { seatbid: [{ bid: [ - {impid: '1', native: {ver: '1.1', link: { url: 'link1' }, assets: [{id: 0, title: {text: 'Asset title text'}}]}}, - {impid: '4', native: {ver: '1.1', link: { url: 'link4' }, assets: [{id: 1, title: {text: 'Asset title text'}}]}} + { impid: '1', native: { ver: '1.1', link: { url: 'link1' }, assets: [{ id: 0, 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: 0, data: {value: 'Asset title text'}}]}}] + bid: [{ impid: '2', native: { ver: '1.1', link: { url: 'link2' }, assets: [{ id: 0, data: { value: 'Asset title text' } }] } }] }] } }; @@ -1003,11 +1007,11 @@ describe('Adf adapter', function () { bids = spec.interpretResponse(serverResponse, bidRequest).map(bid => { const { requestId, native: { ortb: { link: { url } } } } = bid; - return [ requestId, url ]; + return [requestId, url]; }); assert.equal(bids.length, 3); - assert.deepEqual(bids, [[ 'bidId1', 'link1' ], [ 'bidId2', 'link2' ], [ 'bidId4', 'link4' ]]); + assert.deepEqual(bids, [['bidId1', 'link1'], ['bidId2', 'link2'], ['bidId4', 'link4']]); }); it('should set correct values to bid', function () { @@ -1027,7 +1031,7 @@ describe('Adf adapter', function () { imptrackers: ['imptrackers url1', 'imptrackers url2'] }, dealid: 'deal-id', - adomain: [ 'demo.com' ], + adomain: ['demo.com'], ext: { prebid: { type: 'native', @@ -1042,7 +1046,7 @@ describe('Adf adapter', function () { adrender: 1 } }, - cat: [ 'IAB1', 'IAB2' ] + cat: ['IAB1', 'IAB2'] } ] }], @@ -1103,8 +1107,8 @@ describe('Adf adapter', function () { assert.deepEqual(bids[0].mediaType, 'native'); assert.deepEqual(bids[0].meta.mediaType, 'native'); assert.deepEqual(bids[0].meta.primaryCatId, 'IAB1'); - assert.deepEqual(bids[0].meta.secondaryCatIds, [ 'IAB2' ]); - assert.deepEqual(bids[0].meta.advertiserDomains, [ 'demo.com' ]); + assert.deepEqual(bids[0].meta.secondaryCatIds, ['IAB2']); + assert.deepEqual(bids[0].meta.advertiserDomains, ['demo.com']); assert.deepEqual(bids[0].meta.dsa, { behalf: 'some-behalf', paid: 'some-paid', @@ -1156,7 +1160,7 @@ describe('Adf adapter', function () { img: { url: 'test.url.com/Files/58345/308200.jpg?bv=1', w: 300, h: 300 } }], link: { - url: 'clickUrl', clicktrackers: [ 'clickTracker1', 'clickTracker2' ] + url: 'clickUrl', clicktrackers: ['clickTracker1', 'clickTracker2'] }, imptrackers: ['imptracker url1', 'imptracker url2'], jstracker: 'jstracker' @@ -1232,7 +1236,7 @@ describe('Adf adapter', function () { const native = bid[0].native; const assets = native.assets; - assert.deepEqual(result, {ortb: native}); + assert.deepEqual(result, { ortb: native }); }); it('should return empty when there is no bids in response', function () { const serverResponse = { diff --git a/test/spec/modules/adgenerationBidAdapter_spec.js b/test/spec/modules/adgenerationBidAdapter_spec.js index 8a542ba2966..73e872e9996 100644 --- a/test/spec/modules/adgenerationBidAdapter_spec.js +++ b/test/spec/modules/adgenerationBidAdapter_spec.js @@ -1,7 +1,7 @@ -import {expect} from 'chai'; -import {spec} from 'modules/adgenerationBidAdapter.js'; -import {newBidder} from 'src/adapters/bidderFactory.js'; -import {NATIVE} from 'src/mediaTypes.js'; +import { expect } from 'chai'; +import { spec } from 'modules/adgenerationBidAdapter.js'; +import { newBidder } from 'src/adapters/bidderFactory.js'; +import { NATIVE } from 'src/mediaTypes.js'; import prebid from 'package.json'; import { setConfig as setCurrencyConfig } from '../../../modules/currency.js'; import { addFPDToBidderRequest } from '../../helpers/fpd.js'; @@ -59,7 +59,7 @@ describe('AdgenerationAdapter', function () { ], mobile: 0 }; - const schainSmaple = {ver: '1.0', complete: 1, nodes: [{asi: 'indirectseller.com', sid: '00001', hp: 1}]}; + const schainSmaple = { ver: '1.0', complete: 1, nodes: [{ asi: 'indirectseller.com', sid: '00001', hp: 1 }] }; const bidRequests = [ { // banner bidder: 'adg', @@ -151,13 +151,13 @@ describe('AdgenerationAdapter', function () { uid: 'id5-id-test-1234567890' }, imuid: 'i.KrAH6ZAZTJOnH5S4N2sogA', - uid2: {id: 'AgAAAAVacu1uAxgAxH+HJ8+nWlS2H4uVqr6i+HBDCNREHD8WKsio/x7D8xXFuq1cJycUU86yXfTH9Xe/4C8KkH+7UCiU7uQxhyD7Qxnv251pEs6K8oK+BPLYR+8BLY/sJKesa/koKwx1FHgUzIBum582tSy2Oo+7C6wYUaaV4QcLr/4LPA='}, + uid2: { id: 'AgAAAAVacu1uAxgAxH+HJ8+nWlS2H4uVqr6i+HBDCNREHD8WKsio/x7D8xXFuq1cJycUU86yXfTH9Xe/4C8KkH+7UCiU7uQxhyD7Qxnv251pEs6K8oK+BPLYR+8BLY/sJKesa/koKwx1FHgUzIBum582tSy2Oo+7C6wYUaaV4QcLr/4LPA=' }, }, - ortb2Imp: {ext: {gpid: '/1111/homepage#300x250'}}, + ortb2Imp: { ext: { gpid: '/1111/homepage#300x250' } }, ortb2: { site: { domain: 'localhost:9999', - publisher: {'domain': 'localhost:9999'}, + publisher: { 'domain': 'localhost:9999' }, page: 'http://localhost:9999/integrationExamples/gpt/hello_world.html', ref: 'http://localhost:9999/integrationExamples/gpt/hello_world.html' }, @@ -301,7 +301,7 @@ describe('AdgenerationAdapter', function () { } } } - const request = spec.buildRequests(bidRequests, {...bidderRequest, ortb2: criteoParams})[0]; + const request = spec.buildRequests(bidRequests, { ...bidderRequest, ortb2: criteoParams })[0]; expect(request.data.ortb.user).to.deep.equal(criteoParams.user); }); @@ -354,7 +354,7 @@ describe('AdgenerationAdapter', function () { } }, } - const request = spec.buildRequests(bidRequests, {...bidderRequest, ortb2: idparams})[3]; + const request = spec.buildRequests(bidRequests, { ...bidderRequest, ortb2: idparams })[3]; expect(request.data.ortb.user).to.deep.equal(idparams.user); // gpid @@ -388,7 +388,7 @@ describe('AdgenerationAdapter', function () { const bidRequests = { banner: { bidderRequest: { - ortb2: {ext: {prebid: {adServerCurrency: 'JPY'}}} + ortb2: { ext: { prebid: { adServerCurrency: 'JPY' } } } }, method: 'POST', url: 'https://api-test.scaleout.jp/adgen/prebid?id=15415&posall=SSPLOC&sdktype=0', @@ -1004,7 +1004,7 @@ describe('AdgenerationAdapter', function () { { id: 1, required: 1, - title: {text: 'Title'} + title: { text: 'Title' } }, { id: 2, @@ -1025,17 +1025,17 @@ describe('AdgenerationAdapter', function () { required: 1 }, { - data: {value: 'Description'}, + data: { value: 'Description' }, id: 5, required: 0 }, { - data: {value: 'CTA'}, + data: { value: 'CTA' }, id: 6, required: 0 }, { - data: {value: 'Sponsored'}, + data: { value: 'Sponsored' }, id: 4, required: 0 } @@ -1180,7 +1180,7 @@ describe('AdgenerationAdapter', function () { }; it('no bid responses', function () { - const result = spec.interpretResponse({body: serverResponse.noAd}, bidRequests.banner); + const result = spec.interpretResponse({ body: serverResponse.noAd }, bidRequests.banner); expect(result.length).to.equal(0); }); @@ -1192,7 +1192,7 @@ describe('AdgenerationAdapter', function () { } }; return addFPDToBidderRequest(bidderRequest).then(res => { - const sr = {body: serverResponse.normal.upperBillboard}; + const sr = { body: serverResponse.normal.upperBillboard }; const br = { bidderRequest: res, ...bidRequests.upperBillboard }; const result = spec.interpretResponse(sr, br)[0]; @@ -1210,7 +1210,7 @@ describe('AdgenerationAdapter', function () { }); it('handles banner responses for empty adomain', function () { - const result = spec.interpretResponse({body: serverResponse.emptyAdomain.banner}, bidRequests.banner)[0]; + const result = spec.interpretResponse({ body: serverResponse.emptyAdomain.banner }, bidRequests.banner)[0]; expect(result.requestId).to.equal(bidResponses.normal.banner.requestId); expect(result.width).to.equal(bidResponses.normal.banner.width); expect(result.height).to.equal(bidResponses.normal.banner.height); @@ -1226,7 +1226,7 @@ describe('AdgenerationAdapter', function () { }); it('handles native responses for empty adomain', function () { - const result = spec.interpretResponse({body: serverResponse.emptyAdomain.native}, bidRequests.native)[0]; + const result = spec.interpretResponse({ body: serverResponse.emptyAdomain.native }, bidRequests.native)[0]; expect(result.requestId).to.equal(bidResponses.normal.native.requestId); expect(result.width).to.equal(bidResponses.normal.native.width); expect(result.height).to.equal(bidResponses.normal.native.height); @@ -1256,7 +1256,7 @@ describe('AdgenerationAdapter', function () { }); it('handles banner responses for no adomain', function () { - const result = spec.interpretResponse({body: serverResponse.noAdomain.banner}, bidRequests.banner)[0]; + const result = spec.interpretResponse({ body: serverResponse.noAdomain.banner }, bidRequests.banner)[0]; expect(result.requestId).to.equal(bidResponses.normal.banner.requestId); expect(result.width).to.equal(bidResponses.normal.banner.width); expect(result.height).to.equal(bidResponses.normal.banner.height); @@ -1272,7 +1272,7 @@ describe('AdgenerationAdapter', function () { }); it('handles native responses for no adomain', function () { - const result = spec.interpretResponse({body: serverResponse.noAdomain.native}, bidRequests.native)[0]; + const result = spec.interpretResponse({ body: serverResponse.noAdomain.native }, bidRequests.native)[0]; expect(result.requestId).to.equal(bidResponses.normal.native.requestId); expect(result.width).to.equal(bidResponses.normal.native.width); expect(result.height).to.equal(bidResponses.normal.native.height); diff --git a/test/spec/modules/adhashBidAdapter_spec.js b/test/spec/modules/adhashBidAdapter_spec.js index 75ff8b851f0..be48a3cace1 100644 --- a/test/spec/modules/adhashBidAdapter_spec.js +++ b/test/spec/modules/adhashBidAdapter_spec.js @@ -83,7 +83,7 @@ describe('adhashBidAdapter', function () { }; it('should build the request correctly', function () { const result = spec.buildRequests( - [ bidRequest ], + [bidRequest], { gdprConsent: { gdprApplies: true, consentString: 'example' }, refererInfo: { topmostLocation: 'https://example.com/path.html' } } ); expect(result.length).to.equal(1); @@ -101,7 +101,7 @@ describe('adhashBidAdapter', function () { expect(result[0].data).to.have.property('recentAds'); }); it('should build the request correctly without referer', function () { - const result = spec.buildRequests([ bidRequest ], { gdprConsent: { gdprApplies: true, consentString: 'example' } }); + const result = spec.buildRequests([bidRequest], { gdprConsent: { gdprApplies: true, consentString: 'example' } }); expect(result.length).to.equal(1); expect(result[0].method).to.equal('POST'); expect(result[0].url).to.equal('https://bidder.adhash.com/rtb?version=3.6&prebid=true&publisher=0xc3b09b27e9c6ef73957901aa729b9e69e5bbfbfb'); @@ -285,11 +285,11 @@ describe('adhashBidAdapter', function () { }); it('should return empty array when there are no creatives returned', function () { - expect(spec.interpretResponse({body: {creatives: []}}, request).length).to.equal(0); + expect(spec.interpretResponse({ body: { creatives: [] } }, request).length).to.equal(0); }); it('should return empty array when there is no creatives key in the response', function () { - expect(spec.interpretResponse({body: {}}, request).length).to.equal(0); + expect(spec.interpretResponse({ body: {} }, request).length).to.equal(0); }); it('should return empty array when something is not right', function () { diff --git a/test/spec/modules/adheseBidAdapter_spec.js b/test/spec/modules/adheseBidAdapter_spec.js index 4c16b774362..f608b9f0cb4 100644 --- a/test/spec/modules/adheseBidAdapter_spec.js +++ b/test/spec/modules/adheseBidAdapter_spec.js @@ -1,6 +1,6 @@ -import {expect} from 'chai'; -import {spec} from 'modules/adheseBidAdapter.js'; -import {config} from 'src/config.js'; +import { expect } from 'chai'; +import { spec } from 'modules/adheseBidAdapter.js'; +import { config } from 'src/config.js'; const BID_ID = 456; const TTL = 360; @@ -72,68 +72,68 @@ describe('AdheseAdapter', function () { }; it('should include requested slots', function () { - const req = spec.buildRequests([ minimalBid() ], bidderRequest); + const req = spec.buildRequests([minimalBid()], bidderRequest); expect(JSON.parse(req.data).slots[0].slotname).to.equal('_main_page_-leaderboard'); }); it('should include all extra bid params', function () { - const req = spec.buildRequests([ bidWithParams({ 'ag': '25' }) ], bidderRequest); + const req = spec.buildRequests([bidWithParams({ 'ag': '25' })], bidderRequest); - expect(JSON.parse(req.data).slots[0].parameters).to.deep.include({ 'ag': [ '25' ] }); + expect(JSON.parse(req.data).slots[0].parameters).to.deep.include({ 'ag': ['25'] }); }); it('should assign bid params per slot', function () { - const req = spec.buildRequests([ bidWithParams({ 'ag': '25' }), bidWithParams({ 'ag': '25', 'ci': 'gent' }) ], bidderRequest); + const req = spec.buildRequests([bidWithParams({ 'ag': '25' }), bidWithParams({ 'ag': '25', 'ci': 'gent' })], bidderRequest); - expect(JSON.parse(req.data).slots[0].parameters).to.deep.include({ 'ag': [ '25' ] }).and.not.to.deep.include({ 'ci': [ 'gent' ] }); - expect(JSON.parse(req.data).slots[1].parameters).to.deep.include({ 'ag': [ '25' ] }).and.to.deep.include({ 'ci': [ 'gent' ] }); + expect(JSON.parse(req.data).slots[0].parameters).to.deep.include({ 'ag': ['25'] }).and.not.to.deep.include({ 'ci': ['gent'] }); + expect(JSON.parse(req.data).slots[1].parameters).to.deep.include({ 'ag': ['25'] }).and.to.deep.include({ 'ci': ['gent'] }); }); it('should split multiple target values', function () { - const req = spec.buildRequests([ bidWithParams({ 'ci': 'london' }), bidWithParams({ 'ci': 'gent' }) ], bidderRequest); + const req = spec.buildRequests([bidWithParams({ 'ci': 'london' }), bidWithParams({ 'ci': 'gent' })], bidderRequest); - expect(JSON.parse(req.data).slots[0].parameters).to.deep.include({ 'ci': [ 'london' ] }); - expect(JSON.parse(req.data).slots[1].parameters).to.deep.include({ 'ci': [ 'gent' ] }); + expect(JSON.parse(req.data).slots[0].parameters).to.deep.include({ 'ci': ['london'] }); + expect(JSON.parse(req.data).slots[1].parameters).to.deep.include({ 'ci': ['gent'] }); }); it('should filter out empty params', function () { - const req = spec.buildRequests([ bidWithParams({ 'aa': [], 'bb': null, 'cc': '', 'dd': [ '', '' ], 'ee': [ 0, 1, null ], 'ff': 0, 'gg': [ 'x', 'y', '' ] }) ], bidderRequest); + const req = spec.buildRequests([bidWithParams({ 'aa': [], 'bb': null, 'cc': '', 'dd': ['', ''], 'ee': [0, 1, null], 'ff': 0, 'gg': ['x', 'y', ''] })], bidderRequest); const params = JSON.parse(req.data).slots[0].parameters; expect(params).to.not.have.any.keys('aa', 'bb', 'cc', 'dd'); - expect(params).to.deep.include({ 'ee': [ 0, 1 ], 'ff': [ 0 ], 'gg': [ 'x', 'y' ] }); + expect(params).to.deep.include({ 'ee': [0, 1], 'ff': [0], 'gg': ['x', 'y'] }); }); it('should include gdpr consent param', function () { - const req = spec.buildRequests([ minimalBid() ], bidderRequest); + const req = spec.buildRequests([minimalBid()], bidderRequest); - expect(JSON.parse(req.data).parameters).to.deep.include({ 'xt': [ 'CONSENT_STRING' ] }); + expect(JSON.parse(req.data).parameters).to.deep.include({ 'xt': ['CONSENT_STRING'] }); }); it('should include referer param in base64url format', function () { - const req = spec.buildRequests([ minimalBid() ], bidderRequest); + const req = spec.buildRequests([minimalBid()], bidderRequest); - expect(JSON.parse(req.data).parameters).to.deep.include({ 'xf': [ 'aHR0cDovL3ByZWJpZC5vcmcvZGV2LWRvY3Mvc3ViamVjdHM_X2Q9MQ' ] }); + expect(JSON.parse(req.data).parameters).to.deep.include({ 'xf': ['aHR0cDovL3ByZWJpZC5vcmcvZGV2LWRvY3Mvc3ViamVjdHM_X2Q9MQ'] }); }); it('should include eids', function () { const bid = minimalBid(); bid.userIdAsEids = [{ source: 'id5-sync.com', uids: [{ id: 'ID5@59sigaS-...' }] }]; - const req = spec.buildRequests([ bid ], bidderRequest); + const req = spec.buildRequests([bid], bidderRequest); expect(JSON.parse(req.data).user.ext.eids).to.deep.equal(bid.userIdAsEids); }); it('should not include eids field when userid module disabled', function () { - const req = spec.buildRequests([ minimalBid() ], bidderRequest); + const req = spec.buildRequests([minimalBid()], bidderRequest); expect(JSON.parse(req.data)).to.not.have.key('eids'); }); it('should request vast content as url by default', function () { - const req = spec.buildRequests([ minimalBid() ], bidderRequest); + const req = spec.buildRequests([minimalBid()], bidderRequest); expect(JSON.parse(req.data).vastContentAsUrl).to.equal(true); }); @@ -141,7 +141,7 @@ describe('AdheseAdapter', function () { it('should request vast content as markup when configured', function () { sinon.stub(config, 'getConfig').withArgs('adhese').returns({ vastContentAsUrl: false }); - const req = spec.buildRequests([ minimalBid() ], bidderRequest); + const req = spec.buildRequests([minimalBid()], bidderRequest); expect(JSON.parse(req.data).vastContentAsUrl).to.equal(false); config.getConfig.restore(); @@ -149,43 +149,43 @@ describe('AdheseAdapter', function () { it('should include bids', function () { const bid = minimalBid(); - const req = spec.buildRequests([ bid ], bidderRequest); + const req = spec.buildRequests([bid], bidderRequest); - expect(req.bids).to.deep.equal([ bid ]); + expect(req.bids).to.deep.equal([bid]); }); it('should make a POST request', function () { - const req = spec.buildRequests([ minimalBid() ], bidderRequest); + const req = spec.buildRequests([minimalBid()], bidderRequest); expect(req.method).to.equal('POST'); }); it('should request the json endpoint', function () { - const req = spec.buildRequests([ minimalBid() ], bidderRequest); + const req = spec.buildRequests([minimalBid()], bidderRequest); expect(req.url).to.equal('https://ads-demo.adhese.com/json'); }); it('should include params specified in the config', function () { - sinon.stub(config, 'getConfig').withArgs('adhese').returns({ globalTargets: { 'tl': [ 'all' ] } }); - const req = spec.buildRequests([ minimalBid() ], bidderRequest); + sinon.stub(config, 'getConfig').withArgs('adhese').returns({ globalTargets: { 'tl': ['all'] } }); + const req = spec.buildRequests([minimalBid()], bidderRequest); - expect(JSON.parse(req.data).parameters).to.deep.include({ 'tl': [ 'all' ] }); + expect(JSON.parse(req.data).parameters).to.deep.include({ 'tl': ['all'] }); config.getConfig.restore(); }); it('should give priority to bid params over config params', function () { sinon.stub(config, 'getConfig').withArgs('adhese').returns({ globalTargets: { 'xt': ['CONFIG_CONSENT_STRING'] } }); - const req = spec.buildRequests([ minimalBid() ], bidderRequest); + const req = spec.buildRequests([minimalBid()], bidderRequest); - expect(JSON.parse(req.data).parameters).to.deep.include({ 'xt': [ 'CONSENT_STRING' ] }); + expect(JSON.parse(req.data).parameters).to.deep.include({ 'xt': ['CONSENT_STRING'] }); config.getConfig.restore(); }); }); describe('interpretResponse', () => { const bidRequest = { - bids: [ minimalBid() ] + bids: [minimalBid()] }; it('should get correct ssp banner response', () => { @@ -212,7 +212,7 @@ describe('AdheseAdapter', function () { 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'}}, mediaType: 'banner'}, + extension: { 'prebid': { 'cpm': { 'amount': '1.000000', 'currency': 'USD' } }, mediaType: 'banner' }, adomain: [ 'www.example.com' ] @@ -239,7 +239,7 @@ describe('AdheseAdapter', function () { adType: 'leaderboard', seatbid: [ { - bid: [ { crid: '60613369', dealid: null } ], + bid: [{ crid: '60613369', dealid: null }], seat: '958' } ], @@ -267,7 +267,7 @@ describe('AdheseAdapter', function () { width: '640', height: '350', body: '', - extension: {'prebid': {'cpm': {'amount': '2.1', 'currency': 'USD'}}, mediaType: 'video'} + extension: { 'prebid': { 'cpm': { 'amount': '2.1', 'currency': 'USD' } }, mediaType: 'video' } } ] }; @@ -307,7 +307,7 @@ describe('AdheseAdapter', function () { width: '640', height: '350', cachedBodyUrl: 'https://ads-demo.adhese.com/content/38983ccc-4083-4c24-932c-96f798d969b3', - extension: {'prebid': {'cpm': {'amount': '2.1', 'currency': 'USD'}}, mediaType: 'video'} + extension: { 'prebid': { 'cpm': { 'amount': '2.1', 'currency': 'USD' } }, mediaType: 'video' } } ] }; diff --git a/test/spec/modules/adipoloBidAdapter_spec.js b/test/spec/modules/adipoloBidAdapter_spec.js index 823244ab758..31f08a09007 100644 --- a/test/spec/modules/adipoloBidAdapter_spec.js +++ b/test/spec/modules/adipoloBidAdapter_spec.js @@ -1,8 +1,8 @@ -import {expect} from 'chai'; -import {config} from 'src/config.js'; -import {spec} from 'modules/adipoloBidAdapter.js'; -import {deepClone} from 'src/utils'; -import {getBidFloor} from '../../../libraries/xeUtils/bidderUtils.js'; +import { expect } from 'chai'; +import { config } from 'src/config.js'; +import { spec } from 'modules/adipoloBidAdapter.js'; +import { deepClone } from 'src/utils'; +import { getBidFloor } from '../../../libraries/xeUtils/bidderUtils.js'; import sinon from 'sinon'; const US_ENDPOINT = 'https://prebid.adipolo.live'; @@ -50,12 +50,12 @@ defaultRequestVideo.mediaTypes = { const videoBidderRequest = { bidderCode: 'adipolo', - bids: [{mediaTypes: {video: {}}, bidId: 'qwerty'}] + bids: [{ mediaTypes: { video: {} }, bidId: 'qwerty' }] }; const displayBidderRequest = { bidderCode: 'adipolo', - bids: [{bidId: 'qwerty'}] + bids: [{ bidId: 'qwerty' }] }; describe('adipoloBidAdapter', () => { @@ -139,7 +139,7 @@ describe('adipoloBidAdapter', () => { expect(request).to.have.property('tz').and.to.equal(new Date().getTimezoneOffset()); expect(request).to.have.property('bc').and.to.equal(1); expect(request).to.have.property('floor').and.to.equal(null); - expect(request).to.have.property('banner').and.to.deep.equal({sizes: [[300, 250], [300, 200]]}); + expect(request).to.have.property('banner').and.to.deep.equal({ sizes: [[300, 250], [300, 200]] }); expect(request).to.have.property('gdprConsent').and.to.deep.equal({}); expect(request).to.have.property('userEids').and.to.deep.equal([]); expect(request).to.have.property('usPrivacy').and.to.equal(''); @@ -231,7 +231,7 @@ describe('adipoloBidAdapter', () => { it('should build request with valid bidfloor', function () { const bfRequest = deepClone(defaultRequest); - bfRequest.getFloor = () => ({floor: 5, currency: 'USD'}); + bfRequest.getFloor = () => ({ floor: 5, currency: 'USD' }); const request = JSON.parse(spec.buildRequests([bfRequest], {}).data)[0]; expect(request).to.have.property('floor').and.to.equal(5); }); @@ -247,8 +247,8 @@ describe('adipoloBidAdapter', () => { it('should build request with extended ids', function () { const idRequest = deepClone(defaultRequest); idRequest.userIdAsEids = [ - {source: 'adserver.org', uids: [{id: 'TTD_ID_FROM_USER_ID_MODULE', atype: 1, ext: {rtiPartner: 'TDID'}}]}, - {source: 'pubcid.org', uids: [{id: 'pubCommonId_FROM_USER_ID_MODULE', atype: 1}]} + { source: 'adserver.org', uids: [{ id: 'TTD_ID_FROM_USER_ID_MODULE', atype: 1, ext: { rtiPartner: 'TDID' } }] }, + { source: 'pubcid.org', uids: [{ id: 'pubCommonId_FROM_USER_ID_MODULE', atype: 1 }] } ]; const request = JSON.parse(spec.buildRequests([idRequest], {}).data)[0]; expect(request).to.have.property('userEids').and.deep.equal(idRequest.userIdAsEids); @@ -300,7 +300,7 @@ describe('adipoloBidAdapter', () => { } }; - const validResponse = spec.interpretResponse(serverResponse, {bidderRequest: displayBidderRequest}); + const validResponse = spec.interpretResponse(serverResponse, { bidderRequest: displayBidderRequest }); const bid = validResponse[0]; expect(validResponse).to.be.an('array').that.is.not.empty; expect(bid.requestId).to.equal('qwerty'); @@ -309,7 +309,7 @@ describe('adipoloBidAdapter', () => { expect(bid.width).to.equal(300); expect(bid.height).to.equal(250); expect(bid.ttl).to.equal(600); - expect(bid.meta).to.deep.equal({advertiserDomains: ['adipolo']}); + expect(bid.meta).to.deep.equal({ advertiserDomains: ['adipolo'] }); }); it('should interpret valid banner response', function () { @@ -330,7 +330,7 @@ describe('adipoloBidAdapter', () => { } }; - const validResponseBanner = spec.interpretResponse(serverResponse, {bidderRequest: displayBidderRequest}); + const validResponseBanner = spec.interpretResponse(serverResponse, { bidderRequest: displayBidderRequest }); const bid = validResponseBanner[0]; expect(validResponseBanner).to.be.an('array').that.is.not.empty; expect(bid.mediaType).to.equal('banner'); @@ -356,7 +356,7 @@ describe('adipoloBidAdapter', () => { } }; - const validResponseBanner = spec.interpretResponse(serverResponse, {bidderRequest: videoBidderRequest}); + const validResponseBanner = spec.interpretResponse(serverResponse, { bidderRequest: videoBidderRequest }); const bid = validResponseBanner[0]; expect(validResponseBanner).to.be.an('array').that.is.not.empty; expect(bid.mediaType).to.equal('video'); @@ -372,12 +372,12 @@ describe('adipoloBidAdapter', () => { }); it('should return empty if sync is not allowed', function () { - const opts = spec.getUserSyncs({iframeEnabled: false, pixelEnabled: false}); + const opts = spec.getUserSyncs({ iframeEnabled: false, pixelEnabled: false }); expect(opts).to.be.an('array').that.is.empty; }); it('should allow iframe sync', function () { - const opts = spec.getUserSyncs({iframeEnabled: true, pixelEnabled: false}, [{ + const opts = spec.getUserSyncs({ iframeEnabled: true, pixelEnabled: false }, [{ body: { data: [{ requestId: 'qwerty', @@ -396,7 +396,7 @@ describe('adipoloBidAdapter', () => { }); it('should allow pixel sync', function () { - const opts = spec.getUserSyncs({iframeEnabled: false, pixelEnabled: true}, [{ + const opts = spec.getUserSyncs({ iframeEnabled: false, pixelEnabled: true }, [{ body: { data: [{ requestId: 'qwerty', @@ -415,7 +415,7 @@ describe('adipoloBidAdapter', () => { }); it('should allow pixel sync and parse consent params', function () { - const opts = spec.getUserSyncs({iframeEnabled: false, pixelEnabled: true}, [{ + const opts = spec.getUserSyncs({ iframeEnabled: false, pixelEnabled: true }, [{ body: { data: [{ requestId: 'qwerty', @@ -439,20 +439,20 @@ describe('adipoloBidAdapter', () => { describe('getBidFloor', function () { it('should return null when getFloor is not a function', () => { - const bid = {getFloor: 2}; + const bid = { getFloor: 2 }; const result = getBidFloor(bid); expect(result).to.be.null; }); it('should return null when getFloor doesnt return an object', () => { - const bid = {getFloor: () => 2}; + const bid = { getFloor: () => 2 }; const result = getBidFloor(bid); expect(result).to.be.null; }); it('should return null when floor is not a number', () => { const bid = { - getFloor: () => ({floor: 'string', currency: 'USD'}) + getFloor: () => ({ floor: 'string', currency: 'USD' }) }; const result = getBidFloor(bid); expect(result).to.be.null; @@ -460,7 +460,7 @@ describe('adipoloBidAdapter', () => { it('should return null when currency is not USD', () => { const bid = { - getFloor: () => ({floor: 5, currency: 'EUR'}) + getFloor: () => ({ floor: 5, currency: 'EUR' }) }; const result = getBidFloor(bid); expect(result).to.be.null; @@ -468,7 +468,7 @@ describe('adipoloBidAdapter', () => { it('should return floor value when everything is correct', () => { const bid = { - getFloor: () => ({floor: 5, currency: 'USD'}) + getFloor: () => ({ floor: 5, currency: 'USD' }) }; const result = getBidFloor(bid); expect(result).to.equal(5); diff --git a/test/spec/modules/adkernelAdnAnalytics_spec.js b/test/spec/modules/adkernelAdnAnalytics_spec.js index 95d01f53b2b..41477c55588 100644 --- a/test/spec/modules/adkernelAdnAnalytics_spec.js +++ b/test/spec/modules/adkernelAdnAnalytics_spec.js @@ -1,5 +1,5 @@ -import analyticsAdapter, {ExpiringQueue, getUmtSource, storage} from 'modules/adkernelAdnAnalyticsAdapter'; -import {expect} from 'chai'; +import analyticsAdapter, { ExpiringQueue, getUmtSource, storage } from 'modules/adkernelAdnAnalyticsAdapter'; +import { expect } from 'chai'; import adapterManager from 'src/adapterManager'; import { EVENTS } from 'src/constants.js'; @@ -231,17 +231,17 @@ describe('', function () { }); it('should handle auction init event', function () { - events.emit(EVENTS.AUCTION_INIT, {config: {}, bidderRequests: [REQUEST], timeout: 3000}); + events.emit(EVENTS.AUCTION_INIT, { config: {}, bidderRequests: [REQUEST], timeout: 3000 }); const ev = analyticsAdapter.context.queue.peekAll(); expect(ev).to.have.length(1); - expect(ev[0]).to.be.eql({event: 'auctionInit'}); + expect(ev[0]).to.be.eql({ event: 'auctionInit' }); }); it('should handle bid request event', function () { events.emit(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'}); + expect(ev[1]).to.be.eql({ event: 'bidRequested', adapter: 'adapter', tagid: 'container-1' }); }); it('should handle bid response event', function () { @@ -264,7 +264,7 @@ describe('', function () { expect(ev).to.have.length(0); expect(ajaxStub.calledOnce).to.be.equal(true); ev = JSON.parse(ajaxStub.firstCall.args[0]).hb_ev; - expect(ev[3]).to.be.eql({event: 'auctionEnd', time: 0.447}); + expect(ev[3]).to.be.eql({ event: 'auctionEnd', time: 0.447 }); }); it('should handle winning bid', function () { @@ -272,7 +272,7 @@ describe('', function () { timer.tick(4500); expect(ajaxStub.calledTwice).to.be.equal(true); const 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}); + 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 2618d5f8ddb..875f4b6e42a 100644 --- a/test/spec/modules/adkernelAdnBidAdapter_spec.js +++ b/test/spec/modules/adkernelAdnBidAdapter_spec.js @@ -1,6 +1,6 @@ -import {expect} from 'chai'; -import {spec} from 'modules/adkernelAdnBidAdapter'; -import {config} from 'src/config'; +import { expect } from 'chai'; +import { spec } from 'modules/adkernelAdnBidAdapter'; +import { config } from 'src/config'; describe('AdkernelAdn adapter', function () { const bid1_pub1 = { @@ -92,11 +92,11 @@ describe('AdkernelAdn adapter', function () { auctionId: 'auc-001', bidId: 'Bid_01', mediaTypes: { - banner: {sizes: [[300, 250], [300, 200]]}, - video: {context: 'instream', playerSize: [[640, 480]]} + banner: { sizes: [[300, 250], [300, 200]] }, + video: { context: 'instream', playerSize: [[640, 480]] } }, adUnitCode: 'ad-unit-1', - params: {pubId: 7} + params: { pubId: 7 } }; const response = { @@ -220,7 +220,7 @@ describe('AdkernelAdn adapter', function () { } describe('banner request building', function () { - const [_, tagRequests] = buildRequest([bid1_pub1], {ortb2: {source: {tid: 'mock-tid'}}}); + const [_, tagRequests] = buildRequest([bid1_pub1], { ortb2: { source: { tid: 'mock-tid' } } }); const tagRequest = tagRequests[0]; it('should have request id', function () { @@ -260,7 +260,7 @@ describe('AdkernelAdn adapter', function () { it('should contain gdpr and ccpa information if consent is configured', function () { const [_, bidRequests] = buildRequest([bid1_pub1], - {gdprConsent: {gdprApplies: true, consentString: 'test-consent-string'}, uspConsent: '1YNN'}); + { gdprConsent: { gdprApplies: true, consentString: 'test-consent-string' }, uspConsent: '1YNN' }); 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'); @@ -268,14 +268,14 @@ describe('AdkernelAdn adapter', function () { }); it('should\'t contain consent string if gdpr isn\'t applied', function () { - const [_, bidRequests] = buildRequest([bid1_pub1], {gdprConsent: {gdprApplies: false}}); + const [_, 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'); }); it('should\'t contain consent string if gdpr isn\'t applied', function () { - config.setConfig({coppa: true}); + config.setConfig({ coppa: true }); const [_, bidRequests] = buildRequest([bid1_pub1]); config.resetConfig(); expect(bidRequests[0]).to.have.property('user'); @@ -365,7 +365,7 @@ describe('AdkernelAdn adapter', function () { let responses; before(function () { - responses = spec.interpretResponse({body: response}); + responses = spec.interpretResponse({ body: response }); }); it('should parse all responses', function () { @@ -404,9 +404,9 @@ describe('AdkernelAdn adapter', function () { }); it('should perform usersync', function () { - let syncs = spec.getUserSyncs({iframeEnabled: false}, [{body: response}]); + let syncs = spec.getUserSyncs({ iframeEnabled: false }, [{ body: response }]); expect(syncs).to.have.length(0); - syncs = spec.getUserSyncs({iframeEnabled: true}, [{body: response}]); + syncs = spec.getUserSyncs({ iframeEnabled: true }, [{ body: response }]); expect(syncs).to.have.length(1); expect(syncs[0]).to.have.property('type', 'iframe'); expect(syncs[0]).to.have.property('url', 'https://dsp.adkernel.com/sync'); @@ -414,12 +414,12 @@ describe('AdkernelAdn adapter', function () { it('should handle user-sync only response', function () { const [pbRequests, tagRequests] = buildRequest([bid1_pub1]); - const resp = spec.interpretResponse({body: usersyncOnlyResponse}, pbRequests[0]); + const resp = spec.interpretResponse({ body: usersyncOnlyResponse }, pbRequests[0]); expect(resp).to.have.length(0); }); it('shouldn\' fail on empty response', function () { - const syncs = spec.getUserSyncs({iframeEnabled: true}, [{body: ''}]); + const syncs = spec.getUserSyncs({ iframeEnabled: true }, [{ body: '' }]); expect(syncs).to.have.length(0); }); }); diff --git a/test/spec/modules/adkernelBidAdapter_spec.js b/test/spec/modules/adkernelBidAdapter_spec.js index b2ca5c622fe..34afb59ec4a 100644 --- a/test/spec/modules/adkernelBidAdapter_spec.js +++ b/test/spec/modules/adkernelBidAdapter_spec.js @@ -1,15 +1,14 @@ -import {expect} from 'chai'; -import {spec} from 'modules/adkernelBidAdapter'; +import { expect } from 'chai'; +import { spec } from 'modules/adkernelBidAdapter'; import * as utils from 'src/utils'; -import * as navigatorDnt from 'libraries/dnt/index.js'; -import {NATIVE, BANNER, VIDEO} from 'src/mediaTypes'; -import {config} from 'src/config'; -import {parseDomain} from '../../../src/refererDetection.js'; +import { NATIVE, BANNER, VIDEO } from 'src/mediaTypes'; +import { config } from 'src/config'; +import { parseDomain } from '../../../src/refererDetection.js'; describe('Adkernel adapter', function () { const bid1_zone1 = { bidder: 'adkernel', - params: {zoneId: 1, host: 'rtb.adkernel.com'}, + params: { zoneId: 1, host: 'rtb.adkernel.com' }, adUnitCode: 'ad-unit-1', bidId: 'Bid_01', bidderRequestId: 'req-001', @@ -26,7 +25,7 @@ describe('Adkernel adapter', function () { } }; const bid2_zone2 = { bidder: 'adkernel', - params: {zoneId: 2, host: 'rtb.adkernel.com'}, + params: { zoneId: 2, host: 'rtb.adkernel.com' }, adUnitCode: 'ad-unit-2', bidId: 'Bid_02', bidderRequestId: 'req-001', @@ -40,13 +39,13 @@ describe('Adkernel adapter', function () { { source: 'crwdcntrl.net', uids: [ - {atype: 1, id: '97d09fbba28542b7acbb6317c9534945a702b74c5993c352f332cfe83f40cdd9'} + { atype: 1, id: '97d09fbba28542b7acbb6317c9534945a702b74c5993c352f332cfe83f40cdd9' } ] } ] }; const bid3_host2 = { bidder: 'adkernel', - params: {zoneId: 1, host: 'rtb-private.adkernel.com'}, + params: { zoneId: 1, host: 'rtb-private.adkernel.com' }, adUnitCode: 'ad-unit-2', bidId: 'Bid_02', bidderRequestId: 'req-001', @@ -58,7 +57,7 @@ describe('Adkernel adapter', function () { } }; const bid_without_zone = { bidder: 'adkernel', - params: {host: 'rtb-private.adkernel.com'}, + params: { host: 'rtb-private.adkernel.com' }, adUnitCode: 'ad-unit-1', bidId: 'Bid_W', bidderRequestId: 'req-002', @@ -70,7 +69,7 @@ describe('Adkernel adapter', function () { } }; const bid_without_host = { bidder: 'adkernel', - params: {zoneId: 1}, + params: { zoneId: 1 }, adUnitCode: 'ad-unit-1', bidId: 'Bid_W', bidderRequestId: 'req-002', @@ -82,7 +81,7 @@ describe('Adkernel adapter', function () { } }; const bid_with_wrong_zoneId = { bidder: 'adkernel', - params: {zoneId: 'wrong id', host: 'rtb.adkernel.com'}, + params: { zoneId: 'wrong id', host: 'rtb.adkernel.com' }, adUnitCode: 'ad-unit-2', bidId: 'Bid_02', bidderRequestId: 'req-002', @@ -116,10 +115,10 @@ describe('Adkernel adapter', function () { adUnitCode: 'ad-unit-1' }; const bid_multiformat = { bidder: 'adkernel', - params: {zoneId: 1, host: 'rtb.adkernel.com'}, + params: { zoneId: 1, host: 'rtb.adkernel.com' }, mediaTypes: { - banner: {sizes: [[300, 250], [300, 200]]}, - video: {context: 'instream', playerSize: [[640, 480]]} + banner: { sizes: [[300, 250], [300, 200]] }, + video: { context: 'instream', playerSize: [[640, 480]] } }, adUnitCode: 'ad-unit-1', transactionId: 'f82c64b8-c602-42a4-9791-4a268f6559ed', @@ -129,7 +128,7 @@ describe('Adkernel adapter', function () { }; const bid_native = { bidder: 'adkernel', - params: {zoneId: 1, host: 'rtb.adkernel.com'}, + params: { zoneId: 1, host: 'rtb.adkernel.com' }, mediaTypes: { native: { title: { @@ -144,7 +143,7 @@ describe('Adkernel adapter', function () { }, icon: { required: true, - aspect_ratios: [{min_width: 50, min_height: 50}] + aspect_ratios: [{ min_width: 50, min_height: 50 }] }, image: { required: true, @@ -177,25 +176,24 @@ describe('Adkernel adapter', function () { ver: '1.2', assets: [ { - id: 0, required: 1, title: {len: 80} - }, { - id: 1, required: 1, data: {type: 2}}, + id: 0, required: 1, title: { len: 80 } + }, { id: 1, required: 1, data: { type: 2 } }, { - id: 2, required: 1, data: {type: 10} + id: 2, required: 1, data: { type: 10 } }, { - id: 3, required: 1, img: {type: 1, wmin: 50, hmin: 50} + id: 3, required: 1, img: { type: 1, wmin: 50, hmin: 50 } }, { - id: 4, required: 1, img: {type: 3, w: 300, h: 200} + id: 4, required: 1, img: { type: 3, w: 300, h: 200 } }, { - id: 5, required: 0, data: {type: 3} + id: 5, required: 0, data: { type: 3 } }, { - id: 6, required: 0, data: {type: 6} + id: 6, required: 0, data: { type: 6 } }, { - id: 7, required: 0, data: {type: 12} + id: 7, required: 0, data: { type: 12 } }, { - id: 8, required: 0, data: {type: 1} + id: 8, required: 0, data: { type: 1 } }, { - id: 9, required: 0, data: {type: 11} + id: 9, required: 0, data: { type: 11 } } ], privacy: 1 @@ -224,7 +222,7 @@ describe('Adkernel adapter', function () { }] }], ext: { - adk_usersync: [{type: 1, url: 'https://adk.sync.com/sync'}] + adk_usersync: [{ type: 1, url: 'https://adk.sync.com/sync' }] } }; const videoBidResponse = { id: '47ce4badcf7482', @@ -259,7 +257,7 @@ describe('Adkernel adapter', function () { const usersyncOnlyResponse = { id: 'nobid1', ext: { - adk_usersync: [{type: 2, url: 'https://adk.sync.com/sync'}] + adk_usersync: [{ type: 2, url: 'https://adk.sync.com/sync' }] } }; const nativeResponse = { id: '56fbc713-b737-4651-9050-13376aed9818', @@ -272,15 +270,15 @@ describe('Adkernel adapter', function () { adm: JSON.stringify({ native: { assets: [ - {id: 0, title: {text: 'Title'}}, - {id: 3, data: {value: 'Description'}}, - {id: 4, data: {value: 'Additional description'}}, - {id: 1, img: {url: 'http://rtb.com/thumbnail?i=pTuOlf5KHUo_0&imgt=icon', w: 50, h: 50}}, - {id: 2, img: {url: 'http://rtb.com/thumbnail?i=pTuOlf5KHUo_0', w: 300, h: 200}}, - {id: 5, data: {value: 'Sponsor.com'}}, - {id: 14, data: {value: 'displayurl.com'}} + { id: 0, title: { text: 'Title' } }, + { id: 3, data: { value: 'Description' } }, + { id: 4, data: { value: 'Additional description' } }, + { id: 1, img: { url: 'http://rtb.com/thumbnail?i=pTuOlf5KHUo_0&imgt=icon', w: 50, h: 50 } }, + { id: 2, img: { url: 'http://rtb.com/thumbnail?i=pTuOlf5KHUo_0', w: 300, h: 200 } }, + { id: 5, data: { value: 'Sponsor.com' } }, + { id: 14, data: { value: 'displayurl.com' } } ], - link: {url: 'http://rtb.com/click?i=pTuOlf5KHUo_0'}, + link: { url: 'http://rtb.com/click?i=pTuOlf5KHUo_0' }, imptrackers: ['http://rtb.com/win?i=pTuOlf5KHUo_0&f=imp'] } }), @@ -338,15 +336,13 @@ describe('Adkernel adapter', function () { }); function buildBidderRequest(url = 'https://example.com/index.html', params = {}) { - return Object.assign({}, params, {refererInfo: {page: url, domain: parseDomain(url), reachedTop: true}, timeout: 3000, bidderCode: 'adkernel'}); + return Object.assign({}, params, { refererInfo: { page: url, domain: parseDomain(url), reachedTop: true }, timeout: 3000, bidderCode: 'adkernel' }); } const DEFAULT_BIDDER_REQUEST = buildBidderRequest(); - function buildRequest(bidRequests, bidderRequest = DEFAULT_BIDDER_REQUEST, dnt = true) { - const dntmock = sandbox.stub(navigatorDnt, 'getDNT').callsFake(() => dnt); + function buildRequest(bidRequests, bidderRequest = DEFAULT_BIDDER_REQUEST) { bidderRequest.bids = bidRequests; const pbRequests = spec.buildRequests(bidRequests, bidderRequest); - dntmock.restore(); const rtbRequests = pbRequests.map(r => JSON.parse(r.data)); return [pbRequests, rtbRequests]; } @@ -398,7 +394,7 @@ describe('Adkernel adapter', function () { 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}]); + expect(bidRequest.imp[0].banner.format).to.be.eql([{ w: 300, h: 250 }, { w: 300, h: 200 }]); }); it('should respect secure connection', function () { @@ -419,7 +415,6 @@ describe('Adkernel adapter', function () { expect(bidRequest.device).to.have.property('ip', 'caller'); expect(bidRequest.device).to.have.property('ipv6', 'caller'); expect(bidRequest.device).to.have.property('ua', 'caller'); - expect(bidRequest.device).to.have.property('dnt', 1); }); it('should copy FPD to imp.banner', function() { @@ -441,21 +436,22 @@ describe('Adkernel adapter', function () { it('should contain gdpr-related information if consent is configured', function () { const [_, bidRequests] = buildRequest([bid1_zone1], buildBidderRequest('https://example.com/index.html', { - gdprConsent: {gdprApplies: true, consentString: 'test-consent-string', vendorData: {}}, + gdprConsent: { gdprApplies: true, consentString: 'test-consent-string', vendorData: {} }, uspConsent: '1YNN', - gppConsent: {gppString: 'DBABMA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA', applicableSections: [2]}} + gppConsent: { gppString: 'DBABMA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA', applicableSections: [2] } + } )); const bidRequest = bidRequests[0]; expect(bidRequest).to.have.property('regs'); - expect(bidRequest.regs.ext).to.be.eql({'gdpr': 1, 'us_privacy': '1YNN'}); + expect(bidRequest.regs.ext).to.be.eql({ 'gdpr': 1, 'us_privacy': '1YNN' }); expect(bidRequest.regs.gpp).to.be.eql('DBABMA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA'); expect(bidRequest.regs.gpp_sid).to.be.eql([2]); expect(bidRequest).to.have.property('user'); - expect(bidRequest.user.ext).to.be.eql({'consent': 'test-consent-string'}); + expect(bidRequest.user.ext).to.be.eql({ 'consent': 'test-consent-string' }); }); it('should contain coppa if configured', function () { - config.setConfig({coppa: true}); + config.setConfig({ coppa: true }); const [_, bidRequests] = buildRequest([bid1_zone1]); const bidRequest = bidRequests[0]; expect(bidRequest).to.have.property('regs'); @@ -463,18 +459,13 @@ describe('Adkernel adapter', function () { }); it('should\'t contain consent string if gdpr isn\'t applied', function () { - const [_, bidRequests] = buildRequest([bid1_zone1], buildBidderRequest('https://example.com/index.html', {gdprConsent: {gdprApplies: false}})); + const [_, bidRequests] = buildRequest([bid1_zone1], buildBidderRequest('https://example.com/index.html', { gdprConsent: { gdprApplies: false } })); const bidRequest = bidRequests[0]; expect(bidRequest).to.have.property('regs'); - expect(bidRequest.regs.ext).to.be.eql({'gdpr': 0}); + 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 () { - const [_, bidRequests] = buildRequest([bid1_zone1], DEFAULT_BIDDER_REQUEST, false); - expect(bidRequests[0].device).to.not.have.property('dnt'); - }); - it('should forward default bidder timeout', function() { const [_, bidRequests] = buildRequest([bid1_zone1]); expect(bidRequests[0]).to.have.property('tmax', 3000); @@ -552,7 +543,7 @@ describe('Adkernel adapter', function () { expect(bidRequests[0].imp[1].id).to.be.not.eql(bidRequests[0].imp[0].id); }); it('should collect ads back to same requestId', function() { - const bids = spec.interpretResponse({body: multiformat_response}, pbRequests[0]); + const bids = spec.interpretResponse({ body: multiformat_response }, pbRequests[0]); expect(bids).to.have.length(2); expect(bids[0].requestId).to.be.eql('Bid_01'); expect(bids[0].mediaType).to.be.eql('banner'); @@ -658,7 +649,7 @@ describe('Adkernel adapter', function () { describe('responses processing', function () { it('should return fully-initialized banner bid-response', function () { const [pbRequests, _] = buildRequest([bid1_zone1]); - const resp = spec.interpretResponse({body: bannerBidResponse}, pbRequests[0])[0]; + const resp = spec.interpretResponse({ body: bannerBidResponse }, 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); @@ -675,7 +666,7 @@ describe('Adkernel adapter', function () { it('should return fully-initialized video bid-response', function () { const [pbRequests, _] = buildRequest([bid_video]); - const resp = spec.interpretResponse({body: videoBidResponse}, pbRequests[0])[0]; + const 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); @@ -686,7 +677,7 @@ describe('Adkernel adapter', function () { it('should support vast xml in adm', function () { const [pbRequests, _] = buildRequest([bid_video]); - const resp = spec.interpretResponse({body: videoBidResponseWithAdm}, pbRequests[0])[0]; + const resp = spec.interpretResponse({ body: videoBidResponseWithAdm }, pbRequests[0])[0]; expect(resp).to.have.property('requestId', 'Bid_Video'); expect(resp.mediaType).to.equal(VIDEO); expect(resp.cpm).to.equal(0.00145); @@ -698,27 +689,27 @@ describe('Adkernel adapter', function () { it('should add nurl as pixel for banner response', function () { const [pbRequests, _] = buildRequest([bid1_zone1]); - const resp = spec.interpretResponse({body: bannerBidResponse}, pbRequests[0])[0]; + const resp = spec.interpretResponse({ body: bannerBidResponse }, pbRequests[0])[0]; const expectedNurl = bannerBidResponse.seatbid[0].bid[0].nurl + '&px=1'; expect(resp.ad).to.have.string(expectedNurl); }); it('should handle bidresponse with user-sync only', function () { const [pbRequests, _] = buildRequest([bid1_zone1]); - const resp = spec.interpretResponse({body: usersyncOnlyResponse}, pbRequests[0]); + const resp = spec.interpretResponse({ body: usersyncOnlyResponse }, pbRequests[0]); expect(resp).to.have.length(0); }); it('should perform usersync', function () { - let syncs = spec.getUserSyncs({iframeEnabled: true, pixelEnabled: true}, []); + let syncs = spec.getUserSyncs({ iframeEnabled: true, pixelEnabled: true }, []); expect(syncs).to.have.length(0); - syncs = spec.getUserSyncs({iframeEnabled: false, pixelEnabled: false}, [{body: bannerBidResponse}]); + syncs = spec.getUserSyncs({ iframeEnabled: false, pixelEnabled: false }, [{ body: bannerBidResponse }]); expect(syncs).to.have.length(0); - syncs = spec.getUserSyncs({iframeEnabled: true, pixelEnabled: true}, [{body: bannerBidResponse}]); + syncs = spec.getUserSyncs({ iframeEnabled: true, pixelEnabled: true }, [{ body: bannerBidResponse }]); expect(syncs).to.have.length(1); expect(syncs[0]).to.have.property('type', 'iframe'); expect(syncs[0]).to.have.property('url', 'https://adk.sync.com/sync'); - syncs = spec.getUserSyncs({iframeEnabled: false, pixelEnabled: true}, [{body: usersyncOnlyResponse}]); + syncs = spec.getUserSyncs({ iframeEnabled: false, pixelEnabled: true }, [{ body: usersyncOnlyResponse }]); expect(syncs).to.have.length(1); expect(syncs[0]).to.have.property('type', 'image'); expect(syncs[0]).to.have.property('url', 'https://adk.sync.com/sync'); @@ -744,21 +735,21 @@ describe('Adkernel adapter', function () { const request = JSON.parse(bidRequests[0].imp[0].native.request); expect(request).to.have.property('ver', '1.2'); expect(request.assets).to.have.length(10); - expect(request.assets[0]).to.be.eql({id: 0, required: 1, title: {len: 80}}); - expect(request.assets[1]).to.be.eql({id: 1, required: 1, data: {type: 2}}); - expect(request.assets[2]).to.be.eql({id: 2, required: 1, data: {type: 10}}); - expect(request.assets[3]).to.be.eql({id: 3, required: 1, img: {wmin: 50, hmin: 50, type: 1}}); - expect(request.assets[4]).to.be.eql({id: 4, required: 1, img: {w: 300, h: 200, type: 3}}); - expect(request.assets[5]).to.be.eql({id: 5, required: 0, data: {type: 3}}); - expect(request.assets[6]).to.be.eql({id: 6, required: 0, data: {type: 6}}); - expect(request.assets[7]).to.be.eql({id: 7, required: 0, data: {type: 12}}); - expect(request.assets[8]).to.be.eql({id: 8, required: 0, data: {type: 1}}); - expect(request.assets[9]).to.be.eql({id: 9, required: 0, data: {type: 11}}); + expect(request.assets[0]).to.be.eql({ id: 0, required: 1, title: { len: 80 } }); + expect(request.assets[1]).to.be.eql({ id: 1, required: 1, data: { type: 2 } }); + expect(request.assets[2]).to.be.eql({ id: 2, required: 1, data: { type: 10 } }); + expect(request.assets[3]).to.be.eql({ id: 3, required: 1, img: { wmin: 50, hmin: 50, type: 1 } }); + expect(request.assets[4]).to.be.eql({ id: 4, required: 1, img: { w: 300, h: 200, type: 3 } }); + expect(request.assets[5]).to.be.eql({ id: 5, required: 0, data: { type: 3 } }); + expect(request.assets[6]).to.be.eql({ id: 6, required: 0, data: { type: 6 } }); + expect(request.assets[7]).to.be.eql({ id: 7, required: 0, data: { type: 12 } }); + expect(request.assets[8]).to.be.eql({ id: 8, required: 0, data: { type: 1 } }); + expect(request.assets[9]).to.be.eql({ id: 9, required: 0, data: { type: 11 } }); }); it('native response processing', () => { const [pbRequests, _] = buildRequest([bid_native]); - const resp = spec.interpretResponse({body: nativeResponse}, pbRequests[0])[0]; + const resp = spec.interpretResponse({ body: nativeResponse }, pbRequests[0])[0]; expect(resp).to.have.property('requestId', 'Bid_01'); expect(resp).to.have.property('cpm', 2.25); expect(resp).to.have.property('currency', 'EUR'); @@ -774,15 +765,15 @@ describe('Adkernel adapter', function () { expect(resp.native.ortb).to.be.eql({ assets: [ - {id: 0, title: {text: 'Title'}}, - {id: 3, data: {value: 'Description'}}, - {id: 4, data: {value: 'Additional description'}}, - {id: 1, img: {url: 'http://rtb.com/thumbnail?i=pTuOlf5KHUo_0&imgt=icon', w: 50, h: 50}}, - {id: 2, img: {url: 'http://rtb.com/thumbnail?i=pTuOlf5KHUo_0', w: 300, h: 200}}, - {id: 5, data: {value: 'Sponsor.com'}}, - {id: 14, data: {value: 'displayurl.com'}} + { id: 0, title: { text: 'Title' } }, + { id: 3, data: { value: 'Description' } }, + { id: 4, data: { value: 'Additional description' } }, + { id: 1, img: { url: 'http://rtb.com/thumbnail?i=pTuOlf5KHUo_0&imgt=icon', w: 50, h: 50 } }, + { id: 2, img: { url: 'http://rtb.com/thumbnail?i=pTuOlf5KHUo_0', w: 300, h: 200 } }, + { id: 5, data: { value: 'Sponsor.com' } }, + { id: 14, data: { value: 'displayurl.com' } } ], - link: {url: 'http://rtb.com/click?i=pTuOlf5KHUo_0'}, + link: { url: 'http://rtb.com/click?i=pTuOlf5KHUo_0' }, imptrackers: ['http://rtb.com/win?i=pTuOlf5KHUo_0&f=imp'] }); }); @@ -797,7 +788,7 @@ describe('Adkernel adapter', function () { }); it('should trigger pixel for nurl', () => { const [pbRequests, _] = buildRequest([bid_video]); - const bid = spec.interpretResponse({body: videoBidResponseWithAdm}, pbRequests[0])[0]; + const bid = spec.interpretResponse({ body: videoBidResponseWithAdm }, pbRequests[0])[0]; spec.onBidWon(bid); expect(utils.triggerPixel.callCount).to.equal(1); }); diff --git a/test/spec/modules/adlaneRtdProvider_spec.js b/test/spec/modules/adlaneRtdProvider_spec.js index 43f7790ce57..cdb27101859 100644 --- a/test/spec/modules/adlaneRtdProvider_spec.js +++ b/test/spec/modules/adlaneRtdProvider_spec.js @@ -10,7 +10,7 @@ import { import * as utils from 'src/utils.js'; import * as storageManager from 'src/storageManager.js'; import { config } from 'src/config.js'; -import {init} from 'modules/rtdModule' +import { init } from 'modules/rtdModule' describe('adlane Module', () => { let sandbox; diff --git a/test/spec/modules/adlooxAdServerVideo_spec.js b/test/spec/modules/adlooxAdServerVideo_spec.js index 6469f72e59a..5349cd69dc2 100644 --- a/test/spec/modules/adlooxAdServerVideo_spec.js +++ b/test/spec/modules/adlooxAdServerVideo_spec.js @@ -5,7 +5,7 @@ import { expect } from 'chai'; import * as events from 'src/events.js'; import { targeting } from 'src/targeting.js'; import * as utils from 'src/utils.js'; -import {server} from '../../mocks/xhr.js'; +import { server } from '../../mocks/xhr.js'; const analyticsAdapterName = 'adloox'; @@ -17,7 +17,7 @@ describe('Adloox Ad Server Video', function () { mediaTypes: { video: { context: 'instream', - playerSize: [ 640, 480 ] + playerSize: [640, 480] } }, bids: [ @@ -156,7 +156,7 @@ describe('Adloox Ad Server Video', function () { const BID = utils.deepClone(bid); const getWinningBidsStub = sinon.stub(targeting, 'getWinningBids') - getWinningBidsStub.withArgs(adUnit.code).returns([ BID ]); + getWinningBidsStub.withArgs(adUnit.code).returns([BID]); const ret = buildVideoUrl({ url: vastUrl }, function () {}); expect(ret).is.false; @@ -203,7 +203,7 @@ describe('Adloox Ad Server Video', function () { beforeEach(function () { BID = utils.deepClone(bid); getWinningBidsStub = sinon.stub(targeting, 'getWinningBids') - getWinningBidsStub.withArgs(adUnit.code).returns([ BID ]); + getWinningBidsStub.withArgs(adUnit.code).returns([BID]); }); afterEach(function () { getWinningBidsStub.restore(); diff --git a/test/spec/modules/adlooxAnalyticsAdapter_spec.js b/test/spec/modules/adlooxAnalyticsAdapter_spec.js index ede92f5934e..692349a31d6 100644 --- a/test/spec/modules/adlooxAnalyticsAdapter_spec.js +++ b/test/spec/modules/adlooxAnalyticsAdapter_spec.js @@ -40,10 +40,6 @@ describe('Adloox Analytics Adapter', function () { } }; - adapterManager.registerAnalyticsAdapter({ - code: analyticsAdapterName, - adapter: analyticsAdapter - }); describe('enableAnalytics', function () { afterEach(function () { analyticsAdapter.disableAnalytics(); @@ -112,7 +108,7 @@ describe('Adloox Analytics Adapter', function () { done(); }); - [ 'client', 'clientid', 'platformid', 'tagid' ].forEach(function (o) { + ['client', 'clientid', 'platformid', 'tagid'].forEach(function (o) { it('should require options.' + o, function (done) { const analyticsOptionsLocal = utils.deepClone(analyticsOptions); delete analyticsOptionsLocal[o]; @@ -253,7 +249,7 @@ describe('Adloox Analytics Adapter', function () { const data = { url: 'https://example.com?', args: [ - [ 'client', '%%client%%' ] + ['client', '%%client%%'] ], bid: bid, ids: true diff --git a/test/spec/modules/adlooxRtdProvider_spec.js b/test/spec/modules/adlooxRtdProvider_spec.js index 0fb0da58c1a..783b6ad6ca8 100644 --- a/test/spec/modules/adlooxRtdProvider_spec.js +++ b/test/spec/modules/adlooxRtdProvider_spec.js @@ -1,12 +1,12 @@ import adapterManager from 'src/adapterManager.js'; import analyticsAdapter from 'modules/adlooxAnalyticsAdapter.js'; -import {auctionManager} from 'src/auctionManager.js'; +import { auctionManager } from 'src/auctionManager.js'; import { expect } from 'chai'; import * as events from 'src/events.js'; import * as prebidGlobal from 'src/prebidGlobal.js'; import { subModuleObj as rtdProvider } from 'modules/adlooxRtdProvider.js'; import * as utils from 'src/utils.js'; -import {server} from '../../mocks/xhr.js'; +import { server } from '../../mocks/xhr.js'; const analyticsAdapterName = 'adloox'; @@ -24,7 +24,7 @@ describe('Adloox RTD Provider', function () { }, mediaTypes: { banner: { - sizes: [ [300, 250] ] + sizes: [[300, 250]] } }, bids: [ @@ -101,7 +101,7 @@ describe('Adloox RTD Provider', function () { }); it('should reject non-array of integers with value greater than zero config.params.thresholds', function (done) { - const ret = rtdProvider.init({ params: { thresholds: [ 70, null ] } }); + const ret = rtdProvider.init({ params: { thresholds: [70, null] } }); expect(ret).is.false; @@ -149,7 +149,7 @@ describe('Adloox RTD Provider', function () { it('should fetch segments', function (done) { const req = { - adUnitCodes: [ adUnit.code ], + adUnitCodes: [adUnit.code], ortb2Fragments: { global: { site: { @@ -169,7 +169,7 @@ describe('Adloox RTD Provider', function () { }; const adUnitWithSegments = utils.deepClone(adUnit); const getGlobalStub = sinon.stub(prebidGlobal, 'getGlobal').returns({ - adUnits: [ adUnitWithSegments ] + adUnits: [adUnitWithSegments] }); const ret = rtdProvider.init(CONFIG); @@ -191,24 +191,24 @@ describe('Adloox RTD Provider', function () { rtdProvider.getBidRequestData(req, callback, CONFIG, null); const request = server.requests[0]; - const response = { unused: false, _: [ { d: 77 } ] }; + const response = { unused: false, _: [{ d: 77 }] }; request.respond(200, { 'content-type': 'application/json' }, JSON.stringify(response)); }); it('should set ad server targeting', function (done) { const adUnitWithSegments = utils.deepClone(adUnit); - utils.deepSetValue(adUnitWithSegments, 'ortb2Imp.ext.data.adloox_rtd.dis', [ 50, 60 ]); + utils.deepSetValue(adUnitWithSegments, 'ortb2Imp.ext.data.adloox_rtd.dis', [50, 60]); const getGlobalStub = sinon.stub(prebidGlobal, 'getGlobal').returns({ - adUnits: [ adUnitWithSegments ] + adUnits: [adUnitWithSegments] }); - const auction = { adUnits: [ adUnitWithSegments ] }; + const auction = { adUnits: [adUnitWithSegments] }; const getAuctionStub = sinon.stub(auctionManager.index, 'getAuction').returns({ - adUnits: [ adUnitWithSegments ], + adUnits: [adUnitWithSegments], getFPD: () => { return { global: { site: { ext: { data: { adloox_rtd: { ok: true } } } } } } } }); - const targetingData = rtdProvider.getTargetingData([ adUnitWithSegments.code ], CONFIG, null, auction); + const targetingData = rtdProvider.getTargetingData([adUnitWithSegments.code], CONFIG, null, auction); expect(Object.keys(targetingData).length).is.equal(1); expect(Object.keys(targetingData[adUnit.code]).length).is.equal(2); expect(targetingData[adUnit.code].adl_ok).is.equal(1); diff --git a/test/spec/modules/admaticBidAdapter_spec.js b/test/spec/modules/admaticBidAdapter_spec.js index 1fce6a179be..a16fd665f9c 100644 --- a/test/spec/modules/admaticBidAdapter_spec.js +++ b/test/spec/modules/admaticBidAdapter_spec.js @@ -7,7 +7,7 @@ const ENDPOINT = 'https://layer.rtb.admatic.com.tr/pb'; describe('admaticBidAdapter', () => { const adapter = newBidder(spec); - const validRequest = [ { + const validRequest = [{ 'refererInfo': { 'page': 'https://www.admatic.com.tr', 'domain': 'https://www.admatic.com.tr', @@ -241,7 +241,7 @@ describe('admaticBidAdapter', () => { 'cur': 'USD', 'bidder': 'admatic' } - } ]; + }]; const bidderRequest = { 'refererInfo': { 'page': 'https://www.admatic.com.tr', @@ -795,64 +795,66 @@ describe('admaticBidAdapter', () => { describe('interpretResponse', function () { it('should get correct bid responses', function() { - const bids = { body: { - data: [ - { - 'id': 1, - 'creative_id': '374', - 'width': 300, - 'height': 250, - 'price': 0.01, - 'type': 'banner', - 'bidder': 'admatic', - 'currency': 'TRY', - 'mime_type': { - 'name': 'backfill', - 'force': false - }, - 'adomain': ['admatic.com.tr'], - 'party_tag': '
', - 'iurl': 'https://www.admatic.com.tr' - }, - { - 'id': 2, - 'creative_id': '3741', - 'width': 300, - 'height': 250, - 'price': 0.01, - 'currency': 'TRY', - 'type': 'video', - 'mime_type': { - 'name': 'backfill', - 'force': false + const bids = { + body: { + data: [ + { + 'id': 1, + 'creative_id': '374', + 'width': 300, + 'height': 250, + 'price': 0.01, + 'type': 'banner', + 'bidder': 'admatic', + 'currency': 'TRY', + 'mime_type': { + 'name': 'backfill', + 'force': false + }, + 'adomain': ['admatic.com.tr'], + 'party_tag': '
', + 'iurl': 'https://www.admatic.com.tr' }, - 'bidder': 'admatic', - 'adomain': ['admatic.com.tr'], - 'party_tag': '', - 'iurl': 'https://www.admatic.com.tr' - }, - { - 'id': 3, - 'creative_id': '3742', - 'width': 1, - 'height': 1, - 'price': 0.01, - 'currency': 'TRY', - 'type': 'native', - 'mime_type': { - 'name': 'backfill', - 'force': false + { + 'id': 2, + 'creative_id': '3741', + 'width': 300, + 'height': 250, + 'price': 0.01, + 'currency': 'TRY', + 'type': 'video', + 'mime_type': { + 'name': 'backfill', + 'force': false + }, + 'bidder': 'admatic', + 'adomain': ['admatic.com.tr'], + 'party_tag': '', + 'iurl': 'https://www.admatic.com.tr' }, - 'bidder': 'admatic', - 'adomain': ['admatic.com.tr'], - 'party_tag': '{"native":{"ver":"1.1","assets":[{"id":1,"title":{"text":"title"}},{"id":4,"data":{"value":"body"}},{"id":5,"data":{"value":"sponsored"}},{"id":6,"data":{"value":"cta"}},{"id":2,"img":{"url":"https://www.admatic.com.tr","w":1200,"h":628}},{"id":3,"img":{"url":"https://www.admatic.com.tr","w":640,"h":480}}],"link":{"url":"https://www.admatic.com.tr"},"imptrackers":["https://www.admatic.com.tr"]}}', - 'iurl': 'https://www.admatic.com.tr' - } - ], - 'cur': 'TRY', - 'queryId': 'cdnbh24rlv0hhkpfpln0', - 'status': true - }}; + { + 'id': 3, + 'creative_id': '3742', + 'width': 1, + 'height': 1, + 'price': 0.01, + 'currency': 'TRY', + 'type': 'native', + 'mime_type': { + 'name': 'backfill', + 'force': false + }, + 'bidder': 'admatic', + 'adomain': ['admatic.com.tr'], + 'party_tag': '{"native":{"ver":"1.1","assets":[{"id":1,"title":{"text":"title"}},{"id":4,"data":{"value":"body"}},{"id":5,"data":{"value":"sponsored"}},{"id":6,"data":{"value":"cta"}},{"id":2,"img":{"url":"https://www.admatic.com.tr","w":1200,"h":628}},{"id":3,"img":{"url":"https://www.admatic.com.tr","w":640,"h":480}}],"link":{"url":"https://www.admatic.com.tr"},"imptrackers":["https://www.admatic.com.tr"]}}', + 'iurl': 'https://www.admatic.com.tr' + } + ], + 'cur': 'TRY', + 'queryId': 'cdnbh24rlv0hhkpfpln0', + 'status': true + } + }; const expectedResponse = [ { @@ -1136,7 +1138,7 @@ describe('admaticBidAdapter', () => { ] }; - const result = spec.interpretResponse(bids, {data: request}); + const result = spec.interpretResponse(bids, { data: request }); expect(result).to.eql(expectedResponse); }); @@ -1147,14 +1149,16 @@ describe('admaticBidAdapter', () => { 'type': 'admatic' } }; - const bids = { body: { - data: [], - 'queryId': 'cdnbh24rlv0hhkpfpln0', - 'status': true, - 'cur': 'TRY' - }}; + const bids = { + body: { + data: [], + 'queryId': 'cdnbh24rlv0hhkpfpln0', + 'status': true, + 'cur': 'TRY' + } + }; - const result = spec.interpretResponse(bids, {data: request}); + const result = spec.interpretResponse(bids, { data: request }); expect(result.length).to.equal(0); }); }); diff --git a/test/spec/modules/admediaBidAdapter_spec.js b/test/spec/modules/admediaBidAdapter_spec.js index 53ccade28e2..2774e604f8a 100644 --- a/test/spec/modules/admediaBidAdapter_spec.js +++ b/test/spec/modules/admediaBidAdapter_spec.js @@ -1,6 +1,6 @@ -import {assert, expect} from 'chai'; -import {spec} from 'modules/admediaBidAdapter.js'; -import {newBidder} from 'src/adapters/bidderFactory.js'; +import { assert, expect } from 'chai'; +import { spec } from 'modules/admediaBidAdapter.js'; +import { newBidder } from 'src/adapters/bidderFactory.js'; import * as utils from 'src/utils.js'; const ENDPOINT_URL = 'https://prebid.admedia.com/bidder/'; @@ -12,7 +12,7 @@ describe('admediaBidAdapter', function () { adUnitCode: 'adunit-code', bidder: 'admedia', bidId: 'g7ghhs78', - mediaTypes: {banner: {sizes: [[300, 250]]}}, + mediaTypes: { banner: { sizes: [[300, 250]] } }, params: { placementId: '782332', aid: '86858', @@ -31,7 +31,7 @@ describe('admediaBidAdapter', function () { adUnitCode: 'adunit-code', bidder: 'admedia', bidId: 'g7ghhs78', - mediaTypes: {banner: {sizes: [[300, 250]]}}, + mediaTypes: { banner: { sizes: [[300, 250]] } }, params: { placementId: '782332', aid: '86858' diff --git a/test/spec/modules/adminationBidAdapter_spec.js b/test/spec/modules/adminationBidAdapter_spec.js new file mode 100644 index 00000000000..b3401b3c0c4 --- /dev/null +++ b/test/spec/modules/adminationBidAdapter_spec.js @@ -0,0 +1,774 @@ +import { expect } from 'chai'; +import { + spec as adapter, + createDomain, + storage, +} from 'modules/adnimationBidAdapter'; +import * as utils from 'src/utils.js'; +import { version } from 'package.json'; +import { useFakeTimers } from 'sinon'; +import { BANNER, VIDEO } from '../../../src/mediaTypes.js'; +import { config } from '../../../src/config.js'; +import { + hashCode, + extractPID, + extractCID, + extractSubDomain, + getStorageItem, + setStorageItem, + tryParseJSON, + getUniqueDealId, +} from '../../../libraries/vidazooUtils/bidderUtils.js'; +import { getGlobal } from '../../../src/prebidGlobal.js'; + +export const TEST_ID_SYSTEMS = ['britepoolid', 'criteoId', 'id5id', 'idl_env', 'lipb', 'netId', 'parrableId', 'pubcid', 'tdid', 'pubProvidedId']; + +const SUB_DOMAIN = 'exchange'; + +const BID = { + 'bidId': '2d52001cabd527', + 'adUnitCode': 'div-gpt-ad-12345-0', + 'params': { + 'subDomain': SUB_DOMAIN, + 'cId': '59db6b3b4ffaa70004f45cdc', + 'pId': '59ac17c192832d0011283fe3', + 'bidFloor': 0.1, + 'ext': { + 'param1': 'loremipsum', + 'param2': 'dolorsitamet' + } + }, + 'placementCode': 'div-gpt-ad-1460505748561-0', + 'transactionId': 'c881914b-a3b5-4ecf-ad9c-1c2f37c6aabf', + 'sizes': [[300, 250], [300, 600]], + 'bidderRequestId': '1fdb5ff1b6eaa7', + 'auctionId': 'auction_id', + 'bidRequestsCount': 4, + 'bidderRequestsCount': 3, + 'bidderWinsCount': 1, + 'requestId': 'b0777d85-d061-450e-9bc7-260dd54bbb7a', + 'schain': 'a0819c69-005b-41ed-af06-1be1e0aefefc', + 'mediaTypes': [BANNER], + 'ortb2Imp': { + 'ext': { + 'gpid': '0123456789' + } + } +}; + +const VIDEO_BID = { + 'bidId': '2d52001cabd527', + 'adUnitCode': '63550ad1ff6642d368cba59dh5884270560', + 'bidderRequestId': '12a8ae9ada9c13', + 'transactionId': '56e184c6-bde9-497b-b9b9-cf47a61381ee', + 'auctionId': 'auction_id', + 'bidRequestsCount': 4, + 'bidderRequestsCount': 3, + 'bidderWinsCount': 1, + 'schain': 'a0819c69-005b-41ed-af06-1be1e0aefefc', + 'params': { + 'subDomain': SUB_DOMAIN, + 'cId': '635509f7ff6642d368cb9837', + 'pId': '59ac17c192832d0011283fe3', + 'bidFloor': 0.1 + }, + 'sizes': [[545, 307]], + 'mediaTypes': { + 'video': { + 'playerSize': [[545, 307]], + 'context': 'instream', + 'mimes': [ + 'video/mp4', + 'application/javascript' + ], + 'protocols': [2, 3, 5, 6], + 'maxduration': 60, + 'minduration': 0, + 'startdelay': 0, + 'linearity': 1, + 'api': [2], + 'placement': 1 + } + } +} + +const ORTB2_DEVICE = { + sua: { + 'source': 2, + 'platform': { + 'brand': 'Android', + 'version': ['8', '0', '0'] + }, + 'browsers': [ + { 'brand': 'Not_A Brand', 'version': ['99', '0', '0', '0'] }, + { 'brand': 'Google Chrome', 'version': ['109', '0', '5414', '119'] }, + { 'brand': 'Chromium', 'version': ['109', '0', '5414', '119'] } + ], + 'mobile': 1, + 'model': 'SM-G955U', + 'bitness': '64', + 'architecture': '' + }, + w: 980, + h: 1720, + dnt: 0, + ua: 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/125.0.6422.80 Mobile/15E148 Safari/604.1', + language: 'en', + devicetype: 1, + make: 'Apple', + model: 'iPhone 12 Pro Max', + os: 'iOS', + osv: '17.4', + ext: { fiftyonedegrees_deviceId: '17595-133085-133468-18092' }, +}; + +const BIDDER_REQUEST = { + 'gdprConsent': { + 'consentString': 'consent_string', + 'gdprApplies': true + }, + 'gppString': 'gpp_string', + 'gppSid': [7], + 'uspConsent': 'consent_string', + 'refererInfo': { + 'page': 'https://www.greatsite.com', + 'ref': 'https://www.somereferrer.com' + }, + 'ortb2': { + 'site': { + 'content': { + 'language': 'en' + } + }, + 'regs': { + 'gpp': 'gpp_string', + 'gpp_sid': [7], + 'coppa': 0 + }, + 'device': ORTB2_DEVICE, + } +}; + +const SERVER_RESPONSE = { + body: { + cid: 'testcid123', + results: [{ + 'ad': '', + 'price': 0.8, + 'creativeId': '12610997325162499419', + 'exp': 30, + 'width': 300, + 'height': 250, + 'advertiserDomains': ['securepubads.g.doubleclick.net'], + 'cookies': [{ + 'src': 'https://sync.com', + 'type': 'iframe' + }, { + 'src': 'https://sync.com', + 'type': 'img' + }] + }] + } +}; + +const VIDEO_SERVER_RESPONSE = { + body: { + 'cid': '635509f7ff6642d368cb9837', + 'results': [{ + 'ad': '', + 'advertiserDomains': ['adnimation.com'], + 'exp': 60, + 'width': 545, + 'height': 307, + 'mediaType': 'video', + 'creativeId': '12610997325162499419', + 'price': 2, + 'cookies': [] + }] + } +}; + +const ORTB2_OBJ = { + "device": ORTB2_DEVICE, + "regs": { "coppa": 0, "gpp": "gpp_string", "gpp_sid": [7] }, + "site": { "content": { "language": "en" } } +}; + +const REQUEST = { + data: { + width: 300, + height: 250, + bidId: '2d52001cabd527' + } +}; + +function getTopWindowQueryParams() { + try { + const parsedUrl = utils.parseUrl(window.top.document.URL, { decodeSearchAsString: true }); + return parsedUrl.search; + } catch (e) { + return ''; + } +} + +describe('adnimationBidAdapter', function () { + before(() => config.resetConfig()); + after(() => config.resetConfig()); + + describe('validate 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', function () { + expect(adapter.buildRequests).to.exist.and.to.be.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', function () { + expect(adapter.getUserSyncs).to.exist.and.to.be.a('function'); + }); + + it('exists and is a string', function () { + expect(adapter.code).to.exist.and.to.be.a('string'); + }); + + it('exists and contains media types', function () { + expect(adapter.supportedMediaTypes).to.exist.and.to.be.an('array').with.length(2); + expect(adapter.supportedMediaTypes).to.contain.members([BANNER, VIDEO]); + }); + }); + + describe('validate bid requests', function () { + it('should require cId', function () { + const isValid = adapter.isBidRequestValid({ + params: { + pId: 'pid' + } + }); + expect(isValid).to.be.false; + }); + + it('should require pId', function () { + const isValid = adapter.isBidRequestValid({ + params: { + cId: 'cid' + } + }); + expect(isValid).to.be.false; + }); + + it('should validate correctly', function () { + const isValid = adapter.isBidRequestValid({ + params: { + cId: 'cid', + pId: 'pid' + } + }); + expect(isValid).to.be.true; + }); + }); + + describe('build requests', function () { + let sandbox; + before(function () { + getGlobal().bidderSettings = { + adnimation: { + storageAllowed: true + } + }; + sandbox = sinon.createSandbox(); + sandbox.stub(Date, 'now').returns(1000); + }); + + it('should build video request', function () { + const hashUrl = hashCode(BIDDER_REQUEST.refererInfo.page); + config.setConfig({ + bidderTimeout: 3000 + }); + const requests = adapter.buildRequests([VIDEO_BID], BIDDER_REQUEST); + expect(requests).to.have.length(1); + expect(requests[0]).to.deep.equal({ + method: 'POST', + url: `${createDomain(SUB_DOMAIN)}/prebid/multi/635509f7ff6642d368cb9837`, + data: { + adUnitCode: '63550ad1ff6642d368cba59dh5884270560', + bidFloor: 0.1, + bidId: '2d52001cabd527', + bidderVersion: adapter.version, + bidderRequestId: '12a8ae9ada9c13', + cb: 1000, + gdpr: 1, + gdprConsent: 'consent_string', + usPrivacy: 'consent_string', + gppString: 'gpp_string', + gppSid: [7], + prebidVersion: version, + transactionId: '56e184c6-bde9-497b-b9b9-cf47a61381ee', + auctionId: 'auction_id', + bidRequestsCount: 4, + bidderRequestsCount: 3, + bidderWinsCount: 1, + bidderTimeout: 3000, + publisherId: '59ac17c192832d0011283fe3', + url: 'https%3A%2F%2Fwww.greatsite.com', + referrer: 'https://www.somereferrer.com', + res: `${window.top.screen.width}x${window.top.screen.height}`, + schain: VIDEO_BID.schain, + sizes: ['545x307'], + sua: { + 'source': 2, + 'platform': { + 'brand': 'Android', + 'version': ['8', '0', '0'] + }, + 'browsers': [ + { 'brand': 'Not_A Brand', 'version': ['99', '0', '0', '0'] }, + { 'brand': 'Google Chrome', 'version': ['109', '0', '5414', '119'] }, + { 'brand': 'Chromium', 'version': ['109', '0', '5414', '119'] } + ], + 'mobile': 1, + 'model': 'SM-G955U', + 'bitness': '64', + 'architecture': '' + }, + device: ORTB2_DEVICE, + uniqueDealId: `${hashUrl}_${Date.now().toString()}`, + uqs: getTopWindowQueryParams(), + mediaTypes: { + video: { + api: [2], + context: 'instream', + linearity: 1, + maxduration: 60, + mimes: [ + 'video/mp4', + 'application/javascript' + ], + minduration: 0, + placement: 1, + playerSize: [[545, 307]], + protocols: [2, 3, 5, 6], + startdelay: 0 + } + }, + gpid: '', + cat: [], + contentLang: 'en', + contentData: [], + isStorageAllowed: true, + pagecat: [], + ortb2: ORTB2_OBJ, + userData: [], + coppa: 0 + } + }); + }); + + it('should build banner request for each size', function () { + const hashUrl = hashCode(BIDDER_REQUEST.refererInfo.page); + config.setConfig({ + bidderTimeout: 3000 + }); + const requests = adapter.buildRequests([BID], BIDDER_REQUEST); + expect(requests).to.have.length(1); + expect(requests[0]).to.deep.equal({ + method: 'POST', + url: `${createDomain(SUB_DOMAIN)}/prebid/multi/59db6b3b4ffaa70004f45cdc`, + data: { + gdprConsent: 'consent_string', + gdpr: 1, + gppString: 'gpp_string', + gppSid: [7], + usPrivacy: 'consent_string', + transactionId: 'c881914b-a3b5-4ecf-ad9c-1c2f37c6aabf', + auctionId: 'auction_id', + bidRequestsCount: 4, + bidderRequestsCount: 3, + bidderWinsCount: 1, + bidderTimeout: 3000, + bidderRequestId: '1fdb5ff1b6eaa7', + sizes: ['300x250', '300x600'], + sua: { + 'source': 2, + 'platform': { + 'brand': 'Android', + 'version': ['8', '0', '0'] + }, + 'browsers': [ + { 'brand': 'Not_A Brand', 'version': ['99', '0', '0', '0'] }, + { 'brand': 'Google Chrome', 'version': ['109', '0', '5414', '119'] }, + { 'brand': 'Chromium', 'version': ['109', '0', '5414', '119'] } + ], + 'mobile': 1, + 'model': 'SM-G955U', + 'bitness': '64', + 'architecture': '' + }, + device: ORTB2_DEVICE, + url: 'https%3A%2F%2Fwww.greatsite.com', + referrer: 'https://www.somereferrer.com', + cb: 1000, + bidFloor: 0.1, + bidId: '2d52001cabd527', + adUnitCode: 'div-gpt-ad-12345-0', + publisherId: '59ac17c192832d0011283fe3', + uniqueDealId: `${hashUrl}_${Date.now().toString()}`, + bidderVersion: adapter.version, + prebidVersion: version, + schain: BID.schain, + res: `${window.top.screen.width}x${window.top.screen.height}`, + mediaTypes: [BANNER], + gpid: '0123456789', + uqs: getTopWindowQueryParams(), + 'ext.param1': 'loremipsum', + 'ext.param2': 'dolorsitamet', + cat: [], + contentLang: 'en', + contentData: [], + isStorageAllowed: true, + pagecat: [], + ortb2Imp: BID.ortb2Imp, + ortb2: ORTB2_OBJ, + userData: [], + coppa: 0 + } + }); + }); + + after(function () { + getGlobal().bidderSettings = {}; + sandbox.restore(); + }); + }); + describe('getUserSyncs', function () { + it('should have valid user sync with iframeEnabled', function () { + const result = adapter.getUserSyncs({ iframeEnabled: true }, [SERVER_RESPONSE]); + + expect(result).to.deep.equal([{ + type: 'iframe', + url: 'https://sync.adnimation.com/api/sync/iframe/?cid=testcid123&gdpr=0&gdpr_consent=&us_privacy=&coppa=0' + }]); + }); + + it('should have valid user sync with cid on response', function () { + const result = adapter.getUserSyncs({ iframeEnabled: true }, [SERVER_RESPONSE]); + expect(result).to.deep.equal([{ + type: 'iframe', + url: 'https://sync.adnimation.com/api/sync/iframe/?cid=testcid123&gdpr=0&gdpr_consent=&us_privacy=&coppa=0' + }]); + }); + + it('should have valid user sync with pixelEnabled', function () { + const result = adapter.getUserSyncs({ pixelEnabled: true }, [SERVER_RESPONSE]); + + expect(result).to.deep.equal([{ + 'url': 'https://sync.adnimation.com/api/sync/image/?cid=testcid123&gdpr=0&gdpr_consent=&us_privacy=&coppa=0', + 'type': 'image' + }]); + }); + + it('should have valid user sync with coppa on response', function () { + config.setConfig({ + coppa: 1 + }); + const result = adapter.getUserSyncs({ iframeEnabled: true }, [SERVER_RESPONSE]); + expect(result).to.deep.equal([{ + type: 'iframe', + url: 'https://sync.adnimation.com/api/sync/iframe/?cid=testcid123&gdpr=0&gdpr_consent=&us_privacy=&coppa=1' + }]); + }); + + it('should generate url with consent data', function () { + const gdprConsent = { + gdprApplies: true, + consentString: 'consent_string' + }; + const uspConsent = 'usp_string'; + const gppConsent = { + gppString: 'gpp_string', + applicableSections: [7] + } + + const result = adapter.getUserSyncs({ pixelEnabled: true }, [SERVER_RESPONSE], gdprConsent, uspConsent, gppConsent); + + expect(result).to.deep.equal([{ + 'url': 'https://sync.adnimation.com/api/sync/image/?cid=testcid123&gdpr=1&gdpr_consent=consent_string&us_privacy=usp_string&coppa=1&gpp=gpp_string&gpp_sid=7', + 'type': 'image' + }]); + }); + }); + + 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', function () { + const responses = adapter.interpretResponse({ price: 1, ad: '' }); + expect(responses).to.be.empty; + }); + + 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 banner responses', function () { + const responses = adapter.interpretResponse(SERVER_RESPONSE, REQUEST); + expect(responses).to.have.length(1); + expect(responses[0]).to.deep.equal({ + requestId: '2d52001cabd527', + cpm: 0.8, + width: 300, + height: 250, + creativeId: '12610997325162499419', + currency: 'USD', + netRevenue: true, + ttl: 30, + ad: '', + meta: { + advertiserDomains: ['securepubads.g.doubleclick.net'] + } + }); + }); + + it('should get meta from response metaData', function () { + const serverResponse = utils.deepClone(SERVER_RESPONSE); + serverResponse.body.results[0].metaData = { + advertiserDomains: ['adnimation.com'], + agencyName: 'Agency Name', + }; + const responses = adapter.interpretResponse(serverResponse, REQUEST); + expect(responses[0].meta).to.deep.equal({ + advertiserDomains: ['adnimation.com'], + agencyName: 'Agency Name' + }); + }); + + it('should return an array of interpreted video responses', function () { + const responses = adapter.interpretResponse(VIDEO_SERVER_RESPONSE, REQUEST); + expect(responses).to.have.length(1); + expect(responses[0]).to.deep.equal({ + requestId: '2d52001cabd527', + cpm: 2, + width: 545, + height: 307, + mediaType: 'video', + creativeId: '12610997325162499419', + currency: 'USD', + netRevenue: true, + ttl: 60, + vastXml: '', + meta: { + advertiserDomains: ['adnimation.com'] + } + }); + }); + + it('should take default TTL', function () { + const serverResponse = utils.deepClone(SERVER_RESPONSE); + delete serverResponse.body.results[0].exp; + const responses = adapter.interpretResponse(serverResponse, REQUEST); + expect(responses).to.have.length(1); + expect(responses[0].ttl).to.equal(300); + }); + }); + + describe('user id system', function () { + TEST_ID_SYSTEMS.forEach((idSystemProvider) => { + const id = Date.now().toString(); + const bid = utils.deepClone(BID); + + const userId = (function () { + switch (idSystemProvider) { + case 'lipb': + return { lipbid: id }; + case 'id5id': + return { uid: id }; + default: + return id; + } + })(); + + bid.userId = { + [idSystemProvider]: userId + }; + + it(`should include 'uid.${idSystemProvider}' in request params`, function () { + const requests = adapter.buildRequests([bid], BIDDER_REQUEST); + expect(requests[0].data[`uid.${idSystemProvider}`]).to.equal(id); + }); + }); + // testing bid.userIdAsEids handling + it("should include user ids from bid.userIdAsEids (length=1)", function() { + const bid = utils.deepClone(BID); + bid.userIdAsEids = [ + { + "source": "audigent.com", + "uids": [{ "id": "fakeidi6j6dlc6e" }] + } + ] + const requests = adapter.buildRequests([bid], BIDDER_REQUEST); + expect(requests[0].data['uid.audigent.com']).to.equal("fakeidi6j6dlc6e"); + }) + it("should include user ids from bid.userIdAsEids (length=2)", function() { + const bid = utils.deepClone(BID); + bid.userIdAsEids = [ + { + "source": "audigent.com", + "uids": [{ "id": "fakeidi6j6dlc6e" }] + }, + { + "source": "rwdcntrl.net", + "uids": [{ "id": "fakeid6f35197d5c", "atype": 1 }] + } + ] + const requests = adapter.buildRequests([bid], BIDDER_REQUEST); + expect(requests[0].data['uid.audigent.com']).to.equal("fakeidi6j6dlc6e"); + expect(requests[0].data['uid.rwdcntrl.net']).to.equal("fakeid6f35197d5c"); + }) + // testing user.ext.eid handling + it("should include user ids from user.ext.eid (length=1)", function() { + const bid = utils.deepClone(BID); + bid.user = { + ext: { + eids: [ + { + "source": "pubcid.org", + "uids": [{ "id": "fakeid8888dlc6e" }] + } + ] + } + } + const requests = adapter.buildRequests([bid], BIDDER_REQUEST); + expect(requests[0].data['uid.pubcid.org']).to.equal("fakeid8888dlc6e"); + }) + it("should include user ids from user.ext.eid (length=2)", function() { + const bid = utils.deepClone(BID); + bid.user = { + ext: { + eids: [ + { + "source": "pubcid.org", + "uids": [{ "id": "fakeid8888dlc6e" }] + }, + { + "source": "adserver.org", + "uids": [{ "id": "fakeid495ff1" }] + } + ] + } + } + const requests = adapter.buildRequests([bid], BIDDER_REQUEST); + expect(requests[0].data['uid.pubcid.org']).to.equal("fakeid8888dlc6e"); + expect(requests[0].data['uid.adserver.org']).to.equal("fakeid495ff1"); + }) + }); + + describe('alternate param names extractors', function () { + it('should return undefined when param not supported', function () { + const cid = extractCID({ 'c_id': '1' }); + const pid = extractPID({ 'p_id': '1' }); + const subDomain = extractSubDomain({ 'sub_domain': 'prebid' }); + expect(cid).to.be.undefined; + expect(pid).to.be.undefined; + expect(subDomain).to.be.undefined; + }); + + it('should return value when param supported', function () { + const cid = extractCID({ 'cID': '1' }); + const pid = extractPID({ 'Pid': '2' }); + const subDomain = extractSubDomain({ 'subDOMAIN': 'prebid' }); + expect(cid).to.be.equal('1'); + expect(pid).to.be.equal('2'); + expect(subDomain).to.be.equal('prebid'); + }); + }); + + describe('unique deal id', function () { + before(function () { + getGlobal().bidderSettings = { + adnimation: { + storageAllowed: true + } + }; + }); + after(function () { + getGlobal().bidderSettings = {}; + }); + const key = 'myKey'; + let uniqueDealId; + beforeEach(() => { + uniqueDealId = getUniqueDealId(storage, key, 0); + }) + + it('should get current unique deal id', function (done) { + // waiting some time so `now` will become past + setTimeout(() => { + const current = getUniqueDealId(storage, key); + expect(current).to.be.equal(uniqueDealId); + done(); + }, 200); + }); + + it('should get new unique deal id on expiration', function (done) { + setTimeout(() => { + const current = getUniqueDealId(storage, key, 100); + expect(current).to.not.be.equal(uniqueDealId); + done(); + }, 200) + }); + }); + + describe('storage utils', function () { + before(function () { + getGlobal().bidderSettings = { + adnimation: { + storageAllowed: true + } + }; + }); + after(function () { + getGlobal().bidderSettings = {}; + }); + it('should get value from storage with create param', function () { + const now = Date.now(); + const clock = useFakeTimers({ + shouldAdvanceTime: true, + now + }); + setStorageItem(storage, 'myKey', 2020); + const { value, created } = getStorageItem(storage, 'myKey'); + expect(created).to.be.equal(now); + expect(value).to.be.equal(2020); + expect(typeof value).to.be.equal('number'); + expect(typeof created).to.be.equal('number'); + clock.restore(); + }); + + it('should get external stored value', function () { + const value = 'superman' + window.localStorage.setItem('myExternalKey', value); + const item = getStorageItem(storage, 'myExternalKey'); + expect(item).to.be.equal(value); + }); + + it('should parse JSON value', function () { + const data = JSON.stringify({ event: 'send' }); + const { event } = tryParseJSON(data); + expect(event).to.be.equal('send'); + }); + + it('should get original value on parse fail', function () { + const value = 21; + const parsed = tryParseJSON(value); + expect(typeof parsed).to.be.equal('number'); + expect(parsed).to.be.equal(value); + }); + }); +}); diff --git a/test/spec/modules/admixerBidAdapter_spec.js b/test/spec/modules/admixerBidAdapter_spec.js index db8dbd0d203..644ce176867 100644 --- a/test/spec/modules/admixerBidAdapter_spec.js +++ b/test/spec/modules/admixerBidAdapter_spec.js @@ -1,7 +1,7 @@ -import {expect} from 'chai'; -import {spec} from 'modules/admixerBidAdapter.js'; -import {newBidder} from 'src/adapters/bidderFactory.js'; -import {config} from '../../../src/config.js'; +import { expect } from 'chai'; +import { spec } from 'modules/admixerBidAdapter.js'; +import { newBidder } from 'src/adapters/bidderFactory.js'; +import { config } from '../../../src/config.js'; const BIDDER_CODE = 'admixer'; const RTB_BIDDER_CODE = 'rtbstack' @@ -309,9 +309,9 @@ describe('AdmixerAdapter', function () { ]; it('Returns valid values', function () { - const userSyncAll = spec.getUserSyncs({pixelEnabled: true, iframeEnabled: true}, responses); - const userSyncImg = spec.getUserSyncs({pixelEnabled: true, iframeEnabled: false}, responses); - const userSyncFrm = spec.getUserSyncs({pixelEnabled: false, iframeEnabled: true}, responses); + const userSyncAll = spec.getUserSyncs({ pixelEnabled: true, iframeEnabled: true }, responses); + const userSyncImg = spec.getUserSyncs({ pixelEnabled: true, iframeEnabled: false }, responses); + const userSyncFrm = spec.getUserSyncs({ pixelEnabled: false, iframeEnabled: true }, responses); expect(userSyncAll).to.be.an('array').with.lengthOf(2); expect(userSyncImg).to.be.an('array').with.lengthOf(1); expect(userSyncImg[0].url).to.be.equal(imgUrl); diff --git a/test/spec/modules/admixerIdSystem_spec.js b/test/spec/modules/admixerIdSystem_spec.js index af3783558f9..9c251a46e43 100644 --- a/test/spec/modules/admixerIdSystem_spec.js +++ b/test/spec/modules/admixerIdSystem_spec.js @@ -1,9 +1,9 @@ -import {admixerIdSubmodule} from 'modules/admixerIdSystem.js'; +import { admixerIdSubmodule } from 'modules/admixerIdSystem.js'; import * as utils from 'src/utils.js'; -import {server} from 'test/mocks/xhr.js'; +import { server } from 'test/mocks/xhr.js'; const pid = '4D393FAC-B6BB-4E19-8396-0A4813607316'; -const getIdParams = {params: {pid: pid}}; +const getIdParams = { params: { pid: pid } }; describe('admixerId tests', function () { let logErrorStub; @@ -22,15 +22,15 @@ describe('admixerId tests', function () { admixerIdSubmodule.getId({}); expect(logErrorStub.callCount).to.be.equal(2); - admixerIdSubmodule.getId({params: {}}); + admixerIdSubmodule.getId({ params: {} }); expect(logErrorStub.callCount).to.be.equal(3); - admixerIdSubmodule.getId({params: {pid: 123}}); + admixerIdSubmodule.getId({ params: { pid: 123 } }); expect(logErrorStub.callCount).to.be.equal(4); }); it('should NOT call the admixer id endpoint if gdpr applies but consent string is missing', function () { - const submoduleCallback = admixerIdSubmodule.getId(getIdParams, {gdpr: { gdprApplies: true }}); + const submoduleCallback = admixerIdSubmodule.getId(getIdParams, { gdpr: { gdprApplies: true } }); expect(submoduleCallback).to.be.undefined; }); @@ -57,7 +57,7 @@ describe('admixerId tests', function () { request.respond( 200, {}, - JSON.stringify({setData: {visitorid: '571058d70bce453b80e6d98b4f8a81e3'}}) + JSON.stringify({ setData: { visitorid: '571058d70bce453b80e6d98b4f8a81e3' } }) ); expect(callBackSpy.calledOnce).to.be.true; expect(callBackSpy.args[0][0]).to.be.eq('571058d70bce453b80e6d98b4f8a81e3'); diff --git a/test/spec/modules/adnowBidAdapter_spec.js b/test/spec/modules/adnowBidAdapter_spec.js index a8013e3fa04..1048a453411 100644 --- a/test/spec/modules/adnowBidAdapter_spec.js +++ b/test/spec/modules/adnowBidAdapter_spec.js @@ -151,8 +151,8 @@ describe('adnowBidAdapter', function () { const noBidResponses = [ false, {}, - {body: false}, - {body: {}} + { body: false }, + { body: {} } ]; noBidResponses.forEach(response => { diff --git a/test/spec/modules/adnuntiusAnalyticsAdapter_spec.js b/test/spec/modules/adnuntiusAnalyticsAdapter_spec.js index 55cddbb0dd2..0fc8c700aff 100644 --- a/test/spec/modules/adnuntiusAnalyticsAdapter_spec.js +++ b/test/spec/modules/adnuntiusAnalyticsAdapter_spec.js @@ -2,7 +2,7 @@ import adnAnalyticsAdapter, { BID_WON_TIMEOUT } from 'modules/adnuntiusAnalytics import { AD_RENDER_FAILED_REASON, EVENTS, STATUS } from 'src/constants.js'; import { config } from 'src/config.js'; import { server } from 'test/mocks/xhr.js'; -import { setConfig } from 'modules/currency.js'; +import * as adUnits from 'src/utils/adUnits'; const events = require('src/events'); const utils = require('src/utils'); @@ -301,7 +301,7 @@ describe('Adnuntius analytics adapter', function () { } sandbox.stub(events, 'getEvents').returns([]); sandbox.stub(utils, 'timestamp').returns(1519149562416); - sandbox.stub(document, 'getElementById').returns(element); + sandbox.stub(adUnits, 'getAdUnitElement').returns(element); clock = sandbox.useFakeTimers(1519767013781); }); diff --git a/test/spec/modules/adnuntiusBidAdapter_spec.js b/test/spec/modules/adnuntiusBidAdapter_spec.js index 6c6f08b8750..9bedf6789e0 100644 --- a/test/spec/modules/adnuntiusBidAdapter_spec.js +++ b/test/spec/modules/adnuntiusBidAdapter_spec.js @@ -1,23 +1,28 @@ import '../../../src/prebid.js'; -import {expect} from 'chai'; -import {spec} from 'modules/adnuntiusBidAdapter.js'; -import {newBidder} from 'src/adapters/bidderFactory.js'; -import {config} from 'src/config.js'; +import { expect } from 'chai'; +import { spec } from 'modules/adnuntiusBidAdapter.js'; +import { newBidder } from 'src/adapters/bidderFactory.js'; +import { config } from 'src/config.js'; import * as utils from 'src/utils.js'; -import {deepClone, getUnixTimestampFromNow} from 'src/utils.js'; -import {getStorageManager} from 'src/storageManager.js'; -import {getGlobal} from '../../../src/prebidGlobal.js'; -import {getWinDimensions} from '../../../src/utils.js'; +import { deepClone, getUnixTimestampFromNow } from 'src/utils.js'; +import { getStorageManager } from 'src/storageManager.js'; +import { getGlobal } from '../../../src/prebidGlobal.js'; +import { getWinDimensions } from '../../../src/utils.js'; -import {getGlobalVarName} from '../../../src/buildOptions.js'; +import { getGlobalVarName } from '../../../src/buildOptions.js'; -describe('adnuntiusBidAdapter', function () { +describe('adnuntiusBidAdapter', function() { const sandbox = sinon.createSandbox(); const URL = 'https://ads.adnuntius.delivery/i?tzo='; const EURO_URL = 'https://europe.delivery.adnuntius.com/i?tzo='; const usi = utils.generateUUID() - const meta = [{ key: 'valueless' }, { value: 'keyless' }, { key: 'voidAuIds' }, { key: 'voidAuIds', value: [{ auId: '11118b6bc', exp: getUnixTimestampFromNow() }, { exp: getUnixTimestampFromNow(1) }] }, { key: 'valid-withnetwork', value: 'also-valid-network', network: 'the-network', exp: getUnixTimestampFromNow(1) }, { key: 'valid', value: 'also-valid', exp: getUnixTimestampFromNow(1) }, { key: 'expired', value: 'fwefew', exp: getUnixTimestampFromNow() }, { key: 'usi', value: 'should be skipped because timestamp', exp: getUnixTimestampFromNow(), network: 'adnuntius' }, { key: 'usi', value: usi, exp: getUnixTimestampFromNow(100), network: 'adnuntius' }, { key: 'usi', value: 'should be skipped because timestamp', exp: getUnixTimestampFromNow() }] + const meta = [{ key: 'valueless' }, { value: 'keyless' }, { key: 'voidAuIds' }, { key: 'voidAuIds', value: [{ auId: '11118b6bc', exp: getUnixTimestampFromNow() }, { exp: getUnixTimestampFromNow(1) }] }, { key: 'valid-withnetwork', value: 'also-valid-network', network: 'the-network', exp: getUnixTimestampFromNow(1) }, { key: 'valid', value: 'also-valid', exp: getUnixTimestampFromNow(1) }, { key: 'expired', value: 'fwefew', exp: getUnixTimestampFromNow() }, { + key: 'usi', + value: 'should be skipped because timestamp', + exp: getUnixTimestampFromNow(), + network: 'adnuntius' + }, { key: 'usi', value: usi, exp: getUnixTimestampFromNow(100), network: 'adnuntius' }, { key: 'usi', value: 'should be skipped because timestamp', exp: getUnixTimestampFromNow() }] let storage; before(() => { @@ -35,7 +40,7 @@ describe('adnuntiusBidAdapter', function () { resetExpectedUrls(); }); - afterEach(function () { + afterEach(function() { config.resetConfig(); config.setBidderConfig({ bidders: [] }); localStorage.removeItem('adn.metaData'); @@ -81,8 +86,8 @@ describe('adnuntiusBidAdapter', function () { const expectation = sortedExpectations[i]; const actual = sortedActuals[i]; - const expectationAsString = expectation[0] + ":" + expectation[1]; - const actualAsString = actual[0] + ":" + actual[1]; + const expectationAsString = expectation[0] + ':' + expectation[1]; + const actualAsString = actual[0] + ':' + actual[1]; expect(expectationAsString).to.equal(actualAsString); } expect(sortedExpectations.length).to.equal(sortedActuals.length); @@ -138,51 +143,55 @@ describe('adnuntiusBidAdapter', function () { } ]; - const legacyNativeBidderRequest = {bid: [ - { - bidId: 'adn-0000000000000551', - bidder: 'adnuntius', - params: { - auId: '0000000000000551', - network: 'adnuntius', - }, - mediaTypes: { - native: { - sizes: [[200, 200], [300, 300]], - image: { - required: true, - sizes: [250, 250] + const legacyNativeBidderRequest = { + bid: [ + { + bidId: 'adn-0000000000000551', + bidder: 'adnuntius', + params: { + auId: '0000000000000551', + network: 'adnuntius', + }, + mediaTypes: { + native: { + sizes: [[200, 200], [300, 300]], + image: { + required: true, + sizes: [250, 250] + } } } - } - }]}; + }] + }; - const nativeBidderRequest = {bid: [ - { - bidId: 'adn-0000000000000551', - bidder: 'adnuntius', - params: { - auId: '0000000000000551', - network: 'adnuntius', - }, - mediaTypes: { - native: { - sizes: [[200, 200], [300, 300]], - ortb: { - assets: [{ - id: 1, - required: 1, - img: { - type: 3, - w: 250, - h: 250, - } - }] + const nativeBidderRequest = { + bid: [ + { + bidId: 'adn-0000000000000551', + bidder: 'adnuntius', + params: { + auId: '0000000000000551', + network: 'adnuntius', + }, + mediaTypes: { + native: { + sizes: [[200, 200], [300, 300]], + ortb: { + assets: [{ + id: 1, + required: 1, + img: { + type: 3, + w: 250, + h: 250, + } + }] + } } - } - }, - } - ]}; + }, + } + ] + }; const multiBidAndFormatRequest = { bid: [{ @@ -365,7 +374,7 @@ describe('adnuntiusBidAdapter', function () { 'layoutId': 'buyers_network_image_layout_1', 'layoutName': 'Image', 'layoutExternalReference': '', - 'html': "\n\n\n \n \n \n\n\n
\n
\"\"/
\n
\n \n\n", + 'html': '\n\n\n \n \n \n\n\n
\n
\n
\n \n\n', 'renderTemplate': '' } ]; @@ -606,7 +615,7 @@ describe('adnuntiusBidAdapter', function () { { 'auId': '0000000000000551', 'targetId': 'adn-0000000000000551', - 'html': "\n\n\n \n \n \n\n\n
\n
\"\"/
\n
\n \n\n", + 'html': '\n\n\n \n \n \n\n\n
\n
\n
\n \n\n', 'matchedAdCount': 1, 'responseId': 'adn-rsp--229633088', 'deals': deals, @@ -870,20 +879,20 @@ describe('adnuntiusBidAdapter', function () { } } - describe('inherited functions', function () { - it('exists and is a function', function () { + 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 () { + describe('isBidRequestValid', function() { + it('should return true when required params found', function() { expect(spec.isBidRequestValid(bidderRequests[0])).to.equal(true); }); }); - describe('buildRequests', function () { - it('Test requests', function () { + describe('buildRequests', function() { + it('Test requests', function() { const winDimensions = getWinDimensions(); const viewport = winDimensions.innerWidth + 'x' + winDimensions.innerHeight; const prebidVersion = window[getGlobalVarName()].version; @@ -954,23 +963,23 @@ describe('adnuntiusBidAdapter', function () { })); }); - it('should pass for different end points in config', function () { + it('should pass for different end points in config', function() { config.setConfig({ env: 'localhost', protocol: 'http' }) - const request = config.runWithBidder('adnuntius', () => spec.buildRequests(bidderRequests, { })); + const request = config.runWithBidder('adnuntius', () => spec.buildRequests(bidderRequests, {})); expect(request.length).to.equal(1); expect(request[0]).to.have.property('url'); expectUrlsEqual(request[0].url, LOCALHOST_URL); }); - it('Test specifying deal IDs', function () { + it('Test specifying deal IDs', function() { const dealIdRequest = deepClone(bidderRequests); dealIdRequest[0].params.dealId = 'simplestringdeal'; dealIdRequest[0].params.inventory = { pmp: { - deals: [{id: '123', bidfloor: 12, bidfloorcur: 'USD'}] + deals: [{ id: '123', bidfloor: 12, bidfloorcur: 'USD' }] } }; let request = spec.buildRequests(dealIdRequest, {}); @@ -988,7 +997,7 @@ describe('adnuntiusBidAdapter', function () { expect(request[0].data).to.equal('{"adUnits":[{"auId":"000000000008b6bc","targetId":"123","dealId":[{"id":"123","bidfloor":12,"bidfloorcur":"USD"}],"maxDeals":1,"dimensions":[[640,480],[600,400]]},{"auId":"0000000000000551","targetId":"adn-0000000000000551","dimensions":[[1640,1480],[1600,1400]]}]}'); }); - it('Test requests with no local storage', function () { + it('Test requests with no local storage', function() { storage.setDataInLocalStorage('adn.metaData', JSON.stringify([{}])); const request = spec.buildRequests(bidderRequests, {}); expect(request.length).to.equal(1); @@ -1007,7 +1016,7 @@ describe('adnuntiusBidAdapter', function () { expectUrlsEqual(request2[0].url, ENDPOINT_URL_BASE); }); - it('Test request changes for voided au ids', function () { + it('Test request changes for voided au ids', function() { storage.setDataInLocalStorage('adn.metaData', JSON.stringify([{ key: 'voidAuIds', value: [{ auId: '11118b6bc', exp: getUnixTimestampFromNow(1) }, { auId: '0000000000000023', exp: getUnixTimestampFromNow(1) }] }])); const bRequests = bidderRequests.concat([{ bidId: 'adn-11118b6bc', @@ -1059,7 +1068,7 @@ describe('adnuntiusBidAdapter', function () { expect(request[0].data).to.equal('{"adUnits":[{"auId":"000000000008b6bc","targetId":"123","maxDeals":1,"dimensions":[[640,480],[600,400]]},{"auId":"0000000000000551","targetId":"adn-0000000000000551","dimensions":[[1640,1480],[1600,1400]]},{"auId":"13","targetId":"adn-13","dimensions":[[164,140],[10,1400]]}]}'); }); - it('Test Video requests', function () { + it('Test Video requests', function() { const request = spec.buildRequests(videoBidderRequest, {}); expect(request.length).to.equal(1); @@ -1075,7 +1084,7 @@ describe('adnuntiusBidAdapter', function () { expectUrlsEqual(request[0].url, ENDPOINT_URL); }); - it('Test multiformat requests', function () { + it('Test multiformat requests', function() { const request = spec.buildRequests(multiBidderRequest, {}); expect(request.length).to.equal(1); expect(request.data) @@ -1092,7 +1101,7 @@ describe('adnuntiusBidAdapter', function () { expectUrlsEqual(request[0].url, ENDPOINT_URL); }); - it('should pass segments if available in config and merge from targeting', function () { + it('should pass segments if available in config and merge from targeting', function() { const ortb2 = { user: { data: [{ @@ -1124,7 +1133,8 @@ describe('adnuntiusBidAdapter', function () { }).length; } - it('should pass site data ext as key values to ad server', function () { + it('should pass site data ext as key values to ad server', function() { + delete bidderRequests[0].ortb2Imp; const ortb2 = { site: { ext: { @@ -1146,17 +1156,18 @@ describe('adnuntiusBidAdapter', function () { expect(request.length).to.equal(1); expect(request[0]).to.have.property('url') const data = JSON.parse(request[0].data); - expect(countMatches(data.adUnits[0].kv, {'9090': ['take it over']})).to.equal(1); - expect(countMatches(data.adUnits[0].kv, {'merge': ['this']})).to.equal(1); - expect(countMatches(data.adUnits[0].kv, {'9090': 'should-be-retained'})).to.equal(1); - expect(countMatches(data.adUnits[0].kv, {'45678': 'true'})).to.equal(1); - expect(countMatches(data.adUnits[0].kv, {'12345': 'true'})).to.equal(1); + expect(countMatches(data.adUnits[0].kv, { '9090': ['take it over'] })).to.equal(1); + expect(countMatches(data.adUnits[0].kv, { 'merge': ['this'] })).to.equal(1); + expect(countMatches(data.adUnits[0].kv, { '9090': 'should-be-retained' })).to.equal(1); + expect(countMatches(data.adUnits[0].kv, { '45678': 'true' })).to.equal(1); + expect(countMatches(data.adUnits[0].kv, { '12345': 'true' })).to.equal(1); expect(data.adUnits[0].kv.length).to.equal(5); delete bidderRequests[0].params.targeting; }); - it('should pass site.ext.data and user.ext.data as key values to ad server with targeting in different format', function () { + it('should pass site.ext.data and user.ext.data as key values to ad server with targeting in different format', function() { + delete bidderRequests[0].ortb2Imp; const ortb2 = { user: { ext: { @@ -1178,27 +1189,72 @@ describe('adnuntiusBidAdapter', function () { }; bidderRequests[0].params.targeting = { kv: [ - {'merge': ['this']}, - {'9090': ['take it over']} + { 'merge': ['this'] }, + { '9090': ['take it over'] } ] }; + bidderRequests[0].ortb2Imp = { + ext: { + data: { + 'fromImp': 'imp-value', + '9090': 'from-imp' + } + } + }; const request = config.runWithBidder('adnuntius', () => spec.buildRequests(bidderRequests, { ortb2 })); expect(request.length).to.equal(1); expect(request[0]).to.have.property('url') const data = JSON.parse(request[0].data); - expect(countMatches(data.adUnits[0].kv, {'from': 'user'})).to.equal(1); - expect(countMatches(data.adUnits[0].kv, {'9090': 'from-user'})).to.equal(1); - expect(countMatches(data.adUnits[0].kv, {'9090': ['take it over']})).to.equal(1); - expect(countMatches(data.adUnits[0].kv, {'merge': ['this']})).to.equal(1); - expect(countMatches(data.adUnits[0].kv, {'9090': 'should-be-retained'})).to.equal(1); - expect(countMatches(data.adUnits[0].kv, {'45678': 'true'})).to.equal(1); - expect(countMatches(data.adUnits[0].kv, {'12345': 'true'})).to.equal(1); - expect(data.adUnits[0].kv.length).to.equal(7); + expect(countMatches(data.adUnits[0].kv, { '9090': ['take it over'] })).to.equal(1); + expect(countMatches(data.adUnits[0].kv, { 'merge': ['this'] })).to.equal(1); + expect(countMatches(data.adUnits[0].kv, { '9090': 'should-be-retained' })).to.equal(1); + expect(countMatches(data.adUnits[0].kv, { '45678': 'true' })).to.equal(1); + expect(countMatches(data.adUnits[0].kv, { '12345': 'true' })).to.equal(1); + expect(countMatches(data.adUnits[0].kv, { '9090': 'from-user' })).to.equal(1); + expect(countMatches(data.adUnits[0].kv, { 'from': 'user' })).to.equal(1); + expect(countMatches(data.adUnits[0].kv, { '9090': 'from-imp' })).to.equal(1); + expect(countMatches(data.adUnits[0].kv, { 'fromImp': 'imp-value' })).to.equal(1); + expect(data.adUnits[0].kv.length).to.equal(9); delete bidderRequests[0].params.targeting; + delete bidderRequests[0].ortb2Imp; }); - it('should pass site data ext as key values to ad server even if no kv targeting specified in params.targeting', function () { + it('should pass values from ortb2Imp.ext.data', function() { + delete bidderRequests[0].params.targeting; + bidderRequests[0].ortb2Imp = { + ext: { + data: { + 'arrayVal': ['a', 'b'], + 'anotherVal': ['c', { 'fred': 'said' }], + 'objectVal': { + 'nested': 'nope' + }, + 'stringVal': 'ok' + } + } + }; + + const request = config.runWithBidder('adnuntius', () => spec.buildRequests(bidderRequests, {})); + expect(request.length).to.equal(1); + expect(request[0]).to.have.property('url'); + const data = JSON.parse(request[0].data); + expect(countMatches(data.adUnits[0].kv, { 'arrayVal': ['a', 'b'] })).to.equal(1); + const anotherVal = (data.adUnits[0].kv.find(kv => kv.anotherVal) || {}).anotherVal; + expect(anotherVal).to.be.an('array').with.lengthOf(2); + expect(anotherVal[0]).to.equal('c'); + expect(anotherVal[1]).to.be.a('string'); + expect(anotherVal[1]).to.contain('fred'); + expect(anotherVal[1]).to.contain('said'); + expect(countMatches(data.adUnits[0].kv, { 'objectVal': { 'nested': 'nope' } })).to.equal(1); + expect(countMatches(data.adUnits[0].kv, { 'stringVal': 'ok' })).to.equal(1); + expect(data.adUnits[0].kv.length).to.equal(4); + + delete bidderRequests[0].ortb2Imp; + }); + + it('should pass site data ext as key values to ad server even if no kv targeting specified in params.targeting', function() { + delete bidderRequests[0].ortb2Imp; const ortb2 = { site: { ext: { @@ -1214,19 +1270,19 @@ describe('adnuntiusBidAdapter', function () { expect(request.length).to.equal(1); expect(request[0]).to.have.property('url') const data = JSON.parse(request[0].data); - expect(countMatches(data.adUnits[0].kv, {'9090': 'should-be-retained'})).to.equal(1); - expect(countMatches(data.adUnits[0].kv, {'45678': 'true'})).to.equal(1); - expect(countMatches(data.adUnits[0].kv, {'12345': 'true'})).to.equal(1); + expect(countMatches(data.adUnits[0].kv, { '9090': 'should-be-retained' })).to.equal(1); + expect(countMatches(data.adUnits[0].kv, { '45678': 'true' })).to.equal(1); + expect(countMatches(data.adUnits[0].kv, { '12345': 'true' })).to.equal(1); expect(data.adUnits[0].kv.length).to.equal(3); delete bidderRequests[0].params.targeting; }); - it('should skip passing site ext if missing', function () { + it('should skip passing site ext if missing', function() { + delete bidderRequests[0].ortb2Imp; const ortb2 = { site: { - ext: { - } + ext: {} } }; @@ -1238,7 +1294,8 @@ describe('adnuntiusBidAdapter', function () { expect(data.adUnits[0]).to.not.have.property('kv'); }); - it('should skip passing site ext data if missing', function () { + it('should skip passing site ext data if missing', function() { + delete bidderRequests[0].ortb2Imp; const ortb2 = { site: { ext: { @@ -1255,7 +1312,7 @@ describe('adnuntiusBidAdapter', function () { expect(data.adUnits[0]).to.not.have.property('kv'); }); - it('should skip segments in config if not either id or array of strings', function () { + it('should skip segments in config if not either id or array of strings', function() { const ortb2 = { user: { data: [{ @@ -1278,22 +1335,22 @@ describe('adnuntiusBidAdapter', function () { }); }); - describe('user privacy', function () { - it('should send GDPR Consent data if gdprApplies', function () { + describe('user privacy', function() { + it('should send GDPR Consent data if gdprApplies', function() { const request = spec.buildRequests(bidderRequests, { gdprConsent: { gdprApplies: true, consentString: 'consentString' } }); expect(request.length).to.equal(1); expect(request[0]).to.have.property('url'); expectUrlsEqual(request[0].url, ENDPOINT_URL_CONSENT); }); - it('should not send GDPR Consent data if gdprApplies equals undefined', function () { + it('should not send GDPR Consent data if gdprApplies equals undefined', function() { const request = spec.buildRequests(bidderRequests, { gdprConsent: { gdprApplies: undefined, consentString: 'consentString' } }); expect(request.length).to.equal(1); expect(request[0]).to.have.property('url'); expectUrlsEqual(request[0].url, ENDPOINT_URL); }); - it('should pass segments if available in config', function () { + it('should pass segments if available in config', function() { const ortb2 = { user: { data: [{ @@ -1313,7 +1370,7 @@ describe('adnuntiusBidAdapter', function () { expectUrlsEqual(request[0].url, ENDPOINT_URL_SEGMENTS); }); - it('should skip segments in config if not either id or array of strings', function () { + it('should skip segments in config if not either id or array of strings', function() { const ortb2 = { user: { data: [{ @@ -1335,7 +1392,7 @@ describe('adnuntiusBidAdapter', function () { expectUrlsEqual(request[0].url, ENDPOINT_URL_SEGMENTS); }); - it('should user user ID if present in ortb2.user.id field', function () { + it('should user user ID if present in ortb2.user.id field', function() { const ortb2 = { user: { id: usi @@ -1348,7 +1405,7 @@ describe('adnuntiusBidAdapter', function () { expectUrlsEqual(request[0].url, ENDPOINT_URL); }); - it('should user in user', function () { + it('should user in user', function() { config.setBidderConfig({ bidders: ['adnuntius'], }); @@ -1368,13 +1425,13 @@ describe('adnuntiusBidAdapter', function () { expect(request[0]).to.have.property('url'); expectUrlsEqual(request[0].url, `${ENDPOINT_URL_BASE}&userId=${usi}`); - const ortb2 = {user: {ext: {eids: [{source: 'a', uids: [{id: '123', atype: 1}]}, {source: 'b', uids: [{id: '456', atype: 3, ext: {some: '1'}}]}]}}}; + const ortb2 = { user: { ext: { eids: [{ source: 'a', uids: [{ id: '123', atype: 1 }] }, { source: 'b', uids: [{ id: '456', atype: 3, ext: { some: '1' } }] }] } } }; request = config.runWithBidder('adnuntius', () => spec.buildRequests(req, { bids: req, ortb2: ortb2 })); expect(request.length).to.equal(1); expect(request[0]).to.have.property('url'); expectUrlsEqual(request[0].url, `${ENDPOINT_URL_BASE}&userId=${usi}&eids=%5B%7B%22source%22%3A%22a%22%2C%22uids%22%3A%5B%7B%22id%22%3A%22123%22%2C%22atype%22%3A1%7D%5D%7D%2C%7B%22source%22%3A%22b%22%2C%22uids%22%3A%5B%7B%22id%22%3A%22456%22%2C%22atype%22%3A3%2C%22ext%22%3A%7B%22some%22%3A%221%22%7D%7D%5D%7D%5D`); - ortb2.user.id = "ortb2userid" + ortb2.user.id = 'ortb2userid' request = config.runWithBidder('adnuntius', () => spec.buildRequests(req, { bids: req, ortb2: ortb2 })); expect(request.length).to.equal(1); expect(request[0]).to.have.property('url'); @@ -1386,7 +1443,7 @@ describe('adnuntiusBidAdapter', function () { expect(request[0]).to.have.property('url'); expectUrlsEqual(request[0].url, `${ENDPOINT_URL_BASE}&userId=different_user_id`); - const eids = [{source: 'pubcid', uids: [{id: '123', atype: 1}]}, {source: 'otherid', uids: [{id: '456', atype: 3, ext: {some: '1'}}]}]; + const eids = [{ source: 'pubcid', uids: [{ id: '123', atype: 1 }] }, { source: 'otherid', uids: [{ id: '456', atype: 3, ext: { some: '1' } }] }]; req[0].params.userId = 'different_user_id'; req[0].params.userIdAsEids = eids; request = config.runWithBidder('adnuntius', () => spec.buildRequests(req, { bids: req })); @@ -1400,7 +1457,7 @@ describe('adnuntiusBidAdapter', function () { expectUrlsEqual(request[0].url, `${ENDPOINT_URL_BASE}&userId=different_user_id&eids=` + encodeURIComponent(JSON.stringify(eids))); }); - it('should handle no user specified', function () { + it('should handle no user specified', function() { config.setBidderConfig({ bidders: ['adnuntius'], }); @@ -1421,15 +1478,15 @@ describe('adnuntiusBidAdapter', function () { }); }); - describe('user privacy', function () { - it('should send GDPR Consent data if gdprApplies', function () { + describe('user privacy', function() { + it('should send GDPR Consent data if gdprApplies', function() { const request = spec.buildRequests(bidderRequests, { gdprConsent: { gdprApplies: true, consentString: 'consentString' } }); expect(request.length).to.equal(1); expect(request[0]).to.have.property('url'); expectUrlsEqual(request[0].url, ENDPOINT_URL_CONSENT); }); - it('should not send GDPR Consent data if gdprApplies equals undefined', function () { + it('should not send GDPR Consent data if gdprApplies equals undefined', function() { const request = spec.buildRequests(bidderRequests, { gdprConsent: { gdprApplies: undefined, consentString: 'consentString' } }); expect(request.length).to.equal(1); expect(request[0]).to.have.property('url'); @@ -1437,8 +1494,8 @@ describe('adnuntiusBidAdapter', function () { }); }); - describe('use cookie', function () { - it('should send noCookie in url if set to false.', function () { + describe('use cookie', function() { + it('should send noCookie in url if set to false.', function() { config.setBidderConfig({ bidders: ['adnuntius'], config: { @@ -1454,8 +1511,8 @@ describe('adnuntiusBidAdapter', function () { }); }); - describe('validate auId', function () { - it('should fail when auId is not hexadecimal', function () { + describe('validate auId', function() { + it('should fail when auId is not hexadecimal', function() { const invalidRequest = { bidId: 'adn-000000000008b6bc', bidder: 'adnuntius', @@ -1467,7 +1524,7 @@ describe('adnuntiusBidAdapter', function () { expect(valid).to.equal(false); }); - it('should pass when auId is hexadecimal', function () { + it('should pass when auId is hexadecimal', function() { const invalidRequest = { bidId: 'adn-000000000008b6bc', bidder: 'adnuntius', @@ -1480,8 +1537,8 @@ describe('adnuntiusBidAdapter', function () { }); }); - describe('request deals', function () { - it('Should set max deals.', function () { + describe('request deals', function() { + it('Should set max deals.', function() { config.setBidderConfig({ bidders: ['adnuntius'] }); @@ -1498,7 +1555,7 @@ describe('adnuntiusBidAdapter', function () { expect(bidderRequests[1].params).to.not.have.property('maxBids'); expect(data.adUnits[1].maxDeals).to.equal(undefined); }); - it('Should allow a maximum of 5 deals.', function () { + it('Should allow a maximum of 5 deals.', function() { config.setBidderConfig({ bidders: ['adnuntius'], }); @@ -1521,7 +1578,7 @@ describe('adnuntiusBidAdapter', function () { expect(data.adUnits.length).to.equal(1); expect(data.adUnits[0].maxDeals).to.equal(5); }); - it('Should allow a minimum of 0 deals.', function () { + it('Should allow a minimum of 0 deals.', function() { config.setBidderConfig({ bidders: ['adnuntius'], }); @@ -1544,7 +1601,7 @@ describe('adnuntiusBidAdapter', function () { expect(data.adUnits.length).to.equal(1); expect(data.adUnits[0].maxDeals).to.equal(undefined); }); - it('Should set max deals using bidder config.', function () { + it('Should set max deals using bidder config.', function() { config.setBidderConfig({ bidders: ['adnuntius'], config: { @@ -1559,7 +1616,7 @@ describe('adnuntiusBidAdapter', function () { expect(request[0]).to.have.property('url') expectUrlsEqual(request[0].url, ENDPOINT_URL + '&ds=2'); }); - it('Should allow a maximum of 5 deals when using bidder config.', function () { + it('Should allow a maximum of 5 deals when using bidder config.', function() { config.setBidderConfig({ bidders: ['adnuntius'], config: { @@ -1572,7 +1629,7 @@ describe('adnuntiusBidAdapter', function () { expect(request[0]).to.have.property('url'); expectUrlsEqual(request[0].url, ENDPOINT_URL + '&ds=5'); }); - it('Should allow a minimum of 0 deals when using bidder config.', function () { + it('Should allow a minimum of 0 deals when using bidder config.', function() { config.setBidderConfig({ bidders: ['adnuntius'], config: { @@ -1588,8 +1645,8 @@ describe('adnuntiusBidAdapter', function () { }); }); - describe('interpretResponse', function () { - it('should return valid response when passed valid server response', function () { + describe('interpretResponse', function() { + it('should return valid response when passed valid server response', function() { config.setBidderConfig({ bidders: ['adnuntius'], config: { @@ -1664,7 +1721,7 @@ describe('adnuntiusBidAdapter', function () { expect(randomApiEntry.exp).to.be.greaterThan(getUnixTimestampFromNow(90)); }); - it('should return valid response when passed valid multiformat server response', function () { + it('should return valid response when passed valid multiformat server response', function() { config.setBidderConfig({ bidders: ['adnuntius'], config: { @@ -1710,7 +1767,7 @@ describe('adnuntiusBidAdapter', function () { expect(interpretedResponse[2].ttl).to.equal(360); }); - it('should not process valid response when passed alt bidder that is an adndeal', function () { + it('should not process valid response when passed alt bidder that is an adndeal', function() { const altBidder = { bid: [ { @@ -1728,7 +1785,7 @@ describe('adnuntiusBidAdapter', function () { serverResponse.body.adUnits[0].deals = deals; }); - it('should return valid response when passed alt bidder', function () { + it('should return valid response when passed alt bidder', function() { const altBidder = { bid: [ { @@ -1765,8 +1822,8 @@ describe('adnuntiusBidAdapter', function () { }); }); - describe('interpretVideoResponse', function () { - it('should return valid response when passed valid server response', function () { + describe('interpretVideoResponse', function() { + it('should return valid response when passed valid server response', function() { const interpretedResponse = spec.interpretResponse(serverVideoResponse, videoBidRequest); const ad = serverVideoResponse.body.adUnits[0].ads[0] const deal = serverVideoResponse.body.adUnits[0].deals[0] @@ -1802,8 +1859,8 @@ describe('adnuntiusBidAdapter', function () { }); }); - describe('Native ads handling', function () { - it('should pass requests on correctly', function () { + describe('Native ads handling', function() { + it('should pass requests on correctly', function() { const request = spec.buildRequests(nativeBidderRequest.bid, {}); expect(request.length).to.equal(1); expect(request[0]).to.have.property('bid'); @@ -1811,7 +1868,7 @@ describe('adnuntiusBidAdapter', function () { expect(request[0].data).to.equal('{"adUnits":[{"auId":"0000000000000551","targetId":"adn-0000000000000551","adType":"NATIVE","nativeRequest":{"ortb":{"assets":[{"id":1,"required":1,"img":{"type":3,"w":250,"h":250}}]}},"dimensions":[[200,200],[300,300]]}]}'); }); - it('should return valid response when passed valid server response', function () { + it('should return valid response when passed valid server response', function() { const interpretedResponse = spec.interpretResponse(nativeResponse, nativeBidderRequest); const ad = nativeResponse.body.adUnits[0].ads[0] expect(interpretedResponse).to.have.lengthOf(1); @@ -1830,7 +1887,7 @@ describe('adnuntiusBidAdapter', function () { expect(JSON.stringify(interpretedResponse[0].native.ortb)).to.equal('{"link":{"url":"https://whatever.com"},"assets":[{"id":1,"required":1,"img":{"url":"http://something.com/something.png"}}]}'); }); - it('should pass legacy requests on correctly', function () { + it('should pass legacy requests on correctly', function() { const request = spec.buildRequests(legacyNativeBidderRequest.bid, {}); expect(request.length).to.equal(1); expect(request[0]).to.have.property('bid'); @@ -1838,7 +1895,7 @@ describe('adnuntiusBidAdapter', function () { expect(request[0].data).to.equal('{"adUnits":[{"auId":"0000000000000551","targetId":"adn-0000000000000551","adType":"NATIVE","nativeRequest":{"ortb":{"ver":"1.2","assets":[{"id":0,"required":1,"img":{"type":3,"w":250,"h":250}}],"eventtrackers":[{"event":1,"methods":[1]},{"event":2,"methods":[1]}]}},"dimensions":[[200,200],[300,300]]}]}'); }); - it('should return valid legacy response when passed valid server response', function () { + it('should return valid legacy response when passed valid server response', function() { const interpretedResponse = spec.interpretResponse(nativeResponse, legacyNativeBidderRequest); const ad = nativeResponse.body.adUnits[0].ads[0] expect(interpretedResponse).to.have.lengthOf(1); diff --git a/test/spec/modules/adoceanBidAdapter_spec.js b/test/spec/modules/adoceanBidAdapter_spec.js index 53a82ec963d..4ab511a5a2d 100644 --- a/test/spec/modules/adoceanBidAdapter_spec.js +++ b/test/spec/modules/adoceanBidAdapter_spec.js @@ -36,7 +36,7 @@ describe('AdoceanAdapter', function () { }); it('should return false when required params are not passed', function () { - const invalidBid = Object.assign({}, bannerBid, {params: {masterId: 0}}); + const invalidBid = Object.assign({}, bannerBid, { params: { masterId: 0 } }); expect(spec.isBidRequestValid(invalidBid)).to.equal(false); }); @@ -63,38 +63,6 @@ describe('AdoceanAdapter', function () { expect(spec.isBidRequestValid(videoInscreenBid)).to.equal(true); }); - const videoAdpodBid = { - bidder: 'adocean', - params: { - masterId: 'tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7', - slaveId: 'adoceanmyaozpniqismex', - emitter: 'myao.adocean.pl' - }, - adUnitCode: 'adunit-code', - mediaTypes: { - video: { - context: 'adpod', - playerSize: [300, 250], - adPodDurationSec: 300, - durationRangeSec: [15, 30], - requireExactDuration: false - } - }, - bidId: '30b31c1838de1e', - bidderRequestId: '22edbae2733bf6', - auctionId: '1d1a030790a475', - }; - - it('should return true for adpod video without requireExactDuration', function () { - expect(spec.isBidRequestValid(videoAdpodBid)).to.equal(true); - }); - - it('should return false for adpod video with requireExactDuration', function () { - const invalidBid = Object.assign({}, videoAdpodBid); - invalidBid.mediaTypes.video.requireExactDuration = true; - expect(spec.isBidRequestValid(invalidBid)).to.equal(false); - }); - const videoOutstreamBid = { bidder: 'adocean', params: { @@ -243,41 +211,6 @@ describe('AdoceanAdapter', function () { expect(request.url).to.include('maxdur=60'); expect(request.url).to.include('mindur=10'); }); - - const videoAdpodBidRequests = [ - { - bidder: 'adocean', - params: { - masterId: 'tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7', - slaveId: 'adoceanmyaozpniqismex', - emitter: 'myao.adocean.pl' - }, - adUnitCode: 'adunit-code', - mediaTypes: { - video: { - playerSize: [200, 200], - context: 'adpod', - adPodDurationSec: 300, - durationRangeSec: [15, 30], - requireExactDuration: false - } - }, - bidId: '30b31c1838de1h', - bidderRequestId: '22edbae2733bf6', - auctionId: '1d1a030790a476', - } - ]; - - it('should build correct video adpod request', function () { - const request = spec.buildRequests(videoAdpodBidRequests, bidderRequest)[0]; - expect(request).to.exist; - expect(request.url).to.include('id=' + videoAdpodBidRequests[0].params.masterId); - expect(request.url).to.include('slaves=zpniqismex'); - expect(request.url).to.include('spots=20'); // 300 / 15 = 20 - expect(request.url).to.include('dur=300'); - expect(request.url).to.include('maxdur=30'); - expect(request.url).to.not.include('mindur='); - }); }); describe('interpretResponseBanner', function () { diff --git a/test/spec/modules/adpartnerBidAdapter_spec.js b/test/spec/modules/adpartnerBidAdapter_spec.js index fdc63bade6d..f13ef1fd782 100644 --- a/test/spec/modules/adpartnerBidAdapter_spec.js +++ b/test/spec/modules/adpartnerBidAdapter_spec.js @@ -1,6 +1,6 @@ -import {expect} from 'chai'; -import {spec, ENDPOINT_PROTOCOL, ENDPOINT_DOMAIN, ENDPOINT_PATH} from 'modules/adpartnerBidAdapter.js'; -import {newBidder} from 'src/adapters/bidderFactory.js'; +import { expect } from 'chai'; +import { spec, ENDPOINT_PROTOCOL, ENDPOINT_DOMAIN, ENDPOINT_PATH } from 'modules/adpartnerBidAdapter.js'; +import { newBidder } from 'src/adapters/bidderFactory.js'; import * as miUtils from 'libraries/mediaImpactUtils/index.js'; const BIDDER_CODE = 'adpartner'; @@ -144,9 +144,9 @@ describe('AdpartnerAdapter', function () { 'test.domain' ], 'syncs': [ - {'type': 'image', 'url': 'https://test.domain/tracker_1.gif'}, - {'type': 'image', 'url': 'https://test.domain/tracker_2.gif'}, - {'type': 'image', 'url': 'https://test.domain/tracker_3.gif'} + { 'type': 'image', 'url': 'https://test.domain/tracker_1.gif' }, + { 'type': 'image', 'url': 'https://test.domain/tracker_2.gif' }, + { 'type': 'image', 'url': 'https://test.domain/tracker_3.gif' } ], 'winNotification': [ { @@ -154,7 +154,7 @@ describe('AdpartnerAdapter', function () { 'path': '/hb/bid_won?test=1', 'data': { 'ad': [ - {'dsp': 8, 'id': 800008, 'cost': 1.0e-5, 'nurl': 'https://test.domain/'} + { 'dsp': 8, 'id': 800008, 'cost': 1.0e-5, 'nurl': 'https://test.domain/' } ], 'unit_id': 1234, 'site_id': 123 @@ -179,7 +179,7 @@ describe('AdpartnerAdapter', function () { expect(result[0].currency).to.equal('USD'); expect(result[0].ttl).to.equal(60); expect(result[0].meta.advertiserDomains).to.deep.equal(['test.domain']); - expect(result[0].winNotification[0]).to.deep.equal({'method': 'POST', 'path': '/hb/bid_won?test=1', 'data': {'ad': [{'dsp': 8, 'id': 800008, 'cost': 1.0e-5, 'nurl': 'https://test.domain/'}], 'unit_id': 1234, 'site_id': 123}}); + expect(result[0].winNotification[0]).to.deep.equal({ 'method': 'POST', 'path': '/hb/bid_won?test=1', 'data': { 'ad': [{ 'dsp': 8, 'id': 800008, 'cost': 1.0e-5, 'nurl': 'https://test.domain/' }], 'unit_id': 1234, 'site_id': 123 } }); }); }); @@ -227,7 +227,7 @@ describe('AdpartnerAdapter', function () { 'path': '/hb/bid_won?test=1', 'data': { 'ad': [ - {'dsp': 8, 'id': 800008, 'cost': 0.01, 'nurl': 'http://test.domain/'} + { 'dsp': 8, 'id': 800008, 'cost': 0.01, 'nurl': 'http://test.domain/' } ], 'unit_id': 1234, 'site_id': 123 @@ -268,9 +268,9 @@ describe('AdpartnerAdapter', function () { 'test.domain' ], 'syncs': [ - {'type': 'image', 'link': 'https://test.domain/tracker_1.gif'}, - {'type': 'image', 'link': 'https://test.domain/tracker_2.gif'}, - {'type': 'image', 'link': 'https://test.domain/tracker_3.gif'} + { 'type': 'image', 'link': 'https://test.domain/tracker_1.gif' }, + { 'type': 'image', 'link': 'https://test.domain/tracker_2.gif' }, + { 'type': 'image', 'link': 'https://test.domain/tracker_3.gif' } ], 'winNotification': [ { @@ -278,7 +278,7 @@ describe('AdpartnerAdapter', function () { 'path': '/hb/bid_won?test=1', 'data': { 'ad': [ - {'dsp': 8, 'id': 800008, 'cost': 1.0e-5, 'nurl': 'https://test.domain/'} + { 'dsp': 8, 'id': 800008, 'cost': 1.0e-5, 'nurl': 'https://test.domain/' } ], 'unit_id': 1234, 'site_id': 123 diff --git a/test/spec/modules/adplusBidAdapter_spec.js b/test/spec/modules/adplusBidAdapter_spec.js index 5dc02ca37c5..2e60cf94357 100644 --- a/test/spec/modules/adplusBidAdapter_spec.js +++ b/test/spec/modules/adplusBidAdapter_spec.js @@ -1,6 +1,6 @@ -import {expect} from 'chai'; -import {ADPLUS_ENDPOINT, BIDDER_CODE, spec,} from 'modules/adplusBidAdapter.js'; -import {newBidder} from 'src/adapters/bidderFactory.js'; +import { expect } from 'chai'; +import { ADPLUS_ENDPOINT, BIDDER_CODE, spec, } from 'modules/adplusBidAdapter.js'; +import { newBidder } from 'src/adapters/bidderFactory.js'; const TEST_UID = 'test-uid-value'; diff --git a/test/spec/modules/adplusIdSystem_spec.js b/test/spec/modules/adplusIdSystem_spec.js index 9a7d9458335..f13172dd729 100644 --- a/test/spec/modules/adplusIdSystem_spec.js +++ b/test/spec/modules/adplusIdSystem_spec.js @@ -4,7 +4,7 @@ import { ADPLUS_COOKIE_NAME, } from 'modules/adplusIdSystem.js'; import { server } from 'test/mocks/xhr.js'; -import {createEidsArray} from '../../../modules/userId/eids.js'; +import { createEidsArray } from '../../../modules/userId/eids.js'; const UID_VALUE = '191223.3413767593'; diff --git a/test/spec/modules/adpod_spec.js b/test/spec/modules/adpod_spec.js deleted file mode 100644 index 6e94d98b488..00000000000 --- a/test/spec/modules/adpod_spec.js +++ /dev/null @@ -1,1238 +0,0 @@ -import * as utils from 'src/utils.js'; -import { config } from 'src/config.js'; -import * as videoCache from 'src/videoCache.js'; -import * as auction from 'src/auction.js'; -import { ADPOD } from 'src/mediaTypes.js'; - -import { callPrebidCacheHook, checkAdUnitSetupHook, checkVideoBidSetupHook, adpodSetConfig, sortByPricePerSecond } from 'modules/adpod.js'; - -const 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 = []; - - const callbackFn = function() { - callbackResult = true; - }; - - const auctionInstance = { - getAuctionStatus: function() { - return auction.AUCTION_IN_PROGRESS; - } - } - - const fakeStoreFn = function(bids, callback) { - const 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); - }); - clock = sinon.useFakeTimers(); - config.setConfig({ - cache: { - url: 'https://test.cache.url/endpoint' - } - }); - }); - - afterEach(function() { - storeStub.restore(); - logWarnStub.restore(); - logInfoStub.restore(); - addBidToAuctionStub.restore(); - clock.restore(); - config.resetConfig(); - auctionBids = []; - }) - - it('should redirect back to the original function if bid is not an adpod video', function () { - const bid = { - adId: 'testAdId_123', - mediaType: 'video' - }; - - const videoMT = { - context: 'outstream' - }; - - callPrebidCacheHook(callbackFn, auctionInstance, bid, function () {}, videoMT); - 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 - } - }); - - const 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 - } - }; - - const 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 - } - }; - - const videoMT = { - context: ADPOD, - playerSize: [[300, 300]], - adPodDurationSec: 300, - durationRangeSec: [15, 30, 45], - requireExactDuration: false - }; - - callPrebidCacheHook(callbackFn, auctionInstance, bidResponse1, afterBidAddedSpy, videoMT); - callPrebidCacheHook(callbackFn, auctionInstance, bidResponse2, afterBidAddedSpy, videoMT); - - // 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 - } - }); - - const 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 - } - }; - const 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 - } - }; - const videoMT = { - context: ADPOD, - playerSize: [[300, 300]], - adPodDurationSec: 120, - durationRangeSec: [15, 30], - requireExactDuration: false - }; - - callPrebidCacheHook(callbackFn, auctionInstance, bidResponse1, afterBidAddedSpy, videoMT); - callPrebidCacheHook(callbackFn, auctionInstance, bidResponse2, afterBidAddedSpy, videoMT); - - 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 - } - }); - - const 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 - } - }; - const videoMT = { - context: ADPOD, - playerSize: [[300, 300]], - adPodDurationSec: 120, - durationRangeSec: [15, 30], - requireExactDuration: true - }; - - callPrebidCacheHook(callbackFn, auctionInstance, bidResponse, afterBidAddedSpy, videoMT); - 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 - } - }); - - const 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 - } - }; - const 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 - } - }; - const 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 - } - }; - - const videoMT = { - context: ADPOD, - playerSize: [[300, 300]], - adPodDurationSec: 45, - durationRangeSec: [15, 30], - requireExactDuration: false - }; - - callPrebidCacheHook(callbackFn, auctionInstance, bidResponse1, afterBidAddedSpy, videoMT); - callPrebidCacheHook(callbackFn, auctionInstance, bidResponse2, afterBidAddedSpy, videoMT); - callPrebidCacheHook(callbackFn, auctionInstance, bidResponse3, afterBidAddedSpy, videoMT); - 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 - } - }); - - const 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 - } - }; - const 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 - } - }; - - const videoMT = { - context: ADPOD, - playerSize: [[300, 300]], - adPodDurationSec: 45, - durationRangeSec: [15, 30], - requireExactDuration: false - }; - - callPrebidCacheHook(callbackFn, auctionInstance, bidResponse1, afterBidAddedSpy, videoMT); - callPrebidCacheHook(callbackFn, auctionInstance, bidResponse2, afterBidAddedSpy, videoMT); - - 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 - } - }); - - const bidResponse1 = { - adId: 'missingCat_ad1', - auctionId: 'missing_category_abc345', - mediaType: 'video', - cpm: 10, - meta: { - adServerCatId: undefined - }, - video: { - context: ADPOD, - durationSeconds: 15, - durationBucket: 15 - } - }; - - const videoMT = { - context: ADPOD, - playerSize: [[300, 300]], - adPodDurationSec: 45, - durationRangeSec: [15, 30], - requireExactDuration: false - }; - - callPrebidCacheHook(callbackFn, auctionInstance, bidResponse1, afterBidAddedSpy, videoMT); - - 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) { - const 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 - } - }); - - const 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 - } - }; - const 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 - } - }; - const videoMT = { - context: ADPOD, - playerSize: [[300, 300]], - adPodDurationSec: 120, - durationRangeSec: [15, 30, 45], - requireExactDuration: false - }; - - callPrebidCacheHook(callbackFn, auctionInstance, bidResponse1, afterBidAddedSpy, videoMT); - callPrebidCacheHook(callbackFn, auctionInstance, bidResponse2, afterBidAddedSpy, videoMT); - - 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) { - const payload = []; - const errmsg = 'invalid json'; - - callback(errmsg, payload); - }); - - config.setConfig({ - adpod: { - bidQueueSizeLimit: 2, - deferCaching: false, - brandCategoryExclusion: true - } - }); - - const bidResponse1 = { - adId: 'err_ad_1', - auctionId: 'error_xyz123', - mediaType: 'video', - cpm: 5, - meta: { - adServerCatId: 'tech' - }, - video: { - context: ADPOD, - durationSeconds: 30, - durationBucket: 30 - } - }; - const bidResponse2 = { - adId: 'err_ad_2', - auctionId: 'error_xyz123', - mediaType: 'video', - cpm: 5, - meta: { - adServerCatId: 'tech' - }, - video: { - context: ADPOD, - durationSeconds: 30, - durationBucket: 30 - } - }; - const videoMT = { - context: ADPOD, - playerSize: [[300, 300]], - adPodDurationSec: 120, - durationRangeSec: [15, 30, 45], - requireExactDuration: false - }; - - callPrebidCacheHook(callbackFn, auctionInstance, bidResponse1, afterBidAddedSpy, videoMT); - callPrebidCacheHook(callbackFn, auctionInstance, bidResponse2, afterBidAddedSpy, videoMT); - - 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 - 'max': 5, - 'increment': 0.01 // from $0 to $5, 1-cent increments - }, - { - 'precision': 2, - 'max': 8, - 'increment': 0.05 // from $5 to $8, round down to the previous 5-cent increment - }, - { - 'precision': 2, - 'max': 40, - 'increment': 0.5 // from $8 to $40, round down to the previous 50-cent increment - }] - }; - config.setConfig({ - priceGranularity: customConfigObject, - adpod: { - brandCategoryExclusion: true - } - }); - - const 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 - } - }; - - const videoMT = { - context: ADPOD, - playerSize: [[300, 300]], - adPodDurationSec: 45, - durationRangeSec: [15, 30], - requireExactDuration: false - }; - - callPrebidCacheHook(callbackFn, auctionInstance, bidResponse1, afterBidAddedSpy, videoMT); - - expect(callbackResult).to.be.null; - expect(afterBidAddedSpy.calledOnce).to.equal(true); - expect(storeStub.called).to.equal(false); - expect(auctionBids.length).to.equal(1); - }); - - it('should set deal tier in place of cpm when prioritzeDeals is true', function() { - config.setConfig({ - adpod: { - deferCaching: true, - brandCategoryExclusion: true, - prioritizeDeals: true, - dealTier: { - 'appnexus': { - 'prefix': 'tier', - 'minDealTier': 5 - } - } - } - }); - - const bidResponse1 = { - adId: 'adId01277', - auctionId: 'no_defer_123', - mediaType: 'video', - bidderCode: 'appnexus', - cpm: 5, - pbMg: '5.00', - adserverTargeting: { - hb_pb: '5.00' - }, - meta: { - adServerCatId: 'test' - }, - video: { - context: ADPOD, - durationSeconds: 15, - durationBucket: 15, - dealTier: 7 - } - }; - - const bidResponse2 = { - adId: 'adId46547', - auctionId: 'no_defer_123', - mediaType: 'video', - bidderCode: 'appnexus', - cpm: 12, - pbMg: '12.00', - adserverTargeting: { - hb_pb: '12.00' - }, - meta: { - adServerCatId: 'value' - }, - video: { - context: ADPOD, - durationSeconds: 15, - durationBucket: 15 - } - }; - - const videoMT = { - context: ADPOD, - playerSize: [[300, 300]], - adPodDurationSec: 300, - durationRangeSec: [15, 30, 45], - requireExactDuration: false - }; - - callPrebidCacheHook(callbackFn, auctionInstance, bidResponse1, afterBidAddedSpy, videoMT); - callPrebidCacheHook(callbackFn, auctionInstance, bidResponse2, afterBidAddedSpy, videoMT); - - expect(auctionBids[0].adserverTargeting.hb_pb_cat_dur).to.equal('tier7_test_15s'); - expect(auctionBids[1].adserverTargeting.hb_pb_cat_dur).to.equal('12.00_value_15s'); - }) - }); - - describe('checkAdUnitSetupHook', function () { - let results; - const 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() { - const 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() { - const 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() { - const 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() { - const 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: { - primaryCatId: 'testCategory_123' - }, - vastXml: 'test XML here' - }; - const adUnitNoExact = { - mediaTypes: { - video: { - context: ADPOD, - playerSize: [[300, 400]], - durationRangeSec: [15, 45], - requireExactDuration: false, - adPodDurationSec: 300 - } - } - }; - const adUnitWithExact = { - 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: 'https://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() { - const 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: 'https://test.cache.url/endpoint' - }, - adpod: { - brandCategoryExclusion: false - } - }); - - const goodBid = utils.deepClone(adpodTestBid); - goodBid.meta.primaryCatId = undefined; - checkVideoBidSetupHook(callbackFn, goodBid, adUnitNoExact, adUnitNoExact.mediaTypes.video, 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() { - const goodBid = utils.deepClone(adpodTestBid); - checkVideoBidSetupHook(callbackFn, goodBid, adUnitNoExact, adUnitNoExact.mediaTypes.video, 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, adUnitNoExact, adUnitNoExact.mediaTypes.video, ADPOD); - expect(callbackResult).to.be.null; - expect(bailResult).to.equal(false); - expect(logErrorStub.called).to.equal(shouldErrorBeLogged); - } - - const noCatBid = utils.deepClone(adpodTestBid); - noCatBid.meta.primaryCatId = undefined; - testInvalidAdpodBid(noCatBid, false); - - const noContextBid = utils.deepClone(adpodTestBid); - delete noContextBid.video.context; - testInvalidAdpodBid(noContextBid, false); - - const wrongContextBid = utils.deepClone(adpodTestBid); - wrongContextBid.video.context = 'instream'; - testInvalidAdpodBid(wrongContextBid, false); - - const noDurationBid = utils.deepClone(adpodTestBid); - delete noDurationBid.video.durationSeconds; - testInvalidAdpodBid(noDurationBid, false); - - config.resetConfig(); - const noCacheUrlBid = utils.deepClone(adpodTestBid); - testInvalidAdpodBid(noCacheUrlBid, true); - }); - - describe('checkBidDuration', function() { - const basicBid = { - video: { - context: ADPOD, - durationSeconds: 30 - }, - meta: { - primaryCatId: 'testCategory_123' - }, - vastXml: '' - }; - - it('when requireExactDuration is true', function() { - const goodBid = utils.deepClone(basicBid); - checkVideoBidSetupHook(callbackFn, goodBid, adUnitWithExact, adUnitWithExact.mediaTypes.video, ADPOD); - - expect(callbackResult).to.be.null; - expect(goodBid.video.durationBucket).to.equal(30); - expect(bailResult).to.equal(true); - expect(logWarnStub.called).to.equal(false); - - const badBid = utils.deepClone(basicBid); - badBid.video.durationSeconds = 14; - checkVideoBidSetupHook(callbackFn, badBid, adUnitWithExact, adUnitWithExact.mediaTypes.video, 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, adUnitNoExact, adUnitNoExact.mediaTypes.video, ADPOD); - expect(callbackResult).to.be.null; - expect(bid.video.durationBucket).to.equal(bucketValue); - expect(bailResult).to.equal(true); - expect(logWarnStub.called).to.equal(false); - } - - const goodBid45 = utils.deepClone(basicBid); - goodBid45.video.durationSeconds = 45; - testRoundingForGoodBId(goodBid45, 45); - - const goodBid30 = utils.deepClone(basicBid); - goodBid30.video.durationSeconds = 30; - testRoundingForGoodBId(goodBid30, 45); - - const goodBid10 = utils.deepClone(basicBid); - goodBid10.video.durationSeconds = 10; - testRoundingForGoodBId(goodBid10, 15); - - const goodBid16 = utils.deepClone(basicBid); - goodBid16.video.durationSeconds = 16; - testRoundingForGoodBId(goodBid16, 15); - - const 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, adUnitNoExact, adUnitNoExact.mediaTypes.video, ADPOD); - expect(callbackResult).to.be.null; - expect(bid.video.durationBucket).to.be.undefined; - expect(bailResult).to.equal(false); - expect(logWarnStub.called).to.equal(true); - } - - const badBid100 = utils.deepClone(basicBid); - badBid100.video.durationSeconds = 100; - testRoundingForBadBid(badBid100); - - const 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() { - const 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); - const 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 index f28eecdd6a8..ac8cd4a2fca 100644 --- a/test/spec/modules/adponeBidAdapter_spec.js +++ b/test/spec/modules/adponeBidAdapter_spec.js @@ -1,6 +1,6 @@ import { expect } from 'chai'; import { spec } from 'modules/adponeBidAdapter.js'; -import {newBidder} from 'src/adapters/bidderFactory.js'; +import { newBidder } from 'src/adapters/bidderFactory.js'; import * as utils from 'src/utils.js'; const EMPTY_ARRAY = []; @@ -66,7 +66,7 @@ describe('adponeBidAdapter', function () { it('should return false when necessary information is not found', function () { // empty bid - expect(spec.isBidRequestValid({bidId: '', params: {}})).to.be.false; + expect(spec.isBidRequestValid({ bidId: '', params: {} })).to.be.false; // empty bidId bid.bidId = ''; @@ -112,7 +112,7 @@ describe('adponeBidAdapter', function () { }); describe('interpretResponse', function () { let serverResponse; - const bidRequest = { data: {id: '1234'} }; + const bidRequest = { data: { id: '1234' } }; beforeEach(function () { serverResponse = { diff --git a/test/spec/modules/adprimeBidAdapter_spec.js b/test/spec/modules/adprimeBidAdapter_spec.js index a917d85d6ab..d49281d79c6 100644 --- a/test/spec/modules/adprimeBidAdapter_spec.js +++ b/test/spec/modules/adprimeBidAdapter_spec.js @@ -436,7 +436,7 @@ describe('AdprimeBidAdapter', function () { const syncData = spec.getUserSyncs({}, {}, { consentString: 'ALL', gdprApplies: true, - }, {}); + }, undefined); expect(syncData).to.be.an('array').which.is.not.empty; expect(syncData[0]).to.be.an('object') expect(syncData[0].type).to.be.a('string') @@ -445,9 +445,7 @@ describe('AdprimeBidAdapter', function () { expect(syncData[0].url).to.equal('https://sync.adprime.com/image?pbjs=1&gdpr=1&gdpr_consent=ALL&coppa=0') }); it('Should return array of objects with proper sync config , include CCPA', function() { - const syncData = spec.getUserSyncs({}, {}, {}, { - consentString: '1---' - }); + const syncData = spec.getUserSyncs({}, {}, {}, '1---'); expect(syncData).to.be.an('array').which.is.not.empty; expect(syncData[0]).to.be.an('object') expect(syncData[0].type).to.be.a('string') @@ -456,7 +454,7 @@ describe('AdprimeBidAdapter', function () { expect(syncData[0].url).to.equal('https://sync.adprime.com/image?pbjs=1&ccpa_consent=1---&coppa=0') }); it('Should return array of objects with proper sync config , include GPP', function() { - const syncData = spec.getUserSyncs({}, {}, {}, {}, { + const syncData = spec.getUserSyncs({}, {}, {}, undefined, { gppString: 'abc123', applicableSections: [8] }); diff --git a/test/spec/modules/adqueryBidAdapter_spec.js b/test/spec/modules/adqueryBidAdapter_spec.js index e101eaabdb2..b914bf0f5f8 100644 --- a/test/spec/modules/adqueryBidAdapter_spec.js +++ b/test/spec/modules/adqueryBidAdapter_spec.js @@ -45,7 +45,7 @@ describe('adqueryBidAdapter', function () { 'adDomains': ['https://example.com'], 'tag': ' ', 'adqLib': 'https://example.com/js/example.js', - 'mediaType': {'width': 300, 'height': 250, 'name': 'banner', 'type': 'banner300x250'}, + 'mediaType': { 'width': 300, 'height': 250, 'name': 'banner', 'type': 'banner300x250' }, 'cpm': 2.5, 'meta': { 'advertiserDomains': ['example.com'], @@ -431,7 +431,7 @@ describe('adqueryBidAdapter', function () { }) describe('buildRequests', function () { - const req = spec.buildRequests([ bidRequest ], { refererInfo: { } })[0] + const req = spec.buildRequests([bidRequest], { refererInfo: { } })[0] it('should return request object', function () { expect(req).to.not.be.null @@ -665,7 +665,7 @@ describe('adqueryBidAdapter', function () { } } } - ], {refererInfo: {}})[0] + ], { refererInfo: {} })[0] it('should include video', function () { expect(req_video.data.bidPageUrl).not.be.null @@ -734,7 +734,7 @@ describe('adqueryBidAdapter', function () { const req_video_for_floor = spec.buildRequests([ { "getFloor": function () { - return {currency: "USD", floor: 1.13}; + return { currency: "USD", floor: 1.13 }; }, "bidder": "adquery", "params": { @@ -921,7 +921,7 @@ describe('adqueryBidAdapter', function () { } } } - ], {refererInfo: {}})[0] + ], { refererInfo: {} })[0] it('data with floor must have video bidfloor property', function () { expect(req_video_for_floor.data.imp[0].video.bidfloor).eq(1.13); @@ -1078,7 +1078,7 @@ describe('adqueryBidAdapter', function () { expect(utils.triggerPixel.called).to.equal(true); }); it('should use nurl if exists', function () { - var response = spec.onBidWon({nurl: "https://example.com/test-nurl"}); + var response = spec.onBidWon({ nurl: "https://example.com/test-nurl" }); expect(response).to.be.an('undefined') expect(utils.triggerPixel.calledWith("https://example.com/test-nurl")).to.equal(true); }); diff --git a/test/spec/modules/adqueryIdSystem_spec.js b/test/spec/modules/adqueryIdSystem_spec.js index 9b7304d1984..ee02148162f 100644 --- a/test/spec/modules/adqueryIdSystem_spec.js +++ b/test/spec/modules/adqueryIdSystem_spec.js @@ -1,9 +1,9 @@ -import {adqueryIdSubmodule, storage} from 'modules/adqueryIdSystem.js'; -import {server} from 'test/mocks/xhr.js'; +import { adqueryIdSubmodule, storage } from 'modules/adqueryIdSystem.js'; +import { server } from 'test/mocks/xhr.js'; import sinon from 'sinon'; -import {attachIdSystem} from '../../../modules/userId/index.js'; -import {createEidsArray} from '../../../modules/userId/eids.js'; -import {expect} from 'chai/index.mjs'; +import { attachIdSystem } from '../../../modules/userId/index.js'; +import { createEidsArray } from '../../../modules/userId/eids.js'; +import { expect } from 'chai/index.mjs'; const config = { storage: { diff --git a/test/spec/modules/adrelevantisBidAdapter_spec.js b/test/spec/modules/adrelevantisBidAdapter_spec.js index eb7d81dbd5f..2c46e0ddff0 100644 --- a/test/spec/modules/adrelevantisBidAdapter_spec.js +++ b/test/spec/modules/adrelevantisBidAdapter_spec.js @@ -73,7 +73,7 @@ describe('AdrelevantisAdapter', function () { 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}]); + expect(payload.tags[0].private_sizes).to.deep.equal([{ width: 300, height: 250 }]); }); it('should add source and verison to the tag', function () { @@ -102,7 +102,7 @@ describe('AdrelevantisAdapter', function () { it('should populate the ad_types array on outstream requests', function () { const bidRequest = Object.assign({}, bidRequests[0]); bidRequest.mediaTypes = {}; - bidRequest.mediaTypes.video = {context: 'outstream'}; + bidRequest.mediaTypes.video = { context: 'outstream' }; const request = spec.buildRequests([bidRequest], {}); const payload = JSON.parse(request.data); @@ -226,12 +226,12 @@ describe('AdrelevantisAdapter', function () { site: { keywords: 'US Open', ext: { - data: {category: 'sports/tennis'} + data: { category: 'sports/tennis' } } } }; - const request = spec.buildRequests([bidRequest], {ortb2}); + const request = spec.buildRequests([bidRequest], { ortb2 }); const payload = JSON.parse(request.data); expect(payload.fpd.keywords).to.equal('US Open'); @@ -244,22 +244,22 @@ describe('AdrelevantisAdapter', function () { { mediaType: 'native', nativeParams: { - 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} + 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 } } } ); @@ -268,22 +268,22 @@ describe('AdrelevantisAdapter', function () { const payload = JSON.parse(request.data); expect(payload.tags[0].native.layouts[0]).to.deep.equal({ - 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}, + 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 }); expect(payload.tags[0].hb_source).to.equal(1); @@ -303,14 +303,14 @@ describe('AdrelevantisAdapter', function () { 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}]); + 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}]); + 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 () { @@ -327,7 +327,7 @@ describe('AdrelevantisAdapter', function () { singleValNum: 123, emptyStr: '', emptyArr: [''], - badValue: {'foo': 'bar'} // should be dropped + badValue: { 'foo': 'bar' } // should be dropped } } } @@ -556,7 +556,7 @@ describe('AdrelevantisAdapter', function () { adUnitCode: 'code' }] } - const result = spec.interpretResponse({ body: response }, {bidderRequest}); + const result = spec.interpretResponse({ body: response }, { bidderRequest }); expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); }); @@ -572,7 +572,7 @@ describe('AdrelevantisAdapter', function () { }; let bidderRequest; - const result = spec.interpretResponse({ body: response }, {bidderRequest}); + const result = spec.interpretResponse({ body: response }, { bidderRequest }); expect(result.length).to.equal(0); }); @@ -605,7 +605,7 @@ describe('AdrelevantisAdapter', function () { }] } - const result = spec.interpretResponse({ body: response }, {bidderRequest}); + const 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'); @@ -640,7 +640,7 @@ describe('AdrelevantisAdapter', function () { }] } - const result = spec.interpretResponse({ body: response }, {bidderRequest}); + const 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'); @@ -689,7 +689,7 @@ describe('AdrelevantisAdapter', function () { }] } - const result = spec.interpretResponse({ body: response1 }, {bidderRequest}); + const 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'); @@ -717,7 +717,7 @@ describe('AdrelevantisAdapter', function () { }] }; - const result = spec.interpretResponse({ body: outstreamResponse }, {bidderRequest}); + const result = spec.interpretResponse({ body: outstreamResponse }, { bidderRequest }); expect(result[0].renderer.config).to.deep.equal( bidderRequest.bids[0].renderer.options ); @@ -734,7 +734,7 @@ describe('AdrelevantisAdapter', function () { adUnitCode: 'code' }] } - const result = spec.interpretResponse({ body: responseWithDeal }, {bidderRequest}); + const result = spec.interpretResponse({ body: responseWithDeal }, { bidderRequest }); expect(Object.keys(result[0].adrelevantis)).to.include.members(['buyerMemberId', 'dealPriority', 'dealCode']); }); @@ -748,7 +748,7 @@ describe('AdrelevantisAdapter', function () { adUnitCode: 'code' }] } - const result = spec.interpretResponse({ body: responseAdvertiserId }, {bidderRequest}); + const result = spec.interpretResponse({ body: responseAdvertiserId }, { bidderRequest }); expect(Object.keys(result[0].meta)).to.include.members(['advertiserId']); }) }); diff --git a/test/spec/modules/adrinoBidAdapter_spec.js b/test/spec/modules/adrinoBidAdapter_spec.js index f86fe7126de..b97c0820ea1 100644 --- a/test/spec/modules/adrinoBidAdapter_spec.js +++ b/test/spec/modules/adrinoBidAdapter_spec.js @@ -1,6 +1,6 @@ import { expect } from 'chai'; import { spec } from 'modules/adrinoBidAdapter.js'; -import {config} from '../../../src/config.js'; +import { config } from '../../../src/config.js'; import * as utils from '../../../src/utils.js'; describe('adrinoBidAdapter', function () { @@ -67,8 +67,8 @@ describe('adrinoBidAdapter', function () { }, sizes: [[300, 250], [970, 250]], userIdAsEids: [ - {source: 'src1.org', uids: [{id: '1234', atype: 1}]}, - {source: 'src2.org', uids: [{id: '5678', atype: 1}]} + { source: 'src1.org', uids: [{ id: '1234', atype: 1 }] }, + { source: 'src2.org', uids: [{ id: '5678', atype: 1 }] } ], adUnitCode: 'adunit-code-2', bidId: '12345678901234', @@ -78,7 +78,7 @@ describe('adrinoBidAdapter', function () { it('should build the request correctly', function () { const result = spec.buildRequests( - [ bidRequest ], + [bidRequest], { refererInfo: { page: 'http://example.com/' } } ); expect(result.length).to.equal(1); @@ -95,11 +95,11 @@ describe('adrinoBidAdapter', function () { expect(result[0].data[0].eids).to.be.an('array').with.lengthOf(2); expect(result[0].data[0].eids).to.deep.include({ source: 'src1.org', - uids: [{id: '1234', atype: 1}] + uids: [{ id: '1234', atype: 1 }] }); expect(result[0].data[0].eids).to.deep.include({ source: 'src2.org', - uids: [{id: '5678', atype: 1}] + uids: [{ id: '5678', atype: 1 }] }); }); }); @@ -131,8 +131,8 @@ describe('adrinoBidAdapter', function () { } }, userIdAsEids: [ - {source: 'src1.org', uids: [{id: '1234', atype: 1}]}, - {source: 'src2.org', uids: [{id: '5678', atype: 1}]} + { source: 'src1.org', uids: [{ id: '1234', atype: 1 }] }, + { source: 'src2.org', uids: [{ id: '5678', atype: 1 }] } ], adUnitCode: 'adunit-code', bidId: '12345678901234', @@ -141,9 +141,9 @@ describe('adrinoBidAdapter', function () { }; it('should build the request correctly with custom domain', function () { - config.setConfig({adrino: { host: 'https://stg-prebid-bidder.adrino.io' }}); + config.setConfig({ adrino: { host: 'https://stg-prebid-bidder.adrino.io' } }); const result = spec.buildRequests( - [ bidRequest ], + [bidRequest], { refererInfo: { page: 'http://example.com/' } } ); expect(result.length).to.equal(1); @@ -160,17 +160,17 @@ describe('adrinoBidAdapter', function () { expect(result[0].data[0].eids).to.be.an('array').with.lengthOf(2); expect(result[0].data[0].eids).to.deep.include({ source: 'src1.org', - uids: [{id: '1234', atype: 1}] + uids: [{ id: '1234', atype: 1 }] }); expect(result[0].data[0].eids).to.deep.include({ source: 'src2.org', - uids: [{id: '5678', atype: 1}] + uids: [{ id: '5678', atype: 1 }] }); }); it('should build the request correctly with gdpr', function () { const result = spec.buildRequests( - [ bidRequest ], + [bidRequest], { gdprConsent: { gdprApplies: true, consentString: 'abc123' }, refererInfo: { page: 'http://example.com/' } } ); expect(result.length).to.equal(1); @@ -187,17 +187,17 @@ describe('adrinoBidAdapter', function () { expect(result[0].data[0].eids).to.be.an('array').with.lengthOf(2); expect(result[0].data[0].eids).to.deep.include({ source: 'src1.org', - uids: [{id: '1234', atype: 1}] + uids: [{ id: '1234', atype: 1 }] }); expect(result[0].data[0].eids).to.deep.include({ source: 'src2.org', - uids: [{id: '5678', atype: 1}] + uids: [{ id: '5678', atype: 1 }] }); }); it('should build the request correctly without gdpr', function () { const result = spec.buildRequests( - [ bidRequest ], + [bidRequest], { refererInfo: { page: 'http://example.com/' } } ); expect(result.length).to.equal(1); @@ -214,11 +214,11 @@ describe('adrinoBidAdapter', function () { expect(result[0].data[0].eids).to.be.an('array').with.lengthOf(2); expect(result[0].data[0].eids).to.deep.include({ source: 'src1.org', - uids: [{id: '1234', atype: 1}] + uids: [{ id: '1234', atype: 1 }] }); expect(result[0].data[0].eids).to.deep.include({ source: 'src2.org', - uids: [{id: '5678', atype: 1}] + uids: [{ id: '5678', atype: 1 }] }); }); }); diff --git a/test/spec/modules/adriverBidAdapter_spec.js b/test/spec/modules/adriverBidAdapter_spec.js index 75c786c07eb..15608a74d33 100644 --- a/test/spec/modules/adriverBidAdapter_spec.js +++ b/test/spec/modules/adriverBidAdapter_spec.js @@ -377,7 +377,7 @@ describe('adriverAdapter', function () { adUnitCode: 'code' }] }; - const result = spec.interpretResponse({ body: response }, {bidderRequest}); + const result = spec.interpretResponse({ body: response }, { bidderRequest }); expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); }); @@ -393,7 +393,7 @@ describe('adriverAdapter', function () { }; let bidderRequest; - const result = spec.interpretResponse({ body: response }, {bidderRequest}); + const result = spec.interpretResponse({ body: response }, { bidderRequest }); expect(result.length).to.equal(0); }); }); @@ -517,7 +517,7 @@ describe('adriverAdapter', function () { const bitRequestGetFloorBySized = JSON.parse(JSON.stringify(bidRequests)); - bitRequestGetFloorBySized[0].getFloor = (requestParams = {currency: 'USD', mediaType: '*', size: '*'}) => { + bitRequestGetFloorBySized[0].getFloor = (requestParams = { currency: 'USD', mediaType: '*', size: '*' }) => { if (requestParams.size.length === 2 && requestParams.size[0] === 300 && requestParams.size[1] === 250) { return { 'currency': 'RUB', diff --git a/test/spec/modules/ads_interactiveBidAdapter_spec.js b/test/spec/modules/ads_interactiveBidAdapter_spec.js index c16f5a5a7b5..c3d27008182 100644 --- a/test/spec/modules/ads_interactiveBidAdapter_spec.js +++ b/test/spec/modules/ads_interactiveBidAdapter_spec.js @@ -482,7 +482,7 @@ describe('AdsInteractiveBidAdapter', function () { const syncData = spec.getUserSyncs({}, {}, { consentString: 'ALL', gdprApplies: true, - }, {}); + }, undefined); expect(syncData).to.be.an('array').which.is.not.empty; expect(syncData[0]).to.be.an('object') expect(syncData[0].type).to.be.a('string') @@ -491,9 +491,7 @@ describe('AdsInteractiveBidAdapter', function () { expect(syncData[0].url).to.equal('https://cstb.adsinteractive.com/image?pbjs=1&gdpr=1&gdpr_consent=ALL&coppa=0') }); it('Should return array of objects with proper sync config , include CCPA', function() { - const syncData = spec.getUserSyncs({}, {}, {}, { - consentString: '1---' - }); + const syncData = spec.getUserSyncs({}, {}, {}, '1---'); expect(syncData).to.be.an('array').which.is.not.empty; expect(syncData[0]).to.be.an('object') expect(syncData[0].type).to.be.a('string') @@ -502,7 +500,7 @@ describe('AdsInteractiveBidAdapter', function () { expect(syncData[0].url).to.equal('https://cstb.adsinteractive.com/image?pbjs=1&ccpa_consent=1---&coppa=0') }); it('Should return array of objects with proper sync config , include GPP', function() { - const syncData = spec.getUserSyncs({}, {}, {}, {}, { + const syncData = spec.getUserSyncs({}, {}, {}, undefined, { gppString: 'abc123', applicableSections: [8] }); diff --git a/test/spec/modules/adstirBidAdapter_spec.js b/test/spec/modules/adstirBidAdapter_spec.js index d7f8efc3c88..2bfb46bc700 100644 --- a/test/spec/modules/adstirBidAdapter_spec.js +++ b/test/spec/modules/adstirBidAdapter_spec.js @@ -175,7 +175,7 @@ describe('AdstirAdapter', function () { }); it('ref.page, ref.tloc and ref.referrer correspond to refererInfo', function () { - const [ request ] = spec.buildRequests([validBidRequests[0]], { + const [request] = spec.buildRequests([validBidRequests[0]], { auctionId: 'b06c5141-fe8f-4cdf-9d7d-54415490a917', refererInfo: { page: null, @@ -279,7 +279,7 @@ describe('AdstirAdapter', function () { }; const serializedSchain = '1.0,1!exchange1.example,1234%21abcd,1,bid-request-1,publisher%2C%20Inc.,publisher.example!exchange2.example,abcd,1,bid-request-2,intermediary,intermediary.example'; - const [ request ] = spec.buildRequests([utils.mergeDeep(utils.deepClone(validBidRequests[0]), { ortb2: { source: { ext: { schain } } } })], bidderRequest); + const [request] = spec.buildRequests([utils.mergeDeep(utils.deepClone(validBidRequests[0]), { ortb2: { source: { ext: { schain } } } })], bidderRequest); const d = JSON.parse(request.data); expect(d.schain).to.deep.equal(serializedSchain); }); @@ -305,7 +305,7 @@ describe('AdstirAdapter', function () { }; const serializedSchain = '1.0,1!exchange1.example,1234%21abcd,1,,,!,,,,,!exchange2.example,abcd,1,,,'; - const [ request ] = spec.buildRequests([utils.mergeDeep(utils.deepClone(validBidRequests[0]), { ortb2: { source: { ext: { schain } } } })], bidderRequest); + const [request] = spec.buildRequests([utils.mergeDeep(utils.deepClone(validBidRequests[0]), { ortb2: { source: { ext: { schain } } } })], bidderRequest); const d = JSON.parse(request.data); expect(d.schain).to.deep.equal(serializedSchain); }); diff --git a/test/spec/modules/adtelligentBidAdapter_spec.js b/test/spec/modules/adtelligentBidAdapter_spec.js index bcce942f47f..d0366a68794 100644 --- a/test/spec/modules/adtelligentBidAdapter_spec.js +++ b/test/spec/modules/adtelligentBidAdapter_spec.js @@ -52,24 +52,6 @@ const VIDEO_REQUEST = { 'ortb2Imp': { 'ext': { 'gpid': '12345/adunit-code' } }, }; -const ADPOD_REQUEST = { - 'bidder': 'adtelligent', - 'mediaTypes': { - 'video': { - 'context': 'adpod', - 'playerSize': [[640, 480]], - 'anyField': 10 - } - }, - 'params': { - 'aid': 12345 - }, - 'bidderRequestId': '7101db09af0db2', - 'auctionId': '2e41f65424c87c', - 'adUnitCode': 'adunit-code', - 'bidId': '2e41f65424c87c' -}; - const SERVER_VIDEO_RESPONSE = { 'source': { 'aid': 12345, 'pubId': 54321 }, 'bids': [{ @@ -310,13 +292,6 @@ describe('adtelligentBidAdapter', () => { expect(displayRequest.every(comparator)).to.be.true; expect(videoAndDisplayRequests.every(comparator)).to.be.true; }); - it('forms correct ADPOD request', () => { - const pbBidReqData = spec.buildRequests([ADPOD_REQUEST], DEFAULT_ADATPER_REQ)[0].data; - const impRequest = pbBidReqData.BidRequests[0] - expect(impRequest.AdType).to.be.equal('video'); - expect(impRequest.Adpod).to.be.a('object'); - expect(impRequest.Adpod.anyField).to.be.equal(10); - }) it('sends correct video bid parameters', () => { const data = videoRequest[0].data; @@ -326,7 +301,10 @@ describe('adtelligentBidAdapter', () => { Aid: 12345, Sizes: '480x360,640x480', PlacementId: 'adunit-code', - GPID: '12345/adunit-code' + GPID: '12345/adunit-code', + DistanceToView: 0, + ElementHeight: 1, + PlacementPercentView: 0, }; expect(data.BidRequests[0]).to.deep.equal(eq); }); @@ -340,7 +318,10 @@ describe('adtelligentBidAdapter', () => { Aid: 12345, Sizes: '300x250', PlacementId: 'adunit-code', - GPID: '12345/adunit-code' + GPID: '12345/adunit-code', + DistanceToView: 0, + ElementHeight: 1, + PlacementPercentView: 0, }; expect(data.BidRequests[0]).to.deep.equal(eq); @@ -354,14 +335,20 @@ describe('adtelligentBidAdapter', () => { Aid: 12345, Sizes: '300x250', PlacementId: 'adunit-code', - GPID: '12345/adunit-code' + GPID: '12345/adunit-code', + DistanceToView: 0, + ElementHeight: 1, + PlacementPercentView: 0, }, { CallbackId: '84ab500420319d', AdType: 'video', Aid: 12345, Sizes: '480x360,640x480', PlacementId: 'adunit-code', - GPID: '12345/adunit-code' + GPID: '12345/adunit-code', + DistanceToView: 0, + ElementHeight: 1, + PlacementPercentView: 0, }] expect(bidRequests.BidRequests).to.deep.equal(expectedBidReqs); @@ -454,12 +441,6 @@ describe('adtelligentBidAdapter', () => { nobidServerResponseCheck(); }); - - it('forms correct ADPOD response', () => { - const videoBids = spec.interpretResponse({ body: SERVER_VIDEO_RESPONSE }, { adapterRequest: { bids: [ADPOD_REQUEST] } }); - expect(videoBids[0].video.durationSeconds).to.be.equal(30); - expect(videoBids[0].video.context).to.be.equal('adpod'); - }) describe('outstream setup', () => { const videoBids = spec.interpretResponse({ body: SERVER_OUSTREAM_VIDEO_RESPONSE }, { adapterRequest: outstreamVideoBidderRequest }); it('should return renderer with expected outstream params config', () => { diff --git a/test/spec/modules/adtrgtmeBidAdapter_spec.js b/test/spec/modules/adtrgtmeBidAdapter_spec.js index 702086c48a2..7b6e808dcaa 100644 --- a/test/spec/modules/adtrgtmeBidAdapter_spec.js +++ b/test/spec/modules/adtrgtmeBidAdapter_spec.js @@ -9,7 +9,7 @@ const DEFAULT_BANNER_URL = 'https://cdn.adtarget.me/libs/banner/300x250.jpg'; const BIDDER_VERSION = '1.0.7'; const PREBIDJS_VERSION = '$prebid.version$'; -const createBidRequest = ({bidId, adUnitCode, bidOverride, zid, ortb2}) => { +const createBidRequest = ({ bidId, adUnitCode, bidOverride, zid, ortb2 }) => { const bR = { auctionId: 'f3c594t-3o0ch1b0rm-ayn93c3o0ch1b0rm', adUnitCode, @@ -55,7 +55,7 @@ const createBidderRequest = (arr, code = 'default-code', ortb2 = {}) => { }; }; -const createRequestMock = ({bidId, adUnitCode, type, zid, bidOverride, pubIdMode, ortb2}) => { +const createRequestMock = ({ bidId, adUnitCode, type, zid, bidOverride, pubIdMode, ortb2 }) => { const bR = createBidRequest({ bidId: bidId || '84ab500420319d', adUnitCode: adUnitCode || '/1220291391/banner', @@ -101,10 +101,10 @@ const createResponseMock = (type) => { }] } }; - const { validBR, bidderRequest } = createRequestMock({type}); + const { validBR, bidderRequest } = createRequestMock({ type }); const data = spec.buildRequests(validBR, bidderRequest)[0].data; - return {sR, data, bidderRequest}; + return { sR, data, bidderRequest }; } describe('Adtrgtme Bid Adapter:', () => { @@ -163,8 +163,8 @@ describe('Adtrgtme Bid Adapter:', () => { expect(pixels.length).to.equal(2); expect(pixels).to.deep.equal( [ - {type: 'iframe', 'url': IFRAME_SYNC_ONE_URL}, - {type: 'iframe', 'url': IFRAME_SYNC_TWO_URL} + { type: 'iframe', 'url': IFRAME_SYNC_ONE_URL }, + { type: 'iframe', 'url': IFRAME_SYNC_TWO_URL } ] ) }); @@ -178,7 +178,7 @@ describe('Adtrgtme Bid Adapter:', () => { expect(pixels.length).to.equal(1); expect(pixels).to.deep.equal( [ - {type: 'image', 'url': IMAGE_SYNC_URL} + { type: 'image', 'url': IMAGE_SYNC_URL } ] ) }); @@ -192,9 +192,9 @@ describe('Adtrgtme Bid Adapter:', () => { expect(pixels.length).to.equal(3); expect(pixels).to.deep.equal( [ - {type: 'image', 'url': IMAGE_SYNC_URL}, - {type: 'iframe', 'url': IFRAME_SYNC_ONE_URL}, - {type: 'iframe', 'url': IFRAME_SYNC_TWO_URL} + { type: 'image', 'url': IMAGE_SYNC_URL }, + { type: 'iframe', 'url': IFRAME_SYNC_ONE_URL }, + { type: 'iframe', 'url': IFRAME_SYNC_TWO_URL } ] ) }); @@ -203,12 +203,12 @@ describe('Adtrgtme Bid Adapter:', () => { describe('Check if bid request is OK', () => { const BAD_VALUE = [ {}, - {params: {}}, - {params: {sid: 1220291391, zid: '1836455615'}}, - {params: {sid: '1220291391', zid: 'A'}}, - {params: {sid: '', zid: '1836455615'}}, - {params: {sid: '', zid: 'A'}}, - {params: {zid: ''}}, + { params: {} }, + { params: { sid: 1220291391, zid: '1836455615' } }, + { params: { sid: '1220291391', zid: 'A' } }, + { params: { sid: '', zid: '1836455615' } }, + { params: { sid: '', zid: 'A' } }, + { params: { zid: '' } }, ]; BAD_VALUE.forEach(value => { @@ -218,10 +218,10 @@ describe('Adtrgtme Bid Adapter:', () => { }); const OK_VALUE = [ - {params: {sid: '1220291391'}}, - {params: {sid: '1220291391', zid: 1836455615}}, - {params: {sid: '1220291391', zid: '1836455615'}}, - {params: {sid: '1220291391', zid: '1836455615A'}}, + { params: { sid: '1220291391' } }, + { params: { sid: '1220291391', zid: 1836455615 } }, + { params: { sid: '1220291391', zid: '1836455615' } }, + { params: { sid: '1220291391', zid: '1836455615A' } }, ]; OK_VALUE.forEach(value => { @@ -234,7 +234,7 @@ describe('Adtrgtme Bid Adapter:', () => { describe('Bidfloor support:', () => { it('should get bidfloor from getFloor method', () => { const { bidRequest, validBR, bidderRequest } = createRequestMock({}); - bidRequest.params.bidOverride = {cur: 'AUD'}; + bidRequest.params.bidOverride = { cur: 'AUD' }; bidRequest.getFloor = (floorObj) => { return { floor: bidRequest.floors.values[floorObj.mediaType + '|300x250'], @@ -277,11 +277,11 @@ describe('Adtrgtme Bid Adapter:', () => { }); describe('Check Site obj support (ortb2):', () => { - const BAD_ORTB2_TYPES = [ null, [], 123, 'invalidID', true, false, undefined ]; + const BAD_ORTB2_TYPES = [null, [], 123, 'invalidID', true, false, undefined]; BAD_ORTB2_TYPES.forEach(key => { it(`should remove bad site data: ${JSON.stringify(key)}`, () => { const ortb2 = { site: key } - const { validBR, bidderRequest } = createRequestMock({ortb2}); + const { validBR, bidderRequest } = createRequestMock({ ortb2 }); const data = spec.buildRequests(validBR, bidderRequest)[0].data; expect(data.site[key]).to.be.undefined; }); @@ -297,7 +297,7 @@ describe('Adtrgtme Bid Adapter:', () => { [key]: 'some value here' } }; - const { validBR, bidderRequest } = createRequestMock({ortb2}); + const { validBR, bidderRequest } = createRequestMock({ ortb2 }); const data = spec.buildRequests(validBR, bidderRequest)[0].data; expect(data.site[key]).to.exist; expect(data.site[key]).to.be.a('string'); @@ -312,7 +312,7 @@ describe('Adtrgtme Bid Adapter:', () => { [key]: ['some value here'] } }; - const { validBR, bidderRequest } = createRequestMock({ortb2}); + const { validBR, bidderRequest } = createRequestMock({ ortb2 }); const data = spec.buildRequests(validBR, bidderRequest)[0].data; expect(data.site[key]).to.exist; expect(data.site[key]).to.be.a('array'); @@ -330,7 +330,7 @@ describe('Adtrgtme Bid Adapter:', () => { } } }; - const { validBR, bidderRequest } = createRequestMock({ortb2}); + const { validBR, bidderRequest } = createRequestMock({ ortb2 }); const data = spec.buildRequests(validBR, bidderRequest)[0].data; expect(data.site.content[key]).to.exist; expect(data.site.content[key]).to.be.a('string'); @@ -348,7 +348,7 @@ describe('Adtrgtme Bid Adapter:', () => { } } }; - const { validBR, bidderRequest } = createRequestMock({ortb2}); + const { validBR, bidderRequest } = createRequestMock({ ortb2 }); const data = spec.buildRequests(validBR, bidderRequest)[0].data; expect(data.site.content[key]).to.be.a('array'); expect(data.site.content[key]).to.be.equal(ortb2.site.content[key]); @@ -357,11 +357,11 @@ describe('Adtrgtme Bid Adapter:', () => { }); describe('Check ortb2 user support:', () => { - const BAD_ORTB2_TYPES = [ null, [], 'unsupportedKeyName', true, false, undefined ]; + const BAD_ORTB2_TYPES = [null, [], 'unsupportedKeyName', true, false, undefined]; BAD_ORTB2_TYPES.forEach(key => { it(`should not allow bad site types to be added to bid request: ${JSON.stringify(key)}`, () => { const ortb2 = { user: key } - const { validBR, bidderRequest } = createRequestMock({ortb2}); + const { validBR, bidderRequest } = createRequestMock({ ortb2 }); const data = spec.buildRequests(validBR, bidderRequest)[0].data; expect(data.user[key]).to.be.undefined; }); @@ -375,7 +375,7 @@ describe('Adtrgtme Bid Adapter:', () => { [key]: 'some value here' } }; - const { validBR, bidderRequest } = createRequestMock({ortb2}); + const { validBR, bidderRequest } = createRequestMock({ ortb2 }); const data = spec.buildRequests(validBR, bidderRequest)[0].data; expect(data.user[key]).to.exist; expect(data.user[key]).to.be.a('string'); @@ -388,16 +388,16 @@ describe('Adtrgtme Bid Adapter:', () => { it(`should allow user ext to be added to the bid request: ${JSON.stringify(key)}`, () => { const ortb2 = { user: { - [key]: {a: '123', b: '456'} + [key]: { a: '123', b: '456' } } }; - const { validBR, bidderRequest } = createRequestMock({ortb2}); + const { validBR, bidderRequest } = createRequestMock({ ortb2 }); const data = spec.buildRequests(validBR, bidderRequest)[0].data; expect(data.user[key]).to.exist; expect(data.user[key]).to.be.a('object'); expect(data.user[key].a).to.be.equal('123'); expect(data.user[key].b).to.be.equal('456'); - config.setConfig({ortb2: {}}); + config.setConfig({ ortb2: {} }); }); }); @@ -495,7 +495,7 @@ describe('Adtrgtme Bid Adapter:', () => { it('should return a single request object for single request mode', () => { let { bidRequest, validBR, bidderRequest } = createRequestMock({}); - const { bidRequest: mock } = createRequestMock({bidId: '6heos7ks8z0j', zid: '98876543210', adUnitCode: 'bidder-code'}); + const { bidRequest: mock } = createRequestMock({ bidId: '6heos7ks8z0j', zid: '98876543210', adUnitCode: 'bidder-code' }); validBR = [bidRequest, mock]; bidderRequest.bids = validBR; @@ -562,7 +562,7 @@ describe('Adtrgtme Bid Adapter:', () => { }); expect(data.device).to.deep.equal({ - dnt: 0, + dnt: 0, // DNT deprecated by W3C; Prebid no longer supports DNT ua: navigator.userAgent, ip: undefined }); @@ -584,7 +584,7 @@ describe('Adtrgtme Bid Adapter:', () => { }); it('should use siteId value as site.id', () => { - const { validBR, bidderRequest } = createRequestMock({pubIdMode: true}); + const { validBR, bidderRequest } = createRequestMock({ pubIdMode: true }); validBR[0].params.sid = '9876543210'; const data = spec.buildRequests(validBR, bidderRequest).data; expect(data.site.id).to.equal('9876543210'); @@ -608,7 +608,7 @@ describe('Adtrgtme Bid Adapter:', () => { const data = spec.buildRequests(validBR, bidderRequest)[0].data; expect(data.imp[0].banner).to.deep.equal({ mimes: ['text/html', 'text/javascript', 'application/javascript', 'image/jpg'], - format: [{w: 300, h: 250}] + format: [{ w: 300, h: 250 }] }); }); @@ -620,7 +620,7 @@ describe('Adtrgtme Bid Adapter:', () => { const data = spec.buildRequests(validBR, bidderRequest)[0].data; expect(data.imp[0].banner).to.deep.equal({ mimes: ['text/html', 'text/javascript', 'application/javascript', 'image/jpg'], - format: [{w: 300, h: 250}] + format: [{ w: 300, h: 250 }] }); }); }); @@ -629,7 +629,7 @@ describe('Adtrgtme Bid Adapter:', () => { describe('for mediaTypes: "banner"', () => { it('should insert banner object into response[0].ad', () => { const { sR, bidderRequest } = createResponseMock('banner'); - const response = spec.interpretResponse(sR, {bidderRequest}); + const response = spec.interpretResponse(sR, { bidderRequest }); expect(response[0].ad).to.equal(` `); expect(response[0].mediaType).to.equal('banner'); @@ -639,7 +639,7 @@ describe('Adtrgtme Bid Adapter:', () => { describe('Support adomains', () => { it('should append bid-response adomain to meta.advertiserDomains', () => { const { sR, bidderRequest } = createResponseMock('banner'); - const response = spec.interpretResponse(sR, {bidderRequest}); + const response = spec.interpretResponse(sR, { bidderRequest }); expect(response[0].meta.advertiserDomains).to.be.a('array'); expect(response[0].meta.advertiserDomains[0]).to.equal('some-advertiser-domain.com'); }) @@ -650,7 +650,7 @@ describe('Adtrgtme Bid Adapter:', () => { const { sR, bidderRequest } = createResponseMock('banner'); const adId = 'bid-response-adId'; sR.body.seatbid[0].bid[0].adId = adId; - const response = spec.interpretResponse(sR, {bidderRequest}); + const response = spec.interpretResponse(sR, { bidderRequest }); expect(response[0].adId).to.equal(adId); }); @@ -658,7 +658,7 @@ describe('Adtrgtme Bid Adapter:', () => { const { sR, bidderRequest } = createResponseMock('banner'); const impid = 'y7v7iu0uljj94rbjcw9'; sR.body.seatbid[0].bid[0].impid = impid; - const response = spec.interpretResponse(sR, {bidderRequest}); + const response = spec.interpretResponse(sR, { bidderRequest }); expect(response[0].adId).to.equal(impid); }); @@ -667,7 +667,7 @@ describe('Adtrgtme Bid Adapter:', () => { const crid = 'passback-12579'; sR.body.seatbid[0].bid[0].impid = undefined; sR.body.seatbid[0].bid[0].crid = crid; - const response = spec.interpretResponse(sR, {bidderRequest}); + const response = spec.interpretResponse(sR, { bidderRequest }); expect(response[0].adId).to.equal(crid); }); }); @@ -680,14 +680,14 @@ describe('Adtrgtme Bid Adapter:', () => { config.setConfig({ adtrgtme: { ttl: key } }); - const response = spec.interpretResponse(sR, {bidderRequest}); + const response = spec.interpretResponse(sR, { bidderRequest }); expect(response[0].ttl).to.equal(300); }); it('should not set unsupported ttl formats and check default to 300', () => { const { sR, bidderRequest } = createResponseMock('banner'); bidderRequest.bids[0].params.ttl = key; - const response = spec.interpretResponse(sR, {bidderRequest}); + const response = spec.interpretResponse(sR, { bidderRequest }); expect(response[0].ttl).to.equal(300); }); }); @@ -699,14 +699,14 @@ describe('Adtrgtme Bid Adapter:', () => { config.setConfig({ adtrgtme: { ttl: key } }); - const response = spec.interpretResponse(sR, {bidderRequest}); + const response = spec.interpretResponse(sR, { bidderRequest }); expect(response[0].ttl).to.equal(300); }); it('should not set bad keys.ttl values', () => { const { sR, bidderRequest } = createResponseMock('banner'); bidderRequest.bids[0].params.ttl = key; - const response = spec.interpretResponse(sR, {bidderRequest}); + const response = spec.interpretResponse(sR, { bidderRequest }); expect(response[0].ttl).to.equal(300); }); }); @@ -717,7 +717,7 @@ describe('Adtrgtme Bid Adapter:', () => { adtrgtme: { ttl: 500 } }); bidderRequest.bids[0].params.ttl = 400; - const response = spec.interpretResponse(sR, {bidderRequest}); + const response = spec.interpretResponse(sR, { bidderRequest }); expect(response[0].ttl).to.equal(500); }); }); @@ -725,7 +725,7 @@ describe('Adtrgtme Bid Adapter:', () => { describe('Alias support', () => { it('should return undefined as the bidder code value', () => { const { sR, bidderRequest } = createResponseMock('banner'); - const response = spec.interpretResponse(sR, {bidderRequest}); + const response = spec.interpretResponse(sR, { bidderRequest }); expect(response[0].bidderCode).to.be.undefined; }); }); diff --git a/test/spec/modules/adtrueBidAdapter_spec.js b/test/spec/modules/adtrueBidAdapter_spec.js index 97e8e7cd966..cc7f20fb285 100644 --- a/test/spec/modules/adtrueBidAdapter_spec.js +++ b/test/spec/modules/adtrueBidAdapter_spec.js @@ -1,8 +1,8 @@ -import {expect} from 'chai' -import {spec} from 'modules/adtrueBidAdapter.js' -import {newBidder} from 'src/adapters/bidderFactory.js' +import { expect } from 'chai' +import { spec } from 'modules/adtrueBidAdapter.js' +import { newBidder } from 'src/adapters/bidderFactory.js' import * as utils from '../../../src/utils.js'; -import {config} from 'src/config.js'; +import { config } from 'src/config.js'; describe('AdTrueBidAdapter', function () { const adapter = newBidder(spec) @@ -489,14 +489,14 @@ describe('AdTrueBidAdapter', function () { sandbox.restore(); }); it('execute as per config', function () { - expect(spec.getUserSyncs({iframeEnabled: true}, [bidResponses], undefined, undefined)).to.deep.equal([{ + expect(spec.getUserSyncs({ iframeEnabled: true }, [bidResponses], undefined, undefined)).to.deep.equal([{ type: 'iframe', url: 'https://hb.adtrue.com/prebid/usersync?bidder=adtrue&publisherId=1212&zoneId=21423&gdpr=0&gdpr_consent=&us_privacy=&coppa=0' }]); }); // Multiple user sync output it('execute as per config', function () { - expect(spec.getUserSyncs({iframeEnabled: true}, [bidResponses2], undefined, undefined)).to.deep.equal([ + expect(spec.getUserSyncs({ iframeEnabled: true }, [bidResponses2], undefined, undefined)).to.deep.equal([ { type: 'image', url: 'https://hb.adtrue.com/prebid/usersync?bidder=adtrue&type=image&publisherId=1212&zoneId=21423&gdpr=0&gdpr_consent=&us_privacy=&coppa=0' diff --git a/test/spec/modules/aduptechBidAdapter_spec.js b/test/spec/modules/aduptechBidAdapter_spec.js index 2b909ebe20d..3aa297aa0e7 100644 --- a/test/spec/modules/aduptechBidAdapter_spec.js +++ b/test/spec/modules/aduptechBidAdapter_spec.js @@ -154,7 +154,7 @@ describe('AduptechBidAdapter', () => { sizes: [[12, 34], [56, 78]] }; - expect(internal.extractBannerConfig(bidRequest)).to.deep.equal({sizes: bidRequest.sizes}); + expect(internal.extractBannerConfig(bidRequest)).to.deep.equal({ sizes: bidRequest.sizes }); }); }); diff --git a/test/spec/modules/advRedAnalyticsAdapter_spec.js b/test/spec/modules/advRedAnalyticsAdapter_spec.js index fd56126d1db..fa051901c90 100644 --- a/test/spec/modules/advRedAnalyticsAdapter_spec.js +++ b/test/spec/modules/advRedAnalyticsAdapter_spec.js @@ -1,7 +1,7 @@ import advRedAnalytics from 'modules/advRedAnalyticsAdapter.js'; -import {expect} from 'chai'; -import {server} from 'test/mocks/xhr.js'; -import {expectEvents} from '../../helpers/analytics.js'; +import { expect } from 'chai'; +import { server } from 'test/mocks/xhr.js'; +import { expectEvents } from '../../helpers/analytics.js'; import { EVENTS } from 'src/constants.js'; import sinon from 'sinon'; diff --git a/test/spec/modules/advangelistsBidAdapter_spec.js b/test/spec/modules/advangelistsBidAdapter_spec.js index 4a01d375902..e8367dc143f 100755 --- a/test/spec/modules/advangelistsBidAdapter_spec.js +++ b/test/spec/modules/advangelistsBidAdapter_spec.js @@ -7,9 +7,9 @@ describe('advangelistsBidAdapter', function () { 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'}]; + 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}, 'crumbs': {'pubcid': '979fde13-c71e-4ac2-98b7-28c90f99b449'}, 'mediaTypes': {'video': {'playerSize': [[320, 480]], 'context': 'instream', 'skip': 1, 'mimes': ['video/mp4', 'application/javascript'], 'playbackmethod': [2, 6], 'maxduration': 30}}, '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'}]; + bidRequestsVid = [{ 'bidder': 'advangelists', 'params': { 'pubid': '8537f00948fc37cc03c5f0f88e198a76', 'placement': 1234 }, 'crumbs': { 'pubcid': '979fde13-c71e-4ac2-98b7-28c90f99b449' }, 'mediaTypes': { 'video': { 'playerSize': [[320, 480]], 'context': 'instream', 'skip': 1, 'mimes': ['video/mp4', 'application/javascript'], 'playbackmethod': [2, 6], 'maxduration': 30 } }, '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 () { @@ -44,19 +44,19 @@ describe('advangelistsBidAdapter', function () { describe('spec.buildRequests', function () { it('should create a POST request for each bid', function () { const bidRequest = bidRequests[0]; - const requests = spec.buildRequests([ bidRequest ], { timeout: 1000 }); + const requests = spec.buildRequests([bidRequest], { timeout: 1000 }); 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 ], { timeout: 1000 }); + const requests = spec.buildRequests([bidRequest], { timeout: 1000 }); expect(requests[0].method).to.equal('POST'); }); it('should have domain in request', function () { const bidRequest = bidRequests[0]; - const requests = spec.buildRequests([ bidRequest ], { timeout: 1000 }); + const requests = spec.buildRequests([bidRequest], { timeout: 1000 }); expect(requests[0].data.site.domain).to.have.length.above(0); }); }); @@ -65,8 +65,8 @@ describe('advangelistsBidAdapter', function () { describe('for banner bids', function () { it('should return valid video bid responses', function () { const _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'], '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 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'], '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']; @@ -85,16 +85,17 @@ describe('advangelistsBidAdapter', function () { }); it('should return valid banner bid responses', function () { - const advangelistsbidreq = {bids: {}}; + const advangelistsbidreq = { bids: {} }; bidRequests.forEach(bid => { const _mediaTypes = (bid.mediaTypes && bid.mediaTypes.video ? VIDEO : BANNER); - advangelistsbidreq.bids[bid.bidId] = {mediaTypes: _mediaTypes, + 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': '
', 'iurl': '', 'cid': 'campaign1', 'crid': 'abcde-12345', 'adomain': ['chevrolet.com'], 'w': 300, 'h': 250}], 'seat': '19513bcfca8006'}], 'bidid': '19513bcfca8006', 'cur': 'USD', 'w': 300, 'h': 250}; + const serverResponse = { 'id': '2aa73f571eaf29', 'seatbid': [{ 'bid': [{ 'id': '2c5e8a1a84522d', 'impid': '2c5e8a1a84522d', 'price': 0.81, 'adid': 'abcde-12345', 'nurl': '', 'adm': '
', 'iurl': '', 'cid': 'campaign1', 'crid': 'abcde-12345', 'adomain': ['chevrolet.com'], '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({ diff --git a/test/spec/modules/advertisingBidAdapter_spec.js b/test/spec/modules/advertisingBidAdapter_spec.js index d9067f63826..440a15227f8 100644 --- a/test/spec/modules/advertisingBidAdapter_spec.js +++ b/test/spec/modules/advertisingBidAdapter_spec.js @@ -700,7 +700,7 @@ describe('advertisingBidAdapter ', function () { mediaTypes: { video: { context: 'instream', - playerSize: [[ 640, 480 ]], + playerSize: [[640, 480]], startdelay: 1, linearity: 1, plcmt: 1, @@ -709,7 +709,7 @@ describe('advertisingBidAdapter ', function () { }, adUnitCode: 'video1', transactionId: '93e5def8-29aa-4fe8-bd3a-0298c39f189a', - sizes: [[ 640, 480 ]], + sizes: [[640, 480]], bidId: '2624fabbb078e8', bidderRequestId: '117954d20d7c9c', auctionId: 'defd525f-4f1e-4416-a4cb-ae53be90e706', diff --git a/test/spec/modules/adverxoBidAdapter_spec.js b/test/spec/modules/adverxoBidAdapter_spec.js index 8dea5e3a326..676f7126c94 100644 --- a/test/spec/modules/adverxoBidAdapter_spec.js +++ b/test/spec/modules/adverxoBidAdapter_spec.js @@ -1,6 +1,6 @@ -import {expect} from 'chai'; -import {spec} from 'modules/adverxoBidAdapter.js'; -import {config} from 'src/config'; +import { expect } from 'chai'; +import { spec } from 'modules/adverxoBidAdapter.js'; +import { config } from 'src/config'; describe('Adverxo Bid Adapter', () => { function makeBidRequestWithParams(params) { @@ -8,7 +8,7 @@ describe('Adverxo Bid Adapter', () => { bidId: '2e9f38ea93bb9e', bidder: 'adverxo', adUnitCode: 'adunit-code', - mediaTypes: {banner: {sizes: [[300, 250]]}}, + mediaTypes: { banner: { sizes: [[300, 250]] } }, params: params, bidderRequestId: 'test-bidder-request-id' }; @@ -26,7 +26,7 @@ describe('Adverxo Bid Adapter', () => { 'id': '01EAJWWNEPN3CYMM5N8M5VXY22' }] }], - mediaTypes: {banner: {sizes: [[300, 250]]}}, + mediaTypes: { banner: { sizes: [[300, 250]] } }, params: { host: 'bid.example.com', adUnitId: 1, @@ -48,17 +48,17 @@ describe('Adverxo Bid Adapter', () => { { id: 1, required: 1, - img: {type: 3, w: 150, h: 50} + img: { type: 3, w: 150, h: 50 } }, { id: 2, required: 1, - title: {len: 80} + title: { len: 80 } }, { id: 3, required: 0, - data: {type: 1} + data: { type: 1 } } ] }; @@ -246,7 +246,7 @@ describe('Adverxo Bid Adapter', () => { const bidRequests = [ { bidder: 'bidsmind', - mediaTypes: {banner: {sizes: [[300, 250]]}}, + mediaTypes: { banner: { sizes: [[300, 250]] } }, params: { adUnitId: 1, auth: 'authExample', @@ -269,7 +269,7 @@ describe('Adverxo Bid Adapter', () => { const bidRequests = [ { bidder: 'adverxo', - mediaTypes: {banner: {sizes: [[300, 250]]}}, + mediaTypes: { banner: { sizes: [[300, 250]] } }, params: { adUnitId: 1, auth: 'authExample', @@ -311,19 +311,19 @@ describe('Adverxo Bid Adapter', () => { expect(nativeRequest.assets[0]).to.deep.equal({ id: 1, required: 1, - img: {w: 150, h: 50, type: 3} + img: { w: 150, h: 50, type: 3 } }); expect(nativeRequest.assets[1]).to.deep.equal({ id: 2, required: 1, - title: {len: 80} + title: { len: 80 } }); expect(nativeRequest.assets[2]).to.deep.equal({ id: 3, required: 0, - data: {type: 1} + data: { type: 1 } }); }); } @@ -359,7 +359,7 @@ describe('Adverxo Bid Adapter', () => { it('should add bid floor to request', function () { const bannerBidRequestWithFloor = { ...bannerBidRequests[0], - getFloor: () => ({currency: 'USD', floor: 3}) + getFloor: () => ({ currency: 'USD', floor: 3 }) }; const request = spec.buildRequests([bannerBidRequestWithFloor], {})[0].data; @@ -505,11 +505,11 @@ describe('Adverxo Bid Adapter', () => { native: { ortb: { assets: [ - {id: 2, title: {text: 'Title'}}, - {id: 3, data: {value: 'Description'}}, - {id: 1, img: {url: 'http://example.com?img', w: 150, h: 50}} + { id: 2, title: { text: 'Title' } }, + { id: 3, data: { value: 'Description' } }, + { id: 1, img: { url: 'http://example.com?img', w: 150, h: 50 } } ], - link: {url: 'http://example.com?link'} + link: { url: 'http://example.com?link' } } } } @@ -646,14 +646,14 @@ describe('Adverxo Bid Adapter', () => { describe('getUserSyncs', () => { const exampleUrl = 'https://example.com/usync?id=5'; - const iframeConfig = {iframeEnabled: true}; + const iframeConfig = { iframeEnabled: true }; const responses = [{ - body: {ext: {avx_usync: [exampleUrl]}} + body: { ext: { avx_usync: [exampleUrl] } } }]; const responseWithoutQueryString = [{ - body: {ext: {avx_usync: ['https://example.com/usync/sf/5']}} + body: { ext: { avx_usync: ['https://example.com/usync/sf/5'] } } }]; it('should not return empty list if not allowed', function () { @@ -685,17 +685,17 @@ describe('Adverxo Bid Adapter', () => { }); it('should add GDPR parameters if provided', function () { - expect(spec.getUserSyncs(iframeConfig, responses, {gdprApplies: true}, undefined, undefined)).to.deep.equal([{ + expect(spec.getUserSyncs(iframeConfig, responses, { gdprApplies: true }, undefined, undefined)).to.deep.equal([{ type: 'iframe', url: `${exampleUrl}&type=iframe&gdpr=1&gdpr_consent=` }]); expect(spec.getUserSyncs(iframeConfig, responses, - {gdprApplies: true, consentString: 'foo?'}, undefined, undefined)).to.deep.equal([{ + { gdprApplies: true, consentString: 'foo?' }, undefined, undefined)).to.deep.equal([{ type: 'iframe', url: `${exampleUrl}&type=iframe&gdpr=1&gdpr_consent=foo%3F` }]); expect(spec.getUserSyncs(iframeConfig, responses, - {gdprApplies: false, consentString: 'bar'}, undefined, undefined)).to.deep.equal([{ + { gdprApplies: false, consentString: 'bar' }, undefined, undefined)).to.deep.equal([{ type: 'iframe', url: `${exampleUrl}&type=iframe&gdpr=0&gdpr_consent=bar` }]); }); @@ -707,7 +707,7 @@ describe('Adverxo Bid Adapter', () => { }); it('should not apply if not gppConsent.gppString', function () { - const gppConsent = {gppString: '', applicableSections: [123]}; + const gppConsent = { gppString: '', applicableSections: [123] }; const result = spec.getUserSyncs(iframeConfig, responses, undefined, undefined, gppConsent); expect(result).to.deep.equal([{ type: 'iframe', url: `${exampleUrl}&type=iframe` @@ -715,7 +715,7 @@ describe('Adverxo Bid Adapter', () => { }); it('should not apply if not gppConsent.applicableSections', function () { - const gppConsent = {gppString: '', applicableSections: undefined}; + const gppConsent = { gppString: '', applicableSections: undefined }; const result = spec.getUserSyncs(iframeConfig, responses, undefined, undefined, gppConsent); expect(result).to.deep.equal([{ type: 'iframe', url: `${exampleUrl}&type=iframe` @@ -723,7 +723,7 @@ describe('Adverxo Bid Adapter', () => { }); it('should not apply if empty gppConsent.applicableSections', function () { - const gppConsent = {gppString: '', applicableSections: []}; + const gppConsent = { gppString: '', applicableSections: [] }; const result = spec.getUserSyncs(iframeConfig, responses, undefined, undefined, gppConsent); expect(result).to.deep.equal([{ type: 'iframe', url: `${exampleUrl}&type=iframe` @@ -731,7 +731,7 @@ describe('Adverxo Bid Adapter', () => { }); it('should apply if all above are available', function () { - const gppConsent = {gppString: 'foo?', applicableSections: [123]}; + const gppConsent = { gppString: 'foo?', applicableSections: [123] }; const result = spec.getUserSyncs(iframeConfig, responses, undefined, undefined, gppConsent); expect(result).to.deep.equal([{ type: 'iframe', url: `${exampleUrl}&type=iframe&gpp=foo%3F&gpp_sid=123` @@ -739,7 +739,7 @@ describe('Adverxo Bid Adapter', () => { }); it('should support multiple sections', function () { - const gppConsent = {gppString: 'foo', applicableSections: [123, 456]}; + const gppConsent = { gppString: 'foo', applicableSections: [123, 456] }; const result = spec.getUserSyncs(iframeConfig, responses, undefined, undefined, gppConsent); expect(result).to.deep.equal([{ type: 'iframe', url: `${exampleUrl}&type=iframe&gpp=foo&gpp_sid=123%2C456` diff --git a/test/spec/modules/adxcgBidAdapter_spec.js b/test/spec/modules/adxcgBidAdapter_spec.js index 0f14bad94ce..885a1d2a7bf 100644 --- a/test/spec/modules/adxcgBidAdapter_spec.js +++ b/test/spec/modules/adxcgBidAdapter_spec.js @@ -1,10 +1,10 @@ // jshint esversion: 6, es3: false, node: true /* eslint dot-notation:0, quote-props:0 */ -import {assert, expect} from 'chai'; -import {spec} from 'modules/adxcgBidAdapter.js'; -import {config} from 'src/config.js'; +import { assert, expect } from 'chai'; +import { spec } from 'modules/adxcgBidAdapter.js'; +import { config } from 'src/config.js'; -import {addFPDToBidderRequest} from '../../helpers/fpd.js'; +import { addFPDToBidderRequest } from '../../helpers/fpd.js'; const utils = require('src/utils'); diff --git a/test/spec/modules/adyoulikeBidAdapter_spec.js b/test/spec/modules/adyoulikeBidAdapter_spec.js index 337bd6fea75..e3a8f223256 100644 --- a/test/spec/modules/adyoulikeBidAdapter_spec.js +++ b/test/spec/modules/adyoulikeBidAdapter_spec.js @@ -19,8 +19,8 @@ describe('Adyoulike Adapter', function () { consentString: consentString, gdprApplies: true }, - refererInfo: {location: referrerUrl, canonicalUrl, domain, topmostLocation: 'fakePageURL'}, - ortb2: {site: {page: pageUrl, ref: referrerUrl}} + refererInfo: { location: referrerUrl, canonicalUrl, domain, topmostLocation: 'fakePageURL' }, + ortb2: { site: { page: pageUrl, ref: referrerUrl } } }; const bidRequestWithEmptyPlacement = [ { @@ -30,9 +30,9 @@ describe('Adyoulike Adapter', function () { 'params': {}, 'sizes': '300x250', 'mediaTypes': - { 'banner': - {'sizes': ['300x250', '300x600'] - } + { + 'banner': + { 'sizes': ['300x250', '300x600'] } } } ]; @@ -64,39 +64,40 @@ describe('Adyoulike Adapter', function () { }, 'sizes': '300x250', 'mediaTypes': - { 'banner': - {'sizes': ['300x250'] - }, - 'native': - { 'image': { - 'required': true, - }, - 'title': { - 'required': true, - 'len': 80 - }, - 'cta': { - 'required': false - }, - 'sponsoredBy': { - 'required': true - }, - 'clickUrl': { - 'required': true - }, - 'privacyIcon': { - 'required': false - }, - 'privacyLink': { - 'required': false - }, - 'body': { - 'required': true - }, - 'icon': { - 'required': true, - 'sizes': [] - } + { + 'banner': + { 'sizes': ['300x250'] }, + 'native': + { + 'image': { + 'required': true, + }, + 'title': { + 'required': true, + 'len': 80 + }, + 'cta': { + 'required': false + }, + 'sponsoredBy': { + 'required': true + }, + 'clickUrl': { + 'required': true + }, + 'privacyIcon': { + 'required': false + }, + 'privacyLink': { + 'required': false + }, + 'body': { + 'required': true + }, + 'icon': { + 'required': true, + 'sizes': [] + } }, }, 'ortb2': { @@ -178,7 +179,7 @@ describe('Adyoulike Adapter', function () { { 'video': { 'context': 'instream', - 'playerSize': [[ 640, 480 ]] + 'playerSize': [[640, 480]] } }, ortb2Imp: { @@ -309,9 +310,9 @@ describe('Adyoulike Adapter', function () { }, 'sizes': '300x250', 'mediaTypes': - { 'banner': - {'sizes': ['300x250'] - } + { + 'banner': + { 'sizes': ['300x250'] } }, ortb2Imp: { ext: { @@ -331,9 +332,9 @@ describe('Adyoulike Adapter', function () { }, 'sizes': '300x250', 'mediaTypes': - { 'banner': - {'sizes': ['300x250'] - } + { + 'banner': + { 'sizes': ['300x250'] } }, ortb2Imp: { ext: { @@ -350,9 +351,9 @@ describe('Adyoulike Adapter', function () { }, 'sizes': [[300, 600]], 'mediaTypes': - { 'banner': - {'sizes': ['300x600'] - } + { + 'banner': + { 'sizes': ['300x600'] } }, ortb2Imp: { ext: { @@ -389,7 +390,8 @@ describe('Adyoulike Adapter', function () { const requestDataOnePlacement = { 'bid_id_0': - { 'PlacementID': 'e622af275681965d3095808561a1e510', + { + 'PlacementID': 'e622af275681965d3095808561a1e510', 'TransactionID': '1bca18cc-c0fe-439b-88c2-8247d3448f22', 'Width': 300, 'Height': 600, @@ -399,14 +401,16 @@ describe('Adyoulike Adapter', function () { const requestDataMultiPlacement = { 'bid_id_0': - { 'PlacementID': 'e622af275681965d3095808561a1e510', + { + 'PlacementID': 'e622af275681965d3095808561a1e510', 'TransactionID': '1bca18cc-c0fe-439b-88c2-8247d3448f22', 'Width': 300, 'Height': 600, 'AvailableSizes': '300x600' }, 'bid_id_1': - { 'PlacementID': 'e622af275681965d3095808561a1e510', + { + 'PlacementID': 'e622af275681965d3095808561a1e510', 'TransactionID': 'e63b2d86-ca60-4167-9cf1-497607079634', 'Width': 400, 'Height': 250, @@ -746,11 +750,11 @@ describe('Adyoulike Adapter', function () { expect(payload.Bids['bid_id_0'].PlacementID).to.be.equal('placement_0'); expect(payload.PageRefreshed).to.equal(false); expect(payload.Bids['bid_id_0'].TransactionID).to.be.equal('bid_id_0_transaction_id'); - expect(payload.ortb2).to.deep.equal({site: {page: pageUrl, ref: referrerUrl}}); + expect(payload.ortb2).to.deep.equal({ site: { page: pageUrl, ref: referrerUrl } }); }); it('sends bid request to endpoint with single placement without canonical', function () { - const request = spec.buildRequests(bidRequestWithSinglePlacement, {...bidderRequest, refererInfo: {...bidderRequest.refererInfo, canonicalUrl: null}}); + const request = spec.buildRequests(bidRequestWithSinglePlacement, { ...bidderRequest, refererInfo: { ...bidderRequest.refererInfo, canonicalUrl: null } }); const payload = JSON.parse(request.data); expect(request.url).to.contain(getEndpoint()); @@ -765,7 +769,7 @@ describe('Adyoulike Adapter', function () { }); it('sends bid request to endpoint with single placement multiple mediatype', function () { - const request = spec.buildRequests(bidRequestWithSinglePlacement, {...bidderRequest, refererInfo: {...bidderRequest.refererInfo, canonicalUrl: null}}); + const request = spec.buildRequests(bidRequestWithSinglePlacement, { ...bidderRequest, refererInfo: { ...bidderRequest.refererInfo, canonicalUrl: null } }); const payload = JSON.parse(request.data); expect(request.url).to.contain(getEndpoint()); @@ -837,7 +841,7 @@ describe('Adyoulike Adapter', function () { it('receive reponse with single placement', function () { serverResponse.body = responseWithSinglePlacement; - const result = spec.interpretResponse(serverResponse, {data: '{"Bids":' + JSON.stringify(requestDataOnePlacement) + '}'}); + const result = spec.interpretResponse(serverResponse, { data: '{"Bids":' + JSON.stringify(requestDataOnePlacement) + '}' }); expect(result.length).to.equal(1); expect(result[0].cpm).to.equal(0.5); @@ -849,7 +853,7 @@ describe('Adyoulike Adapter', function () { it('receive reponse with multiple placement', function () { serverResponse.body = responseWithMultiplePlacements; - const result = spec.interpretResponse(serverResponse, {data: '{"Bids":' + JSON.stringify(requestDataMultiPlacement) + '}'}); + const result = spec.interpretResponse(serverResponse, { data: '{"Bids":' + JSON.stringify(requestDataMultiPlacement) + '}' }); expect(result.length).to.equal(2); @@ -866,7 +870,7 @@ describe('Adyoulike Adapter', function () { it('receive reponse with Native from ad markup', function () { serverResponse.body = responseWithSinglePlacement; - const result = spec.interpretResponse(serverResponse, {data: '{"Bids":' + JSON.stringify(sentBidNative) + '}'}); + const result = spec.interpretResponse(serverResponse, { data: '{"Bids":' + JSON.stringify(sentBidNative) + '}' }); expect(result.length).to.equal(1); @@ -875,7 +879,7 @@ describe('Adyoulike Adapter', function () { it('receive reponse with Native ad', function () { serverResponse.body = responseWithSingleNative; - const result = spec.interpretResponse(serverResponse, {data: '{"Bids":' + JSON.stringify(sentBidNative) + '}'}); + const result = spec.interpretResponse(serverResponse, { data: '{"Bids":' + JSON.stringify(sentBidNative) + '}' }); expect(result.length).to.equal(1); @@ -890,7 +894,7 @@ describe('Adyoulike Adapter', function () { it('receive Vast reponse with Video ad', function () { serverResponse.body = responseWithSingleVideo; - const result = spec.interpretResponse(serverResponse, {data: '{"Bids":' + JSON.stringify(sentBidVideo) + '}'}); + const result = spec.interpretResponse(serverResponse, { data: '{"Bids":' + JSON.stringify(sentBidVideo) + '}' }); expect(result.length).to.equal(1); expect(result).to.deep.equal(videoResult); @@ -916,14 +920,14 @@ describe('Adyoulike Adapter', function () { }); it('should add GDPR parameters if provided', function() { - expect(spec.getUserSyncs(userSyncConfig, {}, {gdprApplies: true, consentString: undefined}, undefined)).to.deep.equal([{ + expect(spec.getUserSyncs(userSyncConfig, {}, { gdprApplies: true, consentString: undefined }, undefined)).to.deep.equal([{ type: 'iframe', url: `${syncurl_iframe}&gdpr=1&gdpr_consent=` }]); - expect(spec.getUserSyncs(userSyncConfig, {}, {gdprApplies: true, consentString: 'foo?'}, undefined)).to.deep.equal([{ + expect(spec.getUserSyncs(userSyncConfig, {}, { gdprApplies: true, consentString: 'foo?' }, undefined)).to.deep.equal([{ type: 'iframe', url: `${syncurl_iframe}&gdpr=1&gdpr_consent=foo%3F` }]); - expect(spec.getUserSyncs(userSyncConfig, {}, {gdprApplies: false, consentString: 'bar'}, undefined)).to.deep.equal([{ + expect(spec.getUserSyncs(userSyncConfig, {}, { gdprApplies: false, consentString: 'bar' }, undefined)).to.deep.equal([{ type: 'iframe', url: `${syncurl_iframe}&gdpr=0&gdpr_consent=bar` }]); }); @@ -1007,7 +1011,7 @@ describe('Adyoulike Adapter', function () { it('should return empty list of syncs', function() { expect(spec.getUserSyncs(userSyncConfig, {}, undefined, undefined)).to.deep.equal(emptySync); - expect(spec.getUserSyncs(userSyncConfig, {}, {gdprApplies: true, consentString: 'foo'}, 'bar')).to.deep.equal(emptySync); + expect(spec.getUserSyncs(userSyncConfig, {}, { gdprApplies: true, consentString: 'foo' }, 'bar')).to.deep.equal(emptySync); }); }); }); diff --git a/test/spec/modules/afpBidAdapter_spec.js b/test/spec/modules/afpBidAdapter_spec.js index 48c9fc1c701..7efebcaebc5 100644 --- a/test/spec/modules/afpBidAdapter_spec.js +++ b/test/spec/modules/afpBidAdapter_spec.js @@ -32,8 +32,8 @@ const sizes = [[imageWidth, imageHeight]] const bidderRequest = { refererInfo: { referer: pageUrl }, } -const mediaTypeBanner = { [BANNER]: {sizes: [[imageWidth, imageHeight]]} } -const mediaTypeVideo = { [VIDEO]: {playerSize: [[imageWidth, imageHeight]]} } +const mediaTypeBanner = { [BANNER]: { sizes: [[imageWidth, imageHeight]] } } +const mediaTypeVideo = { [VIDEO]: { playerSize: [[imageWidth, imageHeight]] } } const commonParams = { placeId, placeContainer, @@ -117,7 +117,7 @@ const configByPlaceType = { }) }, } -const getTransformedConfig = ({mediaTypes, params}) => { +const getTransformedConfig = ({ mediaTypes, params }) => { return { params: params, sizes, diff --git a/test/spec/modules/aidemBidAdapter_spec.js b/test/spec/modules/aidemBidAdapter_spec.js index 979d2cb922f..52654465f82 100644 --- a/test/spec/modules/aidemBidAdapter_spec.js +++ b/test/spec/modules/aidemBidAdapter_spec.js @@ -1,10 +1,10 @@ -import {expect} from 'chai'; -import {setEndPoints, spec} from 'modules/aidemBidAdapter.js'; +import { expect } from 'chai'; +import { setEndPoints, spec } from 'modules/aidemBidAdapter.js'; import * as utils from '../../../src/utils.js'; -import {deepSetValue} from '../../../src/utils.js'; -import {server} from '../../mocks/xhr.js'; -import {config} from '../../../src/config.js'; -import {NATIVE} from '../../../src/mediaTypes.js'; +import { deepSetValue } from '../../../src/utils.js'; +import { server } from '../../mocks/xhr.js'; +import { config } from '../../../src/config.js'; +import { NATIVE } from '../../../src/mediaTypes.js'; // Full banner + Full Video + Basic Banner + Basic Video const VALID_BIDS = [ @@ -161,8 +161,8 @@ const DEFAULT_VALID_BANNER_REQUESTS = [ mediaTypes: { banner: { sizes: [ - [ 300, 250 ], - [ 300, 600 ] + [300, 250], + [300, 600] ] } }, @@ -448,7 +448,7 @@ describe('Aidem adapter', () => { }); it('should have a well formatted banner payload', () => { - const {data} = spec.buildRequests(DEFAULT_VALID_BANNER_REQUESTS, VALID_BIDDER_REQUEST); + const { data } = spec.buildRequests(DEFAULT_VALID_BANNER_REQUESTS, VALID_BIDDER_REQUEST); expect(data).to.be.a('object').that.has.all.keys( 'id', 'imp', 'regs', 'site', 'environment', 'at', 'test' ) @@ -464,7 +464,7 @@ describe('Aidem adapter', () => { if (FEATURES.VIDEO) { it('should have a well formatted video payload', () => { - const {data} = spec.buildRequests(DEFAULT_VALID_VIDEO_REQUESTS, VALID_BIDDER_REQUEST); + const { data } = spec.buildRequests(DEFAULT_VALID_VIDEO_REQUESTS, VALID_BIDDER_REQUEST); expect(data).to.be.a('object').that.has.all.keys( 'id', 'imp', 'regs', 'site', 'environment', 'at', 'test' ) @@ -480,7 +480,7 @@ describe('Aidem adapter', () => { } it('should hav wpar keys in environment object', function () { - const {data} = spec.buildRequests(DEFAULT_VALID_VIDEO_REQUESTS, VALID_BIDDER_REQUEST); + const { data } = spec.buildRequests(DEFAULT_VALID_VIDEO_REQUESTS, VALID_BIDDER_REQUEST); expect(data).to.have.property('environment') expect(data.environment).to.be.a('object').that.have.property('wpar') expect(data.environment.wpar).to.be.a('object').that.has.keys('innerWidth', 'innerHeight') @@ -489,8 +489,8 @@ describe('Aidem adapter', () => { describe('interpretResponse', () => { it('should return a valid bid array with a banner bid', () => { - const {data} = spec.buildRequests(DEFAULT_VALID_BANNER_REQUESTS, VALID_BIDDER_REQUEST) - const bids = spec.interpretResponse({body: SERVER_RESPONSE_BANNER}, { data }) + const { data } = spec.buildRequests(DEFAULT_VALID_BANNER_REQUESTS, VALID_BIDDER_REQUEST) + const bids = spec.interpretResponse({ body: SERVER_RESPONSE_BANNER }, { data }) expect(bids).to.be.a('array').that.has.lengthOf(1) bids.forEach(value => { expect(value).to.be.a('object').that.has.all.keys( @@ -501,8 +501,8 @@ describe('Aidem adapter', () => { if (FEATURES.VIDEO) { it('should return a valid bid array with a video bid', () => { - const {data} = spec.buildRequests(DEFAULT_VALID_VIDEO_REQUESTS, VALID_BIDDER_REQUEST) - const bids = spec.interpretResponse({body: SERVER_RESPONSE_VIDEO}, { data }) + const { data } = spec.buildRequests(DEFAULT_VALID_VIDEO_REQUESTS, VALID_BIDDER_REQUEST) + const bids = spec.interpretResponse({ body: SERVER_RESPONSE_VIDEO }, { data }) expect(bids).to.be.a('array').that.has.lengthOf(1) bids.forEach(value => { expect(value).to.be.a('object').that.has.all.keys( @@ -513,8 +513,8 @@ describe('Aidem adapter', () => { } it('should return a valid bid array with netRevenue', () => { - const {data} = spec.buildRequests(DEFAULT_VALID_VIDEO_REQUESTS, VALID_BIDDER_REQUEST) - const bids = spec.interpretResponse({body: SERVER_RESPONSE_VIDEO}, { data }) + const { data } = spec.buildRequests(DEFAULT_VALID_VIDEO_REQUESTS, VALID_BIDDER_REQUEST) + const bids = spec.interpretResponse({ body: SERVER_RESPONSE_VIDEO }, { data }) expect(bids).to.be.a('array').that.has.lengthOf(1) expect(bids[0].netRevenue).to.be.true }); diff --git a/test/spec/modules/airgridRtdProvider_spec.js b/test/spec/modules/airgridRtdProvider_spec.js index 3e885dbe55d..5c6c43a93eb 100644 --- a/test/spec/modules/airgridRtdProvider_spec.js +++ b/test/spec/modules/airgridRtdProvider_spec.js @@ -1,5 +1,5 @@ -import {config} from 'src/config.js'; -import {deepAccess} from 'src/utils.js'; +import { config } from 'src/config.js'; +import { deepAccess } from 'src/utils.js'; import * as agRTD from 'modules/airgridRtdProvider.js'; import { loadExternalScriptStub } from 'test/mocks/adloaderStub.js'; @@ -78,7 +78,7 @@ describe('airgrid RTD Submodule', function () { const bidderOrtb2 = {}; - agRTD.setAudiencesAsBidderOrtb2({ortb2Fragments: {bidder: bidderOrtb2}}, RTD_CONFIG.dataProviders[0], audiences); + agRTD.setAudiencesAsBidderOrtb2({ ortb2Fragments: { bidder: bidderOrtb2 } }, RTD_CONFIG.dataProviders[0], audiences); const bidders = RTD_CONFIG.dataProviders[0].params.bidders diff --git a/test/spec/modules/ajaBidAdapter_spec.js b/test/spec/modules/ajaBidAdapter_spec.js index b9acda490e9..37a68934ed9 100644 --- a/test/spec/modules/ajaBidAdapter_spec.js +++ b/test/spec/modules/ajaBidAdapter_spec.js @@ -54,9 +54,9 @@ describe('AjaAdapter', function () { version: ['8', '0', '0'] }, browsers: [ - {brand: 'Not_A Brand', version: ['99', '0', '0', '0']}, - {brand: 'Google Chrome', version: ['109', '0', '5414', '119']}, - {brand: 'Chromium', version: ['109', '0', '5414', '119']} + { brand: 'Not_A Brand', version: ['99', '0', '0', '0'] }, + { brand: 'Google Chrome', version: ['109', '0', '5414', '119'] }, + { brand: 'Chromium', version: ['109', '0', '5414', '119'] } ], mobile: 1, model: 'SM-G955U', @@ -206,7 +206,7 @@ describe('AjaAdapter', function () { ]; let bidderRequest; - const result = spec.interpretResponse({ body: response }, {bidderRequest}); + const result = spec.interpretResponse({ body: response }, { bidderRequest }); expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); }); @@ -217,7 +217,7 @@ describe('AjaAdapter', function () { }; let bidderRequest; - const result = spec.interpretResponse({ body: response }, {bidderRequest}); + const result = spec.interpretResponse({ body: response }, { bidderRequest }); expect(result.length).to.equal(0); }); }); diff --git a/test/spec/modules/allegroBidAdapter_spec.js b/test/spec/modules/allegroBidAdapter_spec.js index 59f5ed1018d..5575f17a358 100644 --- a/test/spec/modules/allegroBidAdapter_spec.js +++ b/test/spec/modules/allegroBidAdapter_spec.js @@ -1,16 +1,16 @@ -import {expect} from 'chai'; -import {spec} from 'modules/allegroBidAdapter.js'; -import {config} from 'src/config.js'; +import { expect } from 'chai'; +import { spec } from 'modules/allegroBidAdapter.js'; +import { config } from 'src/config.js'; import sinon from 'sinon'; import * as utils from 'src/utils.js'; -function buildBidRequest({bidId = 'bid1', adUnitCode = 'div-1', sizes = [[300, 250]], params = {}, mediaTypes} = {}) { +function buildBidRequest({ bidId = 'bid1', adUnitCode = 'div-1', sizes = [[300, 250]], params = {}, mediaTypes } = {}) { return { bidId, adUnitCode, bidder: 'allegro', params, - mediaTypes: mediaTypes || {banner: {sizes}}, + mediaTypes: mediaTypes || { banner: { sizes } }, }; } @@ -20,11 +20,11 @@ function buildBidderRequest(bidRequests, ortb2Overrides = {}) { bids: bidRequests, auctionId: 'auc-1', timeout: 1000, - refererInfo: {page: 'https://example.com', domain: 'example.com', ref: '', stack: ['https://example.com']}, + refererInfo: { page: 'https://example.com', domain: 'example.com', ref: '', stack: ['https://example.com'] }, ortb2: Object.assign({ device: { dnt: 0, - sua: {mobile: 0} + sua: { mobile: 0 } } }, ortb2Overrides) }; @@ -79,12 +79,12 @@ describe('Allegro Bid Adapter', () => { configStub = sinon.stub(config, 'getConfig').callsFake((key) => undefined); const bidRequests = [buildBidRequest({})]; const ortb2 = { - site: {ext: {siteCustom: 'val'}, publisher: {ext: {pubCustom: 'pub'}}}, - user: {ext: {userCustom: 'usr'}, data: [{ext: {dataCustom: 'd1'}}]}, - device: {ext: {deviceCustom: 'dev'}, sua: {mobile: 1}, dnt: 1}, - regs: {ext: {gdpr: 1, other: 'x'}}, - source: {ext: {sourceCustom: 'src'}}, - ext: {requestCustom: 'req'} + site: { ext: { siteCustom: 'val' }, publisher: { ext: { pubCustom: 'pub' } } }, + user: { ext: { userCustom: 'usr' }, data: [{ ext: { dataCustom: 'd1' } }] }, + device: { ext: { deviceCustom: 'dev' }, sua: { mobile: 1 }, dnt: 1 }, + regs: { ext: { gdpr: 1, other: 'x' } }, + source: { ext: { sourceCustom: 'src' } }, + ext: { requestCustom: 'req' } }; const bidderRequest = buildBidderRequest(bidRequests, ortb2); const req = spec.buildRequests(bidRequests, bidderRequest); @@ -109,7 +109,7 @@ describe('Allegro Bid Adapter', () => { return undefined; }); const bidRequests = [buildBidRequest({})]; - const ortb2 = {site: {ext: {siteCustom: 'val'}}}; + const ortb2 = { site: { ext: { siteCustom: 'val' } } }; const req = spec.buildRequests(bidRequests, buildBidderRequest(bidRequests, ortb2)); expect(req.data.site.ext.siteCustom).to.equal('val'); expect(req.data.site['[com.google.doubleclick.site]']).to.equal(undefined); @@ -117,7 +117,7 @@ describe('Allegro Bid Adapter', () => { it('converts numeric flags to booleans (topframe, secure, test) when present', () => { configStub = sinon.stub(config, 'getConfig').callsFake((key) => undefined); - const bidRequests = [buildBidRequest({mediaTypes: {banner: {sizes: [[300, 250]], topframe: 1}}, params: {secure: 1}})]; + const bidRequests = [buildBidRequest({ mediaTypes: { banner: { sizes: [[300, 250]], topframe: 1 } }, params: { secure: 1 } })]; const bidderRequest = buildBidderRequest(bidRequests); // add test flag via ortb2 without clobbering existing device object bidderRequest.ortb2.test = 1; @@ -134,13 +134,13 @@ describe('Allegro Bid Adapter', () => { describe('interpretResponse', () => { it('returns undefined for empty body', () => { - const result = spec.interpretResponse({}, {data: {}}); + const result = spec.interpretResponse({}, { data: {} }); expect(result).to.equal(undefined); }); it('returns converted bids for a valid ORTB response', () => { configStub = sinon.stub(config, 'getConfig').callsFake((key) => undefined); - const bidRequests = [buildBidRequest({bidId: 'imp-1'})]; + const bidRequests = [buildBidRequest({ bidId: 'imp-1' })]; const bidderRequest = buildBidderRequest(bidRequests); const built = spec.buildRequests(bidRequests, bidderRequest); const impId = built.data.imp[0].id; // use actual id from converter @@ -148,10 +148,10 @@ describe('Allegro Bid Adapter', () => { id: 'resp1', seatbid: [{ seat: 'seat1', - bid: [{impid: impId, price: 1.23, crid: 'creative1', w: 300, h: 250}] + bid: [{ impid: impId, price: 1.23, crid: 'creative1', w: 300, h: 250 }] }] }; - const result = spec.interpretResponse({body: ortbResponse}, built); + const result = spec.interpretResponse({ body: ortbResponse }, built); expect(result).to.be.an('array').with.lengthOf(1); const bid = result[0]; expect(bid.cpm).to.equal(1.23); @@ -162,13 +162,13 @@ describe('Allegro Bid Adapter', () => { it('ignores bids with impid not present in original request', () => { configStub = sinon.stub(config, 'getConfig').callsFake((key) => undefined); - const bidRequests = [buildBidRequest({bidId: 'imp-1'})]; + const bidRequests = [buildBidRequest({ bidId: 'imp-1' })]; const bidderRequest = buildBidderRequest(bidRequests); const built = spec.buildRequests(bidRequests, bidderRequest); const ortbResponse = { - seatbid: [{seat: 'seat1', bid: [{impid: 'unknown', price: 0.5, crid: 'x'}]}] + seatbid: [{ seat: 'seat1', bid: [{ impid: 'unknown', price: 0.5, crid: 'x' }] }] }; - const result = spec.interpretResponse({body: ortbResponse}, built); + const result = spec.interpretResponse({ body: ortbResponse }, built); expect(result).to.be.an('array').that.is.empty; }); }); @@ -179,7 +179,7 @@ describe('Allegro Bid Adapter', () => { if (key === 'allegro.triggerImpressionPixel') return false; return undefined; }); - const bid = {burl: 'https://example.com/win?price=${AUCTION_PRICE}', cpm: 1.2}; + const bid = { burl: 'https://example.com/win?price=${AUCTION_PRICE}', cpm: 1.2 }; expect(spec.onBidWon(bid)).to.equal(undefined); }); diff --git a/test/spec/modules/alliance_gravityBidAdapter_spec.js b/test/spec/modules/alliance_gravityBidAdapter_spec.js new file mode 100644 index 00000000000..980b0d2b023 --- /dev/null +++ b/test/spec/modules/alliance_gravityBidAdapter_spec.js @@ -0,0 +1,536 @@ +import { expect } from 'chai'; +import { spec } from 'modules/alliance_gravityBidAdapter.js'; +import sinon from 'sinon'; +const sandbox = sinon.createSandbox(); + +describe('Alliance Gravity bid adapter tests', () => { + const DEFAULT_OPTIONS = { + gdprConsent: { + gdprApplies: true, + consentString: 'COw4XqPOw4XqPAHABBFRDdCgAP_AAAAAAAYgI5wBQAKgArABaACEAGAAQgBAACUAFQAKwAXgAwABkADgAHgAQQAiABIACYAFIALAAYgAzABqADcAHAAOgAdgA9AB-AEIAIoASAAkgBLgCZAE0AJwATwAoABSgCqAFgALQAWwAuwBfADAAGCAMKAYcAxQBjQDIAGTAMsAZcAzQBnADPAGgANGAacA1ABqwDWgGuANoAbYA3QBvQDgAHFAOPAdAA6QB1ADsAHeAPKAfQA_AB_gEAAIEAQQAhIBCwCHAEPgIkAReAjUBHQCPgEgAJFASQAkwBKICVAEsAJeAS-AmIBMwCagE2gJwATsAnwBQICggFDAKIAUYApIBSwCmgFQgKiAVKAqgBVoCrgFYAK0AV0Ar4BYQCxQFjgLKAWaAs8BaIC1AFtALgAXGAucBeAC9AF9AL-AYGAw', + vendorData: {}, + }, + refererInfo: { + referer: 'https://www.prebid.org', + canonicalUrl: 'https://www.prebid.org/the/link/to/the/page', + } + }; + + describe('isBidRequestValid()', () => { + let bannerBid; + beforeEach(() => { + bannerBid = { + bidder: 'alliance_gravity', + mediaTypes: { banner: { sizes: [[300, 250], [300, 600]] } }, + adUnitCode: 'div-1', + transactionId: '70bdc37e-9475-4b27-8c74-4634bdc2ee66', + sizes: [[300, 250], [300, 600]], + bidId: '4906582fc87d0c', + bidderRequestId: '332fda16002dbe', + auctionId: '98932591-c822-42e3-850e-4b3cf748d063', + } + }); + + it('No srid', () => { + bannerBid.params = {}; + expect(spec.isBidRequestValid(bannerBid)).to.be.equal(false); + }); + + it('Invalid srid type (number)', () => { + bannerBid.params = { srid: 1234 }; + expect(spec.isBidRequestValid(bannerBid)).to.be.equal(false); + }); + + it('Invalid srid type (object', () => { + bannerBid.params = { srid: {} }; + expect(spec.isBidRequestValid(bannerBid)).to.be.equal(false); + }); + + it('Empty srid', () => { + bannerBid.params = { srid: '' }; + expect(spec.isBidRequestValid(bannerBid)).to.be.equal(false); + }); + + it('Valid srid', () => { + bannerBid.params = { srid: '12345' }; + expect(spec.isBidRequestValid(bannerBid)).to.be.equal(true); + }); + }); + + describe('buildRequests()', () => { + before(() => { + const documentStub = sandbox.stub(document, 'getElementById'); + documentStub.withArgs('div-1').returns({ + offsetWidth: 200, + offsetHeight: 250, + style: { + maxWidth: '400px', + maxHeight: '350px', + }, + getBoundingClientRect() { return { width: 200, height: 250 }; } + }); + }); + describe('Multiple display bids', () => { + const sampleBids = [ + { + bidder: 'alliance_gravity', + params: { + srid: "12345" + }, + adUnitCode: 'header-ad-1234', + transactionId: '469a570d-f187-488d-b1cb-48c1a2009be9', + sizes: [[300, 250], [300, 600]], + bidId: '44a2706ac3574', + bidderRequestId: '359bf8a3c06b2e', + auctionId: '2e684815-b44e-4e04-b812-56da54adbe74', + }, + { + bidder: 'alliance_gravity', + params: { + srid: '67890', + }, + mediaTypes: { + banner: { + sizes: [[728, 90], [970, 250]] + } + }, + + adUnitCode: 'div-2-abcd', + transactionId: '6196885d-4e76-40dc-a09c-906ed232626b', + sizes: [[728, 90], [970, 250]], + bidId: '5ba94555219a03', + bidderRequestId: '359bf8a3c06b2e', + auctionId: '2e684815-b44e-4e04-b812-56da54adbe74', + } + ]; + const bidderRequest = { + bidderCode: 'alliance_gravity', + auctionId: '2e684815-b44e-4e04-b812-56da54adbe74', + bidderRequestId: '359bf8a3c06b2e', + gdprConsent: { + gdprApplies: true, + consentString: 'CPhdLUAPhdLUAAKAsAENCmCsAP_AAE7AAAqIJFNd_H__bW9r-f5_aft0eY1P9_r37uQzDhfNk-8F3L_W_LwX52E7NF36tq4KmR4ku1LBIUNlHMHUDUmwaokVryHsak2cpzNKJ7BEknMZOydYGF9vmxtj-QKY7_5_d3bx2D-t_9v239z3z81Xn3d53-_03LCdV5_9Dfn9fR_bc9KPt_58v8v8_____3_e__3_7997BIiAaADgAJYBnwEeAJXAXmAwQBj4DtgHcgPBAeKBIgAA.YAAAAAAAAAAA', + } + }; + it('2 display adunits', () => { + const displayBids = structuredClone(sampleBids); + displayBids[0].mediaTypes = { + banner: { + sizes: [[300, 250], [300, 600]] + } + }; + const request = spec.buildRequests(displayBids, bidderRequest); + const requestContent = request.data; + expect(request).to.have.property('method').and.to.equal('POST'); + const expectedRequest = { + imp: [ + { + id: '44a2706ac3574', + banner: { + topframe: 0, + format: [ + { w: 300, h: 250 }, + { w: 300, h: 600 }, + ], + }, + secure: 1, + tagid: 'header-ad-1234', + ext: { + adUnitCode: 'header-ad-1234', + prebid: { + storedrequest: { + id: "12345" + } + }, + }, + }, + { + id: '5ba94555219a03', + banner: { + topframe: 0, + format: [ + { w: 728, h: 90 }, + { w: 970, h: 250 }, + ], + }, + secure: 1, + tagid: 'div-2-abcd', + ext: { + adUnitCode: 'div-2-abcd', + prebid: { + storedrequest: { + id: "67890" + } + }, + }, + }, + ], + id: requestContent.id, + test: 0 + }; + expect(requestContent).to.be.eql(expectedRequest); + }); + + if (FEATURES.VIDEO) { + it('multiformat adunit', () => { + const multiformatBids = structuredClone(sampleBids); + multiformatBids[0].mediaTypes = { + banner: { + sizes: [[300, 250], [300, 600]] + }, + video: { + context: 'outstream', + playerSize: [640, 480], + mimes: ['video/mp4'], + protocols: [1, 2, 3, 4, 5, 6, 7, 8], + playbackmethod: [2], + skip: 1, + playback_method: ['auto_play_sound_off'] + } + }; + const request = spec.buildRequests(multiformatBids, bidderRequest); + const video = request.data.imp[0].video; + const expectedVideo = { + mimes: ['video/mp4'], + protocols: [1, 2, 3, 4, 5, 6, 7, 8], + playbackmethod: [2], + skip: 1, + w: 640, + h: 480, + ext: { + playerSize: [640, 480], + context: 'outstream', + }, + }; + expect(video).to.eql(expectedVideo); + }); + + it('instream adunit', () => { + const videoBids = structuredClone(sampleBids); + videoBids[0].mediaTypes = { + video: { + context: 'instream', + playerSize: [640, 480], + mimes: ['video/mp4'], + protocols: [1, 2, 3, 4, 5, 6], + playbackmethod: [2], + skip: 1 + } + }; + const request = spec.buildRequests(videoBids, bidderRequest); + const requestContent = request.data; + expect(request).to.have.property('method').and.to.equal('POST'); + expect(requestContent.imp[0].video.ext.context).to.be.eql('instream'); + expect(requestContent.imp[0].video.playbackmethod[0]).to.be.eql(2); + }); + } + }); + after(() => { + sandbox.restore() + }); + }); + + describe('intepretResponse()', () => { + it('empty response', () => { + const response = { + body: '' + }; + const output = spec.interpretResponse(response); + expect(output.length).to.be.eql(0); + }); + it('banner responses with adm', () => { + const response = { + body: { + id: 'a8d3a675-a4ba-4d26-807f-c8f2fad821e0', + cur: 'USD', + seatbid: [ + { + bid: [ + { + id: '4427551302944024629', + impid: '226175918ebeda', + price: 1.5, + adomain: [ + 'http://prebid.org', + ], + crid: '98493581', + ssp: 'appnexus', + h: 600, + w: 300, + adm: '
TestAd
', + cat: [ + 'IAB3-1', + ], + ext: { + adUnitCode: 'div-1', + mediaType: 'banner', + adUrl: 'https://fast.nexx360.io/cache?uuid=fdddcebc-1edf-489d-880d-1418d8bdc493', + ssp: 'appnexus', + }, + }, + ], + seat: 'appnexus', + }, + ], + ext: { + id: 'de3de7c7-e1cf-4712-80a9-94eb26bfc718', + cookies: [], + }, + }, + }; + const output = spec.interpretResponse(response); + const expectedOutput = [{ + requestId: '226175918ebeda', + cpm: 1.5, + width: 300, + height: 600, + creativeId: '98493581', + currency: 'USD', + netRevenue: true, + ttl: 120, + mediaType: 'banner', + meta: { + advertiserDomains: [ + 'http://prebid.org', + ], + demandSource: 'appnexus', + }, + ad: '
TestAd
', + }]; + expect(output).to.eql(expectedOutput); + }); + + it('instream responses', () => { + const response = { + body: { + id: '2be64380-ba0c-405a-ab53-51f51c7bde51', + cur: 'USD', + seatbid: [ + { + bid: [ + { + id: '8275140264321181514', + impid: '263cba3b8bfb72', + price: 5, + adomain: [ + 'appnexus.com', + ], + crid: '97517771', + h: 1, + w: 1, + adm: 'vast', + ext: { + mediaType: 'instream', + ssp: 'appnexus', + adUnitCode: 'video1' + }, + }, + ], + seat: 'appnexus', + }, + ], + ext: { + cookies: [], + }, + }, + }; + + const output = spec.interpretResponse(response); + const expectedOutput = [{ + requestId: '263cba3b8bfb72', + cpm: 5, + width: 1, + height: 1, + creativeId: '97517771', + currency: 'USD', + netRevenue: true, + ttl: 120, + mediaType: 'video', + meta: { advertiserDomains: ['appnexus.com'], demandSource: 'appnexus' }, + vastXml: 'vast', + }]; + expect(output).to.eql(expectedOutput); + }); + + it('outstream responses', () => { + const response = { + body: { + id: '40c23932-135e-4602-9701-ca36f8d80c07', + cur: 'USD', + seatbid: [ + { + bid: [ + { + id: '1186971142548769361', + impid: '4ce809b61a3928', + price: 5, + adomain: [ + 'appnexus.com', + ], + crid: '97517771', + h: 1, + w: 1, + adm: 'vast', + ext: { + mediaType: 'outstream', + ssp: 'appnexus', + adUnitCode: 'div-1' + }, + }, + ], + seat: 'appnexus', + }, + ], + ext: { + cookies: [], + }, + }, + }; + + const output = spec.interpretResponse(response); + const expectedOutput = [{ + requestId: '4ce809b61a3928', + cpm: 5, + width: 1, + height: 1, + adUnitCode: 'div-1', + creativeId: '97517771', + currency: 'USD', + netRevenue: true, + ttl: 120, + mediaType: 'video', + meta: { advertiserDomains: ['appnexus.com'], demandSource: 'appnexus' }, + vastXml: 'vast', + renderer: output[0].renderer, + }]; + expect(output).to.eql(expectedOutput); + }); + + it('native responses', () => { + const response = { + body: { + id: '3c0290c1-6e75-4ef7-9e37-17f5ebf3bfa3', + cur: 'USD', + seatbid: [ + { + bid: [ + { + id: '6624930625245272225', + impid: '23e11d845514bb', + price: 10, + adomain: [ + 'prebid.org', + ], + crid: '97494204', + h: 1, + w: 1, + cat: [ + 'IAB3-1', + ], + ext: { + mediaType: 'native', + ssp: 'appnexus', + adUnitCode: '/19968336/prebid_native_example_1', + }, + 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:\\/\\/ams3-ib.adnxs.com\\/click?AAAAAAAAJEAAAAAAAAAkQAAAAAAAACRAAAAAAAAAJEAAAAAAAAAkQKZS4ZZl5vVbR6p-A-MwnyTZ7QVkAAAAAOLoyQBtJAAAbSQAAAIAAAC8pM8FnPgWAAAAAABVU0QAVVNEAAEAAQBNXQAAAAABAgMCAAAAALoAURe69gAAAAA.\\/bcr=AAAAAAAA8D8=\\/pp=${AUCTION_PRICE}\\/cnd=%21JBC72Aj8-LwKELzJvi4YnPFbIAQoADEAAAAAAAAkQDoJQU1TMzo2MTM1QNAwSQAAAAAAAPA_UQAAAAAAAAAAWQAAAAAAAAAAYQAAAAAAAAAAaQAAAAAAAAAAcQAAAAAAAAAAeACJAQAAAAAAAAAA\\/cca=OTMyNSNBTVMzOjYxMzU=\\/bn=97062\\/clickenc=http%3A%2F%2Fprebid.org%2Fdev-docs%2Fshow-native-ads.html"},"eventtrackers":[{"event":1,"method":1,"url":"https:\\/\\/ams3-ib.adnxs.com\\/it?an_audit=0&referrer=https%3A%2F%2Ftest.nexx360.io%2Fadapter%2Fnative%2Ftest.html&e=wqT_3QKJCqAJBQAAAwDWAAUBCNnbl6AGEKalhbfZzPn6WxjH1PqbsJzMzyQqNgkAAAECCCRAEQEHEAAAJEAZEQkAIREJACkRCQAxEQmoMOLRpwY47UhA7UhIAlC8yb4uWJzxW2AAaM26dXim9gWAAQGKAQNVU0SSAQEG9F4BmAEBoAEBqAEBsAEAuAECwAEDyAEC0AEJ2AEA4AEA8AEAigIpdWYoJ2EnLCAyNTI5ODg1LCAwKTt1ZigncicsIDk3NDk0MjA0LCAwKTuSAvEDIS0xRDNJQWo4LUx3S0VMekp2aTRZQUNDYzhWc3dBRGdBUUFSSTdVaFE0dEduQmxnQVlQX19fXzhQYUFCd0FYZ0JnQUVCaUFFQmtBRUJtQUVCb0FFQnFBRURzQUVBdVFIenJXcWtBQUFrUU1FQjg2MXFwQUFBSkVESkFYSUtWbWViSmZJXzJRRUFBQUFBQUFEd1AtQUJBUFVCQUFBQUFKZ0NBS0FDQUxVQ0FBQUFBTDBDQUFBQUFNQUNBY2dDQWRBQ0FkZ0NBZUFDQU9nQ0FQZ0NBSUFEQVpnREFib0RDVUZOVXpNNk5qRXpOZUFEMERDSUJBQ1FCQUNZQkFIQkJBQUFBQUFBQUFBQXlRUUFBCQscQUFOZ0VBUEURlSxBQUFDSUJmY3ZxUVUBDQRBQQGoCDdFRgEKCQEMREJCUQkKAQEAeRUoAUwyKAAAWi4oALg0QVhBaEQzd0JhTEQzd0w0QmQyMG1nR0NCZ05WVTBTSUJnQ1FCZ0dZQmdDaEJnQQFONEFBQ1JBcUFZQnNnWWtDHXQARR0MAEcdDABJHQw8dUFZS5oClQEhSkJDNzJBajL1ASRuUEZiSUFRb0FEFfhUa1FEb0pRVTFUTXpvMk1UTTFRTkF3UxFRDFBBX1URDAxBQUFXHQwAWR0MAGEdDABjHQwQZUFDSkEdEMjYAvfpA-ACrZhI6gIwaHR0cHM6Ly90ZXN0Lm5leHgzNjAuaW8vYWRhcHRlci9uYXRpdmUJH_CaaHRtbIADAIgDAZADAJgDFKADAaoDAMAD4KgByAMA2AMA4AMA6AMA-AMDgAQAkgQJL29wZW5ydGIymAQAqAQAsgQMCAAQABgAIAAwADgAuAQAwASA2rgiyAQA0gQOOTMyNSNBTVMzOjYxMzXaBAIIAeAEAPAEvMm-LvoEEgkAAABAPG1IQBEAAACgV8oCQIgFAZgFAKAF______8BBbABqgUkM2MwMjkwYzEtNmU3NS00ZWY3LTllMzctMTdmNWViZjNiZmEzwAUAyQWJFxTwP9IFCQkJDHgAANgFAeAFAfAFmfQh-gUECAAQAJAGAZgGALgGAMEGCSUo8D_QBvUv2gYWChAJERkBAdpg4AYM8gYCCACABwGIBwCgB0HIB6b2BdIHDRVkASYI2gcGAV1oGADgBwDqBwIIAPAHAIoIAhAAlQgAAIA_mAgB&s=ccf63f2e483a37091d2475d895e7cf7c911d1a78&pp=${AUCTION_PRICE}"}]}', + }, + ], + seat: 'appnexus', + }, + ], + ext: { + cookies: [], + }, + }, + }; + + const output = spec.interpretResponse(response); + const expectOutput = [{ + requestId: '23e11d845514bb', + cpm: 10, + width: 1, + height: 1, + creativeId: '97494204', + currency: 'USD', + netRevenue: true, + ttl: 120, + mediaType: 'native', + meta: { + advertiserDomains: [ + 'prebid.org', + ], + demandSource: 'appnexus', + }, + native: { + ortb: { + 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://ams3-ib.adnxs.com/click?AAAAAAAAJEAAAAAAAAAkQAAAAAAAACRAAAAAAAAAJEAAAAAAAAAkQKZS4ZZl5vVbR6p-A-MwnyTZ7QVkAAAAAOLoyQBtJAAAbSQAAAIAAAC8pM8FnPgWAAAAAABVU0QAVVNEAAEAAQBNXQAAAAABAgMCAAAAALoAURe69gAAAAA./bcr=AAAAAAAA8D8=/pp=${AUCTION_PRICE}/cnd=%21JBC72Aj8-LwKELzJvi4YnPFbIAQoADEAAAAAAAAkQDoJQU1TMzo2MTM1QNAwSQAAAAAAAPA_UQAAAAAAAAAAWQAAAAAAAAAAYQAAAAAAAAAAaQAAAAAAAAAAcQAAAAAAAAAAeACJAQAAAAAAAAAA/cca=OTMyNSNBTVMzOjYxMzU=/bn=97062/clickenc=http%3A%2F%2Fprebid.org%2Fdev-docs%2Fshow-native-ads.html', + }, + eventtrackers: [ + { + event: 1, + method: 1, + url: 'https://ams3-ib.adnxs.com/it?an_audit=0&referrer=https%3A%2F%2Ftest.nexx360.io%2Fadapter%2Fnative%2Ftest.html&e=wqT_3QKJCqAJBQAAAwDWAAUBCNnbl6AGEKalhbfZzPn6WxjH1PqbsJzMzyQqNgkAAAECCCRAEQEHEAAAJEAZEQkAIREJACkRCQAxEQmoMOLRpwY47UhA7UhIAlC8yb4uWJzxW2AAaM26dXim9gWAAQGKAQNVU0SSAQEG9F4BmAEBoAEBqAEBsAEAuAECwAEDyAEC0AEJ2AEA4AEA8AEAigIpdWYoJ2EnLCAyNTI5ODg1LCAwKTt1ZigncicsIDk3NDk0MjA0LCAwKTuSAvEDIS0xRDNJQWo4LUx3S0VMekp2aTRZQUNDYzhWc3dBRGdBUUFSSTdVaFE0dEduQmxnQVlQX19fXzhQYUFCd0FYZ0JnQUVCaUFFQmtBRUJtQUVCb0FFQnFBRURzQUVBdVFIenJXcWtBQUFrUU1FQjg2MXFwQUFBSkVESkFYSUtWbWViSmZJXzJRRUFBQUFBQUFEd1AtQUJBUFVCQUFBQUFKZ0NBS0FDQUxVQ0FBQUFBTDBDQUFBQUFNQUNBY2dDQWRBQ0FkZ0NBZUFDQU9nQ0FQZ0NBSUFEQVpnREFib0RDVUZOVXpNNk5qRXpOZUFEMERDSUJBQ1FCQUNZQkFIQkJBQUFBQUFBQUFBQXlRUUFBCQscQUFOZ0VBUEURlSxBQUFDSUJmY3ZxUVUBDQRBQQGoCDdFRgEKCQEMREJCUQkKAQEAeRUoAUwyKAAAWi4oALg0QVhBaEQzd0JhTEQzd0w0QmQyMG1nR0NCZ05WVTBTSUJnQ1FCZ0dZQmdDaEJnQQFONEFBQ1JBcUFZQnNnWWtDHXQARR0MAEcdDABJHQw8dUFZS5oClQEhSkJDNzJBajL1ASRuUEZiSUFRb0FEFfhUa1FEb0pRVTFUTXpvMk1UTTFRTkF3UxFRDFBBX1URDAxBQUFXHQwAWR0MAGEdDABjHQwQZUFDSkEdEMjYAvfpA-ACrZhI6gIwaHR0cHM6Ly90ZXN0Lm5leHgzNjAuaW8vYWRhcHRlci9uYXRpdmUJH_CaaHRtbIADAIgDAZADAJgDFKADAaoDAMAD4KgByAMA2AMA4AMA6AMA-AMDgAQAkgQJL29wZW5ydGIymAQAqAQAsgQMCAAQABgAIAAwADgAuAQAwASA2rgiyAQA0gQOOTMyNSNBTVMzOjYxMzXaBAIIAeAEAPAEvMm-LvoEEgkAAABAPG1IQBEAAACgV8oCQIgFAZgFAKAF______8BBbABqgUkM2MwMjkwYzEtNmU3NS00ZWY3LTllMzctMTdmNWViZjNiZmEzwAUAyQWJFxTwP9IFCQkJDHgAANgFAeAFAfAFmfQh-gUECAAQAJAGAZgGALgGAMEGCSUo8D_QBvUv2gYWChAJERkBAdpg4AYM8gYCCACABwGIBwCgB0HIB6b2BdIHDRVkASYI2gcGAV1oGADgBwDqBwIIAPAHAIoIAhAAlQgAAIA_mAgB&s=ccf63f2e483a37091d2475d895e7cf7c911d1a78&pp=${AUCTION_PRICE}', + }, + ], + }, + }, + }]; + expect(output).to.eql(expectOutput); + }); + }); + + describe('getUserSyncs()', () => { + const response = { body: { cookies: [] } }; + it('Verifies user sync without cookie in bid response', () => { + const syncs = spec.getUserSyncs({}, [response], DEFAULT_OPTIONS.gdprConsent, DEFAULT_OPTIONS.uspConsent); + expect(syncs).to.eql([]); + }); + it('Verifies user sync with cookies in bid response', () => { + response.body.ext = { + cookies: [{ 'type': 'image', 'url': 'http://www.cookie.sync.org/' }] + }; + const syncs = spec.getUserSyncs({}, [response], DEFAULT_OPTIONS.gdprConsent); + const expectedSyncs = [{ type: 'image', url: 'http://www.cookie.sync.org/' }]; + expect(syncs).to.eql(expectedSyncs); + }); + it('Verifies user sync with no bid response', () => { + var syncs = spec.getUserSyncs({}, null, DEFAULT_OPTIONS.gdprConsent, DEFAULT_OPTIONS.uspConsent); + expect(syncs).to.eql([]); + }); + it('Verifies user sync with no bid body response', () => { + let syncs = spec.getUserSyncs({}, [], DEFAULT_OPTIONS.gdprConsent, DEFAULT_OPTIONS.uspConsent); + expect(syncs).to.eql([]); + syncs = spec.getUserSyncs({}, [{}], DEFAULT_OPTIONS.gdprConsent, DEFAULT_OPTIONS.uspConsent); + expect(syncs).to.eql([]); + }); + }); +}); diff --git a/test/spec/modules/ampliffyBidAdapter_spec.js b/test/spec/modules/ampliffyBidAdapter_spec.js index 4c1a170477d..195bbf6e5d8 100644 --- a/test/spec/modules/ampliffyBidAdapter_spec.js +++ b/test/spec/modules/ampliffyBidAdapter_spec.js @@ -6,9 +6,9 @@ import { mergeParams, paramsToQueryString, setCurrentURL } from 'modules/ampliffyBidAdapter.js'; -import {expect} from 'chai'; -import {BANNER, VIDEO} from 'src/mediaTypes'; -import {newBidder} from 'src/adapters/bidderFactory'; +import { expect } from 'chai'; +import { BANNER, VIDEO } from 'src/mediaTypes'; +import { newBidder } from 'src/adapters/bidderFactory'; describe('Ampliffy bid adapter Test', function () { const adapter = newBidder(spec); @@ -240,8 +240,8 @@ describe('Ampliffy bid adapter Test', function () { 'adnetwork': 'ampliffy.com', 'SERVER': 'bidder.ampliffy.com' }, - 'crumbs': {'pubcid': '29844d69-c4e5-4b00-8602-6dd09815363a'}, - 'ortb2Imp': {'ext': {'data': {'pbadslot': 'video1'}}}, + 'crumbs': { 'pubcid': '29844d69-c4e5-4b00-8602-6dd09815363a' }, + 'ortb2Imp': { 'ext': { 'data': { 'pbadslot': 'video1' } } }, 'mediaTypes': { 'video': { 'context': 'instream', @@ -285,8 +285,8 @@ describe('Ampliffy bid adapter Test', function () { 'adnetwork': 'ampliffy.com', 'SERVER': 'bidder.ampliffy.com' }, - 'crumbs': {'pubcid': '29844d69-c4e5-4b00-8602-6dd09815363a'}, - 'ortb2Imp': {'ext': {'data': {'pbadslot': 'video1'}}}, + 'crumbs': { 'pubcid': '29844d69-c4e5-4b00-8602-6dd09815363a' }, + 'ortb2Imp': { 'ext': { 'data': { 'pbadslot': 'video1' } } }, 'mediaTypes': { 'video': { 'context': 'instream', @@ -335,8 +335,8 @@ describe('Ampliffy bid adapter Test', function () { ] } }, - ortb2Imp: {ext: {}}, - params: {placementId: 13144370}, + ortb2Imp: { ext: {} }, + params: { placementId: 13144370 }, sizes: [ [300, 250], [300, 600] @@ -344,7 +344,7 @@ describe('Ampliffy bid adapter Test', function () { src: 'client', transactionId: '103b2b58-6ed1-45e9-9486-c942d6042e3' }, - data: {bidId: '2d40b8dcd02ade'}, + data: { bidId: '2d40b8dcd02ade' }, method: 'GET', url: 'https://test.com', }; diff --git a/test/spec/modules/amxBidAdapter_spec.js b/test/spec/modules/amxBidAdapter_spec.js index 1328a28e438..a44b427a478 100644 --- a/test/spec/modules/amxBidAdapter_spec.js +++ b/test/spec/modules/amxBidAdapter_spec.js @@ -7,7 +7,7 @@ import { server } from 'test/mocks/xhr.js'; import * as utils from 'src/utils.js'; import { getGlobal } from '../../../src/prebidGlobal.js'; -import {getGlobalVarName} from '../../../src/buildOptions.js'; +import { getGlobalVarName } from '../../../src/buildOptions.js'; const sampleRequestId = '82c91e127a9b93e'; const sampleDisplayAd = ``; diff --git a/test/spec/modules/amxIdSystem_spec.js b/test/spec/modules/amxIdSystem_spec.js index eaeb372d730..628220d0aa9 100644 --- a/test/spec/modules/amxIdSystem_spec.js +++ b/test/spec/modules/amxIdSystem_spec.js @@ -1,9 +1,9 @@ import { amxIdSubmodule, storage } from 'modules/amxIdSystem.js'; import { server } from 'test/mocks/xhr.js'; import * as utils from 'src/utils.js'; -import {attachIdSystem} from '../../../modules/userId/index.js'; -import {createEidsArray} from '../../../modules/userId/eids.js'; -import {expect} from 'chai/index.mjs'; +import { attachIdSystem } from '../../../modules/userId/index.js'; +import { createEidsArray } from '../../../modules/userId/eids.js'; +import { expect } from 'chai/index.mjs'; const TEST_ID = '51b561e3-0d82-4aea-8487-093fffca4a3a'; const ERROR_CODES = [404, 501, 500, 403]; diff --git a/test/spec/modules/anonymisedRtdProvider_spec.js b/test/spec/modules/anonymisedRtdProvider_spec.js index 91d3fe1bfd3..1dc7e0df8d4 100644 --- a/test/spec/modules/anonymisedRtdProvider_spec.js +++ b/test/spec/modules/anonymisedRtdProvider_spec.js @@ -1,5 +1,5 @@ -import {config} from 'src/config.js'; -import {getRealTimeData, anonymisedRtdSubmodule, storage} from 'modules/anonymisedRtdProvider.js'; +import { config } from 'src/config.js'; +import { getRealTimeData, anonymisedRtdSubmodule, storage } from 'modules/anonymisedRtdProvider.js'; import { loadExternalScriptStub } from 'test/mocks/adloaderStub.js'; describe('anonymisedRtdProvider', function() { diff --git a/test/spec/modules/anyclipBidAdapter_spec.js b/test/spec/modules/anyclipBidAdapter_spec.js index a25c285e6c4..9ef9ae84bc9 100644 --- a/test/spec/modules/anyclipBidAdapter_spec.js +++ b/test/spec/modules/anyclipBidAdapter_spec.js @@ -1,8 +1,8 @@ -import {expect} from 'chai'; -import {config} from 'src/config.js'; -import {spec} from 'modules/anyclipBidAdapter.js'; -import {deepClone} from 'src/utils'; -import {getBidFloor} from '../../../libraries/xeUtils/bidderUtils.js'; +import { expect } from 'chai'; +import { config } from 'src/config.js'; +import { spec } from 'modules/anyclipBidAdapter.js'; +import { deepClone } from 'src/utils'; +import { getBidFloor } from '../../../libraries/xeUtils/bidderUtils.js'; const ENDPOINT = 'https://prebid.anyclip.com'; @@ -49,12 +49,12 @@ defaultRequestVideo.mediaTypes = { const videoBidderRequest = { bidderCode: 'anyclip', - bids: [{mediaTypes: {video: {}}, bidId: 'qwerty'}] + bids: [{ mediaTypes: { video: {} }, bidId: 'qwerty' }] }; const displayBidderRequest = { bidderCode: 'anyclip', - bids: [{bidId: 'qwerty'}] + bids: [{ bidId: 'qwerty' }] }; describe('anyclipBidAdapter', () => { @@ -109,7 +109,7 @@ describe('anyclipBidAdapter', () => { expect(request).to.have.property('transactionId').and.to.equal(defaultRequest.ortb2Imp.ext.tid); expect(request).to.have.property('tz').and.to.equal(new Date().getTimezoneOffset()); expect(request).to.have.property('bc').and.to.equal(1); - expect(request).to.have.property('banner').and.to.deep.equal({sizes: [[300, 250], [300, 200]]}); + expect(request).to.have.property('banner').and.to.deep.equal({ sizes: [[300, 250], [300, 200]] }); expect(request).to.have.property('gdprConsent').and.to.deep.equal({}); expect(request).to.have.property('userEids').and.to.deep.equal([]); expect(request).to.have.property('usPrivacy').and.to.equal(''); @@ -203,7 +203,7 @@ describe('anyclipBidAdapter', () => { it('should build request with valid bidfloor', function () { const bfRequest = deepClone(defaultRequest); - bfRequest.getFloor = () => ({floor: 5, currency: 'USD'}); + bfRequest.getFloor = () => ({ floor: 5, currency: 'USD' }); const request = JSON.parse(spec.buildRequests([bfRequest], {}).data)[0].env; expect(request).to.have.property('floor').and.to.equal(5); }); @@ -219,8 +219,8 @@ describe('anyclipBidAdapter', () => { it('should build request with extended ids', function () { const idRequest = deepClone(defaultRequest); idRequest.userIdAsEids = [ - {source: 'adserver.org', uids: [{id: 'TTD_ID_FROM_USER_ID_MODULE', atype: 1, ext: {rtiPartner: 'TDID'}}]}, - {source: 'pubcid.org', uids: [{id: 'pubCommonId_FROM_USER_ID_MODULE', atype: 1}]} + { source: 'adserver.org', uids: [{ id: 'TTD_ID_FROM_USER_ID_MODULE', atype: 1, ext: { rtiPartner: 'TDID' } }] }, + { source: 'pubcid.org', uids: [{ id: 'pubCommonId_FROM_USER_ID_MODULE', atype: 1 }] } ]; const request = JSON.parse(spec.buildRequests([idRequest], {}).data)[0]; expect(request).to.have.property('userEids').and.deep.equal(idRequest.userIdAsEids); @@ -272,7 +272,7 @@ describe('anyclipBidAdapter', () => { } }; - const validResponse = spec.interpretResponse(serverResponse, {bidderRequest: displayBidderRequest}); + const validResponse = spec.interpretResponse(serverResponse, { bidderRequest: displayBidderRequest }); const bid = validResponse[0]; expect(validResponse).to.be.an('array').that.is.not.empty; expect(bid.requestId).to.equal('qwerty'); @@ -281,7 +281,7 @@ describe('anyclipBidAdapter', () => { expect(bid.width).to.equal(300); expect(bid.height).to.equal(250); expect(bid.ttl).to.equal(600); - expect(bid.meta).to.deep.equal({advertiserDomains: ['anyclip']}); + expect(bid.meta).to.deep.equal({ advertiserDomains: ['anyclip'] }); }); it('should interpret valid banner response', function () { @@ -302,7 +302,7 @@ describe('anyclipBidAdapter', () => { } }; - const validResponseBanner = spec.interpretResponse(serverResponse, {bidderRequest: displayBidderRequest}); + const validResponseBanner = spec.interpretResponse(serverResponse, { bidderRequest: displayBidderRequest }); const bid = validResponseBanner[0]; expect(validResponseBanner).to.be.an('array').that.is.not.empty; expect(bid.mediaType).to.equal('banner'); @@ -328,7 +328,7 @@ describe('anyclipBidAdapter', () => { } }; - const validResponseBanner = spec.interpretResponse(serverResponse, {bidderRequest: videoBidderRequest}); + const validResponseBanner = spec.interpretResponse(serverResponse, { bidderRequest: videoBidderRequest }); const bid = validResponseBanner[0]; expect(validResponseBanner).to.be.an('array').that.is.not.empty; expect(bid.mediaType).to.equal('video'); @@ -344,12 +344,12 @@ describe('anyclipBidAdapter', () => { }); it('should return empty if sync is not allowed', function () { - const opts = spec.getUserSyncs({iframeEnabled: false, pixelEnabled: false}); + const opts = spec.getUserSyncs({ iframeEnabled: false, pixelEnabled: false }); expect(opts).to.be.an('array').that.is.empty; }); it('should allow iframe sync', function () { - const opts = spec.getUserSyncs({iframeEnabled: true, pixelEnabled: false}, [{ + const opts = spec.getUserSyncs({ iframeEnabled: true, pixelEnabled: false }, [{ body: { data: [{ requestId: 'qwerty', @@ -368,7 +368,7 @@ describe('anyclipBidAdapter', () => { }); it('should allow pixel sync', function () { - const opts = spec.getUserSyncs({iframeEnabled: false, pixelEnabled: true}, [{ + const opts = spec.getUserSyncs({ iframeEnabled: false, pixelEnabled: true }, [{ body: { data: [{ requestId: 'qwerty', @@ -387,7 +387,7 @@ describe('anyclipBidAdapter', () => { }); it('should allow pixel sync and parse consent params', function () { - const opts = spec.getUserSyncs({iframeEnabled: false, pixelEnabled: true}, [{ + const opts = spec.getUserSyncs({ iframeEnabled: false, pixelEnabled: true }, [{ body: { data: [{ requestId: 'qwerty', @@ -411,20 +411,20 @@ describe('anyclipBidAdapter', () => { describe('getBidFloor', function () { it('should return null when getFloor is not a function', () => { - const bid = {getFloor: 2}; + const bid = { getFloor: 2 }; const result = getBidFloor(bid); expect(result).to.be.null; }); it('should return null when getFloor doesnt return an object', () => { - const bid = {getFloor: () => 2}; + const bid = { getFloor: () => 2 }; const result = getBidFloor(bid); expect(result).to.be.null; }); it('should return null when floor is not a number', () => { const bid = { - getFloor: () => ({floor: 'string', currency: 'USD'}) + getFloor: () => ({ floor: 'string', currency: 'USD' }) }; const result = getBidFloor(bid); expect(result).to.be.null; @@ -432,7 +432,7 @@ describe('anyclipBidAdapter', () => { it('should return null when currency is not USD', () => { const bid = { - getFloor: () => ({floor: 5, currency: 'EUR'}) + getFloor: () => ({ floor: 5, currency: 'EUR' }) }; const result = getBidFloor(bid); expect(result).to.be.null; @@ -440,7 +440,7 @@ describe('anyclipBidAdapter', () => { it('should return floor value when everything is correct', () => { const bid = { - getFloor: () => ({floor: 5, currency: 'USD'}) + getFloor: () => ({ floor: 5, currency: 'USD' }) }; const result = getBidFloor(bid); expect(result).to.equal(5); diff --git a/test/spec/modules/apesterBidAdapter_spec.js b/test/spec/modules/apesterBidAdapter_spec.js new file mode 100644 index 00000000000..f33ea20880b --- /dev/null +++ b/test/spec/modules/apesterBidAdapter_spec.js @@ -0,0 +1,774 @@ +import { expect } from 'chai'; +import { + spec as adapter, + createDomain, + storage, +} from 'modules/apesterBidAdapter'; +import * as utils from 'src/utils.js'; +import { version } from 'package.json'; +import { useFakeTimers } from 'sinon'; +import { BANNER, VIDEO } from '../../../src/mediaTypes.js'; +import { config } from '../../../src/config.js'; +import { + hashCode, + extractPID, + extractCID, + extractSubDomain, + getStorageItem, + setStorageItem, + tryParseJSON, + getUniqueDealId, +} from '../../../libraries/vidazooUtils/bidderUtils.js'; +import { getGlobal } from '../../../src/prebidGlobal.js'; + +export const TEST_ID_SYSTEMS = ['britepoolid', 'criteoId', 'id5id', 'idl_env', 'lipb', 'netId', 'parrableId', 'pubcid', 'tdid', 'pubProvidedId']; + +const SUB_DOMAIN = 'exchange'; + +const BID = { + 'bidId': '2d52001cabd527', + 'adUnitCode': 'div-gpt-ad-12345-0', + 'params': { + 'subDomain': SUB_DOMAIN, + 'cId': '59db6b3b4ffaa70004f45cdc', + 'pId': '59ac17c192832d0011283fe3', + 'bidFloor': 0.1, + 'ext': { + 'param1': 'loremipsum', + 'param2': 'dolorsitamet' + } + }, + 'placementCode': 'div-gpt-ad-1460505748561-0', + 'transactionId': 'c881914b-a3b5-4ecf-ad9c-1c2f37c6aabf', + 'sizes': [[300, 250], [300, 600]], + 'bidderRequestId': '1fdb5ff1b6eaa7', + 'auctionId': 'auction_id', + 'bidRequestsCount': 4, + 'bidderRequestsCount': 3, + 'bidderWinsCount': 1, + 'requestId': 'b0777d85-d061-450e-9bc7-260dd54bbb7a', + 'schain': 'a0819c69-005b-41ed-af06-1be1e0aefefc', + 'mediaTypes': [BANNER], + 'ortb2Imp': { + 'ext': { + 'gpid': '0123456789' + } + } +}; + +const VIDEO_BID = { + 'bidId': '2d52001cabd527', + 'adUnitCode': '63550ad1ff6642d368cba59dh5884270560', + 'bidderRequestId': '12a8ae9ada9c13', + 'transactionId': '56e184c6-bde9-497b-b9b9-cf47a61381ee', + 'auctionId': 'auction_id', + 'bidRequestsCount': 4, + 'bidderRequestsCount': 3, + 'bidderWinsCount': 1, + 'schain': 'a0819c69-005b-41ed-af06-1be1e0aefefc', + 'params': { + 'subDomain': SUB_DOMAIN, + 'cId': '635509f7ff6642d368cb9837', + 'pId': '59ac17c192832d0011283fe3', + 'bidFloor': 0.1 + }, + 'sizes': [[545, 307]], + 'mediaTypes': { + 'video': { + 'playerSize': [[545, 307]], + 'context': 'instream', + 'mimes': [ + 'video/mp4', + 'application/javascript' + ], + 'protocols': [2, 3, 5, 6], + 'maxduration': 60, + 'minduration': 0, + 'startdelay': 0, + 'linearity': 1, + 'api': [2], + 'placement': 1 + } + } +} + +const ORTB2_DEVICE = { + sua: { + 'source': 2, + 'platform': { + 'brand': 'Android', + 'version': ['8', '0', '0'] + }, + 'browsers': [ + { 'brand': 'Not_A Brand', 'version': ['99', '0', '0', '0'] }, + { 'brand': 'Google Chrome', 'version': ['109', '0', '5414', '119'] }, + { 'brand': 'Chromium', 'version': ['109', '0', '5414', '119'] } + ], + 'mobile': 1, + 'model': 'SM-G955U', + 'bitness': '64', + 'architecture': '' + }, + w: 980, + h: 1720, + dnt: 0, + ua: 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/125.0.6422.80 Mobile/15E148 Safari/604.1', + language: 'en', + devicetype: 1, + make: 'Apple', + model: 'iPhone 12 Pro Max', + os: 'iOS', + osv: '17.4', + ext: { fiftyonedegrees_deviceId: '17595-133085-133468-18092' }, +}; + +const BIDDER_REQUEST = { + 'gdprConsent': { + 'consentString': 'consent_string', + 'gdprApplies': true + }, + 'gppString': 'gpp_string', + 'gppSid': [7], + 'uspConsent': 'consent_string', + 'refererInfo': { + 'page': 'https://www.greatsite.com', + 'ref': 'https://www.somereferrer.com' + }, + 'ortb2': { + 'site': { + 'content': { + 'language': 'en' + } + }, + 'regs': { + 'gpp': 'gpp_string', + 'gpp_sid': [7], + 'coppa': 0 + }, + 'device': ORTB2_DEVICE, + } +}; + +const SERVER_RESPONSE = { + body: { + cid: 'testcid123', + results: [{ + 'ad': '', + 'price': 0.8, + 'creativeId': '12610997325162499419', + 'exp': 30, + 'width': 300, + 'height': 250, + 'advertiserDomains': ['securepubads.g.doubleclick.net'], + 'cookies': [{ + 'src': 'https://sync.com', + 'type': 'iframe' + }, { + 'src': 'https://sync.com', + 'type': 'img' + }] + }] + } +}; + +const VIDEO_SERVER_RESPONSE = { + body: { + 'cid': '635509f7ff6642d368cb9837', + 'results': [{ + 'ad': '', + 'advertiserDomains': ['bidder.apester.com'], + 'exp': 60, + 'width': 545, + 'height': 307, + 'mediaType': 'video', + 'creativeId': '12610997325162499419', + 'price': 2, + 'cookies': [] + }] + } +}; + +const ORTB2_OBJ = { + "device": ORTB2_DEVICE, + "regs": { "coppa": 0, "gpp": "gpp_string", "gpp_sid": [7] }, + "site": { "content": { "language": "en" } } +}; + +const REQUEST = { + data: { + width: 300, + height: 250, + bidId: '2d52001cabd527' + } +}; + +function getTopWindowQueryParams() { + try { + const parsedUrl = utils.parseUrl(window.top.document.URL, { decodeSearchAsString: true }); + return parsedUrl.search; + } catch (e) { + return ''; + } +} + +describe('apesterBidAdapter', function () { + before(() => config.resetConfig()); + after(() => config.resetConfig()); + + describe('validate 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', function () { + expect(adapter.buildRequests).to.exist.and.to.be.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', function () { + expect(adapter.getUserSyncs).to.exist.and.to.be.a('function'); + }); + + it('exists and is a string', function () { + expect(adapter.code).to.exist.and.to.be.a('string'); + }); + + it('exists and contains media types', function () { + expect(adapter.supportedMediaTypes).to.exist.and.to.be.an('array').with.length(2); + expect(adapter.supportedMediaTypes).to.contain.members([BANNER, VIDEO]); + }); + }); + + describe('validate bid requests', function () { + it('should require cId', function () { + const isValid = adapter.isBidRequestValid({ + params: { + pId: 'pid' + } + }); + expect(isValid).to.be.false; + }); + + it('should require pId', function () { + const isValid = adapter.isBidRequestValid({ + params: { + cId: 'cid' + } + }); + expect(isValid).to.be.false; + }); + + it('should validate correctly', function () { + const isValid = adapter.isBidRequestValid({ + params: { + cId: 'cid', + pId: 'pid' + } + }); + expect(isValid).to.be.true; + }); + }); + + describe('build requests', function () { + let sandbox; + before(function () { + getGlobal().bidderSettings = { + apester: { + storageAllowed: true + } + }; + sandbox = sinon.createSandbox(); + sandbox.stub(Date, 'now').returns(1000); + }); + + it('should build video request', function () { + const hashUrl = hashCode(BIDDER_REQUEST.refererInfo.page); + config.setConfig({ + bidderTimeout: 3000 + }); + const requests = adapter.buildRequests([VIDEO_BID], BIDDER_REQUEST); + expect(requests).to.have.length(1); + expect(requests[0]).to.deep.equal({ + method: 'POST', + url: `${createDomain(SUB_DOMAIN)}/prebid/multi/635509f7ff6642d368cb9837`, + data: { + adUnitCode: '63550ad1ff6642d368cba59dh5884270560', + bidFloor: 0.1, + bidId: '2d52001cabd527', + bidderVersion: adapter.version, + bidderRequestId: '12a8ae9ada9c13', + cb: 1000, + gdpr: 1, + gdprConsent: 'consent_string', + usPrivacy: 'consent_string', + gppString: 'gpp_string', + gppSid: [7], + prebidVersion: version, + transactionId: '56e184c6-bde9-497b-b9b9-cf47a61381ee', + auctionId: 'auction_id', + bidRequestsCount: 4, + bidderRequestsCount: 3, + bidderWinsCount: 1, + bidderTimeout: 3000, + publisherId: '59ac17c192832d0011283fe3', + url: 'https%3A%2F%2Fwww.greatsite.com', + referrer: 'https://www.somereferrer.com', + res: `${window.top.screen.width}x${window.top.screen.height}`, + schain: VIDEO_BID.schain, + sizes: ['545x307'], + sua: { + 'source': 2, + 'platform': { + 'brand': 'Android', + 'version': ['8', '0', '0'] + }, + 'browsers': [ + { 'brand': 'Not_A Brand', 'version': ['99', '0', '0', '0'] }, + { 'brand': 'Google Chrome', 'version': ['109', '0', '5414', '119'] }, + { 'brand': 'Chromium', 'version': ['109', '0', '5414', '119'] } + ], + 'mobile': 1, + 'model': 'SM-G955U', + 'bitness': '64', + 'architecture': '' + }, + device: ORTB2_DEVICE, + uniqueDealId: `${hashUrl}_${Date.now().toString()}`, + uqs: getTopWindowQueryParams(), + mediaTypes: { + video: { + api: [2], + context: 'instream', + linearity: 1, + maxduration: 60, + mimes: [ + 'video/mp4', + 'application/javascript' + ], + minduration: 0, + placement: 1, + playerSize: [[545, 307]], + protocols: [2, 3, 5, 6], + startdelay: 0 + } + }, + gpid: '', + cat: [], + contentLang: 'en', + contentData: [], + isStorageAllowed: true, + pagecat: [], + ortb2: ORTB2_OBJ, + userData: [], + coppa: 0 + } + }); + }); + + it('should build banner request for each size', function () { + const hashUrl = hashCode(BIDDER_REQUEST.refererInfo.page); + config.setConfig({ + bidderTimeout: 3000 + }); + const requests = adapter.buildRequests([BID], BIDDER_REQUEST); + expect(requests).to.have.length(1); + expect(requests[0]).to.deep.equal({ + method: 'POST', + url: `${createDomain(SUB_DOMAIN)}/prebid/multi/59db6b3b4ffaa70004f45cdc`, + data: { + gdprConsent: 'consent_string', + gdpr: 1, + gppString: 'gpp_string', + gppSid: [7], + usPrivacy: 'consent_string', + transactionId: 'c881914b-a3b5-4ecf-ad9c-1c2f37c6aabf', + auctionId: 'auction_id', + bidRequestsCount: 4, + bidderRequestsCount: 3, + bidderWinsCount: 1, + bidderTimeout: 3000, + bidderRequestId: '1fdb5ff1b6eaa7', + sizes: ['300x250', '300x600'], + sua: { + 'source': 2, + 'platform': { + 'brand': 'Android', + 'version': ['8', '0', '0'] + }, + 'browsers': [ + { 'brand': 'Not_A Brand', 'version': ['99', '0', '0', '0'] }, + { 'brand': 'Google Chrome', 'version': ['109', '0', '5414', '119'] }, + { 'brand': 'Chromium', 'version': ['109', '0', '5414', '119'] } + ], + 'mobile': 1, + 'model': 'SM-G955U', + 'bitness': '64', + 'architecture': '' + }, + device: ORTB2_DEVICE, + url: 'https%3A%2F%2Fwww.greatsite.com', + referrer: 'https://www.somereferrer.com', + cb: 1000, + bidFloor: 0.1, + bidId: '2d52001cabd527', + adUnitCode: 'div-gpt-ad-12345-0', + publisherId: '59ac17c192832d0011283fe3', + uniqueDealId: `${hashUrl}_${Date.now().toString()}`, + bidderVersion: adapter.version, + prebidVersion: version, + schain: BID.schain, + res: `${window.top.screen.width}x${window.top.screen.height}`, + mediaTypes: [BANNER], + gpid: '0123456789', + uqs: getTopWindowQueryParams(), + 'ext.param1': 'loremipsum', + 'ext.param2': 'dolorsitamet', + cat: [], + contentLang: 'en', + contentData: [], + isStorageAllowed: true, + pagecat: [], + ortb2Imp: BID.ortb2Imp, + ortb2: ORTB2_OBJ, + userData: [], + coppa: 0 + } + }); + }); + + after(function () { + getGlobal().bidderSettings = {}; + sandbox.restore(); + }); + }); + describe('getUserSyncs', function () { + it('should have valid user sync with iframeEnabled', function () { + const result = adapter.getUserSyncs({ iframeEnabled: true }, [SERVER_RESPONSE]); + + expect(result).to.deep.equal([{ + type: 'iframe', + url: 'https://sync.apester.com/api/sync/iframe/?cid=testcid123&gdpr=0&gdpr_consent=&us_privacy=&coppa=0' + }]); + }); + + it('should have valid user sync with cid on response', function () { + const result = adapter.getUserSyncs({ iframeEnabled: true }, [SERVER_RESPONSE]); + expect(result).to.deep.equal([{ + type: 'iframe', + url: 'https://sync.apester.com/api/sync/iframe/?cid=testcid123&gdpr=0&gdpr_consent=&us_privacy=&coppa=0' + }]); + }); + + it('should have valid user sync with pixelEnabled', function () { + const result = adapter.getUserSyncs({ pixelEnabled: true }, [SERVER_RESPONSE]); + + expect(result).to.deep.equal([{ + 'url': 'https://sync.apester.com/api/sync/image/?cid=testcid123&gdpr=0&gdpr_consent=&us_privacy=&coppa=0', + 'type': 'image' + }]); + }); + + it('should have valid user sync with coppa on response', function () { + config.setConfig({ + coppa: 1 + }); + const result = adapter.getUserSyncs({ iframeEnabled: true }, [SERVER_RESPONSE]); + expect(result).to.deep.equal([{ + type: 'iframe', + url: 'https://sync.apester.com/api/sync/iframe/?cid=testcid123&gdpr=0&gdpr_consent=&us_privacy=&coppa=1' + }]); + }); + + it('should generate url with consent data', function () { + const gdprConsent = { + gdprApplies: true, + consentString: 'consent_string' + }; + const uspConsent = 'usp_string'; + const gppConsent = { + gppString: 'gpp_string', + applicableSections: [7] + } + + const result = adapter.getUserSyncs({ pixelEnabled: true }, [SERVER_RESPONSE], gdprConsent, uspConsent, gppConsent); + + expect(result).to.deep.equal([{ + 'url': 'https://sync.apester.com/api/sync/image/?cid=testcid123&gdpr=1&gdpr_consent=consent_string&us_privacy=usp_string&coppa=1&gpp=gpp_string&gpp_sid=7', + 'type': 'image' + }]); + }); + }); + + 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', function () { + const responses = adapter.interpretResponse({ price: 1, ad: '' }); + expect(responses).to.be.empty; + }); + + 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 banner responses', function () { + const responses = adapter.interpretResponse(SERVER_RESPONSE, REQUEST); + expect(responses).to.have.length(1); + expect(responses[0]).to.deep.equal({ + requestId: '2d52001cabd527', + cpm: 0.8, + width: 300, + height: 250, + creativeId: '12610997325162499419', + currency: 'USD', + netRevenue: true, + ttl: 30, + ad: '', + meta: { + advertiserDomains: ['securepubads.g.doubleclick.net'] + } + }); + }); + + it('should get meta from response metaData', function () { + const serverResponse = utils.deepClone(SERVER_RESPONSE); + serverResponse.body.results[0].metaData = { + advertiserDomains: ['bidder.apester.com'], + agencyName: 'Agency Name', + }; + const responses = adapter.interpretResponse(serverResponse, REQUEST); + expect(responses[0].meta).to.deep.equal({ + advertiserDomains: ['bidder.apester.com'], + agencyName: 'Agency Name' + }); + }); + + it('should return an array of interpreted video responses', function () { + const responses = adapter.interpretResponse(VIDEO_SERVER_RESPONSE, REQUEST); + expect(responses).to.have.length(1); + expect(responses[0]).to.deep.equal({ + requestId: '2d52001cabd527', + cpm: 2, + width: 545, + height: 307, + mediaType: 'video', + creativeId: '12610997325162499419', + currency: 'USD', + netRevenue: true, + ttl: 60, + vastXml: '', + meta: { + advertiserDomains: ['bidder.apester.com'] + } + }); + }); + + it('should take default TTL', function () { + const serverResponse = utils.deepClone(SERVER_RESPONSE); + delete serverResponse.body.results[0].exp; + const responses = adapter.interpretResponse(serverResponse, REQUEST); + expect(responses).to.have.length(1); + expect(responses[0].ttl).to.equal(300); + }); + }); + + describe('user id system', function () { + TEST_ID_SYSTEMS.forEach((idSystemProvider) => { + const id = Date.now().toString(); + const bid = utils.deepClone(BID); + + const userId = (function () { + switch (idSystemProvider) { + case 'lipb': + return { lipbid: id }; + case 'id5id': + return { uid: id }; + default: + return id; + } + })(); + + bid.userId = { + [idSystemProvider]: userId + }; + + it(`should include 'uid.${idSystemProvider}' in request params`, function () { + const requests = adapter.buildRequests([bid], BIDDER_REQUEST); + expect(requests[0].data[`uid.${idSystemProvider}`]).to.equal(id); + }); + }); + // testing bid.userIdAsEids handling + it("should include user ids from bid.userIdAsEids (length=1)", function() { + const bid = utils.deepClone(BID); + bid.userIdAsEids = [ + { + "source": "audigent.com", + "uids": [{ "id": "fakeidi6j6dlc6e" }] + } + ] + const requests = adapter.buildRequests([bid], BIDDER_REQUEST); + expect(requests[0].data['uid.audigent.com']).to.equal("fakeidi6j6dlc6e"); + }) + it("should include user ids from bid.userIdAsEids (length=2)", function() { + const bid = utils.deepClone(BID); + bid.userIdAsEids = [ + { + "source": "audigent.com", + "uids": [{ "id": "fakeidi6j6dlc6e" }] + }, + { + "source": "rwdcntrl.net", + "uids": [{ "id": "fakeid6f35197d5c", "atype": 1 }] + } + ] + const requests = adapter.buildRequests([bid], BIDDER_REQUEST); + expect(requests[0].data['uid.audigent.com']).to.equal("fakeidi6j6dlc6e"); + expect(requests[0].data['uid.rwdcntrl.net']).to.equal("fakeid6f35197d5c"); + }) + // testing user.ext.eid handling + it("should include user ids from user.ext.eid (length=1)", function() { + const bid = utils.deepClone(BID); + bid.user = { + ext: { + eids: [ + { + "source": "pubcid.org", + "uids": [{ "id": "fakeid8888dlc6e" }] + } + ] + } + } + const requests = adapter.buildRequests([bid], BIDDER_REQUEST); + expect(requests[0].data['uid.pubcid.org']).to.equal("fakeid8888dlc6e"); + }) + it("should include user ids from user.ext.eid (length=2)", function() { + const bid = utils.deepClone(BID); + bid.user = { + ext: { + eids: [ + { + "source": "pubcid.org", + "uids": [{ "id": "fakeid8888dlc6e" }] + }, + { + "source": "adserver.org", + "uids": [{ "id": "fakeid495ff1" }] + } + ] + } + } + const requests = adapter.buildRequests([bid], BIDDER_REQUEST); + expect(requests[0].data['uid.pubcid.org']).to.equal("fakeid8888dlc6e"); + expect(requests[0].data['uid.adserver.org']).to.equal("fakeid495ff1"); + }) + }); + + describe('alternate param names extractors', function () { + it('should return undefined when param not supported', function () { + const cid = extractCID({ 'c_id': '1' }); + const pid = extractPID({ 'p_id': '1' }); + const subDomain = extractSubDomain({ 'sub_domain': 'prebid' }); + expect(cid).to.be.undefined; + expect(pid).to.be.undefined; + expect(subDomain).to.be.undefined; + }); + + it('should return value when param supported', function () { + const cid = extractCID({ 'cID': '1' }); + const pid = extractPID({ 'Pid': '2' }); + const subDomain = extractSubDomain({ 'subDOMAIN': 'prebid' }); + expect(cid).to.be.equal('1'); + expect(pid).to.be.equal('2'); + expect(subDomain).to.be.equal('prebid'); + }); + }); + + describe('unique deal id', function () { + before(function () { + getGlobal().bidderSettings = { + apester: { + storageAllowed: true + } + }; + }); + after(function () { + getGlobal().bidderSettings = {}; + }); + const key = 'myKey'; + let uniqueDealId; + beforeEach(() => { + uniqueDealId = getUniqueDealId(storage, key, 0); + }) + + it('should get current unique deal id', function (done) { + // waiting some time so `now` will become past + setTimeout(() => { + const current = getUniqueDealId(storage, key); + expect(current).to.be.equal(uniqueDealId); + done(); + }, 200); + }); + + it('should get new unique deal id on expiration', function (done) { + setTimeout(() => { + const current = getUniqueDealId(storage, key, 100); + expect(current).to.not.be.equal(uniqueDealId); + done(); + }, 200) + }); + }); + + describe('storage utils', function () { + before(function () { + getGlobal().bidderSettings = { + apester: { + storageAllowed: true + } + }; + }); + after(function () { + getGlobal().bidderSettings = {}; + }); + it('should get value from storage with create param', function () { + const now = Date.now(); + const clock = useFakeTimers({ + shouldAdvanceTime: true, + now + }); + setStorageItem(storage, 'myKey', 2020); + const { value, created } = getStorageItem(storage, 'myKey'); + expect(created).to.be.equal(now); + expect(value).to.be.equal(2020); + expect(typeof value).to.be.equal('number'); + expect(typeof created).to.be.equal('number'); + clock.restore(); + }); + + it('should get external stored value', function () { + const value = 'superman' + window.localStorage.setItem('myExternalKey', value); + const item = getStorageItem(storage, 'myExternalKey'); + expect(item).to.be.equal(value); + }); + + it('should parse JSON value', function () { + const data = JSON.stringify({ event: 'send' }); + const { event } = tryParseJSON(data); + expect(event).to.be.equal('send'); + }); + + it('should get original value on parse fail', function () { + const value = 21; + const parsed = tryParseJSON(value); + expect(typeof parsed).to.be.equal('number'); + expect(parsed).to.be.equal(value); + }); + }); +}); diff --git a/test/spec/modules/appStockSSPBidAdapter_spec.js b/test/spec/modules/appStockSSPBidAdapter_spec.js index 7ee7d739dd3..64dac5fe8e7 100644 --- a/test/spec/modules/appStockSSPBidAdapter_spec.js +++ b/test/spec/modules/appStockSSPBidAdapter_spec.js @@ -499,7 +499,7 @@ describe('AppStockSSPBidAdapter', function () { const syncData = spec.getUserSyncs({}, {}, { consentString: 'ALL', gdprApplies: true, - }, {}); + }, undefined); expect(syncData).to.be.an('array').which.is.not.empty; expect(syncData[0]).to.be.an('object') expect(syncData[0].type).to.be.a('string') @@ -508,9 +508,7 @@ describe('AppStockSSPBidAdapter', function () { expect(syncData[0].url).to.equal('https://csync.al-ad.com/image?pbjs=1&gdpr=1&gdpr_consent=ALL&coppa=0') }); it('Should return array of objects with proper sync config , include CCPA', function() { - const syncData = spec.getUserSyncs({}, {}, {}, { - consentString: '1---' - }); + const syncData = spec.getUserSyncs({}, {}, {}, '1---'); expect(syncData).to.be.an('array').which.is.not.empty; expect(syncData[0]).to.be.an('object') expect(syncData[0].type).to.be.a('string') @@ -519,7 +517,7 @@ describe('AppStockSSPBidAdapter', function () { expect(syncData[0].url).to.equal('https://csync.al-ad.com/image?pbjs=1&ccpa_consent=1---&coppa=0') }); it('Should return array of objects with proper sync config , include GPP', function() { - const syncData = spec.getUserSyncs({}, {}, {}, {}, { + const syncData = spec.getUserSyncs({}, {}, {}, undefined, { gppString: 'abc123', applicableSections: [8] }); diff --git a/test/spec/modules/appierAnalyticsAdapter_spec.js b/test/spec/modules/appierAnalyticsAdapter_spec.js index e380672b73c..3e7ad334384 100644 --- a/test/spec/modules/appierAnalyticsAdapter_spec.js +++ b/test/spec/modules/appierAnalyticsAdapter_spec.js @@ -2,7 +2,7 @@ import { appierAnalyticsAdapter, getCpmInUsd, parseBidderCode, parseAdUnitCode, ANALYTICS_VERSION, BIDDER_STATUS } from 'modules/appierAnalyticsAdapter.js'; -import {expect} from 'chai'; +import { expect } from 'chai'; const events = require('src/events'); const constants = require('src/constants.js'); @@ -122,7 +122,7 @@ describe('Appier Prebid AnalyticsAdapter Testing', function () { }); describe('#getCachedAuction()', function() { - const existing = {timeoutBids: [{}]}; + const existing = { timeoutBids: [{}] }; appierAnalyticsAdapter.cachedAuctions['test_auction_id'] = existing; it('should get the existing cached object if it exists', function() { diff --git a/test/spec/modules/appierBidAdapter_spec.js b/test/spec/modules/appierBidAdapter_spec.js index 93f95fbd182..0b295b7c609 100644 --- a/test/spec/modules/appierBidAdapter_spec.js +++ b/test/spec/modules/appierBidAdapter_spec.js @@ -137,7 +137,7 @@ describe('AppierAdapter', function () { describe('getApiServer', function() { it('should use the server specified by setConfig(appier.server)', function() { config.setConfig({ - 'appier': {'server': 'fake_server'} + 'appier': { 'server': 'fake_server' } }); const server = spec.getApiServer(); @@ -147,7 +147,7 @@ describe('AppierAdapter', function () { it('should retrieve a farm specific hostname if server is not specpfied', function() { config.setConfig({ - 'appier': {'farm': 'tw'} + 'appier': { 'farm': 'tw' } }); const server = spec.getApiServer(); @@ -157,7 +157,7 @@ describe('AppierAdapter', function () { it('if farm is not recognized, use the default farm', function() { config.setConfig({ - 'appier': {'farm': 'no_this_farm'} + 'appier': { 'farm': 'no_this_farm' } }); const server = spec.getApiServer(); diff --git a/test/spec/modules/appnexusBidAdapter_spec.js b/test/spec/modules/appnexusBidAdapter_spec.js index f4a00bfb264..9ac4e762d9b 100644 --- a/test/spec/modules/appnexusBidAdapter_spec.js +++ b/test/spec/modules/appnexusBidAdapter_spec.js @@ -5,7 +5,7 @@ import { auctionManager } from 'src/auctionManager.js'; import { deepClone } from 'src/utils.js'; import * as utils from 'src/utils.js'; import { config } from 'src/config.js'; -import {getGlobal} from '../../../src/prebidGlobal.js'; +import { getGlobal } from '../../../src/prebidGlobal.js'; const ENDPOINT = 'https://ib.adnxs.com/ut/v3/prebid'; @@ -536,185 +536,6 @@ describe('AppNexusAdapter', function () { playback_method: 2 }); }); - - it('should duplicate adpod placements into batches and set correct maxduration', function () { - const bidRequest = Object.assign({}, - bidRequests[0], - { - 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 () { - const 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); - }); - - it('should duplicate adpod placements when requireExactDuration is set', function () { - const 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('should set durations for placements when requireExactDuration is set and numbers are uneven', function () { - const bidRequest = Object.assign({}, - bidRequests[0], - { - 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); - - 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 () { - const 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('should contain hb_source value for adpod', function () { - const bidRequest = Object.assign({}, - bidRequests[0], - { - params: { placementId: '14542875' } - }, - { - mediaTypes: { - video: { - context: 'adpod', - playerSize: [640, 480], - adPodDurationSec: 300, - durationRangeSec: [15, 30], - } - } - } - ); - const request = spec.buildRequests([bidRequest])[0]; - const payload = JSON.parse(request.data); - expect(payload.tags[0].hb_source).to.deep.equal(7); - }); } // VIDEO it('sends bid request to ENDPOINT via POST', function () { @@ -817,21 +638,6 @@ describe('AppNexusAdapter', function () { expect(payload.tags[0].hb_source).to.deep.equal(1); }); - it('adds brand_category_exclusion to request when set', function () { - const 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('adds auction level keywords and ortb2 keywords to request when set', function () { const bidRequest = Object.assign({}, bidRequests[0]); sinon @@ -2024,46 +1830,6 @@ describe('AppNexusAdapter', function () { expect(result[0]).to.have.property('vastImpUrl'); expect(result[0]).to.have.property('mediaType', 'video'); }); - - it('handles adpod responses', function () { - const response = { - 'tags': [{ - 'uuid': '84ab500420319d', - 'ads': [{ - 'ad_type': 'video', - 'brand_category_id': 10, - 'cpm': 0.500000, - 'notify_url': 'imptracker.com', - 'rtb': { - 'video': { - 'asset_url': 'https://sample.vastURL.com/here/adpod', - 'duration_ms': 30000, - } - }, - 'viewability': { - 'config': '' - } - }] - }] - }; - - const bidderRequest = { - bids: [{ - bidId: '84ab500420319d', - adUnitCode: 'code', - mediaTypes: { - video: { - context: 'adpod' - } - } - }] - }; - - const 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); - }); } if (FEATURES.NATIVE) { @@ -2377,33 +2143,6 @@ describe('AppNexusAdapter', function () { bidderRequest.bids[0].renderer.options ); }); - - it('should add deal_priority and deal_code', function () { - const responseWithDeal = deepClone(response); - responseWithDeal.tags[0].ads[0].ad_type = 'video'; - responseWithDeal.tags[0].ads[0].deal_priority = 5; - responseWithDeal.tags[0].ads[0].deal_code = '123'; - responseWithDeal.tags[0].ads[0].rtb.video = { - duration_ms: 1500, - player_width: 640, - player_height: 340, - }; - - const bidderRequest = { - bids: [{ - bidId: '3db3773286ee59', - adUnitCode: 'code', - mediaTypes: { - video: { - context: 'adpod' - } - } - }] - } - const result = spec.interpretResponse({ body: responseWithDeal }, { bidderRequest }); - expect(Object.keys(result[0].appnexus)).to.include.members(['buyerMemberId', 'dealPriority', 'dealCode']); - expect(result[0].video.dealTier).to.equal(5); - }); } it('should add advertiser id', function () { diff --git a/test/spec/modules/apsBidAdapter_spec.js b/test/spec/modules/apsBidAdapter_spec.js index 429572b0343..8bf37db5be8 100644 --- a/test/spec/modules/apsBidAdapter_spec.js +++ b/test/spec/modules/apsBidAdapter_spec.js @@ -213,6 +213,30 @@ describe('apsBidAdapter', () => { expect(result.data.ext.account).to.equal(undefined); }); + it('should include agerange in request ext when configured', () => { + bidderRequest.ortb2 = { + regs: { + ext: { agerange: 2 }, + }, + }; + + const result = spec.buildRequests(bidRequests, bidderRequest); + + expect(result.data.regs.ext.agerange).to.equal(2); + }); + + it('should include agerange in request ext when configured, even when 0', () => { + bidderRequest.ortb2 = { + regs: { + ext: { agerange: 0 }, + }, + }; + + const result = spec.buildRequests(bidRequests, bidderRequest); + + expect(result.data.regs.ext.agerange).to.equal(0); + }); + it('should remove sensitive geo data from device', () => { bidderRequest.ortb2 = { device: { diff --git a/test/spec/modules/apstreamBidAdapter_spec.js b/test/spec/modules/apstreamBidAdapter_spec.js index de13e45b6b9..723ac11b962 100644 --- a/test/spec/modules/apstreamBidAdapter_spec.js +++ b/test/spec/modules/apstreamBidAdapter_spec.js @@ -1,9 +1,9 @@ // jshint esversion: 6, es3: false, node: true -import {assert, expect} from 'chai'; -import {config} from 'src/config.js'; -import {spec} from 'modules/apstreamBidAdapter.js'; +import { assert, expect } from 'chai'; +import { config } from 'src/config.js'; +import { spec } from 'modules/apstreamBidAdapter.js'; import * as utils from 'src/utils.js'; -import {getGlobal} from '../../../src/prebidGlobal.js'; +import { getGlobal } from '../../../src/prebidGlobal.js'; const validBidRequests = [{ bidId: 'bidId', @@ -60,7 +60,7 @@ describe('AP Stream adapter', function() { }); it('should return false when publisherId is configured and two media types', function() { - bid.mediaTypes.video = {sizes: [300, 250]}; + bid.mediaTypes.video = { sizes: [300, 250] }; assert.isFalse(spec.isBidRequestValid(bid)) }); @@ -76,7 +76,7 @@ describe('AP Stream adapter', function() { const request = spec.buildRequests(validBidRequests, { })[0]; assert.equal(request.method, 'GET'); - assert.deepEqual(request.options, {withCredentials: false}); + assert.deepEqual(request.options, { withCredentials: false }); assert.ok(request.data); }); diff --git a/test/spec/modules/asoBidAdapter_spec.js b/test/spec/modules/asoBidAdapter_spec.js index e0fffee68d8..304d7add0ba 100644 --- a/test/spec/modules/asoBidAdapter_spec.js +++ b/test/spec/modules/asoBidAdapter_spec.js @@ -1,9 +1,9 @@ -import {expect} from 'chai'; -import {spec} from 'modules/asoBidAdapter.js'; -import {BANNER, NATIVE, VIDEO} from 'src/mediaTypes.js'; -import {OUTSTREAM} from 'src/video.js'; -import {addFPDToBidderRequest} from '../../helpers/fpd.js'; -import {parseUrl} from '../../../src/utils.js'; +import { expect } from 'chai'; +import { spec } from 'modules/asoBidAdapter.js'; +import { BANNER, NATIVE, VIDEO } from 'src/mediaTypes.js'; +import { OUTSTREAM } from 'src/video.js'; +import { addFPDToBidderRequest } from '../../helpers/fpd.js'; +import { parseUrl } from '../../../src/utils.js'; import 'modules/priceFloors.js'; import 'modules/consentManagementTcf.js'; @@ -381,8 +381,8 @@ describe('Adserver.Online bidding adapter', function () { crid: 123, adm: JSON.stringify({ assets: [ - {id: 0, title: {text: 'Title'}}, - {id: 1, img: {type: 3, url: 'https://img'}}, + { id: 0, title: { text: 'Title' } }, + { id: 1, img: { type: 3, url: 'https://img' } }, ], }), adomain: ['example.com'], diff --git a/test/spec/modules/asteriobidAnalyticsAdapter_spec.js b/test/spec/modules/asteriobidAnalyticsAdapter_spec.js index e8a1cca534b..54106f4ed48 100644 --- a/test/spec/modules/asteriobidAnalyticsAdapter_spec.js +++ b/test/spec/modules/asteriobidAnalyticsAdapter_spec.js @@ -1,8 +1,8 @@ -import asteriobidAnalytics, {storage} from 'modules/asteriobidAnalyticsAdapter.js'; -import {expect} from 'chai'; -import {server} from 'test/mocks/xhr.js'; +import asteriobidAnalytics, { storage } from 'modules/asteriobidAnalyticsAdapter.js'; +import { expect } from 'chai'; +import { server } from 'test/mocks/xhr.js'; import * as utils from 'src/utils.js'; -import {expectEvents} from '../../helpers/analytics.js'; +import { expectEvents } from '../../helpers/analytics.js'; import { EVENTS } from 'src/constants.js'; const events = require('src/events'); diff --git a/test/spec/modules/atsAnalyticsAdapter_spec.js b/test/spec/modules/atsAnalyticsAdapter_spec.js index c294a5e08d0..43c9f612256 100644 --- a/test/spec/modules/atsAnalyticsAdapter_spec.js +++ b/test/spec/modules/atsAnalyticsAdapter_spec.js @@ -1,12 +1,12 @@ -import atsAnalyticsAdapter, {parseBrowser, analyticsUrl, bidRequestedHandler} from '../../../modules/atsAnalyticsAdapter.js'; +import atsAnalyticsAdapter, { parseBrowser, analyticsUrl, bidRequestedHandler } from '../../../modules/atsAnalyticsAdapter.js'; import { expect } from 'chai'; import adapterManager from 'src/adapterManager.js'; -import {server} from '../../mocks/xhr.js'; +import { server } from '../../mocks/xhr.js'; -import {getCoreStorageManager, getStorageManager} from '../../../src/storageManager.js'; +import { getCoreStorageManager, getStorageManager } from '../../../src/storageManager.js'; -import {EVENTS} from 'src/constants.js'; -import {getGlobal} from '../../../src/prebidGlobal.js'; +import { EVENTS } from 'src/constants.js'; +import { getGlobal } from '../../../src/prebidGlobal.js'; const utils = require('src/utils'); const events = require('src/events'); @@ -299,7 +299,7 @@ describe('ats analytics adapter', function () { 'bidId': '30c77d079cdf17', 'userIdAsEids': [{ 'source': 'liveramp.com', - 'uids': [{'id': 'AmThEbO1ssIWjrNdU4noT4ZFBILSVBBYHbipOYt_JP40e5nZdXns2g'}] + 'uids': [{ 'id': 'AmThEbO1ssIWjrNdU4noT4ZFBILSVBBYHbipOYt_JP40e5nZdXns2g' }] }] }], 'auctionId': 'a5b849e5-87d7-4205-8300-d063084fcfb7' @@ -337,7 +337,7 @@ describe('ats analytics adapter', function () { 'bidId': '30c77d079cdf17', 'userIdAsEids': [{ 'source': 'id5-sync.com', - 'uids': [{'id': 'some-other-id'}] + 'uids': [{ 'id': 'some-other-id' }] }] }], 'auctionId': 'a5b849e5-87d7-4205-8300-d063084fcfb7' @@ -461,15 +461,15 @@ describe('ats analytics adapter', function () { 'userIdAsEids': [ { 'source': 'id5-sync.com', - 'uids': [{'id': 'id5-value'}] + 'uids': [{ 'id': 'id5-value' }] }, { 'source': 'liveramp.com', - 'uids': [{'id': 'liveramp-value'}] + 'uids': [{ 'id': 'liveramp-value' }] }, { 'source': 'criteo.com', - 'uids': [{'id': 'criteo-value'}] + 'uids': [{ 'id': 'criteo-value' }] } ] }], diff --git a/test/spec/modules/automatadAnalyticsAdapter_spec.js b/test/spec/modules/automatadAnalyticsAdapter_spec.js index 9a7e6b13a6e..4f203da09de 100644 --- a/test/spec/modules/automatadAnalyticsAdapter_spec.js +++ b/test/spec/modules/automatadAnalyticsAdapter_spec.js @@ -1,7 +1,7 @@ import * as events from 'src/events'; import * as utils from 'src/utils.js'; -import spec, {self as exports} from 'modules/automatadAnalyticsAdapter.js'; +import spec, { self as exports } from 'modules/automatadAnalyticsAdapter.js'; import { EVENTS } from 'src/constants.js'; import { expect } from 'chai'; @@ -154,47 +154,47 @@ describe('Automatad Analytics Adapter', () => { }) it('Should call the auctionInitHandler when the auction init event is fired', () => { - events.emit(AUCTION_INIT, {type: AUCTION_INIT}) + events.emit(AUCTION_INIT, { type: AUCTION_INIT }) expect(global.window.atmtdAnalytics.auctionInitHandler.called).to.equal(true) }); it('Should call the bidRequested when the bidRequested event is fired', () => { - events.emit(BID_REQUESTED, {type: BID_REQUESTED}) + events.emit(BID_REQUESTED, { type: BID_REQUESTED }) expect(global.window.atmtdAnalytics.bidRequestedHandler.called).to.equal(true) }); it('Should call the bidRejected when the bidRejected event is fired', () => { - events.emit(BID_REJECTED, {type: BID_REJECTED}) + events.emit(BID_REJECTED, { type: BID_REJECTED }) expect(global.window.atmtdAnalytics.bidRejectedHandler.called).to.equal(true) }); it('Should call the bidResponseHandler when the bidResponse event is fired', () => { - events.emit(BID_RESPONSE, {type: BID_RESPONSE}) + events.emit(BID_RESPONSE, { type: BID_RESPONSE }) expect(global.window.atmtdAnalytics.bidResponseHandler.called).to.equal(true) }); it('Should call the bidderDoneHandler when the bidderDone event is fired', () => { - events.emit(BIDDER_DONE, {type: BIDDER_DONE}) + events.emit(BIDDER_DONE, { type: BIDDER_DONE }) expect(global.window.atmtdAnalytics.bidderDoneHandler.called).to.equal(true) }); it('Should call the bidWonHandler when the bidWon event is fired', () => { - events.emit(BID_WON, {type: BID_WON}) + events.emit(BID_WON, { type: BID_WON }) expect(global.window.atmtdAnalytics.bidWonHandler.called).to.equal(true) }); it('Should call the noBidHandler when the noBid event is fired', () => { - events.emit(NO_BID, {type: NO_BID}) + events.emit(NO_BID, { type: NO_BID }) expect(global.window.atmtdAnalytics.noBidHandler.called).to.equal(true) }); it('Should call the bidTimeoutHandler when the bidTimeout event is fired', () => { - events.emit(BID_TIMEOUT, {type: BID_TIMEOUT}) + events.emit(BID_TIMEOUT, { type: BID_TIMEOUT }) expect(global.window.atmtdAnalytics.bidderTimeoutHandler.called).to.equal(true) }); it('Should call the auctionDebugHandler when the auctionDebug event is fired', () => { - events.emit(AUCTION_DEBUG, {type: AUCTION_DEBUG}) + events.emit(AUCTION_DEBUG, { type: AUCTION_DEBUG }) expect(global.window.atmtdAnalytics.auctionDebugHandler.called).to.equal(true) }); }); @@ -223,7 +223,7 @@ describe('Automatad Analytics Adapter', () => { }) it('Should push to the que when the auctionInit event is fired', () => { - events.emit(AUCTION_INIT, {type: AUCTION_INIT}) + events.emit(AUCTION_INIT, { type: AUCTION_INIT }) expect(exports.__atmtdAnalyticsQueue.push.called).to.equal(true) expect(exports.__atmtdAnalyticsQueue).to.be.an('array').to.have.lengthOf(1) expect(exports.__atmtdAnalyticsQueue[0]).to.have.lengthOf(2) @@ -232,7 +232,7 @@ describe('Automatad Analytics Adapter', () => { }); it('Should push to the que when the bidResponse event is fired', () => { - events.emit(BID_RESPONSE, {type: BID_RESPONSE}) + events.emit(BID_RESPONSE, { type: BID_RESPONSE }) expect(exports.__atmtdAnalyticsQueue.push.called).to.equal(true) expect(exports.__atmtdAnalyticsQueue).to.be.an('array').to.have.lengthOf(1) expect(exports.__atmtdAnalyticsQueue[0]).to.have.lengthOf(2) @@ -241,7 +241,7 @@ describe('Automatad Analytics Adapter', () => { }); it('Should push to the que when the bidRequested event is fired', () => { - events.emit(BID_REQUESTED, {type: BID_REQUESTED}) + events.emit(BID_REQUESTED, { type: BID_REQUESTED }) expect(exports.__atmtdAnalyticsQueue.push.called).to.equal(true) expect(exports.__atmtdAnalyticsQueue).to.be.an('array').to.have.lengthOf(1) expect(exports.__atmtdAnalyticsQueue[0]).to.have.lengthOf(2) @@ -250,7 +250,7 @@ describe('Automatad Analytics Adapter', () => { }); it('Should push to the que when the bidRejected event is fired', () => { - events.emit(BID_REJECTED, {type: BID_REJECTED}) + events.emit(BID_REJECTED, { type: BID_REJECTED }) expect(exports.__atmtdAnalyticsQueue.push.called).to.equal(true) expect(exports.__atmtdAnalyticsQueue).to.be.an('array').to.have.lengthOf(1) expect(exports.__atmtdAnalyticsQueue[0]).to.have.lengthOf(2) @@ -259,7 +259,7 @@ describe('Automatad Analytics Adapter', () => { }); it('Should push to the que when the bidderDone event is fired', () => { - events.emit(BIDDER_DONE, {type: BIDDER_DONE}) + events.emit(BIDDER_DONE, { type: BIDDER_DONE }) expect(exports.__atmtdAnalyticsQueue.push.called).to.equal(true) expect(exports.__atmtdAnalyticsQueue).to.be.an('array').to.have.lengthOf(1) expect(exports.__atmtdAnalyticsQueue[0]).to.have.lengthOf(2) @@ -268,7 +268,7 @@ describe('Automatad Analytics Adapter', () => { }); it('Should push to the que when the bidWon event is fired', () => { - events.emit(BID_WON, {type: BID_WON}) + events.emit(BID_WON, { type: BID_WON }) expect(exports.__atmtdAnalyticsQueue.push.called).to.equal(true) expect(exports.__atmtdAnalyticsQueue).to.be.an('array').to.have.lengthOf(1) expect(exports.__atmtdAnalyticsQueue[0]).to.have.lengthOf(2) @@ -277,7 +277,7 @@ describe('Automatad Analytics Adapter', () => { }); it('Should push to the que when the noBid event is fired', () => { - events.emit(NO_BID, {type: NO_BID}) + events.emit(NO_BID, { type: NO_BID }) expect(exports.__atmtdAnalyticsQueue.push.called).to.equal(true) expect(exports.__atmtdAnalyticsQueue).to.be.an('array').to.have.lengthOf(1) expect(exports.__atmtdAnalyticsQueue[0]).to.have.lengthOf(2) @@ -286,7 +286,7 @@ describe('Automatad Analytics Adapter', () => { }); it('Should push to the que when the auctionDebug is fired', () => { - events.emit(AUCTION_DEBUG, {type: AUCTION_DEBUG}) + events.emit(AUCTION_DEBUG, { type: AUCTION_DEBUG }) expect(exports.__atmtdAnalyticsQueue.push.called).to.equal(true) expect(exports.__atmtdAnalyticsQueue).to.be.an('array').to.have.lengthOf(1) expect(exports.__atmtdAnalyticsQueue[0]).to.have.lengthOf(2) @@ -295,7 +295,7 @@ describe('Automatad Analytics Adapter', () => { }); it('Should push to the que when the bidderTimeout event is fired', () => { - events.emit(BID_TIMEOUT, {type: BID_TIMEOUT}) + events.emit(BID_TIMEOUT, { type: BID_TIMEOUT }) expect(exports.__atmtdAnalyticsQueue.push.called).to.equal(true) expect(exports.__atmtdAnalyticsQueue).to.be.an('array').to.have.lengthOf(1) expect(exports.__atmtdAnalyticsQueue[0]).to.have.lengthOf(2) @@ -331,7 +331,7 @@ describe('Automatad Analytics Adapter', () => { }) it('Should push to the que when the auctionInit event is fired and push to the que even after SDK has loaded after auctionInit event', () => { - events.emit(BID_RESPONSE, {type: BID_RESPONSE}) + events.emit(BID_RESPONSE, { type: BID_RESPONSE }) expect(exports.__atmtdAnalyticsQueue.push.called).to.equal(true) expect(exports.__atmtdAnalyticsQueue).to.be.an('array').to.have.lengthOf(1) expect(exports.__atmtdAnalyticsQueue[0]).to.have.lengthOf(2) @@ -340,7 +340,7 @@ describe('Automatad Analytics Adapter', () => { expect(exports.qBeingUsed).to.equal(true) expect(exports.qTraversalComplete).to.equal(undefined) global.window.atmtdAnalytics = obj - events.emit(BID_RESPONSE, {type: BID_RESPONSE}) + events.emit(BID_RESPONSE, { type: BID_RESPONSE }) expect(exports.__atmtdAnalyticsQueue.push.calledTwice).to.equal(true) expect(exports.__atmtdAnalyticsQueue).to.be.an('array').to.have.lengthOf(2) expect(exports.__atmtdAnalyticsQueue[1]).to.have.lengthOf(2) @@ -352,8 +352,8 @@ describe('Automatad Analytics Adapter', () => { it('Should push to the que when the auctionInit event is fired and push to the analytics adapter handler after the que is processed', () => { expect(exports.qBeingUsed).to.equal(undefined) - events.emit(AUCTION_INIT, {type: AUCTION_INIT}) - global.window.atmtdAnalytics = {...obj} + events.emit(AUCTION_INIT, { type: AUCTION_INIT }) + global.window.atmtdAnalytics = { ...obj } const handlers = global.window.atmtdAnalytics Object.keys(handlers).forEach((handler) => global.window.atmtdAnalytics[handler].resetHistory()) expect(exports.__atmtdAnalyticsQueue.push.called).to.equal(true) @@ -367,13 +367,13 @@ describe('Automatad Analytics Adapter', () => { clock.tick(2000) expect(exports.qBeingUsed).to.equal(true) expect(exports.qTraversalComplete).to.equal(undefined) - events.emit(NO_BID, {type: NO_BID}) + events.emit(NO_BID, { type: NO_BID }) expect(exports.__atmtdAnalyticsQueue).to.be.an('array').to.have.lengthOf(2) expect(exports.__atmtdAnalyticsQueue.push.calledTwice).to.equal(true) clock.tick(1500) expect(exports.qBeingUsed).to.equal(false) expect(exports.qTraversalComplete).to.equal(true) - events.emit(BID_RESPONSE, {type: BID_RESPONSE}) + events.emit(BID_RESPONSE, { type: BID_RESPONSE }) expect(exports.__atmtdAnalyticsQueue).to.be.an('array').to.have.lengthOf(2) expect(exports.__atmtdAnalyticsQueue.push.calledTwice).to.equal(true) expect(exports.__atmtdAnalyticsQueue.push.calledThrice).to.equal(false) @@ -419,7 +419,7 @@ describe('Automatad Analytics Adapter', () => { it('Should retry processing auctionInit in certain intervals', () => { expect(exports.queuePointer).to.equal(0) expect(exports.retryCount).to.equal(0) - const que = [[AUCTION_INIT, {type: AUCTION_INIT}]] + const que = [[AUCTION_INIT, { type: AUCTION_INIT }]] exports.__atmtdAnalyticsQueue.push(que[0]) exports.processEvents() expect(exports.prettyLog.getCall(0).args[0]).to.equal('status') @@ -451,7 +451,7 @@ describe('Automatad Analytics Adapter', () => { it('Should retry processing slotRenderEnded in certain intervals', () => { expect(exports.queuePointer).to.equal(0) expect(exports.retryCount).to.equal(0) - const que = [['slotRenderEnded', {type: 'slotRenderEnded'}]] + const que = [['slotRenderEnded', { type: 'slotRenderEnded' }]] exports.__atmtdAnalyticsQueue.push(que[0]) exports.processEvents() expect(exports.prettyLog.getCall(0).args[0]).to.equal('status') @@ -483,7 +483,7 @@ describe('Automatad Analytics Adapter', () => { it('Should retry processing impressionViewable in certain intervals', () => { expect(exports.queuePointer).to.equal(0) expect(exports.retryCount).to.equal(0) - const que = [['impressionViewable', {type: 'impressionViewable'}]] + const que = [['impressionViewable', { type: 'impressionViewable' }]] exports.__atmtdAnalyticsQueue.push(que[0]) exports.processEvents() expect(exports.prettyLog.getCall(0).args[0]).to.equal('status') @@ -545,17 +545,17 @@ describe('Automatad Analytics Adapter', () => { exports.retryCount = 0; exports.queuePointer = 0; exports.__atmtdAnalyticsQueue = [ - [AUCTION_INIT, {type: AUCTION_INIT}], - [BID_RESPONSE, {type: BID_RESPONSE}], - [BID_REQUESTED, {type: BID_REQUESTED}], - [BID_REJECTED, {type: BID_REJECTED}], - [NO_BID, {type: NO_BID}], - [BID_WON, {type: BID_WON}], - [BIDDER_DONE, {type: BIDDER_DONE}], - [AUCTION_DEBUG, {type: AUCTION_DEBUG}], - [BID_TIMEOUT, {type: BID_TIMEOUT}], - ['slotRenderEnded', {type: 'slotRenderEnded'}], - ['impressionViewable', {type: 'impressionViewable'}] + [AUCTION_INIT, { type: AUCTION_INIT }], + [BID_RESPONSE, { type: BID_RESPONSE }], + [BID_REQUESTED, { type: BID_REQUESTED }], + [BID_REJECTED, { type: BID_REJECTED }], + [NO_BID, { type: NO_BID }], + [BID_WON, { type: BID_WON }], + [BIDDER_DONE, { type: BIDDER_DONE }], + [AUCTION_DEBUG, { type: AUCTION_DEBUG }], + [BID_TIMEOUT, { type: BID_TIMEOUT }], + ['slotRenderEnded', { type: 'slotRenderEnded' }], + ['impressionViewable', { type: 'impressionViewable' }] ] }); afterEach(() => { diff --git a/test/spec/modules/automatadBidAdapter_spec.js b/test/spec/modules/automatadBidAdapter_spec.js index 050563d721b..d99df260431 100644 --- a/test/spec/modules/automatadBidAdapter_spec.js +++ b/test/spec/modules/automatadBidAdapter_spec.js @@ -7,7 +7,7 @@ describe('automatadBidAdapter', function () { const bidRequestRequiredParams = { bidder: 'automatad', - params: {siteId: '123ad'}, + params: { siteId: '123ad' }, mediaTypes: { banner: { sizes: [[300, 600]], @@ -25,7 +25,7 @@ describe('automatadBidAdapter', function () { const bidRequestAllParams = { bidder: 'automatad', - params: {siteId: '123ad', placementId: '123abc345'}, + params: { siteId: '123ad', placementId: '123abc345' }, mediaTypes: { banner: { sizes: [[300, 600]], @@ -93,7 +93,7 @@ describe('automatadBidAdapter', function () { }) describe('buildRequests', function () { - const req = spec.buildRequests([ bidRequestRequiredParams ], { refererInfo: { } }) + const req = spec.buildRequests([bidRequestRequiredParams], { refererInfo: { } }) let rdata it('should have withCredentials option as true', function() { @@ -178,12 +178,12 @@ describe('automatadBidAdapter', function () { } }] const result = spec.interpretResponse(multipleBidResponse[0]).map(bid => { - const {requestId} = bid; - return [ requestId ]; + const { requestId } = bid; + return [requestId]; }); assert.equal(result.length, 2); - assert.deepEqual(result, [[ 'imp1' ], [ 'imp2' ]]); + assert.deepEqual(result, [['imp1'], ['imp2']]); }) it('handles empty bid response', function () { diff --git a/test/spec/modules/azerionedgeRtdProvider_spec.js b/test/spec/modules/azerionedgeRtdProvider_spec.js index 82f4bb46c41..ef0da3f061b 100644 --- a/test/spec/modules/azerionedgeRtdProvider_spec.js +++ b/test/spec/modules/azerionedgeRtdProvider_spec.js @@ -13,7 +13,7 @@ describe('Azerion Edge RTD submodule', function () { const bidders = ['appnexus', 'improvedigital']; const process = { key: 'value' }; const dataProvider = { name: 'azerionedge', waitForIt: true }; - const userConsent = {gdpr: {gdprApplies: 'gdpr-applies', consentString: 'consent-string'}, usp: 'usp'}; + const userConsent = { gdpr: { gdprApplies: 'gdpr-applies', consentString: 'consent-string' }, usp: 'usp' }; const resetAll = () => { window.azerionPublisherAudiences.resetHistory(); @@ -65,7 +65,7 @@ describe('Azerion Edge RTD submodule', function () { ['uspConsent', userConsent.usp], ].forEach(([key, value]) => { it(`should call azerionPublisherAudiencesStub with ${key}:${value}`, function () { - expect(window.azerionPublisherAudiences.args[0][0]).to.include({[key]: value}); + expect(window.azerionPublisherAudiences.args[0][0]).to.include({ [key]: value }); }); }); @@ -104,7 +104,7 @@ describe('Azerion Edge RTD submodule', function () { ...Object.entries(process), ].forEach(([key, value]) => { it(`should call azerionPublisherAudiencesStub with ${key}:${value}`, function () { - expect(window.azerionPublisherAudiences.args[0][0]).to.include({[key]: value}); + expect(window.azerionPublisherAudiences.args[0][0]).to.include({ [key]: value }); }); }); }); diff --git a/test/spec/modules/beachfrontBidAdapter_spec.js b/test/spec/modules/beachfrontBidAdapter_spec.js index a881928a017..5425a84fffb 100644 --- a/test/spec/modules/beachfrontBidAdapter_spec.js +++ b/test/spec/modules/beachfrontBidAdapter_spec.js @@ -136,7 +136,7 @@ describe('BeachfrontAdapter', function () { it('should create a POST request for each bid', function () { const bidRequest = bidRequests[0]; bidRequest.mediaTypes = { video: {} }; - const requests = spec.buildRequests([ bidRequest ], {}); + const requests = spec.buildRequests([bidRequest], {}); expect(requests[0].method).to.equal('POST'); expect(requests[0].url).to.equal(VIDEO_ENDPOINT + bidRequest.params.appId); }); @@ -148,7 +148,7 @@ describe('BeachfrontAdapter', function () { bidRequest.params.tagid = '7cd7a7b4-ef3f-4aeb-9565-3627f255fa10'; bidRequest.mediaTypes = { video: { - playerSize: [ width, height ] + playerSize: [width, height] } }; const topLocation = parseUrl('http://www.example.com?foo=bar', { decodeSearchAsString: true }); @@ -157,7 +157,7 @@ describe('BeachfrontAdapter', function () { page: topLocation.href } }; - const requests = spec.buildRequests([ bidRequest ], bidderRequest); + const requests = spec.buildRequests([bidRequest], bidderRequest); const data = requests[0].data; expect(data.isPrebid).to.equal(true); expect(data.appId).to.equal(bidRequest.params.appId); @@ -175,7 +175,7 @@ describe('BeachfrontAdapter', function () { const bidRequest = bidRequests[0]; bidRequest.mediaTypes = { video: {} }; bidRequest.getFloor = () => ({ currency: 'USD', floor: 1.16 }); - const requests = spec.buildRequests([ bidRequest ], {}); + const requests = spec.buildRequests([bidRequest], {}); const data = requests[0].data; expect(data.imp[0].bidfloor).to.equal(1.16); }); @@ -184,7 +184,7 @@ describe('BeachfrontAdapter', function () { const bidRequest = bidRequests[0]; bidRequest.mediaTypes = { video: {} }; bidRequest.getFloor = () => ({}); - const requests = spec.buildRequests([ bidRequest ], {}); + const requests = spec.buildRequests([bidRequest], {}); const data = requests[0].data; expect(data.imp[0].bidfloor).to.equal(bidRequest.params.bidfloor); }); @@ -195,10 +195,10 @@ describe('BeachfrontAdapter', function () { const bidRequest = bidRequests[0]; bidRequest.mediaTypes = { video: { - playerSize: [[ width, height ]] + playerSize: [[width, height]] } }; - const requests = spec.buildRequests([ bidRequest ], {}); + const requests = spec.buildRequests([bidRequest], {}); const data = requests[0].data; expect(data.imp[0].video).to.deep.contain({ w: width, h: height }); }); @@ -212,7 +212,7 @@ describe('BeachfrontAdapter', function () { playerSize: `${width}x${height}` } }; - const requests = spec.buildRequests([ bidRequest ], {}); + const requests = spec.buildRequests([bidRequest], {}); const data = requests[0].data; expect(data.imp[0].video).to.deep.contain({ w: width, h: height }); }); @@ -224,7 +224,7 @@ describe('BeachfrontAdapter', function () { playerSize: [] } }; - const requests = spec.buildRequests([ bidRequest ], {}); + const requests = spec.buildRequests([bidRequest], {}); const data = requests[0].data; expect(data.imp[0].video).to.deep.contain({ w: undefined, h: undefined }); }); @@ -233,9 +233,9 @@ describe('BeachfrontAdapter', function () { const width = 640; const height = 480; const bidRequest = bidRequests[0]; - bidRequest.sizes = [ width, height ]; + bidRequest.sizes = [width, height]; bidRequest.mediaTypes = { video: {} }; - const requests = spec.buildRequests([ bidRequest ], {}); + const requests = spec.buildRequests([bidRequest], {}); const data = requests[0].data; expect(data.imp[0].video).to.deep.contain({ w: width, h: height }); }); @@ -250,7 +250,7 @@ describe('BeachfrontAdapter', function () { bidRequest.mediaTypes = { video: { mimes, playbackmethod, maxduration, plcmt, skip } }; - const requests = spec.buildRequests([ bidRequest ], {}); + const requests = spec.buildRequests([bidRequest], {}); const data = requests[0].data; expect(data.imp[0].video).to.deep.contain({ mimes, playbackmethod, maxduration, plcmt, skip }); }); @@ -264,7 +264,7 @@ describe('BeachfrontAdapter', function () { const skip = 1; bidRequest.mediaTypes = { video: { plcmt: 3, skip: 0 } }; bidRequest.params.video = { mimes, playbackmethod, maxduration, plcmt, skip }; - const requests = spec.buildRequests([ bidRequest ], {}); + const requests = spec.buildRequests([bidRequest], {}); const data = requests[0].data; expect(data.imp[0].video).to.deep.contain({ mimes, playbackmethod, maxduration, plcmt, skip }); }); @@ -274,7 +274,7 @@ describe('BeachfrontAdapter', function () { bidRequest.mediaTypes = { video: {} }; const uspConsent = '2112YYZ'; const bidderRequest = { uspConsent }; - const requests = spec.buildRequests([ bidRequest ], bidderRequest); + const requests = spec.buildRequests([bidRequest], bidderRequest); const data = requests[0].data; expect(data.regs.ext.us_privacy).to.equal(uspConsent); }); @@ -289,7 +289,7 @@ describe('BeachfrontAdapter', function () { consentString } }; - const requests = spec.buildRequests([ bidRequest ], bidderRequest); + const requests = spec.buildRequests([bidRequest], bidderRequest); const data = requests[0].data; expect(data.regs.ext.gdpr).to.equal(1); expect(data.user.ext.consent).to.equal(consentString); @@ -306,7 +306,7 @@ describe('BeachfrontAdapter', function () { applicableSections } }; - const requests = spec.buildRequests([ bidRequest ], bidderRequest); + const requests = spec.buildRequests([bidRequest], bidderRequest); const data = requests[0].data; expect(data.regs.gpp).to.equal(gppString); expect(data.regs.gpp_sid).to.deep.equal(applicableSections); @@ -328,7 +328,7 @@ describe('BeachfrontAdapter', function () { const bidRequest = bidRequests[0]; bidRequest.mediaTypes = { video: {} }; bidRequest.ortb2 = { source: { ext: { schain: schain } } }; - const requests = spec.buildRequests([ bidRequest ], {}); + const requests = spec.buildRequests([bidRequest], {}); const data = requests[0].data; expect(data.source.ext.schain).to.deep.equal(schain); }); @@ -343,7 +343,7 @@ describe('BeachfrontAdapter', function () { const bidRequest = bidRequests[0]; bidRequest.mediaTypes = { video: {} }; bidRequest.userId = userId; - const requests = spec.buildRequests([ bidRequest ], {}); + const requests = spec.buildRequests([bidRequest], {}); const data = requests[0].data; expect(data.user.ext.eids).to.deep.equal([ { @@ -408,7 +408,7 @@ describe('BeachfrontAdapter', function () { bidRequest.params.tagid = '7cd7a7b4-ef3f-4aeb-9565-3627f255fa10'; bidRequest.mediaTypes = { banner: { - sizes: [ width, height ] + sizes: [width, height] } }; const topLocation = parseUrl('http://www.example.com?foo=bar', { decodeSearchAsString: true }); @@ -417,7 +417,7 @@ describe('BeachfrontAdapter', function () { page: topLocation.href } }; - const requests = spec.buildRequests([ bidRequest ], bidderRequest); + const requests = spec.buildRequests([bidRequest], bidderRequest); const data = requests[0].data; expect(data.slots).to.deep.equal([ { @@ -438,7 +438,7 @@ describe('BeachfrontAdapter', function () { const bidRequest = bidRequests[0]; bidRequest.mediaTypes = { banner: {} }; bidRequest.getFloor = () => ({ currency: 'USD', floor: 1.16 }); - const requests = spec.buildRequests([ bidRequest ], {}); + const requests = spec.buildRequests([bidRequest], {}); const data = requests[0].data; expect(data.slots[0].bidfloor).to.equal(1.16); }); @@ -447,7 +447,7 @@ describe('BeachfrontAdapter', function () { const bidRequest = bidRequests[0]; bidRequest.mediaTypes = { banner: {} }; bidRequest.getFloor = () => ({}); - const requests = spec.buildRequests([ bidRequest ], {}); + const requests = spec.buildRequests([bidRequest], {}); const data = requests[0].data; expect(data.slots[0].bidfloor).to.equal(bidRequest.params.bidfloor); }); @@ -458,10 +458,10 @@ describe('BeachfrontAdapter', function () { const bidRequest = bidRequests[0]; bidRequest.mediaTypes = { banner: { - sizes: [[ width, height ]] + sizes: [[width, height]] } }; - const requests = spec.buildRequests([ bidRequest ], {}); + const requests = spec.buildRequests([bidRequest], {}); const data = requests[0].data; expect(data.slots[0].sizes).to.deep.equal([ { w: width, h: height } @@ -477,7 +477,7 @@ describe('BeachfrontAdapter', function () { sizes: `${width}x${height}` } }; - const requests = spec.buildRequests([ bidRequest ], {}); + const requests = spec.buildRequests([bidRequest], {}); const data = requests[0].data; expect(data.slots[0].sizes).to.deep.equal([ { w: width, h: height } @@ -491,7 +491,7 @@ describe('BeachfrontAdapter', function () { sizes: [] } }; - const requests = spec.buildRequests([ bidRequest ], {}); + const requests = spec.buildRequests([bidRequest], {}); const data = requests[0].data; expect(data.slots[0].sizes).to.deep.equal([]); }); @@ -500,9 +500,9 @@ describe('BeachfrontAdapter', function () { const width = 300; const height = 250; const bidRequest = bidRequests[0]; - bidRequest.sizes = [ width, height ]; + bidRequest.sizes = [width, height]; bidRequest.mediaTypes = { banner: {} }; - const requests = spec.buildRequests([ bidRequest ], {}); + const requests = spec.buildRequests([bidRequest], {}); const data = requests[0].data; expect(data.slots[0].sizes).to.deep.contain({ w: width, h: height }); }); @@ -512,7 +512,7 @@ describe('BeachfrontAdapter', function () { bidRequest.mediaTypes = { banner: {} }; const uspConsent = '2112YYZ'; const bidderRequest = { uspConsent }; - const requests = spec.buildRequests([ bidRequest ], bidderRequest); + const requests = spec.buildRequests([bidRequest], bidderRequest); const data = requests[0].data; expect(data.usPrivacy).to.equal(uspConsent); }); @@ -527,7 +527,7 @@ describe('BeachfrontAdapter', function () { consentString } }; - const requests = spec.buildRequests([ bidRequest ], bidderRequest); + const requests = spec.buildRequests([bidRequest], bidderRequest); const data = requests[0].data; expect(data.gdpr).to.equal(1); expect(data.gdprConsent).to.equal(consentString); @@ -544,7 +544,7 @@ describe('BeachfrontAdapter', function () { applicableSections } }; - const requests = spec.buildRequests([ bidRequest ], bidderRequest); + const requests = spec.buildRequests([bidRequest], bidderRequest); const data = requests[0].data; expect(data.gpp).to.equal(gppString); expect(data.gppSid).to.deep.equal(applicableSections); @@ -566,7 +566,7 @@ describe('BeachfrontAdapter', function () { const bidRequest = bidRequests[0]; bidRequest.mediaTypes = { banner: {} }; bidRequest.ortb2 = { source: { ext: { schain: schain } } }; - const requests = spec.buildRequests([ bidRequest ], {}); + const requests = spec.buildRequests([bidRequest], {}); const data = requests[0].data; expect(data.schain).to.deep.equal(schain); }); @@ -581,7 +581,7 @@ describe('BeachfrontAdapter', function () { const bidRequest = bidRequests[0]; bidRequest.mediaTypes = { banner: {} }; bidRequest.userId = userId; - const requests = spec.buildRequests([ bidRequest ], {}); + const requests = spec.buildRequests([bidRequest], {}); const data = requests[0].data; expect(data.tdid).to.equal(userId.tdid); expect(data.idl).to.equal(userId.idl_env); @@ -609,7 +609,7 @@ describe('BeachfrontAdapter', function () { }, ortb2 }; - const requests = spec.buildRequests([ bidRequest ], bidderRequest); + const requests = spec.buildRequests([bidRequest], bidderRequest); const data = requests[0].data; expect(data.user.data).to.equal('some user data'); expect(data.site.keywords).to.equal('test keyword'); @@ -628,7 +628,7 @@ describe('BeachfrontAdapter', function () { }; const bidRequest = bidRequests[0]; bidRequest.mediaTypes = { banner: {} }; - const requests = spec.buildRequests([ bidRequest ], {ortb2}); + const requests = spec.buildRequests([bidRequest], { ortb2 }); const data = requests[0].data; expect(data.ortb2.user.data).to.equal('some user data'); expect(data.ortb2.site.keywords).to.equal('test keyword'); @@ -642,10 +642,10 @@ describe('BeachfrontAdapter', function () { const bidRequest = bidRequests[0]; bidRequest.mediaTypes = { video: { - playerSize: [ width, height ] + playerSize: [width, height] }, banner: { - sizes: [ width, height ] + sizes: [width, height] } }; bidRequest.params = { @@ -658,7 +658,7 @@ describe('BeachfrontAdapter', function () { appId: '3b16770b-17af-4d22-daff-9606bdf2c9c3' } }; - const requests = spec.buildRequests([ bidRequest ], {}); + const requests = spec.buildRequests([bidRequest], {}); expect(requests.length).to.equal(2); expect(requests[0].url).to.contain(VIDEO_ENDPOINT); expect(requests[1].url).to.contain(BANNER_ENDPOINT); @@ -668,10 +668,10 @@ describe('BeachfrontAdapter', function () { const bidRequest = bidRequests[0]; bidRequest.mediaTypes = { video: { - playerSize: [ 640, 360 ] + playerSize: [640, 360] }, banner: { - sizes: [ 300, 250 ] + sizes: [300, 250] } }; bidRequest.params = { @@ -684,7 +684,7 @@ describe('BeachfrontAdapter', function () { appId: '3b16770b-17af-4d22-daff-9606bdf2c9c3' } }; - const requests = spec.buildRequests([ bidRequest ], {}); + const requests = spec.buildRequests([bidRequest], {}); expect(requests[0].data.imp[0].video).to.deep.contain({ w: 640, h: 360 }); expect(requests[1].data.slots[0].sizes).to.deep.equal([{ w: 300, h: 250 }]); }); @@ -716,7 +716,7 @@ describe('BeachfrontAdapter', function () { const bidRequest = bidRequests[0]; bidRequest.mediaTypes = { video: { - playerSize: [ width, height ] + playerSize: [width, height] } }; const serverResponse = { @@ -749,7 +749,7 @@ describe('BeachfrontAdapter', function () { const bidRequest = bidRequests[0]; bidRequest.mediaTypes = { video: { - playerSize: [ width, height ] + playerSize: [width, height] } }; bidRequest.params.video = { responseType: 'nurl' }; @@ -770,7 +770,7 @@ describe('BeachfrontAdapter', function () { const bidRequest = bidRequests[0]; bidRequest.mediaTypes = { video: { - playerSize: [ width, height ] + playerSize: [width, height] } }; bidRequest.params.video = { responseType: 'adm' }; @@ -811,7 +811,7 @@ describe('BeachfrontAdapter', function () { const bidRequest = bidRequests[0]; bidRequest.mediaTypes = { video: { - playerSize: [ width, height ] + playerSize: [width, height] } }; const serverResponse = { @@ -849,12 +849,12 @@ describe('BeachfrontAdapter', function () { it('should return valid banner bid responses', function () { bidRequests[0].mediaTypes = { banner: { - sizes: [[ 300, 250 ], [ 728, 90 ]] + sizes: [[300, 250], [728, 90]] } }; bidRequests[1].mediaTypes = { banner: { - sizes: [[ 300, 600 ], [ 200, 200 ]] + sizes: [[300, 600], [200, 200]] } }; const serverResponse = [{ @@ -875,14 +875,14 @@ describe('BeachfrontAdapter', function () { const bidResponse = spec.interpretResponse({ body: serverResponse }, { bidRequest: bidRequests }); expect(bidResponse.length).to.equal(2); for (let i = 0; i < bidRequests.length; i++) { - expect(bidResponse[ i ]).to.deep.equal({ - requestId: bidRequests[ i ].bidId, + expect(bidResponse[i]).to.deep.equal({ + requestId: bidRequests[i].bidId, bidderCode: spec.code, - ad: serverResponse[ i ].adm, - creativeId: serverResponse[ i ].crid, - cpm: serverResponse[ i ].price, - width: serverResponse[ i ].w, - height: serverResponse[ i ].h, + ad: serverResponse[i].adm, + creativeId: serverResponse[i].crid, + cpm: serverResponse[i].price, + width: serverResponse[i].w, + height: serverResponse[i].h, meta: { mediaType: 'banner', advertiserDomains: [] }, mediaType: 'banner', currency: 'USD', @@ -895,7 +895,7 @@ describe('BeachfrontAdapter', function () { it('should return meta data for the bid response', () => { bidRequests[0].mediaTypes = { banner: { - sizes: [[ 300, 250 ], [ 728, 90 ]] + sizes: [[300, 250], [728, 90]] } }; const serverResponse = [{ diff --git a/test/spec/modules/beopBidAdapter_spec.js b/test/spec/modules/beopBidAdapter_spec.js index 08e14b76182..15f10fc8719 100644 --- a/test/spec/modules/beopBidAdapter_spec.js +++ b/test/spec/modules/beopBidAdapter_spec.js @@ -1,5 +1,5 @@ import { expect } from 'chai'; -import { spec } from 'modules/beopBidAdapter.js'; +import { spec, __storage } from 'modules/beopBidAdapter.js'; import { newBidder } from 'src/adapters/bidderFactory.js'; import { config } from 'src/config.js'; import { setConfig as setCurrencyConfig } from '../../../modules/currency.js'; @@ -288,7 +288,7 @@ describe('BeOp Bid Adapter tests', () => { spec.onBidWon({}); spec.onBidWon(); expect(triggerPixelStub.getCall(0)).to.be.null; - spec.onBidWon({params: {accountId: '5a8af500c9e77c00017e4cad'}, cpm: 1.2}); + spec.onBidWon({ params: { accountId: '5a8af500c9e77c00017e4cad' }, cpm: 1.2 }); expect(triggerPixelStub.getCall(0)).to.not.be.null; expect(triggerPixelStub.getCall(0).args[0]).to.exist.and.to.include('https://t.collectiveaudience.co'); expect(triggerPixelStub.getCall(0).args[0]).to.include('se_ca=bid'); @@ -299,7 +299,7 @@ describe('BeOp Bid Adapter tests', () => { spec.onBidWon({}); spec.onBidWon(); expect(triggerPixelStub.getCall(0)).to.be.null; - spec.onBidWon({params: [{accountId: '5a8af500c9e77c00017e4cad'}], cpm: 1.2}); + spec.onBidWon({ params: [{ accountId: '5a8af500c9e77c00017e4cad' }], cpm: 1.2 }); expect(triggerPixelStub.getCall(0)).to.not.be.null; expect(triggerPixelStub.getCall(0).args[0]).to.exist.and.to.include('https://t.collectiveaudience.co'); expect(triggerPixelStub.getCall(0).args[0]).to.include('se_ca=bid'); @@ -368,7 +368,7 @@ describe('BeOp Bid Adapter tests', () => { it(`should get eids from bid`, function () { const bid = Object.assign({}, validBid); - bid.userIdAsEids = [{source: 'provider.com', uids: [{id: 'someid', atype: 1, ext: {whatever: true}}]}]; + bid.userIdAsEids = [{ source: 'provider.com', uids: [{ id: 'someid', atype: 1, ext: { whatever: true } }] }]; bidRequests.push(bid); const request = spec.buildRequests(bidRequests, {}); @@ -380,15 +380,215 @@ describe('BeOp Bid Adapter tests', () => { describe('Ensure first party cookie is well managed', function () { const bidRequests = []; + let sandbox; - it(`should generate a new uuid`, function () { + beforeEach(function () { + sandbox = sinon.createSandbox(); + }); + + afterEach(function () { + sandbox.restore(); + }); + + it('should set fg to a 24-char hex ObjectID (caudid) when no cookie present', function () { + sandbox.stub(__storage, 'cookiesAreEnabled').returns(true); + sandbox.stub(__storage, 'getCookie').returns(undefined); const bid = Object.assign({}, validBid); - bidRequests.push(bid); - const request = spec.buildRequests(bidRequests, {}); + const request = spec.buildRequests([bid], {}); const payload = JSON.parse(request.data); expect(payload.fg).to.exist; - }) - }) + expect(payload.fg).to.match(/^[0-9a-f]{24}$/); + }); + + it('should set both caudid and caudid_date cookies when generating a new id', function () { + sandbox.stub(__storage, 'cookiesAreEnabled').returns(true); + sandbox.stub(__storage, 'getCookie').returns(undefined); + const setCookieSpy = sandbox.stub(__storage, 'setCookie'); + + const bid = Object.assign({}, validBid); + const request = spec.buildRequests([bid], {}); + const payload = JSON.parse(request.data); + + expect(setCookieSpy.calledTwice).to.be.true; + expect(setCookieSpy.firstCall.args[0]).to.equal('caudid'); + expect(setCookieSpy.firstCall.args[1]).to.match(/^[0-9a-f]{24}$/); + expect(setCookieSpy.secondCall.args[0]).to.equal('caudid_date'); + expect(setCookieSpy.secondCall.args[1]).to.match(/^\d+$/); + expect(Number(setCookieSpy.secondCall.args[1])).to.be.closeTo(Date.now(), 5000); + expect(payload.fg).to.equal(setCookieSpy.firstCall.args[1]); + }); + + it('should always produce 24-char caudid when clock is in the past (timestamp padding)', function () { + sandbox.stub(__storage, 'cookiesAreEnabled').returns(true); + sandbox.stub(__storage, 'getCookie').returns(undefined); + const setCookieSpy = sandbox.stub(__storage, 'setCookie'); + // Unix epoch 0 → hex "0"; without padding that would yield 1 + 16 = 17 chars and fail validIdRegExp + sandbox.stub(Date, 'now').returns(0); + + const bid = Object.assign({}, validBid); + const request = spec.buildRequests([bid], {}); + const payload = JSON.parse(request.data); + + const caudid = setCookieSpy.firstCall.args[1]; + expect(caudid).to.have.lengthOf(24); + expect(caudid).to.match(/^[0-9a-f]{24}$/); + expect(caudid.substring(0, 8)).to.equal('00000000'); + expect(payload.fg).to.equal(caudid); + }); + + it('should not set cookies when a valid caudid cookie already exists', function () { + const existingCaudid = '674a1b2c3d4e5f6789abcdef'; + sandbox.stub(__storage, 'cookiesAreEnabled').returns(true); + sandbox.stub(__storage, 'getCookie').callsFake((name) => + name === 'caudid' ? existingCaudid : undefined + ); + const setCookieSpy = sandbox.stub(__storage, 'setCookie'); + + const bid = Object.assign({}, validBid); + const request = spec.buildRequests([bid], {}); + const payload = JSON.parse(request.data); + + expect(setCookieSpy.called).to.be.false; + expect(payload.fg).to.equal(existingCaudid); + }); + + it('should regenerate caudid and set both cookies when existing caudid is invalid format', function () { + sandbox.stub(__storage, 'cookiesAreEnabled').returns(true); + sandbox.stub(__storage, 'getCookie').callsFake((name) => + name === 'caudid' ? 'invalid-uuid-format' : undefined + ); + const setCookieSpy = sandbox.stub(__storage, 'setCookie'); + + const bid = Object.assign({}, validBid); + const request = spec.buildRequests([bid], {}); + const payload = JSON.parse(request.data); + + expect(setCookieSpy.calledTwice).to.be.true; + expect(setCookieSpy.firstCall.args[0]).to.equal('caudid'); + expect(setCookieSpy.firstCall.args[1]).to.match(/^[0-9a-f]{24}$/); + expect(setCookieSpy.secondCall.args[0]).to.equal('caudid_date'); + expect(payload.fg).to.equal(setCookieSpy.firstCall.args[1]); + }); + + it('should clear both caudid and caudid_date when cookies are disabled', function () { + sandbox.stub(__storage, 'cookiesAreEnabled').returns(false); + const setCookieSpy = sandbox.stub(__storage, 'setCookie'); + + const bid = Object.assign({}, validBid); + const request = spec.buildRequests([bid], {}); + const payload = JSON.parse(request.data); + + expect(setCookieSpy.calledTwice).to.be.true; + expect(setCookieSpy.firstCall.args[0]).to.equal('caudid'); + expect(setCookieSpy.firstCall.args[1]).to.equal(''); + expect(setCookieSpy.firstCall.args[2]).to.equal(0); + expect(setCookieSpy.secondCall.args[0]).to.equal('caudid_date'); + expect(setCookieSpy.secondCall.args[1]).to.equal(''); + expect(setCookieSpy.secondCall.args[2]).to.equal(0); + expect(payload.fg).to.equal(''); + }); + }); + describe('slot name normalization', function () { + it('should preserve non-GPT adUnitCode unchanged (case-sensitive)', function () { + const bid = Object.assign({}, validBid); + bid.adUnitCode = '/Network/TopBanner'; + const request = spec.buildRequests([bid], {}); + const payload = JSON.parse(request.data); + expect(payload.slts[0].name).to.equal('/Network/TopBanner'); + }); + + it('should preserve mixed-case custom adUnitCode unchanged', function () { + const bid = Object.assign({}, validBid); + bid.adUnitCode = 'InArticleSlot'; + const request = spec.buildRequests([bid], {}); + const payload = JSON.parse(request.data); + expect(payload.slts[0].name).to.equal('InArticleSlot'); + }); + + it('should normalize GPT auto-generated adUnitCode by removing prefix', function () { + const bid = Object.assign({}, validBid); + bid.adUnitCode = 'div-gpt-ad-article_top'; + const request = spec.buildRequests([bid], {}); + const payload = JSON.parse(request.data); + expect(payload.slts[0].name).to.equal('article_top'); + }); + + it('should remove long numeric suffix from GPT adUnitCode', function () { + const bid = Object.assign({}, validBid); + bid.adUnitCode = 'div-gpt-ad-sidebar_123456'; + const request = spec.buildRequests([bid], {}); + const payload = JSON.parse(request.data); + expect(payload.slts[0].name).to.equal('sidebar'); + }); + + it('should remove timestamp-like suffix from GPT adUnitCode', function () { + const bid = Object.assign({}, validBid); + bid.adUnitCode = 'div-gpt-ad-header-1678459238475'; + const request = spec.buildRequests([bid], {}); + const payload = JSON.parse(request.data); + expect(payload.slts[0].name).to.equal('header'); + }); + + it('should preserve short numeric suffix in GPT adUnitCode', function () { + const bid = Object.assign({}, validBid); + bid.adUnitCode = 'div-gpt-ad-topbanner-1'; + const request = spec.buildRequests([bid], {}); + const payload = JSON.parse(request.data); + expect(payload.slts[0].name).to.equal('topbanner-1'); + }); + + it('should preserve short numeric suffix like -2 in GPT adUnitCode', function () { + const bid = Object.assign({}, validBid); + bid.adUnitCode = 'div-gpt-ad-article_slot-2'; + const request = spec.buildRequests([bid], {}); + const payload = JSON.parse(request.data); + expect(payload.slts[0].name).to.equal('article_slot-2'); + }); + + it('should handle GPT adUnitCode with underscore separator', function () { + const bid = Object.assign({}, validBid); + bid.adUnitCode = 'div-gpt-ad_content_main'; + const request = spec.buildRequests([bid], {}); + const payload = JSON.parse(request.data); + expect(payload.slts[0].name).to.equal('content_main'); + }); + + it('should return undefined for too short GPT slot names', function () { + const bid = Object.assign({}, validBid); + bid.adUnitCode = 'div-gpt-ad-ab'; + const request = spec.buildRequests([bid], {}); + const payload = JSON.parse(request.data); + expect(payload.slts[0].name).to.be.undefined; + }); + + it('should prefer gpid over adUnitCode', function () { + const bid = Object.assign({}, validBid); + bid.adUnitCode = 'div-gpt-ad-fallback'; + bid.ortb2Imp = { ext: { gpid: '/123/preferred-slot' } }; + const request = spec.buildRequests([bid], {}); + const payload = JSON.parse(request.data); + expect(payload.slts[0].name).to.equal('/123/preferred-slot'); + }); + + it('should prefer adslot over adUnitCode', function () { + const bid = Object.assign({}, validBid); + bid.adUnitCode = 'div-gpt-ad-fallback'; + bid.ortb2Imp = { ext: { data: { adslot: '/456/adslot-name' } } }; + const request = spec.buildRequests([bid], {}); + const payload = JSON.parse(request.data); + expect(payload.slts[0].name).to.equal('/456/adslot-name'); + }); + + it('should prefer tagid over normalized adUnitCode', function () { + const bid = Object.assign({}, validBid); + bid.adUnitCode = 'div-gpt-ad-fallback'; + bid.ortb2Imp = { tagid: 'custom-tagid' }; + const request = spec.buildRequests([bid], {}); + const payload = JSON.parse(request.data); + expect(payload.slts[0].name).to.equal('custom-tagid'); + }); + }); + describe('getUserSyncs', function () { it('should return iframe sync when iframeEnabled and syncFrame provided', function () { const syncOptions = { iframeEnabled: true, pixelEnabled: false }; diff --git a/test/spec/modules/betweenBidAdapter_spec.js b/test/spec/modules/betweenBidAdapter_spec.js index 857779ca355..da76286ae22 100644 --- a/test/spec/modules/betweenBidAdapter_spec.js +++ b/test/spec/modules/betweenBidAdapter_spec.js @@ -16,7 +16,7 @@ describe('betweenBidAdapterTests', function () { const bidRequestData = [{ bidId: 'bid1234', bidder: 'between', - params: {w: 240, h: 400, s: 1112}, + params: { w: 240, h: 400, s: 1112 }, sizes: [[240, 400]] }] const request = spec.buildRequests(bidRequestData); @@ -28,7 +28,7 @@ describe('betweenBidAdapterTests', function () { const bidRequestData = [{ bidId: 'bid1234', bidder: 'between', - params: {w: 240, h: 400, s: 1112}, + params: { w: 240, h: 400, s: 1112 }, mediaTypes: { video: { context: 'outstream', @@ -305,8 +305,8 @@ describe('betweenBidAdapterTests', function () { it('check getUserSyncs', function() { const syncs = spec.getUserSyncs({}, {}); expect(syncs).to.be.an('array').that.to.have.lengthOf(2); - expect(syncs[0]).to.deep.equal({type: 'iframe', url: 'https://ads.betweendigital.com/sspmatch-iframe'}); - expect(syncs[1]).to.deep.equal({type: 'image', url: 'https://ads.betweendigital.com/sspmatch'}); + expect(syncs[0]).to.deep.equal({ type: 'iframe', url: 'https://ads.betweendigital.com/sspmatch-iframe' }); + expect(syncs[1]).to.deep.equal({ type: 'image', url: 'https://ads.betweendigital.com/sspmatch' }); }); it('check sizes', function() { diff --git a/test/spec/modules/beyondmediaBidAdapter_spec.js b/test/spec/modules/beyondmediaBidAdapter_spec.js index 79bf88cb6be..5ee8d73207b 100644 --- a/test/spec/modules/beyondmediaBidAdapter_spec.js +++ b/test/spec/modules/beyondmediaBidAdapter_spec.js @@ -431,7 +431,7 @@ describe('AndBeyondMediaBidAdapter', function () { const syncData = spec.getUserSyncs({}, {}, { consentString: 'ALL', gdprApplies: true, - }, {}); + }, undefined); expect(syncData).to.be.an('array').which.is.not.empty; expect(syncData[0]).to.be.an('object') expect(syncData[0].type).to.be.a('string') @@ -440,9 +440,7 @@ describe('AndBeyondMediaBidAdapter', function () { expect(syncData[0].url).to.equal('https://cookies.andbeyond.media/image?pbjs=1&gdpr=1&gdpr_consent=ALL&coppa=0') }); it('Should return array of objects with proper sync config , include CCPA', function() { - const syncData = spec.getUserSyncs({}, {}, {}, { - consentString: '1---' - }); + const syncData = spec.getUserSyncs({}, {}, {}, '1---'); expect(syncData).to.be.an('array').which.is.not.empty; expect(syncData[0]).to.be.an('object') expect(syncData[0].type).to.be.a('string') @@ -451,7 +449,7 @@ describe('AndBeyondMediaBidAdapter', function () { expect(syncData[0].url).to.equal('https://cookies.andbeyond.media/image?pbjs=1&ccpa_consent=1---&coppa=0') }); it('Should return array of objects with proper sync config , include GPP', function() { - const syncData = spec.getUserSyncs({}, {}, {}, {}, { + const syncData = spec.getUserSyncs({}, {}, {}, undefined, { gppString: 'abc123', applicableSections: [8] }); diff --git a/test/spec/modules/bidResponseFilter_spec.js b/test/spec/modules/bidResponseFilter_spec.js index c37003bde50..c7e6433a112 100644 --- a/test/spec/modules/bidResponseFilter_spec.js +++ b/test/spec/modules/bidResponseFilter_spec.js @@ -35,7 +35,7 @@ describe('bidResponseFilter', () => { it('should not run if not configured', () => { reset(); - addBidResponse.call({dispatch}, 'au', {}, reject); + addBidResponse.call({ dispatch }, 'au', {}, reject); sinon.assert.notCalled(reject); sinon.assert.called(dispatch); }); @@ -44,7 +44,7 @@ describe('bidResponseFilter', () => { config.setConfig({ bidResponseFilter: {} }); - addBidResponse.call({dispatch}, 'au', {}, reject); + addBidResponse.call({ dispatch }, 'au', {}, reject); sinon.assert.called(reject); sinon.assert.notCalled(dispatch); }) @@ -69,7 +69,8 @@ describe('bidResponseFilter', () => { advertiserDomains: ['domain1.com', 'domain2.com'], primaryCatId: 'EXAMPLE-CAT-ID', attr: 'attr', - mediaType: 'banner' + mediaType: 'banner', + cattax: 1 } }; @@ -85,7 +86,8 @@ describe('bidResponseFilter', () => { meta: { advertiserDomains: ['domain1.com', 'domain2.com'], primaryCatId: 'BANNED_CAT1', - attr: 'attr' + attr: 'attr', + cattax: 1 } }; mockAuctionIndex.getOrtb2 = () => ({ @@ -96,6 +98,109 @@ describe('bidResponseFilter', () => { sinon.assert.calledWith(reject, BID_CATEGORY_REJECTION_REASON); }); + describe('cattax (category taxonomy) match', () => { + it('should reject with BID_CATEGORY_REJECTION_REASON when cattax matches and primaryCatId is in bcat blocklist', () => { + const reject = sinon.stub(); + const call = sinon.stub(); + const bid = { + meta: { + advertiserDomains: ['domain1.com'], + primaryCatId: 'BANNED_CAT1', + attr: 1, + mediaType: 'banner', + cattax: 1 + } + }; + mockAuctionIndex.getOrtb2 = () => ({ + badv: [], bcat: ['BANNED_CAT1'], cattax: 1 + }); + mockAuctionIndex.getBidRequest = () => ({ + mediaTypes: { banner: {} }, + ortb2Imp: {} + }); + + addBidResponseHook(call, 'adcode', bid, reject, mockAuctionIndex); + sinon.assert.calledWith(reject, BID_CATEGORY_REJECTION_REASON); + sinon.assert.notCalled(call); + }); + + it('should pass when cattax matches and primaryCatId is not in bcat blocklist', () => { + const reject = sinon.stub(); + const call = sinon.stub(); + const bid = { + meta: { + advertiserDomains: ['domain1.com'], + primaryCatId: 'ALLOWED_CAT', + attr: 1, + mediaType: 'banner', + cattax: 1 + } + }; + mockAuctionIndex.getOrtb2 = () => ({ + badv: [], bcat: ['BANNED_CAT1'], cattax: 1 + }); + mockAuctionIndex.getBidRequest = () => ({ + mediaTypes: { banner: {} }, + ortb2Imp: {} + }); + + addBidResponseHook(call, 'adcode', bid, reject, mockAuctionIndex); + sinon.assert.notCalled(reject); + sinon.assert.calledOnce(call); + }); + + it('should reject with BID_CATEGORY_REJECTION_REASON when cattax does not match (treat primaryCatId as unknown)', () => { + const reject = sinon.stub(); + const call = sinon.stub(); + const bid = { + meta: { + advertiserDomains: ['domain1.com'], + primaryCatId: 'ALLOWED_CAT', + attr: 1, + mediaType: 'banner', + cattax: 2 + } + }; + mockAuctionIndex.getOrtb2 = () => ({ + badv: [], bcat: ['BANNED_CAT1'], cattax: 1 + }); + mockAuctionIndex.getBidRequest = () => ({ + mediaTypes: { banner: {} }, + ortb2Imp: {} + }); + + addBidResponseHook(call, 'adcode', bid, reject, mockAuctionIndex); + sinon.assert.calledWith(reject, BID_CATEGORY_REJECTION_REASON); + sinon.assert.notCalled(call); + }); + + it('should pass when cattax does not match and blockUnknown is false (do not treat as unknown)', () => { + const reject = sinon.stub(); + const call = sinon.stub(); + const bid = { + meta: { + advertiserDomains: ['domain1.com'], + primaryCatId: 'BANNED_CAT1', + attr: 1, + mediaType: 'banner', + cattax: 2 + } + }; + mockAuctionIndex.getOrtb2 = () => ({ + badv: [], bcat: ['BANNED_CAT1'], cattax: 1 + }); + mockAuctionIndex.getBidRequest = () => ({ + mediaTypes: { banner: {} }, + ortb2Imp: {} + }); + config.setConfig({ [MODULE_NAME]: { cat: { blockUnknown: false } } }); + + addBidResponseHook(call, 'adcode', bid, reject, mockAuctionIndex); + sinon.assert.notCalled(reject); + sinon.assert.calledOnce(call); + }); + }); + it('should reject the bid after failed ortb2 adv domains rule validation', () => { const rejection = sinon.stub(); const call = sinon.stub(); @@ -103,7 +208,8 @@ describe('bidResponseFilter', () => { meta: { advertiserDomains: ['domain1.com', 'domain2.com'], primaryCatId: 'VALID_CAT', - attr: 'attr' + attr: 'attr', + cattax: 1 } }; mockAuctionIndex.getOrtb2 = () => ({ @@ -121,7 +227,8 @@ describe('bidResponseFilter', () => { meta: { advertiserDomains: ['validdomain1.com', 'validdomain2.com'], primaryCatId: 'VALID_CAT', - attr: 'BANNED_ATTR' + attr: 'BANNED_ATTR', + cattax: 1 }, mediaType: 'video' }; @@ -149,6 +256,7 @@ describe('bidResponseFilter', () => { primaryCatId: 'BANNED_CAT1', attr: 'valid_attr', mediaType: 'banner', + cattax: 1 } }; @@ -163,7 +271,7 @@ describe('bidResponseFilter', () => { badv: ['domain2.com'], bcat: ['BANNED_CAT1', 'BANNED_CAT2'] }); - config.setConfig({[MODULE_NAME]: {cat: {enforce: false}}}); + config.setConfig({ [MODULE_NAME]: { cat: { enforce: false } } }); addBidResponseHook(call, 'adcode', bid, () => { }, mockAuctionIndex); @@ -177,7 +285,8 @@ describe('bidResponseFilter', () => { advertiserDomains: ['validdomain1.com', 'validdomain2.com'], primaryCatId: undefined, attr: 'valid_attr', - mediaType: 'banner' + mediaType: 'banner', + cattax: 1 } }; @@ -192,7 +301,7 @@ describe('bidResponseFilter', () => { ortb2Imp: {} }) - config.setConfig({[MODULE_NAME]: {cat: {blockUnknown: false}}}); + config.setConfig({ [MODULE_NAME]: { cat: { blockUnknown: false } } }); addBidResponseHook(call, 'adcode', bid, () => { }, mockAuctionIndex); @@ -207,7 +316,8 @@ describe('bidResponseFilter', () => { advertiserDomains: ['validdomain1.com', 'validdomain2.com'], primaryCatId: 'VALID_CAT', attr: 6, - mediaType: 'audio' + mediaType: 'audio', + cattax: 1 }, }; diff --git a/test/spec/modules/bidViewabilityIO_spec.js b/test/spec/modules/bidViewabilityIO_spec.js index 947d9227ca5..f76a0c6230b 100644 --- a/test/spec/modules/bidViewabilityIO_spec.js +++ b/test/spec/modules/bidViewabilityIO_spec.js @@ -4,6 +4,9 @@ import * as utils from 'src/utils.js'; import * as sinon from 'sinon'; import { expect } from 'chai'; import { EVENTS } from 'src/constants.js'; +import { EVENT_TYPE_VIEWABLE, TRACKER_METHOD_IMG } from 'src/eventTrackers.js'; +import * as bidViewability from '../../../modules/bidViewability.js'; +import adapterManager from '../../../src/adapterManager.js'; describe('#bidViewabilityIO', function() { const makeElement = (id) => { @@ -101,6 +104,71 @@ describe('#bidViewabilityIO', function() { }); }) + describe('viewability pixels', function() { + let sandbox; + let triggerPixelSpy; + const mockObserver = { unobserve: sinon.spy() }; + const mockEntry = { target: makeElement('pixel_target_id') }; + + const VIEWABILITY_PIXEL_URLS = [ + 'https://io-viewable-1.com/pixel', + 'https://io-viewable-2.com/track' + ]; + + const bidWithEventTrackers = { + adUnitCode: 'banner_id', + mediaType: 'banner', + width: 728, + height: 90, + eventtrackers: VIEWABILITY_PIXEL_URLS.map(url => ({ event: EVENT_TYPE_VIEWABLE, method: TRACKER_METHOD_IMG, url })) + }; + + beforeEach(function() { + sandbox = sinon.createSandbox(); + triggerPixelSpy = sandbox.spy(utils, ['triggerPixel']); + }); + + afterEach(function() { + sandbox.restore(); + }); + + it('fires viewability pixels when markViewed callback runs with bid that has eventTrackers (EVENT_TYPE_VIEWABLE)', function() { + const func = bidViewabilityIO.markViewed(bidWithEventTrackers, mockEntry, mockObserver); + func(); + expect(triggerPixelSpy.callCount).to.equal(VIEWABILITY_PIXEL_URLS.length); + VIEWABILITY_PIXEL_URLS.forEach((url, i) => { + expect(triggerPixelSpy.getCall(i).args[0]).to.equal(url); + }); + }); + + it('does not fire pixels when bid has empty eventTrackers', function() { + const bidWithEmptyTrackers = { ...banner_bid, eventtrackers: [] }; + const func = bidViewabilityIO.markViewed(bidWithEmptyTrackers, mockEntry, mockObserver); + func(); + expect(triggerPixelSpy.callCount).to.equal(0); + }); + + it('should call onBidViewable', () => { + sandbox.stub(adapterManager, 'callBidViewableBidder'); + const bid = { + bidder: 'mockBidder', + ...banner_bid + } + bidViewabilityIO.markViewed(bid, mockEntry, mockObserver)(); + sinon.assert.calledWith(adapterManager.callBidViewableBidder, 'mockBidder', bid); + }); + + it('should call the triggerBilling function if the viewable bid has deferBilling set to true', function() { + sandbox.stub(adapterManager, 'triggerBilling'); + const bid = { + ...banner_bid, + deferBilling: true + } + bidViewabilityIO.markViewed(bid, mockEntry, mockObserver)(); + sinon.assert.called(adapterManager.triggerBilling); + }); + }) + describe('viewCallbackFactory tests', function() { let sandbox; diff --git a/test/spec/modules/bidViewability_spec.js b/test/spec/modules/bidViewability_spec.js index e33e4a9687b..11257c0d03a 100644 --- a/test/spec/modules/bidViewability_spec.js +++ b/test/spec/modules/bidViewability_spec.js @@ -3,11 +3,12 @@ import { config } from 'src/config.js'; import * as events from 'src/events.js'; import * as utils from 'src/utils.js'; import * as sinon from 'sinon'; -import {expect, spy} from 'chai'; +import { expect, spy } from 'chai'; import * as prebidGlobal from 'src/prebidGlobal.js'; import { EVENTS } from 'src/constants.js'; import adapterManager, { gdprDataHandler, uspDataHandler } from 'src/adapterManager.js'; import parse from 'url-parse'; +import { EVENT_TYPE_VIEWABLE, TRACKER_METHOD_IMG } from 'src/eventTrackers.js'; const GPT_SLOT = { getAdUnitPath() { @@ -23,6 +24,12 @@ const EVENT_OBJ = { slot: GPT_SLOT } +const VIEWABILITY_PIXEL_URLS = [ + 'https://domain-1.com/end-point?a=1', + 'https://domain-2.com/end-point/', + 'https://domain-3.com/end-point?a=1' +]; + const PBJS_WINNING_BID = { 'adUnitCode': '/harshad/Jan/2021/', 'bidderCode': 'pubmatic', @@ -39,11 +46,7 @@ const PBJS_WINNING_BID = { 'creativeId': 'id', 'netRevenue': true, 'currency': 'USD', - 'vurls': [ - 'https://domain-1.com/end-point?a=1', - 'https://domain-2.com/end-point/', - 'https://domain-3.com/end-point?a=1' - ] + 'eventtrackers': VIEWABILITY_PIXEL_URLS.map(url => ({ event: EVENT_TYPE_VIEWABLE, method: TRACKER_METHOD_IMG, url })) }; describe('#bidViewability', function() { @@ -55,22 +58,6 @@ describe('#bidViewability', function() { pbjsWinningBid = Object.assign({}, PBJS_WINNING_BID); }); - describe('isBidAdUnitCodeMatchingSlot', function() { - it('match found by GPT Slot getAdUnitPath', function() { - expect(bidViewability.isBidAdUnitCodeMatchingSlot(pbjsWinningBid, gptSlot)).to.equal(true); - }); - - it('match found by GPT Slot getSlotElementId', function() { - pbjsWinningBid.adUnitCode = 'DIV-1'; - expect(bidViewability.isBidAdUnitCodeMatchingSlot(pbjsWinningBid, gptSlot)).to.equal(true); - }); - - it('match not found', function() { - pbjsWinningBid.adUnitCode = 'DIV-10'; - expect(bidViewability.isBidAdUnitCodeMatchingSlot(pbjsWinningBid, gptSlot)).to.equal(false); - }); - }); - describe('getMatchingWinningBidForGPTSlot', function() { let winningBidsArray; let sandbox @@ -89,159 +76,44 @@ describe('#bidViewability', function() { sandbox.restore(); }) - it('should find a match by using customMatchFunction provided in config', function() { - // Needs config to be passed with customMatchFunction - const bidViewabilityConfig = { - customMatchFunction(bid, slot) { - return ('AD-' + slot.getAdUnitPath()) === bid.adUnitCode; + it('should find a match by using customGptSlotMatching provided in config', function() { + config.setConfig({ + customGptSlotMatching: slot => { + return (adUnitCode) => ('AD-' + slot.getAdUnitPath()) === adUnitCode; } - }; - const newWinningBid = Object.assign({}, PBJS_WINNING_BID, {adUnitCode: 'AD-' + PBJS_WINNING_BID.adUnitCode}); + }); + const newWinningBid = Object.assign({}, PBJS_WINNING_BID, { adUnitCode: 'AD-' + PBJS_WINNING_BID.adUnitCode }); // Needs pbjs.getWinningBids to be implemented with match winningBidsArray.push(newWinningBid); - const wb = bidViewability.getMatchingWinningBidForGPTSlot(bidViewabilityConfig, gptSlot); + const wb = bidViewability.getMatchingWinningBidForGPTSlot(gptSlot); expect(wb).to.deep.equal(newWinningBid); + config.resetConfig(); }); - it('should NOT find a match by using customMatchFunction provided in config', function() { - // Needs config to be passed with customMatchFunction - const bidViewabilityConfig = { - customMatchFunction(bid, slot) { - return ('AD-' + slot.getAdUnitPath()) === bid.adUnitCode; - } - }; - // Needs pbjs.getWinningBids to be implemented without match; winningBidsArray is set to empty in beforeEach - const wb = bidViewability.getMatchingWinningBidForGPTSlot(bidViewabilityConfig, gptSlot); + it('should NOT find a match when customGptSlotMatching is set and no winning bid matches', function() { + config.setConfig({ + customGptSlotMatching: slot => (adUnitCode) => ('AD-' + slot.getAdUnitPath()) === adUnitCode + }); + // winningBidsArray is empty in beforeEach, so no bid matches + const wb = bidViewability.getMatchingWinningBidForGPTSlot(gptSlot); expect(wb).to.equal(null); + config.resetConfig(); }); it('should find a match by using default matching function', function() { - // Needs config to be passed without customMatchFunction - // Needs pbjs.getWinningBids to be implemented with match + // No customGptSlotMatching in config; pbjs.getWinningBids returns matching bid winningBidsArray.push(PBJS_WINNING_BID); - const wb = bidViewability.getMatchingWinningBidForGPTSlot({}, gptSlot); + const wb = bidViewability.getMatchingWinningBidForGPTSlot(gptSlot); expect(wb).to.deep.equal(PBJS_WINNING_BID); }); it('should NOT find a match by using default matching function', function() { - // Needs config to be passed without customMatchFunction - // Needs pbjs.getWinningBids to be implemented without match; winningBidsArray is set to empty in beforeEach - const wb = bidViewability.getMatchingWinningBidForGPTSlot({}, gptSlot); + // No customGptSlotMatching; winningBidsArray is empty in beforeEach + const wb = bidViewability.getMatchingWinningBidForGPTSlot(gptSlot); expect(wb).to.equal(null); }); }); - describe('fireViewabilityPixels', function() { - let sandbox; - let triggerPixelSpy; - - beforeEach(function() { - sandbox = sinon.createSandbox(); - triggerPixelSpy = sandbox.spy(utils, ['triggerPixel']); - }); - - afterEach(function() { - sandbox.restore(); - }); - - it('DO NOT fire pixels if NOT mentioned in module config', function() { - const moduleConfig = {}; - bidViewability.fireViewabilityPixels(moduleConfig, PBJS_WINNING_BID); - expect(triggerPixelSpy.callCount).to.equal(0); - }); - - it('fire pixels if mentioned in module config', function() { - const moduleConfig = {firePixels: true}; - bidViewability.fireViewabilityPixels(moduleConfig, PBJS_WINNING_BID); - PBJS_WINNING_BID.vurls.forEach((url, i) => { - const call = triggerPixelSpy.getCall(i); - expect(call.args[0]).to.equal(url); - }); - }); - - it('USP: should include the us_privacy key when USP Consent is available', function () { - const uspDataHandlerStub = sinon.stub(uspDataHandler, 'getConsentData'); - uspDataHandlerStub.returns('1YYY'); - const moduleConfig = {firePixels: true}; - bidViewability.fireViewabilityPixels(moduleConfig, PBJS_WINNING_BID); - PBJS_WINNING_BID.vurls.forEach((url, i) => { - const call = triggerPixelSpy.getCall(i); - expect(call.args[0].indexOf(url)).to.equal(0); - const testurl = parse(call.args[0]); - const queryObject = utils.parseQS(testurl.query); - expect(queryObject.us_privacy).to.equal('1YYY'); - }); - uspDataHandlerStub.restore(); - }); - - it('USP: should not include the us_privacy key when USP Consent is not available', function () { - const moduleConfig = {firePixels: true}; - bidViewability.fireViewabilityPixels(moduleConfig, PBJS_WINNING_BID); - PBJS_WINNING_BID.vurls.forEach((url, i) => { - const call = triggerPixelSpy.getCall(i); - expect(call.args[0].indexOf(url)).to.equal(0); - const testurl = parse(call.args[0]); - const queryObject = utils.parseQS(testurl.query); - expect(queryObject.us_privacy).to.equal(undefined); - }); - }); - - it('GDPR: should include the GDPR keys when GDPR Consent is available', function() { - const gdprDataHandlerStub = sinon.stub(gdprDataHandler, 'getConsentData'); - gdprDataHandlerStub.returns({ - gdprApplies: true, - consentString: 'consent', - addtlConsent: 'moreConsent' - }); - const moduleConfig = {firePixels: true}; - bidViewability.fireViewabilityPixels(moduleConfig, PBJS_WINNING_BID); - PBJS_WINNING_BID.vurls.forEach((url, i) => { - const call = triggerPixelSpy.getCall(i); - expect(call.args[0].indexOf(url)).to.equal(0); - const testurl = parse(call.args[0]); - const queryObject = utils.parseQS(testurl.query); - expect(queryObject.gdpr).to.equal('1'); - expect(queryObject.gdpr_consent).to.equal('consent'); - expect(queryObject.addtl_consent).to.equal('moreConsent'); - }); - gdprDataHandlerStub.restore(); - }); - - it('GDPR: should not include the GDPR keys when GDPR Consent is not available', function () { - const moduleConfig = {firePixels: true}; - bidViewability.fireViewabilityPixels(moduleConfig, PBJS_WINNING_BID); - PBJS_WINNING_BID.vurls.forEach((url, i) => { - const call = triggerPixelSpy.getCall(i); - expect(call.args[0].indexOf(url)).to.equal(0); - const testurl = parse(call.args[0]); - const queryObject = utils.parseQS(testurl.query); - expect(queryObject.gdpr).to.equal(undefined); - expect(queryObject.gdpr_consent).to.equal(undefined); - expect(queryObject.addtl_consent).to.equal(undefined); - }); - }); - - it('GDPR: should only include the GDPR keys for GDPR Consent fields with values', function () { - const gdprDataHandlerStub = sinon.stub(gdprDataHandler, 'getConsentData'); - gdprDataHandlerStub.returns({ - gdprApplies: true, - consentString: 'consent' - }); - const moduleConfig = {firePixels: true}; - bidViewability.fireViewabilityPixels(moduleConfig, PBJS_WINNING_BID); - PBJS_WINNING_BID.vurls.forEach((url, i) => { - const call = triggerPixelSpy.getCall(i); - expect(call.args[0].indexOf(url)).to.equal(0); - const testurl = parse(call.args[0]); - const queryObject = utils.parseQS(testurl.query); - expect(queryObject.gdpr).to.equal('1'); - expect(queryObject.gdpr_consent).to.equal('consent'); - expect(queryObject.addtl_consent).to.equal(undefined); - }); - gdprDataHandlerStub.restore(); - }) - }); - describe('impressionViewableHandler', function() { let sandbox; let triggerPixelSpy; @@ -288,7 +160,7 @@ describe('#bidViewability', function() { winningBidsArray.push(PBJS_WINNING_BID); bidViewability.impressionViewableHandler(moduleConfig, EVENT_OBJ); // fire pixels should be called - PBJS_WINNING_BID.vurls.forEach((url, i) => { + VIEWABILITY_PIXEL_URLS.forEach((url, i) => { const call = triggerPixelSpy.getCall(i); expect(call.args[0]).to.equal(url); }); diff --git a/test/spec/modules/biddoBidAdapter_spec.js b/test/spec/modules/biddoBidAdapter_spec.js index 25986b3407f..99affeb3815 100644 --- a/test/spec/modules/biddoBidAdapter_spec.js +++ b/test/spec/modules/biddoBidAdapter_spec.js @@ -1,12 +1,12 @@ -import {expect} from 'chai'; -import {spec} from 'modules/biddoBidAdapter.js'; +import { expect } from 'chai'; +import { spec } from 'modules/biddoBidAdapter.js'; describe('biddo bid adapter tests', function () { describe('bid requests', function () { it('should accept valid bid', function () { const validBid = { bidder: 'biddo', - params: {zoneId: 123}, + params: { zoneId: 123 }, }; expect(spec.isBidRequestValid(validBid)).to.equal(true); @@ -24,7 +24,7 @@ describe('biddo bid adapter tests', function () { it('should correctly build payload string', function () { const bidRequests = [{ bidder: 'biddo', - params: {zoneId: 123}, + params: { zoneId: 123 }, mediaTypes: { banner: { sizes: [[300, 250]], @@ -46,7 +46,7 @@ describe('biddo bid adapter tests', function () { it('should support multiple bids', function () { const bidRequests = [{ bidder: 'biddo', - params: {zoneId: 123}, + params: { zoneId: 123 }, mediaTypes: { banner: { sizes: [[300, 250]], @@ -58,7 +58,7 @@ describe('biddo bid adapter tests', function () { transactionId: '92489f71-1bf2-49a0-adf9-000cea934729', }, { bidder: 'biddo', - params: {zoneId: 321}, + params: { zoneId: 321 }, mediaTypes: { banner: { sizes: [[728, 90]], @@ -77,7 +77,7 @@ describe('biddo bid adapter tests', function () { it('should support multiple sizes', function () { const bidRequests = [{ bidder: 'biddo', - params: {zoneId: 123}, + params: { zoneId: 123 }, mediaTypes: { banner: { sizes: [[300, 250], [300, 600]], @@ -118,7 +118,7 @@ describe('biddo bid adapter tests', function () { }, }; - const bids = spec.interpretResponse(serverResponse, {bidderRequest}); + const bids = spec.interpretResponse(serverResponse, { bidderRequest }); expect(bids).to.be.lengthOf(1); expect(bids[0].requestId).to.equal('23acc48ad47af5'); @@ -144,7 +144,7 @@ describe('biddo bid adapter tests', function () { }, }; - const bids = spec.interpretResponse(serverResponse, {bidderRequest}); + const bids = spec.interpretResponse(serverResponse, { bidderRequest }); expect(bids).to.be.lengthOf(0); }); @@ -164,7 +164,7 @@ describe('biddo bid adapter tests', function () { }, }; - const bids = spec.interpretResponse(serverResponse, {bidderRequest}); + const bids = spec.interpretResponse(serverResponse, { bidderRequest }); expect(bids).to.be.lengthOf(0); }); diff --git a/test/spec/modules/bidfuseBidAdapter_spec.js b/test/spec/modules/bidfuseBidAdapter_spec.js index 95c84dcdf87..419cf6e6f3e 100644 --- a/test/spec/modules/bidfuseBidAdapter_spec.js +++ b/test/spec/modules/bidfuseBidAdapter_spec.js @@ -287,7 +287,7 @@ describe('BidfuseBidAdapter', function () { describe('getUserSyncs', function () { it('should have valid user sync with iframeEnabled', function () { - const result = spec.getUserSyncs({iframeEnabled: true}, [serverResponse]); + const result = spec.getUserSyncs({ iframeEnabled: true }, [serverResponse]); expect(result).to.deep.equal([{ type: 'iframe', @@ -296,7 +296,7 @@ describe('BidfuseBidAdapter', function () { }); it('should have valid user sync with cid on response', function () { - const result = spec.getUserSyncs({iframeEnabled: true}, [serverResponse]); + const result = spec.getUserSyncs({ iframeEnabled: true }, [serverResponse]); expect(result).to.deep.equal([{ type: 'iframe', url: 'https://syncbf.bidfuse.com/iframe?pbjs=1&coppa=0' @@ -304,7 +304,7 @@ describe('BidfuseBidAdapter', function () { }); it('should have valid user sync with pixelEnabled', function () { - const result = spec.getUserSyncs({pixelEnabled: true}, [serverResponse]); + const result = spec.getUserSyncs({ pixelEnabled: true }, [serverResponse]); expect(result).to.deep.equal([{ 'url': 'https://syncbf.bidfuse.com/image?pbjs=1&coppa=0', @@ -316,7 +316,7 @@ describe('BidfuseBidAdapter', function () { config.setConfig({ coppa: 1 }); - const result = spec.getUserSyncs({iframeEnabled: true}, [serverResponse]); + const result = spec.getUserSyncs({ iframeEnabled: true }, [serverResponse]); expect(result).to.deep.equal([{ type: 'iframe', url: 'https://syncbf.bidfuse.com/iframe?pbjs=1&coppa=1' @@ -334,10 +334,10 @@ describe('BidfuseBidAdapter', function () { applicableSections: [7] } - const result = spec.getUserSyncs({pixelEnabled: true}, [serverResponse], gdprConsent, uspConsent, gppConsent); + const result = spec.getUserSyncs({ pixelEnabled: true }, [serverResponse], gdprConsent, uspConsent, gppConsent); expect(result).to.deep.equal([{ - 'url': 'https://syncbf.bidfuse.com/image?pbjs=1&gdpr=1&gdpr_consent=consent_string&gpp=gpp_string&gpp_sid=7&coppa=1', + 'url': 'https://syncbf.bidfuse.com/image?pbjs=1&gdpr=1&gdpr_consent=consent_string&ccpa_consent=usp_string&gpp=gpp_string&gpp_sid=7&coppa=1', 'type': 'image' }]); }); diff --git a/test/spec/modules/bidmaticBidAdapter_spec.js b/test/spec/modules/bidmaticBidAdapter_spec.js index dfce07648f7..bf02d6e3ab1 100644 --- a/test/spec/modules/bidmaticBidAdapter_spec.js +++ b/test/spec/modules/bidmaticBidAdapter_spec.js @@ -386,9 +386,11 @@ describe('bidmaticBidAdapter', function () { const syncOptions = { pixelEnabled: true, iframeEnabled: true }; const serverResponses = [ { body: { cookieURLs: ['https://sync1.bidmatic.com/pixel'] } }, - { body: [ - { cookieURLs: ['https://sync2.bidmatic.com/iframe'], cookieURLSTypes: ['iframe'] } - ]} + { + body: [ + { cookieURLs: ['https://sync2.bidmatic.com/iframe'], cookieURLSTypes: ['iframe'] } + ] + } ]; const result = getUserSyncsFn(syncOptions, serverResponses); diff --git a/test/spec/modules/bidtheatreBidAdapter_spec.js b/test/spec/modules/bidtheatreBidAdapter_spec.js index 351f59680b5..74cff812db4 100644 --- a/test/spec/modules/bidtheatreBidAdapter_spec.js +++ b/test/spec/modules/bidtheatreBidAdapter_spec.js @@ -34,7 +34,7 @@ const BANNER_BID_REQUEST = [ } ]; -const BANNER_BIDDER_REQUEST = {'bidderCode': BIDDER_CODE, 'bids': BANNER_BID_REQUEST}; +const BANNER_BIDDER_REQUEST = { 'bidderCode': BIDDER_CODE, 'bids': BANNER_BID_REQUEST }; const BANNER_BID_RESPONSE = { 'cur': 'USD', @@ -92,7 +92,7 @@ const VIDEO_BID_REQUEST = [ } ]; -const VIDEO_BIDDER_REQUEST = {'bidderCode': BIDDER_CODE, 'bids': VIDEO_BID_REQUEST}; +const VIDEO_BIDDER_REQUEST = { 'bidderCode': BIDDER_CODE, 'bids': VIDEO_BID_REQUEST }; const VIDEO_BID_RESPONSE = { 'cur': 'USD', @@ -195,20 +195,20 @@ describe('BidtheatreAdapter', function () { describe('interpretResponse', function () { it('should have exactly one bid in banner response', function () { const request = spec.buildRequests(BANNER_BID_REQUEST, BANNER_BIDDER_REQUEST); - const bids = spec.interpretResponse({body: BANNER_BID_RESPONSE}, request[0]); + const bids = spec.interpretResponse({ body: BANNER_BID_RESPONSE }, request[0]); expect(bids).to.be.an('array').with.lengthOf(1); expect(bids[0]).to.be.an('object'); }); it('should have currency set to USD in banner response', function () { const request = spec.buildRequests(BANNER_BID_REQUEST, BANNER_BIDDER_REQUEST); - const bids = spec.interpretResponse({body: BANNER_BID_RESPONSE}, request[0]); + const bids = spec.interpretResponse({ body: BANNER_BID_RESPONSE }, request[0]); expect(bids[0].currency).to.be.a('string').and.to.equal(DEFAULT_CURRENCY); }); it('should have ad in response and auction price macros replaced in banner response', function () { const request = spec.buildRequests(BANNER_BID_REQUEST, BANNER_BIDDER_REQUEST); - const bids = spec.interpretResponse({body: BANNER_BID_RESPONSE}, request[0]); + const bids = spec.interpretResponse({ body: BANNER_BID_RESPONSE }, request[0]); const ad = bids[0].ad; expect(ad).to.exist; expect(ad).to.be.a('string'); @@ -219,20 +219,20 @@ describe('BidtheatreAdapter', function () { if (FEATURES.VIDEO) { it('should have exactly one bid in video response', function () { const request = spec.buildRequests(VIDEO_BID_REQUEST, VIDEO_BIDDER_REQUEST); - const bids = spec.interpretResponse({body: VIDEO_BID_RESPONSE}, request[0]); + const bids = spec.interpretResponse({ body: VIDEO_BID_RESPONSE }, request[0]); expect(bids).to.be.an('array').with.lengthOf(1); expect(bids[0]).to.be.an('object'); }); it('should have currency set to USD in video response', function () { const request = spec.buildRequests(VIDEO_BID_REQUEST, VIDEO_BIDDER_REQUEST); - const bids = spec.interpretResponse({body: VIDEO_BID_RESPONSE}, request[0]); + const bids = spec.interpretResponse({ body: VIDEO_BID_RESPONSE }, request[0]); expect(bids[0].currency).to.be.a('string').and.to.equal(DEFAULT_CURRENCY); }); it('should have vastUrl in response and auction price macros replaced in video response', function () { const request = spec.buildRequests(VIDEO_BID_REQUEST, VIDEO_BIDDER_REQUEST); - const bids = spec.interpretResponse({body: VIDEO_BID_RESPONSE}, request[0]); + const bids = spec.interpretResponse({ body: VIDEO_BID_RESPONSE }, request[0]); const vastUrl = bids[0].vastUrl; expect(vastUrl).to.exist; expect(vastUrl).to.be.a('string'); @@ -260,7 +260,7 @@ describe('BidtheatreAdapter', function () { }); it('should return usersync url when pixel is allowed and present in bid response', function () { - expect(spec.getUserSyncs({ pixelEnabled: true }, [{body: bidResponse}], gdprConsent)).to.deep.equal([{ type: 'image', url: bidResponseSyncURL }]); + expect(spec.getUserSyncs({ pixelEnabled: true }, [{ body: bidResponse }], gdprConsent)).to.deep.equal([{ type: 'image', url: bidResponseSyncURL }]); }); }); }); diff --git a/test/spec/modules/big-richmediaBidAdapter_spec.js b/test/spec/modules/big-richmediaBidAdapter_spec.js index ffddad0003d..5ed446f2720 100644 --- a/test/spec/modules/big-richmediaBidAdapter_spec.js +++ b/test/spec/modules/big-richmediaBidAdapter_spec.js @@ -215,7 +215,7 @@ describe('bigRichMediaAdapterTests', function () { adUnitCode: 'code' }] }; - const result = spec.interpretResponse({ body: response }, {bidderRequest}); + const result = spec.interpretResponse({ body: response }, { bidderRequest }); expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); }); @@ -249,7 +249,7 @@ describe('bigRichMediaAdapterTests', function () { }] } - const result = spec.interpretResponse({ body: response }, {bidderRequest}); + const result = spec.interpretResponse({ body: response }, { bidderRequest }); expect(result[0]).not.to.have.property('vastXml'); expect(result[0]).not.to.have.property('vastUrl'); expect(result[0]).to.have.property('width', 1); diff --git a/test/spec/modules/bitmediaBidAdapter_spec.js b/test/spec/modules/bitmediaBidAdapter_spec.js index 537ae38354b..7f55a53307c 100644 --- a/test/spec/modules/bitmediaBidAdapter_spec.js +++ b/test/spec/modules/bitmediaBidAdapter_spec.js @@ -1,8 +1,8 @@ -import {expect} from 'chai'; -import {spec, STORAGE, ENDPOINT_URL} from 'modules/bitmediaBidAdapter.js'; +import { expect } from 'chai'; +import { spec, STORAGE, ENDPOINT_URL } from 'modules/bitmediaBidAdapter.js'; import * as utils from 'src/utils.js'; -import {config} from 'src/config.js'; -import {BANNER} from '../../../src/mediaTypes.js'; +import { config } from 'src/config.js'; +import { BANNER } from '../../../src/mediaTypes.js'; describe('Bitmedia Bid Adapter', function () { const createBidRequest = (sandbox, overrides = {}) => { @@ -331,11 +331,11 @@ describe('Bitmedia Bid Adapter', function () { beforeEach(function () { const getFloorStub = sinon.stub(); - getFloorStub.withArgs({currency: 'USD', mediaType: 'banner', size: [300, 250]}).returns({ + getFloorStub.withArgs({ currency: 'USD', mediaType: 'banner', size: [300, 250] }).returns({ currency: 'USD', floor: 0.5 }); - getFloorStub.withArgs({currency: 'USD', mediaType: 'banner', size: [300, 600]}).returns({ + getFloorStub.withArgs({ currency: 'USD', mediaType: 'banner', size: [300, 600] }).returns({ currency: 'USD', floor: 0.7 }); @@ -351,9 +351,9 @@ describe('Bitmedia Bid Adapter', function () { it('should include the correct bidfloor per impression', function () { expect(imp[0].bidfloor).to.equal(0.5); - expect(imp[0].banner).to.deep.equal({w: 300, h: 250}); + expect(imp[0].banner).to.deep.equal({ w: 300, h: 250 }); expect(imp[1].bidfloor).to.equal(0.7); - expect(imp[1].banner).to.deep.equal({w: 300, h: 600}); + expect(imp[1].banner).to.deep.equal({ w: 300, h: 600 }); }); }); @@ -456,7 +456,7 @@ describe('Bitmedia Bid Adapter', function () { }); it('should return an empty array when server response is empty', function () { - const serverResponse = {body: {}}; + const serverResponse = { body: {} }; const bidRequest = {}; const bids = spec.interpretResponse(serverResponse, bidRequest); diff --git a/test/spec/modules/blastoBidAdapter_spec.js b/test/spec/modules/blastoBidAdapter_spec.js index d0cd8e22b5e..9265ebd0cd0 100644 --- a/test/spec/modules/blastoBidAdapter_spec.js +++ b/test/spec/modules/blastoBidAdapter_spec.js @@ -185,7 +185,7 @@ describe('blastoAdapter', function () { }); it('should send the CCPA data in the request', async function () { - const serverRequest = spec.buildRequests([SIMPLE_BID_REQUEST], await addFPDToBidderRequest({...bidderRequest, ...{uspConsent: '1YYY'}})); + const serverRequest = spec.buildRequests([SIMPLE_BID_REQUEST], await addFPDToBidderRequest({ ...bidderRequest, ...{ uspConsent: '1YYY' } })); expect(serverRequest.data.regs.ext.us_privacy).to.equal('1YYY'); }); }); diff --git a/test/spec/modules/bliinkBidAdapter_spec.js b/test/spec/modules/bliinkBidAdapter_spec.js index 885a50e0caa..61926b7d570 100644 --- a/test/spec/modules/bliinkBidAdapter_spec.js +++ b/test/spec/modules/bliinkBidAdapter_spec.js @@ -355,17 +355,19 @@ const GetUserIds = [ { title: 'Should return eids if exists', args: { - fn: getUserIds([{ userIdAsEids: [ - { - 'source': 'criteo.com', - 'uids': [ - { - 'id': 'testId', - 'atype': 1 - } - ] - } - ] }]), + fn: getUserIds([{ + userIdAsEids: [ + { + 'source': 'criteo.com', + 'uids': [ + { + 'id': 'testId', + 'atype': 1 + } + ] + } + ] + }]), }, want: [ { diff --git a/test/spec/modules/blueconicRtdProvider_spec.js b/test/spec/modules/blueconicRtdProvider_spec.js index 4fe85d8a9c8..37bf82fbf91 100644 --- a/test/spec/modules/blueconicRtdProvider_spec.js +++ b/test/spec/modules/blueconicRtdProvider_spec.js @@ -1,5 +1,5 @@ -import {config} from 'src/config.js'; -import {RTD_LOCAL_NAME, addRealTimeData, getRealTimeData, blueconicSubmodule, storage} from 'modules/blueconicRtdProvider.js'; +import { config } from 'src/config.js'; +import { RTD_LOCAL_NAME, addRealTimeData, getRealTimeData, blueconicSubmodule, storage } from 'modules/blueconicRtdProvider.js'; describe('blueconicRtdProvider', function() { let getDataFromLocalStorageStub; @@ -22,13 +22,13 @@ describe('blueconicRtdProvider', function() { it('merges ortb2Fragment data', function() { const setConfigUserObj1 = { name: 'www.dataprovider1.com', - ext: {segtax: 1}, - segment: [{id: '1776'}] + ext: { segtax: 1 }, + segment: [{ id: '1776' }] }; const setConfigUserObj2 = { name: 'www.dataprovider2.com', - ext: {segtax: 1}, - segment: [{id: '1914'} + ext: { segtax: 1 }, + segment: [{ id: '1914' } ] }; @@ -44,8 +44,8 @@ describe('blueconicRtdProvider', function() { const rtdUserObj1 = { name: 'www.dataprovider4.com', - ext: {segtax: 1}, - segment: [{id: '1918'}, {id: '1939'} + ext: { segtax: 1 }, + segment: [{ id: '1918' }, { id: '1939' } ] }; @@ -66,16 +66,15 @@ describe('blueconicRtdProvider', function() { it('merges data without duplication', function() { const userObj1 = { name: 'www.dataprovider1.com', - ext: {segtax: 1}, - segment: [{id: '1776'} + ext: { segtax: 1 }, + segment: [{ id: '1776' } ] }; const userObj2 = { - ext: {segtax: 1}, + ext: { segtax: 1 }, name: 'www.dataprovider2.com', - segment: [{id: '1914' - }] + segment: [{ id: '1914' }] }; const bidConfig = { @@ -113,19 +112,20 @@ describe('blueconicRtdProvider', function() { requestParams: { publisherId: 'Publisher1', coppa: true - }} + } + } }; - const bidConfig = {ortb2Fragments: {global: {}}}; + const bidConfig = { ortb2Fragments: { global: {} } }; const rtdUserObj1 = { name: 'blueconic', - ext: {segtax: 1}, - segment: [{id: 'bf23d802-931d-4619-8266-ce9a6328aa2a'}], + ext: { segtax: 1 }, + segment: [{ id: 'bf23d802-931d-4619-8266-ce9a6328aa2a' }], bidId: '1234' }; - const cachedRtd = {ext: {segtax: 1}, 'segment': [{id: 'bf23d802-931d-4619-8266-ce9a6328aa2a'}], 'bidId': '1234'} + const cachedRtd = { ext: { segtax: 1 }, 'segment': [{ id: 'bf23d802-931d-4619-8266-ce9a6328aa2a' }], 'bidId': '1234' } getDataFromLocalStorageStub.withArgs(RTD_LOCAL_NAME).returns(JSON.stringify(cachedRtd)); getRealTimeData(bidConfig, () => {}, rtdConfig, {}); diff --git a/test/spec/modules/boldwinBidAdapter_spec.js b/test/spec/modules/boldwinBidAdapter_spec.js index 5d8c7fab9fd..fdaa0f19f3f 100644 --- a/test/spec/modules/boldwinBidAdapter_spec.js +++ b/test/spec/modules/boldwinBidAdapter_spec.js @@ -434,7 +434,7 @@ describe('BoldwinBidAdapter', function () { const syncData = spec.getUserSyncs({}, {}, { consentString: 'ALL', gdprApplies: true, - }, {}); + }, undefined); expect(syncData).to.be.an('array').which.is.not.empty; expect(syncData[0]).to.be.an('object') expect(syncData[0].type).to.be.a('string') @@ -443,9 +443,7 @@ describe('BoldwinBidAdapter', function () { expect(syncData[0].url).to.equal('https://sync.videowalldirect.com/image?pbjs=1&gdpr=1&gdpr_consent=ALL&coppa=0') }); it('Should return array of objects with proper sync config , include CCPA', function() { - const syncData = spec.getUserSyncs({}, {}, {}, { - consentString: '1---' - }); + const syncData = spec.getUserSyncs({}, {}, {}, '1---'); expect(syncData).to.be.an('array').which.is.not.empty; expect(syncData[0]).to.be.an('object') expect(syncData[0].type).to.be.a('string') @@ -454,7 +452,7 @@ describe('BoldwinBidAdapter', function () { expect(syncData[0].url).to.equal('https://sync.videowalldirect.com/image?pbjs=1&ccpa_consent=1---&coppa=0') }); it('Should return array of objects with proper sync config , include GPP', function() { - const syncData = spec.getUserSyncs({}, {}, {}, {}, { + const syncData = spec.getUserSyncs({}, {}, {}, undefined, { gppString: 'abc123', applicableSections: [8] }); diff --git a/test/spec/modules/brandmetricsRtdProvider_spec.js b/test/spec/modules/brandmetricsRtdProvider_spec.js index 6bc521b21c1..6e21023e9e8 100644 --- a/test/spec/modules/brandmetricsRtdProvider_spec.js +++ b/test/spec/modules/brandmetricsRtdProvider_spec.js @@ -1,5 +1,5 @@ import * as brandmetricsRTD from '../../../modules/brandmetricsRtdProvider.js'; -import {config} from 'src/config.js'; +import { config } from 'src/config.js'; import * as events from '../../../src/events.js'; import * as sinon from 'sinon'; @@ -135,7 +135,7 @@ describe('getBidRequestData', () => { it('should set targeting keys for specified bidders', () => { const bidderOrtb2 = {}; - brandmetricsRTD.brandmetricsSubmodule.getBidRequestData({ortb2Fragments: {bidder: bidderOrtb2}}, () => { + brandmetricsRTD.brandmetricsSubmodule.getBidRequestData({ ortb2Fragments: { bidder: bidderOrtb2 } }, () => { const expected = VALID_CONFIG.params.bidders expected.forEach(exp => { @@ -172,7 +172,7 @@ describe('getBidRequestData', () => { }); const bidderOrtb2 = {}; - brandmetricsRTD.brandmetricsSubmodule.getBidRequestData({ortb2Fragments: {bidder: bidderOrtb2}}, () => {}, VALID_CONFIG); + brandmetricsRTD.brandmetricsSubmodule.getBidRequestData({ ortb2Fragments: { bidder: bidderOrtb2 } }, () => {}, VALID_CONFIG); expect(Object.keys(bidderOrtb2).length).to.equal(0) }); @@ -190,7 +190,7 @@ describe('getBidRequestData', () => { }); const bidderOrtb2 = {}; - brandmetricsRTD.brandmetricsSubmodule.getBidRequestData({ortb2Fragments: {bidder: bidderOrtb2}}, () => {}, VALID_CONFIG); + brandmetricsRTD.brandmetricsSubmodule.getBidRequestData({ ortb2Fragments: { bidder: bidderOrtb2 } }, () => {}, VALID_CONFIG); const expected = VALID_CONFIG.params.bidders diff --git a/test/spec/modules/braveBidAdapter_spec.js b/test/spec/modules/braveBidAdapter_spec.js index 92a235a92ea..1d6366f72b7 100644 --- a/test/spec/modules/braveBidAdapter_spec.js +++ b/test/spec/modules/braveBidAdapter_spec.js @@ -65,25 +65,26 @@ const bidRequest = { const request_video = { code: 'brave-video-prebid', - mediaTypes: { video: { - minduration: 1, - maxduration: 999, - boxingallowed: 1, - skip: 0, - mimes: [ - 'application/javascript', - 'video/mp4' - ], - playerSize: [[768, 1024]], - protocols: [ - 2, 3 - ], - linearity: 1, - api: [ - 1, - 2 - ] - } + mediaTypes: { + video: { + minduration: 1, + maxduration: 999, + boxingallowed: 1, + skip: 0, + mimes: [ + 'application/javascript', + 'video/mp4' + ], + playerSize: [[768, 1024]], + protocols: [ + 2, 3 + ], + linearity: 1, + api: [ + 1, + 2 + ] + } }, bidder: 'brave', @@ -144,17 +145,18 @@ const response_native = { impid: 'request_imp_id', price: 5, adomain: ['example.com'], - adm: { native: + adm: { + native: { assets: [ - {id: 1, title: 'dummyText'}, - {id: 3, image: imgData}, + { id: 1, title: 'dummyText' }, + { id: 3, image: imgData }, { id: 5, - data: {value: 'organization.name'} + data: { value: 'organization.name' } } ], - link: {url: 'example.com'}, + link: { url: 'example.com' }, imptrackers: ['tracker1.com', 'tracker2.com', 'tracker3.com'], jstracker: 'tracker1.com' } @@ -340,7 +342,7 @@ describe('BraveBidAdapter', function() { creativeId: response_native.seatbid[0].bid[0].crid, dealId: response_native.seatbid[0].bid[0].dealid, mediaType: 'native', - native: {clickUrl: response_native.seatbid[0].bid[0].adm.native.link.url} + native: { clickUrl: response_native.seatbid[0].bid[0].adm.native.link.url } } const nativeResponses = spec.interpretResponse(nativeResponse); diff --git a/test/spec/modules/bridBidAdapter_spec.js b/test/spec/modules/bridBidAdapter_spec.js index 4819f817427..a0e10482d3d 100644 --- a/test/spec/modules/bridBidAdapter_spec.js +++ b/test/spec/modules/bridBidAdapter_spec.js @@ -70,7 +70,7 @@ describe('Brid Bid Adapter', function() { }; const bidderRequest = null; - const bidResponse = spec.interpretResponse({ body: responseBody }, {bidderRequest}); + const bidResponse = spec.interpretResponse({ body: responseBody }, { bidderRequest }); expect(bidResponse.length).to.equal(0); }); @@ -99,7 +99,7 @@ describe('Brid Bid Adapter', function() { bids: videoRequest }; - const bidResponse = spec.interpretResponse({ body: responseBody }, {bidderRequest}); + const bidResponse = spec.interpretResponse({ body: responseBody }, { bidderRequest }); expect(bidResponse).to.not.be.empty; const bid = bidResponse[0]; @@ -161,13 +161,13 @@ describe('Brid Bid Adapter', function() { }); it('Test userSync valid sync url for iframe', function () { - const [userSync] = spec.getUserSyncs({ iframeEnabled: true }, {}, {consentString: 'anyString'}); + const [userSync] = spec.getUserSyncs({ iframeEnabled: true }, {}, { consentString: 'anyString' }); expect(userSync.url).to.contain(SYNC_URL + 'load-cookie.html?endpoint=brid&gdpr=0&gdpr_consent=anyString'); expect(userSync.type).to.be.equal('iframe'); }); it('Test userSyncs iframeEnabled=false', function () { - const userSyncs = spec.getUserSyncs({iframeEnabled: false}); + const userSyncs = spec.getUserSyncs({ iframeEnabled: false }); expect(userSyncs).to.have.lengthOf(0); }); }); diff --git a/test/spec/modules/bridgewellBidAdapter_spec.js b/test/spec/modules/bridgewellBidAdapter_spec.js index 3802d97614d..03b0fff56fd 100644 --- a/test/spec/modules/bridgewellBidAdapter_spec.js +++ b/test/spec/modules/bridgewellBidAdapter_spec.js @@ -5,8 +5,8 @@ import { newBidder } from 'src/adapters/bidderFactory.js'; const userId = { 'criteoId': 'vYlICF9oREZlTHBGRVdrJTJCUUJnc3U2ckNVaXhrV1JWVUZVSUxzZmJlcnJZR0ZxbVhFRnU5bDAlMkJaUWwxWTlNcmdEeHFrJTJGajBWVlV4T3lFQ0FyRVcxNyUyQlIxa0lLSlFhcWJpTm9PSkdPVkx0JTJCbzlQRTQlM0Q', 'pubcid': '074864cb-3705-430e-9ff7-48ccf3c21b94', - 'sharedid': {'id': '01F61MX53D786DSB2WYD38ZVM7', 'third': '01F61MX53D786DSB2WYD38ZVM7'}, - 'uid2': {'id': 'eb33b0cb-8d35-1234-b9c0-1a31d4064777'}, + 'sharedid': { 'id': '01F61MX53D786DSB2WYD38ZVM7', 'third': '01F61MX53D786DSB2WYD38ZVM7' }, + 'uid2': { 'id': 'eb33b0cb-8d35-1234-b9c0-1a31d4064777' }, } const userIdAsEids = [{ source: 'test.org', @@ -165,16 +165,20 @@ describe('bridgewellBidAdapter', function () { expect(payload).to.be.an('object'); expect(payload.adUnits).to.be.an('array'); expect(payload.url).to.exist.and.to.equal('https://www.bridgewell.com/'); + expect(payload.userIds).to.deep.equal(userId); + expect(payload.userIdAsEids).to.deep.equal(userIdAsEids); + expect(payload.auctionId).to.equal('1d1a030790a475'); + expect(payload.bidderRequestId).to.equal('22edbae2733bf6'); for (let i = 0, max_i = payload.adUnits.length; i < max_i; i++) { const u = payload.adUnits[i]; expect(u).to.have.property('ChannelID').that.is.a('string'); expect(u).to.not.have.property('cid'); expect(u).to.have.property('adUnitCode').and.to.equal('adunit-code-2'); expect(u).to.have.property('requestId').and.to.equal('3150ccb55da321'); - expect(u).to.have.property('userIds'); - expect(u.userIds).to.deep.equal(userId); - expect(u).to.have.property('userIdAsEids'); - expect(u.userIdAsEids).to.deep.equal(userIdAsEids); + expect(u).to.have.property('transactionId'); + expect(u).to.have.property('sizes'); + expect(u).to.have.property('mediaTypes'); + expect(u).to.have.property('ortb2Imp'); } }); @@ -213,16 +217,20 @@ describe('bridgewellBidAdapter', function () { expect(payload).to.be.an('object'); expect(payload.adUnits).to.be.an('array'); expect(payload.url).to.exist.and.to.equal('https://www.bridgewell.com/'); + expect(payload.userIds).to.deep.equal(userId); + expect(payload.userIdAsEids).to.deep.equal(userIdAsEids); + expect(payload.auctionId).to.equal('1d1a030790a475'); + expect(payload.bidderRequestId).to.equal('22edbae2733bf6'); for (let i = 0, max_i = payload.adUnits.length; i < max_i; i++) { const u = payload.adUnits[i]; expect(u).to.have.property('cid').that.is.a('number'); expect(u).to.not.have.property('ChannelID'); expect(u).to.have.property('adUnitCode').and.to.equal('adunit-code-2'); expect(u).to.have.property('requestId').and.to.equal('3150ccb55da321'); - expect(u).to.have.property('userIds'); - expect(u.userIds).to.deep.equal(userId); - expect(u).to.have.property('userIdAsEids'); - expect(u.userIdAsEids).to.deep.equal(userIdAsEids); + expect(u).to.have.property('transactionId'); + expect(u).to.have.property('sizes'); + expect(u).to.have.property('mediaTypes'); + expect(u).to.have.property('ortb2Imp'); } }); @@ -240,6 +248,178 @@ describe('bridgewellBidAdapter', function () { const validBidRequests = request.validBidRequests; expect(validBidRequests).to.deep.equal(bidRequests); }); + + it('should include ortb2Imp fields in adUnit', function () { + const bidderRequest = { + refererInfo: { + page: 'https://www.bridgewell.com/', + legacy: { + referer: 'https://www.bridgewell.com/', + } + } + } + const bidRequestsWithOrtb2 = [ + { + 'bidder': 'bridgewell', + 'params': { + 'cid': 1234, + }, + 'adUnitCode': 'adunit-code-1', + 'transactionId': 'trans-123', + 'adUnitId': 'adunit-123', + 'sizes': [[300, 250]], + 'mediaTypes': { + 'banner': { + 'sizes': [[300, 250]] + } + }, + 'bidId': 'bid-123', + 'ortb2Imp': { + 'ext': { + 'data': { + 'adserver': { + 'name': 'gam', + 'adslot': '/1234/test' + }, + 'pbadslot': '/1234/test-pbadslot' + }, + 'gpid': 'test-gpid', + 'prebid': { + 'passthrough': { + 'bucket': 'test-bucket', + 'client': 'test-client', + 'gamAdCode': 'test-gam', + 'undefinedField': undefined + } + } + }, + 'banner': { + 'pos': 1 + } + }, + 'userId': userId, + 'userIdAsEids': userIdAsEids, + } + ]; + + const request = spec.buildRequests(bidRequestsWithOrtb2, bidderRequest); + const adUnit = request.data.adUnits[0]; + + expect(adUnit.transactionId).to.equal('trans-123'); + expect(adUnit.adUnitId).to.equal('adunit-123'); + expect(adUnit.sizes).to.deep.equal([[300, 250]]); + expect(adUnit.ortb2Imp.ext.data.adserver.name).to.equal('gam'); + expect(adUnit.ortb2Imp.ext.data.adserver.adslot).to.equal('/1234/test'); + expect(adUnit.ortb2Imp.ext.data.pbadslot).to.equal('/1234/test-pbadslot'); + expect(adUnit.ortb2Imp.ext.gpid).to.equal('test-gpid'); + expect(adUnit.ortb2Imp.banner.pos).to.equal(1); + expect(adUnit.ortb2Imp.ext.prebid.passthrough).to.deep.equal({ + bucket: 'test-bucket', + client: 'test-client', + gamAdCode: 'test-gam' + }); + expect(adUnit.ortb2Imp.ext.prebid.passthrough).to.not.have.property('undefinedField'); + }); + + it('should include floor information when getFloor is available', function () { + const bidderRequest = { + refererInfo: { + page: 'https://www.bridgewell.com/', + legacy: { + referer: 'https://www.bridgewell.com/', + } + } + } + const bidRequestsWithFloor = [ + { + 'bidder': 'bridgewell', + 'params': { + 'cid': 1234, + }, + 'adUnitCode': 'adunit-code-1', + 'mediaTypes': { + 'banner': { + 'sizes': [[300, 250]] + } + }, + 'bidId': 'bid-123', + 'getFloor': function() { + return { + floor: 1.5, + currency: 'USD' + }; + }, + 'userId': userId, + 'userIdAsEids': userIdAsEids, + } + ]; + + const request = spec.buildRequests(bidRequestsWithFloor, bidderRequest); + const adUnit = request.data.adUnits[0]; + + expect(adUnit.floor).to.equal(1.5); + expect(adUnit.currency).to.equal('USD'); + }); + + it('should include additional bid request fields in payload', function () { + const bidderRequest = { + refererInfo: { + page: 'https://www.bridgewell.com/', + ref: 'https://www.referrer.com/', + legacy: { + referer: 'https://www.bridgewell.com/', + } + }, + ortb2: { + site: { + name: 'test-site' + } + } + } + const bidRequestsWithMetrics = [ + { + 'bidder': 'bridgewell', + 'params': { + 'cid': 1234, + }, + 'adUnitCode': 'adunit-code-1', + 'mediaTypes': { + 'banner': { + 'sizes': [[300, 250]] + } + }, + 'bidId': 'bid-123', + 'bidderRequestId': 'bidder-req-123', + 'auctionId': 'auction-123', + 'src': 's2s', + 'auctionsCount': 5, + 'bidRequestsCount': 10, + 'bidderRequestsCount': 3, + 'bidderWinsCount': 2, + 'deferBilling': true, + 'metrics': { + 'test': 'metric' + }, + 'userId': userId, + 'userIdAsEids': userIdAsEids, + } + ]; + + const request = spec.buildRequests(bidRequestsWithMetrics, bidderRequest); + const payload = request.data; + + expect(payload.auctionId).to.equal('auction-123'); + expect(payload.bidderRequestId).to.equal('bidder-req-123'); + expect(payload.src).to.equal('s2s'); + expect(payload.auctionsCount).to.equal(5); + expect(payload.bidRequestsCount).to.equal(10); + expect(payload.bidderRequestsCount).to.equal(3); + expect(payload.bidderWinsCount).to.equal(2); + expect(payload.deferBilling).to.equal(true); + expect(payload.metrics).to.deep.equal({ test: 'metric' }); + expect(payload.referrer).to.equal('https://www.referrer.com/'); + expect(payload.ortb2).to.deep.equal({ site: { name: 'test-site' } }); + }); }); describe('interpretResponse', function () { diff --git a/test/spec/modules/browsiBidAdapter_spec.js b/test/spec/modules/browsiBidAdapter_spec.js index a4f1778af6d..f7a99f014af 100644 --- a/test/spec/modules/browsiBidAdapter_spec.js +++ b/test/spec/modules/browsiBidAdapter_spec.js @@ -1,8 +1,8 @@ -import {ENDPOINT, spec} from 'modules/browsiBidAdapter.js'; -import {config} from 'src/config.js'; -import {VIDEO, BANNER} from 'src/mediaTypes.js'; +import { ENDPOINT, spec } from 'modules/browsiBidAdapter.js'; +import { config } from 'src/config.js'; +import { VIDEO, BANNER } from 'src/mediaTypes.js'; -const {expect} = require('chai'); +const { expect } = require('chai'); const DATA = 'brwvidtag'; const ADAPTER = '__bad'; @@ -52,7 +52,7 @@ describe('browsi Bid Adapter Test', function () { let bidderRequest; beforeEach(function () { window[DATA] = {} - window[DATA][ADAPTER] = {index: 0}; + window[DATA][ADAPTER] = { index: 0 }; bidRequest = [ { 'params': { @@ -76,7 +76,7 @@ describe('browsi Bid Adapter Test', function () { } } }, - 'mediaTypes': {video: {playerSize: [640, 480]}} + 'mediaTypes': { video: { playerSize: [640, 480] } } } ]; bidderRequest = { @@ -119,7 +119,7 @@ describe('browsi Bid Adapter Test', function () { gdpr: bidderRequest.gdprConsent, ccpa: bidderRequest.uspConsent, sizes: inputRequest.sizes, - video: {playerSize: [640, 480]}, + video: { playerSize: [640, 480] }, aUCode: inputRequest.adUnitCode, aID: inputRequest.auctionId, tID: inputRequest.ortb2Imp.ext.tid, @@ -135,7 +135,7 @@ describe('browsi Bid Adapter Test', function () { expect(requests[0].data.timeout).to.equal(8000); }); it('should pass timeout in config', function() { - config.setConfig({'bidderTimeout': 6000}); + config.setConfig({ 'bidderTimeout': 6000 }); const requests = spec.buildRequests(bidRequest, bidderRequest); expect(requests[0].data.timeout).to.equal(6000); }); @@ -186,32 +186,32 @@ describe('browsi Bid Adapter Test', function () { describe('getUserSyncs', function () { const bidResponse = { userSyncs: [ - {url: 'syncUrl1', type: 'image'}, - {url: 'http://syncUrl2', type: 'iframe'} + { url: 'syncUrl1', type: 'image' }, + { url: 'http://syncUrl2', type: 'iframe' } ] } const serverResponse = [ - {body: bidResponse} + { body: bidResponse } ]; it('should return iframe type userSync', function () { - const userSyncs = spec.getUserSyncs({iframeEnabled: true, pixelEnabled: false}, serverResponse[0]); + const userSyncs = spec.getUserSyncs({ iframeEnabled: true, pixelEnabled: false }, serverResponse[0]); expect(userSyncs.length).to.equal(1); const userSync = userSyncs[0]; expect(userSync.url).to.equal('http://syncUrl2'); expect(userSync.type).to.equal('iframe'); }); it('should return image type userSyncs', function () { - const userSyncs = spec.getUserSyncs({iframeEnabled: false, pixelEnabled: true}, serverResponse[0]); + const userSyncs = spec.getUserSyncs({ iframeEnabled: false, pixelEnabled: true }, serverResponse[0]); const userSync = userSyncs[0]; expect(userSync.url).to.equal('http://syncUrl1'); expect(userSync.type).to.equal('image'); }); it('should handle multiple server responses', function () { - const userSyncs = spec.getUserSyncs({iframeEnabled: false, pixelEnabled: true}, serverResponse); + const userSyncs = spec.getUserSyncs({ iframeEnabled: false, pixelEnabled: true }, serverResponse); expect(userSyncs.length).to.equal(1); }); it('should return empty userSyncs', function () { - const userSyncs = spec.getUserSyncs({iframeEnabled: false, pixelEnabled: false}, serverResponse); + const userSyncs = spec.getUserSyncs({ iframeEnabled: false, pixelEnabled: false }, serverResponse); expect(userSyncs.length).to.equal(0); }); }); diff --git a/test/spec/modules/bucksenseBidAdapter_spec.js b/test/spec/modules/bucksenseBidAdapter_spec.js index 533dc2f014c..d6417d1bebf 100644 --- a/test/spec/modules/bucksenseBidAdapter_spec.js +++ b/test/spec/modules/bucksenseBidAdapter_spec.js @@ -1,5 +1,5 @@ -import {expect} from 'chai'; -import {spec} from 'modules/bucksenseBidAdapter.js'; +import { expect } from 'chai'; +import { spec } from 'modules/bucksenseBidAdapter.js'; describe('Bucksense Adapter', function() { const BIDDER_CODE = 'bucksense'; @@ -54,12 +54,12 @@ describe('Bucksense Adapter', function() { }, 'mediaTypes': { 'banner': { - 'sizes': [ [ 300, 250 ], [ 300, 600 ] ] + 'sizes': [[300, 250], [300, 600]] } }, 'adUnitCode': 'test-div', 'transactionId': '52b3ed07-2a09-4f58-a426-75acc7602c96', - 'sizes': [ [ 300, 250 ], [ 300, 600 ] ], + 'sizes': [[300, 250], [300, 600]], 'bidId': '22aecdacdcd773', 'bidderRequestId': '1feebcb5938c7e', 'auctionId': '73540558-86cb-4eef-895f-bf99c5353bd7', @@ -109,7 +109,7 @@ describe('Bucksense Adapter', function() { 'params': { 'placementId': '1000' }, - 'sizes': [ [ 300, 250 ], [ 300, 600 ] ] + 'sizes': [[300, 250], [300, 600]] } }; diff --git a/test/spec/modules/buzzoolaBidAdapter_spec.js b/test/spec/modules/buzzoolaBidAdapter_spec.js index 5bb60cd12bd..6fff511852d 100644 --- a/test/spec/modules/buzzoolaBidAdapter_spec.js +++ b/test/spec/modules/buzzoolaBidAdapter_spec.js @@ -1,20 +1,20 @@ -import {expect} from 'chai'; -import {spec} from 'modules/buzzoolaBidAdapter.js'; -import {newBidder} from 'src/adapters/bidderFactory.js'; +import { expect } from 'chai'; +import { spec } from 'modules/buzzoolaBidAdapter.js'; +import { newBidder } from 'src/adapters/bidderFactory.js'; import '../../../src/prebid.js'; -import {executeRenderer, Renderer} from '../../../src/Renderer.js'; -import {deepClone} from '../../../src/utils.js'; +import { executeRenderer, Renderer } from '../../../src/Renderer.js'; +import { deepClone } from '../../../src/utils.js'; 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]]}}, + 'mediaTypes': { 'banner': { 'sizes': [[240, 400], [300, 600]] } }, 'sizes': [[240, 400], [300, 600]] }, { 'bidder': 'buzzoola', - 'params': {'placementId': 417846}, + 'params': { 'placementId': 417846 }, 'sizes': [[240, 400], [300, 600]] }, { 'bidder': 'buzzoola', @@ -28,13 +28,13 @@ const INVALID_BIDS = [{ } }, { 'bidder': 'buzzoola', - 'params': {'placementId': 417845} + 'params': { 'placementId': 417845 } }]; const BANNER_BID = { 'bidder': 'buzzoola', - 'params': {'placementId': 417846}, - 'mediaTypes': {'banner': {'sizes': [[240, 400], [300, 600]]}}, + 'params': { 'placementId': 417846 }, + 'mediaTypes': { 'banner': { 'sizes': [[240, 400], [300, 600]] } }, 'sizes': [[240, 400], [300, 600]], 'bidId': '2a11641ada3c6a' }; @@ -79,7 +79,7 @@ const REQUIRED_BANNER_FIELDS = [ const VIDEO_BID = { 'bidder': 'buzzoola', - 'params': {'placementId': 417845}, + 'params': { 'placementId': 417845 }, 'mediaTypes': { 'video': { 'context': 'instream', @@ -140,7 +140,7 @@ const REQUIRED_VIDEO_FIELDS = [ const NATIVE_BID = { 'bidder': 'buzzoola', - 'params': {'placementId': 417845}, + 'params': { 'placementId': 417845 }, 'mediaTypes': { 'native': { 'image': { @@ -296,13 +296,13 @@ describe('buzzoolaBidAdapter', () => { const emptyResponse = ''; function nobidServerResponseCheck(request, response = noBidServerResponse) { - const noBidResult = spec.interpretResponse({body: response}, {data: request}); + 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}); + const result = spec.interpretResponse({ body: response }, { data: request }); expect(result).to.deep.equal(response); result.forEach(bid => { @@ -369,7 +369,7 @@ describe('buzzoolaBidAdapter', () => { const scriptStub = sinon.stub(document, 'createElement'); scriptStub.withArgs('script').returns(scriptElement); - result = spec.interpretResponse({body: VIDEO_RESPONSE}, {data: outstreamVideoRequest})[0]; + result = spec.interpretResponse({ body: VIDEO_RESPONSE }, { data: outstreamVideoRequest })[0]; renderer = result.renderer; result.adUnitCode = 'adUnitCode'; diff --git a/test/spec/modules/byDataAnalyticsAdapter_spec.js b/test/spec/modules/byDataAnalyticsAdapter_spec.js index 862a0ee79e5..2fd2efdfe32 100644 --- a/test/spec/modules/byDataAnalyticsAdapter_spec.js +++ b/test/spec/modules/byDataAnalyticsAdapter_spec.js @@ -1,6 +1,6 @@ import ascAdapter from 'modules/byDataAnalyticsAdapter'; import { expect } from 'chai'; -import {EVENTS} from 'src/constants.js'; +import { EVENTS } from 'src/constants.js'; const adapterManager = require('src/adapterManager').default; const events = require('src/events'); @@ -36,7 +36,7 @@ const noBidArgs = { bidId: '14480e9832f2d2b', bidder: 'appnexus', bidderRequestId: '13b87b6c20d3636', - mediaTypes: {banner: {sizes: [[300, 250], [250, 250]]}}, + mediaTypes: { banner: { sizes: [[300, 250], [250, 250]] } }, sizes: [[300, 250], [250, 250]], src: 'client', transactionId: 'c8ee3914-1ee0-4ce6-9126-748d5692188c' @@ -57,9 +57,9 @@ const auctionEndArgs = { adUnitCodes: ['div-gpt-ad-mrec1'], adUnits: [{ code: 'div-gpt-ad-mrec1', - mediaTypes: {banner: {sizes: [[300, 250], [250, 250]]}}, + mediaTypes: { banner: { sizes: [[300, 250], [250, 250]] } }, sizes: [[300, 250], [250, 250]], - bids: [{bidder: 'appnexus', params: {placementId: '19305195'}}], + bids: [{ bidder: 'appnexus', params: { placementId: '19305195' } }], transactionId: 'c8ee3914-1ee0-4ce6-9126-748d5692188c' }], auctionEnd: 1627973487504, @@ -80,7 +80,7 @@ const auctionEndArgs = { bidder: 'appnexus', bidderRequestId: '13b87b6c20d3636', src: 'client', - mediaTypes: {banner: {sizes: [[300, 250], [250, 250]]}}, + mediaTypes: { banner: { sizes: [[300, 250], [250, 250]] } }, sizes: [[300, 250], [250, 250]], transactionId: 'c8ee3914-1ee0-4ce6-9126-748d5692188c' } @@ -91,7 +91,7 @@ const expectedDataArgs = { visitor_data: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOiIyNzFhOC0yYjg2LWY0YTQtZjU5YmMiLCJjaWQiOiJhc2MwMDAwMCIsInBpZCI6Ind3dy5sZXRzcnVuLmNvbSIsIm9zIjoiTWFjaW50b3NoIiwib3N2IjoxMC4xNTcsImJyIjoiQ2hyb21lIiwiYnJ2IjoxMDMsInNzIjp7IndpZHRoIjoxNzkyLCJoZWlnaHQiOjExMjB9LCJkZSI6IkRlc2t0b3AiLCJ0eiI6IkFzaWEvQ2FsY3V0dGEifQ.Oj3qnh--t06XO-foVmrMJCGqFfOBed09A-f7LZX5rtfBf4w1_RNRZ4F3on4TMPLonSa7GgzbcEfJS9G_amnleQ', aid: auctionId, as: 1627973484504, - auctionData: [ { + auctionData: [{ au: 'div-gpt-ad-mrec1', auc: 'div-gpt-ad-mrec1', aus: '300x250', diff --git a/test/spec/modules/cadent_aperture_mxBidAdapter_spec.js b/test/spec/modules/cadent_aperture_mxBidAdapter_spec.js index de33d5ff6da..b82b1010d12 100644 --- a/test/spec/modules/cadent_aperture_mxBidAdapter_spec.js +++ b/test/spec/modules/cadent_aperture_mxBidAdapter_spec.js @@ -747,7 +747,7 @@ describe('cadent_aperture_mx Adapter', function () { const vastServerResponse = utils.deepClone(serverResponse); vastServerResponse.seatbid[0].bid[0].adm = ''; vastServerResponse.seatbid[1].bid[0].adm = ''; - const result = spec.interpretResponse({body: vastServerResponse}, bid_outstream); + const result = spec.interpretResponse({ body: vastServerResponse }, bid_outstream); const ad0 = result[0]; const ad1 = result[1]; expect(ad0.renderer).to.exist.and.to.be.a('object'); @@ -779,7 +779,7 @@ describe('cadent_aperture_mx Adapter', function () { it('returns valid advertiser domains', function () { const bidResponse = utils.deepClone(serverResponse); - const result = spec.interpretResponse({body: bidResponse}); + const result = spec.interpretResponse({ body: bidResponse }); expect(result[0].meta.advertiserDomains).to.deep.equal(expectedResponse[0].meta.advertiserDomains); // case where adomains are not in request expect(result[1].meta).to.not.exist; @@ -836,7 +836,7 @@ describe('cadent_aperture_mx Adapter', function () { }); it('should pass gpp string and section id', function() { - const syncs = spec.getUserSyncs({iframeEnabled: true}, {}, {}, {}, { + const syncs = spec.getUserSyncs({ iframeEnabled: true }, {}, {}, {}, { gppString: 'abcdefgs', applicableSections: [1, 2, 4] }); diff --git a/test/spec/modules/carodaBidAdapter_spec.js b/test/spec/modules/carodaBidAdapter_spec.js index 063294410da..5a95140c9cc 100644 --- a/test/spec/modules/carodaBidAdapter_spec.js +++ b/test/spec/modules/carodaBidAdapter_spec.js @@ -208,8 +208,8 @@ describe('Caroda adapter', function () { bid_id: 'bidId', params: {}, userIdAsEids: [ - { source: 'adserver.org', uids: [ { id: 'TTD_ID_FROM_USER_ID_MODULE', atype: 1, ext: { rtiPartner: 'TDID' } } ] }, - { source: 'pubcid.org', uids: [ { id: 'pubCommonId_FROM_USER_ID_MODULE', atype: 1 } ] } + { source: 'adserver.org', uids: [{ id: 'TTD_ID_FROM_USER_ID_MODULE', atype: 1, ext: { rtiPartner: 'TDID' } }] }, + { source: 'pubcid.org', uids: [{ id: 'pubCommonId_FROM_USER_ID_MODULE', atype: 1 }] } ] }]; @@ -297,21 +297,21 @@ describe('Caroda adapter', function () { describe('price floors', function () { it('should not add if floors module not configured', function () { - const validBidRequests = [{ bid_id: 'bidId', params: {ctok: 'ctok1'}, mediaTypes: {video: {}} }]; + const validBidRequests = [{ bid_id: 'bidId', params: { ctok: 'ctok1' }, mediaTypes: { video: {} } }]; const imp = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } })[0].data); assert.equal(imp.bidfloor, undefined); assert.equal(imp.bidfloorcur, undefined); }); it('should not add if floor price not defined', function () { - const validBidRequests = [ getBidWithFloor() ]; + const validBidRequests = [getBidWithFloor()]; const imp = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } })[0].data); assert.equal(imp.bidfloor, undefined); assert.equal(imp.bidfloorcur, 'EUR'); }); it('should request floor price in adserver currency', function () { - const validBidRequests = [ getBidWithFloor() ]; + const validBidRequests = [getBidWithFloor()]; setCurrencyConfig({ adServerCurrency: 'DKK' }); const bidderRequest = { refererInfo: { page: 'page' } }; return addFPDToBidderRequest(bidderRequest).then(res => { @@ -323,7 +323,7 @@ describe('Caroda adapter', function () { }); it('should add correct floor values', function () { - const expectedFloors = [ 1, 1.3, 0.5 ]; + const expectedFloors = [1, 1.3, 0.5]; const validBidRequests = expectedFloors.map(getBidWithFloor); const imps = spec .buildRequests(validBidRequests, { refererInfo: { page: 'page' } }) @@ -367,7 +367,7 @@ describe('Caroda adapter', function () { native: {} } }]; - const [ first, second ] = spec + const [first, second] = spec .buildRequests(validBidRequests, { refererInfo: { page: 'page' } }) .map(r => JSON.parse(r.data)); @@ -392,7 +392,7 @@ describe('Caroda adapter', function () { }]; const { banner } = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } })[0].data); assert.deepEqual(banner, { - format: [ { w: 100, h: 100 }, { w: 200, h: 300 } ] + format: [{ w: 100, h: 100 }, { w: 200, h: 300 }] }); }); }); diff --git a/test/spec/modules/categoryTranslation_spec.js b/test/spec/modules/categoryTranslation_spec.js deleted file mode 100644 index 3aeca0fbf75..00000000000 --- a/test/spec/modules/categoryTranslation_spec.js +++ /dev/null @@ -1,109 +0,0 @@ -import { getAdserverCategoryHook, initTranslation, storage } from 'modules/categoryTranslation.js'; -import { config } from 'src/config.js'; -import * as utils from 'src/utils.js'; -import { expect } from 'chai'; -import {server} from '../../mocks/xhr.js'; - -describe('category translation', function () { - let getLocalStorageStub; - - beforeEach(function () { - getLocalStorageStub = sinon.stub(storage, 'getDataFromLocalStorage'); - }); - - afterEach(function() { - 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' - } - } - })); - const bid = { - meta: { - primaryCatId: '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' - } - } - })); - const bid = { - meta: { - primaryCatId: '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 () { - const clock = sinon.useFakeTimers(utils.timestamp()); - getLocalStorageStub.returns(JSON.stringify({ - lastUpdated: utils.timestamp(), - mapping: { - 'iab-1': '1' - } - })); - initTranslation(); - expect(server.requests.length).to.equal(0); - clock.restore(); - }); - - it('should make ajax call to update mapping file if data found in localstorage is expired', function () { - const clock = sinon.useFakeTimers(utils.timestamp()); - getLocalStorageStub.returns(JSON.stringify({ - lastUpdated: utils.timestamp() - 2 * 24 * 60 * 60 * 1000, - mapping: { - 'iab-1': '1' - } - })); - initTranslation(); - expect(server.requests.length).to.equal(1); - 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(server.requests.length).to.equal(1); - expect(server.requests[0].url).to.equal('http://sample.com/'); - }); - - it('should use publisher defined mapping file', function () { - config.setConfig({ - 'brandCategoryTranslation': { - 'translationFile': 'http://sample.com' - } - }); - getLocalStorageStub.returns(null); - initTranslation('http://sample.com', 'somekey'); - expect(server.requests.length).to.equal(2); - expect(server.requests[0].url).to.equal('http://sample.com/'); - }); -}); diff --git a/test/spec/modules/ccxBidAdapter_spec.js b/test/spec/modules/ccxBidAdapter_spec.js index 83865c72907..99acf6ec971 100644 --- a/test/spec/modules/ccxBidAdapter_spec.js +++ b/test/spec/modules/ccxBidAdapter_spec.js @@ -1,5 +1,5 @@ import { expect } from 'chai'; -import {addFPDToBidderRequest} from '../../helpers/fpd.js'; +import { addFPDToBidderRequest } from '../../helpers/fpd.js'; import { spec } from 'modules/ccxBidAdapter.js'; import * as utils from 'src/utils.js'; @@ -84,7 +84,7 @@ describe('ccxAdapter', function () { }); it('Valid bid request - default', function () { - const response = spec.buildRequests(bids, {bids, bidderRequestId: 'id'}); + const response = spec.buildRequests(bids, { bids, bidderRequestId: 'id' }); expect(response).to.be.not.empty; expect(response.data).to.be.not.empty; @@ -171,7 +171,7 @@ describe('ccxAdapter', function () { bidsClone[1].params.video.skip = 1; bidsClone[1].params.video.skipafter = 5; - const response = spec.buildRequests(bidsClone, {'bids': bidsClone}); + const response = spec.buildRequests(bidsClone, { 'bids': bidsClone }); const data = JSON.parse(response.data); expect(data.imp).to.deep.have.same.members(imps); @@ -217,7 +217,7 @@ describe('ccxAdapter', function () { } ]; - const response = spec.buildRequests(bidsClone, {'bids': bidsClone}); + const response = spec.buildRequests(bidsClone, { 'bids': bidsClone }); const data = JSON.parse(response.data); expect(data.imp).to.deep.have.same.members(imps); @@ -246,7 +246,7 @@ describe('ccxAdapter', function () { } ]; - const response = spec.buildRequests(bidsClone, {'bids': bidsClone}); + const response = spec.buildRequests(bidsClone, { 'bids': bidsClone }); const data = JSON.parse(response.data); expect(data.imp).to.deep.have.same.members(imps); @@ -260,7 +260,7 @@ describe('ccxAdapter', function () { consentString: 'awefasdfwefasdfasd', gdprApplies: true }; - const response = spec.buildRequests(bidsClone, {'bids': bidsClone, 'gdprConsent': gdprConsent}); + const response = spec.buildRequests(bidsClone, { 'bids': bidsClone, 'gdprConsent': gdprConsent }); const data = JSON.parse(response.data); expect(data.regs.ext.gdpr).to.equal(1); @@ -270,7 +270,7 @@ describe('ccxAdapter', function () { describe('GDPR absence conformity', function () { it('should transmit correct data', function () { - const response = spec.buildRequests(bids, {bids}); + const response = spec.buildRequests(bids, { bids }); const data = JSON.parse(response.data); expect(data.regs).to.be.undefined; @@ -362,7 +362,7 @@ describe('ccxAdapter', function () { } } ]; - expect(spec.interpretResponse({body: response})).to.deep.have.same.members(bidResponses); + expect(spec.interpretResponse({ body: response })).to.deep.have.same.members(bidResponses); }); it('Valid bid response - single', function () { @@ -383,7 +383,7 @@ describe('ccxAdapter', function () { } } ]; - expect(spec.interpretResponse({body: response})).to.deep.have.same.members(bidResponses); + expect(spec.interpretResponse({ body: response })).to.deep.have.same.members(bidResponses); }); it('Empty bid response', function () { @@ -408,7 +408,7 @@ describe('ccxAdapter', function () { url: 'http://foo.sync?param=2' } ]; - expect(spec.getUserSyncs(syncOptions, [{body: response}])).to.deep.have.same.members(expectedSyncs); + expect(spec.getUserSyncs(syncOptions, [{ body: response }])).to.deep.have.same.members(expectedSyncs); }); it('Valid syncs - only image', function () { @@ -421,23 +421,23 @@ describe('ccxAdapter', function () { type: 'image', url: 'http://foo.sync?param=1' } ]; - expect(spec.getUserSyncs(syncOptions, [{body: response}])).to.deep.have.same.members(expectedSyncs); + expect(spec.getUserSyncs(syncOptions, [{ body: response }])).to.deep.have.same.members(expectedSyncs); }); it('Valid syncs - only iframe', function () { - const syncOptions = {iframeEnabled: true, pixelEnabled: false}; + const syncOptions = { iframeEnabled: true, pixelEnabled: false }; const expectedSyncs = [ { type: 'iframe', url: 'http://foo.sync?param=2' } ]; - expect(spec.getUserSyncs(syncOptions, [{body: response}])).to.deep.have.same.members(expectedSyncs); + expect(spec.getUserSyncs(syncOptions, [{ body: response }])).to.deep.have.same.members(expectedSyncs); }); it('Valid syncs - empty', function () { - const syncOptions = {iframeEnabled: true, pixelEnabled: true}; + const syncOptions = { iframeEnabled: true, pixelEnabled: true }; response.ext.usersync = {}; - expect(spec.getUserSyncs(syncOptions, [{body: response}])).to.be.empty; + expect(spec.getUserSyncs(syncOptions, [{ body: response }])).to.be.empty; }); }); @@ -489,84 +489,10 @@ describe('ccxAdapter', function () { const bidsClone = utils.deepClone(bids); - const response = spec.buildRequests(bidsClone, {'bids': bidsClone}); + const response = spec.buildRequests(bidsClone, { 'bids': bidsClone }); const data = JSON.parse(response.data); expect(data.imp).to.deep.have.same.members(imps); }); }); - - describe('FLEDGE', function () { - it('should properly build a request when FLEDGE is enabled', async function () { - const bidderRequest = { - paapi: { - enabled: true - } - }; - const bids = [ - { - adUnitCode: 'banner', - auctionId: '0b9de793-8eda-481e-a548-aaaaaaaaaaa1', - bidId: '2e56e1af51ccc1', - bidder: 'ccx', - bidderRequestId: '17e7b9f58accc1', - mediaTypes: { - banner: { - sizes: [[300, 250]] - } - }, - params: { - placementId: 609 - }, - sizes: [[300, 250]], - transactionId: 'befddd38-cfa0-48ab-8bdd-bbbbbbbbbbb1', - ortb2Imp: { - ext: { - ae: 1 - } - } - } - ]; - - const ortbRequest = spec.buildRequests(bids, await addFPDToBidderRequest(bidderRequest)); - const data = JSON.parse(ortbRequest.data); - expect(data.imp[0].ext.ae).to.equal(1); - }); - - it('should properly build a request when FLEDGE is disabled', async function () { - const bidderRequest = { - paapi: { - enabled: false - } - }; - const bids = [ - { - adUnitCode: 'banner', - auctionId: '0b9de793-8eda-481e-a548-aaaaaaaaaaa2', - bidId: '2e56e1af51ccc2', - bidder: 'ccx', - bidderRequestId: '17e7b9f58accc2', - mediaTypes: { - banner: { - sizes: [[300, 250]] - } - }, - params: { - placementId: 610 - }, - sizes: [[300, 250]], - transactionId: 'befddd38-cfa0-48ab-8bdd-bbbbbbbbbbb2', - ortb2Imp: { - ext: { - ae: 1 - } - } - } - ]; - - const ortbRequest = spec.buildRequests(bids, await addFPDToBidderRequest(bidderRequest)); - const data = JSON.parse(ortbRequest.data); - expect(data.imp[0].ext.ae).to.be.undefined; - }); - }); }); diff --git a/test/spec/modules/chromeAiRtdProvider_spec.js b/test/spec/modules/chromeAiRtdProvider_spec.js index 744f09ed4df..dfbf454bff9 100644 --- a/test/spec/modules/chromeAiRtdProvider_spec.js +++ b/test/spec/modules/chromeAiRtdProvider_spec.js @@ -135,6 +135,71 @@ describe('Chrome AI RTD Provider', function () { expect(chromeAiRtdProvider.CONSTANTS.SUBMODULE_NAME).to.equal('chromeAi'); expect(chromeAiRtdProvider.CONSTANTS.STORAGE_KEY).to.equal('chromeAi_detected_data'); expect(chromeAiRtdProvider.CONSTANTS.MIN_TEXT_LENGTH).to.be.a('number'); + expect(chromeAiRtdProvider.CONSTANTS.MAX_TEXT_LENGTH).to.be.a('number'); + expect(chromeAiRtdProvider.CONSTANTS.MAX_TEXT_LENGTH).to.equal(1000); + }); + }); + + // Test getPageText and text truncation + describe('getPageText (text truncation)', function () { + // Override document.body.textContent via Object.defineProperty so we can + // control the value returned to getPageText() without mutating the actual + // DOM (which would break the Karma test-runner UI). + function setBodyText(text) { + Object.defineProperty(document.body, 'textContent', { + get: () => text, + configurable: true + }); + } + + afterEach(function () { + // Remove the instance-level override to restore the inherited getter + delete document.body.textContent; + }); + + it('should return null for text shorter than MIN_TEXT_LENGTH', function () { + setBodyText('short'); + const result = chromeAiRtdProvider.getPageText(); + expect(result).to.be.null; + expect(logMessageStub.calledWith(sinon.match('Not enough text content'))).to.be.true; + }); + + it('should return null for empty text', function () { + setBodyText(''); + const result = chromeAiRtdProvider.getPageText(); + expect(result).to.be.null; + }); + + it('should return full text when length is between MIN and MAX', function () { + const text = 'A'.repeat(500); + setBodyText(text); + const result = chromeAiRtdProvider.getPageText(); + expect(result).to.equal(text); + expect(result).to.have.lengthOf(500); + }); + + it('should return text at exactly MAX_TEXT_LENGTH without truncating', function () { + const exactText = 'B'.repeat(chromeAiRtdProvider.CONSTANTS.MAX_TEXT_LENGTH); + setBodyText(exactText); + const result = chromeAiRtdProvider.getPageText(); + expect(result).to.equal(exactText); + expect(logMessageStub.calledWith(sinon.match('Truncating'))).to.be.false; + }); + + it('should truncate text exceeding MAX_TEXT_LENGTH', function () { + const longText = 'C'.repeat(2000); + setBodyText(longText); + const result = chromeAiRtdProvider.getPageText(); + expect(result).to.have.lengthOf(chromeAiRtdProvider.CONSTANTS.MAX_TEXT_LENGTH); + expect(result).to.equal('C'.repeat(1000)); + }); + + it('should log a message when truncating text', function () { + setBodyText('D'.repeat(2000)); + chromeAiRtdProvider.getPageText(); + expect(logMessageStub.calledWith( + sinon.match('Truncating text from 2000 to 1000') + )).to.be.true; }); }); diff --git a/test/spec/modules/clickforceBidAdapter_spec.js b/test/spec/modules/clickforceBidAdapter_spec.js index 42b0f6e5017..e95f7f9af7c 100644 --- a/test/spec/modules/clickforceBidAdapter_spec.js +++ b/test/spec/modules/clickforceBidAdapter_spec.js @@ -160,13 +160,13 @@ describe('ClickforceAdapter', function () { it('should get the correct bid response by display ad', function () { let bidderRequest; - const result = spec.interpretResponse({ body: response }, {bidderRequest}); + const result = spec.interpretResponse({ body: response }, { bidderRequest }); expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); }); it('should get the correct bid response by native ad', function () { let bidderRequest; - const result = spec.interpretResponse({ body: response1 }, {bidderRequest}); + const result = spec.interpretResponse({ body: response1 }, { bidderRequest }); expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse1[0])); }); diff --git a/test/spec/modules/codefuelBidAdapter_spec.js b/test/spec/modules/codefuelBidAdapter_spec.js index 4e891c8ce2c..d304f4bf20c 100644 --- a/test/spec/modules/codefuelBidAdapter_spec.js +++ b/test/spec/modules/codefuelBidAdapter_spec.js @@ -1,8 +1,8 @@ -import {expect} from 'chai'; -import {spec} from 'modules/codefuelBidAdapter.js'; -import {config} from 'src/config.js'; +import { expect } from 'chai'; +import { spec } from 'modules/codefuelBidAdapter.js'; +import { config } from 'src/config.js'; import * as utils from 'src/utils.js'; -import {server} from 'test/mocks/xhr'; +import { server } from 'test/mocks/xhr'; const USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/92.0.4515.159 Safari/537.36'; const DEFAULT_USER_AGENT = window.navigator.userAgent; @@ -269,7 +269,7 @@ describe('Codefuel Adapter', function () { ad: '
ad
', width: 300, height: 250, - meta: {'advertiserDomains': []} + meta: { 'advertiserDomains': [] } } ] @@ -294,25 +294,25 @@ describe('Codefuel Adapter', function () { }) it('should not return user sync if pixel enabled with codefuel config', function () { - const ret = spec.getUserSyncs({pixelEnabled: true}) + const ret = spec.getUserSyncs({ pixelEnabled: true }) expect(ret).to.be.an('array').that.is.empty }) it('should not return user sync if pixel disabled', function () { - const ret = spec.getUserSyncs({pixelEnabled: false}) + const ret = spec.getUserSyncs({ pixelEnabled: false }) expect(ret).to.be.an('array').that.is.empty }) it('should not return user sync if url is not set', function () { config.resetConfig() - const ret = spec.getUserSyncs({pixelEnabled: true}) + const ret = spec.getUserSyncs({ pixelEnabled: true }) expect(ret).to.be.an('array').that.is.empty }) it('should not pass GDPR consent', function() { - expect(spec.getUserSyncs({ pixelEnabled: true }, {}, {gdprApplies: true, consentString: 'foo'}, undefined)).to.to.be.an('array').that.is.empty - expect(spec.getUserSyncs({ pixelEnabled: true }, {}, {gdprApplies: false, consentString: 'foo'}, undefined)).to.be.an('array').that.is.empty - expect(spec.getUserSyncs({ pixelEnabled: true }, {}, {gdprApplies: true, consentString: undefined}, undefined)).to.be.an('array').that.is.empty + expect(spec.getUserSyncs({ pixelEnabled: true }, {}, { gdprApplies: true, consentString: 'foo' }, undefined)).to.to.be.an('array').that.is.empty + expect(spec.getUserSyncs({ pixelEnabled: true }, {}, { gdprApplies: false, consentString: 'foo' }, undefined)).to.be.an('array').that.is.empty + expect(spec.getUserSyncs({ pixelEnabled: true }, {}, { gdprApplies: true, consentString: undefined }, undefined)).to.be.an('array').that.is.empty }); it('should not pass US consent', function() { @@ -320,7 +320,7 @@ describe('Codefuel Adapter', function () { }); it('should pass GDPR and US consent', function() { - expect(spec.getUserSyncs({ pixelEnabled: true }, {}, {gdprApplies: true, consentString: 'foo'}, '1NYN')).to.be.an('array').that.is.empty + expect(spec.getUserSyncs({ pixelEnabled: true }, {}, { gdprApplies: true, consentString: 'foo' }, '1NYN')).to.be.an('array').that.is.empty }); }) }) diff --git a/test/spec/modules/coinzillaBidAdapter_spec.js b/test/spec/modules/coinzillaBidAdapter_spec.js index 08d6c8aede9..b02f3ccf255 100644 --- a/test/spec/modules/coinzillaBidAdapter_spec.js +++ b/test/spec/modules/coinzillaBidAdapter_spec.js @@ -1,6 +1,6 @@ -import {assert, expect} from 'chai'; -import {spec} from 'modules/coinzillaBidAdapter.js'; -import {newBidder} from 'src/adapters/bidderFactory.js'; +import { assert, expect } from 'chai'; +import { spec } from 'modules/coinzillaBidAdapter.js'; +import { newBidder } from 'src/adapters/bidderFactory.js'; const ENDPOINT_URL = 'https://request.czilladx.com/serve/request.php'; @@ -114,7 +114,7 @@ describe('coinzillaBidAdapter', function () { 'ttl': 3000, 'ad': '

I am an ad

', 'mediaType': 'banner', - 'meta': {'advertiserDomains': ['none.com']} + 'meta': { 'advertiserDomains': ['none.com'] } }]; const result = spec.interpretResponse(serverResponse, bidRequest[0]); expect(Object.keys(result)).to.deep.equal(Object.keys(expectedResponse)); diff --git a/test/spec/modules/colossussspBidAdapter_spec.js b/test/spec/modules/colossussspBidAdapter_spec.js index 9c92661fd54..cc926e61b9e 100644 --- a/test/spec/modules/colossussspBidAdapter_spec.js +++ b/test/spec/modules/colossussspBidAdapter_spec.js @@ -447,7 +447,7 @@ describe('ColossussspAdapter', function () { }) describe('getUserSyncs', function () { - const userSync = spec.getUserSyncs({}, {}, { consentString: 'xxx', gdprApplies: 1 }, { consentString: '1YN-' }); + const userSync = spec.getUserSyncs({}, {}, { consentString: 'xxx', gdprApplies: 1 }, '1YN-'); it('Returns valid URL and type', function () { expect(userSync).to.be.an('array').with.lengthOf(1); expect(userSync[0].type).to.exist; diff --git a/test/spec/modules/compassBidAdapter_spec.js b/test/spec/modules/compassBidAdapter_spec.js index 8d0e1cc5715..57412de4379 100644 --- a/test/spec/modules/compassBidAdapter_spec.js +++ b/test/spec/modules/compassBidAdapter_spec.js @@ -482,7 +482,7 @@ describe('CompassBidAdapter', function () { const syncData = config.runWithBidder(bidder, () => spec.getUserSyncs({}, {}, { consentString: 'ALL', gdprApplies: true, - }, {})); + }, undefined)); expect(syncData).to.be.an('array').which.is.not.empty; expect(syncData[0]).to.be.an('object') expect(syncData[0].type).to.be.a('string') @@ -491,9 +491,7 @@ describe('CompassBidAdapter', function () { expect(syncData[0].url).to.equal('https://sa-cs.deliverimp.com/image?pbjs=1&gdpr=1&gdpr_consent=ALL&coppa=0') }); it('Should return array of objects with proper sync config , include CCPA', function() { - const syncData = config.runWithBidder(bidder, () => spec.getUserSyncs({}, {}, {}, { - consentString: '1---' - })); + const syncData = config.runWithBidder(bidder, () => spec.getUserSyncs({}, {}, {}, '1---')); expect(syncData).to.be.an('array').which.is.not.empty; expect(syncData[0]).to.be.an('object') expect(syncData[0].type).to.be.a('string') @@ -502,7 +500,7 @@ describe('CompassBidAdapter', function () { expect(syncData[0].url).to.equal('https://sa-cs.deliverimp.com/image?pbjs=1&ccpa_consent=1---&coppa=0') }); it('Should return array of objects with proper sync config , include GPP', function() { - const syncData = config.runWithBidder(bidder, () => spec.getUserSyncs({}, {}, {}, {}, { + const syncData = config.runWithBidder(bidder, () => spec.getUserSyncs({}, {}, {}, undefined, { gppString: 'abc123', applicableSections: [8] })); diff --git a/test/spec/modules/conceptxBidAdapter_spec.js b/test/spec/modules/conceptxBidAdapter_spec.js index 8e9bd2f8cc0..02780045496 100644 --- a/test/spec/modules/conceptxBidAdapter_spec.js +++ b/test/spec/modules/conceptxBidAdapter_spec.js @@ -1,70 +1,75 @@ -// import or require modules necessary for the test, e.g.: -import { expect } from 'chai'; // may prefer 'assert' in place of 'expect' +import { expect } from 'chai'; import { spec } from 'modules/conceptxBidAdapter.js'; import { newBidder } from 'src/adapters/bidderFactory.js'; -// import { config } from 'src/config.js'; describe('conceptxBidAdapter', function () { - const URL = 'https://conceptx.cncpt-central.com/openrtb'; - - const ENDPOINT_URL = `${URL}`; - const ENDPOINT_URL_CONSENT = `${URL}?gdpr_applies=true&consentString=ihaveconsented`; + const ENDPOINT_URL = 'https://cxba-s2s.cncpt.dk/openrtb2/auction'; + const ENDPOINT_URL_CONSENT = + ENDPOINT_URL + '?gdpr_applies=1&gdpr_consent=ihaveconsented'; const adapter = newBidder(spec); const bidderRequests = [ { bidId: '123', bidder: 'conceptx', + adUnitCode: 'div-1', + auctionId: 'auc-1', params: { - site: 'example', - adunit: 'some-id-3' + site: 'example.com', + adunit: 'some-id-3', }, mediaTypes: { banner: { sizes: [[930, 180]], - } + }, }, - } - ] - - const singleBidRequest = { - bid: [ - { - bidId: '123', - } - ] - } + }, + ]; const serverResponse = { body: { - 'bidResponses': [ + id: 'resp-1', + cur: 'DKK', + seatbid: [ { - 'ads': [ + seat: 'conceptx', + bid: [ { - 'referrer': 'http://localhost/prebidpage_concept_bidder.html', - 'ttl': 360, - 'html': '

DUMMY

', - 'requestId': '214dfadd1f8826', - 'cpm': 46, - 'currency': 'DKK', - 'width': 930, - 'height': 180, - 'creativeId': 'FAKE-ID', - 'meta': { - 'mediaType': 'banner' - }, - 'netRevenue': true, - 'destinationUrls': { - 'destination': 'https://concept.dk' - } - } + id: 'bid-1', + impid: '123', + price: 46, + w: 930, + h: 180, + crid: 'FAKE-ID', + adm: '

DUMMY

', + }, ], - 'matchedAdCount': 1, - 'targetId': '214dfadd1f8826' - } - ] - } - } + }, + ], + }, + }; + + const requestPayload = { + data: JSON.stringify({ + id: 'auc-1', + site: { id: 'example.com', domain: 'example.com', page: 'example.com' }, + imp: [ + { + id: '123', + ext: { + prebid: { + storedrequest: { id: 'some-id-3' }, + }, + }, + }, + ], + ext: { + prebid: { + storedrequest: { id: 'cx_global' }, + }, + }, + }), + }; describe('inherited functions', function () { it('exists and is a function', function () { @@ -73,52 +78,105 @@ describe('conceptxBidAdapter', function () { }); describe('isBidRequestValid', function () { - it('should return true when required params found', function () { + it('should return true when bidId and params.adunit are present', function () { expect(spec.isBidRequestValid(bidderRequests[0])).to.equal(true); }); + + it('should return false when params.adunit is missing', function () { + expect( + spec.isBidRequestValid({ + bidId: '123', + bidder: 'conceptx', + params: { site: 'example' }, + }) + ).to.equal(false); + }); + + it('should return false when bidId is missing', function () { + expect( + spec.isBidRequestValid({ + bidder: 'conceptx', + params: { site: 'example', adunit: 'id-1' }, + }) + ).to.equal(false); + }); }); describe('buildRequests', function () { - it('Test requests', function () { - const request = spec.buildRequests(bidderRequests, {}); - expect(request.length).to.equal(1); - expect(request[0]).to.have.property('data'); - const bid = JSON.parse(request[0].data).adUnits[0] - expect(bid.site).to.equal('example'); - expect(bid.adunit).to.equal('some-id-3'); - expect(JSON.stringify(bid.dimensions)).to.equal(JSON.stringify([ - [930, 180]])); + it('should build OpenRTB request with stored requests', function () { + const requests = spec.buildRequests(bidderRequests, {}); + expect(requests).to.have.lengthOf(1); + expect(requests[0]).to.have.property('method', 'POST'); + expect(requests[0]).to.have.property('url', ENDPOINT_URL); + expect(requests[0]).to.have.property('data'); + + const payload = JSON.parse(requests[0].data); + expect(payload).to.have.property('site'); + expect(payload.site).to.have.property('id', 'example.com'); + expect(payload).to.have.property('imp'); + expect(payload.imp).to.have.lengthOf(1); + expect(payload.imp[0].ext.prebid.storedrequest).to.deep.equal({ + id: 'some-id-3', + }); + expect(payload.ext.prebid.storedrequest).to.deep.equal({ + id: 'cx_global', + }); + expect(payload.imp[0].banner.format).to.deep.equal([{ w: 930, h: 180 }]); + }); + + it('should include withCredentials in options', function () { + const requests = spec.buildRequests(bidderRequests, {}); + expect(requests[0].options).to.deep.include({ withCredentials: true }); }); }); describe('user privacy', function () { - it('should NOT send GDPR Consent data if gdprApplies equals undefined', function () { - const request = spec.buildRequests(bidderRequests, { gdprConsent: { gdprApplies: undefined, consentString: 'iDoNotConsent' } }); - expect(request.length).to.equal(1); - expect(request[0]).to.have.property('url') - expect(request[0].url).to.equal(ENDPOINT_URL); + it('should NOT add GDPR params to URL when gdprApplies is undefined', function () { + const requests = spec.buildRequests(bidderRequests, { + gdprConsent: { gdprApplies: undefined, consentString: 'iDoNotConsent' }, + }); + expect(requests[0].url).to.equal(ENDPOINT_URL); }); - it('should send GDPR Consent data if gdprApplies', function () { - const request = spec.buildRequests(bidderRequests, { gdprConsent: { gdprApplies: true, consentString: 'ihaveconsented' } }); - expect(request.length).to.equal(1); - expect(request[0]).to.have.property('url') - expect(request[0].url).to.equal(ENDPOINT_URL_CONSENT); + + it('should add gdpr_applies and gdpr_consent to URL when GDPR applies', function () { + const requests = spec.buildRequests(bidderRequests, { + gdprConsent: { gdprApplies: true, consentString: 'ihaveconsented' }, + }); + expect(requests[0].url).to.include('gdpr_applies=1'); + expect(requests[0].url).to.include('gdpr_consent=ihaveconsented'); }); }); describe('interpretResponse', function () { - it('should return valid response when passed valid server response', function () { - const interpretedResponse = spec.interpretResponse(serverResponse, singleBidRequest); - const ad = serverResponse.body.bidResponses[0].ads[0] - expect(interpretedResponse).to.have.lengthOf(1); - expect(interpretedResponse[0].cpm).to.equal(ad.cpm); - expect(interpretedResponse[0].width).to.equal(Number(ad.width)); - expect(interpretedResponse[0].height).to.equal(Number(ad.height)); - expect(interpretedResponse[0].creativeId).to.equal(ad.creativeId); - expect(interpretedResponse[0].currency).to.equal(ad.currency); - expect(interpretedResponse[0].netRevenue).to.equal(true); - expect(interpretedResponse[0].ad).to.equal(ad.html); - expect(interpretedResponse[0].ttl).to.equal(360); + it('should return valid bids from PBS seatbid format', function () { + const interpreted = spec.interpretResponse(serverResponse, requestPayload); + expect(interpreted).to.have.lengthOf(1); + expect(interpreted[0].requestId).to.equal('123'); + expect(interpreted[0].cpm).to.equal(46); + expect(interpreted[0].width).to.equal(930); + expect(interpreted[0].height).to.equal(180); + expect(interpreted[0].creativeId).to.equal('FAKE-ID'); + expect(interpreted[0].currency).to.equal('DKK'); + expect(interpreted[0].netRevenue).to.equal(true); + expect(interpreted[0].ad).to.equal('

DUMMY

'); + expect(interpreted[0].ttl).to.equal(300); + }); + + it('should return empty array when no seatbid', function () { + const emptyResponse = { body: { seatbid: [] } }; + expect(spec.interpretResponse(emptyResponse, {})).to.deep.equal([]); + }); + + it('should return empty array when seatbid is missing', function () { + const noSeatbid = { body: {} }; + expect(spec.interpretResponse(noSeatbid, {})).to.deep.equal([]); + }); + }); + + describe('getUserSyncs', function () { + it('should return empty array (sync handled by runPbsCookieSync)', function () { + const syncs = spec.getUserSyncs({ iframeEnabled: true, pixelEnabled: true }, [], {}, '', {}); + expect(syncs).to.deep.equal([]); }); }); }); diff --git a/test/spec/modules/concertAnalyticsAdapter_spec.js b/test/spec/modules/concertAnalyticsAdapter_spec.js index 639011ac481..054af3e18e6 100644 --- a/test/spec/modules/concertAnalyticsAdapter_spec.js +++ b/test/spec/modules/concertAnalyticsAdapter_spec.js @@ -1,6 +1,6 @@ import concertAnalytics from 'modules/concertAnalyticsAdapter.js'; import { expect } from 'chai'; -import {expectEvents} from '../../helpers/analytics.js'; +import { expectEvents } from '../../helpers/analytics.js'; import { EVENTS } from 'src/constants.js'; import { server } from 'test/mocks/xhr.js'; diff --git a/test/spec/modules/concertBidAdapter_spec.js b/test/spec/modules/concertBidAdapter_spec.js index 6c842e58d37..54150aab322 100644 --- a/test/spec/modules/concertBidAdapter_spec.js +++ b/test/spec/modules/concertBidAdapter_spec.js @@ -2,7 +2,8 @@ import { expect } from 'chai'; import sinon from 'sinon'; import { spec, storage } from 'modules/concertBidAdapter.js'; import { hook } from 'src/hook.js'; -import {getGlobal} from '../../../src/prebidGlobal.js'; +import { getGlobal } from '../../../src/prebidGlobal.js'; +import * as adUnits from 'src/utils/adUnits'; describe('ConcertAdapter', function () { let bidRequests; @@ -86,7 +87,7 @@ describe('ConcertAdapter', function () { } sandbox = sinon.createSandbox(); - sandbox.stub(document, 'getElementById').withArgs('desktop_leaderboard_variable').returns(element) + sandbox.stub(adUnits, 'getAdUnitElement').returns(element) }); afterEach(function () { @@ -301,7 +302,7 @@ describe('ConcertAdapter', function () { }); it('should return empty bids if there are no bids from the server', function() { - const bids = spec.interpretResponse({ body: {bids: []} }, bidRequest); + const bids = spec.interpretResponse({ body: { bids: [] } }, bidRequest); expect(bids).to.have.lengthOf(0); }); }); diff --git a/test/spec/modules/connatixBidAdapter_spec.js b/test/spec/modules/connatixBidAdapter_spec.js index ce7af687b8d..82479617769 100644 --- a/test/spec/modules/connatixBidAdapter_spec.js +++ b/test/spec/modules/connatixBidAdapter_spec.js @@ -1,23 +1,23 @@ import { expect } from 'chai'; import sinon from 'sinon'; +import { config } from 'src/config.js'; import { _getBidRequests, - _canSelectViewabilityContainer as connatixCanSelectViewabilityContainer, detectViewability as connatixDetectViewability, getBidFloor as connatixGetBidFloor, _getMinSize as connatixGetMinSize, - _getViewability as connatixGetViewability, hasQueryParams as connatixHasQueryParams, - _isViewabilityMeasurable as connatixIsViewabilityMeasurable, readFromLocalStorage as connatixReadFromLocalStorage, saveInLocalStorage as connatixSaveInLocalStorage, spec, storage } from '../../../modules/connatixBidAdapter.js'; import adapterManager from '../../../src/adapterManager.js'; +import * as utils from '../../../src/utils.js'; import * as ajax from '../../../src/ajax.js'; import { ADPOD, BANNER, VIDEO } from '../../../src/mediaTypes.js'; import * as winDimensions from '../../../src/utils/winDimensions.js'; +import * as adUnits from 'src/utils/adUnits'; const BIDDER_CODE = 'connatix'; @@ -84,67 +84,18 @@ describe('connatixBidAdapter', function () { }); }); - describe('_isIframe', () => { - let querySelectorStub; - - beforeEach(() => { - querySelectorStub = sinon.stub(window.top.document, 'querySelector'); - }); - - afterEach(() => { - querySelectorStub.restore(); - }); - - it('should return true when window.top.document.querySelector does not throw an error', () => { - querySelectorStub.returns({}); - expect(connatixCanSelectViewabilityContainer()).to.be.true; - }); - - it('should return false when window.top.document.querySelector throws an error', () => { - querySelectorStub.throws(new Error('test error')); - expect(connatixCanSelectViewabilityContainer()).to.be.false; - }); - }); - - describe('_isViewabilityMeasurable', () => { - let querySelectorStub; - - beforeEach(() => { - querySelectorStub = sinon.stub(window.top.document, 'querySelector'); - }); - - afterEach(() => { - querySelectorStub.restore(); - }); - - it('should return false if the element is null or undefined', () => { - expect(connatixIsViewabilityMeasurable(null)).to.be.false; - expect(connatixIsViewabilityMeasurable(undefined)).to.be.false; - }); - - it('should return false if _isIframe returns true', () => { - querySelectorStub.throws(new Error('test error')); - - const element = document.createElement('div'); - expect(connatixIsViewabilityMeasurable(element)).to.be.false; - }); - - it('should return true if _isIframe returns false', () => { - querySelectorStub.returns(document.createElement('div')) - - const element = document.createElement('div'); - expect(connatixIsViewabilityMeasurable(element)).to.be.true; - }); - }); - - describe('_getViewability', () => { + describe('detectViewability', () => { let element; let getBoundingClientRectStub; let topWinMock; + let querySelectorStub; + let getElementStub; + let sandbox; beforeEach(() => { + sandbox = sinon.createSandbox(); element = document.createElement('div'); - getBoundingClientRectStub = sinon.stub(element, 'getBoundingClientRect'); + getBoundingClientRectStub = sandbox.stub(element, 'getBoundingClientRect'); topWinMock = { document: { @@ -153,96 +104,60 @@ describe('connatixBidAdapter', function () { innerWidth: 800, innerHeight: 600 }; - }); - - afterEach(() => { - getBoundingClientRectStub.restore(); - }); - - it('should return 0 if the document is not visible', () => { - topWinMock.document.visibilityState = 'hidden'; - - const viewability = connatixGetViewability(element, topWinMock); - - expect(viewability).to.equal(0); - }); - it('should return 100% if the element is fully in view', () => { - const boundingBox = { left: 100, top: 100, right: 300, bottom: 300, width: 200, height: 200 }; - getBoundingClientRectStub.returns(boundingBox); - - const viewability = connatixGetViewability(element, topWinMock); - - expect(viewability).to.equal(100); - }); - - it('should return the correct percentage if the element is partially in view', () => { - const boundingBox = { left: 700, top: 500, right: 900, bottom: 700, width: 200, height: 200 }; - getBoundingClientRectStub.returns(boundingBox); - const getWinDimensionsStub = sinon.stub(winDimensions, 'getWinDimensions'); - getWinDimensionsStub.returns({ innerWidth: topWinMock.innerWidth, innerHeight: topWinMock.innerHeight}); - - const viewability = connatixGetViewability(element, topWinMock); - - expect(viewability).to.equal(25); // 100x100 / 200x200 = 0.25 -> 25% - getWinDimensionsStub.restore(); + querySelectorStub = sandbox.stub(window.top.document, 'querySelector'); + getElementStub = sandbox.stub(adUnits, 'getAdUnitElement'); + sandbox.stub(winDimensions, 'getWinDimensions').callsFake(() => ( + { + document: { + documentElement: { + clientWidth: topWinMock.innerWidth, + clientHeight: topWinMock.innerHeight + } + } + } + )); }); - it('should return 0% if the element is not in view', () => { - const getWinDimensionsStub = sinon.stub(winDimensions, 'getWinDimensions'); - getWinDimensionsStub.returns({ innerWidth: topWinMock.innerWidth, innerHeight: topWinMock.innerHeight}); - const boundingBox = { left: 900, top: 700, right: 1100, bottom: 900, width: 200, height: 200 }; - getBoundingClientRectStub.returns(boundingBox); - - const viewability = connatixGetViewability(element, topWinMock); - - expect(viewability).to.equal(0); - getWinDimensionsStub.restore(); + afterEach(() => { + sandbox.restore(); }); - it('should use provided width and height if element dimensions are zero', () => { - const boundingBox = { left: 100, top: 100, right: 100, bottom: 100, width: 0, height: 0 }; - getBoundingClientRectStub.returns(boundingBox); - - const dimensions = { w: 200, h: 200 }; - const viewability = connatixGetViewability(element, topWinMock, dimensions); + it('should return 100% viewability when the element is fully within view and has a valid viewabilityContainerIdentifier', () => { + sandbox.stub(utils, 'inIframe').returns(false); + sandbox.stub(utils, 'getWindowTop').returns(topWinMock); - expect(viewability).to.equal(100); // Element fully in view with provided dimensions - }); - }); + const bid = { + params: { viewabilityContainerIdentifier: '#validElement' }, + adUnitCode: 'adUnitCode123', + mediaTypes: { banner: { sizes: [[300, 250]] } }, + sizes: [[300, 250]] + }; - describe('detectViewability', () => { - let element; - let getBoundingClientRectStub; - let topWinMock; - let querySelectorStub; - let getElementByIdStub; + getBoundingClientRectStub.returns({ + left: 100, + top: 100, + right: 400, + bottom: 350, + width: 300, + height: 250 + }); - beforeEach(() => { - element = document.createElement('div'); - getBoundingClientRectStub = sinon.stub(element, 'getBoundingClientRect'); + querySelectorStub.withArgs('#validElement').returns(element); + getElementStub.returns(null); - topWinMock = { - document: { - visibilityState: 'visible' - }, - innerWidth: 800, - innerHeight: 600 - }; + const result = connatixDetectViewability(bid); - querySelectorStub = sinon.stub(window.top.document, 'querySelector'); - getElementByIdStub = sinon.stub(document, 'getElementById'); + // Expected calculation: the element is fully in view, so 100% viewability + expect(result).to.equal(100); }); - afterEach(() => { - getBoundingClientRectStub.restore(); - querySelectorStub.restore(); - getElementByIdStub.restore(); - }); + it('should use getElementById and return 100% viewability when viewabilityContainerIdentifier is an ID without # prefix', () => { + sandbox.stub(utils, 'inIframe').returns(false); + sandbox.stub(utils, 'getWindowTop').returns(topWinMock); - it('should return 100% viewability when the element is fully within view and has a valid viewabilityContainerIdentifier', () => { const bid = { - params: { viewabilityContainerIdentifier: '#validElement' }, + params: { viewabilityContainerIdentifier: 'validElement' }, adUnitCode: 'adUnitCode123', mediaTypes: { banner: { sizes: [[300, 250]] } }, sizes: [[300, 250]] @@ -257,16 +172,20 @@ describe('connatixBidAdapter', function () { height: 250 }); - querySelectorStub.withArgs('#validElement').returns(element); - getElementByIdStub.returns(null); + const getElementByIdStub = sandbox.stub(document, 'getElementById'); + getElementByIdStub.withArgs('validElement').returns(element); const result = connatixDetectViewability(bid); - // Expected calculation: the element is fully in view, so 100% viewability expect(result).to.equal(100); + expect(getElementByIdStub.calledWith('validElement')).to.be.true; + expect(querySelectorStub.calledWith('validElement')).to.be.false; }); it('should fall back to using bid sizes and adUnitCode when the viewabilityContainerIdentifier is invalid or was not provided', () => { + sandbox.stub(utils, 'inIframe').returns(false); + sandbox.stub(utils, 'getWindowTop').returns(topWinMock); + const bid = { params: { viewabilityContainerIdentifier: '#invalidElement' }, adUnitCode: 'adUnitCode123', @@ -284,7 +203,7 @@ describe('connatixBidAdapter', function () { }); querySelectorStub.withArgs('#invalidElement').returns(null); - getElementByIdStub.withArgs('adUnitCode123').returns(element); + getElementStub.returns(element); const result = connatixDetectViewability(bid); @@ -292,6 +211,9 @@ describe('connatixBidAdapter', function () { }); it('should use the adUnitCode as a fallback when querying an element fails due to a browser error, and return 100% viewability because adUnitCode container is fully in view', () => { + sandbox.stub(utils, 'inIframe').returns(false); + sandbox.stub(utils, 'getWindowTop').returns(topWinMock); + const bid = { params: { viewabilityContainerIdentifier: '#invalidElement' }, adUnitCode: 'adUnitCode123', @@ -311,7 +233,7 @@ describe('connatixBidAdapter', function () { }); // The fallback should use the adUnitCode to find the element - getElementByIdStub.withArgs('adUnitCode123').returns(element); + getElementStub.returns(element); const result = connatixDetectViewability(bid); @@ -649,13 +571,13 @@ describe('connatixBidAdapter', function () { describe('interpretResponse', function () { const CustomerId = '99f20d18-c4b4-4a28-3d8e-d43e2c8cb4ac'; const PlayerId = 'e4984e88-9ff4-45a3-8b9d-33aabcad634f'; - const Bid = {Cpm: 0.1, RequestId: '2f897340c4eaa3', Ttl: 86400, CustomerId, PlayerId, Lurl: 'test-lurl'}; + const Bid = { Cpm: 0.1, RequestId: '2f897340c4eaa3', Ttl: 86400, CustomerId, PlayerId, Lurl: 'test-lurl' }; let serverResponse; this.beforeEach(function () { serverResponse = { body: { - Bids: [ Bid ] + Bids: [Bid] }, headers: function() { } }; @@ -682,7 +604,7 @@ describe('connatixBidAdapter', function () { it('Should contains the same values as in the serverResponse', function() { const bidResponses = spec.interpretResponse(serverResponse); - const [ bidResponse ] = bidResponses; + const [bidResponse] = bidResponses; expect(bidResponse.requestId).to.equal(serverResponse.body.Bids[0].RequestId); expect(bidResponse.cpm).to.equal(serverResponse.body.Bids[0].Cpm); expect(bidResponse.ttl).to.equal(serverResponse.body.Bids[0].Ttl); @@ -693,7 +615,7 @@ describe('connatixBidAdapter', function () { }); it('Should return n bid responses for n bids', function() { - serverResponse.body.Bids = [ { ...Bid }, { ...Bid } ]; + serverResponse.body.Bids = [{ ...Bid }, { ...Bid }]; const firstBidCpm = 4; serverResponse.body.Bids[0].Cpm = firstBidCpm; @@ -710,10 +632,10 @@ describe('connatixBidAdapter', function () { it('Should contain specific values for banner bids', function () { const adHtml = 'ad html' - serverResponse.body.Bids = [ { ...Bid, Ad: adHtml } ]; + serverResponse.body.Bids = [{ ...Bid, Ad: adHtml }]; const bidResponses = spec.interpretResponse(serverResponse); - const [ bidResponse ] = bidResponses; + const [bidResponse] = bidResponses; expect(bidResponse.vastXml).to.be.undefined; expect(bidResponse.ad).to.equal(adHtml); @@ -722,10 +644,10 @@ describe('connatixBidAdapter', function () { it('Should contain specific values for video bids', function () { const adVastXml = 'ad vast xml' - serverResponse.body.Bids = [ { ...Bid, VastXml: adVastXml } ]; + serverResponse.body.Bids = [{ ...Bid, VastXml: adVastXml }]; const bidResponses = spec.interpretResponse(serverResponse); - const [ bidResponse ] = bidResponses; + const [bidResponse] = bidResponses; expect(bidResponse.ad).to.be.undefined; expect(bidResponse.vastXml).to.equal(adVastXml); @@ -738,48 +660,52 @@ describe('connatixBidAdapter', function () { const PlayerId = 'e4984e88-9ff4-45a3-8b9d-33aabcad634f'; const UserSyncEndpoint = 'https://connatix.com/sync' const UserSyncEndpointWithParams = 'https://connatix.com/sync?param1=value1' - const Bid = {Cpm: 0.1, RequestId: '2f897340c4eaa3', Ttl: 86400, CustomerId, PlayerId}; + const Bid = { Cpm: 0.1, RequestId: '2f897340c4eaa3', Ttl: 86400, CustomerId, PlayerId }; const serverResponse = { body: { UserSyncEndpoint, - Bids: [ Bid ] + Bids: [Bid] }, headers: function() { } }; const serverResponse2 = { body: { UserSyncEndpoint: UserSyncEndpointWithParams, - Bids: [ Bid ] + Bids: [Bid] }, headers: function() { } }; + afterEach(() => { + config.resetConfig(); + }); + it('Should return an empty array when iframeEnabled: false', function () { - expect(spec.getUserSyncs({iframeEnabled: false, pixelEnabled: true}, [], {}, {}, {})).to.be.an('array').that.is.empty; + expect(spec.getUserSyncs({ iframeEnabled: false, pixelEnabled: true }, [], {}, {}, {})).to.be.an('array').that.is.empty; }); it('Should return an empty array when serverResponses is emprt array', function () { - expect(spec.getUserSyncs({iframeEnabled: true, pixelEnabled: true}, [], {}, {}, {})).to.be.an('array').that.is.empty; + expect(spec.getUserSyncs({ iframeEnabled: true, pixelEnabled: true }, [], {}, {}, {})).to.be.an('array').that.is.empty; }); it('Should return an empty array when iframeEnabled: true but serverResponses in an empty array', function () { - expect(spec.getUserSyncs({iframeEnabled: false, pixelEnabled: true}, [serverResponse], {}, {}, {})).to.be.an('array').that.is.empty; + expect(spec.getUserSyncs({ iframeEnabled: false, pixelEnabled: true }, [serverResponse], {}, {}, {})).to.be.an('array').that.is.empty; }); it('Should return an empty array when iframeEnabled: true but serverResponses in an not defined or null', function () { - expect(spec.getUserSyncs({iframeEnabled: false, pixelEnabled: true}, undefined, {}, {}, {})).to.be.an('array').that.is.empty; - expect(spec.getUserSyncs({iframeEnabled: false, pixelEnabled: true}, null, {}, {}, {})).to.be.an('array').that.is.empty; + expect(spec.getUserSyncs({ iframeEnabled: false, pixelEnabled: true }, undefined, {}, {}, {})).to.be.an('array').that.is.empty; + expect(spec.getUserSyncs({ iframeEnabled: false, pixelEnabled: true }, null, {}, {}, {})).to.be.an('array').that.is.empty; }); it('Should return one user sync object when iframeEnabled is true and serverResponses is not an empry array', function () { - expect(spec.getUserSyncs({iframeEnabled: true, pixelEnabled: true}, [serverResponse], {}, {}, {})).to.be.an('array').that.is.not.empty; + expect(spec.getUserSyncs({ iframeEnabled: true, pixelEnabled: true }, [serverResponse], {}, {}, {})).to.be.an('array').that.is.not.empty; }); it('Should return a list containing a single object having type: iframe and url: syncUrl', function () { - const userSyncList = spec.getUserSyncs({iframeEnabled: true, pixelEnabled: true}, [serverResponse], undefined, undefined, undefined); + const userSyncList = spec.getUserSyncs({ iframeEnabled: true, pixelEnabled: true }, [serverResponse], undefined, undefined, undefined); const { type, url } = userSyncList[0]; expect(type).to.equal('iframe'); expect(url).to.equal(UserSyncEndpoint); }); it('Should append gdpr: 0 if gdprConsent object is provided but gdprApplies field is not provided', function () { const userSyncList = spec.getUserSyncs( - {iframeEnabled: true, pixelEnabled: true}, + { iframeEnabled: true, pixelEnabled: true }, [serverResponse], {}, undefined, @@ -790,9 +716,9 @@ describe('connatixBidAdapter', function () { }); it('Should append gdpr having the value of gdprApplied if gdprConsent object is present and have gdprApplies field', function () { const userSyncList = spec.getUserSyncs( - {iframeEnabled: true, pixelEnabled: true}, + { iframeEnabled: true, pixelEnabled: true }, [serverResponse], - {gdprApplies: true}, + { gdprApplies: true }, undefined, undefined ); @@ -801,9 +727,9 @@ describe('connatixBidAdapter', function () { }); it('Should append gdpr_consent if gdprConsent object is present and have gdprApplies field', function () { const userSyncList = spec.getUserSyncs( - {iframeEnabled: true, pixelEnabled: true}, + { iframeEnabled: true, pixelEnabled: true }, [serverResponse], - {gdprApplies: true, consentString: 'alabala'}, + { gdprApplies: true, consentString: 'alabala' }, undefined, undefined ); @@ -812,9 +738,9 @@ describe('connatixBidAdapter', function () { }); it('Should encodeURI gdpr_consent corectly', function () { const userSyncList = spec.getUserSyncs( - {iframeEnabled: true, pixelEnabled: true}, + { iframeEnabled: true, pixelEnabled: true }, [serverResponse], - {gdprApplies: true, consentString: 'test&2'}, + { gdprApplies: true, consentString: 'test&2' }, undefined, undefined ); @@ -823,36 +749,50 @@ describe('connatixBidAdapter', function () { }); it('Should append usp_consent to the url if uspConsent is provided', function () { const userSyncList = spec.getUserSyncs( - {iframeEnabled: true, pixelEnabled: true}, + { iframeEnabled: true, pixelEnabled: true }, [serverResponse], - {gdprApplies: true, consentString: 'test&2'}, + { gdprApplies: true, consentString: 'test&2' }, '1YYYN', undefined ); const { url } = userSyncList[0]; expect(url).to.equal(`${UserSyncEndpoint}?gdpr=1&gdpr_consent=test%262&us_privacy=1YYYN`); }); - it('Should not modify the sync url if gppConsent param is provided', function () { + it('Should append gpp and gpp_sid to the url if gppConsent param is provided', function () { const userSyncList = spec.getUserSyncs( - {iframeEnabled: true, pixelEnabled: true}, + { iframeEnabled: true, pixelEnabled: true }, [serverResponse], - {gdprApplies: true, consentString: 'test&2'}, + { gdprApplies: true, consentString: 'test&2' }, '1YYYN', - {consent: '1'} + { gppString: 'GPP', applicableSections: [2, 4] } ); const { url } = userSyncList[0]; - expect(url).to.equal(`${UserSyncEndpoint}?gdpr=1&gdpr_consent=test%262&us_privacy=1YYYN`); + expect(url).to.equal(`${UserSyncEndpoint}?gdpr=1&gdpr_consent=test%262&us_privacy=1YYYN&gpp=GPP&gpp_sid=2,4`); }); it('Should correctly append all consents to the sync url if the url contains query params', function () { const userSyncList = spec.getUserSyncs( - {iframeEnabled: true, pixelEnabled: true}, + { iframeEnabled: true, pixelEnabled: true }, [serverResponse2], - {gdprApplies: true, consentString: 'test&2'}, + { gdprApplies: true, consentString: 'test&2' }, + '1YYYN', + { gppString: 'GPP', applicableSections: [2, 4] } + ); + const { url } = userSyncList[0]; + expect(url).to.equal(`${UserSyncEndpointWithParams}&gdpr=1&gdpr_consent=test%262&us_privacy=1YYYN&gpp=GPP&gpp_sid=2,4`); + }); + it('Should append coppa to the url if coppa is true', function () { + config.setConfig({ + coppa: true + }); + const userSyncList = spec.getUserSyncs( + { iframeEnabled: true, pixelEnabled: true }, + [serverResponse], + { gdprApplies: true, consentString: 'test&2' }, '1YYYN', - {consent: '1'} + { gppString: 'GPP', applicableSections: [2, 4] } ); const { url } = userSyncList[0]; - expect(url).to.equal(`${UserSyncEndpointWithParams}&gdpr=1&gdpr_consent=test%262&us_privacy=1YYYN`); + expect(url).to.equal(`${UserSyncEndpoint}?gdpr=1&gdpr_consent=test%262&us_privacy=1YYYN&gpp=GPP&gpp_sid=2,4&coppa=1`); }); }); diff --git a/test/spec/modules/connectIdSystem_spec.js b/test/spec/modules/connectIdSystem_spec.js index 48ef3a30fe3..e30547a4a12 100644 --- a/test/spec/modules/connectIdSystem_spec.js +++ b/test/spec/modules/connectIdSystem_spec.js @@ -1,7 +1,7 @@ -import {expect} from 'chai'; -import {connectIdSubmodule, storage} from 'modules/connectIdSystem.js'; -import {server} from '../../mocks/xhr.js'; -import {parseQS, parseUrl} from 'src/utils.js'; +import { expect } from 'chai'; +import { connectIdSubmodule, storage } from 'modules/connectIdSystem.js'; +import { server } from '../../mocks/xhr.js'; +import { parseQS, parseUrl } from 'src/utils.js'; import * as refererDetection from '../../../src/refererDetection.js'; const TEST_SERVER_URL = 'http://localhost:9876/'; @@ -96,20 +96,20 @@ describe('Yahoo ConnectID Submodule', () => { { detail: 'cookie data over local storage data', cookie: '{"connectId":"foo"}', - localStorage: JSON.stringify({connectId: 'bar'}), - expected: {connectId: 'foo'} + localStorage: JSON.stringify({ connectId: 'bar' }), + expected: { connectId: 'foo' } }, { detail: 'cookie data if only cookie data exists', cookie: '{"connectId":"foo"}', localStorage: undefined, - expected: {connectId: 'foo'} + expected: { connectId: 'foo' } }, { detail: 'local storage data if only it local storage data exists', cookie: undefined, - localStorage: JSON.stringify({connectId: 'bar'}), - expected: {connectId: 'bar'} + localStorage: JSON.stringify({ connectId: 'bar' }), + expected: { connectId: 'bar' } }, { detail: 'undefined when both cookie and local storage are empty', @@ -150,7 +150,7 @@ describe('Yahoo ConnectID Submodule', () => { describe('with valid module configuration', () => { describe('when data is in client storage', () => { it('returns an object with the stored ID from cookies for valid module configuration and sync is done', () => { - const cookieData = {connectId: 'foobar'}; + const cookieData = { connectId: 'foobar' }; getCookieStub.withArgs(STORAGE_KEY).returns(JSON.stringify(cookieData)); const result = invokeGetIdAPI({ he: HASHED_EMAIL, @@ -164,7 +164,7 @@ describe('Yahoo ConnectID Submodule', () => { it('returns an object with the stored ID from cookies for valid module configuration with no user sync', () => { const last13Days = Date.now() - (60 * 60 * 24 * 1000 * 13); - const cookieData = {connectId: 'foobar', he: HASHED_EMAIL, lastSynced: last13Days}; + const cookieData = { connectId: 'foobar', he: HASHED_EMAIL, lastSynced: last13Days }; getCookieStub.withArgs(STORAGE_KEY).returns(JSON.stringify(cookieData)); const result = invokeGetIdAPI({ he: HASHED_EMAIL, @@ -179,11 +179,11 @@ describe('Yahoo ConnectID Submodule', () => { it('returns an object with the stored ID and refreshes the storages with the new lastUsed', () => { const last13Days = Date.now() - (60 * 60 * 24 * 1000 * 13); - const cookieData = {connectId: 'foobar', he: HASHED_EMAIL, lastSynced: last13Days, lastUsed: 1}; + const cookieData = { connectId: 'foobar', he: HASHED_EMAIL, lastSynced: last13Days, lastUsed: 1 }; getCookieStub.withArgs(STORAGE_KEY).returns(JSON.stringify(cookieData)); const dateNowStub = sinon.stub(Date, 'now'); dateNowStub.returns(20); - const newCookieData = Object.assign({}, cookieData, {lastUsed: 20}) + const newCookieData = Object.assign({}, cookieData, { lastUsed: 20 }) const result = invokeGetIdAPI({ he: HASHED_EMAIL, pixelId: PIXEL_ID @@ -203,7 +203,7 @@ describe('Yahoo ConnectID Submodule', () => { it('returns an object with the stored ID from cookies and no sync when puid stays the same', () => { const last13Days = Date.now() - (60 * 60 * 24 * 1000 * 13); - const cookieData = {connectId: 'foobar', puid: '123', lastSynced: last13Days}; + const cookieData = { connectId: 'foobar', puid: '123', lastSynced: last13Days }; getCookieStub.withArgs(STORAGE_KEY).returns(JSON.stringify(cookieData)); const dateNowStub = sinon.stub(Date, 'now'); dateNowStub.returns(20); @@ -221,7 +221,7 @@ describe('Yahoo ConnectID Submodule', () => { it('returns an object with the stored ID from cookies and syncs because of expired auto generated puid', () => { const last13Days = Date.now() - (60 * 60 * 24 * 1000 * 13); const last31Days = Date.now() - (60 * 60 * 24 * 1000 * 31); - const cookieData = {connectId: 'foo', he: 'email', lastSynced: last13Days, puid: '9', lastUsed: last31Days}; + const cookieData = { connectId: 'foo', he: 'email', lastSynced: last13Days, puid: '9', lastUsed: last31Days }; getCookieStub.withArgs(STORAGE_KEY).returns(JSON.stringify(cookieData)); const result = invokeGetIdAPI({ he: HASHED_EMAIL, @@ -259,7 +259,7 @@ describe('Yahoo ConnectID Submodule', () => { }); it('returns an object with the stored ID from localStorage for valid module configuration and sync is done', () => { - const localStorageData = {connectId: 'foobarbaz'}; + const localStorageData = { connectId: 'foobarbaz' }; getLocalStorageStub.withArgs(STORAGE_KEY).returns(localStorageData); const result = invokeGetIdAPI({ he: HASHED_EMAIL, @@ -272,7 +272,7 @@ describe('Yahoo ConnectID Submodule', () => { }); it('returns an object with the stored ID from localStorage and refreshes the cookie storage', () => { - const localStorageData = {connectId: 'foobarbaz'}; + const localStorageData = { connectId: 'foobarbaz' }; getLocalStorageStub.withArgs(STORAGE_KEY).returns(localStorageData); const dateNowStub = sinon.stub(Date, 'now'); dateNowStub.returns(1); @@ -294,7 +294,7 @@ describe('Yahoo ConnectID Submodule', () => { const last2Days = Date.now() - (60 * 60 * 24 * 1000 * 2); const last21Days = Date.now() - (60 * 60 * 24 * 1000 * 21); const ttl = 10000; - const cookieData = {connectId: 'foo', he: 'email', lastSynced: last2Days, puid: '9', lastUsed: last21Days, ttl}; + const cookieData = { connectId: 'foo', he: 'email', lastSynced: last2Days, puid: '9', lastUsed: last21Days, ttl }; getCookieStub.withArgs(STORAGE_KEY).returns(JSON.stringify(cookieData)); const result = invokeGetIdAPI({ @@ -311,7 +311,7 @@ describe('Yahoo ConnectID Submodule', () => { const last2Days = Date.now() - (60 * 60 * 24 * 1000 * 2); const last21Days = Date.now() - (60 * 60 * 24 * 1000 * 21); const ttl = 60 * 60 * 24 * 1000 * 3; - const cookieData = {connectId: 'foo', he: HASHED_EMAIL, lastSynced: last2Days, puid: '9', lastUsed: last21Days, ttl}; + const cookieData = { connectId: 'foo', he: HASHED_EMAIL, lastSynced: last2Days, puid: '9', lastUsed: last21Days, ttl }; getCookieStub.withArgs(STORAGE_KEY).returns(JSON.stringify(cookieData)); const result = invokeGetIdAPI({ @@ -328,7 +328,7 @@ describe('Yahoo ConnectID Submodule', () => { const last2Days = Date.now() - (60 * 60 * 24 * 1000 * 2); const last21Days = Date.now() - (60 * 60 * 24 * 1000 * 21); const ttl = 60 * 60 * 24 * 1000 * 3; - const cookieData = {connectId: 'foo', he: HASHED_EMAIL, lastSynced: last2Days, puid: '9', lastUsed: last21Days, ttl}; + const cookieData = { connectId: 'foo', he: HASHED_EMAIL, lastSynced: last2Days, puid: '9', lastUsed: last21Days, ttl }; getCookieStub.withArgs(STORAGE_KEY).returns(JSON.stringify(cookieData)); const result = invokeGetIdAPI({ @@ -346,7 +346,7 @@ describe('Yahoo ConnectID Submodule', () => { const last2Days = Date.now() - (60 * 60 * 24 * 1000 * 2); const last21Days = Date.now() - (60 * 60 * 24 * 1000 * 21); const ttl = 60 * 60 * 24 * 1000 * 3; - const cookieData = {connectId: 'foo', he: HASHED_EMAIL, lastSynced: last2Days, puid: '9', lastUsed: last21Days, ttl}; + const cookieData = { connectId: 'foo', he: HASHED_EMAIL, lastSynced: last2Days, puid: '9', lastUsed: last21Days, ttl }; getCookieStub.withArgs(STORAGE_KEY).returns(JSON.stringify(cookieData)); const getRefererInfoStub = sinon.stub(refererDetection, 'getRefererInfo'); getRefererInfoStub.returns({ @@ -396,7 +396,7 @@ describe('Yahoo ConnectID Submodule', () => { expect(ajaxStub.firstCall.args[0].indexOf(`${PROD_ENDPOINT}?`)).to.equal(0); expect(requestQueryParams).to.deep.equal(expectedParams); - expect(ajaxStub.firstCall.args[3]).to.deep.equal({method: 'GET', withCredentials: true}); + expect(ajaxStub.firstCall.args[3]).to.deep.equal({ method: 'GET', withCredentials: true }); }); it('Makes an ajax GET request to the production API endpoint without the stored puid after 30 days', () => { @@ -430,7 +430,7 @@ describe('Yahoo ConnectID Submodule', () => { expect(ajaxStub.firstCall.args[0].indexOf(`${PROD_ENDPOINT}?`)).to.equal(0); expect(requestQueryParams).to.deep.equal(expectedParams); - expect(ajaxStub.firstCall.args[3]).to.deep.equal({method: 'GET', withCredentials: true}); + expect(ajaxStub.firstCall.args[3]).to.deep.equal({ method: 'GET', withCredentials: true }); }); it('Makes an ajax GET request to the production API endpoint with provided puid', () => { @@ -465,11 +465,11 @@ describe('Yahoo ConnectID Submodule', () => { expect(ajaxStub.firstCall.args[0].indexOf(`${PROD_ENDPOINT}?`)).to.equal(0); expect(requestQueryParams).to.deep.equal(expectedParams); - expect(ajaxStub.firstCall.args[3]).to.deep.equal({method: 'GET', withCredentials: true}); + expect(ajaxStub.firstCall.args[3]).to.deep.equal({ method: 'GET', withCredentials: true }); }); it('deletes local storage data when expiry has passed', () => { - const localStorageData = {connectId: 'foobarbaz', __expires: Date.now() - 10000}; + const localStorageData = { connectId: 'foobarbaz', __expires: Date.now() - 10000 }; getLocalStorageStub.withArgs(STORAGE_KEY).returns(localStorageData); const result = invokeGetIdAPI({ he: HASHED_EMAIL, @@ -482,7 +482,7 @@ describe('Yahoo ConnectID Submodule', () => { }); it('will not delete local storage data when expiry has not passed', () => { - const localStorageData = {connectId: 'foobarbaz', __expires: Date.now() + 10000}; + const localStorageData = { connectId: 'foobarbaz', __expires: Date.now() + 10000 }; getLocalStorageStub.withArgs(STORAGE_KEY).returns(localStorageData); const result = invokeGetIdAPI({ he: HASHED_EMAIL, @@ -596,7 +596,7 @@ describe('Yahoo ConnectID Submodule', () => { expect(ajaxStub.firstCall.args[0].indexOf(`${PROD_ENDPOINT}?`)).to.equal(0); expect(requestQueryParams).to.deep.equal(expectedParams); - expect(ajaxStub.firstCall.args[3]).to.deep.equal({method: 'GET', withCredentials: true}); + expect(ajaxStub.firstCall.args[3]).to.deep.equal({ method: 'GET', withCredentials: true }); }); it('Makes an ajax GET request to the production API endpoint with pixelId and puid query params', () => { @@ -621,7 +621,7 @@ describe('Yahoo ConnectID Submodule', () => { expect(ajaxStub.firstCall.args[0].indexOf(`${PROD_ENDPOINT}?`)).to.equal(0); expect(requestQueryParams).to.deep.equal(expectedParams); - expect(ajaxStub.firstCall.args[3]).to.deep.equal({method: 'GET', withCredentials: true}); + expect(ajaxStub.firstCall.args[3]).to.deep.equal({ method: 'GET', withCredentials: true }); }); it('Makes an ajax GET request to the production API endpoint with pixelId, puid and he query params', () => { @@ -648,7 +648,7 @@ describe('Yahoo ConnectID Submodule', () => { expect(ajaxStub.firstCall.args[0].indexOf(`${PROD_ENDPOINT}?`)).to.equal(0); expect(requestQueryParams).to.deep.equal(expectedParams); - expect(ajaxStub.firstCall.args[3]).to.deep.equal({method: 'GET', withCredentials: true}); + expect(ajaxStub.firstCall.args[3]).to.deep.equal({ method: 'GET', withCredentials: true }); }); it('Makes an ajax GET request to the specified override API endpoint with query params', () => { @@ -672,7 +672,7 @@ describe('Yahoo ConnectID Submodule', () => { expect(ajaxStub.firstCall.args[0].indexOf(`${OVERRIDE_ENDPOINT}?`)).to.equal(0); expect(requestQueryParams).to.deep.equal(expectedParams); - expect(ajaxStub.firstCall.args[3]).to.deep.equal({method: 'GET', withCredentials: true}); + expect(ajaxStub.firstCall.args[3]).to.deep.equal({ method: 'GET', withCredentials: true }); }); it('Makes an ajax GET request to the specified override API endpoint without GPP', () => { @@ -695,7 +695,7 @@ describe('Yahoo ConnectID Submodule', () => { expect(ajaxStub.firstCall.args[0].indexOf(`${OVERRIDE_ENDPOINT}?`)).to.equal(0); expect(requestQueryParams).to.deep.equal(expectedParams); - expect(ajaxStub.firstCall.args[3]).to.deep.equal({method: 'GET', withCredentials: true}); + expect(ajaxStub.firstCall.args[3]).to.deep.equal({ method: 'GET', withCredentials: true }); }); it('sets the callbacks param of the ajax function call correctly', () => { @@ -748,7 +748,7 @@ describe('Yahoo ConnectID Submodule', () => { const dateNowStub = sinon.stub(Date, 'now'); dateNowStub.returns(0); getAjaxFnStub.restore(); - const upsResponse = {connectid: 'foobarbaz'}; + const upsResponse = { connectid: 'foobarbaz' }; const expiryDelta = new Date(60 * 60 * 24 * 365 * 1000); invokeGetIdAPI({ puid: PUBLISHER_USER_ID, @@ -757,7 +757,7 @@ describe('Yahoo ConnectID Submodule', () => { const request = server.requests[0]; request.respond( 200, - {'Content-Type': 'application/json'}, + { 'Content-Type': 'application/json' }, JSON.stringify(upsResponse) ); const storage = Object.assign({}, upsResponse, { @@ -784,7 +784,7 @@ describe('Yahoo ConnectID Submodule', () => { cookiesEnabledStub.returns(false); const dateNowStub = sinon.stub(Date, 'now'); dateNowStub.returns(0); - const upsResponse = {connectid: 'barfoo'}; + const upsResponse = { connectid: 'barfoo' }; const expectedStoredData = { connectid: 'barfoo', puid: PUBLISHER_USER_ID, @@ -798,7 +798,7 @@ describe('Yahoo ConnectID Submodule', () => { const request = server.requests[0]; request.respond( 200, - {'Content-Type': 'application/json'}, + { 'Content-Type': 'application/json' }, JSON.stringify(upsResponse) ); dateNowStub.restore(); @@ -812,7 +812,7 @@ describe('Yahoo ConnectID Submodule', () => { getAjaxFnStub.restore(); const dateNowStub = sinon.stub(Date, 'now'); dateNowStub.returns(0); - const upsResponse = {connectid: 'html5only'}; + const upsResponse = { connectid: 'html5only' }; const expectedStoredData = { connectid: 'html5only', puid: PUBLISHER_USER_ID, @@ -822,11 +822,11 @@ describe('Yahoo ConnectID Submodule', () => { invokeGetIdAPI({ puid: PUBLISHER_USER_ID, pixelId: PIXEL_ID - }, consentData, {type: 'html5'}); + }, consentData, { type: 'html5' }); const request = server.requests[0]; request.respond( 200, - {'Content-Type': 'application/json'}, + { 'Content-Type': 'application/json' }, JSON.stringify(upsResponse) ); dateNowStub.restore(); @@ -841,7 +841,7 @@ describe('Yahoo ConnectID Submodule', () => { getAjaxFnStub.restore(); const dateNowStub = sinon.stub(Date, 'now'); dateNowStub.returns(0); - const upsResponse = {connectid: 'cookieonly'}; + const upsResponse = { connectid: 'cookieonly' }; const expectedStoredData = { connectid: 'cookieonly', puid: PUBLISHER_USER_ID, @@ -852,11 +852,11 @@ describe('Yahoo ConnectID Submodule', () => { invokeGetIdAPI({ puid: PUBLISHER_USER_ID, pixelId: PIXEL_ID - }, consentData, {type: 'cookie'}); + }, consentData, { type: 'cookie' }); const request = server.requests[0]; request.respond( 200, - {'Content-Type': 'application/json'}, + { 'Content-Type': 'application/json' }, JSON.stringify(upsResponse) ); dateNowStub.restore(); @@ -869,27 +869,27 @@ describe('Yahoo ConnectID Submodule', () => { }); it('does not sync localStorage to cookie when storage type is html5', () => { - const localStorageData = {connectId: 'foobarbaz'}; + const localStorageData = { connectId: 'foobarbaz' }; getLocalStorageStub.withArgs(STORAGE_KEY).returns(localStorageData); invokeGetIdAPI({ he: HASHED_EMAIL, pixelId: PIXEL_ID - }, consentData, {type: 'html5'}); + }, consentData, { type: 'html5' }); expect(setCookieStub.called).to.be.false; }); it('updates existing ID with html5 storage type without writing cookie', () => { const last13Days = Date.now() - (60 * 60 * 24 * 1000 * 13); - const cookieData = {connectId: 'foobar', he: HASHED_EMAIL, lastSynced: last13Days}; + const cookieData = { connectId: 'foobar', he: HASHED_EMAIL, lastSynced: last13Days }; getCookieStub.withArgs(STORAGE_KEY).returns(JSON.stringify(cookieData)); const dateNowStub = sinon.stub(Date, 'now'); dateNowStub.returns(20); - const newCookieData = Object.assign({}, cookieData, {lastUsed: 20}) + const newCookieData = Object.assign({}, cookieData, { lastUsed: 20 }) const result = invokeGetIdAPI({ he: HASHED_EMAIL, pixelId: PIXEL_ID - }, consentData, {type: 'html5'}); + }, consentData, { type: 'html5' }); dateNowStub.restore(); expect(result).to.be.an('object').that.has.all.keys('id'); @@ -903,7 +903,7 @@ describe('Yahoo ConnectID Submodule', () => { getAjaxFnStub.restore(); const dateNowStub = sinon.stub(Date, 'now'); dateNowStub.returns(0); - const upsResponse = {connectid: 'both'}; + const upsResponse = { connectid: 'both' }; const expectedStoredData = { connectid: 'both', puid: PUBLISHER_USER_ID, @@ -914,11 +914,11 @@ describe('Yahoo ConnectID Submodule', () => { invokeGetIdAPI({ puid: PUBLISHER_USER_ID, pixelId: PIXEL_ID - }, consentData, {type: 'cookie&html5'}); + }, consentData, { type: 'cookie&html5' }); const request = server.requests[0]; request.respond( 200, - {'Content-Type': 'application/json'}, + { 'Content-Type': 'application/json' }, JSON.stringify(upsResponse) ); dateNowStub.restore(); @@ -982,12 +982,12 @@ describe('Yahoo ConnectID Submodule', () => { VALID_API_RESPONSES.forEach(responseData => { it('should return a newly constructed object with the connect ID for a payload with ${responseData.key} key(s)', () => { expect(connectIdSubmodule.decode(responseData.payload)).to.deep.equal( - {connectId: responseData.expected} + { connectId: responseData.expected } ); }); }); - [{}, '', {foo: 'bar'}].forEach((response) => { + [{}, '', { foo: 'bar' }].forEach((response) => { it(`should return undefined for an invalid response "${JSON.stringify(response)}"`, () => { expect(connectIdSubmodule.decode(response)).to.be.undefined; }); diff --git a/test/spec/modules/connectadBidAdapter_spec.js b/test/spec/modules/connectadBidAdapter_spec.js index 724f4eb93d4..92431c1265a 100644 --- a/test/spec/modules/connectadBidAdapter_spec.js +++ b/test/spec/modules/connectadBidAdapter_spec.js @@ -1,7 +1,7 @@ -import {expect} from 'chai'; -import {spec} from 'modules/connectadBidAdapter.js'; +import { expect } from 'chai'; +import { spec } from 'modules/connectadBidAdapter.js'; import { config } from 'src/config.js'; -import {newBidder} from 'src/adapters/bidderFactory.js'; +import { newBidder } from 'src/adapters/bidderFactory.js'; import assert from 'assert'; describe('ConnectAd Adapter', function () { @@ -357,10 +357,10 @@ describe('ConnectAd Adapter', function () { const data = JSON.parse(request.data); expect(data.bcat).to.deep.equal(localBidderRequest.ortb2.bcat); expect(data.badv).to.deep.equal(localBidderRequest.ortb2.badv); - expect(data.site).to.nested.include({'ext.data': 'some site data'}); - expect(data.device).to.nested.include({'ext.data': 'some device data'}); - expect(data.user).to.nested.include({'ext.data': 'some user data'}); - expect(data.regs).to.nested.include({'ext.data': 'some regs data'}); + expect(data.site).to.nested.include({ 'ext.data': 'some site data' }); + expect(data.device).to.nested.include({ 'ext.data': 'some device data' }); + expect(data.user).to.nested.include({ 'ext.data': 'some user data' }); + expect(data.regs).to.nested.include({ 'ext.data': 'some regs data' }); }); it('should accept tmax from global config if not set by requestBids method', function() { @@ -685,7 +685,7 @@ describe('ConnectAd Adapter', function () { describe('GPP Sync', function() { it('should concatenate gppString and applicableSections values in the returned image url', () => { const gppConsent = { gppString: 'DBACNYA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA~1YNN', applicableSections: [5] }; - const result = spec.getUserSyncs({iframeEnabled: false, pixelEnabled: true}, undefined, undefined, undefined, gppConsent); + const result = spec.getUserSyncs({ iframeEnabled: false, pixelEnabled: true }, undefined, undefined, undefined, gppConsent); expect(result).to.deep.equal([{ type: 'image', url: `https://sync.connectad.io/ImageSyncer?gpp=DBACNYA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA~1YNN&gpp_sid=5&` @@ -694,7 +694,7 @@ describe('ConnectAd Adapter', function () { it('should concatenate gppString and applicableSections values in the returned iFrame url', () => { const gppConsent = { gppString: 'DBACNYA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA~1YNN', applicableSections: [5, 6] }; - const result = spec.getUserSyncs({iframeEnabled: true}, undefined, undefined, undefined, gppConsent); + const result = spec.getUserSyncs({ iframeEnabled: true }, undefined, undefined, undefined, gppConsent); expect(result).to.deep.equal([{ type: 'iframe', url: `https://sync.connectad.io/iFrameSyncer?gpp=DBACNYA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA~1YNN&gpp_sid=5%2C6&` @@ -702,7 +702,7 @@ describe('ConnectAd Adapter', function () { }); it('should return url without Gpp consent if gppConsent is undefined', () => { - const result = spec.getUserSyncs({iframeEnabled: true}, undefined, undefined, undefined, undefined); + const result = spec.getUserSyncs({ iframeEnabled: true }, undefined, undefined, undefined, undefined); expect(result).to.deep.equal([{ type: 'iframe', url: `https://sync.connectad.io/iFrameSyncer?` @@ -711,7 +711,7 @@ describe('ConnectAd Adapter', function () { it('should return iFrame url without Gpp consent if gppConsent.gppString is undefined', () => { const gppConsent = { applicableSections: ['5'] }; - const result = spec.getUserSyncs({iframeEnabled: true}, undefined, undefined, undefined, gppConsent); + const result = spec.getUserSyncs({ iframeEnabled: true }, undefined, undefined, undefined, gppConsent); expect(result).to.deep.equal([{ type: 'iframe', url: `https://sync.connectad.io/iFrameSyncer?` @@ -731,7 +731,7 @@ describe('ConnectAd Adapter', function () { }, { name: 'iframe/gdpr', - arguments: [{ iframeEnabled: true, pixelEnabled: false }, {}, {gdprApplies: true, consentString: '234234'}], + arguments: [{ iframeEnabled: true, pixelEnabled: false }, {}, { gdprApplies: true, consentString: '234234' }], expect: { type: 'iframe', pixels: ['https://sync.connectad.io/iFrameSyncer?gdpr=1&gdpr_consent=234234&'] @@ -747,7 +747,7 @@ describe('ConnectAd Adapter', function () { }, { name: 'iframe/ccpa & gdpr', - arguments: [{ iframeEnabled: true, pixelEnabled: false }, {}, {gdprApplies: true, consentString: '234234'}, 'YN12'], + arguments: [{ iframeEnabled: true, pixelEnabled: false }, {}, { gdprApplies: true, consentString: '234234' }, 'YN12'], expect: { type: 'iframe', pixels: ['https://sync.connectad.io/iFrameSyncer?gdpr=1&gdpr_consent=234234&us_privacy=YN12&'] @@ -755,7 +755,7 @@ describe('ConnectAd Adapter', function () { }, { name: 'image/ccpa & gdpr', - arguments: [{ iframeEnabled: false, pixelEnabled: true }, {}, {gdprApplies: true, consentString: '234234'}, 'YN12'], + arguments: [{ iframeEnabled: false, pixelEnabled: true }, {}, { gdprApplies: true, consentString: '234234' }, 'YN12'], expect: { type: 'image', pixels: ['https://sync.connectad.io/ImageSyncer?gdpr=1&gdpr_consent=234234&us_privacy=YN12&'] @@ -763,7 +763,7 @@ describe('ConnectAd Adapter', function () { }, { name: 'image/gdpr', - arguments: [{ iframeEnabled: false, pixelEnabled: true }, {}, {gdprApplies: true, consentString: '234234'}], + arguments: [{ iframeEnabled: false, pixelEnabled: true }, {}, { gdprApplies: true, consentString: '234234' }], expect: { type: 'image', pixels: ['https://sync.connectad.io/ImageSyncer?gdpr=1&gdpr_consent=234234&'] diff --git a/test/spec/modules/consentManagementGpp_spec.js b/test/spec/modules/consentManagementGpp_spec.js index e143311a880..25fd87a65da 100644 --- a/test/spec/modules/consentManagementGpp_spec.js +++ b/test/spec/modules/consentManagementGpp_spec.js @@ -4,9 +4,9 @@ import { resetConsentData, setConsentConfig, } from 'modules/consentManagementGpp.js'; -import {gppDataHandler} from 'src/adapterManager.js'; +import { gppDataHandler } from 'src/adapterManager.js'; import * as utils from 'src/utils.js'; -import {config} from 'src/config.js'; +import { config } from 'src/config.js'; import 'src/prebid.js'; const expect = require('chai').expect; @@ -179,7 +179,7 @@ describe('consentManagementGpp', function () { let gppClient, gppData, eventListener; function mockClient(apiVersion = '1.1', cmpVersion = '1.1') { - const mockCmp = sinon.stub().callsFake(function ({command, callback}) { + const mockCmp = sinon.stub().callsFake(function ({ command, callback }) { if (command === 'addEventListener') { eventListener = callback; } else { @@ -253,7 +253,7 @@ describe('consentManagementGpp', function () { Object.entries(tests).forEach(([t, value]) => { describe(t, () => { it('should not update', (done) => { - Object.assign(gppData, {[prop]: value}); + Object.assign(gppData, { [prop]: value }); gppClient.updateConsent(gppData).catch(err => { expect(err.message).to.match(/unexpected/); expect(err.args).to.eql([gppData]); @@ -270,13 +270,13 @@ describe('consentManagementGpp', function () { describe('init', () => { it('does not use initial pingData if CMP is not ready', () => { - gppClient.init({...gppData, signalStatus: 'not ready'}); + gppClient.init({ ...gppData, signalStatus: 'not ready' }); expect(eventListener).to.exist; expect(gppDataHandler.ready).to.be.false; }); it('uses initial pingData (and resolves promise) if CMP is ready', () => { - return gppClient.init({...gppData, signalStatus: 'ready'}).then(data => { + return gppClient.init({ ...gppData, signalStatus: 'ready' }).then(data => { expect(eventListener).to.exist; sinon.assert.match(data, gppData); sinon.assert.match(gppDataHandler.getConsentData(), gppData); @@ -284,7 +284,7 @@ describe('consentManagementGpp', function () { }); it('rejects promise when CMP errors out', (done) => { - gppClient.init({signalStatus: 'not ready'}).catch((err) => { + gppClient.init({ signalStatus: 'not ready' }).catch((err) => { expect(err.message).to.match(/error/); expect(err.args).to.eql(['error']) done(); @@ -295,10 +295,10 @@ describe('consentManagementGpp', function () { Object.entries({ 'empty': {}, 'null': null, - 'irrelevant': {eventName: 'irrelevant'} + 'irrelevant': { eventName: 'irrelevant' } }).forEach(([t, evt]) => { it(`ignores ${t} events`, () => { - const pm = gppClient.init({signalStatus: 'not ready'}).catch((err) => err.args[0] !== 'done' && Promise.reject(err)); + const pm = gppClient.init({ signalStatus: 'not ready' }).catch((err) => err.args[0] !== 'done' && Promise.reject(err)); eventListener(evt); eventListener('done', false); return pm; @@ -306,8 +306,8 @@ describe('consentManagementGpp', function () { }); it('rejects the promise when cmpStatus is "error"', (done) => { - const evt = {eventName: 'other', pingData: {cmpStatus: 'error'}}; - gppClient.init({signalStatus: 'not ready'}).catch(err => { + const evt = { eventName: 'other', pingData: { cmpStatus: 'error' } }; + gppClient.init({ signalStatus: 'not ready' }).catch(err => { expect(err.message).to.match(/error/); expect(err.args).to.eql([evt]); done(); @@ -326,34 +326,34 @@ describe('consentManagementGpp', function () { let gppData2 beforeEach(() => { - gppData2 = Object.assign(gppData, {gppString: '2nd'}); + gppData2 = Object.assign(gppData, { gppString: '2nd' }); }); it('does not fire consent data updates if the CMP is not ready', (done) => { - gppClient.init({signalStatus: 'not ready'}).catch(() => { + gppClient.init({ signalStatus: 'not ready' }).catch(() => { expect(gppDataHandler.ready).to.be.false; done(); }); - eventListener({...gppData2, signalStatus: 'not ready'}); + eventListener({ ...gppData2, signalStatus: 'not ready' }); eventListener('done', false); }) it('fires consent data updates (and resolves promise) if CMP is ready', (done) => { - gppClient.init({signalStatus: 'not ready'}).then(data => { + gppClient.init({ signalStatus: 'not ready' }).then(data => { sinon.assert.match(data, gppData2); done() }); - eventListener(makeEvent({...gppData2, signalStatus: 'ready'})); + eventListener(makeEvent({ ...gppData2, signalStatus: 'ready' })); }); it('keeps updating consent data on new events', () => { - const pm = gppClient.init({signalStatus: 'not ready'}).then(data => { + const pm = gppClient.init({ signalStatus: 'not ready' }).then(data => { sinon.assert.match(data, gppData); sinon.assert.match(gppDataHandler.getConsentData(), gppData); }); - eventListener(makeEvent({...gppData, signalStatus: 'ready'})); + eventListener(makeEvent({ ...gppData, signalStatus: 'ready' })); return pm.then(() => { - eventListener(makeEvent({...gppData2, signalStatus: 'ready'})) + eventListener(makeEvent({ ...gppData2, signalStatus: 'ready' })) }).then(() => { sinon.assert.match(gppDataHandler.getConsentData(), gppData2); }); @@ -377,7 +377,7 @@ describe('consentManagementGpp', function () { 'undefined': [false, undefined] }).forEach(([t, [expected, signalStatus]]) => { it(`should be ${expected} when signalStatus is ${t}`, () => { - expect(gppClient.isCMPReady(Object.assign({}, {signalStatus}))).to.equal(expected); + expect(gppClient.isCMPReady(Object.assign({}, { signalStatus }))).to.equal(expected); }); }); }); @@ -498,7 +498,7 @@ describe('consentManagementGpp', function () { window.__gpp = sinon.stub().callsFake(function (command, callback) { switch (command) { case 'addEventListener': - triggerCMPEvent = (event, payload = {}) => callback({eventName: event, pingData: {...pingData, ...payload}}) + triggerCMPEvent = (event, payload = {}) => callback({ eventName: event, pingData: { ...pingData, ...payload } }) break; case 'ping': callback(pingData) @@ -526,9 +526,9 @@ describe('consentManagementGpp', function () { it('should wait for signalStatus: ready', async () => { const didHookRun = startHook(); expect(await didHookRun()).to.be.false; - triggerCMPEvent('sectionChange', {signalStatus: 'not ready'}); + triggerCMPEvent('sectionChange', { signalStatus: 'not ready' }); expect(await didHookRun()).to.be.false; - triggerCMPEvent('sectionChange', {signalStatus: 'ready'}); + triggerCMPEvent('sectionChange', { signalStatus: 'ready' }); await consentConfig.loadConsentData(); expect(await didHookRun()).to.be.true; expect(gppDataHandler.getConsentData().gppString).to.eql('xyz'); @@ -537,7 +537,7 @@ describe('consentManagementGpp', function () { it('should re-use GPP data once ready', async () => { let didHookRun = startHook(); await didHookRun(); - triggerCMPEvent('sectionChange', {signalStatus: 'ready'}); + triggerCMPEvent('sectionChange', { signalStatus: 'ready' }); await consentConfig.loadConsentData(); window.__gpp.resetHistory(); didHookRun = startHook(); @@ -549,13 +549,13 @@ describe('consentManagementGpp', function () { it('after signalStatus: ready, should wait again for signalStatus: ready', async () => { let didHookRun = startHook(); await didHookRun(); - triggerCMPEvent('sectionChange', {signalStatus: 'ready'}); + triggerCMPEvent('sectionChange', { signalStatus: 'ready' }); await consentConfig.loadConsentData(); for (const run of ['first', 'second']) { - triggerCMPEvent('cmpDisplayStatus', {signalStatus: 'not ready'}); + triggerCMPEvent('cmpDisplayStatus', { signalStatus: 'not ready' }); didHookRun = startHook(); expect(await didHookRun()).to.be.false; - triggerCMPEvent('sectionChange', {signalStatus: 'ready', gppString: run}); + triggerCMPEvent('sectionChange', { signalStatus: 'ready', gppString: run }); await consentConfig.loadConsentData(); expect(await didHookRun()).to.be.true; expect(gppDataHandler.getConsentData().gppString).to.eql(run); diff --git a/test/spec/modules/consentManagementUsp_spec.js b/test/spec/modules/consentManagementUsp_spec.js index 8b011f20cbb..f9cfa19ed62 100644 --- a/test/spec/modules/consentManagementUsp_spec.js +++ b/test/spec/modules/consentManagementUsp_spec.js @@ -8,9 +8,9 @@ import { } from 'modules/consentManagementUsp.js'; import * as utils from 'src/utils.js'; import { config } from 'src/config.js'; -import adapterManager, {gdprDataHandler, uspDataHandler} from 'src/adapterManager.js'; -import {requestBids} from '../../../src/prebid.js'; -import {defer} from '../../../src/utils/promise.js'; +import adapterManager, { gdprDataHandler, uspDataHandler } from 'src/adapterManager.js'; +import { requestBids } from '../../../src/prebid.js'; +import { defer } from '../../../src/utils/promise.js'; const expect = require('chai').expect; @@ -79,7 +79,7 @@ describe('consentManagement', function () { }); it('should use system default values', function () { - setConsentConfig({usp: {}}); + setConsentConfig({ usp: {} }); expect(consentAPI).to.be.equal('iab'); expect(consentTimeout).to.be.equal(50); sinon.assert.callCount(utils.logInfo, 3); @@ -113,7 +113,7 @@ describe('consentManagement', function () { }); it('should immediately start looking up consent data', () => { - setConsentConfig({usp: {cmpApi: 'invalid'}}); + setConsentConfig({ usp: { cmpApi: 'invalid' } }); expect(uspDataHandler.ready).to.be.true; }); }); @@ -138,12 +138,12 @@ describe('consentManagement', function () { }); it('should enable uspDataHandler', () => { - setConsentConfig({usp: {cmpApi: 'daa', timeout: 7500}}); + setConsentConfig({ usp: { cmpApi: 'daa', timeout: 7500 } }); expect(uspDataHandler.enabled).to.be.true; }); it('should call setConsentData(null) on invalid CMP api', () => { - setConsentConfig({usp: {cmpApi: 'invalid'}}); + setConsentConfig({ usp: { cmpApi: 'invalid' } }); let hookRan = false; requestBidsHook(() => { hookRan = true; @@ -296,7 +296,7 @@ describe('consentManagement', function () { }); it('should call uspDataHandler.setConsentData(null) on timeout', (done) => { - setConsentConfig({usp: {timeout: 10}}); + setConsentConfig({ usp: { timeout: 10 } }); let hookRan = false; uspStub = sinon.stub(window, '__uspapi').callsFake(() => {}); requestBidsHook(() => { hookRan = true; }, {}); @@ -338,7 +338,7 @@ describe('consentManagement', function () { if (event && event.data) { const data = event.data; if (data.__uspapiCall) { - const {command, version, callId} = data.__uspapiCall; + const { command, version, callId } = data.__uspapiCall; let response = mockApi(command, version, callId); if (response) { response = { @@ -515,7 +515,7 @@ describe('consentManagement', function () { if (cmd === 'registerDeletion') { throw new Error('CMP not compliant'); } else if (cmd === 'getUSPData') { - cb({uspString: 'string'}, true); + cb({ uspString: 'string' }, true); } }); setConsentConfig(goodConfig); @@ -527,7 +527,7 @@ describe('consentManagement', function () { if (cmd === 'registerDeletion') { cb(null, false); } else { - cb({uspString: 'string'}, true); + cb({ uspString: 'string' }, true); } }); setConsentConfig(goodConfig); diff --git a/test/spec/modules/consentManagement_spec.js b/test/spec/modules/consentManagement_spec.js index dfe5f3d6a0e..8e6d08b9605 100644 --- a/test/spec/modules/consentManagement_spec.js +++ b/test/spec/modules/consentManagement_spec.js @@ -1,7 +1,7 @@ -import {consentConfig, gdprScope, resetConsentData, setConsentConfig, tcfCmpEventManager} from 'modules/consentManagementTcf.js'; -import {gdprDataHandler} from 'src/adapterManager.js'; +import { consentConfig, gdprScope, resetConsentData, setConsentConfig, tcfCmpEventManager } from 'modules/consentManagementTcf.js'; +import { gdprDataHandler } from 'src/adapterManager.js'; import * as utils from 'src/utils.js'; -import {config} from 'src/config.js'; +import { config } from 'src/config.js'; import 'src/prebid.js'; const expect = require('chai').expect; @@ -9,7 +9,7 @@ const expect = require('chai').expect; describe('consentManagement', function () { function mockCMP(cmpResponse) { return function(...args) { - args[2](Object.assign({eventStatus: 'tcloaded'}, cmpResponse), true); + args[2](Object.assign({ eventStatus: 'tcloaded' }, cmpResponse), true); } } @@ -41,7 +41,7 @@ describe('consentManagement', function () { }); it('should exit consent manager if gdpr not set with new config structure', async function () { - await setConsentConfig({usp: {cmpApi: 'iab', timeout: 50}}); + await setConsentConfig({ usp: { cmpApi: 'iab', timeout: 50 } }); expect(consentConfig.cmpHandler).to.be.undefined; sinon.assert.calledOnce(utils.logWarn); }); @@ -60,7 +60,7 @@ describe('consentManagement', function () { }) it('should immediately look up consent data', async () => { - await setConsentConfig({gdpr: {cmpApi: 'invalid'}}); + await setConsentConfig({ gdpr: { cmpApi: 'invalid' } }); expect(gdprDataHandler.ready).to.be.true; }) }); @@ -85,7 +85,7 @@ describe('consentManagement', function () { it('should use new consent manager config structure for gdpr', async function () { await setConsentConfig({ - gdpr: {cmpApi: 'daa', timeout: 8700} + gdpr: { cmpApi: 'daa', timeout: 8700 } }); expect(consentConfig.cmpHandler).to.be.equal('daa'); @@ -94,8 +94,8 @@ describe('consentManagement', function () { it('should ignore config.usp and use config.gdpr, with default cmpApi', async function () { await setConsentConfig({ - gdpr: {timeout: 5000}, - usp: {cmpApi: 'daa', timeout: 50} + gdpr: { timeout: 5000 }, + usp: { cmpApi: 'daa', timeout: 50 } }); expect(consentConfig.cmpHandler).to.be.equal('iab'); @@ -105,7 +105,7 @@ describe('consentManagement', function () { it('should ignore config.usp and use config.gdpr, with default cmpAip and timeout', async function () { await setConsentConfig({ gdpr: {}, - usp: {cmpApi: 'daa', timeout: 50} + usp: { cmpApi: 'daa', timeout: 50 } }); expect(consentConfig.cmpHandler).to.be.equal('iab'); @@ -134,14 +134,14 @@ describe('consentManagement', function () { }); it('should enable gdprDataHandler', async () => { - await setConsentConfig({gdpr: {}}); + await setConsentConfig({ gdpr: {} }); expect(gdprDataHandler.enabled).to.be.true; }); }); describe('static consent string setConsentConfig value', () => { Object.entries({ - 'getTCData': (cfg) => ({getTCData: cfg}), + 'getTCData': (cfg) => ({ getTCData: cfg }), 'consent data directly': (cfg) => cfg, }).forEach(([t, packageCfg]) => { describe(`using ${t}`, () => { @@ -284,7 +284,7 @@ describe('consentManagement', function () { }); it('should call gdprDataHandler.setConsentData() when unknown CMP api is used', async () => { - await setConsentConfig({gdpr: {cmpApi: 'invalid'}}); + await setConsentConfig({ gdpr: { cmpApi: 'invalid' } }); expect(await runHook()).to.be.true; expect(gdprDataHandler.ready).to.be.true; }) @@ -319,7 +319,7 @@ describe('consentManagement', function () { it('should not trip when adUnits have no size', async () => { await setConsentConfig(staticConfig); - expect(await runHook({adUnits: [{code: 'test', mediaTypes: {video: {}}}]})).to.be.true; + expect(await runHook({ adUnits: [{ code: 'test', mediaTypes: { video: {} } }] })).to.be.true; }); it('should continue the auction immediately, without consent data, if timeout is 0', async () => { @@ -585,7 +585,7 @@ describe('consentManagement', function () { [utils.logWarn, utils.logError].forEach((stub) => stub.resetHistory()); - expect(await runHook({bidsBackHandler: () => bidsBackHandlerReturn = true})).to.be.false; + expect(await runHook({ bidsBackHandler: () => bidsBackHandlerReturn = true })).to.be.false; const consent = gdprDataHandler.getConsentData(); sinon.assert.calledOnce(utils.logError); @@ -701,7 +701,7 @@ describe('consentManagement', function () { mockTcfEvent({ eventStatus: 'cmpuishown', tcString: cs, - vendorData: {random: 'junk'} + vendorData: { random: 'junk' } }); return runAuction().then(() => { const consent = gdprDataHandler.getConsentData(); diff --git a/test/spec/modules/consumableBidAdapter_spec.js b/test/spec/modules/consumableBidAdapter_spec.js index 51db64019b5..c60a0abebd0 100644 --- a/test/spec/modules/consumableBidAdapter_spec.js +++ b/test/spec/modules/consumableBidAdapter_spec.js @@ -1,8 +1,8 @@ -import {expect} from 'chai'; -import {spec} from 'modules/consumableBidAdapter.js'; -import {createBid} from 'src/bidfactory.js'; -import {config} from 'src/config.js'; -import {deepClone} from 'src/utils.js'; +import { expect } from 'chai'; +import { spec } from 'modules/consumableBidAdapter.js'; +import { createBid } from 'src/bidfactory.js'; +import { config } from 'src/config.js'; +import { deepClone } from 'src/utils.js'; import { createEidsArray } from 'modules/userId/eids.js'; const ENDPOINT = 'https://e.serverbid.com/api/v2'; @@ -242,7 +242,7 @@ const AD_SERVER_RESPONSE = { 'height': 90, 'width': 728, 'events': [], - 'pricing': {'price': 0.5, 'clearPrice': 0.5, 'revenue': 0.0005, 'rateType': 2, 'eCPM': 0.5} + 'pricing': { 'price': 0.5, 'clearPrice': 0.5, 'revenue': 0.0005, 'rateType': 2, 'eCPM': 0.5 } }, '123': { 'adId': 2364764, @@ -265,7 +265,7 @@ const AD_SERVER_RESPONSE = { 'height': 90, 'width': 728, 'events': [], - 'pricing': {'price': 0.5, 'clearPrice': 0.5, 'revenue': 0.0005, 'rateType': 2, 'eCPM': 0.5} + 'pricing': { 'price': 0.5, 'clearPrice': 0.5, 'revenue': 0.0005, 'rateType': 2, 'eCPM': 0.5 } } } } @@ -299,7 +299,7 @@ const AD_SERVER_RESPONSE_2 = { 'height': 90, 'width': 728, 'events': [], - 'pricing': {'price': 0.5, 'clearPrice': 0.5, 'revenue': 0.0005, 'rateType': 2, 'eCPM': 0.5}, + 'pricing': { 'price': 0.5, 'clearPrice': 0.5, 'revenue': 0.0005, 'rateType': 2, 'eCPM': 0.5 }, 'mediaType': 'banner', 'cats': ['IAB1', 'IAB2', 'IAB3'], 'networkId': 1234567, @@ -325,7 +325,7 @@ const AD_SERVER_RESPONSE_2 = { 'height': 90, 'width': 728, 'events': [], - 'pricing': {'price': 0.5, 'clearPrice': 0.5, 'revenue': 0.0005, 'rateType': 2, 'eCPM': 0.5}, + 'pricing': { 'price': 0.5, 'clearPrice': 0.5, 'revenue': 0.0005, 'rateType': 2, 'eCPM': 0.5 }, 'mediaType': 'banner', 'cats': ['IAB1', 'IAB2'], 'networkId': 2345678, @@ -614,7 +614,7 @@ describe('Consumable BidAdapter', function () { }) it('handles nobid responses', function () { - const EMPTY_RESP = Object.assign({}, AD_SERVER_RESPONSE, {'body': {'decisions': null}}) + const EMPTY_RESP = Object.assign({}, AD_SERVER_RESPONSE, { 'body': { 'decisions': null } }) const bids = spec.interpretResponse(EMPTY_RESP, BUILD_REQUESTS_OUTPUT); expect(bids).to.be.empty; @@ -627,7 +627,7 @@ describe('Consumable BidAdapter', function () { }); }); describe('getUserSyncs', function () { - const syncOptions = {'iframeEnabled': true}; + const syncOptions = { 'iframeEnabled': true }; it('handles empty sync options', function () { const opts = spec.getUserSyncs({}); @@ -724,7 +724,7 @@ describe('Consumable BidAdapter', function () { }) it('should return a sync url if pixel syncs are enabled and some are returned from the server', function () { - const syncOptions = {'pixelEnabled': true}; + const syncOptions = { 'pixelEnabled': true }; const opts = spec.getUserSyncs(syncOptions, [AD_SERVER_RESPONSE]); expect(opts.length).to.equal(1); diff --git a/test/spec/modules/contentexchangeBidAdapter_spec.js b/test/spec/modules/contentexchangeBidAdapter_spec.js index 12a4c6c5de2..cdf5be50250 100644 --- a/test/spec/modules/contentexchangeBidAdapter_spec.js +++ b/test/spec/modules/contentexchangeBidAdapter_spec.js @@ -481,7 +481,7 @@ describe('ContentexchangeBidAdapter', function () { const syncData = spec.getUserSyncs({}, {}, { consentString: 'ALL', gdprApplies: true, - }, {}); + }, undefined); expect(syncData).to.be.an('array').which.is.not.empty; expect(syncData[0]).to.be.an('object') expect(syncData[0].type).to.be.a('string') @@ -490,9 +490,7 @@ describe('ContentexchangeBidAdapter', function () { expect(syncData[0].url).to.equal('https://sync2.adnetwork.agency/image?pbjs=1&gdpr=1&gdpr_consent=ALL&coppa=0') }); it('Should return array of objects with proper sync config , include CCPA', function() { - const syncData = spec.getUserSyncs({}, {}, {}, { - consentString: '1---' - }); + const syncData = spec.getUserSyncs({}, {}, {}, '1---'); expect(syncData).to.be.an('array').which.is.not.empty; expect(syncData[0]).to.be.an('object') expect(syncData[0].type).to.be.a('string') @@ -501,7 +499,7 @@ describe('ContentexchangeBidAdapter', function () { expect(syncData[0].url).to.equal('https://sync2.adnetwork.agency/image?pbjs=1&ccpa_consent=1---&coppa=0') }); it('Should return array of objects with proper sync config , include GPP', function() { - const syncData = spec.getUserSyncs({}, {}, {}, {}, { + const syncData = spec.getUserSyncs({}, {}, {}, undefined, { gppString: 'abc123', applicableSections: [8] }); diff --git a/test/spec/modules/conversantBidAdapter_spec.js b/test/spec/modules/conversantBidAdapter_spec.js index ac19bcd10d3..209e660d0bd 100644 --- a/test/spec/modules/conversantBidAdapter_spec.js +++ b/test/spec/modules/conversantBidAdapter_spec.js @@ -1,7 +1,7 @@ -import {expect} from 'chai'; -import {spec} from 'modules/conversantBidAdapter.js'; +import { expect } from 'chai'; +import { spec } from 'modules/conversantBidAdapter.js'; import * as utils from 'src/utils.js'; -import {deepAccess} from 'src/utils'; +import { deepAccess } from 'src/utils'; // load modules that register ORTB processors import 'src/prebid.js' import 'modules/currency.js'; @@ -9,8 +9,8 @@ import 'modules/userId/index.js'; // handles eids import 'modules/priceFloors.js'; import 'modules/consentManagementTcf.js'; import 'modules/consentManagementUsp.js'; -import {hook} from '../../../src/hook.js' -import {BANNER} from '../../../src/mediaTypes.js'; +import { hook } from '../../../src/hook.js' +import { BANNER } from '../../../src/mediaTypes.js'; describe('Conversant adapter tests', function() { const siteId = '108060'; @@ -233,8 +233,8 @@ describe('Conversant adapter tests', function() { it('Verify isBidRequestValid', function() { expect(spec.isBidRequestValid({})).to.be.false; - expect(spec.isBidRequestValid({params: {}})).to.be.false; - expect(spec.isBidRequestValid({params: {site_id: '123'}})).to.be.true; + expect(spec.isBidRequestValid({ params: {} })).to.be.false; + expect(spec.isBidRequestValid({ params: { site_id: '123' } })).to.be.true; bidRequests.forEach((bid) => { expect(spec.isBidRequestValid(bid)).to.be.true; }); @@ -316,7 +316,7 @@ describe('Conversant adapter tests', function() { expect(payload.imp[0]).to.have.property('banner'); expect(payload.imp[0].banner).to.have.property('pos', 1); expect(payload.imp[0].banner).to.have.property('format'); - expect(payload.imp[0].banner.format).to.deep.equal([{w: 300, h: 250}]); + expect(payload.imp[0].banner.format).to.deep.equal([{ w: 300, h: 250 }]); expect(payload.imp[0]).to.not.have.property('video'); }); @@ -330,7 +330,7 @@ describe('Conversant adapter tests', function() { 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: 728, h: 90}, {w: 468, h: 60}]); + expect(payload.imp[1].banner.format).to.deep.equal([{ w: 728, h: 90 }, { w: 468, h: 60 }]); }); it('Banner with tagid and position', () => { @@ -342,7 +342,7 @@ describe('Conversant adapter tests', function() { expect(payload.imp[2]).to.have.property('banner'); expect(payload.imp[2].banner).to.have.property('pos', 2); expect(payload.imp[2].banner).to.have.property('format'); - expect(payload.imp[2].banner.format).to.deep.equal([{w: 300, h: 600}, {w: 160, h: 600}]); + expect(payload.imp[2].banner.format).to.deep.equal([{ w: 300, h: 600 }, { w: 160, h: 600 }]); }); if (FEATURES.VIDEO) { @@ -427,8 +427,8 @@ describe('Conversant adapter tests', function() { it('Verify first party data', () => { const bidderRequest = { - refererInfo: {page: 'http://test.com?a=b&c=123'}, - ortb2: {site: {content: {series: 'MySeries', season: 'MySeason', episode: 3, title: 'MyTitle'}}} + refererInfo: { page: 'http://test.com?a=b&c=123' }, + ortb2: { site: { content: { series: 'MySeries', season: 'MySeason', episode: 3, title: 'MyTitle' } } } }; const request = spec.buildRequests(bidRequests, bidderRequest); const payload = request.data; @@ -440,20 +440,20 @@ describe('Conversant adapter tests', function() { }); it('Verify currency', () => { - const bidderRequest = { timeout: 9999, ortb2: {cur: ['EUR']} }; + const bidderRequest = { timeout: 9999, ortb2: { cur: ['EUR'] } }; const request = spec.buildRequests(bidRequests, bidderRequest); const payload = request.data; expect(payload.cur).deep.equal(['USD']); }) it('Verify supply chain data', () => { - const bidderRequest = {refererInfo: {page: 'http://test.com?a=b&c=123'}}; - const schain = {complete: 1, ver: '1.0', nodes: [{asi: 'bidderA.com', sid: '00001', hp: 1}]}; + const bidderRequest = { refererInfo: { page: 'http://test.com?a=b&c=123' } }; + const schain = { complete: 1, ver: '1.0', nodes: [{ asi: 'bidderA.com', sid: '00001', hp: 1 }] }; // Add schain to bidderRequest bidderRequest.ortb2 = { source: { - ext: {schain: schain} + ext: { schain: schain } } }; @@ -461,7 +461,7 @@ describe('Conversant adapter tests', function() { return Object.assign({ ortb2: { source: { - ext: {schain: schain} + ext: { schain: schain } } } }, bid); @@ -474,7 +474,7 @@ describe('Conversant adapter tests', function() { it('Verify override url', function() { const testUrl = 'https://someurl?name=value'; - const request = spec.buildRequests([{params: {white_label_url: testUrl}}], {}); + const request = spec.buildRequests([{ params: { white_label_url: testUrl } }], {}); expect(request.url).to.equal(testUrl); }); @@ -564,10 +564,11 @@ describe('Conversant adapter tests', function() { const nativeMarkup = JSON.stringify({ native: { assets: [ - {id: 1, title: {text: 'TextValue!'}}, - {id: 5, data: {value: 'Epsilon'}}, + { id: 1, title: { text: 'TextValue!' } }, + { id: 5, data: { value: 'Epsilon' } }, ], - link: { url: 'https://www.epsilon.com/us', }, } + link: { url: 'https://www.epsilon.com/us', }, + } }); const nativeBidResponse = { @@ -607,13 +608,13 @@ describe('Conversant adapter tests', function() { // clone bidRequests const requests = utils.deepClone(bidRequests); - const eidArray = [{'source': 'pubcid.org', 'uids': [{'id': '112233', 'atype': 1}]}, {'source': 'liveramp.com', 'uids': [{'id': '334455', 'atype': 3}]}]; + const eidArray = [{ 'source': 'pubcid.org', 'uids': [{ 'id': '112233', 'atype': 1 }] }, { 'source': 'liveramp.com', 'uids': [{ 'id': '334455', 'atype': 3 }] }]; // construct http post payload - const payload = spec.buildRequests(requests, {ortb2: {user: {ext: {eids: eidArray}}}}).data; + const payload = spec.buildRequests(requests, { ortb2: { user: { ext: { eids: eidArray } } } }).data; expect(payload).to.have.deep.nested.property('user.ext.eids', [ - {source: 'pubcid.org', uids: [{id: '112233', atype: 1}]}, - {source: 'liveramp.com', uids: [{id: '334455', atype: 3}]} + { source: 'pubcid.org', uids: [{ id: '112233', atype: 1 }] }, + { source: 'liveramp.com', uids: [{ id: '334455', atype: 3 }] } ]); }); }); @@ -694,7 +695,7 @@ describe('Conversant adapter tests', function() { describe('getUserSyncs', function() { const syncurl_iframe = 'https://sync.dotomi.com:8080/iframe'; const syncurl_image = 'https://sync.dotomi.com:8080/pixel'; - const cnvrResponse = {ext: {psyncs: [syncurl_image], fsyncs: [syncurl_iframe]}}; + const cnvrResponse = { ext: { psyncs: [syncurl_image], fsyncs: [syncurl_iframe] } }; let sandbox; beforeEach(function () { sandbox = sinon.createSandbox(); @@ -706,43 +707,43 @@ describe('Conversant adapter tests', function() { it('empty params', function() { expect(spec.getUserSyncs({ iframeEnabled: true }, [], undefined, undefined)) .to.deep.equal([]); - expect(spec.getUserSyncs({ iframeEnabled: true }, [{body: {ext: {}}}], undefined, undefined)) + expect(spec.getUserSyncs({ iframeEnabled: true }, [{ body: { ext: {} } }], undefined, undefined)) .to.deep.equal([]); - expect(spec.getUserSyncs({ iframeEnabled: true }, [{body: cnvrResponse}], undefined, undefined)) + expect(spec.getUserSyncs({ iframeEnabled: true }, [{ body: cnvrResponse }], undefined, undefined)) .to.deep.equal([{ type: 'iframe', url: syncurl_iframe }]); - expect(spec.getUserSyncs({ pixelEnabled: true }, [{body: cnvrResponse}], undefined, undefined)) + expect(spec.getUserSyncs({ pixelEnabled: true }, [{ body: cnvrResponse }], undefined, undefined)) .to.deep.equal([{ type: 'image', url: syncurl_image }]); - expect(spec.getUserSyncs({ pixelEnabled: true, iframeEnabled: true }, [{body: cnvrResponse}], undefined, undefined)) - .to.deep.equal([{type: 'iframe', url: syncurl_iframe}, {type: 'image', url: syncurl_image}]); + expect(spec.getUserSyncs({ pixelEnabled: true, iframeEnabled: true }, [{ body: cnvrResponse }], undefined, undefined)) + .to.deep.equal([{ type: 'iframe', url: syncurl_iframe }, { type: 'image', url: syncurl_image }]); }); it('URL building', function() { - expect(spec.getUserSyncs({pixelEnabled: true}, [{body: {ext: {psyncs: [`${syncurl_image}?sid=1234`]}}}], undefined, undefined)) - .to.deep.equal([{type: 'image', url: `${syncurl_image}?sid=1234`}]); - expect(spec.getUserSyncs({pixelEnabled: true}, [{body: {ext: {psyncs: [`${syncurl_image}?sid=1234`]}}}], undefined, '1NYN')) - .to.deep.equal([{type: 'image', url: `${syncurl_image}?sid=1234&us_privacy=1NYN`}]); + expect(spec.getUserSyncs({ pixelEnabled: true }, [{ body: { ext: { psyncs: [`${syncurl_image}?sid=1234`] } } }], undefined, undefined)) + .to.deep.equal([{ type: 'image', url: `${syncurl_image}?sid=1234` }]); + expect(spec.getUserSyncs({ pixelEnabled: true }, [{ body: { ext: { psyncs: [`${syncurl_image}?sid=1234`] } } }], undefined, '1NYN')) + .to.deep.equal([{ type: 'image', url: `${syncurl_image}?sid=1234&us_privacy=1NYN` }]); }); it('GDPR', function() { - expect(spec.getUserSyncs({ iframeEnabled: true }, [{body: cnvrResponse}], {gdprApplies: true, consentString: 'consentstring'}, undefined)) + expect(spec.getUserSyncs({ iframeEnabled: true }, [{ body: cnvrResponse }], { gdprApplies: true, consentString: 'consentstring' }, undefined)) .to.deep.equal([{ type: 'iframe', url: `${syncurl_iframe}?gdpr=1&gdpr_consent=consentstring` }]); - expect(spec.getUserSyncs({ iframeEnabled: true }, [{body: cnvrResponse}], {gdprApplies: false, consentString: 'consentstring'}, undefined)) + expect(spec.getUserSyncs({ iframeEnabled: true }, [{ body: cnvrResponse }], { gdprApplies: false, consentString: 'consentstring' }, undefined)) .to.deep.equal([{ type: 'iframe', url: `${syncurl_iframe}?gdpr=0&gdpr_consent=consentstring` }]); - expect(spec.getUserSyncs({ iframeEnabled: true }, [{body: cnvrResponse}], {gdprApplies: true, consentString: undefined}, undefined)) + expect(spec.getUserSyncs({ iframeEnabled: true }, [{ body: cnvrResponse }], { gdprApplies: true, consentString: undefined }, undefined)) .to.deep.equal([{ type: 'iframe', url: `${syncurl_iframe}?gdpr=1&gdpr_consent=` }]); - expect(spec.getUserSyncs({ pixelEnabled: true }, [{body: cnvrResponse}], {gdprApplies: true, consentString: 'consentstring'}, undefined)) + expect(spec.getUserSyncs({ pixelEnabled: true }, [{ body: cnvrResponse }], { gdprApplies: true, consentString: 'consentstring' }, undefined)) .to.deep.equal([{ type: 'image', url: `${syncurl_image}?gdpr=1&gdpr_consent=consentstring` }]); - expect(spec.getUserSyncs({ pixelEnabled: true }, [{body: cnvrResponse}], {gdprApplies: false, consentString: 'consentstring'}, undefined)) + expect(spec.getUserSyncs({ pixelEnabled: true }, [{ body: cnvrResponse }], { gdprApplies: false, consentString: 'consentstring' }, undefined)) .to.deep.equal([{ type: 'image', url: `${syncurl_image}?gdpr=0&gdpr_consent=consentstring` }]); - expect(spec.getUserSyncs({ pixelEnabled: true }, [{body: cnvrResponse}], {gdprApplies: true, consentString: undefined}, undefined)) + expect(spec.getUserSyncs({ pixelEnabled: true }, [{ body: cnvrResponse }], { gdprApplies: true, consentString: undefined }, undefined)) .to.deep.equal([{ type: 'image', url: `${syncurl_image}?gdpr=1&gdpr_consent=` }]); }); it('US_Privacy', function() { - expect(spec.getUserSyncs({ iframeEnabled: true }, [{body: cnvrResponse}], undefined, '1NYN')) + expect(spec.getUserSyncs({ iframeEnabled: true }, [{ body: cnvrResponse }], undefined, '1NYN')) .to.deep.equal([{ type: 'iframe', url: `${syncurl_iframe}?us_privacy=1NYN` }]); - expect(spec.getUserSyncs({ pixelEnabled: true }, [{body: cnvrResponse}], undefined, '1NYN')) + expect(spec.getUserSyncs({ pixelEnabled: true }, [{ body: cnvrResponse }], undefined, '1NYN')) .to.deep.equal([{ type: 'image', url: `${syncurl_image}?us_privacy=1NYN` }]); }); }); diff --git a/test/spec/modules/copper6sspBidAdapter_spec.js b/test/spec/modules/copper6sspBidAdapter_spec.js index 3c32750cbc0..888291c5997 100644 --- a/test/spec/modules/copper6sspBidAdapter_spec.js +++ b/test/spec/modules/copper6sspBidAdapter_spec.js @@ -484,7 +484,7 @@ describe('Copper6SSPBidAdapter', function () { const syncData = spec.getUserSyncs({}, {}, { consentString: 'ALL', gdprApplies: true, - }, {}); + }, undefined); expect(syncData).to.be.an('array').which.is.not.empty; expect(syncData[0]).to.be.an('object') expect(syncData[0].type).to.be.a('string') @@ -493,9 +493,7 @@ describe('Copper6SSPBidAdapter', function () { expect(syncData[0].url).to.equal('https://сsync.copper6.com/image?pbjs=1&gdpr=1&gdpr_consent=ALL&coppa=0') }); it('Should return array of objects with proper sync config , include CCPA', function() { - const syncData = spec.getUserSyncs({}, {}, {}, { - consentString: '1---' - }); + const syncData = spec.getUserSyncs({}, {}, {}, '1---'); expect(syncData).to.be.an('array').which.is.not.empty; expect(syncData[0]).to.be.an('object') expect(syncData[0].type).to.be.a('string') @@ -504,7 +502,7 @@ describe('Copper6SSPBidAdapter', function () { expect(syncData[0].url).to.equal('https://сsync.copper6.com/image?pbjs=1&ccpa_consent=1---&coppa=0') }); it('Should return array of objects with proper sync config , include GPP', function() { - const syncData = spec.getUserSyncs({}, {}, {}, {}, { + const syncData = spec.getUserSyncs({}, {}, {}, undefined, { gppString: 'abc123', applicableSections: [8] }); diff --git a/test/spec/modules/craftBidAdapter_spec.js b/test/spec/modules/craftBidAdapter_spec.js index 45922e1cc25..963662653e3 100644 --- a/test/spec/modules/craftBidAdapter_spec.js +++ b/test/spec/modules/craftBidAdapter_spec.js @@ -1,8 +1,8 @@ -import {expect} from 'chai'; -import {spec} from 'modules/craftBidAdapter.js'; -import {newBidder} from 'src/adapters/bidderFactory.js'; -import {config} from 'src/config.js'; -import {getGlobal} from '../../../src/prebidGlobal.js'; +import { expect } from 'chai'; +import { spec } from 'modules/craftBidAdapter.js'; +import { newBidder } from 'src/adapters/bidderFactory.js'; +import { config } from 'src/config.js'; +import { getGlobal } from '../../../src/prebidGlobal.js'; describe('craftAdapter', function () { const adapter = newBidder(spec); @@ -101,8 +101,8 @@ describe('craftAdapter', function () { }, }, userIdAsEids: [ - {source: 'foobar1.com', uids: [{id: 'xxxxxxx', atype: 1}]}, - {source: 'foobar2.com', uids: [{id: 'yyyyyyy', atype: 1}]}, + { source: 'foobar1.com', uids: [{ id: 'xxxxxxx', atype: 1 }] }, + { source: 'foobar2.com', uids: [{ id: 'yyyyyyy', atype: 1 }] }, ], }]; const bidderRequest = { @@ -134,8 +134,8 @@ describe('craftAdapter', function () { }); expect(data.user).to.deep.equals({ eids: [ - {source: 'foobar1.com', uids: [{id: 'xxxxxxx', atype: 1}]}, - {source: 'foobar2.com', uids: [{id: 'yyyyyyy', atype: 1}]}, + { source: 'foobar1.com', uids: [{ id: 'xxxxxxx', atype: 1 }] }, + { source: 'foobar2.com', uids: [{ id: 'yyyyyyy', atype: 1 }] }, ] }); }); @@ -172,7 +172,7 @@ describe('craftAdapter', function () { }] }; it('should get correct bid response', function() { - const bids = spec.interpretResponse(serverResponse, {bidderRequest: bidderRequest}); + const bids = spec.interpretResponse(serverResponse, { bidderRequest: bidderRequest }); expect(bids).to.have.lengthOf(1); expect(bids[0]).to.deep.equals({ _adUnitCode: 'craft-prebid-example', diff --git a/test/spec/modules/criteoBidAdapter_spec.js b/test/spec/modules/criteoBidAdapter_spec.js index f6f6d31fe72..a4c2a02f0b2 100644 --- a/test/spec/modules/criteoBidAdapter_spec.js +++ b/test/spec/modules/criteoBidAdapter_spec.js @@ -1,18 +1,18 @@ -import {expect} from 'chai'; -import {spec, storage} from 'modules/criteoBidAdapter.js'; +import { expect } from 'chai'; +import { spec, storage } from 'modules/criteoBidAdapter.js'; import * as utils from 'src/utils.js'; import * as refererDetection from 'src/refererDetection.js'; import * as ajax from 'src/ajax.js'; -import {config} from '../../../src/config.js'; -import {BANNER, NATIVE, VIDEO} from '../../../src/mediaTypes.js'; -import {addFPDToBidderRequest} from '../../helpers/fpd.js'; +import { config } from '../../../src/config.js'; +import { BANNER, NATIVE, VIDEO } from '../../../src/mediaTypes.js'; +import { addFPDToBidderRequest } from '../../helpers/fpd.js'; import 'modules/userId/index.js'; import 'modules/consentManagementTcf.js'; import 'modules/consentManagementUsp.js'; import 'modules/consentManagementGpp.js'; -import {hook} from '../../../src/hook.js'; -import {getGlobal} from '../../../src/prebidGlobal.js'; +import { hook } from '../../../src/hook.js'; +import { getGlobal } from '../../../src/prebidGlobal.js'; describe('The Criteo bidding adapter', function () { let sandbox, ajaxStub, logWarnStub; @@ -80,7 +80,7 @@ describe('The Criteo bidding adapter', function () { const gppConsent = { gppString: 'gpp_string', - applicableSections: [ 1, 2 ] + applicableSections: [1, 2] }; const gdprConsent = { @@ -1182,7 +1182,7 @@ describe('The Criteo bidding adapter', function () { } }; - const ortbRequest = spec.buildRequests(bidRequests, await addFPDToBidderRequest({...bidderRequest, ortb2})).data; + const ortbRequest = spec.buildRequests(bidRequests, await addFPDToBidderRequest({ ...bidderRequest, ortb2 })).data; expect(ortbRequest.regs.ext.gpp).to.equal('gpp_consent_string'); expect(ortbRequest.regs.ext.gpp_sid).to.deep.equal([0, 1, 2]); }); @@ -1222,7 +1222,7 @@ describe('The Criteo bidding adapter', function () { } }; - const ortbRequest = spec.buildRequests(bidRequests, await addFPDToBidderRequest({...bidderRequest, ortb2})).data; + const ortbRequest = spec.buildRequests(bidRequests, await addFPDToBidderRequest({ ...bidderRequest, ortb2 })).data; expect(ortbRequest.regs.ext.dsa).to.deep.equal(dsa); }); @@ -1235,7 +1235,7 @@ describe('The Criteo bidding adapter', function () { bidder: 'criteo', ortb2: { source: { - ext: {schain: expectedSchain} + ext: { schain: expectedSchain } } }, adUnitCode: 'bid-123', @@ -1255,7 +1255,7 @@ describe('The Criteo bidding adapter', function () { ...bidderRequest, ortb2: { source: { - ext: {schain: expectedSchain} + ext: { schain: expectedSchain } } } }; @@ -1547,8 +1547,8 @@ describe('The Criteo bidding adapter', function () { segtax: 3 }, segment: [ - {'id': '1001'}, - {'id': '1002'} + { 'id': '1001' }, + { 'id': '1002' } ] }] }, @@ -1566,8 +1566,8 @@ describe('The Criteo bidding adapter', function () { segtax: 3 }, segment: [ - {'id': '1001'}, - {'id': '1002'} + { 'id': '1001' }, + { 'id': '1002' } ] }], ext: { @@ -1606,13 +1606,13 @@ describe('The Criteo bidding adapter', function () { user: userData }; - const ortbRequest = spec.buildRequests(bidRequests, await addFPDToBidderRequest({...bidderRequest, ortb2})).data; - expect(ortbRequest.user).to.deep.equal({...userData, ext: {...userData.ext, consent: 'consentDataString'}}); + const ortbRequest = spec.buildRequests(bidRequests, await addFPDToBidderRequest({ ...bidderRequest, ortb2 })).data; + expect(ortbRequest.user).to.deep.equal({ ...userData, ext: { ...userData.ext, consent: 'consentDataString' } }); expect(ortbRequest.site).to.deep.equal({ ...siteData, page: refererUrl, domain: 'criteo.com', - publisher: {...ortbRequest.site.publisher, domain: 'criteo.com'} + publisher: { ...ortbRequest.site.publisher, domain: 'criteo.com' } }); expect(ortbRequest.imp[0].ext.bidfloor).to.equal(0.75); expect(ortbRequest.imp[0].ext.data.someContextAttribute).to.equal('abc') @@ -1621,7 +1621,7 @@ describe('The Criteo bidding adapter', function () { it('should properly build a request when coppa flag is true', async function () { const bidRequests = []; const bidderRequest = {}; - config.setConfig({coppa: true}); + config.setConfig({ coppa: true }); const ortbRequest = spec.buildRequests(bidRequests, await addFPDToBidderRequest(bidderRequest)).data; expect(ortbRequest.regs.coppa).to.equal(1); }); @@ -1629,7 +1629,7 @@ describe('The Criteo bidding adapter', function () { it('should properly build a request when coppa flag is false', async function () { const bidRequests = []; const bidderRequest = {}; - config.setConfig({coppa: false}); + config.setConfig({ coppa: false }); const ortbRequest = spec.buildRequests(bidRequests, await addFPDToBidderRequest(bidderRequest)).data; expect(ortbRequest.regs.coppa).to.equal(0); }); @@ -1676,8 +1676,8 @@ describe('The Criteo bidding adapter', function () { const ortbRequest = spec.buildRequests(bidRequests, await addFPDToBidderRequest(bidderRequest)).data; expect(ortbRequest.imp[0].ext.floors).to.deep.equal({ 'banner': { - '300x250': {'currency': 'USD', 'floor': 1}, - '728x90': {'currency': 'USD', 'floor': 2} + '300x250': { 'currency': 'USD', 'floor': 1 }, + '728x90': { 'currency': 'USD', 'floor': 2 } } }); }); @@ -1703,8 +1703,8 @@ describe('The Criteo bidding adapter', function () { const ortbRequest = spec.buildRequests(bidRequests, await addFPDToBidderRequest(bidderRequest)).data; expect(ortbRequest.imp[0].ext.floors).to.deep.equal({ 'banner': { - '300x250': {'currency': 'EUR', 'floor': 1}, - '728x90': {'currency': 'EUR', 'floor': 1} + '300x250': { 'currency': 'EUR', 'floor': 1 }, + '728x90': { 'currency': 'EUR', 'floor': 1 } } }); }); @@ -1744,8 +1744,8 @@ describe('The Criteo bidding adapter', function () { const ortbRequest = spec.buildRequests(bidRequests, await addFPDToBidderRequest(bidderRequest)).data; expect(ortbRequest.imp[0].ext.floors).to.deep.equal({ 'video': { - '300x250': {'currency': 'USD', 'floor': 1}, - '728x90': {'currency': 'USD', 'floor': 2} + '300x250': { 'currency': 'USD', 'floor': 1 }, + '728x90': { 'currency': 'USD', 'floor': 2 } } }); }); @@ -1811,14 +1811,14 @@ describe('The Criteo bidding adapter', function () { expect(ortbRequest.imp[0].ext.data.someContextAttribute).to.deep.equal('abc'); expect(ortbRequest.imp[0].ext.floors).to.deep.equal({ 'banner': { - '300x250': {'currency': 'USD', 'floor': 1}, - '728x90': {'currency': 'USD', 'floor': 2} + '300x250': { 'currency': 'USD', 'floor': 1 }, + '728x90': { 'currency': 'USD', 'floor': 2 } }, 'video': { - '640x480': {'currency': 'EUR', 'floor': 3.2} + '640x480': { 'currency': 'EUR', 'floor': 3.2 } }, 'native': { - '*': {'currency': 'YEN', 'floor': 4.99} + '*': { 'currency': 'YEN', 'floor': 4.99 } } }); }); @@ -1872,70 +1872,6 @@ describe('The Criteo bidding adapter', function () { expect(ortbRequest.imp[0].ext?.rwdd).to.equal(0); }); - it('should properly build a request when FLEDGE is enabled', async function () { - const bidderRequest = { - paapi: { - enabled: true - } - }; - const bidRequests = [ - { - bidder: 'criteo', - adUnitCode: 'bid-123', - mediaTypes: { - banner: { - sizes: [[728, 90]] - } - }, - params: { - zoneId: 123 - }, - ortb2Imp: { - ext: { - igs: { - ae: 1 - } - } - } - }, - ]; - - const ortbRequest = spec.buildRequests(bidRequests, await addFPDToBidderRequest(bidderRequest)).data; - expect(ortbRequest.imp[0].ext.igs.ae).to.equal(1); - }); - - it('should properly build a request when FLEDGE is disabled', async function () { - const bidderRequest = { - paapi: { - enabled: false - }, - }; - const bidRequests = [ - { - bidder: 'criteo', - adUnitCode: 'bid-123', - mediaTypes: { - banner: { - sizes: [[728, 90]] - } - }, - params: { - zoneId: 123 - }, - ortb2Imp: { - ext: { - igs: { - ae: 1 - } - } - } - }, - ]; - - const ortbRequest = spec.buildRequests(bidRequests, await addFPDToBidderRequest(bidderRequest)).data; - expect(ortbRequest.imp[0].ext.igs?.ae).to.be.undefined; - }); - it('should properly transmit the pubid and slot uid if available', async function () { const bidderRequest = { ortb2: { @@ -2008,21 +1944,21 @@ describe('The Criteo bidding adapter', function () { }); it('should interpret correctly gzip configuration given as a string', async function() { - bidderConfigStub.returns({criteo: {gzipEnabled: 'false'}}); + bidderConfigStub.returns({ criteo: { gzipEnabled: 'false' } }); const request = spec.buildRequests(defaultBidRequests, await addFPDToBidderRequest(bidderRequest)); expect(request.options.endpointCompression).to.be.false; }); it('should interpret correctly gzip configuration given as a boolean', async function () { - bidderConfigStub.returns({criteo: {gzipEnabled: false}}); + bidderConfigStub.returns({ criteo: { gzipEnabled: false } }); const request = spec.buildRequests(defaultBidRequests, await addFPDToBidderRequest(bidderRequest)); expect(request.options.endpointCompression).to.be.false; }); it('should default to true when it receives an invalid configuration', async function () { - bidderConfigStub.returns({criteo: {gzipEnabled: 'randomString'}}); + bidderConfigStub.returns({ criteo: { gzipEnabled: 'randomString' } }); const request = spec.buildRequests(defaultBidRequests, await addFPDToBidderRequest(bidderRequest)); expect(request.options.endpointCompression).to.be.true; @@ -2178,9 +2114,9 @@ describe('The Criteo bidding adapter', function () { it('should return an empty array when parsing a well-formed no bid response', async function () { const bidRequests = []; - const response = {seatbid: []}; + const response = { seatbid: [] }; const request = spec.buildRequests(bidRequests, await addFPDToBidderRequest(bidderRequest)); - const bids = spec.interpretResponse({body: response}, request); + const bids = spec.interpretResponse({ body: response }, request); expect(bids).to.have.lengthOf(0); }); @@ -2199,7 +2135,7 @@ describe('The Criteo bidding adapter', function () { }]; const response = mockResponse('test-bidId', BANNER); const request = spec.buildRequests(bidRequests, await addFPDToBidderRequest(bidderRequest)); - const bids = spec.interpretResponse({body: response}, request); + const bids = spec.interpretResponse({ body: response }, request); expect(bids).to.have.lengthOf(1); expect(bids[0].mediaType).to.equal(BANNER); expect(bids[0].requestId).to.equal('test-bidId'); @@ -2237,7 +2173,7 @@ describe('The Criteo bidding adapter', function () { }]; const response = mockResponse('test-bidId', VIDEO); const request = spec.buildRequests(bidRequests, await addFPDToBidderRequest(bidderRequest)); - const bids = spec.interpretResponse({body: response}, request); + const bids = spec.interpretResponse({ body: response }, request); expect(bids).to.have.lengthOf(1); expect(bids[0].mediaType).to.equal(VIDEO); expect(bids[0].requestId).to.equal('test-bidId'); @@ -2273,7 +2209,7 @@ describe('The Criteo bidding adapter', function () { }]; const response = mockResponse('test-bidId', VIDEO); const request = spec.buildRequests(bidRequests, await addFPDToBidderRequest(bidderRequest)); - const bids = spec.interpretResponse({body: response}, request); + const bids = spec.interpretResponse({ body: response }, request); expect(bids).to.have.lengthOf(1); expect(bids[0].mediaType).to.equal(VIDEO); expect(bids[0].requestId).to.equal('test-bidId'); @@ -2302,7 +2238,7 @@ describe('The Criteo bidding adapter', function () { }]; const response = mockResponse('test-bidId', NATIVE); const request = spec.buildRequests(bidRequests, await addFPDToBidderRequest(bidderRequest)); - const bids = spec.interpretResponse({body: response}, request); + const bids = spec.interpretResponse({ body: response }, request); expect(bids).to.have.lengthOf(1); expect(bids[0].mediaType).to.equal(NATIVE); expect(bids[0].requestId).to.equal('test-bidId'); @@ -2353,7 +2289,7 @@ describe('The Criteo bidding adapter', function () { }]; const response = mockResponse('test-bidId2', BANNER); const request = spec.buildRequests(bidRequests, await addFPDToBidderRequest(bidderRequest)); - const bids = spec.interpretResponse({body: response}, request); + const bids = spec.interpretResponse({ body: response }, request); expect(bids).to.have.lengthOf(1); expect(bids[0].mediaType).to.equal(BANNER); expect(bids[0].requestId).to.equal('test-bidId2'); @@ -2402,7 +2338,7 @@ describe('The Criteo bidding adapter', function () { }]; const response = mockResponse('test-bidId', VIDEO); const request = spec.buildRequests(bidRequests, await addFPDToBidderRequest(bidderRequest)); - const bids = spec.interpretResponse({body: response}, request); + const bids = spec.interpretResponse({ body: response }, request); expect(bids).to.have.lengthOf(1); expect(bids[0].mediaType).to.equal(VIDEO); expect(bids[0].requestId).to.equal('test-bidId'); @@ -2442,7 +2378,7 @@ describe('The Criteo bidding adapter', function () { }]; const response = mockResponse('test-bidId', NATIVE); const request = spec.buildRequests(bidRequests, await addFPDToBidderRequest(bidderRequest)); - const bids = spec.interpretResponse({body: response}, request); + const bids = spec.interpretResponse({ body: response }, request); expect(bids).to.have.lengthOf(1); expect(bids[0].mediaType).to.equal(NATIVE); expect(bids[0].requestId).to.equal('test-bidId'); @@ -2462,154 +2398,6 @@ describe('The Criteo bidding adapter', function () { }); } - it('should properly parse a bid response with FLEDGE auction configs', async function () { - const auctionConfig1 = { - auctionSignals: {}, - decisionLogicUrl: 'https://grid-mercury.criteo.com/fledge/decision', - interestGroupBuyers: ['https://first-buyer-domain.com', 'https://second-buyer-domain.com'], - perBuyerSignals: { - 'https://first-buyer-domain.com': { - foo: 'bar', - }, - 'https://second-buyer-domain.com': { - foo: 'baz' - }, - }, - perBuyerTimeout: { - '*': 500, - 'buyer1': 100, - 'buyer2': 200 - }, - perBuyerGroupLimits: { - '*': 60, - 'buyer1': 300, - 'buyer2': 400 - }, - seller: 'https://seller-domain.com', - sellerTimeout: 500, - sellerSignals: { - foo: 'bar', - foo2: 'bar2', - floor: 1, - currency: 'USD', - perBuyerTimeout: { - 'buyer1': 100, - 'buyer2': 200 - }, - perBuyerGroupLimits: { - 'buyer1': 300, - 'buyer2': 400 - }, - }, - sellerCurrency: 'USD', - }; - const auctionConfig2 = { - auctionSignals: {}, - decisionLogicUrl: 'https://grid-mercury.criteo.com/fledge/decision', - interestGroupBuyers: ['https://first-buyer-domain.com', 'https://second-buyer-domain.com'], - perBuyerSignals: { - 'https://first-buyer-domain.com': { - foo: 'bar', - }, - 'https://second-buyer-domain.com': { - foo: 'baz' - }, - }, - perBuyerTimeout: { - '*': 500, - 'buyer1': 100, - 'buyer2': 200 - }, - perBuyerGroupLimits: { - '*': 60, - 'buyer1': 300, - 'buyer2': 400 - }, - seller: 'https://seller-domain.com', - sellerTimeout: 500, - sellerSignals: { - foo: 'bar', - floor: 1, - perBuyerTimeout: { - 'buyer1': 100, - 'buyer2': 200 - }, - perBuyerGroupLimits: { - 'buyer1': 300, - 'buyer2': 400 - }, - }, - sellerCurrency: '???' - }; - const bidRequests = [ - { - bidId: 'test-bidId', - bidder: 'criteo', - adUnitCode: 'bid-123', - transactionId: 'transaction-123', - mediaTypes: { - banner: { - sizes: [[728, 90]] - } - }, - params: { - bidFloor: 1, - bidFloorCur: 'EUR' - } - }, - { - bidId: 'test-bidId-2', - bidder: 'criteo', - adUnitCode: 'bid-123', - transactionId: 'transaction-123', - mediaTypes: { - banner: { - sizes: [[728, 90]] - } - }, - params: { - bidFloor: 1, - bidFloorCur: 'EUR' - } - }, - ]; - const response = { - ext: { - igi: [{ - impid: 'test-bidId', - igs: [{ - impid: 'test-bidId', - bidId: 'test-bidId', - config: auctionConfig1 - }] - }, { - impid: 'test-bidId-2', - igs: [{ - impid: 'test-bidId-2', - bidId: 'test-bidId-2', - config: auctionConfig2 - }] - }] - }, - }; - const request = spec.buildRequests(bidRequests, await addFPDToBidderRequest(bidderRequest)); - const interpretedResponse = spec.interpretResponse({body: response}, request); - expect(interpretedResponse).to.have.property('bids'); - expect(interpretedResponse).to.have.property('paapi'); - expect(interpretedResponse.bids).to.have.lengthOf(0); - expect(interpretedResponse.paapi).to.have.lengthOf(2); - expect(interpretedResponse.paapi[0]).to.deep.equal({ - bidId: 'test-bidId', - impid: 'test-bidId', - config: auctionConfig1, - }); - expect(interpretedResponse.paapi[1]).to.deep.equal({ - bidId: 'test-bidId-2', - impid: 'test-bidId-2', - config: auctionConfig2, - }); - }); - [{ hasBidResponseLevelPafData: true, hasBidResponseBidLevelPafData: true, @@ -2670,7 +2458,7 @@ describe('The Criteo bidding adapter', function () { }; const request = spec.buildRequests(bidRequests, await addFPDToBidderRequest(bidderRequest)); - const bids = spec.interpretResponse({body: response}, request); + const bids = spec.interpretResponse({ body: response }, request); expect(bids).to.have.lengthOf(1); @@ -2839,7 +2627,7 @@ describe('The Criteo bidding adapter', function () { ]; for (const nativeParams of nativeParamsWithoutSendId) { - let transformedBidRequests = {...bidRequests}; + let transformedBidRequests = { ...bidRequests }; transformedBidRequests = [Object.assign(transformedBidRequests[0], nativeParams), Object.assign(transformedBidRequests[1], nativeParams)]; spec.buildRequests(transformedBidRequests, await addFPDToBidderRequest(bidderRequest)); } diff --git a/test/spec/modules/criteoIdSystem_spec.js b/test/spec/modules/criteoIdSystem_spec.js index 1472a224a11..b82b4d56cb1 100644 --- a/test/spec/modules/criteoIdSystem_spec.js +++ b/test/spec/modules/criteoIdSystem_spec.js @@ -2,9 +2,9 @@ import { criteoIdSubmodule, storage } from 'modules/criteoIdSystem.js'; import * as utils from 'src/utils.js'; import { gdprDataHandler, uspDataHandler, gppDataHandler } from '../../../src/adapterManager.js'; import { server } from '../../mocks/xhr.js'; -import {attachIdSystem} from '../../../modules/userId/index.js'; -import {createEidsArray} from '../../../modules/userId/eids.js'; -import {expect} from 'chai/index.mjs'; +import { attachIdSystem } from '../../../modules/userId/index.js'; +import { createEidsArray } from '../../../modules/userId/eids.js'; +import { expect } from 'chai/index.mjs'; const pastDateString = new Date(0).toString() @@ -373,7 +373,7 @@ describe('CriteoId module', function () { expect(newEids.length).to.equal(1); expect(newEids[0]).to.deep.equal({ source: 'criteo.com', - uids: [{id: 'some-random-id-value', atype: 1}] + uids: [{ id: 'some-random-id-value', atype: 1 }] }); }); }) diff --git a/test/spec/modules/currency_spec.js b/test/spec/modules/currency_spec.js index 774e4617404..35b50eeb1bb 100644 --- a/test/spec/modules/currency_spec.js +++ b/test/spec/modules/currency_spec.js @@ -2,7 +2,7 @@ import { getCurrencyRates } from 'test/fixtures/fixtures.js'; -import {getGlobal} from 'src/prebidGlobal.js'; +import { getGlobal } from 'src/prebidGlobal.js'; import { setConfig, @@ -11,13 +11,13 @@ import { currencyRates, responseReady } from 'modules/currency.js'; -import {createBid} from '../../../src/bidfactory.js'; +import { createBid } from '../../../src/bidfactory.js'; import * as utils from 'src/utils.js'; -import {EVENTS, REJECTION_REASON} from '../../../src/constants.js'; -import {server} from '../../mocks/xhr.js'; +import { EVENTS, REJECTION_REASON } from '../../../src/constants.js'; +import { server } from '../../mocks/xhr.js'; import * as events from 'src/events.js'; import { enrichFPD } from '../../../src/fpd/enrichment.js'; -import {requestBidsHook} from '../../../modules/currency.js'; +import { requestBidsHook } from '../../../modules/currency.js'; var assert = require('chai').assert; var expect = require('chai').expect; @@ -90,7 +90,8 @@ describe('currency', function () { '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'); @@ -305,7 +306,7 @@ describe('currency', function () { describe('when rates fail to load', () => { let bid, addBidResponse, reject; beforeEach(() => { - bid = makeBid({cpm: 100, currency: 'JPY', bidder: 'rubicoin'}); + bid = makeBid({ cpm: 100, currency: 'JPY', bidder: 'rubicoin' }); addBidResponse = sinon.spy(); reject = sinon.spy(); }) @@ -389,7 +390,7 @@ describe('currency', function () { describe('currency.addBidResponseDecorator', function () { let reject; beforeEach(() => { - reject = sinon.stub().returns({status: 'rejected'}); + reject = sinon.stub().returns({ status: 'rejected' }); }); it('should leave bid at 1 when currency support is not enabled and fromCurrency is USD', function () { @@ -457,9 +458,9 @@ describe('currency', function () { it('should reject bid when rates have not loaded when the auction times out', () => { fakeCurrencyFileServer.respondWith(JSON.stringify(getCurrencyRates())); - setConfig({'adServerCurrency': 'JPY'}); - const bid = makeBid({cpm: 1, currency: 'USD', auctionId: 'aid'}); - const noConversionBid = makeBid({cpm: 1, currency: 'JPY', auctionId: 'aid'}); + setConfig({ 'adServerCurrency': 'JPY' }); + const bid = makeBid({ cpm: 1, currency: 'USD', auctionId: 'aid' }); + const noConversionBid = makeBid({ cpm: 1, currency: 'JPY', auctionId: 'aid' }); const reject = sinon.spy(); const addBidResponse = sinon.spy(); addBidResponseHook(addBidResponse, 'au', bid, reject); @@ -557,7 +558,7 @@ describe('currency', function () { }); it('should delay auction start when auctionDelay set in module config', () => { - setConfig({auctionDelay: 2000, adServerCurrency: 'USD'}); + setConfig({ auctionDelay: 2000, adServerCurrency: 'USD' }); const reqBidsConfigObj = { auctionId: '128937' }; @@ -567,7 +568,7 @@ describe('currency', function () { }); it('should start auction when auctionDelay time passed', () => { - setConfig({auctionDelay: 2000, adServerCurrency: 'USD'}); + setConfig({ auctionDelay: 2000, adServerCurrency: 'USD' }); const reqBidsConfigObj = { auctionId: '128937' }; @@ -578,7 +579,7 @@ describe('currency', function () { }); it('should run auction if rates were fetched before auctionDelay time', () => { - setConfig({auctionDelay: 3000, adServerCurrency: 'USD'}); + setConfig({ auctionDelay: 3000, adServerCurrency: 'USD' }); const reqBidsConfigObj = { auctionId: '128937' }; diff --git a/test/spec/modules/cwireBidAdapter_spec.js b/test/spec/modules/cwireBidAdapter_spec.js index 1a514d33155..6943618cf51 100644 --- a/test/spec/modules/cwireBidAdapter_spec.js +++ b/test/spec/modules/cwireBidAdapter_spec.js @@ -6,6 +6,7 @@ import * as utils from "src/utils.js"; import sinon, { stub } from "sinon"; import { config } from "../../../src/config.js"; import * as autoplayLib from "../../../libraries/autoplayDetection/autoplay.js"; +import * as adUnits from 'src/utils/adUnits'; describe("C-WIRE bid adapter", () => { config.setConfig({ debug: true }); @@ -101,8 +102,8 @@ describe("C-WIRE bid adapter", () => { describe("buildRequests reads adUnit offsetWidth and offsetHeight", function () { beforeEach(function () { - const documentStub = sandbox.stub(document, "getElementById"); - documentStub.withArgs(`${bidRequests[0].adUnitCode}`).returns({ + const documentStub = sandbox.stub(adUnits, "getAdUnitElement"); + documentStub.returns({ offsetWidth: 200, offsetHeight: 250, getBoundingClientRect() { @@ -115,11 +116,9 @@ describe("C-WIRE bid adapter", () => { const request = spec.buildRequests([bidRequest], bidderRequest); const payload = JSON.parse(request.data); - const el = document.getElementById(`${bidRequest.adUnitCode}`); logInfo(JSON.stringify(payload)); - expect(el).to.exist; expect(payload.slots[0].cwExt.dimensions.width).to.equal(200); expect(payload.slots[0].cwExt.dimensions.height).to.equal(250); expect(payload.slots[0].cwExt.style.maxHeight).to.not.exist; @@ -131,8 +130,8 @@ describe("C-WIRE bid adapter", () => { }); describe("buildRequests reads style attributes", function () { beforeEach(function () { - const documentStub = sandbox.stub(document, "getElementById"); - documentStub.withArgs(`${bidRequests[0].adUnitCode}`).returns({ + const documentStub = sandbox.stub(adUnits, "getAdUnitElement"); + documentStub.returns({ style: { maxWidth: "400px", maxHeight: "350px", @@ -147,11 +146,8 @@ describe("C-WIRE bid adapter", () => { const request = spec.buildRequests([bidRequest], bidderRequest); const payload = JSON.parse(request.data); - const el = document.getElementById(`${bidRequest.adUnitCode}`); - logInfo(JSON.stringify(payload)); - expect(el).to.exist; expect(payload.slots[0].cwExt.style.maxWidth).to.eq("400px"); expect(payload.slots[0].cwExt.style.maxHeight).to.eq("350px"); }); diff --git a/test/spec/modules/czechAdIdSystem_spec.js b/test/spec/modules/czechAdIdSystem_spec.js index 5ce3b7fbbac..430fb1f7a79 100644 --- a/test/spec/modules/czechAdIdSystem_spec.js +++ b/test/spec/modules/czechAdIdSystem_spec.js @@ -1,7 +1,7 @@ -import {czechAdIdSubmodule, storage} from 'modules/czechAdIdSystem.js'; -import {attachIdSystem} from '../../../modules/userId/index.js'; -import {createEidsArray} from '../../../modules/userId/eids.js'; -import {expect} from 'chai/index.mjs'; +import { czechAdIdSubmodule, storage } from 'modules/czechAdIdSystem.js'; +import { attachIdSystem } from '../../../modules/userId/index.js'; +import { createEidsArray } from '../../../modules/userId/eids.js'; +import { expect } from 'chai/index.mjs'; describe('czechAdId module', function () { let getCookieStub; @@ -21,7 +21,7 @@ describe('czechAdId module', function () { it('should return the uid when it exists in cookie', function () { getCookieStub.withArgs('czaid').returns('00000000-0000-4000-8000-000000000000'); const id = czechAdIdSubmodule.getId(); - expect(id).to.be.deep.equal({id: '00000000-0000-4000-8000-000000000000'}); + expect(id).to.be.deep.equal({ id: '00000000-0000-4000-8000-000000000000' }); }); cookieTestCasesForEmpty.forEach(testCase => it('should not return the uid when it doesnt exist in cookie', function () { @@ -35,7 +35,7 @@ describe('czechAdId module', function () { it('should return the uid when it exists in cookie', function () { getCookieStub.withArgs('czaid').returns('00000000-0000-4000-8000-000000000000'); const decoded = czechAdIdSubmodule.decode(); - expect(decoded).to.be.deep.equal({czechAdId: '00000000-0000-4000-8000-000000000000'}); + expect(decoded).to.be.deep.equal({ czechAdId: '00000000-0000-4000-8000-000000000000' }); }); }); describe('eid', () => { @@ -45,11 +45,11 @@ describe('czechAdId module', function () { it('czechAdId', () => { const id = 'some-random-id-value'; - const userId = {czechAdId: id}; + const userId = { czechAdId: id }; const [eid] = createEidsArray(userId); expect(eid).to.deep.equal({ source: 'czechadid.cz', - uids: [{id: 'some-random-id-value', atype: 1}] + uids: [{ id: 'some-random-id-value', atype: 1 }] }); }); }); diff --git a/test/spec/modules/dacIdSystem_spec.js b/test/spec/modules/dacIdSystem_spec.js index 0246e65a310..54a2059f3d1 100644 --- a/test/spec/modules/dacIdSystem_spec.js +++ b/test/spec/modules/dacIdSystem_spec.js @@ -82,22 +82,22 @@ describe('dacId module', function () { // valid oid, fuuid, no AoneId getCookieStub.withArgs(FUUID_COOKIE_NAME).returns(FUUID_DUMMY_VALUE); const callbackSpy = sinon.spy(); - const callback = dacIdSystemSubmodule.getId({params: {oid: configParamTestCase.params.oid[0]}}).callback; + const callback = dacIdSystemSubmodule.getId({ params: { oid: configParamTestCase.params.oid[0] } }).callback; callback(callbackSpy); const request = server.requests[0]; - request.respond(200, { 'Content-Type': 'application/json' }, JSON.stringify({'uid': AONEID_DUMMY_VALUE})); - expect(callbackSpy.lastCall.lastArg).to.deep.equal({fuuid: 'dacIdTest', uid: AONEID_DUMMY_VALUE}); + request.respond(200, { 'Content-Type': 'application/json' }, JSON.stringify({ 'uid': AONEID_DUMMY_VALUE })); + expect(callbackSpy.lastCall.lastArg).to.deep.equal({ fuuid: 'dacIdTest', uid: AONEID_DUMMY_VALUE }); }); cookieTestCasesForEmpty.forEach(testCase => it('should return undefined when AoneId not exists & API result is empty', function () { // valid oid, fuuid, no AoneId, API result empty getCookieStub.withArgs(FUUID_COOKIE_NAME).returns(FUUID_DUMMY_VALUE); const callbackSpy = sinon.spy(); - const callback = dacIdSystemSubmodule.getId({params: {oid: configParamTestCase.params.oid[0]}}).callback; + const callback = dacIdSystemSubmodule.getId({ params: { oid: configParamTestCase.params.oid[0] } }).callback; callback(callbackSpy); const request = server.requests[0]; - request.respond(200, { 'Content-Type': 'application/json' }, JSON.stringify({'uid': testCase})); - expect(callbackSpy.lastCall.lastArg).to.deep.equal({fuuid: 'dacIdTest', uid: undefined}); + request.respond(200, { 'Content-Type': 'application/json' }, JSON.stringify({ 'uid': testCase })); + expect(callbackSpy.lastCall.lastArg).to.deep.equal({ fuuid: 'dacIdTest', uid: undefined }); })); it('should return the fuuid & AoneId when they exist', function () { diff --git a/test/spec/modules/dataController_spec.js b/test/spec/modules/dataController_spec.js index 07e1c9c19f5..95beb722fa7 100644 --- a/test/spec/modules/dataController_spec.js +++ b/test/spec/modules/dataController_spec.js @@ -1,7 +1,7 @@ -import {expect} from 'chai'; -import {config} from 'src/config.js'; -import {filterBidData, init} from 'modules/dataControllerModule/index.js'; -import {startAuction} from 'src/prebid.js'; +import { expect } from 'chai'; +import { config } from 'src/config.js'; +import { filterBidData, init } from 'modules/dataControllerModule/index.js'; +import { startAuction } from 'src/prebid.js'; describe('data controller', function () { let spyFn; @@ -85,7 +85,7 @@ describe('data controller', function () { afterEach(function () { config.resetConfig(); - startAuction.getHooks({hook: filterBidData}).remove(); + startAuction.getHooks({ hook: filterBidData }).remove(); }); it('filterEIDwhenSDA for All SDA ', function () { diff --git a/test/spec/modules/datablocksBidAdapter_spec.js b/test/spec/modules/datablocksBidAdapter_spec.js index 9e2b311e5c6..0ff7b89e880 100644 --- a/test/spec/modules/datablocksBidAdapter_spec.js +++ b/test/spec/modules/datablocksBidAdapter_spec.js @@ -2,7 +2,7 @@ import { expect } from 'chai'; import { spec, BotClientTests } from '../../../modules/datablocksBidAdapter.js'; import { getStorageManager } from '../../../src/storageManager.js'; -import {deepClone} from '../../../src/utils.js'; +import { deepClone } from '../../../src/utils.js'; const bid = { bidId: '2dd581a2b6281d', @@ -381,7 +381,7 @@ describe('DatablocksAdapter', function() { describe('get / store syncs', function() { it('Should return true / array', function() { - expect(spec.store_syncs([{id: 1, uid: 'test'}])).to.be.true; + expect(spec.store_syncs([{ id: 1, uid: 'test' }])).to.be.true; expect(spec.get_syncs()).to.be.a('object'); }); }) @@ -425,7 +425,7 @@ describe('DatablocksAdapter', function() { describe('getUserSyncs', function() { it('Should return array of syncs', function() { - expect(spec.getUserSyncs({iframeEnabled: true, pixelEnabled: true}, [res_object], {gdprApplies: true, gdpr: 1, gdpr_consent: 'consent_string'}, {})).to.be.an('array'); + expect(spec.getUserSyncs({ iframeEnabled: true, pixelEnabled: true }, [res_object], { gdprApplies: true, gdpr: 1, gdpr_consent: 'consent_string' }, {})).to.be.an('array'); }); }); @@ -437,7 +437,7 @@ describe('DatablocksAdapter', function() { describe('onBidWon', function() { it('Should return undefined', function() { - const won_bid = {params: [{source_id: 1}], requestId: 1, adUnitCode: 'unit', auctionId: 1, size: '300x250', cpm: 10, adserverTargeting: {hb_pb: 10}, timeToRespond: 10, ttl: 10}; + const won_bid = { params: [{ source_id: 1 }], requestId: 1, adUnitCode: 'unit', auctionId: 1, size: '300x250', cpm: 10, adserverTargeting: { hb_pb: 10 }, timeToRespond: 10, ttl: 10 }; expect(spec.onBidWon(won_bid)).to.equal(undefined); }); }); diff --git a/test/spec/modules/datawrkzBidAdapter_spec.js b/test/spec/modules/datawrkzBidAdapter_spec.js index ad513c20b8b..c485889c48f 100644 --- a/test/spec/modules/datawrkzBidAdapter_spec.js +++ b/test/spec/modules/datawrkzBidAdapter_spec.js @@ -43,13 +43,13 @@ describe('datawrkzAdapterTests', function () { it('should return false when required site_id param not found', function () { const invalidBid = Object.assign({}, bid); - invalidBid.params = {'bidfloor': '1.0'} + invalidBid.params = { 'bidfloor': '1.0' } expect(spec.isBidRequestValid(invalidBid)).to.equal(false); }); it('should return false when adunit is adpod video', function () { const invalidBid = Object.assign({}, bid); - invalidBid.params = {'bidfloor': '1.0', 'site_id': SITE_ID}; + invalidBid.params = { 'bidfloor': '1.0', 'site_id': SITE_ID }; invalidBid.mediaTypes = { 'video': { 'context': 'adpod' @@ -67,12 +67,12 @@ describe('datawrkzAdapterTests', function () { 'bidderRequestId': '22edbae2733bf6', 'timeout': 3000, 'uspConsent': consentString, - 'gdprConsent': {'gdprApplies': true}, + 'gdprConsent': { 'gdprApplies': true }, }; const bannerBidRequests = [{ 'bidder': BIDDER_CODE, - 'params': {'site_id': SITE_ID, 'bidfloor': 1.00}, - 'mediaTypes': {'banner': {'sizes': [[300, 250], [300, 600]]}}, + 'params': { 'site_id': SITE_ID, 'bidfloor': 1.00 }, + 'mediaTypes': { 'banner': { 'sizes': [[300, 250], [300, 600]] } }, 'adUnitCode': 'adUnitCode', 'transactionId': 'transactionId', 'sizes': [[300, 250], [300, 600]], @@ -82,8 +82,8 @@ describe('datawrkzAdapterTests', function () { }]; const bannerBidRequestsSingleArraySlotAndDeals = [{ 'bidder': BIDDER_CODE, - 'params': {'site_id': SITE_ID, 'bidfloor': 1.00, 'deals': [{id: 'deal_1'}, {id: 'deal_2'}]}, - 'mediaTypes': {'banner': {}}, + 'params': { 'site_id': SITE_ID, 'bidfloor': 1.00, 'deals': [{ id: 'deal_1' }, { id: 'deal_2' }] }, + 'mediaTypes': { 'banner': {} }, 'adUnitCode': 'adUnitCode', 'transactionId': 'transactionId', 'sizes': [300, 250], @@ -93,15 +93,17 @@ describe('datawrkzAdapterTests', function () { }]; const nativeBidRequests = [{ 'bidder': BIDDER_CODE, - 'params': {'site_id': SITE_ID, 'bidfloor': 1.00}, - 'mediaTypes': {'native': { - 'title': {'required': true, 'len': 80}, - 'image': {'required': true, 'sizes': [[300, 250]]}, - 'icon': {'required': true, 'sizes': [[50, 50]]}, - 'sponsoredBy': {'required': true}, - 'cta': {'required': true}, - 'body': {'required': true, 'len': 100} - }}, + 'params': { 'site_id': SITE_ID, 'bidfloor': 1.00 }, + 'mediaTypes': { + 'native': { + 'title': { 'required': true, 'len': 80 }, + 'image': { 'required': true, 'sizes': [[300, 250]] }, + 'icon': { 'required': true, 'sizes': [[50, 50]] }, + 'sponsoredBy': { 'required': true }, + 'cta': { 'required': true }, + 'body': { 'required': true, 'len': 100 } + } + }, 'adUnitCode': 'adUnitCode', 'transactionId': 'transactionId', 'bidId': 'bidId', @@ -110,15 +112,17 @@ describe('datawrkzAdapterTests', function () { }]; const nativeBidRequestsSingleArraySlotAndDeals = [{ 'bidder': BIDDER_CODE, - 'params': {'site_id': SITE_ID, 'bidfloor': 1.00, 'deals': [{id: 'deal_1'}, {id: 'deal_2'}]}, - 'mediaTypes': {'native': { - 'title': {'len': 80}, - 'image': {'sizes': [300, 250]}, - 'icon': {'sizes': [50, 50]}, - 'sponsoredBy': {}, - 'cta': {}, - 'body': {'len': 100} - }}, + 'params': { 'site_id': SITE_ID, 'bidfloor': 1.00, 'deals': [{ id: 'deal_1' }, { id: 'deal_2' }] }, + 'mediaTypes': { + 'native': { + 'title': { 'len': 80 }, + 'image': { 'sizes': [300, 250] }, + 'icon': { 'sizes': [50, 50] }, + 'sponsoredBy': {}, + 'cta': {}, + 'body': { 'len': 100 } + } + }, 'adUnitCode': 'adUnitCode', 'transactionId': 'transactionId', 'bidId': 'bidId', @@ -127,8 +131,8 @@ describe('datawrkzAdapterTests', function () { }]; const instreamVideoBidRequests = [{ 'bidder': BIDDER_CODE, - 'params': {'site_id': SITE_ID, 'bidfloor': 1.00}, - 'mediaTypes': {'video': {'context': 'instream', 'playerSize': [[640, 480]]}}, + 'params': { 'site_id': SITE_ID, 'bidfloor': 1.00 }, + 'mediaTypes': { 'video': { 'context': 'instream', 'playerSize': [[640, 480]] } }, 'adUnitCode': 'adUnitCode', 'transactionId': 'transactionId', 'bidId': 'bidId', @@ -137,8 +141,8 @@ describe('datawrkzAdapterTests', function () { }]; const instreamVideoBidRequestsSingleArraySlotAndDeals = [{ 'bidder': BIDDER_CODE, - 'params': {'site_id': SITE_ID, 'bidfloor': 1.00, 'deals': [{id: 'deal_1'}, {id: 'deal_2'}]}, - 'mediaTypes': {'video': {'context': 'instream', 'playerSize': [640, 480]}}, + 'params': { 'site_id': SITE_ID, 'bidfloor': 1.00, 'deals': [{ id: 'deal_1' }, { id: 'deal_2' }] }, + 'mediaTypes': { 'video': { 'context': 'instream', 'playerSize': [640, 480] } }, 'adUnitCode': 'adUnitCode', 'transactionId': 'transactionId', 'bidId': 'bidId', @@ -147,8 +151,8 @@ describe('datawrkzAdapterTests', function () { }]; const outstreamVideoBidRequests = [{ 'bidder': BIDDER_CODE, - 'params': {'site_id': SITE_ID, 'bidfloor': 1.00}, - 'mediaTypes': {'video': {'context': 'outstream', 'playerSize': [[640, 480]], 'mimes': ['video/mp4', 'video/x-flv']}}, + 'params': { 'site_id': SITE_ID, 'bidfloor': 1.00 }, + 'mediaTypes': { 'video': { 'context': 'outstream', 'playerSize': [[640, 480]], 'mimes': ['video/mp4', 'video/x-flv'] } }, 'adUnitCode': 'adUnitCode', 'transactionId': 'transactionId', 'bidId': 'bidId', @@ -157,8 +161,8 @@ describe('datawrkzAdapterTests', function () { }]; const outstreamVideoBidRequestsSingleArraySlotAndDeals = [{ 'bidder': BIDDER_CODE, - 'params': {'site_id': SITE_ID, 'bidfloor': 1.00, 'deals': [{id: 'deal_1'}, {id: 'deal_2'}]}, - 'mediaTypes': {'video': {'context': 'outstream', 'playerSize': [640, 480], 'mimes': ['video/mp4', 'video/x-flv']}}, + 'params': { 'site_id': SITE_ID, 'bidfloor': 1.00, 'deals': [{ id: 'deal_1' }, { id: 'deal_2' }] }, + 'mediaTypes': { 'video': { 'context': 'outstream', 'playerSize': [640, 480], 'mimes': ['video/mp4', 'video/x-flv'] } }, 'adUnitCode': 'adUnitCode', 'transactionId': 'transactionId', 'bidId': 'bidId', @@ -167,7 +171,7 @@ describe('datawrkzAdapterTests', function () { }]; const bidRequestsWithNoMediaType = [{ 'bidder': BIDDER_CODE, - 'params': {'site_id': SITE_ID, 'bidfloor': 1.00}, + 'params': { 'site_id': SITE_ID, 'bidfloor': 1.00 }, 'adUnitCode': 'adUnitCode', 'transactionId': 'transactionId', 'bidId': 'bidId', @@ -186,7 +190,7 @@ describe('datawrkzAdapterTests', function () { }); it('invalid media type in bid request', function () { - bidRequestsWithNoMediaType[0].mediaTypes = {'test': {}}; + bidRequestsWithNoMediaType[0].mediaTypes = { 'test': {} }; const requests = spec.buildRequests(bidRequestsWithNoMediaType, bidderRequest); assert.lengthOf(requests, 0); }); @@ -212,11 +216,11 @@ describe('datawrkzAdapterTests', function () { expect(requests[0].method).to.equal('POST'); expect(requests[0].url).to.equal(FINAL_URL); expect(payload.imp).to.exist; - expect(payload).to.nested.include({'imp[0].banner.w': 300}); - expect(payload).to.nested.include({'imp[0].banner.h': 250}); - expect(payload).to.nested.include({'regs.ext.us_privacy': consentString}); - expect(payload).to.nested.include({'regs.ext.gdpr': '1'}); - expect(payload).to.nested.include({'regs.coppa': '1'}); + expect(payload).to.nested.include({ 'imp[0].banner.w': 300 }); + expect(payload).to.nested.include({ 'imp[0].banner.h': 250 }); + expect(payload).to.nested.include({ 'regs.ext.us_privacy': consentString }); + expect(payload).to.nested.include({ 'regs.ext.gdpr': '1' }); + expect(payload).to.nested.include({ 'regs.coppa': '1' }); expect(requests[0].bidRequest).to.exist; expect(requests[0].bidRequest.requestedMediaType).to.equal('banner'); }); @@ -227,10 +231,10 @@ describe('datawrkzAdapterTests', function () { expect(requests[0].method).to.equal('POST'); expect(requests[0].url).to.equal(FINAL_URL); expect(payload.imp).to.exist; - expect(payload).to.nested.include({'imp[0].banner.w': 300}); - expect(payload).to.nested.include({'imp[0].banner.h': 250}); - expect(payload).to.nested.include({'imp[0].pmp.deals[0].id': 'deal_1'}); - expect(payload).to.nested.include({'imp[0].pmp.deals[1].id': 'deal_2'}); + expect(payload).to.nested.include({ 'imp[0].banner.w': 300 }); + expect(payload).to.nested.include({ 'imp[0].banner.h': 250 }); + expect(payload).to.nested.include({ 'imp[0].pmp.deals[0].id': 'deal_1' }); + expect(payload).to.nested.include({ 'imp[0].pmp.deals[1].id': 'deal_2' }); expect(requests[0].bidRequest).to.exist; expect(requests[0].bidRequest.requestedMediaType).to.equal('banner'); }); @@ -243,9 +247,9 @@ describe('datawrkzAdapterTests', function () { expect(requests[0].method).to.equal('POST'); expect(requests[0].url).to.equal(FINAL_URL); expect(payload.imp[0].native.request).to.exist; - expect(payload).to.nested.include({'regs.ext.us_privacy': consentString}); - expect(payload).to.nested.include({'regs.ext.gdpr': '1'}); - expect(payload).to.nested.include({'regs.coppa': '1'}); + expect(payload).to.nested.include({ 'regs.ext.us_privacy': consentString }); + expect(payload).to.nested.include({ 'regs.ext.gdpr': '1' }); + expect(payload).to.nested.include({ 'regs.coppa': '1' }); expect(requests[0].bidRequest).to.exist; expect(requests[0].bidRequest.requestedMediaType).to.equal('native'); }); @@ -257,8 +261,8 @@ describe('datawrkzAdapterTests', function () { expect(requests[0].url).to.equal(FINAL_URL); expect(payload.imp).to.exist; expect(payload.imp[0].native.request).to.exist; - expect(payload).to.nested.include({'imp[0].pmp.deals[0].id': 'deal_1'}); - expect(payload).to.nested.include({'imp[0].pmp.deals[1].id': 'deal_2'}); + expect(payload).to.nested.include({ 'imp[0].pmp.deals[0].id': 'deal_1' }); + expect(payload).to.nested.include({ 'imp[0].pmp.deals[1].id': 'deal_2' }); expect(requests[0].bidRequest).to.exist; expect(requests[0].bidRequest.requestedMediaType).to.equal('native'); }); @@ -270,9 +274,9 @@ describe('datawrkzAdapterTests', function () { const payload = JSON.parse(requests[0].data); expect(requests[0].method).to.equal('POST'); expect(requests[0].url).to.equal(FINAL_URL); - expect(payload).to.nested.include({'regs.ext.us_privacy': consentString}); - expect(payload).to.nested.include({'regs.ext.gdpr': '1'}); - expect(payload).to.nested.include({'regs.coppa': '1'}); + expect(payload).to.nested.include({ 'regs.ext.us_privacy': consentString }); + expect(payload).to.nested.include({ 'regs.ext.gdpr': '1' }); + expect(payload).to.nested.include({ 'regs.coppa': '1' }); expect(requests[0].bidRequest).to.exist; expect(requests[0].bidRequest.requestedMediaType).to.equal('video'); }); @@ -293,11 +297,11 @@ describe('datawrkzAdapterTests', function () { const payload = JSON.parse(requests[0].data); expect(requests[0].method).to.equal('POST'); expect(requests[0].url).to.equal(FINAL_URL); - expect(payload).to.nested.include({'imp[0].video.w': 640}); - expect(payload).to.nested.include({'imp[0].video.h': 480}); - expect(payload).to.nested.include({'regs.ext.us_privacy': consentString}); - expect(payload).to.nested.include({'regs.ext.gdpr': '1'}); - expect(payload).to.nested.include({'regs.coppa': '1'}); + expect(payload).to.nested.include({ 'imp[0].video.w': 640 }); + expect(payload).to.nested.include({ 'imp[0].video.h': 480 }); + expect(payload).to.nested.include({ 'regs.ext.us_privacy': consentString }); + expect(payload).to.nested.include({ 'regs.ext.gdpr': '1' }); + expect(payload).to.nested.include({ 'regs.coppa': '1' }); expect(requests[0].bidRequest).to.exist; expect(requests[0].bidRequest.requestedMediaType).to.equal('video'); }); @@ -307,10 +311,10 @@ describe('datawrkzAdapterTests', function () { const payload = JSON.parse(requests[0].data); expect(requests[0].method).to.equal('POST'); expect(requests[0].url).to.equal(FINAL_URL); - expect(payload).to.nested.include({'imp[0].video.w': 640}); - expect(payload).to.nested.include({'imp[0].video.h': 480}); - expect(payload).to.nested.include({'imp[0].pmp.deals[0].id': 'deal_1'}); - expect(payload).to.nested.include({'imp[0].pmp.deals[1].id': 'deal_2'}); + expect(payload).to.nested.include({ 'imp[0].video.w': 640 }); + expect(payload).to.nested.include({ 'imp[0].video.h': 480 }); + expect(payload).to.nested.include({ 'imp[0].pmp.deals[0].id': 'deal_1' }); + expect(payload).to.nested.include({ 'imp[0].pmp.deals[1].id': 'deal_2' }); expect(requests[0].bidRequest).to.exist; expect(requests[0].bidRequest.requestedMediaType).to.equal('video'); }); @@ -319,8 +323,8 @@ describe('datawrkzAdapterTests', function () { describe('interpretResponse', function () { const bidRequest = { 'bidder': BIDDER_CODE, - 'params': {'site_id': SITE_ID, 'bidfloor': 1.00}, - 'mediaTypes': {'banner': {'sizes': [[300, 250], [300, 600]]}}, + 'params': { 'site_id': SITE_ID, 'bidfloor': 1.00 }, + 'mediaTypes': { 'banner': { 'sizes': [[300, 250], [300, 600]] } }, 'adUnitCode': 'adUnitCode', 'transactionId': 'transactionId', 'sizes': [[300, 250], [300, 600]], @@ -331,15 +335,17 @@ describe('datawrkzAdapterTests', function () { }; const nativeBidRequest = { 'bidder': BIDDER_CODE, - 'params': {'site_id': SITE_ID, 'bidfloor': 1.00}, - 'mediaTypes': {'native': { - 'title': {'required': true, 'len': 80}, - 'image': {'required': true, 'sizes': [300, 250]}, - 'icon': {'required': true, 'sizes': [50, 50]}, - 'sponsoredBy': {'required': true}, - 'cta': {'required': true}, - 'body': {'required': true} - }}, + 'params': { 'site_id': SITE_ID, 'bidfloor': 1.00 }, + 'mediaTypes': { + 'native': { + 'title': { 'required': true, 'len': 80 }, + 'image': { 'required': true, 'sizes': [300, 250] }, + 'icon': { 'required': true, 'sizes': [50, 50] }, + 'sponsoredBy': { 'required': true }, + 'cta': { 'required': true }, + 'body': { 'required': true } + } + }, 'adUnitCode': 'adUnitCode', 'transactionId': 'transactionId', 'bidId': 'bidId', @@ -347,18 +353,18 @@ describe('datawrkzAdapterTests', function () { 'auctionId': 'auctionId', 'requestedMediaType': 'native', 'assets': [ - {'id': 1, 'required': 1, 'title': {'len': 80}}, - {'id': 2, 'required': 1, 'img': {'type': 3, 'w': 300, 'h': 250}}, - {'id': 3, 'required': 1, 'img': {'type': 1, 'w': 50, 'h': 50}}, - {'id': 4, 'required': 1, 'data': {'type': 1}}, - {'id': 5, 'required': 1, 'data': {'type': 12}}, - {'id': 6, 'required': 1, 'data': {'type': 2, 'len': 100}} + { 'id': 1, 'required': 1, 'title': { 'len': 80 } }, + { 'id': 2, 'required': 1, 'img': { 'type': 3, 'w': 300, 'h': 250 } }, + { 'id': 3, 'required': 1, 'img': { 'type': 1, 'w': 50, 'h': 50 } }, + { 'id': 4, 'required': 1, 'data': { 'type': 1 } }, + { 'id': 5, 'required': 1, 'data': { 'type': 12 } }, + { 'id': 6, 'required': 1, 'data': { 'type': 2, 'len': 100 } } ] }; const instreamVideoBidRequest = { 'bidder': BIDDER_CODE, - 'params': {'site_id': SITE_ID, 'bidfloor': 1.00}, - 'mediaTypes': {'video': {'context': 'instream', 'playerSize': [640, 480]}}, + 'params': { 'site_id': SITE_ID, 'bidfloor': 1.00 }, + 'mediaTypes': { 'video': { 'context': 'instream', 'playerSize': [640, 480] } }, 'adUnitCode': 'adUnitCode', 'transactionId': 'transactionId', 'bidId': 'bidId', @@ -368,12 +374,14 @@ describe('datawrkzAdapterTests', function () { }; const outstreamVideoBidRequest = { 'bidder': BIDDER_CODE, - 'params': {'site_id': SITE_ID, + 'params': { + 'site_id': SITE_ID, 'bidfloor': 1.00, 'outstreamType': 'slider_top_left', 'outstreamConfig': - {'ad_unit_audio': 1, 'show_player_close_button_after': 5, 'hide_player_control': 0}}, - 'mediaTypes': {'video': {'context': 'outstream', 'playerSize': [640, 480]}}, + { 'ad_unit_audio': 1, 'show_player_close_button_after': 5, 'hide_player_control': 0 } + }, + 'mediaTypes': { 'video': { 'context': 'outstream', 'playerSize': [640, 480] } }, 'adUnitCode': 'adUnitCode', 'transactionId': 'transactionId', 'bidId': 'bidId', @@ -394,37 +402,37 @@ describe('datawrkzAdapterTests', function () { }); it('check if id missing in response', function () { - const serverResponse = {'body': {'seatbid': [{}]}, 'headers': {}}; + const serverResponse = { 'body': { 'seatbid': [{}] }, 'headers': {} }; const result = spec.interpretResponse(serverResponse, request); expect(result).to.deep.equal([]); }); it('check if seatbid present in response', function () { - const serverResponse = {'body': {'id': 'id'}, 'headers': {}}; + const serverResponse = { 'body': { 'id': 'id' }, 'headers': {} }; const result = spec.interpretResponse(serverResponse, request); expect(result).to.deep.equal([]); }); it('check empty array response seatbid', function () { - const serverResponse = {'body': {'id': 'id', 'seatbid': []}, 'headers': {}}; + const serverResponse = { 'body': { 'id': 'id', 'seatbid': [] }, 'headers': {} }; const result = spec.interpretResponse(serverResponse, request); expect(result).to.deep.equal([]); }); it('check bid present in seatbid', function () { - const serverResponse = {'body': {'id': 'id', 'seatbid': [{}]}, 'headers': {}}; + const serverResponse = { 'body': { 'id': 'id', 'seatbid': [{}] }, 'headers': {} }; const result = spec.interpretResponse(serverResponse, request); expect(result).to.have.lengthOf(0); }); it('check empty array bid in seatbid', function () { - const serverResponse = {'body': {'id': 'id', 'seatbid': [{'bid': []}]}, 'headers': {}}; + const serverResponse = { 'body': { 'id': 'id', 'seatbid': [{ 'bid': [] }] }, 'headers': {} }; const result = spec.interpretResponse(serverResponse, request); expect(result).to.have.lengthOf(0); }); it('banner response missing bid price', function () { - const serverResponse = {'body': {'id': 'id', 'seatbid': [{'bid': [{'id': 1}]}]}, 'headers': {}}; + const serverResponse = { 'body': { 'id': 'id', 'seatbid': [{ 'bid': [{ 'id': 1 }] }] }, 'headers': {} }; const result = spec.interpretResponse(serverResponse, request); expect(result).to.have.lengthOf(1); expect(result[0].requestId).to.equal('bidId'); diff --git a/test/spec/modules/debugging_mod_spec.js b/test/spec/modules/debugging_mod_spec.js index 4989eb7c2e3..ee8089b7a10 100644 --- a/test/spec/modules/debugging_mod_spec.js +++ b/test/spec/modules/debugging_mod_spec.js @@ -1,5 +1,5 @@ -import {expect} from 'chai'; -import {makebidInterceptor} from '../../../modules/debugging/bidInterceptor.js'; +import { expect } from 'chai'; +import { makebidInterceptor } from '../../../modules/debugging/bidInterceptor.js'; import { makeBidderBidInterceptor, disableDebugging, @@ -7,9 +7,9 @@ import { sessionLoader, } from '../../../modules/debugging/debugging.js'; import '../../../modules/debugging/index.js'; -import {makePbsInterceptor} from '../../../modules/debugging/pbsInterceptor.js'; -import {config} from '../../../src/config.js'; -import {hook} from '../../../src/hook.js'; +import { makePbsInterceptor } from '../../../modules/debugging/pbsInterceptor.js'; +import { config } from '../../../src/config.js'; +import { hook } from '../../../src/hook.js'; import { addBidderRequestsBound, addBidderRequestsHook, @@ -17,18 +17,18 @@ import { addBidResponseHook, } from '../../../modules/debugging/legacy.js'; import * as utils from '../../../src/utils.js'; -import {addBidderRequests, addBidResponse} from '../../../src/auction.js'; -import {prefixLog} from '../../../src/utils.js'; -import {createBid} from '../../../src/bidfactory.js'; -import {VIDEO, BANNER, NATIVE} from '../../../src/mediaTypes.js'; -import {Renderer} from '../../../src/Renderer.js'; +import { addBidderRequests, addBidResponse } from '../../../src/auction.js'; +import { prefixLog } from '../../../src/utils.js'; +import { createBid } from '../../../src/bidfactory.js'; +import { VIDEO, BANNER, NATIVE } from '../../../src/mediaTypes.js'; +import { Renderer } from '../../../src/Renderer.js'; describe('bid interceptor', () => { let interceptor, mockSetTimeout; beforeEach(() => { mockSetTimeout = sinon.stub().callsFake((fn) => fn()); - const BidInterceptor = makebidInterceptor({utils, VIDEO, BANNER, NATIVE, Renderer}) - interceptor = new BidInterceptor({setTimeout: mockSetTimeout, logger: prefixLog('TEST')}); + const BidInterceptor = makebidInterceptor({ utils, VIDEO, BANNER, NATIVE, Renderer }) + interceptor = new BidInterceptor({ setTimeout: mockSetTimeout, logger: prefixLog('TEST') }); }); function setRules(...rules) { @@ -48,8 +48,8 @@ describe('bid interceptor', () => { set: new Set(), }).forEach(([test, arg]) => { it(`should filter out ${test}`, () => { - const valid = [{key1: 'value'}, {key2: 'value'}]; - const ser = interceptor.serializeConfig([...valid, {outer: {inner: arg}}]); + const valid = [{ key1: 'value' }, { key2: 'value' }]; + const ser = interceptor.serializeConfig([...valid, { outer: { inner: arg } }]); expect(ser).to.eql(valid); }); }); @@ -57,33 +57,33 @@ describe('bid interceptor', () => { describe('match()', () => { Object.entries({ - value: {key: 'value'}, - regex: {key: /^value$/}, + value: { key: 'value' }, + regex: { key: /^value$/ }, 'function': (o) => o.key === 'value' }).forEach(([test, matcher]) => { describe(`by ${test}`, () => { it('should work on matching top-level properties', () => { - setRules({when: matcher}); - const rule = interceptor.match({key: 'value'}); + setRules({ when: matcher }); + const rule = interceptor.match({ key: 'value' }); expect(rule).to.not.eql(null); }); it('should work on matching nested properties', () => { - setRules({when: {outer: {inner: matcher}}}); - const rule = interceptor.match({outer: {inner: {key: 'value'}}}); + setRules({ when: { outer: { inner: matcher } } }); + const rule = interceptor.match({ outer: { inner: { key: 'value' } } }); expect(rule).to.not.eql(null); }); it('should not work on non-matching inputs', () => { - setRules({when: matcher}); - expect(interceptor.match({key: 'different-value'})).to.not.be.ok; - expect(interceptor.match({differentKey: 'value'})).to.not.be.ok; + setRules({ when: matcher }); + expect(interceptor.match({ key: 'different-value' })).to.not.be.ok; + expect(interceptor.match({ differentKey: 'value' })).to.not.be.ok; }); }); }); it('should respect rule order', () => { - setRules({when: {key: 'value'}}, {when: {}}, {when: {}}); + setRules({ when: { key: 'value' } }, { when: {} }, { when: {} }); const rule = interceptor.match({}); expect(rule.no).to.equal(2); }); @@ -91,11 +91,11 @@ describe('bid interceptor', () => { it('should pass extra arguments to property function matchers', () => { const matchDef = { key: sinon.stub(), - outer: {inner: {key: sinon.stub()}} + outer: { inner: { key: sinon.stub() } } }; const extraArgs = [{}, {}]; - setRules({when: matchDef}); - interceptor.match({key: {}, outer: {inner: {key: {}}}}, ...extraArgs); + setRules({ when: matchDef }); + interceptor.match({ key: {}, outer: { inner: { key: {} } } }, ...extraArgs); [matchDef.key, matchDef.outer.inner.key].forEach((fn) => { expect(fn.calledOnceWith(sinon.match.any, ...extraArgs.map(sinon.match.same))).to.be.true; }); @@ -103,7 +103,7 @@ describe('bid interceptor', () => { it('should pass extra arguments to single-function matcher', () => { const matchDef = sinon.stub(); - setRules({when: matchDef}); + setRules({ when: matchDef }); const args = [{}, {}, {}]; interceptor.match(...args); expect(matchDef.calledOnceWith(...args.map(sinon.match.same))).to.be.true; @@ -111,8 +111,8 @@ describe('bid interceptor', () => { }); describe('rule', () => { - function matchingRule({replace, options, paapi}) { - setRules({when: {}, then: replace, options: options, paapi}); + function matchingRule({ replace, options }) { + setRules({ when: {}, then: replace, options: options }); return interceptor.match({}); } @@ -127,27 +127,27 @@ describe('bid interceptor', () => { }); Object.entries({ - value: {key: 'value'}, - 'function': () => ({key: 'value'}) + value: { key: 'value' }, + 'function': () => ({ key: 'value' }) }).forEach(([test, replDef]) => { describe(`by ${test}`, () => { it('should merge top-level properties with replace definition', () => { - const result = matchingRule({replace: replDef}).replace({}); + const result = matchingRule({ replace: replDef }).replace({}); expect(result).to.include.keys(REQUIRED_KEYS); expect(result.key).to.equal('value'); }); it('should merge nested properties with replace definition', () => { - const result = matchingRule({replace: {outer: {inner: replDef}}}).replace({}); + const result = matchingRule({ replace: { outer: { inner: replDef } } }).replace({}); expect(result).to.include.keys(REQUIRED_KEYS); - expect(result.outer.inner).to.eql({key: 'value'}); + expect(result.outer.inner).to.eql({ key: 'value' }); }); it('should respect array vs object definitions', () => { - const result = matchingRule({replace: {item: [replDef]}}).replace({}); + const result = matchingRule({ replace: { item: [replDef] } }).replace({}); expect(result.item).to.be.an('array'); expect(result.item.length).to.equal(1); - expect(result.item[0]).to.eql({key: 'value'}); + expect(result.item[0]).to.eql({ key: 'value' }); }); }); }); @@ -155,100 +155,57 @@ describe('bid interceptor', () => { it('should pass extra arguments to single function replacer', () => { const replDef = sinon.stub(); const args = [{}, {}, {}]; - matchingRule({replace: replDef}).replace(...args); + matchingRule({ replace: replDef }).replace(...args); expect(replDef.calledOnceWith(...args.map(sinon.match.same))).to.be.true; }); it('should pass extra arguments to function property replacers', () => { const replDef = { key: sinon.stub(), - outer: {inner: {key: sinon.stub()}} + outer: { inner: { key: sinon.stub() } } }; const args = [{}, {}, {}]; - matchingRule({replace: replDef}).replace(...args); + matchingRule({ replace: replDef }).replace(...args); [replDef.key, replDef.outer.inner.key].forEach((repl) => { expect(repl.calledOnceWith(...args.map(sinon.match.same))).to.be.true; }); }); }); - describe('paapi', () => { - it('should accept literals', () => { - const mockConfig = [ - {config: {paapi: 1}}, - {config: {paapi: 2}} - ] - const paapi = matchingRule({paapi: mockConfig}).paapi({}); - expect(paapi).to.eql(mockConfig); - }); - - it('should accept a function and pass extra args to it', () => { - const paapiDef = sinon.stub(); - const args = [{}, {}, {}]; - matchingRule({paapi: paapiDef}).paapi(...args); - expect(paapiDef.calledOnceWith(...args.map(sinon.match.same))).to.be.true; - }); - - Object.entries({ - 'literal': (cfg) => [cfg], - 'function': (cfg) => () => [cfg] - }).forEach(([t, makeConfigs]) => { - describe(`when paapi is defined as a ${t}`, () => { - it('should wrap top-level configs in "config"', () => { - const cfg = {decisionLogicURL: 'example'}; - expect(matchingRule({paapi: makeConfigs(cfg)}).paapi({})).to.eql([{ - config: cfg - }]) - }); - - Object.entries({ - 'config': {config: 1}, - 'igb': {igb: 1}, - 'config and igb': {config: 1, igb: 2} - }).forEach(([t, cfg]) => { - it(`should not wrap configs that define top-level ${t}`, () => { - expect(matchingRule({paapi: makeConfigs(cfg)}).paapi({})).to.eql([cfg]); - }) - }) - }) - }) - }) - describe('.options', () => { it('should include default rule options', () => { - const optDef = {someOption: 'value'}; - const ruleOptions = matchingRule({options: optDef}).options; + const optDef = { someOption: 'value' }; + const ruleOptions = matchingRule({ options: optDef }).options; expect(ruleOptions).to.include(optDef); expect(ruleOptions).to.include(interceptor.DEFAULT_RULE_OPTIONS); }); it('should override defaults', () => { - const optDef = {delay: 123}; - const ruleOptions = matchingRule({options: optDef}).options; + const optDef = { delay: 123 }; + const ruleOptions = matchingRule({ options: optDef }).options; expect(ruleOptions).to.eql(optDef); }); }); }); describe('intercept()', () => { - let done, addBid, addPaapiConfig; + let done, addBid; function intercept(args = {}) { - const bidRequest = {bids: args.bids || []}; - return interceptor.intercept(Object.assign({bidRequest, done, addBid, addPaapiConfig}, args)); + const bidRequest = { bids: args.bids || [] }; + return interceptor.intercept(Object.assign({ bidRequest, done, addBid }, args)); } beforeEach(() => { done = sinon.spy(); addBid = sinon.spy(); - addPaapiConfig = sinon.spy(); }); describe('on no match', () => { it('should return untouched bids and bidRequest', () => { const bids = [{}, {}]; const bidRequest = {}; - const result = intercept({bids, bidRequest}); + const result = intercept({ bids, bidRequest }); expect(result.bids).to.equal(bids); expect(result.bidRequest).to.equal(bidRequest); }); @@ -271,69 +228,55 @@ describe('bid interceptor', () => { const DELAY_2 = 321; const REQUEST = { bids: [ - {id: 1, match: false}, - {id: 2, match: 1}, - {id: 3, match: 2} + { id: 1, match: false }, + { id: 2, match: 1 }, + { id: 3, match: 2 } ] }; beforeEach(() => { match1 = sinon.stub().callsFake((bid) => bid.match === 1); match2 = sinon.stub().callsFake((bid) => bid.match === 2); - repl1 = sinon.stub().returns({replace: 1}); - repl2 = sinon.stub().returns({replace: 2}); + repl1 = sinon.stub().returns({ replace: 1 }); + repl2 = sinon.stub().returns({ replace: 2 }); setRules( - {when: match1, then: repl1, options: {delay: DELAY_1}}, - {when: match2, then: repl2, options: {delay: DELAY_2}}, + { when: match1, then: repl1, options: { delay: DELAY_1 } }, + { when: match2, then: repl2, options: { delay: DELAY_2 } }, ); }); it('should return only non-matching bids', () => { - const {bids, bidRequest} = intercept({bidRequest: REQUEST}); + const { bids, bidRequest } = intercept({ bidRequest: REQUEST }); expect(bids).to.eql([REQUEST.bids[0]]); expect(bidRequest.bids).to.eql([REQUEST.bids[0]]); }); it('should call addBid for each matching bid', () => { - intercept({bidRequest: REQUEST}); + intercept({ bidRequest: REQUEST }); expect(addBid.callCount).to.equal(2); - expect(addBid.calledWith(sinon.match({replace: 1, isDebug: true}), REQUEST.bids[1])).to.be.true; - expect(addBid.calledWith(sinon.match({replace: 2, isDebug: true}), REQUEST.bids[2])).to.be.true; + expect(addBid.calledWith(sinon.match({ replace: 1, isDebug: true }), REQUEST.bids[1])).to.be.true; + expect(addBid.calledWith(sinon.match({ replace: 2, isDebug: true }), REQUEST.bids[2])).to.be.true; [DELAY_1, DELAY_2].forEach((delay) => { expect(mockSetTimeout.calledWith(sinon.match.any, delay)).to.be.true; }); }); - it('should call addPaapiConfigs when provided', () => { - const mockPaapiConfigs = [ - {config: {paapi: 1}}, - {config: {paapi: 2}} - ] - setRules({ - when: {id: 2}, - paapi: mockPaapiConfigs, - }); - intercept({bidRequest: REQUEST}); - expect(addPaapiConfig.callCount).to.eql(2); - mockPaapiConfigs.forEach(cfg => sinon.assert.calledWith(addPaapiConfig, cfg)) - }) - it('should not call onBid when then is null', () => { setRules({ - when: {id: 2}, + when: { id: 2 }, then: null }); - intercept({bidRequest: REQUEST}); + intercept({ bidRequest: REQUEST }); sinon.assert.notCalled(addBid); }) it('should call done()', () => { - intercept({bidRequest: REQUEST}); + intercept({ bidRequest: REQUEST }); expect(done.calledOnce).to.be.true; }); it('should pass bid and bidRequest to match and replace functions', () => { - intercept({bidRequest: REQUEST}); + intercept({ bidRequest: REQUEST }); Object.entries({ 1: [match1, repl1], 2: [match2, repl2] @@ -351,20 +294,20 @@ describe('Debugging config', () => { it('should behave gracefully when sessionStorage throws', () => { const logError = sinon.stub(); const getStorage = () => { throw new Error() }; - getConfig({enabled: false}, {getStorage, logger: {logError}, hook, utils}); + getConfig({ enabled: false }, { getStorage, logger: { logError }, hook, utils }); expect(logError.called).to.be.true; }); }); describe('bidderBidInterceptor', () => { - let next, interceptBids, onCompletion, interceptResult, done, addBid, wrapCallback, addPaapiConfig, wrapped, bidderBidInterceptor; + let next, interceptBids, onCompletion, interceptResult, done, addBid, wrapCallback, wrapped, bidderBidInterceptor; - function interceptorArgs({spec = {}, bids = [], bidRequest = {}, ajax = {}, cbs = {}} = {}) { - return [next, interceptBids, spec, bids, bidRequest, ajax, wrapCallback, Object.assign({onCompletion}, cbs)]; + function interceptorArgs({ spec = {}, bids = [], bidRequest = {}, ajax = {}, cbs = {} } = {}) { + return [next, interceptBids, spec, bids, bidRequest, ajax, wrapCallback, Object.assign({ onCompletion }, cbs)]; } beforeEach(() => { - bidderBidInterceptor = makeBidderBidInterceptor({utils}); + bidderBidInterceptor = makeBidderBidInterceptor({ utils }); next = sinon.spy(); wrapped = false; wrapCallback = sinon.stub().callsFake(cb => { @@ -381,18 +324,17 @@ describe('bidderBidInterceptor', () => { interceptBids = sinon.stub().callsFake((opts) => { done = opts.done; addBid = opts.addBid; - addPaapiConfig = opts.addPaapiConfig; return interceptResult; }); onCompletion = sinon.spy(); - interceptResult = {bids: [], bidRequest: {}}; + interceptResult = { bids: [], bidRequest: {} }; }); it('should pass to interceptBid an addBid that triggers onBid', () => { const onBid = sinon.stub().callsFake(() => { expect(wrapped).to.be.true; }); - bidderBidInterceptor(...interceptorArgs({cbs: {onBid}})); + bidderBidInterceptor(...interceptorArgs({ cbs: { onBid } })); const bid = { bidder: 'bidder' }; @@ -400,15 +342,6 @@ describe('bidderBidInterceptor', () => { expect(onBid.calledWith(sinon.match.same(bid))).to.be.true; }); - it('should pass addPaapiConfig that triggers onPaapi', () => { - const onPaapi = sinon.stub().callsFake(() => { - expect(wrapped).to.be.true; - }); - bidderBidInterceptor(...interceptorArgs({cbs: {onPaapi}})); - addPaapiConfig({paapi: 'config'}, {bidId: 'bidId'}); - sinon.assert.calledWith(onPaapi, {paapi: 'config', bidId: 'bidId'}) - }) - describe('with no remaining bids', () => { it('should pass a done callback that triggers onCompletion', () => { bidderBidInterceptor(...interceptorArgs()); @@ -419,7 +352,7 @@ describe('bidderBidInterceptor', () => { it('should call onResponse', () => { const onResponse = sinon.stub(); - bidderBidInterceptor(...interceptorArgs({cbs: {onResponse}})); + bidderBidInterceptor(...interceptorArgs({ cbs: { onResponse } })); sinon.assert.called(onResponse); }) @@ -430,9 +363,9 @@ describe('bidderBidInterceptor', () => { }); describe('with remaining bids', () => { - const REMAINING_BIDS = [{id: 1}, {id: 2}]; + const REMAINING_BIDS = [{ id: 1 }, { id: 2 }]; beforeEach(() => { - interceptResult = {bids: REMAINING_BIDS, bidRequest: {bids: REMAINING_BIDS}}; + interceptResult = { bids: REMAINING_BIDS, bidRequest: { bids: REMAINING_BIDS } }; }); it('should call next', () => { @@ -441,7 +374,7 @@ describe('bidderBidInterceptor', () => { onRequest: {}, onBid: {} }; - const args = interceptorArgs({cbs: callbacks}); + const args = interceptorArgs({ cbs: callbacks }); const expectedNextArgs = [ args[2], interceptResult.bids, @@ -469,7 +402,7 @@ describe('bidderBidInterceptor', () => { }); describe('pbsBidInterceptor', () => { - const EMPTY_INT_RES = {bids: [], bidRequest: {bids: []}}; + const EMPTY_INT_RES = { bids: [], bidRequest: { bids: [] } }; let next, interceptBids, s2sBidRequest, bidRequests, ajax, onResponse, onError, onBid, interceptResults, addBids, dones, reqIdx; @@ -487,22 +420,22 @@ describe('pbsBidInterceptor', () => { return interceptResults[reqIdx++]; }); s2sBidRequest = {}; - bidRequests = [{bids: []}, {bids: []}]; + bidRequests = [{ bids: [] }, { bids: [] }]; interceptResults = [EMPTY_INT_RES, EMPTY_INT_RES]; }); - const pbsBidInterceptor = makePbsInterceptor({createBid, utils}); + const pbsBidInterceptor = makePbsInterceptor({ createBid, utils }); function callInterceptor() { - return pbsBidInterceptor(next, interceptBids, s2sBidRequest, bidRequests, ajax, {onResponse, onError, onBid}); + return pbsBidInterceptor(next, interceptBids, s2sBidRequest, bidRequests, ajax, { onResponse, onError, onBid }); } it('passes addBids that trigger onBid', () => { callInterceptor(); bidRequests.forEach((_, i) => { - const bid = {adUnitCode: i, prop: i}; - const bidRequest = {req: i}; + const bid = { adUnitCode: i, prop: i }; + const bidRequest = { req: i }; addBids[i](bid, bidRequest); - expect(onBid.calledWith({adUnit: i, bid: sinon.match(bid)})); + expect(onBid.calledWith({ adUnit: i, bid: sinon.match(bid) })); }); }); @@ -527,21 +460,21 @@ describe('pbsBidInterceptor', () => { let matchingBids; beforeEach(() => { matchingBids = [ - [{bidId: 1, matching: true}, {bidId: 2, matching: true}], + [{ bidId: 1, matching: true }, { bidId: 2, matching: true }], [], - [{bidId: 3, matching: true}] + [{ bidId: 3, matching: true }] ]; - interceptResults = matchingBids.map((bids) => ({bids, bidRequest: {bids}})); + interceptResults = matchingBids.map((bids) => ({ bids, bidRequest: { bids } })); s2sBidRequest = { ad_units: [ - {bids: [{bid_id: 1, matching: true}, {bid_id: 3, matching: true}, {bid_id: 100}, {bid_id: 101}]}, - {bids: [{bid_id: 2, matching: true}, {bid_id: 110}, {bid_id: 111}]}, - {bids: [{bid_id: 120}]} + { bids: [{ bid_id: 1, matching: true }, { bid_id: 3, matching: true }, { bid_id: 100 }, { bid_id: 101 }] }, + { bids: [{ bid_id: 2, matching: true }, { bid_id: 110 }, { bid_id: 111 }] }, + { bids: [{ bid_id: 120 }] } ] }; bidRequests = matchingBids.map((mBids, i) => [ - {bidId: 100 + (i * 10)}, - {bidId: 101 + (i * 10)}, + { bidId: 100 + (i * 10) }, + { bidId: 101 + (i * 10) }, ...mBids ]); }); @@ -571,7 +504,7 @@ describe('pbsBidInterceptor', () => { const passedBidReqs = next.args[0][1]; interceptResults .filter((r) => r.bids.length > 0) - .forEach(({bidRequest}, i) => { + .forEach(({ bidRequest }, i) => { expect(passedBidReqs[i]).to.equal(bidRequest); }); }); @@ -612,20 +545,20 @@ describe('bid overrides', function () { }); afterEach(function () { - disableDebugging({hook, logger}); + disableDebugging({ hook, logger }); }); it('should happen when enabled with setConfig', function () { getConfig({ enabled: true - }, {config, hook, logger, utils}); + }, { config, hook, logger, utils }); expect(addBidResponse.getHooks().some(hook => hook.hook === addBidResponseBound)).to.equal(true); expect(addBidderRequests.getHooks().some(hook => hook.hook === addBidderRequestsBound)).to.equal(true); }); it('should happen when configuration found in sessionStorage', function () { sessionLoader({ - storage: {getItem: () => ('{"enabled": true}')}, + storage: { getItem: () => ('{"enabled": true}') }, config, hook, logger @@ -677,7 +610,7 @@ describe('bid overrides', function () { const next = (adUnitCode, bid) => { bids.push(bid); }; - addBidResponseHook.bind({overrides, logger})(next, bid.adUnitCode, bid); + addBidResponseHook.bind({ overrides, logger })(next, bid.adUnitCode, bid); }); } @@ -786,7 +719,7 @@ describe('bid overrides', function () { const next = (b) => { bidderRequests = b; }; - addBidderRequestsHook.bind({overrides, logger})(next, mockBidRequests); + addBidderRequestsHook.bind({ overrides, logger })(next, mockBidRequests); } it('should allow us to exclude bidders', function () { diff --git a/test/spec/modules/deepintentBidAdapter_spec.js b/test/spec/modules/deepintentBidAdapter_spec.js index ead1c8ecc7d..a1fb9f492eb 100644 --- a/test/spec/modules/deepintentBidAdapter_spec.js +++ b/test/spec/modules/deepintentBidAdapter_spec.js @@ -1,5 +1,5 @@ -import {expect} from 'chai'; -import {spec} from 'modules/deepintentBidAdapter.js'; +import { expect } from 'chai'; +import { spec } from 'modules/deepintentBidAdapter.js'; import * as utils from '../../../src/utils.js'; describe('Deepintent adapter', function () { @@ -376,7 +376,7 @@ describe('Deepintent adapter', function () { }); describe('GPP and coppa', function() { it('Request params check with GPP Consent', function () { - const bidderReq = {gppConsent: {gppString: 'gpp-string-test', applicableSections: [5]}}; + const bidderReq = { gppConsent: { gppString: 'gpp-string-test', applicableSections: [5] } }; const bRequest = spec.buildRequests(request, bidderReq); const data = JSON.parse(bRequest.data); expect(data.regs.gpp).to.equal('gpp-string-test'); @@ -397,12 +397,49 @@ describe('Deepintent adapter', function () { expect(data.regs.gpp_sid[0]).to.equal(5); }); it('should include coppa flag in bid request if coppa is set to true', () => { - const bidderReq = {ortb2: {regs: {coppa: 1}}}; + const bidderReq = { ortb2: { regs: { coppa: 1 } } }; const bRequest = spec.buildRequests(request, bidderReq); const data = JSON.parse(bRequest.data); expect(data.regs.coppa).to.equal(1); }); }); + describe('ortb2 blocking (bcat, badv)', function() { + it('should add bcat and badv to payload when bidderRequest.ortb2 has them', function() { + const bidderReq = { + ortb2: { + bcat: ['IAB1', 'IAB2'], + badv: ['example.com'] + } + }; + const bRequest = spec.buildRequests(request, bidderReq); + const data = JSON.parse(bRequest.data); + expect(data.bcat).to.deep.equal(['IAB1', 'IAB2']); + expect(data.badv).to.deep.equal(['example.com']); + }); + it('should not add bcat or badv when bidderRequest.ortb2 does not have them', function() { + const bidderReq = { ortb2: {} }; + const bRequest = spec.buildRequests(request, bidderReq); + const data = JSON.parse(bRequest.data); + expect(data.bcat).to.be.undefined; + expect(data.badv).to.be.undefined; + }); + it('should use params.bcat and params.badv as fallback when ortb2 does not set them', function() { + const requestWithParams = [{ + bidder: 'deepintent', + bidId: 'test-bid-id', + mediaTypes: { banner: { sizes: [[300, 250]] } }, + params: { + tagId: '100013', + bcat: ['IAB25'], + badv: ['blocked-advertiser.com'] + } + }]; + const bRequest = spec.buildRequests(requestWithParams); + const data = JSON.parse(bRequest.data); + expect(data.bcat).to.deep.equal(['IAB25']); + expect(data.badv).to.deep.equal(['blocked-advertiser.com']); + }); + }); describe('deals functionality', function() { it('should add PMP deals when valid deals array is provided', function() { const requestWithDeals = [{ diff --git a/test/spec/modules/deepintentDpesIdsystem_spec.js b/test/spec/modules/deepintentDpesIdsystem_spec.js index 8f8c100afc8..63a9710e4e0 100644 --- a/test/spec/modules/deepintentDpesIdsystem_spec.js +++ b/test/spec/modules/deepintentDpesIdsystem_spec.js @@ -1,9 +1,9 @@ import { expect } from 'chai'; import { deepintentDpesSubmodule } from 'modules/deepintentDpesIdSystem.js'; -import {attachIdSystem} from '../../../modules/userId/index.js'; -import {createEidsArray} from '../../../modules/userId/eids.js'; +import { attachIdSystem } from '../../../modules/userId/index.js'; +import { createEidsArray } from '../../../modules/userId/eids.js'; -const DI_COOKIE_OBJECT = {id: '2cf40748c4f7f60d343336e08f80dc99'}; +const DI_COOKIE_OBJECT = { id: '2cf40748c4f7f60d343336e08f80dc99' }; const DI_UPDATED_STORAGE = '2cf40748c4f7f60d343336e08f80dc99'; const cookieConfig = { @@ -44,11 +44,11 @@ describe('Deepintent DPES System', () => { describe('Deepintent Dpes System : test "decode" method', () => { it('Get the correct decoded value for dpes id, if an object is set return object', () => { - expect(deepintentDpesSubmodule.decode(DI_COOKIE_OBJECT, cookieConfig)).to.deep.equal({'deepintentId': DI_COOKIE_OBJECT}); + expect(deepintentDpesSubmodule.decode(DI_COOKIE_OBJECT, cookieConfig)).to.deep.equal({ 'deepintentId': DI_COOKIE_OBJECT }); }); it('Get the correct decoded value for dpes id, if a string is set return string', () => { - expect(deepintentDpesSubmodule.decode(DI_UPDATED_STORAGE, {})).to.deep.equal({'deepintentId': DI_UPDATED_STORAGE}); + expect(deepintentDpesSubmodule.decode(DI_UPDATED_STORAGE, {})).to.deep.equal({ 'deepintentId': DI_UPDATED_STORAGE }); }); }); @@ -73,7 +73,7 @@ describe('Deepintent DPES System', () => { expect(newEids.length).to.equal(1); expect(newEids[0]).to.deep.equal({ source: 'deepintent.com', - uids: [{id: 'some-random-id-value', atype: 3}] + uids: [{ id: 'some-random-id-value', atype: 3 }] }); }); }) diff --git a/test/spec/modules/deltaprojectsBidAdapter_spec.js b/test/spec/modules/deltaprojectsBidAdapter_spec.js index 30f709f0a06..6cea023b81b 100644 --- a/test/spec/modules/deltaprojectsBidAdapter_spec.js +++ b/test/spec/modules/deltaprojectsBidAdapter_spec.js @@ -57,7 +57,7 @@ describe('deltaprojectsBidAdapter', function() { auctionId: '1d1a030790a475', } const bidRequests = [BIDREQ]; - const bannerRequest = spec.buildRequests(bidRequests, {refererInfo: { page: BID_REQ_REFER, domain: BID_REQ_DOMAIN }})[0]; + const bannerRequest = spec.buildRequests(bidRequests, { refererInfo: { page: BID_REQ_REFER, domain: BID_REQ_DOMAIN } })[0]; const bannerRequestBody = bannerRequest.data; it('send bid request with test tag if it is set in the param', function () { @@ -143,12 +143,12 @@ describe('deltaprojectsBidAdapter', function() { it('should handle gdpr applies being undefined', function() { const gdprRequestBody = getGdprRequestBody(undefined, consentString); - expect(gdprRequestBody.regs).to.deep.equal({ext: {}}); + expect(gdprRequestBody.regs).to.deep.equal({ ext: {} }); expect(gdprRequestBody.user.ext.consent).to.equal(consentString); }) it('should handle gdpr consent being undefined', function() { - const gdprRequest = spec.buildRequests(gdprBidRequests, {refererInfo: { referer: GDPR_REQ_REFERER }})[0]; + const gdprRequest = spec.buildRequests(gdprBidRequests, { refererInfo: { referer: GDPR_REQ_REFERER } })[0]; const gdprRequestBody = gdprRequest.data; expect(gdprRequestBody.regs).to.deep.equal({ ext: {} }); expect(gdprRequestBody.user).to.deep.equal({ ext: {} }); @@ -172,7 +172,7 @@ describe('deltaprojectsBidAdapter', function() { auctionId: '1d1a030790a475', }, ]; - const request = spec.buildRequests(bidRequests, {refererInfo: { referer: BID_REQ_REFER }})[0]; + const request = spec.buildRequests(bidRequests, { refererInfo: { referer: BID_REQ_REFER } })[0]; function makeResponse() { return { body: { @@ -243,7 +243,7 @@ describe('deltaprojectsBidAdapter', function() { const noCridResponse = makeResponse(); delete noCridResponse.body.seatbid[0].bid[0].crid; const fallbackCrid = noCridResponse.body.seatbid[0].bid[0].id; - const noCridResult = Object.assign({}, expectedBid, {'creativeId': fallbackCrid}); + const noCridResult = Object.assign({}, expectedBid, { 'creativeId': fallbackCrid }); const result = spec.interpretResponse(noCridResponse, request); expect(result.length).to.equal(1); expect(result[0]).to.deep.equal(noCridResult); @@ -271,8 +271,8 @@ describe('deltaprojectsBidAdapter', function() { }); it('should keep custom properties', () => { - const customProperties = {test: 'a test message', param: {testParam: 1}}; - const expectedResult = Object.assign({}, expectedBid, {[spec.code]: customProperties}); + const customProperties = { test: 'a test message', param: { testParam: 1 } }; + const expectedResult = Object.assign({}, expectedBid, { [spec.code]: customProperties }); const response = makeResponse(); response.body.seatbid[0].bid[0].ext = customProperties; const result = spec.interpretResponse(response, request); diff --git a/test/spec/modules/dianomiBidAdapter_spec.js b/test/spec/modules/dianomiBidAdapter_spec.js index 761e12edd85..3f962ce4b9c 100644 --- a/test/spec/modules/dianomiBidAdapter_spec.js +++ b/test/spec/modules/dianomiBidAdapter_spec.js @@ -164,7 +164,7 @@ describe('Dianomi adapter', () => { }, ]; const request = JSON.parse( - spec.buildRequests(validBidRequests, {refererInfo: {page: 'page'}, ortb2: {source: {tid: 'tid'}}}).data + spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' }, ortb2: { source: { tid: 'tid' } } }).data ); assert.equal(request.source.tid, 'tid'); diff --git a/test/spec/modules/digitalMatterBidAdapter_spec.js b/test/spec/modules/digitalMatterBidAdapter_spec.js index 2627050e388..adec36a9c7b 100644 --- a/test/spec/modules/digitalMatterBidAdapter_spec.js +++ b/test/spec/modules/digitalMatterBidAdapter_spec.js @@ -1,7 +1,7 @@ -import {assert, expect} from 'chai'; -import {spec} from 'modules/digitalMatterBidAdapter'; -import {config} from '../../../src/config.js'; -import {deepClone} from '../../../src/utils.js'; +import { assert, expect } from 'chai'; +import { spec } from 'modules/digitalMatterBidAdapter'; +import { config } from '../../../src/config.js'; +import { deepClone } from '../../../src/utils.js'; const bid = { 'adUnitCode': 'adUnitCode', @@ -86,7 +86,7 @@ describe('Digital Matter BidAdapter', function () { it('should send info about device', function () { config.setConfig({ - device: {w: 1920, h: 1080} + device: { w: 1920, h: 1080 } }); const request = JSON.parse(spec.buildRequests([bid], bidderRequest).data); @@ -108,10 +108,10 @@ describe('Digital Matter BidAdapter', function () { }); it('should send currency if defined', function () { - config.setConfig({currency: {adServerCurrency: 'EUR'}}); + config.setConfig({ currency: { adServerCurrency: 'EUR' } }); const request = JSON.parse(spec.buildRequests([bid], bidderRequest).data); - assert.deepEqual(request.cur, [{adServerCurrency: 'EUR'}]); + assert.deepEqual(request.cur, [{ adServerCurrency: 'EUR' }]); }); it('should pass supply chain object', function () { @@ -247,7 +247,7 @@ describe('Digital Matter BidAdapter', function () { assert.deepEqual(bids[0].width, firstResponse.width); assert.deepEqual(bids[0].height, firstResponse.height); assert.deepEqual(bids[0].dealId, undefined); - assert.deepEqual(bids[0].meta.advertiserDomains, [ 'advertiser.org' ]); + assert.deepEqual(bids[0].meta.advertiserDomains, ['advertiser.org']); assert.deepEqual(bids[1].requestId, secondResponse.bidid); assert.deepEqual(bids[1].cpm, secondResponse.cpm); @@ -258,7 +258,7 @@ describe('Digital Matter BidAdapter', function () { assert.deepEqual(bids[1].width, secondResponse.width); assert.deepEqual(bids[1].height, secondResponse.height); assert.deepEqual(bids[1].dealId, undefined); - assert.deepEqual(bids[1].meta.advertiserDomains, [ 'advertiser.org' ]); + assert.deepEqual(bids[1].meta.advertiserDomains, ['advertiser.org']); }); }); diff --git a/test/spec/modules/discoveryBidAdapter_spec.js b/test/spec/modules/discoveryBidAdapter_spec.js index fbeef8a8d7e..2b23041a69d 100644 --- a/test/spec/modules/discoveryBidAdapter_spec.js +++ b/test/spec/modules/discoveryBidAdapter_spec.js @@ -10,7 +10,7 @@ import { } from 'modules/discoveryBidAdapter.js'; import { getPageTitle, getPageDescription, getPageKeywords, getConnectionDownLink } from '../../../libraries/fpdUtils/pageInfo.js'; import * as utils from 'src/utils.js'; -import {getHLen} from '../../../libraries/navigatorData/navigatorData.js'; +import { getHLen } from '../../../libraries/navigatorData/navigatorData.js'; describe('discovery:BidAdapterTests', function () { let sandbox; diff --git a/test/spec/modules/displayioBidAdapter_spec.js b/test/spec/modules/displayioBidAdapter_spec.js index b2ab38360f6..d74cf775d73 100644 --- a/test/spec/modules/displayioBidAdapter_spec.js +++ b/test/spec/modules/displayioBidAdapter_spec.js @@ -1,5 +1,5 @@ -import {spec} from 'modules/displayioBidAdapter.js' -import {BANNER} from '../../../src/mediaTypes.js' +import { spec } from 'modules/displayioBidAdapter.js' +import { BANNER } from '../../../src/mediaTypes.js' describe('Displayio adapter', function () { const BIDDER = 'displayio' diff --git a/test/spec/modules/dmdIdSystem_spec.js b/test/spec/modules/dmdIdSystem_spec.js deleted file mode 100644 index d0d8747dee9..00000000000 --- a/test/spec/modules/dmdIdSystem_spec.js +++ /dev/null @@ -1,101 +0,0 @@ -import * as utils from '../../../src/utils.js'; -import { server } from 'test/mocks/xhr.js'; -import { dmdIdSubmodule } from 'modules/dmdIdSystem.js'; - -describe('Dmd ID System', function () { - let logErrorStub; - const config = { - params: { - api_key: '33344ffjddk22k22k222k22234k', - api_url: 'https://aix.hcn.health/api/v1/auths' - } - }; - - beforeEach(function () { - if (utils.logError.restore && utils.logError.restore.sinon) { - utils.logError.restore(); - } - logErrorStub = sinon.stub(utils, 'logError'); - }); - - afterEach(function () { - if (logErrorStub && logErrorStub.restore) { - logErrorStub.restore(); - } - }); - - it('should log an error if no configParams were passed into getId', function () { - dmdIdSubmodule.getId(); - expect(logErrorStub.calledOnce).to.be.true; - }); - - it('should log an error if configParams doesnot have api_key passed to getId', function () { - dmdIdSubmodule.getId({params: {}}); - expect(logErrorStub.calledOnce).to.be.true; - }); - - it('should log an error if configParams has invalid api_key passed into getId', function () { - dmdIdSubmodule.getId({params: {api_key: 123}}); - expect(logErrorStub.calledOnce).to.be.true; - }); - - it('should not log an error if configParams has valid api_key passed into getId', function () { - dmdIdSubmodule.getId({params: {api_key: '3fdbe297-3690-4f5c-9e11-ee9186a6d77c'}}); - expect(logErrorStub.calledOnce).to.be.false; - }); - - it('should return undefined if empty value passed into decode', function () { - expect(dmdIdSubmodule.decode()).to.be.undefined; - }); - - it('should return undefined if invalid dmd-dgid passed into decode', function () { - expect(dmdIdSubmodule.decode(123)).to.be.undefined; - }); - - it('should return dmdId if valid dmd-dgid passed into decode', function () { - const data = { 'dmdId': 'U12345' }; - expect(dmdIdSubmodule.decode('U12345')).to.deep.equal(data); - }); - - it('should return cacheObj if cacheObj is passed into getId', function () { - const data = { 'dmdId': 'U12345' }; - expect(dmdIdSubmodule.getId(config, {}, { cookie: 'dmd-dgid' })).to.deep.equal({ cookie: 'dmd-dgid' }); - expect(server.requests.length).to.eq(0); - }); - - it('Should invoke callback with response from API call', function () { - const callbackSpy = sinon.spy(); - const domain = utils.getWindowLocation().href; - const callback = dmdIdSubmodule.getId(config).callback; - callback(callbackSpy); - const request = server.requests[0]; - expect(request.method).to.eq('GET'); - expect(request.requestHeaders['x-domain']).to.be.eq(domain); - expect(request.url).to.eq(config.params.api_url); - request.respond(200, { 'Content-Type': 'application/json' }, JSON.stringify({ dgid: 'U12345' })); - expect(callbackSpy.lastCall.lastArg).to.deep.equal('U12345'); - }); - - it('Should log error if API response is not valid', function () { - const callbackSpy = sinon.spy(); - const domain = utils.getWindowLocation().href; - const callback = dmdIdSubmodule.getId(config).callback; - callback(callbackSpy); - const request = server.requests[0]; - expect(request.method).to.eq('GET'); - expect(request.requestHeaders['x-domain']).to.be.eq(domain); - expect(request.url).to.eq(config.params.api_url); - request.respond(400, { 'Content-Type': 'application/json' }, undefined); - expect(logErrorStub.calledOnce).to.be.true; - }); - - it('Should log error if API call throws error', function () { - const callbackSpy = sinon.spy(); - const callback = dmdIdSubmodule.getId(config).callback; - callback(callbackSpy); - const request = server.requests[0]; - expect(request.url).to.eq(config.params.api_url); - request.error(); - expect(logErrorStub.calledOnce).to.be.true; - }); -}); diff --git a/test/spec/modules/docereeBidAdapter_spec.js b/test/spec/modules/docereeBidAdapter_spec.js index 6b79264f2d6..d079eaac876 100644 --- a/test/spec/modules/docereeBidAdapter_spec.js +++ b/test/spec/modules/docereeBidAdapter_spec.js @@ -1,5 +1,5 @@ -import {expect} from 'chai'; -import {spec} from '../../../modules/docereeBidAdapter.js'; +import { expect } from 'chai'; +import { spec } from '../../../modules/docereeBidAdapter.js'; import { config } from '../../../src/config.js'; import * as utils from 'src/utils.js'; diff --git a/test/spec/modules/dpaiBidAdapter_spec.js b/test/spec/modules/dpaiBidAdapter_spec.js new file mode 100644 index 00000000000..f81c3aa7c95 --- /dev/null +++ b/test/spec/modules/dpaiBidAdapter_spec.js @@ -0,0 +1,511 @@ +import { expect } from 'chai'; +import { spec } from '../../../modules/dpaiBidAdapter.js'; +import { BANNER, VIDEO, NATIVE } from '../../../src/mediaTypes.js'; +import { getUniqueIdentifierStr } from '../../../src/utils.js'; + +const bidder = 'dpai'; + +describe('DpaiBidAdapter', function () { + const userIdAsEids = [{ + source: 'test.org', + uids: [{ + id: '01**********', + atype: 1, + ext: { + third: '01***********' + } + }] + }]; + const bids = [ + { + bidId: getUniqueIdentifierStr(), + bidder: bidder, + mediaTypes: { + [BANNER]: { + sizes: [[300, 250]] + } + }, + params: { + placementId: 'testBanner', + }, + userIdAsEids + }, + { + bidId: getUniqueIdentifierStr(), + bidder: bidder, + mediaTypes: { + [VIDEO]: { + playerSize: [[300, 300]], + minduration: 5, + maxduration: 60 + } + }, + params: { + placementId: 'testVideo', + }, + userIdAsEids + }, + { + bidId: getUniqueIdentifierStr(), + bidder: bidder, + mediaTypes: { + [NATIVE]: { + native: { + title: { + required: true + }, + body: { + required: true + }, + icon: { + required: true, + size: [64, 64] + } + } + } + }, + params: { + placementId: 'testNative' + }, + userIdAsEids + } + ]; + + const invalidBid = { + bidId: getUniqueIdentifierStr(), + bidder: bidder, + mediaTypes: { + [BANNER]: { + sizes: [[300, 250]] + } + }, + params: { + + } + } + + const bidderRequest = { + uspConsent: '1---', + gdprConsent: { + consentString: 'COvFyGBOvFyGBAbAAAENAPCAAOAAAAAAAAAAAEEUACCKAAA.IFoEUQQgAIQwgIwQABAEAAAAOIAACAIAAAAQAIAgEAACEAAAAAgAQBAAAAAAAGBAAgAAAAAAAFAAECAAAgAAQARAEQAAAAAJAAIAAgAAAYQEAAAQmAgBC3ZAYzUw', + vendorData: {} + }, + refererInfo: { + referer: 'https://test.com', + page: 'https://test.com' + }, + ortb2: { + device: { + w: 1512, + h: 982, + language: 'en-UK', + } + }, + timeout: 500 + }; + + describe('isBidRequestValid', function () { + it('Should return true if there are bidId, params and key parameters present', function () { + expect(spec.isBidRequestValid(bids[0])).to.be.true; + }); + it('Should return false if at least one of parameters is not present', function () { + expect(spec.isBidRequestValid(invalidBid)).to.be.false; + }); + }); + + describe('buildRequests', function () { + let serverRequest = spec.buildRequests(bids, bidderRequest); + + 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 general data valid', function () { + const data = serverRequest.data; + expect(data).to.be.an('object'); + expect(data).to.have.all.keys( + 'deviceWidth', + 'deviceHeight', + 'device', + 'language', + 'secure', + 'host', + 'page', + 'placements', + 'coppa', + 'ccpa', + 'gdpr', + 'tmax', + 'bcat', + 'badv', + 'bapp', + 'battr' + ); + expect(data.deviceWidth).to.be.a('number'); + expect(data.deviceHeight).to.be.a('number'); + expect(data.language).to.be.a('string'); + expect(data.secure).to.be.within(0, 1); + expect(data.host).to.be.a('string'); + expect(data.page).to.be.a('string'); + expect(data.coppa).to.be.a('number'); + expect(data.gdpr).to.be.a('object'); + expect(data.ccpa).to.be.a('string'); + expect(data.tmax).to.be.a('number'); + expect(data.placements).to.have.lengthOf(3); + }); + + it('Returns valid placements', function () { + const { placements } = serverRequest.data; + for (let i = 0, len = placements.length; i < len; i++) { + const placement = placements[i]; + expect(placement.placementId).to.be.oneOf(['testBanner', 'testVideo', 'testNative']); + expect(placement.adFormat).to.be.oneOf([BANNER, VIDEO, NATIVE]); + expect(placement.bidId).to.be.a('string'); + expect(placement.schain).to.be.an('object'); + expect(placement.bidfloor).to.exist.and.to.equal(0); + expect(placement.type).to.exist.and.to.equal('publisher'); + expect(placement.eids).to.exist.and.to.be.deep.equal(userIdAsEids); + + if (placement.adFormat === BANNER) { + expect(placement.sizes).to.be.an('array'); + } + switch (placement.adFormat) { + case BANNER: + expect(placement.sizes).to.be.an('array'); + break; + case VIDEO: + expect(placement.playerSize).to.be.an('array'); + expect(placement.minduration).to.be.an('number'); + expect(placement.maxduration).to.be.an('number'); + break; + case NATIVE: + expect(placement.native).to.be.an('object'); + break; + } + } + }); + + it('Returns valid endpoints', function () { + const bids = [ + { + bidId: getUniqueIdentifierStr(), + bidder: bidder, + mediaTypes: { + [BANNER]: { + sizes: [[300, 250]] + } + }, + params: { + endpointId: 'testBanner', + }, + userIdAsEids + } + ]; + + const serverRequest = spec.buildRequests(bids, bidderRequest); + + const { placements } = serverRequest.data; + for (let i = 0, len = placements.length; i < len; i++) { + const placement = placements[i]; + expect(placement.endpointId).to.be.oneOf(['testBanner', 'testVideo', 'testNative']); + expect(placement.adFormat).to.be.oneOf([BANNER, VIDEO, NATIVE]); + expect(placement.bidId).to.be.a('string'); + expect(placement.schain).to.be.an('object'); + expect(placement.bidfloor).to.exist.and.to.equal(0); + expect(placement.type).to.exist.and.to.equal('network'); + expect(placement.eids).to.exist.and.to.be.deep.equal(userIdAsEids); + + if (placement.adFormat === BANNER) { + expect(placement.sizes).to.be.an('array'); + } + switch (placement.adFormat) { + case BANNER: + expect(placement.sizes).to.be.an('array'); + break; + case VIDEO: + expect(placement.playerSize).to.be.an('array'); + expect(placement.minduration).to.be.an('number'); + expect(placement.maxduration).to.be.an('number'); + break; + case NATIVE: + expect(placement.native).to.be.an('object'); + break; + } + } + }); + + it('Returns data with gdprConsent and without uspConsent', function () { + delete bidderRequest.uspConsent; + serverRequest = spec.buildRequests(bids, bidderRequest); + const data = serverRequest.data; + expect(data.gdpr).to.exist; + expect(data.gdpr).to.be.a('object'); + expect(data.gdpr).to.have.property('consentString'); + expect(data.gdpr).to.not.have.property('vendorData'); + expect(data.gdpr.consentString).to.equal(bidderRequest.gdprConsent.consentString); + expect(data.ccpa).to.not.exist; + delete bidderRequest.gdprConsent; + }); + + it('Returns data with uspConsent and without gdprConsent', function () { + bidderRequest.uspConsent = '1---'; + delete bidderRequest.gdprConsent; + serverRequest = spec.buildRequests(bids, bidderRequest); + const data = serverRequest.data; + expect(data.ccpa).to.exist; + expect(data.ccpa).to.be.a('string'); + expect(data.ccpa).to.equal(bidderRequest.uspConsent); + expect(data.gdpr).to.not.exist; + }); + }); + + describe('gpp consent', function () { + it('bidderRequest.gppConsent', () => { + bidderRequest.gppConsent = { + gppString: 'abc123', + applicableSections: [8] + }; + + const serverRequest = spec.buildRequests(bids, bidderRequest); + const data = serverRequest.data; + expect(data).to.be.an('object'); + expect(data).to.have.property('gpp'); + expect(data).to.have.property('gpp_sid'); + + delete bidderRequest.gppConsent; + }) + + it('bidderRequest.ortb2.regs.gpp', () => { + bidderRequest.ortb2 = bidderRequest.ortb2 || {}; + bidderRequest.ortb2.regs = bidderRequest.ortb2.regs || {}; + bidderRequest.ortb2.regs.gpp = 'abc123'; + bidderRequest.ortb2.regs.gpp_sid = [8]; + + const serverRequest = spec.buildRequests(bids, bidderRequest); + const data = serverRequest.data; + expect(data).to.be.an('object'); + expect(data).to.have.property('gpp'); + expect(data).to.have.property('gpp_sid'); + }) + }); + + describe('interpretResponse', function () { + it('Should interpret banner response', function () { + const banner = { + body: [{ + mediaType: 'banner', + width: 300, + height: 250, + cpm: 0.4, + ad: 'Test', + requestId: '23fhj33i987f', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1', + meta: { + advertiserDomains: ['google.com'], + advertiserId: 1234 + } + }] + }; + const bannerResponses = spec.interpretResponse(banner); + expect(bannerResponses).to.be.an('array').that.is.not.empty; + const dataItem = bannerResponses[0]; + expect(dataItem).to.have.all.keys('requestId', 'cpm', 'width', 'height', 'ad', 'ttl', 'creativeId', + 'netRevenue', 'currency', 'dealId', 'mediaType', 'meta'); + expect(dataItem.requestId).to.equal(banner.body[0].requestId); + expect(dataItem.cpm).to.equal(banner.body[0].cpm); + expect(dataItem.width).to.equal(banner.body[0].width); + expect(dataItem.height).to.equal(banner.body[0].height); + expect(dataItem.ad).to.equal(banner.body[0].ad); + expect(dataItem.ttl).to.equal(banner.body[0].ttl); + expect(dataItem.creativeId).to.equal(banner.body[0].creativeId); + expect(dataItem.netRevenue).to.be.true; + expect(dataItem.currency).to.equal(banner.body[0].currency); + expect(dataItem.meta).to.be.an('object').that.has.any.key('advertiserDomains'); + }); + it('Should interpret video response', function () { + const video = { + body: [{ + vastUrl: 'test.com', + mediaType: 'video', + cpm: 0.5, + requestId: '23fhj33i987f', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1', + meta: { + advertiserDomains: ['google.com'], + advertiserId: 1234 + } + }] + }; + const videoResponses = spec.interpretResponse(video); + expect(videoResponses).to.be.an('array').that.is.not.empty; + + const dataItem = videoResponses[0]; + expect(dataItem).to.have.all.keys('requestId', 'cpm', 'vastUrl', 'ttl', 'creativeId', + 'netRevenue', 'currency', 'dealId', 'mediaType', 'meta'); + expect(dataItem.requestId).to.equal('23fhj33i987f'); + expect(dataItem.cpm).to.equal(0.5); + expect(dataItem.vastUrl).to.equal('test.com'); + expect(dataItem.ttl).to.equal(120); + expect(dataItem.creativeId).to.equal('2'); + expect(dataItem.netRevenue).to.be.true; + expect(dataItem.currency).to.equal('USD'); + expect(dataItem.meta).to.be.an('object').that.has.any.key('advertiserDomains'); + }); + it('Should interpret native response', function () { + const native = { + body: [{ + mediaType: 'native', + native: { + clickUrl: 'test.com', + title: 'Test', + image: 'test.com', + impressionTrackers: ['test.com'], + }, + ttl: 120, + cpm: 0.4, + requestId: '23fhj33i987f', + creativeId: '2', + netRevenue: true, + currency: 'USD', + meta: { + advertiserDomains: ['google.com'], + advertiserId: 1234 + } + }] + }; + const nativeResponses = spec.interpretResponse(native); + expect(nativeResponses).to.be.an('array').that.is.not.empty; + + const dataItem = nativeResponses[0]; + expect(dataItem).to.have.keys('requestId', 'cpm', 'ttl', 'creativeId', 'netRevenue', 'currency', 'mediaType', 'native', 'meta'); + expect(dataItem.native).to.have.keys('clickUrl', 'impressionTrackers', 'title', 'image') + expect(dataItem.requestId).to.equal('23fhj33i987f'); + expect(dataItem.cpm).to.equal(0.4); + expect(dataItem.native.clickUrl).to.equal('test.com'); + expect(dataItem.native.title).to.equal('Test'); + expect(dataItem.native.image).to.equal('test.com'); + expect(dataItem.native.impressionTrackers).to.be.an('array').that.is.not.empty; + expect(dataItem.native.impressionTrackers[0]).to.equal('test.com'); + expect(dataItem.ttl).to.equal(120); + expect(dataItem.creativeId).to.equal('2'); + expect(dataItem.netRevenue).to.be.true; + expect(dataItem.currency).to.equal('USD'); + expect(dataItem.meta).to.be.an('object').that.has.any.key('advertiserDomains'); + }); + it('Should return an empty array if invalid banner response is passed', function () { + const invBanner = { + body: [{ + width: 300, + cpm: 0.4, + ad: 'Test', + requestId: '23fhj33i987f', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1' + }] + }; + + const 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', function () { + const invVideo = { + body: [{ + mediaType: 'video', + cpm: 0.5, + requestId: '23fhj33i987f', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1' + }] + }; + const 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', function () { + const invNative = { + body: [{ + mediaType: 'native', + clickUrl: 'test.com', + title: 'Test', + impressionTrackers: ['test.com'], + ttl: 120, + requestId: '23fhj33i987f', + creativeId: '2', + netRevenue: true, + currency: 'USD', + }] + }; + const serverResponses = spec.interpretResponse(invNative); + expect(serverResponses).to.be.an('array').that.is.empty; + }); + it('Should return an empty array if invalid response is passed', function () { + const invalid = { + body: [{ + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1' + }] + }; + const serverResponses = spec.interpretResponse(invalid); + expect(serverResponses).to.be.an('array').that.is.empty; + }); + }); + + describe('getUserSyncs', function() { + it('Should return array of objects with proper sync config , include GDPR', function() { + const syncData = spec.getUserSyncs({}, {}, { + consentString: 'ALL', + gdprApplies: true, + }, undefined); + expect(syncData).to.be.an('array').which.is.not.empty; + expect(syncData[0]).to.be.an('object') + expect(syncData[0].type).to.be.a('string') + expect(syncData[0].type).to.equal('image') + expect(syncData[0].url).to.be.a('string') + expect(syncData[0].url).to.equal('https://sync.drift-pixel.ai/image?pbjs=1&gdpr=1&gdpr_consent=ALL&coppa=0') + }); + it('Should return array of objects with proper sync config , include CCPA', function() { + const syncData = spec.getUserSyncs({}, {}, {}, '1---'); + expect(syncData).to.be.an('array').which.is.not.empty; + expect(syncData[0]).to.be.an('object') + expect(syncData[0].type).to.be.a('string') + expect(syncData[0].type).to.equal('image') + expect(syncData[0].url).to.be.a('string') + expect(syncData[0].url).to.equal('https://sync.drift-pixel.ai/image?pbjs=1&ccpa_consent=1---&coppa=0') + }); + it('Should return array of objects with proper sync config , include GPP', function() { + const syncData = spec.getUserSyncs({}, {}, {}, undefined, { + gppString: 'abc123', + applicableSections: [8] + }); + expect(syncData).to.be.an('array').which.is.not.empty; + expect(syncData[0]).to.be.an('object') + expect(syncData[0].type).to.be.a('string') + expect(syncData[0].type).to.equal('image') + expect(syncData[0].url).to.be.a('string') + expect(syncData[0].url).to.equal('https://sync.drift-pixel.ai/image?pbjs=1&gpp=abc123&gpp_sid=8&coppa=0') + }); + }); +}); diff --git a/test/spec/modules/driftpixelBidAdapter_spec.js b/test/spec/modules/driftpixelBidAdapter_spec.js index a7b5a164996..d1e78b2be3b 100644 --- a/test/spec/modules/driftpixelBidAdapter_spec.js +++ b/test/spec/modules/driftpixelBidAdapter_spec.js @@ -1,8 +1,8 @@ -import {expect} from 'chai'; -import {config} from 'src/config.js'; -import {spec} from 'modules/driftpixelBidAdapter.js'; -import {deepClone} from 'src/utils'; -import {getBidFloor} from '../../../libraries/xeUtils/bidderUtils.js'; +import { expect } from 'chai'; +import { config } from 'src/config.js'; +import { spec } from 'modules/driftpixelBidAdapter.js'; +import { deepClone } from 'src/utils'; +import { getBidFloor } from '../../../libraries/xeUtils/bidderUtils.js'; const ENDPOINT = 'https://pbjs.driftpixel.live'; @@ -49,12 +49,12 @@ defaultRequestVideo.mediaTypes = { const videoBidderRequest = { bidderCode: 'driftpixel', - bids: [{mediaTypes: {video: {}}, bidId: 'qwerty'}] + bids: [{ mediaTypes: { video: {} }, bidId: 'qwerty' }] }; const displayBidderRequest = { bidderCode: 'driftpixel', - bids: [{bidId: 'qwerty'}] + bids: [{ bidId: 'qwerty' }] }; describe('driftpixelBidAdapter', () => { @@ -110,7 +110,7 @@ describe('driftpixelBidAdapter', () => { expect(request).to.have.property('tz').and.to.equal(new Date().getTimezoneOffset()); expect(request).to.have.property('bc').and.to.equal(1); expect(request).to.have.property('floor').and.to.equal(null); - expect(request).to.have.property('banner').and.to.deep.equal({sizes: [[300, 250], [300, 200]]}); + expect(request).to.have.property('banner').and.to.deep.equal({ sizes: [[300, 250], [300, 200]] }); expect(request).to.have.property('gdprConsent').and.to.deep.equal({}); expect(request).to.have.property('userEids').and.to.deep.equal([]); expect(request).to.have.property('usPrivacy').and.to.equal(''); @@ -203,7 +203,7 @@ describe('driftpixelBidAdapter', () => { it('should build request with valid bidfloor', function () { const bfRequest = deepClone(defaultRequest); - bfRequest.getFloor = () => ({floor: 5, currency: 'USD'}); + bfRequest.getFloor = () => ({ floor: 5, currency: 'USD' }); const request = JSON.parse(spec.buildRequests([bfRequest], {}).data)[0]; expect(request).to.have.property('floor').and.to.equal(5); }); @@ -219,8 +219,8 @@ describe('driftpixelBidAdapter', () => { it('should build request with extended ids', function () { const idRequest = deepClone(defaultRequest); idRequest.userIdAsEids = [ - {source: 'adserver.org', uids: [{id: 'TTD_ID_FROM_USER_ID_MODULE', atype: 1, ext: {rtiPartner: 'TDID'}}]}, - {source: 'pubcid.org', uids: [{id: 'pubCommonId_FROM_USER_ID_MODULE', atype: 1}]} + { source: 'adserver.org', uids: [{ id: 'TTD_ID_FROM_USER_ID_MODULE', atype: 1, ext: { rtiPartner: 'TDID' } }] }, + { source: 'pubcid.org', uids: [{ id: 'pubCommonId_FROM_USER_ID_MODULE', atype: 1 }] } ]; const request = JSON.parse(spec.buildRequests([idRequest], {}).data)[0]; expect(request).to.have.property('userEids').and.deep.equal(idRequest.userIdAsEids); @@ -272,7 +272,7 @@ describe('driftpixelBidAdapter', () => { } }; - const validResponse = spec.interpretResponse(serverResponse, {bidderRequest: displayBidderRequest}); + const validResponse = spec.interpretResponse(serverResponse, { bidderRequest: displayBidderRequest }); const bid = validResponse[0]; expect(validResponse).to.be.an('array').that.is.not.empty; expect(bid.requestId).to.equal('qwerty'); @@ -281,7 +281,7 @@ describe('driftpixelBidAdapter', () => { expect(bid.width).to.equal(300); expect(bid.height).to.equal(250); expect(bid.ttl).to.equal(600); - expect(bid.meta).to.deep.equal({advertiserDomains: ['driftpixel']}); + expect(bid.meta).to.deep.equal({ advertiserDomains: ['driftpixel'] }); }); it('should interpret valid banner response', function () { @@ -302,7 +302,7 @@ describe('driftpixelBidAdapter', () => { } }; - const validResponseBanner = spec.interpretResponse(serverResponse, {bidderRequest: displayBidderRequest}); + const validResponseBanner = spec.interpretResponse(serverResponse, { bidderRequest: displayBidderRequest }); const bid = validResponseBanner[0]; expect(validResponseBanner).to.be.an('array').that.is.not.empty; expect(bid.mediaType).to.equal('banner'); @@ -328,7 +328,7 @@ describe('driftpixelBidAdapter', () => { } }; - const validResponseBanner = spec.interpretResponse(serverResponse, {bidderRequest: videoBidderRequest}); + const validResponseBanner = spec.interpretResponse(serverResponse, { bidderRequest: videoBidderRequest }); const bid = validResponseBanner[0]; expect(validResponseBanner).to.be.an('array').that.is.not.empty; expect(bid.mediaType).to.equal('video'); @@ -344,12 +344,12 @@ describe('driftpixelBidAdapter', () => { }); it('should return empty if sync is not allowed', function () { - const opts = spec.getUserSyncs({iframeEnabled: false, pixelEnabled: false}); + const opts = spec.getUserSyncs({ iframeEnabled: false, pixelEnabled: false }); expect(opts).to.be.an('array').that.is.empty; }); it('should allow iframe sync', function () { - const opts = spec.getUserSyncs({iframeEnabled: true, pixelEnabled: false}, [{ + const opts = spec.getUserSyncs({ iframeEnabled: true, pixelEnabled: false }, [{ body: { data: [{ requestId: 'qwerty', @@ -368,7 +368,7 @@ describe('driftpixelBidAdapter', () => { }); it('should allow pixel sync', function () { - const opts = spec.getUserSyncs({iframeEnabled: false, pixelEnabled: true}, [{ + const opts = spec.getUserSyncs({ iframeEnabled: false, pixelEnabled: true }, [{ body: { data: [{ requestId: 'qwerty', @@ -387,7 +387,7 @@ describe('driftpixelBidAdapter', () => { }); it('should allow pixel sync and parse consent params', function () { - const opts = spec.getUserSyncs({iframeEnabled: false, pixelEnabled: true}, [{ + const opts = spec.getUserSyncs({ iframeEnabled: false, pixelEnabled: true }, [{ body: { data: [{ requestId: 'qwerty', @@ -411,20 +411,20 @@ describe('driftpixelBidAdapter', () => { describe('getBidFloor', function () { it('should return null when getFloor is not a function', () => { - const bid = {getFloor: 2}; + const bid = { getFloor: 2 }; const result = getBidFloor(bid); expect(result).to.be.null; }); it('should return null when getFloor doesnt return an object', () => { - const bid = {getFloor: () => 2}; + const bid = { getFloor: () => 2 }; const result = getBidFloor(bid); expect(result).to.be.null; }); it('should return null when floor is not a number', () => { const bid = { - getFloor: () => ({floor: 'string', currency: 'USD'}) + getFloor: () => ({ floor: 'string', currency: 'USD' }) }; const result = getBidFloor(bid); expect(result).to.be.null; @@ -432,7 +432,7 @@ describe('driftpixelBidAdapter', () => { it('should return null when currency is not USD', () => { const bid = { - getFloor: () => ({floor: 5, currency: 'EUR'}) + getFloor: () => ({ floor: 5, currency: 'EUR' }) }; const result = getBidFloor(bid); expect(result).to.be.null; @@ -440,7 +440,7 @@ describe('driftpixelBidAdapter', () => { it('should return floor value when everything is correct', () => { const bid = { - getFloor: () => ({floor: 5, currency: 'USD'}) + getFloor: () => ({ floor: 5, currency: 'USD' }) }; const result = getBidFloor(bid); expect(result).to.equal(5); diff --git a/test/spec/modules/dsaControl_spec.js b/test/spec/modules/dsaControl_spec.js index a1f8f88f23e..0522524051b 100644 --- a/test/spec/modules/dsaControl_spec.js +++ b/test/spec/modules/dsaControl_spec.js @@ -1,7 +1,7 @@ -import {addBidResponseHook, setMetaDsa, reset} from '../../../modules/dsaControl.js'; +import { addBidResponseHook, setMetaDsa, reset } from '../../../modules/dsaControl.js'; import { REJECTION_REASON } from 'src/constants.js'; -import {auctionManager} from '../../../src/auctionManager.js'; -import {AuctionIndex} from '../../../src/auctionIndex.js'; +import { auctionManager } from '../../../src/auctionManager.js'; +import { AuctionIndex } from '../../../src/auctionIndex.js'; describe('DSA transparency', () => { let sandbox; @@ -25,7 +25,7 @@ describe('DSA transparency', () => { } auction = { getAuctionId: () => auctionId, - getFPD: () => ({global: fpd}) + getFPD: () => ({ global: fpd }) } sandbox.stub(auctionManager, 'index').get(() => new AuctionIndex(() => [auction])); }); @@ -46,7 +46,7 @@ describe('DSA transparency', () => { describe(`when regs.ext.dsa.dsarequired is ${required} (required)`, () => { beforeEach(() => { fpd = { - regs: {ext: {dsa: {dsarequired: required}}} + regs: { ext: { dsa: { dsarequired: required } } } }; }); @@ -55,7 +55,7 @@ describe('DSA transparency', () => { }); it('should accept bids that do', () => { - bid.meta = {dsa: {}}; + bid.meta = { dsa: {} }; expectAcceptance(); }); @@ -65,12 +65,12 @@ describe('DSA transparency', () => { }); it('should reject bids with adrender = 0 (advertiser will not render)', () => { - bid.meta = {dsa: {adrender: 0}}; + bid.meta = { dsa: { adrender: 0 } }; expectRejection(REJECTION_REASON.DSA_MISMATCH); }); it('should accept bids with adrender = 1 (advertiser will render)', () => { - bid.meta = {dsa: {adrender: 1}}; + bid.meta = { dsa: { adrender: 1 } }; expectAcceptance(); }); }); @@ -80,12 +80,12 @@ describe('DSA transparency', () => { }); it('should reject bids with adrender = 1 (advertiser will render)', () => { - bid.meta = {dsa: {adrender: 1}}; + bid.meta = { dsa: { adrender: 1 } }; expectRejection(REJECTION_REASON.DSA_MISMATCH); }); it('should accept bids with adrender = 0 (advertiser will not render)', () => { - bid.meta = {dsa: {adrender: 0}}; + bid.meta = { dsa: { adrender: 0 } }; expectAcceptance(); }) }) @@ -96,7 +96,7 @@ describe('DSA transparency', () => { beforeEach(() => { if (required != null) { fpd = { - regs: {ext: {dsa: {dsarequired: required}}} + regs: { ext: { dsa: { dsarequired: required } } } } } }); diff --git a/test/spec/modules/dspxBidAdapter_spec.js b/test/spec/modules/dspxBidAdapter_spec.js index 34bf9de292c..2064ae2e67d 100644 --- a/test/spec/modules/dspxBidAdapter_spec.js +++ b/test/spec/modules/dspxBidAdapter_spec.js @@ -3,7 +3,7 @@ import { config } from 'src/config.js'; import { spec } from 'modules/dspxBidAdapter.js'; import { newBidder } from 'src/adapters/bidderFactory.js'; import { deepClone } from '../../../src/utils.js'; -import {BANNER} from '../../../src/mediaTypes.js'; +import { BANNER } from '../../../src/mediaTypes.js'; const ENDPOINT_URL = 'https://buyer.dspx.tv/request/'; const ENDPOINT_URL_DEV = 'https://dcbuyer.dspx.tv/request/'; @@ -282,7 +282,7 @@ describe('dspxAdapter', function () { 'dev': { 'endpoint': 'http://localhost', 'placement': '107', - 'pfilter': {'test': 1} + 'pfilter': { 'test': 1 } } }, 'mediaTypes': { @@ -316,7 +316,7 @@ describe('dspxAdapter', function () { }, gdprConsent: { consentString: 'BOJ/P2HOJ/P2HABABMAAAAAZ+A==', - vendorData: {someData: 'value'}, + vendorData: { someData: 'value' }, gdprApplies: true } }; @@ -328,7 +328,7 @@ describe('dspxAdapter', function () { }, gdprConsent: { consentString: 'BOJ/P2HOJ/P2HABABMAAAAAZ+A==', - vendorData: {someData: 'value'}, + vendorData: { someData: 'value' }, gdprApplies: true }, ortb2: { @@ -427,7 +427,7 @@ describe('dspxAdapter', function () { }); // bidfloor tests - const getFloorResponse = {currency: 'EUR', floor: 5}; + const getFloorResponse = { currency: 'EUR', floor: 5 }; let testBidRequest = deepClone(bidRequests[1]); let floorRequest = spec.buildRequests([testBidRequest], bidderRequestWithoutGdpr)[0]; @@ -477,7 +477,7 @@ describe('dspxAdapter', function () { }, gdprConsent: { consentString: 'BOJ/P2HOJ/P2HABABMAAAAAZ+A==', - vendorData: {someData: 'value'}, + vendorData: { someData: 'value' }, gdprApplies: true } }; @@ -516,7 +516,7 @@ describe('dspxAdapter', function () { segclass: 'v1', }, segment: [ - {id: '717'}, {id: '808'}, + { id: '717' }, { id: '808' }, ] } ] @@ -537,13 +537,13 @@ describe('dspxAdapter', function () { segment: [] }, { - segment: [{id: ''}] + segment: [{ id: '' }] }, { - segment: [{id: null}] + segment: [{ id: null }] }, { - segment: [{id: 'dummy'}, {id: '123'}] + segment: [{ id: 'dummy' }, { id: '123' }] }, { ext: { @@ -599,7 +599,7 @@ describe('dspxAdapter', function () { 'ttl': 60, 'netRevenue': true, 'zone': '6682', - 'renderer': {id: 1, url: '//player.example.com', options: {}} + 'renderer': { id: 1, url: '//player.example.com', options: {} } } }; const serverVideoResponseVastUrl = { @@ -616,7 +616,7 @@ describe('dspxAdapter', function () { 'zone': '6682', 'vastUrl': 'https://local/vasturl1', 'videoCacheKey': 'cache_123', - 'bid_appendix': {'someField': 'someValue'} + 'bid_appendix': { 'someField': 'someValue' } } }; @@ -632,7 +632,7 @@ describe('dspxAdapter', function () { ttl: 60, type: 'sspHTML', ad: '', - meta: {advertiserDomains: ['bdomain']}, + meta: { advertiserDomains: ['bdomain'] }, }, { requestId: '23beaa6af6cdde', cpm: 0.5, @@ -646,7 +646,7 @@ describe('dspxAdapter', function () { type: 'vast2', vastXml: '{"reason":7001,"status":"accepted"}', mediaType: 'video', - meta: {advertiserDomains: []}, + meta: { advertiserDomains: [] }, renderer: {} }, { requestId: '23beaa6af6cdde', @@ -662,7 +662,7 @@ describe('dspxAdapter', function () { vastUrl: 'https://local/vasturl1', videoCacheKey: 'cache_123', mediaType: 'video', - meta: {advertiserDomains: []}, + meta: { advertiserDomains: [] }, someField: 'someValue' }]; @@ -762,17 +762,17 @@ describe('dspxAdapter', function () { expect(userSync.type).to.be.equal('iframe'); }); it(`we have valid sync url for iframe`, function () { - const [userSync] = spec.getUserSyncs({ iframeEnabled: true }, serverResponses, {consentString: 'anyString'}); + const [userSync] = spec.getUserSyncs({ iframeEnabled: true }, serverResponses, { consentString: 'anyString' }); expect(userSync.url).to.be.equal('anyIframeUrl?a=1&gdpr_consent=anyString') expect(userSync.type).to.be.equal('iframe'); }); it(`we have valid sync url for image`, function () { - const [userSync] = spec.getUserSyncs({ pixelEnabled: true }, serverResponses, {gdprApplies: true, consentString: 'anyString'}); + const [userSync] = spec.getUserSyncs({ pixelEnabled: true }, serverResponses, { gdprApplies: true, consentString: 'anyString' }); expect(userSync.url).to.be.equal('anyImageUrl?gdpr=1&gdpr_consent=anyString') expect(userSync.type).to.be.equal('image'); }); it(`we have valid sync url for image and iframe`, function () { - const userSync = spec.getUserSyncs({ iframeEnabled: true, pixelEnabled: true }, serverResponses, {gdprApplies: true, consentString: 'anyString'}); + const userSync = spec.getUserSyncs({ iframeEnabled: true, pixelEnabled: true }, serverResponses, { gdprApplies: true, consentString: 'anyString' }); expect(userSync.length).to.be.equal(3); expect(userSync[0].url).to.be.equal('anyIframeUrl?a=1&gdpr=1&gdpr_consent=anyString') expect(userSync[0].type).to.be.equal('iframe'); diff --git a/test/spec/modules/dxkultureBidAdapter_spec.js b/test/spec/modules/dxkultureBidAdapter_spec.js index ad1adf18d02..8e6f2d78ccd 100644 --- a/test/spec/modules/dxkultureBidAdapter_spec.js +++ b/test/spec/modules/dxkultureBidAdapter_spec.js @@ -1,6 +1,6 @@ -import {expect} from 'chai'; -import {spec, SYNC_URL} from 'modules/dxkultureBidAdapter.js'; -import {BANNER, VIDEO} from 'src/mediaTypes.js'; +import { expect } from 'chai'; +import { spec, SYNC_URL } from 'modules/dxkultureBidAdapter.js'; +import { BANNER, VIDEO } from 'src/mediaTypes.js'; const getBannerRequest = () => { return { @@ -20,7 +20,7 @@ const getBannerRequest = () => { mediaTypes: { banner: { sizes: [ - [ 300, 250 ], + [300, 250], ] } }, @@ -421,7 +421,7 @@ describe('dxkultureBidAdapter', function() { beforeEach(function() { bidderBannerRequest = getBannerRequest(); - mockBidderRequest = {refererInfo: {}}; + mockBidderRequest = { refererInfo: {} }; bidRequestsWithMediaTypes = [{ bidder: 'dxkulture', @@ -539,7 +539,7 @@ describe('dxkultureBidAdapter', function() { }); it('handles empty response', function () { - const EMPTY_RESP = Object.assign({}, bidderResponse, {'body': {}}); + const EMPTY_RESP = Object.assign({}, bidderResponse, { 'body': {} }); const bids = spec.interpretResponse(EMPTY_RESP, bidRequest); expect(bids).to.be.empty; @@ -574,32 +574,36 @@ describe('dxkultureBidAdapter', function() { }); it('handles empty response', function () { - const EMPTY_RESP = Object.assign({}, bidderResponse, {'body': {}}); + const EMPTY_RESP = Object.assign({}, bidderResponse, { 'body': {} }); const bids = spec.interpretResponse(EMPTY_RESP, bidRequest); expect(bids).to.be.empty; }); it('should return no bids if the response "nurl" and "adm" are missing', function () { - const SERVER_RESP = Object.assign({}, bidderResponse, {'body': { - seatbid: [{ - bid: [{ - price: 6.01 + const SERVER_RESP = Object.assign({}, bidderResponse, { + 'body': { + seatbid: [{ + bid: [{ + price: 6.01 + }] }] - }] - }}); + } + }); const bids = spec.interpretResponse(SERVER_RESP, bidRequest); expect(bids.length).to.equal(0); }); it('should return no bids if the response "price" is missing', function () { - const SERVER_RESP = Object.assign({}, bidderResponse, {'body': { - seatbid: [{ - bid: [{ - adm: '' + const SERVER_RESP = Object.assign({}, bidderResponse, { + 'body': { + seatbid: [{ + bid: [{ + adm: '' + }] }] - }] - }}); + } + }); const bids = spec.interpretResponse(SERVER_RESP, bidRequest); expect(bids.length).to.equal(0); }); @@ -619,13 +623,13 @@ describe('dxkultureBidAdapter', function() { expect(opts).to.be.an('array').that.is.empty; }); it('returns non if sync is not allowed', function () { - const opts = spec.getUserSyncs({iframeEnabled: false, pixelEnabled: false}); + const opts = spec.getUserSyncs({ iframeEnabled: false, pixelEnabled: false }); expect(opts).to.be.an('array').that.is.empty; }); it('iframe sync enabled should return results', function () { - const opts = spec.getUserSyncs({iframeEnabled: true, pixelEnabled: false}, [bidderResponse]); + const opts = spec.getUserSyncs({ iframeEnabled: true, pixelEnabled: false }, [bidderResponse]); expect(opts.length).to.equal(1); expect(opts[0].type).to.equal('iframe'); @@ -633,7 +637,7 @@ describe('dxkultureBidAdapter', function() { }); it('pixel sync enabled should return results', function () { - const opts = spec.getUserSyncs({iframeEnabled: false, pixelEnabled: true}, [bidderResponse]); + const opts = spec.getUserSyncs({ iframeEnabled: false, pixelEnabled: true }, [bidderResponse]); expect(opts.length).to.equal(1); expect(opts[0].type).to.equal('image'); @@ -641,7 +645,7 @@ describe('dxkultureBidAdapter', function() { }); it('all sync enabled should prioritize iframe', function () { - const opts = spec.getUserSyncs({iframeEnabled: true, pixelEnabled: true}, [bidderResponse]); + const opts = spec.getUserSyncs({ iframeEnabled: true, pixelEnabled: true }, [bidderResponse]); expect(opts.length).to.equal(1); }); diff --git a/test/spec/modules/dxtechBidAdapter_spec.js b/test/spec/modules/dxtechBidAdapter_spec.js index 216610e8246..758aea40d4a 100644 --- a/test/spec/modules/dxtechBidAdapter_spec.js +++ b/test/spec/modules/dxtechBidAdapter_spec.js @@ -1,5 +1,5 @@ -import {expect} from 'chai'; -import {spec} from 'modules/dxtechBidAdapter.js'; +import { expect } from 'chai'; +import { spec } from 'modules/dxtechBidAdapter.js'; const getBannerRequest = () => { return { @@ -19,7 +19,7 @@ const getBannerRequest = () => { mediaTypes: { banner: { sizes: [ - [ 300, 250 ], + [300, 250], ] } }, @@ -393,7 +393,7 @@ describe('dxtechBidAdapter', function() { let mockBidderRequest; beforeEach(function() { - mockBidderRequest = {refererInfo: {}}; + mockBidderRequest = { refererInfo: {} }; bidRequestsWithMediaTypes = [{ bidder: 'dxtech', @@ -494,7 +494,7 @@ describe('dxtechBidAdapter', function() { }); it('handles empty response', function () { - const EMPTY_RESP = Object.assign({}, bidderResponse, {'body': {}}); + const EMPTY_RESP = Object.assign({}, bidderResponse, { 'body': {} }); const bids = spec.interpretResponse(EMPTY_RESP, bidRequest); expect(bids).to.be.empty; @@ -530,32 +530,36 @@ describe('dxtechBidAdapter', function() { }); it('handles empty response', function () { - const EMPTY_RESP = Object.assign({}, bidderResponse, {'body': {}}); + const EMPTY_RESP = Object.assign({}, bidderResponse, { 'body': {} }); const bids = spec.interpretResponse(EMPTY_RESP, bidRequest); expect(bids).to.be.empty; }); it('should return no bids if the response "nurl" and "adm" are missing', function () { - const SERVER_RESP = Object.assign({}, bidderResponse, {'body': { - seatbid: [{ - bid: [{ - price: 6.01 + const SERVER_RESP = Object.assign({}, bidderResponse, { + 'body': { + seatbid: [{ + bid: [{ + price: 6.01 + }] }] - }] - }}); + } + }); const bids = spec.interpretResponse(SERVER_RESP, bidRequest); expect(bids.length).to.equal(0); }); it('should return no bids if the response "price" is missing', function () { - const SERVER_RESP = Object.assign({}, bidderResponse, {'body': { - seatbid: [{ - bid: [{ - adm: '' + const SERVER_RESP = Object.assign({}, bidderResponse, { + 'body': { + seatbid: [{ + bid: [{ + adm: '' + }] }] - }] - }}); + } + }); const bids = spec.interpretResponse(SERVER_RESP, bidRequest); expect(bids.length).to.equal(0); }); @@ -575,12 +579,12 @@ describe('dxtechBidAdapter', function() { }); it('returns none if sync is not allowed', function () { - const opts = spec.getUserSyncs({iframeEnabled: false, pixelEnabled: false}); + const opts = spec.getUserSyncs({ iframeEnabled: false, pixelEnabled: false }); expect(opts).to.be.an('array').that.is.empty; }); it('iframe sync enabled should return results', function () { - const opts = spec.getUserSyncs({iframeEnabled: true, pixelEnabled: false}, [bidderResponse]); + const opts = spec.getUserSyncs({ iframeEnabled: true, pixelEnabled: false }, [bidderResponse]); expect(opts.length).to.equal(1); expect(opts[0].type).to.equal('iframe'); @@ -588,7 +592,7 @@ describe('dxtechBidAdapter', function() { }); it('pixel sync enabled should return results', function () { - const opts = spec.getUserSyncs({iframeEnabled: false, pixelEnabled: true}, [bidderResponse]); + const opts = spec.getUserSyncs({ iframeEnabled: false, pixelEnabled: true }, [bidderResponse]); expect(opts.length).to.equal(1); expect(opts[0].type).to.equal('image'); @@ -596,7 +600,7 @@ describe('dxtechBidAdapter', function() { }); it('all sync enabled should prioritize iframe', function () { - const opts = spec.getUserSyncs({iframeEnabled: true, pixelEnabled: true}, [bidderResponse]); + const opts = spec.getUserSyncs({ iframeEnabled: true, pixelEnabled: true }, [bidderResponse]); expect(opts.length).to.equal(1); }); diff --git a/test/spec/modules/eids_spec.js b/test/spec/modules/eids_spec.js index 17d2b5161b0..9b151835aca 100644 --- a/test/spec/modules/eids_spec.js +++ b/test/spec/modules/eids_spec.js @@ -1,4 +1,4 @@ -import {createEidsArray} from 'modules/userId/eids.js'; +import { createEidsArray } from 'modules/userId/eids.js'; describe('eids array generation for known sub-modules', function () { it('pubProvidedId', function () { diff --git a/test/spec/modules/eightPodAnalyticsAdapter_spec.js b/test/spec/modules/eightPodAnalyticsAdapter_spec.js index 3f798344d0d..5d986125bda 100644 --- a/test/spec/modules/eightPodAnalyticsAdapter_spec.js +++ b/test/spec/modules/eightPodAnalyticsAdapter_spec.js @@ -132,7 +132,7 @@ describe('eightPodAnalyticAdapter', function() { }); it('should add event to the queue', function() { - getContextStub.returns({adUnitCode: {}}); + getContextStub.returns({ adUnitCode: {} }); const event1 = { detail: { diff --git a/test/spec/modules/eightPodBidAdapter_spec.js b/test/spec/modules/eightPodBidAdapter_spec.js index 0259f782fe2..67ef98b2117 100644 --- a/test/spec/modules/eightPodBidAdapter_spec.js +++ b/test/spec/modules/eightPodBidAdapter_spec.js @@ -81,7 +81,8 @@ describe('eightPodBidAdapter', function () { dnt: 1, js: 1, } - } } + } + } }) it('should return an empty array when no bid requests', function () { diff --git a/test/spec/modules/emtvBidAdapter_spec.js b/test/spec/modules/emtvBidAdapter_spec.js index 0e91f3fa719..a75522607c1 100644 --- a/test/spec/modules/emtvBidAdapter_spec.js +++ b/test/spec/modules/emtvBidAdapter_spec.js @@ -485,7 +485,7 @@ describe('EMTVBidAdapter', function () { const syncData = spec.getUserSyncs({}, {}, { consentString: 'ALL', gdprApplies: true, - }, {}); + }, undefined); expect(syncData).to.be.an('array').which.is.not.empty; expect(syncData[0]).to.be.an('object') expect(syncData[0].type).to.be.a('string') @@ -494,9 +494,7 @@ describe('EMTVBidAdapter', function () { expect(syncData[0].url).to.equal(`${syncUrl}/image?pbjs=1&gdpr=1&gdpr_consent=ALL&coppa=0`) }); it('Should return array of objects with proper sync config , include CCPA', function() { - const syncData = spec.getUserSyncs({}, {}, {}, { - consentString: '1---' - }); + const syncData = spec.getUserSyncs({}, {}, {}, '1---'); expect(syncData).to.be.an('array').which.is.not.empty; expect(syncData[0]).to.be.an('object') expect(syncData[0].type).to.be.a('string') @@ -505,7 +503,7 @@ describe('EMTVBidAdapter', function () { expect(syncData[0].url).to.equal(`${syncUrl}/image?pbjs=1&ccpa_consent=1---&coppa=0`) }); it('Should return array of objects with proper sync config , include GPP', function() { - const syncData = spec.getUserSyncs({}, {}, {}, {}, { + const syncData = spec.getUserSyncs({}, {}, {}, undefined, { gppString: 'abc123', applicableSections: [8] }); diff --git a/test/spec/modules/enrichmentLiftMeasurement_spec.js b/test/spec/modules/enrichmentLiftMeasurement_spec.js index 4473c851fbf..e144eb3d3c3 100644 --- a/test/spec/modules/enrichmentLiftMeasurement_spec.js +++ b/test/spec/modules/enrichmentLiftMeasurement_spec.js @@ -1,6 +1,6 @@ import { expect } from "chai"; import { getCalculatedSubmodules, internals, init, reset, storeSplitsMethod, storeTestConfig, suppressionMethod, getStoredTestConfig, compareConfigs, STORAGE_KEY } from "../../../modules/enrichmentLiftMeasurement/index.js"; -import {server} from 'test/mocks/xhr.js'; +import { server } from 'test/mocks/xhr.js'; import { config } from "../../../src/config.js" import { isInteger } from "../../../src/utils.js"; import { ACTIVITY_ENRICH_EIDS } from "../../../src/activities/activities.js"; @@ -35,9 +35,11 @@ describe('enrichmentLiftMeasurement', () => { const mathRandomStub = sinon.stub(Math, 'random').callsFake(() => { return fixedRandoms[callIndex++]; }); - config.setConfig({ enrichmentLiftMeasurement: { - modules: modulesConfig - }}); + config.setConfig({ + enrichmentLiftMeasurement: { + modules: modulesConfig + } + }); const results = []; for (let i = 0; i < TEST_SAMPLE_SIZE; i++) { @@ -45,7 +47,7 @@ describe('enrichmentLiftMeasurement', () => { } modulesConfig.forEach((idSystem) => { const passedIdSystemsCount = results.filter((execution) => { - const item = execution.find(({name}) => idSystem.name === name) + const item = execution.find(({ name }) => idSystem.name === name) return item?.enabled }).length const marginOfError = Number(Math.abs(passedIdSystemsCount / TEST_SAMPLE_SIZE - idSystem.percentage).toFixed(2)); @@ -61,14 +63,16 @@ describe('enrichmentLiftMeasurement', () => { [suppressionMethod.SUBMODULES]: true }).forEach(([method, value]) => { it(method, () => { - config.setConfig({ enrichmentLiftMeasurement: { - suppression: method, - modules: [ - { name: 'idSystem', percentage: 0 } - ] - }}); + config.setConfig({ + enrichmentLiftMeasurement: { + suppression: method, + modules: [ + { name: 'idSystem', percentage: 0 } + ] + } + }); init(); - expect(isActivityAllowed(ACTIVITY_ENRICH_EIDS, activityParams(MODULE_TYPE_UID, 'idSystem', {init: false}))).to.eql(value); + expect(isActivityAllowed(ACTIVITY_ENRICH_EIDS, activityParams(MODULE_TYPE_UID, 'idSystem', { init: false }))).to.eql(value); }); }); }); @@ -84,13 +88,15 @@ describe('enrichmentLiftMeasurement', () => { beforeEach(() => { getCalculatedSubmodulesStub = sinon.stub(internals, 'getCalculatedSubmodules'); - config.setConfig({ enrichmentLiftMeasurement: { - testRun: TEST_RUN_ID, - storeSplits: storeSplitsMethod.SESSION_STORAGE, - modules: [ - { name: 'idSystem', percentage: 1 } - ] - }}); + config.setConfig({ + enrichmentLiftMeasurement: { + testRun: TEST_RUN_ID, + storeSplits: storeSplitsMethod.SESSION_STORAGE, + modules: [ + { name: 'idSystem', percentage: 1 } + ] + } + }); }); afterEach(() => { @@ -115,7 +121,7 @@ describe('enrichmentLiftMeasurement', () => { }); it('should store config if not present', () => { - const stubCalculation = mockConfig.map(module => ({...module, percentage: 0.1})); + const stubCalculation = mockConfig.map(module => ({ ...module, percentage: 0.1 })); getCalculatedSubmodulesStub.returns(stubCalculation); const fakeStorageManager = { sessionStorageIsEnabled: () => true, @@ -125,12 +131,12 @@ describe('enrichmentLiftMeasurement', () => { init(fakeStorageManager); sinon.assert.calledOnce(fakeStorageManager.setDataInSessionStorage); sinon.assert.calledOnce(getCalculatedSubmodulesStub); - const expectedArg = {testRun: TEST_RUN_ID, modules: stubCalculation}; + const expectedArg = { testRun: TEST_RUN_ID, modules: stubCalculation }; expect(fakeStorageManager.setDataInSessionStorage.getCall(0).args[1]).to.deep.eql(JSON.stringify(expectedArg)); }); it('should update config if present is different', () => { - const stubCalculation = mockConfig.map(module => ({...module, percentage: 0.1})); + const stubCalculation = mockConfig.map(module => ({ ...module, percentage: 0.1 })); getCalculatedSubmodulesStub.returns(stubCalculation); const previousTestConfig = { modules: mockConfig, @@ -141,11 +147,13 @@ describe('enrichmentLiftMeasurement', () => { getDataFromSessionStorage: sinon.stub().returns(JSON.stringify(previousTestConfig)), setDataInSessionStorage: sinon.stub() }; - config.setConfig({ enrichmentLiftMeasurement: { - testRun: TEST_RUN_ID, - storeSplits: storeSplitsMethod.SESSION_STORAGE, - modules: mockConfig.map(module => ({...module, percentage: 0.1})) - }}); + config.setConfig({ + enrichmentLiftMeasurement: { + testRun: TEST_RUN_ID, + storeSplits: storeSplitsMethod.SESSION_STORAGE, + modules: mockConfig.map(module => ({ ...module, percentage: 0.1 })) + } + }); init(fakeStorageManager); @@ -161,20 +169,22 @@ describe('enrichmentLiftMeasurement', () => { url: 'https://localhost:9999/endpoint', analyticsType: 'endpoint' }); - config.setConfig({ enrichmentLiftMeasurement: { - modules: mockConfig, - testRun: TEST_RUN_ID, - storeSplits: storeSplitsMethod.PAGE - }}); + config.setConfig({ + enrichmentLiftMeasurement: { + modules: mockConfig, + testRun: TEST_RUN_ID, + storeSplits: storeSplitsMethod.PAGE + } + }); init(); const eventType = EVENTS.BID_WON; - adapter.track({eventType}); + adapter.track({ eventType }); const result = JSON.parse(server.requests[0].requestBody); - sinon.assert.match(result, {labels: {[TEST_RUN_ID]: mockConfig}, eventType}); + sinon.assert.match(result, { labels: { [TEST_RUN_ID]: mockConfig }, eventType }); disableAjaxForAnalytics(); }); @@ -227,15 +237,15 @@ describe('enrichmentLiftMeasurement', () => { const oldConfig = { testRun: 'AB1', modules: [ - {name: 'idSystem1', percentage: 1.0, enabled: true}, - {name: 'idSystem2', percentage: 0.3, enabled: false}, + { name: 'idSystem1', percentage: 1.0, enabled: true }, + { name: 'idSystem2', percentage: 0.3, enabled: false }, ] } const newConfig = { testRun: 'AB1', modules: [ - {name: 'idSystem2', percentage: 0.3}, - {name: 'idSystem1', percentage: 1.0}, + { name: 'idSystem2', percentage: 0.3 }, + { name: 'idSystem1', percentage: 1.0 }, ] } expect(compareConfigs(newConfig, oldConfig)).to.eql(true); @@ -245,15 +255,15 @@ describe('enrichmentLiftMeasurement', () => { const oldConfig = { testRun: 'AB1', modules: [ - {name: 'idSystem1', percentage: 1.0, enabled: true}, - {name: 'idSystem2', percentage: 0.3, enabled: false}, + { name: 'idSystem1', percentage: 1.0, enabled: true }, + { name: 'idSystem2', percentage: 0.3, enabled: false }, ] } const newConfig = { testRun: 'AB2', modules: [ - {name: 'idSystem2', percentage: 0.3}, - {name: 'idSystem1', percentage: 1.0}, + { name: 'idSystem2', percentage: 0.3 }, + { name: 'idSystem1', percentage: 1.0 }, ] } expect(compareConfigs(newConfig, oldConfig)).to.eql(false); diff --git a/test/spec/modules/eplanningBidAdapter_spec.js b/test/spec/modules/eplanningBidAdapter_spec.js index 15f05fc12a5..04e742315d1 100644 --- a/test/spec/modules/eplanningBidAdapter_spec.js +++ b/test/spec/modules/eplanningBidAdapter_spec.js @@ -2,12 +2,12 @@ import { expect } from 'chai'; import { spec, storage } from 'modules/eplanningBidAdapter.js'; import { newBidder } from 'src/adapters/bidderFactory.js'; import { config } from 'src/config.js'; -import {init, getIds} from 'modules/userId/index.js'; +import { init, getIds } from 'modules/userId/index.js'; import * as utils from 'src/utils.js'; -import {hook} from '../../../src/hook.js'; -import {getGlobal} from '../../../src/prebidGlobal.js'; +import { hook } from '../../../src/hook.js'; +import { getGlobal } from '../../../src/prebidGlobal.js'; import { makeSlot } from '../integration/faker/googletag.js'; -import {BANNER, VIDEO} from '../../../src/mediaTypes.js'; +import { BANNER, VIDEO } from '../../../src/mediaTypes.js'; import { internal, resetWinDimensions } from '../../../src/utils.js'; describe('E-Planning Adapter', function () { @@ -1138,7 +1138,7 @@ describe('E-Planning Adapter', function () { return element; }, observe: (element) => { - intersectionCallback([{'target': {'id': element.id}, 'isIntersecting': params[element.id].isIntersecting, 'intersectionRatio': params[element.id].ratio, 'boundingClientRect': {'width': params[element.id].width, 'height': params[element.id].height}}]); + intersectionCallback([{ 'target': { 'id': element.id }, 'isIntersecting': params[element.id].isIntersecting, 'intersectionRatio': params[element.id].ratio, 'boundingClientRect': { 'width': params[element.id].width, 'height': params[element.id].height } }]); }, }; }; @@ -1278,7 +1278,7 @@ describe('E-Planning Adapter', function () { let respuesta; beforeEach(function () { createElementVisible(); - setIntersectionObserverMock({[ADUNIT_CODE_VIEW]: {'ratio': 1, 'isIntersecting': true, 'width': 200, 'height': 200}}); + setIntersectionObserverMock({ [ADUNIT_CODE_VIEW]: { 'ratio': 1, 'isIntersecting': true, 'width': 200, 'height': 200 } }); }); it('when you have a render', function() { respuesta = spec.buildRequests(bidRequests, bidderRequest); @@ -1316,7 +1316,7 @@ describe('E-Planning Adapter', function () { let respuesta; beforeEach(function () { createElementOutOfView(); - setIntersectionObserverMock({[ADUNIT_CODE_VIEW]: {'ratio': 0, 'isIntersecting': false, 'width': 200, 'height': 200}}); + setIntersectionObserverMock({ [ADUNIT_CODE_VIEW]: { 'ratio': 0, 'isIntersecting': false, 'width': 200, 'height': 200 } }); }); it('when you have a render', function() { @@ -1342,7 +1342,7 @@ describe('E-Planning Adapter', function () { let respuesta; it('should register visibility with more than 50%', function() { createPartiallyVisibleElement(); - setIntersectionObserverMock({[ADUNIT_CODE_VIEW]: {'ratio': 0.6, 'isIntersecting': true, 'width': 200, 'height': 200}}); + setIntersectionObserverMock({ [ADUNIT_CODE_VIEW]: { 'ratio': 0.6, 'isIntersecting': true, 'width': 200, 'height': 200 } }); respuesta = spec.buildRequests(bidRequests, bidderRequest); clock.tick(1005); @@ -1351,7 +1351,7 @@ describe('E-Planning Adapter', function () { }); it('you should not register visibility with less than 50%', function() { createPartiallyInvisibleElement(); - setIntersectionObserverMock({[ADUNIT_CODE_VIEW]: {'ratio': 0.4, 'isIntersecting': true, 'width': 200, 'height': 200}}); + setIntersectionObserverMock({ [ADUNIT_CODE_VIEW]: { 'ratio': 0.4, 'isIntersecting': true, 'width': 200, 'height': 200 } }); respuesta = spec.buildRequests(bidRequests, bidderRequest); clock.tick(1005); @@ -1366,7 +1366,7 @@ describe('E-Planning Adapter', function () { const divId = 'div-gpt-ad-123'; createPartiallyVisibleElement(divId); window.googletag.pubads().setSlots([makeSlot({ code, divId })]); - setIntersectionObserverMock({[divId]: {'ratio': 0.6, 'isIntersecting': true, 'width': 200, 'height': 200}}); + setIntersectionObserverMock({ [divId]: { 'ratio': 0.6, 'isIntersecting': true, 'width': 200, 'height': 200 } }); respuesta = spec.buildRequests(bidRequests, bidderRequest); clock.tick(1005); @@ -1381,7 +1381,7 @@ describe('E-Planning Adapter', function () { }); it('if the width is zero but the height is within the range', function() { element.style.width = '0px'; - setIntersectionObserverMock({[ADUNIT_CODE_VIEW]: {'ratio': 0.4, 'isIntersecting': true, 'width': 200, 'height': 200}}); + setIntersectionObserverMock({ [ADUNIT_CODE_VIEW]: { 'ratio': 0.4, 'isIntersecting': true, 'width': 200, 'height': 200 } }); spec.buildRequests(bidRequests, bidderRequest) clock.tick(1005); @@ -1390,7 +1390,7 @@ describe('E-Planning Adapter', function () { }); it('if the height is zero but the width is within the range', function() { element.style.height = '0px'; - setIntersectionObserverMock({[ADUNIT_CODE_VIEW]: {'ratio': 1, 'isIntersecting': true, 'width': 500, 'height': 0}}); + setIntersectionObserverMock({ [ADUNIT_CODE_VIEW]: { 'ratio': 1, 'isIntersecting': true, 'width': 500, 'height': 0 } }); spec.buildRequests(bidRequests, bidderRequest) clock.tick(1005); @@ -1400,7 +1400,7 @@ describe('E-Planning Adapter', function () { it('if both are zero', function() { element.style.height = '0px'; element.style.width = '0px'; - setIntersectionObserverMock({[ADUNIT_CODE_VIEW]: {'ratio': 1, 'isIntersecting': true, 'width': 0, 'height': 0}}); + setIntersectionObserverMock({ [ADUNIT_CODE_VIEW]: { 'ratio': 1, 'isIntersecting': true, 'width': 0, 'height': 0 } }); spec.buildRequests(bidRequests, bidderRequest) clock.tick(1005); @@ -1439,9 +1439,9 @@ describe('E-Planning Adapter', function () { createElementVisible(ADUNIT_CODE_VIEW2); createElementVisible(ADUNIT_CODE_VIEW3); setIntersectionObserverMock({ - [ADUNIT_CODE_VIEW]: {'ratio': 1, 'isIntersecting': true, 'width': 200, 'height': 200}, - [ADUNIT_CODE_VIEW2]: {'ratio': 1, 'isIntersecting': true, 'width': 200, 'height': 200}, - [ADUNIT_CODE_VIEW3]: {'ratio': 1, 'isIntersecting': true, 'width': 200, 'height': 200} + [ADUNIT_CODE_VIEW]: { 'ratio': 1, 'isIntersecting': true, 'width': 200, 'height': 200 }, + [ADUNIT_CODE_VIEW2]: { 'ratio': 1, 'isIntersecting': true, 'width': 200, 'height': 200 }, + [ADUNIT_CODE_VIEW3]: { 'ratio': 1, 'isIntersecting': true, 'width': 200, 'height': 200 } }); respuesta = spec.buildRequests(bidRequestMultiple, bidderRequest); clock.tick(1005); @@ -1456,9 +1456,9 @@ describe('E-Planning Adapter', function () { createElementOutOfView(ADUNIT_CODE_VIEW2); createElementOutOfView(ADUNIT_CODE_VIEW3); setIntersectionObserverMock({ - [ADUNIT_CODE_VIEW]: {'ratio': 0, 'isIntersecting': false, 'width': 200, 'height': 200}, - [ADUNIT_CODE_VIEW2]: {'ratio': 0, 'isIntersecting': false, 'width': 200, 'height': 200}, - [ADUNIT_CODE_VIEW3]: {'ratio': 0, 'isIntersecting': false, 'width': 200, 'height': 200} + [ADUNIT_CODE_VIEW]: { 'ratio': 0, 'isIntersecting': false, 'width': 200, 'height': 200 }, + [ADUNIT_CODE_VIEW2]: { 'ratio': 0, 'isIntersecting': false, 'width': 200, 'height': 200 }, + [ADUNIT_CODE_VIEW3]: { 'ratio': 0, 'isIntersecting': false, 'width': 200, 'height': 200 } }); respuesta = spec.buildRequests(bidRequestMultiple, bidderRequest); clock.tick(1005); @@ -1474,9 +1474,9 @@ describe('E-Planning Adapter', function () { createElementOutOfView(ADUNIT_CODE_VIEW2); createElementOutOfView(ADUNIT_CODE_VIEW3); setIntersectionObserverMock({ - [ADUNIT_CODE_VIEW]: {'ratio': 1, 'isIntersecting': true, 'width': 200, 'height': 200}, - [ADUNIT_CODE_VIEW2]: {'ratio': 0.3, 'isIntersecting': true, 'width': 200, 'height': 200}, - [ADUNIT_CODE_VIEW3]: {'ratio': 0, 'isIntersecting': false, 'width': 200, 'height': 200} + [ADUNIT_CODE_VIEW]: { 'ratio': 1, 'isIntersecting': true, 'width': 200, 'height': 200 }, + [ADUNIT_CODE_VIEW2]: { 'ratio': 0.3, 'isIntersecting': true, 'width': 200, 'height': 200 }, + [ADUNIT_CODE_VIEW3]: { 'ratio': 0, 'isIntersecting': false, 'width': 200, 'height': 200 } }); respuesta = spec.buildRequests(bidRequestMultiple, bidderRequest); clock.tick(1005); diff --git a/test/spec/modules/escalaxBidAdapter_spec.js b/test/spec/modules/escalaxBidAdapter_spec.js index 8a441a04b0b..dcabe3b1686 100644 --- a/test/spec/modules/escalaxBidAdapter_spec.js +++ b/test/spec/modules/escalaxBidAdapter_spec.js @@ -180,7 +180,7 @@ describe('escalaxAdapter', function () { }); it('should send the CCPA data in the request', async function () { - const serverRequest = spec.buildRequests([SIMPLE_BID_REQUEST], await addFPDToBidderRequest({...bidderRequest, ...{uspConsent: '1YYY'}})); + const serverRequest = spec.buildRequests([SIMPLE_BID_REQUEST], await addFPDToBidderRequest({ ...bidderRequest, ...{ uspConsent: '1YYY' } })); expect(serverRequest.data.regs.ext.us_privacy).to.equal('1YYY'); }); }); diff --git a/test/spec/modules/eskimiBidAdapter_spec.js b/test/spec/modules/eskimiBidAdapter_spec.js index a6c987aa72e..03d514b056f 100644 --- a/test/spec/modules/eskimiBidAdapter_spec.js +++ b/test/spec/modules/eskimiBidAdapter_spec.js @@ -1,5 +1,5 @@ -import {expect} from 'chai'; -import {spec} from 'modules/eskimiBidAdapter.js'; +import { expect } from 'chai'; +import { spec } from 'modules/eskimiBidAdapter.js'; import * as utils from 'src/utils'; const BANNER_BID = { @@ -165,8 +165,8 @@ describe('Eskimi bid adapter', function () { it('should properly forward ORTB blocking params', function () { let bid = utils.deepClone(BANNER_BID); bid = utils.mergeDeep(bid, { - params: {bcat: ['IAB1-1'], badv: ['example.com'], bapp: ['com.example']}, - mediaTypes: {banner: {battr: [1]}} + params: { bcat: ['IAB1-1'], badv: ['example.com'], bapp: ['com.example'] }, + mediaTypes: { banner: { battr: [1] } } }); const [request] = spec.buildRequests([bid], BIDDER_REQUEST); @@ -253,7 +253,7 @@ describe('Eskimi bid adapter', function () { const [request] = spec.buildRequests([bid], BIDDER_REQUEST); const response = utils.deepClone(BANNER_BID_RESPONSE); - const bids = spec.interpretResponse({body: response}, request); + const bids = spec.interpretResponse({ body: response }, request); expect(bids).to.be.an('array').that.is.not.empty; expect(bids[0].mediaType).to.equal('banner'); @@ -274,7 +274,7 @@ describe('Eskimi bid adapter', function () { const bid = utils.deepClone(BANNER_BID); const request = spec.buildRequests([bid], BIDDER_REQUEST)[0]; - const EMPTY_RESP = Object.assign({}, BANNER_BID_RESPONSE, {'body': {}}); + const EMPTY_RESP = Object.assign({}, BANNER_BID_RESPONSE, { 'body': {} }); const bids = spec.interpretResponse(EMPTY_RESP, request); expect(bids).to.be.empty; }); @@ -285,7 +285,7 @@ describe('Eskimi bid adapter', function () { const bid = utils.deepClone(VIDEO_BID); const [request] = spec.buildRequests([bid], BIDDER_REQUEST); - const bids = spec.interpretResponse({body: VIDEO_BID_RESPONSE}, request); + const bids = spec.interpretResponse({ body: VIDEO_BID_RESPONSE }, request); expect(bids).to.be.an('array').that.is.not.empty; expect(bids[0].mediaType).to.equal('video'); diff --git a/test/spec/modules/etargetBidAdapter_spec.js b/test/spec/modules/etargetBidAdapter_spec.js index c00856d4f57..d53629443ca 100644 --- a/test/spec/modules/etargetBidAdapter_spec.js +++ b/test/spec/modules/etargetBidAdapter_spec.js @@ -1,5 +1,5 @@ -import {assert, expect} from 'chai'; -import {spec} from 'modules/etargetBidAdapter.js'; +import { assert, expect } from 'chai'; +import { spec } from 'modules/etargetBidAdapter.js'; import { BANNER, VIDEO } from 'src/mediaTypes.js'; import { deepClone } from 'src/utils.js'; @@ -112,7 +112,7 @@ describe('etarget adapter', function () { describe('gdpr', function () { it('should send GDPR Consent data to etarget if gdprApplies', function () { const resultBids = JSON.parse(JSON.stringify(bids[0])); - const request = spec.buildRequests([bids[0]], {gdprConsent: {gdprApplies: true, consentString: 'concentDataString'}}); + const request = spec.buildRequests([bids[0]], { gdprConsent: { gdprApplies: true, consentString: 'concentDataString' } }); const parsedUrl = parseUrl(request.url).query; assert.equal(parsedUrl.gdpr, 'true'); @@ -121,19 +121,19 @@ describe('etarget adapter', function () { it('should not send GDPR Consent data to etarget if gdprApplies is false or undefined', function () { const resultBids = JSON.parse(JSON.stringify(bids[0])); - let request = spec.buildRequests([bids[0]], {gdprConsent: {gdprApplies: false, consentString: 'concentDataString'}}); + let request = spec.buildRequests([bids[0]], { gdprConsent: { gdprApplies: false, consentString: 'concentDataString' } }); const parsedUrl = parseUrl(request.url).query; assert.ok(!parsedUrl.gdpr); assert.ok(!parsedUrl.gdpr_consent); - request = spec.buildRequests([bids[0]], {gdprConsent: {gdprApplies: undefined, consentString: 'concentDataString'}}); + 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'}}); + let request = spec.buildRequests([bids[0]], { gdprConsent: { gdprApplies: true, consentString: 'concentDataString' } }); assert.deepEqual(request.gdpr, { gdpr: true, @@ -200,7 +200,7 @@ describe('etarget adapter', function () { }); it('should set mediaType on bid response', function () { - const expected = [ BANNER, BANNER, BANNER, VIDEO, VIDEO ]; + 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]); @@ -283,7 +283,7 @@ describe('etarget adapter', function () { beforeEach(function () { const sizes = [[250, 300], [300, 250], [300, 600]]; const placementCode = ['div-01', 'div-02', 'div-03', 'div-04', 'div-05']; - const 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'}]; + const 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], diff --git a/test/spec/modules/euidIdSystem_spec.js b/test/spec/modules/euidIdSystem_spec.js index dec68af7025..cb63618fdaa 100644 --- a/test/spec/modules/euidIdSystem_spec.js +++ b/test/spec/modules/euidIdSystem_spec.js @@ -1,13 +1,13 @@ -import {attachIdSystem, coreStorage, init, setSubmoduleRegistry} from 'modules/userId/index.js'; -import {config} from 'src/config.js'; -import {euidIdSubmodule} from 'modules/euidIdSystem.js'; +import { attachIdSystem, coreStorage, init, setSubmoduleRegistry } from 'modules/userId/index.js'; +import { config } from 'src/config.js'; +import { euidIdSubmodule } from 'modules/euidIdSystem.js'; import 'modules/consentManagementTcf.js'; -import {requestBids} from '../../../src/prebid.js'; -import {apiHelpers, cookieHelpers, runAuction, setGdprApplies} from './uid2IdSystem_helpers.js'; -import {hook} from 'src/hook.js'; -import {uninstall as uninstallTcfControl} from 'modules/tcfControl.js'; -import {server} from 'test/mocks/xhr'; -import {createEidsArray} from '../../../modules/userId/eids.js'; +import { requestBids } from '../../../src/prebid.js'; +import { apiHelpers, cookieHelpers, runAuction, setGdprApplies } from './uid2IdSystem_helpers.js'; +import { hook } from 'src/hook.js'; +import { uninstall as uninstallTcfControl } from 'modules/tcfControl.js'; +import { server } from 'test/mocks/xhr'; +import { createEidsArray } from '../../../modules/userId/eids.js'; const expect = require('chai').expect; @@ -21,12 +21,12 @@ const legacyToken = 'legacy-advertising-token'; const refreshedToken = 'refreshed-advertising-token'; const auctionDelayMs = 10; -const makeEuidIdentityContainer = (token) => ({euid: {id: token}}); -const makeEuidOptoutContainer = (token) => ({euid: {optout: true}}); +const makeEuidIdentityContainer = (token) => ({ euid: { id: token } }); +const makeEuidOptoutContainer = (token) => ({ euid: { optout: true } }); const useLocalStorage = true; const makePrebidConfig = (params = null, extraSettings = {}, debug = false) => ({ - userSync: { auctionDelay: auctionDelayMs, userIds: [{name: 'euid', params: {storage: useLocalStorage ? 'localStorage' : 'cookie', ...params}, ...extraSettings}] }, debug + userSync: { auctionDelay: auctionDelayMs, userIds: [{ name: 'euid', params: { storage: useLocalStorage ? 'localStorage' : 'cookie', ...params }, ...extraSettings }] }, debug }); const cstgConfigParams = { serverPublicKey: 'UID2-X-L-24B8a/eLYBmRkXA9yPgRZt+ouKbXewG2OPs23+ov3JC8mtYJBCx6AxGwJ4MlwUcguebhdDp2CvzsCgS9ogwwGA==', subscriptionId: 'subscription-id' } @@ -90,7 +90,7 @@ describe('EUID module', function() { it('When a server-only token value is provided in config, it is available to the auction.', async function() { setGdprApplies(true); - config.setConfig(makePrebidConfig(null, {value: makeEuidIdentityContainer(initialToken)})); + config.setConfig(makePrebidConfig(null, { value: makeEuidIdentityContainer(initialToken) })); const bid = await runAuction(); expectToken(bid, initialToken); }); @@ -98,7 +98,7 @@ describe('EUID module', function() { it('When a server-only token is provided in the module storage cookie but consent is not available, it is not available to the auction.', async function() { setGdprApplies(); coreStorage.setCookie(moduleCookieName, legacyToken, cookieHelpers.getFutureCookieExpiry()); - config.setConfig({userSync: {auctionDelay: auctionDelayMs, userIds: [{name: 'euid'}]}}); + config.setConfig({ userSync: { auctionDelay: auctionDelayMs, userIds: [{ name: 'euid' }] } }); const bid = await runAuction(); expectNoIdentity(bid); }); @@ -106,7 +106,7 @@ describe('EUID module', function() { it('When a server-only token is provided in the module storage cookie, it is available to the auction.', async function() { setGdprApplies(true); coreStorage.setCookie(moduleCookieName, legacyToken, cookieHelpers.getFutureCookieExpiry()); - config.setConfig({userSync: {auctionDelay: auctionDelayMs, userIds: [{name: 'euid'}]}}); + config.setConfig({ userSync: { auctionDelay: auctionDelayMs, userIds: [{ name: 'euid' }] } }); const bid = await runAuction(); expectToken(bid, legacyToken); }) @@ -114,7 +114,7 @@ describe('EUID module', function() { it('When a valid response body is provided in config, it is available to the auction', async function() { setGdprApplies(true); const euidToken = apiHelpers.makeTokenResponse(initialToken, false, false); - config.setConfig(makePrebidConfig({euidToken})); + config.setConfig(makePrebidConfig({ euidToken })); const bid = await runAuction(); expectToken(bid, initialToken); }) @@ -123,7 +123,7 @@ describe('EUID module', function() { setGdprApplies(true); const euidToken = apiHelpers.makeTokenResponse(initialToken, false, false); cookieHelpers.setPublisherCookie(publisherCookieName, euidToken); - config.setConfig(makePrebidConfig({euidCookie: publisherCookieName})); + config.setConfig(makePrebidConfig({ euidCookie: publisherCookieName })); const bid = await runAuction(); expectToken(bid, initialToken); }) @@ -131,7 +131,7 @@ describe('EUID module', function() { it('When an expired token is provided in config, it calls the API.', async function () { setGdprApplies(true); const euidToken = apiHelpers.makeTokenResponse(initialToken, true, true); - config.setConfig(makePrebidConfig({euidToken})); + config.setConfig(makePrebidConfig({ euidToken })); await runAuction(); expect(server.requests[0]?.url).to.have.string('https://prod.euid.eu/'); }); @@ -140,7 +140,7 @@ describe('EUID module', function() { setGdprApplies(true); const euidToken = apiHelpers.makeTokenResponse(initialToken, true, true); configureEuidResponse(200, makeSuccessResponseBody(refreshedToken)); - config.setConfig(makePrebidConfig({euidToken})); + config.setConfig(makePrebidConfig({ euidToken })); apiHelpers.respondAfterDelay(1, server); const bid = await runAuction(); expectToken(bid, refreshedToken); @@ -175,7 +175,7 @@ describe('EUID module', function() { }); it('euid', function() { const userId = { - euid: {'id': 'Sample_AD_Token'} + euid: { 'id': 'Sample_AD_Token' } }; const newEids = createEidsArray(userId); expect(newEids.length).to.equal(1); diff --git a/test/spec/modules/experianRtdProvider_spec.js b/test/spec/modules/experianRtdProvider_spec.js index 556851e3582..e02f036885c 100644 --- a/test/spec/modules/experianRtdProvider_spec.js +++ b/test/spec/modules/experianRtdProvider_spec.js @@ -9,7 +9,7 @@ import { import { getStorageManager } from '../../../src/storageManager.js'; import { MODULE_TYPE_RTD } from '../../../src/activities/modules.js'; import { safeJSONParse, timestamp } from '../../../src/utils.js'; -import {server} from '../../mocks/xhr.js'; +import { server } from '../../mocks/xhr.js'; describe('Experian realtime module', () => { const sandbox = sinon.createSandbox(); @@ -114,7 +114,7 @@ describe('Experian realtime module', () => { bidder: {} } } - const userConsent = {gdpr: {}, uspConsent: {}} + const userConsent = { gdpr: {}, uspConsent: {} } const moduleConfig = { params: { accountId: 'ZylatYg', bidders: ['pubmatic', 'sovrn'] } } const dataEnvelopeSpy = sandbox.spy(experianRtdObj, 'requestDataEnvelope') const alterBidsSpy = sandbox.spy(experianRtdObj, 'alterBids') @@ -152,7 +152,7 @@ describe('Experian realtime module', () => { bidder: {} } } - const userConsent = {gdpr: {}, uspConsent: {}} + const userConsent = { gdpr: {}, uspConsent: {} } const moduleConfig = { params: { accountId: 'ZylatYg', bidders: ['pubmatic', 'sovrn'] } } const dataEnvelopeSpy = sandbox.spy(experianRtdObj, 'requestDataEnvelope') const alterBidsSpy = sandbox.spy(experianRtdObj, 'alterBids') @@ -168,7 +168,7 @@ describe('Experian realtime module', () => { bidder: {} } } - const userConsent = {gdpr: {}, uspConsent: {}} + const userConsent = { gdpr: {}, uspConsent: {} } const moduleConfig = { params: { accountId: 'ZylatYg', bidders: ['pubmatic', 'sovrn'] } } const dataEnvelopeSpy = sandbox.spy(experianRtdObj, 'requestDataEnvelope') const alterBidsSpy = sandbox.spy(experianRtdObj, 'alterBids') @@ -191,7 +191,7 @@ describe('Experian realtime module', () => { bidder: {} } } - const userConsent = {gdpr: {}, uspConsent: {}} + const userConsent = { gdpr: {}, uspConsent: {} } const moduleConfig = { params: { accountId: 'ZylatYg', bidders: ['pubmatic', 'sovrn'] } } const dataEnvelopeSpy = sandbox.spy(experianRtdObj, 'requestDataEnvelope') const alterBidsSpy = sandbox.spy(experianRtdObj, 'alterBids') @@ -215,7 +215,7 @@ describe('Experian realtime module', () => { bidder: {} } } - const userConsent = {gdpr: {}, uspConsent: {}} + const userConsent = { gdpr: {}, uspConsent: {} } const moduleConfig = { params: { accountId: 'ZylatYg', bidders: ['pubmatic', 'sovrn'] } } const dataEnvelopeSpy = sandbox.spy(experianRtdObj, 'requestDataEnvelope') const alterBidsSpy = sandbox.spy(experianRtdObj, 'alterBids') @@ -239,7 +239,7 @@ describe('Experian realtime module', () => { bidder: {} } } - const userConsent = {gdpr: {}, uspConsent: {}} + const userConsent = { gdpr: {}, uspConsent: {} } const moduleConfig = { params: { accountId: 'ZylatYg', bidders: ['pubmatic', 'sovrn'] } } const dataEnvelopeSpy = sandbox.spy(experianRtdObj, 'requestDataEnvelope') const alterBidsSpy = sandbox.spy(experianRtdObj, 'alterBids') @@ -283,10 +283,12 @@ describe('Experian realtime module', () => { } const moduleConfig = { params: { accountId: 'ZylatYg', bidders: ['pubmatic'] } } experianRtdObj.alterBids(bidsConfig, moduleConfig); - expect(bidsConfig.ortb2Fragments.bidder).to.deep.equal({pubmatic: { - experianRtidKey: 'pubmatic-encryption-key-1', - experianRtidData: 'IkhlbGxvLCB3b3JsZC4gSGVsbG8sIHdvcmxkLiBIZWxsbywgd29ybGQuIg==' - }}) + expect(bidsConfig.ortb2Fragments.bidder).to.deep.equal({ + pubmatic: { + experianRtidKey: 'pubmatic-encryption-key-1', + experianRtidData: 'IkhlbGxvLCB3b3JsZC4gSGVsbG8sIHdvcmxkLiBIZWxsbywgd29ybGQuIg==' + } + }) }) }) describe('data envelope is missing bidders from config', () => { @@ -318,7 +320,8 @@ describe('Experian realtime module', () => { sovrn: { experianRtidKey: 'sovrn-encryption-key-1', experianRtidData: 'IkhlbGxvLCB3b3JsZC4gSGVsbG8sIHdvcmxkLiBIZWxsbywgd29ybGQuIg==' - }}) + } + }) }) }) }) @@ -334,7 +337,7 @@ describe('Experian realtime module', () => { ) expect(requests[0].url).to.equal('https://rtid.tapad.com/acc/ZylatYg/ids?gdpr=0&gdpr_consent=wow&us_privacy=1YYY') - expect(safeJSONParse(storage.getDataFromLocalStorage(EXPERIAN_RTID_DATA_KEY, null))).to.deep.equal([{bidder: 'pubmatic', data: {key: 'pubmatic-encryption-key-1', data: 'IkhlbGxvLCB3b3JsZC4gSGVsbG8sIHdvcmxkLiBIZWxsbywgd29ybGQuIg=='}}, {bidder: 'sovrn', data: {key: 'sovrn-encryption-key-1', data: 'IkhlbGxvLCB3b3JsZC4gSGVsbG8sIHdvcmxkLiBIZWxsbywgd29ybGQuIg=='}}]) + expect(safeJSONParse(storage.getDataFromLocalStorage(EXPERIAN_RTID_DATA_KEY, null))).to.deep.equal([{ bidder: 'pubmatic', data: { key: 'pubmatic-encryption-key-1', data: 'IkhlbGxvLCB3b3JsZC4gSGVsbG8sIHdvcmxkLiBIZWxsbywgd29ybGQuIg==' } }, { bidder: 'sovrn', data: { key: 'sovrn-encryption-key-1', data: 'IkhlbGxvLCB3b3JsZC4gSGVsbG8sIHdvcmxkLiBIZWxsbywgd29ybGQuIg==' } }]) expect(storage.getDataFromLocalStorage(EXPERIAN_RTID_STALE_KEY)).to.equal('2023-06-01T00:00:00') expect(storage.getDataFromLocalStorage(EXPERIAN_RTID_EXPIRATION_KEY)).to.equal('2023-06-03T00:00:00') }) diff --git a/test/spec/modules/fabrickIdSystem_spec.js b/test/spec/modules/fabrickIdSystem_spec.js index 7f5227a142b..33ae99c45c0 100644 --- a/test/spec/modules/fabrickIdSystem_spec.js +++ b/test/spec/modules/fabrickIdSystem_spec.js @@ -1,7 +1,7 @@ import * as utils from '../../../src/utils.js'; -import {server} from '../../mocks/xhr.js'; +import { server } from '../../mocks/xhr.js'; -import {fabrickIdSubmodule, appendUrl} from 'modules/fabrickIdSystem.js'; +import { fabrickIdSubmodule, appendUrl } from 'modules/fabrickIdSystem.js'; const defaultConfigParams = { apiKey: '123', @@ -9,7 +9,7 @@ const defaultConfigParams = { p: ['def', 'hij'], url: 'http://localhost:9999/test/mocks/fabrickId.json?' }; -const responseHeader = {'Content-Type': 'application/json'} +const responseHeader = { 'Content-Type': 'application/json' } describe('Fabrick ID System', function() { let logErrorStub; diff --git a/test/spec/modules/fanBidAdapter_spec.js b/test/spec/modules/fanBidAdapter_spec.js index 04fbf5d7fb6..7ebbee45924 100644 --- a/test/spec/modules/fanBidAdapter_spec.js +++ b/test/spec/modules/fanBidAdapter_spec.js @@ -330,7 +330,7 @@ describe('freedomadnetworkAdapter', function() { meta: { libertas: { pxl: [ - {url: 'https://pxl.nurl/track?bid=req456', type: 0} + { url: 'https://pxl.nurl/track?bid=req456', type: 0 } ], }, }, diff --git a/test/spec/modules/feedadBidAdapter_spec.js b/test/spec/modules/feedadBidAdapter_spec.js index dbabb4dd587..4c5f637b15e 100644 --- a/test/spec/modules/feedadBidAdapter_spec.js +++ b/test/spec/modules/feedadBidAdapter_spec.js @@ -1,7 +1,7 @@ -import {expect} from 'chai'; -import {spec} from 'modules/feedadBidAdapter.js'; -import {BANNER, NATIVE, VIDEO} from '../../../src/mediaTypes.js'; -import {server} from 'test/mocks/xhr.js'; +import { expect } from 'chai'; +import { spec } from 'modules/feedadBidAdapter.js'; +import { BANNER, NATIVE, VIDEO } from '../../../src/mediaTypes.js'; +import { server } from 'test/mocks/xhr.js'; const CODE = 'feedad'; const EXPECTED_ADAPTER_VERSION = '1.0.6'; @@ -41,7 +41,7 @@ describe('FeedAdAdapter', function () { const result = spec.isBidRequestValid({ bidder: 'feedad', sizes: [], - params: {placementId: 'placement'} + params: { placementId: 'placement' } }); expect(result).to.equal(false); }); @@ -49,7 +49,7 @@ describe('FeedAdAdapter', function () { const result = spec.isBidRequestValid({ bidder: 'feedad', sizes: [], - params: {clientToken: '', placementId: 'placement'} + params: { clientToken: '', placementId: 'placement' } }); expect(result).to.equal(false); }); @@ -57,7 +57,7 @@ describe('FeedAdAdapter', function () { const result = spec.isBidRequestValid({ bidder: 'feedad', sizes: [], - params: {clientToken: 'clientToken'} + params: { clientToken: 'clientToken' } }); expect(result).to.equal(false); }); @@ -65,7 +65,7 @@ describe('FeedAdAdapter', function () { const result = spec.isBidRequestValid({ bidder: 'feedad', sizes: [], - params: {clientToken: 'clientToken', placementId: ''} + params: { clientToken: 'clientToken', placementId: '' } }); expect(result).to.equal(false); }); @@ -77,7 +77,7 @@ describe('FeedAdAdapter', function () { const result = spec.isBidRequestValid({ bidder: 'feedad', sizes: [], - params: {clientToken: 'clientToken', placementId} + params: { clientToken: 'clientToken', placementId } }); expect(result).to.equal(false); }); @@ -91,7 +91,7 @@ describe('FeedAdAdapter', function () { const result = spec.isBidRequestValid({ bidder: 'feedad', sizes: [], - params: {clientToken: 'clientToken', placementId: id} + params: { clientToken: 'clientToken', placementId: id } }); expect(result).to.equal(false); }); @@ -100,7 +100,7 @@ describe('FeedAdAdapter', function () { const result = spec.isBidRequestValid({ bidder: 'feedad', sizes: [], - params: {clientToken: 'clientToken', placementId: 'placement-id'} + params: { clientToken: 'clientToken', placementId: 'placement-id' } }); expect(result).to.equal(true); }); @@ -126,7 +126,7 @@ describe('FeedAdAdapter', function () { sizes: [[300, 250], [300, 600]], } }, - params: {clientToken: 'clientToken', placementId: 'placement-id'} + params: { clientToken: 'clientToken', placementId: 'placement-id' } }; const result = spec.buildRequests([bid], bidderRequest); expect(result).to.be.empty; @@ -139,7 +139,7 @@ describe('FeedAdAdapter', function () { context: 'instream' } }, - params: {clientToken: 'clientToken', placementId: 'placement-id'} + params: { clientToken: 'clientToken', placementId: 'placement-id' } }; const result = spec.buildRequests([bid], bidderRequest); expect(result).to.be.empty; @@ -152,7 +152,7 @@ describe('FeedAdAdapter', function () { context: 'outstream' } }, - params: {clientToken: 'clientToken', placementId: 'placement-id'} + params: { clientToken: 'clientToken', placementId: 'placement-id' } }; const result = spec.buildRequests([bid], bidderRequest); expect(result.data.bids).to.be.lengthOf(1); @@ -166,7 +166,7 @@ describe('FeedAdAdapter', function () { sizes: [[320, 250]] } }, - params: {clientToken: 'clientToken', placementId: 'placement-id'} + params: { clientToken: 'clientToken', placementId: 'placement-id' } }; const result = spec.buildRequests([bid], bidderRequest); expect(result.data.bids).to.be.lengthOf(1); @@ -180,7 +180,7 @@ describe('FeedAdAdapter', function () { sizes: [[320, 250]] } }, - params: {clientToken: 'clientToken', placementId: 'placement-id', another: 'parameter', more: 'parameters'} + params: { clientToken: 'clientToken', placementId: 'placement-id', another: 'parameter', more: 'parameters' } }; const result = spec.buildRequests([bid], bidderRequest); expect(result.data.bids).to.be.lengthOf(1); @@ -195,7 +195,7 @@ describe('FeedAdAdapter', function () { video: undefined, native: undefined }, - params: {clientToken: 'clientToken', placementId: 'placement-id'} + params: { clientToken: 'clientToken', placementId: 'placement-id' } }; const result = spec.buildRequests([bid], bidderRequest); expect(result).to.be.empty; @@ -208,7 +208,7 @@ describe('FeedAdAdapter', function () { sizes: [[320, 250]] } }, - params: {clientToken: 'clientToken', placementId: 'placement-id'} + params: { clientToken: 'clientToken', placementId: 'placement-id' } }; const result = spec.buildRequests([bid], bidderRequest); expect(result.method).to.equal('POST'); @@ -221,7 +221,7 @@ describe('FeedAdAdapter', function () { sizes: [[320, 250]] } }, - params: {clientToken: 'clientToken', placementId: 'placement-id'} + params: { clientToken: 'clientToken', placementId: 'placement-id' } }; const result = spec.buildRequests([bid], bidderRequest); expect(result.url).to.equal('https://api.feedad.com/1/prebid/web/bids'); @@ -234,7 +234,7 @@ describe('FeedAdAdapter', function () { sizes: [[320, 250]] } }, - params: {clientToken: 'clientToken', placementId: 'placement-id'} + params: { clientToken: 'clientToken', placementId: 'placement-id' } }; const result = spec.buildRequests([bid], bidderRequest); expect(result.options).to.deep.equal({ @@ -249,7 +249,7 @@ describe('FeedAdAdapter', function () { sizes: [[320, 250]] } }, - params: {clientToken: 'clientToken', placementId: 'placement-id'} + params: { clientToken: 'clientToken', placementId: 'placement-id' } }; const result = spec.buildRequests([bid, bid, bid], bidderRequest); expect(result.data).to.deep.include(bidderRequest); @@ -262,7 +262,7 @@ describe('FeedAdAdapter', function () { sizes: [[320, 250]] } }, - params: {clientToken: 'clientToken', placementId: 'placement-id'} + params: { clientToken: 'clientToken', placementId: 'placement-id' } }; const result = spec.buildRequests([bid, bid, bid]); expect(result).to.be.empty; @@ -275,7 +275,7 @@ describe('FeedAdAdapter', function () { sizes: [[320, 250]] } }, - params: {clientToken: 'clientToken', placementId: 'placement-id'} + params: { clientToken: 'clientToken', placementId: 'placement-id' } }; const result = spec.buildRequests([bid], bidderRequest); expect(result.data.gdprApplies).to.be.undefined; @@ -289,7 +289,7 @@ describe('FeedAdAdapter', function () { sizes: [[320, 250]] } }, - params: {clientToken: 'clientToken', placementId: 'placement-id'} + params: { clientToken: 'clientToken', placementId: 'placement-id' } }; const request = Object.assign({}, bidderRequest, { gdprConsent: { @@ -309,7 +309,7 @@ describe('FeedAdAdapter', function () { sizes: [[320, 250]] } }, - params: {clientToken: 'clientToken', placementId: 'placement-id'} + params: { clientToken: 'clientToken', placementId: 'placement-id' } }; const result = spec.buildRequests([bid], bidderRequest); expect(result.data.bids[0].params.prebid_adapter_version).to.equal(EXPECTED_ADAPTER_VERSION); @@ -322,7 +322,7 @@ describe('FeedAdAdapter', function () { const body = [{ ad: 'bar', }]; - const result = spec.interpretResponse({body: JSON.stringify(body)}); + const result = spec.interpretResponse({ body: JSON.stringify(body) }); expect(result).to.deep.equal(body); }); @@ -330,7 +330,7 @@ describe('FeedAdAdapter', function () { const body = [{ ad: 'bar', }]; - const result = spec.interpretResponse({body}); + const result = spec.interpretResponse({ body }); expect(result).to.deep.equal(body); }); @@ -347,7 +347,7 @@ describe('FeedAdAdapter', function () { ad: 'ad html', }; const body = [bid1, bid2, bid3]; - const result = spec.interpretResponse({body: JSON.stringify(body)}); + const result = spec.interpretResponse({ body: JSON.stringify(body) }); expect(result).to.deep.equal([bid1, bid3]); }); @@ -357,22 +357,22 @@ describe('FeedAdAdapter', function () { ad: 'ad html', cpm: 100 }; - const result = spec.interpretResponse({body: JSON.stringify([bid])}); + const result = spec.interpretResponse({ body: JSON.stringify([bid]) }); expect(result[0]).not.to.haveOwnProperty('ext'); }); it('should return an empty array if the response is not an array', function () { const bid = {}; - const result = spec.interpretResponse({body: JSON.stringify(bid)}); + const result = spec.interpretResponse({ body: JSON.stringify(bid) }); expect(result).to.deep.equal([]); }); }); describe('getUserSyncs', function () { - const pixelSync1 = {type: 'image', url: 'the pixel url 1'}; - const pixelSync2 = {type: 'image', url: 'the pixel url 2'}; - const iFrameSync1 = {type: 'iframe', url: 'the iFrame url 1'}; - const iFrameSync2 = {type: 'iframe', url: 'the iFrame url 2'}; + const pixelSync1 = { type: 'image', url: 'the pixel url 1' }; + const pixelSync2 = { type: 'image', url: 'the pixel url 2' }; + const iFrameSync1 = { type: 'iframe', url: 'the iFrame url 1' }; + const iFrameSync2 = { type: 'iframe', url: 'the iFrame url 2' }; const response1 = { body: [{ ext: { @@ -395,7 +395,7 @@ describe('FeedAdAdapter', function () { }] }; it('should pass through the syncs out of the extension fields of the server response', function () { - const result = spec.getUserSyncs({iframeEnabled: true, pixelEnabled: true}, [response1]) + const result = spec.getUserSyncs({ iframeEnabled: true, pixelEnabled: true }, [response1]) expect(result).to.deep.equal([ pixelSync1, pixelSync2, @@ -404,7 +404,7 @@ describe('FeedAdAdapter', function () { }); it('should concat the syncs of all responses', function () { - const result = spec.getUserSyncs({iframeEnabled: true, pixelEnabled: true}, [response1, response2]); + const result = spec.getUserSyncs({ iframeEnabled: true, pixelEnabled: true }, [response1, response2]); expect(result).to.deep.equal([ pixelSync1, pixelSync2, @@ -414,7 +414,7 @@ describe('FeedAdAdapter', function () { }); it('should concat the syncs of all bids', function () { - const result = spec.getUserSyncs({iframeEnabled: true, pixelEnabled: true}, [response2]); + const result = spec.getUserSyncs({ iframeEnabled: true, pixelEnabled: true }, [response2]); expect(result).to.deep.equal([ pixelSync1, iFrameSync1, @@ -424,7 +424,7 @@ describe('FeedAdAdapter', function () { }); it('should filter out duplicates', function () { - const result = spec.getUserSyncs({iframeEnabled: true, pixelEnabled: true}, [response1, response1]); + const result = spec.getUserSyncs({ iframeEnabled: true, pixelEnabled: true }, [response1, response1]); expect(result).to.deep.equal([ pixelSync1, pixelSync2, @@ -433,7 +433,7 @@ describe('FeedAdAdapter', function () { }); it('should not include iFrame syncs if the option is disabled', function () { - const result = spec.getUserSyncs({iframeEnabled: false, pixelEnabled: true}, [response1]); + const result = spec.getUserSyncs({ iframeEnabled: false, pixelEnabled: true }, [response1]); expect(result).to.deep.equal([ pixelSync1, pixelSync2, @@ -441,36 +441,36 @@ describe('FeedAdAdapter', function () { }); it('should not include pixel syncs if the option is disabled', function () { - const result = spec.getUserSyncs({iframeEnabled: true, pixelEnabled: false}, [response1]); + const result = spec.getUserSyncs({ iframeEnabled: true, pixelEnabled: false }, [response1]); expect(result).to.deep.equal([ iFrameSync1, ]); }); it('should not include any syncs if the sync options are disabled or missing', function () { - const result = spec.getUserSyncs({iframeEnabled: false, pixelEnabled: false}, [response1]); + const result = spec.getUserSyncs({ iframeEnabled: false, pixelEnabled: false }, [response1]); expect(result).to.deep.equal([]); }); it('should handle empty responses', function () { - const result = spec.getUserSyncs({iframeEnabled: true, pixelEnabled: true}, []) + const result = spec.getUserSyncs({ iframeEnabled: true, pixelEnabled: true }, []) expect(result).to.deep.equal([]); }); it('should not throw if the server response is weird', function () { const responses = [ - {body: null}, - {body: 'null'}, - {body: 1234}, - {body: {}}, - {body: [{}, 123]}, + { body: null }, + { body: 'null' }, + { body: 1234 }, + { body: {} }, + { body: [{}, 123] }, ]; - expect(() => spec.getUserSyncs({iframeEnabled: true, pixelEnabled: true}, responses)).to.not.throw(); + expect(() => spec.getUserSyncs({ iframeEnabled: true, pixelEnabled: true }, responses)).to.not.throw(); }); it('should return empty array if the body extension is null', function () { - const response = {body: [{ext: null}]}; - const result = spec.getUserSyncs({iframeEnabled: true, pixelEnabled: true}, [response]); + const response = { body: [{ ext: null }] }; + const result = spec.getUserSyncs({ iframeEnabled: true, pixelEnabled: true }, [response]); expect(result).to.deep.equal([]); }); }); @@ -621,7 +621,7 @@ describe('FeedAdAdapter', function () { 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'}); + expect(call.requestHeaders).to.include({ 'Content-Type': 'application/json' }); }) }); }); diff --git a/test/spec/modules/finativeBidAdapter_spec.js b/test/spec/modules/finativeBidAdapter_spec.js index ebb3e9e7a3d..8b603870d41 100644 --- a/test/spec/modules/finativeBidAdapter_spec.js +++ b/test/spec/modules/finativeBidAdapter_spec.js @@ -1,6 +1,6 @@ // jshint esversion: 6, es3: false, node: true -import {assert, expect} from 'chai'; -import {spec} from 'modules/finativeBidAdapter.js'; +import { assert, expect } from 'chai'; +import { spec } from 'modules/finativeBidAdapter.js'; import { NATIVE } from 'src/mediaTypes.js'; import { config } from 'src/config.js'; @@ -109,7 +109,7 @@ describe('Finative adapter', function () { adm: { native: { assets: [ - {id: 0, title: {text: 'this is a title'}} + { id: 0, title: { text: 'this is a title' } } ], imptrackers: ['https://domain.for/imp/tracker?price=${AUCTION_PRICE}'], link: { @@ -125,11 +125,13 @@ describe('Finative adapter', function () { ] } }; - const badResponse = { body: { - cur: 'EUR', - id: '4b516b80-886e-4ec0-82ae-9209e6d625fb', - seatbid: [] - }}; + const badResponse = { + body: { + cur: 'EUR', + id: '4b516b80-886e-4ec0-82ae-9209e6d625fb', + seatbid: [] + } + }; const bidRequest = { data: {}, diff --git a/test/spec/modules/fintezaAnalyticsAdapter_spec.js b/test/spec/modules/fintezaAnalyticsAdapter_spec.js index ed9d7b1639b..8b55532a357 100644 --- a/test/spec/modules/fintezaAnalyticsAdapter_spec.js +++ b/test/spec/modules/fintezaAnalyticsAdapter_spec.js @@ -192,7 +192,7 @@ describe('finteza analytics adapter', function () { expect(url.search.value).to.equal(String(cpm)); expect(url.search.unit).to.equal('usd'); - sinon.assert.calledWith(fntzAnalyticsAdapter.track, sinon.match({eventType: EVENTS.BID_WON})) + sinon.assert.calledWith(fntzAnalyticsAdapter.track, sinon.match({ eventType: EVENTS.BID_WON })) }); }); diff --git a/test/spec/modules/flippBidAdapter_spec.js b/test/spec/modules/flippBidAdapter_spec.js index 0e17f1d2270..9f8105d6fdc 100644 --- a/test/spec/modules/flippBidAdapter_spec.js +++ b/test/spec/modules/flippBidAdapter_spec.js @@ -1,6 +1,6 @@ -import {expect} from 'chai'; -import {spec} from 'modules/flippBidAdapter'; -import {newBidder} from 'src/adapters/bidderFactory'; +import { expect } from 'chai'; +import { spec } from 'modules/flippBidAdapter'; +import { newBidder } from 'src/adapters/bidderFactory'; const ENDPOINT = 'https://ads-flipp.com/flyer-locator-service/client_bidding'; describe('flippAdapter', function () { const adapter = newBidder(spec); @@ -39,7 +39,7 @@ describe('flippAdapter', function () { }, adUnitCode: '/10000/unit_code', sizes: [[300, 600]], - mediaTypes: {banner: {sizes: [[300, 600]]}}, + mediaTypes: { banner: { sizes: [[300, 600]] } }, bidId: '237f4d1a293f99', bidderRequestId: '1a857fa34c1c96', auctionId: 'a297d1aa-7900-4ce4-a0aa-caa8d46c4af7', @@ -110,7 +110,7 @@ describe('flippAdapter', function () { } }] }, - 'location': {'city': 'Oakville'}, + 'location': { 'city': 'Oakville' }, }, }; @@ -162,7 +162,7 @@ describe('flippAdapter', function () { 'decisions': { 'inline': [] }, - 'location': {'city': 'Oakville'}, + 'location': { 'city': 'Oakville' }, }, }; diff --git a/test/spec/modules/floxisBidAdapter_spec.js b/test/spec/modules/floxisBidAdapter_spec.js new file mode 100644 index 00000000000..9cc8fe96c46 --- /dev/null +++ b/test/spec/modules/floxisBidAdapter_spec.js @@ -0,0 +1,501 @@ +import { expect } from 'chai'; +import sinon from 'sinon'; +import { spec } from 'modules/floxisBidAdapter.js'; +import { BANNER, NATIVE, VIDEO } from 'src/mediaTypes.js'; +import * as utils from 'src/utils.js'; + +describe('floxisBidAdapter', function () { + const DEFAULT_PARAMS = { seat: 'Gmtb', region: 'us-e', partner: 'floxis' }; + + const validBannerBid = { + bidId: 'bid-1', + bidder: 'floxis', + adUnitCode: 'adunit-banner', + mediaTypes: { banner: { sizes: [[300, 250], [728, 90]] } }, + params: { ...DEFAULT_PARAMS } + }; + + const validVideoBid = { + bidId: 'bid-2', + bidder: 'floxis', + adUnitCode: 'adunit-video', + mediaTypes: { + video: { + playerSize: [[640, 480]], + mimes: ['video/mp4'], + protocols: [2, 3], + context: 'instream' + } + }, + params: { ...DEFAULT_PARAMS } + }; + + const validNativeBid = { + bidId: 'bid-3', + bidder: 'floxis', + adUnitCode: 'adunit-native', + mediaTypes: { + native: { + image: { required: true, sizes: [150, 50] }, + title: { required: true, len: 80 } + } + }, + params: { ...DEFAULT_PARAMS } + }; + + describe('isBidRequestValid', function () { + it('should return true for valid banner bid', function () { + expect(spec.isBidRequestValid(validBannerBid)).to.be.true; + }); + + it('should return true for valid video bid', function () { + expect(spec.isBidRequestValid(validVideoBid)).to.be.true; + }); + + it('should return true for valid native bid', function () { + expect(spec.isBidRequestValid(validNativeBid)).to.be.true; + }); + + it('should return false when seat is missing', function () { + const bid = { ...validBannerBid, params: { region: 'us-e' } }; + expect(spec.isBidRequestValid(bid)).to.be.false; + }); + + it('should return false when seat is empty string', function () { + const bid = { ...validBannerBid, params: { seat: '', region: 'us-e' } }; + expect(spec.isBidRequestValid(bid)).to.be.false; + }); + + it('should return true when region is missing (default region applies)', function () { + const bid = { ...validBannerBid, params: { seat: 'Gmtb' } }; + expect(spec.isBidRequestValid(bid)).to.be.true; + }); + + it('should return false when region is empty string', function () { + const bid = { ...validBannerBid, params: { seat: 'Gmtb', region: '' } }; + expect(spec.isBidRequestValid(bid)).to.be.false; + }); + + it('should return true when partner is missing (default partner applies)', function () { + const bid = { ...validBannerBid, params: { seat: 'Gmtb', region: 'us-e' } }; + expect(spec.isBidRequestValid(bid)).to.be.true; + }); + + it('should return false when partner is empty string', function () { + const bid = { ...validBannerBid, params: { seat: 'Gmtb', region: 'us-e', partner: '' } }; + expect(spec.isBidRequestValid(bid)).to.be.false; + }); + + it('should return false when params is missing', function () { + const bid = { bidId: 'x', mediaTypes: { banner: { sizes: [[300, 250]] } } }; + expect(spec.isBidRequestValid(bid)).to.be.false; + }); + + it('should return false when seat is not a string', function () { + const bid = { ...validBannerBid, params: { seat: 123, region: 'us-e', partner: 'floxis' } }; + expect(spec.isBidRequestValid(bid)).to.be.false; + }); + + it('should return false when region is not in whitelist', function () { + const bid = { ...validBannerBid, params: { ...DEFAULT_PARAMS, region: 'eu-w' } }; + expect(spec.isBidRequestValid(bid)).to.be.false; + }); + + it('should return false when partner is not in whitelist', function () { + const bid = { ...validBannerBid, params: { ...DEFAULT_PARAMS, partner: 'mypartner' } }; + expect(spec.isBidRequestValid(bid)).to.be.false; + }); + + it('should return true with default partner', function () { + const bid = { ...validBannerBid, params: { ...DEFAULT_PARAMS, partner: 'floxis' } }; + expect(spec.isBidRequestValid(bid)).to.be.true; + }); + }); + + describe('supportedMediaTypes', function () { + it('should include banner, video, and native', function () { + expect(spec.supportedMediaTypes).to.deep.equal([BANNER, VIDEO, NATIVE]); + }); + }); + + describe('buildRequests', function () { + const bidderRequest = { + bidderCode: 'floxis', + auctionId: 'auction-123', + timeout: 3000 + }; + + it('should return an array with one POST request', function () { + const requests = spec.buildRequests([validBannerBid], bidderRequest); + expect(requests).to.be.an('array').with.lengthOf(1); + expect(requests[0].method).to.equal('POST'); + }); + + it('should build URL without partner prefix when partner is floxis', function () { + const requests = spec.buildRequests([validBannerBid], bidderRequest); + expect(requests[0].url).to.equal('https://us-e.floxis.tech/pbjs?seat=Gmtb'); + }); + + it('should return no requests for non-whitelisted partner', function () { + const bidWithPartner = { + ...validBannerBid, + params: { ...DEFAULT_PARAMS, partner: 'mypartner' } + }; + const requests = spec.buildRequests([bidWithPartner], bidderRequest); + expect(requests).to.be.an('array').that.is.empty; + }); + + it('should default region to us-e when missing', function () { + const bidWithoutRegion = { + ...validBannerBid, + params: { seat: 'Gmtb', partner: 'floxis' } + }; + const requests = spec.buildRequests([bidWithoutRegion], bidderRequest); + expect(requests[0].url).to.equal('https://us-e.floxis.tech/pbjs?seat=Gmtb'); + }); + + it('should default partner to floxis when missing', function () { + const bidWithoutPartner = { + ...validBannerBid, + params: { seat: 'Gmtb', region: 'us-e' } + }; + const requests = spec.buildRequests([bidWithoutPartner], bidderRequest); + expect(requests[0].url).to.equal('https://us-e.floxis.tech/pbjs?seat=Gmtb'); + }); + + it('should return empty array for empty bid requests', function () { + const requests = spec.buildRequests([], bidderRequest); + expect(requests).to.be.an('array').that.is.empty; + }); + + it('should produce valid ORTB request payload', function () { + const requests = spec.buildRequests([validBannerBid], bidderRequest); + const data = requests[0].data; + expect(data).to.be.an('object'); + expect(data.imp).to.be.an('array').with.lengthOf(1); + expect(data.at).to.equal(1); + }); + + it('should set ext with adapter info', function () { + const requests = spec.buildRequests([validBannerBid], bidderRequest); + const data = requests[0].data; + expect(data.ext.prebid.adapter).to.equal('floxis'); + expect(data.ext.prebid.adapterVersion).to.be.undefined; + expect(data.ext.prebid.version).to.equal('$prebid.version$'); + }); + + it('should build banner imp correctly', function () { + const requests = spec.buildRequests([validBannerBid], bidderRequest); + const imp = requests[0].data.imp[0]; + expect(imp).to.have.property('banner'); + expect(imp.banner.format).to.be.an('array'); + expect(imp.secure).to.equal(1); + }); + + if (FEATURES.VIDEO) { + it('should build video imp correctly', function () { + const requests = spec.buildRequests([validVideoBid], bidderRequest); + const imp = requests[0].data.imp[0]; + expect(imp).to.have.property('video'); + expect(imp.video.mimes).to.deep.equal(['video/mp4']); + expect(imp.video.protocols).to.deep.equal([2, 3]); + }); + } + + it('should handle multiple bids in single request', function () { + const requests = spec.buildRequests([validBannerBid, validVideoBid], bidderRequest); + expect(requests).to.have.lengthOf(1); + expect(requests[0].data.imp).to.have.lengthOf(2); + }); + + it('should split requests by seat when using allowed defaults', function () { + const mixedBid = { + ...validVideoBid, + params: { + seat: 'Seat2', + region: 'us-e', + partner: 'floxis' + } + }; + + const requests = spec.buildRequests([validBannerBid, mixedBid], bidderRequest); + expect(requests).to.have.lengthOf(2); + expect(requests[0].url).to.equal('https://us-e.floxis.tech/pbjs?seat=Gmtb'); + expect(requests[1].url).to.equal('https://us-e.floxis.tech/pbjs?seat=Seat2'); + expect(requests[0].data.imp).to.have.lengthOf(1); + expect(requests[1].data.imp).to.have.lengthOf(1); + }); + + it('should ignore non-whitelisted bids in mixed request arrays', function () { + const invalidBid = { + ...validVideoBid, + params: { + seat: 'Seat2', + region: 'eu-w', + partner: 'mypartner' + } + }; + + const requests = spec.buildRequests([validBannerBid, invalidBid], bidderRequest); + expect(requests).to.have.lengthOf(1); + expect(requests[0].url).to.equal('https://us-e.floxis.tech/pbjs?seat=Gmtb'); + expect(requests[0].data.imp).to.have.lengthOf(1); + }); + + it('should set withCredentials option', function () { + const requests = spec.buildRequests([validBannerBid], bidderRequest); + expect(requests[0].options.withCredentials).to.be.true; + }); + + describe('Floors Module support', function () { + it('should set bidfloor from getFloor', function () { + const bidWithFloor = { + ...validBannerBid, + getFloor: function () { + return { floor: 2.5, currency: 'USD' }; + } + }; + const requests = spec.buildRequests([bidWithFloor], bidderRequest); + const imp = requests[0].data.imp[0]; + expect(imp.bidfloor).to.equal(2.5); + expect(imp.bidfloorcur).to.equal('USD'); + }); + + it('should not set bidfloor when getFloor is not present', function () { + const requests = spec.buildRequests([validBannerBid], bidderRequest); + const imp = requests[0].data.imp[0]; + expect(imp.bidfloor).to.be.undefined; + }); + + it('should handle getFloor throwing an error gracefully', function () { + const bidBrokenFloor = { + ...validBannerBid, + getFloor: function () { + throw new Error('floor error'); + } + }; + const requests = spec.buildRequests([bidBrokenFloor], bidderRequest); + const imp = requests[0].data.imp[0]; + expect(imp.bidfloor).to.be.undefined; + }); + }); + + describe('ortb2 passthrough', function () { + it('should merge ortb2 data into the ORTB request', function () { + const ortb2BidderRequest = { + ...bidderRequest, + ortb2: { + regs: { ext: { gdpr: 1 } }, + user: { ext: { consent: 'consent-string-123' } } + } + }; + const requests = spec.buildRequests([validBannerBid], ortb2BidderRequest); + const data = requests[0].data; + expect(data.regs.ext.gdpr).to.equal(1); + expect(data.user.ext.consent).to.equal('consent-string-123'); + }); + + it('should merge ortb2 USP data into the ORTB request', function () { + const uspBidderRequest = { + ...bidderRequest, + ortb2: { + regs: { ext: { us_privacy: '1YNN' } } + } + }; + const requests = spec.buildRequests([validBannerBid], uspBidderRequest); + const data = requests[0].data; + expect(data.regs.ext.us_privacy).to.equal('1YNN'); + }); + }); + }); + + describe('interpretResponse', function () { + function buildRequest() { + return spec.buildRequests([validBannerBid], { + bidderCode: 'floxis', + auctionId: 'auction-123' + })[0]; + } + + it('should parse valid banner ORTB response', function () { + const request = buildRequest(); + const serverResponse = { + body: { + seatbid: [{ + bid: [{ + impid: validBannerBid.bidId, + price: 1.23, + w: 300, + h: 250, + crid: 'creative-1', + adm: '
ad
', + mtype: 1 + }] + }] + } + }; + const bids = spec.interpretResponse(serverResponse, request); + expect(bids).to.be.an('array').with.lengthOf(1); + expect(bids[0].cpm).to.equal(1.23); + expect(bids[0].width).to.equal(300); + expect(bids[0].height).to.equal(250); + expect(bids[0].creativeId).to.equal('creative-1'); + expect(bids[0].ad).to.equal('
ad
'); + expect(bids[0].requestId).to.equal(validBannerBid.bidId); + expect(bids[0].netRevenue).to.equal(true); + expect(bids[0].currency).to.equal('USD'); + }); + + if (FEATURES.VIDEO) { + it('should parse valid video ORTB response', function () { + const videoRequest = spec.buildRequests([validVideoBid], { + bidderCode: 'floxis', + auctionId: 'auction-456' + })[0]; + const serverResponse = { + body: { + seatbid: [{ + bid: [{ + impid: validVideoBid.bidId, + price: 5.00, + w: 640, + h: 480, + crid: 'video-creative-1', + adm: '', + mtype: 2 + }] + }] + } + }; + const bids = spec.interpretResponse(serverResponse, videoRequest); + expect(bids).to.be.an('array').with.lengthOf(1); + expect(bids[0].cpm).to.equal(5.00); + expect(bids[0].vastXml).to.equal(''); + expect(bids[0].mediaType).to.equal(VIDEO); + }); + } + + it('should return empty array for empty response', function () { + const request = buildRequest(); + const bids = spec.interpretResponse({ body: {} }, request); + expect(bids).to.be.an('array').that.is.empty; + }); + + it('should return empty array for null response body', function () { + const request = buildRequest(); + const bids = spec.interpretResponse({ body: null }, request); + expect(bids).to.be.an('array').that.is.empty; + }); + + it('should return empty array for undefined response', function () { + const request = buildRequest(); + const bids = spec.interpretResponse(undefined, request); + expect(bids).to.be.an('array').that.is.empty; + }); + + it('should return empty array for undefined request', function () { + const serverResponse = { + body: { + seatbid: [{ + bid: [{ + impid: validBannerBid.bidId, + price: 1.23, + w: 300, + h: 250, + crid: 'creative-1', + adm: '
ad
', + mtype: 1 + }] + }] + } + }; + const bids = spec.interpretResponse(serverResponse, undefined); + expect(bids).to.be.an('array').that.is.empty; + }); + + it('should handle multiple bids in seatbid', function () { + const bids2 = [ + { ...validBannerBid, bidId: 'bid-a' }, + { ...validBannerBid, bidId: 'bid-b', adUnitCode: 'adunit-2' } + ]; + const request = spec.buildRequests(bids2, { bidderCode: 'floxis', auctionId: 'a1' })[0]; + const serverResponse = { + body: { + seatbid: [{ + bid: [ + { impid: 'bid-a', price: 1.0, w: 300, h: 250, crid: 'c1', adm: '
1
', mtype: 1 }, + { impid: 'bid-b', price: 2.0, w: 300, h: 250, crid: 'c2', adm: '
2
', mtype: 1 } + ] + }] + } + }; + const result = spec.interpretResponse(serverResponse, request); + expect(result).to.have.lengthOf(2); + expect(result[0].cpm).to.equal(1.0); + expect(result[1].cpm).to.equal(2.0); + }); + + it('should set advertiserDomains from adomain', function () { + const request = buildRequest(); + const serverResponse = { + body: { + seatbid: [{ + bid: [{ + impid: validBannerBid.bidId, + price: 1.0, + w: 300, + h: 250, + crid: 'c1', + adm: '
ad
', + adomain: ['adv.com'], + mtype: 1 + }] + }] + } + }; + const bids = spec.interpretResponse(serverResponse, request); + expect(bids[0].meta.advertiserDomains).to.deep.equal(['adv.com']); + }); + }); + + describe('getUserSyncs', function () { + it('should return empty array', function () { + expect(spec.getUserSyncs()).to.be.an('array').that.is.empty; + }); + }); + + describe('onBidWon', function () { + let triggerPixelStub; + + beforeEach(function () { + triggerPixelStub = sinon.stub(utils, 'triggerPixel'); + }); + + afterEach(function () { + triggerPixelStub.restore(); + }); + + it('should fire burl pixel', function () { + spec.onBidWon({ burl: 'https://example.com/burl' }); + expect(triggerPixelStub.calledWith('https://example.com/burl')).to.be.true; + }); + + it('should fire nurl pixel', function () { + spec.onBidWon({ nurl: 'https://example.com/nurl' }); + expect(triggerPixelStub.calledWith('https://example.com/nurl')).to.be.true; + }); + + it('should fire both burl and nurl pixels', function () { + spec.onBidWon({ + burl: 'https://example.com/burl', + nurl: 'https://example.com/nurl' + }); + expect(triggerPixelStub.callCount).to.equal(2); + }); + + it('should not fire pixels when no urls present', function () { + spec.onBidWon({}); + expect(triggerPixelStub.called).to.be.false; + }); + }); +}); diff --git a/test/spec/modules/fluctBidAdapter_spec.js b/test/spec/modules/fluctBidAdapter_spec.js index cdc99694d52..b59b1e99e98 100644 --- a/test/spec/modules/fluctBidAdapter_spec.js +++ b/test/spec/modules/fluctBidAdapter_spec.js @@ -1,7 +1,7 @@ -import {expect} from 'chai'; -import {spec} from 'modules/fluctBidAdapter'; -import {newBidder} from 'src/adapters/bidderFactory'; -import {config} from 'src/config'; +import { expect } from 'chai'; +import { spec } from 'modules/fluctBidAdapter'; +import { newBidder } from 'src/adapters/bidderFactory'; +import { config } from 'src/config'; describe('fluctAdapter', function () { const adapter = newBidder(spec); diff --git a/test/spec/modules/fpdModule_spec.js b/test/spec/modules/fpdModule_spec.js index 4536b304f9d..7c9c89e05ab 100644 --- a/test/spec/modules/fpdModule_spec.js +++ b/test/spec/modules/fpdModule_spec.js @@ -1,6 +1,6 @@ -import {expect} from 'chai'; -import {config} from 'src/config.js'; -import {registerSubmodules, reset, startAuctionHook} from 'modules/fpdModule/index.js'; +import { expect } from 'chai'; +import { config } from 'src/config.js'; +import { registerSubmodules, reset, startAuctionHook } from 'modules/fpdModule/index.js'; describe('the first party data module', function () { afterEach(function () { @@ -12,8 +12,8 @@ describe('the first party data module', function () { describe('startAuctionHook', () => { const mockFpd = { - global: {key: 'value'}, - bidder: {A: {bkey: 'bvalue'}} + global: { key: 'value' }, + bidder: { A: { bkey: 'bvalue' } } } beforeEach(() => { reset(); @@ -26,7 +26,7 @@ describe('the first party data module', function () { return mockFpd; } }); - const req = {ortb2Fragments: {}}; + const req = { ortb2Fragments: {} }; return new Promise((resolve) => startAuctionHook(resolve, req)) .then(() => { expect(req.ortb2Fragments).to.eql(mockFpd); @@ -40,7 +40,7 @@ describe('the first party data module', function () { return Promise.resolve(mockFpd); } }); - const req = {ortb2Fragments: {}}; + const req = { ortb2Fragments: {} }; return new Promise((resolve) => { startAuctionHook(resolve, req); }).then(() => { diff --git a/test/spec/modules/freeWheelAdserverVideo_spec.js b/test/spec/modules/freeWheelAdserverVideo_spec.js deleted file mode 100644 index 3da5b411e37..00000000000 --- a/test/spec/modules/freeWheelAdserverVideo_spec.js +++ /dev/null @@ -1,350 +0,0 @@ -import { expect } from 'chai'; -import { adpodUtils } from 'modules/freeWheelAdserverVideo.js'; -import { auctionManager } from 'src/auctionManager.js'; -import { config } from 'src/config.js'; -import { server } from 'test/mocks/xhr.js'; - -describe('freeWheel adserver module', function() { - let amStub; - let amGetAdUnitsStub; - - before(function () { - const 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 () { - config.setConfig({ - adpod: { - brandCategoryExclusion: false, - deferCaching: false - } - }); - }) - - afterEach(function() { - config.resetConfig(); - }); - - 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() { - const 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; - } - }); - - server.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); - }); - - it('should prioritize bids with deal', function() { - config.setConfig({ - adpod: { - deferCaching: true, - prioritizeDeals: true - } - }); - - const tier6Bid = createBid(10, 'preroll_1', 15, 'tier6_395_15s', '123', '395'); - tier6Bid['video']['dealTier'] = 'tier6' - - const tier7Bid = createBid(11, 'preroll_1', 45, 'tier7_395_15s', '123', '395'); - tier7Bid['video']['dealTier'] = 'tier7' - - const bidsReceived = [ - tier6Bid, - tier7Bid, - createBid(15, 'preroll_1', 90, '15.00_395_90s', '123', '395'), - ] - amStub.returns(bidsReceived); - let targeting; - adpodUtils.getTargeting({ - callback: function(errorMsg, targetingResult) { - targeting = targetingResult; - } - }); - - server.requests[0].respond( - 200, - { 'Content-Type': 'text/plain' }, - JSON.stringify({'responses': bidsReceived.slice(1)}) - ); - - expect(targeting['preroll_1'].length).to.equal(3); - expect(targeting['preroll_1']).to.deep.include({'hb_pb_cat_dur': 'tier6_395_15s'}); - expect(targeting['preroll_1']).to.deep.include({'hb_pb_cat_dur': 'tier7_395_15s'}); - expect(targeting['preroll_1']).to.deep.include({'hb_cache_id': '123'}); - }); - - it('should apply minDealTier to bids if configured', function() { - config.setConfig({ - adpod: { - deferCaching: true, - prioritizeDeals: true, - dealTier: { - 'appnexus': { - prefix: 'tier', - minDealTier: 5 - } - } - } - }); - - const tier2Bid = createBid(10, 'preroll_1', 15, 'tier2_395_15s', '123', '395'); - tier2Bid['video']['dealTier'] = 2 - tier2Bid['adserverTargeting']['hb_pb'] = '10.00' - - const tier7Bid = createBid(11, 'preroll_1', 45, 'tier7_395_15s', '123', '395'); - tier7Bid['video']['dealTier'] = 7 - tier7Bid['adserverTargeting']['hb_pb'] = '11.00' - - const bid = createBid(15, 'preroll_1', 15, '15.00_395_90s', '123', '395'); - bid['adserverTargeting']['hb_pb'] = '15.00' - - const bidsReceived = [ - tier2Bid, - tier7Bid, - bid - ] - amStub.returns(bidsReceived); - let targeting; - adpodUtils.getTargeting({ - callback: function(errorMsg, targetingResult) { - targeting = targetingResult; - } - }); - - server.requests[0].respond( - 200, - { 'Content-Type': 'text/plain' }, - JSON.stringify({'responses': [tier7Bid, bid]}) - ); - - expect(targeting['preroll_1'].length).to.equal(3); - expect(targeting['preroll_1']).to.deep.include({'hb_pb_cat_dur': 'tier7_395_15s'}); - expect(targeting['preroll_1']).to.deep.include({'hb_pb_cat_dur': '15.00_395_90s'}); - expect(targeting['preroll_1']).to.not.include({'hb_pb_cat_dur': 'tier2_395_15s'}); - expect(targeting['preroll_1']).to.deep.include({'hb_cache_id': '123'}); - }) -}); - -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': { - 'primaryCatId': 'iab-1', - 'adServerCatId': industry - }, - 'videoCacheKey': '4cf395af-8fee-4960-af0e-88d44e399f14' - } -} diff --git a/test/spec/modules/freepassBidAdapter_spec.js b/test/spec/modules/freepassBidAdapter_spec.js index b36639f573b..2a66348f5a4 100644 --- a/test/spec/modules/freepassBidAdapter_spec.js +++ b/test/spec/modules/freepassBidAdapter_spec.js @@ -1,6 +1,6 @@ -import {expect} from 'chai'; -import {spec} from 'modules/freepassBidAdapter.js'; -import {newBidder} from 'src/adapters/bidderFactory.js'; +import { expect } from 'chai'; +import { spec } from 'modules/freepassBidAdapter.js'; +import { newBidder } from 'src/adapters/bidderFactory.js'; describe('FreePass adapter', function () { const adapter = newBidder(spec); diff --git a/test/spec/modules/ftrackIdSystem_spec.js b/test/spec/modules/ftrackIdSystem_spec.js index 5e3b31dc11b..bec1fa8489c 100644 --- a/test/spec/modules/ftrackIdSystem_spec.js +++ b/test/spec/modules/ftrackIdSystem_spec.js @@ -3,10 +3,10 @@ import * as utils from 'src/utils.js'; import { uspDataHandler } from 'src/adapterManager.js'; import { loadExternalScriptStub } from 'test/mocks/adloaderStub.js'; import { getGlobal } from 'src/prebidGlobal.js'; -import {attachIdSystem, init, setSubmoduleRegistry} from 'modules/userId/index.js'; -import {createEidsArray} from 'modules/userId/eids.js'; -import {config} from 'src/config.js'; -import {server} from 'test/mocks/xhr.js'; +import { attachIdSystem, init, setSubmoduleRegistry } from 'modules/userId/index.js'; +import { createEidsArray } from 'modules/userId/eids.js'; +import { config } from 'src/config.js'; +import { server } from 'test/mocks/xhr.js'; import 'src/prebid.js'; const configMock = { @@ -106,26 +106,26 @@ describe('FTRACK ID System', () => { describe(`ftrackIdSubmodule.isThereConsent():`, () => { describe(`returns 'false' if:`, () => { it(`GDPR: if gdprApplies is truthy`, () => { - expect(ftrackIdSubmodule.isThereConsent({gdpr: {gdprApplies: 1}})).to.not.be.ok; - expect(ftrackIdSubmodule.isThereConsent({gdpr: {gdprApplies: true}})).to.not.be.ok; + expect(ftrackIdSubmodule.isThereConsent({ gdpr: { gdprApplies: 1 } })).to.not.be.ok; + expect(ftrackIdSubmodule.isThereConsent({ gdpr: { gdprApplies: true } })).to.not.be.ok; }); it(`US_PRIVACY version 1: if 'Opt Out Sale' is 'Y'`, () => { - expect(ftrackIdSubmodule.isThereConsent({usp: '1YYY'})).to.not.be.ok; + expect(ftrackIdSubmodule.isThereConsent({ usp: '1YYY' })).to.not.be.ok; }); }); describe(`returns 'true' if`, () => { it(`GDPR: if gdprApplies is undefined, false or 0`, () => { - expect(ftrackIdSubmodule.isThereConsent({gdpr: {gdprApplies: 0}})).to.be.ok; - expect(ftrackIdSubmodule.isThereConsent({gdpr: {gdprApplies: false}})).to.be.ok; - expect(ftrackIdSubmodule.isThereConsent({gdpr: {gdprApplies: null}})).to.be.ok; + expect(ftrackIdSubmodule.isThereConsent({ gdpr: { gdprApplies: 0 } })).to.be.ok; + expect(ftrackIdSubmodule.isThereConsent({ gdpr: { gdprApplies: false } })).to.be.ok; + expect(ftrackIdSubmodule.isThereConsent({ gdpr: { gdprApplies: null } })).to.be.ok; expect(ftrackIdSubmodule.isThereConsent({})).to.be.ok; }); it(`US_PRIVACY version 1: if 'Opt Out Sale' is not 'Y' ('N','-')`, () => { - expect(ftrackIdSubmodule.isThereConsent({usp: '1NNN'})).to.be.ok; - expect(ftrackIdSubmodule.isThereConsent({usp: '1---'})).to.be.ok; + expect(ftrackIdSubmodule.isThereConsent({ usp: '1NNN' })).to.be.ok; + expect(ftrackIdSubmodule.isThereConsent({ usp: '1---' })).to.be.ok; }); }); }); @@ -147,7 +147,7 @@ describe('FTRACK ID System', () => { expect(loadExternalScriptStub.args).to.deep.equal([]); loadExternalScriptStub.resetHistory(); - ftrackIdSubmodule.extendId(configMock, null, {cache: {id: ''}}); + ftrackIdSubmodule.extendId(configMock, null, { cache: { id: '' } }); expect(loadExternalScriptStub.called).to.not.be.ok; expect(loadExternalScriptStub.args).to.deep.equal([]); @@ -323,13 +323,13 @@ describe('FTRACK ID System', () => { describe(`extendId() method`, () => { it(`should not be making requests to retrieve a new ID, it should just be adding additional data to the id object`, () => { - ftrackIdSubmodule.extendId(configMock, null, {cache: {id: ''}}); + ftrackIdSubmodule.extendId(configMock, null, { cache: { id: '' } }); expect(server.requests).to.have.length(0); }); it(`should return cacheIdObj`, () => { - expect(ftrackIdSubmodule.extendId(configMock, null, {cache: {id: ''}})).to.deep.equal({cache: {id: ''}}); + expect(ftrackIdSubmodule.extendId(configMock, null, { cache: { id: '' } })).to.deep.equal({ cache: { id: '' } }); }); }); diff --git a/test/spec/modules/gamAdServerVideo_spec.js b/test/spec/modules/gamAdServerVideo_spec.js index a8ce01c1457..fba726d2bf1 100644 --- a/test/spec/modules/gamAdServerVideo_spec.js +++ b/test/spec/modules/gamAdServerVideo_spec.js @@ -1,22 +1,23 @@ -import {expect} from 'chai'; +import { expect } from 'chai'; import parse from 'url-parse'; -import {buildGamVideoUrl as buildDfpVideoUrl, dep} from 'modules/gamAdServerVideo.js'; +import { buildGamVideoUrl as buildDfpVideoUrl, dep } from 'modules/gamAdServerVideo.js'; import AD_UNIT from 'test/fixtures/video/adUnit.json'; import * as utils from 'src/utils.js'; -import {deepClone} from 'src/utils.js'; -import {config} from 'src/config.js'; -import {targeting} from 'src/targeting.js'; -import {auctionManager} from 'src/auctionManager.js'; -import {gdprDataHandler} from 'src/adapterManager.js'; +import { deepClone } from 'src/utils.js'; +import { config } from 'src/config.js'; +import { targeting } from 'src/targeting.js'; +import { auctionManager } from 'src/auctionManager.js'; +import { gdprDataHandler } from 'src/adapterManager.js'; import * as adServer from 'src/adserver.js'; -import {hook} from '../../../src/hook.js'; -import {stubAuctionIndex} from '../../helpers/indexStub.js'; -import {AuctionIndex} from '../../../src/auctionIndex.js'; +import { hook } from '../../../src/hook.js'; +import { stubAuctionIndex } from '../../helpers/indexStub.js'; +import { AuctionIndex } from '../../../src/auctionIndex.js'; import { getVastXml } from '../../../modules/gamAdServerVideo.js'; import { server } from '../../mocks/xhr.js'; import { generateUUID } from '../../../src/utils.js'; +import { uspDataHandler, gppDataHandler } from '../../../src/consentHandler.js'; describe('The DFP video support module', function () { before(() => { @@ -70,13 +71,13 @@ describe('The DFP video support module', function () { }).forEach(([t, options]) => { describe(`when using ${t}`, () => { it('should use page location as default for description_url', () => { - sandbox.stub(dep, 'ri').callsFake(() => ({page: 'example.com'})); + sandbox.stub(dep, 'ri').callsFake(() => ({ page: 'example.com' })); const prm = getQueryParams(options); expect(prm.description_url).to.eql('example.com'); }); it('should use a URI encoded page location as default for description_url', () => { - sandbox.stub(dep, 'ri').callsFake(() => ({page: 'https://example.com?iu=/99999999/news&cust_params=current_hour%3D12%26newscat%3Dtravel&pbjs_debug=true'})); + sandbox.stub(dep, 'ri').callsFake(() => ({ page: 'https://example.com?iu=/99999999/news&cust_params=current_hour%3D12%26newscat%3Dtravel&pbjs_debug=true' })); const prm = getQueryParams(options); expect(prm.description_url).to.eql('https%3A%2F%2Fexample.com%3Fiu%3D%2F99999999%2Fnews%26cust_params%3Dcurrent_hour%253D12%2526newscat%253Dtravel%26pbjs_debug%3Dtrue'); }); @@ -192,8 +193,8 @@ describe('The DFP video support module', function () { }); Object.entries({ - 'params': {params: {'iu': 'mock/unit'}}, - 'url': {url: 'https://video.adserver.mock/', params: {'iu': 'mock/unit'}} + 'params': { params: { 'iu': 'mock/unit' } }, + 'url': { url: 'https://video.adserver.mock/', params: { 'iu': 'mock/unit' } } }).forEach(([t, opts]) => { describe(`when using ${t}`, () => { it('should be included if available', () => { @@ -319,7 +320,7 @@ describe('The DFP video support module', function () { ] }).forEach(([param, cases]) => { describe(param, () => { - cases.forEach(({video, expected}) => { + cases.forEach(({ video, expected }) => { describe(`when mediaTypes.video has ${JSON.stringify(video)}`, () => { it(`fills in ${param} = ${expected}`, () => { Object.assign(adUnit.mediaTypes.video, video); @@ -384,8 +385,8 @@ describe('The DFP video support module', function () { data: [ { segment: [ - {id: '1'}, - {id: '2'} + { id: '1' }, + { id: '2' } ] }, ] @@ -398,7 +399,7 @@ describe('The DFP video support module', function () { segtax: 1, }, segment: [ - {id: '3'} + { id: '3' } ] } ] @@ -413,8 +414,8 @@ describe('The DFP video support module', function () { segtax: 4, }, segment: [ - {id: '4-1'}, - {id: '4-2'} + { id: '4-1' }, + { id: '4-2' } ] }, { @@ -422,8 +423,8 @@ describe('The DFP video support module', function () { segtax: 4, }, segment: [ - {id: '4-2'}, - {id: '4-3'} + { id: '4-2' }, + { id: '4-3' } ] }, { @@ -431,8 +432,8 @@ describe('The DFP video support module', function () { segtax: 6, }, segment: [ - {id: '6-1'}, - {id: '6-2'} + { id: '6-1' }, + { id: '6-2' } ] }, { @@ -440,8 +441,8 @@ describe('The DFP video support module', function () { segtax: 6, }, segment: [ - {id: '6-2'}, - {id: '6-3'} + { id: '6-2' }, + { id: '6-3' } ] }, ] @@ -498,7 +499,7 @@ describe('The DFP video support module', function () { before(function () { targetingStub = sinon.stub(targeting, 'getAllTargeting'); - targetingStub.returns({'video1': allTargetingData}); + targetingStub.returns({ 'video1': allTargetingData }); config.setConfig({ enableSendAllBids: true @@ -733,7 +734,7 @@ describe('The DFP video support module', function () { }); it('should substitue vast ad tag uri in gam wrapper with blob content in data uri format', (done) => { - config.setConfig({cache: { useLocal: true }}); + config.setConfig({ cache: { useLocal: true } }); const url = 'https://pubads.g.doubleclick.net/gampad/ads' const blobContent = '` + `` + @@ -870,4 +871,332 @@ describe('The DFP video support module', function () { .finally(config.resetConfig); server.respond(); }); + + describe('Retrieve US Privacy string from GPP when using the IMA player', () => { + beforeEach(() => { + config.setConfig({ cache: { useLocal: true } }); + // Install a fake IMA object, because the us_privacy is only set when IMA is available + window.google = { + ima: { + VERSION: '2.3.37' + } + } + }) + afterEach(() => { + config.resetConfig(); + }) + + async function obtainUsPrivacyInVastXmlRequest() { + const url = 'https://pubads.g.doubleclick.net/gampad/ads' + const bidCacheUrl = 'https://prebid-test-cache-server.org/cache?uuid=4536229c-eddb-45b3-a919-89d889e925aa'; + const gamWrapper = ( + `` + + `` + + `` + + `prebid.org wrapper` + + `` + + `` + + `` + + `` + ); + server.respondWith(gamWrapper); + + const result = getVastXml({ url, adUnit: {}, bid: {}, params: { iu: '/19968336/prebid_cache_video_adunit' } }, []).then(() => { + const request = server.requests[0]; + const url = new URL(request.url); + return url.searchParams.get('us_privacy'); + }); + server.respond(); + + return result; + } + + function obtainUsPrivacyInGamVideoUrl() { + const url = 'https://pubads.g.doubleclick.net/gampad/ads' + return new URLSearchParams(buildDfpVideoUrl({ url, adUnit: {}, bid: {}, params: { iu: '/19968336/prebid_cache_video_adunit' } })).get('us_privacy'); + } + + function mockGpp(gpp) { + sandbox.stub(gppDataHandler, 'getConsentData').returns(gpp) + } + + function wrapParsedSectionsIntoGPPData(parsedSections) { + return { + gppData: { + parsedSections: parsedSections + } + } + } + + it('should use usp when available, even when gpp is available', async () => { + const usPrivacy = '1YYY'; + sandbox.stub(uspDataHandler, 'getConsentData').returns(usPrivacy); + mockGpp(wrapParsedSectionsIntoGPPData({ + "uspv1": { + "Version": 1, + "Notice": "Y", + "OptOutSale": "N", + "LspaCovered": "Y" + } + })); + + const usPrivacyFromRequest = await obtainUsPrivacyInVastXmlRequest(); + expect(usPrivacyFromRequest).to.equal(usPrivacy); + + // In this case, the IMA player will add the us_privacy string + // It is not included in the URL returned by Prebid + const usPrivacyFromUrl = obtainUsPrivacyInGamVideoUrl(); + expect(usPrivacyFromUrl).to.be.null; + }) + + it('no us_privacy when neither usp nor gpp is present', async () => { + const usPrivacyFromRequqest = await obtainUsPrivacyInVastXmlRequest(); + expect(usPrivacyFromRequqest).to.be.null; + + const usPrivacyFromUrl = obtainUsPrivacyInGamVideoUrl(); + expect(usPrivacyFromUrl).to.be.null; + }) + + it('can retrieve from usp section in gpp', async () => { + mockGpp(wrapParsedSectionsIntoGPPData({ + "uspv1": { + "Version": 1, + "Notice": "Y", + "OptOutSale": "N", + "LspaCovered": "Y" + } + })); + + const usPrivacyFromRequest = await obtainUsPrivacyInVastXmlRequest(); + expect(usPrivacyFromRequest).to.equal('1YNY'); + + const usPrivacyFromUrl = obtainUsPrivacyInGamVideoUrl(); + expect(usPrivacyFromUrl).to.equal('1YNY'); + }) + it('can retrieve from usnat section in gpp', async () => { + mockGpp(wrapParsedSectionsIntoGPPData({ + "usnat": { + "Version": 1, + "SharingNotice": 2, + "SaleOptOutNotice": 1, + "SharingOptOutNotice": 0, + "TargetedAdvertisingOptOutNotice": 2, + "SensitiveDataProcessingOptOutNotice": 1, + "SensitiveDataLimitUseNotice": 1, + "SaleOptOut": 1, + "SharingOptOut": 2, + "TargetedAdvertisingOptOut": 2, + "SensitiveDataProcessing": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "KnownChildSensitiveDataConsents": [ + 0, + 0, + 0 + ], + "PersonalDataConsents": 0, + "MspaCoveredTransaction": 1, + "MspaOptOutOptionMode": 0, + "MspaServiceProviderMode": 0, + "GpcSegmentType": 1, + "Gpc": false + } + })); + + const usPrivacyFromRequest = await obtainUsPrivacyInVastXmlRequest(); + expect(usPrivacyFromRequest).to.equal('1YYY'); + + const usPrivacyFromUrl = obtainUsPrivacyInGamVideoUrl(); + expect(usPrivacyFromUrl).to.equal('1YYY'); + }) + it('can retrieve from usnat section in gpp when usnat is an array', async() => { + mockGpp(wrapParsedSectionsIntoGPPData({ + "usnat": [ + { + "Version": 1, + "SharingNotice": 2, + "SaleOptOutNotice": 1, + "SharingOptOutNotice": 1, + "TargetedAdvertisingOptOutNotice": 1, + "SensitiveDataProcessingOptOutNotice": 1, + "SensitiveDataLimitUseNotice": 0, + "SaleOptOut": 2, + "SharingOptOut": 2, + "TargetedAdvertisingOptOut": 2, + "PersonalDataConsents": 0, + "MspaCoveredTransaction": 0, + "MspaOptOutOptionMode": 0, + "MspaServiceProviderMode": 0, + }, { + "GpcSegmentType": 1, + "Gpc": false + } + ] + })) + + const usPrivacyFromRequest = await obtainUsPrivacyInVastXmlRequest(); + expect(usPrivacyFromRequest).to.equal('1YNY'); + + const usPrivacyFromUrl = obtainUsPrivacyInGamVideoUrl(); + expect(usPrivacyFromUrl).to.equal('1YNY'); + }) + it('no us_privacy when either SaleOptOutNotice or SaleOptOut is missing', async () => { + // Missing SaleOptOutNotice + mockGpp(wrapParsedSectionsIntoGPPData({ + "usnat": { + "Version": 1, + "SharingNotice": 2, + "SharingOptOutNotice": 0, + "TargetedAdvertisingOptOutNotice": 2, + "SensitiveDataProcessingOptOutNotice": 1, + "SensitiveDataLimitUseNotice": 1, + "SaleOptOut": 1, + "SharingOptOut": 2, + "TargetedAdvertisingOptOut": 2, + "SensitiveDataProcessing": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "KnownChildSensitiveDataConsents": [ + 0, + 0, + 0 + ], + "PersonalDataConsents": 0, + "MspaCoveredTransaction": 1, + "MspaOptOutOptionMode": 0, + "MspaServiceProviderMode": 0, + "GpcSegmentType": 1, + "Gpc": false + } + })); + + const usPrivacyFromRequest = await obtainUsPrivacyInVastXmlRequest(); + expect(usPrivacyFromRequest).to.be.null; + + const usPrivacyFromUrl = obtainUsPrivacyInGamVideoUrl(); + expect(usPrivacyFromUrl).to.be.null; + }) + it('no us_privacy when either SaleOptOutNotice or SaleOptOut is null', async () => { + // null SaleOptOut + mockGpp(wrapParsedSectionsIntoGPPData({ + "usnat": { + "Version": 1, + "SharingNotice": 2, + "SaleOptOutNotice": 1, + "SharingOptOutNotice": 0, + "TargetedAdvertisingOptOutNotice": 2, + "SensitiveDataProcessingOptOutNotice": 1, + "SensitiveDataLimitUseNotice": 1, + "SaleOptOut": null, + "SharingOptOut": 2, + "TargetedAdvertisingOptOut": 2, + "SensitiveDataProcessing": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "KnownChildSensitiveDataConsents": [ + 0, + 0, + 0 + ], + "PersonalDataConsents": 0, + "MspaCoveredTransaction": 1, + "MspaOptOutOptionMode": 0, + "MspaServiceProviderMode": 0, + "GpcSegmentType": 1, + "Gpc": false + } + })); + + const usPrivacyFromRequest = await obtainUsPrivacyInVastXmlRequest(); + expect(usPrivacyFromRequest).to.be.null; + + const usPrivacyFromUrl = obtainUsPrivacyInGamVideoUrl(); + expect(usPrivacyFromUrl).to.be.null; + }) + + it('can retrieve from usca section in gpp', async () => { + mockGpp(wrapParsedSectionsIntoGPPData({ + "usca": { + "Version": 1, + "SaleOptOutNotice": 1, + "SharingOptOutNotice": 1, + "SensitiveDataLimitUseNotice": 1, + "SaleOptOut": 2, + "SharingOptOut": 2, + "SensitiveDataProcessing": [ + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "KnownChildSensitiveDataConsents": [ + 0, + 0 + ], + "PersonalDataConsents": 0, + "MspaCoveredTransaction": 2, + "MspaOptOutOptionMode": 0, + "MspaServiceProviderMode": 0, + "GpcSegmentType": 1, + "Gpc": false + } + })); + + const usPrivacyFromRequest = await obtainUsPrivacyInVastXmlRequest(); + expect(usPrivacyFromRequest).to.equal('1YNY'); + + const usPrivacyFromUrl = obtainUsPrivacyInGamVideoUrl(); + expect(usPrivacyFromUrl).to.equal('1YNY'); + }) + }); }); diff --git a/test/spec/modules/gamAdpod_spec.js b/test/spec/modules/gamAdpod_spec.js deleted file mode 100644 index 31e142d11f8..00000000000 --- a/test/spec/modules/gamAdpod_spec.js +++ /dev/null @@ -1,257 +0,0 @@ -import {auctionManager} from '../../../src/auctionManager.js'; -import {config} from '../../../src/config.js'; -import {gdprDataHandler, uspDataHandler} from '../../../src/consentHandler.js'; -import parse from 'url-parse'; -import {buildAdpodVideoUrl} from '../../../modules/gamAdpod.js'; -import {expect} from 'chai/index.js'; -import * as utils from '../../../src/utils.js'; -import {server} from '../../mocks/xhr.js'; -import * as adpod from 'modules/adpod.js'; - -describe('gamAdpod', function () { - let amStub; - let amGetAdUnitsStub; - - before(function () { - const 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 () { - config.setConfig({ - adpod: { - brandCategoryExclusion: true, - deferCaching: false - } - }); - }) - - afterEach(function() { - config.resetConfig(); - }); - - after(function () { - amGetAdUnitsStub.restore(); - amStub.restore(); - }); - - 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': { - 'primaryCatId': 'iab-1', - 'adServerCatId': label - }, - 'videoCacheKey': '4cf395af-8fee-4960-af0e-88d44e399f14' - } - } - - it('should return masterTag url', function() { - amStub.returns(getBidsReceived()); - const uspDataHandlerStub = sinon.stub(uspDataHandler, 'getConsentData'); - uspDataHandlerStub.returns('1YYY'); - const gdprDataHandlerStub = sinon.stub(gdprDataHandler, 'getConsentData'); - gdprDataHandlerStub.returns({ - gdprApplies: true, - consentString: 'consent', - addtlConsent: 'moreConsent' - }); - 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 = utils.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', 'vast'); - 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'); - expect(queryParams).to.have.property('gdpr', '1'); - expect(queryParams).to.have.property('gdpr_consent', 'consent'); - expect(queryParams).to.have.property('addtl_consent', 'moreConsent'); - - const custParams = utils.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'); - uspDataHandlerStub.restore(); - gdprDataHandlerStub.restore(); - } - }); - - it('should return masterTag url with correct custom params when brandCategoryExclusion is false', function() { - config.setConfig({ - adpod: { - brandCategoryExclusion: false, - } - }); - function getBids() { - const 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 = utils.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 = utils.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', - } - })); - - server.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'); - } - }); -}) diff --git a/test/spec/modules/gammaBidAdapter_spec.js b/test/spec/modules/gammaBidAdapter_spec.js index bff11ded9fa..cd66741cecb 100644 --- a/test/spec/modules/gammaBidAdapter_spec.js +++ b/test/spec/modules/gammaBidAdapter_spec.js @@ -90,7 +90,7 @@ describe('gammaBidAdapter', function() { 'netRevenue': true, 'ttl': 300, 'ad': '', - 'meta': {'advertiserDomains': ['testdomain.com']} + 'meta': { 'advertiserDomains': ['testdomain.com'] } }]; const result = spec.interpretResponse(serverResponse); expect(Object.keys(result)).to.deep.equal(Object.keys(expectedResponse)); diff --git a/test/spec/modules/gamoshiBidAdapter_spec.js b/test/spec/modules/gamoshiBidAdapter_spec.js index c57000b4de1..9b9565ef7ce 100644 --- a/test/spec/modules/gamoshiBidAdapter_spec.js +++ b/test/spec/modules/gamoshiBidAdapter_spec.js @@ -1,5 +1,5 @@ -import {expect} from 'chai'; -import {spec, helper} from 'modules/gamoshiBidAdapter.js'; +import { expect } from 'chai'; +import { spec, helper } from 'modules/gamoshiBidAdapter.js'; import * as utils from 'src/utils.js'; import { newBidder } from 'src/adapters/bidderFactory.js'; import { config } from 'src/config.js'; @@ -55,7 +55,7 @@ describe('GamoshiAdapter', () => { tid: 'a123456789', } }, - refererInfo: {referer: 'http://examplereferer.com'}, + refererInfo: { referer: 'http://examplereferer.com' }, gdprConsent: { consentString: 'some string', gdprApplies: true @@ -81,7 +81,7 @@ describe('GamoshiAdapter', () => { 'sizes': [[300, 250], [300, 600]], 'transactionId': '1d1a030790a475', 'bidId': 'request-id-12345', - refererInfo: {referer: 'http://examplereferer.com'} + refererInfo: { referer: 'http://examplereferer.com' } }; bannerRequestWithEids = { @@ -112,7 +112,7 @@ describe('GamoshiAdapter', () => { 'sizes': [[300, 250], [300, 600]], 'transactionId': '1d1a030790a475', 'bidId': 'request-id-12345', - refererInfo: {referer: 'http://examplereferer.com'} + refererInfo: { referer: 'http://examplereferer.com' } }; videoBidRequest = { @@ -127,7 +127,7 @@ describe('GamoshiAdapter', () => { 'sizes': [[300, 250], [300, 600]], 'transactionId': 'a123456789', 'bidId': '111', - refererInfo: {referer: 'http://examplereferer.com'} + refererInfo: { referer: 'http://examplereferer.com' } }; rtbResponse = { 'id': 'request-id-12345', @@ -135,8 +135,8 @@ describe('GamoshiAdapter', () => { 'cur': 'USD', 'ext': { 'utrk': [ - {'type': 'iframe', 'url': '//rtb.gamoshi.io/user/sync/1?gdpr=[GDPR]&consent=[CONSENT]&usp=[US_PRIVACY]'}, - {'type': 'image', 'url': '//rtb.gamoshi.io/user/sync/2'} + { 'type': 'iframe', 'url': '//rtb.gamoshi.io/user/sync/1?gdpr=[GDPR]&consent=[CONSENT]&usp=[US_PRIVACY]' }, + { 'type': 'image', 'url': '//rtb.gamoshi.io/user/sync/2' } ] }, 'seatbid': [ @@ -160,7 +160,7 @@ describe('GamoshiAdapter', () => { 'ext': { 'vast_url': 'http://my.vast.com', 'utrk': [ - {'type': 'iframe', 'url': '//p.partner1.io/user/sync/1'} + { 'type': 'iframe', 'url': '//p.partner1.io/user/sync/1' } ] } } @@ -185,7 +185,7 @@ describe('GamoshiAdapter', () => { 'w': 300, 'ext': { 'utrk': [ - {'type': 'image', 'url': '//p.partner2.io/user/sync/1'} + { 'type': 'image', 'url': '//p.partner2.io/user/sync/1' } ] } } @@ -289,19 +289,19 @@ describe('GamoshiAdapter', () => { describe('isBidRequestValid', () => { it('should validate supply-partner ID', () => { - expect(spec.isBidRequestValid({params: {}})).to.equal(false); - expect(spec.isBidRequestValid({params: {supplyPartnerId: 123}})).to.equal(true); - expect(spec.isBidRequestValid({params: {supplyPartnerId: '123'}})).to.equal(true); - expect(spec.isBidRequestValid({params: {supply_partner_id: 123}})).to.equal(true); - expect(spec.isBidRequestValid({params: {supply_partner_id: '123'}})).to.equal(true); - expect(spec.isBidRequestValid({params: {inventory_id: 123}})).to.equal(true); - expect(spec.isBidRequestValid({params: {inventory_id: '123'}})).to.equal(true); - expect(spec.isBidRequestValid({params: {inventory_id: 'kukuk1212'}})).to.equal(false); + expect(spec.isBidRequestValid({ params: {} })).to.equal(false); + expect(spec.isBidRequestValid({ params: { supplyPartnerId: 123 } })).to.equal(true); + expect(spec.isBidRequestValid({ params: { supplyPartnerId: '123' } })).to.equal(true); + expect(spec.isBidRequestValid({ params: { supply_partner_id: 123 } })).to.equal(true); + expect(spec.isBidRequestValid({ params: { supply_partner_id: '123' } })).to.equal(true); + expect(spec.isBidRequestValid({ params: { inventory_id: 123 } })).to.equal(true); + expect(spec.isBidRequestValid({ params: { inventory_id: '123' } })).to.equal(true); + expect(spec.isBidRequestValid({ params: { inventory_id: 'kukuk1212' } })).to.equal(false); }); it('should validate RTB endpoint', () => { - 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' } })).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', @@ -312,20 +312,20 @@ describe('GamoshiAdapter', () => { it('should validate bid floor', () => { // bidfloor can be omitted - should be valid - expect(spec.isBidRequestValid({params: {supplyPartnerId: '123'}})).to.equal(true); + expect(spec.isBidRequestValid({ params: { supplyPartnerId: '123' } })).to.equal(true); // bidfloor as string should be invalid - expect(spec.isBidRequestValid({params: {supplyPartnerId: '123', bidfloor: '123'}})).to.equal(true); + expect(spec.isBidRequestValid({ params: { supplyPartnerId: '123', bidfloor: '123' } })).to.equal(true); // bidfloor as zero should be invalid (not positive) - expect(spec.isBidRequestValid({params: {supplyPartnerId: '123', bidfloor: 0}})).to.equal(false); + expect(spec.isBidRequestValid({ params: { supplyPartnerId: '123', bidfloor: 0 } })).to.equal(false); // bidfloor as negative number should be invalid - expect(spec.isBidRequestValid({params: {supplyPartnerId: '123', bidfloor: -0.5}})).to.equal(false); + expect(spec.isBidRequestValid({ params: { supplyPartnerId: '123', bidfloor: -0.5 } })).to.equal(false); // bidfloor as positive number should be valid - expect(spec.isBidRequestValid({params: {supplyPartnerId: '123', bidfloor: 0.1}})).to.equal(true); - expect(spec.isBidRequestValid({params: {supplyPartnerId: '123', bidfloor: 1.5}})).to.equal(true); + expect(spec.isBidRequestValid({ params: { supplyPartnerId: '123', bidfloor: 0.1 } })).to.equal(true); + expect(spec.isBidRequestValid({ params: { supplyPartnerId: '123', bidfloor: 1.5 } })).to.equal(true); // // const getFloorResponse = {currency: 'USD', floor: 5}; // let testBidRequest = deepClone(bidRequest); @@ -372,8 +372,8 @@ describe('GamoshiAdapter', () => { 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'}); + 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); @@ -548,11 +548,11 @@ describe('GamoshiAdapter', () => { describe('interpretResponse', () => { it('returns an empty array on missing response', () => { - let response = spec.interpretResponse(undefined, {bidRequest: bannerBidRequest}); + let response = spec.interpretResponse(undefined, { bidRequest: bannerBidRequest }); expect(Array.isArray(response)).to.equal(true); expect(response.length).to.equal(0); - response = spec.interpretResponse({}, {bidRequest: bannerBidRequest}); + response = spec.interpretResponse({}, { bidRequest: bannerBidRequest }); expect(Array.isArray(response)).to.equal(true); expect(response.length).to.equal(0); @@ -578,7 +578,7 @@ describe('GamoshiAdapter', () => { const mockOrtbRequest = { imp: [{ id: '1', tagid: bannerBidRequest.adUnitCode }] }; - const response = spec.interpretResponse({body: rtbResponse}, {data: mockOrtbRequest, bidRequest: bannerBidRequest}); + const response = spec.interpretResponse({ body: rtbResponse }, { data: mockOrtbRequest, bidRequest: bannerBidRequest }); expect(Array.isArray(response)).to.equal(true); // The ORTB converter handles response processing, just verify it returns an array }); @@ -587,13 +587,13 @@ describe('GamoshiAdapter', () => { const mockOrtbRequest = { imp: [{ id: '1', tagid: videoBidRequest.adUnitCode }] }; - const response = spec.interpretResponse({body: videoResponse}, {data: mockOrtbRequest, bidRequest: videoBidRequest}); + const response = spec.interpretResponse({ body: videoResponse }, { data: mockOrtbRequest, bidRequest: videoBidRequest }); expect(Array.isArray(response)).to.equal(true); // The ORTB converter handles response processing, just verify it returns an array }); it('aggregates user-sync pixels', () => { - const response = spec.getUserSyncs({}, [{body: rtbResponse}]); + 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); @@ -612,12 +612,12 @@ describe('GamoshiAdapter', () => { const mockOrtbRequest = { imp: [{ id: '1', tagid: videoRequest.adUnitCode }] }; - const result = spec.interpretResponse({body: videoResponse}, {data: mockOrtbRequest, bidRequest: videoRequest}); + const result = spec.interpretResponse({ body: videoResponse }, { data: mockOrtbRequest, bidRequest: videoRequest }); expect(Array.isArray(result)).to.equal(true); }); it('validates in/existing of gdpr consent', () => { - let result = spec.getUserSyncs({}, [{body: videoResponse}], gdprConsent, 'gamoshiCCPA'); + let result = spec.getUserSyncs({}, [{ body: videoResponse }], gdprConsent, 'gamoshiCCPA'); // print result expect(result).to.be.an('array'); @@ -626,14 +626,14 @@ describe('GamoshiAdapter', () => { expect(result[0].url).to.equal('https://rtb.gamoshi.io/pix/1275/scm?cb=1545900621675&gdpr=1&consent=consent%20string&us_privacy=gamoshiCCPA'); gdprConsent.gdprApplies = false; - result = spec.getUserSyncs({}, [{body: videoResponse}], gdprConsent, 'gamoshiCCPA'); + result = spec.getUserSyncs({}, [{ body: videoResponse }], gdprConsent, 'gamoshiCCPA'); 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&gdpr=0&consent=&us_privacy=gamoshiCCPA'); videoResponse.ext.utrk[0].url = 'https://rtb.gamoshi.io/pix/1275/scm'; - result = spec.getUserSyncs({}, [{body: videoResponse}], gdprConsent); + 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'); @@ -641,21 +641,21 @@ describe('GamoshiAdapter', () => { }); it('validates existence of gdpr, gdpr consent and usp consent', () => { - let result = spec.getUserSyncs({}, [{body: videoResponse}], gdprConsent, 'gamoshiCCPA'); + let result = spec.getUserSyncs({}, [{ body: videoResponse }], gdprConsent, 'gamoshiCCPA'); 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&gdpr=1&consent=consent%20string&us_privacy=gamoshiCCPA'); gdprConsent.gdprApplies = false; - result = spec.getUserSyncs({}, [{body: videoResponse}], gdprConsent, ''); + 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&gdpr=0&consent=&us_privacy='); videoResponse.ext.utrk[0].url = 'https://rtb.gamoshi.io/pix/1275/scm'; - result = spec.getUserSyncs({}, [{body: videoResponse}], gdprConsent); + 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'); @@ -687,7 +687,7 @@ describe('GamoshiAdapter', () => { const mockOrtbRequest = { imp: [{ id: '1', tagid: videoBidRequest.adUnitCode }] }; - const response = spec.interpretResponse({body: videoResponseWithMeta}, {data: mockOrtbRequest, bidRequest: videoBidRequest}); + const response = spec.interpretResponse({ body: videoResponseWithMeta }, { data: mockOrtbRequest, bidRequest: videoBidRequest }); expect(Array.isArray(response)).to.equal(true); }); diff --git a/test/spec/modules/gemiusIdSystem_spec.js b/test/spec/modules/gemiusIdSystem_spec.js index 76c4549a321..7a2fa2ada7a 100644 --- a/test/spec/modules/gemiusIdSystem_spec.js +++ b/test/spec/modules/gemiusIdSystem_spec.js @@ -47,7 +47,15 @@ describe('GemiusId module', function () { consents: { 1: true, 2: false, - 3: true, 4: true, 5: true, 6: true, 7: true, 8: true, 9: true, 10: true, 11: true + 3: true, + 4: true, + 5: true, + 6: true, + 7: true, + 8: true, + 9: true, + 10: true, + 11: true } } } @@ -69,7 +77,7 @@ describe('GemiusId module', function () { const result = gemiusIdSubmodule.getId({}, { gdpr: gdprConsentData }); - expect(result).to.deep.equal({id: {id: null}}); + expect(result).to.deep.equal({ id: { id: null } }); }); it('should return callback on consent', function () { @@ -92,12 +100,12 @@ describe('GemiusId module', function () { expect(callback).to.be.a('function'); const testRuid = 'test-ruid-123'; - const statusOk = {status: 'ok'}; + const statusOk = { status: 'ok' }; callback(testRuid, statusOk); }); gemiusIdSubmodule.getId().callback((resultId) => { - expect(resultId).to.deep.equal({id: 'test-ruid-123'}); + expect(resultId).to.deep.equal({ id: 'test-ruid-123' }); expect(mockWindow.gemius_cmd.calledOnce).to.be.true; done(); }); @@ -134,7 +142,7 @@ describe('GemiusId module', function () { describe('decode', function () { it('should return object with gemiusId when value exists', function () { - const result = gemiusIdSubmodule.decode({id: 'test-gemius-id'}); + const result = gemiusIdSubmodule.decode({ id: 'test-gemius-id' }); expect(result).to.deep.equal({ gemiusId: 'test-gemius-id' }); diff --git a/test/spec/modules/genericAnalyticsAdapter_spec.js b/test/spec/modules/genericAnalyticsAdapter_spec.js index 72ec0e10dae..015bd9614f4 100644 --- a/test/spec/modules/genericAnalyticsAdapter_spec.js +++ b/test/spec/modules/genericAnalyticsAdapter_spec.js @@ -1,8 +1,8 @@ -import {defaultHandler, GenericAnalytics} from '../../../modules/genericAnalyticsAdapter.js'; +import { defaultHandler, GenericAnalytics } from '../../../modules/genericAnalyticsAdapter.js'; import * as events from 'src/events.js'; -import {EVENTS} from 'src/constants.js'; +import { EVENTS } from 'src/constants.js'; -const {AUCTION_INIT, BID_RESPONSE} = EVENTS; +const { AUCTION_INIT, BID_RESPONSE } = EVENTS; describe('Generic analytics', () => { describe('adapter', () => { @@ -98,12 +98,12 @@ describe('Generic analytics', () => { batchSize: 2 } }); - events.emit(AUCTION_INIT, {i: 0}); + events.emit(AUCTION_INIT, { i: 0 }); sinon.assert.notCalled(handler); - events.emit(BID_RESPONSE, {i: 0}); + events.emit(BID_RESPONSE, { i: 0 }); sinon.assert.calledWith(handler, sinon.match((arg) => { - return sinon.match({eventType: AUCTION_INIT, args: {i: 0}}).test(arg[0]) && - sinon.match({eventType: BID_RESPONSE, args: {i: 0}}).test(arg[1]); + return sinon.match({ eventType: AUCTION_INIT, args: { i: 0 } }).test(arg[0]) && + sinon.match({ eventType: BID_RESPONSE, args: { i: 0 } }).test(arg[1]); })); }); @@ -115,15 +115,15 @@ describe('Generic analytics', () => { } }); handler.throws(new Error()); - events.emit(AUCTION_INIT, {i: 0}); + events.emit(AUCTION_INIT, { i: 0 }); let recv; handler.resetHistory(); handler.resetBehavior(); handler.callsFake((arg) => { recv = arg; }); - events.emit(BID_RESPONSE, {i: 1}); - sinon.assert.match(recv, [sinon.match({eventType: BID_RESPONSE, args: {i: 1}})]) + events.emit(BID_RESPONSE, { i: 1 }); + sinon.assert.match(recv, [sinon.match({ eventType: BID_RESPONSE, args: { i: 1 } })]) }); it('should not cause infinite recursion, if handler triggers more events', () => { @@ -151,7 +151,7 @@ describe('Generic analytics', () => { handler } }); - [0, 1, 2].forEach(i => events.emit(BID_RESPONSE, {i})); + [0, 1, 2].forEach(i => events.emit(BID_RESPONSE, { i })); sinon.assert.calledOnce(handler); clock.tick(100); sinon.assert.calledTwice(handler); @@ -165,10 +165,10 @@ describe('Generic analytics', () => { handler } }); - [0, 1, 2].forEach(i => events.emit(BID_RESPONSE, {i})); + [0, 1, 2].forEach(i => events.emit(BID_RESPONSE, { i })); sinon.assert.calledOnce(handler); clock.tick(50); - events.emit(BID_RESPONSE, {i: 3}); + events.emit(BID_RESPONSE, { i: 3 }); sinon.assert.calledTwice(handler); clock.tick(100); sinon.assert.calledTwice(handler); @@ -204,8 +204,8 @@ describe('Generic analytics', () => { } } }); - events.emit(BID_RESPONSE, {prop: 'value', i: 0}); - sinon.assert.calledWith(handler, sinon.match(data => sinon.match({extra: 'data', prop: 'value'}).test(data[0]))); + events.emit(BID_RESPONSE, { prop: 'value', i: 0 }); + sinon.assert.calledWith(handler, sinon.match(data => sinon.match({ extra: 'data', prop: 'value' }).test(data[0]))); }); it('does not choke if an event handler throws', () => { @@ -223,9 +223,9 @@ describe('Generic analytics', () => { } }); events.emit(AUCTION_INIT, {}); - events.emit(BID_RESPONSE, {i: 0}); + events.emit(BID_RESPONSE, { i: 0 }); sinon.assert.calledOnce(handler); - sinon.assert.calledWith(handler, sinon.match(data => sinon.match({i: 0}).test(data[0]))); + sinon.assert.calledWith(handler, sinon.match(data => sinon.match({ i: 0 }).test(data[0]))); }); it('filters out events when their handler returns undefined', () => { @@ -240,10 +240,10 @@ describe('Generic analytics', () => { } } }); - events.emit(AUCTION_INIT, {i: 0}); - events.emit(BID_RESPONSE, {i: 1}); + events.emit(AUCTION_INIT, { i: 0 }); + events.emit(BID_RESPONSE, { i: 1 }); sinon.assert.calledOnce(handler); - sinon.assert.calledWith(handler, sinon.match(data => sinon.match({i: 0}).test(data[0]))); + sinon.assert.calledWith(handler, sinon.match(data => sinon.match({ i: 0 }).test(data[0]))); }); }); }); @@ -263,22 +263,22 @@ describe('Generic analytics', () => { }).forEach(([method, parse]) => { describe(`when HTTP method is ${method}`, () => { it('should send single event when batchSize is 1', () => { - const handler = defaultHandler({url, method, batchSize: 1, ajax}); - const payload = {i: 0}; + const handler = defaultHandler({ url, method, batchSize: 1, ajax }); + const payload = { i: 0 }; handler([payload, {}]); sinon.assert.calledWith(ajax, url, sinon.match.any, sinon.match(data => sinon.match(payload).test(parse(data))), - {method, keepalive: true} + { method, keepalive: true } ); }); it('should send multiple events when batchSize is greater than 1', () => { - const handler = defaultHandler({url, method, batchSize: 10, ajax}); - const payload = [{i: 0}, {i: 1}]; + const handler = defaultHandler({ url, method, batchSize: 10, ajax }); + const payload = [{ i: 0 }, { i: 1 }]; handler(payload); sinon.assert.calledWith(ajax, url, sinon.match.any, sinon.match(data => sinon.match(payload).test(parse(data))), - {method, keepalive: true} + { method, keepalive: true } ); }); }); diff --git a/test/spec/modules/geolocationRtdProvider_spec.js b/test/spec/modules/geolocationRtdProvider_spec.js index 26a9eef1e24..b34a6dd1953 100644 --- a/test/spec/modules/geolocationRtdProvider_spec.js +++ b/test/spec/modules/geolocationRtdProvider_spec.js @@ -1,16 +1,16 @@ -import {expect} from 'chai'; -import {geolocationSubmodule} from 'modules/geolocationRtdProvider.js'; +import { expect } from 'chai'; +import { geolocationSubmodule } from 'modules/geolocationRtdProvider.js'; import * as activityRules from 'src/activities/rules.js'; import 'src/prebid.js'; -import {PbPromise} from '../../../src/utils/promise.js'; -import {ACTIVITY_TRANSMIT_PRECISE_GEO} from '../../../src/activities/activities.js'; +import { PbPromise } from '../../../src/utils/promise.js'; +import { ACTIVITY_TRANSMIT_PRECISE_GEO } from '../../../src/activities/activities.js'; describe('Geolocation RTD Provider', function () { let sandbox; before(() => { if (!navigator.permissions) { - navigator.permissions = {mock: true, query: false} + navigator.permissions = { mock: true, query: false } } }); @@ -48,13 +48,13 @@ describe('Geolocation RTD Provider', function () { beforeEach(() => { onDone = sinon.stub(); permState = 'prompt'; - rtdConfig = {params: {}}; + rtdConfig = { params: {} }; clock = sandbox.useFakeTimers({ now: 11000, shouldClearNativeTimers: true }); sandbox.stub(navigator.geolocation, 'getCurrentPosition').value((cb) => { - cb({coords: {latitude: 1, longitude: 2}, timestamp: 1000}); + cb({ coords: { latitude: 1, longitude: 2 }, timestamp: 1000 }); }); permGiven = new Promise((resolve) => { sandbox.stub(navigator.permissions, 'query').value(() => { @@ -88,7 +88,7 @@ describe('Geolocation RTD Provider', function () { }); it(`should set geolocation`, async () => { - const requestBidObject = {ortb2Fragments: {global: {}}}; + const requestBidObject = { ortb2Fragments: { global: {} } }; geolocationSubmodule.getBidRequestData(requestBidObject, onDone, rtdConfig); await permGiven; clock.tick(300); @@ -112,7 +112,7 @@ describe('Geolocation RTD Provider', function () { beforeEach(setup); it(`should NOT set geo`, () => { - const req = {ortb2Fragments: {global: {}}}; + const req = { ortb2Fragments: { global: {} } }; geolocationSubmodule.getBidRequestData(req, onDone, rtdConfig); clock.tick(300); expect(req.ortb2Fragments.global.device?.geo).to.not.exist; diff --git a/test/spec/modules/getintentBidAdapter_spec.js b/test/spec/modules/getintentBidAdapter_spec.js index 35788f6f992..b2d836b54f7 100644 --- a/test/spec/modules/getintentBidAdapter_spec.js +++ b/test/spec/modules/getintentBidAdapter_spec.js @@ -1,6 +1,6 @@ import { expect } from 'chai' import { spec } from 'modules/getintentBidAdapter.js' -import {deepClone} from 'src/utils'; +import { deepClone } from 'src/utils'; describe('GetIntent Adapter Tests:', function () { const bidRequests = [{ @@ -133,7 +133,7 @@ describe('GetIntent Adapter Tests:', function () { const bidRequestWithFloor = deepClone(bidRequests[0]); bidRequestWithFloor.params.floor = 10 bidRequestWithFloor.params.cur = 'USD' - const getFloorResponse = {floor: 5, currency: 'EUR'}; + const getFloorResponse = { floor: 5, currency: 'EUR' }; bidRequestWithFloor.getFloor = () => getFloorResponse; const serverRequests = spec.buildRequests([bidRequestWithFloor]); diff --git a/test/spec/modules/glomexBidAdapter_spec.js b/test/spec/modules/glomexBidAdapter_spec.js index 8c53db8d605..e5352c5efac 100644 --- a/test/spec/modules/glomexBidAdapter_spec.js +++ b/test/spec/modules/glomexBidAdapter_spec.js @@ -114,8 +114,8 @@ describe('glomexBidAdapter', function () { 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) + expect(spec.interpretResponse({ body: {} }, { validBidRequests: [] }).length).to.equal(0) + expect(spec.interpretResponse({ body: [] }, { validBidRequests: [] }).length).to.equal(0) }) it('handles the server response', function () { diff --git a/test/spec/modules/gmosspBidAdapter_spec.js b/test/spec/modules/gmosspBidAdapter_spec.js index b3d0c20f3d4..e18ce39109a 100644 --- a/test/spec/modules/gmosspBidAdapter_spec.js +++ b/test/spec/modules/gmosspBidAdapter_spec.js @@ -72,7 +72,7 @@ describe('GmosspAdapter', 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('tid=791e9d84-af92-4903-94da-24c7426d9d0c&bid=2b84475b5b636e&ver=$prebid.version$&sid=123456&im_uid=h.0a4749e7ffe09fa6&shared_id=1111&idl_env=1111&url=https%3A%2F%2Fhoge.com' + '&ref=' + encodeURIComponent(document.referrer) + '&cur=JPY&dnt=0&'); + expect(requests[0].data).to.equal('tid=791e9d84-af92-4903-94da-24c7426d9d0c&bid=2b84475b5b636e&ver=$prebid.version$&sid=123456&im_uid=h.0a4749e7ffe09fa6&shared_id=1111&idl_env=1111&url=https%3A%2F%2Fhoge.com' + '&ref=' + encodeURIComponent(document.referrer) + '&cur=JPY&'); }); it('should use fallback if refererInfo.referer in bid request is empty and im_uid ,shared_id, idl_env cookie is empty', function () { @@ -86,7 +86,7 @@ describe('GmosspAdapter', function () { bidRequests[0].userId.idl_env = ''; const requests = spec.buildRequests(bidRequests, bidderRequest); - const result = 'tid=791e9d84-af92-4903-94da-24c7426d9d0c&bid=2b84475b5b636e&ver=$prebid.version$&sid=123456&ref=' + encodeURIComponent(document.referrer) + '&cur=JPY&dnt=0&'; + const result = 'tid=791e9d84-af92-4903-94da-24c7426d9d0c&bid=2b84475b5b636e&ver=$prebid.version$&sid=123456&ref=' + encodeURIComponent(document.referrer) + '&cur=JPY&'; expect(requests[0].data).to.equal(result); }); }); diff --git a/test/spec/modules/goldbachBidAdapter_spec.js b/test/spec/modules/goldbachBidAdapter_spec.js index ac1207f6d19..caf0efec2ec 100644 --- a/test/spec/modules/goldbachBidAdapter_spec.js +++ b/test/spec/modules/goldbachBidAdapter_spec.js @@ -388,7 +388,7 @@ describe('GoldbachBidAdapter', function () { describe('interpretResponse', function () { it('should map response to valid bids (amount)', function () { const bidRequest = spec.buildRequests(validBidRequests, validBidderRequest); - const bidResponse = deepClone({body: validOrtbBidResponse}); + const bidResponse = deepClone({ body: validOrtbBidResponse }); const response = spec.interpretResponse(bidResponse, bidRequest); expect(response).to.exist; @@ -400,7 +400,7 @@ describe('GoldbachBidAdapter', function () { if (FEATURES.VIDEO) { it('should attach a custom video renderer ', function () { const bidRequest = spec.buildRequests(validBidRequests, validBidderRequest); - const bidResponse = deepClone({body: validOrtbBidResponse}); + const bidResponse = deepClone({ body: validOrtbBidResponse }); bidResponse.body.seatbid[0].bid[1].adm = ''; bidResponse.body.seatbid[0].bid[1].ext = { prebid: { type: 'video', meta: { type: 'video_outstream' } } }; const response = spec.interpretResponse(bidResponse, bidRequest); @@ -411,7 +411,7 @@ describe('GoldbachBidAdapter', function () { it('should set the player accordingly to config', function () { const bidRequest = spec.buildRequests(validBidRequests, validBidderRequest); - const bidResponse = deepClone({body: validOrtbBidResponse}); + const bidResponse = deepClone({ body: validOrtbBidResponse }); bidResponse.body.seatbid[0].bid[1].adm = ''; bidResponse.body.seatbid[0].bid[1].ext = { prebid: { type: 'video', meta: { type: 'video_outstream' } } }; validBidRequests[1].mediaTypes.video.playbackmethod = 1; @@ -427,7 +427,7 @@ describe('GoldbachBidAdapter', function () { it('should not attach a custom video renderer when VAST url/xml is missing', function () { const bidRequest = spec.buildRequests(validBidRequests, validBidderRequest); - const bidResponse = deepClone({body: validOrtbBidResponse}); + const bidResponse = deepClone({ body: validOrtbBidResponse }); bidResponse.body.seatbid[0].bid[1].adm = undefined; bidResponse.body.seatbid[0].bid[1].ext = { prebid: { type: 'video', meta: { type: 'video_outstream' } } }; const response = spec.interpretResponse(bidResponse, bidRequest); @@ -444,8 +444,9 @@ describe('GoldbachBidAdapter', function () { purpose: { consents: 1 } - }}; - const syncOptions = {pixelEnabled: true, iframeEnabled: true}; + } + }; + const syncOptions = { pixelEnabled: true, iframeEnabled: true }; const userSyncs = spec.getUserSyncs(syncOptions, {}, gdprConsent, {}); expect(userSyncs[0].type).to.equal('image'); @@ -459,8 +460,9 @@ describe('GoldbachBidAdapter', function () { purpose: { consents: 1 } - }}; - const syncOptions = {iframeEnabled: true}; + } + }; + const syncOptions = { iframeEnabled: true }; const userSyncs = spec.getUserSyncs(syncOptions, {}, gdprConsent, {}); expect(userSyncs[0].type).to.equal('iframe'); @@ -478,7 +480,7 @@ describe('GoldbachBidAdapter', function () { } } }; - const synOptions = {pixelEnabled: true, iframeEnabled: true}; + const synOptions = { pixelEnabled: true, iframeEnabled: true }; const userSyncs = spec.getUserSyncs(synOptions, {}, gdprConsent, {}); expect(userSyncs[0].url).to.contain(`https://ib.adnxs.com/getuid?${ENDPOINT_COOKIESYNC}`); expect(userSyncs[0].url).to.contain('xandrId=$UID'); diff --git a/test/spec/modules/goldfishAdsRtdProvider_spec.js b/test/spec/modules/goldfishAdsRtdProvider_spec.js index acbba5190df..515a51fc735 100755 --- a/test/spec/modules/goldfishAdsRtdProvider_spec.js +++ b/test/spec/modules/goldfishAdsRtdProvider_spec.js @@ -123,7 +123,7 @@ describe('goldfishAdsRtdProvider is a RTD provider that', function () { describe('has an updateUserData that', function () { it('properly transforms the response', function () { const userData = { - segment: [{id: '1'}, {id: '2'}], + segment: [{ id: '1' }, { id: '2' }], ext: { segtax: 4, } @@ -142,7 +142,7 @@ describe('goldfishAdsRtdProvider is a RTD provider that', function () { storage.setDataInLocalStorage(DATA_STORAGE_KEY, JSON.stringify({ targeting: { name: 'goldfishads.com', - segment: [{id: '1'}, {id: '2'}], + segment: [{ id: '1' }, { id: '2' }], ext: { segtax: 4, } diff --git a/test/spec/modules/gppControl_usstates_spec.js b/test/spec/modules/gppControl_usstates_spec.js index 1e9eb4176a8..2703678c782 100644 --- a/test/spec/modules/gppControl_usstates_spec.js +++ b/test/spec/modules/gppControl_usstates_spec.js @@ -1,4 +1,4 @@ -import {DEFAULT_SID_MAPPING, getSections, NORMALIZATIONS, normalizer} from '../../../modules/gppControl_usstates.js'; +import { DEFAULT_SID_MAPPING, getSections, NORMALIZATIONS, normalizer } from '../../../modules/gppControl_usstates.js'; describe('normalizer', () => { it('sets nullify fields to null', () => { @@ -23,7 +23,7 @@ describe('normalizer', () => { }); }); it('initializes scalar fields to null', () => { - const res = normalizer({}, {untouched: 0, f1: 0, f2: 0})({untouched: 0}); + const res = normalizer({}, { untouched: 0, f1: 0, f2: 0 })({ untouched: 0 }); expect(res).to.eql({ untouched: 0, f1: null, @@ -31,7 +31,7 @@ describe('normalizer', () => { }) }) it('initializes list fields to null-array with correct size', () => { - const res = normalizer({}, {'l1': 2, 'l2': 3})({}); + const res = normalizer({}, { 'l1': 2, 'l2': 3 })({}); expect(res).to.eql({ l1: [null, null], l2: [null, null, null] @@ -45,7 +45,7 @@ describe('normalizer', () => { 'arrays of the same size, with moves': [ [1, 2, 3], [1, 3, 2], - {2: 3, 3: 2} + { 2: 3, 3: 2 } ], 'original larger than normal': [ [1, 2, 3], @@ -54,7 +54,7 @@ describe('normalizer', () => { 'original larger than normal, with moves': [ [1, 2, 3], [null, 1], - {1: 2} + { 1: 2 } ], 'normal larger than original': [ [1, 2], @@ -63,7 +63,7 @@ describe('normalizer', () => { 'normal larger than original, with moves': [ [1, 2], [2, null, 2], - {2: [1, 3]} + { 2: [1, 3] } ], 'original is scalar': [ 'value', @@ -75,7 +75,7 @@ describe('normalizer', () => { ] }).forEach(([t, [from, to, move]]) => { it(`carries over values for list fields - ${t}`, () => { - const res = normalizer({move: {field: move || {}}}, {field: Array.isArray(to) ? to.length : 0})({field: from}); + const res = normalizer({ move: { field: move || {} } }, { field: Array.isArray(to) ? to.length : 0 })({ field: from }); expect(res.field).to.eql(to); }); }); @@ -92,17 +92,17 @@ describe('normalizer', () => { const res = normalizer({ nullify: ['nulled'], move: { - multi: {1: 2} + multi: { 1: 2 } }, fn - }, {nulled: 0, untouched: 0, multi: 2})(orig); + }, { nulled: 0, untouched: 0, multi: 2 })(orig); const transformed = { nulled: null, untouched: 0, multi: [null, 'a'] }; sinon.assert.calledWith(fn, orig, sinon.match(transformed)); - expect(res).to.eql(Object.assign({fn: true}, transformed)); + expect(res).to.eql(Object.assign({ fn: true }, transformed)); }); }); @@ -485,7 +485,7 @@ describe('getSections', () => { }); it('filters by sid', () => { - expect(getSections({sids: [8]})).to.eql([ + expect(getSections({ sids: [8] })).to.eql([ ['usca', [8], NORMALIZATIONS[8]] ]); }); diff --git a/test/spec/modules/gptPreAuction_spec.js b/test/spec/modules/gptPreAuction_spec.js index c369597ecbd..78e169aa970 100644 --- a/test/spec/modules/gptPreAuction_spec.js +++ b/test/spec/modules/gptPreAuction_spec.js @@ -30,7 +30,7 @@ describe('GPT pre-auction module', () => { makeSlot({ code: 'slotCode4', divId: 'div5' }) ]; - const mockTargeting = {'/123456/header-bid-tag-0': {'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'}} + const mockTargeting = { '/123456/header-bid-tag-0': { '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' } } const mockAuctionManager = { findBidByAdId(adId) { @@ -163,7 +163,7 @@ describe('GPT pre-auction module', () => { window.googletag.pubads().setSlots([ makeSlot({ code: '/12345,21212/slot', divId: 'div1' }), ]); - const adUnit = {code: '/12345,21212/slot'}; + const adUnit = { code: '/12345,21212/slot' }; expect(appendGptSlots([adUnit])).to.eql({ '/12345,21212/slot': '/12345,21212/slot' }) @@ -183,9 +183,8 @@ describe('GPT pre-auction module', () => { it('should use the customGptSlotMatching function if one is given', () => { config.setConfig({ - gptPreAuction: { - customGptSlotMatching: slot => - adUnitCode => adUnitCode.toUpperCase() === slot.getAdUnitPath().toUpperCase() + customGptSlotMatching: slot => { + return (adUnitCode) => adUnitCode.toUpperCase() === slot.getAdUnitPath().toUpperCase(); } }); @@ -194,6 +193,7 @@ describe('GPT pre-auction module', () => { appendGptSlots([adUnit]); expect(adUnit.ortb2Imp.ext.data.adserver).to.be.an('object'); expect(adUnit.ortb2Imp.ext.data.adserver).to.deep.equal({ name: 'gam', adslot: 'slotCode1' }); + config.resetConfig(); }); }); @@ -208,27 +208,14 @@ describe('GPT pre-auction module', () => { expect(_currentConfig).to.be.an('object').that.is.empty; }); - it('should accept custom functions in config', () => { - config.setConfig({ - gptPreAuction: { - customGptSlotMatching: () => 'customGptSlot', - } - }); - - expect(_currentConfig.enabled).to.equal(true); - expect(_currentConfig.customGptSlotMatching).to.a('function'); - expect(_currentConfig.customGptSlotMatching()).to.equal('customGptSlot'); - }); - it('should check that custom functions in config are type function', () => { config.setConfig({ gptPreAuction: { - customGptSlotMatching: 12345, + customPreAuction: 12345, } }); expect(_currentConfig).to.deep.equal({ enabled: true, - customGptSlotMatching: false, customPreAuction: false, useDefaultPreAuction: true }); @@ -339,7 +326,7 @@ describe('GPT pre-auction module', () => { window.googletag.pubads().setSlots([ makeSlot({ code: '/12345,21212/slot', divId: 'div1' }), ]); - const adUnit = {code: '/12345,21212/slot'}; + const adUnit = { code: '/12345,21212/slot' }; makeBidRequestsHook(sinon.stub(), [adUnit]); sinon.assert.calledWith(customPreAuction, adUnit, '/12345/slot', adUnit.code); }); @@ -436,7 +423,7 @@ describe('GPT pre-auction module', () => { window.googletag.pubads().setSlots([ makeSlot({ code: '/12345,21212/slot', divId: 'div1' }), ]); - const adUnit = {code: '/12345,21212/slot'}; + const adUnit = { code: '/12345,21212/slot' }; makeBidRequestsHook(sinon.stub(), [adUnit]); expect(adUnit.ortb2Imp.ext.gpid).to.eql('/12345/slot'); }); @@ -464,7 +451,7 @@ describe('GPT pre-auction module', () => { it('should filter out adIds that do not map to any auction', () => { const auctionsIds = getAuctionsIdsFromTargeting({ ...mockTargeting, - 'au': {'hb_adid': 'missing'}, + 'au': { 'hb_adid': 'missing' }, }, mockAuctionManager); expect(auctionsIds).to.eql([mocksAuctions[0].auctionId, mocksAuctions[1].auctionId]); }) @@ -473,7 +460,7 @@ describe('GPT pre-auction module', () => { let auctionsIds = getAuctionsIdsFromTargeting({}, mockAuctionManager); expect(Array.isArray(auctionsIds)).to.equal(true); expect(auctionsIds).to.length(0); - auctionsIds = getAuctionsIdsFromTargeting({'/123456/header-bid-tag-0/bg': {'invalidContent': '123'}}, mockAuctionManager); + auctionsIds = getAuctionsIdsFromTargeting({ '/123456/header-bid-tag-0/bg': { 'invalidContent': '123' } }, mockAuctionManager); expect(Array.isArray(auctionsIds)).to.equal(true); expect(auctionsIds).to.length(0); }); diff --git a/test/spec/modules/gravitoIdSystem_spec.js b/test/spec/modules/gravitoIdSystem_spec.js index 9584f60c81d..095dd92cdd1 100644 --- a/test/spec/modules/gravitoIdSystem_spec.js +++ b/test/spec/modules/gravitoIdSystem_spec.js @@ -27,7 +27,7 @@ describe('gravitompId module', function () { it('should return the gravitompId when it exists in cookie', function () { getCookieStub.withArgs(cookieKey).returns(GRAVITOID_TEST_VALUE); const id = gravitoIdSystemSubmodule.getId(); - expect(id).to.be.deep.equal({id: {gravitompId: GRAVITOID_TEST_VALUE}}); + expect(id).to.be.deep.equal({ id: { gravitompId: GRAVITOID_TEST_VALUE } }); }); cookieTestCasesForEmpty.forEach(testCase => it('should return the gravitompId when it not exists in cookie', function () { @@ -40,7 +40,7 @@ describe('gravitompId module', function () { describe('decode()', function () { it('should return the gravitompId when it exists in cookie', function () { const decoded = gravitoIdSystemSubmodule.decode(GRAVITOID_TEST_OBJ); - expect(decoded).to.be.deep.equal({gravitompId: GRAVITOID_TEST_VALUE}); + expect(decoded).to.be.deep.equal({ gravitompId: GRAVITOID_TEST_VALUE }); }); it('should return the undefined when decode id is not "string"', function () { diff --git a/test/spec/modules/greenbidsAnalyticsAdapter_spec.js b/test/spec/modules/greenbidsAnalyticsAdapter_spec.js index fb59241553d..b6ac130a98d 100644 --- a/test/spec/modules/greenbidsAnalyticsAdapter_spec.js +++ b/test/spec/modules/greenbidsAnalyticsAdapter_spec.js @@ -107,7 +107,7 @@ describe('Greenbids Prebid AnalyticsAdapter Testing', function () { cpm: 0.08, currency: 'USD', ad: 'fake ad2', - params: {'placement ID': 12784} + params: { 'placement ID': 12784 } }, { auctionId: auctionId, diff --git a/test/spec/modules/greenbidsBidAdapter_spec.js b/test/spec/modules/greenbidsBidAdapter_spec.js index 00c0005fe16..6ff7777026e 100644 --- a/test/spec/modules/greenbidsBidAdapter_spec.js +++ b/test/spec/modules/greenbidsBidAdapter_spec.js @@ -2,7 +2,7 @@ import { expect } from 'chai'; import { newBidder } from 'src/adapters/bidderFactory.js'; import { spec, ENDPOINT_URL } from 'modules/greenbidsBidAdapter.js'; import { getScreenOrientation } from 'src/utils.js'; -import {getDevicePixelRatio} from '../../../libraries/devicePixelRatio/devicePixelRatio.js'; +import { getDevicePixelRatio } from '../../../libraries/devicePixelRatio/devicePixelRatio.js'; const AD_SCRIPT = '"'; describe('greenbidsBidAdapter', () => { diff --git a/test/spec/modules/gridBidAdapter_spec.js b/test/spec/modules/gridBidAdapter_spec.js index 9fbc66c7975..a829580fc2c 100644 --- a/test/spec/modules/gridBidAdapter_spec.js +++ b/test/spec/modules/gridBidAdapter_spec.js @@ -2,7 +2,7 @@ import { expect } from 'chai'; import { spec, resetUserSync, getSyncUrl, storage } from 'modules/gridBidAdapter.js'; import { newBidder } from 'src/adapters/bidderFactory.js'; import { config } from 'src/config.js'; -import {ENDPOINT_DOMAIN, ENDPOINT_PROTOCOL} from '../../../modules/adpartnerBidAdapter.js'; +import { ENDPOINT_DOMAIN, ENDPOINT_PROTOCOL } from '../../../modules/adpartnerBidAdapter.js'; describe('TheMediaGrid Adapter', function () { const adapter = newBidder(spec); @@ -156,7 +156,7 @@ describe('TheMediaGrid Adapter', function () { genre: 'Adventure' } }; - const [request] = spec.buildRequests([bidRequests[0]], {...bidderRequest, ortb2: {site}}); + const [request] = spec.buildRequests([bidRequests[0]], { ...bidderRequest, ortb2: { site } }); const payload = parseRequest(request.data); expect(payload.site.cat).to.deep.equal([...site.cat, ...site.pagecat]); expect(payload.site.content.genre).to.deep.equal(site.content.genre); @@ -178,7 +178,7 @@ describe('TheMediaGrid Adapter', function () { 'tmax': bidderRequest.timeout, 'source': { 'tid': bidderRequest.ortb2.source.tid, - 'ext': {'wrapper': 'Prebid_js', 'wrapper_version': '$prebid.version$'} + 'ext': { 'wrapper': 'Prebid_js', 'wrapper_version': '$prebid.version$' } }, 'user': { 'id': fpdUserIdVal @@ -186,12 +186,12 @@ describe('TheMediaGrid Adapter', function () { 'imp': [{ 'id': bidRequests[0].bidId, 'tagid': bidRequests[0].params.uid, - 'ext': {'divid': bidRequests[0].adUnitCode}, + 'ext': { 'divid': bidRequests[0].adUnitCode }, 'bidfloor': bidRequests[0].params.bidFloor, 'banner': { 'w': 300, 'h': 250, - 'format': [{'w': 300, 'h': 250}, {'w': 300, 'h': 600}] + 'format': [{ 'w': 300, 'h': 250 }, { 'w': 300, 'h': 600 }] } }] }); @@ -215,7 +215,7 @@ describe('TheMediaGrid Adapter', function () { 'tmax': bidderRequest.timeout, 'source': { 'tid': bidderRequest.auctionId, - 'ext': {'wrapper': 'Prebid_js', 'wrapper_version': '$prebid.version$'} + 'ext': { 'wrapper': 'Prebid_js', 'wrapper_version': '$prebid.version$' } }, 'user': { 'id': fpdUserIdVal @@ -223,21 +223,21 @@ describe('TheMediaGrid Adapter', function () { 'imp': [{ 'id': bidRequests[0].bidId, 'tagid': bidRequests[0].params.uid, - 'ext': {'divid': bidRequests[0].adUnitCode}, + 'ext': { 'divid': bidRequests[0].adUnitCode }, 'bidfloor': bidRequests[0].params.bidFloor, 'banner': { 'w': 300, 'h': 250, - 'format': [{'w': 300, 'h': 250}, {'w': 300, 'h': 600}] + 'format': [{ 'w': 300, 'h': 250 }, { 'w': 300, 'h': 600 }] } }, { 'id': bidRequests[1].bidId, 'tagid': bidRequests[1].params.uid, - 'ext': {'divid': bidRequests[1].adUnitCode}, + 'ext': { 'divid': bidRequests[1].adUnitCode }, 'banner': { 'w': 300, 'h': 250, - 'format': [{'w': 300, 'h': 250}, {'w': 300, 'h': 600}] + 'format': [{ 'w': 300, 'h': 250 }, { 'w': 300, 'h': 600 }] } }] }); @@ -261,7 +261,7 @@ describe('TheMediaGrid Adapter', function () { 'tmax': bidderRequest.timeout, 'source': { 'tid': bidderRequest.auctionId, - 'ext': {'wrapper': 'Prebid_js', 'wrapper_version': '$prebid.version$'} + 'ext': { 'wrapper': 'Prebid_js', 'wrapper_version': '$prebid.version$' } }, 'user': { 'id': fpdUserIdVal @@ -269,26 +269,26 @@ describe('TheMediaGrid Adapter', function () { 'imp': [{ 'id': bidRequests[0].bidId, 'tagid': bidRequests[0].params.uid, - 'ext': {'divid': bidRequests[0].adUnitCode}, + 'ext': { 'divid': bidRequests[0].adUnitCode }, 'bidfloor': bidRequests[0].params.bidFloor, 'banner': { 'w': 300, 'h': 250, - 'format': [{'w': 300, 'h': 250}, {'w': 300, 'h': 600}] + 'format': [{ 'w': 300, 'h': 250 }, { 'w': 300, 'h': 600 }] } }, { 'id': bidRequests[1].bidId, 'tagid': bidRequests[1].params.uid, - 'ext': {'divid': bidRequests[1].adUnitCode}, + 'ext': { 'divid': bidRequests[1].adUnitCode }, 'banner': { 'w': 300, 'h': 250, - 'format': [{'w': 300, 'h': 250}, {'w': 300, 'h': 600}] + 'format': [{ 'w': 300, 'h': 250 }, { 'w': 300, 'h': 600 }] } }, { 'id': bidRequests[2].bidId, 'tagid': bidRequests[2].params.uid, - 'ext': {'divid': bidRequests[2].adUnitCode}, + 'ext': { 'divid': bidRequests[2].adUnitCode }, 'video': { 'w': 400, 'h': 600, @@ -329,7 +329,7 @@ describe('TheMediaGrid Adapter', function () { 'tmax': bidderRequest.timeout, 'source': { 'tid': bidderRequest.auctionId, - 'ext': {'wrapper': 'Prebid_js', 'wrapper_version': '$prebid.version$'} + 'ext': { 'wrapper': 'Prebid_js', 'wrapper_version': '$prebid.version$' } }, 'user': { 'id': fpdUserIdVal @@ -337,18 +337,22 @@ describe('TheMediaGrid Adapter', function () { 'imp': [{ 'id': bidMultiRequests[i].bidId, 'tagid': bidMultiRequests[i].params.uid, - 'ext': {'divid': bidMultiRequests[i].adUnitCode}, + 'ext': { 'divid': bidMultiRequests[i].adUnitCode }, ...(bidMultiRequests[i].params.bidFloor && { 'bidfloor': bidMultiRequests[i].params.bidFloor }), - ...(banner && { banner: { - 'w': banner.sizes[0][0], - 'h': banner.sizes[0][1], - 'format': banner.sizes.map(([w, h]) => ({ w, h })) - }}), - ...(video && { video: { - 'w': video.playerSize[0][0], - 'h': video.playerSize[0][1], - 'mimes': video.mimes - }}) + ...(banner && { + banner: { + 'w': banner.sizes[0][0], + 'h': banner.sizes[0][1], + 'format': banner.sizes.map(([w, h]) => ({ w, h })) + } + }), + ...(video && { + video: { + 'w': video.playerSize[0][0], + 'h': video.playerSize[0][1], + 'mimes': video.mimes + } + }) }] }); }); @@ -372,7 +376,7 @@ describe('TheMediaGrid Adapter', function () { 'tmax': bidderRequest.timeout, 'source': { 'tid': bidderRequest.auctionId, - 'ext': {'wrapper': 'Prebid_js', 'wrapper_version': '$prebid.version$'} + 'ext': { 'wrapper': 'Prebid_js', 'wrapper_version': '$prebid.version$' } }, 'user': { 'id': fpdUserIdVal @@ -380,26 +384,26 @@ describe('TheMediaGrid Adapter', function () { 'imp': [{ 'id': bidRequests[0].bidId, 'tagid': bidRequests[0].params.uid, - 'ext': {'divid': bidRequests[0].adUnitCode}, + 'ext': { 'divid': bidRequests[0].adUnitCode }, 'bidfloor': bidRequests[0].params.bidFloor, 'banner': { 'w': 300, 'h': 250, - 'format': [{'w': 300, 'h': 250}, {'w': 300, 'h': 600}] + 'format': [{ 'w': 300, 'h': 250 }, { 'w': 300, 'h': 600 }] } }, { 'id': bidRequests[1].bidId, 'tagid': bidRequests[1].params.uid, - 'ext': {'divid': bidRequests[1].adUnitCode}, + 'ext': { 'divid': bidRequests[1].adUnitCode }, 'banner': { 'w': 300, 'h': 250, - 'format': [{'w': 300, 'h': 250}, {'w': 300, 'h': 600}] + 'format': [{ 'w': 300, 'h': 250 }, { 'w': 300, 'h': 600 }] } }, { 'id': bidRequests[2].bidId, 'tagid': bidRequests[2].params.uid, - 'ext': {'divid': bidRequests[2].adUnitCode}, + 'ext': { 'divid': bidRequests[2].adUnitCode }, 'video': { 'w': 400, 'h': 600, @@ -408,11 +412,11 @@ describe('TheMediaGrid Adapter', function () { }, { 'id': bidRequests[3].bidId, 'tagid': bidRequests[3].params.uid, - 'ext': {'divid': bidRequests[3].adUnitCode}, + 'ext': { 'divid': bidRequests[3].adUnitCode }, 'banner': { 'w': 728, 'h': 90, - 'format': [{'w': 728, 'h': 90}] + 'format': [{ 'w': 728, 'h': 90 }] }, 'video': { 'w': 400, @@ -426,7 +430,7 @@ describe('TheMediaGrid Adapter', function () { }); it('if gdprConsent is present payload must have gdpr params', function () { - const gdprBidderRequest = Object.assign({gdprConsent: {consentString: 'AAA', gdprApplies: true}}, bidderRequest); + const gdprBidderRequest = Object.assign({ gdprConsent: { consentString: 'AAA', gdprApplies: true } }, bidderRequest); const [request] = spec.buildRequests(bidRequests, gdprBidderRequest); expect(request.data).to.be.an('string'); const payload = parseRequest(request.data); @@ -439,7 +443,7 @@ describe('TheMediaGrid Adapter', function () { }); it('if usPrivacy is present payload must have us_privacy param', function () { - const bidderRequestWithUSP = Object.assign({uspConsent: '1YNN'}, bidderRequest); + const bidderRequestWithUSP = Object.assign({ uspConsent: '1YNN' }, bidderRequest); const [request] = spec.buildRequests(bidRequests, bidderRequestWithUSP); expect(request.data).to.be.an('string'); const payload = parseRequest(request.data); @@ -450,7 +454,7 @@ describe('TheMediaGrid Adapter', function () { it('should add gpp information to the request via bidderRequest.gppConsent', function () { const consentString = 'abc1234'; - const gppBidderRequest = Object.assign({gppConsent: {gppString: consentString, applicableSections: [8]}}, bidderRequest); + const gppBidderRequest = Object.assign({ gppConsent: { gppString: consentString, applicableSections: [8] } }, bidderRequest); const [request] = spec.buildRequests(bidRequests, gppBidderRequest); const payload = JSON.parse(request.data); @@ -465,7 +469,7 @@ describe('TheMediaGrid Adapter', function () { const gppBidderRequest = { ...bidderRequest, ortb2: { - regs: {gpp: consentString, gpp_sid: [8]}, + regs: { gpp: consentString, gpp_sid: [8] }, ...bidderRequest.ortb2 } }; @@ -517,9 +521,9 @@ describe('TheMediaGrid Adapter', function () { screenHeight: 800, language: 'ru' }; - const ortb2 = {user: {ext: {device: ortb2UserExtDevice}}}; + const ortb2 = { user: { ext: { device: ortb2UserExtDevice } } }; - const [request] = spec.buildRequests(bidRequests, {...bidderRequest, ortb2}); + const [request] = spec.buildRequests(bidRequests, { ...bidderRequest, ortb2 }); expect(request.data).to.be.an('string'); const payload = parseRequest(request.data); expect(payload).to.have.property('user'); @@ -559,7 +563,7 @@ describe('TheMediaGrid Adapter', function () { }); it('if content and segment is present in jwTargeting, payload must have right params', function () { - const jsContent = {id: 'test_jw_content_id'}; + const jsContent = { id: 'test_jw_content_id' }; const jsSegments = ['test_seg_1', 'test_seg_2']; const bidRequestsWithJwTargeting = bidRequests.map((bid) => { return Object.assign({ @@ -582,8 +586,8 @@ describe('TheMediaGrid Adapter', function () { it('should contain the keyword values if it present in ortb2.(site/user)', function () { const ortb2 = { - user: {'keywords': 'foo,any'}, - site: {'keywords': 'bar'} + user: { 'keywords': 'foo,any' }, + site: { 'keywords': 'bar' } }; const keywords = { 'site': { @@ -608,7 +612,7 @@ describe('TheMediaGrid Adapter', function () { } }; const bidRequestWithKW = { ...bidRequests[0], params: { ...bidRequests[0].params, keywords } } - const [request] = spec.buildRequests([bidRequestWithKW], {...bidderRequest, ortb2}); + const [request] = spec.buildRequests([bidRequestWithKW], { ...bidderRequest, ortb2 }); expect(request.data).to.be.an('string'); const payload = parseRequest(request.data); expect(payload.ext.keywords).to.deep.equal({ @@ -669,8 +673,8 @@ describe('TheMediaGrid Adapter', function () { someKey: 'another data' } ]; - const ortb2 = {user: {data: userData}}; - const [request] = spec.buildRequests([bidRequests[0]], {...bidderRequest, ortb2}); + const ortb2 = { user: { data: userData } }; + const [request] = spec.buildRequests([bidRequests[0]], { ...bidderRequest, ortb2 }); const payload = parseRequest(request.data); expect(payload.user.data).to.deep.equal(userData); }); @@ -688,8 +692,8 @@ describe('TheMediaGrid Adapter', function () { ] } ]; - const ortb2 = {site: { content: { data: contentData } }}; - const [request] = spec.buildRequests([bidRequests[0]], {...bidderRequest, ortb2}); + const ortb2 = { site: { content: { data: contentData } } }; + const [request] = spec.buildRequests([bidRequests[0]], { ...bidderRequest, ortb2 }); const payload = parseRequest(request.data); expect(payload.site.content.data).to.deep.equal(contentData); }); @@ -708,9 +712,9 @@ describe('TheMediaGrid Adapter', function () { someKey: 'another data' } ]; - const ortb2 = {user: {data: userData}}; + const ortb2 = { user: { data: userData } }; - const jsContent = {id: 'test_jw_content_id'}; + const jsContent = { id: 'test_jw_content_id' }; const jsSegments = ['test_seg_1', 'test_seg_2']; const bidRequestsWithJwTargeting = Object.assign({}, bidRequests[0], { rtd: { @@ -722,15 +726,15 @@ describe('TheMediaGrid Adapter', function () { } } }); - const [request] = spec.buildRequests([bidRequestsWithJwTargeting], {...bidderRequest, ortb2}); + const [request] = spec.buildRequests([bidRequestsWithJwTargeting], { ...bidderRequest, ortb2 }); const payload = parseRequest(request.data); expect(payload.user.data).to.deep.equal(userData); }); it('should have site.content.id filled from config ortb2.site.content.id', function () { const contentId = 'jw_abc'; - const ortb2 = {site: {content: {id: contentId}}}; - const [request] = spec.buildRequests([bidRequests[0]], {...bidderRequest, ortb2}); + const ortb2 = { site: { content: { id: contentId } } }; + const [request] = spec.buildRequests([bidRequests[0]], { ...bidderRequest, ortb2 }); const payload = parseRequest(request.data); expect(payload.site.content.id).to.equal(contentId); }); @@ -771,7 +775,7 @@ describe('TheMediaGrid Adapter', function () { } }]; const bidRequestsWithOrtb2Imp = bidRequests.slice(0, 3).map((bid, ind) => { - return Object.assign({}, bid, ortb2Imp[ind] ? { ortb2Imp: {...bid.ortb2Imp, ...ortb2Imp[ind]} } : {}); + return Object.assign({}, bid, ortb2Imp[ind] ? { ortb2Imp: { ...bid.ortb2Imp, ...ortb2Imp[ind] } } : {}); }); const [request] = spec.buildRequests(bidRequestsWithOrtb2Imp, bidderRequest); expect(request.data).to.be.an('string'); @@ -812,7 +816,7 @@ describe('TheMediaGrid Adapter', function () { } }]; const bidRequestsWithOrtb2Imp = bidRequests.slice(0, 2).map((bid, ind) => { - return Object.assign({}, bid, ortb2Imp[ind] ? { ortb2Imp: {...bid.ortb2Imp, ...ortb2Imp[ind]} } : {}); + return Object.assign({}, bid, ortb2Imp[ind] ? { ortb2Imp: { ...bid.ortb2Imp, ...ortb2Imp[ind] } } : {}); }); const [request] = spec.buildRequests(bidRequestsWithOrtb2Imp, bidderRequest); expect(request.data).to.be.an('string'); @@ -850,7 +854,7 @@ describe('TheMediaGrid Adapter', function () { } }]; const bidRequestsWithOrtb2Imp = bidRequests.slice(0, 3).map((bid, ind) => { - return Object.assign({}, bid, ortb2Imp[ind] ? { ortb2Imp: {...bid.ortb2Imp, ...ortb2Imp[ind]} } : {}); + return Object.assign({}, bid, ortb2Imp[ind] ? { ortb2Imp: { ...bid.ortb2Imp, ...ortb2Imp[ind] } } : {}); }); const [request] = spec.buildRequests(bidRequestsWithOrtb2Imp, bidderRequest); expect(request.data).to.be.an('string'); @@ -906,7 +910,7 @@ describe('TheMediaGrid Adapter', function () { 'auctionId': 654645, }; const bidderRequestWithNumId = { - refererInfo: {page: 'https://example.com'}, + refererInfo: { page: 'https://example.com' }, bidderRequestId: 345345345, timeout: 3000, ortb2: { @@ -927,7 +931,7 @@ describe('TheMediaGrid Adapter', function () { 'tmax': bidderRequestWithNumId.timeout, 'source': { 'tid': '654645', - 'ext': {'wrapper': 'Prebid_js', 'wrapper_version': '$prebid.version$'} + 'ext': { 'wrapper': 'Prebid_js', 'wrapper_version': '$prebid.version$' } }, 'user': { 'id': '2345543345' @@ -935,11 +939,11 @@ describe('TheMediaGrid Adapter', function () { 'imp': [{ 'id': '123123123', 'tagid': '1', - 'ext': {'divid': '1233'}, + 'ext': { 'divid': '1233' }, 'banner': { 'w': 300, 'h': 250, - 'format': [{'w': 300, 'h': 250}, {'w': 300, 'h': 600}] + 'format': [{ 'w': 300, 'h': 250 }, { 'w': 300, 'h': 600 }] } }] }); @@ -948,10 +952,10 @@ describe('TheMediaGrid Adapter', function () { }) it('tmax should be set as integer', function() { - let [request] = spec.buildRequests([bidRequests[0]], {...bidderRequest, timeout: '10'}); + let [request] = spec.buildRequests([bidRequests[0]], { ...bidderRequest, timeout: '10' }); let payload = parseRequest(request.data); expect(payload.tmax).to.equal(10); - [request] = spec.buildRequests([bidRequests[0]], {...bidderRequest, timeout: 'ddqwdwdq'}); + [request] = spec.buildRequests([bidRequests[0]], { ...bidderRequest, timeout: 'ddqwdwdq' }); payload = parseRequest(request.data); expect(payload.tmax).to.equal(null); }) @@ -1002,7 +1006,7 @@ describe('TheMediaGrid Adapter', function () { it('should return the getFloor.floor value if it is greater than bidfloor', function () { const bidfloor = 0.80; const bidRequestsWithFloor = { ...bidRequest }; - bidRequestsWithFloor.params = Object.assign({bidFloor: bidfloor}, bidRequestsWithFloor.params); + bidRequestsWithFloor.params = Object.assign({ bidFloor: bidfloor }, bidRequestsWithFloor.params); const [request] = spec.buildRequests([bidRequestsWithFloor], bidderRequest); expect(request.data).to.be.an('string'); const payload = parseRequest(request.data); @@ -1011,7 +1015,7 @@ describe('TheMediaGrid Adapter', function () { it('should return the bidfloor value if it is greater than getFloor.floor', function () { const bidfloor = 1.80; const bidRequestsWithFloor = { ...bidRequest }; - bidRequestsWithFloor.params = Object.assign({bidFloor: bidfloor}, bidRequestsWithFloor.params); + bidRequestsWithFloor.params = Object.assign({ bidFloor: bidfloor }, bidRequestsWithFloor.params); const [request] = spec.buildRequests([bidRequestsWithFloor], bidderRequest); expect(request.data).to.be.an('string'); const payload = parseRequest(request.data); @@ -1020,7 +1024,7 @@ describe('TheMediaGrid Adapter', function () { it('should return the bidfloor string value if it is greater than getFloor.floor', function () { const bidfloor = '1.80'; const bidRequestsWithFloor = { ...bidRequest }; - bidRequestsWithFloor.params = Object.assign({bidFloor: bidfloor}, bidRequestsWithFloor.params); + bidRequestsWithFloor.params = Object.assign({ bidFloor: bidfloor }, bidRequestsWithFloor.params); const [request] = spec.buildRequests([bidRequestsWithFloor], bidderRequest); expect(request.data).to.be.an('string'); const payload = parseRequest(request.data); @@ -1031,14 +1035,14 @@ describe('TheMediaGrid Adapter', function () { describe('interpretResponse', function () { const responses = [ - {'bid': [{'impid': '659423fff799cb', 'adid': '13_14_4353', 'price': 1.15, 'adm': '
test content 1
', 'auid': 1, 'h': 250, 'w': 300, 'dealid': 11}], 'seat': '1'}, - {'bid': [{'impid': '4dff80cc4ee346', 'adid': '13_15_6454', 'price': 0.5, 'adm': '
test content 2
', 'auid': 2, 'h': 600, 'w': 300}], 'seat': '1'}, - {'bid': [{'impid': '5703af74d0472a', 'adid': '13_16_7654', 'price': 0.15, 'adm': '
test content 3
', 'auid': 1, 'h': 90, 'w': 728}], 'seat': '1'}, - {'bid': [{'impid': '2344da98f78b42', 'adid': '13_17_5433', 'price': 0, 'auid': 3, 'h': 250, 'w': 300}], 'seat': '1'}, - {'bid': [{'price': 0, 'adid': '13_18_34645', 'adm': '
test content 5
', 'h': 250, 'w': 300}], 'seat': '1'}, + { 'bid': [{ 'impid': '659423fff799cb', 'adid': '13_14_4353', 'price': 1.15, 'adm': '
test content 1
', 'auid': 1, 'h': 250, 'w': 300, 'dealid': 11 }], 'seat': '1' }, + { 'bid': [{ 'impid': '4dff80cc4ee346', 'adid': '13_15_6454', 'price': 0.5, 'adm': '
test content 2
', 'auid': 2, 'h': 600, 'w': 300 }], 'seat': '1' }, + { 'bid': [{ 'impid': '5703af74d0472a', 'adid': '13_16_7654', 'price': 0.15, 'adm': '
test content 3
', 'auid': 1, 'h': 90, 'w': 728 }], 'seat': '1' }, + { 'bid': [{ 'impid': '2344da98f78b42', 'adid': '13_17_5433', 'price': 0, 'auid': 3, 'h': 250, 'w': 300 }], 'seat': '1' }, + { 'bid': [{ 'price': 0, 'adid': '13_18_34645', 'adm': '
test content 5
', 'h': 250, 'w': 300 }], 'seat': '1' }, undefined, - {'bid': [], 'seat': '1'}, - {'seat': '1'}, + { 'bid': [], 'seat': '1' }, + { 'seat': '1' }, ]; it('should get correct bid response', function () { @@ -1075,7 +1079,7 @@ describe('TheMediaGrid Adapter', function () { } ]; - const result = spec.interpretResponse({'body': {'seatbid': [responses[0]]}}, request); + const result = spec.interpretResponse({ 'body': { 'seatbid': [responses[0]] } }, request); expect(result).to.deep.equal(expectedResponse); }); @@ -1167,7 +1171,7 @@ describe('TheMediaGrid Adapter', function () { } ]; - const result = spec.interpretResponse({'body': {'seatbid': responses.slice(0, 3)}}, request); + const result = spec.interpretResponse({ 'body': { 'seatbid': responses.slice(0, 3) } }, request); expect(result).to.deep.equal(expectedResponse); }); @@ -1255,11 +1259,11 @@ describe('TheMediaGrid Adapter', function () { } ]; const response = [ - {'bid': [{'impid': '659423fff799cb', 'adid': '35_56_6454', 'price': 1.15, 'adm': '\n<\/Ad>\n<\/VAST>', 'auid': 11, content_type: 'video', w: 300, h: 600}], 'seat': '2'}, - {'bid': [{'impid': '2bc598e42b6a', 'adid': '35_57_2344', 'price': 1.00, 'adm': '\n<\/Ad>\n<\/VAST>', content_type: 'video'}], 'seat': '2'}, - {'bid': [{'impid': '23312a43bc42', 'adid': '35_58_5345', 'price': 2.00, 'nurl': 'https://some_test_vast_url.com', 'auid': 13, content_type: 'video', 'adomain': ['example.com'], w: 300, h: 600}], 'seat': '2'}, - {'bid': [{'impid': '112432ab4f34', 'adid': '35_59_56756', 'price': 1.80, 'adm': '\n<\/Ad>\n<\/VAST>', 'nurl': 'https://wrong_url.com', 'auid': 14, content_type: 'video', 'adomain': ['example.com'], w: 300, h: 600}], 'seat': '2'}, - {'bid': [{'impid': 'a74b342f8cd', 'adid': '35_60_523452', 'price': 1.50, 'nurl': '', 'auid': 15, content_type: 'video'}], 'seat': '2'} + { 'bid': [{ 'impid': '659423fff799cb', 'adid': '35_56_6454', 'price': 1.15, 'adm': '\n<\/Ad>\n<\/VAST>', 'auid': 11, content_type: 'video', w: 300, h: 600 }], 'seat': '2' }, + { 'bid': [{ 'impid': '2bc598e42b6a', 'adid': '35_57_2344', 'price': 1.00, 'adm': '\n<\/Ad>\n<\/VAST>', content_type: 'video' }], 'seat': '2' }, + { 'bid': [{ 'impid': '23312a43bc42', 'adid': '35_58_5345', 'price': 2.00, 'nurl': 'https://some_test_vast_url.com', 'auid': 13, content_type: 'video', 'adomain': ['example.com'], w: 300, h: 600 }], 'seat': '2' }, + { 'bid': [{ 'impid': '112432ab4f34', 'adid': '35_59_56756', 'price': 1.80, 'adm': '\n<\/Ad>\n<\/VAST>', 'nurl': 'https://wrong_url.com', 'auid': 14, content_type: 'video', 'adomain': ['example.com'], w: 300, h: 600 }], 'seat': '2' }, + { 'bid': [{ 'impid': 'a74b342f8cd', 'adid': '35_60_523452', 'price': 1.50, 'nurl': '', 'auid': 15, content_type: 'video' }], 'seat': '2' } ]; const [request] = spec.buildRequests(bidRequests); const expectedResponse = [ @@ -1338,7 +1342,7 @@ describe('TheMediaGrid Adapter', function () { }, ]; - const result = spec.interpretResponse({'body': {'seatbid': response}}, request); + const result = spec.interpretResponse({ 'body': { 'seatbid': response } }, request); expect(result).to.deep.equal(expectedResponse); }); @@ -1379,17 +1383,17 @@ describe('TheMediaGrid Adapter', function () { } ]; const [request] = spec.buildRequests(bidRequests); - const result = spec.interpretResponse({'body': {'seatbid': responses.slice(2)}}, request); + const result = spec.interpretResponse({ 'body': { 'seatbid': responses.slice(2) } }, request); expect(result.length).to.equal(0); }); it('complicated case', function () { const fullResponse = [ - {'bid': [{'impid': '2164be6358b9', 'adid': '32_52_7543', 'price': 1.15, 'adm': '
test content 1
', 'auid': 1, 'h': 250, 'w': 300, dealid: 11, 'ext': {'dsa': {'adrender': 1}}}], 'seat': '1'}, - {'bid': [{'impid': '4e111f1b66e4', 'adid': '32_54_4535', 'price': 0.5, 'adm': '
test content 2
', 'auid': 2, 'h': 600, 'w': 300, dealid: 12}], 'seat': '1'}, - {'bid': [{'impid': '26d6f897b516', 'adid': '32_53_75467', 'price': 0.15, 'adm': '
test content 3
', 'auid': 1, 'h': 90, 'w': 728}], 'seat': '1'}, - {'bid': [{'impid': '326bde7fbf69', 'adid': '32_54_12342', 'price': 0.15, 'adm': '
test content 4
', 'auid': 1, 'h': 600, 'w': 300}], 'seat': '1'}, - {'bid': [{'impid': '2234f233b22a', 'adid': '32_55_987686', 'price': 0.5, 'adm': '
test content 5
', 'auid': 2, 'h': 600, 'w': 350}], 'seat': '1'}, + { 'bid': [{ 'impid': '2164be6358b9', 'adid': '32_52_7543', 'price': 1.15, 'adm': '
test content 1
', 'auid': 1, 'h': 250, 'w': 300, dealid: 11, 'ext': { 'dsa': { 'adrender': 1 } } }], 'seat': '1' }, + { 'bid': [{ 'impid': '4e111f1b66e4', 'adid': '32_54_4535', 'price': 0.5, 'adm': '
test content 2
', 'auid': 2, 'h': 600, 'w': 300, dealid: 12 }], 'seat': '1' }, + { 'bid': [{ 'impid': '26d6f897b516', 'adid': '32_53_75467', 'price': 0.15, 'adm': '
test content 3
', 'auid': 1, 'h': 90, 'w': 728 }], 'seat': '1' }, + { 'bid': [{ 'impid': '326bde7fbf69', 'adid': '32_54_12342', 'price': 0.15, 'adm': '
test content 4
', 'auid': 1, 'h': 600, 'w': 300 }], 'seat': '1' }, + { 'bid': [{ 'impid': '2234f233b22a', 'adid': '32_55_987686', 'price': 0.5, 'adm': '
test content 5
', 'auid': 2, 'h': 600, 'w': 350 }], 'seat': '1' }, ]; const bidRequests = [ { @@ -1519,7 +1523,7 @@ describe('TheMediaGrid Adapter', function () { } ]; - const result = spec.interpretResponse({'body': {'seatbid': fullResponse}}, request); + const result = spec.interpretResponse({ 'body': { 'seatbid': fullResponse } }, request); expect(result).to.deep.equal(expectedResponse); }); @@ -1583,7 +1587,7 @@ describe('TheMediaGrid Adapter', function () { } ]; - const result = spec.interpretResponse({'body': {'seatbid': [serverResponse]}}, request); + const result = spec.interpretResponse({ 'body': { 'seatbid': [serverResponse] } }, request); expect(result).to.deep.equal(expectedResponse); }); }); @@ -1613,14 +1617,14 @@ describe('TheMediaGrid Adapter', function () { pixelEnabled: true }); - expect(syncs).to.deep.equal({type: 'image', url: syncUrl}); + expect(syncs).to.deep.equal({ type: 'image', url: syncUrl }); }); it('should not register the Emily iframe more than once', function () { let syncs = spec.getUserSyncs({ pixelEnabled: true }); - expect(syncs).to.deep.equal({type: 'image', url: syncUrl}); + expect(syncs).to.deep.equal({ type: 'image', url: syncUrl }); // when called again, should still have only been called once syncs = spec.getUserSyncs(); diff --git a/test/spec/modules/growadsBidAdapter_spec.js b/test/spec/modules/growadsBidAdapter_spec.js index 75c5d241a62..0ecc852d5e7 100644 --- a/test/spec/modules/growadsBidAdapter_spec.js +++ b/test/spec/modules/growadsBidAdapter_spec.js @@ -1,7 +1,7 @@ import { expect } from 'chai'; import { spec } from 'modules/growadsBidAdapter.js'; import * as utils from '../../../src/utils.js'; -import {BANNER, NATIVE} from '../../../src/mediaTypes.js'; +import { BANNER, NATIVE } from '../../../src/mediaTypes.js'; describe('GrowAdvertising Adapter', function() { const ZONE_ID = 'unique-zone-id'; @@ -162,15 +162,15 @@ describe('GrowAdvertising Adapter', function() { ]; const requests = spec.buildRequests(validBids); - expect(requests[0].data).to.include({i: 0}); - expect(requests[1].data).to.include({i: 1}); + expect(requests[0].data).to.include({ i: 0 }); + expect(requests[1].data).to.include({ i: 1 }); }); }); describe('bid responses', function () { describe(BANNER, function () { it('should return complete bid response banner', function () { - const bids = spec.interpretResponse(serverResponseBanner, {bidRequest: bidRequests[0]}); + const bids = spec.interpretResponse(serverResponseBanner, { bidRequest: bidRequests[0] }); expect(bids).to.be.lengthOf(1); expect(bids[0].bidderCode).to.equal('growads'); @@ -190,7 +190,7 @@ describe('GrowAdvertising Adapter', function() { } }); - const bids = spec.interpretResponse(response, {bidRequest: bidRequests[0]}); + const bids = spec.interpretResponse(response, { bidRequest: bidRequests[0] }); expect([]).to.be.lengthOf(0); }); @@ -201,14 +201,14 @@ describe('GrowAdvertising Adapter', function() { } }); - const bids = spec.interpretResponse(response, {bidRequest: bidRequests[0]}); + const bids = spec.interpretResponse(response, { bidRequest: bidRequests[0] }); expect([]).to.be.lengthOf(0); }); }); describe(NATIVE, function () { it('should return complete bid response banner', function () { - const bids = spec.interpretResponse(serverResponseNative, {bidRequest: bidRequests[1]}); + const bids = spec.interpretResponse(serverResponseNative, { bidRequest: bidRequests[1] }); expect(bids).to.be.lengthOf(1); expect(bids[0].bidderCode).to.equal('growads'); diff --git a/test/spec/modules/growthCodeAnalyticsAdapter_spec.js b/test/spec/modules/growthCodeAnalyticsAdapter_spec.js index 266bc104fd8..e871ec4c16d 100644 --- a/test/spec/modules/growthCodeAnalyticsAdapter_spec.js +++ b/test/spec/modules/growthCodeAnalyticsAdapter_spec.js @@ -22,7 +22,6 @@ describe('growthCode analytics adapter', () => { 'bidResponse', 'setTargeting', 'requestBids', - 'addAdUnits', 'noBid', 'bidWon', 'bidderDone'] diff --git a/test/spec/modules/growthCodeIdSystem_spec.js b/test/spec/modules/growthCodeIdSystem_spec.js index 537a77c5b42..79c75df368d 100644 --- a/test/spec/modules/growthCodeIdSystem_spec.js +++ b/test/spec/modules/growthCodeIdSystem_spec.js @@ -2,9 +2,9 @@ import { growthCodeIdSubmodule } from 'modules/growthCodeIdSystem.js'; import * as utils from 'src/utils.js'; import { server } from 'test/mocks/xhr.js'; import { uspDataHandler } from 'src/adapterManager.js'; -import {expect} from 'chai'; -import {getStorageManager} from '../../../src/storageManager.js'; -import {MODULE_TYPE_UID} from '../../../src/activities/modules.js'; +import { expect } from 'chai'; +import { getStorageManager } from '../../../src/storageManager.js'; +import { MODULE_TYPE_UID } from '../../../src/activities/modules.js'; const MODULE_NAME = 'growthCodeId'; const EIDS = '[{"source":"domain.com","uids":[{"id":"8212212191539393121","ext":{"stype":"ppuid"}}]}]'; @@ -15,11 +15,13 @@ const GCID_EID_EID = '{"id": [{"source": "growthcode.io", "uids": [{"atype": 3," const storage = getStorageManager({ moduleType: MODULE_TYPE_UID, moduleName: MODULE_NAME }); -const getIdParams = {params: { - pid: 'TEST01', - publisher_id: '_sharedid', - publisher_id_storage: 'html5', -}}; +const getIdParams = { + params: { + pid: 'TEST01', + publisher_id: '_sharedid', + publisher_id_storage: 'html5', + } +}; describe('growthCodeIdSystem', () => { let logErrorStub; @@ -55,25 +57,31 @@ describe('growthCodeIdSystem', () => { it('test return of the GCID and an additional EID', function () { let ids; - ids = growthCodeIdSubmodule.getId({params: { - customerEids: 'customerEids', - }}); + ids = growthCodeIdSubmodule.getId({ + params: { + customerEids: 'customerEids', + } + }); expect(ids).to.deep.equal(JSON.parse(GCID_EID_EID)); }); it('test return of the GCID and an additional EID (bad Local Store name)', function () { let ids; - ids = growthCodeIdSubmodule.getId({params: { - customerEids: 'customerEidsBad', - }}); + ids = growthCodeIdSubmodule.getId({ + params: { + customerEids: 'customerEidsBad', + } + }); expect(ids).to.deep.equal(JSON.parse(GCID_EID)); }); it('test decode function)', function () { let ids; - ids = growthCodeIdSubmodule.decode(GCID, {params: { - customerEids: 'customerEids', - }}); + ids = growthCodeIdSubmodule.decode(GCID, { + params: { + customerEids: 'customerEids', + } + }); expect(ids).to.deep.equal(JSON.parse('{"growthCodeId":"' + GCID + '"}')); }); }) diff --git a/test/spec/modules/growthCodeRtdProvider_spec.js b/test/spec/modules/growthCodeRtdProvider_spec.js index 47213a3ddd9..76374f5f934 100644 --- a/test/spec/modules/growthCodeRtdProvider_spec.js +++ b/test/spec/modules/growthCodeRtdProvider_spec.js @@ -1,5 +1,5 @@ -import {config} from 'src/config.js'; -import {growthCodeRtdProvider} from '../../../modules/growthCodeRtdProvider.js'; +import { config } from 'src/config.js'; +import { growthCodeRtdProvider } from '../../../modules/growthCodeRtdProvider.js'; import sinon from 'sinon'; import * as ajaxLib from 'src/ajax.js'; @@ -85,26 +85,29 @@ describe('growthCodeRtdProvider', function() { 'client_a': { 'user': - {'ext': - {'data': - {'eids': [ - {'source': 'test.com', - 'uids': [ - { - 'id': '4254074976bb6a6d970f5f693bd8a75c', - 'atype': 3, - 'ext': { - 'stype': 'hemmd5'} - }, { - 'id': 'd0ee291572ffcfba0bf7edb2b1c90ca7c32d255e5040b8b50907f5963abb1898', - 'atype': 3, - 'ext': { - 'stype': 'hemsha256' + { + 'ext': + { + 'data': + { + 'eids': [ + { + 'source': 'test.com', + 'uids': [ + { + 'id': '4254074976bb6a6d970f5f693bd8a75c', + 'atype': 3, + 'ext': { 'stype': 'hemmd5' } + }, { + 'id': 'd0ee291572ffcfba0bf7edb2b1c90ca7c32d255e5040b8b50907f5963abb1898', + 'atype': 3, + 'ext': { + 'stype': 'hemsha256' + } } - } - ] - } - ] + ] + } + ] } } } @@ -118,7 +121,7 @@ describe('growthCodeRtdProvider', function() { 'parameters': JSON.stringify(gcData) }] - const bidConfig = {ortb2Fragments: {bidder: {}}}; + const bidConfig = { ortb2Fragments: { bidder: {} } }; growthCodeRtdProvider.addData(bidConfig, payload) expect(bidConfig.ortb2Fragments.bidder).to.deep.equal(gcData) diff --git a/test/spec/modules/gumgumBidAdapter_spec.js b/test/spec/modules/gumgumBidAdapter_spec.js index 1ceaf4f2646..9a509542d8e 100644 --- a/test/spec/modules/gumgumBidAdapter_spec.js +++ b/test/spec/modules/gumgumBidAdapter_spec.js @@ -316,7 +316,8 @@ describe('gumgumAdapter', function () { }); it('should set the global placement id (gpid) if in adserver property', function () { - const req = { ...bidRequests[0], + const req = { + ...bidRequests[0], ortb2Imp: { ext: { gpid: '/17037559/jeusol/jeusol_D_1', @@ -327,29 +328,12 @@ describe('gumgumAdapter', function () { } } } - } } + } + } const bidRequest = spec.buildRequests([req])[0]; expect(bidRequest.data).to.have.property('gpid'); expect(bidRequest.data.gpid).to.equal('/17037559/jeusol/jeusol_D_1'); }); - it('should set ae value to 1 for PAAPI', function () { - const req = { ...bidRequests[0], - ortb2Imp: { - ext: { - ae: 1, - data: { - adserver: { - name: 'test', - adslot: 123456 - } - } - } - } } - const bidRequest = spec.buildRequests([req])[0]; - expect(bidRequest.data).to.have.property('ae'); - expect(bidRequest.data.ae).to.equal(true); - }); - it('should set the global placement id (gpid) if in gpid property', function () { const gpid = 'abc123' const req = { ...bidRequests[0], ortb2Imp: { ext: { data: {}, gpid } } } @@ -868,9 +852,11 @@ describe('gumgumAdapter', function () { model: 'iPhone 12 Pro Max', os: 'iOS', osv: '17.4', - ext: {fiftyonedegrees_deviceId: '17595-133085-133468-18092'}, + ext: { fiftyonedegrees_deviceId: '17595-133085-133468-18092' }, ip: '127.0.0.1', ipv6: '51dc:5e20:fd6a:c955:66be:03b4:dfa3:35b2', + lmt: 1, + ifa: 'test-ifa-id', sua: suaObject }, @@ -889,11 +875,25 @@ describe('gumgumAdapter', function () { expect(bidRequest.data.foddid).to.equal(ortb2.device.ext.fiftyonedegrees_deviceId); expect(bidRequest.data.ip).to.equal(ortb2.device.ip); expect(bidRequest.data.ipv6).to.equal(ortb2.device.ipv6); + expect(bidRequest.data.lmt).to.equal(ortb2.device.lmt); + expect(bidRequest.data).to.not.have.property('ifa'); expect(bidRequest.data).to.have.property('sua'); expect(() => JSON.parse(bidRequest.data.sua)).to.not.throw(); expect(JSON.parse(bidRequest.data.sua)).to.deep.equal(suaObject); }); + it('should include ifa when lmt is not 1', function () { + const ortb2 = { + device: { + lmt: 0, + ifa: 'test-ifa-id' + } + }; + const bidRequest = spec.buildRequests(bidRequests, { ortb2 })[0]; + expect(bidRequest.data.lmt).to.equal(0); + expect(bidRequest.data.ifa).to.equal('test-ifa-id'); + }); + it('should set tId from ortb2Imp.ext.tid if available', function () { const ortb2Imp = { ext: { tid: 'test-tid-1' } }; const request = { ...bidRequests[0], ortb2Imp }; @@ -1083,7 +1083,7 @@ describe('gumgumAdapter', function () { it('request size that matches response size for in-slot', function () { const request = { ...bidRequest }; const body = { ...serverResponse }; - const expectedSize = [[ 320, 50 ], [300, 600], [300, 250]]; + const expectedSize = [[320, 50], [300, 600], [300, 250]]; let result; request.pi = 3; body.ad.width = 300; diff --git a/test/spec/modules/h12mediaBidAdapter_spec.js b/test/spec/modules/h12mediaBidAdapter_spec.js index a4e5a7926c0..29b09315089 100644 --- a/test/spec/modules/h12mediaBidAdapter_spec.js +++ b/test/spec/modules/h12mediaBidAdapter_spec.js @@ -1,7 +1,7 @@ -import {expect} from 'chai'; -import {spec} from 'modules/h12mediaBidAdapter'; -import {newBidder} from 'src/adapters/bidderFactory'; -import {clearCache} from '../../../libraries/boundingClientRect/boundingClientRect.js'; +import { expect } from 'chai'; +import { spec } from 'modules/h12mediaBidAdapter'; +import { newBidder } from 'src/adapters/bidderFactory'; +import { clearCache } from '../../../libraries/boundingClientRect/boundingClientRect.js'; describe('H12 Media Adapter', function () { const DEFAULT_CURRENCY = 'USD'; @@ -88,8 +88,8 @@ describe('H12 Media Adapter', function () { } }, usersync: [ - {url: 'https://cookiesync.3rdpartypartner.com/?3rdparty_partner_user_id={user_id}&partner_id=h12media&gdpr_applies={gdpr}&gdpr_consent_string={gdpr_cs}', type: 'image'}, - {url: 'https://cookiesync.3rdpartypartner.com/?3rdparty_partner_user_id={user_id}&partner_id=h12media&gdpr_applies={gdpr}&gdpr_consent_string={gdpr_cs}', type: 'iframe'} + { url: 'https://cookiesync.3rdpartypartner.com/?3rdparty_partner_user_id={user_id}&partner_id=h12media&gdpr_applies={gdpr}&gdpr_consent_string={gdpr_cs}', type: 'image' }, + { url: 'https://cookiesync.3rdpartypartner.com/?3rdparty_partner_user_id={user_id}&partner_id=h12media&gdpr_applies={gdpr}&gdpr_consent_string={gdpr_cs}', type: 'iframe' } ], }; @@ -196,7 +196,7 @@ describe('H12 Media Adapter', function () { const requests = spec.buildRequests([validBid, validBid2], bidderRequest); const requestsData = requests[0].data.bidrequest; - expect(requestsData).to.include({adunitSize: validBid.mediaTypes.banner.sizes}); + expect(requestsData).to.include({ adunitSize: validBid.mediaTypes.banner.sizes }); }); it('should return empty bid size', function () { @@ -205,7 +205,7 @@ describe('H12 Media Adapter', function () { const requests = spec.buildRequests([validBid, validBid2], bidderRequest); const requestsData2 = requests[1].data.bidrequest; - expect(requestsData2).to.deep.include({adunitSize: []}); + expect(requestsData2).to.deep.include({ adunitSize: [] }); }); it('should return pubsubid from params', function () { @@ -215,19 +215,19 @@ describe('H12 Media Adapter', function () { const requestsData = requests[0].data.bidrequest; const requestsData2 = requests[1].data.bidrequest; - expect(requestsData).to.include({pubsubid: 'pubsubtestid'}); - expect(requestsData2).to.include({pubsubid: ''}); + expect(requestsData).to.include({ pubsubid: 'pubsubtestid' }); + expect(requestsData2).to.include({ pubsubid: '' }); }); it('should return empty for incorrect pubsubid from params', function () { createElementVisible(validBid.adUnitCode); createElementVisible(validBid2.adUnitCode); - const bidWithPub = {...validBid}; + const bidWithPub = { ...validBid }; bidWithPub.params.pubsubid = 'iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii'; // More than 32 chars const requests = spec.buildRequests([bidWithPub], bidderRequest); const requestsData = requests[0].data.bidrequest; - expect(requestsData).to.include({pubsubid: ''}); + expect(requestsData).to.include({ pubsubid: '' }); }); it('should return bid size from params', function () { @@ -237,8 +237,8 @@ describe('H12 Media Adapter', function () { const requestsData = requests[0].data.bidrequest; const requestsData2 = requests[1].data.bidrequest; - expect(requestsData).to.include({size: ''}); - expect(requestsData2).to.include({size: validBid2.params.size}); + expect(requestsData).to.include({ size: '' }); + expect(requestsData2).to.include({ size: validBid2.params.size }); }); it('should return GDPR info', function () { @@ -248,32 +248,32 @@ describe('H12 Media Adapter', function () { const requestsData = requests[0].data; const requestsData2 = requests[1].data; - expect(requestsData).to.include({gdpr: true, gdpr_cs: bidderRequest.gdprConsent.consentString}); - expect(requestsData2).to.include({gdpr: true, gdpr_cs: bidderRequest.gdprConsent.consentString}); + expect(requestsData).to.include({ gdpr: true, gdpr_cs: bidderRequest.gdprConsent.consentString }); + expect(requestsData2).to.include({ gdpr: true, gdpr_cs: bidderRequest.gdprConsent.consentString }); }); it('should not have error on empty GDPR', function () { createElementVisible(validBid.adUnitCode); createElementVisible(validBid2.adUnitCode); - const bidderRequestWithoutGDRP = {...bidderRequest, gdprConsent: null}; + const bidderRequestWithoutGDRP = { ...bidderRequest, gdprConsent: null }; const requests = spec.buildRequests([validBid, validBid2], bidderRequestWithoutGDRP); const requestsData = requests[0].data; const requestsData2 = requests[1].data; - expect(requestsData).to.include({gdpr: false}); - expect(requestsData2).to.include({gdpr: false}); + expect(requestsData).to.include({ gdpr: false }); + expect(requestsData2).to.include({ gdpr: false }); }); it('should not have error on empty USP', function () { createElementVisible(validBid.adUnitCode); createElementVisible(validBid2.adUnitCode); - const bidderRequestWithoutUSP = {...bidderRequest, uspConsent: null}; + const bidderRequestWithoutUSP = { ...bidderRequest, uspConsent: null }; const requests = spec.buildRequests([validBid, validBid2], bidderRequestWithoutUSP); const requestsData = requests[0].data; const requestsData2 = requests[1].data; - expect(requestsData).to.include({usp: false}); - expect(requestsData2).to.include({usp: false}); + expect(requestsData).to.include({ usp: false }); + expect(requestsData2).to.include({ usp: false }); }); it('should create single POST', function () { @@ -292,7 +292,7 @@ describe('H12 Media Adapter', function () { const requests = spec.buildRequests([validBid], bidderRequest); const requestsData = requests[0].data.bidrequest; - expect(requestsData).to.deep.include({coords: {x: 10, y: 10}}); + expect(requestsData).to.deep.include({ coords: { x: 10, y: 10 } }); }); it('should define iframe', function () { @@ -302,8 +302,8 @@ describe('H12 Media Adapter', function () { const requestsData = requests[0].data; const requestsData2 = requests[1].data; - expect(requestsData).to.include({isiframe: true}); - expect(requestsData2).to.include({isiframe: true}); + expect(requestsData).to.include({ isiframe: true }); + expect(requestsData2).to.include({ isiframe: true }); }); it('should define visible element', function () { @@ -311,7 +311,7 @@ describe('H12 Media Adapter', function () { const requests = spec.buildRequests([validBid], bidderRequest); const requestsData = requests[0].data.bidrequest; - expect(requestsData).to.include({ishidden: false}); + expect(requestsData).to.include({ ishidden: false }); }); it('should define invisible element', function () { @@ -319,7 +319,7 @@ describe('H12 Media Adapter', function () { const requests = spec.buildRequests([validBid], bidderRequest); const requestsData = requests[0].data.bidrequest; - expect(requestsData).to.include({ishidden: true}); + expect(requestsData).to.include({ ishidden: true }); }); it('should define hidden element', function () { @@ -327,7 +327,7 @@ describe('H12 Media Adapter', function () { const requests = spec.buildRequests([validBid], bidderRequest); const requestsData = requests[0].data.bidrequest; - expect(requestsData).to.include({ishidden: true}); + expect(requestsData).to.include({ ishidden: true }); }); }); @@ -348,7 +348,7 @@ describe('H12 Media Adapter', function () { createElementVisible(validBid.adUnitCode); createElementVisible(validBid2.adUnitCode); const request = spec.buildRequests([validBid, validBid2], bidderRequest); - const bidResponse = spec.interpretResponse({body: serverResponse}, request[0]); + const bidResponse = spec.interpretResponse({ body: serverResponse }, request[0]); expect(bidResponse[0]).to.deep.include({ requestId: validBid.bidId, @@ -370,7 +370,7 @@ describe('H12 Media Adapter', function () { createElementVisible(validBid.adUnitCode); createElementVisible(validBid2.adUnitCode); const request = spec.buildRequests([validBid, validBid2], bidderRequest); - const bidResponse = spec.interpretResponse({body: serverResponse2}, request[0]); + const bidResponse = spec.interpretResponse({ body: serverResponse2 }, request[0]); expect(bidResponse[0]).to.deep.include({ requestId: validBid2.bidId, @@ -404,7 +404,7 @@ describe('H12 Media Adapter', function () { type: 'image', url: `https://cookiesync.3rdpartypartner.com/?3rdparty_partner_user_id={user_id}&partner_id=h12media&gdpr_applies=${bidderRequest.gdprConsent.gdprApplies}&gdpr_consent_string=${bidderRequest.gdprConsent.consentString}`, }; - const syncs = spec.getUserSyncs(syncOptions, [{body: serverResponse}], bidderRequest.gdprConsent); + const syncs = spec.getUserSyncs(syncOptions, [{ body: serverResponse }], bidderRequest.gdprConsent); expect(syncs).to.deep.include(result); }); @@ -414,7 +414,7 @@ describe('H12 Media Adapter', function () { type: 'iframe', url: `https://cookiesync.3rdpartypartner.com/?3rdparty_partner_user_id={user_id}&partner_id=h12media&gdpr_applies=${bidderRequest.gdprConsent.gdprApplies}&gdpr_consent_string=${bidderRequest.gdprConsent.consentString}`, }; - const syncs = spec.getUserSyncs(syncOptions, [{body: serverResponse}], bidderRequest.gdprConsent); + const syncs = spec.getUserSyncs(syncOptions, [{ body: serverResponse }], bidderRequest.gdprConsent); expect(syncs).to.deep.include(result); }); @@ -425,15 +425,15 @@ describe('H12 Media Adapter', function () { url: `https://cookiesync.3rdpartypartner.com/?3rdparty_partner_user_id={user_id}&partner_id=h12media&gdpr_applies=false&gdpr_consent_string=`, }; - expect(spec.getUserSyncs(syncOptions, [{body: serverResponse}], null)).to.deep.include(result); + expect(spec.getUserSyncs(syncOptions, [{ body: serverResponse }], null)).to.deep.include(result); }); it('should success without usersync url', function () { - expect(spec.getUserSyncs(syncOptions, [{body: serverResponse2}], bidderRequest.gdprConsent)).to.deep.equal([]); + expect(spec.getUserSyncs(syncOptions, [{ body: serverResponse2 }], bidderRequest.gdprConsent)).to.deep.equal([]); }); it('should return empty usersync on empty response', function () { - expect(spec.getUserSyncs(syncOptions, [{body: {}}])).to.deep.equal([]); + expect(spec.getUserSyncs(syncOptions, [{ body: {} }])).to.deep.equal([]); }); }); }); diff --git a/test/spec/modules/hadronAnalyticsAdapter_spec.js b/test/spec/modules/hadronAnalyticsAdapter_spec.js index 68e5bc3aecb..ec1092fa441 100644 --- a/test/spec/modules/hadronAnalyticsAdapter_spec.js +++ b/test/spec/modules/hadronAnalyticsAdapter_spec.js @@ -12,7 +12,7 @@ describe('Hadron analytics adapter', () => { options: { partnerId: 12349, eventsToTrack: ['auctionInit', 'auctionEnd', 'bidWon', - 'bidderDone', 'requestBids', 'addAdUnits', 'setTargeting', 'adRenderFailed', + 'bidderDone', 'requestBids', 'setTargeting', 'adRenderFailed', 'bidResponse', 'bidTimeout', 'bidRequested', 'bidAdjustment', 'nonExistingEvent' ], } diff --git a/test/spec/modules/hadronIdSystem_spec.js b/test/spec/modules/hadronIdSystem_spec.js index 70aaf06bcc8..be4ca7be8b5 100644 --- a/test/spec/modules/hadronIdSystem_spec.js +++ b/test/spec/modules/hadronIdSystem_spec.js @@ -1,8 +1,8 @@ -import {hadronIdSubmodule, storage, LS_TAM_KEY} from 'modules/hadronIdSystem.js'; -import {server} from 'test/mocks/xhr.js'; -import {attachIdSystem} from '../../../modules/userId/index.js'; -import {createEidsArray} from '../../../modules/userId/eids.js'; -import {expect} from 'chai/index.mjs'; +import { hadronIdSubmodule, storage, LS_TAM_KEY } from 'modules/hadronIdSystem.js'; +import { server } from 'test/mocks/xhr.js'; +import { attachIdSystem } from '../../../modules/userId/index.js'; +import { createEidsArray } from '../../../modules/userId/eids.js'; +import { expect } from 'chai/index.mjs'; describe('HadronIdSystem', function () { const HADRON_TEST = 'tstCachedHadronId1'; @@ -23,7 +23,7 @@ describe('HadronIdSystem', function () { }; getDataFromLocalStorageStub.withArgs(LS_TAM_KEY).returns(HADRON_TEST); const result = hadronIdSubmodule.getId(config); - expect(result).to.deep.equal({id: HADRON_TEST}); + expect(result).to.deep.equal({ id: HADRON_TEST }); }); it('allows configurable id url', function () { diff --git a/test/spec/modules/hadronRtdProvider_spec.js b/test/spec/modules/hadronRtdProvider_spec.js index 8f535c37ce4..f15d30b0144 100644 --- a/test/spec/modules/hadronRtdProvider_spec.js +++ b/test/spec/modules/hadronRtdProvider_spec.js @@ -1,4 +1,4 @@ -import {config} from 'src/config.js'; +import { config } from 'src/config.js'; import { HADRONID_LOCAL_NAME, RTD_LOCAL_NAME, @@ -7,9 +7,9 @@ import { hadronSubmodule, storage } from 'modules/hadronRtdProvider.js'; -import {server} from 'test/mocks/xhr.js'; +import { server } from 'test/mocks/xhr.js'; -const responseHeader = {'Content-Type': 'application/json'}; +const responseHeader = { 'Content-Type': 'application/json' }; describe('hadronRtdProvider', function () { let getDataFromLocalStorageStub; @@ -35,7 +35,7 @@ describe('hadronRtdProvider', function () { const setConfigUserObj1 = { name: 'www.dataprovider1.com', - ext: {taxonomyname: 'iab_audience_taxonomy'}, + ext: { taxonomyname: 'iab_audience_taxonomy' }, segment: [{ id: '1776' }] @@ -43,7 +43,7 @@ describe('hadronRtdProvider', function () { const setConfigUserObj2 = { name: 'www.dataprovider2.com', - ext: {taxonomyname: 'iab_audience_taxonomy'}, + ext: { taxonomyname: 'iab_audience_taxonomy' }, segment: [{ id: '1914' }] @@ -135,7 +135,7 @@ describe('hadronRtdProvider', function () { const userObj1 = { name: 'www.dataprovider1.com', - ext: {taxonomyname: 'iab_audience_taxonomy'}, + ext: { taxonomyname: 'iab_audience_taxonomy' }, segment: [{ id: '1776' }] @@ -143,7 +143,7 @@ describe('hadronRtdProvider', function () { const userObj2 = { name: 'www.dataprovider2.com', - ext: {taxonomyname: 'iab_audience_taxonomy'}, + ext: { taxonomyname: 'iab_audience_taxonomy' }, segment: [{ id: '1914' }] @@ -207,7 +207,7 @@ describe('hadronRtdProvider', function () { const configUserObj1 = { name: 'www.dataprovider1.com', - ext: {segtax: 3}, + ext: { segtax: 3 }, segment: [{ id: '1776' }] @@ -215,7 +215,7 @@ describe('hadronRtdProvider', function () { const configUserObj2 = { name: 'www.dataprovider2.com', - ext: {segtax: 3}, + ext: { segtax: 3 }, segment: [{ id: '1914' }] @@ -223,7 +223,7 @@ describe('hadronRtdProvider', function () { const configUserObj3 = { name: 'www.dataprovider1.com', - ext: {segtax: 3}, + ext: { segtax: 3 }, segment: [{ id: '2003' }] @@ -384,7 +384,7 @@ describe('hadronRtdProvider', function () { const userObj1 = { name: 'www.dataprovider1.com', - ext: {segtax: 3}, + ext: { segtax: 3 }, segment: [{ id: '1776' }] @@ -392,7 +392,7 @@ describe('hadronRtdProvider', function () { const userObj2 = { name: 'www.dataprovider2.com', - ext: {segtax: 3}, + ext: { segtax: 3 }, segment: [{ id: '1914' }] @@ -400,7 +400,7 @@ describe('hadronRtdProvider', function () { const userObj3 = { name: 'www.dataprovider1.com', - ext: {segtax: 3}, + ext: { segtax: 3 }, segment: [{ id: '2003' }] @@ -513,9 +513,9 @@ describe('hadronRtdProvider', function () { params: { handleRtd: function (bidConfig, rtd, rtdConfig, pbConfig) { if (String(rtd.ortb2.user.data[0].segment[0].id) === '1776') { - pbConfig.setConfig({ortb2: rtd.ortb2}); + pbConfig.setConfig({ ortb2: rtd.ortb2 }); } else { - pbConfig.setConfig({ortb2: {}}); + pbConfig.setConfig({ ortb2: {} }); } } } @@ -525,7 +525,7 @@ describe('hadronRtdProvider', function () { const rtdUserObj1 = { name: 'www.dataprovider.com', - ext: {taxonomyname: 'iab_audience_taxonomy'}, + ext: { taxonomyname: 'iab_audience_taxonomy' }, segment: [{ id: '1776' }] @@ -621,8 +621,8 @@ describe('hadronRtdProvider', function () { }; const rtd = { - adBuzz: [{id: 'adBuzzSeg2'}, {id: 'adBuzzSeg3'}], - trueBid: [{id: 'truebidSeg1'}, {id: 'truebidSeg2'}, {id: 'truebidSeg3'}] + adBuzz: [{ id: 'adBuzzSeg2' }, { id: 'adBuzzSeg3' }], + trueBid: [{ id: 'truebidSeg1' }, { id: 'truebidSeg2' }, { id: 'truebidSeg3' }] }; addRealTimeData(bidConfig, rtd, rtdConfig); @@ -644,7 +644,7 @@ describe('hadronRtdProvider', function () { } }; - const bidConfig = {ortb2Fragments: {global: {}}}; + const bidConfig = { ortb2Fragments: { global: {} } }; const rtdUserObj1 = { name: 'www.dataprovider3.com', diff --git a/test/spec/modules/harionBidAdapter_spec.js b/test/spec/modules/harionBidAdapter_spec.js new file mode 100644 index 00000000000..d5a1dd509a6 --- /dev/null +++ b/test/spec/modules/harionBidAdapter_spec.js @@ -0,0 +1,475 @@ +import { expect } from 'chai'; +import { spec } from '../../../modules/harionBidAdapter.js'; +import { BANNER, VIDEO, NATIVE } from '../../../src/mediaTypes.js'; +import { getUniqueIdentifierStr } from '../../../src/utils.js'; + +const bidder = 'Harion'; + +describe('HarionBidAdapter', function () { + const userIdAsEids = [{ + source: 'test.org', + uids: [{ + id: '01**********', + atype: 1, + ext: { + third: '01***********' + } + }] + }]; + const bids = [ + { + bidId: getUniqueIdentifierStr(), + bidder: bidder, + mediaTypes: { + [BANNER]: { + sizes: [[300, 250]] + } + }, + params: { + placementId: 'testBanner', + }, + userIdAsEids + }, + { + bidId: getUniqueIdentifierStr(), + bidder: bidder, + mediaTypes: { + [VIDEO]: { + playerSize: [[300, 300]], + minduration: 5, + maxduration: 60 + } + }, + params: { + placementId: 'testVideo', + }, + userIdAsEids + }, + { + bidId: getUniqueIdentifierStr(), + bidder: bidder, + mediaTypes: { + [NATIVE]: { + native: { + title: { + required: true + }, + body: { + required: true + }, + icon: { + required: true, + size: [64, 64] + } + } + } + }, + params: { + placementId: 'testNative' + }, + userIdAsEids + } + ]; + + const invalidBid = { + bidId: getUniqueIdentifierStr(), + bidder: bidder, + mediaTypes: { + [BANNER]: { + sizes: [[300, 250]] + } + }, + params: { + + } + } + + const bidderRequest = { + uspConsent: '1---', + gdprConsent: { + consentString: 'COvFyGBOvFyGBAbAAAENAPCAAOAAAAAAAAAAAEEUACCKAAA.IFoEUQQgAIQwgIwQABAEAAAAOIAACAIAAAAQAIAgEAACEAAAAAgAQBAAAAAAAGBAAgAAAAAAAFAAECAAAgAAQARAEQAAAAAJAAIAAgAAAYQEAAAQmAgBC3ZAYzUw', + vendorData: {} + }, + refererInfo: { + referer: 'https://test.com', + page: 'https://test.com' + }, + ortb2: { + device: { + w: 1512, + h: 982, + language: 'en-UK', + } + }, + timeout: 500 + }; + + describe('isBidRequestValid', function () { + it('Should return true if there are bidId, params and key parameters present', function () { + expect(spec.isBidRequestValid(bids[0])).to.be.true; + }); + it('Should return false if at least one of parameters is not present', function () { + expect(spec.isBidRequestValid(invalidBid)).to.be.false; + }); + }); + + describe('buildRequests', function () { + let serverRequest = spec.buildRequests(bids, bidderRequest); + + 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 general data valid', function () { + const data = serverRequest.data; + expect(data).to.be.an('object'); + expect(data).to.have.all.keys( + 'deviceWidth', + 'deviceHeight', + 'device', + 'language', + 'secure', + 'host', + 'page', + 'placements', + 'coppa', + 'ccpa', + 'gdpr', + 'tmax', + 'bcat', + 'badv', + 'bapp', + 'battr' + ); + expect(data.deviceWidth).to.be.a('number'); + expect(data.deviceHeight).to.be.a('number'); + expect(data.language).to.be.a('string'); + expect(data.secure).to.be.within(0, 1); + expect(data.host).to.be.a('string'); + expect(data.page).to.be.a('string'); + expect(data.coppa).to.be.a('number'); + expect(data.gdpr).to.be.a('object'); + expect(data.ccpa).to.be.a('string'); + expect(data.tmax).to.be.a('number'); + expect(data.placements).to.have.lengthOf(3); + }); + + it('Returns valid placements', function () { + const { placements } = serverRequest.data; + for (let i = 0, len = placements.length; i < len; i++) { + const placement = placements[i]; + expect(placement.placementId).to.be.oneOf(['testBanner', 'testVideo', 'testNative']); + expect(placement.adFormat).to.be.oneOf([BANNER, VIDEO, NATIVE]); + expect(placement.bidId).to.be.a('string'); + expect(placement.schain).to.be.an('object'); + expect(placement.bidfloor).to.exist.and.to.equal(0); + expect(placement.type).to.exist.and.to.equal('publisher'); + expect(placement.eids).to.exist.and.to.be.deep.equal(userIdAsEids); + + if (placement.adFormat === BANNER) { + expect(placement.sizes).to.be.an('array'); + } + switch (placement.adFormat) { + case BANNER: + expect(placement.sizes).to.be.an('array'); + break; + case VIDEO: + expect(placement.playerSize).to.be.an('array'); + expect(placement.minduration).to.be.an('number'); + expect(placement.maxduration).to.be.an('number'); + break; + case NATIVE: + expect(placement.native).to.be.an('object'); + break; + } + } + }); + + it('Returns valid endpoints', function () { + const bids = [ + { + bidId: getUniqueIdentifierStr(), + bidder: bidder, + mediaTypes: { + [BANNER]: { + sizes: [[300, 250]] + } + }, + params: { + endpointId: 'testBanner', + }, + userIdAsEids + } + ]; + + const serverRequest = spec.buildRequests(bids, bidderRequest); + + const { placements } = serverRequest.data; + for (let i = 0, len = placements.length; i < len; i++) { + const placement = placements[i]; + expect(placement.endpointId).to.be.oneOf(['testBanner', 'testVideo', 'testNative']); + expect(placement.adFormat).to.be.oneOf([BANNER, VIDEO, NATIVE]); + expect(placement.bidId).to.be.a('string'); + expect(placement.schain).to.be.an('object'); + expect(placement.bidfloor).to.exist.and.to.equal(0); + expect(placement.type).to.exist.and.to.equal('network'); + expect(placement.eids).to.exist.and.to.be.deep.equal(userIdAsEids); + + if (placement.adFormat === BANNER) { + expect(placement.sizes).to.be.an('array'); + } + switch (placement.adFormat) { + case BANNER: + expect(placement.sizes).to.be.an('array'); + break; + case VIDEO: + expect(placement.playerSize).to.be.an('array'); + expect(placement.minduration).to.be.an('number'); + expect(placement.maxduration).to.be.an('number'); + break; + case NATIVE: + expect(placement.native).to.be.an('object'); + break; + } + } + }); + + it('Returns data with gdprConsent and without uspConsent', function () { + delete bidderRequest.uspConsent; + serverRequest = spec.buildRequests(bids, bidderRequest); + const data = serverRequest.data; + expect(data.gdpr).to.exist; + expect(data.gdpr).to.be.a('object'); + expect(data.gdpr).to.have.property('consentString'); + expect(data.gdpr).to.not.have.property('vendorData'); + expect(data.gdpr.consentString).to.equal(bidderRequest.gdprConsent.consentString); + expect(data.ccpa).to.not.exist; + delete bidderRequest.gdprConsent; + }); + + it('Returns data with uspConsent and without gdprConsent', function () { + bidderRequest.uspConsent = '1---'; + delete bidderRequest.gdprConsent; + serverRequest = spec.buildRequests(bids, bidderRequest); + const data = serverRequest.data; + expect(data.ccpa).to.exist; + expect(data.ccpa).to.be.a('string'); + expect(data.ccpa).to.equal(bidderRequest.uspConsent); + expect(data.gdpr).to.not.exist; + }); + }); + + describe('gpp consent', function () { + it('bidderRequest.gppConsent', () => { + bidderRequest.gppConsent = { + gppString: 'abc123', + applicableSections: [8] + }; + + const serverRequest = spec.buildRequests(bids, bidderRequest); + const data = serverRequest.data; + expect(data).to.be.an('object'); + expect(data).to.have.property('gpp'); + expect(data).to.have.property('gpp_sid'); + + delete bidderRequest.gppConsent; + }) + + it('bidderRequest.ortb2.regs.gpp', () => { + bidderRequest.ortb2 = bidderRequest.ortb2 || {}; + bidderRequest.ortb2.regs = bidderRequest.ortb2.regs || {}; + bidderRequest.ortb2.regs.gpp = 'abc123'; + bidderRequest.ortb2.regs.gpp_sid = [8]; + + const serverRequest = spec.buildRequests(bids, bidderRequest); + const data = serverRequest.data; + expect(data).to.be.an('object'); + expect(data).to.have.property('gpp'); + expect(data).to.have.property('gpp_sid'); + }) + }); + + describe('interpretResponse', function () { + it('Should interpret banner response', function () { + const banner = { + body: [{ + mediaType: 'banner', + width: 300, + height: 250, + cpm: 0.4, + ad: 'Test', + requestId: '23fhj33i987f', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1', + meta: { + advertiserDomains: ['google.com'], + advertiserId: 1234 + } + }] + }; + const bannerResponses = spec.interpretResponse(banner); + expect(bannerResponses).to.be.an('array').that.is.not.empty; + const dataItem = bannerResponses[0]; + expect(dataItem).to.have.all.keys('requestId', 'cpm', 'width', 'height', 'ad', 'ttl', 'creativeId', + 'netRevenue', 'currency', 'dealId', 'mediaType', 'meta'); + expect(dataItem.requestId).to.equal(banner.body[0].requestId); + expect(dataItem.cpm).to.equal(banner.body[0].cpm); + expect(dataItem.width).to.equal(banner.body[0].width); + expect(dataItem.height).to.equal(banner.body[0].height); + expect(dataItem.ad).to.equal(banner.body[0].ad); + expect(dataItem.ttl).to.equal(banner.body[0].ttl); + expect(dataItem.creativeId).to.equal(banner.body[0].creativeId); + expect(dataItem.netRevenue).to.be.true; + expect(dataItem.currency).to.equal(banner.body[0].currency); + expect(dataItem.meta).to.be.an('object').that.has.any.key('advertiserDomains'); + }); + it('Should interpret video response', function () { + const video = { + body: [{ + vastUrl: 'test.com', + mediaType: 'video', + cpm: 0.5, + requestId: '23fhj33i987f', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1', + meta: { + advertiserDomains: ['google.com'], + advertiserId: 1234 + } + }] + }; + const videoResponses = spec.interpretResponse(video); + expect(videoResponses).to.be.an('array').that.is.not.empty; + + const dataItem = videoResponses[0]; + expect(dataItem).to.have.all.keys('requestId', 'cpm', 'vastUrl', 'ttl', 'creativeId', + 'netRevenue', 'currency', 'dealId', 'mediaType', 'meta'); + expect(dataItem.requestId).to.equal('23fhj33i987f'); + expect(dataItem.cpm).to.equal(0.5); + expect(dataItem.vastUrl).to.equal('test.com'); + expect(dataItem.ttl).to.equal(120); + expect(dataItem.creativeId).to.equal('2'); + expect(dataItem.netRevenue).to.be.true; + expect(dataItem.currency).to.equal('USD'); + expect(dataItem.meta).to.be.an('object').that.has.any.key('advertiserDomains'); + }); + it('Should interpret native response', function () { + const native = { + body: [{ + mediaType: 'native', + native: { + clickUrl: 'test.com', + title: 'Test', + image: 'test.com', + impressionTrackers: ['test.com'], + }, + ttl: 120, + cpm: 0.4, + requestId: '23fhj33i987f', + creativeId: '2', + netRevenue: true, + currency: 'USD', + meta: { + advertiserDomains: ['google.com'], + advertiserId: 1234 + } + }] + }; + const nativeResponses = spec.interpretResponse(native); + expect(nativeResponses).to.be.an('array').that.is.not.empty; + + const dataItem = nativeResponses[0]; + expect(dataItem).to.have.keys('requestId', 'cpm', 'ttl', 'creativeId', 'netRevenue', 'currency', 'mediaType', 'native', 'meta'); + expect(dataItem.native).to.have.keys('clickUrl', 'impressionTrackers', 'title', 'image') + expect(dataItem.requestId).to.equal('23fhj33i987f'); + expect(dataItem.cpm).to.equal(0.4); + expect(dataItem.native.clickUrl).to.equal('test.com'); + expect(dataItem.native.title).to.equal('Test'); + expect(dataItem.native.image).to.equal('test.com'); + expect(dataItem.native.impressionTrackers).to.be.an('array').that.is.not.empty; + expect(dataItem.native.impressionTrackers[0]).to.equal('test.com'); + expect(dataItem.ttl).to.equal(120); + expect(dataItem.creativeId).to.equal('2'); + expect(dataItem.netRevenue).to.be.true; + expect(dataItem.currency).to.equal('USD'); + expect(dataItem.meta).to.be.an('object').that.has.any.key('advertiserDomains'); + }); + it('Should return an empty array if invalid banner response is passed', function () { + const invBanner = { + body: [{ + width: 300, + cpm: 0.4, + ad: 'Test', + requestId: '23fhj33i987f', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1' + }] + }; + + const 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', function () { + const invVideo = { + body: [{ + mediaType: 'video', + cpm: 0.5, + requestId: '23fhj33i987f', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1' + }] + }; + const 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', function () { + const invNative = { + body: [{ + mediaType: 'native', + clickUrl: 'test.com', + title: 'Test', + impressionTrackers: ['test.com'], + ttl: 120, + requestId: '23fhj33i987f', + creativeId: '2', + netRevenue: true, + currency: 'USD', + }] + }; + const serverResponses = spec.interpretResponse(invNative); + expect(serverResponses).to.be.an('array').that.is.empty; + }); + it('Should return an empty array if invalid response is passed', function () { + const invalid = { + body: [{ + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1' + }] + }; + const serverResponses = spec.interpretResponse(invalid); + expect(serverResponses).to.be.an('array').that.is.empty; + }); + }); +}); diff --git a/test/spec/modules/holidBidAdapter_spec.js b/test/spec/modules/holidBidAdapter_spec.js index c3f7a873f5d..08cef8d31da 100644 --- a/test/spec/modules/holidBidAdapter_spec.js +++ b/test/spec/modules/holidBidAdapter_spec.js @@ -34,15 +34,15 @@ describe('holidBidAdapterTests', () => { } }; - describe('isBidRequestValid', () => { - const bid = JSON.parse(JSON.stringify(bidRequestData)); + const clone = (obj) => JSON.parse(JSON.stringify(obj)); + describe('isBidRequestValid', () => { it('should return true', () => { - expect(spec.isBidRequestValid(bid)).to.equal(true); + expect(spec.isBidRequestValid(clone(bidRequestData))).to.equal(true); }); it('should return false when required params are not passed', () => { - const bid = JSON.parse(JSON.stringify(bidRequestData)); + const bid = clone(bidRequestData); delete bid.params.adUnitID; expect(spec.isBidRequestValid(bid)).to.equal(false); @@ -50,7 +50,7 @@ describe('holidBidAdapterTests', () => { }); describe('buildRequests', () => { - const bid = JSON.parse(JSON.stringify(bidRequestData)); + const bid = clone(bidRequestData); const request = spec.buildRequests([bid], bidderRequest); const payload = JSON.parse(request[0].data); @@ -84,6 +84,85 @@ describe('holidBidAdapterTests', () => { }); }); + // NEW: cover tmax behavior introduced in the PR + describe('buildRequests - tmax behavior', () => { + it('should set tmax from bidderRequest.timeout when no params.tmax is provided', () => { + const bid = clone(bidRequestData); + const br = { bidderRequestId: 'test-id', timeout: 1200 }; + + const request = spec.buildRequests([bid], br); + const payload = JSON.parse(request[0].data); + + expect(payload.tmax).to.equal(1200); + }); + + it('should cap params.tmax to bidderRequest.timeout when provided', () => { + const bid = clone(bidRequestData); + bid.params.tmax = 2500; + + const br = { bidderRequestId: 'test-id', timeout: 900 }; + + const request = spec.buildRequests([bid], br); + const payload = JSON.parse(request[0].data); + + expect(payload.tmax).to.equal(900); + }); + + it('should use params.tmax when bidderRequest.timeout is missing', () => { + const bid = clone(bidRequestData); + bid.params.tmax = 750; + + const br = { bidderRequestId: 'test-id' }; + + const request = spec.buildRequests([bid], br); + const payload = JSON.parse(request[0].data); + + expect(payload.tmax).to.equal(750); + }); + }); + + // NEW: ensure ORTB fields are merged rather than clobbered + describe('buildRequests - ORTB merge safety', () => { + it('should merge storedrequest into ext.prebid without clobbering existing ext fields', () => { + const bid = clone(bidRequestData); + bid.ortb2.ext = { someExtKey: 'keep-me', prebid: { somePrebidKey: 'keep-me-too' } }; + + const br = { bidderRequestId: 'test-id', timeout: 1000 }; + const request = spec.buildRequests([bid], br); + const payload = JSON.parse(request[0].data); + + // existing ext preserved + expect(payload.ext).to.exist; + expect(payload.ext.someExtKey).to.equal('keep-me'); + expect(payload.ext.prebid).to.exist; + expect(payload.ext.prebid.somePrebidKey).to.equal('keep-me-too'); + + // storedrequest merged in + expect(payload.ext.prebid.storedrequest).to.exist; + expect(payload.ext.prebid.storedrequest.id).to.equal('12345'); + }); + + it('should merge schain into source.ext.schain without clobbering source fields', () => { + const bid = clone(bidRequestData); + bid.ortb2.source = { + tid: 'tid-123', + ext: { + other: 'keep-me', + schain: { ver: '1.0', complete: 1, nodes: [{ asi: 'example.com', sid: '123', hp: 1 }] } + } + }; + + const br = { bidderRequestId: 'test-id', timeout: 1000 }; + const request = spec.buildRequests([bid], br); + const payload = JSON.parse(request[0].data); + + expect(payload.source).to.exist; + expect(payload.source.tid).to.equal('tid-123'); + expect(payload.source.ext.other).to.equal('keep-me'); + expect(payload.source.ext.schain).to.deep.equal(bid.ortb2.source.ext.schain); + }); + }); + describe('interpretResponse', () => { // Add impid: 'bid-id' so requestId matches bidRequestData.bidId const serverResponse = { @@ -200,7 +279,6 @@ describe('holidBidAdapterTests', () => { }; const uspConsent = 'mkjvbiniwot4827obfoy8sdg8203gb'; - // Updated 'usp_consent' to 'us_privacy' to match adapter code const expectedUserSyncs = [ { type: 'image', @@ -256,5 +334,54 @@ describe('holidBidAdapterTests', () => { expect(userSyncs).to.deep.equal(expectedUserSyncs); }); + + // NEW: verify seatbid[].seat fallback is used when responsetimemillis is missing + it('should derive bidders from seatbid[].seat when responsetimemillis is missing', () => { + const optionsType = { iframeEnabled: true, pixelEnabled: true }; + const serverResponse = [ + { + body: { + seatbid: [ + { seat: 'rubicon', bid: [] }, + { seat: 'pubmatic', bid: [] }, + ] + } + } + ]; + + const userSyncs = spec.getUserSyncs(optionsType, serverResponse); + + const iframe = userSyncs.find(s => s.type === 'iframe'); + expect(iframe).to.exist; + expect(iframe.url).to.include('bidders='); + + const decoded = decodeURIComponent(iframe.url.split('bidders=')[1].split('&')[0]); + expect(decoded).to.include('rubicon'); + expect(decoded).to.include('pubmatic'); + }); + + // NEW: verify pixel-based fallback is used when iframe is disabled + it('should add an extra image sync when iframe is disabled but pixelEnabled is true', () => { + const optionsType = { iframeEnabled: false, pixelEnabled: true }; + const serverResponse = [ + { + body: { + ext: { responsetimemillis: { pubmatic: 12 } } + } + } + ]; + + const userSyncs = spec.getUserSyncs(optionsType, serverResponse); + + // base Adform pixel always exists + expect(userSyncs[0].type).to.equal('image'); + + // additional image sync from our endpoint should exist + const extraImages = userSyncs.filter(s => s.type === 'image'); + expect(extraImages.length).to.be.greaterThan(1); + const urls = extraImages.map(s => s.url).join(' '); + expect(urls).to.include('bidders='); + expect(urls).to.include('type=image'); + }); }); }); diff --git a/test/spec/modules/humansecurityRtdProvider_spec.js b/test/spec/modules/humansecurityRtdProvider_spec.js index c6ad163c544..2104ffc6edd 100644 --- a/test/spec/modules/humansecurityRtdProvider_spec.js +++ b/test/spec/modules/humansecurityRtdProvider_spec.js @@ -10,10 +10,7 @@ const { SUBMODULE_NAME, SCRIPT_URL, main, - load, - onImplLoaded, - onImplMessage, - onGetBidRequestData + load } = __TEST__; describe('humansecurity RTD module', function () { @@ -23,20 +20,20 @@ describe('humansecurity RTD module', function () { const sonarStubId = `sonar_${stubUuid}`; const stubWindow = { [sonarStubId]: undefined }; - beforeEach(function() { + beforeEach(function () { sandbox = sinon.createSandbox(); sandbox.stub(utils, 'getWindowSelf').returns(stubWindow); sandbox.stub(utils, 'generateUUID').returns(stubUuid); sandbox.stub(refererDetection, 'getRefererInfo').returns({ domain: 'example.com' }); }); - afterEach(function() { + afterEach(function () { sandbox.restore(); }); describe('Initialization step', function () { let sandbox2; let connectSpy; - beforeEach(function() { + beforeEach(function () { sandbox2 = sinon.createSandbox(); connectSpy = sandbox.spy(); // Once the impl script is loaded, it registers the API using session ID @@ -46,6 +43,17 @@ describe('humansecurity RTD module', function () { sandbox2.restore(); }); + it('should connect to the implementation script once it loads', function () { + load({}); + + expect(loadExternalScriptStub.calledOnce).to.be.true; + const callback = loadExternalScriptStub.getCall(0).args[3]; + expect(callback).to.be.a('function'); + const args = connectSpy.getCall(0).args; + expect(args[0]).to.haveOwnProperty('cmd'); // pbjs global + expect(args[0]).to.haveOwnProperty('que'); + }); + it('should accept valid configurations', function () { // Default configuration - empty expect(() => load({})).to.not.throw(); @@ -62,14 +70,19 @@ describe('humansecurity RTD module', function () { }); it('should insert implementation script', () => { - load({ }); + load({}); expect(loadExternalScriptStub.calledOnce).to.be.true; const args = loadExternalScriptStub.getCall(0).args; - expect(args[0]).to.be.equal(`${SCRIPT_URL}?r=example.com`); + expect(args[0]).to.include(`${SCRIPT_URL}?r=example.com`); + const mvMatch = args[0].match(/[?&]mv=([^&]+)/); + expect(mvMatch).to.not.equal(null); + const mvValue = Number(mvMatch[1]); + expect(Number.isFinite(mvValue)).to.equal(true); + expect(mvValue).to.be.greaterThan(0); expect(args[2]).to.be.equal(SUBMODULE_NAME); - expect(args[3]).to.be.equal(onImplLoaded); + expect(args[3]).to.be.a('function'); expect(args[4]).to.be.equal(null); expect(args[5]).to.be.deep.equal({ 'data-sid': 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee' }); }); @@ -80,90 +93,14 @@ describe('humansecurity RTD module', function () { expect(loadExternalScriptStub.calledOnce).to.be.true; const args = loadExternalScriptStub.getCall(0).args; - expect(args[0]).to.be.equal(`${SCRIPT_URL}?r=example.com&c=customer123`); - }); - - it('should connect to the implementation script once it loads', function () { - load({ }); - - expect(loadExternalScriptStub.calledOnce).to.be.true; - expect(connectSpy.calledOnce).to.be.true; - - const args = connectSpy.getCall(0).args; - expect(args[0]).to.haveOwnProperty('cmd'); // pbjs global - expect(args[0]).to.haveOwnProperty('que'); - expect(args[1]).to.be.equal(onImplMessage); - }); - }); - - describe('Bid enrichment step', function () { - const hmnsData = { 'v1': 'sometoken' }; - - let sandbox2; - let callbackSpy; - let reqBidsConfig; - beforeEach(function() { - sandbox2 = sinon.createSandbox(); - callbackSpy = sandbox2.spy(); - reqBidsConfig = { ortb2Fragments: { bidder: {}, global: {} } }; - }); - afterEach(function () { - sandbox2.restore(); - }); - - it('should add empty device.ext.hmns to global ortb2 when data is yet to be received from the impl script', () => { - load({ }); - - onGetBidRequestData(reqBidsConfig, callbackSpy, { params: {} }, {}); - - expect(callbackSpy.calledOnce).to.be.true; - expect(reqBidsConfig.ortb2Fragments.global).to.have.own.property('device'); - expect(reqBidsConfig.ortb2Fragments.global.device).to.have.own.property('ext'); - expect(reqBidsConfig.ortb2Fragments.global.device.ext).to.have.own.property('hmns').which.is.an('object').that.deep.equals({}); - }); - - it('should add the default device.ext.hmns to global ortb2 when no "hmns" data was yet received', () => { - load({ }); - - onImplMessage({ type: 'info', data: 'not a hmns message' }); - onGetBidRequestData(reqBidsConfig, callbackSpy, { params: {} }, {}); - - expect(callbackSpy.calledOnce).to.be.true; - expect(reqBidsConfig.ortb2Fragments.global).to.have.own.property('device'); - expect(reqBidsConfig.ortb2Fragments.global.device).to.have.own.property('ext'); - expect(reqBidsConfig.ortb2Fragments.global.device.ext).to.have.own.property('hmns').which.is.an('object').that.deep.equals({}); - }); - - it('should add device.ext.hmns with received tokens to global ortb2 when the data was received', () => { - load({ }); - - onImplMessage({ type: 'hmns', data: hmnsData }); - onGetBidRequestData(reqBidsConfig, callbackSpy, { params: {} }, {}); - - expect(callbackSpy.calledOnce).to.be.true; - expect(reqBidsConfig.ortb2Fragments.global).to.have.own.property('device'); - expect(reqBidsConfig.ortb2Fragments.global.device).to.have.own.property('ext'); - expect(reqBidsConfig.ortb2Fragments.global.device.ext).to.have.own.property('hmns').which.is.an('object').that.deep.equals(hmnsData); - }); - - it('should update device.ext.hmns with new data', () => { - load({ }); - - onImplMessage({ type: 'hmns', data: { 'v1': 'should be overwritten' } }); - onImplMessage({ type: 'hmns', data: hmnsData }); - onGetBidRequestData(reqBidsConfig, callbackSpy, { params: {} }, {}); - - expect(callbackSpy.calledOnce).to.be.true; - expect(reqBidsConfig.ortb2Fragments.global).to.have.own.property('device'); - expect(reqBidsConfig.ortb2Fragments.global.device).to.have.own.property('ext'); - expect(reqBidsConfig.ortb2Fragments.global.device.ext).to.have.own.property('hmns').which.is.an('object').that.deep.equals(hmnsData); + expect(args[0]).to.include(`${SCRIPT_URL}?r=example.com&c=customer123`); }); }); - describe('Sumbodule execution', function() { + describe('Submodule execution', function () { let sandbox2; let submoduleStub; - beforeEach(function() { + beforeEach(function () { sandbox2 = sinon.createSandbox(); submoduleStub = sandbox2.stub(hook, 'submodule'); }); @@ -203,7 +140,7 @@ describe('humansecurity RTD module', function () { it('should commence initialization on default initialization', function () { const { init } = getModule(); - expect(init({ })).to.equal(true); + expect(init({})).to.equal(true); expect(loadExternalScriptStub.calledOnce).to.be.true; }); }); diff --git a/test/spec/modules/hybridBidAdapter_spec.js b/test/spec/modules/hybridBidAdapter_spec.js index a0d479fb4dc..6a49ce1a54d 100644 --- a/test/spec/modules/hybridBidAdapter_spec.js +++ b/test/spec/modules/hybridBidAdapter_spec.js @@ -36,15 +36,15 @@ describe('Hybrid.ai Adapter', function() { } const validBidRequests = [ getSlotConfigs({ banner: {} }, bannerMandatoryParams), - getSlotConfigs({ video: {playerSize: [[640, 480]], context: 'outstream'} }, videoMandatoryParams), - getSlotConfigs({ banner: {sizes: [0, 0]} }, inImageMandatoryParams) + getSlotConfigs({ video: { playerSize: [[640, 480]], context: 'outstream' } }, videoMandatoryParams), + getSlotConfigs({ banner: { sizes: [0, 0] } }, inImageMandatoryParams) ] describe('isBidRequestValid method', function() { describe('returns true', function() { describe('when banner slot config has all mandatory params', () => { describe('and banner placement has the correct value', function() { const slotConfig = getSlotConfigs( - {banner: {}}, + { banner: {} }, { placeId: PLACE_ID, placement: 'banner' diff --git a/test/spec/modules/hypelabBidAdapter_spec.js b/test/spec/modules/hypelabBidAdapter_spec.js index ff98c33b136..a8cb6abee6f 100644 --- a/test/spec/modules/hypelabBidAdapter_spec.js +++ b/test/spec/modules/hypelabBidAdapter_spec.js @@ -13,7 +13,7 @@ import { } from 'modules/hypelabBidAdapter.js'; import { BANNER } from 'src/mediaTypes.js'; -import {getDevicePixelRatio} from '../../../libraries/devicePixelRatio/devicePixelRatio.js'; +import { getDevicePixelRatio } from '../../../libraries/devicePixelRatio/devicePixelRatio.js'; const mockValidBidRequest = { bidder: 'hypelab', diff --git a/test/spec/modules/id5AnalyticsAdapter_spec.js b/test/spec/modules/id5AnalyticsAdapter_spec.js index 1e1df900f81..7073e1e9ac0 100644 --- a/test/spec/modules/id5AnalyticsAdapter_spec.js +++ b/test/spec/modules/id5AnalyticsAdapter_spec.js @@ -1,12 +1,12 @@ import adapterManager from '../../../src/adapterManager.js'; import id5AnalyticsAdapter from '../../../modules/id5AnalyticsAdapter.js'; -import {expect} from 'chai'; +import { expect } from 'chai'; import * as events from '../../../src/events.js'; -import {EVENTS} from '../../../src/constants.js'; -import {generateUUID} from '../../../src/utils.js'; -import {server} from '../../mocks/xhr.js'; -import {getGlobal} from '../../../src/prebidGlobal.js'; -import {enrichEidsRule} from "../../../modules/tcfControl.ts"; +import { EVENTS } from '../../../src/constants.js'; +import { generateUUID } from '../../../src/utils.js'; +import { server } from '../../mocks/xhr.js'; +import { getGlobal } from '../../../src/prebidGlobal.js'; +import { enrichEidsRule } from "../../../modules/tcfControl.ts"; import * as utils from '../../../src/utils.js'; const CONFIG_URL = 'https://api.id5-sync.com/analytics/12349/pbjs'; @@ -188,7 +188,7 @@ describe('ID5 analytics adapter', () => { 'criteoId': '_h_y_19IMUhMZG1TOTRReHFNc29TekJ3TzQ3elhnRU81ayUyQjhiRkdJJTJGaTFXJTJCdDRnVmN4S0FETUhQbXdmQWg0M3g1NWtGbGolMkZXalclMkJvWjJDOXFDSk1HU3ZKaVElM0QlM0Q', 'id5id': { 'uid': 'ID5-ZHMOQ99ulpk687Fd9xVwzxMsYtkQIJnI-qm3iWdtww!ID5*FSycZQy7v7zWXiKbEpPEWoB3_UiWdPGzh554ncYDvOkAAA3rajiR0yNrFAU7oDTu', - 'ext': {'linkType': 1} + 'ext': { 'linkType': 1 } }, 'tdid': '888a6042-8f99-483b-aa26-23c44bc9166b' }, @@ -203,7 +203,7 @@ describe('ID5 analytics adapter', () => { 'uids': [{ 'id': 'ID5-ZHMOQ99ulpk687Fd9xVwzxMsYtkQIJnI-qm3iWdtww!ID5*FSycZQy7v7zWXiKbEpPEWoB3_UiWdPGzh554ncYDvOkAAA3rajiR0yNrFAU7oDTu', 'atype': 1, - 'ext': {'linkType': 1} + 'ext': { 'linkType': 1 } }] }] }]; @@ -517,7 +517,7 @@ describe('ID5 analytics adapter', () => { 'userId': { 'id5id': { 'uid': 'ID5-ZHMOQ99ulpk687Fd9xVwzxMsYtkQIJnI-qm3iWdtww!ID5*FSycZQy7v7zWXiKbEpPEWoB3_UiWdPGzh554ncYDvOkAAA3rajiR0yNrFAU7oDTu', - 'ext': {'linkType': 1} + 'ext': { 'linkType': 1 } } } }]; diff --git a/test/spec/modules/id5IdSystem_spec.js b/test/spec/modules/id5IdSystem_spec.js index 7249560c8c9..47b7623ae00 100644 --- a/test/spec/modules/id5IdSystem_spec.js +++ b/test/spec/modules/id5IdSystem_spec.js @@ -7,18 +7,18 @@ import { setSubmoduleRegistry, startAuctionHook } from '../../../modules/userId/index.ts'; -import {config} from '../../../src/config.js'; +import { config } from '../../../src/config.js'; import * as events from '../../../src/events.js'; -import {EVENTS} from '../../../src/constants.js'; +import { EVENTS } from '../../../src/constants.js'; import * as utils from '../../../src/utils.js'; -import {deepClone} from '../../../src/utils.js'; +import { deepClone } from '../../../src/utils.js'; import '../../../src/prebid.js'; -import {hook} from '../../../src/hook.js'; -import {mockGdprConsent} from '../../helpers/consentData.js'; -import {server} from '../../mocks/xhr.js'; -import {expect} from 'chai'; -import {PbPromise} from '../../../src/utils/promise.js'; -import {createEidsArray} from '../../../modules/userId/eids.js'; +import { hook } from '../../../src/hook.js'; +import { mockGdprConsent } from '../../helpers/consentData.js'; +import { server } from '../../mocks/xhr.js'; +import { expect } from 'chai'; +import { PbPromise } from '../../../src/utils/promise.js'; +import { createEidsArray } from '../../../modules/userId/eids.js'; describe('ID5 ID System', function () { let logInfoStub; @@ -199,9 +199,9 @@ describe('ID5 ID System', function () { function getAdUnitMock(code = 'adUnit-code') { return { code, - mediaTypes: {banner: {}, native: {}}, + mediaTypes: { banner: {}, native: {} }, sizes: [[300, 200], [300, 600]], - bids: [{bidder: 'sampleBidder', params: {placementId: 'banner-only-bidder'}}] + bids: [{ bidder: 'sampleBidder', params: { placementId: 'banner-only-bidder' } }] }; } @@ -391,30 +391,30 @@ describe('ID5 ID System', function () { expect(id5System.id5IdSubmodule.getId({})).is.eq(undefined); // valid params, invalid id5System.storage - expect(id5System.id5IdSubmodule.getId({params: {partner: 123}})).to.be.eq(undefined); - expect(id5System.id5IdSubmodule.getId({params: {partner: 123}, storage: {}})).to.be.eq(undefined); - expect(id5System.id5IdSubmodule.getId({params: {partner: 123}, storage: {name: ''}})).to.be.eq(undefined); - expect(id5System.id5IdSubmodule.getId({params: {partner: 123}, storage: {type: ''}})).to.be.eq(undefined); + expect(id5System.id5IdSubmodule.getId({ params: { partner: 123 } })).to.be.eq(undefined); + expect(id5System.id5IdSubmodule.getId({ params: { partner: 123 }, storage: {} })).to.be.eq(undefined); + expect(id5System.id5IdSubmodule.getId({ params: { partner: 123 }, storage: { name: '' } })).to.be.eq(undefined); + expect(id5System.id5IdSubmodule.getId({ params: { partner: 123 }, storage: { type: '' } })).to.be.eq(undefined); // valid id5System.storage, invalid params - expect(id5System.id5IdSubmodule.getId({storage: {name: 'name', type: 'html5'}})).to.be.eq(undefined); - expect(id5System.id5IdSubmodule.getId({storage: {name: 'name', type: 'html5'}, params: {}})).to.be.eq(undefined); + expect(id5System.id5IdSubmodule.getId({ storage: { name: 'name', type: 'html5' } })).to.be.eq(undefined); + expect(id5System.id5IdSubmodule.getId({ storage: { name: 'name', type: 'html5' }, params: {} })).to.be.eq(undefined); expect(id5System.id5IdSubmodule.getId({ - storage: {name: 'name', type: 'html5'}, - params: {partner: 'abc'} + storage: { name: 'name', type: 'html5' }, + params: { partner: 'abc' } })).to.be.eq(undefined); }); it('should warn with non-recommended id5System.storage params', function () { const logWarnStub = sinon.stub(utils, 'logWarn'); - id5System.id5IdSubmodule.getId({storage: {name: 'name', type: 'html5'}, params: {partner: 123}}); + id5System.id5IdSubmodule.getId({ storage: { name: 'name', type: 'html5' }, params: { partner: 123 } }); expect(logWarnStub.calledOnce).to.be.true; logWarnStub.restore(); id5System.id5IdSubmodule.getId({ - storage: {name: id5System.ID5_STORAGE_NAME, type: 'cookie'}, - params: {partner: 123} + storage: { name: id5System.ID5_STORAGE_NAME, type: 'cookie' }, + params: { partner: 123 } }); expect(logWarnStub.calledOnce).to.be.true; logWarnStub.restore(); @@ -423,14 +423,14 @@ describe('ID5 ID System', function () { describe('Check for valid consent', function () { const dataConsentVals = [ - [{purpose: {consents: {1: false}}}, {vendor: {consents: {131: true}}}, ' no purpose consent'], - [{purpose: {consents: {1: true}}}, {vendor: {consents: {131: false}}}, ' no vendor consent'], - [{purpose: {consents: {1: false}}}, {vendor: {consents: {131: false}}}, ' no purpose and vendor consent'], - [{purpose: {consents: undefined}}, {vendor: {consents: {131: true}}}, ' undefined purpose consent'], - [{purpose: {consents: {1: false}}}, {vendor: {consents: undefined}}], ' undefined vendor consent', - [undefined, {vendor: {consents: {131: true}}}, ' undefined purpose'], - [{purpose: {consents: {1: true}}}, {vendor: undefined}, ' undefined vendor'], - [{purpose: {consents: {1: true}}}, {vendor: {consents: {31: true}}}, ' incorrect vendor consent'] + [{ purpose: { consents: { 1: false } } }, { vendor: { consents: { 131: true } } }, ' no purpose consent'], + [{ purpose: { consents: { 1: true } } }, { vendor: { consents: { 131: false } } }, ' no vendor consent'], + [{ purpose: { consents: { 1: false } } }, { vendor: { consents: { 131: false } } }, ' no purpose and vendor consent'], + [{ purpose: { consents: undefined } }, { vendor: { consents: { 131: true } } }, ' undefined purpose consent'], + [{ purpose: { consents: { 1: false } } }, { vendor: { consents: undefined } }], ' undefined vendor consent', + [undefined, { vendor: { consents: { 131: true } } }, ' undefined purpose'], + [{ purpose: { consents: { 1: true } } }, { vendor: undefined }, ' undefined vendor'], + [{ purpose: { consents: { 1: true } } }, { vendor: { consents: { 31: true } } }, ' incorrect vendor consent'] ]; dataConsentVals.forEach(function ([purposeConsent, vendorConsent, caseName]) { @@ -442,10 +442,10 @@ describe('ID5 ID System', function () { purposeConsent, vendorConsent } }; - expect(id5System.id5IdSubmodule.getId(config, {gdpr: dataConsent})).is.eq(undefined); + expect(id5System.id5IdSubmodule.getId(config, { gdpr: dataConsent })).is.eq(undefined); const cacheIdObject = 'cacheIdObject'; - expect(id5System.id5IdSubmodule.extendId(config, {gdpr: dataConsent}, cacheIdObject)).is.eql({id: cacheIdObject}); + expect(id5System.id5IdSubmodule.extendId(config, { gdpr: dataConsent }, cacheIdObject)).is.eql({ id: cacheIdObject }); }); }); }); @@ -501,7 +501,7 @@ describe('ID5 ID System', function () { // Trigger the fetch but we await on it later const config = getId5FetchConfig(); - const submoduleResponsePromise = callSubmoduleGetId(config, {gdpr: consentData}, undefined); + const submoduleResponsePromise = callSubmoduleGetId(config, { gdpr: consentData }, undefined); const fetchRequest = await xhrServerMock.expectFetchRequest(); const requestBody = JSON.parse(fetchRequest.requestBody); @@ -548,7 +548,7 @@ describe('ID5 ID System', function () { // Trigger the fetch but we await on it later const config = getId5FetchConfig(); - const submoduleResponsePromise = callSubmoduleGetId(config, {gdpr: consentData, usp: usPrivacyString}, undefined); + const submoduleResponsePromise = callSubmoduleGetId(config, { gdpr: consentData, usp: usPrivacyString }, undefined); const fetchRequest = await xhrServerMock.expectFetchRequest(); const requestBody = JSON.parse(fetchRequest.requestBody); @@ -859,7 +859,7 @@ describe('ID5 ID System', function () { it('should call the ID5 server with ab_testing object when abTesting is turned on', async function () { const xhrServerMock = new XhrServerMock(server); const id5Config = getId5FetchConfig(); - id5Config.params.abTesting = {enabled: true, controlGroupPct: 0.234}; + id5Config.params.abTesting = { enabled: true, controlGroupPct: 0.234 }; // Trigger the fetch but we await on it later const submoduleResponsePromise = callSubmoduleGetId(id5Config, undefined, oldStoredObject(ID5_STORED_OBJ)); @@ -876,7 +876,7 @@ describe('ID5 ID System', function () { it('should call the ID5 server without ab_testing object when abTesting is turned off', async function () { const xhrServerMock = new XhrServerMock(server); const id5Config = getId5FetchConfig(); - id5Config.params.abTesting = {enabled: false, controlGroupPct: 0.55}; + id5Config.params.abTesting = { enabled: false, controlGroupPct: 0.55 }; // Trigger the fetch but we await on it later const submoduleResponsePromise = callSubmoduleGetId(id5Config, undefined, oldStoredObject(ID5_STORED_OBJ)); @@ -969,7 +969,7 @@ describe('ID5 ID System', function () { gppString: 'GPP_STRING', applicableSections: [2] }; - const submoduleResponse = callSubmoduleGetId(getId5FetchConfig(), {gpp: gppData}, oldStoredObject(ID5_STORED_OBJ)); + const submoduleResponse = callSubmoduleGetId(getId5FetchConfig(), { gpp: gppData }, oldStoredObject(ID5_STORED_OBJ)); return xhrServerMock.expectFetchRequest() .then(fetchRequest => { @@ -1003,7 +1003,7 @@ describe('ID5 ID System', function () { return xhrServerMock.expectFetchRequest() .then(fetchRequest => { const requestBody = JSON.parse(fetchRequest.requestBody); - expect(requestBody.true_link).is.eql({booted: false}); + expect(requestBody.true_link).is.eql({ booted: false }); fetchRequest.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); return submoduleResponse; }); @@ -1011,7 +1011,7 @@ describe('ID5 ID System', function () { it('should pass full true link info to ID5 server when true link is booted', function () { const xhrServerMock = new XhrServerMock(server); - const trueLinkResponse = {booted: true, redirected: true, id: 'TRUE_LINK_ID'}; + const trueLinkResponse = { booted: true, redirected: true, id: 'TRUE_LINK_ID' }; window.id5Bootstrap = { getTrueLinkInfo: function () { return trueLinkResponse; @@ -1105,7 +1105,7 @@ describe('ID5 ID System', function () { }] }); done(); - }), {ortb2Fragments}); + }), { ortb2Fragments }); }); it('should add stored EUID from cache to bids', function (done) { @@ -1128,7 +1128,7 @@ describe('ID5 ID System', function () { }] }); done(); - }, {ortb2Fragments}); + }, { ortb2Fragments }); }); it('should add stored TRUE_LINK_ID from cache to bids', function (done) { @@ -1147,7 +1147,7 @@ describe('ID5 ID System', function () { }] }); done(); - }), {ortb2Fragments}); + }), { ortb2Fragments }); }); }); @@ -1168,7 +1168,7 @@ describe('ID5 ID System', function () { return new Promise((resolve) => { startAuctionHook(() => { resolve(); - }, {adUnits}); + }, { adUnits }); }).then(() => { expect(xhrServerMock.hasReceivedAnyRequest()).is.false; events.emit(EVENTS.AUCTION_END, {}); @@ -1201,7 +1201,7 @@ describe('ID5 ID System', function () { }] }); done(); - }), {ortb2Fragments}); + }), { ortb2Fragments }); }); it('should add stored EUID from cache to bids - from ids', function (done) { @@ -1222,7 +1222,7 @@ describe('ID5 ID System', function () { expect(eids[0]).is.eql(IDS_ID5ID.eid); expect(eids[1]).is.eql(IDS_EUID.eid); done(); - }), {ortb2Fragments}); + }), { ortb2Fragments }); }); it('should add stored TRUE_LINK_ID from cache to bids - from ids', function (done) { @@ -1241,7 +1241,7 @@ describe('ID5 ID System', function () { startAuctionHook(wrapAsyncExpects(done, function () { expect(ortb2Fragments.global.user.ext.eids[1]).is.eql(IDS_TRUE_LINK_ID.eid); done(); - }), {ortb2Fragments}); + }), { ortb2Fragments }); }); it('should add other id from cache to bids', function (done) { @@ -1286,13 +1286,13 @@ describe('ID5 ID System', function () { }] }); done(); - }), {ortb2Fragments}); + }), { ortb2Fragments }); }); }); }); describe('Decode id5response', function () { - const expectedDecodedObject = {id5id: {uid: ID5_STORED_ID, ext: {linkType: ID5_STORED_LINK_TYPE}}}; + const expectedDecodedObject = { id5id: { uid: ID5_STORED_ID, ext: { linkType: ID5_STORED_LINK_TYPE } } }; it('should return undefined if passed a string', function () { expect(id5System.id5IdSubmodule.decode('somestring', getId5FetchConfig())).is.eq(undefined); @@ -1315,7 +1315,7 @@ describe('ID5 ID System', function () { expect(id5System.id5IdSubmodule.decode(responseF(ID5_STORED_OBJ_WITH_EUID, config), config).euid).is.eql({ 'source': EUID_SOURCE, 'uid': EUID_STORED_ID, - 'ext': {'provider': ID5_SOURCE} + 'ext': { 'provider': ID5_SOURCE } }); }); it('should decode trueLinkId from a stored object with trueLinkId', function () { @@ -1361,13 +1361,8 @@ describe('ID5 ID System', function () { setTargetingStub = sinon.stub(); window.googletag = { cmd: [], - pubads: function () { - return { - setTargeting: setTargetingStub - }; - } + setConfig: setTargetingStub }; - sinon.spy(window.googletag, 'pubads'); storedObject = utils.deepClone(ID5_STORED_OBJ); }); @@ -1391,14 +1386,16 @@ describe('ID5 ID System', function () { for (const [tagName, tagValue] of Object.entries(tagsObj)) { const fullTagName = `${targetingEnabledConfig.params.gamTargetingPrefix}_${tagName}`; - const matchingCall = setTargetingStub.getCalls().find(call => call.args[0] === fullTagName); + const matchingCall = setTargetingStub.getCalls().find(call => { + const config = call.args[0]; + return config.targeting && config.targeting[fullTagName] !== undefined; + }); expect(matchingCall, `Tag ${fullTagName} was not set`).to.exist; - expect(matchingCall.args[1]).to.equal(tagValue); + expect(matchingCall.args[0].targeting[fullTagName]).to.equal(tagValue); } window.googletag.cmd = []; setTargetingStub.reset(); - window.googletag.pubads.resetHistory(); } it('should not set GAM targeting if it is not enabled', function () { @@ -1435,15 +1432,315 @@ describe('ID5 ID System', function () { }) }) + describe('Decode should also expose targeting via id5tags if configured', function () { + let origId5tags, storedObject; + const exposeTargetingConfig = getId5FetchConfig(); + exposeTargetingConfig.params.gamTargetingPrefix = 'id5'; + exposeTargetingConfig.params.exposeTargeting = true; + + beforeEach(function () { + delete window.id5tags; + storedObject = utils.deepClone(ID5_STORED_OBJ); + }); + + afterEach(function () { + delete window.id5tags; + id5System.id5IdSubmodule._reset(); + }); + + it('should not expose targeting if exposeTargeting is not enabled', function () { + const config = getId5FetchConfig(); + config.params.gamTargetingPrefix = 'id5'; + // exposeTargeting is not set + const testObj = { + ...storedObject, + 'tags': { + 'id': 'y', + 'ab': 'n' + } + }; + id5System.id5IdSubmodule.decode(testObj, config); + expect(window.id5tags).to.be.undefined; + }); + + it('should not expose targeting if tags not returned from server', function () { + // tags is not in the response + id5System.id5IdSubmodule.decode(storedObject, exposeTargetingConfig); + expect(window.id5tags).to.be.undefined; + }); + + it('should create id5tags.cmd when it does not exist pre-decode', function () { + const testObj = { + ...storedObject, + 'tags': { + 'id': 'y', + 'ab': 'n' + } + }; + id5System.id5IdSubmodule.decode(testObj, exposeTargetingConfig); + + expect(window.id5tags).to.exist; + expect(window.id5tags.cmd).to.be.an('array'); + expect(window.id5tags.tags).to.deep.equal({ + 'id': 'y', + 'ab': 'n' + }); + }); + + it('should execute queued functions when cmd was created earlier', async function () { + const testTags = { + 'id': 'y', + 'ab': 'n', + 'enrich': 'y' + }; + const testObj = { + ...storedObject, + 'tags': testTags + }; + + const callTracker = []; + let resolvePromise; + const callbackPromise = new Promise((resolve) => { + resolvePromise = resolve; + }); + + // Pre-create id5tags with queued functions + window.id5tags = { + cmd: [ + (tags) => callTracker.push({ call: 1, tags: tags }), + (tags) => callTracker.push({ call: 2, tags: tags }), + (tags) => { + callTracker.push({ call: 3, tags: tags }); + resolvePromise(); + } + ] + }; + + id5System.id5IdSubmodule.decode(testObj, exposeTargetingConfig); + + await callbackPromise; + + // Verify all queued functions were called with the tags + expect(callTracker).to.have.lengthOf(3); + expect(callTracker[0]).to.deep.equal({ call: 1, tags: testTags }); + expect(callTracker[1]).to.deep.equal({ call: 2, tags: testTags }); + expect(callTracker[2]).to.deep.equal({ call: 3, tags: testTags }); + + // Verify tags were stored + expect(window.id5tags.tags).to.deep.equal(testTags); + }); + + it('should override push method to execute functions immediately', function () { + const testTags = { + 'id': 'y', + 'ab': 'n' + }; + const testObj = { + ...storedObject, + 'tags': testTags + }; + + id5System.id5IdSubmodule.decode(testObj, exposeTargetingConfig); + + // Now push a new function and verify it executes immediately + let callResult = null; + window.id5tags.cmd.push((tags) => { + callResult = { executed: true, tags: tags }; + }); + + expect(callResult).to.not.be.null; + expect(callResult.executed).to.be.true; + expect(callResult.tags).to.deep.equal(testTags); + }); + + it('should retrigger functions when tags are different but not when tags are the same', async function () { + const firstTags = { + 'id': 'y', + 'ab': 'n' + }; + const secondTags = { + 'id': 'y', + 'ab': 'y', + 'enrich': 'y' + }; + + const firstObj = { + ...storedObject, + 'tags': firstTags + }; + + const callTracker = []; + + // First decode + let resolveFirstPromise; + const firstCallbackPromise = new Promise((resolve) => { + resolveFirstPromise = resolve; + }); + + window.id5tags = { + cmd: [ + (tags) => { + callTracker.push({ call: 'decode', tags: utils.deepClone(tags) }); + resolveFirstPromise(); + } + ] + }; + + id5System.id5IdSubmodule.decode(firstObj, exposeTargetingConfig); + + await firstCallbackPromise; + + expect(callTracker).to.have.lengthOf(1); + expect(callTracker[0].tags).to.deep.equal(firstTags); + + // Second decode with different tags - should retrigger + const secondObj = { + ...storedObject, + 'tags': secondTags + }; + + let resolveSecondPromise; + const secondCallbackPromise = new Promise((resolve) => { + resolveSecondPromise = resolve; + }); + + // Update the callback to resolve when called again + window.id5tags.cmd[0] = (tags) => { + callTracker.push({ call: 'decode', tags: utils.deepClone(tags) }); + resolveSecondPromise(); + }; + + id5System.id5IdSubmodule.decode(secondObj, exposeTargetingConfig); + + await secondCallbackPromise; + + // The queued function should be called again with new tags + expect(callTracker).to.have.lengthOf(2); + expect(callTracker[1].tags).to.deep.equal(secondTags); + expect(window.id5tags.tags).to.deep.equal(secondTags); + + // Third decode with identical tags content (but different object reference) - should NOT retrigger + const thirdObj = { + ...storedObject, + 'tags': { + 'id': 'y', + 'ab': 'y', + 'enrich': 'y' + } + }; + + id5System.id5IdSubmodule.decode(thirdObj, exposeTargetingConfig); + + // Give it a small delay to ensure it doesn't retrigger + await new Promise(resolve => setTimeout(resolve, 50)); + + // With deepEqual, this should NOT retrigger since content is the same as secondTags + expect(callTracker).to.have.lengthOf(2); + expect(window.id5tags.tags).to.deep.equal(secondTags); + }); + + it('should handle when someone else has set id5tags.cmd earlier', async function () { + const testTags = { + 'id': 'y', + 'ab': 'n' + }; + const testObj = { + ...storedObject, + 'tags': testTags + }; + + const externalCallTracker = []; + let resolvePromise; + const callbackPromise = new Promise((resolve) => { + resolvePromise = resolve; + }); + + // External script creates id5tags + window.id5tags = { + cmd: [], + externalData: 'some-external-value' + }; + + // Add external function + window.id5tags.cmd.push((tags) => { + externalCallTracker.push({ external: true, tags: tags }); + resolvePromise(); + }); + + id5System.id5IdSubmodule.decode(testObj, exposeTargetingConfig); + + await callbackPromise; + + // External function should be called + expect(externalCallTracker).to.have.lengthOf(1); + expect(externalCallTracker[0].external).to.be.true; + expect(externalCallTracker[0].tags).to.deep.equal(testTags); + + // External data should be preserved + expect(window.id5tags.externalData).to.equal('some-external-value'); + + // Tags should be set + expect(window.id5tags.tags).to.deep.equal(testTags); + }); + + it('should work with both gamTargetingPrefix and exposeTargeting enabled', async function () { + // Setup googletag + const origGoogletag = window.googletag; + window.googletag = { + cmd: [], + setConfig: sinon.stub() + }; + + const testTags = { + 'id': 'y', + 'ab': 'n' + }; + const testObj = { + ...storedObject, + 'tags': testTags + }; + + const callTracker = []; + let resolvePromise; + const callbackPromise = new Promise((resolve) => { + resolvePromise = resolve; + }); + + window.id5tags = { + cmd: [(tags) => { + callTracker.push(tags); + resolvePromise(); + }] + }; + + id5System.id5IdSubmodule.decode(testObj, exposeTargetingConfig); + + await callbackPromise; + + // Both mechanisms should work + expect(window.googletag.cmd.length).to.be.at.least(1); + expect(callTracker).to.have.lengthOf(1); + expect(callTracker[0]).to.deep.equal(testTags); + expect(window.id5tags.tags).to.deep.equal(testTags); + + // Restore + if (origGoogletag) { + window.googletag = origGoogletag; + } else { + delete window.googletag; + } + }); + }); + describe('A/B Testing', function () { - const expectedDecodedObjectWithIdAbOff = {id5id: {uid: ID5_STORED_ID, ext: {linkType: ID5_STORED_LINK_TYPE}}}; + const expectedDecodedObjectWithIdAbOff = { id5id: { uid: ID5_STORED_ID, ext: { linkType: ID5_STORED_LINK_TYPE } } }; const expectedDecodedObjectWithIdAbOn = { id5id: { uid: ID5_STORED_ID, - ext: {linkType: ID5_STORED_LINK_TYPE, abTestingControlGroup: false} + ext: { linkType: ID5_STORED_LINK_TYPE, abTestingControlGroup: false } } }; - const expectedDecodedObjectWithoutIdAbOn = {id5id: {uid: '', ext: {linkType: 0, abTestingControlGroup: true}}}; + const expectedDecodedObjectWithoutIdAbOn = { id5id: { uid: '', ext: { linkType: 0, abTestingControlGroup: true } } }; let testConfig, storedObject; beforeEach(function () { @@ -1480,13 +1777,13 @@ describe('ID5 ID System', function () { }); it('should set abTestingControlGroup to false when A/B testing is on but in normal group', function () { - storedObject.ab_testing = {result: 'normal'}; + storedObject.ab_testing = { result: 'normal' }; const decoded = id5System.id5IdSubmodule.decode(id5PrebidResponse(storedObject, testConfig), testConfig); expect(decoded).is.eql(expectedDecodedObjectWithIdAbOn); }); it('should not expose ID when everyone is in control group', function () { - storedObject.ab_testing = {result: 'control'}; + storedObject.ab_testing = { result: 'control' }; storedObject.universal_uid = ''; storedObject.ext = { 'linkType': 0 @@ -1496,7 +1793,7 @@ describe('ID5 ID System', function () { }); it('should log A/B testing errors', function () { - storedObject.ab_testing = {result: 'error'}; + storedObject.ab_testing = { result: 'error' }; const decoded = id5System.id5IdSubmodule.decode(id5PrebidResponse(storedObject, testConfig), testConfig); expect(decoded).is.eql(expectedDecodedObjectWithIdAbOff); sinon.assert.calledOnce(logErrorSpy); @@ -1518,7 +1815,7 @@ describe('ID5 ID System', function () { expect(newEids.length).to.equal(1); expect(newEids[0]).to.deep.equal({ source: 'id5-sync.com', - uids: [{id: 'some-random-id-value', atype: 1}] + uids: [{ id: 'some-random-id-value', atype: 1 }] }); }); diff --git a/test/spec/modules/idImportLibrary_spec.js b/test/spec/modules/idImportLibrary_spec.js index 4604d7ea465..f440789c09b 100644 --- a/test/spec/modules/idImportLibrary_spec.js +++ b/test/spec/modules/idImportLibrary_spec.js @@ -1,13 +1,13 @@ -import {init} from 'modules/userId/index.js'; +import { init } from 'modules/userId/index.js'; import * as utils from 'src/utils.js'; import * as idImportlibrary from 'modules/idImportLibrary.js'; -import {getGlobal} from '../../../src/prebidGlobal.js'; -import {config} from 'src/config.js'; -import {hook} from '../../../src/hook.js'; +import { getGlobal } from '../../../src/prebidGlobal.js'; +import { config } from 'src/config.js'; +import { hook } from '../../../src/hook.js'; import * as activities from '../../../src/activities/rules.js'; import { ACTIVITY_ENRICH_UFPD } from '../../../src/activities/activities.js'; import { CONF_DEFAULT_FULL_BODY_SCAN, CONF_DEFAULT_INPUT_SCAN } from '../../../modules/idImportLibrary.js'; -import {server} from 'test/mocks/xhr.js'; +import { server } from 'test/mocks/xhr.js'; var expect = require('chai').expect; @@ -110,7 +110,7 @@ describe('IdImportLibrary Tests', function () { refreshUserIdSpy = sinon.stub(getGlobal(), 'refreshUserIds'); clock = sinon.useFakeTimers(1046952000000); // 2003-03-06T12:00:00Z mutationObserverStub = sinon.stub(window, 'MutationObserver').returns(mockMutationObserver); - userId = sandbox.stub(getGlobal(), 'getUserIds').returns({id: {'MOCKID': '1111'}}); + userId = sandbox.stub(getGlobal(), 'getUserIds').returns({ id: { 'MOCKID': '1111' } }); server.respondWith('POST', 'URL', [200, { 'Content-Type': 'application/json', diff --git a/test/spec/modules/identityLinkIdSystem_spec.js b/test/spec/modules/identityLinkIdSystem_spec.js index fddca301e36..da3dbea10dc 100644 --- a/test/spec/modules/identityLinkIdSystem_spec.js +++ b/test/spec/modules/identityLinkIdSystem_spec.js @@ -1,17 +1,17 @@ -import {getEnvelopeFromStorage, identityLinkSubmodule} from 'modules/identityLinkIdSystem.js'; +import { getEnvelopeFromStorage, identityLinkSubmodule } from 'modules/identityLinkIdSystem.js'; import * as utils from 'src/utils.js'; -import {server} from 'test/mocks/xhr.js'; -import {getCoreStorageManager} from '../../../src/storageManager.js'; -import {stub} from 'sinon'; -import {attachIdSystem} from '../../../modules/userId/index.js'; -import {createEidsArray} from '../../../modules/userId/eids.js'; -import {expect} from 'chai/index.mjs'; +import { server } from 'test/mocks/xhr.js'; +import { getCoreStorageManager } from '../../../src/storageManager.js'; +import { stub } from 'sinon'; +import { attachIdSystem } from '../../../modules/userId/index.js'; +import { createEidsArray } from '../../../modules/userId/eids.js'; +import { expect } from 'chai/index.mjs'; const storage = getCoreStorageManager(); const pid = '14'; let defaultConfigParams; -const responseHeader = {'Content-Type': 'application/json'}; +const responseHeader = { 'Content-Type': 'application/json' }; const testEnvelope = 'eyJ0aW1lc3RhbXAiOjE2OTEwNjU5MzQwMTcsInZlcnNpb24iOiIxLjIuMSIsImVudmVsb3BlIjoiQWhIenUyMFN3WHZ6T0hPd3c2bkxaODAtd2hoN2Nnd0FqWllNdkQ0UjBXT25xRVc1N21zR2Vral9QejU2b1FwcGdPOVB2aFJFa3VHc2lMdG56c3A2aG13eDRtTTRNLTctRy12NiJ9'; const testEnvelopeValue = '{"timestamp":1691065934017,"version":"1.2.1","envelope":"AhHzu20SwXvzOHOww6nLZ80-whh7cgwAjZYMvD4R0WOnqEW57msGekj_Pz56oQppgO9PvhREkuGsiLtnzsp6hmwx4mM4M-7-G-v6"}'; @@ -26,7 +26,7 @@ describe('IdentityLinkId tests', function () { let gppConsentDataStub; beforeEach(function () { - defaultConfigParams = { params: {pid: pid} }; + defaultConfigParams = { params: { pid: pid } }; logErrorStub = sinon.stub(utils, 'logError'); // remove _lr_retry_request cookie before test storage.setCookie('_lr_retry_request', 'true', 'Thu, 01 Jan 1970 00:00:01 GMT'); @@ -68,13 +68,13 @@ describe('IdentityLinkId tests', function () { gdprApplies: true, consentString: '' }; - const submoduleCallback = identityLinkSubmodule.getId(defaultConfigParams, {gdpr: consentData}); + const submoduleCallback = identityLinkSubmodule.getId(defaultConfigParams, { gdpr: consentData }); expect(submoduleCallback).to.be.undefined; }); it('should NOT call the LiveRamp envelope endpoint if gdpr applies but consent string is missing', function () { const consentData = { gdprApplies: true }; - const submoduleCallback = identityLinkSubmodule.getId(defaultConfigParams, {gdpr: consentData}); + const submoduleCallback = identityLinkSubmodule.getId(defaultConfigParams, { gdpr: consentData }); expect(submoduleCallback).to.be.undefined; }); @@ -87,7 +87,7 @@ describe('IdentityLinkId tests', function () { tcfPolicyVersion: 2 } }; - const submoduleCallback = identityLinkSubmodule.getId(defaultConfigParams, {gdpr: consentData}).callback; + const submoduleCallback = identityLinkSubmodule.getId(defaultConfigParams, { gdpr: consentData }).callback; submoduleCallback(callBackSpy); const request = server.requests[0]; expect(request.url).to.be.eq('https://api.rlcdn.com/api/identity/envelope?pid=14&ct=4&cv=CO4VThZO4VTiuADABBENAzCgAP_AAEOAAAAAAwwAgAEABhAAgAgAAA.YAAAAAAAAAA'); @@ -106,7 +106,7 @@ describe('IdentityLinkId tests', function () { applicableSections: [7] }; const callBackSpy = sinon.spy(); - const submoduleCallback = identityLinkSubmodule.getId(defaultConfigParams, {gpp: gppData}).callback; + const submoduleCallback = identityLinkSubmodule.getId(defaultConfigParams, { gpp: gppData }).callback; submoduleCallback(callBackSpy); const request = server.requests[0]; expect(request.url).to.be.eq('https://api.rlcdn.com/api/identity/envelope?pid=14&gpp=DBABLA~BVVqAAAACqA.QA&gpp_sid=7'); @@ -125,7 +125,7 @@ describe('IdentityLinkId tests', function () { applicableSections: [7] }; const callBackSpy = sinon.spy(); - const submoduleCallback = identityLinkSubmodule.getId(defaultConfigParams, {gpp: gppData}).callback; + const submoduleCallback = identityLinkSubmodule.getId(defaultConfigParams, { gpp: gppData }).callback; submoduleCallback(callBackSpy); const request = server.requests[0]; expect(request.url).to.be.eq('https://api.rlcdn.com/api/identity/envelope?pid=14'); @@ -239,8 +239,10 @@ describe('IdentityLinkId tests', function () { it('if ats is present on a page, and envelope is generated and stored in storage, call a callback', function () { setTestEnvelopeCookie(); const envelopeValueFromStorage = getEnvelopeFromStorage(); - window.ats = {retrieveEnvelope: function() { - }} + window.ats = { + retrieveEnvelope: function() { + } + } // mock ats.retrieveEnvelope to return envelope stub(window.ats, 'retrieveEnvelope').callsFake(function() { return envelopeValueFromStorage }) const callBackSpy = sinon.spy(); @@ -262,7 +264,7 @@ describe('IdentityLinkId tests', function () { expect(newEids.length).to.equal(1); expect(newEids[0]).to.deep.equal({ source: 'liveramp.com', - uids: [{id: 'some-random-id-value', atype: 3}] + uids: [{ id: 'some-random-id-value', atype: 3 }] }); }); }) diff --git a/test/spec/modules/idxIdSystem_spec.js b/test/spec/modules/idxIdSystem_spec.js index a5bb7d5d762..945cc4b263e 100644 --- a/test/spec/modules/idxIdSystem_spec.js +++ b/test/spec/modules/idxIdSystem_spec.js @@ -1,5 +1,5 @@ -import {expect} from 'chai'; -import {idxIdSubmodule, storage} from 'modules/idxIdSystem.js'; +import { expect } from 'chai'; +import { idxIdSubmodule, storage } from 'modules/idxIdSystem.js'; import 'src/prebid.js'; const IDX_COOKIE_NAME = '_idx'; diff --git a/test/spec/modules/illuminBidAdapter_spec.js b/test/spec/modules/illuminBidAdapter_spec.js index 05b803d5f82..50ccd555a4a 100644 --- a/test/spec/modules/illuminBidAdapter_spec.js +++ b/test/spec/modules/illuminBidAdapter_spec.js @@ -1,14 +1,14 @@ -import {expect} from 'chai'; +import { expect } from 'chai'; import { spec as adapter, createDomain, storage } from 'modules/illuminBidAdapter.js'; import * as utils from 'src/utils.js'; -import {version} from 'package.json'; -import {useFakeTimers} from 'sinon'; -import {BANNER, VIDEO} from '../../../src/mediaTypes.js'; -import {config} from '../../../src/config.js'; +import { version } from 'package.json'; +import { useFakeTimers } from 'sinon'; +import { BANNER, VIDEO } from '../../../src/mediaTypes.js'; +import { config } from '../../../src/config.js'; import { hashCode, extractPID, @@ -19,7 +19,7 @@ import { tryParseJSON, getUniqueDealId, } from '../../../libraries/vidazooUtils/bidderUtils.js'; -import {getGlobal} from '../../../src/prebidGlobal.js'; +import { getGlobal } from '../../../src/prebidGlobal.js'; export const TEST_ID_SYSTEMS = ['criteoId', 'id5id', 'idl_env', 'lipb', 'netId', 'pubcid', 'tdid', 'pubProvidedId']; @@ -103,9 +103,9 @@ const ORTB2_DEVICE = { 'version': ['8', '0', '0'] }, 'browsers': [ - {'brand': 'Not_A Brand', 'version': ['99', '0', '0', '0']}, - {'brand': 'Google Chrome', 'version': ['109', '0', '5414', '119']}, - {'brand': 'Chromium', 'version': ['109', '0', '5414', '119']} + { 'brand': 'Not_A Brand', 'version': ['99', '0', '0', '0'] }, + { 'brand': 'Google Chrome', 'version': ['109', '0', '5414', '119'] }, + { 'brand': 'Chromium', 'version': ['109', '0', '5414', '119'] } ], 'mobile': 1, 'model': 'SM-G955U', @@ -122,7 +122,7 @@ const ORTB2_DEVICE = { model: 'iPhone 12 Pro Max', os: 'iOS', osv: '17.4', - ext: {fiftyonedegrees_deviceId: '17595-133085-133468-18092'}, + ext: { fiftyonedegrees_deviceId: '17595-133085-133468-18092' }, }; const BIDDER_REQUEST = { @@ -193,9 +193,8 @@ const VIDEO_SERVER_RESPONSE = { const ORTB2_OBJ = { "device": ORTB2_DEVICE, - "regs": {"coppa": 0, "gpp": "gpp_string", "gpp_sid": [7]}, - "site": {"content": {"language": "en"} - } + "regs": { "coppa": 0, "gpp": "gpp_string", "gpp_sid": [7] }, + "site": { "content": { "language": "en" } } }; const REQUEST = { @@ -208,7 +207,7 @@ const REQUEST = { function getTopWindowQueryParams() { try { - const parsedUrl = utils.parseUrl(window.top.document.URL, {decodeSearchAsString: true}); + const parsedUrl = utils.parseUrl(window.top.document.URL, { decodeSearchAsString: true }); return parsedUrl.search; } catch (e) { return ''; @@ -330,9 +329,9 @@ describe('IlluminBidAdapter', function () { 'version': ['8', '0', '0'] }, 'browsers': [ - {'brand': 'Not_A Brand', 'version': ['99', '0', '0', '0']}, - {'brand': 'Google Chrome', 'version': ['109', '0', '5414', '119']}, - {'brand': 'Chromium', 'version': ['109', '0', '5414', '119']} + { 'brand': 'Not_A Brand', 'version': ['99', '0', '0', '0'] }, + { 'brand': 'Google Chrome', 'version': ['109', '0', '5414', '119'] }, + { 'brand': 'Chromium', 'version': ['109', '0', '5414', '119'] } ], 'mobile': 1, 'model': 'SM-G955U', @@ -404,9 +403,9 @@ describe('IlluminBidAdapter', function () { 'version': ['8', '0', '0'] }, 'browsers': [ - {'brand': 'Not_A Brand', 'version': ['99', '0', '0', '0']}, - {'brand': 'Google Chrome', 'version': ['109', '0', '5414', '119']}, - {'brand': 'Chromium', 'version': ['109', '0', '5414', '119']} + { 'brand': 'Not_A Brand', 'version': ['99', '0', '0', '0'] }, + { 'brand': 'Google Chrome', 'version': ['109', '0', '5414', '119'] }, + { 'brand': 'Chromium', 'version': ['109', '0', '5414', '119'] } ], 'mobile': 1, 'model': 'SM-G955U', @@ -451,7 +450,7 @@ describe('IlluminBidAdapter', function () { }); describe('getUserSyncs', function () { it('should have valid user sync with iframeEnabled', function () { - const result = adapter.getUserSyncs({iframeEnabled: true}, [SERVER_RESPONSE]); + const result = adapter.getUserSyncs({ iframeEnabled: true }, [SERVER_RESPONSE]); expect(result).to.deep.equal([{ type: 'iframe', @@ -460,7 +459,7 @@ describe('IlluminBidAdapter', function () { }); it('should have valid user sync with cid on response', function () { - const result = adapter.getUserSyncs({iframeEnabled: true}, [SERVER_RESPONSE]); + const result = adapter.getUserSyncs({ iframeEnabled: true }, [SERVER_RESPONSE]); expect(result).to.deep.equal([{ type: 'iframe', url: 'https://sync.illumin.com/api/sync/iframe/?cid=testcid123&gdpr=0&gdpr_consent=&us_privacy=&coppa=0' @@ -468,7 +467,7 @@ describe('IlluminBidAdapter', function () { }); it('should have valid user sync with pixelEnabled', function () { - const result = adapter.getUserSyncs({pixelEnabled: true}, [SERVER_RESPONSE]); + const result = adapter.getUserSyncs({ pixelEnabled: true }, [SERVER_RESPONSE]); expect(result).to.deep.equal([{ 'url': 'https://sync.illumin.com/api/sync/image/?cid=testcid123&gdpr=0&gdpr_consent=&us_privacy=&coppa=0', @@ -480,7 +479,7 @@ describe('IlluminBidAdapter', function () { config.setConfig({ coppa: 1 }); - const result = adapter.getUserSyncs({iframeEnabled: true}, [SERVER_RESPONSE]); + const result = adapter.getUserSyncs({ iframeEnabled: true }, [SERVER_RESPONSE]); expect(result).to.deep.equal([{ type: 'iframe', url: 'https://sync.illumin.com/api/sync/iframe/?cid=testcid123&gdpr=0&gdpr_consent=&us_privacy=&coppa=1' @@ -495,12 +494,12 @@ describe('IlluminBidAdapter', function () { }); it('should return empty array when there is no ad', function () { - const responses = adapter.interpretResponse({price: 1, ad: ''}); + const responses = adapter.interpretResponse({ price: 1, ad: '' }); expect(responses).to.be.empty; }); it('should return empty array when there is no price', function () { - const responses = adapter.interpretResponse({price: null, ad: 'great ad'}); + const responses = adapter.interpretResponse({ price: null, ad: 'great ad' }); expect(responses).to.be.empty; }); @@ -573,9 +572,9 @@ describe('IlluminBidAdapter', function () { const userId = (function () { switch (idSystemProvider) { case 'lipb': - return {lipbid: id}; + return { lipbid: id }; case 'id5id': - return {uid: id}; + return { uid: id }; default: return id; } @@ -596,7 +595,7 @@ describe('IlluminBidAdapter', function () { bid.userIdAsEids = [ { "source": "audigent.com", - "uids": [{"id": "fakeidi6j6dlc6e"}] + "uids": [{ "id": "fakeidi6j6dlc6e" }] } ] const requests = adapter.buildRequests([bid], BIDDER_REQUEST); @@ -607,11 +606,11 @@ describe('IlluminBidAdapter', function () { bid.userIdAsEids = [ { "source": "audigent.com", - "uids": [{"id": "fakeidi6j6dlc6e"}] + "uids": [{ "id": "fakeidi6j6dlc6e" }] }, { "source": "rwdcntrl.net", - "uids": [{"id": "fakeid6f35197d5c", "atype": 1}] + "uids": [{ "id": "fakeid6f35197d5c", "atype": 1 }] } ] const requests = adapter.buildRequests([bid], BIDDER_REQUEST); @@ -626,7 +625,7 @@ describe('IlluminBidAdapter', function () { eids: [ { "source": "pubcid.org", - "uids": [{"id": "fakeid8888dlc6e"}] + "uids": [{ "id": "fakeid8888dlc6e" }] } ] } @@ -641,11 +640,11 @@ describe('IlluminBidAdapter', function () { eids: [ { "source": "pubcid.org", - "uids": [{"id": "fakeid8888dlc6e"}] + "uids": [{ "id": "fakeid8888dlc6e" }] }, { "source": "adserver.org", - "uids": [{"id": "fakeid495ff1"}] + "uids": [{ "id": "fakeid495ff1" }] } ] } @@ -658,18 +657,18 @@ describe('IlluminBidAdapter', function () { describe('alternate param names extractors', function () { it('should return undefined when param not supported', function () { - const cid = extractCID({'c_id': '1'}); - const pid = extractPID({'p_id': '1'}); - const subDomain = extractSubDomain({'sub_domain': 'prebid'}); + const cid = extractCID({ 'c_id': '1' }); + const pid = extractPID({ 'p_id': '1' }); + const subDomain = extractSubDomain({ 'sub_domain': 'prebid' }); expect(cid).to.be.undefined; expect(pid).to.be.undefined; expect(subDomain).to.be.undefined; }); it('should return value when param supported', function () { - const cid = extractCID({'cId': '1'}); - const pid = extractPID({'pId': '2'}); - const subDomain = extractSubDomain({'subDomain': 'prebid'}); + const cid = extractCID({ 'cId': '1' }); + const pid = extractPID({ 'pId': '2' }); + const subDomain = extractSubDomain({ 'subDomain': 'prebid' }); expect(cid).to.be.equal('1'); expect(pid).to.be.equal('2'); expect(subDomain).to.be.equal('prebid'); @@ -729,7 +728,7 @@ describe('IlluminBidAdapter', function () { now }); setStorageItem(storage, 'myKey', 2020); - const {value, created} = getStorageItem(storage, 'myKey'); + const { value, created } = getStorageItem(storage, 'myKey'); expect(created).to.be.equal(now); expect(value).to.be.equal(2020); expect(typeof value).to.be.equal('number'); @@ -745,8 +744,8 @@ describe('IlluminBidAdapter', function () { }); it('should parse JSON value', function () { - const data = JSON.stringify({event: 'send'}); - const {event} = tryParseJSON(data); + const data = JSON.stringify({ event: 'send' }); + const { event } = tryParseJSON(data); expect(event).to.be.equal('send'); }); diff --git a/test/spec/modules/imRtdProvider_spec.js b/test/spec/modules/imRtdProvider_spec.js index b06afc5a85b..511beedc15d 100644 --- a/test/spec/modules/imRtdProvider_spec.js +++ b/test/spec/modules/imRtdProvider_spec.js @@ -60,12 +60,12 @@ describe('imRtdProvider', function () { }); it(`should return bid with correct key data: ${bidderName}`, function () { - const bid = {bidder: bidderName}; - expect(getBidderFunction(bidderName)(bid, {'im_segments': ['12345', '67890']}, {params: {}})).to.equal(bid); + const bid = { bidder: bidderName }; + expect(getBidderFunction(bidderName)(bid, { 'im_segments': ['12345', '67890'] }, { params: {} })).to.equal(bid); }); it(`should return bid without data: ${bidderName}`, function () { - const bid = {bidder: bidderName}; - expect(getBidderFunction(bidderName)(bid, '', {params: {}})).to.equal(bid); + const bid = { bidder: bidderName }; + expect(getBidderFunction(bidderName)(bid, '', { params: {} })).to.equal(bid); }); }); it(`should return null with unexpected bidder`, function () { @@ -73,8 +73,8 @@ describe('imRtdProvider', function () { }); describe('fluct bidder function', function () { it('should return a bid w/o im_segments if not any exists', function () { - const bid = {bidder: 'fluct'}; - expect(getBidderFunction('fluct')(bid, '', {params: {}})).to.eql(bid); + const bid = { bidder: 'fluct' }; + expect(getBidderFunction('fluct')(bid, '', { params: {} })).to.eql(bid); }); it('should return a bid w/ im_segments if any exists', function () { const bid = { @@ -87,8 +87,8 @@ describe('imRtdProvider', function () { }; expect(getBidderFunction('fluct')( bid, - {im_segments: ['12345', '67890', '09876']}, - {params: {maxSegments: 2}} + { im_segments: ['12345', '67890', '09876'] }, + { params: { maxSegments: 2 } } )) .to.eql( { @@ -139,7 +139,7 @@ describe('imRtdProvider', function () { describe('setRealTimeData', function () { it('should return true when empty params', function () { - expect(setRealTimeData({adUnits: []}, {params: {}}, {im_segments: []})).to.equal(undefined) + expect(setRealTimeData({ adUnits: [] }, { params: {} }, { im_segments: [] })).to.equal(undefined) }); it('should return true when overwrites and bid params', function () { const config = { @@ -149,7 +149,7 @@ describe('imRtdProvider', function () { } } }; - expect(setRealTimeData(testReqBidsConfigObj, config, {im_segments: []})).to.equal(undefined) + expect(setRealTimeData(testReqBidsConfigObj, config, { im_segments: [] })).to.equal(undefined) }); }) @@ -197,13 +197,13 @@ describe('imRtdProvider', function () { it('should return "undefined" success', function () { const res = getApiCallback(testReqBidsConfigObj, false, moduleConfig); const successResponse = '{"uid": "testid", "segments": "testsegment", "vid": "testvid"}'; - expect(res.success(successResponse, {status: 200})).to.equal(undefined); + expect(res.success(successResponse, { status: 200 })).to.equal(undefined); expect(res.error()).to.equal(undefined); }); it('should return "undefined" catch error response', function () { const res = getApiCallback(testReqBidsConfigObj, false, moduleConfig); - expect(res.success('error response', {status: 400})).to.equal(undefined); + expect(res.success('error response', { status: 400 })).to.equal(undefined); }); }) }) diff --git a/test/spec/modules/impactifyBidAdapter_spec.js b/test/spec/modules/impactifyBidAdapter_spec.js index 464bf4f5972..641a58ae9d8 100644 --- a/test/spec/modules/impactifyBidAdapter_spec.js +++ b/test/spec/modules/impactifyBidAdapter_spec.js @@ -2,7 +2,7 @@ import { expect } from 'chai'; import { spec, STORAGE, STORAGE_KEY } from 'modules/impactifyBidAdapter.js'; import * as utils from 'src/utils.js'; import sinon from 'sinon'; -import {getGlobal} from '../../../src/prebidGlobal.js'; +import { getGlobal } from '../../../src/prebidGlobal.js'; const BIDDER_CODE = 'impactify'; const BIDDER_ALIAS = ['imp']; diff --git a/test/spec/modules/improvedigitalBidAdapter_spec.js b/test/spec/modules/improvedigitalBidAdapter_spec.js index 4d5038b795e..752b444a6f2 100644 --- a/test/spec/modules/improvedigitalBidAdapter_spec.js +++ b/test/spec/modules/improvedigitalBidAdapter_spec.js @@ -1,9 +1,9 @@ -import {expect} from 'chai'; -import {CONVERTER, spec} from 'modules/improvedigitalBidAdapter.js'; -import {config} from 'src/config.js'; -import {deepClone} from 'src/utils.js'; -import {BANNER, NATIVE, VIDEO} from '../../../src/mediaTypes.js'; -import {deepSetValue} from '../../../src/utils.js'; +import { expect } from 'chai'; +import { CONVERTER, spec } from 'modules/improvedigitalBidAdapter.js'; +import { config } from 'src/config.js'; +import { deepClone } from 'src/utils.js'; +import { BANNER, NATIVE, VIDEO } from '../../../src/mediaTypes.js'; +import { deepSetValue } from '../../../src/utils.js'; // load modules that register ORTB processors import 'src/prebid.js'; import 'modules/currency.js'; @@ -12,9 +12,9 @@ import 'modules/multibid/index.js'; import 'modules/priceFloors.js'; import 'modules/consentManagementTcf.js'; import 'modules/consentManagementUsp.js'; -import {decorateAdUnitsWithNativeParams} from '../../../src/native.js'; -import {hook} from '../../../src/hook.js'; -import {addFPDToBidderRequest} from '../../helpers/fpd.js'; +import { decorateAdUnitsWithNativeParams } from '../../../src/native.js'; +import { hook } from '../../../src/hook.js'; +import { addFPDToBidderRequest } from '../../helpers/fpd.js'; import * as prebidGlobal from 'src/prebidGlobal.js'; describe('Improve Digital Adapter Tests', function () { @@ -87,8 +87,8 @@ describe('Improve Digital Adapter Tests', function () { const nativeBidRequest = deepClone(simpleBidRequest); nativeBidRequest.mediaTypes = { native: {} }; nativeBidRequest.nativeParams = { - title: {required: true}, - body: {required: true} + title: { required: true }, + body: { required: true } }; const multiFormatBidRequest = deepClone(simpleBidRequest); @@ -233,7 +233,7 @@ describe('Improve Digital Adapter Tests', function () { expect(payload.tmax).not.to.exist; expect(payload.regs).to.not.exist; expect(payload.schain).to.not.exist; - sinon.assert.match(payload.source, {tid: 'mock-tid'}) + sinon.assert.match(payload.source, { tid: 'mock-tid' }) expect(payload.device).to.be.an('object'); expect(payload.user).to.not.exist; sinon.assert.match(payload.imp, [ @@ -247,8 +247,8 @@ describe('Improve Digital Adapter Tests', function () { }, banner: { format: [ - {w: 300, h: 250}, - {w: 160, h: 600}, + { w: 300, h: 250 }, + { w: 160, h: 600 }, ] } }) @@ -283,8 +283,8 @@ describe('Improve Digital Adapter Tests', function () { }), banner: { format: [ - {w: 300, h: 250}, - {w: 160, h: 600}, + { w: 300, h: 250 }, + { w: 160, h: 600 }, ] } }) @@ -298,10 +298,10 @@ describe('Improve Digital Adapter Tests', function () { const nativeReq = JSON.parse(payload.imp[0].native.request); sinon.assert.match(nativeReq, { eventtrackers: [ - {event: 1, methods: [1, 2]}, + { event: 1, methods: [1, 2] }, ], 'assets': [ - sinon.match({'required': 1, 'data': {'type': 2}}) + sinon.match({ 'required': 1, 'data': { 'type': 2 } }) ] }); } @@ -313,11 +313,11 @@ describe('Improve Digital Adapter Tests', function () { const nativeReq = JSON.parse(payload.imp[0].native.request); sinon.assert.match(nativeReq, { eventtrackers: [ - {event: 1, methods: [1, 2]}, + { event: 1, methods: [1, 2] }, ], assets: [ - sinon.match({required: 1, title: {len: 140}}), - sinon.match({required: 1, data: {type: 2}}) + sinon.match({ required: 1, title: { len: 140 } }), + sinon.match({ required: 1, data: { type: 2 } }) ] }) }); @@ -330,7 +330,7 @@ describe('Improve Digital Adapter Tests', function () { }); it('should not make native request when no assets', function () { - const requests = updateNativeParams([{...nativeBidRequest, nativeParams: {}}]) + const requests = updateNativeParams([{ ...nativeBidRequest, nativeParams: {} }]) const payload = JSON.parse(spec.buildRequests(requests, {})[0].data); expect(payload.imp[0].native).to.not.exist; }); @@ -349,7 +349,7 @@ describe('Improve Digital Adapter Tests', function () { }); it('should add currency', function () { - config.setConfig({currency: {adServerCurrency: 'JPY'}}); + config.setConfig({ currency: { adServerCurrency: 'JPY' } }); try { const bidRequest = Object.assign({}, simpleBidRequest); const payload = JSON.parse(spec.buildRequests([bidRequest], bidderRequest)[0].data); @@ -430,7 +430,7 @@ describe('Improve Digital Adapter Tests', function () { it('should add CCPA consent string', async function () { const bidRequest = Object.assign({}, simpleBidRequest); - const request = spec.buildRequests([bidRequest], await addFPDToBidderRequest({...bidderRequest, ...{ uspConsent: '1YYY' }})); + const request = spec.buildRequests([bidRequest], await addFPDToBidderRequest({ ...bidderRequest, ...{ uspConsent: '1YYY' } })); const payload = JSON.parse(request[0].data); expect(payload.regs.ext.us_privacy).to.equal('1YYY'); }); @@ -531,12 +531,14 @@ describe('Improve Digital Adapter Tests', function () { bidRequest.params.video = videoParams; const request = spec.buildRequests([bidRequest], {})[0]; const payload = JSON.parse(request.data); - expect(payload.imp[0].video).to.deep.equal({...{ - mimes: ['video/mp4'], - w: bidRequest.mediaTypes.video.playerSize[0], - h: bidRequest.mediaTypes.video.playerSize[1], - }, - ...videoParams}); + expect(payload.imp[0].video).to.deep.equal({ + ...{ + mimes: ['video/mp4'], + w: bidRequest.mediaTypes.video.playerSize[0], + h: bidRequest.mediaTypes.video.playerSize[1], + }, + ...videoParams + }); }); // it('should set video params for multi-format', function() { @@ -560,7 +562,7 @@ describe('Improve Digital Adapter Tests', function () { // Add schain to both locations in the bid bidRequest.ortb2 = { source: { - ext: {schain: schain} + ext: { schain: schain } } }; @@ -569,7 +571,7 @@ describe('Improve Digital Adapter Tests', function () { ...bidderRequestReferrer, ortb2: { source: { - ext: {schain: schain} + ext: { schain: schain } } } }; @@ -589,16 +591,20 @@ describe('Improve Digital Adapter Tests', function () { }] } ]; - const expectedUserObject = { ext: { eids: [{ - source: 'id5-sync.com', - uids: [{ - atype: 1, - id: '1111' - }] - }]}}; + const expectedUserObject = { + ext: { + eids: [{ + source: 'id5-sync.com', + uids: [{ + atype: 1, + id: '1111' + }] + }] + } + }; const request = spec.buildRequests([simpleBidRequest], { ...bidderRequestReferrer, - ortb2: {user: {ext: {eids: eids}}} + ortb2: { user: { ext: { eids: eids } } } })[0]; const payload = JSON.parse(request.data); expect(payload.user).to.deep.equal(expectedUserObject); @@ -616,7 +622,7 @@ describe('Improve Digital Adapter Tests', function () { it('should return one request in a single request mode', function () { getConfigStub = sinon.stub(config, 'getConfig'); getConfigStub.withArgs('improvedigital.singleRequest').returns(true); - const requests = spec.buildRequests([ simpleBidRequest, instreamBidRequest ], bidderRequest); + const requests = spec.buildRequests([simpleBidRequest, instreamBidRequest], bidderRequest); expect(requests).to.be.an('array'); expect(requests.length).to.equal(1); expect(requests[0].url).to.equal(formatPublisherUrl(AD_SERVER_BASE_URL, 1234)); @@ -629,7 +635,7 @@ describe('Improve Digital Adapter Tests', function () { it('should create one request per endpoint in a single request mode', function () { getConfigStub = sinon.stub(config, 'getConfig'); getConfigStub.withArgs('improvedigital.singleRequest').returns(true); - const requests = spec.buildRequests([ extendBidRequest, simpleBidRequest, instreamBidRequest ], bidderRequest); + const requests = spec.buildRequests([extendBidRequest, simpleBidRequest, instreamBidRequest], bidderRequest); expect(requests).to.be.an('array'); expect(requests.length).to.equal(2); expect(requests[0].url).to.equal(EXTEND_URL); @@ -669,8 +675,8 @@ describe('Improve Digital Adapter Tests', function () { }); it('should not set site when app is defined in FPD', function () { - const ortb2 = {app: {content: 'XYZ'}}; - const request = spec.buildRequests([simpleBidRequest], {...bidderRequest, ortb2})[0]; + const ortb2 = { app: { content: 'XYZ' } }; + const request = spec.buildRequests([simpleBidRequest], { ...bidderRequest, ortb2 })[0]; const payload = JSON.parse(request.data); expect(payload.site).does.not.exist; expect(payload.app).does.exist; @@ -684,8 +690,8 @@ describe('Improve Digital Adapter Tests', function () { expect(payload.site.page).does.exist.and.equal('https://blah.com/test.html'); expect(payload.site.domain).does.exist.and.equal('blah.com'); - const ortb2 = {site: {content: 'ZZZ'}}; - request = spec.buildRequests([simpleBidRequest], await addFPDToBidderRequest({...bidderRequestReferrer, ortb2}))[0]; + const ortb2 = { site: { content: 'ZZZ' } }; + request = spec.buildRequests([simpleBidRequest], await addFPDToBidderRequest({ ...bidderRequestReferrer, ortb2 }))[0]; payload = JSON.parse(request.data); expect(payload.site.content).does.exist.and.equal('ZZZ'); expect(payload.site.page).does.exist.and.equal('https://blah.com/test.html'); @@ -704,7 +710,7 @@ describe('Improve Digital Adapter Tests', function () { it('should set extend params when extend mode enabled from global configuration', function () { getConfigStub = sinon.stub(config, 'getConfig'); const bannerRequest = deepClone(simpleBidRequest); - const keyValues = { testKey: [ 'testValue' ] }; + const keyValues = { testKey: ['testValue'] }; bannerRequest.params.keyValues = keyValues; getConfigStub.withArgs('improvedigital.extend').returns(true); @@ -1103,7 +1109,7 @@ describe('Improve Digital Adapter Tests', function () { function makeRequest(bidderRequest) { return { - ortbRequest: CONVERTER.toORTB({bidderRequest}) + ortbRequest: CONVERTER.toORTB({ bidderRequest }) } } @@ -1272,7 +1278,7 @@ describe('Improve Digital Adapter Tests', function () { }); describe('getUserSyncs', function () { - const serverResponses = [ serverResponse, serverResponseTwoBids ]; + const serverResponses = [serverResponse, serverResponseTwoBids]; const pixelSyncs = [ { type: 'image', url: 'https://link1' }, { type: 'image', url: 'https://link2' }, @@ -1373,7 +1379,7 @@ describe('Improve Digital Adapter Tests', function () { getConfigStub.withArgs('improvedigital.extend').returns(true); spec.buildRequests([simpleBidRequest], {}); const rawResponse = deepClone(serverResponse) - deepSetValue(rawResponse, 'body.ext.responsetimemillis', {a: 1, b: 1, c: 1, d: 1, e: 1}) + deepSetValue(rawResponse, 'body.ext.responsetimemillis', { a: 1, b: 1, c: 1, d: 1, e: 1 }) const syncs = spec.getUserSyncs({ iframeEnabled: true, pixelEnabled: true }, [rawResponse]); const url = basicIframeSyncUrl + '&pbs=1' + '&bidders=a,b,c,d,e' expect(syncs).to.deep.equal([{ type: 'iframe', url }]); diff --git a/test/spec/modules/imuIdSystem_spec.js b/test/spec/modules/imuIdSystem_spec.js index b3cc5ba73ae..527e31a87ea 100644 --- a/test/spec/modules/imuIdSystem_spec.js +++ b/test/spec/modules/imuIdSystem_spec.js @@ -13,9 +13,9 @@ import { } from 'modules/imuIdSystem.js'; import * as utils from 'src/utils.js'; -import {attachIdSystem} from '../../../modules/userId/index.js'; -import {createEidsArray} from '../../../modules/userId/eids.js'; -import {expect} from 'chai/index.mjs'; +import { attachIdSystem } from '../../../modules/userId/index.js'; +import { createEidsArray } from '../../../modules/userId/eids.js'; +import { expect } from 'chai/index.mjs'; describe('imuId module', function () { // let setLocalStorageStub; @@ -54,10 +54,12 @@ describe('imuId module', function () { getLocalStorageStub.withArgs(storageKey).returns('testUid'); getLocalStorageStub.withArgs(storagePpKey).returns('testPpid'); const id = imuIdSubmodule.getId(configParamTestCase); - expect(id).to.be.deep.equal({id: { - imuid: 'testUid', - imppid: 'testPpid' - }}); + expect(id).to.be.deep.equal({ + id: { + imuid: 'testUid', + imppid: 'testPpid' + } + }); }); storageTestCasesForEmpty.forEach(testCase => it('should return the callback when it not exists in local storages', function () { diff --git a/test/spec/modules/inmobiBidAdapter_spec.js b/test/spec/modules/inmobiBidAdapter_spec.js index 9b22cd173d4..a9dc0250fdb 100644 --- a/test/spec/modules/inmobiBidAdapter_spec.js +++ b/test/spec/modules/inmobiBidAdapter_spec.js @@ -571,7 +571,7 @@ describe('The inmobi bidding adapter', function () { } } }; - const ortbRequest = spec.buildRequests(bidRequests, await addFPDToBidderRequest({...bidderRequest, ortb2})).data; + const ortbRequest = spec.buildRequests(bidRequests, await addFPDToBidderRequest({ ...bidderRequest, ortb2 })).data; expect(ortbRequest.site.domain).to.equal('raapchikgames.com'); expect(ortbRequest.site.publisher.domain).to.equal('inmobi'); expect(ortbRequest.site.page).to.equal('https://raapchikgames.com'); @@ -620,7 +620,7 @@ describe('The inmobi bidding adapter', function () { } } }; - const ortbRequest = spec.buildRequests(bidRequests, await addFPDToBidderRequest({...bidderRequest, ortb2})).data; + const ortbRequest = spec.buildRequests(bidRequests, await addFPDToBidderRequest({ ...bidderRequest, ortb2 })).data; expect(ortbRequest.device.dnt).to.equal(0); expect(ortbRequest.device.lmt).to.equal(1); expect(ortbRequest.device.js).to.equal(1); @@ -638,7 +638,7 @@ describe('The inmobi bidding adapter', function () { }); it('should properly build a request with source object', async function () { - const expectedSchain = {id: 'prebid'}; + const expectedSchain = { id: 'prebid' }; const ortb2 = { source: { pchain: 'inmobi', @@ -660,7 +660,7 @@ describe('The inmobi bidding adapter', function () { }, }, ]; - const ortbRequest = spec.buildRequests(bidRequests, await addFPDToBidderRequest({...bidderRequest, ortb2})).data; + const ortbRequest = spec.buildRequests(bidRequests, await addFPDToBidderRequest({ ...bidderRequest, ortb2 })).data; expect(ortbRequest.source.ext.schain).to.deep.equal(expectedSchain); expect(ortbRequest.source.pchain).to.equal('inmobi'); }); @@ -749,7 +749,7 @@ describe('The inmobi bidding adapter', function () { } }; - const ortbRequest = spec.buildRequests(bidRequests, await addFPDToBidderRequest({...bidderRequest, ortb2})).data; + const ortbRequest = spec.buildRequests(bidRequests, await addFPDToBidderRequest({ ...bidderRequest, ortb2 })).data; expect(ortbRequest.regs.coppa).to.equal(1); expect(ortbRequest.regs.ext.gpp).to.equal('gpp_consent_string'); expect(ortbRequest.regs.ext.gpp_sid).to.deep.equal([0, 1, 2]); @@ -1167,7 +1167,7 @@ describe('The inmobi bidding adapter', function () { it('should properly build a request when coppa flag is true', async function () { const bidRequests = []; const bidderRequest = {}; - config.setConfig({coppa: true}); + config.setConfig({ coppa: true }); const ortbRequest = spec.buildRequests(bidRequests, await addFPDToBidderRequest(bidderRequest)).data; expect(ortbRequest.regs.coppa).to.equal(1); }); @@ -1175,7 +1175,7 @@ describe('The inmobi bidding adapter', function () { it('should properly build a request when coppa flag is false', async function () { const bidRequests = []; const bidderRequest = {}; - config.setConfig({coppa: false}); + config.setConfig({ coppa: false }); const ortbRequest = spec.buildRequests(bidRequests, await addFPDToBidderRequest(bidderRequest)).data; expect(ortbRequest.regs.coppa).to.equal(0); }); @@ -1223,7 +1223,7 @@ describe('The inmobi bidding adapter', function () { plc: '123a' }, getFloor: inputParams => { - return {currency: 'USD', floor: 1.23, size: '*', mediaType: '*'}; + return { currency: 'USD', floor: 1.23, size: '*', mediaType: '*' }; } } ]; @@ -1271,7 +1271,7 @@ describe('The inmobi bidding adapter', function () { plc: '123a' }, getFloor: inputParams => { - return {currency: 'USD', floor: 1.23, size: '*', mediaType: '*'}; + return { currency: 'USD', floor: 1.23, size: '*', mediaType: '*' }; } } ]; @@ -1326,7 +1326,7 @@ describe('The inmobi bidding adapter', function () { plc: '12456' }, getFloor: inputParams => { - return {currency: 'USD', floor: 1.23, size: '*', mediaType: '*'}; + return { currency: 'USD', floor: 1.23, size: '*', mediaType: '*' }; } }, ]; @@ -1652,9 +1652,9 @@ describe('The inmobi bidding adapter', function () { it('should return an empty array when there is no bid response', async function () { const bidRequests = []; - const response = {seatbid: []}; + const response = { seatbid: [] }; const request = spec.buildRequests(bidRequests, await addFPDToBidderRequest(bidderRequest)); - const bids = spec.interpretResponse({body: response}, request); + const bids = spec.interpretResponse({ body: response }, request); expect(bids).to.have.lengthOf(0); }); @@ -1673,7 +1673,7 @@ describe('The inmobi bidding adapter', function () { }]; const response = mockResponse('bidId', 1); const request = spec.buildRequests(bidRequests, await addFPDToBidderRequest(bidderRequest)); - const bids = spec.interpretResponse({body: response}, request); + const bids = spec.interpretResponse({ body: response }, request); expect(bids).to.have.length(1); expect(bids[0].currency).to.deep.equal('USD'); expect(bids[0].mediaType).to.deep.equal('banner'); @@ -1719,7 +1719,7 @@ describe('The inmobi bidding adapter', function () { }]; const response = mockResponse('bidId2', 1); const request = spec.buildRequests(bidRequests, await addFPDToBidderRequest(bidderRequest)); - const bids = spec.interpretResponse({body: response}, request); + const bids = spec.interpretResponse({ body: response }, request); expect(bids[0].currency).to.deep.equal('USD'); expect(bids[0].mediaType).to.deep.equal('banner'); expect(bids[0].requestId).to.deep.equal('bidId2'); @@ -1754,7 +1754,7 @@ describe('The inmobi bidding adapter', function () { }]; const response = mockResponse('bidId', 2); const request = spec.buildRequests(bidRequests, await addFPDToBidderRequest(bidderRequest)); - const bids = spec.interpretResponse({body: response}, request); + const bids = spec.interpretResponse({ body: response }, request); expect(bids).to.have.lengthOf(1); expect(bids[0].mediaType).to.equal(VIDEO); expect(bids[0].currency).to.deep.equal('USD'); @@ -1793,7 +1793,7 @@ describe('The inmobi bidding adapter', function () { }]; const response = mockResponse('bidId', 2); const request = spec.buildRequests(bidRequests, await addFPDToBidderRequest(bidderRequest)); - const bids = spec.interpretResponse({body: response}, request); + const bids = spec.interpretResponse({ body: response }, request); expect(bids).to.have.lengthOf(1); expect(bids[0].mediaType).to.equal(VIDEO); expect(bids[0].currency).to.deep.equal('USD'); @@ -1843,7 +1843,7 @@ describe('The inmobi bidding adapter', function () { }]; const response = mockResponse('bidId', 2); const request = spec.buildRequests(bidRequests, await addFPDToBidderRequest(bidderRequest)); - const bids = spec.interpretResponse({body: response}, request); + const bids = spec.interpretResponse({ body: response }, request); expect(bids).to.have.lengthOf(1); expect(bids[0].mediaType).to.equal(VIDEO); expect(bids[0].currency).to.deep.equal('USD'); @@ -1884,7 +1884,7 @@ describe('The inmobi bidding adapter', function () { const response = mockResponseNative('bidId', 4); const expectedAdmNativeOrtb = JSON.parse(response.seatbid[0].bid[0].adm).native; const request = spec.buildRequests(bidRequests, await addFPDToBidderRequest(bidderRequest)); - const bids = spec.interpretResponse({body: response}, request); + const bids = spec.interpretResponse({ body: response }, request); expect(bids).to.have.lengthOf(1); expect(bids[0].mediaType).to.equal(NATIVE); @@ -1939,7 +1939,7 @@ describe('The inmobi bidding adapter', function () { const response = mockResponseNative('bidId', 4); const expectedAdmNativeOrtb = JSON.parse(response.seatbid[0].bid[0].adm).native; const request = spec.buildRequests(bidRequests, await addFPDToBidderRequest(bidderRequest)); - const bids = spec.interpretResponse({body: response}, request); + const bids = spec.interpretResponse({ body: response }, request); expect(bids).to.have.lengthOf(1); expect(bids[0].mediaType).to.equal(NATIVE); // testing diff --git a/test/spec/modules/insticatorBidAdapter_spec.js b/test/spec/modules/insticatorBidAdapter_spec.js index cee99ca8c38..520630a6e82 100644 --- a/test/spec/modules/insticatorBidAdapter_spec.js +++ b/test/spec/modules/insticatorBidAdapter_spec.js @@ -2,7 +2,7 @@ import { expect } from 'chai'; import { spec, storage } from '../../../modules/insticatorBidAdapter.js'; import { newBidder } from 'src/adapters/bidderFactory.js' import { getWinDimensions } from '../../../src/utils.js'; -import {getGlobal} from '../../../src/prebidGlobal.js'; +import { getGlobal } from '../../../src/prebidGlobal.js'; const USER_ID_KEY = 'hb_insticator_uid'; const USER_ID_DUMMY_VALUE = '74f78609-a92d-4cf1-869f-1b244bbfb5d2'; @@ -520,7 +520,7 @@ describe('InsticatorBidAdapter', function () { } } } - const requests = spec.buildRequests([bidRequest], {...bidRequestWithDsa}); + const requests = spec.buildRequests([bidRequest], { ...bidRequestWithDsa }); const data = JSON.parse(requests[0].data); expect(data.regs).to.be.an('object'); expect(data.regs.ext).to.be.an('object'); @@ -619,7 +619,7 @@ describe('InsticatorBidAdapter', function () { const data = JSON.parse(requests[0].data); expect(data.imp[0].bidfloor).to.equal(1); - tempBiddRequest.mediaTypes.banner.format = [ { w: 300, h: 600 }, + tempBiddRequest.mediaTypes.banner.format = [{ w: 300, h: 600 }, ]; const request2 = spec.buildRequests([tempBiddRequest], bidderRequest); const data2 = JSON.parse(request2[0].data); @@ -812,6 +812,136 @@ describe('InsticatorBidAdapter', function () { const data = JSON.parse(requests[0].data); expect(data.site.publisher).to.not.an('object'); }); + + it('should include publisherId as query parameter in endpoint URL', function () { + const tempBiddRequest = { + ...bidRequest, + } + tempBiddRequest.params = { + ...tempBiddRequest.params, + publisherId: '86dd03a1-053f-4e3e-90e7-389070a0c62c' + } + const requests = spec.buildRequests([tempBiddRequest], bidderRequest); + expect(requests[0].url).to.include('publisherId=86dd03a1-053f-4e3e-90e7-389070a0c62c'); + }); + + it('should not include publisherId query param if publisherId is not present', function () { + const tempBiddRequest = { + ...bidRequest, + } + // Ensure no publisherId in params + delete tempBiddRequest.params.publisherId; + const requests = spec.buildRequests([tempBiddRequest], bidderRequest); + expect(requests[0].url).to.not.include('publisherId'); + }); + + it('should not include publisherId query param if publisherId is empty string', function () { + const tempBiddRequest = { + ...bidRequest, + } + tempBiddRequest.params = { + ...tempBiddRequest.params, + publisherId: '' + } + const requests = spec.buildRequests([tempBiddRequest], bidderRequest); + expect(requests[0].url).to.not.include('publisherId'); + }); + + it('should include publisherId query param with custom endpoint URL', function () { + const tempBiddRequest = { + ...bidRequest, + } + tempBiddRequest.params = { + ...tempBiddRequest.params, + publisherId: 'test-publisher-123', + bid_endpoint_request_url: 'https://custom.endpoint.com/v1/bid' + } + const requests = spec.buildRequests([tempBiddRequest], bidderRequest); + expect(requests[0].url).to.equal('https://custom.endpoint.com/v1/bid?publisherId=test-publisher-123'); + }); + + // ORTB 2.6 Ad Pod video params tests + describe('Ad Pod video params', function () { + it('should include Ad Pod params when present in video mediaType', function () { + const adPodBidRequest = { + ...bidRequest, + mediaTypes: { + video: { + mimes: ['video/mp4', 'video/mpeg'], + w: 640, + h: 480, + podid: 'pod-123', + podseq: 1, + poddur: 300, + slotinpod: 1, + mincpmpersec: 0.02, + maxseq: 5, + rqddurs: [15, 30, 60], + }, + }, + }; + const requests = spec.buildRequests([adPodBidRequest], bidderRequest); + const data = JSON.parse(requests[0].data); + + expect(data.imp[0].video).to.have.property('podid', 'pod-123'); + expect(data.imp[0].video).to.have.property('podseq', 1); + expect(data.imp[0].video).to.have.property('poddur', 300); + expect(data.imp[0].video).to.have.property('slotinpod', 1); + expect(data.imp[0].video).to.have.property('mincpmpersec', 0.02); + expect(data.imp[0].video).to.have.property('maxseq', 5); + expect(data.imp[0].video).to.have.property('rqddurs').that.deep.equals([15, 30, 60]); + }); + + it('should not include invalid ORTB 2.6 video params', function () { + const adPodBidRequest = { + ...bidRequest, + mediaTypes: { + video: { + mimes: ['video/mp4'], + w: 640, + h: 480, + podid: '', // invalid - empty string + podseq: -1, // invalid - negative + poddur: 0, // invalid - zero + slotinpod: 5, // invalid - not in [-1, 0, 1, 2] + mincpmpersec: -0.5, // invalid - negative + maxseq: 0, // invalid - zero + rqddurs: [0, -15], // invalid - contains non-positive values + }, + }, + }; + const requests = spec.buildRequests([adPodBidRequest], bidderRequest); + const data = JSON.parse(requests[0].data); + + expect(data.imp[0].video).to.not.have.property('podid'); + expect(data.imp[0].video).to.not.have.property('podseq'); + expect(data.imp[0].video).to.not.have.property('poddur'); + expect(data.imp[0].video).to.not.have.property('slotinpod'); + expect(data.imp[0].video).to.not.have.property('mincpmpersec'); + expect(data.imp[0].video).to.not.have.property('maxseq'); + expect(data.imp[0].video).to.not.have.property('rqddurs'); + }); + + it('should validate slotinpod accepts valid values [-1, 0, 1, 2]', function () { + [-1, 0, 1, 2].forEach(slotValue => { + const adPodBidRequest = { + ...bidRequest, + mediaTypes: { + video: { + mimes: ['video/mp4'], + w: 640, + h: 480, + slotinpod: slotValue, + }, + }, + }; + const requests = spec.buildRequests([adPodBidRequest], bidderRequest); + const data = JSON.parse(requests[0].data); + + expect(data.imp[0].video).to.have.property('slotinpod', slotValue); + }); + }); + }); }); describe('interpretResponse', function () { @@ -929,7 +1059,7 @@ describe('InsticatorBidAdapter', function () { cpm: 0.5, currency: 'USD', netRevenue: true, - ttl: 60, + ttl: 60, // MIN(60, 300) = 60 - bid.exp is upper bound width: 300, height: 200, mediaType: 'banner', @@ -937,7 +1067,8 @@ describe('InsticatorBidAdapter', function () { adUnitCode: 'adunit-code-1', meta: { advertiserDomains: ['test1.com'], - test: 1 + test: 1, + seat: 'some-dsp' } }, { @@ -953,7 +1084,8 @@ describe('InsticatorBidAdapter', function () { meta: { advertiserDomains: [ 'test2.com' - ] + ], + seat: 'some-dsp' }, ad: 'adm2', adUnitCode: 'adunit-code-2', @@ -971,7 +1103,8 @@ describe('InsticatorBidAdapter', function () { meta: { advertiserDomains: [ 'test3.com' - ] + ], + seat: 'some-dsp' }, ad: 'adm3', adUnitCode: 'adunit-code-3', @@ -996,6 +1129,515 @@ describe('InsticatorBidAdapter', function () { delete response.body.seatbid; expect(spec.interpretResponse(response, bidRequests)).to.have.length(0); }); + + it('should return empty response for 204 No Content (undefined body)', function () { + const response = { body: undefined }; + expect(spec.interpretResponse(response, bidRequests)).to.have.length(0); + }); + + it('should return empty response for 204 No Content (null body)', function () { + const response = { body: null }; + expect(spec.interpretResponse(response, bidRequests)).to.have.length(0); + }); + + it('should return empty response for empty object body', function () { + const response = { body: {} }; + expect(spec.interpretResponse(response, bidRequests)).to.have.length(0); + }); + + // ORTB 2.6 Response Fields Tests + describe('ORTB 2.6 response fields', function () { + const ortb26BidRequests = { + method: 'POST', + url: 'https://ex.ingage.tech/v1/openrtb', + options: { + contentType: 'application/json', + withCredentials: true, + }, + data: '', + bidderRequest: { + bidderRequestId: '22edbae2733bf6', + auctionId: '74f78609-a92d-4cf1-869f-1b244bbfb5d2', + timeout: 300, + bids: [ + { + bidder: 'insticator', + params: { + adUnitId: '1a2b3c4d5e6f1a2b3c4d' + }, + adUnitCode: 'adunit-code-1', + sizes: [[300, 250]], + mediaTypes: { + banner: { + sizes: [[300, 250]] + } + }, + bidId: 'bid1', + } + ] + } + }; + + it('should map category (cat) to meta.primaryCatId and meta.secondaryCatIds', function () { + const response = { + body: { + id: '22edbae2733bf6', + seatbid: [{ + seat: 'dsp-1', + bid: [{ + impid: 'bid1', + crid: 'crid1', + price: 1.0, + w: 300, + h: 250, + adm: 'adm1', + cat: ['IAB1', 'IAB2-1', 'IAB3'], + }] + }] + } + }; + const bidResponse = spec.interpretResponse(response, ortb26BidRequests)[0]; + + expect(bidResponse.meta).to.have.property('primaryCatId', 'IAB1'); + expect(bidResponse.meta).to.have.property('secondaryCatIds').that.deep.equals(['IAB2-1', 'IAB3']); + }); + + it('should map single category without secondaryCatIds', function () { + const response = { + body: { + id: '22edbae2733bf6', + seatbid: [{ + seat: 'dsp-1', + bid: [{ + impid: 'bid1', + crid: 'crid1', + price: 1.0, + w: 300, + h: 250, + adm: 'adm1', + cat: ['IAB1'], + }] + }] + } + }; + const bidResponse = spec.interpretResponse(response, ortb26BidRequests)[0]; + + expect(bidResponse.meta).to.have.property('primaryCatId', 'IAB1'); + expect(bidResponse.meta).to.not.have.property('secondaryCatIds'); + }); + + it('should map seat to meta.seat', function () { + const response = { + body: { + id: '22edbae2733bf6', + seatbid: [{ + seat: 'dsp-seat-123', + bid: [{ + impid: 'bid1', + crid: 'crid1', + price: 1.0, + w: 300, + h: 250, + adm: 'adm1', + adomain: ['test.com'], + }] + }] + } + }; + const bidResponse = spec.interpretResponse(response, ortb26BidRequests)[0]; + + expect(bidResponse.meta).to.have.property('seat', 'dsp-seat-123'); + }); + + it('should map creative attributes (attr) to meta.attr', function () { + const response = { + body: { + id: '22edbae2733bf6', + seatbid: [{ + seat: 'dsp-1', + bid: [{ + impid: 'bid1', + crid: 'crid1', + price: 1.0, + w: 300, + h: 250, + adm: 'adm1', + attr: [1, 2, 3], + }] + }] + } + }; + const bidResponse = spec.interpretResponse(response, ortb26BidRequests)[0]; + + expect(bidResponse.meta).to.have.property('attr').that.deep.equals([1, 2, 3]); + }); + + it('should map dealid to bidResponse.dealId', function () { + const response = { + body: { + id: '22edbae2733bf6', + seatbid: [{ + seat: 'dsp-1', + bid: [{ + impid: 'bid1', + crid: 'crid1', + price: 1.0, + w: 300, + h: 250, + adm: 'adm1', + dealid: 'deal-abc-123', + }] + }] + } + }; + const bidResponse = spec.interpretResponse(response, ortb26BidRequests)[0]; + + expect(bidResponse).to.have.property('dealId', 'deal-abc-123'); + }); + + it('should map billing URL (burl) to bidResponse.burl', function () { + const response = { + body: { + id: '22edbae2733bf6', + seatbid: [{ + seat: 'dsp-1', + bid: [{ + impid: 'bid1', + crid: 'crid1', + price: 1.0, + w: 300, + h: 250, + adm: 'adm1', + burl: 'https://billing.example.com/win?price=${AUCTION_PRICE}', + }] + }] + } + }; + const bidResponse = spec.interpretResponse(response, ortb26BidRequests)[0]; + + expect(bidResponse).to.have.property('burl', 'https://billing.example.com/win?price=${AUCTION_PRICE}'); + }); + + it('should map notice URL (nurl) to bidResponse.nurl', function () { + const response = { + body: { + id: '22edbae2733bf6', + seatbid: [{ + seat: 'dsp-1', + bid: [{ + impid: 'bid1', + crid: 'crid1', + price: 1.0, + w: 300, + h: 250, + adm: 'adm1', + nurl: 'https://win.example.com/notify?price=${AUCTION_PRICE}', + }] + }] + } + }; + const bidResponse = spec.interpretResponse(response, ortb26BidRequests)[0]; + + expect(bidResponse).to.have.property('nurl', 'https://win.example.com/notify?price=${AUCTION_PRICE}'); + }); + + it('should map video duration (dur) to bidResponse.video.durationSeconds', function () { + const videoBidRequests = { + ...ortb26BidRequests, + bidderRequest: { + ...ortb26BidRequests.bidderRequest, + bids: [{ + ...ortb26BidRequests.bidderRequest.bids[0], + mediaTypes: { + video: { + mimes: ['video/mp4'], + playerSize: [[640, 480]], + } + } + }] + } + }; + + const response = { + body: { + id: '22edbae2733bf6', + seatbid: [{ + seat: 'dsp-1', + bid: [{ + impid: 'bid1', + crid: 'crid1', + price: 1.0, + w: 640, + h: 480, + adm: '', + dur: 30, + }] + }] + } + }; + const bidResponse = spec.interpretResponse(response, videoBidRequests)[0]; + + expect(bidResponse).to.have.property('video'); + expect(bidResponse.video).to.have.property('durationSeconds', 30); + }); + + it('should set video.durationSeconds and not set video.context for instream video', function () { + const instreamBidRequests = { + ...ortb26BidRequests, + bidderRequest: { + ...ortb26BidRequests.bidderRequest, + bids: [{ + ...ortb26BidRequests.bidderRequest.bids[0], + mediaTypes: { + video: { + mimes: ['video/mp4'], + playerSize: [[640, 480]], + context: 'instream', + } + } + }] + } + }; + + const response = { + body: { + id: '22edbae2733bf6', + seatbid: [{ + seat: 'dsp-1', + bid: [{ + impid: 'bid1', + crid: 'crid1', + price: 1.0, + w: 640, + h: 480, + adm: '', + dur: 30, + }] + }] + } + }; + const bidResponse = spec.interpretResponse(response, instreamBidRequests)[0]; + + expect(bidResponse).to.have.property('video'); + expect(bidResponse.video).to.have.property('durationSeconds', 30); + expect(bidResponse.video).to.not.have.property('context'); + }); + + it('should use MIN of bid.exp and BID_TTL for ttl (bid.exp is upper bound)', function () { + // When bid.exp (60) is less than BID_TTL (300), use 60 + const responseWithLowExp = { + body: { + id: '22edbae2733bf6', + seatbid: [{ + seat: 'dsp-1', + bid: [{ + impid: 'bid1', + crid: 'crid1', + price: 1.0, + w: 300, + h: 250, + adm: 'adm1', + exp: 60, + }] + }] + } + }; + const bidResponseLow = spec.interpretResponse(responseWithLowExp, ortb26BidRequests)[0]; + expect(bidResponseLow.ttl).to.equal(60); // MIN(60, 300) = 60 + + // When bid.exp (600) is greater than BID_TTL (300), use 300 + const responseWithHighExp = { + body: { + id: '22edbae2733bf6', + seatbid: [{ + seat: 'dsp-1', + bid: [{ + impid: 'bid1', + crid: 'crid1', + price: 1.0, + w: 300, + h: 250, + adm: 'adm1', + exp: 600, + }] + }] + } + }; + const bidResponseHigh = spec.interpretResponse(responseWithHighExp, ortb26BidRequests)[0]; + expect(bidResponseHigh.ttl).to.equal(300); // MIN(600, 300) = 300 + }); + + it('should default ttl to BID_TTL when bid.exp is not provided', function () { + const response = { + body: { + id: '22edbae2733bf6', + seatbid: [{ + seat: 'dsp-1', + bid: [{ + impid: 'bid1', + crid: 'crid1', + price: 1.0, + w: 300, + h: 250, + adm: 'adm1', + // no exp field + }] + }] + } + }; + const bidResponse = spec.interpretResponse(response, ortb26BidRequests)[0]; + expect(bidResponse.ttl).to.equal(300); // defaults to configTTL when no bid.exp + }); + + it('should include all ORTB 2.6 fields in a single response', function () { + const response = { + body: { + id: '22edbae2733bf6', + seatbid: [{ + seat: 'full-dsp', + bid: [{ + impid: 'bid1', + crid: 'crid1', + price: 2.5, + w: 300, + h: 250, + adm: 'adm1', + adomain: ['advertiser.com'], + cat: ['IAB1', 'IAB2'], + attr: [1, 2], + dealid: 'premium-deal', + burl: 'https://billing.example.com/win', + exp: 450, + }] + }] + } + }; + const bidResponse = spec.interpretResponse(response, ortb26BidRequests)[0]; + + // Check all ORTB 2.6 fields + expect(bidResponse.meta.advertiserDomains).to.deep.equal(['advertiser.com']); + expect(bidResponse.meta.primaryCatId).to.equal('IAB1'); + expect(bidResponse.meta.secondaryCatIds).to.deep.equal(['IAB2']); + expect(bidResponse.meta.seat).to.equal('full-dsp'); + expect(bidResponse.meta.attr).to.deep.equal([1, 2]); + expect(bidResponse.dealId).to.equal('premium-deal'); + expect(bidResponse.burl).to.equal('https://billing.example.com/win'); + expect(bidResponse.ttl).to.equal(300); // MIN(450, 300) = 300 - bid.exp is upper bound + }); + + // Media Type Detection Tests + describe('media type detection', function () { + it('should detect video using mtype=2 (ORTB 2.6 standard)', function () { + const response = { + body: { + id: '22edbae2733bf6', + seatbid: [{ + seat: 'dsp-1', + bid: [{ + impid: 'bid1', + crid: 'crid1', + price: 1.0, + w: 300, + h: 250, + adm: 'some non-vast content', + mtype: 2, // video + }] + }] + } + }; + const bidResponse = spec.interpretResponse(response, ortb26BidRequests)[0]; + expect(bidResponse.mediaType).to.equal('video'); + }); + + it('should detect banner using mtype=1 (ORTB 2.6 standard)', function () { + const response = { + body: { + id: '22edbae2733bf6', + seatbid: [{ + seat: 'dsp-1', + bid: [{ + impid: 'bid1', + crid: 'crid1', + price: 1.0, + w: 300, + h: 250, + adm: '', // VAST content but mtype says banner + mtype: 1, // banner + }] + }] + } + }; + const bidResponse = spec.interpretResponse(response, ortb26BidRequests)[0]; + expect(bidResponse.mediaType).to.equal('banner'); + }); + + it('should detect video using case-insensitive VAST detection', function () { + const response = { + body: { + id: '22edbae2733bf6', + seatbid: [{ + seat: 'dsp-1', + bid: [{ + impid: 'bid1', + crid: 'crid1', + price: 1.0, + w: 300, + h: 250, + adm: '', // lowercase vast + // no mtype + }] + }] + } + }; + const bidResponse = spec.interpretResponse(response, ortb26BidRequests)[0]; + expect(bidResponse.mediaType).to.equal('video'); + }); + + it('should default to banner when no video signals present', function () { + const response = { + body: { + id: '22edbae2733bf6', + seatbid: [{ + seat: 'dsp-1', + bid: [{ + impid: 'bid1', + crid: 'crid1', + price: 1.0, + w: 300, + h: 250, + adm: '
banner ad
', + // no mtype, no VAST + }] + }] + } + }; + const bidResponse = spec.interpretResponse(response, ortb26BidRequests)[0]; + expect(bidResponse.mediaType).to.equal('banner'); + }); + + it('should detect banner when VAST-like content is inside script tag', function () { + const response = { + body: { + id: '22edbae2733bf6', + seatbid: [{ + seat: 'dsp-1', + bid: [{ + impid: 'bid1', + crid: 'crid1', + price: 1.0, + w: 300, + h: 250, + adm: '
banner
', + // no mtype + }] + }] + } + }; + const bidResponse = spec.interpretResponse(response, ortb26BidRequests)[0]; + expect(bidResponse.mediaType).to.equal('banner'); + }); + }); + }); }); describe('getUserSyncs', function () { @@ -1157,7 +1799,8 @@ describe('InsticatorBidAdapter', function () { dsaparams: [1, 2] }] } - }} + } + } }, } }; diff --git a/test/spec/modules/instreamTracking_spec.js b/test/spec/modules/instreamTracking_spec.js index db1d931e9a2..e4fca4e8b87 100644 --- a/test/spec/modules/instreamTracking_spec.js +++ b/test/spec/modules/instreamTracking_spec.js @@ -20,11 +20,11 @@ function enableInstreamTracking(regex) { maxWindow: 10, pollingFreq: 0 }, - regex && {urlPattern: regex}, + regex && { urlPattern: regex }, )); } -function mockPerformanceApi({adServerCallSent, videoPresent}) { +function mockPerformanceApi({ adServerCallSent, videoPresent }) { const performanceStub = sandbox.stub(window.performance, 'getEntriesByType'); const entries = [{ name: 'https://domain.com/img.png', @@ -105,21 +105,21 @@ function mockBidRequest(adUnit, bidResponse) { function getMockInput(mediaType) { const bannerAdUnit = { code: 'banner', - mediaTypes: {banner: {sizes: [[300, 250]]}}, + mediaTypes: { banner: { sizes: [[300, 250]] } }, sizes: [[300, 250]], - bids: [{bidder: BIDDER_CODE, params: {placementId: 'id'}}] + bids: [{ bidder: BIDDER_CODE, params: { placementId: 'id' } }] }; const outStreamAdUnit = { code: 'video-' + OUTSTREAM, - mediaTypes: {video: {playerSize: [640, 480], context: OUTSTREAM}}, + mediaTypes: { video: { playerSize: [640, 480], context: OUTSTREAM } }, sizes: [[640, 480]], - bids: [{bidder: BIDDER_CODE, params: {placementId: 'id'}}] + bids: [{ bidder: BIDDER_CODE, params: { placementId: 'id' } }] }; const inStreamAdUnit = { code: 'video-' + INSTREAM, - mediaTypes: {video: {playerSize: [640, 480], context: INSTREAM}}, + mediaTypes: { video: { playerSize: [640, 480], context: INSTREAM } }, sizes: [[640, 480]], - bids: [{bidder: BIDDER_CODE, params: {placementId: 'id'}}] + bids: [{ bidder: BIDDER_CODE, params: { placementId: 'id' } }] }; let adUnit; @@ -148,7 +148,7 @@ function getMockInput(mediaType) { describe('Instream Tracking', function () { beforeEach(function () { sandbox = sinon.createSandbox(); - clock = sandbox.useFakeTimers({shouldClearNativeTimers: true}); + clock = sandbox.useFakeTimers({ shouldClearNativeTimers: true }); }); afterEach(function () { @@ -168,7 +168,7 @@ describe('Instream Tracking', function () { it('run only if instream bids are present', function () { enableInstreamTracking(); - assert.isNotOk(trackInstreamDeliveredImpressions({adUnits: [], bidsReceived: [], bidderRequests: []})); + assert.isNotOk(trackInstreamDeliveredImpressions({ adUnits: [], bidsReceived: [], bidderRequests: [] })); }); it('checks for instream bids', function () { @@ -198,7 +198,7 @@ describe('Instream Tracking', function () { it('BID WON event is not emitted when ad server call is sent', function () { enableInstreamTracking(); - mockPerformanceApi({adServerCallSent: true}); + mockPerformanceApi({ adServerCallSent: true }); clock.tick(10); assert.isNotOk(spyEventsOn.calledWith('bidWon')); }); @@ -207,7 +207,7 @@ describe('Instream Tracking', function () { enableInstreamTracking(/cache/); const bidWonSpy = sandbox.spy(); events.on('bidWon', bidWonSpy); - mockPerformanceApi({adServerCallSent: true, videoPresent: true}); + mockPerformanceApi({ adServerCallSent: true, videoPresent: true }); trackInstreamDeliveredImpressions(getMockInput(INSTREAM)); clock.tick(10); diff --git a/test/spec/modules/insuradsBidAdapter_spec.js b/test/spec/modules/insuradsBidAdapter_spec.js new file mode 100644 index 00000000000..1b321493c56 --- /dev/null +++ b/test/spec/modules/insuradsBidAdapter_spec.js @@ -0,0 +1,737 @@ +import { expect } from 'chai'; +import { + spec, STORAGE, getInsurAdsLocalStorage, getGzipSetting, +} from 'modules/insuradsBidAdapter.js'; +import sinon from 'sinon'; +import { getAmxId } from '../../../libraries/nexx360Utils/index.js'; +const sandbox = sinon.createSandbox(); + +describe('InsurAds bid adapter tests', () => { + const DEFAULT_OPTIONS = { + gdprConsent: { + gdprApplies: true, + consentString: 'BOzZdA0OzZdA0AGABBENDJ-AAAAvh7_______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-33d4-_1vf99yfm1-7ftr3tp_87ues2_Xur__79__3z3_9pxP78k89r7337Mw_v-_v-b7JCPN_Y3v-8Kg', + vendorData: {}, + }, + refererInfo: { + referer: 'https://www.prebid.org', + canonicalUrl: 'https://www.prebid.org/the/link/to/the/page', + }, + uspConsent: '1112223334', + userId: { id5id: { uid: '1111' } }, + schain: { + ver: '1.0', + complete: 1, + nodes: [{ + asi: 'exchange1.com', + sid: '1234', + hp: 1, + rid: 'bid-request-1', + name: 'publisher', + domain: 'publisher.com', + }], + }, + }; + + it('We test getGzipSettings', () => { + const output = getGzipSetting(); + expect(output).to.be.a('boolean'); + }); + + describe('isBidRequestValid()', () => { + let bannerBid; + beforeEach(() => { + bannerBid = { + bidder: 'nexx360', + mediaTypes: { banner: { sizes: [[300, 250], [300, 600]] } }, + adUnitCode: 'div-1', + transactionId: '70bdc37e-9475-4b27-8c74-4634bdc2ee66', + sizes: [[300, 250], [300, 600]], + bidId: '4906582fc87d0c', + bidderRequestId: '332fda16002dbe', + auctionId: '98932591-c822-42e3-850e-4b3cf748d063', + } + }); + + it('We verify isBidRequestValid with unvalid adUnitName', () => { + bannerBid.params = { adUnitName: 1 }; + expect(spec.isBidRequestValid(bannerBid)).to.be.equal(false); + }); + + it('We verify isBidRequestValid with empty adUnitName', () => { + bannerBid.params = { adUnitName: '' }; + expect(spec.isBidRequestValid(bannerBid)).to.be.equal(false); + }); + + it('We verify isBidRequestValid with unvalid adUnitPath', () => { + bannerBid.params = { adUnitPath: 1 }; + expect(spec.isBidRequestValid(bannerBid)).to.be.equal(false); + }); + + it('We verify isBidRequestValid with unvalid divId', () => { + bannerBid.params = { divId: 1 }; + expect(spec.isBidRequestValid(bannerBid)).to.be.equal(false); + }); + + it('We verify isBidRequestValid unvalid allBids', () => { + bannerBid.params = { allBids: 1 }; + expect(spec.isBidRequestValid(bannerBid)).to.be.equal(false); + }); + + it('We verify isBidRequestValid with uncorrect tagid', () => { + bannerBid.params = { 'tagid': 'luvxjvgn' }; + expect(spec.isBidRequestValid(bannerBid)).to.be.equal(false); + }); + + it('We verify isBidRequestValid with correct tagId', () => { + bannerBid.params = { 'tagId': 'luvxjvgn' }; + expect(spec.isBidRequestValid(bannerBid)).to.be.equal(true); + }); + + it('We verify isBidRequestValid with correct placement', () => { + bannerBid.params = { 'placement': 'testad' }; + expect(spec.isBidRequestValid(bannerBid)).to.be.equal(true); + }); + }); + + describe('getInsurAdsLocalStorage disabled', () => { + before(() => { + sandbox.stub(STORAGE, 'localStorageIsEnabled').callsFake(() => false); + }); + it('We test if we get the nexx360Id', () => { + const output = getInsurAdsLocalStorage(); + expect(output).to.be.eql(null); + }); + after(() => { + sandbox.restore() + }); + }); + + describe('getInsurAdsLocalStorage enabled but nothing', () => { + before(() => { + sandbox.stub(STORAGE, 'localStorageIsEnabled').callsFake(() => true); + sandbox.stub(STORAGE, 'setDataInLocalStorage'); + sandbox.stub(STORAGE, 'getDataFromLocalStorage').callsFake((key) => null); + }); + it('We test if we get the nexx360Id', () => { + const output = getInsurAdsLocalStorage(); + expect(typeof output.nexx360Id).to.be.eql('string'); + }); + after(() => { + sandbox.restore() + }); + }); + + describe('getInsurAdsLocalStorage enabled but wrong payload', () => { + before(() => { + sandbox.stub(STORAGE, 'localStorageIsEnabled').callsFake(() => true); + sandbox.stub(STORAGE, 'setDataInLocalStorage'); + sandbox.stub(STORAGE, 'getDataFromLocalStorage').callsFake((key) => '{"nexx360Id":"5ad89a6e-7801-48e7-97bb-fe6f251f6cb4",}'); + }); + it('We test if we get the nexx360Id', () => { + const output = getInsurAdsLocalStorage(); + expect(output).to.be.eql(null); + }); + after(() => { + sandbox.restore() + }); + }); + + describe('getInsurAdsLocalStorage enabled', () => { + before(() => { + sandbox.stub(STORAGE, 'localStorageIsEnabled').callsFake(() => true); + sandbox.stub(STORAGE, 'setDataInLocalStorage'); + sandbox.stub(STORAGE, 'getDataFromLocalStorage').callsFake((key) => '{"nexx360Id":"5ad89a6e-7801-48e7-97bb-fe6f251f6cb4"}'); + }); + it('We test if we get the nexx360Id', () => { + const output = getInsurAdsLocalStorage(); + expect(output.nexx360Id).to.be.eql('5ad89a6e-7801-48e7-97bb-fe6f251f6cb4'); + }); + after(() => { + sandbox.restore() + }); + }); + + describe('getAmxId() with localStorage enabled and data not set', () => { + before(() => { + sandbox.stub(STORAGE, 'localStorageIsEnabled').callsFake(() => true); + sandbox.stub(STORAGE, 'setDataInLocalStorage'); + sandbox.stub(STORAGE, 'getDataFromLocalStorage').callsFake((key) => null); + }); + it('We test if we get the amxId', () => { + const output = getAmxId(STORAGE, 'nexx360'); + expect(output).to.be.eql(null); + }); + after(() => { + sandbox.restore() + }); + }); + + describe('getAmxId() with localStorage enabled and data set', () => { + before(() => { + sandbox.stub(STORAGE, 'localStorageIsEnabled').callsFake(() => true); + sandbox.stub(STORAGE, 'setDataInLocalStorage'); + sandbox.stub(STORAGE, 'getDataFromLocalStorage').callsFake((key) => 'abcdef'); + }); + it('We test if we get the amxId', () => { + const output = getAmxId(STORAGE, 'nexx360'); + expect(output).to.be.eql('abcdef'); + }); + after(() => { + sandbox.restore() + }); + }); + + describe('buildRequests()', () => { + before(() => { + const documentStub = sandbox.stub(document, 'getElementById'); + documentStub.withArgs('div-1').returns({ + offsetWidth: 200, + offsetHeight: 250, + style: { + maxWidth: '400px', + maxHeight: '350px', + }, + getBoundingClientRect() { return { width: 200, height: 250 }; } + }); + sandbox.stub(STORAGE, 'localStorageIsEnabled').callsFake(() => true); + sandbox.stub(STORAGE, 'setDataInLocalStorage'); + sandbox.stub(STORAGE, 'getDataFromLocalStorage').callsFake((key) => 'abcdef'); + }); + describe('We test with a multiple display bids', () => { + const sampleBids = [ + { + bidder: 'nexx360', + params: { + tagId: 'luvxjvgn', + divId: 'div-1', + adUnitName: 'header-ad', + adUnitPath: '/12345/nexx360/Homepage/HP/Header-Ad', + }, + ortb2Imp: { + ext: { + gpid: '/12345/nexx360/Homepage/HP/Header-Ad', + } + }, + adUnitCode: 'header-ad-1234', + transactionId: '469a570d-f187-488d-b1cb-48c1a2009be9', + sizes: [[300, 250], [300, 600]], + bidId: '44a2706ac3574', + bidderRequestId: '359bf8a3c06b2e', + auctionId: '2e684815-b44e-4e04-b812-56da54adbe74', + }, + { + bidder: 'nexx360', + params: { + placement: 'testPlacement', + allBids: true, + }, + mediaTypes: { + banner: { + sizes: [[728, 90], [970, 250]] + } + }, + + adUnitCode: 'div-2-abcd', + transactionId: '6196885d-4e76-40dc-a09c-906ed232626b', + sizes: [[728, 90], [970, 250]], + bidId: '5ba94555219a03', + bidderRequestId: '359bf8a3c06b2e', + auctionId: '2e684815-b44e-4e04-b812-56da54adbe74', + } + ]; + const bidderRequest = { + bidderCode: 'nexx360', + auctionId: '2e684815-b44e-4e04-b812-56da54adbe74', + bidderRequestId: '359bf8a3c06b2e', + refererInfo: { + reachedTop: true, + isAmp: false, + numIframes: 0, + stack: [ + 'https://test.nexx360.io/adapter/index.html' + ], + topmostLocation: 'https://test.nexx360.io/adapter/index.html', + location: 'https://test.nexx360.io/adapter/index.html', + canonicalUrl: null, + page: 'https://test.nexx360.io/adapter/index.html', + domain: 'test.nexx360.io', + ref: null, + legacy: { + reachedTop: true, + isAmp: false, + numIframes: 0, + stack: [ + 'https://test.nexx360.io/adapter/index.html' + ], + referer: 'https://test.nexx360.io/adapter/index.html', + canonicalUrl: null + }, + }, + gdprConsent: { + gdprApplies: true, + consentString: 'CPhdLUAPhdLUAAKAsAENCmCsAP_AAE7AAAqIJFNd_H__bW9r-f5_aft0eY1P9_r37uQzDhfNk-8F3L_W_LwX52E7NF36tq4KmR4ku1LBIUNlHMHUDUmwaokVryHsak2cpzNKJ7BEknMZOydYGF9vmxtj-QKY7_5_d3bx2D-t_9v239z3z81Xn3d53-_03LCdV5_9Dfn9fR_bc9KPt_58v8v8_____3_e__3_7997BIiAaADgAJYBnwEeAJXAXmAwQBj4DtgHcgPBAeKBIgAA.YAAAAAAAAAAA', + } + }; + it('We perform a test with 2 display adunits', () => { + const displayBids = structuredClone(sampleBids); + displayBids[0].mediaTypes = { + banner: { + sizes: [[300, 250], [300, 600]] + } + }; + const request = spec.buildRequests(displayBids, bidderRequest); + const requestContent = request.data; + expect(request).to.have.property('method').and.to.equal('POST'); + const expectedRequest = { + imp: [ + { + id: '44a2706ac3574', + banner: { + topframe: 0, + format: [ + { w: 300, h: 250 }, + { w: 300, h: 600 }, + ], + }, + secure: 1, + tagid: 'header-ad-1234', + ext: { + adUnitCode: 'header-ad-1234', + gpid: '/12345/nexx360/Homepage/HP/Header-Ad', + divId: 'div-1', + dimensions: { + slotW: 200, + slotH: 250, + cssMaxW: '400px', + cssMaxH: '350px', + }, + nexx360: { + tagId: 'luvxjvgn', + adUnitName: 'header-ad', + adUnitPath: '/12345/nexx360/Homepage/HP/Header-Ad', + divId: 'div-1', + }, + adUnitName: 'header-ad', + adUnitPath: '/12345/nexx360/Homepage/HP/Header-Ad', + }, + }, + { + id: '5ba94555219a03', + banner: { + topframe: 0, + format: [ + { w: 728, h: 90 }, + { w: 970, h: 250 }, + ], + }, + secure: 1, + tagid: 'div-2-abcd', + ext: { + adUnitCode: 'div-2-abcd', + divId: 'div-2-abcd', + nexx360: { + placement: 'testPlacement', + divId: 'div-2-abcd', + allBids: true, + }, + }, + }, + ], + id: requestContent.id, + test: 0, + ext: { + version: requestContent.ext.version, + source: 'prebid.js', + pageViewId: requestContent.ext.pageViewId, + bidderVersion: '7.1', + localStorage: { amxId: 'abcdef' }, + sessionId: requestContent.ext.sessionId, + requestCounter: 0, + }, + cur: [ + 'USD', + ], + user: { + ext: { + eids: [ + { + source: 'amxdt.net', + uids: [ + { + id: 'abcdef', + atype: 1, + } + ] + } + ] + } + }, + }; + expect(requestContent).to.be.eql(expectedRequest); + }); + + if (FEATURES.VIDEO) { + it('We perform a test with a multiformat adunit', () => { + const multiformatBids = structuredClone(sampleBids); + multiformatBids[0].mediaTypes = { + banner: { + sizes: [[300, 250], [300, 600]] + }, + video: { + context: 'outstream', + playerSize: [640, 480], + mimes: ['video/mp4'], + protocols: [1, 2, 3, 4, 5, 6, 7, 8], + playbackmethod: [2], + skip: 1, + playback_method: ['auto_play_sound_off'] + } + }; + const request = spec.buildRequests(multiformatBids, bidderRequest); + const video = request.data.imp[0].video; + const expectedVideo = { + mimes: ['video/mp4'], + protocols: [1, 2, 3, 4, 5, 6, 7, 8], + playbackmethod: [2], + skip: 1, + w: 640, + h: 480, + ext: { + playerSize: [640, 480], + context: 'outstream', + }, + }; + expect(video).to.eql(expectedVideo); + }); + + it('We perform a test with a instream adunit', () => { + const videoBids = structuredClone(sampleBids); + videoBids[0].mediaTypes = { + video: { + context: 'instream', + playerSize: [640, 480], + mimes: ['video/mp4'], + protocols: [1, 2, 3, 4, 5, 6], + playbackmethod: [2], + skip: 1 + } + }; + const request = spec.buildRequests(videoBids, bidderRequest); + const requestContent = request.data; + expect(request).to.have.property('method').and.to.equal('POST'); + expect(requestContent.imp[0].video.ext.context).to.be.eql('instream'); + expect(requestContent.imp[0].video.playbackmethod[0]).to.be.eql(2); + }); + } + }); + after(() => { + sandbox.restore() + }); + }); + + describe('We test interpretResponse', () => { + it('empty response', () => { + const response = { + body: '' + }; + const output = spec.interpretResponse(response); + expect(output.length).to.be.eql(0); + }); + it('banner responses with adm', () => { + const response = { + body: { + id: 'a8d3a675-a4ba-4d26-807f-c8f2fad821e0', + cur: 'USD', + seatbid: [ + { + bid: [ + { + id: '4427551302944024629', + impid: '226175918ebeda', + price: 1.5, + adomain: [ + 'http://prebid.org', + ], + crid: '98493581', + ssp: 'appnexus', + h: 600, + w: 300, + adm: '
TestAd
', + cat: [ + 'IAB3-1', + ], + ext: { + adUnitCode: 'div-1', + mediaType: 'banner', + adUrl: 'https://fast.nexx360.io/cache?uuid=fdddcebc-1edf-489d-880d-1418d8bdc493', + ssp: 'appnexus', + }, + }, + ], + seat: 'appnexus', + }, + ], + ext: { + id: 'de3de7c7-e1cf-4712-80a9-94eb26bfc718', + cookies: [], + }, + }, + }; + const output = spec.interpretResponse(response); + const expectedOutput = [{ + requestId: '226175918ebeda', + cpm: 1.5, + width: 300, + height: 600, + creativeId: '98493581', + currency: 'USD', + netRevenue: true, + ttl: 120, + mediaType: 'banner', + meta: { + advertiserDomains: [ + 'http://prebid.org', + ], + demandSource: 'appnexus', + }, + ad: '
TestAd
', + }]; + expect(output).to.eql(expectedOutput); + }); + + it('instream responses', () => { + const response = { + body: { + id: '2be64380-ba0c-405a-ab53-51f51c7bde51', + cur: 'USD', + seatbid: [ + { + bid: [ + { + id: '8275140264321181514', + impid: '263cba3b8bfb72', + price: 5, + adomain: [ + 'appnexus.com', + ], + crid: '97517771', + h: 1, + w: 1, + adm: 'vast', + ext: { + mediaType: 'instream', + ssp: 'appnexus', + adUnitCode: 'video1', + }, + }, + ], + seat: 'appnexus', + }, + ], + ext: { + cookies: [], + }, + }, + }; + + const output = spec.interpretResponse(response); + const expectedOutput = [{ + requestId: '263cba3b8bfb72', + cpm: 5, + width: 1, + height: 1, + creativeId: '97517771', + currency: 'USD', + netRevenue: true, + ttl: 120, + mediaType: 'video', + meta: { advertiserDomains: ['appnexus.com'], demandSource: 'appnexus' }, + vastXml: 'vast', + }]; + expect(output).to.eql(expectedOutput); + }); + + it('outstream responses', () => { + const response = { + body: { + id: '40c23932-135e-4602-9701-ca36f8d80c07', + cur: 'USD', + seatbid: [ + { + bid: [ + { + id: '1186971142548769361', + impid: '4ce809b61a3928', + price: 5, + adomain: [ + 'appnexus.com', + ], + crid: '97517771', + h: 1, + w: 1, + adm: 'vast', + ext: { + mediaType: 'outstream', + ssp: 'appnexus', + adUnitCode: 'div-1', + divId: 'div-1', + }, + }, + ], + seat: 'appnexus', + }, + ], + ext: { + cookies: [], + }, + }, + }; + + const output = spec.interpretResponse(response); + const expectedOutut = [{ + requestId: '4ce809b61a3928', + cpm: 5, + width: 1, + height: 1, + creativeId: '97517771', + currency: 'USD', + netRevenue: true, + divId: 'div-1', + ttl: 120, + mediaType: 'video', + meta: { advertiserDomains: ['appnexus.com'], demandSource: 'appnexus' }, + vastXml: 'vast', + renderer: output[0].renderer, + }]; + expect(output).to.eql(expectedOutut); + }); + + it('native responses', () => { + const response = { + body: { + id: '3c0290c1-6e75-4ef7-9e37-17f5ebf3bfa3', + cur: 'USD', + seatbid: [ + { + bid: [ + { + id: '6624930625245272225', + impid: '23e11d845514bb', + price: 10, + adomain: [ + 'prebid.org', + ], + crid: '97494204', + h: 1, + w: 1, + cat: [ + 'IAB3-1', + ], + ext: { + mediaType: 'native', + ssp: 'appnexus', + adUnitCode: '/19968336/prebid_native_example_1', + }, + 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:\\/\\/ams3-ib.adnxs.com\\/click?AAAAAAAAJEAAAAAAAAAkQAAAAAAAACRAAAAAAAAAJEAAAAAAAAAkQKZS4ZZl5vVbR6p-A-MwnyTZ7QVkAAAAAOLoyQBtJAAAbSQAAAIAAAC8pM8FnPgWAAAAAABVU0QAVVNEAAEAAQBNXQAAAAABAgMCAAAAALoAURe69gAAAAA.\\/bcr=AAAAAAAA8D8=\\/pp=${AUCTION_PRICE}\\/cnd=%21JBC72Aj8-LwKELzJvi4YnPFbIAQoADEAAAAAAAAkQDoJQU1TMzo2MTM1QNAwSQAAAAAAAPA_UQAAAAAAAAAAWQAAAAAAAAAAYQAAAAAAAAAAaQAAAAAAAAAAcQAAAAAAAAAAeACJAQAAAAAAAAAA\\/cca=OTMyNSNBTVMzOjYxMzU=\\/bn=97062\\/clickenc=http%3A%2F%2Fprebid.org%2Fdev-docs%2Fshow-native-ads.html"},"eventtrackers":[{"event":1,"method":1,"url":"https:\\/\\/ams3-ib.adnxs.com\\/it?an_audit=0&referrer=https%3A%2F%2Ftest.nexx360.io%2Fadapter%2Fnative%2Ftest.html&e=wqT_3QKJCqAJBQAAAwDWAAUBCNnbl6AGEKalhbfZzPn6WxjH1PqbsJzMzyQqNgkAAAECCCRAEQEHEAAAJEAZEQkAIREJACkRCQAxEQmoMOLRpwY47UhA7UhIAlC8yb4uWJzxW2AAaM26dXim9gWAAQGKAQNVU0SSAQEG9F4BmAEBoAEBqAEBsAEAuAECwAEDyAEC0AEJ2AEA4AEA8AEAigIpdWYoJ2EnLCAyNTI5ODg1LCAwKTt1ZigncicsIDk3NDk0MjA0LCAwKTuSAvEDIS0xRDNJQWo4LUx3S0VMekp2aTRZQUNDYzhWc3dBRGdBUUFSSTdVaFE0dEduQmxnQVlQX19fXzhQYUFCd0FYZ0JnQUVCaUFFQmtBRUJtQUVCb0FFQnFBRURzQUVBdVFIenJXcWtBQUFrUU1FQjg2MXFwQUFBSkVESkFYSUtWbWViSmZJXzJRRUFBQUFBQUFEd1AtQUJBUFVCQUFBQUFKZ0NBS0FDQUxVQ0FBQUFBTDBDQUFBQUFNQUNBY2dDQWRBQ0FkZ0NBZUFDQU9nQ0FQZ0NBSUFEQVpnREFib0RDVUZOVXpNNk5qRXpOZUFEMERDSUJBQ1FCQUNZQkFIQkJBQUFBQUFBQUFBQXlRUUFBCQscQUFOZ0VBUEURlSxBQUFDSUJmY3ZxUVUBDQRBQQGoCDdFRgEKCQEMREJCUQkKAQEAeRUoAUwyKAAAWi4oALg0QVhBaEQzd0JhTEQzd0w0QmQyMG1nR0NCZ05WVTBTSUJnQ1FCZ0dZQmdDaEJnQQFONEFBQ1JBcUFZQnNnWWtDHXQARR0MAEcdDABJHQw8dUFZS5oClQEhSkJDNzJBajL1ASRuUEZiSUFRb0FEFfhUa1FEb0pRVTFUTXpvMk1UTTFRTkF3UxFRDFBBX1URDAxBQUFXHQwAWR0MAGEdDABjHQwQZUFDSkEdEMjYAvfpA-ACrZhI6gIwaHR0cHM6Ly90ZXN0Lm5leHgzNjAuaW8vYWRhcHRlci9uYXRpdmUJH_CaaHRtbIADAIgDAZADAJgDFKADAaoDAMAD4KgByAMA2AMA4AMA6AMA-AMDgAQAkgQJL29wZW5ydGIymAQAqAQAsgQMCAAQABgAIAAwADgAuAQAwASA2rgiyAQA0gQOOTMyNSNBTVMzOjYxMzXaBAIIAeAEAPAEvMm-LvoEEgkAAABAPG1IQBEAAACgV8oCQIgFAZgFAKAF______8BBbABqgUkM2MwMjkwYzEtNmU3NS00ZWY3LTllMzctMTdmNWViZjNiZmEzwAUAyQWJFxTwP9IFCQkJDHgAANgFAeAFAfAFmfQh-gUECAAQAJAGAZgGALgGAMEGCSUo8D_QBvUv2gYWChAJERkBAdpg4AYM8gYCCACABwGIBwCgB0HIB6b2BdIHDRVkASYI2gcGAV1oGADgBwDqBwIIAPAHAIoIAhAAlQgAAIA_mAgB&s=ccf63f2e483a37091d2475d895e7cf7c911d1a78&pp=${AUCTION_PRICE}"}]}', + }, + ], + seat: 'appnexus', + }, + ], + ext: { + cookies: [], + }, + }, + }; + + const output = spec.interpretResponse(response); + const expectOutput = [{ + requestId: '23e11d845514bb', + cpm: 10, + width: 1, + height: 1, + creativeId: '97494204', + currency: 'USD', + netRevenue: true, + ttl: 120, + mediaType: 'native', + meta: { + advertiserDomains: [ + 'prebid.org', + ], + demandSource: 'appnexus', + }, + native: { + ortb: { + 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://ams3-ib.adnxs.com/click?AAAAAAAAJEAAAAAAAAAkQAAAAAAAACRAAAAAAAAAJEAAAAAAAAAkQKZS4ZZl5vVbR6p-A-MwnyTZ7QVkAAAAAOLoyQBtJAAAbSQAAAIAAAC8pM8FnPgWAAAAAABVU0QAVVNEAAEAAQBNXQAAAAABAgMCAAAAALoAURe69gAAAAA./bcr=AAAAAAAA8D8=/pp=${AUCTION_PRICE}/cnd=%21JBC72Aj8-LwKELzJvi4YnPFbIAQoADEAAAAAAAAkQDoJQU1TMzo2MTM1QNAwSQAAAAAAAPA_UQAAAAAAAAAAWQAAAAAAAAAAYQAAAAAAAAAAaQAAAAAAAAAAcQAAAAAAAAAAeACJAQAAAAAAAAAA/cca=OTMyNSNBTVMzOjYxMzU=/bn=97062/clickenc=http%3A%2F%2Fprebid.org%2Fdev-docs%2Fshow-native-ads.html', + }, + eventtrackers: [ + { + event: 1, + method: 1, + url: 'https://ams3-ib.adnxs.com/it?an_audit=0&referrer=https%3A%2F%2Ftest.nexx360.io%2Fadapter%2Fnative%2Ftest.html&e=wqT_3QKJCqAJBQAAAwDWAAUBCNnbl6AGEKalhbfZzPn6WxjH1PqbsJzMzyQqNgkAAAECCCRAEQEHEAAAJEAZEQkAIREJACkRCQAxEQmoMOLRpwY47UhA7UhIAlC8yb4uWJzxW2AAaM26dXim9gWAAQGKAQNVU0SSAQEG9F4BmAEBoAEBqAEBsAEAuAECwAEDyAEC0AEJ2AEA4AEA8AEAigIpdWYoJ2EnLCAyNTI5ODg1LCAwKTt1ZigncicsIDk3NDk0MjA0LCAwKTuSAvEDIS0xRDNJQWo4LUx3S0VMekp2aTRZQUNDYzhWc3dBRGdBUUFSSTdVaFE0dEduQmxnQVlQX19fXzhQYUFCd0FYZ0JnQUVCaUFFQmtBRUJtQUVCb0FFQnFBRURzQUVBdVFIenJXcWtBQUFrUU1FQjg2MXFwQUFBSkVESkFYSUtWbWViSmZJXzJRRUFBQUFBQUFEd1AtQUJBUFVCQUFBQUFKZ0NBS0FDQUxVQ0FBQUFBTDBDQUFBQUFNQUNBY2dDQWRBQ0FkZ0NBZUFDQU9nQ0FQZ0NBSUFEQVpnREFib0RDVUZOVXpNNk5qRXpOZUFEMERDSUJBQ1FCQUNZQkFIQkJBQUFBQUFBQUFBQXlRUUFBCQscQUFOZ0VBUEURlSxBQUFDSUJmY3ZxUVUBDQRBQQGoCDdFRgEKCQEMREJCUQkKAQEAeRUoAUwyKAAAWi4oALg0QVhBaEQzd0JhTEQzd0w0QmQyMG1nR0NCZ05WVTBTSUJnQ1FCZ0dZQmdDaEJnQQFONEFBQ1JBcUFZQnNnWWtDHXQARR0MAEcdDABJHQw8dUFZS5oClQEhSkJDNzJBajL1ASRuUEZiSUFRb0FEFfhUa1FEb0pRVTFUTXpvMk1UTTFRTkF3UxFRDFBBX1URDAxBQUFXHQwAWR0MAGEdDABjHQwQZUFDSkEdEMjYAvfpA-ACrZhI6gIwaHR0cHM6Ly90ZXN0Lm5leHgzNjAuaW8vYWRhcHRlci9uYXRpdmUJH_CaaHRtbIADAIgDAZADAJgDFKADAaoDAMAD4KgByAMA2AMA4AMA6AMA-AMDgAQAkgQJL29wZW5ydGIymAQAqAQAsgQMCAAQABgAIAAwADgAuAQAwASA2rgiyAQA0gQOOTMyNSNBTVMzOjYxMzXaBAIIAeAEAPAEvMm-LvoEEgkAAABAPG1IQBEAAACgV8oCQIgFAZgFAKAF______8BBbABqgUkM2MwMjkwYzEtNmU3NS00ZWY3LTllMzctMTdmNWViZjNiZmEzwAUAyQWJFxTwP9IFCQkJDHgAANgFAeAFAfAFmfQh-gUECAAQAJAGAZgGALgGAMEGCSUo8D_QBvUv2gYWChAJERkBAdpg4AYM8gYCCACABwGIBwCgB0HIB6b2BdIHDRVkASYI2gcGAV1oGADgBwDqBwIIAPAHAIoIAhAAlQgAAIA_mAgB&s=ccf63f2e483a37091d2475d895e7cf7c911d1a78&pp=${AUCTION_PRICE}', + }, + ], + }, + }, + }]; + expect(output).to.eql(expectOutput); + }); + }); + + describe('getUserSyncs()', () => { + const response = { body: { cookies: [] } }; + it('Verifies user sync without cookie in bid response', () => { + const syncs = spec.getUserSyncs({}, [response], DEFAULT_OPTIONS.gdprConsent, DEFAULT_OPTIONS.uspConsent); + expect(syncs).to.eql([]); + }); + it('Verifies user sync with cookies in bid response', () => { + response.body.ext = { + cookies: [{ 'type': 'image', 'url': 'http://www.cookie.sync.org/' }] + }; + const syncs = spec.getUserSyncs({}, [response], DEFAULT_OPTIONS.gdprConsent); + const expectedSyncs = [{ type: 'image', url: 'http://www.cookie.sync.org/' }]; + expect(syncs).to.eql(expectedSyncs); + }); + it('Verifies user sync with no bid response', () => { + var syncs = spec.getUserSyncs({}, null, DEFAULT_OPTIONS.gdprConsent, DEFAULT_OPTIONS.uspConsent); + expect(syncs).to.eql([]); + }); + it('Verifies user sync with no bid body response', () => { + let syncs = spec.getUserSyncs({}, [], DEFAULT_OPTIONS.gdprConsent, DEFAULT_OPTIONS.uspConsent); + expect(syncs).to.eql([]); + syncs = spec.getUserSyncs({}, [{}], DEFAULT_OPTIONS.gdprConsent, DEFAULT_OPTIONS.uspConsent); + expect(syncs).to.eql([]); + }); + }); +}); diff --git a/test/spec/modules/intentIqAnalyticsAdapter_spec.js b/test/spec/modules/intentIqAnalyticsAdapter_spec.js index e93b6ec1915..0a31b43b5f5 100644 --- a/test/spec/modules/intentIqAnalyticsAdapter_spec.js +++ b/test/spec/modules/intentIqAnalyticsAdapter_spec.js @@ -2,8 +2,10 @@ import { expect } from "chai"; import iiqAnalyticsAnalyticsAdapter from "modules/intentIqAnalyticsAdapter.js"; import * as utils from "src/utils.js"; import { server } from "test/mocks/xhr.js"; +import { config } from "src/config.js"; import { EVENTS } from "src/constants.js"; import * as events from "src/events.js"; +import { getGlobal } from "../../../src/prebidGlobal.js"; import sinon from "sinon"; import { REPORTER_ID, @@ -20,7 +22,7 @@ import { } from "../../../libraries/intentIqConstants/intentIqConstants.js"; import * as detectBrowserUtils from "../../../libraries/intentIqUtils/detectBrowserUtils.js"; import { - getReferrer, + getCurrentUrl, appendVrrefAndFui, } from "../../../libraries/intentIqUtils/getRefferer.js"; import { @@ -29,7 +31,10 @@ import { gdprDataHandler, } from "../../../src/consentHandler.js"; +let getConfigStub; +let userIdConfigForTest; const partner = 10; +const identityName = `iiq_identity_${partner}` const defaultIdentityObject = { firstPartyData: { pcid: "f961ffb1-a0e1-4696-a9d2-a21d815bd344", @@ -41,8 +46,7 @@ const defaultIdentityObject = { sCal: Date.now() - 36000, isOptedOut: false, pid: "profile", - dbsaved: "true", - spd: "spd", + dbsaved: "true" }, partnerData: { abTestUuid: "abTestUuid", @@ -53,7 +57,7 @@ const defaultIdentityObject = { profile: "profile", wsrvcll: true, }, - clientHints: { + clientHints: JSON.stringify({ 0: '"Chromium";v="142", "Google Chrome";v="142", "Not_A Brand";v="99"', 1: "?0", 2: '"macOS"', @@ -62,8 +66,30 @@ const defaultIdentityObject = { 6: '"15.6.1"', 7: "?0", 8: '"Chromium";v="142.0.7444.60", "Google Chrome";v="142.0.7444.60", "Not_A Brand";v="99.0.0.0"', - }, + }), }; +const regionCases = [ + { + name: 'default (no region)', + region: undefined, + expectedEndpoint: 'https://reports.intentiq.com/report' + }, + { + name: 'apac', + region: 'apac', + expectedEndpoint: 'https://reports-apac.intentiq.com/report' + }, + { + name: 'emea', + region: 'emea', + expectedEndpoint: 'https://reports-emea.intentiq.com/report' + }, + { + name: 'gdpr', + region: 'gdpr', + expectedEndpoint: 'https://reports-gdpr.intentiq.com/report' + } +] const version = VERSION; const REPORT_ENDPOINT = "https://reports.intentiq.com/report"; const REPORT_ENDPOINT_GDPR = "https://reports-gdpr.intentiq.com/report"; @@ -78,6 +104,22 @@ const getDefaultConfig = () => { } } +const getUserConfigWithReportingServerAddress = () => [ + { + 'name': 'intentIqId', + 'params': { + 'partner': partner, + 'unpack': null, + }, + 'storage': { + 'type': 'html5', + 'name': 'intentIqId', + 'expires': 60, + 'refreshInSeconds': 14400 + } + } +]; + const getWonRequest = () => ({ bidderCode: "pubmatic", width: 728, @@ -131,6 +173,11 @@ describe("IntentIQ tests all", function () { beforeEach(function () { logErrorStub = sinon.stub(utils, "logError"); sinon.stub(events, "getEvents").returns([]); + + if (config.getConfig && config.getConfig.restore) { + config.getConfig.restore(); + } + iiqAnalyticsAnalyticsAdapter.initOptions = { lsValueInitialized: false, partner: null, @@ -151,11 +198,12 @@ describe("IntentIQ tests all", function () { iiqAnalyticsAnalyticsAdapter.track.restore(); } sinon.spy(iiqAnalyticsAnalyticsAdapter, "track"); - window[`iiq_identity_${partner}`] = defaultIdentityObject; + window[identityName] = utils.deepClone(defaultIdentityObject); }); afterEach(function () { logErrorStub.restore(); + if (getConfigStub && getConfigStub.restore) getConfigStub.restore(); if (getWindowSelfStub) getWindowSelfStub.restore(); if (getWindowTopStub) getWindowTopStub.restore(); if (getWindowLocationStub) getWindowLocationStub.restore(); @@ -272,26 +320,52 @@ describe("IntentIQ tests all", function () { expect(payloadDecoded).to.have.property("adType", externalWinEvent.adType); }); - it("should send report to report-gdpr address if gdpr is detected", function () { - const gppStub = sinon - .stub(gppDataHandler, "getConsentData") - .returns({ gppString: '{"key1":"value1","key2":"value2"}' }); - const uspStub = sinon - .stub(uspDataHandler, "getConsentData") - .returns("1NYN"); - const gdprStub = sinon - .stub(gdprDataHandler, "getConsentData") - .returns({ consentString: "gdprConsent" }); + it("should get pos from pbjs.adUnits when BID_WON has no pos", function () { + const pbjs = getGlobal(); + const prevAdUnits = pbjs.adUnits; - events.emit(EVENTS.BID_WON, getWonRequest()); + pbjs.adUnits = Array.isArray(pbjs.adUnits) ? pbjs.adUnits : []; + pbjs.adUnits.push({ code: "myVideoAdUnit", mediaTypes: { video: { pos: 777 } } }); + + enableAnalyticWithSpecialOptions({ manualWinReportEnabled: false }); + + events.emit(EVENTS.BID_WON, { + ...getWonRequest(), + adUnitCode: "myVideoAdUnit", + mediaType: "video" + }); - expect(server.requests.length).to.be.above(0); const request = server.requests[0]; + const payloadEncoded = new URL(request.url).searchParams.get("payload"); + const payloadDecoded = JSON.parse(atob(JSON.parse(payloadEncoded)[0])); - expect(request.url).to.contain(REPORT_ENDPOINT_GDPR); - gppStub.restore(); - uspStub.restore(); - gdprStub.restore(); + expect(payloadDecoded.pos).to.equal(777); + + pbjs.adUnits = prevAdUnits; + }); + + it("should get pos from reportExternalWin when present", function () { + enableAnalyticWithSpecialOptions({ manualWinReportEnabled: true }); + + const winPos = 999; + + window[`intentIqAnalyticsAdapter_${partner}`].reportExternalWin({ + adUnitCode: "myVideoAdUnit", + bidderCode: "appnexus", + cpm: 1.5, + currency: "USD", + mediaType: "video", + size: "300x250", + status: "rendered", + auctionId: "auc123", + pos: winPos + }); + + const request = server.requests[0]; + const payloadEncoded = new URL(request.url).searchParams.get("payload"); + const payloadDecoded = JSON.parse(atob(JSON.parse(payloadEncoded)[0])); + + expect(payloadDecoded.pos).to.equal(winPos); }); it("should initialize with default configurations", function () { @@ -319,6 +393,9 @@ describe("IntentIQ tests all", function () { }); it("should handle BID_WON event with default group configuration", function () { + const spdData = "server provided data"; + const expectedSpdEncoded = encodeURIComponent(spdData); + window[identityName].partnerData.spd = spdData; const wonRequest = getWonRequest(); events.emit(EVENTS.BID_WON, wonRequest); @@ -331,7 +408,7 @@ describe("IntentIQ tests all", function () { const payload = encodeURIComponent(JSON.stringify([base64String])); const expectedUrl = appendVrrefAndFui( REPORT_ENDPOINT + - `?pid=${partner}&mct=1&iiqid=${defaultIdentityObject.firstPartyData.pcid}&agid=${REPORTER_ID}&jsver=${version}&source=pbjs&uh=&gdpr=0&spd=spd`, + `?pid=${partner}&mct=1&iiqid=${defaultIdentityObject.firstPartyData.pcid}&agid=${REPORTER_ID}&jsver=${version}&source=pbjs&uh=${encodeURIComponent(window[identityName].clientHints)}&gdpr=0&spd=${expectedSpdEncoded}`, iiqAnalyticsAnalyticsAdapter.initOptions.domainName ); const urlWithPayload = expectedUrl + `&payload=${payload}`; @@ -379,6 +456,22 @@ describe("IntentIQ tests all", function () { gdprStub.restore(); }); + regionCases.forEach(({ name, region, expectedEndpoint }) => { + it(`should send request to region-specific report endpoint when region is "${name}"`, function () { + userIdConfigForTest = getUserConfigWithReportingServerAddress(); + getConfigStub = sinon.stub(config, "getConfig"); + getConfigStub.withArgs("userSync.userIds").callsFake(() => userIdConfigForTest); + + enableAnalyticWithSpecialOptions({ region }); + + events.emit(EVENTS.BID_WON, getWonRequest()); + + expect(server.requests.length).to.be.above(0); + const request = server.requests[0]; + expect(request.url).to.contain(expectedEndpoint); + }); + }); + it("should not send request if manualWinReportEnabled is true", function () { iiqAnalyticsAnalyticsAdapter.initOptions.manualWinReportEnabled = true; events.emit(EVENTS.BID_WON, getWonRequest()); @@ -417,7 +510,7 @@ describe("IntentIQ tests all", function () { .stub(utils, "getWindowLocation") .returns({ href: "http://localhost:9876/" }); - const referrer = getReferrer(); + const referrer = getCurrentUrl(); expect(referrer).to.equal("http://localhost:9876/"); }); @@ -428,7 +521,7 @@ describe("IntentIQ tests all", function () { .stub(utils, "getWindowTop") .returns({ location: { href: "http://example.com/" } }); - const referrer = getReferrer(); + const referrer = getCurrentUrl(); expect(referrer).to.equal("http://example.com/"); }); @@ -440,7 +533,7 @@ describe("IntentIQ tests all", function () { .stub(utils, "getWindowTop") .throws(new Error("Access denied")); - const referrer = getReferrer(); + const referrer = getCurrentUrl(); expect(referrer).to.equal(""); expect(logErrorStub.calledOnce).to.be.true; expect(logErrorStub.firstCall.args[0]).to.contain( @@ -528,6 +621,36 @@ describe("IntentIQ tests all", function () { expect(request.url).to.include("general=Lee"); }); + it("should include domainName in both query and payload when fullUrl is empty (cross-origin)", function () { + const domainName = "mydomain-frame.com"; + + enableAnalyticWithSpecialOptions({ domainName }); + + getWindowTopStub = sinon.stub(utils, "getWindowTop").throws(new Error("cross-origin")); + + events.emit(EVENTS.BID_WON, getWonRequest()); + + const request = server.requests[0]; + + // Query contain vrref=domainName + const parsedUrl = new URL(request.url); + const vrrefParam = parsedUrl.searchParams.get("vrref"); + + // Payload contain vrref=domainName + const payloadEncoded = parsedUrl.searchParams.get("payload"); + const payloadDecoded = JSON.parse(atob(JSON.parse(payloadEncoded)[0])); + + expect(server.requests.length).to.be.above(0); + expect(vrrefParam).to.not.equal(null); + expect(decodeURIComponent(vrrefParam)).to.equal(domainName); + expect(parsedUrl.searchParams.get("fui")).to.equal("1"); + + expect(payloadDecoded).to.have.property("vrref"); + expect(decodeURIComponent(payloadDecoded.vrref)).to.equal(domainName); + + restoreReportList(); + }); + it("should not send additionalParams in report if value is too large", function () { const longVal = "x".repeat(5000000); @@ -550,8 +673,9 @@ describe("IntentIQ tests all", function () { it("should include spd parameter from LS in report URL", function () { const spdObject = { foo: "bar", value: 42 }; const expectedSpdEncoded = encodeURIComponent(JSON.stringify(spdObject)); - window[`iiq_identity_${partner}`].firstPartyData.spd = + window[identityName].firstPartyData.spd = JSON.stringify(spdObject); + window[identityName].partnerData.spd = spdObject; getWindowLocationStub = sinon .stub(utils, "getWindowLocation") @@ -568,7 +692,7 @@ describe("IntentIQ tests all", function () { it("should include spd parameter string from LS in report URL", function () { const spdData = "server provided data"; const expectedSpdEncoded = encodeURIComponent(spdData); - window[`iiq_identity_${partner}`].firstPartyData.spd = spdData; + window[identityName].partnerData.spd = spdData; getWindowLocationStub = sinon .stub(utils, "getWindowLocation") @@ -896,4 +1020,56 @@ describe("IntentIQ tests all", function () { // Verify that the group from options is used in the payload expect(decodedPayload).to.have.property("abGroup", providedGroup); }); + + it("should include partnerAuctionId in query params and payload if provided by partner (GET)", function () { + const partnerAuctionId = "TEST-PAUCID-123"; + enableAnalyticWithSpecialOptions({ + manualWinReportEnabled: true, + reportMethod: "GET" + }); + + window[`intentIqAnalyticsAdapter_${partner}`].reportExternalWin({ + cpm: 1, + currency: "USD", + adType: "banner", + partnerAuctionId + }); + + const request = server.requests[0]; + const url = new URL(request.url); + const paucidParam = url.searchParams.get("paucid"); + const payloadEncoded = url.searchParams.get("payload"); + const payloadDecoded = JSON.parse( + atob(JSON.parse(payloadEncoded)[0]) + ); + + expect(payloadEncoded).to.be.a('string'); + expect(JSON.parse(paucidParam)).to.deep.equal([partnerAuctionId]); + expect(payloadDecoded.partnerAuctionId).to.equal(partnerAuctionId); + }); + + it("should include partnerAuctionId in query params and payload if provided by partner (POST)", function () { + const partnerAuctionId = "TEST-PAUCID-123"; + enableAnalyticWithSpecialOptions({ + manualWinReportEnabled: true, + reportMethod: "POST" + }); + + window[`intentIqAnalyticsAdapter_${partner}`].reportExternalWin({ + cpm: 1, + currency: "USD", + adType: "banner", + partnerAuctionId + }); + + const request = server.requests[0]; + const url = new URL(request.url); + const paucidParam = url.searchParams.get("paucid"); + const bodyArray = JSON.parse(request.requestBody); + const payloadDecoded = JSON.parse(atob(bodyArray[0])); + + expect(request.requestBody).to.be.a('string'); + expect(JSON.parse(paucidParam)).to.deep.equal([partnerAuctionId]); + expect(payloadDecoded.partnerAuctionId).to.equal(partnerAuctionId); + }); }); diff --git a/test/spec/modules/intentIqIdSystem_spec.js b/test/spec/modules/intentIqIdSystem_spec.js index 4b9afc38146..61f87168ac8 100644 --- a/test/spec/modules/intentIqIdSystem_spec.js +++ b/test/spec/modules/intentIqIdSystem_spec.js @@ -7,13 +7,14 @@ import { handleClientHints, firstPartyData as moduleFPD, isCMPStringTheSame, createPixelUrl, translateMetadata, - initializeGlobalIIQ + initializeGlobalIIQ, + setGamReporting } from '../../../modules/intentIqIdSystem.js'; import { storage, readData, storeData } from '../../../libraries/intentIqUtils/storageUtils.js'; import { gppDataHandler, uspDataHandler, gdprDataHandler } from '../../../src/consentHandler.js'; import { clearAllCookies } from '../../helpers/cookies.js'; import { detectBrowser, detectBrowserFromUserAgent, detectBrowserFromUserAgentData } from '../../../libraries/intentIqUtils/detectBrowserUtils.js'; -import {CLIENT_HINTS_KEY, FIRST_PARTY_KEY, PREBID, WITH_IIQ, WITHOUT_IIQ} from '../../../libraries/intentIqConstants/intentIqConstants.js'; +import { CLIENT_HINTS_KEY, FIRST_PARTY_KEY, PREBID, WITH_IIQ, WITHOUT_IIQ } from '../../../libraries/intentIqConstants/intentIqConstants.js'; import { decryptData } from '../../../libraries/intentIqUtils/cryptionUtils.js'; import { isCHSupported } from '../../../libraries/intentIqUtils/chUtils.js'; @@ -123,6 +124,20 @@ const mockGAM = () => { }; }; +const regionCases = [ + { name: 'no region (default)', region: undefined, expected: 'https://api.intentiq.com' }, + { name: 'apac', region: 'apac', expected: 'https://api-apac.intentiq.com' }, + { name: 'emea', region: 'emea', expected: 'https://api-emea.intentiq.com' }, + { name: 'gdpr', region: 'gdpr', expected: 'https://api-gdpr.intentiq.com' } +]; + +const syncRegionCases = [ + { name: 'default', region: undefined, expected: 'https://sync.intentiq.com' }, + { name: 'apac', region: 'apac', expected: 'https://sync-apac.intentiq.com' }, + { name: 'emea', region: 'emea', expected: 'https://sync-emea.intentiq.com' }, + { name: 'gdpr', region: 'gdpr', expected: 'https://sync-gdpr.intentiq.com' }, +]; + describe('IntentIQ tests', function () { this.timeout(10000); let sandbox; @@ -174,7 +189,7 @@ describe('IntentIQ tests', function () { it('should create global IIQ identity object', async () => { const globalName = `iiq_identity_${partner}` const callBackSpy = sinon.spy(); - const submoduleCallback = intentIqIdSubmodule.getId({ params: { partner }}).callback; + const submoduleCallback = intentIqIdSubmodule.getId({ params: { partner } }).callback; submoduleCallback(callBackSpy); await waitForClientHints() expect(window[globalName]).to.be.not.undefined @@ -183,7 +198,7 @@ describe('IntentIQ tests', function () { }) it('should not create a global IIQ identity object in case it was already created', () => { - intentIqIdSubmodule.getId({ params: { partner }}) + intentIqIdSubmodule.getId({ params: { partner } }) const secondTimeCalling = initializeGlobalIIQ(partner) expect(secondTimeCalling).to.be.false }) @@ -206,6 +221,45 @@ describe('IntentIQ tests', function () { expect(submodule).to.be.undefined; }); + it('should use setConfig when available in setGamReporting', function () { + const setConfigSpy = sinon.spy(); + const pubadsSetTargetingSpy = sinon.spy(); + const mockGAM = { + cmd: [], + setConfig: setConfigSpy, + pubads: () => ({ + setTargeting: pubadsSetTargetingSpy + }) + }; + + setGamReporting(mockGAM, 'intent_iq_group', 'A'); + mockGAM.cmd.forEach((fn) => fn()); + + expect(setConfigSpy.calledOnce).to.equal(true); + expect(setConfigSpy.firstCall.args[0]).to.deep.equal({ + targeting: { + intent_iq_group: 'A' + } + }); + expect(pubadsSetTargetingSpy.called).to.equal(false); + }); + + it('should fall back to pubads.setTargeting when setConfig is missing', function () { + const pubadsSetTargetingSpy = sinon.spy(); + const mockGAM = { + cmd: [], + pubads: () => ({ + setTargeting: pubadsSetTargetingSpy + }) + }; + + setGamReporting(mockGAM, 'intent_iq_group', 'B'); + mockGAM.cmd.forEach((fn) => fn()); + + expect(pubadsSetTargetingSpy.calledOnce).to.equal(true); + expect(pubadsSetTargetingSpy.firstCall.args).to.deep.equal(['intent_iq_group', 'B']); + }); + it('should not save data in cookie if relevant type not set', async function () { const callBackSpy = sinon.spy(); const submoduleCallback = intentIqIdSubmodule.getId(defaultConfigParams).callback; @@ -240,7 +294,7 @@ describe('IntentIQ tests', function () { const cookieValue = storage.getCookie('_iiq_fdata_' + partner); expect(cookieValue).to.not.equal(null); const decryptedData = JSON.parse(decryptData(JSON.parse(cookieValue).data)); - expect(decryptedData).to.deep.equal({eids: ['test_personid']}); + expect(decryptedData).to.deep.equal({ eids: ['test_personid'] }); }); it('should call the IntentIQ endpoint with only partner', async function () { @@ -266,10 +320,11 @@ describe('IntentIQ tests', function () { it('should send AT=20 request and send source in it', async function () { const usedBrowser = 'chrome'; - intentIqIdSubmodule.getId({params: { - partner: 10, - browserBlackList: usedBrowser - } + intentIqIdSubmodule.getId({ + params: { + partner: 10, + browserBlackList: usedBrowser + } }); const currentBrowserLowerCase = detectBrowser(); @@ -538,7 +593,7 @@ describe('IntentIQ tests', function () { JSON.stringify({ pid: 'test_pid', data: 'test_personid', ls: false }) ); expect(callBackSpy.calledOnce).to.be.true; - expect(callBackSpy.args[0][0]).to.deep.equal({eids: []}); + expect(callBackSpy.args[0][0]).to.deep.equal({ eids: [] }); }); it('send addition parameters if were found in localstorage', async function () { @@ -597,12 +652,13 @@ describe('IntentIQ tests', function () { it('should send AT=20 request and send spd in it', async function () { const spdValue = { foo: 'bar', value: 42 }; const encodedSpd = encodeURIComponent(JSON.stringify(spdValue)); - localStorage.setItem(FIRST_PARTY_KEY, JSON.stringify({pcid: '123', spd: spdValue})); + localStorage.setItem(FIRST_PARTY_KEY + '_' + partner, JSON.stringify({ pcid: '123', spd: spdValue })); - intentIqIdSubmodule.getId({params: { - partner: 10, - browserBlackList: 'chrome' - } + intentIqIdSubmodule.getId({ + params: { + partner: 10, + browserBlackList: 'chrome' + } }); await waitForClientHints(); @@ -615,12 +671,13 @@ describe('IntentIQ tests', function () { it('should send AT=20 request and send spd string in it ', async function () { const spdValue = 'server provided data'; const encodedSpd = encodeURIComponent(spdValue); - localStorage.setItem(FIRST_PARTY_KEY, JSON.stringify({pcid: '123', spd: spdValue})); + localStorage.setItem(FIRST_PARTY_KEY + '_' + partner, JSON.stringify({ pcid: '123', spd: spdValue })); - intentIqIdSubmodule.getId({params: { - partner: 10, - browserBlackList: 'chrome' - } + intentIqIdSubmodule.getId({ + params: { + partner: 10, + browserBlackList: 'chrome' + } }); await waitForClientHints(); @@ -634,7 +691,7 @@ describe('IntentIQ tests', function () { const spdValue = { foo: 'bar', value: 42 }; const encodedSpd = encodeURIComponent(JSON.stringify(spdValue)); - localStorage.setItem(FIRST_PARTY_KEY, JSON.stringify({ pcid: '123', spd: spdValue })); + localStorage.setItem(FIRST_PARTY_KEY + '_' + partner, JSON.stringify({ pcid: '123', spd: spdValue })); const callBackSpy = sinon.spy(); const submoduleCallback = intentIqIdSubmodule.getId(defaultConfigParams).callback; @@ -650,7 +707,7 @@ describe('IntentIQ tests', function () { it('should send spd string from firstPartyData in localStorage in at=39 request', async function () { const spdValue = 'spd string'; const encodedSpd = encodeURIComponent(spdValue); - localStorage.setItem(FIRST_PARTY_KEY, JSON.stringify({ pcid: '123', spd: spdValue })); + localStorage.setItem(FIRST_PARTY_KEY + '_' + partner, JSON.stringify({ pcid: '123', spd: spdValue })); const callBackSpy = sinon.spy(); const submoduleCallback = intentIqIdSubmodule.getId(defaultConfigParams).callback; @@ -676,7 +733,7 @@ describe('IntentIQ tests', function () { JSON.stringify({ pid: 'test_pid', data: 'test_personid', ls: true, spd: spdValue }) ); - const storedLs = readData(FIRST_PARTY_KEY, ['html5', 'cookie'], storage); + const storedLs = readData(FIRST_PARTY_KEY + '_' + partner, ['html5', 'cookie'], storage); const parsedLs = JSON.parse(storedLs); expect(storedLs).to.not.be.null; @@ -738,7 +795,7 @@ describe('IntentIQ tests', function () { expect(result).to.equal('unknown'); }); - it("Should call the server for new partner if FPD has been updated by other partner, and 24 hours have not yet passed.", async () => { + it("Should call the server for new partner if FPD has been updated by other partner, and 72 hours have not yet passed.", async () => { const allowedStorage = ['html5'] const newPartnerId = 12345 const FPD = { @@ -752,14 +809,14 @@ describe('IntentIQ tests', function () { storeData(FIRST_PARTY_KEY, JSON.stringify(FPD), allowedStorage, storage) const callBackSpy = sinon.spy() - const submoduleCallback = intentIqIdSubmodule.getId({...allConfigParams, params: {...allConfigParams.params, partner: newPartnerId}}).callback; + const submoduleCallback = intentIqIdSubmodule.getId({ ...allConfigParams, params: { ...allConfigParams.params, partner: newPartnerId } }).callback; submoduleCallback(callBackSpy); await waitForClientHints(); const request = server.requests[0]; expect(request.url).contain("ProfilesEngineServlet?at=39") // server was called }) - it("Should NOT call the server if FPD has been updated user Opted Out, and 24 hours have not yet passed.", async () => { + it("Should NOT call the server if FPD has been updated user Opted Out, and 72 hours have not yet passed.", async () => { const allowedStorage = ['html5'] const newPartnerId = 12345 const FPD = { @@ -773,7 +830,7 @@ describe('IntentIQ tests', function () { }; storeData(FIRST_PARTY_KEY, JSON.stringify(FPD), allowedStorage, storage) - const returnedObject = intentIqIdSubmodule.getId({...allConfigParams, params: {...allConfigParams.params, partner: newPartnerId}}); + const returnedObject = intentIqIdSubmodule.getId({ ...allConfigParams, params: { ...allConfigParams.params, partner: newPartnerId } }); await waitForClientHints(); expect(returnedObject.callback).to.be.undefined expect(server.requests.length).to.equal(0) // no server requests @@ -842,7 +899,7 @@ describe('IntentIQ tests', function () { const callBackSpy = sinon.spy(); const submoduleCallback = intentIqIdSubmodule.getId(defaultConfigParams).callback; - const data = {eids: {key1: 'value1', key2: 'value2'}} + const data = { eids: { key1: 'value1', key2: 'value2' } } submoduleCallback(callBackSpy); await waitForClientHints(); @@ -870,7 +927,7 @@ describe('IntentIQ tests', function () { it('should clear localStorage, update runtimeEids and trigger callback with empty data if isOptedOut is true in response', async function () { // Save some data to localStorage for FPD and CLIENT_HINTS const FIRST_PARTY_DATA_KEY = FIRST_PARTY_KEY + '_' + partner; - localStorage.setItem(FIRST_PARTY_DATA_KEY, JSON.stringify({terminationCause: 35, some_key: 'someValue'})); + localStorage.setItem(FIRST_PARTY_DATA_KEY, JSON.stringify({ terminationCause: 35, some_key: 'someValue' })); localStorage.setItem(CLIENT_HINTS_KEY, JSON.stringify({ hint: 'someClientHintData' })); mockConsentHandlers(uspData, gppData, gdprData); @@ -885,7 +942,7 @@ describe('IntentIQ tests', function () { request.respond( 200, responseHeader, - JSON.stringify({isOptedOut: true}) + JSON.stringify({ isOptedOut: true }) ); // Check that the URL contains the expected consent data @@ -906,23 +963,15 @@ describe('IntentIQ tests', function () { expect(callbackArgument).to.deep.equal({ eids: [] }); // Ensure that runtimeEids was updated to { eids: [] } }); - it('should make request to correct address api-gdpr.intentiq.com if gdpr is detected', async function() { - const ENDPOINT_GDPR = 'https://api-gdpr.intentiq.com'; - mockConsentHandlers(uspData, gppData, gdprData); - const callBackSpy = sinon.spy(); - const submoduleCallback = intentIqIdSubmodule.getId({...defaultConfigParams}).callback; - - submoduleCallback(callBackSpy); - await waitForClientHints(); - const request = server.requests[0]; - - expect(request.url).to.contain(ENDPOINT_GDPR); - }); - it('should make request to correct address with iiqServerAddress parameter', async function() { - defaultConfigParams.params.iiqServerAddress = testAPILink + const customParams = { + params: { + ...defaultConfigParams.params, + iiqServerAddress: testAPILink + } + }; const callBackSpy = sinon.spy(); - const submoduleCallback = intentIqIdSubmodule.getId({...defaultConfigParams}).callback; + const submoduleCallback = intentIqIdSubmodule.getId({ ...customParams }).callback; submoduleCallback(callBackSpy); await waitForClientHints(); @@ -933,22 +982,77 @@ describe('IntentIQ tests', function () { it('should make request to correct address with iiqPixelServerAddress parameter', async function() { let wasCallbackCalled = false - const callbackConfigParams = { params: { partner: partner, - pai, - partnerClientIdType, - partnerClientId, - browserBlackList: 'Chrome', - iiqPixelServerAddress: syncTestAPILink, - callback: () => { - wasCallbackCalled = true - } } }; + const callbackConfigParams = { + params: { + partner: partner, + pai, + partnerClientIdType, + partnerClientId, + browserBlackList: 'Chrome', + iiqPixelServerAddress: syncTestAPILink, + callback: () => { + wasCallbackCalled = true + } + } + }; - intentIqIdSubmodule.getId({...callbackConfigParams}); + intentIqIdSubmodule.getId({ ...callbackConfigParams }); await waitForClientHints(); const request = server.requests[0]; expect(request.url).to.contain(syncTestAPILink); }); + + regionCases.forEach(({ name, region, expected }) => { + it(`should use region-specific api endpoint when region is "${name}"`, async function () { + mockConsentHandlers(uspData, gppData, gdprData); // gdprApplies = true + + const callBackSpy = sinon.spy(); + const configWithRegion = { + params: { + ...defaultConfigParams.params, + region + } + }; + + const submoduleCallback = intentIqIdSubmodule.getId(configWithRegion).callback; + submoduleCallback(callBackSpy); + await waitForClientHints(); + + const request = server.requests[0]; + expect(request.url).to.contain(expected); + }); + }); + + syncRegionCases.forEach(({ name, region, expected }) => { + it(`should use region-specific sync endpoint when region is "${name}"`, async function () { + let wasCallbackCalled = false; + + const callbackConfigParams = { + params: { + partner, + pai, + partnerClientIdType, + partnerClientId, + browserBlackList: 'Chrome', + region, + callback: () => { + wasCallbackCalled = true; + } + } + }; + + mockConsentHandlers(uspData, gppData, gdprData); + + intentIqIdSubmodule.getId(callbackConfigParams); + + await waitForClientHints(); + + const request = server.requests[0]; + expect(request.url).to.contain(expected); + expect(wasCallbackCalled).to.equal(true); + }); + }); }); it('should get and save client hints to storage', async () => { @@ -1193,14 +1297,18 @@ describe('IntentIQ tests', function () { it('should run callback from params', async () => { let wasCallbackCalled = false - const callbackConfigParams = { params: { partner: partner, - pai, - partnerClientIdType, - partnerClientId, - browserBlackList: 'Chrome', - callback: () => { - wasCallbackCalled = true - } } }; + const callbackConfigParams = { + params: { + partner: partner, + pai, + partnerClientIdType, + partnerClientId, + browserBlackList: 'Chrome', + callback: () => { + wasCallbackCalled = true + } + } + }; await intentIqIdSubmodule.getId(callbackConfigParams); expect(wasCallbackCalled).to.equal(true); @@ -1221,7 +1329,7 @@ describe('IntentIQ tests', function () { it('should NOT send sourceMetaData and sourceMetaDataExternal in AT=39 if it is undefined', async function () { const callBackSpy = sinon.spy(); - const configParams = { params: {...allConfigParams.params, sourceMetaData: undefined} }; + const configParams = { params: { ...allConfigParams.params, sourceMetaData: undefined } }; const submoduleCallback = intentIqIdSubmodule.getId(configParams).callback; submoduleCallback(callBackSpy); await waitForClientHints() @@ -1234,7 +1342,7 @@ describe('IntentIQ tests', function () { it('should NOT send sourceMetaData in AT=39 if value is NAN', async function () { const callBackSpy = sinon.spy(); - const configParams = { params: {...allConfigParams.params, sourceMetaData: NaN} }; + const configParams = { params: { ...allConfigParams.params, sourceMetaData: NaN } }; const submoduleCallback = intentIqIdSubmodule.getId(configParams).callback; submoduleCallback(callBackSpy); await waitForClientHints() @@ -1246,7 +1354,7 @@ describe('IntentIQ tests', function () { it('should send sourceMetaData in AT=20 if it exists in configParams', async function () { const translatedMetaDataValue = translateMetadata(sourceMetaData) - const configParams = { params: {...allConfigParams.params, browserBlackList: 'chrome'} }; + const configParams = { params: { ...allConfigParams.params, browserBlackList: 'chrome' } }; intentIqIdSubmodule.getId(configParams); await waitForClientHints() @@ -1257,7 +1365,7 @@ describe('IntentIQ tests', function () { }); it('should NOT send sourceMetaData in AT=20 if value is NAN', async function () { - const configParams = { params: {...allConfigParams.params, sourceMetaData: NaN, browserBlackList: 'chrome'} }; + const configParams = { params: { ...allConfigParams.params, sourceMetaData: NaN, browserBlackList: 'chrome' } }; intentIqIdSubmodule.getId(configParams); await waitForClientHints() @@ -1270,7 +1378,7 @@ describe('IntentIQ tests', function () { it('should send pcid and idtype in AT=20 if it provided in config', async function () { const partnerClientId = 'partnerClientId 123'; const partnerClientIdType = 0; - const configParams = { params: {...allConfigParams.params, browserBlackList: 'chrome', partnerClientId, partnerClientIdType} }; + const configParams = { params: { ...allConfigParams.params, browserBlackList: 'chrome', partnerClientId, partnerClientIdType } }; intentIqIdSubmodule.getId(configParams); await waitForClientHints() @@ -1284,7 +1392,7 @@ describe('IntentIQ tests', function () { it('should NOT send pcid and idtype in AT=20 if partnerClientId is NOT a string', async function () { const partnerClientId = 123; const partnerClientIdType = 0; - const configParams = { params: {...allConfigParams.params, browserBlackList: 'chrome', partnerClientId, partnerClientIdType} }; + const configParams = { params: { ...allConfigParams.params, browserBlackList: 'chrome', partnerClientId, partnerClientIdType } }; intentIqIdSubmodule.getId(configParams); await waitForClientHints() @@ -1298,7 +1406,7 @@ describe('IntentIQ tests', function () { it('should NOT send pcid and idtype in AT=20 if partnerClientIdType is NOT a number', async function () { const partnerClientId = 'partnerClientId 123'; const partnerClientIdType = 'wrong'; - const configParams = { params: {...allConfigParams.params, browserBlackList: 'chrome', partnerClientId, partnerClientIdType} }; + const configParams = { params: { ...allConfigParams.params, browserBlackList: 'chrome', partnerClientId, partnerClientIdType } }; intentIqIdSubmodule.getId(configParams); await waitForClientHints() @@ -1313,7 +1421,7 @@ describe('IntentIQ tests', function () { const partnerClientId = 'partnerClientId 123'; const partnerClientIdType = 0; const callBackSpy = sinon.spy(); - const configParams = { params: {...allConfigParams.params, partnerClientId, partnerClientIdType} }; + const configParams = { params: { ...allConfigParams.params, partnerClientId, partnerClientIdType } }; const submoduleCallback = intentIqIdSubmodule.getId(configParams).callback; submoduleCallback(callBackSpy); await waitForClientHints() @@ -1329,7 +1437,7 @@ describe('IntentIQ tests', function () { const partnerClientId = 123; const partnerClientIdType = 0; const callBackSpy = sinon.spy(); - const configParams = { params: {...allConfigParams.params, partnerClientId, partnerClientIdType} }; + const configParams = { params: { ...allConfigParams.params, partnerClientId, partnerClientIdType } }; const submoduleCallback = intentIqIdSubmodule.getId(configParams).callback; submoduleCallback(callBackSpy); await waitForClientHints() @@ -1345,7 +1453,7 @@ describe('IntentIQ tests', function () { const partnerClientId = 'partnerClientId-123'; const partnerClientIdType = 'wrong'; const callBackSpy = sinon.spy(); - const configParams = { params: {...allConfigParams.params, partnerClientId, partnerClientIdType} }; + const configParams = { params: { ...allConfigParams.params, partnerClientId, partnerClientIdType } }; const submoduleCallback = intentIqIdSubmodule.getId(configParams).callback; submoduleCallback(callBackSpy); await waitForClientHints() @@ -1358,7 +1466,7 @@ describe('IntentIQ tests', function () { }); it('should NOT send sourceMetaData in AT=20 if sourceMetaDataExternal provided', async function () { - const configParams = { params: {...allConfigParams.params, browserBlackList: 'chrome', sourceMetaDataExternal: 123} }; + const configParams = { params: { ...allConfigParams.params, browserBlackList: 'chrome', sourceMetaDataExternal: 123 } }; intentIqIdSubmodule.getId(configParams); await waitForClientHints() @@ -1369,7 +1477,7 @@ describe('IntentIQ tests', function () { }); it('should store first party data under the silo key when siloEnabled is true', async function () { - const configParams = { params: {...allConfigParams.params, siloEnabled: true} }; + const configParams = { params: { ...allConfigParams.params, siloEnabled: true } }; intentIqIdSubmodule.getId(configParams); await waitForClientHints() @@ -1384,7 +1492,7 @@ describe('IntentIQ tests', function () { it('should send siloEnabled value in the request', async function () { const callBackSpy = sinon.spy(); - const configParams = { params: {...allConfigParams.params, siloEnabled: true} }; + const configParams = { params: { ...allConfigParams.params, siloEnabled: true } }; const submoduleCallback = intentIqIdSubmodule.getId(configParams).callback; submoduleCallback(callBackSpy); await waitForClientHints() diff --git a/test/spec/modules/interactiveOffersBidAdapter_spec.js b/test/spec/modules/interactiveOffersBidAdapter_spec.js index 300e800bc0c..7283d8ceab2 100644 --- a/test/spec/modules/interactiveOffersBidAdapter_spec.js +++ b/test/spec/modules/interactiveOffersBidAdapter_spec.js @@ -1,9 +1,9 @@ import { expect } from 'chai'; -import {spec} from 'modules/interactiveOffersBidAdapter.js'; +import { spec } from 'modules/interactiveOffersBidAdapter.js'; describe('Interactive Offers Prebbid.js Adapter', function() { describe('isBidRequestValid function', function() { - const bid = {bidder: 'interactiveOffers', params: {partnerId: '100', tmax: 300}, mediaTypes: {banner: {sizes: [[300, 250]]}}, adUnitCode: 'pageAd01', transactionId: '16526f30-3be2-43f6-ab37-f1ab1f2ac25d', sizes: [[300, 250]], bidId: '227faa83f86546', bidderRequestId: '1eb79bc9dd44a', auctionId: '1aad860c-e04b-482b-acac-0da55ed491c8', src: 'client', bidRequestsCount: 1, bidderRequestsCount: 1, bidderWinsCount: 0}; + const bid = { bidder: 'interactiveOffers', params: { partnerId: '100', tmax: 300 }, mediaTypes: { banner: { sizes: [[300, 250]] } }, adUnitCode: 'pageAd01', transactionId: '16526f30-3be2-43f6-ab37-f1ab1f2ac25d', sizes: [[300, 250]], bidId: '227faa83f86546', bidderRequestId: '1eb79bc9dd44a', auctionId: '1aad860c-e04b-482b-acac-0da55ed491c8', src: 'client', bidRequestsCount: 1, bidderRequestsCount: 1, bidderWinsCount: 0 }; it('returns true if all the required params are present and properly formatted', function() { expect(spec.isBidRequestValid(bid)).to.be.true; @@ -15,15 +15,15 @@ describe('Interactive Offers Prebbid.js Adapter', function() { }); it('returns false if any if the required params is not properly formatted', function() { - bid.params = {partnerid: '100', tmax: 250}; + bid.params = { partnerid: '100', tmax: 250 }; expect(spec.isBidRequestValid(bid)).to.be.false; - bid.params = {partnerId: '100', tmax: '+250'}; + bid.params = { partnerId: '100', tmax: '+250' }; expect(spec.isBidRequestValid(bid)).to.be.false; }); }); describe('buildRequests function', function() { - const validBidRequests = [{bidder: 'interactiveOffers', params: {partnerId: '100', tmax: 300}, mediaTypes: {banner: {sizes: [[300, 250]]}}, adUnitCode: 'pageAd01', transactionId: '16526f30-3be2-43f6-ab37-f1ab1f2ac25d', sizes: [[300, 250]], bidId: '227faa83f86546', bidderRequestId: '1eb79bc9dd44a', auctionId: '1aad860c-e04b-482b-acac-0da55ed491c8', src: 'client', bidRequestsCount: 1, bidderRequestsCount: 1, bidderWinsCount: 0}]; - const bidderRequest = {bidderCode: 'interactiveOffers', auctionId: '1aad860c-e04b-482b-acac-0da55ed491c8', bidderRequestId: '1eb79bc9dd44a', bids: [{bidder: 'interactiveOffers', params: {partnerId: '100', tmax: 300}, mediaTypes: {banner: {sizes: [[300, 250]]}}, adUnitCode: 'pageAd01', transactionId: '16526f30-3be2-43f6-ab37-f1ab1f2ac25d', sizes: [[300, 250]], bidId: '227faa83f86546', bidderRequestId: '1eb79bc9dd44a', auctionId: '1aad860c-e04b-482b-acac-0da55ed491c8', src: 'client', bidRequestsCount: 1, bidderRequestsCount: 1, bidderWinsCount: 0}], timeout: 5000, refererInfo: {referer: 'http://www.google.com', reachedTop: true, isAmp: false, numIframes: 0, stack: ['http://www.google.com'], canonicalUrl: null}}; + const validBidRequests = [{ bidder: 'interactiveOffers', params: { partnerId: '100', tmax: 300 }, mediaTypes: { banner: { sizes: [[300, 250]] } }, adUnitCode: 'pageAd01', transactionId: '16526f30-3be2-43f6-ab37-f1ab1f2ac25d', sizes: [[300, 250]], bidId: '227faa83f86546', bidderRequestId: '1eb79bc9dd44a', auctionId: '1aad860c-e04b-482b-acac-0da55ed491c8', src: 'client', bidRequestsCount: 1, bidderRequestsCount: 1, bidderWinsCount: 0 }]; + const bidderRequest = { bidderCode: 'interactiveOffers', auctionId: '1aad860c-e04b-482b-acac-0da55ed491c8', bidderRequestId: '1eb79bc9dd44a', bids: [{ bidder: 'interactiveOffers', params: { partnerId: '100', tmax: 300 }, mediaTypes: { banner: { sizes: [[300, 250]] } }, adUnitCode: 'pageAd01', transactionId: '16526f30-3be2-43f6-ab37-f1ab1f2ac25d', sizes: [[300, 250]], bidId: '227faa83f86546', bidderRequestId: '1eb79bc9dd44a', auctionId: '1aad860c-e04b-482b-acac-0da55ed491c8', src: 'client', bidRequestsCount: 1, bidderRequestsCount: 1, bidderWinsCount: 0 }], timeout: 5000, refererInfo: { referer: 'http://www.google.com', reachedTop: true, isAmp: false, numIframes: 0, stack: ['http://www.google.com'], canonicalUrl: null } }; it('returns a Prebid.js request object with a valid json string at the "data" property', function() { const request = spec.buildRequests(validBidRequests, bidderRequest); @@ -31,8 +31,8 @@ describe('Interactive Offers Prebbid.js Adapter', function() { }); }); describe('interpretResponse function', function() { - const openRTBResponse = {body: [{cur: 'USD', id: '2052afa35febb79baa9893cc3ae8b83b89740df65fe98b1bd358dbae6e912801', seatbid: [{seat: 1493, bid: [{ext: {tagid: '227faa83f86546'}, crid: '24477', adm: '', nurl: '', adid: '1138', adomain: ['url.com'], price: '1.53', w: 300, h: 250, iurl: 'http://url.com', cat: ['IAB13-11'], id: '5507ced7a39c06942d3cb260197112ba712e4180', attr: [], impid: 1, cid: '13280'}]}], 'bidid': '0959b9d58ba71b3db3fa29dce3b117c01fc85de0'}], 'headers': {}}; - const prebidRequest = {method: 'POST', url: 'https://url.com', data: '{"id": "1aad860c-e04b-482b-acac-0da55ed491c8", "site": {"id": "url.com", "name": "url.com", "domain": "url.com", "page": "http://url.com", "ref": "http://url.com", "publisher": {"id": 100, "name": "http://url.com", "domain": "url.com"}, "content": {"language": "pt-PT"}}, "source": {"fd": 0, "tid": "1aad860c-e04b-482b-acac-0da55ed491c8", "pchain": ""}, "device": {"ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 11_2_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.192 Safari/537.36", "language": "pt-PT"}, "user": {}, "imp": [{"id":1, "secure": 0, "tagid": "227faa83f86546", "banner": {"pos": 0, "w": 300, "h": 250, "format": [{"w": 300, "h": 250}]}}], "tmax": 300}', bidderRequest: {bidderCode: 'interactiveOffers', auctionId: '1aad860c-e04b-482b-acac-0da55ed491c8', bidderRequestId: '1eb79bc9dd44a', bids: [{bidder: 'interactiveOffers', params: {partnerId: '100', tmax: 300}, mediaTypes: {banner: {sizes: [[300, 250]]}}, adUnitCode: 'pageAd01', transactionId: '16526f30-3be2-43f6-ab37-f1ab1f2ac25d', sizes: [[300, 250]], bidId: '227faa83f86546', bidderRequestId: '1eb79bc9dd44a', auctionId: '1aad860c-e04b-482b-acac-0da55ed491c8', src: 'client', bidRequestsCount: 1, bidderRequestsCount: 1, bidderWinsCount: 0}], timeout: 5000, refererInfo: {referer: 'http://url.com', reachedTop: true, isAmp: false, numIframes: 0, stack: ['http://url.com'], canonicalUrl: null}}}; + const openRTBResponse = { body: [{ cur: 'USD', id: '2052afa35febb79baa9893cc3ae8b83b89740df65fe98b1bd358dbae6e912801', seatbid: [{ seat: 1493, bid: [{ ext: { tagid: '227faa83f86546' }, crid: '24477', adm: '', nurl: '', adid: '1138', adomain: ['url.com'], price: '1.53', w: 300, h: 250, iurl: 'http://url.com', cat: ['IAB13-11'], id: '5507ced7a39c06942d3cb260197112ba712e4180', attr: [], impid: 1, cid: '13280' }] }], 'bidid': '0959b9d58ba71b3db3fa29dce3b117c01fc85de0' }], 'headers': {} }; + const prebidRequest = { method: 'POST', url: 'https://url.com', data: '{"id": "1aad860c-e04b-482b-acac-0da55ed491c8", "site": {"id": "url.com", "name": "url.com", "domain": "url.com", "page": "http://url.com", "ref": "http://url.com", "publisher": {"id": 100, "name": "http://url.com", "domain": "url.com"}, "content": {"language": "pt-PT"}}, "source": {"fd": 0, "tid": "1aad860c-e04b-482b-acac-0da55ed491c8", "pchain": ""}, "device": {"ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 11_2_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.192 Safari/537.36", "language": "pt-PT"}, "user": {}, "imp": [{"id":1, "secure": 0, "tagid": "227faa83f86546", "banner": {"pos": 0, "w": 300, "h": 250, "format": [{"w": 300, "h": 250}]}}], "tmax": 300}', bidderRequest: { bidderCode: 'interactiveOffers', auctionId: '1aad860c-e04b-482b-acac-0da55ed491c8', bidderRequestId: '1eb79bc9dd44a', bids: [{ bidder: 'interactiveOffers', params: { partnerId: '100', tmax: 300 }, mediaTypes: { banner: { sizes: [[300, 250]] } }, adUnitCode: 'pageAd01', transactionId: '16526f30-3be2-43f6-ab37-f1ab1f2ac25d', sizes: [[300, 250]], bidId: '227faa83f86546', bidderRequestId: '1eb79bc9dd44a', auctionId: '1aad860c-e04b-482b-acac-0da55ed491c8', src: 'client', bidRequestsCount: 1, bidderRequestsCount: 1, bidderWinsCount: 0 }], timeout: 5000, refererInfo: { referer: 'http://url.com', reachedTop: true, isAmp: false, numIframes: 0, stack: ['http://url.com'], canonicalUrl: null } } }; it('returns an array of Prebid.js response objects', function() { const prebidResponses = spec.interpretResponse(openRTBResponse, prebidRequest); diff --git a/test/spec/modules/intersectionRtdProvider_spec.js b/test/spec/modules/intersectionRtdProvider_spec.js deleted file mode 100644 index d2885810b90..00000000000 --- a/test/spec/modules/intersectionRtdProvider_spec.js +++ /dev/null @@ -1,164 +0,0 @@ -import {config as _config, config} from 'src/config.js'; -import { expect } from 'chai'; -import * as events from 'src/events.js'; -import * as prebidGlobal from 'src/prebidGlobal.js'; -import { intersectionSubmodule } from 'modules/intersectionRtdProvider.js'; -import * as utils from 'src/utils.js'; -import {getGlobal} from 'src/prebidGlobal.js'; -import 'src/prebid.js'; - -describe('Intersection RTD Provider', function () { - let sandbox; - let placeholder; - const pbjs = getGlobal(); - const adUnit = { - code: 'ad-slot-1', - mediaTypes: { - banner: { - sizes: [ [300, 250] ] - } - }, - bids: [ - { - bidder: 'fake' - } - ] - }; - const providerConfig = {name: 'intersection', waitForIt: true}; - const rtdConfig = {realTimeData: {auctionDelay: 200, dataProviders: [providerConfig]}} - describe('IntersectionObserver not supported', function() { - beforeEach(function() { - sandbox = sinon.createSandbox(); - }); - afterEach(function() { - sandbox.restore(); - sandbox = undefined; - }); - it('init should return false', function () { - sandbox.stub(window, 'IntersectionObserver').value(undefined); - expect(intersectionSubmodule.init({})).is.false; - }); - }); - describe('IntersectionObserver supported', function() { - beforeEach(function() { - sandbox = sinon.createSandbox(); - placeholder = createDiv(); - append(); - const __config = {}; - sandbox.stub(_config, 'getConfig').callsFake(function (path) { - return utils.deepAccess(__config, path); - }); - sandbox.stub(_config, 'setConfig').callsFake(function (obj) { - utils.mergeDeep(__config, obj); - }); - sandbox.stub(window, 'IntersectionObserver').callsFake(function (cb) { - return { - observe(el) { - cb([ - { - target: el, - intersectionRatio: 1, - isIntersecting: true, - time: Date.now(), - boundingClientRect: {left: 0, top: 0, right: 0, bottom: 0, width: 0, height: 0, x: 0, y: 0}, - intersectionRect: {left: 0, top: 0, right: 0, bottom: 0, width: 0, height: 0, x: 0, y: 0}, - rootRect: {left: 0, top: 0, right: 0, bottom: 0, width: 0, height: 0, x: 0, y: 0} - } - ]); - }, - unobserve() {}, - disconnect() {} - }; - }); - }); - afterEach(function() { - sandbox.restore(); - remove(); - sandbox = undefined; - placeholder = undefined; - pbjs.removeAdUnit(); - }); - it('init should return true', function () { - expect(intersectionSubmodule.init({})).is.true; - }); - it('should set intersection. (request with "adUnitCodes")', function(done) { - pbjs.addAdUnits([utils.deepClone(adUnit)]); - config.setConfig(rtdConfig); - const onDone = sandbox.stub(); - const requestBidObject = {adUnitCodes: [adUnit.code]}; - intersectionSubmodule.init({}); - intersectionSubmodule.getBidRequestData( - requestBidObject, - onDone, - providerConfig - ); - setTimeout(function() { - expect(pbjs.adUnits[0].bids[0]).to.have.property('intersection'); - done(); - }, 200); - }); - it('should set intersection. (request with "adUnits")', function(done) { - config.setConfig(rtdConfig); - const onDone = sandbox.stub(); - const requestBidObject = {adUnits: [utils.deepClone(adUnit)]}; - intersectionSubmodule.init(); - intersectionSubmodule.getBidRequestData( - requestBidObject, - onDone, - providerConfig - ); - setTimeout(function() { - expect(requestBidObject.adUnits[0].bids[0]).to.have.property('intersection'); - done(); - }, 200); - }); - it('should set intersection. (request all)', function(done) { - pbjs.addAdUnits([utils.deepClone(adUnit)]); - config.setConfig(rtdConfig); - const onDone = sandbox.stub(); - const requestBidObject = {}; - intersectionSubmodule.init({}); - intersectionSubmodule.getBidRequestData( - requestBidObject, - onDone, - providerConfig - ); - setTimeout(function() { - expect(pbjs.adUnits[0].bids[0]).to.have.property('intersection'); - done(); - }, 200); - }); - it('should call done due timeout', function(done) { - config.setConfig(rtdConfig); - remove(); - const onDone = sandbox.stub(); - const requestBidObject = {adUnits: [utils.deepClone(adUnit)]}; - intersectionSubmodule.init({}); - intersectionSubmodule.getBidRequestData( - requestBidObject, - onDone, - {...providerConfig, test: 1} - ); - setTimeout(function() { - sinon.assert.calledOnce(onDone); - expect(requestBidObject.adUnits[0].bids[0]).to.not.have.property('intersection'); - done(); - }, 300); - }); - }); - function createDiv() { - const div = document.createElement('div'); - div.id = adUnit.code; - return div; - } - function append() { - if (placeholder) { - document.body.appendChild(placeholder); - } - } - function remove() { - if (placeholder && placeholder.parentElement) { - placeholder.parentElement.removeChild(placeholder); - } - } -}); diff --git a/test/spec/modules/invamiaBidAdapter_spec.js b/test/spec/modules/invamiaBidAdapter_spec.js index 2f8f0612e44..7d9367931e0 100644 --- a/test/spec/modules/invamiaBidAdapter_spec.js +++ b/test/spec/modules/invamiaBidAdapter_spec.js @@ -1,12 +1,12 @@ -import {expect} from 'chai'; -import {spec} from 'modules/invamiaBidAdapter.js'; +import { expect } from 'chai'; +import { spec } from 'modules/invamiaBidAdapter.js'; describe('invamia bid adapter tests', function () { describe('bid requests', function () { it('should accept valid bid', function () { const validBid = { bidder: 'invamia', - params: {zoneId: 123}, + params: { zoneId: 123 }, }; expect(spec.isBidRequestValid(validBid)).to.equal(true); @@ -24,7 +24,7 @@ describe('invamia bid adapter tests', function () { it('should correctly build payload string', function () { const bidRequests = [{ bidder: 'invamia', - params: {zoneId: 123}, + params: { zoneId: 123 }, mediaTypes: { banner: { sizes: [[300, 250]], @@ -46,7 +46,7 @@ describe('invamia bid adapter tests', function () { it('should support multiple bids', function () { const bidRequests = [{ bidder: 'invamia', - params: {zoneId: 123}, + params: { zoneId: 123 }, mediaTypes: { banner: { sizes: [[300, 250]], @@ -58,7 +58,7 @@ describe('invamia bid adapter tests', function () { transactionId: '92489f71-1bf2-49a0-adf9-000cea934729', }, { bidder: 'invamia', - params: {zoneId: 321}, + params: { zoneId: 321 }, mediaTypes: { banner: { sizes: [[728, 90]], @@ -77,7 +77,7 @@ describe('invamia bid adapter tests', function () { it('should support multiple sizes', function () { const bidRequests = [{ bidder: 'invamia', - params: {zoneId: 123}, + params: { zoneId: 123 }, mediaTypes: { banner: { sizes: [[300, 250], [300, 600]], @@ -118,7 +118,7 @@ describe('invamia bid adapter tests', function () { }, }; - const bids = spec.interpretResponse(serverResponse, {bidderRequest}); + const bids = spec.interpretResponse(serverResponse, { bidderRequest }); expect(bids).to.be.lengthOf(1); expect(bids[0].requestId).to.equal('23acc48ad47af5'); @@ -144,7 +144,7 @@ describe('invamia bid adapter tests', function () { }, }; - const bids = spec.interpretResponse(serverResponse, {bidderRequest}); + const bids = spec.interpretResponse(serverResponse, { bidderRequest }); expect(bids).to.be.lengthOf(0); }); @@ -164,7 +164,7 @@ describe('invamia bid adapter tests', function () { }, }; - const bids = spec.interpretResponse(serverResponse, {bidderRequest}); + const bids = spec.interpretResponse(serverResponse, { bidderRequest }); expect(bids).to.be.lengthOf(0); }); diff --git a/test/spec/modules/invibesBidAdapter_spec.js b/test/spec/modules/invibesBidAdapter_spec.js index c3c99788a70..66c5e3157ca 100644 --- a/test/spec/modules/invibesBidAdapter_spec.js +++ b/test/spec/modules/invibesBidAdapter_spec.js @@ -1,7 +1,7 @@ -import {expect} from 'chai'; +import { expect } from 'chai'; import { config } from 'src/config.js'; -import {spec, resetInvibes, stubDomainOptions, readGdprConsent, storage} from 'modules/invibesBidAdapter.js'; -import {getGlobal} from '../../../src/prebidGlobal.js'; +import { spec, resetInvibes, stubDomainOptions, readGdprConsent, storage } from 'modules/invibesBidAdapter.js'; +import { getGlobal } from '../../../src/prebidGlobal.js'; describe('invibesBidAdapter:', function () { const BIDDER_CODE = 'invibes'; @@ -250,7 +250,7 @@ describe('invibesBidAdapter:', function () { } } - top.window.invibes.bidResponse = {prop: 'prop'}; + top.window.invibes.bidResponse = { prop: 'prop' }; expect(spec.isBidRequestValid(validBid)).to.be.true; }); }); @@ -501,19 +501,19 @@ describe('invibesBidAdapter:', function () { }); it('sends query string params from localstorage 1', function () { - localStorage.ivbs = JSON.stringify({bvci: 1}); + localStorage.ivbs = JSON.stringify({ bvci: 1 }); const request = spec.buildRequests(bidRequests, bidderRequestWithPageInfo); expect(request.data.bvci).to.equal(1); }); it('sends query string params from localstorage 2', function () { - localStorage.ivbs = JSON.stringify({invibbvlog: true}); + localStorage.ivbs = JSON.stringify({ invibbvlog: true }); const request = spec.buildRequests(bidRequests, bidderRequestWithPageInfo); expect(request.data.invibbvlog).to.equal(true); }); it('does not send query string params from localstorage if unknwon', function () { - localStorage.ivbs = JSON.stringify({someparam: true}); + localStorage.ivbs = JSON.stringify({ someparam: true }); const request = spec.buildRequests(bidRequests, bidderRequestWithPageInfo); expect(request.data.someparam).to.be.undefined; }); @@ -620,7 +620,7 @@ describe('invibesBidAdapter:', function () { vendorData: { gdprApplies: true, hasGlobalConsent: false, - vendor: {consents: {436: true}}, + vendor: { consents: { 436: true } }, purpose: { consents: { 1: true, @@ -651,7 +651,7 @@ describe('invibesBidAdapter:', function () { vendorData: { gdprApplies: true, hasGlobalConsent: false, - vendor: {consents: {436: true}}, + vendor: { consents: { 436: true } }, purpose: { consents: { 1: true, @@ -682,7 +682,7 @@ describe('invibesBidAdapter:', function () { vendorData: { gdprApplies: true, hasGlobalConsent: false, - vendor: {consents: {436: true}}, + vendor: { consents: { 436: true } }, purpose: { consents: { 1: true, @@ -713,7 +713,7 @@ describe('invibesBidAdapter:', function () { vendorData: { gdprApplies: true, hasGlobalConsent: false, - vendor: {consents: {436: true}}, + vendor: { consents: { 436: true } }, purpose: { consents: { 1: true, @@ -768,7 +768,7 @@ describe('invibesBidAdapter:', function () { vendorData: { gdprApplies: true, hasGlobalConsent: false, - vendor: {consents: {436: true}}, + vendor: { consents: { 436: true } }, purpose: { consents: { 1: true, @@ -798,7 +798,7 @@ describe('invibesBidAdapter:', function () { vendorData: { gdprApplies: true, hasGlobalConsent: false, - vendor: {consents: {436: false}}, + vendor: { consents: { 436: false } }, purpose: { consents: { 1: true, @@ -828,7 +828,7 @@ describe('invibesBidAdapter:', function () { vendorData: { gdprApplies: true, hasGlobalConsent: false, - vendor: {consents: {436: false}, legitimateInterests: {436: true}}, + vendor: { consents: { 436: false }, legitimateInterests: { 436: true } }, purpose: { consents: { 1: true, @@ -858,7 +858,7 @@ describe('invibesBidAdapter:', function () { vendorData: { gdprApplies: true, hasGlobalConsent: false, - vendor: {consents: {436: false}, legitimateInterests: {436: false}}, + vendor: { consents: { 436: false }, legitimateInterests: { 436: false } }, purpose: { consents: { 1: true, @@ -888,7 +888,7 @@ describe('invibesBidAdapter:', function () { vendorData: { gdprApplies: true, hasGlobalConsent: false, - vendor: {consents: {436: false}}, + vendor: { consents: { 436: false } }, purpose: {} } }, @@ -906,7 +906,7 @@ describe('invibesBidAdapter:', function () { vendorData: { gdprApplies: true, hasGlobalConsent: false, - vendor: {consents: {436: true}}, + vendor: { consents: { 436: true } }, purpose: { consents: { 1: true, @@ -937,7 +937,7 @@ describe('invibesBidAdapter:', function () { vendorData: { gdprApplies: true, hasGlobalConsent: false, - vendor: {consents: {436: true}}, + vendor: { consents: { 436: true } }, purpose: { consents: { 1: true, @@ -963,7 +963,7 @@ describe('invibesBidAdapter:', function () { vendorData: { gdprApplies: true, hasGlobalConsent: false, - vendor: {consents: null}, + vendor: { consents: null }, purpose: { consents: { 1: true, @@ -994,7 +994,7 @@ describe('invibesBidAdapter:', function () { vendorData: { gdprApplies: true, hasGlobalConsent: false, - vendor: {consents: {436: null}}, + vendor: { consents: { 436: null } }, purpose: { consents: { 1: true, @@ -1025,7 +1025,7 @@ describe('invibesBidAdapter:', function () { vendorData: { gdprApplies: true, hasGlobalConsent: false, - vendorConsents: {436: null}, + vendorConsents: { 436: null }, purposeConsents: { 1: true, 2: true, @@ -1089,7 +1089,7 @@ describe('invibesBidAdapter:', function () { vendorData: { gdprApplies: true, hasGlobalConsent: false, - vendorConsents: {436: true}, + vendorConsents: { 436: true }, purposeConsents: { 1: true, 2: true, @@ -1113,7 +1113,7 @@ describe('invibesBidAdapter:', function () { vendorData: { gdprApplies: true, hasGlobalConsent: false, - vendorConsents: {436: true}, + vendorConsents: { 436: true }, purposeConsents: { 1: false, 2: false, @@ -1137,7 +1137,7 @@ describe('invibesBidAdapter:', function () { vendorData: { gdprApplies: true, hasGlobalConsent: false, - vendorConsents: {436: true}, + vendorConsents: { 436: true }, purposeConsents: { 1: false, 2: false, @@ -1159,7 +1159,7 @@ describe('invibesBidAdapter:', function () { vendorData: { gdprApplies: true, hasGlobalConsent: false, - vendorConsents: {436: false}, + vendorConsents: { 436: false }, purposeConsents: { 1: true, 2: true, @@ -1310,47 +1310,47 @@ describe('invibesBidAdapter:', function () { context('when the response is not valid', function () { it('handles response with no bids requested', function () { - const emptyResult = spec.interpretResponse({body: response}); + const emptyResult = spec.interpretResponse({ body: response }); expect(emptyResult).to.be.empty; }); it('handles empty response', function () { - const emptyResult = spec.interpretResponse(null, {bidRequests}); + const emptyResult = spec.interpretResponse(null, { bidRequests }); expect(emptyResult).to.be.empty; }); it('handles response with bidding is not configured', function () { - const emptyResult = spec.interpretResponse({body: {Ads: [{BidPrice: 1}]}}, {bidRequests}); + const emptyResult = spec.interpretResponse({ body: { Ads: [{ BidPrice: 1 }] } }, { bidRequests }); expect(emptyResult).to.be.empty; }); it('handles response with no ads are received', function () { const emptyResult = spec.interpretResponse({ body: { - BidModel: {PlacementId: '12345'}, + BidModel: { PlacementId: '12345' }, AdReason: 'No ads' } - }, {bidRequests}); + }, { bidRequests }); expect(emptyResult).to.be.empty; }); it('handles response with no ads are received - no ad reason', function () { - const emptyResult = spec.interpretResponse({body: {BidModel: {PlacementId: '12345'}}}, {bidRequests}); + const emptyResult = spec.interpretResponse({ body: { BidModel: { PlacementId: '12345' } } }, { bidRequests }); expect(emptyResult).to.be.empty; }); it('handles response when no placement Id matches', function () { const emptyResult = spec.interpretResponse({ body: { - BidModel: {PlacementId: '123456'}, - Ads: [{BidPrice: 1}] + BidModel: { PlacementId: '123456' }, + Ads: [{ BidPrice: 1 }] } - }, {bidRequests}); + }, { bidRequests }); expect(emptyResult).to.be.empty; }); it('handles response when placement Id is not present', function () { - const emptyResult = spec.interpretResponse({BidModel: {}, Ads: [{BidPrice: 1}]}, {bidRequests}); + const emptyResult = spec.interpretResponse({ BidModel: {}, Ads: [{ BidPrice: 1 }] }, { bidRequests }); expect(emptyResult).to.be.empty; }); @@ -1362,30 +1362,30 @@ describe('invibesBidAdapter:', function () { context('when the multiresponse is valid', function () { it('responds with a valid multiresponse bid', function () { - const result = spec.interpretResponse({body: multiResponse}, {bidRequests}); + const result = spec.interpretResponse({ body: multiResponse }, { bidRequests }); expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); }); it('responds with a valid singleresponse bid', function () { - const result = spec.interpretResponse({body: response}, {bidRequests}); + const result = spec.interpretResponse({ body: response }, { bidRequests }); expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); }); it('does not make multiple bids', function () { - const result = spec.interpretResponse({body: response}, {bidRequests}); - const secondResult = spec.interpretResponse({body: response}, {bidRequests}); + const result = spec.interpretResponse({ body: response }, { bidRequests }); + const secondResult = spec.interpretResponse({ body: response }, { bidRequests }); expect(secondResult).to.be.empty; }); it('bids using the adUnitCode', function () { - const result = spec.interpretResponse({body: responseWithAdUnit}, {bidRequests}); + const result = spec.interpretResponse({ body: responseWithAdUnit }, { bidRequests }); expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); }); }); context('when the response has meta', function () { it('responds with a valid bid, with the meta info', function () { - const result = spec.interpretResponse({body: responseWithMeta}, {bidRequests}); + const result = spec.interpretResponse({ body: responseWithMeta }, { bidRequests }); expect(result[0].meta.advertiserName).to.equal('theadvertiser'); expect(result[0].meta.advertiserDomains).to.contain('theadvertiser.com'); expect(result[0].meta.advertiserDomains).to.contain('theadvertiser_2.com'); @@ -1396,14 +1396,14 @@ describe('invibesBidAdapter:', function () { it('works when no LID is not sent from AdWeb', function() { var firstResponse = buildResponse('12345', 1, [], 123); - var firstResult = spec.interpretResponse({body: firstResponse}, {bidRequests}); + var firstResult = spec.interpretResponse({ body: firstResponse }, { bidRequests }); expect(firstResult[0].creativeId).to.equal(123); }); it('sets lid when AdWeb sends it', function() { var firstResponse = buildResponse('12345', 1, [], 123, true); - spec.interpretResponse({body: firstResponse}, {bidRequests}); + spec.interpretResponse({ body: firstResponse }, { bidRequests }); expect(global.document.cookie.indexOf('ivbsdid')).to.greaterThanOrEqual(0); }); }); @@ -1413,8 +1413,8 @@ describe('invibesBidAdapter:', function () { var firstResponse = buildResponse('12345', 1, [1], 123); var secondResponse = buildResponse('abcde', 2, [2], 456); - var firstResult = spec.interpretResponse({body: firstResponse}, {bidRequests}); - var secondResult = spec.interpretResponse({body: secondResponse}, {bidRequests}); + var firstResult = spec.interpretResponse({ body: firstResponse }, { bidRequests }); + var secondResult = spec.interpretResponse({ body: secondResponse }, { bidRequests }); expect(secondResult[0].creativeId).to.equal(456); }); @@ -1422,8 +1422,8 @@ describe('invibesBidAdapter:', function () { var firstResponse = buildResponse('12345', 1, [], 123); var secondResponse = buildResponse('abcde', 2, [], 456); - var firstResult = spec.interpretResponse({body: firstResponse}, {bidRequests}); - var secondResult = spec.interpretResponse({body: secondResponse}, {bidRequests}); + var firstResult = spec.interpretResponse({ body: firstResponse }, { bidRequests }); + var secondResult = spec.interpretResponse({ body: secondResponse }, { bidRequests }); expect(secondResult[0].creativeId).to.equal(456); }); @@ -1431,8 +1431,8 @@ describe('invibesBidAdapter:', function () { var firstResponse = buildResponse('12345', 1, [2], 123); var secondResponse = buildResponse('abcde', 2, [], 456); - var firstResult = spec.interpretResponse({body: firstResponse}, {bidRequests}); - var secondResult = spec.interpretResponse({body: secondResponse}, {bidRequests}); + var firstResult = spec.interpretResponse({ body: firstResponse }, { bidRequests }); + var secondResult = spec.interpretResponse({ body: secondResponse }, { bidRequests }); expect(secondResult).to.be.empty; }); @@ -1440,8 +1440,8 @@ describe('invibesBidAdapter:', function () { var firstResponse = buildResponse('12345', 1, [], 123); var secondResponse = buildResponse('abcde', 2, [1], 456); - var firstResult = spec.interpretResponse({body: firstResponse}, {bidRequests}); - var secondResult = spec.interpretResponse({body: secondResponse}, {bidRequests}); + var firstResult = spec.interpretResponse({ body: firstResponse }, { bidRequests }); + var secondResult = spec.interpretResponse({ body: secondResponse }, { bidRequests }); expect(secondResult).to.be.empty; }); @@ -1449,8 +1449,8 @@ describe('invibesBidAdapter:', function () { var firstResponse = buildResponse('12345', 1, [1], 123); var secondResponse = buildResponse('abcde', 1, [1], 456); - var firstResult = spec.interpretResponse({body: firstResponse}, {bidRequests}); - var secondResult = spec.interpretResponse({body: secondResponse}, {bidRequests}); + var firstResult = spec.interpretResponse({ body: firstResponse }, { bidRequests }); + var secondResult = spec.interpretResponse({ body: secondResponse }, { bidRequests }); expect(secondResult).to.be.empty; }); }); @@ -1459,14 +1459,14 @@ describe('invibesBidAdapter:', function () { describe('getUserSyncs', function () { it('returns undefined if disableUserSyncs not passed as bid request param ', function () { spec.buildRequests(bidRequestsWithUserId, bidderRequestWithPageInfo); - const response = spec.getUserSyncs({iframeEnabled: true}); + const response = spec.getUserSyncs({ iframeEnabled: true }); expect(response).to.equal(undefined); }); it('returns an iframe if enabled', function () { spec.buildRequests(bidRequests, bidderRequestWithPageInfo); - const response = spec.getUserSyncs({iframeEnabled: true}); + const response = spec.getUserSyncs({ iframeEnabled: true }); expect(response.type).to.equal('iframe'); expect(response.url).to.include(SYNC_ENDPOINT); }); @@ -1475,7 +1475,7 @@ describe('invibesBidAdapter:', function () { top.window.invibes.optIn = 1; spec.buildRequests(bidRequests, bidderRequestWithPageInfo); - const response = spec.getUserSyncs({iframeEnabled: true}); + const response = spec.getUserSyncs({ iframeEnabled: true }); expect(response.type).to.equal('iframe'); expect(response.url).to.include(SYNC_ENDPOINT); expect(response.url).to.include('optIn'); @@ -1488,7 +1488,7 @@ describe('invibesBidAdapter:', function () { global.document.cookie = 'ivbsdid={"id":"dvdjkams6nkq","cr":' + Date.now() + ',"hc":0}'; SetBidderAccess(); - const response = spec.getUserSyncs({iframeEnabled: true}); + const response = spec.getUserSyncs({ iframeEnabled: true }); expect(response.type).to.equal('iframe'); expect(response.url).to.include(SYNC_ENDPOINT); expect(response.url).to.include('optIn'); @@ -1502,7 +1502,7 @@ describe('invibesBidAdapter:', function () { localStorage.ivbsdid = 'dvdjkams6nkq'; SetBidderAccess(); - const response = spec.getUserSyncs({iframeEnabled: true}); + const response = spec.getUserSyncs({ iframeEnabled: true }); expect(response.type).to.equal('iframe'); expect(response.url).to.include(SYNC_ENDPOINT); expect(response.url).to.include('optIn'); @@ -1512,7 +1512,7 @@ describe('invibesBidAdapter:', function () { it('returns undefined if iframe not enabled ', function () { spec.buildRequests(bidRequests, bidderRequestWithPageInfo); - const response = spec.getUserSyncs({iframeEnabled: false}); + const response = spec.getUserSyncs({ iframeEnabled: false }); expect(response).to.equal(undefined); }); diff --git a/test/spec/modules/invisiblyAnalyticsAdapter_spec.js b/test/spec/modules/invisiblyAnalyticsAdapter_spec.js index 71182d146a0..af29701aa22 100644 --- a/test/spec/modules/invisiblyAnalyticsAdapter_spec.js +++ b/test/spec/modules/invisiblyAnalyticsAdapter_spec.js @@ -1,7 +1,7 @@ import invisiblyAdapter from 'modules/invisiblyAnalyticsAdapter.js'; import { expect } from 'chai'; -import {expectEvents} from '../../helpers/analytics.js'; -import {server} from '../../mocks/xhr.js'; +import { expectEvents } from '../../helpers/analytics.js'; +import { server } from '../../mocks/xhr.js'; import { EVENTS, STATUS } from 'src/constants.js'; const events = require('src/events'); @@ -161,7 +161,6 @@ describe('Invisibly Analytics Adapter test suite', function () { REQUEST_BIDS: { call: 'request', }, - ADD_AD_UNITS: { call: 'addAdUnits' }, AD_RENDER_FAILED: { call: 'adRenderFailed' }, INVALID_EVENT: { mockKey: 'this event should not emit', @@ -447,28 +446,6 @@ describe('Invisibly Analytics Adapter test suite', function () { sinon.assert.callCount(invisiblyAdapter.track, 1); }); - // spec for add ad units event - it('add ad units event', function () { - invisiblyAdapter.enableAnalytics(MOCK.config); - events.emit(EVENTS.ADD_AD_UNITS, MOCK.ADD_AD_UNITS); - invisiblyAdapter.flush(); - - const invisiblyEvents = JSON.parse( - requests[0].requestBody.substring(0) - ); - expect(requests.length).to.equal(1); - expect(requests[0].url).to.equal( - 'https://api.pymx5.com/v1/sites/events' - ); - expect(invisiblyEvents.event_data.pageViewId).to.exist; - expect(invisiblyEvents.event_data.ver).to.equal(1); - expect(invisiblyEvents.event_type).to.equal('PREBID_addAdUnits'); - expect(invisiblyEvents.event_data.call).to.equal( - MOCK.ADD_AD_UNITS.call - ); - sinon.assert.callCount(invisiblyAdapter.track, 1); - }); - // spec for ad render failed event it('ad render failed event', function () { invisiblyAdapter.enableAnalytics(MOCK.config); @@ -540,7 +517,6 @@ describe('Invisibly Analytics Adapter test suite', function () { EVENTS.BIDDER_DONE, EVENTS.SET_TARGETING, EVENTS.REQUEST_BIDS, - EVENTS.ADD_AD_UNITS, EVENTS.AD_RENDER_FAILED ]).to.beTrackedBy(invisiblyAdapter.track); }); diff --git a/test/spec/modules/ipromBidAdapter_spec.js b/test/spec/modules/ipromBidAdapter_spec.js index 3a1a6c972e1..3766499b0e6 100644 --- a/test/spec/modules/ipromBidAdapter_spec.js +++ b/test/spec/modules/ipromBidAdapter_spec.js @@ -1,5 +1,5 @@ -import {expect} from 'chai'; -import {spec} from 'modules/ipromBidAdapter.js'; +import { expect } from 'chai'; +import { spec } from 'modules/ipromBidAdapter.js'; describe('iPROM Adapter', function () { let bidRequests; @@ -169,7 +169,8 @@ describe('iPROM Adapter', function () { ad: 'Iprom Header bidding example', aDomains: ['https://example.com'], } - ]}; + ] + }; const request = spec.buildRequests(bidRequests, bidderRequest); const bids = spec.interpretResponse(serverResponse, request); diff --git a/test/spec/modules/iqxBidAdapter_spec.js b/test/spec/modules/iqxBidAdapter_spec.js index 8ca6fce841c..16d0b74e689 100644 --- a/test/spec/modules/iqxBidAdapter_spec.js +++ b/test/spec/modules/iqxBidAdapter_spec.js @@ -1,8 +1,8 @@ -import {expect} from 'chai'; -import {config} from 'src/config.js'; -import {spec} from 'modules/iqxBidAdapter.js'; -import {deepClone} from 'src/utils'; -import {getBidFloor} from '../../../libraries/xeUtils/bidderUtils.js'; +import { expect } from 'chai'; +import { config } from 'src/config.js'; +import { spec } from 'modules/iqxBidAdapter.js'; +import { deepClone } from 'src/utils'; +import { getBidFloor } from '../../../libraries/xeUtils/bidderUtils.js'; const ENDPOINT = 'https://pbjs.iqzonertb.live'; @@ -99,7 +99,7 @@ describe('iqxBidAdapter', () => { expect(request).to.have.property('tz').and.to.equal(new Date().getTimezoneOffset()); expect(request).to.have.property('bc').and.to.equal(1); expect(request).to.have.property('floor').and.to.equal(null); - expect(request).to.have.property('banner').and.to.deep.equal({sizes: [[300, 250], [300, 200]]}); + expect(request).to.have.property('banner').and.to.deep.equal({ sizes: [[300, 250], [300, 200]] }); expect(request).to.have.property('gdprConsent').and.to.deep.equal({}); expect(request).to.have.property('userEids').and.to.deep.equal([]); expect(request).to.have.property('usPrivacy').and.to.equal(''); @@ -192,7 +192,7 @@ describe('iqxBidAdapter', () => { it('should build request with valid bidfloor', function () { const bfRequest = deepClone(defaultRequest); - bfRequest.getFloor = () => ({floor: 5, currency: 'USD'}); + bfRequest.getFloor = () => ({ floor: 5, currency: 'USD' }); const request = JSON.parse(spec.buildRequests([bfRequest], {}).data)[0]; expect(request).to.have.property('floor').and.to.equal(5); }); @@ -208,8 +208,8 @@ describe('iqxBidAdapter', () => { it('should build request with extended ids', function () { const idRequest = deepClone(defaultRequest); idRequest.userIdAsEids = [ - {source: 'adserver.org', uids: [{id: 'TTD_ID_FROM_USER_ID_MODULE', atype: 1, ext: {rtiPartner: 'TDID'}}]}, - {source: 'pubcid.org', uids: [{id: 'pubCommonId_FROM_USER_ID_MODULE', atype: 1}]} + { source: 'adserver.org', uids: [{ id: 'TTD_ID_FROM_USER_ID_MODULE', atype: 1, ext: { rtiPartner: 'TDID' } }] }, + { source: 'pubcid.org', uids: [{ id: 'pubCommonId_FROM_USER_ID_MODULE', atype: 1 }] } ]; const request = JSON.parse(spec.buildRequests([idRequest], {}).data)[0]; expect(request).to.have.property('userEids').and.deep.equal(idRequest.userIdAsEids); @@ -261,7 +261,7 @@ describe('iqxBidAdapter', () => { } }; - const validResponse = spec.interpretResponse(serverResponse, {bidderRequest: defaultRequest}); + const validResponse = spec.interpretResponse(serverResponse, { bidderRequest: defaultRequest }); const bid = validResponse[0]; expect(validResponse).to.be.an('array').that.is.not.empty; expect(bid.requestId).to.equal('qwerty'); @@ -270,7 +270,7 @@ describe('iqxBidAdapter', () => { expect(bid.width).to.equal(300); expect(bid.height).to.equal(250); expect(bid.ttl).to.equal(600); - expect(bid.meta).to.deep.equal({advertiserDomains: ['iqx']}); + expect(bid.meta).to.deep.equal({ advertiserDomains: ['iqx'] }); }); it('should interpret valid banner response', function () { @@ -291,7 +291,7 @@ describe('iqxBidAdapter', () => { } }; - const validResponseBanner = spec.interpretResponse(serverResponse, {bidderRequest: defaultRequest}); + const validResponseBanner = spec.interpretResponse(serverResponse, { bidderRequest: defaultRequest }); const bid = validResponseBanner[0]; expect(validResponseBanner).to.be.an('array').that.is.not.empty; expect(bid.mediaType).to.equal('banner'); @@ -317,7 +317,7 @@ describe('iqxBidAdapter', () => { } }; - const validResponseBanner = spec.interpretResponse(serverResponse, {bidderRequest: defaultRequestVideo}); + const validResponseBanner = spec.interpretResponse(serverResponse, { bidderRequest: defaultRequestVideo }); const bid = validResponseBanner[0]; expect(validResponseBanner).to.be.an('array').that.is.not.empty; expect(bid.mediaType).to.equal('video'); @@ -333,12 +333,12 @@ describe('iqxBidAdapter', () => { }); it('should return empty if sync is not allowed', function () { - const opts = spec.getUserSyncs({iframeEnabled: false, pixelEnabled: false}); + const opts = spec.getUserSyncs({ iframeEnabled: false, pixelEnabled: false }); expect(opts).to.be.an('array').that.is.empty; }); it('should allow iframe sync', function () { - const opts = spec.getUserSyncs({iframeEnabled: true, pixelEnabled: false}, [{ + const opts = spec.getUserSyncs({ iframeEnabled: true, pixelEnabled: false }, [{ body: { data: [{ requestId: 'qwerty', @@ -357,7 +357,7 @@ describe('iqxBidAdapter', () => { }); it('should allow pixel sync', function () { - const opts = spec.getUserSyncs({iframeEnabled: false, pixelEnabled: true}, [{ + const opts = spec.getUserSyncs({ iframeEnabled: false, pixelEnabled: true }, [{ body: { data: [{ requestId: 'qwerty', @@ -376,7 +376,7 @@ describe('iqxBidAdapter', () => { }); it('should allow pixel sync and parse consent params', function () { - const opts = spec.getUserSyncs({iframeEnabled: false, pixelEnabled: true}, [{ + const opts = spec.getUserSyncs({ iframeEnabled: false, pixelEnabled: true }, [{ body: { data: [{ requestId: 'qwerty', @@ -400,20 +400,20 @@ describe('iqxBidAdapter', () => { describe('getBidFloor', function () { it('should return null when getFloor is not a function', () => { - const bid = {getFloor: 2}; + const bid = { getFloor: 2 }; const result = getBidFloor(bid); expect(result).to.be.null; }); it('should return null when getFloor doesnt return an object', () => { - const bid = {getFloor: () => 2}; + const bid = { getFloor: () => 2 }; const result = getBidFloor(bid); expect(result).to.be.null; }); it('should return null when floor is not a number', () => { const bid = { - getFloor: () => ({floor: 'string', currency: 'USD'}) + getFloor: () => ({ floor: 'string', currency: 'USD' }) }; const result = getBidFloor(bid); expect(result).to.be.null; @@ -421,7 +421,7 @@ describe('iqxBidAdapter', () => { it('should return null when currency is not USD', () => { const bid = { - getFloor: () => ({floor: 5, currency: 'EUR'}) + getFloor: () => ({ floor: 5, currency: 'EUR' }) }; const result = getBidFloor(bid); expect(result).to.be.null; @@ -429,7 +429,7 @@ describe('iqxBidAdapter', () => { it('should return floor value when everything is correct', () => { const bid = { - getFloor: () => ({floor: 5, currency: 'USD'}) + getFloor: () => ({ floor: 5, currency: 'USD' }) }; const result = getBidFloor(bid); expect(result).to.equal(5); diff --git a/test/spec/modules/iqzoneBidAdapter_spec.js b/test/spec/modules/iqzoneBidAdapter_spec.js index c14b85b2c8b..7f48b7077bf 100644 --- a/test/spec/modules/iqzoneBidAdapter_spec.js +++ b/test/spec/modules/iqzoneBidAdapter_spec.js @@ -480,7 +480,7 @@ describe('IQZoneBidAdapter', function () { const syncData = spec.getUserSyncs({}, {}, { consentString: 'ALL', gdprApplies: true, - }, {}); + }, undefined); expect(syncData).to.be.an('array').which.is.not.empty; expect(syncData[0]).to.be.an('object') expect(syncData[0].type).to.be.a('string') @@ -489,9 +489,7 @@ describe('IQZoneBidAdapter', function () { expect(syncData[0].url).to.equal('https://cs.iqzone.com/image?pbjs=1&gdpr=1&gdpr_consent=ALL&coppa=0') }); it('Should return array of objects with proper sync config , include CCPA', function() { - const syncData = spec.getUserSyncs({}, {}, {}, { - consentString: '1---' - }); + const syncData = spec.getUserSyncs({}, {}, {}, '1---'); expect(syncData).to.be.an('array').which.is.not.empty; expect(syncData[0]).to.be.an('object') expect(syncData[0].type).to.be.a('string') @@ -500,7 +498,7 @@ describe('IQZoneBidAdapter', function () { expect(syncData[0].url).to.equal('https://cs.iqzone.com/image?pbjs=1&ccpa_consent=1---&coppa=0') }); it('Should return array of objects with proper sync config , include GPP', function() { - const syncData = spec.getUserSyncs({}, {}, {}, {}, { + const syncData = spec.getUserSyncs({}, {}, {}, undefined, { gppString: 'abc123', applicableSections: [8] }); diff --git a/test/spec/modules/ixBidAdapter_spec.js b/test/spec/modules/ixBidAdapter_spec.js index eb352fa4ebe..837225f1844 100644 --- a/test/spec/modules/ixBidAdapter_spec.js +++ b/test/spec/modules/ixBidAdapter_spec.js @@ -2,7 +2,7 @@ import * as utils from 'src/utils.js'; import { config } from 'src/config.js'; import { expect } from 'chai'; import { newBidder } from 'src/adapters/bidderFactory.js'; -import { spec, storage, FEATURE_TOGGLES, LOCAL_STORAGE_FEATURE_TOGGLES_KEY, REQUESTED_FEATURE_TOGGLES, combineImps, bidToVideoImp, bidToNativeImp, deduplicateImpExtFields, removeSiteIDs, addDeviceInfo, getDivIdFromAdUnitCode } from '../../../modules/ixBidAdapter.js'; +import { spec, storage, FEATURE_TOGGLES, LOCAL_STORAGE_FEATURE_TOGGLES_KEY, REQUESTED_FEATURE_TOGGLES, combineImps, bidToVideoImp, bidToNativeImp, deduplicateImpExtFields, removeSiteIDs, addDeviceInfo, getDivIdFromAdUnit } from '../../../modules/ixBidAdapter.js'; import { deepAccess, deepClone } from '../../../src/utils.js'; import * as ajaxLib from 'src/ajax.js'; import * as gptUtils from '../../../libraries/gptUtils/gptUtils.js'; @@ -553,7 +553,7 @@ describe('IndexexchangeAdapter', function () { } }, nativeOrtbRequest: { - assets: [{id: 0, required: 0, img: {type: 1}}, {id: 1, required: 1, title: {len: 140}}, {id: 2, required: 1, data: {type: 2}}, {id: 3, required: 1, img: {type: 3}}, {id: 4, required: false, video: {mimes: ['video/mp4', 'video/webm'], minduration: 0, maxduration: 120, protocols: [2, 3, 5, 6]}}] + assets: [{ id: 0, required: 0, img: { type: 1 } }, { id: 1, required: 1, title: { len: 140 } }, { id: 2, required: 1, data: { type: 2 } }, { id: 3, required: 1, img: { type: 3 } }, { id: 4, required: false, video: { mimes: ['video/mp4', 'video/webm'], minduration: 0, maxduration: 120, protocols: [2, 3, 5, 6] } }] }, adUnitCode: 'div-gpt-ad-1460505748562-0', transactionId: '273f49a8-7549-4218-a23c-e7ba59b47230', @@ -884,52 +884,6 @@ describe('IndexexchangeAdapter', function () { } }; - const DEFAULT_OPTION_FLEDGE_ENABLED_GLOBALLY = { - gdprConsent: { - gdprApplies: true, - consentString: '3huaa11=qu3198ae', - vendorData: {} - }, - refererInfo: { - page: 'https://www.prebid.org', - canonicalUrl: 'https://www.prebid.org/the/link/to/the/page' - }, - ortb2: { - site: { - page: 'https://www.prebid.org' - }, - source: { - tid: 'mock-tid' - } - }, - paapi: { - enabled: true - }, - }; - - const DEFAULT_OPTION_FLEDGE_ENABLED = { - gdprConsent: { - gdprApplies: true, - consentString: '3huaa11=qu3198ae', - vendorData: {} - }, - refererInfo: { - page: 'https://www.prebid.org', - canonicalUrl: 'https://www.prebid.org/the/link/to/the/page' - }, - ortb2: { - site: { - page: 'https://www.prebid.org' - }, - source: { - tid: 'mock-tid' - } - }, - paapi: { - enabled: true - } - }; - const DEFAULT_IDENTITY_RESPONSE = { IdentityIp: { responsePending: false, @@ -958,7 +912,7 @@ describe('IndexexchangeAdapter', function () { '33acrossId': { envelope: 'v1.5fs.1000.fjdiosmclds' }, 'criteoID': { envelope: 'testcriteoID' }, 'euidID': { envelope: 'testeuid' }, - pairId: {envelope: 'testpairId'} + pairId: { envelope: 'testpairId' } }; const DEFAULT_USERID_PAYLOAD = [ @@ -1055,10 +1009,8 @@ describe('IndexexchangeAdapter', function () { id: `uid_id_${i}`, }] }; - eids.push(newEid); } - return eids; } @@ -1978,12 +1930,15 @@ describe('IndexexchangeAdapter', function () { dsaparams: [1] }] } - const request = spec.buildRequests(DEFAULT_BANNER_VALID_BID, { ortb2: {regs: { - ext: { - dsa: deepClone(dsa) + const request = spec.buildRequests(DEFAULT_BANNER_VALID_BID, { + ortb2: { + regs: { + ext: { + dsa: deepClone(dsa) + } + } } - } - }})[0]; + })[0]; const r = extractPayload(request); expect(r.regs.ext.dsa.dsarequired).to.equal(dsa.dsarequired); @@ -1999,12 +1954,15 @@ describe('IndexexchangeAdapter', function () { datatopub: '2', transparency: 20 } - const request = spec.buildRequests(DEFAULT_BANNER_VALID_BID, { ortb2: {regs: { - ext: { - dsa: deepClone(dsa) + const request = spec.buildRequests(DEFAULT_BANNER_VALID_BID, { + ortb2: { + regs: { + ext: { + dsa: deepClone(dsa) + } + } } - } - }})[0]; + })[0]; const r = extractPayload(request); expect(r.regs).to.be.undefined; @@ -2024,18 +1982,21 @@ describe('IndexexchangeAdapter', function () { dsaparams: ['1'] }] } - const request = spec.buildRequests(DEFAULT_BANNER_VALID_BID, { ortb2: {regs: { - ext: { - dsa: deepClone(dsa) + const request = spec.buildRequests(DEFAULT_BANNER_VALID_BID, { + ortb2: { + regs: { + ext: { + dsa: deepClone(dsa) + } + } } - } - }})[0]; + })[0]; const r = extractPayload(request); expect(r.regs).to.be.undefined; }); it('should set gpp and gpp_sid field when defined', function () { - const request = spec.buildRequests(DEFAULT_BANNER_VALID_BID, { ortb2: {regs: {gpp: 'gpp', gpp_sid: [1]}} })[0]; + const request = spec.buildRequests(DEFAULT_BANNER_VALID_BID, { ortb2: { regs: { gpp: 'gpp', gpp_sid: [1] } } })[0]; const r = extractPayload(request); expect(r.regs.gpp).to.equal('gpp'); @@ -2043,13 +2004,13 @@ describe('IndexexchangeAdapter', function () { expect(r.regs.gpp_sid).to.include(1); }); it('should not set gpp, gpp_sid and dsa field when not defined', function () { - const request = spec.buildRequests(DEFAULT_BANNER_VALID_BID, { ortb2: {regs: {}} })[0]; + const request = spec.buildRequests(DEFAULT_BANNER_VALID_BID, { ortb2: { regs: {} } })[0]; const r = extractPayload(request); expect(r.regs).to.be.undefined; }); it('should not set gpp and gpp_sid field when fields arent strings or array defined', function () { - const request = spec.buildRequests(DEFAULT_BANNER_VALID_BID, { ortb2: {regs: {gpp: 1, gpp_sid: 'string'}} })[0]; + const request = spec.buildRequests(DEFAULT_BANNER_VALID_BID, { ortb2: { regs: { gpp: 1, gpp_sid: 'string' } } })[0]; const r = extractPayload(request); expect(r.regs).to.be.undefined; @@ -2550,22 +2511,23 @@ describe('IndexexchangeAdapter', function () { sua: { platform: { brand: 'macOS', - version: [ '12', '6', '1' ] + version: ['12', '6', '1'] }, browsers: [ { brand: 'Chromium', - version: [ '107', '0', '5249', '119' ] + version: ['107', '0', '5249', '119'] }, { brand: 'Google Chrome', - version: [ '107', '0', '5249', '119' ] + version: ['107', '0', '5249', '119'] }, ], mobile: 0, model: '' } - }}; + } + }; const request = spec.buildRequests(DEFAULT_BANNER_VALID_BID, { ortb2 })[0]; const payload = extractPayload(request); @@ -2574,8 +2536,7 @@ describe('IndexexchangeAdapter', function () { }); it('should not set device sua if not available in fpd', function () { - const ortb2 = { - device: {}}; + const ortb2 = { device: {} }; const request = spec.buildRequests(DEFAULT_BANNER_VALID_BID, { ortb2 })[0]; const payload = extractPayload(request); @@ -3629,78 +3590,6 @@ describe('IndexexchangeAdapter', function () { }); }); - describe('buildRequestFledge', function () { - it('impression should have ae=1 in ext when fledge module is enabled and ae is set in ad unit', function () { - const bidderRequest = deepClone(DEFAULT_OPTION_FLEDGE_ENABLED); - const bid = utils.deepClone(DEFAULT_BANNER_VALID_BID_WITH_FLEDGE_ENABLED[0]); - const requestBidFloor = spec.buildRequests([bid], bidderRequest)[0]; - const impression = extractPayload(requestBidFloor).imp[0]; - - expect(impression.ext.ae).to.equal(1); - }); - - it('impression should have ae=1 in ext when request has paapi.enabled = true and ext.ae = 1', function () { - const bidderRequest = deepClone(DEFAULT_OPTION_FLEDGE_ENABLED); - const bid = utils.deepClone(DEFAULT_BANNER_VALID_BID_WITH_FLEDGE_ENABLED[0]); - const requestBidFloor = spec.buildRequests([bid], bidderRequest)[0]; - const impression = extractPayload(requestBidFloor).imp[0]; - - expect(impression.ext.ae).to.equal(1); - }); - - it('impression should not have ae=1 in ext when fledge module is enabled globally through setConfig but overidden at ad unit level', function () { - const bidderRequest = deepClone(DEFAULT_OPTION_FLEDGE_ENABLED); - const bid = utils.deepClone(DEFAULT_BANNER_VALID_BID[0]); - const requestBidFloor = spec.buildRequests([bid], bidderRequest)[0]; - const impression = extractPayload(requestBidFloor).imp[0]; - - expect(impression.ext.ae).to.be.undefined; - }); - - it('impression should not have ae=1 in ext when fledge module is disabled', function () { - const bidderRequest = deepClone(DEFAULT_OPTION); - const bid = utils.deepClone(DEFAULT_BANNER_VALID_BID[0]); - const requestBidFloor = spec.buildRequests([bid], bidderRequest)[0]; - const impression = extractPayload(requestBidFloor).imp[0]; - - expect(impression.ext.ae).to.be.undefined; - }); - - it('should contain correct IXdiag ae property for Fledge', function () { - const bid = DEFAULT_BANNER_VALID_BID_WITH_FLEDGE_ENABLED[0]; - const bidderRequestWithFledgeEnabled = deepClone(DEFAULT_OPTION_FLEDGE_ENABLED); - const request = spec.buildRequests([bid], bidderRequestWithFledgeEnabled); - const diagObj = extractPayload(request[0]).ext.ixdiag; - expect(diagObj.ae).to.equal(true); - }); - - it('should log warning for non integer auction environment in ad unit for fledge', () => { - const logWarnSpy = sinon.spy(utils, 'logWarn'); - const bid = DEFAULT_BANNER_VALID_BID_WITH_FLEDGE_ENABLED[0]; - bid.ortb2Imp.ext.ae = 'malformed' - const bidderRequestWithFledgeEnabled = deepClone(DEFAULT_OPTION_FLEDGE_ENABLED); - spec.buildRequests([bid], bidderRequestWithFledgeEnabled); - expect(logWarnSpy.calledWith('error setting auction environment flag - must be an integer')).to.be.true; - logWarnSpy.restore(); - }); - - it('impression should have paapi extension when passed', function () { - const bidderRequest = deepClone(DEFAULT_OPTION_FLEDGE_ENABLED); - const bid = utils.deepClone(DEFAULT_BANNER_VALID_BID_WITH_FLEDGE_ENABLED[0]); - bid.ortb2Imp.ext.ae = 1 - bid.ortb2Imp.ext.paapi = { - requestedSize: { - width: 300, - height: 250 - } - } - const requestBidFloor = spec.buildRequests([bid], bidderRequest)[0]; - const impression = extractPayload(requestBidFloor).imp[0]; - expect(impression.ext.paapi.requestedSize.width).to.equal(300); - expect(impression.ext.paapi.requestedSize.height).to.equal(250); - }); - }); - describe('integration through exchangeId and externalId', function () { const expectedExchangeId = 123456; // create banner bids with externalId but no siteId as bidder param @@ -4346,106 +4235,6 @@ describe('IndexexchangeAdapter', function () { expect(result[0]).to.deep.equal(expectedParse[0]); }); - describe('Auction config response', function () { - let bidderRequestWithFledgeEnabled; - let serverResponseWithoutFledgeConfigs; - let serverResponseWithFledgeConfigs; - let serverResponseWithMalformedAuctionConfig; - let serverResponseWithMalformedAuctionConfigs; - - beforeEach(() => { - bidderRequestWithFledgeEnabled = spec.buildRequests(DEFAULT_BANNER_VALID_BID_WITH_FLEDGE_ENABLED, {})[0]; - bidderRequestWithFledgeEnabled.paapi = {enabled: true}; - - serverResponseWithoutFledgeConfigs = { - body: { - ...DEFAULT_BANNER_BID_RESPONSE - } - }; - - serverResponseWithFledgeConfigs = { - body: { - ...DEFAULT_BANNER_BID_RESPONSE, - ext: { - protectedAudienceAuctionConfigs: [ - { - bidId: '59f219e54dc2fc', - config: { - seller: 'https://seller.test.indexexchange.com', - decisionLogicUrl: 'https://seller.test.indexexchange.com/decision-logic.js', - interestGroupBuyers: ['https://buyer.test.indexexchange.com'], - sellerSignals: { - callbackURL: 'https://test.com/ig/v1/ck74j8bcvc9c73a8eg6g' - }, - perBuyerSignals: { - 'https://buyer.test.indexexchange.com': {} - } - } - } - ] - } - } - }; - - serverResponseWithMalformedAuctionConfig = { - body: { - ...DEFAULT_BANNER_BID_RESPONSE, - ext: { - protectedAudienceAuctionConfigs: ['malformed'] - } - } - }; - - serverResponseWithMalformedAuctionConfigs = { - body: { - ...DEFAULT_BANNER_BID_RESPONSE, - ext: { - protectedAudienceAuctionConfigs: 'malformed' - } - } - }; - }); - - it('should correctly interpret response with auction configs', () => { - const result = spec.interpretResponse(serverResponseWithFledgeConfigs, bidderRequestWithFledgeEnabled); - const expectedOutput = [ - { - bidId: '59f219e54dc2fc', - config: { - ...serverResponseWithFledgeConfigs.body.ext.protectedAudienceAuctionConfigs[0].config, - perBuyerSignals: { - 'https://buyer.test.indexexchange.com': {} - } - } - } - ]; - expect(result.paapi).to.deep.equal(expectedOutput); - }); - - it('should correctly interpret response without auction configs', () => { - const result = spec.interpretResponse(serverResponseWithoutFledgeConfigs, bidderRequestWithFledgeEnabled); - expect(result.paapi).to.be.undefined; - }); - - it('should handle malformed auction configs gracefully', () => { - const result = spec.interpretResponse(serverResponseWithMalformedAuctionConfig, bidderRequestWithFledgeEnabled); - expect(result.paapi).to.be.empty; - }); - - it('should log warning for malformed auction configs', () => { - const logWarnSpy = sinon.spy(utils, 'logWarn'); - spec.interpretResponse(serverResponseWithMalformedAuctionConfig, bidderRequestWithFledgeEnabled); - expect(logWarnSpy.calledWith('Malformed auction config detected:', 'malformed')).to.be.true; - logWarnSpy.restore(); - }); - - it('should return bids when protected audience auction conigs is malformed', () => { - const result = spec.interpretResponse(serverResponseWithMalformedAuctionConfigs, bidderRequestWithFledgeEnabled); - expect(result.paapi).to.be.undefined; - expect(result.length).to.be.greaterThan(0); - }); - }); - describe('interpretResponse when server response is empty', function() { let serverResponseWithoutBody; let serverResponseWithoutSeatbid; @@ -4460,7 +4249,7 @@ describe('IndexexchangeAdapter', function () { }; bidderRequestWithFledgeEnabled = spec.buildRequests(DEFAULT_BANNER_VALID_BID_WITH_FLEDGE_ENABLED, {})[0]; - bidderRequestWithFledgeEnabled.paapi = {enabled: true}; + bidderRequestWithFledgeEnabled.paapi = { enabled: true }; bidderRequestWithoutFledgeEnabled = spec.buildRequests(DEFAULT_BANNER_VALID_BID, {})[0]; }); @@ -4480,7 +4269,6 @@ describe('IndexexchangeAdapter', function () { }); }); }); - describe('bidrequest consent', function () { it('should have consent info if gdprApplies and consentString exist', function () { const validBidWithConsent = spec.buildRequests(DEFAULT_BANNER_VALID_BID, DEFAULT_OPTION); @@ -5390,7 +5178,8 @@ describe('IndexexchangeAdapter', function () { device: { ip: '192.168.1.1', ipv6: '2001:0db8:85a3:0000:0000:8a2e:0370:7334' - }}; + } + }; const request = spec.buildRequests(DEFAULT_BANNER_VALID_BID, { ortb2 })[0]; const payload = extractPayload(request); expect(payload.device.ip).to.equal('192.168.1.1') @@ -5398,7 +5187,7 @@ describe('IndexexchangeAdapter', function () { }); it('should not add device.ip if neither ip nor ipv6 exists', () => { - const ortb2 = {device: {}}; + const ortb2 = { device: {} }; const request = spec.buildRequests(DEFAULT_BANNER_VALID_BID, { ortb2 })[0]; const payload = extractPayload(request); expect(payload.device.ip).to.be.undefined; @@ -5425,7 +5214,7 @@ describe('IndexexchangeAdapter', function () { }); it('should not add device.geo if it does not exist', () => { - const ortb2 = {device: {}}; + const ortb2 = { device: {} }; const request = spec.buildRequests(DEFAULT_BANNER_VALID_BID, { ortb2 })[0]; const payload = extractPayload(request); expect(payload.device.geo).to.be.undefined; @@ -5438,15 +5227,15 @@ describe('IndexexchangeAdapter', function () { const el = document.createElement('div'); el.id = adUnitCode; document.body.appendChild(el); - expect(getDivIdFromAdUnitCode(adUnitCode)).to.equal(adUnitCode); + expect(getDivIdFromAdUnit(adUnitCode, { code: adUnitCode })).to.equal(adUnitCode); document.body.removeChild(el); }); it('retrieves divId from GPT once and caches result', () => { const adUnitCode = 'div-ad2'; - const stub = sinon.stub(gptUtils, 'getGptSlotInfoForAdUnitCode').returns({divId: 'gpt-div'}); - const first = getDivIdFromAdUnitCode(adUnitCode); - const second = getDivIdFromAdUnitCode(adUnitCode); + const stub = sinon.stub(gptUtils, 'getGptSlotInfoForAdUnitCode').returns({ divId: 'gpt-div' }); + const first = getDivIdFromAdUnit(adUnitCode, {}); + const second = getDivIdFromAdUnit(adUnitCode, {}); expect(first).to.equal('gpt-div'); expect(second).to.equal('gpt-div'); expect(stub.calledOnce).to.be.true; diff --git a/test/spec/modules/jixieBidAdapter_spec.js b/test/spec/modules/jixieBidAdapter_spec.js index bd56aee71a6..fb904e4b12b 100644 --- a/test/spec/modules/jixieBidAdapter_spec.js +++ b/test/spec/modules/jixieBidAdapter_spec.js @@ -89,7 +89,7 @@ describe('jixie Adapter', function () { // to serve as the object that prebid will call jixie buildRequest with: (param2) const bidderRequest_ = { - refererInfo: {referer: pageurl_}, + refererInfo: { referer: pageurl_ }, auctionId: auctionId_, timeout: timeout_ }; @@ -344,7 +344,7 @@ describe('jixie Adapter', function () { it('it should popular the device info when it is available', function () { const getConfigStub = sinon.stub(config, 'getConfig'); - const content = {w: 500, h: 400}; + const content = { w: 500, h: 400 }; getConfigStub.callsFake(function fakeFn(prop) { if (prop === 'device') { return content; @@ -617,14 +617,14 @@ describe('jixie Adapter', function () { 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) + expect(spec.interpretResponse({ body: {} }, { validBidRequests: [] }).length).to.equal(0) + expect(spec.interpretResponse({ body: [] }, { validBidRequests: [] }).length).to.equal(0) }); it('should get correct bid response', function () { const setCookieSpy = sinon.spy(storage, 'setCookie'); const setLocalStorageSpy = sinon.spy(storage, 'setDataInLocalStorage'); - const result = spec.interpretResponse({body: responseBody_}, requestObj_) + const result = spec.interpretResponse({ body: responseBody_ }, requestObj_) expect(setLocalStorageSpy.calledWith('_jxx', '43aacc10-f643-11ea-8a10-c5fe2d394e7e')).to.equal(true); expect(setLocalStorageSpy.calledWith('_jxxs', '1600057934-43aacc10-f643-11ea-8a10-c5fe2d394e7e')).to.equal(true); expect(setCookieSpy.calledWith('_jxxs', '1600057934-43aacc10-f643-11ea-8a10-c5fe2d394e7e')).to.equal(true); diff --git a/test/spec/modules/jixieIdSystem_spec.js b/test/spec/modules/jixieIdSystem_spec.js index 14559bff174..eb95b59b02c 100644 --- a/test/spec/modules/jixieIdSystem_spec.js +++ b/test/spec/modules/jixieIdSystem_spec.js @@ -1,7 +1,7 @@ import { expect } from 'chai'; import { jixieIdSubmodule, storage } from 'modules/jixieIdSystem.js'; import { server } from '../../mocks/xhr.js'; -import {parseUrl} from '../../../src/utils.js'; +import { parseUrl } from '../../../src/utils.js'; const COOKIE_EXPIRATION_FUTURE = (new Date(Date.now() + 60 * 60 * 24 * 1000)).toUTCString(); const COOKIE_EXPIRATION_PAST = (new Date(Date.now() - 60 * 60 * 24 * 1000)).toUTCString(); @@ -54,8 +54,8 @@ describe('JixieId Submodule', () => { params: { stdjxidckname: STD_JXID_KEY, pubExtIds: [ - {pname: EID_TYPE1_PARAMNAME, ckname: EID_TYPE1_COOKIENAME}, - {pname: EID_TYPE2_PARAMNAME, lsname: EID_TYPE2_LSNAME} + { pname: EID_TYPE1_PARAMNAME, ckname: EID_TYPE1_COOKIENAME }, + { pname: EID_TYPE2_PARAMNAME, lsname: EID_TYPE2_LSNAME } ] } }); @@ -76,8 +76,8 @@ describe('JixieId Submodule', () => { params: { stdjxidckname: STD_JXID_KEY, pubExtIds: [ - {pname: EID_TYPE1_PARAMNAME, ckname: EID_TYPE1_COOKIENAME}, - {pname: EID_TYPE2_PARAMNAME, lsname: EID_TYPE2_LSNAME} + { pname: EID_TYPE1_PARAMNAME, ckname: EID_TYPE1_COOKIENAME }, + { pname: EID_TYPE2_PARAMNAME, lsname: EID_TYPE2_LSNAME } ] } }); @@ -98,8 +98,8 @@ describe('JixieId Submodule', () => { params: { stdjxidckname: STD_JXID_KEY, pubExtIds: [ - {pname: EID_TYPE1_PARAMNAME, ckname: EID_TYPE1_COOKIENAME}, - {pname: EID_TYPE2_PARAMNAME, lsname: EID_TYPE2_LSNAME} + { pname: EID_TYPE1_PARAMNAME, ckname: EID_TYPE1_COOKIENAME }, + { pname: EID_TYPE2_PARAMNAME, lsname: EID_TYPE2_LSNAME } ] } }); @@ -126,8 +126,8 @@ describe('JixieId Submodule', () => { params: { accountid: ACCOUNTID, pubExtIds: [ - {pname: EID_TYPE1_PARAMNAME, ckname: EID_TYPE1_COOKIENAME}, - {pname: EID_TYPE2_PARAMNAME, lsname: EID_TYPE2_LSNAME} + { pname: EID_TYPE1_PARAMNAME, ckname: EID_TYPE1_COOKIENAME }, + { pname: EID_TYPE2_PARAMNAME, lsname: EID_TYPE2_LSNAME } ] } }); @@ -161,8 +161,8 @@ describe('JixieId Submodule', () => { params: { stdjxidckname: STD_JXID_KEY, pubExtIds: [ - {pname: EID_TYPE1_PARAMNAME, ckname: EID_TYPE1_COOKIENAME}, - {pname: EID_TYPE2_PARAMNAME, lsname: EID_TYPE2_LSNAME} + { pname: EID_TYPE1_PARAMNAME, ckname: EID_TYPE1_COOKIENAME }, + { pname: EID_TYPE2_PARAMNAME, lsname: EID_TYPE2_LSNAME } ] } }); @@ -200,8 +200,8 @@ describe('JixieId Submodule', () => { params: { stdjxidckname: STD_JXID_KEY, pubExtIds: [ - {pname: EID_TYPE1_PARAMNAME, ckname: EID_TYPE1_COOKIENAME}, - {pname: EID_TYPE2_PARAMNAME, lsname: EID_TYPE2_LSNAME} + { pname: EID_TYPE1_PARAMNAME, ckname: EID_TYPE1_COOKIENAME }, + { pname: EID_TYPE2_PARAMNAME, lsname: EID_TYPE2_LSNAME } ] } }); @@ -217,7 +217,7 @@ describe('JixieId Submodule', () => { }); context('when has rather stale pbjs jixie cookie', () => { it('should call the server and set the id; send available extra info (e.g. esha,psha, consent if available)', () => { - const consentData = {gdpr: {gdprApplies: 1, consentString: MOCK_CONSENT_STRING}}; + const consentData = { gdpr: { gdprApplies: 1, consentString: MOCK_CONSENT_STRING } }; storage.setCookie(PBJS_JXID_KEY, CLIENTID1, COOKIE_EXPIRATION_FUTURE) storage.setCookie(PBJS_IDLOGSTR_KEY, IDLOG_EXPIRED, COOKIE_EXPIRATION_FUTURE) storage.setCookie(EID_TYPE1_COOKIENAME, EID_TYPE1_SAMPLEVALUE, COOKIE_EXPIRATION_FUTURE) @@ -229,8 +229,8 @@ describe('JixieId Submodule', () => { params: { stdjxidckname: STD_JXID_KEY, pubExtIds: [ - {pname: EID_TYPE1_PARAMNAME, ckname: EID_TYPE1_COOKIENAME}, - {pname: EID_TYPE2_PARAMNAME, lsname: EID_TYPE2_LSNAME} + { pname: EID_TYPE1_PARAMNAME, ckname: EID_TYPE1_COOKIENAME }, + { pname: EID_TYPE2_PARAMNAME, lsname: EID_TYPE2_LSNAME } ] } }, consentData); diff --git a/test/spec/modules/justIdSystem_spec.js b/test/spec/modules/justIdSystem_spec.js index 4a5ebb35b0a..b2eaa14132d 100644 --- a/test/spec/modules/justIdSystem_spec.js +++ b/test/spec/modules/justIdSystem_spec.js @@ -29,7 +29,7 @@ describe('JustIdSystem', function () { describe('decode', function() { it('decode justId', function() { const justId = 'aaa'; - expect(justIdSubmodule.decode({uid: justId})).to.deep.eq({justId: justId}); + expect(justIdSubmodule.decode({ uid: justId })).to.deep.eq({ justId: justId }); }) }); @@ -75,7 +75,7 @@ describe('JustIdSystem', function () { const atmVarName = '__fakeAtm'; - justIdSubmodule.getId({params: {atmVarName: atmVarName}}).callback(callbackSpy); + justIdSubmodule.getId({ params: { atmVarName: atmVarName } }).callback(callbackSpy); expect(getAtmStub.lastCall.lastArg).to.equal(atmVarName); }); @@ -106,7 +106,7 @@ describe('JustIdSystem', function () { it('work with stub', function(done) { var calls = []; currentAtm = (cmd, param) => { - calls.push({cmd: cmd, param: param}); + calls.push({ cmd: cmd, param: param }); } const callbackSpy = sinon.stub(); @@ -190,7 +190,7 @@ describe('JustIdSystem', function () { const b = { y: 'y' } const c = { z: 'z' } - justIdSubmodule.getId(a, {gdpr: b}, c).callback(callbackSpy); + justIdSubmodule.getId(a, { gdpr: b }, c).callback(callbackSpy); scriptTagCallback(); diff --git a/test/spec/modules/justpremiumBidAdapter_spec.js b/test/spec/modules/justpremiumBidAdapter_spec.js index 21cd488e745..bb09e83f514 100644 --- a/test/spec/modules/justpremiumBidAdapter_spec.js +++ b/test/spec/modules/justpremiumBidAdapter_spec.js @@ -95,7 +95,7 @@ describe('justpremium adapter', function () { }) it('Verify build request', function () { - expect(spec.isBidRequestValid({bidder: 'justpremium', params: {}})).to.equal(false) + expect(spec.isBidRequestValid({ bidder: 'justpremium', params: {} })).to.equal(false) expect(spec.isBidRequestValid({})).to.equal(false) expect(spec.isBidRequestValid(adUnits[0])).to.equal(true) expect(spec.isBidRequestValid(adUnits[1])).to.equal(true) @@ -176,7 +176,7 @@ describe('justpremium adapter', function () { } ] - const result = spec.interpretResponse({body: response}, request) + const result = spec.interpretResponse({ body: response }, request) expect(Object.keys(result[0])).to.deep.equal(Object.keys(expectedResponse[0])) expect(result[0]).to.not.equal(null) @@ -190,7 +190,7 @@ describe('justpremium adapter', function () { expect(result[0].netRevenue).to.equal(true) expect(result[0].format).to.equal('lb') expect(result[0].meta.advertiserDomains[0]).to.equal('justpremium.com') - expect(result[0].adserverTargeting).to.deep.equal({'hb_deal_justpremium': 'jp_pg'}) + expect(result[0].adserverTargeting).to.deep.equal({ 'hb_deal_justpremium': 'jp_pg' }) }) it('Verify wrong server response', function () { @@ -203,14 +203,14 @@ describe('justpremium adapter', function () { } } - const result = spec.interpretResponse({body: response}, request) + const result = spec.interpretResponse({ body: response }, request) expect(result.length).to.equal(0) }) }) describe('getUserSyncs', function () { it('Verifies sync options for iframe', function () { - const options = spec.getUserSyncs({iframeEnabled: true}, {}, {gdprApplies: true, consentString: 'BOOgjO9OOgjO9APABAENAi-AAAAWd'}, '1YYN') + const options = spec.getUserSyncs({ iframeEnabled: true }, {}, { gdprApplies: true, consentString: 'BOOgjO9OOgjO9APABAENAi-AAAAWd' }, '1YYN') expect(options).to.not.be.undefined expect(options[0].type).to.equal('iframe') expect(options[0].url).to.match(/\/\/pre.ads.justpremium.com\/v\/1.0\/t\/sync/) @@ -218,7 +218,7 @@ describe('justpremium adapter', function () { expect(options[0].url).to.match(/&usPrivacy=1YYN/) }) it('Returns array of user sync pixels', function () { - const options = spec.getUserSyncs({pixelEnabled: true}, serverResponses) + const options = spec.getUserSyncs({ pixelEnabled: true }, serverResponses) expect(options).to.not.be.undefined expect(Array.isArray(options)).to.be.true expect(options[0].type).to.equal('image') diff --git a/test/spec/modules/jwplayerBidAdapter_spec.js b/test/spec/modules/jwplayerBidAdapter_spec.js index ae456919238..4469f4cbfad 100644 --- a/test/spec/modules/jwplayerBidAdapter_spec.js +++ b/test/spec/modules/jwplayerBidAdapter_spec.js @@ -71,19 +71,19 @@ describe('jwplayerBidAdapter', function() { }); it('should be invalid when the bid request only includes a publisher ID', function() { - assert(spec.isBidRequestValid({params: {publisherId: 'foo'}}) === false); + assert(spec.isBidRequestValid({ params: { publisherId: 'foo' } }) === false); }); it('should be invalid when the bid request only includes a placement ID', function() { - assert(spec.isBidRequestValid({params: {placementId: 'foo'}}) === false); + assert(spec.isBidRequestValid({ params: { placementId: 'foo' } }) === false); }); it('should be invalid when the bid request only includes a site ID', function() { - assert(spec.isBidRequestValid({params: {siteId: 'foo'}}) === false); + assert(spec.isBidRequestValid({ params: { siteId: 'foo' } }) === false); }); it('should be valid when the bid includes a placement ID, a publisher ID and a site ID', function() { - assert(spec.isBidRequestValid({params: {placementId: 'foo', publisherId: 'bar', siteId: 'siteId '}}) === true); + assert(spec.isBidRequestValid({ params: { placementId: 'foo', publisherId: 'bar', siteId: 'siteId ' } }) === true); }); }); diff --git a/test/spec/modules/jwplayerRtdProvider_spec.js b/test/spec/modules/jwplayerRtdProvider_spec.js index e60d346de0f..c639f37e9dd 100644 --- a/test/spec/modules/jwplayerRtdProvider_spec.js +++ b/test/spec/modules/jwplayerRtdProvider_spec.js @@ -15,14 +15,14 @@ import { getPlayer, jwplayerSubmodule } from 'modules/jwplayerRtdProvider.js'; -import {server} from 'test/mocks/xhr.js'; -import {deepClone} from '../../../src/utils.js'; +import { server } from 'test/mocks/xhr.js'; +import { deepClone } from '../../../src/utils.js'; describe('jwplayerRtdProvider', function() { const testIdForSuccess = 'test_id_for_success'; const testIdForFailure = 'test_id_for_failure'; const validSegments = ['test_seg_1', 'test_seg_2']; - const responseHeader = {'Content-Type': 'application/json'}; + const responseHeader = { 'Content-Type': 'application/json' }; describe('Fetch targeting for mediaID tests', function () { let request; @@ -527,7 +527,7 @@ describe('jwplayerRtdProvider', function() { bids }; - const ortb2Fragments = {global: {}}; + const ortb2Fragments = { global: {} }; enrichAdUnits([adUnit], ortb2Fragments); const bid1 = bids[0]; const bid2 = bids[1]; @@ -597,13 +597,13 @@ describe('jwplayerRtdProvider', function() { id: 'randomContentId', data: [{ name: 'random', - segment: [{id: 'random'}] + segment: [{ id: 'random' }] }, { name: 'jwplayer.com', - segment: [{id: 'randomJwPlayer'}] + segment: [{ id: 'randomJwPlayer' }] }, { name: 'random2', - segment: [{id: 'random2'}] + segment: [{ id: 'random2' }] }] } } @@ -637,11 +637,11 @@ describe('jwplayerRtdProvider', function() { const randomDatum = data[0]; expect(randomDatum).to.have.property('name', 'random'); - expect(randomDatum.segment).to.deep.equal([{id: 'random'}]); + expect(randomDatum.segment).to.deep.equal([{ id: 'random' }]); const randomDatum2 = data[1]; expect(randomDatum2).to.have.property('name', 'random2'); - expect(randomDatum2.segment).to.deep.equal([{id: 'random2'}]); + expect(randomDatum2.segment).to.deep.equal([{ id: 'random2' }]); const jwplayerDatum = data[2]; expect(jwplayerDatum).to.have.property('name', 'jwplayer.com'); @@ -1531,10 +1531,10 @@ describe('jwplayerRtdProvider', function() { }); describe('Add Targeting to Bid', function () { - const targeting = {foo: 'bar'}; + const targeting = { foo: 'bar' }; it('creates realTimeData when absent from Bid', function () { - const targeting = {foo: 'bar'}; + const targeting = { foo: 'bar' }; const bid = {}; addTargetingToBid(bid, targeting); expect(bid).to.have.property('rtd'); @@ -1804,7 +1804,7 @@ describe('jwplayerRtdProvider', function() { } } }, - bids: [ bid ] + bids: [bid] }; const expectedContentId = 'jw_' + adUnit.ortb2Imp.ext.data.jwTargeting.mediaID; const expectedTargeting = { @@ -1813,7 +1813,7 @@ describe('jwplayerRtdProvider', function() { } }; - jwplayerSubmodule.getBidRequestData({ adUnits: [ adUnit ] }, bidRequestSpy); + jwplayerSubmodule.getBidRequestData({ adUnits: [adUnit] }, bidRequestSpy); expect(bidRequestSpy.calledOnce).to.be.true; expect(bid.rtd.jwplayer.targeting).to.not.have.property('segments'); expect(bid.rtd.jwplayer.targeting).to.not.have.property('segments'); @@ -1828,11 +1828,11 @@ describe('jwplayerRtdProvider', function() { const adUnitWithMediaId = { code: adUnitCode, mediaID: testIdForSuccess, - bids: [ bid1 ] + bids: [bid1] }; const adUnitEmpty = { code: 'test_ad_unit_empty', - bids: [ bid2 ] + bids: [bid2] }; const adUnitEmptyfpd = { @@ -1842,7 +1842,7 @@ describe('jwplayerRtdProvider', function() { id: 'sthg' } }, - bids: [ bid3 ] + bids: [bid3] }; jwplayerSubmodule.getBidRequestData({ adUnits: [adUnitWithMediaId, adUnitEmpty, adUnitEmptyfpd] }, bidRequestSpy); diff --git a/test/spec/modules/kargoBidAdapter_spec.js b/test/spec/modules/kargoBidAdapter_spec.js index c376b444246..4de51a7b860 100644 --- a/test/spec/modules/kargoBidAdapter_spec.js +++ b/test/spec/modules/kargoBidAdapter_spec.js @@ -2,9 +2,9 @@ import { expect } from 'chai'; import { spec } from 'modules/kargoBidAdapter.js'; import { config } from 'src/config.js'; import { getStorageManager } from 'src/storageManager.js'; -import {getGlobal} from '../../../src/prebidGlobal.js'; +import { getGlobal } from '../../../src/prebidGlobal.js'; const utils = require('src/utils'); -const STORAGE = getStorageManager({bidderCode: 'kargo'}); +const STORAGE = getStorageManager({ bidderCode: 'kargo' }); describe('kargo adapter tests', function() { let bid; let outstreamBid; let testBids; let sandbox; let clock; let frozenNow = new Date(); let oldBidderSettings; @@ -53,25 +53,25 @@ describe('kargo adapter tests', function() { userIdAsEids: [ { source: 'adquery.io', - uids: [ { + uids: [{ id: 'adquery-id', atype: 1 - } ] + }] }, { source: 'criteo.com', - uids: [ { + uids: [{ id: 'criteo-id', atype: 1 - } ] + }] }, { source: 'adserver.org', - uids: [ { + uids: [{ id: 'adserver-id', atype: 1, ext: { rtiPartner: 'TDID' } - } ] + }] }, ], floorData: { @@ -82,11 +82,11 @@ describe('kargo adapter tests', function() { config: { ver: '1.0', complete: 1, - nodes: [ { + nodes: [{ asi: 'indirectseller.com', sid: '00001', hp: 1, - } ] + }] } }, }); @@ -96,7 +96,7 @@ describe('kargo adapter tests', function() { placementId: 'foobar' }, mediaTypes: { - banner: { sizes: [ [1, 1] ] } + banner: { sizes: [[1, 1]] } } }); @@ -163,11 +163,11 @@ describe('kargo adapter tests', function() { }, mediaTypes: { banner: { - sizes: [ [970, 250], [1, 1] ] + sizes: [[970, 250], [1, 1]] } }, adUnitCode: 'displayAdunitCode', - sizes: [ [300, 250], [300, 600] ], + sizes: [[300, 250], [300, 600]], bidId: 'randomBidId', bidderRequestId: 'randomBidderRequestId', auctionId: 'randomAuctionId' @@ -183,20 +183,20 @@ describe('kargo adapter tests', function() { video: { context: 'instream', playerSize: [640, 480], - api: [ 1, 2 ], + api: [1, 2], linearity: 1, maxduration: 60, - mimes: [ 'video/mp4', 'video/webm', 'application/javascript' ], + mimes: ['video/mp4', 'video/webm', 'application/javascript'], minduration: 0, placement: 1, - playbackmethod: [ 1, 2, 3 ], + playbackmethod: [1, 2, 3], plcmt: 1, - protocols: [ 2, 3, 5 ], + protocols: [2, 3, 5], skip: 1 } }, adUnitCode: 'instreamAdunitCode', - sizes: [ [300, 250], [300, 600] ], + sizes: [[300, 250], [300, 600]], bidId: 'randomBidId2', bidderRequestId: 'randomBidderRequestId2', auctionId: 'randomAuctionId2' @@ -307,7 +307,7 @@ describe('kargo adapter tests', function() { page: topUrl, reachedTop: true, ref: referer, - stack: [ topUrl ], + stack: [topUrl], topmostLocation: topUrl, }, start: Date.now(), @@ -382,7 +382,7 @@ describe('kargo adapter tests', function() { // Display bid const bidImp = payload.imp[0]; expect(bidImp.id).to.equal('randomBidId'); - expect(bidImp.banner).to.deep.equal({ sizes: [ [970, 250], [1, 1] ] }); + expect(bidImp.banner).to.deep.equal({ sizes: [[970, 250], [1, 1]] }); expect(bidImp.video).to.be.undefined; expect(bidImp.bidRequestCount).to.equal(1); expect(bidImp.bidderRequestCount).to.equal(1); @@ -411,7 +411,7 @@ describe('kargo adapter tests', function() { // General keys expect(payload.aid).to.equal('randomAuctionId'); - expect(payload.device).to.deep.equal({ size: [ window.screen.width, window.screen.height ] }); + expect(payload.device).to.deep.equal({ size: [window.screen.width, window.screen.height] }); expect(payload.ext.ortb2).to.deep.equal(defaultBidParams.ortb2); expect(payload.pbv).to.equal('$prebid.version$'); expect(payload.requestCount).to.equal(spec.buildRequests.callCount - 1); @@ -720,7 +720,7 @@ describe('kargo adapter tests', function() { inventoryCode: 'banner_outstream_test', floor: 1.0, video: { - mimes: [ 'video/mp4' ], + mimes: ['video/mp4'], maxduration: 30, minduration: 6, w: 640, @@ -733,11 +733,11 @@ describe('kargo adapter tests', function() { playerSize: [640, 380] }, banner: { - sizes: [ [970, 250], [1, 1] ] + sizes: [[970, 250], [1, 1]] } }, adUnitCode: 'adunit-code-banner-outstream', - sizes: [ [300, 250], [300, 600], [1, 1, 1], ['flex'] ], + sizes: [[300, 250], [300, 600], [1, 1, 1], ['flex']], bidId: 'banner-outstream-bid-id', bidderRequestId: 'kargo-request-id', auctionId: 'kargo-auction-id', @@ -749,7 +749,7 @@ describe('kargo adapter tests', function() { inventoryCode: 'banner_outstream_test', floor: 1.0, video: { - mimes: [ 'video/mp4' ], + mimes: ['video/mp4'], maxduration: 30, minduration: 6, w: 640, @@ -758,12 +758,12 @@ describe('kargo adapter tests', function() { }, mediaTypes: { banner: { - sizes: [ [970, 250], [1, 1] ] + sizes: [[970, 250], [1, 1]] }, native: {} }, adUnitCode: 'adunit-code-banner-outstream', - sizes: [ [300, 250], [300, 600], [1, 1, 1], ['flex'] ], + sizes: [[300, 250], [300, 600], [1, 1, 1], ['flex']], bidId: 'banner-outstream-bid-id', bidderRequestId: 'kargo-request-id', auctionId: 'kargo-auction-id', @@ -775,7 +775,7 @@ describe('kargo adapter tests', function() { inventoryCode: 'banner_outstream_test', floor: 1.0, video: { - mimes: [ 'video/mp4' ], + mimes: ['video/mp4'], maxduration: 30, minduration: 6, w: 640, @@ -790,7 +790,7 @@ describe('kargo adapter tests', function() { native: {}, }, adUnitCode: 'adunit-code-banner-outstream', - sizes: [ [300, 250], [300, 600], [1, 1, 1], ['flex'] ], + sizes: [[300, 250], [300, 600], [1, 1, 1], ['flex']], bidId: 'banner-outstream-bid-id', bidderRequestId: 'kargo-request-id', auctionId: 'kargo-auction-id', @@ -802,7 +802,7 @@ describe('kargo adapter tests', function() { inventoryCode: 'banner_outstream_test', floor: 1.0, video: { - mimes: [ 'video/mp4' ], + mimes: ['video/mp4'], maxduration: 30, minduration: 6, w: 640, @@ -815,12 +815,12 @@ describe('kargo adapter tests', function() { playerSize: [640, 380] }, banner: { - sizes: [ [970, 250], [1, 1] ] + sizes: [[970, 250], [1, 1]] }, native: {}, }, adUnitCode: 'adunit-code-banner-outstream', - sizes: [ [300, 250], [300, 600], [1, 1, 1], ['flex'] ], + sizes: [[300, 250], [300, 600], [1, 1, 1], ['flex']], bidId: 'banner-outstream-bid-id', bidderRequestId: 'kargo-request-id', auctionId: 'kargo-auction-id', @@ -830,7 +830,7 @@ describe('kargo adapter tests', function() { const payload = getPayloadFromTestBids(testBids); const bannerImp = { - sizes: [ [970, 250], [1, 1] ] + sizes: [[970, 250], [1, 1]] }; const videoImp = { context: 'outstream', @@ -938,7 +938,7 @@ describe('kargo adapter tests', function() { }, ]; - [ 0, null, false, 'foobar' ].forEach(value => testBids.push({ + [0, null, false, 'foobar'].forEach(value => testBids.push({ ...minimumBidParams, bidRequestsCount: value, bidderRequestsCount: value, @@ -1123,14 +1123,14 @@ describe('kargo adapter tests', function() { }); [ - [ 'valid', 'invalidB64', 'cookie' ], - [ 'valid', 'invalidJson', 'cookie' ], - [ 'invalidB64', 'invalidJson', 'none' ], - [ 'invalidB64', 'invalidB64', 'none' ], - [ 'invalidB64', 'valid', 'localStorage' ], - [ 'invalidJson', 'invalidJson', 'none' ], - [ 'invalidJson', 'invalidB64', 'none' ], - [ 'invalidJson', 'valid', 'localStorage' ], + ['valid', 'invalidB64', 'cookie'], + ['valid', 'invalidJson', 'cookie'], + ['invalidB64', 'invalidJson', 'none'], + ['invalidB64', 'invalidB64', 'none'], + ['invalidB64', 'valid', 'localStorage'], + ['invalidJson', 'invalidJson', 'none'], + ['invalidJson', 'invalidB64', 'none'], + ['invalidJson', 'valid', 'localStorage'], ].forEach(config => { it(`uses ${config[2]} if the cookie is ${config[0]} and localStorage is ${config[1]}`, function() { setCrb(config[0], config[1]); @@ -1351,29 +1351,33 @@ describe('kargo adapter tests', function() { ...testBids, { ...minimumBidParams, - ortb2: { device: { sua: { - platform: { - brand: 'macOS', - version: ['12', '6', '0'] - }, - browsers: [ - { - brand: 'Chromium', - version: ['106', '0', '5249', '119'] - }, - { - brand: 'Google Chrome', - version: ['106', '0', '5249', '119'] - }, - { - brand: 'Not;A=Brand', - version: ['99', '0', '0', '0'] + ortb2: { + device: { + sua: { + platform: { + brand: 'macOS', + version: ['12', '6', '0'] + }, + browsers: [ + { + brand: 'Chromium', + version: ['106', '0', '5249', '119'] + }, + { + brand: 'Google Chrome', + version: ['106', '0', '5249', '119'] + }, + { + brand: 'Not;A=Brand', + version: ['99', '0', '0', '0'] + } + ], + mobile: 1, + model: 'model', + source: 1, } - ], - mobile: 1, - model: 'model', - source: 1, - } } } + } + } } ]); expect(payload.device.sua).to.be.undefined; @@ -1383,55 +1387,63 @@ describe('kargo adapter tests', function() { const payload = getPayloadFromTestBids([ { ...minimumBidParams, - ortb2: { device: { sua: { - platform: { - brand: 'macOS', - version: ['12', '6', '0'] - }, - browsers: [ - { - brand: 'Chromium', - version: ['106', '0', '5249', '119'] - }, - { - brand: 'Google Chrome', - version: ['106', '0', '5249', '119'] - }, - { - brand: 'Not;A=Brand', - version: ['99', '0', '0', '0'] + ortb2: { + device: { + sua: { + platform: { + brand: 'macOS', + version: ['12', '6', '0'] + }, + browsers: [ + { + brand: 'Chromium', + version: ['106', '0', '5249', '119'] + }, + { + brand: 'Google Chrome', + version: ['106', '0', '5249', '119'] + }, + { + brand: 'Not;A=Brand', + version: ['99', '0', '0', '0'] + } + ], + mobile: 1, + model: 'model', + source: 1, } - ], - mobile: 1, - model: 'model', - source: 1, - } } } + } + } }, { ...minimumBidParams, - ortb2: { device: { sua: { - platform: { - brand: 'macOS2', - version: ['122', '6', '0'] - }, - browsers: [ - { - brand: 'Chromium2', - version: ['1062', '0', '5249', '119'] - }, - { - brand: 'Google Chrome2', - version: ['102', '0', '5249', '119'] - }, - { - brand: 'Not;A=Brand2', - version: ['992', '0', '0', '0'] + ortb2: { + device: { + sua: { + platform: { + brand: 'macOS2', + version: ['122', '6', '0'] + }, + browsers: [ + { + brand: 'Chromium2', + version: ['1062', '0', '5249', '119'] + }, + { + brand: 'Google Chrome2', + version: ['102', '0', '5249', '119'] + }, + { + brand: 'Not;A=Brand2', + version: ['992', '0', '0', '0'] + } + ], + mobile: 2, + model: 'model2', + source: 2, } - ], - mobile: 2, - model: 'model2', - source: 2, - } } } + } + } } ]); expect(payload.device.sua).to.deep.equal({ @@ -1460,34 +1472,39 @@ describe('kargo adapter tests', function() { }); it('does not send non-mapped attributes', function() { - const payload = getPayloadFromTestBids([{...minimumBidParams, - ortb2: { device: { sua: { - other: 'value', - objectMissing: { - key: 'value' - }, - platform: { - brand: 'macOS', - version: ['12', '6', '0'] - }, - browsers: [ - { - brand: 'Chromium', - version: ['106', '0', '5249', '119'] - }, - { - brand: 'Google Chrome', - version: ['106', '0', '5249', '119'] - }, - { - brand: 'Not;A=Brand', - version: ['99', '0', '0', '0'] + const payload = getPayloadFromTestBids([{ + ...minimumBidParams, + ortb2: { + device: { + sua: { + other: 'value', + objectMissing: { + key: 'value' + }, + platform: { + brand: 'macOS', + version: ['12', '6', '0'] + }, + browsers: [ + { + brand: 'Chromium', + version: ['106', '0', '5249', '119'] + }, + { + brand: 'Google Chrome', + version: ['106', '0', '5249', '119'] + }, + { + brand: 'Not;A=Brand', + version: ['99', '0', '0', '0'] + } + ], + mobile: 1, + model: 'model', + source: 1, } - ], - mobile: 1, - model: 'model', - source: 1, - } } } + } + } }]); expect(payload.device.sua).to.deep.equal({ platform: { @@ -1523,27 +1540,32 @@ describe('kargo adapter tests', function() { ' ', ' ', ].forEach(value => { - const payload = getPayloadFromTestBids([{...minimumBidParams, - ortb2: { device: { sua: { - platform: value, - browsers: [ - { - brand: 'Chromium', - version: ['106', '0', '5249', '119'] - }, - { - brand: 'Google Chrome', - version: ['106', '0', '5249', '119'] - }, - { - brand: 'Not;A=Brand', - version: ['99', '0', '0', '0'] + const payload = getPayloadFromTestBids([{ + ...minimumBidParams, + ortb2: { + device: { + sua: { + platform: value, + browsers: [ + { + brand: 'Chromium', + version: ['106', '0', '5249', '119'] + }, + { + brand: 'Google Chrome', + version: ['106', '0', '5249', '119'] + }, + { + brand: 'Not;A=Brand', + version: ['99', '0', '0', '0'] + } + ], + mobile: 1, + model: 'model', + source: 1, } - ], - mobile: 1, - model: 'model', - source: 1, - } } } + } + } }]); expect(payload.device.sua, `Value - ${JSON.stringify(value)}`).to.deep.equal({ browsers: [ @@ -1570,29 +1592,33 @@ describe('kargo adapter tests', function() { it('does not send 0 for mobile or source', function() { const payload = getPayloadFromTestBids([{ ...minimumBidParams, - ortb2: { device: { sua: { - platform: { - brand: 'macOS', - version: ['12', '6', '0'] - }, - browsers: [ - { - brand: 'Chromium', - version: ['106', '0', '5249', '119'] - }, - { - brand: 'Google Chrome', - version: ['106', '0', '5249', '119'] - }, - { - brand: 'Not;A=Brand', - version: ['99', '0', '0', '0'] + ortb2: { + device: { + sua: { + platform: { + brand: 'macOS', + version: ['12', '6', '0'] + }, + browsers: [ + { + brand: 'Chromium', + version: ['106', '0', '5249', '119'] + }, + { + brand: 'Google Chrome', + version: ['106', '0', '5249', '119'] + }, + { + brand: 'Not;A=Brand', + version: ['99', '0', '0', '0'] + } + ], + mobile: 0, + model: 'model', + source: 0, } - ], - mobile: 0, - model: 'model', - source: 0, - } } } + } + } }]); expect(payload.device.sua).to.deep.equal({ platform: { @@ -1666,71 +1692,73 @@ describe('kargo adapter tests', function() { }); describe('interpretResponse', function() { - const response = Object.freeze({ body: { - 1: { - id: 'foo', - cpm: 3, - adm: '
', - width: 320, - height: 50, - metadata: {}, - creativeID: 'bar' - }, - 2: { - id: 'bar', - cpm: 2.5, - adm: '
', - width: 300, - height: 250, - targetingCustom: 'dmpmptest1234', - metadata: { - landingPageDomain: ['https://foobar.com'] + const response = Object.freeze({ + body: { + 1: { + id: 'foo', + cpm: 3, + adm: '
', + width: 320, + height: 50, + metadata: {}, + creativeID: 'bar' }, - creativeID: 'foo' - }, - 3: { - id: 'bar', - cpm: 2.5, - adm: '
', - width: 300, - height: 250, - creativeID: 'foo' - }, - 4: { - id: 'bar', - cpm: 2.5, - adm: '
', - width: 300, - height: 250, - mediaType: 'banner', - metadata: {}, - creativeID: 'foo', - currency: 'EUR' - }, - 5: { - id: 'bar', - cpm: 2.5, - adm: '', - width: 300, - height: 250, - mediaType: 'video', - metadata: {}, - creativeID: 'foo', - currency: 'EUR' - }, - 6: { - id: 'bar', - cpm: 2.5, - adm: '', - admUrl: 'https://foobar.com/vast_adm', - width: 300, - height: 250, - mediaType: 'video', - metadata: {}, - creativeID: 'foo', - currency: 'EUR' + 2: { + id: 'bar', + cpm: 2.5, + adm: '
', + width: 300, + height: 250, + targetingCustom: 'dmpmptest1234', + metadata: { + landingPageDomain: ['https://foobar.com'] + }, + creativeID: 'foo' + }, + 3: { + id: 'bar', + cpm: 2.5, + adm: '
', + width: 300, + height: 250, + creativeID: 'foo' + }, + 4: { + id: 'bar', + cpm: 2.5, + adm: '
', + width: 300, + height: 250, + mediaType: 'banner', + metadata: {}, + creativeID: 'foo', + currency: 'EUR' + }, + 5: { + id: 'bar', + cpm: 2.5, + adm: '', + width: 300, + height: 250, + mediaType: 'video', + metadata: {}, + creativeID: 'foo', + currency: 'EUR' + }, + 6: { + id: 'bar', + cpm: 2.5, + adm: '', + admUrl: 'https://foobar.com/vast_adm', + width: 300, + height: 250, + mediaType: 'video', + metadata: {}, + creativeID: 'foo', + currency: 'EUR' + } } - }}); + }); const bidderRequest = Object.freeze({ currency: 'USD', bids: [{ @@ -1886,67 +1914,22 @@ describe('kargo adapter tests', function() { }); it('adds landingPageDomain data', function() { - const response = spec.interpretResponse({ body: { 0: { - metadata: { - landingPageDomain: [ - 'https://foo.com', - 'https://bar.com' - ] + const response = spec.interpretResponse({ + body: { + 0: { + metadata: { + landingPageDomain: [ + 'https://foo.com', + 'https://bar.com' + ] + } + } } - } } }, {}); + }, {}); expect(response[0].meta).to.deep.equal({ mediaType: 'banner', clickUrl: 'https://foo.com', - advertiserDomains: [ 'https://foo.com', 'https://bar.com' ] - }); - }); - - it('should return paapi if provided in bid response', function () { - const auctionConfig = { - seller: 'https://kargo.com', - decisionLogicUrl: 'https://kargo.com/decision_logic.js', - interestGroupBuyers: ['https://some_buyer.com'], - perBuyerSignals: { - 'https://some_buyer.com': { a: 1 } - } - } - - const body = response.body; - for (const key in body) { - if (body.hasOwnProperty(key)) { - if (key % 2 !== 0) { // Add auctionConfig to every other object - body[key].auctionConfig = auctionConfig; - } - } - } - - const result = spec.interpretResponse(response, bidderRequest); - - // Test properties of bidResponses - result.bids.forEach(bid => { - expect(bid).to.have.property('requestId'); - expect(bid).to.have.property('cpm'); - expect(bid).to.have.property('width'); - expect(bid).to.have.property('height'); - expect(bid).to.have.property('ttl'); - expect(bid).to.have.property('creativeId'); - expect(bid.netRevenue).to.be.true; - expect(bid).to.have.property('meta').that.is.an('object'); - }); - - // Test properties of paapi - expect(result.paapi).to.have.lengthOf(3); - - const expectedBidIds = ['1', '3', '5']; // Expected bidIDs - result.paapi.forEach(config => { - expect(config).to.have.property('bidId'); - expect(expectedBidIds).to.include(config.bidId); - - expect(config).to.have.property('config').that.is.an('object'); - expect(config.config).to.have.property('seller', 'https://kargo.com'); - expect(config.config).to.have.property('decisionLogicUrl', 'https://kargo.com/decision_logic.js'); - expect(config.config.interestGroupBuyers).to.be.an('array').that.includes('https://some_buyer.com'); - expect(config.config.perBuyerSignals).to.have.property('https://some_buyer.com').that.deep.equals({ a: 1 }); + advertiserDomains: ['https://foo.com', 'https://bar.com'] }); }); }); @@ -2089,7 +2072,7 @@ describe('kargo adapter tests', function() { describe('supportedMediaTypes', function() { it('exposes video and banner', function() { - expect(spec.supportedMediaTypes).to.deep.equal([ 'banner', 'video' ]); + expect(spec.supportedMediaTypes).to.deep.equal(['banner', 'video']); }); }); diff --git a/test/spec/modules/kimberliteBidAdapter_spec.js b/test/spec/modules/kimberliteBidAdapter_spec.js index af1e027ca4c..19942879154 100644 --- a/test/spec/modules/kimberliteBidAdapter_spec.js +++ b/test/spec/modules/kimberliteBidAdapter_spec.js @@ -75,7 +75,7 @@ describe('kimberliteBidAdapter', function () { bidRequests = [ { mediaTypes: { - [BANNER]: {sizes: sizes} + [BANNER]: { sizes: sizes } }, params: { placementId: 'test-placement' @@ -188,7 +188,7 @@ describe('kimberliteBidAdapter', function () { { bidId: 1, mediaTypes: { - banner: {sizes: sizes} + banner: { sizes: sizes } }, params: { placementId: 'test-placement' @@ -252,7 +252,7 @@ describe('kimberliteBidAdapter', function () { }); it('fails on empty response', function () { - const bids = spec.interpretResponse({body: ''}, bidRequest); + const bids = spec.interpretResponse({ body: '' }, bidRequest); assert.empty(bids); }); }); diff --git a/test/spec/modules/kinessoIdSystem_spec.js b/test/spec/modules/kinessoIdSystem_spec.js index c2c0b24aeb5..fab5ec781a6 100644 --- a/test/spec/modules/kinessoIdSystem_spec.js +++ b/test/spec/modules/kinessoIdSystem_spec.js @@ -1,10 +1,10 @@ import sinon from 'sinon'; -import {attachIdSystem} from '../../../modules/userId/index.js'; -import {kinessoIdSubmodule} from '../../../modules/kinessoIdSystem.js'; -import {createEidsArray} from '../../../modules/userId/eids.js'; +import { attachIdSystem } from '../../../modules/userId/index.js'; +import { kinessoIdSubmodule } from '../../../modules/kinessoIdSystem.js'; +import { createEidsArray } from '../../../modules/userId/eids.js'; import * as utils from '../../../src/utils.js'; import * as ajaxLib from '../../../src/ajax.js'; -import {expect} from 'chai/index.mjs'; +import { expect } from 'chai/index.mjs'; describe('kinesso ID', () => { describe('eid', () => { @@ -51,7 +51,7 @@ describe('kinesso ID', () => { it('decodes a string id', function() { const val = 'abc'; const result = kinessoIdSubmodule.decode(val); - expect(result).to.deep.equal({kpuid: val}); + expect(result).to.deep.equal({ kpuid: val }); expect(utils.logInfo.calledOnce).to.be.true; }); }); @@ -72,28 +72,28 @@ describe('kinesso ID', () => { }); it('requires numeric accountid', function() { - const res = kinessoIdSubmodule.getId({params: {accountid: 'bad'}}); + const res = kinessoIdSubmodule.getId({ params: { accountid: 'bad' } }); expect(res).to.be.undefined; expect(utils.logError.calledOnce).to.be.true; expect(ajaxStub.called).to.be.false; }); it('skips on coppa requests', function() { - const res = kinessoIdSubmodule.getId({params: {accountid: 7}}, {coppa: true}); + const res = kinessoIdSubmodule.getId({ params: { accountid: 7 } }, { coppa: true }); expect(res).to.be.undefined; expect(utils.logInfo.calledOnce).to.be.true; expect(ajaxStub.called).to.be.false; }); it('generates an id and posts to the endpoint', function() { - const consent = {gdpr: {gdprApplies: true, consentString: 'CONSENT'}, usp: '1NNN'}; - const result = kinessoIdSubmodule.getId({params: {accountid: 10}}, consent); + const consent = { gdpr: { gdprApplies: true, consentString: 'CONSENT' }, usp: '1NNN' }; + const result = kinessoIdSubmodule.getId({ params: { accountid: 10 } }, consent); expect(result).to.have.property('id').that.is.a('string').with.length(26); expect(ajaxStub.calledOnce).to.be.true; const [url,, payload, options] = ajaxStub.firstCall.args; expect(url).to.equal('https://id.knsso.com/id?accountid=10&us_privacy=1NNN&gdpr=1&gdpr_consent=CONSENT'); - expect(options).to.deep.equal({method: 'POST', withCredentials: true}); + expect(options).to.deep.equal({ method: 'POST', withCredentials: true }); expect(JSON.parse(payload)).to.have.property('id', result.id); }); }); diff --git a/test/spec/modules/kiviadsBidAdapter_spec.js b/test/spec/modules/kiviadsBidAdapter_spec.js index d7f9a233c9d..d7cbd189782 100644 --- a/test/spec/modules/kiviadsBidAdapter_spec.js +++ b/test/spec/modules/kiviadsBidAdapter_spec.js @@ -483,7 +483,7 @@ describe('KiviAdsBidAdapter', function () { const syncData = spec.getUserSyncs({}, {}, { consentString: 'ALL', gdprApplies: true, - }, {}); + }, undefined); expect(syncData).to.be.an('array').which.is.not.empty; expect(syncData[0]).to.be.an('object') expect(syncData[0].type).to.be.a('string') @@ -492,9 +492,7 @@ describe('KiviAdsBidAdapter', function () { expect(syncData[0].url).to.equal(`${syncUrl}/image?pbjs=1&gdpr=1&gdpr_consent=ALL&coppa=0`) }); it('Should return array of objects with proper sync config , include CCPA', function() { - const syncData = spec.getUserSyncs({}, {}, {}, { - consentString: '1---' - }); + const syncData = spec.getUserSyncs({}, {}, {}, '1---'); expect(syncData).to.be.an('array').which.is.not.empty; expect(syncData[0]).to.be.an('object') expect(syncData[0].type).to.be.a('string') @@ -503,7 +501,7 @@ describe('KiviAdsBidAdapter', function () { expect(syncData[0].url).to.equal(`${syncUrl}/image?pbjs=1&ccpa_consent=1---&coppa=0`) }); it('Should return array of objects with proper sync config , include GPP', function() { - const syncData = spec.getUserSyncs({}, {}, {}, {}, { + const syncData = spec.getUserSyncs({}, {}, {}, undefined, { gppString: 'abc123', applicableSections: [8] }); diff --git a/test/spec/modules/koblerBidAdapter_spec.js b/test/spec/modules/koblerBidAdapter_spec.js index 4e70ac6f9f3..78dd23a60a8 100644 --- a/test/spec/modules/koblerBidAdapter_spec.js +++ b/test/spec/modules/koblerBidAdapter_spec.js @@ -1,9 +1,9 @@ -import {expect} from 'chai'; -import {spec} from 'modules/koblerBidAdapter.js'; -import {newBidder} from 'src/adapters/bidderFactory.js'; -import {config} from 'src/config.js'; +import { expect } from 'chai'; +import { spec } from 'modules/koblerBidAdapter.js'; +import { newBidder } from 'src/adapters/bidderFactory.js'; +import { config } from 'src/config.js'; import * as utils from 'src/utils.js'; -import {getRefererInfo} from 'src/refererDetection.js'; +import { getRefererInfo } from 'src/refererDetection.js'; import { setConfig as setCurrencyConfig } from '../../../modules/currency.js'; import { addFPDToBidderRequest } from '../../helpers/fpd.js'; @@ -26,9 +26,9 @@ function createBidderRequest(auctionId, timeout, pageUrl, gdprVendorData = {}, p }; } -function createValidBidRequest(params, bidId, sizes) { +function createValidBidRequest(params, bidId, sizes, adUnitCode) { const validBidRequest = { - adUnitCode: 'adunit-code', + adUnitCode: adUnitCode || 'adunit-code', bidId: bidId || '22c4871113f461', bidder: 'kobler', bidderRequestId: '15246a574e859f', @@ -451,7 +451,8 @@ describe('KoblerAdapter', function () { dealIds: ['623472534328234'] }, '953ee65d-d18a-484f-a840-d3056185a060', - [[400, 600]] + [[400, 600]], + 'ad-unit-1' ), createValidBidRequest( { @@ -459,12 +460,14 @@ describe('KoblerAdapter', function () { dealIds: ['92368234753283', '263845832942'] }, '8320bf79-9d90-4a17-87c6-5d505706a921', - [[400, 500], [200, 250], [300, 350]] + [[400, 500], [200, 250], [300, 350]], + 'ad-unit-2' ), createValidBidRequest( undefined, 'd0de713b-32e3-4191-a2df-a007f08ffe72', - [[800, 900]] + [[800, 900]], + 'ad-unit-3' ) ]; const bidderRequest = createBidderRequest( @@ -520,6 +523,11 @@ describe('KoblerAdapter', function () { id: '623472534328234' } ] + }, + ext: { + prebid: { + adunitcode: 'ad-unit-1' + } } }, { @@ -553,6 +561,11 @@ describe('KoblerAdapter', function () { id: '263845832942' } ] + }, + ext: { + prebid: { + adunitcode: 'ad-unit-2' + } } }, { @@ -569,7 +582,12 @@ describe('KoblerAdapter', function () { }, bidfloor: 0, bidfloorcur: 'USD', - pmp: {} + pmp: {}, + ext: { + prebid: { + adunitcode: 'ad-unit-3' + } + } } ], device: { @@ -723,12 +741,18 @@ describe('KoblerAdapter', function () { const bidderRequest = { refererInfo }; return addFPDToBidderRequest(bidderRequest).then(res => { JSON.parse(spec.buildRequests(validBidRequests, res).data); - const bids = spec.interpretResponse({ body: { seatbid: [{ bid: [{ - originalCpm: 1.532, - price: 8.341, - currency: 'NOK', - nurl: 'https://atag.essrtb.com/serve/prebid_win_notification?payload=sdhfusdaobfadslf234324&sp=${AUCTION_PRICE}&sp_cur=${AUCTION_PRICE_CURRENCY}&asp=${AD_SERVER_PRICE}&asp_cur=${AD_SERVER_PRICE_CURRENCY}', - }]}]}}, { bidderRequest: res }); + const bids = spec.interpretResponse({ + body: { + seatbid: [{ + bid: [{ + originalCpm: 1.532, + price: 8.341, + currency: 'NOK', + nurl: 'https://atag.essrtb.com/serve/prebid_win_notification?payload=sdhfusdaobfadslf234324&sp=${AUCTION_PRICE}&sp_cur=${AUCTION_PRICE_CURRENCY}&asp=${AD_SERVER_PRICE}&asp_cur=${AD_SERVER_PRICE_CURRENCY}', + }] + }] + } + }, { bidderRequest: res }); const bidToWon = bids[0]; bidToWon.adserverTargeting = { hb_pb: 8 diff --git a/test/spec/modules/krushmediaBidAdapter_spec.js b/test/spec/modules/krushmediaBidAdapter_spec.js index 98bdbcbb855..044d9d4f26b 100644 --- a/test/spec/modules/krushmediaBidAdapter_spec.js +++ b/test/spec/modules/krushmediaBidAdapter_spec.js @@ -483,7 +483,7 @@ describe('KrushmediabBidAdapter', function () { const syncData = spec.getUserSyncs({}, {}, { consentString: 'ALL', gdprApplies: true, - }, {}); + }, undefined); expect(syncData).to.be.an('array').which.is.not.empty; expect(syncData[0]).to.be.an('object') expect(syncData[0].type).to.be.a('string') @@ -492,9 +492,7 @@ describe('KrushmediabBidAdapter', function () { expect(syncData[0].url).to.equal('https://cs.krushmedia.com/image?pbjs=1&gdpr=1&gdpr_consent=ALL&coppa=0') }); it('Should return array of objects with proper sync config , include CCPA', function() { - const syncData = spec.getUserSyncs({}, {}, {}, { - consentString: '1---' - }); + const syncData = spec.getUserSyncs({}, {}, {}, '1---'); expect(syncData).to.be.an('array').which.is.not.empty; expect(syncData[0]).to.be.an('object') expect(syncData[0].type).to.be.a('string') @@ -503,7 +501,7 @@ describe('KrushmediabBidAdapter', function () { expect(syncData[0].url).to.equal('https://cs.krushmedia.com/image?pbjs=1&ccpa_consent=1---&coppa=0') }); it('Should return array of objects with proper sync config , include GPP', function() { - const syncData = spec.getUserSyncs({}, {}, {}, {}, { + const syncData = spec.getUserSyncs({}, {}, {}, undefined, { gppString: 'abc123', applicableSections: [8] }); diff --git a/test/spec/modules/kubientBidAdapter_spec.js b/test/spec/modules/kubientBidAdapter_spec.js index 4a162e8575e..074aef4d3ae 100644 --- a/test/spec/modules/kubientBidAdapter_spec.js +++ b/test/spec/modules/kubientBidAdapter_spec.js @@ -1,7 +1,7 @@ import { expect, assert } from 'chai'; import { spec } from 'modules/kubientBidAdapter.js'; import { BANNER, VIDEO } from '../../../src/mediaTypes.js'; -import {config} from '../../../src/config.js'; +import { config } from '../../../src/config.js'; function encodeQueryData(data) { return Object.keys(data).map(function(key) { @@ -117,8 +117,8 @@ describe('KubientAdapter', function () { config.resetConfig(); }); it('Creates Banner 1 ServerRequest object with method, URL and data', function () { - config.setConfig({'coppa': false}); - const serverRequests = spec.buildRequests([bidBanner], Object.assign({}, bidderRequest, {bids: [bidBanner]})); + config.setConfig({ 'coppa': false }); + const serverRequests = spec.buildRequests([bidBanner], Object.assign({}, bidderRequest, { bids: [bidBanner] })); expect(serverRequests).to.be.an('array'); for (let i = 0; i < serverRequests.length; i++) { const serverRequest = serverRequests[i]; @@ -153,8 +153,8 @@ describe('KubientAdapter', function () { config.resetConfig(); }); it('Creates Video 1 ServerRequest object with method, URL and data', function () { - config.setConfig({'coppa': false}); - const serverRequests = spec.buildRequests([bidVideo], Object.assign({}, bidderRequest, {bids: [bidVideo]})); + config.setConfig({ 'coppa': false }); + const serverRequests = spec.buildRequests([bidVideo], Object.assign({}, bidderRequest, { bids: [bidVideo] })); expect(serverRequests).to.be.an('array'); for (let i = 0; i < serverRequests.length; i++) { const serverRequest = serverRequests[i]; @@ -190,8 +190,8 @@ describe('KubientAdapter', function () { config.resetConfig(); }); it('Creates Banner 2 ServerRequest object with method, URL and data with bidBanner', function () { - config.setConfig({'coppa': true}); - const serverRequests = spec.buildRequests([bidBanner], Object.assign({}, bidderRequest, {bids: [bidBanner]})); + config.setConfig({ 'coppa': true }); + const serverRequests = spec.buildRequests([bidBanner], Object.assign({}, bidderRequest, { bids: [bidBanner] })); expect(serverRequests).to.be.an('array'); for (let i = 0; i < serverRequests.length; i++) { const serverRequest = serverRequests[i]; @@ -227,8 +227,8 @@ describe('KubientAdapter', function () { config.resetConfig(); }); it('Creates Video 2 ServerRequest object with method, URL and data', function () { - config.setConfig({'coppa': true}); - const serverRequests = spec.buildRequests([bidVideo], Object.assign({}, bidderRequest, {bids: [bidVideo]})); + config.setConfig({ 'coppa': true }); + const serverRequests = spec.buildRequests([bidVideo], Object.assign({}, bidderRequest, { bids: [bidVideo] })); expect(serverRequests).to.be.an('array'); for (let i = 0; i < serverRequests.length; i++) { const serverRequest = serverRequests[i]; @@ -300,7 +300,7 @@ describe('KubientAdapter', function () { cur: 'USD', netRevenue: false, ttl: 360, - meta: {adomain: ['google.com', 'yahoo.com']} + meta: { adomain: ['google.com', 'yahoo.com'] } } ] } @@ -351,7 +351,7 @@ describe('KubientAdapter', function () { cur: 'USD', netRevenue: false, ttl: 360, - meta: {adomain: ['google.com', 'yahoo.com']} + meta: { adomain: ['google.com', 'yahoo.com'] } } ] } diff --git a/test/spec/modules/kueezRtbBidAdapter_spec.js b/test/spec/modules/kueezRtbBidAdapter_spec.js index 08e4fefdaf8..67ea5b7363c 100644 --- a/test/spec/modules/kueezRtbBidAdapter_spec.js +++ b/test/spec/modules/kueezRtbBidAdapter_spec.js @@ -1,4 +1,4 @@ -import {expect} from 'chai'; +import { expect } from 'chai'; import { spec as adapter, createDomain, @@ -7,10 +7,10 @@ import { createFirstPartyData, } from 'modules/kueezRtbBidAdapter.js'; import * as utils from 'src/utils.js'; -import {version} from 'package.json'; -import {useFakeTimers} from 'sinon'; -import {BANNER, VIDEO} from '../../../src/mediaTypes.js'; -import {config} from '../../../src/config.js'; +import { version } from 'package.json'; +import { useFakeTimers } from 'sinon'; +import { BANNER, VIDEO } from '../../../src/mediaTypes.js'; +import { config } from '../../../src/config.js'; import { hashCode, extractPID, @@ -21,7 +21,7 @@ import { tryParseJSON, getUniqueDealId, } from '../../../libraries/vidazooUtils/bidderUtils.js'; -import {getGlobal} from '../../../src/prebidGlobal.js'; +import { getGlobal } from '../../../src/prebidGlobal.js'; export const TEST_ID_SYSTEMS = ['britepoolid', 'criteoId', 'id5id', 'idl_env', 'lipb', 'netId', 'parrableId', 'pubcid', 'tdid', 'pubProvidedId']; @@ -102,9 +102,9 @@ const ORTB2_DEVICE = { 'version': ['8', '0', '0'] }, 'browsers': [ - {'brand': 'Not_A Brand', 'version': ['99', '0', '0', '0']}, - {'brand': 'Google Chrome', 'version': ['109', '0', '5414', '119']}, - {'brand': 'Chromium', 'version': ['109', '0', '5414', '119']} + { 'brand': 'Not_A Brand', 'version': ['99', '0', '0', '0'] }, + { 'brand': 'Google Chrome', 'version': ['109', '0', '5414', '119'] }, + { 'brand': 'Chromium', 'version': ['109', '0', '5414', '119'] } ], 'mobile': 1, 'model': 'SM-G955U', @@ -121,7 +121,7 @@ const ORTB2_DEVICE = { model: 'iPhone 12 Pro Max', os: 'iOS', osv: '17.4', - ext: {fiftyonedegrees_deviceId: '17595-133085-133468-18092'}, + ext: { fiftyonedegrees_deviceId: '17595-133085-133468-18092' }, }; const BIDDER_REQUEST = { @@ -192,9 +192,8 @@ const VIDEO_SERVER_RESPONSE = { const ORTB2_OBJ = { "device": ORTB2_DEVICE, - "regs": {"coppa": 0, "gpp": "gpp_string", "gpp_sid": [7]}, - "site": {"content": {"language": "en"} - } + "regs": { "coppa": 0, "gpp": "gpp_string", "gpp_sid": [7] }, + "site": { "content": { "language": "en" } } }; const REQUEST = { @@ -207,7 +206,7 @@ const REQUEST = { function getTopWindowQueryParams() { try { - const parsedUrl = utils.parseUrl(window.top.document.URL, {decodeSearchAsString: true}); + const parsedUrl = utils.parseUrl(window.top.document.URL, { decodeSearchAsString: true }); return parsedUrl.search; } catch (e) { return ''; @@ -333,9 +332,9 @@ describe('KueezRtbBidAdapter', function () { 'version': ['8', '0', '0'] }, 'browsers': [ - {'brand': 'Not_A Brand', 'version': ['99', '0', '0', '0']}, - {'brand': 'Google Chrome', 'version': ['109', '0', '5414', '119']}, - {'brand': 'Chromium', 'version': ['109', '0', '5414', '119']} + { 'brand': 'Not_A Brand', 'version': ['99', '0', '0', '0'] }, + { 'brand': 'Google Chrome', 'version': ['109', '0', '5414', '119'] }, + { 'brand': 'Chromium', 'version': ['109', '0', '5414', '119'] } ], 'mobile': 1, 'model': 'SM-G955U', @@ -408,9 +407,9 @@ describe('KueezRtbBidAdapter', function () { 'version': ['8', '0', '0'] }, 'browsers': [ - {'brand': 'Not_A Brand', 'version': ['99', '0', '0', '0']}, - {'brand': 'Google Chrome', 'version': ['109', '0', '5414', '119']}, - {'brand': 'Chromium', 'version': ['109', '0', '5414', '119']} + { 'brand': 'Not_A Brand', 'version': ['99', '0', '0', '0'] }, + { 'brand': 'Google Chrome', 'version': ['109', '0', '5414', '119'] }, + { 'brand': 'Chromium', 'version': ['109', '0', '5414', '119'] } ], 'mobile': 1, 'model': 'SM-G955U', @@ -455,7 +454,7 @@ describe('KueezRtbBidAdapter', function () { }); describe('getUserSyncs', function () { it('should have valid user sync with iframeEnabled', function () { - const result = adapter.getUserSyncs({iframeEnabled: true}, [SERVER_RESPONSE]); + const result = adapter.getUserSyncs({ iframeEnabled: true }, [SERVER_RESPONSE]); expect(result).to.deep.equal([{ type: 'iframe', @@ -464,7 +463,7 @@ describe('KueezRtbBidAdapter', function () { }); it('should have valid user sync with cid on response', function () { - const result = adapter.getUserSyncs({iframeEnabled: true}, [SERVER_RESPONSE]); + const result = adapter.getUserSyncs({ iframeEnabled: true }, [SERVER_RESPONSE]); expect(result).to.deep.equal([{ type: 'iframe', url: 'https://sync.kueezrtb.com/api/sync/iframe/?cid=testcid123&gdpr=0&gdpr_consent=&us_privacy=&coppa=0' @@ -472,7 +471,7 @@ describe('KueezRtbBidAdapter', function () { }); it('should have valid user sync with pixelEnabled', function () { - const result = adapter.getUserSyncs({pixelEnabled: true}, [SERVER_RESPONSE]); + const result = adapter.getUserSyncs({ pixelEnabled: true }, [SERVER_RESPONSE]); expect(result).to.deep.equal([{ 'url': 'https://sync.kueezrtb.com/api/sync/image/?cid=testcid123&gdpr=0&gdpr_consent=&us_privacy=&coppa=0', @@ -484,7 +483,7 @@ describe('KueezRtbBidAdapter', function () { config.setConfig({ coppa: 1 }); - const result = adapter.getUserSyncs({iframeEnabled: true}, [SERVER_RESPONSE]); + const result = adapter.getUserSyncs({ iframeEnabled: true }, [SERVER_RESPONSE]); expect(result).to.deep.equal([{ type: 'iframe', url: 'https://sync.kueezrtb.com/api/sync/iframe/?cid=testcid123&gdpr=0&gdpr_consent=&us_privacy=&coppa=1' @@ -502,7 +501,7 @@ describe('KueezRtbBidAdapter', function () { applicableSections: [7] } - const result = adapter.getUserSyncs({pixelEnabled: true}, [SERVER_RESPONSE], gdprConsent, uspConsent, gppConsent); + const result = adapter.getUserSyncs({ pixelEnabled: true }, [SERVER_RESPONSE], gdprConsent, uspConsent, gppConsent); expect(result).to.deep.equal([{ 'url': 'https://sync.kueezrtb.com/api/sync/image/?cid=testcid123&gdpr=1&gdpr_consent=consent_string&us_privacy=usp_string&coppa=1&gpp=gpp_string&gpp_sid=7', @@ -518,12 +517,12 @@ describe('KueezRtbBidAdapter', function () { }); it('should return empty array when there is no ad', function () { - const responses = adapter.interpretResponse({price: 1, ad: ''}); + const responses = adapter.interpretResponse({ price: 1, ad: '' }); expect(responses).to.be.empty; }); it('should return empty array when there is no price', function () { - const responses = adapter.interpretResponse({price: null, ad: 'great ad'}); + const responses = adapter.interpretResponse({ price: null, ad: 'great ad' }); expect(responses).to.be.empty; }); @@ -596,9 +595,9 @@ describe('KueezRtbBidAdapter', function () { const userId = (function () { switch (idSystemProvider) { case 'lipb': - return {lipbid: id}; + return { lipbid: id }; case 'id5id': - return {uid: id}; + return { uid: id }; default: return id; } @@ -619,7 +618,7 @@ describe('KueezRtbBidAdapter', function () { bid.userIdAsEids = [ { "source": "audigent.com", - "uids": [{"id": "fakeidi6j6dlc6e"}] + "uids": [{ "id": "fakeidi6j6dlc6e" }] } ] const requests = adapter.buildRequests([bid], BIDDER_REQUEST); @@ -630,11 +629,11 @@ describe('KueezRtbBidAdapter', function () { bid.userIdAsEids = [ { "source": "audigent.com", - "uids": [{"id": "fakeidi6j6dlc6e"}] + "uids": [{ "id": "fakeidi6j6dlc6e" }] }, { "source": "rwdcntrl.net", - "uids": [{"id": "fakeid6f35197d5c", "atype": 1}] + "uids": [{ "id": "fakeid6f35197d5c", "atype": 1 }] } ] const requests = adapter.buildRequests([bid], BIDDER_REQUEST); @@ -649,7 +648,7 @@ describe('KueezRtbBidAdapter', function () { eids: [ { "source": "pubcid.org", - "uids": [{"id": "fakeid8888dlc6e"}] + "uids": [{ "id": "fakeid8888dlc6e" }] } ] } @@ -664,11 +663,11 @@ describe('KueezRtbBidAdapter', function () { eids: [ { "source": "pubcid.org", - "uids": [{"id": "fakeid8888dlc6e"}] + "uids": [{ "id": "fakeid8888dlc6e" }] }, { "source": "adserver.org", - "uids": [{"id": "fakeid495ff1"}] + "uids": [{ "id": "fakeid495ff1" }] } ] } @@ -681,18 +680,18 @@ describe('KueezRtbBidAdapter', function () { describe('alternate param names extractors', function () { it('should return undefined when param not supported', function () { - const cid = extractCID({'c_id': '1'}); - const pid = extractPID({'p_id': '1'}); - const subDomain = extractSubDomain({'sub_domain': 'prebid'}); + const cid = extractCID({ 'c_id': '1' }); + const pid = extractPID({ 'p_id': '1' }); + const subDomain = extractSubDomain({ 'sub_domain': 'prebid' }); expect(cid).to.be.undefined; expect(pid).to.be.undefined; expect(subDomain).to.be.undefined; }); it('should return value when param supported', function () { - const cid = extractCID({'cID': '1'}); - const pid = extractPID({'Pid': '2'}); - const subDomain = extractSubDomain({'subDOMAIN': 'prebid'}); + const cid = extractCID({ 'cID': '1' }); + const pid = extractPID({ 'Pid': '2' }); + const subDomain = extractSubDomain({ 'subDOMAIN': 'prebid' }); expect(cid).to.be.equal('1'); expect(pid).to.be.equal('2'); expect(subDomain).to.be.equal('prebid'); @@ -752,7 +751,7 @@ describe('KueezRtbBidAdapter', function () { now }); setStorageItem(storage, 'myKey', 2020); - const {value, created} = getStorageItem(storage, 'myKey'); + const { value, created } = getStorageItem(storage, 'myKey'); expect(created).to.be.equal(now); expect(value).to.be.equal(2020); expect(typeof value).to.be.equal('number'); @@ -768,8 +767,8 @@ describe('KueezRtbBidAdapter', function () { }); it('should parse JSON value', function () { - const data = JSON.stringify({event: 'send'}); - const {event} = tryParseJSON(data); + const data = JSON.stringify({ event: 'send' }); + const { event } = tryParseJSON(data); expect(event).to.be.equal('send'); }); diff --git a/test/spec/modules/leagueMBidAdapter_spec.js b/test/spec/modules/leagueMBidAdapter_spec.js new file mode 100644 index 00000000000..a85b7d65746 --- /dev/null +++ b/test/spec/modules/leagueMBidAdapter_spec.js @@ -0,0 +1,441 @@ +import { expect } from 'chai'; +import { config } from 'src/config.js'; +import { spec } from 'modules/leagueMBidAdapter.js'; +import { deepClone } from 'src/utils'; +import { getBidFloor } from '../../../libraries/xeUtils/bidderUtils.js'; + +const ENDPOINT = 'https://pbjs.league-m.media'; + +const defaultRequest = { + tmax: 0, + adUnitCode: 'test', + bidId: '1', + requestId: 'qwerty', + ortb2: { + source: { + tid: 'auctionId' + } + }, + ortb2Imp: { + ext: { + tid: 'tr1', + } + }, + mediaTypes: { + banner: { + sizes: [ + [300, 250], + [300, 200] + ] + } + }, + bidder: 'leagueM', + params: { + pid: 'aa8217e20131c095fe9dba67981040b0', + ext: {} + }, + bidRequestsCount: 1 +}; + +const defaultRequestVideo = deepClone(defaultRequest); +defaultRequestVideo.mediaTypes = { + video: { + playerSize: [640, 480], + context: 'instream', + skippable: true + } +}; + +const videoBidderRequest = { + bidderCode: 'leagueM', + bids: [{ mediaTypes: { video: {} }, bidId: 'qwerty' }] +}; + +const displayBidderRequest = { + bidderCode: 'leagueM', + bids: [{ bidId: 'qwerty' }] +}; + +describe('leagueMBidAdapter', () => { + describe('isBidRequestValid', function () { + it('should return false when request params is missing', function () { + const invalidRequest = deepClone(defaultRequest); + delete invalidRequest.params; + expect(spec.isBidRequestValid(invalidRequest)).to.equal(false); + }); + + it('should return false when required pid param is missing', function () { + const invalidRequest = deepClone(defaultRequest); + delete invalidRequest.params.pid; + expect(spec.isBidRequestValid(invalidRequest)).to.equal(false); + }); + + it('should return false when video.playerSize is missing', function () { + const invalidRequest = deepClone(defaultRequestVideo); + delete invalidRequest.mediaTypes.video.playerSize; + expect(spec.isBidRequestValid(invalidRequest)).to.equal(false); + }); + + it('should return true when required params found', function () { + expect(spec.isBidRequestValid(defaultRequest)).to.equal(true); + }); + }); + + describe('buildRequests', function () { + beforeEach(function () { + config.resetConfig(); + }); + + it('should send request with correct structure', function () { + const request = spec.buildRequests([defaultRequest], {}); + expect(request.method).to.equal('POST'); + expect(request.url).to.equal(ENDPOINT + '/bid'); + expect(request.options).to.have.property('contentType').and.to.equal('application/json'); + expect(request).to.have.property('data'); + }); + + it('should build basic request structure', function () { + const request = JSON.parse(spec.buildRequests([defaultRequest], {}).data)[0]; + expect(request).to.have.property('tmax').and.to.equal(defaultRequest.tmax); + expect(request).to.have.property('bidId').and.to.equal(defaultRequest.bidId); + expect(request).to.have.property('auctionId').and.to.equal(defaultRequest.ortb2.source.tid); + expect(request).to.have.property('transactionId').and.to.equal(defaultRequest.ortb2Imp.ext.tid); + expect(request).to.have.property('tz').and.to.equal(new Date().getTimezoneOffset()); + expect(request).to.have.property('bc').and.to.equal(1); + expect(request).to.have.property('floor').and.to.equal(null); + expect(request).to.have.property('banner').and.to.deep.equal({ sizes: [[300, 250], [300, 200]] }); + expect(request).to.have.property('gdprConsent').and.to.deep.equal({}); + expect(request).to.have.property('userEids').and.to.deep.equal([]); + expect(request).to.have.property('usPrivacy').and.to.equal(''); + expect(request).to.have.property('sizes').and.to.deep.equal(['300x250', '300x200']); + expect(request).to.have.property('ext').and.to.deep.equal({}); + expect(request).to.have.property('env').and.to.deep.equal({ + pid: 'aa8217e20131c095fe9dba67981040b0' + }); + expect(request).to.have.property('device').and.to.deep.equal({ + ua: navigator.userAgent, + lang: navigator.language + }); + }); + + it('should build request with schain', function () { + const schainRequest = deepClone(defaultRequest); + const bidderRequest = { + ortb2: { + source: { + ext: { + schain: { + ver: '1.0' + } + } + } + } + }; + const request = JSON.parse(spec.buildRequests([schainRequest], bidderRequest).data)[0]; + expect(request).to.have.property('schain').and.to.deep.equal({ + ver: '1.0' + }); + }); + + it('should build request with location', function () { + const bidderRequest = { + refererInfo: { + page: 'page', + location: 'location', + domain: 'domain', + ref: 'ref', + isAmp: false + } + }; + const request = JSON.parse(spec.buildRequests([defaultRequest], bidderRequest).data)[0]; + expect(request).to.have.property('location'); + const location = request.location; + expect(location).to.have.property('page').and.to.equal('page'); + expect(location).to.have.property('location').and.to.equal('location'); + expect(location).to.have.property('domain').and.to.equal('domain'); + expect(location).to.have.property('ref').and.to.equal('ref'); + expect(location).to.have.property('isAmp').and.to.equal(false); + }); + + it('should build request with ortb2 info', function () { + const ortb2Request = deepClone(defaultRequest); + ortb2Request.ortb2 = { + site: { + name: 'name' + } + }; + const request = JSON.parse(spec.buildRequests([ortb2Request], {}).data)[0]; + expect(request).to.have.property('ortb2').and.to.deep.equal({ + site: { + name: 'name' + } + }); + }); + + it('should build request with ortb2Imp info', function () { + const ortb2ImpRequest = deepClone(defaultRequest); + ortb2ImpRequest.ortb2Imp = { + ext: { + data: { + pbadslot: 'home1', + adUnitSpecificAttribute: '1' + } + } + }; + const request = JSON.parse(spec.buildRequests([ortb2ImpRequest], {}).data)[0]; + expect(request).to.have.property('ortb2Imp').and.to.deep.equal({ + ext: { + data: { + pbadslot: 'home1', + adUnitSpecificAttribute: '1' + } + } + }); + }); + + it('should build request with valid bidfloor', function () { + const bfRequest = deepClone(defaultRequest); + bfRequest.getFloor = () => ({ floor: 5, currency: 'USD' }); + const request = JSON.parse(spec.buildRequests([bfRequest], {}).data)[0]; + expect(request).to.have.property('floor').and.to.equal(5); + }); + + it('should build request with usp consent data if applies', function () { + const bidderRequest = { + uspConsent: '1YA-' + }; + const request = JSON.parse(spec.buildRequests([defaultRequest], bidderRequest).data)[0]; + expect(request).to.have.property('usPrivacy').and.equals('1YA-'); + }); + + it('should build request with extended ids', function () { + const idRequest = deepClone(defaultRequest); + idRequest.userIdAsEids = [ + { source: 'adserver.org', uids: [{ id: 'TTD_ID_FROM_USER_ID_MODULE', atype: 1, ext: { rtiPartner: 'TDID' } }] }, + { source: 'pubcid.org', uids: [{ id: 'pubCommonId_FROM_USER_ID_MODULE', atype: 1 }] } + ]; + const request = JSON.parse(spec.buildRequests([idRequest], {}).data)[0]; + expect(request).to.have.property('userEids').and.deep.equal(idRequest.userIdAsEids); + }); + + it('should build request with video', function () { + const request = JSON.parse(spec.buildRequests([defaultRequestVideo], {}).data)[0]; + expect(request).to.have.property('video').and.to.deep.equal({ + playerSize: [640, 480], + context: 'instream', + skippable: true + }); + expect(request).to.have.property('sizes').and.to.deep.equal(['640x480']); + }); + }); + + describe('interpretResponse', function () { + it('should return empty bids', function () { + const serverResponse = { + body: { + data: null + } + }; + + const invalidResponse = spec.interpretResponse(serverResponse, {}); + expect(invalidResponse).to.be.an('array').that.is.empty; + }); + + it('should interpret valid response', function () { + const serverResponse = { + body: { + data: [{ + requestId: 'qwerty', + cpm: 1, + currency: 'USD', + width: 300, + height: 250, + ttl: 600, + meta: { + advertiserDomains: ['leagueM'] + }, + ext: { + pixels: [ + ['iframe', 'surl1'], + ['image', 'surl2'], + ] + } + }] + } + }; + + const validResponse = spec.interpretResponse(serverResponse, { bidderRequest: displayBidderRequest }); + const bid = validResponse[0]; + expect(validResponse).to.be.an('array').that.is.not.empty; + expect(bid.requestId).to.equal('qwerty'); + expect(bid.cpm).to.equal(1); + expect(bid.currency).to.equal('USD'); + expect(bid.width).to.equal(300); + expect(bid.height).to.equal(250); + expect(bid.ttl).to.equal(600); + expect(bid.meta).to.deep.equal({ advertiserDomains: ['leagueM'] }); + }); + + it('should interpret valid banner response', function () { + const serverResponse = { + body: { + data: [{ + requestId: 'qwerty', + cpm: 1, + currency: 'USD', + width: 300, + height: 250, + ttl: 600, + mediaType: 'banner', + creativeId: 'demo-banner', + ad: 'ad', + meta: {} + }] + } + }; + + const validResponseBanner = spec.interpretResponse(serverResponse, { bidderRequest: displayBidderRequest }); + const bid = validResponseBanner[0]; + expect(validResponseBanner).to.be.an('array').that.is.not.empty; + expect(bid.mediaType).to.equal('banner'); + expect(bid.creativeId).to.equal('demo-banner'); + expect(bid.ad).to.equal('ad'); + }); + + it('should interpret valid video response', function () { + const serverResponse = { + body: { + data: [{ + requestId: 'qwerty', + cpm: 1, + currency: 'USD', + width: 600, + height: 480, + ttl: 600, + mediaType: 'video', + creativeId: 'demo-video', + ad: 'vast-xml', + meta: {} + }] + } + }; + + const validResponseBanner = spec.interpretResponse(serverResponse, { bidderRequest: videoBidderRequest }); + const bid = validResponseBanner[0]; + expect(validResponseBanner).to.be.an('array').that.is.not.empty; + expect(bid.mediaType).to.equal('video'); + expect(bid.creativeId).to.equal('demo-video'); + expect(bid.ad).to.equal('vast-xml'); + }); + }); + + describe('getUserSyncs', function () { + it('should handle no params', function () { + const opts = spec.getUserSyncs({}, []); + expect(opts).to.be.an('array').that.is.empty; + }); + + it('should return empty if sync is not allowed', function () { + const opts = spec.getUserSyncs({ iframeEnabled: false, pixelEnabled: false }); + expect(opts).to.be.an('array').that.is.empty; + }); + + it('should allow iframe sync', function () { + const opts = spec.getUserSyncs({ iframeEnabled: true, pixelEnabled: false }, [{ + body: { + data: [{ + requestId: 'qwerty', + ext: { + pixels: [ + ['iframe', 'surl1?a=b'], + ['image', 'surl2?a=b'], + ] + } + }] + } + }]); + expect(opts.length).to.equal(1); + expect(opts[0].type).to.equal('iframe'); + expect(opts[0].url).to.equal('surl1?a=b&us_privacy=&gdpr=0&gdpr_consent='); + }); + + it('should allow pixel sync', function () { + const opts = spec.getUserSyncs({ iframeEnabled: false, pixelEnabled: true }, [{ + body: { + data: [{ + requestId: 'qwerty', + ext: { + pixels: [ + ['iframe', 'surl1?a=b'], + ['image', 'surl2?a=b'], + ] + } + }] + } + }]); + expect(opts.length).to.equal(1); + expect(opts[0].type).to.equal('image'); + expect(opts[0].url).to.equal('surl2?a=b&us_privacy=&gdpr=0&gdpr_consent='); + }); + + it('should allow pixel sync and parse consent params', function () { + const opts = spec.getUserSyncs({ iframeEnabled: false, pixelEnabled: true }, [{ + body: { + data: [{ + requestId: 'qwerty', + ext: { + pixels: [ + ['iframe', 'surl1?a=b'], + ['image', 'surl2?a=b'], + ] + } + }] + } + }], { + gdprApplies: 1, + consentString: '1YA-' + }); + expect(opts.length).to.equal(1); + expect(opts[0].type).to.equal('image'); + expect(opts[0].url).to.equal('surl2?a=b&us_privacy=&gdpr=1&gdpr_consent=1YA-'); + }); + }); + + describe('getBidFloor', function () { + it('should return null when getFloor is not a function', () => { + const bid = { getFloor: 2 }; + const result = getBidFloor(bid); + expect(result).to.be.null; + }); + + it('should return null when getFloor doesnt return an object', () => { + const bid = { getFloor: () => 2 }; + const result = getBidFloor(bid); + expect(result).to.be.null; + }); + + it('should return null when floor is not a number', () => { + const bid = { + getFloor: () => ({ floor: 'string', currency: 'USD' }) + }; + const result = getBidFloor(bid); + expect(result).to.be.null; + }); + + it('should return null when currency is not USD', () => { + const bid = { + getFloor: () => ({ floor: 5, currency: 'EUR' }) + }; + const result = getBidFloor(bid); + expect(result).to.be.null; + }); + + it('should return floor value when everything is correct', () => { + const bid = { + getFloor: () => ({ floor: 5, currency: 'USD' }) + }; + const result = getBidFloor(bid); + expect(result).to.equal(5); + }); + }); +}); diff --git a/test/spec/modules/limelightDigitalBidAdapter_spec.js b/test/spec/modules/limelightDigitalBidAdapter_spec.js index 31b8530c7eb..a4cff04599b 100644 --- a/test/spec/modules/limelightDigitalBidAdapter_spec.js +++ b/test/spec/modules/limelightDigitalBidAdapter_spec.js @@ -1,5 +1,6 @@ import { expect } from 'chai'; import { spec } from '../../../modules/limelightDigitalBidAdapter.js'; +import { deepAccess } from '../../../src/utils.js'; describe('limelightDigitalAdapter', function () { const bid1 = { @@ -42,25 +43,9 @@ describe('limelightDigitalAdapter', function () { } ] } - ], - ortb2: { - source: { - ext: { - schain: { - ver: '1.0', - complete: 1, - nodes: [ - { - asi: 'example.com', - sid: '1', - hp: 1 - } - ] - } - } - } - } - } + ] + }; + const bid2 = { bidId: '58ee9870c3164a', bidder: 'limelightDigital', @@ -77,13 +62,17 @@ describe('limelightDigitalAdapter', function () { }, placementCode: 'placement_1', auctionId: '482f88de-29ab-45c8-981a-d25e39454a34', - sizes: [[350, 200]], + mediaTypes: { + banner: { + sizes: [[350, 200]] + } + }, ortb2Imp: { ext: { - gpid: '/1111/homepage#300x250', + gpid: '/1111/homepage#350x200', tid: '738d5915-6651-43b9-9b6b-d50517350917', data: { - 'pbadslot': '/1111/homepage#300x250' + 'pbadslot': '/1111/homepage#350x200' } } }, @@ -96,30 +85,9 @@ describe('limelightDigitalAdapter', function () { } ] } - ], - ortb2: { - source: { - ext: { - schain: { - ver: '1.0', - complete: 1, - nodes: [ - { - asi: 'example.com', - sid: '1', - hp: 1 - }, - { - asi: 'example1.com', - sid: '2', - hp: 1 - } - ] - } - } - } - } - } + ] + }; + const bid3 = { bidId: '019645c7d69460', bidder: 'limelightDigital', @@ -137,103 +105,77 @@ describe('limelightDigitalAdapter', function () { }, placementCode: 'placement_2', auctionId: 'e4771143-6aa7-41ec-8824-ced4342c96c8', - sizes: [[800, 600]], - ortb2Imp: { - ext: { - gpid: '/1111/homepage#300x250', - tid: '738d5915-6651-43b9-9b6b-d50517350917', - data: { - 'pbadslot': '/1111/homepage#300x250' - } - } - }, - userIdAsEids: [ - { - source: 'test3.org', - uids: [ - { - id: '345', - }, - { - id: '456', - } - ] - } - ], - ortb2: { - source: { - ext: { - schain: { - ver: '1.0', - complete: 1, - nodes: [ - { - asi: 'example.com', - sid: '1', - hp: 1 - } - ] - } - } + mediaTypes: { + video: { + context: 'instream', + playerSize: [[800, 600]], + mimes: ['video/mp4', 'application/javascript'], + protocols: [2, 3, 5, 6], + maxduration: 60, + minduration: 3, + api: [2], + playbackmethod: [1] } - } - } - const bid4 = { - bidId: '019645c7d69460', - bidder: 'limelightDigital', - bidderRequestId: 'f2b15f89e77ba6', - params: { - host: 'exchange.ortb.net', - adUnitId: 789, - adUnitType: 'video', - custom1: 'custom1', - custom2: 'custom2', - custom3: 'custom3', - custom4: 'custom4', - custom5: 'custom5' - }, - placementCode: 'placement_2', - auctionId: 'e4771143-6aa7-41ec-8824-ced4342c96c8', - video: { - playerSize: [800, 600] }, ortb2Imp: { ext: { - gpid: '/1111/homepage#300x250', + gpid: '/1111/homepage#800x600', tid: '738d5915-6651-43b9-9b6b-d50517350917', data: { - 'pbadslot': '/1111/homepage#300x250' + 'pbadslot': '/1111/homepage#800x600' } } }, userIdAsEids: [ { - source: 'test.org', + source: 'test3.org', uids: [ { - id: '111', + id: '345', } ] } - ], - ortb2: { - source: { - ext: { - schain: { - ver: '1.0', - complete: 1, - nodes: [ - { - asi: 'example.com', - sid: '1', - hp: 1 - } - ] - } - } - } - } - } + ] + }; + + describe('isBidRequestValid', function() { + it('should return true when required params found', function() { + expect(spec.isBidRequestValid(bid1)).to.equal(true); + expect(spec.isBidRequestValid(bid2)).to.equal(true); + expect(spec.isBidRequestValid(bid3)).to.equal(true); + }); + + it('should return true when adUnitId is zero', function() { + const bidWithZeroId = { ...bid1, params: { ...bid1.params, adUnitId: 0 } }; + expect(spec.isBidRequestValid(bidWithZeroId)).to.equal(true); + }); + + it('should return false when required params are not passed', function() { + const bidFailed = { + bidder: 'limelightDigital', + bidderRequestId: '145e1d6a7837c9', + params: { + adUnitId: 123, + adUnitType: 'banner' + }, + placementCode: 'placement_0', + auctionId: '74f78609-a92d-4cf1-869f-1b244bbfb5d2' + }; + expect(spec.isBidRequestValid(bidFailed)).to.equal(false); + }); + + it('should return false when host is missing', function() { + const bidWithoutHost = { ...bid1, params: { ...bid1.params } }; + delete bidWithoutHost.params.host; + expect(spec.isBidRequestValid(bidWithoutHost)).to.equal(false); + }); + + it('should return false when adUnitType is missing', function() { + const bidWithoutType = { ...bid1, params: { ...bid1.params } }; + delete bidWithoutType.params.adUnitType; + expect(spec.isBidRequestValid(bidWithoutType)).to.equal(false); + }); + }); describe('buildRequests', function () { const bidderRequest = { @@ -245,620 +187,636 @@ describe('limelightDigitalAdapter', function () { mobile: 1, architecture: 'arm' } + }, + site: { + page: 'https://example.com/page' } }, refererInfo: { - page: 'testPage' - } - } - const serverRequests = spec.buildRequests([bid1, bid2, bid3, bid4], bidderRequest) - it('Creates two ServerRequests', function() { - expect(serverRequests).to.exist - expect(serverRequests).to.have.lengthOf(2) - }) - serverRequests.forEach(serverRequest => { - 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 data if array of bids is valid', function () { - const data = serverRequest.data; - expect(data).to.be.an('object'); - expect(data).to.have.all.keys( - 'deviceWidth', - 'deviceHeight', - 'secure', - 'adUnits', - 'sua', - 'page', - 'ortb2', - 'refererInfo' - ); - expect(data.deviceWidth).to.be.a('number'); - expect(data.deviceHeight).to.be.a('number'); - expect(data.secure).to.be.a('boolean'); - data.adUnits.forEach(adUnit => { - expect(adUnit).to.have.all.keys( - 'id', - 'bidId', - 'type', - 'sizes', - 'transactionId', - 'publisherId', - 'userIdAsEids', - 'supplyChain', - 'custom1', - 'custom2', - 'custom3', - 'custom4', - 'custom5', - 'ortb2Imp' - ); - 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'); - expect(adUnit.userIdAsEids).to.be.an('array'); - expect(adUnit.supplyChain).to.be.an('object'); - expect(adUnit.custom1).to.be.a('string'); - expect(adUnit.custom2).to.be.a('string'); - expect(adUnit.custom3).to.be.a('string'); - expect(adUnit.custom4).to.be.a('string'); - expect(adUnit.custom5).to.be.a('string'); - expect(adUnit.ortb2Imp).to.be.an('object'); - }) - expect(data.sua.browsers).to.be.a('array'); - expect(data.sua.platform).to.be.a('array'); - expect(data.sua.mobile).to.be.a('number'); - expect(data.sua.architecture).to.be.a('string'); - expect(data.page).to.be.a('string'); - expect(data.page).to.be.equal('testPage'); - expect(data.ortb2).to.be.an('object'); - }) - }) - it('Returns valid URL', function () { - expect(serverRequests[0].url).to.equal('https://exchange.ortb.net/hb') - expect(serverRequests[1].url).to.equal('https://ads.project-limelight.com/hb') - }) - it('Returns valid adUnits', function () { - validateAdUnit(serverRequests[0].data.adUnits[0], bid1) - validateAdUnit(serverRequests[1].data.adUnits[0], bid2) - validateAdUnit(serverRequests[0].data.adUnits[1], bid3) - }) - it('Returns empty data if no valid requests are passed', function () { - const serverRequests = spec.buildRequests([]) - expect(serverRequests).to.be.an('array').that.is.empty - }) - it('Returns request with page field value from ortb2 object if ortb2 has page field', function () { - bidderRequest.ortb2.site = { - page: 'testSitePage' + page: 'https://example.com/page' } - const serverRequests = spec.buildRequests([bid1], bidderRequest) - expect(serverRequests).to.have.lengthOf(1) - serverRequests.forEach(serverRequest => { - expect(serverRequest.data.page).to.be.a('string'); - expect(serverRequest.data.page).to.be.equal('testSitePage'); - }) - }) - }) - describe('interpretBannerResponse', function () { - const resObject = { - body: [ { - requestId: '123', - cpm: 0.3, - width: 320, - height: 50, - ad: '

Hello ad

', - ttl: 1000, - creativeId: '123asd', - netRevenue: true, - currency: 'USD', - meta: { - advertiserDomains: ['example.com'], - mediaType: 'banner' - } - } ] }; - 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++) { - const dataItem = serverResponses[i]; - expect(dataItem).to.have.all.keys('requestId', 'cpm', 'width', 'height', 'ad', 'ttl', 'creativeId', - 'netRevenue', 'currency', 'meta'); - 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.meta.advertiserDomains).to.be.an('array'); - expect(dataItem.meta.mediaType).to.be.a('string'); + + it('should create two server requests for different hosts', function() { + const serverRequests = spec.buildRequests([bid1, bid2, bid3], bidderRequest); + expect(serverRequests).to.exist; + expect(serverRequests).to.have.lengthOf(2); + }); + + it('should create ServerRequest objects with method, URL and data', function () { + const serverRequests = spec.buildRequests([bid1], bidderRequest); + serverRequests.forEach(serverRequest => { + expect(serverRequest).to.exist; + expect(serverRequest.method).to.exist; + expect(serverRequest.url).to.exist; + expect(serverRequest.data).to.exist; + }); + }); + + it('should return POST method', function () { + const serverRequests = spec.buildRequests([bid1], bidderRequest); + serverRequests.forEach(serverRequest => { + expect(serverRequest.method).to.equal('POST'); + }); + }); + + it('should return valid OpenRTB request structure', function () { + const serverRequests = spec.buildRequests([bid1], bidderRequest); + const data = serverRequests[0].data; + + expect(data).to.be.an('object'); + expect(data).to.have.property('imp'); + expect(data).to.have.property('site'); + expect(data).to.have.property('device'); + expect(data).to.have.property('id'); + expect(data.imp).to.be.an('array'); + }); + + it('should include custom fields in imp.ext', function() { + const serverRequests = spec.buildRequests([bid1], bidderRequest); + const imp = serverRequests[0].data.imp[0]; + + expect(deepAccess(imp, 'ext.c1')).to.equal('custom1'); + expect(deepAccess(imp, 'ext.c2')).to.equal('custom2'); + expect(deepAccess(imp, 'ext.c3')).to.equal('custom3'); + expect(deepAccess(imp, 'ext.c4')).to.equal('custom4'); + expect(deepAccess(imp, 'ext.c5')).to.equal('custom5'); + }); + + it('should include adUnitId in imp.ext', function() { + const serverRequests = spec.buildRequests([bid1], bidderRequest); + const imp = serverRequests[0].data.imp[0]; + + expect(deepAccess(imp, 'ext.adUnitId')).to.equal(123); + }); + + it('should return valid URLs for different hosts', function () { + const serverRequests = spec.buildRequests([bid1, bid2, bid3], bidderRequest); + + const exchangeRequest = serverRequests.find(req => req.url.includes('exchange.ortb.net')); + const adsRequest = serverRequests.find(req => req.url.includes('ads.project-limelight.com')); + + expect(exchangeRequest.url).to.equal('https://exchange.ortb.net/ortbhb'); + expect(adsRequest.url).to.equal('https://ads.project-limelight.com/ortbhb'); + }); + + it('should group bids by host correctly', function() { + const serverRequests = spec.buildRequests([bid1, bid2, bid3], bidderRequest); + + const exchangeRequest = serverRequests.find(req => req.url.includes('exchange.ortb.net')); + const adsRequest = serverRequests.find(req => req.url.includes('ads.project-limelight.com')); + + expect(exchangeRequest.data.imp).to.have.lengthOf(2); + expect(adsRequest.data.imp).to.have.lengthOf(1); + }); + + it('should return empty array if no valid requests are passed', function () { + const serverRequests = spec.buildRequests([], bidderRequest); + expect(serverRequests).to.be.an('array').that.is.empty; + }); + + it('should include banner format in OpenRTB request', function() { + const serverRequests = spec.buildRequests([bid1], bidderRequest); + const imp = serverRequests[0].data.imp[0]; + + expect(imp.banner).to.exist; + expect(imp.banner.format).to.be.an('array'); + expect(imp.banner.format[0]).to.have.property('w', 300); + expect(imp.banner.format[0]).to.have.property('h', 250); + }); + + it('should include video object in OpenRTB request for video bid', function() { + const serverRequests = spec.buildRequests([bid3], bidderRequest); + const imp = serverRequests[0].data.imp[0]; + if (FEATURES.VIDEO) { + expect(imp.video).to.exist; + expect(imp.video).to.be.an('object'); + expect(imp.video.w).to.equal(800); + expect(imp.video.h).to.equal(600); } - 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; + expect(deepAccess(imp, 'ext.adUnitId')).to.equal(789); + }); + + it('should skip custom fields if they are undefined', function() { + const bidWithoutCustom = { ...bid1, params: { ...bid1.params } }; + delete bidWithoutCustom.params.custom1; + delete bidWithoutCustom.params.custom2; + + const serverRequests = spec.buildRequests([bidWithoutCustom], bidderRequest); + const imp = serverRequests[0].data.imp[0]; + + expect(deepAccess(imp, 'ext.c1')).to.be.undefined; + expect(deepAccess(imp, 'ext.c2')).to.be.undefined; + expect(deepAccess(imp, 'ext.c3')).to.equal('custom3'); + }); + + it('should handle various refererInfo scenarios', function () { + const baseRequest = [{ + bidder: 'limelightDigital', + params: { host: 'exchange.example.com', adUnitId: 'test' }, + mediaTypes: { banner: { sizes: [[300, 250]] } }, + bidId: 'test-bid-id' + }]; + + let requests = spec.buildRequests(baseRequest, { + refererInfo: { page: 'https://test.com' }, + ortb2: {} }); + expect(requests[0].data.site.page).to.equal('https://test.com'); + + requests = spec.buildRequests(baseRequest, { + refererInfo: { page: 'https://referer.com' }, + ortb2: { site: { page: 'https://ortb2.com' } } + }); + expect(requests[0].data.site.page).to.equal('https://ortb2.com'); + + requests = spec.buildRequests(baseRequest, { ortb2: {} }); + expect(requests[0].data.site.page).to.be.undefined; }); - }); - describe('interpretVideoResponse', function () { - const resObject = { - body: [ { - requestId: '123', - cpm: 0.3, - width: 320, - height: 50, - vastXml: '', - ttl: 1000, - creativeId: '123asd', - netRevenue: true, - currency: 'USD', - meta: { - advertiserDomains: ['example.com'], - mediaType: 'video' + + describe('buildRequests - size handling', function () { + it('should handle mediaTypes.banner.sizes', function () { + const bidRequests = [{ + bidder: 'limelightDigital', + params: { + host: 'exchange.example.com', + adUnitId: 'test', + adUnitType: 'banner' + }, + mediaTypes: { + banner: { + sizes: [[300, 250], [728, 90]] + } + }, + adUnitCode: 'test-ad-unit', + bidId: 'test-bid-id' + }]; + + const bidderRequest = { + refererInfo: { page: 'https://test.com' }, + ortb2: { site: { domain: 'test.com' } } + }; + + const requests = spec.buildRequests(bidRequests, bidderRequest); + + expect(requests[0].data.imp[0].banner.format).to.deep.equal([ + { w: 300, h: 250 }, + { w: 728, h: 90 } + ]); + }); + + it('should handle legacy sizes without mediaTypes', function () { + const bidRequests = [{ + bidder: 'limelightDigital', + params: { + host: 'exchange.example.com', + adUnitId: 'test', + adUnitType: 'banner' + }, + sizes: [[300, 250], [728, 90]], + adUnitCode: 'test-ad-unit', + bidId: 'test-bid-id' + }]; + + const bidderRequest = { + refererInfo: { page: 'https://test.com' }, + ortb2: { site: { domain: 'test.com' } } + }; + + const requests = spec.buildRequests(bidRequests, bidderRequest); + + expect(requests[0].data.imp[0].banner.format).to.deep.equal([ + { w: 300, h: 250 }, + { w: 728, h: 90 } + ]); + }); + + it('should merge mediaTypes sizes with bidRequest.sizes', function () { + const bidRequests = [{ + bidder: 'limelightDigital', + params: { + host: 'exchange.example.com', + adUnitId: 'test', + adUnitType: 'banner' + }, + mediaTypes: { + banner: { + sizes: [[300, 250]] + } + }, + sizes: [[728, 90]], + adUnitCode: 'test-ad-unit', + bidId: 'test-bid-id' + }]; + + const bidderRequest = { + refererInfo: { page: 'https://test.com' }, + ortb2: { site: { domain: 'test.com' } } + }; + + const requests = spec.buildRequests(bidRequests, bidderRequest); + + const formats = requests[0].data.imp[0].banner.format; + expect(formats).to.have.lengthOf(2); + expect(formats).to.deep.include({ w: 300, h: 250 }); + expect(formats).to.deep.include({ w: 728, h: 90 }); + }); + + it('should handle video with playerSize', function () { + const bidRequests = [{ + bidder: 'limelightDigital', + params: { + host: 'exchange.example.com', + adUnitId: 'test', + adUnitType: 'video' + }, + mediaTypes: { + video: { + playerSize: [640, 480] + } + }, + adUnitCode: 'test-ad-unit', + bidId: 'test-bid-id' + }]; + + const bidderRequest = { + refererInfo: { page: 'https://test.com' }, + ortb2: { site: { domain: 'test.com' } } + }; + + const requests = spec.buildRequests(bidRequests, bidderRequest); + if (FEATURES.VIDEO) { + expect(requests[0].data.imp[0].video).to.exist; + expect(requests[0].data.imp[0].video.w).to.equal(640); + expect(requests[0].data.imp[0].video.h).to.equal(480); } - } ] - }; - 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++) { - const dataItem = serverResponses[i]; - expect(dataItem).to.have.all.keys('requestId', 'cpm', 'width', 'height', 'vastXml', 'ttl', 'creativeId', - 'netRevenue', 'currency', 'meta'); - 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.meta.advertiserDomains).to.be.an('array'); - expect(dataItem.meta.mediaType).to.be.a('string'); - } - it('should return 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() { - const bid = { - bidId: '2dd581a2b6281d', - bidder: 'limelightDigital', - bidderRequestId: '145e1d6a7837c9', - params: { - host: 'exchange.ortb.net', - adUnitId: 123, - adUnitType: 'banner' - }, - placementCode: 'placement_0', - auctionId: '74f78609-a92d-4cf1-869f-1b244bbfb5d2', - sizes: [[300, 250]], - transactionId: '3bb2f6da-87a6-4029-aeb0-bfe951372e62' + + describe('interpretResponse - Banner', function () { + const bidderRequest = { + ortb2: { + site: { + page: 'https://example.com/page' + } + } }; - it('should return true when required params found', function() { - [bid, bid1, bid2, bid3].forEach(bid => { - expect(spec.isBidRequestValid(bid)).to.equal(true); - }); + it('should return array of valid bid responses', function () { + const serverRequests = spec.buildRequests([bid1], bidderRequest); + const request = serverRequests[0]; + + const ortbResponse = { + body: { + seatbid: [{ + bid: [{ + id: 'bid123', + impid: request.data.imp[0].id, + price: 0.3, + w: 300, + h: 250, + adm: '

Hello ad

', + crid: '123asd', + mtype: 1, + adomain: ['example.com'], + exp: 1000 + }] + }], + cur: 'USD' + } + }; + + const serverResponses = spec.interpretResponse(ortbResponse, request); + + expect(serverResponses).to.be.an('array').that.is.not.empty; + expect(serverResponses).to.have.lengthOf(1); + + const bidResponse = serverResponses[0]; + expect(bidResponse.requestId).to.be.a('string'); + expect(bidResponse.cpm).to.equal(0.3); + expect(bidResponse.width).to.equal(300); + expect(bidResponse.height).to.equal(250); + expect(bidResponse.ad).to.be.a('string'); + expect(bidResponse.ttl).to.be.a('number'); + expect(bidResponse.creativeId).to.be.a('string'); + expect(bidResponse.netRevenue).to.be.a('boolean'); + expect(bidResponse.currency).to.equal('USD'); + expect(bidResponse.mediaType).to.equal('banner'); }); - it('should return true when adUnitId is zero', function() { - bid.params.adUnitId = 0; - expect(spec.isBidRequestValid(bid)).to.equal(true); + it('should return empty array for invalid response', function () { + const serverRequests = spec.buildRequests([bid1], bidderRequest); + const request = serverRequests[0]; + + const serverResponses = spec.interpretResponse({ body: null }, request); + expect(serverResponses).to.be.an('array').that.is.empty; }); - it('should return false when required params are not passed', function() { - const bidFailed = { - bidder: 'limelightDigital', - 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 empty array when response body is missing', function() { + const serverRequests = spec.buildRequests([bid1], bidderRequest); + const request = serverRequests[0]; + + const serverResponses = spec.interpretResponse({}, request); + expect(serverResponses).to.be.an('array').that.is.empty; + }); + + it('should filter out invalid bids', function() { + const serverRequests = spec.buildRequests([bid1], bidderRequest); + const request = serverRequests[0]; + + const invalidResponse = { + body: { + seatbid: [{ + bid: [{ + id: 'bid123', + impid: request.data.imp[0].id, + price: 0.3, + crid: '123asd', + mtype: 1, + adomain: ['example.com'] + }] + }], + cur: 'USD' + } }; - expect(spec.isBidRequestValid(bidFailed)).to.equal(false); + + const serverResponses = spec.interpretResponse(invalidResponse, request); + expect(serverResponses).to.be.an('array').that.is.empty; }); }); - describe('interpretResponse', function() { - const resObject = { - requestId: '123', - cpm: 0.3, - width: 320, - height: 50, - ad: '

Hello ad

', - ttl: 1000, - creativeId: '123asd', - netRevenue: true, - currency: 'USD', - meta: { - advertiserDomains: ['example.com'], - mediaType: 'banner' + + describe('interpretResponse - Video', function () { + const bidderRequest = { + ortb2: { + site: { + page: 'https://example.com/page' + } } }; - it('should skip responses which do not contain required params', function() { - const bidResponses = { - body: [ { - cpm: 0.3, - ttl: 1000, - currency: 'USD', - meta: { - advertiserDomains: ['example.com'], - mediaType: 'banner' - } - }, resObject ] - } - expect(spec.interpretResponse(bidResponses)).to.deep.equal([ resObject ]); - }); - it('should skip responses which do not contain advertiser domains', function() { - const resObjectWithoutAdvertiserDomains = Object.assign({}, resObject); - resObjectWithoutAdvertiserDomains.meta = Object.assign({}, resObject.meta); - delete resObjectWithoutAdvertiserDomains.meta.advertiserDomains; - const bidResponses = { - body: [ resObjectWithoutAdvertiserDomains, resObject ] - } - expect(spec.interpretResponse(bidResponses)).to.deep.equal([ resObject ]); - }); - it('should return responses which contain empty advertiser domains', function() { - const resObjectWithEmptyAdvertiserDomains = Object.assign({}, resObject); - resObjectWithEmptyAdvertiserDomains.meta = Object.assign({}, resObject.meta); - resObjectWithEmptyAdvertiserDomains.meta.advertiserDomains = []; - const bidResponses = { - body: [ resObjectWithEmptyAdvertiserDomains, resObject ] + + it('should return array of valid video bid responses with mtype', function () { + const serverRequests = spec.buildRequests([bid3], bidderRequest); + const request = serverRequests[0]; + + const ortbResponse = { + body: { + seatbid: [{ + bid: [{ + id: 'bid456', + impid: request.data.imp[0].id, + price: 0.5, + w: 800, + h: 600, + adm: '', + crid: '456def', + mtype: 2, + adomain: ['example.com'] + }] + }], + cur: 'USD' + } + }; + + const serverResponses = spec.interpretResponse(ortbResponse, request); + + expect(serverResponses).to.be.an('array'); + if (serverResponses.length > 0) { + const bidResponse = serverResponses[0]; + expect(bidResponse.mediaType).to.equal('video'); + expect(bidResponse.vastXml).to.be.a('string'); } - expect(spec.interpretResponse(bidResponses)).to.deep.equal([resObjectWithEmptyAdvertiserDomains, resObject]); - }); - it('should skip responses which do not contain meta media type', function() { - const resObjectWithoutMetaMediaType = Object.assign({}, resObject); - resObjectWithoutMetaMediaType.meta = Object.assign({}, resObject.meta); - delete resObjectWithoutMetaMediaType.meta.mediaType; - const bidResponses = { - body: [ resObjectWithoutMetaMediaType, resObject ] + }); + + it('should return array of valid video bid responses with ext.mediaType fallback', function () { + const serverRequests = spec.buildRequests([bid3], bidderRequest); + const request = serverRequests[0]; + + const ortbResponse = { + body: { + seatbid: [{ + bid: [{ + id: 'bid456', + impid: request.data.imp[0].id, + price: 0.5, + w: 800, + h: 600, + adm: '', + crid: '456def', + ext: { + mediaType: 'video' + }, + adomain: ['example.com'] + }] + }], + cur: 'USD' + } + }; + + const serverResponses = spec.interpretResponse(ortbResponse, request); + + expect(serverResponses).to.be.an('array'); + if (serverResponses.length > 0) { + const bidResponse = serverResponses[0]; + expect(bidResponse.mediaType).to.equal('video'); + expect(bidResponse.vastXml).to.be.a('string'); } - expect(spec.interpretResponse(bidResponses)).to.deep.equal([ resObject ]); }); }); - describe('getUserSyncs', function () { - it('should return trackers for lm(only iframe) if server responses contain lm user sync header and iframe and image enabled', function () { - const serverResponses = [ - { - headers: { - get: function (header) { - if (header === 'x-pll-usersync-image') { - return 'https://tracker-lm.ortb.net/sync'; - } - if (header === 'x-pll-usersync-iframe') { - return 'https://tracker-lm.ortb.net/sync.html'; - } - } - }, - body: [] + + describe('interpretResponse - mediaType fallback', function() { + const bidderRequest = { + ortb2: { + site: { + page: 'https://example.com/page' } - ]; - const syncOptions = { - iframeEnabled: true, - pixelEnabled: true - }; - expect(spec.getUserSyncs(syncOptions, serverResponses)).to.deep.equal([ - { - type: 'iframe', - url: 'https://tracker-lm.ortb.net/sync.html' + } + }; + + it('should infer mediaType from imp.banner when mtype is missing', function() { + const serverRequests = spec.buildRequests([bid1], bidderRequest); + const request = serverRequests[0]; + + const responseWithoutMtype = { + body: { + seatbid: [{ + bid: [{ + id: 'bid123', + impid: request.data.imp[0].id, + price: 0.3, + w: 300, + h: 250, + adm: '

Hello ad

', + crid: '123asd', + adomain: ['example.com'], + exp: 1000 + }] + }], + cur: 'USD' } - ]); + }; + + const serverResponses = spec.interpretResponse(responseWithoutMtype, request); + + expect(serverResponses).to.have.lengthOf(1); + expect(serverResponses[0].mediaType).to.equal('banner'); }); - it('should return empty array if all sync types are disabled', function () { - const serverResponses = [ - { - headers: { - get: function (header) { - if (header === 'x-pll-usersync-image') { - return 'https://tracker-1.ortb.net/sync'; - } - if (header === 'x-pll-usersync-iframe') { - return 'https://tracker-1.ortb.net/sync.html'; - } - } - }, - body: [] + + it('should use ext.mediaType when available', function() { + const serverRequests = spec.buildRequests([bid1], bidderRequest); + const request = serverRequests[0]; + + const responseWithExtMediaType = { + body: { + seatbid: [{ + bid: [{ + id: 'bid123', + impid: request.data.imp[0].id, + price: 0.3, + w: 300, + h: 250, + adm: '

Hello ad

', + crid: '123asd', + ext: { + mediaType: 'banner' + }, + adomain: ['example.com'], + exp: 1000 + }] + }], + cur: 'USD' } - ]; - const syncOptions = { - iframeEnabled: false, - pixelEnabled: false }; - expect(spec.getUserSyncs(syncOptions, serverResponses)).to.be.an('array').that.is.empty; + + const serverResponses = spec.interpretResponse(responseWithExtMediaType, request); + + expect(serverResponses).to.have.lengthOf(1); + expect(serverResponses[0].mediaType).to.equal('banner'); }); - it('should return no pixels if iframe sync is enabled and headers are blank', function () { - const serverResponses = [ - { - headers: null, - body: [] - } - ]; - const syncOptions = { - iframeEnabled: true, - pixelEnabled: false + }); + + describe('onBidWon', function() { + it('should replace auction price macro in nurl', function() { + const bid = { + pbMg: 1.23, + nurl: 'https://example.com/win?price=${AUCTION_PRICE}' }; - expect(spec.getUserSyncs(syncOptions, serverResponses)).to.be.an('array').that.is.empty; + + expect(() => spec.onBidWon(bid)).to.not.throw(); }); - it('should return image sync urls for lm if pixel sync is enabled and headers have lm pixel', function () { - const serverResponses = [ - { - headers: { - get: function (header) { - if (header === 'x-pll-usersync-image') { - return 'https://tracker-lm.ortb.net/sync'; - } - if (header === 'x-pll-usersync-iframe') { - return 'https://tracker-lm.ortb.net/sync.html'; - } + + it('should handle empty nurl', function() { + const bid = { + pbMg: 1.23, + nurl: '' + }; + + expect(() => spec.onBidWon(bid)).to.not.throw(); + }); + }); + + describe('getUserSyncs', function () { + it('should return iframe sync when available and enabled', function () { + const serverResponses = [{ + headers: { + get: function (header) { + if (header === 'x-pll-usersync-iframe') { + return 'https://tracker-lm.ortb.net/sync.html'; } - }, - body: [] - } - ]; + if (header === 'x-pll-usersync-image') { + return 'https://tracker-lm.ortb.net/sync'; + } + } + }, + body: {} + }]; + const syncOptions = { - iframeEnabled: false, + iframeEnabled: true, pixelEnabled: true }; - expect(spec.getUserSyncs(syncOptions, serverResponses)).to.deep.equal([ - { - type: 'image', - url: 'https://tracker-lm.ortb.net/sync' - } - ]); + + const syncs = spec.getUserSyncs(syncOptions, serverResponses); + expect(syncs).to.deep.equal([{ + type: 'iframe', + url: 'https://tracker-lm.ortb.net/sync.html' + }]); }); - it('should return image sync urls for client1 and clien2 if pixel sync is enabled and two responses and headers have two pixels', function () { - const serverResponses = [ - { - headers: { - get: function (header) { - if (header === 'x-pll-usersync-image') { - return 'https://tracker-1.ortb.net/sync'; - } - if (header === 'x-pll-usersync-iframe') { - return 'https://tracker-1.ortb.net/sync.html'; - } + + it('should return image sync when iframe not available', function () { + const serverResponses = [{ + headers: { + get: function (header) { + if (header === 'x-pll-usersync-image') { + return 'https://tracker-lm.ortb.net/sync'; } - }, - body: [] + } }, - { - headers: { - get: function (header) { - if (header === 'x-pll-usersync-image') { - return 'https://tracker-2.ortb.net/sync'; - } - if (header === 'x-pll-usersync-iframe') { - return 'https://tracker-2.ortb.net/sync.html'; - } - } - }, - body: [] - } - ]; + body: {} + }]; + const syncOptions = { iframeEnabled: false, pixelEnabled: true }; - expect(spec.getUserSyncs(syncOptions, serverResponses)).to.deep.equal([ - { - type: 'image', - url: 'https://tracker-1.ortb.net/sync' + + const syncs = spec.getUserSyncs(syncOptions, serverResponses); + expect(syncs).to.deep.equal([{ + type: 'image', + url: 'https://tracker-lm.ortb.net/sync' + }]); + }); + + it('should return empty array when all sync types disabled', function () { + const serverResponses = [{ + headers: { + get: function (header) { + return 'https://tracker.ortb.net/sync'; + } }, - { - type: 'image', - url: 'https://tracker-2.ortb.net/sync' - } - ]); + body: {} + }]; + + const syncOptions = { + iframeEnabled: false, + pixelEnabled: false + }; + + const syncs = spec.getUserSyncs(syncOptions, serverResponses); + expect(syncs).to.be.an('array').that.is.empty; }); - it('should return image sync url for pll if pixel sync is enabled and two responses and headers have two same pixels', function () { + + it('should deduplicate sync URLs', function() { const serverResponses = [ { headers: { get: function (header) { if (header === 'x-pll-usersync-image') { - return 'https://tracker-lm.ortb.net/sync'; - } - if (header === 'x-pll-usersync-iframe') { - return 'https://tracker-lm.ortb.net/sync.html'; + return 'https://tracker.ortb.net/sync'; } } }, - body: [] + body: {} }, { headers: { get: function (header) { if (header === 'x-pll-usersync-image') { - return 'https://tracker-lm.ortb.net/sync'; - } - if (header === 'x-pll-usersync-iframe') { - return 'https://tracker-lm.ortb.net/sync.html'; + return 'https://tracker.ortb.net/sync'; } } }, - body: [] + body: {} } ]; + const syncOptions = { iframeEnabled: false, pixelEnabled: true }; - expect(spec.getUserSyncs(syncOptions, serverResponses)).to.deep.equal([ - { - type: 'image', - url: 'https://tracker-lm.ortb.net/sync' - } - ]); - }); - it('should return iframe sync url for pll if pixel sync is enabled and iframe is enables and headers have both iframe and img pixels', function () { - const serverResponses = [ - { - headers: { - get: function (header) { - if (header === 'x-pll-usersync-image') { - return 'https://tracker-lm.ortb.net/sync'; - } - if (header === 'x-pll-usersync-iframe') { - return 'https://tracker-lm.ortb.net/sync.html'; - } - } - }, - body: [] - } - ]; - const syncOptions = { - iframeEnabled: true, - pixelEnabled: true - }; - expect(spec.getUserSyncs(syncOptions, serverResponses)).to.deep.equal([ - { - type: 'iframe', - url: 'https://tracker-lm.ortb.net/sync.html' - } - ]); - }); - }); - describe('getFloor support', function() { - const bidderRequest = { - ortb2: { - device: { - sua: { - browsers: [], - platform: [], - mobile: 1, - architecture: 'arm' - } - } - }, - refererInfo: { - page: 'testPage' - } - }; - it('should include floorInfo when getFloor is available', function() { - const bidWithFloor = { - ...bid1, - getFloor: function(params) { - if (params.size[0] === 300 && params.size[1] === 250) { - return { currency: 'USD', floor: 2.0 }; - } - return { currency: 'USD', floor: 0 }; - } - }; - - const serverRequests = spec.buildRequests([bidWithFloor], bidderRequest); - expect(serverRequests).to.have.lengthOf(1); - const adUnit = serverRequests[0].data.adUnits[0]; - expect(adUnit.sizes).to.have.lengthOf(1); - expect(adUnit.sizes[0].floorInfo).to.exist; - expect(adUnit.sizes[0].floorInfo.currency).to.equal('USD'); - expect(adUnit.sizes[0].floorInfo.floor).to.equal(2.0); - }); - it('should set floorInfo to null when getFloor is not available', function() { - const bidWithoutFloor = { ...bid1 }; - delete bidWithoutFloor.getFloor; - - const serverRequests = spec.buildRequests([bidWithoutFloor], bidderRequest); - expect(serverRequests).to.have.lengthOf(1); - expect(serverRequests[0].data.adUnits[0].sizes[0].floorInfo).to.be.null; - }); - it('should handle multiple sizes with different floors', function() { - const bidWithMultipleSizes = { - ...bid1, - mediaTypes: { - banner: { - sizes: [[300, 250], [728, 90]] - } - }, - getFloor: function(params) { - if (params.size[0] === 300 && params.size[1] === 250) { - return { currency: 'USD', floor: 1.5 }; - } - if (params.size[0] === 728 && params.size[1] === 90) { - return { currency: 'USD', floor: 2.0 }; - } - return { currency: 'USD', floor: 0 }; - } - }; - - const serverRequests = spec.buildRequests([bidWithMultipleSizes], bidderRequest); - expect(serverRequests).to.have.lengthOf(1); - const adUnit = serverRequests[0].data.adUnits[0]; - expect(adUnit.sizes).to.have.lengthOf(2); - expect(adUnit.sizes[0].floorInfo.floor).to.equal(1.5); - expect(adUnit.sizes[1].floorInfo.floor).to.equal(2.0); - }); - it('should set floorInfo to null when getFloor returns empty object', function() { - const bidWithEmptyFloor = { - ...bid1, - getFloor: function() { - return {}; - } - }; - - const serverRequests = spec.buildRequests([bidWithEmptyFloor], bidderRequest); - expect(serverRequests).to.have.lengthOf(1); - expect(serverRequests[0].data.adUnits[0].sizes[0].floorInfo).to.deep.equal({}); - }); - it('should handle getFloor errors and set floorInfo to null', function() { - const bidWithErrorFloor = { - ...bid1, - getFloor: function() { - throw new Error('Floor module error'); - } - }; - const serverRequests = spec.buildRequests([bidWithErrorFloor], bidderRequest); - expect(serverRequests).to.have.lengthOf(1); - const adUnit = serverRequests[0].data.adUnits[0]; - expect(adUnit.sizes[0].floorInfo).to.be.null; + const syncs = spec.getUserSyncs(syncOptions, serverResponses); + expect(syncs).to.have.lengthOf(1); }); }); }); - -function validateAdUnit(adUnit, bid) { - expect(adUnit.id).to.equal(bid.params.adUnitId); - expect(adUnit.bidId).to.equal(bid.bidId); - expect(adUnit.type).to.equal(bid.params.adUnitType.toUpperCase()); - expect(adUnit.transactionId).to.equal(bid.ortb2Imp.ext.tid); - let bidSizes = []; - if (bid.mediaTypes) { - if (bid.mediaTypes.video && bid.mediaTypes.video.playerSize) { - bidSizes = bidSizes.concat([bid.mediaTypes.video.playerSize]); - } - if (bid.mediaTypes.banner && bid.mediaTypes.banner.sizes) { - bidSizes = bidSizes.concat(bid.mediaTypes.banner.sizes); - } - } - if (bid.sizes) { - bidSizes = bidSizes.concat(bid.sizes || []); - } - expect(adUnit.sizes).to.deep.equal(bidSizes.map(size => { - return { - width: size[0], - height: size[1], - floorInfo: null - } - })); - expect(adUnit.publisherId).to.equal(bid.params.publisherId); - expect(adUnit.userIdAsEids).to.deep.equal(bid.userIdAsEids); - expect(adUnit.supplyChain).to.deep.equal(bid.ortb2.source.ext.schain); - expect(adUnit.ortb2Imp).to.deep.equal(bid.ortb2Imp); -} diff --git a/test/spec/modules/liveIntentAnalyticsAdapter_spec.js b/test/spec/modules/liveIntentAnalyticsAdapter_spec.js index 869e9eb789c..65a363432a3 100644 --- a/test/spec/modules/liveIntentAnalyticsAdapter_spec.js +++ b/test/spec/modules/liveIntentAnalyticsAdapter_spec.js @@ -57,8 +57,8 @@ describe('LiveIntent Analytics Adapter ', () => { sandbox.stub(events, 'getEvents').returns([]); sandbox.stub(config, 'getConfig').withArgs('userSync.userIds').returns(USERID_CONFIG); sandbox.stub(utils, 'generateUUID').returns(instanceId); - sandbox.stub(refererDetection, 'getRefererInfo').returns({page: url}); - sandbox.stub(auctionManager.index, 'getAuction').withArgs({auctionId: AUCTION_INIT_EVENT.auctionId}).returns({ + sandbox.stub(refererDetection, 'getRefererInfo').returns({ page: url }); + sandbox.stub(auctionManager.index, 'getAuction').withArgs({ auctionId: AUCTION_INIT_EVENT.auctionId }).returns({ getBidRequests: () => AUCTION_INIT_EVENT.bidderRequests, getAuctionStart: () => AUCTION_INIT_EVENT.timestamp }); diff --git a/test/spec/modules/liveIntentExternalIdSystem_spec.js b/test/spec/modules/liveIntentExternalIdSystem_spec.js index c4bd7eec960..c9569c44dbb 100644 --- a/test/spec/modules/liveIntentExternalIdSystem_spec.js +++ b/test/spec/modules/liveIntentExternalIdSystem_spec.js @@ -4,7 +4,7 @@ import { gdprDataHandler, uspDataHandler, gppDataHandler, coppaDataHandler } fro import * as refererDetection from '../../../src/refererDetection.js'; const DEFAULT_AJAX_TIMEOUT = 5000 const PUBLISHER_ID = '89899'; -const defaultConfigParams = { params: {publisherId: PUBLISHER_ID, fireEventDelay: 1} }; +const defaultConfigParams = { params: { publisherId: PUBLISHER_ID, fireEventDelay: 1 } }; describe('LiveIntentExternalId', function() { let uspConsentDataStub; @@ -100,16 +100,18 @@ describe('LiveIntentExternalId', function() { expect(resolveCommand).to.eql({ clientRef: {}, onSuccess: [{ type: 'callback' }], - requestedAttributes: [ 'nonId' ], + requestedAttributes: ['nonId'], type: 'resolve' }) }); it('should fire an event when getId and a hash is provided', function() { - liveIntentExternalIdSubmodule.getId({ params: { - ...defaultConfigParams.params, - emailHash: '58131bc547fb87af94cebdaf3102321f' - }}).callback(() => {}); + liveIntentExternalIdSubmodule.getId({ + params: { + ...defaultConfigParams.params, + emailHash: '58131bc547fb87af94cebdaf3102321f' + } + }).callback(() => {}); expect(window.liQHub).to.have.length(3) @@ -138,7 +140,7 @@ describe('LiveIntentExternalId', function() { expect(resolveCommand).to.eql({ clientRef: {}, onSuccess: [{ type: 'callback' }], - requestedAttributes: [ 'nonId' ], + requestedAttributes: ['nonId'], type: 'resolve' }) }); @@ -277,19 +279,21 @@ describe('LiveIntentExternalId', function() { it('should decode a unifiedId to lipbId and remove it', function() { const result = liveIntentExternalIdSubmodule.decode({ unifiedId: 'data' }, defaultConfigParams); - expect(result).to.eql({'lipb': {'lipbid': 'data'}}); + expect(result).to.eql({ 'lipb': { 'lipbid': 'data' } }); }); it('should decode a nonId to lipbId', function() { const result = liveIntentExternalIdSubmodule.decode({ nonId: 'data' }, defaultConfigParams); - expect(result).to.eql({'lipb': {'lipbid': 'data', 'nonId': 'data'}}); + expect(result).to.eql({ 'lipb': { 'lipbid': 'data', 'nonId': 'data' } }); }); it('should resolve extra attributes', function() { - liveIntentExternalIdSubmodule.getId({ params: { - ...defaultConfigParams.params, - ...{ requestedAttributesOverrides: { 'foo': true, 'bar': false } } - } }).callback(() => {}); + liveIntentExternalIdSubmodule.getId({ + params: { + ...defaultConfigParams.params, + ...{ requestedAttributesOverrides: { 'foo': true, 'bar': false } } + } + }).callback(() => {}); expect(window.liQHub).to.have.length(2) expect(window.liQHub[0]).to.eql({ @@ -312,73 +316,75 @@ describe('LiveIntentExternalId', function() { expect(resolveCommand).to.eql({ clientRef: {}, onSuccess: [{ type: 'callback' }], - requestedAttributes: [ 'nonId', 'foo' ], + requestedAttributes: ['nonId', 'foo'], type: 'resolve' }) }); it('should decode values with the segments but no nonId', function() { - const result = liveIntentExternalIdSubmodule.decode({segments: ['tak']}, defaultConfigParams); - expect(result).to.eql({'lipb': {'segments': ['tak']}}); + const result = liveIntentExternalIdSubmodule.decode({ segments: ['tak'] }, defaultConfigParams); + expect(result).to.eql({ 'lipb': { 'segments': ['tak'] } }); }); it('should decode a uid2 to a separate object when present', function() { const result = liveIntentExternalIdSubmodule.decode({ nonId: 'foo', uid2: 'bar' }, defaultConfigParams); - expect(result).to.eql({'lipb': {'lipbid': 'foo', 'nonId': 'foo', 'uid2': 'bar'}, 'uid2': {'id': 'bar', 'ext': {'provider': 'liveintent.com'}}}); + expect(result).to.eql({ 'lipb': { 'lipbid': 'foo', 'nonId': 'foo', 'uid2': 'bar' }, 'uid2': { 'id': 'bar', 'ext': { 'provider': 'liveintent.com' } } }); }); it('should decode values with uid2 but no nonId', function() { const result = liveIntentExternalIdSubmodule.decode({ uid2: 'bar' }, defaultConfigParams); - expect(result).to.eql({'lipb': {'uid2': 'bar'}, 'uid2': {'id': 'bar', 'ext': {'provider': 'liveintent.com'}}}); + expect(result).to.eql({ 'lipb': { 'uid2': 'bar' }, 'uid2': { 'id': 'bar', 'ext': { 'provider': 'liveintent.com' } } }); }); it('should decode a bidswitch id to a separate object when present', function() { const result = liveIntentExternalIdSubmodule.decode({ nonId: 'foo', bidswitch: 'bar' }, defaultConfigParams); - expect(result).to.eql({'lipb': {'lipbid': 'foo', 'nonId': 'foo', 'bidswitch': 'bar'}, 'bidswitch': {'id': 'bar', 'ext': {'provider': 'liveintent.com'}}}); + expect(result).to.eql({ 'lipb': { 'lipbid': 'foo', 'nonId': 'foo', 'bidswitch': 'bar' }, 'bidswitch': { 'id': 'bar', 'ext': { 'provider': 'liveintent.com' } } }); }); it('should decode a medianet id to a separate object when present', function() { const result = liveIntentExternalIdSubmodule.decode({ nonId: 'foo', medianet: 'bar' }, defaultConfigParams); - expect(result).to.eql({'lipb': {'lipbid': 'foo', 'nonId': 'foo', 'medianet': 'bar'}, 'medianet': {'id': 'bar', 'ext': {'provider': 'liveintent.com'}}}); + expect(result).to.eql({ 'lipb': { 'lipbid': 'foo', 'nonId': 'foo', 'medianet': 'bar' }, 'medianet': { 'id': 'bar', 'ext': { 'provider': 'liveintent.com' } } }); }); it('should decode a sovrn id to a separate object when present', function() { const result = liveIntentExternalIdSubmodule.decode({ nonId: 'foo', sovrn: 'bar' }, defaultConfigParams); - expect(result).to.eql({'lipb': {'lipbid': 'foo', 'nonId': 'foo', 'sovrn': 'bar'}, 'sovrn': {'id': 'bar', 'ext': {'provider': 'liveintent.com'}}}); + expect(result).to.eql({ 'lipb': { 'lipbid': 'foo', 'nonId': 'foo', 'sovrn': 'bar' }, 'sovrn': { 'id': 'bar', 'ext': { 'provider': 'liveintent.com' } } }); }); it('should decode a magnite id to a separate object when present', function() { const result = liveIntentExternalIdSubmodule.decode({ nonId: 'foo', magnite: 'bar' }, defaultConfigParams); - expect(result).to.eql({'lipb': {'lipbid': 'foo', 'nonId': 'foo', 'magnite': 'bar'}, 'magnite': {'id': 'bar', 'ext': {'provider': 'liveintent.com'}}}); + expect(result).to.eql({ 'lipb': { 'lipbid': 'foo', 'nonId': 'foo', 'magnite': 'bar' }, 'magnite': { 'id': 'bar', 'ext': { 'provider': 'liveintent.com' } } }); }); it('should decode an index id to a separate object when present', function() { const result = liveIntentExternalIdSubmodule.decode({ nonId: 'foo', index: 'bar' }, defaultConfigParams); - expect(result).to.eql({'lipb': {'lipbid': 'foo', 'nonId': 'foo', 'index': 'bar'}, 'index': {'id': 'bar', 'ext': {'provider': 'liveintent.com'}}}); + expect(result).to.eql({ 'lipb': { 'lipbid': 'foo', 'nonId': 'foo', 'index': 'bar' }, 'index': { 'id': 'bar', 'ext': { 'provider': 'liveintent.com' } } }); }); it('should decode an openx id to a separate object when present', function () { const result = liveIntentExternalIdSubmodule.decode({ nonId: 'foo', openx: 'bar' }, defaultConfigParams); - expect(result).to.eql({'lipb': {'lipbid': 'foo', 'nonId': 'foo', 'openx': 'bar'}, 'openx': {'id': 'bar', 'ext': {'provider': 'liveintent.com'}}}); + expect(result).to.eql({ 'lipb': { 'lipbid': 'foo', 'nonId': 'foo', 'openx': 'bar' }, 'openx': { 'id': 'bar', 'ext': { 'provider': 'liveintent.com' } } }); }); it('should decode an pubmatic id to a separate object when present', function() { const result = liveIntentExternalIdSubmodule.decode({ nonId: 'foo', pubmatic: 'bar' }, defaultConfigParams); - expect(result).to.eql({'lipb': {'lipbid': 'foo', 'nonId': 'foo', 'pubmatic': 'bar'}, 'pubmatic': {'id': 'bar', 'ext': {'provider': 'liveintent.com'}}}); + expect(result).to.eql({ 'lipb': { 'lipbid': 'foo', 'nonId': 'foo', 'pubmatic': 'bar' }, 'pubmatic': { 'id': 'bar', 'ext': { 'provider': 'liveintent.com' } } }); }); it('should decode a thetradedesk id to a separate object when present', function() { const provider = 'liveintent.com' - refererInfoStub.returns({domain: provider}) + refererInfoStub.returns({ domain: provider }) const result = liveIntentExternalIdSubmodule.decode({ nonId: 'foo', thetradedesk: 'bar' }, defaultConfigParams); - expect(result).to.eql({'lipb': {'lipbid': 'foo', 'nonId': 'foo', 'tdid': 'bar'}, 'tdid': {'id': 'bar', 'ext': {'rtiPartner': 'TDID', 'provider': provider}}}); + expect(result).to.eql({ 'lipb': { 'lipbid': 'foo', 'nonId': 'foo', 'tdid': 'bar' }, 'tdid': { 'id': 'bar', 'ext': { 'rtiPartner': 'TDID', 'provider': provider } } }); }); it('should allow disabling nonId resolution', function() { - liveIntentExternalIdSubmodule.getId({ params: { - ...defaultConfigParams.params, - ...{ requestedAttributesOverrides: { 'nonId': false, 'uid2': true } } - } }).callback(() => {}); + liveIntentExternalIdSubmodule.getId({ + params: { + ...defaultConfigParams.params, + ...{ requestedAttributesOverrides: { 'nonId': false, 'uid2': true } } + } + }).callback(() => {}); expect(window.liQHub).to.have.length(2) expect(window.liQHub[0]).to.eql({ @@ -400,44 +406,44 @@ describe('LiveIntentExternalId', function() { expect(resolveCommand).to.eql({ clientRef: {}, onSuccess: [{ type: 'callback' }], - requestedAttributes: [ 'uid2' ], + requestedAttributes: ['uid2'], type: 'resolve' }) }); it('should decode a sharethrough id to a separate object when present', function() { const result = liveIntentExternalIdSubmodule.decode({ nonId: 'foo', sharethrough: 'bar' }, defaultConfigParams); - expect(result).to.eql({'lipb': {'lipbid': 'foo', 'nonId': 'foo', 'sharethrough': 'bar'}, 'sharethrough': {'id': 'bar', 'ext': {'provider': 'liveintent.com'}}}); + expect(result).to.eql({ 'lipb': { 'lipbid': 'foo', 'nonId': 'foo', 'sharethrough': 'bar' }, 'sharethrough': { 'id': 'bar', 'ext': { 'provider': 'liveintent.com' } } }); }); it('should decode a sonobi id to a separate object when present', function() { const result = liveIntentExternalIdSubmodule.decode({ nonId: 'foo', sonobi: 'bar' }, defaultConfigParams); - expect(result).to.eql({'lipb': {'lipbid': 'foo', 'nonId': 'foo', 'sonobi': 'bar'}, 'sonobi': {'id': 'bar', 'ext': {'provider': 'liveintent.com'}}}); + expect(result).to.eql({ 'lipb': { 'lipbid': 'foo', 'nonId': 'foo', 'sonobi': 'bar' }, 'sonobi': { 'id': 'bar', 'ext': { 'provider': 'liveintent.com' } } }); }); it('should decode a triplelift id to a separate object when present', function() { const result = liveIntentExternalIdSubmodule.decode({ nonId: 'foo', triplelift: 'bar' }, defaultConfigParams); - expect(result).to.eql({'lipb': {'lipbid': 'foo', 'nonId': 'foo', 'triplelift': 'bar'}, 'triplelift': {'id': 'bar', 'ext': {'provider': 'liveintent.com'}}}); + expect(result).to.eql({ 'lipb': { 'lipbid': 'foo', 'nonId': 'foo', 'triplelift': 'bar' }, 'triplelift': { 'id': 'bar', 'ext': { 'provider': 'liveintent.com' } } }); }); it('should decode a zetassp id to a separate object when present', function() { const result = liveIntentExternalIdSubmodule.decode({ nonId: 'foo', zetassp: 'bar' }, defaultConfigParams); - expect(result).to.eql({'lipb': {'lipbid': 'foo', 'nonId': 'foo', 'zetassp': 'bar'}, 'zetassp': {'id': 'bar', 'ext': {'provider': 'liveintent.com'}}}); + expect(result).to.eql({ 'lipb': { 'lipbid': 'foo', 'nonId': 'foo', 'zetassp': 'bar' }, 'zetassp': { 'id': 'bar', 'ext': { 'provider': 'liveintent.com' } } }); }); it('should decode a nexxen id to a separate object when present', function() { const result = liveIntentExternalIdSubmodule.decode({ nonId: 'foo', nexxen: 'bar' }, defaultConfigParams); - expect(result).to.eql({'lipb': {'lipbid': 'foo', 'nonId': 'foo', 'nexxen': 'bar'}, 'nexxen': {'id': 'bar', 'ext': {'provider': 'liveintent.com'}}}); + expect(result).to.eql({ 'lipb': { 'lipbid': 'foo', 'nonId': 'foo', 'nexxen': 'bar' }, 'nexxen': { 'id': 'bar', 'ext': { 'provider': 'liveintent.com' } } }); }); it('should decode a vidazoo id to a separate object when present', function() { const result = liveIntentExternalIdSubmodule.decode({ nonId: 'foo', vidazoo: 'bar' }, defaultConfigParams); - expect(result).to.eql({'lipb': {'lipbid': 'foo', 'nonId': 'foo', 'vidazoo': 'bar'}, 'vidazoo': {'id': 'bar', 'ext': {'provider': 'liveintent.com'}}}); + expect(result).to.eql({ 'lipb': { 'lipbid': 'foo', 'nonId': 'foo', 'vidazoo': 'bar' }, 'vidazoo': { 'id': 'bar', 'ext': { 'provider': 'liveintent.com' } } }); }); it('should decode the segments as part of lipb', function() { const result = liveIntentExternalIdSubmodule.decode({ nonId: 'foo', 'segments': ['bar'] }, defaultConfigParams); - expect(result).to.eql({'lipb': {'lipbid': 'foo', 'nonId': 'foo', 'segments': ['bar']}}); + expect(result).to.eql({ 'lipb': { 'lipbid': 'foo', 'nonId': 'foo', 'segments': ['bar'] } }); }); it('getId does not set the global variables when liModuleEnabled, liTreatmentRate and activatePartialTreatment are undefined', function() { @@ -520,7 +526,7 @@ describe('LiveIntentExternalId', function() { const configWithPartialTreatment = { params: { ...defaultConfigParams.params, activatePartialTreatment: undefined } }; const result = liveIntentExternalIdSubmodule.decode({ nonId: 'foo', vidazoo: 'bar', segments: ['tak'] }, configWithPartialTreatment); - expect(result).to.eql({'lipb': {'lipbid': 'foo', 'nonId': 'foo', 'vidazoo': 'bar', 'segments': ['tak']}, 'vidazoo': {'id': 'bar', 'ext': {'provider': 'liveintent.com'}}}); + expect(result).to.eql({ 'lipb': { 'lipbid': 'foo', 'nonId': 'foo', 'vidazoo': 'bar', 'segments': ['tak'] }, 'vidazoo': { 'id': 'bar', 'ext': { 'provider': 'liveintent.com' } } }); expect(window.liModuleEnabled).to.be.undefined expect(window.liTreatmentRate).to.be.undefined }); @@ -531,7 +537,7 @@ describe('LiveIntentExternalId', function() { const configWithPartialTreatment = { params: { ...defaultConfigParams.params, activatePartialTreatment: undefined } }; const result = liveIntentExternalIdSubmodule.decode({ nonId: 'foo', vidazoo: 'bar', segments: ['tak'] }, configWithPartialTreatment); - expect(result).to.eql({'lipb': {'lipbid': 'foo', 'nonId': 'foo', 'vidazoo': 'bar', 'segments': ['tak']}, 'vidazoo': {'id': 'bar', 'ext': {'provider': 'liveintent.com'}}}); + expect(result).to.eql({ 'lipb': { 'lipbid': 'foo', 'nonId': 'foo', 'vidazoo': 'bar', 'segments': ['tak'] }, 'vidazoo': { 'id': 'bar', 'ext': { 'provider': 'liveintent.com' } } }); expect(window.liModuleEnabled).to.be.undefined expect(window.liTreatmentRate).to.eq(0.7) }); @@ -542,7 +548,7 @@ describe('LiveIntentExternalId', function() { const configWithPartialTreatment = { params: { ...defaultConfigParams.params, activatePartialTreatment: false } }; const result = liveIntentExternalIdSubmodule.decode({ nonId: 'foo', vidazoo: 'bar', segments: ['tak'] }, configWithPartialTreatment); - expect(result).to.eql({'lipb': {'lipbid': 'foo', 'nonId': 'foo', 'vidazoo': 'bar', 'segments': ['tak']}, 'vidazoo': {'id': 'bar', 'ext': {'provider': 'liveintent.com'}}}); + expect(result).to.eql({ 'lipb': { 'lipbid': 'foo', 'nonId': 'foo', 'vidazoo': 'bar', 'segments': ['tak'] }, 'vidazoo': { 'id': 'bar', 'ext': { 'provider': 'liveintent.com' } } }); expect(window.liModuleEnabled).to.be.undefined expect(window.liTreatmentRate).to.eq(0.7) }); @@ -554,7 +560,7 @@ describe('LiveIntentExternalId', function() { const configWithPartialTreatment = { params: { ...defaultConfigParams.params, activatePartialTreatment: true } }; const result = liveIntentExternalIdSubmodule.decode({ nonId: 'foo', vidazoo: 'bar', segments: ['tak'] }, configWithPartialTreatment); - expect(result).to.eql({'lipb': {'lipbid': 'foo', 'nonId': 'foo', 'vidazoo': 'bar', 'segments': ['tak']}, 'vidazoo': {'id': 'bar', 'ext': {'provider': 'liveintent.com'}}}); + expect(result).to.eql({ 'lipb': { 'lipbid': 'foo', 'nonId': 'foo', 'vidazoo': 'bar', 'segments': ['tak'] }, 'vidazoo': { 'id': 'bar', 'ext': { 'provider': 'liveintent.com' } } }); expect(window.liModuleEnabled).to.eq(true) expect(window.liTreatmentRate).to.eq(0.7) }); @@ -613,7 +619,7 @@ describe('LiveIntentExternalId', function() { const configWithPartialTreatment = { params: { ...defaultConfigParams.params, activatePartialTreatment: true } }; const result = liveIntentExternalIdSubmodule.decode({ nonId: 'foo', vidazoo: 'bar', segments: ['tak'] }, configWithPartialTreatment); - expect(result).to.eql({'lipb': {'lipbid': 'foo', 'nonId': 'foo', 'vidazoo': 'bar', 'segments': ['tak']}, 'vidazoo': {'id': 'bar', 'ext': {'provider': 'liveintent.com'}}}); + expect(result).to.eql({ 'lipb': { 'lipbid': 'foo', 'nonId': 'foo', 'vidazoo': 'bar', 'segments': ['tak'] }, 'vidazoo': { 'id': 'bar', 'ext': { 'provider': 'liveintent.com' } } }); expect(window.liModuleEnabled).to.eq(true) expect(window.liTreatmentRate).to.eq(DEFAULT_TREATMENT_RATE) }); diff --git a/test/spec/modules/liveIntentIdMinimalSystem_spec.js b/test/spec/modules/liveIntentIdMinimalSystem_spec.js index c7bbe040986..eb2f93396dd 100644 --- a/test/spec/modules/liveIntentIdMinimalSystem_spec.js +++ b/test/spec/modules/liveIntentIdMinimalSystem_spec.js @@ -5,8 +5,8 @@ import { liveIntentIdSubmodule, reset as resetLiveIntentIdSubmodule, storage } f import * as refererDetection from '../../../src/refererDetection.js'; const PUBLISHER_ID = '89899'; -const defaultConfigParams = { params: {publisherId: PUBLISHER_ID} }; -const responseHeader = {'Content-Type': 'application/json'}; +const defaultConfigParams = { params: { publisherId: PUBLISHER_ID } }; +const responseHeader = { 'Content-Type': 'application/json' }; describe('LiveIntentMinimalId', function() { let logErrorStub; @@ -60,7 +60,7 @@ describe('LiveIntentMinimalId', function() { it('should call the Custom URL of the LiveIntent Identity Exchange endpoint', function() { getCookieStub.returns(null); const callBackSpy = sinon.spy(); - const submoduleCallback = liveIntentIdSubmodule.getId({ params: {...defaultConfigParams.params, ...{'url': 'https://dummy.liveintent.com/idex'}} }).callback; + const submoduleCallback = liveIntentIdSubmodule.getId({ params: { ...defaultConfigParams.params, ...{ 'url': 'https://dummy.liveintent.com/idex' } } }).callback; submoduleCallback(callBackSpy); const request = server.requests[0]; expect(request.url).to.be.eq('https://dummy.liveintent.com/idex/prebid/89899?resolve=nonId'); @@ -103,13 +103,15 @@ describe('LiveIntentMinimalId', function() { it('should call the default url of the LiveIntent Identity Exchange endpoint, with a partner', function() { getCookieStub.returns(null); const callBackSpy = sinon.spy(); - const submoduleCallback = liveIntentIdSubmodule.getId({ params: { - ...defaultConfigParams.params, - ...{ - 'url': 'https://dummy.liveintent.com/idex', - 'partner': 'rubicon' + const submoduleCallback = liveIntentIdSubmodule.getId({ + params: { + ...defaultConfigParams.params, + ...{ + 'url': 'https://dummy.liveintent.com/idex', + 'partner': 'rubicon' + } } - } }).callback; + }).callback; submoduleCallback(callBackSpy); const request = server.requests[0]; expect(request.url).to.be.eq('https://dummy.liveintent.com/idex/rubicon/89899?resolve=nonId'); @@ -172,12 +174,14 @@ describe('LiveIntentMinimalId', function() { const oldCookie = 'a-xxxx--123e4567-e89b-12d3-a456-426655440000' getDataFromLocalStorageStub.withArgs('_li_duid').returns(oldCookie); getDataFromLocalStorageStub.withArgs('_thirdPC').returns('third-pc'); - const configParams = { params: { - ...defaultConfigParams.params, - ...{ - 'identifiersToResolve': ['_thirdPC'] + const configParams = { + params: { + ...defaultConfigParams.params, + ...{ + 'identifiersToResolve': ['_thirdPC'] + } } - }}; + }; const callBackSpy = sinon.spy(); const submoduleCallback = liveIntentIdSubmodule.getId(configParams).callback; submoduleCallback(callBackSpy); @@ -193,13 +197,15 @@ describe('LiveIntentMinimalId', function() { it('should include an additional identifier value to resolve even if it is an object', function() { getCookieStub.returns(null); - getDataFromLocalStorageStub.withArgs('_thirdPC').returns({'key': 'value'}); - const configParams = { params: { - ...defaultConfigParams.params, - ...{ - 'identifiersToResolve': ['_thirdPC'] + getDataFromLocalStorageStub.withArgs('_thirdPC').returns({ 'key': 'value' }); + const configParams = { + params: { + ...defaultConfigParams.params, + ...{ + 'identifiersToResolve': ['_thirdPC'] + } } - }}; + }; const callBackSpy = sinon.spy(); const submoduleCallback = liveIntentIdSubmodule.getId(configParams).callback; submoduleCallback(callBackSpy); @@ -215,20 +221,22 @@ describe('LiveIntentMinimalId', function() { it('should decode a unifiedId to lipbId and remove it', function() { const result = liveIntentIdSubmodule.decode({ unifiedId: 'data' }); - expect(result).to.eql({'lipb': {'lipbid': 'data'}}); + expect(result).to.eql({ 'lipb': { 'lipbid': 'data' } }); }); it('should decode a nonId to lipbId', function() { const result = liveIntentIdSubmodule.decode({ nonId: 'data' }); - expect(result).to.eql({'lipb': {'lipbid': 'data', 'nonId': 'data'}}); + expect(result).to.eql({ 'lipb': { 'lipbid': 'data', 'nonId': 'data' } }); }); it('should resolve extra attributes', function() { const callBackSpy = sinon.spy(); - const submoduleCallback = liveIntentIdSubmodule.getId({ params: { - ...defaultConfigParams.params, - ...{ requestedAttributesOverrides: { 'foo': true, 'bar': false } } - } }).callback; + const submoduleCallback = liveIntentIdSubmodule.getId({ + params: { + ...defaultConfigParams.params, + ...{ requestedAttributesOverrides: { 'foo': true, 'bar': false } } + } + }).callback; submoduleCallback(callBackSpy); const request = server.requests[0]; expect(request.url).to.be.eq(`https://idx.liadm.com/idex/prebid/89899?resolve=nonId&resolve=foo`); @@ -241,68 +249,70 @@ describe('LiveIntentMinimalId', function() { }); it('should decode values with the segments but no nonId', function() { - const result = liveIntentIdSubmodule.decode({segments: ['tak']}, { params: defaultConfigParams }); - expect(result).to.eql({'lipb': {'segments': ['tak']}}); + const result = liveIntentIdSubmodule.decode({ segments: ['tak'] }, { params: defaultConfigParams }); + expect(result).to.eql({ 'lipb': { 'segments': ['tak'] } }); }); it('should decode a uid2 to a separate object when present', function() { const result = liveIntentIdSubmodule.decode({ nonId: 'foo', uid2: 'bar' }); - expect(result).to.eql({'lipb': {'lipbid': 'foo', 'nonId': 'foo', 'uid2': 'bar'}, 'uid2': {'id': 'bar', 'ext': {'provider': 'liveintent.com'}}}); + expect(result).to.eql({ 'lipb': { 'lipbid': 'foo', 'nonId': 'foo', 'uid2': 'bar' }, 'uid2': { 'id': 'bar', 'ext': { 'provider': 'liveintent.com' } } }); }); it('should decode values with uid2 but no nonId', function() { const result = liveIntentIdSubmodule.decode({ uid2: 'bar' }); - expect(result).to.eql({'lipb': {'uid2': 'bar'}, 'uid2': {'id': 'bar', 'ext': {'provider': 'liveintent.com'}}}); + expect(result).to.eql({ 'lipb': { 'uid2': 'bar' }, 'uid2': { 'id': 'bar', 'ext': { 'provider': 'liveintent.com' } } }); }); it('should decode a bidswitch id to a separate object when present', function() { const result = liveIntentIdSubmodule.decode({ nonId: 'foo', bidswitch: 'bar' }); - expect(result).to.eql({'lipb': {'lipbid': 'foo', 'nonId': 'foo', 'bidswitch': 'bar'}, 'bidswitch': {'id': 'bar', 'ext': {'provider': 'liveintent.com'}}}); + expect(result).to.eql({ 'lipb': { 'lipbid': 'foo', 'nonId': 'foo', 'bidswitch': 'bar' }, 'bidswitch': { 'id': 'bar', 'ext': { 'provider': 'liveintent.com' } } }); }); it('should decode a medianet id to a separate object when present', function() { const result = liveIntentIdSubmodule.decode({ nonId: 'foo', medianet: 'bar' }); - expect(result).to.eql({'lipb': {'lipbid': 'foo', 'nonId': 'foo', 'medianet': 'bar'}, 'medianet': {'id': 'bar', 'ext': {'provider': 'liveintent.com'}}}); + expect(result).to.eql({ 'lipb': { 'lipbid': 'foo', 'nonId': 'foo', 'medianet': 'bar' }, 'medianet': { 'id': 'bar', 'ext': { 'provider': 'liveintent.com' } } }); }); it('should decode a sovrn id to a separate object when present', function() { const result = liveIntentIdSubmodule.decode({ nonId: 'foo', sovrn: 'bar' }); - expect(result).to.eql({'lipb': {'lipbid': 'foo', 'nonId': 'foo', 'sovrn': 'bar'}, 'sovrn': {'id': 'bar', 'ext': {'provider': 'liveintent.com'}}}); + expect(result).to.eql({ 'lipb': { 'lipbid': 'foo', 'nonId': 'foo', 'sovrn': 'bar' }, 'sovrn': { 'id': 'bar', 'ext': { 'provider': 'liveintent.com' } } }); }); it('should decode a magnite id to a separate object when present', function() { const result = liveIntentIdSubmodule.decode({ nonId: 'foo', magnite: 'bar' }); - expect(result).to.eql({'lipb': {'lipbid': 'foo', 'nonId': 'foo', 'magnite': 'bar'}, 'magnite': {'id': 'bar', 'ext': {'provider': 'liveintent.com'}}}); + expect(result).to.eql({ 'lipb': { 'lipbid': 'foo', 'nonId': 'foo', 'magnite': 'bar' }, 'magnite': { 'id': 'bar', 'ext': { 'provider': 'liveintent.com' } } }); }); it('should decode an index id to a separate object when present', function() { const result = liveIntentIdSubmodule.decode({ nonId: 'foo', index: 'bar' }); - expect(result).to.eql({'lipb': {'lipbid': 'foo', 'nonId': 'foo', 'index': 'bar'}, 'index': {'id': 'bar', 'ext': {'provider': 'liveintent.com'}}}); + expect(result).to.eql({ 'lipb': { 'lipbid': 'foo', 'nonId': 'foo', 'index': 'bar' }, 'index': { 'id': 'bar', 'ext': { 'provider': 'liveintent.com' } } }); }); it('should decode an openx id to a separate object when present', function () { const result = liveIntentIdSubmodule.decode({ nonId: 'foo', openx: 'bar' }); - expect(result).to.eql({'lipb': {'lipbid': 'foo', 'nonId': 'foo', 'openx': 'bar'}, 'openx': {'id': 'bar', 'ext': {'provider': 'liveintent.com'}}}); + expect(result).to.eql({ 'lipb': { 'lipbid': 'foo', 'nonId': 'foo', 'openx': 'bar' }, 'openx': { 'id': 'bar', 'ext': { 'provider': 'liveintent.com' } } }); }); it('should decode an pubmatic id to a separate object when present', function() { const result = liveIntentIdSubmodule.decode({ nonId: 'foo', pubmatic: 'bar' }); - expect(result).to.eql({'lipb': {'lipbid': 'foo', 'nonId': 'foo', 'pubmatic': 'bar'}, 'pubmatic': {'id': 'bar', 'ext': {'provider': 'liveintent.com'}}}); + expect(result).to.eql({ 'lipb': { 'lipbid': 'foo', 'nonId': 'foo', 'pubmatic': 'bar' }, 'pubmatic': { 'id': 'bar', 'ext': { 'provider': 'liveintent.com' } } }); }); it('should decode a thetradedesk id to a separate object when present', function() { const provider = 'liveintent.com' - refererInfoStub.returns({domain: provider}) + refererInfoStub.returns({ domain: provider }) const result = liveIntentIdSubmodule.decode({ nonId: 'foo', thetradedesk: 'bar' }); - expect(result).to.eql({'lipb': {'lipbid': 'foo', 'nonId': 'foo', 'tdid': 'bar'}, 'tdid': {'id': 'bar', 'ext': {'rtiPartner': 'TDID', 'provider': provider}}}); + expect(result).to.eql({ 'lipb': { 'lipbid': 'foo', 'nonId': 'foo', 'tdid': 'bar' }, 'tdid': { 'id': 'bar', 'ext': { 'rtiPartner': 'TDID', 'provider': provider } } }); }); it('should allow disabling nonId resolution', function() { const callBackSpy = sinon.spy(); - const submoduleCallback = liveIntentIdSubmodule.getId({ params: { - ...defaultConfigParams.params, - ...{ requestedAttributesOverrides: { 'nonId': false, 'uid2': true } } - } }).callback; + const submoduleCallback = liveIntentIdSubmodule.getId({ + params: { + ...defaultConfigParams.params, + ...{ requestedAttributesOverrides: { 'nonId': false, 'uid2': true } } + } + }).callback; submoduleCallback(callBackSpy); const request = server.requests[0]; expect(request.url).to.be.eq(`https://idx.liadm.com/idex/prebid/89899?resolve=uid2`); @@ -316,31 +326,31 @@ describe('LiveIntentMinimalId', function() { it('should decode a sharethrough id to a separate object when present', function() { const result = liveIntentIdSubmodule.decode({ nonId: 'foo', sharethrough: 'bar' }); - expect(result).to.eql({'lipb': {'lipbid': 'foo', 'nonId': 'foo', 'sharethrough': 'bar'}, 'sharethrough': {'id': 'bar', 'ext': {'provider': 'liveintent.com'}}}); + expect(result).to.eql({ 'lipb': { 'lipbid': 'foo', 'nonId': 'foo', 'sharethrough': 'bar' }, 'sharethrough': { 'id': 'bar', 'ext': { 'provider': 'liveintent.com' } } }); }); it('should decode a sonobi id to a separate object when present', function() { const result = liveIntentIdSubmodule.decode({ nonId: 'foo', sonobi: 'bar' }); - expect(result).to.eql({'lipb': {'lipbid': 'foo', 'nonId': 'foo', 'sonobi': 'bar'}, 'sonobi': {'id': 'bar', 'ext': {'provider': 'liveintent.com'}}}); + expect(result).to.eql({ 'lipb': { 'lipbid': 'foo', 'nonId': 'foo', 'sonobi': 'bar' }, 'sonobi': { 'id': 'bar', 'ext': { 'provider': 'liveintent.com' } } }); }); it('should decode a zetassp id to a separate object when present', function() { const result = liveIntentIdSubmodule.decode({ nonId: 'foo', zetassp: 'bar' }, defaultConfigParams); - expect(result).to.eql({'lipb': {'lipbid': 'foo', 'nonId': 'foo', 'zetassp': 'bar'}, 'zetassp': {'id': 'bar', 'ext': {'provider': 'liveintent.com'}}}); + expect(result).to.eql({ 'lipb': { 'lipbid': 'foo', 'nonId': 'foo', 'zetassp': 'bar' }, 'zetassp': { 'id': 'bar', 'ext': { 'provider': 'liveintent.com' } } }); }); it('should decode a vidazoo id to a separate object when present', function() { const result = liveIntentIdSubmodule.decode({ nonId: 'foo', vidazoo: 'bar' }); - expect(result).to.eql({'lipb': {'lipbid': 'foo', 'nonId': 'foo', 'vidazoo': 'bar'}, 'vidazoo': {'id': 'bar', 'ext': {'provider': 'liveintent.com'}}}); + expect(result).to.eql({ 'lipb': { 'lipbid': 'foo', 'nonId': 'foo', 'vidazoo': 'bar' }, 'vidazoo': { 'id': 'bar', 'ext': { 'provider': 'liveintent.com' } } }); }); it('should decode a nexxen id to a separate object when present', function() { const result = liveIntentIdSubmodule.decode({ nonId: 'foo', nexxen: 'bar' }); - expect(result).to.eql({'lipb': {'lipbid': 'foo', 'nonId': 'foo', 'nexxen': 'bar'}, 'nexxen': {'id': 'bar', 'ext': {'provider': 'liveintent.com'}}}); + expect(result).to.eql({ 'lipb': { 'lipbid': 'foo', 'nonId': 'foo', 'nexxen': 'bar' }, 'nexxen': { 'id': 'bar', 'ext': { 'provider': 'liveintent.com' } } }); }); it('should decode the segments as part of lipb', function() { const result = liveIntentIdSubmodule.decode({ nonId: 'foo', 'segments': ['bar'] }, { params: defaultConfigParams }); - expect(result).to.eql({'lipb': {'lipbid': 'foo', 'nonId': 'foo', 'segments': ['bar']}}); + expect(result).to.eql({ 'lipb': { 'lipbid': 'foo', 'nonId': 'foo', 'segments': ['bar'] } }); }); }); diff --git a/test/spec/modules/liveIntentIdSystem_spec.js b/test/spec/modules/liveIntentIdSystem_spec.js index ecf7dc9a634..e6ce3a6cca2 100644 --- a/test/spec/modules/liveIntentIdSystem_spec.js +++ b/test/spec/modules/liveIntentIdSystem_spec.js @@ -4,14 +4,14 @@ import { DEFAULT_TREATMENT_RATE } from 'libraries/liveIntentId/shared.js'; import { gdprDataHandler, uspDataHandler, gppDataHandler, coppaDataHandler } from '../../../src/adapterManager.js'; import { server } from 'test/mocks/xhr.js'; import * as refererDetection from '../../../src/refererDetection.js'; -import {attachIdSystem} from '../../../modules/userId/index.js'; -import {createEidsArray} from '../../../modules/userId/eids.js'; +import { attachIdSystem } from '../../../modules/userId/index.js'; +import { createEidsArray } from '../../../modules/userId/eids.js'; resetLiveIntentIdSubmodule(); liveIntentIdSubmodule.setModuleMode('standard') const PUBLISHER_ID = '89899'; const defaultConfigParams = { params: { publisherId: PUBLISHER_ID, fireEventDelay: 1 } }; -const responseHeader = {'Content-Type': 'application/json'} +const responseHeader = { 'Content-Type': 'application/json' } function requests(...urlRegExps) { return server.requests.filter((request) => urlRegExps.some((regExp) => request.url.match(regExp))) @@ -107,10 +107,12 @@ describe('LiveIntentId', function() { }); it('should fire an event when getId and a hash is provided', function(done) { - liveIntentIdSubmodule.getId({ params: { - ...defaultConfigParams.params, - emailHash: '58131bc547fb87af94cebdaf3102321f' - }}); + liveIntentIdSubmodule.getId({ + params: { + ...defaultConfigParams.params, + emailHash: '58131bc547fb87af94cebdaf3102321f' + } + }); setTimeout(() => { const request = rpRequests()[0]; expect(request.url).to.match(/https:\/\/rp.liadm.com\/j\?.*e=58131bc547fb87af94cebdaf3102321f.+/) @@ -128,16 +130,18 @@ describe('LiveIntentId', function() { }); it('should initialize LiveConnect with the config params when decode and emit an event', function (done) { - liveIntentIdSubmodule.decode({}, { params: { - ...defaultConfigParams.params, - ...{ - url: 'https://dummy.liveintent.com', - liCollectConfig: { - appId: 'a-0001', - collectorUrl: 'https://collector.liveintent.com' + liveIntentIdSubmodule.decode({}, { + params: { + ...defaultConfigParams.params, + ...{ + url: 'https://dummy.liveintent.com', + liCollectConfig: { + appId: 'a-0001', + collectorUrl: 'https://collector.liveintent.com' + } } } - }}); + }); setTimeout(() => { const request = requests(/https:\/\/collector.liveintent.com\/j\?.*aid=a-0001.*&wpn=prebid.*/); expect(request.length).to.be.greaterThan(0); @@ -183,10 +187,12 @@ describe('LiveIntentId', function() { }); it('should fire an event when decode and a hash is provided', function(done) { - liveIntentIdSubmodule.decode({}, { params: { - ...defaultConfigParams.params, - emailHash: '58131bc547fb87af94cebdaf3102321f' - }}); + liveIntentIdSubmodule.decode({}, { + params: { + ...defaultConfigParams.params, + emailHash: '58131bc547fb87af94cebdaf3102321f' + } + }); setTimeout(() => { const request = rpRequests()[0]; expect(request.url).to.match(/https:\/\/rp.liadm.com\/j\?.*e=58131bc547fb87af94cebdaf3102321f.+/); @@ -216,7 +222,7 @@ describe('LiveIntentId', function() { it('should call the custom URL of the LiveIntent Identity Exchange endpoint', function() { getCookieStub.returns(null); const callBackSpy = sinon.spy(); - const submoduleCallback = liveIntentIdSubmodule.getId({ params: {...defaultConfigParams.params, ...{'url': 'https://dummy.liveintent.com/idex'}} }).callback; + const submoduleCallback = liveIntentIdSubmodule.getId({ params: { ...defaultConfigParams.params, ...{ 'url': 'https://dummy.liveintent.com/idex' } } }).callback; submoduleCallback(callBackSpy); const request = requests(/https:\/\/dummy.liveintent.com\/idex\/.*/)[0]; expect(request.url).to.match(/https:\/\/dummy.liveintent.com\/idex\/prebid\/89899\?.*cd=.localhost.*&resolve=nonId.*/); @@ -258,13 +264,15 @@ describe('LiveIntentId', function() { it('should call the default url of the LiveIntent Identity Exchange endpoint, with a partner', function() { getCookieStub.returns(null); const callBackSpy = sinon.spy(); - const submoduleCallback = liveIntentIdSubmodule.getId({ params: { - ...defaultConfigParams.params, - ...{ - 'url': 'https://dummy.liveintent.com/idex', - 'partner': 'rubicon' + const submoduleCallback = liveIntentIdSubmodule.getId({ + params: { + ...defaultConfigParams.params, + ...{ + 'url': 'https://dummy.liveintent.com/idex', + 'partner': 'rubicon' + } } - } }).callback; + }).callback; submoduleCallback(callBackSpy); const request = requests(/https:\/\/dummy.liveintent.com\/idex\/.*/)[0]; expect(request.url).to.match(/https:\/\/dummy.liveintent.com\/idex\/rubicon\/89899\?.*cd=.localhost.*&resolve=nonId.*/); @@ -328,12 +336,14 @@ describe('LiveIntentId', function() { const oldCookie = 'a-xxxx--123e4567-e89b-12d3-a456-426655440000' getCookieStub.withArgs('_lc2_fpi').returns(oldCookie); getDataFromLocalStorageStub.withArgs('_thirdPC').returns('third-pc'); - const configParams = { params: { - ...defaultConfigParams.params, - ...{ - 'identifiersToResolve': ['_thirdPC'] + const configParams = { + params: { + ...defaultConfigParams.params, + ...{ + 'identifiersToResolve': ['_thirdPC'] + } } - }}; + }; const callBackSpy = sinon.spy(); const submoduleCallback = liveIntentIdSubmodule.getId(configParams).callback; submoduleCallback(callBackSpy); @@ -350,13 +360,15 @@ describe('LiveIntentId', function() { it('should include an additional identifier value to resolve even if it is an object', function() { getCookieStub.returns(null); - getDataFromLocalStorageStub.withArgs('_thirdPC').returns({'key': 'value'}); - const configParams = { params: { - ...defaultConfigParams.params, - ...{ - 'identifiersToResolve': ['_thirdPC'] + getDataFromLocalStorageStub.withArgs('_thirdPC').returns({ 'key': 'value' }); + const configParams = { + params: { + ...defaultConfigParams.params, + ...{ + 'identifiersToResolve': ['_thirdPC'] + } } - }}; + }; const callBackSpy = sinon.spy(); const submoduleCallback = liveIntentIdSubmodule.getId(configParams).callback; submoduleCallback(callBackSpy); @@ -371,12 +383,14 @@ describe('LiveIntentId', function() { }); it('should include ip4,ip6,userAgent if it\'s present', function(done) { - liveIntentIdSubmodule.getId({ params: { - ...defaultConfigParams.params, - ipv4: 'foov4', - ipv6: 'foov6', - userAgent: 'boo' - }}); + liveIntentIdSubmodule.getId({ + params: { + ...defaultConfigParams.params, + ipv4: 'foov4', + ipv6: 'foov6', + userAgent: 'boo' + } + }); setTimeout(() => { const request = rpRequests()[0]; expect(request.url).to.match(/^https:\/\/rp\.liadm\.com\/j?.*pip=.*&pip6=.*$/) @@ -393,20 +407,22 @@ describe('LiveIntentId', function() { it('should decode a unifiedId to lipbId and remove it', function() { const result = liveIntentIdSubmodule.decode({ unifiedId: 'data' }, defaultConfigParams); - expect(result).to.eql({'lipb': {'lipbid': 'data'}}); + expect(result).to.eql({ 'lipb': { 'lipbid': 'data' } }); }); it('should decode a nonId to lipbId', function() { const result = liveIntentIdSubmodule.decode({ nonId: 'data' }, defaultConfigParams); - expect(result).to.eql({'lipb': {'lipbid': 'data', 'nonId': 'data'}}); + expect(result).to.eql({ 'lipb': { 'lipbid': 'data', 'nonId': 'data' } }); }); it('should resolve extra attributes', function() { const callBackSpy = sinon.spy(); - const submoduleCallback = liveIntentIdSubmodule.getId({ params: { - ...defaultConfigParams.params, - ...{ requestedAttributesOverrides: { 'foo': true, 'bar': false } } - } }).callback; + const submoduleCallback = liveIntentIdSubmodule.getId({ + params: { + ...defaultConfigParams.params, + ...{ requestedAttributesOverrides: { 'foo': true, 'bar': false } } + } + }).callback; submoduleCallback(callBackSpy); const request = idxRequests()[0]; expect(request.url).to.match(/https:\/\/idx.liadm.com\/idex\/prebid\/89899\?.*cd=.localhost.*&resolve=nonId.*&resolve=foo.*/); @@ -420,72 +436,74 @@ describe('LiveIntentId', function() { it('should decode a uid2 to a separate object when present', function() { const result = liveIntentIdSubmodule.decode({ nonId: 'foo', uid2: 'bar' }, defaultConfigParams); - expect(result).to.eql({'lipb': {'lipbid': 'foo', 'nonId': 'foo', 'uid2': 'bar'}, 'uid2': {'id': 'bar', 'ext': {'provider': 'liveintent.com'}}}); + expect(result).to.eql({ 'lipb': { 'lipbid': 'foo', 'nonId': 'foo', 'uid2': 'bar' }, 'uid2': { 'id': 'bar', 'ext': { 'provider': 'liveintent.com' } } }); }); it('should decode values with the segments but no nonId', function() { - const result = liveIntentIdSubmodule.decode({segments: ['tak']}, defaultConfigParams); - expect(result).to.eql({'lipb': {'segments': ['tak']}}); + const result = liveIntentIdSubmodule.decode({ segments: ['tak'] }, defaultConfigParams); + expect(result).to.eql({ 'lipb': { 'segments': ['tak'] } }); }); it('should decode values with uid2 but no nonId', function() { const result = liveIntentIdSubmodule.decode({ uid2: 'bar' }, defaultConfigParams); - expect(result).to.eql({'lipb': {'uid2': 'bar'}, 'uid2': {'id': 'bar', 'ext': {'provider': 'liveintent.com'}}}); + expect(result).to.eql({ 'lipb': { 'uid2': 'bar' }, 'uid2': { 'id': 'bar', 'ext': { 'provider': 'liveintent.com' } } }); }); it('should decode a bidswitch id to a separate object when present', function() { const result = liveIntentIdSubmodule.decode({ nonId: 'foo', bidswitch: 'bar' }, defaultConfigParams); - expect(result).to.eql({'lipb': {'lipbid': 'foo', 'nonId': 'foo', 'bidswitch': 'bar'}, 'bidswitch': {'id': 'bar', 'ext': {'provider': 'liveintent.com'}}}); + expect(result).to.eql({ 'lipb': { 'lipbid': 'foo', 'nonId': 'foo', 'bidswitch': 'bar' }, 'bidswitch': { 'id': 'bar', 'ext': { 'provider': 'liveintent.com' } } }); }); it('should decode a medianet id to a separate object when present', function() { const result = liveIntentIdSubmodule.decode({ nonId: 'foo', medianet: 'bar' }, defaultConfigParams); - expect(result).to.eql({'lipb': {'lipbid': 'foo', 'nonId': 'foo', 'medianet': 'bar'}, 'medianet': {'id': 'bar', 'ext': {'provider': 'liveintent.com'}}}); + expect(result).to.eql({ 'lipb': { 'lipbid': 'foo', 'nonId': 'foo', 'medianet': 'bar' }, 'medianet': { 'id': 'bar', 'ext': { 'provider': 'liveintent.com' } } }); }); it('should decode a sovrn id to a separate object when present', function() { const result = liveIntentIdSubmodule.decode({ nonId: 'foo', sovrn: 'bar' }, defaultConfigParams); - expect(result).to.eql({'lipb': {'lipbid': 'foo', 'nonId': 'foo', 'sovrn': 'bar'}, 'sovrn': {'id': 'bar', 'ext': {'provider': 'liveintent.com'}}}); + expect(result).to.eql({ 'lipb': { 'lipbid': 'foo', 'nonId': 'foo', 'sovrn': 'bar' }, 'sovrn': { 'id': 'bar', 'ext': { 'provider': 'liveintent.com' } } }); }); it('should decode a magnite id to a separate object when present', function() { const result = liveIntentIdSubmodule.decode({ nonId: 'foo', magnite: 'bar' }, defaultConfigParams); - expect(result).to.eql({'lipb': {'lipbid': 'foo', 'nonId': 'foo', 'magnite': 'bar'}, 'magnite': {'id': 'bar', 'ext': {'provider': 'liveintent.com'}}}); + expect(result).to.eql({ 'lipb': { 'lipbid': 'foo', 'nonId': 'foo', 'magnite': 'bar' }, 'magnite': { 'id': 'bar', 'ext': { 'provider': 'liveintent.com' } } }); }); it('should decode an index id to a separate object when present', function() { const result = liveIntentIdSubmodule.decode({ nonId: 'foo', index: 'bar' }, defaultConfigParams); - expect(result).to.eql({'lipb': {'lipbid': 'foo', 'nonId': 'foo', 'index': 'bar'}, 'index': {'id': 'bar', 'ext': {'provider': 'liveintent.com'}}}); + expect(result).to.eql({ 'lipb': { 'lipbid': 'foo', 'nonId': 'foo', 'index': 'bar' }, 'index': { 'id': 'bar', 'ext': { 'provider': 'liveintent.com' } } }); }); it('should decode an openx id to a separate object when present', function () { const result = liveIntentIdSubmodule.decode({ nonId: 'foo', openx: 'bar' }, defaultConfigParams); - expect(result).to.eql({'lipb': {'lipbid': 'foo', 'nonId': 'foo', 'openx': 'bar'}, 'openx': {'id': 'bar', 'ext': {'provider': 'liveintent.com'}}}); + expect(result).to.eql({ 'lipb': { 'lipbid': 'foo', 'nonId': 'foo', 'openx': 'bar' }, 'openx': { 'id': 'bar', 'ext': { 'provider': 'liveintent.com' } } }); }); it('should decode an pubmatic id to a separate object when present', function() { const result = liveIntentIdSubmodule.decode({ nonId: 'foo', pubmatic: 'bar' }, defaultConfigParams); - expect(result).to.eql({'lipb': {'lipbid': 'foo', 'nonId': 'foo', 'pubmatic': 'bar'}, 'pubmatic': {'id': 'bar', 'ext': {'provider': 'liveintent.com'}}}); + expect(result).to.eql({ 'lipb': { 'lipbid': 'foo', 'nonId': 'foo', 'pubmatic': 'bar' }, 'pubmatic': { 'id': 'bar', 'ext': { 'provider': 'liveintent.com' } } }); }); it('should decode a thetradedesk id to a separate object when present', function() { const provider = 'liveintent.com' - refererInfoStub.returns({domain: provider}) + refererInfoStub.returns({ domain: provider }) const result = liveIntentIdSubmodule.decode({ nonId: 'foo', thetradedesk: 'bar' }, defaultConfigParams); - expect(result).to.eql({'lipb': {'lipbid': 'foo', 'nonId': 'foo', 'tdid': 'bar'}, 'tdid': {'id': 'bar', 'ext': {'rtiPartner': 'TDID', 'provider': provider}}}); + expect(result).to.eql({ 'lipb': { 'lipbid': 'foo', 'nonId': 'foo', 'tdid': 'bar' }, 'tdid': { 'id': 'bar', 'ext': { 'rtiPartner': 'TDID', 'provider': provider } } }); }); it('should decode the segments as part of lipb', function() { const result = liveIntentIdSubmodule.decode({ nonId: 'foo', 'segments': ['bar'] }, defaultConfigParams); - expect(result).to.eql({'lipb': {'lipbid': 'foo', 'nonId': 'foo', 'segments': ['bar']}}); + expect(result).to.eql({ 'lipb': { 'lipbid': 'foo', 'nonId': 'foo', 'segments': ['bar'] } }); }); it('should allow disabling nonId resolution', function() { const callBackSpy = sinon.spy(); - const submoduleCallback = liveIntentIdSubmodule.getId({ params: { - ...defaultConfigParams.params, - ...{ requestedAttributesOverrides: { 'nonId': false, 'uid2': true } } - } }).callback; + const submoduleCallback = liveIntentIdSubmodule.getId({ + params: { + ...defaultConfigParams.params, + ...{ requestedAttributesOverrides: { 'nonId': false, 'uid2': true } } + } + }).callback; submoduleCallback(callBackSpy); const request = idxRequests()[0]; expect(request.url).to.match(/https:\/\/idx.liadm.com\/idex\/prebid\/89899\?.*cd=.localhost.*&resolve=uid2.*/); @@ -499,32 +517,32 @@ describe('LiveIntentId', function() { it('should decode a sharethrough id to a separate object when present', function() { const result = liveIntentIdSubmodule.decode({ nonId: 'foo', sharethrough: 'bar' }, defaultConfigParams); - expect(result).to.eql({'lipb': {'lipbid': 'foo', 'nonId': 'foo', 'sharethrough': 'bar'}, 'sharethrough': {'id': 'bar', 'ext': {'provider': 'liveintent.com'}}}); + expect(result).to.eql({ 'lipb': { 'lipbid': 'foo', 'nonId': 'foo', 'sharethrough': 'bar' }, 'sharethrough': { 'id': 'bar', 'ext': { 'provider': 'liveintent.com' } } }); }); it('should decode a sonobi id to a separate object when present', function() { const result = liveIntentIdSubmodule.decode({ nonId: 'foo', sonobi: 'bar' }, defaultConfigParams); - expect(result).to.eql({'lipb': {'lipbid': 'foo', 'nonId': 'foo', 'sonobi': 'bar'}, 'sonobi': {'id': 'bar', 'ext': {'provider': 'liveintent.com'}}}); + expect(result).to.eql({ 'lipb': { 'lipbid': 'foo', 'nonId': 'foo', 'sonobi': 'bar' }, 'sonobi': { 'id': 'bar', 'ext': { 'provider': 'liveintent.com' } } }); }); it('should decode a triplelift id to a separate object when present', function() { const result = liveIntentIdSubmodule.decode({ nonId: 'foo', triplelift: 'bar' }, defaultConfigParams); - expect(result).to.eql({'lipb': {'lipbid': 'foo', 'nonId': 'foo', 'triplelift': 'bar'}, 'triplelift': {'id': 'bar', 'ext': {'provider': 'liveintent.com'}}}); + expect(result).to.eql({ 'lipb': { 'lipbid': 'foo', 'nonId': 'foo', 'triplelift': 'bar' }, 'triplelift': { 'id': 'bar', 'ext': { 'provider': 'liveintent.com' } } }); }); it('should decode a zetassp id to a separate object when present', function() { const result = liveIntentIdSubmodule.decode({ nonId: 'foo', zetassp: 'bar' }, defaultConfigParams); - expect(result).to.eql({'lipb': {'lipbid': 'foo', 'nonId': 'foo', 'zetassp': 'bar'}, 'zetassp': {'id': 'bar', 'ext': {'provider': 'liveintent.com'}}}); + expect(result).to.eql({ 'lipb': { 'lipbid': 'foo', 'nonId': 'foo', 'zetassp': 'bar' }, 'zetassp': { 'id': 'bar', 'ext': { 'provider': 'liveintent.com' } } }); }); it('should decode a nexxen id to a separate object when present', function() { const result = liveIntentIdSubmodule.decode({ nonId: 'foo', nexxen: 'bar' }, defaultConfigParams); - expect(result).to.eql({'lipb': {'lipbid': 'foo', 'nonId': 'foo', 'nexxen': 'bar'}, 'nexxen': {'id': 'bar', 'ext': {'provider': 'liveintent.com'}}}); + expect(result).to.eql({ 'lipb': { 'lipbid': 'foo', 'nonId': 'foo', 'nexxen': 'bar' }, 'nexxen': { 'id': 'bar', 'ext': { 'provider': 'liveintent.com' } } }); }); it('should decode a vidazoo id to a separate object when present', function() { const result = liveIntentIdSubmodule.decode({ nonId: 'foo', vidazoo: 'bar' }, defaultConfigParams); - expect(result).to.eql({'lipb': {'lipbid': 'foo', 'nonId': 'foo', 'vidazoo': 'bar'}, 'vidazoo': {'id': 'bar', 'ext': {'provider': 'liveintent.com'}}}); + expect(result).to.eql({ 'lipb': { 'lipbid': 'foo', 'nonId': 'foo', 'vidazoo': 'bar' }, 'vidazoo': { 'id': 'bar', 'ext': { 'provider': 'liveintent.com' } } }); }); it('getId does not set the global variables when liModuleEnabled, liTreatmentRate and activatePartialTreatment are undefined', function() { @@ -607,7 +625,7 @@ describe('LiveIntentId', function() { const configWithPartialTreatment = { params: { ...defaultConfigParams.params, activatePartialTreatment: undefined } }; const result = liveIntentIdSubmodule.decode({ nonId: 'foo', vidazoo: 'bar', segments: ['tak'] }, configWithPartialTreatment); - expect(result).to.eql({'lipb': {'lipbid': 'foo', 'nonId': 'foo', 'vidazoo': 'bar', 'segments': ['tak']}, 'vidazoo': {'id': 'bar', 'ext': {'provider': 'liveintent.com'}}}); + expect(result).to.eql({ 'lipb': { 'lipbid': 'foo', 'nonId': 'foo', 'vidazoo': 'bar', 'segments': ['tak'] }, 'vidazoo': { 'id': 'bar', 'ext': { 'provider': 'liveintent.com' } } }); expect(window.liModuleEnabled).to.be.undefined expect(window.liTreatmentRate).to.be.undefined }); @@ -618,7 +636,7 @@ describe('LiveIntentId', function() { const configWithPartialTreatment = { params: { ...defaultConfigParams.params, activatePartialTreatment: undefined } }; const result = liveIntentIdSubmodule.decode({ nonId: 'foo', vidazoo: 'bar', segments: ['tak'] }, configWithPartialTreatment); - expect(result).to.eql({'lipb': {'lipbid': 'foo', 'nonId': 'foo', 'vidazoo': 'bar', 'segments': ['tak']}, 'vidazoo': {'id': 'bar', 'ext': {'provider': 'liveintent.com'}}}); + expect(result).to.eql({ 'lipb': { 'lipbid': 'foo', 'nonId': 'foo', 'vidazoo': 'bar', 'segments': ['tak'] }, 'vidazoo': { 'id': 'bar', 'ext': { 'provider': 'liveintent.com' } } }); expect(window.liModuleEnabled).to.be.undefined expect(window.liTreatmentRate).to.eq(0.7) }); @@ -629,7 +647,7 @@ describe('LiveIntentId', function() { const configWithPartialTreatment = { params: { ...defaultConfigParams.params, activatePartialTreatment: false } }; const result = liveIntentIdSubmodule.decode({ nonId: 'foo', vidazoo: 'bar', segments: ['tak'] }, configWithPartialTreatment); - expect(result).to.eql({'lipb': {'lipbid': 'foo', 'nonId': 'foo', 'vidazoo': 'bar', 'segments': ['tak']}, 'vidazoo': {'id': 'bar', 'ext': {'provider': 'liveintent.com'}}}); + expect(result).to.eql({ 'lipb': { 'lipbid': 'foo', 'nonId': 'foo', 'vidazoo': 'bar', 'segments': ['tak'] }, 'vidazoo': { 'id': 'bar', 'ext': { 'provider': 'liveintent.com' } } }); expect(window.liModuleEnabled).to.be.undefined expect(window.liTreatmentRate).to.eq(0.7) }); @@ -641,7 +659,7 @@ describe('LiveIntentId', function() { const configWithPartialTreatment = { params: { ...defaultConfigParams.params, activatePartialTreatment: true } }; const result = liveIntentIdSubmodule.decode({ nonId: 'foo', vidazoo: 'bar', segments: ['tak'] }, configWithPartialTreatment); - expect(result).to.eql({'lipb': {'lipbid': 'foo', 'nonId': 'foo', 'vidazoo': 'bar', 'segments': ['tak']}, 'vidazoo': {'id': 'bar', 'ext': {'provider': 'liveintent.com'}}}); + expect(result).to.eql({ 'lipb': { 'lipbid': 'foo', 'nonId': 'foo', 'vidazoo': 'bar', 'segments': ['tak'] }, 'vidazoo': { 'id': 'bar', 'ext': { 'provider': 'liveintent.com' } } }); expect(window.liModuleEnabled).to.eq(true) expect(window.liTreatmentRate).to.eq(0.7) }); @@ -700,7 +718,7 @@ describe('LiveIntentId', function() { const configWithPartialTreatment = { params: { ...defaultConfigParams.params, activatePartialTreatment: true } }; const result = liveIntentIdSubmodule.decode({ nonId: 'foo', vidazoo: 'bar', segments: ['tak'] }, configWithPartialTreatment); - expect(result).to.eql({'lipb': {'lipbid': 'foo', 'nonId': 'foo', 'vidazoo': 'bar', 'segments': ['tak']}, 'vidazoo': {'id': 'bar', 'ext': {'provider': 'liveintent.com'}}}); + expect(result).to.eql({ 'lipb': { 'lipbid': 'foo', 'nonId': 'foo', 'vidazoo': 'bar', 'segments': ['tak'] }, 'vidazoo': { 'id': 'bar', 'ext': { 'provider': 'liveintent.com' } } }); expect(window.liModuleEnabled).to.eq(true) expect(window.liTreatmentRate).to.eq(DEFAULT_TREATMENT_RATE) }); @@ -720,8 +738,8 @@ describe('LiveIntentId', function() { expect(newEids.length).to.equal(1); expect(newEids[0]).to.deep.equal({ source: 'liveintent.com', - uids: [{id: 'some-random-id-value', atype: 3}], - ext: {segments: ['s1', 's2']} + uids: [{ id: 'some-random-id-value', atype: 3 }], + ext: { segments: ['s1', 's2'] } }); }); it('fpid; getValue call', function() { @@ -734,12 +752,12 @@ describe('LiveIntentId', function() { expect(newEids.length).to.equal(1); expect(newEids[0]).to.deep.equal({ source: 'fpid.liveintent.com', - uids: [{id: 'some-random-id-value', atype: 1}] + uids: [{ id: 'some-random-id-value', atype: 1 }] }); }); it('bidswitch', function() { const userId = { - bidswitch: {'id': 'sample_id'} + bidswitch: { 'id': 'sample_id' } }; const newEids = createEidsArray(userId); expect(newEids.length).to.equal(1); @@ -754,7 +772,7 @@ describe('LiveIntentId', function() { it('bidswitch with ext', function() { const userId = { - bidswitch: {'id': 'sample_id', 'ext': {'provider': 'some.provider.com'}} + bidswitch: { 'id': 'sample_id', 'ext': { 'provider': 'some.provider.com' } } }; const newEids = createEidsArray(userId); expect(newEids.length).to.equal(1); @@ -771,7 +789,7 @@ describe('LiveIntentId', function() { }); it('medianet', function() { const userId = { - medianet: {'id': 'sample_id'} + medianet: { 'id': 'sample_id' } }; const newEids = createEidsArray(userId); expect(newEids.length).to.equal(1); @@ -786,7 +804,7 @@ describe('LiveIntentId', function() { it('medianet with ext', function() { const userId = { - medianet: {'id': 'sample_id', 'ext': {'provider': 'some.provider.com'}} + medianet: { 'id': 'sample_id', 'ext': { 'provider': 'some.provider.com' } } }; const newEids = createEidsArray(userId); expect(newEids.length).to.equal(1); @@ -804,7 +822,7 @@ describe('LiveIntentId', function() { it('sovrn', function() { const userId = { - sovrn: {'id': 'sample_id'} + sovrn: { 'id': 'sample_id' } }; const newEids = createEidsArray(userId); expect(newEids.length).to.equal(1); @@ -819,7 +837,7 @@ describe('LiveIntentId', function() { it('sovrn with ext', function() { const userId = { - sovrn: {'id': 'sample_id', 'ext': {'provider': 'some.provider.com'}} + sovrn: { 'id': 'sample_id', 'ext': { 'provider': 'some.provider.com' } } }; const newEids = createEidsArray(userId); expect(newEids.length).to.equal(1); @@ -837,7 +855,7 @@ describe('LiveIntentId', function() { it('magnite', function() { const userId = { - magnite: {'id': 'sample_id'} + magnite: { 'id': 'sample_id' } }; const newEids = createEidsArray(userId); expect(newEids.length).to.equal(1); @@ -852,7 +870,7 @@ describe('LiveIntentId', function() { it('magnite with ext', function() { const userId = { - magnite: {'id': 'sample_id', 'ext': {'provider': 'some.provider.com'}} + magnite: { 'id': 'sample_id', 'ext': { 'provider': 'some.provider.com' } } }; const newEids = createEidsArray(userId); expect(newEids.length).to.equal(1); @@ -869,7 +887,7 @@ describe('LiveIntentId', function() { }); it('index', function() { const userId = { - index: {'id': 'sample_id'} + index: { 'id': 'sample_id' } }; const newEids = createEidsArray(userId); expect(newEids.length).to.equal(1); @@ -884,7 +902,7 @@ describe('LiveIntentId', function() { it('index with ext', function() { const userId = { - index: {'id': 'sample_id', 'ext': {'provider': 'some.provider.com'}} + index: { 'id': 'sample_id', 'ext': { 'provider': 'some.provider.com' } } }; const newEids = createEidsArray(userId); expect(newEids.length).to.equal(1); @@ -935,7 +953,7 @@ describe('LiveIntentId', function() { it('pubmatic', function() { const userId = { - pubmatic: {'id': 'sample_id'} + pubmatic: { 'id': 'sample_id' } }; const newEids = createEidsArray(userId); expect(newEids.length).to.equal(1); @@ -950,7 +968,7 @@ describe('LiveIntentId', function() { it('pubmatic with ext', function() { const userId = { - pubmatic: {'id': 'sample_id', 'ext': {'provider': 'some.provider.com'}} + pubmatic: { 'id': 'sample_id', 'ext': { 'provider': 'some.provider.com' } } }; const newEids = createEidsArray(userId); expect(newEids.length).to.equal(1); @@ -976,7 +994,7 @@ describe('LiveIntentId', function() { expect(newEids.length).to.equal(1); expect(newEids[0]).to.deep.equal({ source: 'liveintent.com', - uids: [{id: 'some-random-id-value', atype: 3}] + uids: [{ id: 'some-random-id-value', atype: 3 }] }); }); diff --git a/test/spec/modules/liveIntentRtdProvider_spec.js b/test/spec/modules/liveIntentRtdProvider_spec.js index bde3e48b692..2b8ac0cee0e 100644 --- a/test/spec/modules/liveIntentRtdProvider_spec.js +++ b/test/spec/modules/liveIntentRtdProvider_spec.js @@ -1,4 +1,4 @@ -import {liveIntentRtdSubmodule} from 'modules/liveIntentRtdProvider.js'; +import { liveIntentRtdSubmodule } from 'modules/liveIntentRtdProvider.js'; import * as utils from 'src/utils.js'; import { expect } from 'chai'; @@ -63,7 +63,7 @@ describe('LiveIntent Rtd Provider', function () { liveIntentRtdSubmodule.onBidRequestEvent(bidRequest); const ortb2 = bidRequest.bids[0].ortb2; - const expectedOrtb2 = {user: {data: [{name: 'liveintent.com', segment: [{id: 'asa_1231'}, {id: 'lalo_4311'}, {id: 'liurl_99123'}]}]}} + const expectedOrtb2 = { user: { data: [{ name: 'liveintent.com', segment: [{ id: 'asa_1231' }, { id: 'lalo_4311' }, { id: 'liurl_99123' }] }] } } expect(ortb2).to.deep.equal(expectedOrtb2); }); @@ -73,7 +73,7 @@ describe('LiveIntent Rtd Provider', function () { liveIntentRtdSubmodule.onBidRequestEvent(bidRequest); const ortb2 = bidRequest.bids[0].ortb2; - const expectedOrtb2 = {source: {}, user: {data: [{name: 'liveintent.com', segment: [{id: 'asa_1231'}, {id: 'lalo_4311'}, {id: 'liurl_99123'}]}]}} + const expectedOrtb2 = { source: {}, user: { data: [{ name: 'liveintent.com', segment: [{ id: 'asa_1231' }, { id: 'lalo_4311' }, { id: 'liurl_99123' }] }] } } expect(ortb2).to.deep.equal(expectedOrtb2); }); @@ -86,7 +86,7 @@ describe('LiveIntent Rtd Provider', function () { liveIntentRtdSubmodule.onBidRequestEvent(bidRequest); const ortb2 = bidRequest.bids[0].ortb2; - const expectedOrtb2 = {source: {}, user: {data: [{name: 'liveintent.com', segment: [{id: 'asa_1231'}, {id: 'lalo_4311'}, {id: 'liurl_99123'}]}]}} + const expectedOrtb2 = { source: {}, user: { data: [{ name: 'liveintent.com', segment: [{ id: 'asa_1231' }, { id: 'lalo_4311' }, { id: 'liurl_99123' }] }] } } expect(ortb2).to.deep.equal(expectedOrtb2); }); @@ -109,7 +109,7 @@ describe('LiveIntent Rtd Provider', function () { liveIntentRtdSubmodule.onBidRequestEvent(bidRequest); const ortb2 = bidRequest.bids[0].ortb2; - const expectedOrtb2 = {source: {}, user: {data: [{name: 'example.com', segment: [{id: 'a_1231'}, {id: 'b_4311'}]}, {name: 'liveintent.com', segment: [{id: 'asa_1231'}, {id: 'lalo_4311'}, {id: 'liurl_99123'}]}]}} + const expectedOrtb2 = { source: {}, user: { data: [{ name: 'example.com', segment: [{ id: 'a_1231' }, { id: 'b_4311' }] }, { name: 'liveintent.com', segment: [{ id: 'asa_1231' }, { id: 'lalo_4311' }, { id: 'liurl_99123' }] }] } } expect(ortb2).to.deep.equal(expectedOrtb2); }); }); diff --git a/test/spec/modules/livewrappedAnalyticsAdapter_spec.js b/test/spec/modules/livewrappedAnalyticsAdapter_spec.js index f84d4ace1ff..56b7df292b8 100644 --- a/test/spec/modules/livewrappedAnalyticsAdapter_spec.js +++ b/test/spec/modules/livewrappedAnalyticsAdapter_spec.js @@ -3,6 +3,7 @@ import { AD_RENDER_FAILED_REASON, EVENTS, STATUS } from 'src/constants.js'; import { config } from 'src/config.js'; import { server } from 'test/mocks/xhr.js'; import { setConfig } from 'modules/currency.js'; +import * as adUnits from 'src/utils/adUnits'; const events = require('src/events'); const utils = require('src/utils'); @@ -323,7 +324,7 @@ describe('Livewrapped analytics adapter', function () { } sandbox.stub(events, 'getEvents').returns([]); sandbox.stub(utils, 'timestamp').returns(1519149562416); - sandbox.stub(document, 'getElementById').returns(element); + sandbox.stub(adUnits, 'getAdUnitElement').returns(element); clock = sandbox.useFakeTimers(1519767013781); setConfig({ @@ -554,7 +555,7 @@ describe('Livewrapped analytics adapter', function () { 'bidId': '3ecff0db240757', 'lwflr': { 'flr': 1.1, - 'bflrs': {'livewrapped': 2.2} + 'bflrs': { 'livewrapped': 2.2 } } } ], diff --git a/test/spec/modules/livewrappedBidAdapter_spec.js b/test/spec/modules/livewrappedBidAdapter_spec.js index 86bb680436f..30dd1c4baf3 100644 --- a/test/spec/modules/livewrappedBidAdapter_spec.js +++ b/test/spec/modules/livewrappedBidAdapter_spec.js @@ -1,6 +1,6 @@ -import {expect} from 'chai'; -import {spec, storage} from 'modules/livewrappedBidAdapter.js'; -import {config} from 'src/config.js'; +import { expect } from 'chai'; +import { spec, storage } from 'modules/livewrappedBidAdapter.js'; +import { config } from 'src/config.js'; import * as utils from 'src/utils.js'; import { NATIVE, VIDEO } from 'src/mediaTypes.js'; import { setConfig as setCurrencyConfig } from '../../../modules/currency.js'; @@ -32,7 +32,7 @@ describe('Livewrapped adapter tests', function () { publisherId: '26947112-2289-405D-88C1-A7340C57E63E', userId: 'user id', url: 'https://www.domain.com', - seats: {'dsp': ['seat 1']} + seats: { 'dsp': ['seat 1'] } }, adUnitCode: 'panorama_d_1', sizes: [[980, 240], [980, 120]], @@ -59,7 +59,7 @@ describe('Livewrapped adapter tests', function () { describe('isBidRequestValid', function() { it('should accept a request with id only as valid', function() { - const bid = {params: {adUnitId: '9E153CED-61BC-479E-98DF-24DC0D01BA37'}}; + const bid = { params: { adUnitId: '9E153CED-61BC-479E-98DF-24DC0D01BA37' } }; const result = spec.isBidRequestValid(bid); @@ -67,7 +67,7 @@ describe('Livewrapped adapter tests', function () { }); it('should accept a request with adUnitName and PublisherId as valid', function() { - const bid = {params: {adUnitName: 'panorama_d_1', publisherId: '26947112-2289-405D-88C1-A7340C57E63E'}}; + const bid = { params: { adUnitName: 'panorama_d_1', publisherId: '26947112-2289-405D-88C1-A7340C57E63E' } }; const result = spec.isBidRequestValid(bid); @@ -75,7 +75,7 @@ describe('Livewrapped adapter tests', function () { }); it('should accept a request with adUnitCode and PublisherId as valid', function() { - const bid = {adUnitCode: 'panorama_d_1', params: {publisherId: '26947112-2289-405D-88C1-A7340C57E63E'}}; + const bid = { adUnitCode: 'panorama_d_1', params: { publisherId: '26947112-2289-405D-88C1-A7340C57E63E' } }; const result = spec.isBidRequestValid(bid); @@ -83,7 +83,7 @@ describe('Livewrapped adapter tests', function () { }); it('should accept a request with placementCode and PublisherId as valid', function() { - const bid = {placementCode: 'panorama_d_1', params: {publisherId: '26947112-2289-405D-88C1-A7340C57E63E'}}; + const bid = { placementCode: 'panorama_d_1', params: { publisherId: '26947112-2289-405D-88C1-A7340C57E63E' } }; const result = spec.isBidRequestValid(bid); @@ -91,7 +91,7 @@ describe('Livewrapped adapter tests', function () { }); it('should not accept a request with adUnitName, adUnitCode, placementCode but no PublisherId as valid', function() { - const bid = {placementCode: 'panorama_d_1', adUnitCode: 'panorama_d_1', params: {adUnitName: 'panorama_d_1'}}; + const bid = { placementCode: 'panorama_d_1', adUnitCode: 'panorama_d_1', params: { adUnitName: 'panorama_d_1' } }; const result = spec.isBidRequestValid(bid); @@ -113,7 +113,7 @@ describe('Livewrapped adapter tests', function () { publisherId: '26947112-2289-405D-88C1-A7340C57E63E', userId: 'user id', url: 'https://www.domain.com', - seats: {'dsp': ['seat 1']}, + seats: { 'dsp': ['seat 1'] }, version: '1.4', width: 100, height: 100, @@ -122,7 +122,7 @@ describe('Livewrapped adapter tests', function () { adUnitId: '9E153CED-61BC-479E-98DF-24DC0D01BA37', callerAdUnitId: 'panorama_d_1', bidId: '2ffb201a808da7', - formats: [{width: 980, height: 240}, {width: 980, height: 120}], + formats: [{ width: 980, height: 240 }, { width: 980, height: 120 }], rtbData: { ext: { tid: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D' @@ -138,7 +138,7 @@ describe('Livewrapped adapter tests', function () { sandbox.stub(utils, 'isSafariBrowser').callsFake(() => false); sandbox.stub(storage, 'cookiesAreEnabled').callsFake(() => true); const ortb2ImpRequest = clone(bidderRequest); - ortb2ImpRequest.bids[0].ortb2Imp.ext.data = {key: 'value'}; + ortb2ImpRequest.bids[0].ortb2Imp.ext.data = { key: 'value' }; const result = spec.buildRequests(ortb2ImpRequest.bids, ortb2ImpRequest); const data = JSON.parse(result.data); @@ -149,7 +149,7 @@ describe('Livewrapped adapter tests', function () { publisherId: '26947112-2289-405D-88C1-A7340C57E63E', userId: 'user id', url: 'https://www.domain.com', - seats: {'dsp': ['seat 1']}, + seats: { 'dsp': ['seat 1'] }, version: '1.4', width: 100, height: 100, @@ -158,10 +158,10 @@ describe('Livewrapped adapter tests', function () { adUnitId: '9E153CED-61BC-479E-98DF-24DC0D01BA37', callerAdUnitId: 'panorama_d_1', bidId: '2ffb201a808da7', - formats: [{width: 980, height: 240}, {width: 980, height: 120}], + formats: [{ width: 980, height: 240 }, { width: 980, height: 120 }], rtbData: { ext: { - data: {key: 'value'}, + data: { key: 'value' }, tid: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D' }, } @@ -191,7 +191,7 @@ describe('Livewrapped adapter tests', function () { publisherId: '26947112-2289-405D-88C1-A7340C57E63E', userId: 'user id', url: 'https://www.domain.com', - seats: {'dsp': ['seat 1']}, + seats: { 'dsp': ['seat 1'] }, version: '1.4', width: 100, height: 100, @@ -205,7 +205,7 @@ describe('Livewrapped adapter tests', function () { tid: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D' }, }, - formats: [{width: 980, height: 240}, {width: 980, height: 120}] + formats: [{ width: 980, height: 240 }, { width: 980, height: 120 }] }, { callerAdUnitId: 'box_d_1', bidId: '3ffb201a808da7', @@ -214,7 +214,7 @@ describe('Livewrapped adapter tests', function () { tid: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D' }, }, - formats: [{width: 300, height: 250}] + formats: [{ width: 300, height: 250 }] }] }; @@ -237,7 +237,7 @@ describe('Livewrapped adapter tests', function () { publisherId: '26947112-2289-405D-88C1-A7340C57E63E', userId: 'user id', url: 'https://www.domain.com', - seats: {'dsp': ['seat 1']}, + seats: { 'dsp': ['seat 1'] }, version: '1.4', width: 100, height: 100, @@ -250,7 +250,7 @@ describe('Livewrapped adapter tests', function () { tid: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D' }, }, - formats: [{width: 980, height: 240}, {width: 980, height: 120}] + formats: [{ width: 980, height: 240 }, { width: 980, height: 120 }] }] }; @@ -285,7 +285,7 @@ describe('Livewrapped adapter tests', function () { tid: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D' }, }, - formats: [{width: 980, height: 240}, {width: 980, height: 120}] + formats: [{ width: 980, height: 240 }, { width: 980, height: 120 }] }] }; @@ -320,7 +320,7 @@ describe('Livewrapped adapter tests', function () { tid: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D' }, }, - formats: [{width: 980, height: 240}, {width: 980, height: 120}] + formats: [{ width: 980, height: 240 }, { width: 980, height: 120 }] }] }; @@ -357,7 +357,7 @@ describe('Livewrapped adapter tests', function () { tid: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D' }, }, - formats: [{width: 980, height: 240}, {width: 980, height: 120}] + formats: [{ width: 980, height: 240 }, { width: 980, height: 120 }] }] }; @@ -394,7 +394,7 @@ describe('Livewrapped adapter tests', function () { tid: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D' }, }, - formats: [{width: 980, height: 240}, {width: 980, height: 120}] + formats: [{ width: 980, height: 240 }, { width: 980, height: 120 }] }] }; @@ -408,7 +408,7 @@ describe('Livewrapped adapter tests', function () { 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'}]}; + testbidRequest.bids[0].params.options = { keyvalues: [{ key: 'key', value: 'value' }] }; const result = spec.buildRequests(testbidRequest.bids, testbidRequest); const data = JSON.parse(result.data); @@ -428,8 +428,8 @@ describe('Livewrapped adapter tests', function () { tid: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D' }, }, - formats: [{width: 980, height: 240}, {width: 980, height: 120}], - options: {keyvalues: [{key: 'key', value: 'value'}]} + formats: [{ width: 980, height: 240 }, { width: 980, height: 120 }], + options: { keyvalues: [{ key: 'key', value: 'value' }] } }] }; @@ -464,7 +464,7 @@ describe('Livewrapped adapter tests', function () { tid: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D' }, }, - formats: [{width: 980, height: 240}, {width: 980, height: 120}] + formats: [{ width: 980, height: 240 }, { width: 980, height: 120 }] }] }; @@ -478,7 +478,7 @@ describe('Livewrapped adapter tests', function () { delete testbidRequest.bids[0].params.userId; delete testbidRequest.bids[0].params.seats; delete testbidRequest.bids[0].params.adUnitId; - testbidRequest.bids[0].mediaTypes = {'native': {'nativedata': 'content parsed serverside only'}}; + testbidRequest.bids[0].mediaTypes = { 'native': { 'nativedata': 'content parsed serverside only' } }; const result = spec.buildRequests(testbidRequest.bids, testbidRequest); const data = JSON.parse(result.data); @@ -498,8 +498,8 @@ describe('Livewrapped adapter tests', function () { tid: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D' }, }, - formats: [{width: 980, height: 240}, {width: 980, height: 120}], - native: {'nativedata': 'content parsed serverside only'} + formats: [{ width: 980, height: 240 }, { width: 980, height: 120 }], + native: { 'nativedata': 'content parsed serverside only' } }] }; @@ -513,7 +513,7 @@ describe('Livewrapped adapter tests', function () { delete testbidRequest.bids[0].params.userId; delete testbidRequest.bids[0].params.seats; delete testbidRequest.bids[0].params.adUnitId; - testbidRequest.bids[0].mediaTypes = {'native': {'nativedata': 'content parsed serverside only'}, 'banner': {'sizes': [[980, 240], [980, 120]]}}; + testbidRequest.bids[0].mediaTypes = { 'native': { 'nativedata': 'content parsed serverside only' }, 'banner': { 'sizes': [[980, 240], [980, 120]] } }; const result = spec.buildRequests(testbidRequest.bids, testbidRequest); const data = JSON.parse(result.data); @@ -533,8 +533,8 @@ describe('Livewrapped adapter tests', function () { tid: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D' }, }, - formats: [{width: 980, height: 240}, {width: 980, height: 120}], - native: {'nativedata': 'content parsed serverside only'}, + formats: [{ width: 980, height: 240 }, { width: 980, height: 120 }], + native: { 'nativedata': 'content parsed serverside only' }, banner: true }] }; @@ -549,7 +549,7 @@ describe('Livewrapped adapter tests', function () { delete testbidRequest.bids[0].params.userId; delete testbidRequest.bids[0].params.seats; delete testbidRequest.bids[0].params.adUnitId; - testbidRequest.bids[0].mediaTypes = {'video': {'videodata': 'content parsed serverside only'}}; + testbidRequest.bids[0].mediaTypes = { 'video': { 'videodata': 'content parsed serverside only' } }; const result = spec.buildRequests(testbidRequest.bids, testbidRequest); const data = JSON.parse(result.data); @@ -569,8 +569,8 @@ describe('Livewrapped adapter tests', function () { tid: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D' }, }, - formats: [{width: 980, height: 240}, {width: 980, height: 120}], - video: {'videodata': 'content parsed serverside only'} + formats: [{ width: 980, height: 240 }, { width: 980, height: 120 }], + video: { 'videodata': 'content parsed serverside only' } }] }; @@ -587,10 +587,10 @@ describe('Livewrapped adapter tests', function () { const origGetConfig = config.getConfig; sandbox.stub(config, 'getConfig').callsFake(function (key) { if (key === 'app') { - return {bundle: 'bundle', domain: 'https://appdomain.com'}; + return { bundle: 'bundle', domain: 'https://appdomain.com' }; } if (key === 'device') { - return {ifa: 'ifa', w: 300, h: 200}; + return { ifa: 'ifa', w: 300, h: 200 }; } return origGetConfig.apply(config, arguments); }); @@ -605,7 +605,7 @@ describe('Livewrapped adapter tests', function () { publisherId: '26947112-2289-405D-88C1-A7340C57E63E', userId: 'user id', url: 'https://appdomain.com', - seats: {'dsp': ['seat 1']}, + seats: { 'dsp': ['seat 1'] }, version: '1.4', width: 300, height: 200, @@ -621,7 +621,7 @@ describe('Livewrapped adapter tests', function () { tid: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D' }, }, - formats: [{width: 980, height: 240}, {width: 980, height: 120}] + formats: [{ width: 980, height: 240 }, { width: 980, height: 120 }] }] }; @@ -635,7 +635,7 @@ describe('Livewrapped adapter tests', function () { delete testbidRequest.bids[0].params.userId; delete testbidRequest.bids[0].params.seats; delete testbidRequest.bids[0].params.adUnitId; - testbidRequest.bids[0].mediaTypes = {'banner': {'sizes': [[728, 90]]}}; + testbidRequest.bids[0].mediaTypes = { 'banner': { 'sizes': [[728, 90]] } }; const result = spec.buildRequests(testbidRequest.bids, testbidRequest); const data = JSON.parse(result.data); @@ -655,7 +655,7 @@ describe('Livewrapped adapter tests', function () { tid: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D' }, }, - formats: [{width: 728, height: 90}] + formats: [{ width: 728, height: 90 }] }] }; @@ -680,7 +680,7 @@ describe('Livewrapped adapter tests', function () { publisherId: '26947112-2289-405D-88C1-A7340C57E63E', userId: 'user id', url: 'https://www.domain.com', - seats: {'dsp': ['seat 1']}, + seats: { 'dsp': ['seat 1'] }, version: '1.4', width: 100, height: 100, @@ -696,7 +696,7 @@ describe('Livewrapped adapter tests', function () { tid: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D' }, }, - formats: [{width: 980, height: 240}, {width: 980, height: 120}] + formats: [{ width: 980, height: 240 }, { width: 980, height: 120 }] }] }; @@ -720,7 +720,7 @@ describe('Livewrapped adapter tests', function () { publisherId: '26947112-2289-405D-88C1-A7340C57E63E', userId: 'user id', url: 'https://www.domain.com', - seats: {'dsp': ['seat 1']}, + seats: { 'dsp': ['seat 1'] }, version: '1.4', width: 100, height: 100, @@ -735,7 +735,7 @@ describe('Livewrapped adapter tests', function () { tid: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D' }, }, - formats: [{width: 980, height: 240}, {width: 980, height: 120}] + formats: [{ width: 980, height: 240 }, { width: 980, height: 120 }] }] }; @@ -757,7 +757,7 @@ describe('Livewrapped adapter tests', function () { publisherId: '26947112-2289-405D-88C1-A7340C57E63E', userId: 'user id', url: 'https://www.domain.com', - seats: {'dsp': ['seat 1']}, + seats: { 'dsp': ['seat 1'] }, version: '1.4', width: 100, height: 100, @@ -772,7 +772,7 @@ describe('Livewrapped adapter tests', function () { tid: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D' }, }, - formats: [{width: 980, height: 240}, {width: 980, height: 120}] + formats: [{ width: 980, height: 240 }, { width: 980, height: 120 }] }] }; @@ -801,7 +801,7 @@ describe('Livewrapped adapter tests', function () { publisherId: '26947112-2289-405D-88C1-A7340C57E63E', userId: 'user id', url: 'https://www.domain.com', - seats: {'dsp': ['seat 1']}, + seats: { 'dsp': ['seat 1'] }, version: '1.4', width: 100, height: 100, @@ -816,7 +816,7 @@ describe('Livewrapped adapter tests', function () { tid: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D' }, }, - formats: [{width: 980, height: 240}, {width: 980, height: 120}] + formats: [{ width: 980, height: 240 }, { width: 980, height: 120 }] }] }; @@ -836,7 +836,7 @@ describe('Livewrapped adapter tests', function () { publisherId: '26947112-2289-405D-88C1-A7340C57E63E', userId: 'user id', url: 'https://www.domain.com', - seats: {'dsp': ['seat 1']}, + seats: { 'dsp': ['seat 1'] }, version: '1.4', width: 100, height: 100, @@ -850,7 +850,7 @@ describe('Livewrapped adapter tests', function () { tid: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D' }, }, - formats: [{width: 980, height: 240}, {width: 980, height: 120}] + formats: [{ width: 980, height: 240 }, { width: 980, height: 120 }] }] }; @@ -870,7 +870,7 @@ describe('Livewrapped adapter tests', function () { publisherId: '26947112-2289-405D-88C1-A7340C57E63E', userId: 'user id', url: 'https://www.domain.com', - seats: {'dsp': ['seat 1']}, + seats: { 'dsp': ['seat 1'] }, version: '1.4', width: 100, height: 100, @@ -884,7 +884,7 @@ describe('Livewrapped adapter tests', function () { tid: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D' }, }, - formats: [{width: 980, height: 240}, {width: 980, height: 120}] + formats: [{ width: 980, height: 240 }, { width: 980, height: 120 }] }] }; @@ -893,7 +893,7 @@ describe('Livewrapped adapter tests', function () { it('should use params.url, then bidderRequest.refererInfo.page', function() { const testRequest = clone(bidderRequest); - testRequest.refererInfo = {page: 'https://www.topurl.com'}; + testRequest.refererInfo = { page: 'https://www.topurl.com' }; let result = spec.buildRequests(testRequest.bids, testRequest); let data = JSON.parse(result.data); @@ -913,7 +913,7 @@ describe('Livewrapped adapter tests', function () { sandbox.stub(storage, 'cookiesAreEnabled').callsFake(() => true); const testbidRequest = clone(bidderRequest); delete testbidRequest.bids[0].params.userId; - testbidRequest.bids[0].crumbs = {pubcid: 'pubcid 123'}; + testbidRequest.bids[0].crumbs = { pubcid: 'pubcid 123' }; const result = spec.buildRequests(testbidRequest.bids, testbidRequest); const data = JSON.parse(result.data); @@ -924,7 +924,7 @@ describe('Livewrapped adapter tests', function () { publisherId: '26947112-2289-405D-88C1-A7340C57E63E', userId: 'pubcid 123', url: 'https://www.domain.com', - seats: {'dsp': ['seat 1']}, + seats: { 'dsp': ['seat 1'] }, version: '1.4', width: 100, height: 100, @@ -938,7 +938,7 @@ describe('Livewrapped adapter tests', function () { tid: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D' }, }, - formats: [{width: 980, height: 240}, {width: 980, height: 120}] + formats: [{ width: 980, height: 240 }, { width: 980, height: 120 }] }] }; @@ -949,7 +949,7 @@ describe('Livewrapped adapter tests', function () { sandbox.stub(utils, 'isSafariBrowser').callsFake(() => false); sandbox.stub(storage, 'cookiesAreEnabled').callsFake(() => true); const testbidRequest = clone(bidderRequest); - testbidRequest.bids[0].crumbs = {pubcid: 'pubcid 123'}; + testbidRequest.bids[0].crumbs = { pubcid: 'pubcid 123' }; const result = spec.buildRequests(testbidRequest.bids, testbidRequest); const data = JSON.parse(result.data); @@ -960,7 +960,7 @@ describe('Livewrapped adapter tests', function () { publisherId: '26947112-2289-405D-88C1-A7340C57E63E', userId: 'user id', url: 'https://www.domain.com', - seats: {'dsp': ['seat 1']}, + seats: { 'dsp': ['seat 1'] }, version: '1.4', width: 100, height: 100, @@ -974,7 +974,7 @@ describe('Livewrapped adapter tests', function () { tid: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D' }, }, - formats: [{width: 980, height: 240}, {width: 980, height: 120}] + formats: [{ width: 980, height: 240 }, { width: 980, height: 120 }] }] }; @@ -998,7 +998,7 @@ describe('Livewrapped adapter tests', function () { publisherId: '26947112-2289-405D-88C1-A7340C57E63E', userId: 'user id', url: 'https://www.domain.com', - seats: {'dsp': ['seat 1']}, + seats: { 'dsp': ['seat 1'] }, version: '1.4', width: getWinDimensions().innerWidth, height: getWinDimensions().innerHeight, @@ -1012,7 +1012,7 @@ describe('Livewrapped adapter tests', function () { tid: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D' }, }, - formats: [{width: 980, height: 240}, {width: 980, height: 120}] + formats: [{ width: 980, height: 240 }, { width: 980, height: 120 }] }] }; @@ -1036,7 +1036,7 @@ describe('Livewrapped adapter tests', function () { publisherId: '26947112-2289-405D-88C1-A7340C57E63E', userId: 'user id', url: 'https://www.domain.com', - seats: {'dsp': ['seat 1']}, + seats: { 'dsp': ['seat 1'] }, version: '1.4', width: 100, height: 100, @@ -1050,7 +1050,7 @@ describe('Livewrapped adapter tests', function () { tid: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D' }, }, - formats: [{width: 980, height: 240}, {width: 980, height: 120}] + formats: [{ width: 980, height: 240 }, { width: 980, height: 120 }] }] }; @@ -1076,7 +1076,7 @@ describe('Livewrapped adapter tests', function () { publisherId: '26947112-2289-405D-88C1-A7340C57E63E', userId: 'user id', url: 'https://www.domain.com', - seats: {'dsp': ['seat 1']}, + seats: { 'dsp': ['seat 1'] }, version: '1.4', width: 100, height: 100, @@ -1090,7 +1090,7 @@ describe('Livewrapped adapter tests', function () { tid: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D' }, }, - formats: [{width: 980, height: 240}, {width: 980, height: 120}] + formats: [{ width: 980, height: 240 }, { width: 980, height: 120 }] }] }; @@ -1116,7 +1116,7 @@ describe('Livewrapped adapter tests', function () { publisherId: '26947112-2289-405D-88C1-A7340C57E63E', userId: 'user id', url: 'https://www.domain.com', - seats: {'dsp': ['seat 1']}, + seats: { 'dsp': ['seat 1'] }, version: '1.4', width: 100, height: 100, @@ -1130,7 +1130,7 @@ describe('Livewrapped adapter tests', function () { tid: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D' }, }, - formats: [{width: 980, height: 240}, {width: 980, height: 120}] + formats: [{ width: 980, height: 240 }, { width: 980, height: 120 }] }] }; @@ -1156,7 +1156,7 @@ describe('Livewrapped adapter tests', function () { publisherId: '26947112-2289-405D-88C1-A7340C57E63E', userId: 'user id', url: 'https://www.domain.com', - seats: {'dsp': ['seat 1']}, + seats: { 'dsp': ['seat 1'] }, version: '1.4', width: 100, height: 100, @@ -1170,7 +1170,7 @@ describe('Livewrapped adapter tests', function () { tid: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D' }, }, - formats: [{width: 980, height: 240}, {width: 980, height: 120}] + formats: [{ width: 980, height: 240 }, { width: 980, height: 120 }] }] }; @@ -1217,7 +1217,7 @@ describe('Livewrapped adapter tests', function () { publisherId: '26947112-2289-405D-88C1-A7340C57E63E', userId: 'user id', url: 'https://www.domain.com', - seats: {'dsp': ['seat 1']}, + seats: { 'dsp': ['seat 1'] }, version: '1.4', width: 100, height: 100, @@ -1232,7 +1232,7 @@ describe('Livewrapped adapter tests', function () { tid: '3D1C8CF7-D288-4D7F-8ADD-97C553056C3D' }, }, - formats: [{width: 980, height: 240}, {width: 980, height: 120}], + formats: [{ width: 980, height: 240 }, { width: 980, height: 120 }], flr: 10 }] }; @@ -1276,9 +1276,9 @@ describe('Livewrapped adapter tests', function () { sandbox.stub(utils, 'isSafariBrowser').callsFake(() => false); sandbox.stub(storage, 'cookiesAreEnabled').callsFake(() => true); - const ortb2 = {user: {ext: {prop: 'value'}}}; + const ortb2 = { user: { ext: { prop: 'value' } } }; - const testbidRequest = {...clone(bidderRequest), ortb2}; + const testbidRequest = { ...clone(bidderRequest), ortb2 }; delete testbidRequest.bids[0].params.userId; testbidRequest.bids[0].userIdAsEids = [ { @@ -1292,10 +1292,10 @@ describe('Livewrapped adapter tests', function () { const result = spec.buildRequests(testbidRequest.bids, testbidRequest); const data = JSON.parse(result.data); - var expected = {user: {ext: {prop: 'value', eids: testbidRequest.bids[0].userIdAsEids}}} + var expected = { user: { ext: { prop: 'value', eids: testbidRequest.bids[0].userIdAsEids } } } expect(data.rtbData).to.deep.equal(expected); - expect(ortb2).to.deep.equal({user: {ext: {prop: 'value'}}}); + expect(ortb2).to.deep.equal({ user: { ext: { prop: 'value' } } }); }); it('should send schain object if available', function() { @@ -1360,7 +1360,7 @@ describe('Livewrapped adapter tests', function () { meta: undefined }]; - const bids = spec.interpretResponse({body: lwResponse}); + const bids = spec.interpretResponse({ body: lwResponse }); expect(bids).to.deep.equal(expectedResponse); }) @@ -1399,7 +1399,7 @@ describe('Livewrapped adapter tests', function () { meta: { dealId: "deal id", bidder: "bidder" } }]; - const bids = spec.interpretResponse({body: lwResponse}); + const bids = spec.interpretResponse({ body: lwResponse }); expect(bids).to.deep.equal(expectedResponse); }) @@ -1439,7 +1439,7 @@ describe('Livewrapped adapter tests', function () { bidderCode: "bidder" }]; - const bids = spec.interpretResponse({body: lwResponse}); + const bids = spec.interpretResponse({ body: lwResponse }); expect(bids).to.deep.equal(expectedResponse); }) @@ -1457,7 +1457,7 @@ describe('Livewrapped adapter tests', function () { bidId: '32e50fad901ae89', auctionId: '13e674db-d4d8-4e19-9d28-ff38177db8bf', creativeId: '52cbd598-2715-4c43-a06f-229fc170f945:427077', - native: {'native': 'native'}, + native: { 'native': 'native' }, ttl: 120, meta: undefined } @@ -1476,11 +1476,11 @@ describe('Livewrapped adapter tests', function () { netRevenue: true, currency: 'USD', meta: undefined, - native: {'native': 'native'}, + native: { 'native': 'native' }, mediaType: NATIVE }]; - const bids = spec.interpretResponse({body: lwResponse}); + const bids = spec.interpretResponse({ body: lwResponse }); expect(bids).to.deep.equal(expectedResponse); }) @@ -1521,7 +1521,7 @@ describe('Livewrapped adapter tests', function () { mediaType: VIDEO }]; - const bids = spec.interpretResponse({body: lwResponse}); + const bids = spec.interpretResponse({ body: lwResponse }); expect(bids).to.deep.equal(expectedResponse); }) @@ -1583,7 +1583,7 @@ describe('Livewrapped adapter tests', function () { meta: undefined }]; - const bids = spec.interpretResponse({body: lwResponse}); + const bids = spec.interpretResponse({ body: lwResponse }); expect(bids).to.deep.equal(expectedResponse); }) @@ -1602,7 +1602,7 @@ describe('Livewrapped adapter tests', function () { auctionId: '13e674db-d4d8-4e19-9d28-ff38177db8bf', creativeId: '52cbd598-2715-4c43-a06f-229fc170f945:427077', ttl: 120, - meta: {metadata: 'metadata'} + meta: { metadata: 'metadata' } } ], currency: 'USD' @@ -1618,10 +1618,10 @@ describe('Livewrapped adapter tests', function () { creativeId: '52cbd598-2715-4c43-a06f-229fc170f945:427077', netRevenue: true, currency: 'USD', - meta: {metadata: 'metadata'} + meta: { metadata: 'metadata' } }]; - const bids = spec.interpretResponse({body: lwResponse}); + const bids = spec.interpretResponse({ body: lwResponse }); expect(bids).to.deep.equal(expectedResponse); }) @@ -1654,7 +1654,7 @@ describe('Livewrapped adapter tests', function () { } }; - spec.interpretResponse({body: lwResponse}); + spec.interpretResponse({ body: lwResponse }); expect(debugData).to.equal(lwResponse.dbg); }) @@ -1667,8 +1667,8 @@ describe('Livewrapped adapter tests', function () { serverResponses = [{ body: { pixels: [ - {type: 'Redirect', url: 'https://pixelsync'}, - {type: 'Iframe', url: 'https://iframesync'} + { type: 'Redirect', url: 'https://pixelsync' }, + { type: 'Iframe', url: 'https://iframesync' } ] } }]; @@ -1689,7 +1689,7 @@ describe('Livewrapped adapter tests', function () { const syncs = spec.getUserSyncs({ pixelEnabled: true, iframeEnabled: true - }, [{body: {}}]); + }, [{ body: {} }]); const expectedResponse = []; @@ -1702,7 +1702,7 @@ describe('Livewrapped adapter tests', function () { iframeEnabled: true }, serverResponses); - const expectedResponse = [{type: 'image', url: 'https://pixelsync'}, {type: 'iframe', url: 'https://iframesync'}]; + const expectedResponse = [{ type: 'image', url: 'https://pixelsync' }, { type: 'iframe', url: 'https://iframesync' }]; expect(syncs).to.deep.equal(expectedResponse) }); @@ -1713,7 +1713,7 @@ describe('Livewrapped adapter tests', function () { iframeEnabled: false }, serverResponses); - const expectedResponse = [{type: 'image', url: 'https://pixelsync'}]; + const expectedResponse = [{ type: 'image', url: 'https://pixelsync' }]; expect(syncs).to.deep.equal(expectedResponse) }); @@ -1724,7 +1724,7 @@ describe('Livewrapped adapter tests', function () { iframeEnabled: true }, serverResponses); - const expectedResponse = [{type: 'iframe', url: 'https://iframesync'}]; + const expectedResponse = [{ type: 'iframe', url: 'https://iframesync' }]; expect(syncs).to.deep.equal(expectedResponse) }); diff --git a/test/spec/modules/lm_kiviadsBidAdapter_spec.js b/test/spec/modules/lm_kiviadsBidAdapter_spec.js index 32b8d56309b..0ed1fb9793d 100644 --- a/test/spec/modules/lm_kiviadsBidAdapter_spec.js +++ b/test/spec/modules/lm_kiviadsBidAdapter_spec.js @@ -1,8 +1,8 @@ -import {expect} from 'chai'; -import {config} from 'src/config.js'; -import {spec} from 'modules/lm_kiviadsBidAdapter.js'; -import {deepClone} from 'src/utils'; -import {getBidFloor} from '../../../libraries/xeUtils/bidderUtils.js'; +import { expect } from 'chai'; +import { config } from 'src/config.js'; +import { spec } from 'modules/lm_kiviadsBidAdapter.js'; +import { deepClone } from 'src/utils'; +import { getBidFloor } from '../../../libraries/xeUtils/bidderUtils.js'; const ENDPOINT = 'https://pbjs.kiviads.live'; @@ -99,7 +99,7 @@ describe('lm_kiviadsBidAdapter', () => { expect(request).to.have.property('tz').and.to.equal(new Date().getTimezoneOffset()); expect(request).to.have.property('bc').and.to.equal(1); expect(request).to.have.property('floor').and.to.equal(null); - expect(request).to.have.property('banner').and.to.deep.equal({sizes: [[300, 250], [300, 200]]}); + expect(request).to.have.property('banner').and.to.deep.equal({ sizes: [[300, 250], [300, 200]] }); expect(request).to.have.property('gdprConsent').and.to.deep.equal({}); expect(request).to.have.property('userEids').and.to.deep.equal([]); expect(request).to.have.property('usPrivacy').and.to.equal(''); @@ -192,7 +192,7 @@ describe('lm_kiviadsBidAdapter', () => { it('should build request with valid bidfloor', function () { const bfRequest = deepClone(defaultRequest); - bfRequest.getFloor = () => ({floor: 5, currency: 'USD'}); + bfRequest.getFloor = () => ({ floor: 5, currency: 'USD' }); const request = JSON.parse(spec.buildRequests([bfRequest], {}).data)[0]; expect(request).to.have.property('floor').and.to.equal(5); }); @@ -208,8 +208,8 @@ describe('lm_kiviadsBidAdapter', () => { it('should build request with extended ids', function () { const idRequest = deepClone(defaultRequest); idRequest.userIdAsEids = [ - {source: 'adserver.org', uids: [{id: 'TTD_ID_FROM_USER_ID_MODULE', atype: 1, ext: {rtiPartner: 'TDID'}}]}, - {source: 'pubcid.org', uids: [{id: 'pubCommonId_FROM_USER_ID_MODULE', atype: 1}]} + { source: 'adserver.org', uids: [{ id: 'TTD_ID_FROM_USER_ID_MODULE', atype: 1, ext: { rtiPartner: 'TDID' } }] }, + { source: 'pubcid.org', uids: [{ id: 'pubCommonId_FROM_USER_ID_MODULE', atype: 1 }] } ]; const request = JSON.parse(spec.buildRequests([idRequest], {}).data)[0]; expect(request).to.have.property('userEids').and.deep.equal(idRequest.userIdAsEids); @@ -261,7 +261,7 @@ describe('lm_kiviadsBidAdapter', () => { } }; - const validResponse = spec.interpretResponse(serverResponse, {bidderRequest: defaultRequest}); + const validResponse = spec.interpretResponse(serverResponse, { bidderRequest: defaultRequest }); const bid = validResponse[0]; expect(validResponse).to.be.an('array').that.is.not.empty; expect(bid.requestId).to.equal('qwerty'); @@ -270,7 +270,7 @@ describe('lm_kiviadsBidAdapter', () => { expect(bid.width).to.equal(300); expect(bid.height).to.equal(250); expect(bid.ttl).to.equal(600); - expect(bid.meta).to.deep.equal({advertiserDomains: ['lm_kiviads']}); + expect(bid.meta).to.deep.equal({ advertiserDomains: ['lm_kiviads'] }); }); it('should interpret valid banner response', function () { @@ -291,7 +291,7 @@ describe('lm_kiviadsBidAdapter', () => { } }; - const validResponseBanner = spec.interpretResponse(serverResponse, {bidderRequest: defaultRequest}); + const validResponseBanner = spec.interpretResponse(serverResponse, { bidderRequest: defaultRequest }); const bid = validResponseBanner[0]; expect(validResponseBanner).to.be.an('array').that.is.not.empty; expect(bid.mediaType).to.equal('banner'); @@ -317,7 +317,7 @@ describe('lm_kiviadsBidAdapter', () => { } }; - const validResponseBanner = spec.interpretResponse(serverResponse, {bidderRequest: defaultRequestVideo}); + const validResponseBanner = spec.interpretResponse(serverResponse, { bidderRequest: defaultRequestVideo }); const bid = validResponseBanner[0]; expect(validResponseBanner).to.be.an('array').that.is.not.empty; expect(bid.mediaType).to.equal('video'); @@ -333,12 +333,12 @@ describe('lm_kiviadsBidAdapter', () => { }); it('should return empty if sync is not allowed', function () { - const opts = spec.getUserSyncs({iframeEnabled: false, pixelEnabled: false}); + const opts = spec.getUserSyncs({ iframeEnabled: false, pixelEnabled: false }); expect(opts).to.be.an('array').that.is.empty; }); it('should allow iframe sync', function () { - const opts = spec.getUserSyncs({iframeEnabled: true, pixelEnabled: false}, [{ + const opts = spec.getUserSyncs({ iframeEnabled: true, pixelEnabled: false }, [{ body: { data: [{ requestId: 'qwerty', @@ -357,7 +357,7 @@ describe('lm_kiviadsBidAdapter', () => { }); it('should allow pixel sync', function () { - const opts = spec.getUserSyncs({iframeEnabled: false, pixelEnabled: true}, [{ + const opts = spec.getUserSyncs({ iframeEnabled: false, pixelEnabled: true }, [{ body: { data: [{ requestId: 'qwerty', @@ -376,7 +376,7 @@ describe('lm_kiviadsBidAdapter', () => { }); it('should allow pixel sync and parse consent params', function () { - const opts = spec.getUserSyncs({iframeEnabled: false, pixelEnabled: true}, [{ + const opts = spec.getUserSyncs({ iframeEnabled: false, pixelEnabled: true }, [{ body: { data: [{ requestId: 'qwerty', @@ -400,20 +400,20 @@ describe('lm_kiviadsBidAdapter', () => { describe('getBidFloor', function () { it('should return null when getFloor is not a function', () => { - const bid = {getFloor: 2}; + const bid = { getFloor: 2 }; const result = getBidFloor(bid); expect(result).to.be.null; }); it('should return null when getFloor doesnt return an object', () => { - const bid = {getFloor: () => 2}; + const bid = { getFloor: () => 2 }; const result = getBidFloor(bid); expect(result).to.be.null; }); it('should return null when floor is not a number', () => { const bid = { - getFloor: () => ({floor: 'string', currency: 'USD'}) + getFloor: () => ({ floor: 'string', currency: 'USD' }) }; const result = getBidFloor(bid); expect(result).to.be.null; @@ -421,7 +421,7 @@ describe('lm_kiviadsBidAdapter', () => { it('should return null when currency is not USD', () => { const bid = { - getFloor: () => ({floor: 5, currency: 'EUR'}) + getFloor: () => ({ floor: 5, currency: 'EUR' }) }; const result = getBidFloor(bid); expect(result).to.be.null; @@ -429,7 +429,7 @@ describe('lm_kiviadsBidAdapter', () => { it('should return floor value when everything is correct', () => { const bid = { - getFloor: () => ({floor: 5, currency: 'USD'}) + getFloor: () => ({ floor: 5, currency: 'USD' }) }; const result = getBidFloor(bid); expect(result).to.equal(5); diff --git a/test/spec/modules/lmpIdSystem_spec.js b/test/spec/modules/lmpIdSystem_spec.js index cf525121fad..d7f3591c6be 100644 --- a/test/spec/modules/lmpIdSystem_spec.js +++ b/test/spec/modules/lmpIdSystem_spec.js @@ -1,5 +1,5 @@ -import {expect} from 'chai'; -import {lmpIdSubmodule, storage} from 'modules/lmpIdSystem.js'; +import { expect } from 'chai'; +import { lmpIdSubmodule, storage } from 'modules/lmpIdSystem.js'; describe('LMPID System', () => { let getDataFromLocalStorageStub, localStorageIsEnabledStub; diff --git a/test/spec/modules/locIdSystem_spec.js b/test/spec/modules/locIdSystem_spec.js new file mode 100644 index 00000000000..1b2fc02ddc8 --- /dev/null +++ b/test/spec/modules/locIdSystem_spec.js @@ -0,0 +1,2090 @@ +/** + * This file is licensed under the Apache 2.0 license. + * http://www.apache.org/licenses/LICENSE-2.0 + */ + +import { locIdSubmodule, storage } from 'modules/locIdSystem.js'; +import { createEidsArray } from 'modules/userId/eids.js'; +import { attachIdSystem } from 'modules/userId/index.js'; +import * as ajax from 'src/ajax.js'; +import { VENDORLESS_GVLID } from 'src/consentHandler.js'; +import { uspDataHandler, gppDataHandler } from 'src/adapterManager.js'; +import { expect } from 'chai/index.mjs'; +import sinon from 'sinon'; + +const TEST_ID = 'SYybozbTuRaZkgGqCD7L7EE0FncoNUcx-om4xTfhJt36TFIAES2tF1qPH'; +const TEST_ENDPOINT = 'https://id.example.com/locid'; +const TEST_CONNECTION_IP = '203.0.113.42'; + +describe('LocID System', () => { + let sandbox; + let ajaxStub; + let ajaxBuilderStub; + + beforeEach(() => { + sandbox = sinon.createSandbox(); + sandbox.stub(uspDataHandler, 'getConsentData').returns(null); + sandbox.stub(gppDataHandler, 'getConsentData').returns(null); + sandbox.stub(storage, 'getDataFromLocalStorage').returns(null); + sandbox.stub(storage, 'setDataInLocalStorage'); + ajaxStub = sandbox.stub(); + ajaxBuilderStub = sandbox.stub(ajax, 'ajaxBuilder').returns(ajaxStub); + }); + + afterEach(() => { + sandbox.restore(); + }); + + describe('module properties', () => { + it('should expose correct module name', () => { + expect(locIdSubmodule.name).to.equal('locId'); + }); + + it('should have eids configuration with correct defaults', () => { + expect(locIdSubmodule.eids).to.be.an('object'); + expect(locIdSubmodule.eids.locId).to.be.an('object'); + expect(locIdSubmodule.eids.locId.source).to.equal('locid.com'); + // atype 1 = AdCOM AgentTypeWeb + expect(locIdSubmodule.eids.locId.atype).to.equal(1); + }); + + it('should register as a vendorless TCF module', () => { + expect(locIdSubmodule.gvlid).to.equal(VENDORLESS_GVLID); + }); + + it('should have getValue function that extracts ID', () => { + const getValue = locIdSubmodule.eids.locId.getValue; + expect(getValue('test-id')).to.equal('test-id'); + expect(getValue({ id: 'id-shape' })).to.equal('id-shape'); + expect(getValue({ locId: 'test-id' })).to.equal('test-id'); + expect(getValue({ locid: 'legacy-id' })).to.equal('legacy-id'); + }); + }); + + describe('decode', () => { + it('should decode valid ID correctly', () => { + const result = locIdSubmodule.decode({ id: TEST_ID, connectionIp: TEST_CONNECTION_IP }); + expect(result).to.deep.equal({ locId: TEST_ID }); + }); + + it('should decode ID passed as object', () => { + const result = locIdSubmodule.decode({ id: TEST_ID, connectionIp: TEST_CONNECTION_IP }); + expect(result).to.deep.equal({ locId: TEST_ID }); + }); + + it('should return undefined for invalid values', () => { + [null, undefined, '', {}, [], 123, TEST_ID].forEach(value => { + expect(locIdSubmodule.decode(value)).to.be.undefined; + }); + }); + + it('should return undefined when connection_ip is missing', () => { + expect(locIdSubmodule.decode({ id: TEST_ID })).to.be.undefined; + }); + + it('should return undefined for IDs exceeding max length', () => { + const longId = 'a'.repeat(513); + expect(locIdSubmodule.decode({ id: longId, connectionIp: TEST_CONNECTION_IP })).to.be.undefined; + }); + + it('should accept ID at exactly MAX_ID_LENGTH (512 characters)', () => { + const maxLengthId = 'a'.repeat(512); + const result = locIdSubmodule.decode({ id: maxLengthId, connectionIp: TEST_CONNECTION_IP }); + expect(result).to.deep.equal({ locId: maxLengthId }); + }); + }); + + describe('getId', () => { + it('should return callback for async endpoint fetch', () => { + const config = { + params: { + endpoint: TEST_ENDPOINT + } + }; + const result = locIdSubmodule.getId(config, {}); + expect(result).to.have.property('callback'); + expect(result.callback).to.be.a('function'); + }); + + it('should call endpoint and return tx_cloc on success', (done) => { + ajaxStub.callsFake((url, callbacks, body, options) => { + callbacks.success(JSON.stringify({ tx_cloc: TEST_ID, connection_ip: TEST_CONNECTION_IP })); + }); + + const config = { + params: { + endpoint: TEST_ENDPOINT + } + }; + + const result = locIdSubmodule.getId(config, {}); + result.callback((id) => { + expect(id).to.be.an('object'); + expect(id.id).to.equal(TEST_ID); + expect(id.connectionIp).to.equal(TEST_CONNECTION_IP); + expect(ajaxStub.calledOnce).to.be.true; + done(); + }); + }); + + it('should cache entry with null id when tx_cloc is missing but connection_ip is present', (done) => { + ajaxStub.callsFake((url, callbacks, body, options) => { + callbacks.success(JSON.stringify({ stable_cloc: 'stable-cloc-id-12345', connection_ip: TEST_CONNECTION_IP })); + }); + + const config = { + params: { + endpoint: TEST_ENDPOINT + } + }; + + const result = locIdSubmodule.getId(config, {}); + result.callback((id) => { + expect(id).to.be.an('object'); + expect(id.id).to.be.null; + expect(id.connectionIp).to.equal(TEST_CONNECTION_IP); + done(); + }); + }); + + it('should prefer tx_cloc over stable_cloc when both present', (done) => { + ajaxStub.callsFake((url, callbacks, body, options) => { + callbacks.success(JSON.stringify({ + tx_cloc: TEST_ID, + stable_cloc: 'stable-fallback', + connection_ip: TEST_CONNECTION_IP + })); + }); + + const config = { + params: { + endpoint: TEST_ENDPOINT + } + }; + + const result = locIdSubmodule.getId(config, {}); + result.callback((id) => { + expect(id).to.be.an('object'); + expect(id.id).to.equal(TEST_ID); + expect(id.connectionIp).to.equal(TEST_CONNECTION_IP); + done(); + }); + }); + + it('should return undefined on endpoint error', (done) => { + ajaxStub.callsFake((url, callbacks, body, options) => { + callbacks.error('Network error'); + }); + + const config = { + params: { + endpoint: TEST_ENDPOINT + } + }; + + const result = locIdSubmodule.getId(config, {}); + result.callback((id) => { + expect(id).to.be.undefined; + done(); + }); + }); + + it('should reuse storedId when valid and IP cache matches', () => { + // Set up IP cache matching stored entry's IP + storage.getDataFromLocalStorage.returns(JSON.stringify({ + ip: TEST_CONNECTION_IP, + fetchedAt: Date.now(), + expiresAt: Date.now() + 1000 + })); + + const config = { + params: { + endpoint: TEST_ENDPOINT + }, + storage: { + name: '_locid' + } + }; + const storedId = { + id: 'existing-id', + connectionIp: TEST_CONNECTION_IP, + createdAt: Date.now(), + updatedAt: Date.now(), + expiresAt: Date.now() + 1000 + }; + const result = locIdSubmodule.getId(config, {}, storedId); + expect(result).to.deep.equal({ id: storedId }); + expect(ajaxStub.called).to.be.false; + }); + + it('should not reuse storedId when expired', () => { + const config = { + params: { + endpoint: TEST_ENDPOINT + } + }; + const storedId = { + id: 'expired-id', + connectionIp: TEST_CONNECTION_IP, + createdAt: 1000, + updatedAt: 1000, + expiresAt: Date.now() - 1000 + }; + const result = locIdSubmodule.getId(config, {}, storedId); + expect(result).to.have.property('callback'); + }); + + it('should not reuse storedId when connectionIp is missing', () => { + const config = { + params: { + endpoint: TEST_ENDPOINT + } + }; + const storedId = { + id: 'existing-id', + createdAt: 1000, + updatedAt: 1000, + expiresAt: 2000 + }; + const result = locIdSubmodule.getId(config, {}, storedId); + expect(result).to.have.property('callback'); + }); + + it('should pass x-api-key header when apiKey is configured', (done) => { + ajaxStub.callsFake((url, callbacks, body, options) => { + expect(options.customHeaders).to.deep.equal({ 'x-api-key': 'test-api-key' }); + callbacks.success(JSON.stringify({ tx_cloc: TEST_ID, connection_ip: TEST_CONNECTION_IP })); + }); + + const config = { + params: { + endpoint: TEST_ENDPOINT, + apiKey: 'test-api-key' + } + }; + + const result = locIdSubmodule.getId(config, {}); + result.callback(() => done()); + }); + + it('should not include customHeaders when apiKey is not configured', (done) => { + ajaxStub.callsFake((url, callbacks, body, options) => { + expect(options.customHeaders).to.not.exist; + callbacks.success(JSON.stringify({ tx_cloc: TEST_ID, connection_ip: TEST_CONNECTION_IP })); + }); + + const config = { + params: { + endpoint: TEST_ENDPOINT + } + }; + + const result = locIdSubmodule.getId(config, {}); + result.callback(() => done()); + }); + + it('should pass withCredentials when configured', (done) => { + ajaxStub.callsFake((url, callbacks, body, options) => { + expect(options.withCredentials).to.be.true; + callbacks.success(JSON.stringify({ tx_cloc: TEST_ID, connection_ip: TEST_CONNECTION_IP })); + }); + + const config = { + params: { + endpoint: TEST_ENDPOINT, + withCredentials: true + } + }; + + const result = locIdSubmodule.getId(config, {}); + result.callback(() => done()); + }); + + it('should default withCredentials to false', (done) => { + ajaxStub.callsFake((url, callbacks, body, options) => { + expect(options.withCredentials).to.be.false; + callbacks.success(JSON.stringify({ tx_cloc: TEST_ID, connection_ip: TEST_CONNECTION_IP })); + }); + + const config = { + params: { + endpoint: TEST_ENDPOINT + } + }; + + const result = locIdSubmodule.getId(config, {}); + result.callback(() => done()); + }); + + it('should use default timeout of 800ms when timeoutMs is not configured', (done) => { + ajaxStub.callsFake((url, callbacks, body, options) => { + callbacks.success(JSON.stringify({ tx_cloc: TEST_ID, connection_ip: TEST_CONNECTION_IP })); + }); + + const config = { + params: { + endpoint: TEST_ENDPOINT + } + }; + + const result = locIdSubmodule.getId(config, {}); + result.callback(() => { + expect(ajaxBuilderStub.calledWith(800)).to.be.true; + done(); + }); + }); + + it('should use custom timeout when timeoutMs is configured', (done) => { + ajaxStub.callsFake((url, callbacks, body, options) => { + callbacks.success(JSON.stringify({ tx_cloc: TEST_ID, connection_ip: TEST_CONNECTION_IP })); + }); + + const config = { + params: { + endpoint: TEST_ENDPOINT, + timeoutMs: 1500 + } + }; + + const result = locIdSubmodule.getId(config, {}); + result.callback(() => { + expect(ajaxBuilderStub.calledWith(1500)).to.be.true; + done(); + }); + }); + + it('should always use GET method', (done) => { + ajaxStub.callsFake((url, callbacks, body, options) => { + expect(options.method).to.equal('GET'); + expect(body).to.be.null; + callbacks.success(JSON.stringify({ tx_cloc: TEST_ID, connection_ip: TEST_CONNECTION_IP })); + }); + + const config = { + params: { + endpoint: TEST_ENDPOINT + } + }; + + const result = locIdSubmodule.getId(config, {}); + result.callback(() => done()); + }); + + it('should append alt_id query parameter when altId is configured', (done) => { + ajaxStub.callsFake((url, callbacks, body, options) => { + expect(url).to.equal(TEST_ENDPOINT + '?alt_id=user123'); + callbacks.success(JSON.stringify({ tx_cloc: TEST_ID, connection_ip: TEST_CONNECTION_IP })); + }); + + const config = { + params: { + endpoint: TEST_ENDPOINT, + altId: 'user123' + } + }; + + const result = locIdSubmodule.getId(config, {}); + result.callback(() => done()); + }); + + it('should use & separator when endpoint already has query params', (done) => { + ajaxStub.callsFake((url, callbacks, body, options) => { + expect(url).to.equal(TEST_ENDPOINT + '?existing=param&alt_id=user456'); + callbacks.success(JSON.stringify({ tx_cloc: TEST_ID, connection_ip: TEST_CONNECTION_IP })); + }); + + const config = { + params: { + endpoint: TEST_ENDPOINT + '?existing=param', + altId: 'user456' + } + }; + + const result = locIdSubmodule.getId(config, {}); + result.callback(() => done()); + }); + + it('should URL-encode altId value', (done) => { + ajaxStub.callsFake((url, callbacks, body, options) => { + expect(url).to.equal(TEST_ENDPOINT + '?alt_id=user%40example.com'); + callbacks.success(JSON.stringify({ tx_cloc: TEST_ID, connection_ip: TEST_CONNECTION_IP })); + }); + + const config = { + params: { + endpoint: TEST_ENDPOINT, + altId: 'user@example.com' + } + }; + + const result = locIdSubmodule.getId(config, {}); + result.callback(() => done()); + }); + + it('should not append alt_id when altId is not configured', (done) => { + ajaxStub.callsFake((url, callbacks, body, options) => { + expect(url).to.equal(TEST_ENDPOINT); + callbacks.success(JSON.stringify({ tx_cloc: TEST_ID, connection_ip: TEST_CONNECTION_IP })); + }); + + const config = { + params: { + endpoint: TEST_ENDPOINT + } + }; + + const result = locIdSubmodule.getId(config, {}); + result.callback(() => done()); + }); + + it('should preserve URL fragment when appending alt_id', (done) => { + ajaxStub.callsFake((url, callbacks, body, options) => { + expect(url).to.equal('https://id.example.com/locid?alt_id=user123#frag'); + callbacks.success(JSON.stringify({ tx_cloc: TEST_ID, connection_ip: TEST_CONNECTION_IP })); + }); + + const config = { + params: { + endpoint: 'https://id.example.com/locid#frag', + altId: 'user123' + } + }; + + const result = locIdSubmodule.getId(config, {}); + result.callback(() => done()); + }); + + it('should preserve URL fragment when endpoint has existing query params', (done) => { + ajaxStub.callsFake((url, callbacks, body, options) => { + expect(url).to.equal('https://id.example.com/locid?x=1&alt_id=user456#frag'); + callbacks.success(JSON.stringify({ tx_cloc: TEST_ID, connection_ip: TEST_CONNECTION_IP })); + }); + + const config = { + params: { + endpoint: 'https://id.example.com/locid?x=1#frag', + altId: 'user456' + } + }; + + const result = locIdSubmodule.getId(config, {}); + result.callback(() => done()); + }); + + it('should return undefined via callback when endpoint is empty string', (done) => { + const config = { + params: { + endpoint: '' + } + }; + + const result = locIdSubmodule.getId(config, {}); + result.callback((id) => { + expect(id).to.be.undefined; + expect(ajaxStub.called).to.be.false; + done(); + }); + }); + }); + + describe('extendId', () => { + const config = { + params: { + endpoint: TEST_ENDPOINT + } + }; + + it('should return stored id when valid and IP cache is current', () => { + storage.getDataFromLocalStorage.returns(JSON.stringify({ + ip: TEST_CONNECTION_IP, + fetchedAt: Date.now(), + expiresAt: Date.now() + 14400000 + })); + const storedId = { id: 'existing-id', connectionIp: TEST_CONNECTION_IP }; + const result = locIdSubmodule.extendId(config, {}, storedId); + expect(result).to.deep.equal({ id: storedId }); + }); + + it('should reuse storedId when refreshInSeconds is configured but not due', () => { + const now = Date.now(); + const refreshInSeconds = 60; + storage.getDataFromLocalStorage.returns(JSON.stringify({ + ip: TEST_CONNECTION_IP, + fetchedAt: now, + expiresAt: now + 14400000 + })); + const storedId = { + id: 'existing-id', + connectionIp: TEST_CONNECTION_IP, + createdAt: now - ((refreshInSeconds - 10) * 1000), + expiresAt: now + 10000 + }; + const refreshConfig = { + params: { + endpoint: TEST_ENDPOINT + }, + storage: { + refreshInSeconds + } + }; + const result = locIdSubmodule.extendId(refreshConfig, {}, storedId); + expect(result).to.deep.equal({ id: storedId }); + }); + + it('should return undefined when refreshInSeconds is due', () => { + const now = Date.now(); + const refreshInSeconds = 60; + const storedId = { + id: 'existing-id', + connectionIp: TEST_CONNECTION_IP, + createdAt: now - ((refreshInSeconds + 10) * 1000), + expiresAt: now + 10000 + }; + const refreshConfig = { + params: { + endpoint: TEST_ENDPOINT + }, + storage: { + refreshInSeconds + } + }; + const result = locIdSubmodule.extendId(refreshConfig, {}, storedId); + expect(result).to.be.undefined; + }); + + it('should return undefined when refreshInSeconds is configured and createdAt is missing', () => { + const now = Date.now(); + const refreshInSeconds = 60; + const storedId = { + id: 'existing-id', + connectionIp: TEST_CONNECTION_IP, + expiresAt: now + 10000 + }; + const refreshConfig = { + params: { + endpoint: TEST_ENDPOINT + }, + storage: { + refreshInSeconds + } + }; + const result = locIdSubmodule.extendId(refreshConfig, {}, storedId); + expect(result).to.be.undefined; + }); + + it('should return undefined when storedId is a string', () => { + const result = locIdSubmodule.extendId(config, {}, 'existing-id'); + expect(result).to.be.undefined; + }); + + it('should return undefined when connectionIp is missing', () => { + const result = locIdSubmodule.extendId(config, {}, { id: 'existing-id' }); + expect(result).to.be.undefined; + }); + + it('should return undefined when stored entry is expired', () => { + const storedId = { + id: 'existing-id', + connectionIp: TEST_CONNECTION_IP, + expiresAt: Date.now() - 1000 + }; + const result = locIdSubmodule.extendId(config, {}, storedId); + expect(result).to.be.undefined; + }); + }); + + describe('privacy framework handling', () => { + const config = { + params: { + endpoint: TEST_ENDPOINT + } + }; + + // --- Tests for no privacy signals (LI-based operation) --- + // LocID operates under Legitimate Interest and should proceed when no + // privacy framework is present, unless requirePrivacySignals is enabled. + + it('should proceed when no consentData provided at all (default LI-based operation)', () => { + // When no privacy signals are present, module should proceed by default + const result = locIdSubmodule.getId(config, undefined); + expect(result).to.have.property('callback'); + }); + + it('should proceed when consentData is null (default LI-based operation)', () => { + const result = locIdSubmodule.getId(config, null); + expect(result).to.have.property('callback'); + }); + + it('should proceed when consentData is empty object (default LI-based operation)', () => { + // Empty object has no privacy signals, so should proceed + const result = locIdSubmodule.getId(config, {}); + expect(result).to.have.property('callback'); + }); + + it('should return undefined when no consentData and requirePrivacySignals=true', () => { + const strictConfig = { + params: { + endpoint: TEST_ENDPOINT, + requirePrivacySignals: true + } + }; + const result = locIdSubmodule.getId(strictConfig, undefined); + expect(result).to.be.undefined; + expect(ajaxStub.called).to.be.false; + }); + + it('should return undefined when empty consentData and requirePrivacySignals=true', () => { + const strictConfig = { + params: { + endpoint: TEST_ENDPOINT, + requirePrivacySignals: true + } + }; + const result = locIdSubmodule.getId(strictConfig, {}); + expect(result).to.be.undefined; + expect(ajaxStub.called).to.be.false; + }); + + it('should return undefined when no consentData and privacyMode=requireSignals', () => { + const strictConfig = { + params: { + endpoint: TEST_ENDPOINT, + privacyMode: 'requireSignals' + } + }; + const result = locIdSubmodule.getId(strictConfig, undefined); + expect(result).to.be.undefined; + expect(ajaxStub.called).to.be.false; + }); + + it('should proceed when no consentData and privacyMode=allowWithoutSignals', () => { + const permissiveConfig = { + params: { + endpoint: TEST_ENDPOINT, + privacyMode: 'allowWithoutSignals' + } + }; + const result = locIdSubmodule.getId(permissiveConfig, undefined); + expect(result).to.have.property('callback'); + }); + + it('should proceed with privacy signals present and requirePrivacySignals=true', () => { + const strictConfig = { + params: { + endpoint: TEST_ENDPOINT, + requirePrivacySignals: true + } + }; + const consentData = { + gdprApplies: true, + consentString: 'valid-consent-string', + vendorData: { + vendor: {} + } + }; + const result = locIdSubmodule.getId(strictConfig, consentData); + expect(result).to.have.property('callback'); + }); + + it('should detect privacy signals from uspDataHandler and block on processing restriction', () => { + // Restore and re-stub to return USP processing restriction signal + uspDataHandler.getConsentData.restore(); + sandbox.stub(uspDataHandler, 'getConsentData').returns('1YY-'); + + // Even with empty consentData, handler provides privacy signal + const result = locIdSubmodule.getId(config, {}); + expect(result).to.be.undefined; + expect(ajaxStub.called).to.be.false; + }); + + it('should detect privacy signals from gppDataHandler and block on processing restriction', () => { + // Restore and re-stub to return GPP processing restriction signal + gppDataHandler.getConsentData.restore(); + sandbox.stub(gppDataHandler, 'getConsentData').returns({ + applicableSections: [7], + parsedSections: { + usnat: { + KnownChildSensitiveDataConsents: [1] + } + } + }); + + // Even with empty consentData, handler provides privacy signal + const result = locIdSubmodule.getId(config, {}); + expect(result).to.be.undefined; + expect(ajaxStub.called).to.be.false; + }); + + it('should proceed when uspDataHandler returns non-restrictive value', () => { + // Restore and re-stub to return non-restrictive USP + uspDataHandler.getConsentData.restore(); + sandbox.stub(uspDataHandler, 'getConsentData').returns('1NN-'); + + const result = locIdSubmodule.getId(config, {}); + expect(result).to.have.property('callback'); + }); + + // --- Tests for gdprApplies edge cases (LI-based operation) --- + // gdprApplies alone does NOT constitute a privacy signal. + // A GDPR signal requires actual CMP artifacts (consentString or vendorData). + + it('should proceed when gdprApplies=true but no consentString/vendorData (default LI mode)', () => { + // gdprApplies alone is not a signal - allows LI-based operation without CMP + const consentData = { + gdprApplies: true + }; + const result = locIdSubmodule.getId(config, consentData); + expect(result).to.have.property('callback'); + }); + + it('should proceed when gdprApplies=true and usp is present but no GDPR CMP artifacts', () => { + const consentData = { + gdprApplies: true, + usp: '1NN-' + }; + const result = locIdSubmodule.getId(config, consentData); + expect(result).to.have.property('callback'); + }); + + it('should proceed when gdprApplies=true and consentString is empty (treated as absent CMP artifact)', () => { + // Empty consentString is treated as not present, so LI-mode behavior applies. + const consentData = { + gdprApplies: true, + consentString: '' + }; + const result = locIdSubmodule.getId(config, consentData); + expect(result).to.have.property('callback'); + }); + + it('should return undefined when gdprApplies=true, no CMP artifacts, and strict mode', () => { + const strictConfig = { + params: { + endpoint: TEST_ENDPOINT, + requirePrivacySignals: true + } + }; + const consentData = { + gdprApplies: true + }; + const result = locIdSubmodule.getId(strictConfig, consentData); + expect(result).to.be.undefined; + expect(ajaxStub.called).to.be.false; + }); + + it('should return undefined when gdprApplies=true, vendorData present but no consentString', () => { + // vendorData presence = CMP is present, so signals ARE present + // GDPR applies + signals present + no consentString → deny + const consentData = { + gdprApplies: true, + vendorData: { + vendor: {} + } + }; + const result = locIdSubmodule.getId(config, consentData); + expect(result).to.be.undefined; + expect(ajaxStub.called).to.be.false; + }); + + it('should deny when gdprApplies=false and strict mode is enabled (gdprApplies alone is not a signal)', () => { + // gdprApplies=false alone is not a signal, so strict mode blocks + const strictConfig = { + params: { + endpoint: TEST_ENDPOINT, + requirePrivacySignals: true + } + }; + const consentData = { + gdprApplies: false + }; + const result = locIdSubmodule.getId(strictConfig, consentData); + // gdprApplies=false is not a signal, so strict mode denies + expect(result).to.be.undefined; + expect(ajaxStub.called).to.be.false; + }); + + // --- Original tests for when privacy signals ARE present --- + + it('should proceed with valid GDPR framework data', () => { + const consentData = { + gdprApplies: true, + consentString: 'valid-consent-string' + }; + const result = locIdSubmodule.getId(config, consentData); + expect(result).to.have.property('callback'); + }); + + it('should proceed when GDPR does not apply (no CMP artifacts)', () => { + // gdprApplies=false alone is not a signal - LI operation allowed + const consentData = { + gdprApplies: false + }; + const result = locIdSubmodule.getId(config, consentData); + expect(result).to.have.property('callback'); + }); + + it('should proceed when GDPR applies with consentString and vendor flags deny', () => { + const consentData = { + gdprApplies: true, + consentString: 'valid-consent-string', + vendorData: { + vendor: { + consents: { 999: false }, + legitimateInterests: { 999: false } + } + } + }; + const result = locIdSubmodule.getId(config, consentData); + expect(result).to.have.property('callback'); + }); + + it('should return undefined on US Privacy processing restriction and not call ajax', () => { + const consentData = { + usp: '1YY-' + }; + const result = locIdSubmodule.getId(config, consentData); + expect(result).to.be.undefined; + expect(ajaxStub.called).to.be.false; + }); + + it('should return undefined on legacy US Privacy processing restriction and not call ajax', () => { + const consentData = { + uspConsent: '1YY-' + }; + const result = locIdSubmodule.getId(config, consentData); + expect(result).to.be.undefined; + expect(ajaxStub.called).to.be.false; + }); + + it('should return undefined on GPP processing restriction and not call ajax', () => { + const consentData = { + gpp: { + applicableSections: [7], + parsedSections: { + usnat: { + KnownChildSensitiveDataConsents: [1] + } + } + } + }; + const result = locIdSubmodule.getId(config, consentData); + expect(result).to.be.undefined; + expect(ajaxStub.called).to.be.false; + }); + + it('should return undefined on legacy GPP processing restriction and not call ajax', () => { + const consentData = { + gppConsent: { + applicableSections: [7], + parsedSections: { + usnat: { + KnownChildSensitiveDataConsents: [1] + } + } + } + }; + const result = locIdSubmodule.getId(config, consentData); + expect(result).to.be.undefined; + expect(ajaxStub.called).to.be.false; + }); + + it('should proceed when nested GDPR applies but no CMP artifacts (LI operation)', () => { + // Empty consentString in nested gdpr object is not a signal - LI operation allowed + const consentData = { + gdpr: { + gdprApplies: true, + consentString: '' + } + }; + const result = locIdSubmodule.getId(config, consentData); + expect(result).to.have.property('callback'); + }); + + it('should proceed with valid nested GDPR framework data', () => { + const consentData = { + gdpr: { + gdprApplies: true, + consentString: 'valid-consent-string' + } + }; + const result = locIdSubmodule.getId(config, consentData); + expect(result).to.have.property('callback'); + }); + + it('should proceed when GDPR applies and vendorData is present', () => { + const consentData = { + gdprApplies: true, + consentString: 'valid-consent-string', + vendorData: { + vendor: { + consents: { 999: false }, + legitimateInterests: { 999: false } + } + } + }; + const result = locIdSubmodule.getId(config, consentData); + expect(result).to.have.property('callback'); + }); + + it('should proceed when GDPR applies and vendor is missing from consents', () => { + const consentData = { + gdprApplies: true, + consentString: 'valid-consent-string', + vendorData: { + vendor: { + consents: { 1234: true }, + legitimateInterests: { 1234: true } + } + } + }; + const result = locIdSubmodule.getId(config, consentData); + expect(result).to.have.property('callback'); + }); + + it('should proceed when GDPR applies and vendor consents object is present', () => { + const consentData = { + gdprApplies: true, + consentString: 'valid-consent-string', + vendorData: { + vendor: { + consents: { 999: true } + } + } + }; + const result = locIdSubmodule.getId(config, consentData); + expect(result).to.have.property('callback'); + }); + + it('should proceed when GDPR applies and vendor legitimate interests object is present', () => { + const consentData = { + gdprApplies: true, + consentString: 'valid-consent-string', + vendorData: { + vendor: { + consents: { 999: false }, + legitimateInterests: { 999: true } + } + } + }; + const result = locIdSubmodule.getId(config, consentData); + expect(result).to.have.property('callback'); + }); + + it('should proceed when vendorData is not available (cannot determine vendor permission)', () => { + const consentData = { + gdprApplies: true, + consentString: 'valid-consent-string' + }; + const result = locIdSubmodule.getId(config, consentData); + expect(result).to.have.property('callback'); + }); + + it('should check nested vendorData when using gdpr object shape', () => { + const consentData = { + gdpr: { + gdprApplies: true, + consentString: 'valid-consent-string', + vendorData: { + vendor: { + consents: { 999: true } + } + } + } + }; + const result = locIdSubmodule.getId(config, consentData); + expect(result).to.have.property('callback'); + }); + + it('should proceed when nested vendorData has explicit deny flags', () => { + const consentData = { + gdpr: { + gdprApplies: true, + consentString: 'valid-consent-string', + vendorData: { + vendor: { + consents: { 999: false }, + legitimateInterests: {} + } + } + } + }; + const result = locIdSubmodule.getId(config, consentData); + expect(result).to.have.property('callback'); + }); + + it('should proceed when vendor consents is a function returning true', () => { + const consentData = { + gdprApplies: true, + consentString: 'valid-consent-string', + vendorData: { + vendor: { + consents: (id) => id === 999, + legitimateInterests: {} + } + } + }; + const result = locIdSubmodule.getId(config, consentData); + expect(result).to.have.property('callback'); + }); + + it('should proceed when vendor legitimateInterests is a function returning true', () => { + const consentData = { + gdprApplies: true, + consentString: 'valid-consent-string', + vendorData: { + vendor: { + consents: (id) => false, + legitimateInterests: (id) => id === 999 + } + } + }; + const result = locIdSubmodule.getId(config, consentData); + expect(result).to.have.property('callback'); + }); + + it('should proceed when vendor consent callbacks both return false', () => { + const consentData = { + gdprApplies: true, + consentString: 'valid-consent-string', + vendorData: { + vendor: { + consents: (id) => false, + legitimateInterests: (id) => false + } + } + }; + const result = locIdSubmodule.getId(config, consentData); + expect(result).to.have.property('callback'); + }); + }); + + describe('response parsing', () => { + it('should parse JSON string response', (done) => { + ajaxStub.callsFake((url, callbacks, body, options) => { + callbacks.success('{"tx_cloc":"parsed-id","connection_ip":"203.0.113.42"}'); + }); + + const config = { + params: { + endpoint: TEST_ENDPOINT + } + }; + + const result = locIdSubmodule.getId(config, {}); + result.callback((id) => { + expect(id).to.be.an('object'); + expect(id.id).to.equal('parsed-id'); + expect(id.connectionIp).to.equal('203.0.113.42'); + done(); + }); + }); + + it('should return undefined when connection_ip is missing', (done) => { + ajaxStub.callsFake((url, callbacks, body, options) => { + callbacks.success(JSON.stringify({ tx_cloc: TEST_ID })); + }); + + const config = { + params: { + endpoint: TEST_ENDPOINT + } + }; + + const result = locIdSubmodule.getId(config, {}); + result.callback((id) => { + expect(id).to.be.undefined; + done(); + }); + }); + + it('should return undefined for invalid JSON', (done) => { + ajaxStub.callsFake((url, callbacks, body, options) => { + callbacks.success('not valid json'); + }); + + const config = { + params: { + endpoint: TEST_ENDPOINT + } + }; + + const result = locIdSubmodule.getId(config, {}); + result.callback((id) => { + expect(id).to.be.undefined; + done(); + }); + }); + + it('should cache entry with null id when tx_cloc is empty string', (done) => { + ajaxStub.callsFake((url, callbacks, body, options) => { + callbacks.success(JSON.stringify({ tx_cloc: '', connection_ip: TEST_CONNECTION_IP })); + }); + + const config = { + params: { + endpoint: TEST_ENDPOINT + } + }; + + const result = locIdSubmodule.getId(config, {}); + result.callback((id) => { + expect(id).to.be.an('object'); + expect(id.id).to.be.null; + expect(id.connectionIp).to.equal(TEST_CONNECTION_IP); + done(); + }); + }); + + it('should cache entry with null id when tx_cloc is whitespace-only', (done) => { + ajaxStub.callsFake((url, callbacks, body, options) => { + callbacks.success(JSON.stringify({ tx_cloc: ' \n\t ', connection_ip: TEST_CONNECTION_IP })); + }); + + const config = { + params: { + endpoint: TEST_ENDPOINT + } + }; + + const result = locIdSubmodule.getId(config, {}); + result.callback((id) => { + expect(id).to.be.an('object'); + expect(id.id).to.be.null; + expect(id.connectionIp).to.equal(TEST_CONNECTION_IP); + done(); + }); + }); + + it('should return undefined when tx_cloc is missing', (done) => { + ajaxStub.callsFake((url, callbacks, body, options) => { + callbacks.success(JSON.stringify({ other_field: 'value' })); + }); + + const config = { + params: { + endpoint: TEST_ENDPOINT + } + }; + + const result = locIdSubmodule.getId(config, {}); + result.callback((id) => { + expect(id).to.be.undefined; + done(); + }); + }); + }); + + describe('empty tx_cloc handling', () => { + it('should decode null id as undefined (no EID emitted)', () => { + const result = locIdSubmodule.decode({ id: null, connectionIp: TEST_CONNECTION_IP }); + expect(result).to.be.undefined; + }); + + it('should cache empty tx_cloc response for the full cache period', (done) => { + ajaxStub.callsFake((url, callbacks) => { + callbacks.success(JSON.stringify({ connection_ip: TEST_CONNECTION_IP })); + }); + + const config = { + params: { endpoint: TEST_ENDPOINT }, + storage: { expires: 7 } + }; + + const result = locIdSubmodule.getId(config, {}); + result.callback((id) => { + expect(id).to.be.an('object'); + expect(id.id).to.be.null; + expect(id.connectionIp).to.equal(TEST_CONNECTION_IP); + expect(id.expiresAt).to.be.a('number'); + expect(id.expiresAt).to.be.greaterThan(Date.now()); + done(); + }); + }); + + it('should reuse cached entry with null id on subsequent getId calls', () => { + storage.getDataFromLocalStorage.returns(JSON.stringify({ + ip: TEST_CONNECTION_IP, + fetchedAt: Date.now(), + expiresAt: Date.now() + 1000 + })); + + const config = { + params: { endpoint: TEST_ENDPOINT }, + storage: { name: '_locid' } + }; + const storedId = { + id: null, + connectionIp: TEST_CONNECTION_IP, + createdAt: Date.now(), + updatedAt: Date.now(), + expiresAt: Date.now() + 86400000 + }; + + const result = locIdSubmodule.getId(config, {}, storedId); + expect(result).to.deep.equal({ id: storedId }); + expect(ajaxStub.called).to.be.false; + }); + + it('should not produce EID when tx_cloc is null', () => { + const decoded = locIdSubmodule.decode({ id: null, connectionIp: TEST_CONNECTION_IP }); + expect(decoded).to.be.undefined; + }); + + it('should write IP cache when endpoint returns empty tx_cloc', (done) => { + ajaxStub.callsFake((url, callbacks) => { + callbacks.success(JSON.stringify({ connection_ip: TEST_CONNECTION_IP })); + }); + + const config = { + params: { endpoint: TEST_ENDPOINT }, + storage: { name: '_locid' } + }; + + const result = locIdSubmodule.getId(config, {}); + result.callback(() => { + expect(storage.setDataInLocalStorage.called).to.be.true; + const callArgs = storage.setDataInLocalStorage.getCall(0).args; + expect(callArgs[0]).to.equal('_locid_ip'); + const ipEntry = JSON.parse(callArgs[1]); + expect(ipEntry.ip).to.equal(TEST_CONNECTION_IP); + done(); + }); + }); + }); + + describe('IP cache management', () => { + const ipCacheConfig = { + params: { endpoint: TEST_ENDPOINT }, + storage: { name: '_locid' } + }; + + it('should read valid IP cache entry', () => { + storage.getDataFromLocalStorage.returns(JSON.stringify({ + ip: TEST_CONNECTION_IP, + fetchedAt: Date.now(), + expiresAt: Date.now() + 1000 + })); + + // If IP cache is valid and stored entry matches, should reuse + const storedId = { + id: TEST_ID, + connectionIp: TEST_CONNECTION_IP, + createdAt: Date.now(), + expiresAt: Date.now() + 86400000 + }; + const result = locIdSubmodule.getId(ipCacheConfig, {}, storedId); + expect(result).to.deep.equal({ id: storedId }); + expect(ajaxStub.called).to.be.false; + }); + + it('should treat expired IP cache as missing', (done) => { + storage.getDataFromLocalStorage.returns(JSON.stringify({ + ip: TEST_CONNECTION_IP, + fetchedAt: Date.now() - 20000, + expiresAt: Date.now() - 1000 + })); + + ajaxStub.callsFake((url, callbacks) => { + callbacks.success(JSON.stringify({ tx_cloc: TEST_ID, connection_ip: TEST_CONNECTION_IP })); + }); + + const storedId = { + id: TEST_ID, + connectionIp: TEST_CONNECTION_IP, + createdAt: Date.now(), + expiresAt: Date.now() + 86400000 + }; + const result = locIdSubmodule.getId(ipCacheConfig, {}, storedId); + // Expired IP cache → calls main endpoint + expect(result).to.have.property('callback'); + result.callback((id) => { + expect(id).to.be.an('object'); + done(); + }); + }); + + it('should handle corrupted IP cache JSON gracefully', (done) => { + storage.getDataFromLocalStorage.returns('not-valid-json'); + + ajaxStub.callsFake((url, callbacks) => { + callbacks.success(JSON.stringify({ tx_cloc: TEST_ID, connection_ip: TEST_CONNECTION_IP })); + }); + + const result = locIdSubmodule.getId(ipCacheConfig, {}); + expect(result).to.have.property('callback'); + result.callback((id) => { + expect(id).to.be.an('object'); + expect(id.id).to.equal(TEST_ID); + done(); + }); + }); + + it('should derive IP cache key from storage name', (done) => { + ajaxStub.callsFake((url, callbacks) => { + callbacks.success(JSON.stringify({ tx_cloc: TEST_ID, connection_ip: TEST_CONNECTION_IP })); + }); + + const config = { + params: { endpoint: TEST_ENDPOINT }, + storage: { name: 'custom_key' } + }; + + const result = locIdSubmodule.getId(config, {}); + result.callback(() => { + const setCall = storage.setDataInLocalStorage.getCall(0); + expect(setCall.args[0]).to.equal('custom_key_ip'); + done(); + }); + }); + + it('should use custom ipCacheName when configured', (done) => { + ajaxStub.callsFake((url, callbacks) => { + callbacks.success(JSON.stringify({ tx_cloc: TEST_ID, connection_ip: TEST_CONNECTION_IP })); + }); + + const config = { + params: { endpoint: TEST_ENDPOINT, ipCacheName: 'my_ip_cache' }, + storage: { name: '_locid' } + }; + + const result = locIdSubmodule.getId(config, {}); + result.callback(() => { + const setCall = storage.setDataInLocalStorage.getCall(0); + expect(setCall.args[0]).to.equal('my_ip_cache'); + done(); + }); + }); + + it('should use custom ipCacheTtlMs when configured', (done) => { + ajaxStub.callsFake((url, callbacks) => { + callbacks.success(JSON.stringify({ tx_cloc: TEST_ID, connection_ip: TEST_CONNECTION_IP })); + }); + + const config = { + params: { endpoint: TEST_ENDPOINT, ipCacheTtlMs: 7200000 }, + storage: { name: '_locid' } + }; + + const result = locIdSubmodule.getId(config, {}); + result.callback(() => { + const setCall = storage.setDataInLocalStorage.getCall(0); + const ipEntry = JSON.parse(setCall.args[1]); + // TTL should be ~2 hours + const ttl = ipEntry.expiresAt - ipEntry.fetchedAt; + expect(ttl).to.equal(7200000); + done(); + }); + }); + + it('should write IP cache on every successful main endpoint response', (done) => { + ajaxStub.callsFake((url, callbacks) => { + callbacks.success(JSON.stringify({ tx_cloc: TEST_ID, connection_ip: '10.0.0.1' })); + }); + + const result = locIdSubmodule.getId(ipCacheConfig, {}); + result.callback(() => { + expect(storage.setDataInLocalStorage.called).to.be.true; + const ipEntry = JSON.parse(storage.setDataInLocalStorage.getCall(0).args[1]); + expect(ipEntry.ip).to.equal('10.0.0.1'); + done(); + }); + }); + }); + + describe('getId with ipEndpoint (two-call optimization)', () => { + it('should call ipEndpoint first when IP cache is expired', (done) => { + let callCount = 0; + ajaxStub.callsFake((url, callbacks) => { + callCount++; + if (url === 'https://ip.example.com/check') { + callbacks.success(JSON.stringify({ ip: '10.0.0.1' })); + } else { + callbacks.success(JSON.stringify({ tx_cloc: TEST_ID, connection_ip: '10.0.0.1' })); + } + }); + + const config = { + params: { + endpoint: TEST_ENDPOINT, + ipEndpoint: 'https://ip.example.com/check' + }, + storage: { name: '_locid' } + }; + + const result = locIdSubmodule.getId(config, {}); + result.callback((id) => { + // IP changed (no stored entry) → 2 calls: ipEndpoint + main endpoint + expect(callCount).to.equal(2); + expect(id.id).to.equal(TEST_ID); + done(); + }); + }); + + it('should reuse cached tx_cloc when ipEndpoint returns same IP', (done) => { + let callCount = 0; + ajaxStub.callsFake((url, callbacks) => { + callCount++; + if (url === 'https://ip.example.com/check') { + callbacks.success(JSON.stringify({ ip: TEST_CONNECTION_IP })); + } else { + callbacks.success(JSON.stringify({ tx_cloc: 'new-id', connection_ip: TEST_CONNECTION_IP })); + } + }); + + const config = { + params: { + endpoint: TEST_ENDPOINT, + ipEndpoint: 'https://ip.example.com/check' + }, + storage: { name: '_locid' } + }; + const storedId = { + id: TEST_ID, + connectionIp: TEST_CONNECTION_IP, + createdAt: Date.now(), + expiresAt: Date.now() + 86400000 + }; + + const result = locIdSubmodule.getId(config, {}, storedId); + result.callback((id) => { + // IP unchanged → only 1 call (ipEndpoint), reuses stored tx_cloc + expect(callCount).to.equal(1); + expect(id.id).to.equal(TEST_ID); + done(); + }); + }); + + it('should call main endpoint when ipEndpoint returns different IP', (done) => { + let callCount = 0; + ajaxStub.callsFake((url, callbacks) => { + callCount++; + if (url === 'https://ip.example.com/check') { + callbacks.success(JSON.stringify({ ip: '10.0.0.99' })); + } else { + callbacks.success(JSON.stringify({ tx_cloc: 'new-id', connection_ip: '10.0.0.99' })); + } + }); + + const config = { + params: { + endpoint: TEST_ENDPOINT, + ipEndpoint: 'https://ip.example.com/check' + }, + storage: { name: '_locid' } + }; + const storedId = { + id: TEST_ID, + connectionIp: TEST_CONNECTION_IP, + createdAt: Date.now(), + expiresAt: Date.now() + 86400000 + }; + + const result = locIdSubmodule.getId(config, {}, storedId); + result.callback((id) => { + // IP changed → 2 calls: ipEndpoint + main endpoint + expect(callCount).to.equal(2); + expect(id.id).to.equal('new-id'); + done(); + }); + }); + + it('should fall back to main endpoint when ipEndpoint fails', (done) => { + let callCount = 0; + ajaxStub.callsFake((url, callbacks) => { + callCount++; + if (url === 'https://ip.example.com/check') { + callbacks.error('Network error'); + } else { + callbacks.success(JSON.stringify({ tx_cloc: TEST_ID, connection_ip: TEST_CONNECTION_IP })); + } + }); + + const config = { + params: { + endpoint: TEST_ENDPOINT, + ipEndpoint: 'https://ip.example.com/check' + }, + storage: { name: '_locid' } + }; + + const result = locIdSubmodule.getId(config, {}); + result.callback((id) => { + expect(callCount).to.equal(2); + expect(id.id).to.equal(TEST_ID); + done(); + }); + }); + + it('should fall back to main endpoint when ipEndpoint returns invalid IP', (done) => { + let callCount = 0; + ajaxStub.callsFake((url, callbacks) => { + callCount++; + if (url === 'https://ip.example.com/check') { + callbacks.success('not-an-ip!!!'); + } else { + callbacks.success(JSON.stringify({ tx_cloc: TEST_ID, connection_ip: TEST_CONNECTION_IP })); + } + }); + + const config = { + params: { + endpoint: TEST_ENDPOINT, + ipEndpoint: 'https://ip.example.com/check' + }, + storage: { name: '_locid' } + }; + + const result = locIdSubmodule.getId(config, {}); + result.callback((id) => { + expect(callCount).to.equal(2); + expect(id.id).to.equal(TEST_ID); + done(); + }); + }); + + it('should pass apiKey header to ipEndpoint when configured', (done) => { + ajaxStub.callsFake((url, callbacks, _body, options) => { + if (url === 'https://ip.example.com/check') { + expect(options.customHeaders).to.deep.equal({ 'x-api-key': 'test-key-123' }); + callbacks.success(JSON.stringify({ ip: TEST_CONNECTION_IP })); + } else { + callbacks.success(JSON.stringify({ tx_cloc: TEST_ID, connection_ip: TEST_CONNECTION_IP })); + } + }); + + const config = { + params: { + endpoint: TEST_ENDPOINT, + ipEndpoint: 'https://ip.example.com/check', + apiKey: 'test-key-123' + }, + storage: { name: '_locid' } + }; + + const result = locIdSubmodule.getId(config, {}); + result.callback((id) => { + expect(id).to.be.an('object'); + done(); + }); + }); + + it('should parse plain text IP from ipEndpoint', (done) => { + ajaxStub.callsFake((url, callbacks) => { + if (url === 'https://ip.example.com/check') { + callbacks.success(TEST_CONNECTION_IP); + } else { + callbacks.success(JSON.stringify({ tx_cloc: 'new-id', connection_ip: TEST_CONNECTION_IP })); + } + }); + + const config = { + params: { + endpoint: TEST_ENDPOINT, + ipEndpoint: 'https://ip.example.com/check' + }, + storage: { name: '_locid' } + }; + const storedId = { + id: TEST_ID, + connectionIp: TEST_CONNECTION_IP, + createdAt: Date.now(), + expiresAt: Date.now() + 86400000 + }; + + const result = locIdSubmodule.getId(config, {}, storedId); + result.callback((id) => { + // IP same → reuses stored tx_cloc + expect(id.id).to.equal(TEST_ID); + done(); + }); + }); + }); + + describe('getId tx_cloc preservation (no churn)', () => { + it('should preserve existing tx_cloc when main endpoint returns same IP', (done) => { + ajaxStub.callsFake((url, callbacks) => { + callbacks.success(JSON.stringify({ tx_cloc: 'fresh-id', connection_ip: TEST_CONNECTION_IP })); + }); + + const config = { + params: { endpoint: TEST_ENDPOINT }, + storage: { name: '_locid' } + }; + const storedId = { + id: TEST_ID, + connectionIp: TEST_CONNECTION_IP, + createdAt: Date.now(), + expiresAt: Date.now() + 86400000 + }; + + const result = locIdSubmodule.getId(config, {}, storedId); + result.callback((id) => { + // IP unchanged → preserve existing tx_cloc (don't churn) + expect(id.id).to.equal(TEST_ID); + expect(id.connectionIp).to.equal(TEST_CONNECTION_IP); + done(); + }); + }); + + it('should use fresh non-null tx_cloc when stored entry is null for same IP', (done) => { + ajaxStub.callsFake((url, callbacks) => { + callbacks.success(JSON.stringify({ tx_cloc: 'fresh-id', connection_ip: TEST_CONNECTION_IP })); + }); + + const config = { + params: { endpoint: TEST_ENDPOINT }, + storage: { name: '_locid' } + }; + const storedId = { + id: null, + connectionIp: TEST_CONNECTION_IP, + createdAt: Date.now(), + expiresAt: Date.now() + 86400000 + }; + + const result = locIdSubmodule.getId(config, {}, storedId); + result.callback((id) => { + expect(id.id).to.equal('fresh-id'); + expect(id.connectionIp).to.equal(TEST_CONNECTION_IP); + done(); + }); + }); + + it('should use fresh tx_cloc when main endpoint returns different IP', (done) => { + ajaxStub.callsFake((url, callbacks) => { + callbacks.success(JSON.stringify({ tx_cloc: 'fresh-id', connection_ip: '10.0.0.99' })); + }); + + const config = { + params: { endpoint: TEST_ENDPOINT }, + storage: { name: '_locid' } + }; + const storedId = { + id: TEST_ID, + connectionIp: TEST_CONNECTION_IP, + createdAt: Date.now(), + expiresAt: Date.now() + 86400000 + }; + + const result = locIdSubmodule.getId(config, {}, storedId); + result.callback((id) => { + // IP changed → use fresh tx_cloc + expect(id.id).to.equal('fresh-id'); + expect(id.connectionIp).to.equal('10.0.0.99'); + done(); + }); + }); + + it('should use fresh tx_cloc when stored entry is expired even if IP matches', (done) => { + ajaxStub.callsFake((url, callbacks) => { + callbacks.success(JSON.stringify({ tx_cloc: 'fresh-id', connection_ip: TEST_CONNECTION_IP })); + }); + + const config = { + params: { endpoint: TEST_ENDPOINT }, + storage: { name: '_locid' } + }; + const storedId = { + id: TEST_ID, + connectionIp: TEST_CONNECTION_IP, + createdAt: Date.now() - 86400000, + expiresAt: Date.now() - 1000 + }; + + const result = locIdSubmodule.getId(config, {}, storedId); + result.callback((id) => { + // tx_cloc expired → use fresh even though IP matches + expect(id.id).to.equal('fresh-id'); + done(); + }); + }); + + it('should honor null tx_cloc from main endpoint even when stored entry is reusable', (done) => { + // Server returns connection_ip but no tx_cloc → freshEntry.id === null. + // The stored entry has a valid tx_cloc for the same IP, but the server + // now indicates no ID for this IP. The null response must be honored. + ajaxStub.callsFake((url, callbacks) => { + callbacks.success(JSON.stringify({ connection_ip: TEST_CONNECTION_IP })); + }); + + const config = { + params: { endpoint: TEST_ENDPOINT }, + storage: { name: '_locid' } + }; + const storedId = { + id: TEST_ID, + connectionIp: TEST_CONNECTION_IP, + createdAt: Date.now(), + expiresAt: Date.now() + 86400000 + }; + + const result = locIdSubmodule.getId(config, {}, storedId); + result.callback((id) => { + // Server said no tx_cloc → stored entry must NOT be preserved + expect(id.id).to.be.null; + expect(id.connectionIp).to.equal(TEST_CONNECTION_IP); + done(); + }); + }); + + it('should use fresh entry on first load (no stored entry)', (done) => { + ajaxStub.callsFake((url, callbacks) => { + callbacks.success(JSON.stringify({ tx_cloc: TEST_ID, connection_ip: TEST_CONNECTION_IP })); + }); + + const config = { + params: { endpoint: TEST_ENDPOINT }, + storage: { name: '_locid' } + }; + + const result = locIdSubmodule.getId(config, {}); + result.callback((id) => { + expect(id.id).to.equal(TEST_ID); + expect(id.connectionIp).to.equal(TEST_CONNECTION_IP); + done(); + }); + }); + }); + + describe('extendId with null id and IP cache', () => { + const config = { + params: { endpoint: TEST_ENDPOINT }, + storage: { name: '_locid' } + }; + + it('should accept stored entry with null id (empty tx_cloc)', () => { + storage.getDataFromLocalStorage.returns(JSON.stringify({ + ip: TEST_CONNECTION_IP, + fetchedAt: Date.now(), + expiresAt: Date.now() + 14400000 + })); + const storedId = { + id: null, + connectionIp: TEST_CONNECTION_IP, + createdAt: Date.now(), + expiresAt: Date.now() + 86400000 + }; + const result = locIdSubmodule.extendId(config, {}, storedId); + expect(result).to.deep.equal({ id: storedId }); + }); + + it('should return refresh callback when IP cache shows different IP', (done) => { + ajaxStub.callsFake((url, callbacks) => { + callbacks.success(JSON.stringify({ tx_cloc: 'fresh-id', connection_ip: '10.0.0.99' })); + }); + storage.getDataFromLocalStorage.returns(JSON.stringify({ + ip: '10.0.0.99', + fetchedAt: Date.now(), + expiresAt: Date.now() + 1000 + })); + + const storedId = { + id: TEST_ID, + connectionIp: TEST_CONNECTION_IP, + createdAt: Date.now(), + expiresAt: Date.now() + 86400000 + }; + const result = locIdSubmodule.extendId(config, {}, storedId); + expect(result).to.have.property('callback'); + result.callback((id) => { + expect(id.id).to.equal('fresh-id'); + expect(id.connectionIp).to.equal('10.0.0.99'); + done(); + }); + }); + + it('should extend when IP cache matches stored entry IP', () => { + storage.getDataFromLocalStorage.returns(JSON.stringify({ + ip: TEST_CONNECTION_IP, + fetchedAt: Date.now(), + expiresAt: Date.now() + 1000 + })); + + const storedId = { + id: TEST_ID, + connectionIp: TEST_CONNECTION_IP, + createdAt: Date.now(), + expiresAt: Date.now() + 86400000 + }; + const result = locIdSubmodule.extendId(config, {}, storedId); + expect(result).to.deep.equal({ id: storedId }); + }); + + it('should return refresh callback when IP cache is missing', (done) => { + ajaxStub.callsFake((url, callbacks) => { + callbacks.success(JSON.stringify({ tx_cloc: 'fresh-id', connection_ip: TEST_CONNECTION_IP })); + }); + const storedId = { + id: TEST_ID, + connectionIp: TEST_CONNECTION_IP, + createdAt: Date.now(), + expiresAt: Date.now() + 86400000 + }; + const result = locIdSubmodule.extendId(config, {}, storedId); + expect(result).to.have.property('callback'); + result.callback((id) => { + expect(id.id).to.equal('fresh-id'); + expect(id.connectionIp).to.equal(TEST_CONNECTION_IP); + done(); + }); + }); + + it('should return refresh callback when IP cache is expired', (done) => { + ajaxStub.callsFake((url, callbacks) => { + callbacks.success(JSON.stringify({ tx_cloc: 'fresh-id', connection_ip: TEST_CONNECTION_IP })); + }); + storage.getDataFromLocalStorage.returns(JSON.stringify({ + ip: TEST_CONNECTION_IP, + fetchedAt: Date.now() - 14400000, + expiresAt: Date.now() - 1000 + })); + const storedId = { + id: TEST_ID, + connectionIp: TEST_CONNECTION_IP, + createdAt: Date.now(), + expiresAt: Date.now() + 86400000 + }; + const result = locIdSubmodule.extendId(config, {}, storedId); + expect(result).to.have.property('callback'); + result.callback((id) => { + expect(id.id).to.equal('fresh-id'); + expect(id.connectionIp).to.equal(TEST_CONNECTION_IP); + done(); + }); + }); + + it('should return undefined for undefined id (not null, not valid string)', () => { + const storedId = { + id: undefined, + connectionIp: TEST_CONNECTION_IP, + createdAt: Date.now(), + expiresAt: Date.now() + 86400000 + }; + const result = locIdSubmodule.extendId(config, {}, storedId); + expect(result).to.be.undefined; + }); + }); + + describe('normalizeStoredId with null id', () => { + it('should preserve explicit null id', () => { + // getId is the public interface; test via decode which uses normalized values + const stored = { id: null, connectionIp: TEST_CONNECTION_IP }; + // decode returns undefined for null id (correct: no EID emitted) + const decoded = locIdSubmodule.decode(stored); + expect(decoded).to.be.undefined; + }); + + it('should preserve valid string id', () => { + const stored = { id: TEST_ID, connectionIp: TEST_CONNECTION_IP }; + const decoded = locIdSubmodule.decode(stored); + expect(decoded).to.deep.equal({ locId: TEST_ID }); + }); + + it('should fall back to tx_cloc when id key is absent', () => { + const stored = { tx_cloc: TEST_ID, connectionIp: TEST_CONNECTION_IP }; + const decoded = locIdSubmodule.decode(stored); + expect(decoded).to.deep.equal({ locId: TEST_ID }); + }); + + it('should handle connection_ip alias', () => { + const stored = { id: TEST_ID, connection_ip: TEST_CONNECTION_IP }; + const decoded = locIdSubmodule.decode(stored); + expect(decoded).to.deep.equal({ locId: TEST_ID }); + }); + }); + + describe('parseIpResponse via ipEndpoint', () => { + it('should parse JSON with ip field', (done) => { + ajaxStub.callsFake((url, callbacks) => { + if (url === 'https://ip.example.com/check') { + callbacks.success('{"ip":"1.2.3.4"}'); + } else { + callbacks.success(JSON.stringify({ tx_cloc: TEST_ID, connection_ip: '1.2.3.4' })); + } + }); + + const config = { + params: { + endpoint: TEST_ENDPOINT, + ipEndpoint: 'https://ip.example.com/check' + }, + storage: { name: '_locid' } + }; + + const result = locIdSubmodule.getId(config, {}); + result.callback((id) => { + // IP fetched and used + expect(id).to.be.an('object'); + done(); + }); + }); + + it('should parse JSON with connection_ip field', (done) => { + ajaxStub.callsFake((url, callbacks) => { + if (url === 'https://ip.example.com/check') { + callbacks.success('{"connection_ip":"1.2.3.4"}'); + } else { + callbacks.success(JSON.stringify({ tx_cloc: TEST_ID, connection_ip: '1.2.3.4' })); + } + }); + + const config = { + params: { + endpoint: TEST_ENDPOINT, + ipEndpoint: 'https://ip.example.com/check' + }, + storage: { name: '_locid' } + }; + + const result = locIdSubmodule.getId(config, {}); + result.callback((id) => { + expect(id).to.be.an('object'); + done(); + }); + }); + + it('should parse plain text IP address', (done) => { + ajaxStub.callsFake((url, callbacks) => { + if (url === 'https://ip.example.com/check') { + callbacks.success('1.2.3.4\n'); + } else { + callbacks.success(JSON.stringify({ tx_cloc: TEST_ID, connection_ip: '1.2.3.4' })); + } + }); + + const config = { + params: { + endpoint: TEST_ENDPOINT, + ipEndpoint: 'https://ip.example.com/check' + }, + storage: { name: '_locid' } + }; + + const result = locIdSubmodule.getId(config, {}); + result.callback((id) => { + expect(id).to.be.an('object'); + done(); + }); + }); + }); + + describe('backward compatibility', () => { + it('should work with existing stored entries that have string ids', () => { + storage.getDataFromLocalStorage.returns(JSON.stringify({ + ip: TEST_CONNECTION_IP, + fetchedAt: Date.now(), + expiresAt: Date.now() + 1000 + })); + + const config = { + params: { endpoint: TEST_ENDPOINT }, + storage: { name: '_locid' } + }; + const storedId = { + id: TEST_ID, + connectionIp: TEST_CONNECTION_IP, + createdAt: Date.now(), + expiresAt: Date.now() + 86400000 + }; + const result = locIdSubmodule.getId(config, {}, storedId); + expect(result).to.deep.equal({ id: storedId }); + }); + + it('should work with stored entries using connection_ip alias', () => { + storage.getDataFromLocalStorage.returns(JSON.stringify({ + ip: TEST_CONNECTION_IP, + fetchedAt: Date.now(), + expiresAt: Date.now() + 1000 + })); + + const config = { + params: { endpoint: TEST_ENDPOINT }, + storage: { name: '_locid' } + }; + const storedId = { + id: TEST_ID, + connection_ip: TEST_CONNECTION_IP, + createdAt: Date.now(), + expiresAt: Date.now() + 86400000 + }; + const result = locIdSubmodule.getId(config, {}, storedId); + expect(result.id.connectionIp).to.equal(TEST_CONNECTION_IP); + }); + + it('should work with stored entries using tx_cloc alias', () => { + storage.getDataFromLocalStorage.returns(JSON.stringify({ + ip: TEST_CONNECTION_IP, + fetchedAt: Date.now(), + expiresAt: Date.now() + 1000 + })); + + const config = { + params: { endpoint: TEST_ENDPOINT }, + storage: { name: '_locid' } + }; + const storedId = { + tx_cloc: TEST_ID, + connectionIp: TEST_CONNECTION_IP, + createdAt: Date.now(), + expiresAt: Date.now() + 86400000 + }; + const result = locIdSubmodule.getId(config, {}, storedId); + expect(result.id.id).to.equal(TEST_ID); + }); + }); + + describe('EID round-trip integration', () => { + before(() => { + attachIdSystem(locIdSubmodule); + }); + + it('should produce a valid EID from decode output via createEidsArray', () => { + // Simulate the full Prebid pipeline: + // 1. decode() returns { locId: "string" } + // 2. Prebid extracts idObj["locId"] -> the string + // 3. createEidsArray({ locId: "string" }) should produce a valid EID + const stored = { id: TEST_ID, connectionIp: TEST_CONNECTION_IP }; + const decoded = locIdSubmodule.decode(stored); + expect(decoded).to.deep.equal({ locId: TEST_ID }); + + const eids = createEidsArray(decoded); + expect(eids.length).to.equal(1); + expect(eids[0]).to.deep.equal({ + source: 'locid.com', + uids: [{ id: TEST_ID, atype: 1 }] + }); + expect(eids[0].uids[0].atype).to.not.equal(Number('33' + '84')); + }); + + it('should not produce EID when decode returns undefined', () => { + const decoded = locIdSubmodule.decode(null); + expect(decoded).to.be.undefined; + }); + + it('should not produce EID when only stable_cloc is present', () => { + const decoded = locIdSubmodule.decode({ + stable_cloc: 'stable-only-value', + connectionIp: TEST_CONNECTION_IP + }); + expect(decoded).to.be.undefined; + + const eids = createEidsArray({ locId: decoded?.locId }); + expect(eids).to.deep.equal([]); + }); + + it('should not produce EID when tx_cloc is null', () => { + const decoded = locIdSubmodule.decode({ id: null, connectionIp: TEST_CONNECTION_IP }); + expect(decoded).to.be.undefined; + + const eids = createEidsArray({ locId: decoded?.locId }); + expect(eids).to.deep.equal([]); + }); + + it('should not produce EID when tx_cloc is missing', () => { + const decoded = locIdSubmodule.decode({ connectionIp: TEST_CONNECTION_IP }); + expect(decoded).to.be.undefined; + + const eids = createEidsArray({ locId: decoded?.locId }); + expect(eids).to.deep.equal([]); + }); + }); +}); diff --git a/test/spec/modules/loganBidAdapter_spec.js b/test/spec/modules/loganBidAdapter_spec.js index 8b343761a46..944b51cdf51 100644 --- a/test/spec/modules/loganBidAdapter_spec.js +++ b/test/spec/modules/loganBidAdapter_spec.js @@ -1,5 +1,5 @@ -import {expect} from 'chai'; -import {spec} from '../../../modules/loganBidAdapter.js'; +import { expect } from 'chai'; +import { spec } from '../../../modules/loganBidAdapter.js'; import { BANNER, VIDEO, NATIVE } from '../../../src/mediaTypes.js'; describe('LoganBidAdapter', function () { diff --git a/test/spec/modules/logicadBidAdapter_spec.js b/test/spec/modules/logicadBidAdapter_spec.js index 24cc1faae62..489cdf834da 100644 --- a/test/spec/modules/logicadBidAdapter_spec.js +++ b/test/spec/modules/logicadBidAdapter_spec.js @@ -1,5 +1,5 @@ -import {expect} from 'chai'; -import {spec} from '../../../modules/logicadBidAdapter.js'; +import { expect } from 'chai'; +import { spec } from '../../../modules/logicadBidAdapter.js'; import * as utils from 'src/utils.js'; describe('LogicadAdapter', function () { @@ -36,11 +36,6 @@ describe('LogicadAdapter', function () { } }] }], - ortb2Imp: { - ext: { - ae: 1 - } - }, ortb2: { device: { sua: { @@ -197,9 +192,6 @@ describe('LogicadAdapter', function () { stack: [] }, auctionStart: 1563337198010, - paapi: { - enabled: true - } }; const serverResponse = { body: { @@ -227,48 +219,6 @@ describe('LogicadAdapter', function () { } }; - const paapiServerResponse = { - body: { - seatbid: - [{ - bid: { - requestId: '51ef8751f9aead', - cpm: 101.0234, - width: 300, - height: 250, - creativeId: '2019', - currency: 'JPY', - netRevenue: true, - ttl: 60, - ad: '
TEST
', - meta: { - advertiserDomains: ['logicad.com'] - } - } - }], - ext: { - fledgeAuctionConfigs: [{ - bidId: '51ef8751f9aead', - config: { - seller: 'https://fledge.ladsp.com', - decisionLogicUrl: 'https://fledge.ladsp.com/decision_logic.js', - interestGroupBuyers: ['https://fledge.ladsp.com'], - requestedSize: {width: '300', height: '250'}, - allSlotsRequestedSizes: [{width: '300', height: '250'}], - sellerSignals: {signal: 'signal'}, - sellerTimeout: '500', - perBuyerSignals: {'https://fledge.ladsp.com': {signal: 'signal'}}, - perBuyerCurrencies: {'https://fledge.ladsp.com': 'USD'} - } - }] - }, - userSync: { - type: 'image', - url: 'https://cr-p31.ladsp.jp/cookiesender/31' - } - } - }; - const nativeServerResponse = { body: { seatbid: @@ -339,10 +289,6 @@ describe('LogicadAdapter', function () { const data = JSON.parse(request.data); expect(data.auctionId).to.equal('18fd8b8b0bd757'); - // Protected Audience API flag - expect(data.bids[0]).to.have.property('ae'); - expect(data.bids[0].ae).to.equal(1); - expect(data.eids[0].source).to.equal('sharedid.org'); expect(data.eids[0].uids[0].id).to.equal('fakesharedid'); @@ -407,13 +353,6 @@ describe('LogicadAdapter', function () { expect(interpretedResponse[0].ttl).to.equal(serverResponse.body.seatbid[0].bid.ttl); expect(interpretedResponse[0].meta.advertiserDomains).to.equal(serverResponse.body.seatbid[0].bid.meta.advertiserDomains); - // Protected Audience API - const paapiRequest = spec.buildRequests(bidRequests, bidderRequest)[0]; - const paapiInterpretedResponse = spec.interpretResponse(paapiServerResponse, paapiRequest); - expect(paapiInterpretedResponse).to.have.property('bids'); - expect(paapiInterpretedResponse).to.have.property('paapi'); - expect(paapiInterpretedResponse.paapi[0]).to.deep.equal(paapiServerResponse.body.ext.fledgeAuctionConfigs[0]); - // native const nativeRequest = spec.buildRequests(nativeBidRequests, bidderRequest)[0]; const interpretedResponseForNative = spec.interpretResponse(nativeServerResponse, nativeRequest); @@ -440,10 +379,10 @@ describe('LogicadAdapter', function () { describe('getUserSyncs', function () { it('should perform usersync', function () { - let syncs = spec.getUserSyncs({pixelEnabled: false}, [serverResponse]); + let syncs = spec.getUserSyncs({ pixelEnabled: false }, [serverResponse]); expect(syncs).to.have.length(0); - syncs = spec.getUserSyncs({pixelEnabled: true}, [serverResponse]); + syncs = spec.getUserSyncs({ pixelEnabled: true }, [serverResponse]); expect(syncs).to.have.length(1); expect(syncs[0]).to.have.property('type', 'image'); diff --git a/test/spec/modules/loopmeBidAdapter_spec.js b/test/spec/modules/loopmeBidAdapter_spec.js index 56d185b109a..02174bd19d2 100644 --- a/test/spec/modules/loopmeBidAdapter_spec.js +++ b/test/spec/modules/loopmeBidAdapter_spec.js @@ -7,19 +7,23 @@ const bidder = 'loopme'; const mTypes = [ { [BANNER]: { sizes: [[300, 250]] } }, - { [VIDEO]: { - api: [3, 5], - h: 480, - w: 640, - mimes: ['video/mp4'], - plcmt: 4, - protocols: [1, 2, 3, 4, 5, 6, 7, 8] - } }, - { [NATIVE]: { - adTemplate: `##hb_native_asset_id_1## ##hb_native_asset_id_2## ##hb_native_asset_id_3##`, - image: { required: true, sendId: true }, - title: { required: true }, - body: { required: true } } + { + [VIDEO]: { + api: [3, 5], + h: 480, + w: 640, + mimes: ['video/mp4'], + plcmt: 4, + protocols: [1, 2, 3, 4, 5, 6, 7, 8] + } + }, + { + [NATIVE]: { + adTemplate: `##hb_native_asset_id_1## ##hb_native_asset_id_2## ##hb_native_asset_id_3##`, + image: { required: true, sendId: true }, + title: { required: true }, + body: { required: true } + } } ]; @@ -49,7 +53,7 @@ describe('LoopMeBidAdapter', function () { { publisherId: 'publisherId', bundleId: 'bundleId' }, { publisherId: 'publisherId', placementId: 'placementId' }, { publisherId: 'publisherId' } - ].flatMap(params => mTypes.map(mediaTypes => ({ bidder, bidId, mediaTypes, params}))); + ].flatMap(params => mTypes.map(mediaTypes => ({ bidder, bidId, mediaTypes, params }))); validBids.forEach(function (bid) { it('Should return true if bid request valid', function () { diff --git a/test/spec/modules/lotamePanoramaIdSystem_spec.js b/test/spec/modules/lotamePanoramaIdSystem_spec.js index a441ecbdb21..a68458b8d83 100644 --- a/test/spec/modules/lotamePanoramaIdSystem_spec.js +++ b/test/spec/modules/lotamePanoramaIdSystem_spec.js @@ -5,8 +5,8 @@ import { import * as utils from 'src/utils.js'; import { server } from 'test/mocks/xhr.js'; import sinon from 'sinon'; -import {attachIdSystem} from '../../../modules/userId/index.js'; -import {createEidsArray} from '../../../modules/userId/eids.js'; +import { attachIdSystem } from '../../../modules/userId/index.js'; +import { createEidsArray } from '../../../modules/userId/eids.js'; const responseHeader = { 'Content-Type': 'application/json' }; diff --git a/test/spec/modules/luceadBidAdapter_spec.js b/test/spec/modules/luceadBidAdapter_spec.js index 464a467e9b2..71087fbdcb3 100755 --- a/test/spec/modules/luceadBidAdapter_spec.js +++ b/test/spec/modules/luceadBidAdapter_spec.js @@ -2,7 +2,6 @@ import { expect } from 'chai'; import { spec } from 'modules/luceadBidAdapter.js'; import sinon from 'sinon'; import { newBidder } from 'src/adapters/bidderFactory.js'; -import {deepClone} from 'src/utils.js'; import * as ajax from 'src/ajax.js'; describe('Lucead Adapter', () => { @@ -120,7 +119,7 @@ describe('Lucead Adapter', () => { ] }; - const serverResponse = {body: serverResponseBody}; + const serverResponse = { body: serverResponseBody }; const bidRequest = { data: JSON.stringify({ @@ -129,7 +128,7 @@ describe('Lucead Adapter', () => { 'bid_requests': [{ 'bid_id': '2d663fdd390b49', 'sizes': [[300, 250], [300, 150]], - 'media_types': {'banner': {'sizes': [[300, 250], [300, 150]]}}, + 'media_types': { 'banner': { 'sizes': [[300, 250], [300, 150]] } }, 'placement_id': '1' }], }), @@ -154,30 +153,25 @@ describe('Lucead Adapter', () => { }); it('should return bid empty response', function () { - const serverResponse = {body: {bids: [{cpm: 0}]}}; - const bidRequest = {data: '{}'}; + const serverResponse = { body: { bids: [{ cpm: 0 }] } }; + const bidRequest = { data: '{}' }; const result = spec.interpretResponse(serverResponse, bidRequest); expect(result.bids[0].ad).to.be.equal(''); expect(result.bids[0].cpm).to.be.equal(0); }); it('should add advertiserDomains', function () { - const bidRequest = {data: JSON.stringify({ - bidder: 'lucead', - params: { - placementId: '1', - } - })}; + const bidRequest = { + data: JSON.stringify({ + bidder: 'lucead', + params: { + placementId: '1', + } + }) + }; const result = spec.interpretResponse(serverResponse, bidRequest); expect(Object.keys(result.bids[0].meta)).to.include.members(['advertiserDomains']); }); - - it('should support enable_pa = false', function () { - serverResponse.body.enable_pa = false; - const result = spec.interpretResponse(serverResponse, bidRequest); - expect(result).to.be.an('array'); - expect(result[0].cpm).to.be.greaterThan(0); - }); }); }); diff --git a/test/spec/modules/lunamediahbBidAdapter_spec.js b/test/spec/modules/lunamediahbBidAdapter_spec.js index 79c9c0d6172..25a59ba32fe 100644 --- a/test/spec/modules/lunamediahbBidAdapter_spec.js +++ b/test/spec/modules/lunamediahbBidAdapter_spec.js @@ -1,5 +1,5 @@ -import {expect} from 'chai'; -import {spec} from '../../../modules/lunamediahbBidAdapter.js'; +import { expect } from 'chai'; +import { spec } from '../../../modules/lunamediahbBidAdapter.js'; import { BANNER, VIDEO, NATIVE } from '../../../src/mediaTypes.js'; import { getUniqueIdentifierStr } from '../../../src/utils.js'; @@ -432,7 +432,7 @@ describe('LunamediaHBBidAdapter', function () { const syncData = spec.getUserSyncs({}, {}, { consentString: 'ALL', gdprApplies: true, - }, {}); + }, undefined); expect(syncData).to.be.an('array').which.is.not.empty; expect(syncData[0]).to.be.an('object') expect(syncData[0].type).to.be.a('string') @@ -441,9 +441,7 @@ describe('LunamediaHBBidAdapter', function () { expect(syncData[0].url).to.equal('https://cookie.lmgssp.com/image?pbjs=1&gdpr=1&gdpr_consent=ALL&coppa=0') }); it('Should return array of objects with proper sync config , include CCPA', function() { - const syncData = spec.getUserSyncs({}, {}, {}, { - consentString: '1---' - }); + const syncData = spec.getUserSyncs({}, {}, {}, '1---'); expect(syncData).to.be.an('array').which.is.not.empty; expect(syncData[0]).to.be.an('object') expect(syncData[0].type).to.be.a('string') @@ -452,7 +450,7 @@ describe('LunamediaHBBidAdapter', function () { expect(syncData[0].url).to.equal('https://cookie.lmgssp.com/image?pbjs=1&ccpa_consent=1---&coppa=0') }); it('Should return array of objects with proper sync config , include GPP', function() { - const syncData = spec.getUserSyncs({}, {}, {}, {}, { + const syncData = spec.getUserSyncs({}, {}, {}, undefined, { gppString: 'abc123', applicableSections: [8] }); diff --git a/test/spec/modules/mabidderBidAdapter_spec.js b/test/spec/modules/mabidderBidAdapter_spec.js index 805ab168f5d..4230dc0a9aa 100644 --- a/test/spec/modules/mabidderBidAdapter_spec.js +++ b/test/spec/modules/mabidderBidAdapter_spec.js @@ -2,7 +2,7 @@ import { expect } from 'chai' import { baseUrl, spec } from 'modules/mabidderBidAdapter.js' import { newBidder } from 'src/adapters/bidderFactory.js' import { BANNER } from '../../../src/mediaTypes.js'; -import {getGlobal} from '../../../src/prebidGlobal.js'; +import { getGlobal } from '../../../src/prebidGlobal.js'; describe('mabidderBidAdapter', () => { const adapter = newBidder(spec) diff --git a/test/spec/modules/madvertiseBidAdapter_spec.js b/test/spec/modules/madvertiseBidAdapter_spec.js index 966d5113105..43541b02b5a 100644 --- a/test/spec/modules/madvertiseBidAdapter_spec.js +++ b/test/spec/modules/madvertiseBidAdapter_spec.js @@ -1,7 +1,7 @@ -import {expect} from 'chai'; -import {config} from 'src/config'; +import { expect } from 'chai'; +import { config } from 'src/config'; import * as utils from 'src/utils'; -import {spec} from 'modules/madvertiseBidAdapter'; +import { spec } from 'modules/madvertiseBidAdapter'; describe('madvertise adapater', () => { describe('Test validate req', () => { @@ -155,19 +155,21 @@ describe('madvertise adapater', () => { age: 25, } }; - const resp = spec.interpretResponse({body: { - requestId: 'REQUEST_ID', - cpm: 1, - ad: '

I am an ad

', - Width: 320, - height: 50, - creativeId: 'CREATIVE_ID', - dealId: 'DEAL_ID', - ttl: 180, - currency: 'EUR', - netRevenue: true, - adomain: ['madvertise.com'] - }}, {bidId: bid.bidId}); + const resp = spec.interpretResponse({ + body: { + requestId: 'REQUEST_ID', + cpm: 1, + ad: '

I am an ad

', + Width: 320, + height: 50, + creativeId: 'CREATIVE_ID', + dealId: 'DEAL_ID', + ttl: 180, + currency: 'EUR', + netRevenue: true, + adomain: ['madvertise.com'] + } + }, { bidId: bid.bidId }); expect(resp).to.exist.and.to.be.a('array'); expect(resp[0]).to.have.property('requestId', bid.bidId); @@ -197,7 +199,7 @@ describe('madvertise adapater', () => { age: 25, } }; - const resp = spec.interpretResponse({body: null}, {bidId: bid.bidId}); + const resp = spec.interpretResponse({ body: null }, { bidId: bid.bidId }); expect(resp).to.exist.and.to.be.a('array').that.is.empty; }); diff --git a/test/spec/modules/magniteAnalyticsAdapter_spec.js b/test/spec/modules/magniteAnalyticsAdapter_spec.js index 83a5b83f774..aedf8a5c2d1 100644 --- a/test/spec/modules/magniteAnalyticsAdapter_spec.js +++ b/test/spec/modules/magniteAnalyticsAdapter_spec.js @@ -25,7 +25,6 @@ const { BID_WON, BID_TIMEOUT, BILLABLE_EVENT, - SEAT_NON_BID, PBS_ANALYTICS, BID_REJECTED } = EVENTS; @@ -169,7 +168,7 @@ const MOCK = { getStatusCode: () => 1, metrics }, - SEAT_NON_BID: { + PBS_ANALYTICS: { auctionId: '99785e47-a7c8-4c8a-ae05-ef1c717a4b4d', seatnonbid: [{ seat: 'rubicon', @@ -2359,7 +2358,7 @@ describe('magnite analytics adapter', function () { accountId: 1001 } }); - seatnonbid = utils.deepClone(MOCK.SEAT_NON_BID); + seatnonbid = utils.deepClone(MOCK.PBS_ANALYTICS); }); it('adds seatnonbid info to bids array', () => { @@ -2379,12 +2378,12 @@ describe('magnite analytics adapter', function () { it('adjusts the status according to the status map', () => { const statuses = [ - {code: 0, status: 'no-bid'}, - {code: 100, status: 'error', error: {code: 'request-error', description: 'general error'}}, - {code: 101, status: 'error', error: {code: 'timeout-error', description: 'prebid server timeout'}}, - {code: 200, status: 'rejected'}, - {code: 202, status: 'rejected'}, - {code: 301, status: 'rejected-ipf'} + { code: 0, status: 'no-bid' }, + { code: 100, status: 'error', error: { code: 'request-error', description: 'general error' } }, + { code: 101, status: 'error', error: { code: 'timeout-error', description: 'prebid server timeout' } }, + { code: 200, status: 'rejected' }, + { code: 202, status: 'rejected' }, + { code: 301, status: 'rejected-ipf' } ]; statuses.forEach((info, index) => { checkStatusAgainstCode(info.status, info.code, info.error, index); diff --git a/test/spec/modules/magniteBidAdapter_spec.js b/test/spec/modules/magniteBidAdapter_spec.js new file mode 100644 index 00000000000..e727690e891 --- /dev/null +++ b/test/spec/modules/magniteBidAdapter_spec.js @@ -0,0 +1,924 @@ +import { expect } from 'chai'; +import { + spec, + REQUEST_URL, + SYNC_URL, + masSizeOrdering, + resetMgniConf +} from 'modules/magniteBidAdapter.js'; +import { newBidder } from 'src/adapters/bidderFactory.js'; +import { BANNER, NATIVE, VIDEO } from 'src/mediaTypes.js'; +import { config } from 'src/config.js'; +// load modules that register ORTB processors +import 'src/prebid.js'; +import 'modules/currency.js'; +import 'modules/userId/index.js'; +import 'modules/multibid/index.js'; +import 'modules/priceFloors.js'; +import 'modules/consentManagementTcf.js'; +import 'modules/consentManagementUsp.js'; + +import { hook } from '../../../src/hook.js'; + +function getBannerBidRequest(overrides = {}) { + return { + bidder: 'magnite', + params: { + accountId: 1001, + siteId: 2001, + zoneId: 3001, + ...(overrides.params) + }, + adUnitCode: 'adunit-banner', + mediaTypes: { + banner: { + sizes: [[300, 250], [728, 90]] + } + }, + sizes: [[300, 250], [728, 90]], + bidId: 'bid-banner-1', + bidderRequestId: 'bidder-req-1', + auctionId: 'auction-1', + transactionId: 'trans-1', + ortb2Imp: { + ext: { + tid: 'trans-1' + } + }, + ...overrides + }; +} + +function getVideoBidRequest(overrides = {}) { + return { + bidder: 'magnite', + params: { + accountId: 1001, + siteId: 2001, + zoneId: 3001, + ...(overrides.params) + }, + adUnitCode: 'adunit-video', + mediaTypes: { + video: { + context: 'instream', + playerSize: [640, 480], + mimes: ['video/mp4'], + protocols: [1, 2], + linearity: 1, + ...(overrides.videoMediaType) + } + }, + sizes: [[640, 480]], + bidId: 'bid-video-1', + bidderRequestId: 'bidder-req-1', + auctionId: 'auction-1', + transactionId: 'trans-2', + ortb2Imp: { + ext: { + tid: 'trans-2' + } + }, + ...overrides + }; +} + +function getBidderRequest(overrides = {}) { + return { + bidderCode: 'magnite', + auctionId: 'auction-1', + bidderRequestId: 'bidder-req-1', + timeout: 3000, + refererInfo: { + page: 'https://example.com/test', + reachedTop: true + }, + ortb2: { + source: { + tid: 'auction-1' + } + }, + ...overrides + }; +} + +function getSampleOrtbBidResponse(impid, overrides = {}) { + return { + id: 'response-1', + seatbid: [{ + bid: [{ + impid: impid || 'bid-banner-1', + price: 3.5, + w: 300, + h: 250, + crid: 'creative-123', + dealid: 'deal-456', + adm: '
test ad
', + mtype: 1, + adomain: ['advertiser.com'], + ...overrides + }], + seat: 'magnite' + }], + cur: 'USD' + }; +} + +describe('the magnite adapter', function () { + before(() => { + hook.ready(); + }); + + const adapter = newBidder(spec); + + afterEach(function () { + config.resetConfig(); + resetMgniConf(); + }); + + describe('inherited functions', function () { + it('exists and is a function', function () { + expect(adapter.callBids).to.exist.and.to.be.a('function'); + }); + }); + + describe('spec properties', function () { + it('should have the correct bidder code', function () { + expect(spec.code).to.equal('magnite'); + }); + + it('should have the correct GVL ID', function () { + expect(spec.gvlid).to.equal(52); + }); + + it('should support banner, native, and video media types', function () { + expect(spec.supportedMediaTypes).to.deep.equal([BANNER, NATIVE, VIDEO]); + }); + + it('should have alwaysHasCapacity set to true', function () { + expect(spec.alwaysHasCapacity).to.be.true; + }); + }); + + describe('isBidRequestValid()', function () { + it('should return true when all required params are present', function () { + const bid = getBannerBidRequest(); + expect(spec.isBidRequestValid(bid)).to.be.true; + }); + + it('should return true when params are passed as strings that parse to numbers', function () { + const bid = getBannerBidRequest({ + params: { accountId: '1001', siteId: '2001', zoneId: '3001' } + }); + expect(spec.isBidRequestValid(bid)).to.be.true; + }); + + it('should return false when accountId is missing', function () { + const bid = getBannerBidRequest(); + delete bid.params.accountId; + expect(spec.isBidRequestValid(bid)).to.be.false; + }); + + it('should return false when siteId is missing', function () { + const bid = getBannerBidRequest(); + delete bid.params.siteId; + expect(spec.isBidRequestValid(bid)).to.be.false; + }); + + it('should return false when zoneId is missing', function () { + const bid = getBannerBidRequest(); + delete bid.params.zoneId; + expect(spec.isBidRequestValid(bid)).to.be.false; + }); + + it('should return false when accountId is not a number', function () { + const bid = getBannerBidRequest({ + params: { accountId: 'abc', siteId: 2001, zoneId: 3001 } + }); + expect(spec.isBidRequestValid(bid)).to.be.false; + }); + + it('should return false when params is undefined', function () { + const bid = getBannerBidRequest(); + delete bid.params; + expect(spec.isBidRequestValid(bid)).to.be.false; + }); + + it('should return false when params is empty', function () { + const bid = getBannerBidRequest(); + bid.params = {}; + expect(spec.isBidRequestValid(bid)).to.be.false; + }); + + it('should return true for video bid requests with valid params', function () { + const bid = getVideoBidRequest(); + expect(spec.isBidRequestValid(bid)).to.be.true; + }); + }); + + describe('buildRequests()', function () { + let bidderRequest; + + beforeEach(function () { + bidderRequest = getBidderRequest(); + }); + + it('should return an array of requests', function () { + const requests = spec.buildRequests([getBannerBidRequest()], bidderRequest); + expect(requests).to.be.an('array'); + expect(requests.length).to.be.greaterThan(0); + }); + + it('should use POST method', function () { + const requests = spec.buildRequests([getBannerBidRequest()], bidderRequest); + requests.forEach(req => { + expect(req.method).to.equal('POST'); + }); + }); + + it('should use the default REQUEST_URL', function () { + const requests = spec.buildRequests([getBannerBidRequest()], bidderRequest); + requests.forEach(req => { + expect(req.url).to.include(REQUEST_URL); + }); + }); + + it('should include accountId-siteId in the query string as "as" param', function () { + const bid = getBannerBidRequest(); + const requests = spec.buildRequests([bid], bidderRequest); + expect(requests[0].url).to.include('as=1001-2001'); + }); + + it('should include mediaType in the query string as "m" param', function () { + const bid = getBannerBidRequest(); + const requests = spec.buildRequests([bid], bidderRequest); + expect(requests[0].url).to.include('m=banner'); + }); + + it('should include the number of imps in the query string as "s" param', function () { + const bid = getBannerBidRequest(); + const requests = spec.buildRequests([bid], bidderRequest); + expect(requests[0].url).to.include('s=1'); + }); + + it('should use a custom bid endpoint from config', function () { + config.setConfig({ magnite: { bidEndpoint: 'https://custom.endpoint.com/bid' } }); + const requests = spec.buildRequests([getBannerBidRequest()], bidderRequest); + expect(requests[0].url).to.include('https://custom.endpoint.com/bid'); + }); + + describe('request grouping', function () { + it('should group bids by accountId-siteId-mediaType', function () { + const bid1 = getBannerBidRequest({ bidId: 'bid-1' }); + const bid2 = getBannerBidRequest({ + bidId: 'bid-2', + adUnitCode: 'adunit-banner-2', + params: { accountId: 1001, siteId: 2002, zoneId: 3002 } + }); + const requests = spec.buildRequests([bid1, bid2], bidderRequest); + // Different siteIds => separate requests + expect(requests.length).to.equal(2); + }); + + it('should combine bids with same accountId-siteId-mediaType into one request', function () { + const bid1 = getBannerBidRequest({ bidId: 'bid-1', adUnitCode: 'adunit-1' }); + const bid2 = getBannerBidRequest({ bidId: 'bid-2', adUnitCode: 'adunit-2' }); + const requests = spec.buildRequests([bid1, bid2], bidderRequest); + // Same accountId, siteId, mediaType => one request + expect(requests.length).to.equal(1); + expect(requests[0].url).to.include('s=2'); + }); + + it('should split multi-format bids into separate requests by mediaType', function () { + const multiformatBid = getBannerBidRequest({ + bidId: 'bid-multi', + mediaTypes: { + banner: { sizes: [[300, 250]] }, + video: { + context: 'instream', + playerSize: [640, 480], + mimes: ['video/mp4'], + protocols: [1, 2], + linearity: 1 + } + } + }); + const requests = spec.buildRequests([multiformatBid], bidderRequest); + expect(requests.length).to.equal(2); + + const mediaTypes = requests.map(r => { + const url = new URL(r.url); + return url.searchParams.get('m'); + }); + expect(mediaTypes).to.include('banner'); + expect(mediaTypes).to.include('video'); + }); + + it('should correctly handle mediaType with hyphen', function () { + const bid = getBannerBidRequest({ + bidId: 'bid-hyphen', + mediaTypes: { + 'video-outstream': {} + }, + params: { + accountId: 1001, + siteId: 2001, + zoneId: 3001, + enabledMediaTypes: ['video-outstream'] + } + }); + const requests = spec.buildRequests([bid], bidderRequest); + expect(requests.length).to.equal(1); + const url = new URL(requests[0].url); + expect(url.searchParams.get('m')).to.equal('video-outstream'); + expect(url.searchParams.get('as')).to.equal('1001-2001'); + }); + }); + + describe('chunking', function () { + it('should chunk requests at the default impLimit of 10', function () { + const bids = []; + for (let i = 0; i < 12; i++) { + bids.push(getBannerBidRequest({ + bidId: `bid-${i}`, + adUnitCode: `adunit-${i}` + })); + } + const requests = spec.buildRequests(bids, bidderRequest); + expect(requests.length).to.equal(2); + expect(requests[0].url).to.include('s=10'); + expect(requests[1].url).to.include('s=2'); + }); + + it('should respect custom impLimit from config', function () { + config.setConfig({ magnite: { impLimit: 3 } }); + const bids = []; + for (let i = 0; i < 7; i++) { + bids.push(getBannerBidRequest({ + bidId: `bid-${i}`, + adUnitCode: `adunit-${i}` + })); + } + const requests = spec.buildRequests(bids, bidderRequest); + expect(requests.length).to.equal(3); + expect(requests[0].url).to.include('s=3'); + expect(requests[1].url).to.include('s=3'); + expect(requests[2].url).to.include('s=1'); + }); + }); + + describe('enabledMediaTypes filtering', function () { + it('should filter out mediaTypes not in enabledMediaTypes', function () { + const bid = getBannerBidRequest({ + bidId: 'bid-filter', + mediaTypes: { + banner: { sizes: [[300, 250]] }, + video: { + context: 'instream', + playerSize: [640, 480], + mimes: ['video/mp4'], + protocols: [1, 2], + linearity: 1 + } + }, + params: { + accountId: 1001, + siteId: 2001, + zoneId: 3001, + enabledMediaTypes: ['banner'] + } + }); + const requests = spec.buildRequests([bid], bidderRequest); + // Only banner should be sent, video filtered out + expect(requests.length).to.equal(1); + const url = new URL(requests[0].url); + expect(url.searchParams.get('m')).to.equal('banner'); + }); + }); + + describe('ORTB request data', function () { + it('should produce valid ORTB data with imp array', function () { + const requests = spec.buildRequests([getBannerBidRequest()], bidderRequest); + const data = requests[0].data; + expect(data).to.be.an('object'); + expect(data.imp).to.be.an('array'); + expect(data.imp.length).to.equal(1); + }); + + it('should set imp.secure to 1', function () { + const requests = spec.buildRequests([getBannerBidRequest()], bidderRequest); + expect(requests[0].data.imp[0].secure).to.equal(1); + }); + + it('should not include tmax in the request', function () { + const requests = spec.buildRequests([getBannerBidRequest()], bidderRequest); + expect(requests[0].data.tmax).to.be.undefined; + }); + + it('should not include cur in the request', function () { + const requests = spec.buildRequests([getBannerBidRequest()], bidderRequest); + expect(requests[0].data.cur).to.be.undefined; + }); + + it('should set default channel name to pbjs', function () { + const requests = spec.buildRequests([getBannerBidRequest()], bidderRequest); + expect(requests[0].data.ext.prebid.channel.name).to.equal('pbjs'); + }); + + it('should use int_type from config for channel name', function () { + config.setConfig({ magnite: { int_type: 'custom_integration' } }); + const requests = spec.buildRequests([getBannerBidRequest()], bidderRequest); + expect(requests[0].data.ext.prebid.channel.name).to.equal('custom_integration'); + }); + + it('should move bidder params to dvplus namespace', function () { + const bid = getBannerBidRequest(); + const requests = spec.buildRequests([bid], bidderRequest); + const imp = requests[0].data.imp[0]; + expect(imp.ext.prebid.bidder.dvplus).to.be.an('object'); + expect(imp.ext.prebid.bidder.magnite).to.be.undefined; + }); + + it('should include accountId, siteId, zoneId in dvplus params', function () { + const bid = getBannerBidRequest(); + const requests = spec.buildRequests([bid], bidderRequest); + const dvplus = requests[0].data.imp[0].ext.prebid.bidder.dvplus; + expect(dvplus.accountId).to.equal(1001); + expect(dvplus.siteId).to.equal(2001); + expect(dvplus.zoneId).to.equal(3001); + }); + + it('should remove non-context mediaTypes from imp', function () { + const bid = getBannerBidRequest(); + const requests = spec.buildRequests([bid], bidderRequest); + const imp = requests[0].data.imp[0]; + // This is a banner context request + expect(imp.banner).to.exist; + expect(imp.video).to.be.undefined; + expect(imp.native).to.be.undefined; + }); + + it('should set pos from params.position when not already set', function () { + const bid = getBannerBidRequest({ + params: { accountId: 1001, siteId: 2001, zoneId: 3001, position: 'atf' } + }); + const requests = spec.buildRequests([bid], bidderRequest); + expect(requests[0].data.imp[0].banner.pos).to.equal(1); + }); + + it('should set pos to 3 for btf position', function () { + const bid = getBannerBidRequest({ + params: { accountId: 1001, siteId: 2001, zoneId: 3001, position: 'btf' } + }); + const requests = spec.buildRequests([bid], bidderRequest); + expect(requests[0].data.imp[0].banner.pos).to.equal(3); + }); + + it('should signal multi-format in ext.rp.rtb.formats when bid has multiple mediaTypes', function () { + const bid = getBannerBidRequest({ + bidId: 'bid-multi', + mediaTypes: { + banner: { sizes: [[300, 250]] }, + video: { + context: 'instream', + playerSize: [640, 480], + mimes: ['video/mp4'], + protocols: [1, 2], + linearity: 1 + } + } + }); + const requests = spec.buildRequests([bid], bidderRequest); + // The banner request should have the formats signal + const bannerReq = requests.find(r => r.url.includes('m=banner')); + expect(bannerReq.data.imp[0].ext.rp.rtb.formats).to.include('banner'); + expect(bannerReq.data.imp[0].ext.rp.rtb.formats).to.include('video'); + }); + + it('should not delete device.dnt from request', function () { + const requests = spec.buildRequests([getBannerBidRequest()], bidderRequest); + // dnt should be deleted by the adapter + expect(requests[0].data.device?.dnt).to.be.undefined; + }); + }); + + describe('visitor and inventory FPD cleaning', function () { + it('should wrap non-array visitor values in arrays', function () { + const bid = getBannerBidRequest({ + params: { + accountId: 1001, + siteId: 2001, + zoneId: 3001, + visitor: { age: '25', interests: ['sports', 'tech'] } + } + }); + const requests = spec.buildRequests([bid], bidderRequest); + const dvplus = requests[0].data.imp[0].ext.prebid.bidder.dvplus; + // Non-array value 'age' should be wrapped + expect(dvplus.visitor.age).to.deep.equal(['25']); + // Already-array value should remain unchanged + expect(dvplus.visitor.interests).to.deep.equal(['sports', 'tech']); + }); + + it('should wrap non-array inventory values in arrays', function () { + const bid = getBannerBidRequest({ + params: { + accountId: 1001, + siteId: 2001, + zoneId: 3001, + inventory: { rating: '5-star', prodtype: ['tech', 'mobile'] } + } + }); + const requests = spec.buildRequests([bid], bidderRequest); + const dvplus = requests[0].data.imp[0].ext.prebid.bidder.dvplus; + expect(dvplus.inventory.rating).to.deep.equal(['5-star']); + expect(dvplus.inventory.prodtype).to.deep.equal(['tech', 'mobile']); + }); + }); + + describe('ppuid handling', function () { + it('should set user.id from config user.id when ortb user.id is not set', function () { + config.setConfig({ 'user': { id: 'config-ppuid-123' } }); + const requests = spec.buildRequests([getBannerBidRequest()], bidderRequest); + expect(requests[0].data.user.id).to.equal('config-ppuid-123'); + }); + }); + }); + + describe('interpretResponse()', function () { + let bidderRequest; + + beforeEach(function () { + bidderRequest = getBidderRequest(); + }); + + it('should return an empty array when response body is missing', function () { + const bidRequest = getBannerBidRequest(); + const requests = spec.buildRequests([bidRequest], bidderRequest); + const bids = spec.interpretResponse({}, requests[0]); + expect(bids).to.be.an('array'); + expect(bids.length).to.equal(0); + }); + + it('should return an empty array when response body has no seatbid', function () { + const bidRequest = getBannerBidRequest(); + const requests = spec.buildRequests([bidRequest], bidderRequest); + const bids = spec.interpretResponse({ body: { nbr: 0 } }, requests[0]); + expect(bids).to.be.an('array'); + expect(bids.length).to.equal(0); + }); + + context('when there is a valid banner response', function () { + let bids; + + beforeEach(function () { + const bidRequest = getBannerBidRequest(); + const requests = spec.buildRequests([bidRequest], bidderRequest); + const response = getSampleOrtbBidResponse(bidRequest.bidId); + bids = spec.interpretResponse({ body: response }, requests[0]); + }); + + it('should return at least one bid', function () { + expect(bids).to.be.an('array'); + expect(bids.length).to.be.greaterThan(0); + }); + + it('should return a valid cpm', function () { + expect(bids[0].cpm).to.equal(3.5); + }); + + it('should return the correct creativeId', function () { + expect(bids[0].creativeId).to.equal('creative-123'); + }); + + it('should return the correct width and height', function () { + expect(bids[0].width).to.equal(300); + expect(bids[0].height).to.equal(250); + }); + + it('should return net revenue as true', function () { + expect(bids[0].netRevenue).to.be.true; + }); + + it('should return USD currency', function () { + expect(bids[0].currency).to.equal('USD'); + }); + + it('should return a TTL of 300 for banner', function () { + expect(bids[0].ttl).to.equal(300); + }); + }); + + context('when there is a valid video response', function () { + let bids; + + beforeEach(function () { + const bidRequest = getVideoBidRequest(); + const requests = spec.buildRequests([bidRequest], bidderRequest); + const response = getSampleOrtbBidResponse(bidRequest.bidId, { + mtype: 2, + adm: '', + w: 640, + h: 480 + }); + bids = spec.interpretResponse({ body: response }, requests[0]); + }); + + it('should return at least one bid', function () { + expect(bids).to.be.an('array'); + expect(bids.length).to.be.greaterThan(0); + }); + + it('should return a TTL of 900 for video', function () { + expect(bids[0].ttl).to.equal(900); + }); + }); + + describe('outstream video', function () { + let sandbox; + let clock; + + beforeEach(function () { + sandbox = sinon.createSandbox(); + sandbox.stub(globalThis.URL, 'createObjectURL').returns('blob:test-url'); + sandbox.stub(globalThis.URL, 'revokeObjectURL'); + clock = sandbox.useFakeTimers(); + }); + + afterEach(function () { + sandbox.restore(); + }); + + it('should create a blob URL and revoke it after TTL + 60s', function () { + const bidRequest = getVideoBidRequest({ + mediaTypes: { + video: { + context: 'outstream', + playerSize: [640, 480] + } + } + }); + const requests = spec.buildRequests([bidRequest], bidderRequest); + const response = getSampleOrtbBidResponse(bidRequest.bidId, { + mtype: 2, + adm: '', + w: 640, + h: 480 + }); + + const bids = spec.interpretResponse({ body: response }, requests[0]); + const bid = bids[0]; + + expect(bid.vastUrl).to.equal('blob:test-url'); + expect(globalThis.URL.createObjectURL.calledOnce).to.be.true; + + // TTL is 900 for video + const ttl = 900; + const delay = (ttl + 60) * 1000; + + clock.tick(delay); + expect(globalThis.URL.revokeObjectURL.calledWith('blob:test-url')).to.be.true; + }); + }); + + describe('adm_native handling', function () { + it('should move adm_native to adm when mediaType is native and adm_native is present', function () { + const bidRequest = getBannerBidRequest({ + bidId: 'bid-native-1', + adUnitCode: 'adunit-native', + mediaTypes: { + native: { + ortb: { + assets: [{ id: 0, required: 1, title: { len: 90 } }] + } + } + } + }); + const requests = spec.buildRequests([bidRequest], bidderRequest); + const nativeMarkup = '{"ver":"1.1","assets":[{"id":0,"title":{"text":"test"}}]}'; + const response = getSampleOrtbBidResponse('bid-native-1', { + adm_native: nativeMarkup, + adm: undefined, + mtype: 4 + }); + const bids = spec.interpretResponse({ body: response }, requests[0]); + expect(bids.length).to.be.greaterThan(0); + expect(bids[0].mediaType).to.equal('native'); + }); + + it('should not move adm_native to adm when mediaType is not native', function () { + const bidRequest = getBannerBidRequest(); + const requests = spec.buildRequests([bidRequest], bidderRequest); + const response = getSampleOrtbBidResponse(bidRequest.bidId, { + adm_native: '{"native":{"ver":"1.1","assets":[]}}', + adm: '
test ad
' + }); + const bids = spec.interpretResponse({ body: response }, requests[0]); + expect(bids.length).to.be.greaterThan(0); + // adm should remain unchanged since mediaType is banner, not native + expect(bids[0].ad).to.equal('
test ad
'); + }); + + it('should not modify adm when adm_native is not present', function () { + const bidRequest = getBannerBidRequest(); + const requests = spec.buildRequests([bidRequest], bidderRequest); + const response = getSampleOrtbBidResponse(bidRequest.bidId); + const bids = spec.interpretResponse({ body: response }, requests[0]); + expect(bids.length).to.be.greaterThan(0); + expect(bids[0].ad).to.equal('
test ad
'); + }); + }); + }); + + describe('getUserSyncs()', function () { + it('should return undefined when iframe is not enabled', function () { + const result = spec.getUserSyncs({ iframeEnabled: false }, []); + expect(result).to.be.undefined; + }); + + it('should return a sync object when iframe is enabled', function () { + const result = spec.getUserSyncs({ iframeEnabled: true }, []); + expect(result).to.be.an('object'); + expect(result.type).to.equal('iframe'); + expect(result.url).to.include(SYNC_URL); + }); + + it('should use the default SYNC_URL', function () { + const result = spec.getUserSyncs({ iframeEnabled: true }, []); + expect(result.url).to.equal(SYNC_URL); + }); + + it('should use a custom syncEndpoint from config', function () { + config.setConfig({ magnite: { syncEndpoint: 'https://custom.sync.com/usync.html' } }); + const result = spec.getUserSyncs({ iframeEnabled: true }, []); + expect(result.url).to.include('https://custom.sync.com/usync.html'); + }); + + describe('GDPR consent', function () { + it('should add gdpr and gdpr_consent params when gdprConsent is present', function () { + const gdprConsent = { + gdprApplies: true, + consentString: 'GDPR_CONSENT_STRING' + }; + const result = spec.getUserSyncs({ iframeEnabled: true }, [], gdprConsent); + expect(result.url).to.include('gdpr=1'); + expect(result.url).to.include('gdpr_consent=GDPR_CONSENT_STRING'); + }); + + it('should set gdpr=0 when gdprApplies is false', function () { + const gdprConsent = { + gdprApplies: false, + consentString: 'GDPR_CONSENT_STRING' + }; + const result = spec.getUserSyncs({ iframeEnabled: true }, [], gdprConsent); + expect(result.url).to.include('gdpr=0'); + }); + + it('should not add gdpr param when gdprApplies is not a boolean', function () { + const gdprConsent = { + gdprApplies: 'maybe', + consentString: 'GDPR_CONSENT_STRING' + }; + const result = spec.getUserSyncs({ iframeEnabled: true }, [], gdprConsent); + expect(result.url).to.not.include('gdpr='); + }); + + it('should not add gdpr_consent when consentString is not a string', function () { + const gdprConsent = { + gdprApplies: true, + consentString: 123 + }; + const result = spec.getUserSyncs({ iframeEnabled: true }, [], gdprConsent); + expect(result.url).to.include('gdpr=1'); + expect(result.url).to.not.include('gdpr_consent='); + }); + }); + + describe('USP consent', function () { + it('should add us_privacy param when uspConsent is present', function () { + const result = spec.getUserSyncs({ iframeEnabled: true }, [], undefined, '1YNN'); + expect(result.url).to.include('us_privacy=1YNN'); + }); + + it('should not add us_privacy param when uspConsent is undefined', function () { + const result = spec.getUserSyncs({ iframeEnabled: true }, [], undefined, undefined); + expect(result.url).to.not.include('us_privacy'); + }); + }); + + describe('GPP consent', function () { + it('should add gpp and gpp_sid params when gppConsent is present', function () { + const gppConsent = { + gppString: 'GPP_STRING', + applicableSections: [6, 7] + }; + const result = spec.getUserSyncs({ iframeEnabled: true }, [], undefined, undefined, gppConsent); + expect(result.url).to.include('gpp=GPP_STRING'); + expect(result.url).to.include('gpp_sid=6,7'); + }); + + it('should not add gpp params when gppConsent is undefined', function () { + const result = spec.getUserSyncs({ iframeEnabled: true }, [], undefined, undefined, undefined); + expect(result.url).to.not.include('gpp='); + }); + + it('should not add gpp params when gppString is missing', function () { + const gppConsent = { + applicableSections: [6, 7] + }; + const result = spec.getUserSyncs({ iframeEnabled: true }, [], undefined, undefined, gppConsent); + expect(result.url).to.not.include('gpp='); + }); + }); + + describe('combined consent params', function () { + it('should include all consent params when all are present', function () { + const gdprConsent = { gdprApplies: true, consentString: 'GDPR_STRING' }; + const gppConsent = { gppString: 'GPP_STRING', applicableSections: [6] }; + const result = spec.getUserSyncs({ iframeEnabled: true }, [], gdprConsent, '1YNN', gppConsent); + expect(result.url).to.include('gdpr=1'); + expect(result.url).to.include('gdpr_consent=GDPR_STRING'); + expect(result.url).to.include('us_privacy=1YNN'); + expect(result.url).to.include('gpp=GPP_STRING'); + expect(result.url).to.include('gpp_sid=6'); + }); + }); + }); + + describe('masSizeOrdering()', function () { + it('should sort 300x250 to the first position', function () { + const sizes = [ + { w: 728, h: 90 }, + { w: 300, h: 250 }, + { w: 160, h: 600 } + ]; + const sorted = masSizeOrdering(sizes); + expect(sorted[0]).to.deep.equal({ w: 300, h: 250 }); + }); + + it('should order priority sizes as 300x250, 728x90, 160x600', function () { + const sizes = [ + { w: 160, h: 600 }, + { w: 728, h: 90 }, + { w: 300, h: 250 } + ]; + const sorted = masSizeOrdering(sizes); + expect(sorted[0]).to.deep.equal({ w: 300, h: 250 }); + expect(sorted[1]).to.deep.equal({ w: 728, h: 90 }); + expect(sorted[2]).to.deep.equal({ w: 160, h: 600 }); + }); + + it('should place priority sizes before non-priority sizes', function () { + const sizes = [ + { w: 320, h: 50 }, + { w: 300, h: 250 }, + { w: 970, h: 250 } + ]; + const sorted = masSizeOrdering(sizes); + expect(sorted[0]).to.deep.equal({ w: 300, h: 250 }); + }); + + it('should maintain relative order for non-priority sizes', function () { + const sizes = [ + { w: 320, h: 50 }, + { w: 970, h: 250 } + ]; + const sorted = masSizeOrdering(sizes); + expect(sorted[0]).to.deep.equal({ w: 320, h: 50 }); + expect(sorted[1]).to.deep.equal({ w: 970, h: 250 }); + }); + + it('should handle a single size', function () { + const sizes = [{ w: 300, h: 250 }]; + const sorted = masSizeOrdering(sizes); + expect(sorted).to.deep.equal([{ w: 300, h: 250 }]); + }); + + it('should handle an empty array', function () { + const sorted = masSizeOrdering([]); + expect(sorted).to.deep.equal([]); + }); + }); + + describe('config merging', function () { + it('should read magnite config', function () { + config.setConfig({ magnite: { int_type: 'magnite_test' } }); + const requests = spec.buildRequests([getBannerBidRequest()], getBidderRequest()); + expect(requests[0].data.ext.prebid.channel.name).to.equal('magnite_test'); + }); + + it('should read rubicon config for backward compatibility', function () { + config.setConfig({ rubicon: { int_type: 'rubicon_test' } }); + const requests = spec.buildRequests([getBannerBidRequest()], getBidderRequest()); + expect(requests[0].data.ext.prebid.channel.name).to.equal('rubicon_test'); + }); + + it('should reset config with resetMgniConf', function () { + config.setConfig({ magnite: { int_type: 'custom' } }); + resetMgniConf(); + const requests = spec.buildRequests([getBannerBidRequest()], getBidderRequest()); + // After reset, should use default integration + expect(requests[0].data.ext.prebid.channel.name).to.equal('pbjs'); + }); + }); +}); diff --git a/test/spec/modules/malltvAnalyticsAdapter_spec.js b/test/spec/modules/malltvAnalyticsAdapter_spec.js index 8a88a486a58..cff1d9d4b21 100644 --- a/test/spec/modules/malltvAnalyticsAdapter_spec.js +++ b/test/spec/modules/malltvAnalyticsAdapter_spec.js @@ -115,7 +115,7 @@ describe('Malltv Prebid AnalyticsAdapter Testing', function () { }) describe('#getCachedAuction()', function() { - const existing = {timeoutBids: [{}]} + const existing = { timeoutBids: [{}] } malltvAnalyticsAdapter.cachedAuctions['test_auction_id'] = existing it('should get the existing cached object if it exists', function() { diff --git a/test/spec/modules/mantisBidAdapter_spec.js b/test/spec/modules/mantisBidAdapter_spec.js index 586a6a49181..f3110a6052e 100644 --- a/test/spec/modules/mantisBidAdapter_spec.js +++ b/test/spec/modules/mantisBidAdapter_spec.js @@ -1,7 +1,7 @@ -import {expect} from 'chai'; -import {spec, storage} from 'modules/mantisBidAdapter.js'; -import {newBidder} from 'src/adapters/bidderFactory.js'; -import {sfPostMessage, iframePostMessage} from 'modules/mantisBidAdapter'; +import { expect } from 'chai'; +import { spec, storage } from 'modules/mantisBidAdapter.js'; +import { newBidder } from 'src/adapters/bidderFactory.js'; +import { sfPostMessage, iframePostMessage } from 'modules/mantisBidAdapter'; describe('MantisAdapter', function () { const adapter = newBidder(spec); @@ -60,7 +60,7 @@ describe('MantisAdapter', function () { } ]); - iframePostMessage({innerHeight: 500, innerWidth: 500}, 'mantis', () => viewed = true); + iframePostMessage({ innerHeight: 500, innerWidth: 500 }, 'mantis', () => viewed = true); sandbox.clock.runAll(); @@ -121,19 +121,19 @@ describe('MantisAdapter', function () { ]; it('gdpr consent not required', function () { - const request = spec.buildRequests(bidRequests, {gdprConsent: {gdprApplies: false}}); + const request = spec.buildRequests(bidRequests, { gdprConsent: { gdprApplies: false } }); expect(request.url).not.to.include('consent=false'); }); it('gdpr consent required', function () { - const request = spec.buildRequests(bidRequests, {gdprConsent: {gdprApplies: true}}); + const request = spec.buildRequests(bidRequests, { gdprConsent: { gdprApplies: true } }); expect(request.url).to.include('consent=false'); }); it('usp consent', function () { - const request = spec.buildRequests(bidRequests, {uspConsent: 'foobar'}); + const request = spec.buildRequests(bidRequests, { uspConsent: 'foobar' }); expect(request.url).to.include('usp=foobar'); }); @@ -255,7 +255,7 @@ describe('MantisAdapter', function () { ]; let bidderRequest; - const result = spec.interpretResponse(response, {bidderRequest}); + const result = spec.interpretResponse(response, { bidderRequest }); expect(result[0]).to.deep.equal(expectedResponse[0]); }); @@ -296,7 +296,7 @@ describe('MantisAdapter', function () { ]; let bidderRequest; - const result = spec.interpretResponse(response, {bidderRequest}); + const result = spec.interpretResponse(response, { bidderRequest }); expect(result[0]).to.deep.equal(expectedResponse[0]); }); @@ -339,7 +339,7 @@ describe('MantisAdapter', function () { sandbox.stub(storage, 'hasLocalStorage').returns(true); const spy = sandbox.spy(storage, 'setDataInLocalStorage'); - const result = spec.interpretResponse(response, {bidderRequest}); + const result = spec.interpretResponse(response, { bidderRequest }); expect(spy.calledWith('mantis:uuid', 'uuid')); expect(result[0]).to.deep.equal(expectedResponse[0]); @@ -354,7 +354,7 @@ describe('MantisAdapter', function () { }; let bidderRequest; - const result = spec.interpretResponse(response, {bidderRequest}); + const result = spec.interpretResponse(response, { bidderRequest }); expect(result.length).to.equal(0); }); }); diff --git a/test/spec/modules/marsmediaBidAdapter_spec.js b/test/spec/modules/marsmediaBidAdapter_spec.js index 30c68601767..86fd4275786 100644 --- a/test/spec/modules/marsmediaBidAdapter_spec.js +++ b/test/spec/modules/marsmediaBidAdapter_spec.js @@ -1,8 +1,8 @@ import { spec } from 'modules/marsmediaBidAdapter.js'; import * as utils from 'src/utils.js'; -import * as dnt from 'libraries/dnt/index.js'; import { config } from 'src/config.js'; import { internal, resetWinDimensions } from '../../../src/utils.js'; +import * as adUnits from 'src/utils/adUnits'; var marsAdapter = spec; @@ -32,13 +32,15 @@ describe('marsmedia adapter tests', function () { }; win = { document: { - visibilityState: 'visible' + visibilityState: 'visible', + documentElement: { + clientWidth: 800, + clientHeight: 600 + } }, location: { href: 'http://location' }, - innerWidth: 800, - innerHeight: 600 }; this.defaultBidderRequest = { 'refererInfo': { @@ -71,7 +73,7 @@ describe('marsmedia adapter tests', function () { ]; sandbox = sinon.createSandbox(); - sandbox.stub(document, 'getElementById').withArgs('Unit-Code').returns(element); + sandbox.stub(adUnits, 'getAdUnitElement').returns(element); sandbox.stub(utils, 'getWindowTop').returns(win); sandbox.stub(utils, 'getWindowSelf').returns(win); }); @@ -381,7 +383,7 @@ describe('marsmedia adapter tests', function () { 'zoneId': 9999 }, 'mediaTypes': { - 'banner': {'sizes': [['400', '500'], ['4n0', '5g0']]} + 'banner': { 'sizes': [['400', '500'], ['4n0', '5g0']] } }, 'adUnitCode': 'Unit-Code', 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', @@ -397,15 +399,10 @@ describe('marsmedia adapter tests', function () { expect(openrtbRequest.imp[0].banner.format.length).to.equal(1); }); - it('dnt is correctly set to 1', function () { - var dntStub = sinon.stub(dnt, 'getDNT').returns(1); - + it('dnt is always 0', function () { var bidRequest = marsAdapter.buildRequests(this.defaultBidRequestList, this.defaultBidderRequest); - - dntStub.restore(); - const openrtbRequest = JSON.parse(bidRequest.data); - expect(openrtbRequest.device.dnt).to.equal(1); + expect(openrtbRequest.device.dnt).to.equal(0); }); it('supports string video sizes', function () { @@ -506,6 +503,8 @@ describe('marsmedia adapter tests', function () { context('when element is fully in view', function() { it('returns 100', function() { + sandbox.stub(internal, 'getWindowTop').returns(win); + resetWinDimensions(); Object.assign(element, { width: 600, height: 400 }); const request = marsAdapter.buildRequests(this.defaultBidRequestList, this.defaultBidderRequest); const openrtbRequest = JSON.parse(request.data); @@ -530,7 +529,6 @@ describe('marsmedia adapter tests', function () { const request = marsAdapter.buildRequests(this.defaultBidRequestList, this.defaultBidderRequest); const openrtbRequest = JSON.parse(request.data); expect(openrtbRequest.imp[0].ext.viewability).to.equal(75); - internal.getWindowTop.restore(); }); }); diff --git a/test/spec/modules/mathildeadsBidAdapter_spec.js b/test/spec/modules/mathildeadsBidAdapter_spec.js index 05336197872..21b7b4a7d21 100644 --- a/test/spec/modules/mathildeadsBidAdapter_spec.js +++ b/test/spec/modules/mathildeadsBidAdapter_spec.js @@ -432,7 +432,7 @@ describe('MathildeAdsBidAdapter', function () { const syncData = spec.getUserSyncs({}, {}, { consentString: 'ALL', gdprApplies: true, - }, {}); + }, undefined); expect(syncData).to.be.an('array').which.is.not.empty; expect(syncData[0]).to.be.an('object') expect(syncData[0].type).to.be.a('string') @@ -441,9 +441,7 @@ describe('MathildeAdsBidAdapter', function () { expect(syncData[0].url).to.equal('https://cs2.mathilde-ads.com/image?pbjs=1&gdpr=1&gdpr_consent=ALL&coppa=0') }); it('Should return array of objects with proper sync config , include CCPA', function() { - const syncData = spec.getUserSyncs({}, {}, {}, { - consentString: '1---' - }); + const syncData = spec.getUserSyncs({}, {}, {}, '1---'); expect(syncData).to.be.an('array').which.is.not.empty; expect(syncData[0]).to.be.an('object') expect(syncData[0].type).to.be.a('string') @@ -452,7 +450,7 @@ describe('MathildeAdsBidAdapter', function () { expect(syncData[0].url).to.equal('https://cs2.mathilde-ads.com/image?pbjs=1&ccpa_consent=1---&coppa=0') }); it('Should return array of objects with proper sync config , include GPP', function() { - const syncData = spec.getUserSyncs({}, {}, {}, {}, { + const syncData = spec.getUserSyncs({}, {}, {}, undefined, { gppString: 'abc123', applicableSections: [8] }); diff --git a/test/spec/modules/mediaConsortiumBidAdapter_spec.js b/test/spec/modules/mediaConsortiumBidAdapter_spec.js index d19e4f861f1..14e3521ad74 100644 --- a/test/spec/modules/mediaConsortiumBidAdapter_spec.js +++ b/test/spec/modules/mediaConsortiumBidAdapter_spec.js @@ -1,6 +1,6 @@ import { expect } from 'chai'; import { spec, OPTIMIZATIONS_STORAGE_KEY, getOptimizationsFromLocalStorage } from 'modules/mediaConsortiumBidAdapter.js'; -import {getGlobal} from '../../../src/prebidGlobal.js'; +import { getGlobal } from '../../../src/prebidGlobal.js'; const BANNER_BID = { adUnitCode: 'dfp_ban_atf', @@ -166,7 +166,7 @@ describe('Media Consortium Bid Adapter', function () { } const bids = [BANNER_BID] - const [syncRequest, auctionRequest] = spec.buildRequests(bids, {...bidderRequest, bids}); + const [syncRequest, auctionRequest] = spec.buildRequests(bids, { ...bidderRequest, bids }); expect(syncRequest.data).to.deep.equal(builtSyncRequest) expect(auctionRequest.data).to.deep.equal(builtBidRequest) @@ -215,7 +215,7 @@ describe('Media Consortium Bid Adapter', function () { } const bids = [VIDEO_BID] - const [syncRequest, auctionRequest] = spec.buildRequests(bids, {...bidderRequest, bids}); + const [syncRequest, auctionRequest] = spec.buildRequests(bids, { ...bidderRequest, bids }); expect(syncRequest.data).to.deep.equal(builtSyncRequest) expect(auctionRequest.data).to.deep.equal(builtBidRequest) @@ -264,7 +264,7 @@ describe('Media Consortium Bid Adapter', function () { } const bids = [MULTI_MEDIATYPES_BID] - const [syncRequest, auctionRequest] = spec.buildRequests(bids, {...bidderRequest, bids}); + const [syncRequest, auctionRequest] = spec.buildRequests(bids, { ...bidderRequest, bids }); expect(syncRequest.data).to.deep.equal(builtSyncRequest) expect(auctionRequest.data).to.deep.equal(builtBidRequest) @@ -278,12 +278,12 @@ describe('Media Consortium Bid Adapter', function () { it('should not build a request if optimizations are there for the adunit code', function () { const bids = [BANNER_BID] const optimizations = { - [bids[0].adUnitCode]: {isEnabled: false, expiresAt: Date.now() + 600000} + [bids[0].adUnitCode]: { isEnabled: false, expiresAt: Date.now() + 600000 } } localStorage.setItem(OPTIMIZATIONS_STORAGE_KEY, JSON.stringify(optimizations)) - const requests = spec.buildRequests(bids, {...bidderRequest, bids}); + const requests = spec.buildRequests(bids, { ...bidderRequest, bids }); localStorage.removeItem(OPTIMIZATIONS_STORAGE_KEY) @@ -301,7 +301,7 @@ describe('Media Consortium Bid Adapter', function () { impressions: [{ id: MULTI_MEDIATYPES_WITH_INVALID_VIDEO_CONTEXT.bidId, adUnitCode: MULTI_MEDIATYPES_WITH_INVALID_VIDEO_CONTEXT.adUnitCode, - mediaTypes: {banner: MULTI_MEDIATYPES_WITH_INVALID_VIDEO_CONTEXT.mediaTypes.banner} + mediaTypes: { banner: MULTI_MEDIATYPES_WITH_INVALID_VIDEO_CONTEXT.mediaTypes.banner } }], device: { w: 1200, @@ -330,9 +330,9 @@ describe('Media Consortium Bid Adapter', function () { const invalidVideoBids = [VIDEO_BID_WITH_MISSING_CONTEXT] const multiMediatypesBidWithInvalidVideo = [MULTI_MEDIATYPES_WITH_INVALID_VIDEO_CONTEXT] - expect(spec.buildRequests(invalidVideoBids, {...bidderRequest, bids: invalidVideoBids})).to.be.undefined + expect(spec.buildRequests(invalidVideoBids, { ...bidderRequest, bids: invalidVideoBids })).to.be.undefined - const [syncRequest, auctionRequest] = spec.buildRequests(multiMediatypesBidWithInvalidVideo, {...bidderRequest, bids: multiMediatypesBidWithInvalidVideo}) + const [syncRequest, auctionRequest] = spec.buildRequests(multiMediatypesBidWithInvalidVideo, { ...bidderRequest, bids: multiMediatypesBidWithInvalidVideo }) expect(syncRequest.data).to.deep.equal(builtSyncRequest) expect(auctionRequest.data).to.deep.equal(builtBidRequest) @@ -341,7 +341,7 @@ describe('Media Consortium Bid Adapter', function () { describe('interpretResponse', function () { it('should return an empty array if the response is invalid', function () { - expect(spec.interpretResponse({body: 'INVALID_BODY'}, {})).to.deep.equal([]); + expect(spec.interpretResponse({ body: 'INVALID_BODY' }, {})).to.deep.equal([]); }) it('should return a formatted banner bid', function () { @@ -360,7 +360,7 @@ describe('Media Consortium Bid Adapter', function () { creative: { id: 'CREATIVE_ID', mediaType: 'banner', - size: {width: 320, height: 250}, + size: { width: 320, height: 250 }, markup: '
1
' } }, @@ -428,7 +428,7 @@ describe('Media Consortium Bid Adapter', function () { creative: { id: 'CREATIVE_ID', mediaType: 'video', - size: {width: 320, height: 250}, + size: { width: 320, height: 250 }, markup: '...', rendering: { video: { @@ -486,7 +486,7 @@ describe('Media Consortium Bid Adapter', function () { creative: { id: 'CREATIVE_ID', mediaType: 'video', - size: {width: 320, height: 250}, + size: { width: 320, height: 250 }, markup: '...' } }, @@ -525,7 +525,7 @@ describe('Media Consortium Bid Adapter', function () { creative: { id: 'CREATIVE_ID', mediaType: 'video', - size: {width: 320, height: 250}, + size: { width: 320, height: 250 }, markup: '...' } }, @@ -555,8 +555,8 @@ describe('Media Consortium Bid Adapter', function () { describe('getUserSyncs', function () { it('should return an empty response if the response is invalid or missing data', function () { - expect(spec.getUserSyncs(null, [{body: 'INVALID_BODY'}])).to.be.undefined; - expect(spec.getUserSyncs(null, [{body: 'INVALID_BODY'}, {body: 'INVALID_BODY'}])).to.be.undefined; + expect(spec.getUserSyncs(null, [{ body: 'INVALID_BODY' }])).to.be.undefined; + expect(spec.getUserSyncs(null, [{ body: 'INVALID_BODY' }, { body: 'INVALID_BODY' }])).to.be.undefined; }) it('should return an array of user syncs', function () { @@ -564,9 +564,9 @@ describe('Media Consortium Bid Adapter', function () { { body: { bidders: [ - {type: 'image', url: 'https://test-url.com'}, - {type: 'redirect', url: 'https://test-url.com'}, - {type: 'iframe', url: 'https://test-url.com'} + { type: 'image', url: 'https://test-url.com' }, + { type: 'redirect', url: 'https://test-url.com' }, + { type: 'iframe', url: 'https://test-url.com' } ] } }, @@ -576,9 +576,9 @@ describe('Media Consortium Bid Adapter', function () { ] const formattedUserSyncs = [ - {type: 'image', url: 'https://test-url.com'}, - {type: 'image', url: 'https://test-url.com'}, - {type: 'iframe', url: 'https://test-url.com'} + { type: 'image', url: 'https://test-url.com' }, + { type: 'image', url: 'https://test-url.com' }, + { type: 'iframe', url: 'https://test-url.com' } ] expect(spec.getUserSyncs(null, serverResponses)).to.deep.equal(formattedUserSyncs); @@ -601,7 +601,7 @@ describe('Media Consortium Bid Adapter', function () { creative: { id: 'CREATIVE_ID', mediaType: 'video', - size: {width: 320, height: 250}, + size: { width: 320, height: 250 }, markup: '...', rendering: { video: { @@ -659,7 +659,7 @@ describe('Media Consortium Bid Adapter', function () { creative: { id: 'CREATIVE_ID', mediaType: 'video', - size: {width: 320, height: 250}, + size: { width: 320, height: 250 }, markup: '...', rendering: { video: { @@ -720,7 +720,7 @@ describe('Media Consortium Bid Adapter', function () { creative: { id: 'CREATIVE_ID', mediaType: 'video', - size: {width: 320, height: 250}, + size: { width: 320, height: 250 }, markup: '...' } }, diff --git a/test/spec/modules/mediabramaBidAdapter_spec.js b/test/spec/modules/mediabramaBidAdapter_spec.js index 74c2ac48e5a..44aae9ccb67 100644 --- a/test/spec/modules/mediabramaBidAdapter_spec.js +++ b/test/spec/modules/mediabramaBidAdapter_spec.js @@ -1,5 +1,5 @@ -import {expect} from 'chai'; -import {spec} from '../../../modules/mediabramaBidAdapter.js'; +import { expect } from 'chai'; +import { spec } from '../../../modules/mediabramaBidAdapter.js'; import { BANNER } from '../../../src/mediaTypes.js'; import * as utils from '../../../src/utils.js'; diff --git a/test/spec/modules/mediaeyesBidAdapter_spec.js b/test/spec/modules/mediaeyesBidAdapter_spec.js index 33c5981c530..f320cbde522 100644 --- a/test/spec/modules/mediaeyesBidAdapter_spec.js +++ b/test/spec/modules/mediaeyesBidAdapter_spec.js @@ -5,6 +5,7 @@ import * as utils from '../../../src/utils.js'; describe('mediaeyes adapter', function () { let request; let bannerResponse, invalidResponse; + let videoRequest, videoResponse; beforeEach(function () { request = [ @@ -53,6 +54,38 @@ describe('mediaeyes adapter', function () { } }; + videoRequest = [ + { + bidder: 'mediaeyes', + mediaTypes: { + video: { + context: 'instream', + playerSize: [[640, 480]], + mimes: ['video/mp4'] + } + }, + params: { + itemId: "4d27f3cc8bbd5bd153045e", + bidFloor: 0.01 + } + } + ]; + videoResponse = { + body: { + id: "3c51f851-56d8-4515-b4bb-e5a1612cede3", + seatbid: [{ + bid: [{ + impid: "3db1c7f2867eb5", + price: 0.5, + crid: "6808552", + w: 640, + h: 480, + adm: "...", + adomain: ["mediaeyes.vn"], + }] + }] + } + }; }); describe('validations', function () { @@ -114,7 +147,7 @@ describe('mediaeyes adapter', function () { } let bidderRequest; - const result = spec.interpretResponse(response, {bidderRequest}); + const result = spec.interpretResponse(response, { bidderRequest }); expect(result.length).to.equal(0); }); }) @@ -180,4 +213,53 @@ describe('mediaeyes adapter', function () { expect(data.bidfloor).to.equal(1); }); }); + + describe('video validations', function () { + it('isBidValid : video itemId is passed', function () { + const bid = { + bidder: 'mediaeyes', + mediaTypes: { + video: { + playerSize: [[640, 480]] + } + }, + params: { + itemId: '4d27f3cc8bbd5bd153045e' + } + }; + + const isValid = spec.isBidRequestValid(bid); + expect(isValid).to.equals(true); + }); + }); + + describe('video request building', function () { + it('should build video request correctly', function () { + const bidRequest = spec.buildRequests(videoRequest); + + const data = JSON.parse(bidRequest[0].data); + + expect(data.imp[0]).to.have.property('video'); + expect(data.imp[0].video).to.have.property('w'); + expect(data.imp[0].video).to.have.property('h'); + }); + }); + + describe('video responses processing', function () { + it('should return fully initialized video bid-response', function () { + const bidRequest = spec.buildRequests(videoRequest); + + const resp = spec.interpretResponse(videoResponse, bidRequest[0])[0]; + + expect(resp).to.have.property('requestId'); + expect(resp).to.have.property('cpm'); + expect(resp).to.have.property('width'); + expect(resp).to.have.property('height'); + expect(resp).to.have.property('creativeId'); + expect(resp).to.have.property('currency'); + expect(resp).to.have.property('ttl'); + + expect(resp).to.have.property('meta'); + }); + }); }); diff --git a/test/spec/modules/mediaforceBidAdapter_spec.js b/test/spec/modules/mediaforceBidAdapter_spec.js index 6ae86dc0801..fae28156b08 100644 --- a/test/spec/modules/mediaforceBidAdapter_spec.js +++ b/test/spec/modules/mediaforceBidAdapter_spec.js @@ -1,8 +1,7 @@ -import {assert} from 'chai'; -import {spec, resolveFloor} from 'modules/mediaforceBidAdapter.js'; +import { assert } from 'chai'; +import { spec, resolveFloor } from 'modules/mediaforceBidAdapter.js'; import * as utils from '../../../src/utils.js'; -import { getDNT } from 'libraries/dnt/index.js'; -import {BANNER, NATIVE, VIDEO} from '../../../src/mediaTypes.js'; +import { BANNER, NATIVE, VIDEO } from '../../../src/mediaTypes.js'; describe('mediaforce bid adapter', function () { let sandbox; @@ -44,7 +43,7 @@ describe('mediaforce bid adapter', function () { it('should return false when valid params are not passed', function () { const bid = utils.deepClone(defaultBid); - bid.params = {placement_id: '', publisher_id: ''}; + bid.params = { placement_id: '', publisher_id: '' }; assert.equal(spec.isBidRequestValid(bid), false); }); @@ -55,7 +54,7 @@ describe('mediaforce bid adapter', function () { sizes: [[300, 250]] } }; - bid.params = {publisher_id: 2, placement_id: '123'}; + bid.params = { publisher_id: 2, placement_id: '123' }; assert.equal(spec.isBidRequestValid(bid), true); }); }); @@ -126,7 +125,7 @@ describe('mediaforce bid adapter', function () { ] }; - const dnt = getDNT() ? 1 : 0; + const dnt = 0; // DNT deprecated by W3C; Prebid no longer supports DNT const secure = window.location.protocol === 'https:' ? 1 : 0; const pageUrl = window.location.href; const timeout = 1500; @@ -143,7 +142,7 @@ describe('mediaforce bid adapter', function () { }, site: { id: defaultBid.params.publisher_id, - publisher: {id: defaultBid.params.publisher_id}, + publisher: { id: defaultBid.params.publisher_id }, ref: encodeURIComponent(refererInfo.ref), page: pageUrl, }, @@ -162,14 +161,14 @@ describe('mediaforce bid adapter', function () { transactionId: defaultBid.ortb2Imp.ext.tid, } }, - banner: {w: 300, h: 250}, + banner: { w: 300, h: 250 }, native: { ver: '1.2', request: { assets: [ - {id: 1, title: {len: 800}, required: 1}, - {id: 3, img: {w: 300, h: 250, type: 3}, required: 1}, - {id: 5, data: {type: 1}, required: 0} + { id: 1, title: { len: 800 }, required: 1 }, + { id: 3, img: { w: 300, h: 250, type: 3 }, required: 1 }, + { id: 5, data: { type: 1 }, required: 0 } ], context: 1, plcmttype: 1, @@ -213,10 +212,10 @@ describe('mediaforce bid adapter', function () { placement_id: '203', transactionId: '8df76688-1618-417a-87b1-60ad046841c9' } - ].map(({publisher_id, placement_id, transactionId}) => { + ].map(({ publisher_id, placement_id, transactionId }) => { return { bidder: 'mediaforce', - params: {publisher_id, placement_id}, + params: { publisher_id, placement_id }, mediaTypes: { banner: { sizes: [[300, 250], [600, 400]] @@ -330,7 +329,7 @@ describe('mediaforce bid adapter', function () { const [request] = spec.buildRequests([bid]); const data = JSON.parse(request.data); - assert.deepEqual(data.imp[0].banner, {w: 300, h: 600, format: [{w: 300, h: 250}]}); + assert.deepEqual(data.imp[0].banner, { w: 300, h: 600, format: [{ w: 300, h: 250 }] }); }); it('should skip banner with empty sizes', function () { @@ -370,7 +369,7 @@ describe('mediaforce bid adapter', function () { }, site: { id: 'pub123', - publisher: {id: 'pub123'}, + publisher: { id: 'pub123' }, ref: encodeURIComponent(refererInfo.ref), page: pageUrl, }, @@ -389,7 +388,7 @@ describe('mediaforce bid adapter', function () { transactionId: 'd45dd707-a418-42ec-b8a7-b70a6c6fab0b' } }, - banner: {w: 300, h: 250, format: [{w: 600, h: 400}]}, + banner: { w: 300, h: 250, format: [{ w: 600, h: 400 }] }, }, { tagid: '203', secure: secure, @@ -399,7 +398,7 @@ describe('mediaforce bid adapter', function () { transactionId: 'd45dd707-a418-42ec-b8a7-b70a6c6fab0b' } }, - banner: {w: 300, h: 250, format: [{w: 600, h: 400}]}, + banner: { w: 300, h: 250, format: [{ w: 600, h: 400 }] }, }, { tagid: '203', secure: secure, @@ -409,7 +408,7 @@ describe('mediaforce bid adapter', function () { transactionId: '8df76688-1618-417a-87b1-60ad046841c9' } }, - banner: {w: 300, h: 250, format: [{w: 600, h: 400}]}, + banner: { w: 300, h: 250, format: [{ w: 600, h: 400 }] }, }] } }, @@ -426,7 +425,7 @@ describe('mediaforce bid adapter', function () { }, site: { id: 'pub124', - publisher: {id: 'pub124'}, + publisher: { id: 'pub124' }, ref: encodeURIComponent(refererInfo.ref), page: pageUrl, }, @@ -445,7 +444,7 @@ describe('mediaforce bid adapter', function () { transactionId: 'd45dd707-a418-42ec-b8a7-b70a6c6fab0b' } }, - banner: {w: 300, h: 250, format: [{w: 600, h: 400}]}, + banner: { w: 300, h: 250, format: [{ w: 600, h: 400 }] }, }] } } @@ -527,20 +526,20 @@ describe('mediaforce bid adapter', function () { ext: { advertiser_name: 'MediaForce', native: { - link: {url: nativeLink}, + link: { url: nativeLink }, assets: [{ id: 1, - title: {text: titleText}, + title: { text: titleText }, required: 1 }, { id: 3, img: imgData }, { id: 5, - data: {value: sponsoredByValue} + data: { value: sponsoredByValue } }, { id: 4, - data: {value: bodyValue} + data: { value: bodyValue } }], imptrackers: [nativeTracker], ver: '1' @@ -603,20 +602,20 @@ describe('mediaforce bid adapter', function () { const bodyValue = 'Drivers With No Tickets In 3 Years Should Do This On June'; const adm = JSON.stringify({ native: { - link: {url: nativeLink}, + link: { url: nativeLink }, assets: [{ id: 1, - title: {text: titleText}, + title: { text: titleText }, required: 1 }, { id: 3, img: imgData }, { id: 5, - data: {value: sponsoredByValue} + data: { value: sponsoredByValue } }, { id: 4, - data: {value: bodyValue} + data: { value: bodyValue } }], imptrackers: [nativeTracker], ver: '1' diff --git a/test/spec/modules/mediafuseBidAdapter_spec.js b/test/spec/modules/mediafuseBidAdapter_spec.js index ff806d91f2c..70a2dc43886 100644 --- a/test/spec/modules/mediafuseBidAdapter_spec.js +++ b/test/spec/modules/mediafuseBidAdapter_spec.js @@ -1,1451 +1,1803 @@ -import { expect } from 'chai'; -import { spec } from 'modules/mediafuseBidAdapter.js'; -import { newBidder } from 'src/adapters/bidderFactory.js'; -import * as bidderFactory from 'src/adapters/bidderFactory.js'; -import { auctionManager } from 'src/auctionManager.js'; -import { deepClone } from 'src/utils.js'; -import { config } from 'src/config.js'; -import {getGlobal} from '../../../src/prebidGlobal.js'; - -const ENDPOINT = 'https://ib.adnxs.com/ut/v3/prebid'; +/** + * mediafuseBidAdapter_spec.js — extended tests + * + * Tests for mediafuseBidAdapter.js covering buildRequests, interpretResponse, + * getUserSyncs, and lifecycle callbacks. + */ -describe('MediaFuseAdapter', function () { - const adapter = newBidder(spec); +import { expect } from 'chai'; +import { spec, storage } from 'modules/mediafuseBidAdapter.js'; +import { deepClone } from '../../../src/utils.js'; +import { config } from '../../../src/config.js'; +import * as utils from '../../../src/utils.js'; +import sinon from 'sinon'; + +// --------------------------------------------------------------------------- +// Shared fixtures +// --------------------------------------------------------------------------- +const BASE_BID = { + bidder: 'mediafuse', + adUnitCode: 'adunit-code', + bidId: 'bid-id-1', + params: { placementId: 12345 } +}; + +const BASE_BIDDER_REQUEST = { + auctionId: 'auction-1', + ortb2: { + site: { page: 'http://example.com', domain: 'example.com' }, + user: {} + }, + refererInfo: { + topmostLocation: 'http://example.com', + reachedTop: true, + numIframes: 0, + stack: ['http://example.com'] + }, + bids: [BASE_BID] +}; + +// --------------------------------------------------------------------------- +describe('mediafuseBidAdapter', function () { + let sandbox; + + beforeEach(function () { + sandbox = sinon.createSandbox(); + }); - describe('inherited functions', function () { - it('exists and is a function', function () { - expect(adapter.callBids).to.exist.and.to.be.a('function'); - }); + afterEach(function () { + sandbox.restore(); }); - describe('isBidRequestValid', function () { - const bid = { - 'bidder': 'mediafuse', - '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 () { - const invalidBid = Object.assign({}, bid); - delete invalidBid.params; - invalidBid.params = { - 'member': '1234', - 'invCode': 'ABCD' + // ------------------------------------------------------------------------- + // buildRequests — endpoint selection + // ------------------------------------------------------------------------- + describe('buildRequests - endpoint selection', function () { + it('should use simple endpoint when GDPR purpose 1 consent is missing', function () { + const bidderRequest = deepClone(BASE_BIDDER_REQUEST); + bidderRequest.gdprConsent = { + gdprApplies: true, + consentString: 'test-consent', + vendorData: { purpose: { consents: { 1: false } } } }; + const [req] = spec.buildRequests([deepClone(BASE_BID)], bidderRequest); + expect(req.url).to.include('adnxs-simple.com'); + }); + }); - expect(spec.isBidRequestValid(invalidBid)).to.equal(true); + // ------------------------------------------------------------------------- + // buildRequests — GPID + // ------------------------------------------------------------------------- + describe('buildRequests - GPID', function () { + it('should map ortb2Imp.ext.gpid into imp.ext.appnexus.gpid', function () { + const bid = deepClone(BASE_BID); + bid.ortb2Imp = { ext: { gpid: '/1234/home#header' } }; + const [req] = spec.buildRequests([bid], deepClone(BASE_BIDDER_REQUEST)); + expect(req.data.imp[0].ext.appnexus.gpid).to.equal('/1234/home#header'); }); + }); - it('should return false when required params are not passed', function () { - const invalidBid = Object.assign({}, bid); - delete invalidBid.params; - invalidBid.params = { - 'placementId': 0 - }; - expect(spec.isBidRequestValid(invalidBid)).to.equal(false); + // ------------------------------------------------------------------------- + // buildRequests — global keywords + // ------------------------------------------------------------------------- + describe('buildRequests - global keywords', function () { + it('should include mediafuseAuctionKeywords in request ext', function () { + sandbox.stub(config, 'getConfig').callsFake((key) => { + if (key === 'mediafuseAuctionKeywords') return { section: ['news', 'sports'] }; + return undefined; + }); + const [req] = spec.buildRequests([deepClone(BASE_BID)], deepClone(BASE_BIDDER_REQUEST)); + expect(req.data.ext.appnexus.keywords).to.include('section=news,sports'); }); }); - describe('buildRequests', function () { - let getAdUnitsStub; - const bidRequests = [ - { - 'bidder': 'mediafuse', - 'params': { - 'placementId': '10433394' - }, - 'adUnitCode': 'adunit-code', - 'sizes': [[300, 250], [300, 600]], - 'bidId': '30b31c1838de1e', - 'bidderRequestId': '22edbae2733bf6', - 'auctionId': '1d1a030790a475', - 'transactionId': '04f2659e-c005-4eb1-a57c-fa93145e3843' - } - ]; + // ------------------------------------------------------------------------- + // buildRequests — user params + // ------------------------------------------------------------------------- + describe('buildRequests - user params', function () { + it('should map age, gender, and numeric segments', function () { + const bid = deepClone(BASE_BID); + bid.params.user = { age: 35, gender: 'F', segments: [10, 20] }; + // bidderRequest.bids must contain the bid for the request() hook to find params.user + const bidderRequest = deepClone(BASE_BIDDER_REQUEST); + bidderRequest.bids = [bid]; + const [req] = spec.buildRequests([bid], bidderRequest); + expect(req.data.user.age).to.equal(35); + expect(req.data.user.gender).to.equal('F'); + expect(req.data.user.ext.segments).to.deep.equal([{ id: 10 }, { id: 20 }]); + }); - beforeEach(function() { - getAdUnitsStub = sinon.stub(auctionManager, 'getAdUnits').callsFake(function() { - return []; - }); + it('should map object-style segments and ignore invalid ones', function () { + const bid = deepClone(BASE_BID); + bid.params.user = { segments: [{ id: 99 }, 'bad', null] }; + const bidderRequest = deepClone(BASE_BIDDER_REQUEST); + bidderRequest.bids = [bid]; + const [req] = spec.buildRequests([bid], bidderRequest); + expect(req.data.user.ext.segments).to.deep.equal([{ id: 99 }]); }); + }); - afterEach(function() { - getAdUnitsStub.restore(); + // ------------------------------------------------------------------------- + // buildRequests — app params + // ------------------------------------------------------------------------- + describe('buildRequests - app params', function () { + it('should merge app params into request.app', function () { + const bid = deepClone(BASE_BID); + bid.params.app = { name: 'MyApp', bundle: 'com.myapp', ver: '1.0' }; + // bidderRequest.bids must contain the bid for the request() hook to find params.app + const bidderRequest = deepClone(BASE_BIDDER_REQUEST); + bidderRequest.bids = [bid]; + const [req] = spec.buildRequests([bid], bidderRequest); + expect(req.data.app.name).to.equal('MyApp'); + expect(req.data.app.bundle).to.equal('com.myapp'); }); + }); - it('should parse out private sizes', function () { - const bidRequest = Object.assign({}, - bidRequests[0], - { - params: { - placementId: '10433394', - privateSizes: [300, 250] - } - } - ); + // ------------------------------------------------------------------------- + // buildRequests — privacy: USP, addtlConsent, COPPA + // ------------------------------------------------------------------------- + describe('buildRequests - privacy', function () { + it('should set us_privacy from uspConsent', function () { + const bidderRequest = deepClone(BASE_BIDDER_REQUEST); + bidderRequest.uspConsent = '1YNN'; + const [req] = spec.buildRequests([deepClone(BASE_BID)], bidderRequest); + expect(req.data.regs.ext.us_privacy).to.equal('1YNN'); + }); - const request = spec.buildRequests([bidRequest]); - const payload = JSON.parse(request.data); + it('should parse addtlConsent into array of integers', function () { + const bidderRequest = deepClone(BASE_BIDDER_REQUEST); + bidderRequest.gdprConsent = { + gdprApplies: true, + consentString: 'cs', + addtlConsent: '1~7.12.99' + }; + const [req] = spec.buildRequests([deepClone(BASE_BID)], bidderRequest); + expect(req.data.user.ext.addtl_consent).to.deep.equal([7, 12, 99]); + }); - expect(payload.tags[0].private_sizes).to.exist; - expect(payload.tags[0].private_sizes).to.deep.equal([{width: 300, height: 250}]); + it('should not set addtl_consent when addtlConsent has no ~ separator', function () { + const bidderRequest = deepClone(BASE_BIDDER_REQUEST); + bidderRequest.gdprConsent = { gdprApplies: true, consentString: 'cs', addtlConsent: 'no-tilde' }; + const [req] = spec.buildRequests([deepClone(BASE_BID)], bidderRequest); + expect(req.data.user?.ext?.addtl_consent).to.be.undefined; }); - it('should add publisher_id in request', function() { - const bidRequest = Object.assign({}, - bidRequests[0], - { - params: { - placementId: '10433394', - publisherId: '1231234' - } - }); - const request = spec.buildRequests([bidRequest]); - const payload = JSON.parse(request.data); - - expect(payload.tags[0].publisher_id).to.exist; - expect(payload.tags[0].publisher_id).to.deep.equal(1231234); - expect(payload.publisher_id).to.exist; - expect(payload.publisher_id).to.deep.equal(1231234); - }) - - 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 set regs.coppa=1 when coppa config is true', function () { + sandbox.stub(config, 'getConfig').callsFake((key) => { + if (key === 'coppa') return true; + return undefined; }); + const [req] = spec.buildRequests([deepClone(BASE_BID)], deepClone(BASE_BIDDER_REQUEST)); + expect(req.data.regs.coppa).to.equal(1); }); + }); - it('should populate the ad_types array on all requests', function () { - const adUnits = [{ - code: 'adunit-code', - mediaTypes: { - banner: { - sizes: [[300, 250], [300, 600]] - } - }, - bids: [{ - bidder: 'mediafuse', - params: { - placementId: '10433394' + // ------------------------------------------------------------------------- + // buildRequests — video RTB targeting + // ------------------------------------------------------------------------- + describe('buildRequests - video RTB targeting', function () { + if (FEATURES.VIDEO) { + it('should map skip, skipafter, playbackmethod, and api to AN fields', function () { + const bid = deepClone(BASE_BID); + bid.mediaTypes = { + video: { + context: 'instream', + playerSize: [640, 480], + skip: 1, + skipafter: 5, + playbackmethod: [2], + api: [4] } - }], - transactionId: '04f2659e-c005-4eb1-a57c-fa93145e3843' - }]; + }; + const [req] = spec.buildRequests([bid], deepClone(BASE_BIDDER_REQUEST)); + const video = req.data.imp[0].video; + const extAN = req.data.imp[0].ext.appnexus; + expect(video.skippable).to.be.true; + expect(video.skipoffset).to.equal(5); + expect(video.playback_method).to.equal(2); + // api [4] maps to video_frameworks [5] (4↔5 swap) + expect(extAN.video_frameworks).to.include(5); + }); + + it('should set outstream placement=4 for outstream context', function () { + const bid = deepClone(BASE_BID); + bid.mediaTypes = { video: { context: 'outstream', playerSize: [640, 480] } }; + const [req] = spec.buildRequests([bid], deepClone(BASE_BIDDER_REQUEST)); + expect(req.data.imp[0].video.placement).to.equal(4); + }); + + it('should set video.ext.appnexus.context=1 for instream', function () { + const bid = deepClone(BASE_BID); + bid.mediaTypes = { video: { context: 'instream', playerSize: [640, 480] } }; + const [req] = spec.buildRequests([bid], deepClone(BASE_BIDDER_REQUEST)); + expect(req.data.imp[0].video.ext.appnexus.context).to.equal(1); + }); - ['banner', 'video', 'native'].forEach(type => { - getAdUnitsStub.callsFake(function(...args) { - return adUnits; - }); + it('should set video.ext.appnexus.context=4 for outstream', function () { + const bid = deepClone(BASE_BID); + bid.mediaTypes = { video: { context: 'outstream', playerSize: [640, 480] } }; + const [req] = spec.buildRequests([bid], deepClone(BASE_BIDDER_REQUEST)); + expect(req.data.imp[0].video.ext.appnexus.context).to.equal(4); + }); - const bidRequest = Object.assign({}, bidRequests[0]); - bidRequest.mediaTypes = {}; - bidRequest.mediaTypes[type] = {}; + it('should set video.ext.appnexus.context=5 for in-banner', function () { + const bid = deepClone(BASE_BID); + bid.mediaTypes = { video: { context: 'in-banner', playerSize: [640, 480] } }; + const [req] = spec.buildRequests([bid], deepClone(BASE_BIDDER_REQUEST)); + expect(req.data.imp[0].video.ext.appnexus.context).to.equal(5); + }); - const request = spec.buildRequests([bidRequest]); - const payload = JSON.parse(request.data); + it('should not set video.ext.appnexus.context for unknown context', function () { + const bid = deepClone(BASE_BID); + bid.mediaTypes = { video: { context: 'unknown-type', playerSize: [640, 480] } }; + const [req] = spec.buildRequests([bid], deepClone(BASE_BIDDER_REQUEST)); + expect(req.data.imp[0].video.ext?.appnexus?.context).to.be.undefined; + }); - expect(payload.tags[0].ad_types).to.deep.equal([type]); + it('should set require_asset_url for instream context', function () { + const bid = deepClone(BASE_BID); + bid.mediaTypes = { video: { context: 'instream', playerSize: [640, 480] } }; + const [req] = spec.buildRequests([bid], deepClone(BASE_BIDDER_REQUEST)); + expect(req.data.imp[0].ext.appnexus.require_asset_url).to.be.true; + }); - if (type === 'banner') { - delete adUnits[0].mediaTypes; - } + it('should map video params from bid.params.video (VIDEO_TARGETING fields)', function () { + const bid = deepClone(BASE_BID); + bid.mediaTypes = { video: { context: 'instream', playerSize: [640, 480] } }; + bid.params.video = { minduration: 5, maxduration: 30, frameworks: [1, 2] }; + const [req] = spec.buildRequests([bid], deepClone(BASE_BIDDER_REQUEST)); + expect(req.data.imp[0].video.minduration).to.equal(5); + expect(req.data.imp[0].ext.appnexus.video_frameworks).to.deep.equal([1, 2]); }); - }); + } + }); - 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); + // ------------------------------------------------------------------------- + // buildRequests — OMID support + // ------------------------------------------------------------------------- + describe('buildRequests - OMID support', function () { + it('should set iab_support when bid.params.frameworks includes 6', function () { + const bid = deepClone(BASE_BID); + bid.params.frameworks = [6]; + // hasOmidSupport iterates all bids via .some(), so bid must be in bidderRequest.bids + const bidderRequest = deepClone(BASE_BIDDER_REQUEST); + bidderRequest.bids = [bid]; + const [req] = spec.buildRequests([bid], bidderRequest); + expect(req.data.ext.appnexus.iab_support).to.deep.equal({ + omidpn: 'Mediafuse', + omidpv: '$prebid.version$' + }); + }); - expect(payload.tags[0].ad_types).to.not.exist; + it('should set iab_support when mediaTypes.video.api includes 7', function () { + const bid = deepClone(BASE_BID); + bid.mediaTypes = { video: { context: 'instream', playerSize: [640, 480], api: [7] } }; + // hasOmidSupport iterates all bids via .some(), so bid must be in bidderRequest.bids + const bidderRequest = deepClone(BASE_BIDDER_REQUEST); + bidderRequest.bids = [bid]; + const [req] = spec.buildRequests([bid], bidderRequest); + expect(req.data.ext.appnexus.iab_support).to.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'}; + // ------------------------------------------------------------------------- + // interpretResponse — outstream renderer + // ------------------------------------------------------------------------- + if (FEATURES.VIDEO) { + describe('interpretResponse - outstream renderer', function () { + it('should create renderer when renderer_url and renderer_id are present', function () { + const bid = deepClone(BASE_BID); + bid.mediaTypes = { video: { context: 'outstream', playerSize: [640, 480] } }; + const [req] = spec.buildRequests([bid], deepClone(BASE_BIDDER_REQUEST)); + const impId = req.data.imp[0].id; + + const serverResponse = { + body: { + seatbid: [{ + bid: [{ + impid: impId, + price: 3.0, + ext: { + appnexus: { + bid_ad_type: 1, + renderer_url: 'https://cdn.adnxs.com/renderer.js', + renderer_id: 42, + renderer_config: '{"key":"val"}' + } + } + }] + }] + } + }; - const request = spec.buildRequests([bidRequest]); - const payload = JSON.parse(request.data); + const bids = spec.interpretResponse(serverResponse, req); + expect(bids[0].renderer).to.exist; + expect(bids[0].adResponse.ad.renderer_config).to.equal('{"key":"val"}'); + }); - expect(payload.tags[0].ad_types).to.deep.equal(['video']); - expect(payload.tags[0].hb_source).to.deep.equal(1); - }); + it('should set vastUrl directly from asset_url when no renderer', function () { + const bid = deepClone(BASE_BID); + bid.mediaTypes = { video: { context: 'instream', playerSize: [640, 480] } }; + const [req] = spec.buildRequests([bid], deepClone(BASE_BIDDER_REQUEST)); + const impId = req.data.imp[0].id; + + const serverResponse = { + body: { + seatbid: [{ + bid: [{ + impid: impId, + price: 1.0, + ext: { + appnexus: { + bid_ad_type: 1, + asset_url: 'https://vast.example.com/vast.xml' + } + } + }] + }] + } + }; - 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'); - }); + const bids = spec.interpretResponse(serverResponse, req); + expect(bids[0].vastUrl).to.equal('https://vast.example.com/vast.xml'); + }); - it('should attach valid video params to the tag', function () { - const bidRequest = Object.assign({}, - bidRequests[0], - { - params: { - placementId: '10433394', - video: { - id: 123, - minduration: 100, - foobar: 'invalid' - } + it('should set vastImpUrl from nurl and vastUrl from asset_url when both present', function () { + const bid = deepClone(BASE_BID); + bid.mediaTypes = { video: { context: 'instream', playerSize: [640, 480] } }; + const [req] = spec.buildRequests([bid], deepClone(BASE_BIDDER_REQUEST)); + const impId = req.data.imp[0].id; + + const serverResponse = { + body: { + seatbid: [{ + bid: [{ + impid: impId, + price: 1.0, + nurl: 'https://notify.example.com/win', + ext: { + appnexus: { + bid_ad_type: 1, + asset_url: 'https://vast.example.com/vast.xml' + } + } + }] + }] } - } - ); + }; - const request = spec.buildRequests([bidRequest]); - const payload = JSON.parse(request.data); - expect(payload.tags[0].video).to.deep.equal({ - id: 123, - minduration: 100 + const bids = spec.interpretResponse(serverResponse, req); + expect(bids[0].vastUrl).to.equal('https://vast.example.com/vast.xml'); + expect(bids[0].vastImpUrl).to.equal('https://notify.example.com/win'); }); - expect(payload.tags[0].hb_source).to.deep.equal(1); }); - - it('should include ORTB video values when video params were not set', function() { - const bidRequest = deepClone(bidRequests[0]); - bidRequest.params = { - placementId: '1234235', - video: { - skippable: true, - playback_method: ['auto_play_sound_off', 'auto_play_sound_unknown'], - context: 'outstream' + } // FEATURES.VIDEO + + // ------------------------------------------------------------------------- + // interpretResponse — debug info logging + // ------------------------------------------------------------------------- + describe('interpretResponse - debug info logging', function () { + it('should clean HTML and call logMessage when debug_info is present', function () { + const [req] = spec.buildRequests([deepClone(BASE_BID)], deepClone(BASE_BIDDER_REQUEST)); + const logStub = sandbox.stub(utils, 'logMessage'); + + spec.interpretResponse({ + body: { + seatbid: [], + debug: { debug_info: '

Auction Debug


Row' } } - }; - bidRequest.mediaTypes = { - video: { - playerSize: [640, 480], - context: 'outstream', - mimes: ['video/mp4'], - skip: 0, - minduration: 5, - api: [1, 5, 6], - playbackmethod: [2, 4] - } - }; + }, req); - const request = spec.buildRequests([bidRequest]); - const payload = JSON.parse(request.data); + expect(logStub.calledOnce).to.be.true; + expect(logStub.firstCall.args[0]).to.include('===== Auction Debug ====='); + expect(logStub.firstCall.args[0]).to.not.include('

'); + }); + }); - expect(payload.tags[0].video).to.deep.equal({ - minduration: 5, - playback_method: 2, - skippable: true, - context: 4 + // ------------------------------------------------------------------------- + // interpretResponse — native exhaustive assets + // ------------------------------------------------------------------------- + if (FEATURES.NATIVE) { + describe('interpretResponse - native exhaustive assets', function () { + it('should map all optional native fields', function () { + const bid = deepClone(BASE_BID); + bid.mediaTypes = { native: { title: { required: true } } }; + const [req] = spec.buildRequests([bid], deepClone(BASE_BIDDER_REQUEST)); + const impId = req.data.imp[0].id; + + // OpenRTB 1.2 assets array format (as returned by the /openrtb2/prebidjs endpoint) + const nativeAdm = { + native: { + assets: [ + { id: 1, title: { text: 'Title' } }, + { id: 2, data: { type: 2, value: 'Body' } }, + { id: 3, data: { type: 10, value: 'Body2' } }, + { id: 4, data: { type: 12, value: 'Click' } }, + { id: 5, data: { type: 3, value: '4.5' } }, + { id: 6, data: { type: 1, value: 'Sponsor' } }, + { id: 7, data: { type: 9, value: '123 Main St' } }, + { id: 8, data: { type: 5, value: '1000' } }, + { id: 9, data: { type: 4, value: '500' } }, + { id: 10, data: { type: 8, value: '555-1234' } }, + { id: 11, data: { type: 6, value: '$9.99' } }, + { id: 12, data: { type: 7, value: '$4.99' } }, + { id: 13, data: { type: 11, value: 'example.com' } }, + { id: 14, img: { type: 3, url: 'https://img.example.com/img.jpg', w: 300, h: 250 } }, + { id: 15, img: { type: 1, url: 'https://img.example.com/icon.png', w: 50, h: 50 } } + ], + link: { url: 'https://click.example.com', clicktrackers: ['https://ct.example.com'] }, + privacy: 'https://priv.example.com' + } + }; + + const serverResponse = { + body: { + seatbid: [{ + bid: [{ + impid: impId, + price: 1.5, + adm: JSON.stringify(nativeAdm), + ext: { appnexus: { bid_ad_type: 3 } } + }] + }] + } + }; + + const bids = spec.interpretResponse(serverResponse, req); + const native = bids[0].native; + expect(native.title).to.equal('Title'); + expect(native.body).to.equal('Body'); + expect(native.body2).to.equal('Body2'); + expect(native.cta).to.equal('Click'); + expect(native.rating).to.equal('4.5'); + expect(native.sponsoredBy).to.equal('Sponsor'); + expect(native.privacyLink).to.equal('https://priv.example.com'); + expect(native.address).to.equal('123 Main St'); + expect(native.downloads).to.equal('1000'); + expect(native.likes).to.equal('500'); + expect(native.phone).to.equal('555-1234'); + expect(native.price).to.equal('$9.99'); + expect(native.salePrice).to.equal('$4.99'); + expect(native.displayUrl).to.equal('example.com'); + expect(native.clickUrl).to.equal('https://click.example.com'); + expect(native.clickTrackers).to.deep.equal(['https://ct.example.com']); + expect(native.image.url).to.equal('https://img.example.com/img.jpg'); + expect(native.image.width).to.equal(300); + expect(native.icon.url).to.equal('https://img.example.com/icon.png'); }); - expect(payload.tags[0].video_frameworks).to.deep.equal([1, 4]) - }); - it('should add video property when adUnit includes a renderer', function () { - const videoData = { - mediaTypes: { - video: { - context: 'outstream', - mimes: ['video/mp4'] + it('should map native fields using request asset IDs as type fallback when response omits type', function () { + // Build a real request via spec.buildRequests so ortbConverter registers it in its + // internal WeakMap (required by fromORTB). Then inject native.request directly on + // the imp — this simulates what FEATURES.NATIVE would have built without requiring it. + const bid = deepClone(BASE_BID); + const [req] = spec.buildRequests([bid], deepClone(BASE_BIDDER_REQUEST)); + const impId = req.data.imp[0].id; + + req.data.imp[0].native = { + request: JSON.stringify({ + assets: [ + { id: 1, title: { len: 100 } }, + { id: 2, data: { type: 1 } }, // sponsoredBy + { id: 3, data: { type: 2 } }, // body + { id: 4, img: { type: 3, wmin: 1, hmin: 1 } }, // main image + { id: 5, img: { type: 1, wmin: 50, hmin: 50 } } // icon + ] + }) + }; + + // Response assets intentionally omit type — Xandr does this in practice + const serverResponse = { + body: { + seatbid: [{ + bid: [{ + impid: impId, + price: 1.5, + adm: JSON.stringify({ + native: { + assets: [ + { id: 1, title: { text: 'Fallback Title' } }, + { id: 2, data: { value: 'Fallback Sponsor' } }, + { id: 3, data: { value: 'Fallback Body' } }, + { id: 4, img: { url: 'https://img.test/img.jpg', w: 300, h: 250 } }, + { id: 5, img: { url: 'https://img.test/icon.png', w: 50, h: 50 } } + ], + link: { url: 'https://click.test' } + } + }), + ext: { appnexus: { bid_ad_type: 3 } } + }] + }] } - }, - params: { - placementId: '10433394', - video: { - skippable: true, - playback_method: ['auto_play_sound_off'] + }; + + const bids = spec.interpretResponse(serverResponse, req); + const native = bids[0].native; + expect(native.title).to.equal('Fallback Title'); + expect(native.sponsoredBy).to.equal('Fallback Sponsor'); + expect(native.body).to.equal('Fallback Body'); + expect(native.image.url).to.equal('https://img.test/img.jpg'); + expect(native.icon.url).to.equal('https://img.test/icon.png'); + }); + + it('should handle real-world native response: top-level format (no native wrapper), non-sequential IDs, type fallback', function () { + // Validates the format actually returned by the Mediafuse/Xandr endpoint: + // ADM is top-level {ver, assets, link, eventtrackers} — no 'native' wrapper key. + // Asset IDs are non-sequential (id:0 for title). Data/img assets omit 'type'; + // type is resolved from the native request's asset definitions. + const bid = deepClone(BASE_BID); + bid.mediaTypes = { native: { title: { required: true } } }; + const [req] = spec.buildRequests([bid], deepClone(BASE_BIDDER_REQUEST)); + const impId = req.data.imp[0].id; + + // Inject native.request asset definitions so the type-fallback resolves correctly + req.data.imp[0].native = { + request: JSON.stringify({ + assets: [ + { id: 0, title: { len: 100 } }, + { id: 1, img: { type: 3, wmin: 1, hmin: 1 } }, // main image + { id: 2, data: { type: 1 } } // sponsoredBy + ] + }) + }; + + // Real-world ADM: top-level, assets lack 'type', id:0 title, two eventtrackers + const serverResponse = { + body: { + seatbid: [{ + bid: [{ + impid: impId, + price: 0.88, + adm: JSON.stringify({ + ver: '1.2', + assets: [ + { id: 1, img: { url: 'https://img.example.com/img.jpg', w: 150, h: 150 } }, + { id: 0, title: { text: 'Discover Insights That Matter' } }, + { id: 2, data: { value: 'probescout' } } + ], + link: { url: 'https://click.example.com' }, + eventtrackers: [ + { event: 1, method: 1, url: 'https://tracker1.example.com/it' }, + { event: 1, method: 1, url: 'https://tracker2.example.com/t' } + ] + }), + ext: { appnexus: { bid_ad_type: 3 } } + }] + }] } - } - }; + }; + + const bids = spec.interpretResponse(serverResponse, req); + const native = bids[0].native; + expect(native.title).to.equal('Discover Insights That Matter'); + expect(native.sponsoredBy).to.equal('probescout'); + expect(native.image.url).to.equal('https://img.example.com/img.jpg'); + expect(native.image.width).to.equal(150); + expect(native.image.height).to.equal(150); + expect(native.clickUrl).to.equal('https://click.example.com'); + expect(native.javascriptTrackers).to.be.an('array').with.lengthOf(2); + }); - let bidRequest1 = deepClone(bidRequests[0]); - bidRequest1 = Object.assign({}, bidRequest1, videoData, { - renderer: { - url: 'https://test.renderer.url', - render: function () {} - } + it('should disarm eventtrackers (trk.js) by replacing src= with data-src=', function () { + const bid = deepClone(BASE_BID); + bid.mediaTypes = { native: { title: { required: true } } }; + const [req] = spec.buildRequests([bid], deepClone(BASE_BIDDER_REQUEST)); + const impId = req.data.imp[0].id; + + const nativeAdm = { + native: { + title: 'T', + eventtrackers: [ + { method: 1, url: '//cdn.adnxs.com/v/trk.js?src=1&dom_id=%native_dom_id%' }, + { method: 1, url: 'https://other-tracker.com/pixel' } + ] + } + }; + + const serverResponse = { + body: { + seatbid: [{ + bid: [{ + impid: impId, + price: 1.0, + adm: JSON.stringify(nativeAdm), + ext: { appnexus: { bid_ad_type: 3 } } + }] + }] + } + }; + + const bids = spec.interpretResponse(serverResponse, req); + const trackers = bids[0].native.javascriptTrackers; + expect(trackers).to.be.an('array'); + // The trk.js tracker should be disarmed: 'src=' replaced with 'data-src=' + const trkTracker = trackers.find(t => t.includes('trk.js')); + expect(trkTracker).to.include('data-src='); + // Verify the original 'src=1' param is now 'data-src=1' (not a bare 'src=') + expect(trkTracker).to.not.match(/[^-]src=1/); }); - let bidRequest2 = deepClone(bidRequests[0]); - bidRequest2.adUnitCode = 'adUnit_code_2'; - bidRequest2 = Object.assign({}, bidRequest2, videoData); + it('should handle viewability.config disarming', function () { + const bid = deepClone(BASE_BID); + bid.mediaTypes = { native: { title: { required: true } } }; + const [req] = spec.buildRequests([bid], deepClone(BASE_BIDDER_REQUEST)); + const impId = req.data.imp[0].id; + + const serverResponse = { + body: { + seatbid: [{ + bid: [{ + impid: impId, + price: 1.0, + adm: JSON.stringify({ native: { title: 'T' } }), + ext: { + appnexus: { + bid_ad_type: 3, + viewability: { config: '' } + } + } + }] + }] + } + }; - const request = spec.buildRequests([bidRequest1, bidRequest2]); - const payload = JSON.parse(request.data); - expect(payload.tags[0].video).to.deep.equal({ - skippable: true, - playback_method: 2, - custom_renderer_present: true + const bids = spec.interpretResponse(serverResponse, req); + const trackers = bids[0].native.javascriptTrackers; + expect(trackers).to.be.an('array'); + expect(trackers[0]).to.include('data-src='); }); - expect(payload.tags[1].video).to.deep.equal({ - skippable: true, - playback_method: 2 + + it('should handle malformed native adm gracefully', function () { + const bid = deepClone(BASE_BID); + bid.mediaTypes = { native: { title: { required: true } } }; + const [req] = spec.buildRequests([bid], deepClone(BASE_BIDDER_REQUEST)); + const impId = req.data.imp[0].id; + const logErrorStub = sandbox.stub(utils, 'logError'); + + const serverResponse = { + body: { + seatbid: [{ + bid: [{ + impid: impId, + price: 1.0, + adm: 'NOT_VALID_JSON', + ext: { appnexus: { bid_ad_type: 3 } } + }] + }] + } + }; + + // Should not throw + expect(() => spec.interpretResponse(serverResponse, req)).to.not.throw(); + expect(logErrorStub.calledOnce).to.be.true; }); }); + } // FEATURES.NATIVE + + // ------------------------------------------------------------------------- + // getUserSyncs — gdprApplies not a boolean + // ------------------------------------------------------------------------- + describe('getUserSyncs - gdprApplies undefined', function () { + it('should use only gdpr_consent param when gdprApplies is not a boolean', function () { + const syncOptions = { pixelEnabled: true }; + const serverResponses = [{ + body: { ext: { appnexus: { userSync: { url: 'https://sync.example.com/px' } } } } + }]; + const gdprConsent = { consentString: 'abc123' }; // gdprApplies is undefined - it('should attach valid user params to the tag', function () { - const bidRequest = Object.assign({}, - bidRequests[0], - { - params: { - placementId: '10433394', - user: { - externalUid: '123', - segments: [123, { id: 987, value: 876 }], - foobar: 'invalid' - } - } - } - ); + const syncs = spec.getUserSyncs(syncOptions, serverResponses, gdprConsent); + expect(syncs).to.have.lengthOf(1); + expect(syncs[0].url).to.include('gdpr_consent=abc123'); + expect(syncs[0].url).to.not.include('gdpr='); + }); + }); - const request = spec.buildRequests([bidRequest]); - const payload = JSON.parse(request.data); + // ------------------------------------------------------------------------- + // lifecycle — onBidWon + // ------------------------------------------------------------------------- + + // ------------------------------------------------------------------------- + // interpretResponse — dchain from buyer_member_id + // ------------------------------------------------------------------------- + describe('interpretResponse - dchain', function () { + it('should set meta.dchain when buyer_member_id is present', function () { + const [req] = spec.buildRequests([deepClone(BASE_BID)], deepClone(BASE_BIDDER_REQUEST)); + const impId = req.data.imp[0].id; + + const serverResponse = { + body: { + seatbid: [{ + bid: [{ + impid: impId, + price: 1.0, + ext: { appnexus: { bid_ad_type: 0, buyer_member_id: 77, advertiser_id: 99 } } + }] + }] + } + }; - expect(payload.user).to.exist; - expect(payload.user).to.deep.equal({ - external_uid: '123', - segments: [{id: 123}, {id: 987, value: 876}] + const bids = spec.interpretResponse(serverResponse, req); + expect(bids[0].meta.dchain).to.deep.equal({ + ver: '1.0', + complete: 0, + nodes: [{ bsid: '77' }] }); + expect(bids[0].meta.advertiserId).to.equal(99); }); + }); - it('should attach reserve param when either bid param or getFloor function exists', function () { - const getFloorResponse = { currency: 'USD', floor: 3 }; - let request; let payload = null; - const bidRequest = deepClone(bidRequests[0]); + // ------------------------------------------------------------------------- + // buildRequests — optional params map (allowSmallerSizes, usePaymentRule, etc.) + // ------------------------------------------------------------------------- + describe('buildRequests - optional params', function () { + it('should map allowSmallerSizes, usePaymentRule, trafficSourceCode', function () { + const bid = deepClone(BASE_BID); + bid.params.allowSmallerSizes = true; + bid.params.usePaymentRule = true; + bid.params.trafficSourceCode = 'my-source'; + const [req] = spec.buildRequests([bid], deepClone(BASE_BIDDER_REQUEST)); + const extAN = req.data.imp[0].ext.appnexus; + expect(extAN.allow_smaller_sizes).to.be.true; + expect(extAN.use_pmt_rule).to.be.true; + expect(extAN.traffic_source_code).to.equal('my-source'); + }); - // 1 -> reserve not defined, getFloor not defined > empty - request = spec.buildRequests([bidRequest]); - payload = JSON.parse(request.data); + it('should map externalImpId to ext.appnexus.ext_imp_id', function () { + const bid = deepClone(BASE_BID); + bid.params.externalImpId = 'ext-imp-123'; + const [req] = spec.buildRequests([bid], deepClone(BASE_BIDDER_REQUEST)); + expect(req.data.imp[0].ext.appnexus.ext_imp_id).to.equal('ext-imp-123'); + }); + }); - expect(payload.tags[0].reserve).to.not.exist; + // ------------------------------------------------------------------------- + // isBidRequestValid + // ------------------------------------------------------------------------- + describe('isBidRequestValid', function () { + it('should return true for placement_id (snake_case)', function () { + expect(spec.isBidRequestValid({ params: { placement_id: 12345 } })).to.be.true; + }); - // 2 -> reserve is defined, getFloor not defined > reserve is used - bidRequest.params = { - 'placementId': '10433394', - 'reserve': 0.5 - }; - request = spec.buildRequests([bidRequest]); - payload = JSON.parse(request.data); - - expect(payload.tags[0].reserve).to.exist.and.to.equal(0.5); - - // 3 -> reserve is defined, getFloor is defined > getFloor is used - bidRequest.getFloor = () => getFloorResponse; - - request = spec.buildRequests([bidRequest]); - payload = JSON.parse(request.data); - - expect(payload.tags[0].reserve).to.exist.and.to.equal(3); - }); - - it('should duplicate adpod placements into batches and set correct maxduration', function() { - const bidRequest = Object.assign({}, - bidRequests[0], - { - 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() { - const 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); - }); - - it('should duplicate adpod placements when requireExactDuration is set', function() { - const 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('should set durations for placements when requireExactDuration is set and numbers are uneven', function() { - const bidRequest = Object.assign({}, - bidRequests[0], - { - 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); - - 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() { - const 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('should contain hb_source value for adpod', function() { - const bidRequest = Object.assign({}, - bidRequests[0], - { - params: { placementId: '14542875' } - }, - { - mediaTypes: { - video: { - context: 'adpod', - playerSize: [640, 480], - adPodDurationSec: 300, - durationRangeSec: [15, 30], - } - } - } - ); - const request = spec.buildRequests([bidRequest])[0]; - const payload = JSON.parse(request.data); - expect(payload.tags[0].hb_source).to.deep.equal(7); - }); - - it('should contain hb_source value for other media', function() { - const bidRequest = Object.assign({}, - bidRequests[0], - { - mediaType: 'banner', - params: { - sizes: [[300, 250], [300, 600]], - placementId: 13144370 - } - } - ); - const request = spec.buildRequests([bidRequest]); - const payload = JSON.parse(request.data); - expect(payload.tags[0].hb_source).to.deep.equal(1); - }); - - it('adds brand_category_exclusion to request when set', function() { - const 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('adds auction level keywords to request when set', function() { - const bidRequest = Object.assign({}, bidRequests[0]); - sinon - .stub(config, 'getConfig') - .withArgs('mediafuseAuctionKeywords') - .returns({ - gender: 'm', - music: ['rock', 'pop'], - test: '' - }); - - const request = spec.buildRequests([bidRequest]); - const payload = JSON.parse(request.data); - - expect(payload.keywords).to.deep.equal([{ - 'key': 'gender', - 'value': ['m'] - }, { - 'key': 'music', - 'value': ['rock', 'pop'] - }, { - 'key': 'test' - }]); - - config.getConfig.restore(); - }); - - it('should attach native params to the request', function () { - const bidRequest = Object.assign({}, - bidRequests[0], - { - mediaType: 'native', - nativeParams: { - 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} - } - } - ); - - 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}, - 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 - }); - expect(payload.tags[0].hb_source).to.equal(1); + it('should return true for member + invCode', function () { + expect(spec.isBidRequestValid({ params: { member: '123', invCode: 'inv' } })).to.be.true; }); - it('should always populated tags[].sizes with 1,1 for native if otherwise not defined', function () { - const 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 () { - const bidRequest = Object.assign({}, - bidRequests[0], - { - params: { - placementId: '10433394', - 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([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'] - }, { - 'key': 'emptyStr' - }, { - 'key': 'emptyArr' - }]); - }); - - it('should add payment rules to the request', function () { - const bidRequest = Object.assign({}, - bidRequests[0], - { - params: { - placementId: '10433394', - usePaymentRule: true - } - } - ); + it('should return true for member + inv_code', function () { + expect(spec.isBidRequestValid({ params: { member: '123', inv_code: 'inv' } })).to.be.true; + }); - const request = spec.buildRequests([bidRequest]); - const payload = JSON.parse(request.data); + it('should return false when no params', function () { + expect(spec.isBidRequestValid({})).to.be.false; + }); - expect(payload.tags[0].use_pmt_rule).to.equal(true); + it('should return false for member without invCode or inv_code', function () { + expect(spec.isBidRequestValid({ params: { member: '123' } })).to.be.false; }); + }); - it('should add gpid to the request', function () { - const testGpid = '/12345/my-gpt-tag-0'; - const bidRequest = deepClone(bidRequests[0]); - bidRequest.ortb2Imp = { ext: { data: {}, gpid: testGpid } }; + // ------------------------------------------------------------------------- + // getBidFloor + // ------------------------------------------------------------------------- + describe('buildRequests - getBidFloor', function () { + it('should use getFloor function result when available and currency matches', function () { + const bid = deepClone(BASE_BID); + bid.getFloor = () => ({ currency: 'USD', floor: 1.5 }); + const [req] = spec.buildRequests([bid], deepClone(BASE_BIDDER_REQUEST)); + expect(req.data.imp[0].bidfloor).to.equal(1.5); + }); - const request = spec.buildRequests([bidRequest]); - const payload = JSON.parse(request.data); + it('should return null when getFloor returns wrong currency', function () { + const bid = deepClone(BASE_BID); + bid.getFloor = () => ({ currency: 'EUR', floor: 1.5 }); + const [req] = spec.buildRequests([bid], deepClone(BASE_BIDDER_REQUEST)); + expect(req.data.imp[0].bidfloor).to.be.undefined; + }); - expect(payload.tags[0].gpid).to.exist.and.equal(testGpid) + it('should use params.reserve when no getFloor function', function () { + const bid = deepClone(BASE_BID); + bid.params.reserve = 2.0; + const [req] = spec.buildRequests([bid], deepClone(BASE_BIDDER_REQUEST)); + expect(req.data.imp[0].bidfloor).to.equal(2.0); }); + }); - it('should add gdpr consent information to the request', function () { - const consentString = 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A=='; - const bidderRequest = { - 'bidderCode': 'mediafuse', - 'auctionId': '1d1a030790a475', - 'bidderRequestId': '22edbae2733bf6', - 'timeout': 3000, - 'gdprConsent': { - consentString: consentString, - gdprApplies: true, - addtlConsent: '1~7.12.35.62.66.70.89.93.108' - } - }; - bidderRequest.bids = bidRequests; - - const request = spec.buildRequests(bidRequests, bidderRequest); - expect(request.options).to.deep.equal({withCredentials: true}); - 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; - expect(payload.gdpr_consent.addtl_consent).to.exist.and.to.deep.equal([7, 12, 35, 62, 66, 70, 89, 93, 108]); - }); - - it('should add us privacy string to payload', function() { - const consentString = '1YA-'; - const bidderRequest = { - 'bidderCode': 'mediafuse', - 'auctionId': '1d1a030790a475', - 'bidderRequestId': '22edbae2733bf6', - 'timeout': 3000, - 'uspConsent': consentString - }; - bidderRequest.bids = bidRequests; - - const request = spec.buildRequests(bidRequests, bidderRequest); - const payload = JSON.parse(request.data); - - expect(payload.us_privacy).to.exist; - expect(payload.us_privacy).to.exist.and.to.equal(consentString); - }); - - it('supports sending hybrid mobile app parameters', function () { - const 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.not.exist; - expect(payload.device.geo).to.not.deep.equal({ - lat: 40.0964439, - lng: -75.3009142 - }); + // ------------------------------------------------------------------------- + // buildRequests — inv_code + // ------------------------------------------------------------------------- + describe('buildRequests - inv_code', function () { + it('should set tagid from invCode when no placementId', function () { + const bid = { bidder: 'mediafuse', adUnitCode: 'au', bidId: 'b1', params: { invCode: 'my-inv-code', member: '123' } }; + const [req] = spec.buildRequests([bid], deepClone(BASE_BIDDER_REQUEST)); + expect(req.data.imp[0].tagid).to.equal('my-inv-code'); }); - it('should add referer info to payload', function () { - const bidRequest = Object.assign({}, bidRequests[0]) - const bidderRequest = { - refererInfo: { - topmostLocation: 'https://example.com/page.html', - reachedTop: true, - numIframes: 2, - stack: [ - 'https://example.com/page.html', - 'https://example.com/iframe1.html', - 'https://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: 'https%3A%2F%2Fexample.com%2Fpage.html', - rd_top: true, - rd_ifs: 2, - rd_stk: bidderRequest.refererInfo.stack.map((url) => encodeURIComponent(url)).join(',') - }); + it('should set tagid from inv_code when no placementId', function () { + const bid = { bidder: 'mediafuse', adUnitCode: 'au', bidId: 'b1', params: { inv_code: 'my-inv-code-snake', member: '123' } }; + const [req] = spec.buildRequests([bid], deepClone(BASE_BIDDER_REQUEST)); + expect(req.data.imp[0].tagid).to.equal('my-inv-code-snake'); }); + }); - it('should populate schain if available', function () { - const bidRequest = Object.assign({}, bidRequests[0], { - ortb2: { - source: { - ext: { - schain: { - ver: '1.0', - complete: 1, - nodes: [ - { - 'asi': 'blob.com', - 'sid': '001', - 'hp': 1 - } - ] - } - } - } - } - }); + // ------------------------------------------------------------------------- + // buildRequests — banner_frameworks + // ------------------------------------------------------------------------- + describe('buildRequests - banner_frameworks', function () { + it('should set banner_frameworks from bid.params.banner_frameworks when no banner.api', function () { + const bid = deepClone(BASE_BID); + bid.mediaTypes = { banner: { sizes: [[300, 250]] } }; + bid.params.banner_frameworks = [1, 2, 3]; + const [req] = spec.buildRequests([bid], deepClone(BASE_BIDDER_REQUEST)); + expect(req.data.imp[0].ext.appnexus.banner_frameworks).to.deep.equal([1, 2, 3]); + }); + }); - 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 - } - ] + // ------------------------------------------------------------------------- + // buildRequests — custom_renderer_present via bid.renderer + // ------------------------------------------------------------------------- + describe('buildRequests - custom renderer present', function () { + if (FEATURES.VIDEO) { + it('should set custom_renderer_present when bid.renderer is set for video imp', function () { + const bid = deepClone(BASE_BID); + bid.mediaTypes = { video: { context: 'outstream', playerSize: [640, 480] } }; + bid.renderer = { id: 'custom', url: 'https://renderer.example.com/r.js' }; + const [req] = spec.buildRequests([bid], deepClone(BASE_BIDDER_REQUEST)); + expect(req.data.imp[0].ext.appnexus.custom_renderer_present).to.be.true; }); + } + }); + + // ------------------------------------------------------------------------- + // buildRequests — catch-all unknown camelCase params + // ------------------------------------------------------------------------- + describe('buildRequests - catch-all unknown params', function () { + it('should convert unknown camelCase params to snake_case in extAN', function () { + const bid = deepClone(BASE_BID); + bid.params.unknownCamelCaseParam = 'value123'; + const [req] = spec.buildRequests([bid], deepClone(BASE_BIDDER_REQUEST)); + expect(req.data.imp[0].ext.appnexus.unknown_camel_case_param).to.equal('value123'); + }); + }); + + // ------------------------------------------------------------------------- + // buildRequests — bid-level keywords + // ------------------------------------------------------------------------- + describe('buildRequests - bid keywords', function () { + it('should map bid.params.keywords to extAN.keywords string', function () { + const bid = deepClone(BASE_BID); + bid.params.keywords = { genre: ['rock', 'pop'] }; + const [req] = spec.buildRequests([bid], deepClone(BASE_BIDDER_REQUEST)); + expect(req.data.imp[0].ext.appnexus.keywords).to.be.a('string'); + expect(req.data.imp[0].ext.appnexus.keywords).to.include('genre=rock,pop'); }); + }); + + // ------------------------------------------------------------------------- + // buildRequests — canonicalUrl in referer detection + // ------------------------------------------------------------------------- + describe('buildRequests - canonicalUrl', function () { + it('should set rd_can in referrer_detection when canonicalUrl is present', function () { + const bidderRequest = deepClone(BASE_BIDDER_REQUEST); + bidderRequest.refererInfo.canonicalUrl = 'https://canonical.example.com/page'; + const [req] = spec.buildRequests([deepClone(BASE_BID)], bidderRequest); + expect(req.data.ext.appnexus.referrer_detection.rd_can).to.equal('https://canonical.example.com/page'); + }); + }); - it('should populate coppa if set in config', function () { - const bidRequest = Object.assign({}, bidRequests[0]); - sinon.stub(config, 'getConfig') - .withArgs('coppa') - .returns(true); + // ------------------------------------------------------------------------- + // buildRequests — publisherId → site.publisher.id + // ------------------------------------------------------------------------- + describe('buildRequests - publisherId', function () { + it('should set site.publisher.id from bid.params.publisherId', function () { + const bid = deepClone(BASE_BID); + bid.params.publisherId = 67890; + const bidderRequest = deepClone(BASE_BIDDER_REQUEST); + bidderRequest.bids = [bid]; + const [req] = spec.buildRequests([bid], bidderRequest); + expect(req.data.site.publisher.id).to.equal('67890'); + }); + }); - const request = spec.buildRequests([bidRequest]); - const payload = JSON.parse(request.data); + // ------------------------------------------------------------------------- + // buildRequests — member appended to endpoint URL + // ------------------------------------------------------------------------- + describe('buildRequests - member URL param', function () { + it('should append member_id to endpoint URL when bid.params.member is set', function () { + const bid = deepClone(BASE_BID); + bid.params.member = '456'; + const bidderRequest = deepClone(BASE_BIDDER_REQUEST); + bidderRequest.bids = [bid]; + const [req] = spec.buildRequests([bid], bidderRequest); + expect(req.url).to.include('member_id=456'); + }); + }); - expect(payload.user.coppa).to.equal(true); + // ------------------------------------------------------------------------- + // buildRequests — gppConsent + // ------------------------------------------------------------------------- + describe('buildRequests - gppConsent', function () { + it('should set regs.gpp and regs.gpp_sid from gppConsent', function () { + const bidderRequest = deepClone(BASE_BIDDER_REQUEST); + bidderRequest.gppConsent = { gppString: 'DBACMYA', applicableSections: [7] }; + const [req] = spec.buildRequests([deepClone(BASE_BID)], bidderRequest); + expect(req.data.regs.gpp).to.equal('DBACMYA'); + expect(req.data.regs.gpp_sid).to.deep.equal([7]); + }); + }); - config.getConfig.restore(); + // ------------------------------------------------------------------------- + // buildRequests — gdprApplies=false + // ------------------------------------------------------------------------- + describe('buildRequests - gdprApplies false', function () { + it('should set regs.ext.gdpr=0 when gdprApplies is false', function () { + const bidderRequest = deepClone(BASE_BIDDER_REQUEST); + bidderRequest.gdprConsent = { gdprApplies: false, consentString: 'cs' }; + const [req] = spec.buildRequests([deepClone(BASE_BID)], bidderRequest); + expect(req.data.regs.ext.gdpr).to.equal(0); }); + }); - it('should set the X-Is-Test customHeader if test flag is enabled', function () { - const bidRequest = Object.assign({}, bidRequests[0]); - sinon.stub(config, 'getConfig') - .withArgs('apn_test') - .returns(true); + // ------------------------------------------------------------------------- + // buildRequests — user.externalUid + // ------------------------------------------------------------------------- + describe('buildRequests - user externalUid', function () { + it('should map externalUid to user.external_uid', function () { + const bid = deepClone(BASE_BID); + bid.params.user = { externalUid: 'uid-abc-123' }; + const bidderRequest = deepClone(BASE_BIDDER_REQUEST); + bidderRequest.bids = [bid]; + const [req] = spec.buildRequests([bid], bidderRequest); + expect(req.data.user.external_uid).to.equal('uid-abc-123'); + }); + }); - const request = spec.buildRequests([bidRequest]); - expect(request.options.customHeaders).to.deep.equal({'X-Is-Test': 1}); + // ------------------------------------------------------------------------- + // buildRequests — EID rtiPartner mapping (TDID / UID2) + // ------------------------------------------------------------------------- + describe('buildRequests - EID rtiPartner mapping', function () { + it('should set rtiPartner=TDID inside uids[0].ext for adserver.org EID', function () { + const bid = deepClone(BASE_BID); + const bidderRequest = deepClone(BASE_BIDDER_REQUEST); + bidderRequest.ortb2.user = { ext: { eids: [{ source: 'adserver.org', uids: [{ id: 'tdid-value', atype: 1 }] }] } }; + const [req] = spec.buildRequests([bid], bidderRequest); + const eid = req.data.user?.ext?.eids?.find(e => e.source === 'adserver.org'); + expect(eid).to.exist; + expect(eid.uids[0].ext.rtiPartner).to.equal('TDID'); + expect(eid.rti_partner).to.be.undefined; + }); - config.getConfig.restore(); + it('should set rtiPartner=UID2 inside uids[0].ext for uidapi.com EID', function () { + const bid = deepClone(BASE_BID); + const bidderRequest = deepClone(BASE_BIDDER_REQUEST); + bidderRequest.ortb2.user = { ext: { eids: [{ source: 'uidapi.com', uids: [{ id: 'uid2-value', atype: 3 }] }] } }; + const [req] = spec.buildRequests([bid], bidderRequest); + const eid = req.data.user?.ext?.eids?.find(e => e.source === 'uidapi.com'); + expect(eid).to.exist; + expect(eid.uids[0].ext.rtiPartner).to.equal('UID2'); + expect(eid.rti_partner).to.be.undefined; }); - it('should always set withCredentials: true on the request.options', function () { - const bidRequest = Object.assign({}, bidRequests[0]); - const request = spec.buildRequests([bidRequest]); - expect(request.options.withCredentials).to.equal(true); + it('should preserve existing uid.ext fields when adding rtiPartner', function () { + const bid = deepClone(BASE_BID); + const bidderRequest = deepClone(BASE_BIDDER_REQUEST); + bidderRequest.ortb2.user = { ext: { eids: [{ source: 'adserver.org', uids: [{ id: 'tdid-value', atype: 1, ext: { existing: true } }] }] } }; + const [req] = spec.buildRequests([bid], bidderRequest); + const eid = req.data.user?.ext?.eids?.find(e => e.source === 'adserver.org'); + expect(eid).to.exist; + expect(eid.uids[0].ext.rtiPartner).to.equal('TDID'); + expect(eid.uids[0].ext.existing).to.be.true; }); + }); - it('should set simple domain variant if purpose 1 consent is not given', function () { - const consentString = 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A=='; - const bidderRequest = { - 'bidderCode': 'mediafuse', - 'auctionId': '1d1a030790a475', - 'bidderRequestId': '22edbae2733bf6', - 'timeout': 3000, - 'gdprConsent': { - consentString: consentString, - gdprApplies: true, - apiVersion: 2, - vendorData: { - purpose: { - consents: { - 1: false - } - } - } - } - }; - bidderRequest.bids = bidRequests; - - const request = spec.buildRequests(bidRequests, bidderRequest); - expect(request.url).to.equal('https://ib.adnxs-simple.com/ut/v3/prebid'); - }); - - it('should populate eids when supported userIds are available', function () { - const bidRequest = Object.assign({}, bidRequests[0], { - userIdAsEids: [{ - source: 'adserver.org', - uids: [{ id: 'sample-userid' }] - }, { - source: 'criteo.com', - uids: [{ id: 'sample-criteo-userid' }] - }, { - source: 'netid.de', - uids: [{ id: 'sample-netId-userid' }] - }, { - source: 'liveramp.com', - uids: [{ id: 'sample-idl-userid' }] - }, { - source: 'uidapi.com', - uids: [{ id: 'sample-uid2-value' }] - }, { - source: 'puburl.com', - uids: [{ id: 'pubid1' }] - }, { - source: 'puburl2.com', - uids: [{ id: 'pubid2' }, { id: 'pubid2-123' }] - }] + // ------------------------------------------------------------------------- + // buildRequests — apn_test config → X-Is-Test header + // ------------------------------------------------------------------------- + describe('buildRequests - apn_test config header', function () { + it('should set X-Is-Test:1 custom header when config apn_test=true', function () { + sandbox.stub(config, 'getConfig').callsFake((key) => { + if (key === 'apn_test') return true; + return undefined; }); + const [req] = spec.buildRequests([deepClone(BASE_BID)], deepClone(BASE_BIDDER_REQUEST)); + expect(req.options.customHeaders).to.deep.equal({ 'X-Is-Test': 1 }); + }); + }); - const request = spec.buildRequests([bidRequest]); - const payload = JSON.parse(request.data); - expect(payload.eids).to.deep.include({ - source: 'adserver.org', - id: 'sample-userid', - rti_partner: 'TDID' + // ------------------------------------------------------------------------- + // buildRequests — video minduration already set (skip overwrite) + // ------------------------------------------------------------------------- + describe('buildRequests - video minduration skip overwrite', function () { + if (FEATURES.VIDEO) { + it('should not overwrite minduration set by params.video when mediaTypes.video.minduration also present', function () { + const bid = deepClone(BASE_BID); + bid.mediaTypes = { video: { context: 'instream', playerSize: [640, 480], minduration: 10 } }; + bid.params.video = { minduration: 5 }; + const [req] = spec.buildRequests([bid], deepClone(BASE_BIDDER_REQUEST)); + // params.video sets minduration=5 first; mediaTypes check sees it's already a number → skips + expect(req.data.imp[0].video.minduration).to.equal(5); }); + } + }); - expect(payload.eids).to.deep.include({ - source: 'criteo.com', - id: 'sample-criteo-userid', + // ------------------------------------------------------------------------- + // buildRequests — playbackmethod out of range (>4) + // ------------------------------------------------------------------------- + describe('buildRequests - video playbackmethod out of range', function () { + if (FEATURES.VIDEO) { + it('should not set playback_method when playbackmethod[0] > 4', function () { + const bid = deepClone(BASE_BID); + bid.mediaTypes = { video: { context: 'instream', playerSize: [640, 480], playbackmethod: [5] } }; + const [req] = spec.buildRequests([bid], deepClone(BASE_BIDDER_REQUEST)); + expect(req.data.imp[0].video.playback_method).to.be.undefined; }); + } + }); - expect(payload.eids).to.deep.include({ - source: 'netid.de', - id: 'sample-netId-userid', + // ------------------------------------------------------------------------- + // buildRequests — video api val=6 filtered out + // ------------------------------------------------------------------------- + describe('buildRequests - video api val=6 filtered', function () { + if (FEATURES.VIDEO) { + it('should produce empty video_frameworks when api=[6] since 6 is out of 1-5 range', function () { + const bid = deepClone(BASE_BID); + bid.mediaTypes = { video: { context: 'instream', playerSize: [640, 480], api: [6] } }; + const [req] = spec.buildRequests([bid], deepClone(BASE_BIDDER_REQUEST)); + expect(req.data.imp[0].ext.appnexus.video_frameworks).to.deep.equal([]); }); + } + }); - expect(payload.eids).to.deep.include({ - source: 'liveramp.com', - id: 'sample-idl-userid' + // ------------------------------------------------------------------------- + // buildRequests — video_frameworks already set; api should not override + // ------------------------------------------------------------------------- + describe('buildRequests - video_frameworks not overridden by api', function () { + if (FEATURES.VIDEO) { + it('should keep frameworks from params.video when mediaTypes.video.api is also present', function () { + const bid = deepClone(BASE_BID); + bid.mediaTypes = { video: { context: 'instream', playerSize: [640, 480], api: [4] } }; + bid.params.video = { frameworks: [1, 2] }; + const [req] = spec.buildRequests([bid], deepClone(BASE_BIDDER_REQUEST)); + expect(req.data.imp[0].ext.appnexus.video_frameworks).to.deep.equal([1, 2]); }); + } + }); - expect(payload.eids).to.deep.include({ - source: 'uidapi.com', - id: 'sample-uid2-value', - rti_partner: 'UID2' - }); + // ------------------------------------------------------------------------- + // interpretResponse — adomain string vs empty array + // ------------------------------------------------------------------------- + describe('interpretResponse - adomain handling', function () { + it('should wrap string adomain in an array for advertiserDomains', function () { + const [req] = spec.buildRequests([deepClone(BASE_BID)], deepClone(BASE_BIDDER_REQUEST)); + const impId = req.data.imp[0].id; + const serverResponse = { + body: { + seatbid: [{ + bid: [{ + impid: impId, + price: 1.0, + adomain: 'example.com', + ext: { appnexus: { bid_ad_type: 0 } } + }] + }] + } + }; + const bids = spec.interpretResponse(serverResponse, req); + expect(bids[0].meta.advertiserDomains).to.deep.equal(['example.com']); }); - it('should populate iab_support object at the root level if omid support is detected', function () { - // with bid.params.frameworks - const bidRequest_A = Object.assign({}, bidRequests[0], { - params: { - frameworks: [1, 2, 5, 6], - video: { - frameworks: [1, 2, 5, 6] - } + it('should not set non-empty advertiserDomains when adomain is an empty array', function () { + const [req] = spec.buildRequests([deepClone(BASE_BID)], deepClone(BASE_BIDDER_REQUEST)); + const impId = req.data.imp[0].id; + const serverResponse = { + body: { + seatbid: [{ + bid: [{ + impid: impId, + price: 1.0, + adomain: [], + ext: { appnexus: { bid_ad_type: 0 } } + }] + }] + } + }; + const bids = spec.interpretResponse(serverResponse, req); + // adapter's guard skips setting advertiserDomains for empty arrays; + // ortbConverter may set it to [] — either way it must not be a non-empty array + const domains = bids[0].meta && bids[0].meta.advertiserDomains; + expect(!domains || domains.length === 0).to.be.true; + }); + }); + + // ------------------------------------------------------------------------- + // interpretResponse — banner impression_urls trackers + // ------------------------------------------------------------------------- + describe('interpretResponse - banner trackers', function () { + it('should append tracker pixel HTML to bid.ad when trackers.impression_urls is present', function () { + const [req] = spec.buildRequests([deepClone(BASE_BID)], deepClone(BASE_BIDDER_REQUEST)); + const impId = req.data.imp[0].id; + const serverResponse = { + body: { + seatbid: [{ + bid: [{ + impid: impId, + price: 1.0, + adm: '
ad
', + ext: { + appnexus: { + bid_ad_type: 0, + trackers: [{ impression_urls: ['https://tracker.example.com/impression'] }] + } + } + }] + }] } + }; + const bids = spec.interpretResponse(serverResponse, req); + expect(bids[0].ad).to.include('tracker.example.com/impression'); + }); + }); + + // ------------------------------------------------------------------------- + // interpretResponse — native jsTrackers combinations + // ------------------------------------------------------------------------- + if (FEATURES.NATIVE) { + describe('interpretResponse - native jsTrackers combinations', function () { + function buildNativeReq() { + const bid = deepClone(BASE_BID); + bid.mediaTypes = { native: { title: { required: true } } }; + const [req] = spec.buildRequests([bid], deepClone(BASE_BIDDER_REQUEST)); + return req; + } + + it('should combine string jsTracker with viewability.config into array [str, disarmed]', function () { + const req = buildNativeReq(); + const impId = req.data.imp[0].id; + const serverResponse = { + body: { + seatbid: [{ + bid: [{ + impid: impId, + price: 1.0, + adm: JSON.stringify({ native: { title: 'T', javascript_trackers: 'https://existing-tracker.com/t.js' } }), + ext: { + appnexus: { + bid_ad_type: 3, + viewability: { config: '' } + } + } + }] + }] + } + }; + const bids = spec.interpretResponse(serverResponse, req); + const trackers = bids[0].native.javascriptTrackers; + expect(trackers).to.be.an('array').with.lengthOf(2); + expect(trackers[0]).to.equal('https://existing-tracker.com/t.js'); + expect(trackers[1]).to.include('data-src='); }); - let request = spec.buildRequests([bidRequest_A]); - let payload = JSON.parse(request.data); - expect(payload.iab_support).to.be.an('object'); - expect(payload.iab_support).to.deep.equal({ - omidpn: 'Mediafuse', - omidpv: '$prebid.version$' + + it('should push viewability.config into existing array jsTrackers', function () { + const req = buildNativeReq(); + const impId = req.data.imp[0].id; + const serverResponse = { + body: { + seatbid: [{ + bid: [{ + impid: impId, + price: 1.0, + adm: JSON.stringify({ native: { title: 'T', javascript_trackers: ['https://tracker1.com/t.js'] } }), + ext: { + appnexus: { + bid_ad_type: 3, + viewability: { config: '' } + } + } + }] + }] + } + }; + const bids = spec.interpretResponse(serverResponse, req); + const trackers = bids[0].native.javascriptTrackers; + expect(trackers).to.be.an('array').with.lengthOf(2); + expect(trackers[0]).to.equal('https://tracker1.com/t.js'); + expect(trackers[1]).to.include('data-src='); }); - expect(payload.tags[0].banner_frameworks).to.be.an('array'); - expect(payload.tags[0].banner_frameworks).to.deep.equal([1, 2, 5, 6]); - expect(payload.tags[0].video_frameworks).to.be.an('array'); - expect(payload.tags[0].video_frameworks).to.deep.equal([1, 2, 5, 6]); - expect(payload.tags[0].video.frameworks).to.not.exist; - - // without bid.params.frameworks - const bidRequest_B = Object.assign({}, bidRequests[0]); - request = spec.buildRequests([bidRequest_B]); - payload = JSON.parse(request.data); - expect(payload.iab_support).to.not.exist; - expect(payload.tags[0].banner_frameworks).to.not.exist; - expect(payload.tags[0].video_frameworks).to.not.exist; - - // with video.frameworks but it is not an array - const bidRequest_C = Object.assign({}, bidRequests[0], { - params: { - video: { - frameworks: "'1', '2', '3', '6'" + + it('should combine string jsTracker with eventtrackers into array', function () { + const req = buildNativeReq(); + const impId = req.data.imp[0].id; + const serverResponse = { + body: { + seatbid: [{ + bid: [{ + impid: impId, + price: 1.0, + adm: JSON.stringify({ + native: { + title: 'T', + javascript_trackers: 'https://existing-tracker.com/t.js', + eventtrackers: [{ method: 1, url: 'https://event-tracker.com/track' }] + } + }), + ext: { appnexus: { bid_ad_type: 3 } } + }] + }] } - } + }; + const bids = spec.interpretResponse(serverResponse, req); + const trackers = bids[0].native.javascriptTrackers; + expect(trackers).to.be.an('array').with.lengthOf(2); + expect(trackers[0]).to.equal('https://existing-tracker.com/t.js'); + expect(trackers[1]).to.equal('https://event-tracker.com/track'); }); - request = spec.buildRequests([bidRequest_C]); - payload = JSON.parse(request.data); - expect(payload.iab_support).to.not.exist; - expect(payload.tags[0].banner_frameworks).to.not.exist; - expect(payload.tags[0].video_frameworks).to.not.exist; - }); - }) - - describe('interpretResponse', function () { - let bidderSettingsStorage; - - before(function() { - bidderSettingsStorage = getGlobal().bidderSettings; - }); - - after(function() { - getGlobal().bidderSettings = bidderSettingsStorage; - }); - - const response = { - 'version': '3.0.0', - 'tags': [ - { - 'uuid': '3db3773286ee59', - 'tag_id': 10433394, - 'auction_id': '4534722592064951574', - 'nobid': false, - 'no_ad_url': 'https://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, - 'viewability': { - 'config': '' - }, - 'rtb': { - 'banner': { - 'content': '', - 'width': 300, - 'height': 250 - }, - 'trackers': [ - { - 'impression_urls': [ - 'https://lax1-ib.adnxs.com/impression', - 'https://www.test.com/tracker' - ], - 'video_events': {} + + it('should push eventtrackers into existing array jsTrackers', function () { + const req = buildNativeReq(); + const impId = req.data.imp[0].id; + const serverResponse = { + body: { + seatbid: [{ + bid: [{ + impid: impId, + price: 1.0, + adm: JSON.stringify({ + native: { + title: 'T', + javascript_trackers: ['https://existing-tracker.com/t.js'], + eventtrackers: [{ method: 1, url: 'https://event-tracker.com/track' }] } - ] - } - } - ] - } - ] - }; - - it('should get correct bid response', function () { - const expectedResponse = [ - { - 'requestId': '3db3773286ee59', - 'cpm': 0.5, - 'creativeId': 29681110, - 'dealId': undefined, - 'width': 300, - 'height': 250, - 'ad': '', - 'mediaType': 'banner', - 'currency': 'USD', - 'ttl': 300, - 'netRevenue': true, - 'adUnitCode': 'code', - 'mediafuse': { - 'buyerMemberId': 958 - }, - 'meta': { - 'dchain': { - 'ver': '1.0', - 'complete': 0, - 'nodes': [{ - 'bsid': '958' + }), + ext: { appnexus: { bid_ad_type: 3 } } }] - } + }] } - } - ]; - const bidderRequest = { - bids: [{ - bidId: '3db3773286ee59', - adUnitCode: 'code' - }] + }; + const bids = spec.interpretResponse(serverResponse, req); + const trackers = bids[0].native.javascriptTrackers; + expect(trackers).to.be.an('array').with.lengthOf(2); + expect(trackers[0]).to.equal('https://existing-tracker.com/t.js'); + expect(trackers[1]).to.equal('https://event-tracker.com/track'); + }); + + it('should replace %native_dom_id% macro in eventtrackers during interpretResponse', function () { + const bid = deepClone(BASE_BID); + bid.mediaTypes = { native: { title: { required: true } } }; + const [req] = spec.buildRequests([bid], deepClone(BASE_BIDDER_REQUEST)); + const impId = req.data.imp[0].id; + + const adWithMacro = { + native: { + title: 'T', + eventtrackers: [{ + method: 1, + url: 'https://cdn.adnxs.com/v/trk.js?dom_id=%native_dom_id%&id=123' + }] + } + }; + + const serverResponse = { + body: { + seatbid: [{ + bid: [{ + impid: impId, + price: 1.0, + adm: JSON.stringify(adWithMacro), + ext: { appnexus: { bid_ad_type: 3 } } + }] + }] + } + }; + + const bids = spec.interpretResponse(serverResponse, req); + const parsedAdm = JSON.parse(bids[0].ad); + const trackers = parsedAdm.native?.eventtrackers || parsedAdm.eventtrackers; + expect(trackers[0].url).to.include('pbjs_adid='); + expect(trackers[0].url).to.include('pbjs_auc='); + expect(trackers[0].url).to.not.include('%native_dom_id%'); + }); + }); + } // FEATURES.NATIVE + + // ------------------------------------------------------------------------- + // getUserSyncs — iframe and pixel syncing + // ------------------------------------------------------------------------- + describe('getUserSyncs - iframe and pixel syncing', function () { + it('should add iframe sync when iframeEnabled and purpose-1 consent is present', function () { + const syncOptions = { iframeEnabled: true }; + const gdprConsent = { + gdprApplies: true, + consentString: 'cs', + vendorData: { purpose: { consents: { 1: true } } } }; - const result = spec.interpretResponse({ body: response }, {bidderRequest}); - expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); + const syncs = spec.getUserSyncs(syncOptions, [], gdprConsent); + expect(syncs).to.have.lengthOf(1); + expect(syncs[0].type).to.equal('iframe'); + expect(syncs[0].url).to.include('gdpr=1'); }); - it('should reject 0 cpm bids', function () { - const zeroCpmResponse = deepClone(response); - zeroCpmResponse.tags[0].ads[0].cpm = 0; + it('should have no gdpr params in pixel url when gdprConsent is null', function () { + const syncOptions = { pixelEnabled: true }; + const serverResponses = [{ + body: { ext: { appnexus: { userSync: { url: 'https://sync.example.com/px' } } } } + }]; + const syncs = spec.getUserSyncs(syncOptions, serverResponses, null); + expect(syncs).to.have.lengthOf(1); + expect(syncs[0].url).to.not.include('gdpr'); + }); + + it('should append gdpr params with & when pixel url already contains ?', function () { + const syncOptions = { pixelEnabled: true }; + const serverResponses = [{ + body: { ext: { appnexus: { userSync: { url: 'https://sync.example.com/px?existing=1' } } } } + }]; + const gdprConsent = { gdprApplies: true, consentString: 'cs' }; + const syncs = spec.getUserSyncs(syncOptions, serverResponses, gdprConsent); + expect(syncs[0].url).to.include('existing=1'); + expect(syncs[0].url).to.include('gdpr=1'); + expect(syncs[0].url).to.match(/\?existing=1&/); + }); + }); - const bidderRequest = { - bidderCode: 'mediafuse' + // ------------------------------------------------------------------------- + // getUserSyncs — iframeEnabled but consent denied (no iframe added) + // ------------------------------------------------------------------------- + describe('getUserSyncs - iframeEnabled denied by consent', function () { + it('should not add iframe sync when iframeEnabled but purpose-1 consent is denied', function () { + const syncOptions = { iframeEnabled: true }; + const gdprConsent = { + gdprApplies: true, + consentString: 'cs', + vendorData: { purpose: { consents: { 1: false } } } }; + const syncs = spec.getUserSyncs(syncOptions, [], gdprConsent); + expect(syncs).to.have.lengthOf(0); + }); - const result = spec.interpretResponse({ body: zeroCpmResponse }, { bidderRequest }); - expect(result.length).to.equal(0); + it('should not add pixel sync when serverResponses is empty', function () { + const syncOptions = { pixelEnabled: true }; + const syncs = spec.getUserSyncs(syncOptions, [], null); + expect(syncs).to.have.lengthOf(0); }); - it('should allow 0 cpm bids if allowZeroCpmBids setConfig is true', function () { - getGlobal().bidderSettings = { - mediafuse: { - allowZeroCpmBids: true + it('should not add pixel sync when response has no userSync url', function () { + const syncOptions = { pixelEnabled: true }; + const serverResponses = [{ body: { ext: { appnexus: {} } } }]; + const syncs = spec.getUserSyncs(syncOptions, serverResponses, null); + expect(syncs).to.have.lengthOf(0); + }); + }); + + // ------------------------------------------------------------------------- + // interpretResponse — bid_ad_type not in RESPONSE_MEDIA_TYPE_MAP + // ------------------------------------------------------------------------- + describe('interpretResponse - unknown bid_ad_type', function () { + it('should not throw when bid_ad_type=2 is not in the media type map', function () { + const [req] = spec.buildRequests([deepClone(BASE_BID)], deepClone(BASE_BIDDER_REQUEST)); + const impId = req.data.imp[0].id; + const serverResponse = { + body: { + seatbid: [{ + bid: [{ + impid: impId, + price: 1.5, + adm: '
creative
', + ext: { appnexus: { bid_ad_type: 2 } } + }] + }] } }; + expect(() => spec.interpretResponse(serverResponse, req)).to.not.throw(); + }); + }); - const zeroCpmResponse = deepClone(response); - zeroCpmResponse.tags[0].ads[0].cpm = 0; - - const bidderRequest = { - bidderCode: 'mediafuse', - bids: [{ - bidId: '3db3773286ee59', - adUnitCode: 'code' - }] + // ------------------------------------------------------------------------- + // buildRequests — topmostLocation falsy → rd_ref='' + // ------------------------------------------------------------------------- + describe('buildRequests - topmostLocation falsy', function () { + it('should set rd_ref to empty string when topmostLocation is not present', function () { + const bidderRequest = deepClone(BASE_BIDDER_REQUEST); + bidderRequest.refererInfo = { + topmostLocation: null, + reachedTop: false, + numIframes: 0, + stack: [] }; - - const result = spec.interpretResponse({ body: zeroCpmResponse }, { bidderRequest }); - expect(result.length).to.equal(1); - expect(result[0].cpm).to.equal(0); + const [req] = spec.buildRequests([deepClone(BASE_BID)], bidderRequest); + expect(req.data.ext.appnexus.referrer_detection.rd_ref).to.equal(''); }); + }); - it('handles nobid responses', function () { - const response = { - 'version': '0.0.1', - 'tags': [{ - 'uuid': '84ab500420319d', - 'tag_id': 5976557, - 'auction_id': '297492697822162468', - 'nobid': true - }] - }; - let bidderRequest; - - const result = spec.interpretResponse({ body: response }, {bidderRequest}); - expect(result.length).to.equal(0); - }); - - it('handles outstream video responses', function () { - const response = { - 'tags': [{ - 'uuid': '84ab500420319d', - 'ads': [{ - 'ad_type': 'video', - 'cpm': 0.500000, - 'notify_url': 'imptracker.com', - 'rtb': { - 'video': { - 'content': '' - } - }, - 'javascriptTrackers': '' - }] - }] + // ------------------------------------------------------------------------- + // buildRequests — addtlConsent all-NaN → addtl_consent not set + // ------------------------------------------------------------------------- + describe('buildRequests - addtlConsent all-NaN values', function () { + it('should not set addtl_consent when all values after ~ are NaN', function () { + const bidderRequest = deepClone(BASE_BIDDER_REQUEST); + bidderRequest.gdprConsent = { + gdprApplies: true, + consentString: 'cs', + addtlConsent: '1~abc.def.ghi' }; - const bidderRequest = { - bids: [{ - bidId: '84ab500420319d', - adUnitCode: 'code', - mediaTypes: { - video: { - context: 'outstream' - } - } - }] + const [req] = spec.buildRequests([deepClone(BASE_BID)], bidderRequest); + expect(req.data.user && req.data.user.ext && req.data.user.ext.addtl_consent).to.be.undefined; + }); + }); + + // ------------------------------------------------------------------------- + // buildRequests — EID with unrecognized source passes through unchanged + // ------------------------------------------------------------------------- + describe('buildRequests - EID unrecognized source', function () { + it('should pass through EID unchanged when source is neither adserver.org nor uidapi.com', function () { + const bid = deepClone(BASE_BID); + bid.userIdAsEids = [{ source: 'unknown-id-provider.com', uids: [{ id: 'some-id', atype: 1 }] }]; + const [req] = spec.buildRequests([bid], deepClone(BASE_BIDDER_REQUEST)); + const eid = req.data.user && req.data.user.ext && req.data.user.ext.eids && + req.data.user.ext.eids.find(e => e.source === 'unknown-id-provider.com'); + if (eid) { + expect(eid.rti_partner).to.be.undefined; } + }); + }); - const 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 () { - const response = { - 'tags': [{ - 'uuid': '84ab500420319d', - 'ads': [{ - 'ad_type': 'video', - 'cpm': 0.500000, - 'notify_url': 'imptracker.com', - 'rtb': { - 'video': { - 'asset_url': 'https://sample.vastURL.com/here/vid' - } - }, - 'javascriptTrackers': '' - }] - }] + // ------------------------------------------------------------------------- + // getBidFloor — edge cases + // ------------------------------------------------------------------------- + describe('buildRequests - getBidFloor edge cases', function () { + it('should return null when getFloor returns a non-plain-object (null)', function () { + const bid = deepClone(BASE_BID); + bid.getFloor = () => null; + const [req] = spec.buildRequests([bid], deepClone(BASE_BIDDER_REQUEST)); + expect(req.data.imp[0].bidfloor).to.be.undefined; + }); + + it('should return null when getFloor returns a NaN floor value', function () { + const bid = deepClone(BASE_BID); + bid.getFloor = () => ({ currency: 'USD', floor: NaN }); + const [req] = spec.buildRequests([bid], deepClone(BASE_BIDDER_REQUEST)); + expect(req.data.imp[0].bidfloor).to.be.undefined; + }); + }); + + // ------------------------------------------------------------------------- + // buildRequests — banner_frameworks type guard + // ------------------------------------------------------------------------- + describe('buildRequests - banner_frameworks invalid type', function () { + it('should not set banner_frameworks when value is a string (not array of nums)', function () { + const bid = deepClone(BASE_BID); + bid.mediaTypes = { banner: { sizes: [[300, 250]] } }; + bid.params.banner_frameworks = 'not-an-array'; + const [req] = spec.buildRequests([bid], deepClone(BASE_BIDDER_REQUEST)); + expect(req.data.imp[0].ext.appnexus.banner_frameworks).to.be.undefined; + }); + }); + + // ------------------------------------------------------------------------- + // buildRequests — video params frameworks type guard + // ------------------------------------------------------------------------- + describe('buildRequests - video params frameworks', function () { + it('should not set video_frameworks when params.video.frameworks is not an array', function () { + const bid = deepClone(BASE_BID); + bid.mediaTypes = { video: { context: 'instream', playerSize: [640, 480] } }; + bid.params.video = { frameworks: 'not-an-array' }; + const [req] = spec.buildRequests([bid], deepClone(BASE_BIDDER_REQUEST)); + expect(req.data.imp[0].ext.appnexus.video_frameworks).to.be.undefined; + }); + }); + + // ------------------------------------------------------------------------- + // buildRequests — banner_frameworks param fallback + // ------------------------------------------------------------------------- + describe('buildRequests - banner frameworks param fallback', function () { + it('should use bid.params.frameworks as fallback when banner_frameworks is absent', function () { + const bid = deepClone(BASE_BID); + bid.mediaTypes = { banner: { sizes: [[300, 250]] } }; + bid.params.frameworks = [1, 2]; + const [req] = spec.buildRequests([bid], deepClone(BASE_BIDDER_REQUEST)); + expect(req.data.imp[0].ext.appnexus.banner_frameworks).to.deep.equal([1, 2]); + }); + }); + + // ------------------------------------------------------------------------- + // buildRequests — refererInfo.stack absent + // ------------------------------------------------------------------------- + describe('buildRequests - refererInfo stack absent', function () { + it('should set rd_stk to undefined when stack is not present in refererInfo', function () { + const bidderRequest = deepClone(BASE_BIDDER_REQUEST); + bidderRequest.refererInfo = { + topmostLocation: 'http://example.com', + reachedTop: true, + numIframes: 0 }; - const bidderRequest = { - bids: [{ - bidId: '84ab500420319d', - adUnitCode: 'code', - mediaTypes: { - video: { - context: 'instream' - } + const [req] = spec.buildRequests([deepClone(BASE_BID)], bidderRequest); + expect(req.data.ext.appnexus.referrer_detection.rd_stk).to.be.undefined; + }); + }); + + // ------------------------------------------------------------------------- + // interpretResponse — renderer options from mediaTypes.video.renderer.options + // ------------------------------------------------------------------------- + if (FEATURES.VIDEO) { + describe('interpretResponse - renderer options from mediaTypes.video.renderer', function () { + it('should use mediaTypes.video.renderer.options when available', function () { + const bid = deepClone(BASE_BID); + bid.mediaTypes = { video: { context: 'outstream', playerSize: [640, 480], renderer: { options: { key: 'val' } } } }; + const [req] = spec.buildRequests([bid], deepClone(BASE_BIDDER_REQUEST)); + const impId = req.data.imp[0].id; + const serverResponse = { + body: { + seatbid: [{ + bid: [{ + impid: impId, + price: 3.0, + ext: { + appnexus: { + bid_ad_type: 1, + renderer_url: 'https://cdn.adnxs.com/renderer.js', + renderer_id: 42 + } + } + }] + }] } - }] - } + }; + const bids = spec.interpretResponse(serverResponse, req); + expect(bids[0].renderer).to.exist; + }); + }); + } // FEATURES.VIDEO + + // ------------------------------------------------------------------------- + // buildRequests — video params.video takes priority over mediaTypes.video for maxduration + // ------------------------------------------------------------------------- + describe('buildRequests - video maxduration skip overwrite', function () { + if (FEATURES.VIDEO) { + it('should not overwrite maxduration set by params.video when mediaTypes.video.maxduration also present', function () { + const bid = deepClone(BASE_BID); + bid.mediaTypes = { video: { context: 'instream', playerSize: [640, 480], maxduration: 15 } }; + bid.params.video = { maxduration: 30 }; + const [req] = spec.buildRequests([bid], deepClone(BASE_BIDDER_REQUEST)); + expect(req.data.imp[0].video.maxduration).to.equal(30); + }); + } + }); - const 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 adpod responses', function () { - const response = { - 'tags': [{ - 'uuid': '84ab500420319d', - 'ads': [{ - 'ad_type': 'video', - 'brand_category_id': 10, - 'cpm': 0.500000, - 'notify_url': 'imptracker.com', - 'rtb': { - 'video': { - 'asset_url': 'https://sample.vastURL.com/here/adpod', - 'duration_ms': 30000, - } - }, - 'viewability': { - 'config': '' - } - }] - }] - }; + // ------------------------------------------------------------------------- + // buildRequests — video params.video takes priority over mediaTypes.video for skippable + // ------------------------------------------------------------------------- + describe('buildRequests - video skippable skip overwrite', function () { + if (FEATURES.VIDEO) { + it('should not overwrite skippable set by params.video when mediaTypes.video.skip is also present', function () { + const bid = deepClone(BASE_BID); + bid.mediaTypes = { video: { context: 'instream', playerSize: [640, 480], skip: 1 } }; + bid.params.video = { skippable: false }; + const [req] = spec.buildRequests([bid], deepClone(BASE_BIDDER_REQUEST)); + expect(req.data.imp[0].video.skippable).to.be.false; + }); + } + }); - const bidderRequest = { - bids: [{ - bidId: '84ab500420319d', - adUnitCode: 'code', - mediaTypes: { - video: { - context: 'adpod' - } + // ------------------------------------------------------------------------- + // buildRequests — video params.video takes priority over mediaTypes.video for skipoffset + // ------------------------------------------------------------------------- + describe('buildRequests - video skipoffset skip overwrite', function () { + if (FEATURES.VIDEO) { + it('should not overwrite skipoffset set by params.video when mediaTypes.video.skipafter is also present', function () { + const bid = deepClone(BASE_BID); + bid.mediaTypes = { video: { context: 'instream', playerSize: [640, 480], skipafter: 10 } }; + bid.params.video = { skipoffset: 5 }; + const [req] = spec.buildRequests([bid], deepClone(BASE_BIDDER_REQUEST)); + expect(req.data.imp[0].video.skipoffset).to.equal(5); + }); + } + }); + + // ------------------------------------------------------------------------- + // buildRequests — video playbackmethod type guard + // ------------------------------------------------------------------------- + describe('buildRequests - video playbackmethod', function () { + if (FEATURES.VIDEO) { + it('should not set playback_method when mediaTypes.video.playbackmethod is not an array', function () { + const bid = deepClone(BASE_BID); + bid.mediaTypes = { video: { context: 'instream', playerSize: [640, 480], playbackmethod: 2 } }; + const [req] = spec.buildRequests([bid], deepClone(BASE_BIDDER_REQUEST)); + expect(req.data.imp[0].video.playback_method).to.be.undefined; + }); + } + }); + + // ------------------------------------------------------------------------- + // interpretResponse — video nurl without asset_url + // ------------------------------------------------------------------------- + describe('interpretResponse - video nurl without asset_url', function () { + if (FEATURES.VIDEO) { + it('should set vastImpUrl from nurl and not override vastUrl with redir pattern when asset_url is absent', function () { + const bid = deepClone(BASE_BID); + bid.mediaTypes = { video: { context: 'instream', playerSize: [640, 480] } }; + const [req] = spec.buildRequests([bid], deepClone(BASE_BIDDER_REQUEST)); + const impId = req.data.imp[0].id; + + const serverResponse = { + body: { + seatbid: [{ + bid: [{ + impid: impId, + price: 1.0, + nurl: 'https://notify.example.com/win', + ext: { + appnexus: { + bid_ad_type: 1 + // no asset_url, no renderer_url/renderer_id + } + } + }] + }] } - }] + }; + const bids = spec.interpretResponse(serverResponse, req); + expect(bids[0].vastImpUrl).to.equal('https://notify.example.com/win'); + // ortbConverter sets vastUrl from nurl; our adapter does not override it with a redir pattern + expect(bids[0].vastUrl).to.equal('https://notify.example.com/win'); + }); + } + }); + + // ------------------------------------------------------------------------- + // onBidWon — viewability script reload + // ------------------------------------------------------------------------- + describe('onBidWon - viewability', function () { + it('should not throw when bid has no native property', function () { + expect(() => spec.onBidWon({ cpm: 1.0, adUnitCode: 'test' })).to.not.throw(); + }); + + it('should traverse viewability helpers for a string tracker matching cdn.adnxs.com pattern', function () { + const jsScript = ''; + const bid = { + adId: 'ad-id-1', + adUnitCode: 'adunit-code', + native: { javascriptTrackers: jsScript } }; + // Exercises reloadViewabilityScriptWithCorrectParameters, strIsMediafuseViewabilityScript, + // getMediafuseViewabilityScriptFromJsTrackers, and getViewabilityScriptUrlFromPayload. + expect(() => spec.onBidWon(bid)).to.not.throw(); + }); - const 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 () { - const 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': 'MediaFuse', - 'icon': { - 'width': 0, - 'height': 0, - 'url': 'https://cdn.adnxs.com/icon.png' - }, - 'main_img': { - 'width': 2352, - 'height': 1516, - 'url': 'https://cdn.adnxs.com/img.png' - }, - 'link': { - 'url': 'https://www.mediafuse.com', - 'fallback_url': '', - 'click_trackers': ['https://nym1-ib.adnxs.com/click'] - }, - 'impression_trackers': ['https://example.com'], - 'rating': '5', - 'displayurl': 'https://mediafuse.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': 'https://www.mediafuse.com/privacy-policy-agreement/', - 'javascriptTrackers': '' + it('should handle array of trackers and pick the viewability one', function () { + const jsScript = ''; + const bid = { + adId: 'ad-id-2', + adUnitCode: 'adunit-code', + native: { javascriptTrackers: ['', jsScript] } }; - const bidderRequest = { - bids: [{ - bidId: '3db3773286ee59', - adUnitCode: 'code' - }] - } + // Exercises the array branch in getMediafuseViewabilityScriptFromJsTrackers. + expect(() => spec.onBidWon(bid)).to.not.throw(); + }); - const 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('https://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: [{ - bidId: '3db3773286ee59', - renderer: { - options: { - adText: 'configured' - } - }, - mediaTypes: { - video: { - context: 'outstream' - } - } - }] + it('should not throw when tracker string does not match viewability pattern', function () { + const bid = { + adId: 'ad-id-3', + adUnitCode: 'adunit-code', + native: { javascriptTrackers: '' } }; + expect(() => spec.onBidWon(bid)).to.not.throw(); + }); - const result = spec.interpretResponse({ body: outstreamResponse }, {bidderRequest}); - expect(result[0].renderer.config).to.deep.equal( - bidderRequest.bids[0].renderer.options - ); - }); - - it('should add deal_priority and deal_code', function() { - const responseWithDeal = deepClone(response); - responseWithDeal.tags[0].ads[0].ad_type = 'video'; - responseWithDeal.tags[0].ads[0].deal_priority = 5; - responseWithDeal.tags[0].ads[0].deal_code = '123'; - responseWithDeal.tags[0].ads[0].rtb.video = { - duration_ms: 1500, - player_width: 640, - player_height: 340, + it('should handle cdn.adnxs-simple.com pattern tracker', function () { + const jsScript = ''; + const bid = { + adId: 'ad-id-4', + adUnitCode: 'adunit-code', + native: { javascriptTrackers: jsScript } }; + expect(() => spec.onBidWon(bid)).to.not.throw(); + }); + }); - const bidderRequest = { - bids: [{ - bidId: '3db3773286ee59', - adUnitCode: 'code', - mediaTypes: { - video: { - context: 'adpod' - } - } - }] - } - const result = spec.interpretResponse({ body: responseWithDeal }, {bidderRequest}); - expect(Object.keys(result[0].mediafuse)).to.include.members(['buyerMemberId', 'dealPriority', 'dealCode']); - expect(result[0].video.dealTier).to.equal(5); + // ------------------------------------------------------------------------- + // onBidderError + // ------------------------------------------------------------------------- + describe('onBidderError', function () { + it('should log an error message via utils.logError', function () { + const logSpy = sandbox.spy(utils, 'logError'); + spec.onBidderError({ error: new Error('network timeout'), bidderRequest: deepClone(BASE_BIDDER_REQUEST) }); + expect(logSpy.called).to.be.true; + expect(logSpy.firstCall.args[0]).to.include('Mediafuse Bidder Error'); }); - it('should add advertiser id', function() { - const responseAdvertiserId = deepClone(response); - responseAdvertiserId.tags[0].ads[0].advertiser_id = '123'; + it('should include the error message in the logged string', function () { + const logSpy = sandbox.spy(utils, 'logError'); + spec.onBidderError({ error: new Error('timeout'), bidderRequest: deepClone(BASE_BIDDER_REQUEST) }); + expect(logSpy.firstCall.args[0]).to.include('timeout'); + }); + }); - const bidderRequest = { - bids: [{ - bidId: '3db3773286ee59', - adUnitCode: 'code' - }] - } - const result = spec.interpretResponse({ body: responseAdvertiserId }, {bidderRequest}); - expect(Object.keys(result[0].meta)).to.include.members(['advertiserId']); + // ------------------------------------------------------------------------- + // buildRequests — debug cookie + // ------------------------------------------------------------------------- + describe('buildRequests - debug cookie', function () { + it('should append debug params to URL when valid debug cookie is set', function () { + sandbox.stub(storage, 'getCookie').returns(JSON.stringify({ enabled: true, dongle: 'mfd' })); + const [req] = spec.buildRequests([deepClone(BASE_BID)], deepClone(BASE_BIDDER_REQUEST)); + expect(req.url).to.include('debug=1'); + expect(req.url).to.include('dongle=mfd'); }); - it('should add brand id', function() { - const responseBrandId = deepClone(response); - responseBrandId.tags[0].ads[0].brand_id = 123; + it('should not crash and should skip debug URL when cookie JSON is invalid', function () { + sandbox.stub(storage, 'getCookie').returns('{invalid-json'); + const [req] = spec.buildRequests([deepClone(BASE_BID)], deepClone(BASE_BIDDER_REQUEST)); + expect(req.url).to.not.include('debug=1'); + }); - const bidderRequest = { - bids: [{ - bidId: '3db3773286ee59', - adUnitCode: 'code' - }] - } - const result = spec.interpretResponse({ body: responseBrandId }, {bidderRequest}); - expect(Object.keys(result[0].meta)).to.include.members(['brandId']); + it('should not append debug params when cookie is absent and no debug URL params', function () { + sandbox.stub(storage, 'getCookie').returns(null); + const [req] = spec.buildRequests([deepClone(BASE_BID)], deepClone(BASE_BIDDER_REQUEST)); + expect(req.url).to.not.include('debug=1'); }); + }); - it('should add advertiserDomains', function() { - const responseAdvertiserId = deepClone(response); - responseAdvertiserId.tags[0].ads[0].adomain = ['123']; + // ------------------------------------------------------------------------- + // buildRequests — addtlConsent (GDPR additional consent string) + // ------------------------------------------------------------------------- + describe('buildRequests - addtlConsent', function () { + it('should parse addtlConsent with ~ separator and set user.ext.addtl_consent', function () { + const bidderRequest = deepClone(BASE_BIDDER_REQUEST); + bidderRequest.gdprConsent = { + gdprApplies: true, + consentString: 'consent-string', + addtlConsent: 'abc~1.2.3' + }; + const [req] = spec.buildRequests([deepClone(BASE_BID)], bidderRequest); + expect(req.data.user.ext.addtl_consent).to.deep.equal([1, 2, 3]); + }); - const bidderRequest = { - bids: [{ - bidId: '3db3773286ee59', - adUnitCode: 'code' - }] - } - const result = spec.interpretResponse({ body: responseAdvertiserId }, {bidderRequest}); - expect(Object.keys(result[0].meta)).to.include.members(['advertiserDomains']); - expect(Object.keys(result[0].meta.advertiserDomains)).to.deep.equal([]); + it('should not set addtl_consent when addtlConsent has no ~ separator', function () { + const bidderRequest = deepClone(BASE_BIDDER_REQUEST); + bidderRequest.gdprConsent = { + gdprApplies: true, + consentString: 'consent-string', + addtlConsent: 'abc123' + }; + const [req] = spec.buildRequests([deepClone(BASE_BID)], bidderRequest); + const addtlConsent = utils.deepAccess(req.data, 'user.ext.addtl_consent'); + expect(addtlConsent).to.be.undefined; + }); + + it('should skip addtl_consent when addtlConsent segment list is empty after parsing', function () { + const bidderRequest = deepClone(BASE_BIDDER_REQUEST); + bidderRequest.gdprConsent = { + gdprApplies: true, + consentString: 'consent-string', + addtlConsent: 'abc~' + }; + const [req] = spec.buildRequests([deepClone(BASE_BID)], bidderRequest); + const addtlConsent = utils.deepAccess(req.data, 'user.ext.addtl_consent'); + expect(addtlConsent).to.be.undefined; + }); + }); + + // ------------------------------------------------------------------------- + // buildRequests — refererInfo canonicalUrl branch + // ------------------------------------------------------------------------- + describe('buildRequests - refererInfo canonicalUrl', function () { + it('should include rd_can when canonicalUrl is present in refererInfo', function () { + const bidderRequest = deepClone(BASE_BIDDER_REQUEST); + bidderRequest.refererInfo.canonicalUrl = 'https://canonical.example.com/page'; + const [req] = spec.buildRequests([deepClone(BASE_BID)], bidderRequest); + expect(req.data.ext.appnexus.referrer_detection.rd_can).to.equal('https://canonical.example.com/page'); }); }); + + // ------------------------------------------------------------------------- + // buildRequests — video params.video.frameworks branch + // ------------------------------------------------------------------------- + describe('buildRequests - video params.video.frameworks', function () { + if (FEATURES.VIDEO) { + it('should set video_frameworks from bid.params.video.frameworks', function () { + const bid = deepClone(BASE_BID); + bid.mediaTypes = { video: { context: 'instream', playerSize: [640, 480] } }; + bid.params.video = { frameworks: [1, 2, 6], minduration: 5 }; + const [req] = spec.buildRequests([bid], deepClone(BASE_BIDDER_REQUEST)); + expect(req.data.imp[0].ext.appnexus.video_frameworks).to.deep.equal([1, 2, 6]); + expect(req.data.imp[0].video.minduration).to.equal(5); + }); + } + }); }); diff --git a/test/spec/modules/mediagoBidAdapter_spec.js b/test/spec/modules/mediagoBidAdapter_spec.js index 8c4c6bc0f3a..c69841abb0e 100644 --- a/test/spec/modules/mediagoBidAdapter_spec.js +++ b/test/spec/modules/mediagoBidAdapter_spec.js @@ -141,6 +141,19 @@ describe('mediago:BidAdapterTests', function () { expect(req_data.imp).to.have.lengthOf(1); }); + it('mediago:validate_transactionId_in_request', function () { + request = spec.buildRequests(bidRequestData.bids, bidRequestData); + const req_data = JSON.parse(request.data); + expect(req_data.imp[0].ext.transactionId).to.equal('7b26fdae-96e6-4c35-a18b-218dda11397d'); + }); + + it('mediago:validate_pbjs_source_and_version_in_request', function () { + request = spec.buildRequests(bidRequestData.bids, bidRequestData); + const req_data = JSON.parse(request.data); + expect(req_data.ext.pbjsversion).to.be.a('string'); + expect(req_data.ext.pbjsversion.length).to.be.above(0); + }); + describe('mediago: buildRequests', function() { describe('getPmgUID function', function() { let sandbox; diff --git a/test/spec/modules/mediaimpactBidAdapter_spec.js b/test/spec/modules/mediaimpactBidAdapter_spec.js index 518397b11f8..5db6f9c6611 100644 --- a/test/spec/modules/mediaimpactBidAdapter_spec.js +++ b/test/spec/modules/mediaimpactBidAdapter_spec.js @@ -1,6 +1,6 @@ -import {expect} from 'chai'; -import {spec, ENDPOINT_PROTOCOL, ENDPOINT_DOMAIN, ENDPOINT_PATH} from 'modules/mediaimpactBidAdapter.js'; -import {newBidder} from 'src/adapters/bidderFactory.js'; +import { expect } from 'chai'; +import { spec, ENDPOINT_PROTOCOL, ENDPOINT_DOMAIN, ENDPOINT_PATH } from 'modules/mediaimpactBidAdapter.js'; +import { newBidder } from 'src/adapters/bidderFactory.js'; import * as miUtils from 'libraries/mediaImpactUtils/index.js'; const BIDDER_CODE = 'mediaimpact'; @@ -144,9 +144,9 @@ describe('MediaimpactAdapter', function () { 'advertiserDomains': ['test.domain'] }, 'syncs': [ - {'type': 'image', 'url': 'https://test.domain/tracker_1.gif'}, - {'type': 'image', 'url': 'https://test.domain/tracker_2.gif'}, - {'type': 'image', 'url': 'https://test.domain/tracker_3.gif'} + { 'type': 'image', 'url': 'https://test.domain/tracker_1.gif' }, + { 'type': 'image', 'url': 'https://test.domain/tracker_2.gif' }, + { 'type': 'image', 'url': 'https://test.domain/tracker_3.gif' } ], 'winNotification': [ { @@ -154,7 +154,7 @@ describe('MediaimpactAdapter', function () { 'path': '/hb/bid_won?test=1', 'data': { 'ad': [ - {'dsp': 8, 'id': 800008, 'cost': 1.0e-5, 'nurl': 'https://test.domain/'} + { 'dsp': 8, 'id': 800008, 'cost': 1.0e-5, 'nurl': 'https://test.domain/' } ], 'unit_id': 1234, 'site_id': 123 @@ -179,7 +179,7 @@ describe('MediaimpactAdapter', function () { expect(result[0].currency).to.equal('USD'); expect(result[0].ttl).to.equal(60); expect(result[0].meta.advertiserDomains).to.deep.equal(['test.domain']); - expect(result[0].winNotification[0]).to.deep.equal({'method': 'POST', 'path': '/hb/bid_won?test=1', 'data': {'ad': [{'dsp': 8, 'id': 800008, 'cost': 1.0e-5, 'nurl': 'https://test.domain/'}], 'unit_id': 1234, 'site_id': 123}}); + expect(result[0].winNotification[0]).to.deep.equal({ 'method': 'POST', 'path': '/hb/bid_won?test=1', 'data': { 'ad': [{ 'dsp': 8, 'id': 800008, 'cost': 1.0e-5, 'nurl': 'https://test.domain/' }], 'unit_id': 1234, 'site_id': 123 } }); }); }); @@ -227,7 +227,7 @@ describe('MediaimpactAdapter', function () { 'path': '/hb/bid_won?test=1', 'data': { 'ad': [ - {'dsp': 8, 'id': 800008, 'cost': 0.01, 'nurl': 'http://test.domain/'} + { 'dsp': 8, 'id': 800008, 'cost': 0.01, 'nurl': 'http://test.domain/' } ], 'unit_id': 1234, 'site_id': 123 @@ -268,9 +268,9 @@ describe('MediaimpactAdapter', function () { 'advertiserDomains': ['test.domain'] }, 'syncs': [ - {'type': 'image', 'link': 'https://test.domain/tracker_1.gif'}, - {'type': 'image', 'link': 'https://test.domain/tracker_2.gif'}, - {'type': 'image', 'link': 'https://test.domain/tracker_3.gif'} + { 'type': 'image', 'link': 'https://test.domain/tracker_1.gif' }, + { 'type': 'image', 'link': 'https://test.domain/tracker_2.gif' }, + { 'type': 'image', 'link': 'https://test.domain/tracker_3.gif' } ], 'winNotification': [ { @@ -278,7 +278,7 @@ describe('MediaimpactAdapter', function () { 'path': '/hb/bid_won?test=1', 'data': { 'ad': [ - {'dsp': 8, 'id': 800008, 'cost': 1.0e-5, 'nurl': 'https://test.domain/'} + { 'dsp': 8, 'id': 800008, 'cost': 1.0e-5, 'nurl': 'https://test.domain/' } ], 'unit_id': 1234, 'site_id': 123 diff --git a/test/spec/modules/mediakeysBidAdapter_spec.js b/test/spec/modules/mediakeysBidAdapter_spec.js index d724b0891b1..d3aa61aacc7 100644 --- a/test/spec/modules/mediakeysBidAdapter_spec.js +++ b/test/spec/modules/mediakeysBidAdapter_spec.js @@ -303,7 +303,7 @@ describe('mediakeysBidAdapter', function () { it('should log errors and ignore misformated assets', function() { const bidRequests = [utils.deepClone(bidNative)]; delete bidRequests[0].nativeParams.title.len; - bidRequests[0].nativeParams.unregistred = {required: true}; + bidRequests[0].nativeParams.unregistred = { required: true }; const bidderRequestCopy = utils.deepClone(bidderRequest); bidderRequestCopy.bids = bidRequests; @@ -593,7 +593,7 @@ describe('mediakeysBidAdapter', function () { }; const bidRequests = [utils.deepClone(bid)]; - const request = spec.buildRequests(bidRequests, {...bidderRequest, ortb2}); + const request = spec.buildRequests(bidRequests, { ...bidderRequest, ortb2 }); const data = request.data; expect(data.site.domain).to.equal('domain.example'); expect(data.site.cat[0]).to.equal('IAB12'); diff --git a/test/spec/modules/medianetAnalyticsAdapter_spec.js b/test/spec/modules/medianetAnalyticsAdapter_spec.js index e3933a966ac..fa83d593a92 100644 --- a/test/spec/modules/medianetAnalyticsAdapter_spec.js +++ b/test/spec/modules/medianetAnalyticsAdapter_spec.js @@ -1,14 +1,14 @@ -import {expect} from 'chai'; +import { expect } from 'chai'; import medianetAnalytics from 'modules/medianetAnalyticsAdapter.js'; import * as utils from 'src/utils.js'; -import {EVENTS, REJECTION_REASON} from 'src/constants.js'; +import { EVENTS, REJECTION_REASON } from 'src/constants.js'; import * as events from 'src/events.js'; -import {clearEvents} from 'src/events.js'; -import {deepAccess} from 'src/utils.js'; +import { clearEvents } from 'src/events.js'; +import { deepAccess } from 'src/utils.js'; import 'src/prebid.js'; -import {config} from 'src/config.js'; +import { config } from 'src/config.js'; -import {getGlobal} from 'src/prebidGlobal.js'; +import { getGlobal } from 'src/prebidGlobal.js'; import sinon from "sinon"; import * as mnUtils from '../../../libraries/medianetUtils/utils.js'; @@ -36,7 +36,7 @@ function createBidResponse(bidderCode, requestId, cpm, adId = '3e6e4bce5c8fb3') requestId, mediaType: 'banner', source: 'client', - ext: {pvid: 123, crid: '321'}, + ext: { pvid: 123, crid: '321' }, no_bid: false, cpm, ad: 'AD_CODE', @@ -47,7 +47,7 @@ function createBidResponse(bidderCode, requestId, cpm, adId = '3e6e4bce5c8fb3') dfp_id: 'div-gpt-ad-1460505748561-0', originalCpm: cpm, originalCurrency: 'USD', - floorData: {floorValue: 1.10, floorRule: 'banner'}, + floorData: { floorValue: 1.10, floorRule: 'banner' }, auctionId: '8e0d5245-deb3-406c-96ca-9b609e077ff7', snm: 'SUCCESS', responseTimestamp: 1584563606009, @@ -71,7 +71,7 @@ function createBidResponse(bidderCode, requestId, cpm, adId = '3e6e4bce5c8fb3') hb_format: 'banner', prebid_test: 1 }, - params: [{cid: 'test123', crid: '451466393'}] + params: [{ cid: 'test123', crid: '451466393' }] }; } @@ -81,7 +81,7 @@ function createBidRequest(bidderCode, auctionId, bidId, adUnits) { auctionId, bids: adUnits.map(adUnit => ({ bidder: bidderCode, - params: {cid: 'TEST_CID', crid: '451466393'}, + params: { cid: 'TEST_CID', crid: '451466393' }, mediaTypes: adUnit.mediaTypes, adUnitCode: adUnit.code, sizes: [(adUnit.mediaTypes.banner?.sizes || []), (adUnit.mediaTypes.native?.image?.sizes || []), (adUnit.mediaTypes.video?.playerSize || [])], @@ -100,7 +100,7 @@ function createNoBid(bidder, params) { return { bidder, params, - mediaTypes: {banner: {sizes: [[300, 250]], ext: ['asdads']}}, + mediaTypes: { banner: { sizes: [[300, 250]], ext: ['asdads'] } }, adUnitCode: 'div-gpt-ad-1460505748561-0', transactionId: '303fa0c6-682f-4aea-8e4a-dc68f0d5c7d5', sizes: [[300, 250], [300, 600]], @@ -166,29 +166,29 @@ function createBidWon(bidderCode, adId, requestId, cpm) { prebid_test: 1 }, status: 'rendered', - params: [{cid: 'test123', crid: '451466393'}] + params: [{ cid: 'test123', crid: '451466393' }] }; } -const BANNER_AD_UNIT = {code: 'div-gpt-ad-1460505748561-0', mediaTypes: {banner: {sizes: [[300, 250]]}}}; +const BANNER_AD_UNIT = { code: 'div-gpt-ad-1460505748561-0', mediaTypes: { banner: { sizes: [[300, 250]] } } }; const VIDEO_AD_UNIT = { code: 'div-gpt-ad-1460505748561-0', - mediaTypes: {video: {playerSize: [640, 480], context: 'outstream'}} + mediaTypes: { video: { playerSize: [640, 480], context: 'outstream' } } }; const INSTREAM_VIDEO_AD_UNIT = { code: 'div-gpt-ad-1460505748561-0', - mediaTypes: {video: {playerSize: [640, 480], context: 'instream'}} + mediaTypes: { video: { playerSize: [640, 480], context: 'instream' } } }; const BANNER_NATIVE_AD_UNIT = { code: 'div-gpt-ad-1460505748561-0', - mediaTypes: {banner: {sizes: [[300, 250]]}, native: {image: {required: true, sizes: [150, 50]}}} + mediaTypes: { banner: { sizes: [[300, 250]] }, native: { image: { required: true, sizes: [150, 50] } } } }; const BANNER_NATIVE_VIDEO_AD_UNIT = { code: 'div-gpt-ad-1460505748561-0', mediaTypes: { - banner: {sizes: [[300, 250]]}, - video: {playerSize: [640, 480], context: 'outstream'}, - native: {image: {required: true, sizes: [150, 50]}, title: {required: true, len: 80}} + banner: { sizes: [[300, 250]] }, + video: { playerSize: [640, 480], context: 'outstream' }, + native: { image: { required: true, sizes: [150, 50] }, title: { required: true, len: 80 } } } }; @@ -215,7 +215,7 @@ const MOCK = { auctionId: '8e0d5245-deb3-406c-96ca-9b609e077ff7', timestamp: 1584563605739, timeout: 6000, - bidderRequests: [{bids: [{floorData: {enforcements: {enforceJS: true}}}]}] + bidderRequests: [{ bids: [{ floorData: { enforcements: { enforceJS: true } } }] }] }, MNET_BID_REQUESTED: createBidRequest('medianet', '8e0d5245-deb3-406c-96ca-9b609e077ff7', '28248b0e6aece2', [BANNER_NATIVE_VIDEO_AD_UNIT]), MNET_BID_RESPONSE: createBidResponse('medianet', '28248b0e6aece2', 2.299), @@ -234,14 +234,14 @@ const MOCK = { MULTI_BID_RESPONSES: [ createBidResponse('medianet', '28248b0e6aece2', 2.299, '3e6e4bce5c8fb3'), Object.assign(createBidResponse('medianet', '28248b0e6aebecc', 3.299, '3e6e4bce5c8fb4'), - {originalBidder: 'bidA2', originalRequestId: '28248b0e6aece2'}), + { originalBidder: 'bidA2', originalRequestId: '28248b0e6aece2' }), ], - MNET_NO_BID: createNoBid('medianet', {cid: 'test123', crid: '451466393', site: {}}), + MNET_NO_BID: createNoBid('medianet', { cid: 'test123', crid: '451466393', site: {} }), MNET_BID_TIMEOUT: createBidTimeout('28248b0e6aece2', 'medianet', '8e0d5245-deb3-406c-96ca-9b609e077ff7', [{ cid: 'test123', crid: '451466393', site: {} - }, {cid: '8CUX0H51P', crid: '451466393', site: {}}]), + }, { cid: '8CUX0H51P', crid: '451466393', site: {} }]), AUCTION_END: { auctionId: '8e0d5245-deb3-406c-96ca-9b609e077ff7', auctionEnd: 1584563605739, @@ -266,11 +266,11 @@ const MOCK = { hb_bidder_medianet: 'medianet' } }, - NO_BID_SET_TARGETING: {'div-gpt-ad-1460505748561-0': {}}, + NO_BID_SET_TARGETING: { 'div-gpt-ad-1460505748561-0': {} }, // S2S mocks MNET_S2S_BID_REQUESTED: createS2SBidRequest('medianet', '8e0d5245-deb3-406c-96ca-9b609e077ff7', '28248b0e6aece2', [BANNER_AD_UNIT]), MNET_S2S_BID_RESPONSE: createS2SBidResponse('medianet', '28248b0e6aece2', 2.299), - MNET_S2S_BID_WON: Object.assign({}, createBidWon('medianet', '3e6e4bce5c8fb3', '28248b0e6aece2', 2.299), {source: 's2s'}), + MNET_S2S_BID_WON: Object.assign({}, createBidWon('medianet', '3e6e4bce5c8fb3', '28248b0e6aece2', 2.299), { source: 's2s' }), MNET_S2S_SET_TARGETING: { 'div-gpt-ad-1460505748561-0': { prebid_test: '1', @@ -322,15 +322,15 @@ function waitForPromiseResolve(promise) { } function performNoBidAuction() { - events.emit(AUCTION_INIT, Object.assign({}, MOCK.AUCTION_INIT, {adUnits: MOCK.AD_UNITS})); + events.emit(AUCTION_INIT, Object.assign({}, MOCK.AUCTION_INIT, { adUnits: MOCK.AD_UNITS })); events.emit(BID_REQUESTED, MOCK.MNET_BID_REQUESTED); events.emit(NO_BID, MOCK.MNET_NO_BID); - events.emit(AUCTION_END, Object.assign({}, MOCK.AUCTION_END, {bidderRequests: [MOCK.MNET_BID_REQUESTED]})); + events.emit(AUCTION_END, Object.assign({}, MOCK.AUCTION_END, { bidderRequests: [MOCK.MNET_BID_REQUESTED] })); events.emit(SET_TARGETING, MOCK.MNET_SET_TARGETING); } function performBidWonAuction() { - events.emit(AUCTION_INIT, Object.assign({}, MOCK.AUCTION_INIT, {adUnits: MOCK.AD_UNITS})); + events.emit(AUCTION_INIT, Object.assign({}, MOCK.AUCTION_INIT, { adUnits: MOCK.AD_UNITS })); events.emit(BID_REQUESTED, MOCK.MNET_BID_REQUESTED); events.emit(BID_RESPONSE, MOCK.MNET_BID_RESPONSE); events.emit(SET_TARGETING, MOCK.MNET_SET_TARGETING); @@ -338,63 +338,63 @@ function performBidWonAuction() { } function performBidTimeoutAuction() { - events.emit(AUCTION_INIT, Object.assign({}, MOCK.AUCTION_INIT, {adUnits: MOCK.AD_UNITS})); + events.emit(AUCTION_INIT, Object.assign({}, MOCK.AUCTION_INIT, { adUnits: MOCK.AD_UNITS })); events.emit(BID_REQUESTED, MOCK.MNET_BID_REQUESTED); events.emit(BID_TIMEOUT, MOCK.MNET_BID_TIMEOUT); - events.emit(AUCTION_END, Object.assign({}, MOCK.AUCTION_END, {bidderRequests: [MOCK.MNET_BID_REQUESTED]})); + events.emit(AUCTION_END, Object.assign({}, MOCK.AUCTION_END, { bidderRequests: [MOCK.MNET_BID_REQUESTED] })); events.emit(SET_TARGETING, MOCK.MNET_SET_TARGETING); } function performAuctionWithSameRequestIdBids(bidRespArray) { - events.emit(AUCTION_INIT, Object.assign({}, MOCK.AUCTION_INIT, {adUnits: MOCK.AD_UNITS})); + events.emit(AUCTION_INIT, Object.assign({}, MOCK.AUCTION_INIT, { adUnits: MOCK.AD_UNITS })); events.emit(BID_REQUESTED, MOCK.MNET_BID_REQUESTED); bidRespArray.forEach(bidResp => events.emit(BID_RESPONSE, bidResp)); - events.emit(AUCTION_END, Object.assign({}, MOCK.AUCTION_END, {bidderRequests: [MOCK.MNET_BID_REQUESTED]})); + events.emit(AUCTION_END, Object.assign({}, MOCK.AUCTION_END, { bidderRequests: [MOCK.MNET_BID_REQUESTED] })); events.emit(SET_TARGETING, MOCK.MNET_SET_TARGETING); events.emit(BID_WON, MOCK.MNET_BID_WON); } function performAuctionNoWin() { - events.emit(AUCTION_INIT, Object.assign({}, MOCK.AUCTION_INIT, {adUnits: MOCK.AD_UNITS})); + events.emit(AUCTION_INIT, Object.assign({}, MOCK.AUCTION_INIT, { adUnits: MOCK.AD_UNITS })); MOCK.COMMON_REQ_ID_BID_REQUESTS.forEach(bidReq => events.emit(BID_REQUESTED, bidReq)); MOCK.COMMON_REQ_ID_BID_RESPONSES.forEach(bidResp => events.emit(BID_RESPONSE, bidResp)); - events.emit(AUCTION_END, Object.assign({}, MOCK.AUCTION_END, {bidderRequests: MOCK.COMMON_REQ_ID_BID_REQUESTS})); + events.emit(AUCTION_END, Object.assign({}, MOCK.AUCTION_END, { bidderRequests: MOCK.COMMON_REQ_ID_BID_REQUESTS })); events.emit(SET_TARGETING, MOCK.MNET_SET_TARGETING); } function performMultiBidAuction() { const bidRequest = createBidRequest('medianet', '8e0d5245-deb3-406c-96ca-9b609e077ff7', '28248b0e6aece2', [BANNER_AD_UNIT]); - events.emit(AUCTION_INIT, Object.assign({}, MOCK.AUCTION_INIT, {adUnits: MOCK.AD_UNITS})); + events.emit(AUCTION_INIT, Object.assign({}, MOCK.AUCTION_INIT, { adUnits: MOCK.AD_UNITS })); events.emit(BID_REQUESTED, bidRequest); MOCK.MULTI_BID_RESPONSES.forEach(bidResp => events.emit(BID_RESPONSE, bidResp)); - events.emit(AUCTION_END, Object.assign({}, MOCK.AUCTION_END, {bidderRequests: [bidRequest]})); + events.emit(AUCTION_END, Object.assign({}, MOCK.AUCTION_END, { bidderRequests: [bidRequest] })); events.emit(SET_TARGETING, MOCK.MNET_SET_TARGETING); } function performBidRejectedAuction() { - events.emit(AUCTION_INIT, Object.assign({}, MOCK.AUCTION_INIT, {adUnits: MOCK.AD_UNITS})); + events.emit(AUCTION_INIT, Object.assign({}, MOCK.AUCTION_INIT, { adUnits: MOCK.AD_UNITS })); events.emit(BID_REQUESTED, MOCK.MNET_BID_REQUESTED); events.emit(BID_RESPONSE, MOCK.MNET_BID_RESPONSE); events.emit(BID_REJECTED, Object.assign({}, MOCK.MNET_BID_RESPONSE, { rejectionReason: REJECTION_REASON.FLOOR_NOT_MET, })); - events.emit(AUCTION_END, Object.assign({}, MOCK.AUCTION_END, {bidderRequests: [MOCK.MNET_BID_REQUESTED]})); + events.emit(AUCTION_END, Object.assign({}, MOCK.AUCTION_END, { bidderRequests: [MOCK.MNET_BID_REQUESTED] })); } function performS2SAuction() { - events.emit(AUCTION_INIT, Object.assign({}, MOCK.AUCTION_INIT, {adUnits: MOCK.AD_UNITS})); + events.emit(AUCTION_INIT, Object.assign({}, MOCK.AUCTION_INIT, { adUnits: MOCK.AD_UNITS })); events.emit(BID_REQUESTED, MOCK.MNET_S2S_BID_REQUESTED); events.emit(BID_RESPONSE, MOCK.MNET_S2S_BID_RESPONSE); - events.emit(AUCTION_END, Object.assign({}, MOCK.AUCTION_END, {bidderRequests: [MOCK.MNET_S2S_BID_REQUESTED]})); + events.emit(AUCTION_END, Object.assign({}, MOCK.AUCTION_END, { bidderRequests: [MOCK.MNET_S2S_BID_REQUESTED] })); events.emit(SET_TARGETING, MOCK.MNET_S2S_SET_TARGETING); events.emit(BID_WON, MOCK.MNET_S2S_BID_WON); } function performCurrencyConversionAuction() { - events.emit(AUCTION_INIT, Object.assign({}, MOCK.AUCTION_INIT, {adUnits: MOCK.AD_UNITS})); + events.emit(AUCTION_INIT, Object.assign({}, MOCK.AUCTION_INIT, { adUnits: MOCK.AD_UNITS })); events.emit(BID_REQUESTED, MOCK.MNET_BID_REQUESTED); events.emit(BID_RESPONSE, MOCK.MNET_JPY_BID_RESPONSE); - events.emit(AUCTION_END, Object.assign({}, MOCK.AUCTION_END, {bidderRequests: [MOCK.MNET_BID_REQUESTED]})); + events.emit(AUCTION_END, Object.assign({}, MOCK.AUCTION_END, { bidderRequests: [MOCK.MNET_BID_REQUESTED] })); } describe('Media.net Analytics Adapter', function () { @@ -464,7 +464,7 @@ describe('Media.net Analytics Adapter', function () { }); it('should generate valid tracking URL for video bids', function () { - const VIDEO_AUCTION = Object.assign({}, MOCK.AUCTION_INIT, {adUnits: [INSTREAM_VIDEO_AD_UNIT]}); + const VIDEO_AUCTION = Object.assign({}, MOCK.AUCTION_INIT, { adUnits: [INSTREAM_VIDEO_AD_UNIT] }); const VIDEO_BID_REQUESTED = createBidRequest('medianet', '8e0d5245-deb3-406c-96ca-9b609e077ff7', '28248b0e6aece2', [INSTREAM_VIDEO_AD_UNIT]); const videoBidResponse = Object.assign({}, MOCK.MNET_BID_RESPONSE, { mediaType: 'video', @@ -511,7 +511,7 @@ describe('Media.net Analytics Adapter', function () { }); it('should return error tracker when bidrequest is missing', function () { - const VIDEO_AUCTION = Object.assign({}, MOCK.AUCTION_INIT, {adUnits: [INSTREAM_VIDEO_AD_UNIT]}); + const VIDEO_AUCTION = Object.assign({}, MOCK.AUCTION_INIT, { adUnits: [INSTREAM_VIDEO_AD_UNIT] }); const VIDEO_BID_REQUESTED = createBidRequest('medianet', '8e0d5245-deb3-406c-96ca-9b609e077ff7', '28248b0e6aece2', [INSTREAM_VIDEO_AD_UNIT]); const videoBidResponse = Object.assign({}, MOCK.MNET_BID_RESPONSE, { mediaType: 'video', @@ -632,7 +632,7 @@ describe('Media.net Analytics Adapter', function () { bidCopy.cpm = bidCopy.originalCpm * 0.8; // Simulate bidCpmAdjustment // Emit events to simulate an auction - events.emit(AUCTION_INIT, Object.assign({}, MOCK.AUCTION_INIT, {adUnits: MOCK.AD_UNITS})); + events.emit(AUCTION_INIT, Object.assign({}, MOCK.AUCTION_INIT, { adUnits: MOCK.AD_UNITS })); events.emit(BID_REQUESTED, MOCK.MNET_BID_REQUESTED); events.emit(BID_RESPONSE, bidCopy); events.emit(AUCTION_END, MOCK.AUCTION_END); @@ -738,7 +738,7 @@ describe('Media.net Analytics Adapter', function () { it('should set serverLatencyMillis and filtered pbsExt for S2S bids on AUCTION_END', function (done) { // enable analytics and start an S2S auction flow medianetAnalytics.clearlogsQueue(); - events.emit(AUCTION_INIT, Object.assign({}, MOCK.AUCTION_INIT, {adUnits: MOCK.AD_UNITS})); + events.emit(AUCTION_INIT, Object.assign({}, MOCK.AUCTION_INIT, { adUnits: MOCK.AD_UNITS })); events.emit(BID_REQUESTED, MOCK.MNET_S2S_BID_REQUESTED); events.emit(BID_RESPONSE, MOCK.MNET_S2S_BID_RESPONSE); @@ -776,7 +776,7 @@ describe('Media.net Analytics Adapter', function () { const auctionId = MOCK.MNET_S2S_BID_REQUESTED.auctionId; const bidId = MOCK.MNET_S2S_BID_REQUESTED.bids[0].bidId; - events.emit(AUCTION_INIT, Object.assign({}, MOCK.AUCTION_INIT, {adUnits: MOCK.AD_UNITS})); + events.emit(AUCTION_INIT, Object.assign({}, MOCK.AUCTION_INIT, { adUnits: MOCK.AD_UNITS })); events.emit(BID_REQUESTED, MOCK.MNET_S2S_BID_REQUESTED); // mark the bid as timed out so bidsReceived contains a non-success entry diff --git a/test/spec/modules/medianetBidAdapter_spec.js b/test/spec/modules/medianetBidAdapter_spec.js index c3c14f3d0bb..6368a1abfca 100644 --- a/test/spec/modules/medianetBidAdapter_spec.js +++ b/test/spec/modules/medianetBidAdapter_spec.js @@ -1,11 +1,12 @@ -import {expect, assert} from 'chai'; -import {spec, EVENTS} from '../../../modules/medianetBidAdapter.js'; -import {POST_ENDPOINT} from '../../../libraries/medianetUtils/constants.js'; +import { expect, assert } from 'chai'; +import { spec, EVENTS } from '../../../modules/medianetBidAdapter.js'; +import { POST_ENDPOINT } from '../../../libraries/medianetUtils/constants.js'; import { makeSlot } from '../integration/faker/googletag.js'; import { config } from '../../../src/config.js'; -import {server} from '../../mocks/xhr.js'; -import {resetWinDimensions} from '../../../src/utils.js'; -import {getGlobal} from '../../../src/prebidGlobal.js'; +import { server } from '../../mocks/xhr.js'; +import { resetWinDimensions } from '../../../src/utils.js'; +import { getGlobal } from '../../../src/prebidGlobal.js'; +import * as adUnits from 'src/utils/adUnits'; getGlobal().version = getGlobal().version || 'version'; const VALID_BID_REQUEST = [{ @@ -144,7 +145,7 @@ const VALID_BID_REQUEST_WITH_ORTB2 = [{ 'ortb2Imp': { 'ext': { tid: '277b631f-92f5-4844-8b19-ea13c095d3f1', - 'data': {'pbadslot': '/12345/my-gpt-tag-0'} + 'data': { 'pbadslot': '/12345/my-gpt-tag-0' } } }, 'auctionsCount': 1 @@ -173,7 +174,7 @@ const VALID_BID_REQUEST_WITH_ORTB2 = [{ 'ortb2Imp': { 'ext': { tid: 'c52a5c62-3c2b-4b90-9ff8-ec1487754822', - 'data': {'pbadslot': '/12345/my-gpt-tag-0'} + 'data': { 'pbadslot': '/12345/my-gpt-tag-0' } } }, 'auctionsCount': 1 @@ -1511,7 +1512,7 @@ const SERVER_RESPONSE_PAAPI = { 'callbackURL': 'https://test.com/paapi/v1/abcd' }, 'perBuyerSignals': { - 'https://buyer.test.media.net': [ 'test_buyer_signals' ] + 'https://buyer.test.media.net': ['test_buyer_signals'] }, 'perBuyerTimeouts': { '*': 200 @@ -1571,7 +1572,7 @@ const SERVER_RESPONSE_PAAPI_ORTB = { 'callbackURL': 'https://test.com/paapi/v1/abcd' }, 'perBuyerSignals': { - 'https://buyer.test.media.net': [ 'test_buyer_signals' ] + 'https://buyer.test.media.net': ['test_buyer_signals'] }, 'perBuyerTimeouts': { '*': 200 @@ -1979,17 +1980,13 @@ describe('Media.net bid adapter', function () { beforeEach(function () { getGlobal().medianetGlobals = {}; - const documentStub = sandbox.stub(document, 'getElementById'); const 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({ + sandbox.stub(adUnits, 'getAdUnitElement').returns({ getBoundingClientRect: () => boundingRect }); const windowSizeStub = sandbox.stub(spec, 'getWindowSize'); @@ -2066,19 +2063,6 @@ describe('Media.net bid adapter', function () { expect(JSON.parse(bidReq.data)).to.deep.equal(VALID_PAYLOAD_WITH_USERIDASEIDS); }); - it('should have valid payload when PAAPI is enabled', function () { - const bidReq = spec.buildRequests(VALID_BID_REQUEST_WITH_AE_IN_ORTB2IMP, {...VALID_AUCTIONDATA, paapi: {enabled: true}}); - expect(JSON.parse(bidReq.data)).to.deep.equal(VALID_PAYLOAD_PAAPI); - }); - - it('should send whatever is set in ortb2imp.ext.ae in all bid requests when PAAPI is enabled', function () { - const bidReq = spec.buildRequests(VALID_BID_REQUEST_WITH_AE_IN_ORTB2IMP, {...VALID_AUCTIONDATA, paapi: {enabled: true}}); - const data = JSON.parse(bidReq.data); - expect(data).to.deep.equal(VALID_PAYLOAD_PAAPI); - expect(data.imp[0].ext).to.have.property('ae'); - expect(data.imp[0].ext.ae).to.equal(1); - }); - describe('build requests: when page meta-data is available', () => { beforeEach(() => { spec.clearPageMeta(); @@ -2102,14 +2086,14 @@ describe('Media.net bid adapter', function () { }); describe('slot visibility', function () { - let documentStub; + let elementStub; beforeEach(function () { const windowSizeStub = sandbox.stub(spec, 'getWindowSize'); windowSizeStub.returns({ w: 1000, h: 1000 }); - documentStub = sandbox.stub(document, 'getElementById'); + elementStub = sandbox.stub(adUnits, 'getAdUnitElement'); }); it('slot visibility should be 2 and ratio 0 when ad unit is BTF', function () { const boundingRect = { @@ -2118,10 +2102,7 @@ describe('Media.net bid adapter', function () { bottom: 1050, right: 1050 }; - documentStub.withArgs('div-gpt-ad-1460505748561-123').returns({ - getBoundingClientRect: () => boundingRect - }); - documentStub.withArgs('div-gpt-ad-1460505748561-0').returns({ + elementStub.returns({ getBoundingClientRect: () => boundingRect }); @@ -2137,10 +2118,7 @@ describe('Media.net bid adapter', function () { bottom: 1050, right: 1050 }; - documentStub.withArgs('div-gpt-ad-1460505748561-123').returns({ - getBoundingClientRect: () => boundingRect - }); - documentStub.withArgs('div-gpt-ad-1460505748561-0').returns({ + elementStub.returns({ getBoundingClientRect: () => boundingRect }); const bidReq = spec.buildRequests(VALID_BID_REQUEST, VALID_AUCTIONDATA); @@ -2155,10 +2133,7 @@ describe('Media.net bid adapter', function () { bottom: 1050, right: 1050 }; - documentStub.withArgs('div-gpt-ad-1460505748561-123').returns({ - getBoundingClientRect: () => boundingRect - }); - documentStub.withArgs('div-gpt-ad-1460505748561-0').returns({ + elementStub.returns({ getBoundingClientRect: () => boundingRect }); const bidReq = spec.buildRequests(VALID_BID_REQUEST, VALID_AUCTIONDATA); @@ -2183,14 +2158,11 @@ describe('Media.net bid adapter', function () { bottom: 1050, right: 1050 }; - documentStub.withArgs(divId).returns({ + elementStub.returns({ getBoundingClientRect: () => boundingRect - }); - documentStub.withArgs('div-gpt-ad-1460505748561-123').returns({ - getBoundingClientRect: () => boundingRect - }); + }) - const bidRequest = [{...VALID_BID_REQUEST[0], adUnitCode: code}] + const bidRequest = [{ ...VALID_BID_REQUEST[0], adUnitCode: code }] const bidReq = spec.buildRequests(bidRequest, VALID_AUCTIONDATA); const data = JSON.parse(bidReq.data); expect(data.imp[0].ext.visibility).to.equal(2); @@ -2254,32 +2226,6 @@ describe('Media.net bid adapter', function () { const bids = spec.interpretResponse(SERVER_RESPONSE_EMPTY_BIDLIST, []); expect(bids).to.deep.equal(validBids); }); - - it('should return paapi if PAAPI response is received', function() { - const response = spec.interpretResponse(SERVER_RESPONSE_PAAPI, []); - expect(response).to.have.property('bids'); - expect(response).to.have.property('paapi'); - expect(response.paapi[0]).to.deep.equal(SERVER_RESPONSE_PAAPI.body.ext.paApiAuctionConfigs[0]); - }); - - it('should return paapi if openRTB PAAPI response received', function () { - const response = spec.interpretResponse(SERVER_RESPONSE_PAAPI_ORTB, []); - expect(response).to.have.property('bids'); - expect(response).to.have.property('paapi'); - expect(response.paapi[0]).to.deep.equal(SERVER_RESPONSE_PAAPI_ORTB.body.ext.igi[0].igs[0]) - }); - - it('should have the correlation between paapi[0].bidId and bidreq.imp[0].id', function() { - const bidReq = spec.buildRequests(VALID_BID_REQUEST_WITH_AE_IN_ORTB2IMP, {...VALID_AUCTIONDATA, paapi: {enabled: true}}); - const bidRes = spec.interpretResponse(SERVER_RESPONSE_PAAPI, []); - expect(bidRes.paapi[0].bidId).to.equal(JSON.parse(bidReq.data).imp[0].id) - }); - - it('should have the correlation between paapi[0].bidId and bidreq.imp[0].id for openRTB response', function() { - const bidReq = spec.buildRequests(VALID_BID_REQUEST_WITH_AE_IN_ORTB2IMP, {...VALID_AUCTIONDATA, paapi: {enabled: true}}); - const bidRes = spec.interpretResponse(SERVER_RESPONSE_PAAPI_ORTB, []); - expect(bidRes.paapi[0].bidId).to.equal(JSON.parse(bidReq.data).imp[0].id) - }); }); describe('onTimeout', function () { @@ -2366,7 +2312,7 @@ describe('Media.net bid adapter', function () { }); it('should send bidderError data correctly', function () { const error = { - reason: {message: 'Failed to fetch', status: 500}, + reason: { message: 'Failed to fetch', status: 500 }, timedOut: true, status: 0 } @@ -2380,7 +2326,7 @@ describe('Media.net bid adapter', function () { }]; sandbox.stub(window.navigator, 'sendBeacon').returns(false); - spec.onBidderError({error, bidderRequest: {bids}}); + spec.onBidderError({ error, bidderRequest: { bids } }); const reqBody = new URLSearchParams(server.requests[0].requestBody); assert.equal(server.requests[0].method, 'POST'); diff --git a/test/spec/modules/medianetRtdProvider_spec.js b/test/spec/modules/medianetRtdProvider_spec.js index ffd8109ebf0..90a3c11aa07 100644 --- a/test/spec/modules/medianetRtdProvider_spec.js +++ b/test/spec/modules/medianetRtdProvider_spec.js @@ -51,7 +51,7 @@ describe('medianet realtime module', function () { }); it('auctionInit should pass information to js when loaded', function () { - const auctionObject = {adUnits: []}; + const auctionObject = { adUnits: [] }; medianetRTD.medianetRtdModule.onAuctionInitEvent(auctionObject); const command = window.mnjs.que.pop(); @@ -60,7 +60,7 @@ describe('medianet realtime module', function () { assert.equal(setDataSpy.called, true); assert.equal(setDataSpy.args[0][0].name, 'auctionInit'); - assert.deepEqual(setDataSpy.args[0][0].data, {auction: auctionObject}); + assert.deepEqual(setDataSpy.args[0][0].data, { auction: auctionObject }); }); describe('getTargeting should work correctly', function () { @@ -72,8 +72,8 @@ describe('medianet realtime module', function () { it('should return ad unit codes when ad units are present', function () { const adUnitCodes = ['code1', 'code2']; assert.deepEqual(medianetRTD.medianetRtdModule.getTargetingData(adUnitCodes, {}, {}, {}), { - code1: {'mnadc': 'code1'}, - code2: {'mnadc': 'code2'}, + code1: { 'mnadc': 'code1' }, + code2: { 'mnadc': 'code2' }, }); }); @@ -120,7 +120,7 @@ describe('medianet realtime module', function () { const onCompleteSpy = sandbox.spy(); window.mnjs.onPrebidRequestBid = onPrebidRequestBidSpy = () => { onPrebidRequestBidSpy.called = true; - return {onComplete: onCompleteSpy}; + return { onComplete: onCompleteSpy }; }; medianetRTD.medianetRtdModule.getBidRequestData(requestBidsProps, callbackSpy, conf.dataProviders[0], {}); @@ -136,7 +136,7 @@ describe('medianet realtime module', function () { onCompleteSpy.args[0][0](); assert.equal(callbackSpy.callCount, 1, 'callback should be called when error callback is triggered'); onCompleteSpy.args[0][1]({}, { - 'code1': {ext: {refresh: refreshInformation}} + 'code1': { ext: { refresh: refreshInformation } } }); assert.equal(callbackSpy.callCount, 2, 'callback should be called when success callback is triggered'); assert.isObject(requestBidsProps.adUnits[0].ortb2Imp, 'ORTB object should be set'); diff --git a/test/spec/modules/mediasquareBidAdapter_spec.js b/test/spec/modules/mediasquareBidAdapter_spec.js index caa22f4da1d..3cba1e4bdb7 100644 --- a/test/spec/modules/mediasquareBidAdapter_spec.js +++ b/test/spec/modules/mediasquareBidAdapter_spec.js @@ -1,5 +1,5 @@ -import {expect} from 'chai'; -import {spec} from 'modules/mediasquareBidAdapter.js'; +import { expect } from 'chai'; +import { spec } from 'modules/mediasquareBidAdapter.js'; import { server } from 'test/mocks/xhr.js'; describe('MediaSquare bid adapter tests', function () { @@ -82,37 +82,39 @@ describe('MediaSquare bid adapter tests', function () { sizes: [[300, 250]], getFloor: function (a) { return { currency: 'USD', floor: 1.0 }; }, }]; - var BID_RESPONSE = {'body': { - 'responses': [{ - 'transaction_id': 'cccc1234', - 'cpm': 22.256608, - 'width': 300, - 'height': 250, - 'creative_id': '158534630', - 'currency': 'USD', - 'originalCpm': 25.0123, - 'originalCurrency': 'USD', - 'net_revenue': true, - 'ttl': 300, - 'ad': '< --- creative code --- >', - 'bidder': 'msqClassic', - 'code': 'test/publishername_atf_desktop_rg_pave', - 'bid_id': 'aaaa1234', - 'adomain': ['test.com'], - 'context': 'instream', - 'increment': 1.0, - 'ova': 'cleared', - 'dsa': { - 'behalf': 'some-behalf', - 'paid': 'some-paid', - 'transparency': [{ - 'domain': 'test.com', - 'dsaparams': [1, 2, 3] - }], - 'adrender': 1 - } - }], - }}; + var BID_RESPONSE = { + 'body': { + 'responses': [{ + 'transaction_id': 'cccc1234', + 'cpm': 22.256608, + 'width': 300, + 'height': 250, + 'creative_id': '158534630', + 'currency': 'USD', + 'originalCpm': 25.0123, + 'originalCurrency': 'USD', + 'net_revenue': true, + 'ttl': 300, + 'ad': '< --- creative code --- >', + 'bidder': 'msqClassic', + 'code': 'test/publishername_atf_desktop_rg_pave', + 'bid_id': 'aaaa1234', + 'adomain': ['test.com'], + 'context': 'instream', + 'increment': 1.0, + 'ova': 'cleared', + 'dsa': { + 'behalf': 'some-behalf', + 'paid': 'some-paid', + 'transparency': [{ + 'domain': 'test.com', + 'dsaparams': [1, 2, 3] + }], + 'adrender': 1 + } + }], + } + }; const DEFAULT_OPTIONS = { ortb2: { @@ -260,7 +262,7 @@ describe('MediaSquare bid adapter tests', function () { expect(syncs).to.have.lengthOf(0); }); it('Verifies user sync with cookies in bid response', function () { - BID_RESPONSE.body.cookies = [{'type': 'image', 'url': 'http://www.cookie.sync.org/'}]; + BID_RESPONSE.body.cookies = [{ 'type': 'image', 'url': 'http://www.cookie.sync.org/' }]; var syncs = spec.getUserSyncs({}, [BID_RESPONSE], DEFAULT_OPTIONS.gdprConsent); expect(syncs).to.have.lengthOf(1); expect(syncs[0]).to.have.property('type').and.to.equal('image'); @@ -278,7 +280,7 @@ describe('MediaSquare bid adapter tests', function () { }); it('Verifies native in bid response', function () { const request = spec.buildRequests(NATIVE_PARAMS, DEFAULT_OPTIONS); - BID_RESPONSE.body.responses[0].native = {'title': 'native title'}; + BID_RESPONSE.body.responses[0].native = { 'title': 'native title' }; const response = spec.interpretResponse(BID_RESPONSE, request); expect(response).to.have.lengthOf(1); const bid = response[0]; @@ -287,7 +289,7 @@ describe('MediaSquare bid adapter tests', function () { }); it('Verifies video in bid response', function () { const request = spec.buildRequests(VIDEO_PARAMS, DEFAULT_OPTIONS); - BID_RESPONSE.body.responses[0].video = {'xml': 'my vast XML', 'url': 'my vast url'}; + BID_RESPONSE.body.responses[0].video = { 'xml': 'my vast XML', 'url': 'my vast url' }; const response = spec.interpretResponse(BID_RESPONSE, request); expect(response).to.have.lengthOf(1); const bid = response[0]; @@ -296,4 +298,25 @@ describe('MediaSquare bid adapter tests', function () { expect(bid).to.have.property('renderer'); delete BID_RESPONSE.body.responses[0].video; }); + it('Verifies burls in bid response', function () { + const request = spec.buildRequests(DEFAULT_PARAMS, DEFAULT_OPTIONS); + BID_RESPONSE.body.responses[0].burls = [{ 'url': 'http://myburl.com/track?bid=1.0' }]; + const response = spec.interpretResponse(BID_RESPONSE, request); + expect(response).to.have.lengthOf(1); + const bid = response[0]; + expect(bid.mediasquare).to.have.property('burls'); + expect(bid.mediasquare.burls).to.have.lengthOf(1); + expect(bid.mediasquare.burls[0]).to.have.property('url').and.to.equal('http://myburl.com/track?bid=1.0'); + delete BID_RESPONSE.body.responses[0].burls; + }); + it('Verifies burls bidwon', function () { + const request = spec.buildRequests(DEFAULT_PARAMS, DEFAULT_OPTIONS); + BID_RESPONSE.body.responses[0].burls = [{ 'url': 'http://myburl.com/track?bid=1.0' }]; + const response = spec.interpretResponse(BID_RESPONSE, request); + const won = spec.onBidWon(response[0]); + expect(won).to.equal(true); + expect(server.requests.length).to.equal(1); + expect(server.requests[0].url).to.equal('http://myburl.com/track?bid=1.0'); + delete BID_RESPONSE.body.responses[0].burls; + }); }); diff --git a/test/spec/modules/merkleIdSystem_spec.js b/test/spec/modules/merkleIdSystem_spec.js index 0999cacc8e4..cf307b76117 100644 --- a/test/spec/modules/merkleIdSystem_spec.js +++ b/test/spec/modules/merkleIdSystem_spec.js @@ -1,10 +1,10 @@ import * as ajaxLib from 'src/ajax.js'; import * as utils from 'src/utils.js'; -import {merkleIdSubmodule} from 'modules/merkleIdSystem.js'; +import { merkleIdSubmodule } from 'modules/merkleIdSystem.js'; import sinon from 'sinon'; -import {createEidsArray} from '../../../modules/userId/eids.js'; -import {attachIdSystem} from '../../../modules/userId/index.js'; +import { createEidsArray } from '../../../modules/userId/eids.js'; +import { attachIdSystem } from '../../../modules/userId/index.js'; const expect = require('chai').expect; @@ -62,10 +62,10 @@ describe('Merkle System', function () { }); it('can decode legacy stored object', function() { - const merkleId = {'pam_id': {'id': 'testmerkleId', 'keyID': 1}}; + const merkleId = { 'pam_id': { 'id': 'testmerkleId', 'keyID': 1 } }; expect(merkleIdSubmodule.decode(merkleId)).to.deep.equal({ - merkleId: {'id': 'testmerkleId', 'keyID': 1} + merkleId: { 'id': 'testmerkleId', 'keyID': 1 } }); }) @@ -159,7 +159,7 @@ describe('Merkle System', function () { storage: STORAGE_PARAMS }; - const submoduleCallback = merkleIdSubmodule.getId(config, {gdpr: {gdprApplies: true}}); + const submoduleCallback = merkleIdSubmodule.getId(config, { gdpr: { gdprApplies: true } }); expect(submoduleCallback).to.be.undefined; expect(utils.logError.args[0][0]).to.exist.and.to.equal('User ID - merkleId submodule does not currently handle consent strings'); }); @@ -208,7 +208,7 @@ describe('Merkle System', function () { }; const yesterday = new Date(Date.now() - 86400000).toUTCString(); - const storedId = {value: 'Merkle_Stored_ID', date: yesterday}; + const storedId = { value: 'Merkle_Stored_ID', date: yesterday }; const id = merkleIdSubmodule.extendId(config, undefined, storedId); @@ -225,7 +225,7 @@ describe('Merkle System', function () { }; const yesterday = new Date(Date.now() - 86400000).toUTCString(); - const storedId = {value: 'Merkle_Stored_ID', date: yesterday}; + const storedId = { value: 'Merkle_Stored_ID', date: yesterday }; const submoduleCallback = merkleIdSubmodule.extendId(config, undefined, storedId).callback; @@ -243,7 +243,7 @@ describe('Merkle System', function () { }; const yesterday = new Date(Date.now() - 86400000).toUTCString(); - const storedId = {value: 'Merkle_Stored_ID', date: yesterday}; + const storedId = { value: 'Merkle_Stored_ID', date: yesterday }; const submoduleCallback = merkleIdSubmodule.extendId(config, undefined, storedId).callback; submoduleCallback(callbackSpy); @@ -293,7 +293,8 @@ describe('Merkle System', function () { expect(newEids.length).to.equal(2); expect(newEids[0]).to.deep.equal({ source: 'ssp1.merkleinc.com', - uids: [{id: 'some-random-id-value', + uids: [{ + id: 'some-random-id-value', atype: 3, ext: { enc: 1, @@ -305,7 +306,8 @@ describe('Merkle System', function () { }); expect(newEids[1]).to.deep.equal({ source: 'ssp2.merkleinc.com', - uids: [{id: 'another-random-id-value', + uids: [{ + id: 'another-random-id-value', atype: 3, ext: { third: 4, diff --git a/test/spec/modules/mgidBidAdapter_spec.js b/test/spec/modules/mgidBidAdapter_spec.js index 3019cebe377..d269a99d74d 100644 --- a/test/spec/modules/mgidBidAdapter_spec.js +++ b/test/spec/modules/mgidBidAdapter_spec.js @@ -1,10 +1,9 @@ -import {expect} from 'chai'; +import { expect } from 'chai'; import { spec, storage } from 'modules/mgidBidAdapter.js'; import { version } from 'package.json'; import * as utils from '../../../src/utils.js'; -import { getDNT } from 'libraries/dnt/index.js'; -import {USERSYNC_DEFAULT_CONFIG} from '../../../src/userSync.js'; -import {config} from '../../../src/config.js'; +import { USERSYNC_DEFAULT_CONFIG } from '../../../src/userSync.js'; +import { config } from '../../../src/config.js'; describe('Mgid bid adapter', function () { let sandbox; @@ -23,7 +22,7 @@ describe('Mgid bid adapter', function () { }); const screenHeight = screen.height; const screenWidth = screen.width; - const dnt = getDNT() ? 1 : 0; + const dnt = 0; // DNT deprecated by W3C; Prebid no longer supports DNT const language = navigator.language ? 'language' : 'userLanguage'; let lang = navigator[language].split('-')[0]; if (lang.length !== 2 && lang.length !== 3) { @@ -62,7 +61,7 @@ describe('Mgid bid adapter', function () { it('should return false when valid params are not passed', function () { const bid = Object.assign({}, sbid); delete bid.params; - bid.params = {accountId: '', placementId: ''}; + bid.params = { accountId: '', placementId: '' }; expect(spec.isBidRequestValid(bid)).to.equal(false); }); @@ -75,7 +74,7 @@ describe('Mgid bid adapter', function () { sizes: [[300, 250]] } }; - bid.params = {accountId: 2, placementId: 1}; + bid.params = { accountId: 2, placementId: 1 }; expect(spec.isBidRequestValid(bid)).to.equal(false); }); @@ -88,7 +87,7 @@ describe('Mgid bid adapter', function () { sizes: [[300, 250]] } }; - bid.params = {accountId: 2, placementId: 1}; + bid.params = { accountId: 2, placementId: 1 }; expect(spec.isBidRequestValid(bid)).to.equal(false); }); @@ -101,7 +100,7 @@ describe('Mgid bid adapter', function () { sizes: [[300, 250]] } }; - bid.params = {accountId: 2, placementId: 1}; + bid.params = { accountId: 2, placementId: 1 }; expect(spec.isBidRequestValid(bid)).to.equal(true); }); @@ -113,21 +112,21 @@ describe('Mgid bid adapter', function () { sizes: [[300, 250]] } }; - bid.params = {accountId: '0', placementId: '00'}; + bid.params = { accountId: '0', placementId: '00' }; expect(spec.isBidRequestValid(bid)).to.equal(false); }); it('should return false when valid mediaTypes are not passed', function () { const bid = Object.assign({}, sbid); delete bid.params; - bid.params = {accountId: '1', placementId: '1'}; + 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 () { const bid = Object.assign({}, sbid); delete bid.params; - bid.params = {accountId: '1', placementId: '1'}; + bid.params = { accountId: '1', placementId: '1' }; bid.mediaTypes = { }; expect(spec.isBidRequestValid(bid)).to.equal(false); @@ -136,7 +135,7 @@ describe('Mgid bid adapter', function () { it('should return false when valid mediaTypes.banner.sizes are not passed', function () { const bid = Object.assign({}, sbid); delete bid.params; - bid.params = {accountId: '1', placementId: '1'}; + bid.params = { accountId: '1', placementId: '1' }; bid.mediaTypes = { sizes: [] }; @@ -146,7 +145,7 @@ describe('Mgid bid adapter', function () { it('should return false when valid mediaTypes.banner.sizes are not valid', function () { const bid = Object.assign({}, sbid); delete bid.params; - bid.params = {accountId: '1', placementId: '1'}; + bid.params = { accountId: '1', placementId: '1' }; bid.mediaTypes = { sizes: [300, 250] }; @@ -157,7 +156,7 @@ describe('Mgid bid adapter', function () { const bid = Object.assign({}, sbid); delete bid.params; bid.adUnitCode = 'div'; - bid.params = {accountId: '1', placementId: '1'}; + bid.params = { accountId: '1', placementId: '1' }; bid.mediaTypes = { banner: { sizes: [[300, 250]] @@ -168,7 +167,7 @@ describe('Mgid bid adapter', function () { it('should return false when valid mediaTypes.native is not object', function () { const bid = Object.assign({}, sbid); - bid.params = {accountId: '1', placementId: '1'}; + bid.params = { accountId: '1', placementId: '1' }; bid.mediaTypes = { native: [] }; @@ -178,7 +177,7 @@ describe('Mgid bid adapter', function () { it('should return false when mediaTypes.native is empty object', function () { const bid = Object.assign({}, sbid); delete bid.params; - bid.params = {accountId: '1', placementId: '1'}; + bid.params = { accountId: '1', placementId: '1' }; bid.mediaTypes = { native: {} }; @@ -188,7 +187,7 @@ describe('Mgid bid adapter', function () { it('should return false when mediaTypes.native is invalid object', function () { const bid = Object.assign({}, sbid); delete bid.params; - bid.params = {accountId: '1', placementId: '1'}; + bid.params = { accountId: '1', placementId: '1' }; bid.mediaTypes = { native: { image: { @@ -201,19 +200,19 @@ describe('Mgid bid adapter', function () { it('should return false when mediaTypes.native has unsupported required asset', function () { const bid = Object.assign({}, sbid); - bid.params = {accountId: '2', placementId: '1'}; + bid.params = { accountId: '2', placementId: '1' }; bid.mediaTypes = { native: { - title: {required: true}, - image: {required: false, sizes: [80, 80]}, - sponsored: {required: false}, + 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}, + title: { required: true }, + image: { required: false, sizes: [80, 80] }, + sponsored: { required: false }, + unsupported: { required: true }, }; expect(spec.isBidRequestValid(bid)).to.equal(false); }); @@ -221,18 +220,18 @@ describe('Mgid bid adapter', function () { it('should return true when mediaTypes.native all assets needed', function () { const bid = Object.assign({}, sbid); bid.adUnitCode = 'div'; - bid.params = {accountId: '2', placementId: '1'}; + bid.params = { accountId: '2', placementId: '1' }; bid.mediaTypes = { native: { - title: {required: true}, - image: {required: false, sizes: [80, 80]}, - sponsored: {required: false}, + 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}, + title: { required: true }, + image: { required: false, sizes: [80, 80] }, + sponsored: { required: false }, }; expect(spec.isBidRequestValid(bid)).to.equal(true); }); @@ -334,7 +333,7 @@ describe('Mgid bid adapter', function () { }, }; afterEach(function () { - config.setConfig({coppa: undefined}) + config.setConfig({ coppa: undefined }) }) it('should return undefined if no validBidRequests passed', function () { @@ -357,7 +356,7 @@ describe('Mgid bid adapter', function () { getDataFromLocalStorageStub.restore(); }); it('should proper handle gdpr', function () { - config.setConfig({coppa: 1}) + config.setConfig({ coppa: 1 }) const bid = Object.assign({}, abid); bid.mediaTypes = { banner: { @@ -365,12 +364,12 @@ describe('Mgid bid adapter', function () { } }; const bidRequests = [bid]; - const request = spec.buildRequests(bidRequests, {gdprConsent: {consentString: 'gdpr', gdprApplies: true}, uspConsent: 'usp', gppConsent: {gppString: 'gpp'}}); + const request = spec.buildRequests(bidRequests, { gdprConsent: { consentString: 'gdpr', gdprApplies: true }, uspConsent: 'usp', gppConsent: { gppString: 'gpp' } }); 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, us_privacy: 'usp'}, gpp: 'gpp', coppa: 1}); + expect(data.user).deep.equal({ ext: { consent: 'gdpr' } }); + expect(data.regs).deep.equal({ ext: { gdpr: 1, us_privacy: 'usp' }, gpp: 'gpp', coppa: 1 }); }); it('should handle refererInfo', function () { const bid = Object.assign({}, abid); @@ -383,7 +382,7 @@ describe('Mgid bid adapter', function () { const domain = 'site.com' const page = `http://${domain}/site.html` const ref = 'http://ref.com/ref.html' - const request = spec.buildRequests(bidRequests, {refererInfo: {page, ref}}); + const request = spec.buildRequests(bidRequests, { refererInfo: { page, ref } }); expect(request.url).deep.equal('https://prebid.mgid.com/prebid/1'); expect(request.method).deep.equal('POST'); const data = JSON.parse(request.data); @@ -405,7 +404,7 @@ describe('Mgid bid adapter', function () { const bidRequests = [bid]; const request = spec.buildRequests(bidRequests); const data = JSON.parse(request.data); - expect(data.source).to.deep.equal({ext: {schain: bid.ortb2.source.ext.schain}}); + expect(data.source).to.deep.equal({ ext: { schain: bid.ortb2.source.ext.schain } }); }); it('should handle userId', function () { const bid = Object.assign({}, abid); @@ -415,7 +414,7 @@ describe('Mgid bid adapter', function () { } }; const bidRequests = [bid]; - const bidderRequest = {userId: 'userid'}; + const bidderRequest = { userId: 'userid' }; const request = spec.buildRequests(bidRequests, bidderRequest); expect(request.url).deep.equal('https://prebid.mgid.com/prebid/1'); expect(request.method).deep.equal('POST'); @@ -459,7 +458,7 @@ describe('Mgid bid adapter', function () { expect(data.device.language).to.deep.equal(lang); expect(data.imp[0].tagid).to.deep.equal('2/div'); expect(data.imp[0].ext.gpid).to.deep.equal('/1111/gpid'); - expect(data.imp[0].banner).to.deep.equal({w: 300, h: 250}); + 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', @@ -473,8 +472,8 @@ describe('Mgid bid adapter', function () { native: '', }; bid.nativeParams = { - title: {required: true}, - image: {sizes: [80, 80]}, + title: { required: true }, + image: { sizes: [80, 80] }, }; const bidRequests = [bid]; const request = spec.buildRequests(bidRequests); @@ -486,8 +485,8 @@ describe('Mgid bid adapter', function () { native: '', }; bid.nativeParams = { - title: {required: true}, - image: {sizes: [80, 80]}, + title: { required: true }, + image: { sizes: [80, 80] }, sponsored: { }, }; @@ -509,7 +508,7 @@ describe('Mgid bid adapter', function () { expect(data.device.language).to.deep.equal(lang); expect(data.imp[0].tagid).to.deep.equal('2/div'); expect(data.imp[0].ext.gpid).to.deep.equal('/1111/gpid'); - 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].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', @@ -523,8 +522,8 @@ describe('Mgid bid adapter', function () { native: '', }; bid.nativeParams = { - title: {required: true}, - image: {wmin: 50, hmin: 50, required: true}, + title: { required: true }, + image: { wmin: 50, hmin: 50, required: true }, icon: {}, sponsored: { }, }; @@ -546,7 +545,7 @@ describe('Mgid bid adapter', function () { 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].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', @@ -560,8 +559,8 @@ describe('Mgid bid adapter', function () { native: '', }; bid.nativeParams = { - title: {required: true}, - image: {sizes: [80, 80]}, + title: { required: true }, + image: { sizes: [80, 80] }, sponsoredBy: { }, }; @@ -582,7 +581,7 @@ describe('Mgid bid adapter', function () { 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].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', @@ -615,7 +614,7 @@ describe('Mgid bid adapter', function () { 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}], pos: 1}); + expect(data.imp[0].banner).to.deep.equal({ w: 300, h: 600, format: [{ w: 300, h: 600 }, { w: 300, h: 250 }], pos: 1 }); expect(data.imp[0].secure).to.deep.equal(secure); expect(request).to.deep.equal({ @@ -650,8 +649,8 @@ describe('Mgid bid adapter', function () { segtax: 1, }, segment: [ - {id: '123'}, - {id: '456'}, + { id: '123' }, + { id: '456' }, ], }] } @@ -666,8 +665,8 @@ describe('Mgid bid adapter', function () { segtax: 2, }, segment: [ - {'id': '789'}, - {'id': '987'}, + { 'id': '789' }, + { 'id': '987' }, ], }] }, @@ -696,21 +695,21 @@ describe('Mgid bid adapter', function () { describe('interpretResponse', function () { it('should not push proper native bid response if adm is missing', function () { const 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': 'https nurl', 'burl': 'https burl', 'cid': '44082', 'crid': '2898532/2419121/2592854/2499195', 'cat': ['IAB7', 'IAB14', 'IAB18-3', 'IAB1-2'], 'ext': {'place': 0, 'crtype': 'native'}, 'adomain': ['test.com']}], 'seat': '44082'}]} + 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': 'https nurl', 'burl': 'https burl', 'cid': '44082', 'crid': '2898532/2419121/2592854/2499195', 'cat': ['IAB7', 'IAB14', 'IAB18-3', 'IAB1-2'], 'ext': { 'place': 0, 'crtype': 'native' }, 'adomain': ['test.com'] }], 'seat': '44082' }] } }; const bids = spec.interpretResponse(resp); expect(bids).to.deep.equal([]) }); it('should not push proper native bid response if assets is empty', function () { const 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': 'https nurl', 'burl': 'https 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'}, 'adomain': ['test.com']}], 'seat': '44082'}]} + 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': 'https nurl', 'burl': 'https 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' }, 'adomain': ['test.com'] }], 'seat': '44082' }] } }; const bids = spec.interpretResponse(resp); expect(bids).to.deep.equal([]) }); it('should push proper native bid response, assets1', function () { const 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': 'https nurl', 'burl': 'https 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'}, 'adomain': ['test.com']}], 'seat': '44082'}], ext: {'muidn': 'userid'}} + 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': 'https nurl', 'burl': 'https 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' }, 'adomain': ['test.com'] }], 'seat': '44082' }], ext: { 'muidn': 'userid' } } }; const bids = spec.interpretResponse(resp); expect(bids).to.deep.equal([{ @@ -723,7 +722,7 @@ describe('Mgid bid adapter', function () { 'height': 0, 'isBurl': true, 'mediaType': 'native', - 'meta': {'advertiserDomains': ['test.com']}, + 'meta': { 'advertiserDomains': ['test.com'] }, 'native': { 'clickTrackers': [], 'clickUrl': 'link_url', @@ -754,7 +753,7 @@ describe('Mgid bid adapter', function () { }); it('should push proper native bid response, assets2', function () { const 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': 'https nurl', 'burl': 'https 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'}, 'adomain': ['test.com']}], 'seat': '44082'}]} + 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': 'https nurl', 'burl': 'https 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' }, 'adomain': ['test.com'] }], 'seat': '44082' }] } }; const bids = spec.interpretResponse(resp); expect(bids).to.deep.equal([ @@ -767,7 +766,7 @@ describe('Mgid bid adapter', function () { 'height': 0, 'isBurl': true, 'mediaType': 'native', - 'meta': {'advertiserDomains': ['test.com']}, + 'meta': { 'advertiserDomains': ['test.com'] }, 'netRevenue': true, 'nurl': 'https nurl', 'burl': 'https burl', @@ -801,7 +800,7 @@ describe('Mgid bid adapter', function () { }); it('should push proper banner bid response', function () { const 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': 'https nurl', 'burl': 'https burl', 'adm': 'html: adm', 'cid': '44082', 'crid': '2898532/2419121/2592854/2499195', 'cat': ['IAB7', 'IAB14', 'IAB18-3', 'IAB1-2'], 'adomain': ['test.com']}], 'seat': '44082'}]} + 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': 'https nurl', 'burl': 'https burl', 'adm': 'html: adm', 'cid': '44082', 'crid': '2898532/2419121/2592854/2499195', 'cat': ['IAB7', 'IAB14', 'IAB18-3', 'IAB1-2'], 'adomain': ['test.com'] }], 'seat': '44082' }] } }; const bids = spec.interpretResponse(resp); expect(bids).to.deep.equal([ @@ -814,7 +813,7 @@ describe('Mgid bid adapter', function () { 'height': 600, 'isBurl': true, 'mediaType': 'banner', - 'meta': {'advertiserDomains': ['test.com']}, + 'meta': { 'advertiserDomains': ['test.com'] }, 'netRevenue': true, 'nurl': 'https nurl', 'burl': 'https burl', @@ -828,32 +827,32 @@ describe('Mgid bid adapter', function () { describe('getUserSyncs', function () { afterEach(function() { - config.setConfig({userSync: {syncsPerBidder: USERSYNC_DEFAULT_CONFIG.syncsPerBidder}}); + config.setConfig({ userSync: { syncsPerBidder: USERSYNC_DEFAULT_CONFIG.syncsPerBidder } }); }); it('should do nothing on getUserSyncs without inputs', function () { expect(spec.getUserSyncs()).to.equal(undefined) }); it('should return frame object with empty consents', function () { - const sync = spec.getUserSyncs({iframeEnabled: true}) + const sync = spec.getUserSyncs({ iframeEnabled: true }) expect(sync).to.have.length(1) expect(sync[0]).to.have.property('type', 'iframe') expect(sync[0]).to.have.property('url').match(/https:\/\/cm\.mgid\.com\/i\.html\?cbuster=\d+&gdpr_consent=&gdpr=0/) }); it('should return frame object with gdpr consent', function () { - const sync = spec.getUserSyncs({iframeEnabled: true}, undefined, {consentString: 'consent', gdprApplies: true}) + const sync = spec.getUserSyncs({ iframeEnabled: true }, undefined, { consentString: 'consent', gdprApplies: true }) expect(sync).to.have.length(1) expect(sync[0]).to.have.property('type', 'iframe') expect(sync[0]).to.have.property('url').match(/https:\/\/cm\.mgid\.com\/i\.html\?cbuster=\d+&gdpr_consent=consent&gdpr=1/) }); it('should return frame object with gdpr + usp', function () { - const sync = spec.getUserSyncs({iframeEnabled: true}, undefined, {consentString: 'consent1', gdprApplies: true}, {'consentString': 'consent2'}) + const sync = spec.getUserSyncs({ iframeEnabled: true }, undefined, { consentString: 'consent1', gdprApplies: true }, { 'consentString': 'consent2' }) expect(sync).to.have.length(1) expect(sync[0]).to.have.property('type', 'iframe') expect(sync[0]).to.have.property('url').match(/https:\/\/cm\.mgid\.com\/i\.html\?cbuster=\d+&gdpr_consent=consent1&gdpr=1&us_privacy=consent2/) }); it('should return img object with gdpr + usp', function () { - config.setConfig({userSync: {syncsPerBidder: undefined}}); - const sync = spec.getUserSyncs({pixelEnabled: true}, undefined, {consentString: 'consent1', gdprApplies: true}, {'consentString': 'consent2'}) + config.setConfig({ userSync: { syncsPerBidder: undefined } }); + const sync = spec.getUserSyncs({ pixelEnabled: true }, undefined, { consentString: 'consent1', gdprApplies: true }, { 'consentString': 'consent2' }) expect(sync).to.have.length(USERSYNC_DEFAULT_CONFIG.syncsPerBidder) for (let i = 0; i < USERSYNC_DEFAULT_CONFIG.syncsPerBidder; i++) { expect(sync[i]).to.have.property('type', 'image') @@ -861,14 +860,14 @@ describe('Mgid bid adapter', function () { } }); it('should return frame object with gdpr + usp', function () { - const sync = spec.getUserSyncs({iframeEnabled: true, pixelEnabled: true}, undefined, {consentString: 'consent1', gdprApplies: true}, {'consentString': 'consent2'}) + const sync = spec.getUserSyncs({ iframeEnabled: true, pixelEnabled: true }, undefined, { consentString: 'consent1', gdprApplies: true }, { 'consentString': 'consent2' }) expect(sync).to.have.length(1) expect(sync[0]).to.have.property('type', 'iframe') expect(sync[0]).to.have.property('url').match(/https:\/\/cm\.mgid\.com\/i\.html\?cbuster=\d+&gdpr_consent=consent1&gdpr=1&us_privacy=consent2/) }); it('should return img (pixels) objects with gdpr + usp', function () { - const response = [{body: {ext: {cm: ['http://cm.mgid.com/i.gif?cdsp=1111', 'http://cm.mgid.com/i.gif']}}}] - const sync = spec.getUserSyncs({iframeEnabled: false, pixelEnabled: true}, response, {consentString: 'consent1', gdprApplies: true}, {'consentString': 'consent2'}) + const response = [{ body: { ext: { cm: ['http://cm.mgid.com/i.gif?cdsp=1111', 'http://cm.mgid.com/i.gif'] } } }] + const sync = spec.getUserSyncs({ iframeEnabled: false, pixelEnabled: true }, response, { consentString: 'consent1', gdprApplies: true }, { 'consentString': 'consent2' }) expect(sync).to.have.length(2) expect(sync[0]).to.have.property('type', 'image') expect(sync[0]).to.have.property('url').match(/http:\/\/cm\.mgid\.com\/i\.gif\?cdsp=1111&cbuster=\d+&gdpr_consent=consent1&gdpr=1&us_privacy=consent2/) @@ -879,12 +878,12 @@ describe('Mgid bid adapter', function () { describe('getUserSyncs with img from ext.cm and gdpr + usp + coppa + gpp', function () { afterEach(function() { - config.setConfig({coppa: undefined}) + config.setConfig({ coppa: undefined }) }); it('should return img (pixels) objects with gdpr + usp + coppa + gpp', function () { - config.setConfig({coppa: 1}); - const response = [{body: {ext: {cm: ['http://cm.mgid.com/i.gif?cdsp=1111', 'http://cm.mgid.com/i.gif']}}}] - const sync = spec.getUserSyncs({iframeEnabled: false, pixelEnabled: true}, response, {consentString: 'consent1', gdprApplies: true}, {'consentString': 'consent2'}, {gppString: 'gpp'}) + config.setConfig({ coppa: 1 }); + const response = [{ body: { ext: { cm: ['http://cm.mgid.com/i.gif?cdsp=1111', 'http://cm.mgid.com/i.gif'] } } }] + const sync = spec.getUserSyncs({ iframeEnabled: false, pixelEnabled: true }, response, { consentString: 'consent1', gdprApplies: true }, { 'consentString': 'consent2' }, { gppString: 'gpp' }) expect(sync).to.have.length(2) expect(sync[0]).to.have.property('type', 'image') expect(sync[0]).to.have.property('url').match(/http:\/\/cm\.mgid\.com\/i\.gif\?cdsp=1111&cbuster=\d+&gdpr_consent=consent1&gdpr=1&us_privacy=consent2&gppString=gpp&coppa=1/) @@ -903,7 +902,7 @@ describe('Mgid bid adapter', 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'}]}; + 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'); @@ -911,7 +910,7 @@ describe('Mgid bid adapter', function () { 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'}]}; + 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); diff --git a/test/spec/modules/mgidRtdProvider_spec.js b/test/spec/modules/mgidRtdProvider_spec.js index 7fd41a3c4c5..4aa0d65f1bc 100644 --- a/test/spec/modules/mgidRtdProvider_spec.js +++ b/test/spec/modules/mgidRtdProvider_spec.js @@ -1,7 +1,7 @@ import { mgidSubmodule, storage } from '../../../modules/mgidRtdProvider.js'; -import {expect} from 'chai'; +import { expect } from 'chai'; import * as refererDetection from '../../../src/refererDetection.js'; -import {server} from '../../mocks/xhr.js'; +import { server } from '../../mocks/xhr.js'; describe('Mgid RTD submodule', () => { let clock; @@ -26,14 +26,14 @@ describe('Mgid RTD submodule', () => { }); it('init is successfull, when clientSiteId is defined', () => { - expect(mgidSubmodule.init({params: {clientSiteId: 123}})).to.be.true; + expect(mgidSubmodule.init({ params: { clientSiteId: 123 } })).to.be.true; }); it('init is unsuccessfull, when clientSiteId is not defined', () => { expect(mgidSubmodule.init({})).to.be.false; }); - it('getBidRequestData send all params to our endpoint and succesfully modifies ortb2', () => { + it('getBidRequestData send all params to our endpoint and successfully modifies ortb2', () => { const responseObj = { userSegments: ['100', '200'], userSegtax: 5, @@ -59,7 +59,7 @@ describe('Mgid RTD submodule', () => { mgidSubmodule.getBidRequestData( reqBidsConfigObj, onDone, - {params: {clientSiteId: 123}}, + { params: { clientSiteId: 123 } }, { gdpr: { gdprApplies: true, @@ -71,7 +71,7 @@ describe('Mgid RTD submodule', () => { server.requests[0].respond( 200, - {'Content-Type': 'application/json'}, + { 'Content-Type': 'application/json' }, JSON.stringify(responseObj) ); @@ -134,13 +134,13 @@ describe('Mgid RTD submodule', () => { mgidSubmodule.getBidRequestData( reqBidsConfigObj, onDone, - {params: {clientSiteId: 123}}, + { params: { clientSiteId: 123 } }, {} ); server.requests[0].respond( 200, - {'Content-Type': 'application/json'}, + { 'Content-Type': 'application/json' }, JSON.stringify({}) ); @@ -168,7 +168,7 @@ describe('Mgid RTD submodule', () => { mgidSubmodule.getBidRequestData( reqBidsConfigObj, onDone, - {params: {clientSiteId: 123}}, + { params: { clientSiteId: 123 } }, { gdpr: { gdprApplies: false, @@ -180,7 +180,7 @@ describe('Mgid RTD submodule', () => { server.requests[0].respond( 200, - {'Content-Type': 'application/json'}, + { 'Content-Type': 'application/json' }, JSON.stringify({}) ); @@ -213,13 +213,13 @@ describe('Mgid RTD submodule', () => { mgidSubmodule.getBidRequestData( reqBidsConfigObj, onDone, - {params: {clientSiteId: 123}}, + { params: { clientSiteId: 123 } }, {} ); server.requests[0].respond( 200, - {'Content-Type': 'application/json'}, + { 'Content-Type': 'application/json' }, JSON.stringify({}) ); @@ -246,13 +246,13 @@ describe('Mgid RTD submodule', () => { mgidSubmodule.getBidRequestData( reqBidsConfigObj, onDone, - {params: {clientSiteId: 123}}, + { params: { clientSiteId: 123 } }, {} ); server.requests[0].respond( 200, - {'Content-Type': 'application/json'}, + { 'Content-Type': 'application/json' }, JSON.stringify({}) ); @@ -273,13 +273,13 @@ describe('Mgid RTD submodule', () => { mgidSubmodule.getBidRequestData( reqBidsConfigObj, onDone, - {params: {clientSiteId: 123}}, + { params: { clientSiteId: 123 } }, {} ); server.requests[0].respond( 200, - {'Content-Type': 'application/json'}, + { 'Content-Type': 'application/json' }, '{' ); @@ -299,13 +299,13 @@ describe('Mgid RTD submodule', () => { mgidSubmodule.getBidRequestData( reqBidsConfigObj, onDone, - {params: {clientSiteId: 123}}, + { params: { clientSiteId: 123 } }, {} ); server.requests[0].respond( 204, - {'Content-Type': 'application/json'}, + { 'Content-Type': 'application/json' }, ); assert.deepEqual(reqBidsConfigObj.ortb2Fragments.global, {}); @@ -324,13 +324,13 @@ describe('Mgid RTD submodule', () => { mgidSubmodule.getBidRequestData( reqBidsConfigObj, onDone, - {params: {clientSiteId: 123}}, + { params: { clientSiteId: 123 } }, {} ); server.requests[0].respond( 500, - {'Content-Type': 'application/json'}, + { 'Content-Type': 'application/json' }, '{}' ); @@ -350,7 +350,7 @@ describe('Mgid RTD submodule', () => { mgidSubmodule.getBidRequestData( reqBidsConfigObj, onDone, - {params: {clientSiteId: 123, timeout: 500}}, + { params: { clientSiteId: 123, timeout: 500 } }, {} ); diff --git a/test/spec/modules/mgidXBidAdapter_spec.js b/test/spec/modules/mgidXBidAdapter_spec.js index f6e1fd68082..86d448e5be8 100644 --- a/test/spec/modules/mgidXBidAdapter_spec.js +++ b/test/spec/modules/mgidXBidAdapter_spec.js @@ -492,32 +492,32 @@ describe('MGIDXBidAdapter', function () { describe('getUserSyncs', function () { afterEach(function() { - config.setConfig({userSync: {syncsPerBidder: USERSYNC_DEFAULT_CONFIG.syncsPerBidder}}); + config.setConfig({ userSync: { syncsPerBidder: USERSYNC_DEFAULT_CONFIG.syncsPerBidder } }); }); it('should do nothing on getUserSyncs without inputs', function () { expect(spec.getUserSyncs()).to.equal(undefined) }); it('should return frame object with empty consents', function () { - const sync = spec.getUserSyncs({iframeEnabled: true}) + const sync = spec.getUserSyncs({ iframeEnabled: true }) expect(sync).to.have.length(1) expect(sync[0]).to.have.property('type', 'iframe') expect(sync[0]).to.have.property('url').match(/https:\/\/cm\.mgid\.com\/i\.html\?cbuster=\d+&gdpr_consent=&gdpr=0/) }); it('should return frame object with gdpr consent', function () { - const sync = spec.getUserSyncs({iframeEnabled: true}, undefined, {consentString: 'consent', gdprApplies: true}) + const sync = spec.getUserSyncs({ iframeEnabled: true }, undefined, { consentString: 'consent', gdprApplies: true }) expect(sync).to.have.length(1) expect(sync[0]).to.have.property('type', 'iframe') expect(sync[0]).to.have.property('url').match(/https:\/\/cm\.mgid\.com\/i\.html\?cbuster=\d+&gdpr_consent=consent&gdpr=1/) }); it('should return frame object with gdpr + usp', function () { - const sync = spec.getUserSyncs({iframeEnabled: true}, undefined, {consentString: 'consent1', gdprApplies: true}, {'consentString': 'consent2'}) + const sync = spec.getUserSyncs({ iframeEnabled: true }, undefined, { consentString: 'consent1', gdprApplies: true }, { 'consentString': 'consent2' }) expect(sync).to.have.length(1) expect(sync[0]).to.have.property('type', 'iframe') expect(sync[0]).to.have.property('url').match(/https:\/\/cm\.mgid\.com\/i\.html\?cbuster=\d+&gdpr_consent=consent1&gdpr=1&us_privacy=consent2/) }); it('should return img object with gdpr + usp', function () { - config.setConfig({userSync: {syncsPerBidder: undefined}}); - const sync = spec.getUserSyncs({pixelEnabled: true}, undefined, {consentString: 'consent1', gdprApplies: true}, {'consentString': 'consent2'}) + config.setConfig({ userSync: { syncsPerBidder: undefined } }); + const sync = spec.getUserSyncs({ pixelEnabled: true }, undefined, { consentString: 'consent1', gdprApplies: true }, { 'consentString': 'consent2' }) expect(sync).to.have.length(USERSYNC_DEFAULT_CONFIG.syncsPerBidder) for (let i = 0; i < USERSYNC_DEFAULT_CONFIG.syncsPerBidder; i++) { expect(sync[i]).to.have.property('type', 'image') @@ -525,14 +525,14 @@ describe('MGIDXBidAdapter', function () { } }); it('should return frame object with gdpr + usp', function () { - const sync = spec.getUserSyncs({iframeEnabled: true, pixelEnabled: true}, undefined, {consentString: 'consent1', gdprApplies: true}, {'consentString': 'consent2'}) + const sync = spec.getUserSyncs({ iframeEnabled: true, pixelEnabled: true }, undefined, { consentString: 'consent1', gdprApplies: true }, { 'consentString': 'consent2' }) expect(sync).to.have.length(1) expect(sync[0]).to.have.property('type', 'iframe') expect(sync[0]).to.have.property('url').match(/https:\/\/cm\.mgid\.com\/i\.html\?cbuster=\d+&gdpr_consent=consent1&gdpr=1&us_privacy=consent2/) }); it('should return img (pixels) objects with gdpr + usp', function () { - const response = [{body: {ext: {cm: ['http://cm.mgid.com/i.gif?cdsp=1111', 'http://cm.mgid.com/i.gif']}}}] - const sync = spec.getUserSyncs({iframeEnabled: false, pixelEnabled: true}, response, {consentString: 'consent1', gdprApplies: true}, {'consentString': 'consent2'}) + const response = [{ body: { ext: { cm: ['http://cm.mgid.com/i.gif?cdsp=1111', 'http://cm.mgid.com/i.gif'] } } }] + const sync = spec.getUserSyncs({ iframeEnabled: false, pixelEnabled: true }, response, { consentString: 'consent1', gdprApplies: true }, { 'consentString': 'consent2' }) expect(sync).to.have.length(2) expect(sync[0]).to.have.property('type', 'image') expect(sync[0]).to.have.property('url').match(/http:\/\/cm\.mgid\.com\/i\.gif\?cdsp=1111&cbuster=\d+&gdpr_consent=consent1&gdpr=1&us_privacy=consent2/) diff --git a/test/spec/modules/microadBidAdapter_spec.js b/test/spec/modules/microadBidAdapter_spec.js index f1a4fbec9e7..013d6d3f0f1 100644 --- a/test/spec/modules/microadBidAdapter_spec.js +++ b/test/spec/modules/microadBidAdapter_spec.js @@ -286,36 +286,36 @@ describe('microadBidAdapter', () => { Object.entries({ 'IM-UID': { - userId: {imuid: 'imuid-sample'}, - expected: {aids: JSON.stringify([{type: 6, id: 'imuid-sample'}])} + userId: { imuid: 'imuid-sample' }, + expected: { aids: JSON.stringify([{ type: 6, id: 'imuid-sample' }]) } }, 'ID5 ID': { - userId: {id5id: {uid: 'id5id-sample'}}, - expected: {aids: JSON.stringify([{type: 8, id: 'id5id-sample'}])} + userId: { id5id: { uid: 'id5id-sample' } }, + expected: { aids: JSON.stringify([{ type: 8, id: 'id5id-sample' }]) } }, 'Unified ID': { - userId: {tdid: 'unified-sample'}, - expected: {aids: JSON.stringify([{type: 9, id: 'unified-sample'}])} + userId: { tdid: 'unified-sample' }, + expected: { aids: JSON.stringify([{ type: 9, id: 'unified-sample' }]) } }, 'Novatiq Snowflake ID': { - userId: {novatiq: {snowflake: 'novatiq-sample'}}, - expected: {aids: JSON.stringify([{type: 10, id: 'novatiq-sample'}])} + userId: { novatiq: { snowflake: 'novatiq-sample' } }, + expected: { aids: JSON.stringify([{ type: 10, id: 'novatiq-sample' }]) } }, 'AudienceOne User ID': { - userId: {dacId: {id: 'audience-one-sample'}}, - expected: {aids: JSON.stringify([{type: 12, id: 'audience-one-sample'}])} + userId: { dacId: { id: 'audience-one-sample' } }, + expected: { aids: JSON.stringify([{ type: 12, id: 'audience-one-sample' }]) } }, 'Ramp ID and Liveramp identity': { - userId: {idl_env: 'idl-env-sample'}, - expected: {idl_env: 'idl-env-sample', aids: JSON.stringify([{type: 13, id: 'idl-env-sample'}])} + userId: { idl_env: 'idl-env-sample' }, + expected: { idl_env: 'idl-env-sample', aids: JSON.stringify([{ type: 13, id: 'idl-env-sample' }]) } }, 'Criteo ID': { - userId: {criteoId: 'criteo-id-sample'}, - expected: {aids: JSON.stringify([{type: 14, id: 'criteo-id-sample'}])} + userId: { criteoId: 'criteo-id-sample' }, + expected: { aids: JSON.stringify([{ type: 14, id: 'criteo-id-sample' }]) } }, 'Shared ID': { - userId: {pubcid: 'shared-id-sample'}, - expected: {aids: JSON.stringify([{type: 15, id: 'shared-id-sample'}])} + userId: { pubcid: 'shared-id-sample' }, + expected: { aids: JSON.stringify([{ type: 15, id: 'shared-id-sample' }]) } } }).forEach(([test, arg]) => { it(`should add ${test} if it is available in request parameters`, () => { @@ -333,32 +333,32 @@ describe('microadBidAdapter', () => { Object.entries({ 'ID5 ID': { - userId: {id5id: {uid: 'id5id-sample'}}, + userId: { id5id: { uid: 'id5id-sample' } }, userIdAsEids: [ { source: 'id5-sync.com', - uids: [{id: 'id5id-sample', aType: 1, ext: {linkType: 2, abTestingControlGroup: false}}] + uids: [{ id: 'id5id-sample', aType: 1, ext: { linkType: 2, abTestingControlGroup: false } }] } ], expected: { - aids: JSON.stringify([{type: 8, id: 'id5id-sample', ext: {linkType: 2, abTestingControlGroup: false}}]) + aids: JSON.stringify([{ type: 8, id: 'id5id-sample', ext: { linkType: 2, abTestingControlGroup: false } }]) } }, 'Unified ID': { - userId: {tdid: 'unified-sample'}, + userId: { tdid: 'unified-sample' }, userIdAsEids: [ { source: 'adserver.org', - uids: [{id: 'unified-sample', aType: 1, ext: {rtiPartner: 'TDID'}}] + uids: [{ id: 'unified-sample', aType: 1, ext: { rtiPartner: 'TDID' } }] } ], - expected: {aids: JSON.stringify([{type: 9, id: 'unified-sample', ext: {rtiPartner: 'TDID'}}])} + expected: { aids: JSON.stringify([{ type: 9, id: 'unified-sample', ext: { rtiPartner: 'TDID' } }]) } }, 'not add': { - userId: {id5id: {uid: 'id5id-sample'}}, + userId: { id5id: { uid: 'id5id-sample' } }, userIdAsEids: [], expected: { - aids: JSON.stringify([{type: 8, id: 'id5id-sample'}]) + aids: JSON.stringify([{ type: 8, id: 'id5id-sample' }]) } } }).forEach(([test, arg]) => { @@ -665,12 +665,12 @@ describe('microadBidAdapter', () => { } }; const expectedIframeSyncs = [ - {type: 'iframe', url: 'https://www.example.com/iframe1'}, - {type: 'iframe', url: 'https://www.example.com/iframe2'} + { type: 'iframe', url: 'https://www.example.com/iframe1' }, + { type: 'iframe', url: 'https://www.example.com/iframe2' } ]; const expectedImageSyncs = [ - {type: 'image', url: 'https://www.example.com/image1'}, - {type: 'image', url: 'https://www.example.com/image2'} + { type: 'image', url: 'https://www.example.com/image1' }, + { type: 'image', url: 'https://www.example.com/image2' } ]; it('should return nothing if no sync urls are set', () => { diff --git a/test/spec/modules/mileBidAdapter_spec.js b/test/spec/modules/mileBidAdapter_spec.js new file mode 100644 index 00000000000..34171f86114 --- /dev/null +++ b/test/spec/modules/mileBidAdapter_spec.js @@ -0,0 +1,691 @@ +import { expect } from 'chai'; +import { spec, siteIdTracker, publisherIdTracker } from 'modules/mileBidAdapter.js'; +import { BANNER } from 'src/mediaTypes.js'; +import * as ajax from 'src/ajax.js'; +import * as utils from 'src/utils.js'; + +describe('mileBidAdapter', function () { + describe('isBidRequestValid', function () { + let bid; + + beforeEach(function () { + bid = { + bidder: 'mile', + params: { + placementId: '12345', + siteId: 'site123', + publisherId: 'pub456' + }, + mediaTypes: { + banner: { + sizes: [[300, 250]] + } + } + }; + }); + + it('should return true when all required params are present', function () { + expect(spec.isBidRequestValid(bid)).to.be.true; + }); + + it('should return false when placementId is missing', function () { + delete bid.params.placementId; + expect(spec.isBidRequestValid(bid)).to.be.false; + }); + + it('should return false when siteId is missing', function () { + delete bid.params.siteId; + expect(spec.isBidRequestValid(bid)).to.be.false; + }); + + it('should return false when publisherId is missing', function () { + delete bid.params.publisherId; + expect(spec.isBidRequestValid(bid)).to.be.false; + }); + + it('should return false when params is missing', function () { + delete bid.params; + expect(spec.isBidRequestValid(bid)).to.be.false; + }); + + it('should return false when params is null', function () { + bid.params = null; + expect(spec.isBidRequestValid(bid)).to.be.false; + }); + }); + + describe('buildRequests', function () { + let validBidRequests, bidderRequest; + + beforeEach(function () { + validBidRequests = [{ + bidder: 'mile', + params: { + placementId: '12345', + siteId: 'site123', + publisherId: 'pub456' + }, + mediaTypes: { + banner: { + sizes: [[300, 250], [728, 90]] + } + }, + adUnitCode: 'test-ad-unit', + bidId: 'bid123', + ortb2Imp: { + ext: { + gpid: '/test/ad/unit' + } + } + }]; + + bidderRequest = { + bidderCode: 'mile', + bidderRequestId: 'bidderReq123', + auctionId: 'auction123', + timeout: 3000, + refererInfo: { + page: 'https://example.com/page', + domain: 'example.com', + ref: 'https://google.com' + }, + ortb2: { + source: { + tid: 'transaction123' + } + } + }; + }); + + it('should return a valid server request object', function () { + const request = spec.buildRequests(validBidRequests, bidderRequest); + + expect(request).to.be.an('object'); + expect(request.method).to.equal('POST'); + expect(request.url).to.equal('https://pbs.atmtd.com/mile/v1/request'); + expect(request.data).to.be.an('object'); + }); + + it('should build OpenRTB 2.5 compliant request', function () { + const request = spec.buildRequests(validBidRequests, bidderRequest); + const data = request.data; + + expect(data.id).to.equal('bidderReq123'); + expect(data.imp).to.be.an('array').with.lengthOf(1); + expect(data.tmax).to.equal(3000); + expect(data.cur).to.deep.equal(['USD']); + expect(data.site).to.be.an('object'); + expect(data.device).to.be.an('object'); + expect(data.source).to.be.an('object'); + }); + + it('should include imp object with correct structure', function () { + const request = spec.buildRequests(validBidRequests, bidderRequest); + const imp = request.data.imp[0]; + + expect(imp.id).to.equal('bid123'); + expect(imp.tagid).to.equal('12345'); + expect(imp.secure).to.equal(1); + expect(imp.banner).to.be.an('object'); + expect(imp.banner.format).to.be.an('array').with.lengthOf(2); + expect(imp.banner.format[0]).to.deep.equal({ w: 300, h: 250 }); + expect(imp.banner.format[1]).to.deep.equal({ w: 728, h: 90 }); + }); + + it('should include ext fields in imp object', function () { + const request = spec.buildRequests(validBidRequests, bidderRequest); + const imp = request.data.imp[0]; + + expect(imp.ext.adUnitCode).to.equal('test-ad-unit'); + expect(imp.ext.placementId).to.equal('12345'); + expect(imp.ext.gpid).to.equal('/test/ad/unit'); + }); + + it('should include site object with publisher info', function () { + const request = spec.buildRequests(validBidRequests, bidderRequest); + const site = request.data.site; + + expect(site.id).to.equal('site123'); + expect(site.page).to.equal('https://example.com/page'); + expect(site.domain).to.equal('example.com'); + expect(site.ref).to.equal('https://google.com'); + expect(site.publisher.id).to.equal('pub456'); + }); + + it('should include device object', function () { + const request = spec.buildRequests(validBidRequests, bidderRequest); + const device = request.data.device; + + expect(device.ua).to.be.a('string'); + expect(device.language).to.be.a('string'); + expect(device.dnt).to.be.a('number'); + }); + + it('should include source object with tid', function () { + const request = spec.buildRequests(validBidRequests, bidderRequest); + const source = request.data.source; + + expect(source.tid).to.equal('transaction123'); + }); + + it('should include bidfloor when floor price is available', function () { + validBidRequests[0].getFloor = function() { + return { floor: 0.5, currency: 'USD' }; + }; + + const request = spec.buildRequests(validBidRequests, bidderRequest); + const imp = request.data.imp[0]; + + expect(imp.bidfloor).to.equal(0.5); + expect(imp.bidfloorcur).to.equal('USD'); + }); + + it('should include GDPR consent when present', function () { + bidderRequest.gdprConsent = { + gdprApplies: true, + consentString: 'consent-string-123' + }; + + const request = spec.buildRequests(validBidRequests, bidderRequest); + + expect(request.data.regs.ext.gdpr).to.equal(1); + expect(request.data.user.ext.consent).to.equal('consent-string-123'); + }); + + it('should include US Privacy consent when present', function () { + bidderRequest.uspConsent = '1YNN'; + + const request = spec.buildRequests(validBidRequests, bidderRequest); + + expect(request.data.regs.ext.us_privacy).to.equal('1YNN'); + }); + + it('should include GPP consent when present', function () { + bidderRequest.gppConsent = { + gppString: 'gpp-string-123', + applicableSections: [1, 2, 3] + }; + + const request = spec.buildRequests(validBidRequests, bidderRequest); + + expect(request.data.regs.gpp).to.equal('gpp-string-123'); + expect(request.data.regs.gpp_sid).to.deep.equal([1, 2, 3]); + }); + + it('should include user EIDs when present', function () { + validBidRequests[0].userIdAsEids = [ + { + source: 'pubcid.org', + uids: [{ id: 'user-id-123' }] + } + ]; + + const request = spec.buildRequests(validBidRequests, bidderRequest); + + expect(request.data.user.ext.eids).to.be.an('array').with.lengthOf(1); + expect(request.data.user.ext.eids[0].source).to.equal('pubcid.org'); + }); + + it('should include supply chain when present', function () { + validBidRequests[0].ortb2 = { + source: { + ext: { + schain: { + ver: '1.0', + complete: 1, + nodes: [] + } + } + } + }; + + const request = spec.buildRequests(validBidRequests, bidderRequest); + + expect(request.data.source.ext.schain).to.be.an('object'); + expect(request.data.source.ext.schain.ver).to.equal('1.0'); + }); + + it('should handle multiple bid requests with same siteId and publisherId', function () { + const secondBid = { + ...validBidRequests[0], + bidId: 'bid456', + params: { + placementId: '67890', + siteId: 'site123', + publisherId: 'pub456' + } + }; + + const request = spec.buildRequests([validBidRequests[0], secondBid], bidderRequest); + + expect(request.data.imp).to.be.an('array').with.lengthOf(2); + expect(request.data.imp[0].id).to.equal('bid123'); + expect(request.data.imp[1].id).to.equal('bid456'); + }); + + it('should reject bids with different siteId', function () { + const firstBid = { + bidder: 'mile', + params: { + placementId: '12345', + siteId: 'site123', + publisherId: 'pub456' + }, + mediaTypes: { + banner: { + sizes: [[300, 250]] + } + } + }; + + const secondBid = { + bidder: 'mile', + params: { + placementId: '67890', + siteId: 'differentSite', // Different siteId + publisherId: 'pub456' + }, + mediaTypes: { + banner: { + sizes: [[300, 250]] + } + } + }; + + // First bid should be valid + expect(spec.isBidRequestValid(firstBid)).to.be.true; + + // Second bid should be rejected due to siteId mismatch + expect(spec.isBidRequestValid(secondBid)).to.be.false; + }); + + it('should reject bids with different publisherId', function () { + const firstBid = { + bidder: 'mile', + params: { + placementId: '12345', + siteId: 'site123', + publisherId: 'pub456' + }, + mediaTypes: { + banner: { + sizes: [[300, 250]] + } + } + }; + + const secondBid = { + bidder: 'mile', + params: { + placementId: '67890', + siteId: 'site123', + publisherId: 'differentPub' + }, + mediaTypes: { + banner: { + sizes: [[300, 250]] + } + } + }; + + // First bid should be valid + expect(spec.isBidRequestValid(firstBid)).to.be.true; + + // Second bid should be rejected due to publisherId mismatch + expect(spec.isBidRequestValid(secondBid)).to.be.false; + }); + }); + + describe('interpretResponse', function () { + let serverResponse; + + beforeEach(function () { + serverResponse = { + body: { + site: { + id: 'site123', + domain: 'example.com', + publisher: { + id: 'pub456' + }, + page: 'https://example.com/page', + }, + cur: 'USD', + bids: [ + { + requestId: 'bid123', + cpm: 1.5, + width: 300, + height: 250, + ad: '
test ad
', + creativeId: 'creative123', + ttl: 300, + nurl: 'https://example.com/win?price=${AUCTION_PRICE}', + adomain: ['advertiser.com'], + upstreamBidder: 'upstreamBidder' + } + ] + } + }; + }); + + it('should return an array of bid responses', function () { + const bids = spec.interpretResponse(serverResponse); + + expect(bids).to.be.an('array').with.lengthOf(1); + }); + + it('should parse bid response correctly', function () { + const bids = spec.interpretResponse(serverResponse); + const bid = bids[0]; + + expect(bid.requestId).to.equal('bid123'); + expect(bid.cpm).to.equal(1.5); + expect(bid.width).to.equal(300); + expect(bid.height).to.equal(250); + expect(bid.ad).to.equal('
test ad
'); + expect(bid.creativeId).to.equal('creative123'); + expect(bid.currency).to.equal('USD'); + expect(bid.ttl).to.equal(300); + expect(bid.netRevenue).to.be.true; + expect(bid.mediaType).to.equal(BANNER); + expect(bid.meta.upstreamBidder).to.equal('upstreamBidder'); + expect(bid.meta.siteUID).to.equal('site123'); + expect(bid.meta.publisherID).to.equal('pub456'); + expect(bid.meta.page).to.equal('https://example.com/page'); + expect(bid.meta.domain).to.equal('example.com'); + }); + + it('should include nurl in bid response', function () { + const bids = spec.interpretResponse(serverResponse); + const bid = bids[0]; + + expect(bid.nurl).to.equal('https://example.com/win?price=${AUCTION_PRICE}'); + }); + + it('should include meta.advertiserDomains', function () { + const bids = spec.interpretResponse(serverResponse); + const bid = bids[0]; + + expect(bid.meta.advertiserDomains).to.deep.equal(['advertiser.com']); + }); + + it('should handle empty response', function () { + const bids = spec.interpretResponse({ body: null }); + + expect(bids).to.be.an('array').with.lengthOf(0); + }); + + it('should handle response with no bids', function () { + serverResponse.body.bids = []; + const bids = spec.interpretResponse(serverResponse); + + expect(bids).to.be.an('array').with.lengthOf(0); + }); + + it('should handle alternative field names (w/h instead of width/height)', function () { + serverResponse.body.bids[0] = { + requestId: 'bid123', + cpm: 1.5, + w: 728, + h: 90, + ad: '
test ad
' + }; + + const bids = spec.interpretResponse(serverResponse); + const bid = bids[0]; + + expect(bid.width).to.equal(728); + expect(bid.height).to.equal(90); + }); + + it('should use default currency if not specified', function () { + delete serverResponse.body.cur; + const bids = spec.interpretResponse(serverResponse); + + expect(bids[0].currency).to.equal('USD'); + }); + + it('should handle response with no site or publisher', function () { + delete serverResponse.body.site; + delete serverResponse.body.publisher; + const bids = spec.interpretResponse(serverResponse); + + expect(bids[0].meta.siteUID).to.be.empty; + expect(bids[0].meta.publisherID).to.be.empty; + expect(bids[0].meta.page).to.be.empty; + expect(bids[0].meta.domain).to.be.empty; + }); + }); + + describe('getUserSyncs', function () { + let syncOptions, serverResponses, gdprConsent, uspConsent, gppConsent; + + beforeEach(function () { + syncOptions = { + iframeEnabled: true, + pixelEnabled: true + }; + serverResponses = []; + }); + + it('should return iframe sync when enabled', function () { + const syncs = spec.getUserSyncs(syncOptions, serverResponses); + + expect(syncs).to.be.an('array').with.lengthOf(1); + expect(syncs[0].type).to.equal('iframe'); + expect(syncs[0].url).to.include('https://scripts.atmtd.com/user-sync/load-cookie.html'); + }); + + it('should not return syncs when iframe is disabled', function () { + syncOptions.iframeEnabled = false; + const syncs = spec.getUserSyncs(syncOptions, serverResponses); + + expect(syncs).to.be.an('array').with.lengthOf(0); + }); + + it('should include GDPR consent params', function () { + gdprConsent = { + gdprApplies: true, + consentString: 'consent-string-123' + }; + + const syncs = spec.getUserSyncs(syncOptions, serverResponses, gdprConsent); + + expect(syncs[0].url).to.include('gdpr=1'); + expect(syncs[0].url).to.include('gdpr_consent=consent-string-123'); + }); + + it('should include US Privacy consent param', function () { + uspConsent = '1YNN'; + + const syncs = spec.getUserSyncs(syncOptions, serverResponses, null, uspConsent); + + expect(syncs[0].url).to.include('us_privacy=1YNN'); + }); + + it('should include GPP consent params', function () { + gppConsent = { + gppString: 'gpp-string-123', + applicableSections: [1, 2, 3] + }; + + const syncs = spec.getUserSyncs(syncOptions, serverResponses, null, null, gppConsent); + + expect(syncs[0].url).to.include('gpp=gpp-string-123'); + expect(syncs[0].url).to.include('gpp_sid=1%2C2%2C3'); + }); + + it('should include all consent params when present', function () { + gdprConsent = { gdprApplies: true, consentString: 'gdpr-consent' }; + uspConsent = '1YNN'; + gppConsent = { gppString: 'gpp-string', applicableSections: [1] }; + + const syncs = spec.getUserSyncs(syncOptions, serverResponses, gdprConsent, uspConsent, gppConsent); + + expect(syncs[0].url).to.include('gdpr=1'); + expect(syncs[0].url).to.include('us_privacy=1YNN'); + expect(syncs[0].url).to.include('gpp=gpp-string'); + }); + }); + + describe('onBidWon', function () { + let bid, ajaxStub; + + beforeEach(function () { + bid = { + bidder: 'mile', + adUnitCode: 'test-ad-unit', + requestId: 'bid123', + cpm: 1.5, + width: 300, + height: 250, + nurl: 'https://example.com/win', + meta: { + upstreamBidder: 'upstreamBidder', + siteUID: 'mRUDIL', + publisherID: 'pub456', + page: 'https://example.com/page', + domain: 'example.com' + } + }; + + ajaxStub = sinon.stub(ajax, 'ajax'); + }); + + afterEach(function () { + ajaxStub.restore(); + }); + + it('should call ajax with win notification endpoint', function () { + spec.onBidWon(bid); + + expect(ajaxStub.calledTwice).to.be.true; + + // First call to notification endpoint + const firstCall = ajaxStub.getCall(0); + expect(firstCall.args[0]).to.equal('https://e01.atmtd.com/bidanalytics-event/json'); + + // Second call to nurl + const secondCall = ajaxStub.getCall(1); + expect(secondCall.args[0]).to.equal('https://example.com/win'); + }); + + it('should send correct win notification data', function () { + spec.onBidWon(bid); + + const firstCall = ajaxStub.getCall(0); + const notificationData = JSON.parse(firstCall.args[2])[0]; + + expect(notificationData.adUnitCode).to.equal('test-ad-unit'); + expect(notificationData.metaData.impressionID[0]).to.equal('bid123'); + expect(notificationData.winningBidder).to.equal('upstreamBidder'); + expect(notificationData.cpm).to.equal(1.5); + expect(notificationData.winningSize).to.equal('300x250'); + expect(notificationData.eventType).to.equal('mile-bidder-win-notify'); + expect(notificationData.timestamp).to.be.a('number'); + expect(notificationData.siteUID).to.equal('mRUDIL'); + expect(notificationData.yetiPublisherID).to.equal('pub456'); + expect(notificationData.page).to.equal('https://example.com/page'); + expect(notificationData.site).to.equal('example.com'); + }); + + it('should call nurl with GET request', function () { + spec.onBidWon(bid); + + const secondCall = ajaxStub.getCall(1); + const options = secondCall.args[3]; + + expect(options.method).to.equal('GET'); + }); + }); + + describe('onTimeout', function () { + let timeoutData, ajaxStub; + + beforeEach(function () { + timeoutData = [ + { + bidder: 'mile', + bidId: 'bid123', + adUnitCode: 'test-ad-unit-1', + timeout: 3000, + params: { + placementId: '12345', + siteId: 'site123', + publisherId: 'pub456' + } + }, + { + bidder: 'mile', + bidId: 'bid456', + adUnitCode: 'test-ad-unit-2', + timeout: 3000, + params: { + placementId: '67890', + siteId: 'site123', + publisherId: 'pub456' + } + } + ]; + + ajaxStub = sinon.stub(ajax, 'ajax'); + }); + + afterEach(function () { + ajaxStub.restore(); + }); + + it('should call ajax for each timed out bid', function () { + spec.onTimeout(timeoutData); + + expect(ajaxStub.callCount).to.equal(1); + }); + + it('should send correct timeout notification data', function () { + spec.onTimeout(timeoutData); + + const firstCall = ajaxStub.getCall(0); + expect(firstCall.args[0]).to.equal('https://e01.atmtd.com/bidanalytics-event/json'); + + const notificationData = JSON.parse(firstCall.args[2])[0]; + expect(notificationData.adUnitCode).to.equal('test-ad-unit-1'); + expect(notificationData.metaData.impressionID[0]).to.equal('bid123'); + expect(notificationData.metaData.configuredTimeout[0]).to.equal('3000'); + expect(notificationData.eventType).to.equal('mile-bidder-timeout'); + expect(notificationData.timestamp).to.be.a('number'); + }); + + it('should handle single timeout', function () { + spec.onTimeout([timeoutData[0]]); + + expect(ajaxStub.calledOnce).to.be.true; + }); + + it('should handle empty timeout array', function () { + spec.onTimeout([]); + + expect(ajaxStub.called).to.be.false; + }); + }); + + describe('adapter specification', function () { + it('should have correct bidder code', function () { + expect(spec.code).to.equal('mile'); + }); + + it('should support BANNER media type', function () { + expect(spec.supportedMediaTypes).to.be.an('array'); + expect(spec.supportedMediaTypes).to.include(BANNER); + }); + + it('should have required adapter 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.getUserSyncs).to.be.a('function'); + expect(spec.onBidWon).to.be.a('function'); + expect(spec.onTimeout).to.be.a('function'); + }); + }); +}); diff --git a/test/spec/modules/minutemediaBidAdapter_spec.js b/test/spec/modules/minutemediaBidAdapter_spec.js index c8f41a42781..4bbbe6476b4 100644 --- a/test/spec/modules/minutemediaBidAdapter_spec.js +++ b/test/spec/modules/minutemediaBidAdapter_spec.js @@ -2,9 +2,9 @@ import { expect } from 'chai'; import { spec } from 'modules/minutemediaBidAdapter.js'; import { newBidder } from 'src/adapters/bidderFactory.js'; import { config } from 'src/config.js'; -import {BANNER, NATIVE, VIDEO} from '../../../src/mediaTypes.js'; +import { BANNER, NATIVE, VIDEO } from '../../../src/mediaTypes.js'; import * as utils from 'src/utils.js'; -import {decorateAdUnitsWithNativeParams} from '../../../src/native.js'; +import { decorateAdUnitsWithNativeParams } from '../../../src/native.js'; const ENDPOINT = 'https://hb.minutemedia-prebid.com/hb-mm-multi'; const TEST_ENDPOINT = 'https://hb.minutemedia-prebid.com/hb-multi-mm-test'; @@ -95,7 +95,7 @@ describe('minutemediaAdapter', function () { 'mediaTypes': { 'banner': { 'sizes': [ - [ 300, 250 ] + [300, 250] ] }, 'video': { @@ -324,7 +324,7 @@ describe('minutemediaAdapter', function () { }); it('should have us_privacy param if usPrivacy is available in the bidRequest', function () { - const bidderRequestWithUSP = Object.assign({uspConsent: '1YNN'}, bidderRequest); + const bidderRequestWithUSP = Object.assign({ uspConsent: '1YNN' }, bidderRequest); const request = spec.buildRequests(bidRequests, bidderRequestWithUSP); expect(request.data.params).to.be.an('object'); expect(request.data.params).to.have.property('us_privacy', '1YNN'); @@ -337,7 +337,7 @@ describe('minutemediaAdapter', function () { }); it('should not send the gdpr param if gdprApplies is false in the bidRequest', function () { - const bidderRequestWithGDPR = Object.assign({gdprConsent: {gdprApplies: false}}, bidderRequest); + const bidderRequestWithGDPR = Object.assign({ gdprConsent: { gdprApplies: false } }, bidderRequest); const request = spec.buildRequests(bidRequests, bidderRequestWithGDPR); expect(request.data.params).to.be.an('object'); expect(request.data.params).to.not.have.property('gdpr'); @@ -345,7 +345,7 @@ describe('minutemediaAdapter', function () { }); it('should send the gdpr param if gdprApplies is true in the bidRequest', function () { - const bidderRequestWithGDPR = Object.assign({gdprConsent: {gdprApplies: true, consentString: 'test-consent-string'}}, bidderRequest); + const bidderRequestWithGDPR = Object.assign({ gdprConsent: { gdprApplies: true, consentString: 'test-consent-string' } }, bidderRequest); const request = spec.buildRequests(bidRequests, bidderRequestWithGDPR); expect(request.data.params).to.be.an('object'); expect(request.data.params).to.have.property('gdpr', true); @@ -353,7 +353,7 @@ describe('minutemediaAdapter', function () { }); it('should not send the gpp param if gppConsent is false in the bidRequest', function () { - const bidderRequestWithGPP = Object.assign({gppConsent: false}, bidderRequest); + const bidderRequestWithGPP = Object.assign({ gppConsent: false }, bidderRequest); const request = spec.buildRequests(bidRequests, bidderRequestWithGPP); expect(request.data.params).to.be.an('object'); expect(request.data.params).to.not.have.property('gpp'); @@ -361,7 +361,7 @@ describe('minutemediaAdapter', function () { }); it('should send the gpp param if gppConsent is true in the bidRequest', function () { - const bidderRequestWithGPP = Object.assign({gppConsent: {gppString: 'test-consent-string', applicableSections: [7]}}, bidderRequest); + const bidderRequestWithGPP = Object.assign({ gppConsent: { gppString: 'test-consent-string', applicableSections: [7] } }, bidderRequest); const request = spec.buildRequests(bidRequests, bidderRequestWithGPP); expect(request.data.params).to.be.an('object'); expect(request.data.params).to.have.property('gpp', 'test-consent-string'); @@ -422,15 +422,15 @@ describe('minutemediaAdapter', function () { 'browsers': [ { 'brand': 'Chromium', - 'version': [ '106', '0', '5249', '119' ] + 'version': ['106', '0', '5249', '119'] }, { 'brand': 'Google Chrome', - 'version': [ '106', '0', '5249', '119' ] + 'version': ['106', '0', '5249', '119'] }, { 'brand': 'Not;A=Brand', - 'version': [ '99', '0', '0', '0' ] + 'version': ['99', '0', '0', '0'] } ], 'mobile': 0, @@ -444,20 +444,20 @@ describe('minutemediaAdapter', function () { 'sua': { 'platform': { 'brand': 'macOS', - 'version': [ '12', '4', '0' ] + 'version': ['12', '4', '0'] }, 'browsers': [ { 'brand': 'Chromium', - 'version': [ '106', '0', '5249', '119' ] + 'version': ['106', '0', '5249', '119'] }, { 'brand': 'Google Chrome', - 'version': [ '106', '0', '5249', '119' ] + 'version': ['106', '0', '5249', '119'] }, { 'brand': 'Not;A=Brand', - 'version': [ '99', '0', '0', '0' ] + 'version': ['99', '0', '0', '0'] } ], 'mobile': 0, diff --git a/test/spec/modules/missenaBidAdapter_spec.js b/test/spec/modules/missenaBidAdapter_spec.js index f4e09a981fe..67e64e264d2 100644 --- a/test/spec/modules/missenaBidAdapter_spec.js +++ b/test/spec/modules/missenaBidAdapter_spec.js @@ -4,7 +4,7 @@ import { BANNER } from '../../../src/mediaTypes.js'; import { config } from 'src/config.js'; import * as autoplay from 'libraries/autoplayDetection/autoplay.js'; import { getWinDimensions } from '../../../src/utils.js'; -import {getGlobal} from '../../../src/prebidGlobal.js'; +import { getGlobal } from '../../../src/prebidGlobal.js'; const REFERRER = 'https://referer'; const REFERRER2 = 'https://referer2'; @@ -367,5 +367,69 @@ describe('Missena Adapter', function () { expect(userSync[0].type).to.be.equal('iframe'); expect(userSync[0].url).to.be.equal(expectedUrl); }); + + it('sync frame url should contain gpp data when present', function () { + const gppConsent = { + gppString: 'DBACNYA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA', + applicableSections: [7, 8], + }; + const userSync = spec.getUserSyncs( + iframeEnabledOptions, + [], + {}, + undefined, + gppConsent, + ); + expect(userSync.length).to.be.equal(1); + expect(userSync[0].type).to.be.equal('iframe'); + const syncUrl = new URL(userSync[0].url); + expect(syncUrl.searchParams.get('gpp')).to.equal(gppConsent.gppString); + expect(syncUrl.searchParams.get('gpp_sid')).to.equal('7,8'); + }); + + it('sync frame url should not contain gpp data when gppConsent is undefined', function () { + const userSync = spec.getUserSyncs( + iframeEnabledOptions, + [], + {}, + undefined, + undefined, + ); + expect(userSync.length).to.be.equal(1); + expect(userSync[0].url).to.not.contain('gpp'); + }); + + it('sync frame url should not contain gpp data when gppString is empty', function () { + const userSync = spec.getUserSyncs( + iframeEnabledOptions, + [], + {}, + undefined, + { gppString: '', applicableSections: [7] }, + ); + expect(userSync.length).to.be.equal(1); + expect(userSync[0].url).to.not.contain('gpp'); + }); + + it('sync frame url should contain all consent params together', function () { + const gppConsent = { + gppString: 'DBACNYA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA', + applicableSections: [7], + }; + const userSync = spec.getUserSyncs( + iframeEnabledOptions, + [], + { gdprApplies: true, consentString }, + '1YNN', + gppConsent, + ); + expect(userSync.length).to.be.equal(1); + const syncUrl = new URL(userSync[0].url); + expect(syncUrl.searchParams.get('gdpr')).to.equal('1'); + expect(syncUrl.searchParams.get('gdpr_consent')).to.equal(consentString); + expect(syncUrl.searchParams.get('us_privacy')).to.equal('1YNN'); + expect(syncUrl.searchParams.get('gpp')).to.equal(gppConsent.gppString); + expect(syncUrl.searchParams.get('gpp_sid')).to.equal('7'); + }); }); }); diff --git a/test/spec/modules/mobianRtdProvider_spec.js b/test/spec/modules/mobianRtdProvider_spec.js index 0794e99151d..afb27163d20 100644 --- a/test/spec/modules/mobianRtdProvider_spec.js +++ b/test/spec/modules/mobianRtdProvider_spec.js @@ -8,6 +8,7 @@ import { CATEGORIES, EMOTIONS, GENRES, + MAX_CACHE_SIZE, RISK, SENTIMENT, THEMES, @@ -16,6 +17,7 @@ import { fetchContextData, getConfig, getContextData, + makeMemoizedFetch, makeContextDataToKeyValuesReducer, makeDataFromResponse, setTargeting, @@ -278,4 +280,178 @@ describe('Mobian RTD Submodule', function () { expect(keyValuesObject).to.deep.equal(mockKeyValues); }); }); + + describe('makeMemoizedFetch cache eviction', function () { + it('should evict the oldest entry when cache exceeds maxSize', async function () { + const maxSize = 2; + let fetchCount = 0; + ajaxStub = sinon.stub(ajax, 'ajaxBuilder').returns(function (url, callbacks) { + fetchCount++; + callbacks.success(mockResponse); + }); + + const memoizedFetch = makeMemoizedFetch(maxSize); + + await memoizedFetch(); + expect(fetchCount).to.equal(1); + + await memoizedFetch(); + expect(fetchCount).to.equal(1); + + const originalHref = window.location.href; + try { + history.pushState({}, '', '/page2'); + await memoizedFetch(); + expect(fetchCount).to.equal(2, 'new URL /page2 should trigger a fetch'); + + history.pushState({}, '', '/page3'); + await memoizedFetch(); + expect(fetchCount).to.equal(3, 'new URL /page3 should trigger a fetch and evict the original URL'); + + history.pushState({}, '', '/page2'); + await memoizedFetch(); + expect(fetchCount).to.equal(3, '/page2 should still be cached'); + + history.pushState({}, '', originalHref); + await memoizedFetch(); + expect(fetchCount).to.equal(4, 'original URL was evicted and requires a new fetch'); + } finally { + history.replaceState({}, '', originalHref); + } + }); + + it('should fall back to MAX_CACHE_SIZE when given an invalid maxSize', async function () { + let fetchCount = 0; + ajaxStub = sinon.stub(ajax, 'ajaxBuilder').returns(function (url, callbacks) { + fetchCount++; + callbacks.success(mockResponse); + }); + + const memoizedFetch = makeMemoizedFetch(NaN); + const originalHref = window.location.href; + + try { + for (let i = 0; i < MAX_CACHE_SIZE; i++) { + history.pushState({}, '', `/invalid-size-${i}`); + await memoizedFetch(); + } + expect(fetchCount).to.equal(MAX_CACHE_SIZE, 'should fetch once per unique URL'); + + history.pushState({}, '', '/invalid-size-5'); + await memoizedFetch(); + expect(fetchCount).to.equal(MAX_CACHE_SIZE, 'revisiting a cached URL should not fetch again'); + + history.pushState({}, '', '/invalid-size-overflow'); + await memoizedFetch(); + expect(fetchCount).to.equal(MAX_CACHE_SIZE + 1, 'new URL beyond limit should fetch and evict oldest (URL 0)'); + + history.pushState({}, '', '/invalid-size-0'); + await memoizedFetch(); + expect(fetchCount).to.equal(MAX_CACHE_SIZE + 2, 'URL 0 was evicted and requires a new fetch'); + + history.pushState({}, '', '/invalid-size-5'); + await memoizedFetch(); + expect(fetchCount).to.equal(MAX_CACHE_SIZE + 2, 'URL 5 should still be cached'); + } finally { + history.replaceState({}, '', originalHref); + } + }); + + it('should floor fractional maxSize to an integer', async function () { + let fetchCount = 0; + ajaxStub = sinon.stub(ajax, 'ajaxBuilder').returns(function (url, callbacks) { + fetchCount++; + callbacks.success(mockResponse); + }); + + const memoizedFetch = makeMemoizedFetch(1.9); + const originalHref = window.location.href; + + try { + await memoizedFetch(); + expect(fetchCount).to.equal(1); + + history.pushState({}, '', '/fractional-page2'); + await memoizedFetch(); + expect(fetchCount).to.equal(2); + + history.pushState({}, '', originalHref); + await memoizedFetch(); + expect(fetchCount).to.equal(3); + } finally { + history.replaceState({}, '', originalHref); + } + }); + + it('should share a single in-flight request for concurrent calls to the same URL', async function () { + let fetchCount = 0; + ajaxStub = sinon.stub(ajax, 'ajaxBuilder').returns(function (url, callbacks) { + fetchCount++; + setTimeout(() => callbacks.success(mockResponse), 10); + }); + + const memoizedFetch = makeMemoizedFetch(); + const [result1, result2, result3] = await Promise.all([ + memoizedFetch(), + memoizedFetch(), + memoizedFetch(), + ]); + + expect(fetchCount).to.equal(1); + expect(result1).to.deep.equal(mockContextData); + expect(result2).to.deep.equal(mockContextData); + expect(result3).to.deep.equal(mockContextData); + }); + + it('should delete failed cache entries so subsequent calls refetch after an error', async function () { + let fetchCount = 0; + ajaxStub = sinon.stub(ajax, 'ajaxBuilder').returns(function (url, callbacks) { + fetchCount++; + callbacks.error(new Error('network error')); + }); + + const memoizedFetch = makeMemoizedFetch(); + + const firstResult = await memoizedFetch(); + expect(fetchCount).to.equal(1); + expect(firstResult).to.deep.equal({}); + + const secondResult = await memoizedFetch(); + expect(fetchCount).to.equal(2, 'cache entry was cleared on error so a new fetch should occur'); + expect(secondResult).to.deep.equal({}); + }); + + it('should share a failing in-flight request across concurrent callers and allow a new fetch afterward', async function () { + let fetchCount = 0; + let shouldError = true; + + ajaxStub = sinon.stub(ajax, 'ajaxBuilder').returns(function (url, callbacks) { + fetchCount++; + if (shouldError) { + setTimeout(() => callbacks.error(new Error('server error')), 10); + } else { + setTimeout(() => callbacks.success(mockResponse), 10); + } + }); + + const memoizedFetch = makeMemoizedFetch(); + + const [result1, result2, result3] = await Promise.all([ + memoizedFetch(), + memoizedFetch(), + memoizedFetch(), + ]); + + expect(fetchCount).to.equal(1, 'concurrent callers should share a single in-flight request'); + expect(result1).to.deep.equal({}); + expect(result2).to.deep.equal({}); + expect(result3).to.deep.equal({}); + + shouldError = false; + const value = await memoizedFetch(); + + expect(fetchCount).to.equal(2, 'cache entry was cleared on error so a new fetch should occur'); + expect(value).to.deep.equal(mockContextData); + }); + }); }); diff --git a/test/spec/modules/mobilefuseBidAdapter_spec.js b/test/spec/modules/mobilefuseBidAdapter_spec.js index b234dbc404d..e6ccce07052 100644 --- a/test/spec/modules/mobilefuseBidAdapter_spec.js +++ b/test/spec/modules/mobilefuseBidAdapter_spec.js @@ -60,7 +60,7 @@ const serverResponse = { describe('mobilefuseBidAdapter', function () { it('should validate bids', function () { expect(spec.isBidRequestValid(bidRequest)).to.be.true; - expect(spec.isBidRequestValid({params: {}})).to.be.false; + expect(spec.isBidRequestValid({ params: {} })).to.be.false; }); it('should build a valid request payload', function () { @@ -139,7 +139,7 @@ describe('mobilefuseBidAdapter', function () { it('should return user syncs with proper query params when iframe sync is enabled', function () { const syncs = spec.getUserSyncs( - {iframeEnabled: true}, + { iframeEnabled: true }, [], null, bidderRequest.uspConsent, @@ -168,7 +168,7 @@ describe('mobilefuseBidAdapter', function () { }; const syncs = spec.getUserSyncs( - {iframeEnabled: false}, + { iframeEnabled: false }, [response], null, bidderRequest.uspConsent, diff --git a/test/spec/modules/mobkoiAnalyticsAdapter_spec.js b/test/spec/modules/mobkoiAnalyticsAdapter_spec.js index a3b785f4405..9b42b5ca3bb 100644 --- a/test/spec/modules/mobkoiAnalyticsAdapter_spec.js +++ b/test/spec/modules/mobkoiAnalyticsAdapter_spec.js @@ -71,7 +71,7 @@ const getBidderResponse = () => ({ const getMockEvents = () => { const sizes = [800, 300]; const timestamp = Date.now(); - const auctionOrBidError = {timestamp, error: 'error', bidderRequest: { bidderRequestId: requestId }} + const auctionOrBidError = { timestamp, error: 'error', bidderRequest: { bidderRequestId: requestId } } return { AUCTION_TIMEOUT: auctionOrBidError, @@ -452,7 +452,7 @@ describe('mobkoiAnalyticsAdapter', function () { it('should throw an error if the object type could not be determined', function () { expect(() => { - utils.determineObjType({dumbAttribute: 'bid'}) + utils.determineObjType({ dumbAttribute: 'bid' }) }).to.throw(); }); @@ -482,7 +482,7 @@ describe('mobkoiAnalyticsAdapter', function () { }) it('should throw an error if custom fields are provided and one of them is not a string', () => { - const customFields = {impid: 'bid-123', bidId: 123} + const customFields = { impid: 'bid-123', bidId: 123 } expect(() => { utils.mergePayloadAndCustomFields({}, customFields) }).to.throw(); diff --git a/test/spec/modules/msftBidAdapter_spec.js b/test/spec/modules/msftBidAdapter_spec.js index 9f7d757d897..2f5f1014594 100644 --- a/test/spec/modules/msftBidAdapter_spec.js +++ b/test/spec/modules/msftBidAdapter_spec.js @@ -210,7 +210,6 @@ describe('msftBidAdapter', function () { const bidderRequest = Object.assign({}, testBidderRequest, { bids: bidRequests }); - debugger;// eslint-disable-line no-debugger const request = spec.buildRequests(bidRequests, bidderRequest)[0]; expect(request.method).to.equal('POST'); const data = request.data; diff --git a/test/spec/modules/multibid_spec.js b/test/spec/modules/multibid_spec.js index c48e1d65263..5bd06b6d751 100644 --- a/test/spec/modules/multibid_spec.js +++ b/test/spec/modules/multibid_spec.js @@ -1,4 +1,4 @@ -import {expect} from 'chai'; +import { expect } from 'chai'; import { addBidResponseHook, adjustBidderRequestsHook, @@ -8,8 +8,8 @@ import { targetBidPoolHook, validateMultibid } from 'modules/multibid/index.js'; -import {config} from 'src/config.js'; -import {getHighestCpm} from '../../../src/utils/reducers.js'; +import { config } from 'src/config.js'; +import { getHighestCpm } from '../../../src/utils/reducers.js'; describe('multibid adapter', function () { const bidArray = [{ @@ -73,8 +73,8 @@ describe('multibid adapter', function () { 'bidderRequestId': '10e78266423c0e', 'bids': [{ 'bidder': 'bidderA', - 'params': {'placementId': 1234567}, - 'crumbs': {'pubcid': 'fb4cfc66-ff3d-4fda-bef8-3f2cb6fe9412'}, + 'params': { 'placementId': 1234567 }, + 'crumbs': { 'pubcid': 'fb4cfc66-ff3d-4fda-bef8-3f2cb6fe9412' }, 'mediaTypes': { 'banner': { 'sizes': [[300, 250]] @@ -97,8 +97,8 @@ describe('multibid adapter', function () { 'bidderRequestId': '10e78266423c0e', 'bids': [{ 'bidder': 'bidderB', - 'params': {'placementId': 1234567}, - 'crumbs': {'pubcid': 'fb4cfc66-ff3d-4fda-bef8-3f2cb6fe9412'}, + 'params': { 'placementId': 1234567 }, + 'crumbs': { 'pubcid': 'fb4cfc66-ff3d-4fda-bef8-3f2cb6fe9412' }, 'mediaTypes': { 'banner': { 'sizes': [[300, 250]] @@ -134,7 +134,7 @@ describe('multibid adapter', function () { }); it('does not modify bidderRequest when no multibid config exists', function () { - const bidRequests = [{...bidderRequests[0]}]; + const bidRequests = [{ ...bidderRequests[0] }]; adjustBidderRequestsHook(callbackFn, bidRequests); @@ -143,11 +143,11 @@ describe('multibid adapter', function () { }); it('does modify bidderRequest when multibid config exists', function () { - const bidRequests = [{...bidderRequests[0]}]; + const bidRequests = [{ ...bidderRequests[0] }]; - config.setConfig({multibid: [{bidder: 'bidderA', maxBids: 2}]}); + config.setConfig({ multibid: [{ bidder: 'bidderA', maxBids: 2 }] }); - adjustBidderRequestsHook(callbackFn, [{...bidderRequests[0]}]); + adjustBidderRequestsHook(callbackFn, [{ ...bidderRequests[0] }]); expect(result).to.not.equal(null); expect(result).to.not.deep.equal(bidRequests); @@ -155,11 +155,11 @@ describe('multibid adapter', function () { }); it('does modify bidderRequest when multibid config exists using bidders array', function () { - const bidRequests = [{...bidderRequests[0]}]; + const bidRequests = [{ ...bidderRequests[0] }]; - config.setConfig({multibid: [{bidders: ['bidderA'], maxBids: 2}]}); + config.setConfig({ multibid: [{ bidders: ['bidderA'], maxBids: 2 }] }); - adjustBidderRequestsHook(callbackFn, [{...bidderRequests[0]}]); + adjustBidderRequestsHook(callbackFn, [{ ...bidderRequests[0] }]); expect(result).to.not.equal(null); expect(result).to.not.deep.equal(bidRequests); @@ -167,11 +167,11 @@ describe('multibid adapter', function () { }); it('does only modifies bidderRequest when multibid config exists for bidder', function () { - const bidRequests = [{...bidderRequests[0]}, {...bidderRequests[1]}]; + const bidRequests = [{ ...bidderRequests[0] }, { ...bidderRequests[1] }]; - config.setConfig({multibid: [{bidder: 'bidderA', maxBids: 2}]}); + config.setConfig({ multibid: [{ bidder: 'bidderA', maxBids: 2 }] }); - adjustBidderRequestsHook(callbackFn, [{...bidderRequests[0]}, {...bidderRequests[1]}]); + adjustBidderRequestsHook(callbackFn, [{ ...bidderRequests[0] }, { ...bidderRequests[1] }]); expect(result).to.not.equal(null); expect(result[0]).to.not.deep.equal(bidRequests[0]); @@ -196,9 +196,9 @@ describe('multibid adapter', function () { it('adds original bids and does not modify', function () { const adUnitCode = 'test.div'; - const bids = [{...bidArray[0]}, {...bidArray[1]}]; + const bids = [{ ...bidArray[0] }, { ...bidArray[1] }]; - addBidResponseHook(callbackFn, adUnitCode, {...bids[0]}); + addBidResponseHook(callbackFn, adUnitCode, { ...bids[0] }); expect(result).to.not.equal(null); expect(result.adUnitCode).to.not.equal(null); @@ -208,7 +208,7 @@ describe('multibid adapter', function () { result = null; - addBidResponseHook(callbackFn, adUnitCode, {...bids[1]}); + addBidResponseHook(callbackFn, adUnitCode, { ...bids[1] }); expect(result).to.not.equal(null); expect(result.adUnitCode).to.not.equal(null); @@ -219,11 +219,11 @@ describe('multibid adapter', function () { it('modifies and adds both bids based on multibid configuration', function () { const adUnitCode = 'test.div'; - const bids = [{...bidArray[0]}, {...bidArray[1]}]; + const bids = [{ ...bidArray[0] }, { ...bidArray[1] }]; - config.setConfig({multibid: [{bidder: 'bidderA', maxBids: 2, targetBiddercodePrefix: 'bidA'}]}); + config.setConfig({ multibid: [{ bidder: 'bidderA', maxBids: 2, targetBiddercodePrefix: 'bidA' }] }); - addBidResponseHook(callbackFn, adUnitCode, {...bids[0]}); + addBidResponseHook(callbackFn, adUnitCode, { ...bids[0] }); bids[0].multibidPrefix = 'bidA'; bids[0].originalBidder = 'bidderA'; @@ -236,7 +236,7 @@ describe('multibid adapter', function () { result = null; - addBidResponseHook(callbackFn, adUnitCode, {...bids[1]}); + addBidResponseHook(callbackFn, adUnitCode, { ...bids[1] }); bids[1].multibidPrefix = 'bidA'; bids[1].originalBidder = 'bidderA'; @@ -255,7 +255,7 @@ describe('multibid adapter', function () { it('only modifies bids defined in the multibid configuration', function () { const adUnitCode = 'test.div'; - const bids = [{...bidArray[0]}, {...bidArray[1]}]; + const bids = [{ ...bidArray[0] }, { ...bidArray[1] }]; bids.push({ 'bidderCode': 'bidderB', @@ -265,9 +265,9 @@ describe('multibid adapter', function () { 'bidder': 'bidderB', }); - config.setConfig({multibid: [{bidder: 'bidderA', maxBids: 2, targetBiddercodePrefix: 'bidA'}]}); + config.setConfig({ multibid: [{ bidder: 'bidderA', maxBids: 2, targetBiddercodePrefix: 'bidA' }] }); - addBidResponseHook(callbackFn, adUnitCode, {...bids[0]}); + addBidResponseHook(callbackFn, adUnitCode, { ...bids[0] }); bids[0].multibidPrefix = 'bidA'; bids[0].originalBidder = 'bidderA'; @@ -280,7 +280,7 @@ describe('multibid adapter', function () { result = null; - addBidResponseHook(callbackFn, adUnitCode, {...bids[1]}); + addBidResponseHook(callbackFn, adUnitCode, { ...bids[1] }); bids[1].multibidPrefix = 'bidA'; bids[1].originalBidder = 'bidderA'; @@ -296,7 +296,7 @@ describe('multibid adapter', function () { result = null; - addBidResponseHook(callbackFn, adUnitCode, {...bids[2]}); + addBidResponseHook(callbackFn, adUnitCode, { ...bids[2] }); expect(result).to.not.equal(null); expect(result.adUnitCode).to.not.equal(null); @@ -307,7 +307,7 @@ describe('multibid adapter', function () { it('only modifies and returns bids under limit for a specific bidder in the multibid configuration', function () { const adUnitCode = 'test.div'; - let bids = [{...bidArray[0]}, {...bidArray[1]}]; + let bids = [{ ...bidArray[0] }, { ...bidArray[1] }]; bids.push({ 'bidderCode': 'bidderA', @@ -317,9 +317,9 @@ describe('multibid adapter', function () { 'bidder': 'bidderA', }); - config.setConfig({multibid: [{bidder: 'bidderA', maxBids: 2, targetBiddercodePrefix: 'bidA'}]}); + config.setConfig({ multibid: [{ bidder: 'bidderA', maxBids: 2, targetBiddercodePrefix: 'bidA' }] }); - addBidResponseHook(callbackFn, adUnitCode, {...bids[0]}); + addBidResponseHook(callbackFn, adUnitCode, { ...bids[0] }); bids[0].multibidPrefix = 'bidA'; bids[0].originalBidder = 'bidderA'; @@ -332,7 +332,7 @@ describe('multibid adapter', function () { result = null; - addBidResponseHook(callbackFn, adUnitCode, {...bids[1]}); + addBidResponseHook(callbackFn, adUnitCode, { ...bids[1] }); bids[1].multibidPrefix = 'bidA'; bids[1].originalBidder = 'bidderA'; @@ -348,14 +348,14 @@ describe('multibid adapter', function () { result = null; - addBidResponseHook(callbackFn, adUnitCode, {...bids[2]}); + addBidResponseHook(callbackFn, adUnitCode, { ...bids[2] }); expect(result).to.equal(null); }); it('if no prefix in multibid configuration, modifies and returns bids under limit without preifx property', function () { const adUnitCode = 'test.div'; - const bids = [{...bidArray[0]}, {...bidArray[1]}]; + const bids = [{ ...bidArray[0] }, { ...bidArray[1] }]; bids.push({ 'bidderCode': 'bidderA', @@ -365,9 +365,9 @@ describe('multibid adapter', function () { 'bidder': 'bidderA', }); - config.setConfig({multibid: [{bidder: 'bidderA', maxBids: 2}]}); + config.setConfig({ multibid: [{ bidder: 'bidderA', maxBids: 2 }] }); - addBidResponseHook(callbackFn, adUnitCode, {...bids[0]}); + addBidResponseHook(callbackFn, adUnitCode, { ...bids[0] }); bids[0].originalBidder = 'bidderA'; @@ -379,7 +379,7 @@ describe('multibid adapter', function () { result = null; - addBidResponseHook(callbackFn, adUnitCode, {...bids[1]}); + addBidResponseHook(callbackFn, adUnitCode, { ...bids[1] }); bids[1].originalBidder = 'bidderA'; bids[1].originalRequestId = '2e6j8s05r4363h'; @@ -393,14 +393,14 @@ describe('multibid adapter', function () { result = null; - addBidResponseHook(callbackFn, adUnitCode, {...bids[2]}); + addBidResponseHook(callbackFn, adUnitCode, { ...bids[2] }); expect(result).to.equal(null); }); it('does not include extra bids if cpm is less than floor value', function () { const adUnitCode = 'test.div'; - const bids = [{...bidArrayAlt[1]}, {...bidArrayAlt[0]}, {...bidArrayAlt[2]}, {...bidArrayAlt[3]}]; + const bids = [{ ...bidArrayAlt[1] }, { ...bidArrayAlt[0] }, { ...bidArrayAlt[2] }, { ...bidArrayAlt[3] }]; bids.map(bid => { bid.floorData = { @@ -424,9 +424,9 @@ describe('multibid adapter', function () { return bid; }); - config.setConfig({multibid: [{bidder: 'bidderA', maxBids: 2, targetBiddercodePrefix: 'bidA'}]}); + config.setConfig({ multibid: [{ bidder: 'bidderA', maxBids: 2, targetBiddercodePrefix: 'bidA' }] }); - addBidResponseHook(callbackFn, adUnitCode, {...bids[0]}); + addBidResponseHook(callbackFn, adUnitCode, { ...bids[0] }); bids[0].multibidPrefix = 'bidA'; bids[0].originalBidder = 'bidderA'; @@ -440,13 +440,13 @@ describe('multibid adapter', function () { result = null; - addBidResponseHook(callbackFn, adUnitCode, {...bids[1]}); + addBidResponseHook(callbackFn, adUnitCode, { ...bids[1] }); expect(result).to.equal(null); result = null; - addBidResponseHook(callbackFn, adUnitCode, {...bids[2]}); + addBidResponseHook(callbackFn, adUnitCode, { ...bids[2] }); expect(result).to.not.equal(null); expect(result.adUnitCode).to.not.equal(null); @@ -457,7 +457,7 @@ describe('multibid adapter', function () { result = null; - addBidResponseHook(callbackFn, adUnitCode, {...bids[3]}); + addBidResponseHook(callbackFn, adUnitCode, { ...bids[3] }); expect(result).to.not.equal(null); expect(result.adUnitCode).to.not.equal(null); @@ -469,7 +469,7 @@ describe('multibid adapter', function () { it('does include extra bids if cpm is not less than floor value', function () { const adUnitCode = 'test.div'; - const bids = [{...bidArrayAlt[1]}, {...bidArrayAlt[0]}]; + const bids = [{ ...bidArrayAlt[1] }, { ...bidArrayAlt[0] }]; bids.map(bid => { bid.floorData = { @@ -493,9 +493,9 @@ describe('multibid adapter', function () { return bid; }); - config.setConfig({multibid: [{bidder: 'bidderA', maxBids: 2, targetBiddercodePrefix: 'bidA'}]}); + config.setConfig({ multibid: [{ bidder: 'bidderA', maxBids: 2, targetBiddercodePrefix: 'bidA' }] }); - addBidResponseHook(callbackFn, adUnitCode, {...bids[0]}); + addBidResponseHook(callbackFn, adUnitCode, { ...bids[0] }); bids[0].multibidPrefix = 'bidA'; bids[0].originalBidder = 'bidderA'; @@ -509,7 +509,7 @@ describe('multibid adapter', function () { result = null; - addBidResponseHook(callbackFn, adUnitCode, {...bids[0]}); + addBidResponseHook(callbackFn, adUnitCode, { ...bids[0] }); bids[0].multibidPrefix = 'bidA'; bids[0].originalBidder = 'bidderA'; @@ -543,7 +543,7 @@ describe('multibid adapter', function () { }); it('it does not run filter on bidsReceived if no multibid configuration found', function () { - const bids = [{...bidArray[0]}, {...bidArray[1]}]; + const bids = [{ ...bidArray[0] }, { ...bidArray[1] }]; targetBidPoolHook(callbackFn, bids, getHighestCpm); expect(result).to.not.equal(null); @@ -557,9 +557,9 @@ describe('multibid adapter', function () { }); it('it does filter on bidsReceived if multibid configuration found with no prefix', function () { - const bids = [{...bidArray[0]}, {...bidArray[1]}]; + const bids = [{ ...bidArray[0] }, { ...bidArray[1] }]; - config.setConfig({multibid: [{bidder: 'bidderA', maxBids: 2}]}); + config.setConfig({ multibid: [{ bidder: 'bidderA', maxBids: 2 }] }); targetBidPoolHook(callbackFn, bids, getHighestCpm); bids.pop(); @@ -575,13 +575,13 @@ describe('multibid adapter', function () { }); it('it sorts and creates dynamic alias on bidsReceived if multibid configuration found with prefix', function () { - const modifiedBids = [{...bidArray[1]}, {...bidArray[0]}].map(bid => { - addBidResponseHook(bidResponseCallback, 'test.div', {...bid}); + const modifiedBids = [{ ...bidArray[1] }, { ...bidArray[0] }].map(bid => { + addBidResponseHook(bidResponseCallback, 'test.div', { ...bid }); return bidResult; }); - config.setConfig({multibid: [{bidder: 'bidderA', maxBids: 2, targetBiddercodePrefix: 'bidA'}]}); + config.setConfig({ multibid: [{ bidder: 'bidderA', maxBids: 2, targetBiddercodePrefix: 'bidA' }] }); targetBidPoolHook(callbackFn, modifiedBids, getHighestCpm); @@ -600,13 +600,13 @@ describe('multibid adapter', function () { }); it('it sorts by cpm treating dynamic alias as unique bid when no bid limit defined', function () { - const modifiedBids = [{...bidArrayAlt[0]}, {...bidArrayAlt[2]}, {...bidArrayAlt[3]}, {...bidArrayAlt[1]}].map(bid => { - addBidResponseHook(bidResponseCallback, 'test.div', {...bid}); + const modifiedBids = [{ ...bidArrayAlt[0] }, { ...bidArrayAlt[2] }, { ...bidArrayAlt[3] }, { ...bidArrayAlt[1] }].map(bid => { + addBidResponseHook(bidResponseCallback, 'test.div', { ...bid }); return bidResult; }); - config.setConfig({multibid: [{bidder: 'bidderA', maxBids: 2, targetBiddercodePrefix: 'bidA'}]}); + config.setConfig({ multibid: [{ bidder: 'bidderA', maxBids: 2, targetBiddercodePrefix: 'bidA' }] }); targetBidPoolHook(callbackFn, modifiedBids, getHighestCpm); @@ -633,13 +633,13 @@ describe('multibid adapter', function () { }); it('it should filter out dynamic bid when bid limit is less than unique bid pool', function () { - const modifiedBids = [{...bidArrayAlt[0]}, {...bidArrayAlt[2]}, {...bidArrayAlt[3]}, {...bidArrayAlt[1]}].map(bid => { - addBidResponseHook(bidResponseCallback, 'test.div', {...bid}); + const modifiedBids = [{ ...bidArrayAlt[0] }, { ...bidArrayAlt[2] }, { ...bidArrayAlt[3] }, { ...bidArrayAlt[1] }].map(bid => { + addBidResponseHook(bidResponseCallback, 'test.div', { ...bid }); return bidResult; }); - config.setConfig({ multibid: [{bidder: 'bidderA', maxBids: 2, targetBiddercodePrefix: 'bidA'}] }); + config.setConfig({ multibid: [{ bidder: 'bidderA', maxBids: 2, targetBiddercodePrefix: 'bidA' }] }); targetBidPoolHook(callbackFn, modifiedBids, getHighestCpm, 3); @@ -657,15 +657,15 @@ describe('multibid adapter', function () { }); it('it should collect all bids from auction and bid cache then sort and filter', function () { - config.setConfig({ multibid: [{bidder: 'bidderA', maxBids: 2, targetBiddercodePrefix: 'bidA'}] }); + config.setConfig({ multibid: [{ bidder: 'bidderA', maxBids: 2, targetBiddercodePrefix: 'bidA' }] }); - const modifiedBids = [{...bidArrayAlt[0]}, {...bidArrayAlt[2]}, {...bidArrayAlt[3]}, {...bidArrayAlt[1]}].map(bid => { - addBidResponseHook(bidResponseCallback, 'test.div', {...bid}); + const modifiedBids = [{ ...bidArrayAlt[0] }, { ...bidArrayAlt[2] }, { ...bidArrayAlt[3] }, { ...bidArrayAlt[1] }].map(bid => { + addBidResponseHook(bidResponseCallback, 'test.div', { ...bid }); return bidResult; }); - const bidPool = [].concat.apply(modifiedBids, [{...bidCacheArray[0]}, {...bidCacheArray[1]}]); + const bidPool = [].concat.apply(modifiedBids, [{ ...bidCacheArray[0] }, { ...bidCacheArray[1] }]); expect(bidPool.length).to.equal(6); @@ -688,53 +688,53 @@ describe('multibid adapter', function () { describe('validate multibid', function () { it('should fail validation for missing bidder name in entry', function () { - const conf = [{maxBids: 1}]; + const conf = [{ maxBids: 1 }]; const result = validateMultibid(conf); expect(result).to.equal(false); }); it('should pass validation on all multibid entries', function () { - const conf = [{bidder: 'bidderA', maxBids: 1}, {bidder: 'bidderB', maxBids: 2}]; + const conf = [{ bidder: 'bidderA', maxBids: 1 }, { bidder: 'bidderB', maxBids: 2 }]; const result = validateMultibid(conf); expect(result).to.equal(true); }); it('should fail validation for maxbids less than 1 in entry', function () { - const conf = [{bidder: 'bidderA', maxBids: 0}, {bidder: 'bidderB', maxBids: 2}]; + const conf = [{ bidder: 'bidderA', maxBids: 0 }, { bidder: 'bidderB', maxBids: 2 }]; const result = validateMultibid(conf); expect(result).to.equal(false); }); it('should fail validation for maxbids greater than 9 in entry', function () { - const conf = [{bidder: 'bidderA', maxBids: 10}, {bidder: 'bidderB', maxBids: 2}]; + const conf = [{ bidder: 'bidderA', maxBids: 10 }, { bidder: 'bidderB', maxBids: 2 }]; const result = validateMultibid(conf); expect(result).to.equal(false); }); it('should add multbid entries to global config', function () { - config.setConfig({multibid: [{bidder: 'bidderA', maxBids: 1}]}); + config.setConfig({ multibid: [{ bidder: 'bidderA', maxBids: 1 }] }); const conf = config.getConfig('multibid'); - expect(conf).to.deep.equal([{bidder: 'bidderA', maxBids: 1}]); + expect(conf).to.deep.equal([{ bidder: 'bidderA', maxBids: 1 }]); }); it('should modify multbid entries and add to global config', function () { - config.setConfig({multibid: [{bidder: 'bidderA', maxBids: 0}, {bidder: 'bidderB', maxBids: 15}]}); + config.setConfig({ multibid: [{ bidder: 'bidderA', maxBids: 0 }, { bidder: 'bidderB', maxBids: 15 }] }); const conf = config.getConfig('multibid'); - expect(conf).to.deep.equal([{bidder: 'bidderA', maxBids: 1}, {bidder: 'bidderB', maxBids: 9}]); + expect(conf).to.deep.equal([{ bidder: 'bidderA', maxBids: 1 }, { bidder: 'bidderB', maxBids: 9 }]); }); it('should filter multbid entry and add modified to global config', function () { - config.setConfig({multibid: [{bidder: 'bidderA', maxBids: 0}, {maxBids: 15}]}); + config.setConfig({ multibid: [{ bidder: 'bidderA', maxBids: 0 }, { maxBids: 15 }] }); const conf = config.getConfig('multibid'); expect(conf.length).to.equal(1); - expect(conf).to.deep.equal([{bidder: 'bidderA', maxBids: 1}]); + expect(conf).to.deep.equal([{ bidder: 'bidderA', maxBids: 1 }]); }); }); diff --git a/test/spec/modules/mwOpenLinkIdSystem_spec.js b/test/spec/modules/mwOpenLinkIdSystem_spec.js index fb082b8cd16..55bea0ea24f 100644 --- a/test/spec/modules/mwOpenLinkIdSystem_spec.js +++ b/test/spec/modules/mwOpenLinkIdSystem_spec.js @@ -13,8 +13,8 @@ describe('mwOpenLinkId module', function () { }); it('getId() should return a MediaWallah openLink Id when the MediaWallah openLink first party cookie exists', function () { - writeCookie({eid: 'XX-YY-ZZ-123'}); + writeCookie({ eid: 'XX-YY-ZZ-123' }); const id = mwOpenLinkIdSubModule.getId(P_CONFIG_MOCK); - expect(id).to.be.deep.equal({id: {eid: 'XX-YY-ZZ-123'}}); + expect(id).to.be.deep.equal({ id: { eid: 'XX-YY-ZZ-123' } }); }); }); diff --git a/test/spec/modules/my6senseBidAdapter_spec.js b/test/spec/modules/my6senseBidAdapter_spec.js index 9493b104680..be075537de3 100644 --- a/test/spec/modules/my6senseBidAdapter_spec.js +++ b/test/spec/modules/my6senseBidAdapter_spec.js @@ -105,7 +105,7 @@ describe('My6sense Bid adapter test', function () { }); describe('test bid responses', function () { it('success 1', function () { - var bids = spec.interpretResponse(serverResponses[0], {'bidRequest': bidRequests[0]}); + 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); diff --git a/test/spec/modules/mycodemediaBidAdapter_spec.js b/test/spec/modules/mycodemediaBidAdapter_spec.js index 7cc9b412ea0..fbf40f4b403 100644 --- a/test/spec/modules/mycodemediaBidAdapter_spec.js +++ b/test/spec/modules/mycodemediaBidAdapter_spec.js @@ -478,7 +478,7 @@ describe('MyCodeMediaBidAdapter', function () { const syncData = spec.getUserSyncs({}, {}, { consentString: 'ALL', gdprApplies: true, - }, {}); + }, undefined); expect(syncData).to.be.an('array').which.is.not.empty; expect(syncData[0]).to.be.an('object') expect(syncData[0].type).to.be.a('string') @@ -487,9 +487,7 @@ describe('MyCodeMediaBidAdapter', function () { expect(syncData[0].url).to.equal('https://usersync.mycodemedia.com/image?pbjs=1&gdpr=1&gdpr_consent=ALL&coppa=0') }); it('Should return array of objects with proper sync config , include CCPA', function() { - const syncData = spec.getUserSyncs({}, {}, {}, { - consentString: '1---' - }); + const syncData = spec.getUserSyncs({}, {}, {}, '1---'); expect(syncData).to.be.an('array').which.is.not.empty; expect(syncData[0]).to.be.an('object') expect(syncData[0].type).to.be.a('string') @@ -498,7 +496,7 @@ describe('MyCodeMediaBidAdapter', function () { expect(syncData[0].url).to.equal('https://usersync.mycodemedia.com/image?pbjs=1&ccpa_consent=1---&coppa=0') }); it('Should return array of objects with proper sync config , include GPP', function() { - const syncData = spec.getUserSyncs({}, {}, {}, {}, { + const syncData = spec.getUserSyncs({}, {}, {}, undefined, { gppString: 'abc123', applicableSections: [8] }); diff --git a/test/spec/modules/mygaruIdSystem_spec.js b/test/spec/modules/mygaruIdSystem_spec.js index 44075713a85..46fb811cc30 100644 --- a/test/spec/modules/mygaruIdSystem_spec.js +++ b/test/spec/modules/mygaruIdSystem_spec.js @@ -21,7 +21,7 @@ describe('MygaruID module', function () { await promise; expect(callBackSpy.calledOnce).to.be.true; - expect(callBackSpy.calledWith({mygaruId: '123'})).to.be.true; + expect(callBackSpy.calledWith({ mygaruId: '123' })).to.be.true; }); it('should not fail on error', async () => { const callBackSpy = sinon.spy(); @@ -42,7 +42,7 @@ describe('MygaruID module', function () { await promise; expect(callBackSpy.calledOnce).to.be.true; - expect(callBackSpy.calledWith({mygaruId: undefined})).to.be.true; + expect(callBackSpy.calledWith({ mygaruId: undefined })).to.be.true; }); it('should not modify while decoding', () => { diff --git a/test/spec/modules/netIdSystem_spec.js b/test/spec/modules/netIdSystem_spec.js index bbf59c39f32..2cd68677627 100644 --- a/test/spec/modules/netIdSystem_spec.js +++ b/test/spec/modules/netIdSystem_spec.js @@ -1,7 +1,7 @@ -import {attachIdSystem} from '../../../modules/userId/index.js'; -import {netIdSubmodule} from '../../../modules/netIdSystem.js'; -import {createEidsArray} from '../../../modules/userId/eids.js'; -import {expect} from 'chai/index.mjs'; +import { attachIdSystem } from '../../../modules/userId/index.js'; +import { netIdSubmodule } from '../../../modules/netIdSystem.js'; +import { createEidsArray } from '../../../modules/userId/eids.js'; +import { expect } from 'chai/index.mjs'; describe('Net ID', () => { describe('eid', () => { @@ -16,7 +16,7 @@ describe('Net ID', () => { expect(newEids.length).to.equal(1); expect(newEids[0]).to.deep.equal({ source: 'netid.de', - uids: [{id: 'some-random-id-value', atype: 1}] + uids: [{ id: 'some-random-id-value', atype: 1 }] }); }); }); diff --git a/test/spec/modules/neuwoRtdProvider_spec.js b/test/spec/modules/neuwoRtdProvider_spec.js index 4b2951afe33..bb64b283535 100644 --- a/test/spec/modules/neuwoRtdProvider_spec.js +++ b/test/spec/modules/neuwoRtdProvider_spec.js @@ -1,10 +1,12 @@ import * as neuwo from "modules/neuwoRtdProvider"; +import * as refererDetection from "src/refererDetection.js"; import { server } from "test/mocks/xhr.js"; -const NEUWO_API_URL = "https://api.url.neuwo.ai/edge/GetAiTopics"; +const NEUWO_API_URL = "https://edge.neuwo.ai/api/aitopics/edge/v1/iab"; const NEUWO_API_TOKEN = "token"; -const IAB_CONTENT_TAXONOMY_VERSION = "3.0"; +const IAB_CONTENT_TAXONOMY_VERSION = "2.2"; +// API config const config = () => ({ params: { neuwoApiUrl: NEUWO_API_URL, @@ -13,7 +15,115 @@ const config = () => ({ }, }); +/** + * API Response Mock + * Returns new format with segtax-based structure + * Field names: id, name, relevance (lowercase) + * Structure: { "6": { "1": [...], "2": [...] }, "4": { "3": [...] } } + */ function getNeuwoApiResponse() { + return { + 7: { + 1: [ + { + id: "80DV8O", + }, + { + id: "52", + }, + { + id: "432", + }, + ], + 2: [ + { + id: "90", + }, + ], + 3: [ + { + id: "106", + }, + ], + }, + 1: { + 1: [ + { + id: "IAB12", + }, + ], + }, + 6: { + 1: [ + { + id: "52", + }, + ], + 2: [ + { + id: "90", + }, + { + id: "434", + }, + ], + 3: [ + { + id: "106", + }, + ], + }, + 4: { + 3: [ + { + id: "49", + }, + { + id: "780", + }, + ], + 4: [ + { + id: "431", + }, + { + id: "196", + }, + { + id: "197", + }, + ], + 5: [ + { + id: "98", + }, + ], + }, + }; +} + +// ============================================================================ +// V1 API Constants and Mocks +// ============================================================================ + +const NEUWO_API_URL_V1 = "https://api.url.neuwo.ai/edge/GetAiTopics"; +const IAB_CONTENT_TAXONOMY_VERSION_V1 = "3.0"; + +// Legacy V1 API config (for backward compatibility tests) +const configV1 = () => ({ + params: { + neuwoApiUrl: NEUWO_API_URL_V1, + neuwoApiToken: NEUWO_API_TOKEN, + iabContentTaxonomyVersion: IAB_CONTENT_TAXONOMY_VERSION_V1, + }, +}); + +/** + * V1 API Response Mock + * Returns legacy format with marketing_categories structure + * Field names: ID, label, relevance (capital letters) + */ +function getNeuwoApiResponseV1() { return { brand_safety: { BS_score: "1.0", @@ -65,8 +175,6 @@ function getNeuwoApiResponse() { ], }; } -const CONTENT_TIERS = ["iab_tier_1", "iab_tier_2", "iab_tier_3"]; -const AUDIENCE_TIERS = ["iab_audience_tier_3", "iab_audience_tier_4", "iab_audience_tier_5"]; /** * Object generator, like above, written using alternative techniques @@ -126,38 +234,20 @@ describe("neuwoRtdModule", function () { }); describe("buildIabData", function () { - it("should return an empty segment array when no matching tiers are found", function () { - const marketingCategories = getNeuwoApiResponse().marketing_categories; - const tiers = ["non_existent_tier"]; - const segtax = 0; - const result = neuwo.buildIabData(marketingCategories, tiers, segtax); - const expected = { - name: neuwo.DATA_PROVIDER, - segment: [], - ext: { - segtax, - }, - }; - expect(result, "should produce a valid object with an empty segment array").to.deep.equal( - expected - ); - }); - it("should correctly build the data object for content tiers", function () { - const marketingCategories = getNeuwoApiResponse().marketing_categories; + // format with tier keys "1", "2", "3" + const tierData = { + "1": [{ id: "274", name: "Home & Garden", relevance: "0.47" }], + "2": [{ id: "216", name: "Cooking", relevance: "0.41" }], + "3": [], + }; const segtax = 0; - const result = neuwo.buildIabData(marketingCategories, CONTENT_TIERS, segtax); + const result = neuwo.buildIabData(tierData, segtax); const expected = { name: neuwo.DATA_PROVIDER, segment: [ - { - id: "274", - name: "Home & Garden", - }, - { - id: "216", - name: "Cooking", - }, + { id: "274" }, + { id: "216" }, ], ext: { segtax, @@ -169,24 +259,20 @@ describe("neuwoRtdModule", function () { }); it("should correctly build the data object for audience tiers", function () { - const marketingCategories = getNeuwoApiResponse().marketing_categories; + // format with tier keys "3", "4", "5" for audience + const tierData = { + "3": [{ id: "49", name: "Demographic | Gender | Female |", relevance: "0.9923" }], + "4": [{ id: "127", name: "Demographic | Household Data | 1 Child |", relevance: "0.9673" }], + "5": [{ id: "98", name: "Demographic | Household Data | Parents with Children |", relevance: "0.9066" }], + }; const segtax = 0; - const result = neuwo.buildIabData(marketingCategories, AUDIENCE_TIERS, segtax); + const result = neuwo.buildIabData(tierData, segtax); const expected = { name: neuwo.DATA_PROVIDER, segment: [ - { - id: "49", - name: "Demographic | Gender | Female |", - }, - { - id: "127", - name: "Demographic | Household Data | 1 Child |", - }, - { - id: "98", - name: "Demographic | Household Data | Parents with Children |", - }, + { id: "49" }, + { id: "127" }, + { id: "98" }, ], ext: { segtax, @@ -197,7 +283,7 @@ describe("neuwoRtdModule", function () { ); }); - it("should return an empty segment array when marketingCategories is null or undefined", function () { + it("should return an empty segment array when tierData is null or undefined", function () { const segtax = 4; const expected = { name: neuwo.DATA_PROVIDER, @@ -207,19 +293,19 @@ describe("neuwoRtdModule", function () { }, }; expect( - neuwo.buildIabData(null, CONTENT_TIERS, segtax), - "should handle null marketingCategories gracefully" + neuwo.buildIabData(null, segtax), + "should handle null tierData gracefully" ).to.deep.equal(expected); expect( - neuwo.buildIabData(undefined, CONTENT_TIERS, segtax), - "should handle undefined marketingCategories gracefully" + neuwo.buildIabData(undefined, segtax), + "should handle undefined tierData gracefully" ).to.deep.equal(expected); }); - it("should return an empty segment array when marketingCategories is empty", function () { - const marketingCategories = {}; + it("should return an empty segment array when tierData is empty", function () { + const tierData = {}; const segtax = 4; - const result = neuwo.buildIabData(marketingCategories, CONTENT_TIERS, segtax); + const result = neuwo.buildIabData(tierData, segtax); const expected = { name: neuwo.DATA_PROVIDER, segment: [], @@ -227,23 +313,23 @@ describe("neuwoRtdModule", function () { segtax, }, }; - expect(result, "should handle an empty marketingCategories object").to.deep.equal(expected); + expect(result, "should handle an empty tierData object").to.deep.equal(expected); }); - it("should gracefully handle if a marketing_categories key contains a non-array value", function () { - const marketingCategories = getNeuwoApiResponse().marketing_categories; - // Overwrite iab_tier_1 to be an object instead of an array - marketingCategories.iab_tier_1 = { ID: "274", label: "Home & Garden" }; + it("should gracefully handle if a tier key contains a non-array value", function () { + const tierData = { + "1": { id: "274", name: "Home & Garden" }, // Not an array + "2": [{ id: "216", name: "Cooking", relevance: "0.41" }], + }; const segtax = 4; - const result = neuwo.buildIabData(marketingCategories, CONTENT_TIERS, segtax); + const result = neuwo.buildIabData(tierData, segtax); const expected = { name: neuwo.DATA_PROVIDER, - // The segment should only contain data from the valid iab_tier_2 + // The segment should only contain data from the valid tier "2" segment: [ { id: "216", - name: "Cooking", }, ], ext: { @@ -257,30 +343,29 @@ describe("neuwoRtdModule", function () { }); it("should ignore malformed objects within a tier array", function () { - const marketingCategories = getNeuwoApiResponse().marketing_categories; - // Overwrite iab_tier_1 with various malformed objects - marketingCategories.iab_tier_1 = [ - { ID: "274", label: "Valid Object" }, - { ID: "999" }, // Missing 'label' property - { label: "Another Label" }, // Missing 'ID' property - null, // A null value - "just-a-string", // A string primitive - {}, // An empty object - ]; + // Tier "1" with various malformed objects + const tierData = { + "1": [ + { id: "274", name: "Valid Object" }, + { name: "Another Label" }, // Missing 'id' property + null, // A null value + "just-a-string", // A string primitive + {}, // An empty object + ], + "2": [{ id: "216", name: "Cooking", relevance: "0.41" }], + }; const segtax = 4; - const result = neuwo.buildIabData(marketingCategories, CONTENT_TIERS, segtax); + const result = neuwo.buildIabData(tierData, segtax); const expected = { name: neuwo.DATA_PROVIDER, - // The segment should contain the one valid object from iab_tier_1 and the data from iab_tier_2 + // The segment should contain the one valid object from tier "1" and the data from tier "2" segment: [ { id: "274", - name: "Valid Object", }, { id: "216", - name: "Cooking", }, ], ext: { @@ -293,7 +378,7 @@ describe("neuwoRtdModule", function () { ); }); - it("should return an empty segment array if the entire marketingCategories object is not a valid object", function () { + it("should return an empty segment array if the entire tierData is not a valid object", function () { const segtax = 4; const expected = { name: neuwo.DATA_PROVIDER, @@ -301,1000 +386,3636 @@ describe("neuwoRtdModule", function () { ext: { segtax }, }; // Test with a string - const resultString = neuwo.buildIabData("incorrect format", CONTENT_TIERS, segtax); - expect(resultString, "should handle non-object marketingCategories input").to.deep.equal( + const resultString = neuwo.buildIabData("incorrect format", segtax); + expect(resultString, "should handle non-object tierData input").to.deep.equal( expected ); }); }); - describe("injectOrtbData", function () { - it("should correctly mutate the request bids config object with new data", function () { - const reqBidsConfigObj = { ortb2Fragments: { global: {} } }; - neuwo.injectOrtbData(reqBidsConfigObj, "c.d.e.f", { g: "h" }); - expect( - reqBidsConfigObj.ortb2Fragments.global.c.d.e.f.g, - "should deeply merge the new data into the target object" - ).to.equal("h"); + describe("buildFilterQueryParams", function () { + it("should return empty array when no filters provided", function () { + const result = neuwo.buildFilterQueryParams(null, 6); + expect(result, "should return empty array for null filters").to.deep.equal([]); }); - }); - - describe("getBidRequestData", function () { - describe("when using IAB Content Taxonomy 3.0", function () { - it("should correctly structure the bids object after a successful API response", function () { - const apiResponse = getNeuwoApiResponse(); - const bidsConfig = bidsConfiglike(); - const conf = config(); - // control xhr api request target for testing - conf.params.websiteToAnalyseUrl = - "https://publisher.works/article.php?get=horrible_url_for_testing&id=5"; - neuwo.getBidRequestData(bidsConfig, () => {}, conf, "consent data"); - const request = server.requests[0]; + it("should return empty array when filters parameter is undefined", function () { + const result = neuwo.buildFilterQueryParams(undefined, 6); + expect(result, "should return empty array for undefined filters").to.deep.equal([]); + }); - expect(request.url, "The request URL should be a string").to.be.a("string"); - expect(request.url, "The request URL should include the public API token").to.include( - conf.params.neuwoApiToken - ); - expect(request.url, "The request URL should include the encoded website URL").to.include( - encodeURIComponent(conf.params.websiteToAnalyseUrl) - ); + it("should return empty array when filters is empty object", function () { + const result = neuwo.buildFilterQueryParams({}, 6); + expect(result, "should return empty array for empty filters").to.deep.equal([]); + }); - request.respond( - 200, - { "Content-Type": "application/json; encoding=UTF-8" }, - JSON.stringify(apiResponse) - ); + it("should convert ContentTier1 filter correctly", function () { + const filters = { + ContentTier1: { limit: 3, threshold: 0.5 } + }; + const contentSegtax = 6; + const result = neuwo.buildFilterQueryParams(filters, contentSegtax, false); - const contentData = bidsConfig.ortb2Fragments.global.site.content.data[0]; - expect(contentData.name, "The data provider name should be correctly set").to.equal( - neuwo.DATA_PROVIDER - ); - expect( - contentData.ext.segtax, - "The segtax value should correspond to IAB Content Taxonomy 3.0" - ).to.equal(7); - expect( - contentData.segment[0].id, - "The first segment ID should match the API response" - ).to.equal(apiResponse.marketing_categories.iab_tier_1[0].ID); - expect( - contentData.segment[1].name, - "The second segment name should match the API response" - ).to.equal(apiResponse.marketing_categories.iab_tier_2[0].label); - }); + expect(result).to.include("filter_6_1_limit=3"); + expect(result).to.include("filter_6_1_threshold=0.5"); + expect(result).to.have.lengthOf(2); }); - describe("when using IAB Content Taxonomy 2.2", function () { - it("should correctly structure the bids object after a successful API response", function () { - const apiResponse = getNeuwoApiResponse(); - const bidsConfig = bidsConfiglike(); - const conf = config(); - conf.params.iabContentTaxonomyVersion = "2.2"; - // control xhr api request target for testing - conf.params.websiteToAnalyseUrl = - "https://publisher.works/article.php?get=horrible_url_for_testing&id=5"; + it("should convert ContentTier2 filter correctly", function () { + const filters = { + ContentTier2: { limit: 5, threshold: 0.6 } + }; + const contentSegtax = 7; + const result = neuwo.buildFilterQueryParams(filters, contentSegtax, false); - neuwo.getBidRequestData(bidsConfig, () => {}, conf, "consent data"); - const request = server.requests[0]; + expect(result).to.include("filter_7_2_limit=5"); + expect(result).to.include("filter_7_2_threshold=0.6"); + expect(result).to.have.lengthOf(2); + }); - expect(request.url, "The request URL should be a string").to.be.a("string"); - expect(request.url, "The request URL should include the public API token").to.include( - conf.params.neuwoApiToken - ); - expect(request.url, "The request URL should include the encoded website URL").to.include( - encodeURIComponent(conf.params.websiteToAnalyseUrl) - ); + it("should convert ContentTier3 filter correctly", function () { + const filters = { + ContentTier3: { limit: 4, threshold: 0.8 } + }; + const contentSegtax = 6; + const result = neuwo.buildFilterQueryParams(filters, contentSegtax, false); - request.respond( - 200, - { "Content-Type": "application/json; encoding=UTF-8" }, - JSON.stringify(apiResponse) - ); - const contentData = bidsConfig.ortb2Fragments.global.site.content.data[0]; - expect(contentData.name, "The data provider name should be correctly set").to.equal( - neuwo.DATA_PROVIDER - ); - expect( - contentData.ext.segtax, - "The segtax value should correspond to IAB Content Taxonomy 2.2" - ).to.equal(6); - expect( - contentData.segment[0].id, - "The first segment ID should match the API response" - ).to.equal(apiResponse.marketing_categories.iab_tier_1[0].ID); - expect( - contentData.segment[1].name, - "The second segment name should match the API response" - ).to.equal(apiResponse.marketing_categories.iab_tier_2[0].label); - }); + expect(result).to.include("filter_6_3_limit=4"); + expect(result).to.include("filter_6_3_threshold=0.8"); + expect(result).to.have.lengthOf(2); }); - describe("when using the default IAB Content Taxonomy", function () { - it("should correctly structure the bids object after a successful API response", function () { - const apiResponse = getNeuwoApiResponse(); - const bidsConfig = bidsConfiglike(); - const conf = config(); - conf.params.iabContentTaxonomyVersion = undefined; - // control xhr api request target for testing - conf.params.websiteToAnalyseUrl = - "https://publisher.works/article.php?get=horrible_url_for_testing&id=5"; + it("should convert AudienceTier3 filter correctly", function () { + const filters = { + AudienceTier3: { limit: 2, threshold: 0.9 } + }; + const contentSegtax = 6; + const result = neuwo.buildFilterQueryParams(filters, contentSegtax, false); - neuwo.getBidRequestData(bidsConfig, () => {}, conf, "consent data"); - const request = server.requests[0]; + expect(result).to.include("filter_4_3_limit=2"); + expect(result).to.include("filter_4_3_threshold=0.9"); + expect(result).to.have.lengthOf(2); + }); - expect(request.url, "The request URL should be a string").to.be.a("string"); - expect(request.url, "The request URL should include the public API token").to.include( - conf.params.neuwoApiToken - ); - expect(request.url, "The request URL should include the encoded website URL").to.include( - encodeURIComponent(conf.params.websiteToAnalyseUrl) - ); + it("should convert AudienceTier4 filter correctly", function () { + const filters = { + AudienceTier4: { limit: 10, threshold: 0.85 } + }; + const contentSegtax = 6; + const result = neuwo.buildFilterQueryParams(filters, contentSegtax, false); - request.respond( - 200, - { "Content-Type": "application/json; encoding=UTF-8" }, - JSON.stringify(apiResponse) - ); - const contentData = bidsConfig.ortb2Fragments.global.site.content.data[0]; - expect(contentData.name, "The data provider name should be correctly set").to.equal( - neuwo.DATA_PROVIDER - ); - expect( - contentData.ext.segtax, - "The segtax value should default to IAB Content Taxonomy 3.0" - ).to.equal(7); - expect( - contentData.segment[0].id, - "The first segment ID should match the API response" - ).to.equal(apiResponse.marketing_categories.iab_tier_1[0].ID); - expect( - contentData.segment[1].name, - "The second segment name should match the API response" - ).to.equal(apiResponse.marketing_categories.iab_tier_2[0].label); - }); + expect(result).to.include("filter_4_4_limit=10"); + expect(result).to.include("filter_4_4_threshold=0.85"); + expect(result).to.have.lengthOf(2); }); - describe("when using IAB Audience Taxonomy 1.1", function () { - it("should correctly structure the bids object after a successful API response", function () { - const apiResponse = getNeuwoApiResponse(); - const bidsConfig = bidsConfiglike(); - const conf = config(); - // control xhr api request target for testing - conf.params.websiteToAnalyseUrl = - "https://publisher.works/article.php?get=horrible_url_for_testing&id=5"; + it("should convert AudienceTier5 filter correctly", function () { + const filters = { + AudienceTier5: { limit: 7, threshold: 0.95 } + }; + const contentSegtax = 6; + const result = neuwo.buildFilterQueryParams(filters, contentSegtax, false); - neuwo.getBidRequestData(bidsConfig, () => {}, conf, "consent data"); - const request = server.requests[0]; + expect(result).to.include("filter_4_5_limit=7"); + expect(result).to.include("filter_4_5_threshold=0.95"); + expect(result).to.have.lengthOf(2); + }); - expect(request.url, "The request URL should be a string").to.be.a("string"); - expect(request.url, "The request URL should include the public API token").to.include( - conf.params.neuwoApiToken - ); - expect(request.url, "The request URL should include the encoded website URL").to.include( - encodeURIComponent(conf.params.websiteToAnalyseUrl) - ); + it("should handle multiple content tiers with same segtax", function () { + const filters = { + ContentTier1: { limit: 3 }, + ContentTier2: { limit: 5 }, + ContentTier3: { threshold: 0.7 } + }; + const contentSegtax = 6; + const result = neuwo.buildFilterQueryParams(filters, contentSegtax, false); - request.respond( - 200, - { "Content-Type": "application/json; encoding=UTF-8" }, - JSON.stringify(apiResponse) - ); - const userData = bidsConfig.ortb2Fragments.global.user.data[0]; - expect(userData.name, "The data provider name should be correctly set").to.equal( - neuwo.DATA_PROVIDER - ); - expect( - userData.ext.segtax, - "The segtax value should correspond to IAB Audience Taxonomy 1.1" - ).to.equal(4); - expect( - userData.segment[0].id, - "The first segment ID should match the API response" - ).to.equal(apiResponse.marketing_categories.iab_audience_tier_3[0].ID); - expect( - userData.segment[1].name, - "The second segment name should match the API response" - ).to.equal(apiResponse.marketing_categories.iab_audience_tier_4[0].label); - }); + expect(result).to.include("filter_6_1_limit=3"); + expect(result).to.include("filter_6_2_limit=5"); + expect(result).to.include("filter_6_3_threshold=0.7"); + expect(result).to.have.lengthOf(3); }); - it("should not change the bids object structure after an unsuccessful API response", function () { - const bidsConfig = bidsConfiglike(); - const bidsConfigCopy = bidsConfiglike(); - const conf = config(); + it("should handle multiple audience tiers", function () { + const filters = { + AudienceTier3: { limit: 2 }, + AudienceTier4: { limit: 4 }, + AudienceTier5: { threshold: 0.85 } + }; + const result = neuwo.buildFilterQueryParams(filters, 6, false); - neuwo.getBidRequestData(bidsConfig, () => {}, conf, "consent data"); - const request = server.requests[0]; - request.respond( - 404, - { "Content-Type": "application/json; encoding=UTF-8" }, - JSON.stringify({ detail: "test error" }) - ); - expect( - bidsConfig, - "The bids config object should remain unmodified after a failed API call" - ).to.deep.equal(bidsConfigCopy); + expect(result).to.include("filter_4_3_limit=2"); + expect(result).to.include("filter_4_4_limit=4"); + expect(result).to.include("filter_4_5_threshold=0.85"); + expect(result).to.have.lengthOf(3); }); - }); - - describe("cleanUrl", function () { - describe("when no stripping options are provided", function () { - it("should return the URL unchanged", function () { - const url = "https://example.com/page?foo=bar&baz=qux"; - const result = neuwo.cleanUrl(url, {}); - expect(result, "should return the original URL with all query params intact").to.equal(url); - }); - it("should return the URL unchanged when options object is empty", function () { - const url = "https://example.com/page?foo=bar"; - const result = neuwo.cleanUrl(url); - expect(result, "should handle missing options parameter").to.equal(url); - }); + it("should handle both content and audience tiers together", function () { + const filters = { + ContentTier1: { limit: 3, threshold: 0.5 }, + ContentTier2: { limit: 5 }, + AudienceTier3: { limit: 2, threshold: 0.9 }, + AudienceTier4: { threshold: 0.8 } + }; + const contentSegtax = 6; + const result = neuwo.buildFilterQueryParams(filters, contentSegtax, false); + + expect(result).to.include("filter_6_1_limit=3"); + expect(result).to.include("filter_6_1_threshold=0.5"); + expect(result).to.include("filter_6_2_limit=5"); + expect(result).to.include("filter_4_3_limit=2"); + expect(result).to.include("filter_4_3_threshold=0.9"); + expect(result).to.include("filter_4_4_threshold=0.8"); + expect(result).to.have.lengthOf(6); }); - describe("with query parameters edge cases", function () { - it("should strip all query parameters from the URL for `stripAllQueryParams` (edge cases)", function () { - const stripAll = (url) => neuwo.cleanUrl(url, { stripAllQueryParams: true }); - const expected = "https://example.com/page"; - const expectedWithFragment = "https://example.com/page#anchor"; - - // Basic formats - expect(stripAll("https://example.com/page?key=value"), "should remove basic key=value params").to.equal(expected); - expect(stripAll("https://example.com/page?key="), "should remove params with empty value").to.equal(expected); - expect(stripAll("https://example.com/page?key"), "should remove params without equals sign").to.equal(expected); - expect(stripAll("https://example.com/page?=value"), "should remove params with empty key").to.equal(expected); - - // Multiple parameters - expect(stripAll("https://example.com/page?key1=value1&key2=value2"), "should remove multiple different params").to.equal(expected); - expect(stripAll("https://example.com/page?key=value1&key=value2"), "should remove multiple params with same key").to.equal(expected); + it("should use different content segtax values correctly", function () { + const filters = { + ContentTier1: { limit: 3 } + }; - // Special characters and encoding - expect(stripAll("https://example.com/page?key=value%20with%20spaces"), "should remove URL encoded spaces").to.equal(expected); - expect(stripAll("https://example.com/page?key=value+with+plus"), "should remove plus as space").to.equal(expected); - expect(stripAll("https://example.com/page?key=value%3D%26%3F"), "should remove encoded special chars").to.equal(expected); - expect(stripAll("https://example.com/page?key=%"), "should remove incomplete encoding").to.equal(expected); - expect(stripAll("https://example.com/page?key=value%2"), "should remove malformed encoding").to.equal(expected); + // Test with segtax 6 (IAB 2.2) + const result6 = neuwo.buildFilterQueryParams(filters, 6, false); + expect(result6).to.include("filter_6_1_limit=3"); + expect(result6).to.not.include("filter_7_1_limit=3"); - // Delimiters and syntax edge cases - expect(stripAll("https://example.com/page?&key=value"), "should remove params with leading ampersand").to.equal(expected); - expect(stripAll("https://example.com/page?key=value&"), "should remove params with trailing ampersand").to.equal(expected); - expect(stripAll("https://example.com/page?key=value&&key2=value2"), "should remove params with double ampersand").to.equal(expected); - expect(stripAll("https://example.com/page?key=value?key2=value2"), "should remove params with question mark delimiter").to.equal(expected); - expect(stripAll("https://example.com/page?key=value;key2=value2"), "should remove params with semicolon delimiter").to.equal(expected); + // Test with segtax 7 (IAB 3.0) + const result7 = neuwo.buildFilterQueryParams(filters, 7, false); + expect(result7).to.include("filter_7_1_limit=3"); + expect(result7).to.not.include("filter_6_1_limit=3"); + }); - // Empty and missing cases - expect(stripAll("https://example.com/page?"), "should remove question mark alone").to.equal(expected); - expect(stripAll("https://example.com/page??"), "should remove double question mark").to.equal(expected); - expect(stripAll("https://example.com/page"), "should handle URL without query string").to.equal(expected); + it("should ignore unknown tier names", function () { + const filters = { + ContentTier1: { limit: 3 }, + UnknownTier: { limit: 10 }, + InvalidTier99: { threshold: 0.5 } + }; + const result = neuwo.buildFilterQueryParams(filters, 6, false); - // Unicode and special values - expect(stripAll("https://example.com/page?key=值"), "should remove unicode characters").to.equal(expected); - expect(stripAll("https://example.com/page?key=null"), "should remove string 'null'").to.equal(expected); - expect(stripAll("https://example.com/page?key=undefined"), "should remove string 'undefined'").to.equal(expected); + expect(result).to.include("filter_6_1_limit=3"); + expect(result).to.have.lengthOf(1); + }); - // Fragment positioning (fragments are preserved by default) - expect(stripAll("https://example.com/page?key=value#anchor"), "should remove query params and preserve fragment").to.equal(expectedWithFragment); - expect(stripAll("https://example.com/page#anchor?key=value"), "should preserve fragment before params").to.equal("https://example.com/page#anchor?key=value"); - }); + it("should handle filters with only limit property", function () { + const filters = { + ContentTier1: { limit: 5 } + }; + const result = neuwo.buildFilterQueryParams(filters, 6, false); - it("should strip all query parameters from the URL for `stripQueryParamsForDomains` (edge cases)", function () { - const stripAll = (url) => neuwo.cleanUrl(url, { stripQueryParamsForDomains: ["example.com"] }); - const expected = "https://example.com/page"; - const expectedWithFragment = "https://example.com/page#anchor"; + expect(result).to.include("filter_6_1_limit=5"); + expect(result).to.not.include.match(/filter_6_1_threshold/); + expect(result).to.have.lengthOf(1); + }); - // Basic formats - expect(stripAll("https://example.com/page?key=value"), "should remove basic key=value params").to.equal(expected); - expect(stripAll("https://example.com/page?key="), "should remove params with empty value").to.equal(expected); - expect(stripAll("https://example.com/page?key"), "should remove params without equals sign").to.equal(expected); - expect(stripAll("https://example.com/page?=value"), "should remove params with empty key").to.equal(expected); + it("should handle filters with only threshold property", function () { + const filters = { + AudienceTier3: { threshold: 0.75 } + }; + const result = neuwo.buildFilterQueryParams(filters, 6, false); - // Multiple parameters - expect(stripAll("https://example.com/page?key1=value1&key2=value2"), "should remove multiple different params").to.equal(expected); - expect(stripAll("https://example.com/page?key=value1&key=value2"), "should remove multiple params with same key").to.equal(expected); + expect(result).to.include("filter_4_3_threshold=0.75"); + expect(result).to.not.include.match(/filter_4_3_limit/); + expect(result).to.have.lengthOf(1); + }); - // Special characters and encoding - expect(stripAll("https://example.com/page?key=value%20with%20spaces"), "should remove URL encoded spaces").to.equal(expected); - expect(stripAll("https://example.com/page?key=value+with+plus"), "should remove plus as space").to.equal(expected); - expect(stripAll("https://example.com/page?key=value%3D%26%3F"), "should remove encoded special chars").to.equal(expected); - expect(stripAll("https://example.com/page?key=%"), "should remove incomplete encoding").to.equal(expected); - expect(stripAll("https://example.com/page?key=value%2"), "should remove malformed encoding").to.equal(expected); + it("should handle filters with additional custom properties", function () { + const filters = { + ContentTier1: { limit: 3, threshold: 0.5, customProp: "value" } + }; + const result = neuwo.buildFilterQueryParams(filters, 6, false); - // Delimiters and syntax edge cases - expect(stripAll("https://example.com/page?&key=value"), "should remove params with leading ampersand").to.equal(expected); - expect(stripAll("https://example.com/page?key=value&"), "should remove params with trailing ampersand").to.equal(expected); - expect(stripAll("https://example.com/page?key=value&&key2=value2"), "should remove params with double ampersand").to.equal(expected); - expect(stripAll("https://example.com/page?key=value?key2=value2"), "should remove params with question mark delimiter").to.equal(expected); - expect(stripAll("https://example.com/page?key=value;key2=value2"), "should remove params with semicolon delimiter").to.equal(expected); + expect(result).to.include("filter_6_1_limit=3"); + expect(result).to.include("filter_6_1_threshold=0.5"); + expect(result).to.include("filter_6_1_customProp=value"); + expect(result).to.have.lengthOf(3); + }); - // Empty and missing cases - expect(stripAll("https://example.com/page?"), "should remove question mark alone").to.equal(expected); - expect(stripAll("https://example.com/page??"), "should remove double question mark").to.equal(expected); - expect(stripAll("https://example.com/page"), "should handle URL without query string").to.equal(expected); + it("should handle empty filter objects for tiers", function () { + const filters = { + ContentTier1: {}, + AudienceTier3: {} + }; + const result = neuwo.buildFilterQueryParams(filters, 6, false); - // Unicode and special values - expect(stripAll("https://example.com/page?key=值"), "should remove unicode characters").to.equal(expected); - expect(stripAll("https://example.com/page?key=null"), "should remove string 'null'").to.equal(expected); - expect(stripAll("https://example.com/page?key=undefined"), "should remove string 'undefined'").to.equal(expected); + expect(result).to.have.lengthOf(0); + }); - // Fragment positioning (fragments are preserved by default) - expect(stripAll("https://example.com/page?key=value#anchor"), "should remove query params and preserve fragment").to.equal(expectedWithFragment); - expect(stripAll("https://example.com/page#anchor?key=value"), "should preserve fragment before params").to.equal("https://example.com/page#anchor?key=value"); - }); + it("should not include null limit value", function () { + const filters = { + ContentTier1: { limit: null, threshold: 0.5 } + }; + const result = neuwo.buildFilterQueryParams(filters, 6, false); - it("should strip all query parameters from the URL for `stripQueryParams` (edge cases)", function () { - const stripAll = (url) => neuwo.cleanUrl(url, { stripQueryParams: ["key", "key1", "key2", "", "?"] }); - const expected = "https://example.com/page"; - const expectedWithFragment = "https://example.com/page#anchor"; + expect(result).to.include("filter_6_1_threshold=0.5"); + expect(result).to.have.lengthOf(1); + }); - // Basic formats - expect(stripAll("https://example.com/page?key=value"), "should remove basic key=value params").to.equal(expected); - expect(stripAll("https://example.com/page?key="), "should remove params with empty value").to.equal(expected); - expect(stripAll("https://example.com/page?key"), "should remove params without equals sign").to.equal(expected); - expect(stripAll("https://example.com/page?=value"), "should remove params with empty key").to.equal(expected); + it("should not include undefined limit value", function () { + const filters = { + ContentTier1: { limit: undefined, threshold: 0.5 } + }; + const result = neuwo.buildFilterQueryParams(filters, 6, false); - // Multiple parameters - expect(stripAll("https://example.com/page?key1=value1&key2=value2"), "should remove multiple different params").to.equal(expected); - expect(stripAll("https://example.com/page?key=value1&key=value2"), "should remove multiple params with same key").to.equal(expected); + expect(result).to.include("filter_6_1_threshold=0.5"); + expect(result).to.have.lengthOf(1); + }); - // Special characters and encoding - expect(stripAll("https://example.com/page?key=value%20with%20spaces"), "should remove URL encoded spaces").to.equal(expected); - expect(stripAll("https://example.com/page?key=value+with+plus"), "should remove plus as space").to.equal(expected); - expect(stripAll("https://example.com/page?key=value%3D%26%3F"), "should remove encoded special chars").to.equal(expected); - expect(stripAll("https://example.com/page?key=%"), "should remove incomplete encoding").to.equal(expected); - expect(stripAll("https://example.com/page?key=value%2"), "should remove malformed encoding").to.equal(expected); + it("should not include null threshold value", function () { + const filters = { + AudienceTier3: { limit: 5, threshold: null } + }; + const result = neuwo.buildFilterQueryParams(filters, 6, false); - // Delimiters and syntax edge cases - expect(stripAll("https://example.com/page?&key=value"), "should remove params with leading ampersand").to.equal(expected); - expect(stripAll("https://example.com/page?key=value&"), "should remove params with trailing ampersand").to.equal(expected); - expect(stripAll("https://example.com/page?key=value&&key2=value2"), "should remove params with double ampersand").to.equal(expected); - expect(stripAll("https://example.com/page?key=value?key2=value2"), "should remove params with question mark delimiter").to.equal(expected); - expect(stripAll("https://example.com/page?key=value;key2=value2"), "should remove params with semicolon delimiter").to.equal(expected); + expect(result).to.include("filter_4_3_limit=5"); + expect(result).to.have.lengthOf(1); + }); - // Empty and missing cases - expect(stripAll("https://example.com/page?"), "should remove question mark alone").to.equal(expected); - expect(stripAll("https://example.com/page"), "should handle URL without query string").to.equal(expected); + it("should not include undefined threshold value", function () { + const filters = { + AudienceTier3: { limit: 5, threshold: undefined } + }; + const result = neuwo.buildFilterQueryParams(filters, 6, false); - // Unicode and special values - expect(stripAll("https://example.com/page?key=值"), "should remove unicode characters").to.equal(expected); - expect(stripAll("https://example.com/page?key=null"), "should remove string 'null'").to.equal(expected); - expect(stripAll("https://example.com/page?key=undefined"), "should remove string 'undefined'").to.equal(expected); + expect(result).to.include("filter_4_3_limit=5"); + expect(result).to.have.lengthOf(1); + }); - // Fragment positioning (fragments are preserved by default) - expect(stripAll("https://example.com/page?key=value#anchor"), "should remove query params and preserve fragment").to.equal(expectedWithFragment); - expect(stripAll("https://example.com/page#anchor?key=value"), "should preserve fragment before params").to.equal("https://example.com/page#anchor?key=value"); + // OpenRTB 2.5 Feature Tests + describe("with enableOrtb25Fields enabled (default)", function () { + it("should add IAB 1.0 filter params when ContentTier1 filter is provided", function () { + const filters = { + ContentTier1: { limit: 3, threshold: 0.5 } + }; + const contentSegtax = 6; + const result = neuwo.buildFilterQueryParams(filters, contentSegtax); + + expect(result).to.include("filter_6_1_limit=3"); + expect(result).to.include("filter_6_1_threshold=0.5"); + expect(result).to.include("filter_1_1_limit=3"); + expect(result).to.include("filter_1_1_threshold=0.5"); + expect(result).to.have.lengthOf(4); }); - }); - describe("when stripAllQueryParams is true", function () { - it("should strip all query parameters from the URL", function () { - const url = "https://example.com/page?foo=bar&baz=qux&test=123"; - const expected = "https://example.com/page"; - const result = neuwo.cleanUrl(url, { stripAllQueryParams: true }); - expect(result, "should remove all query parameters").to.equal(expected); + it("should add IAB 1.0 filter params when ContentTier2 filter is provided", function () { + const filters = { + ContentTier2: { limit: 5, threshold: 0.6 } + }; + const contentSegtax = 6; + const result = neuwo.buildFilterQueryParams(filters, contentSegtax); + + expect(result).to.include("filter_6_2_limit=5"); + expect(result).to.include("filter_6_2_threshold=0.6"); + expect(result).to.include("filter_1_2_limit=5"); + expect(result).to.include("filter_1_2_threshold=0.6"); + expect(result).to.have.lengthOf(4); }); - it("should return the URL unchanged if there are no query parameters", function () { - const url = "https://example.com/page"; - const result = neuwo.cleanUrl(url, { stripAllQueryParams: true }); - expect(result, "should handle URLs without query params").to.equal(url); + it("should add IAB 1.0 filter params for both ContentTier1 and ContentTier2", function () { + const filters = { + ContentTier1: { limit: 3 }, + ContentTier2: { limit: 5 } + }; + const contentSegtax = 6; + const result = neuwo.buildFilterQueryParams(filters, contentSegtax); + + expect(result).to.include("filter_6_1_limit=3"); + expect(result).to.include("filter_6_2_limit=5"); + expect(result).to.include("filter_1_1_limit=3"); + expect(result).to.include("filter_1_2_limit=5"); + expect(result).to.have.lengthOf(4); }); - it("should preserve the hash fragment when stripping query params without stripFragments", function () { - const url = "https://example.com/page?foo=bar#section"; - const expected = "https://example.com/page#section"; - const result = neuwo.cleanUrl(url, { stripAllQueryParams: true }); - expect(result, "should preserve hash fragments by default").to.equal(expected); + it("should NOT add ContentTier3 filter to IAB 1.0 (only tiers 1-2 exist)", function () { + const filters = { + ContentTier1: { limit: 3 }, + ContentTier2: { limit: 5 }, + ContentTier3: { threshold: 0.7 } + }; + const contentSegtax = 6; + const result = neuwo.buildFilterQueryParams(filters, contentSegtax); + + expect(result).to.include("filter_6_1_limit=3"); + expect(result).to.include("filter_6_2_limit=5"); + expect(result).to.include("filter_6_3_threshold=0.7"); + expect(result).to.include("filter_1_1_limit=3"); + expect(result).to.include("filter_1_2_limit=5"); + expect(result).to.not.include.match(/filter_1_3/); + expect(result).to.have.lengthOf(5); }); - it("should strip hash fragment when stripFragments is enabled", function () { - const url = "https://example.com/page?foo=bar#section"; - const expected = "https://example.com/page"; - const result = neuwo.cleanUrl(url, { stripAllQueryParams: true, stripFragments: true }); - expect(result, "should strip both query params and fragments").to.equal(expected); + it("should NOT add audience tier filters to IAB 1.0", function () { + const filters = { + AudienceTier3: { limit: 2 }, + AudienceTier4: { limit: 4 } + }; + const result = neuwo.buildFilterQueryParams(filters, 6); + + expect(result).to.include("filter_4_3_limit=2"); + expect(result).to.include("filter_4_4_limit=4"); + expect(result).to.not.include.match(/filter_1/); + expect(result).to.have.lengthOf(2); }); - it("should strip query params but preserve path and protocol", function () { - const url = "https://subdomain.example.com:8080/path/to/page?param=value"; - const expected = "https://subdomain.example.com:8080/path/to/page"; - const result = neuwo.cleanUrl(url, { stripAllQueryParams: true }); - expect(result, "should preserve protocol, domain, port, and path").to.equal(expected); + it("should add IAB 1.0 filter params alongside audience filters", function () { + const filters = { + ContentTier1: { limit: 3 }, + AudienceTier3: { limit: 2 } + }; + const contentSegtax = 6; + const result = neuwo.buildFilterQueryParams(filters, contentSegtax); + + expect(result).to.include("filter_6_1_limit=3"); + expect(result).to.include("filter_1_1_limit=3"); + expect(result).to.include("filter_4_3_limit=2"); + expect(result).to.have.lengthOf(3); }); - }); - describe("when stripQueryParamsForDomains is provided", function () { - it("should strip all query params for exact domain match", function () { - const url = "https://example.com/page?foo=bar&baz=qux"; - const expected = "https://example.com/page"; - const result = neuwo.cleanUrl(url, { - stripQueryParamsForDomains: ["example.com"] - }); - expect(result, "should strip params for exact domain match").to.equal(expected); + it("should not add segtax 1 params when no content filters are provided", function () { + const filters = { + AudienceTier3: { limit: 2 } + }; + const result = neuwo.buildFilterQueryParams(filters, 6); + + expect(result).to.include("filter_4_3_limit=2"); + expect(result).to.not.include.match(/filter_1/); + expect(result).to.have.lengthOf(1); }); - it("should strip all query params for subdomain match", function () { - const url = "https://sub.example.com/page?foo=bar"; - const expected = "https://sub.example.com/page"; - const result = neuwo.cleanUrl(url, { - stripQueryParamsForDomains: ["example.com"] - }); - expect(result, "should strip params for subdomains").to.equal(expected); + it("should not add segtax 1 params when filters is empty", function () { + const result = neuwo.buildFilterQueryParams({}, 6); + + expect(result).to.deep.equal([]); }); - it("should not strip query params if domain does not match", function () { - const url = "https://other.com/page?foo=bar"; - const result = neuwo.cleanUrl(url, { - stripQueryParamsForDomains: ["example.com"] - }); - expect(result, "should preserve params for non-matching domains").to.equal(url); + it("should not add segtax 1 params when filters is null", function () { + const result = neuwo.buildFilterQueryParams(null, 6); + + expect(result).to.deep.equal([]); }); - it("should not strip query params if subdomain is provided for domain", function () { - const url = "https://example.com/page?foo=bar"; - const result = neuwo.cleanUrl(url, { - stripQueryParamsForDomains: ["sub.example.com"] - }); - expect(result, "should preserve params for domain when subdomain is provided").to.equal(url); + it("should work with different content segtax values", function () { + const filters = { + ContentTier1: { limit: 3 } + }; + + // Test with segtax 7 (IAB 3.0) + const result7 = neuwo.buildFilterQueryParams(filters, 7); + expect(result7).to.include("filter_7_1_limit=3"); + expect(result7).to.include("filter_1_1_limit=3"); + expect(result7).to.have.lengthOf(2); }); - it("should handle multiple domains in the list", function () { - const url1 = "https://example.com/page?foo=bar"; - const url2 = "https://test.com/page?foo=bar"; - const url3 = "https://other.com/page?foo=bar"; - const domains = ["example.com", "test.com"]; + it("should not produce duplicate query params when contentSegtax is 1", function () { + const filters = { + ContentTier1: { limit: 5, threshold: 0.8 }, + ContentTier2: { limit: 3 } + }; - const result1 = neuwo.cleanUrl(url1, { stripQueryParamsForDomains: domains }); - const result2 = neuwo.cleanUrl(url2, { stripQueryParamsForDomains: domains }); - const result3 = neuwo.cleanUrl(url3, { stripQueryParamsForDomains: domains }); + const result = neuwo.buildFilterQueryParams(filters, 1); - expect(result1, "should strip params for first domain").to.equal("https://example.com/page"); - expect(result2, "should strip params for second domain").to.equal("https://test.com/page"); - expect(result3, "should preserve params for non-listed domain").to.equal(url3); + // Count occurrences of each param to verify no duplicates + const countOccurrences = (arr, val) => arr.filter(p => p === val).length; + expect(countOccurrences(result, "filter_1_1_limit=5"), "filter_1_1_limit should appear once").to.equal(1); + expect(countOccurrences(result, "filter_1_1_threshold=0.8"), "filter_1_1_threshold should appear once").to.equal(1); + expect(countOccurrences(result, "filter_1_2_limit=3"), "filter_1_2_limit should appear once").to.equal(1); + expect(result, "should have exactly 3 params total").to.have.lengthOf(3); }); + }); - it("should handle deep subdomains correctly", function () { - const url = "https://deep.sub.example.com/page?foo=bar"; - const expected = "https://deep.sub.example.com/page"; - const result1 = neuwo.cleanUrl(url, { - stripQueryParamsForDomains: ["example.com"] - }); - const result2 = neuwo.cleanUrl(url, { - stripQueryParamsForDomains: ["sub.example.com"] - }); - expect(result1, "should strip params for deep subdomains with domain matching").to.equal(expected); - expect(result2, "should strip params for deep subdomains with subdomain matching").to.equal(expected); + describe("with enableOrtb25Fields disabled", function () { + it("should not add IAB 1.0 filter params when disabled", function () { + const filters = { + ContentTier1: { limit: 3, threshold: 0.5 }, + ContentTier2: { limit: 5 } + }; + const contentSegtax = 6; + const result = neuwo.buildFilterQueryParams(filters, contentSegtax, false); + + expect(result).to.include("filter_6_1_limit=3"); + expect(result).to.include("filter_6_1_threshold=0.5"); + expect(result).to.include("filter_6_2_limit=5"); + expect(result).to.not.include.match(/filter_1/); + expect(result).to.have.lengthOf(3); }); - it("should not match partial domain names", function () { - const url = "https://notexample.com/page?foo=bar"; - const result = neuwo.cleanUrl(url, { - stripQueryParamsForDomains: ["example.com"] - }); - expect(result, "should not match partial domain strings").to.equal(url); + it("should work correctly with all tier types when disabled", function () { + const filters = { + ContentTier1: { limit: 3 }, + ContentTier2: { limit: 5 }, + ContentTier3: { threshold: 0.7 }, + AudienceTier3: { limit: 2 } + }; + const contentSegtax = 6; + const result = neuwo.buildFilterQueryParams(filters, contentSegtax, false); + + expect(result).to.include("filter_6_1_limit=3"); + expect(result).to.include("filter_6_2_limit=5"); + expect(result).to.include("filter_6_3_threshold=0.7"); + expect(result).to.include("filter_4_3_limit=2"); + expect(result).to.not.include.match(/filter_1/); + expect(result).to.have.lengthOf(4); }); + }); + }); - it("should handle empty domain list", function () { - const url = "https://example.com/page?foo=bar"; - const result = neuwo.cleanUrl(url, { stripQueryParamsForDomains: [] }); - expect(result, "should not strip params with empty domain list").to.equal(url); - }); + describe("extractCategoryIds", function () { + it("should extract IDs from single tier", function () { + const tierData = { + "1": [ + { id: "IAB12" }, + { id: "IAB12-3" } + ] + }; + const result = neuwo.extractCategoryIds(tierData); + expect(result, "should extract all IDs from tier 1").to.deep.equal(["IAB12", "IAB12-3"]); }); - describe("when stripQueryParams is provided", function () { - it("should strip only specified query parameters", function () { - const url = "https://example.com/page?foo=bar&baz=qux&keep=this"; - const expected = "https://example.com/page?keep=this"; - const result = neuwo.cleanUrl(url, { - stripQueryParams: ["foo", "baz"] + it("should extract IDs from multiple tiers", function () { + const tierData = { + "1": [ + { id: "IAB12" }, + { id: "IAB12-3" } + ], + "2": [ + { id: "IAB12-5" } + ] + }; + const result = neuwo.extractCategoryIds(tierData); + expect(result, "should extract all IDs from all tiers").to.deep.equal(["IAB12", "IAB12-3", "IAB12-5"]); + }); + + it("should handle empty tier arrays", function () { + const tierData = { + "1": [], + "2": [ + { id: "IAB12" } + ] + }; + const result = neuwo.extractCategoryIds(tierData); + expect(result, "should only extract from non-empty tiers").to.deep.equal(["IAB12"]); + }); + + it("should skip items without id property", function () { + const tierData = { + "1": [ + { id: "IAB12" }, + { name: "No ID" }, + { id: "IAB12-3" } + ] + }; + const result = neuwo.extractCategoryIds(tierData); + expect(result, "should skip items without id").to.deep.equal(["IAB12", "IAB12-3"]); + }); + + it("should return empty array for null tierData", function () { + const result = neuwo.extractCategoryIds(null); + expect(result, "should return empty array for null").to.deep.equal([]); + }); + + it("should return empty array for undefined tierData", function () { + const result = neuwo.extractCategoryIds(undefined); + expect(result, "should return empty array for undefined").to.deep.equal([]); + }); + + it("should return empty array for empty object", function () { + const result = neuwo.extractCategoryIds({}); + expect(result, "should return empty array for empty object").to.deep.equal([]); + }); + + it("should handle non-array tier values", function () { + const tierData = { + "1": { id: "IAB12" }, // Not an array + "2": [ + { id: "IAB13" } + ] + }; + const result = neuwo.extractCategoryIds(tierData); + expect(result, "should skip non-array values").to.deep.equal(["IAB13"]); + }); + + it("should handle null items in tier arrays", function () { + const tierData = { + "1": [ + { id: "IAB12" }, + null, + { id: "IAB12-3" } + ] + }; + const result = neuwo.extractCategoryIds(tierData); + expect(result, "should skip null items").to.deep.equal(["IAB12", "IAB12-3"]); + }); + + it("should handle non-object tierData", function () { + expect(neuwo.extractCategoryIds("string"), "should handle string").to.deep.equal([]); + expect(neuwo.extractCategoryIds(123), "should handle number").to.deep.equal([]); + expect(neuwo.extractCategoryIds([]), "should handle array").to.deep.equal([]); + }); + + it("should extract from all tier numbers", function () { + const tierData = { + "1": [{ id: "IAB1" }], + "2": [{ id: "IAB2" }], + "3": [{ id: "IAB3" }], + "4": [{ id: "IAB4" }], + "5": [{ id: "IAB5" }] + }; + const result = neuwo.extractCategoryIds(tierData); + expect(result, "should extract from all tiers").to.deep.equal(["IAB1", "IAB2", "IAB3", "IAB4", "IAB5"]); + }); + }); + + describe("injectOrtbData", function () { + it("should correctly mutate the request bids config object with new data", function () { + const reqBidsConfigObj = { ortb2Fragments: { global: {} } }; + neuwo.injectOrtbData(reqBidsConfigObj, "c.d.e.f", { g: "h" }); + expect( + reqBidsConfigObj.ortb2Fragments.global.c.d.e.f.g, + "should deeply merge the new data into the target object" + ).to.equal("h"); + }); + }); + + describe("injectIabCategories", function () { + it("should not inject data when responseParsed is null or undefined", function () { + const bidsConfig1 = bidsConfiglike(); + const bidsConfig2 = bidsConfiglike(); + const bidsConfigCopy = JSON.parse(JSON.stringify(bidsConfig1)); + + neuwo.injectIabCategories(null, bidsConfig1, "2.2"); + neuwo.injectIabCategories(undefined, bidsConfig2, "2.2"); + + expect( + bidsConfig1.ortb2Fragments.global, + "should not modify ortb2Fragments when response is null" + ).to.deep.equal(bidsConfigCopy.ortb2Fragments.global); + expect( + bidsConfig2.ortb2Fragments.global, + "should not modify ortb2Fragments when response is undefined" + ).to.deep.equal(bidsConfigCopy.ortb2Fragments.global); + }); + + it("should not inject data when responseParsed is not an object", function () { + const bidsConfig1 = bidsConfiglike(); + const bidsConfig2 = bidsConfiglike(); + const bidsConfig3 = bidsConfiglike(); + const bidsConfigCopy = JSON.parse(JSON.stringify(bidsConfig1)); + + neuwo.injectIabCategories("invalid string", bidsConfig1, "2.2"); + neuwo.injectIabCategories(123, bidsConfig2, "2.2"); + neuwo.injectIabCategories([1, 2, 3], bidsConfig3, "2.2"); + + expect( + bidsConfig1.ortb2Fragments.global, + "should not modify ortb2Fragments when response is a string" + ).to.deep.equal(bidsConfigCopy.ortb2Fragments.global); + expect( + bidsConfig2.ortb2Fragments.global, + "should not modify ortb2Fragments when response is a number" + ).to.deep.equal(bidsConfigCopy.ortb2Fragments.global); + expect( + bidsConfig3.ortb2Fragments.global, + "should not modify ortb2Fragments when response is an array" + ).to.deep.equal(bidsConfigCopy.ortb2Fragments.global); + }); + + it("should handle empty object response", function () { + const bidsConfig = bidsConfiglike(); + const bidsConfigCopy = JSON.parse(JSON.stringify(bidsConfig)); + + neuwo.injectIabCategories({}, bidsConfig, "2.2"); + + expect( + bidsConfig.ortb2Fragments.global, + "should not inject data when response object is empty" + ).to.deep.equal(bidsConfigCopy.ortb2Fragments.global); + }); + + it("should inject content data when valid content segments exist", function () { + const response = { + "6": { + "1": [{ id: "52", name: "Food & Drink" }], + "2": [{ id: "90", name: "Cooking" }] + } + }; + const bidsConfig = bidsConfiglike(); + + neuwo.injectIabCategories(response, bidsConfig, "2.2"); + + const contentData = bidsConfig.ortb2Fragments.global?.site?.content?.data?.[0]; + expect(contentData, "should have content data").to.exist; + expect(contentData.ext.segtax, "should have correct segtax").to.equal(6); + expect(contentData.segment, "should have segments").to.have.lengthOf(2); + expect(contentData.segment[0].id, "first segment should match").to.equal("52"); + }); + + it("should inject audience data when valid audience segments exist", function () { + const response = { + "4": { + "3": [{ id: "49", name: "Female" }], + "4": [{ id: "431", name: "Age 25-34" }] + } + }; + const bidsConfig = bidsConfiglike(); + + neuwo.injectIabCategories(response, bidsConfig, "2.2"); + + const userData = bidsConfig.ortb2Fragments.global?.user?.data?.[0]; + expect(userData, "should have user data").to.exist; + expect(userData.ext.segtax, "should have correct segtax").to.equal(4); + expect(userData.segment, "should have segments").to.have.lengthOf(2); + expect(userData.segment[0].id, "first segment should match").to.equal("49"); + }); + + it("should inject both content and audience data when both exist", function () { + const response = { + "6": { + "1": [{ id: "52", name: "Food & Drink" }] + }, + "4": { + "3": [{ id: "49", name: "Female" }] + } + }; + const bidsConfig = bidsConfiglike(); + + neuwo.injectIabCategories(response, bidsConfig, "2.2"); + + const contentData = bidsConfig.ortb2Fragments.global?.site?.content?.data?.[0]; + const userData = bidsConfig.ortb2Fragments.global?.user?.data?.[0]; + + expect(contentData, "should have content data").to.exist; + expect(userData, "should have user data").to.exist; + expect(contentData.ext.segtax, "content should have segtax 6").to.equal(6); + expect(userData.ext.segtax, "audience should have segtax 4").to.equal(4); + }); + + it("should not inject empty audience data when only content segments exist", function () { + const response = { + "6": { + "1": [{ id: "52", name: "Food & Drink" }] + }, + "4": {} + }; + const bidsConfig = bidsConfiglike(); + + neuwo.injectIabCategories(response, bidsConfig, "2.2"); + + const contentData = bidsConfig.ortb2Fragments.global?.site?.content?.data?.[0]; + const userData = bidsConfig.ortb2Fragments.global?.user?.data; + expect(contentData, "should have content data").to.exist; + expect(userData, "should not inject empty audience data").to.be.undefined; + }); + + it("should not inject empty content data when only audience segments exist", function () { + const response = { + "6": {}, + "4": { + "3": [{ id: "49", name: "Female" }] + } + }; + const bidsConfig = bidsConfiglike(); + + neuwo.injectIabCategories(response, bidsConfig, "2.2"); + + const contentData = bidsConfig.ortb2Fragments.global?.site?.content?.data; + const userData = bidsConfig.ortb2Fragments.global?.user?.data?.[0]; + expect(contentData, "should not inject empty content data").to.be.undefined; + expect(userData, "should have audience data").to.exist; + }); + + it("should handle different IAB Content Taxonomy versions", function () { + const response = { + "7": { + "1": [{ id: "80DV8O", name: "Automotive" }] + } + }; + const bidsConfig = bidsConfiglike(); + + neuwo.injectIabCategories(response, bidsConfig, "3.0"); + + const contentData = bidsConfig.ortb2Fragments.global?.site?.content?.data?.[0]; + expect(contentData, "should have content data").to.exist; + expect(contentData.ext.segtax, "should use segtax 7 for IAB 3.0").to.equal(7); + }); + + it("should not inject data when segtax has no segments", function () { + const response1 = { "6": {} }; + const response2 = { "4": {} }; + const response3 = { "6": { "1": [] }, "4": { "3": [] } }; + const bidsConfig1 = bidsConfiglike(); + const bidsConfig2 = bidsConfiglike(); + const bidsConfig3 = bidsConfiglike(); + + neuwo.injectIabCategories(response1, bidsConfig1, "2.2"); + neuwo.injectIabCategories(response2, bidsConfig2, "2.2"); + neuwo.injectIabCategories(response3, bidsConfig3, "2.2"); + + expect(bidsConfig1.ortb2Fragments.global?.site?.content?.data, "should not inject empty content data").to.be.undefined; + expect(bidsConfig2.ortb2Fragments.global?.user?.data, "should not inject empty audience data").to.be.undefined; + expect(bidsConfig3.ortb2Fragments.global?.site?.content?.data, "should not inject content data with empty segments").to.be.undefined; + expect(bidsConfig3.ortb2Fragments.global?.user?.data, "should not inject audience data with empty segments").to.be.undefined; + }); + + it("should use default taxonomy version when invalid version provided", function () { + const response = { + "6": { + "1": [{ id: "52", name: "Food & Drink" }] + } + }; + const bidsConfig = bidsConfiglike(); + + neuwo.injectIabCategories(response, bidsConfig, "invalid-version"); + + const contentData = bidsConfig.ortb2Fragments.global?.site?.content?.data?.[0]; + expect(contentData, "should have content data").to.exist; + expect(contentData.ext.segtax, "should default to segtax 6 (IAB 2.2)").to.equal(6); + }); + + // OpenRTB 2.5 Category Fields Tests + describe("OpenRTB 2.5 category fields", function () { + describe("with enableOrtb25Fields enabled (default)", function () { + it("should inject category fields when IAB 1.0 data exists", function () { + const response = { + "1": { + "1": [{ id: "IAB12" }], + "2": [{ id: "IAB12-3" }, { id: "IAB12-5" }] + }, + "6": { + "1": [{ id: "52" }] + } + }; + const bidsConfig = bidsConfiglike(); + + neuwo.injectIabCategories(response, bidsConfig, "2.2"); + + const siteCat = bidsConfig.ortb2Fragments.global?.site?.cat; + const siteSectioncat = bidsConfig.ortb2Fragments.global?.site?.sectioncat; + const sitePagecat = bidsConfig.ortb2Fragments.global?.site?.pagecat; + const contentCat = bidsConfig.ortb2Fragments.global?.site?.content?.cat; + + expect(siteCat, "should have site.cat").to.deep.equal(["IAB12", "IAB12-3", "IAB12-5"]); + expect(siteSectioncat, "should have site.sectioncat").to.deep.equal(["IAB12", "IAB12-3", "IAB12-5"]); + expect(sitePagecat, "should have site.pagecat").to.deep.equal(["IAB12", "IAB12-3", "IAB12-5"]); + expect(contentCat, "should have site.content.cat").to.deep.equal(["IAB12", "IAB12-3", "IAB12-5"]); }); - expect(result, "should remove only specified params").to.equal(expected); - }); - it("should handle single parameter stripping", function () { - const url = "https://example.com/page?remove=this&keep=that"; - const expected = "https://example.com/page?keep=that"; - const result = neuwo.cleanUrl(url, { - stripQueryParams: ["remove"] + it("should inject category fields with single IAB 1.0 segment", function () { + const response = { + "1": { + "1": [{ id: "IAB12" }] + } + }; + const bidsConfig = bidsConfiglike(); + + neuwo.injectIabCategories(response, bidsConfig, "2.2"); + + const siteCat = bidsConfig.ortb2Fragments.global?.site?.cat; + expect(siteCat, "should have single category").to.deep.equal(["IAB12"]); }); - expect(result, "should remove single specified param").to.equal(expected); - }); - it("should return URL without query string if all params are stripped", function () { - const url = "https://example.com/page?foo=bar&baz=qux"; - const expected = "https://example.com/page"; - const result = neuwo.cleanUrl(url, { - stripQueryParams: ["foo", "baz"] + it("should not inject category fields when IAB 1.0 data is missing", function () { + const response = { + "6": { + "1": [{ id: "52" }] + } + }; + const bidsConfig = bidsConfiglike(); + + neuwo.injectIabCategories(response, bidsConfig, "2.2"); + + const siteCat = bidsConfig.ortb2Fragments.global?.site?.cat; + const siteSectioncat = bidsConfig.ortb2Fragments.global?.site?.sectioncat; + + expect(siteCat, "should not have site.cat").to.be.undefined; + expect(siteSectioncat, "should not have site.sectioncat").to.be.undefined; + }); + + it("should not inject category fields when IAB 1.0 data is empty", function () { + const response = { + "1": {}, + "6": { + "1": [{ id: "52" }] + } + }; + const bidsConfig = bidsConfiglike(); + + neuwo.injectIabCategories(response, bidsConfig, "2.2"); + + const siteCat = bidsConfig.ortb2Fragments.global?.site?.cat; + expect(siteCat, "should not have site.cat with empty IAB 1.0").to.be.undefined; + }); + + it("should not inject category fields when IAB 1.0 tiers are empty arrays", function () { + const response = { + "1": { + "1": [], + "2": [] + } + }; + const bidsConfig = bidsConfiglike(); + + neuwo.injectIabCategories(response, bidsConfig, "2.2"); + + const siteCat = bidsConfig.ortb2Fragments.global?.site?.cat; + expect(siteCat, "should not have site.cat with empty tier arrays").to.be.undefined; + }); + + it("should handle IAB 1.0 data with malformed items", function () { + const response = { + "1": { + "1": [ + { id: "IAB12" }, + { name: "No ID" }, + null, + { id: "IAB12-3" } + ] + } + }; + const bidsConfig = bidsConfiglike(); + + neuwo.injectIabCategories(response, bidsConfig, "2.2"); + + const siteCat = bidsConfig.ortb2Fragments.global?.site?.cat; + expect(siteCat, "should skip malformed items").to.deep.equal(["IAB12", "IAB12-3"]); + }); + + it("should inject both content data and category fields", function () { + const response = { + "1": { + "1": [{ id: "IAB12" }] + }, + "6": { + "1": [{ id: "52", name: "Food & Drink" }] + } + }; + const bidsConfig = bidsConfiglike(); + + neuwo.injectIabCategories(response, bidsConfig, "2.2"); + + const contentData = bidsConfig.ortb2Fragments.global?.site?.content?.data?.[0]; + const siteCat = bidsConfig.ortb2Fragments.global?.site?.cat; + + expect(contentData, "should have content data").to.exist; + expect(contentData.ext.segtax, "should have segtax 6").to.equal(6); + expect(siteCat, "should have category fields").to.deep.equal(["IAB12"]); + }); + + it("should merge category fields with existing data", function () { + const response = { + "1": { + "1": [{ id: "IAB12" }] + } + }; + const bidsConfig = bidsConfiglike(); + // Pre-populate with existing category data + bidsConfig.ortb2Fragments.global = { + site: { + cat: ["EXISTING1"] + } + }; + + neuwo.injectIabCategories(response, bidsConfig, "2.2"); + + const siteCat = bidsConfig.ortb2Fragments.global?.site?.cat; + // mergeDeep should deduplicate and merge arrays + expect(siteCat, "should merge with existing data").to.include("IAB12"); + expect(siteCat, "should preserve existing data").to.include("EXISTING1"); }); - expect(result, "should remove query string when all params stripped").to.equal(expected); }); - it("should handle case where specified params do not exist", function () { - const url = "https://example.com/page?foo=bar"; - const result = neuwo.cleanUrl(url, { - stripQueryParams: ["nonexistent", "alsonothere"] + describe("with enableOrtb25Fields disabled", function () { + it("should not inject category fields when disabled", function () { + const response = { + "1": { + "1": [{ id: "IAB12" }], + "2": [{ id: "IAB12-3" }] + }, + "6": { + "1": [{ id: "52" }] + } + }; + const bidsConfig = bidsConfiglike(); + + neuwo.injectIabCategories(response, bidsConfig, "2.2", false); + + const siteCat = bidsConfig.ortb2Fragments.global?.site?.cat; + const siteSectioncat = bidsConfig.ortb2Fragments.global?.site?.sectioncat; + const contentData = bidsConfig.ortb2Fragments.global?.site?.content?.data?.[0]; + + expect(siteCat, "should not have site.cat").to.be.undefined; + expect(siteSectioncat, "should not have site.sectioncat").to.be.undefined; + expect(contentData, "should still have content data (segtax 6)").to.exist; }); - expect(result, "should handle non-existent params gracefully").to.equal(url); + + it("should inject content data but not category fields when disabled", function () { + const response = { + "1": { + "1": [{ id: "IAB12" }] + }, + "6": { + "1": [{ id: "52", name: "Food & Drink" }] + } + }; + const bidsConfig = bidsConfiglike(); + + neuwo.injectIabCategories(response, bidsConfig, "2.2", false); + + const contentData = bidsConfig.ortb2Fragments.global?.site?.content?.data?.[0]; + const siteCat = bidsConfig.ortb2Fragments.global?.site?.cat; + + expect(contentData, "should have content data").to.exist; + expect(contentData.ext.segtax).to.equal(6); + expect(siteCat, "should not have category fields").to.be.undefined; + }); + }); + }); + }); + + describe("getBidRequestData", function () { + it("should call callback and not make API request when no URL is available", function () { + const getRefererInfoStub = sinon.stub(refererDetection, "getRefererInfo").returns({ page: "" }); + const bidsConfig = bidsConfiglike(); + const conf = config(); + let callbackCalled = false; + + neuwo.getBidRequestData(bidsConfig, () => { callbackCalled = true; }, conf, "consent data"); + + expect(callbackCalled, "callback should be called for empty URL").to.be.true; + expect(server.requests.length, "should not make API request for empty URL").to.equal(0); + const contentData = bidsConfig.ortb2Fragments.global?.site?.content?.data; + expect(contentData, "should not inject any data").to.be.undefined; + + getRefererInfoStub.restore(); + }); + + it("should call callback when response processing throws an error", function (done) { + const bidsConfig = { ortb2Fragments: null }; + const conf = config(); + conf.params.websiteToAnalyseUrl = "https://publisher.works/processing-error"; + + neuwo.getBidRequestData(bidsConfig, () => { + const contentData = bidsConfig.ortb2Fragments?.global?.site?.content?.data; + expect(contentData, "should not inject data after processing error").to.be.undefined; + done(); + }, conf, "consent data"); + + const request = server.requests[0]; + request.respond( + 200, + { "Content-Type": "application/json; encoding=UTF-8" }, + JSON.stringify(getNeuwoApiResponse()) + ); + }); + + describe("when using IAB Content Taxonomy 2.2 (API default)", function () { + it("should correctly structure the bids object after a successful API response", function () { + const apiResponse = getNeuwoApiResponse(); + const bidsConfig = bidsConfiglike(); + const conf = config(); + // control xhr api request target for testing + conf.params.websiteToAnalyseUrl = + "https://publisher.works/article.php?get=horrible_url_for_testing&id=5"; + + neuwo.getBidRequestData(bidsConfig, () => {}, conf, "consent data"); + const request = server.requests[0]; + + expect(request.url, "The request URL should be a string").to.be.a("string"); + expect(request.url, "The request URL should include the public API token").to.include( + conf.params.neuwoApiToken + ); + expect(request.url, "The request URL should include the encoded website URL").to.include( + encodeURIComponent(conf.params.websiteToAnalyseUrl) + ); + expect(request.url, "The request URL should include the product identifier").to.include( + "_neuwo_prod=PrebidModule" + ); + expect(request.url, "API should include iabVersions parameter for segtax 6").to.include( + "iabVersions=6" + ); + expect(request.url, "API should include iabVersions parameter for segtax 4").to.include( + "iabVersions=4" + ); + expect(request.method, "API should use GET method").to.equal("GET"); + + request.respond( + 200, + { "Content-Type": "application/json; encoding=UTF-8" }, + JSON.stringify(apiResponse) + ); + + const contentData = bidsConfig.ortb2Fragments.global.site.content.data[0]; + expect(contentData.name, "The data provider name should be correctly set").to.equal( + neuwo.DATA_PROVIDER + ); + expect( + contentData.ext.segtax, + "The segtax value should correspond to IAB Content Taxonomy 2.2" + ).to.equal(6); + expect( + contentData.segment[0].id, + "The first segment ID should match the API response" + ).to.equal(apiResponse["6"]["1"][0].id); + expect( + contentData.segment[1].id, + "The second segment ID should match the API response" + ).to.equal(apiResponse["6"]["2"][0].id); + }); + }); + + describe("when using IAB Content Taxonomy 1.0", function () { + it("should correctly structure the bids object after a successful API response", function () { + const apiResponse = { + 1: { 1: [{ id: "IAB1" }], 2: [{ id: "IAB1-1" }], 3: [{ id: "IAB1-1-1" }] }, + 4: { 3: [{ id: "49" }, { id: "780" }], 4: [{ id: "431" }], 5: [{ id: "98" }] }, + }; + const bidsConfig = bidsConfiglike(); + const conf = config(); + conf.params.iabContentTaxonomyVersion = "1.0"; + conf.params.websiteToAnalyseUrl = + "https://publisher.works/article.php?get=horrible_url_for_testing&id=5"; + + neuwo.getBidRequestData(bidsConfig, () => {}, conf, "consent data"); + const request = server.requests[0]; + + expect(request.url, "The request URL should be a string").to.be.a("string"); + expect(request.url, "The request URL should include the public API token").to.include( + conf.params.neuwoApiToken + ); + expect(request.url, "The request URL should include the encoded website URL").to.include( + encodeURIComponent(conf.params.websiteToAnalyseUrl) + ); + expect(request.url, "The request URL should include the product identifier").to.include( + "_neuwo_prod=PrebidModule" + ); + expect(request.url, "API should include iabVersions parameter for segtax 1").to.include( + "iabVersions=1" + ); + expect(request.url, "API should include iabVersions parameter for segtax 4").to.include( + "iabVersions=4" + ); + expect(request.method, "API should use GET method").to.equal("GET"); + + request.respond( + 200, + { "Content-Type": "application/json; encoding=UTF-8" }, + JSON.stringify(apiResponse) + ); + + const contentData = bidsConfig.ortb2Fragments.global.site.content.data[0]; + expect(contentData.name, "The data provider name should be correctly set").to.equal( + neuwo.DATA_PROVIDER + ); + expect( + contentData.ext.segtax, + "The segtax value should correspond to IAB Content Taxonomy 1.0" + ).to.equal(1); + expect( + contentData.segment[0].id, + "The first segment ID should match the API response" + ).to.equal(apiResponse["1"]["1"][0].id); + expect( + contentData.segment[1].id, + "The second segment ID should match the API response" + ).to.equal(apiResponse["1"]["2"][0].id); + }); + }); + + describe("when using IAB Content Taxonomy 3.0", function () { + it("should correctly structure the bids object after a successful API response", function () { + const apiResponse = { + 7: { 1: [{ id: "80DV8O" }], 2: [{ id: "90" }], 3: [{ id: "106" }] }, + 4: { 3: [{ id: "49" }, { id: "780" }], 4: [{ id: "431" }], 5: [{ id: "98" }] }, + }; + const bidsConfig = bidsConfiglike(); + const conf = config(); + conf.params.iabContentTaxonomyVersion = "3.0"; + conf.params.websiteToAnalyseUrl = + "https://publisher.works/article.php?get=horrible_url_for_testing&id=5"; + + neuwo.getBidRequestData(bidsConfig, () => {}, conf, "consent data"); + const request = server.requests[0]; + + expect(request.url, "The request URL should be a string").to.be.a("string"); + expect(request.url, "The request URL should include the public API token").to.include( + conf.params.neuwoApiToken + ); + expect(request.url, "The request URL should include the encoded website URL").to.include( + encodeURIComponent(conf.params.websiteToAnalyseUrl) + ); + expect(request.url, "The request URL should include the product identifier").to.include( + "_neuwo_prod=PrebidModule" + ); + expect(request.url, "API should include iabVersions parameter for segtax 7").to.include( + "iabVersions=7" + ); + expect(request.url, "API should include iabVersions parameter for segtax 4").to.include( + "iabVersions=4" + ); + expect(request.method, "API should use GET method").to.equal("GET"); + + request.respond( + 200, + { "Content-Type": "application/json; encoding=UTF-8" }, + JSON.stringify(apiResponse) + ); + + const contentData = bidsConfig.ortb2Fragments.global.site.content.data[0]; + expect(contentData.name, "The data provider name should be correctly set").to.equal( + neuwo.DATA_PROVIDER + ); + expect( + contentData.ext.segtax, + "The segtax value should correspond to IAB Content Taxonomy 3.0" + ).to.equal(7); + expect( + contentData.segment[0].id, + "The first segment ID should match the API response" + ).to.equal(apiResponse["7"]["1"][0].id); + expect( + contentData.segment[1].id, + "The second segment ID should match the API response" + ).to.equal(apiResponse["7"]["2"][0].id); + }); + }); + + describe("when using IAB Audience Taxonomy 1.1", function () { + it("should correctly structure the bids object after a successful API response", function () { + const apiResponse = getNeuwoApiResponse(); + const bidsConfig = bidsConfiglike(); + const conf = config(); + // control xhr api request target for testing + conf.params.websiteToAnalyseUrl = + "https://publisher.works/article.php?get=horrible_url_for_testing&id=5"; + + neuwo.getBidRequestData(bidsConfig, () => {}, conf, "consent data"); + const request = server.requests[0]; + + expect(request.url, "The request URL should be a string").to.be.a("string"); + expect(request.url, "The request URL should include the public API token").to.include( + conf.params.neuwoApiToken + ); + expect(request.url, "The request URL should include the encoded website URL").to.include( + encodeURIComponent(conf.params.websiteToAnalyseUrl) + ); + expect(request.method, "API should use GET method").to.equal("GET"); + + request.respond( + 200, + { "Content-Type": "application/json; encoding=UTF-8" }, + JSON.stringify(apiResponse) + ); + const userData = bidsConfig.ortb2Fragments.global.user.data[0]; + expect(userData.name, "The data provider name should be correctly set").to.equal( + neuwo.DATA_PROVIDER + ); + expect( + userData.ext.segtax, + "The segtax value should correspond to IAB Audience Taxonomy 1.1" + ).to.equal(4); + expect( + userData.segment[0].id, + "The first segment ID should match the API response (tier 3, first item)" + ).to.equal(apiResponse["4"]["3"][0].id); + expect( + userData.segment[1].id, + "The second segment ID should match the API response (tier 3, second item)" + ).to.equal(apiResponse["4"]["3"][1].id); + }); + }); + + it("should not change the bids object structure after an unsuccessful API response", function () { + const bidsConfig = bidsConfiglike(); + const bidsConfigCopy = bidsConfiglike(); + const conf = config(); + + neuwo.getBidRequestData(bidsConfig, () => {}, conf, "consent data"); + const request = server.requests[0]; + request.respond( + 404, + { "Content-Type": "application/json; encoding=UTF-8" }, + JSON.stringify({ detail: "test error" }) + ); + expect( + bidsConfig, + "The bids config object should remain unmodified after a failed API call" + ).to.deep.equal(bidsConfigCopy); + }); + + // OpenRTB 2.5 Feature Tests + describe("OpenRTB 2.5 category fields", function () { + describe("with enableOrtb25Fields enabled (default)", function () { + it("should include iabVersions=1 parameter in API request", function () { + const bidsConfig = bidsConfiglike(); + const conf = config(); + conf.params.websiteToAnalyseUrl = "https://publisher.works/article.php?id=5"; + + neuwo.getBidRequestData(bidsConfig, () => {}, conf, "consent data"); + const request = server.requests[0]; + + expect(request.url, "should include iabVersions=1").to.include("iabVersions=1"); + expect(request.url, "should include iabVersions=6").to.include("iabVersions=6"); + expect(request.url, "should include iabVersions=4").to.include("iabVersions=4"); + }); + + it("should not duplicate iabVersions=1 when iabContentTaxonomyVersion is 1.0", function () { + const bidsConfig = bidsConfiglike(); + const conf = config(); + conf.params.websiteToAnalyseUrl = "https://publisher.works/article.php?id=5"; + conf.params.iabContentTaxonomyVersion = "1.0"; + + neuwo.getBidRequestData(bidsConfig, () => {}, conf, "consent data"); + const request = server.requests[0]; + + const matches = request.url.match(/iabVersions=1(?!\d)/g) || []; + expect(matches.length, "iabVersions=1 should appear exactly once").to.equal(1); + expect(request.url, "should still include iabVersions=4").to.include("iabVersions=4"); + }); + + it("should inject category fields when API returns IAB 1.0 data", function () { + const apiResponse = { + "1": { + "1": [{ id: "IAB12" }], + "2": [{ id: "IAB12-3" }] + }, + "6": { + "1": [{ id: "52" }] + }, + "4": { + "3": [{ id: "49" }] + } + }; + const bidsConfig = bidsConfiglike(); + const conf = config(); + conf.params.websiteToAnalyseUrl = "https://publisher.works/article.php?id=5"; + + neuwo.getBidRequestData(bidsConfig, () => {}, conf, "consent data"); + const request = server.requests[0]; + request.respond( + 200, + { "Content-Type": "application/json; encoding=UTF-8" }, + JSON.stringify(apiResponse) + ); + + const siteCat = bidsConfig.ortb2Fragments.global?.site?.cat; + const contentData = bidsConfig.ortb2Fragments.global?.site?.content?.data?.[0]; + + expect(siteCat, "should have site.cat").to.deep.equal(["IAB12", "IAB12-3"]); + expect(contentData, "should have content data").to.exist; + }); + + it("should send IAB 1.0 filter configuration in URL parameters", function () { + const apiResponse = { + "1": { + "1": [{ id: "IAB12" }], + "2": [{ id: "IAB12-3" }] + }, + "6": { + "1": [{ id: "52" }] + } + }; + const bidsConfig = bidsConfiglike(); + const conf = config(); + conf.params.websiteToAnalyseUrl = "https://publisher.works/article.php?id=5"; + conf.params.iabTaxonomyFilters = { + ContentTier1: { limit: 2 } + }; + + neuwo.getBidRequestData(bidsConfig, () => {}, conf, "consent data"); + + const request = server.requests[0]; + expect(request.url, "should have filter for segtax 1 tier 1").to.include("filter_1_1_limit=2"); + expect(request.url, "should have filter for segtax 6 tier 1").to.include("filter_6_1_limit=2"); + + request.respond( + 200, + { "Content-Type": "application/json; encoding=UTF-8" }, + JSON.stringify(apiResponse) + ); + + const siteCat = bidsConfig.ortb2Fragments.global?.site?.cat; + expect(siteCat, "should inject category fields").to.deep.equal(["IAB12", "IAB12-3"]); + }); + }); + + describe("with enableOrtb25Fields disabled", function () { + it("should not include iabVersions=1 parameter in API request", function () { + const bidsConfig = bidsConfiglike(); + const conf = config(); + conf.params.websiteToAnalyseUrl = "https://publisher.works/article.php?id=5"; + conf.params.enableOrtb25Fields = false; + + neuwo.getBidRequestData(bidsConfig, () => {}, conf, "consent data"); + const request = server.requests[0]; + + expect(request.url, "should not include iabVersions=1").to.not.include("iabVersions=1"); + expect(request.url, "should still include iabVersions=6").to.include("iabVersions=6"); + }); + + it("should not inject category fields even if API returns IAB 1.0 data", function () { + const apiResponse = { + "1": { + "1": [{ id: "IAB12" }] + }, + "6": { + "1": [{ id: "52" }] + } + }; + const bidsConfig = bidsConfiglike(); + const conf = config(); + conf.params.websiteToAnalyseUrl = "https://publisher.works/article.php?id=5"; + conf.params.enableOrtb25Fields = false; + + neuwo.getBidRequestData(bidsConfig, () => {}, conf, "consent data"); + const request = server.requests[0]; + request.respond( + 200, + { "Content-Type": "application/json; encoding=UTF-8" }, + JSON.stringify(apiResponse) + ); + + const siteCat = bidsConfig.ortb2Fragments.global?.site?.cat; + const contentData = bidsConfig.ortb2Fragments.global?.site?.content?.data?.[0]; + + expect(siteCat, "should not have site.cat").to.be.undefined; + expect(contentData, "should still have content data").to.exist; + }); + + it("should not send IAB 1.0 filters in URL parameters", function () { + const bidsConfig = bidsConfiglike(); + const conf = config(); + conf.params.websiteToAnalyseUrl = "https://publisher.works/article.php?id=5"; + conf.params.enableOrtb25Fields = false; + conf.params.iabTaxonomyFilters = { + ContentTier1: { limit: 3 }, + ContentTier2: { limit: 5 } + }; + + neuwo.getBidRequestData(bidsConfig, () => {}, conf, "consent data"); + const request = server.requests[0]; + + expect(request.url, "should not have segtax 1 filters").to.not.match(/filter_1_/); + expect(request.url, "should have segtax 6 filters").to.include("filter_6_1_limit=3"); + expect(request.url, "should have segtax 6 tier 2 filters").to.include("filter_6_2_limit=5"); + }); + }); + }); + }); + + describe("cleanUrl", function () { + describe("when no stripping options are provided", function () { + it("should return the URL unchanged", function () { + const url = "https://example.com/page?foo=bar&baz=qux"; + const result = neuwo.cleanUrl(url, {}); + expect(result, "should return the original URL with all query params intact").to.equal(url); + }); + + it("should return the URL unchanged when options object is empty", function () { + const url = "https://example.com/page?foo=bar"; + const result = neuwo.cleanUrl(url); + expect(result, "should handle missing options parameter").to.equal(url); + }); + }); + + describe("with query parameters edge cases", function () { + it("should strip all query parameters from the URL for `stripAllQueryParams` (edge cases)", function () { + const stripAll = (url) => neuwo.cleanUrl(url, { stripAllQueryParams: true }); + const expected = "https://example.com/page"; + const expectedWithFragment = "https://example.com/page#anchor"; + + // Basic formats + expect(stripAll("https://example.com/page?key=value"), "should remove basic key=value params").to.equal(expected); + expect(stripAll("https://example.com/page?key="), "should remove params with empty value").to.equal(expected); + expect(stripAll("https://example.com/page?key"), "should remove params without equals sign").to.equal(expected); + expect(stripAll("https://example.com/page?=value"), "should remove params with empty key").to.equal(expected); + + // Multiple parameters + expect(stripAll("https://example.com/page?key1=value1&key2=value2"), "should remove multiple different params").to.equal(expected); + expect(stripAll("https://example.com/page?key=value1&key=value2"), "should remove multiple params with same key").to.equal(expected); + + // Special characters and encoding + expect(stripAll("https://example.com/page?key=value%20with%20spaces"), "should remove URL encoded spaces").to.equal(expected); + expect(stripAll("https://example.com/page?key=value+with+plus"), "should remove plus as space").to.equal(expected); + expect(stripAll("https://example.com/page?key=value%3D%26%3F"), "should remove encoded special chars").to.equal(expected); + expect(stripAll("https://example.com/page?key=%"), "should remove incomplete encoding").to.equal(expected); + expect(stripAll("https://example.com/page?key=value%2"), "should remove malformed encoding").to.equal(expected); + + // Delimiters and syntax edge cases + expect(stripAll("https://example.com/page?&key=value"), "should remove params with leading ampersand").to.equal(expected); + expect(stripAll("https://example.com/page?key=value&"), "should remove params with trailing ampersand").to.equal(expected); + expect(stripAll("https://example.com/page?key=value&&key2=value2"), "should remove params with double ampersand").to.equal(expected); + expect(stripAll("https://example.com/page?key=value?key2=value2"), "should remove params with question mark delimiter").to.equal(expected); + expect(stripAll("https://example.com/page?key=value;key2=value2"), "should remove params with semicolon delimiter").to.equal(expected); + + // Empty and missing cases + expect(stripAll("https://example.com/page?"), "should remove question mark alone").to.equal(expected); + expect(stripAll("https://example.com/page??"), "should remove double question mark").to.equal(expected); + expect(stripAll("https://example.com/page"), "should handle URL without query string").to.equal(expected); + + // Unicode and special values + expect(stripAll("https://example.com/page?key=值"), "should remove unicode characters").to.equal(expected); + expect(stripAll("https://example.com/page?key=null"), "should remove string 'null'").to.equal(expected); + expect(stripAll("https://example.com/page?key=undefined"), "should remove string 'undefined'").to.equal(expected); + + // Fragment positioning (fragments are preserved by default) + expect(stripAll("https://example.com/page?key=value#anchor"), "should remove query params and preserve fragment").to.equal(expectedWithFragment); + expect(stripAll("https://example.com/page#anchor?key=value"), "should preserve fragment before params").to.equal("https://example.com/page#anchor?key=value"); + }); + + it("should strip all query parameters from the URL for `stripQueryParamsForDomains` (edge cases)", function () { + const stripAll = (url) => neuwo.cleanUrl(url, { stripQueryParamsForDomains: ["example.com"] }); + const expected = "https://example.com/page"; + const expectedWithFragment = "https://example.com/page#anchor"; + + // Basic formats + expect(stripAll("https://example.com/page?key=value"), "should remove basic key=value params").to.equal(expected); + expect(stripAll("https://example.com/page?key="), "should remove params with empty value").to.equal(expected); + expect(stripAll("https://example.com/page?key"), "should remove params without equals sign").to.equal(expected); + expect(stripAll("https://example.com/page?=value"), "should remove params with empty key").to.equal(expected); + + // Multiple parameters + expect(stripAll("https://example.com/page?key1=value1&key2=value2"), "should remove multiple different params").to.equal(expected); + expect(stripAll("https://example.com/page?key=value1&key=value2"), "should remove multiple params with same key").to.equal(expected); + + // Special characters and encoding + expect(stripAll("https://example.com/page?key=value%20with%20spaces"), "should remove URL encoded spaces").to.equal(expected); + expect(stripAll("https://example.com/page?key=value+with+plus"), "should remove plus as space").to.equal(expected); + expect(stripAll("https://example.com/page?key=value%3D%26%3F"), "should remove encoded special chars").to.equal(expected); + expect(stripAll("https://example.com/page?key=%"), "should remove incomplete encoding").to.equal(expected); + expect(stripAll("https://example.com/page?key=value%2"), "should remove malformed encoding").to.equal(expected); + + // Delimiters and syntax edge cases + expect(stripAll("https://example.com/page?&key=value"), "should remove params with leading ampersand").to.equal(expected); + expect(stripAll("https://example.com/page?key=value&"), "should remove params with trailing ampersand").to.equal(expected); + expect(stripAll("https://example.com/page?key=value&&key2=value2"), "should remove params with double ampersand").to.equal(expected); + expect(stripAll("https://example.com/page?key=value?key2=value2"), "should remove params with question mark delimiter").to.equal(expected); + expect(stripAll("https://example.com/page?key=value;key2=value2"), "should remove params with semicolon delimiter").to.equal(expected); + + // Empty and missing cases + expect(stripAll("https://example.com/page?"), "should remove question mark alone").to.equal(expected); + expect(stripAll("https://example.com/page??"), "should remove double question mark").to.equal(expected); + expect(stripAll("https://example.com/page"), "should handle URL without query string").to.equal(expected); + + // Unicode and special values + expect(stripAll("https://example.com/page?key=值"), "should remove unicode characters").to.equal(expected); + expect(stripAll("https://example.com/page?key=null"), "should remove string 'null'").to.equal(expected); + expect(stripAll("https://example.com/page?key=undefined"), "should remove string 'undefined'").to.equal(expected); + + // Fragment positioning (fragments are preserved by default) + expect(stripAll("https://example.com/page?key=value#anchor"), "should remove query params and preserve fragment").to.equal(expectedWithFragment); + expect(stripAll("https://example.com/page#anchor?key=value"), "should preserve fragment before params").to.equal("https://example.com/page#anchor?key=value"); + }); + + it("should strip all query parameters from the URL for `stripQueryParams` (edge cases)", function () { + const stripAll = (url) => neuwo.cleanUrl(url, { stripQueryParams: ["key", "key1", "key2", "", "?"] }); + const expected = "https://example.com/page"; + const expectedWithFragment = "https://example.com/page#anchor"; + + // Basic formats + expect(stripAll("https://example.com/page?key=value"), "should remove basic key=value params").to.equal(expected); + expect(stripAll("https://example.com/page?key="), "should remove params with empty value").to.equal(expected); + expect(stripAll("https://example.com/page?key"), "should remove params without equals sign").to.equal(expected); + expect(stripAll("https://example.com/page?=value"), "should remove params with empty key").to.equal(expected); + + // Multiple parameters + expect(stripAll("https://example.com/page?key1=value1&key2=value2"), "should remove multiple different params").to.equal(expected); + expect(stripAll("https://example.com/page?key=value1&key=value2"), "should remove multiple params with same key").to.equal(expected); + + // Special characters and encoding + expect(stripAll("https://example.com/page?key=value%20with%20spaces"), "should remove URL encoded spaces").to.equal(expected); + expect(stripAll("https://example.com/page?key=value+with+plus"), "should remove plus as space").to.equal(expected); + expect(stripAll("https://example.com/page?key=value%3D%26%3F"), "should remove encoded special chars").to.equal(expected); + expect(stripAll("https://example.com/page?key=%"), "should remove incomplete encoding").to.equal(expected); + expect(stripAll("https://example.com/page?key=value%2"), "should remove malformed encoding").to.equal(expected); + + // Delimiters and syntax edge cases + expect(stripAll("https://example.com/page?&key=value"), "should remove params with leading ampersand").to.equal(expected); + expect(stripAll("https://example.com/page?key=value&"), "should remove params with trailing ampersand").to.equal(expected); + expect(stripAll("https://example.com/page?key=value&&key2=value2"), "should remove params with double ampersand").to.equal(expected); + expect(stripAll("https://example.com/page?key=value?key2=value2"), "should remove params with question mark delimiter").to.equal(expected); + expect(stripAll("https://example.com/page?key=value;key2=value2"), "should remove params with semicolon delimiter").to.equal(expected); + + // Empty and missing cases + expect(stripAll("https://example.com/page?"), "should remove question mark alone").to.equal(expected); + expect(stripAll("https://example.com/page"), "should handle URL without query string").to.equal(expected); + + // Unicode and special values + expect(stripAll("https://example.com/page?key=值"), "should remove unicode characters").to.equal(expected); + expect(stripAll("https://example.com/page?key=null"), "should remove string 'null'").to.equal(expected); + expect(stripAll("https://example.com/page?key=undefined"), "should remove string 'undefined'").to.equal(expected); + + // Fragment positioning (fragments are preserved by default) + expect(stripAll("https://example.com/page?key=value#anchor"), "should remove query params and preserve fragment").to.equal(expectedWithFragment); + expect(stripAll("https://example.com/page#anchor?key=value"), "should preserve fragment before params").to.equal("https://example.com/page#anchor?key=value"); + }); + }); + + describe("when stripAllQueryParams is true", function () { + it("should strip all query parameters from the URL", function () { + const url = "https://example.com/page?foo=bar&baz=qux&test=123"; + const expected = "https://example.com/page"; + const result = neuwo.cleanUrl(url, { stripAllQueryParams: true }); + expect(result, "should remove all query parameters").to.equal(expected); + }); + + it("should return the URL unchanged if there are no query parameters", function () { + const url = "https://example.com/page"; + const result = neuwo.cleanUrl(url, { stripAllQueryParams: true }); + expect(result, "should handle URLs without query params").to.equal(url); + }); + + it("should preserve the hash fragment when stripping query params without stripFragments", function () { + const url = "https://example.com/page?foo=bar#section"; + const expected = "https://example.com/page#section"; + const result = neuwo.cleanUrl(url, { stripAllQueryParams: true }); + expect(result, "should preserve hash fragments by default").to.equal(expected); + }); + + it("should strip hash fragment when stripFragments is enabled", function () { + const url = "https://example.com/page?foo=bar#section"; + const expected = "https://example.com/page"; + const result = neuwo.cleanUrl(url, { stripAllQueryParams: true, stripFragments: true }); + expect(result, "should strip both query params and fragments").to.equal(expected); + }); + + it("should strip query params but preserve path and protocol", function () { + const url = "https://subdomain.example.com:8080/path/to/page?param=value"; + const expected = "https://subdomain.example.com:8080/path/to/page"; + const result = neuwo.cleanUrl(url, { stripAllQueryParams: true }); + expect(result, "should preserve protocol, domain, port, and path").to.equal(expected); + }); + }); + + describe("when stripQueryParamsForDomains is provided", function () { + it("should strip all query params for exact domain match", function () { + const url = "https://example.com/page?foo=bar&baz=qux"; + const expected = "https://example.com/page"; + const result = neuwo.cleanUrl(url, { + stripQueryParamsForDomains: ["example.com"] + }); + expect(result, "should strip params for exact domain match").to.equal(expected); + }); + + it("should strip all query params for subdomain match", function () { + const url = "https://sub.example.com/page?foo=bar"; + const expected = "https://sub.example.com/page"; + const result = neuwo.cleanUrl(url, { + stripQueryParamsForDomains: ["example.com"] + }); + expect(result, "should strip params for subdomains").to.equal(expected); + }); + + it("should not strip query params if domain does not match", function () { + const url = "https://other.com/page?foo=bar"; + const result = neuwo.cleanUrl(url, { + stripQueryParamsForDomains: ["example.com"] + }); + expect(result, "should preserve params for non-matching domains").to.equal(url); + }); + + it("should not strip query params if subdomain is provided for domain", function () { + const url = "https://example.com/page?foo=bar"; + const result = neuwo.cleanUrl(url, { + stripQueryParamsForDomains: ["sub.example.com"] + }); + expect(result, "should preserve params for domain when subdomain is provided").to.equal(url); + }); + + it("should handle multiple domains in the list", function () { + const url1 = "https://example.com/page?foo=bar"; + const url2 = "https://test.com/page?foo=bar"; + const url3 = "https://other.com/page?foo=bar"; + const domains = ["example.com", "test.com"]; + + const result1 = neuwo.cleanUrl(url1, { stripQueryParamsForDomains: domains }); + const result2 = neuwo.cleanUrl(url2, { stripQueryParamsForDomains: domains }); + const result3 = neuwo.cleanUrl(url3, { stripQueryParamsForDomains: domains }); + + expect(result1, "should strip params for first domain").to.equal("https://example.com/page"); + expect(result2, "should strip params for second domain").to.equal("https://test.com/page"); + expect(result3, "should preserve params for non-listed domain").to.equal(url3); + }); + + it("should handle deep subdomains correctly", function () { + const url = "https://deep.sub.example.com/page?foo=bar"; + const expected = "https://deep.sub.example.com/page"; + const result1 = neuwo.cleanUrl(url, { + stripQueryParamsForDomains: ["example.com"] + }); + const result2 = neuwo.cleanUrl(url, { + stripQueryParamsForDomains: ["sub.example.com"] + }); + expect(result1, "should strip params for deep subdomains with domain matching").to.equal(expected); + expect(result2, "should strip params for deep subdomains with subdomain matching").to.equal(expected); + }); + + it("should not match partial domain names", function () { + const url = "https://notexample.com/page?foo=bar"; + const result = neuwo.cleanUrl(url, { + stripQueryParamsForDomains: ["example.com"] + }); + expect(result, "should not match partial domain strings").to.equal(url); + }); + + it("should handle empty domain list", function () { + const url = "https://example.com/page?foo=bar"; + const result = neuwo.cleanUrl(url, { stripQueryParamsForDomains: [] }); + expect(result, "should not strip params with empty domain list").to.equal(url); + }); + }); + + describe("when stripQueryParams is provided", function () { + it("should strip only specified query parameters", function () { + const url = "https://example.com/page?foo=bar&baz=qux&keep=this"; + const expected = "https://example.com/page?keep=this"; + const result = neuwo.cleanUrl(url, { + stripQueryParams: ["foo", "baz"] + }); + expect(result, "should remove only specified params").to.equal(expected); + }); + + it("should handle single parameter stripping", function () { + const url = "https://example.com/page?remove=this&keep=that"; + const expected = "https://example.com/page?keep=that"; + const result = neuwo.cleanUrl(url, { + stripQueryParams: ["remove"] + }); + expect(result, "should remove single specified param").to.equal(expected); + }); + + it("should return URL without query string if all params are stripped", function () { + const url = "https://example.com/page?foo=bar&baz=qux"; + const expected = "https://example.com/page"; + const result = neuwo.cleanUrl(url, { + stripQueryParams: ["foo", "baz"] + }); + expect(result, "should remove query string when all params stripped").to.equal(expected); + }); + + it("should handle case where specified params do not exist", function () { + const url = "https://example.com/page?foo=bar"; + const result = neuwo.cleanUrl(url, { + stripQueryParams: ["nonexistent", "alsonothere"] + }); + expect(result, "should handle non-existent params gracefully").to.equal(url); + }); + + it("should handle empty param list", function () { + const url = "https://example.com/page?foo=bar"; + const result = neuwo.cleanUrl(url, { stripQueryParams: [] }); + expect(result, "should not strip params with empty list").to.equal(url); + }); + + it("should preserve param order for remaining params", function () { + const url = "https://example.com/page?a=1&b=2&c=3&d=4"; + const result = neuwo.cleanUrl(url, { + stripQueryParams: ["b", "d"] + }); + expect(result, "should preserve order of remaining params").to.include("a=1"); + expect(result, "should preserve order of remaining params").to.include("c=3"); + expect(result, "should not include stripped param b").to.not.include("b=2"); + expect(result, "should not include stripped param d").to.not.include("d=4"); + }); + }); + + describe("error handling", function () { + it("should return null or undefined input unchanged", function () { + expect(neuwo.cleanUrl(null, {}), "should handle null input").to.equal(null); + expect(neuwo.cleanUrl(undefined, {}), "should handle undefined input").to.equal(undefined); + expect(neuwo.cleanUrl("", {}), "should handle empty string").to.equal(""); + }); + + it("should return invalid URL unchanged and log error", function () { + const invalidUrl = "not-a-valid-url"; + const result = neuwo.cleanUrl(invalidUrl, { stripAllQueryParams: true }); + expect(result, "should return invalid URL unchanged").to.equal(invalidUrl); + }); + + it("should handle malformed URLs gracefully", function () { + const malformedUrl = "http://"; + const result = neuwo.cleanUrl(malformedUrl, { stripAllQueryParams: true }); + expect(result, "should return malformed URL unchanged").to.equal(malformedUrl); + }); + }); + + describe("when stripFragments is enabled", function () { + it("should strip URL fragments from URLs without query params", function () { + const url = "https://example.com/page#section"; + const expected = "https://example.com/page"; + const result = neuwo.cleanUrl(url, { stripFragments: true }); + expect(result, "should remove hash fragment").to.equal(expected); + }); + + it("should strip URL fragments from URLs with query params", function () { + const url = "https://example.com/page?foo=bar#section"; + const expected = "https://example.com/page?foo=bar"; + const result = neuwo.cleanUrl(url, { stripFragments: true }); + expect(result, "should remove hash fragment and preserve query params").to.equal(expected); + }); + + it("should strip fragments when combined with stripAllQueryParams", function () { + const url = "https://example.com/page?foo=bar#section"; + const expected = "https://example.com/page"; + const result = neuwo.cleanUrl(url, { stripAllQueryParams: true, stripFragments: true }); + expect(result, "should remove both query params and fragment").to.equal(expected); + }); + + it("should strip fragments when combined with stripQueryParamsForDomains", function () { + const url = "https://example.com/page?foo=bar#section"; + const expected = "https://example.com/page"; + const result = neuwo.cleanUrl(url, { + stripQueryParamsForDomains: ["example.com"], + stripFragments: true + }); + expect(result, "should remove both query params and fragment for matching domain").to.equal(expected); + }); + + it("should strip fragments when combined with stripQueryParams", function () { + const url = "https://example.com/page?foo=bar&keep=this#section"; + const expected = "https://example.com/page?keep=this"; + const result = neuwo.cleanUrl(url, { + stripQueryParams: ["foo"], + stripFragments: true + }); + expect(result, "should remove specified query params and fragment").to.equal(expected); + }); + + it("should handle URLs without fragments gracefully", function () { + const url = "https://example.com/page?foo=bar"; + const expected = "https://example.com/page?foo=bar"; + const result = neuwo.cleanUrl(url, { stripFragments: true }); + expect(result, "should handle URLs without fragments").to.equal(expected); + }); + + it("should handle empty fragments", function () { + const url = "https://example.com/page#"; + const expected = "https://example.com/page"; + const result = neuwo.cleanUrl(url, { stripFragments: true }); + expect(result, "should remove empty fragment").to.equal(expected); + }); + + it("should handle complex fragments with special characters", function () { + const url = "https://example.com/page?foo=bar#section-1/subsection?query"; + const expected = "https://example.com/page?foo=bar"; + const result = neuwo.cleanUrl(url, { stripFragments: true }); + expect(result, "should remove complex fragments").to.equal(expected); + }); + }); + + describe("option priority", function () { + it("should apply stripAllQueryParams first when multiple options are set", function () { + const url = "https://example.com/page?foo=bar&baz=qux"; + const expected = "https://example.com/page"; + const result = neuwo.cleanUrl(url, { + stripAllQueryParams: true, + stripQueryParams: ["foo"] + }); + expect(result, "stripAllQueryParams should take precedence").to.equal(expected); + }); + + it("should apply stripQueryParamsForDomains before stripQueryParams", function () { + const url = "https://example.com/page?foo=bar&baz=qux"; + const expected = "https://example.com/page"; + const result = neuwo.cleanUrl(url, { + stripQueryParamsForDomains: ["example.com"], + stripQueryParams: ["foo"] + }); + expect(result, "domain-specific stripping should take precedence").to.equal(expected); + }); + + it("should not strip for non-matching domain even with stripQueryParams set", function () { + const url = "https://other.com/page?foo=bar&baz=qux"; + const expected = "https://other.com/page?baz=qux"; + const result = neuwo.cleanUrl(url, { + stripQueryParamsForDomains: ["example.com"], + stripQueryParams: ["foo"] + }); + expect(result, "should fall through to stripQueryParams for non-matching domain").to.equal(expected); + }); + }); + }); + + // Integration Tests + describe("injectIabCategories edge cases and merging", function () { + it("should not inject data if response contains no segments", function () { + const apiResponse = { "6": {}, "4": {} }; // Empty response (no segments) + const bidsConfig = bidsConfiglike(); + const bidsConfigCopy = bidsConfiglike(); + const conf = config(); + + neuwo.getBidRequestData(bidsConfig, () => {}, conf, "consent data"); + const request = server.requests[0]; + request.respond( + 200, + { "Content-Type": "application/json; encoding=UTF-8" }, + JSON.stringify(apiResponse) + ); + + // After a successful response with no segments, the global ortb2 fragments should remain empty + // as the data injection logic only injects when segments exist + expect( + bidsConfig.ortb2Fragments.global, + "The global ORTB fragments should remain empty" + ).to.deep.equal(bidsConfigCopy.ortb2Fragments.global); + }); + + it("should append content and user data to existing ORTB fragments", function () { + const apiResponse = getNeuwoApiResponse(); + const bidsConfig = bidsConfiglike(); + // Simulate existing first-party data from another source/module + const existingContentData = { name: "other_content_provider", segment: [{ id: "1" }] }; + const existingUserData = { name: "other_user_provider", segment: [{ id: "2" }] }; + + bidsConfig.ortb2Fragments.global = { + site: { + content: { + data: [existingContentData], + }, + }, + user: { + data: [existingUserData], + }, + }; + const conf = config(); + + neuwo.getBidRequestData(bidsConfig, () => {}, conf, "consent data"); + const request = server.requests[0]; + request.respond( + 200, + { "Content-Type": "application/json; encoding=UTF-8" }, + JSON.stringify(apiResponse) + ); + + const siteData = bidsConfig.ortb2Fragments.global.site.content.data; + const userData = bidsConfig.ortb2Fragments.global.user.data; + + // Check that the existing data is still there (index 0) + expect(siteData[0], "Existing site.content.data should be preserved").to.deep.equal( + existingContentData + ); + expect(userData[0], "Existing user.data should be preserved").to.deep.equal(existingUserData); + + // Check that the new Neuwo data is appended (index 1) + expect(siteData.length, "site.content.data array should have 2 entries").to.equal(2); + expect(userData.length, "user.data array should have 2 entries").to.equal(2); + expect(siteData[1].name, "The appended content data should be from Neuwo").to.equal( + neuwo.DATA_PROVIDER + ); + expect(userData[1].name, "The appended user data should be from Neuwo").to.equal( + neuwo.DATA_PROVIDER + ); + }); + + it("should correctly construct API URL when neuwoApiUrl already contains query parameters", function () { + const apiResponse = getNeuwoApiResponse(); + const bidsConfig = bidsConfiglike(); + const conf = config(); + // Set API URL that already has query parameters + conf.params.neuwoApiUrl = "https://edge.neuwo.ai/api/aitopics/edge/v1/iab?environment=production"; + conf.params.websiteToAnalyseUrl = "https://publisher.works/article.php?id=5"; + + neuwo.getBidRequestData(bidsConfig, () => {}, conf, "consent data"); + const request = server.requests[0]; + + // Should use & as joiner instead of ? + expect(request.url, "URL should contain environment param from base URL").to.include("environment=production"); + expect(request.url, "URL should contain token param joined with &").to.include("&token="); + expect(request.url, "URL should contain url param").to.include("&url="); + expect(request.url, "URL should contain product identifier").to.include("&_neuwo_prod=PrebidModule"); + expect(request.url, "URL should include iabVersions parameter").to.include("iabVersions="); + // Should not have ?? in the URL + expect(request.url, "URL should not contain double question marks").to.not.include("??"); + + request.respond( + 200, + { "Content-Type": "application/json; encoding=UTF-8" }, + JSON.stringify(apiResponse) + ); + + const contentData = bidsConfig.ortb2Fragments.global.site.content.data[0]; + expect(contentData.name, "Should successfully process response").to.equal(neuwo.DATA_PROVIDER); + }); + + it("should treat a legacy URL with /v1/iab in query params as a legacy endpoint", function () { + const bidsConfig = bidsConfiglike(); + const conf = config(); + // Proxy URL where /v1/iab appears in query params, not the path + conf.params.neuwoApiUrl = "https://proxy.example.com/api?redirect=/v1/iab"; + conf.params.websiteToAnalyseUrl = "https://publisher.works/article.php"; + + neuwo.getBidRequestData(bidsConfig, () => {}, conf, "consent data"); + const request = server.requests[0]; + + // Legacy endpoints should NOT include iabVersions params + expect(request.url, "should not include iabVersions for legacy endpoint").to.not.include("iabVersions="); + // Should still include token and url params + expect(request.url, "should include token param").to.include("token="); + expect(request.url, "should include url param").to.include("url="); + }); + + it("should detect /v1/iab endpoint from a malformed URL using fallback parsing", function () { + const bidsConfig = bidsConfiglike(); + const conf = config(); + // Malformed URL that causes new URL() to throw, but has /v1/iab in path + conf.params.neuwoApiUrl = "/v1/iab"; + conf.params.websiteToAnalyseUrl = "https://publisher.works/article.php"; + + neuwo.getBidRequestData(bidsConfig, () => {}, conf, "consent data"); + const request = server.requests[0]; + + // Fallback parser should detect /v1/iab in path portion and treat as IAB endpoint + expect(request.url, "should include iabVersions for IAB endpoint").to.include("iabVersions="); + }); + }); + + describe("getBidRequestData with caching", function () { + describe("when enableCache is true (default)", function () { + it("should cache the API response and reuse it on subsequent calls", function () { + const apiResponse = getNeuwoApiResponse(); + const bidsConfig1 = bidsConfiglike(); + const bidsConfig2 = bidsConfiglike(); + const conf = config(); + conf.params.websiteToAnalyseUrl = "https://publisher.works/article.php?id=1"; + + // First call should make an API request + neuwo.getBidRequestData(bidsConfig1, () => {}, conf, "consent data"); + expect(server.requests.length, "First call should make an API request").to.equal(1); + + const request1 = server.requests[0]; + request1.respond( + 200, + { "Content-Type": "application/json; encoding=UTF-8" }, + JSON.stringify(apiResponse) + ); + + // Second call should use cached response (no new API request) + neuwo.getBidRequestData(bidsConfig2, () => {}, conf, "consent data"); + expect(server.requests.length, "Second call should not make a new API request").to.equal(1); + + // Both configs should have identical data (second served from cache) + const contentData1 = bidsConfig1.ortb2Fragments.global.site.content.data[0]; + const contentData2 = bidsConfig2.ortb2Fragments.global.site.content.data[0]; + expect(contentData1, "First config should have Neuwo data").to.exist; + expect(contentData2, "Second config should have Neuwo data from cache").to.exist; + expect(contentData1.name, "First config should have correct provider").to.equal(neuwo.DATA_PROVIDER); + expect(contentData2.name, "Second config should have correct provider").to.equal(neuwo.DATA_PROVIDER); + expect(contentData1.segment, "Cached data should have same segments as original").to.deep.equal(contentData2.segment); + }); + + it("should cache when enableCache is explicitly set to true", function () { + const apiResponse = getNeuwoApiResponse(); + const bidsConfig1 = bidsConfiglike(); + const bidsConfig2 = bidsConfiglike(); + const conf = config(); + conf.params.websiteToAnalyseUrl = "https://publisher.works/article.php?id=2"; + conf.params.enableCache = true; + + // First call + neuwo.getBidRequestData(bidsConfig1, () => {}, conf, "consent data"); + expect(server.requests.length, "First call should make an API request").to.equal(1); + + const request1 = server.requests[0]; + request1.respond( + 200, + { "Content-Type": "application/json; encoding=UTF-8" }, + JSON.stringify(apiResponse) + ); + + // Second call should use cache + neuwo.getBidRequestData(bidsConfig2, () => {}, conf, "consent data"); + expect(server.requests.length, "Second call should use cached response").to.equal(1); + }); + + it("should handle concurrent requests by sharing a pending request promise", function (done) { + const apiResponse = getNeuwoApiResponse(); + const bidsConfig1 = bidsConfiglike(); + const bidsConfig2 = bidsConfiglike(); + const bidsConfig3 = bidsConfiglike(); + const conf = config(); + conf.params.websiteToAnalyseUrl = "https://publisher.works/article.php?id=concurrent"; + conf.params.enableCache = true; + + let callbackCount = 0; + const callback = () => { + callbackCount++; + if (callbackCount === 3) { + // All callbacks have been called, now verify the data + try { + const contentData1 = bidsConfig1.ortb2Fragments.global.site.content.data[0]; + const contentData2 = bidsConfig2.ortb2Fragments.global.site.content.data[0]; + const contentData3 = bidsConfig3.ortb2Fragments.global.site.content.data[0]; + + expect(contentData1, "First config should have Neuwo data").to.exist; + expect(contentData2, "Second config should have Neuwo data from pending request").to.exist; + expect(contentData3, "Third config should have Neuwo data from pending request").to.exist; + expect(contentData1.name, "First config should have correct provider").to.equal(neuwo.DATA_PROVIDER); + expect(contentData2.name, "Second config should have correct provider").to.equal(neuwo.DATA_PROVIDER); + expect(contentData3.name, "Third config should have correct provider").to.equal(neuwo.DATA_PROVIDER); + done(); + } catch (e) { + done(e); + } + } + }; + + // Make three concurrent calls before responding to the first request + neuwo.getBidRequestData(bidsConfig1, callback, conf, "consent data"); + neuwo.getBidRequestData(bidsConfig2, callback, conf, "consent data"); + neuwo.getBidRequestData(bidsConfig3, callback, conf, "consent data"); + + // Only one API request should be made + expect(server.requests.length, "Only one API request should be made for concurrent calls").to.equal(1); + + const request = server.requests[0]; + request.respond( + 200, + { "Content-Type": "application/json; encoding=UTF-8" }, + JSON.stringify(apiResponse) + ); + }); + + it("should transition through all three cache states: pending request, then cached response", function (done) { + const apiResponse = getNeuwoApiResponse(); + const bidsConfig1 = bidsConfiglike(); + const bidsConfig2 = bidsConfiglike(); + const bidsConfig3 = bidsConfiglike(); + const conf = config(); + conf.params.websiteToAnalyseUrl = "https://publisher.works/article.php?id=three-stage"; + conf.params.enableCache = true; + + let callback1and2Count = 0; + + const callback1and2 = () => { + callback1and2Count++; + if (callback1and2Count === 2) { + // Both first and second callbacks have been called + // Stage 3: Third request should use cached response (not pending request) + neuwo.getBidRequestData(bidsConfig3, () => { + try { + expect(server.requests.length, "Third call should use cache and not make a new API request").to.equal(1); + + // All three configs should have the same data + const contentData1 = bidsConfig1.ortb2Fragments.global.site.content.data[0]; + const contentData2 = bidsConfig2.ortb2Fragments.global.site.content.data[0]; + const contentData3 = bidsConfig3.ortb2Fragments.global.site.content.data[0]; + + expect(contentData1, "First config should have Neuwo data").to.exist; + expect(contentData2, "Second config should have Neuwo data from pending request").to.exist; + expect(contentData3, "Third config should have Neuwo data from cache").to.exist; + expect(contentData1.name, "First config should have correct provider").to.equal(neuwo.DATA_PROVIDER); + expect(contentData2.name, "Second config should have correct provider").to.equal(neuwo.DATA_PROVIDER); + expect(contentData3.name, "Third config should have correct provider").to.equal(neuwo.DATA_PROVIDER); + done(); + } catch (e) { + done(e); + } + }, conf, "consent data"); + } + }; + + // Stage 1: First request initiates API call (creates pending request) + neuwo.getBidRequestData(bidsConfig1, callback1and2, conf, "consent data"); + expect(server.requests.length, "First call should make an API request").to.equal(1); + + // Stage 2: Second request should attach to pending request before response + neuwo.getBidRequestData(bidsConfig2, callback1and2, conf, "consent data"); + expect(server.requests.length, "Second call should not make a new API request").to.equal(1); + + // Respond to the API request, which populates the cache + const request = server.requests[0]; + request.respond( + 200, + { "Content-Type": "application/json; encoding=UTF-8" }, + JSON.stringify(apiResponse) + ); + }); + + it("should not share cache between requests with different parameters", function (done) { + const apiResponse1 = getNeuwoApiResponse(); + const apiResponse2 = getNeuwoApiResponse(); + const bidsConfig1 = bidsConfiglike(); + const bidsConfig2 = bidsConfiglike(); + const conf1 = config(); + conf1.params.websiteToAnalyseUrl = "https://publisher.works/page-a"; + conf1.params.enableCache = true; + const conf2 = config(); + conf2.params.websiteToAnalyseUrl = "https://publisher.works/page-b"; + conf2.params.enableCache = true; + + let callbackCount = 0; + const callback = () => { + callbackCount++; + if (callbackCount === 2) { + try { + // Both should have made separate API requests + expect(server.requests.length, "Should make two separate API requests for different URLs").to.equal(2); + expect(server.requests[0].url).to.contain("page-a"); + expect(server.requests[1].url).to.contain("page-b"); + done(); + } catch (e) { + done(e); + } + } + }; + + // Two concurrent calls with different URLs + neuwo.getBidRequestData(bidsConfig1, callback, conf1, "consent data"); + neuwo.getBidRequestData(bidsConfig2, callback, conf2, "consent data"); + + expect(server.requests.length, "Should make two separate API requests").to.equal(2); + + server.requests[0].respond( + 200, + { "Content-Type": "application/json; encoding=UTF-8" }, + JSON.stringify(apiResponse1) + ); + server.requests[1].respond( + 200, + { "Content-Type": "application/json; encoding=UTF-8" }, + JSON.stringify(apiResponse2) + ); + }); + + it("should use cache for same URL but make new request after config change", function () { + const apiResponse = getNeuwoApiResponse(); + const bidsConfig1 = bidsConfiglike(); + const bidsConfig2 = bidsConfiglike(); + const bidsConfig3 = bidsConfiglike(); + const conf = config(); + conf.params.websiteToAnalyseUrl = "https://publisher.works/page-a"; + conf.params.enableCache = true; + + // First call + neuwo.getBidRequestData(bidsConfig1, () => {}, conf, "consent data"); + expect(server.requests.length).to.equal(1); + server.requests[0].respond( + 200, + { "Content-Type": "application/json; encoding=UTF-8" }, + JSON.stringify(apiResponse) + ); + + // Second call with same URL - should use cache + neuwo.getBidRequestData(bidsConfig2, () => {}, conf, "consent data"); + expect(server.requests.length, "Same URL should use cache").to.equal(1); + + // Third call with different URL (simulating config change) - should make new request + conf.params.websiteToAnalyseUrl = "https://publisher.works/page-b"; + neuwo.getBidRequestData(bidsConfig3, () => {}, conf, "consent data"); + expect(server.requests.length, "Different URL should make new request").to.equal(2); + expect(server.requests[1].url).to.contain("page-b"); + }); + + it("should not share cache when iabContentTaxonomyVersion changes", function () { + const apiResponse = getNeuwoApiResponse(); + const bidsConfig1 = bidsConfiglike(); + const bidsConfig2 = bidsConfiglike(); + const bidsConfig3 = bidsConfiglike(); + const conf = config(); + conf.params.websiteToAnalyseUrl = "https://publisher.works/same-page"; + conf.params.enableCache = true; + conf.params.iabContentTaxonomyVersion = "2.2"; + + // First call with taxonomy 2.2 + neuwo.getBidRequestData(bidsConfig1, () => {}, conf, "consent data"); + expect(server.requests.length).to.equal(1); + expect(server.requests[0].url).to.contain("iabVersions=6"); // segtax 6 = taxonomy 2.2 + server.requests[0].respond( + 200, + { "Content-Type": "application/json; encoding=UTF-8" }, + JSON.stringify(apiResponse) + ); + + // Second call with same taxonomy - should use cache + neuwo.getBidRequestData(bidsConfig2, () => {}, conf, "consent data"); + expect(server.requests.length, "Same taxonomy version should use cache").to.equal(1); + + // Third call with different taxonomy version - should make new request + conf.params.iabContentTaxonomyVersion = "3.0"; + neuwo.getBidRequestData(bidsConfig3, () => {}, conf, "consent data"); + expect(server.requests.length, "Different taxonomy version should make new request").to.equal(2); + expect(server.requests[1].url).to.contain("iabVersions=7"); // segtax 7 = taxonomy 3.0 + }); + + it("should evict the oldest cache entry when MAX_CACHE_ENTRIES is exceeded", async function () { + const apiResponse = getNeuwoApiResponse(); + const conf = config(); + conf.params.enableCache = true; + + // Fill cache with 10 entries (MAX_CACHE_ENTRIES) + for (let i = 0; i < 10; i++) { + const bidsConfig = bidsConfiglike(); + conf.params.websiteToAnalyseUrl = "https://publisher.works/page-" + i; + neuwo.getBidRequestData(bidsConfig, () => {}, conf, "consent data"); + server.requests[i].respond( + 200, + { "Content-Type": "application/json; encoding=UTF-8" }, + JSON.stringify(apiResponse) + ); + } + // Flush microtasks so pendingRequests are cleaned up via .finally() handlers + await Promise.resolve(); + + expect(server.requests.length, "Should have made 10 API requests").to.equal(10); + + // Verify page-0 is cached + const bidsConfigCached = bidsConfiglike(); + conf.params.websiteToAnalyseUrl = "https://publisher.works/page-0"; + neuwo.getBidRequestData(bidsConfigCached, () => {}, conf, "consent data"); + expect(server.requests.length, "page-0 should be served from cache").to.equal(10); + + // Add 11th entry to trigger eviction of the oldest (page-0) + const bidsConfig11 = bidsConfiglike(); + conf.params.websiteToAnalyseUrl = "https://publisher.works/page-10"; + neuwo.getBidRequestData(bidsConfig11, () => {}, conf, "consent data"); + expect(server.requests.length, "page-10 should trigger new request").to.equal(11); + server.requests[10].respond( + 200, + { "Content-Type": "application/json; encoding=UTF-8" }, + JSON.stringify(apiResponse) + ); + await Promise.resolve(); + + // page-0 should have been evicted and require a new request + const bidsConfigEvicted = bidsConfiglike(); + conf.params.websiteToAnalyseUrl = "https://publisher.works/page-0"; + neuwo.getBidRequestData(bidsConfigEvicted, () => {}, conf, "consent data"); + expect(server.requests.length, "page-0 should be evicted and trigger new request").to.equal(12); + + // page-1 should still be cached + const bidsConfigStillCached = bidsConfiglike(); + conf.params.websiteToAnalyseUrl = "https://publisher.works/page-1"; + neuwo.getBidRequestData(bidsConfigStillCached, () => {}, conf, "consent data"); + expect(server.requests.length, "page-1 should still be in cache").to.equal(12); + }); + }); + + describe("when enableCache is false", function () { + it("should not cache the API response and make a new request each time", function () { + const apiResponse = getNeuwoApiResponse(); + const bidsConfig1 = bidsConfiglike(); + const bidsConfig2 = bidsConfiglike(); + const conf = config(); + conf.params.websiteToAnalyseUrl = "https://publisher.works/article.php?id=3"; + conf.params.enableCache = false; + + // First call should make an API request + neuwo.getBidRequestData(bidsConfig1, () => {}, conf, "consent data"); + expect(server.requests.length, "First call should make an API request").to.equal(1); + + const request1 = server.requests[0]; + request1.respond( + 200, + { "Content-Type": "application/json; encoding=UTF-8" }, + JSON.stringify(apiResponse) + ); + + // Second call should make a new API request (not use cache) + neuwo.getBidRequestData(bidsConfig2, () => {}, conf, "consent data"); + expect(server.requests.length, "Second call should make a new API request").to.equal(2); + + const request2 = server.requests[1]; + request2.respond( + 200, + { "Content-Type": "application/json; encoding=UTF-8" }, + JSON.stringify(apiResponse) + ); + + // Both configs should have the same data structure + const contentData1 = bidsConfig1.ortb2Fragments.global.site.content.data[0]; + const contentData2 = bidsConfig2.ortb2Fragments.global.site.content.data[0]; + expect(contentData1, "First config should have Neuwo data").to.exist; + expect(contentData2, "Second config should have Neuwo data from new request").to.exist; + expect(contentData1.name, "First config should have correct provider").to.equal(neuwo.DATA_PROVIDER); + expect(contentData2.name, "Second config should have correct provider").to.equal(neuwo.DATA_PROVIDER); + }); + + it("should bypass existing cache when enableCache is false", function () { + const apiResponse = getNeuwoApiResponse(); + const bidsConfig1 = bidsConfiglike(); + const bidsConfig2 = bidsConfiglike(); + const bidsConfig3 = bidsConfiglike(); + const conf = config(); + conf.params.websiteToAnalyseUrl = "https://publisher.works/article.php?id=4"; + + // First call with caching enabled (default) + neuwo.getBidRequestData(bidsConfig1, () => {}, conf, "consent data"); + expect(server.requests.length, "First call should make an API request").to.equal(1); + + const request1 = server.requests[0]; + request1.respond( + 200, + { "Content-Type": "application/json; encoding=UTF-8" }, + JSON.stringify(apiResponse) + ); + + // Second call with caching enabled should use cache + neuwo.getBidRequestData(bidsConfig2, () => {}, conf, "consent data"); + expect(server.requests.length, "Second call should use cache").to.equal(1); + + // Third call with caching disabled should bypass cache + conf.params.enableCache = false; + neuwo.getBidRequestData(bidsConfig3, () => {}, conf, "consent data"); + expect(server.requests.length, "Third call should bypass cache and make new request").to.equal(2); + + const request2 = server.requests[1]; + request2.respond( + 200, + { "Content-Type": "application/json; encoding=UTF-8" }, + JSON.stringify(apiResponse) + ); }); - it("should handle empty param list", function () { - const url = "https://example.com/page?foo=bar"; - const result = neuwo.cleanUrl(url, { stripQueryParams: [] }); - expect(result, "should not strip params with empty list").to.equal(url); - }); + it("should clear pending request after error response and retry on next call", function (done) { + const bidsConfig1 = bidsConfiglike(); + const bidsConfig2 = bidsConfiglike(); + const conf = config(); + conf.params.websiteToAnalyseUrl = "https://publisher.works/article.php?id=error-test"; + conf.params.enableCache = true; - it("should preserve param order for remaining params", function () { - const url = "https://example.com/page?a=1&b=2&c=3&d=4"; - const result = neuwo.cleanUrl(url, { - stripQueryParams: ["b", "d"] - }); - expect(result, "should preserve order of remaining params").to.include("a=1"); - expect(result, "should preserve order of remaining params").to.include("c=3"); - expect(result, "should not include stripped param b").to.not.include("b=2"); - expect(result, "should not include stripped param d").to.not.include("d=4"); - }); - }); + // First call - will get 404 error + neuwo.getBidRequestData(bidsConfig1, () => { + // After error, data should not be injected + const contentData = bidsConfig1.ortb2Fragments.global?.site?.content?.data; + expect(contentData, "No data should be injected after error").to.be.undefined; + + // Second call - should retry API (pending should be cleared) + neuwo.getBidRequestData(bidsConfig2, () => { + try { + expect(server.requests.length, "Second call should retry after error").to.equal(2); + const contentData2 = bidsConfig2.ortb2Fragments.global?.site?.content?.data?.[0]; + expect(contentData2, "Second call should have Neuwo data after retry").to.exist; + expect(contentData2.name, "Second call should have correct provider").to.equal(neuwo.DATA_PROVIDER); + done(); + } catch (e) { + done(e); + } + }, conf, "consent data"); + + // Respond with success to second request + const request2 = server.requests[1]; + request2.respond( + 200, + { "Content-Type": "application/json; encoding=UTF-8" }, + JSON.stringify(getNeuwoApiResponse()) + ); + }, conf, "consent data"); - describe("error handling", function () { - it("should return null or undefined input unchanged", function () { - expect(neuwo.cleanUrl(null, {}), "should handle null input").to.equal(null); - expect(neuwo.cleanUrl(undefined, {}), "should handle undefined input").to.equal(undefined); - expect(neuwo.cleanUrl("", {}), "should handle empty string").to.equal(""); - }); + expect(server.requests.length, "First call should make an API request").to.equal(1); - it("should return invalid URL unchanged and log error", function () { - const invalidUrl = "not-a-valid-url"; - const result = neuwo.cleanUrl(invalidUrl, { stripAllQueryParams: true }); - expect(result, "should return invalid URL unchanged").to.equal(invalidUrl); + // Respond with error to first request + const request1 = server.requests[0]; + request1.respond( + 404, + { "Content-Type": "application/json; encoding=UTF-8" }, + JSON.stringify({ error: "Not found" }) + ); }); - it("should handle malformed URLs gracefully", function () { - const malformedUrl = "http://"; - const result = neuwo.cleanUrl(malformedUrl, { stripAllQueryParams: true }); - expect(result, "should return malformed URL unchanged").to.equal(malformedUrl); + it("should handle concurrent requests when API returns error", function (done) { + const bidsConfig1 = bidsConfiglike(); + const bidsConfig2 = bidsConfiglike(); + const bidsConfig3 = bidsConfiglike(); + const conf = config(); + conf.params.websiteToAnalyseUrl = "https://publisher.works/article.php?id=concurrent-error"; + conf.params.enableCache = true; + + let callbackCount = 0; + const callback = () => { + callbackCount++; + if (callbackCount === 3) { + try { + // None of the configs should have data after error + const contentData1 = bidsConfig1.ortb2Fragments.global?.site?.content?.data; + const contentData2 = bidsConfig2.ortb2Fragments.global?.site?.content?.data; + const contentData3 = bidsConfig3.ortb2Fragments.global?.site?.content?.data; + + expect(contentData1, "First config should not have data after error").to.be.undefined; + expect(contentData2, "Second config should not have data after error").to.be.undefined; + expect(contentData3, "Third config should not have data after error").to.be.undefined; + done(); + } catch (e) { + done(e); + } + } + }; + + // Make three concurrent calls + neuwo.getBidRequestData(bidsConfig1, callback, conf, "consent data"); + neuwo.getBidRequestData(bidsConfig2, callback, conf, "consent data"); + neuwo.getBidRequestData(bidsConfig3, callback, conf, "consent data"); + + expect(server.requests.length, "Only one API request should be made").to.equal(1); + + // Respond with error + const request = server.requests[0]; + request.respond( + 500, + { "Content-Type": "application/json; encoding=UTF-8" }, + JSON.stringify({ error: "Internal server error" }) + ); }); - }); - describe("when stripFragments is enabled", function () { - it("should strip URL fragments from URLs without query params", function () { - const url = "https://example.com/page#section"; - const expected = "https://example.com/page"; - const result = neuwo.cleanUrl(url, { stripFragments: true }); - expect(result, "should remove hash fragment").to.equal(expected); + it("should handle JSON parsing error in success callback", function (done) { + const bidsConfig = bidsConfiglike(); + const conf = config(); + conf.params.websiteToAnalyseUrl = "https://publisher.works/article.php?id=parse-error"; + conf.params.enableCache = true; + + neuwo.getBidRequestData(bidsConfig, () => { + // Callback should still be called + const contentData = bidsConfig.ortb2Fragments.global?.site?.content?.data; + expect(contentData, "No data should be injected after parsing error").to.be.undefined; + done(); + }, conf, "consent data"); + + expect(server.requests.length, "Should make an API request").to.equal(1); + + // Respond with invalid JSON + const request = server.requests[0]; + request.respond( + 200, + { "Content-Type": "application/json; encoding=UTF-8" }, + "{ invalid json content }" + ); }); - it("should strip URL fragments from URLs with query params", function () { - const url = "https://example.com/page?foo=bar#section"; - const expected = "https://example.com/page?foo=bar"; - const result = neuwo.cleanUrl(url, { stripFragments: true }); - expect(result, "should remove hash fragment and preserve query params").to.equal(expected); + it("should not cache response after JSON parsing error and allow retry", function (done) { + const bidsConfig1 = bidsConfiglike(); + const bidsConfig2 = bidsConfiglike(); + const conf = config(); + conf.params.websiteToAnalyseUrl = "https://publisher.works/article.php?id=parse-error-retry"; + conf.params.enableCache = true; + + neuwo.getBidRequestData(bidsConfig1, () => { + // First call with parsing error + const contentData1 = bidsConfig1.ortb2Fragments.global?.site?.content?.data; + expect(contentData1, "No data after parsing error").to.be.undefined; + + // Second call should retry (not use cached error) + neuwo.getBidRequestData(bidsConfig2, () => { + try { + expect(server.requests.length, "Should retry after parsing error").to.equal(2); + const contentData2 = bidsConfig2.ortb2Fragments.global?.site?.content?.data?.[0]; + expect(contentData2, "Second call should have valid data").to.exist; + expect(contentData2.name, "Should have correct provider").to.equal(neuwo.DATA_PROVIDER); + done(); + } catch (e) { + done(e); + } + }, conf, "consent data"); + + // Second request gets valid response + const request2 = server.requests[1]; + request2.respond( + 200, + { "Content-Type": "application/json; encoding=UTF-8" }, + JSON.stringify(getNeuwoApiResponse()) + ); + }, conf, "consent data"); + + // First request gets invalid JSON + const request1 = server.requests[0]; + request1.respond( + 200, + { "Content-Type": "application/json; encoding=UTF-8" }, + "{ this is not valid JSON }" + ); }); - it("should strip fragments when combined with stripAllQueryParams", function () { - const url = "https://example.com/page?foo=bar#section"; - const expected = "https://example.com/page"; - const result = neuwo.cleanUrl(url, { stripAllQueryParams: true, stripFragments: true }); - expect(result, "should remove both query params and fragment").to.equal(expected); + it("should handle response with empty segments", function (done) { + const bidsConfig = bidsConfiglike(); + const conf = config(); + conf.params.websiteToAnalyseUrl = "https://publisher.works/article.php?id=no-categories"; + + neuwo.getBidRequestData(bidsConfig, () => { + // Callback should still be called even with empty response + const contentData = bidsConfig.ortb2Fragments.global?.site?.content?.data; + expect(contentData, "No data should be injected without segments").to.be.undefined; + done(); + }, conf, "consent data"); + + const request = server.requests[0]; + request.respond( + 200, + { "Content-Type": "application/json; encoding=UTF-8" }, + JSON.stringify({ "6": {}, "4": {} }) // Empty segments + ); }); + }); - it("should strip fragments when combined with stripQueryParamsForDomains", function () { - const url = "https://example.com/page?foo=bar#section"; - const expected = "https://example.com/page"; - const result = neuwo.cleanUrl(url, { - stripQueryParamsForDomains: ["example.com"], - stripFragments: true + describe("with URL query param stripping", function () { + describe("when stripAllQueryParams is enabled", function () { + it("should strip all query parameters from the analyzed URL", function () { + const bidsConfig = bidsConfiglike(); + const conf = config(); + conf.params.websiteToAnalyseUrl = "https://publisher.works/article.php?utm_source=test&utm_campaign=example&id=5"; + conf.params.stripAllQueryParams = true; + + neuwo.getBidRequestData(bidsConfig, () => {}, conf, "consent data"); + const request = server.requests[0]; + + expect(request.url, "The request URL should not contain encoded query params").to.include( + encodeURIComponent("https://publisher.works/article.php") + ); + expect(request.url, "The request URL should not contain utm_source").to.not.include( + encodeURIComponent("utm_source") + ); }); - expect(result, "should remove both query params and fragment for matching domain").to.equal(expected); }); - it("should strip fragments when combined with stripQueryParams", function () { - const url = "https://example.com/page?foo=bar&keep=this#section"; - const expected = "https://example.com/page?keep=this"; - const result = neuwo.cleanUrl(url, { - stripQueryParams: ["foo"], - stripFragments: true + describe("when stripQueryParamsForDomains is enabled", function () { + it("should strip query params only for matching domains", function () { + const bidsConfig = bidsConfiglike(); + const conf = config(); + conf.params.websiteToAnalyseUrl = "https://publisher.works/article.php?foo=bar&id=5"; + conf.params.stripQueryParamsForDomains = ["publisher.works"]; + + neuwo.getBidRequestData(bidsConfig, () => {}, conf, "consent data"); + const request = server.requests[0]; + + expect(request.url, "The request URL should contain the URL without query params").to.include( + encodeURIComponent("https://publisher.works/article.php") + ); + expect(request.url, "The request URL should not contain the id param").to.not.include( + encodeURIComponent("id=5") + ); }); - expect(result, "should remove specified query params and fragment").to.equal(expected); - }); - it("should handle URLs without fragments gracefully", function () { - const url = "https://example.com/page?foo=bar"; - const expected = "https://example.com/page?foo=bar"; - const result = neuwo.cleanUrl(url, { stripFragments: true }); - expect(result, "should handle URLs without fragments").to.equal(expected); - }); + it("should not strip query params for non-matching domains", function () { + const bidsConfig = bidsConfiglike(); + const conf = config(); + conf.params.websiteToAnalyseUrl = "https://other-domain.com/page?foo=bar&id=5"; + conf.params.stripQueryParamsForDomains = ["publisher.works"]; - it("should handle empty fragments", function () { - const url = "https://example.com/page#"; - const expected = "https://example.com/page"; - const result = neuwo.cleanUrl(url, { stripFragments: true }); - expect(result, "should remove empty fragment").to.equal(expected); - }); + neuwo.getBidRequestData(bidsConfig, () => {}, conf, "consent data"); + const request = server.requests[0]; - it("should handle complex fragments with special characters", function () { - const url = "https://example.com/page?foo=bar#section-1/subsection?query"; - const expected = "https://example.com/page?foo=bar"; - const result = neuwo.cleanUrl(url, { stripFragments: true }); - expect(result, "should remove complex fragments").to.equal(expected); - }); - }); + expect(request.url, "The request URL should contain the full URL with query params").to.include( + encodeURIComponent("https://other-domain.com/page?foo=bar&id=5") + ); + }); - describe("option priority", function () { - it("should apply stripAllQueryParams first when multiple options are set", function () { - const url = "https://example.com/page?foo=bar&baz=qux"; - const expected = "https://example.com/page"; - const result = neuwo.cleanUrl(url, { - stripAllQueryParams: true, - stripQueryParams: ["foo"] + it("should handle subdomain matching correctly", function () { + const bidsConfig = bidsConfiglike(); + const conf = config(); + conf.params.websiteToAnalyseUrl = "https://sub.publisher.works/page?tracking=123"; + conf.params.stripQueryParamsForDomains = ["publisher.works"]; + + neuwo.getBidRequestData(bidsConfig, () => {}, conf, "consent data"); + const request = server.requests[0]; + + expect(request.url, "The request URL should strip params for subdomain").to.include( + encodeURIComponent("https://sub.publisher.works/page") + ); + expect(request.url, "The request URL should not contain tracking param").to.not.include( + encodeURIComponent("tracking=123") + ); }); - expect(result, "stripAllQueryParams should take precedence").to.equal(expected); }); - it("should apply stripQueryParamsForDomains before stripQueryParams", function () { - const url = "https://example.com/page?foo=bar&baz=qux"; - const expected = "https://example.com/page"; - const result = neuwo.cleanUrl(url, { - stripQueryParamsForDomains: ["example.com"], - stripQueryParams: ["foo"] + describe("when stripQueryParams is enabled", function () { + it("should strip only specified query parameters", function () { + const bidsConfig = bidsConfiglike(); + const conf = config(); + conf.params.websiteToAnalyseUrl = "https://publisher.works/article.php?utm_source=test&utm_campaign=example&id=5"; + conf.params.stripQueryParams = ["utm_source", "utm_campaign"]; + + neuwo.getBidRequestData(bidsConfig, () => {}, conf, "consent data"); + const request = server.requests[0]; + + expect(request.url, "The request URL should contain the id param").to.include( + encodeURIComponent("id=5") + ); + expect(request.url, "The request URL should not contain utm_source").to.not.include( + encodeURIComponent("utm_source") + ); + expect(request.url, "The request URL should not contain utm_campaign").to.not.include( + encodeURIComponent("utm_campaign") + ); }); - expect(result, "domain-specific stripping should take precedence").to.equal(expected); - }); - it("should not strip for non-matching domain even with stripQueryParams set", function () { - const url = "https://other.com/page?foo=bar&baz=qux"; - const expected = "https://other.com/page?baz=qux"; - const result = neuwo.cleanUrl(url, { - stripQueryParamsForDomains: ["example.com"], - stripQueryParams: ["foo"] + it("should handle stripping params that result in no query string", function () { + const bidsConfig = bidsConfiglike(); + const conf = config(); + conf.params.websiteToAnalyseUrl = "https://publisher.works/article.php?utm_source=test"; + conf.params.stripQueryParams = ["utm_source"]; + + neuwo.getBidRequestData(bidsConfig, () => {}, conf, "consent data"); + const request = server.requests[0]; + + expect(request.url, "The request URL should not contain a query string").to.include( + encodeURIComponent("https://publisher.works/article.php") + ); + expect(request.url, "The request URL should not contain utm_source").to.not.include( + encodeURIComponent("utm_source") + ); }); - expect(result, "should fall through to stripQueryParams for non-matching domain").to.equal(expected); - }); - }); - }); - // Integration Tests - describe("injectIabCategories edge cases and merging", function () { - it("should not inject data if 'marketing_categories' is missing from the successful API response", function () { - const apiResponse = { brand_safety: { BS_score: "1.0" } }; // Missing marketing_categories - const bidsConfig = bidsConfiglike(); - const bidsConfigCopy = bidsConfiglike(); - const conf = config(); + it("should leave URL unchanged if specified params do not exist", function () { + const bidsConfig = bidsConfiglike(); + const conf = config(); + const originalUrl = "https://publisher.works/article.php?id=5"; + conf.params.websiteToAnalyseUrl = originalUrl; + conf.params.stripQueryParams = ["utm_source", "nonexistent"]; - neuwo.getBidRequestData(bidsConfig, () => {}, conf, "consent data"); - const request = server.requests[0]; - request.respond( - 200, - { "Content-Type": "application/json; encoding=UTF-8" }, - JSON.stringify(apiResponse) - ); + neuwo.getBidRequestData(bidsConfig, () => {}, conf, "consent data"); + const request = server.requests[0]; - // After a successful response with missing data, the global ortb2 fragments should remain empty - // as the data injection logic checks for marketingCategories. - expect( - bidsConfig.ortb2Fragments.global, - "The global ORTB fragments should remain empty" - ).to.deep.equal(bidsConfigCopy.ortb2Fragments.global); - }); + expect(request.url, "The request URL should contain the original URL").to.include( + encodeURIComponent(originalUrl) + ); + }); + }); - it("should append content and user data to existing ORTB fragments", function () { - const apiResponse = getNeuwoApiResponse(); - const bidsConfig = bidsConfiglike(); - // Simulate existing first-party data from another source/module - const existingContentData = { name: "other_content_provider", segment: [{ id: "1" }] }; - const existingUserData = { name: "other_user_provider", segment: [{ id: "2" }] }; + describe("when no stripping options are provided", function () { + it("should send the URL with all query parameters intact", function () { + const bidsConfig = bidsConfiglike(); + const conf = config(); + const originalUrl = "https://publisher.works/article.php?get=horrible_url_for_testing&id=5"; + conf.params.websiteToAnalyseUrl = originalUrl; - bidsConfig.ortb2Fragments.global = { - site: { - content: { - data: [existingContentData], - }, - }, - user: { - data: [existingUserData], - }, - }; - const conf = config(); + neuwo.getBidRequestData(bidsConfig, () => {}, conf, "consent data"); + const request = server.requests[0]; - neuwo.getBidRequestData(bidsConfig, () => {}, conf, "consent data"); - const request = server.requests[0]; - request.respond( - 200, - { "Content-Type": "application/json; encoding=UTF-8" }, - JSON.stringify(apiResponse) - ); + expect(request.url, "The request URL should contain the full original URL").to.include( + encodeURIComponent(originalUrl) + ); + }); + }); + }); + }); - const siteData = bidsConfig.ortb2Fragments.global.site.content.data; - const userData = bidsConfig.ortb2Fragments.global.user.data; + // V1 API Format Tests + // These tests use the legacy V1 API response format with marketing_categories structure. + // V1 API uses GET requests and returns: { marketing_categories: { iab_tier_1: [], iab_tier_2: [], etc. } } + // Field names in V1: ID, label, relevance (capital letters) + describe("V1 API", function () { + describe("getBidRequestData", function () { + describe("when using IAB Content Taxonomy 3.0", function () { + it("should correctly structure the bids object after a successful API response", function () { + const apiResponse = getNeuwoApiResponseV1(); + const bidsConfig = bidsConfiglike(); + const conf = configV1(); + // control xhr api request target for testing + conf.params.websiteToAnalyseUrl = + "https://publisher.works/article.php?get=horrible_url_for_testing&id=5"; + + neuwo.getBidRequestData(bidsConfig, () => {}, conf, "consent data"); + const request = server.requests[0]; + + expect(request.url, "The request URL should be a string").to.be.a("string"); + expect(request.url, "The request URL should include the public API token").to.include( + conf.params.neuwoApiToken + ); + expect(request.url, "The request URL should include the encoded website URL").to.include( + encodeURIComponent(conf.params.websiteToAnalyseUrl) + ); + expect(request.url, "The request URL should include the product identifier").to.include( + "_neuwo_prod=PrebidModule" + ); + expect(request.url, "V1 API should NOT include iabVersions parameter").to.not.include( + "iabVersions" + ); + + request.respond( + 200, + { "Content-Type": "application/json; encoding=UTF-8" }, + JSON.stringify(apiResponse) + ); + + const contentData = bidsConfig.ortb2Fragments.global.site.content.data[0]; + expect(contentData.name, "The data provider name should be correctly set").to.equal( + neuwo.DATA_PROVIDER + ); + expect( + contentData.ext.segtax, + "The segtax value should correspond to IAB Content Taxonomy 3.0" + ).to.equal(7); + expect( + contentData.segment[0].id, + "The first segment ID should match the API response (transformed from V1)" + ).to.equal(apiResponse.marketing_categories.iab_tier_1[0].ID); + expect( + contentData.segment[1].id, + "The second segment ID should match the API response (transformed from V1)" + ).to.equal(apiResponse.marketing_categories.iab_tier_2[0].ID); + }); + }); - // Check that the existing data is still there (index 0) - expect(siteData[0], "Existing site.content.data should be preserved").to.deep.equal( - existingContentData - ); - expect(userData[0], "Existing user.data should be preserved").to.deep.equal(existingUserData); + describe("when using IAB Audience Taxonomy 1.1", function () { + it("should correctly structure the bids object after a successful API response", function () { + const apiResponse = getNeuwoApiResponseV1(); + const bidsConfig = bidsConfiglike(); + const conf = configV1(); + // control xhr api request target for testing + conf.params.websiteToAnalyseUrl = + "https://publisher.works/article.php?get=horrible_url_for_testing&id=5"; + + neuwo.getBidRequestData(bidsConfig, () => {}, conf, "consent data"); + const request = server.requests[0]; + + expect(request.url, "The request URL should be a string").to.be.a("string"); + expect(request.url, "The request URL should include the public API token").to.include( + conf.params.neuwoApiToken + ); + expect(request.url, "The request URL should include the encoded website URL").to.include( + encodeURIComponent(conf.params.websiteToAnalyseUrl) + ); + + request.respond( + 200, + { "Content-Type": "application/json; encoding=UTF-8" }, + JSON.stringify(apiResponse) + ); + const userData = bidsConfig.ortb2Fragments.global.user.data[0]; + expect(userData.name, "The data provider name should be correctly set").to.equal( + neuwo.DATA_PROVIDER + ); + expect( + userData.ext.segtax, + "The segtax value should correspond to IAB Audience Taxonomy 1.1" + ).to.equal(4); + expect( + userData.segment[0].id, + "The first segment ID should match the API response (transformed from V1)" + ).to.equal(apiResponse.marketing_categories.iab_audience_tier_3[0].ID); + expect( + userData.segment[1].id, + "The second segment ID should match the API response (transformed from V1)" + ).to.equal(apiResponse.marketing_categories.iab_audience_tier_4[0].ID); + }); + }); - // Check that the new Neuwo data is appended (index 1) - expect(siteData.length, "site.content.data array should have 2 entries").to.equal(2); - expect(userData.length, "user.data array should have 2 entries").to.equal(2); - expect(siteData[1].name, "The appended content data should be from Neuwo").to.equal( - neuwo.DATA_PROVIDER - ); - expect(userData[1].name, "The appended user data should be from Neuwo").to.equal( - neuwo.DATA_PROVIDER - ); + describe("edge cases", function () { + it("should not inject data if 'marketing_categories' is missing from the successful API response", function () { + const apiResponse = { brand_safety: { BS_score: "1.0" } }; // Missing marketing_categories + const bidsConfig = bidsConfiglike(); + const bidsConfigCopy = bidsConfiglike(); + const conf = configV1(); + + neuwo.getBidRequestData(bidsConfig, () => {}, conf, "consent data"); + const request = server.requests[0]; + request.respond( + 200, + { "Content-Type": "application/json; encoding=UTF-8" }, + JSON.stringify(apiResponse) + ); + + // After a successful response with missing data, the global ortb2 fragments should remain empty + // as the data injection logic checks for marketing_categories in V1 format + expect( + bidsConfig.ortb2Fragments.global, + "The global ORTB fragments should remain empty" + ).to.deep.equal(bidsConfigCopy.ortb2Fragments.global); + }); + + it("should handle response with missing marketing_categories", function (done) { + const bidsConfig = bidsConfiglike(); + const conf = configV1(); + conf.params.websiteToAnalyseUrl = "https://publisher.works/article.php?id=no-categories"; + + neuwo.getBidRequestData(bidsConfig, () => { + // Callback should still be called even without marketing_categories + const contentData = bidsConfig.ortb2Fragments.global?.site?.content?.data; + expect(contentData, "No data should be injected without marketing_categories").to.be.undefined; + done(); + }, conf, "consent data"); + + const request = server.requests[0]; + request.respond( + 200, + { "Content-Type": "application/json; encoding=UTF-8" }, + JSON.stringify({ brand_safety: { BS_score: "1.0" } }) // Missing marketing_categories + ); + }); + }); }); - }); - describe("getBidRequestData with caching", function () { - describe("when enableCache is true (default)", function () { - it("should cache the API response and reuse it on subsequent calls", function () { - const apiResponse = getNeuwoApiResponse(); - const bidsConfig1 = bidsConfiglike(); - const bidsConfig2 = bidsConfiglike(); - const conf = config(); - conf.params.websiteToAnalyseUrl = "https://publisher.works/article.php?id=1"; + describe("with iabTaxonomyFilters (client-side filtering)", function () { + it("should work without filtering when no iabTaxonomyFilters provided", function (done) { + const bidsConfig = bidsConfiglike(); + const conf = configV1(); + + neuwo.getBidRequestData( + bidsConfig, + () => { + const contentData = bidsConfig.ortb2Fragments.global?.site?.content?.data?.[0]; + const userData = bidsConfig.ortb2Fragments.global?.user?.data?.[0]; + + expect(contentData, "should have content data").to.exist; + expect(contentData.segment, "should have unfiltered content segments").to.have.lengthOf(2); + expect(userData, "should have user data").to.exist; + expect(userData.segment, "should have unfiltered audience segments").to.have.lengthOf(3); + done(); + }, + conf, + "consent data" + ); - // First call should make an API request - neuwo.getBidRequestData(bidsConfig1, () => {}, conf, "consent data"); - expect(server.requests.length, "First call should make an API request").to.equal(1); + const request = server.requests[0]; + request.respond(200, {}, JSON.stringify(getNeuwoApiResponseV1())); + }); - const request1 = server.requests[0]; - request1.respond( - 200, - { "Content-Type": "application/json; encoding=UTF-8" }, - JSON.stringify(apiResponse) + it("should apply client-side filtering when iabTaxonomyFilters are provided", function (done) { + const bidsConfig = bidsConfiglike(); + const conf = configV1(); + conf.params.iabTaxonomyFilters = { + ContentTier1: { limit: 1, threshold: 0.4 }, + AudienceTier3: { limit: 1, threshold: 0.9 } + }; + + neuwo.getBidRequestData( + bidsConfig, + () => { + const contentData = bidsConfig.ortb2Fragments.global?.site?.content?.data?.[0]; + const userData = bidsConfig.ortb2Fragments.global?.user?.data?.[0]; + + expect(contentData, "should have content data").to.exist; + expect(contentData.segment, "should have filtered content segments").to.have.lengthOf(2); + + // Check that tier 1 was limited to 1 + const tier1Items = contentData.segment.filter(s => s.id === "274"); + expect(tier1Items, "should have only 1 tier 1 item").to.have.lengthOf(1); + + expect(userData, "should have user data").to.exist; + // Audience tier 3 should be filtered to 1, but tiers 4 and 5 should remain + expect(userData.segment, "should have filtered audience segments").to.have.lengthOf(3); + + done(); + }, + conf, + "consent data" ); - // Second call should use cached response (no new API request) - neuwo.getBidRequestData(bidsConfig2, () => {}, conf, "consent data"); - expect(server.requests.length, "Second call should not make a new API request").to.equal(1); + const request = server.requests[0]; + request.respond(200, {}, JSON.stringify(getNeuwoApiResponseV1())); + }); + + it("should apply strict client-side filtering that removes all low-relevance items", function (done) { + const bidsConfig = bidsConfiglike(); + const conf = configV1(); + conf.params.iabTaxonomyFilters = { + ContentTier1: { threshold: 0.9 }, // Only keep items with 90%+ relevance + ContentTier2: { threshold: 0.9 }, + AudienceTier4: { threshold: 0.99 }, + AudienceTier5: { threshold: 0.99 } + }; + + neuwo.getBidRequestData( + bidsConfig, + () => { + // Tier 1 has 0.47, Tier 2 has 0.41 - both below 0.9 threshold + // All content segments are filtered out, so content data should not be injected + const contentData = bidsConfig.ortb2Fragments.global?.site?.content?.data; + expect(contentData, "should not inject content data when all segments filtered out").to.be.undefined; + + const userData = bidsConfig.ortb2Fragments.global?.user?.data?.[0]; + expect(userData, "should have user data").to.exist; + // Tier 3 has 0.9923 (passes), Tier 4 has 0.9673 (fails), Tier 5 has 0.9066 (fails) + expect(userData.segment, "should have only 1 audience segment").to.have.lengthOf(1); + + done(); + }, + conf, + "consent data" + ); - // Both configs should have the same data - const contentData1 = bidsConfig1.ortb2Fragments.global.site.content.data[0]; - const contentData2 = bidsConfig2.ortb2Fragments.global.site.content.data[0]; - expect(contentData1, "First config should have Neuwo data").to.exist; - expect(contentData2, "Second config should have Neuwo data from cache").to.exist; - expect(contentData1.name, "First config should have correct provider").to.equal(neuwo.DATA_PROVIDER); - expect(contentData2.name, "Second config should have correct provider").to.equal(neuwo.DATA_PROVIDER); + const request = server.requests[0]; + request.respond(200, {}, JSON.stringify(getNeuwoApiResponseV1())); }); - it("should cache when enableCache is explicitly set to true", function () { - const apiResponse = getNeuwoApiResponse(); + it("should handle client-side filtering with cached response", function (done) { const bidsConfig1 = bidsConfiglike(); const bidsConfig2 = bidsConfiglike(); - const conf = config(); - conf.params.websiteToAnalyseUrl = "https://publisher.works/article.php?id=2"; - conf.params.enableCache = true; + const conf = configV1(); + conf.params.iabTaxonomyFilters = { + ContentTier1: { limit: 1 } + }; + + // First request + neuwo.getBidRequestData( + bidsConfig1, + () => { + // Second request (should use cache) + neuwo.getBidRequestData( + bidsConfig2, + () => { + const contentData = bidsConfig2.ortb2Fragments.global?.site?.content?.data?.[0]; + expect(contentData, "should have content data from cache").to.exist; + expect(contentData.segment, "should apply filtering to cached response").to.have.lengthOf(2); + done(); + }, + conf, + "consent data" + ); + }, + conf, + "consent data" + ); - // First call - neuwo.getBidRequestData(bidsConfig1, () => {}, conf, "consent data"); - expect(server.requests.length, "First call should make an API request").to.equal(1); + const request = server.requests[0]; + expect(server.requests, "should only make one API request").to.have.lengthOf(1); + request.respond(200, {}, JSON.stringify(getNeuwoApiResponseV1())); + }); + }); - const request1 = server.requests[0]; - request1.respond( + describe("OpenRTB 2.5 category fields (V1 API compatibility)", function () { + it("should not include iabVersions parameter with legacy API", function () { + const bidsConfig = bidsConfiglike(); + const conf = configV1(); + conf.params.enableOrtb25Fields = true; + + neuwo.getBidRequestData(bidsConfig, () => {}, conf, "consent data"); + + const request = server.requests[0]; + expect(request.url, "should not include iabVersions parameter").to.not.include("iabVersions"); + }); + + it("should not inject category fields with legacy API", function () { + const bidsConfig = bidsConfiglike(); + const conf = configV1(); + conf.params.enableOrtb25Fields = true; + + neuwo.getBidRequestData(bidsConfig, () => {}, conf, "consent data"); + const request = server.requests[0]; + request.respond( 200, { "Content-Type": "application/json; encoding=UTF-8" }, - JSON.stringify(apiResponse) + JSON.stringify(getNeuwoApiResponseV1()) ); - // Second call should use cache - neuwo.getBidRequestData(bidsConfig2, () => {}, conf, "consent data"); - expect(server.requests.length, "Second call should use cached response").to.equal(1); + const siteCat = bidsConfig.ortb2Fragments.global?.site?.cat; + expect(siteCat, "should not have site.cat with legacy API").to.be.undefined; }); }); - describe("when enableCache is false", function () { - it("should not cache the API response and make a new request each time", function () { - const apiResponse = getNeuwoApiResponse(); - const bidsConfig1 = bidsConfiglike(); - const bidsConfig2 = bidsConfiglike(); - const conf = config(); - conf.params.websiteToAnalyseUrl = "https://publisher.works/article.php?id=3"; - conf.params.enableCache = false; + describe("filterIabTaxonomyTier", function () { + it("should return original array when no filter is provided", function () { + const taxonomies = [ + { ID: "1", label: "Category 1", relevance: "0.8" }, + { ID: "2", label: "Category 2", relevance: "0.5" }, + { ID: "3", label: "Category 3", relevance: "0.3" } + ]; + const result = neuwo.filterIabTaxonomyTier(taxonomies, {}); + expect(result, "should return all items when no filter is provided").to.have.lengthOf(3); + }); - // First call should make an API request - neuwo.getBidRequestData(bidsConfig1, () => {}, conf, "consent data"); - expect(server.requests.length, "First call should make an API request").to.equal(1); + it("should return original array when filter is empty", function () { + const taxonomies = [ + { ID: "1", label: "Category 1", relevance: "0.8" }, + { ID: "2", label: "Category 2", relevance: "0.5" } + ]; + const result = neuwo.filterIabTaxonomyTier(taxonomies); + expect(result, "should return all items when no filter parameter").to.have.lengthOf(2); + }); - const request1 = server.requests[0]; - request1.respond( - 200, - { "Content-Type": "application/json; encoding=UTF-8" }, - JSON.stringify(apiResponse) - ); + it("should filter by threshold only", function () { + const taxonomies = [ + { ID: "1", label: "Category 1", relevance: "0.8" }, + { ID: "2", label: "Category 2", relevance: "0.5" }, + { ID: "3", label: "Category 3", relevance: "0.3" }, + { ID: "4", label: "Category 4", relevance: "0.1" } + ]; + const result = neuwo.filterIabTaxonomyTier(taxonomies, { threshold: 0.4 }); + expect(result, "should filter out items below threshold").to.have.lengthOf(2); + expect(result[0].ID, "should keep highest relevance item").to.equal("1"); + expect(result[1].ID, "should keep second highest relevance item").to.equal("2"); + }); - // Second call should make a new API request (not use cache) - neuwo.getBidRequestData(bidsConfig2, () => {}, conf, "consent data"); - expect(server.requests.length, "Second call should make a new API request").to.equal(2); + it("should limit by count only", function () { + const taxonomies = [ + { ID: "1", label: "Category 1", relevance: "0.8" }, + { ID: "2", label: "Category 2", relevance: "0.5" }, + { ID: "3", label: "Category 3", relevance: "0.3" }, + { ID: "4", label: "Category 4", relevance: "0.1" } + ]; + const result = neuwo.filterIabTaxonomyTier(taxonomies, { limit: 2 }); + expect(result, "should limit to specified count").to.have.lengthOf(2); + expect(result[0].ID, "should keep highest relevance item").to.equal("1"); + expect(result[1].ID, "should keep second highest relevance item").to.equal("2"); + }); - const request2 = server.requests[1]; - request2.respond( - 200, - { "Content-Type": "application/json; encoding=UTF-8" }, - JSON.stringify(apiResponse) - ); + it("should return empty array when limit is 0", function () { + const taxonomies = [ + { ID: "1", label: "Category 1", relevance: "0.8" }, + { ID: "2", label: "Category 2", relevance: "0.5" } + ]; + const result = neuwo.filterIabTaxonomyTier(taxonomies, { limit: 0 }); + expect(result, "limit 0 should suppress the tier entirely").to.be.an("array").that.is.empty; + }); - // Both configs should have the same data structure - const contentData1 = bidsConfig1.ortb2Fragments.global.site.content.data[0]; - const contentData2 = bidsConfig2.ortb2Fragments.global.site.content.data[0]; - expect(contentData1, "First config should have Neuwo data").to.exist; - expect(contentData2, "Second config should have Neuwo data from new request").to.exist; - expect(contentData1.name, "First config should have correct provider").to.equal(neuwo.DATA_PROVIDER); - expect(contentData2.name, "Second config should have correct provider").to.equal(neuwo.DATA_PROVIDER); + it("should apply both threshold and limit", function () { + const taxonomies = [ + { ID: "1", label: "Category 1", relevance: "0.9" }, + { ID: "2", label: "Category 2", relevance: "0.7" }, + { ID: "3", label: "Category 3", relevance: "0.6" }, + { ID: "4", label: "Category 4", relevance: "0.5" }, + { ID: "5", label: "Category 5", relevance: "0.2" } + ]; + const result = neuwo.filterIabTaxonomyTier(taxonomies, { threshold: 0.5, limit: 2 }); + expect(result, "should apply both threshold and limit").to.have.lengthOf(2); + expect(result[0].ID, "should keep highest relevance item").to.equal("1"); + expect(result[1].ID, "should keep second highest relevance item").to.equal("2"); }); - it("should bypass existing cache when enableCache is false", function () { - const apiResponse = getNeuwoApiResponse(); - const bidsConfig1 = bidsConfiglike(); - const bidsConfig2 = bidsConfiglike(); - const bidsConfig3 = bidsConfiglike(); - const conf = config(); - conf.params.websiteToAnalyseUrl = "https://publisher.works/article.php?id=4"; + it("should preserve original order when filter is empty", function () { + const taxonomies = [ + { ID: "3", label: "Category 3", relevance: "0.3" }, + { ID: "1", label: "Category 1", relevance: "0.8" }, + { ID: "2", label: "Category 2", relevance: "0.5" } + ]; + const result = neuwo.filterIabTaxonomyTier(taxonomies, {}); + expect(result[0].ID, "first item should keep original position").to.equal("3"); + expect(result[1].ID, "second item should keep original position").to.equal("1"); + expect(result[2].ID, "third item should keep original position").to.equal("2"); + }); - // First call with caching enabled (default) - neuwo.getBidRequestData(bidsConfig1, () => {}, conf, "consent data"); - expect(server.requests.length, "First call should make an API request").to.equal(1); + it("should sort by relevance descending only when limit is set", function () { + const taxonomies = [ + { ID: "3", label: "Category 3", relevance: "0.3" }, + { ID: "1", label: "Category 1", relevance: "0.8" }, + { ID: "2", label: "Category 2", relevance: "0.5" } + ]; + const result = neuwo.filterIabTaxonomyTier(taxonomies, { limit: 2 }); + expect(result).to.have.lengthOf(2); + expect(result[0].ID, "first item should have highest relevance").to.equal("1"); + expect(result[1].ID, "second item should have second highest relevance").to.equal("2"); + }); - const request1 = server.requests[0]; - request1.respond( - 200, - { "Content-Type": "application/json; encoding=UTF-8" }, - JSON.stringify(apiResponse) - ); + it("should not sort when only threshold is set", function () { + const taxonomies = [ + { ID: "3", label: "Category 3", relevance: "0.6" }, + { ID: "1", label: "Category 1", relevance: "0.8" }, + { ID: "2", label: "Category 2", relevance: "0.2" } + ]; + const result = neuwo.filterIabTaxonomyTier(taxonomies, { threshold: 0.5 }); + expect(result).to.have.lengthOf(2); + expect(result[0].ID, "should preserve original order after threshold filter").to.equal("3"); + expect(result[1].ID, "should preserve original order after threshold filter").to.equal("1"); + }); - // Second call with caching enabled should use cache - neuwo.getBidRequestData(bidsConfig2, () => {}, conf, "consent data"); - expect(server.requests.length, "Second call should use cache").to.equal(1); + it("should handle empty array", function () { + const result = neuwo.filterIabTaxonomyTier([], { threshold: 0.5, limit: 2 }); + expect(result, "should return empty array for empty input").to.be.an("array").that.is.empty; + }); - // Third call with caching disabled should bypass cache - conf.params.enableCache = false; - neuwo.getBidRequestData(bidsConfig3, () => {}, conf, "consent data"); - expect(server.requests.length, "Third call should bypass cache and make new request").to.equal(2); + it("should handle null input", function () { + const result = neuwo.filterIabTaxonomyTier(null, { threshold: 0.5 }); + expect(result, "should return empty array for null input").to.be.an("array").that.is.empty; + }); - const request2 = server.requests[1]; - request2.respond( - 200, - { "Content-Type": "application/json; encoding=UTF-8" }, - JSON.stringify(apiResponse) - ); + it("should handle undefined input", function () { + const result = neuwo.filterIabTaxonomyTier(undefined, { threshold: 0.5 }); + expect(result, "should return empty array for undefined input").to.be.an("array").that.is.empty; + }); + + it("should handle items with missing relevance", function () { + const taxonomies = [ + { ID: "1", label: "Category 1", relevance: "0.8" }, + { ID: "2", label: "Category 2" }, + { ID: "3", label: "Category 3", relevance: "0.5" } + ]; + const result = neuwo.filterIabTaxonomyTier(taxonomies, { threshold: 0.3 }); + expect(result, "should handle missing relevance").to.have.lengthOf(2); + }); + + it("should not mutate original array", function () { + const taxonomies = [ + { ID: "1", label: "Category 1", relevance: "0.8" }, + { ID: "2", label: "Category 2", relevance: "0.5" }, + { ID: "3", label: "Category 3", relevance: "0.3" } + ]; + const original = [...taxonomies]; + neuwo.filterIabTaxonomyTier(taxonomies, { limit: 1 }); + expect(taxonomies, "should not mutate original array").to.deep.equal(original); + }); + + it("should sort items with undefined/null relevance to the end", function () { + const taxonomies = [ + { ID: "1", label: "Category 1", relevance: "0.8" }, + { ID: "2", label: "Category 2" }, // missing relevance + { ID: "3", label: "Category 3", relevance: null }, + { ID: "4", label: "Category 4", relevance: undefined }, + { ID: "5", label: "Category 5", relevance: "0.5" } + ]; + const result = neuwo.filterIabTaxonomyTier(taxonomies, { limit: 5 }); + expect(result, "should return all items").to.have.lengthOf(5); + expect(result[0].ID, "should have highest relevance first").to.equal("1"); + expect(result[1].ID, "should have second highest relevance").to.equal("5"); + // Items with missing/null/undefined relevance should be sorted to the end + const lastThreeIds = [result[2].ID, result[3].ID, result[4].ID].sort(); + expect(lastThreeIds, "items with no relevance should be at the end").to.deep.equal(["2", "3", "4"]); }); }); - }); - describe("getBidRequestData with URL query param stripping", function () { - describe("when stripAllQueryParams is enabled", function () { - it("should strip all query parameters from the analyzed URL", function () { - const bidsConfig = bidsConfiglike(); - const conf = config(); - conf.params.websiteToAnalyseUrl = "https://publisher.works/article.php?utm_source=test&utm_campaign=example&id=5"; - conf.params.stripAllQueryParams = true; + describe("filterIabTaxonomies", function () { + function getTestMarketingCategories() { + return { + iab_tier_1: [ + { ID: "1", label: "Cat 1", relevance: "0.9" }, + { ID: "2", label: "Cat 2", relevance: "0.7" }, + { ID: "3", label: "Cat 3", relevance: "0.5" } + ], + iab_tier_2: [ + { ID: "4", label: "Cat 4", relevance: "0.8" }, + { ID: "5", label: "Cat 5", relevance: "0.6" } + ], + iab_audience_tier_3: [ + { ID: "6", label: "Aud 1", relevance: "0.95" }, + { ID: "7", label: "Aud 2", relevance: "0.85" }, + { ID: "8", label: "Aud 3", relevance: "0.75" } + ] + }; + } + + it("should return original data when no filters provided", function () { + const marketingCategories = getTestMarketingCategories(); + const result = neuwo.filterIabTaxonomies(marketingCategories, {}); + expect(result.iab_tier_1, "should return all tier 1 items").to.have.lengthOf(3); + expect(result.iab_tier_2, "should return all tier 2 items").to.have.lengthOf(2); + expect(result.iab_audience_tier_3, "should return all audience tier 3 items").to.have.lengthOf(3); + }); - neuwo.getBidRequestData(bidsConfig, () => {}, conf, "consent data"); - const request = server.requests[0]; + it("should return original data when filters parameter is undefined", function () { + const marketingCategories = getTestMarketingCategories(); + const result = neuwo.filterIabTaxonomies(marketingCategories); + expect(result.iab_tier_1, "should return all tier 1 items").to.have.lengthOf(3); + }); - expect(request.url, "The request URL should not contain encoded query params").to.include( - encodeURIComponent("https://publisher.works/article.php") - ); - expect(request.url, "The request URL should not contain utm_source").to.not.include( - encodeURIComponent("utm_source") - ); + it("should filter ContentTier1 correctly", function () { + const marketingCategories = getTestMarketingCategories(); + const filters = { + ContentTier1: { limit: 1, threshold: 0.8 } + }; + const result = neuwo.filterIabTaxonomies(marketingCategories, filters); + expect(result.iab_tier_1, "should filter tier 1").to.have.lengthOf(1); + expect(result.iab_tier_1[0].ID, "should keep highest relevance item").to.equal("1"); + expect(result.iab_tier_2, "should not filter tier 2").to.have.lengthOf(2); + }); + + it("should filter multiple tiers independently", function () { + const marketingCategories = getTestMarketingCategories(); + const filters = { + ContentTier1: { limit: 2, threshold: 0.6 }, + ContentTier2: { limit: 1, threshold: 0.7 }, + AudienceTier3: { limit: 2, threshold: 0.8 } + }; + const result = neuwo.filterIabTaxonomies(marketingCategories, filters); + expect(result.iab_tier_1, "should filter tier 1 to 2 items").to.have.lengthOf(2); + expect(result.iab_tier_2, "should filter tier 2 to 1 item").to.have.lengthOf(1); + expect(result.iab_tier_2[0].ID, "tier 2 should keep highest item").to.equal("4"); + expect(result.iab_audience_tier_3, "should filter audience tier 3 to 2 items").to.have.lengthOf(2); + }); + + it("should handle tier with no matching config", function () { + const marketingCategories = getTestMarketingCategories(); + const filters = { + ContentTier1: { limit: 1 } + }; + const result = neuwo.filterIabTaxonomies(marketingCategories, filters); + expect(result.iab_tier_1, "should filter configured tier").to.have.lengthOf(1); + expect(result.iab_tier_2, "should keep all items in non-configured tier").to.have.lengthOf(2); + }); + + it("should preserve non-array tier data", function () { + const marketingCategories = { + iab_tier_1: [{ ID: "1", label: "Cat 1", relevance: "0.9" }], + some_other_field: "string value", + another_field: 123 + }; + const filters = { + ContentTier1: { limit: 1 } + }; + const result = neuwo.filterIabTaxonomies(marketingCategories, filters); + expect(result.some_other_field, "should preserve string field").to.equal("string value"); + expect(result.another_field, "should preserve number field").to.equal(123); + }); + + it("should handle null marketingCategories", function () { + const result = neuwo.filterIabTaxonomies(null, { ContentTier1: { limit: 1 } }); + expect(result, "should return null for null input").to.be.null; + }); + + it("should handle undefined marketingCategories", function () { + const result = neuwo.filterIabTaxonomies(undefined, { ContentTier1: { limit: 1 } }); + expect(result, "should return undefined for undefined input").to.be.undefined; + }); + + it("should handle all tier configurations", function () { + const marketingCategories = { + iab_tier_1: [ + { ID: "1", label: "C1", relevance: "0.9" }, + { ID: "2", label: "C2", relevance: "0.5" } + ], + iab_tier_2: [ + { ID: "3", label: "C3", relevance: "0.8" }, + { ID: "4", label: "C4", relevance: "0.4" } + ], + iab_tier_3: [ + { ID: "5", label: "C5", relevance: "0.7" }, + { ID: "6", label: "C6", relevance: "0.3" } + ], + iab_audience_tier_3: [ + { ID: "7", label: "A1", relevance: "0.95" }, + { ID: "8", label: "A2", relevance: "0.45" } + ], + iab_audience_tier_4: [ + { ID: "9", label: "A3", relevance: "0.85" }, + { ID: "10", label: "A4", relevance: "0.35" } + ], + iab_audience_tier_5: [ + { ID: "11", label: "A5", relevance: "0.75" }, + { ID: "12", label: "A6", relevance: "0.25" } + ] + }; + const filters = { + ContentTier1: { limit: 1, threshold: 0.8 }, + ContentTier2: { limit: 1, threshold: 0.7 }, + ContentTier3: { limit: 1, threshold: 0.6 }, + AudienceTier3: { limit: 1, threshold: 0.9 }, + AudienceTier4: { limit: 1, threshold: 0.8 }, + AudienceTier5: { limit: 1, threshold: 0.7 } + }; + const result = neuwo.filterIabTaxonomies(marketingCategories, filters); + expect(result.iab_tier_1, "ContentTier1 filtered").to.have.lengthOf(1); + expect(result.iab_tier_2, "ContentTier2 filtered").to.have.lengthOf(1); + expect(result.iab_tier_3, "ContentTier3 filtered").to.have.lengthOf(1); + expect(result.iab_audience_tier_3, "AudienceTier3 filtered").to.have.lengthOf(1); + expect(result.iab_audience_tier_4, "AudienceTier4 filtered").to.have.lengthOf(1); + expect(result.iab_audience_tier_5, "AudienceTier5 filtered").to.have.lengthOf(1); }); }); - describe("when stripQueryParamsForDomains is enabled", function () { - it("should strip query params only for matching domains", function () { - const bidsConfig = bidsConfiglike(); - const conf = config(); - conf.params.websiteToAnalyseUrl = "https://publisher.works/article.php?foo=bar&id=5"; - conf.params.stripQueryParamsForDomains = ["publisher.works"]; + describe("transformSegmentsV1ToV2", function () { + it("should transform V1 segment format to V2 format", function () { + const v1Segments = [ + { ID: "274", label: "Home & Garden", relevance: "0.47" }, + { ID: "216", label: "Cooking", relevance: "0.41" } + ]; + const result = neuwo.transformSegmentsV1ToV2(v1Segments); + + expect(result, "should return array with same length").to.have.lengthOf(2); + expect(result[0], "first segment should have id property").to.have.property("id", "274"); + expect(result[0], "first segment should have name property").to.have.property("name", "Home & Garden"); + expect(result[0], "first segment should have relevance property").to.have.property("relevance", "0.47"); + expect(result[1], "second segment should have id property").to.have.property("id", "216"); + expect(result[1], "second segment should have name property").to.have.property("name", "Cooking"); + expect(result[1], "second segment should have relevance property").to.have.property("relevance", "0.41"); + }); - neuwo.getBidRequestData(bidsConfig, () => {}, conf, "consent data"); - const request = server.requests[0]; + it("should handle empty array", function () { + const result = neuwo.transformSegmentsV1ToV2([]); + expect(result, "should return empty array").to.be.an("array").that.is.empty; + }); - expect(request.url, "The request URL should contain the URL without query params").to.include( - encodeURIComponent("https://publisher.works/article.php") - ); - expect(request.url, "The request URL should not contain the id param").to.not.include( - encodeURIComponent("id=5") - ); + it("should handle null input", function () { + const result = neuwo.transformSegmentsV1ToV2(null); + expect(result, "should return empty array for null").to.be.an("array").that.is.empty; }); - it("should not strip query params for non-matching domains", function () { - const bidsConfig = bidsConfiglike(); - const conf = config(); - conf.params.websiteToAnalyseUrl = "https://other-domain.com/page?foo=bar&id=5"; - conf.params.stripQueryParamsForDomains = ["publisher.works"]; + it("should handle undefined input", function () { + const result = neuwo.transformSegmentsV1ToV2(undefined); + expect(result, "should return empty array for undefined").to.be.an("array").that.is.empty; + }); - neuwo.getBidRequestData(bidsConfig, () => {}, conf, "consent data"); - const request = server.requests[0]; + it("should handle non-array input", function () { + const result = neuwo.transformSegmentsV1ToV2("not an array"); + expect(result, "should return empty array for non-array").to.be.an("array").that.is.empty; + }); - expect(request.url, "The request URL should contain the full URL with query params").to.include( - encodeURIComponent("https://other-domain.com/page?foo=bar&id=5") - ); + it("should handle segments with missing properties", function () { + const v1Segments = [ + { ID: "274", label: "Home & Garden" }, // missing relevance + { ID: "216", relevance: "0.41" }, // missing label + { label: "Test", relevance: "0.5" } // missing ID + ]; + const result = neuwo.transformSegmentsV1ToV2(v1Segments); + + expect(result, "should return array with same length").to.have.lengthOf(3); + expect(result[0].id, "should handle missing relevance").to.equal("274"); + expect(result[0].relevance, "should set undefined for missing relevance").to.be.undefined; + expect(result[1].name, "should set undefined for missing label").to.be.undefined; + expect(result[2].id, "should set undefined for missing ID").to.be.undefined; }); - it("should handle subdomain matching correctly", function () { - const bidsConfig = bidsConfiglike(); - const conf = config(); - conf.params.websiteToAnalyseUrl = "https://sub.publisher.works/page?tracking=123"; - conf.params.stripQueryParamsForDomains = ["publisher.works"]; + it("should preserve all properties from V1 format", function () { + const v1Segments = [ + { ID: "49", label: "Female", relevance: "0.9923" } + ]; + const result = neuwo.transformSegmentsV1ToV2(v1Segments); - neuwo.getBidRequestData(bidsConfig, () => {}, conf, "consent data"); - const request = server.requests[0]; + expect(result[0], "should only have id, name, and relevance properties").to.have.all.keys("id", "name", "relevance"); + }); + }); - expect(request.url, "The request URL should strip params for subdomain").to.include( - encodeURIComponent("https://sub.publisher.works/page") - ); - expect(request.url, "The request URL should not contain tracking param").to.not.include( - encodeURIComponent("tracking=123") - ); + describe("transformV1ResponseToV2", function () { + it("should transform complete V1 response to V2 format", function () { + const v1Response = { + marketing_categories: { + iab_tier_1: [{ ID: "274", label: "Home & Garden", relevance: "0.47" }], + iab_tier_2: [{ ID: "216", label: "Cooking", relevance: "0.41" }], + iab_tier_3: [{ ID: "388", label: "Recipes", relevance: "0.35" }], + iab_audience_tier_3: [{ ID: "49", label: "Female", relevance: "0.9923" }], + iab_audience_tier_4: [{ ID: "431", label: "Age 25-34", relevance: "0.9673" }], + iab_audience_tier_5: [{ ID: "98", label: "Interest: Cooking", relevance: "0.9066" }] + } + }; + const contentSegtax = 7; // IAB Content Taxonomy 3.0 + const result = neuwo.transformV1ResponseToV2(v1Response, contentSegtax); + + expect(result, "should have content segtax key").to.have.property("7"); + expect(result, "should have audience segtax key").to.have.property("4"); + expect(result["7"], "content segtax should have all tiers").to.have.all.keys("1", "2", "3"); + expect(result["4"], "audience segtax should have all tiers").to.have.all.keys("3", "4", "5"); + expect(result["7"]["1"][0], "content tier 1 should be transformed").to.deep.equal({ + id: "274", + name: "Home & Garden", + relevance: "0.47" + }); + expect(result["4"]["3"][0], "audience tier 3 should be transformed").to.deep.equal({ + id: "49", + name: "Female", + relevance: "0.9923" + }); + }); + + it("should handle different content segtax values", function () { + const v1Response = { + marketing_categories: { + iab_tier_1: [{ ID: "274", label: "Home & Garden", relevance: "0.47" }] + } + }; + + // Test with segtax 6 (IAB 2.2) + const result6 = neuwo.transformV1ResponseToV2(v1Response, 6); + expect(result6, "should use segtax 6 for content").to.have.property("6"); + expect(result6["6"], "should have tier 1").to.have.property("1"); + + // Test with segtax 1 (IAB 1.0) + const result1 = neuwo.transformV1ResponseToV2(v1Response, 1); + expect(result1, "should use segtax 1 for content").to.have.property("1"); + expect(result1["1"], "should have tier 1").to.have.property("1"); + }); + + it("should handle missing marketing_categories", function () { + const v1Response = {}; + const result = neuwo.transformV1ResponseToV2(v1Response, 6); + + expect(result, "should have content segtax key").to.have.property("6"); + expect(result, "should have audience segtax key").to.have.property("4"); + expect(result["6"], "content segtax should be empty object").to.deep.equal({}); + expect(result["4"], "audience segtax should be empty object").to.deep.equal({}); + }); + + it("should handle null v1Response", function () { + const result = neuwo.transformV1ResponseToV2(null, 6); + + expect(result, "should have content segtax key").to.have.property("6"); + expect(result, "should have audience segtax key").to.have.property("4"); + expect(result["6"], "content segtax should be empty object").to.deep.equal({}); + expect(result["4"], "audience segtax should be empty object").to.deep.equal({}); + }); + + it("should handle undefined v1Response", function () { + const result = neuwo.transformV1ResponseToV2(undefined, 6); + + expect(result, "should have content segtax key").to.have.property("6"); + expect(result, "should have audience segtax key").to.have.property("4"); + expect(result["6"], "content segtax should be empty object").to.deep.equal({}); + expect(result["4"], "audience segtax should be empty object").to.deep.equal({}); + }); + + it("should handle partial V1 response with only content tiers", function () { + const v1Response = { + marketing_categories: { + iab_tier_1: [{ ID: "274", label: "Home & Garden", relevance: "0.47" }], + iab_tier_2: [{ ID: "216", label: "Cooking", relevance: "0.41" }] + // No tier 3, no audience tiers + } + }; + const result = neuwo.transformV1ResponseToV2(v1Response, 6); + + expect(result["6"], "should have only tier 1 and 2").to.have.all.keys("1", "2"); + expect(result["6"], "should not have tier 3").to.not.have.property("3"); + expect(result["4"], "audience segtax should be empty").to.deep.equal({}); + }); + + it("should handle partial V1 response with only audience tiers", function () { + const v1Response = { + marketing_categories: { + iab_audience_tier_3: [{ ID: "49", label: "Female", relevance: "0.9923" }], + iab_audience_tier_4: [{ ID: "431", label: "Age 25-34", relevance: "0.9673" }] + // No content tiers + } + }; + const result = neuwo.transformV1ResponseToV2(v1Response, 6); + + expect(result["6"], "content segtax should be empty").to.deep.equal({}); + expect(result["4"], "should have tier 3 and 4").to.have.all.keys("3", "4"); + expect(result["4"], "should not have tier 5").to.not.have.property("5"); + }); + + it("should handle empty arrays in V1 response", function () { + const v1Response = { + marketing_categories: { + iab_tier_1: [], + iab_tier_2: [], + iab_audience_tier_3: [] + } + }; + const result = neuwo.transformV1ResponseToV2(v1Response, 6); + + expect(result["6"]["1"], "content tier 1 should be empty array").to.be.an("array").that.is.empty; + expect(result["6"]["2"], "content tier 2 should be empty array").to.be.an("array").that.is.empty; + expect(result["4"]["3"], "audience tier 3 should be empty array").to.be.an("array").that.is.empty; + }); + + it("should convert segtax to string keys", function () { + const v1Response = { + marketing_categories: { + iab_tier_1: [{ ID: "274", label: "Home & Garden", relevance: "0.47" }] + } + }; + const result = neuwo.transformV1ResponseToV2(v1Response, 6); + + expect(Object.keys(result), "segtax keys should be strings").to.include.members(["6", "4"]); + expect(typeof Object.keys(result)[0], "key type should be string").to.equal("string"); + }); + + it("should preserve brand_safety and other non-marketing_categories fields", function () { + const v1Response = { + brand_safety: { BS_score: "1.0" }, + marketing_categories: { + iab_tier_1: [{ ID: "274", label: "Home & Garden", relevance: "0.47" }] + }, + custom_field: "value" + }; + const result = neuwo.transformV1ResponseToV2(v1Response, 6); + + expect(result, "should not have brand_safety").to.not.have.property("brand_safety"); + expect(result, "should not have custom_field").to.not.have.property("custom_field"); + expect(result, "should only have segtax keys").to.have.all.keys("6", "4"); }); }); - describe("when stripQueryParams is enabled", function () { - it("should strip only specified query parameters", function () { - const bidsConfig = bidsConfiglike(); - const conf = config(); - conf.params.websiteToAnalyseUrl = "https://publisher.works/article.php?utm_source=test&utm_campaign=example&id=5"; - conf.params.stripQueryParams = ["utm_source", "utm_campaign"]; + describe("getBidRequestData with caching (legacy cache key isolation)", function () { + beforeEach(function () { + neuwo.clearCache(); + }); - neuwo.getBidRequestData(bidsConfig, () => {}, conf, "consent data"); - const request = server.requests[0]; + it("should not share cache between different iabContentTaxonomyVersion values", function () { + const apiResponse = getNeuwoApiResponseV1(); + const bidsConfig1 = bidsConfiglike(); + const bidsConfig2 = bidsConfiglike(); + const conf1 = configV1(); + conf1.params.websiteToAnalyseUrl = "https://publisher.works/same-page"; + conf1.params.enableCache = true; + conf1.params.iabContentTaxonomyVersion = "3.0"; + + const conf2 = configV1(); + conf2.params.websiteToAnalyseUrl = "https://publisher.works/same-page"; + conf2.params.enableCache = true; + conf2.params.iabContentTaxonomyVersion = "2.2"; + + // First call with taxonomy 3.0 + neuwo.getBidRequestData(bidsConfig1, () => {}, conf1, "consent data"); + expect(server.requests.length, "First call should make an API request").to.equal(1); - expect(request.url, "The request URL should contain the id param").to.include( - encodeURIComponent("id=5") - ); - expect(request.url, "The request URL should not contain utm_source").to.not.include( - encodeURIComponent("utm_source") - ); - expect(request.url, "The request URL should not contain utm_campaign").to.not.include( - encodeURIComponent("utm_campaign") + server.requests[0].respond( + 200, + { "Content-Type": "application/json; encoding=UTF-8" }, + JSON.stringify(apiResponse) ); - }); - it("should handle stripping params that result in no query string", function () { - const bidsConfig = bidsConfiglike(); - const conf = config(); - conf.params.websiteToAnalyseUrl = "https://publisher.works/article.php?utm_source=test"; - conf.params.stripQueryParams = ["utm_source"]; + // Verify first call has content under segtax 7 (taxonomy 3.0) + const contentData1 = bidsConfig1.ortb2Fragments.global?.site?.content?.data?.[0]; + expect(contentData1, "First call should have content data").to.exist; + expect(contentData1.ext.segtax, "First call should use segtax 7").to.equal(7); - neuwo.getBidRequestData(bidsConfig, () => {}, conf, "consent data"); - const request = server.requests[0]; + // Second call with taxonomy 2.2 - should NOT use cache from taxonomy 3.0 + neuwo.getBidRequestData(bidsConfig2, () => {}, conf2, "consent data"); + expect(server.requests.length, "Second call should make a new API request for different taxonomy").to.equal(2); - expect(request.url, "The request URL should not contain a query string").to.include( - encodeURIComponent("https://publisher.works/article.php") + server.requests[1].respond( + 200, + { "Content-Type": "application/json; encoding=UTF-8" }, + JSON.stringify(apiResponse) ); - expect(request.url, "The request URL should not contain utm_source").to.not.include( - encodeURIComponent("utm_source") + + // Verify second call has content under segtax 6 (taxonomy 2.2) + const contentData2 = bidsConfig2.ortb2Fragments.global?.site?.content?.data?.[0]; + expect(contentData2, "Second call should have content data").to.exist; + expect(contentData2.ext.segtax, "Second call should use segtax 6").to.equal(6); + }); + + it("should not share cache between different iabTaxonomyFilters values", function () { + const apiResponse = getNeuwoApiResponseV1(); + const bidsConfig1 = bidsConfiglike(); + const bidsConfig2 = bidsConfiglike(); + const conf1 = configV1(); + conf1.params.websiteToAnalyseUrl = "https://publisher.works/same-page"; + conf1.params.enableCache = true; + conf1.params.iabTaxonomyFilters = { ContentTier1: { limit: 1 } }; + + const conf2 = configV1(); + conf2.params.websiteToAnalyseUrl = "https://publisher.works/same-page"; + conf2.params.enableCache = true; + // No filters + + // First call with filters + neuwo.getBidRequestData(bidsConfig1, () => {}, conf1, "consent data"); + expect(server.requests.length, "First call should make an API request").to.equal(1); + + server.requests[0].respond( + 200, + { "Content-Type": "application/json; encoding=UTF-8" }, + JSON.stringify(apiResponse) ); + + // Second call without filters - should NOT reuse filtered cache + neuwo.getBidRequestData(bidsConfig2, () => {}, conf2, "consent data"); + expect(server.requests.length, "Second call should make a new API request for different filters").to.equal(2); }); - it("should leave URL unchanged if specified params do not exist", function () { - const bidsConfig = bidsConfiglike(); - const conf = config(); - const originalUrl = "https://publisher.works/article.php?id=5"; - conf.params.websiteToAnalyseUrl = originalUrl; - conf.params.stripQueryParams = ["utm_source", "nonexistent"]; + it("should share cache when legacy config is identical", function () { + const apiResponse = getNeuwoApiResponseV1(); + const bidsConfig1 = bidsConfiglike(); + const bidsConfig2 = bidsConfiglike(); + const conf = configV1(); + conf.params.websiteToAnalyseUrl = "https://publisher.works/same-page"; + conf.params.enableCache = true; - neuwo.getBidRequestData(bidsConfig, () => {}, conf, "consent data"); - const request = server.requests[0]; + // First call + neuwo.getBidRequestData(bidsConfig1, () => {}, conf, "consent data"); + expect(server.requests.length, "First call should make an API request").to.equal(1); - expect(request.url, "The request URL should contain the original URL").to.include( - encodeURIComponent(originalUrl) + server.requests[0].respond( + 200, + { "Content-Type": "application/json; encoding=UTF-8" }, + JSON.stringify(apiResponse) ); - }); - }); - describe("when no stripping options are provided", function () { - it("should send the URL with all query parameters intact", function () { - const bidsConfig = bidsConfiglike(); - const conf = config(); - const originalUrl = "https://publisher.works/article.php?get=horrible_url_for_testing&id=5"; - conf.params.websiteToAnalyseUrl = originalUrl; + // Second call with same config - should use cache + neuwo.getBidRequestData(bidsConfig2, () => {}, conf, "consent data"); + expect(server.requests.length, "Second call should use cache for identical config").to.equal(1); - neuwo.getBidRequestData(bidsConfig, () => {}, conf, "consent data"); - const request = server.requests[0]; + const contentData1 = bidsConfig1.ortb2Fragments.global?.site?.content?.data?.[0]; + const contentData2 = bidsConfig2.ortb2Fragments.global?.site?.content?.data?.[0]; + expect(contentData1, "First call should have content data").to.exist; + expect(contentData2, "Second call should have content data from cache").to.exist; + expect(contentData1.segment, "Cached data should match original").to.deep.equal(contentData2.segment); + }); - expect(request.url, "The request URL should contain the full original URL").to.include( - encodeURIComponent(originalUrl) + it("should not share pending requests between different taxonomy versions", function (done) { + const apiResponse = getNeuwoApiResponseV1(); + const bidsConfig1 = bidsConfiglike(); + const bidsConfig2 = bidsConfiglike(); + const conf1 = configV1(); + conf1.params.websiteToAnalyseUrl = "https://publisher.works/same-page"; + conf1.params.enableCache = true; + conf1.params.iabContentTaxonomyVersion = "3.0"; + + const conf2 = configV1(); + conf2.params.websiteToAnalyseUrl = "https://publisher.works/same-page"; + conf2.params.enableCache = true; + conf2.params.iabContentTaxonomyVersion = "2.2"; + + let callbackCount = 0; + const callback = () => { + callbackCount++; + if (callbackCount === 2) { + try { + // Both should have made separate API requests + expect(server.requests.length, "Should make two API requests for different taxonomies").to.equal(2); + + const contentData1 = bidsConfig1.ortb2Fragments.global?.site?.content?.data?.[0]; + const contentData2 = bidsConfig2.ortb2Fragments.global?.site?.content?.data?.[0]; + expect(contentData1, "First call should have content data").to.exist; + expect(contentData2, "Second call should have content data").to.exist; + expect(contentData1.ext.segtax, "First call should use segtax 7").to.equal(7); + expect(contentData2.ext.segtax, "Second call should use segtax 6").to.equal(6); + done(); + } catch (e) { + done(e); + } + } + }; + + // Two concurrent calls with different taxonomy versions + neuwo.getBidRequestData(bidsConfig1, callback, conf1, "consent data"); + neuwo.getBidRequestData(bidsConfig2, callback, conf2, "consent data"); + + // Should be two separate requests, not shared pending promise + expect(server.requests.length, "Should make two separate API requests").to.equal(2); + + server.requests[0].respond( + 200, + { "Content-Type": "application/json; encoding=UTF-8" }, + JSON.stringify(apiResponse) + ); + server.requests[1].respond( + 200, + { "Content-Type": "application/json; encoding=UTF-8" }, + JSON.stringify(apiResponse) ); }); }); diff --git a/test/spec/modules/newspassidBidAdapter_spec.js b/test/spec/modules/newspassidBidAdapter_spec.js index b1668aafe17..e8f3fe176f0 100644 --- a/test/spec/modules/newspassidBidAdapter_spec.js +++ b/test/spec/modules/newspassidBidAdapter_spec.js @@ -161,13 +161,13 @@ describe('newspassidBidAdapter', function () { describe('interpretResponse', function() { it('should return empty array if no valid bids', function() { - const invalidResponse = {body: {}}; + const invalidResponse = { body: {} }; const bids = spec.interpretResponse(invalidResponse); expect(bids).to.be.empty; }); it('should return empty array if no seatbid', function() { - const noSeatbidResponse = {body: {cur: 'USD'}}; + const noSeatbidResponse = { body: { cur: 'USD' } }; const bids = spec.interpretResponse(noSeatbidResponse); expect(bids).to.be.empty; }); @@ -198,24 +198,24 @@ describe('newspassidBidAdapter', function () { }); it('should expect correct host', function() { - const syncs = spec.getUserSyncs({iframeEnabled: true}, [], {}, '', {}); + const syncs = spec.getUserSyncs({ iframeEnabled: true }, [], {}, '', {}); const url = new URL(syncs[0].url); expect(url.host).to.equal('npid.amspbs.com'); }); it('should expect correct pathname', function() { - const syncs = spec.getUserSyncs({iframeEnabled: true}, [], {}, '', {}); + const syncs = spec.getUserSyncs({ iframeEnabled: true }, [], {}, '', {}); const url = new URL(syncs[0].url); expect(url.pathname).to.equal('/v0/user/sync'); }); it('should return empty array when iframe sync option is disabled', function() { - const syncs = spec.getUserSyncs({iframeEnabled: false, pixelEnabled: false}); + const syncs = spec.getUserSyncs({ iframeEnabled: false, pixelEnabled: false }); expect(syncs).to.be.empty; }); it('should use iframe sync when iframe enabled', function() { - const syncs = spec.getUserSyncs({iframeEnabled: true}); + const syncs = spec.getUserSyncs({ iframeEnabled: true }); expect(syncs).to.have.lengthOf(1); expect(syncs[0].type).to.equal('iframe'); expect(syncs[0].url).to.equal('https://npid.amspbs.com/v0/user/sync?gdpr=0&gdpr_consent=&gpp=&gpp_sid=&us_privacy='); @@ -234,7 +234,7 @@ describe('newspassidBidAdapter', function () { } } }; - const syncs = spec.getUserSyncs({iframeEnabled: true}, [], gdprConsent); + const syncs = spec.getUserSyncs({ iframeEnabled: true }, [], gdprConsent); const url = new URL(syncs[0].url); expect(url.searchParams.get('gdpr')).to.equal('1'); expect(url.searchParams.get('gdpr_consent')).to.equal(encodeURIComponent(consentString)); @@ -253,13 +253,13 @@ describe('newspassidBidAdapter', function () { } } }; - const syncs = spec.getUserSyncs({iframeEnabled: true}, [], gdprConsent); + const syncs = spec.getUserSyncs({ iframeEnabled: true }, [], gdprConsent); expect(syncs).to.be.empty; }); it('should include correct us_privacy param', function() { const uspConsent = '1YNN'; - const syncs = spec.getUserSyncs({iframeEnabled: true}, [], {}, uspConsent, {}); + const syncs = spec.getUserSyncs({ iframeEnabled: true }, [], {}, uspConsent, {}); const url = new URL(syncs[0].url); expect(url.searchParams.get('gdpr')).to.equal('0'); expect(url.searchParams.get('gdpr_consent')).to.equal(''); @@ -276,7 +276,7 @@ describe('newspassidBidAdapter', function () { gppString: gppConsentString, applicableSections: gppSections }; - const syncs = spec.getUserSyncs({iframeEnabled: true}, [], {}, '', gppConsent); + const syncs = spec.getUserSyncs({ iframeEnabled: true }, [], {}, '', gppConsent); const url = new URL(syncs[0].url); expect(url.searchParams.get('gdpr')).to.equal('0'); expect(url.searchParams.get('gdpr_consent')).to.equal(''); @@ -291,7 +291,7 @@ describe('newspassidBidAdapter', function () { publisherId: TEST_PUBLISHER_ID } }); - const syncs = spec.getUserSyncs({iframeEnabled: true}); + const syncs = spec.getUserSyncs({ iframeEnabled: true }); const url = new URL(syncs[0].url); expect(url.searchParams.get('gdpr')).to.equal('0'); expect(url.searchParams.get('gdpr_consent')).to.equal(''); @@ -302,8 +302,8 @@ describe('newspassidBidAdapter', function () { }); it('should have zero user syncs if coppa is true', function() { - config.setConfig({coppa: true}); - const syncs = spec.getUserSyncs({iframeEnabled: true}); + config.setConfig({ coppa: true }); + const syncs = spec.getUserSyncs({ iframeEnabled: true }); expect(syncs).to.be.empty; }); @@ -333,7 +333,7 @@ describe('newspassidBidAdapter', function () { publisherId: TEST_PUBLISHER_ID } }); - const syncs = spec.getUserSyncs({iframeEnabled: true}, [], gdprConsent, uspConsent, gppConsent); + const syncs = spec.getUserSyncs({ iframeEnabled: true }, [], gdprConsent, uspConsent, gppConsent); const url = new URL(syncs[0].url); expect(url.searchParams.get('gdpr')).to.equal('1'); expect(url.searchParams.get('gdpr_consent')).to.equal(encodeURIComponent(consentString)); diff --git a/test/spec/modules/nextMillenniumBidAdapter_spec.js b/test/spec/modules/nextMillenniumBidAdapter_spec.js index e05d797526a..f5f1cf76ab1 100644 --- a/test/spec/modules/nextMillenniumBidAdapter_spec.js +++ b/test/spec/modules/nextMillenniumBidAdapter_spec.js @@ -21,14 +21,14 @@ describe('nextMillenniumBidAdapterTests', () => { impId: '5', id: '123', bid: { - mediaTypes: {banner: {sizes: [[300, 250], [320, 250]]}}, + mediaTypes: { banner: { sizes: [[300, 250], [320, 250]] } }, adUnitCode: 'test-banner-1', bidId: 'e36ea395f67f', }, mediaTypes: { banner: { - data: {sizes: [[300, 250], [320, 250]]}, + data: { sizes: [[300, 250], [320, 250]] }, bidfloorcur: 'EUR', bidfloor: 1.11, pos: 3, @@ -40,12 +40,12 @@ describe('nextMillenniumBidAdapterTests', () => { id: '5', bidfloorcur: 'EUR', bidfloor: 1.11, - ext: {prebid: {storedrequest: {id: '123'}}}, + ext: { prebid: { storedrequest: { id: '123' } } }, banner: { pos: 3, w: 300, h: 250, - format: [{w: 300, h: 250}, {w: 320, h: 250}], + format: [{ w: 300, h: 250 }, { w: 320, h: 250 }], }, }, }, @@ -56,14 +56,14 @@ describe('nextMillenniumBidAdapterTests', () => { impId: '3', id: '234', bid: { - mediaTypes: {video: {playerSize: [400, 300], api: [2], placement: 1, plcmt: 1}}, + mediaTypes: { video: { playerSize: [400, 300], api: [2], placement: 1, plcmt: 1 } }, adUnitCode: 'test-video-1', bidId: 'e36ea395f67f', }, mediaTypes: { video: { - data: {playerSize: [400, 300], api: [2], placement: 1, plcmt: 1}, + data: { playerSize: [400, 300], api: [2], placement: 1, plcmt: 1 }, bidfloorcur: 'USD', pos: 0, }, @@ -73,7 +73,7 @@ describe('nextMillenniumBidAdapterTests', () => { expected: { id: '3', bidfloorcur: 'USD', - ext: {prebid: {storedrequest: {id: '234'}}}, + ext: { prebid: { storedrequest: { id: '234' } } }, video: { mimes: ['video/mp4', 'video/x-ms-wmv', 'application/javascript'], api: [2], @@ -92,13 +92,13 @@ describe('nextMillenniumBidAdapterTests', () => { impId: '4', id: '234', bid: { - mediaTypes: {video: {w: 640, h: 480}}, + mediaTypes: { video: { w: 640, h: 480 } }, bidId: 'e36ea395f67f', }, mediaTypes: { video: { - data: {w: 640, h: 480}, + data: { w: 640, h: 480 }, bidfloorcur: 'USD', }, }, @@ -107,8 +107,8 @@ describe('nextMillenniumBidAdapterTests', () => { expected: { id: '4', bidfloorcur: 'USD', - ext: {prebid: {storedrequest: {id: '234'}}}, - video: {w: 640, h: 480, mimes: ['video/mp4', 'video/x-ms-wmv', 'application/javascript']}, + ext: { prebid: { storedrequest: { id: '234' } } }, + video: { w: 640, h: 480, mimes: ['video/mp4', 'video/x-ms-wmv', 'application/javascript'] }, }, }, @@ -118,15 +118,15 @@ describe('nextMillenniumBidAdapterTests', () => { impId: '2', id: '123', bid: { - mediaTypes: {banner: {sizes: [[300, 250], [320, 250]]}}, + mediaTypes: { banner: { sizes: [[300, 250], [320, 250]] } }, adUnitCode: 'test-gpid-1', bidId: 'e36ea395f67a', - ortb2Imp: {ext: {gpid: 'imp-gpid-123'}}, + ortb2Imp: { ext: { gpid: 'imp-gpid-123' } }, }, mediaTypes: { banner: { - data: {sizes: [[300, 250], [320, 250]]}, + data: { sizes: [[300, 250], [320, 250]] }, }, }, }, @@ -134,10 +134,10 @@ describe('nextMillenniumBidAdapterTests', () => { expected: { id: '2', ext: { - prebid: {storedrequest: {id: '123'}}, + prebid: { storedrequest: { id: '123' } }, gpid: 'imp-gpid-123' }, - banner: {w: 300, h: 250, format: [{w: 300, h: 250}, {w: 320, h: 250}]}, + banner: { w: 300, h: 250, format: [{ w: 300, h: 250 }, { w: 320, h: 250 }] }, }, }, @@ -147,7 +147,7 @@ describe('nextMillenniumBidAdapterTests', () => { impId: '1', id: '123', bid: { - mediaTypes: {banner: {sizes: [[300, 250], [320, 250]]}}, + mediaTypes: { banner: { sizes: [[300, 250], [320, 250]] } }, adUnitCode: 'test-gpid-1', bidId: 'e36ea395f67a', ortb2Imp: { @@ -161,7 +161,7 @@ describe('nextMillenniumBidAdapterTests', () => { mediaTypes: { banner: { - data: {sizes: [[300, 250], [320, 250]]}, + data: { sizes: [[300, 250], [320, 250]] }, }, }, }, @@ -169,16 +169,16 @@ describe('nextMillenniumBidAdapterTests', () => { expected: { id: '1', ext: { - prebid: {storedrequest: {id: '123'}}, + prebid: { storedrequest: { id: '123' } }, }, - banner: {w: 300, h: 250, format: [{w: 300, h: 250}, {w: 320, h: 250}]}, + banner: { w: 300, h: 250, format: [{ w: 300, h: 250 }, { w: 320, h: 250 }] }, }, }, ]; - for (const {title, data, expected} of dataTests) { + for (const { title, data, expected } of dataTests) { it(title, () => { - const {impId, bid, id, mediaTypes} = data; + const { impId, bid, id, mediaTypes } = data; const imp = getImp(impId, bid, id, mediaTypes); expect(imp).to.deep.equal(expected); }); @@ -190,13 +190,13 @@ describe('nextMillenniumBidAdapterTests', () => { { title: 'position is - 1', pos: 0, - expected: {pos: 0}, + expected: { pos: 0 }, }, { title: 'position is - 2', pos: 7, - expected: {pos: 7}, + expected: { pos: 7 }, }, { @@ -205,7 +205,7 @@ describe('nextMillenniumBidAdapterTests', () => { }, ]; - for (const {title, pos, expected} of tests) { + for (const { title, pos, expected } of tests) { it(title, () => { const obj = {}; setImpPos(obj, pos); @@ -235,7 +235,7 @@ describe('nextMillenniumBidAdapterTests', () => { config: { ver: '1.0', complete: 1, - nodes: [{asi: 'test.test', sid: '00001', hp: 1}], + nodes: [{ asi: 'test.test', sid: '00001', hp: 1 }], }, }, }, @@ -249,7 +249,7 @@ describe('nextMillenniumBidAdapterTests', () => { config: { ver: '1.0', complete: 1, - nodes: [{asi: 'test.test', sid: '00001', hp: 1}], + nodes: [{ asi: 'test.test', sid: '00001', hp: 1 }], }, }, }, @@ -265,7 +265,7 @@ describe('nextMillenniumBidAdapterTests', () => { config: { ver: '1.0', complete: 1, - nodes: [{asi: 'test.test', sid: '00001', hp: 1}], + nodes: [{ asi: 'test.test', sid: '00001', hp: 1 }], }, }, }, @@ -279,7 +279,7 @@ describe('nextMillenniumBidAdapterTests', () => { config: { ver: '1.0', complete: 1, - nodes: [{asi: 'test.test', sid: '00001', hp: 1}], + nodes: [{ asi: 'test.test', sid: '00001', hp: 1 }], }, }, }, @@ -296,7 +296,7 @@ describe('nextMillenniumBidAdapterTests', () => { config: { ver: '1.0', complete: 1, - nodes: [{asi: 'test.test', sid: '00001', hp: 1}], + nodes: [{ asi: 'test.test', sid: '00001', hp: 1 }], }, }, }, @@ -311,14 +311,14 @@ describe('nextMillenniumBidAdapterTests', () => { config: { ver: '1.0', complete: 1, - nodes: [{asi: 'test.test', sid: '00001', hp: 1}], + nodes: [{ asi: 'test.test', sid: '00001', hp: 1 }], }, }, }, }, ]; - for (const {title, validBidRequests, bidderRequest, expected} of dataTests) { + for (const { title, validBidRequests, bidderRequest, expected } of dataTests) { it(title, () => { const source = getSourceObj(validBidRequests, bidderRequest); expect(source).to.deep.equal(expected); @@ -334,14 +334,14 @@ describe('nextMillenniumBidAdapterTests', () => { postBody: {}, bidderRequest: { uspConsent: '1---', - gppConsent: {gppString: 'DBACNYA~CPXxRfAPXxR', applicableSections: [7]}, - gdprConsent: {consentString: 'kjfdniwjnifwenrif3', gdprApplies: true}, - ortb2: {regs: {gpp: 'DSFHFHWEUYVDC', gpp_sid: [8, 9, 10], coppa: 1}}, + gppConsent: { gppString: 'DBACNYA~CPXxRfAPXxR', applicableSections: [7] }, + gdprConsent: { consentString: 'kjfdniwjnifwenrif3', gdprApplies: true }, + ortb2: { regs: { gpp: 'DSFHFHWEUYVDC', gpp_sid: [8, 9, 10], coppa: 1 } }, }, }, expected: { - user: {consent: 'kjfdniwjnifwenrif3'}, + user: { consent: 'kjfdniwjnifwenrif3' }, regs: { gpp: 'DBACNYA~CPXxRfAPXxR', gpp_sid: [7], @@ -357,13 +357,13 @@ describe('nextMillenniumBidAdapterTests', () => { data: { postBody: {}, bidderRequest: { - gdprConsent: {consentString: 'ewtewbefbawyadexv', gdprApplies: false}, - ortb2: {regs: {gpp: 'DSFHFHWEUYVDC', gpp_sid: [8, 9, 10], coppa: 0}}, + gdprConsent: { consentString: 'ewtewbefbawyadexv', gdprApplies: false }, + ortb2: { regs: { gpp: 'DSFHFHWEUYVDC', gpp_sid: [8, 9, 10], coppa: 0 } }, }, }, expected: { - user: {consent: 'ewtewbefbawyadexv'}, + user: { consent: 'ewtewbefbawyadexv' }, regs: { gpp: 'DSFHFHWEUYVDC', gpp_sid: [8, 9, 10], @@ -377,11 +377,11 @@ describe('nextMillenniumBidAdapterTests', () => { title: 'gdprConsent(false)', data: { postBody: {}, - bidderRequest: {gdprConsent: {gdprApplies: false}}, + bidderRequest: { gdprConsent: { gdprApplies: false } }, }, expected: { - regs: {gdpr: 0}, + regs: { gdpr: 0 }, }, }, @@ -396,9 +396,9 @@ describe('nextMillenniumBidAdapterTests', () => { }, ]; - for (const {title, data, expected} of dataTests) { + for (const { title, data, expected } of dataTests) { it(title, () => { - const {postBody, bidderRequest} = data; + const { postBody, bidderRequest } = data; setConsentStrings(postBody, bidderRequest); expect(postBody).to.deep.equal(expected); }); @@ -412,8 +412,8 @@ describe('nextMillenniumBidAdapterTests', () => { data: { url: 'https://some.url?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}}&type={{.TYPE_PIXEL}}', uspConsent: '1---', - gppConsent: {gppString: 'DBACNYA~CPXxRfAPXxR', applicableSections: [7, 8]}, - gdprConsent: {consentString: 'kjfdniwjnifwenrif3', gdprApplies: true}, + gppConsent: { gppString: 'DBACNYA~CPXxRfAPXxR', applicableSections: [7, 8] }, + gdprConsent: { consentString: 'kjfdniwjnifwenrif3', gdprApplies: true }, type: 'image', }, @@ -425,8 +425,8 @@ describe('nextMillenniumBidAdapterTests', () => { data: { url: 'https://some.url?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&type={{.TYPE_PIXEL}}', uspConsent: '1---', - gppConsent: {gppString: 'DBACNYA~CPXxRfAPXxR', applicableSections: [7, 8]}, - gdprConsent: {consentString: 'kjfdniwjnifwenrif3', gdprApplies: false}, + gppConsent: { gppString: 'DBACNYA~CPXxRfAPXxR', applicableSections: [7, 8] }, + gdprConsent: { consentString: 'kjfdniwjnifwenrif3', gdprApplies: false }, type: 'iframe', }, @@ -438,8 +438,8 @@ describe('nextMillenniumBidAdapterTests', () => { data: { url: 'https://some.url?param1=value1¶m2=value2', uspConsent: '1---', - gppConsent: {gppString: 'DBACNYA~CPXxRfAPXxR', applicableSections: [7, 8]}, - gdprConsent: {consentString: 'kjfdniwjnifwenrif3', gdprApplies: false}, + gppConsent: { gppString: 'DBACNYA~CPXxRfAPXxR', applicableSections: [7, 8] }, + gdprConsent: { consentString: 'kjfdniwjnifwenrif3', gdprApplies: false }, type: 'iframe', }, @@ -456,9 +456,9 @@ describe('nextMillenniumBidAdapterTests', () => { }, ]; - for (const {title, data, expected} of dataTests) { + for (const { title, data, expected } of dataTests) { it(title, () => { - const {url, gdprConsent, uspConsent, gppConsent, type} = data; + const { url, gdprConsent, uspConsent, gppConsent, type } = data; const newUrl = replaceUsersyncMacros(url, gdprConsent, uspConsent, gppConsent, type); expect(newUrl).to.equal(expected); }); @@ -470,159 +470,189 @@ describe('nextMillenniumBidAdapterTests', () => { { title: 'pixels from responses ({iframeEnabled: true, pixelEnabled: true})', data: { - syncOptions: {iframeEnabled: true, pixelEnabled: true}, + syncOptions: { iframeEnabled: true, pixelEnabled: true }, responses: [ - {body: {ext: {sync: { - image: [ - 'https://some.1.url?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}}', - 'https://some.2.url?us_privacy={{.USPrivacy}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}}', - 'https://some.3.url?param=1234', - ], - - iframe: [ - 'https://some.4.url?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}}', - 'https://some.5.url?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}', - ], - }}}}, + { + body: { + ext: { + sync: { + image: [ + 'https://some.1.url?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}}', + 'https://some.2.url?us_privacy={{.USPrivacy}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}}', + 'https://some.3.url?param=1234', + ], + + iframe: [ + 'https://some.4.url?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}}', + 'https://some.5.url?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}', + ], + } + } + } + }, - {body: {ext: {sync: { - iframe: [ - 'https://some.6.url?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}}', - 'https://some.7.url?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}', - ], - }}}}, + { + body: { + ext: { + sync: { + iframe: [ + 'https://some.6.url?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}}', + 'https://some.7.url?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}', + ], + } + } + } + }, - {body: {ext: {sync: { - image: [ - 'https://some.8.url?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}}', - ], - }}}}, + { + body: { + ext: { + sync: { + image: [ + 'https://some.8.url?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}}', + ], + } + } + } + }, ], uspConsent: '1---', - gppConsent: {gppString: 'DBACNYA~CPXxRfAPXxR', applicableSections: [7, 8]}, - gdprConsent: {consentString: 'kjfdniwjnifwenrif3', gdprApplies: true}, + gppConsent: { gppString: 'DBACNYA~CPXxRfAPXxR', applicableSections: [7, 8] }, + gdprConsent: { consentString: 'kjfdniwjnifwenrif3', gdprApplies: true }, }, expected: [ - {type: 'image', url: 'https://some.1.url?gdpr=1&gdpr_consent=kjfdniwjnifwenrif3&us_privacy=1---&gpp=DBACNYA~CPXxRfAPXxR&gpp_sid=7,8'}, - {type: 'image', url: 'https://some.2.url?us_privacy=1---&gpp=DBACNYA~CPXxRfAPXxR&gpp_sid=7,8'}, - {type: 'image', url: 'https://some.3.url?param=1234'}, - {type: 'iframe', url: 'https://some.4.url?gdpr=1&gdpr_consent=kjfdniwjnifwenrif3&gpp=DBACNYA~CPXxRfAPXxR&gpp_sid=7,8'}, - {type: 'iframe', url: 'https://some.5.url?gdpr=1&gdpr_consent=kjfdniwjnifwenrif3&us_privacy=1---'}, - {type: 'iframe', url: 'https://some.6.url?gdpr=1&gdpr_consent=kjfdniwjnifwenrif3&gpp=DBACNYA~CPXxRfAPXxR&gpp_sid=7,8'}, - {type: 'iframe', url: 'https://some.7.url?gdpr=1&gdpr_consent=kjfdniwjnifwenrif3&us_privacy=1---'}, - {type: 'image', url: 'https://some.8.url?gdpr=1&gdpr_consent=kjfdniwjnifwenrif3&us_privacy=1---&gpp=DBACNYA~CPXxRfAPXxR&gpp_sid=7,8'}, + { type: 'image', url: 'https://some.1.url?gdpr=1&gdpr_consent=kjfdniwjnifwenrif3&us_privacy=1---&gpp=DBACNYA~CPXxRfAPXxR&gpp_sid=7,8' }, + { type: 'image', url: 'https://some.2.url?us_privacy=1---&gpp=DBACNYA~CPXxRfAPXxR&gpp_sid=7,8' }, + { type: 'image', url: 'https://some.3.url?param=1234' }, + { type: 'iframe', url: 'https://some.4.url?gdpr=1&gdpr_consent=kjfdniwjnifwenrif3&gpp=DBACNYA~CPXxRfAPXxR&gpp_sid=7,8' }, + { type: 'iframe', url: 'https://some.5.url?gdpr=1&gdpr_consent=kjfdniwjnifwenrif3&us_privacy=1---' }, + { type: 'iframe', url: 'https://some.6.url?gdpr=1&gdpr_consent=kjfdniwjnifwenrif3&gpp=DBACNYA~CPXxRfAPXxR&gpp_sid=7,8' }, + { type: 'iframe', url: 'https://some.7.url?gdpr=1&gdpr_consent=kjfdniwjnifwenrif3&us_privacy=1---' }, + { type: 'image', url: 'https://some.8.url?gdpr=1&gdpr_consent=kjfdniwjnifwenrif3&us_privacy=1---&gpp=DBACNYA~CPXxRfAPXxR&gpp_sid=7,8' }, ], }, { title: 'pixels from responses ({iframeEnabled: true, pixelEnabled: false})', data: { - syncOptions: {iframeEnabled: true, pixelEnabled: false}, + syncOptions: { iframeEnabled: true, pixelEnabled: false }, responses: [ - {body: {ext: {sync: { - image: [ - 'https://some.1.url?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}}', - 'https://some.2.url?us_privacy={{.USPrivacy}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}}', - 'https://some.3.url?param=1234', - ], - - iframe: [ - 'https://some.4.url?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}}', - 'https://some.5.url?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}', - ], - }}}}, + { + body: { + ext: { + sync: { + image: [ + 'https://some.1.url?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}}', + 'https://some.2.url?us_privacy={{.USPrivacy}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}}', + 'https://some.3.url?param=1234', + ], + + iframe: [ + 'https://some.4.url?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}}', + 'https://some.5.url?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}', + ], + } + } + } + }, ], uspConsent: '1---', - gppConsent: {gppString: 'DBACNYA~CPXxRfAPXxR', applicableSections: [7, 8]}, - gdprConsent: {consentString: 'kjfdniwjnifwenrif3', gdprApplies: true}, + gppConsent: { gppString: 'DBACNYA~CPXxRfAPXxR', applicableSections: [7, 8] }, + gdprConsent: { consentString: 'kjfdniwjnifwenrif3', gdprApplies: true }, }, expected: [ - {type: 'iframe', url: 'https://some.4.url?gdpr=1&gdpr_consent=kjfdniwjnifwenrif3&gpp=DBACNYA~CPXxRfAPXxR&gpp_sid=7,8'}, - {type: 'iframe', url: 'https://some.5.url?gdpr=1&gdpr_consent=kjfdniwjnifwenrif3&us_privacy=1---'}, + { type: 'iframe', url: 'https://some.4.url?gdpr=1&gdpr_consent=kjfdniwjnifwenrif3&gpp=DBACNYA~CPXxRfAPXxR&gpp_sid=7,8' }, + { type: 'iframe', url: 'https://some.5.url?gdpr=1&gdpr_consent=kjfdniwjnifwenrif3&us_privacy=1---' }, ], }, { title: 'pixels from responses ({iframeEnabled: false, pixelEnabled: true})', data: { - syncOptions: {iframeEnabled: false, pixelEnabled: true}, + syncOptions: { iframeEnabled: false, pixelEnabled: true }, responses: [ - {body: {ext: {sync: { - image: [ - 'https://some.1.url?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}}', - 'https://some.2.url?us_privacy={{.USPrivacy}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}}', - 'https://some.3.url?param=1234', - ], - - iframe: [ - 'https://some.4.url?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}}', - 'https://some.5.url?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}', - ], - }}}}, + { + body: { + ext: { + sync: { + image: [ + 'https://some.1.url?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}}', + 'https://some.2.url?us_privacy={{.USPrivacy}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}}', + 'https://some.3.url?param=1234', + ], + + iframe: [ + 'https://some.4.url?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}}', + 'https://some.5.url?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}', + ], + } + } + } + }, ], uspConsent: '1---', - gppConsent: {gppString: 'DBACNYA~CPXxRfAPXxR', applicableSections: [7, 8]}, - gdprConsent: {consentString: 'kjfdniwjnifwenrif3', gdprApplies: true}, + gppConsent: { gppString: 'DBACNYA~CPXxRfAPXxR', applicableSections: [7, 8] }, + gdprConsent: { consentString: 'kjfdniwjnifwenrif3', gdprApplies: true }, }, expected: [ - {type: 'image', url: 'https://some.1.url?gdpr=1&gdpr_consent=kjfdniwjnifwenrif3&us_privacy=1---&gpp=DBACNYA~CPXxRfAPXxR&gpp_sid=7,8'}, - {type: 'image', url: 'https://some.2.url?us_privacy=1---&gpp=DBACNYA~CPXxRfAPXxR&gpp_sid=7,8'}, - {type: 'image', url: 'https://some.3.url?param=1234'}, + { type: 'image', url: 'https://some.1.url?gdpr=1&gdpr_consent=kjfdniwjnifwenrif3&us_privacy=1---&gpp=DBACNYA~CPXxRfAPXxR&gpp_sid=7,8' }, + { type: 'image', url: 'https://some.2.url?us_privacy=1---&gpp=DBACNYA~CPXxRfAPXxR&gpp_sid=7,8' }, + { type: 'image', url: 'https://some.3.url?param=1234' }, ], }, { title: 'pixels - responses is empty ({iframeEnabled: true, pixelEnabled: true})', data: { - syncOptions: {iframeEnabled: true, pixelEnabled: true}, + syncOptions: { iframeEnabled: true, pixelEnabled: true }, responses: [], uspConsent: '1---', - gppConsent: {gppString: 'DBACNYA~CPXxRfAPXxR', applicableSections: [7, 8]}, - gdprConsent: {consentString: 'kjfdniwjnifwenrif3', gdprApplies: true}, + gppConsent: { gppString: 'DBACNYA~CPXxRfAPXxR', applicableSections: [7, 8] }, + gdprConsent: { consentString: 'kjfdniwjnifwenrif3', gdprApplies: true }, }, expected: [ - {type: 'image', url: 'https://cookies.nextmillmedia.com/sync?gdpr=1&gdpr_consent=kjfdniwjnifwenrif3&us_privacy=1---&gpp=DBACNYA~CPXxRfAPXxR&gpp_sid=7,8&type=image'}, - {type: 'iframe', url: 'https://cookies.nextmillmedia.com/sync?gdpr=1&gdpr_consent=kjfdniwjnifwenrif3&us_privacy=1---&gpp=DBACNYA~CPXxRfAPXxR&gpp_sid=7,8&type=iframe'}, + { type: 'image', url: 'https://cookies.nextmillmedia.com/sync?gdpr=1&gdpr_consent=kjfdniwjnifwenrif3&us_privacy=1---&gpp=DBACNYA~CPXxRfAPXxR&gpp_sid=7,8&type=image' }, + { type: 'iframe', url: 'https://cookies.nextmillmedia.com/sync?gdpr=1&gdpr_consent=kjfdniwjnifwenrif3&us_privacy=1---&gpp=DBACNYA~CPXxRfAPXxR&gpp_sid=7,8&type=iframe' }, ], }, { title: 'pixels - responses is empty ({iframeEnabled: true, pixelEnabled: false})', data: { - syncOptions: {iframeEnabled: true, pixelEnabled: false}, + syncOptions: { iframeEnabled: true, pixelEnabled: false }, uspConsent: '1---', - gppConsent: {gppString: 'DBACNYA~CPXxRfAPXxR', applicableSections: [7, 8]}, - gdprConsent: {consentString: 'kjfdniwjnifwenrif3', gdprApplies: true}, + gppConsent: { gppString: 'DBACNYA~CPXxRfAPXxR', applicableSections: [7, 8] }, + gdprConsent: { consentString: 'kjfdniwjnifwenrif3', gdprApplies: true }, }, expected: [ - {type: 'iframe', url: 'https://cookies.nextmillmedia.com/sync?gdpr=1&gdpr_consent=kjfdniwjnifwenrif3&us_privacy=1---&gpp=DBACNYA~CPXxRfAPXxR&gpp_sid=7,8&type=iframe'}, + { type: 'iframe', url: 'https://cookies.nextmillmedia.com/sync?gdpr=1&gdpr_consent=kjfdniwjnifwenrif3&us_privacy=1---&gpp=DBACNYA~CPXxRfAPXxR&gpp_sid=7,8&type=iframe' }, ], }, { title: 'pixels - responses is empty ({iframeEnabled: false, pixelEnabled: false})', data: { - syncOptions: {iframeEnabled: false, pixelEnabled: false}, + syncOptions: { iframeEnabled: false, pixelEnabled: false }, uspConsent: '1---', - gppConsent: {gppString: 'DBACNYA~CPXxRfAPXxR', applicableSections: [7, 8]}, - gdprConsent: {consentString: 'kjfdniwjnifwenrif3', gdprApplies: true}, + gppConsent: { gppString: 'DBACNYA~CPXxRfAPXxR', applicableSections: [7, 8] }, + gdprConsent: { consentString: 'kjfdniwjnifwenrif3', gdprApplies: true }, }, expected: [], }, ]; - for (const {title, data, expected} of dataTests) { + for (const { title, data, expected } of dataTests) { it(title, () => { - const {syncOptions, responses, gdprConsent, uspConsent, gppConsent} = data; + const { syncOptions, responses, gdprConsent, uspConsent, gppConsent } = data; const pixels = spec.getUserSyncs(syncOptions, responses, gdprConsent, uspConsent, gppConsent); expect(pixels).to.deep.equal(expected); }); @@ -642,7 +672,7 @@ describe('nextMillenniumBidAdapterTests', () => { wlangb: ['en', 'fr', 'de'], site: { pagecat: ['IAB2-11', 'IAB2-12', 'IAB2-14'], - content: {cat: ['IAB2-11', 'IAB2-12', 'IAB2-14'], language: 'EN'}, + content: { cat: ['IAB2-11', 'IAB2-12', 'IAB2-14'], language: 'EN' }, } }, }, @@ -653,7 +683,7 @@ describe('nextMillenniumBidAdapterTests', () => { wlang: ['en', 'fr', 'de'], site: { pagecat: ['IAB2-11', 'IAB2-12', 'IAB2-14'], - content: {cat: ['IAB2-11', 'IAB2-12', 'IAB2-14'], language: 'EN'}, + content: { cat: ['IAB2-11', 'IAB2-12', 'IAB2-14'], language: 'EN' }, } }, }, @@ -664,20 +694,20 @@ describe('nextMillenniumBidAdapterTests', () => { postBody: {}, ortb2: { wlangb: ['en', 'fr', 'de'], - user: {keywords: 'key7,key8,key9'}, + user: { keywords: 'key7,key8,key9' }, site: { keywords: 'key1,key2,key3', - content: {keywords: 'key4,key5,key6'}, + content: { keywords: 'key4,key5,key6' }, }, }, }, expected: { wlangb: ['en', 'fr', 'de'], - user: {keywords: 'key7,key8,key9'}, + user: { keywords: 'key7,key8,key9' }, site: { keywords: 'key1,key2,key3', - content: {keywords: 'key4,key5,key6'}, + content: { keywords: 'key4,key5,key6' }, }, }, }, @@ -685,31 +715,35 @@ describe('nextMillenniumBidAdapterTests', () => { { title: 'only site.content.language', data: { - postBody: {site: {domain: 'some.domain'}}, - ortb2: {site: { - content: {language: 'EN'}, - }}, + postBody: { site: { domain: 'some.domain' } }, + ortb2: { + site: { + content: { language: 'EN' }, + } + }, }, - expected: {site: { - domain: 'some.domain', - content: {language: 'EN'}, - }}, + expected: { + site: { + domain: 'some.domain', + content: { language: 'EN' }, + } + }, }, { title: 'object ortb2 is empty', data: { - postBody: {imp: []}, + postBody: { imp: [] }, }, - expected: {imp: []}, + expected: { imp: [] }, }, ]; - for (const {title, data, expected} of dataTests) { + for (const { title, data, expected } of dataTests) { it(title, () => { - const {postBody, ortb2} = data; + const { postBody, ortb2 } = data; setOrtb2Parameters(ALLOWED_ORTB2_PARAMETERS, postBody, ortb2); expect(postBody).to.deep.equal(expected); }); @@ -755,12 +789,12 @@ describe('nextMillenniumBidAdapterTests', () => { userIdAsEids: [ { source: '33across.com', - uids: [{id: 'some-random-id-value', atype: 1}], + uids: [{ id: 'some-random-id-value', atype: 1 }], }, { source: 'utiq.com', - uids: [{id: 'some-random-id-value', atype: 1}], + uids: [{ id: 'some-random-id-value', atype: 1 }], }, ], }, @@ -769,7 +803,7 @@ describe('nextMillenniumBidAdapterTests', () => { userIdAsEids: [ { source: 'test.test', - uids: [{id: 'some-random-id-value', atype: 1}], + uids: [{ id: 'some-random-id-value', atype: 1 }], }, ], }, @@ -781,12 +815,12 @@ describe('nextMillenniumBidAdapterTests', () => { eids: [ { source: '33across.com', - uids: [{id: 'some-random-id-value', atype: 1}], + uids: [{ id: 'some-random-id-value', atype: 1 }], }, { source: 'utiq.com', - uids: [{id: 'some-random-id-value', atype: 1}], + uids: [{ id: 'some-random-id-value', atype: 1 }], }, ], }, @@ -901,17 +935,17 @@ describe('nextMillenniumBidAdapterTests', () => { const expectedNextMilImps = [ { impId: '1', - nextMillennium: {refresh_count: 1}, + nextMillennium: { refresh_count: 1 }, }, { impId: '2', - nextMillennium: {refresh_count: 1}, + nextMillennium: { refresh_count: 1 }, }, { impId: '3', - nextMillennium: {refresh_count: 1}, + nextMillennium: { refresh_count: 1 }, }, ]; @@ -936,7 +970,7 @@ describe('nextMillenniumBidAdapterTests', () => { eventName: 'bidRequested', bids: { bidderCode: 'appnexus', - bids: [{bidder: 'appnexus', params: {}}], + bids: [{ bidder: 'appnexus', params: {} }], }, expected: undefined, @@ -947,7 +981,7 @@ describe('nextMillenniumBidAdapterTests', () => { eventName: 'bidRequested', bids: { bidderCode: 'appnexus', - bids: [{bidder: 'appnexus', params: {placement_id: '807'}}], + bids: [{ bidder: 'appnexus', params: { placement_id: '807' } }], }, expected: undefined, @@ -958,7 +992,7 @@ describe('nextMillenniumBidAdapterTests', () => { eventName: 'bidRequested', bids: { bidderCode: 'nextMillennium', - bids: [{bidder: 'nextMillennium', params: {placement_id: '807'}}], + bids: [{ bidder: 'nextMillennium', params: { placement_id: '807' } }], }, expected: 'https://hb-analytics.nextmillmedia.com/statistics/metric?event=bidRequested&bidder=nextMillennium&source=pbjs&placements=807', @@ -970,8 +1004,8 @@ describe('nextMillenniumBidAdapterTests', () => { bids: { bidderCode: 'nextMillennium', bids: [ - {bidder: 'nextMillennium', params: {placement_id: '807'}}, - {bidder: 'nextMillennium', params: {placement_id: '111'}}, + { bidder: 'nextMillennium', params: { placement_id: '807' } }, + { bidder: 'nextMillennium', params: { placement_id: '111' } }, ], }, @@ -983,7 +1017,7 @@ describe('nextMillenniumBidAdapterTests', () => { eventName: 'bidRequested', bids: { bidderCode: 'nextMillennium', - bids: [{bidder: 'nextMillennium', params: {placement_id: '807', group_id: '123'}}], + bids: [{ bidder: 'nextMillennium', params: { placement_id: '807', group_id: '123' } }], }, expected: 'https://hb-analytics.nextmillmedia.com/statistics/metric?event=bidRequested&bidder=nextMillennium&source=pbjs&groups=123', @@ -995,9 +1029,9 @@ describe('nextMillenniumBidAdapterTests', () => { bids: { bidderCode: 'nextMillennium', bids: [ - {bidder: 'nextMillennium', params: {placement_id: '807', group_id: '123'}}, - {bidder: 'nextMillennium', params: {group_id: '456'}}, - {bidder: 'nextMillennium', params: {placement_id: '222'}}, + { bidder: 'nextMillennium', params: { placement_id: '807', group_id: '123' } }, + { bidder: 'nextMillennium', params: { group_id: '456' } }, + { bidder: 'nextMillennium', params: { placement_id: '222' } }, ], }, @@ -1019,7 +1053,7 @@ describe('nextMillenniumBidAdapterTests', () => { eventName: 'bidResponse', bids: { bidderCode: 'nextMillennium', - params: {placement_id: '807'}, + params: { placement_id: '807' }, }, expected: 'https://hb-analytics.nextmillmedia.com/statistics/metric?event=bidResponse&bidder=nextMillennium&source=pbjs&placements=807', @@ -1040,7 +1074,7 @@ describe('nextMillenniumBidAdapterTests', () => { eventName: 'noBid', bids: { bidder: 'nextMillennium', - params: {placement_id: '807'}, + params: { placement_id: '807' }, }, expected: 'https://hb-analytics.nextmillmedia.com/statistics/metric?event=noBid&bidder=nextMillennium&source=pbjs&placements=807', @@ -1061,7 +1095,7 @@ describe('nextMillenniumBidAdapterTests', () => { eventName: 'bidTimeout', bids: { bidder: 'nextMillennium', - params: {placement_id: '807'}, + params: { placement_id: '807' }, }, expected: 'https://hb-analytics.nextmillmedia.com/statistics/metric?event=bidTimeout&bidder=nextMillennium&source=pbjs&placements=807', @@ -1074,20 +1108,20 @@ describe('nextMillenniumBidAdapterTests', () => { { bidderCode: 'nextMillennium', bids: [ - {bidder: 'nextMillennium', params: {placement_id: '807', group_id: '123'}}, - {bidder: 'nextMillennium', params: {group_id: '456'}}, - {bidder: 'nextMillennium', params: {placement_id: '222'}}, + { bidder: 'nextMillennium', params: { placement_id: '807', group_id: '123' } }, + { bidder: 'nextMillennium', params: { group_id: '456' } }, + { bidder: 'nextMillennium', params: { placement_id: '222' } }, ], }, { bidderCode: 'nextMillennium', - params: {group_id: '7777'}, + params: { group_id: '7777' }, }, { bidderCode: 'nextMillennium', - params: {placement_id: '8888'}, + params: { placement_id: '8888' }, }, ], @@ -1095,7 +1129,7 @@ describe('nextMillenniumBidAdapterTests', () => { }, ]; - for (const {title, eventName, bids, expected} of dataForTests) { + for (const { title, eventName, bids, expected } of dataForTests) { it(title, () => { const url = spec._getUrlPixelMetric(eventName, bids); expect(url).to.equal(expected); @@ -1107,7 +1141,7 @@ describe('nextMillenniumBidAdapterTests', () => { const tests = [ { title: 'test - 1', - bidderRequest: {bidderRequestId: 'mock-uuid', timeout: 1200}, + bidderRequest: { bidderRequestId: 'mock-uuid', timeout: 1200 }, bidRequests: [ { adUnitCode: 'test-div', @@ -1117,7 +1151,7 @@ describe('nextMillenniumBidAdapterTests', () => { params: { placement_id: '-1' }, sizes: [[300, 250]], uspConsent: '1---', - gppConsent: {gppString: 'DBACNYA~CPXxRfAPXxR', applicableSections: [7]}, + gppConsent: { gppString: 'DBACNYA~CPXxRfAPXxR', applicableSections: [7] }, gdprConsent: { consentString: 'kjfdniwjnifwenrif3', gdprApplies: true @@ -1144,7 +1178,7 @@ describe('nextMillenniumBidAdapterTests', () => { params: { placement_id: '333' }, sizes: [[300, 250]], uspConsent: '1---', - gppConsent: {gppString: 'DBACNYA~CPXxRfAPXxR', applicableSections: [7]}, + gppConsent: { gppString: 'DBACNYA~CPXxRfAPXxR', applicableSections: [7] }, gdprConsent: { consentString: 'kjfdniwjnifwenrif3', gdprApplies: true @@ -1174,7 +1208,7 @@ describe('nextMillenniumBidAdapterTests', () => { }, ]; - for (const {title, bidRequests, bidderRequest, expected} of tests) { + for (const { title, bidRequests, bidderRequest, expected } of tests) { it(title, () => { const request = spec.buildRequests(bidRequests, bidderRequest); expect(request.length).to.equal(expected.requestSize); @@ -1223,7 +1257,7 @@ describe('nextMillenniumBidAdapterTests', () => { adomain: ['test.addomain.com'], w: 400, h: 300, - ext: {prebid: {type: 'video'}}, + ext: { prebid: { type: 'video' } }, }, { @@ -1235,7 +1269,7 @@ describe('nextMillenniumBidAdapterTests', () => { adomain: ['test.addomain.com'], w: 640, h: 480, - ext: {prebid: {type: 'video'}}, + ext: { prebid: { type: 'video' } }, }, ], }, @@ -1295,7 +1329,7 @@ describe('nextMillenniumBidAdapterTests', () => { }, ]; - for (const {title, serverResponse, bidRequest, expected} of tests) { + for (const { title, serverResponse, bidRequest, expected } of tests) { describe(title, () => { const bids = spec.interpretResponse(serverResponse, bidRequest); for (let i = 0; i < bids.length; i++) { @@ -1350,7 +1384,7 @@ describe('nextMillenniumBidAdapterTests', () => { }, ]; - for (const {title, impId, bid, expected} of tests) { + for (const { title, impId, bid, expected } of tests) { it(title, () => { const extNextMilImp = getExtNextMilImp(impId, bid); expect(extNextMilImp.impId).to.deep.equal(expected.impId); diff --git a/test/spec/modules/nextrollBidAdapter_spec.js b/test/spec/modules/nextrollBidAdapter_spec.js index 5838e2929a0..a74bda4e52b 100644 --- a/test/spec/modules/nextrollBidAdapter_spec.js +++ b/test/spec/modules/nextrollBidAdapter_spec.js @@ -36,13 +36,13 @@ describe('nextrollBidAdapter', function() { bidId: 'bid_id', mediaTypes: { native: { - title: {required: true, len: 80}, - image: {required: true, sizes: [728, 90]}, - sponsoredBy: {required: false, len: 20}, - clickUrl: {required: true}, - body: {required: true, len: 25}, - icon: {required: true, sizes: [50, 50], aspect_ratios: [{ratio_height: 3, ratio_width: 4}]}, - someRandomAsset: {required: false, len: 100} // This should be ignored + title: { required: true, len: 80 }, + image: { required: true, sizes: [728, 90] }, + sponsoredBy: { required: false, len: 20 }, + clickUrl: { required: true }, + body: { required: true, len: 25 }, + icon: { required: true, sizes: [50, 50], aspect_ratios: [{ ratio_height: 3, ratio_width: 4 }] }, + someRandomAsset: { required: false, len: 100 } // This should be ignored } }, params: { @@ -56,11 +56,11 @@ describe('nextrollBidAdapter', function() { const assets = request[0].data.imp.native.request.native.assets const excptedAssets = [ - {id: 1, required: 1, title: {len: 80}}, - {id: 2, required: 1, img: {w: 728, h: 90, wmin: 1, hmin: 1, type: 3}}, - {id: 3, required: 1, img: {w: 50, h: 50, wmin: 4, hmin: 3, type: 1}}, - {id: 5, required: 0, data: {len: 20, type: 1}}, - {id: 6, required: 1, data: {len: 25, type: 2}} + { id: 1, required: 1, title: { len: 80 } }, + { id: 2, required: 1, img: { w: 728, h: 90, wmin: 1, hmin: 1, type: 3 } }, + { id: 3, required: 1, img: { w: 50, h: 50, wmin: 4, hmin: 3, type: 1 } }, + { id: 5, required: 0, data: { len: 20, type: 1 } }, + { id: 6, required: 1, data: { len: 25, type: 2 } } ] expect(assets).to.be.deep.equal(excptedAssets) }) @@ -149,7 +149,7 @@ describe('nextrollBidAdapter', function() { it('sets the CCPA consent string', function () { const us_privacy = '1YYY'; - const request = spec.buildRequests([validBid], {'uspConsent': us_privacy})[0]; + const request = spec.buildRequests([validBid], { 'uspConsent': us_privacy })[0]; expect(request.data.regs.ext.us_privacy).to.be.equal(us_privacy); }); @@ -190,11 +190,11 @@ describe('nextrollBidAdapter', function() { }); it('builds the same amount of responses as server responses it receives', function () { - expect(spec.interpretResponse({body: responseBody}, {})).to.be.lengthOf(2); + expect(spec.interpretResponse({ body: responseBody }, {})).to.be.lengthOf(2); }); it('builds a response with the expected fields', function () { - const response = spec.interpretResponse({body: responseBody}, {})[0]; + const response = spec.interpretResponse({ body: responseBody }, {})[0]; expect(response.requestId).to.be.equal('bidresponse_id'); expect(response.cpm).to.be.equal(1.2); @@ -226,11 +226,11 @@ describe('nextrollBidAdapter', function() { price: 1.2, crid: 'crid1', adm: { - link: {url: clickUrl}, + link: { url: clickUrl }, assets: [ - {id: 1, title: {text: titleText}}, - {id: 2, img: {w: imgW, h: imgH, url: imgUrl}}, - {id: 5, data: {value: brandText}} + { id: 1, title: { text: titleText } }, + { id: 2, img: { w: imgW, h: imgH, url: imgUrl } }, + { id: 5, data: { value: brandText } } ], imptrackers: [impUrl] } @@ -247,7 +247,7 @@ describe('nextrollBidAdapter', function() { privacyLink: 'https://app.adroll.com/optout/personalized', privacyIcon: 'https://s.adroll.com/j/ad-choices-small.png', title: titleText, - image: {url: imgUrl, width: imgW, height: imgH}, + image: { url: imgUrl, width: imgW, height: imgH }, sponsoredBy: brandText, clickTrackers: [], jstracker: [] @@ -263,9 +263,9 @@ describe('nextrollBidAdapter', function() { const bodyText = 'Some body text' allAssetsResponse.body.seatbid[0].bid[0].adm.assets.push(...[ - {id: 3, img: {w: iconW, h: iconH, url: iconUrl}}, - {id: 4, img: {w: logoW, h: logoH, url: logoUrl}}, - {id: 6, data: {value: bodyText}} + { id: 3, img: { w: iconW, h: iconH, url: iconUrl } }, + { id: 4, img: { w: logoW, h: logoH, url: logoUrl } }, + { id: 6, data: { value: bodyText } } ]) const response = spec.interpretResponse(allAssetsResponse) @@ -277,9 +277,9 @@ describe('nextrollBidAdapter', function() { privacyLink: 'https://app.adroll.com/optout/personalized', privacyIcon: 'https://s.adroll.com/j/ad-choices-small.png', title: titleText, - image: {url: imgUrl, width: imgW, height: imgH}, - icon: {url: iconUrl, width: iconW, height: iconH}, - logo: {url: logoUrl, width: logoW, height: logoH}, + image: { url: imgUrl, width: imgW, height: imgH }, + icon: { url: iconUrl, width: iconW, height: iconH }, + logo: { url: logoUrl, width: logoW, height: logoH }, body: bodyText, sponsoredBy: brandText } diff --git a/test/spec/modules/nexverseBidAdapter_spec.js b/test/spec/modules/nexverseBidAdapter_spec.js index 63d4046a28e..bb73acba923 100644 --- a/test/spec/modules/nexverseBidAdapter_spec.js +++ b/test/spec/modules/nexverseBidAdapter_spec.js @@ -31,7 +31,7 @@ describe('nexverseBidAdapterTests', () => { it('should return false when valid params are not passed', function () { const bid = Object.assign({}, sbid); delete bid.params; - bid.params = {uid: '', pubId: '', pubEpid: ''}; + bid.params = { uid: '', pubId: '', pubEpid: '' }; expect(spec.isBidRequestValid(bid)).to.equal(false); }); @@ -44,7 +44,7 @@ describe('nexverseBidAdapterTests', () => { sizes: [[300, 250]] } }; - bid.params = {uid: '77d4a2eb3d209ce6c7691dc79fcab358', pubId: '24051'}; + bid.params = { uid: '77d4a2eb3d209ce6c7691dc79fcab358', pubId: '24051' }; expect(spec.isBidRequestValid(bid)).to.equal(false); }); it('should return true when valid params are passed as nums', function () { @@ -55,7 +55,7 @@ describe('nexverseBidAdapterTests', () => { sizes: [[300, 250]] } }; - bid.params = {uid: '77d4a2eb3d209ce6c7691dc79fcab358', pubId: '24051', pubEpid: '34561'}; + bid.params = { uid: '77d4a2eb3d209ce6c7691dc79fcab358', pubId: '24051', pubEpid: '34561' }; expect(spec.isBidRequestValid(bid)).to.equal(true); }); }); diff --git a/test/spec/modules/nexx360BidAdapter_spec.js b/test/spec/modules/nexx360BidAdapter_spec.js index d3ba872946f..f6f5461d361 100644 --- a/test/spec/modules/nexx360BidAdapter_spec.js +++ b/test/spec/modules/nexx360BidAdapter_spec.js @@ -345,7 +345,7 @@ describe('Nexx360 bid adapter tests', () => { source: 'prebid.js', pageViewId: requestContent.ext.pageViewId, bidderVersion: '7.1', - localStorage: { amxId: 'abcdef'}, + localStorage: { amxId: 'abcdef' }, sessionId: requestContent.ext.sessionId, requestCounter: 0, }, @@ -717,7 +717,7 @@ describe('Nexx360 bid adapter tests', () => { }); it('Verifies user sync with cookies in bid response', () => { response.body.ext = { - cookies: [{'type': 'image', 'url': 'http://www.cookie.sync.org/'}] + cookies: [{ 'type': 'image', 'url': 'http://www.cookie.sync.org/' }] }; const syncs = spec.getUserSyncs({}, [response], DEFAULT_OPTIONS.gdprConsent); const expectedSyncs = [{ type: 'image', url: 'http://www.cookie.sync.org/' }]; diff --git a/test/spec/modules/nobidAnalyticsAdapter_spec.js b/test/spec/modules/nobidAnalyticsAdapter_spec.js index e20348f51cc..5b7abf42762 100644 --- a/test/spec/modules/nobidAnalyticsAdapter_spec.js +++ b/test/spec/modules/nobidAnalyticsAdapter_spec.js @@ -1,6 +1,6 @@ import nobidAnalytics from 'modules/nobidAnalyticsAdapter.js'; -import {expect} from 'chai'; -import {server} from 'test/mocks/xhr.js'; +import { expect } from 'chai'; +import { server } from 'test/mocks/xhr.js'; import { EVENTS } from 'src/constants.js'; const events = require('src/events'); const adapterManager = require('src/adapterManager').default; @@ -46,10 +46,12 @@ describe('NoBid Prebid Analytic', function () { }); // Step 2: Send init auction event - events.emit(EVENTS.AUCTION_INIT, {config: initOptions, + events.emit(EVENTS.AUCTION_INIT, { + config: initOptions, auctionId: '13', timestamp: Date.now(), - bidderRequests: [{refererInfo: {topmostLocation: TOP_LOCATION}}]}); + bidderRequests: [{ refererInfo: { topmostLocation: TOP_LOCATION } }] + }); expect(nobidAnalytics.initOptions).to.have.property('siteId', SITE_ID); expect(nobidAnalytics).to.have.property('topLocation', TOP_LOCATION); @@ -77,10 +79,12 @@ describe('NoBid Prebid Analytic', function () { }); // Step 2: Send init auction event - events.emit(EVENTS.AUCTION_INIT, {config: initOptions, + events.emit(EVENTS.AUCTION_INIT, { + config: initOptions, auctionId: '13', timestamp: Date.now(), - bidderRequests: [{refererInfo: {topmostLocation: TOP_LOCATION}}]}); + bidderRequests: [{ refererInfo: { topmostLocation: TOP_LOCATION } }] + }); events.emit(EVENTS.BID_WON, {}); clock.tick(5000); expect(server.requests).to.have.length(1); @@ -191,10 +195,12 @@ describe('NoBid Prebid Analytic', function () { }); // Step 2: Send init auction event - events.emit(EVENTS.AUCTION_INIT, {config: initOptions, + events.emit(EVENTS.AUCTION_INIT, { + config: initOptions, auctionId: '13', timestamp: Date.now(), - bidderRequests: [{refererInfo: {topmostLocation: TOP_LOCATION}}]}); + bidderRequests: [{ refererInfo: { topmostLocation: TOP_LOCATION } }] + }); // Step 3: Send bid won event events.emit(EVENTS.BID_WON, requestIncoming); @@ -388,10 +394,12 @@ describe('NoBid Prebid Analytic', function () { }); // Step 2: Send init auction event - events.emit(EVENTS.AUCTION_INIT, {config: initOptions, + events.emit(EVENTS.AUCTION_INIT, { + config: initOptions, auctionId: '13', timestamp: Date.now(), - bidderRequests: [{refererInfo: {topmostLocation: `${TOP_LOCATION}_something`}}]}); + bidderRequests: [{ refererInfo: { topmostLocation: `${TOP_LOCATION}_something` } }] + }); // Step 3: Send bid won event events.emit(EVENTS.AUCTION_END, requestIncoming); @@ -426,30 +434,30 @@ describe('NoBid Prebid Analytic', function () { it('Analytics disabled test', function (done) { let disabled; - nobidAnalytics.processServerResponse(JSON.stringify({disabled: 0})); + nobidAnalytics.processServerResponse(JSON.stringify({ disabled: 0 })); disabled = nobidAnalytics.isAnalyticsDisabled(); expect(disabled).to.equal(false); - events.emit(EVENTS.AUCTION_END, {auctionId: '1234567890'}); + events.emit(EVENTS.AUCTION_END, { auctionId: '1234567890' }); clock.tick(1000); expect(server.requests).to.have.length(1); - events.emit(EVENTS.AUCTION_END, {auctionId: '12345678901'}); + events.emit(EVENTS.AUCTION_END, { auctionId: '12345678901' }); clock.tick(1000); expect(server.requests).to.have.length(2); nobidAnalytics.processServerResponse('disabled: true'); - events.emit(EVENTS.AUCTION_END, {auctionId: '12345678902'}); + events.emit(EVENTS.AUCTION_END, { auctionId: '12345678902' }); clock.tick(1000); expect(server.requests).to.have.length(3); - nobidAnalytics.processServerResponse(JSON.stringify({disabled: 1})); + nobidAnalytics.processServerResponse(JSON.stringify({ disabled: 1 })); disabled = nobidAnalytics.isAnalyticsDisabled(); expect(disabled).to.equal(true); - events.emit(EVENTS.AUCTION_END, {auctionId: '12345678902'}); + events.emit(EVENTS.AUCTION_END, { auctionId: '12345678902' }); clock.tick(5000); expect(server.requests).to.have.length(3); nobidAnalytics.retentionSeconds = 5; - nobidAnalytics.processServerResponse(JSON.stringify({disabled: 1})); + nobidAnalytics.processServerResponse(JSON.stringify({ disabled: 1 })); clock.tick(1000); disabled = nobidAnalytics.isAnalyticsDisabled(); expect(disabled).to.equal(true); @@ -484,32 +492,32 @@ describe('NoBid Prebid Analytic', function () { let eventType = EVENTS.AUCTION_END; let disabled; - nobidAnalytics.processServerResponse(JSON.stringify({disabled: 0})); + nobidAnalytics.processServerResponse(JSON.stringify({ disabled: 0 })); disabled = nobidAnalytics.isAnalyticsDisabled(); expect(disabled).to.equal(false); - events.emit(eventType, {auctionId: '1234567890'}); + events.emit(eventType, { auctionId: '1234567890' }); clock.tick(1000); expect(server.requests).to.have.length(1); - events.emit(eventType, {auctionId: '12345678901'}); + events.emit(eventType, { auctionId: '12345678901' }); clock.tick(1000); expect(server.requests).to.have.length(2); server.requests.length = 0; expect(server.requests).to.have.length(0); - nobidAnalytics.processServerResponse(JSON.stringify({disabled_auctionEnd: 1})); + nobidAnalytics.processServerResponse(JSON.stringify({ disabled_auctionEnd: 1 })); disabled = nobidAnalytics.isAnalyticsDisabled(eventType); expect(disabled).to.equal(true); - events.emit(eventType, {auctionId: '1234567890'}); + events.emit(eventType, { auctionId: '1234567890' }); clock.tick(1000); expect(server.requests).to.have.length(0); server.requests.length = 0; - nobidAnalytics.processServerResponse(JSON.stringify({disabled_auctionEnd: 0})); + nobidAnalytics.processServerResponse(JSON.stringify({ disabled_auctionEnd: 0 })); disabled = nobidAnalytics.isAnalyticsDisabled(eventType); expect(disabled).to.equal(false); - events.emit(EVENTS.AUCTION_END, {auctionId: '1234567890'}); + events.emit(EVENTS.AUCTION_END, { auctionId: '1234567890' }); clock.tick(1000); expect(server.requests).to.have.length(1); @@ -517,10 +525,10 @@ describe('NoBid Prebid Analytic', function () { expect(server.requests).to.have.length(0); eventType = EVENTS.BID_WON; - nobidAnalytics.processServerResponse(JSON.stringify({disabled_bidWon: 1})); + nobidAnalytics.processServerResponse(JSON.stringify({ disabled_bidWon: 1 })); disabled = nobidAnalytics.isAnalyticsDisabled(eventType); expect(disabled).to.equal(true); - events.emit(eventType, {bidderCode: 'nobid'}); + events.emit(eventType, { bidderCode: 'nobid' }); clock.tick(1000); expect(server.requests).to.have.length(0); @@ -528,10 +536,10 @@ describe('NoBid Prebid Analytic', function () { expect(server.requests).to.have.length(0); eventType = EVENTS.AUCTION_END; - nobidAnalytics.processServerResponse(JSON.stringify({disabled: 1})); + nobidAnalytics.processServerResponse(JSON.stringify({ disabled: 1 })); disabled = nobidAnalytics.isAnalyticsDisabled(eventType); expect(disabled).to.equal(true); - events.emit(eventType, {auctionId: '1234567890'}); + events.emit(eventType, { auctionId: '1234567890' }); clock.tick(1000); expect(server.requests).to.have.length(0); @@ -539,15 +547,15 @@ describe('NoBid Prebid Analytic', function () { expect(server.requests).to.have.length(0); eventType = EVENTS.AUCTION_END; - nobidAnalytics.processServerResponse(JSON.stringify({disabled_auctionEnd: 1, disabled_bidWon: 0})); + nobidAnalytics.processServerResponse(JSON.stringify({ disabled_auctionEnd: 1, disabled_bidWon: 0 })); disabled = nobidAnalytics.isAnalyticsDisabled(eventType); expect(disabled).to.equal(true); - events.emit(eventType, {auctionId: '1234567890'}); + events.emit(eventType, { auctionId: '1234567890' }); clock.tick(1000); expect(server.requests).to.have.length(0); disabled = nobidAnalytics.isAnalyticsDisabled(EVENTS.BID_WON); expect(disabled).to.equal(false); - events.emit(EVENTS.BID_WON, {bidderCode: 'nobid'}); + events.emit(EVENTS.BID_WON, { bidderCode: 'nobid' }); clock.tick(1000); expect(server.requests).to.have.length(1); @@ -574,17 +582,17 @@ describe('NoBid Prebid Analytic', function () { let active = nobidCarbonizer.isActive(); expect(active).to.equal(false); - nobidAnalytics.processServerResponse(JSON.stringify({carbonizer_active: false})); + nobidAnalytics.processServerResponse(JSON.stringify({ carbonizer_active: false })); active = nobidCarbonizer.isActive(); expect(active).to.equal(false); - nobidAnalytics.processServerResponse(JSON.stringify({carbonizer_active: true})); + nobidAnalytics.processServerResponse(JSON.stringify({ carbonizer_active: true })); active = nobidCarbonizer.isActive(); expect(active).to.equal(true); const previousRetention = nobidAnalytics.retentionSeconds; nobidAnalytics.retentionSeconds = 3; - nobidAnalytics.processServerResponse(JSON.stringify({carbonizer_active: true})); + nobidAnalytics.processServerResponse(JSON.stringify({ carbonizer_active: true })); let stored = nobidCarbonizer.getStoredLocalData(); expect(stored[nobidAnalytics.ANALYTICS_DATA_NAME]).to.contain(`{"carbonizer_active":true,"ts":`); clock.tick(5000); @@ -592,7 +600,7 @@ describe('NoBid Prebid Analytic', function () { expect(active).to.equal(false); nobidAnalytics.retentionSeconds = previousRetention; - nobidAnalytics.processServerResponse(JSON.stringify({carbonizer_active: true})); + nobidAnalytics.processServerResponse(JSON.stringify({ carbonizer_active: true })); active = nobidCarbonizer.isActive(); expect(active).to.equal(true); diff --git a/test/spec/modules/nobidBidAdapter_spec.js b/test/spec/modules/nobidBidAdapter_spec.js index 53a8381216c..4f5034edf6b 100644 --- a/test/spec/modules/nobidBidAdapter_spec.js +++ b/test/spec/modules/nobidBidAdapter_spec.js @@ -33,7 +33,7 @@ describe('Nobid Adapter', function () { ]; const bidderRequest = { - refererInfo: {page: REFERER} + refererInfo: { page: REFERER } } it('should FLoor = 1', function () { @@ -98,7 +98,7 @@ describe('Nobid Adapter', function () { ]; const bidderRequest = { - refererInfo: {page: REFERER}, bidderCode: BIDDER_CODE + refererInfo: { page: REFERER }, bidderCode: BIDDER_CODE } const siteName = 'example'; @@ -116,16 +116,16 @@ describe('Nobid Adapter', function () { site: { name: siteName, domain: siteDomain, - cat: [ siteCat ], - sectioncat: [ siteSectionCat ], - pagecat: [ sitePageCat ], + cat: [siteCat], + sectioncat: [siteSectionCat], + pagecat: [sitePageCat], page: sitePage, ref: siteRef, keywords: siteKeywords, search: siteSearch } }; - const request = spec.buildRequests(bidRequests, {...bidderRequest, ortb2}); + const request = spec.buildRequests(bidRequests, { ...bidderRequest, ortb2 }); let payload = JSON.parse(request.data); payload = JSON.parse(JSON.stringify(payload)); expect(payload.sid).to.equal(SITE_ID); @@ -163,9 +163,9 @@ describe('Nobid Adapter', function () { const GPP_SID = [1, 3]; const bidderRequest = { - refererInfo: {page: REFERER}, + refererInfo: { page: REFERER }, bidderCode: BIDDER_CODE, - gppConsent: {gppString: GPP, applicableSections: GPP_SID} + gppConsent: { gppString: GPP, applicableSections: GPP_SID } } it('gpp should match', function () { @@ -187,7 +187,7 @@ describe('Nobid Adapter', function () { it('gpp ortb2 should match', function () { delete bidderRequest.gppConsent; - bidderRequest.ortb2 = {regs: {gpp: GPP, gpp_sid: GPP_SID}}; + bidderRequest.ortb2 = { regs: { gpp: GPP, gpp_sid: GPP_SID } }; const request = spec.buildRequests(bidRequests, bidderRequest); let payload = JSON.parse(request.data); payload = JSON.parse(JSON.stringify(payload)); @@ -215,7 +215,7 @@ describe('Nobid Adapter', function () { ]; const bidderRequest = { - refererInfo: {page: REFERER}, bidderCode: BIDDER_CODE + refererInfo: { page: REFERER }, bidderCode: BIDDER_CODE } it('should add source and version to the tag', function () { @@ -389,7 +389,7 @@ describe('Nobid Adapter', function () { ]; const bidderRequest = { - refererInfo: {page: REFERER} + refererInfo: { page: REFERER } } it('should add source and version to the tag', function () { @@ -479,7 +479,7 @@ describe('Nobid Adapter', function () { ]; const bidderRequest = { - refererInfo: {page: REFERER} + refererInfo: { page: REFERER } } it('should add source and version to the tag', function () { @@ -565,7 +565,7 @@ describe('Nobid Adapter', function () { ]; const bidderRequest = { - refererInfo: {page: REFERER} + refererInfo: { page: REFERER } } it('should criteo eid', function () { @@ -599,7 +599,7 @@ describe('Nobid Adapter', function () { ]; const bidderRequest = { - refererInfo: {page: REFERER} + refererInfo: { page: REFERER } } it('should add source and version to the tag', function () { @@ -733,7 +733,7 @@ describe('Nobid Adapter', function () { ]; const bidderRequest = { - refererInfo: {page: REFERER} + refererInfo: { page: REFERER } } it('should refreshCount = 4', function () { @@ -761,12 +761,13 @@ describe('Nobid Adapter', function () { device: 'COMPUTER', site: 2, bids: [ - {id: 1, + { + id: 1, bdrid: 101, divid: ADUNIT_300x250, dealid: DEAL_ID, creativeid: CREATIVE_ID_300x250, - size: {'w': 300, 'h': 250}, + size: { 'w': 300, 'h': 250 }, adm: ADMARKUP_300x250, price: '' + PRICE_300x250 } @@ -796,7 +797,7 @@ describe('Nobid Adapter', function () { adUnitCode: ADUNIT_300x250 }] } - const result = spec.interpretResponse({ body: response }, {bidderRequest: bidderRequest}); + const 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); @@ -810,7 +811,7 @@ describe('Nobid Adapter', function () { adUnitCode: ADUNIT_300x250 + '1' }] } - const result = spec.interpretResponse({ body: response }, {bidderRequest: bidderRequest}); + const result = spec.interpretResponse({ body: response }, { bidderRequest: bidderRequest }); expect(result.length).to.equal(0); }); @@ -837,7 +838,7 @@ describe('Nobid Adapter', function () { adUnitCode: ADUNIT_300x250 }] } - const result = spec.interpretResponse({ body: response }, {bidderRequest: bidderRequest}); + const result = spec.interpretResponse({ body: response }, { bidderRequest: bidderRequest }); expect(result.length).to.equal(expectedResponse.length); expect(result[0].dealId).to.equal(expectedResponse[0].dealId); }); @@ -858,12 +859,13 @@ describe('Nobid Adapter', function () { site: 2, rlimit: REFRESH_LIMIT, bids: [ - {id: 1, + { + id: 1, bdrid: 101, divid: ADUNIT_300x250, dealid: DEAL_ID, creativeid: CREATIVE_ID_300x250, - size: {'w': 300, 'h': 250}, + size: { 'w': 300, 'h': 250 }, adm: ADMARKUP_300x250, price: '' + PRICE_300x250 } @@ -877,7 +879,7 @@ describe('Nobid Adapter', function () { adUnitCode: ADUNIT_300x250 }] } - const result = spec.interpretResponse({ body: response }, {bidderRequest: bidderRequest}); + const result = spec.interpretResponse({ body: response }, { bidderRequest: bidderRequest }); expect(nobid.refreshLimit).to.equal(REFRESH_LIMIT); }); }); @@ -896,12 +898,13 @@ describe('Nobid Adapter', function () { device: 'COMPUTER', site: 2, bids: [ - {id: 1, + { + id: 1, bdrid: 101, divid: ADUNIT_300x250, dealid: DEAL_ID, creativeid: CREATIVE_ID_300x250, - size: {'w': 300, 'h': 250}, + size: { 'w': 300, 'h': 250 }, adm: ADMARKUP_300x250, price: '' + PRICE_300x250, meta: { @@ -918,7 +921,7 @@ describe('Nobid Adapter', function () { adUnitCode: ADUNIT_300x250 }] } - const result = spec.interpretResponse({ body: response }, {bidderRequest: bidderRequest}); + const result = spec.interpretResponse({ body: response }, { bidderRequest: bidderRequest }); expect(result[0].meta.advertiserDomains).to.equal(ADOMAINS); }); }); @@ -994,12 +997,13 @@ describe('Nobid Adapter', function () { site: 2, ublock: ULIMIT, bids: [ - {id: 1, + { + id: 1, bdrid: 101, divid: ADUNIT_300x250, dealid: DEAL_ID, creativeid: CREATIVE_ID_300x250, - size: {'w': 300, 'h': 250}, + size: { 'w': 300, 'h': 250 }, adm: ADMARKUP_300x250, price: '' + PRICE_300x250 } @@ -1026,7 +1030,7 @@ describe('Nobid Adapter', function () { 'auctionId': '1d1a030790a475', } ]; - spec.interpretResponse({ body: response }, {bidderRequest: bidderRequest}); + spec.interpretResponse({ body: response }, { bidderRequest: bidderRequest }); const request = spec.buildRequests(bidRequests, bidderRequest); expect(request).to.equal(undefined); }); @@ -1035,30 +1039,30 @@ describe('Nobid Adapter', function () { describe('getUserSyncs', function () { const GDPR_CONSENT_STRING = 'GDPR_CONSENT_STRING'; it('should get correct user sync when iframeEnabled', function () { - const pixel = spec.getUserSyncs({iframeEnabled: true}) + const pixel = spec.getUserSyncs({ iframeEnabled: true }) expect(pixel[0].type).to.equal('iframe'); expect(pixel[0].url).to.equal('https://public.servenobid.com/sync.html'); }); it('should get correct user sync when iframeEnabled and pixelEnabled', function () { - const pixel = spec.getUserSyncs({iframeEnabled: true, pixelEnabled: true}) + const pixel = spec.getUserSyncs({ iframeEnabled: true, pixelEnabled: true }) expect(pixel[0].type).to.equal('iframe'); expect(pixel[0].url).to.equal('https://public.servenobid.com/sync.html'); }); it('should get correct user sync when iframeEnabled', function () { - const pixel = spec.getUserSyncs({iframeEnabled: true}, {}, {gdprApplies: true, consentString: GDPR_CONSENT_STRING}) + const 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://public.servenobid.com/sync.html?gdpr=1&gdpr_consent=' + GDPR_CONSENT_STRING); }); it('should get correct user sync when !iframeEnabled', function () { - const pixel = spec.getUserSyncs({iframeEnabled: false}) + const pixel = spec.getUserSyncs({ iframeEnabled: false }) expect(pixel.length).to.equal(0); }); it('should get correct user sync when !iframeEnabled and pixelEnabled', function () { - const pixel = spec.getUserSyncs({iframeEnabled: false, pixelEnabled: true}, [{body: {syncs: ['sync_url']}}]) + const pixel = spec.getUserSyncs({ iframeEnabled: false, pixelEnabled: true }, [{ body: { syncs: ['sync_url'] } }]) expect(pixel.length).to.equal(1); expect(pixel[0].type).to.equal('image'); expect(pixel[0].url).to.equal('sync_url'); diff --git a/test/spec/modules/nodalsAiRtdProvider_spec.js b/test/spec/modules/nodalsAiRtdProvider_spec.js index 9155e07b2ff..631cdfaca7b 100644 --- a/test/spec/modules/nodalsAiRtdProvider_spec.js +++ b/test/spec/modules/nodalsAiRtdProvider_spec.js @@ -197,7 +197,7 @@ describe('NodalsAI RTD Provider', () => { }); it('should return true when initialised with valid config and gdpr consent is null', function () { - const result = nodalsAiRtdSubmodule.init(validConfig, {gdpr: null}); + const result = nodalsAiRtdSubmodule.init(validConfig, { gdpr: null }); server.respond(); expect(result).to.be.true; @@ -494,7 +494,7 @@ describe('NodalsAI RTD Provider', () => { }); it('should return an empty object when getTargetingData throws error', () => { - createTargetingEngineStub({adUnit1: {someKey: 'someValue'}}, true); + createTargetingEngineStub({ adUnit1: { someKey: 'someValue' } }, true); setDataInLocalStorage({ data: successPubEndpointResponse, createdAt: Date.now(), @@ -669,7 +669,7 @@ describe('NodalsAI RTD Provider', () => { it('should not store function arguments in a queue when no data is in localstorage and make a HTTP request for data', () => { const callback = sinon.spy(); - const requestObj = {dummy: 'obj'} + const requestObj = { dummy: 'obj' } nodalsAiRtdSubmodule.getBidRequestData( requestObj, callback, validConfig, permissiveUserConsent ); @@ -686,7 +686,7 @@ describe('NodalsAI RTD Provider', () => { createdAt: Date.now(), }); const callback = sinon.spy(); - const reqBidsConfigObj = {dummy: 'obj'} + const reqBidsConfigObj = { dummy: 'obj' } nodalsAiRtdSubmodule.getBidRequestData( reqBidsConfigObj, callback, validConfig, permissiveUserConsent ); @@ -696,7 +696,7 @@ describe('NodalsAI RTD Provider', () => { expect(window.$nodals.cmdQueue).to.be.an('array').with.length(1); expect(window.$nodals.cmdQueue[0].cmd).to.equal('getBidRequestData'); expect(window.$nodals.cmdQueue[0].runtimeFacts).to.have.keys(['prebid.version', 'page.url']); - expect(window.$nodals.cmdQueue[0].data).to.deep.include({config: validConfig, reqBidsConfigObj, callback, userConsent: permissiveUserConsent}); + expect(window.$nodals.cmdQueue[0].data).to.deep.include({ config: validConfig, reqBidsConfigObj, callback, userConsent: permissiveUserConsent }); expect(window.$nodals.cmdQueue[0].data.storedData).to.have.property('deps').that.deep.equals( successPubEndpointResponse.deps); expect(window.$nodals.cmdQueue[0].data.storedData).to.have.property('facts').that.deep.includes( @@ -713,7 +713,7 @@ describe('NodalsAI RTD Provider', () => { }); const engine = createTargetingEngineStub(); const callback = sinon.spy(); - const reqBidsConfigObj = {dummy: 'obj'} + const reqBidsConfigObj = { dummy: 'obj' } nodalsAiRtdSubmodule.getBidRequestData( reqBidsConfigObj, callback, validConfig, permissiveUserConsent ); @@ -739,7 +739,7 @@ describe('NodalsAI RTD Provider', () => { }); const engine = createTargetingEngineStub(); const callback = sinon.spy(); - const reqBidsConfigObj = {dummy: 'obj'} + const reqBidsConfigObj = { dummy: 'obj' } const configWithManagedConsent = { params: { propertyId: '10312dd2', publisherProvidedConsent: true } }; nodalsAiRtdSubmodule.getBidRequestData( reqBidsConfigObj, callback, configWithManagedConsent, leastPermissiveUserConsent @@ -767,7 +767,7 @@ describe('NodalsAI RTD Provider', () => { createdAt: Date.now(), }); const engine = createTargetingEngineStub(); - const bidResponse = {dummy: 'obj', 'bid': 'foo'}; + const bidResponse = { dummy: 'obj', 'bid': 'foo' }; nodalsAiRtdSubmodule.onBidResponseEvent( bidResponse, validConfig, vendorRestrictiveUserConsent ); @@ -780,7 +780,7 @@ describe('NodalsAI RTD Provider', () => { }); it('should not store function arguments in a queue when no data is in localstorage and make a HTTP request for data', () => { - const bidResponse = {dummy: 'obj', 'bid': 'foo'}; + const bidResponse = { dummy: 'obj', 'bid': 'foo' }; nodalsAiRtdSubmodule.onBidResponseEvent( bidResponse, validConfig, permissiveUserConsent ); @@ -796,7 +796,7 @@ describe('NodalsAI RTD Provider', () => { createdAt: Date.now(), }); const userConsent = generateGdprConsent(); - const bidResponse = {dummy: 'obj', 'bid': 'foo'}; + const bidResponse = { dummy: 'obj', 'bid': 'foo' }; nodalsAiRtdSubmodule.onBidResponseEvent( bidResponse, validConfig, permissiveUserConsent ); @@ -805,7 +805,7 @@ describe('NodalsAI RTD Provider', () => { expect(window.$nodals.cmdQueue).to.be.an('array').with.length(1); expect(window.$nodals.cmdQueue[0].cmd).to.equal('onBidResponseEvent'); expect(window.$nodals.cmdQueue[0].runtimeFacts).to.have.keys(['prebid.version', 'page.url']); - expect(window.$nodals.cmdQueue[0].data).to.deep.include({config: validConfig, bidResponse, userConsent: permissiveUserConsent }); + expect(window.$nodals.cmdQueue[0].data).to.deep.include({ config: validConfig, bidResponse, userConsent: permissiveUserConsent }); expect(window.$nodals.cmdQueue[0].data.storedData).to.have.property('deps').that.deep.equals( successPubEndpointResponse.deps); expect(window.$nodals.cmdQueue[0].data.storedData).to.have.property('facts').that.deep.includes( @@ -821,7 +821,7 @@ describe('NodalsAI RTD Provider', () => { createdAt: Date.now(), }); const engine = createTargetingEngineStub(); - const bidResponse = {dummy: 'obj', 'bid': 'foo'}; + const bidResponse = { dummy: 'obj', 'bid': 'foo' }; nodalsAiRtdSubmodule.onBidResponseEvent( bidResponse, validConfig, permissiveUserConsent ); @@ -844,7 +844,7 @@ describe('NodalsAI RTD Provider', () => { createdAt: Date.now(), }); const engine = createTargetingEngineStub(); - const bidResponse = {dummy: 'obj', 'bid': 'foo'}; + const bidResponse = { dummy: 'obj', 'bid': 'foo' }; const configWithManagedConsent = { params: { propertyId: '10312dd2', publisherProvidedConsent: true } }; nodalsAiRtdSubmodule.onBidResponseEvent( bidResponse, configWithManagedConsent, leastPermissiveUserConsent @@ -870,7 +870,7 @@ describe('NodalsAI RTD Provider', () => { createdAt: Date.now(), }); const engine = createTargetingEngineStub(); - const auctionDetails = {dummy: 'obj', auction: 'foo'}; + const auctionDetails = { dummy: 'obj', auction: 'foo' }; nodalsAiRtdSubmodule.onAuctionEndEvent( auctionDetails, validConfig, vendorRestrictiveUserConsent ); @@ -883,7 +883,7 @@ describe('NodalsAI RTD Provider', () => { }); it('should not store function arguments in a queue when no data is in localstorage and make a HTTP request for data', () => { - const auctionDetails = {dummy: 'obj', auction: 'foo'}; + const auctionDetails = { dummy: 'obj', auction: 'foo' }; nodalsAiRtdSubmodule.onAuctionEndEvent( auctionDetails, validConfig, permissiveUserConsent ); @@ -898,7 +898,7 @@ describe('NodalsAI RTD Provider', () => { data: successPubEndpointResponse, createdAt: Date.now(), }); - const auctionDetails = {dummy: 'obj', auction: 'foo'}; + const auctionDetails = { dummy: 'obj', auction: 'foo' }; nodalsAiRtdSubmodule.onAuctionEndEvent( auctionDetails, validConfig, permissiveUserConsent ); @@ -907,7 +907,7 @@ describe('NodalsAI RTD Provider', () => { expect(window.$nodals.cmdQueue).to.be.an('array').with.length(1); expect(window.$nodals.cmdQueue[0].cmd).to.equal('onAuctionEndEvent'); expect(window.$nodals.cmdQueue[0].runtimeFacts).to.have.keys(['prebid.version', 'page.url']); - expect(window.$nodals.cmdQueue[0].data).to.deep.include({config: validConfig, auctionDetails, userConsent: permissiveUserConsent }); + expect(window.$nodals.cmdQueue[0].data).to.deep.include({ config: validConfig, auctionDetails, userConsent: permissiveUserConsent }); expect(window.$nodals.cmdQueue[0].data.storedData).to.have.property('deps').that.deep.equals( successPubEndpointResponse.deps); expect(window.$nodals.cmdQueue[0].data.storedData).to.have.property('facts').that.deep.includes( @@ -924,7 +924,7 @@ describe('NodalsAI RTD Provider', () => { }); const engine = createTargetingEngineStub(); const userConsent = generateGdprConsent(); - const auctionDetails = {dummy: 'obj', auction: 'foo'}; + const auctionDetails = { dummy: 'obj', auction: 'foo' }; nodalsAiRtdSubmodule.onAuctionEndEvent( auctionDetails, validConfig, permissiveUserConsent ); @@ -947,7 +947,7 @@ describe('NodalsAI RTD Provider', () => { createdAt: Date.now(), }); const engine = createTargetingEngineStub(); - const auctionDetails = {dummy: 'obj', auction: 'foo'}; + const auctionDetails = { dummy: 'obj', auction: 'foo' }; const configWithManagedConsent = { params: { propertyId: '10312dd2', publisherProvidedConsent: true } }; nodalsAiRtdSubmodule.onAuctionEndEvent( auctionDetails, configWithManagedConsent, leastPermissiveUserConsent diff --git a/test/spec/modules/novatiqIdSystem_spec.js b/test/spec/modules/novatiqIdSystem_spec.js index b8906a9a1cc..1ea33ebd651 100644 --- a/test/spec/modules/novatiqIdSystem_spec.js +++ b/test/spec/modules/novatiqIdSystem_spec.js @@ -158,7 +158,7 @@ describe('novatiqIdSystem', function () { const novatiqId = {}; novatiqId.id = '81b001ec-8914-488c-a96e-8c220d4ee08895ef'; novatiqId.syncResponse = 2; - var config = {params: {removeAdditionalInfo: true}}; + var config = { params: { removeAdditionalInfo: true } }; const response = novatiqIdSubmodule.decode(novatiqId, config); expect(response.novatiq.ext.syncResponse).should.be.not.empty; expect(response.novatiq.snowflake.id).should.be.not.empty; diff --git a/test/spec/modules/oftmediaRtdProvider_spec.js b/test/spec/modules/oftmediaRtdProvider_spec.js index c8b43b14853..20746d9d955 100644 --- a/test/spec/modules/oftmediaRtdProvider_spec.js +++ b/test/spec/modules/oftmediaRtdProvider_spec.js @@ -17,7 +17,7 @@ const RTD_CONFIG = { bidderCode: 'appnexus', enrichRequest: true }, - }, ], + },], }; const TIMEOUT = 10; diff --git a/test/spec/modules/oguryBidAdapter_spec.js b/test/spec/modules/oguryBidAdapter_spec.js index 3cd7542f6c2..f6c09c5a829 100644 --- a/test/spec/modules/oguryBidAdapter_spec.js +++ b/test/spec/modules/oguryBidAdapter_spec.js @@ -3,7 +3,7 @@ import sinon from 'sinon'; import { spec, ortbConverterProps } from 'modules/oguryBidAdapter'; import * as utils from 'src/utils.js'; import { server } from '../../mocks/xhr.js'; -import {getDevicePixelRatio} from '../../../libraries/devicePixelRatio/devicePixelRatio.js'; +import { getDevicePixelRatio } from '../../../libraries/devicePixelRatio/devicePixelRatio.js'; const BID_URL = 'https://mweb-hb.presage.io/api/header-bidding-request'; const TIMEOUT_URL = 'https://ms-ads-monitoring-events.presage.io/bid_timeout' @@ -114,8 +114,8 @@ describe('OguryBidAdapter', () => { bids: bidRequests, bidderRequestId: 'mock-uuid', auctionId: bidRequests[0].auctionId, - gdprConsent: {consentString: 'myConsentString', vendorData: {}, gdprApplies: true}, - gppConsent: {gppString: 'myGppString', gppData: {}, applicableSections: [7], parsedSections: {}}, + gdprConsent: { consentString: 'myConsentString', vendorData: {}, gdprApplies: true }, + gppConsent: { gppString: 'myGppString', gppData: {}, applicableSections: [7], parsedSections: {} }, timeout: 1000, ortb2 }; @@ -636,7 +636,7 @@ describe('OguryBidAdapter', () => { beforeEach(() => { windowTopStub = sinon.stub(utils, 'getWindowTop'); - windowTopStub.returns({ location: { href: currentLocation }, devicePixelRatio: stubbedDevicePixelRatio}); + windowTopStub.returns({ location: { href: currentLocation }, devicePixelRatio: stubbedDevicePixelRatio }); }); afterEach(() => { diff --git a/test/spec/modules/omnidexBidAdapter_spec.js b/test/spec/modules/omnidexBidAdapter_spec.js index 66bc66f047f..9a19f7e109b 100644 --- a/test/spec/modules/omnidexBidAdapter_spec.js +++ b/test/spec/modules/omnidexBidAdapter_spec.js @@ -1,14 +1,14 @@ -import {expect} from 'chai'; +import { expect } from 'chai'; import { spec as adapter, createDomain, storage, } from 'modules/omnidexBidAdapter'; import * as utils from 'src/utils.js'; -import {version} from 'package.json'; -import {useFakeTimers} from 'sinon'; -import {BANNER, VIDEO} from '../../../src/mediaTypes.js'; -import {config} from '../../../src/config.js'; +import { version } from 'package.json'; +import { useFakeTimers } from 'sinon'; +import { BANNER, VIDEO } from '../../../src/mediaTypes.js'; +import { config } from '../../../src/config.js'; import { hashCode, extractPID, @@ -19,7 +19,7 @@ import { tryParseJSON, getUniqueDealId, } from '../../../libraries/vidazooUtils/bidderUtils.js'; -import {getGlobal} from '../../../src/prebidGlobal.js'; +import { getGlobal } from '../../../src/prebidGlobal.js'; export const TEST_ID_SYSTEMS = ['britepoolid', 'criteoId', 'id5id', 'idl_env', 'lipb', 'netId', 'parrableId', 'pubcid', 'tdid', 'pubProvidedId']; @@ -100,9 +100,9 @@ const ORTB2_DEVICE = { 'version': ['8', '0', '0'] }, 'browsers': [ - {'brand': 'Not_A Brand', 'version': ['99', '0', '0', '0']}, - {'brand': 'Google Chrome', 'version': ['109', '0', '5414', '119']}, - {'brand': 'Chromium', 'version': ['109', '0', '5414', '119']} + { 'brand': 'Not_A Brand', 'version': ['99', '0', '0', '0'] }, + { 'brand': 'Google Chrome', 'version': ['109', '0', '5414', '119'] }, + { 'brand': 'Chromium', 'version': ['109', '0', '5414', '119'] } ], 'mobile': 1, 'model': 'SM-G955U', @@ -119,7 +119,7 @@ const ORTB2_DEVICE = { model: 'iPhone 12 Pro Max', os: 'iOS', osv: '17.4', - ext: {fiftyonedegrees_deviceId: '17595-133085-133468-18092'}, + ext: { fiftyonedegrees_deviceId: '17595-133085-133468-18092' }, }; const BIDDER_REQUEST = { @@ -190,9 +190,8 @@ const VIDEO_SERVER_RESPONSE = { const ORTB2_OBJ = { "device": ORTB2_DEVICE, - "regs": {"coppa": 0, "gpp": "gpp_string", "gpp_sid": [7]}, - "site": {"content": {"language": "en"} - } + "regs": { "coppa": 0, "gpp": "gpp_string", "gpp_sid": [7] }, + "site": { "content": { "language": "en" } } }; const REQUEST = { @@ -205,7 +204,7 @@ const REQUEST = { function getTopWindowQueryParams() { try { - const parsedUrl = utils.parseUrl(window.top.document.URL, {decodeSearchAsString: true}); + const parsedUrl = utils.parseUrl(window.top.document.URL, { decodeSearchAsString: true }); return parsedUrl.search; } catch (e) { return ''; @@ -327,9 +326,9 @@ describe('OmnidexBidAdapter', function () { 'version': ['8', '0', '0'] }, 'browsers': [ - {'brand': 'Not_A Brand', 'version': ['99', '0', '0', '0']}, - {'brand': 'Google Chrome', 'version': ['109', '0', '5414', '119']}, - {'brand': 'Chromium', 'version': ['109', '0', '5414', '119']} + { 'brand': 'Not_A Brand', 'version': ['99', '0', '0', '0'] }, + { 'brand': 'Google Chrome', 'version': ['109', '0', '5414', '119'] }, + { 'brand': 'Chromium', 'version': ['109', '0', '5414', '119'] } ], 'mobile': 1, 'model': 'SM-G955U', @@ -400,9 +399,9 @@ describe('OmnidexBidAdapter', function () { 'version': ['8', '0', '0'] }, 'browsers': [ - {'brand': 'Not_A Brand', 'version': ['99', '0', '0', '0']}, - {'brand': 'Google Chrome', 'version': ['109', '0', '5414', '119']}, - {'brand': 'Chromium', 'version': ['109', '0', '5414', '119']} + { 'brand': 'Not_A Brand', 'version': ['99', '0', '0', '0'] }, + { 'brand': 'Google Chrome', 'version': ['109', '0', '5414', '119'] }, + { 'brand': 'Chromium', 'version': ['109', '0', '5414', '119'] } ], 'mobile': 1, 'model': 'SM-G955U', @@ -447,7 +446,7 @@ describe('OmnidexBidAdapter', function () { }); describe('getUserSyncs', function () { it('should have valid user sync with iframeEnabled', function () { - const result = adapter.getUserSyncs({iframeEnabled: true}, [SERVER_RESPONSE]); + const result = adapter.getUserSyncs({ iframeEnabled: true }, [SERVER_RESPONSE]); expect(result).to.deep.equal([{ type: 'iframe', @@ -456,7 +455,7 @@ describe('OmnidexBidAdapter', function () { }); it('should have valid user sync with cid on response', function () { - const result = adapter.getUserSyncs({iframeEnabled: true}, [SERVER_RESPONSE]); + const result = adapter.getUserSyncs({ iframeEnabled: true }, [SERVER_RESPONSE]); expect(result).to.deep.equal([{ type: 'iframe', url: 'https://sync.omni-dex.io/api/sync/iframe/?cid=testcid123&gdpr=0&gdpr_consent=&us_privacy=&coppa=0' @@ -464,7 +463,7 @@ describe('OmnidexBidAdapter', function () { }); it('should have valid user sync with pixelEnabled', function () { - const result = adapter.getUserSyncs({pixelEnabled: true}, [SERVER_RESPONSE]); + const result = adapter.getUserSyncs({ pixelEnabled: true }, [SERVER_RESPONSE]); expect(result).to.deep.equal([{ 'url': 'https://sync.omni-dex.io/api/sync/image/?cid=testcid123&gdpr=0&gdpr_consent=&us_privacy=&coppa=0', @@ -476,7 +475,7 @@ describe('OmnidexBidAdapter', function () { config.setConfig({ coppa: 1 }); - const result = adapter.getUserSyncs({iframeEnabled: true}, [SERVER_RESPONSE]); + const result = adapter.getUserSyncs({ iframeEnabled: true }, [SERVER_RESPONSE]); expect(result).to.deep.equal([{ type: 'iframe', url: 'https://sync.omni-dex.io/api/sync/iframe/?cid=testcid123&gdpr=0&gdpr_consent=&us_privacy=&coppa=1' @@ -494,7 +493,7 @@ describe('OmnidexBidAdapter', function () { applicableSections: [7] } - const result = adapter.getUserSyncs({pixelEnabled: true}, [SERVER_RESPONSE], gdprConsent, uspConsent, gppConsent); + const result = adapter.getUserSyncs({ pixelEnabled: true }, [SERVER_RESPONSE], gdprConsent, uspConsent, gppConsent); expect(result).to.deep.equal([{ 'url': 'https://sync.omni-dex.io/api/sync/image/?cid=testcid123&gdpr=1&gdpr_consent=consent_string&us_privacy=usp_string&coppa=1&gpp=gpp_string&gpp_sid=7', @@ -510,12 +509,12 @@ describe('OmnidexBidAdapter', function () { }); it('should return empty array when there is no ad', function () { - const responses = adapter.interpretResponse({price: 1, ad: ''}); + const responses = adapter.interpretResponse({ price: 1, ad: '' }); expect(responses).to.be.empty; }); it('should return empty array when there is no price', function () { - const responses = adapter.interpretResponse({price: null, ad: 'great ad'}); + const responses = adapter.interpretResponse({ price: null, ad: 'great ad' }); expect(responses).to.be.empty; }); @@ -588,9 +587,9 @@ describe('OmnidexBidAdapter', function () { const userId = (function () { switch (idSystemProvider) { case 'lipb': - return {lipbid: id}; + return { lipbid: id }; case 'id5id': - return {uid: id}; + return { uid: id }; default: return id; } @@ -611,7 +610,7 @@ describe('OmnidexBidAdapter', function () { bid.userIdAsEids = [ { "source": "audigent.com", - "uids": [{"id": "fakeidi6j6dlc6e"}] + "uids": [{ "id": "fakeidi6j6dlc6e" }] } ] const requests = adapter.buildRequests([bid], BIDDER_REQUEST); @@ -622,11 +621,11 @@ describe('OmnidexBidAdapter', function () { bid.userIdAsEids = [ { "source": "audigent.com", - "uids": [{"id": "fakeidi6j6dlc6e"}] + "uids": [{ "id": "fakeidi6j6dlc6e" }] }, { "source": "rwdcntrl.net", - "uids": [{"id": "fakeid6f35197d5c", "atype": 1}] + "uids": [{ "id": "fakeid6f35197d5c", "atype": 1 }] } ] const requests = adapter.buildRequests([bid], BIDDER_REQUEST); @@ -641,7 +640,7 @@ describe('OmnidexBidAdapter', function () { eids: [ { "source": "pubcid.org", - "uids": [{"id": "fakeid8888dlc6e"}] + "uids": [{ "id": "fakeid8888dlc6e" }] } ] } @@ -656,11 +655,11 @@ describe('OmnidexBidAdapter', function () { eids: [ { "source": "pubcid.org", - "uids": [{"id": "fakeid8888dlc6e"}] + "uids": [{ "id": "fakeid8888dlc6e" }] }, { "source": "adserver.org", - "uids": [{"id": "fakeid495ff1"}] + "uids": [{ "id": "fakeid495ff1" }] } ] } @@ -673,18 +672,18 @@ describe('OmnidexBidAdapter', function () { describe('alternate param names extractors', function () { it('should return undefined when param not supported', function () { - const cid = extractCID({'c_id': '1'}); - const pid = extractPID({'p_id': '1'}); - const subDomain = extractSubDomain({'sub_domain': 'prebid'}); + const cid = extractCID({ 'c_id': '1' }); + const pid = extractPID({ 'p_id': '1' }); + const subDomain = extractSubDomain({ 'sub_domain': 'prebid' }); expect(cid).to.be.undefined; expect(pid).to.be.undefined; expect(subDomain).to.be.undefined; }); it('should return value when param supported', function () { - const cid = extractCID({'cID': '1'}); - const pid = extractPID({'Pid': '2'}); - const subDomain = extractSubDomain({'subDOMAIN': 'prebid'}); + const cid = extractCID({ 'cID': '1' }); + const pid = extractPID({ 'Pid': '2' }); + const subDomain = extractSubDomain({ 'subDOMAIN': 'prebid' }); expect(cid).to.be.equal('1'); expect(pid).to.be.equal('2'); expect(subDomain).to.be.equal('prebid'); @@ -744,7 +743,7 @@ describe('OmnidexBidAdapter', function () { now }); setStorageItem(storage, 'myKey', 2020); - const {value, created} = getStorageItem(storage, 'myKey'); + const { value, created } = getStorageItem(storage, 'myKey'); expect(created).to.be.equal(now); expect(value).to.be.equal(2020); expect(typeof value).to.be.equal('number'); @@ -760,8 +759,8 @@ describe('OmnidexBidAdapter', function () { }); it('should parse JSON value', function () { - const data = JSON.stringify({event: 'send'}); - const {event} = tryParseJSON(data); + const data = JSON.stringify({ event: 'send' }); + const { event } = tryParseJSON(data); expect(event).to.be.equal('send'); }); diff --git a/test/spec/modules/omsBidAdapter_spec.js b/test/spec/modules/omsBidAdapter_spec.js index f1f7df46843..1ec6ed7c80b 100644 --- a/test/spec/modules/omsBidAdapter_spec.js +++ b/test/spec/modules/omsBidAdapter_spec.js @@ -1,8 +1,9 @@ -import {expect} from 'chai'; +import { expect } from 'chai'; import * as utils from 'src/utils.js'; -import {spec} from 'modules/omsBidAdapter'; -import {newBidder} from 'src/adapters/bidderFactory.js'; +import { spec } from 'modules/omsBidAdapter'; +import { newBidder } from 'src/adapters/bidderFactory.js'; import * as winDimensions from 'src/utils/winDimensions.js'; +import * as adUnits from 'src/utils/adUnits'; const URL = 'https://rt.marphezis.com/hb'; @@ -34,7 +35,11 @@ describe('omsBidAdapter', function () { }; win = { document: { - visibilityState: 'visible' + visibilityState: 'visible', + documentElement: { + clientWidth: 800, + clientHeight: 600, + } }, location: { href: "http:/location" @@ -79,7 +84,8 @@ describe('omsBidAdapter', function () { }]; sandbox = sinon.createSandbox(); - sandbox.stub(document, 'getElementById').withArgs('adunit-code').returns(element); + sandbox.stub(adUnits, 'getAdUnitElement').returns(element); + sandbox.stub(winDimensions, 'getWinDimensions').returns(win); sandbox.stub(utils, 'getWindowTop').returns(win); sandbox.stub(utils, 'getWindowSelf').returns(win); }); @@ -135,7 +141,7 @@ describe('omsBidAdapter', function () { 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}]); + expect(payload.imp[0].banner.format).to.deep.equal([{ w: 300, h: 250 }, { w: 300, h: 600 }]); }); it('sets the proper video object when ad unit media type is video', function () { @@ -187,7 +193,7 @@ describe('omsBidAdapter', function () { bidRequests[0].mediaTypes.banner.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}]); + expect(payload.imp[0].banner.format).to.deep.equal([{ w: 300, h: 250 }]); }); it('sends bidfloor param if present', function () { @@ -257,11 +263,21 @@ describe('omsBidAdapter', function () { }); it('sends coppa', function () { - const data = JSON.parse(spec.buildRequests(bidRequests, {ortb2: {regs: {coppa: 1}}}).data) + const data = JSON.parse(spec.buildRequests(bidRequests, { ortb2: { regs: { coppa: 1 } } }).data) expect(data.regs).to.not.be.undefined; expect(data.regs.coppa).to.equal(1); }); + it('sends instl property when ortb2Imp.instl = 1', function () { + const data = JSON.parse(spec.buildRequests([{ ...bidRequests[0], ortb2Imp: { instl: 1 } }]).data); + expect(data.imp[0].instl).to.equal(1); + }); + + it('ignores instl property when ortb2Imp.instl is falsy', function () { + const data = JSON.parse(spec.buildRequests(bidRequests).data); + expect(data.imp[0].instl).to.be.undefined; + }); + it('sends schain', function () { const data = JSON.parse(spec.buildRequests(bidRequests).data); expect(data).to.not.be.undefined; @@ -329,7 +345,7 @@ describe('omsBidAdapter', function () { context('when element is fully in view', function () { it('returns 100', function () { - Object.assign(element, {width: 600, height: 400}); + 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); @@ -338,7 +354,7 @@ describe('omsBidAdapter', function () { context('when element is out of view', function () { it('returns 0', function () { - Object.assign(element, {x: -300, y: 0, width: 207, height: 320}); + 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); @@ -347,9 +363,7 @@ describe('omsBidAdapter', function () { context('when element is partially in view', function () { it('returns percentage', function () { - const getWinDimensionsStub = sandbox.stub(winDimensions, 'getWinDimensions') - getWinDimensionsStub.returns({ innerHeight: win.innerHeight, innerWidth: win.innerWidth }); - Object.assign(element, {width: 800, height: 800}); + 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); @@ -358,9 +372,7 @@ describe('omsBidAdapter', function () { context('when width or height of the element is zero', function () { it('try to use alternative values', function () { - const getWinDimensionsStub = sandbox.stub(winDimensions, 'getWinDimensions') - getWinDimensionsStub.returns({ innerHeight: win.innerHeight, innerWidth: win.innerWidth }); - Object.assign(element, {width: 0, height: 0}); + Object.assign(element, { width: 0, height: 0 }); bidRequests[0].mediaTypes.banner.sizes = [[800, 2400]]; const request = spec.buildRequests(bidRequests); const payload = JSON.parse(request.data); @@ -370,7 +382,7 @@ describe('omsBidAdapter', function () { context('when nested iframes', function () { it('returns \'na\'', function () { - Object.assign(element, {width: 600, height: 400}); + Object.assign(element, { width: 600, height: 400 }); utils.getWindowTop.restore(); utils.getWindowSelf.restore(); @@ -385,7 +397,7 @@ describe('omsBidAdapter', function () { context('when tab is inactive', function () { it('returns 0', function () { - Object.assign(element, {width: 600, height: 400}); + Object.assign(element, { width: 600, height: 400 }); utils.getWindowTop.restore(); win.document.visibilityState = 'hidden'; diff --git a/test/spec/modules/oneKeyRtdProvider_spec.js b/test/spec/modules/oneKeyRtdProvider_spec.js index 70023e35196..b43965db2f8 100644 --- a/test/spec/modules/oneKeyRtdProvider_spec.js +++ b/test/spec/modules/oneKeyRtdProvider_spec.js @@ -1,5 +1,5 @@ -import {oneKeyDataSubmodule} from 'modules/oneKeyRtdProvider.js'; -import {getAdUnits} from '../../fixtures/fixtures.js'; +import { oneKeyDataSubmodule } from 'modules/oneKeyRtdProvider.js'; +import { getAdUnits } from '../../fixtures/fixtures.js'; const defaultSeed = { version: '0.1', @@ -91,7 +91,7 @@ describe('oneKeyDataSubmodule', () => { rtdConfig: { params: { proxyHostName: 'host', - bidders: [ 'bidder42', 'bidder24' ] + bidders: ['bidder42', 'bidder24'] } }, expectedFragment: { diff --git a/test/spec/modules/onetagBidAdapter_spec.js b/test/spec/modules/onetagBidAdapter_spec.js index dd37a929b45..8acdae76265 100644 --- a/test/spec/modules/onetagBidAdapter_spec.js +++ b/test/spec/modules/onetagBidAdapter_spec.js @@ -26,7 +26,7 @@ const getFloor = function(params) { floorPrice = 5.0; break; } - return {currency: params.currency, floor: floorPrice}; + return { currency: params.currency, floor: floorPrice }; }; describe('onetag', function () { @@ -110,7 +110,7 @@ describe('onetag', function () { currency: 'EUR', schema: { delimiter: '|', - fields: [ 'mediaType', 'size' ] + fields: ['mediaType', 'size'] }, values: { 'native|*': 1.10 @@ -180,7 +180,7 @@ describe('onetag', function () { currency: 'EUR', schema: { delimiter: '|', - fields: [ 'mediaType', 'size' ] + fields: ['mediaType', 'size'] }, values: { 'native|*': 1.10 @@ -201,7 +201,7 @@ describe('onetag', function () { currency: 'EUR', schema: { delimiter: '|', - fields: [ 'mediaType', 'size' ] + fields: ['mediaType', 'size'] }, values: { 'banner|300x250': 0.10 @@ -224,7 +224,7 @@ describe('onetag', function () { currency: 'EUR', schema: { delimiter: '|', - fields: [ 'mediaType', 'size' ] + fields: ['mediaType', 'size'] }, values: { 'video|640x480': 0.10 @@ -246,7 +246,7 @@ describe('onetag', function () { currency: 'EUR', schema: { delimiter: '|', - fields: [ 'mediaType', 'size' ] + fields: ['mediaType', 'size'] }, values: { 'video|640x480': 0.10 @@ -694,7 +694,7 @@ describe('onetag', function () { cids: ['iris_c73g5jq96mwso4d8'] }, // the bare minimum are the IDs. These IDs are the ones from the new IAB Content Taxonomy v3 - segment: [ { id: '687' }, { id: '123' } ] + segment: [{ id: '687' }, { id: '123' }] }] }, ext: { @@ -768,38 +768,6 @@ describe('onetag', function () { expect(payload.ortb2).to.exist; expect(payload.ortb2).to.exist.and.to.deep.equal(dsa); }); - it('Should send FLEDGE eligibility flag when FLEDGE is enabled', function () { - const bidderRequest = { - 'bidderCode': 'onetag', - 'auctionId': '1d1a030790a475', - 'bidderRequestId': '22edbae2733bf6', - 'timeout': 3000, - 'paapi': { - 'enabled': true - } - }; - const serverRequest = spec.buildRequests([bannerBid], bidderRequest); - const payload = JSON.parse(serverRequest.data); - - expect(payload.fledgeEnabled).to.exist; - expect(payload.fledgeEnabled).to.exist.and.to.equal(bidderRequest.paapi.enabled); - }); - it('Should send FLEDGE eligibility flag when FLEDGE is not enabled', function () { - const bidderRequest = { - 'bidderCode': 'onetag', - 'auctionId': '1d1a030790a475', - 'bidderRequestId': '22edbae2733bf6', - 'timeout': 3000, - paapi: { - enabled: false - } - }; - const serverRequest = spec.buildRequests([bannerBid], bidderRequest); - const payload = JSON.parse(serverRequest.data); - - expect(payload.fledgeEnabled).to.exist; - expect(payload.fledgeEnabled).to.exist.and.to.equal(bidderRequest.paapi.enabled); - }); it('Should send FLEDGE eligibility flag set to false when fledgeEnabled is not defined', function () { const bidderRequest = { 'bidderCode': 'onetag', @@ -821,13 +789,7 @@ describe('onetag', function () { const requestData = JSON.parse(request.data); it('Returns an array of valid server responses if response object is valid', function () { const interpretedResponse = spec.interpretResponse(response, request); - const fledgeInterpretedResponse = spec.interpretResponse(fledgeResponse, request); expect(interpretedResponse).to.be.an('array').that.is.not.empty; - expect(fledgeInterpretedResponse).to.be.an('object'); - expect(fledgeInterpretedResponse.bids).to.satisfy(function (value) { - return value === null || Array.isArray(value); - }); - expect(fledgeInterpretedResponse.paapi).to.be.an('array').that.is.not.empty; for (let i = 0; i < interpretedResponse.length; i++) { const dataItem = interpretedResponse[i]; expect(dataItem).to.include.all.keys('requestId', 'cpm', 'width', 'height', 'ttl', 'creativeId', 'netRevenue', 'currency', 'meta', 'dealId'); @@ -875,8 +837,8 @@ describe('onetag', function () { }, 'adrender': 1 }; - const responseWithDsa = {...response}; - responseWithDsa.body.bids.forEach(bid => bid.dsa = {...dsaResponseObj}); + const responseWithDsa = { ...response }; + responseWithDsa.body.bids.forEach(bid => bid.dsa = { ...dsaResponseObj }); const serverResponse = spec.interpretResponse(responseWithDsa, request); serverResponse.forEach(bid => expect(bid.meta.dsa).to.deep.equals(dsaResponseObj)); }); diff --git a/test/spec/modules/onomagicBidAdapter_spec.js b/test/spec/modules/onomagicBidAdapter_spec.js index 6b4d44f49ed..2f382b11908 100644 --- a/test/spec/modules/onomagicBidAdapter_spec.js +++ b/test/spec/modules/onomagicBidAdapter_spec.js @@ -3,6 +3,7 @@ import * as utils from 'src/utils.js'; import { spec } from 'modules/onomagicBidAdapter.js'; import { newBidder } from 'src/adapters/bidderFactory.js'; import * as winDimensions from 'src/utils/winDimensions.js'; +import * as adUnits from 'src/utils/adUnits'; const URL = 'https://bidder.onomagic.com/hb'; @@ -34,9 +35,12 @@ describe('onomagicBidAdapter', function() { }; win = { document: { - visibilityState: 'visible' + visibilityState: 'visible', + documentElement: { + clientWidth: 800, + clientHeight: 600 + } }, - innerWidth: 800, innerHeight: 600 }; @@ -57,7 +61,8 @@ describe('onomagicBidAdapter', function() { }]; sandbox = sinon.createSandbox(); - sandbox.stub(document, 'getElementById').withArgs('adunit-code').returns(element); + sandbox.stub(winDimensions, 'getWinDimensions').returns(win); + sandbox.stub(adUnits, 'getAdUnitElement').returns(element); sandbox.stub(utils, 'getWindowTop').returns(win); sandbox.stub(utils, 'getWindowSelf').returns(win); }); @@ -113,14 +118,14 @@ describe('onomagicBidAdapter', function() { 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}]); + 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].mediaTypes.banner.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}]); + expect(payload.imp[0].banner.format).to.deep.equal([{ w: 300, h: 250 }]); }); it('sends bidfloor param if present', function () { @@ -162,8 +167,6 @@ describe('onomagicBidAdapter', function() { context('when element is partially in view', function() { it('returns percentage', function() { - const getWinDimensionsStub = sandbox.stub(winDimensions, 'getWinDimensions') - getWinDimensionsStub.returns({ innerHeight: win.innerHeight, innerWidth: win.innerWidth }); Object.assign(element, { width: 800, height: 800 }); const request = spec.buildRequests(bidRequests); const payload = JSON.parse(request.data); @@ -173,8 +176,6 @@ describe('onomagicBidAdapter', function() { context('when width or height of the element is zero', function() { it('try to use alternative values', function() { - const getWinDimensionsStub = sandbox.stub(winDimensions, 'getWinDimensions') - getWinDimensionsStub.returns({ innerHeight: win.innerHeight, innerWidth: win.innerWidth }); Object.assign(element, { width: 0, height: 0 }); bidRequests[0].mediaTypes.banner.sizes = [[800, 2400]]; const request = spec.buildRequests(bidRequests); @@ -287,7 +288,7 @@ describe('onomagicBidAdapter', function() { }); describe('getUserSyncs ', () => { - const syncOptions = {iframeEnabled: true, pixelEnabled: true}; + const syncOptions = { iframeEnabled: true, pixelEnabled: true }; it('should not return', () => { const returnStatement = spec.getUserSyncs(syncOptions, []); diff --git a/test/spec/modules/ooloAnalyticsAdapter_spec.js b/test/spec/modules/ooloAnalyticsAdapter_spec.js index 3a86f567f05..fe088b18bdc 100644 --- a/test/spec/modules/ooloAnalyticsAdapter_spec.js +++ b/test/spec/modules/ooloAnalyticsAdapter_spec.js @@ -1,6 +1,6 @@ import ooloAnalytics, { PAGEVIEW_ID } from 'modules/ooloAnalyticsAdapter.js'; -import {expect} from 'chai'; -import {server} from 'test/mocks/xhr.js'; +import { expect } from 'chai'; +import { server } from 'test/mocks/xhr.js'; import { EVENTS } from 'src/constants.js' import * as events from 'src/events' import { config } from 'src/config'; diff --git a/test/spec/modules/opaMarketplaceBidAdapter_spec.js b/test/spec/modules/opaMarketplaceBidAdapter_spec.js index 4a7b4895aef..297f4c79fe1 100644 --- a/test/spec/modules/opaMarketplaceBidAdapter_spec.js +++ b/test/spec/modules/opaMarketplaceBidAdapter_spec.js @@ -1,14 +1,14 @@ -import {expect} from 'chai'; +import { expect } from 'chai'; import { spec as adapter, createDomain, storage, } from 'modules/opaMarketplaceBidAdapter'; import * as utils from 'src/utils.js'; -import {version} from 'package.json'; -import {useFakeTimers} from 'sinon'; -import {BANNER, VIDEO} from '../../../src/mediaTypes.js'; -import {config} from '../../../src/config.js'; +import { version } from 'package.json'; +import { useFakeTimers } from 'sinon'; +import { BANNER, VIDEO } from '../../../src/mediaTypes.js'; +import { config } from '../../../src/config.js'; import { hashCode, extractPID, @@ -19,7 +19,7 @@ import { tryParseJSON, getUniqueDealId, } from '../../../libraries/vidazooUtils/bidderUtils.js'; -import {getGlobal} from '../../../src/prebidGlobal.js'; +import { getGlobal } from '../../../src/prebidGlobal.js'; export const TEST_ID_SYSTEMS = ['britepoolid', 'criteoId', 'id5id', 'idl_env', 'lipb', 'netId', 'parrableId', 'pubcid', 'tdid', 'pubProvidedId']; @@ -100,9 +100,9 @@ const ORTB2_DEVICE = { 'version': ['8', '0', '0'] }, 'browsers': [ - {'brand': 'Not_A Brand', 'version': ['99', '0', '0', '0']}, - {'brand': 'Google Chrome', 'version': ['109', '0', '5414', '119']}, - {'brand': 'Chromium', 'version': ['109', '0', '5414', '119']} + { 'brand': 'Not_A Brand', 'version': ['99', '0', '0', '0'] }, + { 'brand': 'Google Chrome', 'version': ['109', '0', '5414', '119'] }, + { 'brand': 'Chromium', 'version': ['109', '0', '5414', '119'] } ], 'mobile': 1, 'model': 'SM-G955U', @@ -119,7 +119,7 @@ const ORTB2_DEVICE = { model: 'iPhone 12 Pro Max', os: 'iOS', osv: '17.4', - ext: {fiftyonedegrees_deviceId: '17595-133085-133468-18092'}, + ext: { fiftyonedegrees_deviceId: '17595-133085-133468-18092' }, }; const BIDDER_REQUEST = { @@ -190,9 +190,8 @@ const VIDEO_SERVER_RESPONSE = { const ORTB2_OBJ = { "device": ORTB2_DEVICE, - "regs": {"coppa": 0, "gpp": "gpp_string", "gpp_sid": [7]}, - "site": {"content": {"language": "en"} - } + "regs": { "coppa": 0, "gpp": "gpp_string", "gpp_sid": [7] }, + "site": { "content": { "language": "en" } } }; const REQUEST = { @@ -205,7 +204,7 @@ const REQUEST = { function getTopWindowQueryParams() { try { - const parsedUrl = utils.parseUrl(window.top.document.URL, {decodeSearchAsString: true}); + const parsedUrl = utils.parseUrl(window.top.document.URL, { decodeSearchAsString: true }); return parsedUrl.search; } catch (e) { return ''; @@ -327,9 +326,9 @@ describe('OpaMarketplaceBidAdapter', function () { 'version': ['8', '0', '0'] }, 'browsers': [ - {'brand': 'Not_A Brand', 'version': ['99', '0', '0', '0']}, - {'brand': 'Google Chrome', 'version': ['109', '0', '5414', '119']}, - {'brand': 'Chromium', 'version': ['109', '0', '5414', '119']} + { 'brand': 'Not_A Brand', 'version': ['99', '0', '0', '0'] }, + { 'brand': 'Google Chrome', 'version': ['109', '0', '5414', '119'] }, + { 'brand': 'Chromium', 'version': ['109', '0', '5414', '119'] } ], 'mobile': 1, 'model': 'SM-G955U', @@ -400,9 +399,9 @@ describe('OpaMarketplaceBidAdapter', function () { 'version': ['8', '0', '0'] }, 'browsers': [ - {'brand': 'Not_A Brand', 'version': ['99', '0', '0', '0']}, - {'brand': 'Google Chrome', 'version': ['109', '0', '5414', '119']}, - {'brand': 'Chromium', 'version': ['109', '0', '5414', '119']} + { 'brand': 'Not_A Brand', 'version': ['99', '0', '0', '0'] }, + { 'brand': 'Google Chrome', 'version': ['109', '0', '5414', '119'] }, + { 'brand': 'Chromium', 'version': ['109', '0', '5414', '119'] } ], 'mobile': 1, 'model': 'SM-G955U', @@ -447,7 +446,7 @@ describe('OpaMarketplaceBidAdapter', function () { }); describe('getUserSyncs', function () { it('should have valid user sync with iframeEnabled', function () { - const result = adapter.getUserSyncs({iframeEnabled: true}, [SERVER_RESPONSE]); + const result = adapter.getUserSyncs({ iframeEnabled: true }, [SERVER_RESPONSE]); expect(result).to.have.length(1); const url = new URL(result[0].url); expect(result[0].type).to.equal('iframe') @@ -457,7 +456,7 @@ describe('OpaMarketplaceBidAdapter', function () { }); it('should have valid user sync with cid on response', function () { - const result = adapter.getUserSyncs({iframeEnabled: true}, [SERVER_RESPONSE]); + const result = adapter.getUserSyncs({ iframeEnabled: true }, [SERVER_RESPONSE]); expect(result).to.have.length(1); const url = new URL(result[0].url); expect(result[0].type).to.equal('iframe') @@ -467,7 +466,7 @@ describe('OpaMarketplaceBidAdapter', function () { }); it('should have valid user sync with pixelEnabled', function () { - const result = adapter.getUserSyncs({pixelEnabled: true}, [SERVER_RESPONSE]); + const result = adapter.getUserSyncs({ pixelEnabled: true }, [SERVER_RESPONSE]); expect(result).to.have.length(1); const url = new URL(result[0].url); expect(result[0].type).to.equal('image') @@ -480,7 +479,7 @@ describe('OpaMarketplaceBidAdapter', function () { config.setConfig({ coppa: 1 }); - const result = adapter.getUserSyncs({iframeEnabled: true}, [SERVER_RESPONSE]); + const result = adapter.getUserSyncs({ iframeEnabled: true }, [SERVER_RESPONSE]); expect(result).to.have.length(1); const url = new URL(result[0].url); expect(result[0].type).to.equal('iframe') @@ -500,7 +499,7 @@ describe('OpaMarketplaceBidAdapter', function () { applicableSections: [7] } - const result = adapter.getUserSyncs({pixelEnabled: true}, [SERVER_RESPONSE], gdprConsent, uspConsent, gppConsent); + const result = adapter.getUserSyncs({ pixelEnabled: true }, [SERVER_RESPONSE], gdprConsent, uspConsent, gppConsent); const url = new URL(result[0].url); expect(result).to.deep.equal([{ 'url': 'https://sync.opamarketplace.com/api/sync/image/?cid=testcid123&gdpr=1&gdpr_consent=consent_string&us_privacy=usp_string&coppa=1&gpp=gpp_string&gpp_sid=7', @@ -520,12 +519,12 @@ describe('OpaMarketplaceBidAdapter', function () { }); it('should return empty array when there is no ad', function () { - const responses = adapter.interpretResponse({price: 1, ad: ''}); + const responses = adapter.interpretResponse({ price: 1, ad: '' }); expect(responses).to.be.empty; }); it('should return empty array when there is no price', function () { - const responses = adapter.interpretResponse({price: null, ad: 'great ad'}); + const responses = adapter.interpretResponse({ price: null, ad: 'great ad' }); expect(responses).to.be.empty; }); @@ -598,9 +597,9 @@ describe('OpaMarketplaceBidAdapter', function () { const userId = (function () { switch (idSystemProvider) { case 'lipb': - return {lipbid: id}; + return { lipbid: id }; case 'id5id': - return {uid: id}; + return { uid: id }; default: return id; } @@ -621,7 +620,7 @@ describe('OpaMarketplaceBidAdapter', function () { bid.userIdAsEids = [ { "source": "audigent.com", - "uids": [{"id": "fakeidi6j6dlc6e"}] + "uids": [{ "id": "fakeidi6j6dlc6e" }] } ] const requests = adapter.buildRequests([bid], BIDDER_REQUEST); @@ -632,11 +631,11 @@ describe('OpaMarketplaceBidAdapter', function () { bid.userIdAsEids = [ { "source": "audigent.com", - "uids": [{"id": "fakeidi6j6dlc6e"}] + "uids": [{ "id": "fakeidi6j6dlc6e" }] }, { "source": "rwdcntrl.net", - "uids": [{"id": "fakeid6f35197d5c", "atype": 1}] + "uids": [{ "id": "fakeid6f35197d5c", "atype": 1 }] } ] const requests = adapter.buildRequests([bid], BIDDER_REQUEST); @@ -651,7 +650,7 @@ describe('OpaMarketplaceBidAdapter', function () { eids: [ { "source": "pubcid.org", - "uids": [{"id": "fakeid8888dlc6e"}] + "uids": [{ "id": "fakeid8888dlc6e" }] } ] } @@ -666,11 +665,11 @@ describe('OpaMarketplaceBidAdapter', function () { eids: [ { "source": "pubcid.org", - "uids": [{"id": "fakeid8888dlc6e"}] + "uids": [{ "id": "fakeid8888dlc6e" }] }, { "source": "adserver.org", - "uids": [{"id": "fakeid495ff1"}] + "uids": [{ "id": "fakeid495ff1" }] } ] } @@ -683,18 +682,18 @@ describe('OpaMarketplaceBidAdapter', function () { describe('alternate param names extractors', function () { it('should return undefined when param not supported', function () { - const cid = extractCID({'c_id': '1'}); - const pid = extractPID({'p_id': '1'}); - const subDomain = extractSubDomain({'sub_domain': 'prebid'}); + const cid = extractCID({ 'c_id': '1' }); + const pid = extractPID({ 'p_id': '1' }); + const subDomain = extractSubDomain({ 'sub_domain': 'prebid' }); expect(cid).to.be.undefined; expect(pid).to.be.undefined; expect(subDomain).to.be.undefined; }); it('should return value when param supported', function () { - const cid = extractCID({'cID': '1'}); - const pid = extractPID({'Pid': '2'}); - const subDomain = extractSubDomain({'subDOMAIN': 'prebid'}); + const cid = extractCID({ 'cID': '1' }); + const pid = extractPID({ 'Pid': '2' }); + const subDomain = extractSubDomain({ 'subDOMAIN': 'prebid' }); expect(cid).to.be.equal('1'); expect(pid).to.be.equal('2'); expect(subDomain).to.be.equal('prebid'); @@ -754,7 +753,7 @@ describe('OpaMarketplaceBidAdapter', function () { now }); setStorageItem(storage, 'myKey', 2020); - const {value, created} = getStorageItem(storage, 'myKey'); + const { value, created } = getStorageItem(storage, 'myKey'); expect(created).to.be.equal(now); expect(value).to.be.equal(2020); expect(typeof value).to.be.equal('number'); @@ -770,8 +769,8 @@ describe('OpaMarketplaceBidAdapter', function () { }); it('should parse JSON value', function () { - const data = JSON.stringify({event: 'send'}); - const {event} = tryParseJSON(data); + const data = JSON.stringify({ event: 'send' }); + const { event } = tryParseJSON(data); expect(event).to.be.equal('send'); }); diff --git a/test/spec/modules/openPairIdSystem_spec.js b/test/spec/modules/openPairIdSystem_spec.js index e6fbd653749..07a0cbc82a9 100644 --- a/test/spec/modules/openPairIdSystem_spec.js +++ b/test/spec/modules/openPairIdSystem_spec.js @@ -10,7 +10,7 @@ import { setSubmoduleRegistry } from '../../../modules/userId/index.js'; -import {createEidsArray, getEids} from '../../../modules/userId/eids.js'; +import { createEidsArray, getEids } from '../../../modules/userId/eids.js'; describe('openPairId', function () { let sandbox; @@ -26,25 +26,26 @@ describe('openPairId', function () { it('should read publisher id from specified clean room if configured with storageKey', function() { const publisherIds = ['dGVzdC1wYWlyLWlkMQ==', 'test-pair-id2', 'test-pair-id3']; - sandbox.stub(storage, 'getDataFromLocalStorage').withArgs('habu_pairId_custom').returns(btoa(JSON.stringify({'envelope': publisherIds}))); + sandbox.stub(storage, 'getDataFromLocalStorage').withArgs('habu_pairId_custom').returns(btoa(JSON.stringify({ 'envelope': publisherIds }))); const id = openPairIdSubmodule.getId({ params: { habu: { storageKey: 'habu_pairId_custom' } - }}) + } + }) - expect(id).to.be.deep.equal({id: publisherIds}); + expect(id).to.be.deep.equal({ id: publisherIds }); }); it('should read publisher id from liveramp with default storageKey and additional clean room with configured storageKey', function() { const getDataStub = sandbox.stub(storage, 'getDataFromLocalStorage'); const liveRampPublisherIds = ['lr-test-pair-id1', 'lr-test-pair-id2', 'lr-test-pair-id3']; - getDataStub.withArgs('_lr_pairId').returns(btoa(JSON.stringify({'envelope': liveRampPublisherIds}))); + getDataStub.withArgs('_lr_pairId').returns(btoa(JSON.stringify({ 'envelope': liveRampPublisherIds }))); const habuPublisherIds = ['habu-test-pair-id1', 'habu-test-pair-id2', 'habu-test-pair-id3']; - getDataStub.withArgs('habu_pairId_custom').returns(btoa(JSON.stringify({'envelope': habuPublisherIds}))); + getDataStub.withArgs('habu_pairId_custom').returns(btoa(JSON.stringify({ 'envelope': habuPublisherIds }))); const id = openPairIdSubmodule.getId({ params: { @@ -52,9 +53,10 @@ describe('openPairId', function () { storageKey: 'habu_pairId_custom' }, liveramp: {} - }}) + } + }) - expect(id).to.be.deep.equal({id: habuPublisherIds.concat(liveRampPublisherIds)}); + expect(id).to.be.deep.equal({ id: habuPublisherIds.concat(liveRampPublisherIds) }); }); it('should log an error if no ID is found when getId', function() { @@ -67,7 +69,7 @@ describe('openPairId', function () { sandbox.stub(storage, 'getDataFromLocalStorage').withArgs('pairId').returns(btoa(JSON.stringify(publisherIds))); const id = openPairIdSubmodule.getId({ params: {} }); - expect(id).to.be.deep.equal({id: publisherIds}); + expect(id).to.be.deep.equal({ id: publisherIds }); }); it('should read publisher id from cookie if exists', function() { @@ -75,39 +77,42 @@ describe('openPairId', function () { sandbox.stub(storage, 'getCookie').withArgs('pairId').returns(btoa(JSON.stringify(publisherIds))); const id = openPairIdSubmodule.getId({ params: {} }); - expect(id).to.be.deep.equal({id: publisherIds}); + expect(id).to.be.deep.equal({ id: publisherIds }); }); it('should read publisher id from default liveramp envelope local storage key if configured', function() { const publisherIds = ['test-pair-id1', 'test-pair-id2', 'test-pair-id3']; - sandbox.stub(storage, 'getDataFromLocalStorage').withArgs('_lr_pairId').returns(btoa(JSON.stringify({'envelope': publisherIds}))); + sandbox.stub(storage, 'getDataFromLocalStorage').withArgs('_lr_pairId').returns(btoa(JSON.stringify({ 'envelope': publisherIds }))); const id = openPairIdSubmodule.getId({ params: { liveramp: {} - }}) - expect(id).to.be.deep.equal({id: publisherIds}) + } + }) + expect(id).to.be.deep.equal({ id: publisherIds }) }); it('should read publisher id from default liveramp envelope cookie entry if configured', function() { const publisherIds = ['test-pair-id4', 'test-pair-id5', 'test-pair-id6']; - sandbox.stub(storage, 'getDataFromLocalStorage').withArgs('_lr_pairId').returns(btoa(JSON.stringify({'envelope': publisherIds}))); + sandbox.stub(storage, 'getDataFromLocalStorage').withArgs('_lr_pairId').returns(btoa(JSON.stringify({ 'envelope': publisherIds }))); const id = openPairIdSubmodule.getId({ params: { liveramp: {} - }}) - expect(id).to.be.deep.equal({id: publisherIds}) + } + }) + expect(id).to.be.deep.equal({ id: publisherIds }) }); it('should read publisher id from specified liveramp envelope cookie entry if configured with storageKey', function() { const publisherIds = ['test-pair-id7', 'test-pair-id8', 'test-pair-id9']; - sandbox.stub(storage, 'getDataFromLocalStorage').withArgs('lr_pairId_custom').returns(btoa(JSON.stringify({'envelope': publisherIds}))); + sandbox.stub(storage, 'getDataFromLocalStorage').withArgs('lr_pairId_custom').returns(btoa(JSON.stringify({ 'envelope': publisherIds }))); const id = openPairIdSubmodule.getId({ params: { liveramp: { storageKey: 'lr_pairId_custom' } - }}) - expect(id).to.be.deep.equal({id: publisherIds}) + } + }) + expect(id).to.be.deep.equal({ id: publisherIds }) }); it('should not get data from storage if local storage and cookies are disabled', function () { @@ -155,7 +160,7 @@ describe('openPairId', function () { const publisherIds = [value]; - const stored = btoa(JSON.stringify({'envelope': publisherIds})); + const stored = btoa(JSON.stringify({ 'envelope': publisherIds })); const read = JSON.parse(atob(stored)); diff --git a/test/spec/modules/openwebBidAdapter_spec.js b/test/spec/modules/openwebBidAdapter_spec.js index 197cdb85d11..71649ddfbcc 100644 --- a/test/spec/modules/openwebBidAdapter_spec.js +++ b/test/spec/modules/openwebBidAdapter_spec.js @@ -2,9 +2,9 @@ import { expect } from 'chai'; import { spec } from 'modules/openwebBidAdapter.js'; import { newBidder } from 'src/adapters/bidderFactory.js'; import { config } from 'src/config.js'; -import {BANNER, NATIVE, VIDEO} from '../../../src/mediaTypes.js'; +import { BANNER, NATIVE, VIDEO } from '../../../src/mediaTypes.js'; import * as utils from 'src/utils.js'; -import {decorateAdUnitsWithNativeParams} from '../../../src/native.js'; +import { decorateAdUnitsWithNativeParams } from '../../../src/native.js'; const ENDPOINT = 'https://hb.openwebmp.com/hb-multi'; const TEST_ENDPOINT = 'https://hb.openwebmp.com/hb-multi-test'; @@ -106,7 +106,7 @@ describe('openwebAdapter', function () { 'mediaTypes': { 'banner': { 'sizes': [ - [ 300, 250 ] + [300, 250] ] }, 'video': { @@ -333,7 +333,7 @@ describe('openwebAdapter', function () { }); it('should have us_privacy param if usPrivacy is available in the bidRequest', function () { - const bidderRequestWithUSP = Object.assign({uspConsent: '1YNN'}, bidderRequest); + const bidderRequestWithUSP = Object.assign({ uspConsent: '1YNN' }, bidderRequest); const request = spec.buildRequests(bidRequests, bidderRequestWithUSP); expect(request.data.params).to.be.an('object'); expect(request.data.params).to.have.property('us_privacy', '1YNN'); @@ -346,7 +346,7 @@ describe('openwebAdapter', function () { }); it('should not send the gdpr param if gdprApplies is false in the bidRequest', function () { - const bidderRequestWithGDPR = Object.assign({gdprConsent: {gdprApplies: false}}, bidderRequest); + const bidderRequestWithGDPR = Object.assign({ gdprConsent: { gdprApplies: false } }, bidderRequest); const request = spec.buildRequests(bidRequests, bidderRequestWithGDPR); expect(request.data.params).to.be.an('object'); expect(request.data.params).to.not.have.property('gdpr'); @@ -354,7 +354,7 @@ describe('openwebAdapter', function () { }); it('should send the gdpr param if gdprApplies is true in the bidRequest', function () { - const bidderRequestWithGDPR = Object.assign({gdprConsent: {gdprApplies: true, consentString: 'test-consent-string'}}, bidderRequest); + const bidderRequestWithGDPR = Object.assign({ gdprConsent: { gdprApplies: true, consentString: 'test-consent-string' } }, bidderRequest); const request = spec.buildRequests(bidRequests, bidderRequestWithGDPR); expect(request.data.params).to.be.an('object'); expect(request.data.params).to.have.property('gdpr', true); @@ -362,7 +362,7 @@ describe('openwebAdapter', function () { }); it('should not send the gpp param if gppConsent is false in the bidRequest', function () { - const bidderRequestWithGPP = Object.assign({gppConsent: false}, bidderRequest); + const bidderRequestWithGPP = Object.assign({ gppConsent: false }, bidderRequest); const request = spec.buildRequests(bidRequests, bidderRequestWithGPP); expect(request.data.params).to.be.an('object'); expect(request.data.params).to.not.have.property('gpp'); @@ -370,7 +370,7 @@ describe('openwebAdapter', function () { }); it('should send the gpp param if gppConsent is true in the bidRequest', function () { - const bidderRequestWithGPP = Object.assign({gppConsent: {gppString: 'test-consent-string', applicableSections: [7]}}, bidderRequest); + const bidderRequestWithGPP = Object.assign({ gppConsent: { gppString: 'test-consent-string', applicableSections: [7] } }, bidderRequest); const request = spec.buildRequests(bidRequests, bidderRequestWithGPP); expect(request.data.params).to.be.an('object'); expect(request.data.params).to.have.property('gpp', 'test-consent-string'); @@ -431,15 +431,15 @@ describe('openwebAdapter', function () { 'browsers': [ { 'brand': 'Chromium', - 'version': [ '106', '0', '5249', '119' ] + 'version': ['106', '0', '5249', '119'] }, { 'brand': 'Google Chrome', - 'version': [ '106', '0', '5249', '119' ] + 'version': ['106', '0', '5249', '119'] }, { 'brand': 'Not;A=Brand', - 'version': [ '99', '0', '0', '0' ] + 'version': ['99', '0', '0', '0'] } ], 'mobile': 0, @@ -453,20 +453,20 @@ describe('openwebAdapter', function () { 'sua': { 'platform': { 'brand': 'macOS', - 'version': [ '12', '4', '0' ] + 'version': ['12', '4', '0'] }, 'browsers': [ { 'brand': 'Chromium', - 'version': [ '106', '0', '5249', '119' ] + 'version': ['106', '0', '5249', '119'] }, { 'brand': 'Google Chrome', - 'version': [ '106', '0', '5249', '119' ] + 'version': ['106', '0', '5249', '119'] }, { 'brand': 'Not;A=Brand', - 'version': [ '99', '0', '0', '0' ] + 'version': ['99', '0', '0', '0'] } ], 'mobile': 0, diff --git a/test/spec/modules/openxBidAdapter_spec.js b/test/spec/modules/openxBidAdapter_spec.js index dfcdecdf586..c801ecd2bfc 100644 --- a/test/spec/modules/openxBidAdapter_spec.js +++ b/test/spec/modules/openxBidAdapter_spec.js @@ -1,10 +1,9 @@ -import {expect} from 'chai'; -import {spec, REQUEST_URL, SYNC_URL, DEFAULT_PH} from 'modules/openxBidAdapter.js'; -import {newBidder} from 'src/adapters/bidderFactory.js'; -import {BANNER, NATIVE, VIDEO} from 'src/mediaTypes.js'; -import {config} from 'src/config.js'; +import { expect } from 'chai'; +import { spec, REQUEST_URL, SYNC_URL, DEFAULT_PH } from 'modules/openxBidAdapter.js'; +import { newBidder } from 'src/adapters/bidderFactory.js'; +import { BANNER, NATIVE, VIDEO } from 'src/mediaTypes.js'; +import { config } from 'src/config.js'; import * as utils from 'src/utils.js'; -import * as dnt from 'libraries/dnt/index.js'; // load modules that register ORTB processors import 'src/prebid.js' import 'modules/currency.js'; @@ -13,12 +12,11 @@ import 'modules/multibid/index.js'; import 'modules/priceFloors.js'; import 'modules/consentManagementTcf.js'; import 'modules/consentManagementUsp.js'; -import 'modules/paapi.js'; -import {deepClone} from 'src/utils.js'; -import {version} from 'package.json'; -import {addFPDToBidderRequest} from '../../helpers/fpd.js'; -import {hook} from '../../../src/hook.js'; +import { deepClone } from 'src/utils.js'; +import { version } from 'package.json'; +import { addFPDToBidderRequest } from '../../helpers/fpd.js'; +import { hook } from '../../../src/hook.js'; const DEFAULT_SYNC = SYNC_URL + '?ph=' + DEFAULT_PH; const BidRequestBuilder = function BidRequestBuilder(options) { @@ -93,7 +91,7 @@ describe('OpenxRtbAdapter', function () { bidder: 'openx', params: {}, adUnitCode: 'adunit-code', - mediaTypes: {banner: {}}, + mediaTypes: { banner: {} }, sizes: [[300, 250], [300, 600]], bidId: '30b31c1838de1e', bidderRequestId: '22edbae2733bf6', @@ -102,13 +100,13 @@ describe('OpenxRtbAdapter', function () { }); it('should return false when there is no delivery domain', function () { - bannerBid.params = {'unit': '12345678'}; + 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'} + bannerBid.params = { delDomain: 'test-delivery-domain' } }); it('should return false when there is no ad unit id and size', function () { @@ -364,7 +362,7 @@ describe('OpenxRtbAdapter', function () { }; beforeEach(function () { - mockBidderRequest = {refererInfo: {}}; + mockBidderRequest = { refererInfo: {} }; bidRequestsWithMediaTypes = [{ bidder: 'openx', @@ -504,7 +502,7 @@ describe('OpenxRtbAdapter', function () { params: { unit: '12345678', delDomain: 'test-del-domain', - customParams: {'Test1': 'testval1+', 'test2': ['testval2/', 'testval3']} + customParams: { 'Test1': 'testval1+', 'test2': ['testval2/', 'testval3'] } } } ); @@ -586,7 +584,7 @@ describe('OpenxRtbAdapter', function () { describe('FPD', function() { let bidRequests; - const mockBidderRequest = {refererInfo: {}}; + const mockBidderRequest = { refererInfo: {} }; beforeEach(function () { bidRequests = [{ @@ -803,27 +801,28 @@ describe('OpenxRtbAdapter', function () { describe('with user agent client hints', function () { it('should add device.sua if available', function () { - const bidderRequestWithUserAgentClientHints = { refererInfo: {}, + const bidderRequestWithUserAgentClientHints = { + refererInfo: {}, ortb2: { device: { sua: { source: 2, platform: { brand: 'macOS', - version: [ '12', '4', '0' ] + version: ['12', '4', '0'] }, browsers: [ { brand: 'Chromium', - version: [ '106', '0', '5249', '119' ] + version: ['106', '0', '5249', '119'] }, { brand: 'Google Chrome', - version: [ '106', '0', '5249', '119' ] + version: ['106', '0', '5249', '119'] }, { brand: 'Not;A=Brand', - version: [ '99', '0', '0', '0' ] + version: ['99', '0', '0', '0'] }], mobile: 0, model: 'Pro', @@ -831,12 +830,13 @@ describe('OpenxRtbAdapter', function () { architecture: 'x86' } } - }}; + } + }; let request = spec.buildRequests(bidRequests, bidderRequestWithUserAgentClientHints); expect(request[0].data.device.sua).to.exist; expect(request[0].data.device.sua).to.deep.equal(bidderRequestWithUserAgentClientHints.ortb2.device.sua); - const bidderRequestWithoutUserAgentClientHints = {refererInfo: {}, ortb2: {}}; + const bidderRequestWithoutUserAgentClientHints = { refererInfo: {}, ortb2: {} }; request = spec.buildRequests(bidRequests, bidderRequestWithoutUserAgentClientHints); expect(request[0].data.device?.sua).to.not.exist; }); @@ -1041,7 +1041,7 @@ describe('OpenxRtbAdapter', function () { it('should send a coppa flag there is when there is coppa param settings in the bid params', async function () { const request = spec.buildRequests(bidRequestsWithMediaTypes, await addFPDToBidderRequest(mockBidderRequest)); - request.params = {coppa: true}; + request.params = { coppa: true }; expect(request[0].data.regs.coppa).to.equal(1); }); @@ -1051,32 +1051,7 @@ describe('OpenxRtbAdapter', function () { }); context('do not track (DNT)', function() { - let doNotTrackStub; - - beforeEach(function () { - doNotTrackStub = sinon.stub(dnt, 'getDNT'); - }); - afterEach(function() { - doNotTrackStub.restore(); - }); - - it('when there is a do not track, should send a dnt', async function () { - doNotTrackStub.returns(1); - - const request = spec.buildRequests(bidRequestsWithMediaTypes, await addFPDToBidderRequest(mockBidderRequest)); - expect(request[0].data.device.dnt).to.equal(1); - }); - - it('when there is not do not track, don\'t send dnt', async function () { - doNotTrackStub.returns(0); - - const request = spec.buildRequests(bidRequestsWithMediaTypes, await addFPDToBidderRequest(mockBidderRequest)); - expect(request[0].data.device.dnt).to.equal(0); - }); - - it('when there is no defined do not track, don\'t send dnt', async function () { - doNotTrackStub.returns(null); - + it('always sends dnt as 0', async function () { const request = spec.buildRequests(bidRequestsWithMediaTypes, await addFPDToBidderRequest(mockBidderRequest)); expect(request[0].data.device.dnt).to.equal(0); }); @@ -1135,17 +1110,19 @@ describe('OpenxRtbAdapter', function () { bidId: 'test-bid-id-1', bidderRequestId: 'test-bid-request-1', auctionId: 'test-auction-1', - ortb2: {source: { - schain: schainConfig, - ext: {schain: schainConfig} - }} + ortb2: { + source: { + schain: schainConfig, + ext: { schain: schainConfig } + } + } }]; // Add schain to mockBidderRequest as well mockBidderRequest.ortb2 = { source: { schain: schainConfig, - ext: {schain: schainConfig} + ext: { schain: schainConfig } } }; }); @@ -1216,7 +1193,7 @@ describe('OpenxRtbAdapter', function () { }]; // enrich bid request with userId key/value - mockBidderRequest.ortb2 = {user: {ext: {eids}}} + mockBidderRequest.ortb2 = { user: { ext: { eids } } } const request = spec.buildRequests(bidRequests, mockBidderRequest); expect(request[0].data.user.ext.eids).to.eql(eids); }); @@ -1226,18 +1203,6 @@ describe('OpenxRtbAdapter', function () { expect(request[0].data).to.not.have.any.keys('user'); }); }); - - context('FLEDGE', function() { - it('when FLEDGE is enabled, should send whatever is set in ortb2imp.ext.ae in all bid requests', function () { - const request = spec.buildRequests(bidRequestsWithMediaTypes, { - ...mockBidderRequest, - paapi: { - enabled: true - } - }); - expect(request[0].data.imp[0].ext.ae).to.equal(2); - }); - }); }); context('banner', function () { @@ -1321,10 +1286,10 @@ describe('OpenxRtbAdapter', function () { auctionId: 'test-auction-id' }]; - bidRequest = spec.buildRequests(bidRequestConfigs, {refererInfo: {}})[0]; + bidRequest = spec.buildRequests(bidRequestConfigs, { refererInfo: {} })[0]; - bidResponse = {nbr: 0}; // Unknown error - response = spec.interpretResponse({body: bidResponse}, bidRequest); + bidResponse = { nbr: 0 }; // Unknown error + response = spec.interpretResponse({ body: bidResponse }, bidRequest); }); it('should not return any bids', function () { @@ -1352,10 +1317,10 @@ describe('OpenxRtbAdapter', function () { auctionId: 'test-auction-id' }]; - bidRequest = spec.buildRequests(bidRequestConfigs, {refererInfo: {}})[0]; + bidRequest = spec.buildRequests(bidRequestConfigs, { refererInfo: {} })[0]; - bidResponse = {ext: {}, id: 'test-bid-id'}; - response = spec.interpretResponse({body: bidResponse}, bidRequest); + bidResponse = { ext: {}, id: 'test-bid-id' }; + response = spec.interpretResponse({ body: bidResponse }, bidRequest); }); it('should not return any bids', function () { @@ -1383,10 +1348,10 @@ describe('OpenxRtbAdapter', function () { auctionId: 'test-auction-id' }]; - bidRequest = spec.buildRequests(bidRequestConfigs, {refererInfo: {}})[0]; + bidRequest = spec.buildRequests(bidRequestConfigs, { refererInfo: {} })[0]; bidResponse = ''; // Unknown error - response = spec.interpretResponse({body: bidResponse}, bidRequest); + response = spec.interpretResponse({ body: bidResponse }, bidRequest); }); it('should not return any bids', function () { @@ -1436,10 +1401,10 @@ describe('OpenxRtbAdapter', function () { context('when there is a response, the common response properties', function () { beforeEach(function () { bidRequestConfigs = deepClone(SAMPLE_BID_REQUESTS); - bidRequest = spec.buildRequests(bidRequestConfigs, {refererInfo: {}})[0]; + bidRequest = spec.buildRequests(bidRequestConfigs, { refererInfo: {} })[0]; bidResponse = deepClone(SAMPLE_BID_RESPONSE); - bid = spec.interpretResponse({body: bidResponse}, bidRequest).bids[0]; + bid = spec.interpretResponse({ body: bidResponse }, bidRequest).bids[0]; }); it('should return a price', function () { @@ -1515,7 +1480,7 @@ describe('OpenxRtbAdapter', function () { auctionId: 'test-auction-id' }]; - bidRequest = spec.buildRequests(bidRequestConfigs, {refererInfo: {}})[0]; + bidRequest = spec.buildRequests(bidRequestConfigs, { refererInfo: {} })[0]; bidResponse = { seatbid: [{ @@ -1533,7 +1498,7 @@ describe('OpenxRtbAdapter', function () { cur: 'AUS' }; - bid = spec.interpretResponse({body: bidResponse}, bidRequest).bids[0]; + bid = spec.interpretResponse({ body: bidResponse }, bidRequest).bids[0]; }); it('should return the proper mediaType', function () { @@ -1561,7 +1526,7 @@ describe('OpenxRtbAdapter', function () { auctionId: 'test-auction-id' }]; - bidRequest = spec.buildRequests(bidRequestConfigs, {refererInfo: {}})[0]; + bidRequest = spec.buildRequests(bidRequestConfigs, { refererInfo: {} })[0]; bidResponse = { seatbid: [{ @@ -1580,14 +1545,14 @@ describe('OpenxRtbAdapter', function () { }); it('should return the proper mediaType', function () { - bid = spec.interpretResponse({body: bidResponse}, bidRequest).bids[0]; + bid = spec.interpretResponse({ body: bidResponse }, bidRequest).bids[0]; expect(bid.mediaType).to.equal(Object.keys(bidRequestConfigs[0].mediaTypes)[0]); }); it('should return the proper vastUrl', function () { const winUrl = 'https//my.win.url'; bidResponse.seatbid[0].bid[0].nurl = winUrl - bid = spec.interpretResponse({body: bidResponse}, bidRequest).bids[0]; + bid = spec.interpretResponse({ body: bidResponse }, bidRequest).bids[0]; expect(bid.vastUrl).to.equal(winUrl); }); @@ -1615,7 +1580,7 @@ describe('OpenxRtbAdapter', function () { auctionId: 'test-auction-id' }]; - bidRequest = spec.buildRequests(bidRequestConfigs, {refererInfo: {}})[0]; + bidRequest = spec.buildRequests(bidRequestConfigs, { refererInfo: {} })[0]; bidResponse = { seatbid: [{ @@ -1631,7 +1596,7 @@ describe('OpenxRtbAdapter', function () { }); it('should return video mediaType', function () { - bid = spec.interpretResponse({body: bidResponse}, bidRequest).bids[0]; + bid = spec.interpretResponse({ body: bidResponse }, bidRequest).bids[0]; expect(bid.mediaType).to.equal(VIDEO); }); }); @@ -1671,7 +1636,7 @@ describe('OpenxRtbAdapter', function () { auctionId: 'test-auction-id-2' }]; - bidRequest = spec.buildRequests(bidRequestConfigs, {refererInfo: {}})[0]; + bidRequest = spec.buildRequests(bidRequestConfigs, { refererInfo: {} })[0]; bidResponse = { seatbid: [{ @@ -1687,7 +1652,7 @@ describe('OpenxRtbAdapter', function () { }); it('should return banner mediaType', function () { - bid = spec.interpretResponse({body: bidResponse}, bidRequest).bids[0]; + bid = spec.interpretResponse({ body: bidResponse }, bidRequest).bids[0]; expect(bid.mediaType).to.equal(BANNER); }); }); @@ -1724,7 +1689,7 @@ describe('OpenxRtbAdapter', function () { auctionId: 'test-auction-id' }]; - bidRequest = spec.buildRequests(bidRequestConfigs, {refererInfo: {}})[0]; + bidRequest = spec.buildRequests(bidRequestConfigs, { refererInfo: {} })[0]; bidResponse = { seatbid: [{ @@ -1740,21 +1705,21 @@ describe('OpenxRtbAdapter', function () { }); it('should return the proper mediaType', function () { - bid = spec.interpretResponse({body: bidResponse}, bidRequest).bids[0]; + bid = spec.interpretResponse({ body: bidResponse }, bidRequest).bids[0]; expect(bid.mediaType).to.equal(Object.keys(bidRequestConfigs[0].mediaTypes)[0]); }); it('should return parsed adm JSON in native.ortb response field', function () { - bid = spec.interpretResponse({body: bidResponse}, bidRequest).bids[0]; + bid = spec.interpretResponse({ body: bidResponse }, bidRequest).bids[0]; expect(bid.native.ortb).to.deep.equal({ ver: '1.2', assets: [{ id: 1, required: 1, - title: {text: 'OpenX (Title)'} + title: { text: 'OpenX (Title)' } }], - link: {url: 'https://www.openx.com/'}, + link: { url: 'https://www.openx.com/' }, eventtrackers: [{ event: 1, method: 1, @@ -1797,7 +1762,7 @@ describe('OpenxRtbAdapter', function () { auctionId: 'test-auction-id' }]; - bidRequest = spec.buildRequests(bidRequestConfigs, {refererInfo: {}})[0]; + bidRequest = spec.buildRequests(bidRequestConfigs, { refererInfo: {} })[0]; bidResponse = { seatbid: [{ @@ -1813,7 +1778,7 @@ describe('OpenxRtbAdapter', function () { }); it('should return banner mediaType', function () { - bid = spec.interpretResponse({body: bidResponse}, bidRequest).bids[0]; + bid = spec.interpretResponse({ body: bidResponse }, bidRequest).bids[0]; expect(bid.mediaType).to.equal(BANNER); }); }); @@ -1864,7 +1829,7 @@ describe('OpenxRtbAdapter', function () { auctionId: 'test-auction-id-2' }]; - bidRequest = spec.buildRequests(bidRequestConfigs, {refererInfo: {}})[0]; + bidRequest = spec.buildRequests(bidRequestConfigs, { refererInfo: {} })[0]; bidResponse = { seatbid: [{ @@ -1880,150 +1845,52 @@ describe('OpenxRtbAdapter', function () { }); it('should return native mediaType', function () { - bid = spec.interpretResponse({body: bidResponse}, bidRequest).bids[0]; + bid = spec.interpretResponse({ body: bidResponse }, bidRequest).bids[0]; expect(bid.mediaType).to.equal(NATIVE); }); }); } - - context('when the response contains FLEDGE interest groups config', function() { - let response; - - beforeEach(function () { - sinon.stub(config, 'getConfig') - .withArgs('fledgeEnabled') - .returns(true); - - bidRequestConfigs = [{ - bidder: 'openx', - params: { - unit: '12345678', - delDomain: 'test-del-domain' - }, - adUnitCode: 'adunit-code', - mediaTypes: { - banner: { - sizes: [[300, 250], [300, 600]], - }, - }, - bidId: 'test-bid-id', - bidderRequestId: 'test-bidder-request-id', - auctionId: 'test-auction-id' - }]; - - bidRequest = spec.buildRequests(bidRequestConfigs, {refererInfo: {}})[0]; - - bidResponse = { - seatbid: [{ - bid: [{ - impid: 'test-bid-id', - price: 2, - w: 300, - h: 250, - crid: 'test-creative-id', - dealid: 'test-deal-id', - adm: 'test-ad-markup' - }] - }], - cur: 'AUS', - ext: { - fledge_auction_configs: { - 'test-bid-id': { - seller: 'codinginadtech.com', - interestGroupBuyers: ['somedomain.com'], - sellerTimeout: 0, - perBuyerSignals: { - 'somedomain.com': { - base_bid_micros: 0.1, - disallowed_advertiser_ids: [ - '1234', - '2345' - ], - multiplier: 1.3, - use_bid_multiplier: true, - win_reporting_id: '1234567asdf' - } - } - } - } - } - }; - - response = spec.interpretResponse({body: bidResponse}, bidRequest); - }); - - afterEach(function () { - config.getConfig.restore(); - }); - - it('should return FLEDGE auction_configs alongside bids', function () { - expect(response).to.have.property('bids'); - expect(response).to.have.property('paapi'); - expect(response.paapi.length).to.equal(1); - expect(response.paapi[0].bidId).to.equal('test-bid-id'); - }); - - it('should inject ortb2Imp in auctionSignals', function () { - const auctionConfig = response.paapi[0].config; - expect(auctionConfig).to.deep.include({ - auctionSignals: { - ortb2Imp: { - id: 'test-bid-id', - tagid: '12345678', - banner: { - topframe: 0, - format: bidRequestConfigs[0].mediaTypes.banner.sizes.map(([w, h]) => ({w, h})) - }, - ext: { - divid: 'adunit-code', - }, - secure: 1 - } - } - }); - }) - }); }); describe('user sync', function () { it('should register the default image pixel if no pixels available', function () { const syncs = spec.getUserSyncs( - {pixelEnabled: true}, + { pixelEnabled: true }, [] ); - expect(syncs).to.deep.equal([{type: 'image', url: DEFAULT_SYNC}]); + expect(syncs).to.deep.equal([{ type: 'image', url: DEFAULT_SYNC }]); }); it('should register custom syncUrl when exists', function () { const syncs = spec.getUserSyncs( - {pixelEnabled: true}, - [{body: {ext: {delDomain: 'www.url.com'}}}] + { pixelEnabled: true }, + [{ body: { ext: { delDomain: 'www.url.com' } } }] ); - expect(syncs).to.deep.equal([{type: 'image', url: 'https://www.url.com/w/1.0/pd'}]); + expect(syncs).to.deep.equal([{ type: 'image', url: 'https://www.url.com/w/1.0/pd' }]); }); it('should register custom syncUrl when exists', function () { const syncs = spec.getUserSyncs( - {pixelEnabled: true}, - [{body: {ext: {platform: 'abc'}}}] + { pixelEnabled: true }, + [{ body: { ext: { platform: 'abc' } } }] ); - expect(syncs).to.deep.equal([{type: 'image', url: SYNC_URL + '?ph=abc'}]); + expect(syncs).to.deep.equal([{ type: 'image', url: SYNC_URL + '?ph=abc' }]); }); it('when iframe sync is allowed, it should register an iframe sync', function () { const syncs = spec.getUserSyncs( - {iframeEnabled: true}, + { iframeEnabled: true }, [] ); - expect(syncs).to.deep.equal([{type: 'iframe', url: DEFAULT_SYNC}]); + expect(syncs).to.deep.equal([{ type: 'iframe', url: DEFAULT_SYNC }]); }); it('should prioritize iframe over image for user sync', function () { const syncs = spec.getUserSyncs( - {iframeEnabled: true, pixelEnabled: true}, + { iframeEnabled: true, pixelEnabled: true }, [] ); - expect(syncs).to.deep.equal([{type: 'iframe', url: DEFAULT_SYNC}]); + expect(syncs).to.deep.equal([{ type: 'iframe', url: DEFAULT_SYNC }]); }); describe('when gdpr applies', function () { @@ -2041,8 +1908,8 @@ describe('OpenxRtbAdapter', function () { }); it('when there is a response, it should have the gdpr query params', () => { - const [{url}] = spec.getUserSyncs( - {iframeEnabled: true, pixelEnabled: true}, + const [{ url }] = spec.getUserSyncs( + { iframeEnabled: true, pixelEnabled: true }, [], gdprConsent ); @@ -2052,8 +1919,8 @@ describe('OpenxRtbAdapter', function () { }); it('should not send signals if no consent object is available', function () { - const [{url}] = spec.getUserSyncs( - {iframeEnabled: true, pixelEnabled: true}, + const [{ url }] = spec.getUserSyncs( + { iframeEnabled: true, pixelEnabled: true }, [], ); expect(url).to.not.have.string('gdpr_consent='); @@ -2067,8 +1934,8 @@ describe('OpenxRtbAdapter', function () { gppString: 'gpp-pixel-consent', applicableSections: [6, 7] } - const [{url}] = spec.getUserSyncs( - {iframeEnabled: true, pixelEnabled: true}, + const [{ url }] = spec.getUserSyncs( + { iframeEnabled: true, pixelEnabled: true }, [], undefined, undefined, @@ -2088,8 +1955,8 @@ describe('OpenxRtbAdapter', function () { gppString: 'gpp-pixel-consent', applicableSections: [6, 7] } - const [{url}] = spec.getUserSyncs( - {iframeEnabled: true, pixelEnabled: true}, + const [{ url }] = spec.getUserSyncs( + { iframeEnabled: true, pixelEnabled: true }, [], gdprConsent, undefined, @@ -2106,8 +1973,8 @@ describe('OpenxRtbAdapter', function () { const gppConsent = { applicableSections: [6, 7] } - const [{url}] = spec.getUserSyncs( - {iframeEnabled: true, pixelEnabled: true}, + const [{ url }] = spec.getUserSyncs( + { iframeEnabled: true, pixelEnabled: true }, [], undefined, undefined, @@ -2122,8 +1989,8 @@ describe('OpenxRtbAdapter', function () { const gppConsent = { gppString: 'gpp-pixel-consent', } - const [{url}] = spec.getUserSyncs( - {iframeEnabled: true, pixelEnabled: true}, + const [{ url }] = spec.getUserSyncs( + { iframeEnabled: true, pixelEnabled: true }, [], undefined, undefined, @@ -2139,8 +2006,8 @@ describe('OpenxRtbAdapter', function () { gppString: 'gpp-pixel-consent', applicableSections: [] } - const [{url}] = spec.getUserSyncs( - {iframeEnabled: true, pixelEnabled: true}, + const [{ url }] = spec.getUserSyncs( + { iframeEnabled: true, pixelEnabled: true }, [], undefined, undefined, @@ -2152,8 +2019,8 @@ describe('OpenxRtbAdapter', function () { }); it('should not send GPP query params when GPP consent object not available', function () { - const [{url}] = spec.getUserSyncs( - {iframeEnabled: true, pixelEnabled: true}, + const [{ url }] = spec.getUserSyncs( + { iframeEnabled: true, pixelEnabled: true }, [], undefined, undefined, undefined ); expect(url).to.not.have.string('gpp='); @@ -2170,8 +2037,8 @@ describe('OpenxRtbAdapter', function () { uspPixelUrl = `${DEFAULT_SYNC}&us_privacy=${privacyString}` }); it('should send the us privacy string, ', () => { - const [{url}] = spec.getUserSyncs( - {iframeEnabled: true, pixelEnabled: true}, + const [{ url }] = spec.getUserSyncs( + { iframeEnabled: true, pixelEnabled: true }, [], undefined, usPrivacyConsent @@ -2180,8 +2047,8 @@ describe('OpenxRtbAdapter', function () { }); it('should not send signals if no consent string is available', function () { - const [{url}] = spec.getUserSyncs( - {iframeEnabled: true, pixelEnabled: true}, + const [{ url }] = spec.getUserSyncs( + { iframeEnabled: true, pixelEnabled: true }, [], ); expect(url).to.not.have.string('us_privacy='); diff --git a/test/spec/modules/operaadsBidAdapter_spec.js b/test/spec/modules/operaadsBidAdapter_spec.js index 0ff16d72293..4c875eedb0a 100644 --- a/test/spec/modules/operaadsBidAdapter_spec.js +++ b/test/spec/modules/operaadsBidAdapter_spec.js @@ -1,7 +1,7 @@ -import {expect} from 'chai'; -import {spec} from 'modules/operaadsBidAdapter.js'; -import {newBidder} from 'src/adapters/bidderFactory.js'; -import {BANNER, NATIVE, VIDEO} from 'src/mediaTypes.js'; +import { expect } from 'chai'; +import { spec } from 'modules/operaadsBidAdapter.js'; +import { newBidder } from 'src/adapters/bidderFactory.js'; +import { BANNER, NATIVE, VIDEO } from 'src/mediaTypes.js'; describe('Opera Ads Bid Adapter', function () { describe('Test isBidRequestValid', function () { @@ -274,7 +274,7 @@ describe('Opera Ads Bid Adapter', function () { bidder: 'operaads', bidderRequestId: '15246a574e859f', mediaTypes: { - banner: {sizes: [[300, 250]]} + banner: { sizes: [[300, 250]] } }, params: { placementId: 's12345678', @@ -814,7 +814,7 @@ describe('Opera Ads Bid Adapter', function () { describe('Test onBidWon', function () { it('onBidWon should not throw', function () { - expect(spec.onBidWon({nurl: '#', originalCpm: '1.04', currency: 'USD'})).to.not.throw; + expect(spec.onBidWon({ nurl: '#', originalCpm: '1.04', currency: 'USD' })).to.not.throw; }); }); diff --git a/test/spec/modules/operaadsIdSystem_spec.js b/test/spec/modules/operaadsIdSystem_spec.js index b6acb942331..466a092ff52 100644 --- a/test/spec/modules/operaadsIdSystem_spec.js +++ b/test/spec/modules/operaadsIdSystem_spec.js @@ -1,8 +1,8 @@ import { operaIdSubmodule } from 'modules/operaadsIdSystem' import * as ajaxLib from 'src/ajax.js' -import {attachIdSystem} from '../../../modules/userId/index.js'; -import {createEidsArray} from '../../../modules/userId/eids.js'; -import {expect} from 'chai/index.mjs'; +import { attachIdSystem } from '../../../modules/userId/index.js'; +import { createEidsArray } from '../../../modules/userId/eids.js'; +import { expect } from 'chai/index.mjs'; const TEST_ID = 'opera-test-id'; const operaIdRemoteResponse = { uid: TEST_ID }; diff --git a/test/spec/modules/opscoBidAdapter_spec.js b/test/spec/modules/opscoBidAdapter_spec.js index c0095edb0f1..49ac49c1826 100644 --- a/test/spec/modules/opscoBidAdapter_spec.js +++ b/test/spec/modules/opscoBidAdapter_spec.js @@ -1,7 +1,7 @@ -import {expect} from 'chai'; -import {spec} from 'modules/opscoBidAdapter'; -import {newBidder} from 'src/adapters/bidderFactory.js'; -import {addFPDToBidderRequest} from "../../helpers/fpd.js"; +import { expect } from 'chai'; +import { spec } from 'modules/opscoBidAdapter'; +import { newBidder } from 'src/adapters/bidderFactory.js'; +import { addFPDToBidderRequest } from "../../helpers/fpd.js"; import 'modules/priceFloors.js'; describe('opscoBidAdapter', function () { @@ -32,36 +32,36 @@ describe('opscoBidAdapter', function () { }); it('should return false when placementId is missing', function () { - const invalidBid = {...validBid}; + const invalidBid = { ...validBid }; delete invalidBid.params.placementId; expect(spec.isBidRequestValid(invalidBid)).to.be.false; }); it('should return false when publisherId is missing', function () { - const invalidBid = {...validBid}; + const invalidBid = { ...validBid }; delete invalidBid.params.publisherId; expect(spec.isBidRequestValid(invalidBid)).to.be.false; }); it('should return false when mediaTypes.banner.sizes is missing', function () { - const invalidBid = {...validBid}; + const invalidBid = { ...validBid }; delete invalidBid.mediaTypes.banner.sizes; expect(spec.isBidRequestValid(invalidBid)).to.be.false; }); it('should return false when mediaTypes.banner is missing', function () { - const invalidBid = {...validBid}; + const invalidBid = { ...validBid }; delete invalidBid.mediaTypes.banner; expect(spec.isBidRequestValid(invalidBid)).to.be.false; }); it('should return false when bid params are missing', function () { - const invalidBid = {bidder: 'opsco'}; + const invalidBid = { bidder: 'opsco' }; expect(spec.isBidRequestValid(invalidBid)).to.be.false; }); it('should return false when bid params are empty', function () { - const invalidBid = {bidder: 'opsco', params: {}}; + const invalidBid = { bidder: 'opsco', params: {} }; expect(spec.isBidRequestValid(invalidBid)).to.be.false; }); }); @@ -127,25 +127,25 @@ describe('opscoBidAdapter', function () { it('should send CCPA in the payload if present', async function () { const request = spec.buildRequests([validBid], await addFPDToBidderRequest({ ...bidderRequest, - ...{uspConsent: '1YYY'} + ...{ uspConsent: '1YYY' } })); expect(request.data.regs.ext.us_privacy).to.equal('1YYY'); }); it('should send eids in the payload if present', async function () { - const eids = [{source: 'test', uids: [{id: '123', ext: {}}]}]; + const eids = [{ source: 'test', uids: [{ id: '123', ext: {} }] }]; const request = spec.buildRequests([validBid], await addFPDToBidderRequest({ ...bidderRequest, - ortb2: {user: {ext: {eids: eids}}} + ortb2: { user: { ext: { eids: eids } } } })); expect(request.data.user.ext.eids).to.deep.equal(eids); }); it('should send schain in the payload if present', function () { - const schain = {'ver': '1.0', 'complete': 1, 'nodes': [{'asi': 'exchange1.com', 'sid': '1234', 'hp': 1}]}; + const schain = { 'ver': '1.0', 'complete': 1, 'nodes': [{ 'asi': 'exchange1.com', 'sid': '1234', 'hp': 1 }] }; const request = spec.buildRequests([validBid], { ...bidderRequest, - ortb2: {source: {ext: {schain: schain}}} + ortb2: { source: { ext: { schain: schain } } } }); expect(request.data.source.ext.schain).to.deep.equal(schain); }); @@ -241,10 +241,10 @@ describe('opscoBidAdapter', function () { ext: { usersync: { sovrn: { - syncs: [{type: 'iframe', url: 'https://sovrn.com/iframe_sync'}] + syncs: [{ type: 'iframe', url: 'https://sovrn.com/iframe_sync' }] }, appnexus: { - syncs: [{type: 'image', url: 'https://appnexus.com/image_sync'}] + syncs: [{ type: 'image', url: 'https://appnexus.com/image_sync' }] } } } @@ -257,26 +257,26 @@ describe('opscoBidAdapter', function () { }); it('should return empty array if neither iframe nor pixel is enabled', function () { - const opts = spec.getUserSyncs({iframeEnabled: false, pixelEnabled: false}); + const opts = spec.getUserSyncs({ iframeEnabled: false, pixelEnabled: false }); expect(opts).to.be.an('array').that.is.empty; }); it('should return syncs only for iframe sync type', function () { - const opts = spec.getUserSyncs({iframeEnabled: true, pixelEnabled: false}, [RESPONSE]); + const 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('should return syncs only for pixel sync types', function () { - const opts = spec.getUserSyncs({iframeEnabled: false, pixelEnabled: true}, [RESPONSE]); + const 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('should return syncs when both iframe and pixel are enabled', function () { - const opts = spec.getUserSyncs({iframeEnabled: true, pixelEnabled: true}, [RESPONSE]); + const opts = spec.getUserSyncs({ iframeEnabled: true, pixelEnabled: true }, [RESPONSE]); expect(opts.length).to.equal(2); }); }); diff --git a/test/spec/modules/optableBidAdapter_spec.js b/test/spec/modules/optableBidAdapter_spec.js deleted file mode 100644 index b7cf2e3b44d..00000000000 --- a/test/spec/modules/optableBidAdapter_spec.js +++ /dev/null @@ -1,116 +0,0 @@ -import { expect } from 'chai'; -import { spec } from 'modules/optableBidAdapter'; -import { newBidder } from 'src/adapters/bidderFactory.js'; - -describe('optableBidAdapter', function() { - const adapter = newBidder(spec); - - describe('isBidRequestValid', function() { - const validBid = { - bidder: 'optable', - params: { site: '123' }, - }; - - it('should return true when required params are present', function() { - expect(spec.isBidRequestValid(validBid)).to.be.true; - }); - - it('should return false when site is missing', function() { - const invalidBid = { ...validBid }; - delete invalidBid.params.site; - expect(spec.isBidRequestValid(invalidBid)).to.be.false; - }); - }); - - describe('buildRequests', function() { - const validBid = { - bidder: 'optable', - params: { - site: '123', - }, - }; - - const bidderRequest = { - bidderRequestId: 'bid123', - refererInfo: { - domain: 'example.com', - page: 'https://example.com/page', - ref: 'https://referrer.com' - }, - }; - - it('should include site as tagid in imp', function() { - const request = spec.buildRequests([validBid], bidderRequest); - expect(request.url).to.equal('https://ads.optable.co/ca/ortb2/v1/ssp/bid'); - expect(request.method).to.equal('POST'); - expect(request.data.imp[0].tagid).to.equal('123') - }); - }); - - describe('buildPAAPIConfigs', () => { - function makeRequest({bidId, site = 'mockSite', ae = 1}) { - return { - bidId, - params: { - site - }, - ortb2Imp: { - ext: {ae} - } - } - } - it('should generate auction configs for ae requests', () => { - const configs = spec.buildPAAPIConfigs([ - makeRequest({bidId: 'bid1', ae: 1}), - makeRequest({bidId: 'bid2', ae: 0}), - makeRequest({bidId: 'bid3', ae: 1}), - ]); - expect(configs.map(cfg => cfg.bidId)).to.eql(['bid1', 'bid3']); - configs.forEach(cfg => sinon.assert.match(cfg.config, { - seller: 'https://ads.optable.co', - decisionLogicURL: `https://ads.optable.co/ca/paapi/v1/ssp/decision-logic.js?origin=mockSite`, - interestGroupBuyers: ['https://ads.optable.co'] - })) - }) - }) - - describe('interpretResponse', function() { - const validBid = { - bidder: 'optable', - params: { - site: '123', - }, - }; - - const bidderRequest = { - bidderRequestId: 'bid123', - refererInfo: { - domain: 'example.com', - page: 'https://example.com/page', - ref: 'https://referrer.com' - }, - }; - - const response = { - body: { - ext: { - optable: { - fledge: { - auctionconfigs: [ - { impid: 'bid123', seller: 'https://ads.optable.co' }, - ] - } - } - } - } - }; - - it('maps paapi from ext.optable.fledge.auctionconfigs', function() { - const request = spec.buildRequests([validBid], bidderRequest); - const result = spec.interpretResponse(response, request); - expect(result.paapi).to.deep.equal([ - { bidId: 'bid123', config: { seller: 'https://ads.optable.co' } } - ]); - }); - }); -}); diff --git a/test/spec/modules/optableRtdProvider_spec.js b/test/spec/modules/optableRtdProvider_spec.js index 271d31d0185..b408c0b991f 100644 --- a/test/spec/modules/optableRtdProvider_spec.js +++ b/test/spec/modules/optableRtdProvider_spec.js @@ -25,29 +25,29 @@ describe('Optable RTD Submodule', function () { }); it('trims bundleUrl if it contains extra spaces', function () { - const config = {params: {bundleUrl: ' https://cdn.optable.co/bundle.js '}}; + const config = { params: { bundleUrl: ' https://cdn.optable.co/bundle.js ' } }; expect(parseConfig(config).bundleUrl).to.equal('https://cdn.optable.co/bundle.js'); }); it('returns null bundleUrl for invalid bundleUrl format', function () { - expect(parseConfig({params: {bundleUrl: 'invalidURL'}}).bundleUrl).to.be.null; - expect(parseConfig({params: {bundleUrl: 'www.invalid.com'}}).bundleUrl).to.be.null; + expect(parseConfig({ params: { bundleUrl: 'invalidURL' } }).bundleUrl).to.be.null; + expect(parseConfig({ params: { bundleUrl: 'www.invalid.com' } }).bundleUrl).to.be.null; }); it('returns null bundleUrl for non-HTTPS bundleUrl', function () { - expect(parseConfig({params: {bundleUrl: 'http://cdn.optable.co/bundle.js'}}).bundleUrl).to.be.null; - expect(parseConfig({params: {bundleUrl: '//cdn.optable.co/bundle.js'}}).bundleUrl).to.be.null; - expect(parseConfig({params: {bundleUrl: '/bundle.js'}}).bundleUrl).to.be.null; + expect(parseConfig({ params: { bundleUrl: 'http://cdn.optable.co/bundle.js' } }).bundleUrl).to.be.null; + expect(parseConfig({ params: { bundleUrl: '//cdn.optable.co/bundle.js' } }).bundleUrl).to.be.null; + expect(parseConfig({ params: { bundleUrl: '/bundle.js' } }).bundleUrl).to.be.null; }); it('defaults adserverTargeting to true if missing', function () { expect(parseConfig( - {params: {bundleUrl: 'https://cdn.optable.co/bundle.js'}} + { params: { bundleUrl: 'https://cdn.optable.co/bundle.js' } } ).adserverTargeting).to.be.true; }); it('returns null handleRtd if handleRtd is not a function', function () { - expect(parseConfig({params: {handleRtd: 'notAFunction'}}).handleRtd).to.be.null; + expect(parseConfig({ params: { handleRtd: 'notAFunction' } }).handleRtd).to.be.null; }); }); @@ -56,7 +56,7 @@ describe('Optable RTD Submodule', function () { beforeEach(() => { sandbox = sinon.createSandbox(); - reqBidsConfigObj = {ortb2Fragments: {global: {}}}; + reqBidsConfigObj = { ortb2Fragments: { global: {} } }; mergeFn = sinon.spy(); window.optable = { instance: { @@ -71,7 +71,7 @@ describe('Optable RTD Submodule', function () { }); it('merges valid targeting data into the global ORTB2 object', async function () { - const targetingData = {ortb2: {user: {ext: {optable: 'testData'}}}}; + const targetingData = { ortb2: { user: { ext: { optable: 'testData' } } } }; window.optable.instance.targetingFromCache.returns(targetingData); window.optable.instance.targeting.resolves(targetingData); @@ -95,7 +95,7 @@ describe('Optable RTD Submodule', function () { }); it('uses targeting data from cache if available', async function () { - const targetingData = {ortb2: {user: {ext: {optable: 'testData'}}}}; + const targetingData = { ortb2: { user: { ext: { optable: 'testData' } } } }; window.optable.instance.targetingFromCache.returns(targetingData); await defaultHandleRtd(reqBidsConfigObj, {}, mergeFn); @@ -103,7 +103,7 @@ describe('Optable RTD Submodule', function () { }); it('calls targeting function if no data is found in cache', async function () { - const targetingData = {ortb2: {user: {ext: {optable: 'testData'}}}}; + const targetingData = { ortb2: { user: { ext: { optable: 'testData' } } } }; window.optable.instance.targetingFromCache.returns(null); // Dispatch event with targeting data after a short delay @@ -125,7 +125,7 @@ describe('Optable RTD Submodule', function () { beforeEach(() => { sandbox = sinon.createSandbox(); mergeFn = sinon.spy(); - reqBidsConfigObj = {ortb2Fragments: {global: {}}}; + reqBidsConfigObj = { ortb2Fragments: { global: {} } }; }); afterEach(() => { @@ -150,11 +150,11 @@ describe('Optable RTD Submodule', function () { beforeEach(() => { sandbox = sinon.createSandbox(); - reqBidsConfigObj = {ortb2Fragments: {global: {}}}; + reqBidsConfigObj = { ortb2Fragments: { global: {} } }; callback = sinon.spy(); - moduleConfig = {params: {bundleUrl: 'https://cdn.optable.co/bundle.js'}}; + moduleConfig = { params: { bundleUrl: 'https://cdn.optable.co/bundle.js' } }; - sandbox.stub(window, 'optable').value({cmd: []}); + sandbox.stub(window, 'optable').value({ cmd: [] }); sandbox.stub(window.document, 'createElement'); sandbox.stub(window.document, 'head'); }); @@ -191,7 +191,7 @@ describe('Optable RTD Submodule', function () { // Dispatch the event after a short delay setTimeout(() => { const event = new CustomEvent('optable-targeting:change', { - detail: {ortb2: {user: {ext: {optable: 'testData'}}}} + detail: { ortb2: { user: { ext: { optable: 'testData' } } } } }); window.dispatchEvent(event); }, 10); @@ -237,7 +237,7 @@ describe('Optable RTD Submodule', function () { // Dispatch event after a short delay setTimeout(() => { const event = new CustomEvent('optable-targeting:change', { - detail: {ortb2: {user: {ext: {optable: 'testData'}}}} + detail: { ortb2: { user: { ext: { optable: 'testData' } } } } }); window.dispatchEvent(event); }, 10); @@ -269,7 +269,7 @@ describe('Optable RTD Submodule', function () { // Dispatch event after a short delay setTimeout(() => { const event = new CustomEvent('optable-targeting:change', { - detail: {ortb2: {user: {ext: {optable: 'testData'}}}} + detail: { ortb2: { user: { ext: { optable: 'testData' } } } } }); window.dispatchEvent(event); }, 10); @@ -289,8 +289,8 @@ describe('Optable RTD Submodule', function () { beforeEach(() => { sandbox = sinon.createSandbox(); - moduleConfig = {params: {adserverTargeting: true}}; - window.optable = {instance: {targetingKeyValuesFromCache: sandbox.stub().returns({key1: 'value1'})}}; + moduleConfig = { params: { adserverTargeting: true } }; + window.optable = { instance: { targetingKeyValuesFromCache: sandbox.stub().returns({ key1: 'value1' }) } }; }); afterEach(() => { @@ -299,7 +299,7 @@ describe('Optable RTD Submodule', function () { it('returns correct targeting data when Optable data is available', function () { const result = getTargetingData(['adUnit1'], moduleConfig, {}, {}); - expect(result).to.deep.equal({adUnit1: {key1: 'value1'}}); + expect(result).to.deep.equal({ adUnit1: { key1: 'value1' } }); }); it('returns empty object when no Optable data is found', function () { @@ -313,10 +313,10 @@ describe('Optable RTD Submodule', function () { }); it('returns empty object when provided keys contain no data', function () { - window.optable.instance.targetingKeyValuesFromCache.returns({key1: []}); + window.optable.instance.targetingKeyValuesFromCache.returns({ key1: [] }); expect(getTargetingData(['adUnit1'], moduleConfig, {}, {})).to.deep.equal({}); - window.optable.instance.targetingKeyValuesFromCache.returns({key1: [], key2: [], key3: []}); + window.optable.instance.targetingKeyValuesFromCache.returns({ key1: [], key2: [], key3: [] }); expect(getTargetingData(['adUnit1'], moduleConfig, {}, {})).to.deep.equal({}); }); }); diff --git a/test/spec/modules/optidigitalBidAdapter_spec.js b/test/spec/modules/optidigitalBidAdapter_spec.js index 3b4ef61e961..baf561cba16 100755 --- a/test/spec/modules/optidigitalBidAdapter_spec.js +++ b/test/spec/modules/optidigitalBidAdapter_spec.js @@ -63,20 +63,19 @@ describe('optidigitalAdapterTests', function () { 'adserver': { 'name': 'gam', 'adslot': '/19968336/header-bid-tag-0' - }, - 'pbadslot': '/19968336/header-bid-tag-0' + } }, 'gpid': '/19968336/header-bid-tag-0' } }, 'mediaTypes': { 'banner': { - 'sizes': [ [ 300, 250 ], [ 300, 600 ] ] + 'sizes': [[300, 250], [300, 600]] } }, 'adUnitCode': 'div-gpt-ad-1460505748561-0', 'transactionId': '0cb56262-9637-474d-a572-86fa860fd8b7', - 'sizes': [ [ 300, 250 ], [ 300, 600 ] ], + 'sizes': [[300, 250], [300, 600]], 'bidId': '245d89f17f289f', 'bidderRequestId': '199d7ffafa1e91', 'auctionId': 'b66f01cd-3441-4403-99fa-d8062e795933', @@ -212,7 +211,8 @@ describe('optidigitalAdapterTests', function () { it('should send bid request to given endpoint', function() { const request = spec.buildRequests(validBidRequests, bidderRequest); - expect(request.url).to.equal(ENDPOINT); + const finalEndpoint = `${ENDPOINT}/s123`; + expect(request.url).to.equal(finalEndpoint); }); it('should be bidRequest data', function () { @@ -282,6 +282,49 @@ describe('optidigitalAdapterTests', function () { expect(payload.imp[0].adContainerHeight).to.exist; }); + it('should read container size from DOM when divId exists', function () { + const containerId = 'od-test-container'; + const el = document.createElement('div'); + el.id = containerId; + el.style.width = '321px'; + el.style.height = '111px'; + el.style.position = 'absolute'; + el.style.left = '0'; + el.style.top = '0'; + document.body.appendChild(el); + + const validBidRequestsWithDivId = [ + { + 'bidder': 'optidigital', + 'bidId': '51ef8751f9aead', + 'params': { + 'publisherId': 's123', + 'placementId': 'Billboard_Top', + 'divId': containerId + }, + 'adUnitCode': containerId, + 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', + 'mediaTypes': { + 'banner': { + 'sizes': [[300, 50], [300, 250], [300, 600]] + } + }, + 'sizes': [[320, 50], [300, 250], [300, 600]], + 'bidderRequestId': '418b37f85e772c', + 'auctionId': '18fd8b8b0bd757' + } + ]; + + const request = spec.buildRequests(validBidRequestsWithDivId, bidderRequest); + const payload = JSON.parse(request.data); + try { + expect(payload.imp[0].adContainerWidth).to.equal(el.offsetWidth); + expect(payload.imp[0].adContainerHeight).to.equal(el.offsetHeight); + } finally { + document.body.removeChild(el); + } + }); + it('should add pageTemplate to payload if pageTemplate exsists in parameter', function () { const validBidRequestsWithDivId = [ { @@ -325,7 +368,8 @@ describe('optidigitalAdapterTests', function () { 'domain': 'example.com', 'publisher': { 'domain': 'example.com' - } + }, + 'keywords': 'key1,key2' }, 'device': { 'w': 1507, @@ -386,6 +430,12 @@ describe('optidigitalAdapterTests', function () { expect(payload.bapp).to.deep.equal(validBidRequests[0].params.bapp); }); + it('should add keywords to payload when site keywords present', function() { + const request = spec.buildRequests(validBidRequests, bidderRequest); + const payload = JSON.parse(request.data); + expect(payload.site.keywords).to.deep.equal('key1,key2'); + }); + it('should send empty GDPR consent and required set to false', function() { const request = spec.buildRequests(validBidRequests, bidderRequest); const payload = JSON.parse(request.data); @@ -401,6 +451,7 @@ describe('optidigitalAdapterTests', function () { 'vendorData': { 'hasGlobalConsent': false }, + 'addtlConsent': '1~7.12.35.62.66.70.89.93.108', 'apiVersion': 1 } const request = spec.buildRequests(validBidRequests, bidderRequest); @@ -408,6 +459,7 @@ describe('optidigitalAdapterTests', function () { expect(payload.gdpr).to.exist; expect(payload.gdpr.consent).to.equal(consentString); expect(payload.gdpr.required).to.exist.and.to.be.true; + expect(payload.gdpr.addtlConsent).to.exist; }); it('should send empty GDPR consent to endpoint', function() { @@ -425,6 +477,22 @@ describe('optidigitalAdapterTests', function () { expect(payload.gdpr.consent).to.equal(''); }); + it('should send GDPR consent and required set to false when gdprApplies is not boolean', function() { + let consentString = 'DFR8KRePoQNsRREZCADBG+A=='; + bidderRequest.gdprConsent = { + 'consentString': consentString, + 'gdprApplies': "", + 'vendorData': { + 'hasGlobalConsent': false + }, + 'apiVersion': 1 + } + const request = spec.buildRequests(validBidRequests, bidderRequest); + const payload = JSON.parse(request.data); + expect(payload.gdpr.consent).to.equal(consentString); + expect(payload.gdpr.required).to.exist.and.to.be.false; + }); + it('should send uspConsent to given endpoint', function() { bidderRequest.uspConsent = '1YYY'; const request = spec.buildRequests(validBidRequests, bidderRequest); @@ -457,6 +525,21 @@ describe('optidigitalAdapterTests', function () { expect(payload.gpp).to.exist; }); + it('should set testMode when optidigitalTestMode flag present in location', function() { + const originalUrl = window.location.href; + const newUrl = originalUrl.includes('optidigitalTestMode=true') + ? originalUrl + : `${window.location.pathname}${window.location.search}${window.location.search && window.location.search.length ? '&' : '?'}optidigitalTestMode=true${window.location.hash || ''}`; + try { + window.history.pushState({}, '', newUrl); + const request = spec.buildRequests(validBidRequests, bidderRequest); + const payload = JSON.parse(request.data); + expect(payload.testMode).to.equal(true); + } finally { + window.history.replaceState({}, '', originalUrl); + } + }); + it('should use appropriate mediaTypes banner sizes', function() { const mediaTypesBannerSize = { 'mediaTypes': { @@ -532,6 +615,23 @@ describe('optidigitalAdapterTests', function () { expect(payload.user).to.deep.equal(undefined); }); + it('should add gpid to payload when gpid', function() { + validBidRequests[0].ortb2Imp = { + 'ext': { + 'data': { + 'adserver': { + 'name': 'gam', + 'adslot': '/19968336/header-bid-tag-0' + } + }, + 'gpid': '/19968336/header-bid-tag-0' + } + }; + const request = spec.buildRequests(validBidRequests, bidderRequest); + const payload = JSON.parse(request.data); + expect(payload.imp[0].gpid).to.deep.equal('/19968336/header-bid-tag-0'); + }); + function returnBannerSizes(mediaTypes, expectedSizes) { const bidRequest = Object.assign(validBidRequests[0], mediaTypes); const request = spec.buildRequests([bidRequest], bidderRequest); @@ -563,27 +663,27 @@ describe('optidigitalAdapterTests', function () { }); it('should return appropriate URL with GDPR equals to 1 and GDPR consent', function() { - expect(spec.getUserSyncs({ iframeEnabled: true }, {}, {gdprApplies: true, consentString: 'foo'}, undefined)).to.deep.equal([{ + expect(spec.getUserSyncs({ iframeEnabled: true }, {}, { gdprApplies: true, consentString: 'foo' }, undefined)).to.deep.equal([{ type: 'iframe', url: `${syncurlIframe}&gdpr=1&gdpr_consent=foo` }]); }); it('should return appropriate URL with GDPR equals to 0 and GDPR consent', function() { - expect(spec.getUserSyncs({ iframeEnabled: true }, {}, {gdprApplies: false, consentString: 'foo'}, undefined)).to.deep.equal([{ + expect(spec.getUserSyncs({ iframeEnabled: true }, {}, { gdprApplies: false, consentString: 'foo' }, undefined)).to.deep.equal([{ type: 'iframe', url: `${syncurlIframe}&gdpr=0&gdpr_consent=foo` }]); }); it('should return appropriate URL with GDPR equals to 1 and no consent', function() { - expect(spec.getUserSyncs({ iframeEnabled: true }, {}, {gdprApplies: true, consentString: undefined}, undefined)).to.deep.equal([{ + expect(spec.getUserSyncs({ iframeEnabled: true }, {}, { gdprApplies: true, consentString: undefined }, undefined)).to.deep.equal([{ type: 'iframe', url: `${syncurlIframe}&gdpr=1&gdpr_consent=` }]); }); it('should return appropriate URL with GDPR equals to 1, GDPR consent and US Privacy consent', function() { - expect(spec.getUserSyncs({ iframeEnabled: true }, {}, {gdprApplies: true, consentString: 'foo'}, 'fooUsp')).to.deep.equal([{ + expect(spec.getUserSyncs({ iframeEnabled: true }, {}, { gdprApplies: true, consentString: 'foo' }, 'fooUsp')).to.deep.equal([{ type: 'iframe', url: `${syncurlIframe}&gdpr=1&gdpr_consent=foo&us_privacy=fooUsp` }]); }); it('should return appropriate URL with GDPR equals to 1, GDPR consent, US Privacy consent and GPP consent', function() { - expect(spec.getUserSyncs({ iframeEnabled: true }, {}, {gdprApplies: true, consentString: 'foo'}, 'fooUsp', {gppString: 'fooGpp', applicableSections: [7]})).to.deep.equal([{ + expect(spec.getUserSyncs({ iframeEnabled: true }, {}, { gdprApplies: true, consentString: 'foo' }, 'fooUsp', { gppString: 'fooGpp', applicableSections: [7] })).to.deep.equal([{ type: 'iframe', url: `${syncurlIframe}&gdpr=1&gdpr_consent=foo&us_privacy=fooUsp&gpp=fooGpp&gpp_sid=7` }]); }); diff --git a/test/spec/modules/optimonAnalyticsAdapter_spec.js b/test/spec/modules/optimonAnalyticsAdapter_spec.js index 270f3aec395..e7f5ddf91a8 100644 --- a/test/spec/modules/optimonAnalyticsAdapter_spec.js +++ b/test/spec/modules/optimonAnalyticsAdapter_spec.js @@ -3,7 +3,7 @@ import { expect } from 'chai'; import optimonAnalyticsAdapter from '../../../modules/optimonAnalyticsAdapter.js'; import adapterManager from 'src/adapterManager'; import * as events from 'src/events'; -import {expectEvents} from '../../helpers/analytics.js'; +import { expectEvents } from '../../helpers/analytics.js'; const AD_UNIT_CODE = 'demo-adunit-1'; const PUBLISHER_CONFIG = { diff --git a/test/spec/modules/optoutBidAdapter_spec.js b/test/spec/modules/optoutBidAdapter_spec.js index 0b8e9574ba1..886d2359c61 100644 --- a/test/spec/modules/optoutBidAdapter_spec.js +++ b/test/spec/modules/optoutBidAdapter_spec.js @@ -1,115 +1,925 @@ import { expect } from 'chai'; +import sinon from 'sinon'; +import { config } from 'src/config.js'; +import * as gdprUtils from 'src/utils/gdpr.js'; import { spec } from 'modules/optoutBidAdapter.js'; -import {config} from 'src/config.js'; describe('optoutAdapterTest', function () { - describe('bidRequestValidity', function () { - it('bidRequest with adslot param', function () { + afterEach(function () { + config.resetConfig(); + sinon.restore(); + }); + + describe('isBidRequestValid', function () { + it('valid when publisher + adSlot exist', function () { expect(spec.isBidRequestValid({ bidder: 'optout', - params: { - 'adslot': 'prebid_demo', - 'publisher': '8' - } + params: { adSlot: 'prebid_demo', publisher: '8' } })).to.equal(true); }); - it('bidRequest with no adslot param', function () { + it('valid when publisher + adslot (lowercase) exist', function () { expect(spec.isBidRequestValid({ bidder: 'optout', - params: { - 'publisher': '8' - } + params: { adslot: 'prebid_demo', publisher: '8' } + })).to.equal(true); + }); + + it('invalid when adSlot missing', function () { + expect(spec.isBidRequestValid({ + bidder: 'optout', + params: { publisher: '8' } })).to.equal(false); }); - it('bidRequest with no publisher param', function () { + it('invalid when publisher missing', function () { expect(spec.isBidRequestValid({ bidder: 'optout', - params: { - 'adslot': 'prebid_demo' - } + params: { adSlot: 'prebid_demo' } })).to.equal(false); }); - it('bidRequest without params', function () { + it('invalid when params missing', function () { expect(spec.isBidRequestValid({ bidder: 'optout', - params: { } + params: {} })).to.equal(false); }); + + it('invalid when bid is null', function () { + expect(spec.isBidRequestValid(null)).to.equal(false); + }); + + it('invalid when bid is undefined', function () { + expect(spec.isBidRequestValid(undefined)).to.equal(false); + }); }); - describe('bidRequest', function () { - const bidRequests = [{ - 'bidder': 'optout', - 'params': { - 'adslot': 'prebid_demo', - 'publisher': '8' - }, - 'adUnitCode': 'aaa', - 'transactionId': '1b8389fe-615c-482d-9f1a-177fb8f7d5b0', - 'bidId': '9304jr394ddfj', - 'bidderRequestId': '70deaff71c281d', - 'auctionId': '5c66da22-426a-4bac-b153-77360bef5337' - }, - { - 'bidder': 'optout', - 'params': { - 'adslot': 'testslot2', - 'publisher': '2' - }, - 'adUnitCode': 'bbb', - 'transactionId': '193995b4-7122-4739-959b-2463282a138b', - 'bidId': '893j4f94e8jei', - 'bidderRequestId': '70deaff71c281d', - 'gdprConsent': { - consentString: '', - gdprApplies: true, - apiVersion: 2 + describe('buildRequests', function () { + const bidRequests = [ + { + bidder: 'optout', + params: { adSlot: 'prebid_demo', publisher: '8' }, + bidId: 'bidA' }, - 'auctionId': 'e97cafd0-ebfc-4f5c-b7c9-baa0fd335a4a' - }]; + { + bidder: 'optout', + params: { adSlot: 'testslot2', publisher: '8' }, + bidId: 'bidB' + } + ]; + + it('returns [] when no validBidRequests', function () { + const requests = spec.buildRequests([], {}); + expect(requests).to.deep.equal([]); + }); - it('bidRequest HTTP method', function () { + it('returns a single POST request', function () { const requests = spec.buildRequests(bidRequests, {}); - requests.forEach(function(requestItem) { - expect(requestItem.method).to.equal('POST'); - }); + expect(requests).to.have.lengthOf(1); + expect(requests[0].method).to.equal('POST'); }); - it('bidRequest url without consent', function () { + it('uses optout endpoint when no gdprConsent', function () { const requests = spec.buildRequests(bidRequests, {}); - requests.forEach(function(requestItem) { - expect(requestItem.url).to.match(new RegExp('adscience-nocookie\\.nl/prebid/display')); - }); + expect(requests[0].url).to.match(/optoutadserving\.com\/prebid\/display/); + }); + + it('uses optin endpoint when gdprApplies is false', function () { + const bidderRequest = { + gdprConsent: { + gdprApplies: false, + consentString: 'test', + apiVersion: 2 + } + }; + + const requests = spec.buildRequests(bidRequests, bidderRequest); + expect(requests[0].url).to.match(/optinadserving\.com\/prebid\/display/); + expect(requests[0].data.gdpr).to.equal(0); + }); + + it('handles gdprConsent with apiVersion 1 (no special branching)', function () { + const bidderRequest = { + gdprConsent: { + gdprApplies: true, + consentString: '', + apiVersion: 1 + } + }; + + // purpose1 not stubbed; safest expected is still optout when gdprApplies true & hasPurpose1Consent false/unknown + sinon.stub(gdprUtils, 'hasPurpose1Consent').returns(false); + + const requests = spec.buildRequests(bidRequests, bidderRequest); + expect(requests[0].data.gdpr).to.equal(1); + expect(requests[0].data.consent).to.equal(''); + expect(requests[0].url).to.match(/optoutadserving\.com\/prebid\/display/); + }); + + it('always includes sdk_version=prebid', function () { + const requests = spec.buildRequests(bidRequests, {}); + expect(requests[0].data.sdk_version).to.equal('prebid'); }); - it('bidRequest id', function () { + it('currency defaults to EUR when not configured', function () { + config.resetConfig(); const requests = spec.buildRequests(bidRequests, {}); - expect(requests[0].data.requestId).to.equal('9304jr394ddfj'); - expect(requests[1].data.requestId).to.equal('893j4f94e8jei'); + expect(requests[0].data.cur.adServerCurrency).to.equal('EUR'); }); - it('bidRequest with config for currency', function () { + it('currency uses config when provided', function () { config.setConfig({ - currency: { - adServerCurrency: 'USD', - granularityMultiplier: 1 - } - }) + currency: { adServerCurrency: 'USD', granularityMultiplier: 1 } + }); const requests = spec.buildRequests(bidRequests, {}); expect(requests[0].data.cur.adServerCurrency).to.equal('USD'); - expect(requests[1].data.cur.adServerCurrency).to.equal('USD'); }); - it('bidRequest without config for currency', function () { - config.resetConfig(); + it('builds slots with id and requestId', function () { + const requests = spec.buildRequests(bidRequests, {}); + const slots = requests[0].data.slots; + + expect(slots[0].id).to.equal('prebid_demo'); + expect(slots[0].requestId).to.equal('bidA'); + expect(slots[1].id).to.equal('testslot2'); + expect(slots[1].requestId).to.equal('bidB'); + }); + it('uses refererInfo.canonicalUrl when present (sanitized to origin+pathname)', function () { + const bidderRequest = { refererInfo: { canonicalUrl: 'https://example.com/path?secret=1#frag' } }; + const requests = spec.buildRequests(bidRequests, bidderRequest); + expect(requests[0].data.url).to.equal('https://example.com/path'); + }); + + it('falls back to refererInfo.page when canonicalUrl missing (sanitized)', function () { + const bidderRequest = { refererInfo: { page: 'https://example.com/page?x=1#y' } }; + const requests = spec.buildRequests(bidRequests, bidderRequest); + expect(requests[0].data.url).to.equal('https://example.com/page'); + }); + + it('falls back to window.location (sanitized) when refererInfo missing', function () { const requests = spec.buildRequests(bidRequests, {}); - expect(requests[0].data.cur.adServerCurrency).to.equal('EUR'); - expect(requests[1].data.cur.adServerCurrency).to.equal('EUR'); + // The adapter sanitizes to origin+pathname + expect(requests[0].data.url).to.equal(window.location.origin + window.location.pathname); + }); + + it('uses publisher from the first bid when batching, even if later bids differ', function () { + const br = [ + { bidder: 'optout', params: { adSlot: 'slot1', publisher: 'PUB1' }, bidId: '1' }, + { bidder: 'optout', params: { adSlot: 'slot2', publisher: 'PUB2' }, bidId: '2' } + ]; + + const requests = spec.buildRequests(br, {}); + expect(requests[0].data.publisher).to.equal('PUB1'); + // Slots are still batched into one request + expect(requests[0].data.slots).to.have.lengthOf(2); + }); + + it('normalizes customs to strings, flattens arrays, stringifies objects, and drops invalid (circular omitted)', function () { + const circular = {}; + circular.self = circular; + + const br = [{ + bidder: 'optout', + params: { + adSlot: 'slot', + publisher: '8', + customs: { foo: 'bar', obj: { k: 'v' }, bad: circular } + }, + bidId: '1' + }]; + + const bidderRequest = { + ortb2: { + ext: { data: { a: ['x', 'y'], b: 123, c: null } } + } + }; + + const requests = spec.buildRequests(br, bidderRequest); + const customs = requests[0].data.customs; + + expect(customs.foo).to.equal('bar'); + expect(customs.a).to.equal('x,y'); + expect(customs.b).to.equal('123'); + expect(customs.obj).to.equal(JSON.stringify({ k: 'v' })); + expect(customs).to.not.have.property('c'); + // 'bad' was circular, so it must be dropped + expect(customs).to.not.have.property('bad'); + }); + + it('handles customs as null/undefined without throwing', function () { + const br = [{ + bidder: 'optout', + params: { + adSlot: 'slot', + publisher: '8', + customs: null + }, + bidId: '1' + }]; + + const requests = spec.buildRequests(br, {}); + // customs absent -> no slot.customs + expect(requests[0].data.slots[0]).to.not.have.property('customs'); + }); + + it('does not mutate input customs objects', function () { + const original = { a: ['x', 'y'], obj: { k: 'v' } }; + + const br = [{ + bidder: 'optout', + params: { adSlot: 'slot', publisher: '8', customs: original }, + bidId: '1' + }]; + + spec.buildRequests(br, {}); + expect(original.a).to.deep.equal(['x', 'y']); + expect(original.obj).to.deep.equal({ k: 'v' }); + }); + + it('includes per-slot customs when provided in bid params', function () { + const br = [{ + bidder: 'optout', + params: { + adSlot: 'slot', + publisher: '8', + customs: { x: 1, arr: ['a', 'b'], obj: { p: true } } + }, + bidId: '1' + }]; + + const requests = spec.buildRequests(br, {}); + const slotCustoms = requests[0].data.slots[0].customs; + + expect(slotCustoms.x).to.equal('1'); + expect(slotCustoms.arr).to.equal('a,b'); + expect(slotCustoms.obj).to.equal(JSON.stringify({ p: true })); + }); + + it('includes ortb2 payload when any bid sets includeOrtb2', function () { + const br = [{ + bidder: 'optout', + params: { adSlot: 'slot', publisher: '8', includeOrtb2: true }, + bidId: '1' + }]; + + const bidderRequest = { ortb2: { site: { domain: 'example.com' } } }; + const requests = spec.buildRequests(br, bidderRequest); + + expect(requests[0].data.ortb2).to.equal(JSON.stringify(bidderRequest.ortb2)); + }); + + it('does not include ortb2 payload when includeOrtb2 is false/absent', function () { + const br = [{ + bidder: 'optout', + params: { adSlot: 'slot', publisher: '8' }, + bidId: '1' + }]; + + const bidderRequest = { ortb2: { site: { domain: 'example.com' } } }; + const requests = spec.buildRequests(br, bidderRequest); + + expect(requests[0].data).to.not.have.property('ortb2'); + }); + + it('includes slot id when params.id is explicitly set', function () { + const br = [{ + bidder: 'optout', + params: { adSlot: 'slot', publisher: '8', id: 'customId123' }, + bidId: '1' + }]; + + const requests = spec.buildRequests(br, {}); + expect(requests[0].data.slots[0].id).to.equal('customId123'); + }); + + it('handles when validBidRequests is null', function () { + const requests = spec.buildRequests(null, {}); + expect(requests).to.deep.equal([]); + }); + + it('handles when validBidRequests is not an array', function () { + const requests = spec.buildRequests('not-an-array', {}); + expect(requests).to.deep.equal([]); + }); + + it('handles when bidderRequest is null', function () { + const br = [{ + bidder: 'optout', + params: { adSlot: 'slot', publisher: '8' }, + bidId: '1' + }]; + + const requests = spec.buildRequests(br, null); + expect(requests).to.have.lengthOf(1); + expect(requests[0].data.gdpr).to.equal(0); + }); + + it('handles when gdprConsent is a non-object truthy value', function () { + const br = [{ + bidder: 'optout', + params: { adSlot: 'slot', publisher: '8' }, + bidId: '1' + }]; + + const bidderRequest = { gdprConsent: 'some-string' }; + const requests = spec.buildRequests(br, bidderRequest); + expect(requests[0].data.gdpr).to.equal(0); + expect(requests[0].url).to.match(/optoutadserving\.com\/prebid\/display/); + }); + + it('merges customs from ortb2.site.ext.data', function () { + const br = [{ + bidder: 'optout', + params: { adSlot: 'slot', publisher: '8' }, + bidId: '1' + }]; + + const bidderRequest = { + ortb2: { + site: { ext: { data: { siteKey: 'siteValue' } } } + } + }; + + const requests = spec.buildRequests(br, bidderRequest); + expect(requests[0].data.customs.siteKey).to.equal('siteValue'); + }); + + it('merges customs from ortb2.app.ext.data', function () { + const br = [{ + bidder: 'optout', + params: { adSlot: 'slot', publisher: '8' }, + bidId: '1' + }]; + + const bidderRequest = { + ortb2: { + app: { ext: { data: { appKey: 'appValue' } } } + } + }; + + const requests = spec.buildRequests(br, bidderRequest); + expect(requests[0].data.customs.appKey).to.equal('appValue'); + }); + + it('merges customs from ortb2.user.ext.data', function () { + const br = [{ + bidder: 'optout', + params: { adSlot: 'slot', publisher: '8' }, + bidId: '1' + }]; + + const bidderRequest = { + ortb2: { + user: { ext: { data: { userKey: 'userValue' } } } + } + }; + + const requests = spec.buildRequests(br, bidderRequest); + expect(requests[0].data.customs.userKey).to.equal('userValue'); + }); + + it('does not include slot.customs when params.customs is empty/undefined', function () { + const br = [{ + bidder: 'optout', + params: { adSlot: 'slot', publisher: '8' }, + bidId: '1' + }]; + + const requests = spec.buildRequests(br, {}); + expect(requests[0].data.slots[0]).to.not.have.property('customs'); + }); + + it('builds slots with lowercase adslot param', function () { + const br = [{ + bidder: 'optout', + params: { adslot: 'lowercase_slot', publisher: '8' }, + bidId: '1' + }]; + + const requests = spec.buildRequests(br, {}); + const slot = requests[0].data.slots[0]; + + expect(slot.adSlot).to.equal('lowercase_slot'); + expect(slot.requestId).to.equal('1'); + }); + + it('normalizes customs with non-object input (returns empty object)', function () { + const br = [{ + bidder: 'optout', + params: { adSlot: 'slot', publisher: '8', customs: 'not-an-object' }, + bidId: '1' + }]; + + const requests = spec.buildRequests(br, {}); + expect(requests[0].data.slots[0]).to.not.have.property('customs'); + }); + + it('normalizes customs with undefined input', function () { + const br = [{ + bidder: 'optout', + params: { adSlot: 'slot', publisher: '8', customs: undefined }, + bidId: '1' + }]; + + const requests = spec.buildRequests(br, {}); + expect(requests[0].data.slots[0]).to.not.have.property('customs'); + }); + + it('handles gdprConsent.consentString as empty when missing', function () { + const br = [{ + bidder: 'optout', + params: { adSlot: 'slot', publisher: '8' }, + bidId: '1' + }]; + + const bidderRequest = { + gdprConsent: { + gdprApplies: true, + apiVersion: 2 + } + }; + + sinon.stub(gdprUtils, 'hasPurpose1Consent').returns(false); + + const requests = spec.buildRequests(br, bidderRequest); + expect(requests[0].data.consent).to.equal(''); + }); + }); + + describe('buildRequests (GDPR Purpose 1)', function () { + const bidRequests = [ + { bidder: 'optout', params: { adSlot: 'slot', publisher: '8' }, bidId: '1' } + ]; + + it('routes to optin when purpose1 consent is true', function () { + sinon.stub(gdprUtils, 'hasPurpose1Consent').returns(true); + + const bidderRequest = { + gdprConsent: { + gdprApplies: true, + consentString: 'CONSENT', + apiVersion: 2 + } + }; + + const requests = spec.buildRequests(bidRequests, bidderRequest); + expect(requests[0].url).to.match(/optinadserving\.com\/prebid\/display/); + expect(requests[0].data.gdpr).to.equal(1); + }); + + it('routes to optout when purpose1 consent is false', function () { + sinon.stub(gdprUtils, 'hasPurpose1Consent').returns(false); + + const bidderRequest = { + gdprConsent: { + gdprApplies: true, + consentString: 'CONSENT', + apiVersion: 2 + } + }; + + const requests = spec.buildRequests(bidRequests, bidderRequest); + expect(requests[0].url).to.match(/optoutadserving\.com\/prebid\/display/); + expect(requests[0].data.gdpr).to.equal(1); + }); + }); + + describe('interpretResponse', function () { + // Helper to create standard bidRequest structure + const createBidRequest = (slots) => ({ data: { slots } }); + + // Helper to create standard serverResponse structure + const createServerResponse = (bids) => ({ body: { bids } }); + + // Standard slot and bid for reuse + const standardSlot = { id: 'slotA', requestId: 'bidA' }; + + it('maps bids correctly using slot id or requestId', function () { + const bidRequest = createBidRequest([ + { id: 'slotA', requestId: 'bidA' }, + { id: 'slotB', requestId: 'bidB' } + ]); + + const serverResponse = createServerResponse([ + { requestId: 'bidA', cpm: 1, currency: 'EUR', width: 300, height: 250, ad: '
', ttl: 300, creativeId: 'c1' }, + { requestId: 'slotB', cpm: 2, currency: 'EUR', width: 728, height: 90, ad: '
', ttl: 300, creativeId: 'c2' } + ]); + + const out = spec.interpretResponse(serverResponse, bidRequest); + expect(out).to.have.lengthOf(2); + expect(out[0].netRevenue).to.equal(true); + }); + + it('filters bids whose requestId does not map to a sent slot/requestId', function () { + const bidRequest = createBidRequest([standardSlot]); + const serverResponse = createServerResponse([ + { requestId: 'unknown', cpm: 1, currency: 'EUR', width: 300, height: 250, ad: '
', ttl: 300, creativeId: 'c1' } + ]); + + const out = spec.interpretResponse(serverResponse, bidRequest); + expect(out).to.deep.equal([]); + }); + + it('supports serverResponse.body as an array (defensive parsing)', function () { + // This also verifies numeric coercion is robust if server returns strings. + const bidRequest = createBidRequest([standardSlot]); + const serverResponse = { + body: [{ requestId: 'bidA', cpm: '1.2', currency: 'EUR', width: '300', height: '250', ad: '
', ttl: '120', creativeId: 'c1' }] + }; + + const out = spec.interpretResponse(serverResponse, bidRequest); + expect(out).to.have.lengthOf(1); + expect(out[0].cpm).to.equal(1.2); + expect(out[0].ttl).to.equal(120); + }); + + it('drops bids with missing/invalid requestId', function () { + const bidRequest = createBidRequest([standardSlot]); + const serverResponse = createServerResponse([ + { requestId: null, cpm: 1, currency: 'EUR', width: 300, height: 250, ad: '
' }, + { /* missing requestId */ cpm: 1, currency: 'EUR', width: 300, height: 250, ad: '
' } + ]); + + const out = spec.interpretResponse(serverResponse, bidRequest); + expect(out).to.deep.equal([]); + }); + + it('drops incomplete bids missing required fields', function () { + const bidRequest = createBidRequest([standardSlot]); + const serverResponse = createServerResponse([ + { requestId: 'bidA', cpm: 1, /* currency missing */ width: 300, height: 250, ad: '
' }, + { requestId: 'bidA', cpm: 1, currency: 'EUR', /* width missing */ height: 250, ad: '
' }, + { requestId: 'bidA', cpm: 1, currency: 'EUR', width: 300, /* height missing */ ad: '
' }, + { requestId: 'bidA', cpm: 1, currency: 'EUR', width: 300, height: 250 /* ad missing */ } + ]); + + const out = spec.interpretResponse(serverResponse, bidRequest); + expect(out).to.deep.equal([]); + }); + + it('drops bids with cpm = 0', function () { + const bidRequest = { + data: { slots: [{ id: 'slotA', requestId: 'bidA' }] } + }; + + const serverResponse = { + body: { + bids: [{ requestId: 'bidA', cpm: 0, currency: 'EUR', width: 300, height: 250, ad: '
' }] + } + }; + + const out = spec.interpretResponse(serverResponse, bidRequest); + expect(out).to.deep.equal([]); + }); + + it('drops bids with negative cpm', function () { + const bidRequest = { + data: { slots: [{ id: 'slotA', requestId: 'bidA' }] } + }; + + const serverResponse = { + body: { + bids: [{ requestId: 'bidA', cpm: -1, currency: 'EUR', width: 300, height: 250, ad: '
' }] + } + }; + + const out = spec.interpretResponse(serverResponse, bidRequest); + expect(out).to.deep.equal([]); + }); + + it('drops bids with NaN cpm', function () { + const bidRequest = { + data: { slots: [{ id: 'slotA', requestId: 'bidA' }] } + }; + + const serverResponse = { + body: { + bids: [{ requestId: 'bidA', cpm: NaN, currency: 'EUR', width: 300, height: 250, ad: '
' }] + } + }; + + const out = spec.interpretResponse(serverResponse, bidRequest); + expect(out).to.deep.equal([]); + }); + + it('drops bids with cpm = null', function () { + const bidRequest = { + data: { slots: [{ id: 'slotA', requestId: 'bidA' }] } + }; + + const serverResponse = { + body: { + bids: [{ requestId: 'bidA', cpm: null, currency: 'EUR', width: 300, height: 250, ad: '
' }] + } + }; + + const out = spec.interpretResponse(serverResponse, bidRequest); + expect(out).to.deep.equal([]); + }); + + it('drops bids with width = 0', function () { + const bidRequest = { + data: { slots: [{ id: 'slotA', requestId: 'bidA' }] } + }; + + const serverResponse = { + body: { + bids: [{ requestId: 'bidA', cpm: 1, currency: 'EUR', width: 0, height: 250, ad: '
' }] + } + }; + + const out = spec.interpretResponse(serverResponse, bidRequest); + expect(out).to.deep.equal([]); + }); + + it('drops bids with negative width', function () { + const bidRequest = { + data: { slots: [{ id: 'slotA', requestId: 'bidA' }] } + }; + + const serverResponse = { + body: { + bids: [{ requestId: 'bidA', cpm: 1, currency: 'EUR', width: -300, height: 250, ad: '
' }] + } + }; + + const out = spec.interpretResponse(serverResponse, bidRequest); + expect(out).to.deep.equal([]); + }); + + it('drops bids with non-finite width', function () { + const bidRequest = { + data: { slots: [{ id: 'slotA', requestId: 'bidA' }] } + }; + + const serverResponse = { + body: { + bids: [{ requestId: 'bidA', cpm: 1, currency: 'EUR', width: Infinity, height: 250, ad: '
' }] + } + }; + + const out = spec.interpretResponse(serverResponse, bidRequest); + expect(out).to.deep.equal([]); + }); + + it('drops bids with height = 0', function () { + const bidRequest = { + data: { slots: [{ id: 'slotA', requestId: 'bidA' }] } + }; + + const serverResponse = { + body: { + bids: [{ requestId: 'bidA', cpm: 1, currency: 'EUR', width: 300, height: 0, ad: '
' }] + } + }; + + const out = spec.interpretResponse(serverResponse, bidRequest); + expect(out).to.deep.equal([]); + }); + + it('drops bids with negative height', function () { + const bidRequest = { + data: { slots: [{ id: 'slotA', requestId: 'bidA' }] } + }; + + const serverResponse = { + body: { + bids: [{ requestId: 'bidA', cpm: 1, currency: 'EUR', width: 300, height: -250, ad: '
' }] + } + }; + + const out = spec.interpretResponse(serverResponse, bidRequest); + expect(out).to.deep.equal([]); + }); + + it('drops bids with non-finite height', function () { + const bidRequest = { + data: { slots: [{ id: 'slotA', requestId: 'bidA' }] } + }; + + const serverResponse = { + body: { + bids: [{ requestId: 'bidA', cpm: 1, currency: 'EUR', width: 300, height: Infinity, ad: '
' }] + } + }; + + const out = spec.interpretResponse(serverResponse, bidRequest); + expect(out).to.deep.equal([]); + }); + + it('defaults ttl to 300 when missing', function () { + const bidRequest = { + data: { slots: [{ id: 'slotA', requestId: 'bidA' }] } + }; + + const serverResponse = { + body: { + bids: [{ requestId: 'bidA', cpm: 1, currency: 'EUR', width: 300, height: 250, ad: '
', creativeId: 'c1' }] + } + }; + + const out = spec.interpretResponse(serverResponse, bidRequest); + expect(out[0].ttl).to.equal(300); + }); + + it('passes through optOutExt and meta when present', function () { + const bidRequest = { + data: { slots: [{ id: 'slotA', requestId: 'bidA' }] } + }; + + const serverResponse = { + body: { + bids: [{ + requestId: 'bidA', + cpm: 1, + currency: 'EUR', + width: 300, + height: 250, + ad: '
', + ttl: 300, + creativeId: 'c1', + optOutExt: { foo: 'bar' }, + meta: { advertiserDomains: ['example.com'] } + }] + } + }; + + const out = spec.interpretResponse(serverResponse, bidRequest); + expect(out[0].optOutExt).to.deep.equal({ foo: 'bar' }); + expect(out[0].meta).to.deep.equal({ advertiserDomains: ['example.com'] }); + }); + + it('uses requestId as creativeId fallback when creativeId is missing', function () { + const bidRequest = { + data: { slots: [{ id: 'slotA', requestId: 'bidA' }] } + }; + + const serverResponse = { + body: { + bids: [{ + requestId: 'bidA', + cpm: 1, + currency: 'EUR', + width: 300, + height: 250, + ad: '
', + ttl: 300 + // creativeId intentionally missing + }] + } + }; + + const out = spec.interpretResponse(serverResponse, bidRequest); + expect(out).to.have.lengthOf(1); + expect(out[0].creativeId).to.equal('bidA'); // falls back to requestId + }); + + it('handles when serverResponse is null', function () { + const bidRequest = { + data: { slots: [{ id: 'slotA', requestId: 'bidA' }] } + }; + + const out = spec.interpretResponse(null, bidRequest); + expect(out).to.deep.equal([]); + }); + + it('handles when serverResponse is undefined', function () { + const bidRequest = { + data: { slots: [{ id: 'slotA', requestId: 'bidA' }] } + }; + + const out = spec.interpretResponse(undefined, bidRequest); + expect(out).to.deep.equal([]); + }); + + it('handles when serverResponse.body is null', function () { + const bidRequest = { + data: { slots: [{ id: 'slotA', requestId: 'bidA' }] } + }; + + const out = spec.interpretResponse({ body: null }, bidRequest); + expect(out).to.deep.equal([]); + }); + + it('handles when serverResponse.body is undefined', function () { + const bidRequest = { + data: { slots: [{ id: 'slotA', requestId: 'bidA' }] } + }; + + const out = spec.interpretResponse({ body: undefined }, bidRequest); + expect(out).to.deep.equal([]); + }); + + it('handles when bidRequest.data.slots is missing', function () { + const bidRequest = { data: {} }; + + const serverResponse = { + body: { + bids: [{ requestId: 'bidA', cpm: 1, currency: 'EUR', width: 300, height: 250, ad: '
' }] + } + }; + + const out = spec.interpretResponse(serverResponse, bidRequest); + expect(out).to.deep.equal([]); + }); + }); + + describe('getUserSyncs', function () { + it('returns [] when gdprConsent missing', function () { + const out = spec.getUserSyncs({ iframeEnabled: true }, [], null); + expect(out).to.deep.equal([]); + }); + + it('returns [] when gdprConsent is a non-object truthy value', function () { + const out = spec.getUserSyncs({ iframeEnabled: true }, [], 'CONSENTSTRING'); + expect(out).to.deep.equal([]); + }); + + it('returns [] when iframeEnabled is false', function () { + const out = spec.getUserSyncs( + { iframeEnabled: false }, + [], + { gdprApplies: false, consentString: 'abc' } + ); + expect(out).to.deep.equal([]); + }); + + it('returns iframe sync when iframeEnabled and gdprApplies is false', function () { + const out = spec.getUserSyncs( + { iframeEnabled: true }, + [], + { gdprApplies: false, consentString: 'abc' } + ); + + expect(out).to.have.lengthOf(1); + expect(out[0].type).to.equal('iframe'); + expect(out[0].url).to.include('gdpr=0'); + expect(out[0].url).to.include('gdpr_consent=' + encodeURIComponent('abc')); + }); + + it('returns iframe sync when gdprApplies true and purpose1 consent true', function () { + sinon.stub(gdprUtils, 'hasPurpose1Consent').returns(true); + + const out = spec.getUserSyncs( + { iframeEnabled: true }, + [], + { gdprApplies: true, consentString: 'CONSENT' } + ); + + expect(out).to.have.lengthOf(1); + expect(out[0].type).to.equal('iframe'); + expect(out[0].url).to.include('gdpr=1'); + expect(out[0].url).to.include('gdpr_consent=' + encodeURIComponent('CONSENT')); + }); + + it('returns [] when gdprApplies true and purpose1 consent false', function () { + sinon.stub(gdprUtils, 'hasPurpose1Consent').returns(false); + + const out = spec.getUserSyncs( + { iframeEnabled: true }, + [], + { gdprApplies: true, consentString: 'CONSENT' } + ); + + expect(out).to.deep.equal([]); + }); + + it('handles missing consentString gracefully', function () { + sinon.stub(gdprUtils, 'hasPurpose1Consent').returns(true); + + const out = spec.getUserSyncs( + { iframeEnabled: true }, + [], + { gdprApplies: true } + ); + + expect(out).to.have.lengthOf(1); + expect(out[0].url).to.include('gdpr_consent='); + }); + + it('handles empty consentString', function () { + const out = spec.getUserSyncs( + { iframeEnabled: true }, + [], + { gdprApplies: false, consentString: '' } + ); + + expect(out).to.have.lengthOf(1); + expect(out[0].url).to.include('gdpr_consent='); + }); + + it('handles gdprApplies as non-boolean (converts to 0)', function () { + const out = spec.getUserSyncs( + { iframeEnabled: true }, + [], + { gdprApplies: 'not-a-boolean', consentString: 'abc' } + ); + + expect(out).to.have.lengthOf(1); + expect(out[0].url).to.include('gdpr=0'); }); }); }); diff --git a/test/spec/modules/orakiBidAdapter_spec.js b/test/spec/modules/orakiBidAdapter_spec.js index 4a6b8fa7d36..f3f31be3e30 100644 --- a/test/spec/modules/orakiBidAdapter_spec.js +++ b/test/spec/modules/orakiBidAdapter_spec.js @@ -478,7 +478,7 @@ describe('OrakiBidAdapter', function () { const syncData = spec.getUserSyncs({}, {}, { consentString: 'ALL', gdprApplies: true, - }, {}); + }, undefined); expect(syncData).to.be.an('array').which.is.not.empty; expect(syncData[0]).to.be.an('object') expect(syncData[0].type).to.be.a('string') @@ -487,9 +487,7 @@ describe('OrakiBidAdapter', function () { expect(syncData[0].url).to.equal('https://sync.oraki.io/image?pbjs=1&gdpr=1&gdpr_consent=ALL&coppa=0') }); it('Should return array of objects with proper sync config , include CCPA', function() { - const syncData = spec.getUserSyncs({}, {}, {}, { - consentString: '1---' - }); + const syncData = spec.getUserSyncs({}, {}, {}, '1---'); expect(syncData).to.be.an('array').which.is.not.empty; expect(syncData[0]).to.be.an('object') expect(syncData[0].type).to.be.a('string') @@ -498,7 +496,7 @@ describe('OrakiBidAdapter', function () { expect(syncData[0].url).to.equal('https://sync.oraki.io/image?pbjs=1&ccpa_consent=1---&coppa=0') }); it('Should return array of objects with proper sync config , include GPP', function() { - const syncData = spec.getUserSyncs({}, {}, {}, {}, { + const syncData = spec.getUserSyncs({}, {}, {}, undefined, { gppString: 'abc123', applicableSections: [8] }); diff --git a/test/spec/modules/orbidderBidAdapter_spec.js b/test/spec/modules/orbidderBidAdapter_spec.js index dc33222f8ae..e949dcd9817 100644 --- a/test/spec/modules/orbidderBidAdapter_spec.js +++ b/test/spec/modules/orbidderBidAdapter_spec.js @@ -3,7 +3,7 @@ import { spec } from 'modules/orbidderBidAdapter.js'; import { newBidder } from 'src/adapters/bidderFactory.js'; import * as _ from 'lodash'; import { BANNER, NATIVE } from '../../../src/mediaTypes.js'; -import {getGlobal} from '../../../src/prebidGlobal.js'; +import { getGlobal } from '../../../src/prebidGlobal.js'; describe('orbidderBidAdapter', () => { const adapter = newBidder(spec); diff --git a/test/spec/modules/orbitsoftBidAdapter_spec.js b/test/spec/modules/orbitsoftBidAdapter_spec.js index 9615daa9887..001f175347c 100644 --- a/test/spec/modules/orbitsoftBidAdapter_spec.js +++ b/test/spec/modules/orbitsoftBidAdapter_spec.js @@ -1,5 +1,5 @@ -import {expect} from 'chai'; -import {spec} from 'modules/orbitsoftBidAdapter.js'; +import { expect } from 'chai'; +import { spec } from 'modules/orbitsoftBidAdapter.js'; const ENDPOINT_URL = 'https://orbitsoft.com/php/ads/hb.phps'; const REFERRER_URL = 'http://referrer.url/?_='; @@ -65,7 +65,7 @@ describe('Orbitsoft adapter', function () { } } }, - refererInfo: {referer: REFERRER_URL}, + refererInfo: { referer: REFERRER_URL }, }; const isValid = spec.isBidRequestValid(validBid); expect(isValid).to.equal(true); @@ -105,7 +105,7 @@ describe('Orbitsoft adapter', function () { clickUrl: 'http://testclickurl.com' } }, - refererInfo: {referer: REFERRER_URL}, + refererInfo: { referer: REFERRER_URL }, }; const isValid = spec.isBidRequestValid(validBid); expect(isValid).to.equal(true); @@ -162,7 +162,7 @@ describe('Orbitsoft adapter', function () { } } ]; - const bids = spec.interpretResponse(serverResponse, {'bidRequest': bidRequests[0]}); + const bids = spec.interpretResponse(serverResponse, { 'bidRequest': bidRequests[0] }); expect(bids).to.be.lengthOf(1); expect(bids[0].cpm).to.equal(serverResponse.body.cpm); expect(bids[0].width).to.equal(serverResponse.body.width); @@ -191,7 +191,7 @@ describe('Orbitsoft adapter', function () { cpm: 0 } }; - const bids = spec.interpretResponse(serverResponse, {'bidRequest': bidRequests[0]}); + const bids = spec.interpretResponse(serverResponse, { 'bidRequest': bidRequests[0] }); expect(bids).to.be.lengthOf(0); }); @@ -214,7 +214,7 @@ describe('Orbitsoft adapter', function () { height: 0 } }; - const bids = spec.interpretResponse(serverResponse, {'bidRequest': bidRequests[0]}); + const bids = spec.interpretResponse(serverResponse, { 'bidRequest': bidRequests[0] }); expect(bids).to.be.lengthOf(0); }); @@ -229,8 +229,8 @@ describe('Orbitsoft adapter', function () { } } ]; - const serverResponse = {error: 'error'}; - const bids = spec.interpretResponse(serverResponse, {'bidRequest': bidRequests[0]}); + const serverResponse = { error: 'error' }; + const bids = spec.interpretResponse(serverResponse, { 'bidRequest': bidRequests[0] }); expect(bids).to.be.lengthOf(0); }); @@ -246,7 +246,7 @@ describe('Orbitsoft adapter', function () { } ]; const serverResponse = {}; - const bids = spec.interpretResponse(serverResponse, {'bidRequest': bidRequests[0]}); + const bids = spec.interpretResponse(serverResponse, { 'bidRequest': bidRequests[0] }); expect(bids).to.be.lengthOf(0); }); diff --git a/test/spec/modules/otmBidAdapter_spec.js b/test/spec/modules/otmBidAdapter_spec.js index 27da17b8415..3503247d221 100644 --- a/test/spec/modules/otmBidAdapter_spec.js +++ b/test/spec/modules/otmBidAdapter_spec.js @@ -1,5 +1,5 @@ -import {expect} from 'chai'; -import {spec} from 'modules/otmBidAdapter'; +import { expect } from 'chai'; +import { spec } from 'modules/otmBidAdapter'; describe('otmBidAdapter', function () { it('pub_params', function () { @@ -42,7 +42,7 @@ describe('otmBidAdapter', function () { sizes: [[240, 400]] }]; - const bidderRequest = {refererInfo: {page: `https://github.com:3000/`, domain: 'github.com:3000'}} + const bidderRequest = { refererInfo: { page: `https://github.com:3000/`, domain: 'github.com:3000' } } const request = spec.buildRequests(bidRequestData, bidderRequest); const req_data = request[0].data; diff --git a/test/spec/modules/outbrainBidAdapter_spec.js b/test/spec/modules/outbrainBidAdapter_spec.js index 06b94d985f2..c3c33082403 100644 --- a/test/spec/modules/outbrainBidAdapter_spec.js +++ b/test/spec/modules/outbrainBidAdapter_spec.js @@ -647,7 +647,7 @@ describe('Outbrain Adapter', function () { const res = spec.buildRequests( [bidRequest], - {...commonBidderRequest, ...ortb2WithDeviceData}, + { ...commonBidderRequest, ...ortb2WithDeviceData }, ); expect(JSON.parse(res.data).device).to.deep.equal(ortb2WithDeviceData.ortb2.device); }); diff --git a/test/spec/modules/oxxionAnalyticsAdapter_spec.js b/test/spec/modules/oxxionAnalyticsAdapter_spec.js index da8c3f698b8..f569da9c181 100644 --- a/test/spec/modules/oxxionAnalyticsAdapter_spec.js +++ b/test/spec/modules/oxxionAnalyticsAdapter_spec.js @@ -1,4 +1,4 @@ -import oxxionAnalytics, {dereferenceWithoutRenderer} from 'modules/oxxionAnalyticsAdapter.js'; +import oxxionAnalytics, { dereferenceWithoutRenderer } from 'modules/oxxionAnalyticsAdapter.js'; import { expect } from 'chai'; import { server } from 'test/mocks/xhr.js'; @@ -324,7 +324,7 @@ describe('Oxxion Analytics', function () { }); it('test bidWon', function() { - window.OXXION_MODE = {'abtest': true}; + window.OXXION_MODE = { 'abtest': true }; adapterManager.registerAnalyticsAdapter({ code: 'oxxion', adapter: oxxionAnalytics diff --git a/test/spec/modules/oxxionRtdProvider_spec.js b/test/spec/modules/oxxionRtdProvider_spec.js index f5d2606e8ee..aedd0ffdea7 100644 --- a/test/spec/modules/oxxionRtdProvider_spec.js +++ b/test/spec/modules/oxxionRtdProvider_spec.js @@ -1,4 +1,4 @@ -import {oxxionSubmodule} from 'modules/oxxionRtdProvider.js'; +import { oxxionSubmodule } from 'modules/oxxionRtdProvider.js'; import 'src/prebid.js'; const utils = require('src/utils.js'); @@ -22,15 +22,15 @@ const request = { { 'code': 'msq_tag_200124_banner', 'mediaTypes': { 'banner': { 'sizes': [[300, 600]] } }, - 'bids': [{'bidder': 'appnexus', 'params': {'placementId': 123456}}], + 'bids': [{ 'bidder': 'appnexus', 'params': { 'placementId': 123456 } }], 'transactionId': 'de664ccb-e18b-4436-aeb0-362382eb1b40' }, { 'code': 'msq_tag_200125_video', 'mediaTypes': { 'video': { 'context': 'instream' }, playerSize: [640, 480], mimes: ['video/mp4'] }, 'bids': [ - {'bidder': 'mediasquare', 'params': {'code': 'publishername_atf_desktop_rg_video', 'owner': 'test'}}, - {'bidder': 'appnexusAst', 'params': {'placementId': 345678}}, + { 'bidder': 'mediasquare', 'params': { 'code': 'publishername_atf_desktop_rg_video', 'owner': 'test' } }, + { 'bidder': 'appnexusAst', 'params': { 'placementId': 345678 } }, ], 'transactionId': 'de664ccb-e18b-4436-aeb0-362382eb1b41' }, @@ -38,7 +38,7 @@ const request = { 'code': 'msq_tag_200125_banner', 'mediaTypes': { 'banner': { 'sizes': [[300, 250]] } }, 'bids': [ - {'bidder': 'appnexusAst', 'params': {'placementId': 345678}}, + { 'bidder': 'appnexusAst', 'params': { 'placementId': 345678 } }, ], 'transactionId': 'de664ccb-e18b-4436-aeb0-362382eb1b41' } @@ -114,10 +114,10 @@ const bids = [{ ]; const bidInterests = [ - {'id': 0, 'rate': 50.0, 'suggestion': true}, - {'id': 1, 'rate': 12.0, 'suggestion': false}, - {'id': 2, 'rate': 0.0, 'suggestion': true}, - {'id': 3, 'rate': 0.0, 'suggestion': false}, + { 'id': 0, 'rate': 50.0, 'suggestion': true }, + { 'id': 1, 'rate': 12.0, 'suggestion': false }, + { 'id': 2, 'rate': 0.0, 'suggestion': true }, + { 'id': 3, 'rate': 0.0, 'suggestion': false }, ]; const userConsent = { diff --git a/test/spec/modules/ozoneBidAdapter_spec.js b/test/spec/modules/ozoneBidAdapter_spec.js index 3eabc8a5190..9e6092f2cad 100644 --- a/test/spec/modules/ozoneBidAdapter_spec.js +++ b/test/spec/modules/ozoneBidAdapter_spec.js @@ -1,9 +1,9 @@ import { expect } from 'chai'; import { spec, getWidthAndHeightFromVideoObject, defaultSize } from 'modules/ozoneBidAdapter.js'; import { config } from 'src/config.js'; -import {Renderer} from '../../../src/Renderer.js'; +import { Renderer } from '../../../src/Renderer.js'; import * as utils from '../../../src/utils.js'; -import {deepSetValue, mergeDeep} from '../../../src/utils.js'; +import { deepSetValue, mergeDeep } from '../../../src/utils.js'; const OZONEURI = 'https://elb.the-ozone-project.com/openrtb2/auction'; const BIDDER_CODE = 'ozone'; spec.getGetParametersAsObject = function() { @@ -20,8 +20,8 @@ var validBidRequests = [ bidRequestsCount: 1, bidder: 'ozone', bidderRequestId: '1c1586b27a1b5c8', - crumbs: {pubcid: '203a0692-f728-4856-87f6-9a25a6b63715'}, - params: { publisherId: '9876abcd12-3', customData: [{'settings': {}, 'targeting': {'gender': 'bart', 'age': 'low'}}], 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 } } ] }, + crumbs: { pubcid: '203a0692-f728-4856-87f6-9a25a6b63715' }, + params: { publisherId: '9876abcd12-3', customData: [{ 'settings': {}, 'targeting': { 'gender': 'bart', 'age': 'low' } }], 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' } @@ -34,8 +34,8 @@ var validBidRequestsNoCustomData = [ bidRequestsCount: 1, bidder: 'ozone', bidderRequestId: '1c1586b27a1b5c8', - crumbs: {pubcid: '203a0692-f728-4856-87f6-9a25a6b63715'}, - 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 } } ] }, + crumbs: { pubcid: '203a0692-f728-4856-87f6-9a25a6b63715' }, + 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' } @@ -49,8 +49,8 @@ var validBidRequestsMulti = [ bidRequestsCount: 1, bidder: 'ozone', bidderRequestId: '1c1586b27a1b5c8', - crumbs: {pubcid: '203a0692-f728-4856-87f6-9a25a6b63715'}, - params: { publisherId: '9876abcd12-3', customData: [{'settings': {}, 'targeting': {'gender': 'bart', 'age': 'low'}}], 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 } } ] }, + crumbs: { pubcid: '203a0692-f728-4856-87f6-9a25a6b63715' }, + params: { publisherId: '9876abcd12-3', customData: [{ 'settings': {}, 'targeting': { 'gender': 'bart', 'age': 'low' } }], 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' }, @@ -62,8 +62,8 @@ var validBidRequestsMulti = [ bidRequestsCount: 1, bidder: 'ozone', bidderRequestId: '1c1586b27a1b5c0', - crumbs: {pubcid: '203a0692-f728-4856-87f6-9a25a6b63715'}, - params: { publisherId: '9876abcd12-3', customData: [{'settings': {}, 'targeting': {'gender': 'bart', 'age': 'low'}}], 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 } } ] }, + crumbs: { pubcid: '203a0692-f728-4856-87f6-9a25a6b63715' }, + params: { publisherId: '9876abcd12-3', customData: [{ 'settings': {}, 'targeting': { 'gender': 'bart', 'age': 'low' } }], 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' } @@ -769,8 +769,8 @@ var validBidRequestsWithUserIdData = [ bidRequestsCount: 1, bidder: 'ozone', bidderRequestId: '1c1586b27a1b5c8', - crumbs: {pubcid: '203a0692-f728-4856-87f6-9a25a6b63715'}, - params: { publisherId: '9876abcd12-3', customData: [{'settings': {}, 'targeting': {'gender': 'bart', 'age': 'low'}}], 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 } } ] }, + crumbs: { pubcid: '203a0692-f728-4856-87f6-9a25a6b63715' }, + params: { publisherId: '9876abcd12-3', customData: [{ 'settings': {}, 'targeting': { 'gender': 'bart', 'age': 'low' } }], 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', userId: { @@ -779,9 +779,9 @@ var validBidRequestsWithUserIdData = [ 'id5id': { uid: '1111', ext: { linkType: 2, abTestingControlGroup: false } }, 'criteoId': '1111criteoId', 'idl_env': 'liverampId', - 'lipb': {'lipbid': 'lipbidId123'}, - 'parrableId': {'eid': '01.5678.parrableid'}, - 'sharedid': {'id': '01EAJWWNEPN3CYMM5N8M5VXY22', 'third': '01EAJWWNEPN3CYMM5N8M5VXY22'} + 'lipb': { 'lipbid': 'lipbidId123' }, + 'parrableId': { 'eid': '01.5678.parrableid' }, + 'sharedid': { 'id': '01EAJWWNEPN3CYMM5N8M5VXY22', 'third': '01EAJWWNEPN3CYMM5N8M5VXY22' } }, userIdAsEids: [ { @@ -827,14 +827,14 @@ var validBidRequestsWithUserIdData = [ { 'source': 'lipb', 'uids': [{ - 'id': {'lipbid': 'lipbidId123'}, + 'id': { 'lipbid': 'lipbidId123' }, 'atype': 1, }] }, { 'source': 'parrableId', 'uids': [{ - 'id': {'eid': '01.5678.parrableid'}, + 'id': { 'eid': '01.5678.parrableid' }, 'atype': 1, }] } @@ -849,7 +849,7 @@ var validBidRequestsMinimal = [ 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 } } ] }, + 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' } @@ -862,8 +862,8 @@ var validBidRequestsNoSizes = [ bidRequestsCount: 1, bidder: 'ozone', bidderRequestId: '1c1586b27a1b5c8', - crumbs: {pubcid: '203a0692-f728-4856-87f6-9a25a6b63715'}, - params: { publisherId: '9876abcd12-3', customData: [{'settings': {}, 'targeting': {'gender': 'bart', 'age': 'low'}}], 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 } } ] }, + crumbs: { pubcid: '203a0692-f728-4856-87f6-9a25a6b63715' }, + params: { publisherId: '9876abcd12-3', customData: [{ 'settings': {}, 'targeting': { 'gender': 'bart', 'age': 'low' } }], 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' } ]; @@ -875,9 +875,9 @@ var validBidRequestsWithBannerMediaType = [ bidRequestsCount: 1, bidder: 'ozone', bidderRequestId: '1c1586b27a1b5c8', - crumbs: {pubcid: '203a0692-f728-4856-87f6-9a25a6b63715'}, - params: { publisherId: '9876abcd12-3', customData: [{'settings': {}, 'targeting': {'gender': 'bart', 'age': 'low'}}], 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]]}}, + crumbs: { pubcid: '203a0692-f728-4856-87f6-9a25a6b63715' }, + params: { publisherId: '9876abcd12-3', customData: [{ 'settings': {}, 'targeting': { 'gender': 'bart', 'age': 'low' } }], 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' } ]; @@ -889,9 +889,9 @@ var validBidRequestsWithNonBannerMediaTypesAndValidOutstreamVideo = [ bidRequestsCount: 1, bidder: 'ozone', bidderRequestId: '1c1586b27a1b5c8', - crumbs: {pubcid: '203a0692-f728-4856-87f6-9a25a6b63715'}, - params: { publisherId: '9876abcd12-3', customData: [{'settings': {}, 'targeting': {'gender': 'bart', 'age': 'low'}}], 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], playerSize: [640, 480]}, native: {info: 'dummy data'}}, + crumbs: { pubcid: '203a0692-f728-4856-87f6-9a25a6b63715' }, + params: { publisherId: '9876abcd12-3', customData: [{ 'settings': {}, 'targeting': { 'gender': 'bart', 'age': 'low' } }], 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], playerSize: [640, 480] }, native: { info: 'dummy data' } }, transactionId: '2e63c0ed-b10c-4008-aed5-84582cecfe87' } ]; @@ -1091,8 +1091,8 @@ var validBidderRequest = { bidRequestsCount: 1, bidder: 'ozone', bidderRequestId: '1c1586b27a1b5c8', - crumbs: {pubcid: '203a0692-f728-4856-87f6-9a25a6b63715'}, - params: { publisherId: '9876abcd12-3', customData: [{'settings': {}, 'targeting': {'gender': 'bart', 'age': 'low'}}], 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' } ] }, + crumbs: { pubcid: '203a0692-f728-4856-87f6-9a25a6b63715' }, + params: { publisherId: '9876abcd12-3', customData: [{ 'settings': {}, 'targeting': { 'gender': 'bart', 'age': 'low' } }], 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' }], @@ -1112,8 +1112,8 @@ var validBidderRequestWithCookieDeprecation = { bidRequestsCount: 1, bidder: 'ozone', bidderRequestId: '1c1586b27a1b5c8', - crumbs: {pubcid: '203a0692-f728-4856-87f6-9a25a6b63715'}, - params: { publisherId: '9876abcd12-3', customData: [{'settings': {}, 'targeting': {'gender': 'bart', 'age': 'low'}}], 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' } ] }, + crumbs: { pubcid: '203a0692-f728-4856-87f6-9a25a6b63715' }, + params: { publisherId: '9876abcd12-3', customData: [{ 'settings': {}, 'targeting': { 'gender': 'bart', 'age': 'low' } }], 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' }], @@ -1172,8 +1172,8 @@ var bidderRequestWithFullGdpr = { bidRequestsCount: 1, bidder: 'ozone', bidderRequestId: '1c1586b27a1b5c8', - crumbs: {pubcid: '203a0692-f728-4856-87f6-9a25a6b63715'}, - params: { publisherId: '9876abcd12-3', customData: [{'settings': {}, 'targeting': {'gender': 'bart', 'age': 'low'}}], 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' } ] }, + crumbs: { pubcid: '203a0692-f728-4856-87f6-9a25a6b63715' }, + params: { publisherId: '9876abcd12-3', customData: [{ 'settings': {}, 'targeting': { 'gender': 'bart', 'age': 'low' } }], 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' }], @@ -1258,16 +1258,16 @@ var bidderRequestWithPartialGdpr = { bidRequestsCount: 1, bidder: 'ozone', bidderRequestId: '1c1586b27a1b5c8', - crumbs: {pubcid: '203a0692-f728-4856-87f6-9a25a6b63715'}, + crumbs: { pubcid: '203a0692-f728-4856-87f6-9a25a6b63715' }, params: { publisherId: '9876abcd12-3', - customData: [{'settings': {}, 'targeting': {'gender': 'bart', 'age': 'low'}}], + customData: [{ 'settings': {}, 'targeting': { 'gender': 'bart', 'age': 'low' } }], 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}]}, + banner: { topframe: 1, w: 300, h: 250, format: [{ w: 300, h: 250 }, { w: 300, h: 600 }] }, id: '2899ec066a91ff8', secure: 1, tagid: 'undefined' @@ -1412,7 +1412,7 @@ var validResponse2Bids = { } } } - } ], + }], 'seat': 'appnexus' } ], @@ -1498,7 +1498,7 @@ var validResponse2BidsSameAdunit = { } } } - } ], + }], 'seat': 'ozappnexus' } ], @@ -2212,37 +2212,37 @@ describe('ozone Adapter', function () { it('should check deepSet means "unconditionally set element to this value, optionally building the path" and Object.assign will blend the keys together, neither will deeply merge nested objects successfully.', function () { let xx = {}; let yy = { - 'publisher': {'id': 123}, + 'publisher': { 'id': 123 }, 'page': 567, 'id': 900 }; deepSetValue(xx, 'site', yy); expect(xx.site).to.have.all.keys(['publisher', 'page', 'id']); - xx = {site: {'name': 'test1'}}; + xx = { site: { 'name': 'test1' } }; deepSetValue(xx, 'site', yy); - expect(xx.site).to.have.all.keys([ 'publisher', 'page', 'id']); - xx = {site: {'name': 'test1'}}; + expect(xx.site).to.have.all.keys(['publisher', 'page', 'id']); + xx = { site: { 'name': 'test1' } }; Object.assign(xx.site, yy); - expect(xx.site).to.have.all.keys([ 'publisher', 'page', 'id', 'name']); - xx = {site: {'name': 'test1'}}; + expect(xx.site).to.have.all.keys(['publisher', 'page', 'id', 'name']); + xx = { site: { 'name': 'test1' } }; deepSetValue(xx, 'site', yy); - expect(xx.site).to.have.all.keys([ 'publisher', 'page', 'id']); - xx = {regs: {dsa: {'val1:': 1}}}; - deepSetValue(xx, 'regs.ext.usprivacy', {'usp_key': 'usp_value'}); + expect(xx.site).to.have.all.keys(['publisher', 'page', 'id']); + xx = { regs: { dsa: { 'val1:': 1 } } }; + deepSetValue(xx, 'regs.ext.usprivacy', { 'usp_key': 'usp_value' }); expect(xx.regs).to.have.all.keys(['dsa', 'ext']); - xx = {regs: {dsa: {'val1:': 1}}}; - deepSetValue(xx.regs, 'ext.usprivacy', {'usp_key': 'usp_value'}); + xx = { regs: { dsa: { 'val1:': 1 } } }; + deepSetValue(xx.regs, 'ext.usprivacy', { 'usp_key': 'usp_value' }); expect(xx.regs).to.have.all.keys(['dsa', 'ext']); - let ozoneRequest = {user: { ext: {'data': 'some data ... '}, keywords: "a,b,c"}}; - Object.assign(ozoneRequest, {user: {ext: {eids: ['some eid', 'another one']}}}); + let ozoneRequest = { user: { ext: { 'data': 'some data ... ' }, keywords: "a,b,c" } }; + Object.assign(ozoneRequest, { user: { ext: { eids: ['some eid', 'another one'] } } }); expect(ozoneRequest.user.ext).to.have.all.keys(['eids']); }); it('should verify that mergeDeep does what I want it to do', function() { - let ozoneRequest = {user: { ext: {'data': 'some data ... '}, keywords: "a,b,c"}}; - ozoneRequest = mergeDeep(ozoneRequest, {user: {ext: {eids: ['some eid', 'another one']}}}); + let ozoneRequest = { user: { ext: { 'data': 'some data ... ' }, keywords: "a,b,c" } }; + ozoneRequest = mergeDeep(ozoneRequest, { user: { ext: { eids: ['some eid', 'another one'] } } }); expect(ozoneRequest.user.ext).to.have.all.keys(['eids', 'data']); - ozoneRequest = {user: { ext: {'data': 'some data ... '}, keywords: "a,b,c"}}; - mergeDeep(ozoneRequest, {user: {ext: {eids: ['some eid', 'another one']}}}); + ozoneRequest = { user: { ext: { 'data': 'some data ... ' }, keywords: "a,b,c" } }; + mergeDeep(ozoneRequest, { user: { ext: { eids: ['some eid', 'another one'] } } }); expect(ozoneRequest.user.ext).to.have.all.keys(['eids', 'data']); }); }); @@ -2264,7 +2264,7 @@ describe('ozone Adapter', function () { placementId: '1310000099', publisherId: '9876abcd12-3', siteId: '1234567890', - customData: [{'settings': {}, 'targeting': {'gender': 'bart', 'age': 'low'}}] + customData: [{ 'settings': {}, 'targeting': { 'gender': 'bart', 'age': 'low' } }] }, siteId: 1234567890 } @@ -2446,7 +2446,7 @@ describe('ozone Adapter', function () { 'placementId': '1234567890', 'publisherId': '9876abcd12-3', 'siteId': '1234567890', - 'customData': {'gender': 'bart', 'age': 'low'} + 'customData': { 'gender': 'bart', 'age': 'low' } } }; it('should not validate customData being an object, not an array', function () { @@ -2469,7 +2469,7 @@ describe('ozone Adapter', function () { params: { 'placementId': '1234567890', 'publisherId': '9876abcd12-3', - 'customData': [{'settings': {}, 'xx': {'gender': 'bart', 'age': 'low'}}], + 'customData': [{ 'settings': {}, 'xx': { 'gender': 'bart', 'age': 'low' } }], siteId: '1234567890' } }; @@ -2481,7 +2481,7 @@ describe('ozone Adapter', function () { params: { 'placementId': '1234567890', 'publisherId': '9876abcd12-3', - 'customData': [{'settings': {}, 'targeting': 'this should be an object'}], + 'customData': [{ 'settings': {}, 'targeting': 'this should be an object' }], siteId: '1234567890' } }; @@ -2508,8 +2508,7 @@ describe('ozone Adapter', function () { siteId: '1234567890' }, mediaTypes: { - video: { - mimes: ['video/mp4']} + video: { mimes: ['video/mp4'] } } }; it('should not validate video without context attribute', function () { @@ -2525,7 +2524,8 @@ describe('ozone Adapter', function () { mediaTypes: { video: { mimes: ['video/mp4'], - 'context': 'outstream'}, + 'context': 'outstream' + }, } }; it('should validate video outstream being sent', function () { @@ -2573,7 +2573,7 @@ describe('ozone Adapter', function () { }); it('ignores ozoneData in & after version 2.1.1', function () { const validBidRequestsWithOzoneData = JSON.parse(JSON.stringify(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'}; + 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(validBidRequestsWithOzoneData, validBidderRequest); expect(request.data).to.be.a('string'); var data = JSON.parse(request.data); @@ -2603,11 +2603,11 @@ describe('ozone Adapter', function () { 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}}); + 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']); - config.setConfig({'ozone': {'singleRequest': true}}); + config.setConfig({ 'ozone': { 'singleRequest': true } }); }); it('should add gdpr consent information to the request when ozone is true', function () { const consentString = 'BOcocyaOcocyaAfEYDENCD-AAAAjx7_______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-33d4-_1vf99yfm1-7ftr3tp_87ues2_Xur__59__3z3_NphLgA=='; @@ -2618,8 +2618,8 @@ describe('ozone Adapter', function () { vendorData: { metadata: consentString, gdprApplies: true, - vendorConsents: {524: true}, - purposeConsents: {1: true, 2: true, 3: true, 4: true, 5: true} + vendorConsents: { 524: true }, + purposeConsents: { 1: true, 2: true, 3: true, 4: true, 5: true } } } const request = spec.buildRequests(validBidRequestsNoSizes, bidderRequest); @@ -2653,7 +2653,7 @@ describe('ozone Adapter', function () { metadata: consentString, gdprApplies: true, vendorConsents: {}, - purposeConsents: {1: true, 2: true, 3: true, 4: true, 5: true} + purposeConsents: { 1: true, 2: true, 3: true, 4: true, 5: true } } }; const request = spec.buildRequests(validBidRequestsNoSizes, bidderRequest); @@ -2664,7 +2664,7 @@ describe('ozone Adapter', function () { const gppString = 'gppConsentString'; const gppSections = [7, 8, 9]; const bidderRequest = JSON.parse(JSON.stringify(validBidderRequest)); - bidderRequest.ortb2 = {regs: {gpp: gppString, gpp_sid: gppSections}}; + bidderRequest.ortb2 = { regs: { gpp: gppString, gpp_sid: gppSections } }; const request = spec.buildRequests(validBidRequestsNoSizes, bidderRequest); const payload = JSON.parse(request.data); expect(payload.regs.ext.gpp).to.equal(gppString); @@ -2684,19 +2684,19 @@ describe('ozone Adapter', function () { vendorData: { metadata: consentString, gdprApplies: true, - vendorConsents: {524: true}, - purposeConsents: {1: true, 2: true, 3: true, 4: true, 5: true} + vendorConsents: { 524: true }, + purposeConsents: { 1: true, 2: true, 3: true, 4: true, 5: true } } }; const bidRequests = JSON.parse(JSON.stringify(validBidRequests)); bidRequests[0]['userId'] = { - 'digitrustid': {data: {id: 'DTID', keyv: 4, privacy: {optout: false}, producer: 'ABC', version: 2}}, + 'digitrustid': { data: { id: 'DTID', keyv: 4, privacy: { optout: false }, producer: 'ABC', version: 2 } }, 'id5id': { uid: '1111', ext: { linkType: 2, abTestingControlGroup: false } }, 'idl_env': '3333', 'parrableid': 'eidVersion.encryptionKeyReference.encryptedValue', 'pubcid': '5555', 'tdid': '6666', - 'sharedid': {'id': '01EAJWWNEPN3CYMM5N8M5VXY22', 'third': '01EAJWWNEPN3CYMM5N8M5VXY22'} + 'sharedid': { 'id': '01EAJWWNEPN3CYMM5N8M5VXY22', 'third': '01EAJWWNEPN3CYMM5N8M5VXY22' } }; bidRequests[0]['userIdAsEids'] = validBidRequestsWithUserIdData[0]['userIdAsEids']; const request = spec.buildRequests(bidRequests, bidderRequest); @@ -2733,32 +2733,32 @@ describe('ozone Adapter', function () { }); it('replaces the auction url for a config override', function () { const fakeOrigin = 'http://sometestendpoint'; - config.setConfig({'ozone': {'endpointOverride': {'origin': fakeOrigin}}}); + config.setConfig({ 'ozone': { 'endpointOverride': { 'origin': fakeOrigin } } }); const request = spec.buildRequests(validBidRequests, validBidderRequest); expect(request.url).to.equal(fakeOrigin + '/openrtb2/auction'); expect(request.method).to.equal('POST'); const data = JSON.parse(request.data); expect(data.ext.ozone.origin).to.equal(fakeOrigin); - config.setConfig({'ozone': {'kvpPrefix': null, 'endpointOverride': null}}); + config.setConfig({ 'ozone': { 'kvpPrefix': null, 'endpointOverride': null } }); }); it('replaces the FULL auction url for a config override', function () { const fakeurl = 'http://sometestendpoint/myfullurl'; - config.setConfig({'ozone': {'endpointOverride': {'auctionUrl': fakeurl}}}); + config.setConfig({ 'ozone': { 'endpointOverride': { 'auctionUrl': fakeurl } } }); const request = spec.buildRequests(validBidRequests, validBidderRequest); expect(request.url).to.equal(fakeurl); expect(request.method).to.equal('POST'); const data = JSON.parse(request.data); expect(data.ext.ozone.origin).to.equal(fakeurl); - config.setConfig({'ozone': {'kvpPrefix': null, 'endpointOverride': null}}); + config.setConfig({ 'ozone': { 'kvpPrefix': null, 'endpointOverride': null } }); }); it('replaces the renderer url for a config override', function () { const fakeUrl = 'http://renderer.com'; - config.setConfig({'ozone': {'endpointOverride': {'rendererUrl': fakeUrl}}}); + config.setConfig({ 'ozone': { 'endpointOverride': { 'rendererUrl': fakeUrl } } }); const result = spec.interpretResponse(getCleanValidVideoResponse(), validBidderRequest1OutstreamVideo2020); const bid = result[0]; expect(bid.renderer).to.be.an.instanceOf(Renderer); expect(bid.renderer.url).to.equal(fakeUrl); - config.setConfig({'ozone': {'kvpPrefix': null, 'endpointOverride': null}}); + config.setConfig({ 'ozone': { 'kvpPrefix': null, 'endpointOverride': null } }); }); it('should create a meta object on each bid returned', function () { const request = spec.buildRequests(validBidRequests, validBidderRequest); @@ -2769,7 +2769,7 @@ describe('ozone Adapter', function () { it('should use oztestmode GET value if set', function() { var specMock = utils.deepClone(spec); specMock.getGetParametersAsObject = function() { - return {'oztestmode': 'mytestvalue_123'}; + return { 'oztestmode': 'mytestvalue_123' }; }; const request = specMock.buildRequests(validBidRequests, validBidderRequest); const data = JSON.parse(request.data); @@ -2779,7 +2779,7 @@ describe('ozone Adapter', function () { it('should ignore these GET params if present (removed 202410): ozf, ozpf, ozrp, ozip', function() { var specMock = utils.deepClone(spec); specMock.getGetParametersAsObject = function() { - return {ozf: '1', ozpf: '10', ozrp: '2', ozip: '123'}; + return { ozf: '1', ozpf: '10', ozrp: '2', ozip: '123' }; }; const request = specMock.buildRequests(validBidRequests, validBidderRequest); const data = JSON.parse(request.data); @@ -2788,7 +2788,7 @@ describe('ozone Adapter', function () { it('should use oztestmode GET value if set, even if there is no customdata in config', function() { var specMock = utils.deepClone(spec); specMock.getGetParametersAsObject = function() { - return {'oztestmode': 'mytestvalue_123'}; + return { 'oztestmode': 'mytestvalue_123' }; }; const request = specMock.buildRequests(validBidRequestsMinimal, validBidderRequest); const data = JSON.parse(request.data); @@ -2804,7 +2804,7 @@ describe('ozone Adapter', function () { expect(data.imp[0].ext.gpid).to.equal('/22037345/projectozone'); }); it('should batch into 10s if config is set to true', function () { - config.setConfig({ozone: {'batchRequests': true}}); + config.setConfig({ ozone: { 'batchRequests': true } }); var specMock = utils.deepClone(spec); const arrReq = []; for (let i = 0; i < 25; i++) { @@ -2817,7 +2817,7 @@ describe('ozone Adapter', function () { config.resetConfig(); }); it('should batch into 7 if config is set to 7', function () { - config.setConfig({ozone: {'batchRequests': 7}}); + config.setConfig({ ozone: { 'batchRequests': 7 } }); var specMock = utils.deepClone(spec); const arrReq = []; for (let i = 0; i < 25; i++) { @@ -2830,7 +2830,7 @@ describe('ozone Adapter', function () { config.resetConfig(); }); it('should not batch if config is set to false and singleRequest is true', function () { - config.setConfig({ozone: {'batchRequests': false, 'singleRequest': true}}); + config.setConfig({ ozone: { 'batchRequests': false, 'singleRequest': true } }); var specMock = utils.deepClone(spec); const arrReq = []; for (let i = 0; i < 15; i++) { @@ -2843,7 +2843,7 @@ describe('ozone Adapter', function () { config.resetConfig(); }); it('should not batch if config is set to invalid value -10 and singleRequest is true', function () { - config.setConfig({ozone: {'batchRequests': -10, 'singleRequest': true}}); + config.setConfig({ ozone: { 'batchRequests': -10, 'singleRequest': true } }); var specMock = utils.deepClone(spec); const arrReq = []; for (let i = 0; i < 15; i++) { @@ -2858,7 +2858,7 @@ describe('ozone Adapter', function () { it('should use GET values for batchRequests if found', function() { var specMock = utils.deepClone(spec); specMock.getGetParametersAsObject = function() { - return {'batchRequests': '5'}; + return { 'batchRequests': '5' }; }; const arrReq = []; for (let i = 0; i < 25; i++) { @@ -2870,25 +2870,25 @@ describe('ozone Adapter', function () { expect(request.length).to.equal(5); specMock = utils.deepClone(spec); specMock.getGetParametersAsObject = function() { - return {'batchRequests': '10'}; + return { 'batchRequests': '10' }; }; request = specMock.buildRequests(arrReq, validBidderRequest); expect(request.length).to.equal(3); specMock = utils.deepClone(spec); specMock.getGetParametersAsObject = function() { - return {'batchRequests': true}; + return { 'batchRequests': true }; }; request = specMock.buildRequests(arrReq, validBidderRequest); expect(request.method).to.equal('POST'); specMock = utils.deepClone(spec); specMock.getGetParametersAsObject = function() { - return {'batchRequests': 'true'}; + return { 'batchRequests': 'true' }; }; request = specMock.buildRequests(arrReq, validBidderRequest); expect(request.method).to.equal('POST'); specMock = utils.deepClone(spec); specMock.getGetParametersAsObject = function() { - return {'batchRequests': -5}; + return { 'batchRequests': -5 }; }; request = specMock.buildRequests(arrReq, validBidderRequest); expect(request.method).to.equal('POST'); @@ -2896,7 +2896,7 @@ describe('ozone Adapter', function () { it('should use a valid ozstoredrequest GET value if set to override the placementId values, and set oz_rw if we find it', function() { var specMock = utils.deepClone(spec); specMock.getGetParametersAsObject = function() { - return {'ozstoredrequest': '1122334455'}; + return { 'ozstoredrequest': '1122334455' }; }; const request = specMock.buildRequests(validBidRequestsMinimal, validBidderRequest); const data = JSON.parse(request.data); @@ -2906,7 +2906,7 @@ describe('ozone Adapter', function () { it('should NOT use an invalid ozstoredrequest GET value if set to override the placementId values, and set oz_rw to 0', function() { var specMock = utils.deepClone(spec); specMock.getGetParametersAsObject = function() { - return {'ozstoredrequest': 'BADVAL'}; + return { 'ozstoredrequest': 'BADVAL' }; }; const request = specMock.buildRequests(validBidRequestsMinimal, validBidderRequest); const data = JSON.parse(request.data); @@ -2914,7 +2914,7 @@ describe('ozone Adapter', function () { expect(data.imp[0].ext.prebid.storedrequest.id).to.equal('1310000099'); }); it('should pick up the config value of coppa & set it in the request', function () { - config.setConfig({'coppa': true}); + config.setConfig({ 'coppa': true }); const request = spec.buildRequests(validBidRequestsNoSizes, validBidderRequest); const payload = JSON.parse(request.data); expect(payload.regs).to.include.keys('coppa'); @@ -2922,21 +2922,21 @@ describe('ozone Adapter', function () { config.resetConfig(); }); it('should pick up the config value of coppa & only set it in the request if its true', function () { - config.setConfig({'coppa': false}); + config.setConfig({ 'coppa': false }); const request = spec.buildRequests(validBidRequestsNoSizes, validBidderRequest); const payload = JSON.parse(request.data); expect(utils.deepAccess(payload, 'regs.coppa')).to.be.undefined; config.resetConfig(); }); it('should handle oz_omp_floor correctly', function () { - config.setConfig({'ozone': {'oz_omp_floor': 1.56}}); + config.setConfig({ 'ozone': { 'oz_omp_floor': 1.56 } }); const request = spec.buildRequests(validBidRequestsNoSizes, validBidderRequest); const payload = JSON.parse(request.data); expect(utils.deepAccess(payload, 'ext.ozone.oz_omp_floor')).to.equal(1.56); config.resetConfig(); }); it('should ignore invalid oz_omp_floor values', function () { - config.setConfig({'ozone': {'oz_omp_floor': '1.56'}}); + config.setConfig({ 'ozone': { 'oz_omp_floor': '1.56' } }); const request = spec.buildRequests(validBidRequestsNoSizes, validBidderRequest); const payload = JSON.parse(request.data); expect(utils.deepAccess(payload, 'ext.ozone.oz_omp_floor')).to.be.undefined; @@ -2973,13 +2973,13 @@ describe('ozone Adapter', function () { expect(utils.deepAccess(payload2, 'ext.ozone.pv')).to.equal(utils.deepAccess(payload, 'ext.ozone.pv')); }); it('should indicate that the whitelist was used when it contains valid data', function () { - config.setConfig({'ozone': {'oz_whitelist_adserver_keys': ['oz_ozappnexus_pb', 'oz_ozappnexus_imp_id']}}); + config.setConfig({ 'ozone': { 'oz_whitelist_adserver_keys': ['oz_ozappnexus_pb', 'oz_ozappnexus_imp_id'] } }); const request = spec.buildRequests(validBidRequests, validBidderRequest); const payload = JSON.parse(request.data); expect(payload.ext.ozone.oz_kvp_rw).to.equal(1); }); it('should indicate that the whitelist was not used when it contains no data', function () { - config.setConfig({'ozone': {'oz_whitelist_adserver_keys': []}}); + config.setConfig({ 'ozone': { 'oz_whitelist_adserver_keys': [] } }); const request = spec.buildRequests(validBidRequests, validBidderRequest); const payload = JSON.parse(request.data); expect(payload.ext.ozone.oz_kvp_rw).to.equal(0); @@ -3068,7 +3068,7 @@ describe('ozone Adapter', function () { for (let i = 0; i < keys.length; i++) { expect(allowed).to.include(keys[i]); } - expect(payload.imp[0].video.ext).to.include({'context': 'outstream'}); + expect(payload.imp[0].video.ext).to.include({ 'context': 'outstream' }); }); it('should handle standard floor config correctly', function () { config.setConfig({ @@ -3090,7 +3090,7 @@ describe('ozone Adapter', function () { } }); const localBidRequest = JSON.parse(JSON.stringify(validBidRequestsWithBannerMediaType)); - localBidRequest[0].getFloor = function(x) { return {'currency': 'USD', 'floor': 0.8} }; + localBidRequest[0].getFloor = function(x) { return { 'currency': 'USD', 'floor': 0.8 } }; const request = spec.buildRequests(localBidRequest, validBidderRequest); const payload = JSON.parse(request.data); expect(utils.deepAccess(payload, 'imp.0.floor.banner.currency')).to.equal('USD'); @@ -3154,18 +3154,9 @@ describe('ozone Adapter', function () { const payload = JSON.parse(request.data); expect(payload.ext.ozone.cookieDeprecationLabel).to.equal('none'); }); - it('should handle fledge requests', function () { - const bidderRequest = JSON.parse(JSON.stringify(validBidderRequest)); - const bidRequests = JSON.parse(JSON.stringify(validBidRequests)); - deepSetValue(bidRequests[0], 'ortb2Imp.ext.ae', 1); - bidderRequest.fledgeEnabled = true; - const request = spec.buildRequests(bidRequests, bidderRequest); - const payload = JSON.parse(request.data); - expect(payload.imp[0].ext.ae).to.equal(1); - }); it('Single request: should use ortb auction ID & transaction ID values if set (this will be the case when publisher opts in with config)', function() { var specMock = utils.deepClone(spec); - config.setConfig({'ozone': {'singleRequest': true}}); + config.setConfig({ 'ozone': { 'singleRequest': true } }); const request = specMock.buildRequests(validBidRequestsWithAuctionIdTransactionId, validBidderRequest); expect(request).to.be.an('Object'); const payload = JSON.parse(request.data); @@ -3176,7 +3167,7 @@ describe('ozone Adapter', function () { }); it('non-Single request: should use ortb auction ID & transaction ID values if set (this will be the case when publisher opts in with config)', function() { var specMock = utils.deepClone(spec); - config.setConfig({'ozone': {'singleRequest': false}}); + config.setConfig({ 'ozone': { 'singleRequest': false } }); const request = specMock.buildRequests(validBidRequestsWithAuctionIdTransactionId, validBidderRequest); expect(request).to.be.an('Array'); const payload = JSON.parse(request[0].data); @@ -3187,7 +3178,7 @@ describe('ozone Adapter', function () { }); it('Batch request (flat array of single requests): should use ortb auction ID & transaction ID values if set (this will be the case when publisher opts in with config)', function() { var specMock = utils.deepClone(spec); - config.setConfig({'ozone': {'batchRequests': 3}}); + config.setConfig({ 'ozone': { 'batchRequests': 3 } }); const request = specMock.buildRequests(valid6BidRequestsWithAuctionIdTransactionId, validBidderRequest); expect(request).to.be.an('Array'); expect(request).to.have.lengthOf(2); @@ -3211,7 +3202,7 @@ describe('ozone Adapter', function () { model: 'iPhone 12 Pro Max', os: 'iOS', osv: '17.4', - ext: {fiftyonedegrees_deviceId: '17595-133085-133468-18092'}, + ext: { fiftyonedegrees_deviceId: '17595-133085-133468-18092' }, }, }; const request = spec.buildRequests(validBidRequests, bidderRequest); @@ -3238,7 +3229,7 @@ describe('ozone Adapter', function () { }); it('should build bid array with gdpr', function () { const validBR = JSON.parse(JSON.stringify(bidderRequestWithFullGdpr)); - validBR.gdprConsent = {'gdprApplies': 1, 'consentString': 'This is the gdpr consent string'}; + validBR.gdprConsent = { 'gdprApplies': 1, 'consentString': 'This is the gdpr consent string' }; const request = spec.buildRequests(validBidRequests, validBR); const result = spec.interpretResponse(validResponse, request); expect(result.length).to.equal(1); @@ -3253,7 +3244,7 @@ describe('ozone Adapter', function () { }); it('should build bid array with only partial gdpr', function () { var validBidderRequestWithGdpr = bidderRequestWithPartialGdpr.bidderRequest; - validBidderRequestWithGdpr.gdprConsent = {'gdprApplies': 1, 'consentString': 'This is the gdpr consent string'}; + validBidderRequestWithGdpr.gdprConsent = { 'gdprApplies': 1, 'consentString': 'This is the gdpr consent string' }; const request = spec.buildRequests(validBidRequests, validBidderRequestWithGdpr); const payload = JSON.parse(request.data); expect(payload.user.ext.consent).to.be.a('string'); @@ -3264,7 +3255,7 @@ describe('ozone Adapter', function () { expect(result).to.be.empty; }); it('should fail ok if seatbid is not an array', function () { - const result = spec.interpretResponse({'body': {'seatbid': 'nothing_here'}}, {}); + const result = spec.interpretResponse({ 'body': { 'seatbid': 'nothing_here' } }, {}); expect(result).to.be.an('array'); expect(result).to.be.empty; }); @@ -3293,13 +3284,13 @@ describe('ozone Adapter', function () { expect(result[0].ttl).to.equal(300); }); it('should handle oz_omp_floor_dollars correctly, inserting 1 as necessary', function () { - config.setConfig({'ozone': {'oz_omp_floor': 0.01}}); + config.setConfig({ 'ozone': { 'oz_omp_floor': 0.01 } }); const request = spec.buildRequests(validBidRequests, validBidderRequest); const result = spec.interpretResponse(validResponse, request); expect(utils.deepAccess(result[0].adserverTargeting, 'oz_appnexus_omp')).to.equal('1'); }); it('should handle oz_omp_floor_dollars correctly, inserting 0 as necessary', function () { - config.setConfig({'ozone': {'oz_omp_floor': 2.50}}); + config.setConfig({ 'ozone': { 'oz_omp_floor': 2.50 } }); const request = spec.buildRequests(validBidRequests, validBidderRequest); const result = spec.interpretResponse(validResponse, request); expect(utils.deepAccess(result[0].adserverTargeting, 'oz_appnexus_omp')).to.equal('0'); @@ -3312,7 +3303,7 @@ describe('ozone Adapter', function () { it('should handle ext.bidder.ozone.floor correctly, setting flr & rid as necessary', function () { const request = spec.buildRequests(validBidRequests, validBidderRequest); const vres = JSON.parse(JSON.stringify(validResponse)); - vres.body.seatbid[0].bid[0].ext.bidder.ozone = {floor: 1, ruleId: 'ZjbsYE1q'}; + vres.body.seatbid[0].bid[0].ext.bidder.ozone = { floor: 1, ruleId: 'ZjbsYE1q' }; const result = spec.interpretResponse(vres, request); expect(utils.deepAccess(result[0].adserverTargeting, 'oz_appnexus_flr')).to.equal(1); expect(utils.deepAccess(result[0].adserverTargeting, 'oz_appnexus_rid')).to.equal('ZjbsYE1q'); @@ -3320,7 +3311,7 @@ describe('ozone Adapter', function () { it('should handle ext.bidder.ozone.floor correctly, inserting 0 as necessary', function () { const request = spec.buildRequests(validBidRequests, validBidderRequest); const vres = JSON.parse(JSON.stringify(validResponse)); - vres.body.seatbid[0].bid[0].ext.bidder.ozone = {floor: 0, ruleId: 'ZjbXXE1q'}; + vres.body.seatbid[0].bid[0].ext.bidder.ozone = { floor: 0, ruleId: 'ZjbXXE1q' }; const result = spec.interpretResponse(vres, request); expect(utils.deepAccess(result[0].adserverTargeting, 'oz_appnexus_flr')).to.equal(0); expect(utils.deepAccess(result[0].adserverTargeting, 'oz_appnexus_rid')).to.equal('ZjbXXE1q'); @@ -3341,21 +3332,21 @@ describe('ozone Adapter', function () { expect(utils.deepAccess(result[0].adserverTargeting, 'oz_appnexus_rid', null)).to.equal(null); }); it('should handle a valid whitelist, removing items not on the list & leaving others', function () { - config.setConfig({'ozone': {'oz_whitelist_adserver_keys': ['oz_appnexus_crid', 'oz_appnexus_adId']}}); + config.setConfig({ 'ozone': { 'oz_whitelist_adserver_keys': ['oz_appnexus_crid', 'oz_appnexus_adId'] } }); const request = spec.buildRequests(validBidRequests, validBidderRequest); const result = spec.interpretResponse(validResponse, request); expect(utils.deepAccess(result[0].adserverTargeting, 'oz_appnexus_adv')).to.be.undefined; expect(utils.deepAccess(result[0].adserverTargeting, 'oz_appnexus_adId')).to.equal('2899ec066a91ff8-0-oz-0'); }); it('should ignore a whitelist if enhancedAdserverTargeting is false', function () { - config.setConfig({'ozone': {'oz_whitelist_adserver_keys': ['oz_appnexus_crid', 'oz_appnexus_imp_id'], 'enhancedAdserverTargeting': false}}); + config.setConfig({ 'ozone': { 'oz_whitelist_adserver_keys': ['oz_appnexus_crid', 'oz_appnexus_imp_id'], 'enhancedAdserverTargeting': false } }); const request = spec.buildRequests(validBidRequests, validBidderRequest); const result = spec.interpretResponse(validResponse, request); expect(utils.deepAccess(result[0].adserverTargeting, 'oz_appnexus_adv')).to.be.undefined; expect(utils.deepAccess(result[0].adserverTargeting, 'oz_appnexus_imp_id')).to.be.undefined; }); it('should correctly handle enhancedAdserverTargeting being false', function () { - config.setConfig({'ozone': {'enhancedAdserverTargeting': false}}); + config.setConfig({ 'ozone': { 'enhancedAdserverTargeting': false } }); const request = spec.buildRequests(validBidRequests, validBidderRequest); const result = spec.interpretResponse(validResponse, request); expect(utils.deepAccess(result[0].adserverTargeting, 'oz_appnexus_adv')).to.be.undefined; @@ -3364,7 +3355,7 @@ describe('ozone Adapter', function () { it('should add flr into ads request if floor exists in the auction response', function () { const request = spec.buildRequests(validBidRequestsMulti, validBidderRequest); const validres = JSON.parse(JSON.stringify(validResponse2Bids)); - validres.body.seatbid[0].bid[0].ext.bidder.ozone = {'floor': 1}; + validres.body.seatbid[0].bid[0].ext.bidder.ozone = { 'floor': 1 }; const result = spec.interpretResponse(validres, request); expect(utils.deepAccess(result[0].adserverTargeting, 'oz_appnexus_flr')).to.equal(1); expect(utils.deepAccess(result[1].adserverTargeting, 'oz_appnexus_flr', '')).to.equal(''); @@ -3372,7 +3363,7 @@ describe('ozone Adapter', function () { it('should add rid into ads request if ruleId exists in the auction response', function () { const request = spec.buildRequests(validBidRequestsMulti, validBidderRequest); const validres = JSON.parse(JSON.stringify(validResponse2Bids)); - validres.body.seatbid[0].bid[0].ext.bidder.ozone = {'ruleId': 123}; + validres.body.seatbid[0].bid[0].ext.bidder.ozone = { 'ruleId': 123 }; const result = spec.interpretResponse(validres, request); expect(utils.deepAccess(result[0].adserverTargeting, 'oz_appnexus_rid')).to.equal(123); expect(utils.deepAccess(result[1].adserverTargeting, 'oz_appnexus_rid', '')).to.equal(''); @@ -3424,20 +3415,6 @@ describe('ozone Adapter', function () { const bid = result[0]; expect(bid.mediaType).to.equal('video'); }); - it('should handle fledge response', function () { - const req = spec.buildRequests(validBidRequests, validBidderRequest); - const objResp = JSON.parse(JSON.stringify(validResponse)); - objResp.body.ext = {igi: [{ - 'impid': '1', - 'igb': [{ - 'origin': 'https://paapi.dsp.com', - 'pbs': '{"key": "value"}' - }] - }]}; - const result = spec.interpretResponse(objResp, req); - expect(result).to.be.an('object'); - expect(result.fledgeAuctionConfigs[0]['impid']).to.equal('1'); - }); it('should add labels in the adserver request if they are present in the auction response', function () { const request = spec.buildRequests(validBidRequestsMulti, validBidderRequest); const validres = JSON.parse(JSON.stringify(validResponse2Bids)); @@ -3464,11 +3441,11 @@ describe('ozone Adapter', function () { const validres = JSON.parse(JSON.stringify(validResponse2Bids)); validres.body.seatbid.push(JSON.parse(JSON.stringify(validres.body.seatbid[0]))); validres.body.seatbid[1].seat = 'marktest'; - validres.body.seatbid[1].bid[0].ext.bidder.prebid = {label: ['b1', 'b2', 'b3']}; + validres.body.seatbid[1].bid[0].ext.bidder.prebid = { label: ['b1', 'b2', 'b3'] }; validres.body.seatbid[1].bid[0].price = 10; validres.body.seatbid[1].bid[1].price = 0; - validres.body.seatbid[0].bid[0].ext.bidder.prebid = {label: ['bid1label1', 'bid1label2', 'bid1label3']}; - validres.body.seatbid[0].bid[1].ext.bidder.prebid = {label: ['bid2label']}; + validres.body.seatbid[0].bid[0].ext.bidder.prebid = { label: ['bid1label1', 'bid1label2', 'bid1label3'] }; + validres.body.seatbid[0].bid[1].ext.bidder.prebid = { label: ['bid2label'] }; const result = spec.interpretResponse(validres, request); expect(result.length).to.equal(4); expect(utils.deepAccess(result[0].adserverTargeting, 'oz_winner')).to.equal('marktest'); @@ -3481,7 +3458,7 @@ describe('ozone Adapter', function () { expect(utils.deepAccess(result[3].adserverTargeting, 'oz_labels')).to.equal('bid2label'); }); it('should not add labels in the adserver request if they are present in the auction response when config contains ozone.enhancedAdserverTargeting', function () { - config.setConfig({'ozone': {'enhancedAdserverTargeting': false}}); + config.setConfig({ 'ozone': { 'enhancedAdserverTargeting': false } }); const request = spec.buildRequests(validBidRequestsMulti, validBidderRequest); const validres = JSON.parse(JSON.stringify(validResponse2Bids)); validres.body.seatbid.push(JSON.parse(JSON.stringify(validres.body.seatbid[0]))); @@ -3515,7 +3492,7 @@ describe('ozone Adapter', function () { }); it('should append the various values if they exist', function() { spec.buildRequests(validBidRequests, validBidderRequest); - const result = spec.getUserSyncs({iframeEnabled: true}, 'good server response', gdpr1); + const result = spec.getUserSyncs({ iframeEnabled: true }, 'good server response', gdpr1); expect(result).to.be.an('array'); expect(result[0].url).to.include('publisherId=9876abcd12-3'); expect(result[0].url).to.include('siteId=1234567890'); @@ -3524,51 +3501,51 @@ describe('ozone Adapter', function () { }); it('should append ccpa (usp data)', function() { spec.buildRequests(validBidRequests, validBidderRequest); - const result = spec.getUserSyncs({iframeEnabled: true}, 'good server response', gdpr1, '1YYN'); + const result = spec.getUserSyncs({ iframeEnabled: true }, 'good server response', gdpr1, '1YYN'); expect(result).to.be.an('array'); expect(result[0].url).to.include('usp_consent=1YYN'); }); it('should use "" if no usp is sent to cookieSync', function() { spec.buildRequests(validBidRequests, validBidderRequest); - const result = spec.getUserSyncs({iframeEnabled: true}, 'good server response', gdpr1); + const result = spec.getUserSyncs({ iframeEnabled: true }, 'good server response', gdpr1); expect(result).to.be.an('array'); expect(result[0].url).to.include('usp_consent=&'); }); it('should add gpp if its present', function () { - const result = spec.getUserSyncs({iframeEnabled: true}, 'good server response', gdpr1, '1---', { gppString: 'gppStringHere', applicableSections: [7, 8, 9] }); + const result = spec.getUserSyncs({ iframeEnabled: true }, 'good server response', gdpr1, '1---', { gppString: 'gppStringHere', applicableSections: [7, 8, 9] }); expect(result[0].url).to.include('gpp=gppStringHere&gpp_sid=7,8,9'); }); }); describe('video object utils', function () { it('should find width & height from video object', function () { - const obj = {'playerSize': [640, 480], 'mimes': ['video/mp4'], 'context': 'outstream'}; + const 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 () { - const obj = {'playerSize': [], 'mimes': ['video/mp4'], 'context': 'outstream'}; + const 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 () { - const obj = {'mimes': ['video/mp4'], 'context': 'outstream'}; + const obj = { 'mimes': ['video/mp4'], 'context': 'outstream' }; const result = getWidthAndHeightFromVideoObject(obj); expect(result).to.be.null; }); it('should find null from bad video object3', function () { - const obj = {'playerSize': 'should be an array', 'mimes': ['video/mp4'], 'context': 'outstream'}; + const 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 () { - const obj = {'playerSize': [[640, 480]], 'mimes': ['video/mp4'], 'context': 'outstream'}; + const 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 () { - const obj = {'playerSize': [[[640, 480]]], 'mimes': ['video/mp4'], 'context': 'outstream'}; + const obj = { 'playerSize': [[[640, 480]]], 'mimes': ['video/mp4'], 'context': 'outstream' }; const result = getWidthAndHeightFromVideoObject(obj); expect(result).to.be.null; }); @@ -3594,12 +3571,12 @@ describe('ozone Adapter', function () { config.resetConfig() }) it('should return true if oz_request is false', function() { - config.setConfig({'ozone': {'oz_request': false}}); + config.setConfig({ 'ozone': { 'oz_request': false } }); const result = spec.blockTheRequest(); expect(result).to.be.true; }); it('should return false if oz_request is true', function() { - config.setConfig({'ozone': {'oz_request': true}}); + config.setConfig({ 'ozone': { 'oz_request': true } }); const result = spec.blockTheRequest(); expect(result).to.be.false; }); @@ -3676,7 +3653,7 @@ describe('ozone Adapter', function () { }); it('should correctly add video defaults if page config videoParams is defined, also check skip in the parent', function () { var specMock = utils.deepClone(spec); - config.setConfig({'ozone': {videoParams: {outstream: 3, instream: 1}}}); + config.setConfig({ 'ozone': { videoParams: { outstream: 3, instream: 1 } } }); const mediaTypes = { playerSize: [640, 480], mimes: ['video/mp4'], @@ -3712,19 +3689,19 @@ describe('ozone Adapter', function () { }); describe('setBidMediaTypeIfNotExist', function() { it('should leave the bid object alone if it already contains mediaType', function() { - const thisBid = {mediaType: 'marktest'}; + const thisBid = { mediaType: 'marktest' }; spec.setBidMediaTypeIfNotExist(thisBid, 'replacement'); expect(thisBid.mediaType).to.equal('marktest'); }); it('should change the bid object if it doesnt already contain mediaType', function() { - const thisBid = {someKey: 'someValue'}; + const thisBid = { someKey: 'someValue' }; spec.setBidMediaTypeIfNotExist(thisBid, 'replacement'); expect(thisBid.mediaType).to.equal('replacement'); }); }); describe('getLoggableBidObject', function() { it('should return an object without a "renderer" element', function () { - const obj = {'renderer': {}, 'somevalue': '', 'h': 100}; + const obj = { 'renderer': {}, 'somevalue': '', 'h': 100 }; const ret = spec.getLoggableBidObject(obj); expect(ret).to.not.have.own.property('renderer'); expect(ret.h).to.equal(100); @@ -3732,7 +3709,8 @@ describe('ozone Adapter', function () { }); describe('getUserIdFromEids', function() { it('should iterate over userIdAsEids when it is an object', function () { - let bid = { userIdAsEids: + let bid = { + userIdAsEids: [ { source: 'pubcid.org', @@ -3789,7 +3767,7 @@ describe('ozone Adapter', function () { expect(Object.keys(response).length).to.equal(0); }); it('find pubcid in the old location when there are eids and when there arent', function () { - let bid = {crumbs: {pubcid: 'some-random-id-value' }}; + let bid = { crumbs: { pubcid: 'some-random-id-value' } }; Object.defineProperty(bid, 'userIdAsEids', { value: undefined, writable: false, @@ -3909,14 +3887,14 @@ describe('ozone Adapter', function () { } } `); - const parsed = spec.pruneToExtPaths(jsonObj, {maxTestDepth: 2}); + const parsed = spec.pruneToExtPaths(jsonObj, { maxTestDepth: 2 }); expect(parsed).to.have.all.keys('site', 'user', 'regs', 'ext'); expect(Object.keys(parsed.site).length).to.equal(1); expect(parsed.site).to.have.all.keys('ext'); }); it('should prune a json object according to my params even when its empty', function () { const jsonObj = {}; - const parsed = spec.pruneToExtPaths(jsonObj, {maxTestDepth: 2}); + const parsed = spec.pruneToExtPaths(jsonObj, { maxTestDepth: 2 }); expect(Object.keys(parsed).length).to.equal(0); }); it('should prune a json object using Infinity as max depth', function () { @@ -4027,7 +4005,7 @@ describe('ozone Adapter', function () { } } `); - const parsed = spec.pruneToExtPaths(jsonObj, {maxTestDepth: Infinity}); + const parsed = spec.pruneToExtPaths(jsonObj, { maxTestDepth: Infinity }); expect(parsed.site.content.data[0].ext.segtax).to.equal('7'); }); it('should prune another json object', function () { @@ -4053,7 +4031,7 @@ describe('ozone Adapter', function () { } } }`); - const parsed = spec.pruneToExtPaths(jsonObj, {maxTestDepth: 2}); + const parsed = spec.pruneToExtPaths(jsonObj, { maxTestDepth: 2 }); expect(Object.keys(parsed.user).length).to.equal(1); expect(Object.keys(parsed)).to.have.members(['site', 'user']); expect(Object.keys(parsed.user)).to.have.members(['ext']); diff --git a/test/spec/modules/paapiForGpt_spec.js b/test/spec/modules/paapiForGpt_spec.js deleted file mode 100644 index eb75d51540d..00000000000 --- a/test/spec/modules/paapiForGpt_spec.js +++ /dev/null @@ -1,216 +0,0 @@ -import { - getPAAPISizeHook, - onAuctionConfigFactory, - setPAAPIConfigFactory, setTargetingHookFactory, - slotConfigurator -} from 'modules/paapiForGpt.js'; -import * as gptUtils from '../../../libraries/gptUtils/gptUtils.js'; -import 'modules/appnexusBidAdapter.js'; -import 'modules/rubiconBidAdapter.js'; -import {deepSetValue} from '../../../src/utils.js'; -import {config} from 'src/config.js'; - -describe('paapiForGpt module', () => { - let sandbox, fledgeAuctionConfig; - - beforeEach(() => { - sandbox = sinon.createSandbox(); - fledgeAuctionConfig = { - seller: 'bidder', - mock: 'config' - }; - }); - afterEach(() => { - sandbox.restore(); - }); - - describe('slotConfigurator', () => { - let setGptConfig; - function mockGptSlot(auPath) { - return { - setConfig: sinon.stub(), - getAdUnitPath: () => auPath - } - } - beforeEach(() => { - setGptConfig = slotConfigurator(); - }); - - Object.entries({ - 'single slot': [mockGptSlot('mock/gpt/au')], - 'multiple slots': [mockGptSlot('mock/gpt/au'), mockGptSlot('mock/gpt/au2')] - }).forEach(([t, gptSlots]) => { - describe(`when ad unit code matches ${t}`, () => { - it('should set GPT slot config', () => { - setGptConfig('au', gptSlots, [fledgeAuctionConfig]); - gptSlots.forEach(slot => { - sinon.assert.calledWith(slot.setConfig, { - componentAuction: [{ - configKey: 'bidder', - auctionConfig: fledgeAuctionConfig, - }] - }); - }) - }); - describe('when reset = true', () => { - it('should reset GPT slot config', () => { - setGptConfig('au', gptSlots, [fledgeAuctionConfig]); - gptSlots.forEach(slot => slot.setConfig.resetHistory()); - setGptConfig('au', gptSlots, [], true); - gptSlots.forEach(slot => { - sinon.assert.calledWith(slot.setConfig, { - componentAuction: [{ - configKey: 'bidder', - auctionConfig: null - }] - }); - }) - }); - - it('should reset only sellers with no fresh config', () => { - setGptConfig('au', gptSlots, [{seller: 's1'}, {seller: 's2'}]); - gptSlots.forEach(slot => slot.setConfig.resetHistory()); - setGptConfig('au', gptSlots, [{seller: 's1'}], true); - gptSlots.forEach(slot => { - sinon.assert.calledWith(slot.setConfig, { - componentAuction: [{ - configKey: 's1', - auctionConfig: {seller: 's1'} - }, { - configKey: 's2', - auctionConfig: null - }] - }) - }) - }); - - it('should not reset sellers that were already reset', () => { - setGptConfig('au', gptSlots, [{seller: 's1'}]); - setGptConfig('au', gptSlots, [], true); - gptSlots.forEach(slot => slot.setConfig.resetHistory()); - setGptConfig('au', gptSlots, [], true); - gptSlots.forEach(slot => sinon.assert.notCalled(slot.setConfig)); - }) - - it('should keep track of configuration history by ad unit', () => { - setGptConfig('au1', gptSlots, [{seller: 's1'}]); - setGptConfig('au1', gptSlots, [{seller: 's2'}], false); - setGptConfig('au2', gptSlots, [{seller: 's3'}]); - gptSlots.forEach(slot => slot.setConfig.resetHistory()); - setGptConfig('au1', gptSlots, [], true); - gptSlots.forEach(slot => { - sinon.assert.calledWith(slot.setConfig, { - componentAuction: [{ - configKey: 's1', - auctionConfig: null - }, { - configKey: 's2', - auctionConfig: null - }] - }); - }) - }) - }); - }) - }) - }); - describe('setTargeting hook', () => { - let setPaapiConfig, setTargetingHook, next; - beforeEach(() => { - setPaapiConfig = sinon.stub() - setTargetingHook = setTargetingHookFactory(setPaapiConfig); - next = sinon.stub(); - }); - function expectFilters(...filters) { - expect(setPaapiConfig.args.length).to.eql(filters.length) - filters.forEach(filter => { - sinon.assert.calledWith(setPaapiConfig, filter, 'mock-matcher') - }) - } - function runHook(adUnit) { - setTargetingHook(next, adUnit, 'mock-matcher'); - sinon.assert.calledWith(next, adUnit, 'mock-matcher'); - } - it('should invoke with no filters when adUnit is undef', () => { - runHook(); - expectFilters(undefined); - }); - it('should invoke once when adUnit is a string', () => { - runHook('mock-au'); - expectFilters({adUnitCode: 'mock-au'}) - }); - it('should invoke once per ad unit when an array', () => { - runHook(['au1', 'au2']); - expectFilters({adUnitCode: 'au1'}, {adUnitCode: 'au2'}); - }) - }) - describe('setPAAPIConfigForGpt', () => { - let getPAAPIConfig, setGptConfig, getSlots, setPAAPIConfigForGPT; - beforeEach(() => { - getPAAPIConfig = sinon.stub(); - setGptConfig = sinon.stub(); - getSlots = sinon.stub().callsFake((codes) => Object.fromEntries(codes.map(code => [code, ['mock-slot']]))) - setPAAPIConfigForGPT = setPAAPIConfigFactory(getPAAPIConfig, setGptConfig, getSlots); - }); - - Object.entries({ - missing: null, - empty: {} - }).forEach(([t, configs]) => { - it(`does not set GPT slot config when config is ${t}`, () => { - getPAAPIConfig.returns(configs); - setPAAPIConfigForGPT('mock-filters'); - sinon.assert.calledWith(getPAAPIConfig, 'mock-filters'); - sinon.assert.notCalled(setGptConfig); - }) - }); - - it('passes customSlotMatching to getSlots', () => { - getPAAPIConfig.returns({au1: {}}); - setPAAPIConfigForGPT('mock-filters', 'mock-custom-matching'); - sinon.assert.calledWith(getSlots, ['au1'], 'mock-custom-matching'); - }) - - it('sets GPT slot config for each ad unit that has PAAPI config, and resets the rest', () => { - const cfg = { - au1: { - componentAuctions: [{seller: 's1'}, {seller: 's2'}] - }, - au2: { - componentAuctions: [{seller: 's3'}] - }, - au3: null - } - getPAAPIConfig.returns(cfg); - setPAAPIConfigForGPT('mock-filters'); - sinon.assert.calledWith(getPAAPIConfig, 'mock-filters'); - Object.entries(cfg).forEach(([au, config]) => { - sinon.assert.calledWith(setGptConfig, au, ['mock-slot'], config?.componentAuctions ?? [], true); - }) - }); - }); - - describe('getPAAPISizeHook', () => { - let next; - beforeEach(() => { - next = sinon.stub(); - next.bail = sinon.stub(); - }); - - it('should pick largest supported size over larger unsupported size', () => { - getPAAPISizeHook(next, [[999, 999], [300, 250], [300, 600], [1234, 4321]]); - sinon.assert.calledWith(next.bail, [300, 600]); - }); - - Object.entries({ - 'present': [], - 'supported': [[123, 4], [321, 5]], - 'defined': undefined, - }).forEach(([t, sizes]) => { - it(`should defer to next when no size is ${t}`, () => { - getPAAPISizeHook(next, sizes); - sinon.assert.calledWith(next, sizes); - }) - }) - }) -}); diff --git a/test/spec/modules/paapi_spec.js b/test/spec/modules/paapi_spec.js deleted file mode 100644 index 2d491cdfe14..00000000000 --- a/test/spec/modules/paapi_spec.js +++ /dev/null @@ -1,2014 +0,0 @@ -import {expect} from 'chai'; -import {config} from '../../../src/config.js'; -import adapterManager from '../../../src/adapterManager.js'; -import * as utils from '../../../src/utils.js'; -import {deepAccess, deepClone} from '../../../src/utils.js'; -import {hook} from '../../../src/hook.js'; -import 'modules/appnexusBidAdapter.js'; -import 'modules/rubiconBidAdapter.js'; -import { - adAuctionHeadersHook, - addPaapiConfigHook, - addPaapiData, - ASYNC_SIGNALS, AsyncPAAPIParam, buildPAAPIParams, - buyersToAuctionConfigs, - getPAAPIConfig, - getPAAPISize, - IGB_TO_CONFIG, - mergeBuyers, NAVIGATOR_APIS, - onAuctionInit, - parallelPaapiProcessing, - parseExtIgi, - parseExtPrebidFledge, - partitionBuyers, - partitionBuyersByBidder, - registerSubmodule, - reset, - setImpExtAe, - setResponsePaapiConfigs -} from 'modules/paapi.js'; -import * as events from 'src/events.js'; -import {EVENTS} from 'src/constants.js'; -import {getGlobal} from '../../../src/prebidGlobal.js'; -import {auctionManager} from '../../../src/auctionManager.js'; -import {stubAuctionIndex} from '../../helpers/indexStub.js'; -import {AuctionIndex} from '../../../src/auctionIndex.js'; -import {buildActivityParams} from '../../../src/activities/params.js'; - -describe('paapi module', () => { - let sandbox; - before(reset); - beforeEach(() => { - sandbox = sinon.createSandbox(); - }); - afterEach(() => { - sandbox.restore(); - reset(); - }); - - describe(`using paapi configuration`, () => { - let getPAAPISizeStub; - - function getPAAPISizeHook(next, sizes) { - next.bail(getPAAPISizeStub(sizes)); - } - - before(() => { - getPAAPISize.before(getPAAPISizeHook, 100); - }); - - after(() => { - getPAAPISize.getHooks({hook: getPAAPISizeHook}).remove(); - }); - - beforeEach(() => { - getPAAPISizeStub = sinon.stub(); - }); - - describe('adAuctionHeadersHook', () => { - let bidderRequest, ajax; - beforeEach(() => { - ajax = sinon.stub(); - bidderRequest = {paapi: {}} - }) - function getWrappedAjax() { - let wrappedAjax; - const next = sinon.stub().callsFake((spec, bids, br, ajax) => { - wrappedAjax = ajax; - }); - adAuctionHeadersHook(next, {}, [], bidderRequest, ajax); - return wrappedAjax; - } - describe('when PAAPI is enabled', () => { - beforeEach(() => { - bidderRequest.paapi.enabled = true; - }); - [ - undefined, - {}, - {adAuctionHeaders: true} - ].forEach(options => - it(`should set adAuctionHeaders = true (when options are ${JSON.stringify(options)})`, () => { - getWrappedAjax()('url', {}, 'data', options); - sinon.assert.calledWith(ajax, 'url', {}, 'data', sinon.match({adAuctionHeaders: true})); - })); - - it('should respect adAuctionHeaders: false', () => { - getWrappedAjax()('url', {}, 'data', {adAuctionHeaders: false}); - sinon.assert.calledWith(ajax, 'url', {}, 'data', sinon.match({adAuctionHeaders: false})); - }) - }); - it('should not alter ajax when paapi is not enabled', () => { - expect(getWrappedAjax()).to.equal(ajax); - }) - }) - - describe('getPAAPIConfig', function () { - let nextFnSpy, auctionConfig, paapiConfig; - before(() => { - config.setConfig({paapi: {enabled: true}}); - }); - beforeEach(() => { - auctionConfig = { - seller: 'bidder', - mock: 'config' - }; - paapiConfig = { - config: auctionConfig - }; - nextFnSpy = sinon.spy(); - }); - - describe('on a single auction', function () { - const auctionId = 'aid'; - beforeEach(function () { - sandbox.stub(auctionManager, 'index').value(stubAuctionIndex({auctionId})); - }); - - it('should call next()', function () { - const request = {auctionId, adUnitCode: 'auc'}; - addPaapiConfigHook(nextFnSpy, request, paapiConfig); - sinon.assert.calledWith(nextFnSpy, request, paapiConfig); - }); - - describe('igb', () => { - let igb1, igb2, buyerAuctionConfig; - beforeEach(() => { - igb1 = { - origin: 'buyer.1' - }; - igb2 = { - origin: 'buyer.2' - }; - buyerAuctionConfig = { - seller: 'seller', - decisionLogicURL: 'seller-decision-logic' - }; - config.mergeConfig({ - paapi: { - componentSeller: { - auctionConfig: buyerAuctionConfig - } - } - }); - }); - - function addIgb(request, igb) { - addPaapiConfigHook(nextFnSpy, Object.assign({auctionId}, request), {igb}); - } - - it('should be collected into an auction config', () => { - addIgb({adUnitCode: 'au1'}, igb1); - addIgb({adUnitCode: 'au1'}, igb2); - events.emit(EVENTS.AUCTION_END, {auctionId, adUnitCodes: ['au1']}); - const buyerConfig = getPAAPIConfig({auctionId}).au1.componentAuctions[0]; - sinon.assert.match(buyerConfig, { - interestGroupBuyers: [igb1.origin, igb2.origin], - ...buyerAuctionConfig - }); - }); - - describe('FPD', () => { - let ortb2, ortb2Imp; - beforeEach(() => { - ortb2 = {'fpd': 1}; - ortb2Imp = {'fpd': 2}; - }); - - function getBuyerAuctionConfig() { - addIgb({adUnitCode: 'au1', ortb2, ortb2Imp}, igb1); - events.emit(EVENTS.AUCTION_END, {auctionId, adUnitCodes: ['au1']}); - return getPAAPIConfig({auctionId}).au1.componentAuctions[0]; - } - - it('should be added to auction config', () => { - sinon.assert.match(getBuyerAuctionConfig().perBuyerSignals[igb1.origin], { - prebid: { - ortb2, - ortb2Imp - } - }); - }); - - it('should not override existing perBuyerSignals', () => { - const original = { - ortb2: { - fpd: 'original' - } - }; - igb1.pbs = { - prebid: deepClone(original) - }; - sinon.assert.match(getBuyerAuctionConfig().perBuyerSignals[igb1.origin], { - prebid: original - }); - }); - }); - }); - - describe('should collect auction configs', () => { - let cf1, cf2; - beforeEach(() => { - cf1 = {...auctionConfig, id: 1, seller: 'b1'}; - cf2 = {...auctionConfig, id: 2, seller: 'b2'}; - addPaapiConfigHook(nextFnSpy, {auctionId, adUnitCode: 'au1'}, {config: cf1}); - addPaapiConfigHook(nextFnSpy, {auctionId, adUnitCode: 'au2'}, {config: cf2}); - events.emit(EVENTS.AUCTION_END, {auctionId, adUnitCodes: ['au1', 'au2', 'au3']}); - }); - - it('and make them available at end of auction', () => { - sinon.assert.match(getPAAPIConfig({auctionId}), { - au1: { - componentAuctions: [cf1] - }, - au2: { - componentAuctions: [cf2] - } - }); - }); - - it('and filter them by ad unit', () => { - const cfg = getPAAPIConfig({auctionId, adUnitCode: 'au1'}); - expect(Object.keys(cfg)).to.have.members(['au1']); - sinon.assert.match(cfg.au1, { - componentAuctions: [cf1] - }); - }); - - it('and not return them again', () => { - getPAAPIConfig(); - const cfg = getPAAPIConfig(); - expect(cfg).to.eql({}); - }); - - describe('includeBlanks = true', () => { - it('includes all ad units', () => { - const cfg = getPAAPIConfig({}, true); - expect(Object.keys(cfg)).to.have.members(['au1', 'au2', 'au3']); - expect(cfg.au3).to.eql(null); - }); - it('includes the targeted adUnit', () => { - expect(getPAAPIConfig({adUnitCode: 'au3'}, true)).to.eql({ - au3: null - }); - }); - it('includes the targeted auction', () => { - const cfg = getPAAPIConfig({auctionId}, true); - expect(Object.keys(cfg)).to.have.members(['au1', 'au2', 'au3']); - expect(cfg.au3).to.eql(null); - }); - it('does not include non-existing ad units', () => { - expect(getPAAPIConfig({adUnitCode: 'other'})).to.eql({}); - }); - it('does not include non-existing auctions', () => { - expect(getPAAPIConfig({auctionId: 'other'})).to.eql({}); - }); - }); - }); - - it('should drop auction configs after end of auction', () => { - events.emit(EVENTS.AUCTION_END, {auctionId}); - addPaapiConfigHook(nextFnSpy, {auctionId, adUnitCode: 'au'}, paapiConfig); - expect(getPAAPIConfig({auctionId})).to.eql({}); - }); - - describe('FPD', () => { - let ortb2, ortb2Imp; - beforeEach(() => { - ortb2 = {fpd: 1}; - ortb2Imp = {fpd: 2}; - }); - - function getComponentAuctionConfig() { - addPaapiConfigHook(nextFnSpy, { - auctionId, - adUnitCode: 'au1', - ortb2: {fpd: 1}, - ortb2Imp: {fpd: 2} - }, paapiConfig); - events.emit(EVENTS.AUCTION_END, {auctionId}); - return getPAAPIConfig({auctionId}).au1.componentAuctions[0]; - } - - it('should be added to auctionSignals', () => { - sinon.assert.match(getComponentAuctionConfig().auctionSignals, { - prebid: {ortb2, ortb2Imp} - }); - }); - it('should not override existing auctionSignals', () => { - auctionConfig.auctionSignals = {prebid: {ortb2: {fpd: 'original'}}}; - sinon.assert.match(getComponentAuctionConfig().auctionSignals, { - prebid: { - ortb2: {fpd: 'original'}, - ortb2Imp - } - }); - }); - - it('should be added to perBuyerSignals', () => { - auctionConfig.interestGroupBuyers = ['buyer.1', 'buyer.2']; - const pbs = getComponentAuctionConfig().perBuyerSignals; - sinon.assert.match(pbs, { - 'buyer.1': {prebid: {ortb2, ortb2Imp}}, - 'buyer.2': {prebid: {ortb2, ortb2Imp}} - }); - }); - - it('should not override existing perBuyerSignals', () => { - auctionConfig.interestGroupBuyers = ['buyer']; - const original = { - prebid: { - ortb2: { - fpd: 'original' - } - } - }; - auctionConfig.perBuyerSignals = { - buyer: deepClone(original) - }; - sinon.assert.match(getComponentAuctionConfig().perBuyerSignals.buyer, original); - }); - }); - - describe('submodules', () => { - let submods; - beforeEach(() => { - submods = [1, 2].map(i => ({ - name: `test${i}`, - onAuctionConfig: sinon.stub() - })); - submods.forEach(registerSubmodule); - }); - - describe('onAuctionConfig', () => { - const auctionId = 'aid'; - it('is invoked with null configs when there\'s no config', () => { - events.emit(EVENTS.AUCTION_END, {auctionId, adUnitCodes: ['au']}); - submods.forEach(submod => sinon.assert.calledWith(submod.onAuctionConfig, auctionId, {au: null})); - }); - it('is invoked with relevant configs', () => { - addPaapiConfigHook(nextFnSpy, {auctionId, adUnitCode: 'au1'}, paapiConfig); - addPaapiConfigHook(nextFnSpy, {auctionId, adUnitCode: 'au2'}, paapiConfig); - events.emit(EVENTS.AUCTION_END, {auctionId, adUnitCodes: ['au1', 'au2', 'au3']}); - submods.forEach(submod => { - sinon.assert.calledWith(submod.onAuctionConfig, auctionId, { - au1: {componentAuctions: [auctionConfig]}, - au2: {componentAuctions: [auctionConfig]}, - au3: null - }); - }); - }); - }); - }); - - describe('floor signal', () => { - before(() => { - if (!getGlobal().convertCurrency) { - getGlobal().convertCurrency = () => null; - getGlobal().convertCurrency.mock = true; - } - }); - after(() => { - if (getGlobal().convertCurrency.mock) { - delete getGlobal().convertCurrency; - } - }); - - beforeEach(() => { - sandbox.stub(getGlobal(), 'convertCurrency').callsFake((amount, from, to) => { - if (from === to) return amount; - if (from === 'USD' && to === 'JPY') return amount * 100; - if (from === 'JPY' && to === 'USD') return amount / 100; - throw new Error('unexpected currency conversion'); - }); - }); - - Object.entries({ - 'bids': (payload, values) => { - payload.bidsReceived = values - .map((val) => ({adUnitCode: 'au', cpm: val.amount, currency: val.cur})) - .concat([{adUnitCode: 'other', cpm: 10000, currency: 'EUR'}]); - }, - 'no bids': (payload, values) => { - payload.bidderRequests = values - .map((val) => ({ - bids: [{ - adUnitCode: 'au', - getFloor: () => ({floor: val.amount, currency: val.cur}) - }] - })) - .concat([{bids: {adUnitCode: 'other', getFloor: () => ({floor: -10000, currency: 'EUR'})}}]); - } - }).forEach(([tcase, setup]) => { - describe(`when auction has ${tcase}`, () => { - Object.entries({ - 'no currencies': { - values: [{amount: 1}, {amount: 100}, {amount: 10}, {amount: 100}], - 'bids': { - bidfloor: 100, - bidfloorcur: undefined - }, - 'no bids': { - bidfloor: 1, - bidfloorcur: undefined, - } - }, - 'only zero values': { - values: [{amount: 0, cur: 'USD'}, {amount: 0, cur: 'JPY'}], - 'bids': { - bidfloor: undefined, - bidfloorcur: undefined, - }, - 'no bids': { - bidfloor: undefined, - bidfloorcur: undefined, - } - }, - 'matching currencies': { - values: [{amount: 10, cur: 'JPY'}, {amount: 100, cur: 'JPY'}], - 'bids': { - bidfloor: 100, - bidfloorcur: 'JPY', - }, - 'no bids': { - bidfloor: 10, - bidfloorcur: 'JPY', - } - }, - 'mixed currencies': { - values: [{amount: 10, cur: 'USD'}, {amount: 10, cur: 'JPY'}], - 'bids': { - bidfloor: 10, - bidfloorcur: 'USD' - }, - 'no bids': { - bidfloor: 10, - bidfloorcur: 'JPY', - } - } - }).forEach(([t, testConfig]) => { - const values = testConfig.values; - const {bidfloor, bidfloorcur} = testConfig[tcase]; - - describe(`with ${t}`, () => { - let payload; - beforeEach(() => { - payload = {auctionId}; - setup(payload, values); - }); - - it('should populate bidfloor/bidfloorcur', () => { - addPaapiConfigHook(nextFnSpy, {auctionId, adUnitCode: 'au'}, paapiConfig); - events.emit(EVENTS.AUCTION_END, payload); - const cfg = getPAAPIConfig({auctionId}).au; - const signals = cfg.auctionSignals; - sinon.assert.match(cfg.componentAuctions[0].auctionSignals, signals || {}); - expect(signals?.prebid?.bidfloor).to.eql(bidfloor); - expect(signals?.prebid?.bidfloorcur).to.eql(bidfloorcur); - }); - }); - }); - }); - }); - }); - - describe('requestedSize', () => { - let adUnit; - beforeEach(() => { - adUnit = { - code: 'au', - }; - }); - - function getConfig() { - addPaapiConfigHook(nextFnSpy, {auctionId, adUnitCode: adUnit.code}, paapiConfig); - events.emit(EVENTS.AUCTION_END, {auctionId, adUnitCodes: [adUnit.code], adUnits: [adUnit]}); - return getPAAPIConfig()[adUnit.code]; - } - - Object.entries({ - 'adUnit.ortb2Imp.ext.paapi.requestedSize'() { - adUnit.ortb2Imp = { - ext: { - paapi: { - requestedSize: { - width: 123, - height: 321 - } - } - } - }; - }, - 'largest size'() { - getPAAPISizeStub.returns([123, 321]); - } - }).forEach(([t, setup]) => { - describe(`should be set from ${t}`, () => { - beforeEach(setup); - - it('without overriding component auctions, if set', () => { - auctionConfig.requestedSize = {width: '1px', height: '2px'}; - expect(getConfig().componentAuctions[0].requestedSize).to.eql({ - width: '1px', - height: '2px' - }); - }); - - it('on component auction, if missing', () => { - expect(getConfig().componentAuctions[0].requestedSize).to.eql({ - width: 123, - height: 321 - }); - }); - - it('on top level auction', () => { - expect(getConfig().requestedSize).to.eql({ - width: 123, - height: 321, - }); - }); - }); - }); - }); - }); - - describe('with multiple auctions', () => { - const AUCTION1 = 'auction1'; - const AUCTION2 = 'auction2'; - - function mockAuction(auctionId) { - return { - getAuctionId() { - return auctionId; - } - }; - } - - function expectAdUnitsFromAuctions(actualConfig, auToAuctionMap) { - expect(Object.keys(actualConfig)).to.have.members(Object.keys(auToAuctionMap)); - Object.entries(actualConfig).forEach(([au, cfg]) => { - cfg.componentAuctions.forEach(cmp => expect(cmp.auctionId).to.eql(auToAuctionMap[au])); - }); - } - - let configs; - beforeEach(() => { - const mockAuctions = [mockAuction(AUCTION1), mockAuction(AUCTION2)]; - sandbox.stub(auctionManager, 'index').value(new AuctionIndex(() => mockAuctions)); - configs = {[AUCTION1]: {}, [AUCTION2]: {}}; - Object.entries({ - [AUCTION1]: [['au1', 'au2'], ['missing-1']], - [AUCTION2]: [['au2', 'au3'], []], - }).forEach(([auctionId, [adUnitCodes, noConfigAdUnitCodes]]) => { - adUnitCodes.forEach(adUnitCode => { - const cfg = {...auctionConfig, auctionId, adUnitCode}; - configs[auctionId][adUnitCode] = cfg; - addPaapiConfigHook(nextFnSpy, {auctionId, adUnitCode}, {config: cfg}); - }); - events.emit(EVENTS.AUCTION_END, {auctionId, adUnitCodes: adUnitCodes.concat(noConfigAdUnitCodes)}); - }); - }); - - it('should filter by auction', () => { - expectAdUnitsFromAuctions(getPAAPIConfig({auctionId: AUCTION1}), {au1: AUCTION1, au2: AUCTION1}); - expectAdUnitsFromAuctions(getPAAPIConfig({auctionId: AUCTION2}), {au2: AUCTION2, au3: AUCTION2}); - }); - - it('should filter by auction and ad unit', () => { - expectAdUnitsFromAuctions(getPAAPIConfig({auctionId: AUCTION1, adUnitCode: 'au2'}), {au2: AUCTION1}); - expectAdUnitsFromAuctions(getPAAPIConfig({auctionId: AUCTION2, adUnitCode: 'au2'}), {au2: AUCTION2}); - }); - - it('should use last auction for each ad unit', () => { - expectAdUnitsFromAuctions(getPAAPIConfig(), {au1: AUCTION1, au2: AUCTION2, au3: AUCTION2}); - }); - - it('should filter by ad unit and use latest auction', () => { - expectAdUnitsFromAuctions(getPAAPIConfig({adUnitCode: 'au2'}), {au2: AUCTION2}); - }); - - it('should keep track of which configs were returned', () => { - expectAdUnitsFromAuctions(getPAAPIConfig({auctionId: AUCTION1}), {au1: AUCTION1, au2: AUCTION1}); - expect(getPAAPIConfig({auctionId: AUCTION1})).to.eql({}); - expectAdUnitsFromAuctions(getPAAPIConfig(), {au2: AUCTION2, au3: AUCTION2}); - }); - - describe('includeBlanks = true', () => { - Object.entries({ - 'auction with blanks': { - filters: {auctionId: AUCTION1}, - expected: {au1: true, au2: true, 'missing-1': false} - }, - 'blank adUnit in an auction': { - filters: {auctionId: AUCTION1, adUnitCode: 'missing-1'}, - expected: {'missing-1': false} - }, - 'non-existing auction': { - filters: {auctionId: 'other'}, - expected: {} - }, - 'non-existing adUnit in an auction': { - filters: {auctionId: AUCTION2, adUnitCode: 'other'}, - expected: {} - }, - 'non-existing ad unit': { - filters: {adUnitCode: 'other'}, - expected: {}, - }, - 'non existing ad unit in a non-existing auction': { - filters: {adUnitCode: 'other', auctionId: 'other'}, - expected: {} - }, - 'all ad units': { - filters: {}, - expected: {'au1': true, 'au2': true, 'missing-1': false, 'au3': true} - } - }).forEach(([t, {filters, expected}]) => { - it(t, () => { - const cfg = getPAAPIConfig(filters, true); - expect(Object.keys(cfg)).to.have.members(Object.keys(expected)); - Object.entries(expected).forEach(([au, shouldBeFilled]) => { - if (shouldBeFilled) { - expect(cfg[au]).to.not.be.null; - } else { - expect(cfg[au]).to.be.null; - } - }); - }); - }); - }); - }); - }); - - describe('markForFledge', function () { - const navProps = Object.fromEntries(['runAdAuction', 'joinAdInterestGroup'].map(p => [p, navigator[p]])); - let adUnits; - - before(function () { - // navigator.runAdAuction & co may not exist, so we can't stub it normally with - // sinon.stub(navigator, 'runAdAuction') or something - Object.keys(navProps).forEach(p => { - navigator[p] = sinon.stub(); - }); - hook.ready(); - config.resetConfig(); - }); - - after(function () { - Object.entries(navProps).forEach(([p, orig]) => navigator[p] = orig); - }); - - beforeEach(() => { - getPAAPISizeStub = sinon.stub(); - adUnits = [{ - 'code': '/19968336/header-bid-tag1', - 'mediaTypes': { - 'banner': { - 'sizes': [[728, 90]] - }, - }, - 'bids': [ - { - 'bidder': 'appnexus', - }, - { - 'bidder': 'rubicon', - }, - ] - }]; - }); - - afterEach(function () { - config.resetConfig(); - }); - - describe('makeBidRequests', () => { - before(() => { - NAVIGATOR_APIS.forEach(method => { - if (navigator[method] == null) { - navigator[method] = () => null; - after(() => { - delete navigator[method]; - }) - } - }) - }); - beforeEach(() => { - NAVIGATOR_APIS.forEach(method => { - sandbox.stub(navigator, method) - }) - }); - - function mark() { - return Object.fromEntries( - adapterManager.makeBidRequests( - adUnits, - Date.now(), - utils.getUniqueIdentifierStr(), - function callback() { - }, - [] - ).map(b => [b.bidderCode, b]) - ); - } - - async function testAsyncParams(bidderRequest) { - for (const method of NAVIGATOR_APIS) { - navigator[method].returns('result'); - expect(await bidderRequest.paapi[method]('arg').resolve()).to.eql('result'); - sinon.assert.calledWith(navigator[method], 'arg'); - } - } - - async function expectFledgeFlags(...enableFlags) { - const bidRequests = mark(); - expect(bidRequests.appnexus.paapi?.enabled).to.eql(enableFlags[0].enabled); - if (bidRequests.appnexus.paapi?.enabled) { - await testAsyncParams(bidRequests.appnexus) - } - bidRequests.appnexus.bids.forEach(bid => expect(bid.ortb2Imp.ext.ae).to.eql(enableFlags[0].ae)); - - expect(bidRequests.rubicon.paapi?.enabled).to.eql(enableFlags[1].enabled); - if (bidRequests.rubicon.paapi?.enabled) { - testAsyncParams(bidRequests.rubicon); - } - - bidRequests.rubicon.bids.forEach(bid => expect(bid.ortb2Imp?.ext?.ae).to.eql(enableFlags[1].ae)); - - Object.values(bidRequests).flatMap(req => req.bids).forEach(bid => { - if (bid.ortb2Imp?.ext?.ae) { - sinon.assert.match(bid.ortb2Imp.ext.igs, { - ae: bid.ortb2Imp.ext.ae, - biddable: 1 - }); - } - }); - } - - describe('with setConfig()', () => { - it('should set paapi.enabled correctly per bidder', async function () { - config.setConfig({ - bidderSequence: 'fixed', - paapi: { - enabled: true, - bidders: ['appnexus'], - defaultForSlots: 1, - } - }); - await expectFledgeFlags({enabled: true, ae: 1}, {enabled: false, ae: 0}); - }); - - it('should set paapi.enabled correctly for all bidders', async function () { - config.setConfig({ - bidderSequence: 'fixed', - paapi: { - enabled: true, - defaultForSlots: 1, - } - }); - await expectFledgeFlags({enabled: true, ae: 1}, {enabled: true, ae: 1}); - }); - - Object.entries({ - 'not set': { - cfg: {}, - componentSeller: false - }, - 'set': { - cfg: { - componentSeller: { - auctionConfig: { - decisionLogicURL: 'publisher.example' - } - } - }, - componentSeller: true - } - }).forEach(([t, {cfg, componentSeller}]) => { - it(`should set request paapi.componentSeller = ${componentSeller} when config componentSeller is ${t}`, () => { - config.setConfig({ - paapi: { - enabled: true, - defaultForSlots: 1, - ...cfg - } - }); - Object.values(mark()).forEach(br => expect(br.paapi?.componentSeller).to.eql(componentSeller)); - }); - }); - }); - }); - describe('addPaapiData', () => { - function getEnrichedAdUnits() { - const next = sinon.stub(); - addPaapiData(next, adUnits); - sinon.assert.calledWith(next, adUnits); - return adUnits; - } - - function getImpExt() { - const next = sinon.stub(); - addPaapiData(next, adUnits); - sinon.assert.calledWith(next, adUnits); - return { - global: adUnits[0].ortb2Imp?.ext, - ...Object.fromEntries(adUnits[0].bids.map(bid => [bid.bidder, bid.ortb2Imp?.ext])) - } - } - - it('should not override pub-defined ext.ae', () => { - config.setConfig({ - paapi: { - enabled: true, - defaultForSlots: 1, - } - }); - Object.assign(adUnits[0], {ortb2Imp: {ext: {ae: 0}}}); - sinon.assert.match(getImpExt(), { - global: { - ae: 0, - }, - rubicon: undefined, - appnexus: undefined - }); - }); - - it('should override per-bidder when excluded via paapi.bidders', () => { - config.setConfig({ - paapi: { - enabled: true, - defaultForSlots: 1, - bidders: ['rubicon'] - } - }) - sinon.assert.match(getImpExt(), { - global: { - ae: 1, - igs: { - ae: 1, - biddable: 1 - } - }, - rubicon: undefined, - appnexus: { - ae: 0, - igs: { - ae: 0, - biddable: 0 - } - } - }) - }) - - it('should populate ext.igs when request has ext.ae', () => { - config.setConfig({ - paapi: { - enabled: true - } - }); - Object.assign(adUnits[0], {ortb2Imp: {ext: {ae: 3}}}); - sinon.assert.match(getImpExt(), { - global: { - ae: 3, - igs: { - ae: 3, - biddable: 1 - } - }, - rubicon: undefined, - appnexus: undefined, - }); - }); - - it('should not override pub-defined ext.igs', () => { - config.setConfig({ - paapi: { - enabled: true - } - }); - Object.assign(adUnits[0], {ortb2Imp: {ext: {ae: 1, igs: {biddable: 0}}}}); - sinon.assert.match(getImpExt(), { - global: { - ae: 1, - igs: { - ae: 1, - biddable: 0 - } - }, - rubicon: undefined, - appnexus: undefined - }) - }); - - it('should fill ext.ae from ext.igs, if defined', () => { - config.setConfig({ - paapi: { - enabled: true - } - }); - Object.assign(adUnits[0], {ortb2Imp: {ext: {igs: {}}}}); - sinon.assert.match(getImpExt(), { - global: { - ae: 1, - igs: { - ae: 1, - biddable: 1 - } - }, - appnexus: undefined, - rubicon: undefined - }) - }); - - describe('ortb2Imp.ext.paapi.requestedSize', () => { - beforeEach(() => { - config.setConfig({ - paapi: { - enabled: true, - defaultForSlots: 1, - } - }); - }); - - it('should default to value returned by getPAAPISize', () => { - getPAAPISizeStub.returns([123, 321]); - expect(getImpExt().global.paapi).to.eql({ - requestedSize: { - width: 123, - height: 321 - } - }); - }); - - it('should not be overridden, if provided by the pub', () => { - adUnits[0].ortb2Imp = { - ext: { - paapi: { - requestedSize: { - width: '123px', - height: '321px' - } - } - } - }; - expect(getImpExt().global.paapi).to.eql({ - requestedSize: { - width: '123px', - height: '321px' - } - }) - sinon.assert.notCalled(getPAAPISizeStub); - }); - - it('should not be set if adUnit has no banner sizes', () => { - adUnits[0].mediaTypes = { - video: {} - }; - expect(getImpExt().global?.paapi?.requestedSize).to.not.exist; - }); - }); - }); - }); - }); - - describe('igb', () => { - let igb1, igb2; - const buyer1 = 'https://buyer1.example'; - const buyer2 = 'https://buyer2.example'; - beforeEach(() => { - igb1 = { - origin: buyer1, - cur: 'EUR', - maxbid: 1, - pbs: { - signal: 1 - }, - ps: { - priority: 1 - } - }; - igb2 = { - origin: buyer2, - cur: 'USD', - maxbid: 2, - pbs: { - signal: 2 - }, - ps: { - priority: 2 - } - }; - }); - - describe('mergeBuyers', () => { - it('should merge multiple igb into a partial auction config', () => { - sinon.assert.match(mergeBuyers([igb1, igb2]), { - interestGroupBuyers: [buyer1, buyer2], - perBuyerCurrencies: { - [buyer1]: 'EUR', - [buyer2]: 'USD' - }, - perBuyerSignals: { - [buyer1]: { - signal: 1 - }, - [buyer2]: { - signal: 2 - } - }, - perBuyerPrioritySignals: { - [buyer1]: { - priority: 1 - }, - [buyer2]: { - priority: 2 - } - }, - auctionSignals: { - prebid: { - perBuyerMaxbid: { - [buyer1]: 1, - [buyer2]: 2 - } - } - } - }); - }); - - Object.entries(IGB_TO_CONFIG).forEach(([igbField, configField]) => { - it(`should not set ${configField} if ${igbField} is undefined`, () => { - delete igb1[igbField]; - expect(deepAccess(mergeBuyers([igb1, igb2]), configField)[buyer1]).to.not.exist; - }); - }); - - it('ignores igbs that have no origin', () => { - delete igb1.origin; - expect(mergeBuyers([igb1, igb2])).to.eql(mergeBuyers([igb2])); - }); - - it('ignores igbs with duplicate origin', () => { - igb2.origin = igb1.origin; - expect(mergeBuyers([igb1, igb2])).to.eql(mergeBuyers([igb1])); - }); - }); - - describe('partitionBuyers', () => { - it('should return a single partition when there are no duplicates', () => { - expect(partitionBuyers([igb1, igb2])).to.eql([[igb1, igb2]]); - }); - it('should ignore igbs that have no origin', () => { - delete igb1.origin; - expect(partitionBuyers([igb1, igb2])).to.eql([[igb2]]); - }); - it('should return a single partition when duplicates exist, but do not conflict', () => { - expect(partitionBuyers([igb1, igb2, deepClone(igb1)])).to.eql([[igb1, igb2]]); - }); - it('should return multiple partitions when there are conflicts', () => { - const igb3 = deepClone(igb1); - const igb4 = deepClone(igb1); - igb3.pbs.signal = 'conflict'; - igb4.ps.signal = 'conflict'; - expect(partitionBuyers([igb1, igb2, igb3, igb4])).to.eql([ - [igb1, igb2], - [igb3], - [igb4] - ]); - }); - }); - - describe('partitionBuyersByBidder', () => { - it('should split requests by bidder', () => { - expect(partitionBuyersByBidder([[{bidder: 'a'}, igb1], [{bidder: 'b'}, igb2]])).to.eql([ - [{bidder: 'a'}, [igb1]], - [{bidder: 'b'}, [igb2]] - ]); - }); - - it('accepts repeated buyers, if from different bidders', () => { - expect(partitionBuyersByBidder([ - [{bidder: 'a', extra: 'data'}, igb1], - [{bidder: 'b', more: 'data'}, igb1], - [{bidder: 'a'}, igb2], - [{bidder: 'b'}, igb2] - ])).to.eql([ - [{bidder: 'a', extra: 'data'}, [igb1, igb2]], - [{bidder: 'b', more: 'data'}, [igb1, igb2]] - ]); - }); - describe('buyersToAuctionConfig', () => { - let config, partitioners, merge, igbRequests; - beforeEach(() => { - config = { - auctionConfig: { - decisionLogicURL: 'mock-decision-logic' - } - }; - partitioners = { - compact: sinon.stub(), - expand: sinon.stub(), - }; - let i = 0; - merge = sinon.stub().callsFake(() => ({config: i++})); - igbRequests = [ - [{}, igb1], - [{}, igb2] - ]; - }); - - function toAuctionConfig(reqs = igbRequests) { - return buyersToAuctionConfigs(reqs, merge, config, partitioners); - } - - it('uses compact partitions by default, and returns an auction config for each one', () => { - partitioners.compact.returns([[{}, 1], [{}, 2]]); - const [cf1, cf2] = toAuctionConfig(); - sinon.assert.match(cf1[1], { - ...config.auctionConfig, - config: 0 - }); - sinon.assert.match(cf2[1], { - ...config.auctionConfig, - config: 1 - }); - sinon.assert.calledWith(partitioners.compact, igbRequests); - [1, 2].forEach(mockPart => sinon.assert.calledWith(merge, mockPart)); - }); - - it('uses per-bidder partition when config has separateAuctions', () => { - config.separateAuctions = true; - partitioners.expand.returns([]); - toAuctionConfig(); - sinon.assert.called(partitioners.expand); - }); - - it('does not return any auction config when configuration does not specify auctionConfig', () => { - delete config.auctionConfig; - expect(toAuctionConfig()).to.eql([]); - Object.values(partitioners).forEach(part => sinon.assert.notCalled(part)); - }); - - it('sets FPD in auction signals when partitioner returns it', () => { - const fpd = { - ortb2: {fpd: 1}, - ortb2Imp: {fpd: 2} - }; - partitioners.compact.returns([[{}], [fpd]]); - const [cf1, cf2] = toAuctionConfig(); - expect(cf1[1].auctionSignals?.prebid).to.not.exist; - expect(cf2[1].auctionSignals.prebid).to.eql(fpd); - }); - }); - }); - }); - - describe('getPAAPISize', () => { - before(() => { - getPAAPISize.getHooks().remove(); - }); - - Object.entries({ - 'ignores placeholders': { - in: [[1, 1], [0, 0], [3, 4]], - out: [3, 4] - }, - 'picks largest size by area': { - in: [[200, 100], [150, 150]], - out: [150, 150] - }, - 'can handle no sizes': { - in: [], - out: undefined - }, - 'can handle no input': { - in: undefined, - out: undefined - }, - 'can handle placeholder sizes': { - in: [[1, 1]], - out: undefined - } - }).forEach(([t, {in: input, out}]) => { - it(t, () => { - expect(getPAAPISize(input)).to.eql(out); - }); - }); - }); - - describe('buildPaapiParameters', () => { - let next, bidderRequest, spec, bids; - beforeEach(() => { - next = sinon.stub(); - spec = {}; - bidderRequest = {paapi: {enabled: true}}; - bids = []; - }); - - function runParamHook() { - return Promise.resolve(buildPAAPIParams(next, spec, bids, bidderRequest)); - } - - Object.entries({ - 'has no paapiParameters': () => null, - 'returns empty parameter map'() { - spec.paapiParameters = () => ({}) - }, - 'returns null parameter map'() { - spec.paapiParameters = () => null - }, - 'returns params, but PAAPI is disabled'() { - bidderRequest.paapi.enabled = false; - spec.paapiParameters = () => ({param: new AsyncPAAPIParam()}) - } - }).forEach(([t, setup]) => { - it(`should do nothing if spec ${t}`, async () => { - setup(); - await runParamHook(); - sinon.assert.calledWith(next, spec, bids, bidderRequest); - }) - }) - - describe('when paapiParameters returns a map', () => { - let params; - beforeEach(() => { - spec.paapiParameters = sinon.stub().callsFake(() => params); - }); - it('should be invoked with bids & bidderRequest', async () => { - await runParamHook(); - sinon.assert.calledWith(spec.paapiParameters, bids, bidderRequest); - }); - it('should leave most things (including promises) untouched', async () => { - params = { - 'p1': 'scalar', - 'p2': Promise.resolve() - } - await runParamHook(); - expect(bidderRequest.paapi.params).to.eql(params); - }); - it('should resolve async PAAPI parameeters', async () => { - params = { - 'resolved': new AsyncPAAPIParam(() => Promise.resolve('value')), - } - await runParamHook(); - expect(bidderRequest.paapi.params).to.eql({ - 'resolved': 'value' - }) - }) - - it('should still call next if the resolution fails', async () => { - params = { - error: new AsyncPAAPIParam(() => Promise.reject(new Error())) - } - await runParamHook(); - sinon.assert.called(next); - expect(bidderRequest.paapi.params).to.not.exist; - }) - }) - }) - - describe('parallel PAAPI auctions', () => { - describe('parallellPaapiProcessing', () => { - let next, spec, bids, bidderRequest, restOfTheArgs, mockConfig, mockAuction, bidsReceived, bidderRequests, adUnitCodes, adUnits; - - beforeEach(() => { - next = sinon.stub(); - spec = { - code: 'mockBidder', - }; - bids = [{ - bidder: 'mockBidder', - bidId: 'bidId', - adUnitCode: 'au', - auctionId: 'aid', - ortb2: { - source: { - tid: 'aid' - }, - }, - mediaTypes: { - banner: { - sizes: [[123, 321]] - } - } - }]; - bidderRequest = { - auctionId: 'aid', - bidderCode: 'mockBidder', - paapi: {enabled: true}, - bids, - ortb2: { - source: { - tid: 'aid' - } - } - }; - restOfTheArgs = [{more: 'args'}]; - mockConfig = { - seller: 'mock.seller', - decisionLogicURL: 'mock.seller/decisionLogic', - interestGroupBuyers: ['mock.buyer'] - } - mockAuction = {}; - bidsReceived = [{adUnitCode: 'au', cpm: 1}]; - adUnits = [{code: 'au'}] - adUnitCodes = ['au']; - bidderRequests = [bidderRequest]; - sandbox.stub(auctionManager.index, 'getAuction').callsFake(() => mockAuction); - sandbox.stub(auctionManager.index, 'getAdUnit').callsFake((req) => bids.find(bid => bid.adUnitCode === req.adUnitCode)) - config.setConfig({paapi: {enabled: true}}); - }); - - afterEach(() => { - sinon.assert.calledWith(next, spec, bids, bidderRequest, ...restOfTheArgs); - config.resetConfig(); - }); - - function startParallel() { - parallelPaapiProcessing(next, spec, bids, bidderRequest, ...restOfTheArgs); - onAuctionInit({auctionId: 'aid'}) - } - - function endAuction() { - events.emit(EVENTS.AUCTION_END, {auctionId: 'aid', bidsReceived, bidderRequests, adUnitCodes, adUnits}) - } - - describe('should have no effect when', () => { - afterEach(() => { - expect(getPAAPIConfig({}, true)).to.eql({au: null}); - }) - it('spec has no buildPAAPIConfigs', () => { - startParallel(); - }); - Object.entries({ - 'returns no configs': () => { spec.buildPAAPIConfigs = sinon.stub().callsFake(() => []); }, - 'throws': () => { spec.buildPAAPIConfigs = sinon.stub().callsFake(() => { throw new Error() }) }, - 'returns too little config': () => { spec.buildPAAPIConfigs = sinon.stub().callsFake(() => [ {bidId: 'bidId', config: {seller: 'mock.seller'}} ]) }, - 'bidder is not paapi enabled': () => { - bidderRequest.paapi.enabled = false; - spec.buildPAAPIConfigs = sinon.stub().callsFake(() => [{config: mockConfig, bidId: 'bidId'}]) - }, - 'paapi module is not enabled': () => { - delete bidderRequest.paapi; - spec.buildPAAPIConfigs = sinon.stub().callsFake(() => [{config: mockConfig, bidId: 'bidId'}]) - }, - 'bidId points to missing bid': () => { spec.buildPAAPIConfigs = sinon.stub().callsFake(() => [{config: mockConfig, bidId: 'missing'}]) } - }).forEach(([t, setup]) => { - it(`buildPAAPIConfigs ${t}`, () => { - setup(); - startParallel(); - }); - }); - }); - - function resolveConfig(auctionConfig) { - return Promise.all( - Object.entries(auctionConfig) - .map(([key, value]) => Promise.resolve(value).then(value => [key, value])) - ).then(result => Object.fromEntries(result)) - } - - describe('when buildPAAPIConfigs returns valid config', () => { - let builtCfg; - beforeEach(() => { - builtCfg = [{bidId: 'bidId', config: mockConfig}]; - spec.buildPAAPIConfigs = sinon.stub().callsFake(() => builtCfg); - }); - - it('should make async config available from getPAAPIConfig', () => { - startParallel(); - const actual = getPAAPIConfig(); - const promises = Object.fromEntries(ASYNC_SIGNALS.map(signal => [signal, sinon.match((arg) => arg instanceof Promise)])) - sinon.assert.match(actual, { - au: sinon.match({ - ...promises, - requestedSize: { - width: 123, - height: 321 - }, - componentAuctions: [ - sinon.match({ - ...mockConfig, - ...promises, - requestedSize: { - width: 123, - height: 321 - } - }) - ] - }) - }); - }); - - it('should work when called multiple times for the same auction', () => { - startParallel(); - spec.buildPAAPIConfigs = sinon.stub().callsFake(() => []); - startParallel(); - expect(getPAAPIConfig().au.componentAuctions.length).to.eql(1); - }); - - it('should hide TIDs from buildPAAPIConfigs', () => { - config.setConfig({enableTIDs: false}); - startParallel(); - sinon.assert.calledWith( - spec.buildPAAPIConfigs, - sinon.match(bidRequests => bidRequests.every(req => req.auctionId == null)), - sinon.match(bidderRequest => bidderRequest.auctionId == null) - ); - }); - - it('should show TIDs when enabled', () => { - config.setConfig({enableTIDs: true}); - startParallel(); - sinon.assert.calledWith( - spec.buildPAAPIConfigs, - sinon.match(bidRequests => bidRequests.every(req => req.auctionId === 'aid')), - sinon.match(bidderRequest => bidderRequest.auctionId === 'aid') - ) - }) - - it('should respect requestedSize from adapter', () => { - mockConfig.requestedSize = {width: 1, height: 2}; - startParallel(); - sinon.assert.match(getPAAPIConfig().au, { - requestedSize: { - width: 123, - height: 321 - }, - componentAuctions: [sinon.match({ - requestedSize: { - width: 1, - height: 2 - } - })] - }) - }) - - it('should not accept multiple partial configs for the same bid/seller', () => { - builtCfg.push(builtCfg[0]) - startParallel(); - expect(getPAAPIConfig().au.componentAuctions.length).to.eql(1); - }); - it('should resolve top level config with auction signals', async () => { - startParallel(); - let config = getPAAPIConfig().au; - endAuction(); - config = await resolveConfig(config); - sinon.assert.match(config, { - auctionSignals: { - prebid: {bidfloor: 1} - } - }) - }); - - describe('when adapter returns the rest of auction config', () => { - let configRemainder; - beforeEach(() => { - configRemainder = { - ...Object.fromEntries(ASYNC_SIGNALS.map(signal => [signal, {type: signal}])), - seller: 'mock.seller' - }; - }) - function returnRemainder() { - addPaapiConfigHook(sinon.stub(), bids[0], {config: configRemainder}); - } - it('should resolve component configs with values returned by adapters', async () => { - startParallel(); - let config = getPAAPIConfig().au.componentAuctions[0]; - returnRemainder(); - endAuction(); - config = await resolveConfig(config); - sinon.assert.match(config, configRemainder); - }); - - it('should pick first config that matches bidId/seller', async () => { - startParallel(); - let config = getPAAPIConfig().au.componentAuctions[0]; - returnRemainder(); - const expectedSignals = {...configRemainder}; - configRemainder = { - ...configRemainder, - auctionSignals: { - this: 'should be ignored' - } - } - returnRemainder(); - endAuction(); - config = await resolveConfig(config); - sinon.assert.match(config, expectedSignals); - }); - - describe('should default to values returned from buildPAAPIConfigs when interpretResponse does not return', () => { - beforeEach(() => { - ASYNC_SIGNALS.forEach(signal => mockConfig[signal] = {default: signal}) - }); - Object.entries({ - 'returns no matching config'() { - }, - 'does not include values in response'() { - configRemainder = {}; - returnRemainder(); - } - }).forEach(([t, postResponse]) => { - it(t, async () => { - startParallel(); - let config = getPAAPIConfig().au.componentAuctions[0]; - postResponse(); - endAuction(); - config = await resolveConfig(config); - sinon.assert.match(config, mockConfig); - }); - }); - }); - - it('should resolve to undefined when no value is available', async () => { - startParallel(); - let config = getPAAPIConfig().au.componentAuctions[0]; - delete configRemainder.sellerSignals; - returnRemainder(); - endAuction(); - config = await resolveConfig(config); - expect(config.sellerSignals).to.be.undefined; - }); - - [ - { - start: {t: 'scalar', value: 'str'}, - end: {t: 'array', value: ['abc']}, - should: {t: 'array', value: ['abc']} - }, - { - start: {t: 'object', value: {a: 'b'}}, - end: {t: 'scalar', value: 'abc'}, - should: {t: 'scalar', value: 'abc'} - }, - { - start: {t: 'object', value: {outer: {inner: 'val'}}}, - end: {t: 'object', value: {outer: {other: 'val'}}}, - should: {t: 'merge', value: {outer: {inner: 'val', other: 'val'}}} - } - ].forEach(({start, end, should}) => { - it(`when buildPAAPIConfigs returns ${start.t}, interpretResponse return ${end.t}, promise should resolve to ${should.t}`, async () => { - mockConfig.sellerSignals = start.value - startParallel(); - let config = getPAAPIConfig().au.componentAuctions[0]; - configRemainder.sellerSignals = end.value; - returnRemainder(); - endAuction(); - config = await resolveConfig(config); - expect(config.sellerSignals).to.eql(should.value); - }) - }) - - it('should make extra configs available', async () => { - startParallel(); - returnRemainder(); - configRemainder = {...configRemainder, seller: 'other.seller'}; - returnRemainder(); - endAuction(); - let configs = getPAAPIConfig().au.componentAuctions; - configs = [await resolveConfig(configs[0]), configs[1]]; - expect(configs.map(cfg => cfg.seller)).to.eql(['mock.seller', 'other.seller']); - }); - - describe('submodule\'s onAuctionConfig', () => { - let onAuctionConfig; - beforeEach(() => { - onAuctionConfig = sinon.stub(); - registerSubmodule({onAuctionConfig}) - }); - - Object.entries({ - 'parallel=true, some configs deferred': { - setup() { - config.mergeConfig({paapi: {parallel: true}}) - }, - delayed: false, - }, - 'parallel=true, no deferred configs': { - setup() { - config.mergeConfig({paapi: {parallel: true}}); - spec.buildPAAPIConfigs = sinon.stub().callsFake(() => []); - }, - delayed: true - }, - 'parallel=false, some configs deferred': { - setup() { - config.mergeConfig({paapi: {parallel: false}}) - }, - delayed: true - } - }).forEach(([t, {setup, delayed}]) => { - describe(`when ${t}`, () => { - beforeEach(() => { - mockAuction.requestsDone = Promise.resolve(); - setup(); - }); - - function expectInvoked(shouldBeInvoked) { - if (shouldBeInvoked) { - sinon.assert.calledWith(onAuctionConfig, 'aid', sinon.match(arg => arg.au.componentAuctions[0].seller === 'mock.seller')); - } else { - sinon.assert.notCalled(onAuctionConfig); - } - } - - it(`should invoke onAuctionConfig when ${delayed ? 'auction ends' : 'auction requests have started'}`, async () => { - startParallel(); - await mockAuction.requestsDone; - expectInvoked(!delayed); - onAuctionConfig.resetHistory(); - returnRemainder(); - endAuction(); - expectInvoked(delayed); - }) - }) - }) - }) - }); - }); - describe('when buildPAAPIConfigs returns igb', () => { - let builtCfg, igb, auctionConfig; - beforeEach(() => { - igb = {origin: 'mock.buyer'} - builtCfg = [{bidId: 'bidId', igb}]; - spec.buildPAAPIConfigs = sinon.stub().callsFake(() => builtCfg); - auctionConfig = { - seller: 'mock.seller', - decisionLogicUrl: 'mock.seller/decisionLogic' - } - config.mergeConfig({ - paapi: { - componentSeller: { - auctionConfig - } - } - }) - bidderRequest.paapi.componentSeller = true; - }); - Object.entries({ - 'componentSeller not configured'() { - bidderRequest.paapi.componentSeller = false; - }, - 'buildPAAPIconfig returns nothing'() { - builtCfg = [] - }, - 'returned igb is not valid'() { - builtCfg = [{bidId: 'bidId', igb: {}}]; - } - }).forEach(([t, setup]) => { - it(`should have no effect when ${t}`, () => { - setup(); - startParallel(); - expect(getPAAPIConfig()).to.eql({}); - }) - }) - - describe('when component seller is set up', () => { - it('should generate a deferred auctionConfig', () => { - startParallel(); - sinon.assert.match(getPAAPIConfig().au.componentAuctions[0], { - ...auctionConfig, - interestGroupBuyers: ['mock.buyer'], - }) - }); - - it('should use signal values from componentSeller.auctionConfig', async () => { - auctionConfig.auctionSignals = {test: 'signal'}; - config.mergeConfig({ - paapi: {componentSeller: {auctionConfig}} - }) - startParallel(); - endAuction(); - const cfg = await resolveConfig(getPAAPIConfig().au.componentAuctions[0]); - sinon.assert.match(cfg.auctionSignals, auctionConfig.auctionSignals); - }) - - it('should collate buyers', () => { - startParallel(); - startParallel(); - sinon.assert.match(getPAAPIConfig().au.componentAuctions[0], { - interestGroupBuyers: ['mock.buyer'] - }); - }); - - function returnIgb(igb) { - addPaapiConfigHook(sinon.stub(), bids[0], {igb}); - } - - it('should resolve to values from interpretResponse as well as buildPAAPIConfigs', async () => { - igb.cur = 'cur'; - igb.pbs = {over: 'ridden'} - startParallel(); - let cfg = getPAAPIConfig().au.componentAuctions[0]; - returnIgb({ - origin: 'mock.buyer', - pbs: {some: 'signal'} - }); - endAuction(); - cfg = await resolveConfig(cfg); - sinon.assert.match(cfg, { - perBuyerSignals: { - [igb.origin]: {some: 'signal'}, - }, - perBuyerCurrencies: { - [igb.origin]: 'cur' - } - }) - }); - - it('should not overwrite config once resolved', () => { - startParallel(); - returnIgb({ - origin: 'mock.buyer', - }); - endAuction(); - const cfg = getPAAPIConfig().au; - sinon.assert.match(cfg, Object.fromEntries(ASYNC_SIGNALS.map(signal => [signal, sinon.match(arg => arg instanceof Promise)]))) - }) - - it('can resolve multiple igbs', async () => { - igb.cur = 'cur1'; - startParallel(); - spec.code = 'other'; - igb.origin = 'other.buyer' - igb.cur = 'cur2' - startParallel(); - let cfg = getPAAPIConfig().au.componentAuctions[0]; - returnIgb({ - origin: 'mock.buyer', - pbs: {signal: 1} - }); - returnIgb({ - origin: 'other.buyer', - pbs: {signal: 2} - }); - endAuction(); - cfg = await resolveConfig(cfg); - sinon.assert.match(cfg, { - perBuyerSignals: { - 'mock.buyer': {signal: 1}, - 'other.buyer': {signal: 2} - }, - perBuyerCurrencies: { - 'mock.buyer': 'cur1', - 'other.buyer': 'cur2' - } - }) - }) - - function startMultiple() { - startParallel(); - spec.code = 'other'; - igb.origin = 'other.buyer' - startParallel(); - } - - describe('when using separateAuctions=false', () => { - beforeEach(() => { - config.mergeConfig({ - paapi: { - componentSeller: { - separateAuctions: false - } - } - }) - }); - - it('should merge igb from different specs into a single auction config', () => { - startMultiple(); - sinon.assert.match(getPAAPIConfig().au.componentAuctions[0], { - interestGroupBuyers: ['mock.buyer', 'other.buyer'] - }); - }); - }) - - describe('when using separateAuctions=true', () => { - beforeEach(() => { - config.mergeConfig({ - paapi: { - componentSeller: { - separateAuctions: true - } - } - }) - }); - it('should generate an auction config for each bidder', () => { - startMultiple(); - const components = getPAAPIConfig().au.componentAuctions; - sinon.assert.match(components[0], { - interestGroupBuyers: ['mock.buyer'] - }) - sinon.assert.match(components[1], { - interestGroupBuyers: ['other.buyer'] - }) - }) - }) - }) - }) - }); - }); - - describe('ortb processors for fledge', () => { - it('imp.ext.ae should be removed if fledge is not enabled', () => { - const imp = {ext: {ae: 1, igs: {}}}; - setImpExtAe(imp, {}, {bidderRequest: {}}); - expect(imp.ext.ae).to.not.exist; - expect(imp.ext.igs).to.not.exist; - }); - it('imp.ext.ae should be left intact if fledge is enabled', () => { - const imp = {ext: {ae: 2, igs: {biddable: 0}}}; - setImpExtAe(imp, {}, {bidderRequest: {paapi: {enabled: true}}}); - expect(imp.ext).to.eql({ - ae: 2, - igs: { - biddable: 0 - } - }); - }); - - describe('response parsing', () => { - function generateImpCtx(fledgeFlags) { - return Object.fromEntries(Object.entries(fledgeFlags).map(([impid, fledgeEnabled]) => [impid, {imp: {ext: {ae: fledgeEnabled}}}])); - } - - function extractResult(type, ctx) { - return Object.fromEntries( - Object.entries(ctx) - .map(([impid, ctx]) => [impid, ctx.paapiConfigs?.map(cfg => cfg[type].id)]) - .filter(([_, val]) => val != null) - ); - } - - Object.entries({ - 'parseExtPrebidFledge': { - parser: parseExtPrebidFledge, - responses: { - 'ext.prebid.fledge'(configs) { - return { - ext: { - prebid: { - fledge: { - auctionconfigs: configs - } - } - } - }; - }, - } - }, - 'parseExtIgi': { - parser: parseExtIgi, - responses: { - 'ext.igi.igs'(configs) { - return { - ext: { - igi: [{ - igs: configs - }] - } - }; - }, - 'ext.igi.igs with impid on igi'(configs) { - return { - ext: { - igi: configs.map(cfg => { - const impid = cfg.impid; - delete cfg.impid; - return { - impid, - igs: [cfg] - }; - }) - } - }; - }, - 'ext.igi.igs with conflicting impid'(configs) { - return { - ext: { - igi: [{ - impid: 'conflict', - igs: configs - }] - } - }; - } - } - } - }).forEach(([t, {parser, responses}]) => { - describe(t, () => { - Object.entries(responses).forEach(([t, packageConfigs]) => { - describe(`when response uses ${t}`, () => { - function generateCfg(impid, ...ids) { - return ids.map((id) => ({impid, config: {id}})); - } - - it('should collect auction configs by imp', () => { - const ctx = { - impContext: generateImpCtx({e1: 1, e2: 1, d1: 0}) - }; - const resp = packageConfigs( - generateCfg('e1', 1, 2, 3) - .concat(generateCfg('e2', 4) - .concat(generateCfg('d1', 5, 6))) - ); - parser({}, resp, ctx); - expect(extractResult('config', ctx.impContext)).to.eql({ - e1: [1, 2, 3], - e2: [4], - }); - }); - it('should not choke if fledge config references unknown imp', () => { - const ctx = {impContext: generateImpCtx({i: 1})}; - const resp = packageConfigs(generateCfg('unknown', 1)); - parser({}, resp, ctx); - expect(extractResult('config', ctx.impContext)).to.eql({}); - }); - }); - }); - }); - }); - - describe('response ext.igi.igb', () => { - it('should collect igb by imp', () => { - const ctx = { - impContext: generateImpCtx({e1: 1, e2: 1, d1: 0}) - }; - const resp = { - ext: { - igi: [ - { - impid: 'e1', - igb: [ - {id: 1}, - {id: 2} - ] - }, - { - impid: 'e2', - igb: [ - {id: 3} - ] - }, - { - impid: 'd1', - igb: [ - {id: 4} - ] - } - ] - } - }; - parseExtIgi({}, resp, ctx); - expect(extractResult('igb', ctx.impContext)).to.eql({ - e1: [1, 2], - e2: [3], - }); - }); - }); - }); - - describe('setResponsePaapiConfigs', () => { - it('should set paapi configs/igb paired with their corresponding bid id', () => { - const ctx = { - impContext: { - 1: { - bidRequest: {bidId: 'bid1'}, - paapiConfigs: [{config: {id: 1}}, {config: {id: 2}}] - }, - 2: { - bidRequest: {bidId: 'bid2'}, - paapiConfigs: [{config: {id: 3}}] - }, - 3: { - bidRequest: {bidId: 'bid3'} - }, - 4: { - bidRequest: {bidId: 'bid1'}, - paapiConfigs: [{igb: {id: 4}}] - } - } - }; - const resp = {}; - setResponsePaapiConfigs(resp, {}, ctx); - expect(resp.paapi).to.eql([ - {bidId: 'bid1', config: {id: 1}}, - {bidId: 'bid1', config: {id: 2}}, - {bidId: 'bid2', config: {id: 3}}, - {bidId: 'bid1', igb: {id: 4}} - ]); - }); - it('should not set paapi if no config or igb exists', () => { - const resp = {}; - setResponsePaapiConfigs(resp, {}, { - impContext: { - 1: { - paapiConfigs: [] - }, - 2: {} - } - }); - expect(resp).to.eql({}); - }); - }); - }); -}); diff --git a/test/spec/modules/padsquadBidAdapter_spec.js b/test/spec/modules/padsquadBidAdapter_spec.js index 1229ce778ff..73ccd0e2e2c 100644 --- a/test/spec/modules/padsquadBidAdapter_spec.js +++ b/test/spec/modules/padsquadBidAdapter_spec.js @@ -1,5 +1,5 @@ -import {expect} from 'chai'; -import {spec} from 'modules/padsquadBidAdapter.js'; +import { expect } from 'chai'; +import { spec } from 'modules/padsquadBidAdapter.js'; const REQUEST = { 'bidderCode': 'padsquad', @@ -219,7 +219,7 @@ describe('Padsquad bid adapter', function () { }); it('handles empty response', function () { - const EMPTY_RESP = Object.assign({}, RESPONSE, {'body': {}}); + const EMPTY_RESP = Object.assign({}, RESPONSE, { 'body': {} }); const bids = spec.interpretResponse(EMPTY_RESP, REQUEST); expect(bids).to.be.empty; @@ -232,13 +232,13 @@ describe('Padsquad bid adapter', function () { expect(opts).to.be.an('array').that.is.empty; }); it('returns non if sync is not allowed', function () { - const opts = spec.getUserSyncs({iframeEnabled: false, pixelEnabled: false}); + const opts = spec.getUserSyncs({ iframeEnabled: false, pixelEnabled: false }); expect(opts).to.be.an('array').that.is.empty; }); it('iframe sync enabled should return results', function () { - const opts = spec.getUserSyncs({iframeEnabled: true, pixelEnabled: false}, [RESPONSE]); + const opts = spec.getUserSyncs({ iframeEnabled: true, pixelEnabled: false }, [RESPONSE]); expect(opts.length).to.equal(1); expect(opts[0].type).to.equal('iframe'); @@ -246,7 +246,7 @@ describe('Padsquad bid adapter', function () { }); it('pixel sync enabled should return results', function () { - const opts = spec.getUserSyncs({iframeEnabled: false, pixelEnabled: true}, [RESPONSE]); + const opts = spec.getUserSyncs({ iframeEnabled: false, pixelEnabled: true }, [RESPONSE]); expect(opts.length).to.equal(1); expect(opts[0].type).to.equal('image'); @@ -254,7 +254,7 @@ describe('Padsquad bid adapter', function () { }); it('all sync enabled should return all results', function () { - const opts = spec.getUserSyncs({iframeEnabled: true, pixelEnabled: true}, [RESPONSE]); + const opts = spec.getUserSyncs({ iframeEnabled: true, pixelEnabled: true }, [RESPONSE]); expect(opts.length).to.equal(2); }); diff --git a/test/spec/modules/pairIdSystem_spec.js b/test/spec/modules/pairIdSystem_spec.js index 1228100f3f8..ae297ad61ca 100644 --- a/test/spec/modules/pairIdSystem_spec.js +++ b/test/spec/modules/pairIdSystem_spec.js @@ -23,7 +23,7 @@ describe('pairId', function () { sandbox.stub(storage, 'getDataFromLocalStorage').withArgs('pairId').returns(btoa(JSON.stringify(pairIds))); const id = pairIdSubmodule.getId({ params: {} }); - expect(id).to.be.deep.equal({id: pairIds}); + expect(id).to.be.deep.equal({ id: pairIds }); }); it('should read pairId from cookie if exists', function() { @@ -31,39 +31,42 @@ describe('pairId', function () { sandbox.stub(storage, 'getCookie').withArgs('pairId').returns(btoa(JSON.stringify(pairIds))); const id = pairIdSubmodule.getId({ params: {} }); - expect(id).to.be.deep.equal({id: pairIds}); + expect(id).to.be.deep.equal({ id: pairIds }); }); it('should read pairId from default liveramp envelope local storage key if configured', function() { const pairIds = ['test-pair-id1', 'test-pair-id2', 'test-pair-id3']; - sandbox.stub(storage, 'getDataFromLocalStorage').withArgs('_lr_pairId').returns(btoa(JSON.stringify({'envelope': pairIds}))); + sandbox.stub(storage, 'getDataFromLocalStorage').withArgs('_lr_pairId').returns(btoa(JSON.stringify({ 'envelope': pairIds }))); const id = pairIdSubmodule.getId({ params: { liveramp: {} - }}) - expect(id).to.be.deep.equal({id: pairIds}) + } + }) + expect(id).to.be.deep.equal({ id: pairIds }) }) it('should read pairId from default liveramp envelope cookie entry if configured', function() { const pairIds = ['test-pair-id4', 'test-pair-id5', 'test-pair-id6']; - sandbox.stub(storage, 'getDataFromLocalStorage').withArgs('_lr_pairId').returns(btoa(JSON.stringify({'envelope': pairIds}))); + sandbox.stub(storage, 'getDataFromLocalStorage').withArgs('_lr_pairId').returns(btoa(JSON.stringify({ 'envelope': pairIds }))); const id = pairIdSubmodule.getId({ params: { liveramp: {} - }}) - expect(id).to.be.deep.equal({id: pairIds}) + } + }) + expect(id).to.be.deep.equal({ id: pairIds }) }) it('should read pairId from specified liveramp envelope cookie entry if configured with storageKey', function() { const pairIds = ['test-pair-id7', 'test-pair-id8', 'test-pair-id9']; - sandbox.stub(storage, 'getDataFromLocalStorage').withArgs('lr_pairId_custom').returns(btoa(JSON.stringify({'envelope': pairIds}))); + sandbox.stub(storage, 'getDataFromLocalStorage').withArgs('lr_pairId_custom').returns(btoa(JSON.stringify({ 'envelope': pairIds }))); const id = pairIdSubmodule.getId({ params: { liveramp: { storageKey: 'lr_pairId_custom' } - }}) - expect(id).to.be.deep.equal({id: pairIds}) + } + }) + expect(id).to.be.deep.equal({ id: pairIds }) }) it('should not get data from storage if local storage and cookies are disabled', function () { diff --git a/test/spec/modules/panxoBidAdapter_spec.js b/test/spec/modules/panxoBidAdapter_spec.js new file mode 100644 index 00000000000..4ce770aec12 --- /dev/null +++ b/test/spec/modules/panxoBidAdapter_spec.js @@ -0,0 +1,346 @@ +import { expect } from 'chai'; +import { spec, storage } from 'modules/panxoBidAdapter.js'; +import { BANNER } from 'src/mediaTypes.js'; + +describe('PanxoBidAdapter', function () { + const PROPERTY_KEY = 'abc123def456'; + const USER_ID = 'test-user-id-12345'; + + // Mock storage.getDataFromLocalStorage + let getDataStub; + + beforeEach(function () { + getDataStub = sinon.stub(storage, 'getDataFromLocalStorage'); + getDataStub.withArgs('panxo_uid').returns(USER_ID); + }); + + afterEach(function () { + getDataStub.restore(); + }); + + describe('isBidRequestValid', function () { + it('should return true when propertyKey is present', function () { + const bid = { + bidder: 'panxo', + params: { propertyKey: PROPERTY_KEY }, + mediaTypes: { banner: { sizes: [[300, 250]] } } + }; + expect(spec.isBidRequestValid(bid)).to.be.true; + }); + + it('should return false when propertyKey is missing', function () { + const bid = { + bidder: 'panxo', + params: {}, + mediaTypes: { banner: { sizes: [[300, 250]] } } + }; + expect(spec.isBidRequestValid(bid)).to.be.false; + }); + + it('should return false when banner mediaType is missing', function () { + const bid = { + bidder: 'panxo', + params: { propertyKey: PROPERTY_KEY }, + mediaTypes: { video: {} } + }; + expect(spec.isBidRequestValid(bid)).to.be.false; + }); + }); + + describe('buildRequests', function () { + const bidderRequest = { + bidderRequestId: 'test-request-id', + auctionId: 'test-auction-id', + timeout: 1500, + refererInfo: { + page: 'https://example.com/page', + domain: 'example.com', + ref: 'https://google.com' + } + }; + + const validBidRequests = [{ + bidder: 'panxo', + bidId: 'bid-id-1', + adUnitCode: 'ad-unit-1', + params: { propertyKey: PROPERTY_KEY }, + mediaTypes: { banner: { sizes: [[300, 250], [728, 90]] } } + }]; + + it('should build a valid OpenRTB request', function () { + const requests = spec.buildRequests(validBidRequests, bidderRequest); + + expect(requests).to.be.an('array').with.lengthOf(1); + expect(requests[0].method).to.equal('POST'); + expect(requests[0].url).to.include('panxo-sys.com/openrtb/2.5/bid'); + expect(requests[0].url).to.include(`key=${PROPERTY_KEY}`); + expect(requests[0].data).to.be.an('object'); + }); + + it('should include user.buyeruid from localStorage', function () { + const requests = spec.buildRequests(validBidRequests, bidderRequest); + + expect(requests[0].data.user).to.be.an('object'); + expect(requests[0].data.user.buyeruid).to.equal(USER_ID); + }); + + it('should build correct impressions', function () { + const requests = spec.buildRequests(validBidRequests, bidderRequest); + + expect(requests[0].data.imp).to.be.an('array'); + expect(requests[0].data.imp[0].id).to.equal('bid-id-1'); + expect(requests[0].data.imp[0].banner.format).to.have.lengthOf(2); + expect(requests[0].data.imp[0].tagid).to.equal('ad-unit-1'); + }); + + it('should return empty array when panxo_uid is not found', function () { + getDataStub.withArgs('panxo_uid').returns(null); + const requests = spec.buildRequests(validBidRequests, bidderRequest); + + expect(requests).to.be.an('array').that.is.empty; + }); + + it('should include GDPR consent when gdprApplies is true', function () { + const gdprBidderRequest = { + ...bidderRequest, + gdprConsent: { + gdprApplies: true, + consentString: 'CO-test-consent-string' + } + }; + const requests = spec.buildRequests(validBidRequests, gdprBidderRequest); + + expect(requests[0].data.regs.ext.gdpr).to.equal(1); + expect(requests[0].data.user.ext.consent).to.equal('CO-test-consent-string'); + }); + + it('should not include gdpr flag when gdprApplies is undefined', function () { + const gdprBidderRequest = { + ...bidderRequest, + gdprConsent: { + gdprApplies: undefined, + consentString: 'CO-test-consent-string' + } + }; + const requests = spec.buildRequests(validBidRequests, gdprBidderRequest); + + expect(requests[0].data.regs.ext.gdpr).to.be.undefined; + expect(requests[0].data.user.ext.consent).to.equal('CO-test-consent-string'); + }); + + it('should include USP consent when available', function () { + const uspBidderRequest = { + ...bidderRequest, + uspConsent: '1YNN' + }; + const requests = spec.buildRequests(validBidRequests, uspBidderRequest); + + expect(requests[0].data.regs.ext.us_privacy).to.equal('1YNN'); + }); + + it('should include schain when available', function () { + const schainBidderRequest = { + ...bidderRequest, + ortb2: { + source: { + ext: { + schain: { + ver: '1.0', + complete: 1, + nodes: [{ asi: 'example.com', sid: '12345', hp: 1 }] + } + } + } + } + }; + const requests = spec.buildRequests(validBidRequests, schainBidderRequest); + + expect(requests[0].data.source.ext.schain).to.deep.equal(schainBidderRequest.ortb2.source.ext.schain); + }); + + it('should use floor from getFloor function', function () { + const bidWithFloor = [{ + ...validBidRequests[0], + getFloor: () => ({ currency: 'USD', floor: 1.50 }) + }]; + const requests = spec.buildRequests(bidWithFloor, bidderRequest); + + expect(requests[0].data.imp[0].bidfloor).to.equal(1.50); + }); + + it('should include full ortb2Imp object in impression', function () { + const bidWithOrtb2Imp = [{ + ...validBidRequests[0], + ortb2Imp: { + instl: 1, + ext: { data: { customField: 'value' } } + } + }]; + const requests = spec.buildRequests(bidWithOrtb2Imp, bidderRequest); + + expect(requests[0].data.imp[0].instl).to.equal(1); + expect(requests[0].data.imp[0].ext.data.customField).to.equal('value'); + }); + + it('should split requests by different propertyKeys', function () { + const multiPropertyBids = [ + { + bidder: 'panxo', + bidId: 'bid-id-1', + adUnitCode: 'ad-unit-1', + params: { propertyKey: 'property-a' }, + mediaTypes: { banner: { sizes: [[300, 250]] } } + }, + { + bidder: 'panxo', + bidId: 'bid-id-2', + adUnitCode: 'ad-unit-2', + params: { propertyKey: 'property-b' }, + mediaTypes: { banner: { sizes: [[728, 90]] } } + } + ]; + const requests = spec.buildRequests(multiPropertyBids, bidderRequest); + + expect(requests).to.have.lengthOf(2); + expect(requests[0].url).to.include('key=property-a'); + expect(requests[1].url).to.include('key=property-b'); + }); + }); + + describe('interpretResponse', function () { + const request = { + bidderRequest: { + bids: [{ bidId: 'bid-id-1', adUnitCode: 'ad-unit-1' }] + } + }; + + const serverResponse = { + body: { + id: 'response-id', + seatbid: [{ + seat: 'panxo', + bid: [{ + impid: 'bid-id-1', + price: 2.50, + w: 300, + h: 250, + adm: '
Ad creative
', + crid: 'creative-123', + adomain: ['advertiser.com'], + nurl: 'https://panxo-sys.com/win?price=${AUCTION_PRICE}' + }] + }], + cur: 'USD' + } + }; + + it('should parse valid bid response', function () { + const bids = spec.interpretResponse(serverResponse, request); + + expect(bids).to.have.lengthOf(1); + expect(bids[0].requestId).to.equal('bid-id-1'); + expect(bids[0].cpm).to.equal(2.50); + expect(bids[0].width).to.equal(300); + expect(bids[0].height).to.equal(250); + expect(bids[0].currency).to.equal('USD'); + expect(bids[0].netRevenue).to.be.true; + expect(bids[0].ad).to.equal('
Ad creative
'); + expect(bids[0].meta.advertiserDomains).to.include('advertiser.com'); + }); + + it('should return empty array for empty response', function () { + const emptyResponse = { body: {} }; + const bids = spec.interpretResponse(emptyResponse, request); + + expect(bids).to.be.an('array').that.is.empty; + }); + + it('should return empty array for no seatbid', function () { + const noSeatbidResponse = { body: { id: 'test', seatbid: [] } }; + const bids = spec.interpretResponse(noSeatbidResponse, request); + + expect(bids).to.be.an('array').that.is.empty; + }); + + it('should include nurl in bid response', function () { + const bids = spec.interpretResponse(serverResponse, request); + + expect(bids[0].nurl).to.include('panxo-sys.com/win'); + }); + }); + + describe('getUserSyncs', function () { + it('should return pixel sync when enabled', function () { + const syncOptions = { pixelEnabled: true }; + const syncs = spec.getUserSyncs(syncOptions); + + expect(syncs).to.have.lengthOf(1); + expect(syncs[0].type).to.equal('image'); + expect(syncs[0].url).to.include('panxo-sys.com/usersync'); + }); + + it('should return empty array when pixel sync disabled', function () { + const syncOptions = { pixelEnabled: false }; + const syncs = spec.getUserSyncs(syncOptions); + + expect(syncs).to.be.an('array').that.is.empty; + }); + + it('should include GDPR params when gdprApplies is true', function () { + const syncOptions = { pixelEnabled: true }; + const gdprConsent = { gdprApplies: true, consentString: 'test-consent' }; + const syncs = spec.getUserSyncs(syncOptions, [], gdprConsent); + + expect(syncs[0].url).to.include('gdpr=1'); + expect(syncs[0].url).to.include('gdpr_consent=test-consent'); + }); + + it('should not include gdpr flag when gdprApplies is undefined', function () { + const syncOptions = { pixelEnabled: true }; + const gdprConsent = { gdprApplies: undefined, consentString: 'test-consent' }; + const syncs = spec.getUserSyncs(syncOptions, [], gdprConsent); + + expect(syncs[0].url).to.not.include('gdpr='); + expect(syncs[0].url).to.include('gdpr_consent=test-consent'); + }); + + it('should include USP params when available', function () { + const syncOptions = { pixelEnabled: true }; + const uspConsent = '1YNN'; + const syncs = spec.getUserSyncs(syncOptions, [], null, uspConsent); + + expect(syncs[0].url).to.include('us_privacy=1YNN'); + }); + }); + + describe('onBidWon', function () { + it('should fire win notification pixel', function () { + const bid = { + nurl: 'https://panxo-sys.com/win?price=${AUCTION_PRICE}', + cpm: 2.50 + }; + + // Mock document.createElement + const imgStub = { src: '', style: {} }; + const createElementStub = sinon.stub(document, 'createElement').returns(imgStub); + const appendChildStub = sinon.stub(document.body, 'appendChild'); + + spec.onBidWon(bid); + + expect(imgStub.src).to.include('price=2.5'); + + createElementStub.restore(); + appendChildStub.restore(); + }); + }); + + describe('spec properties', function () { + it('should have correct bidder code', function () { + expect(spec.code).to.equal('panxo'); + }); + + it('should support banner media type', function () { + expect(spec.supportedMediaTypes).to.include(BANNER); + }); + }); +}); diff --git a/test/spec/modules/panxoRtdProvider_spec.js b/test/spec/modules/panxoRtdProvider_spec.js new file mode 100644 index 00000000000..d3e0cbe12bf --- /dev/null +++ b/test/spec/modules/panxoRtdProvider_spec.js @@ -0,0 +1,311 @@ +import { loadExternalScriptStub } from 'test/mocks/adloaderStub.js'; + +import * as utils from '../../../src/utils.js'; +import * as hook from '../../../src/hook.js'; +import * as refererDetection from '../../../src/refererDetection.js'; + +import { __TEST__ } from '../../../modules/panxoRtdProvider.js'; + +const { + SUBMODULE_NAME, + SCRIPT_URL, + main, + load, + onImplLoaded, + onImplMessage, + onGetBidRequestData, + flushPendingCallbacks +} = __TEST__; + +describe('panxo RTD module', function () { + let sandbox; + + const stubUuid = 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee'; + const panxoBridgeId = `panxo_${stubUuid}`; + const stubWindow = { [panxoBridgeId]: undefined }; + + const validSiteId = 'a1b2c3d4e5f67890'; + + beforeEach(function() { + sandbox = sinon.createSandbox(); + sandbox.stub(utils, 'getWindowSelf').returns(stubWindow); + sandbox.stub(utils, 'generateUUID').returns(stubUuid); + sandbox.stub(refererDetection, 'getRefererInfo').returns({ domain: 'example.com' }); + }); + afterEach(function() { + sandbox.restore(); + }); + + describe('Initialization step', function () { + let sandbox2; + let connectSpy; + beforeEach(function() { + sandbox2 = sinon.createSandbox(); + connectSpy = sandbox.spy(); + // Simulate: once the impl script is loaded, it registers the bridge API + sandbox2.stub(stubWindow, panxoBridgeId).value({ connect: connectSpy }); + }); + afterEach(function () { + sandbox2.restore(); + }); + + it('should accept valid configuration with siteId', function () { + expect(() => load({ params: { siteId: validSiteId } })).to.not.throw(); + }); + + it('should throw an Error when siteId is missing', function () { + expect(() => load({})).to.throw(); + expect(() => load({ params: {} })).to.throw(); + expect(() => load({ params: { siteId: '' } })).to.throw(); + }); + + it('should throw an Error when siteId is not a valid hex string', function () { + expect(() => load({ params: { siteId: 'abc' } })).to.throw(); + expect(() => load({ params: { siteId: 123 } })).to.throw(); + expect(() => load({ params: { siteId: 'zzzzzzzzzzzzzzzz' } })).to.throw(); + expect(() => load({ params: { siteId: 'a1b2c3d4e5f6789' } })).to.throw(); // 15 chars + expect(() => load({ params: { siteId: 'a1b2c3d4e5f678901' } })).to.throw(); // 17 chars + }); + + it('should insert implementation script with correct URL', () => { + load({ params: { siteId: validSiteId } }); + + expect(loadExternalScriptStub.calledOnce).to.be.true; + + const args = loadExternalScriptStub.getCall(0).args; + expect(args[0]).to.be.equal( + `${SCRIPT_URL}?siteId=${validSiteId}&session=${stubUuid}&r=example.com` + ); + expect(args[2]).to.be.equal(SUBMODULE_NAME); + expect(args[3]).to.be.equal(onImplLoaded); + }); + + it('should connect to the implementation script once it loads', function () { + load({ params: { siteId: validSiteId } }); + + expect(loadExternalScriptStub.calledOnce).to.be.true; + expect(connectSpy.calledOnce).to.be.true; + + const args = connectSpy.getCall(0).args; + expect(args[0]).to.haveOwnProperty('cmd'); // pbjs global + expect(args[0]).to.haveOwnProperty('que'); + expect(args[1]).to.be.equal(onImplMessage); + }); + + it('should flush pending callbacks when bridge is unavailable', function () { + sandbox2.restore(); + // Bridge is not registered on the window -- onImplLoaded should fail open + sandbox2 = sinon.createSandbox(); + sandbox2.stub(stubWindow, panxoBridgeId).value(undefined); + + load({ params: { siteId: validSiteId } }); + + const callbackSpy = sandbox2.spy(); + const reqBidsConfig = { ortb2Fragments: { bidder: {}, global: {} } }; + + // Queue a callback before bridge fails + onGetBidRequestData(reqBidsConfig, callbackSpy, { params: {} }, {}); + // onImplLoaded already ran (bridge undefined) and flushed + expect(callbackSpy.calledOnce).to.be.true; + }); + + it('should not throw when bridge message is null', function () { + load({ params: { siteId: validSiteId } }); + expect(() => onImplMessage(null)).to.not.throw(); + expect(() => onImplMessage(undefined)).to.not.throw(); + }); + }); + + describe('Bid enrichment step', function () { + const signalData = { + device: { v1: 'session-token-123' }, + site: { ai: true, src: 'chatgpt', conf: 0.95, seg: 'technology', co: 'US' } + }; + + let sandbox2; + let callbackSpy; + let reqBidsConfig; + beforeEach(function() { + sandbox2 = sinon.createSandbox(); + callbackSpy = sandbox2.spy(); + reqBidsConfig = { ortb2Fragments: { bidder: {}, global: {} } }; + // Prevent onImplLoaded from firing automatically so tests can + // control module readiness via onImplMessage directly. + loadExternalScriptStub.callsFake(() => {}); + }); + afterEach(function () { + loadExternalScriptStub.reset(); + sandbox2.restore(); + }); + + it('should defer callback when implementation has not sent a signal yet', () => { + load({ params: { siteId: validSiteId } }); + + onGetBidRequestData(reqBidsConfig, callbackSpy, { params: {} }, {}); + + // Callback is deferred until the implementation script sends its first signal + expect(callbackSpy.notCalled).to.be.true; + }); + + it('should flush deferred callback once a signal arrives', () => { + load({ params: { siteId: validSiteId } }); + + onGetBidRequestData(reqBidsConfig, callbackSpy, { params: {} }, {}); + expect(callbackSpy.notCalled).to.be.true; + + // First signal arrives -- deferred callback should now fire + onImplMessage({ type: 'signal', data: { device: { v1: 'tok' }, site: {} } }); + expect(callbackSpy.calledOnce).to.be.true; + }); + + it('should flush all deferred callbacks when the first signal arrives', () => { + load({ params: { siteId: validSiteId } }); + + const spy1 = sandbox2.spy(); + const spy2 = sandbox2.spy(); + const cfg1 = { ortb2Fragments: { bidder: {}, global: {} } }; + const cfg2 = { ortb2Fragments: { bidder: {}, global: {} } }; + + onGetBidRequestData(cfg1, spy1, { params: {} }, {}); + onGetBidRequestData(cfg2, spy2, { params: {} }, {}); + expect(spy1.notCalled).to.be.true; + expect(spy2.notCalled).to.be.true; + + onImplMessage({ type: 'signal', data: { device: { v1: 'tok' }, site: {} } }); + expect(spy1.calledOnce).to.be.true; + expect(spy2.calledOnce).to.be.true; + }); + + it('should call callback immediately once implementation is ready', () => { + load({ params: { siteId: validSiteId } }); + + // Mark implementation as ready via a signal + onImplMessage({ type: 'signal', data: { device: { v1: 'tok' }, site: {} } }); + + // Subsequent calls should resolve immediately + onGetBidRequestData(reqBidsConfig, callbackSpy, { params: {} }, {}); + expect(callbackSpy.calledOnce).to.be.true; + }); + + it('should call callback when implementation reports an error', () => { + load({ params: { siteId: validSiteId } }); + + onGetBidRequestData(reqBidsConfig, callbackSpy, { params: {} }, {}); + expect(callbackSpy.notCalled).to.be.true; + + // An error still unblocks the auction + onImplMessage({ type: 'error', data: 'some error' }); + expect(callbackSpy.calledOnce).to.be.true; + // No device or site should be added since panxoData is empty + }); + + it('should add device.ext.panxo with session token when signal is received', () => { + load({ params: { siteId: validSiteId } }); + + onImplMessage({ type: 'signal', data: signalData }); + onGetBidRequestData(reqBidsConfig, callbackSpy, { params: {} }, {}); + + expect(callbackSpy.calledOnce).to.be.true; + expect(reqBidsConfig.ortb2Fragments.global).to.have.own.property('device'); + expect(reqBidsConfig.ortb2Fragments.global.device).to.have.own.property('ext'); + expect(reqBidsConfig.ortb2Fragments.global.device.ext).to.have.own.property('panxo') + .which.is.an('object') + .that.deep.equals(signalData.device); + }); + + it('should add site.ext.data.panxo with AI classification data', () => { + load({ params: { siteId: validSiteId } }); + + onImplMessage({ type: 'signal', data: signalData }); + onGetBidRequestData(reqBidsConfig, callbackSpy, { params: {} }, {}); + + expect(callbackSpy.calledOnce).to.be.true; + expect(reqBidsConfig.ortb2Fragments.global).to.have.own.property('site'); + expect(reqBidsConfig.ortb2Fragments.global.site).to.have.own.property('ext'); + expect(reqBidsConfig.ortb2Fragments.global.site.ext).to.have.own.property('data'); + expect(reqBidsConfig.ortb2Fragments.global.site.ext.data).to.have.own.property('panxo') + .which.is.an('object') + .that.deep.equals(signalData.site); + }); + + it('should update panxo data when new signal is received', () => { + load({ params: { siteId: validSiteId } }); + + const updatedData = { + device: { v1: 'updated-token' }, + site: { ai: true, src: 'perplexity', conf: 0.88, seg: 'finance', co: 'UK' } + }; + + onImplMessage({ type: 'signal', data: { device: { v1: 'old-token' }, site: {} } }); + onImplMessage({ type: 'signal', data: updatedData }); + onGetBidRequestData(reqBidsConfig, callbackSpy, { params: {} }, {}); + + expect(callbackSpy.calledOnce).to.be.true; + expect(reqBidsConfig.ortb2Fragments.global.device.ext.panxo) + .to.deep.equal(updatedData.device); + expect(reqBidsConfig.ortb2Fragments.global.site.ext.data.panxo) + .to.deep.equal(updatedData.site); + }); + + it('should not add site data when site object is empty', () => { + load({ params: { siteId: validSiteId } }); + + onImplMessage({ type: 'signal', data: { device: { v1: 'token' }, site: {} } }); + onGetBidRequestData(reqBidsConfig, callbackSpy, { params: {} }, {}); + + expect(callbackSpy.calledOnce).to.be.true; + expect(reqBidsConfig.ortb2Fragments.global.device.ext.panxo) + .to.deep.equal({ v1: 'token' }); + // site should not have panxo data since it was empty + expect(reqBidsConfig.ortb2Fragments.global).to.not.have.own.property('site'); + }); + }); + + describe('Submodule execution', function() { + let sandbox2; + let submoduleStub; + beforeEach(function() { + sandbox2 = sinon.createSandbox(); + submoduleStub = sandbox2.stub(hook, 'submodule'); + }); + afterEach(function () { + sandbox2.restore(); + }); + + function getModule() { + main(); + + expect(submoduleStub.calledOnceWith('realTimeData')).to.equal(true); + + const submoduleDef = submoduleStub.getCall(0).args[1]; + expect(submoduleDef).to.be.an('object'); + expect(submoduleDef).to.have.own.property('name', SUBMODULE_NAME); + expect(submoduleDef).to.have.own.property('init').that.is.a('function'); + expect(submoduleDef).to.have.own.property('getBidRequestData').that.is.a('function'); + + return submoduleDef; + } + + it('should register panxo RTD submodule provider', function () { + getModule(); + }); + + it('should refuse initialization when siteId is missing', function () { + const { init } = getModule(); + expect(init({ params: {} })).to.equal(false); + expect(loadExternalScriptStub.notCalled).to.be.true; + }); + + it('should refuse initialization when siteId is invalid', function () { + const { init } = getModule(); + expect(init({ params: { siteId: 'invalid' } })).to.equal(false); + expect(loadExternalScriptStub.notCalled).to.be.true; + }); + + it('should commence initialization with valid siteId', function () { + const { init } = getModule(); + expect(init({ params: { siteId: validSiteId } })).to.equal(true); + expect(loadExternalScriptStub.calledOnce).to.be.true; + }); + }); +}); diff --git a/test/spec/modules/performaxBidAdapter_spec.js b/test/spec/modules/performaxBidAdapter_spec.js index 218f9402e75..f26024eb47d 100644 --- a/test/spec/modules/performaxBidAdapter_spec.js +++ b/test/spec/modules/performaxBidAdapter_spec.js @@ -1,5 +1,8 @@ import { expect } from 'chai'; -import { spec, converter } from 'modules/performaxBidAdapter.js'; +import { spec, converter, storeData, readData, storage, resetUserSyncsInit } from 'modules/performaxBidAdapter.js'; +import * as utils from '../../../src/utils.js'; +import * as ajax from 'src/ajax.js'; +import sinon from 'sinon'; describe('Performax adapter', function () { const bids = [{ @@ -14,7 +17,9 @@ describe('Performax adapter', function () { banner: { sizes: [ [300, 300], - ]}}, + ] + } + }, adUnitCode: 'postbid_iframe', transactionId: '84deda92-e9ba-4b0d-a797-43be5e522430', adUnitId: '4ee4643b-931f-4a17-a571-ccba57886dc8', @@ -47,7 +52,9 @@ describe('Performax adapter', function () { banner: { sizes: [ [300, 600], - ]}}, + ] + } + }, adUnitCode: 'postbid_halfpage_iframe', transactionId: '84deda92-e9ba-4b0d-a797-43be5e522430', adUnitId: '4ee4643b-931f-4a17-a571-ccba57886dc8', @@ -65,7 +72,8 @@ describe('Performax adapter', function () { source: {}, site: {}, device: {} - }}]; + } + }]; const bidderRequest = { bidderCode: 'performax2', @@ -77,7 +85,8 @@ describe('Performax adapter', function () { regs: { ext: { gdpr: 1 - }}, + } + }, user: { ext: { consent: 'consent-string' @@ -85,7 +94,8 @@ describe('Performax adapter', function () { }, site: {}, device: {} - }}; + } + }; const serverResponse = { body: { @@ -101,25 +111,77 @@ describe('Performax adapter', function () { h: 300, adm: 'My ad' } - ]}]}, + ] + }] + }, } describe('isBidRequestValid', function () { const bid = {}; it('should return false when missing "tagid" param', function() { - bid.params = {slotId: 'param'}; + bid.params = { slotId: 'param' }; expect(spec.isBidRequestValid(bid)).to.equal(false); bid.params = {}; expect(spec.isBidRequestValid(bid)).to.equal(false); }); it('should return true when tagid is correct', function() { - bid.params = {tagid: 'sample'}; + bid.params = { tagid: 'sample' }; expect(spec.isBidRequestValid(bid)).to.equal(true); }); }) describe('buildRequests', function () { + let sandbox; + + beforeEach(() => { + sandbox = sinon.createSandbox(); + }); + + afterEach(() => { + sandbox.restore(); + }); + + it('should inject stored UIDs into user.ext.uids if they exist', function() { + sandbox.stub(storage, 'localStorageIsEnabled').returns(true); + sandbox.stub(storage, 'getDataFromLocalStorage') + .withArgs('px_uids') // BIDDER_SHORT_CODE + '_uids' + .returns(JSON.stringify({ someVendor: '12345' })); + + const requests = spec.buildRequests(bids, bidderRequest); + const data = requests[0].data; + + expect(data.user).to.exist; + expect(data.user.ext).to.exist; + expect(data.user.ext.uids).to.deep.include({ someVendor: '12345' }); + }); + + it('should merge stored UIDs with existing user.ext.uids (preserving existing)', function() { + sandbox.stub(storage, 'localStorageIsEnabled').returns(true); + sandbox.stub(storage, 'getDataFromLocalStorage') + .withArgs('px_uids') + .returns(JSON.stringify({ storedVendor: 'storedId' })); + + const requestWithUids = { + ...bidderRequest, + ortb2: { + user: { + ext: { + uids: { existingVendor: 'existingId' } + } + } + } + }; + + const requests = spec.buildRequests(bids, requestWithUids); + const data = requests[0].data; + + expect(data.user.ext.uids).to.deep.equal({ + existingVendor: 'existingId', + storedVendor: 'storedId' + }); + }); + it('should set correct request method and url', function () { const requests = spec.buildRequests([bids[0]], bidderRequest); expect(requests).to.be.an('array').that.has.lengthOf(1); @@ -131,31 +193,36 @@ describe('Performax adapter', function () { it('should pass correct imp', function () { const requests = spec.buildRequests([bids[0]], bidderRequest); - const {data} = requests[0]; - const {imp} = data; + const { data } = requests[0]; + const { imp } = data; expect(imp).to.be.an('array').that.has.lengthOf(1); expect(imp[0]).to.be.an('object'); const bid = imp[0]; expect(bid.id).to.equal('2bc545c347dbbe'); - expect(bid.banner).to.deep.equal({topframe: 0, format: [{w: 300, h: 300}]}); + expect(bid.banner).to.deep.equal({ topframe: 0, format: [{ w: 300, h: 300 }] }); }); it('should process multiple bids', function () { const requests = spec.buildRequests(bids, bidderRequest); expect(requests).to.be.an('array').that.has.lengthOf(1); - const {data} = requests[0]; - const {imp} = data; + const { data } = requests[0]; + const { imp } = data; expect(imp).to.be.an('array').that.has.lengthOf(bids.length); const bid1 = imp[0]; - expect(bid1.banner).to.deep.equal({topframe: 0, format: [{w: 300, h: 300}]}); + expect(bid1.banner).to.deep.equal({ topframe: 0, format: [{ w: 300, h: 300 }] }); const bid2 = imp[1]; - expect(bid2.banner).to.deep.equal({topframe: 0, format: [{w: 300, h: 600}]}); + expect(bid2.banner).to.deep.equal({ topframe: 0, format: [{ w: 300, h: 600 }] }); }); }); describe('interpretResponse', function () { + it('should return an empty array if the response body is missing', function () { + const result = spec.interpretResponse({}, {}); + expect(result).to.deep.equal([]); + }); + it('should map params correctly', function () { - const ortbRequest = {data: converter.toORTB({bidderRequest, bids})}; + const ortbRequest = { data: converter.toORTB({ bidderRequest, bids }) }; serverResponse.body.id = ortbRequest.data.id; serverResponse.body.seatbid[0].bid[0].imp_id = ortbRequest.data.imp[0].id; @@ -172,4 +239,299 @@ describe('Performax adapter', function () { expect(bid.creativeId).to.equal('sample'); }); }); + + describe('Storage Helpers', () => { + let sandbox; + let logWarnSpy; + let logErrorSpy; + + beforeEach(() => { + sandbox = sinon.createSandbox(); + sandbox.stub(storage, 'localStorageIsEnabled'); + sandbox.stub(storage, 'setDataInLocalStorage'); + sandbox.stub(storage, 'getDataFromLocalStorage'); + sandbox.stub(utils, 'debugTurnedOn').returns(true); + + logWarnSpy = sandbox.stub(utils, 'logWarn'); + logErrorSpy = sandbox.stub(utils, 'logError'); + }); + + afterEach(() => { + sandbox.restore(); + }); + + describe('storeData', () => { + it('should store serialized data when local storage is enabled', () => { + storage.localStorageIsEnabled.returns(true); + const testData = { foo: 'bar' }; + + storeData('testKey', testData); + + sandbox.assert.calledWithExactly( + storage.setDataInLocalStorage, + 'testKey', + JSON.stringify(testData) + ); + }); + + it('should log a warning and exit if local storage is disabled', () => { + storage.localStorageIsEnabled.returns(false); + + storeData('testKey', { foo: 'bar' }); + + expect(storage.setDataInLocalStorage.called).to.be.false; + sandbox.assert.calledOnce(logWarnSpy); + }); + + it('should log an error if setDataInLocalStorage throws', () => { + storage.localStorageIsEnabled.returns(true); + storage.setDataInLocalStorage.throws(new Error('QuotaExceeded')); + + storeData('testKey', 'someValue'); + + sandbox.assert.calledOnce(logErrorSpy); + }); + }); + + describe('readData', () => { + it('should return parsed data when it exists in storage', () => { + storage.localStorageIsEnabled.returns(true); + const mockValue = { id: 123 }; + storage.getDataFromLocalStorage.withArgs('myKey').returns(JSON.stringify(mockValue)); + + const result = readData('myKey', {}); + + expect(result).to.deep.equal(mockValue); + }); + + it('should return defaultValue if local storage is disabled', () => { + storage.localStorageIsEnabled.returns(false); + const defaultValue = { status: 'default' }; + + const result = readData('myKey', defaultValue); + + expect(result).to.equal(defaultValue); + sandbox.assert.calledOnce(logWarnSpy); + }); + + it('should return defaultValue if the key does not exist (returns null)', () => { + storage.localStorageIsEnabled.returns(true); + storage.getDataFromLocalStorage.returns(null); + + const result = readData('missingKey', 'fallback'); + + expect(result).to.equal('fallback'); + }); + + it('should return defaultValue when stored value is a JSON primitive', () => { + storage.localStorageIsEnabled.returns(true); + const defaultValue = { fallback: true }; + + storage.getDataFromLocalStorage.returns('"hello"'); + expect(readData('k', defaultValue)).to.deep.equal(defaultValue); + + storage.getDataFromLocalStorage.returns('42'); + expect(readData('k', defaultValue)).to.deep.equal(defaultValue); + + storage.getDataFromLocalStorage.returns('true'); + expect(readData('k', defaultValue)).to.deep.equal(defaultValue); + }); + + it('should return defaultValue when stored value is a JSON array', () => { + storage.localStorageIsEnabled.returns(true); + const defaultValue = { fallback: true }; + storage.getDataFromLocalStorage.returns('[1,2]'); + + expect(readData('k', defaultValue)).to.deep.equal(defaultValue); + }); + + it('should return defaultValue and log an error if JSON is malformed', () => { + storage.localStorageIsEnabled.returns(true); + storage.getDataFromLocalStorage.returns('not-valid-json{'); + + const result = readData('badKey', { error: true }); + + expect(result).to.deep.equal({ error: true }); + sandbox.assert.calledOnce(logErrorSpy); + }); + }); + }); + + describe('logging', function () { + let ajaxStub; + let randomStub; + + beforeEach(() => { + ajaxStub = sinon.stub(ajax, 'ajax'); + randomStub = sinon.stub(Math, 'random').returns(0); + }); + + afterEach(() => { + ajaxStub.restore(); + randomStub.restore(); + }); + + it('should call ajax when onTimeout is triggered', function () { + const timeoutData = [{ bidId: '123' }]; + spec.onTimeout(timeoutData); + + expect(ajaxStub.calledOnce).to.be.true; + + const [url, callback, data, options] = ajaxStub.firstCall.args; + const parsedData = JSON.parse(data); + + expect(parsedData.type).to.equal('timeout'); + expect(parsedData.payload).to.deep.equal(timeoutData); + expect(options.method).to.equal('POST'); + }); + + it('should call ajax when onBidderError is triggered', function () { + const errorData = { bidderRequest: { some: 'data' } }; + spec.onBidderError(errorData); + + expect(ajaxStub.calledOnce).to.be.true; + + const [url, callback, data] = ajaxStub.firstCall.args; + const parsedData = JSON.parse(data); + + expect(parsedData.type).to.equal('bidderError'); + expect(parsedData.payload).to.deep.equal(errorData.bidderRequest); + }); + + it('should NOT call ajax if sampling logic fails', function () { + randomStub.returns(1.1); + + spec.onTimeout({}); + expect(ajaxStub.called).to.be.false; + }); + + it('should call ajax with correct type "intervention"', function () { + const bidData = { bidId: 'abc' }; + spec.onIntervention({ bid: bidData }); + + expect(ajaxStub.calledOnce).to.be.true; + const [url, callback, data] = ajaxStub.firstCall.args; + const parsed = JSON.parse(data); + + expect(parsed.type).to.equal('intervention'); + expect(parsed.payload).to.deep.equal(bidData); + }); + }); + + describe('getUserSyncs', function () { + let sandbox; + let logWarnSpy; + + beforeEach(() => { + sandbox = sinon.createSandbox(); + sandbox.stub(utils, 'debugTurnedOn').returns(true); + logWarnSpy = sandbox.stub(utils, 'logWarn'); + sandbox.stub(storage, 'localStorageIsEnabled').returns(true); + sandbox.stub(storage, 'setDataInLocalStorage'); + sandbox.stub(storage, 'getDataFromLocalStorage').returns(null); + resetUserSyncsInit(); + }); + + afterEach(() => { + sandbox.restore(); + }); + + it('should return empty array and log warning if iframeEnabled is false', function () { + const syncs = spec.getUserSyncs({ iframeEnabled: false }); + expect(syncs).to.deep.equal([]); + expect(logWarnSpy.calledOnce).to.be.true; + }); + + it('should return correct iframe sync url without GDPR', function () { + const syncs = spec.getUserSyncs({ iframeEnabled: true }); + expect(syncs).to.have.lengthOf(1); + expect(syncs[0].type).to.equal('iframe'); + expect(syncs[0].url).to.equal('https://cdn.performax.cz/px2/cookie_sync_bundle.html'); + }); + + it('should append GDPR params when gdprApplies is a boolean', function () { + const consent = { gdprApplies: true, consentString: 'abc' }; + const syncs = spec.getUserSyncs({ iframeEnabled: true }, [], consent); + + expect(syncs[0].url).to.include('?gdpr=1&gdpr_consent=abc'); + }); + + it('should append GDPR params when gdprApplies is undefined/non-boolean', function () { + const consent = { gdprApplies: undefined, consentString: 'abc' }; + const syncs = spec.getUserSyncs({ iframeEnabled: true }, [], consent); + + expect(syncs[0].url).to.include('?gdpr_consent=abc'); + }); + + describe('PostMessage Listener', function () { + it('should store data when valid message is received', function () { + const addEventListenerStub = sandbox.stub(window, 'addEventListener'); + spec.getUserSyncs({ iframeEnabled: true }); + expect(addEventListenerStub.calledWith('message')).to.be.true; + const callback = addEventListenerStub.args.find(arg => arg[0] === 'message')[1]; + + const mockEvent = { + origin: 'https://cdn.performax.cz', + data: { + flexo_sync_cookie: { + uid: 'user123', + vendor: 'vendorXYZ' + } + } + }; + + callback(mockEvent); + + expect(storage.setDataInLocalStorage.calledOnce).to.be.true; + + const [key, value] = storage.setDataInLocalStorage.firstCall.args; + expect(key).to.equal('px_uids'); + expect(JSON.parse(value)).to.deep.equal({ + vendorXYZ: 'user123' + }); + }); + + it('should ignore messages from invalid origins', function () { + const addEventListenerStub = sandbox.stub(window, 'addEventListener'); + spec.getUserSyncs({ iframeEnabled: true }); + + const callback = addEventListenerStub.args.find(arg => arg[0] === 'message')[1]; + + const mockEvent = { + origin: 'https://not.cdn.performax.cz', + data: { flexo_sync_cookie: { uid: '1', vendor: '2' } } + }; + + callback(mockEvent); + + expect(storage.setDataInLocalStorage.called).to.be.false; + }); + + it('should ignore messages with missing structure', function () { + const addEventListenerStub = sandbox.stub(window, 'addEventListener'); + spec.getUserSyncs({ iframeEnabled: true }); + + const callback = addEventListenerStub.args.find(arg => arg[0] === 'message')[1]; + + const mockEvent = { + origin: 'https://cdn.performax.cz', + data: { wrong_key: 123 } // Missing flexo_sync_cookie + }; + + callback(mockEvent); + + expect(storage.setDataInLocalStorage.called).to.be.false; + }); + + it('should not register duplicate listeners on multiple calls', function () { + const addEventListenerStub = sandbox.stub(window, 'addEventListener'); + + spec.getUserSyncs({ iframeEnabled: true }); + expect(addEventListenerStub.calledOnce).to.be.true; + + spec.getUserSyncs({ iframeEnabled: true }); + expect(addEventListenerStub.calledOnce).to.be.true; + }); + }); + }); }); diff --git a/test/spec/modules/permutiveCombined_spec.js b/test/spec/modules/permutiveCombined_spec.js index 244558d8378..1870c2161d4 100644 --- a/test/spec/modules/permutiveCombined_spec.js +++ b/test/spec/modules/permutiveCombined_spec.js @@ -1127,7 +1127,7 @@ describe('permutiveIdentityManagerIdSystem', () => { it('will optionally wait for Permutive SDK if no identities are in local storage already', async () => { const cleanup = setWindowPermutive() try { - const result = permutiveIdentityManagerIdSubmodule.getId({params: {ajaxTimeout: 300}}) + const result = permutiveIdentityManagerIdSubmodule.getId({ params: { ajaxTimeout: 300 } }) expect(result).not.to.be.undefined expect(result.id).to.be.undefined expect(result.callback).not.to.be.undefined diff --git a/test/spec/modules/pgamsspBidAdapter_spec.js b/test/spec/modules/pgamsspBidAdapter_spec.js index 6eea9bec92a..b881be50402 100644 --- a/test/spec/modules/pgamsspBidAdapter_spec.js +++ b/test/spec/modules/pgamsspBidAdapter_spec.js @@ -481,7 +481,7 @@ describe('PGAMBidAdapter', function () { const syncData = spec.getUserSyncs({}, {}, { consentString: 'ALL', gdprApplies: true, - }, {}); + }, undefined); expect(syncData).to.be.an('array').which.is.not.empty; expect(syncData[0]).to.be.an('object') expect(syncData[0].type).to.be.a('string') @@ -490,9 +490,7 @@ describe('PGAMBidAdapter', function () { expect(syncData[0].url).to.equal('https://cs.pgammedia.com/image?pbjs=1&gdpr=1&gdpr_consent=ALL&coppa=0') }); it('Should return array of objects with proper sync config , include CCPA', function() { - const syncData = spec.getUserSyncs({}, {}, {}, { - consentString: '1---' - }); + const syncData = spec.getUserSyncs({}, {}, {}, '1---'); expect(syncData).to.be.an('array').which.is.not.empty; expect(syncData[0]).to.be.an('object') expect(syncData[0].type).to.be.a('string') @@ -501,7 +499,7 @@ describe('PGAMBidAdapter', function () { expect(syncData[0].url).to.equal('https://cs.pgammedia.com/image?pbjs=1&ccpa_consent=1---&coppa=0') }); it('Should return array of objects with proper sync config , include GPP', function() { - const syncData = spec.getUserSyncs({}, {}, {}, {}, { + const syncData = spec.getUserSyncs({}, {}, {}, undefined, { gppString: 'abc123', applicableSections: [8] }); diff --git a/test/spec/modules/pianoDmpAnalyticsAdapter_spec.js b/test/spec/modules/pianoDmpAnalyticsAdapter_spec.js index ea0dd4ab793..d11e916d2b2 100644 --- a/test/spec/modules/pianoDmpAnalyticsAdapter_spec.js +++ b/test/spec/modules/pianoDmpAnalyticsAdapter_spec.js @@ -53,7 +53,7 @@ describe('Piano DMP Analytics Adapter', () => { // Then const callQueue = (window.cX || {}).callQueue; - testEvents.forEach(({event, args}) => { + testEvents.forEach(({ event, args }) => { const [method, params] = callQueue.filter(item => item[1].eventType === event)[0]; expect(method).to.equal('prebid'); expect(params.params).to.deep.equal(args); diff --git a/test/spec/modules/pixfutureBidAdapter_spec.js b/test/spec/modules/pixfutureBidAdapter_spec.js index 78069c62441..1048b29718e 100644 --- a/test/spec/modules/pixfutureBidAdapter_spec.js +++ b/test/spec/modules/pixfutureBidAdapter_spec.js @@ -247,7 +247,7 @@ describe('PixFutureAdapter', function () { expect(bidRequest.data).to.exist; expect(bidRequest.data.sizes).to.deep.equal([[300, 250]]); - expect(bidRequest.data.params).to.deep.equal({'pix_id': '777'}); + expect(bidRequest.data.params).to.deep.equal({ 'pix_id': '777' }); expect(bidRequest.data.adUnitCode).to.deep.equal('26335x300x250x14x_ADSLOT88'); }); }); diff --git a/test/spec/modules/prebidServerBidAdapter_spec.js b/test/spec/modules/prebidServerBidAdapter_spec.js index 595b95c6db8..620a3d4fddf 100644 --- a/test/spec/modules/prebidServerBidAdapter_spec.js +++ b/test/spec/modules/prebidServerBidAdapter_spec.js @@ -1,21 +1,21 @@ -import {expect} from 'chai'; +import { expect } from 'chai'; import { PrebidServer as Adapter, resetSyncedStatus, validateConfig, s2sDefaultConfig } from 'modules/prebidServerBidAdapter/index.js'; -import adapterManager, {PBS_ADAPTER_NAME} from 'src/adapterManager.js'; +import adapterManager, { PBS_ADAPTER_NAME } from 'src/adapterManager.js'; import * as utils from 'src/utils.js'; -import {deepAccess, deepClone, getWinDimensions, mergeDeep} from 'src/utils.js'; -import {ajax} from 'src/ajax.js'; -import {config} from 'src/config.js'; +import { deepAccess, deepClone, getWinDimensions, mergeDeep } from 'src/utils.js'; +import { ajax } from 'src/ajax.js'; +import { config } from 'src/config.js'; import * as events from 'src/events.js'; import { EVENTS, DEBUG_MODE } from 'src/constants.js'; -import {server} from 'test/mocks/xhr.js'; +import { server } from 'test/mocks/xhr.js'; import 'modules/appnexusBidAdapter.js'; // appnexus alias test import 'modules/rubiconBidAdapter.js'; // rubicon alias test -import {requestBids} from 'src/prebid.js'; +import { requestBids } from 'src/prebid.js'; import 'modules/currency.js'; // adServerCurrency test import 'modules/userId/index.js'; import 'modules/multibid/index.js'; @@ -23,25 +23,23 @@ import 'modules/priceFloors.js'; import 'modules/consentManagementTcf.js'; import 'modules/consentManagementUsp.js'; import 'modules/consentManagementGpp.js'; -import 'modules/paapi.js'; import * as redactor from 'src/activities/redactor.js'; import * as activityRules from 'src/activities/rules.js'; -import {hook} from '../../../src/hook.js'; -import {decorateAdUnitsWithNativeParams} from '../../../src/native.js'; -import {auctionManager} from '../../../src/auctionManager.js'; -import {stubAuctionIndex} from '../../helpers/indexStub.js'; -import {addPaapiConfig, registerBidder} from 'src/adapters/bidderFactory.js'; -import {getGlobal} from '../../../src/prebidGlobal.js'; -import {addFPDToBidderRequest} from '../../helpers/fpd.js'; -import {deepSetValue} from '../../../src/utils.js'; -import {ACTIVITY_TRANSMIT_UFPD} from '../../../src/activities/activities.js'; -import {MODULE_TYPE_PREBID} from '../../../src/activities/modules.js'; +import { hook } from '../../../src/hook.js'; +import { decorateAdUnitsWithNativeParams } from '../../../src/native.js'; +import { auctionManager } from '../../../src/auctionManager.js'; +import { stubAuctionIndex } from '../../helpers/indexStub.js'; +import { registerBidder } from 'src/adapters/bidderFactory.js'; +import { getGlobal } from '../../../src/prebidGlobal.js'; +import { addFPDToBidderRequest } from '../../helpers/fpd.js'; +import { ACTIVITY_TRANSMIT_UFPD } from '../../../src/activities/activities.js'; +import { MODULE_TYPE_PREBID } from '../../../src/activities/modules.js'; import { consolidateEids, extractEids, getPBSBidderConfig } from '../../../modules/prebidServerBidAdapter/bidderConfig.js'; -import {markWinningBid} from '../../../src/adRendering.js'; +import { markWinningBid } from '../../../src/adRendering.js'; let CONFIG = { accountId: '1', @@ -617,7 +615,7 @@ describe('S2S Adapter', function () { beforeEach(function () { config.resetConfig(); - config.setConfig({floors: {enabled: false}}); + config.setConfig({ floors: { enabled: false } }); adapter = new Adapter(); BID_REQUESTS = [ { @@ -705,7 +703,7 @@ describe('S2S Adapter', function () { const s2sConfig = { ...CONFIG, }; - config.setConfig({s2sConfig}); + config.setConfig({ s2sConfig }); s2sReq = { ...REQUEST, s2sConfig @@ -747,10 +745,10 @@ describe('S2S Adapter', function () { beforeEach(() => { s2sReq = { ...REQUEST, - ortb2Fragments: {global: {}}, - ad_units: REQUEST.ad_units.map(au => ({...au, ortb2Imp: {ext: {tid: 'mock-tid'}}})), + ortb2Fragments: { global: {} }, + ad_units: REQUEST.ad_units.map(au => ({ ...au, ortb2Imp: { ext: { tid: 'mock-tid' } } })), }; - BID_REQUESTS[0].bids[0].ortb2Imp = {ext: {tid: 'mock-tid'}}; + BID_REQUESTS[0].bids[0].ortb2Imp = { ext: { tid: 'mock-tid' } }; }); function makeRequest() { @@ -767,7 +765,7 @@ describe('S2S Adapter', function () { }); it('should be set to auction ID otherwise', () => { - config.setConfig({s2sConfig: CONFIG, enableTIDs: true}); + config.setConfig({ s2sConfig: CONFIG, enableTIDs: true }); const req = makeRequest(); expect(req.source.tid).to.eql(BID_REQUESTS[0].auctionId); expect(req.imp[0].ext.tid).to.eql('mock-tid'); @@ -790,7 +788,7 @@ describe('S2S Adapter', function () { } return false; }); - config.setConfig({s2sConfig: CONFIG}); + config.setConfig({ s2sConfig: CONFIG }); const ajax = sinon.stub(); adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax); sinon.assert.calledWith(ajax, sinon.match.any, sinon.match.any, sinon.match.any, sinon.match({ @@ -801,8 +799,8 @@ describe('S2S Adapter', function () { }) it('should set tmaxmax correctly when publisher has specified it', () => { - const cfg = {...CONFIG}; - config.setConfig({s2sConfig: cfg}) + const cfg = { ...CONFIG }; + config.setConfig({ s2sConfig: cfg }) // publisher has specified a tmaxmax in their setup const ortb2Fragments = { @@ -812,7 +810,7 @@ describe('S2S Adapter', function () { } } }; - const s2sCfg = {...REQUEST, cfg} + const s2sCfg = { ...REQUEST, cfg } const payloadWithFragments = { ...s2sCfg, ortb2Fragments }; adapter.callBids(payloadWithFragments, BID_REQUESTS, addBidResponse, done, ajax); @@ -822,13 +820,13 @@ describe('S2S Adapter', function () { }); it('should set tmaxmax correctly when publisher has not specified it', () => { - const cfg = {...CONFIG}; - config.setConfig({s2sConfig: cfg}) + const cfg = { ...CONFIG }; + config.setConfig({ s2sConfig: cfg }) // publisher has not specified a tmaxmax in their setup - so we should be // falling back to requestBidsTimeout const ortb2Fragments = {}; - const s2sCfg = {...REQUEST, cfg}; + const s2sCfg = { ...REQUEST, cfg }; const requestBidsTimeout = 808; const payloadWithFragments = { ...s2sCfg, ortb2Fragments, requestBidsTimeout }; @@ -844,19 +842,19 @@ describe('S2S Adapter', function () { let cfg; beforeEach(() => { - cfg = {accountId: '1', endpoint: 'mock-endpoint', maxTimeout}; - config.setConfig({s2sConfig: cfg}); + cfg = { accountId: '1', endpoint: 'mock-endpoint', maxTimeout }; + config.setConfig({ s2sConfig: cfg }); maxTimeout = maxTimeout ?? s2sDefaultConfig.maxTimeout }); it('should cap tmax to maxTimeout', () => { - adapter.callBids({...REQUEST, requestBidsTimeout: maxTimeout * 2, s2sConfig: cfg}, BID_REQUESTS, addBidResponse, done, ajax); + adapter.callBids({ ...REQUEST, requestBidsTimeout: maxTimeout * 2, s2sConfig: cfg }, BID_REQUESTS, addBidResponse, done, ajax); const req = JSON.parse(server.requests[0].requestBody); expect(req.tmax).to.eql(maxTimeout); }); it('should be set to 0.75 * requestTimeout, if lower than maxTimeout', () => { - adapter.callBids({...REQUEST, requestBidsTimeout: maxTimeout / 2}, BID_REQUESTS, addBidResponse, done, ajax); + adapter.callBids({ ...REQUEST, requestBidsTimeout: maxTimeout / 2 }, BID_REQUESTS, addBidResponse, done, ajax); const req = JSON.parse(server.requests[0].requestBody); expect(req.tmax).to.eql(Math.floor(maxTimeout / 2 * 0.75)); }) @@ -933,16 +931,16 @@ describe('S2S Adapter', function () { }); it('filters ad units without bidders when filterBidderlessCalls is true', function () { - const cfg = {...CONFIG, filterBidderlessCalls: true}; - config.setConfig({s2sConfig: cfg}); + const cfg = { ...CONFIG, filterBidderlessCalls: true }; + config.setConfig({ s2sConfig: cfg }); const badReq = utils.deepClone(REQUEST); badReq.s2sConfig = cfg; - badReq.ad_units = [{...REQUEST.ad_units[0], bids: [{bidder: null}]}]; + badReq.ad_units = [{ ...REQUEST.ad_units[0], bids: [{ bidder: null }] }]; const badBidderRequest = utils.deepClone(BID_REQUESTS); badBidderRequest[0].bidderCode = null; - badBidderRequest[0].bids = [{...badBidderRequest[0].bids[0], bidder: null}]; + badBidderRequest[0].bids = [{ ...badBidderRequest[0].bids[0], bidder: null }]; adapter.callBids(badReq, badBidderRequest, addBidResponse, done, ajax); @@ -996,8 +994,8 @@ describe('S2S Adapter', function () { }); it('should gzip payload when enabled and supported', function(done) { - const s2sCfg = Object.assign({}, CONFIG, {endpointCompression: true}); - config.setConfig({s2sConfig: s2sCfg}); + const s2sCfg = Object.assign({}, CONFIG, { endpointCompression: true }); + config.setConfig({ s2sConfig: s2sCfg }); const req = utils.deepClone(REQUEST); req.s2sConfig = s2sCfg; gzipSupportStub.returns(true); @@ -1015,8 +1013,8 @@ describe('S2S Adapter', function () { }); it('should not gzip when debug mode is enabled', function(done) { - const s2sCfg = Object.assign({}, CONFIG, {endpointCompression: true}); - config.setConfig({s2sConfig: s2sCfg}); + const s2sCfg = Object.assign({}, CONFIG, { endpointCompression: true }); + config.setConfig({ s2sConfig: s2sCfg }); const req = utils.deepClone(REQUEST); req.s2sConfig = s2sCfg; gzipSupportStub.returns(true); @@ -1036,11 +1034,11 @@ describe('S2S Adapter', function () { expect(adapter.callBids).to.exist.and.to.be.a('function'); }); - function mockTCF({applies = true, hasP1Consent = true} = {}) { + function mockTCF({ applies = true, hasP1Consent = true } = {}) { return { consentString: 'mockConsent', gdprApplies: applies, - vendorData: {purpose: {consents: {1: hasP1Consent}}}, + vendorData: { purpose: { consents: { 1: hasP1Consent } } }, } } @@ -1050,7 +1048,7 @@ describe('S2S Adapter', function () { }); it('adds gdpr consent information to ortb2 request depending on presence of module', async function () { - const consentConfig = {consentManagement: {cmpApi: 'iab'}, s2sConfig: CONFIG}; + const consentConfig = { consentManagement: { cmpApi: 'iab' }, s2sConfig: CONFIG }; config.setConfig(consentConfig); const gdprBidRequest = utils.deepClone(BID_REQUESTS); @@ -1063,7 +1061,7 @@ describe('S2S Adapter', function () { expect(requestBid.user.ext.consent).is.equal('mockConsent'); config.resetConfig(); - config.setConfig({s2sConfig: CONFIG}); + config.setConfig({ s2sConfig: CONFIG }); adapter.callBids(await addFpdEnrichmentsToS2SRequest(REQUEST, BID_REQUESTS), BID_REQUESTS, addBidResponse, done, ajax); requestBid = JSON.parse(server.requests[1].requestBody); @@ -1073,7 +1071,7 @@ describe('S2S Adapter', function () { }); it('adds additional consent information to ortb2 request depending on presence of module', async function () { - const consentConfig = {consentManagement: {cmpApi: 'iab'}, s2sConfig: CONFIG}; + const consentConfig = { consentManagement: { cmpApi: 'iab' }, s2sConfig: CONFIG }; config.setConfig(consentConfig); const gdprBidRequest = utils.deepClone(BID_REQUESTS); @@ -1089,7 +1087,7 @@ describe('S2S Adapter', function () { expect(requestBid.user.ext.ConsentedProvidersSettings.consented_providers).is.equal('superduperconsent'); config.resetConfig(); - config.setConfig({s2sConfig: CONFIG}); + config.setConfig({ s2sConfig: CONFIG }); adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax); requestBid = JSON.parse(server.requests[1].requestBody); @@ -1105,7 +1103,7 @@ describe('S2S Adapter', function () { }); it('is added to ortb2 request when in FPD', async function () { - config.setConfig({s2sConfig: CONFIG}); + config.setConfig({ s2sConfig: CONFIG }); const uspBidRequest = utils.deepClone(BID_REQUESTS); uspBidRequest[0].uspConsent = '1NYN'; @@ -1116,7 +1114,7 @@ describe('S2S Adapter', function () { expect(requestBid.regs.ext.us_privacy).is.equal('1NYN'); config.resetConfig(); - config.setConfig({s2sConfig: CONFIG}); + config.setConfig({ s2sConfig: CONFIG }); adapter.callBids(await addFpdEnrichmentsToS2SRequest(REQUEST, BID_REQUESTS), BID_REQUESTS, addBidResponse, done, ajax); requestBid = JSON.parse(server.requests[1].requestBody); @@ -1131,7 +1129,7 @@ describe('S2S Adapter', function () { }); it('is added to ortb2 request when in bidRequest', async function () { - config.setConfig({s2sConfig: CONFIG}); + config.setConfig({ s2sConfig: CONFIG }); const consentBidRequest = utils.deepClone(BID_REQUESTS); consentBidRequest[0].uspConsent = '1NYN'; @@ -1145,7 +1143,7 @@ describe('S2S Adapter', function () { expect(requestBid.user.ext.consent).is.equal('mockConsent'); config.resetConfig(); - config.setConfig({s2sConfig: CONFIG}); + config.setConfig({ s2sConfig: CONFIG }); adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax); requestBid = JSON.parse(server.requests[1].requestBody); @@ -1186,8 +1184,8 @@ describe('S2S Adapter', function () { ...REQUEST, ortb2Fragments: { global: { - device: {ifa: '6D92078A-8246-4BA4-AE5B-76104861E7DC'}, - app: {bundle: 'com.test.app'}, + device: { ifa: '6D92078A-8246-4BA4-AE5B-76104861E7DC' }, + app: { bundle: 'com.test.app' }, } } }, BID_REQUESTS) @@ -1200,7 +1198,7 @@ describe('S2S Adapter', function () { }) sinon.assert.match(requestBid.app, { bundle: 'com.test.app', - publisher: {'id': '1'} + publisher: { 'id': '1' } }); }); @@ -1218,8 +1216,8 @@ describe('S2S Adapter', function () { ...REQUEST, ortb2Fragments: { global: { - device: {ifa: '6D92078A-8246-4BA4-AE5B-76104861E7DC'}, - app: {bundle: 'com.test.app'}, + device: { ifa: '6D92078A-8246-4BA4-AE5B-76104861E7DC' }, + app: { bundle: 'com.test.app' }, } } }, BID_REQUESTS) @@ -1232,7 +1230,7 @@ describe('S2S Adapter', function () { }) sinon.assert.match(requestBid.app, { bundle: 'com.test.app', - publisher: {'id': '1'} + publisher: { 'id': '1' } }); }); @@ -1370,16 +1368,16 @@ describe('S2S Adapter', function () { code: 'au1', transactionId: 't1', mediaTypes: { - banner: {sizes: [1, 1]} + banner: { sizes: [1, 1] } }, - bids: [{bidder: 'b1', bid_id: 1}] + bids: [{ bidder: 'b1', bid_id: 1 }] }, { code: 'au2', transactionId: 't2', - bids: [{bidder: 'b2', bid_id: 2}], + bids: [{ bidder: 'b2', bid_id: 2 }], mediaTypes: { - banner: {sizes: [1, 1]} + banner: { sizes: [1, 1] } } } ]; @@ -1454,12 +1452,12 @@ describe('S2S Adapter', function () { code: 'au1', transactionId: 't1', mediaTypes: { - banner: {sizes: [1, 1]} + banner: { sizes: [1, 1] } }, bids: [ - {bidder: 'b2', bid_id: 2}, - {bidder: 'b3', bid_id: 3}, - {bidder: 'b1', bid_id: 1}, + { bidder: 'b2', bid_id: 2 }, + { bidder: 'b3', bid_id: 3 }, + { bidder: 'b1', bid_id: 1 }, ] } ] @@ -1487,13 +1485,13 @@ describe('S2S Adapter', function () { }, 'mediaType level floors': { target: 'imp.0.banner.ext', - floorFilter: ({mediaType, size}) => size === '*' && mediaType !== '*' + floorFilter: ({ mediaType, size }) => size === '*' && mediaType !== '*' }, 'format level floors': { target: 'imp.0.banner.format.0.ext', - floorFilter: ({size}) => size !== '*' + floorFilter: ({ size }) => size !== '*' } - }).forEach(([t, {target, floorFilter}]) => { + }).forEach(([t, { target, floorFilter }]) => { describe(t, () => { beforeEach(() => { if (floorFilter != null) { @@ -1534,7 +1532,7 @@ describe('S2S Adapter', function () { throw new Error(); } } - }).forEach(([t, {expectDesc, expectedFloor, expectedCur, conversionFn}]) => { + }).forEach(([t, { expectDesc, expectedFloor, expectedCur, conversionFn }]) => { describe(`and currency conversion ${t}`, () => { let mockConvertCurrency; const origConvertCurrency = getGlobal().convertCurrency; @@ -1631,7 +1629,7 @@ describe('S2S Adapter', function () { }; config.setConfig(_config); - adapter.callBids({...REQUEST, s2sConfig: Object.assign({}, CONFIG, s2sDefaultConfig)}, BID_REQUESTS, addBidResponse, done, ajax); + adapter.callBids({ ...REQUEST, s2sConfig: Object.assign({}, CONFIG, s2sDefaultConfig) }, BID_REQUESTS, addBidResponse, done, ajax); const requestBid = JSON.parse(server.requests[0].requestBody); const ortbReq = JSON.parse(requestBid.imp[0].native.request); expect(ortbReq).to.deep.equal({ @@ -1669,27 +1667,27 @@ describe('S2S Adapter', function () { ...CONFIG, ortbNative: { eventtrackers: [ - {event: 1, methods: [1, 2]} + { event: 1, methods: [1, 2] } ] } } config.setConfig({ s2sConfig: cfg }); - adapter.callBids({...REQUEST, s2sConfig: cfg}, BID_REQUESTS, addBidResponse, done, ajax); + adapter.callBids({ ...REQUEST, s2sConfig: cfg }, BID_REQUESTS, addBidResponse, done, ajax); const requestBid = JSON.parse(server.requests[0].requestBody); const ortbReq = JSON.parse(requestBid.imp[0].native.request); expect(ortbReq).to.eql({ ...ORTB_NATIVE_REQ, eventtrackers: [ - {event: 1, methods: [1, 2]} + { event: 1, methods: [1, 2] } ] }) }) it('should not include ext.aspectratios if adunit\'s aspect_ratios do not define radio_width and ratio_height', () => { const req = deepClone(REQUEST); - req.ad_units[0].mediaTypes.native.icon.aspect_ratios[0] = {'min_width': 1, 'min_height': 2}; + req.ad_units[0].mediaTypes.native.icon.aspect_ratios[0] = { 'min_width': 1, 'min_height': 2 }; prepRequest(req); adapter.callBids(req, BID_REQUESTS, addBidResponse, done, ajax); const nativeReq = JSON.parse(JSON.parse(server.requests[0].requestBody).imp[0].native.request); @@ -1757,7 +1755,7 @@ describe('S2S Adapter', function () { ...REQUEST, ortb2Fragments: { global: { - app: {bundle: 'com.test.app'}, + app: { bundle: 'com.test.app' }, site: { publisher: { id: '1234', @@ -1788,7 +1786,7 @@ describe('S2S Adapter', function () { const request = utils.deepClone(REQUEST); request.ad_units[0].bids = [aliasBidder]; - adapter.callBids(request, [{...BID_REQUESTS[0], bidderCode: 'beintoo'}], addBidResponse, done, ajax); + adapter.callBids(request, [{ ...BID_REQUESTS[0], bidderCode: 'beintoo' }], addBidResponse, done, ajax); const requestBid = JSON.parse(server.requests[0].requestBody); expect(requestBid.ext).to.haveOwnProperty('prebid'); @@ -1823,7 +1821,7 @@ describe('S2S Adapter', function () { request.ad_units[0].bids = [aliasBidder]; request.s2sConfig = adjustedConfig; - adapter.callBids(request, [{...BID_REQUESTS[0], bidderCode: aliasBidder.bidder}], addBidResponse, done, ajax); + adapter.callBids(request, [{ ...BID_REQUESTS[0], bidderCode: aliasBidder.bidder }], addBidResponse, done, ajax); const requestBid = JSON.parse(server.requests[0].requestBody); expect(requestBid.ext.prebid.aliases).to.deep.equal({ bidderD: 'mockBidder' }); @@ -1844,7 +1842,7 @@ describe('S2S Adapter', function () { // TODO: stub this getGlobal().aliasBidder('appnexus', alias); - adapter.callBids(request, [{...BID_REQUESTS[0], bidderCode: 'foobar'}], addBidResponse, done, ajax); + adapter.callBids(request, [{ ...BID_REQUESTS[0], bidderCode: 'foobar' }], addBidResponse, done, ajax); const requestBid = JSON.parse(server.requests[0].requestBody); expect(requestBid.ext).to.haveOwnProperty('prebid'); @@ -1883,7 +1881,7 @@ describe('S2S Adapter', function () { const request = utils.deepClone(REQUEST); request.ad_units[0].bids = [aliasBidder]; - adapter.callBids(request, [{...BID_REQUESTS[0], bidderCode: aliasBidder.bidder}], addBidResponse, done, ajax); + adapter.callBids(request, [{ ...BID_REQUESTS[0], bidderCode: aliasBidder.bidder }], addBidResponse, done, ajax); const requestBid = JSON.parse(server.requests[0].requestBody); @@ -1920,7 +1918,7 @@ describe('S2S Adapter', function () { // TODO: stub this getGlobal().aliasBidder('appnexus', alias, { skipPbsAliasing: true }); - adapter.callBids(request, [{...BID_REQUESTS[0], bidderCode: aliasBidder.bidder}], addBidResponse, done, ajax); + adapter.callBids(request, [{ ...BID_REQUESTS[0], bidderCode: aliasBidder.bidder }], addBidResponse, done, ajax); const requestBid = JSON.parse(server.requests[0].requestBody); @@ -2087,7 +2085,7 @@ describe('S2S Adapter', function () { }); it('when gdprApplies is false', () => { - bidderReqs[0].gdprConsent = mockTCF({applies: false}); + bidderReqs[0].gdprConsent = mockTCF({ applies: false }); const req = callCookieSync(); expect(req.gdpr).is.equal(0); expect(req.gdpr_consent).is.undefined; @@ -2162,7 +2160,7 @@ describe('S2S Adapter', function () { site: { domain: 'nytimes.com', page: 'http://www.nytimes.com', - publisher: {id: '2'} + publisher: { id: '2' } }, device, } @@ -2211,7 +2209,7 @@ describe('S2S Adapter', function () { ...CONFIG, bidders: ['appnexus', 'rubicon'] } - config.setConfig({s2sConfig}); + config.setConfig({ s2sConfig }); req = { ...REQUEST, s2sConfig, @@ -2219,7 +2217,7 @@ describe('S2S Adapter', function () { global: { user: { ext: { - eids: [{source: 'idA', id: 1}, {source: 'idB', id: 2}] + eids: [{ source: 'idA', id: 1 }, { source: 'idB', id: 2 }] } } }, @@ -2227,7 +2225,7 @@ describe('S2S Adapter', function () { appnexus: { user: { ext: { - eids: [{source: 'idC', id: 3}] + eids: [{ source: 'idC', id: 3 }] } } } @@ -2239,9 +2237,9 @@ describe('S2S Adapter', function () { adapter.callBids(req, BID_REQUESTS, addBidResponse, done, ajax); const payload = JSON.parse(server.requests[0].requestBody); expect(payload.user.ext.eids).to.eql([ - {source: 'idA', id: 1}, - {source: 'idB', id: 2}, - {source: 'idC', id: 3} + { source: 'idA', id: 1 }, + { source: 'idB', id: 2 }, + { source: 'idC', id: 3 } ]); expect(payload.ext.prebid.data.eidpermissions).to.eql([{ bidders: ['appnexus'], @@ -2252,7 +2250,7 @@ describe('S2S Adapter', function () { it('should not set eidpermissions for unrequested bidders', () => { req.ortb2Fragments.bidder.unknown = { user: { - eids: [{source: 'idC', id: 3}, {source: 'idD', id: 4}] + eids: [{ source: 'idC', id: 3 }, { source: 'idD', id: 4 }] } } adapter.callBids(req, BID_REQUESTS, addBidResponse, done, ajax); @@ -2278,15 +2276,15 @@ describe('S2S Adapter', function () { req.ortb2Fragments.bidder.rubicon = { user: { ext: { - eids: [{source: 'idC', id: 4}] + eids: [{ source: 'idC', id: 4 }] } } } adapter.callBids(req, BID_REQUESTS, addBidResponse, done, ajax); const payload = JSON.parse(server.requests[0].requestBody); const globalEids = [ - {source: 'idA', id: 1}, - {source: 'idB', id: 2}, + { source: 'idA', id: 1 }, + { source: 'idB', id: 2 }, ] expect(payload.user.ext.eids).to.eql(globalEids); expect(payload.ext.prebid?.data?.eidpermissions).to.not.exist; @@ -2295,7 +2293,7 @@ describe('S2S Adapter', function () { bidders: ['appnexus'], config: { ortb2: { - user: {ext: {eids: globalEids.concat([{source: 'idC', id: 3}])}} + user: { ext: { eids: globalEids.concat([{ source: 'idC', id: 3 }]) } } } } }, @@ -2303,7 +2301,7 @@ describe('S2S Adapter', function () { bidders: ['rubicon'], config: { ortb2: { - user: {ext: {eids: globalEids.concat([{source: 'idC', id: 4}])}} + user: { ext: { eids: globalEids.concat([{ source: 'idC', id: 4 }]) } } } } } @@ -2658,22 +2656,22 @@ describe('S2S Adapter', function () { Object.entries({ 'set': {}, - 'override': {source: {ext: {schain: 'pub-provided'}}} + 'override': { source: { ext: { schain: 'pub-provided' } } } }).forEach(([t, fpd]) => { it(`should not ${t} source.ext.schain`, () => { const bidderReqs = [ - {...deepClone(BID_REQUESTS[0]), bidderCode: 'A'}, - {...deepClone(BID_REQUESTS[0]), bidderCode: 'B'}, - {...deepClone(BID_REQUESTS[0]), bidderCode: 'C'} + { ...deepClone(BID_REQUESTS[0]), bidderCode: 'A' }, + { ...deepClone(BID_REQUESTS[0]), bidderCode: 'B' }, + { ...deepClone(BID_REQUESTS[0]), bidderCode: 'C' } ]; - const chain1 = {chain: 1}; - const chain2 = {chain: 2}; + const chain1 = { chain: 1 }; + const chain2 = { chain: 2 }; bidderReqs[0].bids[0].schain = chain1; bidderReqs[1].bids[0].schain = chain2; bidderReqs[2].bids[0].schain = chain2; - adapter.callBids({...REQUEST, ortb2Fragments: {global: fpd}}, bidderReqs, addBidResponse, done, ajax); + adapter.callBids({ ...REQUEST, ortb2Fragments: { global: fpd } }, bidderReqs, addBidResponse, done, ajax); const req = JSON.parse(server.requests[0].requestBody); expect(req.source?.ext?.schain).to.eql(fpd?.source?.ext?.schain); }) @@ -2760,7 +2758,7 @@ describe('S2S Adapter', function () { }; const site = { - content: {userrating: 4}, + content: { userrating: 4 }, ext: { data: { pageType: 'article', @@ -2770,7 +2768,7 @@ describe('S2S Adapter', function () { }; const user = { yob: '1984', - geo: {country: 'ca'}, + geo: { country: 'ca' }, ext: { data: { registered: true, @@ -2787,7 +2785,7 @@ describe('S2S Adapter', function () { config: { ortb2: { site: { - content: {userrating: 4}, + content: { userrating: 4 }, ext: { data: { pageType: 'article', @@ -2797,7 +2795,7 @@ describe('S2S Adapter', function () { }, user: { yob: '1984', - geo: {country: 'ca'}, + geo: { country: 'ca' }, ext: { data: { registered: true, @@ -2820,8 +2818,8 @@ describe('S2S Adapter', function () { }, commonSite); const ortb2Fragments = { - global: {site: commonSite, user: commonUser, badv, bcat}, - bidder: Object.fromEntries(allowedBidders.map(bidder => [bidder, {site, user, bcat, badv}])) + global: { site: commonSite, user: commonUser, badv, bcat }, + bidder: Object.fromEntries(allowedBidders.map(bidder => [bidder, { site, user, bcat, badv }])) }; adapter.callBids(await addFpdEnrichmentsToS2SRequest({ @@ -2837,10 +2835,10 @@ describe('S2S Adapter', function () { }); it('passes first party data in request for unknown when allowUnknownBidderCodes is true', async () => { - const cfg = {...CONFIG, allowUnknownBidderCodes: true}; - config.setConfig({s2sConfig: cfg}); + const cfg = { ...CONFIG, allowUnknownBidderCodes: true }; + config.setConfig({ s2sConfig: cfg }); - const clonedReq = {...REQUEST, s2sConfig: cfg} + const clonedReq = { ...REQUEST, s2sConfig: cfg } const s2sBidRequest = utils.deepClone(clonedReq); const bidRequests = utils.deepClone(BID_REQUESTS); @@ -2854,7 +2852,7 @@ describe('S2S Adapter', function () { }; const site = { - content: {userrating: 4}, + content: { userrating: 4 }, ext: { data: { pageType: 'article', @@ -2864,7 +2862,7 @@ describe('S2S Adapter', function () { }; const user = { yob: '1984', - geo: {country: 'ca'}, + geo: { country: 'ca' }, ext: { data: { registered: true, @@ -2881,7 +2879,7 @@ describe('S2S Adapter', function () { config: { ortb2: { site: { - content: {userrating: 4}, + content: { userrating: 4 }, ext: { data: { pageType: 'article', @@ -2891,7 +2889,7 @@ describe('S2S Adapter', function () { }, user: { yob: '1984', - geo: {country: 'ca'}, + geo: { country: 'ca' }, ext: { data: { registered: true, @@ -2914,8 +2912,8 @@ describe('S2S Adapter', function () { }, commonSite); const ortb2Fragments = { - global: {site: commonSite, user: commonUser, badv, bcat}, - bidder: Object.fromEntries(allowedBidders.map(bidder => [bidder, {site, user, bcat, badv}])) + global: { site: commonSite, user: commonUser, badv, bcat }, + bidder: Object.fromEntries(allowedBidders.map(bidder => [bidder, { site, user, bcat, badv }])) }; // adapter.callBids({ ...REQUEST, s2sConfig: cfg }, BID_REQUESTS, addBidResponse, done, ajax); @@ -3023,7 +3021,7 @@ describe('S2S Adapter', function () { }) it('should be set on imp.ext.prebid.imp', () => { const s2sReq = utils.deepClone(REQUEST); - s2sReq.ad_units[0].ortb2Imp = {l0: 'adUnit'}; + s2sReq.ad_units[0].ortb2Imp = { l0: 'adUnit' }; s2sReq.ad_units[0].bids = [ { bidder: 'A', @@ -3062,8 +3060,8 @@ describe('S2S Adapter', function () { const req = JSON.parse(server.requests[0].requestBody); expect(req.imp[0].l0).to.eql('adUnit'); expect(req.imp[0].ext.prebid.imp).to.eql({ - A: {l2: 'A'}, - B: {l2: 'B'} + A: { l2: 'A' }, + B: { l2: 'B' } }); }); }); @@ -3122,7 +3120,7 @@ describe('S2S Adapter', function () { let success, error; beforeEach(() => { const mockAjax = function (_, callback) { - ({success, error} = callback); + ({ success, error } = callback); } config.setConfig({ s2sConfig: CONFIG }); adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, mockAjax); @@ -3138,7 +3136,7 @@ describe('S2S Adapter', function () { 'other errors': false }).forEach(([t, timedOut]) => { it(`passing timedOut = ${timedOut} on ${t}`, () => { - error('', {timedOut}); + error('', { timedOut }); sinon.assert.calledWith(done, timedOut); }) }) @@ -3313,34 +3311,17 @@ describe('S2S Adapter', function () { expect(response).to.have.property('ttl', 60); }); - it('handles seatnonbid responses and emits SEAT_NON_BID', function () { - const original = CONFIG; - CONFIG.extPrebid = { returnallbidstatus: true }; - const nonbidResponse = {...RESPONSE_OPENRTB, ext: {seatnonbid: [{}]}}; - config.setConfig({ CONFIG }); - CONFIG = original; - adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax); - const responding = deepClone(nonbidResponse); - Object.assign(responding.ext.seatnonbid, [{auctionId: 2}]) - server.requests[0].respond(200, {}, JSON.stringify(responding)); - const event = events.emit.thirdCall.args; - expect(event[0]).to.equal(EVENTS.SEAT_NON_BID); - expect(event[1].seatnonbid[0]).to.have.property('auctionId', 2); - expect(event[1].requestedBidders).to.deep.equal(['appnexus']); - expect(event[1].response).to.deep.equal(responding); - }); - it('emits the PBS_ANALYTICS event and captures seatnonbid responses', function () { const original = CONFIG; CONFIG.extPrebid = { returnallbidstatus: true }; - const nonbidResponse = {...RESPONSE_OPENRTB, ext: {seatnonbid: [{}]}}; + const nonbidResponse = { ...RESPONSE_OPENRTB, ext: { seatnonbid: [{}] } }; config.setConfig({ CONFIG }); CONFIG = original; adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax); const responding = deepClone(nonbidResponse); - Object.assign(responding.ext.seatnonbid, [{auctionId: 2}]) + Object.assign(responding.ext.seatnonbid, [{ auctionId: 2 }]) server.requests[0].respond(200, {}, JSON.stringify(responding)); - const event = events.emit.getCall(3).args; + const event = events.emit.getCall(2).args; expect(event[0]).to.equal(EVENTS.PBS_ANALYTICS); expect(event[1].seatnonbid[0]).to.have.property('auctionId', 2); expect(event[1].requestedBidders).to.deep.equal(['appnexus']); @@ -3350,7 +3331,7 @@ describe('S2S Adapter', function () { it('emits the PBS_ANALYTICS event and captures atag responses', function () { const original = CONFIG; CONFIG.extPrebid = { returnallbidstatus: true }; - const atagResponse = {...RESPONSE_OPENRTB, ext: {prebid: {analytics: {tags: ['data']}}}}; + const atagResponse = { ...RESPONSE_OPENRTB, ext: { prebid: { analytics: { tags: ['data'] } } } }; config.setConfig({ CONFIG }); CONFIG = original; adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax); @@ -3574,13 +3555,13 @@ describe('S2S Adapter', function () { if (FEATURES.NATIVE) { it('handles OpenRTB native responses', function () { const stub = sinon.stub(auctionManager, 'index'); - stub.get(() => stubAuctionIndex({adUnits: REQUEST.ad_units})); + stub.get(() => stubAuctionIndex({ adUnits: REQUEST.ad_units })); const s2sConfig = Object.assign({}, CONFIG, { endpoint: { p1Consent: 'https://prebidserverurl/openrtb2/auction?querystring=param' } }); - config.setConfig({s2sConfig}); + config.setConfig({ s2sConfig }); const s2sBidRequest = utils.deepClone(REQUEST); s2sBidRequest.s2sConfig = s2sConfig; @@ -3604,7 +3585,7 @@ describe('S2S Adapter', function () { config.setConfig({ s2sConfig: CONFIG }); adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax); const response = deepClone(RESPONSE_OPENRTB); - Object.assign(response.seatbid[0].bid[0], {w: null, h: null}); + Object.assign(response.seatbid[0].bid[0], { w: null, h: null }); server.requests[0].respond(200, {}, JSON.stringify(response)); expect(addBidResponse.reject.calledOnce).to.be.true; expect(addBidResponse.called).to.be.false; @@ -3636,25 +3617,25 @@ describe('S2S Adapter', function () { let bidReq, response; function mks2sReq(s2sConfig = CONFIG) { - return {...REQUEST, s2sConfig, ad_units: [{...REQUEST.ad_units[0], bids: [{bidder: null, bid_id: 'testId'}]}]}; + return { ...REQUEST, s2sConfig, ad_units: [{ ...REQUEST.ad_units[0], bids: [{ bidder: null, bid_id: 'testId' }] }] }; } beforeEach(() => { - bidReq = {...BID_REQUESTS[0], bidderCode: null, bids: [{...BID_REQUESTS[0].bids[0], bidder: null, bidId: 'testId'}]} + bidReq = { ...BID_REQUESTS[0], bidderCode: null, bids: [{ ...BID_REQUESTS[0].bids[0], bidder: null, bidId: 'testId' }] } response = deepClone(RESPONSE_OPENRTB); response.seatbid[0].seat = 'storedImpression'; }) it('uses "null" request\'s ID for all responses, when a null request is present', function () { - const cfg = {...CONFIG, allowUnknownBidderCodes: true}; - config.setConfig({s2sConfig: cfg}); + const cfg = { ...CONFIG, allowUnknownBidderCodes: true }; + config.setConfig({ s2sConfig: cfg }); adapter.callBids(mks2sReq(cfg), [bidReq], addBidResponse, done, ajax); server.requests[0].respond(200, {}, JSON.stringify(response)); - sinon.assert.calledWith(addBidResponse, sinon.match.any, sinon.match({bidderCode: 'storedImpression', requestId: 'testId'})) + sinon.assert.calledWith(addBidResponse, sinon.match.any, sinon.match({ bidderCode: 'storedImpression', requestId: 'testId' })) }); it('does not allow null requests (= stored impressions) if allowUnknownBidderCodes is not set', () => { - config.setConfig({s2sConfig: CONFIG}); + config.setConfig({ s2sConfig: CONFIG }); adapter.callBids(mks2sReq(), [bidReq], addBidResponse, done, ajax); server.requests[0].respond(200, {}, JSON.stringify(response)); expect(addBidResponse.called).to.be.false; @@ -3663,11 +3644,11 @@ describe('S2S Adapter', function () { }) it('copies ortb2Imp to response when there is only a null bid', () => { - const cfg = {...CONFIG}; - config.setConfig({s2sConfig: cfg}); - const ortb2Imp = {ext: {prebid: {storedrequest: 'value'}}}; - const req = {...REQUEST, s2sConfig: cfg, ad_units: [{...REQUEST.ad_units[0], bids: [{bidder: null, bid_id: 'testId'}], ortb2Imp}]}; - const bidReq = {...BID_REQUESTS[0], bidderCode: null, bids: [{...BID_REQUESTS[0].bids[0], bidder: null, bidId: 'testId'}]} + const cfg = { ...CONFIG }; + config.setConfig({ s2sConfig: cfg }); + const ortb2Imp = { ext: { prebid: { storedrequest: 'value' } } }; + const req = { ...REQUEST, s2sConfig: cfg, ad_units: [{ ...REQUEST.ad_units[0], bids: [{ bidder: null, bid_id: 'testId' }], ortb2Imp }] }; + const bidReq = { ...BID_REQUESTS[0], bidderCode: null, bids: [{ ...BID_REQUESTS[0].bids[0], bidder: null, bidId: 'testId' }] } adapter.callBids(req, [bidReq], addBidResponse, done, ajax); const actual = JSON.parse(server.requests[0].requestBody); sinon.assert.match(actual.imp[0], sinon.match(ortb2Imp)); @@ -3734,108 +3715,6 @@ describe('S2S Adapter', function () { }); }); }); - describe('when the response contains ext.prebid.fledge', () => { - const AU = 'div-gpt-ad-1460505748561-0'; - const FLEDGE_RESP = { - ext: { - prebid: { - fledge: { - auctionconfigs: [ - { - impid: AU, - bidder: 'appnexus', - config: { - id: 1 - } - }, - { - impid: AU, - bidder: 'other', - config: { - id: 2 - } - } - ] - } - } - } - } - - let fledgeStub, request, bidderRequests; - - function fledgeHook(next, ...args) { - fledgeStub(...args); - } - - before(() => { - addPaapiConfig.before(fledgeHook); - }); - - after(() => { - addPaapiConfig.getHooks({hook: fledgeHook}).remove(); - }) - - beforeEach(function () { - fledgeStub = sinon.stub(); - config.setConfig({ - s2sConfig: CONFIG, - }); - bidderRequests = deepClone(BID_REQUESTS); - bidderRequests.forEach(req => { - Object.assign(req, { - paapi: { - enabled: true - }, - ortb2: { - fpd: 1 - } - }) - req.bids.forEach(bid => { - Object.assign(bid, { - ortb2Imp: { - fpd: 2, - } - }) - }) - }); - request = deepClone(REQUEST); - request.ad_units.forEach(au => deepSetValue(au, 'ortb2Imp.ext.ae', 1)); - }); - - function expectFledgeCalls() { - const auctionId = bidderRequests[0].auctionId; - sinon.assert.calledWith(fledgeStub, sinon.match({auctionId, adUnitCode: AU, ortb2: bidderRequests[0].ortb2, ortb2Imp: bidderRequests[0].bids[0].ortb2Imp}), sinon.match({config: {id: 1}})) - sinon.assert.calledWith(fledgeStub, sinon.match({auctionId, adUnitCode: AU, ortb2: undefined, ortb2Imp: undefined}), sinon.match({config: {id: 2}})) - } - - it('calls addPaapiConfig alongside addBidResponse', function () { - adapter.callBids(request, bidderRequests, addBidResponse, done, ajax); - server.requests[0].respond(200, {}, JSON.stringify(mergeDeep({}, RESPONSE_OPENRTB, FLEDGE_RESP))); - expect(addBidResponse.called).to.be.true; - expectFledgeCalls(); - }); - - it('calls addPaapiConfig when there is no bid in the response', () => { - adapter.callBids(request, bidderRequests, addBidResponse, done, ajax); - server.requests[0].respond(200, {}, JSON.stringify(FLEDGE_RESP)); - expect(addBidResponse.called).to.be.false; - expectFledgeCalls(); - }); - - it('wraps call in runWithBidder', () => { - let fail = false; - fledgeStub.callsFake(({bidder}) => { - try { - expect(bidder).to.exist.and.to.eql(config.getCurrentBidder()); - } catch (e) { - fail = true; - } - }); - adapter.callBids(request, bidderRequests, addBidResponse, done, ajax); - server.requests[0].respond(200, {}, JSON.stringify(FLEDGE_RESP)); - expect(fail).to.be.false; - }) - }); }); describe('bid won events', function () { @@ -3878,9 +3757,9 @@ describe('S2S Adapter', function () { }); it('should translate wurl and burl into eventtrackers', () => { - const burlEvent = {event: 1, method: 1, url: 'burl'}; - const winEvent = {event: 500, method: 1, url: 'events.win'}; - const trackerEvent = {event: 500, method: 1, url: 'eventtracker'}; + const burlEvent = { event: 1, method: 1, url: 'burl' }; + const winEvent = { event: 500, method: 1, url: 'events.win' }; + const trackerEvent = { event: 500, method: 1, url: 'eventtracker' }; const resp = utils.deepClone(RESPONSE_OPENRTB); resp.seatbid[0].bid[0].ext.eventtrackers = [ @@ -4039,7 +3918,7 @@ describe('S2S Adapter', function () { // Add syncEndpoint so that the request goes to the User Sync endpoint // Modify the bidders property to include an alias for Rubicon adapter - s2sConfig.syncEndpoint = {p1Consent: 'https://prebid.adnxs.com/pbs/v1/cookie_sync'}; + s2sConfig.syncEndpoint = { p1Consent: 'https://prebid.adnxs.com/pbs/v1/cookie_sync' }; s2sConfig.bidders = ['appnexus', 'rubicon-alias']; setupAlias(s2sConfig); @@ -4177,7 +4056,7 @@ describe('S2S Adapter', function () { expect(requestBid.ext.prebid.floors).to.be.undefined; - config.setConfig({floors: {}}); + config.setConfig({ floors: {} }); adapter.callBids(REQUEST, bidRequest, addBidResponse, done, ajax); requestBid = JSON.parse(server.requests[1].requestBody); @@ -4239,16 +4118,16 @@ describe('S2S Adapter', function () { code: 'au1', transactionId: 't1', mediaTypes: { - banner: {sizes: [1, 1]} + banner: { sizes: [1, 1] } }, - bids: [{bidder: 'b1', bid_id: 1}] + bids: [{ bidder: 'b1', bid_id: 1 }] }, { code: 'au2', transactionId: 't2', - bids: [{bidder: 'b2', bid_id: 2}], + bids: [{ bidder: 'b2', bid_id: 2 }], mediaTypes: { - banner: {sizes: [1, 1]} + banner: { sizes: [1, 1] } } } ]; @@ -4313,16 +4192,16 @@ describe('S2S Adapter', function () { code: 'au1', transactionId: 't1', mediaTypes: { - banner: {sizes: [1, 1]} + banner: { sizes: [1, 1] } }, - bids: [{bidder: 'b1', bid_id: 1}] + bids: [{ bidder: 'b1', bid_id: 1 }] }, { code: 'au2', transactionId: 't2', - bids: [{bidder: 'b2', bid_id: 2}], + bids: [{ bidder: 'b2', bid_id: 2 }], mediaTypes: { - banner: {sizes: [1, 1]} + banner: { sizes: [1, 1] } }, ortb2Imp: { ext: { @@ -4380,12 +4259,12 @@ describe('S2S Adapter', function () { }, bidder: { bidderA: { - k1: {k3: 'val'} + k1: { k3: 'val' } } }, expected: { bidderA: { - k1: {k3: 'val'} + k1: { k3: 'val' } } } }, @@ -4396,19 +4275,19 @@ describe('S2S Adapter', function () { }, bidder: { bidderA: { - k: {inner: 'val'} + k: { inner: 'val' } } }, expected: { bidderA: { - k: {inner: 'val'} + k: { inner: 'val' } } } }, { t: 'uses bidder config on type mismatch (object/array)', global: { - k: {inner: 'val'} + k: { inner: 'val' } }, bidder: { bidderA: { @@ -4495,32 +4374,32 @@ describe('S2S Adapter', function () { { t: 'does not repeat equal elements', global: { - array: [{id: 1}] + array: [{ id: 1 }] }, bidder: { bidderA: { - array: [{id: 1}, {id: 2}] + array: [{ id: 1 }, { id: 2 }] } }, expected: { bidderA: { - array: [{id: 1}, {id: 2}] + array: [{ id: 1 }, { id: 2 }] } } } - ].forEach(({t, global, bidder, expected}) => { + ].forEach(({ t, global, bidder, expected }) => { it(t, () => { - expect(getPBSBidderConfig({global, bidder})).to.eql(expected); + expect(getPBSBidderConfig({ global, bidder })).to.eql(expected); }) }) }); describe('EID handling', () => { function mkEid(source, value = source) { - return {source, value}; + return { source, value }; } function eidEntry(source, value = source, bidders = false) { - return {eid: {source, value}, bidders}; + return { eid: { source, value }, bidders }; } describe('extractEids', () => { @@ -4622,9 +4501,9 @@ describe('S2S Adapter', function () { ] } } - ].forEach(({t, global = {}, bidder = {}, expected}) => { + ].forEach(({ t, global = {}, bidder = {}, expected }) => { it(t, () => { - const {eids, conflicts} = extractEids({global, bidder}); + const { eids, conflicts } = extractEids({ global, bidder }); expect(eids).to.have.deep.members(expected.eids); expect(Array.from(conflicts)).to.have.members(expected.conflicts || []); }) @@ -4660,7 +4539,7 @@ describe('S2S Adapter', function () { ] })).to.eql({ global: [mkEid('idA'), mkEid('idB')], - permissions: [{source: 'idB', bidders: ['bidderB']}], + permissions: [{ source: 'idB', bidders: ['bidderB'] }], bidder: {} }) }) @@ -4706,7 +4585,7 @@ describe('S2S Adapter', function () { conflicts: new Set(['idA']) })).to.eql({ global: [mkEid('idA', 'idA1'), mkEid('idB')], - permissions: [{source: 'idB', bidders: ['bidderB']}], + permissions: [{ source: 'idB', bidders: ['bidderB'] }], bidder: { bidderA: [mkEid('idA', 'idA2')] } diff --git a/test/spec/modules/previousAuctionInfo_spec.js b/test/spec/modules/previousAuctionInfo_spec.js index 762ba5d10ef..c352ccca9e1 100644 --- a/test/spec/modules/previousAuctionInfo_spec.js +++ b/test/spec/modules/previousAuctionInfo_spec.js @@ -3,7 +3,7 @@ import sinon from 'sinon'; import { expect } from 'chai'; import { config } from 'src/config.js'; import * as events from 'src/events.js'; -import {CONFIG_NS, resetPreviousAuctionInfo, startAuctionHook} from '../../../modules/previousAuctionInfo/index.js'; +import { CONFIG_NS, resetPreviousAuctionInfo, startAuctionHook } from '../../../modules/previousAuctionInfo/index.js'; import { REJECTION_REASON } from '../../../src/constants.js'; describe('previous auction info', () => { @@ -177,7 +177,7 @@ describe('previous auction info', () => { next = sinon.spy(); }); function runHook() { - startAuctionHook(next, {ortb2Fragments: {global, bidder}}); + startAuctionHook(next, { ortb2Fragments: { global, bidder } }); } it('should not add info when none is available', () => { runHook(); @@ -191,8 +191,8 @@ describe('previous auction info', () => { describe('when info is available', () => { beforeEach(() => { Object.assign(previousAuctionInfo.auctionState, { - bidder1: [{transactionId: 'tid1', auction: '1'}], - bidder2: [{transactionId: 'tid2', auction: '2'}] + bidder1: [{ transactionId: 'tid1', auction: '1' }], + bidder2: [{ transactionId: 'tid2', auction: '2' }] }) }) @@ -204,19 +204,19 @@ describe('previous auction info', () => { } it('should set info for enabled bidders, when only some are enabled', () => { - config.setConfig({[CONFIG_NS]: {enabled: true, bidders: ['bidder1']}}); + config.setConfig({ [CONFIG_NS]: { enabled: true, bidders: ['bidder1'] } }); runHook(); expect(extractInfo()).to.eql({ - bidder1: [{auction: '1'}] + bidder1: [{ auction: '1' }] }) }); it('should set info for all bidders, when none is specified', () => { - config.setConfig({[CONFIG_NS]: {enabled: true}}); + config.setConfig({ [CONFIG_NS]: { enabled: true } }); runHook(); expect(extractInfo()).to.eql({ - bidder1: [{auction: '1'}], - bidder2: [{auction: '2'}] + bidder1: [{ auction: '1' }], + bidder2: [{ auction: '2' }] }) }) }) diff --git a/test/spec/modules/priceFloors_spec.js b/test/spec/modules/priceFloors_spec.js index 761e5256674..746c8afd667 100644 --- a/test/spec/modules/priceFloors_spec.js +++ b/test/spec/modules/priceFloors_spec.js @@ -1,4 +1,4 @@ -import {expect} from 'chai'; +import { expect } from 'chai'; import * as utils from 'src/utils.js'; import { getGlobal } from 'src/prebidGlobal.js'; import { EVENTS } from 'src/constants.js'; @@ -19,12 +19,12 @@ import { import * as events from 'src/events.js'; import * as mockGpt from '../integration/faker/googletag.js'; import 'src/prebid.js'; -import {createBid} from '../../../src/bidfactory.js'; -import {auctionManager} from '../../../src/auctionManager.js'; -import {stubAuctionIndex} from '../../helpers/indexStub.js'; -import {guardTids} from '../../../src/adapters/bidderFactory.js'; +import { createBid } from '../../../src/bidfactory.js'; +import { auctionManager } from '../../../src/auctionManager.js'; +import { stubAuctionIndex } from '../../helpers/indexStub.js'; +import { guardTids } from '../../../src/adapters/bidderFactory.js'; import * as activities from '../../../src/activities/rules.js'; -import {server} from '../../mocks/xhr.js'; +import { server } from '../../mocks/xhr.js'; describe('the price floors module', function () { let logErrorSpy; @@ -82,6 +82,7 @@ describe('the price floors module', function () { endpoint: {}, enforcement: { enforceJS: true, + enforceBidders: ['*'], enforcePBS: false, floorDeals: false, bidAdjustment: true @@ -95,6 +96,7 @@ describe('the price floors module', function () { endpoint: {}, enforcement: { enforceJS: true, + enforceBidders: ['*'], enforcePBS: false, floorDeals: false, bidAdjustment: true @@ -108,6 +110,7 @@ describe('the price floors module', function () { endpoint: {}, enforcement: { enforceJS: true, + enforceBidders: ['*'], enforcePBS: false, floorDeals: false, bidAdjustment: true @@ -125,8 +128,8 @@ describe('the price floors module', function () { function getAdUnitMock(code = 'adUnit-code') { return { code, - mediaTypes: {banner: { sizes: [[300, 200], [300, 600]] }, native: {}}, - bids: [{bidder: 'someBidder', adUnitCode: code}, {bidder: 'someOtherBidder', adUnitCode: code}] + mediaTypes: { banner: { sizes: [[300, 200], [300, 600]] }, native: {} }, + bids: [{ bidder: 'someBidder', adUnitCode: code }, { bidder: 'someOtherBidder', adUnitCode: code }] }; } beforeEach(function() { @@ -138,7 +141,7 @@ describe('the price floors module', function () { afterEach(function() { clock.restore(); - handleSetFloorsConfig({enabled: false}); + handleSetFloorsConfig({ enabled: false }); sandbox.restore(); utils.logError.restore(); utils.logWarn.restore(); @@ -320,7 +323,7 @@ describe('the price floors module', function () { default: 0.5 }); - expect(getFirstMatchingFloor(inputFloorData, basicBidRequest, {mediaType: 'banner', size: '*'})).to.deep.equal({ + expect(getFirstMatchingFloor(inputFloorData, basicBidRequest, { mediaType: 'banner', size: '*' })).to.deep.equal({ floorMin: 0, floorRuleValue: 0, matchingFloor: 0, @@ -328,7 +331,7 @@ describe('the price floors module', function () { matchingRule: 'test_div_1' }); - expect(getFirstMatchingFloor(inputFloorData, {...basicBidRequest, adUnitCode: 'test_div_2'}, {mediaType: 'banner', size: '*'})).to.deep.equal({ + expect(getFirstMatchingFloor(inputFloorData, { ...basicBidRequest, adUnitCode: 'test_div_2' }, { mediaType: 'banner', size: '*' })).to.deep.equal({ floorMin: 0, floorRuleValue: 2, matchingFloor: 2, @@ -336,7 +339,7 @@ describe('the price floors module', function () { matchingRule: 'test_div_2' }); - expect(getFirstMatchingFloor(inputFloorData, {...basicBidRequest, adUnitCode: 'test_div_3'}, {mediaType: 'banner', size: '*'})).to.deep.equal({ + expect(getFirstMatchingFloor(inputFloorData, { ...basicBidRequest, adUnitCode: 'test_div_3' }, { mediaType: 'banner', size: '*' })).to.deep.equal({ floorMin: 0, floorRuleValue: 0.5, matchingFloor: 0.5, @@ -396,7 +399,7 @@ describe('the price floors module', function () { }); it('selects the right floor for different mediaTypes', function () { // banner with * size (not in rule file so does not do anything) - expect(getFirstMatchingFloor({...basicFloorData}, basicBidRequest, {mediaType: 'banner', size: '*'})).to.deep.equal({ + expect(getFirstMatchingFloor({ ...basicFloorData }, basicBidRequest, { mediaType: 'banner', size: '*' })).to.deep.equal({ floorMin: 0, floorRuleValue: 1.0, matchingFloor: 1.0, @@ -404,7 +407,7 @@ describe('the price floors module', function () { matchingRule: 'banner' }); // video with * size (not in rule file so does not do anything) - expect(getFirstMatchingFloor({...basicFloorData}, basicBidRequest, {mediaType: 'video', size: '*'})).to.deep.equal({ + expect(getFirstMatchingFloor({ ...basicFloorData }, basicBidRequest, { mediaType: 'video', size: '*' })).to.deep.equal({ floorMin: 0, floorRuleValue: 5.0, matchingFloor: 5.0, @@ -412,7 +415,7 @@ describe('the price floors module', function () { matchingRule: 'video' }); // native (not in the rule list) with * size (not in rule file so does not do anything) - expect(getFirstMatchingFloor({...basicFloorData}, basicBidRequest, {mediaType: 'native', size: '*'})).to.deep.equal({ + expect(getFirstMatchingFloor({ ...basicFloorData }, basicBidRequest, { mediaType: 'native', size: '*' })).to.deep.equal({ floorMin: 0, floorRuleValue: 2.5, matchingFloor: 2.5, @@ -423,7 +426,7 @@ describe('the price floors module', function () { handleSetFloorsConfig({ ...minFloorConfigHigh }); - expect(getFirstMatchingFloor({...basicFloorDataHigh}, basicBidRequest, {mediaType: 'banner', size: '*'})).to.deep.equal({ + expect(getFirstMatchingFloor({ ...basicFloorDataHigh }, basicBidRequest, { mediaType: 'banner', size: '*' })).to.deep.equal({ floorMin: 7, floorRuleValue: 1.0, matchingFloor: 7, @@ -434,7 +437,7 @@ describe('the price floors module', function () { handleSetFloorsConfig({ ...minFloorConfigLow }); - expect(getFirstMatchingFloor({...basicFloorDataLow}, basicBidRequest, {mediaType: 'video', size: '*'})).to.deep.equal({ + expect(getFirstMatchingFloor({ ...basicFloorDataLow }, basicBidRequest, { mediaType: 'video', size: '*' })).to.deep.equal({ floorMin: 2.3, floorRuleValue: 5, matchingFloor: 5, @@ -443,9 +446,9 @@ describe('the price floors module', function () { }); }); it('does not alter cached matched input if conversion occurs', function () { - const inputData = {...basicFloorData}; + const inputData = { ...basicFloorData }; [0.2, 0.4, 0.6, 0.8].forEach(modifier => { - const result = getFirstMatchingFloor(inputData, basicBidRequest, {mediaType: 'banner', size: '*'}); + const result = getFirstMatchingFloor(inputData, basicBidRequest, { mediaType: 'banner', size: '*' }); // result should always be the same expect(result).to.deep.equal({ floorMin: 0, @@ -474,7 +477,7 @@ describe('the price floors module', function () { } } // banner with 300x250 size - expect(getFirstMatchingFloor(inputFloorData, basicBidRequest, {mediaType: 'banner', size: [300, 250]})).to.deep.equal({ + expect(getFirstMatchingFloor(inputFloorData, basicBidRequest, { mediaType: 'banner', size: [300, 250] })).to.deep.equal({ floorMin: 0, floorRuleValue: 1.1, matchingFloor: 1.1, @@ -482,7 +485,7 @@ describe('the price floors module', function () { matchingRule: '300x250' }); // video with 300x250 size - expect(getFirstMatchingFloor(inputFloorData, basicBidRequest, {mediaType: 'video', size: [300, 250]})).to.deep.equal({ + expect(getFirstMatchingFloor(inputFloorData, basicBidRequest, { mediaType: 'video', size: [300, 250] })).to.deep.equal({ floorMin: 0, floorRuleValue: 1.1, matchingFloor: 1.1, @@ -490,7 +493,7 @@ describe('the price floors module', function () { matchingRule: '300x250' }); // native (not in the rule list) with 300x600 size - expect(getFirstMatchingFloor(inputFloorData, basicBidRequest, {mediaType: 'native', size: [600, 300]})).to.deep.equal({ + expect(getFirstMatchingFloor(inputFloorData, basicBidRequest, { mediaType: 'native', size: [600, 300] })).to.deep.equal({ floorMin: 0, floorRuleValue: 4.4, matchingFloor: 4.4, @@ -498,7 +501,7 @@ describe('the price floors module', function () { matchingRule: '600x300' }); // n/a mediaType with a size not in file should go to catch all - expect(getFirstMatchingFloor(inputFloorData, basicBidRequest, {mediaType: undefined, size: [1, 1]})).to.deep.equal({ + expect(getFirstMatchingFloor(inputFloorData, basicBidRequest, { mediaType: undefined, size: [1, 1] })).to.deep.equal({ floorMin: 0, floorRuleValue: 5.5, matchingFloor: 5.5, @@ -523,7 +526,7 @@ describe('the price floors module', function () { default: 0.5 }); // banner with 300x250 size - expect(getFirstMatchingFloor(inputFloorData, basicBidRequest, {mediaType: 'banner', size: [300, 250]})).to.deep.equal({ + expect(getFirstMatchingFloor(inputFloorData, basicBidRequest, { mediaType: 'banner', size: [300, 250] })).to.deep.equal({ floorMin: 0, floorRuleValue: 1.1, matchingFloor: 1.1, @@ -531,7 +534,7 @@ describe('the price floors module', function () { matchingRule: 'test_div_1^banner^300x250' }); // video with 300x250 size -> No matching rule so should use default - expect(getFirstMatchingFloor(inputFloorData, basicBidRequest, {mediaType: 'video', size: [300, 250]})).to.deep.equal({ + expect(getFirstMatchingFloor(inputFloorData, basicBidRequest, { mediaType: 'video', size: [300, 250] })).to.deep.equal({ floorMin: 0, floorRuleValue: 0.5, matchingFloor: 0.5, @@ -540,7 +543,7 @@ describe('the price floors module', function () { }); // remove default and should still return the same floor as above since matches are cached delete inputFloorData.default; - expect(getFirstMatchingFloor(inputFloorData, basicBidRequest, {mediaType: 'video', size: [300, 250]})).to.deep.equal({ + expect(getFirstMatchingFloor(inputFloorData, basicBidRequest, { mediaType: 'video', size: [300, 250] })).to.deep.equal({ floorMin: 0, floorRuleValue: 0.5, matchingFloor: 0.5, @@ -549,7 +552,7 @@ describe('the price floors module', function () { }); // update adUnitCode to test_div_2 with weird other params const newBidRequest = { ...basicBidRequest, adUnitCode: 'test_div_2' } - expect(getFirstMatchingFloor(inputFloorData, newBidRequest, {mediaType: 'badmediatype', size: [900, 900]})).to.deep.equal({ + expect(getFirstMatchingFloor(inputFloorData, newBidRequest, { mediaType: 'badmediatype', size: [900, 900] })).to.deep.equal({ floorMin: 0, floorRuleValue: 3.3, matchingFloor: 3.3, @@ -559,12 +562,12 @@ describe('the price floors module', function () { }); it('it does not break if floorData has bad values', function () { let inputFloorData = {}; - expect(getFirstMatchingFloor(inputFloorData, basicBidRequest, {mediaType: 'banner', size: '*'})).to.deep.equal({ + expect(getFirstMatchingFloor(inputFloorData, basicBidRequest, { mediaType: 'banner', size: '*' })).to.deep.equal({ matchingFloor: undefined }); // if default is there use it inputFloorData = normalizeDefault({ default: 5.0 }); - expect(getFirstMatchingFloor(inputFloorData, basicBidRequest, {mediaType: 'banner', size: '*'}).matchingFloor).to.equal(5.0); + expect(getFirstMatchingFloor(inputFloorData, basicBidRequest, { mediaType: 'banner', size: '*' }).matchingFloor).to.equal(5.0); }); describe('with gpt enabled', function () { let gptFloorData; @@ -595,7 +598,7 @@ describe('the price floors module', function () { divId: 'test_div_2' }); indexStub = sinon.stub(auctionManager, 'index'); - indexStub.get(() => stubAuctionIndex({adUnits})) + indexStub.get(() => stubAuctionIndex({ adUnits })) }); afterEach(function () { // reset it so no lingering stuff from other test specs @@ -622,7 +625,7 @@ describe('the price floors module', function () { }); it('picks the gptSlot from the adUnit and does not call the slotMatching', function () { const newBidRequest1 = { ...basicBidRequest, adUnitId: 'au1' }; - adUnits = [{code: newBidRequest1.adUnitCode, adUnitId: 'au1'}]; + adUnits = [{ code: newBidRequest1.adUnitCode, adUnitId: 'au1' }]; utils.deepSetValue(adUnits[0], 'ortb2Imp.ext.data.adserver', { name: 'gam', adslot: '/12345/news/politics' @@ -636,7 +639,7 @@ describe('the price floors module', function () { }); const newBidRequest2 = { ...basicBidRequest, adUnitCode: 'test_div_2', adUnitId: 'au2' }; - adUnits = [{code: newBidRequest2.adUnitCode, adUnitId: newBidRequest2.adUnitId}]; + adUnits = [{ code: newBidRequest2.adUnitCode, adUnitId: newBidRequest2.adUnitId }]; utils.deepSetValue(adUnits[0], 'ortb2Imp.ext.data.adserver', { name: 'gam', adslot: '/12345/news/weather' @@ -774,11 +777,11 @@ describe('the price floors module', function () { let actualAllowedFields = allowedFields; let actualFieldMatchingFunctions = fieldMatchingFunctions; const defaultAllowedFields = [...allowedFields]; - const defaultMatchingFunctions = {...fieldMatchingFunctions}; + const defaultMatchingFunctions = { ...fieldMatchingFunctions }; afterEach(function() { exposedAdUnits = undefined; actualAllowedFields = [...defaultAllowedFields]; - actualFieldMatchingFunctions = {...defaultMatchingFunctions}; + actualFieldMatchingFunctions = { ...defaultMatchingFunctions }; }); it('should not do floor stuff if no resulting floor object can be resolved for auciton', function () { handleSetFloorsConfig({ @@ -804,7 +807,8 @@ describe('the price floors module', function () { data: { ...basicFloorDataLow, noFloorSignalBidders: ['someBidder', 'someOtherBidder'] - }}); + } + }); runStandardAuction(); validateBidRequests(false, { skipped: false, @@ -820,7 +824,8 @@ describe('the price floors module', function () { }) }); it('should not do floor stuff if floors.enforcement is defined by noFloorSignalBidders[]', function() { - handleSetFloorsConfig({ ...basicFloorConfig, + handleSetFloorsConfig({ + ...basicFloorConfig, enforcement: { enforceJS: true, noFloorSignalBidders: ['someBidder', 'someOtherBidder'] @@ -842,7 +847,8 @@ describe('the price floors module', function () { }) }); it('should not do floor stuff and use first floors.data.noFloorSignalBidders if its defined betwen enforcement.noFloorSignalBidders', function() { - handleSetFloorsConfig({ ...basicFloorConfig, + handleSetFloorsConfig({ + ...basicFloorConfig, enforcement: { enforceJS: true, noFloorSignalBidders: ['someBidder'] @@ -867,7 +873,8 @@ describe('the price floors module', function () { }) }); it('it shouldn`t return floor stuff for bidder in the noFloorSignalBidders list', function() { - handleSetFloorsConfig({ ...basicFloorConfig, + handleSetFloorsConfig({ + ...basicFloorConfig, enforcement: { enforceJS: true, }, @@ -893,7 +900,8 @@ describe('the price floors module', function () { }); }) it('it should return floor stuff if we defined wrong bidder name in data.noFloorSignalBidders', function() { - handleSetFloorsConfig({ ...basicFloorConfig, + handleSetFloorsConfig({ + ...basicFloorConfig, enforcement: { enforceJS: true, }, @@ -1016,8 +1024,8 @@ describe('the price floors module', function () { ...basicFloorConfig, data: undefined }); - adUnits[0].floors = {default: 1}; - adUnits[1].floors = {default: 2}; + adUnits[0].floors = { default: 1 }; + adUnits[1].floors = { default: 2 }; expectFloors([1, 2]) }); it('on an adUnit with hidden schema', () => { @@ -1078,7 +1086,7 @@ describe('the price floors module', function () { }); }) it('bidRequests should have getFloor function and flooring meta data when setConfig occurs', function () { - handleSetFloorsConfig({...basicFloorConfig, floorProvider: 'floorprovider'}); + handleSetFloorsConfig({ ...basicFloorConfig, floorProvider: 'floorprovider' }); runStandardAuction(); validateBidRequests(true, { skipped: false, @@ -1301,24 +1309,24 @@ describe('the price floors module', function () { }); }); it('should ignore and reset floor data when provided with invalid data', function () { - handleSetFloorsConfig({...basicFloorConfig}); + handleSetFloorsConfig({ ...basicFloorConfig }); handleSetFloorsConfig({ ...basicFloorConfig, data: { - schema: {fields: ['thisIsNotAllowedSoShouldFail']}, - values: {'*': 1.2}, + schema: { fields: ['thisIsNotAllowedSoShouldFail'] }, + values: { '*': 1.2 }, modelVersion: 'FAIL' } }); runStandardAuction(); - validateBidRequests(false, sinon.match({location: 'noData', skipped: true})); + validateBidRequests(false, sinon.match({ location: 'noData', skipped: true })); }); it('should dynamically add new schema fileds and functions if added via setConfig', function () { let deviceSpoof; handleSetFloorsConfig({ ...basicFloorConfig, data: { - schema: {fields: ['deviceType']}, + schema: { fields: ['deviceType'] }, values: { 'mobile': 1.0, 'desktop': 2.0, @@ -1366,7 +1374,7 @@ describe('the price floors module', function () { }); }); it('Should continue auction of delay is hit without a response from floor provider', function () { - handleSetFloorsConfig({...basicFloorConfig, auctionDelay: 250, endpoint: {url: 'http://www.fakefloorprovider.json//'}}); + handleSetFloorsConfig({ ...basicFloorConfig, auctionDelay: 250, endpoint: { url: 'http://www.fakefloorprovider.json//' } }); // start the auction it should delay and not immediately call `continueAuction` runStandardAuction(); @@ -1404,7 +1412,7 @@ describe('the price floors module', function () { server.respondWith(JSON.stringify(fetchFloorData)); // run setConfig indicating fetch - handleSetFloorsConfig({...basicFloorConfig, floorProvider: 'floorprovider', auctionDelay: 250, endpoint: {url: 'http://www.fakefloorprovider.json/'}}); + handleSetFloorsConfig({ ...basicFloorConfig, floorProvider: 'floorprovider', auctionDelay: 250, endpoint: { url: 'http://www.fakefloorprovider.json/' } }); // floor provider should be called expect(server.requests.length).to.equal(1); @@ -1444,7 +1452,7 @@ describe('the price floors module', function () { server.respondWith(JSON.stringify(fetchFloorData)); // run setConfig indicating fetch - handleSetFloorsConfig({...basicFloorConfig, floorProvider: 'floorproviderC', auctionDelay: 250, endpoint: {url: 'http://www.fakefloorprovider.json/'}}); + handleSetFloorsConfig({ ...basicFloorConfig, floorProvider: 'floorproviderC', auctionDelay: 250, endpoint: { url: 'http://www.fakefloorprovider.json/' } }); // floor provider should be called expect(server.requests.length).to.equal(1); @@ -1485,7 +1493,7 @@ describe('the price floors module', function () { server.respondWith(JSON.stringify(fetchFloorData)); // run setConfig indicating fetch - handleSetFloorsConfig({...basicFloorConfig, floorProvider: 'floorprovider', auctionDelay: 250, endpoint: {url: 'http://www.fakefloorprovider.json/'}}); + handleSetFloorsConfig({ ...basicFloorConfig, floorProvider: 'floorprovider', auctionDelay: 250, endpoint: { url: 'http://www.fakefloorprovider.json/' } }); // floor provider should be called expect(server.requests.length).to.equal(1); @@ -1517,7 +1525,7 @@ describe('the price floors module', function () { }); it('Should not break if floor provider returns 404', function () { // run setConfig indicating fetch - handleSetFloorsConfig({...basicFloorConfig, auctionDelay: 250, endpoint: {url: 'http://www.fakefloorprovider.json/'}}); + handleSetFloorsConfig({ ...basicFloorConfig, auctionDelay: 250, endpoint: { url: 'http://www.fakefloorprovider.json/' } }); // run the auction and make server respond with 404 server.respond(); @@ -1543,7 +1551,7 @@ describe('the price floors module', function () { server.respondWith('Not valid response'); // run setConfig indicating fetch - handleSetFloorsConfig({...basicFloorConfig, auctionDelay: 250, endpoint: {url: 'http://www.fakefloorprovider.json/'}}); + handleSetFloorsConfig({ ...basicFloorConfig, auctionDelay: 250, endpoint: { url: 'http://www.fakefloorprovider.json/' } }); // run the auction and make server respond server.respond(); @@ -1568,8 +1576,8 @@ describe('the price floors module', function () { it('should handle not using fetch correctly', function () { // run setConfig twice indicating fetch server.respondWith(JSON.stringify(basicFloorData)); - handleSetFloorsConfig({...basicFloorConfig, auctionDelay: 250, endpoint: {url: 'http://www.fakefloorprovider.json/'}}); - handleSetFloorsConfig({...basicFloorConfig, auctionDelay: 250, endpoint: {url: 'http://www.fakefloorprovider.json/'}}); + handleSetFloorsConfig({ ...basicFloorConfig, auctionDelay: 250, endpoint: { url: 'http://www.fakefloorprovider.json/' } }); + handleSetFloorsConfig({ ...basicFloorConfig, auctionDelay: 250, endpoint: { url: 'http://www.fakefloorprovider.json/' } }); // log warn should be called and server only should have one request expect(logWarnSpy.calledOnce).to.equal(true); @@ -1578,7 +1586,7 @@ describe('the price floors module', function () { // now we respond and then run again it should work and make another request server.respond(); - handleSetFloorsConfig({...basicFloorConfig, auctionDelay: 250, endpoint: {url: 'http://www.fakefloorprovider.json/'}}); + handleSetFloorsConfig({ ...basicFloorConfig, auctionDelay: 250, endpoint: { url: 'http://www.fakefloorprovider.json/' } }); server.respond(); // now warn still only called once and server called twice @@ -1587,7 +1595,7 @@ describe('the price floors module', function () { // should log error if method is not GET for now expect(logErrorSpy.calledOnce).to.equal(false); - handleSetFloorsConfig({...basicFloorConfig, endpoint: {url: 'http://www.fakefloorprovider.json/', method: 'POST'}}); + handleSetFloorsConfig({ ...basicFloorConfig, endpoint: { url: 'http://www.fakefloorprovider.json/', method: 'POST' } }); expect(logErrorSpy.calledOnce).to.equal(true); }); describe('isFloorsDataValid', function () { @@ -1700,7 +1708,7 @@ describe('the price floors module', function () { inputFloorData.modelGroups[1].modelWeight = 40; // remove values from a model and it should not validate - const tempValues = {...inputFloorData.modelGroups[0].values}; + const tempValues = { ...inputFloorData.modelGroups[0].values }; delete inputFloorData.modelGroups[0].values; expect(isFloorsDataValid(inputFloorData)).to.to.equal(false); inputFloorData.modelGroups[0].values = tempValues; @@ -1731,21 +1739,21 @@ describe('the price floors module', function () { }); // ask for banner - inputParams = {mediaType: 'banner'}; + inputParams = { mediaType: 'banner' }; expect(bidRequest.getFloor(inputParams)).to.deep.equal({ currency: 'USD', floor: 1.0 }); // ask for video - inputParams = {mediaType: 'video'}; + inputParams = { mediaType: 'video' }; expect(bidRequest.getFloor(inputParams)).to.deep.equal({ currency: 'USD', floor: 5.0 }); // ask for * - inputParams = {mediaType: '*'}; + inputParams = { mediaType: '*' }; expect(bidRequest.getFloor(inputParams)).to.deep.equal({ currency: 'USD', floor: 2.5 @@ -1757,7 +1765,7 @@ describe('the price floors module', function () { const req = utils.deepClone(bidRequest); _floorDataForAuction[req.auctionId] = utils.deepClone(basicFloorConfig); - expect(guardTids({bidderCode: 'mock-bidder'}).bidRequest(req).getFloor({})).to.deep.equal({ + expect(guardTids({ bidderCode: 'mock-bidder' }).bidRequest(req).getFloor({})).to.deep.equal({ currency: 'USD', floor: 1.0 }); @@ -1789,28 +1797,28 @@ describe('the price floors module', function () { }); // ask for banner with a size - inputParams = {mediaType: 'banner', size: [300, 600]}; + inputParams = { mediaType: 'banner', size: [300, 600] }; expect(bidRequest.getFloor(inputParams)).to.deep.equal({ currency: 'USD', floor: 1.5 }); // ask for video with a size - inputParams = {mediaType: 'video', size: [640, 480]}; + inputParams = { mediaType: 'video', size: [640, 480] }; expect(bidRequest.getFloor(inputParams)).to.deep.equal({ currency: 'USD', floor: 4.5 }); // ask for video with a size not in rules (should pick rule which has video and *) - inputParams = {mediaType: 'video', size: [111, 222]}; + inputParams = { mediaType: 'video', size: [111, 222] }; expect(bidRequest.getFloor(inputParams)).to.deep.equal({ currency: 'USD', floor: 5.5 }); // ask for native * but no native rule so should use default value if there - inputParams = {mediaType: 'native', size: '*'}; + inputParams = { mediaType: 'native', size: '*' }; expect(bidRequest.getFloor(inputParams)).to.deep.equal({ currency: 'USD', floor: 10.0 @@ -1824,14 +1832,14 @@ describe('the price floors module', function () { }; // assumes banner * - let inputParams = {mediaType: 'banner'}; + let inputParams = { mediaType: 'banner' }; expect(bidRequest.getFloor(inputParams)).to.deep.equal({ currency: 'USD', floor: 1.7778 }); // assumes banner * - inputParams = {mediaType: 'video'}; + inputParams = { mediaType: 'video' }; expect(bidRequest.getFloor(inputParams)).to.deep.equal({ currency: 'USD', floor: 1.1112 @@ -2030,18 +2038,18 @@ describe('the price floors module', function () { inverseParams: {} }, 'only mediaType': { - getFloorParams: {mediaType: 'video'}, - inverseParams: {mediaType: 'video'} + getFloorParams: { mediaType: 'video' }, + inverseParams: { mediaType: 'video' } }, 'only size': { - getFloorParams: {mediaType: '*', size: [1, 2]}, - inverseParams: {size: [1, 2]} + getFloorParams: { mediaType: '*', size: [1, 2] }, + inverseParams: { size: [1, 2] } }, 'both': { - getFloorParams: {mediaType: 'banner', size: [1, 2]}, - inverseParams: {mediaType: 'banner', size: [1, 2]} + getFloorParams: { mediaType: 'banner', size: [1, 2] }, + inverseParams: { mediaType: 'banner', size: [1, 2] } } - }).forEach(([t, {getFloorParams, inverseParams}]) => { + }).forEach(([t, { getFloorParams, inverseParams }]) => { it(`should pass inverseFloorAdjustment mediatype and size (${t})`, () => { getGlobal().bidderSettings = { standard: { @@ -2146,23 +2154,23 @@ describe('the price floors module', function () { }; // because bid req only has video, if a bidder asks for a floor for * we can actually give them the right mediaType - expect(inputBidReq.getFloor({mediaType: '*'})).to.deep.equal({ + expect(inputBidReq.getFloor({ mediaType: '*' })).to.deep.equal({ currency: 'USD', floor: 5.0 // 'video': 5.0 }); delete _floorDataForAuction[inputBidReq.auctionId].data.matchingInputs; // Same for if only banner is in the input bid - inputBidReq.mediaTypes = {banner: {}}; - expect(inputBidReq.getFloor({mediaType: '*'})).to.deep.equal({ + inputBidReq.mediaTypes = { banner: {} }; + expect(inputBidReq.getFloor({ mediaType: '*' })).to.deep.equal({ currency: 'USD', floor: 3.0 // 'banner': 3.0, }); delete _floorDataForAuction[inputBidReq.auctionId].data.matchingInputs; // if both are present then it will really use the * - inputBidReq.mediaTypes = {banner: {}, video: {}}; - expect(inputBidReq.getFloor({mediaType: '*'})).to.deep.equal({ + inputBidReq.mediaTypes = { banner: {}, video: {} }; + expect(inputBidReq.getFloor({ mediaType: '*' })).to.deep.equal({ currency: 'USD', floor: 1.0 // '*': 1.0, }); @@ -2181,47 +2189,47 @@ describe('the price floors module', function () { }; // mediaType is banner and only one size, so if someone asks for banner * we should give them banner 300x250 // instead of banner|* - inputBidReq.mediaTypes = {banner: {sizes: [[300, 250]]}}; - expect(inputBidReq.getFloor({mediaType: 'banner', size: '*'})).to.deep.equal({ + inputBidReq.mediaTypes = { banner: { sizes: [[300, 250]] } }; + expect(inputBidReq.getFloor({ mediaType: 'banner', size: '*' })).to.deep.equal({ currency: 'USD', floor: 2.0 // 'banner|300x250': 2.0, }); delete _floorDataForAuction[inputBidReq.auctionId].data.matchingInputs; // now for video it should look at playersize (prebid core translates playersize into typical array of size arrays) - inputBidReq.mediaTypes = {video: {playerSize: [[728, 90]]}}; - expect(inputBidReq.getFloor({mediaType: 'video', size: '*'})).to.deep.equal({ + inputBidReq.mediaTypes = { video: { playerSize: [[728, 90]] } }; + expect(inputBidReq.getFloor({ mediaType: 'video', size: '*' })).to.deep.equal({ currency: 'USD', floor: 6.0 // 'video|728x90': 6.0, }); delete _floorDataForAuction[inputBidReq.auctionId].data.matchingInputs; // Now if multiple sizes are there, it will actually use * since can't infer - inputBidReq.mediaTypes = {banner: {sizes: [[300, 250], [728, 90]]}}; - expect(inputBidReq.getFloor({mediaType: 'banner', size: '*'})).to.deep.equal({ + inputBidReq.mediaTypes = { banner: { sizes: [[300, 250], [728, 90]] } }; + expect(inputBidReq.getFloor({ mediaType: 'banner', size: '*' })).to.deep.equal({ currency: 'USD', floor: 4.0 // 'banner|*': 4.0, }); delete _floorDataForAuction[inputBidReq.auctionId].data.matchingInputs; // lastly, if you pass in * mediaType and * size it should resolve both if possble - inputBidReq.mediaTypes = {banner: {sizes: [[300, 250]]}}; - expect(inputBidReq.getFloor({mediaType: '*', size: '*'})).to.deep.equal({ + inputBidReq.mediaTypes = { banner: { sizes: [[300, 250]] } }; + expect(inputBidReq.getFloor({ mediaType: '*', size: '*' })).to.deep.equal({ currency: 'USD', floor: 2.0 // 'banner|300x250': 2.0, }); delete _floorDataForAuction[inputBidReq.auctionId].data.matchingInputs; - inputBidReq.mediaTypes = {video: {playerSize: [[300, 250]]}}; - expect(inputBidReq.getFloor({mediaType: '*', size: '*'})).to.deep.equal({ + inputBidReq.mediaTypes = { video: { playerSize: [[300, 250]] } }; + expect(inputBidReq.getFloor({ mediaType: '*', size: '*' })).to.deep.equal({ currency: 'USD', floor: 5.0 // 'video|300x250': 5.0, }); delete _floorDataForAuction[inputBidReq.auctionId].data.matchingInputs; // now it has both mediaTypes so will use * mediaType and thus not use sizes either - inputBidReq.mediaTypes = {video: {playerSize: [[300, 250]]}, banner: {sizes: [[300, 250]]}}; - expect(inputBidReq.getFloor({mediaType: '*', size: '*'})).to.deep.equal({ + inputBidReq.mediaTypes = { video: { playerSize: [[300, 250]] }, banner: { sizes: [[300, 250]] } }; + expect(inputBidReq.getFloor({ mediaType: '*', size: '*' })).to.deep.equal({ currency: 'USD', floor: 1.0 // '*|*': 1.0, }); @@ -2247,9 +2255,9 @@ describe('the price floors module', function () { }; beforeEach(function () { returnedBidResponse = null; - reject = sinon.stub().returns({status: 'rejected'}); + reject = sinon.stub().returns({ status: 'rejected' }); indexStub = sinon.stub(auctionManager, 'index'); - indexStub.get(() => stubAuctionIndex({adUnits: [adUnit]})); + indexStub.get(() => stubAuctionIndex({ adUnits: [adUnit] })); }); afterEach(() => { @@ -2286,6 +2294,51 @@ describe('the price floors module', function () { expect(reject.calledOnce).to.be.true; expect(returnedBidResponse).to.not.exist; }); + it('enforces floors for all bidders by default', function () { + _floorDataForAuction[AUCTION_ID] = utils.deepClone(basicFloorConfig); + _floorDataForAuction[AUCTION_ID].data.values = { 'banner': 1.0 }; + returnedBidResponse = null; + runBidResponse({ + ...basicBidResponse, + bidderCode: 'rubicon' + }); + expect(reject.calledOnce).to.be.true; + expect(returnedBidResponse).to.equal(null); + }); + it('enforces floors only for configured enforceBidders when provided', function () { + _floorDataForAuction[AUCTION_ID] = utils.deepClone(basicFloorConfig); + _floorDataForAuction[AUCTION_ID].enforcement.enforceBidders = ['rubicon']; + _floorDataForAuction[AUCTION_ID].data.values = { 'banner': 1.0 }; + + runBidResponse({ + ...basicBidResponse, + bidderCode: 'appnexus' + }); + expect(reject.called).to.be.false; + expect(returnedBidResponse).to.haveOwnProperty('floorData'); + + returnedBidResponse = null; + runBidResponse({ + ...basicBidResponse, + bidderCode: 'rubicon' + }); + expect(reject.calledOnce).to.be.true; + expect(returnedBidResponse).to.equal(null); + }); + it('uses adapterCode when checking enforceBidders', function () { + _floorDataForAuction[AUCTION_ID] = utils.deepClone(basicFloorConfig); + _floorDataForAuction[AUCTION_ID].enforcement.enforceBidders = ['rubicon']; + _floorDataForAuction[AUCTION_ID].data.values = { 'banner': 1.0 }; + + runBidResponse({ + ...basicBidResponse, + bidderCode: 'alternateBidder', + adapterCode: 'rubicon' + }); + + expect(reject.calledOnce).to.be.true; + expect(returnedBidResponse).to.equal(null); + }); it('if it finds a rule and does not floor should update the bid accordingly', function () { _floorDataForAuction[AUCTION_ID] = utils.deepClone(basicFloorConfig); _floorDataForAuction[AUCTION_ID].data.values = { 'banner': 0.3 }; @@ -2299,6 +2352,7 @@ describe('the price floors module', function () { cpmAfterAdjustments: 0.5, enforcements: { bidAdjustment: true, + enforceBidders: ['*'], enforceJS: true, enforcePBS: false, floorDeals: false @@ -2336,6 +2390,7 @@ describe('the price floors module', function () { cpmAfterAdjustments: 0.5, enforcements: { bidAdjustment: true, + enforceBidders: ['*'], enforceJS: true, enforcePBS: false, floorDeals: false @@ -2364,6 +2419,7 @@ describe('the price floors module', function () { cpmAfterAdjustments: 7.5, enforcements: { bidAdjustment: true, + enforceBidders: ['*'], enforceJS: true, enforcePBS: false, floorDeals: false @@ -2385,7 +2441,7 @@ describe('the price floors module', function () { }; }); it('should wait 3 seconds before deleting auction floor data', function () { - handleSetFloorsConfig({enabled: true}); + handleSetFloorsConfig({ enabled: true }); _floorDataForAuction[AUCTION_END_EVENT.auctionId] = utils.deepClone(basicFloorConfig); events.emit(EVENTS.AUCTION_END, AUCTION_END_EVENT); // should still be here @@ -2417,7 +2473,7 @@ describe('the price floors module', function () { { code: req.adUnitCode, adUnitId: req.adUnitId, - ortb2Imp: {ext: {data: {adserver: {name: 'gam', adslot: 'slot'}}}} + ortb2Imp: { ext: { data: { adserver: { name: 'gam', adslot: 'slot' } } } } } ] })); @@ -2488,7 +2544,7 @@ describe('setting null as rule value', () => { } _floorDataForAuction[bidRequest.auctionId] = basicFloorConfig; - const inputParams = {mediaType: 'banner', size: [600, 300]}; + const inputParams = { mediaType: 'banner', size: [600, 300] }; expect(bidRequest.getFloor(inputParams)).to.deep.equal(null); }) @@ -2508,12 +2564,12 @@ describe('setting null as rule value', () => { server.respondWith(JSON.stringify(nullFloorData)); let exposedAdUnits; - handleSetFloorsConfig({...basicFloorConfig, floorProvider: 'floorprovider', endpoint: {url: 'http://www.fakefloorprovider.json/'}}); + handleSetFloorsConfig({ ...basicFloorConfig, floorProvider: 'floorprovider', endpoint: { url: 'http://www.fakefloorprovider.json/' } }); const adUnits = [{ cod: 'test_div_1', - mediaTypes: {banner: { sizes: [[600, 300]] }, native: {}}, - bids: [{bidder: 'someBidder', adUnitCode: 'test_div_1'}, {bidder: 'someOtherBidder', adUnitCode: 'test_div_1'}] + mediaTypes: { banner: { sizes: [[600, 300]] }, native: {} }, + bids: [{ bidder: 'someBidder', adUnitCode: 'test_div_1' }, { bidder: 'someOtherBidder', adUnitCode: 'test_div_1' }] }]; requestBidsHook(config => exposedAdUnits = config.adUnits, { @@ -2521,7 +2577,7 @@ describe('setting null as rule value', () => { adUnits }); - const inputParams = {mediaType: 'banner', size: [600, 300]}; + const inputParams = { mediaType: 'banner', size: [600, 300] }; expect(exposedAdUnits[0].bids[0].getFloor(inputParams)).to.deep.equal(null); }); diff --git a/test/spec/modules/prismaBidAdapter_spec.js b/test/spec/modules/prismaBidAdapter_spec.js index a368378a481..e715348f6f0 100644 --- a/test/spec/modules/prismaBidAdapter_spec.js +++ b/test/spec/modules/prismaBidAdapter_spec.js @@ -1,7 +1,7 @@ -import {expect} from 'chai'; -import {spec} from 'modules/prismaBidAdapter.js'; -import {newBidder} from 'src/adapters/bidderFactory.js'; -import {config} from 'src/config.js'; +import { expect } from 'chai'; +import { spec } from 'modules/prismaBidAdapter.js'; +import { newBidder } from 'src/adapters/bidderFactory.js'; +import { config } from 'src/config.js'; import * as utils from 'src/utils.js'; import { requestBidsHook } from 'modules/consentManagementTcf.js'; @@ -51,25 +51,27 @@ describe('Prisma bid adapter tests', function () { 'bidderWinsCount': 0 }]; - const DISPLAY_BID_RESPONSE = {'body': { - 'responses': [ - { - 'bidId': '4d9e29504f8af6', - 'cpm': 0.437245, - 'width': 300, - 'height': 250, - 'creativeId': '98493581', - 'currency': 'EUR', - 'netRevenue': true, - 'type': 'banner', - 'ttl': 360, - 'uuid': 'ce6d1ee3-2a05-4d7c-b97a-9e62097798ec', - 'bidder': 'appnexus', - 'consent': 1, - 'tagId': 'luvxjvgn' - } - ], - }}; + const DISPLAY_BID_RESPONSE = { + 'body': { + 'responses': [ + { + 'bidId': '4d9e29504f8af6', + 'cpm': 0.437245, + 'width': 300, + 'height': 250, + 'creativeId': '98493581', + 'currency': 'EUR', + 'netRevenue': true, + 'type': 'banner', + 'ttl': 360, + 'uuid': 'ce6d1ee3-2a05-4d7c-b97a-9e62097798ec', + 'bidder': 'appnexus', + 'consent': 1, + 'tagId': 'luvxjvgn' + } + ], + } + }; const VIDEO_BID_REQUEST = [ { @@ -101,25 +103,27 @@ describe('Prisma bid adapter tests', function () { } ] - const VIDEO_BID_RESPONSE = {'body': { - 'responses': [ - { - 'bidId': '2c129e8e01859a', - 'type': 'video', - 'uuid': 'b8e7b2f0-c378-479f-aa4f-4f55d5d7d1d5', - 'cpm': 4.5421, - 'width': 1, - 'height': 1, - 'creativeId': '97517771', - 'currency': 'EUR', - 'netRevenue': true, - 'ttl': 360, - 'bidder': 'appnexus', - 'consent': 1, - 'tagId': 'yqsc1tfj' - } - ] - }}; + const VIDEO_BID_RESPONSE = { + 'body': { + 'responses': [ + { + 'bidId': '2c129e8e01859a', + 'type': 'video', + 'uuid': 'b8e7b2f0-c378-479f-aa4f-4f55d5d7d1d5', + 'cpm': 4.5421, + 'width': 1, + 'height': 1, + 'creativeId': '97517771', + 'currency': 'EUR', + 'netRevenue': true, + 'ttl': 360, + 'bidder': 'appnexus', + 'consent': 1, + 'tagId': 'yqsc1tfj' + } + ] + } + }; const DEFAULT_OPTIONS = { gdprConsent: { @@ -242,7 +246,7 @@ describe('Prisma bid adapter tests', function () { expect(syncs).to.have.lengthOf(0); }); it('Verifies user sync with cookies in bid response', function () { - DISPLAY_BID_RESPONSE.body.cookies = [{'type': 'image', 'url': 'http://www.cookie.sync.org/'}]; + DISPLAY_BID_RESPONSE.body.cookies = [{ 'type': 'image', 'url': 'http://www.cookie.sync.org/' }]; var syncs = spec.getUserSyncs({}, [DISPLAY_BID_RESPONSE], DEFAULT_OPTIONS.gdprConsent); expect(syncs).to.have.lengthOf(1); expect(syncs[0]).to.have.property('type').and.to.equal('image'); diff --git a/test/spec/modules/programmaticXBidAdapter_spec.js b/test/spec/modules/programmaticXBidAdapter_spec.js index 2c857efc8fb..83f939bcd27 100644 --- a/test/spec/modules/programmaticXBidAdapter_spec.js +++ b/test/spec/modules/programmaticXBidAdapter_spec.js @@ -1,14 +1,14 @@ -import {expect} from 'chai'; +import { expect } from 'chai'; import { spec as adapter, createDomain, storage } from 'modules/programmaticXBidAdapter'; import * as utils from 'src/utils.js'; -import {version} from 'package.json'; -import {useFakeTimers} from 'sinon'; -import {BANNER, VIDEO} from '../../../src/mediaTypes.js' -import {config} from '../../../src/config.js'; +import { version } from 'package.json'; +import { useFakeTimers } from 'sinon'; +import { BANNER, VIDEO } from '../../../src/mediaTypes.js' +import { config } from '../../../src/config.js'; import { hashCode, extractPID, @@ -19,7 +19,7 @@ import { tryParseJSON, getUniqueDealId, } from '../../../libraries/vidazooUtils/bidderUtils.js'; -import {getGlobal} from '../../../src/prebidGlobal.js'; +import { getGlobal } from '../../../src/prebidGlobal.js'; export const TEST_ID_SYSTEMS = ['criteoId', 'id5id', 'netId', 'tdid', 'pubProvidedId', 'intentIqId', 'liveIntentId']; @@ -105,9 +105,9 @@ const ORTB2_DEVICE = { 'version': ['8', '0', '0'] }, 'browsers': [ - {'brand': 'Not_A Brand', 'version': ['99', '0', '0', '0']}, - {'brand': 'Google Chrome', 'version': ['109', '0', '5414', '119']}, - {'brand': 'Chromium', 'version': ['109', '0', '5414', '119']} + { 'brand': 'Not_A Brand', 'version': ['99', '0', '0', '0'] }, + { 'brand': 'Google Chrome', 'version': ['109', '0', '5414', '119'] }, + { 'brand': 'Chromium', 'version': ['109', '0', '5414', '119'] } ], 'mobile': 1, 'model': 'SM-G955U', @@ -124,7 +124,7 @@ const ORTB2_DEVICE = { model: 'iPhone 12 Pro Max', os: 'iOS', osv: '17.4', - ext: {fiftyonedegrees_deviceId: '17595-133085-133468-18092'}, + ext: { fiftyonedegrees_deviceId: '17595-133085-133468-18092' }, }; const BIDDER_REQUEST = { @@ -195,9 +195,8 @@ const VIDEO_SERVER_RESPONSE = { const ORTB2_OBJ = { "device": ORTB2_DEVICE, - "regs": {"coppa": 0, "gpp": "gpp_string", "gpp_sid": [7]}, - "site": {"content": {"language": "en"} - } + "regs": { "coppa": 0, "gpp": "gpp_string", "gpp_sid": [7] }, + "site": { "content": { "language": "en" } } }; const REQUEST = { @@ -210,7 +209,7 @@ const REQUEST = { function getTopWindowQueryParams() { try { - const parsedUrl = utils.parseUrl(window.top.document.URL, {decodeSearchAsString: true}); + const parsedUrl = utils.parseUrl(window.top.document.URL, { decodeSearchAsString: true }); return parsedUrl.search; } catch (e) { return ''; @@ -335,9 +334,9 @@ describe('programmaticXBidAdapter', function () { 'version': ['8', '0', '0'] }, 'browsers': [ - {'brand': 'Not_A Brand', 'version': ['99', '0', '0', '0']}, - {'brand': 'Google Chrome', 'version': ['109', '0', '5414', '119']}, - {'brand': 'Chromium', 'version': ['109', '0', '5414', '119']} + { 'brand': 'Not_A Brand', 'version': ['99', '0', '0', '0'] }, + { 'brand': 'Google Chrome', 'version': ['109', '0', '5414', '119'] }, + { 'brand': 'Chromium', 'version': ['109', '0', '5414', '119'] } ], 'mobile': 1, 'model': 'SM-G955U', @@ -409,9 +408,9 @@ describe('programmaticXBidAdapter', function () { 'version': ['8', '0', '0'] }, 'browsers': [ - {'brand': 'Not_A Brand', 'version': ['99', '0', '0', '0']}, - {'brand': 'Google Chrome', 'version': ['109', '0', '5414', '119']}, - {'brand': 'Chromium', 'version': ['109', '0', '5414', '119']} + { 'brand': 'Not_A Brand', 'version': ['99', '0', '0', '0'] }, + { 'brand': 'Google Chrome', 'version': ['109', '0', '5414', '119'] }, + { 'brand': 'Chromium', 'version': ['109', '0', '5414', '119'] } ], 'mobile': 1, 'model': 'SM-G955U', @@ -457,7 +456,7 @@ describe('programmaticXBidAdapter', function () { }); describe('getUserSyncs', function () { it('should have valid user sync with iframeEnabled', function () { - const result = adapter.getUserSyncs({iframeEnabled: true}, [SERVER_RESPONSE]); + const result = adapter.getUserSyncs({ iframeEnabled: true }, [SERVER_RESPONSE]); expect(result).to.deep.equal([{ type: 'iframe', @@ -466,7 +465,7 @@ describe('programmaticXBidAdapter', function () { }); it('should have valid user sync with cid on response', function () { - const result = adapter.getUserSyncs({iframeEnabled: true}, [SERVER_RESPONSE]); + const result = adapter.getUserSyncs({ iframeEnabled: true }, [SERVER_RESPONSE]); expect(result).to.deep.equal([{ type: 'iframe', url: 'https://sync.programmaticx.ai/api/sync/iframe/?cid=testcid123&gdpr=0&gdpr_consent=&us_privacy=&coppa=0' @@ -474,7 +473,7 @@ describe('programmaticXBidAdapter', function () { }); it('should have valid user sync with pixelEnabled', function () { - const result = adapter.getUserSyncs({pixelEnabled: true}, [SERVER_RESPONSE]); + const result = adapter.getUserSyncs({ pixelEnabled: true }, [SERVER_RESPONSE]); expect(result).to.deep.equal([{ 'url': 'https://sync.programmaticx.ai/api/sync/image/?cid=testcid123&gdpr=0&gdpr_consent=&us_privacy=&coppa=0', @@ -486,7 +485,7 @@ describe('programmaticXBidAdapter', function () { config.setConfig({ coppa: 1 }); - const result = adapter.getUserSyncs({iframeEnabled: true}, [SERVER_RESPONSE]); + const result = adapter.getUserSyncs({ iframeEnabled: true }, [SERVER_RESPONSE]); expect(result).to.deep.equal([{ type: 'iframe', url: 'https://sync.programmaticx.ai/api/sync/iframe/?cid=testcid123&gdpr=0&gdpr_consent=&us_privacy=&coppa=1' @@ -504,7 +503,7 @@ describe('programmaticXBidAdapter', function () { applicableSections: [7] } - const result = adapter.getUserSyncs({pixelEnabled: true}, [SERVER_RESPONSE], gdprConsent, uspConsent, gppConsent); + const result = adapter.getUserSyncs({ pixelEnabled: true }, [SERVER_RESPONSE], gdprConsent, uspConsent, gppConsent); expect(result).to.deep.equal([{ 'url': 'https://sync.programmaticx.ai/api/sync/image/?cid=testcid123&gdpr=1&gdpr_consent=consent_string&us_privacy=usp_string&coppa=1&gpp=gpp_string&gpp_sid=7', @@ -520,12 +519,12 @@ describe('programmaticXBidAdapter', function () { }); it('should return empty array when there is no ad', function () { - const responses = adapter.interpretResponse({price: 1, ad: ''}); + const responses = adapter.interpretResponse({ price: 1, ad: '' }); expect(responses).to.be.empty; }); it('should return empty array when there is no price', function () { - const responses = adapter.interpretResponse({price: null, ad: 'great ad'}); + const responses = adapter.interpretResponse({ price: null, ad: 'great ad' }); expect(responses).to.be.empty; }); @@ -598,9 +597,9 @@ describe('programmaticXBidAdapter', function () { const userId = (function () { switch (idSystemProvider) { case 'lipb': - return {lipbid: id}; + return { lipbid: id }; case 'id5id': - return {uid: id}; + return { uid: id }; default: return id; } @@ -621,7 +620,7 @@ describe('programmaticXBidAdapter', function () { bid.userIdAsEids = [ { "source": "audigent.com", - "uids": [{"id": "fakeidi6j6dlc6e"}] + "uids": [{ "id": "fakeidi6j6dlc6e" }] } ] const requests = adapter.buildRequests([bid], BIDDER_REQUEST); @@ -632,11 +631,11 @@ describe('programmaticXBidAdapter', function () { bid.userIdAsEids = [ { "source": "audigent.com", - "uids": [{"id": "fakeidi6j6dlc6e"}] + "uids": [{ "id": "fakeidi6j6dlc6e" }] }, { "source": "rwdcntrl.net", - "uids": [{"id": "fakeid6f35197d5c", "atype": 1}] + "uids": [{ "id": "fakeid6f35197d5c", "atype": 1 }] } ] const requests = adapter.buildRequests([bid], BIDDER_REQUEST); @@ -651,7 +650,7 @@ describe('programmaticXBidAdapter', function () { eids: [ { "source": "pubcid.org", - "uids": [{"id": "fakeid8888dlc6e"}] + "uids": [{ "id": "fakeid8888dlc6e" }] } ] } @@ -666,11 +665,11 @@ describe('programmaticXBidAdapter', function () { eids: [ { "source": "pubcid.org", - "uids": [{"id": "fakeid8888dlc6e"}] + "uids": [{ "id": "fakeid8888dlc6e" }] }, { "source": "adserver.org", - "uids": [{"id": "fakeid495ff1"}] + "uids": [{ "id": "fakeid495ff1" }] } ] } @@ -683,18 +682,18 @@ describe('programmaticXBidAdapter', function () { describe('alternate param names extractors', function () { it('should return undefined when param not supported', function () { - const cid = extractCID({'c_id': '1'}); - const pid = extractPID({'p_id': '1'}); - const subDomain = extractSubDomain({'sub_domain': 'prebid'}); + const cid = extractCID({ 'c_id': '1' }); + const pid = extractPID({ 'p_id': '1' }); + const subDomain = extractSubDomain({ 'sub_domain': 'prebid' }); expect(cid).to.be.undefined; expect(pid).to.be.undefined; expect(subDomain).to.be.undefined; }); it('should return value when param supported', function () { - const cid = extractCID({'cID': '1'}); - const pid = extractPID({'Pid': '2'}); - const subDomain = extractSubDomain({'subDOMAIN': 'prebid'}); + const cid = extractCID({ 'cID': '1' }); + const pid = extractPID({ 'Pid': '2' }); + const subDomain = extractSubDomain({ 'subDOMAIN': 'prebid' }); expect(cid).to.be.equal('1'); expect(pid).to.be.equal('2'); expect(subDomain).to.be.equal('prebid'); @@ -754,7 +753,7 @@ describe('programmaticXBidAdapter', function () { now }); setStorageItem(storage, 'myKey', 2020); - const {value, created} = getStorageItem(storage, 'myKey'); + const { value, created } = getStorageItem(storage, 'myKey'); expect(created).to.be.equal(now); expect(value).to.be.equal(2020); expect(typeof value).to.be.equal('number'); @@ -770,8 +769,8 @@ describe('programmaticXBidAdapter', function () { }); it('should parse JSON value', function () { - const data = JSON.stringify({event: 'send'}); - const {event} = tryParseJSON(data); + const data = JSON.stringify({ event: 'send' }); + const { event } = tryParseJSON(data); expect(event).to.be.equal('send'); }); diff --git a/test/spec/modules/proxistoreBidAdapter_spec.js b/test/spec/modules/proxistoreBidAdapter_spec.js index 767ef93cf81..c24c80cc41c 100644 --- a/test/spec/modules/proxistoreBidAdapter_spec.js +++ b/test/spec/modules/proxistoreBidAdapter_spec.js @@ -1,167 +1,493 @@ import { expect } from 'chai'; import { spec } from 'modules/proxistoreBidAdapter.js'; -import { newBidder } from 'src/adapters/bidderFactory.js'; -import { config } from '../../../src/config.js'; +import { BANNER } from 'src/mediaTypes.js'; const BIDDER_CODE = 'proxistore'; +const COOKIE_BASE_URL = 'https://abs.proxistore.com/v3/rtb/openrtb'; +const COOKIE_LESS_URL = 'https://abs.cookieless-proxistore.com/v3/rtb/openrtb'; + describe('ProxistoreBidAdapter', function () { const consentString = 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A=='; - const bidderRequest = { + + const baseBidderRequest = { bidderCode: BIDDER_CODE, auctionId: '1025ba77-5463-4877-b0eb-14b205cb9304', bidderRequestId: '10edf38ec1a719', - gdprConsent: { - apiVersion: 2, - gdprApplies: true, - consentString: consentString, - vendorData: { - vendor: { - consents: { - 418: true, - }, + timeout: 1000, + }; + + const gdprConsentWithVendor = { + apiVersion: 2, + gdprApplies: true, + consentString: consentString, + vendorData: { + vendor: { + consents: { + 418: true, }, }, }, }; - const bid = { - sizes: [[300, 600]], + + const gdprConsentWithoutVendor = { + apiVersion: 2, + gdprApplies: true, + consentString: consentString, + vendorData: { + vendor: { + consents: { + 418: false, + }, + }, + }, + }; + + const gdprConsentNoVendorData = { + apiVersion: 2, + gdprApplies: true, + consentString: consentString, + vendorData: null, + }; + + const baseBid = { + bidder: BIDDER_CODE, params: { website: 'example.fr', language: 'fr', }, - ortb2: { - user: { ext: { data: { segments: [], contextual_categories: {} } } }, + mediaTypes: { + banner: { + sizes: [[300, 600], [300, 250]], + }, }, - auctionId: 442133079, - bidId: 464646969, - transactionId: 511916005, + adUnitCode: 'div-gpt-ad-123', + transactionId: '511916005', + bidId: '464646969', + auctionId: '1025ba77-5463-4877-b0eb-14b205cb9304', }; + + describe('spec properties', function () { + it('should have correct bidder code', function () { + expect(spec.code).to.equal(BIDDER_CODE); + }); + + it('should have correct GVLID', function () { + expect(spec.gvlid).to.equal(418); + }); + + it('should support banner media type', function () { + expect(spec.supportedMediaTypes).to.deep.equal([BANNER]); + }); + + it('should have browsingTopics enabled', function () { + expect(spec.browsingTopics).to.be.true; + }); + + it('should have getUserSyncs function', function () { + expect(spec.getUserSyncs).to.be.a('function'); + }); + }); + describe('isBidRequestValid', function () { - it('it should be true if required params are presents and there is no info in the local storage', function () { - expect(spec.isBidRequestValid(bid)).to.equal(true); + it('should return true when website and language params are present', function () { + expect(spec.isBidRequestValid(baseBid)).to.equal(true); + }); + + it('should return false when website param is missing', function () { + const bid = { ...baseBid, params: { language: 'fr' } }; + expect(spec.isBidRequestValid(bid)).to.equal(false); }); - it('it should be false if the value in the localstorage is less than 5minutes of the actual time', function () { - const date = new Date(); - date.setMinutes(date.getMinutes() - 1); - localStorage.setItem(`PX_NoAds_${bid.params.website}`, date); - expect(spec.isBidRequestValid(bid)).to.equal(true); + + it('should return false when language param is missing', function () { + const bid = { ...baseBid, params: { website: 'example.fr' } }; + expect(spec.isBidRequestValid(bid)).to.equal(false); }); - it('it should be true if the value in the localstorage is more than 5minutes of the actual time', function () { - const date = new Date(); - date.setMinutes(date.getMinutes() - 10); - localStorage.setItem(`PX_NoAds_${bid.params.website}`, date); - expect(spec.isBidRequestValid(bid)).to.equal(true); + + it('should return false when params object is empty', function () { + const bid = { ...baseBid, params: {} }; + expect(spec.isBidRequestValid(bid)).to.equal(false); }); }); + describe('buildRequests', function () { - const url = { - cookieBase: 'https://abs.proxistore.com/v3/rtb/prebid/multi', - cookieLess: - 'https://abs.cookieless-proxistore.com/v3/rtb/prebid/multi', - }; - - let request = spec.buildRequests([bid], bidderRequest); - it('should return a valid object', function () { - expect(request).to.be.an('object'); - expect(request.method).to.exist; - expect(request.url).to.exist; - expect(request.data).to.exist; - }); - it('request method should be POST', function () { - expect(request.method).to.equal('POST'); - }); - it('should have the value consentGiven to true bc we have 418 in the vendor list', function () { - const data = JSON.parse(request.data); - expect(data.gdpr.consentString).equal( - bidderRequest.gdprConsent.consentString - ); - expect(data.gdpr.applies).to.be.true; - expect(data.gdpr.consentGiven).to.be.true; - }); - it('should contain a valid url', function () { - // has gdpr consent - expect(request.url).equal(url.cookieBase); - // doens't have gdpr consent - bidderRequest.gdprConsent.vendorData = null; - - request = spec.buildRequests([bid], bidderRequest); - expect(request.url).equal(url.cookieLess); - - // api v2 - bidderRequest.gdprConsent = { + describe('request structure', function () { + const bidderRequest = { ...baseBidderRequest, gdprConsent: gdprConsentWithVendor }; + const request = spec.buildRequests([baseBid], bidderRequest); + + it('should return a valid object', function () { + expect(request).to.be.an('object'); + expect(request.method).to.exist; + expect(request.url).to.exist; + expect(request.data).to.exist; + expect(request.options).to.exist; + }); + + it('should use POST method', function () { + expect(request.method).to.equal('POST'); + }); + + it('should have correct options', function () { + expect(request.options.contentType).to.equal('application/json'); + expect(request.options.customHeaders).to.deep.equal({ version: '2.0.0' }); + }); + }); + + describe('OpenRTB data format', function () { + const bidderRequest = { ...baseBidderRequest, gdprConsent: gdprConsentWithVendor }; + const request = spec.buildRequests([baseBid], bidderRequest); + const data = request.data; + + it('should have valid OpenRTB structure', function () { + expect(data).to.be.an('object'); + expect(data.id).to.be.a('string'); + expect(data.imp).to.be.an('array'); + }); + + it('should have imp array with correct length', function () { + expect(data.imp.length).to.equal(1); + }); + + it('should have imp with banner object', function () { + expect(data.imp[0].banner).to.be.an('object'); + expect(data.imp[0].banner.format).to.be.an('array'); + }); + + it('should include banner formats from bid sizes', function () { + const formats = data.imp[0].banner.format; + expect(formats).to.deep.include({ w: 300, h: 600 }); + expect(formats).to.deep.include({ w: 300, h: 250 }); + }); + + it('should set imp.id to bidId', function () { + expect(data.imp[0].id).to.equal(baseBid.bidId); + }); + + it('should include tmax from bidderRequest timeout', function () { + expect(data.tmax).to.equal(1000); + }); + + it('should include website and language in ext.proxistore', function () { + expect(data.ext).to.be.an('object'); + expect(data.ext.proxistore).to.be.an('object'); + expect(data.ext.proxistore.website).to.equal('example.fr'); + expect(data.ext.proxistore.language).to.equal('fr'); + }); + }); + + describe('endpoint URL selection', function () { + it('should use cookie URL when GDPR consent is given for vendor 418', function () { + const bidderRequest = { ...baseBidderRequest, gdprConsent: gdprConsentWithVendor }; + const request = spec.buildRequests([baseBid], bidderRequest); + expect(request.url).to.equal(COOKIE_BASE_URL); + }); + + it('should use cookieless URL when GDPR applies but consent not given', function () { + const bidderRequest = { ...baseBidderRequest, gdprConsent: gdprConsentWithoutVendor }; + const request = spec.buildRequests([baseBid], bidderRequest); + expect(request.url).to.equal(COOKIE_LESS_URL); + }); + + it('should use cookieless URL when vendorData is null', function () { + const bidderRequest = { ...baseBidderRequest, gdprConsent: gdprConsentNoVendorData }; + const request = spec.buildRequests([baseBid], bidderRequest); + expect(request.url).to.equal(COOKIE_LESS_URL); + }); + + it('should use cookie URL when GDPR does not apply', function () { + const bidderRequest = { + ...baseBidderRequest, + gdprConsent: { + gdprApplies: false, + consentString: consentString, + }, + }; + const request = spec.buildRequests([baseBid], bidderRequest); + expect(request.url).to.equal(COOKIE_BASE_URL); + }); + + it('should use cookie URL when no gdprConsent object', function () { + const bidderRequest = { ...baseBidderRequest }; + const request = spec.buildRequests([baseBid], bidderRequest); + expect(request.url).to.equal(COOKIE_BASE_URL); + }); + }); + + describe('withCredentials option', function () { + it('should set withCredentials to true when consent given', function () { + const bidderRequest = { ...baseBidderRequest, gdprConsent: gdprConsentWithVendor }; + const request = spec.buildRequests([baseBid], bidderRequest); + expect(request.options.withCredentials).to.be.true; + }); + + it('should set withCredentials to false when consent not given', function () { + const bidderRequest = { ...baseBidderRequest, gdprConsent: gdprConsentWithoutVendor }; + const request = spec.buildRequests([baseBid], bidderRequest); + expect(request.options.withCredentials).to.be.false; + }); + + it('should set withCredentials to false when no vendorData', function () { + const bidderRequest = { ...baseBidderRequest, gdprConsent: gdprConsentNoVendorData }; + const request = spec.buildRequests([baseBid], bidderRequest); + expect(request.options.withCredentials).to.be.false; + }); + + it('should set withCredentials to false when no gdprConsent', function () { + const bidderRequest = { ...baseBidderRequest }; + const request = spec.buildRequests([baseBid], bidderRequest); + expect(request.options.withCredentials).to.be.false; + }); + }); + + describe('multiple bids', function () { + it('should create imp for each bid request', function () { + const secondBid = { + ...baseBid, + bidId: '789789789', + adUnitCode: 'div-gpt-ad-456', + }; + const bidderRequest = { ...baseBidderRequest, gdprConsent: gdprConsentWithVendor }; + const request = spec.buildRequests([baseBid, secondBid], bidderRequest); + const data = request.data; + + expect(data.imp.length).to.equal(2); + expect(data.imp[0].id).to.equal(baseBid.bidId); + expect(data.imp[1].id).to.equal(secondBid.bidId); + }); + }); + }); + + describe('interpretResponse', function () { + it('should return empty array for empty response', function () { + const bidderRequest = { ...baseBidderRequest, gdprConsent: gdprConsentWithVendor }; + const request = spec.buildRequests([baseBid], bidderRequest); + const emptyResponse = { body: null }; + + const bids = spec.interpretResponse(emptyResponse, request); + expect(bids).to.be.an('array'); + expect(bids.length).to.equal(0); + }); + + it('should return empty array for response with no seatbid', function () { + const bidderRequest = { ...baseBidderRequest, gdprConsent: gdprConsentWithVendor }; + const request = spec.buildRequests([baseBid], bidderRequest); + const response = { body: { id: '123', seatbid: [] } }; + + const bids = spec.interpretResponse(response, request); + expect(bids).to.be.an('array'); + expect(bids.length).to.equal(0); + }); + + it('should correctly parse OpenRTB bid response', function () { + const bidderRequest = { ...baseBidderRequest, gdprConsent: gdprConsentWithVendor }; + const request = spec.buildRequests([baseBid], bidderRequest); + const requestData = request.data; + + const serverResponse = { + body: { + id: requestData.id, + seatbid: [{ + seat: 'proxistore', + bid: [{ + id: 'bid-id-1', + impid: baseBid.bidId, + price: 6.25, + adm: '
Ad markup
', + w: 300, + h: 600, + crid: '22c3290b-8cd5-4cd6-8e8c-28a2de180ccd', + dealid: '2021-03_deal123', + adomain: ['advertiser.com'], + }], + }], + cur: 'EUR', + }, + }; + + const bids = spec.interpretResponse(serverResponse, request); + + expect(bids).to.be.an('array'); + expect(bids.length).to.equal(1); + + const bid = bids[0]; + expect(bid.requestId).to.equal(baseBid.bidId); + expect(bid.cpm).to.equal(6.25); + expect(bid.width).to.equal(300); + expect(bid.height).to.equal(600); + expect(bid.ad).to.equal('
Ad markup
'); + expect(bid.creativeId).to.equal('22c3290b-8cd5-4cd6-8e8c-28a2de180ccd'); + expect(bid.dealId).to.equal('2021-03_deal123'); + expect(bid.currency).to.equal('EUR'); + expect(bid.netRevenue).to.be.true; + expect(bid.ttl).to.equal(30); + expect(bid.meta.advertiserDomains).to.deep.equal(['advertiser.com']); + }); + + it('should handle multiple bids in response', function () { + const secondBid = { + ...baseBid, + bidId: '789789789', + adUnitCode: 'div-gpt-ad-456', + }; + const bidderRequest = { ...baseBidderRequest, gdprConsent: gdprConsentWithVendor }; + const request = spec.buildRequests([baseBid, secondBid], bidderRequest); + const requestData = request.data; + + const serverResponse = { + body: { + id: requestData.id, + seatbid: [{ + seat: 'proxistore', + bid: [ + { + id: 'bid-id-1', + impid: baseBid.bidId, + price: 6.25, + adm: '
Ad 1
', + w: 300, + h: 600, + crid: 'creative-1', + }, + { + id: 'bid-id-2', + impid: secondBid.bidId, + price: 4.50, + adm: '
Ad 2
', + w: 300, + h: 250, + crid: 'creative-2', + }, + ], + }], + cur: 'EUR', + }, + }; + + const bids = spec.interpretResponse(serverResponse, request); + + expect(bids).to.be.an('array'); + expect(bids.length).to.equal(2); + expect(bids[0].requestId).to.equal(baseBid.bidId); + expect(bids[0].cpm).to.equal(6.25); + expect(bids[1].requestId).to.equal(secondBid.bidId); + expect(bids[1].cpm).to.equal(4.50); + }); + }); + + describe('getUserSyncs', function () { + const SYNC_BASE_URL = 'https://abs.proxistore.com/v3/rtb/sync'; + + it('should return empty array when GDPR applies and consent not given', function () { + const syncOptions = { pixelEnabled: true, iframeEnabled: true }; + const gdprConsent = { gdprApplies: true, - allowAuctionWithoutConsent: true, consentString: consentString, vendorData: { vendor: { - consents: { - 418: true, - }, + consents: { 418: false }, }, }, - apiVersion: 2, }; - // has gdpr consent - request = spec.buildRequests([bid], bidderRequest); - expect(request.url).equal(url.cookieBase); - - bidderRequest.gdprConsent.vendorData.vendor = {}; - request = spec.buildRequests([bid], bidderRequest); - expect(request.url).equal(url.cookieLess); - }); - it('should have a property a length of bids equal to one if there is only one bid', function () { - const data = JSON.parse(request.data); - expect(data.hasOwnProperty('bids')).to.be.true; - expect(data.bids).to.be.an('array'); - expect(data.bids.length).equal(1); - expect(data.bids[0].hasOwnProperty('id')).to.be.true; - expect(data.bids[0].sizes).to.be.an('array'); - }); - it('should correctly set bidfloor on imp when getfloor in scope', function () { - let data = JSON.parse(request.data); - expect(data.bids[0].floor).to.be.null; - - bid.params['bidFloor'] = 1; - let req = spec.buildRequests([bid], bidderRequest); - data = JSON.parse(req.data); - expect(data.bids[0].floor).equal(1); - bid.getFloor = function () { - return { currency: 'USD', floor: 1.0 }; + + const syncs = spec.getUserSyncs(syncOptions, [], gdprConsent); + expect(syncs).to.be.an('array'); + expect(syncs.length).to.equal(0); + }); + + it('should return pixel sync when pixelEnabled and consent given', function () { + const syncOptions = { pixelEnabled: true, iframeEnabled: false }; + const gdprConsent = { + gdprApplies: true, + consentString: consentString, + vendorData: { + vendor: { + consents: { 418: true }, + }, + }, }; - req = spec.buildRequests([bid], bidderRequest); - data = JSON.parse(req.data); - expect(data.bids[0].floor).to.be.null; + + const syncs = spec.getUserSyncs(syncOptions, [], gdprConsent); + expect(syncs).to.be.an('array'); + expect(syncs.length).to.equal(1); + expect(syncs[0].type).to.equal('image'); + expect(syncs[0].url).to.include(`${SYNC_BASE_URL}/image`); + expect(syncs[0].url).to.include('gdpr=1'); + expect(syncs[0].url).to.include(`gdpr_consent=${encodeURIComponent(consentString)}`); }); - }); - describe('interpretResponse', function () { - const emptyResponseParam = { body: [] }; - const fakeResponseParam = { - body: [ - { - ad: '', - cpm: 6.25, - creativeId: '22c3290b-8cd5-4cd6-8e8c-28a2de180ccd', - currency: 'EUR', - dealId: '2021-03_a63ec55e-b9bb-4ca4-b2c9-f456be67e656', - height: 600, - netRevenue: true, - requestId: '3543724f2a033c9', - segments: [], - ttl: 10, - vastUrl: null, - vastXml: null, - width: 300, + + it('should return iframe sync when iframeEnabled and consent given', function () { + const syncOptions = { pixelEnabled: false, iframeEnabled: true }; + const gdprConsent = { + gdprApplies: true, + consentString: consentString, + vendorData: { + vendor: { + consents: { 418: true }, + }, }, - ], - }; - - it('should always return an array', function () { - let response = spec.interpretResponse(emptyResponseParam, bid); - expect(response).to.be.an('array'); - expect(response.length).equal(0); - response = spec.interpretResponse(fakeResponseParam, bid); - expect(response).to.be.an('array'); - expect(response.length).equal(1); + }; + + const syncs = spec.getUserSyncs(syncOptions, [], gdprConsent); + expect(syncs).to.be.an('array'); + expect(syncs.length).to.equal(1); + expect(syncs[0].type).to.equal('iframe'); + expect(syncs[0].url).to.include(`${SYNC_BASE_URL}/iframe`); + }); + + it('should return both syncs when both enabled and consent given', function () { + const syncOptions = { pixelEnabled: true, iframeEnabled: true }; + const gdprConsent = { + gdprApplies: true, + consentString: consentString, + vendorData: { + vendor: { + consents: { 418: true }, + }, + }, + }; + + const syncs = spec.getUserSyncs(syncOptions, [], gdprConsent); + expect(syncs).to.be.an('array'); + expect(syncs.length).to.equal(2); + expect(syncs[0].type).to.equal('image'); + expect(syncs[1].type).to.equal('iframe'); + }); + + it('should return syncs when GDPR does not apply', function () { + const syncOptions = { pixelEnabled: true, iframeEnabled: true }; + const gdprConsent = { + gdprApplies: false, + consentString: consentString, + }; + + const syncs = spec.getUserSyncs(syncOptions, [], gdprConsent); + expect(syncs).to.be.an('array'); + expect(syncs.length).to.equal(2); + expect(syncs[0].url).to.include('gdpr=0'); + }); + + it('should return syncs when no gdprConsent provided', function () { + const syncOptions = { pixelEnabled: true, iframeEnabled: true }; + + const syncs = spec.getUserSyncs(syncOptions, [], undefined); + expect(syncs).to.be.an('array'); + expect(syncs.length).to.equal(2); + }); + + it('should return empty array when no sync options enabled', function () { + const syncOptions = { pixelEnabled: false, iframeEnabled: false }; + const gdprConsent = { + gdprApplies: true, + consentString: consentString, + vendorData: { + vendor: { + consents: { 418: true }, + }, + }, + }; + + const syncs = spec.getUserSyncs(syncOptions, [], gdprConsent); + expect(syncs).to.be.an('array'); + expect(syncs.length).to.equal(0); }); }); }); diff --git a/test/spec/modules/pubCircleBidAdapter_spec.js b/test/spec/modules/pubCircleBidAdapter_spec.js index 97953192a6e..ebf53063c52 100644 --- a/test/spec/modules/pubCircleBidAdapter_spec.js +++ b/test/spec/modules/pubCircleBidAdapter_spec.js @@ -432,7 +432,7 @@ describe('PubCircleBidAdapter', function () { const syncData = spec.getUserSyncs({}, {}, { consentString: 'ALL', gdprApplies: true, - }, {}); + }, undefined); expect(syncData).to.be.an('array').which.is.not.empty; expect(syncData[0]).to.be.an('object') expect(syncData[0].type).to.be.a('string') @@ -441,9 +441,7 @@ describe('PubCircleBidAdapter', function () { expect(syncData[0].url).to.equal('https://cs.pubcircle.ai/image?pbjs=1&gdpr=1&gdpr_consent=ALL&coppa=0') }); it('Should return array of objects with proper sync config , include CCPA', function() { - const syncData = spec.getUserSyncs({}, {}, {}, { - consentString: '1---' - }); + const syncData = spec.getUserSyncs({}, {}, {}, '1---'); expect(syncData).to.be.an('array').which.is.not.empty; expect(syncData[0]).to.be.an('object') expect(syncData[0].type).to.be.a('string') @@ -452,7 +450,7 @@ describe('PubCircleBidAdapter', function () { expect(syncData[0].url).to.equal('https://cs.pubcircle.ai/image?pbjs=1&ccpa_consent=1---&coppa=0') }); it('Should return array of objects with proper sync config , include GPP', function() { - const syncData = spec.getUserSyncs({}, {}, {}, {}, { + const syncData = spec.getUserSyncs({}, {}, {}, undefined, { gppString: 'abc123', applicableSections: [8] }); diff --git a/test/spec/modules/publinkIdSystem_spec.js b/test/spec/modules/publinkIdSystem_spec.js index fda67f24864..c3c990972ab 100644 --- a/test/spec/modules/publinkIdSystem_spec.js +++ b/test/spec/modules/publinkIdSystem_spec.js @@ -1,8 +1,8 @@ -import {publinkIdSubmodule} from 'modules/publinkIdSystem.js'; -import {getCoreStorageManager, getStorageManager} from '../../../src/storageManager.js'; -import {server} from 'test/mocks/xhr.js'; +import { publinkIdSubmodule } from 'modules/publinkIdSystem.js'; +import { getCoreStorageManager, getStorageManager } from '../../../src/storageManager.js'; +import { server } from 'test/mocks/xhr.js'; import sinon from 'sinon'; -import {parseUrl} from '../../../src/utils.js'; +import { parseUrl } from '../../../src/utils.js'; const storage = getCoreStorageManager(); @@ -11,7 +11,7 @@ describe('PublinkIdSystem', () => { describe('decode', () => { it('decode', () => { const result = publinkIdSubmodule.decode(TEST_COOKIE_VALUE); - expect(result).deep.equals({publinkId: TEST_COOKIE_VALUE}); + expect(result).deep.equals({ publinkId: TEST_COOKIE_VALUE }); }); }); @@ -19,8 +19,8 @@ describe('PublinkIdSystem', () => { const PUBLINK_COOKIE = '_publink'; const PUBLINK_SRV_COOKIE = '_publink_srv'; const EXP = Date.now() + 60 * 60 * 24 * 7 * 1000; - const COOKIE_VALUE = {publink: 'publinkCookieValue', exp: EXP}; - const LOCAL_VALUE = {publink: 'publinkLocalStorageValue', exp: EXP}; + const COOKIE_VALUE = { publink: 'publinkCookieValue', exp: EXP }; + const LOCAL_VALUE = { publink: 'publinkLocalStorageValue', exp: EXP }; const COOKIE_EXPIRATION = (new Date(Date.now() + 60 * 60 * 24 * 1000)).toUTCString(); const DELETE_COOKIE = 'Thu, 01 Jan 1970 00:00:01 GMT'; it('publink srv cookie', () => { @@ -64,7 +64,7 @@ describe('PublinkIdSystem', () => { }); describe('getId', () => { - const serverResponse = {publink: 'ec0xHT3yfAOnykP64Qf0ORSi7LjNT1wju04ZSCsoPBekOJdBwK-0Zl_lXKDNnzhauC4iszBc-PvA1Be6IMlh1QocA'}; + const serverResponse = { publink: 'ec0xHT3yfAOnykP64Qf0ORSi7LjNT1wju04ZSCsoPBekOJdBwK-0Zl_lXKDNnzhauC4iszBc-PvA1Be6IMlh1QocA' }; it('no config', () => { const result = publinkIdSubmodule.getId(); expect(result).to.exist; @@ -79,7 +79,7 @@ describe('PublinkIdSystem', () => { }); it('Has cached id', () => { - const config = {storage: {type: 'cookie'}}; + const config = { storage: { type: 'cookie' } }; const submoduleCallback = publinkIdSubmodule.getId(config, undefined, TEST_COOKIE_VALUE).callback; submoduleCallback(callbackSpy); @@ -98,7 +98,7 @@ describe('PublinkIdSystem', () => { }); it('Request path has priority', () => { - const config = {storage: {type: 'cookie'}, params: {e: 'ca11c0ca7', site_id: '102030'}}; + const config = { storage: { type: 'cookie' }, params: { e: 'ca11c0ca7', site_id: '102030' } }; const submoduleCallback = publinkIdSubmodule.getId(config, undefined, TEST_COOKIE_VALUE).callback; submoduleCallback(callbackSpy); @@ -117,8 +117,8 @@ describe('PublinkIdSystem', () => { }); it('Fetch with GDPR consent data', () => { - const config = {storage: {type: 'cookie'}, params: {e: 'ca11c0ca7', site_id: '102030'}}; - const consentData = {gdpr: {gdprApplies: 1, consentString: 'myconsentstring'}}; + const config = { storage: { type: 'cookie' }, params: { e: 'ca11c0ca7', site_id: '102030' } }; + const consentData = { gdpr: { gdprApplies: 1, consentString: 'myconsentstring' } }; const submoduleCallback = publinkIdSubmodule.getId(config, consentData).callback; submoduleCallback(callbackSpy); @@ -140,7 +140,7 @@ describe('PublinkIdSystem', () => { }); it('server doesnt respond', () => { - const config = {storage: {type: 'cookie'}, params: {e: 'ca11c0ca7'}}; + const config = { storage: { type: 'cookie' }, params: { e: 'ca11c0ca7' } }; const submoduleCallback = publinkIdSubmodule.getId(config).callback; submoduleCallback(callbackSpy); @@ -157,8 +157,8 @@ describe('PublinkIdSystem', () => { }); it('reject plain email address', () => { - const config = {storage: {type: 'cookie'}, params: {e: 'tester@test.com'}}; - const consentData = {gdprApplies: 1, consentString: 'myconsentstring'}; + const config = { storage: { type: 'cookie' }, params: { e: 'tester@test.com' } }; + const consentData = { gdprApplies: 1, consentString: 'myconsentstring' }; const submoduleCallback = publinkIdSubmodule.getId(config, consentData).callback; submoduleCallback(callbackSpy); @@ -171,8 +171,8 @@ describe('PublinkIdSystem', () => { const callbackSpy = sinon.spy(); it('Fetch with usprivacy data', () => { - const config = {storage: {type: 'cookie'}, params: {e: 'ca11c0ca7', api_key: 'abcdefg'}}; - const submoduleCallback = publinkIdSubmodule.getId(config, {usp: '1YNN'}).callback; + const config = { storage: { type: 'cookie' }, params: { e: 'ca11c0ca7', api_key: 'abcdefg' } }; + const submoduleCallback = publinkIdSubmodule.getId(config, { usp: '1YNN' }).callback; submoduleCallback(callbackSpy); const request = server.requests[0]; diff --git a/test/spec/modules/publirBidAdapter_spec.js b/test/spec/modules/publirBidAdapter_spec.js index 0265fbb4020..f6d42328ea4 100644 --- a/test/spec/modules/publirBidAdapter_spec.js +++ b/test/spec/modules/publirBidAdapter_spec.js @@ -186,7 +186,7 @@ describe('publirAdapter', function () { }); it('should have us_privacy param if usPrivacy is available in the bidRequest', function () { - const bidderRequestWithUSP = Object.assign({uspConsent: '1YNN'}, bidderRequest); + const bidderRequestWithUSP = Object.assign({ uspConsent: '1YNN' }, bidderRequest); const request = spec.buildRequests(bidRequests, bidderRequestWithUSP); expect(request.data.params).to.be.an('object'); expect(request.data.params).to.have.property('us_privacy', '1YNN'); @@ -199,7 +199,7 @@ describe('publirAdapter', function () { }); it('should not send the gdpr param if gdprApplies is false in the bidRequest', function () { - const bidderRequestWithGDPR = Object.assign({gdprConsent: {gdprApplies: false}}, bidderRequest); + const bidderRequestWithGDPR = Object.assign({ gdprConsent: { gdprApplies: false } }, bidderRequest); const request = spec.buildRequests(bidRequests, bidderRequestWithGDPR); expect(request.data.params).to.be.an('object'); expect(request.data.params).to.not.have.property('gdpr'); @@ -207,7 +207,7 @@ describe('publirAdapter', function () { }); it('should send the gdpr param if gdprApplies is true in the bidRequest', function () { - const bidderRequestWithGDPR = Object.assign({gdprConsent: {gdprApplies: true, consentString: 'test-consent-string'}}, bidderRequest); + const bidderRequestWithGDPR = Object.assign({ gdprConsent: { gdprApplies: true, consentString: 'test-consent-string' } }, bidderRequest); const request = spec.buildRequests(bidRequests, bidderRequestWithGDPR); expect(request.data.params).to.be.an('object'); expect(request.data.params).to.have.property('gdpr', true); @@ -268,15 +268,15 @@ describe('publirAdapter', function () { 'browsers': [ { 'brand': 'Chromium', - 'version': [ '106', '0', '5249', '119' ] + 'version': ['106', '0', '5249', '119'] }, { 'brand': 'Google Chrome', - 'version': [ '106', '0', '5249', '119' ] + 'version': ['106', '0', '5249', '119'] }, { 'brand': 'Not;A=Brand', - 'version': [ '99', '0', '0', '0' ] + 'version': ['99', '0', '0', '0'] } ], 'mobile': 0, @@ -290,20 +290,20 @@ describe('publirAdapter', function () { 'sua': { 'platform': { 'brand': 'macOS', - 'version': [ '12', '4', '0' ] + 'version': ['12', '4', '0'] }, 'browsers': [ { 'brand': 'Chromium', - 'version': [ '106', '0', '5249', '119' ] + 'version': ['106', '0', '5249', '119'] }, { 'brand': 'Google Chrome', - 'version': [ '106', '0', '5249', '119' ] + 'version': ['106', '0', '5249', '119'] }, { 'brand': 'Not;A=Brand', - 'version': [ '99', '0', '0', '0' ] + 'version': ['99', '0', '0', '0'] } ], 'mobile': 0, diff --git a/test/spec/modules/pubmaticBidAdapter_spec.js b/test/spec/modules/pubmaticBidAdapter_spec.js index 2ed65dd7311..2f57022d229 100644 --- a/test/spec/modules/pubmaticBidAdapter_spec.js +++ b/test/spec/modules/pubmaticBidAdapter_spec.js @@ -3,7 +3,7 @@ import { spec, cpmAdjustment, addViewabilityToImp, shouldAddDealTargeting } from import * as utils from 'src/utils.js'; import { bidderSettings } from 'src/bidderSettings.js'; import { config } from 'src/config.js'; -import {getGlobal} from '../../../src/prebidGlobal.js'; +import { getGlobal } from '../../../src/prebidGlobal.js'; describe('PubMatic adapter', () => { let firstBid, videoBid, firstResponse, response, videoResponse, firstAliasBid; @@ -53,7 +53,7 @@ describe('PubMatic adapter', () => { js: 1, connectiontype: 6 }, - site: {domain: 'ebay.com', page: 'https://ebay.com', publisher: {id: '5670'}}, + site: { domain: 'ebay.com', page: 'https://ebay.com', publisher: { id: '5670' } }, source: {}, user: { ext: { @@ -136,7 +136,7 @@ describe('PubMatic adapter', () => { js: 1, connectiontype: 6 }, - site: {domain: 'ebay.com', page: 'https://ebay.com', publisher: {id: '5670'}}, + site: { domain: 'ebay.com', page: 'https://ebay.com', publisher: { id: '5670' } }, source: {}, user: { ext: { @@ -195,7 +195,7 @@ describe('PubMatic adapter', () => { }, 'dealid': 'PUBDEAL1', 'mtype': 2, - 'params': {'outstreamAU': 'outstreamAU', 'renderer': 'renderer_test_pubmatic'} + 'params': { 'outstreamAU': 'outstreamAU', 'renderer': 'renderer_test_pubmatic' } }] }; firstResponse = { @@ -255,7 +255,7 @@ describe('PubMatic adapter', () => { js: 1, connectiontype: 6 }, - site: {domain: 'ebay.com', page: 'https://ebay.com'}, + site: { domain: 'ebay.com', page: 'https://ebay.com' }, source: {}, user: { ext: { @@ -287,7 +287,7 @@ describe('PubMatic adapter', () => { js: 1, connectiontype: 6 }, - site: {domain: 'ebay.com', page: 'https://ebay.com'}, + site: { domain: 'ebay.com', page: 'https://ebay.com' }, source: {}, user: { ext: { @@ -716,6 +716,38 @@ describe('PubMatic adapter', () => { expect(imp).to.be.an('array'); expect(imp[0]).to.have.property('native'); }); + + it('should set privacy to 1 in native request when privacyLink is present', () => { + nativeBidderRequest.bids[0].mediaTypes.native.privacyLink = { required: false }; + const request = spec.buildRequests(validBidRequests, nativeBidderRequest); + const { imp } = request?.data; + expect(imp).to.be.an('array'); + expect(imp[0]).to.have.property('native'); + const nativeRequest = JSON.parse(imp[0].native.request); + expect(nativeRequest).to.have.property('privacy').equal(1); + }); + + it('should not add privacyLink as an asset in the native request', () => { + nativeBidderRequest.bids[0].mediaTypes.native.privacyLink = { required: true }; + const request = spec.buildRequests(validBidRequests, nativeBidderRequest); + const { imp } = request?.data; + expect(imp).to.be.an('array'); + expect(imp[0]).to.have.property('native'); + const nativeRequest = JSON.parse(imp[0].native.request); + const hasPrivacyLinkAsset = nativeRequest.assets.some(asset => asset.privacyLink !== undefined); + expect(hasPrivacyLinkAsset).to.be.false; + }); + + it('should set privacy to 1 and have no assets when privacyLink is the only native key', () => { + nativeBidderRequest.bids[0].mediaTypes.native = { privacyLink: { required: false } }; + const request = spec.buildRequests(validBidRequests, nativeBidderRequest); + const { imp } = request?.data; + expect(imp).to.be.an('array'); + expect(imp[0]).to.have.property('native'); + const nativeRequest = JSON.parse(imp[0].native.request); + expect(nativeRequest).to.have.property('privacy').equal(1); + expect(nativeRequest.assets).to.deep.equal([]); + }); }); } describe('ShouldAddDealTargeting', () => { @@ -1140,7 +1172,7 @@ describe('PubMatic adapter', () => { ] }; beforeEach(() => { - bidderRequest.ortb2.regs = {ext: { dsa }}; + bidderRequest.ortb2.regs = { ext: { dsa } }; }); it('should have DSA in regs.ext', () => { @@ -1856,13 +1888,13 @@ describe('addViewabilityToImp', () => { }); it('should add viewability to imp.ext when measurable', () => { - addViewabilityToImp(imp, 'Div1', { w: 300, h: 250 }); + addViewabilityToImp(imp, { adUnitCode: 'Div1' }, { w: 300, h: 250 }); expect(imp.ext).to.have.property('viewability'); }); it('should set viewability amount to "na" if not measurable (e.g., in iframe)', () => { const isIframeStub = sandbox.stub(utils, 'inIframe').returns(true); - addViewabilityToImp(imp, 'Div1', { w: 300, h: 250 }); + addViewabilityToImp(imp, { adUnitCode: 'Div1' }, { w: 300, h: 250 }); expect(imp.ext).to.have.property('viewability'); expect(imp.ext.viewability.amount).to.equal('na'); }); @@ -1870,13 +1902,13 @@ describe('addViewabilityToImp', () => { it('should not add viewability if element is not found', () => { document.getElementById.restore(); sandbox.stub(document, 'getElementById').returns(null); - addViewabilityToImp(imp, 'Div1', { w: 300, h: 250 }); + addViewabilityToImp(imp, { adUnitCode: 'Div1' }, { w: 300, h: 250 }); expect(imp.ext).to.not.have.property('viewability'); }); it('should create imp.ext if not present', () => { imp = {}; - addViewabilityToImp(imp, 'Div1', { w: 300, h: 250 }); + addViewabilityToImp(imp, { adUnitCode: 'Div1' }, { w: 300, h: 250 }); expect(imp.ext).to.exist; expect(imp.ext).to.have.property('viewability'); }); diff --git a/test/spec/modules/pubmaticIdSystem_spec.js b/test/spec/modules/pubmaticIdSystem_spec.js index 3d6b4ab40ea..d8fb99a8a1c 100644 --- a/test/spec/modules/pubmaticIdSystem_spec.js +++ b/test/spec/modules/pubmaticIdSystem_spec.js @@ -52,7 +52,7 @@ describe('pubmaticIdSystem', () => { const expectedURL = 'https://image6.pubmatic.com/AdServer/UCookieSetPug?oid=5&p=12345&publisherId=12345&gdpr=0&gdpr_consent=&src=pbjs_uid&ver=1&coppa=0&us_privacy=&gpp=&gpp_sid='; expect(request.url).to.equal(expectedURL); - expect(completeCallback.calledOnceWithExactly({id: '6C3F0AB9-AE82-45C2-AD6F-9721E542DC4A'})).to.be.true; + expect(completeCallback.calledOnceWithExactly({ id: '6C3F0AB9-AE82-45C2-AD6F-9721E542DC4A' })).to.be.true; }); it('should log an error if configuration is invalid', () => { diff --git a/test/spec/modules/pubperfAnalyticsAdapter_spec.js b/test/spec/modules/pubperfAnalyticsAdapter_spec.js index 0d75c64f97f..7e273b54c45 100644 --- a/test/spec/modules/pubperfAnalyticsAdapter_spec.js +++ b/test/spec/modules/pubperfAnalyticsAdapter_spec.js @@ -1,7 +1,7 @@ import pubperfAnalytics from 'modules/pubperfAnalyticsAdapter.js'; -import {expect} from 'chai'; -import {server} from 'test/mocks/xhr.js'; -import {expectEvents, fireEvents} from '../../helpers/analytics.js'; +import { expect } from 'chai'; +import { server } from 'test/mocks/xhr.js'; +import { expectEvents, fireEvents } from '../../helpers/analytics.js'; const events = require('src/events'); const utils = require('src/utils.js'); diff --git a/test/spec/modules/pubriseBidAdapter_spec.js b/test/spec/modules/pubriseBidAdapter_spec.js index 786f6a98b5c..37aaa964602 100644 --- a/test/spec/modules/pubriseBidAdapter_spec.js +++ b/test/spec/modules/pubriseBidAdapter_spec.js @@ -482,7 +482,7 @@ describe('PubriseBidAdapter', function () { const syncData = spec.getUserSyncs({}, {}, { consentString: 'ALL', gdprApplies: true, - }, {}); + }, undefined); expect(syncData).to.be.an('array').which.is.not.empty; expect(syncData[0]).to.be.an('object') expect(syncData[0].type).to.be.a('string') @@ -491,9 +491,7 @@ describe('PubriseBidAdapter', function () { expect(syncData[0].url).to.equal('https://sync.pubrise.ai/image?pbjs=1&gdpr=1&gdpr_consent=ALL&coppa=0') }); it('Should return array of objects with proper sync config , include CCPA', function() { - const syncData = spec.getUserSyncs({}, {}, {}, { - consentString: '1---' - }); + const syncData = spec.getUserSyncs({}, {}, {}, '1---'); expect(syncData).to.be.an('array').which.is.not.empty; expect(syncData[0]).to.be.an('object') expect(syncData[0].type).to.be.a('string') @@ -502,7 +500,7 @@ describe('PubriseBidAdapter', function () { expect(syncData[0].url).to.equal('https://sync.pubrise.ai/image?pbjs=1&ccpa_consent=1---&coppa=0') }); it('Should return array of objects with proper sync config , include GPP', function() { - const syncData = spec.getUserSyncs({}, {}, {}, {}, { + const syncData = spec.getUserSyncs({}, {}, {}, undefined, { gppString: 'abc123', applicableSections: [8] }); diff --git a/test/spec/modules/pubstackAnalyticsAdapter_spec.js b/test/spec/modules/pubstackAnalyticsAdapter_spec.js index 6e532698d8b..8ef362192c3 100644 --- a/test/spec/modules/pubstackAnalyticsAdapter_spec.js +++ b/test/spec/modules/pubstackAnalyticsAdapter_spec.js @@ -2,7 +2,7 @@ import * as utils from 'src/utils.js'; import pubstackAnalytics from '../../../modules/pubstackAnalyticsAdapter.js'; import adapterManager from 'src/adapterManager'; import * as events from 'src/events'; -import {expectEvents} from '../../helpers/analytics.js'; +import { expectEvents } from '../../helpers/analytics.js'; describe('Pubstack Analytics Adapter', () => { const scope = utils.getWindowSelf(); diff --git a/test/spec/modules/pubstackBidAdapter_spec.js b/test/spec/modules/pubstackBidAdapter_spec.js new file mode 100644 index 00000000000..1f8143e47fa --- /dev/null +++ b/test/spec/modules/pubstackBidAdapter_spec.js @@ -0,0 +1,348 @@ +import { expect } from 'chai'; +import { spec } from 'modules/pubstackBidAdapter'; +import * as utils from 'src/utils.js'; +import { config } from 'src/config.js'; +import { hook } from 'src/hook.js'; +import 'src/prebid.js'; +import 'modules/consentManagementTcf.js'; +import 'modules/consentManagementUsp.js'; +import 'modules/consentManagementGpp.js'; + +describe('pubstackBidAdapter', function () { + const baseBidRequest = { + adUnitCode: 'adunit-code', + auctionId: 'auction-1', + bidId: 'bid-1', + bidder: 'pubstack', + bidderRequestId: 'request-1', + mediaTypes: { banner: { sizes: [[300, 250]] } }, + params: { + siteId: 'site-123', + adUnitName: 'adunit-1' + }, + sizes: [[300, 250]], + transactionId: 'transaction-1' + }; + + const baseBidderRequest = { + gdprConsent: { + gdprApplies: true, + consentString: 'consent-string', + vendorData: { + purpose: { + consents: { 1: true } + } + } + }, + uspConsent: '1YYN', + gppConsent: { + gppString: 'gpp-string', + applicableSections: [7, 8] + }, + refererInfo: { + referer: 'https://example.com' + } + }; + + const clone = (obj) => JSON.parse(JSON.stringify(obj)); + + const createBidRequest = (overrides = {}) => { + const bidRequest = clone(baseBidRequest); + const { params = {}, ...otherOverrides } = overrides; + Object.assign(bidRequest, otherOverrides); + bidRequest.params = { + ...bidRequest.params, + ...params + }; + return bidRequest; + }; + + const createBidderRequest = (bidRequest, overrides = {}) => ({ + ...clone(baseBidderRequest), + bids: [bidRequest], + ...overrides + }); + + const extractBids = (result) => Array.isArray(result) ? result : result?.bids; + + const findSyncForSite = (syncs, siteId) => + syncs.find((sync) => new URL(sync.url).searchParams.get('siteId') === siteId); + + const getDecodedSyncPayload = (sync) => + JSON.parse(atob(new URL(sync.url).searchParams.get('consent'))); + + before(() => { + hook.ready(); + }); + + beforeEach(function () { + config.resetConfig(); + }); + + afterEach(function () { + config.resetConfig(); + }); + + describe('isBidRequestValid', function () { + it('returns true when required params are present', function () { + expect(spec.isBidRequestValid(createBidRequest())).to.equal(true); + }); + + it('returns false for invalid params when debug is disabled', function () { + config.setConfig({ debug: false }); + expect(spec.isBidRequestValid(createBidRequest({ params: { siteId: undefined } }))).to.equal(false); + expect(spec.isBidRequestValid(createBidRequest({ params: { adUnitName: undefined } }))).to.equal(false); + }); + + it('returns true for invalid params when debug is enabled', function () { + config.setConfig({ debug: true }); + expect(spec.isBidRequestValid(createBidRequest({ params: { siteId: undefined } }))).to.equal(true); + expect(spec.isBidRequestValid(createBidRequest({ params: { adUnitName: undefined } }))).to.equal(true); + }); + }); + + describe('buildRequests', function () { + it('builds a POST request with ORTB data and bidder extensions', function () { + const bidRequest = createBidRequest(); + const bidderRequest = createBidderRequest(bidRequest); + const request = spec.buildRequests([bidRequest], bidderRequest); + + expect(request.method).to.equal('POST'); + expect(request.url).to.equal('https://node.pbstck.com/openrtb2/auction?siteId=site-123'); + expect(utils.deepAccess(request, 'data.site.publisher.id')).to.equal('site-123'); + expect(utils.deepAccess(request, 'data.test')).to.equal(0); + expect(request.data.imp).to.have.lengthOf(1); + expect(utils.deepAccess(request, 'data.imp.0.id')).to.equal('bid-1'); + expect(utils.deepAccess(request, 'data.imp.0.ext.prebid.bidder.pubstack.adUnitName')).to.equal('adunit-1'); + expect(utils.deepAccess(request, 'data.imp.0.ext.prebid.placement.code')).to.equal('adunit-code'); + expect(utils.deepAccess(request, 'data.imp.0.ext.prebid.placement.viewability')).to.be.a('number'); + expect(utils.deepAccess(request, 'data.imp.0.ext.prebid.placement.viewportDistance')).to.be.a('number'); + expect(utils.deepAccess(request, 'data.imp.0.ext.prebid.placement.height')).to.be.a('number'); + expect(utils.deepAccess(request, 'data.ext.prebid.version')).to.be.a('string'); + expect(utils.deepAccess(request, 'data.ext.prebid.request.count')).to.be.a('number'); + expect(utils.deepAccess(request, 'data.ext.prebid.request.timeoutCount')).to.be.a('number'); + expect(utils.deepAccess(request, 'data.ext.prebid.page.tabActive')).to.be.a('boolean'); + expect(utils.deepAccess(request, 'data.ext.prebid.page.height')).to.be.a('number'); + expect(utils.deepAccess(request, 'data.ext.prebid.page.viewportHeight')).to.be.a('number'); + expect(utils.deepAccess(request, 'data.ext.prebid.page.timeFromNavigation')).to.be.a('number'); + }); + + it('sets test to 1 when prebid debug mode is enabled', function () { + config.setConfig({ debug: true }); + const bidRequest = createBidRequest({ bidId: 'bid-debug' }); + const bidderRequest = createBidderRequest(bidRequest); + const request = spec.buildRequests([bidRequest], bidderRequest); + + expect(utils.deepAccess(request, 'data.test')).to.equal(1); + }); + + it('increments request counter for each call', function () { + const firstBidRequest = createBidRequest({ bidId: 'bid-counter-1' }); + const firstRequest = spec.buildRequests([firstBidRequest], createBidderRequest(firstBidRequest)); + const secondBidRequest = createBidRequest({ + bidId: 'bid-counter-2', + adUnitCode: 'adunit-code-2', + params: { adUnitName: 'adunit-2' } + }); + const secondRequest = spec.buildRequests([secondBidRequest], createBidderRequest(secondBidRequest)); + + expect(utils.deepAccess(secondRequest, 'data.ext.prebid.request.count')) + .to.equal(utils.deepAccess(firstRequest, 'data.ext.prebid.request.count') + 1); + }); + + it('updates timeout count after onTimeout callback', function () { + const bidRequest = createBidRequest({ bidId: 'bid-timeout-rate-1' }); + const firstRequest = spec.buildRequests([bidRequest], createBidderRequest(bidRequest)); + expect(utils.deepAccess(firstRequest, 'data.ext.prebid.request.timeoutCount')).to.equal(0); + + spec.onTimeout([]); + + const secondBidRequest = createBidRequest({ bidId: 'bid-timeout-rate-2' }); + const secondRequest = spec.buildRequests([secondBidRequest], createBidderRequest(secondBidRequest)); + expect(utils.deepAccess(secondRequest, 'data.ext.prebid.request.timeoutCount')).to.equal(1); + }); + }); + + describe('interpretResponse', function () { + it('returns empty array when response has no body', function () { + const bidRequest = createBidRequest(); + const request = spec.buildRequests([bidRequest], createBidderRequest(bidRequest)); + const bids = spec.interpretResponse({ body: null }, request); + expect(bids).to.be.an('array'); + expect(bids).to.have.lengthOf(0); + }); + + it('maps ORTB bid responses into prebid bids', function () { + const bidRequest = createBidRequest(); + const request = spec.buildRequests([bidRequest], createBidderRequest(bidRequest)); + const serverResponse = { + body: { + id: 'resp-1', + cur: 'USD', + seatbid: [ + { + bid: [ + { + impid: 'bid-1', + mtype: 1, + price: 1.23, + w: 300, + h: 250, + adm: '
ad
', + crid: 'creative-1' + } + ] + } + ] + } + }; + + const result = spec.interpretResponse(serverResponse, request); + const bids = extractBids(result); + expect(bids).to.have.lengthOf(1); + expect(bids[0]).to.include({ + requestId: 'bid-1', + cpm: 1.23, + width: 300, + height: 250, + ad: '
ad
', + creativeId: 'creative-1' + }); + expect(bids[0]).to.have.property('currency', 'USD'); + }); + + it('returns no bids when ORTB response impid does not match request imp ids', function () { + const bidRequest = createBidRequest({ bidId: 'bid-match-required' }); + const request = spec.buildRequests([bidRequest], createBidderRequest(bidRequest)); + const serverResponse = { + body: { + seatbid: [{ + bid: [{ + impid: 'unknown-imp-id', + price: 2.5, + w: 300, + h: 250, + adm: '
ad
', + crid: 'creative-unknown' + }] + }] + } + }; + + expect(extractBids(spec.interpretResponse(serverResponse, request))).to.deep.equal([]); + }); + }); + + describe('getUserSyncs', function () { + it('returns iframe sync with encoded consent payload and site id', function () { + const bidRequest = createBidRequest(); + const bidderRequest = createBidderRequest(bidRequest); + spec.buildRequests([bidRequest], bidderRequest); + + const syncs = spec.getUserSyncs( + { iframeEnabled: true, pixelEnabled: true }, + [], + bidderRequest.gdprConsent, + bidderRequest.uspConsent, + bidderRequest.gppConsent + ); + + const siteSync = findSyncForSite(syncs, 'site-123'); + expect(siteSync).to.not.equal(undefined); + expect(siteSync.type).to.equal('iframe'); + expect(siteSync.url).to.include('https://cdn.pbstck.com/async_usersync.html'); + + const consentPayload = getDecodedSyncPayload(siteSync); + expect(consentPayload).to.deep.equal({ + gdprConsentString: 'consent-string', + gdprApplies: true, + uspConsent: '1YYN', + gpp: 'gpp-string', + gpp_sid: [7, 8] + }); + }); + + it('returns image sync when iframe sync is disabled', function () { + const bidRequest = createBidRequest({ bidId: 'bid-pixel' }); + const bidderRequest = createBidderRequest(bidRequest); + spec.buildRequests([bidRequest], bidderRequest); + + const syncs = spec.getUserSyncs( + { iframeEnabled: false, pixelEnabled: true }, + [], + bidderRequest.gdprConsent, + bidderRequest.uspConsent, + bidderRequest.gppConsent + ); + + const siteSync = findSyncForSite(syncs, 'site-123'); + expect(siteSync).to.not.equal(undefined); + expect(siteSync.type).to.equal('image'); + expect(siteSync.url).to.include('https://cdn.pbstck.com/async_usersync.png'); + }); + + it('returns no syncs when both iframe and pixel sync are disabled', function () { + const bidRequest = createBidRequest({ bidId: 'bid-disabled-syncs' }); + const bidderRequest = createBidderRequest(bidRequest); + spec.buildRequests([bidRequest], bidderRequest); + + const syncs = spec.getUserSyncs( + { iframeEnabled: false, pixelEnabled: false }, + [], + bidderRequest.gdprConsent, + bidderRequest.uspConsent, + bidderRequest.gppConsent + ); + + expect(syncs).to.deep.equal([]); + }); + + it('includes sync entries for each seen site id', function () { + const bidA = createBidRequest({ + bidId: 'bid-site-a', + adUnitCode: 'ad-site-a', + params: { siteId: 'site-a', adUnitName: 'adunit-a' } + }); + const bidB = createBidRequest({ + bidId: 'bid-site-b', + adUnitCode: 'ad-site-b', + params: { siteId: 'site-b', adUnitName: 'adunit-b' } + }); + + spec.buildRequests([bidA], createBidderRequest(bidA)); + spec.buildRequests([bidB], createBidderRequest(bidB)); + + const syncs = spec.getUserSyncs( + { iframeEnabled: true, pixelEnabled: false }, + [], + baseBidderRequest.gdprConsent, + baseBidderRequest.uspConsent, + baseBidderRequest.gppConsent + ); + const siteIds = syncs.map((sync) => new URL(sync.url).searchParams.get('siteId')); + + expect(siteIds).to.include('site-a'); + expect(siteIds).to.include('site-b'); + }); + + it('supports null consent objects in the sync payload', function () { + const bidRequest = createBidRequest({ + bidId: 'bid-null-consent', + params: { siteId: 'site-null-consent', adUnitName: 'adunit-null-consent' } + }); + spec.buildRequests([bidRequest], createBidderRequest(bidRequest)); + + const syncs = spec.getUserSyncs( + { iframeEnabled: true, pixelEnabled: false }, + [], + null, + null, + null + ); + + const siteSync = findSyncForSite(syncs, 'site-null-consent'); + expect(siteSync).to.not.equal(undefined); + expect(getDecodedSyncPayload(siteSync)).to.deep.equal({ uspConsent: null }); + }); + }); +}); diff --git a/test/spec/modules/pubwiseAnalyticsAdapter_spec.js b/test/spec/modules/pubwiseAnalyticsAdapter_spec.js index 35284fbdd87..730624e08bd 100644 --- a/test/spec/modules/pubwiseAnalyticsAdapter_spec.js +++ b/test/spec/modules/pubwiseAnalyticsAdapter_spec.js @@ -1,7 +1,7 @@ -import {expect} from 'chai'; +import { expect } from 'chai'; import pubwiseAnalytics from 'modules/pubwiseAnalyticsAdapter.js'; -import {expectEvents} from '../../helpers/analytics.js'; -import {server} from '../../mocks/xhr.js'; +import { expectEvents } from '../../helpers/analytics.js'; +import { server } from '../../mocks/xhr.js'; import { EVENTS } from 'src/constants.js'; const events = require('src/events'); @@ -17,17 +17,17 @@ describe('PubWise Prebid Analytics', function () { provider: 'pubwiseanalytics', options: { site: ['b1ccf317-a6fc-428d-ba69-0c9c208aa61c'], - custom: {'c_script_type': 'test-script-type', 'c_host': 'test-host', 'c_slot1': 'test-slot1', 'c_slot2': 'test-slot2', 'c_slot3': 'test-slot3', 'c_slot4': 'test-slot4'} + custom: { 'c_script_type': 'test-script-type', 'c_host': 'test-host', 'c_slot1': 'test-slot1', 'c_slot2': 'test-slot2', 'c_slot3': 'test-slot3', 'c_slot4': 'test-slot4' } } }; - mock.AUCTION_INIT = {auctionId: '53c35d77-bd62-41e7-b920-244140e30c77'}; + mock.AUCTION_INIT = { auctionId: '53c35d77-bd62-41e7-b920-244140e30c77' }; mock.AUCTION_INIT_EXTRAS = { auctionId: '53c35d77-bd62-41e7-b920-244140e30c77', adUnitCodes: 'not empty', adUnits: '', bidderRequests: ['0'], bidsReceived: '0', - config: {test: 'config'}, + config: { test: 'config' }, noBids: 'no bids today', winningBids: 'winning bids', extraProp: 'extraProp retained' @@ -35,7 +35,7 @@ describe('PubWise Prebid Analytics', function () { beforeEach(function() { sandbox = sinon.createSandbox(); - clock = sandbox.useFakeTimers({shouldClearNativeTimers: true}); + clock = sandbox.useFakeTimers({ shouldClearNativeTimers: true }); sandbox.stub(events, 'getEvents').returns([]); requests = server.requests; diff --git a/test/spec/modules/pubxBidAdapter_spec.js b/test/spec/modules/pubxBidAdapter_spec.js index f0148bb1d06..0561987b348 100644 --- a/test/spec/modules/pubxBidAdapter_spec.js +++ b/test/spec/modules/pubxBidAdapter_spec.js @@ -1,6 +1,6 @@ -import {expect} from 'chai'; -import {spec} from 'modules/pubxBidAdapter.js'; -import {newBidder} from 'src/adapters/bidderFactory.js'; +import { expect } from 'chai'; +import { spec } from 'modules/pubxBidAdapter.js'; +import { newBidder } from 'src/adapters/bidderFactory.js'; import * as utils from 'src/utils.js'; describe('pubxAdapter', function () { diff --git a/test/spec/modules/pulsepointBidAdapter_spec.js b/test/spec/modules/pulsepointBidAdapter_spec.js index a13b285b69b..c2724bfedbc 100644 --- a/test/spec/modules/pulsepointBidAdapter_spec.js +++ b/test/spec/modules/pulsepointBidAdapter_spec.js @@ -1,8 +1,8 @@ /* eslint dot-notation:0, quote-props:0 */ -import {expect} from 'chai'; -import {spec} from 'modules/pulsepointBidAdapter.js'; -import {addFPDToBidderRequest} from '../../helpers/fpd.js'; -import {deepClone} from '../../../src/utils.js'; +import { expect } from 'chai'; +import { spec } from 'modules/pulsepointBidAdapter.js'; +import { addFPDToBidderRequest } from '../../helpers/fpd.js'; +import { deepClone } from '../../../src/utils.js'; import 'modules/consentManagementTcf'; import 'modules/consentManagementUsp'; import 'modules/userId/index'; @@ -134,20 +134,26 @@ describe('PulsePoint Adapter Tests', function () { bidfloor: 1.5, badv: ['cocacola.com', 'lays.com'] }, - ortb2: {source: {ext: {schain: { - 'ver': '1.0', - 'complete': 1, - 'nodes': [ - { - 'asi': 'exchange1.com', - 'sid': '1234', - 'hp': 1, - 'rid': 'bid-request-1', - 'name': 'publisher', - 'domain': 'publisher.com' + ortb2: { + source: { + ext: { + schain: { + 'ver': '1.0', + 'complete': 1, + 'nodes': [ + { + 'asi': 'exchange1.com', + 'sid': '1234', + 'hp': 1, + 'rid': 'bid-request-1', + 'name': 'publisher', + 'domain': 'publisher.com' + } + ] + } } - ] - }}}} + } + } }]; const bidderRequest = { @@ -174,11 +180,11 @@ describe('PulsePoint Adapter Tests', function () { // slot 1 expect(ortbRequest.imp[0].tagid).to.equal('t10000'); expect(ortbRequest.imp[0].banner).to.not.equal(null); - expect(ortbRequest.imp[0].banner.format).to.deep.eq([{'w': 728, 'h': 90}, {'w': 160, 'h': 600}]); + expect(ortbRequest.imp[0].banner.format).to.deep.eq([{ 'w': 728, 'h': 90 }, { 'w': 160, 'h': 600 }]); // slot 2 expect(ortbRequest.imp[1].tagid).to.equal('t20000'); expect(ortbRequest.imp[1].banner).to.not.equal(null); - expect(ortbRequest.imp[1].banner.format).to.deep.eq([{'w': 728, 'h': 90}]); + expect(ortbRequest.imp[1].banner.format).to.deep.eq([{ 'w': 728, 'h': 90 }]); }); it('Verify parse response', async function () { @@ -199,7 +205,7 @@ describe('PulsePoint Adapter Tests', function () { }] }] }; - const bids = spec.interpretResponse({body: ortbResponse}, request); + const bids = spec.interpretResponse({ body: ortbResponse }, request); expect(bids).to.have.lengthOf(1); // verify first bid const bid = bids[0]; @@ -218,7 +224,7 @@ describe('PulsePoint Adapter Tests', function () { it('Verify full passback', function () { const request = spec.buildRequests(slotConfigs, bidderRequest); - const bids = spec.interpretResponse({body: null}, request) + const bids = spec.interpretResponse({ body: null }, request) expect(bids).to.have.lengthOf(0); }); @@ -266,11 +272,11 @@ describe('PulsePoint Adapter Tests', function () { const ortbRequest = request.data; const nativeResponse = { assets: [ - {id: 1, img: {type: 3, url: 'https://images.cdn.brand.com/123'}}, - {id: 2, title: {text: 'Ad Title'}}, - {id: 3, data: {type: 1, value: 'Sponsored By: Brand'}} + { id: 1, img: { type: 3, url: 'https://images.cdn.brand.com/123' } }, + { id: 2, title: { text: 'Ad Title' } }, + { id: 3, data: { type: 1, value: 'Sponsored By: Brand' } } ], - link: {url: 'https://brand.clickme.com/'}, + link: { url: 'https://brand.clickme.com/' }, imptrackers: ['https://imp1.trackme.com/', 'https://imp1.contextweb.com/'] }; @@ -284,7 +290,7 @@ describe('PulsePoint Adapter Tests', function () { }] }] }; - const bids = spec.interpretResponse({body: ortbResponse}, request); + const bids = spec.interpretResponse({ body: ortbResponse }, request); // verify bid const bid = bids[0]; expect(bid.cpm).to.equal(1.25); diff --git a/test/spec/modules/pwbidBidAdapter_spec.js b/test/spec/modules/pwbidBidAdapter_spec.js index 25dd79a224e..60d70e3ae1f 100644 --- a/test/spec/modules/pwbidBidAdapter_spec.js +++ b/test/spec/modules/pwbidBidAdapter_spec.js @@ -1,7 +1,7 @@ // import or require modules necessary for the test, e.g.: -import {expect} from 'chai'; -import {spec, _checkVideoPlacement, _checkMediaType, _parseAdSlot} from 'modules/pwbidBidAdapter.js'; // _ functions exported only for testing so maintaining the JS convention of _ to indicate the intent +import { expect } from 'chai'; +import { spec, _checkVideoPlacement, _checkMediaType, _parseAdSlot } from 'modules/pwbidBidAdapter.js'; // _ functions exported only for testing so maintaining the JS convention of _ to indicate the intent import * as utils from 'src/utils.js'; const sampleRequestBanner = { @@ -495,7 +495,7 @@ describe('PubWiseAdapter', function () { // endpointBidRequest.forEach((bidRequest) => { // bidRequest.params.endpoint_url = newEndpoint; // }); - const result = spec.buildRequests(endpointBidRequest, {auctionId: 'placeholder'}); + const result = spec.buildRequests(endpointBidRequest, { auctionId: 'placeholder' }); expect(result.url).to.equal(referenceEndpoint); }); @@ -505,7 +505,7 @@ describe('PubWiseAdapter', function () { endpointBidRequest.forEach((bidRequest) => { bidRequest.params.endpoint_url = newEndpoint; }); - const result = spec.buildRequests(endpointBidRequest, {auctionId: 'placeholder'}); + const result = spec.buildRequests(endpointBidRequest, { auctionId: 'placeholder' }); expect(result.url).to.equal(newEndpoint); }); }); @@ -560,7 +560,7 @@ describe('PubWiseAdapter', function () { describe('Handling Request Construction', function () { it('bid requests are not mutable', function() { const sourceBidRequest = utils.deepClone(sampleValidBidRequests); - spec.buildRequests(sampleValidBidRequests, {auctionId: 'placeholder'}); + spec.buildRequests(sampleValidBidRequests, { auctionId: 'placeholder' }); expect(sampleValidBidRequests).to.deep.equal(sourceBidRequest, 'Should be unedited as they are used elsewhere'); }); it('should handle complex bidRequest', function() { @@ -578,15 +578,15 @@ describe('PubWiseAdapter', function () { describe('Identifies Media Types', function () { it('identifies native adm type', function() { const adm = '{"ver":"1.2","assets":[{"title":{"text":"PubWise Test"}},{"img":{"type":3,"url":"http://www.pubwise.io"}},{"img":{"type":1,"url":"http://www.pubwise.io"}},{"data":{"type":2,"value":"PubWise Test Desc"}},{"data":{"type":1,"value":"PubWise.io"}}],"link":{"url":""}}'; - const newBid = {mediaType: 'unknown'}; - _checkMediaType({adm}, newBid); + const newBid = { mediaType: 'unknown' }; + _checkMediaType({ adm }, newBid); expect(newBid.mediaType).to.equal('native', adm + ' Is a Native adm'); }); it('identifies banner adm type', function() { let adm = '

PubWise Test Bid

'; - let newBid = {mediaType: 'unknown'}; - _checkMediaType({adm}, newBid); + let newBid = { mediaType: 'unknown' }; + _checkMediaType({ adm }, newBid); expect(newBid.mediaType).to.equal('banner', adm + ' Is a Banner adm'); }); }); @@ -602,7 +602,7 @@ describe('PubWiseAdapter', function () { describe('Properly Handles Response', function () { it('handles response with muiltiple responses', function() { // the request when it comes back is on the data object - const pbResponse = spec.interpretResponse(sampleRTBResponse, {'data': sampleRequest}) + const pbResponse = spec.interpretResponse(sampleRTBResponse, { 'data': sampleRequest }) expect(pbResponse).to.deep.equal(samplePBBidObjects); }); }); diff --git a/test/spec/modules/pxyzBidAdapter_spec.js b/test/spec/modules/pxyzBidAdapter_spec.js index 2ce6ed0140b..ed16e244168 100644 --- a/test/spec/modules/pxyzBidAdapter_spec.js +++ b/test/spec/modules/pxyzBidAdapter_spec.js @@ -70,7 +70,7 @@ describe('pxyzBidAdapter', function () { 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(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'); }); @@ -145,7 +145,7 @@ describe('pxyzBidAdapter', function () { describe('interpretResponse', function () { const response = { 'id': 'bidd_id', - 'seatbid': [ { + 'seatbid': [{ 'bid': [ { 'id': '4434762738980910431', @@ -153,7 +153,7 @@ describe('pxyzBidAdapter', function () { 'price': 1, 'adid': '91673066', 'adm': '', - 'adomain': [ 'pg.xyz' ], + 'adomain': ['pg.xyz'], 'iurl': 'http://pgxyz.com/cr?id=91673066', 'cid': 'c_id', 'crid': 'c_rid', @@ -197,14 +197,14 @@ describe('pxyzBidAdapter', function () { } } ]; - const result = spec.interpretResponse({ body: response }, {bidderRequest}); + const result = spec.interpretResponse({ body: response }, { bidderRequest }); expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); expect(result[0].meta.advertiserDomains).to.deep.equal(expectedResponse[0].meta.advertiserDomains); }); it('handles nobid response', function () { const response = undefined; - const result = spec.interpretResponse({ body: response }, {bidderRequest}); + const result = spec.interpretResponse({ body: response }, { bidderRequest }); expect(result.length).to.equal(0); }); }); diff --git a/test/spec/modules/qortexRtdProvider_spec.js b/test/spec/modules/qortexRtdProvider_spec.js index 3f6379af822..56a857e0876 100644 --- a/test/spec/modules/qortexRtdProvider_spec.js +++ b/test/spec/modules/qortexRtdProvider_spec.js @@ -12,7 +12,7 @@ import { requestContextData, windowPostMessageReceived } from '../../../modules/qortexRtdProvider.js'; -import {server} from '../../mocks/xhr.js'; +import { server } from '../../mocks/xhr.js'; import { cloneDeep } from 'lodash'; describe('qortexRtdProvider', () => { @@ -71,12 +71,12 @@ describe('qortexRtdProvider', () => { const QortexPostMessageInitialized = { target: 'QORTEX-PREBIDJS-RTD-MODULE', message: 'CX-BID-ENRICH-INITIALIZED', - params: {groupConfig: {data: true}} + params: { groupConfig: { data: true } } } const QortexPostMessageContext = { target: 'QORTEX-PREBIDJS-RTD-MODULE', message: 'DISPATCH-CONTEXT', - params: {context: {data: true}} + params: { context: { data: true } } } const invalidTypeQortexEvent = { detail: { @@ -136,7 +136,7 @@ describe('qortexRtdProvider', () => { } beforeEach(() => { - ortb2Stub = sinon.stub(reqBidsConfig, 'ortb2Fragments').value({bidder: {}, global: {}}) + ortb2Stub = sinon.stub(reqBidsConfig, 'ortb2Fragments').value({ bidder: {}, global: {} }) logWarnSpy = sinon.spy(utils, 'logWarn'); logMessageSpy = sinon.spy(utils, 'logMessage'); }) @@ -291,14 +291,14 @@ describe('qortexRtdProvider', () => { }) it('Properly sends analytics event with valid config', () => { - const testData = {auctionId: reqBidsConfig.auctionId, data: 'data'}; + const testData = { auctionId: reqBidsConfig.auctionId, data: 'data' }; module.onAuctionEndEvent(testData); }) }) describe('requestContextData', () => { before(() => { - setContextData({data: true}); + setContextData({ data: true }); }) after(() => { @@ -400,11 +400,11 @@ describe('qortexRtdProvider', () => { }) it('processes incoming qortex component "initialize" message', () => { - windowPostMessageReceived({data: QortexPostMessageInitialized}) + windowPostMessageReceived({ data: QortexPostMessageInitialized }) }) it('processes incoming qortex component "context" message', () => { - windowPostMessageReceived({data: QortexPostMessageContext}) + windowPostMessageReceived({ data: QortexPostMessageContext }) }) }) }) diff --git a/test/spec/modules/qtBidAdapter_spec.js b/test/spec/modules/qtBidAdapter_spec.js index 279962d0d3c..b2b7511cb18 100644 --- a/test/spec/modules/qtBidAdapter_spec.js +++ b/test/spec/modules/qtBidAdapter_spec.js @@ -481,7 +481,7 @@ describe('QTBidAdapter', function () { const syncData = spec.getUserSyncs({}, {}, { consentString: 'ALL', gdprApplies: true, - }, {}); + }, undefined); expect(syncData).to.be.an('array').which.is.not.empty; expect(syncData[0]).to.be.an('object') expect(syncData[0].type).to.be.a('string') @@ -490,9 +490,7 @@ describe('QTBidAdapter', function () { expect(syncData[0].url).to.equal('https://cs.qt.io/image?pbjs=1&gdpr=1&gdpr_consent=ALL&coppa=0') }); it('Should return array of objects with proper sync config , include CCPA', function() { - const syncData = spec.getUserSyncs({}, {}, {}, { - consentString: '1---' - }); + const syncData = spec.getUserSyncs({}, {}, {}, '1---'); expect(syncData).to.be.an('array').which.is.not.empty; expect(syncData[0]).to.be.an('object') expect(syncData[0].type).to.be.a('string') @@ -501,7 +499,7 @@ describe('QTBidAdapter', function () { expect(syncData[0].url).to.equal('https://cs.qt.io/image?pbjs=1&ccpa_consent=1---&coppa=0') }); it('Should return array of objects with proper sync config , include GPP', function() { - const syncData = spec.getUserSyncs({}, {}, {}, {}, { + const syncData = spec.getUserSyncs({}, {}, {}, undefined, { gppString: 'abc123', applicableSections: [8] }); diff --git a/test/spec/modules/quantcastBidAdapter_spec.js b/test/spec/modules/quantcastBidAdapter_spec.js deleted file mode 100644 index dbf6b2c9ef4..00000000000 --- a/test/spec/modules/quantcastBidAdapter_spec.js +++ /dev/null @@ -1,859 +0,0 @@ -import { expect } from 'chai'; -import { - QUANTCAST_DOMAIN, - QUANTCAST_TEST_DOMAIN, - QUANTCAST_NET_REVENUE, - QUANTCAST_TTL, - QUANTCAST_TEST_PUBLISHER, - QUANTCAST_PROTOCOL, - QUANTCAST_PORT, - spec as qcSpec, - storage -} from '../../../modules/quantcastBidAdapter.js'; -import { newBidder } from '../../../src/adapters/bidderFactory.js'; -import { parseUrl } from 'src/utils.js'; -import { config } from 'src/config.js'; -import {getGlobal} from '../../../src/prebidGlobal.js'; - -describe('Quantcast adapter', function () { - const quantcastAdapter = newBidder(qcSpec); - let bidRequest; - let bidderRequest; - - afterEach(function () { - getGlobal().bidderSettings = {}; - }); - beforeEach(function () { - getGlobal().bidderSettings = { - quantcast: { - storageAllowed: true - } - }; - bidRequest = { - bidder: 'quantcast', - bidId: '2f7b179d443f14', - auctionId: '595ffa73-d78a-46c9-b18e-f99548a5be6b', - bidderRequestId: '1cc026909c24c8', - placementCode: 'div-gpt-ad-1438287399331-0', - params: { - 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 - }, - mediaTypes: { - banner: { - sizes: [[300, 250]] - } - } - }; - - bidderRequest = { - refererInfo: { - page: 'http://example.com/hello.html', - ref: 'http://example.com/hello.html', - domain: 'example.com' - } - }; - - storage.setCookie('__qca', '', 'Thu, 01 Jan 1970 00:00:00 GMT'); - }); - - function setupVideoBidRequest(videoParams, mediaTypesParams) { - bidRequest.params = { - publisherId: 'test-publisher', // REQUIRED - Publisher ID provided by Quantcast - // Video object as specified in OpenRTB 2.5 - video: videoParams - }; - if (mediaTypesParams) { - bidRequest['mediaTypes'] = { - video: mediaTypesParams - } - } - }; - - describe('inherited functions', function () { - it('exists and is a function', function () { - expect(quantcastAdapter.callBids).to.exist.and.to.be.a('function'); - }); - }); - - 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(true); - }); - - it('should return `false` when bid has no publisherId', function () { - const bidRequest = { - bidder: 'quantcast', - params: { - } - }; - - expect(qcSpec.isBidRequestValid(bidRequest)).to.equal(false); - }); - }); - - describe('`buildRequests`', function () { - it('sends secure bid requests', function () { - const requests = qcSpec.buildRequests([bidRequest]); - const url = parseUrl(requests[0]['url']); - expect(url.protocol).to.equal('https'); - }); - - it('sends bid requests to Quantcast Canary Endpoint if `publisherId` is `test-publisher`', function () { - const requests = qcSpec.buildRequests([bidRequest]); - const url = parseUrl(requests[0]['url']); - expect(url.hostname).to.equal(QUANTCAST_TEST_DOMAIN); - }); - - 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', function () { - const requests = qcSpec.buildRequests([bidRequest]); - - expect(requests[0].method).to.equal('POST'); - }); - - const expectedBannerBidRequest = { - publisherId: QUANTCAST_TEST_PUBLISHER, - requestId: '2f7b179d443f14', - imp: [ - { - banner: { - battr: [1, 2], - sizes: [{ width: 300, height: 250 }] - }, - 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, - uspSignal: 0, - coppa: 0, - prebidJsVersion: '$prebid.version$', - fpa: '' - }; - - it('sends banner bid requests contains all the required parameters', function () { - const requests = qcSpec.buildRequests([bidRequest], bidderRequest); - - expect(requests[0].data).to.equal(JSON.stringify(expectedBannerBidRequest)); - }); - - it('supports deprecated banner format', function () { - bidRequest.sizes = bidRequest.mediaTypes.banner.sizes; - delete bidRequest.mediaTypes; - const requests = qcSpec.buildRequests([bidRequest], bidderRequest); - - 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 - api: [2, 3] // optional - }, { - context: 'instream', - playerSize: [600, 300] - }); - - 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], - 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, - uspSignal: 0, - coppa: 0, - prebidJsVersion: '$prebid.version$', - fpa: '' - }; - - expect(requests[0].data).to.equal(JSON.stringify(expectedVideoBidRequest)); - }); - - it('sends video bid requests containing all the required parameters from mediaTypes', function() { - setupVideoBidRequest(null, { - 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 - api: [2, 3], // optional - context: 'instream', - playerSize: [600, 300] - }); - - 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], - 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, - uspSignal: 0, - coppa: 0, - prebidJsVersion: '$prebid.version$', - fpa: '' - }; - - expect(requests[0].data).to.equal(JSON.stringify(expectedVideoBidRequest)); - }); - - it('overrides video parameters with parameters from adunit', function() { - setupVideoBidRequest({ - mimes: ['video/mp4'] - }, { - context: 'instream', - playerSize: [600, 300] - }); - 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, - uspSignal: 0, - coppa: 0, - prebidJsVersion: '$prebid.version$', - fpa: '' - }; - - expect(requests[0].data).to.equal(JSON.stringify(expectedVideoBidRequest)); - }); - - it('sends video bid request when no video parameters are given', function () { - setupVideoBidRequest(null, { - context: 'instream', - playerSize: [600, 300] - }); - - 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, - uspSignal: 0, - coppa: 0, - prebidJsVersion: '$prebid.version$', - fpa: '' - }; - - 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]] - } - }; - - 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', - gdprSignal: 0, - uspSignal: 0, - coppa: 0, - prebidJsVersion: '$prebid.version$', - fpa: '' - }; - - expect(requests[0].data).to.equal(JSON.stringify(expectedBidRequest)); - }); - }); - - it('propagates GDPR consent string and signal', function () { - const bidderRequest = { - gdprConsent: { - gdprApplies: true, - consentString: 'consentString' - } - }; - - const requests = qcSpec.buildRequests([bidRequest], bidderRequest); - const parsed = JSON.parse(requests[0].data); - - expect(parsed.gdprSignal).to.equal(1); - expect(parsed.gdprConsent).to.equal('consentString'); - }); - - it('allows TCF v2 request when Quantcast has consent for purpose 1', function() { - const bidderRequest = { - gdprConsent: { - gdprApplies: true, - consentString: 'consentString', - vendorData: { - vendor: { - consents: { - '11': true - } - }, - purpose: { - consents: { - '1': true - } - } - }, - apiVersion: 2 - } - }; - - const requests = qcSpec.buildRequests([bidRequest], bidderRequest); - const parsed = JSON.parse(requests[0].data); - - expect(parsed.gdprSignal).to.equal(1); - expect(parsed.gdprConsent).to.equal('consentString'); - }); - - it('blocks TCF v2 request when no consent for Quantcast', function() { - const bidderRequest = { - gdprConsent: { - gdprApplies: true, - consentString: 'consentString', - vendorData: { - vendor: { - consents: { - '11': false - } - }, - purpose: { - consents: { - '1': true - } - } - }, - apiVersion: 2 - } - }; - - const requests = qcSpec.buildRequests([bidRequest], bidderRequest); - - expect(requests).to.equal(undefined); - }); - - it('blocks TCF v2 request when no consent for purpose 1', function() { - const bidderRequest = { - gdprConsent: { - gdprApplies: true, - consentString: 'consentString', - vendorData: { - vendor: { - consents: { - '11': true - } - }, - purpose: { - consents: { - '1': false - } - } - }, - apiVersion: 2 - } - }; - - const requests = qcSpec.buildRequests([bidRequest], bidderRequest); - - expect(requests).to.equal(undefined); - }); - - it('blocks TCF v2 request when Quantcast not allowed by publisher', function () { - const bidderRequest = { - gdprConsent: { - gdprApplies: true, - consentString: 'consentString', - vendorData: { - vendor: { - consents: { - '11': true - } - }, - purpose: { - consents: { - '1': true - } - }, - publisher: { - restrictions: { - '1': { - '11': 0 - } - } - } - }, - apiVersion: 2 - } - }; - - const requests = qcSpec.buildRequests([bidRequest], bidderRequest); - - expect(requests).to.equal(undefined); - }); - - it('blocks TCF v2 request when legitimate interest required', function () { - const bidderRequest = { - gdprConsent: { - gdprApplies: true, - consentString: 'consentString', - vendorData: { - vendor: { - consents: { - '11': true - } - }, - purpose: { - consents: { - '1': true - } - }, - publisher: { - restrictions: { - '1': { - '11': 2 - } - } - } - }, - apiVersion: 2 - } - }; - - const requests = qcSpec.buildRequests([bidRequest], bidderRequest); - - expect(requests).to.equal(undefined); - }); - - it('propagates US Privacy/CCPA consent information', function () { - const bidderRequest = { uspConsent: 'consentString' } - const requests = qcSpec.buildRequests([bidRequest], bidderRequest); - const parsed = JSON.parse(requests[0].data); - expect(parsed.uspSignal).to.equal(1); - expect(parsed.uspConsent).to.equal('consentString'); - }); - - it('propagates Quantcast first-party cookie (fpa)', function() { - storage.setCookie('__qca', 'P0-TestFPA'); - const requests = qcSpec.buildRequests([bidRequest], bidderRequest); - const parsed = JSON.parse(requests[0].data); - expect(parsed.fpa).to.equal('P0-TestFPA'); - }); - - describe('propagates coppa', function() { - let sandbox; - beforeEach(() => { - sandbox = sinon.createSandbox(); - }); - - afterEach(() => { - sandbox.restore(); - }); - - it('propagates coppa as 1 if coppa param is set to true in the bid request', function () { - bidRequest.params = { - publisherId: 'test_publisher_id', - coppa: true - }; - sandbox.stub(config, 'getConfig').callsFake((key) => { - const config = { - 'coppa': true - }; - return config[key]; - }); - const requests = qcSpec.buildRequests([bidRequest], bidderRequest); - expect(JSON.parse(requests[0].data).coppa).to.equal(1); - }); - - it('propagates coppa as 0 if there is no coppa param or coppa is set to false in the bid request', function () { - const requestsWithoutCoppa = qcSpec.buildRequests([bidRequest], bidderRequest); - expect(JSON.parse(requestsWithoutCoppa[0].data).coppa).to.equal(0); - - bidRequest.params = { - publisherId: 'test_publisher_id', - coppa: false - }; - sandbox.stub(config, 'getConfig').callsFake((key) => { - const config = { - 'coppa': false - }; - return config[key]; - }); - const requestsWithFalseCoppa = qcSpec.buildRequests([bidRequest], bidderRequest); - expect(JSON.parse(requestsWithFalseCoppa[0].data).coppa).to.equal(0); - }); - }); - - 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 - 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 - bids: [ - { - statusCode: 1, - placementCode: 'imp1', // Changing this to placementCode to be reflective - cpm: 4.5, - currency: 'USD', - ad: - '
Quantcast
', - creativeId: 1001, - width: 300, - height: 250, - meta: { - advertiserDomains: ['dailymail.com'] - } - } - ] - }; - - const response = { - body, - headers: {} - }; - - 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`', 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`', function () { - const interpretedResponse = qcSpec.interpretResponse({ bids: [] }); - - expect(interpretedResponse.length).to.equal(0); - }); - - it('should get correct bid response', function () { - 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', - meta: { - advertiserDomains: ['dailymail.com'] - } - }; - const interpretedResponse = qcSpec.interpretResponse(response); - - expect(interpretedResponse[0]).to.deep.equal(expectedResponse); - }); - - 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', - meta: { - advertiserDomains: ['dailymail.com'] - } - }; - 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 - bids: [] - }; - const response = { - body, - headers: {} - }; - const interpretedResponse = qcSpec.interpretResponse(response); - - expect(interpretedResponse.length).to.equal(0); - }); - - it('should return pixel url when available userSync available', function () { - const syncOptions = { - pixelEnabled: true - }; - const serverResponses = [ - { - body: { - userSync: { - url: 'http://quantcast.com/pixelUrl' - } - } - }, - { - body: { - - } - } - ]; - - const actualSyncs = qcSpec.getUserSyncs(syncOptions, serverResponses); - const expectedSync = { - type: 'image', - url: 'http://quantcast.com/pixelUrl' - }; - expect(actualSyncs.length).to.equal(1); - expect(actualSyncs[0]).to.deep.equal(expectedSync); - qcSpec.resetUserSync(); - }); - - it('should not return user syncs if done already', function () { - const syncOptions = { - pixelEnabled: true - }; - const serverResponses = [ - { - body: { - userSync: { - url: 'http://quantcast.com/pixelUrl' - } - } - }, - { - body: { - - } - } - ]; - - let actualSyncs = qcSpec.getUserSyncs(syncOptions, serverResponses); - const expectedSync = { - type: 'image', - url: 'http://quantcast.com/pixelUrl' - }; - expect(actualSyncs.length).to.equal(1); - expect(actualSyncs[0]).to.deep.equal(expectedSync); - - actualSyncs = qcSpec.getUserSyncs(syncOptions, serverResponses); - expect(actualSyncs.length).to.equal(0); - - qcSpec.resetUserSync(); - }); - }); -}); diff --git a/test/spec/modules/quantcastIdSystem_spec.js b/test/spec/modules/quantcastIdSystem_spec.js deleted file mode 100644 index 157c00e7567..00000000000 --- a/test/spec/modules/quantcastIdSystem_spec.js +++ /dev/null @@ -1,405 +0,0 @@ -import { quantcastIdSubmodule, storage, firePixel, hasCCPAConsent, hasGDPRConsent, checkTCFv2 } from 'modules/quantcastIdSystem.js'; -import * as utils from 'src/utils.js'; -import {coppaDataHandler} from 'src/adapterManager'; -import {attachIdSystem} from '../../../modules/userId/index.js'; -import {createEidsArray} from '../../../modules/userId/eids.js'; -import {expect} from 'chai/index.mjs'; - -describe('QuantcastId module', function () { - beforeEach(function() { - sinon.stub(coppaDataHandler, 'getCoppa'); - sinon.stub(utils, 'triggerPixel'); - sinon.stub(window, 'addEventListener'); - }); - - afterEach(function () { - utils.triggerPixel.restore(); - coppaDataHandler.getCoppa.restore(); - window.addEventListener.restore(); - }); - - it('getId() should return a quantcast id when the Quantcast first party cookie exists', function () { - sinon.stub(storage, 'getCookie').returns('P0-TestFPA'); - const id = quantcastIdSubmodule.getId(); - expect(id).to.be.deep.equal({id: {quantcastId: 'P0-TestFPA'}}); - storage.getCookie.restore(); - }); - - it('getId() should return an empty id when the Quantcast first party cookie is missing', function () { - const id = quantcastIdSubmodule.getId(); - expect(id).to.be.deep.equal({id: undefined}); - }); -}); - -describe('QuantcastId fire pixel', function () { - beforeEach(function () { - storage.setCookie('__qca', '', 'Thu, 01 Jan 1970 00:00:00 GMT'); - sinon.stub(storage, 'setCookie'); - sinon.stub(utils, 'triggerPixel'); - }); - - afterEach(function () { - utils.triggerPixel.restore(); - storage.setCookie.restore(); - }); - - it('fpa should be set when not present on this call', function () { - firePixel('clientId'); - var urlString = utils.triggerPixel.getCall(0).args[0]; - var parsedUrl = utils.parseUrl(urlString); - var urlSearchParams = parsedUrl.search; - assert.equal(urlSearchParams.fpan, '1'); - assert.notEqual(urlSearchParams.fpa, null); - }); - - it('fpa should be extracted from the Quantcast first party cookie when present on this call', function () { - sinon.stub(storage, 'getCookie').returns('P0-TestFPA'); - firePixel('clientId'); - var urlString = utils.triggerPixel.getCall(0).args[0]; - var parsedUrl = utils.parseUrl(urlString); - var urlSearchParams = parsedUrl.search; - assert.equal(urlSearchParams.fpan, '0'); - assert.equal(urlSearchParams.fpa, 'P0-TestFPA'); - storage.getCookie.restore(); - }); - - it('function to trigger pixel is called once', function () { - firePixel('clientId'); - expect(utils.triggerPixel.calledOnce).to.equal(true); - }); - - it('function to trigger pixel is not called when client id is absent', function () { - firePixel(); - expect(utils.triggerPixel.calledOnce).to.equal(false); - }); -}); - -describe('Quantcast CCPA consent check', function() { - it('returns true when CCPA constent string is not present', function() { - expect(hasCCPAConsent()).to.equal(true); - }); - - it("returns true when notice_given or do-not-sell in CCPA constent string is not 'Y' ", function() { - expect(hasCCPAConsent('1NNN')).to.equal(true); - expect(hasCCPAConsent('1YNN')).to.equal(true); - expect(hasCCPAConsent('1NYN')).to.equal(true); - }); - - it("returns false when CCPA consent string is present, and notice_given or do-not-sell in the string is 'Y' ", function() { - expect(hasCCPAConsent('1YYN')).to.equal(false); - }); -}); - -describe('Quantcast GDPR consent check', function() { - it("returns true when GDPR doesn't apply", function() { - expect(hasGDPRConsent({gdprApplies: false})).to.equal(true); - }); - - it('returns false if denied consent, even if special purpose 1 treatment is true in DE', function() { - expect(checkTCFv2({ - gdprApplies: true, - publisherCC: 'DE', - purposeOneTreatment: true, - vendor: { - consents: { '11': false } - }, - purpose: { - consents: { '1': false } - }, - publisher: { - restrictions: { - '1': { - '11': 0 // flatly disallow Quantcast - } - } - } - }, ['1'])).to.equal(false); - }); - - it('returns false if publisher flatly denies required purpose', function() { - expect(checkTCFv2({ - gdprApplies: true, - vendor: { - consents: { '11': true } - }, - purpose: { - consents: { '1': true } - }, - publisher: { - restrictions: { - '1': { - '11': 0 // flatly disallow Quantcast - } - } - } - }, ['1'])).to.equal(false); - }); - - it('returns true if positive consent for required purpose', function() { - expect(checkTCFv2({ - gdprApplies: true, - vendor: { - consents: { '11': true } - }, - purpose: { - consents: { '1': true } - } - }, ['1'])).to.equal(true); - }); - - it('returns false if positive consent but publisher requires legitimate interest for required purpose', function() { - expect(checkTCFv2({ - gdprApplies: true, - vendor: { - consents: { '11': true } - }, - purpose: { - consents: { '1': true } - }, - publisher: { - restrictions: { - '1': { - '11': 2 // require legitimate interest for Quantcast - } - } - } - }, ['1'])).to.equal(false); - }); - - it('returns false if no vendor consent and no legitimate interest', function() { - expect(checkTCFv2({ - gdprApplies: true, - vendor: { - consents: { '11': false } - }, - purpose: { - consents: { '1': true } - } - }, ['1'])).to.equal(false); - }); - - it('returns false if no purpose consent and no legitimate interest', function() { - expect(checkTCFv2({ - gdprApplies: true, - vendor: { - consents: { '11': true } - }, - purpose: { - consents: { '1': false } - } - }, ['1'])).to.equal(false); - }); - - it('returns false if no consent, but legitimate interest for consent-first purpose, and no restrictions specified', function() { - expect(checkTCFv2({ - gdprApplies: true, - vendor: { - consents: { '11': true }, - legitimateInterests: { '11': true } - }, - purpose: { - consents: { '1': false }, - legitimateInterests: { '1': true } - } - }, ['1'])).to.equal(false); - }); - - it('returns false if consent, but no legitimate interest for legitimate-interest-first purpose, and no restrictions specified', function() { - expect(checkTCFv2({ - gdprApplies: true, - vendor: { - consents: { '11': true }, - legitimateInterests: { '11': true } - }, - purpose: { - consents: { '10': true }, - legitimateInterests: { '10': false } - } - }, ['10'])).to.equal(false); - }); - - it('returns true if consent, but no legitimate interest for legitimate-interest-first purpose, and corresponding consent restriction specified', function() { - expect(checkTCFv2({ - gdprApplies: true, - vendor: { - consents: { '11': true }, - legitimateInterests: { '11': true } - }, - purpose: { - consents: { '10': true }, - legitimateInterests: { '10': false } - }, - publisher: { - restrictions: { - '10': { - '11': 1 // require consent for Quantcast - } - } - } - }, ['10'])).to.equal(true); - }); - - it('returns false if no consent but legitimate interest for required purpose other than 1, but publisher requires consent', function() { - expect(checkTCFv2({ - gdprApplies: true, - vendor: { - consents: { '11': false }, - legitimateInterests: { '11': true } - }, - purpose: { - consents: { '10': false }, - legitimateInterests: { '10': true } - }, - publisher: { - restrictions: { - '10': { - '11': 1 // require consent for Quantcast - } - } - } - }, ['10'])).to.equal(false); - }); - - it('returns false if no consent and no legitimate interest for vendor for required purpose other than 1', function() { - expect(checkTCFv2({ - gdprApplies: true, - vendor: { - consents: { '11': false }, - legitimateInterests: { '11': false } - }, - purpose: { - consents: { '10': false }, - legitimateInterests: { '10': true } - } - }, ['10'])).to.equal(false); - }); - - it('returns false if no consent and no legitimate interest for required purpose other than 1', function() { - expect(checkTCFv2({ - gdprApplies: true, - vendor: { - consents: { '11': false }, - legitimateInterests: { '11': true } - }, - purpose: { - consents: { '10': false }, - legitimateInterests: { '10': false } - } - }, ['10'])).to.equal(false); - }); - - it('returns false if no consent but legitimate interest for required purpose, but required purpose is purpose 1', function() { - expect(checkTCFv2({ - gdprApplies: true, - vendor: { - consents: { '11': false }, - legitimateInterests: { '11': true } - }, - purpose: { - consents: { '1': false }, - legitimateInterests: { '1': true } - } - }, ['1'])).to.equal(false); - }); - - it('returns true if different legal bases for multiple required purposes', function() { - expect(checkTCFv2({ - gdprApplies: true, - vendor: { - consents: { '11': true }, - legitimateInterests: { '11': true } - }, - purpose: { - consents: { - '1': true, - '10': false - }, - legitimateInterests: { - '1': false, - '10': true - } - }, - publisher: { - restrictions: { - '10': { - '11': 2 // require legitimate interest for Quantcast - } - } - } - })).to.equal(true); - }); - - it('returns true if full consent and legitimate interest for all required purposes with no restrictions specified', function() { - expect(checkTCFv2({ - gdprApplies: true, - vendor: { - consents: { '11': true }, - legitimateInterests: { '11': true } - }, - purpose: { - consents: { - '1': true, - '3': true, - '7': true, - '8': true, - '9': true, - '10': true - }, - legitimateInterests: { - '1': true, - '3': true, - '7': true, - '8': true, - '9': true, - '10': true - } - } - })).to.equal(true); - }); - - it('returns false if one of multiple required purposes has no legal basis', function() { - expect(checkTCFv2({ - gdprApplies: true, - vendor: { - consents: { '11': true }, - legitimateInterests: { '11': true } - }, - purpose: { - consents: { - '1': true, - '10': false - }, - legitimateInterests: { - '11': false, - '10': true - } - }, - publisher: { - restrictions: { - '10': { - '11': 1 // require consent for Quantcast - } - } - } - })).to.equal(false); - }); - describe('eids', () => { - before(() => { - attachIdSystem(quantcastIdSubmodule); - }); - it('quantcastId', function() { - const userId = { - quantcastId: 'some-random-id-value' - }; - const newEids = createEidsArray(userId); - expect(newEids.length).to.equal(1); - expect(newEids[0]).to.deep.equal({ - source: 'quantcast.com', - uids: [{ - id: 'some-random-id-value', - atype: 1 - }] - }); - }); - }) -}); diff --git a/test/spec/modules/qwarryBidAdapter_spec.js b/test/spec/modules/qwarryBidAdapter_spec.js index ae930277476..00e5ab4594a 100644 --- a/test/spec/modules/qwarryBidAdapter_spec.js +++ b/test/spec/modules/qwarryBidAdapter_spec.js @@ -103,7 +103,7 @@ describe('qwarryBidAdapter', function () { expect(bidderRequest.method).to.equal('POST') expect(bidderRequest.data.requestId).to.equal('123') expect(bidderRequest.data.referer).to.equal('http://test.com/path.html') - expect(bidderRequest.data.schain).to.deep.contains({ver: '1.0', complete: 1, nodes: [{asi: 'qwarry.com', sid: '00001', hp: 1}]}) + expect(bidderRequest.data.schain).to.deep.contains({ ver: '1.0', complete: 1, nodes: [{ asi: 'qwarry.com', sid: '00001', hp: 1 }] }) expect(bidderRequest.data.bids).to.deep.contains({ bidId: '456', zoneToken: 'e64782a4-8e68-4c38-965b-80ccf115d46f', pos: 7, sizes: [{ width: 100, height: 200 }, { width: 300, height: 400 }] }) expect(bidderRequest.data.gdprConsent).to.deep.contains({ consentRequired: true, consentString: 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A==' }) expect(bidderRequest.options.customHeaders).to.deep.equal({ 'Rtb-Direct': true }) diff --git a/test/spec/modules/r2b2AnalytiscAdapter_spec.js b/test/spec/modules/r2b2AnalytiscAdapter_spec.js index 1cc675aded5..9098cf10c13 100644 --- a/test/spec/modules/r2b2AnalytiscAdapter_spec.js +++ b/test/spec/modules/r2b2AnalytiscAdapter_spec.js @@ -1,15 +1,16 @@ -import r2b2Analytics, {resetAnalyticAdapter} from '../../../modules/r2b2AnalyticsAdapter.js'; +import r2b2Analytics, { resetAnalyticAdapter } from '../../../modules/r2b2AnalyticsAdapter.js'; import { expect } from 'chai'; -import {EVENTS, AD_RENDER_FAILED_REASON, REJECTION_REASON} from 'src/constants.js'; +import { EVENTS, AD_RENDER_FAILED_REASON, REJECTION_REASON } from 'src/constants.js'; import * as pbEvents from 'src/events.js'; import * as ajax from 'src/ajax.js'; import * as utils from 'src/utils'; -import {getGlobal} from 'src/prebidGlobal'; +import { getGlobal } from 'src/prebidGlobal'; import * as prebidGlobal from 'src/prebidGlobal'; const adapterManager = require('src/adapterManager').default; -const { NO_BID, AUCTION_INIT, BID_REQUESTED, BID_TIMEOUT, BID_RESPONSE, BID_REJECTED, BIDDER_DONE, +const { + NO_BID, AUCTION_INIT, BID_REQUESTED, BID_TIMEOUT, BID_RESPONSE, BID_REJECTED, BIDDER_DONE, AUCTION_END, BID_WON, SET_TARGETING, STALE_RENDER, AD_RENDER_SUCCEEDED, AD_RENDER_FAILED, BID_VIEWABLE } = EVENTS; @@ -30,10 +31,10 @@ const AD_UNIT_1 = { }, 'bids': [{ 'bidder': 'r2b2', - 'params': {'pid': R2B2_PID_1} + 'params': { 'pid': R2B2_PID_1 } }, { 'bidder': 'adf', - 'params': {'mid': 1799592} + 'params': { 'mid': 1799592 } }], 'sizes': BANNER_SETTING_1.sizes, 'transactionId': AD_UNIT_1_TID, @@ -50,7 +51,7 @@ const AD_UNIT_2 = { }, 'bids': [{ 'bidder': 'r2b2', - 'params': {'pid': R2B2_PID_2} + 'params': { 'pid': R2B2_PID_2 } }, { 'bidder': 'stroeerCore', 'params': { 'sid': '9532ef8d-e630-45a9-88f6-3eb3eb265d58' } @@ -71,7 +72,7 @@ const R2B2_BIDDER_REQUEST = { 'bids': [ { 'bidder': 'r2b2', - 'params': {'pid': R2B2_PID_1}, + 'params': { 'pid': R2B2_PID_1 }, 'mediaTypes': { 'banner': BANNER_SETTING_1 }, 'adUnitCode': AD_UNIT_1_CODE, 'transactionId': '0b3464bb-d80a-490e-8367-a65201a37ba3', @@ -82,7 +83,7 @@ const R2B2_BIDDER_REQUEST = { }, { 'bidder': 'r2b2', - 'params': {'pid': R2B2_PID_2}, + 'params': { 'pid': R2B2_PID_2 }, 'mediaTypes': { 'banner': BANNER_SETTING_2 }, 'adUnitCode': AD_UNIT_2_CODE, 'transactionId': 'c8c3643c-9de0-43ea-bcd6-cc0072ec9b45', @@ -274,7 +275,7 @@ const MOCK = { } function fireEvents(events) { return events.map((ev, i) => { - ev = Array.isArray(ev) ? ev : [ev, {i: i}]; + ev = Array.isArray(ev) ? ev : [ev, { i: i }]; pbEvents.emit.apply(null, ev) return ev; }); @@ -286,7 +287,7 @@ function expectEvents(events, sandbox) { to: { beTrackedBy(trackFn) { events.forEach(([eventType, args]) => { - sandbox.assert.calledWithMatch(trackFn, sandbox.match({eventType, args})); + sandbox.assert.calledWithMatch(trackFn, sandbox.match({ eventType, args })); }); }, beBundledTo(bundleFn) { @@ -553,7 +554,7 @@ describe('r2b2 Analytics', function () { expect(adformBidRequest.d).to.be.deep.equal({ ai: AUCTION_ID, b: 'adf', - u: {[AD_UNIT_1_CODE]: 1} + u: { [AD_UNIT_1_CODE]: 1 } }); done(); @@ -599,7 +600,7 @@ describe('r2b2 Analytics', function () { expect(timeoutEvent.d).to.be.deep.equal({ ai: AUCTION_ID, b: { - r2b2: {[AD_UNIT_1_CODE]: 2} + r2b2: { [AD_UNIT_1_CODE]: 2 } } }); @@ -649,7 +650,7 @@ describe('r2b2 Analytics', function () { sz: '300x100', bi: R2B2_AD_UNIT_2_BID.requestId, }], - u: {[AD_UNIT_2_CODE]: {b: {r2b2: 1}}}, + u: { [AD_UNIT_2_CODE]: { b: { r2b2: 1 } } }, o: 1, bc: 1, nbc: 0, diff --git a/test/spec/modules/r2b2BidAdapter_spec.js b/test/spec/modules/r2b2BidAdapter_spec.js index 2d506ab8dc3..56f761b5056 100644 --- a/test/spec/modules/r2b2BidAdapter_spec.js +++ b/test/spec/modules/r2b2BidAdapter_spec.js @@ -1,5 +1,5 @@ -import {expect} from 'chai'; -import {spec, internal as r2b2, internal} from 'modules/r2b2BidAdapter.js'; +import { expect } from 'chai'; +import { spec, internal as r2b2, internal } from 'modules/r2b2BidAdapter.js'; import * as utils from '../../../src/utils.js'; import 'modules/userId/index.js'; @@ -46,11 +46,11 @@ describe('R2B2 adapter', function () { const id2 = { pid: 'd/g/p/1' }; const id2Object = { d: 'd', g: 'g', p: 'p', m: 1 }; const badId = { pid: 'd/g/' }; - const bid1 = { bidId: bidId1, bidder, params: [ id1 ] }; - const bid2 = { bidId: bidId2, bidder, params: [ id2 ] }; - const bidWithBadSetup = { bidId: bidId3, bidder, params: [ badId ] }; - const bidForeign1 = { bidId: bidId4, bidder: foreignBidder, params: [ { id: 'abc' } ] }; - const bidForeign2 = { bidId: bidId5, bidder: foreignBidder, params: [ { id: 'xyz' } ] }; + const bid1 = { bidId: bidId1, bidder, params: [id1] }; + const bid2 = { bidId: bidId2, bidder, params: [id2] }; + const bidWithBadSetup = { bidId: bidId3, bidder, params: [badId] }; + const bidForeign1 = { bidId: bidId4, bidder: foreignBidder, params: [{ id: 'abc' }] }; + const bidForeign2 = { bidId: bidId5, bidder: foreignBidder, params: [{ id: 'xyz' }] }; const fakeTime = 1234567890; const cacheBusterRegex = /[\?&]cb=([^&]+)/; let bidStub, time; @@ -91,7 +91,7 @@ describe('R2B2 adapter', function () { }, site: {}, device: {}, - source: {ext: {schain: schain}} + source: { ext: { schain: schain } } }, }, { bidder: 'r2b2', @@ -128,7 +128,7 @@ describe('R2B2 adapter', function () { }, site: {}, device: {}, - source: {ext: {schain: schain}} + source: { ext: { schain: schain } } }, }]; bidderRequest = { @@ -150,7 +150,7 @@ describe('R2B2 adapter', function () { }, site: {}, device: {}, - source: {ext: {schain: schain}} + source: { ext: { schain: schain } } }, gdprConsent: { consentString: 'consent-string', @@ -191,7 +191,7 @@ describe('R2B2 adapter', function () { requestForInterpretResponse = { data: { imp: [ - {id: impId} + { id: impId } ] }, bids @@ -202,51 +202,51 @@ describe('R2B2 adapter', function () { const bid = {}; it('should return false when missing required "pid" param', function () { - bid.params = {random: 'param'}; + bid.params = { random: 'param' }; expect(spec.isBidRequestValid(bid)).to.equal(false); - bid.params = {d: 'd', g: 'g', p: 'p', m: 1}; + bid.params = { d: 'd', g: 'g', p: 'p', m: 1 }; expect(spec.isBidRequestValid(bid)).to.equal(false) }); it('should return false when "pid" is malformed', function () { - bid.params = {pid: 'pid'}; + bid.params = { pid: 'pid' }; expect(spec.isBidRequestValid(bid)).to.equal(false); - bid.params = {pid: '///'}; + bid.params = { pid: '///' }; expect(spec.isBidRequestValid(bid)).to.equal(false); - bid.params = {pid: '/g/p/m'}; + bid.params = { pid: '/g/p/m' }; expect(spec.isBidRequestValid(bid)).to.equal(false); - bid.params = {pid: 'd//p/m'}; + bid.params = { pid: 'd//p/m' }; expect(spec.isBidRequestValid(bid)).to.equal(false); - bid.params = {pid: 'd/g//m'}; + bid.params = { pid: 'd/g//m' }; expect(spec.isBidRequestValid(bid)).to.equal(false); - bid.params = {pid: 'd/p/'}; + bid.params = { pid: 'd/p/' }; expect(spec.isBidRequestValid(bid)).to.equal(false); - bid.params = {pid: 'd/g/p/m/t'}; + bid.params = { pid: 'd/g/p/m/t' }; expect(spec.isBidRequestValid(bid)).to.equal(false); }); it('should return true when "pid" is a correct dgpm', function () { - bid.params = {pid: 'd/g/p/m'}; + bid.params = { pid: 'd/g/p/m' }; expect(spec.isBidRequestValid(bid)).to.equal(true); }); it('should return true when type is blank', function () { - bid.params = {pid: 'd/g/p/'}; + bid.params = { pid: 'd/g/p/' }; expect(spec.isBidRequestValid(bid)).to.equal(true); }); it('should return true when type is missing', function () { - bid.params = {pid: 'd/g/p'}; + bid.params = { pid: 'd/g/p' }; expect(spec.isBidRequestValid(bid)).to.equal(true); }); it('should return true when "pid" is a number', function () { - bid.params = {pid: 12356}; + bid.params = { pid: 12356 }; expect(spec.isBidRequestValid(bid)).to.equal(true); }); it('should return true when "pid" is a numeric string', function () { - bid.params = {pid: '12356'}; + bid.params = { pid: '12356' }; expect(spec.isBidRequestValid(bid)).to.equal(true); }); it('should return true for selfpromo unit', function () { - bid.params = {pid: 'selfpromo'}; + bid.params = { pid: 'selfpromo' }; expect(spec.isBidRequestValid(bid)).to.equal(true) }); }); @@ -269,8 +269,8 @@ describe('R2B2 adapter', function () { it('should pass correct parameters', function () { const requests = spec.buildRequests([bids[0]], bidderRequest); - const {data} = requests[0]; - const {imp, device, site, source, ext, cur, test} = data; + const { data } = requests[0]; + const { imp, device, site, source, ext, cur, test } = data; expect(imp).to.be.an('array').that.has.lengthOf(1); expect(device).to.be.an('object'); expect(site).to.be.an('object'); @@ -282,15 +282,15 @@ describe('R2B2 adapter', function () { it('should pass correct imp', function () { const requests = spec.buildRequests([bids[0]], bidderRequest); - const {data} = requests[0]; - const {imp} = data; + const { data } = requests[0]; + const { imp } = data; expect(imp).to.be.an('array').that.has.lengthOf(1); expect(imp[0]).to.be.an('object'); const bid = imp[0]; expect(bid.id).to.equal('20917a54ee9858'); - expect(bid.banner).to.deep.equal({topframe: 0, format: [{w: 300, h: 250}]}); + expect(bid.banner).to.deep.equal({ topframe: 0, format: [{ w: 300, h: 250 }] }); expect(bid.ext).to.be.an('object'); - expect(bid.ext.r2b2).to.deep.equal({d: 'example.com', g: 'generic', p: '300x250', m: 1}); + expect(bid.ext.r2b2).to.deep.equal({ d: 'example.com', g: 'generic', p: '300x250', m: 1 }); }); it('should map type correctly', function () { @@ -330,27 +330,27 @@ describe('R2B2 adapter', function () { it('should pass correct parameters for test ad', function () { const testAdBid = bids[0]; - testAdBid.params = {pid: 'selfpromo'}; + testAdBid.params = { pid: 'selfpromo' }; const requests = spec.buildRequests([testAdBid], bidderRequest); - const {data} = requests[0]; - const {imp} = data; + const { data } = requests[0]; + const { imp } = data; expect(imp).to.be.an('array').that.has.lengthOf(1); expect(imp[0]).to.be.an('object'); const bid = imp[0]; expect(bid.ext).to.be.an('object'); - expect(bid.ext.r2b2).to.deep.equal({d: 'test', g: 'test', p: 'selfpromo', m: 0, 'selfpromo': 1}); + expect(bid.ext.r2b2).to.deep.equal({ d: 'test', g: 'test', p: 'selfpromo', m: 0, 'selfpromo': 1 }); }); it('should pass multiple bids', function () { const requests = spec.buildRequests(bids, bidderRequest); expect(requests).to.be.an('array').that.has.lengthOf(1); - const {data} = requests[0]; - const {imp} = data; + const { data } = requests[0]; + const { imp } = data; expect(imp).to.be.an('array').that.has.lengthOf(bids.length); const bid1 = imp[0]; - expect(bid1.ext.r2b2).to.deep.equal({d: 'example.com', g: 'generic', p: '300x250', m: 1}); + expect(bid1.ext.r2b2).to.deep.equal({ d: 'example.com', g: 'generic', p: '300x250', m: 1 }); const bid2 = imp[1]; - expect(bid2.ext.r2b2).to.deep.equal({d: 'example.com', g: 'generic', p: '300x600', m: 0}); + expect(bid2.ext.r2b2).to.deep.equal({ d: 'example.com', g: 'generic', p: '300x600', m: 0 }); }); it('should set up internal variables', function () { @@ -359,15 +359,15 @@ describe('R2B2 adapter', function () { const bid2Id = bids[1].bidId; expect(r2b2.placementsToSync).to.be.an('array').that.has.lengthOf(2); expect(r2b2.mappedParams).to.have.property(bid1Id); - expect(r2b2.mappedParams[bid1Id]).to.deep.equal({d: 'example.com', g: 'generic', p: '300x250', m: 1, pid: 'example.com/generic/300x250/1'}); + expect(r2b2.mappedParams[bid1Id]).to.deep.equal({ d: 'example.com', g: 'generic', p: '300x250', m: 1, pid: 'example.com/generic/300x250/1' }); expect(r2b2.mappedParams).to.have.property(bid2Id); - expect(r2b2.mappedParams[bid2Id]).to.deep.equal({d: 'example.com', g: 'generic', p: '300x600', m: 0, pid: 'example.com/generic/300x600/0'}); + expect(r2b2.mappedParams[bid2Id]).to.deep.equal({ d: 'example.com', g: 'generic', p: '300x600', m: 0, pid: 'example.com/generic/300x600/0' }); }); it('should pass gdpr properties', function () { const requests = spec.buildRequests(bids, bidderRequest); - const {data} = requests[0]; - const {user, regs} = data; + const { data } = requests[0]; + const { user, regs } = data; expect(user).to.be.an('object').that.has.property('ext'); expect(regs).to.be.an('object').that.has.property('ext'); expect(user.ext.consent).to.equal('consent-string'); @@ -376,21 +376,21 @@ describe('R2B2 adapter', function () { it('should pass us privacy properties', function () { const requests = spec.buildRequests(bids, bidderRequest); - const {data} = requests[0]; - const {regs} = data; + const { data } = requests[0]; + const { regs } = data; expect(regs).to.be.an('object').that.has.property('ext'); expect(regs.ext.us_privacy).to.equal('1YYY'); }); it('should pass supply chain', function () { const requests = spec.buildRequests(bids, bidderRequest); - const {data} = requests[0]; - const {source} = data; + const { data } = requests[0]; + const { source } = data; expect(source).to.be.an('object').that.has.property('ext'); expect(source.ext.schain).to.deep.equal({ complete: 1, nodes: [ - {asi: 'example.com', hp: 1, sid: '00001'} + { asi: 'example.com', hp: 1, sid: '00001' } ], ver: '1.0' }) @@ -420,7 +420,7 @@ describe('R2B2 adapter', function () { ], }, ]; - bidderRequest.ortb2 = {user: {ext: {eids: eidsArray}}} + bidderRequest.ortb2 = { user: { ext: { eids: eidsArray } } } const requests = spec.buildRequests(bids, bidderRequest); const request = requests[0]; const eids = request.data.user.ext.eids; @@ -435,9 +435,9 @@ describe('R2B2 adapter', function () { expect(result).to.be.an('array').that.has.lengthOf(0); result = spec.interpretResponse({ body: { seatbid: [] } }, {}); expect(result).to.be.an('array').that.has.lengthOf(0); - result = spec.interpretResponse({ body: { seatbid: [ {} ] } }, {}); + result = spec.interpretResponse({ body: { seatbid: [{}] } }, {}); expect(result).to.be.an('array').that.has.lengthOf(0); - result = spec.interpretResponse({ body: { seatbid: [ { bids: [] } ] } }, {}); + result = spec.interpretResponse({ body: { seatbid: [{ bids: [] }] } }, {}); expect(result).to.be.an('array').that.has.lengthOf(0); }); @@ -467,7 +467,7 @@ describe('R2B2 adapter', function () { }); it('should map ext params correctly', function() { - const dgpm = {something: 'something'}; + const dgpm = { something: 'something' }; r2b2.mappedParams = {}; r2b2.mappedParams[impId] = dgpm; const result = spec.interpretResponse({ body: serverResponse }, requestForInterpretResponse); @@ -507,7 +507,7 @@ describe('R2B2 adapter', function () { b2.w = w2; b2.h = h2; serverResponse.seatbid[0].bid.push(b2); - requestForInterpretResponse.data.imp.push({id: impId2}); + requestForInterpretResponse.data.imp.push({ id: impId2 }); const result = spec.interpretResponse({ body: serverResponse }, requestForInterpretResponse); expect(result).to.be.an('array').that.has.lengthOf(2); const firstBid = result[0]; @@ -567,7 +567,7 @@ describe('R2B2 adapter', function () { ext: { dgpm: { d: 'r2b2.cz', g: 'generic', m: 1, p: '300x300', pid: 'r2b2.cz/generic/300x300/1' } }, - params: [ { pid: 'r2b2.cz/generic/300x300/1' } ], + params: [{ pid: 'r2b2.cz/generic/300x300/1' }], }; }); afterEach(function() { diff --git a/test/spec/modules/rakutenBidAdapter_spec.js b/test/spec/modules/rakutenBidAdapter_spec.js index e6cdb12e31d..0c0d20d2a45 100644 --- a/test/spec/modules/rakutenBidAdapter_spec.js +++ b/test/spec/modules/rakutenBidAdapter_spec.js @@ -1,7 +1,7 @@ import { expect } from 'chai' import { spec } from 'modules/rakutenBidAdapter/index.js' import { newBidder } from 'src/adapters/bidderFactory.js' -import {config} from '../../../src/config.js'; +import { config } from '../../../src/config.js'; describe('rakutenBidAdapter', function() { const adapter = newBidder(spec); @@ -161,10 +161,10 @@ describe('rakutenBidAdapter', function() { pixelEnabled: true } }); - it('sucess usersync url', function () { + it('success 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'}); + 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/raveltechRtdProvider_spec.js b/test/spec/modules/raveltechRtdProvider_spec.js index 051221a9248..6747eda0b4d 100644 --- a/test/spec/modules/raveltechRtdProvider_spec.js +++ b/test/spec/modules/raveltechRtdProvider_spec.js @@ -1,8 +1,8 @@ -import {hook} from '../../../src/hook.js'; -import {BANNER} from '../../../src/mediaTypes.js'; -import {raveltechSubmodule} from 'modules/raveltechRtdProvider'; +import { hook } from '../../../src/hook.js'; +import { BANNER } from '../../../src/mediaTypes.js'; +import { raveltechSubmodule } from 'modules/raveltechRtdProvider'; import adapterManager from '../../../src/adapterManager.js'; -import {registerBidder} from 'src/adapters/bidderFactory.js'; +import { registerBidder } from 'src/adapters/bidderFactory.js'; describe('raveltechRtdProvider', () => { const fakeBuildRequests = sinon.spy((valibBidRequests) => { @@ -18,7 +18,7 @@ describe('raveltechRtdProvider', () => { auctionId: 'abc', bidId: 'abc123', userIdAsEids: [ - { source: 'usersource.com', uids: [ { id: 'testid123', atype: 1 } ] } + { source: 'usersource.com', uids: [{ id: 'testid123', atype: 1 }] } ] }; @@ -37,7 +37,7 @@ describe('raveltechRtdProvider', () => { adapterManager.aliasBidAdapter('test', 'alias2'); // Init module - raveltechSubmodule.init({ params: { bidders: [ 'alias1', 'test' ], preserveOriginalBid: true } }); + raveltechSubmodule.init({ params: { bidders: ['alias1', 'test'], preserveOriginalBid: true } }); }) afterEach(() => { @@ -51,7 +51,7 @@ describe('raveltechRtdProvider', () => { auctionId: '123', bidderCode: 'alias2', bidderRequestId: 'abc', - bids: [ { ...fakeBidReq, bidder: 'alias2' } ] + bids: [{ ...fakeBidReq, bidder: 'alias2' }] }, sinon.stub(), sinon.stub(), fakeAjax, sinon.stub(), sinon.stub()); expect(fakeAjax.calledOnce).to.be.true; expect(fakeZkad.called).to.be.false; @@ -65,7 +65,7 @@ describe('raveltechRtdProvider', () => { auctionId: '123', bidderCode: 'test', bidderRequestId: 'abc', - bids: [ { ...fakeBidReq, bidder: 'test' } ] + bids: [{ ...fakeBidReq, bidder: 'test' }] }, sinon.stub(), sinon.stub(), fakeAjax, sinon.stub(), sinon.stub()); expect(fakeAjax.calledOnce).to.be.true; expect(fakeZkad.called).to.be.false; @@ -79,7 +79,7 @@ describe('raveltechRtdProvider', () => { auctionId: '123', bidderCode: 'test', bidderRequestId: 'abc', - bids: [ { ...fakeBidReq, bidder: 'test' } ] + bids: [{ ...fakeBidReq, bidder: 'test' }] }, sinon.stub(), sinon.stub(), fakeAjax, sinon.stub(), sinon.stub()); expect(fakeAjax.calledOnce).to.be.true; expect(fakeZkad.called).to.be.false; @@ -94,7 +94,7 @@ describe('raveltechRtdProvider', () => { auctionId: '123', bidderCode: 'test', bidderRequestId: 'abc', - bids: [ { ...fakeBidReq, bidder: 'test' } ] + bids: [{ ...fakeBidReq, bidder: 'test' }] }, sinon.stub(), sinon.stub(), fakeAjax, sinon.stub(), sinon.stub()); expect(fakeAjax.calledTwice).to.be.true; expect(fakeZkad.calledOnce).to.be.true; diff --git a/test/spec/modules/readpeakBidAdapter_spec.js b/test/spec/modules/readpeakBidAdapter_spec.js index 32a4d991054..8248c07014f 100644 --- a/test/spec/modules/readpeakBidAdapter_spec.js +++ b/test/spec/modules/readpeakBidAdapter_spec.js @@ -303,7 +303,7 @@ describe('ReadPeakAdapter', function() { consentString: undefined, } } - const request = spec.buildRequests([nativeBidRequest], {...bidderRequest, ...gdprData}); + const request = spec.buildRequests([nativeBidRequest], { ...bidderRequest, ...gdprData }); const data = JSON.parse(request.data); @@ -327,7 +327,7 @@ describe('ReadPeakAdapter', function() { consentString: tcString } } - const request = spec.buildRequests([nativeBidRequest], {...bidderRequest, ...gdprData}); + const request = spec.buildRequests([nativeBidRequest], { ...bidderRequest, ...gdprData }); const data = JSON.parse(request.data); @@ -465,7 +465,7 @@ describe('ReadPeakAdapter', function() { consentString: undefined, } } - const request = spec.buildRequests([bannerBidRequest], {...bidderRequest, ...gdprData}); + const request = spec.buildRequests([bannerBidRequest], { ...bidderRequest, ...gdprData }); const data = JSON.parse(request.data); @@ -489,7 +489,7 @@ describe('ReadPeakAdapter', function() { consentString: tcString } } - const request = spec.buildRequests([bannerBidRequest], {...bidderRequest, ...gdprData}); + const request = spec.buildRequests([bannerBidRequest], { ...bidderRequest, ...gdprData }); const data = JSON.parse(request.data); diff --git a/test/spec/modules/realTimeDataModule_spec.js b/test/spec/modules/realTimeDataModule_spec.js index 1e8a6d53993..ff23a1052be 100644 --- a/test/spec/modules/realTimeDataModule_spec.js +++ b/test/spec/modules/realTimeDataModule_spec.js @@ -1,14 +1,14 @@ import * as rtdModule from 'modules/rtdModule/index.js'; -import {config} from 'src/config.js'; +import { config } from 'src/config.js'; import * as sinon from 'sinon'; import { EVENTS } from '../../../src/constants.js'; import * as events from '../../../src/events.js'; import 'src/prebid.js'; -import {attachRealTimeDataProvider, onDataDeletionRequest} from 'modules/rtdModule/index.js'; -import {GDPR_GVLIDS} from '../../../src/consentHandler.js'; -import {MODULE_TYPE_RTD} from '../../../src/activities/modules.js'; -import {registerActivityControl} from '../../../src/activities/rules.js'; -import {ACTIVITY_ENRICH_UFPD, ACTIVITY_TRANSMIT_EIDS} from '../../../src/activities/activities.js'; +import { attachRealTimeDataProvider, onDataDeletionRequest } from 'modules/rtdModule/index.js'; +import { GDPR_GVLIDS } from '../../../src/consentHandler.js'; +import { MODULE_TYPE_RTD } from '../../../src/activities/modules.js'; +import { registerActivityControl } from '../../../src/activities/rules.js'; +import { ACTIVITY_ENRICH_UFPD, ACTIVITY_TRANSMIT_EIDS } from '../../../src/activities/activities.js'; describe('Real time module', function () { let eventHandlers; @@ -42,7 +42,7 @@ describe('Real time module', function () { name: 'validSM', init: () => { return true }, getTargetingData: (adUnitsCodes) => { - return {'ad2': {'key': 'validSM'}} + return { 'ad2': { 'key': 'validSM' } } }, getBidRequestData: getBidRequestDataStub }; @@ -51,7 +51,7 @@ describe('Real time module', function () { name: 'validSMWait', init: () => { return true }, getTargetingData: (adUnitsCodes) => { - return {'ad1': {'key': 'validSMWait'}} + return { 'ad1': { 'key': 'validSMWait' } } }, getBidRequestData: getBidRequestDataStub }; @@ -104,7 +104,7 @@ describe('Real time module', function () { it('are registered when RTD module is registered', () => { let mod; try { - mod = attachRealTimeDataProvider({name: 'mockRtd', gvlid: 123}); + mod = attachRealTimeDataProvider({ name: 'mockRtd', gvlid: 123 }); sinon.assert.calledWith(GDPR_GVLIDS.register, MODULE_TYPE_RTD, 'mockRtd', 123); } finally { if (mod) { @@ -124,10 +124,10 @@ describe('Real time module', function () { config.setConfig(conf); rules = [ registerActivityControl(ACTIVITY_TRANSMIT_EIDS, 'test', (params) => { - return {allow: false}; + return { allow: false }; }), registerActivityControl(ACTIVITY_ENRICH_UFPD, 'test', (params) => { - return {allow: false}; + return { allow: false }; }) ] }); @@ -143,13 +143,13 @@ describe('Real time module', function () { }); it('should be able to modify bid request', function (done) { - const request = {bidRequest: {}}; + const request = { bidRequest: {} }; getBidRequestDataStub.callsFake((req) => { req.foo = 'bar'; }); rtdModule.setBidRequestsData(() => { assert(getBidRequestDataStub.calledTwice); - assert(getBidRequestDataStub.calledWith(sinon.match({bidRequest: {}}))); + assert(getBidRequestDataStub.calledWith(sinon.match({ bidRequest: {} }))); expect(request.foo).to.eql('bar'); done(); }, request) @@ -170,7 +170,7 @@ describe('Real time module', function () { } } }; - const request = {ortb2Fragments}; + const request = { ortb2Fragments }; getBidRequestDataStub.callsFake((req) => { expect(req.ortb2Fragments.global.user.eids).to.not.exist; expect(req.ortb2Fragments.bidder.bidderA.eids).to.not.exist; @@ -197,7 +197,7 @@ describe('Real time module', function () { }, { code: 'ad2', - adserverTargeting: {preKey: 'preValue'} + adserverTargeting: { preKey: 'preValue' } } ] }; @@ -205,7 +205,7 @@ describe('Real time module', function () { const expectedAdUnits = [ { code: 'ad1', - adserverTargeting: {key: 'validSMWait'} + adserverTargeting: { key: 'validSMWait' } }, { code: 'ad2', @@ -234,7 +234,7 @@ describe('Real time module', function () { ] }; validSM.getTargetingData = (adUnits) => { - const targeting = {'module1': 'targeting'} + const targeting = { 'module1': 'targeting' } return { ad1: targeting, ad2: targeting @@ -256,7 +256,7 @@ describe('Real time module', function () { function runSetBidRequestData() { return new Promise((resolve) => { - rtdModule.setBidRequestsData(resolve, {bidRequest: {}}); + rtdModule.setBidRequestsData(resolve, { bidRequest: {} }); }); } @@ -348,7 +348,7 @@ describe('Real time module', function () { providers.forEach(p => p.getTargetingData = sinon.spy()); const auction = { adUnitCodes: ['a1'], - adUnits: [{code: 'a1'}] + adUnits: [{ code: 'a1' }] }; mockEmitEvent(EVENTS.AUCTION_END, auction); providers.forEach(p => { @@ -420,8 +420,8 @@ describe('Real time module', function () { it('calls onDataDeletionRequest on submodules', () => { const next = sinon.stub(); - onDataDeletionRequest(next, {a: 0}); - sinon.assert.calledWith(next, {a: 0}); + onDataDeletionRequest(next, { a: 0 }); + sinon.assert.calledWith(next, { a: 0 }); sinon.assert.calledWith(sm1.onDataDeletionRequest, cfg1); sinon.assert.calledWith(sm2.onDataDeletionRequest, cfg2); }); diff --git a/test/spec/modules/reconciliationRtdProvider_spec.js b/test/spec/modules/reconciliationRtdProvider_spec.js index 6efe55ddf46..d98409da518 100644 --- a/test/spec/modules/reconciliationRtdProvider_spec.js +++ b/test/spec/modules/reconciliationRtdProvider_spec.js @@ -44,14 +44,14 @@ describe('Reconciliation Real time data submodule', function () { }); it('should log error if initializied without parameters', function () { - expect(reconciliationSubmodule.init({'name': 'reconciliation', 'params': {}})).to.equal(true); + expect(reconciliationSubmodule.init({ 'name': 'reconciliation', 'params': {} })).to.equal(true); expect(utilsLogErrorSpy.calledOnce).to.be.true; }); }); describe('getData', function () { it('should return data in proper format', function () { - makeSlot({code: '/reconciliationAdunit1', divId: 'reconciliationAd1'}); + makeSlot({ code: '/reconciliationAdunit1', divId: 'reconciliationAd1' }); const targetingData = reconciliationSubmodule.getTargetingData(['/reconciliationAdunit1']); expect(targetingData['/reconciliationAdunit1'].RSDK_AUID).to.eql('/reconciliationAdunit1'); @@ -59,7 +59,7 @@ describe('Reconciliation Real time data submodule', function () { }); it('should return unit path if called with divId', function () { - makeSlot({code: '/reconciliationAdunit2', divId: 'reconciliationAd2'}); + makeSlot({ code: '/reconciliationAdunit2', divId: 'reconciliationAd2' }); const targetingData = reconciliationSubmodule.getTargetingData(['reconciliationAd2']); expect(targetingData['reconciliationAd2'].RSDK_AUID).to.eql('/reconciliationAdunit2'); @@ -67,7 +67,7 @@ describe('Reconciliation Real time data submodule', function () { }); it('should skip empty adUnit id', function () { - makeSlot({code: '/reconciliationAdunit3', divId: 'reconciliationAd3'}); + makeSlot({ code: '/reconciliationAdunit3', divId: 'reconciliationAd3' }); const targetingData = reconciliationSubmodule.getTargetingData(['reconciliationAd3', '']); expect(targetingData).to.have.all.keys('reconciliationAd3'); @@ -134,7 +134,7 @@ describe('Reconciliation Real time data submodule', function () { adSlotElement.appendChild(adSlotIframe); document.body.appendChild(adSlotElement); - const adSlot = makeSlot({code: '/reconciliationAdunit', divId: adSlotElement.id}); + const adSlot = makeSlot({ code: '/reconciliationAdunit', divId: adSlotElement.id }); expect(getSlotByWin(adSlotIframe.contentWindow)).to.eql(adSlot); }); @@ -147,7 +147,7 @@ describe('Reconciliation Real time data submodule', function () { document.body.appendChild(adSlotElement); document.body.appendChild(adSlotIframe); // iframe is not in ad slot - const adSlot = makeSlot({code: '/reconciliationAdunit', divId: adSlotElement.id}); + const adSlot = makeSlot({ code: '/reconciliationAdunit', divId: adSlotElement.id }); expect(getSlotByWin(adSlotIframe.contentWindow)).to.be.null; }); @@ -162,7 +162,7 @@ describe('Reconciliation Real time data submodule', function () { adSlotElement.appendChild(adSlotIframe); document.body.appendChild(adSlotElement); - const adSlot = makeSlot({code: '/reconciliationAdunit', divId: adSlotElement.id}); + const adSlot = makeSlot({ code: '/reconciliationAdunit', divId: adSlotElement.id }); // Fix targeting methods adSlot.targeting = {}; adSlot.setTargeting = function(key, value) { diff --git a/test/spec/modules/redtramBidAdapter_spec.js b/test/spec/modules/redtramBidAdapter_spec.js index 45d2b08a51f..f029a2e22a2 100644 --- a/test/spec/modules/redtramBidAdapter_spec.js +++ b/test/spec/modules/redtramBidAdapter_spec.js @@ -1,5 +1,5 @@ -import {expect} from 'chai'; -import {spec} from '../../../modules/redtramBidAdapter.js'; +import { expect } from 'chai'; +import { spec } from '../../../modules/redtramBidAdapter.js'; import { BANNER } from '../../../src/mediaTypes.js'; import * as utils from '../../../src/utils.js'; diff --git a/test/spec/modules/relaidoBidAdapter_spec.js b/test/spec/modules/relaidoBidAdapter_spec.js index da3584a2de0..85ef22bbf35 100644 --- a/test/spec/modules/relaidoBidAdapter_spec.js +++ b/test/spec/modules/relaidoBidAdapter_spec.js @@ -1,8 +1,8 @@ -import {expect} from 'chai'; -import {spec} from 'modules/relaidoBidAdapter.js'; +import { expect } from 'chai'; +import { spec } from 'modules/relaidoBidAdapter.js'; import * as utils from 'src/utils.js'; -import {VIDEO} from 'src/mediaTypes.js'; -import {getCoreStorageManager} from '../../../src/storageManager.js'; +import { VIDEO } from 'src/mediaTypes.js'; +import { getCoreStorageManager } from '../../../src/storageManager.js'; import * as mockGpt from '../integration/faker/googletag.js'; const UUID_KEY = 'relaido_uuid'; @@ -350,7 +350,7 @@ describe('RelaidoAdapter', function () { const data = JSON.parse(bidRequests.data); expect(data.bids).to.have.lengthOf(1); const request = data.bids[0]; - expect(request.pagekvt).to.deep.equal({testkey: ['testvalue']}); + expect(request.pagekvt).to.deep.equal({ testkey: ['testvalue'] }); }); it('should get canonicalUrl (ogUrl:true)', function () { @@ -483,7 +483,7 @@ describe('RelaidoAdapter', function () { describe('spec.getUserSyncs', function () { it('should choose iframe sync urls', function () { - const userSyncs = spec.getUserSyncs({iframeEnabled: true}, [serverResponse]); + const userSyncs = spec.getUserSyncs({ iframeEnabled: true }, [serverResponse]); expect(userSyncs).to.deep.equal([{ type: 'iframe', url: serverResponse.body.syncUrl + '?uu=hogehoge' @@ -491,7 +491,7 @@ describe('RelaidoAdapter', function () { }); it('should choose iframe sync urls if serverResponse are empty', function () { - const userSyncs = spec.getUserSyncs({iframeEnabled: true}, []); + const userSyncs = spec.getUserSyncs({ iframeEnabled: true }, []); expect(userSyncs).to.deep.equal([{ type: 'iframe', url: 'https://api.relaido.jp/tr/v1/prebid/sync.html?uu=hogehoge' @@ -500,7 +500,7 @@ describe('RelaidoAdapter', function () { it('should choose iframe sync urls if syncUrl are undefined', function () { serverResponse.body.syncUrl = undefined; - const userSyncs = spec.getUserSyncs({iframeEnabled: true}, [serverResponse]); + const userSyncs = spec.getUserSyncs({ iframeEnabled: true }, [serverResponse]); expect(userSyncs).to.deep.equal([{ type: 'iframe', url: 'https://api.relaido.jp/tr/v1/prebid/sync.html?uu=hogehoge' @@ -508,7 +508,7 @@ describe('RelaidoAdapter', function () { }); it('should return empty if iframeEnabled are false', function () { - const userSyncs = spec.getUserSyncs({iframeEnabled: false}, [serverResponse]); + const userSyncs = spec.getUserSyncs({ iframeEnabled: false }, [serverResponse]); expect(userSyncs).to.have.lengthOf(0); }); }); diff --git a/test/spec/modules/relevadRtdProvider_spec.js b/test/spec/modules/relevadRtdProvider_spec.js index 6902d910f13..2b9e84c3e11 100644 --- a/test/spec/modules/relevadRtdProvider_spec.js +++ b/test/spec/modules/relevadRtdProvider_spec.js @@ -1,9 +1,9 @@ import { addRtdData, getBidRequestData, relevadSubmodule, serverData } from 'modules/relevadRtdProvider.js'; import { server } from 'test/mocks/xhr.js'; -import {config} from 'src/config.js'; +import { config } from 'src/config.js'; import { deepClone, deepAccess, deepSetValue } from '../../../src/utils.js'; -const responseHeader = {'Content-Type': 'application/json'}; +const responseHeader = { 'Content-Type': 'application/json' }; const moduleConfigCommon = { 'dryrun': true, @@ -119,7 +119,7 @@ describe('relevadRtdProvider', function() { expect(reqBids.adUnits[0].bids[3].params || {}).to.not.have.deep.property('target', 'relevad_rtd=segment1;relevad_rtd=segment2;relevad_rtd=category3'); expect(reqBids.adUnits[0].bids[5].ortb2?.user?.ext?.data || {}).to.not.have.deep.property('segments', ['segment1', 'segment2']); expect(reqBids.adUnits[0].bids[5].ortb2?.user?.ext?.data || {}).to.not.have.deep.property('contextual_categories', ['category3']); - expect(reqBids.adUnits[0].bids[5].ortb2?.user?.ext?.data || {}).to.not.have.deep.property('contextual_categories', {'0': 'category3'}); + expect(reqBids.adUnits[0].bids[5].ortb2?.user?.ext?.data || {}).to.not.have.deep.property('contextual_categories', { '0': 'category3' }); expect(reqBids.ortb2Fragments?.bidder?.rubicon?.user?.ext?.data || {}).to.not.have.deep.property('relevad_rtd', ['segment1', 'segment2']); expect(config.getConfig('ix.firstPartyData') || {}).to.not.have.deep.property('relevad_rtd', ['segment1', 'segment2', 'category3']); }); @@ -139,7 +139,7 @@ describe('relevadRtdProvider', function() { const reqBids = { 'timeout': 10000, 'adUnits': deepClone(adUnitsCommon), - 'adUnitCodes': [ '/19968336/header-bid-tag-0' ], + 'adUnitCodes': ['/19968336/header-bid-tag-0'], 'ortb2Fragments': { 'global': { 'site': { @@ -165,7 +165,7 @@ describe('relevadRtdProvider', function() { const data = { segments: ['segment1', 'segment2'], - cats: {'category3': 100} + cats: { 'category3': 100 } }; (config.getConfig('ix') || {}).firstPartyData = null; addRtdData(reqBids, data, moduleConfig, () => {}); @@ -200,7 +200,7 @@ describe('relevadRtdProvider', function() { const data = { segments: ['segment1', 'segment2'], - cats: {'category3': 100} + cats: { 'category3': 100 } }; getBidRequestData(reqBidsConfigObj, () => {}, moduleConfig, {}); @@ -225,7 +225,7 @@ describe('Process auction end data', function() { { 'code': '/19968336/header-bid-tag-0', 'mediaTypes': { - 'banner': { 'sizes': [ [ 728, 90 ] ] } + 'banner': { 'sizes': [[728, 90]] } }, 'bids': [ { @@ -233,16 +233,16 @@ describe('Process auction end data', function() { 'params': { 'placementId': '13144370', 'keywords': { - 'relevad_rtd': [ 'IAB410-391', 'IAB63-53' ] + 'relevad_rtd': ['IAB410-391', 'IAB63-53'] } } } ], - 'ortb2Imp': { 'ext': { 'data': { 'relevad_rtd': [ 'IAB410-391', 'IAB63-53' ] }, } }, - 'sizes': [ [ 728, 90 ] ], + 'ortb2Imp': { 'ext': { 'data': { 'relevad_rtd': ['IAB410-391', 'IAB63-53'] }, } }, + 'sizes': [[728, 90]], } ], - 'adUnitCodes': [ '/19968336/header-bid-tag-0' ], + 'adUnitCodes': ['/19968336/header-bid-tag-0'], 'bidderRequests': [ { 'bidderCode': 'appnexus', @@ -261,11 +261,11 @@ describe('Process auction end data', function() { } }, 'ortb2Imp': { - 'ext': { 'data': { 'relevad_rtd': [ 'IAB410-391', 'IAB63-53' ] }, } + 'ext': { 'data': { 'relevad_rtd': ['IAB410-391', 'IAB63-53'] }, } }, - 'mediaTypes': { 'banner': { 'sizes': [ [ 728, 90 ] ] } }, + 'mediaTypes': { 'banner': { 'sizes': [[728, 90]] } }, 'adUnitCode': '/19968336/header-bid-tag-0', - 'sizes': [ [ 728, 90 ] ], + 'sizes': [[728, 90]], 'bidId': '20f0b347b07f94', 'bidderRequestId': '1d917281b2bf6c', 'auctionId': 'f7ec9895-5809-475e-8fef-49cbc221921a', @@ -278,9 +278,9 @@ describe('Process auction end data', function() { 'page': 'http://www.localhost.localdomain:8888/integrationExamples/gpt/relevadRtdProvider_example.html', 'domain': 'localhost.localdomain:8888', 'publisher': { 'domain': 'localhost.localdomain:8888' }, - 'cat': [ 'IAB410-391', 'IAB63-53' ], - 'pagecat': [ 'IAB410-391', 'IAB63-53' ], - 'sectioncat': [ 'IAB410-391', 'IAB63-53' ] + 'cat': ['IAB410-391', 'IAB63-53'], + 'pagecat': ['IAB410-391', 'IAB63-53'], + 'sectioncat': ['IAB410-391', 'IAB63-53'] }, 'device': { 'w': 326, @@ -310,7 +310,7 @@ describe('Process auction end data', function() { 'reachedTop': true, 'isAmp': false, 'numIframes': 0, - 'stack': [ 'http://www.localhost.localdomain:8888/integrationExamples/gpt/relevadRtdProvider_example.html' ], + 'stack': ['http://www.localhost.localdomain:8888/integrationExamples/gpt/relevadRtdProvider_example.html'], 'referer': 'http://www.localhost.localdomain:8888/integrationExamples/gpt/relevadRtdProvider_example.html', 'canonicalUrl': null } @@ -320,9 +320,9 @@ describe('Process auction end data', function() { 'page': 'http://www.localhost.localdomain:8888/integrationExamples/gpt/relevadRtdProvider_example.html', 'domain': 'localhost.localdomain:8888', 'publisher': { 'domain': 'localhost.localdomain:8888' }, - 'cat': [ 'IAB410-391', 'IAB63-53' ], - 'pagecat': [ 'IAB410-391', 'IAB63-53' ], - 'sectioncat': [ 'IAB410-391', 'IAB63-53' ] + 'cat': ['IAB410-391', 'IAB63-53'], + 'pagecat': ['IAB410-391', 'IAB63-53'], + 'sectioncat': ['IAB410-391', 'IAB63-53'] }, 'device': { 'w': 326, diff --git a/test/spec/modules/relevantdigitalBidAdapter_spec.js b/test/spec/modules/relevantdigitalBidAdapter_spec.js index 45a84d5991d..ee9bea32ad3 100644 --- a/test/spec/modules/relevantdigitalBidAdapter_spec.js +++ b/test/spec/modules/relevantdigitalBidAdapter_spec.js @@ -1,4 +1,4 @@ -import {spec, resetBidderConfigs} from 'modules/relevantdigitalBidAdapter.js'; +import { spec, resetBidderConfigs } from 'modules/relevantdigitalBidAdapter.js'; import { parseUrl } from 'src/utils.js'; const expect = require('chai').expect; @@ -228,7 +228,7 @@ const resetAndBuildRequest = (params) => { describe('Relevant Digital Bid Adaper', function () { describe('buildRequests', () => { const [request] = resetAndBuildRequest(); - const {data, url} = request + const { data, url } = request it('should give the correct URL', () => { expect(url).equal(`https://${PBS_HOST}/openrtb2/auction`); }); @@ -292,7 +292,7 @@ describe('Relevant Digital Bid Adaper', function () { const responseSyncs = BID_RESPONSE.ext.relevant.sync; const allSyncs = spec.getUserSyncs({ pixelEnabled: true }, [{ body: BID_RESPONSE }], null, null); it('should return one sync object per pixel', () => { - const expectedResult = responseSyncs.map(({ url }) => ({url, type: 'image'})); + const expectedResult = responseSyncs.map(({ url }) => ({ url, type: 'image' })); expect(allSyncs).to.deep.equal(expectedResult) }); }); diff --git a/test/spec/modules/resetdigitalBidAdapter_spec.js b/test/spec/modules/resetdigitalBidAdapter_spec.js index d010ee86556..0cc2663fdbc 100644 --- a/test/spec/modules/resetdigitalBidAdapter_spec.js +++ b/test/spec/modules/resetdigitalBidAdapter_spec.js @@ -82,7 +82,7 @@ describe('resetdigitalBidAdapter', function () { }) describe('buildRequests', function () { - const req = spec.buildRequests([ bannerRequest ], { refererInfo: { } }) + const req = spec.buildRequests([bannerRequest], { refererInfo: { } }) let rdata it('should return request object', function () { diff --git a/test/spec/modules/retailspotBidAdapter_spec.js b/test/spec/modules/retailspotBidAdapter_spec.js index 7e693c7973d..eba905c54d2 100644 --- a/test/spec/modules/retailspotBidAdapter_spec.js +++ b/test/spec/modules/retailspotBidAdapter_spec.js @@ -18,8 +18,8 @@ describe('RetailSpot Adapter', function () { consentString: consentString, gdprApplies: true }, - refererInfo: {location: referrerUrl, canonicalUrl, domain, topmostLocation: 'fakePageURL'}, - ortb2: {site: {page: pageUrl, ref: referrerUrl}} + refererInfo: { location: referrerUrl, canonicalUrl, domain, topmostLocation: 'fakePageURL' }, + ortb2: { site: { page: pageUrl, ref: referrerUrl } } }; const bidRequestWithSinglePlacement = [ @@ -83,9 +83,9 @@ describe('RetailSpot Adapter', function () { }, 'sizes': '300x250', 'mediaTypes': - { 'banner': - {'sizes': ['300x250'] - } + { + 'banner': + { 'sizes': ['300x250'] } }, 'transactionId': 'bid_id_0_transaction_id' } @@ -101,9 +101,9 @@ describe('RetailSpot Adapter', function () { }, 'sizes': '300x250', 'mediaTypes': - { 'banner': - {'sizes': ['300x250'] - } + { + 'banner': + { 'sizes': ['300x250'] } }, 'transactionId': 'bid_id_0_transaction_id' }, @@ -116,9 +116,9 @@ describe('RetailSpot Adapter', function () { }, 'sizes': [[300, 600]], 'mediaTypes': - { 'banner': - {'sizes': ['300x600'] - } + { + 'banner': + { 'sizes': ['300x600'] } }, 'transactionId': 'bid_id_1_transaction_id' }, @@ -388,7 +388,7 @@ describe('RetailSpot Adapter', function () { it('receive reponse with single placement', function () { serverResponse.body = responseWithSinglePlacement; - const result = spec.interpretResponse(serverResponse, {data: '{"bids":' + JSON.stringify(requestDataOnePlacement) + '}'}); + const result = spec.interpretResponse(serverResponse, { data: '{"bids":' + JSON.stringify(requestDataOnePlacement) + '}' }); expect(result.length).to.equal(1); expect(result[0].cpm).to.equal(0.5); @@ -400,7 +400,7 @@ describe('RetailSpot Adapter', function () { it('receive reponse with multiple placement', function () { serverResponse.body = responseWithMultiplePlacements; - const result = spec.interpretResponse(serverResponse, {data: '{"bids":' + JSON.stringify(requestDataMultiPlacement) + '}'}); + const result = spec.interpretResponse(serverResponse, { data: '{"bids":' + JSON.stringify(requestDataMultiPlacement) + '}' }); expect(result.length).to.equal(2); @@ -417,7 +417,7 @@ describe('RetailSpot Adapter', function () { it('receive Vast reponse with Video ad', function () { serverResponse.body = responseWithSingleVideo; - const result = spec.interpretResponse(serverResponse, {data: '{"bids":' + JSON.stringify(sentBidVideo) + '}'}); + const result = spec.interpretResponse(serverResponse, { data: '{"bids":' + JSON.stringify(sentBidVideo) + '}' }); expect(result.length).to.equal(1); expect(result).to.deep.equal(videoResult); diff --git a/test/spec/modules/revantageBidAdapter_spec.js b/test/spec/modules/revantageBidAdapter_spec.js new file mode 100644 index 00000000000..b560c218bfd --- /dev/null +++ b/test/spec/modules/revantageBidAdapter_spec.js @@ -0,0 +1,994 @@ +import { expect } from 'chai'; +import sinon from 'sinon'; +import { spec } from '../../../modules/revantageBidAdapter.js'; +import { newBidder } from '../../../src/adapters/bidderFactory.js'; +import { deepClone } from '../../../src/utils.js'; +import { BANNER, VIDEO } from '../../../src/mediaTypes.js'; +import * as utils from '../../../src/utils.js'; + +const ENDPOINT_URL = 'https://bid.revantage.io/bid'; +const SYNC_URL = 'https://sync.revantage.io/sync'; + +describe('RevantageBidAdapter', 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 () { + const validBid = { + bidder: 'revantage', + params: { + feedId: 'test-feed-123' + }, + 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(validBid)).to.equal(true); + }); + + it('should return false when bid is undefined', function () { + expect(spec.isBidRequestValid(undefined)).to.equal(false); + }); + + it('should return false when bid is null', function () { + expect(spec.isBidRequestValid(null)).to.equal(false); + }); + + it('should return false when params is missing', function () { + const invalidBid = deepClone(validBid); + delete invalidBid.params; + expect(spec.isBidRequestValid(invalidBid)).to.equal(false); + }); + + it('should return false when feedId is missing', function () { + const invalidBid = deepClone(validBid); + invalidBid.params = {}; + expect(spec.isBidRequestValid(invalidBid)).to.equal(false); + }); + + it('should return false when feedId is empty string', function () { + const invalidBid = deepClone(validBid); + invalidBid.params = { feedId: '' }; + expect(spec.isBidRequestValid(invalidBid)).to.equal(false); + }); + + it('should return true with optional params', function () { + const bidWithOptional = deepClone(validBid); + bidWithOptional.params.placementId = 'test-placement'; + bidWithOptional.params.publisherId = 'test-publisher'; + expect(spec.isBidRequestValid(bidWithOptional)).to.equal(true); + }); + }); + + describe('buildRequests', function () { + const validBidRequests = [{ + bidder: 'revantage', + params: { + feedId: 'test-feed-123', + placementId: 'test-placement', + publisherId: 'test-publisher' + }, + adUnitCode: 'adunit-code', + sizes: [[300, 250], [300, 600]], + bidId: '30b31c1838de1e', + bidderRequestId: '22edbae2733bf6', + auctionId: '1d1a030790a475', + mediaTypes: { + banner: { + sizes: [[300, 250], [300, 600]] + } + }, + getFloor: function(params) { + return { + currency: 'USD', + floor: 0.5 + }; + } + }]; + + const bidderRequest = { + auctionId: '1d1a030790a475', + bidderRequestId: '22edbae2733bf6', + timeout: 3000, + gdprConsent: { + consentString: 'BOJ/P2HOJ/P2HABABMAAAAAZ+A==', + gdprApplies: true + }, + uspConsent: '1---', + gppConsent: { + gppString: 'DBACNYA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA', + applicableSections: [7, 8] + }, + ortb2: { + site: { + domain: 'example.com', + page: 'https://example.com/test' + }, + device: { + ua: 'Mozilla/5.0...', + language: 'en' + } + } + }; + + it('should return valid request object', function () { + const request = spec.buildRequests(validBidRequests, bidderRequest); + + expect(request).to.be.an('object'); + expect(request.method).to.equal('POST'); + expect(request.url).to.include(ENDPOINT_URL); + expect(request.url).to.include('feed=test-feed-123'); + expect(request.options.contentType).to.equal('text/plain'); + expect(request.options.withCredentials).to.equal(false); + expect(request.bidRequests).to.equal(validBidRequests); + }); + + it('should include all required OpenRTB fields', function () { + const request = spec.buildRequests(validBidRequests, bidderRequest); + const data = JSON.parse(request.data); + + expect(data.id).to.equal('1d1a030790a475'); + expect(data.imp).to.be.an('array').with.lengthOf(1); + expect(data.site).to.be.an('object'); + expect(data.device).to.be.an('object'); + expect(data.user).to.be.an('object'); + expect(data.regs).to.be.an('object'); + expect(data.cur).to.deep.equal(['USD']); + expect(data.tmax).to.equal(3000); + }); + + it('should build correct impression object', function () { + const request = spec.buildRequests(validBidRequests, bidderRequest); + const data = JSON.parse(request.data); + const imp = data.imp[0]; + + expect(imp.id).to.equal('30b31c1838de1e'); + expect(imp.tagid).to.equal('adunit-code'); + expect(imp.bidfloor).to.equal(0.5); + expect(imp.banner).to.be.an('object'); + expect(imp.banner.w).to.equal(300); + expect(imp.banner.h).to.equal(250); + expect(imp.banner.format).to.deep.equal([ + { w: 300, h: 250 }, + { w: 300, h: 600 } + ]); + }); + + it('should include bidder-specific ext parameters', function () { + const request = spec.buildRequests(validBidRequests, bidderRequest); + const data = JSON.parse(request.data); + const imp = data.imp[0]; + + expect(imp.ext.feedId).to.equal('test-feed-123'); + expect(imp.ext.bidder.placementId).to.equal('test-placement'); + expect(imp.ext.bidder.publisherId).to.equal('test-publisher'); + }); + + it('should include GDPR consent data', function () { + const request = spec.buildRequests(validBidRequests, bidderRequest); + const data = JSON.parse(request.data); + + expect(data.regs.ext.gdpr).to.equal(1); + expect(data.user.ext.consent).to.equal('BOJ/P2HOJ/P2HABABMAAAAAZ+A=='); + }); + + it('should include CCPA/USP consent', function () { + const request = spec.buildRequests(validBidRequests, bidderRequest); + const data = JSON.parse(request.data); + + expect(data.regs.ext.us_privacy).to.equal('1---'); + }); + + it('should include GPP consent with sections as array', function () { + const request = spec.buildRequests(validBidRequests, bidderRequest); + const data = JSON.parse(request.data); + + expect(data.regs.ext.gpp).to.equal('DBACNYA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA'); + expect(data.regs.ext.gpp_sid).to.deep.equal([7, 8]); + }); + + it('should handle GDPR not applies', function () { + const bidderRequestNoGdpr = deepClone(bidderRequest); + bidderRequestNoGdpr.gdprConsent.gdprApplies = false; + + const request = spec.buildRequests(validBidRequests, bidderRequestNoGdpr); + const data = JSON.parse(request.data); + + expect(data.regs.ext.gdpr).to.equal(0); + }); + + it('should handle missing getFloor function', function () { + const bidRequestsWithoutFloor = deepClone(validBidRequests); + delete bidRequestsWithoutFloor[0].getFloor; + + const request = spec.buildRequests(bidRequestsWithoutFloor, bidderRequest); + const data = JSON.parse(request.data); + + expect(data.imp[0].bidfloor).to.equal(0); + }); + + it('should handle getFloor returning non-USD currency', function () { + const bidRequestsEurFloor = deepClone(validBidRequests); + bidRequestsEurFloor[0].getFloor = function() { + return { currency: 'EUR', floor: 0.5 }; + }; + + const request = spec.buildRequests(bidRequestsEurFloor, bidderRequest); + const data = JSON.parse(request.data); + + expect(data.imp[0].bidfloor).to.equal(0); + }); + + it('should handle missing ortb2 data', function () { + const bidderRequestNoOrtb2 = deepClone(bidderRequest); + delete bidderRequestNoOrtb2.ortb2; + + const request = spec.buildRequests(validBidRequests, bidderRequestNoOrtb2); + const data = JSON.parse(request.data); + + expect(data.site).to.be.an('object'); + expect(data.site.domain).to.exist; + expect(data.device).to.be.an('object'); + }); + + it('should include supply chain when present in bidderRequest', function () { + const bidderRequestWithSchain = deepClone(bidderRequest); + bidderRequestWithSchain.schain = { + ver: '1.0', + complete: 1, + nodes: [{ + asi: 'example.com', + sid: '12345', + hp: 1 + }] + }; + + const request = spec.buildRequests(validBidRequests, bidderRequestWithSchain); + const data = JSON.parse(request.data); + + expect(data.schain).to.exist; + expect(data.schain.ver).to.equal('1.0'); + expect(data.schain.complete).to.equal(1); + expect(data.schain.nodes).to.have.lengthOf(1); + }); + + it('should include supply chain from first bid request', function () { + const bidRequestsWithSchain = deepClone(validBidRequests); + bidRequestsWithSchain[0].schain = { + ver: '1.0', + complete: 1, + nodes: [{ asi: 'bidder.com', sid: '999', hp: 1 }] + }; + + const bidderRequestNoSchain = deepClone(bidderRequest); + delete bidderRequestNoSchain.schain; + + const request = spec.buildRequests(bidRequestsWithSchain, bidderRequestNoSchain); + const data = JSON.parse(request.data); + + expect(data.schain).to.exist; + expect(data.schain.nodes[0].asi).to.equal('bidder.com'); + }); + + it('should include user EIDs when present', function () { + const bidRequestsWithEids = deepClone(validBidRequests); + bidRequestsWithEids[0].userIdAsEids = [ + { + source: 'id5-sync.com', + uids: [{ id: 'test-id5-id', atype: 1 }] + } + ]; + + const request = spec.buildRequests(bidRequestsWithEids, bidderRequest); + const data = JSON.parse(request.data); + + expect(data.user.eids).to.be.an('array'); + expect(data.user.eids[0].source).to.equal('id5-sync.com'); + }); + + it('should return empty array when feedIds differ across bids', function () { + const mixedFeedBidRequests = [ + { + bidder: 'revantage', + params: { feedId: 'feed-1' }, + adUnitCode: 'adunit-1', + mediaTypes: { banner: { sizes: [[300, 250]] } }, + sizes: [[300, 250]], + bidId: 'bid1', + bidderRequestId: '22edbae2733bf6', + auctionId: '1d1a030790a475' + }, + { + bidder: 'revantage', + params: { feedId: 'feed-2' }, + adUnitCode: 'adunit-2', + mediaTypes: { banner: { sizes: [[728, 90]] } }, + sizes: [[728, 90]], + bidId: 'bid2', + bidderRequestId: '22edbae2733bf6', + auctionId: '1d1a030790a475' + } + ]; + + const request = spec.buildRequests(mixedFeedBidRequests, bidderRequest); + expect(request).to.deep.equal([]); + }); + + it('should return empty array on exception', function () { + const request = spec.buildRequests(null, bidderRequest); + expect(request).to.deep.equal([]); + }); + + it('should handle video media type', function () { + const videoBidRequests = [{ + bidder: 'revantage', + params: { feedId: 'test-feed-123' }, + adUnitCode: 'video-adunit', + bidId: 'video-bid-1', + bidderRequestId: '22edbae2733bf6', + auctionId: '1d1a030790a475', + mediaTypes: { + video: { + playerSize: [[640, 480]], + mimes: ['video/mp4', 'video/webm'], + protocols: [2, 3, 5, 6], + api: [1, 2], + placement: 1, + minduration: 5, + maxduration: 30, + skip: 1, + skipmin: 5, + skipafter: 5 + } + } + }]; + + const request = spec.buildRequests(videoBidRequests, bidderRequest); + const data = JSON.parse(request.data); + const imp = data.imp[0]; + + expect(imp.video).to.exist; + expect(imp.video.w).to.equal(640); + expect(imp.video.h).to.equal(480); + expect(imp.video.mimes).to.deep.equal(['video/mp4', 'video/webm']); + expect(imp.video.protocols).to.deep.equal([2, 3, 5, 6]); + expect(imp.video.minduration).to.equal(5); + expect(imp.video.maxduration).to.equal(30); + expect(imp.video.skip).to.equal(1); + expect(imp.banner).to.be.undefined; + }); + + it('should handle multi-format (banner + video) bid', function () { + const multiFormatBidRequests = [{ + bidder: 'revantage', + params: { feedId: 'test-feed-123' }, + adUnitCode: 'multi-format-adunit', + bidId: 'multi-bid-1', + bidderRequestId: '22edbae2733bf6', + auctionId: '1d1a030790a475', + mediaTypes: { + banner: { + sizes: [[300, 250]] + }, + video: { + playerSize: [[640, 480]], + mimes: ['video/mp4'] + } + } + }]; + + const request = spec.buildRequests(multiFormatBidRequests, bidderRequest); + const data = JSON.parse(request.data); + const imp = data.imp[0]; + + expect(imp.banner).to.exist; + expect(imp.video).to.exist; + }); + + it('should handle multiple impressions with same feedId', function () { + const multipleBidRequests = [ + { + bidder: 'revantage', + params: { feedId: 'test-feed-123' }, + adUnitCode: 'adunit-1', + mediaTypes: { banner: { sizes: [[300, 250]] } }, + sizes: [[300, 250]], + bidId: 'bid1', + bidderRequestId: '22edbae2733bf6', + auctionId: '1d1a030790a475' + }, + { + bidder: 'revantage', + params: { feedId: 'test-feed-123' }, + adUnitCode: 'adunit-2', + mediaTypes: { banner: { sizes: [[728, 90]] } }, + sizes: [[728, 90]], + bidId: 'bid2', + bidderRequestId: '22edbae2733bf6', + auctionId: '1d1a030790a475' + } + ]; + + const request = spec.buildRequests(multipleBidRequests, bidderRequest); + const data = JSON.parse(request.data); + + expect(data.imp).to.have.lengthOf(2); + expect(data.imp[0].id).to.equal('bid1'); + expect(data.imp[1].id).to.equal('bid2'); + }); + + it('should use default sizes when sizes array is empty', function () { + const bidWithEmptySizes = [{ + bidder: 'revantage', + params: { feedId: 'test-feed' }, + adUnitCode: 'adunit-code', + mediaTypes: { banner: { sizes: [] } }, + sizes: [], + bidId: '30b31c1838de1e', + bidderRequestId: '22edbae2733bf6', + auctionId: '1d1a030790a475' + }]; + + const request = spec.buildRequests(bidWithEmptySizes, bidderRequest); + const data = JSON.parse(request.data); + + expect(data.imp[0].banner.w).to.equal(300); + expect(data.imp[0].banner.h).to.equal(250); + }); + + it('should include prebid version in ext', function () { + const request = spec.buildRequests(validBidRequests, bidderRequest); + const data = JSON.parse(request.data); + + expect(data.ext).to.exist; + expect(data.ext.prebid).to.exist; + expect(data.ext.prebid.version).to.exist; + }); + }); + + describe('interpretResponse', function () { + const serverResponse = { + body: { + id: '1d1a030790a475', + seatbid: [{ + seat: 'test-dsp', + bid: [{ + id: 'test-bid-id', + impid: '30b31c1838de1e', + price: 1.25, + crid: 'test-creative-123', + adm: '
Test Ad Markup
', + w: 300, + h: 250, + adomain: ['advertiser.com'], + dealid: 'deal-123' + }] + }], + cur: 'USD' + } + }; + + const bidRequest = { + bidRequests: [{ + bidId: '30b31c1838de1e', + adUnitCode: 'adunit-code', + mediaTypes: { + banner: { + sizes: [[300, 250]] + } + } + }] + }; + + it('should return valid banner bid response', function () { + const result = spec.interpretResponse(serverResponse, bidRequest); + + expect(result).to.be.an('array').with.lengthOf(1); + + const bid = result[0]; + expect(bid.requestId).to.equal('30b31c1838de1e'); + expect(bid.cpm).to.equal(1.25); + expect(bid.width).to.equal(300); + expect(bid.height).to.equal(250); + expect(bid.creativeId).to.equal('test-creative-123'); + expect(bid.currency).to.equal('USD'); + expect(bid.netRevenue).to.equal(true); + expect(bid.ttl).to.equal(300); + expect(bid.ad).to.equal('
Test Ad Markup
'); + expect(bid.mediaType).to.equal(BANNER); + expect(bid.dealId).to.equal('deal-123'); + }); + + it('should include meta data in bid response', function () { + const result = spec.interpretResponse(serverResponse, bidRequest); + const bid = result[0]; + + expect(bid.meta).to.be.an('object'); + expect(bid.meta.advertiserDomains).to.deep.equal(['advertiser.com']); + expect(bid.meta.dsp).to.equal('test-dsp'); + expect(bid.meta.networkName).to.equal('Revantage'); + }); + + it('should include burl when provided', function () { + const responseWithBurl = deepClone(serverResponse); + responseWithBurl.body.seatbid[0].bid[0].burl = 'https://bid.revantage.io/win?auction=1d1a030790a475&dsp=test-dsp&price=0.625000&impid=30b31c1838de1e&bidid=test-bid-id&adid=test-creative-123&page=&domain=&country=&feedid=test-feed&ref='; + + const result = spec.interpretResponse(responseWithBurl, bidRequest); + const bid = result[0]; + + expect(bid.burl).to.include('https://bid.revantage.io/win'); + expect(bid.burl).to.include('dsp=test-dsp'); + expect(bid.burl).to.include('impid=30b31c1838de1e'); + }); + + it('should handle video response with vastXml', function () { + const videoResponse = deepClone(serverResponse); + videoResponse.body.seatbid[0].bid[0].vastXml = '...'; + delete videoResponse.body.seatbid[0].bid[0].adm; + + const videoBidRequest = { + bidRequests: [{ + bidId: '30b31c1838de1e', + adUnitCode: 'video-adunit', + mediaTypes: { + video: { + playerSize: [[640, 480]] + } + } + }] + }; + + const result = spec.interpretResponse(videoResponse, videoBidRequest); + const bid = result[0]; + + expect(bid.mediaType).to.equal(VIDEO); + expect(bid.vastXml).to.equal('...'); + }); + + it('should handle video response with vastUrl', function () { + const videoResponse = deepClone(serverResponse); + videoResponse.body.seatbid[0].bid[0].vastUrl = 'https://vast.example.com/vast.xml'; + delete videoResponse.body.seatbid[0].bid[0].adm; + + const videoBidRequest = { + bidRequests: [{ + bidId: '30b31c1838de1e', + adUnitCode: 'video-adunit', + mediaTypes: { + video: { + playerSize: [[640, 480]] + } + } + }] + }; + + const result = spec.interpretResponse(videoResponse, videoBidRequest); + const bid = result[0]; + + expect(bid.mediaType).to.equal(VIDEO); + expect(bid.vastUrl).to.equal('https://vast.example.com/vast.xml'); + }); + + it('should detect video from ext.mediaType', function () { + const videoResponse = deepClone(serverResponse); + videoResponse.body.seatbid[0].bid[0].adm = '...'; + videoResponse.body.seatbid[0].bid[0].ext = { mediaType: 'video' }; + + const result = spec.interpretResponse(videoResponse, bidRequest); + const bid = result[0]; + + expect(bid.mediaType).to.equal(VIDEO); + expect(bid.vastXml).to.equal('...'); + }); + + it('should use default dimensions from bid request when missing in response', function () { + const responseNoDimensions = deepClone(serverResponse); + delete responseNoDimensions.body.seatbid[0].bid[0].w; + delete responseNoDimensions.body.seatbid[0].bid[0].h; + + const result = spec.interpretResponse(responseNoDimensions, bidRequest); + const bid = result[0]; + + expect(bid.width).to.equal(300); + expect(bid.height).to.equal(250); + }); + + it('should include dspPrice from ext when available', function () { + const responseWithDspPrice = deepClone(serverResponse); + responseWithDspPrice.body.seatbid[0].bid[0].ext = { dspPrice: 1.50 }; + + const result = spec.interpretResponse(responseWithDspPrice, bidRequest); + const bid = result[0]; + + expect(bid.meta.dspPrice).to.equal(1.50); + }); + + it('should return empty array for null response body', function () { + const result = spec.interpretResponse({ body: null }, bidRequest); + expect(result).to.deep.equal([]); + }); + + it('should return empty array for undefined response body', function () { + const result = spec.interpretResponse({}, bidRequest); + expect(result).to.deep.equal([]); + }); + + it('should return empty array when seatbid is not an array', function () { + const invalidResponse = { + body: { + id: '1d1a030790a475', + seatbid: 'not-an-array', + cur: 'USD' + } + }; + + const result = spec.interpretResponse(invalidResponse, bidRequest); + expect(result).to.deep.equal([]); + }); + + it('should return empty array for empty seatbid', function () { + const emptyResponse = { + body: { + id: '1d1a030790a475', + seatbid: [], + cur: 'USD' + } + }; + + const result = spec.interpretResponse(emptyResponse, bidRequest); + expect(result).to.deep.equal([]); + }); + + it('should filter out bids with zero price', function () { + const zeroPriceResponse = deepClone(serverResponse); + zeroPriceResponse.body.seatbid[0].bid[0].price = 0; + + const result = spec.interpretResponse(zeroPriceResponse, bidRequest); + expect(result).to.deep.equal([]); + }); + + it('should filter out bids with negative price', function () { + const negativePriceResponse = deepClone(serverResponse); + negativePriceResponse.body.seatbid[0].bid[0].price = -1; + + const result = spec.interpretResponse(negativePriceResponse, bidRequest); + expect(result).to.deep.equal([]); + }); + + it('should filter out bids without ad markup', function () { + const noAdmResponse = deepClone(serverResponse); + delete noAdmResponse.body.seatbid[0].bid[0].adm; + + const result = spec.interpretResponse(noAdmResponse, bidRequest); + expect(result).to.deep.equal([]); + }); + + it('should filter out bids with unknown impid', function () { + const unknownImpidResponse = deepClone(serverResponse); + unknownImpidResponse.body.seatbid[0].bid[0].impid = 'unknown-imp-id'; + + const result = spec.interpretResponse(unknownImpidResponse, bidRequest); + expect(result).to.deep.equal([]); + }); + + it('should handle missing bidRequests in request object', function () { + const result = spec.interpretResponse(serverResponse, {}); + expect(result).to.deep.equal([]); + }); + + it('should handle multiple seatbids', function () { + const multiSeatResponse = deepClone(serverResponse); + multiSeatResponse.body.seatbid.push({ + seat: 'another-dsp', + bid: [{ + id: 'another-bid-id', + impid: 'another-imp-id', + price: 2.00, + crid: 'another-creative', + adm: '
Another Ad
', + w: 728, + h: 90, + adomain: ['another-advertiser.com'] + }] + }); + + const multiBidRequest = { + bidRequests: [ + { + bidId: '30b31c1838de1e', + adUnitCode: 'adunit-code', + mediaTypes: { banner: { sizes: [[300, 250]] } } + }, + { + bidId: 'another-imp-id', + adUnitCode: 'adunit-code-2', + mediaTypes: { banner: { sizes: [[728, 90]] } } + } + ] + }; + + const result = spec.interpretResponse(multiSeatResponse, multiBidRequest); + + expect(result).to.have.lengthOf(2); + expect(result[0].meta.dsp).to.equal('test-dsp'); + expect(result[1].meta.dsp).to.equal('another-dsp'); + }); + + it('should use default currency USD when not specified', function () { + const noCurrencyResponse = deepClone(serverResponse); + delete noCurrencyResponse.body.cur; + + const result = spec.interpretResponse(noCurrencyResponse, bidRequest); + const bid = result[0]; + + expect(bid.currency).to.equal('USD'); + }); + + it('should generate creativeId when crid is missing', function () { + const noCridResponse = deepClone(serverResponse); + delete noCridResponse.body.seatbid[0].bid[0].crid; + + const result = spec.interpretResponse(noCridResponse, bidRequest); + const bid = result[0]; + + expect(bid.creativeId).to.exist; + expect(bid.creativeId).to.satisfy(crid => + crid === 'test-bid-id' || crid.startsWith('revantage-') + ); + }); + + it('should handle empty adomain array', function () { + const noAdomainResponse = deepClone(serverResponse); + delete noAdomainResponse.body.seatbid[0].bid[0].adomain; + + const result = spec.interpretResponse(noAdomainResponse, bidRequest); + const bid = result[0]; + + expect(bid.meta.advertiserDomains).to.deep.equal([]); + }); + + it('should use "unknown" for missing seat', function () { + const noSeatResponse = deepClone(serverResponse); + delete noSeatResponse.body.seatbid[0].seat; + + const result = spec.interpretResponse(noSeatResponse, bidRequest); + const bid = result[0]; + + expect(bid.meta.dsp).to.equal('unknown'); + }); + }); + + describe('getUserSyncs', function () { + const syncOptions = { + iframeEnabled: true, + pixelEnabled: true + }; + + const gdprConsent = { + gdprApplies: true, + consentString: 'BOJ/P2HOJ/P2HABABMAAAAAZ+A==' + }; + + const uspConsent = '1---'; + + const gppConsent = { + gppString: 'DBACNYA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA', + applicableSections: [7, 8] + }; + + it('should return iframe sync when iframe enabled', function () { + const syncs = spec.getUserSyncs( + { iframeEnabled: true, pixelEnabled: false }, + [], + gdprConsent, + uspConsent, + gppConsent + ); + + expect(syncs).to.be.an('array').with.lengthOf(1); + expect(syncs[0].type).to.equal('iframe'); + expect(syncs[0].url).to.include(SYNC_URL); + }); + + it('should return pixel sync when pixel enabled', function () { + const syncs = spec.getUserSyncs( + { iframeEnabled: false, pixelEnabled: true }, + [], + gdprConsent, + uspConsent, + gppConsent + ); + + expect(syncs).to.be.an('array').with.lengthOf(1); + expect(syncs[0].type).to.equal('image'); + expect(syncs[0].url).to.include(SYNC_URL); + expect(syncs[0].url).to.include('tag=img'); + }); + + it('should return both syncs when both enabled', function () { + const syncs = spec.getUserSyncs(syncOptions, [], gdprConsent, uspConsent, gppConsent); + + expect(syncs).to.have.lengthOf(2); + expect(syncs.map(s => s.type)).to.include('iframe'); + expect(syncs.map(s => s.type)).to.include('image'); + }); + + it('should include cache buster parameter', function () { + const syncs = spec.getUserSyncs(syncOptions, [], gdprConsent, uspConsent, gppConsent); + + expect(syncs[0].url).to.include('cb='); + }); + + it('should include GDPR parameters when consent applies', function () { + const syncs = spec.getUserSyncs(syncOptions, [], gdprConsent, uspConsent, gppConsent); + + expect(syncs[0].url).to.include('gdpr=1'); + expect(syncs[0].url).to.include('gdpr_consent=BOJ%2FP2HOJ%2FP2HABABMAAAAAZ%2BA%3D%3D'); + }); + + it('should set gdpr=0 when GDPR does not apply', function () { + const gdprNotApplies = { + gdprApplies: false, + consentString: 'BOJ/P2HOJ/P2HABABMAAAAAZ+A==' + }; + + const syncs = spec.getUserSyncs(syncOptions, [], gdprNotApplies, uspConsent, gppConsent); + + expect(syncs[0].url).to.include('gdpr=0'); + }); + + it('should include USP consent parameter', function () { + const syncs = spec.getUserSyncs(syncOptions, [], gdprConsent, uspConsent, gppConsent); + + expect(syncs[0].url).to.include('us_privacy=1---'); + }); + + it('should include GPP parameters', function () { + const syncs = spec.getUserSyncs(syncOptions, [], gdprConsent, uspConsent, gppConsent); + + expect(syncs[0].url).to.include('gpp='); + expect(syncs[0].url).to.include('gpp_sid=7%2C8'); + }); + + it('should handle missing GDPR consent', function () { + const syncs = spec.getUserSyncs(syncOptions, [], null, uspConsent, gppConsent); + + expect(syncs[0].url).to.not.include('gdpr='); + expect(syncs[0].url).to.not.include('gdpr_consent='); + }); + + it('should handle missing USP consent', function () { + const syncs = spec.getUserSyncs(syncOptions, [], gdprConsent, null, gppConsent); + + expect(syncs[0].url).to.not.include('us_privacy='); + }); + + it('should handle missing GPP consent', function () { + const syncs = spec.getUserSyncs(syncOptions, [], gdprConsent, uspConsent, null); + + expect(syncs[0].url).to.not.include('gpp='); + expect(syncs[0].url).to.not.include('gpp_sid='); + }); + + it('should handle undefined GPP string', function () { + const partialGppConsent = { + applicableSections: [7, 8] + }; + + const syncs = spec.getUserSyncs(syncOptions, [], gdprConsent, uspConsent, partialGppConsent); + + expect(syncs[0].url).to.not.include('gpp='); + expect(syncs[0].url).to.include('gpp_sid=7%2C8'); + }); + + it('should return empty array when no sync options enabled', function () { + const syncs = spec.getUserSyncs( + { iframeEnabled: false, pixelEnabled: false }, + [], + gdprConsent, + uspConsent, + gppConsent + ); + + expect(syncs).to.be.an('array').that.is.empty; + }); + + it('should return empty array when syncOptions is empty object', function () { + const syncs = spec.getUserSyncs({}, [], gdprConsent, uspConsent, gppConsent); + + expect(syncs).to.be.an('array').that.is.empty; + }); + }); + + describe('onBidWon', function () { + let triggerPixelStub; + + beforeEach(function () { + triggerPixelStub = sinon.stub(utils, 'triggerPixel'); + }); + + afterEach(function () { + triggerPixelStub.restore(); + }); + + it('should call triggerPixel with correct burl', function () { + const bid = { + bidId: '30b31c1838de1e', + cpm: 1.25, + adUnitCode: 'adunit-code', + burl: 'https://bid.revantage.io/win?auction=1d1a030790a475&dsp=test-dsp&price=0.625000&impid=30b31c1838de1e&bidid=test-bid-id&adid=test-ad-123&page=https%3A%2F%2Fexample.com&domain=example.com&country=US&feedid=test-feed&ref=' + }; + + spec.onBidWon(bid); + + expect(triggerPixelStub.calledOnce).to.be.true; + expect(triggerPixelStub.firstCall.args[0]).to.include('https://bid.revantage.io/win'); + expect(triggerPixelStub.firstCall.args[0]).to.include('dsp=test-dsp'); + expect(triggerPixelStub.firstCall.args[0]).to.include('impid=30b31c1838de1e'); + expect(triggerPixelStub.firstCall.args[0]).to.include('feedid=test-feed'); + }); + + it('should not throw error when burl is missing', function () { + const bid = { + bidId: '30b31c1838de1e', + cpm: 1.25, + adUnitCode: 'adunit-code' + }; + + expect(() => spec.onBidWon(bid)).to.not.throw(); + expect(triggerPixelStub.called).to.be.false; + }); + + it('should handle burl with all query parameters', function () { + // This is the actual format generated by your RTB server + const burl = 'https://bid.revantage.io/win?' + + 'auction=auction_123456789' + + '&dsp=Improve_Digital' + + '&price=0.750000' + + '&impid=imp_001%7Cfeed123' + // URL encoded pipe for feedId in impid + '&bidid=bid_abc' + + '&adid=creative_xyz' + + '&page=https%3A%2F%2Fexample.com%2Fpage' + + '&domain=example.com' + + '&country=US' + + '&feedid=feed123' + + '&ref=https%3A%2F%2Fgoogle.com'; + + const bid = { + bidId: 'imp_001', + cpm: 1.50, + burl: burl + }; + + spec.onBidWon(bid); + + expect(triggerPixelStub.calledOnce).to.be.true; + const calledUrl = triggerPixelStub.firstCall.args[0]; + expect(calledUrl).to.include('auction=auction_123456789'); + expect(calledUrl).to.include('dsp=Improve_Digital'); + expect(calledUrl).to.include('price=0.750000'); + expect(calledUrl).to.include('domain=example.com'); + expect(calledUrl).to.include('country=US'); + expect(calledUrl).to.include('feedid=feed123'); + }); + }); + + describe('spec properties', function () { + it('should have correct bidder code', function () { + expect(spec.code).to.equal('revantage'); + }); + + it('should support banner and video media types', function () { + expect(spec.supportedMediaTypes).to.deep.equal([BANNER, VIDEO]); + }); + }); +}); diff --git a/test/spec/modules/revcontentBidAdapter_spec.js b/test/spec/modules/revcontentBidAdapter_spec.js index 6d660d1b3b5..7a5da3c7b1d 100644 --- a/test/spec/modules/revcontentBidAdapter_spec.js +++ b/test/spec/modules/revcontentBidAdapter_spec.js @@ -1,6 +1,6 @@ // jshint esversion: 6, es3: false, node: true -import {assert, expect} from 'chai'; -import {spec} from 'modules/revcontentBidAdapter.js'; +import { assert, expect } from 'chai'; +import { spec } from 'modules/revcontentBidAdapter.js'; import { NATIVE } from 'src/mediaTypes.js'; import { config } from 'src/config.js'; import * as utils from 'src/utils.js'; @@ -14,7 +14,7 @@ describe('revcontent adapter', function () { bidder: 'revcontent', nativeParams: {}, params: { - size: {width: 300, height: 250}, + size: { width: 300, height: 250 }, apiKey: '8a33fa9cf220ae685dcc3544f847cdda858d3b1c', userId: 673, domain: 'test.com', @@ -38,18 +38,18 @@ describe('revcontent adapter', function () { bidder: 'revcontent', nativeParams: {}, params: { - size: {width: 300, height: 250}, + size: { width: 300, height: 250 }, apiKey: '8a33fa9cf220ae685dcc3544f847cdda858d3b1c', userId: 673, widgetId: 33861, endpoint: 'trends-s0.revcontent.com' } }]; - let request = spec.buildRequests(validBidRequests, {refererInfo: {page: 'page'}}); + let request = spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } }); request = request[0]; assert.equal(request.method, 'POST'); assert.equal(request.url, 'https://trends-s0.revcontent.com/rtb?apiKey=8a33fa9cf220ae685dcc3544f847cdda858d3b1c&userId=673&widgetId=33861'); - assert.deepEqual(request.options, {contentType: 'application/json'}); + assert.deepEqual(request.options, { contentType: 'application/json' }); assert.ok(request.data); }); @@ -59,14 +59,14 @@ describe('revcontent adapter', function () { bidder: 'revcontent', nativeParams: {}, params: { - size: {width: 300, height: 250}, + size: { width: 300, height: 250 }, apiKey: '8a33fa9cf220ae685dcc3544f847cdda858d3b1c', userId: 673, domain: 'test.com', endpoint: 'trends-s0.revcontent.com' } }]; - let request = spec.buildRequests(validBidRequests, {refererInfo: {page: 'page'}}); + let request = spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } }); request = request[0]; const data = Object.keys(request); @@ -79,7 +79,7 @@ describe('revcontent adapter', function () { bidder: 'revcontent', nativeParams: {}, params: { - size: {width: 300, height: 250}, + size: { width: 300, height: 250 }, apiKey: '8a33fa9cf220ae685dcc3544f847cdda858d3b1c', userId: 673, domain: 'test.com', @@ -87,7 +87,7 @@ describe('revcontent adapter', function () { bidfloor: 0.05 } }]; - let request = spec.buildRequests(validBidRequests, {refererInfo: {page: 'page'}}); + let request = spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } }); request = JSON.parse(request[0].data); assert.equal(request.imp[0].bidfloor, 0.05); assert.equal(request.device.ua, navigator.userAgent); @@ -98,7 +98,7 @@ describe('revcontent adapter', function () { bidder: 'revcontent', nativeParams: {}, params: { - size: {width: 300, height: 250}, + size: { width: 300, height: 250 }, apiKey: '8a33fa9cf220ae685dcc3544f847cdda858d3b1c', userId: 673, domain: 'test.com', @@ -112,7 +112,7 @@ describe('revcontent adapter', function () { currency: 'USD' }; }; - let request = spec.buildRequests(validBidRequests, {refererInfo: {page: 'page'}}); + let request = spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } }); request = JSON.parse(request[0].data); assert.equal(request.imp[0].bidfloor, 0.07); assert.equal(request.device.ua, navigator.userAgent); @@ -139,22 +139,22 @@ describe('revcontent adapter', function () { } }, params: { - size: {width: 300, height: 250}, + size: { width: 300, height: 250 }, apiKey: '8a33fa9cf220ae685dcc3544f847cdda858d3b1c', userId: 673, domain: 'test.com', endpoint: 'trends-s0.revcontent.com' } }]; - const refererInfo = {page: 'page'}; - let request = spec.buildRequests(validBidRequests, {refererInfo}); + const refererInfo = { page: 'page' }; + let request = spec.buildRequests(validBidRequests, { refererInfo }); request = JSON.parse(request[0].data); assert.equal(request.imp[0].bidfloor, 0.1); assert.deepEqual(request.site, { domain: 'test.com', page: 'page', - publisher: {id: 673, domain: 'test.com'} + publisher: { id: 673, domain: 'test.com' } }); }); }); @@ -320,13 +320,13 @@ describe('revcontent adapter', function () { body: { id: null, bidid: null, - seatbid: [{bid: []}], + seatbid: [{ bid: [] }], cur: 'USD' } }; const bidRequest = { data: '{}', - bids: [{bidId: 'bidId1'}] + bids: [{ bidId: 'bidId1' }] }; const result = spec.interpretResponse(serverResponse, bidRequest)[0]; assert.ok(!result); diff --git a/test/spec/modules/revnewBidAdapter_spec.js b/test/spec/modules/revnewBidAdapter_spec.js index 904b59589cb..765a467386f 100644 --- a/test/spec/modules/revnewBidAdapter_spec.js +++ b/test/spec/modules/revnewBidAdapter_spec.js @@ -605,7 +605,7 @@ describe('Revnew bid adapter tests', () => { }); it('Verifies user sync with cookies in bid response', () => { response.body.ext = { - cookies: [{'type': 'image', 'url': 'http://www.cookie.sync.org/'}] + cookies: [{ 'type': 'image', 'url': 'http://www.cookie.sync.org/' }] }; const syncs = spec.getUserSyncs({}, [response], DEFAULT_OPTIONS.gdprConsent); const expectedSyncs = [{ type: 'image', url: 'http://www.cookie.sync.org/' }]; diff --git a/test/spec/modules/rewardedInterestIdSystem_spec.js b/test/spec/modules/rewardedInterestIdSystem_spec.js index b6ce1e03f76..8301944999d 100644 --- a/test/spec/modules/rewardedInterestIdSystem_spec.js +++ b/test/spec/modules/rewardedInterestIdSystem_spec.js @@ -1,8 +1,8 @@ import sinon from 'sinon'; -import {expect} from 'chai'; +import { expect } from 'chai'; import * as utils from 'src/utils.js'; -import {attachIdSystem} from 'modules/userId'; -import {createEidsArray} from 'modules/userId/eids'; +import { attachIdSystem } from 'modules/userId'; +import { createEidsArray } from 'modules/userId/eids'; import { MODULE_NAME, SOURCE, @@ -121,7 +121,7 @@ describe('rewardedInterestIdSystem', () => { it('API is set before getId, getIdentityToken return error', async () => { const error = Error(); - window.__riApi = {getIdentityToken: () => Promise.reject(error)}; + window.__riApi = { getIdentityToken: () => Promise.reject(error) }; const idResponse = rewardedInterestIdSubmodule.getId(); idResponse.callback(callbackSpy); await window.__riApi.getIdentityToken().catch(() => {}); @@ -134,7 +134,7 @@ describe('rewardedInterestIdSystem', () => { mockReadySate = 'loading'; const idResponse = rewardedInterestIdSubmodule.getId(); idResponse.callback(callbackSpy); - window.__riApi = {getIdentityToken: () => Promise.reject(error)}; + window.__riApi = { getIdentityToken: () => Promise.reject(error) }; await window.__riApi.getIdentityToken().catch(() => {}); expect(callbackSpy.calledOnceWithExactly()).to.be.true; expect(logErrorSpy.calledOnceWithExactly(errorIdFetch, error)).to.be.true; diff --git a/test/spec/modules/rhythmoneBidAdapter_spec.js b/test/spec/modules/rhythmoneBidAdapter_spec.js index b9fa0cd37af..ecd21896034 100644 --- a/test/spec/modules/rhythmoneBidAdapter_spec.js +++ b/test/spec/modules/rhythmoneBidAdapter_spec.js @@ -1,6 +1,5 @@ -import {spec} from '../../../modules/rhythmoneBidAdapter.js'; +import { spec } from '../../../modules/rhythmoneBidAdapter.js'; import * as utils from '../../../src/utils.js'; -import * as dnt from 'libraries/dnt/index.js'; import * as sinon from 'sinon'; var r1adapter = spec; @@ -398,7 +397,7 @@ describe('rhythmone adapter tests', function () { 'path': 'mypath' }, 'mediaTypes': { - 'banner': {'sizes': [['400', '500'], ['4n0', '5g0']]} + 'banner': { 'sizes': [['400', '500'], ['4n0', '5g0']] } }, 'adUnitCode': 'div-gpt-ad-1438287399331-0', 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', @@ -414,37 +413,6 @@ describe('rhythmone adapter tests', function () { 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(dnt, '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 to zero', function () { var bidRequestList = [ { diff --git a/test/spec/modules/richaudienceBidAdapter_spec.js b/test/spec/modules/richaudienceBidAdapter_spec.js index fb90c793188..2c7ba990ebc 100644 --- a/test/spec/modules/richaudienceBidAdapter_spec.js +++ b/test/spec/modules/richaudienceBidAdapter_spec.js @@ -1,9 +1,9 @@ // import or require modules necessary for the test, e.g.: -import {expect} from 'chai'; // may prefer 'assert' in place of 'expect' +import { expect } from 'chai'; // may prefer 'assert' in place of 'expect' import { spec } from 'modules/richaudienceBidAdapter.js'; -import {config} from 'src/config.js'; +import { config } from 'src/config.js'; import * as utils from 'src/utils.js'; import sinon from 'sinon'; @@ -378,7 +378,7 @@ describe('Richaudience adapter tests', function () { it('Referer undefined', function () { config.setConfig({ - 'currency': {'adServerCurrency': 'USD'} + 'currency': { 'adServerCurrency': 'USD' } }) const request = spec.buildRequests(DEFAULT_PARAMS_NEW_SIZES, { @@ -724,7 +724,7 @@ describe('Richaudience adapter tests', function () { it('Verifies bidder aliases', function () { expect(spec.aliases).to.have.lengthOf(1); - expect(spec.aliases[0]).to.deep.equal({code: 'ra', gvlid: 108}); + expect(spec.aliases[0]).to.deep.equal({ code: 'ra', gvlid: 108 }); }); it('Verifies bidder gvlid', function () { @@ -940,7 +940,7 @@ describe('Richaudience adapter tests', function () { }); it('Verifies user syncs iframe include', function () { config.setConfig({ - 'userSync': {filterSettings: {iframe: {bidders: '*', filter: 'include'}}} + 'userSync': { filterSettings: { iframe: { bidders: '*', filter: 'include' } } } }) var syncs = spec.getUserSyncs({ @@ -971,17 +971,17 @@ describe('Richaudience adapter tests', function () { syncs = spec.getUserSyncs({ iframeEnabled: true, - }, [], {consentString: '', gdprApplies: false}); + }, [], { consentString: '', gdprApplies: false }); expect(syncs).to.have.lengthOf(1); syncs = spec.getUserSyncs({ iframeEnabled: false, - }, [], {consentString: '', gdprApplies: true}); + }, [], { consentString: '', gdprApplies: true }); expect(syncs).to.have.lengthOf(0); }); it('Verifies user syncs iframe exclude', function () { config.setConfig({ - 'userSync': {filterSettings: {iframe: {bidders: '*', filter: 'exclude'}}} + 'userSync': { filterSettings: { iframe: { bidders: '*', filter: 'exclude' } } } }) var syncs = spec.getUserSyncs({ @@ -1011,18 +1011,18 @@ describe('Richaudience adapter tests', function () { syncs = spec.getUserSyncs({ iframeEnabled: true, - }, [], {consentString: '', gdprApplies: false}); + }, [], { consentString: '', gdprApplies: false }); expect(syncs).to.have.lengthOf(0); syncs = spec.getUserSyncs({ iframeEnabled: false, - }, [], {consentString: '', gdprApplies: true}); + }, [], { consentString: '', gdprApplies: true }); expect(syncs).to.have.lengthOf(0); }); it('Verifies user syncs image include', function () { config.setConfig({ - 'userSync': {filterSettings: {image: {bidders: '*', filter: 'include'}}} + 'userSync': { filterSettings: { image: { bidders: '*', filter: 'include' } } } }) var syncs = spec.getUserSyncs({ @@ -1061,7 +1061,7 @@ describe('Richaudience adapter tests', function () { it('Verifies user syncs image exclude', function () { config.setConfig({ - 'userSync': {filterSettings: {image: {bidders: '*', filter: 'exclude'}}} + 'userSync': { filterSettings: { image: { bidders: '*', filter: 'exclude' } } } }) var syncs = spec.getUserSyncs({ @@ -1099,8 +1099,8 @@ describe('Richaudience adapter tests', function () { config.setConfig({ 'userSync': { filterSettings: { - iframe: {bidders: '*', filter: 'include'}, - image: {bidders: '*', filter: 'include'} + iframe: { bidders: '*', filter: 'include' }, + image: { bidders: '*', filter: 'include' } } } }) @@ -1137,13 +1137,13 @@ describe('Richaudience adapter tests', function () { syncs = spec.getUserSyncs({ iframeEnabled: true, pixelEnabled: true - }, [], {consentString: '', gdprApplies: false}); + }, [], { consentString: '', gdprApplies: false }); expect(syncs).to.have.lengthOf(1); syncs = spec.getUserSyncs({ iframeEnabled: false, pixelEnabled: false - }, [], {consentString: '', gdprApplies: true}); + }, [], { consentString: '', gdprApplies: true }); expect(syncs).to.have.lengthOf(0); }); @@ -1151,8 +1151,8 @@ describe('Richaudience adapter tests', function () { config.setConfig({ 'userSync': { filterSettings: { - iframe: {bidders: '*', filter: 'exclude'}, - image: {bidders: '*', filter: 'exclude'} + iframe: { bidders: '*', filter: 'exclude' }, + image: { bidders: '*', filter: 'exclude' } } } }) @@ -1188,13 +1188,13 @@ describe('Richaudience adapter tests', function () { syncs = spec.getUserSyncs({ iframeEnabled: true, pixelEnabled: true - }, [], {consentString: '', gdprApplies: false}); + }, [], { consentString: '', gdprApplies: false }); expect(syncs).to.have.lengthOf(0); syncs = spec.getUserSyncs({ iframeEnabled: false, pixelEnabled: false - }, [], {consentString: '', gdprApplies: true}); + }, [], { consentString: '', gdprApplies: true }); expect(syncs).to.have.lengthOf(0); }); @@ -1202,8 +1202,8 @@ describe('Richaudience adapter tests', function () { config.setConfig({ 'userSync': { filterSettings: { - iframe: {bidders: '*', filter: 'exclude'}, - image: {bidders: '*', filter: 'include'} + iframe: { bidders: '*', filter: 'exclude' }, + image: { bidders: '*', filter: 'include' } } } }) @@ -1240,13 +1240,13 @@ describe('Richaudience adapter tests', function () { syncs = spec.getUserSyncs({ iframeEnabled: true, pixelEnabled: true - }, [], {consentString: '', gdprApplies: false}); + }, [], { consentString: '', gdprApplies: false }); expect(syncs).to.have.lengthOf(1); syncs = spec.getUserSyncs({ iframeEnabled: false, pixelEnabled: false - }, [], {consentString: '', gdprApplies: true}); + }, [], { consentString: '', gdprApplies: true }); expect(syncs).to.have.lengthOf(0); }); @@ -1254,8 +1254,8 @@ describe('Richaudience adapter tests', function () { config.setConfig({ 'userSync': { filterSettings: { - iframe: {bidders: '*', filter: 'include'}, - image: {bidders: '*', filter: 'exclude'} + iframe: { bidders: '*', filter: 'include' }, + image: { bidders: '*', filter: 'exclude' } } } }) @@ -1292,22 +1292,22 @@ describe('Richaudience adapter tests', function () { syncs = spec.getUserSyncs({ iframeEnabled: true, pixelEnabled: true - }, [], {consentString: '', gdprApplies: false}); + }, [], { consentString: '', gdprApplies: false }); expect(syncs).to.have.lengthOf(1); syncs = spec.getUserSyncs({ iframeEnabled: false, pixelEnabled: false - }, [], {consentString: '', gdprApplies: true}); + }, [], { consentString: '', gdprApplies: true }); expect(syncs).to.have.lengthOf(0); }); it('Verifies user syncs iframe/image include with GPP', function () { config.setConfig({ - 'userSync': {filterSettings: {iframe: {bidders: '*', filter: 'include'}}} + 'userSync': { filterSettings: { iframe: { bidders: '*', filter: 'include' } } } }) - let syncs = spec.getUserSyncs({iframeEnabled: true}, [BID_RESPONSE], { + let syncs = spec.getUserSyncs({ iframeEnabled: true }, [BID_RESPONSE], { gppString: 'DBABL~BVVqAAEABgA.QA', applicableSections: [7] }, @@ -1316,10 +1316,10 @@ describe('Richaudience adapter tests', function () { expect(syncs[0].type).to.equal('iframe'); config.setConfig({ - 'userSync': {filterSettings: {image: {bidders: '*', filter: 'include'}}} + 'userSync': { filterSettings: { image: { bidders: '*', filter: 'include' } } } }) - syncs = spec.getUserSyncs({pixelEnabled: true}, [BID_RESPONSE], { + syncs = spec.getUserSyncs({ pixelEnabled: true }, [BID_RESPONSE], { gppString: 'DBABL~BVVqAAEABgA.QA', applicableSections: [7, 5] }, @@ -1333,7 +1333,7 @@ describe('Richaudience adapter tests', function () { gppString: 'DBACMYA~CP5P4cAP5P4cAPoABAESAlEAAAAAAAAAAAAAA2QAQA2ADZABADYAAAAA.QA2QAQA2AAAA.IA2QAQA2AAAA~BP5P4cAP5P4cAPoABABGBACAAAAAAAAAAAAAAAAAAA.YAAAAAAAAAA', applicableSections: [0] }; - const result = spec.getUserSyncs({pixelEnabled: true}, undefined, undefined, undefined, gppConsent); + const result = spec.getUserSyncs({ pixelEnabled: true }, undefined, undefined, undefined, gppConsent); expect(result).to.deep.equal([{ type: 'image', url: `https://sync.richaudience.com/bf7c142f4339da0278e83698a02b0854/?referrer=http%3A%2F%2Fdomain.com&gpp=DBACMYA~CP5P4cAP5P4cAPoABAESAlEAAAAAAAAAAAAAA2QAQA2ADZABADYAAAAA.QA2QAQA2AAAA.IA2QAQA2AAAA~BP5P4cAP5P4cAPoABABGBACAAAAAAAAAAAAAAAAAAA.YAAAAAAAAAA&gpp_sid=0` diff --git a/test/spec/modules/ringieraxelspringerBidAdapter_spec.js b/test/spec/modules/ringieraxelspringerBidAdapter_spec.js deleted file mode 100644 index 0318a6987c6..00000000000 --- a/test/spec/modules/ringieraxelspringerBidAdapter_spec.js +++ /dev/null @@ -1,10 +0,0 @@ -import { expect } from 'chai'; -import { spec } from 'modules/ringieraxelspringerBidAdapter.js'; - -describe('ringieraxelspringer backward-compatibility shim', function () { - it('should re-export spec from dasBidAdapter', function () { - expect(spec).to.exist; - expect(spec.code).to.equal('das'); - expect(spec.aliases).to.include('ringieraxelspringer'); - }); -}); diff --git a/test/spec/modules/riseBidAdapter_spec.js b/test/spec/modules/riseBidAdapter_spec.js index bc7446a448c..9a9941f17cf 100644 --- a/test/spec/modules/riseBidAdapter_spec.js +++ b/test/spec/modules/riseBidAdapter_spec.js @@ -2,9 +2,9 @@ import { expect } from 'chai'; import { spec } from 'modules/riseBidAdapter.js'; import { newBidder } from 'src/adapters/bidderFactory.js'; import { config } from 'src/config.js'; -import {BANNER, NATIVE, VIDEO} from '../../../src/mediaTypes.js'; +import { BANNER, NATIVE, VIDEO } from '../../../src/mediaTypes.js'; import * as utils from 'src/utils.js'; -import {decorateAdUnitsWithNativeParams} from '../../../src/native.js'; +import { decorateAdUnitsWithNativeParams } from '../../../src/native.js'; const ENDPOINT = 'https://hb.yellowblue.io/hb-multi'; const TEST_ENDPOINT = 'https://hb.yellowblue.io/hb-multi-test'; @@ -104,7 +104,7 @@ describe('riseAdapter', function () { 'mediaTypes': { 'banner': { 'sizes': [ - [ 300, 250 ] + [300, 250] ] }, 'video': { @@ -163,7 +163,7 @@ describe('riseAdapter', function () { const bidderRequest = { bidderCode: 'rise', - ortb2: {device: {}}, + ortb2: { device: {} }, } const placementId = '12345678'; const api = [1, 2]; @@ -348,7 +348,7 @@ describe('riseAdapter', function () { }); it('should have us_privacy param if usPrivacy is available in the bidRequest', function () { - const bidderRequestWithUSP = Object.assign({uspConsent: '1YNN'}, bidderRequest); + const bidderRequestWithUSP = Object.assign({ uspConsent: '1YNN' }, bidderRequest); const request = spec.buildRequests(bidRequests, bidderRequestWithUSP); expect(request.data.params).to.be.an('object'); expect(request.data.params).to.have.property('us_privacy', '1YNN'); @@ -361,7 +361,7 @@ describe('riseAdapter', function () { }); it('should not send the gdpr param if gdprApplies is false in the bidRequest', function () { - const bidderRequestWithGDPR = Object.assign({gdprConsent: {gdprApplies: false}}, bidderRequest); + const bidderRequestWithGDPR = Object.assign({ gdprConsent: { gdprApplies: false } }, bidderRequest); const request = spec.buildRequests(bidRequests, bidderRequestWithGDPR); expect(request.data.params).to.be.an('object'); expect(request.data.params).to.not.have.property('gdpr'); @@ -369,7 +369,7 @@ describe('riseAdapter', function () { }); it('should send the gdpr param if gdprApplies is true in the bidRequest', function () { - const bidderRequestWithGDPR = Object.assign({gdprConsent: {gdprApplies: true, consentString: 'test-consent-string'}}, bidderRequest); + const bidderRequestWithGDPR = Object.assign({ gdprConsent: { gdprApplies: true, consentString: 'test-consent-string' } }, bidderRequest); const request = spec.buildRequests(bidRequests, bidderRequestWithGDPR); expect(request.data.params).to.be.an('object'); expect(request.data.params).to.have.property('gdpr', true); @@ -377,7 +377,7 @@ describe('riseAdapter', function () { }); it('should not send the gpp param if gppConsent is false in the bidRequest', function () { - const bidderRequestWithoutGPP = Object.assign({gppConsent: false}, bidderRequest); + const bidderRequestWithoutGPP = Object.assign({ gppConsent: false }, bidderRequest); const request = spec.buildRequests(bidRequests, bidderRequestWithoutGPP); expect(request.data.params).to.be.an('object'); expect(request.data.params).to.not.have.property('gpp'); @@ -385,7 +385,7 @@ describe('riseAdapter', function () { }); it('should send the gpp param if gppConsent is true in the bidRequest', function () { - const bidderRequestWithGPP = Object.assign({gppConsent: {gppString: 'gpp-consent', applicableSections: [7]}}, bidderRequest); + const bidderRequestWithGPP = Object.assign({ gppConsent: { gppString: 'gpp-consent', applicableSections: [7] } }, bidderRequest); const request = spec.buildRequests(bidRequests, bidderRequestWithGPP); console.log('request.data.params'); console.log(request.data.params); @@ -448,15 +448,15 @@ describe('riseAdapter', function () { 'browsers': [ { 'brand': 'Chromium', - 'version': [ '106', '0', '5249', '119' ] + 'version': ['106', '0', '5249', '119'] }, { 'brand': 'Google Chrome', - 'version': [ '106', '0', '5249', '119' ] + 'version': ['106', '0', '5249', '119'] }, { 'brand': 'Not;A=Brand', - 'version': [ '99', '0', '0', '0' ] + 'version': ['99', '0', '0', '0'] } ], 'mobile': 0, @@ -470,20 +470,20 @@ describe('riseAdapter', function () { 'sua': { 'platform': { 'brand': 'macOS', - 'version': [ '12', '4', '0' ] + 'version': ['12', '4', '0'] }, 'browsers': [ { 'brand': 'Chromium', - 'version': [ '106', '0', '5249', '119' ] + 'version': ['106', '0', '5249', '119'] }, { 'brand': 'Google Chrome', - 'version': [ '106', '0', '5249', '119' ] + 'version': ['106', '0', '5249', '119'] }, { 'brand': 'Not;A=Brand', - 'version': [ '99', '0', '0', '0' ] + 'version': ['99', '0', '0', '0'] } ], 'mobile': 0, @@ -514,7 +514,7 @@ describe('riseAdapter', function () { model: 'iPhone 12 Pro Max', os: 'iOS', osv: '17.4', - ext: {fiftyonedegrees_deviceId: '17595-133085-133468-18092'}, + ext: { fiftyonedegrees_deviceId: '17595-133085-133468-18092' }, }, }; diff --git a/test/spec/modules/rivrAnalyticsAdapter_spec.js b/test/spec/modules/rivrAnalyticsAdapter_spec.js index 1192d1ba604..fef07af2376 100644 --- a/test/spec/modules/rivrAnalyticsAdapter_spec.js +++ b/test/spec/modules/rivrAnalyticsAdapter_spec.js @@ -13,9 +13,10 @@ import analyticsAdapter, { getCookie, storeAndReturnRivrUsrIdCookie, arrayDifference, - activelyWaitForBannersToRender} from 'modules/rivrAnalyticsAdapter.js'; + activelyWaitForBannersToRender +} from 'modules/rivrAnalyticsAdapter.js'; -import {expect} from 'chai'; +import { expect } from 'chai'; import adapterManager from 'src/adapterManager.js'; import * as ajax from 'src/ajax.js'; import { EVENTS } from 'src/constants.js'; diff --git a/test/spec/modules/robustAppsBidAdapter_spec.js b/test/spec/modules/robustAppsBidAdapter_spec.js index 931d50023f6..237706548a8 100644 --- a/test/spec/modules/robustAppsBidAdapter_spec.js +++ b/test/spec/modules/robustAppsBidAdapter_spec.js @@ -1,8 +1,8 @@ -import {expect} from 'chai'; -import {config} from 'src/config.js'; -import {spec} from 'modules/robustAppsBidAdapter.js'; -import {deepClone} from 'src/utils'; -import {getBidFloor} from '../../../libraries/xeUtils/bidderUtils.js'; +import { expect } from 'chai'; +import { config } from 'src/config.js'; +import { spec } from 'modules/robustAppsBidAdapter.js'; +import { deepClone } from 'src/utils'; +import { getBidFloor } from '../../../libraries/xeUtils/bidderUtils.js'; const ENDPOINT = 'https://pbjs.rbstsystems.live'; @@ -48,12 +48,12 @@ defaultRequestVideo.mediaTypes = { const videoBidderRequest = { bidderCode: 'robustApps', - bids: [{mediaTypes: {video: {}}, bidId: 'qwerty'}] + bids: [{ mediaTypes: { video: {} }, bidId: 'qwerty' }] }; const displayBidderRequest = { bidderCode: 'robustApps', - bids: [{bidId: 'qwerty'}] + bids: [{ bidId: 'qwerty' }] }; describe('robustAppsBidAdapter', () => { @@ -103,7 +103,7 @@ describe('robustAppsBidAdapter', () => { expect(request).to.have.property('tz').and.to.equal(new Date().getTimezoneOffset()); expect(request).to.have.property('bc').and.to.equal(1); expect(request).to.have.property('floor').and.to.equal(null); - expect(request).to.have.property('banner').and.to.deep.equal({sizes: [[300, 250], [300, 200]]}); + expect(request).to.have.property('banner').and.to.deep.equal({ sizes: [[300, 250], [300, 200]] }); expect(request).to.have.property('gdprConsent').and.to.deep.equal({}); expect(request).to.have.property('userEids').and.to.deep.equal([]); expect(request).to.have.property('usPrivacy').and.to.equal(''); @@ -195,7 +195,7 @@ describe('robustAppsBidAdapter', () => { it('should build request with valid bidfloor', function () { const bfRequest = deepClone(defaultRequest); - bfRequest.getFloor = () => ({floor: 5, currency: 'USD'}); + bfRequest.getFloor = () => ({ floor: 5, currency: 'USD' }); const request = JSON.parse(spec.buildRequests([bfRequest], {}).data)[0]; expect(request).to.have.property('floor').and.to.equal(5); }); @@ -211,8 +211,8 @@ describe('robustAppsBidAdapter', () => { it('should build request with extended ids', function () { const idRequest = deepClone(defaultRequest); idRequest.userIdAsEids = [ - {source: 'adserver.org', uids: [{id: 'TTD_ID_FROM_USER_ID_MODULE', atype: 1, ext: {rtiPartner: 'TDID'}}]}, - {source: 'pubcid.org', uids: [{id: 'pubCommonId_FROM_USER_ID_MODULE', atype: 1}]} + { source: 'adserver.org', uids: [{ id: 'TTD_ID_FROM_USER_ID_MODULE', atype: 1, ext: { rtiPartner: 'TDID' } }] }, + { source: 'pubcid.org', uids: [{ id: 'pubCommonId_FROM_USER_ID_MODULE', atype: 1 }] } ]; const request = JSON.parse(spec.buildRequests([idRequest], {}).data)[0]; expect(request).to.have.property('userEids').and.deep.equal(idRequest.userIdAsEids); @@ -264,7 +264,7 @@ describe('robustAppsBidAdapter', () => { } }; - const validResponse = spec.interpretResponse(serverResponse, {bidderRequest: displayBidderRequest}); + const validResponse = spec.interpretResponse(serverResponse, { bidderRequest: displayBidderRequest }); const bid = validResponse[0]; expect(validResponse).to.be.an('array').that.is.not.empty; expect(bid.requestId).to.equal('qwerty'); @@ -273,7 +273,7 @@ describe('robustAppsBidAdapter', () => { expect(bid.width).to.equal(300); expect(bid.height).to.equal(250); expect(bid.ttl).to.equal(600); - expect(bid.meta).to.deep.equal({advertiserDomains: ['robustApps']}); + expect(bid.meta).to.deep.equal({ advertiserDomains: ['robustApps'] }); }); it('should interpret valid banner response', function () { @@ -294,7 +294,7 @@ describe('robustAppsBidAdapter', () => { } }; - const validResponseBanner = spec.interpretResponse(serverResponse, {bidderRequest: displayBidderRequest}); + const validResponseBanner = spec.interpretResponse(serverResponse, { bidderRequest: displayBidderRequest }); const bid = validResponseBanner[0]; expect(validResponseBanner).to.be.an('array').that.is.not.empty; expect(bid.mediaType).to.equal('banner'); @@ -320,7 +320,7 @@ describe('robustAppsBidAdapter', () => { } }; - const validResponseBanner = spec.interpretResponse(serverResponse, {bidderRequest: videoBidderRequest}); + const validResponseBanner = spec.interpretResponse(serverResponse, { bidderRequest: videoBidderRequest }); const bid = validResponseBanner[0]; expect(validResponseBanner).to.be.an('array').that.is.not.empty; expect(bid.mediaType).to.equal('video'); @@ -336,12 +336,12 @@ describe('robustAppsBidAdapter', () => { }); it('should return empty if sync is not allowed', function () { - const opts = spec.getUserSyncs({iframeEnabled: false, pixelEnabled: false}); + const opts = spec.getUserSyncs({ iframeEnabled: false, pixelEnabled: false }); expect(opts).to.be.an('array').that.is.empty; }); it('should allow iframe sync', function () { - const opts = spec.getUserSyncs({iframeEnabled: true, pixelEnabled: false}, [{ + const opts = spec.getUserSyncs({ iframeEnabled: true, pixelEnabled: false }, [{ body: { data: [{ requestId: 'qwerty', @@ -360,7 +360,7 @@ describe('robustAppsBidAdapter', () => { }); it('should allow pixel sync', function () { - const opts = spec.getUserSyncs({iframeEnabled: false, pixelEnabled: true}, [{ + const opts = spec.getUserSyncs({ iframeEnabled: false, pixelEnabled: true }, [{ body: { data: [{ requestId: 'qwerty', @@ -379,7 +379,7 @@ describe('robustAppsBidAdapter', () => { }); it('should allow pixel sync and parse consent params', function () { - const opts = spec.getUserSyncs({iframeEnabled: false, pixelEnabled: true}, [{ + const opts = spec.getUserSyncs({ iframeEnabled: false, pixelEnabled: true }, [{ body: { data: [{ requestId: 'qwerty', @@ -403,20 +403,20 @@ describe('robustAppsBidAdapter', () => { describe('getBidFloor', function () { it('should return null when getFloor is not a function', () => { - const bid = {getFloor: 2}; + const bid = { getFloor: 2 }; const result = getBidFloor(bid); expect(result).to.be.null; }); it('should return null when getFloor doesnt return an object', () => { - const bid = {getFloor: () => 2}; + const bid = { getFloor: () => 2 }; const result = getBidFloor(bid); expect(result).to.be.null; }); it('should return null when floor is not a number', () => { const bid = { - getFloor: () => ({floor: 'string', currency: 'USD'}) + getFloor: () => ({ floor: 'string', currency: 'USD' }) }; const result = getBidFloor(bid); expect(result).to.be.null; @@ -424,7 +424,7 @@ describe('robustAppsBidAdapter', () => { it('should return null when currency is not USD', () => { const bid = { - getFloor: () => ({floor: 5, currency: 'EUR'}) + getFloor: () => ({ floor: 5, currency: 'EUR' }) }; const result = getBidFloor(bid); expect(result).to.be.null; @@ -432,7 +432,7 @@ describe('robustAppsBidAdapter', () => { it('should return floor value when everything is correct', () => { const bid = { - getFloor: () => ({floor: 5, currency: 'USD'}) + getFloor: () => ({ floor: 5, currency: 'USD' }) }; const result = getBidFloor(bid); expect(result).to.equal(5); diff --git a/test/spec/modules/rocketlabBidAdapter_spec.js b/test/spec/modules/rocketlabBidAdapter_spec.js index fc162c67959..ffe48e4c2d9 100644 --- a/test/spec/modules/rocketlabBidAdapter_spec.js +++ b/test/spec/modules/rocketlabBidAdapter_spec.js @@ -544,7 +544,7 @@ describe("RocketLabBidAdapter", function () { consentString: "ALL", gdprApplies: true, }, - {} + undefined ); expect(syncData).to.be.an("array").which.is.not.empty; expect(syncData[0]).to.be.an("object"); @@ -560,9 +560,7 @@ describe("RocketLabBidAdapter", function () { {}, {}, {}, - { - consentString: "1---", - } + "1---" ); expect(syncData).to.be.an("array").which.is.not.empty; expect(syncData[0]).to.be.an("object"); @@ -578,7 +576,7 @@ describe("RocketLabBidAdapter", function () { {}, {}, {}, - {}, + undefined, { gppString: "abc123", applicableSections: [8], diff --git a/test/spec/modules/roxotAnalyticsAdapter_spec.js b/test/spec/modules/roxotAnalyticsAdapter_spec.js index 4882d6e7c63..6c57bd6b031 100644 --- a/test/spec/modules/roxotAnalyticsAdapter_spec.js +++ b/test/spec/modules/roxotAnalyticsAdapter_spec.js @@ -1,6 +1,6 @@ import roxotAnalytic from 'modules/roxotAnalyticsAdapter.js'; -import {expect} from 'chai'; -import {server} from 'test/mocks/xhr.js'; +import { expect } from 'chai'; +import { server } from 'test/mocks/xhr.js'; import { EVENTS } from 'src/constants.js'; const events = require('src/events'); @@ -179,7 +179,7 @@ describe('Roxot Prebid Analytic', function () { expect(server.requests.length).to.equal(1); expect(server.requests[0].url).to.equal('https://' + roxotConfigServerUrl + '/c?publisherId=' + publisherId + '&host=localhost'); - server.requests[0].respond(200, {'Content-Type': 'application/json'}, '{"a": 1, "i": 1, "bat": 1}'); + server.requests[0].respond(200, { 'Content-Type': 'application/json' }, '{"a": 1, "i": 1, "bat": 1}'); events.emit(EVENTS.AUCTION_INIT, auctionInit); events.emit(EVENTS.BID_REQUESTED, bidRequested); @@ -258,7 +258,7 @@ describe('Roxot Prebid Analytic', function () { expect(server.requests.length).to.equal(1); expect(server.requests[0].url).to.equal('https://' + roxotConfigServerUrl + '/c?publisherId=' + publisherId + '&host=localhost'); - server.requests[0].respond(200, {'Content-Type': 'application/json'}, '{"a": 1, "i": 1, "bat": 1}'); + server.requests[0].respond(200, { 'Content-Type': 'application/json' }, '{"a": 1, "i": 1, "bat": 1}'); events.emit(EVENTS.AUCTION_INIT, auctionInit); events.emit(EVENTS.BID_REQUESTED, bidRequested); @@ -410,7 +410,7 @@ describe('Roxot Prebid Analytic', function () { server.requests[0].respond(500); - expect(roxotAnalytic.getOptions().serverConfig).to.deep.equal({a: 1, i: 1, bat: 1, isError: 1}); + expect(roxotAnalytic.getOptions().serverConfig).to.deep.equal({ a: 1, i: 1, bat: 1, isError: 1 }); }); }); diff --git a/test/spec/modules/rtbhouseBidAdapter_spec.js b/test/spec/modules/rtbhouseBidAdapter_spec.js index e75190037bd..000ed9a2d56 100644 --- a/test/spec/modules/rtbhouseBidAdapter_spec.js +++ b/test/spec/modules/rtbhouseBidAdapter_spec.js @@ -346,7 +346,7 @@ describe('RTBHouseAdapter', () => { it('should include bidfloor from floor module if available', () => { const bidRequest = Object.assign([], bidRequests); - bidRequest[0].getFloor = () => ({floor: 1.22, currency: 'USD'}); + bidRequest[0].getFloor = () => ({ floor: 1.22, currency: 'USD' }); const request = spec.buildRequests(bidRequest, bidderRequest); const data = JSON.parse(request.data); expect(data.imp[0].bidfloor).to.equal(1.22) @@ -354,7 +354,7 @@ describe('RTBHouseAdapter', () => { it('should use bidfloor from floor module if both floor module and bid floor available', () => { const bidRequest = Object.assign([], bidRequests); - bidRequest[0].getFloor = () => ({floor: 1.22, currency: 'USD'}); + bidRequest[0].getFloor = () => ({ floor: 1.22, currency: 'USD' }); bidRequest[0].params.bidfloor = 0.01; const request = spec.buildRequests(bidRequest, bidderRequest); const data = JSON.parse(request.data); @@ -448,9 +448,9 @@ describe('RTBHouseAdapter', () => { const data = JSON.parse(request.data); expect(data.bcat).to.deep.equal(localBidderRequest.ortb2.bcat); expect(data.badv).to.deep.equal(localBidderRequest.ortb2.badv); - expect(data.site).to.nested.include({'ext.data': 'some site data'}); - expect(data.device).to.nested.include({'ext.data': 'some device data'}); - expect(data.user).to.nested.include({'ext.data': 'some user data'}); + expect(data.site).to.nested.include({ 'ext.data': 'some site data' }); + expect(data.device).to.nested.include({ 'ext.data': 'some device data' }); + expect(data.user).to.nested.include({ 'ext.data': 'some user data' }); }); context('DSA', () => { @@ -816,14 +816,14 @@ describe('RTBHouseAdapter', () => { } ]; let bidderRequest; - const result = spec.interpretResponse({body: response}, {bidderRequest}); + const result = spec.interpretResponse({ body: response }, { bidderRequest }); expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); }); it('handles nobid responses', function () { const response = ''; let bidderRequest; - const result = spec.interpretResponse({body: response}, {bidderRequest}); + const result = spec.interpretResponse({ body: response }, { bidderRequest }); expect(result.length).to.equal(0); }); @@ -862,7 +862,7 @@ describe('RTBHouseAdapter', () => { } ]; let bidderRequest; - const result = spec.interpretResponse({body: response}, {bidderRequest}); + const result = spec.interpretResponse({ body: response }, { bidderRequest }); expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); expect(result[0]).to.have.nested.property('meta.dsa'); @@ -918,7 +918,7 @@ describe('RTBHouseAdapter', () => { }]; it('should contain native assets in valid format', () => { - const bids = spec.interpretResponse({body: response}, {}); + const bids = spec.interpretResponse({ body: response }, {}); expect(bids[0].meta.advertiserDomains).to.deep.equal(['rtbhouse.com']); expect(bids[0].native).to.deep.equal({ title: 'Title text', diff --git a/test/spec/modules/rtbsapeBidAdapter_spec.js b/test/spec/modules/rtbsapeBidAdapter_spec.js index 538a728d03a..57861994c93 100644 --- a/test/spec/modules/rtbsapeBidAdapter_spec.js +++ b/test/spec/modules/rtbsapeBidAdapter_spec.js @@ -1,19 +1,19 @@ -import {expect} from 'chai'; -import {spec} from 'modules/rtbsapeBidAdapter.js'; +import { expect } from 'chai'; +import { spec } from 'modules/rtbsapeBidAdapter.js'; import 'src/prebid.js'; import * as utils from 'src/utils.js'; -import {executeRenderer, Renderer} from 'src/Renderer.js'; +import { executeRenderer, Renderer } from 'src/Renderer.js'; describe('rtbsapeBidAdapterTests', function () { describe('isBidRequestValid', function () { it('valid', function () { - expect(spec.isBidRequestValid({bidder: 'rtbsape', mediaTypes: {banner: true}, params: {placeId: 4321}})).to.equal(true); - expect(spec.isBidRequestValid({bidder: 'rtbsape', mediaTypes: {video: true}, params: {placeId: 4321}})).to.equal(true); + expect(spec.isBidRequestValid({ bidder: 'rtbsape', mediaTypes: { banner: true }, params: { placeId: 4321 } })).to.equal(true); + expect(spec.isBidRequestValid({ bidder: 'rtbsape', mediaTypes: { video: true }, params: { placeId: 4321 } })).to.equal(true); }); it('invalid', function () { - expect(spec.isBidRequestValid({bidder: 'rtbsape', mediaTypes: {banner: true}, params: {}})).to.equal(false); - expect(spec.isBidRequestValid({bidder: 'rtbsape', params: {placeId: 4321}})).to.equal(false); + expect(spec.isBidRequestValid({ bidder: 'rtbsape', mediaTypes: { banner: true }, params: {} })).to.equal(false); + expect(spec.isBidRequestValid({ bidder: 'rtbsape', params: { placeId: 4321 } })).to.equal(false); }); }); @@ -21,7 +21,7 @@ describe('rtbsapeBidAdapterTests', function () { const bidRequestData = [{ bidId: 'bid1234', bidder: 'rtbsape', - params: {placeId: 4321}, + params: { placeId: 4321 }, sizes: [[240, 400]] }]; const bidderRequest = { @@ -54,7 +54,7 @@ describe('rtbsapeBidAdapterTests', function () { }] } }; - const bids = spec.interpretResponse(serverResponse, {data: {bids: [{mediaTypes: {banner: true}}]}}); + const bids = spec.interpretResponse(serverResponse, { data: { bids: [{ mediaTypes: { banner: true } }] } }); expect(bids).to.have.lengthOf(1); const bid = bids[0]; expect(bid.cpm).to.equal(2.21); @@ -123,7 +123,7 @@ describe('rtbsapeBidAdapterTests', function () { let spy = false; window.sapeRtbPlayerHandler = function (id, w, h, m) { - const player = {addSlot: () => [id, w, h, m]} + const player = { addSlot: () => [id, w, h, m] } expect(spy).to.equal(false); spy = sinon.spy(player, 'addSlot'); return player; @@ -168,7 +168,7 @@ describe('rtbsapeBidAdapterTests', function () { }] } }; - const bids = spec.interpretResponse(serverResponse, {data: {bids: [{mediaTypes: {banner: true}}]}}); + const bids = spec.interpretResponse(serverResponse, { data: { bids: [{ mediaTypes: { banner: true } }] } }); expect(bids).to.have.lengthOf(1); const bid = bids[0]; expect(bid.cpm).to.equal(2.23); @@ -182,9 +182,9 @@ describe('rtbsapeBidAdapterTests', function () { }); it('getUserSyncs', function () { - const syncs = spec.getUserSyncs({iframeEnabled: true}); + const syncs = spec.getUserSyncs({ iframeEnabled: true }); expect(syncs).to.be.an('array').that.to.have.lengthOf(1); - expect(syncs[0]).to.deep.equal({type: 'iframe', url: 'https://www.acint.net/mc/?dp=141'}); + expect(syncs[0]).to.deep.equal({ type: 'iframe', url: 'https://www.acint.net/mc/?dp=141' }); }); describe('onBidWon', function () { @@ -197,12 +197,12 @@ describe('rtbsapeBidAdapterTests', function () { }); it('called once', function () { - spec.onBidWon({cpm: '2.21', nurl: 'https://ssp-rtb.sape.ru/track?event=win'}); + spec.onBidWon({ cpm: '2.21', nurl: 'https://ssp-rtb.sape.ru/track?event=win' }); expect(utils.triggerPixel.calledOnce).to.equal(true); }); it('called false', function () { - spec.onBidWon({cpm: '2.21'}); + spec.onBidWon({ cpm: '2.21' }); expect(utils.triggerPixel.called).to.equal(false); }); }); diff --git a/test/spec/modules/rubiconBidAdapter_spec.js b/test/spec/modules/rubiconBidAdapter_spec.js index 70e55c5a7eb..d1d26c7d010 100644 --- a/test/spec/modules/rubiconBidAdapter_spec.js +++ b/test/spec/modules/rubiconBidAdapter_spec.js @@ -1,4 +1,4 @@ -import {expect} from 'chai'; +import { expect } from 'chai'; import { spec, getPriceGranularity, @@ -8,7 +8,7 @@ import { resetImpIdMap, converter } from 'modules/rubiconBidAdapter.js'; -import {config} from 'src/config.js'; +import { config } from 'src/config.js'; import * as utils from 'src/utils.js'; import 'modules/consentManagementTcf.js'; import 'modules/consentManagementUsp.js'; @@ -16,9 +16,9 @@ import 'modules/userId/index.js'; import 'modules/priceFloors.js'; import 'modules/multibid/index.js'; import adapterManager from 'src/adapterManager.js'; -import {addFPDToBidderRequest} from '../../helpers/fpd.js'; +import { addFPDToBidderRequest } from '../../helpers/fpd.js'; import { deepClone } from '../../../src/utils.js'; -import {getGlobal} from '../../../src/prebidGlobal.js'; +import { getGlobal } from '../../../src/prebidGlobal.js'; const INTEGRATION = `pbjs_lite_v$prebid.version$`; // $prebid.version$ will be substituted in by gulp in built prebid const PBS_INTEGRATION = 'pbjs'; @@ -252,7 +252,7 @@ describe('the rubicon adapter', function () { 'size_id': 201, }; bid.userId = { - lipb: {lipbid: '0000-1111-2222-3333', segments: ['segA', 'segB']}, + lipb: { lipbid: '0000-1111-2222-3333', segments: ['segA', 'segB'] }, idl_env: '1111-2222-3333-4444', tdid: '3000', pubcid: '4000', @@ -347,7 +347,7 @@ describe('the rubicon adapter', function () { ] } ]; - bidderRequest.ortb2 = {user: {ext: {eids}}}; + bidderRequest.ortb2 = { user: { ext: { eids } } }; return bidderRequest; } @@ -455,19 +455,19 @@ describe('the rubicon adapter', function () { }; 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'} + { 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)); @@ -581,25 +581,25 @@ describe('the rubicon adapter', function () { expect(data.get('rp_hard_floor')).to.be.null; // make it respond with a non USD floor should not send it - getFloorResponse = {currency: 'EUR', floor: 1.0}; + getFloorResponse = { currency: 'EUR', floor: 1.0 }; [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); data = new URLSearchParams(request.data); expect(data.get('rp_hard_floor')).to.be.null; // make it respond with a non USD floor should not send it - getFloorResponse = {currency: 'EUR'}; + getFloorResponse = { currency: 'EUR' }; [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); data = new URLSearchParams(request.data); expect(data.get('rp_hard_floor')).to.be.null; // make it respond with USD floor and string floor - getFloorResponse = {currency: 'USD', floor: '1.23'}; + getFloorResponse = { currency: 'USD', floor: '1.23' }; [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); data = new URLSearchParams(request.data); expect(data.get('rp_hard_floor')).to.equal('1.23'); // make it respond with USD floor and num floor - getFloorResponse = {currency: 'USD', floor: 1.23}; + getFloorResponse = { currency: 'USD', floor: 1.23 }; [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); data = new URLSearchParams(request.data); expect(data.get('rp_hard_floor')).to.equal('1.23'); @@ -670,7 +670,7 @@ describe('the rubicon adapter', function () { }); it('should correctly send p_pos in sra fashion', function() { - config.setConfig({rubicon: {singleRequest: true}}); + config.setConfig({ rubicon: { singleRequest: true } }); // first one is atf var sraPosRequest = utils.deepClone(bidderRequest); @@ -702,7 +702,7 @@ describe('the rubicon adapter', function () { it('should correctly send cdep signal when requested', () => { var badposRequest = utils.deepClone(bidderRequest); - badposRequest.bids[0].ortb2 = {device: {ext: {cdep: 3}}}; + badposRequest.bids[0].ortb2 = { device: { ext: { cdep: 3 } } }; const [request] = spec.buildRequests(badposRequest.bids, badposRequest); const data = new URLSearchParams(request.data); @@ -815,7 +815,7 @@ describe('the rubicon adapter', function () { ] }; - bidderRequest = Object.assign({refererInfo}, bidderRequest); + bidderRequest = Object.assign({ refererInfo }, bidderRequest); delete bidderRequest.bids[0].params.referrer; const [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); @@ -828,8 +828,8 @@ describe('the rubicon adapter', function () { expect(new URLSearchParams(request.data).get('rf')).to.equal('localhost'); delete bidderRequest.bids[0].params.referrer; - const refererInfo = {page: 'https://www.prebid.org'}; - bidderRequest = Object.assign({refererInfo}, bidderRequest); + const refererInfo = { page: 'https://www.prebid.org' }; + bidderRequest = Object.assign({ refererInfo }, bidderRequest); [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); expect(new URLSearchParams(request.data).get('rf')).to.equal('https://www.prebid.org'); @@ -1073,7 +1073,7 @@ describe('the rubicon adapter', function () { }], gender: 'M', yob: '1984', - geo: {country: 'ca'}, + geo: { country: 'ca' }, keywords: 'd', ext: { data: { @@ -1103,7 +1103,7 @@ describe('the rubicon adapter', function () { }; // get the built request - const [request] = spec.buildRequests(bidderRequest.bids.map((b) => ({...b, ortb2})), bidderRequest); + const [request] = spec.buildRequests(bidderRequest.bids.map((b) => ({ ...b, ortb2 })), bidderRequest); const data = new URLSearchParams(request.data); // make sure that tg_v, tg_i, and kw values are correct @@ -1118,7 +1118,7 @@ describe('the rubicon adapter', function () { it('should group all bid requests with the same site id', function () { sandbox.stub(Math, 'random').callsFake(() => 0.1); - config.setConfig({rubicon: {singleRequest: true}}); + config.setConfig({ rubicon: { singleRequest: true } }); const expectedQuery = { 'account_id': '14062', @@ -1225,7 +1225,7 @@ describe('the rubicon adapter', function () { }); it('should not send more than 10 bids in a request (split into separate requests with <= 10 bids each)', function () { - config.setConfig({rubicon: {singleRequest: true}}); + config.setConfig({ rubicon: { singleRequest: true } }); let serverRequests; let data; @@ -1291,7 +1291,7 @@ describe('the rubicon adapter', function () { }); it('should not group bid requests if singleRequest does not equal true', function () { - config.setConfig({rubicon: {singleRequest: false}}); + config.setConfig({ rubicon: { singleRequest: false } }); const bidCopy = utils.deepClone(bidderRequest.bids[0]); bidderRequest.bids.push(bidCopy); @@ -1309,7 +1309,7 @@ describe('the rubicon adapter', function () { }); it('should not group video bid requests', function () { - config.setConfig({rubicon: {singleRequest: true}}); + config.setConfig({ rubicon: { singleRequest: true } }); const bidCopy = utils.deepClone(bidderRequest.bids[0]); bidderRequest.bids.push(bidCopy); @@ -1602,7 +1602,7 @@ describe('the rubicon adapter', function () { }); describe('Config user.id support', function () { it('should send ppuid when config defines user.id', function () { - config.setConfig({user: {id: '123'}}); + config.setConfig({ user: { id: '123' } }); const clonedBid = utils.deepClone(bidderRequest.bids[0]); clonedBid.userId = { pubcid: '1111' @@ -1756,7 +1756,7 @@ describe('the rubicon adapter', function () { }]; const expectedTransparency = 'testdomain.com~1'; - const [request] = spec.buildRequests(bidderRequest.bids.map((b) => ({...b, ortb2: ortb2Clone})), bidderRequest); + const [request] = spec.buildRequests(bidderRequest.bids.map((b) => ({ ...b, ortb2: ortb2Clone })), bidderRequest); const data = new URLSearchParams(request.data); expect(data.get('dsatransparency')).to.equal(expectedTransparency); @@ -1769,7 +1769,7 @@ describe('the rubicon adapter', function () { params: [], }]; - const [request] = spec.buildRequests(bidderRequest.bids.map((b) => ({...b, ortb2: ortb2Clone})), bidderRequest); + const [request] = spec.buildRequests(bidderRequest.bids.map((b) => ({ ...b, ortb2: ortb2Clone })), bidderRequest); const data = new URLSearchParams(request.data); expect(data.get('dsatransparency')).to.be.null }) @@ -1781,14 +1781,14 @@ describe('the rubicon adapter', function () { dsaparams: [1], }]; - const [request] = spec.buildRequests(bidderRequest.bids.map((b) => ({...b, ortb2: ortb2Clone})), bidderRequest); + const [request] = spec.buildRequests(bidderRequest.bids.map((b) => ({ ...b, ortb2: ortb2Clone })), bidderRequest); const data = new URLSearchParams(request.data); expect(data.get('dsatransparency')).to.be.null }) it('should send dsa signals if \"ortb2.regs.ext.dsa\"', function() { const expectedTransparency = 'testdomain.com~1~~testdomain2.com~1_2' - const [request] = spec.buildRequests(bidderRequest.bids.map((b) => ({...b, ortb2})), bidderRequest) + const [request] = spec.buildRequests(bidderRequest.bids.map((b) => ({ ...b, ortb2 })), bidderRequest) const data = new URLSearchParams(request.data); expect(typeof data).to.equal('object'); @@ -1806,7 +1806,7 @@ describe('the rubicon adapter', function () { const expectedTransparency = 'testdomain.com~1'; const ortb2Clone = deepClone(ortb2); ortb2Clone.regs.ext.dsa.transparency.pop() - const [request] = spec.buildRequests(bidderRequest.bids.map((b) => ({...b, ortb2: ortb2Clone})), bidderRequest) + const [request] = spec.buildRequests(bidderRequest.bids.map((b) => ({ ...b, ortb2: ortb2Clone })), bidderRequest) const data = new URLSearchParams(request.data); expect(typeof data).to.equal('object'); @@ -1815,7 +1815,7 @@ describe('the rubicon adapter', function () { }) }) - it('should send gpid and pbadslot since it is prefered over dfp code', function () { + it('should send gpid and pbadslot since it is preferred over dfp code', function () { bidderRequest.bids[0].ortb2Imp = { ext: { gpid: '/1233/sports&div1', @@ -2043,33 +2043,37 @@ describe('the rubicon adapter', function () { }); it('should not send high entropy if not present when it is low entropy client hints', function () { const bidRequestSua = utils.deepClone(bidderRequest); - bidRequestSua.bids[0].ortb2 = { device: { sua: { - 'source': 1, - 'platform': { - 'brand': 'macOS' - }, - 'browsers': [ - { - 'brand': 'Not A(Brand', - 'version': [ - '8' - ] - }, - { - 'brand': 'Chromium', - 'version': [ - '132' - ] - }, - { - 'brand': 'Google Chrome', - 'version': [ - '132' - ] + bidRequestSua.bids[0].ortb2 = { + device: { + sua: { + 'source': 1, + 'platform': { + 'brand': 'macOS' + }, + 'browsers': [ + { + 'brand': 'Not A(Brand', + 'version': [ + '8' + ] + }, + { + 'brand': 'Chromium', + 'version': [ + '132' + ] + }, + { + 'brand': 'Google Chrome', + 'version': [ + '132' + ] + } + ], + 'mobile': 0 } - ], - 'mobile': 0 - } } }; + } + }; // How should fastlane query be constructed with default SUA const expectedValues = { @@ -2097,14 +2101,18 @@ describe('the rubicon adapter', function () { }); it('should ignore invalid browser hints (missing version)', function () { const bidRequestSua = utils.deepClone(bidderRequest); - bidRequestSua.bids[0].ortb2 = { device: { sua: { - 'browsers': [ - { - 'brand': 'Not A(Brand', - // 'version': ['8'], // missing version - }, - ], - } } }; + bidRequestSua.bids[0].ortb2 = { + device: { + sua: { + 'browsers': [ + { + 'brand': 'Not A(Brand', + // 'version': ['8'], // missing version + }, + ], + } + } + }; // How should fastlane query be constructed with default SUA const expectedValues = { @@ -2165,7 +2173,7 @@ describe('the rubicon adapter', function () { expect(imp.ext.prebid.bidder.rubicon.video.skipafter).to.equal(15); expect(post.ext.prebid.auctiontimestamp).to.equal(1472239426000); // should contain version - expect(post.ext.prebid.channel).to.deep.equal({name: 'pbjs', version: getGlobal().version}); + expect(post.ext.prebid.channel).to.deep.equal({ name: 'pbjs', version: getGlobal().version }); expect(post.user.ext.consent).to.equal('BOJ/P2HOJ/P2HABABMAAAAAZ+A=='); // EIDs should exist expect(post.user.ext).to.have.property('eids').that.is.an('array'); @@ -2277,22 +2285,22 @@ describe('the rubicon adapter', function () { expect(request.data.imp[0].bidfloor).to.be.undefined; // make it respond with a non USD floor should not send it - getFloorResponse = {currency: 'EUR', floor: 1.0}; + getFloorResponse = { currency: 'EUR', floor: 1.0 }; [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); expect(request.data.imp[0].bidfloor).to.be.undefined; // make it respond with a non USD floor should not send it - getFloorResponse = {currency: 'EUR'}; + getFloorResponse = { currency: 'EUR' }; [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); expect(request.data.imp[0].bidfloor).to.be.undefined; // make it respond with USD floor and string floor - getFloorResponse = {currency: 'USD', floor: '1.23'}; + getFloorResponse = { currency: 'USD', floor: '1.23' }; [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); expect(request.data.imp[0].bidfloor).to.equal(1.23); // make it respond with USD floor and num floor - getFloorResponse = {currency: 'USD', floor: 1.23}; + getFloorResponse = { currency: 'USD', floor: 1.23 }; [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); expect(request.data.imp[0].bidfloor).to.equal(1.23); }); @@ -2327,7 +2335,7 @@ describe('the rubicon adapter', function () { // should have the aliases object sent to PBS expect(request.data.ext.prebid).to.haveOwnProperty('aliases'); - expect(request.data.ext.prebid.aliases).to.deep.equal({superRubicon: 'rubicon'}); + expect(request.data.ext.prebid.aliases).to.deep.equal({ superRubicon: 'rubicon' }); // should have the imp ext bidder params be under the alias name not rubicon superRubicon expect(request.data.imp[0].ext.prebid.bidder).to.have.property('superRubicon').that.is.an('object'); @@ -2368,7 +2376,7 @@ describe('the rubicon adapter', function () { maxbids: 2 }]; - config.setConfig({multibid: multibid}); + config.setConfig({ multibid: multibid }); const [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); @@ -2384,7 +2392,7 @@ describe('the rubicon adapter', function () { const payload = request.data; expect(payload.ext.prebid.analytics).to.not.be.undefined; - expect(payload.ext.prebid.analytics).to.deep.equal({'rubicon': {'client-analytics': true}}); + expect(payload.ext.prebid.analytics).to.deep.equal({ 'rubicon': { 'client-analytics': true } }); }); it('should pass client analytics to PBS endpoint if rubicon analytics adapter is included', function () { @@ -2394,7 +2402,7 @@ describe('the rubicon adapter', function () { const payload = request.data; expect(payload.ext.prebid.analytics).to.not.be.undefined; - expect(payload.ext.prebid.analytics).to.deep.equal({'rubicon': {'client-analytics': true}}); + expect(payload.ext.prebid.analytics).to.deep.equal({ 'rubicon': { 'client-analytics': true } }); }); it('should not pass client analytics to PBS endpoint if rubicon analytics adapter is not included', function () { @@ -2651,11 +2659,11 @@ describe('the rubicon adapter', function () { } }, content: { - data: [{foo: 'bar'}] + data: [{ foo: 'bar' }] }, keywords: 'e,f', rating: '4-star', - data: [{foo: 'bar'}] + data: [{ foo: 'bar' }] }; const user = { ext: { @@ -2666,8 +2674,8 @@ describe('the rubicon adapter', function () { keywords: 'd', gender: 'M', yob: '1984', - geo: {country: 'ca'}, - data: [{foo: 'bar'}] + geo: { country: 'ca' }, + data: [{ foo: 'bar' }] }; const ortb2 = { @@ -2675,10 +2683,10 @@ describe('the rubicon adapter', function () { user }; - const [request] = spec.buildRequests(bidderRequest.bids.map((b) => ({...b, ortb2})), bidderRequest); + const [request] = spec.buildRequests(bidderRequest.bids.map((b) => ({ ...b, ortb2 })), bidderRequest); const expected = { - site: Object.assign({}, site, {keywords: bidderRequest.bids[0].params.keywords.join(',')}), + site: Object.assign({}, site, { keywords: bidderRequest.bids[0].params.keywords.join(',') }), user: Object.assign({}, user), siteData: Object.assign({}, site.ext.data, bidderRequest.bids[0].params.inventory), userData: Object.assign({}, user.ext.data, bidderRequest.bids[0].params.visitor), @@ -2762,13 +2770,13 @@ describe('the rubicon adapter', function () { it('should use the integration type provided in the config instead of the default', () => { const bidderRequest = createVideoBidderRequest(); - config.setConfig({rubicon: {int_type: 'testType'}}); + config.setConfig({ rubicon: { int_type: 'testType' } }); const [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); expect(request.data.ext.prebid.bidders.rubicon.integration).to.equal('testType'); }); it('should pass the user.id provided in the config', async function () { - config.setConfig({user: {id: '123'}}); + config.setConfig({ user: { id: '123' } }); const bidderRequest = createVideoBidderRequest(); sandbox.stub(Date, 'now').callsFake(() => @@ -2820,29 +2828,29 @@ describe('the rubicon adapter', 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({ + 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: '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', p2: '' }, {}])).to.deep.equal({ p1: ';;foo;', p2: '' }); - expect(spec.combineSlotUrlParams([{}, {p1: 'foo'}, {p1: ''}])).to.deep.equal({p1: ';foo;'}); + 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'}); + { 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;;'}); + { p1: 'foo', p2: 'test', p3: 'baz' }, + { p1: 'foo', p2: 'bar' }, + { p2: 'test' } + ])).to.deep.equal({ p1: 'foo;foo;', p2: 'test;bar;test', p3: 'baz;;' }); }); }); @@ -2949,13 +2957,13 @@ describe('the rubicon adapter', function () { it('Should return false if both banner and video mediaTypes are set and params.video is not an object', function () { removeVideoParamFromBidderRequest(bidderRequest); const bid = bidderRequest.bids[0]; - bid.mediaTypes.banner = {flag: true}; + bid.mediaTypes.banner = { flag: true }; expect(classifiedAsVideo(bid)).to.equal(false); }); it('Should return true if both banner and video mediaTypes are set and params.video is an object', function () { removeVideoParamFromBidderRequest(bidderRequest); const bid = bidderRequest.bids[0]; - bid.mediaTypes.banner = {flag: true}; + bid.mediaTypes.banner = { flag: true }; bid.params.video = {}; expect(classifiedAsVideo(bid)).to.equal(true); }); @@ -3113,8 +3121,8 @@ describe('the rubicon adapter', function () { describe('with duplicate adUnitCodes', () => { it('should increment PBS request imp[].id starting at 2', () => { - const nativeBidderRequest = addNativeToBidRequest(bidderRequest, {twin: true}); - const request = converter.toORTB({bidderRequest: nativeBidderRequest, bidRequests: nativeBidderRequest.bids}); + const nativeBidderRequest = addNativeToBidRequest(bidderRequest, { twin: true }); + const request = converter.toORTB({ bidderRequest: nativeBidderRequest, bidRequests: nativeBidderRequest.bids }); for (let i = 0; i < nativeBidderRequest.bids.length; i++) { var adUnitCode = nativeBidderRequest.bids[i].adUnitCode; if (i === 0) { @@ -3191,7 +3199,7 @@ describe('the rubicon adapter', function () { ] }; - const bids = spec.interpretResponse({body: response}, { + const bids = spec.interpretResponse({ body: response }, { bidRequest: bidderRequest.bids[0] }); @@ -3294,7 +3302,7 @@ describe('the rubicon adapter', function () { netRevenue: false } }); - let bids = spec.interpretResponse({body: response}, { + let bids = spec.interpretResponse({ body: response }, { bidRequest: bidderRequest.bids[0] }); expect(bids).to.be.lengthOf(2); @@ -3307,7 +3315,7 @@ describe('the rubicon adapter', function () { netRevenue: true } }); - bids = spec.interpretResponse({body: response}, { + bids = spec.interpretResponse({ body: response }, { bidRequest: bidderRequest.bids[0] }); expect(bids).to.be.lengthOf(2); @@ -3320,7 +3328,7 @@ describe('the rubicon adapter', function () { netRevenue: undefined } }); - bids = spec.interpretResponse({body: response}, { + bids = spec.interpretResponse({ body: response }, { bidRequest: bidderRequest.bids[0] }); expect(bids).to.be.lengthOf(2); @@ -3333,7 +3341,7 @@ describe('the rubicon adapter', function () { netRevenue: 'someString' } }); - bids = spec.interpretResponse({body: response}, { + bids = spec.interpretResponse({ body: response }, { bidRequest: bidderRequest.bids[0] }); expect(bids).to.be.lengthOf(2); @@ -3379,7 +3387,7 @@ describe('the rubicon adapter', function () { } ]; - let bids = spec.interpretResponse({body: response}, { + let bids = spec.interpretResponse({ body: response }, { bidRequest: bidderRequest.bids[0] }); expect(bids[0].creativeId).to.equal('8-7'); @@ -3405,7 +3413,7 @@ describe('the rubicon adapter', function () { } ]; - bids = spec.interpretResponse({body: response}, { + bids = spec.interpretResponse({ body: response }, { bidRequest: bidderRequest.bids[0] }); expect(bids[0].creativeId).to.equal('-'); @@ -3432,7 +3440,7 @@ describe('the rubicon adapter', function () { } ]; - bids = spec.interpretResponse({body: response}, { + bids = spec.interpretResponse({ body: response }, { bidRequest: bidderRequest.bids[0] }); expect(bids[0].creativeId).to.equal('8-'); @@ -3459,7 +3467,7 @@ describe('the rubicon adapter', function () { } ]; - bids = spec.interpretResponse({body: response}, { + bids = spec.interpretResponse({ body: response }, { bidRequest: bidderRequest.bids[0] }); expect(bids[0].creativeId).to.equal('-7'); @@ -3484,7 +3492,7 @@ describe('the rubicon adapter', function () { }] }; - const bids = spec.interpretResponse({body: response}, { + const bids = spec.interpretResponse({ body: response }, { bidRequest: bidderRequest.bids[0] }); @@ -3561,7 +3569,7 @@ describe('the rubicon adapter', function () { } ] }; - const bids = spec.interpretResponse({body: response}, { + const bids = spec.interpretResponse({ body: response }, { bidRequest: bidderRequest.bids[0] }); expect(bids).to.be.lengthOf(2); @@ -3701,9 +3709,9 @@ describe('the rubicon adapter', function () { ] }; - config.setConfig({ multibid: [{bidder: 'rubicon', maxbids: 2, targetbiddercodeprefix: 'rubi'}] }); + config.setConfig({ multibid: [{ bidder: 'rubicon', maxbids: 2, targetbiddercodeprefix: 'rubi' }] }); - const bids = spec.interpretResponse({body: response}, { + const bids = spec.interpretResponse({ body: response }, { bidRequest: bidRequests }); @@ -3727,7 +3735,7 @@ describe('the rubicon adapter', function () { 'ads': [] }; - const bids = spec.interpretResponse({body: response}, { + const bids = spec.interpretResponse({ body: response }, { bidRequest: bidderRequest.bids[0] }); @@ -3751,7 +3759,7 @@ describe('the rubicon adapter', function () { }] }; - const bids = spec.interpretResponse({body: response}, { + const bids = spec.interpretResponse({ body: response }, { bidRequest: bidderRequest.bids[0] }); @@ -3761,7 +3769,7 @@ describe('the rubicon adapter', function () { it('should handle an error because of malformed json response', function () { const response = '{test{'; - const bids = spec.interpretResponse({body: response}, { + const bids = spec.interpretResponse({ body: response }, { bidRequest: bidderRequest.bids[0] }); @@ -3787,7 +3795,7 @@ describe('the rubicon adapter', function () { }] }; - const bids = spec.interpretResponse({body: response}, { + const bids = spec.interpretResponse({ body: response }, { bidRequest: [utils.deepClone(bidderRequest.bids[0])] }); @@ -3853,17 +3861,109 @@ describe('the rubicon adapter', function () { } ] }; - const bids = spec.interpretResponse({body: response}, { + const bids = spec.interpretResponse({ body: response }, { bidRequest: bidderRequest.bids[0] }); expect(bids[0].meta.mediaType).to.equal('banner'); expect(bids[1].meta.mediaType).to.equal('video'); }); + it('should handle primaryCatId and secondaryCatIds when bid.bid_cat is present in response', function () { + const response = { + 'status': 'ok', + 'account_id': 14062, + 'site_id': 70608, + 'zone_id': 530022, + 'size_id': 15, + 'alt_size_ids': [ + 43 + ], + 'tracking': '', + 'inventory': {}, + 'ads': [ + { + 'status': 'ok', + 'impression_id': '153dc240-8229-4604-b8f5-256933b9374c', + 'size_id': '15', + 'ad_id': '6', + 'advertiser': 7, + 'network': 8, + 'creative_id': 'crid-9', + 'type': 'script', + 'script': 'alert(\'foo\')', + 'campaign_id': 10, + 'cpm': 0.811, + 'emulated_format': 'video', + 'targeting': [ + { + 'key': 'rpfl_14062', + 'values': [ + '15_tier_all_test' + ] + } + ], + 'bid_cat': ['IAB1-1'] + }, + { + 'status': 'ok', + 'impression_id': '153dc240-8229-4604-b8f5-256933b9374d', + 'size_id': '43', + 'ad_id': '7', + 'advertiser': 7, + 'network': 8, + 'creative_id': 'crid-9', + 'type': 'script', + 'script': 'alert(\'foo\')', + 'campaign_id': 10, + 'cpm': 0.911, + 'targeting': [ + { + 'key': 'rpfl_14062', + 'values': [ + '43_tier_all_test' + ] + } + ], + 'bid_cat': ['IAB1-2', 'IAB1-3'] + }, + { + 'status': 'ok', + 'impression_id': '153dc240-8229-4604-b8f5-256933b9374d', + 'size_id': '43', + 'ad_id': '7', + 'advertiser': 7, + 'network': 8, + 'creative_id': 'crid-9', + 'type': 'script', + 'script': 'alert(\'foo\')', + 'campaign_id': 10, + 'cpm': 10, + 'targeting': [ + { + 'key': 'rpfl_14062', + 'values': [ + '43_tier_all_test' + ] + } + ] + } + ] + }; + const bids = spec.interpretResponse({ body: response }, { + bidRequest: bidderRequest.bids[0] + }); + expect(bids[0].meta.primaryCatId).to.be.undefined; + expect(bids[0].meta.secondaryCatIds).to.be.undefined; + expect(bids[1].meta.primaryCatId).to.equal(response.ads[1].bid_cat[0]); + expect(bids[1].meta.secondaryCatIds).to.deep.equal(response.ads[1].bid_cat.slice(1)); + expect(bids[2].meta.primaryCatId).to.equal(response.ads[0].bid_cat[0]); + expect(bids[2].meta.secondaryCatIds).to.be.undefined; + }); + describe('singleRequest enabled', function () { it('handles bidRequest of type Array and returns associated adUnits', function () { const overrideMap = []; - overrideMap[0] = {impression_id: '1'}; + overrideMap[0] = { impression_id: '1' }; const stubAds = []; for (let i = 0; i < 10; i++) { @@ -3886,7 +3986,7 @@ describe('the rubicon adapter', function () { 'inventory': {}, 'ads': stubAds } - }, {bidRequest: stubBids}); + }, { bidRequest: stubBids }); expect(bids).to.be.a('array').with.lengthOf(10); bids.forEach((bid) => { @@ -3919,7 +4019,7 @@ describe('the rubicon adapter', function () { it('handles incorrect adUnits length by returning all bids with matching ads', function () { const overrideMap = []; - overrideMap[0] = {impression_id: '1'}; + overrideMap[0] = { impression_id: '1' }; const stubAds = []; for (let i = 0; i < 6; i++) { @@ -3942,7 +4042,7 @@ describe('the rubicon adapter', function () { 'inventory': {}, 'ads': stubAds } - }, {bidRequest: stubBids}); + }, { bidRequest: stubBids }); // no bids expected because response didn't match requested bid number expect(bids).to.be.a('array').with.lengthOf(6); @@ -3953,11 +4053,11 @@ describe('the rubicon adapter', function () { // 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'}; + 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)); @@ -3979,7 +4079,7 @@ describe('the rubicon adapter', function () { 'inventory': {}, 'ads': stubAds } - }, {bidRequest: stubBids}); + }, { bidRequest: stubBids }); expect(bids).to.be.a('array').with.lengthOf(6); bids.forEach((bid) => { @@ -4048,9 +4148,9 @@ describe('the rubicon adapter', function () { }], }; - const request = converter.toORTB({bidderRequest, bidRequests: bidderRequest.bids}); + const request = converter.toORTB({ bidderRequest, bidRequests: bidderRequest.bids }); - const bids = spec.interpretResponse({body: response}, {data: request}); + const bids = spec.interpretResponse({ body: response }, { data: request }); expect(bids).to.be.lengthOf(1); @@ -4059,7 +4159,7 @@ describe('the rubicon adapter', function () { expect(bids[0].cpm).to.equal(2); expect(bids[0].ttl).to.equal(360); expect(bids[0].netRevenue).to.equal(true); - expect(bids[0].adserverTargeting).to.deep.equal({hb_uuid: '0c498f63-5111-4bed-98e2-9be7cb932a64'}); + expect(bids[0].adserverTargeting).to.deep.equal({ hb_uuid: '0c498f63-5111-4bed-98e2-9be7cb932a64' }); expect(bids[0].mediaType).to.equal('video'); expect(bids[0].meta.mediaType).to.equal('video'); expect(String(bids[0].meta.advertiserDomains)).to.equal('test.com'); @@ -4076,18 +4176,18 @@ describe('the rubicon adapter', function () { describe('for native', () => { it('should get a native bid', () => { const nativeBidderRequest = addNativeToBidRequest(bidderRequest); - const request = converter.toORTB({bidderRequest: nativeBidderRequest, bidRequests: nativeBidderRequest.bids}); - const response = getNativeResponse({impid: request.imp[0].id}); - const bids = spec.interpretResponse({body: response}, {data: request}); + const request = converter.toORTB({ bidderRequest: nativeBidderRequest, bidRequests: nativeBidderRequest.bids }); + const response = getNativeResponse({ impid: request.imp[0].id }); + const bids = spec.interpretResponse({ body: response }, { data: request }); expect(bids).to.have.nested.property('[0].native'); }); it('should set 0 to bids width and height if `w` and `h` in response object not defined', () => { const nativeBidderRequest = addNativeToBidRequest(bidderRequest); - const request = converter.toORTB({bidderRequest: nativeBidderRequest, bidRequests: nativeBidderRequest.bids}); - const response = getNativeResponse({impid: request.imp[0].id}); + const request = converter.toORTB({ bidderRequest: nativeBidderRequest, bidRequests: nativeBidderRequest.bids }); + const response = getNativeResponse({ impid: request.imp[0].id }); delete response.seatbid[0].bid[0].w; delete response.seatbid[0].bid[0].h - const bids = spec.interpretResponse({body: response}, {data: request}); + const bids = spec.interpretResponse({ body: response }, { data: request }); expect(bids[0].width).to.equal(0); expect(bids[0].height).to.equal(0); }); @@ -4098,14 +4198,16 @@ describe('the rubicon adapter', function () { describe('for outstream video', function () { const sandbox = sinon.createSandbox(); beforeEach(function () { - config.setConfig({rubicon: { - rendererConfig: { - align: 'left', - closeButton: true, - collapse: false - }, - rendererUrl: 'https://example.test/renderer.js' - }}); + config.setConfig({ + rubicon: { + rendererConfig: { + align: 'left', + closeButton: true, + collapse: false + }, + rendererUrl: 'https://example.test/renderer.js' + } + }); window.MagniteApex = { renderAd: function() { return null; @@ -4150,9 +4252,9 @@ describe('the rubicon adapter', function () { }], }; - const request = converter.toORTB({bidderRequest, bidRequests: bidderRequest.bids}); + const request = converter.toORTB({ bidderRequest, bidRequests: bidderRequest.bids }); - const bids = spec.interpretResponse({body: response}, { data: request }); + const bids = spec.interpretResponse({ body: response }, { data: request }); expect(bids).to.be.lengthOf(1); @@ -4161,7 +4263,7 @@ describe('the rubicon adapter', function () { expect(bids[0].cpm).to.equal(2); expect(bids[0].ttl).to.equal(360); expect(bids[0].netRevenue).to.equal(true); - expect(bids[0].adserverTargeting).to.deep.equal({hb_uuid: '0c498f63-5111-4bed-98e2-9be7cb932a64'}); + expect(bids[0].adserverTargeting).to.deep.equal({ hb_uuid: '0c498f63-5111-4bed-98e2-9be7cb932a64' }); expect(bids[0].mediaType).to.equal('video'); expect(bids[0].meta.mediaType).to.equal('video'); expect(String(bids[0].meta.advertiserDomains)).to.equal('test.com'); @@ -4213,11 +4315,11 @@ describe('the rubicon adapter', function () { }], }; - const request = converter.toORTB({bidderRequest, bidRequests: bidderRequest.bids}); + const request = converter.toORTB({ bidderRequest, bidRequests: bidderRequest.bids }); sinon.spy(window.MagniteApex, 'renderAd'); - const bids = spec.interpretResponse({body: response}, {data: request}); + const bids = spec.interpretResponse({ body: response }, { data: request }); const bid = bids[0]; bid.adUnitCode = 'outstream_video1_placement'; const adUnit = document.createElement('div'); @@ -4230,14 +4332,14 @@ describe('the rubicon adapter', function () { const renderCall = window.MagniteApex.renderAd.getCall(0); expect(renderCall.args[0]).to.deep.equal({ closeButton: true, - collapse: false, height: 320, label: undefined, placement: { align: 'left', - attachTo: adUnitSelector, - position: 'append', + attachTo: adUnit, + position: 'prepend', }, + replay: true, vastUrl: 'https://test.com/vast.xml', width: 640 }); @@ -4283,11 +4385,11 @@ describe('the rubicon adapter', function () { }], }; - const request = converter.toORTB({bidderRequest, bidRequests: bidderRequest.bids}); + const request = converter.toORTB({ bidderRequest, bidRequests: bidderRequest.bids }); sinon.spy(window.MagniteApex, 'renderAd'); - const bids = spec.interpretResponse({body: response}, {data: request}); + const bids = spec.interpretResponse({ body: response }, { data: request }); const bid = bids[0]; bid.adUnitCode = 'outstream_video1_placement'; const adUnit = document.createElement('div'); @@ -4300,14 +4402,14 @@ describe('the rubicon adapter', function () { const renderCall = window.MagniteApex.renderAd.getCall(0); expect(renderCall.args[0]).to.deep.equal({ closeButton: true, - collapse: false, height: 480, label: undefined, placement: { align: 'left', - attachTo: adUnitSelector, - position: 'append', + attachTo: adUnit, + position: 'prepend', }, + replay: true, vastUrl: 'https://test.com/vast.xml', width: 640 }); @@ -4319,7 +4421,7 @@ describe('the rubicon adapter', function () { describe('config with integration type', () => { it('should use the integration type provided in the config instead of the default', () => { - config.setConfig({rubicon: {int_type: 'testType'}}); + config.setConfig({ rubicon: { int_type: 'testType' } }); const [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); expect(new URLSearchParams(request.data).get('tk_flint')).to.equal('testType_v$prebid.version$'); }); @@ -4335,24 +4437,24 @@ describe('the rubicon adapter', function () { iframeEnabled: true }); - expect(syncs).to.deep.equal({type: 'iframe', url: emilyUrl}); + expect(syncs).to.deep.equal({ type: 'iframe', url: emilyUrl }); }); it('should register the Emily iframe more than once', function () { let syncs = spec.getUserSyncs({ iframeEnabled: true }); - expect(syncs).to.deep.equal({type: 'iframe', url: emilyUrl}); + expect(syncs).to.deep.equal({ type: 'iframe', url: emilyUrl }); // when called again, should still have only been called once syncs = spec.getUserSyncs({ iframeEnabled: true }); - expect(syncs).to.deep.equal({type: 'iframe', url: emilyUrl}); + expect(syncs).to.deep.equal({ type: 'iframe', url: emilyUrl }); }); it('should pass gdpr params if consent is true', function () { - expect(spec.getUserSyncs({iframeEnabled: true}, {}, { + expect(spec.getUserSyncs({ iframeEnabled: true }, {}, { gdprApplies: true, consentString: 'foo' })).to.deep.equal({ type: 'iframe', url: `${emilyUrl}?gdpr=1&gdpr_consent=foo` @@ -4360,7 +4462,7 @@ describe('the rubicon adapter', function () { }); it('should pass gdpr params if consent is false', function () { - expect(spec.getUserSyncs({iframeEnabled: true}, {}, { + expect(spec.getUserSyncs({ iframeEnabled: true }, {}, { gdprApplies: false, consentString: 'foo' })).to.deep.equal({ type: 'iframe', url: `${emilyUrl}?gdpr=0&gdpr_consent=foo` @@ -4368,7 +4470,7 @@ describe('the rubicon adapter', function () { }); it('should pass gdpr param gdpr_consent only when gdprApplies is undefined', function () { - expect(spec.getUserSyncs({iframeEnabled: true}, {}, { + expect(spec.getUserSyncs({ iframeEnabled: true }, {}, { consentString: 'foo' })).to.deep.equal({ type: 'iframe', url: `${emilyUrl}?gdpr_consent=foo` @@ -4376,13 +4478,13 @@ describe('the rubicon adapter', function () { }); it('should pass no params if gdpr consentString is not defined', function () { - expect(spec.getUserSyncs({iframeEnabled: true}, {}, {})).to.deep.equal({ + expect(spec.getUserSyncs({ iframeEnabled: true }, {}, {})).to.deep.equal({ type: 'iframe', url: `${emilyUrl}` }); }); it('should pass no params if gdpr consentString is a number', function () { - expect(spec.getUserSyncs({iframeEnabled: true}, {}, { + expect(spec.getUserSyncs({ iframeEnabled: true }, {}, { consentString: 0 })).to.deep.equal({ type: 'iframe', url: `${emilyUrl}` @@ -4390,7 +4492,7 @@ describe('the rubicon adapter', function () { }); it('should pass no params if gdpr consentString is null', function () { - expect(spec.getUserSyncs({iframeEnabled: true}, {}, { + expect(spec.getUserSyncs({ iframeEnabled: true }, {}, { consentString: null })).to.deep.equal({ type: 'iframe', url: `${emilyUrl}` @@ -4398,7 +4500,7 @@ describe('the rubicon adapter', function () { }); it('should pass no params if gdpr consentString is a object', function () { - expect(spec.getUserSyncs({iframeEnabled: true}, {}, { + expect(spec.getUserSyncs({ iframeEnabled: true }, {}, { consentString: {} })).to.deep.equal({ type: 'iframe', url: `${emilyUrl}` @@ -4406,19 +4508,19 @@ describe('the rubicon adapter', function () { }); it('should pass no params if gdpr is not defined', function () { - expect(spec.getUserSyncs({iframeEnabled: true}, {}, undefined)).to.deep.equal({ + expect(spec.getUserSyncs({ iframeEnabled: true }, {}, undefined)).to.deep.equal({ type: 'iframe', url: `${emilyUrl}` }); }); it('should pass us_privacy if uspConsent is defined', function () { - expect(spec.getUserSyncs({iframeEnabled: true}, {}, undefined, '1NYN')).to.deep.equal({ + expect(spec.getUserSyncs({ iframeEnabled: true }, {}, undefined, '1NYN')).to.deep.equal({ type: 'iframe', url: `${emilyUrl}?us_privacy=1NYN` }); }); it('should pass us_privacy after gdpr if both are present', function () { - expect(spec.getUserSyncs({iframeEnabled: true}, {}, { + expect(spec.getUserSyncs({ iframeEnabled: true }, {}, { consentString: 'foo' }, '1NYN')).to.deep.equal({ type: 'iframe', url: `${emilyUrl}?gdpr_consent=foo&us_privacy=1NYN` @@ -4426,7 +4528,7 @@ describe('the rubicon adapter', function () { }); it('should pass gdprApplies', function () { - expect(spec.getUserSyncs({iframeEnabled: true}, {}, { + expect(spec.getUserSyncs({ iframeEnabled: true }, {}, { gdprApplies: true }, '1NYN')).to.deep.equal({ type: 'iframe', url: `${emilyUrl}?gdpr=1&us_privacy=1NYN` @@ -4434,7 +4536,7 @@ describe('the rubicon adapter', function () { }); it('should pass all correctly', function () { - expect(spec.getUserSyncs({iframeEnabled: true}, {}, { + expect(spec.getUserSyncs({ iframeEnabled: true }, {}, { gdprApplies: true, consentString: 'foo' }, '1NYN')).to.deep.equal({ @@ -4443,7 +4545,7 @@ describe('the rubicon adapter', function () { }); it('should pass gpp params when gppConsent is present', function () { - expect(spec.getUserSyncs({iframeEnabled: true}, {}, {}, undefined, { + expect(spec.getUserSyncs({ iframeEnabled: true }, {}, {}, undefined, { gppString: 'foo', applicableSections: [2] })).to.deep.equal({ @@ -4452,7 +4554,7 @@ describe('the rubicon adapter', function () { }); it('should pass multiple sid\'s when multiple are present', function () { - expect(spec.getUserSyncs({iframeEnabled: true}, {}, {}, undefined, { + expect(spec.getUserSyncs({ iframeEnabled: true }, {}, {}, undefined, { gppString: 'foo', applicableSections: [2, 5] })).to.deep.equal({ @@ -4463,7 +4565,7 @@ describe('the rubicon adapter', function () { describe('get price granularity', function () { it('should return correct buckets for all price granularity values', function () { - const CUSTOM_PRICE_BUCKET_ITEM = {max: 5, increment: 0.5}; + const CUSTOM_PRICE_BUCKET_ITEM = { max: 5, increment: 0.5 }; const mockConfig = { priceGranularity: undefined, @@ -4476,12 +4578,12 @@ describe('the rubicon adapter', function () { }); [ - {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}, + { 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; @@ -4630,12 +4732,12 @@ describe('the rubicon adapter', function () { const syncs = spec.getUserSyncs({ iframeEnabled: true }); - expect(syncs).to.deep.equal({type: 'iframe', url: 'https://eus-qa.rubiconproject.com/usync.html'}); + expect(syncs).to.deep.equal({ type: 'iframe', url: 'https://eus-qa.rubiconproject.com/usync.html' }); }); }); }); -function addNativeToBidRequest(bidderRequest, options = {twin: false}) { +function addNativeToBidRequest(bidderRequest, options = { twin: false }) { const nativeOrtbRequest = { assets: [{ id: 0, @@ -4692,7 +4794,7 @@ function addNativeToBidRequest(bidderRequest, options = {twin: false}) { return bidderRequest; } -function getNativeResponse(options = {impid: 1234}) { +function getNativeResponse(options = { impid: 1234 }) { return { 'id': 'd7786a80-bfb4-4541-859f-225a934e81d4', 'seatbid': [ diff --git a/test/spec/modules/rules_spec.js b/test/spec/modules/rules_spec.js new file mode 100644 index 00000000000..24f73a33513 --- /dev/null +++ b/test/spec/modules/rules_spec.js @@ -0,0 +1,856 @@ +import { expect } from 'chai'; +import * as rulesModule from 'modules/rules/index.ts'; +import * as utils from 'src/utils.js'; +import * as storageManager from 'src/storageManager.js'; +import * as analyticsAdapter from 'libraries/analyticsAdapter/AnalyticsAdapter.ts'; +import { isActivityAllowed } from 'src/activities/rules.js'; +import { activityParams } from 'src/activities/activityParams.js'; +import { ACTIVITY_FETCH_BIDS, ACTIVITY_ADD_BID_RESPONSE } from 'src/activities/activities.js'; +import { MODULE_TYPE_BIDDER } from 'src/activities/modules.ts'; +import { config } from 'src/config.js'; + +describe('Rules Module', function() { + let sandbox; + let logWarnStub; + let logInfoStub; + let newStorageManagerStub; + + beforeEach(function() { + sandbox = sinon.createSandbox(); + + logWarnStub = sandbox.stub(utils, 'logWarn'); + logInfoStub = sandbox.stub(utils, 'logInfo'); + + const mockStorageManager = { + localStorageIsEnabled: sandbox.stub().returns(true), + getDataFromLocalStorage: sandbox.stub().returns(null), + setDataInLocalStorage: sandbox.stub() + }; + newStorageManagerStub = sandbox.stub(storageManager, 'newStorageManager').returns(mockStorageManager); + }); + + afterEach(function() { + sandbox.restore(); + config.resetConfig(); + rulesModule.reset(); + }); + + describe('getAssignedModelGroups', function() { + it('should select model group based on weights', function() { + const rulesets = [{ + name: 'testRuleSet', + stage: 'processed-auction-request', + modelGroups: [{ + weight: 50, + selected: false, + analyticsKey: 'testKey1', + schema: [], + rules: [] + }, { + weight: 50, + selected: false, + analyticsKey: 'testKey2', + schema: [], + rules: [] + }] + }]; + + // Mock Math.random to return 0.15 (15 < 50, so first group should be selected) + // randomValue = 0.15 * 100 = 15, 15 < 50 so first group selected + sandbox.stub(Math, 'random').returns(0.15); + + const result = rulesModule.getAssignedModelGroups(rulesets); + + // Verify that first model group was selected in the returned result + expect(result[0].modelGroups[0].selected).to.be.true; + expect(result[0].modelGroups[1].selected).to.be.false; + // Verify original was not mutated + expect(rulesets[0].modelGroups[0].selected).to.be.false; + expect(rulesets[0].modelGroups[1].selected).to.be.false; + }); + + it('should use default weight of 100 when weight is not specified', function() { + const rulesets = [{ + name: 'testRuleSet', + stage: 'processed-auction-request', + modelGroups: [{ + // weight not specified, should default to 100 + selected: false, + analyticsKey: 'testKey1', + schema: [], + rules: [] + }, { + // weight not specified, should default to 100 + selected: false, + analyticsKey: 'testKey2', + schema: [], + rules: [] + }, { + // weight not specified, should default to 100 + selected: false, + analyticsKey: 'testKey3', + schema: [], + rules: [] + }] + }]; + + // Mock Math.random to return value that selects last group + // randomValue = 0.85 * 300 = 255 + // Cumulative weights: [100, 200, 300] + // First group: 255 < 100? No + // Second group: 255 < 200? No + // Third group: 255 < 300? Yes, selected! + sandbox.stub(Math, 'random').returns(0.85); + + const result = rulesModule.getAssignedModelGroups(rulesets); + + expect(result[0].modelGroups[0].selected).to.be.false; + expect(result[0].modelGroups[1].selected).to.be.false; + expect(result[0].modelGroups[2].selected).to.be.true; + // Verify original was not mutated + expect(rulesets[0].modelGroups[0].selected).to.be.false; + expect(rulesets[0].modelGroups[1].selected).to.be.false; + expect(rulesets[0].modelGroups[2].selected).to.be.false; + }); + + it('should select correctly regardless of weight order (descending)', function() { + const rulesets = [{ + name: 'testRuleSet', + stage: 'processed-auction-request', + modelGroups: [{ + weight: 100, // largest first + selected: false, + analyticsKey: 'testKey1', + schema: [], + rules: [] + }, { + weight: 50, // medium + selected: false, + analyticsKey: 'testKey2', + schema: [], + rules: [] + }, { + weight: 25, // smallest last + selected: false, + analyticsKey: 'testKey3', + schema: [], + rules: [] + }] + }]; + + // randomValue = 0.3 * 175 = 52.5 + // Cumulative weights: [100, 150, 175] + // First group: 52.5 < 100? Yes, selected! + sandbox.stub(Math, 'random').returns(0.3); + + const result = rulesModule.getAssignedModelGroups(rulesets); + + expect(result[0].modelGroups[0].selected).to.be.true; + expect(result[0].modelGroups[1].selected).to.be.false; + expect(result[0].modelGroups[2].selected).to.be.false; + }); + + it('should select correctly regardless of weight order (mixed)', function() { + const rulesets = [{ + name: 'testRuleSet', + stage: 'processed-auction-request', + modelGroups: [{ + weight: 30, // medium + selected: false, + analyticsKey: 'testKey1', + schema: [], + rules: [] + }, { + weight: 100, // largest in middle + selected: false, + analyticsKey: 'testKey2', + schema: [], + rules: [] + }, { + weight: 20, // smallest last + selected: false, + analyticsKey: 'testKey3', + schema: [], + rules: [] + }] + }]; + + // randomValue = 0.6 * 150 = 90 + // Cumulative weights: [30, 130, 150] + // First group: 90 < 30? No + // Second group: 90 < 130? Yes, selected! + sandbox.stub(Math, 'random').returns(0.6); + + const result = rulesModule.getAssignedModelGroups(rulesets); + + expect(result[0].modelGroups[0].selected).to.be.false; + expect(result[0].modelGroups[1].selected).to.be.true; + expect(result[0].modelGroups[2].selected).to.be.false; + }); + + it('should select last group when randomValue is in last range', function() { + const rulesets = [{ + name: 'testRuleSet', + stage: 'processed-auction-request', + modelGroups: [{ + weight: 10, + selected: false, + analyticsKey: 'testKey1', + schema: [], + rules: [] + }, { + weight: 20, + selected: false, + analyticsKey: 'testKey2', + schema: [], + rules: [] + }, { + weight: 70, // largest weight + selected: false, + analyticsKey: 'testKey3', + schema: [], + rules: [] + }] + }]; + + // randomValue = 0.8 * 100 = 80 + // Cumulative weights: [10, 30, 100] + // First group: 80 < 10? No + // Second group: 80 < 30? No + // Third group: 80 < 100? Yes, selected! + sandbox.stub(Math, 'random').returns(0.8); + + const result = rulesModule.getAssignedModelGroups(rulesets); + + expect(result[0].modelGroups[0].selected).to.be.false; + expect(result[0].modelGroups[1].selected).to.be.false; + expect(result[0].modelGroups[2].selected).to.be.true; + }); + + it('should select first group when randomValue is very small', function() { + const rulesets = [{ + name: 'testRuleSet', + stage: 'processed-auction-request', + modelGroups: [{ + weight: 10, // smallest + selected: false, + analyticsKey: 'testKey1', + schema: [], + rules: [] + }, { + weight: 50, + selected: false, + analyticsKey: 'testKey2', + schema: [], + rules: [] + }, { + weight: 40, + selected: false, + analyticsKey: 'testKey3', + schema: [], + rules: [] + }] + }]; + + // randomValue = 0.01 * 100 = 1 + // Cumulative weights: [10, 60, 100] + // First group: 1 < 10? Yes, selected! + sandbox.stub(Math, 'random').returns(0.01); + + const result = rulesModule.getAssignedModelGroups(rulesets); + + expect(result[0].modelGroups[0].selected).to.be.true; + expect(result[0].modelGroups[1].selected).to.be.false; + expect(result[0].modelGroups[2].selected).to.be.false; + }); + }); + + describe('evaluateConfig', function() { + beforeEach(function() { + rulesModule.registerActivities(); + }); + + [ + ['processed-auction-request', ACTIVITY_FETCH_BIDS], + ['processed-auction', ACTIVITY_ADD_BID_RESPONSE] + ].forEach(([stage, activity]) => { + it(`should exclude bidder when it matches bidders list for ${stage} stage`, function() { + const rulesJson = { + enabled: true, + timestamp: '1234567890', + ruleSets: [{ + name: 'testRuleSet', + stage: stage, + version: '1.0', + modelGroups: [{ + weight: 100, + selected: true, + analyticsKey: 'testAnalyticsKey', + schema: [{ function: 'adUnitCode', args: [] }], + rules: [{ + conditions: ['adUnit-0000'], + results: [{ + function: 'excludeBidders', + args: [{ + bidders: ['bidder1'], + analyticsValue: 'excluded' + }] + }] + }] + }] + }] + }; + + sandbox.stub(Math, 'random').returns(0.5); + + const bidder1Params = activityParams(MODULE_TYPE_BIDDER, 'bidder1', { + adUnit: { code: 'adUnit-0000' }, + auctionId: 'test-auction-id' + }); + + const bidder2Params = activityParams(MODULE_TYPE_BIDDER, 'bidder2', { + adUnit: { code: 'adUnit-0000' }, + auctionId: 'test-auction-id' + }); + + expect(isActivityAllowed(activity, bidder1Params)).to.be.true; + expect(isActivityAllowed(activity, bidder2Params)).to.be.true; + + rulesModule.evaluateConfig(rulesJson, 'test-auction-id'); + + expect(isActivityAllowed(activity, bidder1Params)).to.be.false; + expect(isActivityAllowed(activity, bidder2Params)).to.be.true; + }); + + it(`should include only bidder when it matches bidders list for ${stage} stage`, function() { + const rulesJson = { + enabled: true, + timestamp: '1234567890', + ruleSets: [{ + name: 'testRuleSet', + stage: stage, + version: '1.0', + modelGroups: [{ + weight: 100, + selected: true, + analyticsKey: 'testAnalyticsKey', + schema: [{ function: 'adUnitCode', args: [] }], + rules: [{ + conditions: ['adUnit-0000'], + results: [{ + function: 'includeBidders', + args: [{ + bidders: ['bidder1'], + analyticsValue: 'included' + }] + }] + }] + }] + }] + }; + + sandbox.stub(Math, 'random').returns(0.5); + + const bidder1Params = activityParams(MODULE_TYPE_BIDDER, 'bidder1', { + adUnit: { code: 'adUnit-0000' }, + auctionId: 'test-auction-id' + }); + + const bidder2Params = activityParams(MODULE_TYPE_BIDDER, 'bidder2', { + adUnit: { code: 'adUnit-0000' }, + auctionId: 'test-auction-id' + }); + + expect(isActivityAllowed(activity, bidder1Params)).to.be.true; + expect(isActivityAllowed(activity, bidder2Params)).to.be.true; + + rulesModule.evaluateConfig(rulesJson, 'test-auction-id'); + + expect(isActivityAllowed(activity, bidder1Params)).to.be.true; + expect(isActivityAllowed(activity, bidder2Params)).to.be.false; + }); + }); + + it('should execute default rules when provided and no rules match', function() { + const setLabelsStub = sandbox.stub(analyticsAdapter, 'setLabels'); + const rulesJson = { + enabled: true, + timestamp: '1234567890', + ruleSets: [{ + name: 'testRuleSet', + stage: 'processed-auction-request', + version: '1.0', + modelGroups: [{ + weight: 100, + selected: true, + analyticsKey: 'testAnalyticsKey', + schema: [{ + function: 'percent', + args: [5] + }], + default: [{ + function: 'logAtag', + args: { analyticsValue: 'default-allow' } + }], + rules: [{ + conditions: ['true'], + results: [{ + function: 'excludeBidders', + args: [{ + bidders: ['bidder1'], + analyticsValue: 'excluded' + }] + }] + }] + }] + }] + }; + + sandbox.stub(Math, 'random').returns(0.5); + const auctionId = 'test-auction-id'; + rulesModule.evaluateConfig(rulesJson, auctionId); + + const bidder1Params = activityParams(MODULE_TYPE_BIDDER, 'bidder1', { + auctionId + }); + + expect(isActivityAllowed(ACTIVITY_FETCH_BIDS, bidder1Params)).to.be.true; + + expect(setLabelsStub.calledWith({ [auctionId + '-testAnalyticsKey']: 'default-allow' })).to.be.true; + + setLabelsStub.resetHistory(); + }); + }); + + describe('getGlobalRandom', function() { + it('should return the same value for the same auctionId and call Math.random only once', function() { + const auctionId = 'test-auction-id'; + const otherAuctionId = 'other-auction-id'; + const mathRandomStub = sandbox.stub(Math, 'random').returns(0.42); + const auction1 = { auctionId: auctionId }; + const auction2 = { auctionId: otherAuctionId }; + const auctions = { + [auctionId]: auction1, + [otherAuctionId]: auction2 + } + + const index = { + getAuction: ({ auctionId }) => auctions[auctionId] + } + + const result1 = rulesModule.dep.getGlobalRandom(auctionId, index); + const result2 = rulesModule.dep.getGlobalRandom(auctionId, index); + const result3 = rulesModule.dep.getGlobalRandom(auctionId, index); + + expect(result1).to.equal(0.42); + expect(result2).to.equal(0.42); + expect(result3).to.equal(0.42); + expect(mathRandomStub.calledOnce).to.equal(true); + + mathRandomStub.returns(0.99); + const result4 = rulesModule.dep.getGlobalRandom(otherAuctionId, index); + + expect(result4).to.equal(0.99); + expect(mathRandomStub.calledTwice).to.equal(true); + }); + }); + + describe('evaluateSchema', function() { + it('should evaluate percent condition', function() { + sandbox.stub(rulesModule.dep, 'getGlobalRandom').returns(0.3); + const func = rulesModule.evaluateSchema('percent', [50], {}); + const result = func(); + // 30 < 50, so should return true + expect(result).to.be.true; + }); + + it('should evaluate adUnitCode condition', function() { + const context = { + adUnit: { + code: 'div-1' + } + }; + const func = rulesModule.evaluateSchema('adUnitCode', [], context); + expect(func()).to.equal('div-1'); + + const func2 = rulesModule.evaluateSchema('adUnitCode', [], context); + expect(func2()).to.equal('div-1'); + }); + + it('should evaluate adUnitCodeIn condition', function() { + const context = { + adUnit: { + code: 'div-1' + } + }; + const func = rulesModule.evaluateSchema('adUnitCodeIn', [['div-1', 'div-2']], context); + expect(func()).to.be.true; + + const func2 = rulesModule.evaluateSchema('adUnitCodeIn', [['div-3', 'div-4']], context); + expect(func2()).to.be.false; + }); + + it('should evaluate deviceCountry condition', function() { + const context = { + ortb2: { + device: { + geo: { + country: 'US' + } + } + } + }; + const func = rulesModule.evaluateSchema('deviceCountry', [], context); + expect(func()).to.equal('US'); + + const func2 = rulesModule.evaluateSchema('deviceCountry', [], context); + expect(func2()).to.equal('US'); + }); + + it('should evaluate deviceCountryIn condition', function() { + const context = { + ortb2: { + device: { + geo: { + country: 'US' + } + } + } + }; + const func = rulesModule.evaluateSchema('deviceCountryIn', [['US', 'UK']], context); + expect(func()).to.be.true; + + const func2 = rulesModule.evaluateSchema('deviceCountryIn', [['DE', 'FR']], context); + expect(func2()).to.be.false; + }); + + it('should evaluate channel condition', function() { + const context1 = { + ortb2: { + ext: { + prebid: { + channel: 'pbjs' + } + } + } + }; + const func1 = rulesModule.evaluateSchema('channel', [], context1); + expect(func1()).to.equal('web'); + }); + + it('should evaluate eidAvailable condition', function() { + const context1 = { + ortb2: { + user: { + eids: [{ source: 'test', id: '123' }] + } + } + }; + const func1 = rulesModule.evaluateSchema('eidAvailable', [], context1); + expect(func1()).to.be.true; + + const context2 = { + ortb2: { + user: { + eids: [] + } + } + }; + const func2 = rulesModule.evaluateSchema('eidAvailable', [], context2); + expect(func2()).to.be.false; + }); + + it('should evaluate userFpdAvailable condition', function() { + const context1 = { + ortb2: { + user: { + data: [{ name: 'test', segment: [] }] + } + } + }; + const func1 = rulesModule.evaluateSchema('userFpdAvailable', [], context1); + expect(func1()).to.be.true; + + const context2 = { + ortb2: { + user: { + ext: { + data: [{ name: 'test', segment: [] }] + } + } + } + }; + const func2 = rulesModule.evaluateSchema('userFpdAvailable', [], context2); + expect(func2()).to.be.true; + + const context3 = { + ortb2: { + user: {} + } + }; + const func3 = rulesModule.evaluateSchema('userFpdAvailable', [], context3); + expect(func3()).to.be.false; + }); + + it('should evaluate fpdAvailable condition', function() { + const context1 = { + ortb2: { + user: { + data: [{ name: 'test' }] + } + } + }; + const func1 = rulesModule.evaluateSchema('fpdAvailable', [], context1); + expect(func1()).to.be.true; + + const context2 = { + ortb2: { + site: { + content: { + data: [{ name: 'test' }] + } + } + } + }; + const func2 = rulesModule.evaluateSchema('fpdAvailable', [], context2); + expect(func2()).to.be.true; + + const context3 = { + ortb2: {} + }; + const func3 = rulesModule.evaluateSchema('fpdAvailable', [], context3); + expect(func3()).to.be.false; + }); + + it('should evaluate gppSidIn condition', function() { + const context1 = { + ortb2: { + regs: { + gpp_sid: [1, 2, 3] + } + } + }; + const func1 = rulesModule.evaluateSchema('gppSidIn', [[2]], context1); + expect(func1()).to.be.true; + + const func2 = rulesModule.evaluateSchema('gppSidIn', [[4]], context1); + expect(func2()).to.be.false; + }); + + it('should evaluate tcfInScope condition', function() { + const context1 = { + ortb2: { + regs: { + ext: { + gdpr: 1 + } + } + } + }; + const func1 = rulesModule.evaluateSchema('tcfInScope', [], context1); + expect(func1()).to.be.true; + + const context2 = { + regs: { + ext: { + gdpr: 0 + } + } + }; + const func2 = rulesModule.evaluateSchema('tcfInScope', [], context2); + expect(func2()).to.be.false; + }); + + it('should evaluate domain condition', function() { + const context1 = { + ortb2: { + site: { + domain: 'example.com' + } + } + }; + const func1 = rulesModule.evaluateSchema('domain', [], context1); + expect(func1()).to.equal('example.com'); + + const context2 = { + ortb2: { + app: { + domain: 'app.example.com' + } + } + }; + const func2 = rulesModule.evaluateSchema('domain', [], context2); + expect(func2()).to.equal('app.example.com'); + + const context3 = { + ortb2: {} + }; + const func3 = rulesModule.evaluateSchema('domain', [], context3); + expect(func3()).to.equal(''); + }); + + it('should evaluate domainIn condition', function() { + const context1 = { + ortb2: { + site: { + domain: 'example.com' + } + } + }; + const func1 = rulesModule.evaluateSchema('domainIn', [['example.com', 'test.com']], context1); + expect(func1()).to.be.true; + + const context2 = { + ortb2: { + app: { + domain: 'app.example.com' + } + } + }; + const func2 = rulesModule.evaluateSchema('domainIn', [['app.example.com']], context2); + expect(func2()).to.be.true; + + const func3 = rulesModule.evaluateSchema('domainIn', [['other.com']], context1); + expect(func3()).to.be.false; + }); + + it('should evaluate bundle condition', function() { + const context1 = { + ortb2: { + app: { + bundle: 'com.example.app' + } + } + }; + const func1 = rulesModule.evaluateSchema('bundle', [], context1); + expect(func1()).to.equal('com.example.app'); + + const context2 = { + ortb2: {} + }; + const func2 = rulesModule.evaluateSchema('bundle', [], context2); + expect(func2()).to.equal(''); + }); + + it('should evaluate bundleIn condition', function() { + const context1 = { + ortb2: { + app: { + bundle: 'com.example.app' + } + } + }; + const func1 = rulesModule.evaluateSchema('bundleIn', ['com.example.app'], context1); + expect(func1()).to.be.true; + + const func2 = rulesModule.evaluateSchema('bundleIn', [['com.other.app']], context1); + expect(func2()).to.be.false; + }); + + it('should evaluate mediaTypeIn condition', function() { + const context1 = { + adUnit: { + mediaTypes: { + banner: {}, + video: {} + } + } + }; + const func1 = rulesModule.evaluateSchema('mediaTypeIn', [['banner']], context1); + expect(func1()).to.be.true; + + const func2 = rulesModule.evaluateSchema('mediaTypeIn', [['native']], context1); + expect(func2()).to.be.false; + }); + + it('should evaluate deviceTypeIn condition', function() { + const context1 = { + ortb2: { + device: { + devicetype: 2 + } + } + }; + const func1 = rulesModule.evaluateSchema('deviceTypeIn', [[2, 3]], context1); + expect(func1()).to.be.true; + + const func2 = rulesModule.evaluateSchema('deviceTypeIn', [[4, 5]], context1); + expect(func2()).to.be.false; + }); + + it('should evaluate bidPrice condition', function() { + const context1 = { + bid: { + cpm: 5.50, + currency: 'USD' + } + }; + const func1 = rulesModule.evaluateSchema('bidPrice', ['gt', 'USD', 5.0], context1); + expect(func1()).to.be.true; + + const func2 = rulesModule.evaluateSchema('bidPrice', ['gt', 'USD', 6.0], context1); + expect(func2()).to.be.false; + + const func3 = rulesModule.evaluateSchema('bidPrice', ['lte', 'USD', 6.0], context1); + expect(func3()).to.be.true; + + const context3 = { + bid: { + cpm: 0, + currency: 'USD' + } + }; + const func4 = rulesModule.evaluateSchema('bidPrice', ['gt', 'USD', 1.0], context3); + expect(func4()).to.be.false; + }); + + it('should return null function for unknown schema function', function() { + const func = rulesModule.evaluateSchema('unknownFunction', [], {}); + expect(func()).to.be.null; + }); + + describe('extraSchemaEvaluators', function() { + it('should use custom browser evaluator from extraSchemaEvaluators', function() { + const browserEvaluator = (args, context) => { + return () => { + const userAgent = context.ortb2?.device?.ua || navigator.userAgent; + if (userAgent.includes('Chrome')) return 'Chrome'; + if (userAgent.includes('Firefox')) return 'Firefox'; + if (userAgent.includes('Safari')) return 'Safari'; + if (userAgent.includes('Edge')) return 'Edge'; + return 'Unknown'; + }; + }; + + config.setConfig({ + shapingRules: { + extraSchemaEvaluators: { + browser: browserEvaluator + } + } + }); + + const context1 = { + ortb2: { + device: { + ua: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/120.0.0.0' + } + } + }; + + const func1 = rulesModule.evaluateSchema('browser', [], context1); + expect(func1()).to.equal('Chrome'); + + const context2 = { + ortb2: { + device: { + ua: 'Mozilla/5.0 Firefox/121.0.0' + } + } + }; + const func2 = rulesModule.evaluateSchema('browser', [], context2); + expect(func2()).to.equal('Firefox'); + }); + }); + }); +}); diff --git a/test/spec/modules/rumbleBidAdapter_spec.js b/test/spec/modules/rumbleBidAdapter_spec.js index a3e34e34d20..b60fd1ca90b 100644 --- a/test/spec/modules/rumbleBidAdapter_spec.js +++ b/test/spec/modules/rumbleBidAdapter_spec.js @@ -1,8 +1,8 @@ -import {spec, converter} from 'modules/rumbleBidAdapter.js'; +import { spec, converter } from 'modules/rumbleBidAdapter.js'; import { config } from '../../../src/config.js'; -import {BANNER} from "../../../src/mediaTypes.js"; -import {deepClone, getUniqueIdentifierStr} from "../../../src/utils.js"; -import {expect} from "chai"; +import { BANNER } from "../../../src/mediaTypes.js"; +import { deepClone, getUniqueIdentifierStr } from "../../../src/utils.js"; +import { expect } from "chai"; const bidder = 'rumble'; diff --git a/test/spec/modules/s2sTesting_spec.js b/test/spec/modules/s2sTesting_spec.js index 273f6747e52..bac8d7013e3 100644 --- a/test/spec/modules/s2sTesting_spec.js +++ b/test/spec/modules/s2sTesting_spec.js @@ -18,57 +18,57 @@ describe('s2sTesting', function () { }); 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; + 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', 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'); - expect(getExpectedSource(0.25, {server: 1, client: 1, both: 2}, sources)).to.equal('client'); - expect(getExpectedSource(0.49999, {server: 1, client: 1, both: 2}, sources)).to.equal('client'); - expect(getExpectedSource(0.5, {server: 1, client: 1, both: 2}, sources)).to.equal('both'); - expect(getExpectedSource(0.99999, {server: 1, client: 1, both: 2}, sources)).to.equal('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'); + expect(getExpectedSource(0.25, { server: 1, client: 1, both: 2 }, sources)).to.equal('client'); + expect(getExpectedSource(0.49999, { server: 1, client: 1, both: 2 }, sources)).to.equal('client'); + expect(getExpectedSource(0.5, { server: 1, client: 1, both: 2 }, sources)).to.equal('both'); + expect(getExpectedSource(0.99999, { server: 1, client: 1, both: 2 }, sources)).to.equal('both'); }); 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'); - expect(getExpectedSource(0.9, {server: 2, client: 3})).to.equal('client'); + 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'); + expect(getExpectedSource(0.9, { server: 2, client: 3 })).to.equal('client'); var sources = ['server', 'client', 'both']; - expect(getExpectedSource(0, {server: 2, client: 3}, sources)).to.equal('server'); - expect(getExpectedSource(0.39999, {server: 2, client: 3}, sources)).to.equal('server'); - expect(getExpectedSource(0.4, {server: 2, client: 3}, sources)).to.equal('client'); - expect(getExpectedSource(0.9, {server: 2, client: 3}, sources)).to.equal('client'); + expect(getExpectedSource(0, { server: 2, client: 3 }, sources)).to.equal('server'); + expect(getExpectedSource(0.39999, { server: 2, client: 3 }, sources)).to.equal('server'); + expect(getExpectedSource(0.4, { server: 2, client: 3 }, sources)).to.equal('client'); + expect(getExpectedSource(0.9, { server: 2, client: 3 }, sources)).to.equal('client'); }); 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'); + 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', 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'); + 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', 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'); - expect(getExpectedSource(0.25, {client: 1, both: 2, server: 1}, sources)).to.equal('client'); - expect(getExpectedSource(0.49999, {server: 1, both: 2, client: 1}, sources)).to.equal('client'); - expect(getExpectedSource(0.5, {both: 2, server: 1, client: 1}, sources)).to.equal('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'); + expect(getExpectedSource(0.25, { client: 1, both: 2, server: 1 }, sources)).to.equal('client'); + expect(getExpectedSource(0.49999, { server: 1, both: 2, client: 1 }, sources)).to.equal('client'); + expect(getExpectedSource(0.5, { both: 2, server: 1, client: 1 }, sources)).to.equal('both'); }); 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'); + 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'); }); }); @@ -83,7 +83,7 @@ describe('s2sTesting', function () { it('sets one client bidder', function () { const s2sConfig = { bidders: ['rubicon'], - bidderControl: {rubicon: {bidSource: {server: 1, client: 1}}} + bidderControl: { rubicon: { bidSource: { server: 1, client: 1 } } } }; s2sTesting.calculateBidSources(s2sConfig); @@ -96,7 +96,7 @@ describe('s2sTesting', function () { it('sets one server bidder', function () { const s2sConfig = { bidders: ['rubicon'], - bidderControl: {rubicon: {bidSource: {server: 4, client: 1}}} + bidderControl: { rubicon: { bidSource: { server: 4, client: 1 } } } } s2sTesting.calculateBidSources(s2sConfig); expect(s2sTesting.getSourceBidderMap()).to.eql({ @@ -120,8 +120,8 @@ describe('s2sTesting', function () { const s2sConfig = { bidders: ['rubicon', 'appnexus'], bidderControl: { - rubicon: {bidSource: {server: 3, client: 1}}, - appnexus: {bidSource: {server: 1, client: 1}} + rubicon: { bidSource: { server: 3, client: 1 } }, + appnexus: { bidSource: { server: 1, client: 1 } } } } s2sTesting.calculateBidSources(s2sConfig); @@ -136,8 +136,8 @@ describe('s2sTesting', function () { const s2sConfig = { bidders: ['rubicon', 'appnexus'], bidderControl: { - rubicon: {bidSource: {server: 1, client: 99}}, - appnexus: {bidSource: {server: 1, client: 99}} + rubicon: { bidSource: { server: 1, client: 99 } }, + appnexus: { bidSource: { server: 1, client: 99 } } } } s2sTesting.calculateBidSources(s2sConfig); @@ -159,8 +159,8 @@ describe('s2sTesting', function () { const s2sConfig2 = { bidders: ['rubicon', 'appnexus'], bidderControl: { - rubicon: {bidSource: {server: 99, client: 1}}, - appnexus: {bidSource: {server: 99, client: 1}} + rubicon: { bidSource: { server: 99, client: 1 } }, + appnexus: { bidSource: { server: 99, client: 1 } } } } s2sTesting.calculateBidSources(s2sConfig2); @@ -180,7 +180,7 @@ describe('s2sTesting', function () { }); describe('setting source through adUnits', function () { - const s2sConfig3 = {testing: true}; + const s2sConfig3 = { testing: true }; beforeEach(function () { // set random number for testing @@ -190,9 +190,11 @@ describe('s2sTesting', function () { it('sets one bidder source from one adUnit', function () { var adUnits = [ - {bids: [ - {bidder: 'rubicon', bidSource: {server: 4, client: 1}} - ]} + { + bids: [ + { bidder: 'rubicon', bidSource: { server: 4, client: 1 } } + ] + } ]; expect(s2sTesting.getSourceBidderMap(adUnits, [])).to.eql({ @@ -204,9 +206,11 @@ describe('s2sTesting', function () { expect(adUnits[0].bids[0].finalSource).to.equal('server'); adUnits = [ - {bids: [ - {bidder: 'rubicon', bidSource: {server: 1, client: 1}} - ]} + { + bids: [ + { bidder: 'rubicon', bidSource: { server: 1, client: 1 } } + ] + } ]; expect(s2sTesting.getSourceBidderMap(adUnits, [])).to.eql({ server: [], @@ -219,9 +223,11 @@ describe('s2sTesting', function () { it('defaults to client if no bidSource', function () { var adUnits = [ - {bids: [ - {bidder: 'rubicon', bidSource: {}} - ]} + { + bids: [ + { bidder: 'rubicon', bidSource: {} } + ] + } ]; expect(s2sTesting.getSourceBidderMap(adUnits, [])).to.eql({ server: [], @@ -234,10 +240,12 @@ describe('s2sTesting', function () { 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}} - ]} + { + bids: [ + { bidder: 'rubicon', bidSource: { server: 2, client: 1 } }, + { bidder: 'appnexus', bidSource: { server: 3, client: 1 } } + ] + } ]; var serverClientBidders = s2sTesting.getSourceBidderMap(adUnits, []); expect(serverClientBidders.server).to.eql(['appnexus']); @@ -251,14 +259,18 @@ describe('s2sTesting', function () { it('sets multiple bidders sources from multiple adUnits', function () { var adUnits = [ - {bids: [ - {bidder: 'rubicon', bidSource: {server: 2, client: 1}}, - {bidder: 'appnexus', bidSource: {server: 1, client: 1}} - ]}, - {bids: [ - {bidder: 'rubicon', bidSource: {server: 4, client: 1}}, - {bidder: 'bidder3', bidSource: {client: 1}} - ]} + { + bids: [ + { bidder: 'rubicon', bidSource: { server: 2, client: 1 } }, + { bidder: 'appnexus', bidSource: { server: 1, client: 1 } } + ] + }, + { + bids: [ + { bidder: 'rubicon', bidSource: { server: 4, client: 1 } }, + { bidder: 'bidder3', bidSource: { client: 1 } } + ] + } ]; var serverClientBidders = s2sTesting.getSourceBidderMap(adUnits, []); expect(serverClientBidders.server).to.have.members(['rubicon']); @@ -277,11 +289,13 @@ describe('s2sTesting', function () { it('should reuse calculated sources', function () { var adUnits = [ - {bids: [ - {bidder: 'rubicon', calcSource: 'client', bidSource: {server: 4, client: 1}}, - {bidder: 'appnexus', calcSource: 'server', bidSource: {server: 1, client: 1}}, - {bidder: 'bidder3', calcSource: 'server', bidSource: {client: 1}} - ]} + { + bids: [ + { bidder: 'rubicon', calcSource: 'client', bidSource: { server: 4, client: 1 } }, + { bidder: 'appnexus', calcSource: 'server', bidSource: { server: 1, client: 1 } }, + { bidder: 'bidder3', calcSource: 'server', bidSource: { client: 1 } } + ] + } ]; var serverClientBidders = s2sTesting.getSourceBidderMap(adUnits, []); @@ -308,10 +322,12 @@ describe('s2sTesting', function () { it('should get sources from both', function () { // set rubicon: server and appnexus: client var adUnits = [ - {bids: [ - {bidder: 'rubicon', bidSource: {server: 4, client: 1}}, - {bidder: 'appnexus', bidSource: {client: 1}} - ]} + { + bids: [ + { bidder: 'rubicon', bidSource: { server: 4, client: 1 } }, + { bidder: 'appnexus', bidSource: { client: 1 } } + ] + } ]; // set rubicon: client and appnexus: server @@ -319,8 +335,8 @@ describe('s2sTesting', function () { bidders: ['rubicon', 'appnexus'], testing: true, bidderControl: { - rubicon: {bidSource: {server: 2, client: 1}}, - appnexus: {bidSource: {server: 1}} + rubicon: { bidSource: { server: 2, client: 1 } }, + appnexus: { bidSource: { server: 1 } } } } s2sTesting.calculateBidSources(s2sConfig); diff --git a/test/spec/modules/scaleableAnalyticsAdapter_spec.js b/test/spec/modules/scaleableAnalyticsAdapter_spec.js index 5f86073894a..68944b56c1c 100644 --- a/test/spec/modules/scaleableAnalyticsAdapter_spec.js +++ b/test/spec/modules/scaleableAnalyticsAdapter_spec.js @@ -55,7 +55,7 @@ describe('Scaleable Analytics Adapter', function() { const bidObj = MOCK_DATA.bidderRequests[0].bids[0]; - const expectedBidRequests = [{bidder: 'scaleable_adunit_request'}].concat([ + const expectedBidRequests = [{ bidder: 'scaleable_adunit_request' }].concat([ { bidder: bidObj.bidder, params: bidObj.params diff --git a/test/spec/modules/scaliburBidAdapter_spec.js b/test/spec/modules/scaliburBidAdapter_spec.js index c2f6a3c65a8..98290963b1e 100644 --- a/test/spec/modules/scaliburBidAdapter_spec.js +++ b/test/spec/modules/scaliburBidAdapter_spec.js @@ -1,5 +1,5 @@ -import {expect} from 'chai'; -import {spec, getFirstPartyData, storage} from 'modules/scaliburBidAdapter.js'; +import { expect } from 'chai'; +import { spec, getFirstPartyData, storage } from 'modules/scaliburBidAdapter.js'; describe('Scalibur Adapter', function () { const BID = { @@ -62,7 +62,7 @@ describe('Scalibur Adapter', function () { ref: 'https://example-referrer.com', }, user: { - data: [{name: 'segments', segment: ['sports', 'entertainment']}], + data: [{ name: 'segments', segment: ['sports', 'entertainment'] }], }, regs: { ext: { @@ -118,7 +118,7 @@ describe('Scalibur Adapter', function () { }); it('should return false for missing placementId', function () { - const invalidBid = {...BID, params: {}}; + const invalidBid = { ...BID, params: {} }; expect(spec.isBidRequestValid(invalidBid)).to.equal(false); }); }); @@ -136,8 +136,8 @@ describe('Scalibur Adapter', function () { expect(imp.ext.placementId).to.equal('test-scl-placement'); expect(imp.ext.gpid).to.equal('/1234/5678/homepage'); expect(imp.banner.format).to.deep.equal([ - {w: 300, h: 250}, - {w: 728, h: 90}, + { w: 300, h: 250 }, + { w: 728, h: 90 }, ]); const video = imp.video; @@ -165,8 +165,8 @@ describe('Scalibur Adapter', function () { expect(imp.ext.placementId).to.equal('test-scl-placement'); expect(imp.ext.gpid).to.equal('/1234/5678/homepage'); expect(imp.banner.format).to.deep.equal([ - {w: 300, h: 250}, - {w: 728, h: 90}, + { w: 300, h: 250 }, + { w: 728, h: 90 }, ]); const video = imp.video; @@ -230,7 +230,7 @@ describe('Scalibur Adapter', function () { describe('getUserSyncs', function () { it('should return iframe and pixel sync URLs with correct params', function () { - const syncOptions = {iframeEnabled: true, pixelEnabled: true}; + const syncOptions = { iframeEnabled: true, pixelEnabled: true }; const gdprConsent = { gdprApplies: true, consentString: 'BOEFEAyOEFEAyAHABDENAI4AAAB9vABAASA', diff --git a/test/spec/modules/schain_spec.js b/test/spec/modules/schain_spec.js index 4517eb976fb..1b9e3fd2ece 100644 --- a/test/spec/modules/schain_spec.js +++ b/test/spec/modules/schain_spec.js @@ -1,7 +1,7 @@ -import {expect} from 'chai/index.js'; +import { expect } from 'chai/index.js'; import * as utils from 'src/utils.js'; -import {config} from 'src/config.js'; -import {applySchainConfig} from 'modules/schain.js'; +import { config } from 'src/config.js'; +import { applySchainConfig } from 'modules/schain.js'; describe('Supply Chain fpd', function() { const SAMPLE_SCHAIN = { diff --git a/test/spec/modules/scope3RtdProvider_spec.js b/test/spec/modules/scope3RtdProvider_spec.js index 367d5823861..de1fc14646e 100644 --- a/test/spec/modules/scope3RtdProvider_spec.js +++ b/test/spec/modules/scope3RtdProvider_spec.js @@ -1,4 +1,4 @@ -import {auctionManager} from 'src/auctionManager.js'; +import { auctionManager } from 'src/auctionManager.js'; import { scope3SubModule } from 'modules/scope3RtdProvider.js'; import * as utils from 'src/utils.js'; import { server } from 'test/mocks/xhr.js'; @@ -584,7 +584,7 @@ describe('Scope3 RTD Module', function() { getFPD: () => { return reqBidsConfigObj.ortb2Fragments } }); - const targetingData = scope3SubModule.getTargetingData([ reqBidsConfigObj.adUnits[0].code ], config, {}, { adUnits: reqBidsConfigObj.adUnits }) + const targetingData = scope3SubModule.getTargetingData([reqBidsConfigObj.adUnits[0].code], config, {}, { adUnits: reqBidsConfigObj.adUnits }) expect(targetingData['test-ad-unit-nocache']).to.be.an('object') expect(targetingData['test-ad-unit-nocache']['axei']).to.be.an('array') expect(targetingData['test-ad-unit-nocache']['axei'].length).to.equal(1) diff --git a/test/spec/modules/screencoreBidAdapter_spec.js b/test/spec/modules/screencoreBidAdapter_spec.js index bd1aad95edf..c80998a4672 100644 --- a/test/spec/modules/screencoreBidAdapter_spec.js +++ b/test/spec/modules/screencoreBidAdapter_spec.js @@ -227,7 +227,7 @@ describe('screencore bid adapter', function () { const requests = adapter.buildRequests([BID], BIDDER_REQUEST); expect(requests).to.exist; expect(requests.method).to.equal('POST'); - expect(requests.url).to.include('screencore.io/prebid'); + expect(requests.url).to.include('screencore.io/pbjs'); expect(requests.data).to.exist; expect(requests.data.placements).to.be.an('array'); expect(requests.data.placements[0].bidId).to.equal(BID.bidId); @@ -394,5 +394,38 @@ describe('screencore bid adapter', function () { stub.restore(); }); + + it('should return correct domain for US/ prefixed timezone', function () { + const stub = sinon.stub(Intl, 'DateTimeFormat').returns({ + resolvedOptions: () => ({ timeZone: 'US/Eastern' }) + }); + + const domain = createDomain(); + expect(domain).to.equal('https://taqus.screencore.io'); + + stub.restore(); + }); + + it('should return correct domain for Canada/ prefixed timezone', function () { + const stub = sinon.stub(Intl, 'DateTimeFormat').returns({ + resolvedOptions: () => ({ timeZone: 'Canada/Eastern' }) + }); + + const domain = createDomain(); + expect(domain).to.equal('https://taqus.screencore.io'); + + stub.restore(); + }); + + it('should return EU domain as default for unknown timezone', function () { + const stub = sinon.stub(Intl, 'DateTimeFormat').returns({ + resolvedOptions: () => ({ timeZone: 'UTC' }) + }); + + const domain = createDomain(); + expect(domain).to.equal('https://taqeu.screencore.io'); + + stub.restore(); + }); }); }); diff --git a/test/spec/modules/seedingAllianceAdapter_spec.js b/test/spec/modules/seedingAllianceAdapter_spec.js index 52be0caeb82..3f62427a2f9 100755 --- a/test/spec/modules/seedingAllianceAdapter_spec.js +++ b/test/spec/modules/seedingAllianceAdapter_spec.js @@ -1,8 +1,8 @@ // jshint esversion: 6, es3: false, node: true -import {assert, expect} from 'chai'; -import {getStorageManager} from 'src/storageManager.js'; -import {spec} from 'modules/seedingAllianceBidAdapter.js'; -import {getGlobal} from '../../../src/prebidGlobal.js'; +import { assert, expect } from 'chai'; +import { getStorageManager } from 'src/storageManager.js'; +import { spec } from 'modules/seedingAllianceBidAdapter.js'; +import { getGlobal } from '../../../src/prebidGlobal.js'; describe('SeedingAlliance adapter', function () { let serverResponse, bidRequest, bidResponses; @@ -152,8 +152,8 @@ describe('SeedingAlliance adapter', function () { adm: JSON.stringify({ native: { assets: [ - {id: 0, title: {text: 'this is a title'}}, - {id: 1, img: {url: 'https://domain.for/img.jpg'}}, + { id: 0, title: { text: 'this is a title' } }, + { id: 1, img: { url: 'https://domain.for/img.jpg' } }, ], imptrackers: ['https://domain.for/imp/tracker?price=${AUCTION_PRICE}'], link: { @@ -189,20 +189,22 @@ describe('SeedingAlliance adapter', function () { } }; - const badResponse = { body: { - cur: 'EUR', - id: 'bidid1', - seatbid: [] - }}; + const badResponse = { + body: { + cur: 'EUR', + id: 'bidid1', + seatbid: [] + } + }; const bidNativeRequest = { data: {}, - bidRequests: [{bidId: '1', nativeParams: {title: {required: true, len: 800}, image: {required: true, sizes: [300, 250]}}}] + bidRequests: [{ bidId: '1', nativeParams: { title: { required: true, len: 800 }, image: { required: true, sizes: [300, 250] } } }] }; const bidBannerRequest = { data: {}, - bidRequests: [{bidId: '1', sizes: [300, 250]}] + bidRequests: [{ bidId: '1', sizes: [300, 250] }] }; it('should return null if body is missing or empty', function () { diff --git a/test/spec/modules/seedtagBidAdapter_spec.js b/test/spec/modules/seedtagBidAdapter_spec.js index 3a448c90d78..cc7a3309e63 100644 --- a/test/spec/modules/seedtagBidAdapter_spec.js +++ b/test/spec/modules/seedtagBidAdapter_spec.js @@ -4,23 +4,13 @@ import * as utils from 'src/utils.js'; import * as mockGpt from 'test/spec/integration/faker/googletag.js'; import { config } from '../../../src/config.js'; import { BIDFLOOR_CURRENCY } from '../../../modules/seedtagBidAdapter.js'; +import * as adUnits from 'src/utils/adUnits'; const PUBLISHER_ID = '0000-0000-01'; const ADUNIT_ID = '000000'; const adUnitCode = '/19968336/header-bid-tag-0' -// create a default adunit -const slot = document.createElement('div'); -slot.id = adUnitCode; -slot.style.width = '300px' -slot.style.height = '250px' -slot.style.position = 'absolute' -slot.style.top = '10px' -slot.style.left = '20px' - -document.body.appendChild(slot); - function getSlotConfigs(mediaTypes, params) { return { params: params, @@ -60,12 +50,25 @@ const createBannerSlotConfig = (mediatypes) => { }; describe('Seedtag Adapter', function () { + let sandbox; beforeEach(function () { mockGpt.reset(); + sandbox = sinon.createSandbox(); + sandbox.stub(adUnits, 'getAdUnitElement').returns({ + getBoundingClientRect() { + return { + top: 10, + left: 20, + width: 300, + height: 250 + } + } + }); }); afterEach(function () { mockGpt.enable(); + sandbox.restore(); }); describe('isBidRequestValid method', function () { describe('returns true', function () { @@ -171,13 +174,13 @@ describe('Seedtag Adapter', function () { ); expect(isBidRequestValid).to.equal(false); }); - it('does not have the AdUnitId.', function () { + it('should be valid with only publisherId and no adUnitId', function () { const isBidRequestValid = spec.isBidRequestValid( createSlotConfig({ publisherId: PUBLISHER_ID, }) ); - expect(isBidRequestValid).to.equal(false); + expect(isBidRequestValid).to.equal(true); }); }); @@ -328,9 +331,13 @@ describe('Seedtag Adapter', function () { }); describe('BidRequests params', function () { - const request = spec.buildRequests(validBidRequests, bidderRequest); - const data = JSON.parse(request.data); - const bidRequests = data.bidRequests; + let request, data, bidRequests; + beforeEach(() => { + request = spec.buildRequests(validBidRequests, bidderRequest); + data = JSON.parse(request.data); + bidRequests = data.bidRequests; + }); + it('should request a Banner', function () { const bannerBid = bidRequests[0]; expect(bannerBid.id).to.equal('30b31c1838de1e'); @@ -369,27 +376,21 @@ describe('Seedtag Adapter', function () { const bidRequests = data.bidRequests; const bannerBid = bidRequests[0]; - // on some CI, the DOM is not initialized, so we need to check if the slot is available - const slot = document.getElementById(adUnitCode) - if (slot) { - expect(bannerBid).to.have.property('geom') - - const params = [['width', 300], ['height', 250], ['top', 10], ['left', 20], ['scrollY', 0]] - params.forEach(([param, value]) => { - expect(bannerBid.geom).to.have.property(param) - expect(bannerBid.geom[param]).to.be.a('number') - expect(bannerBid.geom[param]).to.be.equal(value) - }) - - expect(bannerBid.geom).to.have.property('viewport') - const viewportParams = ['width', 'height'] - viewportParams.forEach(param => { - expect(bannerBid.geom.viewport).to.have.property(param) - expect(bannerBid.geom.viewport[param]).to.be.a('number') - }) - } else { - expect(bannerBid).to.not.have.property('geom') - } + expect(bannerBid).to.have.property('geom') + + const params = [['width', 300], ['height', 250], ['top', 10], ['left', 20], ['scrollY', 0]] + params.forEach(([param, value]) => { + expect(bannerBid.geom).to.have.property(param) + expect(bannerBid.geom[param]).to.be.a('number') + expect(bannerBid.geom[param]).to.be.equal(value) + }) + + expect(bannerBid.geom).to.have.property('viewport') + const viewportParams = ['width', 'height'] + viewportParams.forEach(param => { + expect(bannerBid.geom.viewport).to.have.property(param) + expect(bannerBid.geom.viewport[param]).to.be.a('number') + }) }) it('should have bidfloor parameter if available', function () { @@ -711,6 +712,47 @@ describe('Seedtag Adapter', function () { expect(data.sua).to.be.undefined; }); }); + + describe('integrationType param', function () { + it('should default to publisherToken when integrationType is not provided', function () { + const request = spec.buildRequests(validBidRequests, bidderRequest); + const data = JSON.parse(request.data); + expect(data.integrationType).to.equal('publisherToken'); + }); + + it('should use the provided integrationType value', function () { + const bidRequests = JSON.parse(JSON.stringify(validBidRequests)); + bidRequests[0].params.integrationType = 'ronId'; + + const request = spec.buildRequests(bidRequests, bidderRequest); + const data = JSON.parse(request.data); + expect(data.integrationType).to.equal('ronId'); + }); + }); + + describe('ortb param', function () { + it('should add ortb param to payload when bidderRequest has ortb2', function () { + const ortb2 = { + site: { cat: ['IAB1'] }, + user: { data: [{ name: 'test' }] }, + bcat: ['IAB3'], + }; + bidderRequest['ortb2'] = ortb2; + + const request = spec.buildRequests(validBidRequests, bidderRequest); + const data = JSON.parse(request.data); + expect(data.ortb).to.exist; + expect(data.ortb).to.deep.equal(ortb2); + }); + + it('should not add ortb param to payload when bidderRequest does not have ortb2', function () { + bidderRequest['ortb2'] = undefined; + + const request = spec.buildRequests(validBidRequests, bidderRequest); + const data = JSON.parse(request.data); + expect(data.ortb).to.be.undefined; + }); + }); }) describe('interpret response method', function () { it('should return a void array, when the server response are not correct.', function () { @@ -895,7 +937,7 @@ describe('Seedtag Adapter', function () { utils.triggerPixel.restore(); }); - it('should return the correct endpoint', function () { + it('should return the correct endpoint with adUnitId', function () { const params = { publisherId: '0000', adUnitId: '11111' }; const timeout = 3000; const timeoutData = [{ params: [params], timeout }]; @@ -903,8 +945,21 @@ describe('Seedtag Adapter', function () { expect(timeoutUrl).to.equal( 'https://s.seedtag.com/se/hb/timeout?publisherToken=' + params.publisherId + + '&timeout=' + + timeout + '&adUnitId=' + - params.adUnitId + + params.adUnitId + ); + }); + + it('should return the correct endpoint without adUnitId', function () { + const params = { publisherId: '0000' }; + const timeout = 3000; + const timeoutData = [{ params: [params], timeout }]; + const timeoutUrl = getTimeoutUrl(timeoutData); + expect(timeoutUrl).to.equal( + 'https://s.seedtag.com/se/hb/timeout?publisherToken=' + + params.publisherId + '&timeout=' + timeout ); @@ -919,10 +974,10 @@ describe('Seedtag Adapter', function () { utils.triggerPixel.calledWith( 'https://s.seedtag.com/se/hb/timeout?publisherToken=' + params.publisherId + - '&adUnitId=' + - params.adUnitId + '&timeout=' + - timeout + timeout + + '&adUnitId=' + + params.adUnitId ) ).to.equal(true); }); diff --git a/test/spec/modules/sevioBidAdapter_spec.js b/test/spec/modules/sevioBidAdapter_spec.js index 63b36465dad..56d25307532 100644 --- a/test/spec/modules/sevioBidAdapter_spec.js +++ b/test/spec/modules/sevioBidAdapter_spec.js @@ -133,7 +133,7 @@ describe('sevioBidAdapter', function () { 'ttl': 3000, 'ad': '

I am an ad

', 'mediaType': 'banner', - 'meta': {'advertiserDomains': ['none.com']} + 'meta': { 'advertiserDomains': ['none.com'] } }]; let result = spec.interpretResponse(serverResponse, bidRequest[0]); @@ -370,7 +370,8 @@ describe('sevioBidAdapter', function () { const original = perfTop.getEntriesByType; Object.defineProperty(perfTop, 'getEntriesByType', { - configurable: true, writable: true, + configurable: true, + writable: true, value: (type) => (type === 'navigation' ? [{ responseStart: 152, requestStart: 100 }] : []) }); @@ -521,4 +522,108 @@ describe('sevioBidAdapter', function () { expect(requests[0].data.keywords.tokens).to.deep.equal(['play', 'games', 'fun']); }); }); + + describe('native parsing', function () { + it('parses native assets: title, data->desc (type 2), image (asset.img), clickUrl and trackers', function () { + const serverResponseNative = { + body: { + bids: [ + { + requestId: 'native-1', + cpm: 1.0, + currency: 'EUR', + width: 1, + height: 1, + creativeId: 'native-creative-1', + ad: JSON.stringify({ + ver: '1.2', + assets: [ + { id: 2, title: { text: 'Native Title' } }, + { id: 4, data: { type: 2, value: 'Native body text' } }, + { id: 5, img: { type: 3, url: 'https://img.example/x.png', w: 120, h: 60 } } + ], + eventtrackers: [ + { event: 1, method: 1, url: 'https://impr.example/1' }, + { event: 2, method: 1, url: 'https://view.example/1' } + ], + link: { url: 'https://click.example', clicktrackers: ['https://clickt.example/1'] } + }), + ttl: 300, + netRevenue: true, + mediaType: 'NATIVE', + meta: { advertiserDomains: ['adv.example'] }, + bidder: 'sevio' + } + ] + } + }; + + const result = spec.interpretResponse(serverResponseNative); + expect(result).to.be.an('array').with.lengthOf(1); + + const out = result[0]; + expect(out).to.have.property('native'); + + const native = out.native; + expect(native.title).to.equal('Native Title'); + expect(native.image).to.equal('https://img.example/x.png'); + expect(native.image_width).to.equal(120); + expect(native.image_height).to.equal(60); + expect(native.clickUrl).to.equal('https://click.example'); + + expect(native.impressionTrackers).to.be.an('array').that.includes('https://impr.example/1'); + expect(native.viewableTrackers).to.be.an('array').that.includes('https://view.example/1'); + expect(native.clickTrackers).to.be.an('array').that.includes('https://clickt.example/1'); + + // meta preserved + expect(out.meta).to.have.property('advertiserDomains').that.deep.equals(['adv.example']); + }); + + it('maps legacy asset.id -> image types (13 -> icon, 14 -> image) and sets icon fields', function () { + const serverResponseIcon = { + body: { + bids: [ + { + requestId: 'native-icon', + cpm: 1.0, + currency: 'EUR', + width: 1, + height: 1, + creativeId: 'native-creative-icon', + ad: JSON.stringify({ + ver: '1.2', + assets: [ + // legacy asset id 13 should map to icon (img type 1) + { id: 13, img: { url: 'https://img.example/icon.png', w: 50, h: 50 } }, + // legacy asset id 14 should map to image (img type 3) + { id: 14, img: { url: 'https://img.example/img.png', w: 200, h: 100 } }, + { id: 2, title: { text: 'Legacy Mapping Test' } } + ], + link: { url: 'https://click.example/leg' } + }), + ttl: 300, + netRevenue: true, + mediaType: 'NATIVE', + meta: { advertiserDomains: ['legacy.example'] }, + bidder: 'sevio' + } + ] + } + }; + + const result = spec.interpretResponse(serverResponseIcon); + expect(result).to.be.an('array').with.lengthOf(1); + const native = result[0].native; + + // icon mapped from id 13 + expect(native.icon).to.equal('https://img.example/icon.png'); + expect(native.icon_width).to.equal(50); + expect(native.icon_height).to.equal(50); + + // image mapped from id 14 + expect(native.image).to.equal('https://img.example/img.png'); + expect(native.image_width).to.equal(200); + expect(native.image_height).to.equal(100); + }); + }); }); diff --git a/test/spec/modules/sharedIdSystem_spec.js b/test/spec/modules/sharedIdSystem_spec.js index c258e2ad4f7..7a4c9f9c5ab 100644 --- a/test/spec/modules/sharedIdSystem_spec.js +++ b/test/spec/modules/sharedIdSystem_spec.js @@ -1,11 +1,11 @@ -import {sharedIdSystemSubmodule} from 'modules/sharedIdSystem.js'; -import {config} from 'src/config.js'; +import { sharedIdSystemSubmodule } from 'modules/sharedIdSystem.js'; +import { config } from 'src/config.js'; import sinon from 'sinon'; import * as utils from 'src/utils.js'; -import {createEidsArray} from '../../../modules/userId/eids.js'; -import {attachIdSystem} from '../../../modules/userId/index.js'; -import {getGlobal} from '../../../src/prebidGlobal.js'; +import { createEidsArray } from '../../../modules/userId/eids.js'; +import { attachIdSystem } from '../../../modules/userId/index.js'; +import { getGlobal } from '../../../src/prebidGlobal.js'; const expect = require('chai').expect; @@ -51,7 +51,7 @@ describe('SharedId System', function () { expect(callbackSpy.lastCall.lastArg).to.equal(UUID); }); it('should abort if coppa is set', function () { - const result = sharedIdSystemSubmodule.getId({}, {coppa: true}); + const result = sharedIdSystemSubmodule.getId({}, { coppa: true }); expect(result).to.be.undefined; }); }); @@ -82,7 +82,7 @@ describe('SharedId System', function () { expect(pubcommId).to.equal('TestId'); }); it('should abort if coppa is set', function () { - const result = sharedIdSystemSubmodule.extendId({params: {extend: true}}, {coppa: true}, 'TestId'); + const result = sharedIdSystemSubmodule.extendId({ params: { extend: true } }, { coppa: true }, 'TestId'); expect(result).to.be.undefined; }); }); @@ -101,7 +101,7 @@ describe('SharedId System', function () { expect(newEids.length).to.equal(1); expect(newEids[0]).to.deep.equal({ source: 'pubcid.org', - uids: [{id: 'some-random-id-value', atype: 1}] + uids: [{ id: 'some-random-id-value', atype: 1 }] }); }); @@ -113,7 +113,7 @@ describe('SharedId System', function () { params: { inserter: 'mock-inserter' }, - value: {pubcid: 'mock-id'} + value: { pubcid: 'mock-id' } }] } }); diff --git a/test/spec/modules/sharethroughBidAdapter_spec.js b/test/spec/modules/sharethroughBidAdapter_spec.js index 56f238ff1ba..26e9e57da7c 100644 --- a/test/spec/modules/sharethroughBidAdapter_spec.js +++ b/test/spec/modules/sharethroughBidAdapter_spec.js @@ -963,22 +963,6 @@ describe('sharethrough adapter spec', function () { }); }); - describe('fledge', () => { - it('should attach "ae" as a property to the request if 1) fledge auctions are enabled, and 2) request is display (only supporting display for now)', () => { - // ASSEMBLE - const EXPECTED_AE_VALUE = 1; - - // ACT - bidderRequest.paapi = { enabled: true }; - const builtRequests = spec.buildRequests(bidRequests, bidderRequest); - const ACTUAL_AE_VALUE = builtRequests[0].data.imp[0].ext.ae; - - // ASSERT - expect(ACTUAL_AE_VALUE).to.equal(EXPECTED_AE_VALUE); - expect(builtRequests[1].data.imp[0].ext.ae).to.be.undefined; - }); - }); - describe('isEqtvTest', () => { it('should set publisher id if equativNetworkId param is present', () => { const builtRequest = spec.buildRequests(multiImpBidRequests, bidderRequest)[0]; @@ -1221,53 +1205,6 @@ describe('sharethrough adapter spec', function () { const resp = spec.interpretResponse(response, request)[0]; expect(resp.ttl).to.equal(360); }); - - it('should return correct properties when fledgeAuctionEnabled is true and isEqtvTest is false', () => { - request = spec.buildRequests(bidRequests, bidderRequest)[0]; - response = { - body: { - ext: { - auctionConfigs: { - key: 'value', - }, - }, - seatbid: [ - { - bid: [ - { - id: 'abcd1234', - impid: 'aaaabbbbccccdd', - w: 300, - h: 250, - price: 42, - crid: 'creative', - dealid: 'deal', - adomain: ['domain.com'], - adm: 'markup', - exp: -1, - }, - { - id: 'efgh5678', - impid: 'ddeeeeffffgggg', - w: 300, - h: 250, - price: 42, - crid: 'creative', - dealid: 'deal', - adomain: ['domain.com'], - adm: 'markup', - exp: -1, - }, - ], - }, - ], - }, - }; - - const resp = spec.interpretResponse(response, request); - expect(resp.bids.length).to.equal(2); - expect(resp.paapi).to.deep.equal({ key: 'value' }); - }); }); describe('video', () => { diff --git a/test/spec/modules/shinezBidAdapter_spec.js b/test/spec/modules/shinezBidAdapter_spec.js index b5976fbc7d2..af912cbf079 100644 --- a/test/spec/modules/shinezBidAdapter_spec.js +++ b/test/spec/modules/shinezBidAdapter_spec.js @@ -2,9 +2,9 @@ import { expect } from 'chai'; import { spec } from 'modules/shinezBidAdapter.js'; import { newBidder } from 'src/adapters/bidderFactory.js'; import { config } from 'src/config.js'; -import {BANNER, NATIVE, VIDEO} from '../../../src/mediaTypes.js'; +import { BANNER, NATIVE, VIDEO } from '../../../src/mediaTypes.js'; import * as utils from 'src/utils.js'; -import {decorateAdUnitsWithNativeParams} from '../../../src/native.js'; +import { decorateAdUnitsWithNativeParams } from '../../../src/native.js'; const ENDPOINT = 'https://hb.sweetgum.io/hb-sz-multi'; const TEST_ENDPOINT = 'https://hb.sweetgum.io/hb-multi-sz-test'; @@ -92,7 +92,7 @@ describe('shinezAdapter', function () { 'mediaTypes': { 'banner': { 'sizes': [ - [ 300, 250 ] + [300, 250] ] }, 'video': { @@ -284,7 +284,7 @@ describe('shinezAdapter', function () { }); it('should have us_privacy param if usPrivacy is available in the bidRequest', function () { - const bidderRequestWithUSP = Object.assign({uspConsent: '1YNN'}, bidderRequest); + const bidderRequestWithUSP = Object.assign({ uspConsent: '1YNN' }, bidderRequest); const request = spec.buildRequests(bidRequests, bidderRequestWithUSP); expect(request.data.params).to.be.an('object'); expect(request.data.params).to.have.property('us_privacy', '1YNN'); @@ -297,7 +297,7 @@ describe('shinezAdapter', function () { }); it('should not send the gdpr param if gdprApplies is false in the bidRequest', function () { - const bidderRequestWithGDPR = Object.assign({gdprConsent: {gdprApplies: false}}, bidderRequest); + const bidderRequestWithGDPR = Object.assign({ gdprConsent: { gdprApplies: false } }, bidderRequest); const request = spec.buildRequests(bidRequests, bidderRequestWithGDPR); expect(request.data.params).to.be.an('object'); expect(request.data.params).to.not.have.property('gdpr'); @@ -305,7 +305,7 @@ describe('shinezAdapter', function () { }); it('should send the gdpr param if gdprApplies is true in the bidRequest', function () { - const bidderRequestWithGDPR = Object.assign({gdprConsent: {gdprApplies: true, consentString: 'test-consent-string'}}, bidderRequest); + const bidderRequestWithGDPR = Object.assign({ gdprConsent: { gdprApplies: true, consentString: 'test-consent-string' } }, bidderRequest); const request = spec.buildRequests(bidRequests, bidderRequestWithGDPR); expect(request.data.params).to.be.an('object'); expect(request.data.params).to.have.property('gdpr', true); diff --git a/test/spec/modules/shinezRtbBidAdapter_spec.js b/test/spec/modules/shinezRtbBidAdapter_spec.js index 2dd33d7ef5b..1db32e61bc2 100644 --- a/test/spec/modules/shinezRtbBidAdapter_spec.js +++ b/test/spec/modules/shinezRtbBidAdapter_spec.js @@ -1,4 +1,4 @@ -import {expect} from 'chai'; +import { expect } from 'chai'; import { spec as adapter, createDomain, @@ -14,12 +14,12 @@ import { tryParseJSON, getUniqueDealId, } from '../../../libraries/vidazooUtils/bidderUtils.js'; -import {parseUrl, deepClone, getWinDimensions} from 'src/utils.js'; -import {version} from 'package.json'; -import {useFakeTimers} from 'sinon'; -import {BANNER, VIDEO} from '../../../src/mediaTypes.js'; -import {config} from '../../../src/config.js'; -import {getGlobal} from '../../../src/prebidGlobal.js'; +import { parseUrl, deepClone, getWinDimensions } from 'src/utils.js'; +import { version } from 'package.json'; +import { useFakeTimers } from 'sinon'; +import { BANNER, VIDEO } from '../../../src/mediaTypes.js'; +import { config } from '../../../src/config.js'; +import { getGlobal } from '../../../src/prebidGlobal.js'; import * as utils from 'src/utils.js'; export const TEST_ID_SYSTEMS = ['criteoId', 'id5id', 'idl_env', 'lipb', 'netId', 'pubcid', 'tdid', 'pubProvidedId']; @@ -104,9 +104,9 @@ const ORTB2_DEVICE = { 'version': ['8', '0', '0'] }, 'browsers': [ - {'brand': 'Not_A Brand', 'version': ['99', '0', '0', '0']}, - {'brand': 'Google Chrome', 'version': ['109', '0', '5414', '119']}, - {'brand': 'Chromium', 'version': ['109', '0', '5414', '119']} + { 'brand': 'Not_A Brand', 'version': ['99', '0', '0', '0'] }, + { 'brand': 'Google Chrome', 'version': ['109', '0', '5414', '119'] }, + { 'brand': 'Chromium', 'version': ['109', '0', '5414', '119'] } ], 'mobile': 1, 'model': 'SM-G955U', @@ -123,7 +123,7 @@ const ORTB2_DEVICE = { model: 'iPhone 12 Pro Max', os: 'iOS', osv: '17.4', - ext: {fiftyonedegrees_deviceId: '17595-133085-133468-18092'}, + ext: { fiftyonedegrees_deviceId: '17595-133085-133468-18092' }, }; const BIDDER_REQUEST = { @@ -196,9 +196,8 @@ const VIDEO_SERVER_RESPONSE = { const ORTB2_OBJ = { "device": ORTB2_DEVICE, - "regs": {"coppa": 0, "gpp": "gpp_string", "gpp_sid": [7]}, - "site": {"content": {"language": "en"} - } + "regs": { "coppa": 0, "gpp": "gpp_string", "gpp_sid": [7] }, + "site": { "content": { "language": "en" } } }; const REQUEST = { @@ -211,7 +210,7 @@ const REQUEST = { function getTopWindowQueryParams() { try { - const parsedUrl = parseUrl(window.top.document.URL, {decodeSearchAsString: true}); + const parsedUrl = parseUrl(window.top.document.URL, { decodeSearchAsString: true }); return parsedUrl.search; } catch (e) { return ''; @@ -333,9 +332,9 @@ describe('ShinezRtbBidAdapter', function () { 'version': ['8', '0', '0'] }, 'browsers': [ - {'brand': 'Not_A Brand', 'version': ['99', '0', '0', '0']}, - {'brand': 'Google Chrome', 'version': ['109', '0', '5414', '119']}, - {'brand': 'Chromium', 'version': ['109', '0', '5414', '119']} + { 'brand': 'Not_A Brand', 'version': ['99', '0', '0', '0'] }, + { 'brand': 'Google Chrome', 'version': ['109', '0', '5414', '119'] }, + { 'brand': 'Chromium', 'version': ['109', '0', '5414', '119'] } ], 'mobile': 1, 'model': 'SM-G955U', @@ -407,9 +406,9 @@ describe('ShinezRtbBidAdapter', function () { 'version': ['8', '0', '0'] }, 'browsers': [ - {'brand': 'Not_A Brand', 'version': ['99', '0', '0', '0']}, - {'brand': 'Google Chrome', 'version': ['109', '0', '5414', '119']}, - {'brand': 'Chromium', 'version': ['109', '0', '5414', '119']} + { 'brand': 'Not_A Brand', 'version': ['99', '0', '0', '0'] }, + { 'brand': 'Google Chrome', 'version': ['109', '0', '5414', '119'] }, + { 'brand': 'Chromium', 'version': ['109', '0', '5414', '119'] } ], 'mobile': 1, 'model': 'SM-G955U', @@ -454,7 +453,7 @@ describe('ShinezRtbBidAdapter', function () { }); describe('getUserSyncs', function () { it('should have valid user sync with iframeEnabled', function () { - const result = adapter.getUserSyncs({iframeEnabled: true}, [SERVER_RESPONSE]); + const result = adapter.getUserSyncs({ iframeEnabled: true }, [SERVER_RESPONSE]); expect(result).to.deep.equal([{ type: 'iframe', @@ -463,7 +462,7 @@ describe('ShinezRtbBidAdapter', function () { }); it('should have valid user sync with cid on response', function () { - const result = adapter.getUserSyncs({iframeEnabled: true}, [SERVER_RESPONSE]); + const result = adapter.getUserSyncs({ iframeEnabled: true }, [SERVER_RESPONSE]); expect(result).to.deep.equal([{ type: 'iframe', url: 'https://sync.sweetgum.io/api/sync/iframe/?cid=testcid123&gdpr=0&gdpr_consent=&us_privacy=&coppa=0' @@ -471,7 +470,7 @@ describe('ShinezRtbBidAdapter', function () { }); it('should have valid user sync with pixelEnabled', function () { - const result = adapter.getUserSyncs({pixelEnabled: true}, [SERVER_RESPONSE]); + const result = adapter.getUserSyncs({ pixelEnabled: true }, [SERVER_RESPONSE]); expect(result).to.deep.equal([{ 'url': 'https://sync.sweetgum.io/api/sync/image/?cid=testcid123&gdpr=0&gdpr_consent=&us_privacy=&coppa=0', @@ -483,7 +482,7 @@ describe('ShinezRtbBidAdapter', function () { config.setConfig({ coppa: 1 }); - const result = adapter.getUserSyncs({iframeEnabled: true}, [SERVER_RESPONSE]); + const result = adapter.getUserSyncs({ iframeEnabled: true }, [SERVER_RESPONSE]); expect(result).to.deep.equal([{ type: 'iframe', url: 'https://sync.sweetgum.io/api/sync/iframe/?cid=testcid123&gdpr=0&gdpr_consent=&us_privacy=&coppa=1' @@ -498,12 +497,12 @@ describe('ShinezRtbBidAdapter', function () { }); it('should return empty array when there is no ad', function () { - const responses = adapter.interpretResponse({price: 1, ad: ''}); + const responses = adapter.interpretResponse({ price: 1, ad: '' }); expect(responses).to.be.empty; }); it('should return empty array when there is no price', function () { - const responses = adapter.interpretResponse({price: null, ad: 'great ad'}); + const responses = adapter.interpretResponse({ price: null, ad: 'great ad' }); expect(responses).to.be.empty; }); @@ -576,9 +575,9 @@ describe('ShinezRtbBidAdapter', function () { const userId = (function () { switch (idSystemProvider) { case 'lipb': - return {lipbid: id}; + return { lipbid: id }; case 'id5id': - return {uid: id}; + return { uid: id }; default: return id; } @@ -599,7 +598,7 @@ describe('ShinezRtbBidAdapter', function () { bid.userIdAsEids = [ { "source": "audigent.com", - "uids": [{"id": "fakeidi6j6dlc6e"}] + "uids": [{ "id": "fakeidi6j6dlc6e" }] } ] const requests = adapter.buildRequests([bid], BIDDER_REQUEST); @@ -610,11 +609,11 @@ describe('ShinezRtbBidAdapter', function () { bid.userIdAsEids = [ { "source": "audigent.com", - "uids": [{"id": "fakeidi6j6dlc6e"}] + "uids": [{ "id": "fakeidi6j6dlc6e" }] }, { "source": "rwdcntrl.net", - "uids": [{"id": "fakeid6f35197d5c", "atype": 1}] + "uids": [{ "id": "fakeid6f35197d5c", "atype": 1 }] } ] const requests = adapter.buildRequests([bid], BIDDER_REQUEST); @@ -629,7 +628,7 @@ describe('ShinezRtbBidAdapter', function () { eids: [ { "source": "pubcid.org", - "uids": [{"id": "fakeid8888dlc6e"}] + "uids": [{ "id": "fakeid8888dlc6e" }] } ] } @@ -644,11 +643,11 @@ describe('ShinezRtbBidAdapter', function () { eids: [ { "source": "pubcid.org", - "uids": [{"id": "fakeid8888dlc6e"}] + "uids": [{ "id": "fakeid8888dlc6e" }] }, { "source": "adserver.org", - "uids": [{"id": "fakeid495ff1"}] + "uids": [{ "id": "fakeid495ff1" }] } ] } @@ -661,18 +660,18 @@ describe('ShinezRtbBidAdapter', function () { describe('alternate param names extractors', function () { it('should return undefined when param not supported', function () { - const cid = extractCID({'c_id': '1'}); - const pid = extractPID({'p_id': '1'}); - const subDomain = extractSubDomain({'sub_domain': 'prebid'}); + const cid = extractCID({ 'c_id': '1' }); + const pid = extractPID({ 'p_id': '1' }); + const subDomain = extractSubDomain({ 'sub_domain': 'prebid' }); expect(cid).to.be.undefined; expect(pid).to.be.undefined; expect(subDomain).to.be.undefined; }); it('should return value when param supported', function () { - const cid = extractCID({'cID': '1'}); - const pid = extractPID({'Pid': '2'}); - const subDomain = extractSubDomain({'subDOMAIN': 'prebid'}); + const cid = extractCID({ 'cID': '1' }); + const pid = extractPID({ 'Pid': '2' }); + const subDomain = extractSubDomain({ 'subDOMAIN': 'prebid' }); expect(cid).to.be.equal('1'); expect(pid).to.be.equal('2'); expect(subDomain).to.be.equal('prebid'); @@ -680,6 +679,8 @@ describe('ShinezRtbBidAdapter', function () { }); describe('unique deal id', function () { + let clock; + before(function () { getGlobal().bidderSettings = { shinezRtb: { @@ -690,27 +691,31 @@ describe('ShinezRtbBidAdapter', function () { after(function () { getGlobal().bidderSettings = {}; }); - const key = 'myKey'; + let key; let uniqueDealId; beforeEach(() => { + clock = useFakeTimers({ + now: Date.now() + }); + key = `myKey_${Date.now()}`; uniqueDealId = getUniqueDealId(storage, key, 0); - }) + }); + + afterEach(() => { + clock.restore(); + }); - it('should get current unique deal id', function (done) { - // waiting some time so `now` will become past - setTimeout(() => { - const current = getUniqueDealId(storage, key); - expect(current).to.be.equal(uniqueDealId); - done(); - }, 200); + it('should get current unique deal id', function () { + // advance time in a deterministic way + clock.tick(200); + const current = getUniqueDealId(storage, key); + expect(current).to.be.equal(uniqueDealId); }); - it('should get new unique deal id on expiration', function (done) { - setTimeout(() => { - const current = getUniqueDealId(storage, key, 100); - expect(current).to.not.be.equal(uniqueDealId); - done(); - }, 200) + it('should get new unique deal id on expiration', function () { + clock.tick(200); + const current = getUniqueDealId(storage, key, 100); + expect(current).to.not.be.equal(uniqueDealId); }); }); @@ -732,7 +737,7 @@ describe('ShinezRtbBidAdapter', function () { now }); setStorageItem(storage, 'myKey', 2020); - const {value, created} = getStorageItem(storage, 'myKey'); + const { value, created } = getStorageItem(storage, 'myKey'); expect(created).to.be.equal(now); expect(value).to.be.equal(2020); expect(typeof value).to.be.equal('number'); @@ -748,8 +753,8 @@ describe('ShinezRtbBidAdapter', function () { }); it('should parse JSON value', function () { - const data = JSON.stringify({event: 'send'}); - const {event} = tryParseJSON(data); + const data = JSON.stringify({ event: 'send' }); + const { event } = tryParseJSON(data); expect(event).to.be.equal('send'); }); diff --git a/test/spec/modules/showheroes-bsBidAdapter_spec.js b/test/spec/modules/showheroes-bsBidAdapter_spec.js index 290447c3792..42e6626436a 100644 --- a/test/spec/modules/showheroes-bsBidAdapter_spec.js +++ b/test/spec/modules/showheroes-bsBidAdapter_spec.js @@ -117,19 +117,19 @@ describe('shBidAdapter', () => { bids: [bidRequestVideoV2], ...bidderRequest, ...gdpr, - ...{uspConsent: uspConsent}, + ...{ uspConsent: uspConsent }, ortb2: { source: { - ext: {schain: schain.schain.config} + ext: { schain: schain.schain.config } } } }; bidRequest.ortb2 = { source: { - ext: {schain: schain.schain.config} + ext: { schain: schain.schain.config } } }; - const getFloorResponse = {currency: 'EUR', floor: 3}; + const getFloorResponse = { currency: 'EUR', floor: 3 }; bidRequest.getFloor = () => getFloorResponse; const request = spec.buildRequests([bidRequest], await addFPDToBidderRequest(fullRequest)); const payload = request.data; diff --git a/test/spec/modules/silvermobBidAdapter_spec.js b/test/spec/modules/silvermobBidAdapter_spec.js index 21cdea24d18..a592396cc8b 100644 --- a/test/spec/modules/silvermobBidAdapter_spec.js +++ b/test/spec/modules/silvermobBidAdapter_spec.js @@ -1,5 +1,5 @@ import { expect } from 'chai'; -import {spec} from '../../../modules/silvermobBidAdapter.js'; +import { spec } from '../../../modules/silvermobBidAdapter.js'; import 'modules/priceFloors.js'; import { newBidder } from 'src/adapters/bidderFactory'; import { config } from '../../../src/config.js'; @@ -181,7 +181,7 @@ describe('silvermobAdapter', function () { }); it('should send the CCPA data in the request', async function () { - const serverRequest = spec.buildRequests([SIMPLE_BID_REQUEST], await addFPDToBidderRequest({...bidderRequest, ...{uspConsent: '1YYY'}})); + const serverRequest = spec.buildRequests([SIMPLE_BID_REQUEST], await addFPDToBidderRequest({ ...bidderRequest, ...{ uspConsent: '1YYY' } })); expect(serverRequest.data.regs.ext.us_privacy).to.equal('1YYY'); }); }); diff --git a/test/spec/modules/sirdataRtdProvider_spec.js b/test/spec/modules/sirdataRtdProvider_spec.js index da7c8756cc8..4eb28b52b26 100644 --- a/test/spec/modules/sirdataRtdProvider_spec.js +++ b/test/spec/modules/sirdataRtdProvider_spec.js @@ -13,11 +13,11 @@ import { setUidInStorage, sirdataSubmodule } from 'modules/sirdataRtdProvider.js'; -import {expect} from 'chai'; -import {deepSetValue} from 'src/utils.js'; -import {server} from 'test/mocks/xhr.js'; +import { expect } from 'chai'; +import { deepSetValue } from 'src/utils.js'; +import { server } from 'test/mocks/xhr.js'; -const responseHeader = {'Content-Type': 'application/json'}; +const responseHeader = { 'Content-Type': 'application/json' }; describe('sirdataRtdProvider', function () { describe('sirdata Submodule init', function () { @@ -62,16 +62,16 @@ describe('sirdataRtdProvider', function () { it('sets Id in Storage', function () { setUidInStorage('123456789'); const val = getUidFromStorage(); - expect(val).to.deep.equal([{source: 'sddan.com', uids: [{id: '123456789', atype: 1}]}]); + expect(val).to.deep.equal([{ source: 'sddan.com', uids: [{ id: '123456789', atype: 1 }] }]); }); }); describe('mergeEuidsArrays', function () { it('merges Euids Arrays', function () { - const object1 = [{source: 'sddan.com', uids: [{id: '123456789', atype: 1}]}]; - const object2 = [{source: 'sddan.com', uids: [{id: '987654321', atype: 1}]}]; + const object1 = [{ source: 'sddan.com', uids: [{ id: '123456789', atype: 1 }] }]; + const object2 = [{ source: 'sddan.com', uids: [{ id: '987654321', atype: 1 }] }]; const object3 = mergeEuidsArrays(object1, object2); - expect(object3).to.deep.equal([{source: 'sddan.com', uids: [{id: '123456789', atype: 1}, {id: '987654321', atype: 1}]}]); + expect(object3).to.deep.equal([{ source: 'sddan.com', uids: [{ id: '123456789', atype: 1 }, { id: '987654321', atype: 1 }] }]); }); }); @@ -156,7 +156,7 @@ describe('sirdataRtdProvider', function () { const firstData = { segments: [111111, 222222], - contextual_categories: {'333333': 100}, + contextual_categories: { '333333': 100 }, 'segtaxid': null, 'cattaxid': null, 'shared_taxonomy': { @@ -164,7 +164,7 @@ describe('sirdataRtdProvider', function () { 'segments': [444444, 555555], 'segtaxid': null, 'cattaxid': null, - 'contextual_categories': {'666666': 100} + 'contextual_categories': { '666666': 100 } } }, 'global_taxonomy': { @@ -172,7 +172,7 @@ describe('sirdataRtdProvider', function () { 'segments': [123, 234], 'segtaxid': 4, 'cattaxid': 7, - 'contextual_categories': {'345': 100, '456': 100} + 'contextual_categories': { '345': 100, '456': 100 } }, 'sddan_id': '123456789', 'post_content_token': '987654321' @@ -231,20 +231,20 @@ describe('sirdataRtdProvider', function () { } }, { bidder: 'proxistore', - params: {website: 'demo.sirdata.com', language: 'fr'}, + params: { website: 'demo.sirdata.com', language: 'fr' }, adUnitCode: 'HALFPAGE_CENTER_LOADER', transactionId: '92ac333a-a569-4827-abf1-01fc9d19278a', sizes: [[300, 600]], mediaTypes: { banner: { filteredSizeConfig: [ - {minViewPort: [1600, 0], sizes: [[300, 600]]}, + { minViewPort: [1600, 0], sizes: [[300, 600]] }, ], sizeConfig: [ - {minViewPort: [0, 0], sizes: [[300, 600]]}, - {minViewPort: [768, 0], sizes: [[300, 600]]}, - {minViewPort: [1200, 0], sizes: [[300, 600]]}, - {minViewPort: [1600, 0], sizes: [[300, 600]]}, + { minViewPort: [0, 0], sizes: [[300, 600]] }, + { minViewPort: [768, 0], sizes: [[300, 600]] }, + { minViewPort: [1200, 0], sizes: [[300, 600]] }, + { minViewPort: [1600, 0], sizes: [[300, 600]] }, ], sizes: [[300, 600]], }, @@ -280,19 +280,19 @@ describe('sirdataRtdProvider', function () { 'segments': [111111, 222222], 'segtaxid': null, 'cattaxid': null, - 'contextual_categories': {'333333': 100}, + 'contextual_categories': { '333333': 100 }, 'shared_taxonomy': { '27440': { 'segments': [444444, 555555], 'segtaxid': 552, 'cattaxid': 553, - 'contextual_categories': {'666666': 100} + 'contextual_categories': { '666666': 100 } }, '27446': { 'segments': [777777, 888888], 'segtaxid': 552, 'cattaxid': 553, - 'contextual_categories': {'999999': 100} + 'contextual_categories': { '999999': 100 } } }, 'global_taxonomy': { @@ -300,7 +300,7 @@ describe('sirdataRtdProvider', function () { 'segments': [123, 234], 'segtaxid': 4, 'cattaxid': 7, - 'contextual_categories': {'345': 100, '456': 100} + 'contextual_categories': { '345': 100, '456': 100 } } }, 'sddan_id': '123456789', @@ -317,8 +317,8 @@ describe('sirdataRtdProvider', function () { 'sirdata.com' ); expect(reqBidsConfigObj.ortb2Fragments.global.site.content.data[0].segment).to.eql([ - {id: '345'}, - {id: '456'} + { id: '345' }, + { id: '456' } ]); expect(reqBidsConfigObj.ortb2Fragments.global.site.content.data[0].ext.segtax).to.equal(7); @@ -326,8 +326,8 @@ describe('sirdataRtdProvider', function () { 'sirdata.com' ); expect(reqBidsConfigObj.ortb2Fragments.global.user.data[0].segment).to.eql([ - {id: '123'}, - {id: '234'} + { id: '123' }, + { id: '234' } ]); expect(reqBidsConfigObj.ortb2Fragments.global.user.data[0].ext.segtax).to.equal(4); }); diff --git a/test/spec/modules/sizeMappingV2_spec.js b/test/spec/modules/sizeMappingV2_spec.js index b384e21debe..0e192e366e5 100644 --- a/test/spec/modules/sizeMappingV2_spec.js +++ b/test/spec/modules/sizeMappingV2_spec.js @@ -650,7 +650,7 @@ describe('sizeMappingV2', function () { }, native: {} }, - bids: [{bidder: 'appnexus', params: 1234}] + bids: [{ bidder: 'appnexus', params: 1234 }] }]; checkAdUnitSetupHook(adUnit); @@ -672,7 +672,7 @@ describe('sizeMappingV2', function () { }, native: {} }, - bids: [{bidder: 'appnexus', params: 1234}] + bids: [{ bidder: 'appnexus', params: 1234 }] }]; checkAdUnitSetupHook(adUnit); @@ -691,7 +691,7 @@ describe('sizeMappingV2', function () { mediaTypes: { native: {} }, - bids: [{bidder: 'appnexus', params: 1234}] + bids: [{ bidder: 'appnexus', params: 1234 }] }]; checkAdUnitSetupHook(adUnit); @@ -1456,7 +1456,7 @@ describe('sizeMappingV2', function () { adUnitDetail.transformedMediaTypes.native = {}; const actual = setupAdUnitMediaTypes(adUnits, [])[0]; const bids = bidderMap(actual); - expect(bids.rubicon.mediaTypes).to.deep.equal({banner: adUnitDetail.transformedMediaTypes.banner}); + expect(bids.rubicon.mediaTypes).to.deep.equal({ banner: adUnitDetail.transformedMediaTypes.banner }); }); }); }); diff --git a/test/spec/modules/sizeMapping_spec.js b/test/spec/modules/sizeMapping_spec.js index 795e87e72f5..2b2bbd3ab0e 100644 --- a/test/spec/modules/sizeMapping_spec.js +++ b/test/spec/modules/sizeMapping_spec.js @@ -1,5 +1,5 @@ -import {expect} from 'chai'; -import {resolveStatus, setSizeConfig, sizeSupported} from 'modules/sizeMapping.js'; +import { expect } from 'chai'; +import { resolveStatus, setSizeConfig, sizeSupported } from 'modules/sizeMapping.js'; const utils = require('src/utils.js'); const deepClone = utils.deepClone; @@ -51,7 +51,7 @@ describe('sizeMapping', function () { sandbox = sinon.createSandbox(); - matchMediaOverride = {matches: false}; + matchMediaOverride = { matches: false }; sandbox.stub(utils.getWindowTop(), 'matchMedia').callsFake((...args) => { if (typeof matchMediaOverride === 'function') { @@ -69,7 +69,7 @@ describe('sizeMapping', function () { describe('sizeConfig', () => { it('should allow us to validate a single size', function () { - matchMediaOverride = (str) => str === '(min-width: 1200px)' ? {matches: true} : {matches: false}; + matchMediaOverride = (str) => str === '(min-width: 1200px)' ? { matches: true } : { matches: false }; expect(sizeSupported([300, 250])).to.equal(true); expect(sizeSupported([80, 80])).to.equal(false); @@ -122,11 +122,11 @@ describe('sizeMapping', function () { } } } - Object.entries(suites).forEach(([mediaType, {mediaTypes, getSizes}]) => { + Object.entries(suites).forEach(([mediaType, { mediaTypes, getSizes }]) => { describe(`for ${mediaType}`, () => { describe('when handling sizes', function () { 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}; + matchMediaOverride = (str) => str === '(min-width: 1200px)' ? { matches: true } : { matches: false }; const status = resolveStatus(undefined, mediaTypes, sizeConfig); @@ -140,7 +140,7 @@ describe('sizeMapping', function () { matchMediaOverride = (str) => [ '(min-width: 1200px)', '(min-width: 768px) and (max-width: 1199px)' - ].includes(str) ? {matches: true} : {matches: false}; + ].includes(str) ? { matches: true } : { matches: false }; const status = resolveStatus(undefined, mediaTypes, sizeConfig); expect(status.active).to.equal(true); @@ -150,7 +150,7 @@ describe('sizeMapping', function () { }); it('if no mediaQueries match, it should allow all sizes specified', function () { - matchMediaOverride = () => ({matches: false}); + matchMediaOverride = () => ({ matches: false }); const status = resolveStatus(undefined, mediaTypes, sizeConfig); expect(status.active).to.equal(true); @@ -158,7 +158,7 @@ describe('sizeMapping', function () { }); 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}; + matchMediaOverride = (str) => str === '(min-width: 0px) and (max-width: 767px)' ? { matches: true } : { matches: false }; const status = resolveStatus(undefined, mediaTypes, sizeConfig); expect(status.active).to.equal(false); @@ -166,7 +166,7 @@ describe('sizeMapping', function () { }); 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}; + matchMediaOverride = (str) => str === '(min-width: 0px) and (max-width: 767px)' ? { matches: true } : { matches: false }; const status = resolveStatus(undefined, Object.assign({}, mediaTypes, { native: { type: 'image' @@ -180,7 +180,7 @@ describe('sizeMapping', function () { }); 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}; + matchMediaOverride = (str) => str === '(min-width: 1200px)' ? { matches: true } : { matches: false }; const status = resolveStatus(undefined, mediaTypes, sizeConfigWithLabels); expect(status.active).to.equal(true); @@ -190,7 +190,7 @@ describe('sizeMapping', function () { 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}; + matchMediaOverride = (str) => str === '(min-width: 1200px)' ? { matches: true } : { matches: false }; let status = resolveStatus({ labels: ['desktop'] @@ -252,7 +252,7 @@ describe('sizeMapping', function () { if (FEATURES.VIDEO) { 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}; + matchMediaOverride = (str) => str === '(min-width: 768px) and (max-width: 1199px)' ? { matches: true } : { matches: false }; const multiFormatSizes = { banner: { diff --git a/test/spec/modules/smaatoBidAdapter_spec.js b/test/spec/modules/smaatoBidAdapter_spec.js index f1bd464fbdd..8edeabd466b 100644 --- a/test/spec/modules/smaatoBidAdapter_spec.js +++ b/test/spec/modules/smaatoBidAdapter_spec.js @@ -1,6 +1,6 @@ -import {spec} from 'modules/smaatoBidAdapter.js'; +import { spec } from 'modules/smaatoBidAdapter.js'; import * as utils from 'src/utils.js'; -import {config} from 'src/config.js'; +import { config } from 'src/config.js'; // load modules that register ORTB processors import 'src/prebid.js' @@ -72,50 +72,32 @@ describe('smaatoBidAdapterTest', () => { }); it('is invalid, when params object is empty', () => { - expect(spec.isBidRequestValid({params: {}})).to.be.false; + expect(spec.isBidRequestValid({ params: {} })).to.be.false; }); it('is invalid, when publisherId is present but of wrong type', () => { - expect(spec.isBidRequestValid({params: {publisherId: 123}})).to.be.false; + expect(spec.isBidRequestValid({ params: { publisherId: 123 } })).to.be.false; }); - describe('for ad pod / long form video requests', () => { - const ADPOD = {video: {context: 'adpod'}} - it('is invalid, when adbreakId is missing', () => { - expect(spec.isBidRequestValid({mediaTypes: ADPOD, params: {publisherId: '123'}})).to.be.false; - }); - - it('is invalid, when adbreakId is present but of wrong type', () => { - expect(spec.isBidRequestValid({mediaTypes: ADPOD, params: {publisherId: '123', adbreakId: 456}})).to.be.false; - }); - - it('is valid, when required params are present', () => { - expect(spec.isBidRequestValid({mediaTypes: ADPOD, params: {publisherId: '123', adbreakId: '456'}})).to.be.true; - }); - - it('is invalid, when forbidden adspaceId param is present', () => { - expect(spec.isBidRequestValid({ - mediaTypes: ADPOD, - params: {publisherId: '123', adbreakId: '456', adspaceId: '42'} - })).to.be.false; - }); + it('is invalid, when adbreakId param is present', () => { + expect(spec.isBidRequestValid({ params: { publisherId: '123', adspaceId: '456', adbreakId: '42' } })).to.be.false; }); - describe('for non adpod requests', () => { + describe('for supported requests', () => { it('is invalid, when adspaceId is missing', () => { - expect(spec.isBidRequestValid({params: {publisherId: '123'}})).to.be.false; + expect(spec.isBidRequestValid({ params: { publisherId: '123' } })).to.be.false; }); it('is invalid, when adspaceId is present but of wrong type', () => { - expect(spec.isBidRequestValid({params: {publisherId: '123', adspaceId: 456}})).to.be.false; + expect(spec.isBidRequestValid({ params: { publisherId: '123', adspaceId: 456 } })).to.be.false; }); it('is valid, when required params are present for minimal request', () => { - expect(spec.isBidRequestValid({params: {publisherId: '123', adspaceId: '456'}})).to.be.true; + expect(spec.isBidRequestValid({ params: { publisherId: '123', adspaceId: '456' } })).to.be.true; }); it('is invalid, when forbidden adbreakId param is present', () => { - expect(spec.isBidRequestValid({params: {publisherId: '123', adspaceId: '456', adbreakId: '42'}})).to.be.false; + expect(spec.isBidRequestValid({ params: { publisherId: '123', adspaceId: '456', adbreakId: '42' } })).to.be.false; }); }); }); @@ -291,7 +273,7 @@ describe('smaatoBidAdapterTest', () => { }, }; - const reqs = spec.buildRequests([singleBannerBidRequest], {...defaultBidderRequest, ortb2}); + const reqs = spec.buildRequests([singleBannerBidRequest], { ...defaultBidderRequest, ortb2 }); const req = extractPayloadOfFirstAndOnlyRequest(reqs); expect(req.site.id).to.exist.and.to.be.a('string'); @@ -316,7 +298,7 @@ describe('smaatoBidAdapterTest', () => { }, }; - const reqs = spec.buildRequests([singleBannerBidRequest], {...defaultBidderRequest, ortb2}); + const reqs = spec.buildRequests([singleBannerBidRequest], { ...defaultBidderRequest, ortb2 }); const req = extractPayloadOfFirstAndOnlyRequest(reqs); expect(req.dooh.id).to.exist.and.to.be.a('string'); @@ -346,7 +328,7 @@ describe('smaatoBidAdapterTest', () => { }, }; - const reqs = spec.buildRequests([singleBannerBidRequest], {...defaultBidderRequest, ortb2}); + const reqs = spec.buildRequests([singleBannerBidRequest], { ...defaultBidderRequest, ortb2 }); const req = extractPayloadOfFirstAndOnlyRequest(reqs); expect(req.device.language).to.equal(language); @@ -372,7 +354,7 @@ describe('smaatoBidAdapterTest', () => { }, }; - const reqs = spec.buildRequests([singleBannerBidRequest], {...defaultBidderRequest, ortb2}); + const reqs = spec.buildRequests([singleBannerBidRequest], { ...defaultBidderRequest, ortb2 }); const req = extractPayloadOfFirstAndOnlyRequest(reqs); expect(req.regs.coppa).to.equal(1); @@ -401,7 +383,7 @@ describe('smaatoBidAdapterTest', () => { } }; - const reqs = spec.buildRequests([singleBannerBidRequest], {...defaultBidderRequest, ortb2}); + const reqs = spec.buildRequests([singleBannerBidRequest], { ...defaultBidderRequest, ortb2 }); const req = extractPayloadOfFirstAndOnlyRequest(reqs); expect(req.regs.ext.gpp).to.eql('gppString'); @@ -416,8 +398,8 @@ describe('smaatoBidAdapterTest', () => { }); it('sends instl if instl exists', () => { - const instl = {instl: 1}; - const bidRequestWithInstl = Object.assign({}, singleBannerBidRequest, {ortb2Imp: instl}); + const instl = { instl: 1 }; + const bidRequestWithInstl = Object.assign({}, singleBannerBidRequest, { ortb2Imp: instl }); const reqs = spec.buildRequests([bidRequestWithInstl], defaultBidderRequest); @@ -462,7 +444,7 @@ describe('smaatoBidAdapterTest', () => { } }; - const reqs = spec.buildRequests([singleBannerBidRequest], {...defaultBidderRequest, ortb2}); + const reqs = spec.buildRequests([singleBannerBidRequest], { ...defaultBidderRequest, ortb2 }); const req = extractPayloadOfFirstAndOnlyRequest(reqs); expect(req.user.gender).to.equal('M'); @@ -512,7 +494,7 @@ describe('smaatoBidAdapterTest', () => { } }; - const reqs = spec.buildRequests([singleBannerBidRequest], {...defaultBidderRequest, ortb2}); + const reqs = spec.buildRequests([singleBannerBidRequest], { ...defaultBidderRequest, ortb2 }); const req = extractPayloadOfFirstAndOnlyRequest(reqs); expect(req.regs.ext.dsa.dsarequired).to.eql(2); @@ -529,7 +511,7 @@ describe('smaatoBidAdapterTest', () => { } }; - const reqs = spec.buildRequests([singleBannerBidRequest], {...defaultBidderRequest, ortb2}); + const reqs = spec.buildRequests([singleBannerBidRequest], { ...defaultBidderRequest, ortb2 }); const req = extractPayloadOfFirstAndOnlyRequest(reqs); expect(req.regs.ext.dsa).to.be.undefined; @@ -566,7 +548,7 @@ describe('smaatoBidAdapterTest', () => { skip: 1, skipmin: 5, api: [7], - ext: {rewarded: 0} + ext: { rewarded: 0 } }; const VIDEO_OUTSTREAM_OPENRTB_IMP = { mimes: ['video/mp4', 'video/quicktime', 'video/3gpp', 'video/x-m4v'], @@ -633,8 +615,8 @@ describe('smaatoBidAdapterTest', () => { }); it('sends instl if instl exists', () => { - const instl = {instl: 1}; - const bidRequestWithInstl = Object.assign({}, singleVideoBidRequest, {ortb2Imp: instl}); + const instl = { instl: 1 }; + const bidRequestWithInstl = Object.assign({}, singleVideoBidRequest, { ortb2Imp: instl }); const reqs = spec.buildRequests([bidRequestWithInstl], defaultBidderRequest); @@ -672,299 +654,6 @@ describe('smaatoBidAdapterTest', () => { expect(JSON.parse(reqs[1].data).imp[0].banner).to.not.exist; expect(JSON.parse(reqs[1].data).imp[0].video).to.deep.equal(VIDEO_OUTSTREAM_OPENRTB_IMP); }); - - describe('ad pod / long form video', () => { - describe('required parameters with requireExactDuration false', () => { - const ADBREAK_ID = 'adbreakId'; - const ADPOD = 'adpod'; - const BID_ID = '4331'; - const W = 640; - const H = 480; - const ADPOD_DURATION = 300; - const DURATION_RANGE = [15, 30]; - const longFormVideoBidRequest = { - params: { - publisherId: 'publisherId', - adbreakId: ADBREAK_ID, - }, - mediaTypes: { - video: { - context: ADPOD, - playerSize: [[W, H]], - adPodDurationSec: ADPOD_DURATION, - durationRangeSec: DURATION_RANGE, - requireExactDuration: false - } - }, - bidId: BID_ID - }; - - it('sends required fields', () => { - const reqs = spec.buildRequests([longFormVideoBidRequest], defaultBidderRequest); - - const req = extractPayloadOfFirstAndOnlyRequest(reqs); - expect(req.id).to.exist; - expect(req.imp.length).to.be.equal(ADPOD_DURATION / DURATION_RANGE[0]); - expect(req.imp[0].id).to.be.equal(BID_ID); - expect(req.imp[0].tagid).to.be.equal(ADBREAK_ID); - expect(req.imp[0].bidfloor).to.be.undefined; - expect(req.imp[0].video.ext.context).to.be.equal(ADPOD); - expect(req.imp[0].video.w).to.be.equal(W); - expect(req.imp[0].video.h).to.be.equal(H); - expect(req.imp[0].video.maxduration).to.be.equal(DURATION_RANGE[1]); - expect(req.imp[0].video.sequence).to.be.equal(1); - expect(req.imp[1].id).to.be.equal(BID_ID); - expect(req.imp[1].tagid).to.be.equal(ADBREAK_ID); - expect(req.imp[1].bidfloor).to.be.undefined; - expect(req.imp[1].video.ext.context).to.be.equal(ADPOD); - expect(req.imp[1].video.w).to.be.equal(W); - expect(req.imp[1].video.h).to.be.equal(H); - expect(req.imp[1].video.maxduration).to.be.equal(DURATION_RANGE[1]); - expect(req.imp[1].video.sequence).to.be.equal(2); - }); - - it('sends instl if instl exists', () => { - const instl = {instl: 1}; - const bidRequestWithInstl = Object.assign({}, longFormVideoBidRequest, {ortb2Imp: instl}); - - const reqs = spec.buildRequests([bidRequestWithInstl], defaultBidderRequest); - - const req = extractPayloadOfFirstAndOnlyRequest(reqs); - expect(req.imp[0].instl).to.equal(1); - expect(req.imp[1].instl).to.equal(1); - }); - - it('sends bidfloor when configured', () => { - const longFormVideoBidRequestWithFloor = Object.assign({}, longFormVideoBidRequest); - longFormVideoBidRequestWithFloor.getFloor = function (arg) { - if (arg.currency === 'USD' && - arg.mediaType === 'video' && - JSON.stringify(arg.size) === JSON.stringify([640, 480])) { - return { - currency: 'USD', - floor: 0.789 - } - } - } - const reqs = spec.buildRequests([longFormVideoBidRequestWithFloor], defaultBidderRequest); - - const req = extractPayloadOfFirstAndOnlyRequest(reqs); - expect(req.imp[0].bidfloor).to.be.equal(0.789); - expect(req.imp[1].bidfloor).to.be.equal(0.789); - }); - - it('sends brand category exclusion as true when config is set to true', () => { - config.setConfig({adpod: {brandCategoryExclusion: true}}); - - const reqs = spec.buildRequests([longFormVideoBidRequest], defaultBidderRequest); - - const req = extractPayloadOfFirstAndOnlyRequest(reqs); - expect(req.imp[0].video.ext.brandcategoryexclusion).to.be.equal(true); - }); - - it('sends brand category exclusion as false when config is set to false', () => { - config.setConfig({adpod: {brandCategoryExclusion: false}}); - - const reqs = spec.buildRequests([longFormVideoBidRequest], defaultBidderRequest); - - const req = extractPayloadOfFirstAndOnlyRequest(reqs); - expect(req.imp[0].video.ext.brandcategoryexclusion).to.be.equal(false); - }); - - it('sends brand category exclusion as false when config is not set', () => { - const reqs = spec.buildRequests([longFormVideoBidRequest], defaultBidderRequest); - - const req = extractPayloadOfFirstAndOnlyRequest(reqs); - expect(req.imp[0].video.ext.brandcategoryexclusion).to.be.equal(false); - }); - }); - describe('required parameters with requireExactDuration true', () => { - const ADBREAK_ID = 'adbreakId'; - const ADPOD = 'adpod'; - const BID_ID = '4331'; - const W = 640; - const H = 480; - const ADPOD_DURATION = 5; - const DURATION_RANGE = [5, 15, 25]; - const longFormVideoBidRequest = { - params: { - publisherId: 'publisherId', - adbreakId: ADBREAK_ID, - }, - mediaTypes: { - video: { - context: ADPOD, - playerSize: [[W, H]], - adPodDurationSec: ADPOD_DURATION, - durationRangeSec: DURATION_RANGE, - requireExactDuration: true - } - }, - bidId: BID_ID - }; - - it('sends required fields', () => { - const reqs = spec.buildRequests([longFormVideoBidRequest], defaultBidderRequest); - - const req = extractPayloadOfFirstAndOnlyRequest(reqs); - expect(req.id).to.exist; - expect(req.imp.length).to.be.equal(DURATION_RANGE.length); - expect(req.imp[0].id).to.be.equal(BID_ID); - expect(req.imp[0].tagid).to.be.equal(ADBREAK_ID); - expect(req.imp[0].video.ext.context).to.be.equal(ADPOD); - expect(req.imp[0].video.w).to.be.equal(W); - expect(req.imp[0].video.h).to.be.equal(H); - expect(req.imp[0].video.minduration).to.be.equal(DURATION_RANGE[0]); - expect(req.imp[0].video.maxduration).to.be.equal(DURATION_RANGE[0]); - expect(req.imp[0].video.sequence).to.be.equal(1); - expect(req.imp[1].id).to.be.equal(BID_ID); - expect(req.imp[1].tagid).to.be.equal(ADBREAK_ID); - expect(req.imp[1].video.ext.context).to.be.equal(ADPOD); - expect(req.imp[1].video.w).to.be.equal(W); - expect(req.imp[1].video.h).to.be.equal(H); - expect(req.imp[1].video.minduration).to.be.equal(DURATION_RANGE[1]); - expect(req.imp[1].video.maxduration).to.be.equal(DURATION_RANGE[1]); - expect(req.imp[1].video.sequence).to.be.equal(2); - expect(req.imp[2].id).to.be.equal(BID_ID); - expect(req.imp[2].tagid).to.be.equal(ADBREAK_ID); - expect(req.imp[2].video.ext.context).to.be.equal(ADPOD); - expect(req.imp[2].video.w).to.be.equal(W); - expect(req.imp[2].video.h).to.be.equal(H); - expect(req.imp[2].video.minduration).to.be.equal(DURATION_RANGE[2]); - expect(req.imp[2].video.maxduration).to.be.equal(DURATION_RANGE[2]); - expect(req.imp[2].video.sequence).to.be.equal(3); - }); - }); - - describe('forwarding of optional parameters', () => { - const MIMES = ['video/mp4', 'video/quicktime', 'video/3gpp', 'video/x-m4v']; - const STARTDELAY = 0; - const LINEARITY = 1; - const SKIP = 1; - const PROTOCOLS = [7]; - const SKIPMIN = 5; - const API = [7]; - const validBasicAdpodBidRequest = { - params: { - publisherId: 'publisherId', - adbreakId: 'adbreakId', - }, - mediaTypes: { - video: { - context: 'adpod', - playerSize: [640, 480], - adPodDurationSec: 300, - durationRangeSec: [15, 30], - mimes: MIMES, - startdelay: STARTDELAY, - linearity: LINEARITY, - skip: SKIP, - protocols: PROTOCOLS, - skipmin: SKIPMIN, - api: API - } - }, - bidId: 'bidId' - }; - - it('sends general video fields when they are present', () => { - const reqs = spec.buildRequests([validBasicAdpodBidRequest], defaultBidderRequest); - - const req = extractPayloadOfFirstAndOnlyRequest(reqs); - expect(req.imp[0].video.mimes).to.eql(MIMES); - expect(req.imp[0].video.startdelay).to.be.equal(STARTDELAY); - expect(req.imp[0].video.linearity).to.be.equal(LINEARITY); - expect(req.imp[0].video.skip).to.be.equal(SKIP); - expect(req.imp[0].video.protocols).to.eql(PROTOCOLS); - expect(req.imp[0].video.skipmin).to.be.equal(SKIPMIN); - expect(req.imp[0].video.api).to.eql(API); - }); - - it('sends series name when parameter is present', () => { - const SERIES_NAME = 'foo' - const adpodRequestWithParameter = utils.deepClone(validBasicAdpodBidRequest); - adpodRequestWithParameter.mediaTypes.video.tvSeriesName = SERIES_NAME; - - const reqs = spec.buildRequests([adpodRequestWithParameter], defaultBidderRequest); - - const req = extractPayloadOfFirstAndOnlyRequest(reqs); - expect(req.site.content.series).to.be.equal(SERIES_NAME); - }); - - it('sends episode name when parameter is present', () => { - const EPISODE_NAME = 'foo' - const adpodRequestWithParameter = utils.deepClone(validBasicAdpodBidRequest); - adpodRequestWithParameter.mediaTypes.video.tvEpisodeName = EPISODE_NAME; - - const reqs = spec.buildRequests([adpodRequestWithParameter], defaultBidderRequest); - - const req = extractPayloadOfFirstAndOnlyRequest(reqs); - expect(req.site.content.title).to.be.equal(EPISODE_NAME); - }); - - it('sends season number as string when parameter is present', () => { - const SEASON_NUMBER_AS_NUMBER_IN_PREBID_REQUEST = 42 - const SEASON_NUMBER_AS_STRING_IN_OUTGOING_REQUEST = '42' - const adpodRequestWithParameter = utils.deepClone(validBasicAdpodBidRequest); - adpodRequestWithParameter.mediaTypes.video.tvSeasonNumber = SEASON_NUMBER_AS_NUMBER_IN_PREBID_REQUEST; - - const reqs = spec.buildRequests([adpodRequestWithParameter], defaultBidderRequest); - - const req = extractPayloadOfFirstAndOnlyRequest(reqs); - expect(req.site.content.season).to.be.equal(SEASON_NUMBER_AS_STRING_IN_OUTGOING_REQUEST); - }); - - it('sends episode number when parameter is present', () => { - const EPISODE_NUMBER = 42 - const adpodRequestWithParameter = utils.deepClone(validBasicAdpodBidRequest); - adpodRequestWithParameter.mediaTypes.video.tvEpisodeNumber = EPISODE_NUMBER; - - const reqs = spec.buildRequests([adpodRequestWithParameter], defaultBidderRequest); - - const req = extractPayloadOfFirstAndOnlyRequest(reqs); - expect(req.site.content.episode).to.be.equal(EPISODE_NUMBER); - }); - - it('sends content length when parameter is present', () => { - const LENGTH = 42 - const adpodRequestWithParameter = utils.deepClone(validBasicAdpodBidRequest); - adpodRequestWithParameter.mediaTypes.video.contentLengthSec = LENGTH; - - const reqs = spec.buildRequests([adpodRequestWithParameter], defaultBidderRequest); - - const req = extractPayloadOfFirstAndOnlyRequest(reqs); - expect(req.site.content.len).to.be.equal(LENGTH); - }); - - it('sends livestream as 1 when content mode parameter is live', () => { - const adpodRequestWithParameter = utils.deepClone(validBasicAdpodBidRequest); - adpodRequestWithParameter.mediaTypes.video.contentMode = 'live'; - - const reqs = spec.buildRequests([adpodRequestWithParameter], defaultBidderRequest); - - const req = extractPayloadOfFirstAndOnlyRequest(reqs); - expect(req.site.content.livestream).to.be.equal(1); - }); - - it('sends livestream as 0 when content mode parameter is on-demand', () => { - const adpodRequestWithParameter = utils.deepClone(validBasicAdpodBidRequest); - adpodRequestWithParameter.mediaTypes.video.contentMode = 'on-demand'; - - const reqs = spec.buildRequests([adpodRequestWithParameter], defaultBidderRequest); - - const req = extractPayloadOfFirstAndOnlyRequest(reqs); - expect(req.site.content.livestream).to.be.equal(0); - }); - - it('doesn\'t send any optional parameters when none are present', () => { - const reqs = spec.buildRequests([validBasicAdpodBidRequest], defaultBidderRequest); - - const req = extractPayloadOfFirstAndOnlyRequest(reqs); - expect(req.imp[0].video.ext.requireExactDuration).to.not.exist; - expect(req.site.content).to.not.exist; - }); - }); - }); } }); @@ -1159,7 +848,7 @@ describe('smaatoBidAdapterTest', () => { it('when geo and ifa info present, then add both to device object', () => { const inAppBidRequest = utils.deepClone(inAppBidRequestWithoutAppParams); - inAppBidRequest.params.app = {ifa: DEVICE_ID, geo: LOCATION}; + inAppBidRequest.params.app = { ifa: DEVICE_ID, geo: LOCATION }; const reqs = spec.buildRequests([inAppBidRequest], defaultBidderRequest); @@ -1180,9 +869,9 @@ describe('smaatoBidAdapterTest', () => { }; const inAppBidRequest = utils.deepClone(inAppBidRequestWithoutAppParams); - inAppBidRequest.params.app = {ifa: DEVICE_ID, geo: LOCATION}; + inAppBidRequest.params.app = { ifa: DEVICE_ID, geo: LOCATION }; - const reqs = spec.buildRequests([inAppBidRequest], {...defaultBidderRequest, ortb2}); + const reqs = spec.buildRequests([inAppBidRequest], { ...defaultBidderRequest, ortb2 }); const req = extractPayloadOfFirstAndOnlyRequest(reqs); expect(req.device.geo.lat).to.equal(53.5488); @@ -1192,7 +881,7 @@ describe('smaatoBidAdapterTest', () => { it('when ifa is present but geo is missing, then add only ifa to device object', () => { const inAppBidRequest = utils.deepClone(inAppBidRequestWithoutAppParams); - inAppBidRequest.params.app = {ifa: DEVICE_ID}; + inAppBidRequest.params.app = { ifa: DEVICE_ID }; const reqs = spec.buildRequests([inAppBidRequest], defaultBidderRequest); @@ -1203,7 +892,7 @@ describe('smaatoBidAdapterTest', () => { it('when geo is present but ifa is missing, then add only geo to device object', () => { const inAppBidRequest = utils.deepClone(inAppBidRequestWithoutAppParams); - inAppBidRequest.params.app = {geo: LOCATION}; + inAppBidRequest.params.app = { geo: LOCATION }; const reqs = spec.buildRequests([inAppBidRequest], defaultBidderRequest); @@ -1246,7 +935,7 @@ describe('smaatoBidAdapterTest', () => { tdid: '89145' }, userIdAsEids: [ - {id: 1}, {id: 2} + { id: 1 }, { id: 2 } ] }; @@ -1290,7 +979,7 @@ describe('smaatoBidAdapterTest', () => { }); describe('interpretResponse', () => { - function buildBidRequest(payloadAsJsObj = {imp: [{}]}) { + function buildBidRequest(payloadAsJsObj = { imp: [{}] }) { return { method: 'POST', url: 'https://prebid.ad.smaato.net/oapi/prebid', @@ -1385,7 +1074,7 @@ describe('smaatoBidAdapterTest', () => { adm = ''; break; case ADTYPE_NATIVE: - adm = JSON.stringify({native: NATIVE_RESPONSE}) + adm = JSON.stringify({ native: NATIVE_RESPONSE }) break; default: throw Error('Invalid AdType'); @@ -1436,7 +1125,7 @@ describe('smaatoBidAdapterTest', () => { }; it('returns empty array on no bid responses', () => { - const response_with_empty_body = {body: {}}; + const response_with_empty_body = { body: {} }; const bids = spec.interpretResponse(response_with_empty_body, buildBidRequest()); @@ -1539,7 +1228,6 @@ describe('smaatoBidAdapterTest', () => { }); describe('ad pod', () => { - const bidRequestWithAdpodContext = buildBidRequest({imp: [{video: {ext: {context: 'adpod'}}}]}); const PRIMARY_CAT_ID = 1337 const serverResponse = { body: { @@ -1576,46 +1264,8 @@ describe('smaatoBidAdapterTest', () => { } ] }, - headers: {get: () => undefined} + headers: { get: () => undefined } }; - - it('sets required values for adpod bid from server response', () => { - const bids = spec.interpretResponse(serverResponse, bidRequestWithAdpodContext); - - expect(bids).to.deep.equal([ - { - requestId: '226416e6e6bf41', - cpm: 0.01, - width: 350, - height: 50, - vastXml: '', - ttl: 300, - creativeId: 'CR69381', - dealId: '12345', - netRevenue: true, - currency: 'USD', - mediaType: 'video', - video: { - context: 'adpod', - durationSeconds: 42 - }, - meta: { - advertiserDomains: ['smaato.com'], - agencyId: 'CM6523', - networkName: 'smaato', - mediaType: 'video' - } - } - ]); - }); - - it('sets primary category id in case of enabled brand category exclusion', () => { - config.setConfig({adpod: {brandCategoryExclusion: true}}); - - const bids = spec.interpretResponse(serverResponse, bidRequestWithAdpodContext) - - expect(bids[0].meta.primaryCatId).to.be.equal(PRIMARY_CAT_ID) - }) }); it('uses correct TTL when expire header exists', () => { @@ -1640,7 +1290,7 @@ describe('smaatoBidAdapterTest', () => { it('uses net revenue flag send from server', () => { const resp = buildOpenRtbBidResponse(ADTYPE_IMG); - resp.body.seatbid[0].bid[0].ext = {net: false}; + resp.body.seatbid[0].bid[0].ext = { net: false }; const bids = spec.interpretResponse(resp, buildBidRequest()); @@ -1687,7 +1337,7 @@ describe('smaatoBidAdapterTest', () => { }) it('when pixelEnabled true then returns image sync', () => { - expect(spec.getUserSyncs({pixelEnabled: true}, null, null, null)).to.deep.equal( + expect(spec.getUserSyncs({ pixelEnabled: true }, null, null, null)).to.deep.equal( [ { type: 'image', @@ -1698,7 +1348,7 @@ describe('smaatoBidAdapterTest', () => { }) it('when iframeEnabled true then returns iframe sync', () => { - expect(spec.getUserSyncs({iframeEnabled: true}, null, null, null)).to.deep.equal( + expect(spec.getUserSyncs({ iframeEnabled: true }, null, null, null)).to.deep.equal( [ { type: 'iframe', @@ -1709,8 +1359,8 @@ describe('smaatoBidAdapterTest', () => { }) it('when iframeEnabled true and syncsPerBidder then returns iframe sync', () => { - config.setConfig({userSync: {syncsPerBidder: 5}}); - expect(spec.getUserSyncs({iframeEnabled: true}, null, null, null)).to.deep.equal( + config.setConfig({ userSync: { syncsPerBidder: 5 } }); + expect(spec.getUserSyncs({ iframeEnabled: true }, null, null, null)).to.deep.equal( [ { type: 'iframe', @@ -1721,7 +1371,7 @@ describe('smaatoBidAdapterTest', () => { }) it('when iframeEnabled and pixelEnabled true then returns iframe sync', () => { - expect(spec.getUserSyncs({pixelEnabled: true, iframeEnabled: true}, null, null, null)).to.deep.equal( + expect(spec.getUserSyncs({ pixelEnabled: true, iframeEnabled: true }, null, null, null)).to.deep.equal( [ { type: 'iframe', @@ -1732,7 +1382,7 @@ describe('smaatoBidAdapterTest', () => { }) it('when pixelEnabled true and gdprConsent then returns image sync with gdpr params', () => { - expect(spec.getUserSyncs({pixelEnabled: true}, null, {gdprApplies: true, consentString: CONSENT_STRING}, null)).to.deep.equal( + expect(spec.getUserSyncs({ pixelEnabled: true }, null, { gdprApplies: true, consentString: CONSENT_STRING }, null)).to.deep.equal( [ { type: 'image', @@ -1743,7 +1393,7 @@ describe('smaatoBidAdapterTest', () => { }) it('when iframeEnabled true and gdprConsent then returns iframe with gdpr params', () => { - expect(spec.getUserSyncs({iframeEnabled: true}, null, {gdprApplies: true, consentString: CONSENT_STRING}, null)).to.deep.equal( + expect(spec.getUserSyncs({ iframeEnabled: true }, null, { gdprApplies: true, consentString: CONSENT_STRING }, null)).to.deep.equal( [ { type: 'iframe', @@ -1754,7 +1404,7 @@ describe('smaatoBidAdapterTest', () => { }) it('when pixelEnabled true and gdprConsent without gdpr then returns pixel sync with gdpr_consent', () => { - expect(spec.getUserSyncs({pixelEnabled: true}, null, {consentString: CONSENT_STRING}, null), null).to.deep.equal( + expect(spec.getUserSyncs({ pixelEnabled: true }, null, { consentString: CONSENT_STRING }, null), null).to.deep.equal( [ { type: 'image', @@ -1765,7 +1415,7 @@ describe('smaatoBidAdapterTest', () => { }) it('when iframeEnabled true and gdprConsent without gdpr then returns iframe sync with gdpr_consent', () => { - expect(spec.getUserSyncs({iframeEnabled: true}, null, {consentString: CONSENT_STRING}, null), null).to.deep.equal( + expect(spec.getUserSyncs({ iframeEnabled: true }, null, { consentString: CONSENT_STRING }, null), null).to.deep.equal( [ { type: 'iframe', diff --git a/test/spec/modules/smarthubBidAdapter_spec.js b/test/spec/modules/smarthubBidAdapter_spec.js index 29607365c68..b2bf9a3bdd2 100644 --- a/test/spec/modules/smarthubBidAdapter_spec.js +++ b/test/spec/modules/smarthubBidAdapter_spec.js @@ -446,7 +446,7 @@ describe('SmartHubBidAdapter', function () { const syncData = spec.getUserSyncs({}, {}, { consentString: 'ALL', gdprApplies: true, - }, {}); + }, undefined); expect(syncData).to.be.an('array').which.is.not.empty; expect(syncData[0]).to.be.an('object') expect(syncData[0].type).to.be.a('string') @@ -455,9 +455,7 @@ describe('SmartHubBidAdapter', function () { expect(syncData[0].url).to.equal('https://us4.shb-sync.com/image?pbjs=1&gdpr=1&gdpr_consent=ALL&coppa=0&pid=360') }); it('Should return array of objects with CCPA values', function() { - const syncData = spec.getUserSyncs({}, {}, {}, { - consentString: '1---' - }); + const syncData = spec.getUserSyncs({}, {}, {}, '1---'); expect(syncData).to.be.an('array').which.is.not.empty; expect(syncData[0]).to.be.an('object') expect(syncData[0].type).to.be.a('string') @@ -466,7 +464,7 @@ describe('SmartHubBidAdapter', function () { expect(syncData[0].url).to.equal('https://us4.shb-sync.com/image?pbjs=1&ccpa_consent=1---&coppa=0&pid=360') }); it('Should return array of objects with GPP values', function() { - const syncData = spec.getUserSyncs({}, {}, {}, {}, { + const syncData = spec.getUserSyncs({}, {}, {}, undefined, { gppString: 'ab12345', applicableSections: [8] }); @@ -478,7 +476,7 @@ describe('SmartHubBidAdapter', function () { expect(syncData[0].url).to.equal('https://us4.shb-sync.com/image?pbjs=1&gpp=ab12345&gpp_sid=8&coppa=0&pid=360') }); it('Should return iframe type if iframeEnabled is true', function() { - const syncData = spec.getUserSyncs({iframeEnabled: true}, {}, {}, {}, {}); + const syncData = spec.getUserSyncs({ iframeEnabled: true }, {}, {}, undefined, {}); expect(syncData).to.be.an('array').which.is.not.empty; expect(syncData[0]).to.be.an('object') expect(syncData[0].type).to.be.a('string') diff --git a/test/spec/modules/smarticoBidAdapter_spec.js b/test/spec/modules/smarticoBidAdapter_spec.js index 50fba36e23f..7f0a2df62ae 100644 --- a/test/spec/modules/smarticoBidAdapter_spec.js +++ b/test/spec/modules/smarticoBidAdapter_spec.js @@ -1,6 +1,6 @@ -import {expect} from 'chai'; -import {spec} from 'modules/smarticoBidAdapter.js'; -import {newBidder} from 'src/adapters/bidderFactory.js'; +import { expect } from 'chai'; +import { spec } from 'modules/smarticoBidAdapter.js'; +import { newBidder } from 'src/adapters/bidderFactory.js'; describe('smarticoBidAdapter', function () { const adapter = newBidder(spec); @@ -13,7 +13,7 @@ describe('smarticoBidAdapter', function () { bidderRequestsCount: 1, bidderWinsCount: 0, bidId: '22499d052045', - mediaTypes: {banner: {sizes: [[300, 250]]}}, + mediaTypes: { banner: { sizes: [[300, 250]] } }, params: { token: 'FNVzUGZn9ebpIOoheh3kEJ2GQ6H6IyMH39sHXaya', placementId: 'testPlacementId' @@ -41,7 +41,7 @@ describe('smarticoBidAdapter', function () { }); }); describe('buildRequests', function () { - const bidRequests = [ bid ]; + const bidRequests = [bid]; const request = spec.buildRequests(bidRequests, bidderRequests); it('sends bid request via POST', function () { expect(request.method).to.equal('POST'); @@ -99,7 +99,8 @@ describe('smarticoBidAdapter', function () { meta: { advertiserDomains: ['www.advertiser.com'], advertiserName: 'Advertiser' - }}]; + } + }]; const result = spec.interpretResponse(serverResponse, bidRequest); it('should contain correct creativeId', function () { expect(result[0].creativeId).to.equal(expectedResponse[0].creativeId) diff --git a/test/spec/modules/smartyadsBidAdapter_spec.js b/test/spec/modules/smartyadsBidAdapter_spec.js index 2a3f1e8443c..67e24e0696d 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.js'; +import { expect } from 'chai'; +import { spec } from '../../../modules/smartyadsBidAdapter.js'; import { config } from '../../../src/config.js'; -import {server} from '../../mocks/xhr.js'; +import { server } from '../../mocks/xhr.js'; describe('SmartyadsAdapter', function () { const bid = { @@ -111,7 +111,7 @@ describe('SmartyadsAdapter', function () { netRevenue: true, currency: 'USD', dealId: '1', - meta: {advertiserDomains: ['example.com']} + meta: { advertiserDomains: ['example.com'] } }] }; const bannerResponses = spec.interpretResponse(banner); diff --git a/test/spec/modules/smartytechBidAdapter_spec.js b/test/spec/modules/smartytechBidAdapter_spec.js index 17e1bb59bed..7340d65efc1 100644 --- a/test/spec/modules/smartytechBidAdapter_spec.js +++ b/test/spec/modules/smartytechBidAdapter_spec.js @@ -1,6 +1,6 @@ -import {expect} from 'chai'; -import {spec, ENDPOINT_PROTOCOL, ENDPOINT_DOMAIN, ENDPOINT_PATH, getAliasUserId, storage} from 'modules/smartytechBidAdapter'; -import {newBidder} from 'src/adapters/bidderFactory.js'; +import { expect } from 'chai'; +import { spec, ENDPOINT_PROTOCOL, ENDPOINT_DOMAIN, ENDPOINT_PATH, getAliasUserId, storage } from 'modules/smartytechBidAdapter'; +import { newBidder } from 'src/adapters/bidderFactory.js'; import * as utils from 'src/utils.js'; import sinon from 'sinon'; @@ -135,13 +135,13 @@ describe('SmartyTechDSPAdapter: isBidRequestValid', function () { }); function mockRandomSizeArray(len) { - return Array.apply(null, {length: len}).map(i => { + return Array.apply(null, { length: len }).map(i => { return [Math.floor(Math.random() * 800), Math.floor(Math.random() * 800)] }); } function mockBidRequestListData(mediaType, size, customSizes) { - return Array.apply(null, {length: size}).map((i, index) => { + return Array.apply(null, { length: size }).map((i, index) => { const id = Math.floor(Math.random() * 800) * (index + 1); let mediaTypes; const params = { @@ -436,7 +436,7 @@ describe('SmartyTechDSPAdapter: buildRequests with user IDs', () => { it('should not include userIds when userIdAsEids is undefined', () => { const bidRequestWithUndefinedUserIds = mockBidRequestListData('banner', 2, []).map(req => { - const {userIdAsEids, ...requestWithoutUserIds} = req; + const { userIdAsEids, ...requestWithoutUserIds } = req; return requestWithoutUserIds; }); const request = spec.buildRequests(bidRequestWithUndefinedUserIds, mockReferer); diff --git a/test/spec/modules/smilewantedBidAdapter_spec.js b/test/spec/modules/smilewantedBidAdapter_spec.js index e1d740ea19e..3d8f55c17a7 100644 --- a/test/spec/modules/smilewantedBidAdapter_spec.js +++ b/test/spec/modules/smilewantedBidAdapter_spec.js @@ -635,14 +635,14 @@ describe('smilewantedBidAdapterTests', function () { }); it('SmileWanted - Verify user sync - empty data', function () { - const syncs = spec.getUserSyncs({iframeEnabled: true}, {}, {}, null); + const syncs = spec.getUserSyncs({ iframeEnabled: true }, {}, {}, null); expect(syncs).to.have.lengthOf(1); expect(syncs[0].type).to.equal('iframe'); expect(syncs[0].url).to.equal('https://csync.smilewanted.com'); }); it('SmileWanted - Verify user sync', function () { - let syncs = spec.getUserSyncs({iframeEnabled: true}, {}, { + let syncs = spec.getUserSyncs({ iframeEnabled: true }, {}, { consentString: 'foo' }, '1NYN'); expect(syncs).to.have.lengthOf(1); diff --git a/test/spec/modules/smootBidAdapter_spec.js b/test/spec/modules/smootBidAdapter_spec.js index d9c5580e60d..7ab8100bbe1 100644 --- a/test/spec/modules/smootBidAdapter_spec.js +++ b/test/spec/modules/smootBidAdapter_spec.js @@ -544,7 +544,7 @@ describe('SmootBidAdapter', function () { consentString: 'ALL', gdprApplies: true, }, - {} + undefined ); expect(syncData).to.be.an('array').which.is.not.empty; expect(syncData[0]).to.be.an('object'); @@ -560,9 +560,7 @@ describe('SmootBidAdapter', function () { {}, {}, {}, - { - consentString: '1---', - } + '1---' ); expect(syncData).to.be.an('array').which.is.not.empty; expect(syncData[0]).to.be.an('object'); @@ -578,7 +576,7 @@ describe('SmootBidAdapter', function () { {}, {}, {}, - {}, + undefined, { gppString: 'abc123', applicableSections: [8], diff --git a/test/spec/modules/snigelBidAdapter_spec.js b/test/spec/modules/snigelBidAdapter_spec.js index faeba529abe..d8572ce2dcf 100644 --- a/test/spec/modules/snigelBidAdapter_spec.js +++ b/test/spec/modules/snigelBidAdapter_spec.js @@ -1,9 +1,9 @@ -import {expect} from 'chai'; -import {spec} from 'modules/snigelBidAdapter.js'; -import {config} from 'src/config.js'; -import {isValid} from 'src/adapters/bidderFactory.js'; -import {registerActivityControl} from 'src/activities/rules.js'; -import {ACTIVITY_ACCESS_DEVICE} from 'src/activities/activities.js'; +import { expect } from 'chai'; +import { spec } from 'modules/snigelBidAdapter.js'; +import { config } from 'src/config.js'; +import { isValid } from 'src/adapters/bidderFactory.js'; +import { registerActivityControl } from 'src/activities/rules.js'; +import { ACTIVITY_ACCESS_DEVICE } from 'src/activities/activities.js'; const BASE_BID_REQUEST = { adUnitCode: 'top_leaderboard', @@ -18,7 +18,7 @@ const BASE_BID_REQUEST = { transactionId: 'trans_test', }; const makeBidRequest = function (overrides) { - return {...BASE_BID_REQUEST, ...overrides}; + return { ...BASE_BID_REQUEST, ...overrides }; }; const BASE_BIDDER_REQUEST = { @@ -30,7 +30,7 @@ const BASE_BIDDER_REQUEST = { }, }; const makeBidderRequest = function (overrides) { - return {...BASE_BIDDER_REQUEST, ...overrides}; + return { ...BASE_BIDDER_REQUEST, ...overrides }; }; const DUMMY_USP_CONSENT = '1YYN'; @@ -45,7 +45,7 @@ describe('snigelBidAdapter', function () { }); it('should return true if placement provided', function () { - const bidRequest = makeBidRequest({params: {placement: 'top_leaderboard'}}); + const bidRequest = makeBidRequest({ params: { placement: 'top_leaderboard' } }); expect(spec.isBidRequestValid(bidRequest)).to.equal(true); }); }); @@ -58,8 +58,8 @@ describe('snigelBidAdapter', function () { it('should build a single request for every impression and its placement', function () { const bidderRequest = Object.assign({}, BASE_BIDDER_REQUEST); const bidRequests = [ - makeBidRequest({bidId: 'a', adUnitCode: 'au_a', params: {placement: 'top_leaderboard'}}), - makeBidRequest({bidId: 'b', adUnitCode: 'au_b', params: {placement: 'bottom_leaderboard'}}), + makeBidRequest({ bidId: 'a', adUnitCode: 'au_a', params: { placement: 'top_leaderboard' } }), + makeBidRequest({ bidId: 'b', adUnitCode: 'au_b', params: { placement: 'bottom_leaderboard' } }), ]; const request = spec.buildRequests(bidRequests, bidderRequest); @@ -135,9 +135,9 @@ describe('snigelBidAdapter', function () { it('should forward refresh information', function () { const bidderRequest = Object.assign({}, BASE_BIDDER_REQUEST); - const topLeaderboard = makeBidRequest({adUnitCode: 'top_leaderboard'}); - const bottomLeaderboard = makeBidRequest({adUnitCode: 'bottom_leaderboard'}); - const sidebar = makeBidRequest({adUnitCode: 'sidebar'}); + const topLeaderboard = makeBidRequest({ adUnitCode: 'top_leaderboard' }); + const bottomLeaderboard = makeBidRequest({ adUnitCode: 'bottom_leaderboard' }); + const sidebar = makeBidRequest({ adUnitCode: 'sidebar' }); // first auction, no refresh let request = spec.buildRequests([topLeaderboard, bottomLeaderboard], bidderRequest); @@ -199,9 +199,9 @@ describe('snigelBidAdapter', function () { it('should increment placement counter for each placement', function () { const bidderRequest = Object.assign({}, BASE_BIDDER_REQUEST); - const topLeaderboard = makeBidRequest({adUnitCode: 'top_leaderboard', params: {placement: 'ros'}}); - const bottomLeaderboard = makeBidRequest({adUnitCode: 'bottom_leaderboard', params: {placement: 'ros'}}); - const sidebar = makeBidRequest({adUnitCode: 'sidebar', params: {placement: 'other'}}); + const topLeaderboard = makeBidRequest({ adUnitCode: 'top_leaderboard', params: { placement: 'ros' } }); + const bottomLeaderboard = makeBidRequest({ adUnitCode: 'bottom_leaderboard', params: { placement: 'ros' } }); + const sidebar = makeBidRequest({ adUnitCode: 'sidebar', params: { placement: 'other' } }); let request = spec.buildRequests([topLeaderboard, bottomLeaderboard, sidebar], bidderRequest); expect(request).to.have.property('data'); @@ -231,11 +231,11 @@ describe('snigelBidAdapter', function () { describe('interpretResponse', function () { it('should not return any bids if the request failed', function () { expect(spec.interpretResponse({}, {})).to.be.empty; - expect(spec.interpretResponse({body: 'Some error message'}, {})).to.be.empty; + expect(spec.interpretResponse({ body: 'Some error message' }, {})).to.be.empty; }); it('should not return any bids if the request did not return any bids either', function () { - expect(spec.interpretResponse({body: {bids: []}})).to.be.empty; + expect(spec.interpretResponse({ body: { bids: [] } })).to.be.empty; }); it('should return valid bids with additional meta information', function () { @@ -259,7 +259,7 @@ describe('snigelBidAdapter', function () { }, }; - const bids = spec.interpretResponse(serverResponse, {bidderRequest: {bids: [BASE_BID_REQUEST]}}); + const bids = spec.interpretResponse(serverResponse, { bidderRequest: { bids: [BASE_BID_REQUEST] } }); expect(bids.length).to.equal(1); const bid = bids[0]; expect(isValid(BASE_BID_REQUEST.adUnitCode, bid)).to.be.true; @@ -353,7 +353,7 @@ describe('snigelBidAdapter', function () { consentString: DUMMY_GDPR_CONSENT_STRING, vendorData: { purpose: { - consents: {1: true}, + consents: { 1: true }, }, }, }; @@ -400,7 +400,7 @@ describe('snigelBidAdapter', function () { it('should omit session ID if no device access', function () { const bidderRequest = makeBidderRequest(); const unregisterRule = registerActivityControl(ACTIVITY_ACCESS_DEVICE, 'denyAccess', () => { - return {allow: false, reason: 'no consent'}; + return { allow: false, reason: 'no consent' }; }); try { @@ -419,10 +419,10 @@ describe('snigelBidAdapter', function () { gdprApplies: true, vendorData: { purpose: { - consents: {1: true, 2: true, 3: true, 4: true, 5: true}, + consents: { 1: true, 2: true, 3: true, 4: true, 5: true }, }, vendor: { - consents: {[spec.gvlid]: true}, + consents: { [spec.gvlid]: true }, }, }, }, @@ -432,7 +432,7 @@ describe('snigelBidAdapter', function () { let data = JSON.parse(request.data); expect(data.gdprConsent).to.be.true; - let bidderRequest = {...baseBidderRequest, ...{gdprConsent: {vendorData: {purpose: {consents: {1: false}}}}}}; + let bidderRequest = { ...baseBidderRequest, ...{ gdprConsent: { vendorData: { purpose: { consents: { 1: false } } } } } }; request = spec.buildRequests([], bidderRequest); expect(request).to.have.property('data'); data = JSON.parse(request.data); @@ -440,7 +440,7 @@ describe('snigelBidAdapter', function () { bidderRequest = { ...baseBidderRequest, - ...{gdprConsent: {vendorData: {vendor: {consents: {[spec.gvlid]: false}}}}}, + ...{ gdprConsent: { vendorData: { vendor: { consents: { [spec.gvlid]: false } } } } }, }; request = spec.buildRequests([], bidderRequest); expect(request).to.have.property('data'); diff --git a/test/spec/modules/sonaradsBidAdapter_spec.js b/test/spec/modules/sonaradsBidAdapter_spec.js index e0afd0c7d23..140fa8c95d0 100644 --- a/test/spec/modules/sonaradsBidAdapter_spec.js +++ b/test/spec/modules/sonaradsBidAdapter_spec.js @@ -171,7 +171,7 @@ describe('bridgeuppBidAdapter_spec', function () { } } }; - const ortbRequest = spec.buildRequests(bidRequests, await addFPDToBidderRequest({...bidderRequest, ortb2})).data; + const ortbRequest = spec.buildRequests(bidRequests, await addFPDToBidderRequest({ ...bidderRequest, ortb2 })).data; expect(ortbRequest.site.domain).to.equal(SITE_DOMAIN_NAME); expect(ortbRequest.site.publisher.domain).to.equal('sonarads.com'); expect(ortbRequest.site.page).to.equal(SITE_PAGE); @@ -220,7 +220,7 @@ describe('bridgeuppBidAdapter_spec', function () { } } }; - const ortbRequest = spec.buildRequests(bidRequests, await addFPDToBidderRequest({...bidderRequest, ortb2})).data; + const ortbRequest = spec.buildRequests(bidRequests, await addFPDToBidderRequest({ ...bidderRequest, ortb2 })).data; expect(ortbRequest.device.dnt).to.equal(1); expect(ortbRequest.device.lmt).to.equal(0); expect(ortbRequest.device.js).to.equal(0); @@ -238,7 +238,7 @@ describe('bridgeuppBidAdapter_spec', function () { }); it('should properly build a request with source object', async function () { - const expectedSchain = {id: 'prebid'}; + const expectedSchain = { id: 'prebid' }; const ortb2 = { source: { pchain: 'sonarads', @@ -262,7 +262,7 @@ describe('bridgeuppBidAdapter_spec', function () { }, }, ]; - const ortbRequest = spec.buildRequests(bidRequests, await addFPDToBidderRequest({...bidderRequest, ortb2})).data; + const ortbRequest = spec.buildRequests(bidRequests, await addFPDToBidderRequest({ ...bidderRequest, ortb2 })).data; expect(ortbRequest.source.ext.schain).to.deep.equal(expectedSchain); expect(ortbRequest.source.pchain).to.equal('sonarads'); }); @@ -351,7 +351,7 @@ describe('bridgeuppBidAdapter_spec', function () { } }; - const ortbRequest = spec.buildRequests(bidRequests, await addFPDToBidderRequest({...bidderRequest, ortb2})).data; + const ortbRequest = spec.buildRequests(bidRequests, await addFPDToBidderRequest({ ...bidderRequest, ortb2 })).data; expect(ortbRequest.regs.coppa).to.equal(1); expect(ortbRequest.regs.gpp).to.equal('consent_string'); expect(ortbRequest.regs.gpp_sid).to.deep.equal([0, 1, 2]); @@ -565,7 +565,7 @@ describe('bridgeuppBidAdapter_spec', function () { it('should properly build a request when coppa is true', async function () { const bidRequests = []; const bidderRequest = {}; - config.setConfig({coppa: true}); + config.setConfig({ coppa: true }); const ortbRequest = spec.buildRequests(bidRequests, await addFPDToBidderRequest(bidderRequest)).data; expect(ortbRequest.regs.coppa).to.equal(1); @@ -574,7 +574,7 @@ describe('bridgeuppBidAdapter_spec', function () { it('should properly build a request when coppa is false', async function () { const bidRequests = []; const bidderRequest = {}; - config.setConfig({coppa: false}); + config.setConfig({ coppa: false }); const buildRequests = spec.buildRequests(bidRequests, await addFPDToBidderRequest(bidderRequest)); const ortbRequest = buildRequests.data; expect(ortbRequest.regs.coppa).to.equal(0); @@ -623,7 +623,7 @@ describe('bridgeuppBidAdapter_spec', function () { siteId: 'site-id-12' }, getFloor: () => { - return {currency: 'USD', floor: 1.23, size: '*', mediaType: '*'}; + return { currency: 'USD', floor: 1.23, size: '*', mediaType: '*' }; } } ]; @@ -721,7 +721,7 @@ describe('bridgeuppBidAdapter_spec', function () { return undefined } }, - body: {seatbid: []} + body: { seatbid: [] } }; const interpretedBids = spec.interpretResponse(serverResponse, request); diff --git a/test/spec/modules/sonobiBidAdapter_spec.js b/test/spec/modules/sonobiBidAdapter_spec.js index f7b1d858d50..e9e098b4f44 100644 --- a/test/spec/modules/sonobiBidAdapter_spec.js +++ b/test/spec/modules/sonobiBidAdapter_spec.js @@ -5,7 +5,7 @@ import { userSync } from '../../../src/userSync.js'; import { config } from 'src/config.js'; import * as gptUtils from '../../../libraries/gptUtils/gptUtils.js'; import { parseQS } from '../../../src/utils.js' -import {getGlobal} from '../../../src/prebidGlobal.js'; +import { getGlobal } from '../../../src/prebidGlobal.js'; describe('SonobiBidAdapter', function () { const adapter = newBidder(spec) const originalBuildRequests = spec.buildRequests; diff --git a/test/spec/modules/sovrnBidAdapter_spec.js b/test/spec/modules/sovrnBidAdapter_spec.js index c442b1d53db..41a264f45e7 100644 --- a/test/spec/modules/sovrnBidAdapter_spec.js +++ b/test/spec/modules/sovrnBidAdapter_spec.js @@ -1,6 +1,6 @@ -import {expect} from 'chai' -import {spec} from 'modules/sovrnBidAdapter.js' -import {config} from 'src/config.js' +import { expect } from 'chai' +import { spec } from 'modules/sovrnBidAdapter.js' +import { config } from 'src/config.js' import * as utils from 'src/utils.js' const ENDPOINT = `https://ap.lijit.com/rtb/bid?src=$$REPO_AND_VERSION$$` @@ -113,7 +113,7 @@ describe('sovrnBidAdapter', function() { const payload = JSON.parse(request.data) const impression = payload.imp[0] - expect(impression.banner.format).to.deep.equal([{w: 300, h: 250}, {w: 300, h: 600}]) + expect(impression.banner.format).to.deep.equal([{ w: 300, h: 250 }, { w: 300, h: 600 }]) expect(impression.banner.w).to.equal(1) expect(impression.banner.h).to.equal(1) }) @@ -265,7 +265,7 @@ describe('sovrnBidAdapter', function() { const payload = JSON.parse(request.data) const impression = payload.imp[0] - expect(impression.banner.format).to.deep.equal([{w: 300, h: 250}]) + expect(impression.banner.format).to.deep.equal([{ w: 300, h: 250 }]) expect(impression.banner.w).to.equal(1) expect(impression.banner.h).to.equal(1) }) @@ -331,7 +331,7 @@ describe('sovrnBidAdapter', function() { gdprApplies: true }, } - const {regs} = JSON.parse(spec.buildRequests([baseBidRequest], bidderRequest).data) + const { regs } = JSON.parse(spec.buildRequests([baseBidRequest], bidderRequest).data) expect(regs.coppa).to.be.undefined }) @@ -349,7 +349,7 @@ describe('sovrnBidAdapter', function() { timeout: 3000, bids: [baseBidRequest] } - const {regs} = JSON.parse(spec.buildRequests([baseBidRequest], bidderRequest).data) + const { regs } = JSON.parse(spec.buildRequests([baseBidRequest], bidderRequest).data) expect(regs.coppa).to.equal(1) }) @@ -366,7 +366,7 @@ describe('sovrnBidAdapter', function() { gdprApplies: true }, } - const {bcat} = JSON.parse(spec.buildRequests([baseBidRequest], bidderRequest).data) + const { bcat } = JSON.parse(spec.buildRequests([baseBidRequest], bidderRequest).data) expect(bcat).to.be.undefined }) @@ -382,7 +382,7 @@ describe('sovrnBidAdapter', function() { timeout: 3000, bids: [baseBidRequest] } - const {bcat} = JSON.parse(spec.buildRequests([baseBidRequest], bidderRequest).data) + const { bcat } = JSON.parse(spec.buildRequests([baseBidRequest], bidderRequest).data) expect(bcat).to.exist.and.to.be.a('array') expect(bcat).to.deep.equal(['IAB1-1', 'IAB1-2']) }) @@ -570,7 +570,7 @@ describe('sovrnBidAdapter', function() { it('should use the floor provided from the floor module if present', function() { const floorBid = { ...baseBidRequest, - getFloor: () => ({currency: 'USD', floor: 1.10}), + getFloor: () => ({ currency: 'USD', floor: 1.10 }), params: { tagid: 1234, bidfloor: 2.00 @@ -610,7 +610,7 @@ describe('sovrnBidAdapter', function() { it('floor should be undefined if there is incorrect floor value from the floor module', function() { const floorBid = { ...baseBidRequest, - getFloor: () => ({currency: 'USD', floor: 'incorrect_value'}), + getFloor: () => ({ currency: 'USD', floor: 'incorrect_value' }), params: { tagid: 1234 } @@ -645,7 +645,7 @@ describe('sovrnBidAdapter', function() { } }; - const request = spec.buildRequests([baseBidRequest], {...baseBidderRequest, ortb2}) + const request = spec.buildRequests([baseBidRequest], { ...baseBidderRequest, ortb2 }) const { user, site } = JSON.parse(request.data) expect(user.data).to.equal('some user data') @@ -1199,7 +1199,7 @@ describe('sovrnBidAdapter', function() { const payload = JSON.parse(request.data) it('gets sizes from mediaTypes.banner', function() { - expect(payload.imp[0].banner.format).to.deep.equal([{w: 300, h: 250}, {w: 300, h: 600}]) + 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) }) diff --git a/test/spec/modules/ssmasBidAdapter_spec.js b/test/spec/modules/ssmasBidAdapter_spec.js index a97a40caeac..70ca4c4b478 100644 --- a/test/spec/modules/ssmasBidAdapter_spec.js +++ b/test/spec/modules/ssmasBidAdapter_spec.js @@ -1,6 +1,6 @@ import { expect } from 'chai'; import { spec, SSMAS_CODE, SSMAS_ENDPOINT, SSMAS_REQUEST_METHOD } from 'modules/ssmasBidAdapter.js'; -import {newBidder} from 'src/adapters/bidderFactory.js'; +import { newBidder } from 'src/adapters/bidderFactory.js'; import * as utils from 'src/utils.js'; describe('ssmasBidAdapter', function () { @@ -68,7 +68,7 @@ describe('ssmasBidAdapter', function () { it('test bad bid request', function () { // empty bid - expect(spec.isBidRequestValid({bidId: '', params: {}})).to.be.false; + expect(spec.isBidRequestValid({ bidId: '', params: {} })).to.be.false; // empty bidId bid.bidId = ''; diff --git a/test/spec/modules/sspBCBidAdapter_spec.js b/test/spec/modules/sspBCBidAdapter_spec.js index 53261a3a734..4a893aaedb9 100644 --- a/test/spec/modules/sspBCBidAdapter_spec.js +++ b/test/spec/modules/sspBCBidAdapter_spec.js @@ -690,14 +690,14 @@ describe('SSPBC adapter', function () { expect(result.length).to.equal(bids.length); expect(resultSingle.length).to.equal(1); - expect(resultSingle[0]).to.have.keys('ad', 'cpm', 'width', 'height', 'mediaType', 'meta', 'requestId', 'creativeId', 'currency', 'netRevenue', 'ttl', 'vurls'); + expect(resultSingle[0]).to.have.keys('ad', 'cpm', 'width', 'height', 'mediaType', 'meta', 'requestId', 'creativeId', 'currency', 'netRevenue', 'ttl', 'vurls', 'eventtrackers'); }); it('should create bid from OneCode (parameter-less) request, if response contains siteId', function () { const resultOneCode = spec.interpretResponse(serverResponseOneCode, requestOneCode); expect(resultOneCode.length).to.equal(1); - expect(resultOneCode[0]).to.have.keys('ad', 'cpm', 'width', 'height', 'mediaType', 'meta', 'requestId', 'creativeId', 'currency', 'netRevenue', 'ttl', 'vurls'); + expect(resultOneCode[0]).to.have.keys('ad', 'cpm', 'width', 'height', 'mediaType', 'meta', 'requestId', 'creativeId', 'currency', 'netRevenue', 'ttl', 'vurls', 'eventtrackers'); }); it('should not create bid from OneCode (parameter-less) request, if response does not contain siteId', function () { @@ -724,7 +724,7 @@ describe('SSPBC adapter', function () { expect(resultVideo.length).to.equal(1); const videoBid = resultVideo[0]; - expect(videoBid).to.have.keys('adType', 'cpm', 'creativeId', 'currency', 'width', 'height', 'meta', 'mediaType', 'netRevenue', 'requestId', 'ttl', 'vastContent', 'vastXml', 'vastUrl', 'vurls'); + expect(videoBid).to.have.keys('adType', 'cpm', 'creativeId', 'currency', 'width', 'height', 'meta', 'mediaType', 'netRevenue', 'requestId', 'ttl', 'vastContent', 'vastXml', 'vastUrl', 'vurls', 'eventtrackers'); expect(videoBid.adType).to.equal('instream'); expect(videoBid.mediaType).to.equal('video'); expect(videoBid.vastXml).to.match(/^<\?xml.*<\/VAST>$/); @@ -738,7 +738,7 @@ describe('SSPBC adapter', function () { expect(resultNative.length).to.equal(1); const nativeBid = resultNative[0]; - expect(nativeBid).to.have.keys('cpm', 'creativeId', 'currency', 'width', 'height', 'meta', 'mediaType', 'netRevenue', 'requestId', 'ttl', 'native', 'vurls'); + expect(nativeBid).to.have.keys('cpm', 'creativeId', 'currency', 'width', 'height', 'meta', 'mediaType', 'netRevenue', 'requestId', 'ttl', 'native', 'vurls', 'eventtrackers'); expect(nativeBid.native).to.have.keys('image', 'icon', 'title', 'sponsoredBy', 'body', 'clickUrl', 'impressionTrackers', 'javascriptTrackers', 'clickTrackers'); }); @@ -747,13 +747,6 @@ describe('SSPBC adapter', function () { expect(resultIncorrect.length).to.equal(0); }); - - it('should response with fledge auction configs', function () { - const { bids, fledgeAuctionConfigs } = spec.interpretResponse(serverResponsePaapi, requestSingle); - - expect(bids.length).to.equal(1); - expect(fledgeAuctionConfigs.length).to.equal(1); - }); }); describe('getUserSyncs', function () { diff --git a/test/spec/modules/ssp_genieeBidAdapter_spec.js b/test/spec/modules/ssp_genieeBidAdapter_spec.js index ab7e99ab5e5..a207b21aa73 100644 --- a/test/spec/modules/ssp_genieeBidAdapter_spec.js +++ b/test/spec/modules/ssp_genieeBidAdapter_spec.js @@ -364,20 +364,20 @@ describe('ssp_genieeBidAdapter', function () { it('should include only imuid in extuid query when only imuid exists', function () { const imuid = 'b.a4ad1d3eeb51e600'; - const request = spec.buildRequests([{...BANNER_BID, userId: {imuid}}]); + const request = spec.buildRequests([{ ...BANNER_BID, userId: { imuid } }]); expect(request[0].data.extuid).to.deep.equal(`im:${imuid}`); }); it('should include only id5id in extuid query when only id5id exists', function () { const id5id = 'id5id'; - const request = spec.buildRequests([{...BANNER_BID, userId: {id5id: {uid: id5id}}}]); + const request = spec.buildRequests([{ ...BANNER_BID, userId: { id5id: { uid: id5id } } }]); expect(request[0].data.extuid).to.deep.equal(`id5:${id5id}`); }); it('should include id5id and imuid in extuid query when id5id and imuid exists', function () { const imuid = 'b.a4ad1d3eeb51e600'; const id5id = 'id5id'; - const request = spec.buildRequests([{...BANNER_BID, userId: {id5id: {uid: id5id}, imuid: imuid}}]); + const request = spec.buildRequests([{ ...BANNER_BID, userId: { id5id: { uid: id5id }, imuid: imuid } }]); expect(request[0].data.extuid).to.deep.equal(`id5:${id5id}\tim:${imuid}`); }); diff --git a/test/spec/modules/stackadaptBidAdapter_spec.js b/test/spec/modules/stackadaptBidAdapter_spec.js index 00c799b52cc..1c761590011 100644 --- a/test/spec/modules/stackadaptBidAdapter_spec.js +++ b/test/spec/modules/stackadaptBidAdapter_spec.js @@ -242,7 +242,7 @@ describe('stackadaptBidAdapter', function () { bids: [bidderRequest] }) - const result = spec.interpretResponse(ortbResponse, {data: ortbRequest.data}); + const result = spec.interpretResponse(ortbResponse, { data: ortbRequest.data }); expect(result.length).to.equal(1); expect(result[0]).to.deep.equal(expectedBid); }); @@ -398,7 +398,7 @@ describe('stackadaptBidAdapter', function () { const ortbRequest = spec.buildRequests([bidderRequest1, bidderRequest2], { bids: [bidderRequest1, bidderRequest2] }) - const result = spec.interpretResponse(ortbResponse, {data: ortbRequest.data}); + const result = spec.interpretResponse(ortbResponse, { data: ortbRequest.data }); expect(result.length).to.equal(2); expect(result).to.deep.equal(expectedBids); }); @@ -472,7 +472,7 @@ describe('stackadaptBidAdapter', function () { bids: [bidderRequest] }) - const result = spec.interpretResponse(ortbResponse, {data: ortbRequest.data}); + const result = spec.interpretResponse(ortbResponse, { data: ortbRequest.data }); expect(result.length).to.equal(1); expect(result[0]).to.deep.equal(expectedBid); }); @@ -794,8 +794,8 @@ describe('stackadaptBidAdapter', function () { } } - const ortbRequest = spec.buildRequests(bidRequests, {...bidderRequestWithoutRefererDomain, ortb2}).data; - expect(ortbRequest.site.publisher).to.deep.equal({domain: 'https://publisher.com', id: '11111'}); + const ortbRequest = spec.buildRequests(bidRequests, { ...bidderRequestWithoutRefererDomain, ortb2 }).data; + expect(ortbRequest.site.publisher).to.deep.equal({ domain: 'https://publisher.com', id: '11111' }); }); it('should set first party side data publisher domain taking precedence over referer domain', function () { @@ -804,7 +804,7 @@ describe('stackadaptBidAdapter', function () { domain: 'https://publisher.com', } }; - const ortbRequest = spec.buildRequests(bidRequests, {...bidderRequest, ortb2}).data; + const ortbRequest = spec.buildRequests(bidRequests, { ...bidderRequest, ortb2 }).data; expect(ortbRequest.site.domain).to.equal('https://publisher.com'); }); @@ -812,7 +812,7 @@ describe('stackadaptBidAdapter', function () { const ortb2 = { bcat: ['IAB1', 'IAB2'] }; - const ortbRequest = spec.buildRequests(bidRequests, {...bidderRequest, ortb2}).data; + const ortbRequest = spec.buildRequests(bidRequests, { ...bidderRequest, ortb2 }).data; expect(ortbRequest.bcat).to.deep.equal(['IAB1', 'IAB2']); }); @@ -820,7 +820,7 @@ describe('stackadaptBidAdapter', function () { const ortb2 = { badv: ['chargers.com', 'house.com'] }; - const ortbRequest = spec.buildRequests(bidRequests, {...bidderRequest, ortb2}).data; + const ortbRequest = spec.buildRequests(bidRequests, { ...bidderRequest, ortb2 }).data; expect(ortbRequest.badv).to.deep.equal(['chargers.com', 'house.com']); }); @@ -853,7 +853,7 @@ describe('stackadaptBidAdapter', function () { } } }; - const clonedBidderRequest = {...deepClone(bidderRequest), ortb2}; + const clonedBidderRequest = { ...deepClone(bidderRequest), ortb2 }; const ortbRequest = spec.buildRequests(bidRequests, clonedBidderRequest).data; expect(ortbRequest.user.ext.consent).to.equal(consentString); expect(ortbRequest.regs.ext.gdpr).to.equal(1); @@ -868,7 +868,7 @@ describe('stackadaptBidAdapter', function () { } } }; - const clonedBidderRequest = {...deepClone(bidderRequest), ortb2}; + const clonedBidderRequest = { ...deepClone(bidderRequest), ortb2 }; const ortbRequest = spec.buildRequests(bidRequests, clonedBidderRequest).data; expect(ortbRequest.regs.ext.us_privacy).to.equal(consentString); }); @@ -879,7 +879,7 @@ describe('stackadaptBidAdapter', function () { coppa: 1 } }; - const clonedBidderRequest = {...deepClone(bidderRequest), ortb2}; + const clonedBidderRequest = { ...deepClone(bidderRequest), ortb2 }; const ortbRequest = spec.buildRequests(bidRequests, clonedBidderRequest).data; expect(ortbRequest.regs.coppa).to.equal(1); }); @@ -891,7 +891,7 @@ describe('stackadaptBidAdapter', function () { gpp_sid: [9] } }; - const clonedBidderRequest = {...deepClone(bidderRequest), ortb2}; + const clonedBidderRequest = { ...deepClone(bidderRequest), ortb2 }; const ortbRequest = spec.buildRequests(bidRequests, clonedBidderRequest).data; expect(ortbRequest.regs.gpp).to.equal('DCACTA~1YAA'); expect(ortbRequest.regs.gpp_sid).to.eql([9]); @@ -916,7 +916,7 @@ describe('stackadaptBidAdapter', function () { clonedBidRequests[0].ortb2 = { source: { - ext: {schain: schain} + ext: { schain: schain } } }; clonedBidderRequest.bids = clonedBidRequests; @@ -924,7 +924,7 @@ describe('stackadaptBidAdapter', function () { // Add schain to bidderRequest as well clonedBidderRequest.ortb2 = { source: { - ext: {schain: schain} + ext: { schain: schain } } }; @@ -946,7 +946,7 @@ describe('stackadaptBidAdapter', function () { keywords: 'device={}' } }; - const mergedBidderRequest = {...bidderRequest, ortb2}; + const mergedBidderRequest = { ...bidderRequest, ortb2 }; const ortbRequest = spec.buildRequests(bidRequests, mergedBidderRequest).data; expect(ortbRequest.site.id).to.equal('144da00b-8309-4b2e-9482-4b3829c0b54a'); expect(ortbRequest.site.name).to.equal('game'); @@ -1017,7 +1017,7 @@ describe('stackadaptBidAdapter', function () { } }; - const bidderRequestMerged = {...bidderRequest, ortb2}; + const bidderRequestMerged = { ...bidderRequest, ortb2 }; const ortbRequest = spec.buildRequests(bidRequests, bidderRequestMerged).data; validateExtFirstPartyData(ortbRequest.site.ext) @@ -1032,7 +1032,7 @@ describe('stackadaptBidAdapter', function () { } }; - const bidderRequestMerged = {...bidderRequest, ortb2}; + const bidderRequestMerged = { ...bidderRequest, ortb2 }; const ortbRequest = spec.buildRequests(bidRequests, bidderRequestMerged).data; validateExtFirstPartyData(ortbRequest.user.ext) @@ -1066,7 +1066,7 @@ describe('stackadaptBidAdapter', function () { } }; - const bidderRequestMerged = {...bidderRequest, ortb2}; + const bidderRequestMerged = { ...bidderRequest, ortb2 }; const ortbRequest = spec.buildRequests(bidRequests, bidderRequestMerged).data; validateExtFirstPartyData(ortbRequest.app.ext) @@ -1081,7 +1081,7 @@ describe('stackadaptBidAdapter', function () { } }; - const bidderRequestMerged = {...bidderRequest, ortb2}; + const bidderRequestMerged = { ...bidderRequest, ortb2 }; const ortbRequest = spec.buildRequests(bidRequests, bidderRequestMerged).data; validateExtFirstPartyData(ortbRequest.device.ext) @@ -1096,7 +1096,7 @@ describe('stackadaptBidAdapter', function () { } }; - const bidderRequestMerged = {...bidderRequest, ortb2}; + const bidderRequestMerged = { ...bidderRequest, ortb2 }; const ortbRequest = spec.buildRequests(bidRequests, bidderRequestMerged).data; validateExtFirstPartyData(ortbRequest.pmp.ext) diff --git a/test/spec/modules/startioBidAdapter_spec.js b/test/spec/modules/startioBidAdapter_spec.js index 021c11e80dd..2b7269997aa 100644 --- a/test/spec/modules/startioBidAdapter_spec.js +++ b/test/spec/modules/startioBidAdapter_spec.js @@ -1,7 +1,7 @@ import { expect } from 'chai'; import { spec } from 'modules/startioBidAdapter.js'; import { BANNER, VIDEO, NATIVE } from 'src/mediaTypes.js'; -import {deepClone} from '../../../src/utils.js'; +import { deepClone } from '../../../src/utils.js'; const DEFAULT_REQUEST_DATA = { adUnitCode: 'test-div', @@ -250,11 +250,11 @@ describe('Prebid Adapter: Startio', function () { it('should provide coppa', () => { let bidderRequest = deepClone(DEFAULT_BIDDER_REQUEST); - bidderRequest.ortb2 = {regs: {coppa: 0}}; + bidderRequest.ortb2 = { regs: { coppa: 0 } }; let request = spec.buildRequests([DEFAULT_REQUEST_DATA], bidderRequest)[0].data; expect(request.regs.coppa).to.equal(0); - bidderRequest.ortb2 = {regs: {coppa: 1}}; + bidderRequest.ortb2 = { regs: { coppa: 1 } }; request = spec.buildRequests([DEFAULT_REQUEST_DATA], bidderRequest)[0].data; expect(request.regs.coppa).to.equal(1); }); diff --git a/test/spec/modules/stnBidAdapter_spec.js b/test/spec/modules/stnBidAdapter_spec.js index 021005a90d6..d4634066bf7 100644 --- a/test/spec/modules/stnBidAdapter_spec.js +++ b/test/spec/modules/stnBidAdapter_spec.js @@ -2,9 +2,9 @@ import { expect } from 'chai'; import { spec } from 'modules/stnBidAdapter.js'; import { newBidder } from 'src/adapters/bidderFactory.js'; import { config } from 'src/config.js'; -import {BANNER, NATIVE, VIDEO} from '../../../src/mediaTypes.js'; +import { BANNER, NATIVE, VIDEO } from '../../../src/mediaTypes.js'; import * as utils from 'src/utils.js'; -import {decorateAdUnitsWithNativeParams} from '../../../src/native.js'; +import { decorateAdUnitsWithNativeParams } from '../../../src/native.js'; const ENDPOINT = 'https://hb.stngo.com/hb-multi'; const TEST_ENDPOINT = 'https://hb.stngo.com/hb-multi-test'; @@ -95,7 +95,7 @@ describe('stnAdapter', function () { 'mediaTypes': { 'banner': { 'sizes': [ - [ 300, 250 ] + [300, 250] ] }, 'video': { @@ -325,7 +325,7 @@ describe('stnAdapter', function () { }); it('should have us_privacy param if usPrivacy is available in the bidRequest', function () { - const bidderRequestWithUSP = Object.assign({uspConsent: '1YNN'}, bidderRequest); + const bidderRequestWithUSP = Object.assign({ uspConsent: '1YNN' }, bidderRequest); const request = spec.buildRequests(bidRequests, bidderRequestWithUSP); expect(request.data.params).to.be.an('object'); expect(request.data.params).to.have.property('us_privacy', '1YNN'); @@ -338,7 +338,7 @@ describe('stnAdapter', function () { }); it('should not send the gdpr param if gdprApplies is false in the bidRequest', function () { - const bidderRequestWithGDPR = Object.assign({gdprConsent: {gdprApplies: false}}, bidderRequest); + const bidderRequestWithGDPR = Object.assign({ gdprConsent: { gdprApplies: false } }, bidderRequest); const request = spec.buildRequests(bidRequests, bidderRequestWithGDPR); expect(request.data.params).to.be.an('object'); expect(request.data.params).to.not.have.property('gdpr'); @@ -346,7 +346,7 @@ describe('stnAdapter', function () { }); it('should send the gdpr param if gdprApplies is true in the bidRequest', function () { - const bidderRequestWithGDPR = Object.assign({gdprConsent: {gdprApplies: true, consentString: 'test-consent-string'}}, bidderRequest); + const bidderRequestWithGDPR = Object.assign({ gdprConsent: { gdprApplies: true, consentString: 'test-consent-string' } }, bidderRequest); const request = spec.buildRequests(bidRequests, bidderRequestWithGDPR); expect(request.data.params).to.be.an('object'); expect(request.data.params).to.have.property('gdpr', true); @@ -354,7 +354,7 @@ describe('stnAdapter', function () { }); it('should not send the gpp param if gppConsent is false in the bidRequest', function () { - const bidderRequestWithGPP = Object.assign({gppConsent: false}, bidderRequest); + const bidderRequestWithGPP = Object.assign({ gppConsent: false }, bidderRequest); const request = spec.buildRequests(bidRequests, bidderRequestWithGPP); expect(request.data.params).to.be.an('object'); expect(request.data.params).to.not.have.property('gpp'); @@ -362,7 +362,7 @@ describe('stnAdapter', function () { }); it('should send the gpp param if gppConsent is true in the bidRequest', function () { - const bidderRequestWithGPP = Object.assign({gppConsent: {gppString: 'test-consent-string', applicableSections: [7]}}, bidderRequest); + const bidderRequestWithGPP = Object.assign({ gppConsent: { gppString: 'test-consent-string', applicableSections: [7] } }, bidderRequest); const request = spec.buildRequests(bidRequests, bidderRequestWithGPP); expect(request.data.params).to.be.an('object'); expect(request.data.params).to.have.property('gpp', 'test-consent-string'); @@ -423,15 +423,15 @@ describe('stnAdapter', function () { 'browsers': [ { 'brand': 'Chromium', - 'version': [ '106', '0', '5249', '119' ] + 'version': ['106', '0', '5249', '119'] }, { 'brand': 'Google Chrome', - 'version': [ '106', '0', '5249', '119' ] + 'version': ['106', '0', '5249', '119'] }, { 'brand': 'Not;A=Brand', - 'version': [ '99', '0', '0', '0' ] + 'version': ['99', '0', '0', '0'] } ], 'mobile': 0, @@ -445,20 +445,20 @@ describe('stnAdapter', function () { 'sua': { 'platform': { 'brand': 'macOS', - 'version': [ '12', '4', '0' ] + 'version': ['12', '4', '0'] }, 'browsers': [ { 'brand': 'Chromium', - 'version': [ '106', '0', '5249', '119' ] + 'version': ['106', '0', '5249', '119'] }, { 'brand': 'Google Chrome', - 'version': [ '106', '0', '5249', '119' ] + 'version': ['106', '0', '5249', '119'] }, { 'brand': 'Not;A=Brand', - 'version': [ '99', '0', '0', '0' ] + 'version': ['99', '0', '0', '0'] } ], 'mobile': 0, diff --git a/test/spec/modules/storageControl_spec.js b/test/spec/modules/storageControl_spec.js index a3fb571256b..ac1d86badc0 100644 --- a/test/spec/modules/storageControl_spec.js +++ b/test/spec/modules/storageControl_spec.js @@ -1,18 +1,23 @@ -import {metadataRepository} from '../../../libraries/metadata/metadata.js'; +import { metadataRepository } from '../../../libraries/metadata/metadata.js'; import { checkDisclosure, dynamicDisclosureCollector, ENFORCE_ALIAS, ENFORCE_OFF, ENFORCE_STRICT, getDisclosures, - storageControlRule + storageControlRule, + deactivate } from '../../../modules/storageControl.js'; import { ACTIVITY_PARAM_COMPONENT_NAME, ACTIVITY_PARAM_COMPONENT_TYPE, ACTIVITY_PARAM_STORAGE_KEY, ACTIVITY_PARAM_STORAGE_TYPE } from '../../../src/activities/params.js'; -import {MODULE_TYPE_BIDDER} from '../../../src/activities/modules.js'; -import {STORAGE_TYPE_COOKIES} from '../../../src/storageManager.js'; +import { MODULE_TYPE_BIDDER } from '../../../src/activities/modules.js'; +import { STORAGE_TYPE_COOKIES } from '../../../src/storageManager.js'; + +// since the module is on by default, importing it here turn it on for other tests +// that happen to run together with this suite - turn it off +deactivate(); describe('storageControl', () => { describe('getDisclosures', () => { @@ -186,31 +191,37 @@ describe('storageControl', () => { it('should allow when disclosed is null', () => { enforcement = ENFORCE_STRICT; - checkResult = {disclosed: null}; + checkResult = { disclosed: null }; expect(rule()).to.not.exist; }); it('should allow when there is no disclosure, but enforcement is off', () => { enforcement = ENFORCE_OFF; - checkResult = {disclosed: false, parent: false}; + checkResult = { disclosed: false, parent: false }; expect(rule()).to.not.exist; }); it('should allow when disclosed is true', () => { enforcement = ENFORCE_STRICT; - checkResult = {disclosed: true}; + checkResult = { disclosed: true }; expect(rule()).to.not.exist; }); it('should deny when enforcement is strict and disclosure is done by the aliased module', () => { enforcement = ENFORCE_STRICT; - checkResult = {disclosed: false, parent: true, reason: 'denied'}; - expect(rule()).to.eql({allow: false, reason: 'denied'}); + checkResult = { disclosed: false, parent: true, reason: 'denied' }; + expect(rule()).to.eql({ allow: false, reason: 'denied' }); + }); + + it('should deny by default when enforcement is not set', () => { + enforcement = undefined; + checkResult = { disclosed: false, parent: false, reason: 'denied' }; + expect(rule()).to.eql({ allow: false, reason: 'denied' }); }); it('should allow when enforcement is allowAliases and disclosure is done by the aliased module', () => { enforcement = ENFORCE_ALIAS; - checkResult = {disclosed: false, parent: true, reason: 'allowed'}; + checkResult = { disclosed: false, parent: true, reason: 'allowed' }; expect(rule()).to.not.exist; }); }); @@ -219,10 +230,10 @@ describe('storageControl', () => { let next, hook, getDisclosures; beforeEach(() => { next = sinon.stub(); - ({hook, getDisclosures} = dynamicDisclosureCollector()); + ({ hook, getDisclosures } = dynamicDisclosureCollector()); }); it('should collect and return disclosures', () => { - const disclosure = {identifier: 'mock', type: 'web', purposes: [1]}; + const disclosure = { identifier: 'mock', type: 'web', purposes: [1] }; hook(next, 'module', disclosure); sinon.assert.calledWith(next, 'module', disclosure); expect(getDisclosures()).to.eql([ @@ -233,8 +244,8 @@ describe('storageControl', () => { ]); }); it('should update disclosures for the same identifier', () => { - hook(next, 'module1', {identifier: 'mock', type: 'cookie', maxAgeSeconds: 10, cookieRefresh: true, purposes: [1]}); - hook(next, 'module2', {identifier: 'mock', type: 'cookie', maxAgeSeconds: 1, cookieRefresh: true, purposes: [2]}); + hook(next, 'module1', { identifier: 'mock', type: 'cookie', maxAgeSeconds: 10, cookieRefresh: true, purposes: [1] }); + hook(next, 'module2', { identifier: 'mock', type: 'cookie', maxAgeSeconds: 1, cookieRefresh: true, purposes: [2] }); expect(getDisclosures()).to.eql([{ disclosedBy: ['module1', 'module2'], identifier: 'mock', @@ -256,8 +267,8 @@ describe('storageControl', () => { }]) }) it('should treat web and cookie disclosures as separate', () => { - hook(next, 'module1', {identifier: 'mock', type: 'cookie', purposes: [1]}); - hook(next, 'module2', {identifier: 'mock', type: 'web', purposes: [2]}); + hook(next, 'module1', { identifier: 'mock', type: 'cookie', purposes: [1] }); + hook(next, 'module2', { identifier: 'mock', type: 'web', purposes: [2] }); expect(getDisclosures()).to.have.deep.members([ { disclosedBy: ['module1'], diff --git a/test/spec/modules/stroeerCoreBidAdapter_spec.js b/test/spec/modules/stroeerCoreBidAdapter_spec.js index 5458a33ec79..defefedb6c9 100644 --- a/test/spec/modules/stroeerCoreBidAdapter_spec.js +++ b/test/spec/modules/stroeerCoreBidAdapter_spec.js @@ -1,8 +1,8 @@ -import {assert} from 'chai'; -import {spec} from 'modules/stroeerCoreBidAdapter.js'; +import { assert } from 'chai'; +import { spec } from 'modules/stroeerCoreBidAdapter.js'; import * as utils from 'src/utils.js'; -import {BANNER, VIDEO} from '../../../src/mediaTypes.js'; -import {getGlobal} from '../../../src/prebidGlobal.js'; +import { BANNER, VIDEO } from '../../../src/mediaTypes.js'; +import { getGlobal } from '../../../src/prebidGlobal.js'; import sinon from 'sinon'; describe('stroeerCore bid adapter', function () { @@ -107,8 +107,10 @@ describe('stroeerCore bid adapter', function () { bidder: 'stroeerCore', adUnitCode: 'div-2', mediaTypes: { - banner: { - sizes: [[728, 90]], + video: { + context: 'outstream', + playerSize: [1280, 720], + mimes: ['video/mp4'] } }, params: { @@ -120,7 +122,7 @@ describe('stroeerCore bid adapter', function () { const buildBidderResponse = () => ({ 'bids': [{ - 'bidId': 'bid1', 'cpm': 4.0, 'width': 300, 'height': 600, 'ad': '
tag1
', 'tracking': {'brandId': 123} + 'bidId': 'bid1', 'cpm': 4.0, 'width': 300, 'height': 600, 'ad': '
tag1
', 'tracking': { 'brandId': 123 } }, { 'bidId': 'bid2', 'cpm': 7.3, 'width': 728, 'height': 90, 'ad': '
tag2
' }] @@ -133,7 +135,7 @@ describe('stroeerCore bid adapter', function () { }); const createWindow = (href, params = {}) => { - const {parent, top, frameElement, placementElements = []} = params; + const { parent, top, frameElement, placementElements = [] } = params; const protocol = href.startsWith('https') ? 'https:' : 'http:'; const win = { @@ -196,7 +198,7 @@ describe('stroeerCore bid adapter', function () { const topWin = createWindow('http://www.abc.org/'); topWin.innerHeight = 800; - const midWin = createWindow('http://www.abc.org/', {parent: topWin, top: topWin, frameElement: createElement()}); + const midWin = createWindow('http://www.abc.org/', { parent: topWin, top: topWin, frameElement: createElement() }); midWin.innerHeight = 400; const win = createWindow('http://www.xyz.com/', { @@ -208,7 +210,7 @@ describe('stroeerCore bid adapter', function () { sandBox.stub(utils, 'getWindowSelf').returns(win); sandBox.stub(utils, 'getWindowTop').returns(topWin); - return {topWin, midWin, win}; + return { topWin, midWin, win }; } it('should support BANNER and VIDEO mediaType', function () { @@ -352,19 +354,19 @@ describe('stroeerCore bid adapter', function () { describe('should use custom url if provided', () => { const samples = [{ protocol: 'http:', - params: {sid: 'ODA=', host: 'other.com', port: '234', path: '/xyz'}, + params: { sid: 'ODA=', host: 'other.com', port: '234', path: '/xyz' }, expected: 'https://other.com:234/xyz' }, { protocol: 'https:', - params: {sid: 'ODA=', host: 'other.com', port: '234', path: '/xyz'}, + params: { sid: 'ODA=', host: 'other.com', port: '234', path: '/xyz' }, expected: 'https://other.com:234/xyz' }, { protocol: 'https:', - params: {sid: 'ODA=', host: 'other.com', port: '234', securePort: '871', path: '/xyz'}, + params: { sid: 'ODA=', host: 'other.com', port: '234', securePort: '871', path: '/xyz' }, expected: 'https://other.com:871/xyz' }, { - protocol: 'http:', params: {sid: 'ODA=', port: '234', path: '/xyz'}, expected: 'https://hb.adscale.de:234/xyz' - }, ]; + protocol: 'http:', params: { sid: 'ODA=', port: '234', path: '/xyz' }, expected: 'https://hb.adscale.de:234/xyz' + },]; samples.forEach(sample => { it(`should use ${sample.expected} as endpoint when given params ${JSON.stringify(sample.params)} and protocol ${sample.protocol}`, @@ -430,8 +432,10 @@ describe('stroeerCore bid adapter', function () { 'sid': 'ODA=', 'bid': 'bid2', 'viz': true, - 'ban': { - 'siz': [[728, 90]] + 'vid': { + 'ctx': 'outstream', + 'mim': ['video/mp4'], + 'siz': [1280, 720] } }], 'user': { @@ -547,6 +551,7 @@ describe('stroeerCore bid adapter', function () { 'fp': undefined }, 'sfp': undefined, + 'tid': undefined, }, { 'sid': 'ABC=', @@ -557,6 +562,7 @@ describe('stroeerCore bid adapter', function () { }, 'viz': undefined, 'sfp': undefined, + 'tid': undefined, } ]; @@ -572,6 +578,7 @@ describe('stroeerCore bid adapter', function () { 'fp': undefined }, 'sfp': undefined, + 'tid': undefined, } ]; @@ -615,6 +622,7 @@ describe('stroeerCore bid adapter', function () { 'fp': undefined }, 'sfp': undefined, + 'tid': undefined, } ]; @@ -630,6 +638,7 @@ describe('stroeerCore bid adapter', function () { 'fp': undefined }, 'sfp': undefined, + 'tid': undefined, } ]; @@ -663,11 +672,11 @@ describe('stroeerCore bid adapter', function () { }); const gdprSamples = [ - {consentString: 'RG9ua2V5IEtvbmc=', gdprApplies: true}, - {consentString: 'UGluZyBQb25n', gdprApplies: false}, - {consentString: undefined, gdprApplies: true}, - {consentString: undefined, gdprApplies: false}, - {consentString: undefined, gdprApplies: undefined}, + { consentString: 'RG9ua2V5IEtvbmc=', gdprApplies: true }, + { consentString: 'UGluZyBQb25n', gdprApplies: false }, + { consentString: undefined, gdprApplies: true }, + { consentString: undefined, gdprApplies: false }, + { consentString: undefined, gdprApplies: undefined }, ]; gdprSamples.forEach((sample) => { it(`should add GDPR info ${JSON.stringify(sample)} when provided`, () => { @@ -736,19 +745,24 @@ describe('stroeerCore bid adapter', function () { getFloorStub1 .returns({}) - .withArgs({currency: 'EUR', mediaType: BANNER, size: '*'}) - .returns({currency: 'TRY', floor: 0.7}) - .withArgs({currency: 'EUR', mediaType: 'banner', size: [300, 600]}) - .returns({currency: 'TRY', floor: 1.3}) - .withArgs({currency: 'EUR', mediaType: 'banner', size: [160, 60]}) - .returns({currency: 'TRY', floor: 2.5}) + .withArgs({ currency: 'EUR', mediaType: BANNER, size: '*' }) + .returns({ currency: 'TRY', floor: 0.7 }) + .withArgs({ currency: 'EUR', mediaType: 'banner', size: [300, 600] }) + .returns({ currency: 'TRY', floor: 1.3 }) + .withArgs({ currency: 'EUR', mediaType: 'banner', size: [160, 60] }) + .returns({ currency: 'TRY', floor: 2.5 }) getFloorStub2 .returns({}) - .withArgs({currency: 'EUR', mediaType: 'banner', size: '*'}) - .returns({currency: 'USD', floor: 1.2}) - .withArgs({currency: 'EUR', mediaType: 'banner', size: [728, 90]}) - .returns({currency: 'USD', floor: 1.85}) + .withArgs({ currency: 'EUR', mediaType: 'banner', size: '*' }) + .returns({ currency: 'USD', floor: 1.2 }) + .withArgs({ currency: 'EUR', mediaType: 'banner', size: [728, 90] }) + .returns({ currency: 'USD', floor: 1.85 }) + + delete bidReq.bids[1].mediaTypes.video; + bidReq.bids[1].mediaTypes.banner = { + sizes: [[728, 90]], + }; bidReq.bids[0].getFloor = getFloorStub1; bidReq.bids[1].getFloor = getFloorStub2; @@ -761,13 +775,13 @@ describe('stroeerCore bid adapter', function () { assert.nestedPropertyVal(firstBid, 'ban.fp.def', 0.7); assert.nestedPropertyVal(firstBid, 'ban.fp.cur', 'TRY'); - assert.deepNestedPropertyVal(firstBid, 'ban.fp.siz', [{w: 300, h: 600, p: 1.3}, {w: 160, h: 60, p: 2.5}]); + assert.deepNestedPropertyVal(firstBid, 'ban.fp.siz', [{ w: 300, h: 600, p: 1.3 }, { w: 160, h: 60, p: 2.5 }]); assert.isTrue(getFloorStub1.calledThrice); assert.nestedPropertyVal(secondBid, 'ban.fp.def', 1.2); assert.nestedPropertyVal(secondBid, 'ban.fp.cur', 'USD'); - assert.deepNestedPropertyVal(secondBid, 'ban.fp.siz', [{w: 728, h: 90, p: 1.85}]); + assert.deepNestedPropertyVal(secondBid, 'ban.fp.siz', [{ w: 728, h: 90, p: 1.85 }]); assert.isTrue(getFloorStub2.calledTwice); }); @@ -780,17 +794,17 @@ describe('stroeerCore bid adapter', function () { getFloorStub1 .returns({}) - .withArgs({currency: 'EUR', mediaType: 'video', size: '*'}) - .returns({currency: 'NZD', floor: 3.25}) - .withArgs({currency: 'EUR', mediaType: 'video', size: [640, 480]}) - .returns({currency: 'NZD', floor: 4.10}); + .withArgs({ currency: 'EUR', mediaType: 'video', size: '*' }) + .returns({ currency: 'NZD', floor: 3.25 }) + .withArgs({ currency: 'EUR', mediaType: 'video', size: [640, 480] }) + .returns({ currency: 'NZD', floor: 4.10 }); getFloorStub2 .returns({}) - .withArgs({currency: 'EUR', mediaType: 'video', size: '*'}) - .returns({currency: 'GBP', floor: 4.75}) - .withArgs({currency: 'EUR', mediaType: 'video', size: [1280, 720]}) - .returns({currency: 'GBP', floor: 6.50}) + .withArgs({ currency: 'EUR', mediaType: 'video', size: '*' }) + .returns({ currency: 'GBP', floor: 4.75 }) + .withArgs({ currency: 'EUR', mediaType: 'video', size: [1280, 720] }) + .returns({ currency: 'GBP', floor: 6.50 }) delete bidReq.bids[0].mediaTypes.banner; bidReq.bids[0].mediaTypes.video = { @@ -798,12 +812,6 @@ describe('stroeerCore bid adapter', function () { context: 'instream' }; - delete bidReq.bids[1].mediaTypes.banner; - bidReq.bids[1].mediaTypes.video = { - playerSize: [1280, 720], - context: 'outstream' - }; - bidReq.bids[0].getFloor = getFloorStub1; bidReq.bids[1].getFloor = getFloorStub2; @@ -815,13 +823,13 @@ describe('stroeerCore bid adapter', function () { assert.nestedPropertyVal(firstBid, 'vid.fp.def', 3.25); assert.nestedPropertyVal(firstBid, 'vid.fp.cur', 'NZD'); - assert.deepNestedPropertyVal(firstBid, 'vid.fp.siz', [{w: 640, h: 480, p: 4.10}]); + assert.deepNestedPropertyVal(firstBid, 'vid.fp.siz', [{ w: 640, h: 480, p: 4.10 }]); assert.isTrue(getFloorStub1.calledTwice); assert.nestedPropertyVal(secondBid, 'vid.fp.def', 4.75); assert.nestedPropertyVal(secondBid, 'vid.fp.cur', 'GBP'); - assert.deepNestedPropertyVal(secondBid, 'vid.fp.siz', [{w: 1280, h: 720, p: 6.50}]); + assert.deepNestedPropertyVal(secondBid, 'vid.fp.siz', [{ w: 1280, h: 720, p: 6.50 }]); assert.isTrue(getFloorStub2.calledTwice); }); @@ -840,10 +848,10 @@ describe('stroeerCore bid adapter', function () { const secondBid = serverRequestBids[1]; assert.nestedPropertyVal(firstBid, 'ban.fp', undefined); - assert.nestedPropertyVal(secondBid, 'ban.fp', undefined); + assert.nestedPropertyVal(secondBid, 'vid.fp', undefined); - assert.isTrue(getFloorSpy.calledWith({currency: 'EUR', mediaType: 'banner', size: '*'})); - assert.isTrue(getFloorSpy.calledWith({currency: 'EUR', mediaType: 'banner', size: [728, 90]})); + assert.isTrue(getFloorSpy.calledWith({ currency: 'EUR', mediaType: 'video', size: '*' })); + assert.isTrue(getFloorSpy.calledWith({ currency: 'EUR', mediaType: 'video', size: [1280, 720] })); assert.isTrue(getFloorSpy.calledTwice); }); @@ -852,9 +860,9 @@ describe('stroeerCore bid adapter', function () { const getFloorStub = sinon.stub(); getFloorStub - .returns({currency: 'EUR', floor: 1.9}) - .withArgs({currency: 'EUR', mediaType: BANNER, size: [160, 60]}) - .returns({currency: 'EUR', floor: 2.7}); + .returns({ currency: 'EUR', floor: 1.9 }) + .withArgs({ currency: 'EUR', mediaType: BANNER, size: [160, 60] }) + .returns({ currency: 'EUR', floor: 2.7 }); bidReq.bids[0].getFloor = getFloorStub; @@ -865,7 +873,7 @@ describe('stroeerCore bid adapter', function () { assert.nestedPropertyVal(bid, 'ban.fp.def', 1.9); assert.nestedPropertyVal(bid, 'ban.fp.cur', 'EUR'); - assert.deepNestedPropertyVal(bid, 'ban.fp.siz', [{w: 160, h: 60, p: 2.7}]); + assert.deepNestedPropertyVal(bid, 'ban.fp.siz', [{ w: 160, h: 60, p: 2.7 }]); }); it('should add the DSA signals', () => { @@ -974,7 +982,70 @@ describe('stroeerCore bid adapter', function () { const serverRequestInfo = spec.buildRequests(bidReq.bids, bidReq); const sentOrtb2 = serverRequestInfo.data.ortb2; - assert.deepEqual(sentOrtb2, {site: {ext: ortb2.site.ext}}) + assert.deepEqual(sentOrtb2, { site: { ext: ortb2.site.ext } }) + }); + + it('should add the bid transaction id', () => { + const bidReq = buildBidderRequest(); + const uuid0 = 'f9545c4c-7d3f-4941-9319-d515af162085'; + const uuid1 = '8ce92d85-e9b0-4682-8025-bf58d452b2a7'; + + bidReq.bids[0].transactionId = uuid0; + bidReq.bids[1].transactionId = uuid1; + + const serverRequestInfo = spec.buildRequests(bidReq.bids, bidReq); + + const [bid0, bid1] = serverRequestInfo.data.bids; + + assert.equal(bid0.tid, uuid0); + assert.equal(bid1.tid, uuid1); + }); + + it('should add the source transaction id', () => { + const bidReq = buildBidderRequest(); + const tid = '7c3c82b2-30bb-49dc-9e3b-0148cd769a28'; + + const ortb2 = { + source: { + tid + } + }; + + bidReq.ortb2 = utils.deepClone(ortb2); + + const serverRequestInfo = spec.buildRequests(bidReq.bids, bidReq); + + const sentOrtb2 = serverRequestInfo.data.ortb2; + + assert.equal(sentOrtb2.source.tid, tid); + }); + + describe('ortb2Imp interface', () => { + it('should add the Global Placement IDs (GPID)', () => { + const bidReq = buildBidderRequest(); + + bidReq.bids[0].ortb2Imp = { + ext: { + gpid: '/8292/homepage-top', + do: 'not care about this' + } + }; + + bidReq.bids[1].ortb2Imp = { + random: { + number: 2329 + }, + ext: { + gpid: '/2231/bottom' + } + }; + + const serverRequestInfo = spec.buildRequests(bidReq.bids, bidReq); + const [bid1, bid2] = serverRequestInfo.data.bids; + + assert.deepEqual(bid1.ortb2Imp, { ext: { gpid: '/8292/homepage-top' } }); + assert.deepEqual(bid2.ortb2Imp, { ext: { gpid: '/2231/bottom' } }); + }); }); }); }); @@ -988,7 +1059,7 @@ describe('stroeerCore bid adapter', function () { const invalidResponses = ['', ' ', ' ', undefined, null]; invalidResponses.forEach(sample => { it('should ignore invalid responses (\"' + sample + '\") response', () => { - const result = spec.interpretResponse({body: sample}); + const result = spec.interpretResponse({ body: sample }); assert.isArray(result); assert.lengthOf(result, 0); }); @@ -997,19 +1068,19 @@ describe('stroeerCore bid adapter', function () { it('should interpret a standard response', () => { const bidderResponse = buildBidderResponse(); - const result = spec.interpretResponse({body: bidderResponse}); + const result = spec.interpretResponse({ body: bidderResponse }); assertStandardFieldsOnBannerBid(result[0], 'bid1', '
tag1
', 300, 600, 4); assertStandardFieldsOnBannerBid(result[1], 'bid2', '
tag2
', 728, 90, 7.3); }); it('should return empty array, when response contains no bids', () => { - const result = spec.interpretResponse({body: {bids: []}}); + const result = spec.interpretResponse({ body: { bids: [] } }); assert.deepStrictEqual(result, []); }); it('should interpret a video response', () => { const bidderResponse = buildBidderResponseWithVideo(); - const bidResponses = spec.interpretResponse({body: bidderResponse}); + const bidResponses = spec.interpretResponse({ body: bidderResponse }); const videoBidResponse = bidResponses[0]; assertStandardFieldsOnVideoBid(videoBidResponse, 'bid1', 'video', 800, 250, 4); }) @@ -1035,7 +1106,7 @@ describe('stroeerCore bid adapter', function () { }, }); - const result = spec.interpretResponse({body: response}); + const result = spec.interpretResponse({ body: response }); const firstBidMeta = result[0].meta; assert.deepPropertyVal(firstBidMeta, 'advertiserDomains', ['website.org', 'domain.com']); @@ -1074,13 +1145,13 @@ describe('stroeerCore bid adapter', function () { describe('when iframe option is enabled', () => { it('should perform user connect when there was a response', () => { const expectedUrl = 'https://js.adscale.de/pbsync.html'; - const userSyncResponse = spec.getUserSyncs({iframeEnabled: true}, ['']); + const userSyncResponse = spec.getUserSyncs({ iframeEnabled: true }, ['']); - assert.deepStrictEqual(userSyncResponse, [{type: 'iframe', url: expectedUrl}]); + assert.deepStrictEqual(userSyncResponse, [{ type: 'iframe', url: expectedUrl }]); }); it('should not perform user connect when there was no response', () => { - const userSyncResponse = spec.getUserSyncs({iframeEnabled: true}, []); + const userSyncResponse = spec.getUserSyncs({ iframeEnabled: true }, []); assert.deepStrictEqual(userSyncResponse, []); }); @@ -1089,26 +1160,26 @@ describe('stroeerCore bid adapter', function () { describe('and gdpr applies', () => { it('should place gdpr query param to the user sync url with value of 1', () => { const expectedUrl = 'https://js.adscale.de/pbsync.html?gdpr=1&gdpr_consent='; - const userSyncResponse = spec.getUserSyncs({iframeEnabled: true}, [''], {gdprApplies: true}); + const userSyncResponse = spec.getUserSyncs({ iframeEnabled: true }, [''], { gdprApplies: true }); - assert.deepStrictEqual(userSyncResponse, [{type: 'iframe', url: expectedUrl}]); + assert.deepStrictEqual(userSyncResponse, [{ type: 'iframe', url: expectedUrl }]); }); }); describe('and gdpr does not apply', () => { it('should place gdpr query param to the user sync url with zero value', () => { const expectedUrl = 'https://js.adscale.de/pbsync.html?gdpr=0&gdpr_consent='; - const userSyncResponse = spec.getUserSyncs({iframeEnabled: true}, [''], {gdprApplies: false}); + const userSyncResponse = spec.getUserSyncs({ iframeEnabled: true }, [''], { gdprApplies: false }); - assert.deepStrictEqual(userSyncResponse, [{type: 'iframe', url: expectedUrl}]); + assert.deepStrictEqual(userSyncResponse, [{ type: 'iframe', url: expectedUrl }]); }); describe('because consent does not specify it', () => { it('should place gdpr query param to the user sync url with zero value', () => { const expectedUrl = 'https://js.adscale.de/pbsync.html?gdpr=0&gdpr_consent='; - const userSyncResponse = spec.getUserSyncs({iframeEnabled: true}, [''], {}); + const userSyncResponse = spec.getUserSyncs({ iframeEnabled: true }, [''], {}); - assert.deepStrictEqual(userSyncResponse, [{type: 'iframe', url: expectedUrl}]); + assert.deepStrictEqual(userSyncResponse, [{ type: 'iframe', url: expectedUrl }]); }); }); }); @@ -1117,17 +1188,17 @@ describe('stroeerCore bid adapter', function () { it('should pass consent string to gdpr consent query param', () => { const consentString = 'consent_string'; const expectedUrl = `https://js.adscale.de/pbsync.html?gdpr=1&gdpr_consent=${consentString}`; - const userSyncResponse = spec.getUserSyncs({iframeEnabled: true}, [''], {gdprApplies: true, consentString}); + const userSyncResponse = spec.getUserSyncs({ iframeEnabled: true }, [''], { gdprApplies: true, consentString }); - assert.deepStrictEqual(userSyncResponse, [{type: 'iframe', url: expectedUrl}]); + assert.deepStrictEqual(userSyncResponse, [{ type: 'iframe', url: expectedUrl }]); }); it('should correctly escape invalid characters', () => { const consentString = 'consent ?stri&ng'; const expectedUrl = `https://js.adscale.de/pbsync.html?gdpr=1&gdpr_consent=consent%20%3Fstri%26ng`; - const userSyncResponse = spec.getUserSyncs({iframeEnabled: true}, [''], {gdprApplies: true, consentString}); + const userSyncResponse = spec.getUserSyncs({ iframeEnabled: true }, [''], { gdprApplies: true, consentString }); - assert.deepStrictEqual(userSyncResponse, [{type: 'iframe', url: expectedUrl}]); + assert.deepStrictEqual(userSyncResponse, [{ type: 'iframe', url: expectedUrl }]); }); }); }); @@ -1135,13 +1206,13 @@ describe('stroeerCore bid adapter', function () { describe('when iframe option is disabled', () => { it('should not perform user connect even when there was a response', () => { - const userSyncResponse = spec.getUserSyncs({iframeEnabled: false}, ['']); + const userSyncResponse = spec.getUserSyncs({ iframeEnabled: false }, ['']); assert.deepStrictEqual(userSyncResponse, []); }); it('should not perform user connect when there was no response', () => { - const userSyncResponse = spec.getUserSyncs({iframeEnabled: false}, []); + const userSyncResponse = spec.getUserSyncs({ iframeEnabled: false }, []); assert.deepStrictEqual(userSyncResponse, []); }); diff --git a/test/spec/modules/symitriDapRtdProvider_spec.js b/test/spec/modules/symitriDapRtdProvider_spec.js index f3deb840658..24adfde2ce3 100644 --- a/test/spec/modules/symitriDapRtdProvider_spec.js +++ b/test/spec/modules/symitriDapRtdProvider_spec.js @@ -1,4 +1,4 @@ -import {config} from 'src/config.js'; +import { config } from 'src/config.js'; import { dapUtils, generateRealTimeData, @@ -6,10 +6,10 @@ import { onBidWonListener, storage, DAP_MAX_RETRY_TOKENIZE, DAP_SS_ID, DAP_TOKEN, DAP_MEMBERSHIP, DAP_ENCRYPTED_MEMBERSHIP } from 'modules/symitriDapRtdProvider.js'; -import {server} from 'test/mocks/xhr.js'; -import {hook} from '../../../src/hook.js'; +import { server } from 'test/mocks/xhr.js'; +import { hook } from '../../../src/hook.js'; import { EVENTS } from 'src/constants.js'; -const responseHeader = {'Content-Type': 'application/json'}; +const responseHeader = { 'Content-Type': 'application/json' }; const events = require('src/events'); @@ -90,10 +90,10 @@ describe('symitriDapRtdProvider', function() { 'identity': sampleIdentity } const cacheExpiry = Math.round(Date.now() / 1000.0) + 300; // in seconds - const sampleCachedToken = {'expires_at': cacheExpiry, 'token': 'eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2Iiwia2lkIjoicGFzc3dvcmQxIn0..6buzBd2BjtgoyaNbHN8YnQ.l38avCfm3sYNy798-ETYOugz0cOx1cCkjACkAhYszxzrZ0sUJ0AiF-NdDXVTiTyp2Ih3vCWKzS0rKJ8lbS1zhyEVWVu91QwtwseM2fBbwA5ggAgBEo5wV-IXqDLPxVnxsPF0D3hP6cNCiH9Q2c-vULfsLhMhG5zvvZDPBbn4hUY5fKB8LoCBTF9rbuuWGYK1nramnb4AlS5UK82wBsHQea1Ou_Kp5wWCMNZ6TZk5qKIuRBfPIAhQblWvHECaHXkg1wyoM9VASs_yNhne7RR-qkwzbFiPFiMJibNOt9hF3_vPDJO5-06ZBjRTP1BllYGWxI-uQX6InzN18Wtun2WHqg.63sH0SNlIRcsK57v0pMujfB_nhU8Y5CuQbsHqH5MGoM'}; - const cachedEncryptedMembership = {'expires_at': cacheExpiry, 'encryptedSegments': 'eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2Iiwia2lkIjoic29tZXNlY3JldGludmF1bHQifQ..IvnIUQDqWBVYIS0gbcE9bw.Z4NZGvtogWaWlGH4e-GdYKe_PUc15M2x3Bj85rMWsN1A17mIxQIMOfg2hsQ2tgieLu5LggWPmsFu1Wbph6P0k3kOu1dVReoIhOHzxw50rP0DLHKaEZ5mLMJ7Lcosvwh4miIfFuCHlsX7J0sFgOTAp0zGo1S_UsHLtev1JflhjoSB0AoX95ALbAnyctirPuLJM8gZ1vXTiZ01jpvucGyR1lM4cWjPOeD8jPtgwaPGgSRZXE-3X2Cqy7z4Giam5Uqu74LPWTBuKtUQTGyAXA5QJoP7xwTbsU4O1f69lu3fWNqC92GijeTH1A4Zd_C-WXxWuQlDEURjlkWQoaqTHka2OqlnwukEQIf_v0r5KQQX64CTLhEUH91jeD0-E9ClcIP7pwOLxxqiKoaBmx8Mrnm_6Agj5DtTA1rusy3AL63sI_rsUxrmLrVt0Wft4aCfRkW8QpQxu8clFdOmce0NNCGeBCyCPVw9d9izrILlXJ6rItU2cpFrcbz8uw2otamF5eOFCOY3IzHedWVNNuKHFIUVC_xYSlsYvQ8f2QIP1eiMbmukcuPzmTzjw1h1_7IKaj-jJkXrnrY-TdDgX_4-_Z3rmbpXK2yTR7dBrsg-ubqFbgbKic1b4zlQEO_LbBlgPl3DYdWEuJ8CY2NUt1GfpATQGsufS2FTY1YGw_gkPe3q04l_cgLafDoxHvHh_t_0ZgPjciW82gThB_kN4RP7Mc3krVcXl_P6N1VbV07xyx0hCyVsrrxbLslI8q9wYDiLGci7mNmByM5j7SXV9jPwwPkHtn0HfMJlw2PFbIDPjgG3h7sOyLcBIJTTvuUIgpHPIkRWLIl_4FlIucXbJ7orW2nt5BWleBVHgumzGcnl9ZNcZb3W-dsdYPSOmuj0CY28MRTP2oJ1rzLInbDDpIRffJBtR7SS4nYyy7Vi09PtBigod5YNz1Q0WDSJxr8zeH_aKFaXInw7Bfo_U0IAcLiRgcT0ogsMLeQRjRFy27mr4XNJv3NtHhbdjDAwF2aClCktXyXbQaVdsPH2W71v6m2Q9rB5GQWOktw2s5f-4N1-_EBPGq6TgjF-aJZP22MJVwp1pimT50DfOzoeEqDwi862NNwNNoHmcObH0ZfwAXlhRxsgupNBe20-MNNABj2Phlfv4DUrtQbMdfCnNiypzNCmoTb7G7c_o5_JUwoV_GVkwUtvmi_IUm05P4GeMASSUw8zDKVRAj9h31C2cabM8RjMHGhkbCWpUP2pcz9zlJ7Y76Dh3RLnctfTw7DG9U4w4UlaxNZOgLUiSrGwfyapuSiuGUpuOJkBBLiHmEqAGI5C8oJpcVRccNlHxJAYowgXyFopD5Fr-FkXmv8KMkS0h5C9F6KihmDt5sqDD0qnjM0hHJgq01l7wjVnhEmPpyD-6auFQ-xDnbh1uBOJ_0gCVbRad--FSa5p-dXenggegRxOvZXJ0iAtM6Fal5Og-RCjexIHa9WhVbXhQBJpkSTWwAajZJ64eQ.yih49XB51wE-Xob7COT9OYqBrzBmIMVCQbLFx2UdzkI'}; - const cachedMembership = {'expires_at': cacheExpiry, 'said': 'eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2Iiwia2lkIjoicGFzc3dvcmQxIn0..QwvU5h0NVJYaJbs5EqWCKA.XNaJHSlnsH8P-yBIr3gIEqavLONWDIFyj7QCHFwJVkwXH_EYkxrk0_26b0uMPzfJp5URnqxKZusMH9DzEJsmj8EMrKQv1y3IYYMsW5_0BdP5bcAWfG6fzOqtMOwLiYRkYiQOqn1ZVGzhovheHWEmNr2_oCY0LvAr3iN1eG_K-l-bBKvBWnwvuuGKquUfCqO8NMMq6wtkecEXM9blqFRZ7oNYmW2aIG7qcHUsrUW7HMr9Ev2Ik0sIeEUsOYrgf_X_VA64RgKSTRugS9FupMv1p54JkHokwduF9pOFmW8QLQi8itFogKGbbgvOTNnmahxQUX5FcrjjYLqHwKqC8htLdlHnO5LWU9l4A7vLXrRurvoSnh0cAJy0GsdoyEwTqR9bwVFHoPquxlJjQ4buEd7PIxpBj9Qg9oOPH3b2upbMTu5CQ9oj526eXPhP5G54nwGklm2AZ3Vggd7jCQJn45Jjiq0iIfsXAtpqS2BssCLBN8WhmUTnStK8m5sux6WUBdrpDESQjPj-EEHVS-DB5rA7icRUh6EzRxzen2rndvHvnwVhSG_l6cwPYuJ0HE0KBmYHOoqNpKwzoGiKFHrf4ReA06iWB3V2TEGJucGujhtQ9_18WwHCeJ1XtQiiO1eqa3tp5MwAbFXawVFl3FFOBgadrPyvGmkmUJ6FCLU2MSwHiYZmANMnJsokFX_6DwoAgO3U_QnvEHIVSvefc7ReeJ8fBDdmrH3LtuLrUpXsvLvEIMQdWQ_SXhjKIi7tOODR8CfrhUcdIjsp3PZs1DpuOcDB6YJKbGnKZTluLUJi3TyHgyi-DHXdTm-jSE5i_DYJGW-t2Gf23FoQhexv4q7gdrfsKfcRJNrZLp6Gd6jl4zHhUtY.nprKBsy9taQBk6dCPbA7BFF0CiGhQOEF_MazZ2bedqk', 'cohorts': ['9', '11', '13']}; - const cachedMembershipWithDeals = {'expires_at': cacheExpiry, 'said': 'eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2Iiwia2lkIjoicGFzc3dvcmQxIn0..QwvU5h0NVJYaJbs5EqWCKA.XNaJHSlnsH8P-yBIr3gIEqavLONWDIFyj7QCHFwJVkwXH_EYkxrk0_26b0uMPzfJp5URnqxKZusMH9DzEJsmj8EMrKQv1y3IYYMsW5_0BdP5bcAWfG6fzOqtMOwLiYRkYiQOqn1ZVGzhovheHWEmNr2_oCY0LvAr3iN1eG_K-l-bBKvBWnwvuuGKquUfCqO8NMMq6wtkecEXM9blqFRZ7oNYmW2aIG7qcHUsrUW7HMr9Ev2Ik0sIeEUsOYrgf_X_VA64RgKSTRugS9FupMv1p54JkHokwduF9pOFmW8QLQi8itFogKGbbgvOTNnmahxQUX5FcrjjYLqHwKqC8htLdlHnO5LWU9l4A7vLXrRurvoSnh0cAJy0GsdoyEwTqR9bwVFHoPquxlJjQ4buEd7PIxpBj9Qg9oOPH3b2upbMTu5CQ9oj526eXPhP5G54nwGklm2AZ3Vggd7jCQJn45Jjiq0iIfsXAtpqS2BssCLBN8WhmUTnStK8m5sux6WUBdrpDESQjPj-EEHVS-DB5rA7icRUh6EzRxzen2rndvHvnwVhSG_l6cwPYuJ0HE0KBmYHOoqNpKwzoGiKFHrf4ReA06iWB3V2TEGJucGujhtQ9_18WwHCeJ1XtQiiO1eqa3tp5MwAbFXawVFl3FFOBgadrPyvGmkmUJ6FCLU2MSwHiYZmANMnJsokFX_6DwoAgO3U_QnvEHIVSvefc7ReeJ8fBDdmrH3LtuLrUpXsvLvEIMQdWQ_SXhjKIi7tOODR8CfrhUcdIjsp3PZs1DpuOcDB6YJKbGnKZTluLUJi3TyHgyi-DHXdTm-jSE5i_DYJGW-t2Gf23FoQhexv4q7gdrfsKfcRJNrZLp6Gd6jl4zHhUtY.nprKBsy9taQBk6dCPbA7BFF0CiGhQOEF_MazZ2bedqk', 'cohorts': ['9', '11', '13'], 'deals': ['{"id":"DEMODEAL555","bidfloor":5.0,"at":1,"guar":0}', '{"id":"DEMODEAL111","bidfloor":5.0,"at":1,"guar":0}', '{"id":"DEMODEAL123","bidfloor":5.0,"at":1,"guar":0}']}; + const sampleCachedToken = { 'expires_at': cacheExpiry, 'token': 'eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2Iiwia2lkIjoicGFzc3dvcmQxIn0..6buzBd2BjtgoyaNbHN8YnQ.l38avCfm3sYNy798-ETYOugz0cOx1cCkjACkAhYszxzrZ0sUJ0AiF-NdDXVTiTyp2Ih3vCWKzS0rKJ8lbS1zhyEVWVu91QwtwseM2fBbwA5ggAgBEo5wV-IXqDLPxVnxsPF0D3hP6cNCiH9Q2c-vULfsLhMhG5zvvZDPBbn4hUY5fKB8LoCBTF9rbuuWGYK1nramnb4AlS5UK82wBsHQea1Ou_Kp5wWCMNZ6TZk5qKIuRBfPIAhQblWvHECaHXkg1wyoM9VASs_yNhne7RR-qkwzbFiPFiMJibNOt9hF3_vPDJO5-06ZBjRTP1BllYGWxI-uQX6InzN18Wtun2WHqg.63sH0SNlIRcsK57v0pMujfB_nhU8Y5CuQbsHqH5MGoM' }; + const cachedEncryptedMembership = { 'expires_at': cacheExpiry, 'encryptedSegments': 'eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2Iiwia2lkIjoic29tZXNlY3JldGludmF1bHQifQ..IvnIUQDqWBVYIS0gbcE9bw.Z4NZGvtogWaWlGH4e-GdYKe_PUc15M2x3Bj85rMWsN1A17mIxQIMOfg2hsQ2tgieLu5LggWPmsFu1Wbph6P0k3kOu1dVReoIhOHzxw50rP0DLHKaEZ5mLMJ7Lcosvwh4miIfFuCHlsX7J0sFgOTAp0zGo1S_UsHLtev1JflhjoSB0AoX95ALbAnyctirPuLJM8gZ1vXTiZ01jpvucGyR1lM4cWjPOeD8jPtgwaPGgSRZXE-3X2Cqy7z4Giam5Uqu74LPWTBuKtUQTGyAXA5QJoP7xwTbsU4O1f69lu3fWNqC92GijeTH1A4Zd_C-WXxWuQlDEURjlkWQoaqTHka2OqlnwukEQIf_v0r5KQQX64CTLhEUH91jeD0-E9ClcIP7pwOLxxqiKoaBmx8Mrnm_6Agj5DtTA1rusy3AL63sI_rsUxrmLrVt0Wft4aCfRkW8QpQxu8clFdOmce0NNCGeBCyCPVw9d9izrILlXJ6rItU2cpFrcbz8uw2otamF5eOFCOY3IzHedWVNNuKHFIUVC_xYSlsYvQ8f2QIP1eiMbmukcuPzmTzjw1h1_7IKaj-jJkXrnrY-TdDgX_4-_Z3rmbpXK2yTR7dBrsg-ubqFbgbKic1b4zlQEO_LbBlgPl3DYdWEuJ8CY2NUt1GfpATQGsufS2FTY1YGw_gkPe3q04l_cgLafDoxHvHh_t_0ZgPjciW82gThB_kN4RP7Mc3krVcXl_P6N1VbV07xyx0hCyVsrrxbLslI8q9wYDiLGci7mNmByM5j7SXV9jPwwPkHtn0HfMJlw2PFbIDPjgG3h7sOyLcBIJTTvuUIgpHPIkRWLIl_4FlIucXbJ7orW2nt5BWleBVHgumzGcnl9ZNcZb3W-dsdYPSOmuj0CY28MRTP2oJ1rzLInbDDpIRffJBtR7SS4nYyy7Vi09PtBigod5YNz1Q0WDSJxr8zeH_aKFaXInw7Bfo_U0IAcLiRgcT0ogsMLeQRjRFy27mr4XNJv3NtHhbdjDAwF2aClCktXyXbQaVdsPH2W71v6m2Q9rB5GQWOktw2s5f-4N1-_EBPGq6TgjF-aJZP22MJVwp1pimT50DfOzoeEqDwi862NNwNNoHmcObH0ZfwAXlhRxsgupNBe20-MNNABj2Phlfv4DUrtQbMdfCnNiypzNCmoTb7G7c_o5_JUwoV_GVkwUtvmi_IUm05P4GeMASSUw8zDKVRAj9h31C2cabM8RjMHGhkbCWpUP2pcz9zlJ7Y76Dh3RLnctfTw7DG9U4w4UlaxNZOgLUiSrGwfyapuSiuGUpuOJkBBLiHmEqAGI5C8oJpcVRccNlHxJAYowgXyFopD5Fr-FkXmv8KMkS0h5C9F6KihmDt5sqDD0qnjM0hHJgq01l7wjVnhEmPpyD-6auFQ-xDnbh1uBOJ_0gCVbRad--FSa5p-dXenggegRxOvZXJ0iAtM6Fal5Og-RCjexIHa9WhVbXhQBJpkSTWwAajZJ64eQ.yih49XB51wE-Xob7COT9OYqBrzBmIMVCQbLFx2UdzkI' }; + const cachedMembership = { 'expires_at': cacheExpiry, 'said': 'eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2Iiwia2lkIjoicGFzc3dvcmQxIn0..QwvU5h0NVJYaJbs5EqWCKA.XNaJHSlnsH8P-yBIr3gIEqavLONWDIFyj7QCHFwJVkwXH_EYkxrk0_26b0uMPzfJp5URnqxKZusMH9DzEJsmj8EMrKQv1y3IYYMsW5_0BdP5bcAWfG6fzOqtMOwLiYRkYiQOqn1ZVGzhovheHWEmNr2_oCY0LvAr3iN1eG_K-l-bBKvBWnwvuuGKquUfCqO8NMMq6wtkecEXM9blqFRZ7oNYmW2aIG7qcHUsrUW7HMr9Ev2Ik0sIeEUsOYrgf_X_VA64RgKSTRugS9FupMv1p54JkHokwduF9pOFmW8QLQi8itFogKGbbgvOTNnmahxQUX5FcrjjYLqHwKqC8htLdlHnO5LWU9l4A7vLXrRurvoSnh0cAJy0GsdoyEwTqR9bwVFHoPquxlJjQ4buEd7PIxpBj9Qg9oOPH3b2upbMTu5CQ9oj526eXPhP5G54nwGklm2AZ3Vggd7jCQJn45Jjiq0iIfsXAtpqS2BssCLBN8WhmUTnStK8m5sux6WUBdrpDESQjPj-EEHVS-DB5rA7icRUh6EzRxzen2rndvHvnwVhSG_l6cwPYuJ0HE0KBmYHOoqNpKwzoGiKFHrf4ReA06iWB3V2TEGJucGujhtQ9_18WwHCeJ1XtQiiO1eqa3tp5MwAbFXawVFl3FFOBgadrPyvGmkmUJ6FCLU2MSwHiYZmANMnJsokFX_6DwoAgO3U_QnvEHIVSvefc7ReeJ8fBDdmrH3LtuLrUpXsvLvEIMQdWQ_SXhjKIi7tOODR8CfrhUcdIjsp3PZs1DpuOcDB6YJKbGnKZTluLUJi3TyHgyi-DHXdTm-jSE5i_DYJGW-t2Gf23FoQhexv4q7gdrfsKfcRJNrZLp6Gd6jl4zHhUtY.nprKBsy9taQBk6dCPbA7BFF0CiGhQOEF_MazZ2bedqk', 'cohorts': ['9', '11', '13'] }; + const cachedMembershipWithDeals = { 'expires_at': cacheExpiry, 'said': 'eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2Iiwia2lkIjoicGFzc3dvcmQxIn0..QwvU5h0NVJYaJbs5EqWCKA.XNaJHSlnsH8P-yBIr3gIEqavLONWDIFyj7QCHFwJVkwXH_EYkxrk0_26b0uMPzfJp5URnqxKZusMH9DzEJsmj8EMrKQv1y3IYYMsW5_0BdP5bcAWfG6fzOqtMOwLiYRkYiQOqn1ZVGzhovheHWEmNr2_oCY0LvAr3iN1eG_K-l-bBKvBWnwvuuGKquUfCqO8NMMq6wtkecEXM9blqFRZ7oNYmW2aIG7qcHUsrUW7HMr9Ev2Ik0sIeEUsOYrgf_X_VA64RgKSTRugS9FupMv1p54JkHokwduF9pOFmW8QLQi8itFogKGbbgvOTNnmahxQUX5FcrjjYLqHwKqC8htLdlHnO5LWU9l4A7vLXrRurvoSnh0cAJy0GsdoyEwTqR9bwVFHoPquxlJjQ4buEd7PIxpBj9Qg9oOPH3b2upbMTu5CQ9oj526eXPhP5G54nwGklm2AZ3Vggd7jCQJn45Jjiq0iIfsXAtpqS2BssCLBN8WhmUTnStK8m5sux6WUBdrpDESQjPj-EEHVS-DB5rA7icRUh6EzRxzen2rndvHvnwVhSG_l6cwPYuJ0HE0KBmYHOoqNpKwzoGiKFHrf4ReA06iWB3V2TEGJucGujhtQ9_18WwHCeJ1XtQiiO1eqa3tp5MwAbFXawVFl3FFOBgadrPyvGmkmUJ6FCLU2MSwHiYZmANMnJsokFX_6DwoAgO3U_QnvEHIVSvefc7ReeJ8fBDdmrH3LtuLrUpXsvLvEIMQdWQ_SXhjKIi7tOODR8CfrhUcdIjsp3PZs1DpuOcDB6YJKbGnKZTluLUJi3TyHgyi-DHXdTm-jSE5i_DYJGW-t2Gf23FoQhexv4q7gdrfsKfcRJNrZLp6Gd6jl4zHhUtY.nprKBsy9taQBk6dCPbA7BFF0CiGhQOEF_MazZ2bedqk', 'cohorts': ['9', '11', '13'], 'deals': ['{"id":"DEMODEAL555","bidfloor":5.0,"at":1,"guar":0}', '{"id":"DEMODEAL111","bidfloor":5.0,"at":1,"guar":0}', '{"id":"DEMODEAL123","bidfloor":5.0,"at":1,"guar":0}'] }; const rtdUserObj = { name: 'www.dataprovider3.com', ext: { @@ -154,7 +154,7 @@ describe('symitriDapRtdProvider', function() { let ortb2, bidConfig; beforeEach(function() { - bidConfig = {ortb2Fragments: {}}; + bidConfig = { ortb2Fragments: {} }; ortb2 = bidConfig.ortb2Fragments.global = {}; config.resetConfig(); storage.removeDataFromLocalStorage(DAP_TOKEN); @@ -204,7 +204,7 @@ describe('symitriDapRtdProvider', function() { try { expect(ortb2).to.eql({}); dapUtils.callDapAPIs(bidConfig, () => {}, cmoduleConfig, {}); - const membership = {'cohorts': ['9', '11', '13'], 'said': 'sample-said'} + const membership = { 'cohorts': ['9', '11', '13'], 'said': 'sample-said' } const membershipRequest = server.requests[0]; membershipRequest.respond(200, responseHeader, JSON.stringify(membership)); const tokenWithExpiry = 'Sample-token-with-exp' @@ -234,7 +234,7 @@ describe('symitriDapRtdProvider', function() { tokenizeRequest.requestHeaders['Content-Type'].should.equal('application/json'); responseHeader['Symitri-DAP-Token'] = tokenWithExpiry; tokenizeRequest.respond(200, responseHeader, JSON.stringify(tokenWithExpiry)); - const data = dapUtils.dapGetEncryptedRtdObj({'encryptedSegments': encMembership}, emoduleConfig.params.segtax); + const data = dapUtils.dapGetEncryptedRtdObj({ 'encryptedSegments': encMembership }, emoduleConfig.params.segtax); expect(ortb2.user.data).to.deep.include.members(data.rtd.ortb2.user.data); } finally { dapExtractExpiryFromTokenStub.restore(); @@ -447,7 +447,7 @@ describe('symitriDapRtdProvider', function() { segtax: 708 }; expect(dapUtils.dapRefreshMembership(ortb2, config, 'token', onDone)).to.equal(undefined) - const membership = {cohorts: ['1', '5', '7']} + const membership = { cohorts: ['1', '5', '7'] } expect(dapUtils.dapGetRtdObj(membership, config.segtax)).to.not.equal(undefined); }); }); @@ -518,7 +518,7 @@ describe('symitriDapRtdProvider', function() { const request = server.requests[0]; responseHeader['Symitri-DAP-Token'] = encMembership; request.respond(200, responseHeader, encMembership); - const rtdObj = dapUtils.dapGetEncryptedRtdObj({'encryptedSegments': encMembership}, 710) + const rtdObj = dapUtils.dapGetEncryptedRtdObj({ 'encryptedSegments': encMembership }, 710) expect(ortb2.user.data).to.deep.include.members(rtdObj.rtd.ortb2.user.data); expect(JSON.parse(storage.getDataFromLocalStorage(DAP_ENCRYPTED_MEMBERSHIP)).expires_at).to.equal(expiry); }); @@ -529,7 +529,7 @@ describe('symitriDapRtdProvider', function() { const request = server.requests[0]; responseHeader['Symitri-DAP-Token'] = encMembership; request.respond(200, responseHeader, encMembership); - const rtdObj = dapUtils.dapGetEncryptedRtdObj({'encryptedSegments': encMembership}, 710) + const rtdObj = dapUtils.dapGetEncryptedRtdObj({ 'encryptedSegments': encMembership }, 710) expect(ortb2.user.data).to.deep.include.members(rtdObj.rtd.ortb2.user.data); expect(JSON.parse(storage.getDataFromLocalStorage(DAP_ENCRYPTED_MEMBERSHIP)).expires_at).to.equal(1643830630); }); @@ -556,7 +556,7 @@ describe('symitriDapRtdProvider', function() { describe('dapRefreshMembership test', function () { it('test dapRefreshMembership success response', function () { - const membership = {'cohorts': ['9', '11', '13'], 'said': 'eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2Iiwia2lkIjoicGFzc3dvcmQxIn0..17wnrhz6FbWx0Cf6LXpm1A.m9PKVCradk3CZokNKzVHzE06TOqiXYeijgxTQUiQy5Syx-yicnO8DyYX6zQ6rgPcNgUNRt4R4XE5MXuK0laUVQJr9yc9g3vUfQfw69OMYGW_vRlLMPzoNOhF2c4gSyfkRrLr7C0qgALmZO1D11sPflaCTNmO7pmZtRaCOB5buHoWcQhp1bUSJ09DNDb31dX3llimPwjNGSrUhyq_EZl4HopnnjxbM4qVNMY2G_43C_idlVOvbFoTxcDRATd-6MplJoIOIHQLDZEetpIOVcbEYN9gQ_ndBISITwuu5YEgs5C_WPHA25nm6e4BT5R-tawSA8yPyQAupqE8gk4ZWq_2-T0cqyTstIHrMQnZ_vysYN7h6bkzE-KeZRk7GMtySN87_fiu904hLD9QentGegamX6UAbVqQh7Htj7SnMHXkEenjxXAM5mRqQvNCTlw8k-9-VPXs-vTcKLYP8VFf8gMOmuYykgWac1gX-svyAg-24mo8cUbqcsj9relx4Qj5HiXUVyDMBZxK-mHZi-Xz6uv9GlggcsjE13DSszar-j2OetigpdibnJIxRZ-4ew3-vlvZ0Dul3j0LjeWURVBWYWfMjuZ193G7lwR3ohh_NzlNfwOPBK_SYurdAnLh7jJgTW-lVLjH2Dipmi9JwX9s03IQq9opexAn7hlM9oBI6x5asByH8JF8WwZ5GhzDjpDwpSmHPQNGFRSyrx_Sh2CPWNK6C1NJmLkyqAtJ5iw0_al7vPDQyZrKXaLTjBCUnbpJhUZ8dUKtWLzGPjzFXp10muoDIutd1NfyKxk1aWGhx5aerYuLdywv6cT_M8RZTi8924NGj5VA30V5OvEwLLyX93eDhntXZSCbkPHpAfiRZNGXrPY.GhCbWGQz11mIRD4uPKmoAuFXDH7hGnils54zg7N7-TU'} + const membership = { 'cohorts': ['9', '11', '13'], 'said': 'eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2Iiwia2lkIjoicGFzc3dvcmQxIn0..17wnrhz6FbWx0Cf6LXpm1A.m9PKVCradk3CZokNKzVHzE06TOqiXYeijgxTQUiQy5Syx-yicnO8DyYX6zQ6rgPcNgUNRt4R4XE5MXuK0laUVQJr9yc9g3vUfQfw69OMYGW_vRlLMPzoNOhF2c4gSyfkRrLr7C0qgALmZO1D11sPflaCTNmO7pmZtRaCOB5buHoWcQhp1bUSJ09DNDb31dX3llimPwjNGSrUhyq_EZl4HopnnjxbM4qVNMY2G_43C_idlVOvbFoTxcDRATd-6MplJoIOIHQLDZEetpIOVcbEYN9gQ_ndBISITwuu5YEgs5C_WPHA25nm6e4BT5R-tawSA8yPyQAupqE8gk4ZWq_2-T0cqyTstIHrMQnZ_vysYN7h6bkzE-KeZRk7GMtySN87_fiu904hLD9QentGegamX6UAbVqQh7Htj7SnMHXkEenjxXAM5mRqQvNCTlw8k-9-VPXs-vTcKLYP8VFf8gMOmuYykgWac1gX-svyAg-24mo8cUbqcsj9relx4Qj5HiXUVyDMBZxK-mHZi-Xz6uv9GlggcsjE13DSszar-j2OetigpdibnJIxRZ-4ew3-vlvZ0Dul3j0LjeWURVBWYWfMjuZ193G7lwR3ohh_NzlNfwOPBK_SYurdAnLh7jJgTW-lVLjH2Dipmi9JwX9s03IQq9opexAn7hlM9oBI6x5asByH8JF8WwZ5GhzDjpDwpSmHPQNGFRSyrx_Sh2CPWNK6C1NJmLkyqAtJ5iw0_al7vPDQyZrKXaLTjBCUnbpJhUZ8dUKtWLzGPjzFXp10muoDIutd1NfyKxk1aWGhx5aerYuLdywv6cT_M8RZTi8924NGj5VA30V5OvEwLLyX93eDhntXZSCbkPHpAfiRZNGXrPY.GhCbWGQz11mIRD4uPKmoAuFXDH7hGnils54zg7N7-TU' } dapUtils.dapRefreshMembership(ortb2, sampleConfig, sampleCachedToken.token, onDone); const request = server.requests[0]; request.respond(200, responseHeader, JSON.stringify(membership)); @@ -565,7 +565,7 @@ describe('symitriDapRtdProvider', function() { }); it('test dapRefreshMembership success response with exp claim', function () { - const membership = {'cohorts': ['9', '11', '13'], 'said': 'eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2Iiwia2lkIjoicGFzc3dvcmQxIiwiZXhwIjoxNjQ3OTcxNTU4fQ..ptdM5WO-62ypXlKxFXD4FQ.waEo9MHS2NYQCi-zh_p6HgT9BdqGyQbBq4GfGLfsay4nRBgICsTS-VkV6e7xx5U1T8BgpKkRJIZBwTOY5Pkxk9FpK5nnffDSEljRrp1LXLCkNP4qwrlqHInFbZsonNWW4_mW-7aUPlTwIsTbfjTuyHdXHeQa1ALrwFFFWE7QUmPNd2RsHjDwUsxlJPEb5TnHn5W0Mgo_PQZaxvhJInMbxPgtJLoqnJvOqCBEoQY7au7ALZL_nWK8XIwPMF19J7Z3cBg9vQInhr_E3rMdQcAFHEzYfgoNcIYCCR0t1UOqUE3HNtX-E64kZAYKWdlsBb9eW5Gj9hHYyPNL_4Hntjg5eLXGpsocMg0An-qQKGC6hkrxKzeM-GrjpvSaQLNs4iqDpHUtzA02LW_vkLkMNRUiyXVJ3FUZwfyq6uHSRKWZ6UFdAfL0rfJ8q8x8Ll-qJO2Jfyvidlsi9FIs7x1WJrvDCKepfAQM1UXRTonrQljFBAk83PcL2bmWuJDgJZ0lWS4VnZbIf6A7fDourmkDxdVRptvQq5nSjtzCA6whRw0-wGz8ehNJsaJw9H_nG9k4lRKs7A5Lqsyy7TVFrAPjnA_Q1a2H6xF2ULxrtIqoNqdX7k9RjowEZSQlZgZUOAmI4wzjckdcSyC_pUlYBMcBwmlld34mmOJe9EBHAxjdci7Q_9lvj1HTcwGDcQITXnkW9Ux5Jkt9Naw-IGGrnEIADaT2guUAto8W_Gb05TmwHSd6DCmh4zepQCbqeVe6AvPILtVkTgsTTo27Q-NvS7h-XtthJy8425j5kqwxxpZFJ0l0ytc6DUyNCLJXuxi0JFU6-LoSXcROEMVrHa_Achufr9vHIELwacSAIHuwseEvg_OOu1c1WYEwZH8ynBLSjqzy8AnDj24hYgA0YanPAvDqacrYrTUFqURbHmvcQqLBTcYa_gs7uDx4a1EjtP_NvHRlvCgGAaASrjGMhTX8oJxlTqahhQ.pXm-7KqnNK8sbyyczwkVYhcjgiwkpO8LjBBVw4lcyZE'}; + const membership = { 'cohorts': ['9', '11', '13'], 'said': 'eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2Iiwia2lkIjoicGFzc3dvcmQxIiwiZXhwIjoxNjQ3OTcxNTU4fQ..ptdM5WO-62ypXlKxFXD4FQ.waEo9MHS2NYQCi-zh_p6HgT9BdqGyQbBq4GfGLfsay4nRBgICsTS-VkV6e7xx5U1T8BgpKkRJIZBwTOY5Pkxk9FpK5nnffDSEljRrp1LXLCkNP4qwrlqHInFbZsonNWW4_mW-7aUPlTwIsTbfjTuyHdXHeQa1ALrwFFFWE7QUmPNd2RsHjDwUsxlJPEb5TnHn5W0Mgo_PQZaxvhJInMbxPgtJLoqnJvOqCBEoQY7au7ALZL_nWK8XIwPMF19J7Z3cBg9vQInhr_E3rMdQcAFHEzYfgoNcIYCCR0t1UOqUE3HNtX-E64kZAYKWdlsBb9eW5Gj9hHYyPNL_4Hntjg5eLXGpsocMg0An-qQKGC6hkrxKzeM-GrjpvSaQLNs4iqDpHUtzA02LW_vkLkMNRUiyXVJ3FUZwfyq6uHSRKWZ6UFdAfL0rfJ8q8x8Ll-qJO2Jfyvidlsi9FIs7x1WJrvDCKepfAQM1UXRTonrQljFBAk83PcL2bmWuJDgJZ0lWS4VnZbIf6A7fDourmkDxdVRptvQq5nSjtzCA6whRw0-wGz8ehNJsaJw9H_nG9k4lRKs7A5Lqsyy7TVFrAPjnA_Q1a2H6xF2ULxrtIqoNqdX7k9RjowEZSQlZgZUOAmI4wzjckdcSyC_pUlYBMcBwmlld34mmOJe9EBHAxjdci7Q_9lvj1HTcwGDcQITXnkW9Ux5Jkt9Naw-IGGrnEIADaT2guUAto8W_Gb05TmwHSd6DCmh4zepQCbqeVe6AvPILtVkTgsTTo27Q-NvS7h-XtthJy8425j5kqwxxpZFJ0l0ytc6DUyNCLJXuxi0JFU6-LoSXcROEMVrHa_Achufr9vHIELwacSAIHuwseEvg_OOu1c1WYEwZH8ynBLSjqzy8AnDj24hYgA0YanPAvDqacrYrTUFqURbHmvcQqLBTcYa_gs7uDx4a1EjtP_NvHRlvCgGAaASrjGMhTX8oJxlTqahhQ.pXm-7KqnNK8sbyyczwkVYhcjgiwkpO8LjBBVw4lcyZE' }; dapUtils.dapRefreshMembership(ortb2, sampleConfig, sampleCachedToken.token, onDone); const request = server.requests[0]; request.respond(200, responseHeader, JSON.stringify(membership)); @@ -597,7 +597,7 @@ describe('symitriDapRtdProvider', function() { it('test dapGetEncryptedMembershipFromLocalStorage function with invalid cache', function () { const expiry = Math.round(Date.now() / 1000.0) - 100; // in seconds - const encMembership = {'expiry': expiry, 'encryptedSegments': cachedEncryptedMembership.encryptedSegments} + const encMembership = { 'expiry': expiry, 'encryptedSegments': cachedEncryptedMembership.encryptedSegments } storage.setDataInLocalStorage(DAP_ENCRYPTED_MEMBERSHIP, JSON.stringify(encMembership)) expect(dapUtils.dapGetEncryptedMembershipFromLocalStorage()).to.equal(null); }); @@ -644,11 +644,11 @@ describe('symitriDapRtdProvider', function() { }); it('USP consent present and user have not been provided with option to opt out', function () { - expect(symitriDapRtdSubmodule.init(null, {'usp': '1NYY'})).to.equal(false); + expect(symitriDapRtdSubmodule.init(null, { 'usp': '1NYY' })).to.equal(false); }); it('USP consent present and user have not opted out', function () { - expect(symitriDapRtdSubmodule.init(null, {'usp': '1YNY'})).to.equal(true); + expect(symitriDapRtdSubmodule.init(null, { 'usp': '1YNY' })).to.equal(true); }); }); @@ -695,7 +695,7 @@ describe('symitriDapRtdProvider', function() { }); describe('onBidResponseEvent', function () { - const bidResponse = {adId: 'ad_123', bidder: 'test_bidder', bidderCode: 'test_bidder_code', cpm: '1.5', creativeId: 'creative_123', dealId: 'DEMODEAL555', mediaType: 'banner', responseTimestamp: '1725892736147', ad: ''}; + const bidResponse = { adId: 'ad_123', bidder: 'test_bidder', bidderCode: 'test_bidder_code', cpm: '1.5', creativeId: 'creative_123', dealId: 'DEMODEAL555', mediaType: 'banner', responseTimestamp: '1725892736147', ad: '' }; const url = emoduleConfig.params.pixelUrl + '?token=' + sampleCachedToken.token + '&ad_id=' + bidResponse.adId + '&bidder=' + bidResponse.bidder + '&bidder_code=' + bidResponse.bidderCode + '&cpm=' + bidResponse.cpm + '&creative_id=' + bidResponse.creativeId + '&deal_id=' + bidResponse.dealId + '&media_type=' + bidResponse.mediaType + '&response_timestamp=' + bidResponse.responseTimestamp; const adPixel = `${bidResponse.ad}"'; @@ -428,7 +428,7 @@ describe('teadsBidAdapter', () => { model: 'iPhone 12 Pro Max', os: 'iOS', osv: '17.4', - ext: {fiftyonedegrees_deviceId: '17595-133085-133468-18092'}, + ext: { fiftyonedegrees_deviceId: '17595-133085-133468-18092' }, }, }, }, @@ -765,20 +765,20 @@ describe('teadsBidAdapter', () => { source: 2, platform: { brand: 'macOS', - version: [ '12', '4', '0' ] + version: ['12', '4', '0'] }, browsers: [ { brand: 'Chromium', - version: [ '106', '0', '5249', '119' ] + version: ['106', '0', '5249', '119'] }, { brand: 'Google Chrome', - version: [ '106', '0', '5249', '119' ] + version: ['106', '0', '5249', '119'] }, { brand: 'Not;A=Brand', - version: [ '99', '0', '0', '0' ] + version: ['99', '0', '0', '0'] } ], mobile: 0, @@ -798,20 +798,20 @@ describe('teadsBidAdapter', () => { source: 2, platform: { brand: 'macOS', - version: [ '12', '4', '0' ] + version: ['12', '4', '0'] }, browsers: [ { brand: 'Chromium', - version: [ '106', '0', '5249', '119' ] + version: ['106', '0', '5249', '119'] }, { brand: 'Google Chrome', - version: [ '106', '0', '5249', '119' ] + version: ['106', '0', '5249', '119'] }, { brand: 'Not;A=Brand', - version: [ '99', '0', '0', '0' ] + version: ['99', '0', '0', '0'] } ], mobile: 0, @@ -863,7 +863,7 @@ describe('teadsBidAdapter', () => { const toEid = (sourceId, value) => ({ source: sourceId, - uids: [{id: value}] + uids: [{ id: value }] }) describe('User IDs', function () { @@ -889,7 +889,6 @@ describe('teadsBidAdapter', () => { id5Id: toEid('id5-sync.com', 'id5Id-id'), criteoId: toEid('criteo.com', 'criteoId-id'), yahooConnectId: toEid('yahoo.com', 'yahooConnectId-id'), - quantcastId: toEid('quantcast.com', 'quantcastId-id'), epsilonPublisherLinkId: toEid('epsilon.com', 'epsilonPublisherLinkId-id'), publisherFirstPartyViewerId: toEid('pubcid.org', 'publisherFirstPartyViewerId-id'), merkleId: toEid('merkleinc.com', 'merkleId-id'), @@ -957,7 +956,6 @@ describe('teadsBidAdapter', () => { expect(payload['id5Id']).to.equal('id5Id-id'); expect(payload['criteoId']).to.equal('criteoId-id'); expect(payload['yahooConnectId']).to.equal('yahooConnectId-id'); - expect(payload['quantcastId']).to.equal('quantcastId-id'); expect(payload['epsilonPublisherLinkId']).to.equal('epsilonPublisherLinkId-id'); expect(payload['publisherFirstPartyViewerId']).to.equal('publisherFirstPartyViewerId-id'); expect(payload['merkleId']).to.equal('merkleId-id'); diff --git a/test/spec/modules/teadsIdSystem_spec.js b/test/spec/modules/teadsIdSystem_spec.js index ed4ea887d5b..cae9d6e7b51 100644 --- a/test/spec/modules/teadsIdSystem_spec.js +++ b/test/spec/modules/teadsIdSystem_spec.js @@ -8,7 +8,7 @@ import { getGdprConsentString, getCookieExpirationDate, getTimestampFromDays, getCcpaConsentString } from 'modules/teadsIdSystem.js'; -import {server} from 'test/mocks/xhr.js'; +import { server } from 'test/mocks/xhr.js'; import * as utils from '../../../src/utils.js'; const FP_TEADS_ID_COOKIE_NAME = '_tfpvi'; @@ -234,7 +234,7 @@ describe('TeadsIdSystem', function () { callback(callbackSpy); const request = server.requests[0]; expect(request.url).to.include(teadsUrl); - request.respond(200, {'Content-Type': 'application/json'}, teadsCookieIdSent); + request.respond(200, { 'Content-Type': 'application/json' }, teadsCookieIdSent); expect(callbackSpy.lastCall.lastArg).to.deep.equal(teadsCookieIdSent); }); @@ -248,7 +248,7 @@ describe('TeadsIdSystem', function () { }); const request = server.requests[0]; - request.respond(200, {'Content-Type': 'application/json'}, teadsCookieIdSent); + request.respond(200, { 'Content-Type': 'application/json' }, teadsCookieIdSent); const cookiesMaxAge = getTimestampFromDays(365); // 1 year const expirationCookieDate = getCookieExpirationDate(cookiesMaxAge); @@ -265,7 +265,7 @@ describe('TeadsIdSystem', function () { }); const request = server.requests[0]; - request.respond(200, {'Content-Type': 'application/json'}, ''); + request.respond(200, { 'Content-Type': 'application/json' }, ''); expect(setCookieStub.calledWith(FP_TEADS_ID_COOKIE_NAME, '', EXPIRED_COOKIE_DATE)).to.be.true; }); diff --git a/test/spec/modules/tealBidAdapter_spec.js b/test/spec/modules/tealBidAdapter_spec.js index 12e04d0b4d5..1452c7689f8 100644 --- a/test/spec/modules/tealBidAdapter_spec.js +++ b/test/spec/modules/tealBidAdapter_spec.js @@ -205,7 +205,7 @@ const buildRequest = (params) => { describe('Teal Bid Adaper', function () { describe('buildRequests', () => { - const {data, url} = buildRequest(); + const { data, url } = buildRequest(); it('should give the correct URL', () => { expect(url).equal(`https://${PBS_HOST}/openrtb2/auction`); }); @@ -222,7 +222,7 @@ describe('Teal Bid Adaper', function () { }); }); describe('buildRequests with subAccount', () => { - const {data} = buildRequest({ subAccount: SUB_ACCOUNT }); + const { data } = buildRequest({ subAccount: SUB_ACCOUNT }); it('should set the correct stored request ids', () => { expect(data.ext.prebid.storedrequest.id).equal(SUB_ACCOUNT); }); diff --git a/test/spec/modules/temedyaBidAdapter_spec.js b/test/spec/modules/temedyaBidAdapter_spec.js index 971b4d4d4bb..ec84ab9c3f1 100644 --- a/test/spec/modules/temedyaBidAdapter_spec.js +++ b/test/spec/modules/temedyaBidAdapter_spec.js @@ -1,5 +1,5 @@ -import {expect} from 'chai'; -import {spec} from 'modules/temedyaBidAdapter.js'; +import { expect } from 'chai'; +import { spec } from 'modules/temedyaBidAdapter.js'; import * as utils from 'src/utils.js'; const ENDPOINT_URL = 'https://adm.vidyome.com/'; @@ -165,7 +165,7 @@ describe('temedya adapter', function() { } ]; const request = spec.buildRequests(bidRequests)[0]; - const result = spec.interpretResponse({body: response}, request); + const result = spec.interpretResponse({ body: response }, request); expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); expect(result[0].cpm).to.not.equal(null); expect(result[0].creativeId).to.not.equal(null); diff --git a/test/spec/modules/teqBlazeSalesAgentBidAdapter_spec.js b/test/spec/modules/teqBlazeSalesAgentBidAdapter_spec.js new file mode 100644 index 00000000000..f2dbe70f30d --- /dev/null +++ b/test/spec/modules/teqBlazeSalesAgentBidAdapter_spec.js @@ -0,0 +1,440 @@ +import { expect } from 'chai'; +import { spec } from '../../../modules/teqBlazeSalesAgentBidAdapter.js'; +import { BANNER, VIDEO, NATIVE } from '../../../src/mediaTypes.js'; +import { getUniqueIdentifierStr } from '../../../src/utils.js'; + +const bidder = 'teqBlazeSalesAgent'; + +describe('TeqBlazeSalesAgentBidAdapter', function () { + const userIdAsEids = [{ + source: 'test.org', + uids: [{ + id: '01**********', + atype: 1, + ext: { + third: '01***********' + } + }] + }]; + const bids = [ + { + bidId: getUniqueIdentifierStr(), + bidder: bidder, + mediaTypes: { + [BANNER]: { + sizes: [[300, 250]] + } + }, + params: { + placementId: 'testBanner', + }, + userIdAsEids + }, + { + bidId: getUniqueIdentifierStr(), + bidder: bidder, + mediaTypes: { + [VIDEO]: { + playerSize: [[300, 300]], + minduration: 5, + maxduration: 60 + } + }, + params: { + placementId: 'testVideo', + }, + userIdAsEids + }, + { + bidId: getUniqueIdentifierStr(), + bidder: bidder, + mediaTypes: { + [NATIVE]: { + native: { + title: { + required: true + }, + body: { + required: true + }, + icon: { + required: true, + size: [64, 64] + } + } + } + }, + params: { + placementId: 'testNative' + }, + userIdAsEids + } + ]; + + const invalidBid = { + bidId: getUniqueIdentifierStr(), + bidder: bidder, + mediaTypes: { + [BANNER]: { + sizes: [[300, 250]] + } + }, + params: { + + } + } + + const bidderRequest = { + uspConsent: '1---', + gdprConsent: { + consentString: 'COvFyGBOvFyGBAbAAAENAPCAAOAAAAAAAAAAAEEUACCKAAA.IFoEUQQgAIQwgIwQABAEAAAAOIAACAIAAAAQAIAgEAACEAAAAAgAQBAAAAAAAGBAAgAAAAAAAFAAECAAAgAAQARAEQAAAAAJAAIAAgAAAYQEAAAQmAgBC3ZAYzUw', + vendorData: {} + }, + refererInfo: { + referer: 'https://test.com', + page: 'https://test.com' + }, + ortb2: { + device: { + w: 1512, + h: 982, + language: 'en-UK', + }, + site: { + ext: { + data: { + scope3_aee: { + include: 'include', + exclude: 'exclude', + macro: 'macro' + } + } + } + } + }, + timeout: 500 + }; + + describe('isBidRequestValid', function () { + it('Should return true if there are bidId, params and key parameters present', function () { + expect(spec.isBidRequestValid(bids[0])).to.be.true; + }); + it('Should return false if at least one of parameters is not present', function () { + expect(spec.isBidRequestValid(invalidBid)).to.be.false; + }); + }); + + describe('buildRequests', function () { + let serverRequest = spec.buildRequests(bids, bidderRequest); + + 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 general data valid', function () { + const data = serverRequest.data; + expect(data).to.be.an('object'); + expect(data).to.have.all.keys( + 'deviceWidth', + 'deviceHeight', + 'device', + 'language', + 'secure', + 'host', + 'page', + 'placements', + 'coppa', + 'ccpa', + 'gdpr', + 'tmax', + 'bcat', + 'badv', + 'bapp', + 'battr' + ); + expect(data.deviceWidth).to.be.a('number'); + expect(data.deviceHeight).to.be.a('number'); + expect(data.language).to.be.a('string'); + expect(data.secure).to.be.within(0, 1); + expect(data.host).to.be.a('string'); + expect(data.page).to.be.a('string'); + expect(data.coppa).to.be.a('number'); + expect(data.gdpr).to.be.a('object'); + expect(data.ccpa).to.be.a('string'); + expect(data.tmax).to.be.a('number'); + expect(data.placements).to.have.lengthOf(3); + }); + + it('Returns valid placements', function () { + const { placements } = serverRequest.data; + for (let i = 0, len = placements.length; i < len; i++) { + const placement = placements[i]; + expect(placement.placementId).to.be.oneOf(['testBanner', 'testVideo', 'testNative']); + expect(placement.adFormat).to.be.oneOf([BANNER, VIDEO, NATIVE]); + expect(placement.bidId).to.be.a('string'); + expect(placement.schain).to.be.an('object'); + expect(placement.bidfloor).to.exist.and.to.equal(0); + expect(placement.type).to.exist.and.to.equal('publisher'); + expect(placement.eids).to.exist.and.to.be.deep.equal(userIdAsEids); + expect(placement.axei).to.exist.and.to.be.equal('include'); + expect(placement.axex).to.exist.and.to.be.equal('exclude'); + expect(placement.axem).to.exist.and.to.be.equal('macro'); + + if (placement.adFormat === BANNER) { + expect(placement.sizes).to.be.an('array'); + } + switch (placement.adFormat) { + case BANNER: + expect(placement.sizes).to.be.an('array'); + break; + case VIDEO: + expect(placement.playerSize).to.be.an('array'); + expect(placement.minduration).to.be.an('number'); + expect(placement.maxduration).to.be.an('number'); + break; + case NATIVE: + expect(placement.native).to.be.an('object'); + break; + } + } + }); + + it('Returns data with gdprConsent and without uspConsent', function () { + delete bidderRequest.uspConsent; + serverRequest = spec.buildRequests(bids, bidderRequest); + const data = serverRequest.data; + expect(data.gdpr).to.exist; + expect(data.gdpr).to.be.a('object'); + expect(data.gdpr).to.have.property('consentString'); + expect(data.gdpr).to.not.have.property('vendorData'); + expect(data.gdpr.consentString).to.equal(bidderRequest.gdprConsent.consentString); + expect(data.ccpa).to.not.exist; + delete bidderRequest.gdprConsent; + }); + + it('Returns data with uspConsent and without gdprConsent', function () { + bidderRequest.uspConsent = '1---'; + delete bidderRequest.gdprConsent; + serverRequest = spec.buildRequests(bids, bidderRequest); + const data = serverRequest.data; + expect(data.ccpa).to.exist; + expect(data.ccpa).to.be.a('string'); + expect(data.ccpa).to.equal(bidderRequest.uspConsent); + expect(data.gdpr).to.not.exist; + }); + }); + + describe('gpp consent', function () { + it('bidderRequest.gppConsent', () => { + bidderRequest.gppConsent = { + gppString: 'abc123', + applicableSections: [8] + }; + + const serverRequest = spec.buildRequests(bids, bidderRequest); + const data = serverRequest.data; + expect(data).to.be.an('object'); + expect(data).to.have.property('gpp'); + expect(data).to.have.property('gpp_sid'); + + delete bidderRequest.gppConsent; + }) + + it('bidderRequest.ortb2.regs.gpp', () => { + bidderRequest.ortb2 = bidderRequest.ortb2 || {}; + bidderRequest.ortb2.regs = bidderRequest.ortb2.regs || {}; + bidderRequest.ortb2.regs.gpp = 'abc123'; + bidderRequest.ortb2.regs.gpp_sid = [8]; + + const serverRequest = spec.buildRequests(bids, bidderRequest); + const data = serverRequest.data; + expect(data).to.be.an('object'); + expect(data).to.have.property('gpp'); + expect(data).to.have.property('gpp_sid'); + }) + }); + + describe('interpretResponse', function () { + it('Should interpret banner response', function () { + const banner = { + body: [{ + mediaType: 'banner', + width: 300, + height: 250, + cpm: 0.4, + ad: 'Test', + requestId: '23fhj33i987f', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1', + meta: { + advertiserDomains: ['google.com'], + advertiserId: 1234 + } + }] + }; + const bannerResponses = spec.interpretResponse(banner); + expect(bannerResponses).to.be.an('array').that.is.not.empty; + const dataItem = bannerResponses[0]; + expect(dataItem).to.have.all.keys('requestId', 'cpm', 'width', 'height', 'ad', 'ttl', 'creativeId', + 'netRevenue', 'currency', 'dealId', 'mediaType', 'meta'); + expect(dataItem.requestId).to.equal(banner.body[0].requestId); + expect(dataItem.cpm).to.equal(banner.body[0].cpm); + expect(dataItem.width).to.equal(banner.body[0].width); + expect(dataItem.height).to.equal(banner.body[0].height); + expect(dataItem.ad).to.equal(banner.body[0].ad); + expect(dataItem.ttl).to.equal(banner.body[0].ttl); + expect(dataItem.creativeId).to.equal(banner.body[0].creativeId); + expect(dataItem.netRevenue).to.be.true; + expect(dataItem.currency).to.equal(banner.body[0].currency); + expect(dataItem.meta).to.be.an('object').that.has.any.key('advertiserDomains'); + }); + it('Should interpret video response', function () { + const video = { + body: [{ + vastUrl: 'test.com', + mediaType: 'video', + cpm: 0.5, + requestId: '23fhj33i987f', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1', + meta: { + advertiserDomains: ['google.com'], + advertiserId: 1234 + } + }] + }; + const videoResponses = spec.interpretResponse(video); + expect(videoResponses).to.be.an('array').that.is.not.empty; + + const dataItem = videoResponses[0]; + expect(dataItem).to.have.all.keys('requestId', 'cpm', 'vastUrl', 'ttl', 'creativeId', + 'netRevenue', 'currency', 'dealId', 'mediaType', 'meta'); + expect(dataItem.requestId).to.equal('23fhj33i987f'); + expect(dataItem.cpm).to.equal(0.5); + expect(dataItem.vastUrl).to.equal('test.com'); + expect(dataItem.ttl).to.equal(120); + expect(dataItem.creativeId).to.equal('2'); + expect(dataItem.netRevenue).to.be.true; + expect(dataItem.currency).to.equal('USD'); + expect(dataItem.meta).to.be.an('object').that.has.any.key('advertiserDomains'); + }); + it('Should interpret native response', function () { + const native = { + body: [{ + mediaType: 'native', + native: { + clickUrl: 'test.com', + title: 'Test', + image: 'test.com', + impressionTrackers: ['test.com'], + }, + ttl: 120, + cpm: 0.4, + requestId: '23fhj33i987f', + creativeId: '2', + netRevenue: true, + currency: 'USD', + meta: { + advertiserDomains: ['google.com'], + advertiserId: 1234 + } + }] + }; + const nativeResponses = spec.interpretResponse(native); + expect(nativeResponses).to.be.an('array').that.is.not.empty; + + const dataItem = nativeResponses[0]; + expect(dataItem).to.have.keys('requestId', 'cpm', 'ttl', 'creativeId', 'netRevenue', 'currency', 'mediaType', 'native', 'meta'); + expect(dataItem.native).to.have.keys('clickUrl', 'impressionTrackers', 'title', 'image') + expect(dataItem.requestId).to.equal('23fhj33i987f'); + expect(dataItem.cpm).to.equal(0.4); + expect(dataItem.native.clickUrl).to.equal('test.com'); + expect(dataItem.native.title).to.equal('Test'); + expect(dataItem.native.image).to.equal('test.com'); + expect(dataItem.native.impressionTrackers).to.be.an('array').that.is.not.empty; + expect(dataItem.native.impressionTrackers[0]).to.equal('test.com'); + expect(dataItem.ttl).to.equal(120); + expect(dataItem.creativeId).to.equal('2'); + expect(dataItem.netRevenue).to.be.true; + expect(dataItem.currency).to.equal('USD'); + expect(dataItem.meta).to.be.an('object').that.has.any.key('advertiserDomains'); + }); + it('Should return an empty array if invalid banner response is passed', function () { + const invBanner = { + body: [{ + width: 300, + cpm: 0.4, + ad: 'Test', + requestId: '23fhj33i987f', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1' + }] + }; + + const 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', function () { + const invVideo = { + body: [{ + mediaType: 'video', + cpm: 0.5, + requestId: '23fhj33i987f', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1' + }] + }; + const 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', function () { + const invNative = { + body: [{ + mediaType: 'native', + clickUrl: 'test.com', + title: 'Test', + impressionTrackers: ['test.com'], + ttl: 120, + requestId: '23fhj33i987f', + creativeId: '2', + netRevenue: true, + currency: 'USD', + }] + }; + const serverResponses = spec.interpretResponse(invNative); + expect(serverResponses).to.be.an('array').that.is.empty; + }); + it('Should return an empty array if invalid response is passed', function () { + const invalid = { + body: [{ + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1' + }] + }; + const serverResponses = spec.interpretResponse(invalid); + expect(serverResponses).to.be.an('array').that.is.empty; + }); + }); +}); diff --git a/test/spec/modules/theAdxBidAdapter_spec.js b/test/spec/modules/theAdxBidAdapter_spec.js index fd2a306ca05..2b531b6b10b 100644 --- a/test/spec/modules/theAdxBidAdapter_spec.js +++ b/test/spec/modules/theAdxBidAdapter_spec.js @@ -416,7 +416,7 @@ describe('TheAdxAdapter', function () { expect(result).to.eql([]); }); - it('returns a valid bid response on sucessful banner request', function () { + it('returns a valid bid response on successful banner request', function () { const incomingRequestId = 'XXtestingXX'; const responsePrice = 3.14 @@ -484,7 +484,7 @@ describe('TheAdxAdapter', function () { expect(processedBid.currency).to.equal(responseCurrency); }); - it('returns a valid deal bid response on sucessful banner request with deal', function () { + it('returns a valid deal bid response on successful banner request with deal', function () { const incomingRequestId = 'XXtestingXX'; const responsePrice = 3.14 @@ -556,7 +556,7 @@ describe('TheAdxAdapter', function () { expect(processedBid.dealId).to.equal(dealId); }); - it('returns an valid bid response on sucessful video request', function () { + it('returns an valid bid response on successful video request', function () { const incomingRequestId = 'XXtesting-275XX'; const responsePrice = 6 const vast_url = 'https://theadx.com/vast?rid=a8ae0b48-a8db-4220-ba0c-7458f452b1f5&{FOR_COVARAGE}' @@ -622,7 +622,7 @@ describe('TheAdxAdapter', function () { expect(processedBid.vastUrl).to.equal(vast_url); }); - it('returns an valid bid response on sucessful native request', function () { + it('returns an valid bid response on successful native request', function () { const incomingRequestId = 'XXtesting-275XX'; const responsePrice = 6 const nurl = 'https://app.theadx.com/ixc?rid=02aefd80-2df9-11e9-896d-d33384d77f5c&time=v-1549888312715&sp=1WzMjcRpeyk%3D'; diff --git a/test/spec/modules/timeoutRtdProvider_spec.js b/test/spec/modules/timeoutRtdProvider_spec.js index 4776c52440e..fd21f39b23e 100644 --- a/test/spec/modules/timeoutRtdProvider_spec.js +++ b/test/spec/modules/timeoutRtdProvider_spec.js @@ -101,7 +101,7 @@ describe('Timeout RTD submodule', () => { } }); - const reqBidsConfigObj = {adUnits: [1, 2, 3]} + const reqBidsConfigObj = { adUnits: [1, 2, 3] } const addedTimeout = 400; const rules = { numAdUnits: { @@ -122,7 +122,7 @@ describe('Timeout RTD submodule', () => { } }); - const reqBidsConfigObj = {adUnits: [1, 2, 3]} + const reqBidsConfigObj = { adUnits: [1, 2, 3] } const addedTimeout = 400; const rules = { numAdUnits: { diff --git a/test/spec/modules/tncIdSystem_spec.js b/test/spec/modules/tncIdSystem_spec.js index c681970c27d..301c971a917 100644 --- a/test/spec/modules/tncIdSystem_spec.js +++ b/test/spec/modules/tncIdSystem_spec.js @@ -32,18 +32,18 @@ describe('TNCID tests', function () { describe('getId', () => { afterEach(function () { - Object.defineProperty(window, '__tnc', {value: undefined, configurable: true}); - Object.defineProperty(window, '__tncPbjs', {value: undefined, configurable: true}); + Object.defineProperty(window, '__tnc', { value: undefined, configurable: true }); + Object.defineProperty(window, '__tncPbjs', { value: undefined, configurable: true }); }); it('Should NOT give TNCID if GDPR applies but consent string is missing', function () { - const res = tncidSubModule.getId({}, { gdpr: {gdprApplies: true} }); + const res = tncidSubModule.getId({}, { gdpr: { gdprApplies: true } }); expect(res).to.be.undefined; }); it('Should NOT give TNCID if there is no TNC script on page and no fallback url in configuration', async function () { const completeCallback = sinon.spy(); - const {callback} = tncidSubModule.getId({}, consentData); + const { callback } = tncidSubModule.getId({}, consentData); await callback(completeCallback); expect(callback).to.be.an('function'); @@ -52,7 +52,7 @@ describe('TNCID tests', function () { it('Should NOT give TNCID if fallback script is not loaded correctly', async function () { const completeCallback = sinon.spy(); - const {callback} = tncidSubModule.getId({ + const { callback } = tncidSubModule.getId({ params: { url: 'www.thenewco.tech' } }, consentData); @@ -62,7 +62,7 @@ describe('TNCID tests', function () { it(`Should call external script if TNC is not loaded on page`, async function() { const completeCallback = sinon.spy(); - const {callback} = tncidSubModule.getId({params: {url: 'https://www.thenewco.tech?providerId=test'}}, { gdprApplies: false }); + const { callback } = tncidSubModule.getId({ params: { url: 'https://www.thenewco.tech?providerId=test' } }, { gdprApplies: false }); await callback(completeCallback); expect(window).to.contain.property('__tncPbjs'); @@ -78,7 +78,7 @@ describe('TNCID tests', function () { }); const completeCallback = sinon.spy(); - const {callback} = tncidSubModule.getId({}, { gdprApplies: false }); + const { callback } = tncidSubModule.getId({}, { gdprApplies: false }); await callback(completeCallback); expect(completeCallback.calledOnceWithExactly('TNCID_TEST_ID_1')).to.be.true; @@ -86,7 +86,7 @@ describe('TNCID tests', function () { it('TNC script with ns __tncPbjs is created', async function () { const completeCallback = sinon.spy(); - const {callback} = tncidSubModule.getId({params: {url: 'TEST_URL'}}, consentData); + const { callback } = tncidSubModule.getId({ params: { url: 'TEST_URL' } }, consentData); await callback(completeCallback); expect(window).to.contain.property('__tncPbjs'); @@ -104,7 +104,7 @@ describe('TNCID tests', function () { }); const completeCallback = sinon.spy(); - const {callback} = tncidSubModule.getId({params: {url: 'www.thenewco.tech'}}, consentData); + const { callback } = tncidSubModule.getId({ params: { url: 'www.thenewco.tech' } }, consentData); await callback(completeCallback); expect(completeCallback.calledOnceWithExactly('TNCID_TEST_ID_2')).to.be.true; diff --git a/test/spec/modules/topLevelPaapi_spec.js b/test/spec/modules/topLevelPaapi_spec.js deleted file mode 100644 index bceed8b523a..00000000000 --- a/test/spec/modules/topLevelPaapi_spec.js +++ /dev/null @@ -1,511 +0,0 @@ -import { - addPaapiConfigHook, - getPAAPIConfig, - registerSubmodule, - reset as resetPaapi -} from '../../../modules/paapi.js'; -import {config} from 'src/config.js'; -import {BID_STATUS, EVENTS} from 'src/constants.js'; -import * as events from 'src/events.js'; -import { - getPaapiAdId, - getPAAPIBids, - getRenderingDataHook, markWinningBidHook, - parsePaapiAdId, - parsePaapiSize, resizeCreativeHook, - topLevelPAAPI -} from '../../../modules/topLevelPaapi.js'; -import {auctionManager} from '../../../src/auctionManager.js'; -import {expect} from 'chai/index.js'; -import {getBidToRender} from '../../../src/adRendering.js'; - -describe('topLevelPaapi', () => { - let sandbox, auctionConfig, next, auctionId, auctions; - before(() => { - resetPaapi(); - }); - beforeEach(() => { - registerSubmodule(topLevelPAAPI); - }); - afterEach(() => { - resetPaapi(); - }); - beforeEach(() => { - sandbox = sinon.createSandbox(); - auctions = {}; - sandbox.stub(auctionManager.index, 'getAuction').callsFake(({auctionId}) => auctions[auctionId]?.auction); - next = sinon.stub(); - auctionId = 'auct'; - auctionConfig = { - seller: 'mock.seller' - }; - config.setConfig({ - paapi: { - enabled: true, - defaultForSlots: 1 - } - }); - }); - afterEach(() => { - config.resetConfig(); - sandbox.restore(); - }); - - function addPaapiConfig(adUnitCode, auctionConfig, _auctionId = auctionId) { - let auction = auctions[_auctionId]; - if (!auction) { - auction = auctions[_auctionId] = { - auction: {}, - adUnits: {} - }; - } - if (!auction.adUnits.hasOwnProperty(adUnitCode)) { - auction.adUnits[adUnitCode] = { - code: adUnitCode, - ortb2Imp: { - ext: { - paapi: { - requestedSize: { - width: 123, - height: 321 - } - } - } - } - }; - } - addPaapiConfigHook(next, {adUnitCode, auctionId: _auctionId}, { - config: { - ...auctionConfig, - auctionId: _auctionId, - adUnitCode - } - }); - } - - function endAuctions() { - Object.entries(auctions).forEach(([auctionId, {adUnits}]) => { - events.emit(EVENTS.AUCTION_END, {auctionId, adUnitCodes: Object.keys(adUnits), adUnits: Object.values(adUnits)}); - }); - } - - describe('when configured', () => { - let auctionConfig; - beforeEach(() => { - auctionConfig = { - seller: 'top.seller', - decisionLogicURL: 'https://top.seller/decision-logic.js' - }; - config.mergeConfig({ - paapi: { - topLevelSeller: { - auctionConfig, - autorun: false - } - } - }); - }); - - it('should augment config returned by getPAAPIConfig', () => { - addPaapiConfig('au', auctionConfig); - endAuctions(); - sinon.assert.match(getPAAPIConfig().au, auctionConfig); - }); - - it('should not choke if auction config is not defined', () => { - const cfg = config.getConfig('paapi'); - delete cfg.topLevelSeller.auctionConfig; - config.setConfig(cfg); - addPaapiConfig('au', auctionConfig); - endAuctions(); - expect(getPAAPIConfig().au.componentAuctions).to.exist; - }); - - it('should default resolveToConfig: false', () => { - addPaapiConfig('au', auctionConfig); - endAuctions(); - expect(getPAAPIConfig()['au'].resolveToConfig).to.eql(false); - }); - - describe('when autoRun is set', () => { - let origRaa; - beforeEach(() => { - origRaa = navigator.runAdAuction; - navigator.runAdAuction = sinon.stub(); - }); - afterEach(() => { - navigator.runAdAuction = origRaa; - }); - - it('should start auctions automatically, when autoRun is set', () => { - config.mergeConfig({ - paapi: { - topLevelSeller: { - autorun: true - } - } - }) - addPaapiConfig('au', auctionConfig); - endAuctions(); - sinon.assert.called(navigator.runAdAuction); - }); - }); - - describe('getPAAPIBids', () => { - Object.entries({ - 'a string URN': { - pack: (val) => val, - unpack: (urn) => ({urn}), - canRender: true, - }, - 'a frameConfig object': { - pack: (val) => ({val}), - unpack: (val) => ({frameConfig: {val}}), - canRender: false - } - }).forEach(([t, {pack, unpack, canRender}]) => { - describe(`when runAdAuction returns ${t}`, () => { - let raa; - beforeEach(() => { - raa = sinon.stub().callsFake((cfg) => { - const {auctionId, adUnitCode} = cfg.componentAuctions[0]; - return Promise.resolve(pack(`raa-${adUnitCode}-${auctionId}`)); - }); - }); - - function getBids(filters) { - return getPAAPIBids(filters, raa); - } - - function expectBids(actual, expected) { - expect(Object.keys(actual)).to.eql(Object.keys(expected)); - Object.entries(expected).forEach(([au, val]) => { - sinon.assert.match(actual[au], val == null ? val : { - adId: sinon.match(val => parsePaapiAdId(val)[1] === au), - width: 123, - height: 321, - source: 'paapi', - ...unpack(val) - }); - }); - } - - describe('with one auction config', () => { - beforeEach(() => { - addPaapiConfig('au', auctionConfig, 'auct'); - endAuctions(); - }); - it('should resolve to raa result', () => { - return getBids({adUnitCode: 'au', auctionId}).then(result => { - sinon.assert.calledOnce(raa); - sinon.assert.calledWith( - raa, - sinon.match({ - ...auctionConfig, - componentAuctions: sinon.match([ - sinon.match({ - ...auctionConfig, - auctionId: 'auct', - adUnitCode: 'au' - }) - ]) - }) - ); - expectBids(result, {au: 'raa-au-auct'}); - }); - }); - - Object.entries({ - 'returns null': () => Promise.resolve(), - 'throws': () => { throw new Error() }, - 'rejects': () => Promise.reject(new Error()) - }).forEach(([t, behavior]) => { - it('should resolve to null when runAdAuction returns null', () => { - raa = sinon.stub().callsFake(behavior); - return getBids({adUnitCode: 'au', auctionId: 'auct'}).then(result => { - expectBids(result, {au: null}); - }); - }); - }) - - it('should resolve to the same result when called again', () => { - getBids({adUnitCode: 'au', auctionId}); - return getBids({adUnitCode: 'au', auctionId: 'auct'}).then(result => { - sinon.assert.calledOnce(raa); - expectBids(result, {au: 'raa-au-auct'}); - }); - }); - - describe('events', () => { - beforeEach(() => { - sandbox.stub(events, 'emit'); - }); - it('should fire PAAPI_RUN_AUCTION', () => { - return Promise.all([ - getBids({adUnitCode: 'au', auctionId}), - getBids({adUnitCode: 'other', auctionId}) - ]).then(() => { - sinon.assert.calledWith(events.emit, EVENTS.RUN_PAAPI_AUCTION, { - adUnitCode: 'au', - auctionId, - auctionConfig: sinon.match(auctionConfig) - }); - sinon.assert.neverCalledWith(events.emit, EVENTS.RUN_PAAPI_AUCTION, { - adUnitCode: 'other' - }); - }); - }); - it('should fire PAAPI_BID', () => { - return getBids({adUnitCode: 'au', auctionId}).then(() => { - sinon.assert.calledWith(events.emit, EVENTS.PAAPI_BID, sinon.match({ - ...unpack('raa-au-auct'), - adUnitCode: 'au', - auctionId: 'auct' - })); - }); - }); - it('should fire PAAPI_NO_BID', () => { - raa = sinon.stub().callsFake(() => Promise.resolve(null)); - return getBids({adUnitCode: 'au', auctionId}).then(() => { - sinon.assert.calledWith(events.emit, EVENTS.PAAPI_NO_BID, sinon.match({ - adUnitCode: 'au', - auctionId: 'auct' - })); - }); - }); - - it('should fire PAAPI_ERROR', () => { - raa = sinon.stub().callsFake(() => Promise.reject(new Error('message'))); - return getBids({adUnitCode: 'au', auctionId}).then(res => { - expect(res).to.eql({au: null}); - sinon.assert.calledWith(events.emit, EVENTS.PAAPI_ERROR, sinon.match({ - adUnitCode: 'au', - auctionId: 'auct', - error: sinon.match({message: 'message'}) - })); - }); - }); - }); - - it('should hook into getBidToRender', () => { - return getBids({adUnitCode: 'au', auctionId}).then(res => { - return getBidToRender(res.au.adId).then(bidToRender => [res.au, bidToRender]) - }).then(([paapiBid, bidToRender]) => { - if (canRender) { - expect(bidToRender).to.eql(paapiBid) - } else { - expect(bidToRender).to.not.exist; - } - }); - }); - - describe('when overrideWinner is set', () => { - let mockContextual; - beforeEach(() => { - mockContextual = { - adId: 'mock', - adUnitCode: 'au' - } - sandbox.stub(auctionManager, 'findBidByAdId').returns(mockContextual); - config.mergeConfig({ - paapi: { - topLevelSeller: { - overrideWinner: true - } - } - }); - }); - - it(`should ${!canRender ? 'NOT' : ''} override winning bid for the same adUnit`, () => { - return Promise.all([ - getBids({adUnitCode: 'au', auctionId}).then(res => res.au), - getBidToRender(mockContextual.adId) - ]).then(([paapiBid, bidToRender]) => { - if (canRender) { - expect(bidToRender).to.eql(paapiBid); - expect(paapiBid.overriddenAdId).to.eql(mockContextual.adId); - } else { - expect(bidToRender).to.eql(mockContextual) - } - }) - }); - - it('should not override when the ad unit has no paapi winner', () => { - mockContextual.adUnitCode = 'other'; - return getBidToRender(mockContextual.adId).then(bidToRender => { - expect(bidToRender).to.eql(mockContextual); - }) - }); - - it('should not override when already a paapi bid', () => { - return getBids({adUnitCode: 'au', auctionId}).then(res => { - return getBidToRender(res.au.adId).then((bidToRender) => [bidToRender, res.au]); - }).then(([bidToRender, paapiBid]) => { - expect(bidToRender).to.eql(canRender ? paapiBid : mockContextual) - }) - }); - - if (canRender) { - it('should not not override when the bid was already rendered', () => { - getBids(); - return getBidToRender(mockContextual.adId).then((bid) => { - // first pass - paapi wins over contextual - expect(bid.source).to.eql('paapi'); - bid.status = BID_STATUS.RENDERED; - return getBidToRender(mockContextual.adId, false).then(bidToRender => [bid, bidToRender]) - }).then(([paapiBid, bidToRender]) => { - // if `forRender` = false (bit retrieved for x-domain events and such) - // the referenced bid is still paapi - expect(bidToRender).to.eql(paapiBid); - return getBidToRender(mockContextual.adId); - }).then(bidToRender => { - // second pass, paapi has been rendered, contextual should win - expect(bidToRender).to.eql(mockContextual); - bidToRender.status = BID_STATUS.RENDERED; - return getBidToRender(mockContextual.adId, false); - }).then(bidToRender => { - // if the contextual bid has been rendered, it's the one being referenced - expect(bidToRender).to.eql(mockContextual); - }); - }) - } - }); - }); - - it('should resolve the same result from different filters', () => { - const targets = { - auct1: ['au1', 'au2'], - auct2: ['au1', 'au3'] - }; - Object.entries(targets).forEach(([auctionId, adUnitCodes]) => { - adUnitCodes.forEach(au => addPaapiConfig(au, auctionConfig, auctionId)); - }); - endAuctions(); - return Promise.all( - [ - [ - {adUnitCode: 'au1', auctionId: 'auct1'}, - { - au1: 'raa-au1-auct1' - } - ], - [ - {}, - { - au1: 'raa-au1-auct2', - au2: 'raa-au2-auct1', - au3: 'raa-au3-auct2' - } - ], - [ - {auctionId: 'auct1'}, - { - au1: 'raa-au1-auct1', - au2: 'raa-au2-auct1' - } - ], - [ - {adUnitCode: 'au1'}, - { - au1: 'raa-au1-auct2' - } - ], - ].map(([filters, expected]) => getBids(filters).then(res => [res, expected])) - ).then(res => { - res.forEach(([actual, expected]) => { - expectBids(actual, expected); - }); - }); - }); - }); - }); - }); - }); - - describe('when not configured', () => { - it('should not alter configs returned by getPAAPIConfig', () => { - addPaapiConfig('au', auctionConfig); - endAuctions(); - expect(getPAAPIConfig().au.seller).to.not.exist; - }); - }); - - describe('paapi adId', () => { - [ - ['auctionId', 'adUnitCode'], - ['auction:id', 'adUnit:code'], - ['auction:uid', 'ad:unit'] - ].forEach(([auctionId, adUnitCode]) => { - it(`can encode and decode ${auctionId}, ${adUnitCode}`, () => { - expect(parsePaapiAdId(getPaapiAdId(auctionId, adUnitCode))).to.eql([auctionId, adUnitCode]); - }); - }); - - [undefined, null, 'not-a-paapi-ad', 'paapi:/malformed'].forEach(adId => { - it(`returns null for adId ${adId}`, () => { - expect(parsePaapiAdId(adId)).to.not.exist; - }); - }); - }); - - describe('parsePaapiSize', () => { - [ - [null, null], - [undefined, null], - [123, 123], - ['123', 123], - ['123px', 123], - ['1sw', null], - ['garbage', null] - ].forEach(([input, expected]) => { - it(`can parse ${input} => ${expected}`, () => { - expect(parsePaapiSize(input)).to.eql(expected); - }); - }); - }); - - describe('rendering hooks', () => { - let next; - beforeEach(() => { - next = sinon.stub() - next.bail = sinon.stub() - }); - describe('getRenderingDataHook', () => { - it('intercepts paapi bids', () => { - getRenderingDataHook(next, { - source: 'paapi', - width: 123, - height: null, - urn: 'url' - }); - sinon.assert.calledWith(next.bail, { - width: 123, - height: null, - adUrl: 'url' - }); - }); - it('does not touch non-paapi bids', () => { - getRenderingDataHook(next, {bid: 'data'}, {other: 'options'}); - sinon.assert.calledWith(next, {bid: 'data'}, {other: 'options'}); - }); - }); - - describe('markWinnigBidsHook', () => { - beforeEach(() => { - sandbox.stub(events, 'emit'); - }); - it('handles paapi bids', () => { - const bid = {source: 'paapi'}; - markWinningBidHook(next, bid); - sinon.assert.notCalled(next); - sinon.assert.called(next.bail); - sinon.assert.calledWith(events.emit, EVENTS.BID_WON, bid); - }); - it('ignores non-paapi bids', () => { - markWinningBidHook(next, {other: 'bid'}); - sinon.assert.calledWith(next, {other: 'bid'}); - sinon.assert.notCalled(next.bail); - }); - }); - }); -}); diff --git a/test/spec/modules/topicsFpdModule_spec.js b/test/spec/modules/topicsFpdModule_spec.js index 47838ecdde1..1d8fbbad445 100644 --- a/test/spec/modules/topicsFpdModule_spec.js +++ b/test/spec/modules/topicsFpdModule_spec.js @@ -8,12 +8,12 @@ import { reset, topicStorageName } from '../../../modules/topicsFpdModule.js'; -import {config} from 'src/config.js'; -import {deepClone, safeJSONParse} from '../../../src/utils.js'; -import {getCoreStorageManager} from 'src/storageManager.js'; +import { config } from 'src/config.js'; +import { deepClone, safeJSONParse } from '../../../src/utils.js'; +import { getCoreStorageManager } from 'src/storageManager.js'; import * as activities from '../../../src/activities/rules.js'; -import {registerActivityControl} from '../../../src/activities/rules.js'; -import {ACTIVITY_ENRICH_UFPD} from '../../../src/activities/activities.js'; +import { registerActivityControl } from '../../../src/activities/rules.js'; +import { ACTIVITY_ENRICH_UFPD } from '../../../src/activities/activities.js'; describe('topics', () => { let unregister, enrichUfpdRule; @@ -25,7 +25,7 @@ describe('topics', () => { }); beforeEach(() => { - enrichUfpdRule = () => ({allow: true}); + enrichUfpdRule = () => ({ allow: true }); reset(); }); @@ -61,7 +61,7 @@ describe('topics', () => { segclass: 'm1' }, segment: [ - {id: '123'} + { id: '123' } ] } ] @@ -76,8 +76,8 @@ describe('topics', () => { segclass: 'm1' }, segment: [ - {id: '123'}, - {id: '321'} + { id: '123' }, + { id: '321' } ] } ] @@ -92,8 +92,8 @@ describe('topics', () => { segclass: 'm1' }, segment: [ - {id: '1'}, - {id: '2'} + { id: '1' }, + { id: '2' } ] }, { @@ -102,7 +102,7 @@ describe('topics', () => { segclass: 'm2' }, segment: [ - {id: '3'} + { id: '3' } ] } ] @@ -117,7 +117,7 @@ describe('topics', () => { segclass: 'm1' }, segment: [ - {id: '123'} + { id: '123' } ] } ] @@ -140,7 +140,7 @@ describe('topics', () => { segclass: 'm1' }, segment: [ - {id: '123'} + { id: '123' } ] }, { @@ -149,7 +149,7 @@ describe('topics', () => { segclass: 'm1', }, segment: [ - {id: '321'} + { id: '321' } ] }, { @@ -158,12 +158,12 @@ describe('topics', () => { segclass: 'm2' }, segment: [ - {id: '213'} + { id: '213' } ] } ] } - ].forEach(({t, topics, expected, taxonomies}) => { + ].forEach(({ t, topics, expected, taxonomies }) => { describe(`on ${t}`, () => { it('should convert topics to user.data segments correctly', () => { const actual = getTopicsData('mockName', topics, taxonomies); @@ -231,17 +231,17 @@ describe('topics', () => { const mockData = [ { name: 'domain', - segment: [{id: 123}] + segment: [{ id: 123 }] }, { name: 'domain', - segment: [{id: 321}] + segment: [{ id: 321 }] } ]; it('should add topics data', () => { - return processFpd({}, {global: {}}, {data: Promise.resolve(mockData)}) - .then(({global}) => { + return processFpd({}, { global: {} }, { data: Promise.resolve(mockData) }) + .then(({ global }) => { expect(global.user.data).to.eql(mockData); }); }); @@ -250,18 +250,18 @@ describe('topics', () => { const global = { user: { data: [ - {name: 'preexisting'}, + { name: 'preexisting' }, ] } }; - return processFpd({}, {global: deepClone(global)}, {data: Promise.resolve(mockData)}) + return processFpd({}, { global: deepClone(global) }, { data: Promise.resolve(mockData) }) .then((data) => { expect(data.global.user.data).to.eql(global.user.data.concat(mockData)); }); }); it('should not modify fpd when there is no data', () => { - return processFpd({}, {global: {}}, {data: Promise.resolve([])}) + return processFpd({}, { global: {} }, { data: Promise.resolve([]) }) .then((data) => { expect(data.global).to.eql({}); }); @@ -304,9 +304,9 @@ describe('topics', () => { }); it('does not load frames when accessDevice is not allowed', () => { - enrichUfpdRule = ({component}) => { + enrichUfpdRule = ({ component }) => { if (component === 'bidder.mockBidder') { - return {allow: false} + return { allow: false } } } const doc = { @@ -358,7 +358,7 @@ describe('topics', () => { }); it('should return no segments when not configured', () => { - config.setConfig({userSync: {}}); + config.setConfig({ userSync: {} }); expect(getCachedTopics()).to.eql([]); }) @@ -367,8 +367,8 @@ describe('topics', () => { const storedSegments = JSON.stringify( [['pubmatic', { '2206021246': { - 'ext': {'segtax': 600, 'segclass': '2206021246'}, - 'segment': [{'id': '243'}, {'id': '265'}], + 'ext': { 'segtax': 600, 'segclass': '2206021246' }, + 'segment': [{ 'id': '243' }, { 'id': '265' }], 'name': 'ads.pubmatic.com' }, 'lastUpdated': new Date().getTime() @@ -393,7 +393,7 @@ describe('topics', () => { }); it('should NOT return segments for bidder if enrichUfpd is NOT allowed', () => { - enrichUfpdRule = (params) => ({allow: params.component !== 'bidder.pubmatic'}) + enrichUfpdRule = (params) => ({ allow: params.component !== 'bidder.pubmatic' }) expect(getCachedTopics()).to.eql([]); }); }); @@ -429,7 +429,7 @@ describe('topics', () => { featurePolicy: { allowsFeature() { return true } }, - createElement: sinon.stub().callsFake(() => ({style: {}})), + createElement: sinon.stub().callsFake(() => ({ style: {} })), documentElement: { appendChild() {} } @@ -524,8 +524,8 @@ describe('topics', () => { const storedSegments = JSON.stringify( [['pubmatic', { '2206021246': { - 'ext': {'segtax': 600, 'segclass': '2206021246'}, - 'segment': [{'id': '243'}, {'id': '265'}], + 'ext': { 'segtax': 600, 'segclass': '2206021246' }, + 'segment': [{ 'id': '243' }, { 'id': '265' }], 'name': 'ads.pubmatic.com' }, 'lastUpdated': new Date().getTime() diff --git a/test/spec/modules/tpmnBidAdapter_spec.js b/test/spec/modules/tpmnBidAdapter_spec.js index e099d9d5911..46cc6513343 100644 --- a/test/spec/modules/tpmnBidAdapter_spec.js +++ b/test/spec/modules/tpmnBidAdapter_spec.js @@ -1,11 +1,11 @@ -import {spec, storage, VIDEO_RENDERER_URL} from 'modules/tpmnBidAdapter.js'; -import {generateUUID} from '../../../src/utils.js'; -import {expect} from 'chai'; +import { spec, storage, VIDEO_RENDERER_URL } from 'modules/tpmnBidAdapter.js'; +import { generateUUID } from '../../../src/utils.js'; +import { expect } from 'chai'; import * as utils from 'src/utils'; import * as sinon from 'sinon'; import 'modules/consentManagementTcf.js'; -import {addFPDToBidderRequest} from '../../helpers/fpd.js'; -import {getGlobal} from '../../../src/prebidGlobal.js'; +import { addFPDToBidderRequest } from '../../helpers/fpd.js'; +import { getGlobal } from '../../../src/prebidGlobal.js'; const BIDDER_CODE = 'tpmn'; const BANNER_BID = { @@ -262,7 +262,7 @@ describe('tpmnAdapterTests', function () { expect(spec.isBidRequestValid(bid)).to.equal(true); const requests = spec.buildRequests([bid], BIDDER_REQUEST); const request = requests[0].data; - expect(request.imp[0].video).to.deep.include({...check}); + expect(request.imp[0].video).to.deep.include({ ...check }); }); } @@ -270,8 +270,8 @@ describe('tpmnAdapterTests', function () { it('when mediaType New Video', () => { const NEW_VIDEO_BID = { 'bidder': 'tpmn', - 'params': {'inventoryId': 2, 'bidFloor': 2}, - 'userId': {'pubcid': '88a49ee6-beeb-4dd6-92ac-3b6060e127e1'}, + 'params': { 'inventoryId': 2, 'bidFloor': 2 }, + 'userId': { 'pubcid': '88a49ee6-beeb-4dd6-92ac-3b6060e127e1' }, 'mediaTypes': { 'video': { 'context': 'outstream', @@ -293,7 +293,7 @@ describe('tpmnAdapterTests', function () { const check = { w: 1024, h: 768, - mimes: [ 'video/mp4' ], + mimes: ['video/mp4'], playbackmethod: [2, 4, 6], api: [1, 2, 3, 6], protocols: [3, 4], @@ -340,14 +340,14 @@ describe('tpmnAdapterTests', function () { h: 480 }; - bid.mediaTypes.video = {...check}; + bid.mediaTypes.video = { ...check }; bid.mediaTypes.video.context = 'instream'; bid.mediaTypes.video.playerSize = [[640, 480]]; expect(spec.isBidRequestValid(bid)).to.equal(true); const requests = spec.buildRequests([bid], BIDDER_REQUEST); const request = requests[0].data; - expect(request.imp[0].video).to.deep.include({...check}); + expect(request.imp[0].video).to.deep.include({ ...check }); }); } }); @@ -430,7 +430,7 @@ describe('tpmnAdapterTests', function () { }); it('case 1 -> allow iframe', () => { - const syncs = spec.getUserSyncs({iframeEnabled: true, pixelEnabled: true}); + const syncs = spec.getUserSyncs({ iframeEnabled: true, pixelEnabled: true }); expect(syncs.length).to.equal(1); expect(syncs[0].type).to.equal('iframe'); }); diff --git a/test/spec/modules/trafficgateBidAdapter_spec.js b/test/spec/modules/trafficgateBidAdapter_spec.js index 27550b2cd20..e5897d077ab 100644 --- a/test/spec/modules/trafficgateBidAdapter_spec.js +++ b/test/spec/modules/trafficgateBidAdapter_spec.js @@ -1,8 +1,8 @@ -import {expect} from 'chai'; -import {spec} from 'modules/trafficgateBidAdapter'; -import {newBidder} from 'src/adapters/bidderFactory.js'; -import {BANNER, VIDEO} from 'src/mediaTypes.js'; -import {config} from 'src/config.js'; +import { expect } from 'chai'; +import { spec } from 'modules/trafficgateBidAdapter'; +import { newBidder } from 'src/adapters/bidderFactory.js'; +import { BANNER, VIDEO } from 'src/mediaTypes.js'; +import { config } from 'src/config.js'; import * as utils from 'src/utils.js'; import * as dnt from 'libraries/dnt/index.js'; import 'src/prebid.js' @@ -12,11 +12,10 @@ import 'modules/multibid/index.js'; import 'modules/priceFloors.js'; import 'modules/consentManagementTcf.js'; import 'modules/consentManagementUsp.js'; -import 'modules/paapi.js'; -import {deepClone} from 'src/utils.js'; -import {addFPDToBidderRequest} from '../../helpers/fpd.js'; -import {hook} from '../../../src/hook.js'; +import { deepClone } from 'src/utils.js'; +import { addFPDToBidderRequest } from '../../helpers/fpd.js'; +import { hook } from '../../../src/hook.js'; const BidRequestBuilder = function BidRequestBuilder(options) { const defaults = { @@ -90,7 +89,7 @@ describe('TrafficgateOpenxRtbAdapter', function () { bidder: 'trafficgate', params: {}, adUnitCode: 'adunit-code', - mediaTypes: {banner: {}}, + mediaTypes: { banner: {} }, sizes: [[300, 250], [300, 600]], bidId: '30b31c1838de1e', bidderRequestId: '22edbae2733bf6', @@ -99,13 +98,13 @@ describe('TrafficgateOpenxRtbAdapter', function () { }); it('should return false when there is placementId only', function () { - bannerBid.params = {'placementId': '98765'}; + bannerBid.params = { 'placementId': '98765' }; expect(spec.isBidRequestValid(bannerBid)).to.equal(false); }); describe('should return false when there is a host only', function () { beforeEach(function () { - bannerBid.params = {host: 'test-delivery-domain'} + bannerBid.params = { host: 'test-delivery-domain' } }); it('should return false when there is no placementId and size', function () { @@ -267,7 +266,7 @@ describe('TrafficgateOpenxRtbAdapter', function () { let mockBidderRequest; beforeEach(function () { - mockBidderRequest = {refererInfo: {}}; + mockBidderRequest = { refererInfo: {} }; bidRequestsWithMediaTypes = [{ bidder: 'trafficgate', @@ -412,7 +411,7 @@ describe('TrafficgateOpenxRtbAdapter', function () { describe('FPD', function() { let bidRequests; - const mockBidderRequest = {refererInfo: {}}; + const mockBidderRequest = { refererInfo: {} }; beforeEach(function () { bidRequests = [{ @@ -629,27 +628,28 @@ describe('TrafficgateOpenxRtbAdapter', function () { describe('with user agent client hints', function () { it('should add device.sua if available', function () { - const bidderRequestWithUserAgentClientHints = { refererInfo: {}, + const bidderRequestWithUserAgentClientHints = { + refererInfo: {}, ortb2: { device: { sua: { source: 2, platform: { brand: 'macOS', - version: [ '12', '4', '0' ] + version: ['12', '4', '0'] }, browsers: [ { brand: 'Chromium', - version: [ '106', '0', '5249', '119' ] + version: ['106', '0', '5249', '119'] }, { brand: 'Google Chrome', - version: [ '106', '0', '5249', '119' ] + version: ['106', '0', '5249', '119'] }, { brand: 'Not;A=Brand', - version: [ '99', '0', '0', '0' ] + version: ['99', '0', '0', '0'] }], mobile: 0, model: 'Pro', @@ -657,12 +657,13 @@ describe('TrafficgateOpenxRtbAdapter', function () { architecture: 'x86' } } - }}; + } + }; let request = spec.buildRequests(bidRequests, bidderRequestWithUserAgentClientHints); expect(request[0].data.device.sua).to.exist; expect(request[0].data.device.sua).to.deep.equal(bidderRequestWithUserAgentClientHints.ortb2.device.sua); - const bidderRequestWithoutUserAgentClientHints = {refererInfo: {}, ortb2: {}}; + const bidderRequestWithoutUserAgentClientHints = { refererInfo: {}, ortb2: {} }; request = spec.buildRequests(bidRequests, bidderRequestWithoutUserAgentClientHints); expect(request[0].data.device?.sua).to.not.exist; }); @@ -838,7 +839,7 @@ describe('TrafficgateOpenxRtbAdapter', function () { it('should send a coppa flag there is when there is coppa param settings in the bid params', async function () { const request = spec.buildRequests(bidRequestsWithMediaTypes, await addFPDToBidderRequest(mockBidderRequest)); - request.params = {coppa: true}; + request.params = { coppa: true }; expect(request[0].data.regs.coppa).to.equal(1); }); @@ -847,38 +848,6 @@ describe('TrafficgateOpenxRtbAdapter', function () { }); }); - context('do not track (DNT)', function() { - let doNotTrackStub; - - beforeEach(function () { - doNotTrackStub = sinon.stub(dnt, 'getDNT'); - }); - afterEach(function() { - doNotTrackStub.restore(); - }); - - it('when there is a do not track, should send a dnt', async function () { - doNotTrackStub.returns(1); - - const request = spec.buildRequests(bidRequestsWithMediaTypes, await addFPDToBidderRequest(mockBidderRequest)); - expect(request[0].data.device.dnt).to.equal(1); - }); - - it('when there is not do not track, don\'t send dnt', async function () { - doNotTrackStub.returns(0); - - const request = spec.buildRequests(bidRequestsWithMediaTypes, await addFPDToBidderRequest(mockBidderRequest)); - expect(request[0].data.device.dnt).to.equal(0); - }); - - it('when there is no defined do not track, don\'t send dnt', async function () { - doNotTrackStub.returns(null); - - const request = spec.buildRequests(bidRequestsWithMediaTypes, await addFPDToBidderRequest(mockBidderRequest)); - expect(request[0].data.device.dnt).to.equal(0); - }); - }); - context('supply chain (schain)', function () { let bidRequests; let schainConfig; @@ -932,15 +901,17 @@ describe('TrafficgateOpenxRtbAdapter', function () { bidId: 'test-bid-id-1', bidderRequestId: 'test-bid-request-1', auctionId: 'test-auction-1', - ortb2: {source: { - ext: {schain: schainConfig} - }} + ortb2: { + source: { + ext: { schain: schainConfig } + } + } }]; // Add schain to mockBidderRequest as well mockBidderRequest.ortb2 = { source: { - ext: {schain: schainConfig} + ext: { schain: schainConfig } } }; }); @@ -1012,7 +983,7 @@ describe('TrafficgateOpenxRtbAdapter', function () { auctionId: 'test-auction-1', }]; // enrich bid request with userId key/value - mockBidderRequest.ortb2 = {user: {ext: {eids: userIdAsEids}}} + mockBidderRequest.ortb2 = { user: { ext: { eids: userIdAsEids } } } const request = spec.buildRequests(bidRequestsWithUserId, mockBidderRequest); expect(request[0].data.user.ext.eids).to.eql(userIdAsEids); }); @@ -1118,10 +1089,10 @@ describe('TrafficgateOpenxRtbAdapter', function () { auctionId: 'test-auction-id' }]; - bidRequest = spec.buildRequests(bidRequestConfigs, {refererInfo: {}})[0]; + bidRequest = spec.buildRequests(bidRequestConfigs, { refererInfo: {} })[0]; - bidResponse = {nbr: 0}; // Unknown error - bids = spec.interpretResponse({body: bidResponse}, bidRequest); + bidResponse = { nbr: 0 }; // Unknown error + bids = spec.interpretResponse({ body: bidResponse }, bidRequest); }); it('should not return any bids', function () { @@ -1149,10 +1120,10 @@ describe('TrafficgateOpenxRtbAdapter', function () { auctionId: 'test-auction-id' }]; - bidRequest = spec.buildRequests(bidRequestConfigs, {refererInfo: {}})[0]; + bidRequest = spec.buildRequests(bidRequestConfigs, { refererInfo: {} })[0]; - bidResponse = {ext: {}, id: 'test-bid-id'}; - bids = spec.interpretResponse({body: bidResponse}, bidRequest); + bidResponse = { ext: {}, id: 'test-bid-id' }; + bids = spec.interpretResponse({ body: bidResponse }, bidRequest); }); it('should not return any bids', function () { @@ -1180,10 +1151,10 @@ describe('TrafficgateOpenxRtbAdapter', function () { auctionId: 'test-auction-id' }]; - bidRequest = spec.buildRequests(bidRequestConfigs, {refererInfo: {}})[0]; + bidRequest = spec.buildRequests(bidRequestConfigs, { refererInfo: {} })[0]; bidResponse = ''; // Unknown error - bids = spec.interpretResponse({body: bidResponse}, bidRequest); + bids = spec.interpretResponse({ body: bidResponse }, bidRequest); }); it('should not return any bids', function () { @@ -1231,10 +1202,10 @@ describe('TrafficgateOpenxRtbAdapter', function () { context('when there is a response, the common response properties', function () { beforeEach(function () { bidRequestConfigs = deepClone(SAMPLE_BID_REQUESTS); - bidRequest = spec.buildRequests(bidRequestConfigs, {refererInfo: {}})[0]; + bidRequest = spec.buildRequests(bidRequestConfigs, { refererInfo: {} })[0]; bidResponse = deepClone(SAMPLE_BID_RESPONSE); - bid = spec.interpretResponse({body: bidResponse}, bidRequest)[0]; + bid = spec.interpretResponse({ body: bidResponse }, bidRequest)[0]; }); it('should return a price', function () { @@ -1302,7 +1273,7 @@ describe('TrafficgateOpenxRtbAdapter', function () { auctionId: 'test-auction-id' }]; - bidRequest = spec.buildRequests(bidRequestConfigs, {refererInfo: {}})[0]; + bidRequest = spec.buildRequests(bidRequestConfigs, { refererInfo: {} })[0]; bidResponse = { seatbid: [{ @@ -1319,7 +1290,7 @@ describe('TrafficgateOpenxRtbAdapter', function () { cur: 'AUS' }; - bid = spec.interpretResponse({body: bidResponse}, bidRequest)[0]; + bid = spec.interpretResponse({ body: bidResponse }, bidRequest)[0]; }); it('should return the proper mediaType', function () { @@ -1349,7 +1320,7 @@ describe('TrafficgateOpenxRtbAdapter', function () { auctionId: 'test-auction-id' }]; - bidRequest = spec.buildRequests(bidRequestConfigs, {refererInfo: {}})[0]; + bidRequest = spec.buildRequests(bidRequestConfigs, { refererInfo: {} })[0]; bidResponse = { seatbid: [{ @@ -1368,14 +1339,14 @@ describe('TrafficgateOpenxRtbAdapter', function () { }); it('should return the proper mediaType', function () { - bid = spec.interpretResponse({body: bidResponse}, bidRequest)[0]; + bid = spec.interpretResponse({ body: bidResponse }, bidRequest)[0]; expect(bid.mediaType).to.equal(Object.keys(bidRequestConfigs[0].mediaTypes)[0]); }); it('should return the proper mediaType', function () { const winUrl = 'https//my.win.url'; bidResponse.seatbid[0].bid[0].nurl = winUrl - bid = spec.interpretResponse({body: bidResponse}, bidRequest)[0]; + bid = spec.interpretResponse({ body: bidResponse }, bidRequest)[0]; expect(bid.vastUrl).to.equal(winUrl); }); diff --git a/test/spec/modules/trionBidAdapter_spec.js b/test/spec/modules/trionBidAdapter_spec.js index 306cacc2487..713447d1bd6 100644 --- a/test/spec/modules/trionBidAdapter_spec.js +++ b/test/spec/modules/trionBidAdapter_spec.js @@ -1,8 +1,8 @@ -import {expect} from 'chai'; +import { expect } from 'chai'; import * as utils from 'src/utils.js'; -import {spec, acceptPostMessage, getStorageData, setStorageData} from 'modules/trionBidAdapter.js'; -import {deepClone} from 'src/utils.js'; -import {getGlobal} from '../../../src/prebidGlobal.js'; +import { spec, acceptPostMessage, getStorageData, setStorageData } from 'modules/trionBidAdapter.js'; +import { deepClone } from 'src/utils.js'; +import { getGlobal } from '../../../src/prebidGlobal.js'; const CONSTANTS = require('src/constants.js'); const adloader = require('src/adloader'); @@ -278,13 +278,13 @@ describe('Trion adapter tests', function () { describe('interpretResponse', function () { it('when there is no response do not bid', function () { - const response = spec.interpretResponse(null, {bidRequest: TRION_BID}); + const response = spec.interpretResponse(null, { bidRequest: TRION_BID }); expect(response).to.deep.equal([]); }); it('when place bid is returned as false', function () { TRION_BID_RESPONSE.result.placeBid = false; - const response = spec.interpretResponse({body: TRION_BID_RESPONSE}, {bidRequest: TRION_BID}); + const response = spec.interpretResponse({ body: TRION_BID_RESPONSE }, { bidRequest: TRION_BID }); expect(response).to.deep.equal([]); @@ -293,14 +293,14 @@ describe('Trion adapter tests', function () { it('when no cpm is in the response', function () { TRION_BID_RESPONSE.result.cpm = 0; - const response = spec.interpretResponse({body: TRION_BID_RESPONSE}, {bidRequest: TRION_BID}); + const 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', function () { TRION_BID_RESPONSE.result.ad = null; - const response = spec.interpretResponse({body: TRION_BID_RESPONSE}, {bidRequest: TRION_BID}); + const response = spec.interpretResponse({ body: TRION_BID_RESPONSE }, { bidRequest: TRION_BID }); expect(response).to.deep.equal([]); TRION_BID_RESPONSE.result.ad = 'test'; }); @@ -310,7 +310,7 @@ describe('Trion adapter tests', function () { const bidHeight = '2'; TRION_BID_RESPONSE.result.width = bidWidth; TRION_BID_RESPONSE.result.height = bidHeight; - const response = spec.interpretResponse({body: TRION_BID_RESPONSE}, {bidRequest: TRION_BID}); + const response = spec.interpretResponse({ body: TRION_BID_RESPONSE }, { bidRequest: TRION_BID }); expect(response[0].width).to.equal(bidWidth); expect(response[0].height).to.equal(bidHeight); TRION_BID_RESPONSE.result.width = '300'; @@ -320,14 +320,14 @@ describe('Trion adapter tests', function () { it('cpm is properly set and transformed to cents', function () { const bidCpm = 2; TRION_BID_RESPONSE.result.cpm = bidCpm * 100; - const response = spec.interpretResponse({body: TRION_BID_RESPONSE}, {bidRequest: TRION_BID}); + const response = spec.interpretResponse({ body: TRION_BID_RESPONSE }, { bidRequest: TRION_BID }); expect(response[0].cpm).to.equal(bidCpm); TRION_BID_RESPONSE.result.cpm = 100; }); it('advertiserDomains is included when sent by server', function () { TRION_BID_RESPONSE.result.adomain = ['test_adomain']; - const response = spec.interpretResponse({body: TRION_BID_RESPONSE}, {bidRequest: TRION_BID}); + const response = spec.interpretResponse({ body: TRION_BID_RESPONSE }, { bidRequest: TRION_BID }); expect(Object.keys(response[0].meta)).to.include.members(['advertiserDomains']); expect(response[0].meta.advertiserDomains).to.deep.equal(['test_adomain']); delete TRION_BID_RESPONSE.result.adomain; @@ -352,12 +352,12 @@ describe('Trion adapter tests', function () { }); it('should register trion user script', function () { - const syncs = spec.getUserSyncs({iframeEnabled: true}); + const syncs = spec.getUserSyncs({ iframeEnabled: true }); const pageUrl = getPublisherUrl(); const pubId = 1; const sectionId = 2; const syncString = `?p=${pubId}&s=${sectionId}&u=${pageUrl}`; - expect(syncs[0]).to.deep.equal({type: 'iframe', url: USER_SYNC_URL + syncString}); + expect(syncs[0]).to.deep.equal({ type: 'iframe', url: USER_SYNC_URL + syncString }); }); it('should register trion user script with gdpr params', function () { @@ -365,31 +365,31 @@ describe('Trion adapter tests', function () { consentString: 'test_gdpr_str', gdprApplies: true }; - const syncs = spec.getUserSyncs({iframeEnabled: true}, null, gdprConsent); + const syncs = spec.getUserSyncs({ iframeEnabled: true }, null, gdprConsent); const pageUrl = getPublisherUrl(); const pubId = 1; const sectionId = 2; const gcEncoded = encodeURIComponent(gdprConsent.consentString); const syncString = `?p=${pubId}&s=${sectionId}&gc=${gcEncoded}&g=1&u=${pageUrl}`; - expect(syncs[0]).to.deep.equal({type: 'iframe', url: USER_SYNC_URL + syncString}); + expect(syncs[0]).to.deep.equal({ type: 'iframe', url: USER_SYNC_URL + syncString }); }); it('should register trion user script with us privacy params', function () { const uspConsent = '1YYY'; - const syncs = spec.getUserSyncs({iframeEnabled: true}, null, null, uspConsent); + const syncs = spec.getUserSyncs({ iframeEnabled: true }, null, null, uspConsent); const pageUrl = getPublisherUrl(); const pubId = 1; const sectionId = 2; const uspEncoded = encodeURIComponent(uspConsent); const syncString = `?p=${pubId}&s=${sectionId}&up=${uspEncoded}&u=${pageUrl}`; - expect(syncs[0]).to.deep.equal({type: 'iframe', url: USER_SYNC_URL + syncString}); + expect(syncs[0]).to.deep.equal({ type: 'iframe', url: USER_SYNC_URL + syncString }); }); it('should except posted messages from user sync script', function () { const testId = 'testId'; const message = BASE_KEY + 'userId=' + testId; setStorageData(BASE_KEY + 'int_t', null); - acceptPostMessage({data: message}); + acceptPostMessage({ data: message }); const newKey = getStorageData(BASE_KEY + 'int_t'); expect(newKey).to.equal(testId); }); @@ -399,7 +399,7 @@ describe('Trion adapter tests', function () { const badId = 'badId'; const message = 'Not Trion: userId=' + testId; setStorageData(BASE_KEY + 'int_t', badId); - acceptPostMessage({data: message}); + acceptPostMessage({ data: message }); const newKey = getStorageData(BASE_KEY + 'int_t'); expect(newKey).to.equal(badId); }); diff --git a/test/spec/modules/tripleliftBidAdapter_spec.js b/test/spec/modules/tripleliftBidAdapter_spec.js index 19c537e4da3..e9f205b6745 100644 --- a/test/spec/modules/tripleliftBidAdapter_spec.js +++ b/test/spec/modules/tripleliftBidAdapter_spec.js @@ -5,7 +5,7 @@ import { deepClone } from 'src/utils.js'; import { config } from 'src/config.js'; import prebid from 'package.json'; import * as utils from 'src/utils.js'; -import {getGlobal} from '../../../src/prebidGlobal.js'; +import { getGlobal } from '../../../src/prebidGlobal.js'; const ENDPOINT = 'https://tlx.3lift.com/header/auction?'; const GDPR_CONSENT_STR = 'BOONm0NOONm0NABABAENAa-AAAARh7______b9_3__7_9uz_Kv_K7Vf7nnG072lPVA9LTOQ6gEaY'; @@ -645,7 +645,7 @@ describe('triplelift adapter', function () { 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}]); + 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 () { @@ -654,7 +654,7 @@ describe('triplelift adapter', function () { 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}]); + expect(payload.imp[0].banner.format).to.deep.equal([{ w: 300, h: 250 }, { w: 300, h: 600 }]); // instream expect(payload.imp[1].tagid).to.equal('insteam_test'); expect(payload.imp[1].floor).to.equal(1.0); @@ -663,16 +663,16 @@ describe('triplelift adapter', function () { // banner and outstream video expect(payload.imp[2]).to.have.property('video'); expect(payload.imp[2]).to.have.property('banner'); - expect(payload.imp[2].banner.format).to.deep.equal([{w: 300, h: 250}, {w: 300, h: 600}]); - expect(payload.imp[2].video).to.deep.equal({'mimes': ['video/mp4'], 'maxduration': 30, 'minduration': 6, 'w': 640, 'h': 480, 'context': 'outstream'}); + expect(payload.imp[2].banner.format).to.deep.equal([{ w: 300, h: 250 }, { w: 300, h: 600 }]); + expect(payload.imp[2].video).to.deep.equal({ 'mimes': ['video/mp4'], 'maxduration': 30, 'minduration': 6, 'w': 640, 'h': 480, 'context': 'outstream' }); // banner and incomplete video expect(payload.imp[3]).to.not.have.property('video'); expect(payload.imp[3]).to.have.property('banner'); - expect(payload.imp[3].banner.format).to.deep.equal([{w: 300, h: 250}, {w: 300, h: 600}]); + expect(payload.imp[3].banner.format).to.deep.equal([{ w: 300, h: 250 }, { w: 300, h: 600 }]); // incomplete mediatypes.banner and incomplete video expect(payload.imp[4]).to.not.have.property('video'); expect(payload.imp[4]).to.have.property('banner'); - expect(payload.imp[4].banner.format).to.deep.equal([{w: 300, h: 250}, {w: 300, h: 600}]); + expect(payload.imp[4].banner.format).to.deep.equal([{ w: 300, h: 250 }, { w: 300, h: 600 }]); // banner and instream video expect(payload.imp[5]).to.not.have.property('banner'); expect(payload.imp[5]).to.have.property('video'); @@ -681,20 +681,20 @@ describe('triplelift adapter', function () { // banner and outream video and native expect(payload.imp[6]).to.have.property('video'); expect(payload.imp[6]).to.have.property('banner'); - expect(payload.imp[6].banner.format).to.deep.equal([{w: 300, h: 250}, {w: 300, h: 600}]); - expect(payload.imp[6].video).to.deep.equal({'mimes': ['video/mp4'], 'maxduration': 30, 'minduration': 6, 'w': 640, 'h': 480, 'context': 'outstream'}); + expect(payload.imp[6].banner.format).to.deep.equal([{ w: 300, h: 250 }, { w: 300, h: 600 }]); + expect(payload.imp[6].video).to.deep.equal({ 'mimes': ['video/mp4'], 'maxduration': 30, 'minduration': 6, 'w': 640, 'h': 480, 'context': 'outstream' }); // outstream video only expect(payload.imp[7]).to.have.property('video'); expect(payload.imp[7]).to.not.have.property('banner'); - expect(payload.imp[7].video).to.deep.equal({'mimes': ['video/mp4'], 'maxduration': 30, 'minduration': 6, 'w': 640, 'h': 480, 'context': 'outstream'}); + expect(payload.imp[7].video).to.deep.equal({ 'mimes': ['video/mp4'], 'maxduration': 30, 'minduration': 6, 'w': 640, 'h': 480, 'context': 'outstream' }); // banner and incomplete outstream (missing size); video request is permitted so banner can still monetize expect(payload.imp[8]).to.have.property('video'); expect(payload.imp[8]).to.have.property('banner'); - expect(payload.imp[8].banner.format).to.deep.equal([{w: 300, h: 250}, {w: 300, h: 600}]); - expect(payload.imp[8].video).to.deep.equal({'mimes': ['video/mp4'], 'maxduration': 30, 'minduration': 6, 'context': 'outstream'}); + expect(payload.imp[8].banner.format).to.deep.equal([{ w: 300, h: 250 }, { w: 300, h: 600 }]); + expect(payload.imp[8].video).to.deep.equal({ 'mimes': ['video/mp4'], 'maxduration': 30, 'minduration': 6, 'context': 'outstream' }); // outstream new plcmt value expect(payload.imp[13]).to.have.property('video'); - expect(payload.imp[13].video).to.deep.equal({'mimes': ['video/mp4'], 'maxduration': 30, 'minduration': 6, 'w': 640, 'h': 480, 'context': 'outstream', 'plcmt': 3}); + expect(payload.imp[13].video).to.deep.equal({ 'mimes': ['video/mp4'], 'maxduration': 30, 'minduration': 6, 'w': 640, 'h': 480, 'context': 'outstream', 'plcmt': 3 }); }); it('should check for valid outstream placement values', function () { @@ -779,7 +779,7 @@ describe('triplelift adapter', function () { 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: tdid, atype: 1, ext: {rtiPartner: 'TDID'}}]}]}}); + expect(payload.user).to.deep.equal({ ext: { eids: [{ source: 'adserver.org', uids: [{ id: tdid, atype: 1, ext: { rtiPartner: 'TDID' } }] }] } }); }); it('should add criteoId to the payload if included', function () { @@ -801,7 +801,7 @@ describe('triplelift adapter', function () { const request = tripleliftAdapterSpec.buildRequests(bidRequests, bidderRequest); const payload = request.data; expect(payload).to.exist; - expect(payload.user).to.deep.equal({ext: {eids: [{source: 'criteo.com', uids: [{id: id, atype: 1, ext: {rtiPartner: 'criteoId'}}]}]}}); + expect(payload.user).to.deep.equal({ ext: { eids: [{ source: 'criteo.com', uids: [{ id: id, atype: 1, ext: { rtiPartner: 'criteoId' } }] }] } }); }); it('should add tdid and criteoId to the payload if both are included', function () { @@ -892,12 +892,6 @@ describe('triplelift adapter', function () { const url = request.url; expect(url).to.match(/(\?|&)us_privacy=1YYY/); }); - it('should pass fledge signal when Triplelift is eligible for fledge', function() { - bidderRequest.paapi = {enabled: true}; - const request = tripleliftAdapterSpec.buildRequests(bidRequests, bidderRequest); - const url = request.url; - expect(url).to.match(/(\?|&)fledge=true/); - }); it('should return coppa param when COPPA config is set to true', function() { sinon.stub(config, 'getConfig').withArgs('coppa').returns(true); const request = tripleliftAdapterSpec.buildRequests(bidRequests, bidderRequest); @@ -1011,7 +1005,7 @@ describe('triplelift adapter', function () { } }; - const request = tripleliftAdapterSpec.buildRequests(bidRequests, {...bidderRequest, ortb2}); + const request = tripleliftAdapterSpec.buildRequests(bidRequests, { ...bidderRequest, ortb2 }); const { data: payload } = request; expect(payload.ext.ortb2).to.exist; expect(payload.ext.ortb2.site).to.deep.equal({ @@ -1039,7 +1033,7 @@ describe('triplelift adapter', function () { sens: sens, } } - const request = tripleliftAdapterSpec.buildRequests(bidRequests, {...bidderRequest, ortb2}); + const request = tripleliftAdapterSpec.buildRequests(bidRequests, { ...bidderRequest, ortb2 }); const { data: payload } = request; expect(payload.ext.fpd.user).to.not.exist; expect(payload.ext.fpd.context.ext.data).to.haveOwnProperty('category'); @@ -1355,7 +1349,7 @@ describe('triplelift adapter', function () { meta: {} } ]; - const result = tripleliftAdapterSpec.interpretResponse(response, {bidderRequest}); + const result = tripleliftAdapterSpec.interpretResponse(response, { bidderRequest }); expect(result).to.have.length(4); expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); expect(Object.keys(result[1])).to.have.members(Object.keys(expectedResponse[1])); @@ -1364,7 +1358,7 @@ describe('triplelift adapter', function () { }); it('should identify format of bid and respond accordingly', function() { - const result = tripleliftAdapterSpec.interpretResponse(response, {bidderRequest}); + const result = tripleliftAdapterSpec.interpretResponse(response, { bidderRequest }); expect(result[0].meta.mediaType).to.equal('native'); expect(result[1].mediaType).to.equal('video'); expect(result[1].meta.mediaType).to.equal('video'); @@ -1377,80 +1371,29 @@ describe('triplelift adapter', function () { }) it('should return multiple responses to support SRA', function () { - const result = tripleliftAdapterSpec.interpretResponse(response, {bidderRequest}); + const result = tripleliftAdapterSpec.interpretResponse(response, { bidderRequest }); expect(result).to.have.length(4); }); it('should include the advertiser name in the meta field if available', function () { - const result = tripleliftAdapterSpec.interpretResponse(response, {bidderRequest}); + const result = tripleliftAdapterSpec.interpretResponse(response, { bidderRequest }); expect(result[0].meta.advertiserName).to.equal('fake advertiser name'); expect(result[1].meta).to.not.have.key('advertiserName'); }); it('should include the advertiser domain array in the meta field if available', function () { - const result = tripleliftAdapterSpec.interpretResponse(response, {bidderRequest}); + const result = tripleliftAdapterSpec.interpretResponse(response, { bidderRequest }); expect(result[0].meta.advertiserDomains[0]).to.equal('basspro.com'); expect(result[0].meta.advertiserDomains[1]).to.equal('internetalerts.org'); expect(result[1].meta).to.not.have.key('advertiserDomains'); }); it('should include networkId in the meta field if available', function () { - const result = tripleliftAdapterSpec.interpretResponse(response, {bidderRequest}); + const result = tripleliftAdapterSpec.interpretResponse(response, { bidderRequest }); expect(result[1].meta.networkId).to.equal('10092'); expect(result[2].meta.networkId).to.equal('5989'); expect(result[3].meta.networkId).to.equal('5989'); }); - - it('should return fledgeAuctionConfigs if PAAPI response is received', function() { - response.body.paapi = [ - { - imp_id: '0', - auctionConfig: { - seller: 'https://3lift.com', - decisionLogicUrl: 'https://3lift.com/decision_logic.js', - interestGroupBuyers: ['https://some_buyer.com'], - perBuyerSignals: { - 'https://some_buyer.com': { a: 1 } - } - } - }, - { - imp_id: '2', - auctionConfig: { - seller: 'https://3lift.com', - decisionLogicUrl: 'https://3lift.com/decision_logic.js', - interestGroupBuyers: ['https://some_other_buyer.com'], - perBuyerSignals: { - 'https://some_other_buyer.com': { b: 2 } - } - } - } - ]; - - const result = tripleliftAdapterSpec.interpretResponse(response, {bidderRequest}); - - expect(result).to.have.property('bids'); - expect(result).to.have.property('paapi'); - expect(result.paapi.length).to.equal(2); - expect(result.paapi[0].bidId).to.equal('30b31c1838de1e'); - expect(result.paapi[1].bidId).to.equal('73edc0ba8de203'); - expect(result.paapi[0].config).to.deep.equal( - { - 'seller': 'https://3lift.com', - 'decisionLogicUrl': 'https://3lift.com/decision_logic.js', - 'interestGroupBuyers': ['https://some_buyer.com'], - 'perBuyerSignals': { 'https://some_buyer.com': { 'a': 1 } } - } - ); - expect(result.paapi[1].config).to.deep.equal( - { - 'seller': 'https://3lift.com', - 'decisionLogicUrl': 'https://3lift.com/decision_logic.js', - 'interestGroupBuyers': ['https://some_other_buyer.com'], - 'perBuyerSignals': { 'https://some_other_buyer.com': { 'b': 2 } } - } - ); - }); }); describe('getUserSyncs', function() { diff --git a/test/spec/modules/truereachBidAdapter_spec.js b/test/spec/modules/truereachBidAdapter_spec.js index 6b39d46eac4..58b2c2ebf9c 100644 --- a/test/spec/modules/truereachBidAdapter_spec.js +++ b/test/spec/modules/truereachBidAdapter_spec.js @@ -83,7 +83,7 @@ describe('truereachBidAdapterTests', function () { const user_sync_url = 'https://ads-sg.momagic.com/jsp/usersync.jsp'; it('register_iframe_pixel_if_iframeEnabled_is_true', function() { const syncs = spec.getUserSyncs( - {iframeEnabled: true} + { iframeEnabled: true } ); expect(syncs).to.be.an('array'); expect(syncs.length).to.equal(1); @@ -93,7 +93,7 @@ describe('truereachBidAdapterTests', function () { it('if_pixelEnabled_is_true', function() { const syncs = spec.getUserSyncs( - {pixelEnabled: true} + { pixelEnabled: true } ); expect(syncs).to.be.an('array'); expect(syncs.length).to.equal(0); diff --git a/test/spec/modules/trustxBidAdapter_spec.js b/test/spec/modules/trustxBidAdapter_spec.js index ac572027900..2dfc2e7d41f 100644 --- a/test/spec/modules/trustxBidAdapter_spec.js +++ b/test/spec/modules/trustxBidAdapter_spec.js @@ -1,8 +1,8 @@ -import {expect} from 'chai'; -import {spec} from 'modules/trustxBidAdapter.js'; -import {BANNER, VIDEO} from 'src/mediaTypes.js'; +import { expect } from 'chai'; +import { spec } from 'modules/trustxBidAdapter.js'; +import { BANNER, VIDEO } from 'src/mediaTypes.js'; import sinon from 'sinon'; -import {config} from 'src/config.js'; +import { config } from 'src/config.js'; const getBannerRequest = () => { return { @@ -21,7 +21,7 @@ const getBannerRequest = () => { mediaTypes: { banner: { sizes: [ - [ 300, 250 ], + [300, 250], ] } }, @@ -437,7 +437,7 @@ describe('trustxBidAdapter', function() { beforeEach(function() { bidderBannerRequest = getBannerRequest(); - mockBidderRequest = {refererInfo: {}}; + mockBidderRequest = { refererInfo: {} }; bidRequestsWithMediaTypes = [{ bidder: 'trustx', @@ -682,7 +682,7 @@ describe('trustxBidAdapter', function() { }); it('handles empty response', function () { - const EMPTY_RESP = Object.assign({}, bidderResponse, {'body': {}}); + const EMPTY_RESP = Object.assign({}, bidderResponse, { 'body': {} }); const bids = spec.interpretResponse(EMPTY_RESP, bidRequest); expect(bids).to.be.empty; @@ -919,32 +919,36 @@ describe('trustxBidAdapter', function() { }); it('handles empty response', function () { - const EMPTY_RESP = Object.assign({}, bidderResponse, {'body': {}}); + const EMPTY_RESP = Object.assign({}, bidderResponse, { 'body': {} }); const bids = spec.interpretResponse(EMPTY_RESP, bidRequest); expect(bids).to.be.empty; }); it('should return no bids if the response "nurl" and "adm" are missing', function () { - const SERVER_RESP = Object.assign({}, bidderResponse, {'body': { - seatbid: [{ - bid: [{ - price: 8.01 + const SERVER_RESP = Object.assign({}, bidderResponse, { + 'body': { + seatbid: [{ + bid: [{ + price: 8.01 + }] }] - }] - }}); + } + }); const bids = spec.interpretResponse(SERVER_RESP, bidRequest); expect(bids.length).to.equal(0); }); it('should return no bids if the response "price" is missing', function () { - const SERVER_RESP = Object.assign({}, bidderResponse, {'body': { - seatbid: [{ - bid: [{ - adm: '' + const SERVER_RESP = Object.assign({}, bidderResponse, { + 'body': { + seatbid: [{ + bid: [{ + adm: '' + }] }] - }] - }}); + } + }); const bids = spec.interpretResponse(SERVER_RESP, bidRequest); expect(bids.length).to.equal(0); }); @@ -995,13 +999,13 @@ describe('trustxBidAdapter', function() { 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}); + 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}, [bidderResponse]); + let opts = spec.getUserSyncs({ iframeEnabled: true, pixelEnabled: false }, [bidderResponse]); expect(opts.length).to.equal(1); expect(opts[0].type).to.equal('iframe'); @@ -1009,7 +1013,7 @@ describe('trustxBidAdapter', function() { }); it('pixel sync enabled should return results', function () { - let opts = spec.getUserSyncs({iframeEnabled: false, pixelEnabled: true}, [bidderResponse]); + let opts = spec.getUserSyncs({ iframeEnabled: false, pixelEnabled: true }, [bidderResponse]); expect(opts.length).to.equal(1); expect(opts[0].type).to.equal('image'); @@ -1017,7 +1021,7 @@ describe('trustxBidAdapter', function() { }); it('all sync enabled should prioritize iframe', function () { - let opts = spec.getUserSyncs({iframeEnabled: true, pixelEnabled: true}, [bidderResponse]); + let opts = spec.getUserSyncs({ iframeEnabled: true, pixelEnabled: true }, [bidderResponse]); expect(opts.length).to.equal(1); }); @@ -1088,7 +1092,7 @@ describe('trustxBidAdapter', function() { }; // Test with pixel enabled - let opts = spec.getUserSyncs({iframeEnabled: false, pixelEnabled: true}, [realWorldResponse]); + let opts = spec.getUserSyncs({ iframeEnabled: false, pixelEnabled: true }, [realWorldResponse]); // Should return all pixel syncs from all providers expect(opts).to.be.an('array'); @@ -1100,7 +1104,7 @@ describe('trustxBidAdapter', function() { expect(opts.some(s => s.url.includes('sync.trustx.org'))).to.be.true; // Test with iframe enabled - opts = spec.getUserSyncs({iframeEnabled: true, pixelEnabled: false}, [realWorldResponse]); + opts = spec.getUserSyncs({ iframeEnabled: true, pixelEnabled: false }, [realWorldResponse]); // Should return only iframe syncs expect(opts).to.be.an('array'); diff --git a/test/spec/modules/ttdBidAdapter_spec.js b/test/spec/modules/ttdBidAdapter_spec.js index c11378e72cc..9f30f14af99 100644 --- a/test/spec/modules/ttdBidAdapter_spec.js +++ b/test/spec/modules/ttdBidAdapter_spec.js @@ -449,10 +449,10 @@ describe('ttdBidAdapter', function () { } } const requestBody = testBuildRequests( - baseBannerBidRequests, {...baseBidderRequestWithoutRefererDomain, ortb2} + baseBannerBidRequests, { ...baseBidderRequestWithoutRefererDomain, ortb2 } ).data; config.resetConfig(); - expect(requestBody.site.publisher).to.deep.equal({domain: 'https://foo.bar', id: '13144370'}); + expect(requestBody.site.publisher).to.deep.equal({ domain: 'https://foo.bar', id: '13144370' }); }); it('referer domain overrides first party site data publisher domain', function () { @@ -464,7 +464,7 @@ describe('ttdBidAdapter', function () { } }; const requestBody = testBuildRequests( - baseBannerBidRequests, {...baseBidderRequest, ortb2} + baseBannerBidRequests, { ...baseBidderRequest, ortb2 } ).data; config.resetConfig(); expect(requestBody.site.publisher.domain).to.equal(baseBidderRequest.refererInfo.domain); @@ -476,7 +476,7 @@ describe('ttdBidAdapter', function () { keywords: 'highViewability, clothing, holiday shopping' } }; - const requestBody = testBuildRequests(baseBannerBidRequests, {...baseBidderRequest, ortb2}).data; + const requestBody = testBuildRequests(baseBannerBidRequests, { ...baseBidderRequest, ortb2 }).data; config.resetConfig(); expect(requestBody.ext.ttdprebid.keywords).to.deep.equal(['highViewability', 'clothing', 'holiday shopping']); }); @@ -485,7 +485,7 @@ describe('ttdBidAdapter', function () { const ortb2 = { bcat: ['IAB1-1', 'IAB2-9'] }; - const requestBody = testBuildRequests(baseBannerBidRequests, {...baseBidderRequest, ortb2}).data; + const requestBody = testBuildRequests(baseBannerBidRequests, { ...baseBidderRequest, ortb2 }).data; config.resetConfig(); expect(requestBody.bcat).to.deep.equal(['IAB1-1', 'IAB2-9']); }); @@ -494,7 +494,7 @@ describe('ttdBidAdapter', function () { const ortb2 = { badv: ['adv1.com', 'adv2.com'] }; - const requestBody = testBuildRequests(baseBannerBidRequests, {...baseBidderRequest, ortb2}).data; + const requestBody = testBuildRequests(baseBannerBidRequests, { ...baseBidderRequest, ortb2 }).data; config.resetConfig(); expect(requestBody.badv).to.deep.equal(['adv1.com', 'adv2.com']); }); @@ -543,7 +543,7 @@ describe('ttdBidAdapter', function () { it('adds coppa consent info to the request', function () { const clonedBidderRequest = deepClone(baseBidderRequest); - config.setConfig({coppa: true}); + config.setConfig({ coppa: true }); const requestBody = testBuildRequests(baseBannerBidRequests, clonedBidderRequest).data; config.resetConfig(); expect(requestBody.regs.coppa).to.equal(1); @@ -556,7 +556,7 @@ describe('ttdBidAdapter', function () { gpp_sid: [6, 7] } }; - const clonedBidderRequest = {...deepClone(baseBidderRequest), ortb2}; + const clonedBidderRequest = { ...deepClone(baseBidderRequest), ortb2 }; const requestBody = testBuildRequests(baseBannerBidRequests, clonedBidderRequest).data; config.resetConfig(); expect(requestBody.regs.gpp).to.equal('somegppstring'); @@ -630,7 +630,7 @@ describe('ttdBidAdapter', function () { keywords: 'power tools, drills' } }; - const clonedBidderRequest = {...deepClone(baseBidderRequest), ortb2}; + const clonedBidderRequest = { ...deepClone(baseBidderRequest), ortb2 }; const requestBody = testBuildRequests(baseBannerBidRequests, clonedBidderRequest).data; expect(requestBody.site.name).to.equal('example'); expect(requestBody.site.domain).to.equal('page.example.com'); @@ -678,7 +678,7 @@ describe('ttdBidAdapter', function () { } }; - const clonedBidderRequest = {...deepClone(baseBidderRequest), ortb2}; + const clonedBidderRequest = { ...deepClone(baseBidderRequest), ortb2 }; const requestBody = testBuildRequests(baseBannerBidRequests, clonedBidderRequest).data; validateExtFirstPartyData(requestBody.site.ext) @@ -693,7 +693,7 @@ describe('ttdBidAdapter', function () { } }; - const clonedBidderRequest = {...deepClone(baseBidderRequest), ortb2}; + const clonedBidderRequest = { ...deepClone(baseBidderRequest), ortb2 }; const requestBody = testBuildRequests(baseBannerBidRequests, clonedBidderRequest).data; validateExtFirstPartyData(requestBody.user.ext) @@ -725,7 +725,7 @@ describe('ttdBidAdapter', function () { } }; - const clonedBidderRequest = {...deepClone(baseBidderRequest), ortb2}; + const clonedBidderRequest = { ...deepClone(baseBidderRequest), ortb2 }; const requestBody = testBuildRequests(baseBannerBidRequests, clonedBidderRequest).data; validateExtFirstPartyData(requestBody.app.ext) @@ -740,7 +740,7 @@ describe('ttdBidAdapter', function () { } }; - const clonedBidderRequest = {...deepClone(baseBidderRequest), ortb2}; + const clonedBidderRequest = { ...deepClone(baseBidderRequest), ortb2 }; const requestBody = testBuildRequests(baseBannerBidRequests, clonedBidderRequest).data; validateExtFirstPartyData(requestBody.device.ext) @@ -755,7 +755,7 @@ describe('ttdBidAdapter', function () { } }; - const clonedBidderRequest = {...deepClone(baseBidderRequest), ortb2}; + const clonedBidderRequest = { ...deepClone(baseBidderRequest), ortb2 }; const requestBody = testBuildRequests(baseBannerBidRequests, clonedBidderRequest).data; validateExtFirstPartyData(requestBody.imp[0].pmp.ext) diff --git a/test/spec/modules/twistDigitalBidAdapter_spec.js b/test/spec/modules/twistDigitalBidAdapter_spec.js index ea3779c8be8..97a27f79b65 100644 --- a/test/spec/modules/twistDigitalBidAdapter_spec.js +++ b/test/spec/modules/twistDigitalBidAdapter_spec.js @@ -1,15 +1,15 @@ -import {expect} from 'chai'; +import { expect } from 'chai'; import { spec as adapter, createDomain, storage } from 'modules/twistDigitalBidAdapter.js'; import * as utils from 'src/utils.js'; -import {version} from 'package.json'; -import {useFakeTimers} from 'sinon'; -import {BANNER, VIDEO} from '../../../src/mediaTypes.js'; -import {config} from '../../../src/config.js'; -import {deepSetValue} from 'src/utils.js'; +import { version } from 'package.json'; +import { useFakeTimers } from 'sinon'; +import { BANNER, VIDEO } from '../../../src/mediaTypes.js'; +import { config } from '../../../src/config.js'; +import { deepSetValue } from 'src/utils.js'; import { extractPID, extractCID, @@ -20,7 +20,7 @@ import { tryParseJSON, getUniqueDealId } from '../../../libraries/vidazooUtils/bidderUtils.js'; -import {getGlobal} from '../../../src/prebidGlobal.js'; +import { getGlobal } from '../../../src/prebidGlobal.js'; export const TEST_ID_SYSTEMS = ['britepoolid', 'criteoId', 'id5id', 'idl_env', 'lipb', 'netId', 'parrableId', 'pubcid', 'tdid', 'pubProvidedId']; @@ -103,9 +103,9 @@ const ORTB2_DEVICE = { 'version': ['8', '0', '0'] }, 'browsers': [ - {'brand': 'Not_A Brand', 'version': ['99', '0', '0', '0']}, - {'brand': 'Google Chrome', 'version': ['109', '0', '5414', '119']}, - {'brand': 'Chromium', 'version': ['109', '0', '5414', '119']} + { 'brand': 'Not_A Brand', 'version': ['99', '0', '0', '0'] }, + { 'brand': 'Google Chrome', 'version': ['109', '0', '5414', '119'] }, + { 'brand': 'Chromium', 'version': ['109', '0', '5414', '119'] } ], 'mobile': 1, 'model': 'SM-G955U', @@ -122,7 +122,7 @@ const ORTB2_DEVICE = { model: 'iPhone 12 Pro Max', os: 'iOS', osv: '17.4', - ext: {fiftyonedegrees_deviceId: '17595-133085-133468-18092'}, + ext: { fiftyonedegrees_deviceId: '17595-133085-133468-18092' }, }; const BIDDER_REQUEST = { @@ -149,8 +149,8 @@ const BIDDER_REQUEST = { 'segtax': 7 }, 'segments': [ - {'id': 'segId1'}, - {'id': 'segId2'} + { 'id': 'segId1' }, + { 'id': 'segId2' } ] }] } @@ -164,9 +164,9 @@ const BIDDER_REQUEST = { user: { data: [ { - ext: {segtax: 600, segclass: '1'}, + ext: { segtax: 600, segclass: '1' }, name: 'example.com', - segment: [{id: '243'}], + segment: [{ id: '243' }], }, ], } @@ -215,21 +215,21 @@ const VIDEO_SERVER_RESPONSE = { const ORTB2_OBJ = { "device": ORTB2_DEVICE, - "regs": {"coppa": 0, "gpp": "gpp_string", "gpp_sid": [7]}, + "regs": { "coppa": 0, "gpp": "gpp_string", "gpp_sid": [7] }, "site": { "cat": ["IAB2"], "content": { "data": [{ - "ext": {"segtax": 7}, + "ext": { "segtax": 7 }, "name": "example.com", - "segments": [{"id": "segId1"}, {"id": "segId2"}] + "segments": [{ "id": "segId1" }, { "id": "segId2" }] }], "language": "en" }, "pagecat": ["IAB2-2"] }, "user": { - "data": [{"ext": {"segclass": "1", "segtax": 600}, "name": "example.com", "segment": [{"id": "243"}]}] + "data": [{ "ext": { "segclass": "1", "segtax": 600 }, "name": "example.com", "segment": [{ "id": "243" }] }] } }; @@ -243,7 +243,7 @@ const REQUEST = { function getTopWindowQueryParams() { try { - const parsedUrl = utils.parseUrl(window.top.document.URL, {decodeSearchAsString: true}); + const parsedUrl = utils.parseUrl(window.top.document.URL, { decodeSearchAsString: true }); return parsedUrl.search; } catch (e) { return ''; @@ -369,9 +369,9 @@ describe('TwistDigitalBidAdapter', function () { 'version': ['8', '0', '0'] }, 'browsers': [ - {'brand': 'Not_A Brand', 'version': ['99', '0', '0', '0']}, - {'brand': 'Google Chrome', 'version': ['109', '0', '5414', '119']}, - {'brand': 'Chromium', 'version': ['109', '0', '5414', '119']} + { 'brand': 'Not_A Brand', 'version': ['99', '0', '0', '0'] }, + { 'brand': 'Google Chrome', 'version': ['109', '0', '5414', '119'] }, + { 'brand': 'Chromium', 'version': ['109', '0', '5414', '119'] } ], 'mobile': 1, 'model': 'SM-G955U', @@ -387,15 +387,15 @@ describe('TwistDigitalBidAdapter', function () { 'segtax': 7 }, 'segments': [ - {'id': 'segId1'}, - {'id': 'segId2'} + { 'id': 'segId1' }, + { 'id': 'segId2' } ] }], userData: [ { - ext: {segtax: 600, segclass: '1'}, + ext: { segtax: 600, segclass: '1' }, name: 'example.com', - segment: [{id: '243'}], + segment: [{ id: '243' }], }, ], uniqueDealId: `${hashUrl}_${Date.now().toString()}`, @@ -453,9 +453,9 @@ describe('TwistDigitalBidAdapter', function () { 'version': ['8', '0', '0'] }, 'browsers': [ - {'brand': 'Not_A Brand', 'version': ['99', '0', '0', '0']}, - {'brand': 'Google Chrome', 'version': ['109', '0', '5414', '119']}, - {'brand': 'Chromium', 'version': ['109', '0', '5414', '119']} + { 'brand': 'Not_A Brand', 'version': ['99', '0', '0', '0'] }, + { 'brand': 'Google Chrome', 'version': ['109', '0', '5414', '119'] }, + { 'brand': 'Chromium', 'version': ['109', '0', '5414', '119'] } ], 'mobile': 1, 'model': 'SM-G955U', @@ -493,15 +493,15 @@ describe('TwistDigitalBidAdapter', function () { 'segtax': 7 }, 'segments': [ - {'id': 'segId1'}, - {'id': 'segId2'} + { 'id': 'segId1' }, + { 'id': 'segId2' } ] }], userData: [ { - ext: {segtax: 600, segclass: '1'}, + ext: { segtax: 600, segclass: '1' }, name: 'example.com', - segment: [{id: '243'}], + segment: [{ id: '243' }], }, ] } @@ -544,9 +544,9 @@ describe('TwistDigitalBidAdapter', function () { 'version': ['8', '0', '0'] }, 'browsers': [ - {'brand': 'Not_A Brand', 'version': ['99', '0', '0', '0']}, - {'brand': 'Google Chrome', 'version': ['109', '0', '5414', '119']}, - {'brand': 'Chromium', 'version': ['109', '0', '5414', '119']} + { 'brand': 'Not_A Brand', 'version': ['99', '0', '0', '0'] }, + { 'brand': 'Google Chrome', 'version': ['109', '0', '5414', '119'] }, + { 'brand': 'Chromium', 'version': ['109', '0', '5414', '119'] } ], 'mobile': 1, 'model': 'SM-G955U', @@ -582,15 +582,15 @@ describe('TwistDigitalBidAdapter', function () { 'segtax': 7 }, 'segments': [ - {'id': 'segId1'}, - {'id': 'segId2'} + { 'id': 'segId1' }, + { 'id': 'segId2' } ] }], userData: [ { - ext: {segtax: 600, segclass: '1'}, + ext: { segtax: 600, segclass: '1' }, name: 'example.com', - segment: [{id: '243'}], + segment: [{ id: '243' }], }, ] }; @@ -606,10 +606,12 @@ describe('TwistDigitalBidAdapter', function () { expect(requests[0]).to.deep.equal({ method: 'POST', url: `${createDomain(SUB_DOMAIN)}/prebid/multi/59db6b3b4ffaa70004f45cdc`, - data: {bids: [ - {...REQUEST_DATA, ortb2: ORTB2_OBJ, ortb2Imp: BID.ortb2Imp}, - {...REQUEST_DATA2, ortb2: ORTB2_OBJ, ortb2Imp: BID.ortb2Imp} - ]} + data: { + bids: [ + { ...REQUEST_DATA, ortb2: ORTB2_OBJ, ortb2Imp: BID.ortb2Imp }, + { ...REQUEST_DATA2, ortb2: ORTB2_OBJ, ortb2Imp: BID.ortb2Imp } + ] + } }); }); @@ -639,15 +641,6 @@ describe('TwistDigitalBidAdapter', function () { expect(requests).to.have.length(2); }); - it('should set fledge correctly if enabled', function () { - config.resetConfig(); - const bidderRequest = utils.deepClone(BIDDER_REQUEST); - bidderRequest.paapi = {enabled: true}; - deepSetValue(bidderRequest, 'ortb2Imp.ext.ae', 1); - const requests = adapter.buildRequests([BID], bidderRequest); - expect(requests[0].data.fledge).to.equal(1); - }); - after(function () { getGlobal().bidderSettings = {}; config.resetConfig(); @@ -657,7 +650,7 @@ describe('TwistDigitalBidAdapter', function () { describe('getUserSyncs', function () { it('should have valid user sync with iframeEnabled', function () { - const result = adapter.getUserSyncs({iframeEnabled: true}, [SERVER_RESPONSE]); + const result = adapter.getUserSyncs({ iframeEnabled: true }, [SERVER_RESPONSE]); expect(result).to.deep.equal([{ type: 'iframe', @@ -666,7 +659,7 @@ describe('TwistDigitalBidAdapter', function () { }); it('should have valid user sync with cid on response', function () { - const result = adapter.getUserSyncs({iframeEnabled: true}, [SERVER_RESPONSE]); + const result = adapter.getUserSyncs({ iframeEnabled: true }, [SERVER_RESPONSE]); expect(result).to.deep.equal([{ type: 'iframe', url: 'https://sync.twist.win/api/sync/iframe/?cid=testcid123&gdpr=0&gdpr_consent=&us_privacy=&coppa=0' @@ -674,7 +667,7 @@ describe('TwistDigitalBidAdapter', function () { }); it('should have valid user sync with pixelEnabled', function () { - const result = adapter.getUserSyncs({pixelEnabled: true}, [SERVER_RESPONSE]); + const result = adapter.getUserSyncs({ pixelEnabled: true }, [SERVER_RESPONSE]); expect(result).to.deep.equal([{ 'url': 'https://sync.twist.win/api/sync/image/?cid=testcid123&gdpr=0&gdpr_consent=&us_privacy=&coppa=0', @@ -686,7 +679,7 @@ describe('TwistDigitalBidAdapter', function () { config.setConfig({ coppa: 1 }); - const result = adapter.getUserSyncs({iframeEnabled: true}, [SERVER_RESPONSE]); + const result = adapter.getUserSyncs({ iframeEnabled: true }, [SERVER_RESPONSE]); expect(result).to.deep.equal([{ type: 'iframe', url: 'https://sync.twist.win/api/sync/iframe/?cid=testcid123&gdpr=0&gdpr_consent=&us_privacy=&coppa=1' @@ -701,12 +694,12 @@ describe('TwistDigitalBidAdapter', function () { }); it('should return empty array when there is no ad', function () { - const responses = adapter.interpretResponse({price: 1, ad: ''}); + const responses = adapter.interpretResponse({ price: 1, ad: '' }); expect(responses).to.be.empty; }); it('should return empty array when there is no price', function () { - const responses = adapter.interpretResponse({price: null, ad: 'great ad'}); + const responses = adapter.interpretResponse({ price: null, ad: 'great ad' }); expect(responses).to.be.empty; }); @@ -802,9 +795,9 @@ describe('TwistDigitalBidAdapter', function () { const userId = (function () { switch (idSystemProvider) { case 'lipb': - return {lipbid: id}; + return { lipbid: id }; case 'id5id': - return {uid: id}; + return { uid: id }; default: return id; } @@ -825,7 +818,7 @@ describe('TwistDigitalBidAdapter', function () { bid.userIdAsEids = [ { "source": "audigent.com", - "uids": [{"id": "fakeidi6j6dlc6e"}] + "uids": [{ "id": "fakeidi6j6dlc6e" }] } ] const requests = adapter.buildRequests([bid], BIDDER_REQUEST); @@ -836,11 +829,11 @@ describe('TwistDigitalBidAdapter', function () { bid.userIdAsEids = [ { "source": "audigent.com", - "uids": [{"id": "fakeidi6j6dlc6e"}] + "uids": [{ "id": "fakeidi6j6dlc6e" }] }, { "source": "rwdcntrl.net", - "uids": [{"id": "fakeid6f35197d5c", "atype": 1}] + "uids": [{ "id": "fakeid6f35197d5c", "atype": 1 }] } ] const requests = adapter.buildRequests([bid], BIDDER_REQUEST); @@ -855,7 +848,7 @@ describe('TwistDigitalBidAdapter', function () { eids: [ { "source": "pubcid.org", - "uids": [{"id": "fakeid8888dlc6e"}] + "uids": [{ "id": "fakeid8888dlc6e" }] } ] } @@ -870,11 +863,11 @@ describe('TwistDigitalBidAdapter', function () { eids: [ { "source": "pubcid.org", - "uids": [{"id": "fakeid8888dlc6e"}] + "uids": [{ "id": "fakeid8888dlc6e" }] }, { "source": "adserver.org", - "uids": [{"id": "fakeid495ff1"}] + "uids": [{ "id": "fakeid495ff1" }] } ] } @@ -887,18 +880,18 @@ describe('TwistDigitalBidAdapter', function () { describe('alternate param names extractors', function () { it('should return undefined when param not supported', function () { - const cid = extractCID({'c_id': '1'}); - const pid = extractPID({'p_id': '1'}); - const subDomain = extractSubDomain({'sub_domain': 'prebid'}); + const cid = extractCID({ 'c_id': '1' }); + const pid = extractPID({ 'p_id': '1' }); + const subDomain = extractSubDomain({ 'sub_domain': 'prebid' }); expect(cid).to.be.undefined; expect(pid).to.be.undefined; expect(subDomain).to.be.undefined; }); it('should return value when param supported', function () { - const cid = extractCID({'cID': '1'}); - const pid = extractPID({'Pid': '2'}); - const subDomain = extractSubDomain({'subDOMAIN': 'prebid'}); + const cid = extractCID({ 'cID': '1' }); + const pid = extractPID({ 'Pid': '2' }); + const subDomain = extractSubDomain({ 'subDOMAIN': 'prebid' }); expect(cid).to.be.equal('1'); expect(pid).to.be.equal('2'); expect(subDomain).to.be.equal('prebid'); @@ -971,7 +964,7 @@ describe('TwistDigitalBidAdapter', function () { now }); setStorageItem(storage, 'myKey', 2020); - const {value, created} = getStorageItem(storage, 'myKey'); + const { value, created } = getStorageItem(storage, 'myKey'); expect(created).to.be.equal(now); expect(value).to.be.equal(2020); expect(typeof value).to.be.equal('number'); @@ -987,8 +980,8 @@ describe('TwistDigitalBidAdapter', function () { }); it('should parse JSON value', function () { - const data = JSON.stringify({event: 'send'}); - const {event} = tryParseJSON(data); + const data = JSON.stringify({ event: 'send' }); + const { event } = tryParseJSON(data); expect(event).to.be.equal('send'); }); diff --git a/test/spec/modules/ucfunnelAnalyticsAdapter_spec.js b/test/spec/modules/ucfunnelAnalyticsAdapter_spec.js index 997586c195e..75822849e73 100644 --- a/test/spec/modules/ucfunnelAnalyticsAdapter_spec.js +++ b/test/spec/modules/ucfunnelAnalyticsAdapter_spec.js @@ -3,7 +3,7 @@ import { ANALYTICS_VERSION, BIDDER_STATUS } from 'modules/ucfunnelAnalyticsAdapter.js'; -import {expect} from 'chai'; +import { expect } from 'chai'; const events = require('src/events'); const constants = require('src/constants.js'); @@ -89,7 +89,7 @@ describe('ucfunnel Prebid AnalyticsAdapter Testing', function () { }); describe('#getCachedAuction()', function() { - const existing = {timeoutBids: [{}]}; + const existing = { timeoutBids: [{}] }; ucfunnelAnalyticsAdapter.cachedAuctions['test_auction_id'] = existing; it('should get the existing cached object if it exists', function() { diff --git a/test/spec/modules/ucfunnelBidAdapter_spec.js b/test/spec/modules/ucfunnelBidAdapter_spec.js index 3e78ee4d0d4..3a41c8209f1 100644 --- a/test/spec/modules/ucfunnelBidAdapter_spec.js +++ b/test/spec/modules/ucfunnelBidAdapter_spec.js @@ -1,7 +1,7 @@ import { expect } from 'chai'; import { spec } from 'modules/ucfunnelBidAdapter.js'; -import {BANNER, VIDEO, NATIVE} from 'src/mediaTypes.js'; -import {deepClone} from '../../../src/utils.js'; +import { BANNER, VIDEO, NATIVE } from 'src/mediaTypes.js'; +import { deepClone } from '../../../src/utils.js'; const URL = 'https://hb.aralego.com/header'; const BIDDER_CODE = 'ucfunnel'; @@ -15,13 +15,13 @@ const bidderRequest = { const userId = { 'criteoId': 'vYlICF9oREZlTHBGRVdrJTJCUUJnc3U2ckNVaXhrV1JWVUZVSUxzZmJlcnJZR0ZxbVhFRnU5bDAlMkJaUWwxWTlNcmdEeHFrJTJGajBWVlV4T3lFQ0FyRVcxNyUyQlIxa0lLSlFhcWJpTm9PSkdPVkx0JTJCbzlQRTQlM0Q', - 'id5id': {'uid': 'ID5-8ekgswyBTQqnkEKy0ErmeQ1GN5wV4pSmA-RE4eRedA'}, + 'id5id': { 'uid': 'ID5-8ekgswyBTQqnkEKy0ErmeQ1GN5wV4pSmA-RE4eRedA' }, 'netId': 'fH5A3n2O8_CZZyPoJVD-eabc6ECb7jhxCicsds7qSg', - 'parrableId': {'eid': '01.1608624401.fe44bca9b96873084a0d4e9d0ac5729f13790ba8f8e58fa4707b6b3c096df91c6b5f254992bdad4ab1dd4a89919081e9b877d7a039ac3183709277665bac124f28e277d109f0ff965058'}, + 'parrableId': { 'eid': '01.1608624401.fe44bca9b96873084a0d4e9d0ac5729f13790ba8f8e58fa4707b6b3c096df91c6b5f254992bdad4ab1dd4a89919081e9b877d7a039ac3183709277665bac124f28e277d109f0ff965058' }, 'pubcid': 'd8aa10fa-d86c-451d-aad8-5f16162a9e64', 'tdid': 'D6885E90-2A7A-4E0F-87CB-7734ED1B99A3', 'haloId': {}, - 'uid2': {'id': 'eb33b0cb-8d35-4722-b9c0-1a31d4064888'}, + 'uid2': { 'id': 'eb33b0cb-8d35-4722-b9c0-1a31d4064888' }, 'connectid': '4567' } @@ -177,7 +177,7 @@ describe('ucfunnel Adapter', function () { it('should attach request data', function () { const data = request[0].data; - const [ width, height ] = validBannerBidReq.sizes[0]; + const [width, height] = validBannerBidReq.sizes[0]; expect(data.usprivacy).to.equal('1YNN'); expect(data.adid).to.equal('ad-34BBD2AA24B678BBFD4E7B9EE3B872D'); expect(data.w).to.equal(width); @@ -190,7 +190,7 @@ describe('ucfunnel Adapter', function () { const sizes = [[300, 250], [336, 280]]; const format = '300,250;336,280'; validBannerBidReq.sizes = sizes; - const requests = spec.buildRequests([ validBannerBidReq ], bidderRequest); + const requests = spec.buildRequests([validBannerBidReq], bidderRequest); const data = requests[0].data; expect(data.w).to.equal(sizes[0][0]); expect(data.h).to.equal(sizes[0][1]); @@ -205,7 +205,7 @@ describe('ucfunnel Adapter', function () { floor: 2.02 } }; - const requests = spec.buildRequests([ bid ], bidderRequest); + const requests = spec.buildRequests([bid], bidderRequest); const data = requests[0].data; expect(data.fp).to.equal(2.02); }); @@ -213,7 +213,7 @@ describe('ucfunnel Adapter', function () { it('should set bidfloor if configured', function() { const bid = deepClone(validBannerBidReq); bid.params.bidfloor = 2.01; - const requests = spec.buildRequests([ bid ], bidderRequest); + const requests = spec.buildRequests([bid], bidderRequest); const data = requests[0].data; expect(data.fp).to.equal(2.01); }); @@ -227,7 +227,7 @@ describe('ucfunnel Adapter', function () { } }; bid.params.bidfloor = 2.01; - const requests = spec.buildRequests([ bid ], bidderRequest); + const requests = spec.buildRequests([bid], bidderRequest); const data = requests[0].data; expect(data.fp).to.equal(2.01); }); @@ -237,8 +237,8 @@ describe('ucfunnel Adapter', function () { describe('should support banner', function () { let request, result; before(() => { - request = spec.buildRequests([ validBannerBidReq ], bidderRequest); - result = spec.interpretResponse({body: validBannerBidRes}, request[0]); + request = spec.buildRequests([validBannerBidReq], bidderRequest); + result = spec.interpretResponse({ body: validBannerBidRes }, request[0]); }); it('should build bid array for banner', function () { @@ -260,8 +260,8 @@ describe('ucfunnel Adapter', function () { describe('handle banner no ad', function () { let request, result; before(() => { - request = spec.buildRequests([ validBannerBidReq ], bidderRequest); - result = spec.interpretResponse({body: invalidBannerBidRes}, request[0]); + request = spec.buildRequests([validBannerBidReq], bidderRequest); + result = spec.interpretResponse({ body: invalidBannerBidRes }, request[0]); }) it('should build bid array for banner', function () { expect(result.length).to.equal(1); @@ -281,8 +281,8 @@ describe('ucfunnel Adapter', function () { describe('handle banner cpm under bidfloor', function () { let request, result; before(() => { - request = spec.buildRequests([ validBannerBidReq ], bidderRequest); - result = spec.interpretResponse({body: invalidBannerBidRes}, request[0]); + request = spec.buildRequests([validBannerBidReq], bidderRequest); + result = spec.interpretResponse({ body: invalidBannerBidRes }, request[0]); }) it('should build bid array for banner', function () { expect(result.length).to.equal(1); @@ -302,8 +302,8 @@ describe('ucfunnel Adapter', function () { describe('should support video', function () { let request, result; before(() => { - request = spec.buildRequests([ validVideoBidReq ], bidderRequest); - result = spec.interpretResponse({body: validVideoBidRes}, request[0]); + request = spec.buildRequests([validVideoBidReq], bidderRequest); + result = spec.interpretResponse({ body: validVideoBidRes }, request[0]); }) it('should build bid array', function () { expect(result.length).to.equal(1); @@ -325,8 +325,8 @@ describe('ucfunnel Adapter', function () { describe('should support native', function () { let request, result; before(() => { - request = spec.buildRequests([ validNativeBidReq ], bidderRequest); - result = spec.interpretResponse({body: validNativeBidRes}, request[0]); + request = spec.buildRequests([validNativeBidReq], bidderRequest); + result = spec.interpretResponse({ body: validNativeBidRes }, request[0]); }) it('should build bid array', function () { expect(result.length).to.equal(1); @@ -349,7 +349,7 @@ describe('ucfunnel Adapter', function () { describe('cookie sync', function () { describe('cookie sync iframe', function () { - const result = spec.getUserSyncs({'iframeEnabled': true}, null, gdprConsent); + const result = spec.getUserSyncs({ 'iframeEnabled': true }, null, gdprConsent); it('should return cookie sync iframe info', function () { expect(result[0].type).to.equal('iframe'); @@ -357,7 +357,7 @@ describe('ucfunnel Adapter', function () { }); }); describe('cookie sync image', function () { - const result = spec.getUserSyncs({'pixelEnabled': true}, null, gdprConsent); + const result = spec.getUserSyncs({ 'pixelEnabled': true }, null, gdprConsent); it('should return cookie sync image info', function () { expect(result[0].type).to.equal('image'); expect(result[0].url).to.equal('https://sync.aralego.com/idSync?gdpr=1&euconsent-v2=CO9rhBTO9rhBTAcABBENBCCsAP_AAH_AACiQHItf_X_fb3_j-_59_9t0eY1f9_7_v20zjgeds-8Nyd_X_L8X42M7vB36pq4KuR4Eu3LBIQdlHOHcTUmw6IkVqTPsbk2Mr7NKJ7PEinMbe2dYGH9_n9XTuZKY79_s___z__-__v__7_f_r-3_3_vp9V---3YHIgEmGpfARZiWOBJNGlUKIEIVxIdACACihGFomsICVwU7K4CP0EDABAagIwIgQYgoxZBAAAAAElEQEgB4IBEARAIAAQAqQEIACNAEFgBIGAQACgGhYARQBCBIQZHBUcpgQESLRQTyVgCUXexhhCGUUANAg4AA.YAAAAAAAAAAA&'); diff --git a/test/spec/modules/uid2IdSystem_helpers.js b/test/spec/modules/uid2IdSystem_helpers.js index ec9eef1d488..433f574bc82 100644 --- a/test/spec/modules/uid2IdSystem_helpers.js +++ b/test/spec/modules/uid2IdSystem_helpers.js @@ -1,6 +1,6 @@ -import {setConsentConfig} from 'modules/consentManagementTcf.js'; -import {server} from 'test/mocks/xhr.js'; -import {coreStorage, startAuctionHook} from 'modules/userId/index.js'; +import { setConsentConfig } from 'modules/consentManagementTcf.js'; +import { server } from 'test/mocks/xhr.js'; +import { coreStorage, startAuctionHook } from 'modules/userId/index.js'; const msIn12Hours = 60 * 60 * 12 * 1000; const expireCookieDate = 'Thu, 01 Jan 1970 00:00:01 GMT'; @@ -17,17 +17,17 @@ export const runAuction = async () => { const adUnits = [{ code: 'adUnit-code', - mediaTypes: {banner: {}, native: {}}, + mediaTypes: { banner: {}, native: {} }, sizes: [[300, 200], [300, 600]], - bids: [{bidder: 'sampleBidder', params: {placementId: 'banner-only-bidder'}}] + bids: [{ bidder: 'sampleBidder', params: { placementId: 'banner-only-bidder' } }] }]; - const ortb2Fragments = {global: {}, bidder: {}}; + const ortb2Fragments = { global: {}, bidder: {} }; return new Promise(function(resolve) { startAuctionHook(function() { const bid = Object.assign({}, adUnits[0].bids[0]); bid.userIdAsEids = (ortb2Fragments.global.user?.ext?.eids ?? []).concat(ortb2Fragments.bidder[bid.bidder]?.user?.ext?.eids ?? []); resolve(bid); - }, {adUnits, ortb2Fragments}); + }, { adUnits, ortb2Fragments }); }); } diff --git a/test/spec/modules/uid2IdSystem_spec.js b/test/spec/modules/uid2IdSystem_spec.js index f8980bd3961..7ad45ecb74e 100644 --- a/test/spec/modules/uid2IdSystem_spec.js +++ b/test/spec/modules/uid2IdSystem_spec.js @@ -1,16 +1,16 @@ -import {attachIdSystem, coreStorage, init, setSubmoduleRegistry} from 'modules/userId/index.js'; -import {config} from 'src/config.js'; +import { attachIdSystem, coreStorage, init, setSubmoduleRegistry } from 'modules/userId/index.js'; +import { config } from 'src/config.js'; import * as utils from 'src/utils.js'; import { uid2IdSubmodule } from 'modules/uid2IdSystem.js'; -import {requestBids} from '../../../src/prebid.js'; +import { requestBids } from '../../../src/prebid.js'; import 'modules/consentManagementTcf.js'; import { getGlobal } from 'src/prebidGlobal.js'; import { configureTimerInterceptors } from 'test/mocks/timers.js'; import { cookieHelpers, runAuction, apiHelpers, setGdprApplies } from './uid2IdSystem_helpers.js'; -import {hook} from 'src/hook.js'; -import {uninstall as uninstallTcfControl} from 'modules/tcfControl.js'; -import {server} from 'test/mocks/xhr'; -import {createEidsArray} from '../../../modules/userId/eids.js'; +import { hook } from 'src/hook.js'; +import { uninstall as uninstallTcfControl } from 'modules/tcfControl.js'; +import { server } from 'test/mocks/xhr'; +import { createEidsArray } from '../../../modules/userId/eids.js'; const expect = require('chai').expect; @@ -26,16 +26,16 @@ const refreshedToken = 'refreshed-advertising-token'; const clientSideGeneratedToken = 'client-side-generated-advertising-token'; const optoutToken = 'optout-token'; -const legacyConfigParams = {storage: null}; +const legacyConfigParams = { storage: null }; const serverCookieConfigParams = { uid2ServerCookie: publisherCookieName }; const newServerCookieConfigParams = { uid2Cookie: publisherCookieName }; const cstgConfigParams = { serverPublicKey: 'UID2-X-L-24B8a/eLYBmRkXA9yPgRZt+ouKbXewG2OPs23+ov3JC8mtYJBCx6AxGwJ4MlwUcguebhdDp2CvzsCgS9ogwwGA==', subscriptionId: 'subscription-id' } -const makeUid2IdentityContainer = (token) => ({uid2: {id: token}}); -const makeUid2OptoutContainer = (token) => ({uid2: {optout: true}}); +const makeUid2IdentityContainer = (token) => ({ uid2: { id: token } }); +const makeUid2OptoutContainer = (token) => ({ uid2: { optout: true } }); let useLocalStorage = false; const makePrebidConfig = (params = null, extraSettings = {}, debug = false) => ({ - userSync: { auctionDelay: extraSettings.auctionDelay ?? auctionDelayMs, ...(extraSettings.syncDelay !== undefined && {syncDelay: extraSettings.syncDelay}), userIds: [{name: 'uid2', params: {storage: useLocalStorage ? 'localStorage' : 'cookie', ...params}}] }, debug + userSync: { auctionDelay: extraSettings.auctionDelay ?? auctionDelayMs, ...(extraSettings.syncDelay !== undefined && { syncDelay: extraSettings.syncDelay }), userIds: [{ name: 'uid2', params: { storage: useLocalStorage ? 'localStorage' : 'cookie', ...params } }] }, debug }); const makeOriginalIdentity = (identity, salt = 1) => ({ identity: utils.cyrb53Hash(identity, salt), @@ -184,14 +184,14 @@ describe(`UID2 module`, function () { describe('Configuration', function() { it('When no baseUrl is provided in config, the module calls the production endpoint', async function () { const uid2Token = apiHelpers.makeTokenResponse(initialToken, true, true); - config.setConfig(makePrebidConfig({uid2Token})); + config.setConfig(makePrebidConfig({ uid2Token })); await runAuction(); expect(server.requests[0]?.url).to.have.string('https://prod.uidapi.com/v2/token/refresh'); }); it('When a baseUrl is provided in config, the module calls the provided endpoint', async function () { const uid2Token = apiHelpers.makeTokenResponse(initialToken, true, true); - config.setConfig(makePrebidConfig({uid2Token, uid2ApiBase: 'https://operator-integ.uidapi.com'})); + config.setConfig(makePrebidConfig({ uid2Token, uid2ApiBase: 'https://operator-integ.uidapi.com' })); await runAuction(); expect(server.requests[0]?.url).to.have.string('https://operator-integ.uidapi.com/v2/token/refresh'); }); @@ -199,7 +199,7 @@ describe(`UID2 module`, function () { it('When a legacy value is provided directly in configuration, it is passed on', async function() { const valueConfig = makePrebidConfig(); - valueConfig.userSync.userIds[0].value = {uid2: {id: legacyToken}} + valueConfig.userSync.userIds[0].value = { uid2: { id: legacyToken } } config.setConfig(valueConfig); const bid = await runAuction(); @@ -231,14 +231,14 @@ describe(`UID2 module`, function () { it('and a server cookie is used with a valid server cookie, it should provide the server cookie', async function() { cookieHelpers.setPublisherCookie(publisherCookieName, apiHelpers.makeTokenResponse(initialToken)); await createLegacyTest(newServerCookieConfigParams, [(bid) => expectToken(bid, initialToken), expectNoLegacyToken])(); }); it('and a token is provided in config, it should provide the config token', - createLegacyTest({uid2Token: apiHelpers.makeTokenResponse(initialToken)}, [(bid) => expectToken(bid, initialToken), expectNoLegacyToken])); + createLegacyTest({ uid2Token: apiHelpers.makeTokenResponse(initialToken) }, [(bid) => expectToken(bid, initialToken), expectNoLegacyToken])); it('and GDPR applies, no identity should be provided to the auction', createLegacyTest(legacyConfigParams, [expectNoIdentity], true)); it('and GDPR applies, when getId is called directly it provides no identity', () => { coreStorage.setCookie(moduleCookieName, legacyToken, cookieHelpers.getFutureCookieExpiry()); const consentConfig = setGdprApplies(); const configObj = makePrebidConfig(legacyConfigParams); - const result = uid2IdSubmodule.getId(configObj.userSync.userIds[0], {gdpr: consentConfig.consentData}); + const result = uid2IdSubmodule.getId(configObj.userSync.userIds[0], { gdpr: consentConfig.consentData }); expect(result?.id).to.not.exist; }); @@ -264,7 +264,7 @@ describe(`UID2 module`, function () { { name: 'Token provided in config call', setConfig: (token, extraConfig = {}) => { - const gen = makePrebidConfig({uid2Token: token}, extraConfig); + const gen = makePrebidConfig({ uid2Token: token }, extraConfig); return config.setConfig(gen); }, }, @@ -325,7 +325,7 @@ describe(`UID2 module`, function () { it('the refreshed value from the cookie is used', async function() { const initialIdentity = apiHelpers.makeTokenResponse(initialToken, true, true); const refreshedIdentity = apiHelpers.makeTokenResponse(refreshedToken); - const moduleCookie = {originalToken: initialIdentity, latestToken: refreshedIdentity}; + const moduleCookie = { originalToken: initialIdentity, latestToken: refreshedIdentity }; coreStorage.setCookie(moduleCookieName, JSON.stringify(moduleCookie), cookieHelpers.getFutureCookieExpiry()); scenario.setConfig(initialIdentity); @@ -352,7 +352,7 @@ describe(`UID2 module`, function () { describe(`When a current token which should be refreshed is provided, and the auction is set to run immediately`, function() { beforeEach(function() { - scenario.setConfig(apiHelpers.makeTokenResponse(initialToken, true), {auctionDelay: 0, syncDelay: 1}); + scenario.setConfig(apiHelpers.makeTokenResponse(initialToken, true), { auctionDelay: 0, syncDelay: 1 }); }); testApiSuccessAndFailure(async function() { apiHelpers.respondAfterDelay(10, server); @@ -496,7 +496,7 @@ describe(`UID2 module`, function () { describe('When the storedToken is valid', function() { it('it should use the stored token in the auction', async function() { const refreshedIdentity = apiHelpers.makeTokenResponse(refreshedToken); - const moduleCookie = {originalIdentity: makeOriginalIdentity('test@test.com'), latestToken: refreshedIdentity}; + const moduleCookie = { originalIdentity: makeOriginalIdentity('test@test.com'), latestToken: refreshedIdentity }; coreStorage.setCookie(moduleCookieName, JSON.stringify(moduleCookie), cookieHelpers.getFutureCookieExpiry()); config.setConfig(makePrebidConfig({ ...cstgConfigParams, email: 'test@test.com', auctionDelay: 0, syncDelay: 1 })); const bid = await runAuction(); @@ -507,7 +507,7 @@ describe(`UID2 module`, function () { describe('When the storedToken is expired and can be refreshed ', function() { testApiSuccessAndFailure(async function(apiSucceeds) { const refreshedIdentity = apiHelpers.makeTokenResponse(refreshedToken, true, true); - const moduleCookie = {originalIdentity: makeOriginalIdentity('test@test.com'), latestToken: refreshedIdentity}; + const moduleCookie = { originalIdentity: makeOriginalIdentity('test@test.com'), latestToken: refreshedIdentity }; coreStorage.setCookie(moduleCookieName, JSON.stringify(moduleCookie), cookieHelpers.getFutureCookieExpiry()); config.setConfig(makePrebidConfig({ ...cstgConfigParams, email: 'test@test.com' })); apiHelpers.respondAfterDelay(auctionDelayMs / 10, server); @@ -522,7 +522,7 @@ describe(`UID2 module`, function () { describe('When the storedToken is expired for refresh', function() { testApiSuccessAndFailure(async function(apiSucceeds) { const refreshedIdentity = apiHelpers.makeTokenResponse(refreshedToken, true, true, true); - const moduleCookie = {originalIdentity: makeOriginalIdentity('test@test.com'), latestToken: refreshedIdentity}; + const moduleCookie = { originalIdentity: makeOriginalIdentity('test@test.com'), latestToken: refreshedIdentity }; coreStorage.setCookie(moduleCookieName, JSON.stringify(moduleCookie), cookieHelpers.getFutureCookieExpiry()); config.setConfig(makePrebidConfig({ ...cstgConfigParams, email: 'test@test.com' })); apiHelpers.respondAfterDelay(auctionDelayMs / 10, server); @@ -537,7 +537,7 @@ describe(`UID2 module`, function () { it('when originalIdentity not match, the auction should has no uid2', async function() { const refreshedIdentity = apiHelpers.makeTokenResponse(refreshedToken); - const moduleCookie = {originalIdentity: makeOriginalIdentity('123@test.com'), latestToken: refreshedIdentity}; + const moduleCookie = { originalIdentity: makeOriginalIdentity('123@test.com'), latestToken: refreshedIdentity }; coreStorage.setCookie(moduleCookieName, JSON.stringify(moduleCookie), cookieHelpers.getFutureCookieExpiry()); config.setConfig(makePrebidConfig({ ...cstgConfigParams, email: 'test@test.com' })); const bid = await runAuction(); @@ -613,7 +613,7 @@ describe(`UID2 module`, function () { describe('when there is a non-cstg-derived token in the module cookie', function () { it('the auction use stored token if it is valid', async function () { const originalIdentity = apiHelpers.makeTokenResponse(initialToken); - const moduleCookie = {originalToken: originalIdentity, latestToken: originalIdentity}; + const moduleCookie = { originalToken: originalIdentity, latestToken: originalIdentity }; coreStorage.setCookie(moduleCookieName, JSON.stringify(moduleCookie), cookieHelpers.getFutureCookieExpiry()); config.setConfig(makePrebidConfig({})); const bid = await runAuction(); @@ -622,7 +622,7 @@ describe(`UID2 module`, function () { it('the auction should has no uid2 if stored token is invalid', async function () { const originalIdentity = apiHelpers.makeTokenResponse(initialToken, true, true, true); - const moduleCookie = {originalToken: originalIdentity, latestToken: originalIdentity}; + const moduleCookie = { originalToken: originalIdentity, latestToken: originalIdentity }; coreStorage.setCookie(moduleCookieName, JSON.stringify(moduleCookie), cookieHelpers.getFutureCookieExpiry()); config.setConfig(makePrebidConfig({})); const bid = await runAuction(); @@ -633,7 +633,7 @@ describe(`UID2 module`, function () { describe('when there is a cstg-derived token in the module cookie', function () { it('the auction use stored token if it is valid', async function () { const originalIdentity = apiHelpers.makeTokenResponse(initialToken); - const moduleCookie = {originalIdentity: makeOriginalIdentity('123@test.com'), originalToken: originalIdentity, latestToken: originalIdentity}; + const moduleCookie = { originalIdentity: makeOriginalIdentity('123@test.com'), originalToken: originalIdentity, latestToken: originalIdentity }; coreStorage.setCookie(moduleCookieName, JSON.stringify(moduleCookie), cookieHelpers.getFutureCookieExpiry()); config.setConfig(makePrebidConfig({})); const bid = await runAuction(); @@ -642,7 +642,7 @@ describe(`UID2 module`, function () { it('the auction should has no uid2 if stored token is invalid', async function () { const originalIdentity = apiHelpers.makeTokenResponse(initialToken, true, true, true); - const moduleCookie = {originalIdentity: makeOriginalIdentity('123@test.com'), originalToken: originalIdentity, latestToken: originalIdentity}; + const moduleCookie = { originalIdentity: makeOriginalIdentity('123@test.com'), originalToken: originalIdentity, latestToken: originalIdentity }; coreStorage.setCookie(moduleCookieName, JSON.stringify(moduleCookie), cookieHelpers.getFutureCookieExpiry()); config.setConfig(makePrebidConfig({})); const bid = await runAuction(); @@ -659,7 +659,7 @@ describe(`UID2 module`, function () { describe('eid', () => { it('uid2', function() { const userId = { - uid2: {'id': 'Sample_AD_Token'} + uid2: { 'id': 'Sample_AD_Token' } }; const newEids = createEidsArray(userId); expect(newEids.length).to.equal(1); @@ -674,7 +674,7 @@ describe(`UID2 module`, function () { it('uid2 with ext', function() { const userId = { - uid2: {'id': 'Sample_AD_Token', 'ext': {'provider': 'some.provider.com'}} + uid2: { 'id': 'Sample_AD_Token', 'ext': { 'provider': 'some.provider.com' } } }; const newEids = createEidsArray(userId); expect(newEids.length).to.equal(1); diff --git a/test/spec/modules/undertoneBidAdapter_spec.js b/test/spec/modules/undertoneBidAdapter_spec.js index ed531371af7..d8326ab64c8 100644 --- a/test/spec/modules/undertoneBidAdapter_spec.js +++ b/test/spec/modules/undertoneBidAdapter_spec.js @@ -1,7 +1,9 @@ -import {expect} from 'chai'; -import {spec} from 'modules/undertoneBidAdapter.js'; -import {BANNER, VIDEO} from '../../../src/mediaTypes.js'; -import {deepClone, getWinDimensions} from '../../../src/utils.js'; +import { expect } from 'chai'; +import { spec } from 'modules/undertoneBidAdapter.js'; +import { BANNER, VIDEO } from '../../../src/mediaTypes.js'; +import { deepClone, getWinDimensions } from '../../../src/utils.js'; +import * as adUnits from 'src/utils/adUnits'; +import { getAdUnitElement } from 'src/utils/adUnits'; const URL = 'https://hb.undertone.com/hb'; const BIDDER_CODE = 'undertone'; @@ -153,7 +155,7 @@ const bidReqUserIds = [{ userId: { idl_env: '1111', tdid: '123456', - digitrustid: {data: {id: 'DTID', keyv: 4, privacy: {optout: false}, producer: 'ABC', version: 2}}, + digitrustid: { data: { id: 'DTID', keyv: 4, privacy: { optout: false }, producer: 'ABC', version: 2 } }, id5id: { uid: '1111' } } }, @@ -314,7 +316,7 @@ describe('Undertone Adapter', () => { }; sandbox = sinon.createSandbox(); - sandbox.stub(document, 'getElementById').withArgs('div-gpt-ad-1460505748561-0').returns(element); + sandbox.stub(adUnits, 'getAdUnitElement').returns(element); }); afterEach(function() { @@ -525,12 +527,12 @@ describe('Undertone Adapter', () => { describe('interpretResponse', () => { it('should build bid array', () => { - const result = spec.interpretResponse({body: bidResponse}); + const result = spec.interpretResponse({ body: bidResponse }); expect(result.length).to.equal(1); }); it('should have all relevant fields', () => { - const result = spec.interpretResponse({body: bidResponse}); + const result = spec.interpretResponse({ body: bidResponse }); const bid = result[0]; expect(bid.requestId).to.equal('263be71e91dd9d'); @@ -545,8 +547,8 @@ describe('Undertone Adapter', () => { }); it('should return empty array when response is incorrect', () => { - expect(spec.interpretResponse({body: {}}).length).to.equal(0); - expect(spec.interpretResponse({body: []}).length).to.equal(0); + expect(spec.interpretResponse({ body: {} }).length).to.equal(0); + expect(spec.interpretResponse({ body: [] }).length).to.equal(0); }); it('should only use valid bid responses', () => { @@ -554,7 +556,7 @@ describe('Undertone Adapter', () => { }); it('should detect video response', () => { - const videoResult = spec.interpretResponse({body: bidVideoResponse}); + const videoResult = spec.interpretResponse({ body: bidVideoResponse }); const vbid = videoResult[0]; expect(vbid.mediaType).to.equal('video'); @@ -573,7 +575,7 @@ describe('Undertone Adapter', () => { }, { name: 'with iframe and gdpr on', - arguments: [{ iframeEnabled: true, pixelEnabled: true }, {}, {gdprApplies: true, consentString: '234234'}], + arguments: [{ iframeEnabled: true, pixelEnabled: true }, {}, { gdprApplies: true, consentString: '234234' }], expect: { type: 'iframe', pixels: ['https://cdn.undertone.com/js/usersync.html?gdpr=1&gdprstr=234234'] @@ -589,7 +591,7 @@ describe('Undertone Adapter', () => { }, { name: 'with iframe and no gdpr off or ccpa', - arguments: [{ iframeEnabled: true, pixelEnabled: true }, {}, {gdprApplies: false}], + arguments: [{ iframeEnabled: true, pixelEnabled: true }, {}, { gdprApplies: false }], expect: { type: 'iframe', pixels: ['https://cdn.undertone.com/js/usersync.html?gdpr=0&gdprstr='] @@ -597,7 +599,7 @@ describe('Undertone Adapter', () => { }, { name: 'with iframe and gdpr and ccpa', - arguments: [{ iframeEnabled: true, pixelEnabled: true }, {}, {gdprApplies: true, consentString: '234234'}, 'YN12'], + arguments: [{ iframeEnabled: true, pixelEnabled: true }, {}, { gdprApplies: true, consentString: '234234' }, 'YN12'], expect: { type: 'iframe', pixels: ['https://cdn.undertone.com/js/usersync.html?gdpr=1&gdprstr=234234&ccpa=YN12'] @@ -614,7 +616,7 @@ describe('Undertone Adapter', () => { }, { name: 'with pixels and gdpr on', - arguments: [{ pixelEnabled: true }, {}, {gdprApplies: true, consentString: '234234'}], + arguments: [{ pixelEnabled: true }, {}, { gdprApplies: true, consentString: '234234' }], expect: { type: 'image', pixels: ['https://usr.undertone.com/userPixel/syncOne?id=1&of=2&gdpr=1&gdprstr=234234', @@ -632,7 +634,7 @@ describe('Undertone Adapter', () => { }, { name: 'with pixels and gdpr off', - arguments: [{ pixelEnabled: true }, {}, {gdprApplies: false}], + arguments: [{ pixelEnabled: true }, {}, { gdprApplies: false }], expect: { type: 'image', pixels: ['https://usr.undertone.com/userPixel/syncOne?id=1&of=2&gdpr=0&gdprstr=', @@ -641,7 +643,7 @@ describe('Undertone Adapter', () => { }, { name: 'with pixels and gdpr and ccpa on', - arguments: [{ pixelEnabled: true }, {}, {gdprApplies: true, consentString: '234234'}, 'YN12'], + arguments: [{ pixelEnabled: true }, {}, { gdprApplies: true, consentString: '234234' }, 'YN12'], expect: { type: 'image', pixels: ['https://usr.undertone.com/userPixel/syncOne?id=1&of=2&gdpr=1&gdprstr=234234&ccpa=YN12', diff --git a/test/spec/modules/unicornBidAdapter_spec.js b/test/spec/modules/unicornBidAdapter_spec.js index 7e6d41fc85e..abf3bac8e88 100644 --- a/test/spec/modules/unicornBidAdapter_spec.js +++ b/test/spec/modules/unicornBidAdapter_spec.js @@ -1,8 +1,8 @@ -import {assert, expect} from 'chai'; +import { assert, expect } from 'chai'; import * as utils from 'src/utils.js'; -import {spec} from 'modules/unicornBidAdapter.js'; +import { spec } from 'modules/unicornBidAdapter.js'; import * as _ from 'lodash'; -import {getGlobal} from '../../../src/prebidGlobal.js'; +import { getGlobal } from '../../../src/prebidGlobal.js'; const bidRequests = [ { diff --git a/test/spec/modules/unifiedIdSystem_spec.js b/test/spec/modules/unifiedIdSystem_spec.js index b5d13c57f5c..b7d5426f337 100644 --- a/test/spec/modules/unifiedIdSystem_spec.js +++ b/test/spec/modules/unifiedIdSystem_spec.js @@ -1,17 +1,17 @@ -import {attachIdSystem} from '../../../modules/userId/index.js'; -import {unifiedIdSubmodule} from '../../../modules/unifiedIdSystem.js'; -import {createEidsArray} from '../../../modules/userId/eids.js'; -import {expect} from 'chai/index.mjs'; -import {server} from 'test/mocks/xhr.js'; +import { attachIdSystem } from '../../../modules/userId/index.js'; +import { unifiedIdSubmodule } from '../../../modules/unifiedIdSystem.js'; +import { createEidsArray } from '../../../modules/userId/eids.js'; +import { expect } from 'chai/index.mjs'; +import { server } from 'test/mocks/xhr.js'; describe('Unified ID', () => { describe('getId', () => { it('should use provided URL', () => { - unifiedIdSubmodule.getId({params: {url: 'https://given-url/'}}).callback(); + unifiedIdSubmodule.getId({ params: { url: 'https://given-url/' } }).callback(); expect(server.requests[0].url).to.eql('https://given-url/'); }); it('should use partner URL', () => { - unifiedIdSubmodule.getId({params: {partner: 'rubicon'}}).callback(); + unifiedIdSubmodule.getId({ params: { partner: 'rubicon' } }).callback(); expect(server.requests[0].url).to.equal('https://match.adsrvr.org/track/rid?ttd_pid=rubicon&fmt=json'); }); }); @@ -30,13 +30,13 @@ describe('Unified ID', () => { inserter: 'adserver.org', matcher: 'adserver.org', mm: 4, - uids: [{id: 'some-random-id-value', atype: 1, ext: {rtiPartner: 'TDID'}}] + uids: [{ id: 'some-random-id-value', atype: 1, ext: { rtiPartner: 'TDID' } }] }); }); it('unifiedId: ext generation with provider', function () { const userId = { - tdid: {'id': 'some-sample_id', 'ext': {'provider': 'some.provider.com'}} + tdid: { 'id': 'some-sample_id', 'ext': { 'provider': 'some.provider.com' } } }; const newEids = createEidsArray(userId); expect(newEids.length).to.equal(1); @@ -45,7 +45,7 @@ describe('Unified ID', () => { inserter: 'adserver.org', matcher: 'adserver.org', mm: 4, - uids: [{id: 'some-sample_id', atype: 1, ext: {rtiPartner: 'TDID', provider: 'some.provider.com'}}] + uids: [{ id: 'some-sample_id', atype: 1, ext: { rtiPartner: 'TDID', provider: 'some.provider.com' } }] }); }); diff --git a/test/spec/modules/uniquestAnalyticsAdapter_spec.js b/test/spec/modules/uniquestAnalyticsAdapter_spec.js index 80a573d2b0f..88120c8e59e 100644 --- a/test/spec/modules/uniquestAnalyticsAdapter_spec.js +++ b/test/spec/modules/uniquestAnalyticsAdapter_spec.js @@ -1,7 +1,7 @@ import uniquestAnalyticsAdapter from 'modules/uniquestAnalyticsAdapter.js'; -import {config} from 'src/config'; -import {EVENTS} from 'src/constants.js'; -import {server} from '../../mocks/xhr.js'; +import { config } from 'src/config'; +import { EVENTS } from 'src/constants.js'; +import { server } from '../../mocks/xhr.js'; const events = require('src/events'); diff --git a/test/spec/modules/unrulyBidAdapter_spec.js b/test/spec/modules/unrulyBidAdapter_spec.js index d73b9b6e8c7..20cf4b52708 100644 --- a/test/spec/modules/unrulyBidAdapter_spec.js +++ b/test/spec/modules/unrulyBidAdapter_spec.js @@ -1,9 +1,9 @@ /* globals describe, it, beforeEach, afterEach, sinon */ -import {expect} from 'chai' +import { expect } from 'chai' import * as utils from 'src/utils.js' -import {VIDEO, BANNER} from 'src/mediaTypes.js' -import {Renderer} from 'src/Renderer.js' -import {adapter} from 'modules/unrulyBidAdapter.js' +import { VIDEO, BANNER } from 'src/mediaTypes.js' +import { Renderer } from 'src/Renderer.js' +import { adapter } from 'modules/unrulyBidAdapter.js' describe('UnrulyAdapter', function () { function createOutStreamExchangeBid({ @@ -42,20 +42,7 @@ describe('UnrulyAdapter', function () { } } - function createOutStreamExchangeAuctionConfig() { - return { - 'seller': 'https://nexxen.tech', - 'decisionLogicURL': 'https://nexxen.tech/padecisionlogic', - 'interestGroupBuyers': 'https://mydsp.com', - 'perBuyerSignals': { - 'https://mydsp.com': { - 'floor': 'bouttreefiddy' - } - } - } - }; - - function createExchangeResponse (bidList, auctionConfigs = null) { + function createExchangeResponse (bidList) { let bids = []; if (Array.isArray(bidList)) { bids = bidList; @@ -63,18 +50,9 @@ describe('UnrulyAdapter', function () { bids.push(bidList); } - if (!auctionConfigs) { - return { - 'body': {bids} - }; - } - return { - 'body': { - bids, - auctionConfigs - } - } + 'body': { bids } + }; }; const inStreamServerResponse = { @@ -641,7 +619,7 @@ describe('UnrulyAdapter', function () { }; const getFloor = (data) => { - return {floor: 3} + return { floor: 3 } }; mockBidRequests.bids[0].getFloor = getFloor; @@ -692,231 +670,6 @@ describe('UnrulyAdapter', function () { const result = adapter.buildRequests(mockBidRequests.bids, mockBidRequests); expect(result[0].data).to.deep.equal(expectedResult); }); - describe('Protected Audience Support', function() { - it('should return an array with 2 items and enabled protected audience', function () { - mockBidRequests = { - 'bidderCode': 'unruly', - 'paapi': { - enabled: true - }, - 'bids': [ - { - 'bidder': 'unruly', - 'params': { - 'siteId': 233261, - }, - 'mediaTypes': { - 'video': { - 'context': 'outstream', - 'mimes': [ - 'video/mp4' - ], - 'playerSize': [ - [ - 640, - 480 - ] - ] - } - }, - 'adUnitCode': 'video2', - 'transactionId': 'a89619e3-137d-4cc5-9ed4-58a0b2a0bbc2', - 'sizes': [ - [ - 640, - 480 - ] - ], - 'bidId': '27a3ee1626a5c7', - 'bidderRequestId': '12e00d17dff07b', - 'ortb2Imp': { - 'ext': { - 'ae': 1 - } - } - }, - { - 'bidder': 'unruly', - 'params': { - 'siteId': 2234554, - }, - 'mediaTypes': { - 'video': { - 'context': 'outstream', - 'mimes': [ - 'video/mp4' - ], - 'playerSize': [ - [ - 640, - 480 - ] - ] - } - }, - 'adUnitCode': 'video2', - 'transactionId': 'a89619e3-137d-4cc5-9ed4-58a0b2a0bbc2', - 'sizes': [ - [ - 640, - 480 - ] - ], - 'bidId': '27a3ee1626a5c7', - 'bidderRequestId': '12e00d17dff07b', - 'ortb2Imp': { - 'ext': { - 'ae': 1 - } - } - } - ] - }; - - const result = adapter.buildRequests(mockBidRequests.bids, mockBidRequests); - expect(typeof result).to.equal('object'); - expect(result.length).to.equal(2); - expect(result[0].data.bidderRequest.bids.length).to.equal(1); - expect(result[1].data.bidderRequest.bids.length).to.equal(1); - expect(result[0].data.bidderRequest.bids[0].ortb2Imp.ext.ae).to.equal(1); - expect(result[1].data.bidderRequest.bids[0].ortb2Imp.ext.ae).to.equal(1); - }); - it('should return an array with 2 items and enabled protected audience on only one unit', function () { - mockBidRequests = { - 'bidderCode': 'unruly', - 'paapi': { - enabled: true - }, - 'bids': [ - { - 'bidder': 'unruly', - 'params': { - 'siteId': 233261, - }, - 'mediaTypes': { - 'video': { - 'context': 'outstream', - 'mimes': [ - 'video/mp4' - ], - 'playerSize': [ - [ - 640, - 480 - ] - ] - } - }, - 'adUnitCode': 'video2', - 'transactionId': 'a89619e3-137d-4cc5-9ed4-58a0b2a0bbc2', - 'sizes': [ - [ - 640, - 480 - ] - ], - 'bidId': '27a3ee1626a5c7', - 'bidderRequestId': '12e00d17dff07b', - 'ortb2Imp': { - 'ext': { - 'ae': 1 - } - } - }, - { - 'bidder': 'unruly', - 'params': { - 'siteId': 2234554, - }, - 'mediaTypes': { - 'video': { - 'context': 'outstream', - 'mimes': [ - 'video/mp4' - ], - 'playerSize': [ - [ - 640, - 480 - ] - ] - } - }, - 'adUnitCode': 'video2', - 'transactionId': 'a89619e3-137d-4cc5-9ed4-58a0b2a0bbc2', - 'sizes': [ - [ - 640, - 480 - ] - ], - 'bidId': '27a3ee1626a5c7', - 'bidderRequestId': '12e00d17dff07b', - 'ortb2Imp': { - 'ext': {} - } - } - ] - }; - - const result = adapter.buildRequests(mockBidRequests.bids, mockBidRequests); - expect(typeof result).to.equal('object'); - expect(result.length).to.equal(2); - expect(result[0].data.bidderRequest.bids.length).to.equal(1); - expect(result[1].data.bidderRequest.bids.length).to.equal(1); - expect(result[0].data.bidderRequest.bids[0].ortb2Imp.ext.ae).to.equal(1); - expect(result[1].data.bidderRequest.bids[0].ortb2Imp.ext.ae).to.be.undefined; - }); - it('disables configured protected audience when fledge is not availble', function () { - mockBidRequests = { - 'bidderCode': 'unruly', - 'fledgeEnabled': false, - 'bids': [ - { - 'bidder': 'unruly', - 'params': { - 'siteId': 233261, - }, - 'mediaTypes': { - 'video': { - 'context': 'outstream', - 'mimes': [ - 'video/mp4' - ], - 'playerSize': [ - [ - 640, - 480 - ] - ] - } - }, - 'adUnitCode': 'video2', - 'transactionId': 'a89619e3-137d-4cc5-9ed4-58a0b2a0bbc2', - 'sizes': [ - [ - 640, - 480 - ] - ], - 'bidId': '27a3ee1626a5c7', - 'bidderRequestId': '12e00d17dff07b', - 'ortb2Imp': { - 'ext': { - 'ae': 1 - } - } - } - ] - }; - - const result = adapter.buildRequests(mockBidRequests.bids, mockBidRequests); - expect(typeof result).to.equal('object'); - expect(result.length).to.equal(1); - expect(result[0].data.bidderRequest.bids.length).to.equal(1); - expect(result[0].data.bidderRequest.bids[0].ortb2Imp.ext.ae).to.be.undefined; - }); - }); }); describe('interpretResponse', function () { @@ -927,11 +680,11 @@ describe('UnrulyAdapter', function () { expect(adapter.interpretResponse()).to.deep.equal([]); }); it('should return [] when serverResponse has no bids', function () { - const mockServerResponse = {body: {bids: []}}; + const mockServerResponse = { body: { bids: [] } }; expect(adapter.interpretResponse(mockServerResponse)).to.deep.equal([]) }); it('should return array of bids when receive a successful response from server', function () { - const mockExchangeBid = createOutStreamExchangeBid({adUnitCode: 'video1', requestId: 'mockBidId'}); + const mockExchangeBid = createOutStreamExchangeBid({ adUnitCode: 'video1', requestId: 'mockBidId' }); const mockServerResponse = createExchangeResponse(mockExchangeBid); expect(adapter.interpretResponse(mockServerResponse)).to.deep.equal([ { @@ -967,171 +720,11 @@ describe('UnrulyAdapter', function () { ]); }); - it('should return object with an array of bids and an array of auction configs when it receives a successful response from server', function () { - const bidId = '27a3ee1626a5c7' - const mockExchangeBid = createOutStreamExchangeBid({adUnitCode: 'video1', requestId: 'mockBidId'}); - const mockExchangeAuctionConfig = {}; - mockExchangeAuctionConfig[bidId] = createOutStreamExchangeAuctionConfig(); - const mockServerResponse = createExchangeResponse(mockExchangeBid, mockExchangeAuctionConfig); - const originalRequest = { - 'data': { - 'bidderRequest': { - 'bids': [ - { - 'bidder': 'unruly', - 'params': { - 'siteId': 233261, - }, - 'mediaTypes': { - 'banner': { - 'sizes': [ - [ - 640, - 480 - ], - [ - 640, - 480 - ], - [ - 300, - 250 - ], - [ - 300, - 250 - ] - ] - } - }, - 'adUnitCode': 'video2', - 'transactionId': 'a89619e3-137d-4cc5-9ed4-58a0b2a0bbc2', - 'bidId': bidId, - 'bidderRequestId': '12e00d17dff07b', - } - ] - } - } - }; - - expect(adapter.interpretResponse(mockServerResponse, originalRequest)).to.deep.equal({ - 'bids': [ - { - 'ext': { - 'statusCode': 1, - 'renderer': { - 'id': 'unruly_inarticle', - 'config': { - 'siteId': 123456, - 'targetingUUID': 'xxx-yyy-zzz' - }, - 'url': 'https://video.unrulymedia.com/native/prebid-loader.js' - }, - 'adUnitCode': 'video1' - }, - requestId: 'mockBidId', - bidderCode: 'unruly', - cpm: 20, - width: 323, - height: 323, - vastUrl: 'https://targeting.unrulymedia.com/in_article?uuid=74544e00-d43b-4f3a-a799-69d22ce979ce&supported_mime_type=application/javascript&supported_mime_type=video/mp4&tj=%7B%22site%22%3A%7B%22lang%22%3A%22en-GB%22%2C%22ref%22%3A%22%22%2C%22page%22%3A%22https%3A%2F%2Fdemo.unrulymedia.com%2FinArticle%2Finarticle_nypost_upbeat%2Ftravel_magazines.html%22%2C%22domain%22%3A%22demo.unrulymedia.com%22%7D%2C%22user%22%3A%7B%22profile%22%3A%7B%22quantcast%22%3A%7B%22segments%22%3A%5B%7B%22id%22%3A%22D%22%7D%2C%7B%22id%22%3A%22T%22%7D%5D%7D%7D%7D%7D&video_width=618&video_height=347', - netRevenue: true, - creativeId: 'mockBidId', - ttl: 360, - 'meta': { - 'mediaType': 'video', - 'videoContext': 'outstream' - }, - currency: 'USD', - renderer: fakeRenderer, - mediaType: 'video' - } - ], - 'paapi': [{ - 'bidId': bidId, - 'config': { - 'seller': 'https://nexxen.tech', - 'decisionLogicURL': 'https://nexxen.tech/padecisionlogic', - 'interestGroupBuyers': 'https://mydsp.com', - 'perBuyerSignals': { - 'https://mydsp.com': { - 'floor': 'bouttreefiddy' - } - } - } - }] - }); - }); - - it('should return object with an array of auction configs when it receives a successful response from server without bids', function () { - const bidId = '27a3ee1626a5c7'; - const mockExchangeAuctionConfig = {}; - mockExchangeAuctionConfig[bidId] = createOutStreamExchangeAuctionConfig(); - const mockServerResponse = createExchangeResponse(null, mockExchangeAuctionConfig); - const originalRequest = { - 'data': { - 'bidderRequest': { - 'bids': [ - { - 'bidder': 'unruly', - 'params': { - 'siteId': 233261, - }, - 'mediaTypes': { - 'banner': { - 'sizes': [ - [ - 640, - 480 - ], - [ - 640, - 480 - ], - [ - 300, - 250 - ], - [ - 300, - 250 - ] - ] - } - }, - 'adUnitCode': 'video2', - 'transactionId': 'a89619e3-137d-4cc5-9ed4-58a0b2a0bbc2', - 'bidId': bidId, - 'bidderRequestId': '12e00d17dff07b' - } - ] - } - } - }; - - expect(adapter.interpretResponse(mockServerResponse, originalRequest)).to.deep.equal({ - 'bids': [], - 'paapi': [{ - 'bidId': bidId, - 'config': { - 'seller': 'https://nexxen.tech', - 'decisionLogicURL': 'https://nexxen.tech/padecisionlogic', - 'interestGroupBuyers': 'https://mydsp.com', - 'perBuyerSignals': { - 'https://mydsp.com': { - 'floor': 'bouttreefiddy' - } - } - } - }] - }); - }); - 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', requestId: 'mockBidId'}); + const mockReturnedBid = createOutStreamExchangeBid({ adUnitCode: 'video1', requestId: 'mockBidId' }); const mockRenderer = { url: 'value: mockRendererURL', config: { @@ -1160,7 +753,7 @@ describe('UnrulyAdapter', function () { expect(Renderer.install.called).to.be.false; expect(fakeRenderer.setRender.called).to.be.false; - const mockReturnedBid = createOutStreamExchangeBid({adUnitCode: 'video1', requestId: 'mockBidId'}); + const mockReturnedBid = createOutStreamExchangeBid({ adUnitCode: 'video1', requestId: 'mockBidId' }); const mockRenderer = { url: 'value: mockRendererURL' }; @@ -1184,7 +777,7 @@ describe('UnrulyAdapter', function () { expect(Renderer.install.called).to.be.false; expect(fakeRenderer.setRender.called).to.be.false; - const mockReturnedBid = createOutStreamExchangeBid({adUnitCode: 'video1', requestId: 'mockBidId'}); + const mockReturnedBid = createOutStreamExchangeBid({ adUnitCode: 'video1', requestId: 'mockBidId' }); const mockRenderer = { url: 'value: mockRendererURL', config: {} @@ -1204,7 +797,7 @@ describe('UnrulyAdapter', function () { }); it('bid is placed on the bid queue when render is called', function () { - const exchangeBid = createOutStreamExchangeBid({adUnitCode: 'video', vastUrl: 'value: vastUrl'}); + const exchangeBid = createOutStreamExchangeBid({ adUnitCode: 'video', vastUrl: 'value: vastUrl' }); const exchangeResponse = createExchangeResponse(exchangeBid); adapter.interpretResponse(exchangeResponse); @@ -1224,7 +817,7 @@ describe('UnrulyAdapter', function () { }); it('should ensure that renderer is placed in Prebid supply mode', function () { - const mockExchangeBid = createOutStreamExchangeBid({adUnitCode: 'video1', requestId: 'mockBidId'}); + const mockExchangeBid = createOutStreamExchangeBid({ adUnitCode: 'video1', requestId: 'mockBidId' }); const mockServerResponse = createExchangeResponse(mockExchangeBid); expect('unruly' in window.parent).to.equal(false); @@ -1245,7 +838,7 @@ describe('UnrulyAdapter', function () { }); it('should return correct response when ad type is instream with vastXml', function () { - const mockServerResponse = {...createExchangeResponse(inStreamServerResponseWithVastXml)}; + const mockServerResponse = { ...createExchangeResponse(inStreamServerResponseWithVastXml) }; const expectedResponse = inStreamServerResponseWithVastXml; expectedResponse.mediaType = 'video'; @@ -1253,7 +846,7 @@ describe('UnrulyAdapter', function () { }); it('should return [] and log if no vastUrl in instream response', function () { - const {vastUrl, ...inStreamServerResponseNoVast} = inStreamServerResponse; + const { vastUrl, ...inStreamServerResponseNoVast } = inStreamServerResponse; const mockServerResponse = createExchangeResponse(inStreamServerResponseNoVast); expect(adapter.interpretResponse(mockServerResponse)).to.deep.equal([]); @@ -1277,7 +870,7 @@ describe('UnrulyAdapter', function () { }); it('should return [] and log if no ad in banner response', function () { - const {ad, ...bannerServerResponseNoAd} = bannerServerResponse; + const { ad, ...bannerServerResponseNoAd } = bannerServerResponse; const mockServerResponse = createExchangeResponse(bannerServerResponseNoAd); expect(adapter.interpretResponse(mockServerResponse)).to.deep.equal([]); @@ -1293,7 +886,7 @@ describe('UnrulyAdapter', function () { }); it('should return correct response for multiple bids', function () { - const outStreamServerResponse = createOutStreamExchangeBid({adUnitCode: 'video1', requestId: 'mockBidId'}); + const outStreamServerResponse = createOutStreamExchangeBid({ adUnitCode: 'video1', requestId: 'mockBidId' }); const mockServerResponse = createExchangeResponse([outStreamServerResponse, inStreamServerResponse, bannerServerResponse]); const expectedOutStreamResponse = outStreamServerResponse; expectedOutStreamResponse.mediaType = 'video'; @@ -1308,7 +901,7 @@ describe('UnrulyAdapter', function () { }); it('should return only valid bids', function () { - const {ad, ...bannerServerResponseNoAd} = bannerServerResponse; + const { ad, ...bannerServerResponseNoAd } = bannerServerResponse; const mockServerResponse = createExchangeResponse([bannerServerResponseNoAd, inStreamServerResponse]); const expectedInStreamResponse = inStreamServerResponse; expectedInStreamResponse.mediaType = 'video'; diff --git a/test/spec/modules/userId_spec.js b/test/spec/modules/userId_spec.js index 8d7c66690b0..15ed94ff962 100644 --- a/test/spec/modules/userId_spec.js +++ b/test/spec/modules/userId_spec.js @@ -14,29 +14,29 @@ import { COOKIE_SUFFIXES, HTML5_SUFFIXES, syncDelay, adUnitEidsHook, } from 'modules/userId/index.js'; -import {UID1_EIDS} from 'libraries/uid1Eids/uid1Eids.js'; -import {createEidsArray, EID_CONFIG, getEids} from 'modules/userId/eids.js'; -import {config} from 'src/config.js'; +import { UID1_EIDS } from 'libraries/uid1Eids/uid1Eids.js'; +import { createEidsArray, EID_CONFIG, getEids } from 'modules/userId/eids.js'; +import { config } from 'src/config.js'; import * as utils from 'src/utils.js'; import * as events from 'src/events.js'; -import {EVENTS} from 'src/constants.js'; -import {getGlobal} from 'src/prebidGlobal.js'; -import {resetConsentData, } from 'modules/consentManagementTcf.js'; -import {setEventFiredFlag as liveIntentIdSubmoduleDoNotFireEvent} from '../../../libraries/liveIntentId/idSystem.js'; -import {sharedIdSystemSubmodule} from 'modules/sharedIdSystem.js'; -import {pubProvidedIdSubmodule} from 'modules/pubProvidedIdSystem.js'; +import { EVENTS } from 'src/constants.js'; +import { getGlobal } from 'src/prebidGlobal.js'; +import { resetConsentData, } from 'modules/consentManagementTcf.js'; +import { setEventFiredFlag as liveIntentIdSubmoduleDoNotFireEvent } from '../../../libraries/liveIntentId/idSystem.js'; +import { sharedIdSystemSubmodule } from 'modules/sharedIdSystem.js'; +import { pubProvidedIdSubmodule } from 'modules/pubProvidedIdSystem.js'; import * as mockGpt from '../integration/faker/googletag.js'; -import {requestBids, startAuction} from 'src/prebid.js'; -import {hook} from '../../../src/hook.js'; -import {mockGdprConsent} from '../../helpers/consentData.js'; -import {getPPID} from '../../../src/adserver.js'; -import {uninstall as uninstallTcfControl} from 'modules/tcfControl.js'; -import {allConsent, GDPR_GVLIDS, gdprDataHandler} from '../../../src/consentHandler.js'; -import {MODULE_TYPE_UID} from '../../../src/activities/modules.js'; -import {ACTIVITY_ENRICH_EIDS} from '../../../src/activities/activities.js'; -import {ACTIVITY_PARAM_COMPONENT_NAME, ACTIVITY_PARAM_COMPONENT_TYPE} from '../../../src/activities/params.js'; -import {extractEids} from '../../../modules/prebidServerBidAdapter/bidderConfig.js'; -import {generateSubmoduleContainers, addIdData } from '../../../modules/userId/index.js'; +import { requestBids, startAuction } from 'src/prebid.js'; +import { hook } from '../../../src/hook.js'; +import { mockGdprConsent } from '../../helpers/consentData.js'; +import { getPPID } from '../../../src/adserver.js'; +import { uninstall as uninstallTcfControl } from 'modules/tcfControl.js'; +import { allConsent, GDPR_GVLIDS, gdprDataHandler } from '../../../src/consentHandler.js'; +import { MODULE_TYPE_UID } from '../../../src/activities/modules.js'; +import { ACTIVITY_ENRICH_EIDS } from '../../../src/activities/activities.js'; +import { ACTIVITY_PARAM_COMPONENT_NAME, ACTIVITY_PARAM_COMPONENT_TYPE } from '../../../src/activities/params.js'; +import { extractEids } from '../../../modules/prebidServerBidAdapter/bidderConfig.js'; +import { generateSubmoduleContainers, addIdData } from '../../../modules/userId/index.js'; import { registerActivityControl } from '../../../src/activities/rules.js'; import { discloseStorageUse, @@ -62,12 +62,12 @@ describe('User ID', function () { } function getStorageMock(name = 'pubCommonId', key = 'pubcid', type = 'cookie', expires = 30, refreshInSeconds) { - return {name: name, storage: {name: key, type: type, expires: expires, refreshInSeconds: 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}]} + userSync: { syncDelay: 0, userIds: [{ name: name, value: value }] } } } @@ -108,9 +108,9 @@ describe('User ID', function () { function getAdUnitMock(code = 'adUnit-code') { return { code, - mediaTypes: {banner: {}, native: {}}, + mediaTypes: { banner: {}, native: {} }, sizes: [[300, 200], [300, 600]], - bids: [{bidder: 'sampleBidder', params: {placementId: 'banner-only-bidder'}}, {bidder: 'anotherSampleBidder', params: {placementId: 'banner-only-bidder'}}] + bids: [{ bidder: 'sampleBidder', params: { placementId: 'banner-only-bidder' } }, { bidder: 'anotherSampleBidder', params: { placementId: 'banner-only-bidder' } }] }; } @@ -147,7 +147,7 @@ describe('User ID', function () { function runBidsHook(...args) { startDelay = delay(); - const result = startAuctionHook(...args, {mkDelay: startDelay}); + const result = startAuctionHook(...args, { mkDelay: startDelay }); return new Promise((resolve) => setTimeout(() => resolve(result))); } @@ -160,7 +160,7 @@ describe('User ID', function () { function initModule(config) { callbackDelay = delay(); - return init(config, {mkDelay: callbackDelay}); + return init(config, { mkDelay: callbackDelay }); } before(function () { @@ -181,7 +181,7 @@ describe('User ID', function () { afterEach(() => { sandbox.restore(); config.resetConfig(); - startAuction.getHooks({hook: startAuctionHook}).remove(); + startAuction.getHooks({ hook: startAuctionHook }).remove(); }); after(() => { @@ -197,7 +197,7 @@ describe('User ID', function () { }); it('are registered when ID submodule is registered', () => { - attachIdSystem({name: 'gvlidMock', gvlid: 123}); + attachIdSystem({ name: 'gvlidMock', gvlid: 123 }); sinon.assert.calledWith(GDPR_GVLIDS.register, MODULE_TYPE_UID, 'gvlidMock', 123); }) }) @@ -221,10 +221,10 @@ describe('User ID', function () { Object.entries({ 'not an object': 'garbage', 'missing name': {}, - 'empty name': {name: ''}, - 'empty storage config': {name: 'mockId', storage: {}}, - 'storage type, but no storage name': mockConfig({name: ''}), - 'storage name, but no storage type': mockConfig({type: undefined}), + 'empty name': { name: '' }, + 'empty storage config': { name: 'mockId', storage: {} }, + 'storage type, but no storage name': mockConfig({ name: '' }), + 'storage name, but no storage type': mockConfig({ type: undefined }), }).forEach(([t, config]) => { it(`should log a warning and reject configuration with ${t}`, () => { expect(getValidSubmoduleConfigs([config]).length).to.equal(0); @@ -245,11 +245,11 @@ describe('User ID', function () { ['refreshInSeconds', 'expires'].forEach(param => { describe(`${param} parameter`, () => { it('should be made a number, when possible', () => { - expect(getValidSubmoduleConfigs([mockConfig({[param]: '123'})])[0].storage[param]).to.equal(123); + expect(getValidSubmoduleConfigs([mockConfig({ [param]: '123' })])[0].storage[param]).to.equal(123); }); it('should log a warning when not a number', () => { - expect(getValidSubmoduleConfigs([mockConfig({[param]: 'garbage'})])[0].storage[param]).to.not.exist; + expect(getValidSubmoduleConfigs([mockConfig({ [param]: 'garbage' })])[0].storage[param]).to.not.exist; sinon.assert.called(utils.logWarn) }); @@ -286,8 +286,8 @@ describe('User ID', function () { }); function getGlobalEids() { - const ortb2Fragments = {global: {}}; - return expectImmediateBidHook(sinon.stub(), {ortb2Fragments}).then(() => ortb2Fragments.global.user?.ext?.eids); + const ortb2Fragments = { global: {} }; + return expectImmediateBidHook(sinon.stub(), { ortb2Fragments }).then(() => ortb2Fragments.global.user?.ext?.eids); } it('Check same cookie behavior', async function () { @@ -303,7 +303,7 @@ describe('User ID', function () { expect(eids1).to.eql([ { source: 'pubcid.org', - uids: [{id: pubcid, atype: 1}] + uids: [{ id: pubcid, atype: 1 }] } ]) const eids2 = await getGlobalEids(); @@ -324,7 +324,7 @@ describe('User ID', function () { coreStorage.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); // erase cookie expect(eids1).to.eql([{ source: 'pubcid.org', - uids: [{id: pubcid1, atype: 1}] + uids: [{ id: pubcid1, atype: 1 }] }]) init(config); @@ -335,7 +335,7 @@ describe('User ID', function () { pubcid2 = coreStorage.getCookie('pubcid'); // get second cookie expect(eids2).to.eql([{ source: 'pubcid.org', - uids: [{id: pubcid2, atype: 1}] + uids: [{ id: pubcid2, atype: 1 }] }]) expect(pubcid1).to.not.equal(pubcid2); }); @@ -348,13 +348,13 @@ describe('User ID', function () { const eids = await getGlobalEids(); expect(eids).to.eql([{ source: 'pubcid.org', - uids: [{id: 'altpubcid200000', atype: 1}] + uids: [{ id: 'altpubcid200000', atype: 1 }] }]) }); it('Extend cookie', async function () { let customConfig = getConfigMock(['pubCommonId', 'pubcid_alt', 'cookie']); - customConfig = addConfig(customConfig, 'params', {extend: true}); + customConfig = addConfig(customConfig, 'params', { extend: true }); init(config); setSubmoduleRegistry([sharedIdSystemSubmodule]); @@ -364,13 +364,13 @@ describe('User ID', function () { const eids = await getGlobalEids(); expect(eids).to.deep.equal([{ source: 'pubcid.org', - uids: [{id: 'altpubcid200000', atype: 1}] + uids: [{ id: 'altpubcid200000', atype: 1 }] }]); }); it('Disable auto create', async function () { let customConfig = getConfigMock(['pubCommonId', 'pubcid', 'cookie']); - customConfig = addConfig(customConfig, 'params', {create: false}); + customConfig = addConfig(customConfig, 'params', { create: false }); init(config); setSubmoduleRegistry([sharedIdSystemSubmodule]); @@ -385,28 +385,28 @@ describe('User ID', function () { init(config); setSubmoduleRegistry([ createMockIdSubmodule('mockId1', null, null, - {'mockId1': {source: 'mock1source', atype: 1}}), + { 'mockId1': { source: 'mock1source', atype: 1 } }), createMockIdSubmodule('mockId2v1', null, null, - {'mockId2v1': {source: 'mock2source', atype: 2, getEidExt: () => ({v: 1})}}), + { 'mockId2v1': { source: 'mock2source', atype: 2, getEidExt: () => ({ v: 1 }) } }), createMockIdSubmodule('mockId2v2', null, null, - {'mockId2v2': {source: 'mock2source', atype: 2, getEidExt: () => ({v: 2})}}), + { 'mockId2v2': { source: 'mock2source', atype: 2, getEidExt: () => ({ v: 2 }) } }), createMockIdSubmodule('mockId2v3', null, null, { 'mockId2v3'(ids) { return { source: 'mock2source', inserter: 'ins', - ext: {v: 2}, - uids: ids.map(id => ({id, atype: 2})) + ext: { v: 2 }, + uids: ids.map(id => ({ id, atype: 2 })) } } }), createMockIdSubmodule('mockId2v4', null, null, { 'mockId2v4'(ids) { return ids.map(id => ({ - uids: [{id, atype: 0}], + uids: [{ id, atype: 0 }], source: 'mock2source', inserter: 'ins', - ext: {v: 2} + ext: { v: 2 } })) } }) @@ -428,7 +428,7 @@ describe('User ID', function () { it('should not alter values returned by adapters', () => { let eid = { source: 'someid.org', - uids: [{id: 'id-1'}] + uids: [{ id: 'id-1' }] }; const config = new Map([ ['someId', function () { @@ -439,7 +439,7 @@ describe('User ID', function () { someId: 'id-1', pubProvidedId: [{ source: 'someid.org', - uids: [{id: 'id-2'}] + uids: [{ id: 'id-2' }] }], } createEidsArray(userid, config); @@ -447,10 +447,10 @@ describe('User ID', function () { expect(allEids).to.eql([ { source: 'someid.org', - uids: [{id: 'id-1'}, {id: 'id-2'}] + uids: [{ id: 'id-1' }, { id: 'id-2' }] } ]) - expect(eid.uids).to.eql([{'id': 'id-1'}]) + expect(eid.uids).to.eql([{ 'id': 'id-1' }]) }); it('should filter out entire EID if none of the uids are strings', () => { @@ -516,7 +516,7 @@ describe('User ID', function () { { source: 'mock2source', inserter: 'ins', - ext: {v: 2}, + ext: { v: 2 }, uids: [ { id: 'mock-2-1', @@ -553,7 +553,7 @@ describe('User ID', function () { atype: 0 } ], - ext: {v: 2} + ext: { v: 2 } }]) }) it('when merging with pubCommonId, should not alter its eids', () => { @@ -562,7 +562,7 @@ describe('User ID', function () { { source: 'mock1Source', uids: [ - {id: 'uid2'} + { id: 'uid2' } ] } ], @@ -571,7 +571,7 @@ describe('User ID', function () { const eids = createEidsArray(uid); expect(eids).to.have.length(1); expect(eids[0].uids.map(u => u.id)).to.have.members(['uid1', 'uid2']); - expect(uid.pubProvidedId[0].uids).to.eql([{id: 'uid2'}]); + expect(uid.pubProvidedId[0].uids).to.eql([{ id: 'uid2' }]); }); }) @@ -579,7 +579,7 @@ describe('User ID', function () { init(config); setSubmoduleRegistry([sharedIdSystemSubmodule]); - const ids = {pubcid: '11111'}; + const ids = { pubcid: '11111' }; config.setConfig({ userSync: { auctionDelay: 10, // with auctionDelay > 0, no auction is needed to complete init @@ -599,10 +599,10 @@ describe('User ID', function () { init(config); setSubmoduleRegistry([ - createMockIdSubmodule('mockId1Module', {id: {mockId1: 'mockId1_value'}}), - createMockIdSubmodule('mockId2Module', {id: {mockId2: 'mockId2_value', mockId3: 'mockId3_value_from_mockId2Module'}}), - createMockIdSubmodule('mockId3Module', {id: {mockId1: 'mockId1_value_from_mockId3Module', mockId2: 'mockId2_value_from_mockId3Module', mockId3: 'mockId3_value', mockId4: 'mockId4_value_from_mockId3Module'}}), - createMockIdSubmodule('mockId4Module', {id: {mockId4: 'mockId4_value'}}) + createMockIdSubmodule('mockId1Module', { id: { mockId1: 'mockId1_value' } }), + createMockIdSubmodule('mockId2Module', { id: { mockId2: 'mockId2_value', mockId3: 'mockId3_value_from_mockId2Module' } }), + createMockIdSubmodule('mockId3Module', { id: { mockId1: 'mockId1_value_from_mockId3Module', mockId2: 'mockId2_value_from_mockId3Module', mockId3: 'mockId3_value', mockId4: 'mockId4_value_from_mockId3Module' } }), + createMockIdSubmodule('mockId4Module', { id: { mockId4: 'mockId4_value' } }) ]); config.setConfig({ @@ -633,10 +633,10 @@ describe('User ID', function () { init(config); setSubmoduleRegistry([ - createMockIdSubmodule('mockId1Module', {id: {mockId1: 'mockId1_value'}}), - createMockIdSubmodule('mockId2Module', {id: {mockId2: 'mockId2_value', mockId3: 'mockId3_value_from_mockId2Module'}}, 'mockId2Module_alias'), - createMockIdSubmodule('mockId3Module', {id: {mockId1: 'mockId1_value_from_mockId3Module', mockId2: 'mockId2_value_from_mockId3Module', mockId3: 'mockId3_value', mockId4: 'mockId4_value_from_mockId3Module'}}, 'mockId3Module_alias'), - createMockIdSubmodule('mockId4Module', {id: {mockId4: 'mockId4_value'}}) + createMockIdSubmodule('mockId1Module', { id: { mockId1: 'mockId1_value' } }), + createMockIdSubmodule('mockId2Module', { id: { mockId2: 'mockId2_value', mockId3: 'mockId3_value_from_mockId2Module' } }, 'mockId2Module_alias'), + createMockIdSubmodule('mockId3Module', { id: { mockId1: 'mockId1_value_from_mockId3Module', mockId2: 'mockId2_value_from_mockId3Module', mockId3: 'mockId3_value', mockId4: 'mockId4_value_from_mockId3Module' } }, 'mockId3Module_alias'), + createMockIdSubmodule('mockId4Module', { id: { mockId4: 'mockId4_value' } }) ]); config.setConfig({ @@ -667,8 +667,8 @@ describe('User ID', function () { init(config); setSubmoduleRegistry([ - createMockIdSubmodule('mockId1Module', {id: {mockId1: 'mockId1_value', mockId2: 'mockId2_value_from_mockId1Module'}}), - createMockIdSubmodule('mockId2Module', {id: {mockId1: 'mockId1_value_from_mockId2Module', mockId2: 'mockId2_value'}}), + createMockIdSubmodule('mockId1Module', { id: { mockId1: 'mockId1_value', mockId2: 'mockId2_value_from_mockId1Module' } }), + createMockIdSubmodule('mockId2Module', { id: { mockId1: 'mockId1_value_from_mockId2Module', mockId2: 'mockId2_value' } }), ]); config.setConfig({ @@ -698,10 +698,10 @@ describe('User ID', function () { init(config); setSubmoduleRegistry([ - createMockIdSubmodule('mockId1Module', {id: {mockId1: 'mockId1_value'}}), - createMockIdSubmodule('mockId2Module', {id: {mockId2: 'mockId2_value'}}), - createMockIdSubmodule('mockId3Module', {id: undefined}), - createMockIdSubmodule('mockId4Module', {id: {mockId4: 'mockId4_value'}}) + createMockIdSubmodule('mockId1Module', { id: { mockId1: 'mockId1_value' } }), + createMockIdSubmodule('mockId2Module', { id: { mockId2: 'mockId2_value' } }), + createMockIdSubmodule('mockId3Module', { id: undefined }), + createMockIdSubmodule('mockId4Module', { id: { mockId4: 'mockId4_value' } }) ]); config.setConfig({ @@ -732,7 +732,7 @@ describe('User ID', function () { init(config); setSubmoduleRegistry([sharedIdSystemSubmodule]); - const ids = {'pubcid': '11111'}; + const ids = { 'pubcid': '11111' }; config.setConfig({ userSync: { auctionDelay: 10, @@ -755,7 +755,7 @@ describe('User ID', function () { })]); const moduleConfig = { name: 'mockId', - value: {mockId: 'mockIdValue'}, + value: { mockId: 'mockIdValue' }, some: 'config' }; config.setConfig({ @@ -771,10 +771,10 @@ describe('User ID', function () { it('pbjs.getUserIdsAsEids should prioritize user ids according to config available to core', () => { init(config); setSubmoduleRegistry([ - createMockIdSubmodule('mockId1Module', {id: {uid2: {id: 'uid2_value'}}}, null, createMockEid('uid2')), - createMockIdSubmodule('mockId2Module', {id: {pubcid: 'pubcid_value', lipb: {lipbid: 'lipbid_value_from_mockId2Module'}}}, null, createMockEid('pubcid')), - createMockIdSubmodule('mockId3Module', {id: {uid2: {id: 'uid2_value_from_mockId3Module'}, pubcid: 'pubcid_value_from_mockId3Module', lipb: {lipbid: 'lipbid_value'}, merkleId: {id: 'merkleId_value_from_mockId3Module'}}}, null, {...createMockEid('uid2'), ...createMockEid('merkleId'), ...createMockEid('lipb')}), - createMockIdSubmodule('mockId4Module', {id: {merkleId: {id: 'merkleId_value'}}}, null, createMockEid('merkleId')) + createMockIdSubmodule('mockId1Module', { id: { uid2: { id: 'uid2_value' } } }, null, createMockEid('uid2')), + createMockIdSubmodule('mockId2Module', { id: { pubcid: 'pubcid_value', lipb: { lipbid: 'lipbid_value_from_mockId2Module' } } }, null, createMockEid('pubcid')), + createMockIdSubmodule('mockId3Module', { id: { uid2: { id: 'uid2_value_from_mockId3Module' }, pubcid: 'pubcid_value_from_mockId3Module', lipb: { lipbid: 'lipbid_value' }, merkleId: { id: 'merkleId_value_from_mockId3Module' } } }, null, { ...createMockEid('uid2'), ...createMockEid('merkleId'), ...createMockEid('lipb') }), + createMockIdSubmodule('mockId4Module', { id: { merkleId: { id: 'merkleId_value' } } }, null, createMockEid('merkleId')) ]); config.setConfig({ userSync: { @@ -809,9 +809,9 @@ describe('User ID', function () { it('pbjs.getUserIdsAsEids should prioritize the uid1 according to config available to core', () => { init(config); setSubmoduleRegistry([ - createMockIdSubmodule('mockId1Module', {id: {tdid: {id: 'uid1_value'}}}, null, UID1_EIDS), - createMockIdSubmodule('mockId2Module', {id: {tdid: {id: 'uid1Id_value_from_mockId2Module'}}}, null, UID1_EIDS), - createMockIdSubmodule('mockId3Module', {id: {tdid: {id: 'uid1Id_value_from_mockId3Module'}}}, null, UID1_EIDS) + createMockIdSubmodule('mockId1Module', { id: { tdid: { id: 'uid1_value' } } }, null, UID1_EIDS), + createMockIdSubmodule('mockId2Module', { id: { tdid: { id: 'uid1Id_value_from_mockId2Module' } } }, null, UID1_EIDS), + createMockIdSubmodule('mockId3Module', { id: { tdid: { id: 'uid1Id_value_from_mockId3Module' } } }, null, UID1_EIDS) ]); config.setConfig({ userSync: { @@ -853,11 +853,11 @@ describe('User ID', function () { it('should merge together submodules\' eid configs', () => { setSubmoduleRegistry([ - mockSubmod('mock1', {mock1: {m: 1}}), - mockSubmod('mock2', {mock2: {m: 2}}) + mockSubmod('mock1', { mock1: { m: 1 } }), + mockSubmod('mock2', { mock2: { m: 2 } }) ]); - expect(EID_CONFIG.get('mock1')).to.eql({m: 1}); - expect(EID_CONFIG.get('mock2')).to.eql({m: 2}); + expect(EID_CONFIG.get('mock1')).to.eql({ m: 1 }); + expect(EID_CONFIG.get('mock2')).to.eql({ m: 2 }); }); it('should respect idPriority', () => { @@ -874,11 +874,11 @@ describe('User ID', function () { } }); setSubmoduleRegistry([ - mockSubmod('mod1', {m1: {i: 1}, m2: {i: 2}}), - mockSubmod('mod2', {m1: {i: 3}, m2: {i: 4}}) + mockSubmod('mod1', { m1: { i: 1 }, m2: { i: 2 } }), + mockSubmod('mod2', { m1: { i: 3 }, m2: { i: 4 } }) ]); - expect(EID_CONFIG.get('m1')).to.eql({i: 3}); - expect(EID_CONFIG.get('m2')).to.eql({i: 2}); + expect(EID_CONFIG.get('m1')).to.eql({ i: 3 }); + expect(EID_CONFIG.get('m2')).to.eql({ i: 2 }); }); }) @@ -894,11 +894,11 @@ describe('User ID', function () { userSync: { ppid: 'pubcid.org', userIds: [ - { name: 'pubCommonId', value: {'pubcid': 'pubCommon-id-value-pubCommon-id-value'} }, + { name: 'pubCommonId', value: { 'pubcid': 'pubCommon-id-value-pubCommon-id-value' } }, ] } }); - return expectImmediateBidHook(() => {}, {adUnits}).then(() => { + return expectImmediateBidHook(() => {}, { adUnits }).then(() => { // ppid should have been set without dashes and stuff expect(window.googletag._ppid).to.equal('pubCommonidvaluepubCommonidvalue'); }); @@ -909,8 +909,8 @@ describe('User ID', function () { init(config); setSubmoduleRegistry([ // some of the ids are padded to have length >= 32 characters - createMockIdSubmodule('mockId1Module', {id: {uid2: {id: 'uid2_value_7ac66c0f148de9519b8bd264312c4d64'}}}), - createMockIdSubmodule('mockId2Module', {id: {pubcid: 'pubcid_value_7ac66c0f148de9519b8bd264312c4d64', lipb: {lipbid: 'lipbid_value_from_mockId2Module_7ac66c0f148de9519b8bd264312c4d64'}}}), + createMockIdSubmodule('mockId1Module', { id: { uid2: { id: 'uid2_value_7ac66c0f148de9519b8bd264312c4d64' } } }), + createMockIdSubmodule('mockId2Module', { id: { pubcid: 'pubcid_value_7ac66c0f148de9519b8bd264312c4d64', lipb: { lipbid: 'lipbid_value_from_mockId2Module_7ac66c0f148de9519b8bd264312c4d64' } } }), createMockIdSubmodule('mockId3Module', { id: { uid2: { @@ -930,7 +930,7 @@ describe('User ID', function () { getValue(data) { return data.id } } }), - createMockIdSubmodule('mockId4Module', {id: {merkleId: {id: 'merkleId_value_7ac66c0f148de9519b8bd264312c4d64'}}}) + createMockIdSubmodule('mockId4Module', { id: { merkleId: { id: 'merkleId_value_7ac66c0f148de9519b8bd264312c4d64' } } }) ]); // before ppid should not be set @@ -952,7 +952,7 @@ describe('User ID', function () { } }); - return expectImmediateBidHook(() => {}, {adUnits}).then(() => { + return expectImmediateBidHook(() => {}, { adUnits }).then(() => { expect(window.googletag._ppid).to.equal('uid2valuefrommockId3Module7ac66c0f148de9519b8bd264312c4d64'); }); }); @@ -1016,7 +1016,7 @@ describe('User ID', function () { setSubmoduleRegistry([{ name: 'sharedId', getId: function () { - return {callback} + return { callback } }, decode(d) { return d @@ -1038,10 +1038,10 @@ describe('User ID', function () { ] } }); - return expectImmediateBidHook(() => {}, {adUnits}).then(() => { + return expectImmediateBidHook(() => {}, { adUnits }).then(() => { expect(window.googletag._ppid).to.be.undefined; const uid = 'thismustbelongerthan32characters' - callback.yield({pubcid: uid}); + callback.yield({ pubcid: uid }); expect(window.googletag._ppid).to.equal(uid); }); }); @@ -1056,13 +1056,13 @@ describe('User ID', function () { userSync: { ppid: 'pubcid.org', userIds: [ - { name: 'pubCommonId', value: {'pubcid': 'pubcommonIdValue'} }, + { name: 'pubCommonId', value: { 'pubcid': 'pubcommonIdValue' } }, ] } }); // before ppid should not be set expect(window.googletag._ppid).to.equal(undefined); - return expectImmediateBidHook(() => {}, {adUnits}).then(() => { + return expectImmediateBidHook(() => {}, { adUnits }).then(() => { // ppid should NOT have been set expect(window.googletag._ppid).to.equal(undefined); // a warning should have been emmited @@ -1078,7 +1078,7 @@ describe('User ID', function () { userSync: { ppid: 'pubcid.org', userIds: [ - { name: 'pubCommonId', value: {'pubcid': id} }, + { name: 'pubCommonId', value: { 'pubcid': id } }, ] } }); @@ -1091,8 +1091,8 @@ describe('User ID', function () { init(config); setSubmoduleRegistry([ // some of the ids are padded to have length >= 32 characters - createMockIdSubmodule('mockId1Module', {id: {uid2: {id: 'uid2_value_7ac66c0f148de9519b8bd264312c4d64'}}}), - createMockIdSubmodule('mockId2Module', {id: {pubcid: 'pubcid_value_7ac66c0f148de9519b8bd264312c4d64', lipb: {lipbid: 'lipbid_value_from_mockId2Module_7ac66c0f148de9519b8bd264312c4d64'}}}), + createMockIdSubmodule('mockId1Module', { id: { uid2: { id: 'uid2_value_7ac66c0f148de9519b8bd264312c4d64' } } }), + createMockIdSubmodule('mockId2Module', { id: { pubcid: 'pubcid_value_7ac66c0f148de9519b8bd264312c4d64', lipb: { lipbid: 'lipbid_value_from_mockId2Module_7ac66c0f148de9519b8bd264312c4d64' } } }), createMockIdSubmodule('mockId3Module', { id: { uid2: { @@ -1112,7 +1112,7 @@ describe('User ID', function () { getValue(data) { return data.id } } }), - createMockIdSubmodule('mockId4Module', {id: {merkleId: {id: 'merkleId_value_7ac66c0f148de9519b8bd264312c4d64'}}}) + createMockIdSubmodule('mockId4Module', { id: { merkleId: { id: 'merkleId_value_7ac66c0f148de9519b8bd264312c4d64' } } }) ]); // before ppid should not be set @@ -1140,7 +1140,7 @@ describe('User ID', function () { }); describe('refreshing before init is complete', () => { - const MOCK_ID = {'MOCKID': '1111'}; + const MOCK_ID = { 'MOCKID': '1111' }; let mockIdCallback; let startInit; @@ -1154,7 +1154,7 @@ describe('User ID', function () { 'mid': value['MOCKID'] }; }, - getId: sinon.stub().returns({callback: mockIdCallback}) + getId: sinon.stub().returns({ callback: mockIdCallback }) }; init(config); setSubmoduleRegistry([mockIdSystem]); @@ -1163,7 +1163,7 @@ describe('User ID', function () { auctionDelay: 10, userIds: [{ name: 'mockId', - storage: {name: 'MOCKID', type: 'cookie'} + storage: { name: 'MOCKID', type: 'cookie' } }] } }); @@ -1190,11 +1190,11 @@ describe('User ID', function () { startInit(); startAuctionHook(() => { done(); - }, {adUnits: [getAdUnitMock()]}, {delay: delay()}); + }, { adUnits: [getAdUnitMock()] }, { delay: delay() }); getGlobal().refreshUserIds(); clearStack().then(() => { // simulate init complete - mockIdCallback.callArg(0, {id: {MOCKID: '1111'}}); + mockIdCallback.callArg(0, { id: { MOCKID: '1111' } }); }); }); @@ -1203,7 +1203,7 @@ describe('User ID', function () { startAuctionHook(() => { done(); }, - {adUnits: [getAdUnitMock()]}, + { adUnits: [getAdUnitMock()] }, { delay: delay(), getIds: () => Promise.reject(new Error()) @@ -1230,7 +1230,7 @@ describe('User ID', function () { [name]: value }; }, - getId: sinon.stub().callsFake(() => ({id: name})) + getId: sinon.stub().callsFake(() => ({ id: name })) }; } let id1, id2; @@ -1244,10 +1244,10 @@ describe('User ID', function () { auctionDelay: 10, userIds: [{ name: 'mock1', - storage: {name: 'mock1', type: 'cookie'} + storage: { name: 'mock1', type: 'cookie' } }, { name: 'mock2', - storage: {name: 'mock2', type: 'cookie'} + storage: { name: 'mock2', type: 'cookie' } }] } }) @@ -1259,7 +1259,7 @@ describe('User ID', function () { 'in init': () => id1.getId.callsFake(() => { throw new Error() }), 'in callback': () => { const mockCallback = sinon.stub().callsFake(() => { throw new Error() }); - id1.getId.callsFake(() => ({callback: mockCallback})) + id1.getId.callsFake(() => ({ callback: mockCallback })) } }).forEach(([t, setup]) => { describe(`${t}`, () => { @@ -1274,7 +1274,7 @@ describe('User ID', function () { }); it('pbjs.refreshUserIds updates submodules', function(done) { const sandbox = sinon.createSandbox(); - const mockIdCallback = sandbox.stub().returns({id: {'MOCKID': '1111'}}); + const mockIdCallback = sandbox.stub().returns({ id: { 'MOCKID': '1111' } }); const mockIdSystem = { name: 'mockId', decode: function(value) { @@ -1292,7 +1292,7 @@ describe('User ID', function () { auctionDelay: 10, userIds: [{ name: 'mockId', - value: {id: {mockId: '1111'}} + value: { id: { mockId: '1111' } } }] } }); @@ -1305,7 +1305,7 @@ describe('User ID', function () { auctionDelay: 10, userIds: [{ name: 'mockId', - value: {id: {mockId: '1212'}} + value: { id: { mockId: '1212' } } }] } }); @@ -1320,10 +1320,10 @@ describe('User ID', function () { init(config); setSubmoduleRegistry([ - createMockIdSubmodule('mockId1Module', {id: {mockId1: 'mockId1_value'}}), - createMockIdSubmodule('mockId2Module', {id: {mockId2: 'mockId2_value', mockId3: 'mockId3_value_from_mockId2Module'}}), - createMockIdSubmodule('mockId3Module', {id: {mockId1: 'mockId1_value_from_mockId3Module', mockId2: 'mockId2_value_from_mockId3Module', mockId3: 'mockId3_value', mockId4: 'mockId4_value_from_mockId3Module'}}), - createMockIdSubmodule('mockId4Module', {id: {mockId4: 'mockId4_value'}}) + createMockIdSubmodule('mockId1Module', { id: { mockId1: 'mockId1_value' } }), + createMockIdSubmodule('mockId2Module', { id: { mockId2: 'mockId2_value', mockId3: 'mockId3_value_from_mockId2Module' } }), + createMockIdSubmodule('mockId3Module', { id: { mockId1: 'mockId1_value_from_mockId3Module', mockId2: 'mockId2_value_from_mockId3Module', mockId3: 'mockId3_value', mockId4: 'mockId4_value_from_mockId3Module' } }), + createMockIdSubmodule('mockId4Module', { id: { mockId4: 'mockId4_value' } }) ]); config.setConfig({ @@ -1378,7 +1378,7 @@ describe('User ID', function () { coreStorage.setCookie('refreshedid', '', EXPIRED_COOKIE_DATE); const sandbox = sinon.createSandbox(); - const mockIdCallback = sandbox.stub().returns({id: {'MOCKID': '1111'}}); + const mockIdCallback = sandbox.stub().returns({ id: { 'MOCKID': '1111' } }); const refreshUserIdsCallback = sandbox.stub(); const mockIdSystem = { @@ -1391,7 +1391,7 @@ describe('User ID', function () { getId: mockIdCallback }; - const refreshedIdCallback = sandbox.stub().returns({id: {'REFRESH': '1111'}}); + const refreshedIdCallback = sandbox.stub().returns({ id: { 'REFRESH': '1111' } }); const refreshedIdSystem = { name: 'refreshedId', @@ -1412,17 +1412,17 @@ describe('User ID', function () { userIds: [ { name: 'mockId', - storage: {name: 'MOCKID', type: 'cookie'}, + storage: { name: 'MOCKID', type: 'cookie' }, }, { name: 'refreshedId', - storage: {name: 'refreshedid', type: 'cookie'}, + storage: { name: 'refreshedid', type: 'cookie' }, } ] } }); - return getGlobal().refreshUserIds({submoduleNames: 'refreshedId'}, refreshUserIdsCallback).then(() => { + return getGlobal().refreshUserIds({ submoduleNames: 'refreshedId' }, refreshUserIdsCallback).then(() => { expect(refreshedIdCallback.callCount).to.equal(2); expect(mockIdCallback.callCount).to.equal(1); expect(refreshUserIdsCallback.callCount).to.equal(1); @@ -1486,7 +1486,7 @@ describe('User ID', function () { it('handles config with empty usersync object', function () { init(config); setSubmoduleRegistry([sharedIdSystemSubmodule]); - config.setConfig({userSync: {}}); + config.setConfig({ userSync: {} }); expect(typeof utils.logInfo.args[0]).to.equal('undefined'); }); @@ -1508,10 +1508,10 @@ describe('User ID', function () { userSync: { userIds: [{ name: '', - value: {test: '1'} + value: { test: '1' } }, { name: 'foo', - value: {test: '1'} + value: { test: '1' } }] } }); @@ -1547,10 +1547,10 @@ describe('User ID', function () { userSync: { syncDelay: 0, userIds: [{ - name: 'pubCommonId', value: {'pubcid': '11111'} + name: 'pubCommonId', value: { 'pubcid': '11111' } }, { name: 'pubProvidedId', - storage: {name: 'pubProvidedId', type: 'cookie'} + storage: { name: 'pubProvidedId', type: 'cookie' } }] } }); @@ -1565,7 +1565,7 @@ describe('User ID', function () { syncDelay: 99, userIds: [{ name: 'pubCommonId', - storage: {name: 'pubCommonId', type: 'cookie'} + storage: { name: 'pubCommonId', type: 'cookie' } }] } }); @@ -1580,7 +1580,7 @@ describe('User ID', function () { auctionDelay: 100, userIds: [{ name: 'pubCommonId', - storage: {name: 'pubCommonId', type: 'cookie'} + storage: { name: 'pubCommonId', type: 'cookie' } }] } }); @@ -1595,7 +1595,7 @@ describe('User ID', function () { auctionDelay: '', userIds: [{ name: 'pubCommonId', - storage: {name: 'pubCommonId', type: 'cookie'} + storage: { name: 'pubCommonId', type: 'cookie' } }] } }); @@ -1630,9 +1630,9 @@ describe('User ID', function () { getId: function () { const storedId = coreStorage.getCookie('MOCKID'); if (storedId) { - return {id: {'MOCKID': storedId}}; + return { id: { 'MOCKID': storedId } }; } - return {callback: mockIdCallback}; + return { callback: mockIdCallback }; } }; initModule(config); @@ -1652,12 +1652,12 @@ describe('User ID', function () { auctionDelay: 33, syncDelay: 77, userIds: [{ - name: 'mockId', storage: {name: 'MOCKID', type: 'cookie'} + name: 'mockId', storage: { name: 'MOCKID', type: 'cookie' } }] } }); - return runBidsHook(auctionSpy, {adUnits}).then(() => { + return runBidsHook(auctionSpy, { adUnits }).then(() => { // check auction was delayed startDelay.calledWith(33); auctionSpy.calledOnce.should.equal(false); @@ -1671,7 +1671,7 @@ describe('User ID', function () { auctionSpy.calledOnce.should.equal(true); // does not call auction again once ids are synced - mockIdCallback.callArgWith(0, {'MOCKID': '1234'}); + mockIdCallback.callArgWith(0, { 'MOCKID': '1234' }); auctionSpy.calledOnce.should.equal(true); // no sync after auction ends @@ -1685,12 +1685,12 @@ describe('User ID', function () { auctionDelay: 33, syncDelay: 77, userIds: [{ - name: 'mockId', storage: {name: 'MOCKID', type: 'cookie'} + name: 'mockId', storage: { name: 'MOCKID', type: 'cookie' } }] } }); - return runBidsHook(auctionSpy, {adUnits}).then(() => { + return runBidsHook(auctionSpy, { adUnits }).then(() => { // check auction was delayed startDelay.calledWith(33); auctionSpy.calledOnce.should.equal(false); @@ -1699,7 +1699,7 @@ describe('User ID', function () { mockIdCallback.calledOnce.should.equal(true); // if ids returned, should continue auction - mockIdCallback.callArgWith(0, {'MOCKID': '1234'}); + mockIdCallback.callArgWith(0, { 'MOCKID': '1234' }); return clearStack(); }).then(() => { auctionSpy.calledOnce.should.equal(true); @@ -1722,12 +1722,12 @@ describe('User ID', function () { auctionDelay: 0, syncDelay: 77, userIds: [{ - name: 'mockId', storage: {name: 'MOCKID', type: 'cookie'} + name: 'mockId', storage: { name: 'MOCKID', type: 'cookie' } }] } }); - return expectImmediateBidHook(auctionSpy, {adUnits}) + return expectImmediateBidHook(auctionSpy, { adUnits }) .then(() => { // should not delay auction auctionSpy.calledOnce.should.equal(true); @@ -1755,12 +1755,12 @@ describe('User ID', function () { auctionDelay: 0, syncDelay: 0, userIds: [{ - name: 'mockId', storage: {name: 'MOCKID', type: 'cookie'} + name: 'mockId', storage: { name: 'MOCKID', type: 'cookie' } }] } }); - return expectImmediateBidHook(auctionSpy, {adUnits}) + return expectImmediateBidHook(auctionSpy, { adUnits }) .then(() => { // auction should not be delayed auctionSpy.calledOnce.should.equal(true); @@ -1786,12 +1786,12 @@ describe('User ID', function () { auctionDelay: 33, syncDelay: 77, userIds: [{ - name: 'mockId', storage: {name: 'MOCKID', type: 'cookie'} + name: 'mockId', storage: { name: 'MOCKID', type: 'cookie' } }] } }); - return runBidsHook(auctionSpy, {adUnits}).then(() => { + return runBidsHook(auctionSpy, { adUnits }).then(() => { auctionSpy.calledOnce.should.equal(true); mockIdCallback.calledOnce.should.equal(false); @@ -1810,9 +1810,9 @@ describe('User ID', function () { function getGlobalEids() { return new Promise((resolve) => { - startAuctionHook(function ({ortb2Fragments}) { + startAuctionHook(function ({ ortb2Fragments }) { resolve(ortb2Fragments.global.user?.ext?.eids); - }, {ortb2Fragments: { global: {} }}) + }, { ortb2Fragments: { global: {} } }) }) } @@ -1826,7 +1826,7 @@ describe('User ID', function () { const eids = await getGlobalEids(); expect(eids).to.eql([{ source: 'pubcid.org', - uids: [{id: 'testpubcid', atype: 1}] + uids: [{ id: 'testpubcid', atype: 1 }] }]) } finally { coreStorage.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); @@ -1846,7 +1846,7 @@ describe('User ID', function () { const eids = await getGlobalEids(); expect(eids).to.eql([{ source: 'pubcid.org', - uids: [{id: 'testpubcid', atype: 1}] + uids: [{ id: 'testpubcid', atype: 1 }] }]); } finally { localStorage.removeItem('pubcid'); @@ -1868,7 +1868,7 @@ describe('User ID', function () { const eids = await getGlobalEids(); expect(eids).to.eql([{ source: 'pubcid.org', - uids: [{id: 'testpubcid', atype: 1}] + uids: [{ id: 'testpubcid', atype: 1 }] }]); } finally { coreStorage.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); @@ -1889,7 +1889,7 @@ describe('User ID', function () { const eids = await getGlobalEids(); expect(eids).to.eql([{ source: 'pubcid.org', - uids: [{id: 'testpubcid', atype: 1}] + uids: [{ id: 'testpubcid', atype: 1 }] }]) } finally { localStorage.removeItem('pubcid'); @@ -1908,7 +1908,7 @@ describe('User ID', function () { const eids = await getGlobalEids(); expect(eids).to.eql([{ source: 'pubcid.org', - uids: [{id: 'testpubcid', atype: 1}] + uids: [{ id: 'testpubcid', atype: 1 }] }]); } finally { coreStorage.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); @@ -1918,7 +1918,7 @@ describe('User ID', function () { it('test hook from pubcommonid config value object', async function () { init(config); setSubmoduleRegistry([sharedIdSystemSubmodule]); - config.setConfig(getConfigValueMock('pubCommonId', {'pubcidvalue': 'testpubcidvalue'})); + config.setConfig(getConfigValueMock('pubCommonId', { 'pubcidvalue': 'testpubcidvalue' })); expect(await getGlobalEids()).to.not.exist; // "pubcidvalue" is an un-known submodule for USER_IDS_CONFIG in eids.js }); @@ -1997,9 +1997,9 @@ describe('User ID', function () { userSync: { syncDelay: 0, userIds: [{ - name: 'pubCommonId', storage: {name: 'pubcid', type: 'cookie'} + name: 'pubCommonId', storage: { name: 'pubcid', type: 'cookie' } }, { - name: 'mockId', storage: {name: 'MOCKID', type: 'cookie'} + name: 'mockId', storage: { name: 'MOCKID', type: 'cookie' } }] } }); @@ -2014,7 +2014,7 @@ describe('User ID', function () { }, getId: function (config, consentData, storedId) { if (storedId) return {}; - return {id: {'MOCKID': '1234'}}; + return { id: { 'MOCKID': '1234' } }; }, eids: { mid: { @@ -2037,7 +2037,7 @@ describe('User ID', function () { discloseStorageUse.before(discloseStorageHook) }) after(() => { - discloseStorageUse.getHooks({hook: discloseStorageHook}).remove(); + discloseStorageUse.getHooks({ hook: discloseStorageHook }).remove(); }) beforeEach(() => { disclose = sinon.stub(); @@ -2133,7 +2133,7 @@ describe('User ID', function () { }; }, getId: function () { - return {id: `${name}Value`}; + return { id: `${name}Value` }; }, eids: { [name]: { @@ -2158,12 +2158,12 @@ describe('User ID', function () { userSync: { syncDelay: 0, userIds: MOCK_IDS.map(name => ({ - name, storage: {name, type: 'cookie'} + name, storage: { name, type: 'cookie' } })) } }); const eids = await getGlobalEids(); - const activeSources = eids.map(({source}) => source); + const activeSources = eids.map(({ source }) => source); expect(Array.from(new Set(activeSources))).to.have.members([MOCK_IDS[1]]); }); }) @@ -2197,7 +2197,7 @@ describe('User ID', function () { it('pubcid callback with url', function () { let customCfg = getConfigMock(['pubCommonId', 'pubcid', 'cookie']); - customCfg = addConfig(customCfg, 'params', {pixelUrl: '/any/pubcid/url'}); + customCfg = addConfig(customCfg, 'params', { pixelUrl: '/any/pubcid/url' }); init(config); setSubmoduleRegistry([sharedIdSystemSubmodule]); @@ -2303,7 +2303,7 @@ describe('User ID', function () { }); function setStorage({ - val = JSON.stringify({id: '1234'}), + val = JSON.stringify({ id: '1234' }), lastDelta = 60 * 1000, cst = null } = {}) { @@ -2315,13 +2315,13 @@ describe('User ID', function () { } it('calls getId if no stored consent data and refresh is not needed', function () { - setStorage({lastDelta: 1000}); + setStorage({ lastDelta: 1000 }); config.setConfig(userIdConfig); let innerAdUnits; return runBidsHook((config) => { innerAdUnits = config.adUnits - }, {adUnits}).then(() => { + }, { adUnits }).then(() => { sinon.assert.calledOnce(mockGetId); sinon.assert.calledOnce(mockDecode); sinon.assert.notCalled(mockExtendId); @@ -2335,7 +2335,7 @@ describe('User ID', function () { let innerAdUnits; return runBidsHook((config) => { innerAdUnits = config.adUnits - }, {adUnits}).then(() => { + }, { adUnits }).then(() => { sinon.assert.calledOnce(mockGetId); sinon.assert.calledOnce(mockDecode); sinon.assert.notCalled(mockExtendId); @@ -2343,13 +2343,13 @@ describe('User ID', function () { }); it('calls getId if empty stored consent and refresh not needed', function () { - setStorage({cst: ''}); + setStorage({ cst: '' }); config.setConfig(userIdConfig); let innerAdUnits; return runBidsHook((config) => { innerAdUnits = config.adUnits - }, {adUnits}).then(() => { + }, { adUnits }).then(() => { sinon.assert.calledOnce(mockGetId); sinon.assert.calledOnce(mockDecode); sinon.assert.notCalled(mockExtendId); @@ -2357,7 +2357,7 @@ describe('User ID', function () { }); it('calls getId if stored consent does not match current consent and refresh not needed', function () { - setStorage({cst: getConsentHash()}); + setStorage({ cst: getConsentHash() }); gdprDataHandler.setConsentData({ consentString: 'different' }); @@ -2367,7 +2367,7 @@ describe('User ID', function () { let innerAdUnits; return runBidsHook((config) => { innerAdUnits = config.adUnits - }, {adUnits}).then(() => { + }, { adUnits }).then(() => { sinon.assert.calledOnce(mockGetId); sinon.assert.calledOnce(mockDecode); sinon.assert.notCalled(mockExtendId); @@ -2375,14 +2375,14 @@ describe('User ID', function () { }); it('does not call getId if stored consent matches current consent and refresh not needed', function () { - setStorage({lastDelta: 1000, cst: getConsentHash()}); + setStorage({ lastDelta: 1000, cst: getConsentHash() }); config.setConfig(userIdConfig); let innerAdUnits; return runBidsHook((config) => { innerAdUnits = config.adUnits - }, {adUnits}).then(() => { + }, { adUnits }).then(() => { sinon.assert.notCalled(mockGetId); sinon.assert.calledOnce(mockDecode); sinon.assert.calledOnce(mockExtendId); @@ -2390,16 +2390,16 @@ describe('User ID', function () { }); it('calls getId with the list of enabled storage types', function() { - setStorage({lastDelta: 1000}); + setStorage({ lastDelta: 1000 }); config.setConfig(userIdConfig); let innerAdUnits; return runBidsHook((config) => { innerAdUnits = config.adUnits - }, {adUnits}).then(() => { + }, { adUnits }).then(() => { sinon.assert.calledOnce(mockGetId); - expect(mockGetId.getCall(0).args[0].enabledStorageTypes).to.deep.equal([ userIdConfig.userSync.userIds[0].storage.type ]); + expect(mockGetId.getCall(0).args[0].enabledStorageTypes).to.deep.equal([userIdConfig.userSync.userIds[0].storage.type]); }); }); }); @@ -2409,10 +2409,10 @@ describe('User ID', function () { return { name, getId() { - return {id: value} + return { id: value } }, decode(d) { - return {[name]: d} + return { [name]: d } }, onDataDeletionRequest: sinon.stub() } @@ -2428,7 +2428,7 @@ describe('User ID', function () { cfg1 = getStorageMock('id1', 'id1', 'cookie'); cfg2 = getStorageMock('id2', 'id2', 'html5'); cfg3 = getStorageMock('id3', 'id3', 'cookie&html5'); - cfg4 = {name: 'id4', value: {id4: 'val4'}}; + cfg4 = { name: 'id4', value: { id4: 'val4' } }; setSubmoduleRegistry([mod1, mod2, mod3, mod4]); config.setConfig({ auctionDelay: 1, @@ -2453,10 +2453,10 @@ describe('User ID', function () { it('invokes onDataDeletionRequest', () => { requestDataDeletion(sinon.stub()); - sinon.assert.calledWith(mod1.onDataDeletionRequest, cfg1, {id1: 'val1'}); - sinon.assert.calledWith(mod2.onDataDeletionRequest, cfg2, {id2: 'val2'}); - sinon.assert.calledWith(mod3.onDataDeletionRequest, cfg3, {id3: 'val3'}); - sinon.assert.calledWith(mod4.onDataDeletionRequest, cfg4, {id4: 'val4'}); + sinon.assert.calledWith(mod1.onDataDeletionRequest, cfg1, { id1: 'val1' }); + sinon.assert.calledWith(mod2.onDataDeletionRequest, cfg2, { id2: 'val2' }); + sinon.assert.calledWith(mod3.onDataDeletionRequest, cfg3, { id3: 'val3' }); + sinon.assert.calledWith(mod4.onDataDeletionRequest, cfg4, { id4: 'val4' }); }); describe('does not choke when onDataDeletionRequest', () => { @@ -2467,7 +2467,7 @@ describe('User ID', function () { it(t, () => { setup(); const next = sinon.stub(); - const arg = {random: 'value'}; + const arg = { random: 'value' }; requestDataDeletion(next, arg); sinon.assert.calledOnce(mod2.onDataDeletionRequest); sinon.assert.calledOnce(mod3.onDataDeletionRequest); @@ -2505,7 +2505,7 @@ describe('User ID', function () { userSync: { encryptedSignalSources: { registerDelay: 0, - sources: [{source: ['pubcid.org'], encrypt: false}] + sources: [{ source: ['pubcid.org'], encrypt: false }] } } }); @@ -2578,10 +2578,10 @@ describe('User ID', function () { } } setSubmoduleRegistry([ - createMockIdSubmodule('mockId1Module', {id: {uid2: {id: 'uid2_value'}}}, null, EIDS), - createMockIdSubmodule('mockId2Module', {id: {pubcid: 'pubcid_value', lipb: {lipbid: 'lipbid_value_from_mockId2Module'}}}, null, EIDS), - createMockIdSubmodule('mockId3Module', {id: {uid2: {id: 'uid2_value_from_mockId3Module'}, pubcid: 'pubcid_value_from_mockId3Module', lipb: {lipbid: 'lipbid_value'}, merkleId: {id: 'merkleId_value_from_mockId3Module'}}}, null, EIDS), - createMockIdSubmodule('mockId4Module', {id: {merkleId: {id: 'merkleId_value'}}}, null, EIDS) + createMockIdSubmodule('mockId1Module', { id: { uid2: { id: 'uid2_value' } } }, null, EIDS), + createMockIdSubmodule('mockId2Module', { id: { pubcid: 'pubcid_value', lipb: { lipbid: 'lipbid_value_from_mockId2Module' } } }, null, EIDS), + createMockIdSubmodule('mockId3Module', { id: { uid2: { id: 'uid2_value_from_mockId3Module' }, pubcid: 'pubcid_value_from_mockId3Module', lipb: { lipbid: 'lipbid_value' }, merkleId: { id: 'merkleId_value_from_mockId3Module' } } }, null, EIDS), + createMockIdSubmodule('mockId4Module', { id: { merkleId: { id: 'merkleId_value' } } }, null, EIDS) ]); config.setConfig({ userSync: { @@ -2627,7 +2627,7 @@ describe('User ID', function () { userSync: { auctionDelay: 10, userIds: [{ - name: 'pubCommonId', value: {'pubcid': '11111'} + name: 'pubCommonId', value: { 'pubcid': '11111' } }] } }); @@ -2670,7 +2670,7 @@ describe('User ID', function () { userSync: { auctionDelay: 10, userIds: [{ - name: 'pubCommonId', value: {'pubcid': '11111'} + name: 'pubCommonId', value: { 'pubcid': '11111' } }] } }); @@ -2689,10 +2689,10 @@ describe('User ID', function () { const merkleEids = createMockEid('merkleId', 'merkleinc.com') setSubmoduleRegistry([ - createMockIdSubmodule('mockId1Module', {id: {uid2: {id: 'uid2_value'}}}, null, uid2Eids), - createMockIdSubmodule('mockId2Module', {id: {pubcid: 'pubcid_value', lipb: {lipbid: 'lipbid_value_from_mockId2Module'}}}, null, {...pubcEids, ...liveIntentEids}), - createMockIdSubmodule('mockId3Module', {id: {uid2: {id: 'uid2_value_from_mockId3Module'}, pubcid: 'pubcid_value_from_mockId3Module', lipb: {lipbid: 'lipbid_value'}, merkleId: {id: 'merkleId_value_from_mockId3Module'}}}, null, {...uid2Eids, ...pubcEids, ...liveIntentEids}), - createMockIdSubmodule('mockId4Module', {id: {merkleId: {id: 'merkleId_value'}}}, null, merkleEids) + createMockIdSubmodule('mockId1Module', { id: { uid2: { id: 'uid2_value' } } }, null, uid2Eids), + createMockIdSubmodule('mockId2Module', { id: { pubcid: 'pubcid_value', lipb: { lipbid: 'lipbid_value_from_mockId2Module' } } }, null, { ...pubcEids, ...liveIntentEids }), + createMockIdSubmodule('mockId3Module', { id: { uid2: { id: 'uid2_value_from_mockId3Module' }, pubcid: 'pubcid_value_from_mockId3Module', lipb: { lipbid: 'lipbid_value' }, merkleId: { id: 'merkleId_value_from_mockId3Module' } } }, null, { ...uid2Eids, ...pubcEids, ...liveIntentEids }), + createMockIdSubmodule('mockId4Module', { id: { merkleId: { id: 'merkleId_value' } } }, null, merkleEids) ]); config.setConfig({ userSync: { @@ -2711,10 +2711,10 @@ describe('User ID', function () { }); const ids = { - 'uidapi.com': {'uid2': {id: 'uid2_value_from_mockId3Module'}}, - 'pubcid.org': {'pubcid': 'pubcid_value'}, - 'liveintent.com': {'lipb': {lipbid: 'lipbid_value_from_mockId2Module'}}, - 'merkleinc.com': {'merkleId': {id: 'merkleId_value'}} + 'uidapi.com': { 'uid2': { id: 'uid2_value_from_mockId3Module' } }, + 'pubcid.org': { 'pubcid': 'pubcid_value' }, + 'liveintent.com': { 'lipb': { lipbid: 'lipbid_value_from_mockId2Module' } }, + 'merkleinc.com': { 'merkleId': { id: 'merkleId_value' } } }; return getGlobal().getUserIdsAsync().then(() => { @@ -2751,7 +2751,7 @@ describe('User ID', function () { source: `${extraKey}.com`, atype: 1, getUidExt() { - return {provider: `${key}Module`} + return { provider: `${key}Module` } } }])) } @@ -2775,10 +2775,10 @@ describe('User ID', function () { ]); }) - function enrich({global = {}, bidder = {}} = {}) { + function enrich({ global = {}, bidder = {} } = {}) { return getGlobal().getUserIdsAsync().then(() => { - enrichEids({global, bidder}); - return {global, bidder}; + enrichEids({ global, bidder }); + return { global, bidder }; }) } @@ -2790,7 +2790,7 @@ describe('User ID', function () { atype: 1, }; if (!owner) { - uid.ext = {provider: module} + uid.ext = { provider: module } } return { source: `${key}.com`, @@ -2801,7 +2801,7 @@ describe('User ID', function () { function bidderEids(bidderMappings) { return Object.fromEntries( - Object.entries(bidderMappings).map(([bidder, mapping]) => [bidder, {user: {ext: {eids: eidsFrom(mapping)}}}]) + Object.entries(bidderMappings).map(([bidder, mapping]) => [bidder, { user: { ext: { eids: eidsFrom(mapping) } } }]) ) } @@ -2818,7 +2818,7 @@ describe('User ID', function () { ] } }); - return enrich().then(({global}) => { + return enrich().then(({ global }) => { expect(global.user.ext.eids).to.eql(eidsFrom({ mockId1: 'mockId1Module' })) @@ -2834,7 +2834,7 @@ describe('User ID', function () { ] } }); - return enrich().then(({global}) => { + return enrich().then(({ global }) => { expect(global.user?.ext?.eids).to.not.exist; }); }); @@ -2854,7 +2854,7 @@ describe('User ID', function () { ] } }); - return enrich().then(({global}) => { + return enrich().then(({ global }) => { expect(global.user.ext.eids).to.eql(eidsFrom({ mockId1: 'mockId3Module', mockId2: 'mockId2Module', @@ -2873,7 +2873,7 @@ describe('User ID', function () { ] } }); - return enrich().then(({global, bidder}) => { + return enrich().then(({ global, bidder }) => { expect(global.user.ext.eids).to.eql(eidsFrom({ mockId4: 'mockId4Module' })); @@ -2928,7 +2928,7 @@ describe('User ID', function () { it('should restrict ID if it comes from restricted modules', async () => { setup(); - const {global, bidder} = await enrich(); + const { global, bidder } = await enrich(); expect(global).to.eql({}); expect(bidder).to.eql(bidderEids({ bidderA: { @@ -2945,7 +2945,7 @@ describe('User ID', function () { it('should use secondary module restrictions if ID comes from it', async () => { idValues.mockId1 = []; setup(); - const {global, bidder} = await enrich(); + const { global, bidder } = await enrich(); expect(global).to.eql({}); expect(bidder).to.eql(bidderEids({ bidderA: { @@ -2964,7 +2964,7 @@ describe('User ID', function () { idValues.mockId2 = []; idValues.mockId3 = []; setup(); - const {global, bidder} = await enrich(); + const { global, bidder } = await enrich(); expect(global.user.ext.eids).to.eql(eidsFrom({ mockId1: 'mockId4Module' })); @@ -2989,7 +2989,7 @@ describe('User ID', function () { } it('should not restrict if primary id is available', async () => { setup(); - const {global, bidder} = await enrich(); + const { global, bidder } = await enrich(); expect(global.user.ext.eids).to.eql(eidsFrom({ mockId1: 'mockId1Module' })); @@ -2998,7 +2998,7 @@ describe('User ID', function () { it('should use secondary modules\' restrictions if they provide the ID', async () => { idValues.mockId1 = []; setup(); - const {global, bidder} = await enrich(); + const { global, bidder } = await enrich(); expect(global).to.eql({}); expect(bidder).to.eql(bidderEids({ bidderA: { @@ -3027,7 +3027,7 @@ describe('User ID', function () { ] } }); - return enrich().then(({global, bidder}) => { + return enrich().then(({ global, bidder }) => { expect(global.user?.ext?.eids).to.not.exist; expect(bidder).to.eql(bidderEids({ bidderA: { @@ -3053,17 +3053,17 @@ describe('User ID', function () { ] } }); - const globalEids = [{pub: 'provided'}]; - const bidderAEids = [{bidder: 'A'}] + const globalEids = [{ pub: 'provided' }]; + const bidderAEids = [{ bidder: 'A' }] const fpd = { - global: {user: {ext: {eids: globalEids}}}, + global: { user: { ext: { eids: globalEids } } }, bidder: { bidderA: { - user: {ext: {eids: bidderAEids}} + user: { ext: { eids: bidderAEids } } } } } - return enrich(fpd).then(({global, bidder}) => { + return enrich(fpd).then(({ global, bidder }) => { expect(global.user.ext.eids).to.eql(globalEids.concat(eidsFrom({ mockId4: 'mockId4Module' }))); @@ -3094,7 +3094,7 @@ describe('User ID', function () { mockIdSubmodule(UNALLOWED_MODULE), ]); - const unregisterRule = registerActivityControl(ACTIVITY_ENRICH_EIDS, 'ruleName', ({componentName, init}) => { + const unregisterRule = registerActivityControl(ACTIVITY_ENRICH_EIDS, 'ruleName', ({ componentName, init }) => { if (componentName === 'mockId3Module' && init === false) { return ({ allow: false, reason: "disabled" }); } }); @@ -3236,8 +3236,8 @@ describe('User ID', function () { it('should properly map registry to submodule containers for non-empty previous submodule containers', () => { const previousSubmoduleContainers = [ - {submodule: {name: 'notSharedId'}, config: {name: 'notSharedId'}}, - {submodule: {name: 'notSharedId2'}, config: {name: 'notSharedId2'}}, + { submodule: { name: 'notSharedId' }, config: { name: 'notSharedId' } }, + { submodule: { name: 'notSharedId2' }, config: { name: 'notSharedId2' } }, ]; const submoduleRegistry = [ sharedIdSystemSubmodule, @@ -3252,14 +3252,14 @@ describe('User ID', function () { it('should properly map registry to submodule containers for retainConfig flag', () => { const previousSubmoduleContainers = [ - {submodule: {name: 'shouldBeKept'}, config: {name: 'shouldBeKept'}}, + { submodule: { name: 'shouldBeKept' }, config: { name: 'shouldBeKept' } }, ]; const submoduleRegistry = [ sharedIdSystemSubmodule, createMockIdSubmodule('shouldBeKept', { id: { uid2: { id: 'uid2_value' } } }, null, null), ]; const configRegistry = [{ name: 'sharedId' }]; - const result = generateSubmoduleContainers({retainConfig: true}, configRegistry, previousSubmoduleContainers, submoduleRegistry); + const result = generateSubmoduleContainers({ retainConfig: true }, configRegistry, previousSubmoduleContainers, submoduleRegistry); expect(result).to.have.lengthOf(2); expect(result[0].submodule.name).to.eql('sharedId'); expect(result[1].submodule.name).to.eql('shouldBeKept'); @@ -3267,8 +3267,8 @@ describe('User ID', function () { it('should properly map registry to submodule containers for autoRefresh flag', () => { const previousSubmoduleContainers = [ - {submodule: {name: 'modified'}, config: {name: 'modified', auctionDelay: 300}}, - {submodule: {name: 'unchanged'}, config: {name: 'unchanged', auctionDelay: 300}}, + { submodule: { name: 'modified' }, config: { name: 'modified', auctionDelay: 300 } }, + { submodule: { name: 'unchanged' }, config: { name: 'unchanged', auctionDelay: 300 } }, ]; const submoduleRegistry = [ createMockIdSubmodule('modified', { id: { uid2: { id: 'uid2_value' } } }, null, null), @@ -3276,11 +3276,11 @@ describe('User ID', function () { createMockIdSubmodule('unchanged', { id: { uid2: { id: 'uid2_value' } } }, null, null), ]; const configRegistry = [ - {name: 'modified', auctionDelay: 200}, - {name: 'new'}, - {name: 'unchanged', auctionDelay: 300}, + { name: 'modified', auctionDelay: 200 }, + { name: 'new' }, + { name: 'unchanged', auctionDelay: 300 }, ]; - const result = generateSubmoduleContainers({autoRefresh: true}, configRegistry, previousSubmoduleContainers, submoduleRegistry); + const result = generateSubmoduleContainers({ autoRefresh: true }, configRegistry, previousSubmoduleContainers, submoduleRegistry); expect(result).to.have.lengthOf(3); const itemsWithRefreshIds = result.filter(item => item.refreshIds); const submoduleNames = itemsWithRefreshIds.map(item => item.submodule.name); @@ -3305,7 +3305,7 @@ describe('User ID', function () { before(() => { setSubmoduleRegistry([ - createMockIdSubmodule(UID_MODULE_NAME, {id: {uid2: {id: 'uid2_value'}}}, null, []), + createMockIdSubmodule(UID_MODULE_NAME, { id: { uid2: { id: 'uid2_value' } } }, null, []), ]); }) @@ -3319,15 +3319,15 @@ describe('User ID', function () { }); it('should not warn when reading', () => { - config.setConfig({userSync}); - const storage = getStorageManager({moduleType: MODULE_TYPE_UID, moduleName: UID_MODULE_NAME}); + config.setConfig({ userSync }); + const storage = getStorageManager({ moduleType: MODULE_TYPE_UID, moduleName: UID_MODULE_NAME }); storage.cookiesAreEnabled(); sinon.assert.notCalled(warnLogSpy); }) it('should warn and allow userId module to store data for enforceStorageType unset', () => { - config.setConfig({userSync}); - const storage = getStorageManager({moduleType: MODULE_TYPE_UID, moduleName: UID_MODULE_NAME}); + config.setConfig({ userSync }); + const storage = getStorageManager({ moduleType: MODULE_TYPE_UID, moduleName: UID_MODULE_NAME }); storage.setCookie(cookieName, 'value', 20000); sinon.assert.calledWith(warnLogSpy, `${UID_MODULE_NAME} attempts to store data in ${STORAGE_TYPE_COOKIES} while configuration allows ${STORAGE_TYPE_LOCALSTORAGE}.`); expect(storage.getCookie(cookieName)).to.eql('value'); @@ -3340,7 +3340,7 @@ describe('User ID', function () { ...userSync, } }) - const storage = getStorageManager({moduleType: MODULE_TYPE_UID, moduleName: UID_MODULE_NAME}); + const storage = getStorageManager({ moduleType: MODULE_TYPE_UID, moduleName: UID_MODULE_NAME }); storage.setCookie(cookieName, 'value', 20000); expect(storage.getCookie(cookieName)).to.not.exist; }); diff --git a/test/spec/modules/utiqIdSystem_spec.js b/test/spec/modules/utiqIdSystem_spec.js index 67a40928116..64cceebedbd 100644 --- a/test/spec/modules/utiqIdSystem_spec.js +++ b/test/spec/modules/utiqIdSystem_spec.js @@ -7,7 +7,7 @@ describe('utiqIdSystem', () => { const getStorageData = (idGraph) => { if (!idGraph) { - idGraph = {id: 501, domain: ''}; + idGraph = { id: 501, domain: '' }; } return { 'connectId': { @@ -105,7 +105,7 @@ describe('utiqIdSystem', () => { 'atid': 'atidValue', }; - const response = utiqIdSubmodule.getId({params: {maxDelayTime: 200}}); + const response = utiqIdSubmodule.getId({ params: { maxDelayTime: 200 } }); expect(response).to.have.property('callback'); expect(response.callback.toString()).contain('result(callback)'); @@ -139,12 +139,12 @@ describe('utiqIdSystem', () => { VALID_API_RESPONSES.forEach(responseData => { it('should return a newly constructed object with the utiq for a payload with {utiq: value}', () => { expect(utiqIdSubmodule.decode(responseData.payload)).to.deep.equal( - {utiq: responseData.expected} + { utiq: responseData.expected } ); }); }); - [{}, '', {foo: 'bar'}].forEach((response) => { + [{}, '', { foo: 'bar' }].forEach((response) => { it(`should return null for an invalid response "${JSON.stringify(response)}"`, () => { expect(utiqIdSubmodule.decode(response)).to.be.null; }); diff --git a/test/spec/modules/utiqMtpIdSystem_spec.js b/test/spec/modules/utiqMtpIdSystem_spec.js index 19c42ba1495..7d91750683d 100644 --- a/test/spec/modules/utiqMtpIdSystem_spec.js +++ b/test/spec/modules/utiqMtpIdSystem_spec.js @@ -6,7 +6,7 @@ describe('utiqMtpIdSystem', () => { const getStorageData = (idGraph) => { if (!idGraph) { - idGraph = {id: 501, domain: ''}; + idGraph = { id: 501, domain: '' }; } return { 'connectId': { @@ -104,7 +104,7 @@ describe('utiqMtpIdSystem', () => { 'mtid': 'mtidValue', }; - const response = utiqMtpIdSubmodule.getId({params: {maxDelayTime: 200}}); + const response = utiqMtpIdSubmodule.getId({ params: { maxDelayTime: 200 } }); expect(response).to.have.property('callback'); expect(response.callback.toString()).contain('result(callback)'); @@ -138,12 +138,12 @@ describe('utiqMtpIdSystem', () => { VALID_API_RESPONSES.forEach(responseData => { it('should return a newly constructed object with the utiqMtp for a payload with {utiqMtp: value}', () => { expect(utiqMtpIdSubmodule.decode(responseData.payload)).to.deep.equal( - {utiqMtp: responseData.expected} + { utiqMtp: responseData.expected } ); }); }); - [{}, '', {foo: 'bar'}].forEach((response) => { + [{}, '', { foo: 'bar' }].forEach((response) => { it(`should return null for an invalid response "${JSON.stringify(response)}"`, () => { expect(utiqMtpIdSubmodule.decode(response)).to.be.null; }); diff --git a/test/spec/modules/validationFpdModule_spec.js b/test/spec/modules/validationFpdModule_spec.js index 73e3cbbfcab..12935ec5350 100644 --- a/test/spec/modules/validationFpdModule_spec.js +++ b/test/spec/modules/validationFpdModule_spec.js @@ -1,4 +1,4 @@ -import {expect} from 'chai'; +import { expect } from 'chai'; import * as utils from 'src/utils.js'; import { filterArrayData, @@ -65,7 +65,7 @@ describe('the first party data validation module', function () { it('returns empty array if no valid data', function () { const arr = [{}]; const path = 'site.children.cat'; - const child = {type: 'string'}; + const child = { type: 'string' }; const parent = 'site'; const key = 'cat'; const validated = filterArrayData(arr, child, path, parent, key); @@ -73,9 +73,9 @@ describe('the first party data validation module', function () { }); it('filters invalid type of array data', function () { - const arr = ['foo', {test: 1}]; + const arr = ['foo', { test: 1 }]; const path = 'site.children.cat'; - const child = {type: 'string'}; + const child = { type: 'string' }; const parent = 'site'; const key = 'cat'; const validated = filterArrayData(arr, child, path, parent, key); @@ -83,9 +83,9 @@ describe('the first party data validation module', function () { }); it('filters all data for missing required children', function () { - const arr = [{test: 1}]; + const arr = [{ test: 1 }]; const path = 'site.children.content.children.data'; - const child = {type: 'object'}; + const child = { type: 'object' }; const parent = 'site'; const key = 'data'; const validated = filterArrayData(arr, child, path, parent, key); @@ -93,9 +93,9 @@ describe('the first party data validation module', function () { }); it('filters all data for invalid required children types', function () { - const arr = [{name: 'foo', segment: 1}]; + const arr = [{ name: 'foo', segment: 1 }]; const path = 'site.children.content.children.data'; - const child = {type: 'object'}; + const child = { type: 'object' }; const parent = 'site'; const key = 'data'; const validated = filterArrayData(arr, child, path, parent, key); @@ -103,13 +103,13 @@ describe('the first party data validation module', function () { }); it('returns only data with valid required nested children types', function () { - const arr = [{name: 'foo', segment: [{id: '1'}, {id: 2}, 'foobar']}]; + const arr = [{ name: 'foo', segment: [{ id: '1' }, { id: 2 }, 'foobar'] }]; const path = 'site.children.content.children.data'; - const child = {type: 'object'}; + const child = { type: 'object' }; const parent = 'site'; const key = 'data'; const validated = filterArrayData(arr, child, path, parent, key); - expect(validated).to.deep.equal([{name: 'foo', segment: [{id: '1'}]}]); + expect(validated).to.deep.equal([{ name: 'foo', segment: [{ id: '1' }] }]); }); }); @@ -189,8 +189,8 @@ describe('the first party data validation module', function () { } }; - duplicate.user.data[0].segment.push({test: 3}); - duplicate.user.data[0].segment[0] = {foo: 'bar'}; + duplicate.user.data[0].segment.push({ test: 3 }); + duplicate.user.data[0].segment[0] = { foo: 'bar' }; validated = validateFpd(duplicate); expect(validated).to.deep.equal(expected); @@ -227,7 +227,7 @@ describe('the first party data validation module', function () { } }; - duplicate.site.content.data[0].segment.push({test: 3}); + duplicate.site.content.data[0].segment.push({ test: 3 }); validated = validateFpd(duplicate); expect(validated).to.deep.equal(expected); @@ -265,7 +265,7 @@ describe('the first party data validation module', function () { } }; - duplicate.site.content.data[0].segment.push({test: 3}); + duplicate.site.content.data[0].segment.push({ test: 3 }); validated = validateFpd(duplicate); expect(validated).to.deep.equal(expected); @@ -304,7 +304,7 @@ describe('the first party data validation module', function () { } }; - duplicate.site.content.data[0].segment.push({test: 3}); + duplicate.site.content.data[0].segment.push({ test: 3 }); validated = validateFpd(duplicate); expect(validated).to.deep.equal(expected); diff --git a/test/spec/modules/valuadBidAdapter_spec.js b/test/spec/modules/valuadBidAdapter_spec.js index d2e05930619..4a27bbd32df 100644 --- a/test/spec/modules/valuadBidAdapter_spec.js +++ b/test/spec/modules/valuadBidAdapter_spec.js @@ -6,7 +6,6 @@ import { BANNER } from 'src/mediaTypes.js'; import { deepClone, generateUUID } from 'src/utils.js'; import { config } from 'src/config.js'; import * as utils from 'src/utils.js'; -import * as dnt from 'libraries/dnt/index.js'; import * as gptUtils from 'libraries/gptUtils/gptUtils.js'; import * as refererDetection from 'src/refererDetection.js'; import * as BoundingClientRect from 'libraries/boundingClientRect/boundingClientRect.js'; @@ -122,7 +121,6 @@ describe('ValuadAdapter', function () { }); sandbox.stub(utils, 'canAccessWindowTop').returns(true); - sandbox.stub(dnt, 'getDNT').returns(false); sandbox.stub(utils, 'generateUUID').returns('test-uuid'); sandbox.stub(refererDetection, 'parseDomain').returns('test.com'); diff --git a/test/spec/modules/vdoaiBidAdapter_spec.js b/test/spec/modules/vdoaiBidAdapter_spec.js index 4f3d9621e13..5514844f8ce 100644 --- a/test/spec/modules/vdoaiBidAdapter_spec.js +++ b/test/spec/modules/vdoaiBidAdapter_spec.js @@ -349,7 +349,7 @@ describe('vdoaiBidAdapter', function () { }) describe('interpretBannerResponse', function () { const resObject = { - body: [ { + body: [{ requestId: '123', cpm: 0.3, width: 320, @@ -363,7 +363,7 @@ describe('vdoaiBidAdapter', function () { advertiserDomains: ['example.com'], mediaType: 'banner' } - } ] + }] }; let serverResponses = spec.interpretResponse(resObject); it('Returns an array of valid server responses if response object is valid', function () { @@ -392,7 +392,7 @@ describe('vdoaiBidAdapter', function () { }); describe('interpretVideoResponse', function () { const resObject = { - body: [ { + body: [{ requestId: '123', cpm: 0.3, width: 320, @@ -406,7 +406,7 @@ describe('vdoaiBidAdapter', function () { advertiserDomains: ['example.com'], mediaType: 'video' } - } ] + }] }; let serverResponses = spec.interpretResponse(resObject); it('Returns an array of valid server responses if response object is valid', function () { @@ -494,7 +494,7 @@ describe('vdoaiBidAdapter', function () { }; it('should skip responses which do not contain required params', function() { const bidResponses = { - body: [ { + body: [{ cpm: 0.3, ttl: 1000, currency: 'USD', @@ -502,25 +502,25 @@ describe('vdoaiBidAdapter', function () { advertiserDomains: ['example.com'], mediaType: 'banner' } - }, resObject ] + }, resObject] } - expect(spec.interpretResponse(bidResponses)).to.deep.equal([ resObject ]); + expect(spec.interpretResponse(bidResponses)).to.deep.equal([resObject]); }); it('should skip responses which do not contain advertiser domains', function() { const resObjectWithoutAdvertiserDomains = Object.assign({}, resObject); resObjectWithoutAdvertiserDomains.meta = Object.assign({}, resObject.meta); delete resObjectWithoutAdvertiserDomains.meta.advertiserDomains; const bidResponses = { - body: [ resObjectWithoutAdvertiserDomains, resObject ] + body: [resObjectWithoutAdvertiserDomains, resObject] } - expect(spec.interpretResponse(bidResponses)).to.deep.equal([ resObject ]); + expect(spec.interpretResponse(bidResponses)).to.deep.equal([resObject]); }); it('should return responses which contain empty advertiser domains', function() { const resObjectWithEmptyAdvertiserDomains = Object.assign({}, resObject); resObjectWithEmptyAdvertiserDomains.meta = Object.assign({}, resObject.meta); resObjectWithEmptyAdvertiserDomains.meta.advertiserDomains = []; const bidResponses = { - body: [ resObjectWithEmptyAdvertiserDomains, resObject ] + body: [resObjectWithEmptyAdvertiserDomains, resObject] } expect(spec.interpretResponse(bidResponses)).to.deep.equal([resObjectWithEmptyAdvertiserDomains, resObject]); }); @@ -529,9 +529,9 @@ describe('vdoaiBidAdapter', function () { resObjectWithoutMetaMediaType.meta = Object.assign({}, resObject.meta); delete resObjectWithoutMetaMediaType.meta.mediaType; const bidResponses = { - body: [ resObjectWithoutMetaMediaType, resObject ] + body: [resObjectWithoutMetaMediaType, resObject] } - expect(spec.interpretResponse(bidResponses)).to.deep.equal([ resObject ]); + expect(spec.interpretResponse(bidResponses)).to.deep.equal([resObject]); }); }); describe('getUserSyncs', function () { diff --git a/test/spec/modules/verbenBidAdapter_spec.js b/test/spec/modules/verbenBidAdapter_spec.js new file mode 100644 index 00000000000..9864e9d2b70 --- /dev/null +++ b/test/spec/modules/verbenBidAdapter_spec.js @@ -0,0 +1,478 @@ +import { expect } from 'chai'; +import { spec } from '../../../modules/verbenBidAdapter.js'; +import { BANNER, VIDEO, NATIVE } from '../../../src/mediaTypes.js'; +import { getUniqueIdentifierStr } from '../../../src/utils.js'; + +const bidder = 'verben'; + +describe('VerbenBidAdapter', function () { + const userIdAsEids = [{ + source: 'test.org', + uids: [{ + id: '01**********', + atype: 1, + ext: { + third: '01***********' + } + }] + }]; + const bids = [ + { + bidId: getUniqueIdentifierStr(), + bidder: bidder, + mediaTypes: { + [BANNER]: { + sizes: [[300, 250]] + } + }, + params: { + placementId: 'testBanner' + }, + userIdAsEids + }, + { + bidId: getUniqueIdentifierStr(), + bidder: bidder, + mediaTypes: { + [VIDEO]: { + playerSize: [[300, 300]], + minduration: 5, + maxduration: 60 + } + }, + params: { + placementId: 'testVideo' + }, + userIdAsEids + }, + { + bidId: getUniqueIdentifierStr(), + bidder: bidder, + mediaTypes: { + [NATIVE]: { + native: { + title: { + required: true + }, + body: { + required: true + }, + icon: { + required: true, + size: [64, 64] + } + } + } + }, + params: { + placementId: 'testNative' + }, + userIdAsEids + } + ]; + + const invalidBid = { + bidId: getUniqueIdentifierStr(), + bidder: bidder, + mediaTypes: { + [BANNER]: { + sizes: [[300, 250]] + } + }, + params: { + + } + } + + const bidderRequest = { + uspConsent: '1---', + gdprConsent: { + consentString: 'COvFyGBOvFyGBAbAAAENAPCAAOAAAAAAAAAAAEEUACCKAAA.IFoEUQQgAIQwgIwQABAEAAAAOIAACAIAAAAQAIAgEAACEAAAAAgAQBAAAAAAAGBAAgAAAAAAAFAAECAAAgAAQARAEQAAAAAJAAIAAgAAAYQEAAAQmAgBC3ZAYzUw', + vendorData: {} + }, + refererInfo: { + referer: 'https://test.com', + page: 'https://test.com' + }, + ortb2: { + device: { + w: 1512, + h: 982, + language: 'en-UK' + } + }, + timeout: 500 + }; + + describe('isBidRequestValid', function () { + it('Should return true if there are bidId, params and key parameters present', function () { + expect(spec.isBidRequestValid(bids[0])).to.be.true; + }); + it('Should return false if at least one of parameters is not present', function () { + expect(spec.isBidRequestValid(invalidBid)).to.be.false; + }); + }); + + describe('buildRequests', function () { + let serverRequest = spec.buildRequests(bids, bidderRequest); + + 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('https://east-node.verben.com/pbjs'); + }); + + it('Returns general data valid', function () { + const data = serverRequest.data; + expect(data).to.be.an('object'); + expect(data).to.have.all.keys('deviceWidth', + 'deviceHeight', + 'device', + 'language', + 'secure', + 'host', + 'page', + 'placements', + 'coppa', + 'ccpa', + 'gdpr', + 'tmax', + 'bcat', + 'badv', + 'bapp', + 'battr' + ); + expect(data.deviceWidth).to.be.a('number'); + expect(data.deviceHeight).to.be.a('number'); + expect(data.language).to.be.a('string'); + expect(data.secure).to.be.within(0, 1); + expect(data.host).to.be.a('string'); + expect(data.page).to.be.a('string'); + expect(data.coppa).to.be.a('number'); + expect(data.gdpr).to.be.a('object'); + expect(data.ccpa).to.be.a('string'); + expect(data.tmax).to.be.a('number'); + expect(data.placements).to.have.lengthOf(3); + }); + + it('Returns valid placements', function () { + const { placements } = serverRequest.data; + for (let i = 0, len = placements.length; i < len; i++) { + const placement = placements[i]; + expect(placement.placementId).to.be.oneOf(['testBanner', 'testVideo', 'testNative']); + expect(placement.adFormat).to.be.oneOf([BANNER, VIDEO, NATIVE]); + expect(placement.bidId).to.be.a('string'); + expect(placement.schain).to.be.an('object'); + expect(placement.bidfloor).to.exist.and.to.equal(0); + expect(placement.type).to.exist.and.to.equal('publisher'); + expect(placement.eids).to.exist.and.to.be.deep.equal(userIdAsEids); + + if (placement.adFormat === BANNER) { + expect(placement.sizes).to.be.an('array'); + } + switch (placement.adFormat) { + case BANNER: + expect(placement.sizes).to.be.an('array'); + break; + case VIDEO: + expect(placement.playerSize).to.be.an('array'); + expect(placement.minduration).to.be.an('number'); + expect(placement.maxduration).to.be.an('number'); + break; + case NATIVE: + expect(placement.native).to.be.an('object'); + break; + } + } + }); + + it('Returns valid endpoints', function () { + const bids = [ + { + bidId: getUniqueIdentifierStr(), + bidder: bidder, + mediaTypes: { + [BANNER]: { + sizes: [[300, 250]] + } + }, + params: { + endpointId: 'testBanner', + }, + userIdAsEids + } + ]; + + const serverRequest = spec.buildRequests(bids, bidderRequest); + + const { placements } = serverRequest.data; + for (let i = 0, len = placements.length; i < len; i++) { + const placement = placements[i]; + expect(placement.endpointId).to.be.oneOf(['testBanner', 'testVideo', 'testNative']); + expect(placement.adFormat).to.be.oneOf([BANNER, VIDEO, NATIVE]); + expect(placement.bidId).to.be.a('string'); + expect(placement.schain).to.be.an('object'); + expect(placement.bidfloor).to.exist.and.to.equal(0); + expect(placement.type).to.exist.and.to.equal('network'); + expect(placement.eids).to.exist.and.to.be.deep.equal(userIdAsEids); + + if (placement.adFormat === BANNER) { + expect(placement.sizes).to.be.an('array'); + } + switch (placement.adFormat) { + case BANNER: + expect(placement.sizes).to.be.an('array'); + break; + case VIDEO: + expect(placement.playerSize).to.be.an('array'); + expect(placement.minduration).to.be.an('number'); + expect(placement.maxduration).to.be.an('number'); + break; + case NATIVE: + expect(placement.native).to.be.an('object'); + break; + } + } + }); + + it('Returns data with gdprConsent and without uspConsent', function () { + delete bidderRequest.uspConsent; + serverRequest = spec.buildRequests(bids, bidderRequest); + const data = serverRequest.data; + expect(data.gdpr).to.exist; + expect(data.gdpr).to.be.a('object'); + expect(data.gdpr).to.have.property('consentString'); + expect(data.gdpr).to.not.have.property('vendorData'); + expect(data.gdpr.consentString).to.equal(bidderRequest.gdprConsent.consentString); + expect(data.ccpa).to.not.exist; + delete bidderRequest.gdprConsent; + }); + + it('Returns data with uspConsent and without gdprConsent', function () { + bidderRequest.uspConsent = '1---'; + delete bidderRequest.gdprConsent; + serverRequest = spec.buildRequests(bids, bidderRequest); + const data = serverRequest.data; + expect(data.ccpa).to.exist; + expect(data.ccpa).to.be.a('string'); + expect(data.ccpa).to.equal(bidderRequest.uspConsent); + expect(data.gdpr).to.not.exist; + }); + }); + + describe('gpp consent', function () { + it('bidderRequest.gppConsent', () => { + bidderRequest.gppConsent = { + gppString: 'abc123', + applicableSections: [8] + }; + + const serverRequest = spec.buildRequests(bids, bidderRequest); + const data = serverRequest.data; + expect(data).to.be.an('object'); + expect(data).to.have.property('gpp'); + expect(data).to.have.property('gpp_sid'); + + delete bidderRequest.gppConsent; + }) + + it('bidderRequest.ortb2.regs.gpp', () => { + bidderRequest.ortb2 = bidderRequest.ortb2 || {}; + bidderRequest.ortb2.regs = bidderRequest.ortb2.regs || {}; + bidderRequest.ortb2.regs.gpp = 'abc123'; + bidderRequest.ortb2.regs.gpp_sid = [8]; + + const serverRequest = spec.buildRequests(bids, bidderRequest); + const data = serverRequest.data; + expect(data).to.be.an('object'); + expect(data).to.have.property('gpp'); + expect(data).to.have.property('gpp_sid'); + }) + }); + + describe('interpretResponse', function () { + it('Should interpret banner response', function () { + const banner = { + body: [{ + mediaType: 'banner', + width: 300, + height: 250, + cpm: 0.4, + ad: 'Test', + requestId: '23fhj33i987f', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1', + meta: { + advertiserDomains: ['google.com'], + advertiserId: 1234 + } + }] + }; + const bannerResponses = spec.interpretResponse(banner); + expect(bannerResponses).to.be.an('array').that.is.not.empty; + const dataItem = bannerResponses[0]; + expect(dataItem).to.have.all.keys('requestId', 'cpm', 'width', 'height', 'ad', 'ttl', 'creativeId', + 'netRevenue', 'currency', 'dealId', 'mediaType', 'meta'); + expect(dataItem.requestId).to.equal(banner.body[0].requestId); + expect(dataItem.cpm).to.equal(banner.body[0].cpm); + expect(dataItem.width).to.equal(banner.body[0].width); + expect(dataItem.height).to.equal(banner.body[0].height); + expect(dataItem.ad).to.equal(banner.body[0].ad); + expect(dataItem.ttl).to.equal(banner.body[0].ttl); + expect(dataItem.creativeId).to.equal(banner.body[0].creativeId); + expect(dataItem.netRevenue).to.be.true; + expect(dataItem.currency).to.equal(banner.body[0].currency); + expect(dataItem.meta).to.be.an('object').that.has.any.key('advertiserDomains'); + }); + it('Should interpret video response', function () { + const video = { + body: [{ + vastUrl: 'test.com', + mediaType: 'video', + cpm: 0.5, + requestId: '23fhj33i987f', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1', + meta: { + advertiserDomains: ['google.com'], + advertiserId: 1234 + } + }] + }; + const videoResponses = spec.interpretResponse(video); + expect(videoResponses).to.be.an('array').that.is.not.empty; + + const dataItem = videoResponses[0]; + expect(dataItem).to.have.all.keys('requestId', 'cpm', 'vastUrl', 'ttl', 'creativeId', + 'netRevenue', 'currency', 'dealId', 'mediaType', 'meta'); + expect(dataItem.requestId).to.equal('23fhj33i987f'); + expect(dataItem.cpm).to.equal(0.5); + expect(dataItem.vastUrl).to.equal('test.com'); + expect(dataItem.ttl).to.equal(120); + expect(dataItem.creativeId).to.equal('2'); + expect(dataItem.netRevenue).to.be.true; + expect(dataItem.currency).to.equal('USD'); + expect(dataItem.meta).to.be.an('object').that.has.any.key('advertiserDomains'); + }); + it('Should interpret native response', function () { + const native = { + body: [{ + mediaType: 'native', + native: { + clickUrl: 'test.com', + title: 'Test', + image: 'test.com', + impressionTrackers: ['test.com'], + }, + ttl: 120, + cpm: 0.4, + requestId: '23fhj33i987f', + creativeId: '2', + netRevenue: true, + currency: 'USD', + meta: { + advertiserDomains: ['google.com'], + advertiserId: 1234 + } + }] + }; + const nativeResponses = spec.interpretResponse(native); + expect(nativeResponses).to.be.an('array').that.is.not.empty; + + const dataItem = nativeResponses[0]; + expect(dataItem).to.have.keys('requestId', 'cpm', 'ttl', 'creativeId', 'netRevenue', 'currency', 'mediaType', 'native', 'meta'); + expect(dataItem.native).to.have.keys('clickUrl', 'impressionTrackers', 'title', 'image') + expect(dataItem.requestId).to.equal('23fhj33i987f'); + expect(dataItem.cpm).to.equal(0.4); + expect(dataItem.native.clickUrl).to.equal('test.com'); + expect(dataItem.native.title).to.equal('Test'); + expect(dataItem.native.image).to.equal('test.com'); + expect(dataItem.native.impressionTrackers).to.be.an('array').that.is.not.empty; + expect(dataItem.native.impressionTrackers[0]).to.equal('test.com'); + expect(dataItem.ttl).to.equal(120); + expect(dataItem.creativeId).to.equal('2'); + expect(dataItem.netRevenue).to.be.true; + expect(dataItem.currency).to.equal('USD'); + expect(dataItem.meta).to.be.an('object').that.has.any.key('advertiserDomains'); + }); + it('Should return an empty array if invalid banner response is passed', function () { + const invBanner = { + body: [{ + width: 300, + cpm: 0.4, + ad: 'Test', + requestId: '23fhj33i987f', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1' + }] + }; + + const 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', function () { + const invVideo = { + body: [{ + mediaType: 'video', + cpm: 0.5, + requestId: '23fhj33i987f', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1' + }] + }; + const 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', function () { + const invNative = { + body: [{ + mediaType: 'native', + clickUrl: 'test.com', + title: 'Test', + impressionTrackers: ['test.com'], + ttl: 120, + requestId: '23fhj33i987f', + creativeId: '2', + netRevenue: true, + currency: 'USD', + }] + }; + const serverResponses = spec.interpretResponse(invNative); + expect(serverResponses).to.be.an('array').that.is.empty; + }); + it('Should return an empty array if invalid response is passed', function () { + const invalid = { + body: [{ + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1' + }] + }; + const serverResponses = spec.interpretResponse(invalid); + expect(serverResponses).to.be.an('array').that.is.empty; + }); + }); +}); diff --git a/test/spec/modules/viantBidAdapter_spec.js b/test/spec/modules/viantBidAdapter_spec.js index 46717e1518c..7683578f2ce 100644 --- a/test/spec/modules/viantBidAdapter_spec.js +++ b/test/spec/modules/viantBidAdapter_spec.js @@ -1,8 +1,8 @@ -import {spec, converter} from 'modules/viantBidAdapter.js'; -import {assert, expect} from 'chai'; -import {deepClone} from '../../../src/utils.js'; -import {buildWindowTree} from '../../helpers/refererDetectionHelper.js'; -import {detectReferer} from '../../../src/refererDetection.js'; +import { spec, converter } from 'modules/viantBidAdapter.js'; +import { assert, expect } from 'chai'; +import { deepClone } from '../../../src/utils.js'; +import { buildWindowTree } from '../../helpers/refererDetectionHelper.js'; +import { detectReferer } from '../../../src/refererDetection.js'; describe('viantOrtbBidAdapter', function () { function testBuildRequests(bidRequests, bidderRequestBase) { @@ -353,7 +353,7 @@ describe('viantOrtbBidAdapter', function () { } it('assert video and its fields is present in imp ', function () { - const requests = spec.buildRequests([makeBid()], {referrerInfo: {}}); + const requests = spec.buildRequests([makeBid()], { referrerInfo: {} }); const clonedRequests = deepClone(requests) assert.equal(clonedRequests[0].data.imp[0].video.mimes[0], 'video/mp4') assert.equal(clonedRequests[0].data.imp[0].video.maxduration, 31) @@ -400,8 +400,8 @@ describe('viantOrtbBidAdapter', function () { it('empty bid response test', function () { const request = testBuildRequests(baseBannerBidRequests, baseBidderRequest)[0]; - const bidResponse = {nbr: 0}; // Unknown error - const bids = spec.interpretResponse({body: bidResponse}, request); + const bidResponse = { nbr: 0 }; // Unknown error + const bids = spec.interpretResponse({ body: bidResponse }, request); expect(bids.length).to.equal(0); }); @@ -421,7 +421,7 @@ describe('viantOrtbBidAdapter', function () { }], cur: 'USD' }; - const bids = spec.interpretResponse({body: bidResponse}, request); + const bids = spec.interpretResponse({ body: bidResponse }, request); expect(bids.length).to.equal(1); const bid = bids[0]; it('should return the proper mediaType', function () { @@ -537,7 +537,7 @@ describe('viantOrtbBidAdapter', function () { ], 'cur': 'USD' }; - const bids = spec.interpretResponse({body: VIDEO_BID_RESPONSE}, request); + const bids = spec.interpretResponse({ body: VIDEO_BID_RESPONSE }, request); expect(bids.length).to.equal(1); const bid = bids[0]; it('should return the proper mediaType', function () { diff --git a/test/spec/modules/vibrantmediaBidAdapter_spec.js b/test/spec/modules/vibrantmediaBidAdapter_spec.js index 6aaa84a00c5..4522f383dfe 100644 --- a/test/spec/modules/vibrantmediaBidAdapter_spec.js +++ b/test/spec/modules/vibrantmediaBidAdapter_spec.js @@ -1,8 +1,8 @@ -import {expect} from 'chai'; -import {spec} from 'modules/vibrantmediaBidAdapter.js'; -import {newBidder} from 'src/adapters/bidderFactory.js'; -import {BANNER, NATIVE, VIDEO} from 'src/mediaTypes.js'; -import {INSTREAM, OUTSTREAM} from 'src/video.js'; +import { expect } from 'chai'; +import { spec } from 'modules/vibrantmediaBidAdapter.js'; +import { newBidder } from 'src/adapters/bidderFactory.js'; +import { BANNER, NATIVE, VIDEO } from 'src/mediaTypes.js'; +import { INSTREAM, OUTSTREAM } from 'src/video.js'; import { getWinDimensions } from '../../../src/utils.js'; const EXPECTED_PREBID_SERVER_URL = 'https://prebid.intellitxt.com/prebid'; diff --git a/test/spec/modules/vidazooBidAdapter_spec.js b/test/spec/modules/vidazooBidAdapter_spec.js index ab0820ef32e..08b68058cd2 100644 --- a/test/spec/modules/vidazooBidAdapter_spec.js +++ b/test/spec/modules/vidazooBidAdapter_spec.js @@ -1,4 +1,4 @@ -import {expect} from 'chai'; +import { expect } from 'chai'; import { spec as adapter, storage, @@ -19,12 +19,12 @@ import { getVidazooSessionId } from 'libraries/vidazooUtils/bidderUtils.js' import * as utils from 'src/utils.js'; -import {version} from 'package.json'; -import {useFakeTimers} from 'sinon'; -import {BANNER, VIDEO} from '../../../src/mediaTypes.js'; -import {config} from '../../../src/config.js'; -import {deepSetValue} from 'src/utils.js'; -import {getGlobal} from '../../../src/prebidGlobal.js'; +import { version } from 'package.json'; +import { useFakeTimers } from 'sinon'; +import { BANNER, VIDEO } from '../../../src/mediaTypes.js'; +import { config } from '../../../src/config.js'; +import { deepSetValue } from 'src/utils.js'; +import { getGlobal } from '../../../src/prebidGlobal.js'; export const TEST_ID_SYSTEMS = ['criteoId', 'id5id', 'idl_env', 'lipb', 'netId', 'pubcid', 'tdid', 'pubProvidedId']; @@ -107,9 +107,9 @@ const ORTB2_DEVICE = { 'version': ['8', '0', '0'] }, 'browsers': [ - {'brand': 'Not_A Brand', 'version': ['99', '0', '0', '0']}, - {'brand': 'Google Chrome', 'version': ['109', '0', '5414', '119']}, - {'brand': 'Chromium', 'version': ['109', '0', '5414', '119']} + { 'brand': 'Not_A Brand', 'version': ['99', '0', '0', '0'] }, + { 'brand': 'Google Chrome', 'version': ['109', '0', '5414', '119'] }, + { 'brand': 'Chromium', 'version': ['109', '0', '5414', '119'] } ], 'mobile': 1, 'model': 'SM-G955U', @@ -126,7 +126,7 @@ const ORTB2_DEVICE = { model: 'iPhone 12 Pro Max', os: 'iOS', osv: '17.4', - ext: {fiftyonedegrees_deviceId: '17595-133085-133468-18092'}, + ext: { fiftyonedegrees_deviceId: '17595-133085-133468-18092' }, }; const BIDDER_REQUEST = { @@ -153,8 +153,8 @@ const BIDDER_REQUEST = { 'segtax': 7 }, 'segments': [ - {'id': 'segId1'}, - {'id': 'segId2'} + { 'id': 'segId1' }, + { 'id': 'segId2' } ] }] } @@ -168,9 +168,9 @@ const BIDDER_REQUEST = { user: { data: [ { - ext: {segtax: 600, segclass: '1'}, + ext: { segtax: 600, segclass: '1' }, name: 'example.com', - segment: [{id: '243'}], + segment: [{ id: '243' }], }, ], }, @@ -225,22 +225,22 @@ const VIDEO_SERVER_RESPONSE = { const ORTB2_OBJ = { "device": ORTB2_DEVICE, - "regs": {"coppa": 0, "gpp": "gpp_string", "gpp_sid": [7]}, + "regs": { "coppa": 0, "gpp": "gpp_string", "gpp_sid": [7] }, "site": { "cat": ["IAB2"], "content": { "data": [{ - "ext": {"segtax": 7}, + "ext": { "segtax": 7 }, "name": "example.com", - "segments": [{"id": "segId1"}, {"id": "segId2"}] + "segments": [{ "id": "segId1" }, { "id": "segId2" }] }], "language": "en" }, "pagecat": ["IAB2-2"] }, - "source": {"ext": {"omidpn": "MyIntegrationPartner", "omidpv": "7.1"}}, + "source": { "ext": { "omidpn": "MyIntegrationPartner", "omidpv": "7.1" } }, "user": { - "data": [{"ext": {"segclass": "1", "segtax": 600}, "name": "example.com", "segment": [{"id": "243"}]}] + "data": [{ "ext": { "segclass": "1", "segtax": 600 }, "name": "example.com", "segment": [{ "id": "243" }] }] } }; @@ -375,9 +375,9 @@ describe('VidazooBidAdapter', function () { 'version': ['8', '0', '0'] }, 'browsers': [ - {'brand': 'Not_A Brand', 'version': ['99', '0', '0', '0']}, - {'brand': 'Google Chrome', 'version': ['109', '0', '5414', '119']}, - {'brand': 'Chromium', 'version': ['109', '0', '5414', '119']} + { 'brand': 'Not_A Brand', 'version': ['99', '0', '0', '0'] }, + { 'brand': 'Google Chrome', 'version': ['109', '0', '5414', '119'] }, + { 'brand': 'Chromium', 'version': ['109', '0', '5414', '119'] } ], 'mobile': 1, 'model': 'SM-G955U', @@ -393,15 +393,15 @@ describe('VidazooBidAdapter', function () { 'segtax': 7 }, 'segments': [ - {'id': 'segId1'}, - {'id': 'segId2'} + { 'id': 'segId1' }, + { 'id': 'segId2' } ] }], userData: [ { - ext: {segtax: 600, segclass: '1'}, + ext: { segtax: 600, segclass: '1' }, name: 'example.com', - segment: [{id: '243'}], + segment: [{ id: '243' }], }, ], uniqueDealId: `${hashUrl}_${Date.now().toString()}`, @@ -462,9 +462,9 @@ describe('VidazooBidAdapter', function () { 'version': ['8', '0', '0'] }, 'browsers': [ - {'brand': 'Not_A Brand', 'version': ['99', '0', '0', '0']}, - {'brand': 'Google Chrome', 'version': ['109', '0', '5414', '119']}, - {'brand': 'Chromium', 'version': ['109', '0', '5414', '119']} + { 'brand': 'Not_A Brand', 'version': ['99', '0', '0', '0'] }, + { 'brand': 'Google Chrome', 'version': ['109', '0', '5414', '119'] }, + { 'brand': 'Chromium', 'version': ['109', '0', '5414', '119'] } ], 'mobile': 1, 'model': 'SM-G955U', @@ -506,15 +506,15 @@ describe('VidazooBidAdapter', function () { 'segtax': 7 }, 'segments': [ - {'id': 'segId1'}, - {'id': 'segId2'} + { 'id': 'segId1' }, + { 'id': 'segId2' } ] }], userData: [ { - ext: {segtax: 600, segclass: '1'}, + ext: { segtax: 600, segclass: '1' }, name: 'example.com', - segment: [{id: '243'}], + segment: [{ id: '243' }], }, ], webSessionId: webSessionId @@ -558,9 +558,9 @@ describe('VidazooBidAdapter', function () { 'version': ['8', '0', '0'] }, 'browsers': [ - {'brand': 'Not_A Brand', 'version': ['99', '0', '0', '0']}, - {'brand': 'Google Chrome', 'version': ['109', '0', '5414', '119']}, - {'brand': 'Chromium', 'version': ['109', '0', '5414', '119']} + { 'brand': 'Not_A Brand', 'version': ['99', '0', '0', '0'] }, + { 'brand': 'Google Chrome', 'version': ['109', '0', '5414', '119'] }, + { 'brand': 'Chromium', 'version': ['109', '0', '5414', '119'] } ], 'mobile': 1, 'model': 'SM-G955U', @@ -600,15 +600,15 @@ describe('VidazooBidAdapter', function () { 'segtax': 7 }, 'segments': [ - {'id': 'segId1'}, - {'id': 'segId2'} + { 'id': 'segId1' }, + { 'id': 'segId2' } ] }], userData: [ { - ext: {segtax: 600, segclass: '1'}, + ext: { segtax: 600, segclass: '1' }, name: 'example.com', - segment: [{id: '243'}], + segment: [{ id: '243' }], }, ], webSessionId: webSessionId @@ -626,10 +626,12 @@ describe('VidazooBidAdapter', function () { expect(requests[0]).to.deep.equal({ method: 'POST', url: `${createDomain(SUB_DOMAIN)}/prebid/multi/59db6b3b4ffaa70004f45cdc`, - data: {bids: [ - {...REQUEST_DATA, ortb2: ORTB2_OBJ, ortb2Imp: BID.ortb2Imp}, - {...REQUEST_DATA2, ortb2: ORTB2_OBJ, ortb2Imp: BID.ortb2Imp} - ]} + data: { + bids: [ + { ...REQUEST_DATA, ortb2: ORTB2_OBJ, ortb2Imp: BID.ortb2Imp }, + { ...REQUEST_DATA2, ortb2: ORTB2_OBJ, ortb2Imp: BID.ortb2Imp } + ] + } }); }); @@ -659,15 +661,6 @@ describe('VidazooBidAdapter', function () { expect(requests).to.have.length(2); }); - it('should set fledge correctly if enabled', function () { - config.resetConfig(); - const bidderRequest = utils.deepClone(BIDDER_REQUEST); - bidderRequest.paapi = {enabled: true}; - deepSetValue(bidderRequest, 'ortb2Imp.ext.ae', 1); - const requests = adapter.buildRequests([BID], bidderRequest); - expect(requests[0].data.fledge).to.equal(1); - }); - after(function () { getGlobal().bidderSettings = {}; config.resetConfig(); @@ -677,7 +670,7 @@ describe('VidazooBidAdapter', function () { describe('getUserSyncs', function () { it('should have valid user sync with iframeEnabled', function () { - const result = adapter.getUserSyncs({iframeEnabled: true}, [SERVER_RESPONSE]); + const result = adapter.getUserSyncs({ iframeEnabled: true }, [SERVER_RESPONSE]); expect(result).to.deep.equal([{ type: 'iframe', @@ -686,7 +679,7 @@ describe('VidazooBidAdapter', function () { }); it('should have valid user sync with cid on response', function () { - const result = adapter.getUserSyncs({iframeEnabled: true}, [SERVER_RESPONSE]); + const result = adapter.getUserSyncs({ iframeEnabled: true }, [SERVER_RESPONSE]); expect(result).to.deep.equal([{ type: 'iframe', url: 'https://sync.cootlogix.com/api/sync/iframe/?cid=testcid123&gdpr=0&gdpr_consent=&us_privacy=&coppa=0' @@ -694,7 +687,7 @@ describe('VidazooBidAdapter', function () { }); it('should have valid user sync with pixelEnabled', function () { - const result = adapter.getUserSyncs({pixelEnabled: true}, [SERVER_RESPONSE]); + const result = adapter.getUserSyncs({ pixelEnabled: true }, [SERVER_RESPONSE]); expect(result).to.deep.equal([{ 'url': 'https://sync.cootlogix.com/api/sync/image/?cid=testcid123&gdpr=0&gdpr_consent=&us_privacy=&coppa=0', @@ -706,7 +699,7 @@ describe('VidazooBidAdapter', function () { config.setConfig({ coppa: 1 }); - const result = adapter.getUserSyncs({iframeEnabled: true}, [SERVER_RESPONSE]); + const result = adapter.getUserSyncs({ iframeEnabled: true }, [SERVER_RESPONSE]); expect(result).to.deep.equal([{ type: 'iframe', url: 'https://sync.cootlogix.com/api/sync/iframe/?cid=testcid123&gdpr=0&gdpr_consent=&us_privacy=&coppa=1' @@ -721,12 +714,12 @@ describe('VidazooBidAdapter', function () { }); it('should return empty array when there is no ad', function () { - const responses = adapter.interpretResponse({price: 1, ad: ''}); + const responses = adapter.interpretResponse({ price: 1, ad: '' }); expect(responses).to.be.empty; }); it('should return empty array when there is no price', function () { - const responses = adapter.interpretResponse({price: null, ad: 'great ad'}); + const responses = adapter.interpretResponse({ price: null, ad: 'great ad' }); expect(responses).to.be.empty; }); @@ -822,9 +815,9 @@ describe('VidazooBidAdapter', function () { const userId = (function () { switch (idSystemProvider) { case 'lipb': - return {lipbid: id}; + return { lipbid: id }; case 'id5id': - return {uid: id}; + return { uid: id }; default: return id; } @@ -845,7 +838,7 @@ describe('VidazooBidAdapter', function () { bid.userIdAsEids = [ { "source": "audigent.com", - "uids": [{"id": "fakeidi6j6dlc6e"}] + "uids": [{ "id": "fakeidi6j6dlc6e" }] } ] const requests = adapter.buildRequests([bid], BIDDER_REQUEST); @@ -856,11 +849,11 @@ describe('VidazooBidAdapter', function () { bid.userIdAsEids = [ { "source": "audigent.com", - "uids": [{"id": "fakeidi6j6dlc6e"}] + "uids": [{ "id": "fakeidi6j6dlc6e" }] }, { "source": "rwdcntrl.net", - "uids": [{"id": "fakeid6f35197d5c", "atype": 1}] + "uids": [{ "id": "fakeid6f35197d5c", "atype": 1 }] } ] const requests = adapter.buildRequests([bid], BIDDER_REQUEST); @@ -875,7 +868,7 @@ describe('VidazooBidAdapter', function () { eids: [ { "source": "pubcid.org", - "uids": [{"id": "fakeid8888dlc6e"}] + "uids": [{ "id": "fakeid8888dlc6e" }] } ] } @@ -890,11 +883,11 @@ describe('VidazooBidAdapter', function () { eids: [ { "source": "pubcid.org", - "uids": [{"id": "fakeid8888dlc6e"}] + "uids": [{ "id": "fakeid8888dlc6e" }] }, { "source": "adserver.org", - "uids": [{"id": "fakeid495ff1"}] + "uids": [{ "id": "fakeid495ff1" }] } ] } @@ -907,18 +900,18 @@ describe('VidazooBidAdapter', function () { describe('alternate param names extractors', function () { it('should return undefined when param not supported', function () { - const cid = extractCID({'c_id': '1'}); - const pid = extractPID({'p_id': '1'}); - const subDomain = extractSubDomain({'sub_domain': 'prebid'}); + const cid = extractCID({ 'c_id': '1' }); + const pid = extractPID({ 'p_id': '1' }); + const subDomain = extractSubDomain({ 'sub_domain': 'prebid' }); expect(cid).to.be.undefined; expect(pid).to.be.undefined; expect(subDomain).to.be.undefined; }); it('should return value when param supported', function () { - const cid = extractCID({'cID': '1'}); - const pid = extractPID({'Pid': '2'}); - const subDomain = extractSubDomain({'subDOMAIN': 'prebid'}); + const cid = extractCID({ 'cID': '1' }); + const pid = extractPID({ 'Pid': '2' }); + const subDomain = extractSubDomain({ 'subDOMAIN': 'prebid' }); expect(cid).to.be.equal('1'); expect(pid).to.be.equal('2'); expect(subDomain).to.be.equal('prebid'); @@ -1031,7 +1024,7 @@ describe('VidazooBidAdapter', function () { now }); setStorageItem(storage, 'myKey', 2020); - const {value, created} = getStorageItem(storage, 'myKey'); + const { value, created } = getStorageItem(storage, 'myKey'); expect(created).to.be.equal(now); expect(value).to.be.equal(2020); expect(typeof value).to.be.equal('number'); @@ -1047,8 +1040,8 @@ describe('VidazooBidAdapter', function () { }); it('should parse JSON value', function () { - const data = JSON.stringify({event: 'send'}); - const {event} = tryParseJSON(data); + const data = JSON.stringify({ event: 'send' }); + const { event } = tryParseJSON(data); expect(event).to.be.equal('send'); }); diff --git a/test/spec/modules/videoModule/adQueue_spec.js b/test/spec/modules/videoModule/adQueue_spec.js index 352b2e984a5..d3ab5cfde4c 100644 --- a/test/spec/modules/videoModule/adQueue_spec.js +++ b/test/spec/modules/videoModule/adQueue_spec.js @@ -70,7 +70,7 @@ describe('Ad Queue Coordinator', function () { }; const coordinator = AdQueueCoordinator(mockVideoCore, mockEvents); coordinator.registerProvider(testId); - coordinator.queueAd('testAdTag', testId, {prefetchedVastXml: ''}); + coordinator.queueAd('testAdTag', testId, { prefetchedVastXml: '' }); setupComplete('', { divId: testId }); expect(mockVideoCore.setAdXml.calledOnce).to.be.true; diff --git a/test/spec/modules/videoModule/pbVideo_spec.js b/test/spec/modules/videoModule/pbVideo_spec.js index 5e8aea82d50..18daf1f76d6 100644 --- a/test/spec/modules/videoModule/pbVideo_spec.js +++ b/test/spec/modules/videoModule/pbVideo_spec.js @@ -120,7 +120,7 @@ describe('Prebid Video', function () { pbVideoFactory(videoCore, getConfig); getConfigCallback({ video: { providers } }); const expectedType = 'test_event'; - const expectedPayload = {'test': 'data'}; + const expectedPayload = { 'test': 'data' }; eventHandler(expectedType, expectedPayload); expect(pbEventsMock.emit.calledOnce).to.be.true; expect(pbEventsMock.emit.getCall(0).args[0]).to.be.equal('video' + expectedType.replace(/^./, expectedType[0].toUpperCase())); @@ -252,7 +252,7 @@ describe('Prebid Video', function () { gamSubmoduleMock.getAdTagUrl.resetHistory(); videoCoreMock.setAdTagUrl.resetHistory(); adQueueCoordinatorMock.queueAd.resetHistory(); - auctionResults = { adUnits: [ expectedAdUnit, {} ] }; + auctionResults = { adUnits: [expectedAdUnit, {}] }; }); let beforeBidRequestCallback; @@ -283,7 +283,7 @@ describe('Prebid Video', function () { requestBids, getHighestCpmBids: () => [] }); - auctionResults.adUnits[1].video = {divId: 'other-div'}; + auctionResults.adUnits[1].video = { divId: 'other-div' }; pbVideoFactory(null, getConfig, pbGlobal, requestBids, pbEvents); beforeBidRequestCallback(() => {}, {}); return auctionEndCallback(auctionResults) @@ -327,7 +327,7 @@ describe('Prebid Video', function () { code: expectedAdUnitCode, video: { divId: expectedDivId } }; - const auctionResults = { adUnits: [ expectedAdUnit, {} ] }; + const auctionResults = { adUnits: [expectedAdUnit, {}] }; pbVideoFactory(null, () => ({ providers: [] }), pbGlobal, requestBids, pbEvents); beforeBidRequestCallback(() => {}, {}); diff --git a/test/spec/modules/videoModule/shared/vastXmlBuilder_spec.js b/test/spec/modules/videoModule/shared/vastXmlBuilder_spec.js index edf268a829f..1eb48ab897d 100644 --- a/test/spec/modules/videoModule/shared/vastXmlBuilder_spec.js +++ b/test/spec/modules/videoModule/shared/vastXmlBuilder_spec.js @@ -1,7 +1,9 @@ -import { buildVastWrapper, getVastNode, getAdNode, getWrapperNode, getAdSystemNode, - getAdTagUriNode, getErrorNode, getImpressionNode } from 'libraries/video/shared/vastXmlBuilder.js'; +import { + buildVastWrapper, getVastNode, getAdNode, getWrapperNode, getAdSystemNode, + getAdTagUriNode, getErrorNode, getImpressionNode +} from 'libraries/video/shared/vastXmlBuilder.js'; import { expect } from 'chai'; -import {getGlobal} from '../../../../../src/prebidGlobal.js'; +import { getGlobal } from '../../../../../src/prebidGlobal.js'; describe('buildVastWrapper', function () { it('should include impression and error nodes when requested', function () { diff --git a/test/spec/modules/videoModule/submodules/adplayerproVideoProvider_spec.js b/test/spec/modules/videoModule/submodules/adplayerproVideoProvider_spec.js index b1d4faabef7..81b5a289849 100644 --- a/test/spec/modules/videoModule/submodules/adplayerproVideoProvider_spec.js +++ b/test/spec/modules/videoModule/submodules/adplayerproVideoProvider_spec.js @@ -16,11 +16,11 @@ import { SETUP_FAILED, VOLUME } from 'libraries/video/constants/events.js'; -import adPlayerProSubmoduleFactory, {callbackStorageFactory} from '../../../../../modules/adplayerproVideoProvider.js'; -import {PLACEMENT} from '../../../../../libraries/video/constants/ortb.js'; +import adPlayerProSubmoduleFactory, { callbackStorageFactory } from '../../../../../modules/adplayerproVideoProvider.js'; +import { PLACEMENT } from '../../../../../libraries/video/constants/ortb.js'; import sinon from 'sinon'; -const {AdPlayerProProvider, utils} = require('modules/adplayerproVideoProvider.js'); +const { AdPlayerProProvider, utils } = require('modules/adplayerproVideoProvider.js'); const { PROTOCOLS, API_FRAMEWORKS, VIDEO_MIME_TYPE, PLAYBACK_METHODS, VPAID_MIME_TYPE, PLCMT @@ -93,7 +93,7 @@ describe('AdPlayerProProvider', function () { beforeEach(() => { addDiv(); - config = {divId: 'test', playerConfig: {placementId: 'testId'}}; + config = { divId: 'test', playerConfig: { placementId: 'testId' } }; callbackStorage = callbackStorageFactory(); utilsMock = getUtilsMock(); player = getPlayerMock(); @@ -264,7 +264,7 @@ describe('AdPlayerProProvider', function () { const setupSpy = player.setup = sinon.spy(player.setup); const provider = AdPlayerProProvider(config, makePlayerFactoryMock(player), callbackStorage, utils); provider.init(); - provider.setAdTagUrl('', {adXml: 'https://test.com'}); + provider.setAdTagUrl('', { adXml: 'https://test.com' }); expect(setupSpy.calledOnce).to.be.true; }); @@ -398,15 +398,15 @@ describe('AdPlayerProProvider utils', function () { test(false, PLACEMENT.BANNER); test({}, PLACEMENT.BANNER); - test({type: 'test'}, PLACEMENT.BANNER); - test({type: 'inPage'}, PLACEMENT.ARTICLE); - test({type: 'rewarded'}, PLACEMENT.INTERSTITIAL_SLIDER_FLOATING); - test({type: 'inView'}, PLACEMENT.INTERSTITIAL_SLIDER_FLOATING); + test({ type: 'test' }, PLACEMENT.BANNER); + test({ type: 'inPage' }, PLACEMENT.ARTICLE); + test({ type: 'rewarded' }, PLACEMENT.INTERSTITIAL_SLIDER_FLOATING); + test({ type: 'inView' }, PLACEMENT.INTERSTITIAL_SLIDER_FLOATING); }); it('getPlaybackMethod', function () { function test(autoplay, mute, expected) { - expect(utils.getPlaybackMethod({autoplay, mute})).to.be.equal(expected); + expect(utils.getPlaybackMethod({ autoplay, mute })).to.be.equal(expected); } test(false, false, PLAYBACK_METHODS.CLICK_TO_PLAY); @@ -417,7 +417,7 @@ describe('AdPlayerProProvider utils', function () { it('getPlcmt', function () { function test(type, autoplay, muted, file, expected) { - expect(utils.getPlcmt({type, autoplay, muted, file})).to.be.equal(expected); + expect(utils.getPlcmt({ type, autoplay, muted, file })).to.be.equal(expected); } test('inStream', false, false, 'f', PLCMT.INSTREAM); diff --git a/test/spec/modules/videoModule/submodules/jwplayerVideoProvider_spec.js b/test/spec/modules/videoModule/submodules/jwplayerVideoProvider_spec.js index c414265129d..284e8a358f0 100644 --- a/test/spec/modules/videoModule/submodules/jwplayerVideoProvider_spec.js +++ b/test/spec/modules/videoModule/submodules/jwplayerVideoProvider_spec.js @@ -348,7 +348,7 @@ describe('JWPlayerProvider', function () { const provider = JWPlayerProvider({ divId: 'test' }, makePlayerFactoryMock(player), {}, {}, {}, {}, sharedUtils); provider.init(); const xml = ''; - const options = {foo: 'bar'}; + const options = { foo: 'bar' }; provider.setAdXml(xml, options); expect(loadSpy.calledOnceWith(xml, options)).to.be.true; }); @@ -2345,44 +2345,44 @@ describe('utils', function () { it('should return an empty object when width and aspectratio are not strings', function () { expect(getPlayerSizeFromAspectRatio({ getContainer: () => testContainer }, {})).to.deep.equal({}); - expect(getPlayerSizeFromAspectRatio({ getContainer: () => testContainer }, {width: 100})).to.deep.equal({}); - expect(getPlayerSizeFromAspectRatio({ getContainer: () => testContainer }, {aspectratio: '1:2', width: 100})).to.deep.equal({}); + expect(getPlayerSizeFromAspectRatio({ getContainer: () => testContainer }, { width: 100 })).to.deep.equal({}); + expect(getPlayerSizeFromAspectRatio({ getContainer: () => testContainer }, { aspectratio: '1:2', width: 100 })).to.deep.equal({}); }); it('should return an empty object when aspectratio is malformed', function () { - expect(getPlayerSizeFromAspectRatio({ getContainer: () => testContainer }, {aspectratio: '0.5', width: '100%'})).to.deep.equal({}); - expect(getPlayerSizeFromAspectRatio({ getContainer: () => testContainer }, {aspectratio: '1-2', width: '100%'})).to.deep.equal({}); - expect(getPlayerSizeFromAspectRatio({ getContainer: () => testContainer }, {aspectratio: '1:', width: '100%'})).to.deep.equal({}); - expect(getPlayerSizeFromAspectRatio({ getContainer: () => testContainer }, {aspectratio: ':2', width: '100%'})).to.deep.equal({}); - expect(getPlayerSizeFromAspectRatio({ getContainer: () => testContainer }, {aspectratio: ':', width: '100%'})).to.deep.equal({}); - expect(getPlayerSizeFromAspectRatio({ getContainer: () => testContainer }, {aspectratio: '1:2:3', width: '100%'})).to.deep.equal({}); + expect(getPlayerSizeFromAspectRatio({ getContainer: () => testContainer }, { aspectratio: '0.5', width: '100%' })).to.deep.equal({}); + expect(getPlayerSizeFromAspectRatio({ getContainer: () => testContainer }, { aspectratio: '1-2', width: '100%' })).to.deep.equal({}); + expect(getPlayerSizeFromAspectRatio({ getContainer: () => testContainer }, { aspectratio: '1:', width: '100%' })).to.deep.equal({}); + expect(getPlayerSizeFromAspectRatio({ getContainer: () => testContainer }, { aspectratio: ':2', width: '100%' })).to.deep.equal({}); + expect(getPlayerSizeFromAspectRatio({ getContainer: () => testContainer }, { aspectratio: ':', width: '100%' })).to.deep.equal({}); + expect(getPlayerSizeFromAspectRatio({ getContainer: () => testContainer }, { aspectratio: '1:2:3', width: '100%' })).to.deep.equal({}); }); it('should return an empty object when player container cannot be obtained', function () { - expect(getPlayerSizeFromAspectRatio({}, {aspectratio: '1:2', width: '100%'})).to.deep.equal({}); - expect(getPlayerSizeFromAspectRatio({ getContainer: () => undefined }, {aspectratio: '1:2', width: '100%'})).to.deep.equal({}); + expect(getPlayerSizeFromAspectRatio({}, { aspectratio: '1:2', width: '100%' })).to.deep.equal({}); + expect(getPlayerSizeFromAspectRatio({ getContainer: () => undefined }, { aspectratio: '1:2', width: '100%' })).to.deep.equal({}); }); it('should calculate the size given the width percentage and aspect ratio', function () { - expect(getPlayerSizeFromAspectRatio({ getContainer: () => testContainer }, {aspectratio: '2:1', width: '100%'})).to.deep.equal({ height: 320, width: 640 }); - expect(getPlayerSizeFromAspectRatio({ getContainer: () => testContainer }, {aspectratio: '4:1', width: '70%'})).to.deep.equal({ height: 112, width: 448 }); + expect(getPlayerSizeFromAspectRatio({ getContainer: () => testContainer }, { aspectratio: '2:1', width: '100%' })).to.deep.equal({ height: 320, width: 640 }); + expect(getPlayerSizeFromAspectRatio({ getContainer: () => testContainer }, { aspectratio: '4:1', width: '70%' })).to.deep.equal({ height: 112, width: 448 }); }); it('should return the container height when smaller than the calculated height', function () { - expect(getPlayerSizeFromAspectRatio({ getContainer: () => testContainer }, {aspectratio: '1:1', width: '100%'})).to.deep.equal({ height: 480, width: 640 }); + expect(getPlayerSizeFromAspectRatio({ getContainer: () => testContainer }, { aspectratio: '1:1', width: '100%' })).to.deep.equal({ height: 480, width: 640 }); }); it('should handle non-numeric aspect ratio values', function () { - expect(getPlayerSizeFromAspectRatio({ getContainer: () => testContainer }, {aspectratio: 'abc:def', width: '100%'})).to.deep.equal({}); + expect(getPlayerSizeFromAspectRatio({ getContainer: () => testContainer }, { aspectratio: 'abc:def', width: '100%' })).to.deep.equal({}); }); it('should handle non-numeric width percentage', function () { - expect(getPlayerSizeFromAspectRatio({ getContainer: () => testContainer }, {aspectratio: '16:9', width: 'abc%'})).to.deep.equal({}); + expect(getPlayerSizeFromAspectRatio({ getContainer: () => testContainer }, { aspectratio: '16:9', width: 'abc%' })).to.deep.equal({}); }); it('should handle zero aspect ratio values', function () { - expect(getPlayerSizeFromAspectRatio({ getContainer: () => testContainer }, {aspectratio: '0:9', width: '100%'})).to.deep.equal({}); - expect(getPlayerSizeFromAspectRatio({ getContainer: () => testContainer }, {aspectratio: '16:0', width: '100%'})).to.deep.equal({}); + expect(getPlayerSizeFromAspectRatio({ getContainer: () => testContainer }, { aspectratio: '0:9', width: '100%' })).to.deep.equal({}); + expect(getPlayerSizeFromAspectRatio({ getContainer: () => testContainer }, { aspectratio: '16:0', width: '100%' })).to.deep.equal({}); }); }); @@ -2493,7 +2493,7 @@ describe('utils', function () { it('should be FLOATING when player is floating', function () { const player = getPlayerMock(); player.getFloating = () => true; - const placement = getPlacement({outstream: true}, player); + const placement = getPlacement({ outstream: true }, player); expect(placement).to.be.equal(PLACEMENT.FLOATING); }); @@ -2501,19 +2501,19 @@ describe('utils', function () { const player = getPlayerMock(); player.getFloating = () => false; - let placement = getPlacement({placement: 'banner', outstream: true}, player); + let placement = getPlacement({ placement: 'banner', outstream: true }, player); expect(placement).to.be.equal(PLACEMENT.BANNER); - placement = getPlacement({placement: 'article', outstream: true}, player); + placement = getPlacement({ placement: 'article', outstream: true }, player); expect(placement).to.be.equal(PLACEMENT.ARTICLE); - placement = getPlacement({placement: 'feed', outstream: true}, player); + placement = getPlacement({ placement: 'feed', outstream: true }, player); expect(placement).to.be.equal(PLACEMENT.FEED); - placement = getPlacement({placement: 'interstitial', outstream: true}, player); + placement = getPlacement({ placement: 'interstitial', outstream: true }, player); expect(placement).to.be.equal(PLACEMENT.INTERSTITIAL); - placement = getPlacement({placement: 'slider', outstream: true}, player); + placement = getPlacement({ placement: 'slider', outstream: true }, player); expect(placement).to.be.equal(PLACEMENT.SLIDER); }); @@ -2526,10 +2526,10 @@ describe('utils', function () { const player = getPlayerMock(); player.getFloating = () => false; - let placement = getPlacement({placement: 'BANNER', outstream: true}, player); + let placement = getPlacement({ placement: 'BANNER', outstream: true }, player); expect(placement).to.be.equal(PLACEMENT.BANNER); - placement = getPlacement({placement: 'Article', outstream: true}, player); + placement = getPlacement({ placement: 'Article', outstream: true }, player); expect(placement).to.be.equal(PLACEMENT.ARTICLE); }); @@ -2537,7 +2537,7 @@ describe('utils', function () { const player = getPlayerMock(); player.getFloating = () => false; - const placement = getPlacement({placement: 'unknown', outstream: true}, player); + const placement = getPlacement({ placement: 'unknown', outstream: true }, player); expect(placement).to.be.undefined; }); }); @@ -2648,7 +2648,7 @@ describe('utils', function () { }); describe('getIsoLanguageCode', function () { - const sampleAudioTracks = [{language: 'ht'}, {language: 'fr'}, {language: 'es'}, {language: 'pt'}]; + const sampleAudioTracks = [{ language: 'ht' }, { language: 'fr' }, { language: 'es' }, { language: 'pt' }]; it('should return undefined when audio tracks are unavailable', function () { const player = getPlayerMock(); @@ -2708,7 +2708,7 @@ describe('utils', function () { it('should handle audio tracks with missing language property', function () { const player = getPlayerMock(); - player.getAudioTracks = () => [{}, {language: 'en'}, {}]; + player.getAudioTracks = () => [{}, { language: 'en' }, {}]; player.getCurrentAudioTrack = () => 0; const languageCode = utils.getIsoLanguageCode(player); expect(languageCode).to.be.undefined; @@ -2716,7 +2716,7 @@ describe('utils', function () { it('should handle audio tracks with null language property', function () { const player = getPlayerMock(); - player.getAudioTracks = () => [{language: null}, {language: 'en'}, {}]; + player.getAudioTracks = () => [{ language: null }, { language: 'en' }, {}]; player.getCurrentAudioTrack = () => 0; const languageCode = utils.getIsoLanguageCode(player); expect(languageCode).to.be.null; @@ -2758,24 +2758,24 @@ describe('utils', function () { it('should convert segments to objects', function () { const segs = ['a', 'b']; expect(getSegments(segs)).to.deep.equal([ - {id: 'a'}, - {id: 'b'} + { id: 'a' }, + { id: 'b' } ]); }); it('should handle single segment', function () { const segs = ['single']; expect(getSegments(segs)).to.deep.equal([ - {id: 'single'} + { id: 'single' } ]); }); it('should handle segments with special characters', function () { const segs = ['segment-1', 'segment_2', 'segment 3']; expect(getSegments(segs)).to.deep.equal([ - {id: 'segment-1'}, - {id: 'segment_2'}, - {id: 'segment 3'} + { id: 'segment-1' }, + { id: 'segment_2' }, + { id: 'segment 3' } ]); }); @@ -2795,7 +2795,7 @@ describe('utils', function () { }); it('should set media id and segments', function () { - const segments = [{id: 'x'}]; + const segments = [{ id: 'x' }]; expect(getContentDatum('id1', segments)).to.deep.equal({ name: 'jwplayer.com', segment: segments, @@ -2813,7 +2813,7 @@ describe('utils', function () { }); it('should set only segments when media id missing', function () { - const segments = [{id: 'y'}]; + const segments = [{ id: 'y' }]; expect(getContentDatum(null, segments)).to.deep.equal({ name: 'jwplayer.com', segment: segments, diff --git a/test/spec/modules/videoModule/submodules/videojsVideoProvider_spec.js b/test/spec/modules/videoModule/submodules/videojsVideoProvider_spec.js index 6646cfb611f..d8f3e105885 100644 --- a/test/spec/modules/videoModule/submodules/videojsVideoProvider_spec.js +++ b/test/spec/modules/videoModule/submodules/videojsVideoProvider_spec.js @@ -4,7 +4,7 @@ import { } from 'libraries/video/constants/events.js'; import { getWinDimensions } from '../../../../../src/utils.js'; -const {VideojsProvider, utils, adStateFactory, timeStateFactory} = require('modules/videojsVideoProvider'); +const { VideojsProvider, utils, adStateFactory, timeStateFactory } = require('modules/videojsVideoProvider'); const { PROTOCOLS, API_FRAMEWORKS, VIDEO_MIME_TYPE, PLAYBACK_METHODS, PLCMT, VPAID_MIME_TYPE, AD_POSITION @@ -42,7 +42,7 @@ describe('videojsProvider', function () { }); it('should trigger failure when videojs version is under min supported version', function () { - const provider = VideojsProvider(config, {...videojs, VERSION: '0.0.0'}, adState, timeState, callbackStorage, utils); + const provider = VideojsProvider(config, { ...videojs, VERSION: '0.0.0' }, adState, timeState, callbackStorage, utils); const setupFailed = sinon.spy(); provider.onEvent(SETUP_FAILED, setupFailed, {}); provider.init(); @@ -63,7 +63,7 @@ describe('videojsProvider', function () { }); it('should instantiate the player when uninstantied', function () { - config.playerConfig = {testAttr: true}; + config.playerConfig = { testAttr: true }; config.divId = 'test-div' const div = document.createElement('div'); div.setAttribute('id', 'test-div'); @@ -116,7 +116,7 @@ describe('videojsProvider', function () { describe('getOrtbParams', function () { beforeEach(() => { - config = {divId: 'test'}; + config = { divId: 'test' }; // initialize videojs element document.body.innerHTML = `