From a34e3e4983418d74e57055a12dc61e554c995089 Mon Sep 17 00:00:00 2001 From: pavel Date: Tue, 30 Jan 2024 15:52:37 +0200 Subject: [PATCH] Revert "Merge branch 'prebid:master' into setupad-adapter" This reverts commit 1c14dbe88883e4c25bd303e2094c72f64e361877, reversing changes made to 7fe9ea569e144c37beeab83bda98564b543c7b09. --- .devcontainer/devcontainer.json | 4 +- .eslintrc.js | 35 +- .github/workflows/codeql-analysis.yml | 8 +- .github/workflows/issue_tracker.yml | 2 +- PR_REVIEW.md | 1 + features.json | 3 +- gulpfile.js | 27 +- integrationExamples/gpt/adUnitFloors.html | 1 + .../gpt/adnuntius_example.html | 95 - .../gpt/contxtfulRtdProvider_example.html | 91 - integrationExamples/gpt/growthcode.html | 15 +- integrationExamples/gpt/hello_world.html | 30 +- integrationExamples/gpt/inskin_example.html | 100 + .../gpt/pbjs_video_adUnit.html | 109 + .../gpt/prebidServer_example.html | 40 +- .../gpt/prebidServer_fledge_example.html | 2 +- .../gpt/prebidServer_native_example.html | 2 +- integrationExamples/gpt/tpmn_example.html | 168 -- .../gpt/tpmn_serverless_example.html | 121 - .../gpt/x-domain/creative.html | 108 +- .../noadserver/native_noadserver.html | 173 -- integrationExamples/topics/topics-server.js | 72 - .../videoModule/jwplayer/bidMarkedAsUsed.html | 4 +- .../jwplayer/bidRequestScheduling.html | 2 +- .../jwplayer/bidsBackHandlerOverride.html | 137 - .../videoModule/jwplayer/eventListeners.html | 6 +- .../videoModule/jwplayer/eventsUI.html | 4 +- .../jwplayer/gamAdServerMediation.html | 4 +- .../videoModule/jwplayer/mediaMetadata.html | 4 +- .../videoModule/jwplayer/playlist.html | 4 +- .../videoModule/videojs/bidMarkedAsUsed.html | 2 +- .../videojs/bidRequestScheduling.html | 2 +- .../videojs/bidsBackHandlerOverride.html | 164 -- .../videoModule/videojs/eventListeners.html | 2 +- .../videoModule/videojs/eventsUI.html | 2 +- .../videojs/gamAdServerMediation.html | 2 +- .../videoModule/videojs/mediaMetadata.html | 2 +- .../videoModule/videojs/playlist.html | 2 +- .../anKeywords.js | 15 +- libraries/appnexusUtils/anUtils.js | 25 - libraries/chunk/chunk.js | 19 - libraries/cmp/cmpClient.js | 169 -- libraries/creativeRender/constants.js | 10 - libraries/creativeRender/crossDomain.js | 57 - libraries/creativeRender/direct.js | 62 - libraries/creativeRender/writer.js | 34 - libraries/currencyUtils/currency.js | 31 - libraries/gptUtils/gptUtils.js | 37 - libraries/htmlEscape/htmlEscape.js | 26 - libraries/mspa/activityControls.js | 127 - libraries/ortbConverter/README.md | 9 +- libraries/sizeUtils/sizeUtils.js | 29 - .../transformParamsUtils/convertTypes.js | 36 - libraries/uid2Eids/uid2Eids.js | 14 - libraries/urlUtils/urlUtils.js | 7 - modules/.submodules.json | 24 +- modules/33acrossAnalyticsAdapter.js | 656 ----- modules/33acrossAnalyticsAdapter.md | 76 - modules/33acrossBidAdapter.js | 40 +- modules/33acrossIdSystem.js | 34 +- modules/a1MediaBidAdapter.js | 104 - modules/a1MediaBidAdapter.md | 93 - modules/a1MediaRtdProvider.js | 96 - modules/a1MediaRtdProvider.md | 46 - modules/ablidaBidAdapter.js | 6 - ...dsBidAdapter.js => acuityAdsBidAdapter.js} | 9 - ...dsBidAdapter.md => acuityAdsBidAdapter.md} | 2 +- modules/ad2ictionBidAdapter.js | 59 - modules/ad2ictionBidAdapter.md | 30 - modules/adWMGBidAdapter.js | 2 +- modules/adagioAnalyticsAdapter.js | 404 +-- modules/adagioAnalyticsAdapter.md | 10 +- modules/adagioBidAdapter.js | 104 +- modules/adagioBidAdapter.md | 7 +- modules/adfBidAdapter.js | 5 +- modules/adfusionBidAdapter.js | 120 - modules/adfusionBidAdapter.md | 61 - modules/adgenerationBidAdapter.js | 21 +- modules/adkernelAdnBidAdapter.js | 2 +- modules/adkernelBidAdapter.js | 167 +- modules/adlooxAnalyticsAdapter.js | 2 +- modules/adlooxRtdProvider.js | 2 +- modules/admanBidAdapter.js | 15 +- modules/admaticBidAdapter.js | 234 +- modules/admediaBidAdapter.js | 6 - modules/admixerBidAdapter.js | 11 +- modules/admixerBidAdapter.md | 45 - modules/admixerIdSystem.js | 13 - modules/adnowBidAdapter.js | 4 - modules/adnuntiusBidAdapter.js | 260 +- modules/adnuntiusRtdProvider.js | 4 - modules/adotBidAdapter.js | 4 +- modules/adpod.js | 24 +- modules/adqueryBidAdapter.js | 59 +- modules/adqueryIdSystem.js | 81 +- modules/adrelevantisBidAdapter.js | 15 +- modules/adriverBidAdapter.js | 2 +- modules/adriverIdSystem.js | 7 - modules/adsinteractiveBidAdapter.js | 6 +- modules/adstirBidAdapter.js | 91 - modules/adstirBidAdapter.md | 36 - modules/adtargetBidAdapter.js | 3 +- modules/adtelligentBidAdapter.js | 32 +- modules/adtelligentIdSystem.js | 13 - modules/aduptechBidAdapter.js | 8 +- modules/adxcgBidAdapter.js | 413 ++- modules/adyoulikeBidAdapter.js | 59 +- modules/agmaAnalyticsAdapter.js | 225 -- modules/agmaAnalyticsAdapter.md | 28 - modules/aidemBidAdapter.js | 371 ++- modules/airgridRtdProvider.js | 4 - modules/ajaBidAdapter.js | 163 +- modules/ajaBidAdapter.md | 60 +- modules/alkimiBidAdapter.js | 54 +- modules/ampliffyBidAdapter.js | 419 --- modules/ampliffyBidAdapter.md | 39 - modules/amxBidAdapter.js | 133 +- modules/amxIdSystem.js | 6 - modules/apacdexBidAdapter.js | 4 +- modules/appierBidAdapter.js | 5 - modules/appnexusBidAdapter.js | 60 +- modules/arcspanRtdProvider.js | 4 - modules/asoBidAdapter.js | 4 +- modules/asteriobidAnalyticsAdapter.js | 336 --- modules/asteriobidAnalyticsAdapter.md | 41 - modules/astraoneBidAdapter.js | 6 - modules/audiencerunBidAdapter.js | 12 +- modules/automatadAnalyticsAdapter.js | 325 --- modules/automatadAnalyticsAdapter.md | 23 - modules/axisBidAdapter.js | 210 -- modules/axisBidAdapter.md | 83 - modules/beachfrontBidAdapter.js | 38 +- modules/beopBidAdapter.js | 41 +- modules/beopBidAdapter.md | 36 +- modules/betweenBidAdapter.js | 11 +- modules/bidViewability.js | 7 - modules/bidViewability.md | 17 +- modules/biddoBidAdapter.js | 5 - modules/bidglassBidAdapter.js | 9 +- modules/big-richmediaBidAdapter.js | 5 - modules/bizzclickBidAdapter.js | 355 ++- modules/bizzclickBidAdapter.md | 183 +- modules/bliinkBidAdapter.js | 111 +- modules/blueconicRtdProvider.js | 10 +- modules/boldwinBidAdapter.js | 11 +- modules/brandmetricsRtdProvider.js | 107 +- modules/brandmetricsRtdProvider.md | 19 +- modules/braveBidAdapter.js | 5 - modules/bridBidAdapter.js | 229 -- modules/bridgewellBidAdapter.js | 5 - modules/brightcomBidAdapter.js | 15 +- modules/brightcomSSPBidAdapter.js | 3 +- modules/britepoolIdSystem.js | 13 - modules/browsiBidAdapter.js | 5 - modules/browsiRtdProvider.js | 9 +- modules/bucksenseBidAdapter.js | 18 +- modules/buzzoolaBidAdapter.js | 6 - modules/c1xBidAdapter.js | 4 - modules/cadentApertureMXBidAdapter.js | 34 +- modules/cadentApertureMXBidAdapter.md | 4 +- modules/categoryTranslation.js | 5 +- modules/cleanioRtdProvider.js | 4 - modules/clickforceBidAdapter.js | 6 - modules/codefuelBidAdapter.js | 50 +- modules/cointrafficBidAdapter.js | 7 - modules/coinzillaBidAdapter.js | 8 +- modules/colossussspBidAdapter.js | 5 - modules/conceptxBidAdapter.js | 24 +- modules/concertBidAdapter.js | 17 - modules/connatixBidAdapter.js | 184 -- modules/connatixBidAdapter.md | 54 - modules/connectIdSystem.js | 49 +- modules/connectadBidAdapter.js | 4 +- modules/consentManagement.js | 98 +- modules/consentManagementGpp.js | 467 ++-- modules/consentManagementUsp.js | 116 +- modules/consumableBidAdapter.js | 27 +- modules/contxtfulRtdProvider.js | 150 -- modules/contxtfulRtdProvider.md | 65 - modules/conversantBidAdapter.js | 404 ++- modules/cpmstarBidAdapter.js | 12 +- modules/craftBidAdapter.js | 6 +- modules/criteoBidAdapter.js | 218 +- modules/criteoIdSystem.js | 65 +- modules/currency.js | 112 +- modules/cwireBidAdapter.js | 25 - modules/czechAdIdSystem.js | 11 - modules/dacIdSystem.js | 6 - modules/datablocksBidAdapter.js | 3 +- modules/datawrkzBidAdapter.js | 15 +- modules/dchain.js | 4 +- modules/deepintentBidAdapter.js | 2 - modules/deepintentDpesIdSystem.js | 15 +- modules/deltaprojectsBidAdapter.js | 16 +- modules/dfpAdServerVideo.js | 109 +- modules/dgkeywordRtdProvider.js | 52 +- modules/discoveryBidAdapter.js | 237 +- modules/displayioBidAdapter.js | 4 +- modules/dmdIdSystem.js | 13 - modules/docereeAdManagerBidAdapter.js | 128 - modules/docereeAdManagerBidAdapter.md | 68 - modules/docereeBidAdapter.js | 31 +- modules/dsp_genieeBidAdapter.js | 123 - modules/dsp_genieeBidAdapter.md | 39 - modules/dspxBidAdapter.js | 4 - modules/dxkultureBidAdapter.js | 340 --- modules/dynamicAdBoostRtdProvider.js | 114 - modules/dynamicAdBoostRtdProvider.md | 40 - modules/ebdrBidAdapter.js | 2 +- modules/edge226BidAdapter.js | 188 -- modules/edge226BidAdapter.md | 79 - modules/eplanningBidAdapter.js | 22 +- modules/eskimiBidAdapter.js | 13 +- modules/euidIdSystem.js | 26 +- modules/euidIdSystem.md | 52 +- modules/experianRtdProvider.js | 141 - modules/experianRtdProvider.md | 52 - modules/fabrickIdSystem.js | 13 - modules/feedadBidAdapter.js | 9 - modules/fledgeForGpt.js | 103 +- modules/fledgeForGpt.md | 27 +- modules/flippBidAdapter.js | 194 -- modules/flippBidAdapter.md | 45 - modules/fluctBidAdapter.js | 28 +- modules/freepassBidAdapter.js | 117 - modules/freepassBidAdapter.md | 34 - modules/freepassIdSystem.js | 28 +- modules/freewheel-sspBidAdapter.js | 55 +- modules/ftrackIdSystem.js | 23 - modules/gammaBidAdapter.js | 5 - modules/gdprEnforcement.js | 255 +- modules/genericAnalyticsAdapter.js | 12 +- modules/geoedgeRtdProvider.js | 116 +- modules/geoedgeRtdProvider.md | 3 +- modules/geolocationRtdProvider.js | 65 - modules/getintentBidAdapter.js | 15 +- modules/gjirafaBidAdapter.js | 17 +- modules/gmosspBidAdapter.js | 16 +- modules/gnetBidAdapter.js | 6 - modules/goldbachBidAdapter.js | 24 +- modules/goldfishAdsRtdProvider.js | 198 -- modules/goldfishAdsRtdProvider.md | 48 - modules/gothamadsBidAdapter.js | 5 - modules/gppControl_usnat.js | 11 - modules/gppControl_usstates.js | 176 -- modules/gptPreAuction.js | 28 +- modules/gravitoIdSystem.js | 30 +- modules/greenbidsAnalyticsAdapter.js | 111 +- modules/greenbidsAnalyticsAdapter.md | 37 +- modules/greenbidsRtdProvider.js | 74 +- modules/greenbidsRtdProvider.md | 2 +- modules/gridBidAdapter.js | 43 +- modules/growadvertisingBidAdapter.js | 2 +- modules/growthCodeAnalyticsAdapter.js | 20 +- modules/growthCodeAnalyticsAdapter.md | 8 +- modules/growthCodeIdSystem.js | 28 +- modules/growthCodeRtdProvider.js | 13 +- modules/gumgumBidAdapter.js | 54 +- modules/hadronIdSystem.js | 46 +- modules/hadronIdSystem.md | 3 +- modules/hadronRtdProvider.js | 4 - modules/holidBidAdapter.js | 78 +- modules/hybridBidAdapter.js | 7 - modules/iasRtdProvider.js | 16 +- modules/id5IdSystem.js | 303 +-- modules/id5IdSystem.md | 13 +- modules/idWardRtdProvider.js | 31 +- modules/identityLinkIdSystem.js | 39 +- modules/idxIdSystem.js | 11 - modules/illuminBidAdapter.js | 336 --- modules/illuminBidAdapter.md | 35 - modules/imRtdProvider.js | 8 +- modules/imdsBidAdapter.js | 54 +- modules/imdsBidAdapter.md | 8 +- modules/impactifyBidAdapter.js | 224 +- modules/impactifyBidAdapter.md | 38 +- modules/improvedigitalBidAdapter.js | 12 - modules/imuIdSystem.js | 15 - modules/incrxBidAdapter.js | 38 +- modules/insticatorBidAdapter.js | 154 +- modules/instreamTracking.js | 6 - modules/integr8BidAdapter.js | 33 +- modules/integr8BidAdapter.md | 12 +- modules/intentIqIdSystem.js | 23 +- modules/invamiaBidAdapter.js | 5 - modules/invibesBidAdapter.js | 69 +- modules/ipromBidAdapter.js | 4 +- modules/iqmBidAdapter.js | 4 - modules/iqxBidAdapter.js | 207 -- modules/iqxBidAdapter.md | 54 - modules/ivsBidAdapter.js | 9 +- modules/ixBidAdapter.js | 662 +---- modules/ixBidAdapter.md | 5 - modules/jixieBidAdapter.js | 151 +- modules/justIdSystem.js | 13 - modules/jwplayerRtdProvider.js | 21 +- modules/kargoBidAdapter.js | 43 +- modules/kinessoIdSystem.js | 15 +- modules/kueezBidAdapter.js | 14 +- modules/kueezRtbBidAdapter.js | 11 +- modules/kulturemediaBidAdapter.js | 472 ++++ ...idAdapter.md => kulturemediaBidAdapter.md} | 20 +- modules/lemmaDigitalBidAdapter.js | 85 +- modules/lifestreetBidAdapter.js | 4 - modules/limelightDigitalBidAdapter.js | 6 - modules/liveIntentIdSystem.js | 153 +- modules/livewrappedAnalyticsAdapter.js | 66 +- modules/livewrappedBidAdapter.js | 5 - modules/lm_kiviadsBidAdapter.js | 215 -- modules/lm_kiviadsBidAdapter.md | 54 - modules/lockerdomeBidAdapter.js | 2 +- modules/lotamePanoramaIdSystem.js | 13 - modules/madvertiseBidAdapter.js | 5 - modules/magniteAnalyticsAdapter.js | 4 - modules/malltvBidAdapter.js | 17 +- modules/mediabramaBidAdapter.js | 155 -- modules/mediabramaBidAdapter.md | 33 - modules/mediafilterRtdProvider.js | 94 - modules/mediafilterRtdProvider.md | 37 - modules/mediaforceBidAdapter.js | 6 - modules/mediafuseBidAdapter.js | 20 +- modules/mediagoBidAdapter.js | 274 +- modules/mediakeysBidAdapter.js | 2 +- modules/medianetBidAdapter.js | 19 +- modules/mediasniperBidAdapter.js | 5 +- modules/mediasquareBidAdapter.js | 102 +- modules/merkleIdSystem.js | 59 +- modules/mgidBidAdapter.js | 9 +- modules/mgidRtdProvider.js | 4 - modules/mgidXBidAdapter.js | 7 +- modules/minutemediaBidAdapter.js | 49 +- modules/minutemediaBidAdapter.md | 1 - modules/minutemediaplusBidAdapter.js | 11 +- modules/missenaBidAdapter.js | 69 +- modules/mobfoxpbBidAdapter.js | 9 - modules/multibid/index.js | 58 +- modules/mwOpenLinkIdSystem.js | 37 +- modules/mygaruIdSystem.js | 104 - modules/mygaruIdSystem.md | 24 - modules/nativoBidAdapter.js | 24 +- modules/naveggIdSystem.js | 37 +- modules/netIdSystem.js | 13 - modules/neuwoRtdProvider.js | 20 +- modules/neuwoRtdProvider.md | 2 - modules/newspassid.md | 76 - modules/newspassidBidAdapter.js | 62 +- modules/nextMillenniumBidAdapter.js | 321 +-- modules/nextrollBidAdapter.js | 9 +- modules/nexx360BidAdapter.js | 21 +- modules/nobidAnalyticsAdapter.js | 242 -- modules/nobidAnalyticsAdapter.md | 38 - modules/nobidBidAdapter.js | 91 +- modules/nobidBidAdapter.md | 1 - modules/novatiqIdSystem.js | 41 +- modules/oguryBidAdapter.js | 55 +- modules/oneKeyIdSystem.js | 63 +- modules/oneKeyRtdProvider.js | 4 - modules/onetagBidAdapter.js | 29 +- modules/onomagicBidAdapter.js | 3 +- modules/open8BidAdapter.js | 3 +- modules/openwebBidAdapter.js | 3 +- modules/openxBidAdapter.js | 7 +- modules/operaadsBidAdapter.js | 54 +- modules/operaadsBidAdapter.md | 66 +- modules/operaadsIdSystem.js | 111 - modules/operaadsIdSystem.md | 52 - modules/optidigitalBidAdapter.js | 111 +- modules/optidigitalBidAdapter.md | 13 +- modules/optimeraRtdProvider.js | 33 +- modules/optimeraRtdProvider.md | 6 +- modules/optimonAnalyticsAdapter.js | 16 +- modules/orbidderBidAdapter.js | 22 +- modules/orbitsoftBidAdapter.js | 13 +- modules/otmBidAdapter.js | 3 +- modules/outbrainBidAdapter.js | 22 +- modules/oxxionAnalyticsAdapter.js | 79 +- modules/oxxionRtdProvider.js | 196 +- modules/oxxionRtdProvider.md | 20 +- modules/ozoneBidAdapter.js | 89 +- modules/ozoneBidAdapter.md | 4 +- modules/pairIdSystem.js | 40 +- modules/pangleBidAdapter.js | 178 -- modules/pangleBidAdapter.md | 32 - modules/parrableIdSystem.js | 46 +- modules/permutiveRtdProvider.js | 4 - modules/pgamsspBidAdapter.js | 229 -- modules/pgamsspBidAdapter.md | 79 - modules/pilotxBidAdapter.js | 38 +- modules/pixfutureBidAdapter.js | 5 +- modules/prebidServerBidAdapter/config.js | 24 +- modules/prebidServerBidAdapter/index.js | 34 +- .../prebidServerBidAdapter/ortbConverter.js | 27 +- modules/precisoBidAdapter.js | 212 -- modules/precisoBidAdapter.md | 84 - modules/priceFloors.js | 93 +- modules/prismaBidAdapter.js | 59 +- modules/programmaticaBidAdapter.js | 153 -- modules/programmaticaBidAdapter.md | 46 - modules/pubCircleBidAdapter.js | 231 -- modules/pubCircleBidAdapter.md | 79 - modules/pubProvidedIdSystem.js | 9 +- modules/publinkIdSystem.js | 57 +- modules/pubmaticAnalyticsAdapter.js | 120 +- modules/pubmaticBidAdapter.js | 98 +- modules/pubwiseBidAdapter.js | 7 - modules/pubxBidAdapter.js | 4 +- modules/pubxaiAnalyticsAdapter.js | 3 +- modules/pulsepointBidAdapter.js | 488 +++- modules/pulsepointBidAdapter.md | 84 +- modules/pxyzBidAdapter.js | 5 - modules/qortexRtdProvider.js | 165 -- modules/qortexRtdProvider.md | 69 - modules/quantcastBidAdapter.js | 5 - modules/quantcastIdSystem.js | 10 - modules/r2b2BidAdapter.js | 309 --- modules/r2b2BidAdapter.md | 37 - modules/radsBidAdapter.js | 4 - modules/rasBidAdapter.js | 55 +- modules/reconciliationRtdProvider.js | 4 - modules/relaidoBidAdapter.js | 17 +- modules/relayBidAdapter.js | 99 - modules/relayBidAdapter.md | 79 - modules/relevadRtdProvider.js | 13 +- modules/relevantdigitalBidAdapter.js | 29 +- modules/resetdigitalBidAdapter.js | 122 +- modules/retailspotBidAdapter.js | 7 +- modules/revcontentBidAdapter.js | 3 +- modules/richaudienceBidAdapter.js | 21 +- modules/riseBidAdapter.js | 40 +- modules/riseBidAdapter.md | 1 - modules/rtbhouseBidAdapter.js | 17 +- modules/rtbhouseBidAdapter.md | 35 +- modules/rtbsapeBidAdapter.js | 8 - modules/rtdModule/index.js | 5 +- modules/rubiconBidAdapter.js | 116 +- modules/schain.js | 27 +- modules/seedtagBidAdapter.js | 32 +- modules/sharedIdSystem.js | 16 +- modules/sharethroughAnalyticsAdapter.js | 2 +- modules/sharethroughBidAdapter.js | 160 +- modules/shinezBidAdapter.js | 14 +- modules/shinezRtbBidAdapter.js | 336 --- modules/shinezRtbBidAdapter.md | 35 - modules/showheroes-bsBidAdapter.js | 6 +- modules/silverpushBidAdapter.js | 4 +- modules/sizeMappingV2.js | 2 +- modules/slimcutBidAdapter.js | 39 +- modules/smaatoBidAdapter.js | 30 +- modules/smartadserverBidAdapter.js | 53 +- modules/smartxBidAdapter.js | 21 +- modules/smartxBidAdapter.md | 4 +- modules/smartyadsBidAdapter.js | 70 +- modules/smartyadsBidAdapter.md | 18 +- modules/smilewantedBidAdapter.js | 128 +- modules/snigelBidAdapter.js | 87 +- modules/snigelBidAdapter.md | 12 +- modules/sonobiAnalyticsAdapter.js | 2 +- modules/sonobiBidAdapter.js | 57 +- modules/sovrnAnalyticsAdapter.md | 2 +- modules/sovrnBidAdapter.js | 26 +- modules/sovrnBidAdapter.md | 2 +- modules/sparteoBidAdapter.js | 170 -- modules/sparteoBidAdapter.md | 35 - modules/spotxBidAdapter.js | 24 +- modules/ssmasBidAdapter.js | 133 - modules/ssmasBidAdapter.md | 35 - modules/sspBCBidAdapter.js | 72 +- modules/stroeerCoreBidAdapter.js | 5 +- modules/stvBidAdapter.js | 63 +- modules/sublimeBidAdapter.js | 6 - modules/taboolaBidAdapter.js | 270 +- modules/tagorasBidAdapter.js | 342 --- modules/tagorasBidAdapter.md | 35 - modules/tapadIdSystem.js | 6 - modules/tappxBidAdapter.js | 66 +- modules/tappxBidAdapter.md | 17 - modules/targetVideoBidAdapter.js | 5 - modules/teadsBidAdapter.js | 44 +- modules/teadsIdSystem.js | 35 +- modules/temedyaBidAdapter.js | 38 +- modules/theAdxBidAdapter.js | 21 +- modules/theAdxBidAdapter.md | 19 +- modules/timeoutRtdProvider.js | 6 +- modules/tncIdSystem.js | 6 - modules/topicsFpdModule.js | 130 +- modules/topicsFpdModule.md | 4 - modules/tpmnBidAdapter.js | 349 +-- modules/tpmnBidAdapter.md | 86 +- modules/trafficgateBidAdapter.js | 199 +- modules/trionBidAdapter.js | 3 +- modules/tripleliftBidAdapter.js | 60 +- modules/ttdBidAdapter.js | 13 +- modules/ucfunnelBidAdapter.js | 61 +- modules/uid2IdSystem.js | 22 +- modules/uid2IdSystem.md | 131 +- modules/uid2IdSystem_shared.js | 611 +---- modules/underdogmediaBidAdapter.js | 4 +- modules/undertoneBidAdapter.js | 1 - modules/undertoneBidAdapter.md | 2 +- modules/unicornBidAdapter.js | 28 - modules/unifiedIdSystem.js | 17 - modules/unrulyBidAdapter.js | 64 +- modules/userId/eids.js | 459 +++- modules/userId/eids.md | 80 +- modules/userId/index.js | 270 +- modules/userId/userId.md | 6 - modules/utiqSystem.js | 9 - modules/vdoaiBidAdapter.js | 8 +- modules/ventesBidAdapter.js | 3 +- modules/verizonMediaIdSystem.js | 13 - modules/viantOrtbBidAdapter.js | 113 - modules/viantOrtbBidAdapter.md | 54 - modules/vibrantmediaBidAdapter.js | 7 - modules/vidazooBidAdapter.js | 100 +- modules/videobyteBidAdapter.js | 8 - modules/videoheroesBidAdapter.js | 5 - modules/videojsVideoProvider.js | 11 +- modules/videonowBidAdapter.js | 6 - modules/videoreachBidAdapter.js | 2 +- modules/viqeoBidAdapter.js | 8 - modules/visxBidAdapter.js | 87 +- modules/voxBidAdapter.js | 30 +- modules/vrtcalBidAdapter.js | 45 +- modules/weboramaRtdProvider.js | 85 +- modules/welectBidAdapter.js | 7 - modules/winrBidAdapter.js | 11 +- modules/xeBidAdapter.js | 33 +- modules/yahoosspBidAdapter.js | 73 +- modules/yahoosspBidAdapter.md | 203 +- modules/yandexBidAdapter.js | 166 +- modules/yieldlabBidAdapter.js | 9 - modules/yieldloveBidAdapter.js | 149 -- modules/yieldloveBidAdapter.md | 38 - modules/yieldmoBidAdapter.js | 34 +- modules/zeotapIdPlusIdSystem.js | 11 - modules/zetaBidAdapter.js | 42 +- modules/zeta_global_sspAnalyticsAdapter.js | 77 +- modules/zeta_global_sspBidAdapter.js | 33 +- modules/zmaticooBidAdapter.js | 141 - modules/zmaticooBidAdapter.md | 45 - package-lock.json | 784 ++---- package.json | 8 +- plugins/pbjsGlobals.js | 3 +- src/activities/params.js | 5 +- src/activities/redactor.js | 12 +- src/adRendering.js | 86 +- src/adServerManager.js | 2 +- src/adapterManager.js | 68 +- src/adapters/bidderFactory.js | 62 +- src/adloader.js | 11 +- src/ajax.js | 225 +- src/auction.js | 217 +- src/auctionIndex.js | 45 +- src/auctionManager.js | 122 +- src/bidTTL.js | 25 - src/config.js | 30 +- src/consentHandler.js | 82 +- src/constants.json | 21 +- src/events.js | 94 +- src/fpd/enrichment.js | 25 +- src/fpd/sua.js | 10 +- src/mediaTypes.js | 8 +- src/native.js | 27 +- src/prebid.js | 166 +- src/secureCreatives.js | 94 +- src/targeting.js | 69 +- src/userSync.js | 17 +- src/utils.js | 485 +++- src/utils/currency.js | 16 + src/utils/reducers.js | 44 - src/utils/ttlCollection.js | 139 - src/video.js | 30 +- test/mocks/xhr.js | 265 +- test/spec/appnexusKeywords_spec.js | 2 +- test/spec/auctionmanager_spec.js | 485 ++-- .../spec/creative/crossDomainCreative_spec.js | 182 -- .../e2e/modules/e2e_consent_mgt_gdpr.spec.js | 4 +- test/spec/fpd/enrichment_spec.js | 78 +- test/spec/fpd/sua_spec.js | 12 - test/spec/libraries/cmp/cmpClient_spec.js | 296 --- test/spec/libraries/currencyUtils_spec.js | 113 - .../libraries/mspa/activityControls_spec.js | 256 -- test/spec/libraries/sizeUtils_spec.js | 30 - test/spec/libraries/urlUtils_spec.js | 24 - .../modules/33acrossAnalyticsAdapter_spec.js | 1163 --------- test/spec/modules/33acrossBidAdapter_spec.js | 223 +- test/spec/modules/33acrossIdSystem_spec.js | 149 +- test/spec/modules/a1MediaBidAdapter_spec.js | 248 -- test/spec/modules/a1MediaRtdProvider_spec.js | 105 - ...er_spec.js => acuityAdsBidAdapter_spec.js} | 12 +- test/spec/modules/acuityadsBidAdapter_spec.js | 428 ---- test/spec/modules/ad2ictionBidAdapter_spec.js | 223 -- .../modules/adagioAnalyticsAdapter_spec.js | 659 +---- test/spec/modules/adagioBidAdapter_spec.js | 147 +- test/spec/modules/adfBidAdapter_spec.js | 47 - test/spec/modules/adfusionBidAdapter_spec.js | 130 - .../modules/adgenerationBidAdapter_spec.js | 12 +- .../modules/adkernelAdnBidAdapter_spec.js | 3 +- test/spec/modules/adkernelBidAdapter_spec.js | 83 +- test/spec/modules/adlooxAdServerVideo_spec.js | 6 +- test/spec/modules/adlooxRtdProvider_spec.js | 6 +- test/spec/modules/admanBidAdapter_spec.js | 10 +- test/spec/modules/admaticBidAdapter_spec.js | 1014 ++------ test/spec/modules/admixerBidAdapter_spec.js | 43 +- test/spec/modules/adnuntiusBidAdapter_spec.js | 320 +-- test/spec/modules/adpod_spec.js | 3 + test/spec/modules/adqueryBidAdapter_spec.js | 6 +- test/spec/modules/adqueryIdSystem_spec.js | 39 +- test/spec/modules/adstirBidAdapter_spec.js | 412 --- .../modules/adtelligentBidAdapter_spec.js | 17 +- test/spec/modules/adxcgBidAdapter_spec.js | 1372 ++++++---- test/spec/modules/adyoulikeBidAdapter_spec.js | 149 +- .../spec/modules/agmaAnalyticsAdapter_spec.js | 388 --- test/spec/modules/aidemBidAdapter_spec.js | 483 ++-- test/spec/modules/ajaBidAdapter_spec.js | 145 +- test/spec/modules/alkimiBidAdapter_spec.js | 29 +- test/spec/modules/ampliffyBidAdapter_spec.js | 453 ---- test/spec/modules/amxBidAdapter_spec.js | 194 +- test/spec/modules/appnexusBidAdapter_spec.js | 71 +- .../asteriobidAnalyticsAdapter_spec.js | 151 -- .../modules/automatadAnalyticsAdapter_spec.js | 533 ---- test/spec/modules/axisBidAdapter_spec.js | 414 --- .../spec/modules/beachfrontBidAdapter_spec.js | 37 +- test/spec/modules/beopBidAdapter_spec.js | 20 +- test/spec/modules/bidViewability_spec.js | 33 +- test/spec/modules/bizzclickBidAdapter_spec.js | 569 ++-- test/spec/modules/bliinkBidAdapter_spec.js | 885 ++----- test/spec/modules/boldwinBidAdapter_spec.js | 35 +- .../modules/brandmetricsRtdProvider_spec.js | 82 +- test/spec/modules/bridBidAdapter_spec.js | 129 - test/spec/modules/browsiRtdProvider_spec.js | 6 + .../modules/byDataAnalyticsAdapter_spec.js | 16 +- .../cadentApertureMXBidAdapter_spec.js | 516 ++-- test/spec/modules/categoryTranslation_spec.js | 16 +- test/spec/modules/chtnwBidAdapter_spec.js | 5 +- test/spec/modules/concertBidAdapter_spec.js | 42 +- test/spec/modules/connatixBidAdapter_spec.js | 350 --- test/spec/modules/connectIdSystem_spec.js | 74 - .../spec/modules/consentManagementGpp_spec.js | 924 +++---- .../spec/modules/consentManagementUsp_spec.js | 13 - test/spec/modules/consentManagement_spec.js | 26 +- .../spec/modules/consumableBidAdapter_spec.js | 72 +- .../spec/modules/contxtfulRtdProvider_spec.js | 200 -- .../conversantAnalyticsAdapter_spec.js | 8 +- .../spec/modules/conversantBidAdapter_spec.js | 543 ++-- test/spec/modules/criteoBidAdapter_spec.js | 640 +---- test/spec/modules/criteoIdSystem_spec.js | 69 +- test/spec/modules/currency_spec.js | 129 +- test/spec/modules/cwireBidAdapter_spec.js | 47 - .../spec/modules/datablocksBidAdapter_spec.js | 10 +- test/spec/modules/dfpAdServerVideo_spec.js | 489 ++-- .../spec/modules/dgkeywordRtdProvider_spec.js | 34 +- test/spec/modules/discoveryBidAdapter_spec.js | 502 +--- test/spec/modules/dmdIdSystem_spec.js | 4 +- .../docereeAdManagerBidAdapter_spec.js | 125 - test/spec/modules/docereeBidAdapter_spec.js | 33 - .../spec/modules/dsp_genieeBidAdapter_spec.js | 173 -- test/spec/modules/dxkultureBidAdapter_spec.js | 649 ----- .../modules/dynamicAdBoostRtdProvider_spec.js | 77 - test/spec/modules/edge226BidAdapter_spec.js | 373 --- test/spec/modules/eids_spec.js | 234 -- test/spec/modules/eplanningBidAdapter_spec.js | 67 - test/spec/modules/euidIdSystem_spec.js | 53 +- test/spec/modules/experianRtdProvider_spec.js | 365 --- test/spec/modules/feedadBidAdapter_spec.js | 2 +- test/spec/modules/fledgeForGpt_spec.js | 419 --- test/spec/modules/fledge_spec.js | 208 ++ test/spec/modules/flippBidAdapter_spec.js | 170 -- test/spec/modules/fluctBidAdapter_spec.js | 159 +- test/spec/modules/freepassBidAdapter_spec.js | 205 -- test/spec/modules/freepassIdSystem_spec.js | 87 +- .../modules/freewheel-sspBidAdapter_spec.js | 20 +- test/spec/modules/gdprEnforcement_spec.js | 595 ++--- .../modules/genericAnalyticsAdapter_spec.js | 2 +- test/spec/modules/geoedgeRtdProvider_spec.js | 134 +- .../modules/geolocationRtdProvider_spec.js | 110 - .../modules/goldfishAdsRtdProvider_spec.js | 163 -- test/spec/modules/gppControl_usstates_spec.js | 519 ---- test/spec/modules/gptPreAuction_spec.js | 12 - .../modules/greenbidsAnalyticsAdapter_spec.js | 152 +- .../spec/modules/greenbidsRtdProvider_spec.js | 203 +- test/spec/modules/gridBidAdapter_spec.js | 157 +- .../growthCodeAnalyticsAdapter_spec.js | 1 + test/spec/modules/gumgumBidAdapter_spec.js | 68 +- test/spec/modules/hadronIdSystem_spec.js | 4 +- test/spec/modules/holidBidAdapter_spec.js | 32 +- test/spec/modules/id5AnalyticsAdapter_spec.js | 8 +- test/spec/modules/id5IdSystem_spec.js | 1035 ++++---- .../spec/modules/identityLinkIdSystem_spec.js | 107 +- test/spec/modules/illuminBidAdapter_spec.js | 634 ----- test/spec/modules/imdsBidAdapter_spec.js | 115 +- test/spec/modules/impactifyBidAdapter_spec.js | 184 +- .../modules/improvedigitalBidAdapter_spec.js | 10 - .../spec/modules/insticatorBidAdapter_spec.js | 262 +- test/spec/modules/integr8BidAdapter_spec.js | 4 +- test/spec/modules/intentIqIdSystem_spec.js | 40 +- test/spec/modules/invibesBidAdapter_spec.js | 254 +- .../modules/invisiblyAnalyticsAdapter_spec.js | 8 +- test/spec/modules/iqxBidAdapter_spec.js | 455 ---- test/spec/modules/ixBidAdapter_spec.js | 1332 +--------- test/spec/modules/jixieBidAdapter_spec.js | 243 +- test/spec/modules/jwplayerRtdProvider_spec.js | 54 +- test/spec/modules/kargoBidAdapter_spec.js | 278 +- test/spec/modules/kueezRtbBidAdapter_spec.js | 21 +- .../modules/kulturemediaBidAdapter_spec.js | 612 +++++ test/spec/modules/lassoBidAdapter_spec.js | 10 +- .../modules/liveIntentIdMinimalSystem_spec.js | 43 +- test/spec/modules/liveIntentIdSystem_spec.js | 102 +- .../spec/modules/lm_kiviadsBidAdapter_spec.js | 455 ---- .../modules/magniteAnalyticsAdapter_spec.js | 38 +- .../spec/modules/mediabramaBidAdapter_spec.js | 256 -- .../modules/mediafilterRtdProvider_spec.js | 147 -- test/spec/modules/mediagoBidAdapter_spec.js | 512 +--- test/spec/modules/medianetBidAdapter_spec.js | 153 +- .../modules/mediasquareBidAdapter_spec.js | 11 - test/spec/modules/mgidRtdProvider_spec.js | 6 +- test/spec/modules/mgidXBidAdapter_spec.js | 13 +- .../modules/minutemediaBidAdapter_spec.js | 51 +- .../modules/minutemediaplusBidAdapter_spec.js | 19 - test/spec/modules/missenaBidAdapter_spec.js | 154 +- test/spec/modules/mobfoxpbBidAdapter_spec.js | 33 +- test/spec/modules/multibid_spec.js | 21 +- test/spec/modules/mygaruIdSystem_spec.js | 62 - test/spec/modules/nativoBidAdapter_spec.js | 7 +- .../spec/modules/newspassidBidAdapter_spec.js | 6 - .../modules/nextMillenniumBidAdapter_spec.js | 601 +---- test/spec/modules/nexx360BidAdapter_spec.js | 51 +- .../modules/nobidAnalyticsAdapter_spec.js | 498 ---- test/spec/modules/nobidBidAdapter_spec.js | 57 +- test/spec/modules/oguryBidAdapter_spec.js | 426 +-- test/spec/modules/onetagBidAdapter_spec.js | 312 +-- .../spec/modules/ooloAnalyticsAdapter_spec.js | 2 +- test/spec/modules/openxBidAdapter_spec.js | 2 - test/spec/modules/operaadsBidAdapter_spec.js | 89 - test/spec/modules/operaadsIdSystem_spec.js | 53 - .../modules/optidigitalBidAdapter_spec.js | 33 +- test/spec/modules/optimeraRtdProvider_spec.js | 71 +- test/spec/modules/orbidderBidAdapter_spec.js | 149 +- test/spec/modules/outbrainBidAdapter_spec.js | 63 +- .../modules/oxxionAnalyticsAdapter_spec.js | 51 +- test/spec/modules/oxxionRtdProvider_spec.js | 65 +- test/spec/modules/ozoneBidAdapter_spec.js | 41 +- test/spec/modules/pangleBidAdapter_spec.js | 387 --- test/spec/modules/pgamsspBidAdapter_spec.js | 400 --- .../modules/prebidServerBidAdapter_spec.js | 657 +++-- test/spec/modules/precisoBidAdapter_spec.js | 162 -- test/spec/modules/priceFloors_spec.js | 475 +--- .../modules/programmaticaBidAdapter_spec.js | 263 -- test/spec/modules/publinkIdSystem_spec.js | 45 +- .../modules/pubmaticAnalyticsAdapter_spec.js | 495 +--- test/spec/modules/pubmaticBidAdapter_spec.js | 788 +++--- .../modules/pubwiseAnalyticsAdapter_spec.js | 10 +- .../modules/pubxaiAnalyticsAdapter_spec.js | 16 +- .../spec/modules/pulsepointBidAdapter_spec.js | 711 +++-- test/spec/modules/qortexRtdProvider_spec.js | 333 --- test/spec/modules/r2b2BidAdapter_spec.js | 689 ----- test/spec/modules/rasBidAdapter_spec.js | 52 - test/spec/modules/relaidoBidAdapter_spec.js | 28 +- test/spec/modules/relayBidAdapter_spec.js | 131 - .../modules/relevantdigitalBidAdapter_spec.js | 92 +- .../modules/richaudienceBidAdapter_spec.js | 44 - test/spec/modules/riseBidAdapter_spec.js | 18 +- test/spec/modules/rtbhouseBidAdapter_spec.js | 35 - test/spec/modules/rubiconBidAdapter_spec.js | 508 +--- test/spec/modules/seedtagBidAdapter_spec.js | 54 +- .../modules/sharethroughBidAdapter_spec.js | 332 +-- test/spec/modules/shinezRtbBidAdapter_spec.js | 639 ----- test/spec/modules/smaatoBidAdapter_spec.js | 10 +- .../modules/smartadserverBidAdapter_spec.js | 108 +- test/spec/modules/smartyadsBidAdapter_spec.js | 99 +- .../modules/smilewantedBidAdapter_spec.js | 186 +- test/spec/modules/snigelBidAdapter_spec.js | 64 +- .../modules/sonobiAnalyticsAdapter_spec.js | 6 +- test/spec/modules/sonobiBidAdapter_spec.js | 59 +- .../modules/sovrnAnalyticsAdapter_spec.js | 2 +- test/spec/modules/sovrnBidAdapter_spec.js | 120 +- test/spec/modules/sparteoBidAdapter_spec.js | 467 ---- test/spec/modules/ssmasBidAdapter_spec.js | 244 -- test/spec/modules/sspBCBidAdapter_spec.js | 37 +- .../modules/stroeerCoreBidAdapter_spec.js | 13 +- test/spec/modules/stvBidAdapter_spec.js | 44 +- test/spec/modules/taboolaBidAdapter_spec.js | 275 +- test/spec/modules/tagorasBidAdapter_spec.js | 651 ----- test/spec/modules/tappxBidAdapter_spec.js | 19 - test/spec/modules/teadsBidAdapter_spec.js | 267 +- test/spec/modules/teadsIdSystem_spec.js | 2 +- test/spec/modules/theAdxBidAdapter_spec.js | 72 - test/spec/modules/topicsFpdModule_spec.js | 777 +++--- test/spec/modules/tpmnBidAdapter_spec.js | 511 +--- .../modules/trafficgateBidAdapter_spec.js | 1548 ++--------- .../spec/modules/tripleliftBidAdapter_spec.js | 112 +- test/spec/modules/ttdBidAdapter_spec.js | 5 - test/spec/modules/ucfunnelBidAdapter_spec.js | 63 +- test/spec/modules/uid2IdSystem_helpers.js | 14 +- test/spec/modules/uid2IdSystem_spec.js | 365 +-- .../modules/underdogmediaBidAdapter_spec.js | 15 - test/spec/modules/undertoneBidAdapter_spec.js | 38 +- test/spec/modules/unicornBidAdapter_spec.js | 45 +- test/spec/modules/unrulyBidAdapter_spec.js | 433 +--- test/spec/modules/userId_spec.js | 657 +---- test/spec/modules/viantOrtbBidAdapter_spec.js | 475 ---- test/spec/modules/vidazooBidAdapter_spec.js | 134 +- test/spec/modules/visxBidAdapter_spec.js | 192 +- test/spec/modules/voxBidAdapter_spec.js | 93 - test/spec/modules/vrtcalBidAdapter_spec.js | 51 +- test/spec/modules/xeBidAdapter_spec.js | 734 +++--- test/spec/modules/yahoosspBidAdapter_spec.js | 735 +++--- test/spec/modules/yandexBidAdapter_spec.js | 120 +- test/spec/modules/yieldloveBidAdapter_spec.js | 128 - test/spec/modules/yieldmoBidAdapter_spec.js | 53 +- .../zeta_global_sspAnalyticsAdapter_spec.js | 92 +- .../modules/zeta_global_sspBidAdapter_spec.js | 46 +- test/spec/modules/zmaticooBidAdapter_spec.js | 83 - test/spec/native_spec.js | 25 +- test/spec/unit/core/adapterManager_spec.js | 45 +- test/spec/unit/core/ajax_spec.js | 429 ---- test/spec/unit/core/bidderFactory_spec.js | 2281 ++++++++--------- test/spec/unit/core/consentHandler_spec.js | 84 +- test/spec/unit/core/events_spec.js | 45 - test/spec/unit/core/targeting_spec.js | 42 +- test/spec/unit/pbjs_api_spec.js | 59 +- test/spec/unit/secureCreatives_spec.js | 73 +- test/spec/unit/utils/reducers_spec.js | 124 - test/spec/unit/utils/ttlCollection_spec.js | 180 -- test/spec/utils_spec.js | 165 +- test/spec/videoCache_spec.js | 8 +- test/spec/video_spec.js | 244 +- test/test_deps.js | 10 - webpack.creative.js | 19 - 829 files changed, 20081 insertions(+), 72467 deletions(-) delete mode 100644 integrationExamples/gpt/adnuntius_example.html delete mode 100644 integrationExamples/gpt/contxtfulRtdProvider_example.html create mode 100644 integrationExamples/gpt/inskin_example.html create mode 100644 integrationExamples/gpt/pbjs_video_adUnit.html delete mode 100644 integrationExamples/gpt/tpmn_example.html delete mode 100644 integrationExamples/gpt/tpmn_serverless_example.html delete mode 100755 integrationExamples/noadserver/native_noadserver.html delete mode 100644 integrationExamples/topics/topics-server.js delete mode 100644 integrationExamples/videoModule/jwplayer/bidsBackHandlerOverride.html delete mode 100644 integrationExamples/videoModule/videojs/bidsBackHandlerOverride.html rename libraries/{appnexusUtils => appnexusKeywords}/anKeywords.js (91%) delete mode 100644 libraries/appnexusUtils/anUtils.js delete mode 100644 libraries/chunk/chunk.js delete mode 100644 libraries/cmp/cmpClient.js delete mode 100644 libraries/creativeRender/constants.js delete mode 100644 libraries/creativeRender/crossDomain.js delete mode 100644 libraries/creativeRender/direct.js delete mode 100644 libraries/creativeRender/writer.js delete mode 100644 libraries/currencyUtils/currency.js delete mode 100644 libraries/gptUtils/gptUtils.js delete mode 100644 libraries/htmlEscape/htmlEscape.js delete mode 100644 libraries/mspa/activityControls.js delete mode 100644 libraries/sizeUtils/sizeUtils.js delete mode 100644 libraries/transformParamsUtils/convertTypes.js delete mode 100644 libraries/uid2Eids/uid2Eids.js delete mode 100644 libraries/urlUtils/urlUtils.js delete mode 100644 modules/33acrossAnalyticsAdapter.js delete mode 100644 modules/33acrossAnalyticsAdapter.md delete mode 100644 modules/a1MediaBidAdapter.js delete mode 100644 modules/a1MediaBidAdapter.md delete mode 100644 modules/a1MediaRtdProvider.js delete mode 100644 modules/a1MediaRtdProvider.md rename modules/{acuityadsBidAdapter.js => acuityAdsBidAdapter.js} (94%) rename modules/{acuityadsBidAdapter.md => acuityAdsBidAdapter.md} (98%) delete mode 100644 modules/ad2ictionBidAdapter.js delete mode 100644 modules/ad2ictionBidAdapter.md delete mode 100644 modules/adfusionBidAdapter.js delete mode 100644 modules/adfusionBidAdapter.md delete mode 100644 modules/adstirBidAdapter.js delete mode 100644 modules/adstirBidAdapter.md delete mode 100644 modules/agmaAnalyticsAdapter.js delete mode 100644 modules/agmaAnalyticsAdapter.md delete mode 100644 modules/ampliffyBidAdapter.js delete mode 100644 modules/ampliffyBidAdapter.md delete mode 100644 modules/asteriobidAnalyticsAdapter.js delete mode 100644 modules/asteriobidAnalyticsAdapter.md delete mode 100644 modules/automatadAnalyticsAdapter.js delete mode 100644 modules/automatadAnalyticsAdapter.md delete mode 100644 modules/axisBidAdapter.js delete mode 100644 modules/axisBidAdapter.md delete mode 100644 modules/bridBidAdapter.js delete mode 100644 modules/connatixBidAdapter.js delete mode 100644 modules/connatixBidAdapter.md delete mode 100644 modules/contxtfulRtdProvider.js delete mode 100644 modules/contxtfulRtdProvider.md delete mode 100644 modules/docereeAdManagerBidAdapter.js delete mode 100644 modules/docereeAdManagerBidAdapter.md delete mode 100644 modules/dsp_genieeBidAdapter.js delete mode 100644 modules/dsp_genieeBidAdapter.md delete mode 100644 modules/dxkultureBidAdapter.js delete mode 100644 modules/dynamicAdBoostRtdProvider.js delete mode 100644 modules/dynamicAdBoostRtdProvider.md delete mode 100644 modules/edge226BidAdapter.js delete mode 100644 modules/edge226BidAdapter.md delete mode 100644 modules/experianRtdProvider.js delete mode 100644 modules/experianRtdProvider.md delete mode 100644 modules/flippBidAdapter.js delete mode 100644 modules/flippBidAdapter.md delete mode 100644 modules/freepassBidAdapter.js delete mode 100644 modules/freepassBidAdapter.md delete mode 100644 modules/geolocationRtdProvider.js delete mode 100755 modules/goldfishAdsRtdProvider.js delete mode 100755 modules/goldfishAdsRtdProvider.md delete mode 100644 modules/gppControl_usnat.js delete mode 100644 modules/gppControl_usstates.js delete mode 100644 modules/illuminBidAdapter.js delete mode 100644 modules/illuminBidAdapter.md delete mode 100644 modules/iqxBidAdapter.js delete mode 100644 modules/iqxBidAdapter.md create mode 100644 modules/kulturemediaBidAdapter.js rename modules/{dxkultureBidAdapter.md => kulturemediaBidAdapter.md} (86%) delete mode 100644 modules/lm_kiviadsBidAdapter.js delete mode 100644 modules/lm_kiviadsBidAdapter.md delete mode 100644 modules/mediabramaBidAdapter.js delete mode 100644 modules/mediabramaBidAdapter.md delete mode 100644 modules/mediafilterRtdProvider.js delete mode 100644 modules/mediafilterRtdProvider.md delete mode 100644 modules/mygaruIdSystem.js delete mode 100644 modules/mygaruIdSystem.md delete mode 100644 modules/newspassid.md delete mode 100644 modules/nobidAnalyticsAdapter.js delete mode 100644 modules/nobidAnalyticsAdapter.md delete mode 100644 modules/operaadsIdSystem.js delete mode 100644 modules/operaadsIdSystem.md delete mode 100644 modules/pangleBidAdapter.js delete mode 100644 modules/pangleBidAdapter.md delete mode 100644 modules/pgamsspBidAdapter.js delete mode 100644 modules/pgamsspBidAdapter.md delete mode 100644 modules/precisoBidAdapter.js delete mode 100644 modules/precisoBidAdapter.md delete mode 100644 modules/programmaticaBidAdapter.js delete mode 100644 modules/programmaticaBidAdapter.md delete mode 100644 modules/pubCircleBidAdapter.js delete mode 100644 modules/pubCircleBidAdapter.md delete mode 100644 modules/qortexRtdProvider.js delete mode 100644 modules/qortexRtdProvider.md delete mode 100644 modules/r2b2BidAdapter.js delete mode 100644 modules/r2b2BidAdapter.md delete mode 100644 modules/relayBidAdapter.js delete mode 100644 modules/relayBidAdapter.md delete mode 100644 modules/shinezRtbBidAdapter.js delete mode 100644 modules/shinezRtbBidAdapter.md delete mode 100644 modules/sparteoBidAdapter.js delete mode 100644 modules/sparteoBidAdapter.md delete mode 100644 modules/ssmasBidAdapter.js delete mode 100644 modules/ssmasBidAdapter.md delete mode 100644 modules/tagorasBidAdapter.js delete mode 100644 modules/tagorasBidAdapter.md delete mode 100644 modules/viantOrtbBidAdapter.js delete mode 100644 modules/viantOrtbBidAdapter.md delete mode 100644 modules/yieldloveBidAdapter.js delete mode 100644 modules/yieldloveBidAdapter.md delete mode 100644 modules/zmaticooBidAdapter.js delete mode 100644 modules/zmaticooBidAdapter.md delete mode 100644 src/bidTTL.js create mode 100644 src/utils/currency.js delete mode 100644 src/utils/reducers.js delete mode 100644 src/utils/ttlCollection.js delete mode 100644 test/spec/creative/crossDomainCreative_spec.js delete mode 100644 test/spec/libraries/cmp/cmpClient_spec.js delete mode 100644 test/spec/libraries/currencyUtils_spec.js delete mode 100644 test/spec/libraries/mspa/activityControls_spec.js delete mode 100644 test/spec/libraries/sizeUtils_spec.js delete mode 100644 test/spec/libraries/urlUtils_spec.js delete mode 100644 test/spec/modules/33acrossAnalyticsAdapter_spec.js delete mode 100644 test/spec/modules/a1MediaBidAdapter_spec.js delete mode 100644 test/spec/modules/a1MediaRtdProvider_spec.js rename test/spec/modules/{pubCircleBidAdapter_spec.js => acuityAdsBidAdapter_spec.js} (96%) delete mode 100644 test/spec/modules/acuityadsBidAdapter_spec.js delete mode 100644 test/spec/modules/ad2ictionBidAdapter_spec.js delete mode 100644 test/spec/modules/adfusionBidAdapter_spec.js delete mode 100644 test/spec/modules/adstirBidAdapter_spec.js delete mode 100644 test/spec/modules/agmaAnalyticsAdapter_spec.js delete mode 100644 test/spec/modules/ampliffyBidAdapter_spec.js delete mode 100644 test/spec/modules/asteriobidAnalyticsAdapter_spec.js delete mode 100644 test/spec/modules/automatadAnalyticsAdapter_spec.js delete mode 100644 test/spec/modules/axisBidAdapter_spec.js delete mode 100644 test/spec/modules/bridBidAdapter_spec.js delete mode 100644 test/spec/modules/connatixBidAdapter_spec.js delete mode 100644 test/spec/modules/contxtfulRtdProvider_spec.js delete mode 100644 test/spec/modules/docereeAdManagerBidAdapter_spec.js delete mode 100644 test/spec/modules/dsp_genieeBidAdapter_spec.js delete mode 100644 test/spec/modules/dxkultureBidAdapter_spec.js delete mode 100644 test/spec/modules/dynamicAdBoostRtdProvider_spec.js delete mode 100644 test/spec/modules/edge226BidAdapter_spec.js delete mode 100644 test/spec/modules/experianRtdProvider_spec.js delete mode 100644 test/spec/modules/fledgeForGpt_spec.js create mode 100644 test/spec/modules/fledge_spec.js delete mode 100644 test/spec/modules/flippBidAdapter_spec.js delete mode 100644 test/spec/modules/freepassBidAdapter_spec.js delete mode 100644 test/spec/modules/geolocationRtdProvider_spec.js delete mode 100755 test/spec/modules/goldfishAdsRtdProvider_spec.js delete mode 100644 test/spec/modules/gppControl_usstates_spec.js delete mode 100644 test/spec/modules/illuminBidAdapter_spec.js delete mode 100644 test/spec/modules/iqxBidAdapter_spec.js create mode 100644 test/spec/modules/kulturemediaBidAdapter_spec.js delete mode 100644 test/spec/modules/lm_kiviadsBidAdapter_spec.js delete mode 100644 test/spec/modules/mediabramaBidAdapter_spec.js delete mode 100644 test/spec/modules/mediafilterRtdProvider_spec.js delete mode 100644 test/spec/modules/mygaruIdSystem_spec.js delete mode 100644 test/spec/modules/nobidAnalyticsAdapter_spec.js delete mode 100644 test/spec/modules/operaadsIdSystem_spec.js delete mode 100644 test/spec/modules/pangleBidAdapter_spec.js delete mode 100644 test/spec/modules/pgamsspBidAdapter_spec.js delete mode 100644 test/spec/modules/precisoBidAdapter_spec.js delete mode 100644 test/spec/modules/programmaticaBidAdapter_spec.js delete mode 100644 test/spec/modules/qortexRtdProvider_spec.js delete mode 100644 test/spec/modules/r2b2BidAdapter_spec.js delete mode 100644 test/spec/modules/relayBidAdapter_spec.js delete mode 100644 test/spec/modules/shinezRtbBidAdapter_spec.js delete mode 100644 test/spec/modules/sparteoBidAdapter_spec.js delete mode 100644 test/spec/modules/ssmasBidAdapter_spec.js delete mode 100644 test/spec/modules/tagorasBidAdapter_spec.js delete mode 100644 test/spec/modules/viantOrtbBidAdapter_spec.js delete mode 100644 test/spec/modules/yieldloveBidAdapter_spec.js delete mode 100644 test/spec/modules/zmaticooBidAdapter_spec.js delete mode 100644 test/spec/unit/core/ajax_spec.js delete mode 100644 test/spec/unit/core/events_spec.js delete mode 100644 test/spec/unit/utils/reducers_spec.js delete mode 100644 test/spec/unit/utils/ttlCollection_spec.js delete mode 100644 webpack.creative.js diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index d4c34929569..104d9a38132 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -9,9 +9,7 @@ }, "postCreateCommand": "bash .devcontainer/postCreate.sh", - // Make is-docker work again - "postStartCommand": "test -f /.dockerenv || sudo touch /.dockerenv", - + // Set *default* container specific settings.json values on container create. "settings": {}, diff --git a/.eslintrc.js b/.eslintrc.js index 511e78048e4..fc3ad3afe66 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -11,25 +11,12 @@ module.exports = { node: { moduleDirectory: ['node_modules', './'] } - }, - 'jsdoc': { - mode: 'typescript', - tagNamePreference: { - 'tag constructor': 'constructor', - extends: 'extends', - method: 'method', - return: 'return', - } } }, - extends: [ - 'standard', - 'plugin:jsdoc/recommended' - ], + extends: 'standard', plugins: [ 'prebid', - 'import', - 'jsdoc' + 'import' ], globals: { 'BROWSERSTACK_USERNAME': false, @@ -59,24 +46,6 @@ module.exports = { 'no-undef': 2, 'no-useless-escape': 'off', 'no-console': 'error', - 'jsdoc/check-types': 'off', - 'jsdoc/newline-after-description': 'off', - 'jsdoc/require-jsdoc': 'off', - 'jsdoc/require-param': 'off', - 'jsdoc/require-param-description': 'off', - 'jsdoc/require-param-name': 'off', - 'jsdoc/require-param-type': 'off', - 'jsdoc/require-property': 'off', - 'jsdoc/require-property-description': 'off', - 'jsdoc/require-property-name': 'off', - 'jsdoc/require-property-type': 'off', - 'jsdoc/require-returns': 'off', - 'jsdoc/require-returns-check': 'off', - 'jsdoc/require-returns-description': 'off', - 'jsdoc/require-returns-type': 'off', - 'jsdoc/require-yields': 'off', - 'jsdoc/require-yields-check': 'off', - 'jsdoc/tag-lines': 'off' }, overrides: Object.keys(allowedModules).map((key) => ({ files: key + '/**/*.js', diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 3bee8f7c947..584f6f8894a 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -38,11 +38,11 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v3 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v3 + uses: github/codeql-action/init@v2 with: languages: ${{ matrix.language }} config-file: ./.github/codeql/codeql-config.yml @@ -57,7 +57,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@v3 + uses: github/codeql-action/autobuild@v2 # ℹī¸ Command-line programs to run using the OS shell. # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun @@ -70,4 +70,4 @@ jobs: # ./location_of_script_within_repo/buildscript.sh - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v3 + uses: github/codeql-action/analyze@v2 diff --git a/.github/workflows/issue_tracker.yml b/.github/workflows/issue_tracker.yml index b5c59c85160..69cf4c5fc7f 100644 --- a/.github/workflows/issue_tracker.yml +++ b/.github/workflows/issue_tracker.yml @@ -14,7 +14,7 @@ jobs: steps: - name: Generate token id: generate_token - uses: tibdex/github-app-token@3beb63f4bd073e61482598c45c71c1019b59b73a + uses: tibdex/github-app-token@b62528385c34dbc9f38e5f4225ac829252d1ea92 with: app_id: ${{ secrets.ISSUE_APP_ID }} private_key: ${{ secrets.ISSUE_APP_PEM }} diff --git a/PR_REVIEW.md b/PR_REVIEW.md index 9deac9963fb..45ca30a7a3d 100644 --- a/PR_REVIEW.md +++ b/PR_REVIEW.md @@ -55,6 +55,7 @@ Follow steps above for general review process. In addition, please verify the fo - Adapters that accept a floor parameter must also support the [floors module](https://docs.prebid.org/dev-docs/modules/floors.html) -- look for a call to the `getFloor()` function. - Adapters cannot accept an schain parameter. Rather, they must look for the schain parameter at bidRequest.schain. - The bidderRequest.refererInfo.referer must be checked in addition to any bidder-specific parameter. + - If they're getting the COPPA flag, it must come from config.getConfig('coppa'); - Page position must come from bidrequest.mediaTypes.banner.pos or bidrequest.mediaTypes.video.pos - Global OpenRTB fields should come from [getConfig('ortb2');](https://docs.prebid.org/dev-docs/publisher-api-reference/setConfig.html#setConfig-fpd): - bcat, battr, badv diff --git a/features.json b/features.json index 4d8377cda7d..ccb2166a05f 100644 --- a/features.json +++ b/features.json @@ -1,5 +1,4 @@ [ "NATIVE", - "VIDEO", - "UID2_CSTG" + "VIDEO" ] diff --git a/gulpfile.js b/gulpfile.js index 5e16af8b0c1..09de874e389 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -158,20 +158,6 @@ function makeWebpackPkg(extraConfig = {}) { } } -function buildCreative() { - return gulp.src(['**/*']) - .pipe(webpackStream(require('./webpack.creative.js'))) - .pipe(gulp.dest('build/creative')) -} - -function updateCreativeExample(cb) { - const CREATIVE_EXAMPLE = 'integrationExamples/gpt/x-domain/creative.html'; - const root = require('node-html-parser').parse(fs.readFileSync(CREATIVE_EXAMPLE)); - root.querySelectorAll('script')[0].textContent = fs.readFileSync('build/creative/creative.js') - fs.writeFileSync(CREATIVE_EXAMPLE, root.toString()) - cb(); -} - function getModulesListToAddInBanner(modules) { if (!modules || modules.length === helpers.getModuleNames().length) { return 'All available modules for this version.' @@ -419,7 +405,6 @@ function watchTaskMaker(options = {}) { return function watch(done) { var mainWatcher = gulp.watch([ 'src/**/*.js', - 'libraries/**/*.js', 'modules/**/*.js', ].concat(options.alsoWatch)); @@ -430,8 +415,8 @@ function watchTaskMaker(options = {}) { } } -const watch = watchTaskMaker({alsoWatch: ['test/**/*.js'], task: () => gulp.series(clean, gulp.parallel(lint, 'build-bundle-dev', test, buildCreative))}); -const watchFast = watchTaskMaker({livereload: false, task: () => gulp.parallel('build-bundle-dev', buildCreative)}); +const watch = watchTaskMaker({alsoWatch: ['test/**/*.js'], task: () => gulp.series(clean, gulp.parallel(lint, 'build-bundle-dev', test))}); +const watchFast = watchTaskMaker({livereload: false, task: () => gulp.series('build-bundle-dev')}); // support tasks gulp.task(lint); @@ -462,23 +447,21 @@ gulp.task('build-bundle-verbose', gulp.series(makeWebpackPkg({ } }), gulpBundle.bind(null, false))); -gulp.task('build-creative', gulp.series(buildCreative, updateCreativeExample)); - // public tasks (dependencies are needed for each task since they can be ran on their own) gulp.task('test-only', test); gulp.task('test-all-features-disabled', testTaskMaker({disableFeatures: require('./features.json'), oneBrowser: 'chrome', watch: false})); -gulp.task('test', gulp.series(clean, lint, gulp.parallel('build-creative', gulp.series('test-all-features-disabled', 'test-only')))); +gulp.task('test', gulp.series(clean, lint, gulp.series('test-all-features-disabled', 'test-only'))); gulp.task('test-coverage', gulp.series(clean, testCoverage)); gulp.task(viewCoverage); gulp.task('coveralls', gulp.series('test-coverage', coveralls)); -gulp.task('build', gulp.series(clean, 'build-bundle-prod', 'build-creative')); +gulp.task('build', gulp.series(clean, 'build-bundle-prod')); gulp.task('build-postbid', gulp.series(escapePostbidConfig, buildPostbid)); gulp.task('serve', gulp.series(clean, lint, gulp.parallel('build-bundle-dev', watch, test))); -gulp.task('serve-fast', gulp.series(clean, gulp.parallel('build-bundle-dev', buildCreative, watchFast))); +gulp.task('serve-fast', gulp.series(clean, gulp.parallel('build-bundle-dev', watchFast))); gulp.task('serve-prod', gulp.series(clean, gulp.parallel('build-bundle-prod', startLocalServer))); gulp.task('serve-and-test', gulp.series(clean, gulp.parallel('build-bundle-dev', watchFast, testTaskMaker({watch: true})))); gulp.task('serve-e2e', gulp.series(clean, 'build-bundle-prod', gulp.parallel(() => startIntegServer(), startLocalServer))); diff --git a/integrationExamples/gpt/adUnitFloors.html b/integrationExamples/gpt/adUnitFloors.html index a80e1b2380b..bb48a20ef78 100644 --- a/integrationExamples/gpt/adUnitFloors.html +++ b/integrationExamples/gpt/adUnitFloors.html @@ -109,3 +109,4 @@
Div-1
+ diff --git a/integrationExamples/gpt/adnuntius_example.html b/integrationExamples/gpt/adnuntius_example.html deleted file mode 100644 index b61c4e0674e..00000000000 --- a/integrationExamples/gpt/adnuntius_example.html +++ /dev/null @@ -1,95 +0,0 @@ - - - - - - - -

Adnuntius Prebid Adaptor Test

-
Ad Slot 1
- - -
- -
- - - diff --git a/integrationExamples/gpt/contxtfulRtdProvider_example.html b/integrationExamples/gpt/contxtfulRtdProvider_example.html deleted file mode 100644 index 29284de81a2..00000000000 --- a/integrationExamples/gpt/contxtfulRtdProvider_example.html +++ /dev/null @@ -1,91 +0,0 @@ - - - - - - - - - -

Contxtful RTD Provider

-
- - - - \ No newline at end of file diff --git a/integrationExamples/gpt/growthcode.html b/integrationExamples/gpt/growthcode.html index 35de2b710ad..d8ad6c4a5af 100644 --- a/integrationExamples/gpt/growthcode.html +++ b/integrationExamples/gpt/growthcode.html @@ -56,11 +56,20 @@ provider: 'growthCodeAnalytics', options: { pid: 'TEST01', - //url: 'http://localhost:8080/v3/pb/analytics', trackEvents: [ + 'auctionInit', 'auctionEnd', + 'bidAdjustment', + 'bidTimeout', + 'bidTimeout', + 'bidRequested', + 'bidResponse', + 'setTargeting', + 'requestBids', + 'addAdUnits', + 'noBid', 'bidWon', - ] + 'bidderDone'] } }); pbjs.setConfig({ @@ -71,7 +80,7 @@ auctionDelay: 1000, dataProviders: [{ name: 'growthCodeRtd', - waitForIt: false, + waitForIt: true, params: { pid: 'TEST01', } diff --git a/integrationExamples/gpt/hello_world.html b/integrationExamples/gpt/hello_world.html index 03a2356f0ef..47ba5b8f18a 100644 --- a/integrationExamples/gpt/hello_world.html +++ b/integrationExamples/gpt/hello_world.html @@ -8,7 +8,6 @@ --> - @@ -20,10 +19,9 @@ code: 'div-gpt-ad-1460505748561-0', mediaTypes: { banner: { - sizes: [[300, 250]], + sizes: [[300, 250], [300,600]], } }, - // Replace this object to test a new Adapter! bids: [{ bidder: 'appnexus', @@ -42,13 +40,12 @@ - +

Prebid.js Test

+
Div-1
+
+ +
- \ No newline at end of file diff --git a/integrationExamples/gpt/inskin_example.html b/integrationExamples/gpt/inskin_example.html new file mode 100644 index 00000000000..45fd448cf48 --- /dev/null +++ b/integrationExamples/gpt/inskin_example.html @@ -0,0 +1,100 @@ + + + + + + + + + + + + + + + +

Prebid.js Test

+
Div-1
+
+ +
+ + diff --git a/integrationExamples/gpt/pbjs_video_adUnit.html b/integrationExamples/gpt/pbjs_video_adUnit.html new file mode 100644 index 00000000000..080ca9be142 --- /dev/null +++ b/integrationExamples/gpt/pbjs_video_adUnit.html @@ -0,0 +1,109 @@ + + + + + Prebid.js video adUnit example + + + + + + + + + + + +
+ +
+ + + + + diff --git a/integrationExamples/gpt/prebidServer_example.html b/integrationExamples/gpt/prebidServer_example.html index ded50777ad2..f23554369bc 100644 --- a/integrationExamples/gpt/prebidServer_example.html +++ b/integrationExamples/gpt/prebidServer_example.html @@ -33,41 +33,31 @@ pbjs.que.push(function() { var adUnits = [{ - code: 'div-gpt-ad-1460505748561-0', - mediaTypes: { - banner: { - sizes: [600, 500] - } - }, - bids: [ - { - bidder: 'appnexus', - params: { - placementId: 12883451 + code: 'div-gpt-ad-1460505748561-0', + mediaTypes: { + banner: { + sizes: [[300, 250]] + } + }, + bids: [ + { + bidder: 'appnexus', + params: { + placementId: 13144370 + } } - } - ] - }]; + ] + }]; - pbjs.bidderSettings = { - appnexus: { - bidCpmAdjustment: function () { - return 10; - } - } - } pbjs.setConfig({ bidderTimeout: 3000, s2sConfig : { accountId : '1', enabled : true, //default value set to false - defaultVendor: 'appnexuspsp', + defaultVendor: 'appnexus', bidders : ['appnexus'], timeout : 1000, //default value is 1000 adapter : 'prebidServer', //if we have any other s2s adapter, default value is s2s - }, - ortb2: { - test: 1 } }); diff --git a/integrationExamples/gpt/prebidServer_fledge_example.html b/integrationExamples/gpt/prebidServer_fledge_example.html index eb2fc438997..8523c0f2920 100644 --- a/integrationExamples/gpt/prebidServer_fledge_example.html +++ b/integrationExamples/gpt/prebidServer_fledge_example.html @@ -50,7 +50,7 @@ s2sConfig: [{ accountId : '1', enabled : true, - defaultVendor: 'appnexuspsp', + defaultVendor: 'appnexus', bidders : ['openx'], timeout : 1500, adapter : 'prebidServer' diff --git a/integrationExamples/gpt/prebidServer_native_example.html b/integrationExamples/gpt/prebidServer_native_example.html index a5fb0ffa894..c590f0bcee5 100644 --- a/integrationExamples/gpt/prebidServer_native_example.html +++ b/integrationExamples/gpt/prebidServer_native_example.html @@ -114,7 +114,7 @@ s2sConfig: { accountId: '1', enabled: true, //default value set to false - bidders: ['appnexuspsp'], + bidders: ['appnexus'], timeout: 1000, //default value is 1000 adapter: 'prebidServer', //if we have any other s2s adapter, default value is s2s endpoint: 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction' diff --git a/integrationExamples/gpt/tpmn_example.html b/integrationExamples/gpt/tpmn_example.html deleted file mode 100644 index f215181c7e0..00000000000 --- a/integrationExamples/gpt/tpmn_example.html +++ /dev/null @@ -1,168 +0,0 @@ - - - - - Prebid.js Banner Example - - - - - - - - - - -

Prebid.js TPMN Banner Example

- -
-

Prebid.js TPMN Video Example

-
- -
-
-
- diff --git a/integrationExamples/gpt/tpmn_serverless_example.html b/integrationExamples/gpt/tpmn_serverless_example.html deleted file mode 100644 index 0acaefbeb9c..00000000000 --- a/integrationExamples/gpt/tpmn_serverless_example.html +++ /dev/null @@ -1,121 +0,0 @@ - - - - - - - - - - - - - -

Ad Serverless Test Page

- - -
-
- - diff --git a/integrationExamples/gpt/x-domain/creative.html b/integrationExamples/gpt/x-domain/creative.html index f1c0c647e72..2216d0ed6ae 100644 --- a/integrationExamples/gpt/x-domain/creative.html +++ b/integrationExamples/gpt/x-domain/creative.html @@ -1,13 +1,105 @@ + +const windowLocation = window.location; +const urlParser = document.createElement('a'); +urlParser.href = '%%PATTERN:url%%'; +const publisherDomain = urlParser.protocol + '//' + urlParser.hostname; +const adId = '%%PATTERN:hb_adid%%'; - diff --git a/integrationExamples/noadserver/native_noadserver.html b/integrationExamples/noadserver/native_noadserver.html deleted file mode 100755 index 81c71d2acfd..00000000000 --- a/integrationExamples/noadserver/native_noadserver.html +++ /dev/null @@ -1,173 +0,0 @@ - - - - - - - - - - - - - -

Prebid Native

-
-
- -
-
- - - - diff --git a/integrationExamples/topics/topics-server.js b/integrationExamples/topics/topics-server.js deleted file mode 100644 index 0d248e5557c..00000000000 --- a/integrationExamples/topics/topics-server.js +++ /dev/null @@ -1,72 +0,0 @@ -// This is an example of a server-side endpoint that is utilizing the Topics API header functionality. -// Note: This test endpoint requires the following to run: node.js, npm, express, cors, body-parser - -const bodyParser = require('body-parser'); -const cors = require('cors'); -const express = require('express'); - -const port = process.env.PORT || 3000; - -const app = express(); -app.use(cors()); -app.use( - bodyParser.urlencoded({ - extended: true, - }) -); -app.use(bodyParser.json()); -app.use(express.static('public')); -app.set('port', port); - -const listener = app.listen(port, () => { - const host = - listener.address().address === '::' - ? 'http://localhost' - : 'http://' + listener.address().address; - // eslint-disable-next-line no-console - console.log( - `${__filename} is listening on ${host}:${listener.address().port}\n` - ); -}); - -app.get('*', (req, res) => { - res.setHeader('Observe-Browsing-Topics', '?1'); - - const resData = { - segment: { - domain: req.hostname, - topics: generateTopicArrayFromHeader(req.headers['sec-browsing-topics']), - bidder: req.query['bidder'], - }, - date: Date.now(), - }; - - res.json(resData); -}); - -const generateTopicArrayFromHeader = (topicString) => { - const result = []; - const topicArray = topicString.split(', '); - if (topicArray.length > 1) { - topicArray.pop(); - topicArray.map((topic) => { - const topicId = topic.split(';')[0]; - const versionsString = topic.split(';')[1].split('=')[1]; - const [config, taxonomy, model] = versionsString.split(':'); - const numTopicsWithSameVersions = topicId - .substring(1, topicId.length - 1) - .split(' '); - - numTopicsWithSameVersions.map((tpId) => { - result.push({ - topic: tpId, - version: versionsString, - configVersion: config, - taxonomyVersion: taxonomy, - modelVersion: model, - }); - }); - }); - } - return result; -}; diff --git a/integrationExamples/videoModule/jwplayer/bidMarkedAsUsed.html b/integrationExamples/videoModule/jwplayer/bidMarkedAsUsed.html index 80ea81d09b6..044b4295e67 100644 --- a/integrationExamples/videoModule/jwplayer/bidMarkedAsUsed.html +++ b/integrationExamples/videoModule/jwplayer/bidMarkedAsUsed.html @@ -37,7 +37,7 @@ divId: 'player', vendorCode: 1, // jwplayer vendorCode playerConfig: { - licenseKey: 'zwqnWJlovTKhXv2JIcKBj0Si//K7cVPmBDEyaILcAMw+nVKaizsJRA==', + licenseKey: 'IAjLREYRLylTWsfLN3FoN/O3iQLbs+AfgZLlkAoyH8gSf7TnNtmOLcR8CUY=', params: { vendorConfig: { file: 'http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/SubaruOutbackOnStreetAndDirt.mp4', @@ -84,7 +84,7 @@ console.log('An Ad Impression came from a Bid: ', e); }); - pbjs.requestBids(); + pbjs.requestBids(adUnits); }); diff --git a/integrationExamples/videoModule/jwplayer/bidRequestScheduling.html b/integrationExamples/videoModule/jwplayer/bidRequestScheduling.html index 663765317b0..620f891fa50 100644 --- a/integrationExamples/videoModule/jwplayer/bidRequestScheduling.html +++ b/integrationExamples/videoModule/jwplayer/bidRequestScheduling.html @@ -20,7 +20,7 @@ divId: 'player', vendorCode: 1, // JW Player vendorCode playerConfig: { - licenseKey: 'zwqnWJlovTKhXv2JIcKBj0Si//K7cVPmBDEyaILcAMw+nVKaizsJRA==', + licenseKey: 'IAjLREYRLylTWsfLN3FoN/O3iQLbs+AfgZLlkAoyH8gSf7TnNtmOLcR8CUY=', params: { vendorConfig: { file: 'http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/SubaruOutbackOnStreetAndDirt.mp4', diff --git a/integrationExamples/videoModule/jwplayer/bidsBackHandlerOverride.html b/integrationExamples/videoModule/jwplayer/bidsBackHandlerOverride.html deleted file mode 100644 index 66eaff26090..00000000000 --- a/integrationExamples/videoModule/jwplayer/bidsBackHandlerOverride.html +++ /dev/null @@ -1,137 +0,0 @@ - - - - - - - JW Player with Bids Back Handler override - - - - - -

JW Player with Bids Back Handler override

-
Div-1: Player placeholder div
-
- - - diff --git a/integrationExamples/videoModule/jwplayer/eventListeners.html b/integrationExamples/videoModule/jwplayer/eventListeners.html index 39acb086107..0b43ff44d6c 100644 --- a/integrationExamples/videoModule/jwplayer/eventListeners.html +++ b/integrationExamples/videoModule/jwplayer/eventListeners.html @@ -29,7 +29,7 @@ } }] }]; - + pbjs.que.push(function () { pbjs.setConfig({ video: { @@ -37,7 +37,7 @@ divId: 'player', vendorCode: 1, // vendorCode for jwplayer playerConfig: { - licenseKey: 'zwqnWJlovTKhXv2JIcKBj0Si//K7cVPmBDEyaILcAMw+nVKaizsJRA==', + licenseKey: 'IAjLREYRLylTWsfLN3FoN/O3iQLbs+AfgZLlkAoyH8gSf7TnNtmOLcR8CUY=', params: { vendorConfig: { file: 'http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/SubaruOutbackOnStreetAndDirt.mp4', @@ -237,7 +237,7 @@ console.log('The ad error resulted from a bid! \n', event); }); - pbjs.requestBids(); + pbjs.requestBids(adUnits); }); diff --git a/integrationExamples/videoModule/jwplayer/eventsUI.html b/integrationExamples/videoModule/jwplayer/eventsUI.html index 78d72a6153d..69ace5f6c2b 100644 --- a/integrationExamples/videoModule/jwplayer/eventsUI.html +++ b/integrationExamples/videoModule/jwplayer/eventsUI.html @@ -39,7 +39,7 @@ divId: 'player', vendorCode: 1, // jwplayer vendorCode playerConfig: { - licenseKey: 'zwqnWJlovTKhXv2JIcKBj0Si//K7cVPmBDEyaILcAMw+nVKaizsJRA==', + licenseKey: '577+5vXsluqV2Uy0drAS8wrgiqJlYijZxz3DmoYDm8FTJjdoIe8zYA==', params: { vendorConfig: { playlist: [{ @@ -278,7 +278,7 @@ eventHandler(e, "bidImpression", "auction", ); }); - pbjs.requestBids(); + pbjs.requestBids(adUnits); }); diff --git a/integrationExamples/videoModule/jwplayer/gamAdServerMediation.html b/integrationExamples/videoModule/jwplayer/gamAdServerMediation.html index 018c8eba8b2..296a3265bd1 100644 --- a/integrationExamples/videoModule/jwplayer/gamAdServerMediation.html +++ b/integrationExamples/videoModule/jwplayer/gamAdServerMediation.html @@ -36,7 +36,7 @@ divId: 'player', vendorCode: 1, // JW Player vendorCode playerConfig: { - licenseKey: 'zwqnWJlovTKhXv2JIcKBj0Si//K7cVPmBDEyaILcAMw+nVKaizsJRA==', + licenseKey: 'IAjLREYRLylTWsfLN3FoN/O3iQLbs+AfgZLlkAoyH8gSf7TnNtmOLcR8CUY=', params: { vendorConfig: { file: 'http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/SubaruOutbackOnStreetAndDirt.mp4', @@ -106,7 +106,7 @@ console.log('An Ad Impression came from a Bid: ', e); }); - pbjs.requestBids(); + pbjs.requestBids(adUnits); }); diff --git a/integrationExamples/videoModule/jwplayer/mediaMetadata.html b/integrationExamples/videoModule/jwplayer/mediaMetadata.html index 7581af571d3..815f5c4d6d7 100644 --- a/integrationExamples/videoModule/jwplayer/mediaMetadata.html +++ b/integrationExamples/videoModule/jwplayer/mediaMetadata.html @@ -37,7 +37,7 @@ divId: 'player', vendorCode: 1, // JW Player vendorCode playerConfig: { - licenseKey: 'zwqnWJlovTKhXv2JIcKBj0Si//K7cVPmBDEyaILcAMw+nVKaizsJRA==', + licenseKey: 'IAjLREYRLylTWsfLN3FoN/O3iQLbs+AfgZLlkAoyH8gSf7TnNtmOLcR8CUY=', params: { vendorConfig: { mediaid: 'XYXYXYXY', @@ -66,7 +66,7 @@ console.log('videos pb contentLoaded: ', e); }); - pbjs.requestBids(); + pbjs.requestBids(adUnits); }); diff --git a/integrationExamples/videoModule/jwplayer/playlist.html b/integrationExamples/videoModule/jwplayer/playlist.html index 89efaea3d5c..b77a1ec05fc 100644 --- a/integrationExamples/videoModule/jwplayer/playlist.html +++ b/integrationExamples/videoModule/jwplayer/playlist.html @@ -38,7 +38,7 @@ vendorCode: 1, // JW Player vendorCode playerConfig: { params: { - licenseKey: 'zwqnWJlovTKhXv2JIcKBj0Si//K7cVPmBDEyaILcAMw+nVKaizsJRA==', + licenseKey: 'IAjLREYRLylTWsfLN3FoN/O3iQLbs+AfgZLlkAoyH8gSf7TnNtmOLcR8CUY=', vendorConfig: { playlist: [{ mediaid: 'XYXYXYXY', @@ -107,7 +107,7 @@ console.log('videos pb playlistComplete: ', e); }); - pbjs.requestBids(); + pbjs.requestBids(adUnits); }); diff --git a/integrationExamples/videoModule/videojs/bidMarkedAsUsed.html b/integrationExamples/videoModule/videojs/bidMarkedAsUsed.html index d6656bc0c93..b645a58a4fc 100644 --- a/integrationExamples/videoModule/videojs/bidMarkedAsUsed.html +++ b/integrationExamples/videoModule/videojs/bidMarkedAsUsed.html @@ -109,7 +109,7 @@ console.log('An Ad Impression came from a Bid: ', e); }); - pbjs.requestBids(); + pbjs.requestBids(adUnits); }); diff --git a/integrationExamples/videoModule/videojs/bidRequestScheduling.html b/integrationExamples/videoModule/videojs/bidRequestScheduling.html index eb10fda89a2..48f8b8685e1 100644 --- a/integrationExamples/videoModule/videojs/bidRequestScheduling.html +++ b/integrationExamples/videoModule/videojs/bidRequestScheduling.html @@ -111,7 +111,7 @@ pbjs.onEvent('videoTime', (e) => { // when the video reaches the midroll, request bids and update the offset. if (e.position >= midrollOffset) { - pbjs.requestBids(); + pbjs.requestBids(adUnits); midrollOffset += 10; } }); diff --git a/integrationExamples/videoModule/videojs/bidsBackHandlerOverride.html b/integrationExamples/videoModule/videojs/bidsBackHandlerOverride.html deleted file mode 100644 index ac8f4163e76..00000000000 --- a/integrationExamples/videoModule/videojs/bidsBackHandlerOverride.html +++ /dev/null @@ -1,164 +0,0 @@ - - - - - - - - - - - - - - VideoJS with Bids Back Handler override - - - - - -

VideoJS with Bids Back Handler override

-
Div-1: Player placeholder div
- - - - - - diff --git a/integrationExamples/videoModule/videojs/eventListeners.html b/integrationExamples/videoModule/videojs/eventListeners.html index 1966f134e02..16edaf4da41 100644 --- a/integrationExamples/videoModule/videojs/eventListeners.html +++ b/integrationExamples/videoModule/videojs/eventListeners.html @@ -225,7 +225,7 @@ console.log('videos pb bid Error: ', e); }); - pbjs.requestBids(); + pbjs.requestBids(adUnits); }); diff --git a/integrationExamples/videoModule/videojs/eventsUI.html b/integrationExamples/videoModule/videojs/eventsUI.html index 9eba09f7a52..04e0ca9eaf8 100644 --- a/integrationExamples/videoModule/videojs/eventsUI.html +++ b/integrationExamples/videoModule/videojs/eventsUI.html @@ -301,7 +301,7 @@ eventHandler(e, "bidImpression", "auction", ); }); - pbjs.requestBids(); + pbjs.requestBids(adUnits); }); diff --git a/integrationExamples/videoModule/videojs/gamAdServerMediation.html b/integrationExamples/videoModule/videojs/gamAdServerMediation.html index 6ffc1a67c03..9efd77e9681 100644 --- a/integrationExamples/videoModule/videojs/gamAdServerMediation.html +++ b/integrationExamples/videoModule/videojs/gamAdServerMediation.html @@ -123,7 +123,7 @@ console.log('An Ad Impression came from a Bid: ', e); }); - pbjs.requestBids(); + pbjs.requestBids(adUnits); }); diff --git a/integrationExamples/videoModule/videojs/mediaMetadata.html b/integrationExamples/videoModule/videojs/mediaMetadata.html index ede076fd814..eef12b0e19d 100644 --- a/integrationExamples/videoModule/videojs/mediaMetadata.html +++ b/integrationExamples/videoModule/videojs/mediaMetadata.html @@ -87,7 +87,7 @@ console.log('videos pb contentLoaded: ', e); }); - pbjs.requestBids(); + pbjs.requestBids(adUnits); }); diff --git a/integrationExamples/videoModule/videojs/playlist.html b/integrationExamples/videoModule/videojs/playlist.html index eb813f095f7..a9e3d1bc99b 100644 --- a/integrationExamples/videoModule/videojs/playlist.html +++ b/integrationExamples/videoModule/videojs/playlist.html @@ -134,7 +134,7 @@ console.log('videos pb complete: ', e); }); - pbjs.requestBids(); + pbjs.requestBids(adUnits); }); diff --git a/libraries/appnexusUtils/anKeywords.js b/libraries/appnexusKeywords/anKeywords.js similarity index 91% rename from libraries/appnexusUtils/anKeywords.js rename to libraries/appnexusKeywords/anKeywords.js index d6714dacc21..5dc0b453253 100644 --- a/libraries/appnexusUtils/anKeywords.js +++ b/libraries/appnexusKeywords/anKeywords.js @@ -1,4 +1,4 @@ -import {_each, deepAccess, isArray, isNumber, isStr, mergeDeep, logWarn} from '../../src/utils.js'; +import {_each, deepAccess, getValueString, isArray, isStr, mergeDeep, isNumber} from '../../src/utils.js'; import {getAllOrtbKeywords} from '../keywords/keywords.js'; import {CLIENT_SECTIONS} from '../../src/fpd/oneClient.js'; @@ -12,19 +12,6 @@ const ORTB_SEG_PATHS = ['user.data'].concat( CLIENT_SECTIONS.map((prefix) => `${prefix}.content.data`) ); -function getValueString(param, val, defaultValue) { - if (val === undefined || val === null) { - return defaultValue; - } - if (isStr(val)) { - return val; - } - if (isNumber(val)) { - return val.toString(); - } - logWarn('Unsuported type for param: ' + param + ' required type: String'); -} - /** * Converts an object of arrays (either strings or numbers) into an array of objects containing key and value properties * normally read from bidder params diff --git a/libraries/appnexusUtils/anUtils.js b/libraries/appnexusUtils/anUtils.js deleted file mode 100644 index 9b55cd5c2a4..00000000000 --- a/libraries/appnexusUtils/anUtils.js +++ /dev/null @@ -1,25 +0,0 @@ -/** - * 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'; - -export function convertCamelToUnderscore(value) { - return value.replace(/(?:^|\.?)([A-Z])/g, function (x, y) { - return '_' + y.toLowerCase(); - }).replace(/^_/, ''); -} - -/** - * Creates an array of n length and fills each item with the given value - */ -export function fill(value, length) { - let newArray = []; - - for (let i = 0; i < length; i++) { - let valueToPush = isPlainObject(value) ? deepClone(value) : value; - newArray.push(valueToPush); - } - - return newArray; -} diff --git a/libraries/chunk/chunk.js b/libraries/chunk/chunk.js deleted file mode 100644 index 57be7bd5016..00000000000 --- a/libraries/chunk/chunk.js +++ /dev/null @@ -1,19 +0,0 @@ -/** - * http://npm.im/chunk - * Returns an array with *size* chunks from given array - * - * Example: - * ['a', 'b', 'c', 'd', 'e'] chunked by 2 => - * [['a', 'b'], ['c', 'd'], ['e']] - */ -export function chunk(array, size) { - let newArray = []; - - for (let i = 0; i < Math.ceil(array.length / size); i++) { - let start = i * size; - let end = start + size; - newArray.push(array.slice(start, end)); - } - - return newArray; -} diff --git a/libraries/cmp/cmpClient.js b/libraries/cmp/cmpClient.js deleted file mode 100644 index 1d0b327cee4..00000000000 --- a/libraries/cmp/cmpClient.js +++ /dev/null @@ -1,169 +0,0 @@ -import {GreedyPromise} from '../../src/utils/promise.js'; - -/** - * @typedef {function} CMPClient - * - * @param {{}} params CMP parameters. Currently this is a subset of {command, callback, parameter, version}. - * @param {boolean} once if true, discard cross-frame event listeners once a reply message is received. - * @returns {Promise<*>} a promise to the API's "result" - see the `mode` argument to `cmpClient` on how that's determined. - * @property {boolean} isDirect true if the CMP is directly accessible (no postMessage required) - * @property {() => void} close close the client; currently, this just stops listening for cross-frame messages. - */ - -export const MODE_MIXED = 0; -export const MODE_RETURN = 1; -export const MODE_CALLBACK = 2; - -/** - * Returns a client function that can interface with a CMP regardless of where it's located. - * - * @param {object} obj - * @param obj.apiName name of the CMP api, e.g. "__gpp" - * @param [obj.apiVersion] CMP API version - * @param [obj.apiArgs] names of the arguments taken by the api function, in order. - * @param [obj.callbackArgs] names of the cross-frame response payload properties that should be passed as callback arguments, in order - * @param [obj.mode] controls the callbacks passed to the underlying API, and how the promises returned by the client are resolved. - * - * The client behaves differently when it's provided a `callback` argument vs when it's not - for short, let's name these - * cases "subscriptions" and "one-shot calls" respectively: - * - * With `mode: MODE_MIXED` (the default), promises returned on subscriptions are resolved to undefined when the callback - * is first run (that is, the promise resolves when the CMP replies, but what it replies with is discarded and - * left for the callback to deal with). For one-shot calls, the returned promise is resolved to the API's - * return value when it's directly accessible, or with the result from the first (and, presumably, the only) - * cross-frame reply when it's not; - * - * With `mode: MODE_RETURN`, the returned promise always resolves to the API's return value - which is taken to be undefined - * when cross-frame; - * - * With `mode: MODE_CALLBACK`, the underlying API is expected to never directly return anything significant; instead, - * it should always accept a callback and - for one-shot calls - invoke it only once with the result. The client will - * automatically generate an appropriate callback for one-shot calls and use the result it's given to resolve - * the returned promise. Subscriptions are treated in the same way as MODE_MIXED. - * - * @param win - * @returns {CMPClient} CMP invocation function (or null if no CMP was found). - */ -export function cmpClient( - { - apiName, - apiVersion, - apiArgs = ['command', 'callback', 'parameter', 'version'], - callbackArgs = ['returnValue', 'success'], - mode = MODE_MIXED, - }, - win = window -) { - const cmpCallbacks = {}; - const callName = `${apiName}Call`; - const cmpDataPkgName = `${apiName}Return`; - - function handleMessage(event) { - const json = (typeof event.data === 'string' && event.data.includes(cmpDataPkgName)) ? JSON.parse(event.data) : event.data; - if (json?.[cmpDataPkgName]?.callId) { - const payload = json[cmpDataPkgName]; - - if (cmpCallbacks.hasOwnProperty(payload.callId)) { - cmpCallbacks[payload.callId](...callbackArgs.map(name => payload[name])); - } - } - } - - function findCMP() { - let f = win; - let cmpFrame; - let isDirect = false; - while (f != null) { - try { - if (typeof f[apiName] === 'function') { - cmpFrame = f; - isDirect = true; - break; - } - } catch (e) { - } - - // need separate try/catch blocks due to the exception errors thrown when trying to check for a frame that doesn't exist in 3rd party env - try { - if (f.frames[`${apiName}Locator`]) { - cmpFrame = f; - break; - } - } catch (e) { - } - - if (f === win.top) break; - f = f.parent; - } - - return [ - cmpFrame, - isDirect - ]; - } - - const [cmpFrame, isDirect] = findCMP(); - - if (!cmpFrame) { - return; - } - - function resolveParams(params) { - params = Object.assign({version: apiVersion}, params); - return apiArgs.map(arg => [arg, params[arg]]) - } - - function wrapCallback(callback, resolve, reject, preamble) { - const haveCb = typeof callback === 'function'; - - return function (result, success) { - preamble && preamble(); - if (mode !== MODE_RETURN) { - const resolver = success == null || success ? resolve : reject; - resolver(haveCb ? undefined : result); - } - haveCb && callback.apply(this, arguments); - } - } - - let client; - - if (isDirect) { - client = function invokeCMPDirect(params = {}) { - return new GreedyPromise((resolve, reject) => { - const ret = cmpFrame[apiName](...resolveParams({ - ...params, - callback: (params.callback || mode === MODE_CALLBACK) ? wrapCallback(params.callback, resolve, reject) : undefined, - }).map(([_, val]) => val)); - if (mode === MODE_RETURN || (params.callback == null && mode === MODE_MIXED)) { - resolve(ret); - } - }); - }; - } else { - win.addEventListener('message', handleMessage, false); - - client = function invokeCMPFrame(params, once = false) { - return new GreedyPromise((resolve, reject) => { - // call CMP via postMessage - const callId = Math.random().toString(); - const msg = { - [callName]: { - ...Object.fromEntries(resolveParams(params).filter(([param]) => param !== 'callback')), - callId: callId - } - }; - - cmpCallbacks[callId] = wrapCallback(params?.callback, resolve, reject, (once || params?.callback == null) && (() => { delete cmpCallbacks[callId] })); - cmpFrame.postMessage(msg, '*'); - if (mode === MODE_RETURN) resolve(); - }); - }; - } - return Object.assign(client, { - isDirect, - close() { - !isDirect && win.removeEventListener('message', handleMessage); - } - }) -} diff --git a/libraries/creativeRender/constants.js b/libraries/creativeRender/constants.js deleted file mode 100644 index 7b67f8ed5cd..00000000000 --- a/libraries/creativeRender/constants.js +++ /dev/null @@ -1,10 +0,0 @@ -import events from '../../src/constants.json'; - -export const PREBID_NATIVE = 'Prebid Native'; -export const PREBID_REQUEST = 'Prebid Request'; -export const PREBID_RESPONSE = 'Prebid Response'; -export const PREBID_EVENT = 'Prebid Event'; -export const AD_RENDER_SUCCEEDED = events.EVENTS.AD_RENDER_SUCCEEDED; -export const AD_RENDER_FAILED = events.EVENTS.AD_RENDER_FAILED; -export const NO_AD = events.AD_RENDER_FAILED_REASON.NO_AD; -export const EXCEPTION = events.AD_RENDER_FAILED_REASON.EXCEPTION; diff --git a/libraries/creativeRender/crossDomain.js b/libraries/creativeRender/crossDomain.js deleted file mode 100644 index ffa8b468f12..00000000000 --- a/libraries/creativeRender/crossDomain.js +++ /dev/null @@ -1,57 +0,0 @@ -import {mkFrame, writeAd} from './writer.js'; -import { - AD_RENDER_FAILED, - AD_RENDER_SUCCEEDED, - PREBID_EVENT, - PREBID_RESPONSE, - PREBID_REQUEST, - EXCEPTION -} from './constants.js'; - -export function renderer(win = window) { - return function ({adId, pubUrl, clickUrl}) { - const pubDomain = (function() { - const a = win.document.createElement('a'); - a.href = pubUrl; - return a.protocol + '//' + a.host; - })(); - function sendMessage(type, payload, transfer) { - win.parent.postMessage(JSON.stringify(Object.assign({message: type, adId}, payload)), pubDomain, transfer); - } - function cb(err) { - sendMessage(PREBID_EVENT, { - event: err == null ? AD_RENDER_SUCCEEDED : AD_RENDER_FAILED, - info: err - }); - } - function onMessage(ev) { - let data = {}; - try { - data = JSON.parse(ev[ev.message ? 'message' : 'data']); - } catch (e) { - return; - } - if (data.message === PREBID_RESPONSE && data.adId === adId) { - try { - let doc = win.document - if (data.ad) { - doc = mkFrame(doc, {width: data.width, height: data.height}).contentDocument; - doc.open(); - } - writeAd(data, cb, doc); - } catch (e) { - // eslint-disable-next-line standard/no-callback-literal - cb({ reason: EXCEPTION, message: e.message }) - } - } - } - - const channel = new MessageChannel(); - channel.port1.onmessage = onMessage; - sendMessage(PREBID_REQUEST, { - options: {clickUrl} - }, [channel.port2]); - win.addEventListener('message', onMessage, false); - } -} -window.renderAd = renderer(); diff --git a/libraries/creativeRender/direct.js b/libraries/creativeRender/direct.js deleted file mode 100644 index 19d34e16844..00000000000 --- a/libraries/creativeRender/direct.js +++ /dev/null @@ -1,62 +0,0 @@ -import {emitAdRenderFail, emitAdRenderSucceeded, handleRender} from '../../src/adRendering.js'; -import {writeAd} from './writer.js'; -import {auctionManager} from '../../src/auctionManager.js'; -import CONSTANTS from '../../src/constants.json'; -import {inIframe, insertElement} from '../../src/utils.js'; -import {getGlobal} from '../../src/prebidGlobal.js'; -import {EXCEPTION} from './constants.js'; - -export function renderAdDirect(doc, adId, options) { - let bid; - function cb(err) { - if (err != null) { - emitAdRenderFail(Object.assign({id: adId, bid}, err)); - } else { - emitAdRenderSucceeded({doc, bid, adId}) - } - } - function renderFn(adData) { - writeAd(adData, cb, doc); - if (doc.defaultView && doc.defaultView.frameElement) { - doc.defaultView.frameElement.width = adData.width; - doc.defaultView.frameElement.height = adData.height; - } - // 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'); - } - try { - if (!adId || !doc) { - // eslint-disable-next-line standard/no-callback-literal - cb({ - reason: CONSTANTS.AD_RENDER_FAILED_REASON.MISSING_DOC_OR_ADID, - message: `missing ${adId ? 'doc' : 'adId'}` - }); - } else { - bid = auctionManager.findBidByAdId(adId); - - if (FEATURES.VIDEO) { - // TODO: could the video module implement this as a custom renderer, rather than a special case in here? - const adUnit = bid && auctionManager.index.getAdUnit(bid); - const videoModule = getGlobal().videoModule; - if (adUnit?.video && videoModule) { - videoModule.renderBid(adUnit.video.divId, bid); - return; - } - } - - if ((doc === document && !inIframe())) { - // eslint-disable-next-line standard/no-callback-literal - cb({ - reason: CONSTANTS.AD_RENDER_FAILED_REASON.PREVENT_WRITING_ON_MAIN_DOCUMENT, - message: `renderAd was prevented from writing to the main document.` - }) - } else { - handleRender(renderFn, {adId, options: {clickUrl: options?.clickThrough}, bidResponse: bid, doc}); - } - } - } catch (e) { - // eslint-disable-next-line standard/no-callback-literal - cb({reason: EXCEPTION, message: e.message}) - } -} diff --git a/libraries/creativeRender/writer.js b/libraries/creativeRender/writer.js deleted file mode 100644 index 80bb0592a1f..00000000000 --- a/libraries/creativeRender/writer.js +++ /dev/null @@ -1,34 +0,0 @@ -import {NO_AD} from './constants.js'; - -const IFRAME_ATTRS = { - frameBorder: 0, - scrolling: 'no', - marginHeight: 0, - marginWidth: 0, - topMargin: 0, - leftMargin: 0, - allowTransparency: 'true', -}; - -export function mkFrame(doc, attrs) { - const frame = doc.createElement('iframe'); - attrs = Object.assign({}, attrs, IFRAME_ATTRS); - Object.entries(attrs).forEach(([k, v]) => frame.setAttribute(k, v)); - doc.body.appendChild(frame); - return frame; -} - -export function writeAd({ad, adUrl, width, height}, cb, doc = document) { - if (!ad && !adUrl) { - // eslint-disable-next-line standard/no-callback-literal - cb({reason: NO_AD, message: 'Missing ad markup or URL'}); - } else { - if (adUrl && !ad) { - mkFrame(doc, {width, height, src: adUrl}) - } else { - doc.write(ad); - doc.close(); - } - cb(); - } -} diff --git a/libraries/currencyUtils/currency.js b/libraries/currencyUtils/currency.js deleted file mode 100644 index 924f8f200d8..00000000000 --- a/libraries/currencyUtils/currency.js +++ /dev/null @@ -1,31 +0,0 @@ -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`. - * - * By default, when the conversion is not possible (currency module not present or - * throwing errors), the amount is returned unchanged. This behavior can be - * toggled off with bestEffort = false. - */ -export function convertCurrency(amount, fromCur, toCur, bestEffort = true) { - if (fromCur === toCur) return amount; - let result = amount; - try { - result = getGlobal().convertCurrency(amount, fromCur, toCur); - } catch (e) { - if (!bestEffort) throw e; - } - return result; -} - -export function currencyNormalizer(toCurrency = null, bestEffort = true, convert = convertCurrency) { - return function (amount, currency) { - if (toCurrency == null) toCurrency = currency; - return convert(amount, currency, toCurrency, bestEffort); - } -} - -export function currencyCompare(get = (obj) => [obj.cpm, obj.currency], normalize = currencyNormalizer()) { - return keyCompare(obj => normalize.apply(null, get(obj))) -} diff --git a/libraries/gptUtils/gptUtils.js b/libraries/gptUtils/gptUtils.js deleted file mode 100644 index 950f28c618f..00000000000 --- a/libraries/gptUtils/gptUtils.js +++ /dev/null @@ -1,37 +0,0 @@ -import {find} from '../../src/polyfill.js'; -import {compareCodeAndSlot, isGptPubadsDefined} from '../../src/utils.js'; - -/** - * Returns filter function to match adUnitCode in slot - * @param {string} adUnitCode AdUnit code - * @return {function} filter function - */ -export function isSlotMatchingAdUnitCode(adUnitCode) { - return (slot) => compareCodeAndSlot(slot, adUnitCode); -} - -/** - * @summary Uses the adUnit's code in order to find a matching gpt slot object on the page - */ -export function getGptSlotForAdUnitCode(adUnitCode) { - let matchingSlot; - if (isGptPubadsDefined()) { - // find the first matching gpt slot on the page - matchingSlot = find(window.googletag.pubads().getSlots(), isSlotMatchingAdUnitCode(adUnitCode)); - } - return matchingSlot; -} - -/** - * @summary Uses the adUnit's code in order to find a matching gptSlot on the page - */ -export function getGptSlotInfoForAdUnitCode(adUnitCode) { - const matchingSlot = getGptSlotForAdUnitCode(adUnitCode); - if (matchingSlot) { - return { - gptSlot: matchingSlot.getAdUnitPath(), - divId: matchingSlot.getSlotElementId() - }; - } - return {}; -} diff --git a/libraries/htmlEscape/htmlEscape.js b/libraries/htmlEscape/htmlEscape.js deleted file mode 100644 index f0952c02e3c..00000000000 --- a/libraries/htmlEscape/htmlEscape.js +++ /dev/null @@ -1,26 +0,0 @@ -/** - * Encode a string for inclusion in HTML. - * See https://pragmaticwebsecurity.com/articles/spasecurity/json-stringify-xss.html and - * https://codeql.github.com/codeql-query-help/javascript/js-bad-code-sanitization/ - * @return {string} - */ -export const escapeUnsafeChars = (() => { - const escapes = { - '<': '\\u003C', - '>': '\\u003E', - '/': '\\u002F', - '\\': '\\\\', - '\b': '\\b', - '\f': '\\f', - '\n': '\\n', - '\r': '\\r', - '\t': '\\t', - '\0': '\\0', - '\u2028': '\\u2028', - '\u2029': '\\u2029' - }; - - return function (str) { - return str.replace(/[<>\b\f\n\r\t\0\u2028\u2029\\]/g, x => escapes[x]); - }; -})(); diff --git a/libraries/mspa/activityControls.js b/libraries/mspa/activityControls.js deleted file mode 100644 index eaf515e2385..00000000000 --- a/libraries/mspa/activityControls.js +++ /dev/null @@ -1,127 +0,0 @@ -import {registerActivityControl} from '../../src/activities/rules.js'; -import { - ACTIVITY_ENRICH_EIDS, - ACTIVITY_ENRICH_UFPD, - ACTIVITY_SYNC_USER, - ACTIVITY_TRANSMIT_PRECISE_GEO -} from '../../src/activities/activities.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 - -const SENSITIVE_DATA_GEO = 7; - -function isApplicable(val) { - return val != null && val !== 0 -} - -export function isBasicConsentDenied(cd) { - // service provider mode is always consent denied - return ['MspaServiceProviderMode', 'Gpc'].some(prop => cd[prop] === 1) || - // you cannot consent to what you were not notified of - cd.PersonalDataConsents === 2 || - // minors 13+ who have not given consent - cd.KnownChildSensitiveDataConsents[0] === 1 || - // minors under 13 cannot consent - isApplicable(cd.KnownChildSensitiveDataConsents[1]) || - // covered cannot be zero - cd.MspaCoveredTransaction === 0; -} - -export function sensitiveNoticeIs(cd, value) { - return ['SensitiveDataProcessingOptOutNotice', 'SensitiveDataLimitUseNotice'].some(prop => cd[prop] === value) -} - -export function isConsentDenied(cd) { - return isBasicConsentDenied(cd) || - ['Sale', 'Sharing', 'TargetedAdvertising'].some(scope => { - const oo = cd[`${scope}OptOut`]; - const notice = cd[`${scope}OptOutNotice`]; - // user opted out - return oo === 1 || - // opt-out notice was not given - notice === 2 || - // do not trust CMP if it signals opt-in but no opt-out notice was given - (oo === 2 && notice === 0); - }) || - // no sharing notice was given ... - cd.SharingNotice === 2 || - // ... or the CMP says it was not applicable, while also claiming it got consent - (cd.SharingOptOut === 2 && cd.SharingNotice === 0); -} - -export const isTransmitUfpdConsentDenied = (() => { - // deny anything that smells like: genetic, biometric, state/national ID, financial, union membership, - // or personal communication data - const cannotBeInScope = [6, 7, 9, 10, 12].map(el => --el); - // require consent for everything else (except geo, which is treated separately) - const allExceptGeo = Array.from(Array(12).keys()).filter((el) => el !== SENSITIVE_DATA_GEO) - const mustHaveConsent = allExceptGeo.filter(el => !cannotBeInScope.includes(el)); - - return function (cd) { - return isConsentDenied(cd) || - // no notice about sensitive data was given - sensitiveNoticeIs(cd, 2) || - // extra-sensitive data is applicable - cannotBeInScope.some(i => isApplicable(cd.SensitiveDataProcessing[i])) || - // user opted out for not-as-sensitive data - mustHaveConsent.some(i => cd.SensitiveDataProcessing[i] === 1) || - // CMP says it has consent, but did not give notice about it - (sensitiveNoticeIs(cd, 0) && allExceptGeo.some(i => cd.SensitiveDataProcessing[i] === 2)) - } -})(); - -export function isTransmitGeoConsentDenied(cd) { - const geoConsent = cd.SensitiveDataProcessing[SENSITIVE_DATA_GEO]; - return geoConsent === 1 || - isBasicConsentDenied(cd) || - // no sensitive data notice was given - sensitiveNoticeIs(cd, 2) || - // do not trust CMP if it says it has consent for geo but didn't show a sensitive data notice - (sensitiveNoticeIs(cd, 0) && geoConsent === 2) -} - -const CONSENT_RULES = { - [ACTIVITY_SYNC_USER]: isConsentDenied, - [ACTIVITY_ENRICH_EIDS]: isConsentDenied, - [ACTIVITY_ENRICH_UFPD]: isTransmitUfpdConsentDenied, - [ACTIVITY_TRANSMIT_PRECISE_GEO]: isTransmitGeoConsentDenied -}; - -export function mspaRule(sids, getConsent, denies, applicableSids = () => gppDataHandler.getConsentData()?.applicableSections) { - return function () { - if (applicableSids().some(sid => sids.includes(sid))) { - const consent = getConsent(); - if (consent == null) { - return {allow: false, reason: 'consent data not available'}; - } - if (denies(consent)) { - return {allow: false}; - } - } - }; -} - -function flatSection(subsections) { - if (subsections == null) return subsections; - return subsections.reduceRight((subsection, consent) => { - return Object.assign(consent, subsection); - }, {}); -} - -export function setupRules(api, sids, normalizeConsent = (c) => c, rules = CONSENT_RULES, registerRule = registerActivityControl, getConsentData = () => gppDataHandler.getConsentData()) { - const unreg = []; - const ruleName = `MSPA (GPP '${api}' for section${sids.length > 1 ? 's' : ''} ${sids.join(', ')})`; - logInfo(`Enabling activity controls for ${ruleName}`) - Object.entries(rules).forEach(([activity, denies]) => { - unreg.push(registerRule(activity, ruleName, mspaRule( - sids, - () => normalizeConsent(flatSection(getConsentData()?.parsedSections?.[api])), - denies, - () => getConsentData()?.applicableSections || [] - ))); - }); - return () => unreg.forEach(ur => ur()); -} diff --git a/libraries/ortbConverter/README.md b/libraries/ortbConverter/README.md index 751971eebdc..31f56b4c754 100644 --- a/libraries/ortbConverter/README.md +++ b/libraries/ortbConverter/README.md @@ -80,7 +80,8 @@ However, there are two restrictions (to avoid them, use the [other customization ) ``` -### Fine grained customization - imp, request, bidResponse, response + +### Fine grained customization - imp, request, bidResponse, response When invoked, `toORTB({bidRequests, bidderRequest})` first loops through each request in `bidRequests`, converting them into ORTB `imp` objects. It then packages them into a single ORTB request, adding other parameters that are not imp-specific (such as for example `request.tmax`). @@ -90,7 +91,7 @@ a single return value. You can customize each of these steps using the `ortbConverter` arguments `imp`, `request`, `bidResponse` and `response`: -### Customizing imps: `imp(buildImp, bidRequest, context)` +### Customizing imps: `imp(buildImp, bidRequest, context)` Invoked once for each input `bidRequest`; should return the ORTB `imp` object to include in the request. The arguments are: @@ -100,7 +101,7 @@ The arguments are: - `context`: a [context object](#context) that contains at least: - `bidderRequest`: the `bidderRequest` argument passed to `toORTB`. -#### Example: attaching custom bid params +#### Example: attaching custom bid params ```javascript const converter = ortbConverter({ @@ -350,7 +351,7 @@ const converter = ortbConverter({ - the `context` argument of `ortbConverter`: e.g. `ortbConverter({context: {ttl: 30}})`. This will set `context.ttl = 30` globally for the converter. - the `context` argument of `toORTB`: e.g. `converter.toORTB({bidRequests, bidderRequest, context: {ttl: 30}})`. This will set `context.ttl = 30` only for this request. -### Special `context` properties +### Special `context` properties For ease of use, the conversion logic gives special meaning to some context properties: diff --git a/libraries/sizeUtils/sizeUtils.js b/libraries/sizeUtils/sizeUtils.js deleted file mode 100644 index 41cdd71df89..00000000000 --- a/libraries/sizeUtils/sizeUtils.js +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Read an adUnit object and return the sizes used in an [[728, 90]] format (even if they had [728, 90] defined) - * Preference is given to the `adUnit.mediaTypes.banner.sizes` object over the `adUnit.sizes` - * @param {object} adUnit one adUnit object from the normal list of adUnits - * @returns {Array.} array of arrays containing numeric sizes - */ -export function getAdUnitSizes(adUnit) { - if (!adUnit) { - return; - } - - let sizes = []; - if (adUnit.mediaTypes && adUnit.mediaTypes.banner && Array.isArray(adUnit.mediaTypes.banner.sizes)) { - let bannerSizes = adUnit.mediaTypes.banner.sizes; - if (Array.isArray(bannerSizes[0])) { - sizes = bannerSizes; - } else { - sizes.push(bannerSizes); - } - // TODO - remove this else block when we're ready to deprecate adUnit.sizes for bidders - } else if (Array.isArray(adUnit.sizes)) { - if (Array.isArray(adUnit.sizes[0])) { - sizes = adUnit.sizes; - } else { - sizes.push(adUnit.sizes); - } - } - return sizes; -} diff --git a/libraries/transformParamsUtils/convertTypes.js b/libraries/transformParamsUtils/convertTypes.js deleted file mode 100644 index 813d8e6e693..00000000000 --- a/libraries/transformParamsUtils/convertTypes.js +++ /dev/null @@ -1,36 +0,0 @@ -import {isFn} from '../../src/utils.js'; - -/** - * Try to convert a value to a type. - * If it can't be done, the value will be returned. - * - * @param {string} typeToConvert The target type. e.g. "string", "number", etc. - * @param {*} value The value to be converted into typeToConvert. - */ -function tryConvertType(typeToConvert, value) { - if (typeToConvert === 'string') { - return value && value.toString(); - } else if (typeToConvert === 'number') { - return Number(value); - } else { - return value; - } -} - -export function convertTypes(types, params) { - Object.keys(types).forEach(key => { - if (params[key]) { - if (isFn(types[key])) { - params[key] = types[key](params[key]); - } else { - params[key] = tryConvertType(types[key], params[key]); - } - - // don't send invalid values - if (isNaN(params[key])) { - delete params.key; - } - } - }); - return params; -} diff --git a/libraries/uid2Eids/uid2Eids.js b/libraries/uid2Eids/uid2Eids.js deleted file mode 100644 index ce4f4fa3b2a..00000000000 --- a/libraries/uid2Eids/uid2Eids.js +++ /dev/null @@ -1,14 +0,0 @@ -export const UID2_EIDS = { - 'uid2': { - source: 'uidapi.com', - atype: 3, - getValue: function(data) { - return data.id; - }, - getUidExt: function(data) { - if (data.ext) { - return data.ext; - } - } - } -} diff --git a/libraries/urlUtils/urlUtils.js b/libraries/urlUtils/urlUtils.js deleted file mode 100644 index f0c5823aab1..00000000000 --- a/libraries/urlUtils/urlUtils.js +++ /dev/null @@ -1,7 +0,0 @@ -export function tryAppendQueryString(existingUrl, key, value) { - if (value) { - return existingUrl + key + '=' + encodeURIComponent(value) + '&'; - } - - return existingUrl; -} diff --git a/modules/.submodules.json b/modules/.submodules.json index d2a13a57330..b45fb7f2303 100644 --- a/modules/.submodules.json +++ b/modules/.submodules.json @@ -46,9 +46,7 @@ "zeotapIdPlusIdSystem", "adqueryIdSystem", "gravitoIdSystem", - "freepassIdSystem", - "operaadsIdSystem", - "mygaruIdSystem" + "freepassIdSystem" ], "adpod": [ "freeWheelAdserverVideo", @@ -56,44 +54,30 @@ ], "rtdModule": [ "1plusXRtdProvider", - "a1MediaRtdProvider", "aaxBlockmeterRtdProvider", - "adlooxRtdProvider", - "adnuntiusRtdProvider", "airgridRtdProvider", "akamaiDapRtdProvider", "arcspanRtdProvider", "blueconicRtdProvider", - "brandmetricsRtdProvider", "browsiRtdProvider", "captifyRtdProvider", - "mediafilterRtdProvider", "confiantRtdProvider", "dgkeywordRtdProvider", - "experianRtdProvider", "geoedgeRtdProvider", - "geolocationRtdProvider", - "greenbidsRtdProvider", - "growthCodeRtdProvider", "hadronRtdProvider", + "haloRtdProvider", "iasRtdProvider", - "idWardRtdProvider", - "imRtdProvider", - "intersectionRtdProvider", "jwplayerRtdProvider", "medianetRtdProvider", "mgidRtdProvider", - "neuwoRtdProvider", "oneKeyRtdProvider", "optimeraRtdProvider", - "oxxionRtdProvider", "permutiveRtdProvider", - "qortexRtdProvider", "reconciliationRtdProvider", - "relevadRtdProvider", "sirdataRtdProvider", "timeoutRtdProvider", - "weboramaRtdProvider" + "weboramaRtdProvider", + "zeusPrimeRtdProvider" ], "fpdModule": [ "validationFpdModule", diff --git a/modules/33acrossAnalyticsAdapter.js b/modules/33acrossAnalyticsAdapter.js deleted file mode 100644 index e3539906b13..00000000000 --- a/modules/33acrossAnalyticsAdapter.js +++ /dev/null @@ -1,656 +0,0 @@ -import { deepAccess, logInfo, logWarn, logError, deepClone } from '../src/utils.js'; -import buildAdapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; -import adapterManager, { coppaDataHandler, gdprDataHandler, gppDataHandler, uspDataHandler } from '../src/adapterManager.js'; -import CONSTANTS from '../src/constants.json'; - -/** - * @typedef {typeof import('../src/constants.json').EVENTS} EVENTS - */ -const { EVENTS } = CONSTANTS; - -/** @typedef {'pending'|'available'|'targetingSet'|'rendered'|'timeout'|'rejected'|'noBid'|'error'} BidStatus */ -/** - * @type {Object} - */ -const BidStatus = { - PENDING: 'pending', - AVAILABLE: 'available', - TARGETING_SET: 'targetingSet', - RENDERED: 'rendered', - TIMEOUT: 'timeout', - REJECTED: 'rejected', - NOBID: 'noBid', - ERROR: 'error', -} - -const ANALYTICS_VERSION = '1.0.0'; -const PROVIDER_NAME = '33across'; -const GVLID = 58; -/** Time to wait for all transactions in an auction to complete before sending the report */ -const DEFAULT_TRANSACTION_TIMEOUT = 10000; -/** Time to wait after all GAM slots have registered before sending the report */ -export const POST_GAM_TIMEOUT = 500; -export const DEFAULT_ENDPOINT = 'https://analytics.33across.com/api/v1/event'; - -export const log = getLogger(); - -/** - * @typedef {Object} AnalyticsReport - Sent when all bids are complete (as determined by `bidWon` and `slotRenderEnded` events) - * @property {string} analyticsVersion - Version of the Prebid.js 33Across Analytics Adapter - * @property {string} pid - Partner ID - * @property {string} src - Source of the report ('pbjs') - * @property {string} pbjsVersion - Version of Prebid.js - * @property {Auction[]} auctions - */ - -/** - * @typedef {Object} AnalyticsCache - * @property {string} pid Partner ID - * @property {Object} auctions - * @property {string} [usPrivacy] - */ - -/** - * @typedef {Object} Auction - Parsed auction data - * @property {AdUnit[]} adUnits - * @property {string} auctionId - * @property {string[]} userIds - */ - -/** - * @typedef {`${number}x${number}`} AdUnitSize - */ - -/** - * @typedef {('banner'|'native'|'video')} AdUnitMediaType - */ - -/** - * @typedef {Object} BidResponse - * @property {number} cpm - * @property {string} cur - * @property {number} [cpmOrig] - * @property {number} cpmFloor - * @property {AdUnitMediaType} mediaType - * @property {AdUnitSize} size - */ - -/** - * @typedef {Object} Bid - Parsed bid data - * @property {string} bidder - * @property {string} bidId - * @property {string} source - * @property {string} status - * @property {BidResponse} [bidResponse] - * @property {1|0} [hasWon] - */ - -/** - * @typedef {Object} AdUnit - Parsed adUnit data - * @property {string} transactionId - Primary key for *this* auction/adUnit combination - * @property {string} adUnitCode - * @property {string} slotId - Equivalent to GPID. (Note that - * GPID supports adUnits where multiple units have the same `code` values - * by appending a `#UNIQUIFIER`. The value of the UNIQUIFIER is likely to be the div-id, - * but, if div-id is randomized / unavailable, may be something else like the media size) - * @property {Array} mediaTypes - * @property {Array} sizes - * @property {Array} bids - */ - -/** - * After the first transaction begins, wait until all transactions are complete - * before calling `onComplete`. If the timeout is reached before all transactions - * are complete, send the report anyway. - * - * Use this to track all transactions per auction, and send the report as soon - * as all adUnits have been won (or after timeout) even if other bid/auction - * activity is still happening. - */ -class TransactionManager { - /** - * Milliseconds between activity to allow until this collection automatically completes. - * @type {number} - */ - #sendTimeout; - #sendTimeoutId; - #transactionsPending = new Set(); - #transactionsCompleted = new Set(); - #onComplete; - - constructor({ timeout, onComplete }) { - this.#sendTimeout = timeout; - this.#onComplete = onComplete; - } - - status() { - return { - pending: [...this.#transactionsPending], - completed: [...this.#transactionsCompleted], - }; - } - - initiate(transactionId) { - this.#transactionsPending.add(transactionId); - this.#restartSendTimeout(); - } - - complete(transactionId) { - if (!this.#transactionsPending.has(transactionId)) { - log.warn(`transactionId "${transactionId}" was not found. No transaction to mark as complete.`); - return; - } - - this.#transactionsPending.delete(transactionId); - this.#transactionsCompleted.add(transactionId); - - if (this.#transactionsPending.size === 0) { - this.#flushTransactions(); - } - } - - #flushTransactions() { - this.#clearSendTimeout(); - this.#transactionsPending = new Set(); - this.#onComplete(); - } - - // gulp-eslint is using eslint 6, a version that doesn't support private method syntax - // eslint-disable-next-line no-dupe-class-members - #clearSendTimeout() { - return clearTimeout(this.#sendTimeoutId); - } - - // eslint-disable-next-line no-dupe-class-members - #restartSendTimeout() { - this.#clearSendTimeout(); - - this.#sendTimeoutId = setTimeout(() => { - if (this.#sendTimeout !== 0) { - log.warn(`Timed out waiting for ad transactions to complete. Sending report.`); - } - - this.#flushTransactions(); - }, this.#sendTimeout); - } -} - -/** - * Initialized during `enableAnalytics`. Exported for testing purposes. - */ -export const locals = { - /** @type {Object} - one manager per auction */ - transactionManagers: {}, - /** @type {AnalyticsCache} */ - cache: { - auctions: {}, - pid: '', - }, - /** @type {Object} */ - adUnitMap: {}, - reset() { - this.transactionManagers = {}; - this.cache = { - auctions: {}, - pid: '', - }; - this.adUnitMap = {}; - } -} - -/** - * @typedef {Object} AnalyticsAdapter - * @property {function} track - * @property {function} enableAnalytics - * @property {function} disableAnalytics - * @property {function} [originEnableAnalytics] - * @property {function} [originDisableAnalytics] - * @property {function} [_oldEnable] - */ - -/** - * @type {AnalyticsAdapter} - */ -const analyticsAdapter = Object.assign( - buildAdapter({ analyticsType: 'endpoint' }), - { track: analyticEventHandler } -); - -analyticsAdapter.originEnableAnalytics = analyticsAdapter.enableAnalytics; -analyticsAdapter.enableAnalytics = enableAnalyticsWrapper; - -/** - * @typedef {Object} AnalyticsConfig - * @property {string} provider - set by pbjs at module registration time - * @property {Object} options - * @property {string} options.pid - Publisher/Partner ID - * @property {string} [options.endpoint=DEFAULT_ENDPOINT] - Endpoint to send analytics data - * @property {number} [options.timeout=DEFAULT_TRANSACTION_TIMEOUT] - Timeout for sending analytics data - */ - -/** - * @param {AnalyticsConfig} config Analytics module configuration - */ -function enableAnalyticsWrapper(config) { - const { options } = config; - - const pid = options.pid; - if (!pid) { - log.error('No partnerId provided for "options.pid". No analytics will be sent.'); - - return; - } - - const endpoint = calculateEndpoint(options.endpoint); - this.getUrl = () => endpoint; - - const timeout = calculateTransactionTimeout(options.timeout); - this.getTimeout = () => timeout; - - locals.cache = { - pid, - auctions: {}, - }; - - window.googletag = window.googletag || { cmd: [] }; - window.googletag.cmd.push(subscribeToGamSlots); - - analyticsAdapter.originEnableAnalytics(config); -} - -/** - * @param {string} [endpoint] - * @returns {string} - */ -function calculateEndpoint(endpoint = DEFAULT_ENDPOINT) { - if (typeof endpoint === 'string' && endpoint.startsWith('http')) { - return endpoint; - } - - log.info(`Invalid endpoint provided for "options.endpoint". Using default endpoint.`); - - return DEFAULT_ENDPOINT; -} -/** - * @param {number} [configTimeout] - * @returns {number} Transaction Timeout - */ -function calculateTransactionTimeout(configTimeout = DEFAULT_TRANSACTION_TIMEOUT) { - if (typeof configTimeout === 'number' && configTimeout >= 0) { - return configTimeout; - } - - log.info(`Invalid timeout provided for "options.timeout". Using default timeout of ${DEFAULT_TRANSACTION_TIMEOUT}ms.`); - - return DEFAULT_TRANSACTION_TIMEOUT; -} - -function subscribeToGamSlots() { - window.googletag.pubads().addEventListener('slotRenderEnded', event => { - setTimeout(() => { - const { transactionId, auctionId } = - getAdUnitMetadata(event.slot.getAdUnitPath(), event.slot.getSlotElementId()); - if (!transactionId || !auctionId) { - const slotName = `${event.slot.getAdUnitPath()} - ${event.slot.getSlotElementId()}`; - log.warn('Could not find configured ad unit matching GAM render of slot:', { slotName }); - return; - } - - locals.transactionManagers[auctionId] && - locals.transactionManagers[auctionId].complete(transactionId); - }, POST_GAM_TIMEOUT); - }); -} - -function getAdUnitMetadata(adUnitPath, adSlotElementId) { - const adUnitMeta = locals.adUnitMap[adUnitPath] || locals.adUnitMap[adSlotElementId]; - if (adUnitMeta && adUnitMeta.length > 0) { - return adUnitMeta[adUnitMeta.length - 1]; - } - return {}; -} - -/** necessary for testing */ -analyticsAdapter.originDisableAnalytics = analyticsAdapter.disableAnalytics; -analyticsAdapter.disableAnalytics = function () { - analyticsAdapter._oldEnable = enableAnalyticsWrapper; - locals.reset(); - analyticsAdapter.originDisableAnalytics(); -}; - -adapterManager.registerAnalyticsAdapter({ - adapter: analyticsAdapter, - code: PROVIDER_NAME, - gvlid: GVLID, -}); - -export default analyticsAdapter; - -/** - * @param {AnalyticsCache} analyticsCache - * @param {string} completedAuctionId value of auctionId - * @return {AnalyticsReport} Analytics report - */ -function createReportFromCache(analyticsCache, completedAuctionId) { - const { pid, auctions } = analyticsCache; - - const report = { - pid, - src: 'pbjs', - analyticsVersion: ANALYTICS_VERSION, - pbjsVersion: '$prebid.version$', // Replaced by build script - auctions: [ auctions[completedAuctionId] ], - } - if (uspDataHandler.getConsentData()) { - report.usPrivacy = uspDataHandler.getConsentData(); - } - - if (gdprDataHandler.getConsentData()) { - report.gdpr = Number(Boolean(gdprDataHandler.getConsentData().gdprApplies)); - report.gdprConsent = gdprDataHandler.getConsentData().consentString || ''; - } - - if (gppDataHandler.getConsentData()) { - report.gpp = gppDataHandler.getConsentData().gppString; - report.gppSid = gppDataHandler.getConsentData().applicableSections; - } - - if (coppaDataHandler.getCoppa()) { - report.coppa = Number(coppaDataHandler.getCoppa()); - } - - return report; -} - -function getCachedBid(auctionId, bidId) { - const auction = locals.cache.auctions[auctionId]; - for (let adUnit of auction.adUnits) { - for (let bid of adUnit.bids) { - if (bid.bidId === bidId) { - return bid; - } - } - } - log.error(`Cannot find bid "${bidId}" in auction "${auctionId}".`); -}; - -/** - * @param {Object} args - * @param {Object} args.args Event data - * @param {EVENTS[keyof EVENTS]} args.eventType - */ -function analyticEventHandler({ eventType, args }) { - if (!locals.cache) { - log.error('Something went wrong. Analytics cache is not initialized.'); - return; - } - - switch (eventType) { - case EVENTS.AUCTION_INIT: - onAuctionInit(args); - break; - case EVENTS.BID_REQUESTED: // BidStatus.PENDING - onBidRequested(args); - break; - case EVENTS.BID_TIMEOUT: - for (let bid of args) { - setCachedBidStatus(bid.auctionId, bid.bidId, BidStatus.TIMEOUT); - } - break; - case EVENTS.BID_RESPONSE: - onBidResponse(args); - break; - case EVENTS.BID_REJECTED: - onBidRejected(args); - break; - case EVENTS.NO_BID: - case EVENTS.SEAT_NON_BID: - setCachedBidStatus(args.auctionId, args.bidId, BidStatus.NOBID); - break; - case EVENTS.BIDDER_ERROR: - if (args.bidderRequest && args.bidderRequest.bids) { - for (let bid of args.bidderRequest.bids) { - setCachedBidStatus(args.bidderRequest.auctionId, bid.bidId, BidStatus.ERROR); - } - } - break; - case EVENTS.AUCTION_END: - onAuctionEnd(args); - break; - case EVENTS.BID_WON: // BidStatus.TARGETING_SET | BidStatus.RENDERED | BidStatus.ERROR - onBidWon(args); - break; - default: - break; - } -} - -/**************** - * AUCTION_INIT * - ***************/ -function onAuctionInit({ adUnits, auctionId, bidderRequests }) { - if (typeof auctionId !== 'string' || !Array.isArray(bidderRequests)) { - log.error('Analytics adapter failed to parse auction.'); - return; - } - - locals.cache.auctions[auctionId] = { - auctionId, - adUnits: adUnits.map(au => { - setAdUnitMap(au.code, auctionId, au.transactionId); - - return { - transactionId: au.transactionId, - adUnitCode: au.code, - // Note: GPID supports adUnits that have matching `code` values by appending a `#UNIQUIFIER`. - // The value of the UNIQUIFIER is likely to be the div-id, - // but, if div-id is randomized / unavailable, may be something else like the media size) - slotId: deepAccess(au, 'ortb2Imp.ext.gpid') || deepAccess(au, 'ortb2Imp.ext.data.pbadslot', au.code), - mediaTypes: Object.keys(au.mediaTypes), - sizes: au.sizes.map(size => size.join('x')), - bids: [], - } - }), - userIds: Object.keys(deepAccess(bidderRequests, '0.bids.0.userId', {})), - }; - - locals.transactionManagers[auctionId] ||= - new TransactionManager({ - timeout: analyticsAdapter.getTimeout(), - onComplete() { - sendReport( - createReportFromCache(locals.cache, auctionId), - analyticsAdapter.getUrl() - ); - delete locals.transactionManagers[auctionId]; - } - }); -} - -function setAdUnitMap(adUnitCode, auctionId, transactionId) { - if (!locals.adUnitMap[adUnitCode]) { - locals.adUnitMap[adUnitCode] = []; - } - - locals.adUnitMap[adUnitCode].push({ auctionId, transactionId }); -} - -/***************** - * BID_REQUESTED * - ****************/ -function onBidRequested({ auctionId, bids }) { - for (let { bidder, bidId, transactionId, src } of bids) { - const auction = locals.cache.auctions[auctionId]; - const adUnit = auction.adUnits.find(adUnit => adUnit.transactionId === transactionId); - if (!adUnit) return; - adUnit.bids.push({ - bidder, - bidId, - status: BidStatus.PENDING, - hasWon: 0, - source: src, - }); - - // if there is no manager for this auction, then the auction has already been completed - locals.transactionManagers[auctionId] && - locals.transactionManagers[auctionId].initiate(transactionId); - } -} - -/**************** - * BID_RESPONSE * - ***************/ -function onBidResponse({ requestId, auctionId, cpm, currency, originalCpm, floorData, mediaType, size, status, source }) { - const bid = getCachedBid(auctionId, requestId); - if (!bid) return; - - setBidStatus(bid, status); - Object.assign(bid, - { - bidResponse: { - cpm, - cur: currency, - cpmOrig: originalCpm, - cpmFloor: floorData?.floorValue, - mediaType, - size - }, - source - } - ); -} - -/**************** - * BID_REJECTED * - ***************/ -function onBidRejected({ requestId, auctionId, cpm, currency, originalCpm, floorData, mediaType, width, height, source }) { - const bid = getCachedBid(auctionId, requestId); - if (!bid) return; - - setBidStatus(bid, BidStatus.REJECTED); - Object.assign(bid, - { - bidResponse: { - cpm, - cur: currency, - cpmOrig: originalCpm, - cpmFloor: floorData?.floorValue, - mediaType, - size: `${width}x${height}` - }, - source - } - ); -} - -/*************** - * AUCTION_END * - **************/ -/** - * @param {Object} args - * @param {{requestId: string, status: string}[]} args.bidsReceived - * @param {string} args.auctionId - * @returns {void} - */ -function onAuctionEnd({ bidsReceived, auctionId }) { - for (let bid of bidsReceived) { - setCachedBidStatus(auctionId, bid.requestId, bid.status); - } -} - -/*********** - * BID_WON * - **********/ -function onBidWon(bidWon) { - const { auctionId, requestId, transactionId } = bidWon; - const bid = getCachedBid(auctionId, requestId); - if (!bid) { - return; - } - - setBidStatus(bid, bidWon.status ?? BidStatus.ERROR); - - locals.transactionManagers[auctionId] && - locals.transactionManagers[auctionId].complete(transactionId); -} - -/** - * @param {Bid} bid - * @param {BidStatus} [status] - * @returns {void} - */ -function setBidStatus(bid, status = BidStatus.AVAILABLE) { - const statusStates = { - pending: { - next: [BidStatus.AVAILABLE, BidStatus.TARGETING_SET, BidStatus.RENDERED, BidStatus.TIMEOUT, BidStatus.REJECTED, BidStatus.NOBID, BidStatus.ERROR], - }, - available: { - next: [BidStatus.TARGETING_SET, BidStatus.RENDERED, BidStatus.TIMEOUT, BidStatus.REJECTED, BidStatus.NOBID, BidStatus.ERROR], - }, - targetingSet: { - next: [BidStatus.RENDERED, BidStatus.ERROR, BidStatus.TIMEOUT], - }, - rendered: { - next: [], - }, - timeout: { - next: [], - }, - rejected: { - next: [], - }, - noBid: { - next: [], - }, - error: { - next: [BidStatus.TARGETING_SET, BidStatus.RENDERED, BidStatus.TIMEOUT, BidStatus.REJECTED, BidStatus.NOBID, BidStatus.ERROR], - }, - } - - const winningStatuses = [BidStatus.RENDERED]; - - if (statusStates[bid.status].next.includes(status)) { - bid.status = status; - if (winningStatuses.includes(status)) { - // occassionally we can detect a bidWon before prebid reports it as such - bid.hasWon = 1; - } - } -} - -function setCachedBidStatus(auctionId, bidId, status) { - const bid = getCachedBid(auctionId, bidId); - if (!bid) return; - setBidStatus(bid, status); -} - -/** - * Guarantees sending of data without waiting for response, even after page is left/closed - * - * @param {AnalyticsReport} report Request payload - * @param {string} endpoint URL - */ -function sendReport(report, endpoint) { - if (navigator.sendBeacon(endpoint, JSON.stringify(report))) { - log.info(`Analytics report sent to ${endpoint}`, report); - - return; - } - - log.error('Analytics report exceeded User-Agent data limits and was not sent.', report); -} - -/** - * Encapsulate certain logger functions and add a prefix to the final messages. - * - * @return {Object} New logger functions - */ -function getLogger() { - const LPREFIX = `${PROVIDER_NAME} Analytics: `; - - return { - info: (msg, ...args) => logInfo(`${LPREFIX}${msg}`, ...deepClone(args)), - warn: (msg, ...args) => logWarn(`${LPREFIX}${msg}`, ...deepClone(args)), - error: (msg, ...args) => logError(`${LPREFIX}${msg}`, ...deepClone(args)), - } -} diff --git a/modules/33acrossAnalyticsAdapter.md b/modules/33acrossAnalyticsAdapter.md deleted file mode 100644 index c56059e5526..00000000000 --- a/modules/33acrossAnalyticsAdapter.md +++ /dev/null @@ -1,76 +0,0 @@ -# Overview - -```txt -Module Name: 33Across Analytics Adapter -Module Type: Analytics Adapter -Maintainer: analytics_support@33across.com -``` - -#### About - -This analytics adapter collects data about the performance of your ad slots -for each auction run on your site. It also provides insight into how identifiers -from the -[33Across User ID Sub-module](https://docs.prebid.org/dev-docs/modules/userid-submodules/33across.html) -and other user ID sub-modules improve your monetization. The data is sent at -the earliest opportunity for each auction to provide a more complete picture of -your ad performance. - -The analytics adapter is free to use! -However, the publisher must work with our account management team to obtain a -Publisher/Partner ID (PID) and enable Analytics for their account. -To get a PID and to have the publisher account enabled for Analytics, -you can reach out to our team at the following email - analytics_support@33across.com - -If you are an existing publisher and you already use a 33Across PID, -you can reach out to analytics_support@33across.com -to have your account enabled for analytics. - -The 33Across privacy policy is at . - -#### Analytics Options - -| Name | Scope | Example | Type | Description | -|-----------|----------|---------|----------|-------------| -| `pid` | required | abc123 | `string` | 33Across Publisher ID | -| `timeout` | optional | 10000 | `int` | Milliseconds to wait after last seen auction transaction before sending report (default 10000). | - -#### Configuration - -The data is sent at the earliest opportunity for each auction to provide -a more complete picture of your ad performance, even if the auction is interrupted -by a page navigation. At the latest, the adapter will always send the report -when the page is unloaded, at the end of the auction, or after the timeout, -whichever comes first. - -In order to guarantee consistent reports of your ad slot behavior, we recommend -including the GPT Pre-Auction Module, `gptPreAuction`. This module is included -by default when Prebid is downloaded. If you are compiling from source, -this might look something like: - -```sh -gulp bundle --modules=gptPreAuction,consentManagement,consentManagementGpp,consentManagementUsp,enrichmentFpdModule,gdprEnforcement,33acrossBidAdapter,33acrossIdSystem,33acrossAnalyticsAdapter -``` - -Enable the 33Across Analytics Adapter in Prebid.js using the analytics provider `33across` -and options as seen in the example below. - -#### Example Configuration - -```js -pbjs.enableAnalytics({ - provider: '33across', - options: { - /** - * The 33Across Publisher ID. - */ - pid: 'abc123', - /** - * Timeout in milliseconds after which an auction report - * will be sent regardless of auction state. - * [optional] - */ - timeout: 10000 - } -}); -``` diff --git a/modules/33acrossBidAdapter.js b/modules/33acrossBidAdapter.js index 0e9beb22013..26fc0a11fd5 100644 --- a/modules/33acrossBidAdapter.js +++ b/modules/33acrossBidAdapter.js @@ -6,6 +6,7 @@ import { getWindowTop, isArray, isGptPubadsDefined, + isSlotMatchingAdUnitCode, logInfo, logWarn, mergeDeep, @@ -13,7 +14,6 @@ import { uniques } from '../src/utils.js'; import {BANNER, VIDEO} from '../src/mediaTypes.js'; -import {isSlotMatchingAdUnitCode} from '../libraries/gptUtils/gptUtils.js'; // **************************** UTILS *************************** // const BIDDER_CODE = '33across'; @@ -167,7 +167,6 @@ function buildRequests(bidRequests, bidderRequest) { ttxSettings, gdprConsent, uspConsent, - gppConsent, pageUrl, referer } = _buildRequestParams(bidRequests, bidderRequest); @@ -182,7 +181,6 @@ function buildRequests(bidRequests, bidderRequest) { bidRequests: groupedRequests[key], gdprConsent, uspConsent, - gppConsent, pageUrl, referer, ttxSettings, @@ -202,15 +200,20 @@ function _buildRequestParams(bidRequests, bidderRequest) { gdprApplies: false }, bidderRequest && bidderRequest.gdprConsent); + const uspConsent = bidderRequest && bidderRequest.uspConsent; + + const pageUrl = bidderRequest?.refererInfo?.page; + + const referer = bidderRequest?.refererInfo?.ref; + adapterState.uniqueSiteIds = bidRequests.map(req => req.params.siteId).filter(uniques); return { ttxSettings, gdprConsent, - uspConsent: bidderRequest?.uspConsent, - gppConsent: bidderRequest?.gppConsent, - pageUrl: bidderRequest?.refererInfo?.page, - referer: bidderRequest?.refererInfo?.ref + uspConsent, + pageUrl, + referer } } @@ -244,11 +247,10 @@ function _getMRAKey(bidRequest) { } // Infer the necessary data from valid bid for a minimal ttxRequest and create HTTP request -function _createServerRequest({ bidRequests, gdprConsent = {}, uspConsent, gppConsent = {}, pageUrl, referer, ttxSettings, bidderRequest }) { +function _createServerRequest({ bidRequests, gdprConsent = {}, uspConsent, pageUrl, referer, ttxSettings, bidderRequest }) { const ttxRequest = {}; const firstBidRequest = bidRequests[0]; const { siteId, test } = firstBidRequest.params; - const coppaValue = config.getConfig('coppa'); /* * Infer data for the request payload @@ -294,17 +296,6 @@ function _createServerRequest({ bidRequests, gdprConsent = {}, uspConsent, gppCo }); } - if (gppConsent.gppString) { - Object.assign(ttxRequest.regs, { - 'gpp': gppConsent.gppString, - 'gpp_sid': gppConsent.applicableSections - }); - } - - if (coppaValue !== undefined) { - ttxRequest.regs.coppa = Number(!!coppaValue); - } - ttxRequest.ext = { ttx: { prebidStartedAt: Date.now(), @@ -726,10 +717,10 @@ function _createBidResponse(bid, cur) { // Else no syncs // For logic on how we handle gdpr data see _createSyncs and module's unit tests // '33acrossBidAdapter#getUserSyncs' -function getUserSyncs(syncOptions, responses, gdprConsent, uspConsent, gppConsent) { +function getUserSyncs(syncOptions, responses, gdprConsent, uspConsent) { const syncUrls = ( (syncOptions.iframeEnabled) - ? adapterState.uniqueSiteIds.map((siteId) => _createSync({ gdprConsent, uspConsent, gppConsent, siteId })) + ? adapterState.uniqueSiteIds.map((siteId) => _createSync({ gdprConsent, uspConsent, siteId })) : ([]) ); @@ -740,16 +731,15 @@ function getUserSyncs(syncOptions, responses, gdprConsent, uspConsent, gppConsen } // Sync object will always be of type iframe for TTX -function _createSync({ siteId = 'zzz000000000003zzz', gdprConsent = {}, uspConsent, gppConsent = {} }) { +function _createSync({ siteId = 'zzz000000000003zzz', gdprConsent = {}, uspConsent }) { const ttxSettings = config.getConfig('ttxSettings'); const syncUrl = (ttxSettings && ttxSettings.syncUrl) || SYNC_ENDPOINT; const { consentString, gdprApplies } = gdprConsent; - const { gppString = '', applicableSections = [] } = gppConsent; const sync = { type: 'iframe', - url: `${syncUrl}&id=${siteId}&gdpr_consent=${encodeURIComponent(consentString)}&us_privacy=${encodeURIComponent(uspConsent)}&gpp=${encodeURIComponent(gppString)}&gpp_sid=${encodeURIComponent(applicableSections.join(','))}` + url: `${syncUrl}&id=${siteId}&gdpr_consent=${encodeURIComponent(consentString)}&us_privacy=${encodeURIComponent(uspConsent)}` }; if (typeof gdprApplies === 'boolean') { diff --git a/modules/33acrossIdSystem.js b/modules/33acrossIdSystem.js index 0cb1b1f3382..be81b26b110 100644 --- a/modules/33acrossIdSystem.js +++ b/modules/33acrossIdSystem.js @@ -8,13 +8,7 @@ import { logMessage, logError } from '../src/utils.js'; import { ajaxBuilder } from '../src/ajax.js'; import { submodule } from '../src/hook.js'; -import { uspDataHandler, coppaDataHandler, gppDataHandler } from '../src/adapterManager.js'; - -/** - * @typedef {import('../modules/userId/index.js').Submodule} Submodule - * @typedef {import('../modules/userId/index.js').SubmoduleConfig} SubmoduleConfig - * @typedef {import('../modules/userId/index.js').IdResponse} IdResponse - */ +import { uspDataHandler } from '../src/adapterManager.js'; const MODULE_NAME = '33acrossId'; const API_URL = 'https://lexicon.33across.com/v1/envelope'; @@ -43,30 +37,19 @@ function getEnvelope(response) { function calculateQueryStringParams(pid, gdprConsentData) { const uspString = uspDataHandler.getConsentData(); const gdprApplies = Boolean(gdprConsentData?.gdprApplies); - const coppaValue = coppaDataHandler.getCoppa(); - const gppConsent = gppDataHandler.getConsentData(); - const params = { pid, gdpr: Number(gdprApplies), src: CALLER_NAME, - ver: '$prebid.version$', - coppa: Number(coppaValue) + ver: '$prebid.version$' }; if (uspString) { params.us_privacy = uspString; } - if (gppConsent) { - const { gppString = '', applicableSections = [] } = gppConsent; - - params.gpp = gppString; - params.gpp_sid = encodeURIComponent(applicableSections.join(',')) - } - - if (gdprConsentData?.consentString) { - params.gdpr_consent = gdprConsentData.consentString; + if (gdprApplies) { + params.gdpr_consent = gdprConsentData.consentString || ''; } return params; @@ -132,15 +115,6 @@ export const thirthyThreeAcrossIdSubmodule = { }, calculateQueryStringParams(pid, gdprConsentData), { method: 'GET', withCredentials: true }); } }; - }, - eids: { - '33acrossId': { - source: '33across.com', - atype: 1, - getValue: function(data) { - return data.envelope; - } - }, } }; diff --git a/modules/a1MediaBidAdapter.js b/modules/a1MediaBidAdapter.js deleted file mode 100644 index d640bbfe2d7..00000000000 --- a/modules/a1MediaBidAdapter.js +++ /dev/null @@ -1,104 +0,0 @@ -import { ortbConverter } from '../libraries/ortbConverter/converter.js'; -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { BANNER, VIDEO, NATIVE } from '../src/mediaTypes.js'; -import { replaceAuctionPrice } from '../src/utils.js'; - -const BIDDER_CODE = 'a1media'; -const END_POINT = 'https://d11.contentsfeed.com/dsp/breq/a1'; -const DEFAULT_CURRENCY = 'JPY'; - -const converter = ortbConverter({ - context: { - netRevenue: true, - ttl: 30, - }, - imp(buildImp, bidRequest, context) { - const imp = buildImp(bidRequest, context); - if (!imp.bidfloor) { - imp.bidfloor = bidRequest.params.bidfloor || 0; - imp.bidfloorcur = bidRequest.params.currency || DEFAULT_CURRENCY; - } - if (bidRequest.params.battr) { - Object.keys(bidRequest.mediaTypes).forEach(mType => { - imp[mType].battr = bidRequest.params.battr; - }) - } - return imp; - }, - request(buildRequest, imps, bidderRequest, context) { - const request = buildRequest(imps, bidderRequest, context); - const bid = context.bidRequests[0]; - if (!request.cur) { - request.cur = [bid.params.currency || DEFAULT_CURRENCY]; - } - if (bid.params.bcat) { - request.bcat = bid.params.bcat; - } - return request; - }, - bidResponse(buildBidResponse, bid, context) { - const { bidRequest } = context; - - let resMediaType; - const reqMediaTypes = Object.keys(bidRequest.mediaTypes); - if (reqMediaTypes.length === 1) { - resMediaType = reqMediaTypes[0]; - } else { - if (bid.adm.search(/^(<\?xml| { - const parsedBid = seatbidItem.bid.map((bidItem) => ({ - ...bidItem, - adm: replaceAuctionPrice(bidItem.adm, bidItem.price), - nurl: replaceAuctionPrice(bidItem.nurl, bidItem.price) - })); - return {...seatbidItem, bid: parsedBid}; - }); - - const responseBody = {...serverResponse.body, seatbid: parsedSeatbid}; - const bids = converter.fromORTB({ - response: responseBody, - request: bidRequest.data, - }).bids; - return bids; - }, - -}; -registerBidder(spec); diff --git a/modules/a1MediaBidAdapter.md b/modules/a1MediaBidAdapter.md deleted file mode 100644 index 304b7e1bb5a..00000000000 --- a/modules/a1MediaBidAdapter.md +++ /dev/null @@ -1,93 +0,0 @@ -# Overview - -```markdown -Module Name: A1Media Bid Adapter -Module Type: Bidder Adapter -Maintainer: dev@a1mediagroup.co.kr -``` - -# Description - -Connects to A1Media exchange for bids. - -# Test Parameters - -## Sample Banner Ad Unit - -```javascript -var adUnits = [ - { - code: 'test-div', - mediaTypes: { - banner: { - sizes: [[320, 100]], - } - }, - bids: [ - { - bidder: "a1media", - params: { - bidfloor: 0.9, //optional - currency: 'JPY' //optional - battr: [ 13 ], //optional - bcat: ['IAB1-1'] //optional - } - } - ] - } -] -``` - -## Sample Video Ad Unit - -```javascript -var adUnits = [ - { - code: 'test-div', - mediaTypes: { - video: { - mimes: ['video/mp4'], - } - }, - bids: [ - { - bidder: "a1media", - params: { - bidfloor: 0.9, //optional - currency: 'JPY' //optional - battr: [ 13 ], //optional - bcat: ['IAB1-1'] //optional - } - } - ] - } -] -``` - -## Sample Native Ad Unit - -```javascript -var adUnits = [ - { - code: 'test-div', - mediaTypes: { - native: { - title: { - len: 140 - }, - } - }, - bids: [ - { - bidder: "a1media", - params: { - bidfloor: 0.9, //optional - currency: 'JPY' //optional - battr: [ 13 ], //optional - bcat: ['IAB1-1'] //optional - } - } - ] - } -] -``` diff --git a/modules/a1MediaRtdProvider.js b/modules/a1MediaRtdProvider.js deleted file mode 100644 index 445ed47181d..00000000000 --- a/modules/a1MediaRtdProvider.js +++ /dev/null @@ -1,96 +0,0 @@ -import { MODULE_TYPE_RTD } from '../src/activities/modules.js'; -import { loadExternalScript } from '../src/adloader.js'; -import { submodule } from '../src/hook.js'; -import { getStorageManager } from '../src/storageManager.js'; -import { isEmptyStr, mergeDeep } from '../src/utils.js'; - -/** - * @typedef {import('../modules/rtdModule/index.js').RtdSubmodule} RtdSubmodule - */ - -const REAL_TIME_MODULE = 'realTimeData'; -const MODULE_NAME = 'a1Media'; -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}); - -/** @type {RtdSubmodule} */ -export const subModuleObj = { - name: MODULE_NAME, - init: init, - getBidRequestData: alterBidRequests, -}; - -export function getStorageData(key) { - let storageValue = ''; - if (storage.getDataFromLocalStorage(key)) { - storageValue = storage.getDataFromLocalStorage(key); - } else if (storage.getCookie(key)) { - storageValue = storage.getCookie(key); - } - return storageValue; -} - -function loadLbScript(tagname) { - const linkback = window.linkback = window.linkback || {}; - if (!linkback.l) { - linkback.l = true; - - const scriptUrl = `${SCRIPT_URL}/${tagname}`; - loadExternalScript(scriptUrl, MODULE_NAME); - } -} - -function init(config, userConsent) { - const tagId = config.params.tagId; - if (tagId && !isEmptyStr(tagId)) { - loadLbScript(config.params.tagId); - return true; - } - if (!isEmptyStr(getStorageData(A1_SEG_KEY))) { - return true; - } - return false; -} - -function alterBidRequests(reqBidsConfigObj, callback, config, userConsent) { - const a1seg = getStorageData(A1_SEG_KEY); - const a1gid = getStorageData(A1_AUD_KEY); - - const a1UserSegData = { - name: 'a1mediagroup.com', - ext: { - segtax: 900 - }, - segment: a1seg.split(',').map(x => ({id: x})) - }; - - const a1UserEid = { - source: 'a1mediagroup.com', - uids: [ - { - id: a1gid, - atype: 1 - } - ] - }; - - const a1Ortb2 = { - user: { - data: [ - a1UserSegData - ], - ext: { - eids: [ - a1UserEid - ] - } - } - }; - mergeDeep(reqBidsConfigObj.ortb2Fragments.global, a1Ortb2); - callback(); -} - -submodule(REAL_TIME_MODULE, subModuleObj); diff --git a/modules/a1MediaRtdProvider.md b/modules/a1MediaRtdProvider.md deleted file mode 100644 index ab2077ebbbb..00000000000 --- a/modules/a1MediaRtdProvider.md +++ /dev/null @@ -1,46 +0,0 @@ -# Overview - -Module Name: A1Media Rtd Provider -Module Type: Rtd Provider -Maintainer: dev@a1mediagroup.co.kr - -# Description - -This module loads external code using the passed parameter (params.tagId). - -The A1Media RTD module loads A1Media script for obtains user segments, and provides user segment data to bid-requests.
-to get user segments, you will need a1media script customized for site. - -To use this module, you’ll need to work with [A1MediaGroup](https://www.a1mediagroup.com/) to get an account and receive instructions on how to set up your pages and ad server. - -Contact dev@a1mediagroup.co.kr for information. - -### Integration - -1) Build the A1Media RTD Module into the Prebid.js package with: - -``` -gulp build --modules=a1MediaRtdProvider,... -``` - -2) Use `setConfig` to instruct Prebid.js to initilaize the A1Media RTD module, as specified below. - -### Configuration - -```javascript -pbjs.setConfig({ - realTimeData: { - auctionDelay: 1000, - dataProviders: [ - { - name: "a1Media", - waitForIt: true, - params: { - // 'tagId' is unique value for each account. - tagId: 'lb4test' - } - } - ] - } -}); -``` diff --git a/modules/ablidaBidAdapter.js b/modules/ablidaBidAdapter.js index 175d5ff7c72..805a2020fb4 100644 --- a/modules/ablidaBidAdapter.js +++ b/modules/ablidaBidAdapter.js @@ -3,12 +3,6 @@ import {registerBidder} from '../src/adapters/bidderFactory.js'; import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js'; import {convertOrtbRequestToProprietaryNative} from '../src/native.js'; -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse - */ - const BIDDER_CODE = 'ablida'; const ENDPOINT_URL = 'https://bidder.ablida.net/prebid'; diff --git a/modules/acuityadsBidAdapter.js b/modules/acuityAdsBidAdapter.js similarity index 94% rename from modules/acuityadsBidAdapter.js rename to modules/acuityAdsBidAdapter.js index 5b12eb2133b..b0bb132ddae 100644 --- a/modules/acuityadsBidAdapter.js +++ b/modules/acuityAdsBidAdapter.js @@ -153,15 +153,6 @@ export const spec = { tmax: bidderRequest.timeout }; - // Add GPP consent - if (bidderRequest.gppConsent) { - request.gpp = bidderRequest.gppConsent.gppString; - request.gpp_sid = bidderRequest.gppConsent.applicableSections; - } else if (bidderRequest.ortb2?.regs?.gpp) { - request.gpp = bidderRequest.ortb2.regs.gpp; - request.gpp_sid = bidderRequest.ortb2.regs.gpp_sid; - } - const len = validBidRequests.length; for (let i = 0; i < len; i++) { const bid = validBidRequests[i]; diff --git a/modules/acuityadsBidAdapter.md b/modules/acuityAdsBidAdapter.md similarity index 98% rename from modules/acuityadsBidAdapter.md rename to modules/acuityAdsBidAdapter.md index 7f001cd9376..a19e0a6b0ba 100644 --- a/modules/acuityadsBidAdapter.md +++ b/modules/acuityAdsBidAdapter.md @@ -3,7 +3,7 @@ ``` Module Name: AcuityAds Bidder Adapter Module Type: AcuityAds Bidder Adapter -Maintainer: rafi.babler@acuityads.com +Maintainer: sa-support@brightcom.com ``` # Description diff --git a/modules/ad2ictionBidAdapter.js b/modules/ad2ictionBidAdapter.js deleted file mode 100644 index 0f7cea45d14..00000000000 --- a/modules/ad2ictionBidAdapter.js +++ /dev/null @@ -1,59 +0,0 @@ -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { BANNER } from '../src/mediaTypes.js'; -import { getStorageManager } from '../src/storageManager.js'; - -export const BIDDER_CODE = 'ad2iction'; -export const SUPPORTED_AD_TYPES = [BANNER]; -export const API_ENDPOINT = 'https://ads.ad2iction.com/html/prebid/'; -export const API_VERSION_NUMBER = 3; -export const COOKIE_NAME = 'ad2udid'; - -export const storage = getStorageManager({ bidderCode: BIDDER_CODE }); - -export const spec = { - code: BIDDER_CODE, - aliases: ['ad2'], - supportedMediaTypes: SUPPORTED_AD_TYPES, - isBidRequestValid: (bid) => { - return !!bid.params.id && typeof bid.params.id === 'string'; - }, - buildRequests: (validBidRequests, bidderRequest) => { - const ids = validBidRequests.map((bid) => { - return { bannerId: bid.params.id, bidId: bid.bidId }; - }); - - const options = { - contentType: 'application/json', - withCredentials: false, - }; - - const udid = storage.cookiesAreEnabled() && storage.getCookie(COOKIE_NAME); - - const data = { - ids: JSON.stringify(ids), - ortb2: bidderRequest.ortb2, - refererInfo: bidderRequest.refererInfo, - v: API_VERSION_NUMBER, - udid: udid || '', - _: Math.round(new Date().getTime()), - }; - - return { - method: 'POST', - url: API_ENDPOINT, - data, - options, - }; - }, - interpretResponse: (serverResponse, bidRequest) => { - if (!Array.isArray(serverResponse.body)) { - return []; - } - - const bidResponses = serverResponse.body; - - return bidResponses; - }, -}; - -registerBidder(spec); diff --git a/modules/ad2ictionBidAdapter.md b/modules/ad2ictionBidAdapter.md deleted file mode 100644 index 47e355aa795..00000000000 --- a/modules/ad2ictionBidAdapter.md +++ /dev/null @@ -1,30 +0,0 @@ -# Overview - -**Module Name**: Ad2iction Bidder Adapter -**Module Type**: Bidder Adapter -**Maintainer**: prebid@ad2iction.com - -# Description - -The Ad2iction Bidding adapter requires setup before beginning. Please contact us on https://www.ad2iction.com. - -# Sample Ad Unit Config -``` -var adUnits = [ - // Banner adUnit - { - code: 'banner-div', - mediaTypes: { - banner: { - sizes: [[300, 250], [336, 280]] - } - }, - bids: [{ - bidder: 'ad2iction', - params: { - id: 'accepted-uuid' - } - }] - } -]; -``` diff --git a/modules/adWMGBidAdapter.js b/modules/adWMGBidAdapter.js index d268c4cafa8..36935e80d3b 100644 --- a/modules/adWMGBidAdapter.js +++ b/modules/adWMGBidAdapter.js @@ -1,9 +1,9 @@ 'use strict'; +import { tryAppendQueryString } from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { config } from '../src/config.js'; import { BANNER } from '../src/mediaTypes.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 de0aa1cb5d7..96f3f089cbd 100644 --- a/modules/adagioAnalyticsAdapter.js +++ b/modules/adagioAnalyticsAdapter.js @@ -5,50 +5,16 @@ import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; import CONSTANTS from '../src/constants.json'; -import { ajax } from '../src/ajax.js'; -import { BANNER } from '../src/mediaTypes.js'; -import { getWindowTop, getWindowSelf, deepAccess, logInfo, logError } from '../src/utils.js'; -import { getGlobal } from '../src/prebidGlobal.js'; +import { getWindowTop } from '../src/utils.js'; const emptyUrl = ''; const analyticsType = 'endpoint'; const events = Object.keys(CONSTANTS.EVENTS).map(key => CONSTANTS.EVENTS[key]); -const ADAGIO_GVLID = 617; -const VERSION = '3.0.0'; -const PREBID_VERSION = '$prebid.version$'; -const ENDPOINT = 'https://c.4dex.io/pba.gif'; -const cache = { - auctions: {}, - getAuction: function(auctionId, adUnitCode) { - return this.auctions[auctionId][adUnitCode]; - }, - getBiddersFromAuction: function(auctionId, adUnitCode) { - return this.getAuction(auctionId, adUnitCode).bdrs.split(','); - }, - getAllAdUnitCodes: function(auctionId) { - return Object.keys(this.auctions[auctionId]); - }, - updateAuction: function(auctionId, adUnitCode, values) { - this.auctions[auctionId][adUnitCode] = { - ...this.auctions[auctionId][adUnitCode], - ...values - }; - }, +const VERSION = '2.0.0'; - // Map prebid auction id to adagio auction id - auctionIdReferences: {}, - addPrebidAuctionIdRef(auctionId, adagioAuctionId) { - this.auctionIdReferences[auctionId] = adagioAuctionId; - }, - getAdagioAuctionId(auctionId) { - return this.auctionIdReferences[auctionId]; - } -}; -const enc = window.encodeURIComponent; - -/** -/* BEGIN ADAGIO.JS CODE - */ +const adagioEnqueue = function adagioEnqueue(action, data) { + getWindowTop().ADAGIO.queue.push({ action, data, ts: Date.now() }); +} function canAccessTopWindow() { try { @@ -58,356 +24,12 @@ function canAccessTopWindow() { } catch (error) { return false; } -}; - -function getCurrentWindow() { - return currentWindow; -}; - -let currentWindow; - -const adagioEnqueue = function adagioEnqueue(action, data) { - getCurrentWindow().ADAGIO.queue.push({ action, data, ts: Date.now() }); -}; - -/** - * END ADAGIO.JS CODE - */ - -/** - * UTILS FUNCTIONS - */ - -const guard = { - adagio: (value) => isAdagio(value), - bidTracked: (auctionId, adUnitCode) => deepAccess(cache, `auctions.${auctionId}.${adUnitCode}`, false), - auctionTracked: (auctionId) => deepAccess(cache, `auctions.${auctionId}`, false) -}; - -function removeDuplicates(arr, getKey) { - const seen = {}; - return arr.filter(item => { - const key = getKey(item); - return seen.hasOwnProperty(key) ? false : (seen[key] = true); - }); -}; - -function getAdapterNameForAlias(aliasName) { - return adapterManager.aliasRegistry[aliasName] || aliasName; -}; - -function isAdagio(value) { - if (!value) { - return false - } - - return value.toLowerCase().includes('adagio') || - getAdapterNameForAlias(value).toLowerCase().includes('adagio'); -}; - -function getMediaTypeAlias(mediaType) { - const mediaTypesMap = { - banner: 'ban', - outstream: 'vidout', - instream: 'vidin', - adpod: 'vidadpod', - native: 'nat' - }; - return mediaTypesMap[mediaType] || mediaType; -}; - -function addKeyPrefix(obj, prefix) { - return Object.keys(obj).reduce((acc, key) => { - // We don't want to prefix already prefixed keys. - if (key.startsWith(prefix)) { - acc[key] = obj[key]; - return acc; - } - - acc[`${prefix}${key}`] = obj[key]; - return acc; - }, {}); -} - -/** - * sendRequest to Adagio. It filter null values and encode each query param. - * @param {Object} qp - */ -function sendRequest(qp) { - // Removing null values - qp = Object.keys(qp).reduce((acc, key) => { - if (qp[key] !== null) { - acc[key] = qp[key]; - } - return acc; - }, {}); - - const url = `${ENDPOINT}?${Object.keys(qp).map(key => `${key}=${enc(qp[key])}`).join('&')}`; - ajax(url, null, null, {method: 'GET'}); -}; - -/** - * Send a new beacon to Adagio. It increment the version of the beacon. - * @param {string} auctionId - * @param {string} adUnitCode - */ -function sendNewBeacon(auctionId, adUnitCode) { - cache.updateAuction(auctionId, adUnitCode, { - v: (cache.getAuction(auctionId, adUnitCode).v || 0) + 1 - }); - sendRequest(cache.getAuction(auctionId, adUnitCode)); -}; - -function getTargetedAuctionId(bid) { - return deepAccess(bid, 'latestTargetedAuctionId') || deepAccess(bid, 'auctionId'); } -/** - * END UTILS FUNCTIONS - */ - -/** - * HANDLERS - * - handlerAuctionInit - * - handlerBidResponse - * - handlerAuctionEnd - * - handlerBidWon - * - handlerAdRender - * - * Each handler is called when the event is fired. - */ - -function handlerAuctionInit(event) { - const w = getCurrentWindow(); - - const prebidAuctionId = event.auctionId; - const adUnitCodes = removeDuplicates(event.adUnitCodes, adUnitCode => adUnitCode); - - // Check if Adagio is on the bid requests. - // If not, we don't need to track the auction. - const adagioBidRequest = event.bidderRequests.find(bidRequest => isAdagio(bidRequest.bidderCode)); - if (!adagioBidRequest) { - logInfo(`Adagio is not on the bid requests for auction '${prebidAuctionId}'`) - return; - } - - cache.auctions[prebidAuctionId] = {}; - - adUnitCodes.forEach(adUnitCode => { - const adUnits = event.adUnits.filter(adUnit => adUnit.code === adUnitCode); - - // Get all bidders configures for the ad unit. - const bidders = removeDuplicates( - adUnits.map(adUnit => adUnit.bids.map(bid => ({bidder: bid.bidder, params: bid.params}))).flat(), - bidder => bidder.bidder - ); - - // Check if Adagio is configured for the ad unit. - // If not, we don't need to track the ad unit. - const adagioBidder = bidders.find(bidder => isAdagio(bidder.bidder)); - if (!adagioBidder) { - logInfo(`Adagio is not configured for ad unit '${adUnitCode}'`); - return; - } - - // Get all media types and banner sizes configured for the ad unit. - const mediaTypes = adUnits.map(adUnit => adUnit.mediaTypes); - const mediaTypesKeys = removeDuplicates( - mediaTypes.map(mediaTypeObj => Object.keys(mediaTypeObj)).flat(), - mediaTypeKey => mediaTypeKey - ).map(mediaType => getMediaTypeAlias(mediaType)).sort(); - const bannerSizes = removeDuplicates( - mediaTypes.filter(mediaType => mediaType.hasOwnProperty(BANNER)) - .map(mediaType => mediaType[BANNER].sizes.map(size => size.join('x'))) - .flat(), - bannerSize => bannerSize - ).sort(); - - // Get all Adagio bids for the ad unit from the bidRequest. - // If no bids, we don't need to track the ad unit. - const adagioAdUnitBids = adagioBidRequest.bids.filter(bid => bid.adUnitCode === adUnitCode); - if (deepAccess(adagioAdUnitBids, 'length', 0) <= 0) { - logInfo(`Adagio is not on the bid requests for ad unit '${adUnitCode}' and auction '${prebidAuctionId}'`) - return; - } - // Get Adagio params from the first bid. - // We assume that all Adagio bids for a same adunit have the same params. - const params = adagioAdUnitBids[0].params; - - const adagioAuctionId = params.adagioAuctionId; - cache.addPrebidAuctionIdRef(prebidAuctionId, adagioAuctionId); - - // Get all media types requested for Adagio. - const adagioMediaTypes = removeDuplicates( - adagioAdUnitBids.map(bid => Object.keys(bid.mediaTypes)).flat(), - mediaTypeKey => mediaTypeKey - ).flat().map(mediaType => getMediaTypeAlias(mediaType)).sort(); - - const qp = { - v: 0, - pbjsv: PREBID_VERSION, - org_id: params.organizationId, - site: params.site, - pv_id: params.pageviewId, - auct_id: adagioAuctionId, - adu_code: adUnitCode, - url_dmn: w.location.hostname, - pgtyp: params.pagetype, - plcmt: params.placement, - t_n: params.testName || null, - t_v: params.testVersion || null, - mts: mediaTypesKeys.join(','), - ban_szs: bannerSizes.join(','), - bdrs: bidders.map(bidder => getAdapterNameForAlias(bidder.bidder)).sort().join(','), - adg_mts: adagioMediaTypes.join(',') - }; - - cache.auctions[prebidAuctionId][adUnitCode] = qp; - sendNewBeacon(prebidAuctionId, adUnitCode); - }); -}; - -/** - * handlerBidResponse allow to track the adagio bid response - * and to update the auction cache with the seat ID. - * No beacon is sent here. - */ -function handlerBidResponse(event) { - if (!guard.adagio(event.bidder)) { - return; - } - - if (!guard.bidTracked(event.auctionId, event.adUnitCode)) { - return; - } - - if (!event.pba) { - return; - } - - cache.updateAuction(event.auctionId, event.adUnitCode, { - ...addKeyPrefix(event.pba, 'e_') - }); -}; - -function handlerAuctionEnd(event) { - const { auctionId } = event; - - if (!guard.auctionTracked(auctionId)) { - return; - } - - const adUnitCodes = cache.getAllAdUnitCodes(auctionId); - adUnitCodes.forEach(adUnitCode => { - const mapper = (bidder) => event.bidsReceived.find(bid => bid.adUnitCode === adUnitCode && bid.bidder === bidder) ? '1' : '0'; - - cache.updateAuction(auctionId, adUnitCode, { - bdrs_bid: cache.getBiddersFromAuction(auctionId, adUnitCode).map(mapper).join(',') - }); - sendNewBeacon(auctionId, adUnitCode); - }); -} - -function handlerBidWon(event) { - let auctionId = getTargetedAuctionId(event); - - if (!guard.bidTracked(auctionId, event.adUnitCode)) { - return; - } - - let adsCurRateToUSD = (event.currency === 'USD') ? 1 : null; - let ogCurRateToUSD = (event.originalCurrency === 'USD') ? 1 : null; - try { - if (typeof getGlobal().convertCurrency === 'function') { - // Currency module is loaded, we can calculate the conversion rate. - - // Get the conversion rate from the original currency to USD. - ogCurRateToUSD = getGlobal().convertCurrency(1, event.originalCurrency, 'USD'); - // Get the conversion rate from the ad server currency to USD. - adsCurRateToUSD = getGlobal().convertCurrency(1, event.currency, 'USD'); - } - } catch (error) { - logError('Error on Adagio Analytics Adapter - handlerBidWon', error); - } - - const adagioAuctionCacheId = ( - (event.latestTargetedAuctionId && event.latestTargetedAuctionId !== event.auctionId) - ? cache.getAdagioAuctionId(event.auctionId) - : null); - - cache.updateAuction(auctionId, event.adUnitCode, { - win_bdr: getAdapterNameForAlias(event.bidder), - win_mt: getMediaTypeAlias(event.mediaType), - win_ban_sz: event.mediaType === BANNER ? `${event.width}x${event.height}` : null, - - // ad server currency - win_cpm: event.cpm, - cur: event.currency, - cur_rate: adsCurRateToUSD, - - // original currency from bidder - og_cpm: event.originalCpm, - og_cur: event.originalCurrency, - og_cur_rate: ogCurRateToUSD, - - // cache bid id - auct_id_c: adagioAuctionCacheId, - }); - sendNewBeacon(auctionId, event.adUnitCode); -}; - -function handlerAdRender(event, isSuccess) { - const { adUnitCode } = event.bid; - let auctionId = getTargetedAuctionId(event.bid); - - if (!guard.bidTracked(auctionId, adUnitCode)) { - return; - } - - cache.updateAuction(auctionId, adUnitCode, { - rndr: isSuccess ? 1 : 0 - }); - sendNewBeacon(auctionId, adUnitCode); -}; - -/** - * END HANDLERS - */ - let adagioAdapter = Object.assign(adapter({ emptyUrl, analyticsType }), { - track: function(event) { - const { eventType, args } = event; - - try { - switch (eventType) { - case CONSTANTS.EVENTS.AUCTION_INIT: - handlerAuctionInit(args); - break; - case CONSTANTS.EVENTS.BID_RESPONSE: - handlerBidResponse(args); - break; - case CONSTANTS.EVENTS.AUCTION_END: - handlerAuctionEnd(args); - break; - case CONSTANTS.EVENTS.BID_WON: - handlerBidWon(args); - break; - // AD_RENDER_SUCCEEDED seems redundant with BID_WON. - // case CONSTANTS.EVENTS.AD_RENDER_SUCCEEDED: - case CONSTANTS.EVENTS.AD_RENDER_FAILED: - handlerAdRender(args, eventType === CONSTANTS.EVENTS.AD_RENDER_SUCCEEDED); - break; - } - } catch (error) { - logError('Error on Adagio Analytics Adapter', error); - } - - try { - if (typeof args !== 'undefined' && events.indexOf(eventType) !== -1) { - adagioEnqueue('pb-analytics-event', { eventName: eventType, args }); - } - } catch (error) { - logError('Error on Adagio Analytics Adapter - adagio.js', error); + track: function({ eventType, args }) { + if (typeof args !== 'undefined' && events.indexOf(eventType) !== -1) { + adagioEnqueue('pb-analytics-event', { eventName: eventType, args }); } } }); @@ -415,8 +37,11 @@ let adagioAdapter = Object.assign(adapter({ emptyUrl, analyticsType }), { adagioAdapter.originEnableAnalytics = adagioAdapter.enableAnalytics; adagioAdapter.enableAnalytics = config => { - const w = (canAccessTopWindow()) ? getWindowTop() : getWindowSelf(); - currentWindow = w; + if (!canAccessTopWindow()) { + return; + } + + const w = getWindowTop(); w.ADAGIO = w.ADAGIO || {}; w.ADAGIO.queue = w.ADAGIO.queue || []; @@ -428,8 +53,7 @@ adagioAdapter.enableAnalytics = config => { adapterManager.registerAnalyticsAdapter({ adapter: adagioAdapter, - code: 'adagio', - gvlid: ADAGIO_GVLID, + code: 'adagio' }); export default adagioAdapter; diff --git a/modules/adagioAnalyticsAdapter.md b/modules/adagioAnalyticsAdapter.md index 9fc2cb0bb88..312a26ea8da 100644 --- a/modules/adagioAnalyticsAdapter.md +++ b/modules/adagioAnalyticsAdapter.md @@ -8,10 +8,10 @@ Maintainer: dev@adagio.io Analytics adapter for Adagio -# Settings +# Test Parameters -```js - pbjs.enableAnalytics({ - provider: 'adagio', - }); +``` +{ + provider: 'adagio' +} ``` diff --git a/modules/adagioBidAdapter.js b/modules/adagioBidAdapter.js index 112722383a9..57dd80aa9b1 100644 --- a/modules/adagioBidAdapter.js +++ b/modules/adagioBidAdapter.js @@ -1,10 +1,12 @@ import {find} from '../src/polyfill.js'; import { + _map, cleanObj, deepAccess, deepClone, generateUUID, getDNT, + getGptSlotInfoForAdUnitCode, getUniqueIdentifierStr, getWindowSelf, getWindowTop, @@ -32,7 +34,6 @@ import {OUTSTREAM} from '../src/video.js'; import { getGlobal } from '../src/prebidGlobal.js'; import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; import { userSync } from '../src/userSync.js'; -import {getGptSlotInfoForAdUnitCode} from '../libraries/gptUtils/gptUtils.js'; const BIDDER_CODE = 'adagio'; const LOG_PREFIX = 'Adagio:'; @@ -53,9 +54,8 @@ const ADAGIO_PUBKEY = 'AL16XT44Sfp+8SHVF1UdC7hydPSMVLMhsYknKDdwqq+0ToDSJrP0+Qh0k const ADAGIO_PUBKEY_E = 65537; const CURRENCY = 'USD'; -// This provide a whitelist and a basic validation of OpenRTB 2.5 options used by the Adagio SSP. -// Accept all options but 'protocol', 'companionad', 'companiontype', 'ext' -// https://www.iab.com/wp-content/uploads/2016/03/OpenRTB-API-Specification-Version-2-5-FINAL.pdf +// This provide a whitelist and a basic validation of OpenRTB 2.6 options used by the Adagio SSP. +// https://iabtechlab.com/wp-content/uploads/2022/04/OpenRTB-2-6_FINAL.pdf export const ORTB_VIDEO_PARAMS = { 'mimes': (value) => Array.isArray(value) && value.length > 0 && value.every(v => typeof v === 'string'), 'minduration': (value) => isInteger(value), @@ -399,17 +399,6 @@ function _getUspConsent(bidderRequest) { return (deepAccess(bidderRequest, 'uspConsent')) ? { uspConsent: bidderRequest.uspConsent } : false; } -function _getGppConsent(bidderRequest) { - let gpp = deepAccess(bidderRequest, 'gppConsent.gppString') - let gppSid = deepAccess(bidderRequest, 'gppConsent.applicableSections') - - if (!gpp || !gppSid) { - gpp = deepAccess(bidderRequest, 'ortb2.regs.gpp', '') - gppSid = deepAccess(bidderRequest, 'ortb2.regs.gpp_sid', []) - } - return { gpp, gppSid } -} - function _getSchain(bidRequest) { return deepAccess(bidRequest, 'schain'); } @@ -569,7 +558,6 @@ function _parseNativeBidResponse(bid) { bid.native = native } -// bidRequest param must be the `bidRequest` object with the original `auctionId` value. function _getFloors(bidRequest) { if (!isFn(bidRequest.getFloor)) { return false; @@ -700,9 +688,9 @@ function getPageDimensions() { } /** - * @todo Move to prebid Core as Utils. - * @returns - */ +* @todo Move to prebid Core as Utils. +* @returns +*/ function getViewPortDimensions() { if (!isSafeFrameWindow() && !canAccessTopWindow()) { return ''; @@ -832,8 +820,8 @@ function getPrintNumber(adUnitCode, bidderRequest) { } /** - * domLoading feature is computed on window.top if reachable. - */ + * domLoading feature is computed on window.top if reachable. + */ function getDomLoadingDuration() { let domLoadingDuration = -1; let performance; @@ -988,7 +976,6 @@ export const spec = { const gdprConsent = _getGdprConsent(bidderRequest) || {}; const uspConsent = _getUspConsent(bidderRequest) || {}; const coppa = _getCoppa(); - const gppConsent = _getGppConsent(bidderRequest) const schain = _getSchain(validBidRequests[0]); const eids = _getEids(validBidRequests[0]) || []; const syncEnabled = deepAccess(config.getConfig('userSync'), 'syncEnabled') @@ -996,8 +983,8 @@ export const spec = { const aucId = generateUUID() - const adUnits = validBidRequests.map(rawBidRequest => { - const bidRequest = deepClone(rawBidRequest); + const adUnits = _map(validBidRequests, (rawBidRequest) => { + const bidRequest = {...rawBidRequest} // Fix https://github.com/prebid/Prebid.js/issues/9781 bidRequest.auctionId = aucId @@ -1020,9 +1007,6 @@ export const spec = { } } - // Enforce the organizationId param to be a string - bidRequest.params.organizationId = bidRequest.params.organizationId.toString(); - // Force the Data Layer key and value to be a String if (bidRequest.params.dataLayer) { if (isStr(bidRequest.params.dataLayer) || isNumber(bidRequest.params.dataLayer) || isArray(bidRequest.params.dataLayer) || isFn(bidRequest.params.dataLayer)) { @@ -1071,10 +1055,7 @@ export const spec = { }); // Handle priceFloors module - // We need to use `rawBidRequest` as param because: - // - adagioBidAdapter generates its own auctionId due to transmitTid activity limitation (see https://github.com/prebid/Prebid.js/pull/10079) - // - the priceFloors.getFloor() uses a `_floorDataForAuction` map to store the floors based on the auctionId. - const computedFloors = _getFloors(rawBidRequest); + const computedFloors = _getFloors(bidRequest); if (isArray(computedFloors) && computedFloors.length) { bidRequest.floors = computedFloors @@ -1118,59 +1099,41 @@ export const spec = { _buildVideoBidRequest(bidRequest); } - const gpid = deepAccess(bidRequest, 'ortb2Imp.ext.gpid') || deepAccess(bidRequest, 'ortb2Imp.ext.data.pbadslot'); - if (gpid) { - bidRequest.gpid = gpid; - } - - // store the whole bidRequest (adUnit) object in the ADAGIO namespace. storeRequestInAdagioNS(bidRequest); - // Remove some params that are not needed on the server side. - delete bidRequest.params.siteId; - - // whitelist the fields that are allowed to be sent to the server. - const adUnit = { - adUnitCode: bidRequest.adUnitCode, - auctionId: bidRequest.auctionId, - bidder: bidRequest.bidder, - bidId: bidRequest.bidId, - params: bidRequest.params, - features: bidRequest.features, - gpid: bidRequest.gpid, - mediaTypes: bidRequest.mediaTypes, - nativeParams: bidRequest.nativeParams, - score: bidRequest.score, - transactionId: bidRequest.transactionId, - } + // Remove these fields at the very end, so we can still use them before. + delete bidRequest.transactionId; + delete bidRequest.ortb2Imp; + delete bidRequest.ortb2; + delete bidRequest.sizes; - return adUnit; + return bidRequest; }); // Group ad units by organizationId const groupedAdUnits = adUnits.reduce((groupedAdUnits, adUnit) => { - const organizationId = adUnit.params.organizationId + const adUnitCopy = deepClone(adUnit); + adUnitCopy.params.organizationId = adUnitCopy.params.organizationId.toString(); - groupedAdUnits[organizationId] = groupedAdUnits[organizationId] || []; - groupedAdUnits[organizationId].push(adUnit); + // remove useless props + delete adUnitCopy.floorData; + delete adUnitCopy.params.siteId; + delete adUnitCopy.userId + delete adUnitCopy.userIdAsEids + + groupedAdUnits[adUnitCopy.params.organizationId] = groupedAdUnits[adUnitCopy.params.organizationId] || []; + groupedAdUnits[adUnitCopy.params.organizationId].push(adUnitCopy); return groupedAdUnits; }, {}); - // Adding more params on the original bid object. - // Those params are not sent to the server. - // They are used for further operations on analytics adapter. - validBidRequests.forEach(rawBidRequest => { - rawBidRequest.params.adagioAuctionId = aucId - rawBidRequest.params.pageviewId = pageviewId - }); - // Build one request per organizationId - const requests = Object.keys(groupedAdUnits).map(organizationId => { + const requests = _map(Object.keys(groupedAdUnits), organizationId => { return { method: 'POST', url: ENDPOINT, data: { + id: generateUUID(), organizationId: organizationId, secure: secure, device: device, @@ -1181,9 +1144,7 @@ export const spec = { regs: { gdpr: gdprConsent, coppa: coppa, - ccpa: uspConsent, - gpp: gppConsent.gpp, - gppSid: gppConsent.gppSid + ccpa: uspConsent }, schain: schain, user: { @@ -1191,8 +1152,7 @@ export const spec = { }, prebidVersion: '$prebid.version$', featuresVersion: FEATURES_VERSION, - usIfr: usIfr, - adgjs: storage.localStorageIsEnabled() + usIfr: usIfr }, options: { contentType: 'text/plain' diff --git a/modules/adagioBidAdapter.md b/modules/adagioBidAdapter.md index 19673571982..45f39fc6f2d 100644 --- a/modules/adagioBidAdapter.md +++ b/modules/adagioBidAdapter.md @@ -107,11 +107,10 @@ var adUnits = [ cpm: 3.00 // default to 1.00 }, video: { - api: [2], // Required - Your video player must at least support the value 2 + api: [2, 7], // Required - Your video player must at least support the value 2 and/or 7. playbackMethod: [6], // Highly recommended skip: 0 - // OpenRTB 2.5 video options defined here override ones defined in mediaTypes. - // Not supported: 'protocol', 'companionad', 'companiontype', 'ext' + // OpenRTB video options defined here override ones defined in mediaTypes. }, native: { // Optional OpenRTB Native 1.2 request object. Only `context`, `plcmttype` fields are supported. @@ -194,8 +193,6 @@ If the FPD value is an array, the 1st value of this array will be used. placement: 'in_article', adUnitElementId: 'article_outstream', video: { - api: [2], - playbackMethod: [6], skip: 0 }, debug: { diff --git a/modules/adfBidAdapter.js b/modules/adfBidAdapter.js index e5b40f66176..5c4b03c3bb2 100644 --- a/modules/adfBidAdapter.js +++ b/modules/adfBidAdapter.js @@ -69,11 +69,8 @@ export const spec = { bid.netRevenue = pt; const floorInfo = bid.getFloor ? bid.getFloor({ - currency: currency || 'USD', - size: '*', - mediaType: '*' + currency: currency || 'USD' }) : {}; - const bidfloor = floorInfo.floor; const bidfloorcur = floorInfo.currency; const { mid, inv, mname } = bid.params; diff --git a/modules/adfusionBidAdapter.js b/modules/adfusionBidAdapter.js deleted file mode 100644 index a206ee5e899..00000000000 --- a/modules/adfusionBidAdapter.js +++ /dev/null @@ -1,120 +0,0 @@ -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'; - -const adpterVersion = '1.0'; -export const REQUEST_URL = 'https://spicyrtb.com/auction/prebid'; -export const DEFAULT_CURRENCY = 'USD'; - -export const spec = { - code: 'adfusion', - gvlid: 844, - supportedMediaTypes: [BANNER, VIDEO], - isBidRequestValid, - buildRequests, - interpretResponse, - isBannerBid, - isVideoBid, -}; - -registerBidder(spec); - -const converter = ortbConverter({ - context: { - netRevenue: true, - ttl: 300, - currency: DEFAULT_CURRENCY, - }, - imp(buildImp, bidRequest, context) { - const imp = buildImp(bidRequest, context); - const floor = getBidFloor(bidRequest); - if (floor) { - imp.bidfloor = floor; - imp.bidfloorcur = DEFAULT_CURRENCY; - } - - return imp; - }, - request(buildRequest, imps, bidderRequest, context) { - const req = buildRequest(imps, bidderRequest, context); - const bid = context.bidRequests[0]; - utils.mergeDeep(req, { - at: 1, - ext: { - prebid: { - accountid: bid.params.accountId, - adapterVersion: `${adpterVersion}`, - }, - }, - }); - return req; - }, - response(buildResponse, bidResponses, ortbResponse, context) { - const response = buildResponse(bidResponses, ortbResponse, context); - return response.bids; - }, -}); - -function isBidRequestValid(bidRequest) { - const isValid = bidRequest.params.accountId; - if (!isValid) { - utils.logError('AdFusion adapter bidRequest has no accountId'); - return false; - } - return true; -} - -function buildRequests(bids, bidderRequest) { - let videoBids = bids.filter((bid) => isVideoBid(bid)); - let bannerBids = bids.filter((bid) => isBannerBid(bid)); - let requests = bannerBids.length - ? [createRequest(bannerBids, bidderRequest, BANNER)] - : []; - videoBids.forEach((bid) => { - requests.push(createRequest([bid], bidderRequest, VIDEO)); - }); - return requests; -} - -function createRequest(bidRequests, bidderRequest, mediaType) { - return { - method: 'POST', - url: REQUEST_URL, - data: converter.toORTB({ - bidRequests, - bidderRequest, - context: { mediaType }, - }), - }; -} - -function isVideoBid(bid) { - return utils.deepAccess(bid, 'mediaTypes.video'); -} - -function isBannerBid(bid) { - return utils.deepAccess(bid, 'mediaTypes.banner'); -} - -function interpretResponse(resp, req) { - return converter.fromORTB({ request: req.data, response: resp.body }); -} - -function getBidFloor(bid) { - if (utils.isFn(bid.getFloor)) { - let floor = bid.getFloor({ - currency: DEFAULT_CURRENCY, - mediaType: '*', - size: '*', - }); - if ( - utils.isPlainObject(floor) && - !isNaN(floor.floor) && - floor.currency === DEFAULT_CURRENCY - ) { - return floor.floor; - } - } - return null; -} diff --git a/modules/adfusionBidAdapter.md b/modules/adfusionBidAdapter.md deleted file mode 100644 index 803a03ba1a1..00000000000 --- a/modules/adfusionBidAdapter.md +++ /dev/null @@ -1,61 +0,0 @@ -# Overview - -``` -Module Name: AdFusion Bid Adapter -Module Type: Bidder Adapter -Maintainer: prebid@adfusion.pl -``` - -# Description - -Module that connects to AdFusion demand sources - -# Banner Test Parameters - -```js -var adUnits = [ - { - code: "test-banner", - mediaTypes: { - banner: { - sizes: [ - [300, 250], - [300, 600], - [320, 480], - ], - }, - }, - bids: [ - { - bidder: "adfusion", - params: { - accountId: 1234, // required - }, - }, - ], - }, -]; -``` - -# Video Test Parameters - -```js -var videoAdUnit = { - code: "video1", - mediaTypes: { - video: { - context: "instream", - playerSize: [640, 480], - mimes: ["video/mp4"], - }, - }, - bids: [ - { - bidder: "adfusion", - params: { - accountId: 1234, // required - }, - }, - ], -}; -``` diff --git a/modules/adgenerationBidAdapter.js b/modules/adgenerationBidAdapter.js index e0538fe2815..f18b7629d8f 100644 --- a/modules/adgenerationBidAdapter.js +++ b/modules/adgenerationBidAdapter.js @@ -1,18 +1,8 @@ -import {deepAccess, getBidIdParameter} from '../src/utils.js'; +import {tryAppendQueryString, getBidIdParameter, escapeUnsafeChars, deepAccess} 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 {convertOrtbRequestToProprietaryNative} from '../src/native.js'; -import {tryAppendQueryString} from '../libraries/urlUtils/urlUtils.js'; -import {escapeUnsafeChars} from '../libraries/htmlEscape/htmlEscape.js'; - -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse - * @typedef {import('../src/adapters/bidderFactory.js').SyncOptions} SyncOptions - * @typedef {import('../src/adapters/bidderFactory.js').UserSync} UserSync - */ const ADG_BIDDER_CODE = 'adgeneration'; @@ -38,7 +28,7 @@ export const spec = { buildRequests: function (validBidRequests, bidderRequest) { // convert Native ORTB definition to old-style prebid native definition validBidRequests = convertOrtbRequestToProprietaryNative(validBidRequests); - const ADGENE_PREBID_VERSION = '1.6.2'; + const ADGENE_PREBID_VERSION = '1.6.0'; let serverRequests = []; for (let i = 0, len = validBidRequests.length; i < len; i++) { const validReq = validBidRequests[i]; @@ -51,7 +41,6 @@ export const spec = { const imuid = deepAccess(validReq, 'userId.imuid'); const gpid = deepAccess(validReq, 'ortb2Imp.ext.gpid'); const sua = deepAccess(validReq, 'ortb2.device.sua'); - const uid2 = deepAccess(validReq, 'userId.uid2.id'); let data = ``; data = tryAppendQueryString(data, 'posall', 'SSPLOC'); const id = getBidIdParameter('id', validReq.params); @@ -69,10 +58,10 @@ export const spec = { data = tryAppendQueryString(data, 'adgext_id5_id', id5id); data = tryAppendQueryString(data, 'adgext_id5_id_link_type', id5LinkType); data = tryAppendQueryString(data, 'adgext_imuid', imuid); - data = tryAppendQueryString(data, 'adgext_uid2', uid2); - data = tryAppendQueryString(data, 'gpid', gpid); + data = tryAppendQueryString(data, 'adgext_uid2', validReq.userId ? validReq.userId.uid2 : null); + data = tryAppendQueryString(data, 'gpid', gpid ? encodeURIComponent(gpid) : null); data = tryAppendQueryString(data, 'uach', sua ? JSON.stringify(sua) : null); - data = tryAppendQueryString(data, 'schain', validReq.schain ? JSON.stringify(validReq.schain) : null); + data = tryAppendQueryString(data, 'schain', validReq.schain ? encodeURIComponent(JSON.stringify(validReq.schain)) : null); // nativeäģĨ外ãĢvideoį­‰ãŽå¯žåŋœãŒå…ĨãŖた場合はčĻäŋŽæ­Ŗ if (!validReq.mediaTypes || !validReq.mediaTypes.native) { diff --git a/modules/adkernelAdnBidAdapter.js b/modules/adkernelAdnBidAdapter.js index 81067a3efcf..4db46aca3c6 100644 --- a/modules/adkernelAdnBidAdapter.js +++ b/modules/adkernelAdnBidAdapter.js @@ -159,7 +159,7 @@ export const spec = { code: 'adkernelAdn', gvlid: GVLID, supportedMediaTypes: [BANNER, VIDEO], - aliases: ['engagesimply', 'adpluto_dsp'], + aliases: ['engagesimply'], isBidRequestValid: function(bidRequest) { return 'params' in bidRequest && diff --git a/modules/adkernelBidAdapter.js b/modules/adkernelBidAdapter.js index d6a4030057a..64567832dbd 100644 --- a/modules/adkernelBidAdapter.js +++ b/modules/adkernelBidAdapter.js @@ -5,6 +5,7 @@ import { createTrackPixelHtml, deepAccess, deepSetValue, + getAdUnitSizes, getDefinedParams, getDNT, isArray, @@ -21,31 +22,25 @@ import {registerBidder} from '../src/adapters/bidderFactory.js'; import {find} from '../src/polyfill.js'; import {config} from '../src/config.js'; import {convertOrtbRequestToProprietaryNative} from '../src/native.js'; -import {getAdUnitSizes} from '../libraries/sizeUtils/sizeUtils.js'; -/** +/* * In case you're AdKernel whitelable platform's client who needs branded adapter to * work with Adkernel platform - DO NOT COPY THIS ADAPTER UNDER NEW NAME * - * Please contact prebid@adkernel.com and we'll add your adapter as an alias - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - * @typedef {import('../src/adapters/bidderFactory.js').ServerRequest} ServerRequest - * @typedef {import('../src/adapters/bidderFactory.js').UserSync} UserSync + * Please contact prebid@adkernel.com and we'll add your adapter as an alias. */ - -const VIDEO_PARAMS = ['pos', 'context', 'placement', 'plcmt', 'api', 'mimes', 'protocols', 'playbackmethod', 'minduration', 'maxduration', +const VIDEO_PARAMS = ['pos', 'context', 'placement', 'api', 'mimes', 'protocols', 'playbackmethod', 'minduration', 'maxduration', 'startdelay', 'linearity', 'skip', 'skipmin', 'skipafter', 'minbitrate', 'maxbitrate', 'delivery', 'playbackend', 'boxingallowed']; const VIDEO_FPD = ['battr', 'pos']; const NATIVE_FPD = ['battr', 'api']; -const BANNER_PARAMS = ['pos']; const BANNER_FPD = ['btype', 'battr', 'pos', 'api']; const VERSION = '1.6'; const SYNC_IFRAME = 1; const SYNC_IMAGE = 2; -const SYNC_TYPES = { +const SYNC_TYPES = Object.freeze({ 1: 'iframe', 2: 'image' -}; +}); const GVLID = 14; const NATIVE_MODEL = [ @@ -71,11 +66,6 @@ const NATIVE_INDEX = NATIVE_MODEL.reduce((acc, val, idx) => { return acc; }, {}); -const MULTI_FORMAT_SUFFIX = '__mf'; -const MULTI_FORMAT_SUFFIX_BANNER = 'b' + MULTI_FORMAT_SUFFIX; -const MULTI_FORMAT_SUFFIX_VIDEO = 'v' + MULTI_FORMAT_SUFFIX; -const MULTI_FORMAT_SUFFIX_NATIVE = 'n' + MULTI_FORMAT_SUFFIX; - /** * Adapter for requesting bids from AdKernel white-label display platform */ @@ -108,10 +98,9 @@ export const spec = { {code: 'displayioads'}, {code: 'rtbdemand_com'}, {code: 'bidbuddy'}, + {code: 'adliveconnect'}, {code: 'didnadisplay'}, - {code: 'qortex'}, - {code: 'adpluto'}, - {code: 'headbidder'} + {code: 'qortex'} ], supportedMediaTypes: [BANNER, VIDEO, NATIVE], @@ -182,9 +171,6 @@ export const spec = { ttl: 360, netRevenue: true }; - if (prBid.requestId.endsWith(MULTI_FORMAT_SUFFIX)) { - prBid.requestId = stripMultiformatSuffix(prBid.requestId); - } if ('banner' in imp) { prBid.mediaType = BANNER; prBid.width = rtbBid.w; @@ -251,13 +237,13 @@ registerBidder(spec); function groupImpressionsByHostZone(bidRequests, refererInfo) { let secure = (refererInfo && refererInfo.page?.indexOf('https:') === 0); return Object.values( - bidRequests.map(bidRequest => buildImps(bidRequest, secure)) + bidRequests.map(bidRequest => buildImp(bidRequest, secure)) .reduce((acc, curr, index) => { let bidRequest = bidRequests[index]; let {zoneId, host} = bidRequest.params; let key = `${host}_${zoneId}`; acc[key] = acc[key] || {host: host, zoneId: zoneId, imps: []}; - acc[key].imps.push(...curr); + acc[key].imps.push(curr); return acc; }, {}) ); @@ -276,97 +262,57 @@ function getBidFloor(bid, mediaType, sizes) { } /** - * Builds rtb imp object(s) for single adunit + * Builds rtb imp object for single adunit * @param bidRequest {BidRequest} * @param secure {boolean} */ -function buildImps(bidRequest, secure) { - let imp = { +function buildImp(bidRequest, secure) { + const imp = { 'id': bidRequest.bidId, 'tagid': bidRequest.adUnitCode }; - if (secure) { - imp.secure = 1; - } + var mediaType; var sizes = []; - let mediaTypes = bidRequest.mediaTypes; - let isMultiformat = (~~!!mediaTypes?.banner + ~~!!mediaTypes?.video + ~~!!mediaTypes?.native) > 1; - let result = []; - let typedImp; - - if (mediaTypes?.banner) { - if (isMultiformat) { - typedImp = {...imp}; - typedImp.id = imp.id + MULTI_FORMAT_SUFFIX_BANNER; - } else { - typedImp = imp; - } + + if (deepAccess(bidRequest, 'mediaTypes.banner')) { sizes = getAdUnitSizes(bidRequest); - let pbBanner = mediaTypes.banner; - typedImp.banner = { - ...getDefinedParamsOrEmpty(bidRequest.ortb2Imp, BANNER_FPD), - ...getDefinedParamsOrEmpty(pbBanner, BANNER_PARAMS), + imp.banner = { format: sizes.map(wh => parseGPTSingleSizeArrayToRtbSize(wh)), topframe: 0 }; - initImpBidfloor(typedImp, bidRequest, sizes, isMultiformat ? '*' : BANNER); - result.push(typedImp); - } - - if (mediaTypes?.video) { - if (isMultiformat) { - typedImp = {...imp}; - typedImp.id = typedImp.id + MULTI_FORMAT_SUFFIX_VIDEO; - } else { - typedImp = imp; + populateImpFpd(imp.banner, bidRequest, BANNER_FPD); + mediaType = BANNER; + } else if (deepAccess(bidRequest, 'mediaTypes.video')) { + let video = deepAccess(bidRequest, 'mediaTypes.video'); + imp.video = getDefinedParams(video, VIDEO_PARAMS); + populateImpFpd(imp.video, bidRequest, VIDEO_FPD); + if (video.playerSize) { + sizes = video.playerSize[0]; + imp.video = Object.assign(imp.video, parseGPTSingleSizeArrayToRtbSize(sizes) || {}); + } else if (video.w && video.h) { + imp.video.w = video.w; + imp.video.h = video.h; } - let pbVideo = mediaTypes.video; - typedImp.video = { - ...getDefinedParamsOrEmpty(bidRequest.ortb2Imp, VIDEO_FPD), - ...getDefinedParamsOrEmpty(pbVideo, VIDEO_PARAMS) - }; - if (pbVideo.playerSize) { - sizes = pbVideo.playerSize[0]; - typedImp.video = Object.assign(typedImp.video, parseGPTSingleSizeArrayToRtbSize(sizes) || {}); - } else if (pbVideo.w && pbVideo.h) { - typedImp.video.w = pbVideo.w; - typedImp.video.h = pbVideo.h; - } - initImpBidfloor(typedImp, bidRequest, sizes, isMultiformat ? '*' : VIDEO); - result.push(typedImp); - } - - if (mediaTypes?.native) { - if (isMultiformat) { - typedImp = {...imp}; - typedImp.id = typedImp.id + MULTI_FORMAT_SUFFIX_NATIVE; - } else { - typedImp = imp; - } - let nativeRequest = buildNativeRequest(mediaTypes.native); - typedImp.native = { - ...getDefinedParamsOrEmpty(bidRequest.ortb2Imp, NATIVE_FPD), + mediaType = VIDEO; + } else if (deepAccess(bidRequest, 'mediaTypes.native')) { + let nativeRequest = buildNativeRequest(bidRequest.mediaTypes.native); + imp.native = { ver: '1.1', request: JSON.stringify(nativeRequest) }; - initImpBidfloor(typedImp, bidRequest, sizes, isMultiformat ? '*' : NATIVE); - result.push(typedImp); + populateImpFpd(imp.native, bidRequest, NATIVE_FPD); + mediaType = NATIVE; + } else { + throw new Error('Unsupported bid received'); } - return result; -} - -function initImpBidfloor(imp, bid, sizes, mediaType) { - let bidfloor = getBidFloor(bid, mediaType, sizes); - if (bidfloor) { - imp.bidfloor = bidfloor; + let floor = getBidFloor(bidRequest, mediaType, sizes); + if (floor) { + imp.bidfloor = floor; } -} - -function getDefinedParamsOrEmpty(object, params) { - if (object === undefined) { - return {}; + if (secure) { + imp.secure = 1; } - return getDefinedParams(object, params); + return imp; } /** @@ -398,6 +344,19 @@ function buildNativeRequest(nativeReq) { return request; } +/** + * Populate impression-level FPD from bid request + * @param target {Object} + * @param bidRequest {BidRequest} + * @param props {String[]} + */ +function populateImpFpd(target, bidRequest, props) { + if (bidRequest.ortb2Imp === undefined) { + return; + } + Object.assign(target, getDefinedParams(bidRequest.ortb2Imp, props)); +} + /** * Builds image asset request */ @@ -497,9 +456,7 @@ function makeUser(bidderRequest, fpd) { if (eids) { deepSetValue(user, 'ext.eids', eids); } - if (!isEmpty(user)) { - return {user: user}; - } + if (!isEmpty(user)) { return {user: user}; } } /** @@ -508,17 +465,13 @@ function makeUser(bidderRequest, fpd) { * @returns {{regs: Object} | undefined} */ function makeRegulations(bidderRequest) { - let {gdprConsent, uspConsent, gppConsent} = bidderRequest; + let {gdprConsent, uspConsent} = bidderRequest; let regs = {}; if (gdprConsent) { if (gdprConsent.gdprApplies !== undefined) { deepSetValue(regs, 'regs.ext.gdpr', ~~gdprConsent.gdprApplies); } } - if (gppConsent) { - deepSetValue(regs, 'regs.gpp', gppConsent.gppString); - deepSetValue(regs, 'regs.gpp_sid', gppConsent.applicableSections); - } if (uspConsent) { deepSetValue(regs, 'regs.ext.us_privacy', uspConsent); } @@ -684,7 +637,3 @@ function buildNativeAd(nativeResp) { }); return cleanObj(nativeAd); } - -function stripMultiformatSuffix(impid) { - return impid.substr(0, impid.length - MULTI_FORMAT_SUFFIX.length - 1); -} diff --git a/modules/adlooxAnalyticsAdapter.js b/modules/adlooxAnalyticsAdapter.js index 9284d543298..5db75c656bb 100644 --- a/modules/adlooxAnalyticsAdapter.js +++ b/modules/adlooxAnalyticsAdapter.js @@ -14,6 +14,7 @@ import {find} from '../src/polyfill.js'; import {getRefererInfo} from '../src/refererDetection.js'; import { deepAccess, + getGptSlotInfoForAdUnitCode, getUniqueIdentifierStr, insertElement, isFn, @@ -27,7 +28,6 @@ import { mergeDeep, parseUrl } from '../src/utils.js'; -import {getGptSlotInfoForAdUnitCode} from '../libraries/gptUtils/gptUtils.js'; const MODULE = 'adlooxAnalyticsAdapter'; diff --git a/modules/adlooxRtdProvider.js b/modules/adlooxRtdProvider.js index 727dc84e399..c2037429185 100644 --- a/modules/adlooxRtdProvider.js +++ b/modules/adlooxRtdProvider.js @@ -25,6 +25,7 @@ import { deepAccess, deepClone, deepSetValue, + getGptSlotInfoForAdUnitCode, isArray, isBoolean, isInteger, @@ -36,7 +37,6 @@ import { parseUrl, safeJSONParse } from '../src/utils.js'; -import {getGptSlotInfoForAdUnitCode} from '../libraries/gptUtils/gptUtils.js'; const MODULE_NAME = 'adloox'; const MODULE = `${MODULE_NAME}RtdProvider`; diff --git a/modules/admanBidAdapter.js b/modules/admanBidAdapter.js index b78737722bd..ff8e6b02ad5 100644 --- a/modules/admanBidAdapter.js +++ b/modules/admanBidAdapter.js @@ -4,7 +4,6 @@ import { isFn, deepAccess, logMessage } from '../src/utils.js'; import {config} from '../src/config.js'; import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; -const GVLID = 149; const BIDDER_CODE = 'adman'; const AD_URL = 'https://pub.admanmedia.com/?c=o&m=multi'; const URL_SYNC = 'https://sync.admanmedia.com'; @@ -58,7 +57,6 @@ function getUserId(eids, id, source, uidExt) { export const spec = { code: BIDDER_CODE, - gvlid: GVLID, supportedMediaTypes: [BANNER, VIDEO, NATIVE], isBidRequestValid: (bid) => { @@ -68,7 +66,6 @@ export const spec = { buildRequests: (validBidRequests = [], bidderRequest) => { // convert Native ORTB definition to old-style prebid native definition validBidRequests = convertOrtbRequestToProprietaryNative(validBidRequests); - const content = deepAccess(bidderRequest, 'ortb2.site.content', config.getAnyConfig('ortb2.site.content')); let winTop = window; let location; @@ -96,12 +93,7 @@ export const spec = { request.ccpa = bidderRequest.uspConsent; } if (bidderRequest.gdprConsent) { - request.gdpr = { - consentString: bidderRequest.gdprConsent.consentString - }; - } - if (content) { - request.content = content; + request.gdpr = bidderRequest.gdprConsent } } const len = validBidRequests.length; @@ -117,11 +109,6 @@ export const spec = { bidFloor: getBidFloor(bid) } - if (bid.transactionId) { - placement.ext = placement.ext || {}; - placement.ext.tid = bid.transactionId; - } - if (bid.schain) { placement.schain = bid.schain; } diff --git a/modules/admaticBidAdapter.js b/modules/admaticBidAdapter.js index 3f87476def7..027f924ac5d 100644 --- a/modules/admaticBidAdapter.js +++ b/modules/admaticBidAdapter.js @@ -1,47 +1,16 @@ -import {getValue, formatQS, logError, deepAccess, isArray, getBidIdParameter} from '../src/utils.js'; +import { getValue, logError, isEmpty, deepAccess, getBidIdParameter, isArray } 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'; - -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - * @typedef {import('../src/adapters/bidderFactory.js').ServerRequest} ServerRequest - */ - -export const OPENRTB = { - NATIVE: { - IMAGE_TYPE: { - ICON: 1, - MAIN: 3, - }, - ASSET_ID: { - TITLE: 1, - IMAGE: 2, - ICON: 3, - BODY: 4, - SPONSORED: 5, - CTA: 6 - }, - DATA_ASSET_TYPE: { - SPONSORED: 1, - DESC: 2, - CTA_TEXT: 12, - }, - } -}; - +import { BANNER, VIDEO } from '../src/mediaTypes.js'; let SYNC_URL = ''; const BIDDER_CODE = 'admatic'; - export const spec = { code: BIDDER_CODE, aliases: [ {code: 'pixad'} ], - supportedMediaTypes: [BANNER, VIDEO, NATIVE], - /** - * f + supportedMediaTypes: [BANNER, VIDEO], + /** f * @param {object} bid * @return {boolean} */ @@ -64,21 +33,23 @@ export const spec = { * @return {ServerRequest} */ buildRequests: (validBidRequests, bidderRequest) => { - const tmax = bidderRequest.timeout; const bids = validBidRequests.map(buildRequestObject); - const ortb = bidderRequest.ortb2; + const blacklist = bidderRequest.ortb2; const networkId = getValue(validBidRequests[0].params, 'networkId'); const host = getValue(validBidRequests[0].params, 'host'); const currency = config.getConfig('currency.adServerCurrency') || 'TRY'; const bidderName = validBidRequests[0].bidder; const payload = { - ortb, + user: { + ua: navigator.userAgent + }, + blacklist: [], site: { - page: bidderRequest.refererInfo.page, - ref: bidderRequest.refererInfo.page, + page: location.href, + ref: location.origin, publisher: { - name: bidderRequest.refererInfo.domain, + name: location.hostname, publisherId: networkId } }, @@ -86,59 +57,17 @@ export const spec = { ext: { cur: currency, bidder: bidderName - }, - schain: {}, - regs: { - ext: { - } - }, - user: { - ext: {} - }, - at: 1, - tmax: parseInt(tmax) - }; - - if (bidderRequest && bidderRequest.gdprConsent && bidderRequest.gdprConsent.gdprApplies) { - const consentStr = (bidderRequest.gdprConsent.consentString) - ? bidderRequest.gdprConsent.consentString.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '') : ''; - const gdpr = bidderRequest.gdprConsent.gdprApplies ? 1 : 0; - payload.regs.ext.gdpr = gdpr; - payload.regs.ext.consent = consentStr; - } - - if (bidderRequest && bidderRequest.coppa) { - payload.regs.ext.coppa = bidderRequest.coppa === true ? 1 : (bidderRequest.coppa === false ? 0 : undefined); - } - - if (bidderRequest && bidderRequest.ortb2?.regs?.gpp) { - payload.regs.ext.gpp = bidderRequest.ortb2?.regs?.gpp; - } - - if (bidderRequest && bidderRequest.ortb2?.regs?.gpp_sid) { - payload.regs.ext.gpp_sid = bidderRequest.ortb2?.regs?.gpp_sid; - } - - if (bidderRequest && bidderRequest.uspConsent) { - payload.regs.ext.uspIab = bidderRequest.uspConsent; - } - - if (validBidRequests[0].schain) { - const schain = mapSchain(validBidRequests[0].schain); - if (schain) { - payload.schain = schain; } - } + }; - if (validBidRequests[0].userIdAsEids) { - const eids = { eids: validBidRequests[0].userIdAsEids }; - payload.user.ext = { ...payload.user.ext, ...eids }; - } + if (!isEmpty(blacklist.badv)) { + payload.blacklist = blacklist.badv; + }; if (payload) { switch (bidderName) { case 'pixad': - SYNC_URL = 'https://static.cdn.pixad.com.tr/sync.html'; + SYNC_URL = 'https://static.pixad.com.tr/sync.html'; break; default: SYNC_URL = 'https://cdn.serve.admatic.com.tr/showad/sync.html'; @@ -149,36 +78,12 @@ export const spec = { } }, - getUserSyncs: function (syncOptions, responses, gdprConsent, uspConsent, gppConsent) { - if (!hasSynced && syncOptions.iframeEnabled) { - // data is only assigned if params are available to pass to syncEndpoint - let params = {}; - - if (gdprConsent) { - if (typeof gdprConsent.gdprApplies === 'boolean') { - params['gdpr'] = Number(gdprConsent.gdprApplies); - } - if (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(); - } - - params = Object.keys(params).length ? `?${formatQS(params)}` : ''; - - hasSynced = true; - return { + getUserSyncs: function (syncOptions, responses) { + if (syncOptions.iframeEnabled) { + return [{ type: 'iframe', - url: SYNC_URL + params - }; + url: SYNC_URL + }]; } }, @@ -201,7 +106,6 @@ export const spec = { netRevenue: true, creativeId: bid.creative_id, meta: { - model: bid.mime_type, advertiserDomains: bid && bid.adomain ? bid.adomain : [] }, bidder: bid.bidder, @@ -217,8 +121,6 @@ export const spec = { resbid.vastImpUrl = bid.iurl; } else if (resbid.mediaType === 'banner') { resbid.ad = bid.party_tag; - } else if (resbid.mediaType === 'native') { - resbid.native = interpretNativeAd(bid.party_tag) }; bidResponses.push(resbid); @@ -228,41 +130,6 @@ export const spec = { } }; -var hasSynced = false; - -export function resetUserSync() { - hasSynced = false; -} - -/** - * @param {object} schain object set by Publisher - * @returns {object} OpenRTB SupplyChain object - */ -function mapSchain(schain) { - if (!schain) { - return null; - } - if (!validateSchain(schain)) { - logError('AdMatic: required schain params missing'); - return null; - } - return schain; -} - -/** - * @param {object} schain object set by Publisher - * @returns {object} bool - */ -function validateSchain(schain) { - if (!schain.nodes) { - return false; - } - const requiredFields = ['asi', 'sid', 'hp']; - return schain.nodes.every(node => { - return requiredFields.every(field => node[field]); - }); -} - function isUrl(str) { try { URL(str); @@ -289,11 +156,6 @@ function enrichSlotWithFloors(slot, bidRequest) { videoSizes.forEach(videoSize => slotFloors.video[parseSize(videoSize).toString()] = bidRequest.getFloor({ size: videoSize, mediaType: VIDEO })); } - if (bidRequest.mediaTypes?.native) { - slotFloors.native = {}; - slotFloors.native['*'] = bidRequest.getFloor({ size: '*', mediaType: NATIVE }); - } - if (Object.keys(slotFloors).length > 0) { if (!slot) { slot = {} @@ -333,16 +195,6 @@ function buildRequestObject(bid) { reqObj.type = 'video'; reqObj.mediatype = bid.mediaTypes.video; } - if (bid.mediaTypes?.native) { - reqObj.type = 'native'; - reqObj.size = [{w: 1, h: 1}]; - reqObj.mediatype = bid.mediaTypes.native; - } - - if (deepAccess(bid, 'ortb2Imp.ext')) { - reqObj.ext = bid.ortb2Imp.ext; - } - reqObj.id = getBidIdParameter('bidId', bid); enrichSlotWithFloors(reqObj, bid); @@ -357,11 +209,10 @@ function getSizes(bid) { function concatSizes(bid) { let playerSize = deepAccess(bid, 'mediaTypes.video.playerSize'); let videoSizes = deepAccess(bid, 'mediaTypes.video.sizes'); - let nativeSizes = deepAccess(bid, 'mediaTypes.native.sizes'); let bannerSizes = deepAccess(bid, 'mediaTypes.banner.sizes'); if (isArray(bannerSizes) || isArray(playerSize) || isArray(videoSizes)) { - let mediaTypesSizes = [bannerSizes, videoSizes, nativeSizes, playerSize]; + let mediaTypesSizes = [bannerSizes, videoSizes, playerSize]; return mediaTypesSizes .reduce(function(acc, currSize) { if (isArray(currSize)) { @@ -376,45 +227,6 @@ function concatSizes(bid) { } } -function interpretNativeAd(adm) { - const native = JSON.parse(adm).native; - const result = { - clickUrl: encodeURI(native.link.url), - impressionTrackers: native.imptrackers - }; - native.assets.forEach(asset => { - switch (asset.id) { - case OPENRTB.NATIVE.ASSET_ID.TITLE: - result.title = asset.title.text; - break; - case OPENRTB.NATIVE.ASSET_ID.IMAGE: - result.image = { - url: encodeURI(asset.img.url), - width: asset.img.w, - height: asset.img.h - }; - break; - case OPENRTB.NATIVE.ASSET_ID.ICON: - result.icon = { - url: encodeURI(asset.img.url), - width: asset.img.w, - height: asset.img.h - }; - break; - case OPENRTB.NATIVE.ASSET_ID.BODY: - result.body = asset.data.value; - break; - case OPENRTB.NATIVE.ASSET_ID.SPONSORED: - result.sponsoredBy = asset.data.value; - break; - case OPENRTB.NATIVE.ASSET_ID.CTA: - result.cta = asset.data.value; - break; - } - }); - return result; -} - function _validateId(id) { return (parseInt(id) > 0); } diff --git a/modules/admediaBidAdapter.js b/modules/admediaBidAdapter.js index 5ea3e27b0d9..42593a36159 100644 --- a/modules/admediaBidAdapter.js +++ b/modules/admediaBidAdapter.js @@ -1,12 +1,6 @@ import {registerBidder} from '../src/adapters/bidderFactory.js'; import {BANNER} from '../src/mediaTypes.js'; -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse - */ - const BIDDER_CODE = 'admedia'; const ENDPOINT_URL = 'https://prebid.admedia.com/bidder/'; diff --git a/modules/admixerBidAdapter.js b/modules/admixerBidAdapter.js index f5f0b5bf665..1006fef631c 100644 --- a/modules/admixerBidAdapter.js +++ b/modules/admixerBidAdapter.js @@ -14,7 +14,6 @@ const ALIASES = [ {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: 'admixerwl', endpoint: 'https://inv-nets-adxwl.admixer.com/adxwlprebid.aspx'}, ]; export const spec = { code: BIDDER_CODE, @@ -24,9 +23,7 @@ export const spec = { * Determines whether or not the given bid request is valid. */ isBidRequestValid: function (bid) { - return bid.bidder === 'admixerwl' - ? !!bid.params.clientId && !!bid.params.endpointId - : !!bid.params.zone; + return !!bid.params.zone; }, /** * Make a server request from the list of BidRequests. @@ -76,14 +73,12 @@ export const spec = { validRequest.forEach((bid) => { let imp = {}; Object.keys(bid).forEach(key => imp[key] = bid[key]); - imp.ortb2 && delete imp.ortb2; payload.imps.push(imp); }); - - let urlForRequest = endpointUrl || getEndpointUrl(bidderRequest.bidderCode) return { method: 'POST', - url: bidderRequest.bidderCode === 'admixerwl' ? `${urlForRequest}?client=${payload.imps[0]?.params?.clientId}` : urlForRequest, + url: + endpointUrl || getEndpointUrl(bidderRequest.bidderCode), data: payload, }; }, diff --git a/modules/admixerBidAdapter.md b/modules/admixerBidAdapter.md index 64f8dd64ee4..682f5629115 100644 --- a/modules/admixerBidAdapter.md +++ b/modules/admixerBidAdapter.md @@ -50,48 +50,3 @@ Please use ```admixer``` as the bidder code. }, ]; ``` - -### AdmixerWL Test Parameters -``` - var adUnits = [ - { - code: 'desktop-banner-ad-div', - sizes: [[300, 250]], // a display size - bids: [ - { - bidder: "admixer", - params: { - endpointId: 41512, - clientId: 62 - } - } - ] - },{ - code: 'mobile-banner-ad-div', - sizes: [[300, 50]], // a mobile size - bids: [ - { - bidder: "admixer", - params: { - endpointId: 41512, - clientId: 62 - } - } - ] - },{ - code: 'video-ad', - sizes: [[300, 50]], - mediaType: 'video', - bids: [ - { - bidder: "admixer", - params: { - endpointId: 41512, - clientId: 62 - } - } - ] - }, - ]; -``` - diff --git a/modules/admixerIdSystem.js b/modules/admixerIdSystem.js index cb7248c9537..7fbebecfc12 100644 --- a/modules/admixerIdSystem.js +++ b/modules/admixerIdSystem.js @@ -11,13 +11,6 @@ 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 - * @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 NAME = 'admixerId'; export const storage = getStorageManager({moduleType: MODULE_TYPE_UID, moduleName: NAME}); @@ -79,12 +72,6 @@ export const admixerIdSubmodule = { }; return { callback: resp }; - }, - eids: { - 'admixerId': { - source: 'admixer.net', - atype: 3 - }, } }; function retrieveVisitorId(url, callback) { diff --git a/modules/adnowBidAdapter.js b/modules/adnowBidAdapter.js index 99f56df58b2..f83dbf68a1f 100644 --- a/modules/adnowBidAdapter.js +++ b/modules/adnowBidAdapter.js @@ -9,10 +9,6 @@ const ENDPOINT = 'https://n.ads3-adnow.com/a'; /** * @typedef {object} CommonBidData - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - * @typedef {import('../src/adapters/bidderFactory.js').ServerRequest} ServerRequest - * @typedef {import('../src/adapters/bidderFactory.js').BidderSpec} BidderSpec * * @property {string} requestId The specific BidRequest which this bid is aimed at. * This should match the BidRequest.bidId which this Bid targets. diff --git a/modules/adnuntiusBidAdapter.js b/modules/adnuntiusBidAdapter.js index a498d056513..d1192c2eb64 100644 --- a/modules/adnuntiusBidAdapter.js +++ b/modules/adnuntiusBidAdapter.js @@ -6,165 +6,61 @@ import { getStorageManager } from '../src/storageManager.js'; const BIDDER_CODE = 'adnuntius'; const BIDDER_CODE_DEAL_ALIAS_BASE = 'adndeal'; -const BIDDER_CODE_DEAL_ALIASES = [1, 2, 3, 4, 5].map(num => { - return BIDDER_CODE_DEAL_ALIAS_BASE + num; -}); +const BIDDER_CODE_DEAL_ALIAS_1 = BIDDER_CODE_DEAL_ALIAS_BASE + '1'; +const BIDDER_CODE_DEAL_ALIAS_2 = BIDDER_CODE_DEAL_ALIAS_BASE + '2'; +const BIDDER_CODE_DEAL_ALIAS_3 = BIDDER_CODE_DEAL_ALIAS_BASE + '3'; +const BIDDER_CODE_DEAL_ALIAS_4 = BIDDER_CODE_DEAL_ALIAS_BASE + '4'; +const BIDDER_CODE_DEAL_ALIAS_5 = BIDDER_CODE_DEAL_ALIAS_BASE + '5'; const ENDPOINT_URL = 'https://ads.adnuntius.delivery/i'; const ENDPOINT_URL_EUROPE = 'https://europe.delivery.adnuntius.com/i'; const GVLID = 855; const DEFAULT_VAST_VERSION = 'vast4' const MAXIMUM_DEALS_LIMIT = 5; -const VALID_BID_TYPES = ['netBid', 'grossBid']; -const META_DATA_KEY = 'adn.metaData'; -export const misc = { - getUnixTimestamp: function (addDays, asMinutes) { - const multiplication = addDays / (asMinutes ? 1440 : 1); - return Date.now() + (addDays && addDays > 0 ? (1000 * 60 * 60 * 24 * multiplication) : 0); - } -}; - -const storageTool = (function () { - const storage = getStorageManager({ bidderCode: BIDDER_CODE }); - let metaInternal; - - const getMetaInternal = function () { - if (!storage.localStorageIsEnabled()) { - return []; - } - - let parsedJson; - try { - parsedJson = JSON.parse(storage.getDataFromLocalStorage(META_DATA_KEY)); - } catch (e) { - return []; - } - - let filteredEntries = parsedJson ? parsedJson.filter((datum) => { - if (datum.key === 'voidAuIds' && Array.isArray(datum.value)) { - return true; - } - return datum.key && datum.value && datum.exp && datum.exp > misc.getUnixTimestamp(); - }) : []; - const voidAuIdsEntry = filteredEntries.find(entry => entry.key === 'voidAuIds'); - if (voidAuIdsEntry) { - const now = misc.getUnixTimestamp(); - voidAuIdsEntry.value = voidAuIdsEntry.value.filter(voidAuId => voidAuId.auId && voidAuId.exp > now); - if (!voidAuIdsEntry.value.length) { - filteredEntries = filteredEntries.filter(entry => entry.key !== 'voidAuIds'); - } - } - return filteredEntries; - }; - - const setMetaInternal = function (apiResponse) { - if (!storage.localStorageIsEnabled()) { - return; - } - - const updateVoidAuIds = function (currentVoidAuIds, auIdsAsString) { - const newAuIds = isStr(auIdsAsString) ? auIdsAsString.split(';') : []; - const notNewExistingAuIds = currentVoidAuIds.filter(auIdObj => { - return newAuIds.indexOf(auIdObj.value) < -1; - }) || []; - const oneDayFromNow = misc.getUnixTimestamp(1); - const apiIdsArray = newAuIds.map(auId => { - return { exp: oneDayFromNow, auId: auId }; - }) || []; - return notNewExistingAuIds.concat(apiIdsArray) || []; - } +const checkSegment = function (segment) { + if (isStr(segment)) return segment; + if (segment.id) return segment.id +} - const metaAsObj = getMetaInternal().reduce((a, entry) => ({ ...a, [entry.key]: { value: entry.value, exp: entry.exp } }), {}); - for (const key in apiResponse) { - if (key !== 'voidAuIds') { - metaAsObj[key] = { - value: apiResponse[key], - exp: misc.getUnixTimestamp(100) - } - } - } - const currentAuIds = updateVoidAuIds(metaAsObj.voidAuIds || [], apiResponse.voidAuIds); - if (currentAuIds.length > 0) { - metaAsObj.voidAuIds = { value: currentAuIds }; - } - const metaDataForSaving = Object.entries(metaAsObj).map((entrySet) => { - if (entrySet[0] === 'voidAuIds') { - return { - key: entrySet[0], - value: entrySet[1].value - }; - } - return { - key: entrySet[0], - value: entrySet[1].value, - exp: entrySet[1].exp +const getSegmentsFromOrtb = function (ortb2) { + const userData = deepAccess(ortb2, 'user.data'); + let segments = []; + if (userData) { + userData.forEach(userdat => { + if (userdat.segment) { + segments.push(...userdat.segment.filter(checkSegment).map(checkSegment)); } }); - storage.setDataInLocalStorage(META_DATA_KEY, JSON.stringify(metaDataForSaving)); - }; - - const getUsi = function (meta, ortb2) { - let usi = (meta && meta.usi) ? meta.usi : false; - if (ortb2 && ortb2.user && ortb2.user.id) { - usi = ortb2.user.id - } - return usi; } + return segments +} - const getSegmentsFromOrtb = function (ortb2) { - const userData = deepAccess(ortb2, 'user.data'); - let segments = []; - if (userData) { - userData.forEach(userdat => { - if (userdat.segment) { - segments.push(...userdat.segment.map((segment) => { - if (isStr(segment)) return segment; - if (isStr(segment.id)) return segment.id; - }).filter((seg) => !!seg)); - } - }); - } - return segments +const handleMeta = function () { + const storage = getStorageManager({ bidderCode: BIDDER_CODE }) + let adnMeta = null + if (storage.localStorageIsEnabled()) { + adnMeta = JSON.parse(storage.getDataFromLocalStorage('adn.metaData')) } + return (adnMeta !== null) ? adnMeta.reduce((acc, cur) => { return { ...acc, [cur.key]: cur.value } }, {}) : {} +} - return { - refreshStorage: function (bidderRequest) { - const ortb2 = bidderRequest.ortb2 || {}; - metaInternal = getMetaInternal().reduce((a, entry) => ({ ...a, [entry.key]: entry.value }), {}); - metaInternal.usi = getUsi(metaInternal, ortb2); - if (!metaInternal.usi) { - delete metaInternal.usi; - } - if (metaInternal.voidAuIds) { - metaInternal.voidAuIdsArray = metaInternal.voidAuIds.map((voidAuId) => { - return voidAuId.auId; - }); - } - metaInternal.segments = getSegmentsFromOrtb(ortb2); - }, - saveToStorage: function (serverData) { - setMetaInternal(serverData); - }, - getUrlRelatedData: function () { - const { segments, usi, voidAuIdsArray } = metaInternal; - return { segments, usi, voidAuIdsArray }; - }, - getPayloadRelatedData: function () { - const { segments, usi, userId, voidAuIdsArray, voidAuIds, ...payloadRelatedData } = metaInternal; - return payloadRelatedData; - } - }; -})(); - -const validateBidType = function (bidTypeOption) { - return VALID_BID_TYPES.indexOf(bidTypeOption || '') > -1 ? bidTypeOption : 'bid'; +const getUsi = function (meta, ortb2, bidderRequest) { + let usi = (meta !== null && meta.usi) ? meta.usi : false; + if (ortb2 && ortb2.user && ortb2.user.id) { usi = ortb2.user.id } + return usi } const AU_ID_REGEX = new RegExp('^[0-9A-Fa-f]{1,20}$'); export const spec = { code: BIDDER_CODE, - aliases: BIDDER_CODE_DEAL_ALIASES, + aliases: [ + BIDDER_CODE_DEAL_ALIAS_1, + BIDDER_CODE_DEAL_ALIAS_2, + BIDDER_CODE_DEAL_ALIAS_3, + BIDDER_CODE_DEAL_ALIAS_4, + BIDDER_CODE_DEAL_ALIAS_5 + ], gvlid: GVLID, supportedMediaTypes: [BANNER, VIDEO], isBidRequestValid: function (bid) { @@ -174,38 +70,34 @@ export const spec = { }, buildRequests: function (validBidRequests, bidderRequest) { - const queryParamsAndValues = []; - queryParamsAndValues.push('tzo=' + new Date().getTimezoneOffset()) - queryParamsAndValues.push('format=json') + const networks = {}; + const bidRequests = {}; + const requests = []; + const request = []; + const ortb2 = bidderRequest.ortb2 || {}; + const bidderConfig = config.getConfig(); + + const adnMeta = handleMeta() + const usi = getUsi(adnMeta, ortb2, bidderRequest) + const segments = getSegmentsFromOrtb(ortb2); + const tzo = new Date().getTimezoneOffset(); const gdprApplies = deepAccess(bidderRequest, 'gdprConsent.gdprApplies'); const consentString = deepAccess(bidderRequest, 'gdprConsent.consentString'); - if (gdprApplies !== undefined) { - const flag = gdprApplies ? '1' : '0' - queryParamsAndValues.push('consentString=' + consentString); - queryParamsAndValues.push('gdpr=' + flag); - } - - storageTool.refreshStorage(bidderRequest); - const urlRelatedMetaData = storageTool.getUrlRelatedData(); - if (urlRelatedMetaData.segments.length > 0) queryParamsAndValues.push('segments=' + urlRelatedMetaData.segments.join(',')); - if (urlRelatedMetaData.usi) queryParamsAndValues.push('userId=' + urlRelatedMetaData.usi); - - const bidderConfig = config.getConfig(); - if (bidderConfig.useCookie === false) queryParamsAndValues.push('noCookies=true'); - if (bidderConfig.maxDeals > 0) queryParamsAndValues.push('ds=' + Math.min(bidderConfig.maxDeals, MAXIMUM_DEALS_LIMIT)); - - const bidRequests = {}; - const networks = {}; + request.push('tzo=' + tzo) + request.push('format=json') + if (gdprApplies !== undefined) request.push('consentString=' + consentString); + if (segments.length > 0) request.push('segments=' + segments.join(',')); + if (usi) request.push('userId=' + usi); + if (bidderConfig.useCookie === false) request.push('noCookies=true'); + if (bidderConfig.maxDeals > 0) request.push('ds=' + Math.min(bidderConfig.maxDeals, MAXIMUM_DEALS_LIMIT)); for (let i = 0; i < validBidRequests.length; i++) { - const bid = validBidRequests[i]; - if ((urlRelatedMetaData.voidAuIdsArray && (urlRelatedMetaData.voidAuIdsArray.indexOf(bid.params.auId) > -1 || urlRelatedMetaData.voidAuIdsArray.indexOf(bid.params.auId.padStart(16, '0')) > -1))) { - // This auId is void. Do NOT waste time and energy sending a request to the server - continue; - } - + const bid = validBidRequests[i] let network = bid.params.network || 'network'; + const maxDeals = Math.max(0, Math.min(bid.params.maxDeals || 0, MAXIMUM_DEALS_LIMIT)); + const targeting = bid.params.targeting || {}; + if (bid.mediaTypes && bid.mediaTypes.video && bid.mediaTypes.video.context !== 'outstream') { network += '_video' } @@ -216,31 +108,21 @@ export const spec = { networks[network] = networks[network] || {}; networks[network].adUnits = networks[network].adUnits || []; if (bidderRequest && bidderRequest.refererInfo) networks[network].context = bidderRequest.refererInfo.page; - - const payloadRelatedData = storageTool.getPayloadRelatedData(); - if (Object.keys(payloadRelatedData).length > 0) { - networks[network].metaData = payloadRelatedData; - } - - const targeting = bid.params.targeting || {}; - const adUnit = { ...targeting, auId: bid.params.auId, targetId: bid.params.targetId || bid.bidId }; - const maxDeals = Math.max(0, Math.min(bid.params.maxDeals || 0, MAXIMUM_DEALS_LIMIT)); - if (maxDeals > 0) { - adUnit.maxDeals = maxDeals; - } + if (adnMeta) networks[network].metaData = adnMeta; + const adUnit = { ...targeting, auId: bid.params.auId, targetId: bid.bidId, maxDeals: maxDeals } if (bid.mediaTypes && bid.mediaTypes.banner && bid.mediaTypes.banner.sizes) adUnit.dimensions = bid.mediaTypes.banner.sizes networks[network].adUnits.push(adUnit); } - const requests = []; const networkKeys = Object.keys(networks) for (let j = 0; j < networkKeys.length; j++) { const network = networkKeys[j]; - if (network.indexOf('_video') > -1) { queryParamsAndValues.push('tt=' + DEFAULT_VAST_VERSION) } + const networkRequest = [...request] + if (network.indexOf('_video') > -1) { networkRequest.push('tt=' + DEFAULT_VAST_VERSION) } const requestURL = gdprApplies ? ENDPOINT_URL_EUROPE : ENDPOINT_URL requests.push({ method: 'POST', - url: requestURL + '?' + queryParamsAndValues.join('&'), + url: requestURL + '?' + networkRequest.join('&'), data: JSON.stringify(networks[network]), bid: bidRequests[network] }); @@ -250,20 +132,8 @@ export const spec = { }, interpretResponse: function (serverResponse, bidRequest) { - if (serverResponse.body.metaData) { - storageTool.saveToStorage(serverResponse.body.metaData); - } const adUnits = serverResponse.body.adUnits; - let validatedBidType = validateBidType(config.getConfig().bidType); - if (bidRequest.bid) { - bidRequest.bid.forEach(b => { - if (b.params && b.params.bidType) { - validatedBidType = validateBidType(b.params.bidType); - } - }); - } - function buildAdResponse(bidderCode, ad, adUnit, dealCount) { const destinationUrls = ad.destinationUrls || {}; const advertiserDomains = []; @@ -273,7 +143,7 @@ export const spec = { const adResponse = { bidderCode: bidderCode, requestId: adUnit.targetId, - cpm: ad[validatedBidType] ? ad[validatedBidType].amount * 1000 : 0, + cpm: (ad.bid) ? ad.bid.amount * 1000 : 0, width: Number(ad.creativeWidth), height: Number(ad.creativeHeight), creativeId: ad.creativeId, @@ -308,7 +178,7 @@ export const spec = { const hasBidAdUnits = adUnits.filter((au) => { const bid = bidsById[au.targetId]; - if (bid && bid.bidder && BIDDER_CODE_DEAL_ALIASES.indexOf(bid.bidder) < 0) { + if (bid && bid.bidder === BIDDER_CODE) { return au.matchedAdCount > 0; } else { // We do NOT accept bids when using this adaptor via one of the diff --git a/modules/adnuntiusRtdProvider.js b/modules/adnuntiusRtdProvider.js index 1d5d639aa55..9234a30aa33 100644 --- a/modules/adnuntiusRtdProvider.js +++ b/modules/adnuntiusRtdProvider.js @@ -5,10 +5,6 @@ import { ajax } from '../src/ajax.js'; import { config as sourceConfig } from '../src/config.js'; -/** - * @typedef {import('../modules/rtdModule/index.js').RtdSubmodule} RtdSubmodule - */ - const GVLID = 855; function init(config, userConsent) { diff --git a/modules/adotBidAdapter.js b/modules/adotBidAdapter.js index b48a7ec43b0..c34af4d3d17 100644 --- a/modules/adotBidAdapter.js +++ b/modules/adotBidAdapter.js @@ -9,7 +9,6 @@ import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; const BIDDER_CODE = 'adot'; const ADAPTER_VERSION = 'v2.0.0'; -const GVLID = 272; const BID_METHOD = 'POST'; const BIDDER_URL = 'https://dsp.adotmob.com/headerbidding{PUBLISHER_PATH}/bidrequest'; const REQUIRED_VIDEO_PARAMS = ['mimes', 'protocols']; @@ -636,8 +635,7 @@ export const spec = { isBidRequestValid, buildRequests, interpretResponse, - getFloor, - gvlid: GVLID + getFloor }; registerBidder(spec); diff --git a/modules/adpod.js b/modules/adpod.js index f6d8309cd9f..4ab8e4e5ab9 100644 --- a/modules/adpod.js +++ b/modules/adpod.js @@ -13,6 +13,7 @@ */ import { + compareOn, deepAccess, generateUUID, groupBy, @@ -27,6 +28,7 @@ import { import { addBidToAuction, AUCTION_IN_PROGRESS, + doCallbacksIfTimedout, getPriceByGranularity, getPriceGranularity } from '../src/auction.js'; @@ -210,6 +212,9 @@ function firePrebidCacheCall(auctionInstance, bidList, afterBidAdded) { store(bidList, function (error, cacheIds) { if (error) { logWarn(`Failed to save to the video cache: ${error}. Video bid(s) must be discarded.`); + for (let i = 0; i < bidList.length; i++) { + doCallbacksIfTimedout(auctionInstance, bidList[i]); + } } 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 @@ -319,7 +324,7 @@ export function checkAdUnitSetupHook(fn, adUnits) { * @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; let bidDuration = deepAccess(bidResponse, 'video.durationSeconds'); @@ -590,23 +595,6 @@ function getAdPodAdUnits(codes) { .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 diff --git a/modules/adqueryBidAdapter.js b/modules/adqueryBidAdapter.js index d5a8ba6da84..8a953f0d97f 100644 --- a/modules/adqueryBidAdapter.js +++ b/modules/adqueryBidAdapter.js @@ -1,14 +1,7 @@ import {registerBidder} from '../src/adapters/bidderFactory.js'; import {BANNER} from '../src/mediaTypes.js'; -import {buildUrl, logInfo, parseSizesInput, triggerPixel} from '../src/utils.js'; - -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - * @typedef {import('../src/adapters/bidderFactory.js').ServerRequest} ServerRequest - * @typedef {import('../src/adapters/bidderFactory.js').BidderSpec} BidderSpec - * @typedef {import('../src/adapters/bidderFactory.js').TimedOutBid} TimedOutBid - */ +import { logInfo, buildUrl, triggerPixel, parseSizesInput } from '../src/utils.js'; +import { getStorageManager } from '../src/storageManager.js'; const ADQUERY_GVLID = 902; const ADQUERY_BIDDER_CODE = 'adquery'; @@ -18,6 +11,7 @@ const ADQUERY_USER_SYNC_DOMAIN = ADQUERY_BIDDER_DOMAIN_PROTOCOL + '://' + ADQUER const ADQUERY_DEFAULT_CURRENCY = 'PLN'; const ADQUERY_NET_REVENUE = true; const ADQUERY_TTL = 360; +const storage = getStorageManager({bidderCode: ADQUERY_BIDDER_CODE}); /** @type {BidderSpec} */ export const spec = { @@ -25,8 +19,7 @@ export const spec = { gvlid: ADQUERY_GVLID, supportedMediaTypes: [BANNER], - /** - * f + /** f * @param {object} bid * @return {boolean} */ @@ -65,6 +58,7 @@ export const spec = { logInfo(request); logInfo(response); + let qid = null; const res = response && response.body && response.body.data; let bidResponses = []; @@ -93,6 +87,17 @@ export const spec = { bidResponses.push(bidResponse); logInfo('bidResponses', bidResponses); + if (res && res.qid) { + if (storage.getDataFromLocalStorage('qid')) { + qid = storage.getDataFromLocalStorage('qid'); + if (qid && qid.includes('%7B%22')) { + storage.setDataInLocalStorage('qid', res.qid); + } + } else { + storage.setDataInLocalStorage('qid', res.qid); + } + } + return bidResponses; }, @@ -125,12 +130,8 @@ export const spec = { */ onBidWon: (bid) => { logInfo('onBidWon', bid); - const bidString = JSON.stringify(bid); - let copyOfBid = JSON.parse(bidString); - delete copyOfBid.ad; - const shortBidString = JSON.stringify(bid); - const encodedBuf = window.btoa(shortBidString); + const encodedBuf = window.btoa(bidString); let params = { q: encodedBuf, @@ -186,29 +187,10 @@ export const spec = { url: syncUrl }]; } -}; +}; function buildRequest(validBidRequests, bidderRequest) { let bid = validBidRequests; - logInfo('buildRequest: ', bid); - - let userId = null; - if (window.qid) { - userId = window.qid; - } - - if (bid.userId && bid.userId.qid) { - userId = bid.userId.qid - } - - if (!userId) { - // onetime User ID - const ramdomValues = Array.from(window.crypto.getRandomValues(new Uint32Array(4))); - userId = ramdomValues.map(val => val.toString(36)).join('').substring(0, 20); - logInfo('generated onetime User ID: ', userId); - window.qid = userId; - } - let pageUrl = ''; if (bidderRequest && bidderRequest.refererInfo) { pageUrl = bidderRequest.refererInfo.page || ''; @@ -217,10 +199,11 @@ function buildRequest(validBidRequests, bidderRequest) { return { v: '$prebid.version$', placementCode: bid.params.placementId, - auctionId: null, + // TODO: fix auctionId leak: https://github.com/prebid/Prebid.js/issues/9781 + auctionId: bid.auctionId, type: bid.params.type, adUnitCode: bid.adUnitCode, - bidQid: userId, + bidQid: storage.getDataFromLocalStorage('qid') || null, bidId: bid.bidId, bidder: bid.bidder, bidPageUrl: pageUrl, diff --git a/modules/adqueryIdSystem.js b/modules/adqueryIdSystem.js index eb00011593d..5171802caba 100644 --- a/modules/adqueryIdSystem.js +++ b/modules/adqueryIdSystem.js @@ -8,15 +8,9 @@ import {ajax} from '../src/ajax.js'; import {getStorageManager} from '../src/storageManager.js'; import {submodule} from '../src/hook.js'; -import {isFn, isPlainObject, isStr, logError, logInfo} from '../src/utils.js'; +import { isFn, isStr, isPlainObject, logError } from '../src/utils.js'; import {MODULE_TYPE_UID} from '../src/activities/modules.js'; -/** - * @typedef {import('../modules/userId/index.js').Submodule} Submodule - * @typedef {import('../modules/userId/index.js').SubmoduleConfig} SubmoduleConfig - * @typedef {import('../modules/userId/index.js').IdResponse} IdResponse - */ - const MODULE_NAME = 'qid'; const AU_GVLID = 902; @@ -57,7 +51,11 @@ export const adqueryIdSubmodule = { * @returns {{qid:Object}} */ decode(value) { - return {qid: value} + let qid = storage.getDataFromLocalStorage('qid'); + if (isStr(qid)) { + return {qid: qid}; + } + return (value && typeof value['qid'] === 'string') ? { 'qid': value['qid'] } : undefined; }, /** * performs action to obtain id and return a value in the callback's response argument @@ -66,59 +64,40 @@ export const adqueryIdSubmodule = { * @returns {IdResponse|undefined} */ getId(config) { - logInfo('adqueryIdSubmodule getId'); if (!isPlainObject(config.params)) { config.params = {}; } - - const url = paramOrDefault( - config.params.url, + const url = paramOrDefault(config.params.url, `https://bidder.adquery.io/prebid/qid`, - config.params.urlArg - ); + config.params.urlArg); const resp = function (callback) { - let qid = window.qid; - - if (!qid) { - const ramdomValues = Array.from(window.crypto.getRandomValues(new Uint32Array(4))); - qid = ramdomValues.map(val => val.toString(36)).join('').substring(0, 20); - - logInfo('adqueryIdSubmodule ID QID GENERTAED:', qid); - } - logInfo('adqueryIdSubmodule ID QID:', qid); - - const callbacks = { - success: response => { - let responseObj; - if (response) { - try { - responseObj = JSON.parse(response); - } catch (error) { - logError(error); + let qid = storage.getDataFromLocalStorage('qid'); + if (isStr(qid)) { + const responseObj = {qid: qid}; + callback(responseObj); + } else { + const callbacks = { + success: response => { + let responseObj; + if (response) { + try { + responseObj = JSON.parse(response); + } catch (error) { + logError(error); + } } + callback(responseObj); + }, + error: error => { + logError(`${MODULE_NAME}: ID fetch encountered an error`, error); + callback(); } - if (responseObj.qid) { - let myQid = responseObj.qid; - storage.setDataInLocalStorage('qid', myQid); - return callback(myQid); - } - callback(); - }, - error: error => { - logError(`${MODULE_NAME}: ID fetch encountered an error`, error); - callback(); - } - }; - ajax(url + '?qid=' + qid, callbacks, undefined, {method: 'GET'}); + }; + ajax(url, callbacks, undefined, {method: 'GET'}); + } }; return {callback: resp}; - }, - eids: { - 'qid': { - source: 'adquery.io', - atype: 1 - }, } }; diff --git a/modules/adrelevantisBidAdapter.js b/modules/adrelevantisBidAdapter.js index 68cd859e24e..cf785a1fc87 100644 --- a/modules/adrelevantisBidAdapter.js +++ b/modules/adrelevantisBidAdapter.js @@ -1,5 +1,8 @@ import {Renderer} from '../src/Renderer.js'; import { + chunk, + convertCamelToUnderscore, + convertTypes, createTrackPixelHtml, deepAccess, deepClone, @@ -18,15 +21,7 @@ import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js'; import {find, includes} from '../src/polyfill.js'; import {INSTREAM, OUTSTREAM} from '../src/video.js'; import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; -import {getANKeywordParam, transformBidderParamKeywords} from '../libraries/appnexusUtils/anKeywords.js'; -import {convertCamelToUnderscore} from '../libraries/appnexusUtils/anUtils.js'; -import {convertTypes} from '../libraries/transformParamsUtils/convertTypes.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 {getANKeywordParam, transformBidderParamKeywords} from '../libraries/appnexusKeywords/anKeywords.js'; const BIDDER_CODE = 'adrelevantis'; const URL = 'https://ssp.adrelevantis.com/prebid'; @@ -602,8 +597,6 @@ function parseMediaType(rtbBid) { const adType = rtbBid.ad_type; if (adType === VIDEO) { return VIDEO; - } else if (adType === NATIVE) { - return NATIVE; } else { return BANNER; } diff --git a/modules/adriverBidAdapter.js b/modules/adriverBidAdapter.js index 5bce315f572..1af0cffa700 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} from '../src/utils.js'; +import { logInfo, getWindowLocation, getBidIdParameter, _each } from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { getStorageManager } from '../src/storageManager.js'; diff --git a/modules/adriverIdSystem.js b/modules/adriverIdSystem.js index 1da75f2063d..c04ebf48028 100644 --- a/modules/adriverIdSystem.js +++ b/modules/adriverIdSystem.js @@ -11,13 +11,6 @@ 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 - * @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 = 'adriverId'; export const storage = getStorageManager({moduleType: MODULE_TYPE_UID, moduleName: MODULE_NAME}); diff --git a/modules/adsinteractiveBidAdapter.js b/modules/adsinteractiveBidAdapter.js index ad6bdfeb299..304b8bcade0 100644 --- a/modules/adsinteractiveBidAdapter.js +++ b/modules/adsinteractiveBidAdapter.js @@ -6,14 +6,12 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER } from '../src/mediaTypes.js'; const ADSINTERACTIVE_CODE = 'adsinteractive'; -const USER_SYNC_URL_IMAGE = 'https://sync.adsinteractive.com/img'; -const USER_SYNC_URL_IFRAME = 'https://sync.adsinteractive.com/sync'; -const GVLID = 1212; +const USER_SYNC_URL_IMAGE = 'https://pb.adsinteractive.com/img'; +const USER_SYNC_URL_IFRAME = 'https://pb.adsinteractive.com/sync'; export const spec = { code: ADSINTERACTIVE_CODE, supportedMediaTypes: [BANNER], - gvlid: GVLID, isBidRequestValid: (bid) => { return ( diff --git a/modules/adstirBidAdapter.js b/modules/adstirBidAdapter.js deleted file mode 100644 index 4b22d568785..00000000000 --- a/modules/adstirBidAdapter.js +++ /dev/null @@ -1,91 +0,0 @@ -import * as utils from '../src/utils.js'; -import { config } from '../src/config.js'; -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { BANNER } from '../src/mediaTypes.js'; - -const BIDDER_CODE = 'adstir'; -const ENDPOINT = 'https://ad.ad-stir.com/prebid' - -export const spec = { - code: BIDDER_CODE, - supportedMediaTypes: [BANNER], - - isBidRequestValid: function (bid) { - return !!(utils.isStr(bid.params.appId) && !utils.isEmptyStr(bid.params.appId) && utils.isInteger(bid.params.adSpaceNo)); - }, - - buildRequests: function (validBidRequests, bidderRequest) { - const sua = utils.deepAccess(validBidRequests[0], 'ortb2.device.sua', null); - - const requests = validBidRequests.map((r) => { - return { - method: 'POST', - url: ENDPOINT, - data: JSON.stringify({ - appId: r.params.appId, - adSpaceNo: r.params.adSpaceNo, - auctionId: r.auctionId, - transactionId: r.transactionId, - bidId: r.bidId, - mediaTypes: r.mediaTypes, - sizes: r.sizes, - ref: { - page: bidderRequest.refererInfo.page, - tloc: bidderRequest.refererInfo.topmostLocation, - referrer: bidderRequest.refererInfo.ref, - topurl: config.getConfig('pageUrl') ? false : bidderRequest.refererInfo.reachedTop, - }, - sua, - gdpr: utils.deepAccess(bidderRequest, 'gdprConsent.gdprApplies', false), - usp: (bidderRequest.uspConsent || '1---') !== '1---', - eids: utils.deepAccess(r, 'userIdAsEids', []), - schain: serializeSchain(utils.deepAccess(r, 'schain', null)), - pbVersion: '$prebid.version$', - }), - } - }); - - return requests; - }, - - interpretResponse: function (serverResponse, bidRequest) { - const seatbid = serverResponse.body.seatbid; - if (!utils.isArray(seatbid)) { - return []; - } - const bids = []; - seatbid.forEach((b) => { - const bid = b.bid || null; - if (!bid) { - return; - } - bids.push(bid); - }); - return bids; - }, -} - -function serializeSchain(schain) { - if (!schain) { - return null; - } - - let serializedSchain = `${schain.ver},${schain.complete}`; - - schain.nodes.map(node => { - serializedSchain += `!${encodeURIComponentForRFC3986(node.asi || '')},`; - serializedSchain += `${encodeURIComponentForRFC3986(node.sid || '')},`; - serializedSchain += `${encodeURIComponentForRFC3986(node.hp || '')},`; - serializedSchain += `${encodeURIComponentForRFC3986(node.rid || '')},`; - serializedSchain += `${encodeURIComponentForRFC3986(node.name || '')},`; - serializedSchain += `${encodeURIComponentForRFC3986(node.domain || '')}`; - }); - - return serializedSchain; -} - -function encodeURIComponentForRFC3986(str) { - return encodeURIComponent(str).replace(/[!'()*]/g, c => `%${c.charCodeAt(0).toString(16)}`); -} - -registerBidder(spec); diff --git a/modules/adstirBidAdapter.md b/modules/adstirBidAdapter.md deleted file mode 100644 index 7485375a09d..00000000000 --- a/modules/adstirBidAdapter.md +++ /dev/null @@ -1,36 +0,0 @@ -# Overview - -``` -Module Name: adstir Bidder Adapter -Module Type: Bidder Adapter -Maintainer: support@ad-stir.com -``` - -# Description - -Module that connects to adstir's demand sources - -# Test Parameters - -``` - var adUnits = [ - // Banner adUnit - { - code: 'test-div', - mediaTypes: { - banner: { - sizes: [[300, 250]], - } - }, - bids: [ - { - bidder: 'adstir', - params: { - appId: 'TEST-MEDIA', - adSpaceNo: 1, - } - } - ] - } - ]; -``` diff --git a/modules/adtargetBidAdapter.js b/modules/adtargetBidAdapter.js index a1dec5a420f..89ba4878acf 100644 --- a/modules/adtargetBidAdapter.js +++ b/modules/adtargetBidAdapter.js @@ -1,9 +1,8 @@ -import {_map, deepAccess, flatten, isArray, logError, parseSizesInput} from '../src/utils.js'; +import {_map, chunk, 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 {find} from '../src/polyfill.js'; -import {chunk} from '../libraries/chunk/chunk.js'; const ENDPOINT = 'https://ghb.console.adtarget.com.tr/v2/auction/'; const BIDDER_CODE = 'adtarget'; diff --git a/modules/adtelligentBidAdapter.js b/modules/adtelligentBidAdapter.js index cadba499b5c..cbba1bb49eb 100644 --- a/modules/adtelligentBidAdapter.js +++ b/modules/adtelligentBidAdapter.js @@ -1,15 +1,9 @@ -import {_map, deepAccess, flatten, isArray, parseSizesInput} from '../src/utils.js'; +import {_map, chunk, convertTypes, 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 {find} from '../src/polyfill.js'; -import {convertTypes} from '../libraries/transformParamsUtils/convertTypes.js'; -import {chunk} from '../libraries/chunk/chunk.js'; - -/** - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - */ const subdomainSuffixes = ['', 1, 2]; const AUCTION_PATH = '/v2/auction/'; @@ -21,12 +15,17 @@ const HOST_GETTERS = { return 'ghb' + subdomainSuffixes[num++ % subdomainSuffixes.length] + '.adtelligent.com'; } }()), + navelix: () => 'ghb.hb.navelix.com', + appaloosa: () => 'ghb.hb.appaloosa.media', + onefiftytwomedia: () => 'ghb.ads.152media.com', + bidsxchange: () => 'ghb.hbd.bidsxchange.com', streamkey: () => 'ghb.hb.streamkey.net', janet: () => 'ghb.bidder.jmgads.com', + pgam: () => 'ghb.pgamssp.com', ocm: () => 'ghb.cenarius.orangeclickmedia.com', + vidcrunchllc: () => 'ghb.platform.vidcrunch.com', '9dotsmedia': () => 'ghb.platform.audiodots.com', - copper6: () => 'ghb.app.copper6.com', - indicue: () => 'ghb.console.indicue.com', + copper6: () => 'ghb.app.copper6.com' } const getUri = function (bidderCode) { let bidderWithoutSuffix = bidderCode.split('_')[0]; @@ -43,13 +42,18 @@ export const spec = { code: BIDDER_CODE, gvlid: 410, aliases: [ + 'onefiftytwomedia', + 'appaloosa', + 'bidsxchange', 'streamkey', 'janet', { code: 'selectmedia', gvlid: 775 }, + { code: 'navelix', gvlid: 380 }, + 'pgam', { code: 'ocm', gvlid: 1148 }, + { code: 'vidcrunchllc', gvlid: 1145 }, '9dotsmedia', 'copper6', - 'indicue', ], supportedMediaTypes: [VIDEO, BANNER], isBidRequestValid: function (bid) { @@ -196,14 +200,6 @@ function bidToTag(bidRequests, adapterRequest) { tag.DMPId = window.adtDmp.getUID(); } - if (adapterRequest.gppConsent) { - tag.GPP = adapterRequest.gppConsent.gppString; - tag.GPPSid = adapterRequest.gppConsent.applicableSections?.toString(); - } else if (adapterRequest.ortb2?.regs?.gpp) { - tag.GPP = adapterRequest.ortb2.regs.gpp; - tag.GPPSid = adapterRequest.ortb2.regs.gpp_sid; - } - // end publisher env const bids = []; diff --git a/modules/adtelligentIdSystem.js b/modules/adtelligentIdSystem.js index 76713f29775..fb3b5f6fe2a 100644 --- a/modules/adtelligentIdSystem.js +++ b/modules/adtelligentIdSystem.js @@ -8,13 +8,6 @@ import * as ajax from '../src/ajax.js'; import { submodule } from '../src/hook.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 gvlid = 410; const moduleName = 'adtelligent'; const syncUrl = 'https://idrs.adtelligent.com/get'; @@ -92,12 +85,6 @@ export const adtelligentIdModule = { } } - }, - eids: { - 'adtelligentId': { - source: 'adtelligent.com', - atype: 3 - }, } }; diff --git a/modules/aduptechBidAdapter.js b/modules/aduptechBidAdapter.js index fdc1249ded4..1ea5f1a0096 100644 --- a/modules/aduptechBidAdapter.js +++ b/modules/aduptechBidAdapter.js @@ -1,13 +1,7 @@ -import {deepClone, isArray, isBoolean, isEmpty, isFn, isPlainObject} from '../src/utils.js'; +import {deepClone, getAdUnitSizes, 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'; - -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').BidderRequest} BidderRequest - */ export const BIDDER_CODE = 'aduptech'; export const GVLID = 647; diff --git a/modules/adxcgBidAdapter.js b/modules/adxcgBidAdapter.js index dda88575ff5..5930f3adb67 100644 --- a/modules/adxcgBidAdapter.js +++ b/modules/adxcgBidAdapter.js @@ -1,65 +1,307 @@ // jshint esversion: 6, es3: false, node: true -import { ortbConverter } from '../libraries/ortbConverter/converter.js'; -import { convertTypes } from '../libraries/transformParamsUtils/convertTypes.js'; -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; +'use strict'; + +import {registerBidder} from '../src/adapters/bidderFactory.js'; +import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js'; import { + _map, + deepAccess, + deepSetValue, + getDNT, isArray, + isPlainObject, + isStr, + mergeDeep, + parseSizesInput, replaceAuctionPrice, - triggerPixel, - logMessage, - deepSetValue, - getBidIdParameter + triggerPixel } from '../src/utils.js'; -import { config } from '../src/config.js'; +import {config} from '../src/config.js'; +import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; + +const { getConfig } = config; const BIDDER_CODE = 'adxcg'; const SECURE_BID_URL = 'https://pbc.adxcg.net/rtb/ortb/pbc?adExchangeId=1'; -const DEFAULT_CURRENCY = 'EUR'; -const KNOWN_PARAMS = ['cp', 'ct', 'cf', 'battr', 'deals']; -const DEFAULT_TMAX = 500; +const NATIVE_ASSET_IDS = { 0: 'title', 2: 'icon', 3: 'image', 5: 'sponsoredBy', 4: 'body', 1: 'cta' }; +const NATIVE_PARAMS = { + title: { + id: 0, + name: 'title' + }, + icon: { + id: 2, + type: 1, + name: 'img' + }, + image: { + id: 3, + type: 3, + name: 'img' + }, + sponsoredBy: { + id: 5, + name: 'data', + type: 1 + }, + body: { + id: 4, + name: 'data', + type: 2 + }, + cta: { + id: 1, + type: 12, + name: 'data' + } +}; -/** - * Adxcg Bid Adapter. - * - */ export const spec = { - code: BIDDER_CODE, - - aliases: ['mediaopti'], - - supportedMediaTypes: [BANNER, NATIVE, VIDEO], - + supportedMediaTypes: [ NATIVE, BANNER, VIDEO ], isBidRequestValid: (bid) => { - logMessage('adxcg - validating isBidRequestValid'); const params = bid.params || {}; const { adzoneid } = params; return !!(adzoneid); }, + buildRequests: (validBidRequests, bidderRequest) => { + // convert Native ORTB definition to old-style prebid native definition + validBidRequests = convertOrtbRequestToProprietaryNative(validBidRequests); + + let app, site; + + const commonFpd = bidderRequest.ortb2 || {}; + let { user } = commonFpd; + + if (typeof getConfig('app') === 'object') { + app = getConfig('app') || {}; + if (commonFpd.app) { + mergeDeep(app, commonFpd.app); + } + } else { + site = getConfig('site') || {}; + if (commonFpd.site) { + mergeDeep(site, commonFpd.site); + } + + if (!site.page) { + site.page = bidderRequest.refererInfo.page; + site.domain = bidderRequest.refererInfo.domain; + } + } + + const device = getConfig('device') || {}; + device.w = device.w || window.innerWidth; + device.h = device.h || window.innerHeight; + device.ua = device.ua || navigator.userAgent; + device.dnt = getDNT() ? 1 : 0; + device.language = (navigator && navigator.language) ? navigator.language.split('-')[0] : ''; + + const tid = bidderRequest.ortb2?.source?.tid; + const test = setOnAny(validBidRequests, 'params.test'); + const currency = getConfig('currency.adServerCurrency'); + const cur = currency && [ currency ]; + const eids = setOnAny(validBidRequests, 'userIdAsEids'); + const schain = setOnAny(validBidRequests, 'schain'); + + const imp = validBidRequests.map((bid, id) => { + const floorInfo = bid.getFloor ? bid.getFloor({ + currency: currency || 'USD' + }) : {}; + const bidfloor = floorInfo.floor; + const bidfloorcur = floorInfo.currency; + const { adzoneid } = bid.params; + + const imp = { + id: id + 1, + tagid: adzoneid, + secure: 1, + bidfloor, + bidfloorcur, + ext: { + } + }; + + const assets = _map(bid.nativeParams, (bidParams, key) => { + const props = NATIVE_PARAMS[key]; + const asset = { + required: bidParams.required & 1, + }; + if (props) { + asset.id = props.id; + let wmin, hmin, w, h; + let aRatios = bidParams.aspect_ratios; + + if (aRatios && aRatios[0]) { + aRatios = aRatios[0]; + wmin = aRatios.min_width || 0; + hmin = aRatios.ratio_height * wmin / aRatios.ratio_width | 0; + } + + if (bidParams.sizes) { + const sizes = flatten(bidParams.sizes); + w = sizes[0]; + h = sizes[1]; + } + + asset[props.name] = { + len: bidParams.len, + type: props.type, + wmin, + hmin, + w, + h + }; + + return asset; + } + }).filter(Boolean); + + if (assets.length) { + imp.native = { + request: JSON.stringify({assets: assets}) + }; + } + + const bannerParams = deepAccess(bid, 'mediaTypes.banner'); + + if (bannerParams && bannerParams.sizes) { + const sizes = parseSizesInput(bannerParams.sizes); + const format = sizes.map(size => { + const [ width, height ] = size.split('x'); + const w = parseInt(width, 10); + const h = parseInt(height, 10); + return { w, h }; + }); + + imp.banner = { + format + }; + } + + const videoParams = deepAccess(bid, 'mediaTypes.video'); + if (videoParams) { + imp.video = videoParams; + } + + return imp; + }); + + const request = { + id: bidderRequest.auctionId, + site, + app, + user, + geo: { utcoffset: new Date().getTimezoneOffset() }, + device, + source: { tid, fd: 1 }, + ext: { + prebid: { + channel: { + name: 'pbjs', + version: '$prebid.version$' + } + } + }, + cur, + imp + }; + + if (test) { + request.is_debug = !!test; + request.test = 1; + } + if (deepAccess(bidderRequest, 'gdprConsent.gdprApplies') !== undefined) { + deepSetValue(request, 'user.ext.consent', bidderRequest.gdprConsent.consentString); + deepSetValue(request, 'regs.ext.gdpr', bidderRequest.gdprConsent.gdprApplies & 1); + } + + if (bidderRequest.uspConsent) { + deepSetValue(request, 'regs.ext.us_privacy', bidderRequest.uspConsent); + } + + if (eids) { + deepSetValue(request, 'user.ext.eids', eids); + } + + if (schain) { + deepSetValue(request, 'source.ext.schain', schain); + } - buildRequests: (bidRequests, bidderRequest) => { - const data = converter.toORTB({ bidRequests, bidderRequest }); return { method: 'POST', url: SECURE_BID_URL, - data, + data: JSON.stringify(request), options: { contentType: 'application/json' }, - bidderRequest + bids: validBidRequests }; }, - - interpretResponse: (response, request) => { - if (response.body) { - const bids = converter.fromORTB({ response: response.body, request: request.data }).bids; - return bids; + interpretResponse: function(serverResponse, { bids }) { + if (!serverResponse.body) { + return; } - return []; - }, + const { seatbid, cur } = serverResponse.body; + + const bidResponses = flatten(seatbid.map(seat => seat.bid)).reduce((result, bid) => { + result[bid.impid - 1] = bid; + return result; + }, []); + + return bids.map((bid, id) => { + const bidResponse = bidResponses[id]; + if (bidResponse) { + const mediaType = deepAccess(bidResponse, 'ext.crType'); + const result = { + requestId: bid.bidId, + cpm: bidResponse.price, + creativeId: bidResponse.crid, + ttl: bidResponse.ttl ? bidResponse.ttl : 300, + netRevenue: bid.netRevenue === 'net', + currency: cur, + burl: bid.burl || '', + mediaType: mediaType, + width: bidResponse.w, + height: bidResponse.h, + dealId: bidResponse.dealid, + }; + + deepSetValue(result, 'meta.mediaType', mediaType); + if (isArray(bidResponse.adomain)) { + deepSetValue(result, 'meta.advertiserDomains', bidResponse.adomain); + } + if (isPlainObject(bidResponse.ext)) { + if (isStr(bidResponse.ext.mediaType)) { + deepSetValue(result, 'meta.mediaType', mediaType); + } + if (isStr(bidResponse.ext.advertiser_id)) { + deepSetValue(result, 'meta.advertiserId', bidResponse.ext.advertiser_id); + } + if (isStr(bidResponse.ext.advertiser_name)) { + deepSetValue(result, 'meta.advertiserName', bidResponse.ext.advertiser_name); + } + if (isStr(bidResponse.ext.agency_name)) { + deepSetValue(result, 'meta.agencyName', bidResponse.ext.agency_name); + } + } + if (mediaType === BANNER) { + result.ad = bidResponse.adm; + } else if (mediaType === NATIVE) { + result.native = parseNative(bidResponse); + result.width = 0; + result.height = 0; + } else if (mediaType === VIDEO) { + result.vastUrl = bidResponse.nurl; + result.vastXml = bidResponse.adm; + } + + return result; + } + }).filter(Boolean); + }, getUserSyncs: (syncOptions, responses, gdprConsent, uspConsent) => { const syncs = []; let syncUrl = config.getConfig('adxcg.usersyncUrl'); @@ -81,95 +323,44 @@ export const spec = { } return syncs; }, - onBidWon: (bid) => { // for native requests we put the nurl as an imp tracker, otherwise if the auction takes place on prebid server // the server JS adapter puts the nurl in the adm as a tracking pixel and removes the attribute if (bid.nurl) { triggerPixel(replaceAuctionPrice(bid.nurl, bid.originalCpm)) } - }, - transformBidParams: function (params) { - return convertTypes({ - 'cf': 'string', - 'cp': 'number', - 'ct': 'number', - 'adzoneid': 'string' - }, params); } }; -const converter = ortbConverter({ - context: { - netRevenue: true, - ttl: 300, - currency: 'EUR' - }, - - imp(buildImp, bidRequest, context) { - const imp = buildImp(bidRequest, context); - // tagid - imp.tagid = bidRequest.params.adzoneid.toString(); - // unknown params - const unknownParams = slotUnknownParams(bidRequest); - if (imp.ext || unknownParams) { - imp.ext = Object.assign({}, imp.ext, unknownParams); - } - // battr - if (bidRequest.params.battr) { - ['banner', 'video', 'audio', 'native'].forEach(k => { - if (imp[k]) { - imp[k].battr = bidRequest.params.battr; - } - }); - } - // deals - if (bidRequest.params.deals && isArray(bidRequest.params.deals)) { - imp.pmp = { - private_auction: 0, - deals: bidRequest.params.deals - }; - } - - imp.secure = Number(window.location.protocol === 'https:'); +registerBidder(spec); - if (!imp.bidfloor && bidRequest.params.bidFloor) { - imp.bidfloor = bidRequest.params.bidFloor; - imp.bidfloorcur = getBidIdParameter('bidFloorCur', bidRequest.params).toUpperCase() || 'USD' +function parseNative(bid) { + const { assets, link, imptrackers, jstracker } = JSON.parse(bid.adm); + const result = { + clickUrl: link.url, + clickTrackers: link.clicktrackers || undefined, + impressionTrackers: imptrackers || undefined, + javascriptTrackers: jstracker ? [ jstracker ] : undefined + }; + assets.forEach(asset => { + const kind = NATIVE_ASSET_IDS[asset.id]; + const content = kind && asset[NATIVE_PARAMS[kind].name]; + if (content) { + result[kind] = content.text || content.value || { url: content.url, width: content.w, height: content.h }; } - return imp; - }, - - request(buildRequest, imps, bidderRequest, context) { - const request = buildRequest(imps, bidderRequest, context); - request.tmax = request.tmax || DEFAULT_TMAX; - request.test = config.getConfig('debug') ? 1 : 0; - request.at = 1; - deepSetValue(request, 'ext.prebid.channel.name', 'pbjs'); - deepSetValue(request, 'ext.prebid.channel.version', '$prebid.version$'); - return request; - }, + }); + return result; +} - bidResponse(buildBidResponse, bid, context) { - const bidResponse = buildBidResponse(bid, context); - bidResponse.cur = bid.cur || DEFAULT_CURRENCY; - return bidResponse; - }, -}); - -/** - * Unknown params are captured and sent on ext - */ -function slotUnknownParams(slot) { - const ext = {}; - const knownParamsMap = {}; - KNOWN_PARAMS.forEach(value => knownParamsMap[value] = 1); - Object.keys(slot.params).forEach(key => { - if (!knownParamsMap[key]) { - ext[key] = slot.params[key]; +function setOnAny(collection, key) { + for (let i = 0, result; i < collection.length; i++) { + result = deepAccess(collection[i], key); + if (result) { + return result; } - }); - return Object.keys(ext).length > 0 ? { prebid: ext } : null; + } } -registerBidder(spec); +function flatten(arr) { + return [].concat(...arr); +} diff --git a/modules/adyoulikeBidAdapter.js b/modules/adyoulikeBidAdapter.js index 9bc24b11ac3..4080d9f25cd 100644 --- a/modules/adyoulikeBidAdapter.js +++ b/modules/adyoulikeBidAdapter.js @@ -1,15 +1,9 @@ import {buildUrl, deepAccess, parseSizesInput} from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; -import { config } from '../src/config.js'; import {find} from '../src/polyfill.js'; import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js'; import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - */ - const VERSION = '1.0'; const BIDDER_CODE = 'adyoulike'; const DEFAULT_DC = 'hb-api'; @@ -69,7 +63,6 @@ export const spec = { // convert Native ORTB definition to old-style prebid native definition bidRequests = convertOrtbRequestToProprietaryNative(bidRequests); let hasVideo = false; - let eids; const payload = { Version: VERSION, Bids: bidRequests.reduce((accumulator, bidReq) => { @@ -88,9 +81,6 @@ export const spec = { if (bidReq.schain) { accumulator[bidReq.bidId].SChain = bidReq.schain; } - if (!eids && bidReq.userIdAsEids && bidReq.userIdAsEids.length) { - eids = bidReq.userIdAsEids; - } if (mediatype === NATIVE) { let nativeReq = bidReq.mediaTypes.native; if (nativeReq.type === 'image') { @@ -130,8 +120,9 @@ export const spec = { if (bidderRequest.ortb2) { payload.ortb2 = bidderRequest.ortb2; } - if (eids) { - payload.eids = eids; + + if (deepAccess(bidderRequest, 'userIdAsEids')) { + payload.userId = bidderRequest.userIdAsEids; } payload.pbjs_version = '$prebid.version$'; @@ -172,50 +163,6 @@ export const spec = { } }); return bidResponses; - }, - - /** - * List user sync endpoints. - * Legal information have to be added to the request. - * Only iframe syncs are supported. - * - * @param {*} syncOptions Publisher prebid configuration. - * @param {*} serverResponses A successful response from the server. - * @return {syncs[]} An array of syncs that should be executed. - */ - getUserSyncs: function (syncOptions, serverResponses, gdprConsent, uspConsent, gppConsent) { - if (!syncOptions.iframeEnabled) { - return []; - } - - let params = ''; - - // GDPR - if (gdprConsent) { - params += '&gdpr=' + (gdprConsent.gdprApplies ? 1 : 0); - params += '&gdpr_consent=' + encodeURIComponent(gdprConsent.consentString || ''); - } - - // coppa compliance - if (config.getConfig('coppa') === true) { - params += '&coppa=1'; - } - - // CCPA - if (uspConsent) { - params += '&us_privacy=' + encodeURIComponent(uspConsent); - } - - // GPP - if (gppConsent?.gppString && gppConsent?.applicableSections?.length) { - params += '&gpp=' + encodeURIComponent(gppConsent.gppString); - params += '&gpp_sid=' + encodeURIComponent(gppConsent?.applicableSections?.join(',')); - } - - return [{ - type: 'iframe', - url: `https://visitor.omnitagjs.com/visitor/isync?uid=19340f4f097d16f41f34fc0274981ca4${params}` - }]; } } diff --git a/modules/agmaAnalyticsAdapter.js b/modules/agmaAnalyticsAdapter.js deleted file mode 100644 index e43dee063c5..00000000000 --- a/modules/agmaAnalyticsAdapter.js +++ /dev/null @@ -1,225 +0,0 @@ -import { ajax } from '../src/ajax.js'; -import { - generateUUID, - logInfo, - logError, - getPerformanceNow, - isEmpty, - isEmptyStr, -} from '../src/utils.js'; -import { getGlobal } from '../src/prebidGlobal.js'; -import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; -import CONSTANTS from '../src/constants.json'; -import adapterManager, { gdprDataHandler } from '../src/adapterManager.js'; -import { getRefererInfo } from '../src/refererDetection.js'; -import { config } from '../src/config.js'; - -const GVLID = 1122; -const ModuleCode = 'agma'; -const analyticsType = 'endpoint'; -const scriptVersion = '1.7.1'; -const batchDelayInMs = 1000; -const agmaURL = 'https://pbc.agma-analytics.de/v1'; -const pageViewId = generateUUID(); - -const { - EVENTS: { AUCTION_INIT }, -} = CONSTANTS; - -// Helper functions -const getScreen = () => { - const w = window; - const d = document; - const e = d.documentElement; - const g = d.getElementsByTagName('body')[0]; - const x = w.innerWidth || e.clientWidth || g.clientWidth; - const y = w.innerHeight || e.clientHeight || g.clientHeight; - return { x, y }; -}; - -const getUserIDs = () => { - try { - return getGlobal().getUserIdsAsEids(); - } catch (e) {} - return []; -}; - -export const getOrtb2Data = (options) => { - let site = null; - let user = null; - - // check if data is provided via config - if (options.ortb2) { - if (options.ortb2.user) { - user = options.ortb2.user; - } - if (options.ortb2.site) { - site = options.ortb2.site; - } - if (site && user) { - return { site, user }; - } - } - try { - const configData = config.getConfig(); - // try to fallback to global config - if (configData.ortb2) { - site = site || configData.ortb2.site; - user = user || configData.ortb2.user; - } - } catch (e) {} - - return { site, user }; -}; - -export const getTiming = () => { - // Timing API V2 - let ttfb = 0; - try { - const entry = performance.getEntriesByType('navigation')[0]; - ttfb = Math.round(entry.responseStart - entry.startTime); - } catch (e) { - // Timing API V1 - try { - const entry = performance.timing; - ttfb = Math.round(entry.responseStart - entry.fetchStart); - } catch (e) { - // Timing API not available - return null; - } - } - const elapsedTime = getPerformanceNow(); - ttfb = ttfb >= 0 && ttfb <= elapsedTime ? ttfb : 0; - return { - ttfb, - elapsedTime, - }; -}; - -export const getPayload = (auctionIds, options) => { - if (!options || !auctionIds || auctionIds.length === 0) { - return false; - } - const consentData = gdprDataHandler.getConsentData(); - let gdprApplies = true; // we assume gdpr applies - let useExtendedPayload = false; - if (consentData) { - gdprApplies = consentData.gdprApplies; - const consents = consentData.vendorData?.vendor?.consents || {}; - useExtendedPayload = consents[GVLID]; - } - const ortb2 = getOrtb2Data(options); - const ri = getRefererInfo() || {}; - - let payload = { - auctionIds: auctionIds, - triggerEvent: options.triggerEvent, - pageViewId, - domain: ri.domain, - gdprApplies, - code: options.code, - ortb2: { site: ortb2.site }, - pageUrl: ri.page, - prebidVersion: '$prebid.version$', - scriptVersion, - debug: options.debug, - timing: getTiming(), - }; - - if (useExtendedPayload) { - const { x, y } = getScreen(); - const userIdsAsEids = getUserIDs(); - payload = { - ...payload, - ortb2, - extended: true, - timestamp: Date.now(), - gdprConsentString: consentData.consentString, - timezoneOffset: new Date().getTimezoneOffset(), - language: window.navigator.language, - referrer: ri.topmostLocation, - pageUrl: ri.page, - screenWidth: x, - screenHeight: y, - userIdsAsEids, - }; - } - return payload; -}; - -const agmaAnalytics = Object.assign(adapter({ analyticsType }), { - auctionIds: [], - timer: null, - track(data) { - const { eventType, args } = data; - if (eventType === this.options.triggerEvent && args && args.auctionId) { - this.auctionIds.push(args.auctionId); - if (this.timer === null) { - this.timer = setTimeout(() => { - this.processBatch(); - }, batchDelayInMs); - } - } - }, - processBatch() { - const currentBatch = [...this.auctionIds]; - const payload = getPayload(currentBatch, this.options); - this.auctionIds = []; - if (this.timer) { - clearTimeout(this.timer); - this.timer = null; - } - this.send(payload); - }, - send(payload) { - if (!payload) { - return; - } - return ajax( - agmaURL, - () => { - logInfo(ModuleCode, 'flushed', payload); - }, - JSON.stringify(payload), - { - contentType: 'text/plain', - method: 'POST', - } - ); - }, -}); - -agmaAnalytics.originEnableAnalytics = agmaAnalytics.enableAnalytics; -agmaAnalytics.enableAnalytics = function (config = {}) { - const { options } = config; - - if (isEmpty(options)) { - logError(ModuleCode, 'Please set options'); - return false; - } - - if (options.site && !options.code) { - logError(ModuleCode, 'Please set `code` - `site` is deprecated'); - options.code = options.site; - } - - if (!options.code || isEmptyStr(options.code)) { - logError(ModuleCode, 'Please set `code` option - agma Analytics is disabled'); - return false; - } - - agmaAnalytics.options = { - triggerEvent: AUCTION_INIT, - ...options, - }; - - agmaAnalytics.originEnableAnalytics(config); -}; - -adapterManager.registerAnalyticsAdapter({ - adapter: agmaAnalytics, - code: ModuleCode, - gvlid: GVLID, -}); - -export default agmaAnalytics; diff --git a/modules/agmaAnalyticsAdapter.md b/modules/agmaAnalyticsAdapter.md deleted file mode 100644 index 30c88fb92ec..00000000000 --- a/modules/agmaAnalyticsAdapter.md +++ /dev/null @@ -1,28 +0,0 @@ -# Overview - Module Name: Agma Analytics - Module Type: Analytics Adapter - Maintainer: [www.agma-mmc.de](https://www.agma-mmc.de) - Technical Support: [info@mllrsohn.com](mailto:info@mllrsohn.com) - -# Description - -Agma Analytics adapter. Please contact [team-internet@agma-mmc.de](mailto:team-internet@agma-mmc.de) for signup and access to [futher documentation](https://docs.agma-analytics.de). - -# Usage - -Add the `agmaAnalyticsAdapter` to your build: - -``` -gulp build --modules=...,agmaAnalyticsAdapter... -``` - -Configure the analytics module: - -```javascript -pbjs.enableAnalytics({ - provider: 'agma', - options: { - code: 'provided-by-agma' // change to the code you received from agma - } -}); -``` diff --git a/modules/aidemBidAdapter.js b/modules/aidemBidAdapter.js index c6a5cd96fb6..7469f26156b 100644 --- a/modules/aidemBidAdapter.js +++ b/modules/aidemBidAdapter.js @@ -1,15 +1,27 @@ -import {deepAccess, deepSetValue, isBoolean, isNumber, isStr, logError, logInfo} from '../src/utils.js'; +import { + _each, + contains, + deepAccess, + deepSetValue, + getDNT, + 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 AVAILABLE_CURRENCIES = ['USD']; +const DEFAULT_CURRENCY = ['USD']; // NOTE - USD is the only supported currency right now; Hardcoded for bids const SUPPORTED_MEDIA_TYPES = [BANNER, VIDEO]; const REQUIRED_VIDEO_PARAMS = [ 'mimes', 'protocols', 'context' ]; @@ -25,63 +37,34 @@ export const ERROR_CODES = { }; const endpoints = { - request: `${BASE_URL}/prebidjs/ortb/v2.6/bid/request`, - // notice: { - // win: `${BASE_URL}/notice/win`, - // timeout: `${BASE_URL}/notice/timeout`, - // error: `${BASE_URL}/notice/error`, - // } + request: `${BASE_URL}/bid/request`, + notice: { + win: `${BASE_URL}/notice/win`, + timeout: `${BASE_URL}/notice/timeout`, + error: `${BASE_URL}/notice/error`, + } }; -export function setEndPoints(env = null, path = '') { +export function setEndPoints(env = null, path = '', mediaType = BANNER) { switch (env) { case 'local': - endpoints.request = `${LOCAL_BASE_URL}${path}/prebidjs/ortb/v2.6/bid/request`; + endpoints.request = mediaType === BANNER ? `${LOCAL_BASE_URL}${path}/bid/request` : `${LOCAL_BASE_URL}${path}/bid/videorequest`; + endpoints.notice.win = `${LOCAL_BASE_URL}${path}/notice/win`; + endpoints.notice.error = `${LOCAL_BASE_URL}${path}/notice/error`; + endpoints.notice.timeout = `${LOCAL_BASE_URL}${path}/notice/timeout`; break; case 'main': - endpoints.request = `${BASE_URL}${path}/prebidjs/ortb/v2.6/bid/request`; + endpoints.request = mediaType === BANNER ? `${BASE_URL}${path}/bid/request` : `${BASE_URL}${path}/bid/videorequest`; + endpoints.notice.win = `${BASE_URL}${path}/notice/win`; + endpoints.notice.error = `${BASE_URL}${path}/notice/error`; + endpoints.notice.timeout = `${BASE_URL}${path}/notice/timeout`; break; } return endpoints; } config.getConfig('aidem', function (config) { - if (config.aidem.env) { setEndPoints(config.aidem.env, config.aidem.path); } -}); - -const converter = ortbConverter({ - context: { - netRevenue: true, - ttl: 30 - }, - request(buildRequest, imps, bidderRequest, context) { - logInfo('Building request'); - const request = buildRequest(imps, bidderRequest, context); - deepSetValue(request, 'at', 1); - setPrebidRequestEnvironment(request); - deepSetValue(request, 'regs', getRegs()); - deepSetValue(request, 'site.publisher.id', bidderRequest.bids[0].params.publisherId); - deepSetValue(request, 'site.id', bidderRequest.bids[0].params.siteId); - return request; - }, - imp(buildImp, bidRequest, context) { - logInfo('Building imp bidRequest', bidRequest); - const imp = buildImp(bidRequest, context); - deepSetValue(imp, 'tagId', bidRequest.params.placementId); - return imp; - }, - bidResponse(buildBidResponse, bid, context) { - const {bidRequest} = context; - const bidResponse = buildBidResponse(bid, context); - logInfo('Building bidResponse'); - logInfo('bid', bid); - logInfo('bidRequest', bidRequest); - logInfo('bidResponse', bidResponse); - if (bidResponse.mediaType === VIDEO) { - deepSetValue(bidResponse, 'vastUrl', bid.adm); - } - return bidResponse; - } + if (config.aidem.env) { setEndPoints(config.aidem.env, config.aidem.path, config.aidem.mediaType); } }); // AIDEM Custom FN @@ -106,6 +89,49 @@ function recur(obj) { return result; } +// ================================================================================= +function getConnectionType() { + const connection = navigator.connection || navigator.webkitConnection; + if (!connection) { + return 0; + } + switch (connection.type) { + case 'ethernet': + return 1; + case 'wifi': + return 2; + case 'cellular': + switch (connection.effectiveType) { + case 'slow-2g': + return 4; + case '2g': + return 4; + case '3g': + return 5; + case '4g': + return 6; + case '5g': + return 7; + default: + return 3; + } + default: + return 0; + } +} + +function getDevice() { + const language = navigator.language || navigator.browserLanguage || navigator.userLanguage || navigator.systemLanguage; + return { + ua: navigator.userAgent, + dnt: !!getDNT(), + language: language, + connectiontype: getConnectionType(), + screen_width: screen.width, + screen_height: screen.height + }; +} + function getRegs() { let regs = {}; const consentManagement = config.getConfig('consentManagement'); @@ -131,6 +157,129 @@ function getRegs() { return regs; } +function getPageUrl(bidderRequest) { + return bidderRequest?.refererInfo?.page; +} + +function buildWinNotice(bid) { + const params = bid.params[0]; + const app = deepAccess(bid, 'meta.ext.app') + const winNoticeExt = deepAccess(bid, 'meta.ext.win_notice_ext') + return { + publisherId: params.publisherId, + siteId: params.siteId, + placementId: params.placementId, + burl: deepAccess(bid, 'meta.burl'), + cpm: bid.cpm, + currency: bid.currency, + impid: deepAccess(bid, 'meta.impid'), + dsp_id: deepAccess(bid, 'meta.dsp_id'), + adUnitCode: bid.adUnitCode, + // TODO: fix auctionId/transactionId leak: https://github.com/prebid/Prebid.js/issues/9781 + auctionId: bid.auctionId, + transactionId: bid.transactionId, + ttl: bid.ttl, + requestTimestamp: bid.requestTimestamp, + responseTimestamp: bid.responseTimestamp, + mediatype: bid.mediaType, + environment: app ? 'app' : 'web', + ...app, + ext: winNoticeExt, + }; +} + +function buildErrorNotice(prebidErrorResponse) { + return { + message: `Prebid.js: Server call for ${prebidErrorResponse.bidderCode} failed.`, + url: encodeURIComponent(getPageUrl(prebidErrorResponse)), + auctionId: prebidErrorResponse.auctionId, + bidderRequestId: prebidErrorResponse.bidderRequestId, + metrics: {} + }; +} + +function hasValidFloor(obj) { + if (!obj) return false; + const hasValue = !isNaN(Number(obj.value)); + const hasCurrency = contains(AVAILABLE_CURRENCIES, obj.currency); + return hasValue && hasCurrency; +} + +function getMediaType(bidRequest) { + if ((bidRequest.mediaTypes && bidRequest.mediaTypes.hasOwnProperty('video')) || bidRequest.params.hasOwnProperty('video')) { return VIDEO; } + return BANNER; +} + +function getPrebidRequestFields(bidderRequest, bidRequests) { + const payload = {}; + // Base Payload Data + deepSetValue(payload, 'id', bidderRequest.bidderRequestId); + // Impressions + setPrebidImpressionObject(bidRequests, payload); + // Device + deepSetValue(payload, 'device', getDevice()); + // Timeout + deepSetValue(payload, 'tmax', bidderRequest.timeout); + // Currency + deepSetValue(payload, 'cur', DEFAULT_CURRENCY); + // Timezone + deepSetValue(payload, 'tz', new Date().getTimezoneOffset()); + // Privacy Regs + deepSetValue(payload, 'regs', getRegs()); + // Site + setPrebidSiteObject(bidderRequest, payload); + // Environment + setPrebidRequestEnvironment(payload); + // AT auction type + deepSetValue(payload, 'at', 1); + + return payload; +} + +function setPrebidImpressionObject(bidRequests, payload) { + payload.imp = []; + _each(bidRequests, function (bidRequest) { + const impressionObject = {}; + // Placement or ad tag used to initiate the auction + deepSetValue(impressionObject, 'id', bidRequest.bidId); + // Transaction id + // TODO: `imp.tid` is not ORTB, is this intentional? + deepSetValue(impressionObject, 'tid', deepAccess(bidRequest, 'ortb2Imp.ext.tid')); + // placement id + deepSetValue(impressionObject, 'tagid', deepAccess(bidRequest, 'params.placementId', null)); + // Publisher id + deepSetValue(payload, 'site.publisher.id', deepAccess(bidRequest, 'params.publisherId')); + // Site id + deepSetValue(payload, 'site.id', deepAccess(bidRequest, 'params.siteId')); + const mediaType = getMediaType(bidRequest); + switch (mediaType) { + case 'banner': + setPrebidImpressionObjectBanner(bidRequest, impressionObject); + break; + case 'video': + setPrebidImpressionObjectVideo(bidRequest, impressionObject); + break; + } + + // Floor (optional) + setPrebidImpressionObjectFloor(bidRequest, impressionObject); + + impressionObject.imp_ext = {}; + + payload.imp.push(impressionObject); + }); +} + +function setPrebidSiteObject(bidderRequest, payload) { + deepSetValue(payload, 'site.domain', deepAccess(bidderRequest, 'refererInfo.domain')); + deepSetValue(payload, 'site.page', deepAccess(bidderRequest, 'refererInfo.page')); + deepSetValue(payload, 'site.referer', deepAccess(bidderRequest, 'refererInfo.ref')); + deepSetValue(payload, 'site.cat', deepAccess(bidderRequest, 'ortb2.site.cat')); + deepSetValue(payload, 'site.sectioncat', deepAccess(bidderRequest, 'ortb2.site.sectioncat')); + deepSetValue(payload, 'site.keywords', deepAccess(bidderRequest, 'ortb2.site.keywords')); + deepSetValue(payload, 'site.site_ext', deepAccess(bidderRequest, 'ortb2.site.ext')); // see https://docs.prebid.org/features/firstPartyData.html +} + function setPrebidRequestEnvironment(payload) { const __navigator = JSON.parse(JSON.stringify(recur(navigator))); delete __navigator.plugins; @@ -147,6 +296,92 @@ function setPrebidRequestEnvironment(payload) { deepSetValue(payload, 'environment.wpar.innerHeight', window.innerHeight); } +function setPrebidImpressionObjectFloor(bidRequest, impressionObject) { + const floor = deepAccess(bidRequest, 'params.floor'); + if (hasValidFloor(floor)) { + deepSetValue(impressionObject, 'floor.value', floor.value); + deepSetValue(impressionObject, 'floor.currency', floor.currency); + } +} + +function setPrebidImpressionObjectBanner(bidRequest, impressionObject) { + deepSetValue(impressionObject, 'mediatype', BANNER); + deepSetValue(impressionObject, 'banner.topframe', 1); + deepSetValue(impressionObject, 'banner.format', []); + _each(bidRequest.mediaTypes.banner.sizes, function (bannerFormat) { + const format = {}; + deepSetValue(format, 'w', bannerFormat[0]); + deepSetValue(format, 'h', bannerFormat[1]); + deepSetValue(format, 'format_ext', {}); + impressionObject.banner.format.push(format); + }); +} + +function setPrebidImpressionObjectVideo(bidRequest, impressionObject) { + deepSetValue(impressionObject, 'mediatype', VIDEO); + deepSetValue(impressionObject, 'video.format', []); + deepSetValue(impressionObject, 'video.mimes', bidRequest.mediaTypes.video.mimes); + deepSetValue(impressionObject, 'video.minDuration', bidRequest.mediaTypes.video.minduration); + deepSetValue(impressionObject, 'video.maxDuration', bidRequest.mediaTypes.video.maxduration); + deepSetValue(impressionObject, 'video.protocols', bidRequest.mediaTypes.video.protocols); + deepSetValue(impressionObject, 'video.context', bidRequest.mediaTypes.video.context); + deepSetValue(impressionObject, 'video.playbackmethod', bidRequest.mediaTypes.video.playbackmethod); + deepSetValue(impressionObject, 'skip', bidRequest.mediaTypes.video.skip); + deepSetValue(impressionObject, 'skipafter', bidRequest.mediaTypes.video.skipafter); + deepSetValue(impressionObject, 'video.pos', bidRequest.mediaTypes.video.pos); + _each(bidRequest.mediaTypes.video.playerSize, function (videoPlayerSize) { + const format = {}; + deepSetValue(format, 'w', videoPlayerSize[0]); + deepSetValue(format, 'h', videoPlayerSize[1]); + deepSetValue(format, 'format_ext', {}); + impressionObject.video.format.push(format); + }); +} + +function getPrebidResponseBidObject(openRTBResponseBidObject) { + const prebidResponseBidObject = {}; + // Common properties + deepSetValue(prebidResponseBidObject, 'requestId', openRTBResponseBidObject.impid); + deepSetValue(prebidResponseBidObject, 'cpm', parseFloat(openRTBResponseBidObject.price)); + deepSetValue(prebidResponseBidObject, 'creativeId', openRTBResponseBidObject.crid); + deepSetValue(prebidResponseBidObject, 'currency', openRTBResponseBidObject.cur ? openRTBResponseBidObject.cur.toUpperCase() : DEFAULT_CURRENCY); + deepSetValue(prebidResponseBidObject, 'width', openRTBResponseBidObject.w); + deepSetValue(prebidResponseBidObject, 'height', openRTBResponseBidObject.h); + deepSetValue(prebidResponseBidObject, 'dealId', openRTBResponseBidObject.dealid); + deepSetValue(prebidResponseBidObject, 'netRevenue', true); + deepSetValue(prebidResponseBidObject, 'ttl', 60000); + + if (openRTBResponseBidObject.mediatype === VIDEO) { + logInfo('bidObject.mediatype == VIDEO'); + deepSetValue(prebidResponseBidObject, 'mediaType', VIDEO); + deepSetValue(prebidResponseBidObject, 'vastUrl', openRTBResponseBidObject.adm); + } else { + logInfo('bidObject.mediatype == BANNER'); + deepSetValue(prebidResponseBidObject, 'mediaType', BANNER); + deepSetValue(prebidResponseBidObject, 'ad', openRTBResponseBidObject.adm); + } + setPrebidResponseBidObjectMeta(prebidResponseBidObject, openRTBResponseBidObject); + return prebidResponseBidObject; +} + +function setPrebidResponseBidObjectMeta(prebidResponseBidObject, openRTBResponseBidObject) { + logInfo('AIDEM Bid Adapter meta', openRTBResponseBidObject); + deepSetValue(prebidResponseBidObject, 'meta.advertiserDomains', deepAccess(openRTBResponseBidObject, 'meta.advertiserDomains')); + deepSetValue(prebidResponseBidObject, 'meta.ext', deepAccess(openRTBResponseBidObject, 'meta.ext')); + if (openRTBResponseBidObject.cat && Array.isArray(openRTBResponseBidObject.cat)) { + const primaryCatId = openRTBResponseBidObject.cat.shift(); + deepSetValue(prebidResponseBidObject, 'meta.primaryCatId', primaryCatId); + deepSetValue(prebidResponseBidObject, 'meta.secondaryCatIds', openRTBResponseBidObject.cat); + } + deepSetValue(prebidResponseBidObject, 'meta.id', openRTBResponseBidObject.id); + deepSetValue(prebidResponseBidObject, 'meta.dsp_id', openRTBResponseBidObject.dsp_id); + deepSetValue(prebidResponseBidObject, 'meta.adid', openRTBResponseBidObject.adid); + deepSetValue(prebidResponseBidObject, 'meta.burl', openRTBResponseBidObject.burl); + deepSetValue(prebidResponseBidObject, 'meta.impid', openRTBResponseBidObject.impid); + deepSetValue(prebidResponseBidObject, 'meta.cat', openRTBResponseBidObject.cat); + deepSetValue(prebidResponseBidObject, 'meta.cid', openRTBResponseBidObject.cid); +} + function hasValidMediaType(bidRequest) { const supported = hasBannerMediaType(bidRequest) || hasVideoMediaType(bidRequest); if (!supported) { @@ -259,38 +494,48 @@ export const spec = { return passesRateLimit(bidRequest); }, - buildRequests: function(bidRequests, bidderRequest) { - logInfo('bidRequests: ', bidRequests); + buildRequests: function(validBidRequests, bidderRequest) { + logInfo('validBidRequests: ', validBidRequests); logInfo('bidderRequest: ', bidderRequest); - const data = converter.toORTB({bidRequests, bidderRequest}); - logInfo('request payload', data); + const prebidRequest = getPrebidRequestFields(bidderRequest, validBidRequests); + const payloadString = JSON.stringify(prebidRequest); + return { method: 'POST', url: endpoints.request, - data, + data: payloadString, options: { withCredentials: true } }; }, - 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; - logInfo('ortbBids: ', ortbBids); - return ortbBids; + interpretResponse: function (serverResponse) { + const bids = []; + logInfo('serverResponse: ', serverResponse); + _each(serverResponse.body.bid, function (bidObject) { + logInfo('bidObject: ', bidObject); + if (!bidObject.price || !bidObject.adm) { + return; + } + logInfo('CPM OK'); + const bid = getPrebidResponseBidObject(bidObject); + bids.push(bid); + }); + return bids; }, onBidWon: function(bid) { // Bidder specific code logInfo('onBidWon bid: ', bid); - ajax(bid.burl); + const notice = buildWinNotice(bid); + ajax(endpoints.notice.win, null, JSON.stringify(notice), { method: 'POST', withCredentials: true }); }, - // onBidderError: function({ bidderRequest }) { - // const notice = buildErrorNotice(bidderRequest); - // ajax(endpoints.notice.error, null, JSON.stringify(notice), { method: 'POST', withCredentials: true }); - // }, + onBidderError: function({ bidderRequest }) { + // Bidder specific code + const notice = buildErrorNotice(bidderRequest); + ajax(endpoints.notice.error, null, JSON.stringify(notice), { method: 'POST', withCredentials: true }); + }, }; registerBidder(spec); diff --git a/modules/airgridRtdProvider.js b/modules/airgridRtdProvider.js index 2184544807e..7c6cf1f5de0 100644 --- a/modules/airgridRtdProvider.js +++ b/modules/airgridRtdProvider.js @@ -11,10 +11,6 @@ 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 - */ - const MODULE_NAME = 'realTimeData'; const SUBMODULE_NAME = 'airgrid'; const AG_TCF_ID = 782; diff --git a/modules/ajaBidAdapter.js b/modules/ajaBidAdapter.js index fcbe0c12c6e..ffab41611ef 100644 --- a/modules/ajaBidAdapter.js +++ b/modules/ajaBidAdapter.js @@ -1,12 +1,7 @@ -import {createTrackPixelHtml, logError, getBidIdParameter} from '../src/utils.js'; +import { getBidIdParameter, tryAppendQueryString, createTrackPixelHtml, logError, logWarn, deepAccess } from '../src/utils.js'; +import { Renderer } from '../src/Renderer.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { BANNER } from '../src/mediaTypes.js'; -import {tryAppendQueryString} from '../libraries/urlUtils/urlUtils.js'; - -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').ServerRequest} ServerRequest - */ +import { VIDEO, BANNER, NATIVE } from '../src/mediaTypes.js'; const BidderCode = 'aja'; const URL = 'https://ad.as.amanad.adtdp.com/v2/prebid'; @@ -29,7 +24,7 @@ const BannerSizeMap = { export const spec = { code: BidderCode, - supportedMediaTypes: [BANNER], + supportedMediaTypes: [VIDEO, BANNER, NATIVE], /** * Determines whether or not the given bid has all the params needed to make a valid request. @@ -55,20 +50,12 @@ export const spec = { for (let i = 0, len = validBidRequests.length; i < len; i++) { const bidRequest = validBidRequests[i]; - if ( - (bidRequest.mediaTypes?.native || bidRequest.mediaTypes?.video) && - bidRequest.mediaTypes?.banner) { - continue - } - let queryString = ''; const asi = getBidIdParameter('asi', bidRequest.params); queryString = tryAppendQueryString(queryString, 'asi', asi); queryString = tryAppendQueryString(queryString, 'skt', SDKType); - queryString = tryAppendQueryString(queryString, 'gpid', bidRequest.ortb2Imp?.ext?.gpid) queryString = tryAppendQueryString(queryString, 'tid', bidRequest.ortb2Imp?.ext?.tid) - queryString = tryAppendQueryString(queryString, 'cdep', bidRequest.ortb2?.device?.ext?.cdep) queryString = tryAppendQueryString(queryString, 'prebid_id', bidRequest.bidId); queryString = tryAppendQueryString(queryString, 'prebid_ver', '$prebid.version$'); @@ -76,8 +63,19 @@ export const spec = { queryString = tryAppendQueryString(queryString, 'page_url', pageUrl); } - const adFormatIDs = pickAdFormats(bidRequest) - if (adFormatIDs && adFormatIDs.length > 0) { + const banner = deepAccess(bidRequest, `mediaTypes.${BANNER}`) + if (banner) { + const adFormatIDs = []; + for (const size of banner.sizes || []) { + if (size.length !== 2) { + continue + } + + const adFormatID = BannerSizeMap[`${size[0]}x${size[1]}`]; + if (adFormatID) { + adFormatIDs.push(adFormatID); + } + } queryString = tryAppendQueryString(queryString, 'ad_format_ids', adFormatIDs.join(',')); } @@ -88,7 +86,7 @@ export const spec = { })); } - const sua = bidRequest.ortb2?.device?.sua + const sua = deepAccess(bidRequest, 'ortb2.device.sua'); if (sua) { queryString = tryAppendQueryString(queryString, 'sua', JSON.stringify(sua)); } @@ -111,17 +109,9 @@ export const spec = { } const ad = bidderResponseBody.ad; - if (AdType.Banner !== ad.ad_type) { - return [] - } - const bannerAd = bidderResponseBody.ad.banner; const bid = { requestId: ad.prebid_id, - mediaType: BANNER, - ad: bannerAd.tag, - width: bannerAd.w, - height: bannerAd.h, cpm: ad.price, creativeId: ad.creative_id, dealId: ad.deal_id, @@ -129,16 +119,80 @@ export const spec = { netRevenue: true, ttl: 300, // 5 minutes meta: { - advertiserDomains: bannerAd.adomain, + advertiserDomains: [] }, } - try { - bannerAd.imps.forEach(impTracker => { - const tracker = createTrackPixelHtml(impTracker); - bid.ad += tracker; + + if (AdType.Video === ad.ad_type) { + const videoAd = bidderResponseBody.ad.video; + Object.assign(bid, { + vastXml: videoAd.vtag, + width: videoAd.w, + height: videoAd.h, + renderer: newRenderer(bidderResponseBody), + adResponse: bidderResponseBody, + mediaType: VIDEO + }); + + Array.prototype.push.apply(bid.meta.advertiserDomains, videoAd.adomain) + } else if (AdType.Banner === ad.ad_type) { + const bannerAd = bidderResponseBody.ad.banner; + Object.assign(bid, { + width: bannerAd.w, + height: bannerAd.h, + ad: bannerAd.tag, + mediaType: BANNER + }); + try { + bannerAd.imps.forEach(impTracker => { + const tracker = createTrackPixelHtml(impTracker); + bid.ad += tracker; + }); + } catch (error) { + logError('Error appending tracking pixel', error); + } + + Array.prototype.push.apply(bid.meta.advertiserDomains, bannerAd.adomain) + } else if (AdType.Native === ad.ad_type) { + const nativeAds = ad.native.template_and_ads.ads; + if (nativeAds.length === 0) { + return []; + } + + const nativeAd = nativeAds[0]; + const assets = nativeAd.assets; + + Object.assign(bid, { + mediaType: NATIVE }); - } catch (error) { - logError('Error appending tracking pixel', error); + + bid.native = { + title: assets.title, + body: assets.description, + cta: assets.cta_text, + sponsoredBy: assets.sponsor, + clickUrl: assets.lp_link, + impressionTrackers: nativeAd.imps, + privacyLink: assets.adchoice_url + }; + + if (assets.img_main !== undefined) { + bid.native.image = { + url: assets.img_main, + width: parseInt(assets.img_main_width, 10), + height: parseInt(assets.img_main_height, 10) + }; + } + + if (assets.img_icon !== undefined) { + bid.native.icon = { + url: assets.img_icon, + width: parseInt(assets.img_icon_width, 10), + height: parseInt(assets.img_icon_height, 10) + }; + } + + Array.prototype.push.apply(bid.meta.advertiserDomains, nativeAd.adomain) } return [bid]; @@ -174,23 +228,34 @@ export const spec = { }, } -function pickAdFormats(bidRequest) { - let sizes = bidRequest.sizes || [] - sizes.push(...(bidRequest.mediaTypes?.banner?.sizes || [])) - - const adFormatIDs = []; - for (const size of sizes) { - if (size.length !== 2) { - continue - } +function newRenderer(bidderResponse) { + const renderer = Renderer.install({ + id: bidderResponse.ad.prebid_id, + url: bidderResponse.ad.video.purl, + loaded: false, + }); - const adFormatID = BannerSizeMap[`${size[0]}x${size[1]}`]; - if (adFormatID) { - adFormatIDs.push(adFormatID); - } + try { + renderer.setRender(outstreamRender); + } catch (err) { + logWarn('Prebid Error calling setRender on newRenderer', err); } - return [...new Set(adFormatIDs)] + return renderer; +} + +function outstreamRender(bid) { + bid.renderer.push(() => { + window['aja_vast_player'].init({ + vast_tag: bid.adResponse.ad.video.vtag, + ad_unit_code: bid.adUnitCode, // target div id to render video + width: bid.width, + height: bid.height, + progress: bid.adResponse.ad.video.progress, + loop: bid.adResponse.ad.video.loop, + inread: bid.adResponse.ad.video.inread + }); + }); } registerBidder(spec); diff --git a/modules/ajaBidAdapter.md b/modules/ajaBidAdapter.md index 92ffecaeb9f..66155875f4d 100644 --- a/modules/ajaBidAdapter.md +++ b/modules/ajaBidAdapter.md @@ -8,7 +8,7 @@ Maintainer: ssp_support@aja-kk.co.jp # Description Connects to Aja exchange for bids. -Aja bid adapter supports Banner. +Aja bid adapter supports Banner and Outstream Video. # Test Parameters ```js @@ -29,6 +29,64 @@ var adUnits = [ asi: 'tk82gbLmg' } }] + }, + // Video outstream adUnit + { + code: 'prebid_video', + mediaTypes: { + video: { + context: 'outstream', + playerSize: [300, 250] + } + }, + bids: [{ + bidder: 'aja', + params: { + asi: '1-KwEG_iR' + } + }] + }, + // Native adUnit + { + code: 'prebid_native', + mediaTypes: { + native: { + image: { + required: true, + sendId: false + }, + title: { + required: true, + sendId: true + }, + sponsoredBy: { + required: false, + sendId: true + }, + clickUrl: { + required: false, + sendId: true + }, + body: { + required: false, + sendId: true + }, + icon: { + required: false, + sendId: false + }, + privacyLink: { + required: true, + sendId: true + }, + } + }, + bids: [{ + bidder: 'aja', + params: { + asi: 'qxueUGliR' + } + }] } ]; ``` diff --git a/modules/alkimiBidAdapter.js b/modules/alkimiBidAdapter.js index 53f6460434f..c087b3061a0 100644 --- a/modules/alkimiBidAdapter.js +++ b/modules/alkimiBidAdapter.js @@ -1,20 +1,18 @@ import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {deepAccess, deepClone, getDNT, generateUUID, replaceAuctionPrice} from '../src/utils.js'; +import {deepAccess, deepClone} from '../src/utils.js'; import {ajax} from '../src/ajax.js'; -import {VIDEO, BANNER} from '../src/mediaTypes.js'; +import {VIDEO} from '../src/mediaTypes.js'; import {config} from '../src/config.js'; const BIDDER_CODE = 'alkimi'; -const GVLID = 1169; export const ENDPOINT = 'https://exchange.alkimi-onboarding.com/bid?prebid=true'; export const spec = { code: BIDDER_CODE, - gvlid: GVLID, supportedMediaTypes: ['banner', 'video'], isBidRequestValid: function (bid) { - return !!(bid.params && bid.params.token); + return !!(bid.params && bid.params.bidFloor && bid.params.token); }, buildRequests: function (validBidRequests, bidderRequest) { @@ -30,15 +28,12 @@ export const spec = { bids.push({ token: bidRequest.params.token, - instl: bidRequest.params.instl, - exp: bidRequest.params.exp, + pos: bidRequest.params.pos, bidFloor: getBidFloor(bidRequest, formatTypes), sizes: prepareSizes(deepAccess(bidRequest, 'mediaTypes.banner.sizes')), playerSizes: prepareSizes(deepAccess(bidRequest, 'mediaTypes.video.playerSize')), impMediaTypes: formatTypes, - adUnitCode: bidRequest.adUnitCode, - video: deepAccess(bidRequest, 'mediaTypes.video'), - banner: deepAccess(bidRequest, 'mediaTypes.banner') + adUnitCode: bidRequest.adUnitCode }) bidIds.push(bidRequest.bidId) }) @@ -46,26 +41,14 @@ export const spec = { const alkimiConfig = config.getConfig('alkimi'); let payload = { - requestId: generateUUID(), + // TODO: fix auctionId leak: https://github.com/prebid/Prebid.js/issues/9781 + requestId: bidderRequest.auctionId, signRequest: {bids, randomUUID: alkimiConfig && alkimiConfig.randomUUID}, bidIds, referer: bidderRequest.refererInfo.page, signature: alkimiConfig && alkimiConfig.signature, schain: validBidRequests[0].schain, - cpp: config.getConfig('coppa') ? 1 : 0, - device: { - dnt: getDNT() ? 1 : 0, - w: screen.width, - h: screen.height - }, - ortb2: { - site: { - keywords: bidderRequest.ortb2?.site?.keywords - }, - at: bidderRequest.ortb2?.at, - bcat: bidderRequest.ortb2?.bcat, - wseat: bidderRequest.ortb2?.wseat - } + cpp: config.getConfig('coppa') ? 1 : 0 } if (bidderRequest && bidderRequest.gdprConsent) { @@ -116,7 +99,7 @@ export const spec = { // banner or video if (VIDEO === bid.mediaType) { - bid.vastUrl = replaceAuctionPrice(bid.winUrl, bid.cpm); + bid.vastXml = bid.ad; } bid.meta = {}; @@ -129,12 +112,21 @@ export const spec = { }, onBidWon: function (bid) { - if (BANNER == bid.mediaType && bid.winUrl) { - const winUrl = replaceAuctionPrice(bid.winUrl, bid.cpm); - ajax(winUrl, null); - return true; + let winUrl; + if (bid.winUrl || bid.vastUrl) { + winUrl = bid.winUrl ? bid.winUrl : bid.vastUrl; + winUrl = winUrl.replace(/\$\{AUCTION_PRICE}/, bid.cpm); + } else if (bid.ad) { + let trackImg = bid.ad.match(/(?!^)/); + bid.ad = bid.ad.replace(trackImg[0], ''); + winUrl = trackImg[0].split('"')[1]; + winUrl = winUrl.replace(/\$%7BAUCTION_PRICE%7D/, bid.cpm); + } else { + return false; } - return false; + + ajax(winUrl, null); + return true; } } diff --git a/modules/ampliffyBidAdapter.js b/modules/ampliffyBidAdapter.js deleted file mode 100644 index bcd28e5bcf1..00000000000 --- a/modules/ampliffyBidAdapter.js +++ /dev/null @@ -1,419 +0,0 @@ -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {logError, logInfo, triggerPixel} from '../src/utils.js'; - -const BIDDER_CODE = 'ampliffy'; -const GVLID = 1258; -const DEFAULT_ENDPOINT = 'bidder.ampliffy.com'; -const TTL = 600; // Time-to-Live - how long (in seconds) Prebid can use this bid. -const LOG_PREFIX = 'AmpliffyBidder: '; - -function isBidRequestValid(bid) { - logInfo(LOG_PREFIX + 'isBidRequestValid: Code: ' + bid.adUnitCode + ': Param' + JSON.stringify(bid.params), bid.adUnitCode); - if (bid.params) { - if (!bid.params.placementId || !bid.params.format) return false; - - if (bid.params.format.toLowerCase() !== 'video' && bid.params.format.toLowerCase() !== 'display' && bid.params.format.toLowerCase() !== 'all') return false; - if (bid.params.format.toLowerCase() === 'video' && !bid.mediaTypes['video']) return false; - if (bid.params.format.toLowerCase() === 'display' && !bid.mediaTypes['banner']) return false; - - if (!bid.params.server || bid.params.server === '') { - const server = bid.params.type + bid.params.region + bid.params.adnetwork; - if (server && server !== '') bid.params.server = server; - else bid.params.server = DEFAULT_ENDPOINT; - } - return true; - } - return false; -} - -function manageConsentArguments(bidderRequest) { - let consent = null; - if (bidderRequest?.gdprConsent) { - consent = { - gdpr: bidderRequest.gdprConsent.gdprApplies ? '1' : '0', - }; - if (bidderRequest.gdprConsent.consentString) { - consent.consent_string = bidderRequest.gdprConsent.consentString; - } - if (bidderRequest.gdprConsent.addtlConsent && bidderRequest.gdprConsent.addtlConsent.indexOf('~') !== -1) { - consent.addtl_consent = bidderRequest.gdprConsent.addtlConsent; - } - } - return consent; -} - -function buildRequests(validBidRequests, bidderRequest) { - const bidRequests = []; - for (const bidRequest of validBidRequests) { - for (const sizes of bidRequest.sizes) { - let extraParams = mergeParams(getDefaultParams(), bidRequest.params.extraParams); - // Apply GDPR parameters to request. - extraParams = mergeParams(extraParams, manageConsentArguments(bidderRequest)); - const serverURL = getServerURL(bidRequest.params.server, sizes, bidRequest.params.placementId, extraParams); - logInfo(LOG_PREFIX + serverURL, 'requests'); - extraParams.bidId = bidRequest.bidId; - bidRequests.push({ - method: 'GET', - url: serverURL, - data: extraParams, - bidRequest, - }); - } - logInfo(LOG_PREFIX + 'Building request from: ' + bidderRequest.url + ': ' + JSON.stringify(bidRequests), bidRequest.adUnitCode); - } - return bidRequests; -} -export function getDefaultParams() { - return { - ciu_szs: '1x1', - gdfp_req: '1', - env: 'vp', - output: 'xml_vast4', - unviewed_position_start: '1' - }; -} -export function mergeParams(params, extraParams) { - if (extraParams) { - for (const k in extraParams) { - params[k] = extraParams[k]; - } - } - return params; -} -export function paramsToQueryString(params) { - return Object.entries(params).filter(e => typeof e[1] !== 'undefined').map(e => { - if (e[1]) return encodeURIComponent(e[0]) + '=' + encodeURIComponent(e[1]); - else return encodeURIComponent(e[0]); - }).join('&'); -} -const getCacheBuster = () => Math.floor(Math.random() * (9999999999 - 1000000000)); - -// For testing purposes -let currentUrl = null; -export function getCurrentURL() { - if (!currentUrl) currentUrl = top.location.href; - return currentUrl; -} -export function setCurrentURL(url) { - currentUrl = url; -} -const getCurrentURLEncoded = () => encodeURIComponent(getCurrentURL()); -function getServerURL(server, sizes, iu, queryParams) { - const random = getCacheBuster(); - const size = sizes[0] + 'x' + sizes[1]; - let serverURL = '//' + server + '/gampad/ads'; - queryParams.sz = size; - queryParams.iu = iu; - queryParams.url = getCurrentURL(); - queryParams.description_url = getCurrentURL(); - queryParams.correlator = random; - - return serverURL; -} -function interpretResponse(serverResponse, bidRequest) { - const bidResponses = []; - - const bidResponse = {}; - let mediaType = 'video'; - if ( - bidRequest.bidRequest?.mediaTypes && - !bidRequest.bidRequest.mediaTypes['video'] - ) { - mediaType = 'banner'; - } - bidResponse.requestId = bidRequest.bidRequest.bidId; - bidResponse.width = bidRequest.bidRequest?.sizes[0][0]; - bidResponse.height = bidRequest.bidRequest?.sizes[0][1]; - bidResponse.ttl = TTL; - bidResponse.creativeId = 'ampCreativeID134'; - bidResponse.netRevenue = true; - bidResponse.mediaType = mediaType; - bidResponse.meta = { - advertiserDomains: [], - }; - let xmlStr = serverResponse.body; - const xml = new window.DOMParser().parseFromString(xmlStr, 'text/xml'); - const xmlData = parseXML(xml, bidResponse); - logInfo(LOG_PREFIX + 'Response from: ' + bidRequest.url + ': ' + JSON.stringify(xmlData), bidRequest.bidRequest.adUnitCode); - if (xmlData.cpm < 0 || !xmlData.creativeURL || !xmlData.bidUp) { - return []; - } - bidResponse.cpm = xmlData.cpm; - bidResponse.currency = xmlData.currency; - - if (mediaType === 'video') { - logInfo(LOG_PREFIX + xmlData.creativeURL, 'requests'); - bidResponse.vastUrl = xmlData.creativeURL; - } else { - bidResponse.adUrl = xmlData.creativeURL; - } - if (xmlData.trackingUrl) { - bidResponse.vastImpUrl = xmlData.trackingUrl; - bidResponse.trackingUrl = xmlData.trackingUrl; - } - bidResponses.push(bidResponse); - return bidResponses; -} -const replaceMacros = (txt, cpm, bid) => { - const size = bid.width + 'x' + bid.height; - txt = txt.replaceAll('%%CACHEBUSTER%%', getCacheBuster()); - txt = txt.replaceAll('@@CACHEBUSTER@@', getCacheBuster()); - txt = txt.replaceAll('%%REFERER%%', getCurrentURLEncoded()); - txt = txt.replaceAll('@@REFERER@@', getCurrentURLEncoded()); - txt = txt.replaceAll('%%REFERRER_URL_UNESC%%', getCurrentURLEncoded()); - txt = txt.replaceAll('@@REFERRER_URL_UNESC@@', getCurrentURLEncoded()); - txt = txt.replaceAll('%%PRICE_ESC%%', encodePrice(cpm)); - txt = txt.replaceAll('@@PRICE_ESC@@', encodePrice(cpm)); - txt = txt.replaceAll('%%SIZES%%', size); - txt = txt.replaceAll('@@SIZES@@', size); - return txt; -} -const encodePrice = (price) => { - price = parseFloat(price); - const s = 116.54; - const c = 1; - const a = 1; - let encodedPrice = s * Math.log10(price + a) + c; - encodedPrice = Math.min(200, encodedPrice); - encodedPrice = Math.round(Math.max(1, encodedPrice)); - - // Format the encoded price with leading zeros if necessary - const formattedEncodedPrice = encodedPrice.toString().padStart(3, '0'); - - // Build the encoding key - const encodingKey = `H--${formattedEncodedPrice}`; - - return encodeURIComponent(`vch=${encodingKey}`); -}; - -function extractCT(xml) { - let ct = null; - try { - try { - const vastAdTagURI = xml.getElementsByTagName('VASTAdTagURI')[0] - if (vastAdTagURI) { - let url = null; - for (const childNode of vastAdTagURI.childNodes) { - if (childNode.nodeValue.trim().includes('http')) { - url = decodeURIComponent(childNode.nodeValue); - } - } - const urlParams = new URLSearchParams(url); - ct = urlParams.get('ct') - } - } catch (e) { - } - if (!ct) { - const geoExtensions = xml.querySelectorAll('Extension[type="geo"]'); - geoExtensions.forEach((geoExtension) => { - const countryElement = geoExtension.querySelector('Country'); - if (countryElement) { - ct = countryElement.textContent; - } - }); - } - } catch (e) {} - return ct; -} - -function extractCPM(htmlContent, ct, cpm) { - const cpmMapDiv = htmlContent.querySelectorAll('[cpmMap]')[0]; - if (cpmMapDiv) { - let cpmMapJSON = JSON.parse(cpmMapDiv.getAttribute('cpmMap')); - if ((cpmMapJSON)) { - if (cpmMapJSON[ct]) { - cpm = cpmMapJSON[ct]; - } else if (cpmMapJSON['default']) { - cpm = cpmMapJSON['default']; - } - } - } - return cpm; -} - -function extractCurrency(htmlContent, currency) { - const currencyDiv = htmlContent.querySelectorAll('[cpmCurrency]')[0]; - if (currencyDiv) { - const currencyValue = currencyDiv.getAttribute('cpmCurrency'); - if (currencyValue && currencyValue !== '') { - currency = currencyValue; - } - } - return currency; -} - -function extractCreativeURL(htmlContent, ct, cpm, bid) { - let creativeURL = null; - const creativeMap = htmlContent.querySelectorAll('[creativeMap]')[0]; - if (creativeMap) { - const creativeMapString = creativeMap.getAttribute('creativeMap'); - - const creativeMapJSON = JSON.parse(creativeMapString); - let defaultURL = null; - for (const url of Object.keys(creativeMapJSON)) { - const geo = creativeMapJSON[url]; - if (geo.includes(ct)) { - creativeURL = replaceMacros(url, cpm, bid); - } else if (geo.includes('default')) { - defaultURL = url; - } - } - if (!creativeURL && defaultURL) creativeURL = replaceMacros(defaultURL, cpm, bid); - } - return creativeURL; -} - -function extractSyncs(htmlContent) { - let userSyncsJSON = null; - const userSyncs = htmlContent.querySelectorAll('[userSyncs]')[0]; - if (userSyncs) { - const userSyncsString = userSyncs.getAttribute('userSyncs'); - - userSyncsJSON = JSON.parse(userSyncsString); - } - return userSyncsJSON; -} - -function extractTrackingURL(htmlContent, ret) { - const trackingUrlDiv = htmlContent.querySelectorAll('[bidder-tracking-url]')[0]; - if (trackingUrlDiv) { - const trackingUrl = trackingUrlDiv.getAttribute('bidder-tracking-url'); - // eslint-disable-next-line no-console - logInfo(LOG_PREFIX + 'parseXML: trackingUrl: ', trackingUrl) - ret.trackingUrl = trackingUrl; - } -} - -export function parseXML(xml, bid) { - const ret = { cpm: 0.001, currency: 'EUR', creativeURL: null, bidUp: false }; - const ct = extractCT(xml); - if (!ct) return ret; - - try { - if (ct) { - const companion = xml.getElementsByTagName('Companion')[0]; - const htmlResource = companion.getElementsByTagName('HTMLResource')[0]; - const htmlContent = document.createElement('html'); - htmlContent.innerHTML = htmlResource.textContent; - - ret.cpm = extractCPM(htmlContent, ct, ret.cpm); - ret.currency = extractCurrency(htmlContent, ret.currency); - ret.creativeURL = extractCreativeURL(htmlContent, ct, ret.cpm, bid); - extractTrackingURL(htmlContent, ret); - ret.bidUp = isAllowedToBidUp(htmlContent, getCurrentURL()); - ret.userSyncs = extractSyncs(htmlContent); - } - } catch (e) { - // eslint-disable-next-line no-console - logError(LOG_PREFIX + 'Error parsing XML', e); - } - // eslint-disable-next-line no-console - logInfo(LOG_PREFIX + 'parseXML RET:', ret); - - return ret; -} -export function isAllowedToBidUp(html, currentURL) { - currentURL = currentURL.split('?')[0]; // Remove parameters - let allowedToPush = false; - try { - const domainsMap = html.querySelectorAll('[domainMap]')[0]; - if (domainsMap) { - let domains = JSON.parse(domainsMap.getAttribute('domainMap')); - if (domains.domainMap) { - domains = domains.domainMap; - } - domains.forEach((d) => { - if (currentURL.includes(d) || d === 'all' || d === '*') allowedToPush = true; - }) - } else { - allowedToPush = true; - } - if (allowedToPush) { - const excludedURL = html.querySelectorAll('[excludedURLs]')[0]; - if (excludedURL) { - const excludedURLsString = domainsMap.getAttribute('excludedURLs'); - if (excludedURLsString !== '') { - let excluded = JSON.parse(excludedURLsString); - excluded.forEach((d) => { - if (currentURL.includes(d)) allowedToPush = false; - }) - } - } - } - } catch (e) { - // eslint-disable-next-line no-console - logError(LOG_PREFIX + 'isAllowedToBidUp', e); - } - return allowedToPush; -} - -function getSyncData(options, syncs) { - const ret = []; - if (syncs?.length) { - for (const sync of syncs) { - if (sync.type === 'syncImage' && options.pixelEnabled) { - ret.push({url: sync.url, type: 'image'}); - } else if (sync.type === 'syncIframe' && options.iframeEnabled) { - ret.push({url: sync.url, type: 'iframe'}); - } - } - } - return ret; -} - -function getUserSyncs(syncOptions, serverResponses) { - const userSyncs = []; - for (const serverResponse of serverResponses) { - if (serverResponse.body) { - try { - const xmlStr = serverResponse.body; - const xml = new window.DOMParser().parseFromString(xmlStr, 'text/xml'); - const xmlData = parseXML(xml, {}); - if (xmlData.userSyncs) { - userSyncs.push(...getSyncData(syncOptions, xmlData.userSyncs)); - } - } catch (e) {} - } - } - return userSyncs; -} - -function onBidWon(bid) { - logInfo(`${LOG_PREFIX} WON AMPLIFFY`); - if (bid.trackingUrl) { - let url = bid.trackingUrl; - - // Replace macros with URL-encoded bid parameters - Object.keys(bid).forEach(key => { - const macroKey = `%%${key.toUpperCase()}%%`; - const value = encodeURIComponent(JSON.stringify(bid[key])); - url = url.split(macroKey).join(value); - }); - - triggerPixel(url, () => { - logInfo(`${LOG_PREFIX} send data success`); - }, - (e) => { - logError(`${LOG_PREFIX} send data error`, e); - }); - } -} -function onTimeOut() { - // eslint-disable-next-line no-console - logInfo(LOG_PREFIX + 'TIMEOUT'); -} - -export const spec = { - code: BIDDER_CODE, - gvlid: GVLID, - aliases: ['ampliffy', 'amp', 'videoffy', 'publiffy'], - supportedMediaTypes: ['video', 'banner'], - isBidRequestValid, - buildRequests, - interpretResponse, - getUserSyncs, - onTimeOut, - onBidWon, -}; - -registerBidder(spec); diff --git a/modules/ampliffyBidAdapter.md b/modules/ampliffyBidAdapter.md deleted file mode 100644 index a425d910582..00000000000 --- a/modules/ampliffyBidAdapter.md +++ /dev/null @@ -1,39 +0,0 @@ -# Overview - -``` -Module Name: Ampliffy Bidder Adapter -Module Type: Bidder Adapter -Maintainer: bidder@ampliffy.com -``` - -# Description - -Connects to Ampliffy Ad server for bids. - -Ampliffy bid adapter supports Video currently, and has initial support for Banner. - -For more information about [Ampliffy](https://www.ampliffy.com/en/), please contact [info@ampliffy.com](info@ampliffy.com). - -# Sample Ad Unit: For Publishers -```javascript -var videoAdUnit = [ -{ - code: 'video1', - mediaTypes: { - video: { - playerSize: [[640, 480]], - context: 'instream' - }, - }, - bids: [{ - bidder: 'ampliffy', - params: { - server: 'bidder.ampliffy.com', - placementId: '1213213/example/vrutal_/', - format: 'video' - } - }] -}]; -``` - -``` diff --git a/modules/amxBidAdapter.js b/modules/amxBidAdapter.js index 6e14f65b0c8..7286001b888 100644 --- a/modules/amxBidAdapter.js +++ b/modules/amxBidAdapter.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 { _each, deepAccess, @@ -10,35 +10,23 @@ import { logError, parseUrl, triggerPixel, - generateUUID, } from '../src/utils.js'; -import { config } from '../src/config.js'; -import { getStorageManager } from '../src/storageManager.js'; -import { fetch } from '../src/ajax.js'; +import {config} from '../src/config.js'; +import {getStorageManager} from '../src/storageManager.js'; const BIDDER_CODE = 'amx'; const storage = getStorageManager({ bidderCode: BIDDER_CODE }); const SIMPLE_TLD_TEST = /\.com?\.\w{2,4}$/; const DEFAULT_ENDPOINT = 'https://prebid.a-mo.net/a/c'; -const VERSION = 'pba1.3.4'; +const VERSION = 'pba1.3.2'; const VAST_RXP = /^\s*<\??(?:vast|xml)/i; -const TRACKING_BASE = 'https://1x1.a-mo.net/'; -const TRACKING_ENDPOINT = TRACKING_BASE + 'hbx/'; -const POST_TRACKING_ENDPOINT = TRACKING_BASE + 'e'; +const TRACKING_ENDPOINT = 'https://1x1.a-mo.net/hbx/'; const AMUID_KEY = '__amuidpb'; function getLocation(request) { return parseUrl(request.refererInfo?.topmostLocation || window.location.href); } -function getTimeoutSize(timeoutData) { - if (timeoutData.sizes == null || timeoutData.sizes.length === 0) { - return [0, 0]; - } - - return timeoutData.sizes[0]; -} - const largestSize = (sizes, mediaTypes) => { const allSizes = sizes .concat(deepAccess(mediaTypes, `${BANNER}.sizes`, []) || []) @@ -160,9 +148,7 @@ function convertRequest(bid) { const tid = deepAccess(bid, 'params.tagId'); const au = - bid.params != null && - typeof bid.params.adUnitId === 'string' && - bid.params.adUnitId !== '' + bid.params != null && typeof bid.params.adUnitId === 'string' && bid.params.adUnitId !== '' ? bid.params.adUnitId : bid.adUnitCode; @@ -215,10 +201,7 @@ function isSyncEnabled(syncConfigP, syncType) { return false; } - if ( - syncConfig.bidders === '*' || - (isArray(syncConfig.bidders) && syncConfig.bidders.indexOf('amx') !== -1) - ) { + if (syncConfig.bidders === '*' || (isArray(syncConfig.bidders) && syncConfig.bidders.indexOf('amx') !== -1)) { return syncConfig.filter == null || syncConfig.filter === 'include'; } @@ -235,17 +218,12 @@ function getSyncSettings() { d: 0, l: 0, t: 0, - e: true, + e: true }; } - const settings = { - d: syncConfig.syncDelay, - l: syncConfig.syncsPerBidder, - t: 0, - e: syncConfig.syncEnabled, - }; - const all = isSyncEnabled(syncConfig.filterSettings, 'all'); + const settings = { d: syncConfig.syncDelay, l: syncConfig.syncsPerBidder, t: 0, e: syncConfig.syncEnabled } + const all = isSyncEnabled(syncConfig.filterSettings, 'all') if (all) { settings.t = SYNC_IMAGE & SYNC_IFRAME; @@ -277,14 +255,12 @@ function getGpp(bidderRequest) { return bidderRequest.gppConsent; } - return ( - bidderRequest?.ortb2?.regs?.gpp ?? { gppString: '', applicableSections: '' } - ); + return bidderRequest?.ortb2?.regs?.gpp ?? {gppString: '', applicableSections: ''}; } function buildReferrerInfo(bidderRequest) { if (bidderRequest.refererInfo == null) { - return { r: '', t: false, c: '', l: 0, s: [] }; + return {r: '', t: false, c: '', l: 0, s: []} } const re = bidderRequest.refererInfo; @@ -295,7 +271,7 @@ function buildReferrerInfo(bidderRequest) { l: re.numIframes, s: re.stack, c: re.canonicalUrl, - }; + } } const isTrue = (boolValue) => @@ -327,7 +303,8 @@ export const spec = { }; const payload = { - a: generateUUID(), + // TODO: fix auctionId leak: https://github.com/prebid/Prebid.js/issues/9781 + a: bidderRequest.auctionId, B: 0, b: loc.host, brc: fbid.bidderRequestsCount || 0, @@ -381,35 +358,28 @@ export const spec = { return { data: payload, method: 'POST', - browsingTopics: true, url: deepAccess(bidRequests[0], 'params.endpoint', DEFAULT_ENDPOINT), withCredentials: true, }; }, - getUserSyncs( - syncOptions, - serverResponses, - gdprConsent, - uspConsent, - gppConsent - ) { + getUserSyncs(syncOptions, serverResponses, gdprConsent, uspConsent, gppConsent) { const qp = { gdpr_consent: enc(gdprConsent?.consentString || ''), gdpr: enc(gdprConsent?.gdprApplies ? 1 : 0), us_privacy: enc(uspConsent || ''), gpp: enc(gppConsent?.gppString || ''), - gpp_sid: enc(gppConsent?.applicableSections || ''), + gpp_sid: enc(gppConsent?.applicableSections || '') }; const iframeSync = { url: `https://prebid.a-mo.net/isyn?${formatQS(qp)}`, - type: 'iframe', + type: 'iframe' }; if (serverResponses == null || serverResponses.length === 0) { if (syncOptions.iframeEnabled) { - return [iframeSync]; + return [iframeSync] } return []; @@ -418,16 +388,13 @@ export const spec = { const output = []; let hasFrame = false; - _each(serverResponses, function({ body: response }) { + _each(serverResponses, function ({ body: response }) { if (response != null && response.p != null && response.p.hreq) { - _each(response.p.hreq, function(syncPixel) { + _each(response.p.hreq, function (syncPixel) { const pixelType = syncPixel.indexOf('__st=iframe') !== -1 ? 'iframe' : 'image'; if (syncOptions.iframeEnabled || pixelType === 'image') { - hasFrame = - hasFrame || - pixelType === 'iframe' || - syncPixel.indexOf('cchain') !== -1; + hasFrame = hasFrame || (pixelType === 'iframe') || (syncPixel.indexOf('cchain') !== -1) output.push({ url: syncPixel, type: pixelType, @@ -438,7 +405,7 @@ export const spec = { }); if (!hasFrame && output.length < 2) { - output.push(iframeSync); + output.push(iframeSync) } return output; @@ -503,58 +470,20 @@ export const spec = { aud: targetingData.requestId, a: targetingData.adUnitCode, c2: nestedQs(targetingData.adserverTargeting), - cn3: targetingData.timeToRespond, }); }, onTimeout(timeoutData) { - if (timeoutData == null || !timeoutData.length) { + if (timeoutData == null) { return; } - let common = null; - const events = timeoutData.map((timeout) => { - const params = timeout.params || {}; - const size = getTimeoutSize(timeout); - const { domain, page, ref } = - timeout.ortb2 != null && timeout.ortb2.site != null - ? timeout.ortb2.site - : {}; - - if (common == null) { - common = { - do: domain, - u: page, - U: getUIDSafe(), - re: ref, - V: '$prebid.version$', - vg: '$$PREBID_GLOBAL$$', - }; - } - - return { - A: timeout.bidder, - mid: params.tagId, - a: params.adunitId || timeout.adUnitCode, - bid: timeout.bidId, - n: 'g_pbto', - aud: timeout.transactionId, - w: size[0], - h: size[1], - cn: timeout.timeout, - cn2: timeout.bidderRequestsCount, - cn3: timeout.bidderWinsCount, - }; - }); - - const payload = JSON.stringify({ c: common, e: events }); - fetch(POST_TRACKING_ENDPOINT, { - body: payload, - keepalive: true, - withCredentials: true, - method: 'POST' - }).catch((_e) => { - // do nothing; ignore errors + trackEvent('pbto', { + A: timeoutData.bidder, + bid: timeoutData.bidId, + a: timeoutData.adUnitCode, + cn: timeoutData.timeout, + aud: timeoutData.auctionId, }); }, diff --git a/modules/amxIdSystem.js b/modules/amxIdSystem.js index 184eff76c34..5deffa00dfe 100644 --- a/modules/amxIdSystem.js +++ b/modules/amxIdSystem.js @@ -152,12 +152,6 @@ export const amxIdSubmodule = { return { callback }; }, - eids: { - amxId: { - source: 'amxdt.net', - atype: 1, - }, - } }; submodule('userId', amxIdSubmodule); diff --git a/modules/apacdexBidAdapter.js b/modules/apacdexBidAdapter.js index 834df134c2e..e1557d9c6d3 100644 --- a/modules/apacdexBidAdapter.js +++ b/modules/apacdexBidAdapter.js @@ -96,8 +96,8 @@ export const spec = { payload.device = {}; payload.device.ua = navigator.userAgent; - payload.device.height = window.screen.height; - payload.device.width = window.screen.width; + payload.device.height = window.screen.width; + payload.device.width = window.screen.height; payload.device.dnt = _getDoNotTrack(); payload.device.language = navigator.language; diff --git a/modules/appierBidAdapter.js b/modules/appierBidAdapter.js index fa314f0bd5f..12346d15130 100644 --- a/modules/appierBidAdapter.js +++ b/modules/appierBidAdapter.js @@ -2,11 +2,6 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER } from '../src/mediaTypes.js'; import { config } from '../src/config.js'; -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - */ - export const ADAPTER_VERSION = '1.0.0'; const SUPPORTED_AD_TYPES = [BANNER]; diff --git a/modules/appnexusBidAdapter.js b/modules/appnexusBidAdapter.js index e62824cb2aa..0660f4f4b10 100644 --- a/modules/appnexusBidAdapter.js +++ b/modules/appnexusBidAdapter.js @@ -1,10 +1,17 @@ import { + chunk, + convertCamelToUnderscore, + convertTypes, createTrackPixelHtml, deepAccess, deepClone, + fill, getBidRequest, + getMaxValueFromArray, + getMinValueFromArray, getParameterByName, getUniqueIdentifierStr, + getWindowFromDocument, isArray, isArrayOfNums, isEmpty, @@ -33,15 +40,7 @@ import { getANKewyordParamFromMaps, getANKeywordParam, transformBidderParamKeywords -} from '../libraries/appnexusUtils/anKeywords.js'; -import {convertCamelToUnderscore, fill} from '../libraries/appnexusUtils/anUtils.js'; -import {convertTypes} from '../libraries/transformParamsUtils/convertTypes.js'; -import {chunk} from '../libraries/chunk/chunk.js'; - -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - */ +} from '../libraries/appnexusKeywords/anKeywords.js'; const BIDDER_CODE = 'appnexus'; const URL = 'https://ib.adnxs.com/ut/v3/prebid'; @@ -115,7 +114,6 @@ export const spec = { { code: 'beintoo', gvlid: 618 }, { code: 'projectagora', gvlid: 1032 }, { code: 'uol', gvlid: 32 }, - { code: 'adzymic', gvlid: 32 }, ], supportedMediaTypes: [BANNER, VIDEO, NATIVE], @@ -351,36 +349,6 @@ export const spec = { } } - if (bidderRequest?.ortb2?.regs?.ext?.dsa) { - const pubDsaObj = bidderRequest.ortb2.regs.ext.dsa; - const dsaObj = {}; - ['required', 'pubrender', 'datatopub'].forEach((dsaKey) => { - if (isNumber(pubDsaObj[dsaKey])) { - if (dsaKey === 'required') { - // our client-side endpoint has a different name for the 'required' field - // ...this is the only exception to the openrtb spec for these fields - dsaObj.dsainfo = pubDsaObj[dsaKey]; - } else { - dsaObj[dsaKey] = pubDsaObj[dsaKey]; - } - } - }); - - if (isArray(pubDsaObj.transparency) && pubDsaObj.transparency.every((v) => isPlainObject(v))) { - const tpData = []; - pubDsaObj.transparency.forEach((tpObj) => { - if (isStr(tpObj.domain) && tpObj.domain != '' && isArray(tpObj.params) && tpObj.params.every((v) => isNumber(v))) { - tpData.push(tpObj); - } - }); - if (tpData.length > 0) { - dsaObj.transparency = tpData; - } - } - - if (!isEmpty(dsaObj)) payload.dsa = dsaObj; - } - if (tags[0].publisher_id) { payload.publisher_id = tags[0].publisher_id; } @@ -620,10 +588,6 @@ function newBid(serverBid, rtbBid, bidderRequest) { bid.meta = Object.assign({}, bid.meta, { advertiserId: rtbBid.advertiser_id }); } - if (rtbBid.dsa) { - bid.meta = Object.assign({}, bid.meta, { dsa: rtbBid.dsa }); - } - // temporary function; may remove at later date if/when adserver fully supports dchain function setupDChain(rtbBid) { let dchain = { @@ -824,7 +788,7 @@ function bidToTag(bid) { tag.keywords = auKeywords; } - let gpid = deepAccess(bid, 'ortb2Imp.ext.gpid') || deepAccess(bid, 'ortb2Imp.ext.data.pbadslot'); + let gpid = deepAccess(bid, 'ortb2Imp.ext.data.pbadslot'); if (gpid) { tag.gpid = gpid; } @@ -1067,7 +1031,7 @@ function createAdPodRequest(tags, adPodBid) { const { durationRangeSec, requireExactDuration } = adPodBid.mediaTypes.video; const numberOfPlacements = getAdPodPlacementNumber(adPodBid.mediaTypes.video); - const maxDuration = Math.max(...durationRangeSec); + const maxDuration = getMaxValueFromArray(durationRangeSec); const tagToDuplicate = tags.filter(tag => tag.uuid === adPodBid.bidId); let request = fill(...tagToDuplicate, numberOfPlacements); @@ -1093,7 +1057,7 @@ function createAdPodRequest(tags, adPodBid) { function getAdPodPlacementNumber(videoParams) { const { adPodDurationSec, durationRangeSec, requireExactDuration } = videoParams; - const minAllowedDuration = Math.min(...durationRangeSec); + const minAllowedDuration = getMinValueFromArray(durationRangeSec); const numberOfPlacements = Math.floor(adPodDurationSec / minAllowedDuration); return requireExactDuration @@ -1178,7 +1142,7 @@ function outstreamRender(bid, doc) { hideSASIframe(bid.adUnitCode); // push to render queue because ANOutstreamVideo may not be loaded yet bid.renderer.push(() => { - const win = doc?.defaultView || window; + const win = getWindowFromDocument(doc) || window; win.ANOutstreamVideo.renderAd({ tagId: bid.adResponse.tag_id, sizes: [bid.getSize().split('x')], diff --git a/modules/arcspanRtdProvider.js b/modules/arcspanRtdProvider.js index 8ccf3d160b9..a7ffa059279 100644 --- a/modules/arcspanRtdProvider.js +++ b/modules/arcspanRtdProvider.js @@ -2,10 +2,6 @@ import { submodule } from '../src/hook.js'; import { mergeDeep } from '../src/utils.js'; import {loadExternalScript} from '../src/adloader.js'; -/** - * @typedef {import('../modules/rtdModule/index.js').RtdSubmodule} RtdSubmodule - */ - /** @type {string} */ const MODULE_NAME = 'realTimeData'; const SUBMODULE_NAME = 'arcspan'; diff --git a/modules/asoBidAdapter.js b/modules/asoBidAdapter.js index 704cffefb39..e569f04a2a8 100644 --- a/modules/asoBidAdapter.js +++ b/modules/asoBidAdapter.js @@ -7,14 +7,14 @@ import { isArray, isFn, logWarn, - parseSizesInput + parseSizesInput, + tryAppendQueryString } 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 { parseDomain } from '../src/refererDetection.js'; -import {tryAppendQueryString} from '../libraries/urlUtils/urlUtils.js'; const BIDDER_CODE = 'aso'; const DEFAULT_SERVER_URL = 'https://srv.aso1.net'; diff --git a/modules/asteriobidAnalyticsAdapter.js b/modules/asteriobidAnalyticsAdapter.js deleted file mode 100644 index 516a3a65667..00000000000 --- a/modules/asteriobidAnalyticsAdapter.js +++ /dev/null @@ -1,336 +0,0 @@ -import { generateUUID, getParameterByName, logError, logInfo, parseUrl } from '../src/utils.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 CONSTANTS from '../src/constants.json' -import { MODULE_TYPE_ANALYTICS } from '../src/activities/modules.js' -import {getRefererInfo} from '../src/refererDetection.js'; - -/** - * asteriobidAnalyticsAdapter.js - analytics adapter for AsterioBid - */ -export const storage = getStorageManager({ moduleType: MODULE_TYPE_ANALYTICS, moduleName: 'asteriobid' }) -const DEFAULT_EVENT_URL = 'https://endpt.asteriobid.com/endpoint' -const analyticsType = 'endpoint' -const analyticsName = 'AsterioBid Analytics' -const utmTags = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content'] -const _VERSION = 1 - -let ajax = ajaxBuilder(20000) -let initOptions -let auctionStarts = {} -let auctionTimeouts = {} -let sampling -let pageViewId -let flushInterval -let eventQueue = [] -let asteriobidAnalyticsEnabled = false - -let asteriobidAnalytics = Object.assign(adapter({ url: DEFAULT_EVENT_URL, analyticsType }), { - track({ eventType, args }) { - handleEvent(eventType, args) - } -}) - -asteriobidAnalytics.originEnableAnalytics = asteriobidAnalytics.enableAnalytics -asteriobidAnalytics.enableAnalytics = function (config) { - initOptions = config.options || {} - - pageViewId = initOptions.pageViewId || generateUUID() - sampling = initOptions.sampling || 1 - - if (Math.floor(Math.random() * sampling) === 0) { - asteriobidAnalyticsEnabled = true - flushInterval = setInterval(flush, 1000) - } else { - logInfo(`${analyticsName} isn't enabled because of sampling`) - } - - asteriobidAnalytics.originEnableAnalytics(config) -} - -asteriobidAnalytics.originDisableAnalytics = asteriobidAnalytics.disableAnalytics -asteriobidAnalytics.disableAnalytics = function () { - if (!asteriobidAnalyticsEnabled) { - return - } - flush() - clearInterval(flushInterval) - asteriobidAnalytics.originDisableAnalytics() -} - -function collectUtmTagData() { - let newUtm = false - let pmUtmTags = {} - try { - utmTags.forEach(function (utmKey) { - let utmValue = getParameterByName(utmKey) - if (utmValue !== '') { - newUtm = true - } - pmUtmTags[utmKey] = utmValue - }) - if (newUtm === false) { - utmTags.forEach(function (utmKey) { - let itemValue = storage.getDataFromLocalStorage(`pm_${utmKey}`) - if (itemValue && itemValue.length !== 0) { - pmUtmTags[utmKey] = itemValue - } - }) - } else { - utmTags.forEach(function (utmKey) { - storage.setDataInLocalStorage(`pm_${utmKey}`, pmUtmTags[utmKey]) - }) - } - } catch (e) { - logError(`${analyticsName} Error`, e) - pmUtmTags['error_utm'] = 1 - } - return pmUtmTags -} - -function collectPageInfo() { - const pageInfo = { - domain: window.location.hostname, - } - if (document.referrer) { - pageInfo.referrerDomain = parseUrl(document.referrer).hostname - } - - const refererInfo = getRefererInfo() - pageInfo.page = refererInfo.page - pageInfo.ref = refererInfo.ref - - return pageInfo -} - -function flush() { - if (!asteriobidAnalyticsEnabled) { - return - } - - if (eventQueue.length > 0) { - const data = { - pageViewId: pageViewId, - ver: _VERSION, - bundleId: initOptions.bundleId, - events: eventQueue, - utmTags: collectUtmTagData(), - pageInfo: collectPageInfo(), - sampling: sampling - } - eventQueue = [] - - if ('version' in initOptions) { - data.version = initOptions.version - } - if ('tcf_compliant' in initOptions) { - data.tcf_compliant = initOptions.tcf_compliant - } - if ('adUnitDict' in initOptions) { - data.adUnitDict = initOptions.adUnitDict; - } - if ('customParam' in initOptions) { - data.customParam = initOptions.customParam; - } - - const url = initOptions.url ? initOptions.url : DEFAULT_EVENT_URL - ajax( - url, - () => logInfo(`${analyticsName} sent events batch`), - _VERSION + ':' + JSON.stringify(data), - { - contentType: 'text/plain', - method: 'POST', - withCredentials: true - } - ) - } -} - -function trimAdUnit(adUnit) { - if (!adUnit) return adUnit - const res = {} - res.code = adUnit.code - res.sizes = adUnit.sizes - return res -} - -function trimBid(bid) { - if (!bid) return bid - const res = {} - res.auctionId = bid.auctionId - res.bidder = bid.bidder - res.bidderRequestId = bid.bidderRequestId - res.bidId = bid.bidId - res.crumbs = bid.crumbs - res.cpm = bid.cpm - res.currency = bid.currency - res.mediaTypes = bid.mediaTypes - res.sizes = bid.sizes - res.transactionId = bid.transactionId - res.adUnitCode = bid.adUnitCode - res.bidRequestsCount = bid.bidRequestsCount - res.serverResponseTimeMs = bid.serverResponseTimeMs - return res -} - -function trimBidderRequest(bidderRequest) { - if (!bidderRequest) return bidderRequest - const res = {} - res.auctionId = bidderRequest.auctionId - res.auctionStart = bidderRequest.auctionStart - res.bidderRequestId = bidderRequest.bidderRequestId - res.bidderCode = bidderRequest.bidderCode - res.bids = bidderRequest.bids && bidderRequest.bids.map(trimBid) - return res -} - -function handleEvent(eventType, eventArgs) { - if (!asteriobidAnalyticsEnabled) { - return - } - - try { - eventArgs = eventArgs ? JSON.parse(JSON.stringify(eventArgs)) : {} - } catch (e) { - // keep eventArgs as is - } - - const pmEvent = {} - pmEvent.timestamp = eventArgs.timestamp || Date.now() - pmEvent.eventType = eventType - - switch (eventType) { - case CONSTANTS.EVENTS.AUCTION_INIT: { - pmEvent.auctionId = eventArgs.auctionId - pmEvent.timeout = eventArgs.timeout - pmEvent.adUnits = eventArgs.adUnits && eventArgs.adUnits.map(trimAdUnit) - pmEvent.bidderRequests = eventArgs.bidderRequests && eventArgs.bidderRequests.map(trimBidderRequest) - auctionStarts[pmEvent.auctionId] = pmEvent.timestamp - auctionTimeouts[pmEvent.auctionId] = pmEvent.timeout - break - } - case CONSTANTS.EVENTS.AUCTION_END: { - pmEvent.auctionId = eventArgs.auctionId - pmEvent.end = eventArgs.end - pmEvent.start = eventArgs.start - pmEvent.adUnitCodes = eventArgs.adUnitCodes - pmEvent.bidsReceived = eventArgs.bidsReceived && eventArgs.bidsReceived.map(trimBid) - pmEvent.start = auctionStarts[pmEvent.auctionId] - pmEvent.end = Date.now() - break - } - case CONSTANTS.EVENTS.BID_ADJUSTMENT: { - break - } - case CONSTANTS.EVENTS.BID_TIMEOUT: { - pmEvent.bidders = eventArgs && eventArgs.map ? eventArgs.map(trimBid) : eventArgs - pmEvent.duration = auctionTimeouts[pmEvent.auctionId] - break - } - case CONSTANTS.EVENTS.BID_REQUESTED: { - pmEvent.auctionId = eventArgs.auctionId - pmEvent.bidderCode = eventArgs.bidderCode - pmEvent.doneCbCallCount = eventArgs.doneCbCallCount - pmEvent.start = eventArgs.start - pmEvent.bidderRequestId = eventArgs.bidderRequestId - pmEvent.bids = eventArgs.bids && eventArgs.bids.map(trimBid) - pmEvent.auctionStart = eventArgs.auctionStart - pmEvent.timeout = eventArgs.timeout - break - } - case CONSTANTS.EVENTS.BID_RESPONSE: { - pmEvent.bidderCode = eventArgs.bidderCode - pmEvent.width = eventArgs.width - pmEvent.height = eventArgs.height - pmEvent.adId = eventArgs.adId - pmEvent.mediaType = eventArgs.mediaType - pmEvent.cpm = eventArgs.cpm - pmEvent.currency = eventArgs.currency - pmEvent.requestId = eventArgs.requestId - pmEvent.adUnitCode = eventArgs.adUnitCode - pmEvent.auctionId = eventArgs.auctionId - pmEvent.timeToRespond = eventArgs.timeToRespond - pmEvent.requestTimestamp = eventArgs.requestTimestamp - pmEvent.responseTimestamp = eventArgs.responseTimestamp - pmEvent.netRevenue = eventArgs.netRevenue - pmEvent.size = eventArgs.size - pmEvent.adserverTargeting = eventArgs.adserverTargeting - break - } - case CONSTANTS.EVENTS.BID_WON: { - pmEvent.auctionId = eventArgs.auctionId - pmEvent.adId = eventArgs.adId - pmEvent.adserverTargeting = eventArgs.adserverTargeting - pmEvent.adUnitCode = eventArgs.adUnitCode - pmEvent.bidderCode = eventArgs.bidderCode - pmEvent.height = eventArgs.height - pmEvent.mediaType = eventArgs.mediaType - pmEvent.netRevenue = eventArgs.netRevenue - pmEvent.cpm = eventArgs.cpm - pmEvent.requestTimestamp = eventArgs.requestTimestamp - pmEvent.responseTimestamp = eventArgs.responseTimestamp - pmEvent.size = eventArgs.size - pmEvent.width = eventArgs.width - pmEvent.currency = eventArgs.currency - pmEvent.bidder = eventArgs.bidder - break - } - case CONSTANTS.EVENTS.BIDDER_DONE: { - pmEvent.auctionId = eventArgs.auctionId - pmEvent.auctionStart = eventArgs.auctionStart - pmEvent.bidderCode = eventArgs.bidderCode - pmEvent.bidderRequestId = eventArgs.bidderRequestId - pmEvent.bids = eventArgs.bids && eventArgs.bids.map(trimBid) - pmEvent.doneCbCallCount = eventArgs.doneCbCallCount - pmEvent.start = eventArgs.start - pmEvent.timeout = eventArgs.timeout - pmEvent.tid = eventArgs.tid - pmEvent.src = eventArgs.src - break - } - case CONSTANTS.EVENTS.SET_TARGETING: { - break - } - case CONSTANTS.EVENTS.REQUEST_BIDS: { - break - } - case CONSTANTS.EVENTS.ADD_AD_UNITS: { - break - } - case CONSTANTS.EVENTS.AD_RENDER_FAILED: { - pmEvent.bid = eventArgs.bid - pmEvent.message = eventArgs.message - pmEvent.reason = eventArgs.reason - break - } - default: - return - } - - sendEvent(pmEvent) -} - -function sendEvent(event) { - eventQueue.push(event) - logInfo(`${analyticsName} Event ${event.eventType}:`, event) - - if (event.eventType === CONSTANTS.EVENTS.AUCTION_END) { - flush() - } -} - -adapterManager.registerAnalyticsAdapter({ - adapter: asteriobidAnalytics, - code: 'asteriobid' -}) - -asteriobidAnalytics.getOptions = function () { - return initOptions -} - -asteriobidAnalytics.flush = flush - -export default asteriobidAnalytics diff --git a/modules/asteriobidAnalyticsAdapter.md b/modules/asteriobidAnalyticsAdapter.md deleted file mode 100644 index 524cf6e2721..00000000000 --- a/modules/asteriobidAnalyticsAdapter.md +++ /dev/null @@ -1,41 +0,0 @@ -# Overview - -Module Name: AsterioBid Analytics Adapter -Module Type: Analytics Adapter -Maintainer: admin@asteriobid.com - -# Description -Analytics adapter for
AsterioBid. Contact admin@asteriobid.com for information. - -# Test Parameters - -``` -pbjs.enableAnalytics({ - provider: 'asteriobid', - options: { - bundleId: '04bcf17b-9733-4675-9f67-d475f881ab78' - } -}); - -``` - -# Advanced Parameters - -``` -pbjs.enableAnalytics({ - provider: 'asteriobid', - options: { - bundleId: '04bcf17b-9733-4675-9f67-d475f881ab78', - version: 'v1', // configuration version for the comparison - adUnitDict: { // provide names of the ad units for better reporting - adunitid1: 'Top Banner', - adunitid2: 'Bottom Banner' - }, - customParam: { // provide custom parameters values that you want to collect and report - param1: 'value1', - param2: 'value2' - } - } -}); - -``` diff --git a/modules/astraoneBidAdapter.js b/modules/astraoneBidAdapter.js index 9645b8e68bd..d7f92bb5fac 100644 --- a/modules/astraoneBidAdapter.js +++ b/modules/astraoneBidAdapter.js @@ -2,12 +2,6 @@ import { _map } 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 - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse - */ - const BIDDER_CODE = 'astraone'; const SSP_ENDPOINT = 'https://ssp.astraone.io/auction/prebid'; const TTL = 60; diff --git a/modules/audiencerunBidAdapter.js b/modules/audiencerunBidAdapter.js index 92a4343b3ed..9beb20d4f77 100644 --- a/modules/audiencerunBidAdapter.js +++ b/modules/audiencerunBidAdapter.js @@ -1,7 +1,8 @@ import { _each, deepAccess, - formatQS, getBidIdParameter, + formatQS, + getBidIdParameter, getValue, isArray, isFn, @@ -12,15 +13,6 @@ import {config} from '../src/config.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {BANNER} from '../src/mediaTypes.js'; -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - * @typedef {import('../src/adapters/bidderFactory.js').ServerRequest} ServerRequest - * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse - * @typedef {import('../src/adapters/bidderFactory.js').SyncOptions} SyncOptions - * @typedef {import('../src/adapters/bidderFactory.js').UserSync} UserSync - */ - const BIDDER_CODE = 'audiencerun'; const BASE_URL = 'https://d.audiencerun.com'; const AUCTION_URL = `${BASE_URL}/prebid`; diff --git a/modules/automatadAnalyticsAdapter.js b/modules/automatadAnalyticsAdapter.js deleted file mode 100644 index 7d7bd8cb34c..00000000000 --- a/modules/automatadAnalyticsAdapter.js +++ /dev/null @@ -1,325 +0,0 @@ -import { - logError, - logInfo, - logMessage -} from '../src/utils.js'; - -import CONSTANTS from '../src/constants.json'; -import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; -import adapterManager from '../src/adapterManager.js'; -import { config } from '../src/config.js' - -/** Prebid Event Handlers */ - -const ADAPTER_CODE = 'automatadAnalytics' -const trialCountMilsMapping = [1500, 3000, 5000, 10000]; - -var isLoggingEnabled; var queuePointer = 0; var retryCount = 0; var timer = null; var __atmtdAnalyticsQueue = []; - -const prettyLog = (level, text, isGroup = false, cb = () => {}) => { - if (self.isLoggingEnabled === undefined) { - if (window.localStorage.getItem('__aggLoggingEnabled')) { - self.isLoggingEnabled = true - } else { - const queryParams = new URLSearchParams(new URL(window.location.href).search) - self.isLoggingEnabled = queryParams.has('aggLoggingEnabled') - } - } - - if (self.isLoggingEnabled) { - if (isGroup) { - logInfo(`ATD Analytics Adapter: ${level.toUpperCase()}: ${text} --- Group Start ---`) - try { - cb(); - } catch (error) { - logError(`ATD Analytics Adapter: ERROR: ${'Error during cb function in prettyLog'}`) - } - logInfo(`ATD Analytics Adapter: ${level.toUpperCase()}: ${text} --- Group End ---`) - } else { - logInfo(`ATD Analytics Adapter: ${level.toUpperCase()}: ${text}`) - } - } -} - -const processEvents = () => { - if (self.retryCount === trialCountMilsMapping.length) { - self.prettyLog('error', `Aggregator still hasn't loaded. Processing que stopped`, trialCountMilsMapping, self.retryCount) - return; - } - - self.prettyLog('status', `Que has been inactive for a while. Adapter starting to process que now... Trial Count = ${self.retryCount + 1}`) - - let shouldTryAgain = false - - while (self.queuePointer < self.__atmtdAnalyticsQueue.length) { - const eventType = self.__atmtdAnalyticsQueue[self.queuePointer][0] - const args = self.__atmtdAnalyticsQueue[self.queuePointer][1] - - try { - switch (eventType) { - case CONSTANTS.EVENTS.AUCTION_INIT: - if (window.atmtdAnalytics && window.atmtdAnalytics.auctionInitHandler) { - window.atmtdAnalytics.auctionInitHandler(args); - } else { - shouldTryAgain = true - } - break; - case CONSTANTS.EVENTS.BID_REQUESTED: - if (window.atmtdAnalytics && window.atmtdAnalytics.bidRequestedHandler) { - window.atmtdAnalytics.bidRequestedHandler(args); - } - break; - case CONSTANTS.EVENTS.BID_RESPONSE: - if (window.atmtdAnalytics && window.atmtdAnalytics.bidResponseHandler) { - window.atmtdAnalytics.bidResponseHandler(args); - } - break; - case CONSTANTS.EVENTS.BID_REJECTED: - if (window.atmtdAnalytics && window.atmtdAnalytics.bidRejectedHandler) { - window.atmtdAnalytics.bidRejectedHandler(args); - } - break; - case CONSTANTS.EVENTS.BIDDER_DONE: - if (window.atmtdAnalytics && window.atmtdAnalytics.bidderDoneHandler) { - window.atmtdAnalytics.bidderDoneHandler(args); - } - break; - case CONSTANTS.EVENTS.BID_WON: - if (window.atmtdAnalytics && window.atmtdAnalytics.bidWonHandler) { - window.atmtdAnalytics.bidWonHandler(args); - } - break; - case CONSTANTS.EVENTS.NO_BID: - if (window.atmtdAnalytics && window.atmtdAnalytics.noBidHandler) { - window.atmtdAnalytics.noBidHandler(args); - } - break; - case CONSTANTS.EVENTS.BID_TIMEOUT: - if (window.atmtdAnalytics && window.atmtdAnalytics.bidderTimeoutHandler) { - window.atmtdAnalytics.bidderTimeoutHandler(args); - } - break; - case CONSTANTS.EVENTS.AUCTION_DEBUG: - if (window.atmtdAnalytics && window.atmtdAnalytics.auctionDebugHandler) { - window.atmtdAnalytics.auctionDebugHandler(args); - } - break; - case 'slotRenderEnded': - if (window.atmtdAnalytics && window.atmtdAnalytics.slotRenderEndedGPTHandler) { - window.atmtdAnalytics.slotRenderEndedGPTHandler(args); - } else { - shouldTryAgain = true - } - break; - case 'impressionViewable': - if (window.atmtdAnalytics && window.atmtdAnalytics.impressionViewableHandler) { - window.atmtdAnalytics.impressionViewableHandler(args); - } else { - shouldTryAgain = true - } - break; - } - - if (shouldTryAgain) break; - } catch (error) { - self.prettyLog('error', `Unhandled Error while processing ${eventType} of ${self.queuePointer}th index in the que. Will not be retrying this raw event ...`, true, () => { - logError(`The error is `, error) - }) - } - - self.queuePointer = self.queuePointer + 1 - } - - if (shouldTryAgain) { - if (trialCountMilsMapping[self.retryCount]) self.prettyLog('warn', `Adapter failed to process event as aggregator has not loaded. Retrying in ${trialCountMilsMapping[self.retryCount]}ms ...`); - setTimeout(self.processEvents, trialCountMilsMapping[self.retryCount]) - self.retryCount = self.retryCount + 1 - } -} - -const addGPTHandlers = () => { - const googletag = window.googletag || {} - googletag.cmd = googletag.cmd || [] - googletag.cmd.push(() => { - googletag.pubads().addEventListener('slotRenderEnded', (event) => { - if (window.atmtdAnalytics && window.atmtdAnalytics.slotRenderEndedGPTHandler) { - if (window.__atmtdAggregatorFirstAuctionInitialized === true) { - window.atmtdAnalytics.slotRenderEndedGPTHandler(event) - return; - } - } - self.__atmtdAnalyticsQueue.push(['slotRenderEnded', event]) - self.prettyLog(`warn`, `Aggregator not initialised at auctionInit, exiting slotRenderEnded handler and pushing to que instead`) - }) - - googletag.pubads().addEventListener('impressionViewable', (event) => { - if (window.atmtdAnalytics && window.atmtdAnalytics.impressionViewableHandler) { - if (window.__atmtdAggregatorFirstAuctionInitialized === true) { - window.atmtdAnalytics.impressionViewableHandler(event) - return; - } - } - self.__atmtdAnalyticsQueue.push(['impressionViewable', event]) - self.prettyLog(`warn`, `Aggregator not initialised at auctionInit, exiting impressionViewable handler and pushing to que instead`) - }) - }) -} - -const initializeQueue = () => { - self.__atmtdAnalyticsQueue.push = (args) => { - Array.prototype.push.apply(self.__atmtdAnalyticsQueue, [args]); - if (timer) { - clearTimeout(timer); - timer = null; - } - - if (args[0] === CONSTANTS.EVENTS.AUCTION_INIT) { - const timeout = parseInt(config.getConfig('bidderTimeout')) + 1500 - timer = setTimeout(() => { - self.processEvents() - }, timeout); - } else { - timer = setTimeout(() => { - self.processEvents() - }, 1500); - } - }; -} - -// ANALYTICS ADAPTER - -let baseAdapter = adapter({analyticsType: 'bundle'}); -let atmtdAdapter = Object.assign({}, baseAdapter, { - - disableAnalytics() { - baseAdapter.disableAnalytics.apply(this, arguments); - }, - - track({eventType, args}) { - switch (eventType) { - case CONSTANTS.EVENTS.AUCTION_INIT: - if (window.atmtdAnalytics && window.atmtdAnalytics.auctionInitHandler) { - self.prettyLog('status', 'Aggregator loaded, initialising auction through handlers'); - window.atmtdAnalytics.auctionInitHandler(args); - } else { - self.prettyLog('warn', 'Aggregator not loaded, initialising auction through que ...'); - self.__atmtdAnalyticsQueue.push([eventType, args]) - } - break; - case CONSTANTS.EVENTS.BID_REQUESTED: - if (window.atmtdAnalytics && window.atmtdAnalytics.bidRequestedHandler) { - window.atmtdAnalytics.bidRequestedHandler(args); - } else { - self.prettyLog('warn', `Aggregator not loaded, pushing ${eventType} to que instead ...`); - self.__atmtdAnalyticsQueue.push([eventType, args]) - } - break; - case CONSTANTS.EVENTS.BID_REJECTED: - if (window.atmtdAnalytics && window.atmtdAnalytics.bidRejectedHandler) { - window.atmtdAnalytics.bidRejectedHandler(args); - } else { - self.prettyLog('warn', `Aggregator not loaded, pushing ${eventType} to que instead ...`); - self.__atmtdAnalyticsQueue.push([eventType, args]) - } - break; - case CONSTANTS.EVENTS.BID_RESPONSE: - if (window.atmtdAnalytics && window.atmtdAnalytics.bidResponseHandler) { - window.atmtdAnalytics.bidResponseHandler(args); - } else { - self.prettyLog('warn', `Aggregator not loaded, pushing ${eventType} to que instead ...`); - self.__atmtdAnalyticsQueue.push([eventType, args]) - } - break; - case CONSTANTS.EVENTS.BIDDER_DONE: - if (window.atmtdAnalytics && window.atmtdAnalytics.bidderDoneHandler) { - window.atmtdAnalytics.bidderDoneHandler(args); - } else { - self.prettyLog('warn', `Aggregator not loaded, pushing ${eventType} to que instead ...`); - self.__atmtdAnalyticsQueue.push([eventType, args]) - } - break; - case CONSTANTS.EVENTS.BID_WON: - if (window.atmtdAnalytics && window.atmtdAnalytics.bidWonHandler) { - window.atmtdAnalytics.bidWonHandler(args); - } else { - self.prettyLog('warn', `Aggregator not loaded, pushing ${eventType} to que instead ...`); - self.__atmtdAnalyticsQueue.push([eventType, args]) - } - break; - case CONSTANTS.EVENTS.NO_BID: - if (window.atmtdAnalytics && window.atmtdAnalytics.noBidHandler) { - window.atmtdAnalytics.noBidHandler(args); - } else { - self.prettyLog('warn', `Aggregator not loaded, pushing ${eventType} to que instead ...`); - self.__atmtdAnalyticsQueue.push([eventType, args]) - } - break; - case CONSTANTS.EVENTS.AUCTION_DEBUG: - if (window.atmtdAnalytics && window.atmtdAnalytics.auctionDebugHandler) { - window.atmtdAnalytics.auctionDebugHandler(args); - } else { - self.prettyLog('warn', `Aggregator not loaded, pushing ${eventType} to que instead ...`); - self.__atmtdAnalyticsQueue.push([eventType, args]) - } - break; - case CONSTANTS.EVENTS.BID_TIMEOUT: - if (window.atmtdAnalytics && window.atmtdAnalytics.bidderTimeoutHandler) { - window.atmtdAnalytics.bidderTimeoutHandler(args); - } else { - self.prettyLog('warn', `Aggregator not loaded, pushing ${eventType} to que instead ...`); - self.__atmtdAnalyticsQueue.push([eventType, args]) - } - break; - } - } -}); - -atmtdAdapter.originEnableAnalytics = atmtdAdapter.enableAnalytics - -atmtdAdapter.enableAnalytics = function (configuration) { - if ((configuration === undefined && typeof configuration !== 'object') || configuration.options === undefined) { - logError('A valid configuration must be passed to the Atmtd Analytics Adapter.'); - return; - } - - const conf = configuration.options - - if (conf === undefined || typeof conf !== 'object' || conf.siteID === undefined || conf.publisherID === undefined) { - logError('A valid publisher ID and siteID must be passed to the Atmtd Analytics Adapter.'); - return; - } - - self.initializeQueue() - self.addGPTHandlers() - - window.__atmtdSDKConfig = { - publisherID: conf.publisherID, - siteID: conf.siteID, - collectDebugMessages: conf.logDebug ? conf.logDebug : false - } - - logMessage(`Automatad Analytics Adapter enabled with sdk config`, window.__atmtdSDKConfig) - - // eslint-disable-next-line - atmtdAdapter.originEnableAnalytics(configuration) -}; - -/// /////////// ADAPTER REGISTRATION ////////////// - -adapterManager.registerAnalyticsAdapter({ - adapter: atmtdAdapter, - code: ADAPTER_CODE -}); - -export var self = { - __atmtdAnalyticsQueue, - processEvents, - initializeQueue, - addGPTHandlers, - prettyLog, - queuePointer, - retryCount, - isLoggingEnabled -} - -export default atmtdAdapter; diff --git a/modules/automatadAnalyticsAdapter.md b/modules/automatadAnalyticsAdapter.md deleted file mode 100644 index 2be1af87f20..00000000000 --- a/modules/automatadAnalyticsAdapter.md +++ /dev/null @@ -1,23 +0,0 @@ - -# Overview - -Module Name: Automatad Analytics Adapter -Module Type: Analytics Adapter -Maintainer: tech@automatad.com - -# Description - -Analytics adapter for automatad.com. Contact tech@automatad.com for information. - -# Test Parameters - -``` -{ - provider: 'automatadAnalytics', - options: { - publisherID: 'N8vZLx', - siteID: 'PXfvBq' - } -} - -``` \ No newline at end of file diff --git a/modules/axisBidAdapter.js b/modules/axisBidAdapter.js deleted file mode 100644 index 8d7f2dd04fd..00000000000 --- a/modules/axisBidAdapter.js +++ /dev/null @@ -1,210 +0,0 @@ -import { isFn, deepAccess, logMessage, logError } from '../src/utils.js'; -import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; - -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; -import { config } from '../src/config.js'; - -const BIDDER_CODE = 'axis'; -const AD_URL = 'https://prebid.axis-marketplace.com/pbjs'; -const SYNC_URL = 'https://cs.axis-marketplace.com'; - -function isBidResponseValid(bid) { - if (!bid.requestId || !bid.cpm || !bid.creativeId || !bid.ttl || !bid.currency) { - return false; - } - - switch (bid.mediaType) { - case BANNER: - return Boolean(bid.width && bid.height && bid.ad); - case VIDEO: - return Boolean(bid.vastUrl || bid.vastXml); - case NATIVE: - return Boolean(bid.native && bid.native.impressionTrackers && bid.native.impressionTrackers.length); - default: - return false; - } -} - -function getPlacementReqData(bid) { - const { params, bidId, mediaTypes } = bid; - const schain = bid.schain || {}; - const { integration, token } = params; - const bidfloor = getBidFloor(bid); - - const placement = { - integration, - token, - bidId, - schain, - bidfloor - }; - - if (mediaTypes && mediaTypes[BANNER]) { - placement.adFormat = BANNER; - placement.pos = mediaTypes[BANNER].pos; - placement.sizes = mediaTypes[BANNER].sizes; - } else if (mediaTypes && mediaTypes[VIDEO]) { - placement.adFormat = VIDEO; - placement.pos = mediaTypes[VIDEO].pos; - placement.playerSize = mediaTypes[VIDEO].playerSize; - placement.minduration = mediaTypes[VIDEO].minduration; - placement.maxduration = mediaTypes[VIDEO].maxduration; - placement.mimes = mediaTypes[VIDEO].mimes; - placement.protocols = mediaTypes[VIDEO].protocols; - placement.startdelay = mediaTypes[VIDEO].startdelay; - placement.placement = mediaTypes[VIDEO].placement; - placement.skip = mediaTypes[VIDEO].skip; - placement.skipafter = mediaTypes[VIDEO].skipafter; - placement.minbitrate = mediaTypes[VIDEO].minbitrate; - placement.maxbitrate = mediaTypes[VIDEO].maxbitrate; - placement.delivery = mediaTypes[VIDEO].delivery; - placement.playbackmethod = mediaTypes[VIDEO].playbackmethod; - placement.api = mediaTypes[VIDEO].api; - placement.linearity = mediaTypes[VIDEO].linearity; - placement.context = mediaTypes[VIDEO].context; - } else if (mediaTypes && mediaTypes[NATIVE]) { - placement.native = mediaTypes[NATIVE]; - placement.adFormat = NATIVE; - } - - return placement; -} - -function getBidFloor(bid) { - if (!isFn(bid.getFloor)) { - return deepAccess(bid, 'params.bidfloor', 0); - } - - try { - const bidFloor = bid.getFloor({ - currency: 'USD', - mediaType: '*', - size: '*', - }); - return bidFloor.floor; - } catch (e) { - logError(e); - return 0; - } -} - -export const spec = { - code: BIDDER_CODE, - supportedMediaTypes: [BANNER, VIDEO, NATIVE], - - isBidRequestValid: (bid = {}) => { - const { params, bidId, mediaTypes } = bid; - let valid = Boolean(bidId && params && params.integration && params.token); - - if (mediaTypes && mediaTypes[BANNER]) { - valid = valid && Boolean(mediaTypes[BANNER] && mediaTypes[BANNER].sizes); - } else if (mediaTypes && mediaTypes[VIDEO]) { - valid = valid && Boolean(mediaTypes[VIDEO] && mediaTypes[VIDEO].playerSize); - } else if (mediaTypes && mediaTypes[NATIVE]) { - valid = valid && Boolean(mediaTypes[NATIVE]); - } else { - valid = false; - } - return valid; - }, - - buildRequests: (validBidRequests = [], bidderRequest = {}) => { - // convert Native ORTB definition to old-style prebid native definition - validBidRequests = convertOrtbRequestToProprietaryNative(validBidRequests); - - let deviceWidth = 0; - let deviceHeight = 0; - - let winLocation; - try { - const winTop = window.top; - deviceWidth = winTop.screen.width; - deviceHeight = winTop.screen.height; - winLocation = winTop.location; - } catch (e) { - logMessage(e); - winLocation = window.location; - } - - const refferUrl = bidderRequest.refererInfo && bidderRequest.refererInfo.page; - let refferLocation; - try { - refferLocation = refferUrl && new URL(refferUrl); - } catch (e) { - logMessage(e); - } - - let location = refferLocation || winLocation; - const language = (navigator && navigator.language) ? navigator.language.split('-')[0] : ''; - const host = location.host; - const page = location.pathname; - const secure = location.protocol === 'https:' ? 1 : 0; - const placements = []; - const request = { - deviceWidth, - deviceHeight, - language, - secure, - host, - page, - placements, - iabCat: deepAccess(bidderRequest, 'ortb2.site.cat'), - coppa: deepAccess(bidderRequest, 'ortb2.regs.coppa') ? 1 : 0, - ccpa: bidderRequest.uspConsent || undefined, - gdpr: bidderRequest.gdprConsent || undefined, - tmax: bidderRequest.timeout || 3000, - }; - - const len = validBidRequests.length; - for (let i = 0; i < len; i++) { - const bid = validBidRequests[i]; - placements.push(getPlacementReqData(bid)); - } - - return { - method: 'POST', - url: AD_URL, - data: request - }; - }, - - interpretResponse: (serverResponse) => { - let response = []; - for (let i = 0; i < serverResponse.body.length; i++) { - let resItem = serverResponse.body[i]; - if (isBidResponseValid(resItem)) { - const advertiserDomains = resItem.adomain && resItem.adomain.length ? resItem.adomain : []; - resItem.meta = { ...resItem.meta, advertiserDomains }; - - response.push(resItem); - } - } - return response; - }, - - getUserSyncs: (syncOptions, serverResponses, gdprConsent, uspConsent) => { - let syncType = syncOptions.iframeEnabled ? 'iframe' : 'image'; - let syncUrl = SYNC_URL + `/${syncType}?pbjs=1`; - if (gdprConsent && gdprConsent.consentString) { - if (typeof gdprConsent.gdprApplies === 'boolean') { - syncUrl += `&gdpr=${Number(gdprConsent.gdprApplies)}&gdpr_consent=${gdprConsent.consentString}`; - } else { - syncUrl += `&gdpr=0&gdpr_consent=${gdprConsent.consentString}`; - } - } - if (uspConsent && uspConsent.consentString) { - syncUrl += `&ccpa=${uspConsent.consentString}`; - } - - const coppa = config.getConfig('coppa') ? 1 : 0; - syncUrl += `&coppa=${coppa}`; - - return [{ - type: syncType, - url: syncUrl - }]; - } -}; - -registerBidder(spec); diff --git a/modules/axisBidAdapter.md b/modules/axisBidAdapter.md deleted file mode 100644 index d1625a56176..00000000000 --- a/modules/axisBidAdapter.md +++ /dev/null @@ -1,83 +0,0 @@ -# Overview - -``` -Module Name: Axis Bidder Adapter -Module Type: Axis Bidder Adapter -Maintainer: help@axis-marketplace.com -``` - -# Description - -Connects to Axis exchange for bids. -Axis 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] ], - pos: 1 - } - }, - bids: [ - { - bidder: 'axis', - params: { - integration: '000000', - token: '000000' - } - } - ] - }, - { - code: 'addunit2', - mediaTypes: { - video: { - playerSize: [ [640, 480] ], - minduration: 5, - maxduration: 60, - pos: 1 - } - }, - bids: [ - { - bidder: 'axis', - params: { - integration: '000000', - token: '000000' - } - } - ] - }, - { - code: 'addunit3', - mediaTypes: { - native: { - title: { - required: true - }, - body: { - required: true - }, - icon: { - required: true, - size: [64, 64] - } - } - }, - bids: [ - { - bidder: 'axis', - params: { - integration: '000000', - token: '000000' - } - } - ] - } - ]; -``` diff --git a/modules/beachfrontBidAdapter.js b/modules/beachfrontBidAdapter.js index 658fc30b43b..37de8e637a9 100644 --- a/modules/beachfrontBidAdapter.js +++ b/modules/beachfrontBidAdapter.js @@ -7,15 +7,14 @@ import { isFn, logWarn, parseSizesInput, - parseUrl, - formatQS + parseUrl } 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 {find, includes} from '../src/polyfill.js'; -const ADAPTER_VERSION = '1.20'; +const ADAPTER_VERSION = '1.19'; const ADAPTER_NAME = 'BFIO_PREBID'; const OUTSTREAM = 'outstream'; const CURRENCY = 'USD'; @@ -23,8 +22,6 @@ const CURRENCY = 'USD'; export const VIDEO_ENDPOINT = 'https://reachms.bfmio.com/bid.json?exchange_id='; export const BANNER_ENDPOINT = 'https://display.bfmio.com/prebid_display'; export const OUTSTREAM_SRC = 'https://player-cdn.beachfrontmedia.com/playerapi/loader/outstream.js'; -export const SYNC_IFRAME_ENDPOINT = 'https://sync.bfmio.com/sync_iframe'; -export const SYNC_IMAGE_ENDPOINT = 'https://sync.bfmio.com/syncb'; export const VIDEO_TARGETING = ['mimes', 'playbackmethod', 'maxduration', 'placement', 'skip', 'skipmin', 'skipafter']; export const DEFAULT_MIMES = ['video/mp4', 'application/javascript']; @@ -155,22 +152,11 @@ export const spec = { } }, - getUserSyncs(syncOptions, serverResponses = [], gdprConsent = {}, uspConsent = '', gppConsent = {}) { + getUserSyncs(syncOptions, serverResponses = [], gdprConsent = {}, uspConsent = '') { + let syncs = []; let { gdprApplies, consentString = '' } = gdprConsent; - let { gppString = '', applicableSections = [] } = gppConsent; let bannerResponse = find(serverResponses, (res) => isArray(res.body)); - let syncs = []; - let params = { - id: appId, - gdpr: gdprApplies ? 1 : 0, - gc: consentString, - gce: 1, - us_privacy: uspConsent, - gpp: gppString, - gpp_sid: Array.isArray(applicableSections) ? applicableSections.join(',') : '' - }; - if (bannerResponse) { if (syncOptions.iframeEnabled) { bannerResponse.body @@ -185,12 +171,12 @@ export const spec = { } else if (syncOptions.iframeEnabled) { syncs.push({ type: 'iframe', - url: `${SYNC_IFRAME_ENDPOINT}?ifg=1&${formatQS(params)}` + url: `https://sync.bfmio.com/sync_iframe?ifg=1&id=${appId}&gdpr=${gdprApplies ? 1 : 0}&gc=${consentString}&gce=1&us_privacy=${uspConsent}` }); } else if (syncOptions.pixelEnabled) { syncs.push({ type: 'image', - url: `${SYNC_IMAGE_ENDPOINT}?pid=144&${formatQS(params)}` + url: `https://sync.bfmio.com/syncb?pid=144&id=${appId}&gdpr=${gdprApplies ? 1 : 0}&gc=${consentString}&gce=1&us_privacy=${uspConsent}` }); } @@ -418,12 +404,6 @@ function createVideoRequestData(bid, bidderRequest) { deepSetValue(payload, 'user.ext.consent', consentString); } - if (bidderRequest && bidderRequest.gppConsent) { - let { gppString, applicableSections } = bidderRequest.gppConsent; - deepSetValue(payload, 'regs.gpp', gppString); - deepSetValue(payload, 'regs.gpp_sid', applicableSections); - } - if (bid.schain) { deepSetValue(payload, 'source.ext.schain', bid.schain); } @@ -479,12 +459,6 @@ function createBannerRequestData(bids, bidderRequest) { payload.gdprConsent = consentString; } - if (bidderRequest && bidderRequest.gppConsent) { - let { gppString, applicableSections } = bidderRequest.gppConsent; - payload.gpp = gppString; - payload.gppSid = applicableSections; - } - if (bids[0] && bids[0].schain) { payload.schain = bids[0].schain; } diff --git a/modules/beopBidAdapter.js b/modules/beopBidAdapter.js index ad52af824ee..6348854ec2a 100644 --- a/modules/beopBidAdapter.js +++ b/modules/beopBidAdapter.js @@ -1,6 +1,7 @@ import { buildUrl, - deepAccess, getBidIdParameter, + deepAccess, + getBidIdParameter, getValue, isArray, logInfo, @@ -23,11 +24,11 @@ export const spec = { gvlid: TCF_VENDOR_ID, aliases: ['bp'], /** - * Test if the bid request is valid. - * - * @param {bid} : The Bid params - * @return boolean true if the bid request is valid (aka contains a valid accountId or networkId and is open for BANNER), false otherwise. - */ + * Test if the bid request is valid. + * + * @param {bid} : The Bid params + * @return boolean true if the bid request is valid (aka contains a valid accountId or networkId and is open for BANNER), false otherwise. + */ isBidRequestValid: function(bid) { const id = bid.params.accountId || bid.params.networkId; if (id === null || typeof id === 'undefined') { @@ -39,18 +40,16 @@ export const spec = { return bid.mediaTypes.banner !== null && typeof bid.mediaTypes.banner !== 'undefined'; }, /** - * Create a BeOp server request from a list of BidRequest - * - * @param {validBidRequests[], ...} : The array of validated bidRequests - * @param {... , bidderRequest} : Common params for each bidRequests - * @return ServerRequest Info describing the request to the BeOp's server - */ + * Create a BeOp server request from a list of BidRequest + * + * @param {validBidRequests[], ...} : The array of validated bidRequests + * @param {... , bidderRequest} : Common params for each bidRequests + * @return ServerRequest Info describing the request to the BeOp's server + */ buildRequests: function(validBidRequests, bidderRequest) { const slots = validBidRequests.map(beOpRequestSlotsMaker); - const firstPartyData = bidderRequest.ortb2 || {}; - const psegs = firstPartyData.user?.ext?.permutive || firstPartyData.user?.ext?.data?.permutive || []; - const userBpSegs = firstPartyData.user?.ext?.bpsegs || firstPartyData.user?.ext?.data?.bpsegs || []; - const siteBpSegs = firstPartyData.site?.ext?.bpsegs || firstPartyData.site?.ext?.data?.bpsegs || []; + const firstPartyData = bidderRequest.ortb2; + const psegs = (firstPartyData && firstPartyData.user && firstPartyData.user.ext && firstPartyData.user.ext.data) ? firstPartyData.user.ext.data.permutive : undefined; const pageUrl = getPageUrl(bidderRequest.refererInfo, window); const gdpr = bidderRequest.gdprConsent; const firstSlot = slots[0]; @@ -62,8 +61,6 @@ export const spec = { nid: firstSlot.nid, nptnid: firstSlot.nptnid, pid: firstSlot.pid, - psegs: psegs, - bpsegs: (userBpSegs.concat(siteBpSegs)).map(item => item.toString()), url: pageUrl, lang: (window.navigator.language || window.navigator.languages[0]), kwds: keywords, @@ -74,6 +71,10 @@ export const spec = { tc_string: (gdpr && gdpr.gdprApplies) ? gdpr.consentString : null, }; + if (psegs) { + Object.assign(payloadObject, {psegs: psegs}); + } + const payloadString = JSON.stringify(payloadObject); return { method: 'POST', @@ -128,6 +129,8 @@ function buildTrackingParams(data, info, value) { nptnid: params.networkPartnerId, bid: data.bidId || data.requestId, sl_n: data.adUnitCode, + // TODO: fix auctionId leak: https://github.com/prebid/Prebid.js/issues/9781 + aid: data.auctionId, se_ca: 'bid', se_ac: info, se_va: value, @@ -155,6 +158,8 @@ function beOpRequestSlotsMaker(bid) { bid: getBidIdParameter('bidId', bid), brid: getBidIdParameter('bidderRequestId', bid), name: getBidIdParameter('adUnitCode', bid), + // TODO: fix auctionId leak: https://github.com/prebid/Prebid.js/issues/9781 + aid: getBidIdParameter('auctionId', bid), tid: bid.ortb2Imp?.ext?.tid || '', brc: getBidIdParameter('bidRequestsCount', bid), bdrc: getBidIdParameter('bidderRequestCount', bid), diff --git a/modules/beopBidAdapter.md b/modules/beopBidAdapter.md index 53d2542b69c..c0e88cb1ceb 100644 --- a/modules/beopBidAdapter.md +++ b/modules/beopBidAdapter.md @@ -9,14 +9,13 @@ Module that connects to BeOp's demand sources # Test Parameters - ``` var adUnits = [ { code: 'in-article', mediaTypes: { banner: { - sizes: [[1,1]], + sizes: [[1,1]], } }, bids: [ @@ -32,36 +31,3 @@ Module that connects to BeOp's demand sources ]; ``` -# Custom Bidder data - -If you want to pass your first party data to BeOp, you can set your bidder `config.ortb2` object with - -```json -{ - "site": { - "ext": { - "bpsegs": ["Your", 1, "ST", "party", "data"], - "data": { - "bpsegs": ["Your", 1, "ST", "party", "data"] - } - } - }, - "user": { - "ext": { - "bpsegs": ["Your", 1, "ST", "party", "data"], - "data": { - "bpsegs": ["Your", 1, "ST", "party", "data"] - } - } - } -} -``` - -You can choose the location between: - -- `site.ext` -- `site.ext.data` -- `user.ext` -- `user.ext.data` - -and our BidAdapter will be able to find them diff --git a/modules/betweenBidAdapter.js b/modules/betweenBidAdapter.js index 4a953875d99..d615e433cc0 100644 --- a/modules/betweenBidAdapter.js +++ b/modules/betweenBidAdapter.js @@ -1,15 +1,6 @@ import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {parseSizesInput} from '../src/utils.js'; +import {getAdUnitSizes, parseSizesInput} from '../src/utils.js'; import {includes} from '../src/polyfill.js'; -import {getAdUnitSizes} from '../libraries/sizeUtils/sizeUtils.js'; - -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse - * @typedef {import('../src/adapters/bidderFactory.js').SyncOptions} SyncOptions - * @typedef {import('../src/adapters/bidderFactory.js').UserSync} UserSync - */ const BIDDER_CODE = 'between'; let ENDPOINT = 'https://ads.betweendigital.com/adjson?t=prebid'; diff --git a/modules/bidViewability.js b/modules/bidViewability.js index be18095e369..a5cab99b1a7 100644 --- a/modules/bidViewability.js +++ b/modules/bidViewability.js @@ -67,8 +67,6 @@ export let logWinningBidNotFound = (slot) => { export let impressionViewableHandler = (globalModuleConfig, slot, event) => { let respectiveBid = getMatchingWinningBidForGPTSlot(globalModuleConfig, slot); - let respectiveDeferredAdUnit = getGlobal().adUnits.find(adUnit => adUnit.deferBilling && respectiveBid.adUnitCode === adUnit.code); - if (respectiveBid === null) { logWinningBidNotFound(slot); } else { @@ -76,11 +74,6 @@ export let impressionViewableHandler = (globalModuleConfig, slot, event) => { fireViewabilityPixels(globalModuleConfig, respectiveBid); // trigger respective bidder's onBidViewable handler adapterManager.callBidViewableBidder(respectiveBid.adapterCode || respectiveBid.bidder, respectiveBid); - - if (respectiveDeferredAdUnit) { - adapterManager.callBidBillableBidder(respectiveBid); - } - // emit the BID_VIEWABLE event with bid details, this event can be consumed by bidders and analytics pixels events.emit(CONSTANTS.EVENTS.BID_VIEWABLE, respectiveBid); } diff --git a/modules/bidViewability.md b/modules/bidViewability.md index 922a4a9def4..78a1539fb1a 100644 --- a/modules/bidViewability.md +++ b/modules/bidViewability.md @@ -2,20 +2,19 @@ Module Name: bidViewability -Purpose: Track when a bid is viewable (and also ready for billing) +Purpose: Track when a bid is viewable Maintainer: harshad.mane@pubmatic.com # Description -- This module, when included, will trigger a BID_VIEWABLE event which can be consumed by Analytics adapters, bidders will need to implement the `onBidViewable` method to capture this event -- Bidders can check if this module is part of the final build and whether it is enabled or not by accessing ```pbjs.getConfig('bidViewability')``` +- This module, when included, will trigger a BID_VIEWABLE event which can be consumed by Analytics adapters, bidders will need to implement `onBidViewable` method to capture this event +- Bidderes can check if this module is part of the final build and whether it is enabled or not by accessing ```pbjs.getConfig('bidViewability')``` - 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 +- The module does not work with adserver 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``` -- 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). +- When a rendered PBJS bid is viewable the module will trigger BID_VIEWABLE event, which can be consumed by bidders and analytics adapters +- For the viewable bid if ```bid.vurls type array``` param is and module config ``` firePixels: true ``` is set then the URLs mentioned in bid.vurls will be executed. Please note that GDPR and USP related parameters will be added to the given URLs # Params - enabled [required] [type: boolean, default: false], when set to true, the module will emit BID_VIEWABLE when applicable @@ -45,6 +44,6 @@ Refer: https://support.google.com/admanager/answer/4524488 ``` # Please Note: -- This module doesn't seem to work with Instream Video, https://docs.prebid.org/dev-docs/examples/instream-banner-mix.html as GPT's impressionViewable event is not triggered for instream-video-creative -- Works with Banner, Outsteam and Native creatives +- Doesn't seems to work with Instream Video, https://docs.prebid.org/dev-docs/examples/instream-banner-mix.html as GPT's impressionViewable event is not triggered for instream-video-creative +- Works with Banner, Outsteam, Native creatives diff --git a/modules/biddoBidAdapter.js b/modules/biddoBidAdapter.js index cf39c572629..5512ca60f8e 100644 --- a/modules/biddoBidAdapter.js +++ b/modules/biddoBidAdapter.js @@ -1,11 +1,6 @@ import {registerBidder} from '../src/adapters/bidderFactory.js'; import {BANNER} from '../src/mediaTypes.js'; -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse - */ - const BIDDER_CODE = 'biddo'; const ENDPOINT_URL = 'https://ad.adopx.net/delivery/impress'; diff --git a/modules/bidglassBidAdapter.js b/modules/bidglassBidAdapter.js index 6074d1a0e4c..3184372881b 100644 --- a/modules/bidglassBidAdapter.js +++ b/modules/bidglassBidAdapter.js @@ -1,12 +1,7 @@ -import {_each, isArray, deepClone, getUniqueIdentifierStr, getBidIdParameter} from '../src/utils.js'; +import { _each, isArray, getBidIdParameter, deepClone, getUniqueIdentifierStr } from '../src/utils.js'; +// import {config} from 'src/config.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse - */ - const BIDDER_CODE = 'bidglass'; export const spec = { diff --git a/modules/big-richmediaBidAdapter.js b/modules/big-richmediaBidAdapter.js index ecb1724c2a1..8a03aac1ace 100644 --- a/modules/big-richmediaBidAdapter.js +++ b/modules/big-richmediaBidAdapter.js @@ -3,11 +3,6 @@ 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 - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - */ - const BIDDER_CODE = 'big-richmedia'; const metadataByRequestId = {}; diff --git a/modules/bizzclickBidAdapter.js b/modules/bizzclickBidAdapter.js index d2eba3f0f81..dc7731231ab 100644 --- a/modules/bizzclickBidAdapter.js +++ b/modules/bizzclickBidAdapter.js @@ -1,77 +1,322 @@ -import { ortbConverter } from '../libraries/ortbConverter/converter.js'; -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { config } from '../src/config.js'; -import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; +import {_map, deepAccess, deepSetValue, getDNT, logMessage, logWarn} 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'; const BIDDER_CODE = 'bizzclick'; -const SOURCE_ID_MACRO = '[sourceid]'; -const ACCOUNT_ID_MACRO = '[accountid]'; -const HOST_MACRO = '[host]'; -const URL = `https://${HOST_MACRO}.bizzclick.com/bid?rtb_seat_id=${SOURCE_ID_MACRO}&secret_key=${ACCOUNT_ID_MACRO}&integration_type=prebidjs`; -const DEFAULT_CURRENCY = 'USD'; -const DEFAULT_HOST = 'us-e-node1'; - -const converter = ortbConverter({ - context: { - netRevenue: true, - ttl: 20, +const ACCOUNTID_MACROS = '[account_id]'; +const URL_ENDPOINT = `https://us-e-node1.bizzclick.com/bid?rtb_seat_id=prebidjs&secret_key=${ACCOUNTID_MACROS}`; +const NATIVE_ASSET_IDS = { 0: 'title', 2: 'icon', 3: 'image', 5: 'sponsoredBy', 4: 'body', 1: 'cta' }; +const NATIVE_PARAMS = { + title: { + id: 0, + name: 'title' }, - imp(buildImp, bidRequest, context) { - const imp = buildImp(bidRequest, context); - if (!imp.bidfloor) imp.bidfloor = bidRequest.params.bidfloor || 0; - imp.ext = { - [BIDDER_CODE]: { - accountId: bidRequest.params.accountId, - sourceId: bidRequest.params.sourceId, - host: bidRequest.params.host || DEFAULT_HOST, - } - } - return imp; + icon: { + id: 2, + type: 1, + name: 'img' + }, + image: { + id: 3, + type: 3, + name: 'img' + }, + sponsoredBy: { + id: 5, + name: 'data', + type: 1 }, - request(buildRequest, imps, bidderRequest, context) { - const request = buildRequest(imps, bidderRequest, context); - const bid = context.bidRequests[0]; - request.test = config.getConfig('debug') ? 1 : 0; - if (!request.cur) request.cur = [bid.params.currency || DEFAULT_CURRENCY]; - return request; + body: { + id: 4, + name: 'data', + type: 2 }, - bidResponse(buildBidResponse, bid, context) { - const bidResponse = buildBidResponse(bid, context); - bidResponse.cur = bid.cur || DEFAULT_CURRENCY; - return bidResponse; + cta: { + id: 1, + type: 12, + name: 'data' } -}); - +}; +const NATIVE_VERSION = '1.2'; export const spec = { code: BIDDER_CODE, supportedMediaTypes: [BANNER, VIDEO, NATIVE], - + /** + * Determines whether or not the given bid request is valid. + * + * @param {object} bid The bid to validate. + * @return boolean True if this is a valid bid, and false otherwise. + */ isBidRequestValid: (bid) => { - return Boolean(bid.params.sourceId) && Boolean(bid.params.accountId); + return Boolean(bid.params.accountId) && Boolean(bid.params.placementId) }, - + /** + * Make a server request from the list of BidRequests. + * + * @param {BidRequest[]} validBidRequests A non-empty list of valid bid requests that should be sent to the Server. + * @return ServerRequest Info describing the request to the server. + */ buildRequests: (validBidRequests, bidderRequest) => { - if (validBidRequests && validBidRequests.length === 0) return []; - const { sourceId, accountId } = validBidRequests[0].params; - const host = validBidRequests[0].params.host || 'USE'; - const endpointURL = URL.replace(HOST_MACRO, host || DEFAULT_HOST) - .replace(ACCOUNT_ID_MACRO, accountId) - .replace(SOURCE_ID_MACRO, sourceId); - const request = converter.toORTB({ bidRequests: validBidRequests, bidderRequest }); + // convert Native ORTB definition to old-style prebid native definition + validBidRequests = convertOrtbRequestToProprietaryNative(validBidRequests); + + if (validBidRequests && validBidRequests.length === 0) return [] + let accuontId = validBidRequests[0].params.accountId; + const endpointURL = URL_ENDPOINT.replace(ACCOUNTID_MACROS, accuontId); + let winTop = window; + let location; + try { + location = new URL(bidderRequest.refererInfo.page) + winTop = window.top; + } catch (e) { + location = winTop.location; + logMessage(e); + }; + let bids = []; + for (let bidRequest of validBidRequests) { + let impObject = prepareImpObject(bidRequest); + let data = { + id: bidRequest.bidId, + test: config.getConfig('debug') ? 1 : 0, + at: 1, + cur: ['USD'], + device: { + w: winTop.screen.width, + h: winTop.screen.height, + dnt: getDNT() ? 1 : 0, + language: (navigator && navigator.language) ? navigator.language.indexOf('-') != -1 ? navigator.language.split('-')[0] : navigator.language : '', + }, + site: { + page: location.pathname, + host: location.host + }, + source: { + tid: bidRequest.ortb2Imp?.ext?.tid, + ext: { + schain: {} + } + }, + regs: { + coppa: config.getConfig('coppa') === true ? 1 : 0, + ext: {} + }, + user: { + ext: {} + }, + ext: { + ts: Date.now() + }, + tmax: bidRequest.timeout, + imp: [impObject], + }; + + let connection = navigator.connection || navigator.webkitConnection; + if (connection && connection.effectiveType) { + data.device.connectiontype = connection.effectiveType; + } + if (bidRequest) { + if (bidRequest.schain) { + deepSetValue(data, 'source.ext.schain', bidRequest.schain); + } + + if (bidRequest.gdprConsent && bidRequest.gdprConsent.gdprApplies) { + deepSetValue(data, 'regs.ext.gdpr', bidRequest.gdprConsent.gdprApplies ? 1 : 0); + deepSetValue(data, 'user.ext.consent', bidRequest.gdprConsent.consentString); + } + + if (bidRequest.uspConsent !== undefined) { + deepSetValue(data, 'regs.ext.us_privacy', bidRequest.uspConsent); + } + } + bids.push(data) + } return { method: 'POST', url: endpointURL, - data: request + data: bids }; }, + /** + * 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: (serverResponse) => { + if (!serverResponse || !serverResponse.body) return [] + let bizzclickResponse = serverResponse.body; + let bids = []; + for (let response of bizzclickResponse) { + let mediaType = response.seatbid[0].bid[0].ext && response.seatbid[0].bid[0].ext.mediaType ? response.seatbid[0].bid[0].ext.mediaType : BANNER; + let bid = { + requestId: response.id, + cpm: response.seatbid[0].bid[0].price, + width: response.seatbid[0].bid[0].w, + height: response.seatbid[0].bid[0].h, + ttl: response.ttl || 1200, + currency: response.cur || 'USD', + netRevenue: true, + creativeId: response.seatbid[0].bid[0].crid, + dealId: response.seatbid[0].bid[0].dealid, + mediaType: mediaType + }; + + bid.meta = {}; + if (response.seatbid[0].bid[0].adomain && response.seatbid[0].bid[0].adomain.length > 0) { + bid.meta.advertiserDomains = response.seatbid[0].bid[0].adomain; + } - interpretResponse: (response, request) => { - if (response?.body) { - const bids = converter.fromORTB({ response: response.body, request: request.data }).bids; - return bids; + switch (mediaType) { + case VIDEO: + bid.vastXml = response.seatbid[0].bid[0].adm + bid.vastUrl = response.seatbid[0].bid[0].ext.vastUrl + break + case NATIVE: + bid.native = parseNative(response.seatbid[0].bid[0].adm) + break + default: + bid.ad = response.seatbid[0].bid[0].adm + } + bids.push(bid); } - return []; + return bids; }, }; - +/** + * Determine type of request + * + * @param bidRequest + * @param type + * @returns {boolean} + */ +const checkRequestType = (bidRequest, type) => { + return (typeof deepAccess(bidRequest, `mediaTypes.${type}`) !== 'undefined'); +} +const parseNative = admObject => { + const { assets, link, imptrackers, jstracker } = admObject.native; + const result = { + clickUrl: link.url, + clickTrackers: link.clicktrackers || undefined, + impressionTrackers: imptrackers || undefined, + javascriptTrackers: jstracker ? [ jstracker ] : undefined + }; + assets.forEach(asset => { + const kind = NATIVE_ASSET_IDS[asset.id]; + const content = kind && asset[NATIVE_PARAMS[kind].name]; + if (content) { + result[kind] = content.text || content.value || { url: content.url, width: content.w, height: content.h }; + } + }); + return result; +} +const prepareImpObject = (bidRequest) => { + let impObject = { + id: bidRequest.transactionId, + secure: 1, + ext: { + placementId: bidRequest.params.placementId + } + }; + if (checkRequestType(bidRequest, BANNER)) { + impObject.banner = addBannerParameters(bidRequest); + } + if (checkRequestType(bidRequest, VIDEO)) { + impObject.video = addVideoParameters(bidRequest); + } + if (checkRequestType(bidRequest, NATIVE)) { + impObject.native = { + ver: NATIVE_VERSION, + request: addNativeParameters(bidRequest) + }; + } + return impObject +}; +const addNativeParameters = bidRequest => { + let impObject = { + // TODO: top-level ID is not in ORTB native 1.2, is this intentional? + // (despite the name, this appears to be an ORTB native request - not an imp - object) + id: bidRequest.bidId, + ver: NATIVE_VERSION, + }; + const assets = _map(bidRequest.mediaTypes.native, (bidParams, key) => { + const props = NATIVE_PARAMS[key]; + const asset = { + required: bidParams.required & 1, + }; + if (props) { + asset.id = props.id; + let wmin, hmin; + let aRatios = bidParams.aspect_ratios; + if (aRatios && aRatios[0]) { + aRatios = aRatios[0]; + wmin = aRatios.min_width || 0; + hmin = aRatios.ratio_height * wmin / aRatios.ratio_width | 0; + } + if (bidParams.sizes) { + const sizes = flatten(bidParams.sizes); + wmin = sizes[0]; + hmin = sizes[1]; + } + asset[props.name] = {}; + if (bidParams.len) asset[props.name]['len'] = bidParams.len; + if (props.type) asset[props.name]['type'] = props.type; + if (wmin) asset[props.name]['wmin'] = wmin; + if (hmin) asset[props.name]['hmin'] = hmin; + return asset; + } + }).filter(Boolean); + impObject.assets = assets; + return impObject +} +const addBannerParameters = (bidRequest) => { + let bannerObject = {}; + const size = parseSizes(bidRequest, 'banner'); + bannerObject.w = size[0]; + bannerObject.h = size[1]; + return bannerObject; +}; +const parseSizes = (bid, mediaType) => { + let mediaTypes = bid.mediaTypes; + if (mediaType === 'video') { + let size = []; + if (mediaTypes.video && mediaTypes.video.w && mediaTypes.video.h) { + size = [ + mediaTypes.video.w, + mediaTypes.video.h + ]; + } else if (Array.isArray(deepAccess(bid, 'mediaTypes.video.playerSize')) && bid.mediaTypes.video.playerSize.length === 1) { + size = bid.mediaTypes.video.playerSize[0]; + } else if (Array.isArray(bid.sizes) && bid.sizes.length > 0 && Array.isArray(bid.sizes[0]) && bid.sizes[0].length > 1) { + size = bid.sizes[0]; + } + return size; + } + let sizes = []; + if (Array.isArray(mediaTypes.banner.sizes)) { + sizes = mediaTypes.banner.sizes[0]; + } else if (Array.isArray(bid.sizes) && bid.sizes.length > 0) { + sizes = bid.sizes + } else { + logWarn('no sizes are setup or found'); + } + return sizes +} +const addVideoParameters = (bidRequest) => { + let videoObj = {}; + let supportParamsList = ['mimes', 'minduration', 'maxduration', 'protocols', 'startdelay', 'placement', 'skip', 'skipafter', 'minbitrate', 'maxbitrate', 'delivery', 'playbackmethod', 'api', 'linearity'] + for (let param of supportParamsList) { + if (bidRequest.mediaTypes.video[param] !== undefined) { + videoObj[param] = bidRequest.mediaTypes.video[param]; + } + } + const size = parseSizes(bidRequest, 'video'); + videoObj.w = size[0]; + videoObj.h = size[1]; + return videoObj; +} +const flatten = arr => { + return [].concat(...arr); +} registerBidder(spec); diff --git a/modules/bizzclickBidAdapter.md b/modules/bizzclickBidAdapter.md index ad342f34e07..6fc1bebf546 100644 --- a/modules/bizzclickBidAdapter.md +++ b/modules/bizzclickBidAdapter.md @@ -11,99 +11,94 @@ Maintainer: support@bizzclick.com Module that connects to BizzClick SSP demand sources # Test Parameters +``` + var adUnits = [{ + code: 'placementId', + mediaTypes: { + banner: { + sizes: [[300, 250], [300,600]] + } + }, + bids: [{ + bidder: 'bizzclick', + params: { + placementId: 'hash', + accountId: 'accountId' + } + }] + }, + { + code: 'native_example', + // sizes: [[1, 1]], + mediaTypes: { + native: { + title: { + required: true, + len: 800 + }, + image: { + required: true, + len: 80 + }, + sponsoredBy: { + required: true + }, + clickUrl: { + required: true + }, + privacyLink: { + required: false + }, + body: { + required: true + }, + icon: { + required: true, + sizes: [50, 50] + } + } -```js -const adUnits = [ - { - code: "placementId", - mediaTypes: { - banner: { - sizes: [ - [300, 250], - [300, 600], - ], - }, - }, - bids: [ - { - bidder: "bizzclick", - params: { - placementId: "hash", - accountId: "accountId", - host: "host", - }, - }, - ], - }, - { - code: "native_example", - // sizes: [[1, 1]], - mediaTypes: { - native: { - title: { - required: true, - len: 800, - }, - image: { - required: true, - len: 80, - }, - sponsoredBy: { - required: true, - }, - clickUrl: { - required: true, - }, - privacyLink: { - required: false, - }, - body: { - required: true, - }, - icon: { - required: true, - sizes: [50, 50], - }, - }, - }, + }, + bids: [ { + bidder: 'bizzclick', + params: { + placementId: 'hash', + accountId: 'accountId' + } + }] + }, + { + code: 'video1', + sizes: [640,480], + mediaTypes: { video: { + minduration:0, + maxduration:999, + boxingallowed:1, + skip:0, + mimes:[ + 'application/javascript', + 'video/mp4' + ], + w:1920, + h:1080, + protocols:[ + 2 + ], + linearity:1, + api:[ + 1, + 2 + ] + } }, bids: [ - { - bidder: "bizzclick", - params: { - placementId: "hash", - accountId: "accountId", - host: "host", - }, - }, - ], - }, - { - code: "video1", - sizes: [640, 480], - mediaTypes: { - video: { - minduration: 0, - maxduration: 999, - boxingallowed: 1, - skip: 0, - mimes: ["application/javascript", "video/mp4"], - w: 1920, - h: 1080, - protocols: [2], - linearity: 1, - api: [1, 2], - }, - }, - bids: [ - { - bidder: "bizzclick", - params: { - placementId: "hash", - accountId: "accountId", - host: "host", - }, - }, - ], - }, -]; -``` + { + bidder: 'bizzclick', + params: { + placementId: 'hash', + accountId: 'accountId' + } + } + ] + } + ]; +``` \ No newline at end of file diff --git a/modules/bliinkBidAdapter.js b/modules/bliinkBidAdapter.js index debe956d7eb..9a9d74d14c1 100644 --- a/modules/bliinkBidAdapter.js +++ b/modules/bliinkBidAdapter.js @@ -2,9 +2,8 @@ // eslint-disable-next-line prebid/validate-imports import { registerBidder } from '../src/adapters/bidderFactory.js' import { config } from '../src/config.js' -import { _each, deepAccess, deepSetValue, getWindowSelf, getWindowTop } from '../src/utils.js' +import {_each, deepAccess, deepSetValue} from '../src/utils.js' export const BIDDER_CODE = 'bliink' -export const GVL_ID = 658 export const BLIINK_ENDPOINT_ENGINE = 'https://engine.bliink.io/prebid' export const BLIINK_ENDPOINT_COOKIE_SYNC_IFRAME = 'https://tag.bliink.io/usersync.html' @@ -13,10 +12,9 @@ export const META_DESCRIPTION = 'description' const VIDEO = 'video' const BANNER = 'banner' -window.bliinkBid = window.bliinkBid || {}; + const supportedMediaTypes = [BANNER, VIDEO] const aliasBidderCode = ['bk'] -const CURRENCY = 'EUR'; /** * @description get coppa value from config @@ -25,36 +23,6 @@ function getCoppa() { return config.getConfig('coppa') === true ? 1 : 0; } -/** - * Retrieves the effective connection type from the browser's Navigator API. - * @returns {string} The effective connection type or 'unsupported' if unavailable. - */ -export function getEffectiveConnectionType() { - /** - * The effective connection type obtained from the browser's Navigator API. - * @type {string|undefined} - */ - const navigatorEffectiveType = navigator?.connection?.effectiveType; - - if (navigatorEffectiveType) { - return navigatorEffectiveType; - } - - return 'unsupported'; -} - -/** - * Retrieves the user IDs as EIDs from the first valid bid request. - * - * @param {Array} validBidRequests - Array of valid bid requests - * @returns {Array|undefined} - Array of user IDs as EIDs, or undefined if not found - */ -export function getUserIds(validBidRequests) { - /** @type {Object} */ - if (validBidRequests?.[0]?.userIdAsEids) { - return validBidRequests[0].userIdAsEids; - } -} export function getMetaList(name) { if (!name || name.length === 0) return [] @@ -123,35 +91,6 @@ export function getKeywords() { return []; } -function canAccessTopWindow() { - try { - if (getWindowTop().location.href) { - return true; - } - } catch (error) { - return false; - } -} - -/** - * domLoading feature is computed on window.top if reachable. - */ -export function getDomLoadingDuration() { - let domLoadingDuration = -1; - let performance; - - performance = (canAccessTopWindow()) ? getWindowTop().performance : getWindowSelf().performance; - - if (performance && performance.timing && performance.timing.navigationStart > 0) { - const val = performance.timing.domLoading - performance.timing.navigationStart; - if (val > 0) { - domLoadingDuration = val; - } - } - - return domLoadingDuration; -} - /** * @param bidRequest * @return {({cpm, netRevenue: boolean, requestId, width: number, currency, ttl: number, creativeId, height: number}&{mediaType: string, vastXml})|null} @@ -181,7 +120,7 @@ export const buildBid = (bidResponse) => { } return Object.assign(bid, { cpm: bidResponse.price, - currency: bidResponse.currency || CURRENCY, + currency: bidResponse.currency || 'EUR', creativeId: deepAccess(bidResponse, 'extras.deal_id'), requestId: deepAccess(bidResponse, 'extras.transaction_id'), width: deepAccess(bidResponse, `creative.${bid.mediaType}.width`) || 1, @@ -210,59 +149,29 @@ export const isBidRequestValid = (bid) => { */ export const buildRequests = (validBidRequests, bidderRequest) => { if (!validBidRequests || !bidderRequest || !bidderRequest.bids) return null - const domLoadingDuration = getDomLoadingDuration().toString(); + const tags = bidderRequest.bids.map((bid) => { - let bidFloor; - const sizes = bid.sizes.map((size) => ({ w: size[0], h: size[1] })); - const mediaTypes = Object.keys(bid.mediaTypes) - if (typeof bid.getFloor === 'function') { - bidFloor = bid.getFloor({ - currency: CURRENCY, - mediaType: mediaTypes[0], - size: sizes[0] - }); - } - const id = bid.params.tagId - const request = { + return { sizes: bid.sizes.map((size) => ({ w: size[0], h: size[1] })), - id, + id: bid.params.tagId, // TODO: bidId is globally unique, is it a good choice for transaction ID (vs ortb2Imp.ext.tid)? transactionId: bid.bidId, - mediaTypes: mediaTypes, + mediaTypes: Object.keys(bid.mediaTypes), imageUrl: deepAccess(bid, 'params.imageUrl', ''), - videoUrl: deepAccess(bid, 'params.videoUrl', ''), - refresh: (window.bliinkBid[id] = (window.bliinkBid[id] ?? -1) + 1) || undefined, - } - if (bidFloor) { - request.bidFloor = bidFloor - } - return request; + }; }); let request = { tags, pageTitle: document.title, - pageUrl: deepAccess(bidderRequest, 'refererInfo.page').replace(/\?.*$/, ''), + pageUrl: deepAccess(bidderRequest, 'refererInfo.page'), pageDescription: getMetaValue(META_DESCRIPTION), keywords: getKeywords().join(','), - ect: getEffectiveConnectionType(), }; - const schain = deepAccess(validBidRequests[0], 'schain') - const eids = getUserIds(validBidRequests) - const device = bidderRequest.ortb2?.device if (schain) { request.schain = schain } - if (domLoadingDuration > -1) { - request.domLoadingDuration = domLoadingDuration - } - if (device) { - request.device = device - } - if (eids) { - request.eids = eids - } const gdprConsent = deepAccess(bidderRequest, 'gdprConsent'); if (!!gdprConsent && gdprConsent.gdprApplies) { request.gdpr = true @@ -274,6 +183,7 @@ export const buildRequests = (validBidRequests, bidderRequest) => { if (bidderRequest.uspConsent) { deepSetValue(request, 'uspConsent', bidderRequest.uspConsent); } + return { method: 'POST', url: BLIINK_ENDPOINT_ENGINE, @@ -344,7 +254,6 @@ const getUserSyncs = (syncOptions, serverResponses, gdprConsent, uspConsent) => */ export const spec = { code: BIDDER_CODE, - gvlid: GVL_ID, aliases: aliasBidderCode, supportedMediaTypes: supportedMediaTypes, isBidRequestValid, diff --git a/modules/blueconicRtdProvider.js b/modules/blueconicRtdProvider.js index 6b10e79b94c..b6eb9374671 100644 --- a/modules/blueconicRtdProvider.js +++ b/modules/blueconicRtdProvider.js @@ -11,10 +11,6 @@ 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 - */ - const MODULE_NAME = 'realTimeData'; const SUBMODULE_NAME = 'blueconic'; @@ -23,9 +19,9 @@ export const RTD_LOCAL_NAME = 'bcPrebidData'; export const storage = getStorageManager({moduleType: MODULE_TYPE_RTD, moduleName: SUBMODULE_NAME}); /** - * Try parsing stringified array of data. - * @param {String} data - */ +* Try parsing stringified array of data. +* @param {String} data +*/ function parseJson(data) { try { return JSON.parse(data); diff --git a/modules/boldwinBidAdapter.js b/modules/boldwinBidAdapter.js index c7def383b5e..3915df8b976 100644 --- a/modules/boldwinBidAdapter.js +++ b/modules/boldwinBidAdapter.js @@ -5,7 +5,7 @@ import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; const BIDDER_CODE = 'boldwin'; const AD_URL = 'https://ssp.videowalldirect.com/pbjs'; -const SYNC_URL = 'https://sync.videowalldirect.com'; +const SYNC_URL = 'https://cs.videowalldirect.com' function isBidResponseValid(bid) { if (!bid.requestId || !bid.cpm || !bid.creativeId || @@ -80,15 +80,6 @@ export const spec = { if (bidderRequest.gdprConsent) { request.gdpr = bidderRequest.gdprConsent; } - - // Add GPP consent - if (bidderRequest.gppConsent) { - request.gpp = bidderRequest.gppConsent.gppString; - request.gpp_sid = bidderRequest.gppConsent.applicableSections; - } else if (bidderRequest.ortb2?.regs?.gpp) { - request.gpp = bidderRequest.ortb2.regs.gpp; - request.gpp_sid = bidderRequest.ortb2.regs.gpp_sid; - } } const len = validBidRequests.length; diff --git a/modules/brandmetricsRtdProvider.js b/modules/brandmetricsRtdProvider.js index 17336baa76c..53868eccc4c 100644 --- a/modules/brandmetricsRtdProvider.js +++ b/modules/brandmetricsRtdProvider.js @@ -6,14 +6,8 @@ * @requires module:modules/realTimeData */ import {submodule} from '../src/hook.js'; -import {deepAccess, deepSetValue, logError, mergeDeep, generateUUID} from '../src/utils.js'; +import {deepAccess, deepSetValue, logError, mergeDeep} from '../src/utils.js'; import {loadExternalScript} from '../src/adloader.js'; -import * as events from '../src/events.js'; -import CONSTANTS from '../src/constants.json'; - -/** - * @typedef {import('../modules/rtdModule/index.js').RtdSubmodule} RtdSubmodule - */ const MODULE_NAME = 'brandmetrics' const MODULE_CODE = MODULE_NAME @@ -21,18 +15,14 @@ const RECEIVED_EVENTS = [] const GVL_ID = 422 const TCF_PURPOSES = [1, 7] -let billableEventsInitialized = false - function init (config, userConsent) { const hasConsent = checkConsent(userConsent) - const initialize = hasConsent !== false - if (initialize) { + if (hasConsent) { const moduleConfig = getMergedConfig(config) initializeBrandmetrics(moduleConfig.params.scriptId) - initializeBillableEvents() } - return initialize + return hasConsent } /** @@ -41,45 +31,43 @@ function init (config, userConsent) { * @returns {boolean} */ function checkConsent (userConsent) { - let consent - - if (userConsent) { - if (userConsent.gdpr && userConsent.gdpr.gdprApplies) { - const gdpr = userConsent.gdpr - - if (gdpr.vendorData) { - const vendor = gdpr.vendorData.vendor - const purpose = gdpr.vendorData.purpose - - let vendorConsent = false - if (vendor.consents) { - vendorConsent = vendor.consents[GVL_ID] - } - - if (vendor.legitimateInterests) { - vendorConsent = vendorConsent || vendor.legitimateInterests[GVL_ID] - } - - const purposes = TCF_PURPOSES.map(id => { - return (purpose.consents && purpose.consents[id]) || (purpose.legitimateInterests && purpose.legitimateInterests[id]) - }) - const purposesValid = purposes.filter(p => p === true).length === TCF_PURPOSES.length - consent = vendorConsent && purposesValid + let consent = false + + if (userConsent && userConsent.gdpr && userConsent.gdpr.gdprApplies) { + const gdpr = userConsent.gdpr + + if (gdpr.vendorData) { + const vendor = gdpr.vendorData.vendor + const purpose = gdpr.vendorData.purpose + + let vendorConsent = false + if (vendor.consents) { + vendorConsent = vendor.consents[GVL_ID] } - } else if (userConsent.usp) { - const usp = userConsent.usp - consent = usp[1] !== 'N' && usp[2] !== 'Y' + + if (vendor.legitimateInterests) { + vendorConsent = vendorConsent || vendor.legitimateInterests[GVL_ID] + } + + const purposes = TCF_PURPOSES.map(id => { + return (purpose.consents && purpose.consents[id]) || (purpose.legitimateInterests && purpose.legitimateInterests[id]) + }) + const purposesValid = purposes.filter(p => p === true).length === TCF_PURPOSES.length + consent = vendorConsent && purposesValid } + } else if (userConsent.usp) { + const usp = userConsent.usp + consent = usp[1] !== 'N' && usp[2] !== 'Y' } return consent } /** - * Add event- listeners to hook in to brandmetrics events - * @param {Object} reqBidsConfigObj - * @param {function} callback - */ +* Add event- listeners to hook in to brandmetrics events +* @param {Object} reqBidsConfigObj +* @param {function} callback +*/ function processBrandmetricsEvents (reqBidsConfigObj, moduleConfig, callback) { const callBidTargeting = (event) => { if (event.available && event.conf) { @@ -94,6 +82,7 @@ function processBrandmetricsEvents (reqBidsConfigObj, moduleConfig, callback) { if (RECEIVED_EVENTS.length > 0) { callBidTargeting(RECEIVED_EVENTS[RECEIVED_EVENTS.length - 1]) } else { + window._brandmetrics = window._brandmetrics || [] window._brandmetrics.push({ cmd: '_addeventlistener', val: { @@ -131,8 +120,6 @@ function setBidderTargeting (reqBidsConfigObj, moduleConfig, key, val) { * @param {string} scriptId - The script- id provided by brandmetrics or brandmetrics partner */ function initializeBrandmetrics(scriptId) { - window._brandmetrics = window._brandmetrics || [] - if (scriptId) { const path = 'https://cdn.brandmetrics.com/survey/script/' const file = scriptId + '.js' @@ -142,34 +129,6 @@ function initializeBrandmetrics(scriptId) { } } -/** - * Hook in to brandmetrics creative_in_view- event and emit billable- event for creatives measured by brandmetrics. - */ -function initializeBillableEvents() { - if (!billableEventsInitialized) { - window._brandmetrics.push({ - cmd: '_addeventlistener', - val: { - event: 'creative_in_view', - handler: (ev) => { - if (ev.source && ev.source.type === 'pbj') { - const bid = ev.source.data; - events.emit(CONSTANTS.EVENTS.BILLABLE_EVENT, { - vendor: 'brandmetrics', - type: 'creative_in_view', - measurementId: ev.mid, - billingId: generateUUID(), - auctionId: bid.auctionId, - transactionId: bid.transactionId, - }); - } - }, - } - }) - billableEventsInitialized = true - } -} - /** * Merges a provided config with default values * @param {Object} customConfig diff --git a/modules/brandmetricsRtdProvider.md b/modules/brandmetricsRtdProvider.md index d6304f9ae12..89ee6bb75cf 100644 --- a/modules/brandmetricsRtdProvider.md +++ b/modules/brandmetricsRtdProvider.md @@ -16,10 +16,10 @@ Enable the Brandmetrics RTD in your Prebid configuration, using the below format pbjs.setConfig({ ..., realTimeData: { - auctionDelay: 500, + auctionDelay: 500, // auction delay dataProviders: [{ name: 'brandmetrics', - waitForIt: true, + waitForIt: true // should be true if there's an `auctionDelay`, params: { scriptId: '00000000-0000-0000-0000-000000000000', bidders: ['ozone'] @@ -29,7 +29,6 @@ pbjs.setConfig({ ... }) ``` -The scriptId- parameter is provided by brandmetrics or a brandmetrics partner. ## Parameters | Name | Type | Description | Default | @@ -39,17 +38,3 @@ The scriptId- parameter is provided by brandmetrics or a brandmetrics partner. | params | Object | | - | | params.bidders | String[] | An array of bidders which should receive targeting keys. | `[]` | | params.scriptId | String | A script- id GUID if the brandmetrics- script should be initialized. | `undefined` | - -## Billable events -The module emits a billable event for creatives that are measured by brandmetrics and are considered in- view. - -```javascript -{ - vendor: 'brandmetrics', - type: 'creative_in_view', - measurementId: string, // UUID, brandmetrics measurement id - billingId: string, // UUID, unique billing id - auctionId: string, // Prebid auction id - transactionId: string, //Prebid transaction id -} -``` \ No newline at end of file diff --git a/modules/braveBidAdapter.js b/modules/braveBidAdapter.js index 4c5448482db..d954522ae24 100644 --- a/modules/braveBidAdapter.js +++ b/modules/braveBidAdapter.js @@ -4,11 +4,6 @@ import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js'; import {config} from '../src/config.js'; import {convertOrtbRequestToProprietaryNative} from '../src/native.js'; -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - */ - const BIDDER_CODE = 'brave'; const DEFAULT_CUR = 'USD'; const ENDPOINT_URL = `https://point.bravegroup.tv/?t=2&partner=hash`; diff --git a/modules/bridBidAdapter.js b/modules/bridBidAdapter.js deleted file mode 100644 index e784ea517ac..00000000000 --- a/modules/bridBidAdapter.js +++ /dev/null @@ -1,229 +0,0 @@ -import {createTrackPixelHtml, _each, deepAccess, getDefinedParams, parseGPTSingleSizeArrayToRtbSize} from '../src/utils.js'; -import {VIDEO} from '../src/mediaTypes.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {getRefererInfo} from '../src/refererDetection.js'; - -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - * @typedef {import('../src/adapters/bidderFactory.js').BidderRequest} BidderRequest - */ - -const SOURCE = 'pbjs'; -const BIDDER_CODE = 'brid'; -const ENDPOINT_URL = 'https://pbs.prebrid.tv/openrtb2/auction'; -const GVLID = 934; -const TIME_TO_LIVE = 300; -const VIDEO_PARAMS = [ - 'api', 'linearity', 'maxduration', 'mimes', 'minduration', 'placement', - 'playbackmethod', 'protocols', 'startdelay' -]; - -export const spec = { - - code: BIDDER_CODE, - gvlid: GVLID, - supportedMediaTypes: [VIDEO], - - /** - * Determines whether or not the given bid request is valid. - * - * @param {object} bid The bid to validate. - * @return boolean True if this is a valid bid, and false otherwise. - */ - isBidRequestValid: function(bid) { - return !!(bid && bid.params && bid.params.placementId); - }, - - /** - * Make a server request from the list of BidRequests. - * - * @param {BidRequest[]} bidRequests A non-empty list of bid requests which should be sent to the Server. - * @param {BidderRequest} bidderRequest bidder request object. - * @return ServerRequest Info describing the request to the server. - */ - buildRequests: function(bidRequests, bidderRequest) { - const requests = []; - - _each(bidRequests, function(bid) { - const placementId = bid.params.placementId; - const bidId = bid.bidId; - let sizes = bid.sizes; - if (sizes && !Array.isArray(sizes[0])) sizes = [sizes]; - - const site = getSiteObj(); - - const postBody = { - sdk: { - source: SOURCE, - version: '$prebid.version$' - }, - id: bidderRequest.bidderRequestId, - site, - imp: [] - }; - - const imp = { - ext: { - prebid: { - storedrequest: {'id': placementId} - } - } - }; - - const video = deepAccess(bid, 'mediaTypes.video'); - if (video) { - imp.video = getDefinedParams(video, VIDEO_PARAMS); - if (video.playerSize) { - imp.video = Object.assign( - imp.video, parseGPTSingleSizeArrayToRtbSize(video.playerSize[0]) || {} - ); - } else if (video.w && video.h) { - imp.video.w = video.w; - imp.video.h = video.h; - }; - }; - - postBody.imp.push(imp); - - const gdprConsent = bidderRequest && bidderRequest.gdprConsent; - const uspConsent = bidderRequest && bidderRequest.uspConsent; - - if (gdprConsent || uspConsent) { - postBody.regs = { ext: {} }; - - if (uspConsent) { - postBody.regs.ext.us_privacy = uspConsent; - }; - - if (gdprConsent) { - if (typeof gdprConsent.gdprApplies !== 'undefined') { - postBody.regs.ext.gdpr = gdprConsent.gdprApplies ? 1 : 0; - }; - - if (typeof gdprConsent.consentString !== 'undefined') { - postBody.user = { - ext: { consent: gdprConsent.consentString } - }; - }; - }; - }; - - if (bidRequests[0].schain) { - postBody.schain = bidRequests[0].schain; - } - - const params = bid.params; - - requests.push({ - method: 'POST', - url: ENDPOINT_URL, - data: JSON.stringify(postBody), - options: { - withCredentials: true - }, - bidId, - params - }); - }); - - return requests; - }, - - /** - * 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, bidRequest) { - const response = serverResponse.body; - const bidResponses = []; - - _each(response.seatbid, (resp) => { - _each(resp.bid, (bid) => { - const requestId = bidRequest.bidId; - const params = bidRequest.params; - - const {ad, adUrl, vastUrl, vastXml} = getAd(bid); - - const bidResponse = { - requestId, - params, - cpm: bid.price, - width: bid.w, - height: bid.h, - creativeId: bid.adid, - currency: response.cur, - netRevenue: false, - ttl: TIME_TO_LIVE, - meta: { - advertiserDomains: bid.adomain || [] - } - }; - - if (vastUrl || vastXml) { - bidResponse.mediaType = VIDEO; - if (vastUrl) bidResponse.vastUrl = vastUrl; - if (vastXml) bidResponse.vastXml = vastXml; - } else { - bidResponse.ad = ad; - bidResponse.adUrl = adUrl; - }; - - bidResponses.push(bidResponse); - }); - }); - - return bidResponses; - }, - -} - -/** - * Helper function to get ad - * - * @param {object} bid The bid. - * @return {object} ad object. - */ -function getAd(bid) { - let ad, adUrl, vastXml, vastUrl; - - switch (deepAccess(bid, 'ext.prebid.type')) { - case VIDEO: - if (bid.adm.substr(0, 4) === 'http') { - vastUrl = bid.adm; - } else { - vastXml = bid.adm; - }; - break; - default: - if (bid.adm && bid.nurl) { - ad = bid.adm; - ad += createTrackPixelHtml(decodeURIComponent(bid.nurl)); - } else if (bid.adm) { - ad = bid.adm; - } else if (bid.nurl) { - adUrl = bid.nurl; - }; - } - - return {ad, adUrl, vastXml, vastUrl}; -} - -/** - * Helper function to get site object - * - * @return {object} siteObj. - */ -function getSiteObj() { - const refInfo = (getRefererInfo && getRefererInfo()) || {}; - - return { - page: refInfo.page, - ref: refInfo.ref, - domain: refInfo.domain - }; -} - -registerBidder(spec); diff --git a/modules/bridgewellBidAdapter.js b/modules/bridgewellBidAdapter.js index 578acf8a358..6088cefaa55 100644 --- a/modules/bridgewellBidAdapter.js +++ b/modules/bridgewellBidAdapter.js @@ -4,11 +4,6 @@ import {BANNER, NATIVE} from '../src/mediaTypes.js'; import {find} from '../src/polyfill.js'; import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - */ - const BIDDER_CODE = 'bridgewell'; const REQUEST_ENDPOINT = 'https://prebid.scupio.com/recweb/prebid.aspx?cb='; const BIDDER_VERSION = '1.1.0'; diff --git a/modules/brightcomBidAdapter.js b/modules/brightcomBidAdapter.js index 1fa1dac4e95..c4cc5394a03 100644 --- a/modules/brightcomBidAdapter.js +++ b/modules/brightcomBidAdapter.js @@ -1,17 +1,4 @@ -import { - _each, - isArray, - getWindowTop, - getUniqueIdentifierStr, - deepSetValue, - logError, - logWarn, - createTrackPixelHtml, - getWindowSelf, - isFn, - isPlainObject, - getBidIdParameter -} from '../src/utils.js'; +import { getBidIdParameter, _each, isArray, getWindowTop, getUniqueIdentifierStr, deepSetValue, logError, logWarn, createTrackPixelHtml, getWindowSelf, isFn, isPlainObject } from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER } from '../src/mediaTypes.js'; import { config } from '../src/config.js'; diff --git a/modules/brightcomSSPBidAdapter.js b/modules/brightcomSSPBidAdapter.js index 4750881da40..b85a01c8fc7 100644 --- a/modules/brightcomSSPBidAdapter.js +++ b/modules/brightcomSSPBidAdapter.js @@ -1,4 +1,5 @@ import { + getBidIdParameter, isArray, getWindowTop, getUniqueIdentifierStr, @@ -8,7 +9,7 @@ import { createTrackPixelHtml, getWindowSelf, isFn, - isPlainObject, getBidIdParameter, + isPlainObject, } from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {BANNER} from '../src/mediaTypes.js'; diff --git a/modules/britepoolIdSystem.js b/modules/britepoolIdSystem.js index 37ace544dc7..2316fbb732d 100644 --- a/modules/britepoolIdSystem.js +++ b/modules/britepoolIdSystem.js @@ -10,13 +10,6 @@ import {ajax} from '../src/ajax.js'; import {submodule} from '../src/hook.js'; const PIXEL = 'https://px.britepool.com/new?partner_id=t'; -/** - * @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').SubmoduleParams} SubmoduleParams - */ - /** @type {Submodule} */ export const britepoolIdSubmodule = { /** @@ -143,12 +136,6 @@ export const britepoolIdSubmodule = { } } return valueObj; - }, - eids: { - 'britepoolid': { - source: 'britepool.com', - atype: 3 - }, } }; diff --git a/modules/browsiBidAdapter.js b/modules/browsiBidAdapter.js index fa1cacaa568..03b6b2a8f3d 100644 --- a/modules/browsiBidAdapter.js +++ b/modules/browsiBidAdapter.js @@ -3,11 +3,6 @@ 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 - * @typedef {import('../src/adapters/bidderFactory.js').UserSync} UserSync - */ - const BIDDER_CODE = 'browsi'; const DATA = 'brwvidtag'; const ADAPTER = '__bad'; diff --git a/modules/browsiRtdProvider.js b/modules/browsiRtdProvider.js index 5281274616a..4a61f40600d 100644 --- a/modules/browsiRtdProvider.js +++ b/modules/browsiRtdProvider.js @@ -25,11 +25,6 @@ import {getGlobal} from '../src/prebidGlobal.js'; import * as events from '../src/events.js'; import CONSTANTS from '../src/constants.json'; import {MODULE_TYPE_RTD} from '../src/activities/modules.js'; - -/** - * @typedef {import('../modules/rtdModule/index.js').RtdSubmodule} RtdSubmodule - */ - const MODULE_NAME = 'browsi'; const storage = getStorageManager({moduleType: MODULE_TYPE_RTD, moduleName: MODULE_NAME}); @@ -93,7 +88,6 @@ export function collectData() { let predictorData = { ...{ sk: _moduleParams.siteKey, - pk: _moduleParams.pubKey, sw: (win.screen && win.screen.width) || -1, sh: (win.screen && win.screen.height) || -1, url: `${doc.location.protocol}//${doc.location.host}${doc.location.pathname}`, @@ -140,6 +134,7 @@ function getRTD(auc) { const adSlot = getSlotByCode(uc); const identifier = adSlot ? getMacroId(_browsiData['pmd'], adSlot) : uc; const _pd = _bp[identifier]; + rp[uc] = getKVObject(-1); if (!_pd) { return rp } @@ -280,7 +275,7 @@ function getPredictionsFromServer(url) { if (req.status === 200) { try { const data = JSON.parse(response); - if (data) { + if (data && data.p && data.kn) { setData({p: data.p, kn: data.kn, pmd: data.pmd, bet: data.bet}); } else { setData({}); diff --git a/modules/bucksenseBidAdapter.js b/modules/bucksenseBidAdapter.js index 5aa14f2a53b..7b6c3911ea1 100644 --- a/modules/bucksenseBidAdapter.js +++ b/modules/bucksenseBidAdapter.js @@ -2,18 +2,12 @@ import { logInfo } 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 - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - */ - const WHO = 'BKSHBID-005'; const BIDDER_CODE = 'bucksense'; const URL = 'https://directo.prebidserving.com/prebidjs/'; export const spec = { code: BIDDER_CODE, - gvlid: 235, supportedMediaTypes: [BANNER], /** @@ -21,7 +15,7 @@ export const spec = { * * @param {object} bid The bid to validate. * @return boolean True if this is a valid bid, and false otherwise. - */ + */ isBidRequestValid: function (bid) { logInfo(WHO + ' isBidRequestValid() - INPUT bid:', bid); if (bid.bidder !== BIDDER_CODE || typeof bid.params === 'undefined') { @@ -34,10 +28,10 @@ export const spec = { }, /** - * Make a server request from the list of BidRequests. - * - * @param {BidRequest[]} validBidRequests A non-empty list of valid bid requests that should be sent to the Server. - * @return ServerRequest Info describing the request to the server. + * Make a server request from the list of BidRequests. + * + * @param {BidRequest[]} validBidRequests A non-empty list of valid bid requests that should be sent to the Server. + * @return ServerRequest Info describing the request to the server. */ buildRequests: function (validBidRequests, bidderRequest) { logInfo(WHO + ' buildRequests() - INPUT validBidRequests:', validBidRequests, 'INPUT bidderRequest:', bidderRequest); @@ -79,7 +73,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, request) { logInfo(WHO + ' interpretResponse() - INPUT serverResponse:', serverResponse, 'INPUT request:', request); diff --git a/modules/buzzoolaBidAdapter.js b/modules/buzzoolaBidAdapter.js index 14a26203484..b5ea6227f58 100644 --- a/modules/buzzoolaBidAdapter.js +++ b/modules/buzzoolaBidAdapter.js @@ -5,12 +5,6 @@ import {Renderer} from '../src/Renderer.js'; import {OUTSTREAM} from '../src/video.js'; import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse - */ - const BIDDER_CODE = 'buzzoola'; const ENDPOINT = 'https://exchange.buzzoola.com/ssp/prebidjs'; const RENDERER_SRC = 'https://tube.buzzoola.com/new/build/buzzlibrary.js'; diff --git a/modules/c1xBidAdapter.js b/modules/c1xBidAdapter.js index 79ba8cf499d..8c9407825ba 100644 --- a/modules/c1xBidAdapter.js +++ b/modules/c1xBidAdapter.js @@ -2,10 +2,6 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { logInfo, logError } from '../src/utils.js'; import { BANNER } from '../src/mediaTypes.js'; -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - */ - const BIDDER_CODE = 'c1x'; const URL = 'https://hb-stg.c1exchange.com/ht'; // const PIXEL_ENDPOINT = '//px.c1exchange.com/pubpixel/'; diff --git a/modules/cadentApertureMXBidAdapter.js b/modules/cadentApertureMXBidAdapter.js index e73564dacdb..26e8639154c 100644 --- a/modules/cadentApertureMXBidAdapter.js +++ b/modules/cadentApertureMXBidAdapter.js @@ -1,6 +1,7 @@ import { _each, - deepAccess, getBidIdParameter, + deepAccess, + getBidIdParameter, isArray, isFn, isPlainObject, @@ -169,22 +170,6 @@ export const cadentAdapter = { return cadentData; }, - - getGpp: (bidRequest, cadentData) => { - if (bidRequest.gppConsent) { - const {gppString: gpp, applicableSections: gppSid} = bidRequest.gppConsent; - if (cadentData.regs) { - cadentData.regs.gpp = gpp; - cadentData.regs.gpp_sid = gppSid; - } else { - cadentData.regs = { - gpp: gpp, - gpp_sid: gppSid - } - } - } - return cadentData; - }, getSupplyChain: (bidderRequest, cadentData) => { if (bidderRequest.bids[0] && bidderRequest.bids[0].schain) { cadentData.source = { @@ -276,7 +261,7 @@ export const spec = { let isVideo = !!bid.mediaTypes.video; let data = { id: bid.bidId, - tid: bid.ortb2Imp?.ext?.tid, + tid: bid.transactionId, tagid, secure }; @@ -296,7 +281,7 @@ export const spec = { }); let cadentData = { - id: bidderRequest.auctionId ?? bidderRequest.bidderRequestId, + id: bidderRequest.auctionId, imp: cadentImps, device, site, @@ -305,7 +290,6 @@ export const spec = { }; cadentData = cadentAdapter.getGdpr(bidderRequest, Object.assign({}, cadentData)); - cadentData = cadentAdapter.getGpp(bidderRequest, Object.assign({}, cadentData)); cadentData = cadentAdapter.getSupplyChain(bidderRequest, Object.assign({}, cadentData)); if (bidderRequest && bidderRequest.uspConsent) { cadentData.us_privacy = bidderRequest.uspConsent; @@ -373,7 +357,7 @@ export const spec = { } return cadentBidResponses; }, - getUserSyncs: function (syncOptions, responses, gdprConsent, uspConsent, gppConsent) { + getUserSyncs: function (syncOptions, responses, gdprConsent, uspConsent) { const syncs = []; const consentParams = []; if (syncOptions.iframeEnabled) { @@ -389,14 +373,6 @@ export const spec = { if (uspConsent && typeof uspConsent.consentString === 'string') { consentParams.push(`usp=${uspConsent.consentString}`); } - if (gppConsent && typeof gppConsent === 'object') { - if (gppConsent.gppString && typeof gppConsent.gppString === 'string') { - consentParams.push(`gpp=${gppConsent.gppString}`); - } - if (gppConsent.applicableSections && typeof gppConsent.applicableSections === 'object') { - consentParams.push(`gpp_sid=${gppConsent.applicableSections}`); - } - } if (consentParams.length > 0) { url = url + '?' + consentParams.join('&'); } diff --git a/modules/cadentApertureMXBidAdapter.md b/modules/cadentApertureMXBidAdapter.md index d924f904be4..11d63134587 100644 --- a/modules/cadentApertureMXBidAdapter.md +++ b/modules/cadentApertureMXBidAdapter.md @@ -3,14 +3,14 @@ ``` Module Name: Cadent Aperture MX Adapter Module Type: Bidder Adapter -Maintainer: contactaperturemx@cadent.tv +Maintainer: git@emxdigital.com ``` # Description The Cadent Aperture MX adapter provides publishers with access to the Cadent Aperture MX SSP. The adapter is GDPR compliant. Please note that the adapter supports Banner and Video (Instream & Outstream) media types. -Note: The Cadent Aperture MX adapter requires approval and implementation guidelines from the Cadent team, including existing publishers that work with Cadent. Please reach out to your account manager or contactaperturemx@cadent.tv for more information. +Note: The Cadent Aperture MX adapter requires approval and implementation guidelines from the Cadent team, including existing publishers that work with Cadent. Please reach out to your account manager or prebid@emxdigital.com for more information. The bidder code should be ```cadent_aperture_mx``` The params used by the bidder are : diff --git a/modules/categoryTranslation.js b/modules/categoryTranslation.js index eb6cb83730a..ba73aa01b85 100644 --- a/modules/categoryTranslation.js +++ b/modules/categoryTranslation.js @@ -12,7 +12,7 @@ */ import {config} from '../src/config.js'; -import {hook, setupBeforeHookFnOnce, ready} from '../src/hook.js'; +import {hook, setupBeforeHookFnOnce} from '../src/hook.js'; import {ajax} from '../src/ajax.js'; import {logError, timestamp} from '../src/utils.js'; import {addBidResponse} from '../src/auction.js'; @@ -32,8 +32,7 @@ export const registerAdserver = hook('async', function(adServer) { initTranslation(url, DEFAULT_IAB_TO_FW_MAPPING_KEY); } }, 'registerAdserver'); - -ready.then(() => registerAdserver()); +registerAdserver(); export const getAdserverCategoryHook = timedBidResponseHook('categoryTranslation', function getAdserverCategoryHook(fn, adUnitCode, bid, reject) { if (!bid) { diff --git a/modules/cleanioRtdProvider.js b/modules/cleanioRtdProvider.js index f9bed5357ee..7d0f461108b 100644 --- a/modules/cleanioRtdProvider.js +++ b/modules/cleanioRtdProvider.js @@ -12,10 +12,6 @@ import { logError, generateUUID, insertElement } from '../src/utils.js'; import * as events from '../src/events.js'; import CONSTANTS from '../src/constants.json'; -/** - * @typedef {import('../modules/rtdModule/index.js').RtdSubmodule} RtdSubmodule - */ - // ============================ MODULE STATE =============================== /** diff --git a/modules/clickforceBidAdapter.js b/modules/clickforceBidAdapter.js index be81ff1885c..92bc9b1bad2 100644 --- a/modules/clickforceBidAdapter.js +++ b/modules/clickforceBidAdapter.js @@ -2,12 +2,6 @@ import { _each } from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {BANNER, NATIVE} from '../src/mediaTypes.js'; import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; - -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - */ - const BIDDER_CODE = 'clickforce'; const ENDPOINT_URL = 'https://ad.holmesmind.com/adserver/prebid.json?cb=' + new Date().getTime() + '&hb=1&ver=1.21'; diff --git a/modules/codefuelBidAdapter.js b/modules/codefuelBidAdapter.js index a289e29bd19..2548b20189b 100644 --- a/modules/codefuelBidAdapter.js +++ b/modules/codefuelBidAdapter.js @@ -2,14 +2,6 @@ import {deepAccess, isArray} 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 - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse - * @typedef {import('../src/adapters/bidderFactory.js').SyncOptions} SyncOptions - * @typedef {import('../src/adapters/bidderFactory.js').UserSync} UserSync - */ - const BIDDER_CODE = 'codefuel'; const CURRENCY = 'USD'; @@ -18,11 +10,11 @@ export const spec = { supportedMediaTypes: [ BANNER ], aliases: ['ex'], // short code /** - * Determines whether or not the given bid request is valid. - * - * @param {BidRequest} bid The bid params to validate. - * @return boolean True if this is a valid bid, and false otherwise. - */ + * Determines whether or not the given bid request is valid. + * + * @param {BidRequest} bid The bid params to validate. + * @return boolean True if this is a valid bid, and false otherwise. + */ isBidRequestValid: function(bid) { if (bid.nativeParams) { return false; @@ -30,11 +22,11 @@ export const spec = { return !!(bid.params.placementId || (bid.params.member && bid.params.invCode)); }, /** - * Make a server request from the list of BidRequests. - * - * @param {validBidRequests[]} - an array of bids - * @return ServerRequest Info describing the request to the server. - */ + * Make a server request from the list of BidRequests. + * + * @param {validBidRequests[]} - an array of bids + * @return ServerRequest Info describing the request to the server. + */ buildRequests: function(validBidRequests, bidderRequest) { const page = bidderRequest.refererInfo.page; const domain = bidderRequest.refererInfo.domain; @@ -86,11 +78,11 @@ export const spec = { }; }, /** - * Unpack the 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. - */ + * Unpack the 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. + */ interpretResponse: (serverResponse, { bids }) => { if (!serverResponse.body) { return []; @@ -124,12 +116,12 @@ export const spec = { }, /** - * Register the user sync pixels which should be dropped after the auction. - * - * @param {SyncOptions} syncOptions Which user syncs are allowed? - * @param {ServerResponse[]} serverResponses List of server's responses. - * @return {UserSync[]} The user syncs which should be dropped. - */ + * Register the user sync pixels which should be dropped after the auction. + * + * @param {SyncOptions} syncOptions Which user syncs are allowed? + * @param {ServerResponse[]} serverResponses List of server's responses. + * @return {UserSync[]} The user syncs which should be dropped. + */ getUserSyncs: function(syncOptions, serverResponses, gdprConsent, uspConsent) { return []; } diff --git a/modules/cointrafficBidAdapter.js b/modules/cointrafficBidAdapter.js index 3b90529b6cc..380e1f5fc77 100644 --- a/modules/cointrafficBidAdapter.js +++ b/modules/cointrafficBidAdapter.js @@ -3,13 +3,6 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER } from '../src/mediaTypes.js' import { config } from '../src/config.js' -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse - * @typedef {import('../src/adapters/bidderFactory.js').BidderSpec} BidderSpec - */ - const BIDDER_CODE = 'cointraffic'; const ENDPOINT_URL = 'https://apps-pbd.ctraffic.io/pb/tmp'; const DEFAULT_CURRENCY = 'EUR'; diff --git a/modules/coinzillaBidAdapter.js b/modules/coinzillaBidAdapter.js index 9ae2c74547d..15731423c49 100644 --- a/modules/coinzillaBidAdapter.js +++ b/modules/coinzillaBidAdapter.js @@ -1,12 +1,6 @@ import { parseSizesInput } from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse - */ - const BIDDER_CODE = 'coinzilla'; const ENDPOINT_URL = 'https://request.czilladx.com/serve/request.php'; @@ -83,7 +77,7 @@ export const spec = { dealId: dealId, currency: currency, netRevenue: netRevenue, - ttl: response.timeout, + ttl: bidRequest.timeout, referrer: referrer, ad: response.ad, mediaType: response.mediaType, diff --git a/modules/colossussspBidAdapter.js b/modules/colossussspBidAdapter.js index cc3e452f20c..b1ee8875422 100644 --- a/modules/colossussspBidAdapter.js +++ b/modules/colossussspBidAdapter.js @@ -5,11 +5,6 @@ import { ajax } from '../src/ajax.js'; import { config } from '../src/config.js'; import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - */ - const BIDDER_CODE = 'colossusssp'; const G_URL = 'https://colossusssp.com/?c=o&m=multi'; const G_URL_SYNC = 'https://sync.colossusssp.com'; diff --git a/modules/conceptxBidAdapter.js b/modules/conceptxBidAdapter.js index 87ac96f2131..1147a50e71f 100644 --- a/modules/conceptxBidAdapter.js +++ b/modules/conceptxBidAdapter.js @@ -3,23 +3,23 @@ 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'; +let ENDPOINT_URL = 'https://conceptx.cncpt-central.com/openrtb'; // const LOG_PREFIX = 'ConceptX: '; export const spec = { code: BIDDER_CODE, supportedMediaTypes: [BANNER], isBidRequestValid: function (bid) { - return !!(bid.bidId && bid.params.site && bid.params.adunit); + return !!(bid.bidId); }, 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; + ENDPOINT_URL += '?gdpr_applies=' + bidderRequest.gdprConsent.gdprApplies; + ENDPOINT_URL += '&consentString=' + bidderRequest.gdprConsent.consentString; } for (var i = 0; i < validBidRequests.length; i++) { const requestParent = { adUnits: [], meta: {} }; @@ -33,7 +33,7 @@ export const spec = { requestParent.adUnits.push(adUnit); requests.push({ method: 'POST', - url: requestUrl, + url: ENDPOINT_URL, options: { withCredentials: false, }, @@ -45,16 +45,10 @@ export const spec = { }, 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 - } - const firstSeat = firstBid.ads[0] + const firstDummy = bidResponsesFromServer[0] + const firstSeat = firstDummy.ads[0] + const bidResponses = []; const bidResponse = { requestId: firstSeat.requestId, cpm: firstSeat.cpm, diff --git a/modules/concertBidAdapter.js b/modules/concertBidAdapter.js index bd738a39bba..bf4079322ff 100644 --- a/modules/concertBidAdapter.js +++ b/modules/concertBidAdapter.js @@ -3,13 +3,6 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { getStorageManager } from '../src/storageManager.js'; import { hasPurpose1Consent } from '../src/utils/gpdr.js'; -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse - * @typedef {import('../src/adapters/bidderFactory.js').validBidRequests} validBidRequests - */ - const BIDDER_CODE = 'concert'; const CONCERT_ENDPOINT = 'https://bids.concert.io'; @@ -48,7 +41,6 @@ export const spec = { prebidVersion: '$prebid.version$', pageUrl: bidderRequest.refererInfo.page, screen: [window.screen.width, window.screen.height].join('x'), - browserLanguage: window.navigator.language, debug: debugTurnedOn(), uid: getUid(bidderRequest, validBidRequests), optedOut: hasOptedOutOfPersonalization(), @@ -56,7 +48,6 @@ export const spec = { uspConsent: bidderRequest.uspConsent, gdprConsent: bidderRequest.gdprConsent, gppConsent: bidderRequest.gppConsent, - tdid: getTdid(bidderRequest, validBidRequests), } }; @@ -271,11 +262,3 @@ function getOffset(el) { }; } } - -function getTdid(bidderRequest, validBidRequests) { - if (hasOptedOutOfPersonalization() || !consentAllowsPpid(bidderRequest)) { - return null; - } - - return deepAccess(validBidRequests[0], 'userId.tdid') || null; -} diff --git a/modules/connatixBidAdapter.js b/modules/connatixBidAdapter.js deleted file mode 100644 index 7524cd4e194..00000000000 --- a/modules/connatixBidAdapter.js +++ /dev/null @@ -1,184 +0,0 @@ -import { - registerBidder -} from '../src/adapters/bidderFactory.js'; - -import { - deepAccess, - isFn, - logError, - isArray, - formatQS -} from '../src/utils.js'; - -import { - BANNER, -} from '../src/mediaTypes.js'; - -const BIDDER_CODE = 'connatix'; -const AD_URL = 'https://capi.connatix.com/rtb/hba'; -const DEFAULT_MAX_TTL = '3600'; -const DEFAULT_CURRENCY = 'USD'; - -/* - * Get the bid floor value from the bid object, either using the getFloor function or by accessing the 'params.bidfloor' property. - * If the bid floor cannot be determined, return 0 as a fallback value. - */ -export function getBidFloor(bid) { - if (!isFn(bid.getFloor)) { - return deepAccess(bid, 'params.bidfloor', 0); - } - - try { - const bidFloor = bid.getFloor({ - currency: DEFAULT_CURRENCY, - mediaType: '*', - size: '*', - }); - return bidFloor.floor; - } catch (err) { - logError(err); - return 0; - } -} - -export const spec = { - code: BIDDER_CODE, - gvlid: 143, - supportedMediaTypes: [BANNER], - - /* - * Validate the bid request. - * If the request is valid, Connatix is trying to obtain at least one bid. - * Otherwise, the request to the Connatix server is not made - */ - isBidRequestValid: (bid = {}) => { - const bidId = deepAccess(bid, 'bidId'); - const mediaTypes = deepAccess(bid, 'mediaTypes', {}); - const params = deepAccess(bid, 'params', {}); - const bidder = deepAccess(bid, 'bidder'); - - const banner = deepAccess(mediaTypes, BANNER, {}); - - const hasBidId = Boolean(bidId); - const isValidBidder = (bidder === BIDDER_CODE); - const isValidSize = (Boolean(banner.sizes) && isArray(mediaTypes[BANNER].sizes) && mediaTypes[BANNER].sizes.length > 0); - const hasSizes = mediaTypes[BANNER] ? isValidSize : false; - const hasRequiredBidParams = Boolean(params.placementId); - - const isValid = isValidBidder && hasBidId && hasSizes && hasRequiredBidParams; - if (!isValid) { - logError(`Invalid bid request: isValidBidder: ${isValidBidder} hasBidId: ${hasBidId}, hasSizes: ${hasSizes}, hasRequiredBidParams: ${hasRequiredBidParams}`); - } - return isValid; - }, - - /* - * Build the request payload by processing valid bid requests and extracting the necessary information. - * Determine the host and page from the bidderRequest's refferUrl, and include ccpa and gdpr consents. - * Return an object containing the request method, url, and the constructed payload. - */ - buildRequests: (validBidRequests = [], bidderRequest = {}) => { - const bidRequests = validBidRequests.map(bid => { - const { - bidId, - mediaTypes, - params, - sizes, - } = bid; - return { - bidId, - mediaTypes, - sizes, - placementId: params.placementId, - floor: getBidFloor(bid), - }; - }); - - const requestPayload = { - ortb2: bidderRequest.ortb2, - gdprConsent: bidderRequest.gdprConsent, - uspConsent: bidderRequest.uspConsent, - refererInfo: bidderRequest.refererInfo, - bidRequests, - }; - - return { - method: 'POST', - url: AD_URL, - data: requestPayload - }; - }, - - /* - * Interpret the server response and create an array of bid responses by extracting and formatting - * relevant information such as requestId, cpm, ttl, width, height, creativeId, referrer and ad - * Returns an array of bid responses by extracting and formatting the server response - */ - interpretResponse: (serverResponse) => { - const responseBody = serverResponse.body; - const bids = responseBody.Bids; - - if (!isArray(bids)) { - return []; - } - - const referrer = responseBody.Referrer; - return bids.map(bidResponse => ({ - requestId: bidResponse.RequestId, - cpm: bidResponse.Cpm, - ttl: bidResponse.Ttl || DEFAULT_MAX_TTL, - currency: 'USD', - mediaType: BANNER, - netRevenue: true, - width: bidResponse.Width, - height: bidResponse.Height, - creativeId: bidResponse.CreativeId, - ad: bidResponse.Ad, - referrer: referrer, - })); - }, - - /* - * Determine the user sync type (either 'iframe' or 'image') based on syncOptions. - * Construct the sync URL by appending required query parameters such as gdpr, ccpa, and coppa consents. - * Return an array containing an object with the sync type and the constructed URL. - */ - getUserSyncs: (syncOptions, serverResponses, gdprConsent, uspConsent, gppConsent) => { - if (!syncOptions.iframeEnabled) { - return []; - } - - if (!serverResponses || !serverResponses.length) { - return []; - } - - const params = {}; - - if (gdprConsent) { - if (typeof gdprConsent.gdprApplies === 'boolean') { - params['gdpr'] = Number(gdprConsent.gdprApplies); - } else { - params['gdpr'] = 0; - } - - if (typeof gdprConsent.consentString === 'string') { - params['gdpr_consent'] = encodeURIComponent(gdprConsent.consentString); - } - } - - if (typeof uspConsent === 'string') { - params['us_privacy'] = encodeURIComponent(uspConsent); - } - - const syncUrl = serverResponses[0].body.UserSyncEndpoint; - const queryParams = Object.keys(params).length > 0 ? formatQS(params) : ''; - - const url = queryParams ? `${syncUrl}?${queryParams}` : syncUrl; - return [{ - type: 'iframe', - url - }]; - } -}; - -registerBidder(spec); diff --git a/modules/connatixBidAdapter.md b/modules/connatixBidAdapter.md deleted file mode 100644 index 595c294e311..00000000000 --- a/modules/connatixBidAdapter.md +++ /dev/null @@ -1,54 +0,0 @@ - -# Overview - -``` -Module Name: Connatix Bidder Adapter -Module Type: Bidder Adapter -Maintainer: prebid_integration@connatix.com -``` - -# Description -Connects to Connatix demand source to fetch bids. -Please use ```connatix``` as the bidder code. - -# Configuration -Connatix requires that ```iframe``` is used for user syncing. - -Example configuration: -``` -pbjs.setConfig({ - userSync: { - filterSettings: { - iframe: { - bidders: '*', // represents all bidders - filter: 'include' - } - } - } -}); -``` - -# Test Parameters -``` -var adUnits = [ - { - code: '1', - mediaTypes: { - banner: { - sizes: [[640, 480], [320, 180]], - }, - }, - bids: [ - { - bidder: 'connatix', - params: { - placementId: 'e4984e88-9ff4-45a3-8b9d-33aabcad634e', // required - bidfloor: 2.5, // optional - }, - }, - // Add more bidders and their parameters as needed - ], - }, - // Define more ad units here if necessary -]; -``` \ No newline at end of file diff --git a/modules/connectIdSystem.js b/modules/connectIdSystem.js index 2ebc68baa84..1caff49199d 100644 --- a/modules/connectIdSystem.js +++ b/modules/connectIdSystem.js @@ -10,17 +10,10 @@ import {submodule} from '../src/hook.js'; import {includes} from '../src/polyfill.js'; import {getRefererInfo} from '../src/refererDetection.js'; import {getStorageManager} from '../src/storageManager.js'; -import {formatQS, isNumber, isPlainObject, logError, parseUrl} from '../src/utils.js'; +import {formatQS, isPlainObject, logError, parseUrl} from '../src/utils.js'; import {uspDataHandler, gppDataHandler} from '../src/adapterManager.js'; import {MODULE_TYPE_UID} from '../src/activities/modules.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 = 'connectId'; const STORAGE_EXPIRY_DAYS = 365; const STORAGE_DURATION = 60 * 60 * 24 * 1000 * STORAGE_EXPIRY_DAYS; @@ -33,16 +26,6 @@ const PLACEHOLDER = '__PIXEL_ID__'; const UPS_ENDPOINT = `https://ups.analytics.yahoo.com/ups/${PLACEHOLDER}/fed`; const OVERRIDE_OPT_OUT_KEY = 'connectIdOptOut'; const INPUT_PARAM_KEYS = ['pixelId', 'he', 'puid']; -const O_AND_O_DOMAINS = [ - 'yahoo.com', - 'aol.com', - 'aol.ca', - 'aol.de', - 'aol.co.uk', - 'engadget.com', - 'techcrunch.com', - 'autoblog.com', -]; export const storage = getStorageManager({moduleType: MODULE_TYPE_UID, moduleName: MODULE_NAME}); /** @@ -121,11 +104,9 @@ function syncLocalStorageToCookie() { } function isStale(storedIdData) { - if (isOAndOTraffic()) { + if (isPlainObject(storedIdData) && storedIdData.lastSynced && + (storedIdData.lastSynced + VALID_ID_DURATION) <= Date.now()) { return true; - } else if (isPlainObject(storedIdData) && storedIdData.lastSynced) { - const validTTL = storedIdData.ttl || VALID_ID_DURATION; - return storedIdData.lastSynced + validTTL <= Date.now(); } return false; } @@ -146,17 +127,6 @@ function getSiteHostname() { return pageInfo.hostname; } -function isOAndOTraffic() { - let referer = getRefererInfo().ref; - - if (referer) { - referer = parseUrl(referer).hostname; - const subDomains = referer.split('.'); - referer = subDomains.slice(subDomains.length - 2, subDomains.length).join('.'); - } - return O_AND_O_DOMAINS.indexOf(referer) >= 0; -} - /** @type {Submodule} */ export const connectIdSubmodule = { /** @@ -268,13 +238,6 @@ export const connectIdSubmodule = { responseObj.puid = params.puid || responseObj.puid; responseObj.lastSynced = Date.now(); responseObj.lastUsed = Date.now(); - if (isNumber(responseObj.ttl)) { - let validTTLMiliseconds = responseObj.ttl * 60 * 60 * 1000; - if (validTTLMiliseconds > VALID_ID_DURATION) { - validTTLMiliseconds = VALID_ID_DURATION; - } - responseObj.ttl = validTTLMiliseconds; - } storeObject(responseObj); } else { logError(`${MODULE_NAME} module: UPS response returned an invalid payload ${response}`); @@ -331,12 +294,6 @@ export const connectIdSubmodule = { */ getAjaxFn() { return ajax; - }, - eids: { - 'connectId': { - source: 'yahoo.com', - atype: 3 - }, } }; diff --git a/modules/connectadBidAdapter.js b/modules/connectadBidAdapter.js index b40ef30f6bc..d5665b318be 100644 --- a/modules/connectadBidAdapter.js +++ b/modules/connectadBidAdapter.js @@ -1,9 +1,7 @@ -import { deepSetValue, logWarn } from '../src/utils.js'; +import { deepSetValue, convertTypes, tryAppendQueryString, logWarn } 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 {convertTypes} from '../libraries/transformParamsUtils/convertTypes.js'; const BIDDER_CODE = 'connectad'; const BIDDER_CODE_ALIAS = 'connectadrealtime'; const ENDPOINT_URL = 'https://i.connectad.io/api/v2'; diff --git a/modules/consentManagement.js b/modules/consentManagement.js index 05447a890cb..6c22b3b9da4 100644 --- a/modules/consentManagement.js +++ b/modules/consentManagement.js @@ -12,7 +12,6 @@ import {timedAuctionHook} from '../src/utils/perfMetrics.js'; import {registerOrtbProcessor, REQUEST} from '../src/pbjsORTB.js'; import {enrichFPD} from '../src/fpd/enrichment.js'; import {getGlobal} from '../src/prebidGlobal.js'; -import {cmpClient} from '../libraries/cmp/cmpClient.js'; const DEFAULT_CMP = 'iab'; const DEFAULT_CONSENT_TIMEOUT = 10000; @@ -49,6 +48,36 @@ function lookupStaticConsentData({onSuccess, onError}) { * @param {function(string, ...{}?)} cmpError acts as an error callback while interacting with CMP; pass along an error message (string) and any extra error arguments (purely for logging) */ function lookupIabConsent({onSuccess, onError, onEvent}) { + function findCMP() { + let f = window; + let cmpFrame; + let cmpFunction; + while (true) { + try { + if (typeof f.__tcfapi === 'function') { + cmpFunction = f.__tcfapi; + cmpFrame = f; + break; + } + } catch (e) { } + + // need separate try/catch blocks due to the exception errors thrown when trying to check for a frame that doesn't exist in 3rd party env + try { + if (f.frames['__tcfapiLocator']) { + cmpFrame = f; + break; + } + } catch (e) { } + + if (f === window.top) break; + f = f.parent; + } + return { + cmpFrame, + cmpFunction + }; + } + function cmpResponseCallback(tcfData, success) { logInfo('Received a response from CMP', tcfData); if (success) { @@ -61,25 +90,70 @@ function lookupIabConsent({onSuccess, onError, onEvent}) { } } - const cmp = cmpClient({ - apiName: '__tcfapi', - apiVersion: CMP_VERSION, - apiArgs: ['command', 'version', 'callback', 'parameter'], - }); + const cmpCallbacks = {}; + const { cmpFrame, cmpFunction } = findCMP(); - if (!cmp) { + if (!cmpFrame) { return onError('TCF2 CMP not found.'); } - if (cmp.isDirect) { + // to collect the consent information from the user, we perform two calls to the CMP in parallel: + // first to collect the user's consent choices represented in an encoded string (via getConsentData) + // second to collect the user's full unparsed consent information (via getVendorConsents) + + // the following code also determines where the CMP is located and uses the proper workflow to communicate with it: + // check to see if CMP is found on the same window level as prebid and call it directly if so + // check to see if prebid is in a safeframe (with CMP support) + // else assume prebid may be inside an iframe and use the IAB CMP locator code to see if CMP's located in a higher parent window. this works in cross domain iframes + // if the CMP is not found, the iframe function will call the cmpError exit callback to abort the rest of the CMP workflow + + if (typeof cmpFunction === 'function') { logInfo('Detected CMP API is directly accessible, calling it now...'); + cmpFunction('addEventListener', CMP_VERSION, cmpResponseCallback); } else { logInfo('Detected CMP is outside the current iframe where Prebid.js is located, calling it now...'); + callCmpWhileInIframe('addEventListener', cmpFrame, cmpResponseCallback); } - cmp({ - command: 'addEventListener', - callback: cmpResponseCallback - }) + function callCmpWhileInIframe(commandName, cmpFrame, moduleCallback) { + const apiName = '__tcfapi'; + + const callName = `${apiName}Call`; + + /* Setup up a __cmp function to do the postMessage and stash the callback. + This function behaves (from the caller's perspective identicially to the in-frame __cmp call */ + window[apiName] = function (cmd, cmpVersion, callback, arg) { + const callId = Math.random() + ''; + const msg = { + [callName]: { + command: cmd, + version: cmpVersion, + parameter: arg, + callId: callId + } + }; + + cmpCallbacks[callId] = callback; + cmpFrame.postMessage(msg, '*'); + } + + /** when we get the return message, call the stashed callback */ + window.addEventListener('message', readPostMessageResponse, false); + + // call CMP + window[apiName](commandName, CMP_VERSION, moduleCallback); + + function readPostMessageResponse(event) { + const cmpDataPkgName = `${apiName}Return`; + const json = (typeof event.data === 'string' && includes(event.data, cmpDataPkgName)) ? JSON.parse(event.data) : event.data; + if (json[cmpDataPkgName] && json[cmpDataPkgName].callId) { + const payload = json[cmpDataPkgName]; + // TODO - clean up this logic (move listeners?); we have duplicate messages responses because 2 eventlisteners are active from the 2 cmp requests running in parallel + if (cmpCallbacks.hasOwnProperty(payload.callId)) { + cmpCallbacks[payload.callId](payload.returnValue, payload.success); + } + } + } + } } /** diff --git a/modules/consentManagementGpp.js b/modules/consentManagementGpp.js index f696ce25902..3b0e1c25c6a 100644 --- a/modules/consentManagementGpp.js +++ b/modules/consentManagementGpp.js @@ -4,319 +4,180 @@ * 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, isNumber, isPlainObject, isStr, logError, logInfo, logWarn} from '../src/utils.js'; +import {deepSetValue, isNumber, isPlainObject, isStr, logError, logInfo, logWarn} from '../src/utils.js'; import {config} from '../src/config.js'; import {gppDataHandler} from '../src/adapterManager.js'; +import {includes} from '../src/polyfill.js'; import {timedAuctionHook} from '../src/utils/perfMetrics.js'; -import {enrichFPD} from '../src/fpd/enrichment.js'; +import { enrichFPD } from '../src/fpd/enrichment.js'; import {getGlobal} from '../src/prebidGlobal.js'; -import {cmpClient, MODE_CALLBACK, MODE_MIXED, MODE_RETURN} from '../libraries/cmp/cmpClient.js'; -import {GreedyPromise} from '../src/utils/promise.js'; -import {buildActivityParams} from '../src/activities/params.js'; const DEFAULT_CMP = 'iab'; const DEFAULT_CONSENT_TIMEOUT = 10000; +const CMP_VERSION = 1; export let userCMP; export let consentTimeout; -let staticConsentData; +export let staticConsentData; let consentData; let addedConsentHook = false; -function pipeCallbacks(fn, {onSuccess, onError}) { - new GreedyPromise((resolve) => resolve(fn())).then(onSuccess, (err) => { - if (err instanceof GPPError) { - onError(err.message, ...err.args); - } else { - onError(`GPP error:`, err); - } - }); -} +// add new CMPs here, with their dedicated lookup function +const cmpCallMap = { + 'iab': lookupIabConsent, + 'static': lookupStaticConsentData +}; -function lookupStaticConsentData(callbacks) { - return pipeCallbacks(() => processCmpData(staticConsentData), callbacks); +/** + * This function checks the state of the IAB gppData's applicableSection field (to ensure it's populated and has a valid value). + * section === 0 represents a CMP's default value when CMP is loading, it shoud not be used a real user's section. + * + * TODO --- The initial version of the GPP CMP API spec used this naming convention, but it was later changed as an update to the spec. + * CMPs should adjust their logic to use the new format (applicableSecctions), but that may not be the case with the initial release. + * Added support just in case for this transition period, can likely be removed at a later date... + * @param gppData represents the IAB gppData object + * @returns true|false + */ +function checkApplicableSectionIsReady(gppData) { + return gppData && Array.isArray(gppData.applicableSection) && gppData.applicableSection.length > 0 && gppData.applicableSection[0] !== 0; } -const GPP_10 = '1.0'; -const GPP_11 = '1.1'; +/** + * This function checks the state of the IAB gppData's applicableSections field (to ensure it's populated and has a valid value). + * section === 0 represents a CMP's default value when CMP is loading, it shoud not be used a real user's section. + * @param gppData represents the IAB gppData object + * @returns true|false + */ +function checkApplicableSectionsIsReady(gppData) { + return gppData && Array.isArray(gppData.applicableSections) && gppData.applicableSections.length > 0 && gppData.applicableSections[0] !== 0; +} -class GPPError { - constructor(message, arg) { - this.message = message; - this.args = arg == null ? [] : [arg]; - } +/** + * This function reads the consent string from the config to obtain the consent information of the user. + * @param {function({})} onSuccess acts as a success callback when the value is read from config; pass along consentObject from CMP + */ +function lookupStaticConsentData({onSuccess, onError}) { + processCmpData(staticConsentData, {onSuccess, onError}); } -export class GPPClient { - static CLIENTS = {}; +/** + * This function handles interacting with an IAB compliant CMP to obtain the consent information of the user. + * Given the async nature of the CMP's API, we pass in acting success/error callback functions to exit this function + * based on the appropriate result. + * @param {function({})} onSuccess acts as a success callback when CMP returns a value; pass along consentObjectfrom CMP + * @param {function(string, ...{}?)} cmpError acts as an error callback while interacting with CMP; pass along an error message (string) and any extra error arguments (purely for logging) + */ +function lookupIabConsent({onSuccess, onError}) { + const cmpApiName = '__gpp'; + const cmpCallbacks = {}; + let registeredPostMessageResponseListener = false; + + function findCMP() { + let f = window; + let cmpFrame; + let cmpDirectAccess = false; + while (true) { + try { + if (typeof f[cmpApiName] === 'function') { + cmpFrame = f; + cmpDirectAccess = true; + break; + } + } catch (e) {} - static register(apiVersion, defaultVersion = false) { - this.apiVersion = apiVersion; - this.CLIENTS[apiVersion] = this; - if (defaultVersion) { - this.CLIENTS.default = this; - } - } + // need separate try/catch blocks due to the exception errors thrown when trying to check for a frame that doesn't exist in 3rd party env + try { + if (f.frames['__gppLocator']) { + cmpFrame = f; + break; + } + } catch (e) {} - static INST; - - /** - * Ping the CMP to set up an appropriate client for it, and initialize it. - * - * @param mkCmp - * @returns {Promise<[GPPClient,Promise<{}>]>} a promise to two objects: - * - a GPPClient that talks the best GPP dialect we know for the CMP's version; - * - a promise to GPP data. - */ - static init(mkCmp = cmpClient) { - let inst = this.INST; - if (!inst) { - let err; - const reset = () => err && (this.INST = null); - inst = this.INST = this.ping(mkCmp).catch(e => { - err = true; - reset(); - throw e; - }); - reset(); + if (f === window.top) break; + f = f.parent; } - return inst.then(([client, pingData]) => [ - client, - client.initialized ? client.refresh() : client.init(pingData) - ]); - } - /** - * Ping the CMP to determine its version and set up a client appropriate for it. - * - * @param mkCmp - * @returns {Promise<[GPPClient, {}]>} a promise to two objects: - * - a GPPClient that talks the best GPP dialect we know for the CMP's version; - * - the result from pinging the CMP. - */ - static ping(mkCmp = cmpClient) { - const cmpOptions = { - apiName: '__gpp', - apiArgs: ['command', 'callback', 'parameter'], // do not pass version - not clear what it's for (or what we should use) + return { + cmpFrame, + cmpDirectAccess }; - - // in 1.0, 'ping' should return pingData but ignore callback; - // in 1.1 it should not return anything but run the callback - // the following looks for either - but once the version is known, produce a client that knows whether the - // rest of the interactions should pick return values or pass callbacks - - const probe = mkCmp({...cmpOptions, mode: MODE_RETURN}); - return new GreedyPromise((resolve, reject) => { - if (probe == null) { - reject(new GPPError('GPP CMP not found')); - return; - } - let done = false; // some CMPs do both return value and callbacks - avoid repeating log messages - const pong = (result, success) => { - if (done) return; - if (success != null && !success) { - reject(result); - return; - } - if (result == null) return; - done = true; - const cmpVersion = result?.gppVersion; - const Client = this.getClient(cmpVersion); - if (cmpVersion !== Client.apiVersion) { - logWarn(`Unrecognized GPP CMP version: ${cmpVersion}. Continuing using GPP API version ${Client}...`); - } else { - logInfo(`Using GPP version ${cmpVersion}`); - } - const mode = Client.apiVersion === GPP_10 ? MODE_MIXED : MODE_CALLBACK; - const client = new Client( - cmpVersion, - mkCmp({...cmpOptions, mode}) - ); - resolve([client, result]); - }; - - probe({ - command: 'ping', - callback: pong - }).then((res) => pong(res, true), reject); - }).finally(() => { - probe && probe.close(); - }); - } - - static getClient(cmpVersion) { - return this.CLIENTS.hasOwnProperty(cmpVersion) ? this.CLIENTS[cmpVersion] : this.CLIENTS.default; } - #resolve; - #reject; - #pending = []; - - initialized = false; + const {cmpFrame, cmpDirectAccess} = findCMP(); - constructor(cmpVersion, cmp) { - this.apiVersion = this.constructor.apiVersion; - this.cmpVersion = cmp; - this.cmp = cmp; - [this.#resolve, this.#reject] = [0, 1].map(slot => (result) => { - while (this.#pending.length) { - this.#pending.pop()[slot](result); - } - }); + if (!cmpFrame) { + return onError('GPP CMP not found.'); } - /** - * initialize this client - update consent data if already available, - * and set up event listeners to also update on CMP changes - * - * @param pingData - * @returns {Promise<{}>} a promise to GPP consent data - */ - init(pingData) { - const ready = this.updateWhenReady(pingData); - if (!this.initialized) { - this.initialized = true; - this.cmp({ - command: 'addEventListener', - callback: (event, success) => { - if (success != null && !success) { - this.#reject(new GPPError('Received error response from CMP', event)); - } else if (event?.pingData?.cmpStatus === 'error') { - this.#reject(new GPPError('CMP status is "error"; please check CMP setup', event)); - } else if (this.isCMPReady(event?.pingData || {}) && this.events.includes(event?.eventName)) { - this.#resolve(this.updateConsent(event.pingData)); - } - } - }); + const invokeCMP = (cmpDirectAccess) ? invokeCMPDirect : invokeCMPFrame; + + function invokeCMPDirect({command, callback, parameter, version = CMP_VERSION}, resultCb) { + if (typeof resultCb === 'function') { + resultCb(cmpFrame[cmpApiName](command, callback, parameter, version)); + } else { + cmpFrame[cmpApiName](command, callback, parameter, version); } - return ready; } - refresh() { - return this.cmp({command: 'ping'}).then(this.updateWhenReady.bind(this)); - } + function invokeCMPFrame({command, callback, parameter, version = CMP_VERSION}, resultCb) { + const callName = `${cmpApiName}Call`; + if (!registeredPostMessageResponseListener) { + // when we get the return message, call the stashed callback; + window.addEventListener('message', readPostMessageResponse, false); + registeredPostMessageResponseListener = true; + } - /** - * Retrieve and store GPP consent data. - * - * @param pingData - * @returns {Promise<{}>} a promise to GPP consent data - */ - updateConsent(pingData) { - return this.getGPPData(pingData).then((data) => { - if (data == null || isEmpty(data)) { - throw new GPPError('Received empty response from CMP', data); + // call CMP via postMessage + const callId = Math.random().toString(); + const msg = { + [callName]: { + command: command, + parameter, + version, + callId: callId } - return processCmpData(data); - }).then((data) => { - logInfo('Retrieved GPP consent from CMP:', data); - return data; - }); - } - - /** - * Return a promise to GPP consent data, to be retrieved the next time the CMP signals it's ready. - * - * @returns {Promise<{}>} - */ - nextUpdate() { - return new GreedyPromise((resolve, reject) => { - this.#pending.push([resolve, reject]); - }); - } - - /** - * Return a promise to GPP consent data, to be retrieved immediately if the CMP is ready according to `pingData`, - * or as soon as it signals that it's ready otherwise. - * - * @param pingData - * @returns {Promise<{}>} - */ - updateWhenReady(pingData) { - return this.isCMPReady(pingData) ? this.updateConsent(pingData) : this.nextUpdate(); - } -} - -// eslint-disable-next-line no-unused-vars -class GPP10Client extends GPPClient { - static { - super.register(GPP_10); - } - - events = ['sectionChange', 'cmpStatus']; - - isCMPReady(pingData) { - return pingData.cmpStatus === 'loaded'; - } - - getGPPData(pingData) { - const parsedSections = GreedyPromise.all( - (pingData.supportedAPIs || pingData.apiSupport || []).map((api) => this.cmp({ - command: 'getSection', - parameter: api - }).catch(err => { - logWarn(`Could not retrieve GPP section '${api}'`, err); - }).then((section) => [api, section])) - ).then(sections => { - // parse single section object into [core, gpc] to uniformize with 1.1 parsedSections - return Object.fromEntries( - sections.filter(([_, val]) => val != null) - .map(([api, section]) => { - const subsections = [ - Object.fromEntries(Object.entries(section).filter(([k]) => k !== 'Gpc')) - ]; - if (section.Gpc != null) { - subsections.push({ - SubsectionType: 1, - Gpc: section.Gpc - }); - } - return [api, subsections]; - }) - ); - }); - return GreedyPromise.all([ - this.cmp({command: 'getGPPData'}), - parsedSections - ]).then(([gppData, parsedSections]) => Object.assign({}, gppData, {parsedSections})); - } -} - -// eslint-disable-next-line no-unused-vars -class GPP11Client extends GPPClient { - static { - super.register(GPP_11, true); - } + }; - events = ['sectionChange', 'signalStatus']; + // TODO? - add logic to check if random was already used in the same session, and roll another if so? + cmpCallbacks[callId] = (typeof callback === 'function') ? callback : resultCb; + cmpFrame.postMessage(msg, '*'); - isCMPReady(pingData) { - return pingData.signalStatus === 'ready'; - } + function readPostMessageResponse(event) { + const cmpDataPkgName = `${cmpApiName}Return`; + const json = (typeof event.data === 'string' && event.data.includes(cmpDataPkgName)) ? JSON.parse(event.data) : event.data; + if (json[cmpDataPkgName] && json[cmpDataPkgName].callId) { + const payload = json[cmpDataPkgName]; - getGPPData(pingData) { - return GreedyPromise.resolve(pingData); + if (cmpCallbacks.hasOwnProperty(payload.callId)) { + cmpCallbacks[payload.callId](payload.returnValue); + } + } + } } -} -/** - * This function handles interacting with an IAB compliant CMP to obtain the consent information of the user. - * Given the async nature of the CMP's API, we pass in acting success/error callback functions to exit this function - * based on the appropriate result. - * @param {function({})} onSuccess acts as a success callback when CMP returns a value; pass along consentObjectfrom CMP - * @param {function(string, ...{}?)} cmpError acts as an error callback while interacting with CMP; pass along an error message (string) and any extra error arguments (purely for logging) - */ -export function lookupIabConsent({onSuccess, onError}, mkCmp = cmpClient) { - pipeCallbacks(() => GPPClient.init(mkCmp).then(([client, gppDataPm]) => gppDataPm), {onSuccess, onError}); + const startupMsg = (cmpDirectAccess) ? 'Detected GPP CMP API is directly accessible, calling it now...' + : 'Detected GPP CMP is outside the current iframe where Prebid.js is located, calling it now...'; + logInfo(startupMsg); + + invokeCMP({ + command: 'addEventListener', + callback: function (evt) { + if (evt) { + logInfo(`Received a ${(cmpDirectAccess ? 'direct' : 'postmsg')} response from GPP CMP for event`, evt); + if (evt.eventName === 'sectionChange' || evt.pingData.cmpStatus === 'loaded') { + invokeCMP({command: 'getGPPData'}, function (gppData) { + logInfo(`Received a ${cmpDirectAccess ? 'direct' : 'postmsg'} response from GPP CMP for getGPPData`, gppData); + processCmpData(gppData, {onSuccess, onError}); + }); + } else if (evt.pingData.cmpStatus === 'error') { + onError('CMP returned with a cmpStatus:error response. Please check CMP setup.'); + } + } + } + }); } -// add new CMPs here, with their dedicated lookup function -const cmpCallMap = { - 'iab': lookupIabConsent, - 'static': lookupStaticConsentData -}; - /** * Look up consent data and store it in the `consentData` global as well as `adapterManager.js`' gdprDataHandler. * @@ -338,7 +199,7 @@ function loadConsentData(cb) { } } - if (!cmpCallMap.hasOwnProperty(userCMP)) { + if (!includes(Object.keys(cmpCallMap), userCMP)) { done(null, false, `GPP CMP framework (${userCMP}) is not a supported framework. Aborting consentManagement module and resuming auction.`); return; } @@ -348,19 +209,19 @@ function loadConsentData(cb) { onError: function (msg, ...extraArgs) { done(null, true, msg, ...extraArgs); } - }; + } cmpCallMap[userCMP](callbacks); if (!isDone) { const onTimeout = () => { const continueToAuction = (data) => { done(data, false, 'GPP CMP did not load, continuing auction...'); - }; - pipeCallbacks(() => processCmpData(consentData), { + } + processCmpData(consentData, { onSuccess: continueToAuction, - onError: () => continueToAuction(storeConsentData()) - }); - }; + onError: () => continueToAuction(storeConsentData(undefined)) + }) + } if (consentTimeout === 0) { onTimeout(); } else { @@ -415,30 +276,43 @@ export const requestBidsHook = timedAuctionHook('gpp', function requestBidsHook( }); }); -function processCmpData(consentData) { - if ( - (consentData?.applicableSections != null && !Array.isArray(consentData.applicableSections)) || - (consentData?.gppString != null && !isStr(consentData.gppString)) || - (consentData?.parsedSections != null && !isPlainObject(consentData.parsedSections)) - ) { - throw new GPPError('CMP returned unexpected value during lookup process.', consentData); +/** + * This function checks the consent data provided by CMP to ensure it's in an expected state. + * If it's bad, we call `onError` + * If it's good, then we store the value and call `onSuccess` + */ +function processCmpData(consentObject, {onSuccess, onError}) { + function checkData() { + const gppString = consentObject && consentObject.gppString; + const gppSection = (checkApplicableSectionsIsReady(consentObject)) ? consentObject.applicableSections + : (checkApplicableSectionIsReady(consentObject)) ? consentObject.applicableSection : []; + + return !!( + (!Array.isArray(gppSection)) || + (Array.isArray(gppSection) && (!gppString || !isStr(gppString))) + ); + } + + if (checkData()) { + onError(`CMP returned unexpected value during lookup process.`, consentObject); + } else { + onSuccess(storeConsentData(consentObject)); } - return storeConsentData(consentData); } /** * Stores CMP data locally in module to make information available in adaptermanager.js for later in the auction - * @param {{}} gppData the result of calling a CMP's `getGPPData` (or equivalent) - * @param {{}} sectionData map from GPP section name to the result of calling a CMP's `getSection` (or equivalent) + * @param {object} cmpConsentObject required; an object representing user's consent choices (can be undefined in certain use-cases for this function only) */ -export function storeConsentData(gppData = {}) { +function storeConsentData(cmpConsentObject) { consentData = { - gppString: gppData?.gppString, - applicableSections: gppData?.applicableSections || [], - parsedSections: gppData?.parsedSections || {}, - gppData: gppData + gppString: (cmpConsentObject) ? cmpConsentObject.gppString : undefined, + + fullGppData: (cmpConsentObject) || undefined, }; - gppDataHandler.setConsentData(gppData); + consentData.applicableSections = (checkApplicableSectionsIsReady(cmpConsentObject)) ? cmpConsentObject.applicableSections + : (checkApplicableSectionIsReady(cmpConsentObject)) ? cmpConsentObject.applicableSection : []; + consentData.apiVersion = CMP_VERSION; return consentData; } @@ -450,7 +324,6 @@ export function resetConsentData() { userCMP = undefined; consentTimeout = undefined; gppDataHandler.reset(); - GPPClient.INST = null; } /** @@ -491,15 +364,11 @@ export function setConsentConfig(config) { if (!addedConsentHook) { getGlobal().requestBids.before(requestBidsHook, 50); - buildActivityParams.before((next, params) => { - return next(Object.assign({gppConsent: gppDataHandler.getConsentData()}, params)); - }); } addedConsentHook = true; gppDataHandler.enable(); loadConsentData(); // immediately look up consent data to make it available without requiring an auction } - config.getConfig('consentManagement', config => setConsentConfig(config.consentManagement)); export function enrichFPDHook(next, fpd) { diff --git a/modules/consentManagementUsp.js b/modules/consentManagementUsp.js index 1218c3724f4..fcc13152aa9 100644 --- a/modules/consentManagementUsp.js +++ b/modules/consentManagementUsp.js @@ -4,13 +4,12 @@ * 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 {deepSetValue, isFn, 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'; const DEFAULT_CONSENT_API = 'iab'; const DEFAULT_CONSENT_TIMEOUT = 50; @@ -42,6 +41,35 @@ function lookupStaticConsentData({onSuccess, onError}) { * based on the appropriate result. */ function lookupUspConsent({onSuccess, onError}) { + function findUsp() { + let f = window; + let uspapiFrame; + let uspapiFunction; + + while (true) { + try { + if (typeof f.__uspapi === 'function') { + uspapiFunction = f.__uspapi; + uspapiFrame = f; + break; + } + } catch (e) {} + + try { + if (f.frames['__uspapiLocator']) { + uspapiFrame = f; + break; + } + } catch (e) {} + if (f === window.top) break; + f = f.parent; + } + return { + uspapiFrame, + uspapiFunction, + }; + } + function handleUspApiResponseCallbacks() { const uspResponse = {}; @@ -64,36 +92,86 @@ function lookupUspConsent({onSuccess, onError}) { } let callbackHandler = handleUspApiResponseCallbacks(); + let uspapiCallbacks = {}; - const cmp = cmpClient({ - apiName: '__uspapi', - apiVersion: USPAPI_VERSION, - apiArgs: ['command', 'version', 'callback'], - }); + let { uspapiFrame, uspapiFunction } = findUsp(); - if (!cmp) { + if (!uspapiFrame) { return onError('USP CMP not found.'); } - if (cmp.isDirect) { + function registerDataDelHandler(invoker, arg2) { + try { + invoker('registerDeletion', arg2, adapterManager.callDataDeletionRequest); + } catch (e) { + logError('Error invoking CMP `registerDeletion`:', e); + } + } + + // to collect the consent information from the user, we perform a call to USPAPI + // to collect the user's consent choices represented as a string (via getUSPData) + + // the following code also determines where the USPAPI is located and uses the proper workflow to communicate with it: + // - use the USPAPI locator code to see if USP's located in the current window or an ancestor window. + // - else assume prebid is in an iframe, and use the locator to see if the CMP is located in a higher parent window. This works in cross domain iframes. + // - if USPAPI is not found, the iframe function will call the uspError exit callback to abort the rest of the USPAPI workflow + + if (isFn(uspapiFunction)) { logInfo('Detected USP CMP is directly accessible, calling it now...'); + uspapiFunction( + 'getUSPData', + USPAPI_VERSION, + callbackHandler.consentDataCallback + ); + registerDataDelHandler(uspapiFunction, USPAPI_VERSION); } else { logInfo( 'Detected USP CMP is outside the current iframe where Prebid.js is located, calling it now...' ); + callUspApiWhileInIframe( + 'getUSPData', + uspapiFrame, + callbackHandler.consentDataCallback + ); + registerDataDelHandler(callUspApiWhileInIframe, uspapiFrame); } - cmp({ - command: 'getUSPData', - callback: callbackHandler.consentDataCallback - }); + let listening = false; - cmp({ - command: 'registerDeletion', - callback: (res, success) => (success == null || success) && adapterManager.callDataDeletionRequest(res) - }).catch(e => { - logError('Error invoking CMP `registerDeletion`:', e); - }); + function callUspApiWhileInIframe(commandName, uspapiFrame, moduleCallback) { + function callUsp(cmd, ver, callback) { + let callId = Math.random() + ''; + let msg = { + __uspapiCall: { + command: cmd, + version: ver, + callId: callId, + }, + }; + + uspapiCallbacks[callId] = callback; + uspapiFrame.postMessage(msg, '*'); + }; + + /** when we get the return message, call the stashed callback */ + if (!listening) { + window.addEventListener('message', readPostMessageResponse, false); + listening = true; + } + + // call uspapi + callUsp(commandName, USPAPI_VERSION, moduleCallback); + + function readPostMessageResponse(event) { + const res = event && event.data && event.data.__uspapiReturn; + if (res && res.callId) { + if (uspapiCallbacks.hasOwnProperty(res.callId)) { + uspapiCallbacks[res.callId](res.returnValue, res.success); + delete uspapiCallbacks[res.callId]; + } + } + } + } } /** diff --git a/modules/consumableBidAdapter.js b/modules/consumableBidAdapter.js index 30b081e53d3..4e2a92fb594 100644 --- a/modules/consumableBidAdapter.js +++ b/modules/consumableBidAdapter.js @@ -3,12 +3,6 @@ import {config} from '../src/config.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - * @typedef {import('../src/adapters/bidderFactory.js').validBidRequests} validBidRequests - */ - const BIDDER_CODE = 'consumable'; const BASE_URI = 'https://e.serverbid.com/api/v2'; @@ -71,11 +65,6 @@ export const spec = { }; } - if (bidderRequest && bidderRequest.gppConsent && bidderRequest.gppConsent.gppString) { - data.gpp = bidderRequest.gppConsent.gppString; - data.gpp_sid = bidderRequest.gppConsent.applicableSections; - } - if (bidderRequest && bidderRequest.uspConsent) { data.ccpa = bidderRequest.uspConsent; } @@ -191,26 +180,20 @@ export const spec = { return bidResponses; }, - getUserSyncs: function(syncOptions, serverResponses, gdprConsent, uspConsent, gppConsent) { + getUserSyncs: function(syncOptions, serverResponses, gdprConsent, uspConsent) { let syncUrl = 'https://sync.serverbid.com/ss/' + siteId + '.html'; if (syncOptions.iframeEnabled) { if (gdprConsent && gdprConsent.consentString) { if (typeof gdprConsent.gdprApplies === 'boolean') { - syncUrl = appendUrlParam(syncUrl, `gdpr=${Number(gdprConsent.gdprApplies)}&gdpr_consent=${encodeURIComponent(gdprConsent.consentString) || ''}`); + syncUrl = appendUrlParam(syncUrl, `gdpr=${Number(gdprConsent.gdprApplies)}&gdpr_consent=${gdprConsent.consentString}`); } else { - syncUrl = appendUrlParam(syncUrl, `gdpr=0&gdpr_consent=${encodeURIComponent(gdprConsent.consentString) || ''}`); - } - } - if (gppConsent && gppConsent.gppString) { - syncUrl = appendUrlParam(syncUrl, `gpp=${encodeURIComponent(gppConsent.gppString)}`); - if (gppConsent.applicableSections && gppConsent.applicableSections.length > 0) { - syncUrl = appendUrlParam(syncUrl, `gpp_sid=${encodeURIComponent(gppConsent.applicableSections.join(','))}`); + syncUrl = appendUrlParam(syncUrl, `gdpr=0&gdpr_consent=${gdprConsent.consentString}`); } } - if (uspConsent) { - syncUrl = appendUrlParam(syncUrl, `us_privacy=${encodeURIComponent(uspConsent)}`); + if (uspConsent && uspConsent.consentString) { + syncUrl = appendUrlParam(syncUrl, `us_privacy=${uspConsent.consentString}`); } if (!serverResponses || serverResponses.length === 0 || !serverResponses[0].body.bdr || serverResponses[0].body.bdr !== 'cx') { diff --git a/modules/contxtfulRtdProvider.js b/modules/contxtfulRtdProvider.js deleted file mode 100644 index 6d4b2a2ce29..00000000000 --- a/modules/contxtfulRtdProvider.js +++ /dev/null @@ -1,150 +0,0 @@ -/** - * Contxtful Technologies Inc - * This RTD module provides receptivity feature that can be accessed using the - * getReceptivity() function. The value returned by this function enriches the ad-units - * that are passed within the `getTargetingData` functions and GAM. - */ - -import { submodule } from '../src/hook.js'; -import { - logInfo, - logError, - isStr, - isEmptyStr, - buildUrl, -} from '../src/utils.js'; -import { loadExternalScript } from '../src/adloader.js'; - -const MODULE_NAME = 'contxtful'; -const MODULE = `${MODULE_NAME}RtdProvider`; - -const CONTXTFUL_RECEPTIVITY_DOMAIN = 'api.receptivity.io'; - -let initialReceptivity = null; -let contxtfulModule = null; - -/** - * Init function used to start sub module - * @param { { params: { version: String, customer: String, hostname: String } } } config - * @return { Boolean } - */ -function init(config) { - logInfo(MODULE, 'init', config); - initialReceptivity = null; - contxtfulModule = null; - - try { - const {version, customer, hostname} = extractParameters(config); - initCustomer(version, customer, hostname); - return true; - } catch (error) { - logError(MODULE, error); - return false; - } -} - -/** - * Extract required configuration for the sub module. - * validate that all required configuration are present and are valid. - * Throws an error if any config is missing of invalid. - * @param { { params: { version: String, customer: String, hostname: String } } } config - * @return { { version: String, customer: String, hostname: String } } - * @throws params.{name} should be a non-empty string - */ -function extractParameters(config) { - const version = config?.params?.version; - if (!isStr(version) || isEmptyStr(version)) { - throw Error(`${MODULE}: params.version should be a non-empty string`); - } - - const customer = config?.params?.customer; - if (!isStr(customer) || isEmptyStr(customer)) { - throw Error(`${MODULE}: params.customer should be a non-empty string`); - } - - const hostname = config?.params?.hostname || CONTXTFUL_RECEPTIVITY_DOMAIN; - - return {version, customer, hostname}; -} - -/** - * Initialize sub module for a customer. - * This will load the external resources for the sub module. - * @param { String } version - * @param { String } customer - * @param { String } hostname - */ -function initCustomer(version, customer, hostname) { - const CONNECTOR_URL = buildUrl({ - protocol: 'https', - host: hostname, - pathname: `/${version}/prebid/${customer}/connector/p.js`, - }); - - const externalScript = loadExternalScript(CONNECTOR_URL, MODULE_NAME); - addExternalScriptEventListener(externalScript); -} - -/** - * Add event listener to the script tag for the expected events from the external script. - * @param { HTMLScriptElement } script - */ -function addExternalScriptEventListener(script) { - if (!script) { - return; - } - - script.addEventListener('initialReceptivity', ({ detail }) => { - let receptivityState = detail?.ReceptivityState; - if (isStr(receptivityState) && !isEmptyStr(receptivityState)) { - initialReceptivity = receptivityState; - } - }); - - script.addEventListener('rxEngineIsReady', ({ detail: api }) => { - contxtfulModule = api; - }); -} - -/** - * Return current receptivity. - * @return { { ReceptivityState: String } } - */ -function getReceptivity() { - return { - ReceptivityState: contxtfulModule?.GetReceptivity()?.ReceptivityState || initialReceptivity - }; -} - -/** - * Set targeting data for ad server - * @param { [String] } adUnits - * @param {*} _config - * @param {*} _userConsent - * @return {{ code: { ReceptivityState: String } }} - */ -function getTargetingData(adUnits, _config, _userConsent) { - logInfo(MODULE, 'getTargetingData'); - if (!adUnits) { - return {}; - } - - const receptivity = getReceptivity(); - if (!receptivity?.ReceptivityState) { - return {}; - } - - return adUnits.reduce((targets, code) => { - targets[code] = receptivity; - return targets; - }, {}); -} - -export const contxtfulSubmodule = { - name: MODULE_NAME, - init, - extractParameters, - getTargetingData, -}; - -submodule('realTimeData', contxtfulSubmodule); diff --git a/modules/contxtfulRtdProvider.md b/modules/contxtfulRtdProvider.md deleted file mode 100644 index dfefca2067a..00000000000 --- a/modules/contxtfulRtdProvider.md +++ /dev/null @@ -1,65 +0,0 @@ -# Overview - -**Module Name:** Contxtful RTD Provider -**Module Type:** RTD Provider -**Maintainer:** [prebid@contxtful.com](mailto:prebid@contxtful.com) - -# Description - -The Contxtful RTD module offers a unique feature—Receptivity. Receptivity is an efficiency metric, enabling the qualification of any instant in a session in real time based on attention. The core idea is straightforward: the likelihood of an ad’s success increases when it grabs attention and is presented in the right context at the right time. - -To utilize this module, you need to register for an account with [Contxtful](https://contxtful.com). For inquiries, please contact [prebid@contxtful.com](mailto:prebid@contxtful.com). - -# Configuration - -## Build Instructions - -To incorporate this module into your `prebid.js`, compile the module using the following command: - -```sh -gulp build --modules=contxtfulRtdProvider, -``` - -## Module Configuration - -Configure the `contxtfulRtdProvider` by passing the required settings through the `setConfig` function in `prebid.js`. - -```js -import pbjs from 'prebid.js'; - -pbjs.setConfig({ - "realTimeData": { - "auctionDelay": 1000, - "dataProviders": [ - { - "name": "contxtful", - "waitForIt": true, - "params": { - "version": "", - "customer": "" - } - } - ] - } -}); -``` - -### Configuration Parameters - -| Name | Type | Scope | Description | -|------------|----------|----------|-------------------------------------------| -| `version` | `string` | Required | Specifies the API version of Contxtful. | -| `customer` | `string` | Required | Your unique customer identifier. | - -# Usage - -The `contxtfulRtdProvider` module loads an external JavaScript file and authenticates with Contxtful APIs. The `getTargetingData` function then adds a `ReceptivityState` to each ad slot, which can have one of two values: `Receptive` or `NonReceptive`. - -```json -{ - "adUnitCode1": { "ReceptivityState": "Receptive" }, - "adUnitCode2": { "ReceptivityState": "NonReceptive" } -} -``` - -This module also integrates seamlessly with Google Ad Manager, ensuring that the `ReceptivityState` is available as early as possible in the ad serving process. \ No newline at end of file diff --git a/modules/conversantBidAdapter.js b/modules/conversantBidAdapter.js index dffc1a9f33b..fd436e51461 100644 --- a/modules/conversantBidAdapter.js +++ b/modules/conversantBidAdapter.js @@ -1,125 +1,33 @@ import { - buildUrl, + logWarn, + isStr, deepAccess, - deepSetValue, - getBidIdParameter, isArray, + getBidIdParameter, + deepSetValue, + isEmpty, + _each, + convertTypes, + parseUrl, + mergeDeep, + buildUrl, + _map, + logError, isFn, isPlainObject, - isStr, - logError, - logWarn, - mergeDeep, - parseUrl, } from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {BANNER, VIDEO} from '../src/mediaTypes.js'; import {getStorageManager} from '../src/storageManager.js'; -import {convertTypes} from '../libraries/transformParamsUtils/convertTypes.js'; -import {ortbConverter} from '../libraries/ortbConverter/converter.js'; -import {ORTB_MTYPES} from '../libraries/ortbConverter/processors/mediaType.js'; // Maintainer: mediapsr@epsilon.com -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - * @typedef {import('../src/adapters/bidderFactory.js').ServerRequest} ServerRequest - * @typedef {import('../src/adapters/bidderFactory.js').Device} Device - */ - const GVLID = 24; const BIDDER_CODE = 'conversant'; export const storage = getStorageManager({gvlid: GVLID, bidderCode: BIDDER_CODE}); const URL = 'https://web.hb.ad.cpe.dotomi.com/cvx/client/hb/ortb/25'; -function setSiteId(bidRequest, request) { - if (bidRequest.params.site_id) { - if (request.site) { - request.site.id = bidRequest.params.site_id; - } - if (request.app) { - request.app.id = bidRequest.params.site_id; - } - } -} - -function setPubcid(bidRequest, request) { - // Add common id if available - const pubcid = getPubcid(bidRequest); - if (pubcid) { - deepSetValue(request, 'user.ext.fpc', pubcid); - } -} - -const converter = ortbConverter({ - context: { - netRevenue: true, - ttl: 300 - }, - request: function (buildRequest, imps, bidderRequest, context) { - const request = buildRequest(imps, bidderRequest, context); - request.at = 1; - if (context.bidRequests) { - const bidRequest = context.bidRequests[0]; - setSiteId(bidRequest, request); - setPubcid(bidRequest, request); - } - - return request; - }, - imp(buildImp, bidRequest, context) { - const imp = buildImp(bidRequest, context); - const data = { - secure: 1, - bidfloor: getBidFloor(bidRequest) || 0, - displaymanager: 'Prebid.js', - displaymanagerver: '$prebid.version$' - }; - copyOptProperty(bidRequest.params.tag_id, data, 'tagid'); - mergeDeep(imp, data, imp); - return imp; - }, - bidResponse: function (buildBidResponse, bid, context) { - if (!bid.price) return; - - // ensure that context.mediaType is set to banner or video otherwise - if (!context.mediaType && context.bidRequest.mediaTypes) { - const [type] = Object.keys(context.bidRequest.mediaTypes); - if (Object.values(ORTB_MTYPES).includes(type)) { - context.mediaType = type; - } - } - const bidResponse = buildBidResponse(bid, context); - return bidResponse; - }, - - overrides: { - imp: { - banner(fillBannerImp, imp, bidRequest, context) { - 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}}}); - } - fillBannerImp(imp, bidRequest, context); - }, - video(fillVideoImp, imp, bidRequest, context) { - if (bidRequest.mediaTypes && !bidRequest.mediaTypes.video) return; - const videoData = {}; - copyOptProperty(bidRequest.params?.position, videoData, 'pos'); - copyOptProperty(bidRequest.params?.mimes, videoData, 'mimes'); - copyOptProperty(bidRequest.params?.maxduration, videoData, 'maxduration'); - copyOptProperty(bidRequest.params?.protocols, videoData, 'protocols'); - copyOptProperty(bidRequest.params?.api, videoData, 'api'); - imp.video = mergeDeep(videoData, imp.video); - fillVideoImp(imp, bidRequest, context); - } - }, - } -}); - export const spec = { code: BIDDER_CODE, gvlid: GVLID, @@ -157,14 +65,148 @@ export const spec = { return true; }, - buildRequests: function(bidRequests, bidderRequest) { - const payload = converter.toORTB({bidderRequest, bidRequests}); - const result = { + /** + * Make a server request from the list of BidRequests. + * + * @param {BidRequest[]} validBidRequests - an array of bids + * @param bidderRequest + * @return {ServerRequest} Info describing the request to the server. + */ + buildRequests: function(validBidRequests, bidderRequest) { + const page = (bidderRequest && bidderRequest.refererInfo) ? bidderRequest.refererInfo.page : ''; + let siteId = ''; + let pubcid = null; + let pubcidName = '_pubcid'; + let bidurl = URL; + + const conversantImps = validBidRequests.map(function(bid) { + const bidfloor = getBidFloor(bid); + + siteId = getBidIdParameter('site_id', bid.params) || siteId; + pubcidName = getBidIdParameter('pubcid_name', bid.params) || pubcidName; + + const imp = { + id: bid.bidId, + secure: 1, + bidfloor: bidfloor || 0, + displaymanager: 'Prebid.js', + displaymanagerver: '$prebid.version$' + }; + if (bid.ortb2Imp) { + mergeDeep(imp, bid.ortb2Imp); + } + + copyOptProperty(bid.params.tag_id, imp, 'tagid'); + + if (isVideoRequest(bid)) { + const videoData = deepAccess(bid, 'mediaTypes.video') || {}; + const format = convertSizes(videoData.playerSize || bid.sizes); + const video = {}; + + if (format && format[0]) { + copyOptProperty(format[0].w, video, 'w'); + copyOptProperty(format[0].h, video, 'h'); + } + + copyOptProperty(bid.params.position || videoData.pos, video, 'pos'); + copyOptProperty(bid.params.mimes || videoData.mimes, video, 'mimes'); + copyOptProperty(bid.params.maxduration || videoData.maxduration, video, 'maxduration'); + copyOptProperty(bid.params.protocols || videoData.protocols, video, 'protocols'); + copyOptProperty(bid.params.api || videoData.api, video, 'api'); + + imp.video = video; + } else { + const bannerData = deepAccess(bid, 'mediaTypes.banner') || {}; + const format = convertSizes(bannerData.sizes || bid.sizes); + const banner = {format: format}; + + copyOptProperty(bid.params.position || bannerData.pos, banner, 'pos'); + + imp.banner = banner; + } + + if (bid.userId && bid.userId.pubcid) { + pubcid = bid.userId.pubcid; + } else if (bid.crumbs && bid.crumbs.pubcid) { + pubcid = bid.crumbs.pubcid; + } + if (bid.params.white_label_url) { + bidurl = bid.params.white_label_url; + } + + return imp; + }); + + const payload = { + id: bidderRequest.bidderRequestId, + imp: conversantImps, + source: { + tid: bidderRequest.ortb2?.source?.tid, + }, + site: { + id: siteId, + mobile: document.querySelector('meta[name="viewport"][content*="width=device-width"]') !== null ? 1 : 0, + page: page + }, + device: getDevice(), + at: 1 + }; + + let userExt = {}; + + // pass schain object if it is present + const schain = deepAccess(validBidRequests, '0.schain'); + if (schain) { + deepSetValue(payload, 'source.ext.schain', schain); + } + + if (bidderRequest) { + if (bidderRequest.timeout) { + deepSetValue(payload, 'tmax', bidderRequest.timeout); + } + + // Add GDPR flag and consent string + if (bidderRequest.gdprConsent) { + userExt.consent = bidderRequest.gdprConsent.consentString; + + if (typeof bidderRequest.gdprConsent.gdprApplies === 'boolean') { + deepSetValue(payload, 'regs.ext.gdpr', bidderRequest.gdprConsent.gdprApplies ? 1 : 0); + } + } + + if (bidderRequest.uspConsent) { + deepSetValue(payload, 'regs.ext.us_privacy', bidderRequest.uspConsent); + } + } + + if (!pubcid) { + pubcid = readStoredValue(pubcidName); + } + + // Add common id if available + if (pubcid) { + userExt.fpc = pubcid; + } + + // Add Eids if available + const eids = collectEids(validBidRequests); + if (eids.length > 0) { + userExt.eids = eids; + } + + // Only add the user object if it's not empty + if (!isEmpty(userExt)) { + payload.user = {ext: userExt}; + } + + const firstPartyData = bidderRequest.ortb2 || {}; + mergeDeep(payload, firstPartyData); + + return { method: 'POST', - url: makeBidUrl(bidRequests[0]), + url: bidurl, data: payload, }; - return result; }, /** * Unpack the response from the server into a list of bids. @@ -174,7 +216,59 @@ export const spec = { * @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}); + const bidResponses = []; + const requestMap = {}; + serverResponse = serverResponse.body; + + if (bidRequest && bidRequest.data && bidRequest.data.imp) { + _each(bidRequest.data.imp, imp => requestMap[imp.id] = imp); + } + + if (serverResponse && isArray(serverResponse.seatbid)) { + _each(serverResponse.seatbid, function(bidList) { + _each(bidList.bid, function(conversantBid) { + const responseCPM = parseFloat(conversantBid.price); + if (responseCPM > 0.0 && conversantBid.impid) { + const responseAd = conversantBid.adm || ''; + const responseNurl = conversantBid.nurl || ''; + const request = requestMap[conversantBid.impid]; + + const bid = { + requestId: conversantBid.impid, + currency: serverResponse.cur || 'USD', + cpm: responseCPM, + creativeId: conversantBid.crid || '', + ttl: 300, + netRevenue: true + }; + bid.meta = {}; + if (conversantBid.adomain && conversantBid.adomain.length > 0) { + bid.meta.advertiserDomains = conversantBid.adomain; + } + + if (request.video) { + if (responseAd.charAt(0) === '<') { + bid.vastXml = responseAd; + } else { + bid.vastUrl = responseAd; + } + + bid.mediaType = 'video'; + bid.width = request.video.w; + bid.height = request.video.h; + } else { + bid.ad = responseAd + ''; + bid.width = conversantBid.w; + bid.height = conversantBid.h; + } + + bidResponses.push(bid); + } + }) + }); + } + + return bidResponses; }, /** @@ -234,18 +328,51 @@ export const spec = { } }; -function getPubcid(bidRequest) { - let pubcid = null; - if (bidRequest.userId && bidRequest.userId.pubcid) { - pubcid = bidRequest.userId.pubcid; - } else if (bidRequest.crumbs && bidRequest.crumbs.pubcid) { - pubcid = bidRequest.crumbs.pubcid; - } - if (!pubcid) { - const pubcidName = getBidIdParameter('pubcid_name', bidRequest.params) || '_pubcid'; - pubcid = readStoredValue(pubcidName); +/** + * Determine do-not-track state + * + * @returns {boolean} + */ +function getDNT() { + return navigator.doNotTrack === '1' || window.doNotTrack === '1' || navigator.msDoNoTrack === '1' || navigator.doNotTrack === 'yes'; +} + +/** + * Return openrtb device object that includes ua, width, and height. + * + * @returns {Device} Openrtb device object + */ +function getDevice() { + const language = navigator.language ? 'language' : 'userLanguage'; + return { + h: screen.height, + w: screen.width, + dnt: getDNT() ? 1 : 0, + language: navigator[language].split('-')[0], + make: navigator.vendor ? navigator.vendor : '', + ua: navigator.userAgent + }; +} + +/** + * Convert arrays of widths and heights to an array of objects with w and h properties. + * + * [[300, 250], [300, 600]] => [{w: 300, h: 250}, {w: 300, h: 600}] + * + * @param {Array.>} bidSizes - arrays of widths and heights + * @returns {object[]} Array of objects with w and h + */ +function convertSizes(bidSizes) { + let format; + if (Array.isArray(bidSizes)) { + if (bidSizes.length === 2 && typeof bidSizes[0] === 'number' && typeof bidSizes[1] === 'number') { + format = [{w: bidSizes[0], h: bidSizes[1]}]; + } else { + format = _map(bidSizes, d => { return {w: d[0], h: d[1]}; }); + } } - return pubcid; + + return format; } /** @@ -271,6 +398,33 @@ function copyOptProperty(src, dst, dstName) { } } +/** + * Collect IDs from validBidRequests and store them as an extended id array + * @param bidRequests valid bid requests + */ +function collectEids(bidRequests) { + const request = bidRequests[0]; // bidRequests have the same userId object + const eids = []; + if (isArray(request.userIdAsEids) && request.userIdAsEids.length > 0) { + // later following white-list can be converted to block-list if needed + const requiredSourceValues = { + 'epsilon.com': 1, + 'adserver.org': 1, + 'liveramp.com': 1, + 'criteo.com': 1, + 'id5-sync.com': 1, + 'parrable.com': 1, + 'liveintent.com': 1 + }; + request.userIdAsEids.forEach(function(eid) { + if (requiredSourceValues.hasOwnProperty(eid.source)) { + eids.push(eid); + } + }); + } + return eids; +} + /** * Look for a stored value from both cookie and local storage and return the first value found. * @param key Key for the search @@ -326,12 +480,4 @@ function getBidFloor(bid) { return floor } -function makeBidUrl(bid) { - let bidurl = URL; - if (bid.params.white_label_url) { - bidurl = bid.params.white_label_url; - } - return bidurl; -} - registerBidder(spec); diff --git a/modules/cpmstarBidAdapter.js b/modules/cpmstarBidAdapter.js index e076fb4b0bb..9e237ef2558 100755 --- a/modules/cpmstarBidAdapter.js +++ b/modules/cpmstarBidAdapter.js @@ -1,8 +1,8 @@ + import * as utils 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 {getBidIdParameter} from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { VIDEO, BANNER } from '../src/mediaTypes.js'; +import { config } from '../src/config.js'; const BIDDER_CODE = 'cpmstar'; @@ -49,13 +49,13 @@ export const spec = { var bidRequest = validBidRequests[i]; var referer = bidderRequest.refererInfo.page ? bidderRequest.refererInfo.page : bidderRequest.refererInfo.domain; referer = encodeURIComponent(referer); - var e = getBidIdParameter('endpoint', bidRequest.params); + var e = utils.getBidIdParameter('endpoint', bidRequest.params); var ENDPOINT = e == 'dev' ? ENDPOINT_DEV : e == 'staging' ? ENDPOINT_STAGING : ENDPOINT_PRODUCTION; var mediaType = spec.getMediaType(bidRequest); var playerSize = spec.getPlayerSize(bidRequest); var videoArgs = '&fv=0' + (playerSize ? ('&w=' + playerSize[0] + '&h=' + playerSize[1]) : ''); var url = ENDPOINT + '?media=' + mediaType + (mediaType == VIDEO ? videoArgs : '') + - '&json=c_b&mv=1&poolid=' + getBidIdParameter('placementId', bidRequest.params) + + '&json=c_b&mv=1&poolid=' + utils.getBidIdParameter('placementId', bidRequest.params) + '&reachedTop=' + encodeURIComponent(bidderRequest.refererInfo.reachedTop) + '&requestid=' + bidRequest.bidId + '&referer=' + encodeURIComponent(referer); diff --git a/modules/craftBidAdapter.js b/modules/craftBidAdapter.js index a2a054d7659..8f7821173c1 100644 --- a/modules/craftBidAdapter.js +++ b/modules/craftBidAdapter.js @@ -1,4 +1,4 @@ -import {getBidRequest, logError} from '../src/utils.js'; +import {convertCamelToUnderscore, convertTypes, getBidRequest, logError} from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js'; import {auctionManager} from '../src/auctionManager.js'; @@ -7,9 +7,7 @@ import {getStorageManager} from '../src/storageManager.js'; import {ajax} from '../src/ajax.js'; import {hasPurpose1Consent} from '../src/utils/gpdr.js'; import {convertOrtbRequestToProprietaryNative} from '../src/native.js'; -import {getANKeywordParam, transformBidderParamKeywords} from '../libraries/appnexusUtils/anKeywords.js'; -import {convertCamelToUnderscore} from '../libraries/appnexusUtils/anUtils.js'; -import {convertTypes} from '../libraries/transformParamsUtils/convertTypes.js'; +import {getANKeywordParam, transformBidderParamKeywords} from '../libraries/appnexusKeywords/anKeywords.js'; const BIDDER_CODE = 'craft'; const URL_BASE = 'https://gacraft.jp/prebid-v3'; diff --git a/modules/criteoBidAdapter.js b/modules/criteoBidAdapter.js index a8a1416e1f3..993346df849 100644 --- a/modules/criteoBidAdapter.js +++ b/modules/criteoBidAdapter.js @@ -3,30 +3,19 @@ import { loadExternalScript } from '../src/adloader.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { config } from '../src/config.js'; import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; +import { find } from '../src/polyfill.js'; import { verify } from 'criteo-direct-rsa-validate/build/verify.js'; // ref#2 import { getStorageManager } from '../src/storageManager.js'; import { getRefererInfo } from '../src/refererDetection.js'; import { hasPurpose1Consent } from '../src/utils/gpdr.js'; import { Renderer } from '../src/Renderer.js'; import { OUTSTREAM } from '../src/video.js'; -import { ajax } from '../src/ajax.js'; - -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - * @typedef {import('../src/adapters/bidderFactory.js').ServerRequest} ServerRequest - * @typedef {import('../src/adapters/bidderFactory.js').BidderSpec} BidderSpec - * @typedef {import('../src/adapters/bidderFactory.js').TimedOutBid} TimedOutBid - */ const GVLID = 91; export const ADAPTER_VERSION = 36; const BIDDER_CODE = 'criteo'; const CDB_ENDPOINT = 'https://bidder.criteo.com/cdb'; const PROFILE_ID_INLINE = 207; -const FLEDGE_SELLER_DOMAIN = 'https://grid-mercury.criteo.com'; -const FLEDGE_SELLER_TIMEOUT = 500; -const FLEDGE_DECISION_LOGIC_URL = 'https://grid-mercury.criteo.com/fledge/decision'; export const PROFILE_ID_PUBLISHERTAG = 185; export const storage = getStorageManager({ bidderCode: BIDDER_CODE }); const LOG_PREFIX = 'Criteo: '; @@ -39,7 +28,7 @@ const LOG_PREFIX = 'Criteo: '; Unminified source code can be found in the privately shared repo: https://github.com/Prebid-org/prebid-js-external-js-criteo/blob/master/dist/prod.js */ const FAST_BID_VERSION_PLACEHOLDER = '%FAST_BID_VERSION%'; -export const FAST_BID_VERSION_CURRENT = 144; +export const FAST_BID_VERSION_CURRENT = 136; const FAST_BID_VERSION_LATEST = 'latest'; const FAST_BID_VERSION_NONE = 'none'; const PUBLISHER_TAG_URL_TEMPLATE = 'https://static.criteo.net/js/ld/publishertag.prebid' + FAST_BID_VERSION_PLACEHOLDER + '.js'; @@ -133,8 +122,7 @@ export const spec = { return []; }, - /** - * f + /** f * @param {object} bid * @return {boolean} */ @@ -213,7 +201,7 @@ export const spec = { /** * @param {*} response * @param {ServerRequest} request - * @return {Bid[] | {bids: Bid[], fledgeAuctionConfigs: object[]}} + * @return {Bid[]} */ interpretResponse: (response, request) => { const body = response.body || response; @@ -227,105 +215,57 @@ export const spec = { } const bids = []; - const fledgeAuctionConfigs = []; if (body && body.slots && isArray(body.slots)) { body.slots.forEach(slot => { - const bidRequest = getAssociatedBidRequest(request.bidRequests, slot); - if (bidRequest) { - const bidId = bidRequest.bidId; - const bid = { - requestId: bidId, - cpm: slot.cpm, - currency: slot.currency, - netRevenue: true, - ttl: slot.ttl || 60, - creativeId: slot.creativecode, - width: slot.width, - height: slot.height, - dealId: slot.deal, + const bidRequest = find(request.bidRequests, b => b.adUnitCode === slot.impid && (!b.params.zoneId || parseInt(b.params.zoneId) === slot.zoneid)); + const bidId = bidRequest.bidId; + const bid = { + requestId: bidId, + cpm: slot.cpm, + currency: slot.currency, + netRevenue: true, + ttl: slot.ttl || 60, + creativeId: slot.creativecode, + width: slot.width, + height: slot.height, + dealId: slot.deal, + }; + if (body.ext?.paf?.transmission && slot.ext?.paf?.content_id) { + const pafResponseMeta = { + content_id: slot.ext.paf.content_id, + transmission: response.ext.paf.transmission }; - if (body.ext?.paf?.transmission && slot.ext?.paf?.content_id) { - const pafResponseMeta = { - content_id: slot.ext.paf.content_id, - transmission: response.ext.paf.transmission - }; - bid.meta = Object.assign({}, bid.meta, { paf: pafResponseMeta }); - } - if (slot.adomain) { - bid.meta = Object.assign({}, bid.meta, { advertiserDomains: [slot.adomain].flat() }); - } - if (slot.ext?.meta?.networkName) { - bid.meta = Object.assign({}, bid.meta, { networkName: slot.ext.meta.networkName }) - } - if (slot.native) { - if (bidRequest.params.nativeCallback) { - bid.ad = createNativeAd(bidId, slot.native, bidRequest.params.nativeCallback); - } else { - bid.native = createPrebidNativeAd(slot.native); - bid.mediaType = NATIVE; - } - } else if (slot.video) { - bid.vastUrl = slot.displayurl; - bid.mediaType = VIDEO; - const context = deepAccess(bidRequest, 'mediaTypes.video.context'); - // if outstream video, add a default render for it. - if (context === OUTSTREAM) { - bid.renderer = createOutstreamVideoRenderer(slot); - } - } else { - bid.ad = slot.creative; - } - bids.push(bid); + bid.meta = Object.assign({}, bid.meta, { paf: pafResponseMeta }); } - }); - } - - if (isArray(body.ext?.igbid)) { - const seller = body.ext.seller || FLEDGE_SELLER_DOMAIN; - const sellerTimeout = body.ext.sellerTimeout || FLEDGE_SELLER_TIMEOUT; - body.ext.igbid.forEach((igbid) => { - const perBuyerSignals = {}; - igbid.igbuyer.forEach(buyerItem => { - perBuyerSignals[buyerItem.origin] = buyerItem.buyerdata; - }); - const bidRequest = request.bidRequests.find(b => b.bidId === igbid.impid); - const bidId = bidRequest.bidId; - let sellerSignals = body.ext.sellerSignals || {}; - if (!sellerSignals.floor && bidRequest.params.bidFloor) { - sellerSignals.floor = bidRequest.params.bidFloor; + if (slot.adomain) { + bid.meta = Object.assign({}, bid.meta, { advertiserDomains: [slot.adomain].flat() }); } - if (!sellerSignals.sellerCurrency && bidRequest.params.bidFloorCur) { - sellerSignals.sellerCurrency = bidRequest.params.bidFloorCur; + if (slot.ext?.meta?.networkName) { + bid.meta = Object.assign({}, bid.meta, { networkName: slot.ext.meta.networkName }) } - if (body?.ext?.sellerSignalsPerImp !== undefined) { - const sellerSignalsPerImp = body.ext.sellerSignalsPerImp[bidId]; - if (sellerSignalsPerImp !== undefined) { - sellerSignals = {...sellerSignals, ...sellerSignalsPerImp}; + if (slot.native) { + if (bidRequest.params.nativeCallback) { + bid.ad = createNativeAd(bidId, slot.native, bidRequest.params.nativeCallback); + } else { + bid.native = createPrebidNativeAd(slot.native); + bid.mediaType = NATIVE; } + } else if (slot.video) { + bid.vastUrl = slot.displayurl; + bid.mediaType = VIDEO; + const context = deepAccess(bidRequest, 'mediaTypes.video.context'); + // if outstream video, add a default render for it. + if (context === OUTSTREAM) { + bid.renderer = createOutstreamVideoRenderer(slot); + } + } else { + bid.ad = slot.creative; } - fledgeAuctionConfigs.push({ - bidId, - config: { - seller, - sellerSignals, - sellerTimeout, - perBuyerSignals, - auctionSignals: {}, - decisionLogicUrl: FLEDGE_DECISION_LOGIC_URL, - interestGroupBuyers: Object.keys(perBuyerSignals), - }, - }); + bids.push(bid); }); } - if (fledgeAuctionConfigs.length) { - return { - bids, - fledgeAuctionConfigs, - }; - } - return bids; }, /** @@ -366,23 +306,6 @@ export const spec = { adapter.handleSetTargeting(bid); } }, - - /** - * @param {BidRequest[]} bidRequests - */ - onDataDeletionRequest: (bidRequests) => { - const id = readFromAllStorages(BUNDLE_COOKIE_NAME); - if (id) { - deleteFromAllStorages(BUNDLE_COOKIE_NAME); - ajax('https://privacy.criteo.com/api/privacy/datadeletionrequest', - null, - JSON.stringify({ publisherUserId: id }), - { - contentType: 'application/json', - method: 'POST' - }); - } - } }; function readFromAllStorages(name) { @@ -521,7 +444,6 @@ function buildCdbRequest(context, bidRequests, bidderRequest) { networkId = bidRequest.params.networkId || networkId; schain = bidRequest.schain || schain; const slot = { - slotid: bidRequest.bidId, impid: bidRequest.adUnitCode, transactionid: bidRequest.ortb2Imp?.ext?.tid }; @@ -561,7 +483,6 @@ function buildCdbRequest(context, bidRequests, bidderRequest) { if (hasVideoMediaType(bidRequest)) { const video = { - context: bidRequest.mediaTypes.video.context, playersizes: parseSizes(deepAccess(bidRequest, 'mediaTypes.video.playerSize'), parseSize), mimes: bidRequest.mediaTypes.video.mimes, protocols: bidRequest.mediaTypes.video.protocols, @@ -572,19 +493,7 @@ function buildCdbRequest(context, bidRequests, bidderRequest) { minduration: bidRequest.mediaTypes.video.minduration, playbackmethod: bidRequest.mediaTypes.video.playbackmethod, startdelay: bidRequest.mediaTypes.video.startdelay, - plcmt: bidRequest.mediaTypes.video.plcmt, - w: bidRequest.mediaTypes.video.w, - h: bidRequest.mediaTypes.video.h, - linearity: bidRequest.mediaTypes.video.linearity, - skipmin: bidRequest.mediaTypes.video.skipmin, - skipafter: bidRequest.mediaTypes.video.skipafter, - minbitrate: bidRequest.mediaTypes.video.minbitrate, - maxbitrate: bidRequest.mediaTypes.video.maxbitrate, - delivery: bidRequest.mediaTypes.video.delivery, - pos: bidRequest.mediaTypes.video.pos, - playbackend: bidRequest.mediaTypes.video.playbackend, - adPodDurationSec: bidRequest.mediaTypes.video.adPodDurationSec, - durationRangeSec: bidRequest.mediaTypes.video.durationRangeSec, + plcmt: bidRequest.mediaTypes.video.plcmt }; const paramsVideo = bidRequest.params.video; if (paramsVideo !== undefined) { @@ -600,10 +509,6 @@ function buildCdbRequest(context, bidRequests, bidderRequest) { enrichSlotWithFloors(slot, bidRequest); - if (!bidderRequest.fledgeEnabled && slot.ext?.ae) { - delete slot.ext.ae; - } - return slot; }), }; @@ -622,8 +527,6 @@ function buildCdbRequest(context, bidRequests, bidderRequest) { }; request.user = bidderRequest.ortb2?.user || {}; request.site = bidderRequest.ortb2?.site || {}; - request.app = bidderRequest.ortb2?.app || {}; - request.device = bidderRequest.ortb2?.device || {}; if (bidderRequest && bidderRequest.ceh) { request.user.ceh = bidderRequest.ceh; } @@ -698,7 +601,17 @@ function hasValidVideoMediaType(bidRequest) { } }); - return isValid; + if (isValid) { + const videoPlacement = bidRequest.mediaTypes.video.placement || bidRequest.params.video.placement; + // We do not support long form for now, also we have to check that context & placement are consistent + if (bidRequest.mediaTypes.video.context == 'instream' && videoPlacement === 1) { + return true; + } else if (bidRequest.mediaTypes.video.context == 'outstream' && videoPlacement !== 1) { + return true; + } + } + + return false; } /** @@ -855,27 +768,6 @@ function createOutstreamVideoRenderer(slot) { return renderer; } -function getAssociatedBidRequest(bidRequests, slot) { - for (const request of bidRequests) { - if (request.adUnitCode === slot.impid) { - if (request.params.zoneId && parseInt(request.params.zoneId) === slot.zoneid) { - return request; - } else if (slot.native) { - if (request.mediaTypes?.native || request.nativeParams) { - return request; - } - } else if (slot.video) { - if (request.mediaTypes?.video) { - return request; - } - } else if (request.mediaTypes?.banner || request.sizes) { - return request; - } - } - } - return undefined; -} - export function tryGetCriteoFastBid() { // begin ref#1 try { diff --git a/modules/criteoIdSystem.js b/modules/criteoIdSystem.js index 0c42858a0fb..b48612203f8 100644 --- a/modules/criteoIdSystem.js +++ b/modules/criteoIdSystem.js @@ -13,12 +13,6 @@ import { getStorageManager } from '../src/storageManager.js'; import { MODULE_TYPE_UID } from '../src/activities/modules.js'; import { gdprDataHandler, uspDataHandler, gppDataHandler } from '../src/adapterManager.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 - */ - const gvlid = 91; const bidderCode = 'criteo'; export const storage = getStorageManager({ moduleType: MODULE_TYPE_UID, moduleName: bidderCode }); @@ -28,9 +22,6 @@ const bundleStorageKey = 'cto_bundle'; const dnaBundleStorageKey = 'cto_dna_bundle'; const cookiesMaxAge = 13 * 30 * 24 * 60 * 60 * 1000; -const STORAGE_TYPE_LOCALSTORAGE = 'html5'; -const STORAGE_TYPE_COOKIES = 'cookie'; - const pastDateString = new Date(0).toString(); const expirationString = new Date(timestamp() + cookiesMaxAge).toString(); @@ -41,26 +32,14 @@ function extractProtocolHost(url, returnOnlyHost = false) { : `${parsedUrl.protocol}://${parsedUrl.hostname}${parsedUrl.port ? ':' + parsedUrl.port : ''}/`; } -function getFromStorage(submoduleConfig, key) { - if (submoduleConfig?.storage?.type === STORAGE_TYPE_LOCALSTORAGE) { - return storage.getDataFromLocalStorage(key); - } else if (submoduleConfig?.storage?.type === STORAGE_TYPE_COOKIES) { - return storage.getCookie(key); - } - +function getFromAllStorages(key) { return storage.getCookie(key) || storage.getDataFromLocalStorage(key); } -function saveOnStorage(submoduleConfig, key, value, hostname) { +function saveOnAllStorages(key, value, hostname) { if (key && value) { - if (submoduleConfig?.storage?.type === STORAGE_TYPE_LOCALSTORAGE) { - storage.setDataInLocalStorage(key, value); - } else if (submoduleConfig?.storage?.type === STORAGE_TYPE_COOKIES) { - setCookieOnAllDomains(key, value, expirationString, hostname, true); - } else { - storage.setDataInLocalStorage(key, value); - setCookieOnAllDomains(key, value, expirationString, hostname, true); - } + storage.setDataInLocalStorage(key, value); + setCookieOnAllDomains(key, value, expirationString, hostname, true); } } @@ -91,11 +70,11 @@ function deleteFromAllStorages(key, hostname) { storage.removeDataFromLocalStorage(key); } -function getCriteoDataFromStorage(submoduleConfig) { +function getCriteoDataFromAllStorages() { return { - bundle: getFromStorage(submoduleConfig, bundleStorageKey), - dnaBundle: getFromStorage(submoduleConfig, dnaBundleStorageKey), - bidId: getFromStorage(submoduleConfig, bididStorageKey), + bundle: getFromAllStorages(bundleStorageKey), + dnaBundle: getFromAllStorages(dnaBundleStorageKey), + bidId: getFromAllStorages(bididStorageKey), } } @@ -129,7 +108,7 @@ function buildCriteoUsersyncUrl(topUrl, domain, bundle, dnaBundle, areCookiesWri return url; } -function callSyncPixel(submoduleConfig, domain, pixel) { +function callSyncPixel(domain, pixel) { if (pixel.writeBundleInStorage && pixel.bundlePropertyName && pixel.storageKeyName) { ajax( pixel.pixelUrl, @@ -138,7 +117,7 @@ function callSyncPixel(submoduleConfig, domain, pixel) { if (response) { const jsonResponse = JSON.parse(response); if (jsonResponse && jsonResponse[pixel.bundlePropertyName]) { - saveOnStorage(submoduleConfig, pixel.storageKeyName, jsonResponse[pixel.bundlePropertyName], domain); + saveOnAllStorages(pixel.storageKeyName, jsonResponse[pixel.bundlePropertyName], domain); } } }, @@ -154,9 +133,9 @@ function callSyncPixel(submoduleConfig, domain, pixel) { } } -function callCriteoUserSync(submoduleConfig, parsedCriteoData, callback) { - const cw = (submoduleConfig?.storage?.type === undefined || submoduleConfig?.storage?.type === STORAGE_TYPE_COOKIES) && storage.cookiesAreEnabled(); - const lsw = (submoduleConfig?.storage?.type === undefined || submoduleConfig?.storage?.type === STORAGE_TYPE_LOCALSTORAGE) && storage.localStorageIsEnabled(); +function callCriteoUserSync(parsedCriteoData, callback) { + const cw = storage.cookiesAreEnabled(); + const lsw = storage.localStorageIsEnabled(); const topUrl = extractProtocolHost(getRefererInfo().page); // TODO: should domain really be extracted from the current frame? const domain = extractProtocolHost(document.location.href, true); @@ -177,18 +156,18 @@ function callCriteoUserSync(submoduleConfig, parsedCriteoData, callback) { const jsonResponse = JSON.parse(response); if (jsonResponse.pixels) { - jsonResponse.pixels.forEach(pixel => callSyncPixel(submoduleConfig, domain, pixel)); + jsonResponse.pixels.forEach(pixel => callSyncPixel(domain, pixel)); } if (jsonResponse.acwsUrl) { const urlsToCall = typeof jsonResponse.acwsUrl === 'string' ? [jsonResponse.acwsUrl] : jsonResponse.acwsUrl; urlsToCall.forEach(url => triggerPixel(url)); } else if (jsonResponse.bundle) { - saveOnStorage(submoduleConfig, bundleStorageKey, jsonResponse.bundle, domain); + saveOnAllStorages(bundleStorageKey, jsonResponse.bundle, domain); } if (jsonResponse.bidId) { - saveOnStorage(submoduleConfig, bididStorageKey, jsonResponse.bidId, domain); + saveOnAllStorages(bididStorageKey, jsonResponse.bidId, domain); const criteoId = { criteoId: jsonResponse.bidId }; callback(criteoId); } else { @@ -228,21 +207,15 @@ export const criteoIdSubmodule = { * @param {ConsentData} [consentData] * @returns {{id: {criteoId: string} | undefined}}} */ - getId(submoduleConfig) { - let localData = getCriteoDataFromStorage(submoduleConfig); + getId() { + let localData = getCriteoDataFromAllStorages(); - const result = (callback) => callCriteoUserSync(submoduleConfig, localData, callback); + const result = (callback) => callCriteoUserSync(localData, callback); return { id: localData.bidId ? { criteoId: localData.bidId } : undefined, callback: result } - }, - eids: { - 'criteoId': { - source: 'criteo.com', - atype: 1 - }, } }; diff --git a/modules/currency.js b/modules/currency.js index eaed4c50df2..3da0cfe73e8 100644 --- a/modules/currency.js +++ b/modules/currency.js @@ -7,24 +7,29 @@ import {getHook} from '../src/hook.js'; import {defer} from '../src/utils/promise.js'; import {registerOrtbProcessor, REQUEST} from '../src/pbjsORTB.js'; import {timedBidResponseHook} from '../src/utils/perfMetrics.js'; -import {on as onEvent, off as offEvent} from '../src/events.js'; const DEFAULT_CURRENCY_RATE_URL = 'https://cdn.jsdelivr.net/gh/prebid/currency-file@1/latest.json?date=$$TODAY$$'; const CURRENCY_RATE_PRECISION = 4; -let ratesURL; -let bidResponseQueue = []; -let conversionCache = {}; -let currencyRatesLoaded = false; -let needToCallForCurrencyFile = true; -let adServerCurrency = 'USD'; +var bidResponseQueue = []; +var conversionCache = {}; +var currencyRatesLoaded = false; +var needToCallForCurrencyFile = true; +var adServerCurrency = 'USD'; export var currencySupportEnabled = false; export var currencyRates = {}; -let bidderCurrencyDefault = {}; -let defaultRates; +var bidderCurrencyDefault = {}; +var defaultRates; -export let responseReady = defer(); +export const ready = (() => { + let ctl; + function reset() { + ctl = defer(); + } + reset(); + return {done: () => ctl.resolve(), reset, promise: () => ctl.promise} +})(); /** * Configuration function for currency @@ -59,7 +64,7 @@ export let responseReady = defer(); * there is an error loading the config.conversionRateFile. */ export function setConfig(config) { - ratesURL = DEFAULT_CURRENCY_RATE_URL; + let url = DEFAULT_CURRENCY_RATE_URL; if (typeof config.rates === 'object') { currencyRates.conversions = config.rates; @@ -81,14 +86,14 @@ export function setConfig(config) { adServerCurrency = config.adServerCurrency; if (config.conversionRateFile) { logInfo('currency using override conversionRateFile:', config.conversionRateFile); - ratesURL = config.conversionRateFile; + url = config.conversionRateFile; } // see if the url contains a date macro // this is a workaround to the fact that jsdelivr doesn't currently support setting a 24-hour HTTP cache header // So this is an approach to let the browser cache a copy of the file each day // We should remove the macro once the CDN support a day-level HTTP cache setting - const macroLocation = ratesURL.indexOf('$$TODAY$$'); + const macroLocation = url.indexOf('$$TODAY$$'); if (macroLocation !== -1) { // get the date to resolve the macro const d = new Date(); @@ -99,10 +104,10 @@ export function setConfig(config) { const todaysDate = `${d.getFullYear()}${month}${day}`; // replace $$TODAY$$ with todaysDate - ratesURL = `${ratesURL.substring(0, macroLocation)}${todaysDate}${ratesURL.substring(macroLocation + 9, ratesURL.length)}`; + url = `${url.substring(0, macroLocation)}${todaysDate}${url.substring(macroLocation + 9, url.length)}`; } - initCurrency(); + initCurrency(url); } else { // currency support is disabled, setting defaults logInfo('disabling currency support'); @@ -123,11 +128,20 @@ function errorSettingsRates(msg) { } } -function loadRates() { +function initCurrency(url) { + conversionCache = {}; + currencySupportEnabled = true; + + logInfo('Installing addBidResponse decorator for currency module', arguments); + + // Adding conversion function to prebid global for external module and on page use + getGlobal().convertCurrency = (cpm, fromCurrency, toCurrency) => parseFloat(cpm) * getCurrencyConversion(fromCurrency, toCurrency); + getHook('addBidResponse').before(addBidResponseHook, 100); + + // call for the file if we haven't already if (needToCallForCurrencyFile) { needToCallForCurrencyFile = false; - currencyRatesLoaded = false; - ajax(ratesURL, + ajax(url, { success: function (response) { try { @@ -136,45 +150,26 @@ function loadRates() { conversionCache = {}; currencyRatesLoaded = true; processBidResponseQueue(); + ready.done(); } catch (e) { errorSettingsRates('Failed to parse currencyRates response: ' + response); } }, error: function (...args) { errorSettingsRates(...args); - currencyRatesLoaded = true; - processBidResponseQueue(); - needToCallForCurrencyFile = true; + ready.done(); } } ); } else { - processBidResponseQueue(); + ready.done(); } } -function initCurrency() { - conversionCache = {}; - currencySupportEnabled = true; - - logInfo('Installing addBidResponse decorator for currency module', arguments); - - // Adding conversion function to prebid global for external module and on page use - getGlobal().convertCurrency = (cpm, fromCurrency, toCurrency) => parseFloat(cpm) * getCurrencyConversion(fromCurrency, toCurrency); - getHook('addBidResponse').before(addBidResponseHook, 100); - getHook('responsesReady').before(responsesReadyHook); - onEvent(CONSTANTS.EVENTS.AUCTION_TIMEOUT, rejectOnAuctionTimeout); - onEvent(CONSTANTS.EVENTS.AUCTION_INIT, loadRates); - loadRates(); -} - function resetCurrency() { logInfo('Uninstalling addBidResponse decorator for currency module', arguments); getHook('addBidResponse').getHooks({hook: addBidResponseHook}).remove(); - getHook('responsesReady').getHooks({hook: responsesReadyHook}).remove(); - offEvent(CONSTANTS.EVENTS.AUCTION_TIMEOUT, rejectOnAuctionTimeout); - offEvent(CONSTANTS.EVENTS.AUCTION_INIT, loadRates); delete getGlobal().convertCurrency; adServerCurrency = 'USD'; @@ -184,11 +179,6 @@ function resetCurrency() { needToCallForCurrencyFile = true; currencyRates = {}; bidderCurrencyDefault = {}; - responseReady = defer(); -} - -function responsesReadyHook(next, ready) { - next(ready.then(() => responseReady.promise)); } export const addBidResponseHook = timedBidResponseHook('currency', function addBidResponseHook(fn, adUnitCode, bid, reject) { @@ -221,25 +211,24 @@ export const addBidResponseHook = timedBidResponseHook('currency', function addB if (bid.currency === adServerCurrency) { return fn.call(this, adUnitCode, bid, reject); } - bidResponseQueue.push([fn, this, adUnitCode, bid, reject]); + + bidResponseQueue.push(wrapFunction(fn, this, [adUnitCode, bid, reject])); if (!currencySupportEnabled || currencyRatesLoaded) { processBidResponseQueue(); + } else { + fn.untimed.bail(ready.promise()); } }); -function rejectOnAuctionTimeout({auctionId}) { - bidResponseQueue = bidResponseQueue.filter(([fn, ctx, adUnitCode, bid, reject]) => { - if (bid.auctionId === auctionId) { - reject(CONSTANTS.REJECTION_REASON.CANNOT_CONVERT_CURRENCY) - } else { - return true; - } - }); -} - function processBidResponseQueue() { while (bidResponseQueue.length > 0) { - const [fn, ctx, adUnitCode, bid, reject] = bidResponseQueue.shift(); + (bidResponseQueue.shift())(); + } +} + +function wrapFunction(fn, context, params) { + return function() { + let bid = params[1]; if (bid !== undefined && 'currency' in bid && 'cpm' in bid) { let fromCurrency = bid.currency; try { @@ -250,13 +239,12 @@ function processBidResponseQueue() { } } catch (e) { logWarn('getCurrencyConversion threw error: ', e); - reject(CONSTANTS.REJECTION_REASON.CANNOT_CONVERT_CURRENCY); - continue; + params[2](CONSTANTS.REJECTION_REASON.CANNOT_CONVERT_CURRENCY); + return; } } - fn.call(ctx, adUnitCode, bid, reject); - } - responseReady.resolve(); + return fn.apply(context, params); + }; } function getCurrencyConversion(fromCurrency, toCurrency = adServerCurrency) { diff --git a/modules/cwireBidAdapter.js b/modules/cwireBidAdapter.js index d36948d162d..604d7235d0f 100644 --- a/modules/cwireBidAdapter.js +++ b/modules/cwireBidAdapter.js @@ -2,13 +2,6 @@ import {registerBidder} from '../src/adapters/bidderFactory.js'; import {getStorageManager} from '../src/storageManager.js'; import {BANNER} from '../src/mediaTypes.js'; import {generateUUID, getParameterByName, isNumber, logError, logInfo} from '../src/utils.js'; -import {hasPurpose1Consent} from '../src/utils/gpdr.js'; - -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse - */ // ------------------------------------ const BIDDER_CODE = 'cwire'; @@ -237,23 +230,5 @@ export const spec = { navigator.sendBeacon(EVENT_ENDPOINT, JSON.stringify(event)) }, - getUserSyncs: function(syncOptions, serverResponses, gdprConsent, uspConsent) { - logInfo('Collecting user-syncs: ', JSON.stringify({syncOptions, gdprConsent, uspConsent, serverResponses})); - - const syncs = [] - if (hasPurpose1Consent(gdprConsent)) { - logInfo('GDPR purpose 1 consent was given, adding user-syncs') - let type = (syncOptions.pixelEnabled) ? 'image' : null ?? (syncOptions.iframeEnabled) ? 'iframe' : null - if (type) { - syncs.push({ - type: type, - url: 'https://ib.adnxs.com/getuid?https://prebid.cwi.re/v1/cookiesync?xandrId=$UID' - }) - } - } - logInfo('Collected user-syncs: ', JSON.stringify({syncs})) - return syncs - } - }; registerBidder(spec); diff --git a/modules/czechAdIdSystem.js b/modules/czechAdIdSystem.js index 7fdf462183a..957b3ed30bd 100644 --- a/modules/czechAdIdSystem.js +++ b/modules/czechAdIdSystem.js @@ -9,11 +9,6 @@ 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 - * @typedef {import('../modules/userId/index.js').IdResponse} IdResponse - */ - // Returns StorageManager export const storage = getStorageManager({ moduleType: MODULE_TYPE_UID, moduleName: 'czechAdId' }) @@ -47,12 +42,6 @@ export const czechAdIdSubmodule = { getId () { const id = readId() return id ? { id: id } : undefined - }, - eids: { - 'czechAdId': { - source: 'czechadid.cz', - atype: 1 - }, } } diff --git a/modules/dacIdSystem.js b/modules/dacIdSystem.js index ffdadef18e8..5adca074c87 100644 --- a/modules/dacIdSystem.js +++ b/modules/dacIdSystem.js @@ -170,12 +170,6 @@ export const dacIdSystemSubmodule = { } return fetchAoneId(configParams.oid, cookie.fuuid); - }, - eids: { - 'dacId': { - source: 'impact-ad.jp', - atype: 1 - }, } }; diff --git a/modules/datablocksBidAdapter.js b/modules/datablocksBidAdapter.js index 395706994fe..11d3ebb1589 100644 --- a/modules/datablocksBidAdapter.js +++ b/modules/datablocksBidAdapter.js @@ -1,11 +1,10 @@ -import {deepAccess, getWindowTop, isEmpty, isGptPubadsDefined} from '../src/utils.js'; +import {deepAccess, getAdUnitSizes, getWindowTop, isEmpty, 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'; export const storage = getStorageManager({bidderCode: 'datablocks'}); diff --git a/modules/datawrkzBidAdapter.js b/modules/datawrkzBidAdapter.js index db795c89155..2cf28c36330 100644 --- a/modules/datawrkzBidAdapter.js +++ b/modules/datawrkzBidAdapter.js @@ -1,12 +1,4 @@ -import { - deepAccess, - isArray, - getUniqueIdentifierStr, - contains, - isFn, - isPlainObject, - getBidIdParameter -} from '../src/utils.js'; +import { deepAccess, getBidIdParameter, isArray, getUniqueIdentifierStr, contains, isFn, isPlainObject } from '../src/utils.js'; import { config } from '../src/config.js'; import { Renderer } from '../src/Renderer.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; @@ -15,11 +7,6 @@ import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; import CONSTANTS from '../src/constants.json'; import { OUTSTREAM, INSTREAM } from '../src/video.js'; -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - */ - const BIDDER_CODE = 'datawrkz'; const ALIASES = []; const ENDPOINT_URL = 'https://at.datawrkz.com/exchange/openrtb23/'; diff --git a/modules/dchain.js b/modules/dchain.js index 7f84282b81e..daf97a7551f 100644 --- a/modules/dchain.js +++ b/modules/dchain.js @@ -1,7 +1,7 @@ import {includes} from '../src/polyfill.js'; 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 {_each, deepAccess, deepClone, hasOwn, isArray, isPlainObject, isStr, logError, logWarn} from '../src/utils.js'; import {timedBidResponseHook} from '../src/utils/perfMetrics.js'; const shouldBeAString = ' should be a string'; @@ -49,7 +49,7 @@ export function checkDchainSyntax(bid, mode) { appendFailMsg(`dchain.ver` + shouldBeAString); } - if (dchainObj.hasOwnProperty('ext')) { + if (hasOwn(dchainObj, 'ext')) { if (!isPlainObject(dchainObj.ext)) { appendFailMsg(`dchain.ext` + shouldBeAnObject); } diff --git a/modules/deepintentBidAdapter.js b/modules/deepintentBidAdapter.js index 7c24cd6a8f6..e062686b320 100644 --- a/modules/deepintentBidAdapter.js +++ b/modules/deepintentBidAdapter.js @@ -2,7 +2,6 @@ import { generateUUID, deepSetValue, deepAccess, isArray, isInteger, logError, l import {registerBidder} from '../src/adapters/bidderFactory.js'; import {BANNER, VIDEO} from '../src/mediaTypes.js'; const BIDDER_CODE = 'deepintent'; -const GVL_ID = 541; const BIDDER_ENDPOINT = 'https://prebid.deepintent.com/prebid'; const USER_SYNC_URL = 'https://cdn.deepintent.com/syncpixel.html'; const DI_M_V = '1.0.0'; @@ -33,7 +32,6 @@ export const ORTB_VIDEO_PARAMS = { }; export const spec = { code: BIDDER_CODE, - gvlid: GVL_ID, supportedMediaTypes: [BANNER, VIDEO], aliases: [], diff --git a/modules/deepintentDpesIdSystem.js b/modules/deepintentDpesIdSystem.js index 2d3eae980cd..f2b50d535eb 100644 --- a/modules/deepintentDpesIdSystem.js +++ b/modules/deepintentDpesIdSystem.js @@ -9,12 +9,6 @@ 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 - * @typedef {import('../modules/userId/index.js').SubmoduleConfig} SubmoduleConfig - * @typedef {import('../modules/userId/index.js').ConsentData} ConsentData - */ - const MODULE_NAME = 'deepintentId'; export const storage = getStorageManager({moduleType: MODULE_TYPE_UID, moduleName: MODULE_NAME}); @@ -45,13 +39,8 @@ export const deepintentDpesSubmodule = { */ getId(config, consentData, cacheIdObj) { return cacheIdObj; - }, - eids: { - 'deepintentId': { - source: 'deepintent.com', - atype: 3 - }, - }, + } + }; submodule('userId', deepintentDpesSubmodule); diff --git a/modules/deltaprojectsBidAdapter.js b/modules/deltaprojectsBidAdapter.js index 870378a13dd..c66e381b8f1 100644 --- a/modules/deltaprojectsBidAdapter.js +++ b/modules/deltaprojectsBidAdapter.js @@ -16,7 +16,7 @@ export const BIDDER_CODE = 'deltaprojects'; export const BIDDER_ENDPOINT_URL = 'https://d5p.de17a.com/dogfight/prebid'; export const USERSYNC_URL = 'https://userservice.de17a.com/getuid/prebid'; -/** -- isBidRequestValid -- */ +/** -- isBidRequestValid --**/ function isBidRequestValid(bid) { if (!bid) return false; @@ -32,9 +32,9 @@ function isBidRequestValid(bid) { return true; } -/** -- Build requests -- */ +/** -- Build requests --**/ function buildRequests(validBidRequests, bidderRequest) { - /** == shared == */ + /** == shared ==**/ // -- build id const id = bidderRequest.bidderRequestId; @@ -146,7 +146,7 @@ function buildImpressionBanner(bid, bannerMediaType) { }; } -/** -- Interpret response -- */ +/** -- Interpret response --**/ function interpretResponse(serverResponse) { if (!serverResponse.body) { logWarn('Response body is invalid, return !!'); @@ -189,7 +189,7 @@ function interpretResponse(serverResponse) { return bidResponses; } -/** -- On Bid Won -- */ +/** -- On Bid Won -- **/ function onBidWon(bid) { let cpm = bid.cpm; if (bid.currency && bid.currency !== bid.originalCurrency && typeof bid.getCpmInNewCurrency === 'function') { @@ -200,7 +200,7 @@ function onBidWon(bid) { bid.ad = bid.ad.replace(wonPriceMacroPatten, wonPrice); } -/** -- Get user syncs -- */ +/** -- Get user syncs --**/ function getUserSyncs(syncOptions, serverResponses, gdprConsent) { const syncs = [] @@ -223,7 +223,7 @@ function getUserSyncs(syncOptions, serverResponses, gdprConsent) { return syncs; } -/** -- Get bid floor -- */ +/** -- Get bid floor --**/ export function getBidFloor(bid, mediaType, size, currency) { if (isFn(bid.getFloor)) { const bidFloorCurrency = currency || 'USD'; @@ -234,7 +234,7 @@ export function getBidFloor(bid, mediaType, size, currency) { } } -/** -- Helper methods -- */ +/** -- Helper methods --**/ function setOnAny(collection, key) { for (let i = 0, result; i < collection.length; i++) { result = deepAccess(collection[i], key); diff --git a/modules/dfpAdServerVideo.js b/modules/dfpAdServerVideo.js index 7f275992210..3394fd8b3f4 100644 --- a/modules/dfpAdServerVideo.js +++ b/modules/dfpAdServerVideo.js @@ -2,28 +2,17 @@ * This module adds [DFP support]{@link https://www.doubleclickbygoogle.com/} for Video to Prebid. */ -import {registerVideoSupport} from '../src/adServerManager.js'; -import {targeting} from '../src/targeting.js'; -import { - isNumber, - buildUrl, - deepAccess, - formatQS, - isEmpty, - logError, - parseSizesInput, - parseUrl, - uniques -} from '../src/utils.js'; -import {config} from '../src/config.js'; -import {getHook, submodule} from '../src/hook.js'; -import {auctionManager} from '../src/auctionManager.js'; -import {gdprDataHandler} from '../src/adapterManager.js'; +import { registerVideoSupport } from '../src/adServerManager.js'; +import { targeting } from '../src/targeting.js'; +import { deepAccess, isEmpty, logError, parseSizesInput, formatQS, parseUrl, buildUrl } from '../src/utils.js'; +import { config } from '../src/config.js'; +import { getHook, submodule } from '../src/hook.js'; +import { auctionManager } from '../src/auctionManager.js'; +import { gdprDataHandler, uspDataHandler, gppDataHandler } from '../src/adapterManager.js'; import * as events from '../src/events.js'; import CONSTANTS from '../src/constants.json'; import {getPPID} from '../src/adserver.js'; import {getRefererInfo} from '../src/refererDetection.js'; -import {CLIENT_SECTIONS} from '../src/fpd/oneClient.js'; /** * @typedef {Object} DfpVideoParams @@ -124,6 +113,7 @@ export function buildDfpVideoUrl(options) { const descriptionUrl = getDescriptionUrl(bid, options, 'params'); if (descriptionUrl) { queryParams.description_url = descriptionUrl; } + const gdprConsent = gdprDataHandler.getConsentData(); if (gdprConsent) { if (typeof gdprConsent.gdprApplies === 'boolean') { queryParams.gdpr = Number(gdprConsent.gdprApplies); } @@ -131,6 +121,14 @@ export function buildDfpVideoUrl(options) { if (gdprConsent.addtlConsent) { queryParams.addtl_consent = gdprConsent.addtlConsent; } } + 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... + } + if (!queryParams.ppid) { const ppid = getPPID(); if (ppid != null) { @@ -138,70 +136,6 @@ export function buildDfpVideoUrl(options) { } } - const video = options.adUnit?.mediaTypes?.video; - Object.entries({ - plcmt: () => video?.plcmt, - min_ad_duration: () => isNumber(video?.minduration) ? video.minduration * 1000 : null, - max_ad_duration: () => isNumber(video?.maxduration) ? video.maxduration * 1000 : null, - vpos() { - const startdelay = video?.startdelay; - if (isNumber(startdelay)) { - if (startdelay === -2) return 'postroll'; - if (startdelay === -1 || startdelay > 0) return 'midroll'; - return 'preroll'; - } - }, - vconp: () => Array.isArray(video?.playbackmethod) && video.playbackmethod.every(m => m === 7) ? '2' : undefined, - vpa() { - // playbackmethod = 3 is play on click; 1, 2, 4, 5, 6 are autoplay - if (Array.isArray(video?.playbackmethod)) { - const click = video.playbackmethod.some(m => m === 3); - const auto = video.playbackmethod.some(m => [1, 2, 4, 5, 6].includes(m)); - if (click && !auto) return 'click'; - if (auto && !click) return 'auto'; - } - }, - vpmute() { - // playbackmethod = 2, 6 are muted; 1, 3, 4, 5 are not - if (Array.isArray(video?.playbackmethod)) { - const muted = video.playbackmethod.some(m => [2, 6].includes(m)); - const talkie = video.playbackmethod.some(m => [1, 3, 4, 5].includes(m)); - if (muted && !talkie) return '1'; - if (talkie && !muted) return '0'; - } - } - }).forEach(([param, getter]) => { - if (!queryParams.hasOwnProperty(param)) { - const val = getter(); - if (val != null) { - queryParams[param] = val; - } - } - }); - const fpd = auctionManager.index.getBidRequest(options.bid || {})?.ortb2 ?? - auctionManager.index.getAuction(options.bid || {})?.getFPD()?.global; - - function getSegments(sections, segtax) { - return sections - .flatMap(section => deepAccess(fpd, section) || []) - .filter(datum => datum.ext?.segtax === segtax) - .flatMap(datum => datum.segment?.map(seg => seg.id)) - .filter(ob => ob) - .filter(uniques) - } - - const signals = Object.entries({ - IAB_AUDIENCE_1_1: getSegments(['user.data'], 4), - IAB_CONTENT_2_2: getSegments(CLIENT_SECTIONS.map(section => `${section}.content.data`), 6) - }).map(([taxonomy, values]) => values.length ? {taxonomy, values} : null) - .filter(ob => ob); - - if (signals.length) { - queryParams.ppsj = btoa(JSON.stringify({ - PublisherProvidedTaxonomySignals: signals - })) - } - return buildUrl(Object.assign({ protocol: 'https', host: 'securepubads.g.doubleclick.net', @@ -230,8 +164,6 @@ if (config.getConfig('brandCategoryTranslation.translationFile')) { getHook('reg * @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.dfp.buildAdpodVideoUrl.html if (!params || !callback) { logError(`A params object and a callback is required to use pbjs.adServers.dfp.buildAdpodVideoUrl`); return; @@ -293,6 +225,9 @@ export function buildAdpodVideoUrl({code, params, callback} = {}) { if (gdprConsent.addtlConsent) { queryParams.addtl_consent = gdprConsent.addtlConsent; } } + const uspConsent = uspDataHandler.getConsentData(); + if (uspConsent) { queryParams.us_privacy = uspConsent; } + const masterTag = buildUrl({ protocol: 'https', host: 'securepubads.g.doubleclick.net', @@ -314,9 +249,7 @@ export function buildAdpodVideoUrl({code, params, callback} = {}) { */ function buildUrlFromAdserverUrlComponents(components, bid, options) { const descriptionUrl = getDescriptionUrl(bid, components, 'search'); - if (descriptionUrl) { - components.search.description_url = descriptionUrl; - } + if (descriptionUrl) { components.search.description_url = descriptionUrl; } components.search.cust_params = getCustParams(bid, options, components.search.cust_params); return buildUrl(components); @@ -331,7 +264,7 @@ function buildUrlFromAdserverUrlComponents(components, bid, options) { * @return {string | undefined} The encoded vast url if it exists, or undefined */ function getDescriptionUrl(bid, components, prop) { - return deepAccess(components, `${prop}.description_url`) || encodeURIComponent(dep.ri().page); + return deepAccess(components, `${prop}.description_url`) || dep.ri().page; } /** diff --git a/modules/dgkeywordRtdProvider.js b/modules/dgkeywordRtdProvider.js index 14519ae2713..99df3b18a39 100644 --- a/modules/dgkeywordRtdProvider.js +++ b/modules/dgkeywordRtdProvider.js @@ -6,15 +6,12 @@ * @module modules/dgkeywordProvider * @requires module:modules/realTimeData */ -import { logMessage, deepSetValue, logError, logInfo, isStr, isArray } from '../src/utils.js'; + +import {logMessage, deepSetValue, logError, logInfo, mergeDeep} from '../src/utils.js'; import { ajax } from '../src/ajax.js'; import { submodule } from '../src/hook.js'; import { getGlobal } from '../src/prebidGlobal.js'; -/** - * @typedef {import('../modules/rtdModule/index.js').RtdSubmodule} RtdSubmodule - */ - /** * get keywords from api server. and set keywords. * @param {Object} reqBidsConfigObj @@ -60,12 +57,20 @@ export function getDgKeywordsAndSet(reqBidsConfigObj, callback, moduleConfig, us if (Object.keys(keywords).length > 0) { const targetBidKeys = {}; for (let bid of setKeywordTargetBidders) { - // set keywords to ortb2Imp - deepSetValue(bid, 'ortb2Imp.ext.data.keywords', convertKeywordsToString(keywords)); + // set keywords to params + bid.params.keywords = keywords; if (!targetBidKeys[bid.bidder]) { targetBidKeys[bid.bidder] = true; } } + + if (!reqBidsConfigObj._ignoreSetOrtb2) { + // set keywrods to ortb2 + let addOrtb2 = {}; + deepSetValue(addOrtb2, 'site.keywords', keywords); + deepSetValue(addOrtb2, 'user.keywords', keywords); + mergeDeep(reqBidsConfigObj.ortb2Fragments.bidder, Object.fromEntries(Object.keys(targetBidKeys).map(bidder => [bidder, addOrtb2]))); + } } } isFinish = true; @@ -151,37 +156,4 @@ function init(moduleConfig) { function registerSubModule() { submodule('realTimeData', dgkeywordSubmodule); } - -// keywords: { 'genre': ['rock', 'pop'], 'pets': ['dog'] } goes to 'genre=rock,genre=pop,pets=dog' -export function convertKeywordsToString(keywords) { - let result = ''; - Object.keys(keywords).forEach(key => { - // if 'text' or '' - if (isStr(keywords[key])) { - if (keywords[key] !== '') { - result += `${key}=${keywords[key]},` - } else { - result += `${key},`; - } - } else if (isArray(keywords[key])) { - let isValSet = false - keywords[key].forEach(val => { - if (isStr(val) && val) { - result += `${key}=${val},` - isValSet = true - } - }); - if (!isValSet) { - result += `${key},` - } - } else { - result += `${key},` - } - }); - - // remove last trailing comma - result = result.substring(0, result.length - 1); - return result; -} - registerSubModule(); diff --git a/modules/discoveryBidAdapter.js b/modules/discoveryBidAdapter.js index ad8f5616d44..675ede273a6 100644 --- a/modules/discoveryBidAdapter.js +++ b/modules/discoveryBidAdapter.js @@ -3,27 +3,17 @@ import { getStorageManager } from '../src/storageManager.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER, NATIVE } from '../src/mediaTypes.js'; -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse - */ - 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}); +const storage = getStorageManager({bidderCode: BIDDER_CODE}); let globals = {}; let itemMaps = {}; const MEDIATYPE = [BANNER, NATIVE]; /* ----- _ss_pp_id:start ------ */ const COOKIE_KEY_SSPPID = '_ss_pp_id'; -export const COOKIE_KEY_MGUID = '__mguid_'; -const COOKIE_KEY_PMGUID = '__pmguid_'; -const COOKIE_RETENTION_TIME = 365 * 24 * 60 * 60 * 1000; // 1 year -const COOKY_SYNC_IFRAME_URL = 'https://asset.popin.cc/js/cookieSync.html'; -export const THIRD_PARTY_COOKIE_ORIGIN = 'https://asset.popin.cc'; +const COOKIE_KEY_MGUID = '__mguid_'; const NATIVERET = { id: 'id', @@ -65,79 +55,24 @@ const NATIVERET = { }; /** - * get page title - * @returns {string} - */ - -export function getPageTitle(win = window) { - try { - const ogTitle = win.top.document.querySelector('meta[property="og:title"]') - return win.top.document.title || (ogTitle && ogTitle.content) || ''; - } catch (e) { - const ogTitle = document.querySelector('meta[property="og:title"]') - return document.title || (ogTitle && ogTitle.content) || ''; - } -} - -/** - * get page description - * @returns {string} - */ -export function getPageDescription(win = window) { - let element; - - try { - element = win.top.document.querySelector('meta[name="description"]') || - win.top.document.querySelector('meta[property="og:description"]') - } catch (e) { - element = document.querySelector('meta[name="description"]') || - document.querySelector('meta[property="og:description"]') - } - - return (element && element.content) || ''; -} - -/** - * get page keywords - * @returns {string} - */ -export function getPageKeywords(win = window) { - let element; - - try { - element = win.top.document.querySelector('meta[name="keywords"]'); - } catch (e) { - element = document.querySelector('meta[name="keywords"]'); - } - - return (element && element.content) || ''; -} - -/** - * get connection downlink - * @returns {number} - */ -export function getConnectionDownLink(win = window) { - const nav = win.navigator || {}; - return nav && nav.connection && nav.connection.downlink >= 0 ? nav.connection.downlink.toString() : undefined; -} - -/** - * get pmg uid - * čŽˇå–åšļį”Ÿæˆį”¨æˆˇįš„id + * čŽˇå–į”¨æˆˇid * @return {string} */ -export const getPmgUID = () => { - if (!storage.cookiesAreEnabled()) return; - - let pmgUid = storage.getCookie(COOKIE_KEY_PMGUID); - if (!pmgUid) { - pmgUid = utils.generateUUID(); - try { - storage.setCookie(COOKIE_KEY_PMGUID, pmgUid, getCurrentTimeToUTCString()); - } catch (e) {} +const getUserID = () => { + let idd = storage.getCookie(COOKIE_KEY_SSPPID); + let idm = storage.getCookie(COOKIE_KEY_MGUID); + + if (idd && !idm) { + idm = idd; + } else if (idm && !idd) { + idd = idm; + } else if (!idd && !idm) { + const uuid = utils.generateUUID(); + storage.setCookie(COOKIE_KEY_MGUID, uuid); + storage.setCookie(COOKIE_KEY_SSPPID, uuid); + return uuid; } - return pmgUid; + return idd; }; /* ----- _ss_pp_id:end ------ */ @@ -276,74 +211,6 @@ const popInAdSize = [ { w: 336, h: 280 }, ]; -/** - * get screen size - * - * @returns {Array} eg: "['widthxheight']" - */ -function getScreenSize() { - return utils.parseSizesInput([window.screen.width, window.screen.height]); -} - -/** - * @param {BidRequest} bidRequest - * @param bidderRequest - * @returns {string} - */ -function getReferrer(bidRequest = {}, bidderRequest = {}) { - let pageUrl; - if (bidRequest.params && bidRequest.params.referrer) { - pageUrl = bidRequest.params.referrer; - } else { - pageUrl = utils.deepAccess(bidderRequest, 'refererInfo.page'); - } - return pageUrl; -} - -/** - * get current time to UTC string - * @returns utc string - */ -export function getCurrentTimeToUTCString() { - const date = new Date(); - date.setTime(date.getTime() + COOKIE_RETENTION_TIME); - return date.toUTCString(); -} - -/** - * format imp ad test ext params - * - * @param validBidRequest sigleBidRequest - * @param bidderRequest - */ -function addImpExtParams(bidRequest = {}, bidderRequest = {}) { - const { deepAccess } = utils; - const { params = {}, adUnitCode, bidId } = bidRequest; - const ext = { - bidId: bidId || '', - adUnitCode: adUnitCode || '', - token: params.token || '', - siteId: params.siteId || '', - zoneId: params.zoneId || '', - publisher: params.publisher || '', - p_pos: params.position || '', - screenSize: getScreenSize(), - referrer: getReferrer(bidRequest, bidderRequest), - stack: deepAccess(bidRequest, 'refererInfo.stack', []), - b_pos: deepAccess(bidRequest, 'mediaTypes.banner.pos', '', ''), - ortbUser: deepAccess(bidRequest, 'ortb2.user', {}, {}), - ortbSite: deepAccess(bidRequest, 'ortb2.site', {}, {}), - tid: deepAccess(bidRequest, 'ortb2Imp.ext.tid', '', ''), - browsiViewability: deepAccess(bidRequest, 'ortb2Imp.ext.data.browsi.browsiViewability', '', ''), - adserverName: deepAccess(bidRequest, 'ortb2Imp.ext.data.adserver.name', '', ''), - adslot: deepAccess(bidRequest, 'ortb2Imp.ext.data.adserver.adslot', '', ''), - keywords: deepAccess(bidRequest, 'ortb2Imp.ext.data.keywords', '', ''), - gpid: deepAccess(bidRequest, 'ortb2Imp.ext.gpid', '', ''), - pbadslot: deepAccess(bidRequest, 'ortb2Imp.ext.data.pbadslot', '', ''), - }; - return ext; -} - /** * get aditem setting * @param {Array} validBidRequests an an array of bids @@ -391,14 +258,9 @@ function getItems(validBidRequests, bidderRequest) { format: sizes, }, ext: {}, - tagid: req.params && req.params.tagid + tagid: globals['tagid'], }; } - - try { - ret.ext = addImpExtParams(req, bidderRequest); - } catch (e) {} - itemMaps[id] = { req, ret, @@ -436,10 +298,6 @@ function getParam(validBidRequests, bidderRequest) { const page = utils.deepAccess(bidderRequest, 'refererInfo.page'); const referer = utils.deepAccess(bidderRequest, 'refererInfo.ref'); const firstPartyData = bidderRequest.ortb2; - const topWindow = window.top; - const title = getPageTitle(); - const desc = getPageDescription(); - const keywords = getPageKeywords(); if (items && items.length) { let c = { @@ -460,24 +318,12 @@ function getParam(validBidRequests, bidderRequest) { ext: { eids, firstPartyData, - ssppid: storage.getCookie(COOKIE_KEY_SSPPID) || undefined, - pmguid: getPmgUID(), - page: { - title: title ? title.slice(0, 100) : undefined, - desc: desc ? desc.slice(0, 300) : undefined, - keywords: keywords ? keywords.slice(0, 100) : undefined, - hLen: topWindow.history?.length || undefined, - }, - device: { - nbw: getConnectionDownLink(), - hc: topWindow.navigator?.hardwareConcurrency || undefined, - dm: topWindow.navigator?.deviceMemory || undefined, - } }, user: { - buyeruid: storage.getCookie(COOKIE_KEY_MGUID) || undefined, + buyeruid: getUserID(), id: sharedid || pubcid, }, + eids, tmax: timeout, site: { name: domain, @@ -526,7 +372,7 @@ export const spec = { if (bid.params.badv) { globals['badv'] = Array.isArray(bid.params.badv) ? bid.params.badv : []; } - return true; + return !!(bid.params.token && bid.params.publisher && bid.params.tagid); }, /** @@ -537,8 +383,6 @@ export const spec = { * @return ServerRequest Info describing the request to the server. */ buildRequests: function (validBidRequests, bidderRequest) { - if (!globals['token']) return; - let payload = getParam(validBidRequests, bidderRequest); const payloadString = JSON.stringify(payload); @@ -641,45 +485,6 @@ export const spec = { return bidResponses; }, - getUserSyncs: function (syncOptions, serverResponse, gdprConsent, uspConsent, gppConsent) { - const origin = encodeURIComponent(location.origin || `https://${location.host}`); - let syncParamUrl = `dm=${origin}`; - - if (gdprConsent && gdprConsent.consentString) { - if (typeof gdprConsent.gdprApplies === 'boolean') { - syncParamUrl += `&gdpr=${Number(gdprConsent.gdprApplies)}&gdpr_consent=${gdprConsent.consentString}`; - } else { - syncParamUrl += `&gdpr=0&gdpr_consent=${gdprConsent.consentString}`; - } - } - if (uspConsent && uspConsent.consentString) { - syncParamUrl += `&ccpa_consent=${uspConsent.consentString}`; - } - - if (syncOptions.iframeEnabled) { - window.addEventListener('message', function handler(event) { - if (!event.data || event.origin != THIRD_PARTY_COOKIE_ORIGIN) { - return; - } - - this.removeEventListener('message', handler); - - event.stopImmediatePropagation(); - - const response = event.data; - if (!response.optout && response.mguid) { - storage.setCookie(COOKIE_KEY_MGUID, response.mguid, getCurrentTimeToUTCString()); - } - }, true); - return [ - { - type: 'iframe', - url: `${COOKY_SYNC_IFRAME_URL}?${syncParamUrl}` - } - ]; - } - }, - /** * Register bidder specific code, which will execute if bidder timed out after an auction * @param {data} Containing timeout specific data diff --git a/modules/displayioBidAdapter.js b/modules/displayioBidAdapter.js index 3cdfd3a77cd..3d34f2c8b26 100644 --- a/modules/displayioBidAdapter.js +++ b/modules/displayioBidAdapter.js @@ -1,7 +1,7 @@ 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 {getWindowFromDocument, logWarn} from '../src/utils.js'; import {getStorageManager} from '../src/storageManager.js'; import {getAllOrtbKeywords} from '../libraries/keywords/keywords.js'; @@ -156,7 +156,7 @@ function newRenderer(bid) { function webisRender(bid, doc) { bid.renderer.push(() => { - const win = doc?.defaultView || window; + const win = getWindowFromDocument(doc) || window; win.webis.init(bid.adData, bid.adUnitCode, bid.params); }) } diff --git a/modules/dmdIdSystem.js b/modules/dmdIdSystem.js index 3575e658a2a..b42315d66ee 100644 --- a/modules/dmdIdSystem.js +++ b/modules/dmdIdSystem.js @@ -9,13 +9,6 @@ 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} */ @@ -92,12 +85,6 @@ export const dmdIdSubmodule = { }; return { callback: resp }; } - }, - eids: { - 'dmdId': { - source: 'hcn.health', - atype: 3 - }, } }; diff --git a/modules/docereeAdManagerBidAdapter.js b/modules/docereeAdManagerBidAdapter.js deleted file mode 100644 index d3765f5a130..00000000000 --- a/modules/docereeAdManagerBidAdapter.js +++ /dev/null @@ -1,128 +0,0 @@ -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { config } from '../src/config.js'; -import { BANNER } from '../src/mediaTypes.js'; -const BIDDER_CODE = 'docereeadmanager'; -const END_POINT = 'https://dai.doceree.com/drs/quest'; - -export const spec = { - code: BIDDER_CODE, - url: '', - supportedMediaTypes: [BANNER], - - isBidRequestValid: (bid) => { - const { placementId } = bid.params; - return !!placementId; - }, - isGdprConsentPresent: (bid) => { - const { gdpr, gdprconsent } = bid.params; - if (gdpr == '1') { - return !!gdprconsent; - } - return true; - }, - buildRequests: (validBidRequests) => { - const serverRequests = []; - const { data } = config.getConfig('docereeadmanager.user') || {}; - - validBidRequests.forEach(function (validBidRequest) { - const payload = getPayload(validBidRequest, data); - - if (!payload) { - return; - } - - serverRequests.push({ - method: 'POST', - url: END_POINT, - data: JSON.stringify(payload.data), - options: { - contentType: 'application/json', - withCredentials: true, - }, - }); - }); - - return serverRequests; - }, - interpretResponse: (serverResponse) => { - const responseJson = serverResponse ? serverResponse.body : {}; - const bidResponse = { - ad: responseJson.ad, - width: Number(responseJson.width), - height: Number(responseJson.height), - requestId: responseJson.requestId, - netRevenue: true, - ttl: 30, - cpm: responseJson.cpm, - currency: responseJson.currency, - mediaType: BANNER, - creativeId: responseJson.creativeId, - meta: { - advertiserDomains: - Array.isArray(responseJson.meta.advertiserDomains) && - responseJson.meta.advertiserDomains.length > 0 - ? responseJson.meta.advertiserDomains - : [], - }, - }; - - return [bidResponse]; - }, -}; - -function getPayload(bid, userData) { - if (!userData || !bid) { - return false; - } - - const { bidId, params } = bid; - const { placementId } = params; - const { - userid, - email, - firstname, - lastname, - specialization, - hcpid, - gender, - city, - state, - zipcode, - hashedNPI, - hashedhcpid, - hashedemail, - hashedmobile, - country, - organization, - dob, - } = userData; - - const data = { - userid: userid || '', - email: email || '', - firstname: firstname || '', - lastname: lastname || '', - specialization: specialization || '', - hcpid: hcpid || '', - gender: gender || '', - city: city || '', - state: state || '', - zipcode: zipcode || '', - hashedNPI: hashedNPI || '', - pb: 1, - adunit: placementId || '', - requestId: bidId || '', - hashedhcpid: hashedhcpid || '', - hashedemail: hashedemail || '', - hashedmobile: hashedmobile || '', - country: country || '', - organization: organization || '', - dob: dob || '', - userconsent: 1, - }; - return { - data, - }; -} - -registerBidder(spec); diff --git a/modules/docereeAdManagerBidAdapter.md b/modules/docereeAdManagerBidAdapter.md deleted file mode 100644 index bedbf57b179..00000000000 --- a/modules/docereeAdManagerBidAdapter.md +++ /dev/null @@ -1,68 +0,0 @@ -# Overview - -``` -Module Name: Doceree AdManager Bidder Adapter -Module Type: Bidder Adapter -Maintainer: tech.stack@doceree.com -``` - - - -Connects to Doceree demand source to fetch bids. -Please use `docereeadmanager` as the bidder code. - -# Test Parameters - -``` -var adUnits = [ - { - code: 'DOC-397-1', - sizes: [ - [300, 250] - ], - bids: [ - { - bidder: 'docereeadmanager', - params: { - placementId: 'DOC-19-1', //required - publisherUrl: document.URL || window.location.href, //optional - gdpr: '1', //optional - gdprconsent:'CPQfU1jPQfU1jG0AAAENAwCAAAAAAAAAAAAAAAAAAAAA.IGLtV_T9fb2vj-_Z99_tkeYwf95y3p-wzhheMs-8NyZeH_B4Wv2MyvBX4JiQKGRgksjLBAQdtHGlcTQgBwIlViTLMYk2MjzNKJrJEilsbO2dYGD9Pn8HT3ZCY70-vv__7v3ff_3g', //optional - } - } - ] - } -]; -``` - -```javascript -pbjs.setBidderConfig({ - bidders: ['docereeadmanager'], - config: { - docereeadmanager: { - user: { - data: { - email: 'XXX.XXX@GMAIL.COM', - firstname: 'DR. XXX', - lastname: 'XXX', - mobile: '981234XXXX', - specialization: 'Internal Medicine', - organization: 'Max Lifecare', - hcpid: '199291XXXX', - dob: '1987-08-27', - gender: 'Female', - city: 'Oildale', - state: 'California', - country: 'California', - hashedhcpid: '', - hashedemail: '', - hashedmobile: '', - userid: '7d26d8ca-233a-46c2-9d36-7c5d261e151d', - zipcode: '', - userconsent: '1', - }, - }, - }, - }, -}); -``` diff --git a/modules/docereeBidAdapter.js b/modules/docereeBidAdapter.js index 2731e1ff397..524f464cee3 100644 --- a/modules/docereeBidAdapter.js +++ b/modules/docereeBidAdapter.js @@ -1,11 +1,9 @@ +import { tryAppendQueryString } from '../src/utils.js'; 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'; const BIDDER_CODE = 'doceree'; const END_POINT = 'https://bidder.doceree.com' -const TRACKING_END_POINT = 'https://tracking.doceree.com' export const spec = { code: BIDDER_CODE, @@ -71,33 +69,6 @@ export const spec = { } }; return [bidResponse]; - }, - onTimeout: function(timeoutData) { - if (timeoutData == null || !timeoutData.length) { - return; - } - timeoutData.forEach(td => { - const encodedBuf = window.btoa(encodeURIComponent(JSON.stringify({ - bidId: td.bidId, - timeout: td.timeout, - }))); - triggerPixel(TRACKING_END_POINT + '/v1/hbTimeout?adp=prebidjs&data=' + encodedBuf); - }) - }, - onBidWon: function (bidWon) { - if (bidWon == null) { - return; - } - const encodedBuf = window.btoa(encodeURIComponent(JSON.stringify({ - requestId: bidWon.requestId, - cpm: bidWon.cpm, - adId: bidWon.adId, - currency: bidWon.currency, - netRevenue: bidWon.netRevenue, - status: bidWon.status, - hb_pb: bidWon.adserverTargeting && bidWon.adserverTargeting.hb_pb, - }))); - triggerPixel(TRACKING_END_POINT + '/v1/hbBidWon?adp=prebidjs&data=' + encodedBuf); } }; diff --git a/modules/dsp_genieeBidAdapter.js b/modules/dsp_genieeBidAdapter.js deleted file mode 100644 index 17865e6793a..00000000000 --- a/modules/dsp_genieeBidAdapter.js +++ /dev/null @@ -1,123 +0,0 @@ -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { BANNER } from '../src/mediaTypes.js'; -import { ortbConverter } from '../libraries/ortbConverter/converter.js'; -import { deepAccess, deepSetValue } from '../src/utils.js'; -import { config } from '../src/config.js'; -const BIDDER_CODE = 'dsp_geniee'; -const ENDPOINT_URL = 'https://rt.gsspat.jp/prebid_auction'; -const ENDPOINT_URL_UNCOMFORTABLE = 'https://rt.gsspat.jp/prebid_uncomfortable'; -const ENDPOINT_USERSYNC = 'https://rt.gsspat.jp/prebid_cs'; -const VALID_CURRENCIES = ['USD', 'JPY']; -const converter = ortbConverter({ - context: { ttl: 300, netRevenue: true }, - // set optional parameters - imp(buildImp, bidRequest, context) { - const imp = buildImp(bidRequest, context); - deepSetValue(imp, 'ext', bidRequest.params); - return imp; - } -}); - -function USPConsent(consent) { - return typeof consent === 'string' && consent[0] === '1' && consent.toUpperCase()[2] === 'Y'; -} - -function invalidCurrency(currency) { - return typeof currency === 'string' && VALID_CURRENCIES.indexOf(currency.toUpperCase()) === -1; -} - -function hasTest(imp) { - if (typeof imp !== 'object') { - return false; - } - for (let i = 0; i < imp.length; i++) { - if (deepAccess(imp[i], 'ext.test') === 1) { - return true; - } - } - return false; -} - -export const spec = { - code: BIDDER_CODE, - supportedMediaTypes: [BANNER], - /** - * Determines whether or not the given bid request is valid. - * - * @param {BidRequest} - The bid params to validate. - * @return boolean True if this is a valid bid, and false otherwise. - */ - isBidRequestValid: function (_) { - return true; - }, - /** - * Make a server request from the list of BidRequests. - * - * @param {validBidRequests[]} - an array of bids - * @param {bidderRequest} - the master bidRequest object - * @return ServerRequest Info describing the request to the server. - */ - buildRequests: function (validBidRequests, bidderRequest) { - if (deepAccess(bidderRequest, 'gdprConsent.gdprApplies') || // gdpr - USPConsent(bidderRequest.uspConsent) || // usp - config.getConfig('coppa') || // coppa - invalidCurrency(config.getConfig('currency.adServerCurrency')) // currency validation - ) { - return { - method: 'GET', - url: ENDPOINT_URL_UNCOMFORTABLE - }; - } - - const payload = converter.toORTB({ validBidRequests, bidderRequest }); - - if (hasTest(deepAccess(payload, 'imp'))) { - deepSetValue(payload, 'test', 1); - } - - deepSetValue(payload, 'at', 1); // first price auction only - - return { - method: 'POST', - url: ENDPOINT_URL, - data: payload - }; - }, - /** - * Unpack the response from the server into a list of bids. - * - * @param {ServerResponse} serverResponse A successful response from the server. - * @param {BidRequest} bidRequest - the master bidRequest object - * @return {bids} - An array of bids which were nested inside the server. - */ - interpretResponse: function (serverResponse, bidRequest) { - if (!serverResponse.body) { // empty response (no bids) - return []; - } - const bids = converter.fromORTB({ response: serverResponse.body, request: bidRequest.data }).bids; - return bids; - }, - - /** - * Register the user sync pixels which should be dropped after the auction. - * - * @param {SyncOptions} syncOptions Which user syncs are allowed? - * @param {ServerResponse[]} serverResponses List of server's responses. - * @return {UserSync[]} The user syncs which should be dropped. - */ - getUserSyncs: function (syncOptions, serverResponses, gdprConsent, uspConsent) { - const syncs = []; - // gdpr & usp - if (deepAccess(gdprConsent, 'gdprApplies') || USPConsent(uspConsent)) { - return syncs; - } - if (syncOptions.pixelEnabled) { - syncs.push({ - type: 'image', - url: ENDPOINT_USERSYNC - }); - } - return syncs; - } -}; -registerBidder(spec); diff --git a/modules/dsp_genieeBidAdapter.md b/modules/dsp_genieeBidAdapter.md deleted file mode 100644 index d51d66884af..00000000000 --- a/modules/dsp_genieeBidAdapter.md +++ /dev/null @@ -1,39 +0,0 @@ -# Overview - -```markdown -Module Name: Geniee Bid Adapter -Module Type: Bidder Adapter -Maintainer: dsp_back@geniee.co.jp -``` - -# Description -This is [Geniee](https://geniee.co.jp) Bidder Adapter for Prebid.js. - -Please contact us before using the adapter. - -We will provide ads when satisfy the following conditions: - -- There are a certain number bid requests by zone -- The request is a Banner ad -- Payment is possible in Japanese yen or US dollars -- The request is not for GDPR or COPPA users - -Thus, even if the following test, it will be no bids if the request does not reach a certain requests. - -# Test AdUnits -```javascript -var adUnits={ - code: 'geniee-test-ad', - bids: [{ - bidder: 'dsp_geniee', - params: { - test: 1, - } - }], - mediaTypes: { - banner: { - sizes: [[300, 250]] - } - } -}; -``` diff --git a/modules/dspxBidAdapter.js b/modules/dspxBidAdapter.js index b4490095894..b8e812f581a 100644 --- a/modules/dspxBidAdapter.js +++ b/modules/dspxBidAdapter.js @@ -4,10 +4,6 @@ import {BANNER, VIDEO} from '../src/mediaTypes.js'; import {Renderer} from '../src/Renderer.js'; import {includes} from '../src/polyfill.js'; -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - */ - const BIDDER_CODE = 'dspx'; const ENDPOINT_URL = 'https://buyer.dspx.tv/request/'; const ENDPOINT_URL_DEV = 'https://dcbuyer.dspx.tv/request/'; diff --git a/modules/dxkultureBidAdapter.js b/modules/dxkultureBidAdapter.js deleted file mode 100644 index c167baef6ea..00000000000 --- a/modules/dxkultureBidAdapter.js +++ /dev/null @@ -1,340 +0,0 @@ -import { - logInfo, - logWarn, - logError, - logMessage, - deepAccess, - 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' - -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - */ - -const BIDDER_CODE = 'dxkulture'; -const DEFAULT_BID_TTL = 300; -const DEFAULT_NET_REVENUE = true; -const DEFAULT_CURRENCY = 'USD'; -const DEFAULT_OUTSTREAM_RENDERER_URL = 'https://cdn.dxkulture.com/players/dxOutstreamPlayer.js'; - -const converter = ortbConverter({ - context: { - netRevenue: DEFAULT_NET_REVENUE, - ttl: DEFAULT_BID_TTL - }, - imp(buildImp, bidRequest, context) { - const imp = buildImp(bidRequest, context); - - if (!imp.bidfloor) { - imp.bidfloor = bidRequest.params.bidfloor || 0; - imp.bidfloorcur = bidRequest.params.currency || DEFAULT_CURRENCY; - } - return imp; - }, - request(buildRequest, imps, bidderRequest, context) { - const req = buildRequest(imps, bidderRequest, context); - mergeDeep(req, { - ext: { - hb: 1, - prebidver: '$prebid.version$', - adapterver: '1.0.0', - } - }) - - // Attaching GDPR Consent Params - if (bidderRequest.gdprConsent) { - deepSetValue(req, 'user.ext.consent', bidderRequest.gdprConsent.consentString); - deepSetValue(req, 'regs.ext.gdpr', (bidderRequest.gdprConsent.gdprApplies ? 1 : 0)); - } - - // CCPA - if (bidderRequest.uspConsent) { - deepSetValue(req, 'regs.ext.us_privacy', bidderRequest.uspConsent); - } - - return req; - }, - bidResponse(buildBidResponse, bid, context) { - let resMediaType; - const {bidRequest} = context; - - if (bid.adm?.trim().startsWith(' { - const userSync = deepAccess(resp, 'body.ext.usersync'); - if (userSync) { - let syncDetails = []; - Object.keys(userSync).forEach(key => { - const value = userSync[key]; - if (value.syncs && value.syncs.length) { - syncDetails = syncDetails.concat(value.syncs); - } - }); - syncDetails.forEach(syncDetails => { - let queryParamStrings = []; - let syncUrl = syncDetails.url; - - if (syncDetails.type === 'iframe') { - if (gdprConsent) { - queryParamStrings.push('gdpr=' + (gdprConsent.gdprApplies ? 1 : 0)); - queryParamStrings.push('gdpr_consent=' + encodeURIComponent(gdprConsent.consentString || '')); - } - if (uspConsent) { - queryParamStrings.push('us_privacy=' + encodeURIComponent(uspConsent)); - } - syncUrl = `${syncDetails.url}${queryParamStrings.length > 0 ? '?' + queryParamStrings.join('&') : ''}` - } - - syncs.push({ - type: syncDetails.type === 'iframe' ? 'iframe' : 'image', - url: syncUrl - }); - }); - - if (syncOptions.iframeEnabled) { - syncs = syncs.filter(s => s.type == 'iframe'); - } else if (syncOptions.pixelEnabled) { - syncs = syncs.filter(s => s.type == 'image'); - } - } - }); - logInfo('dxkulture.getUserSyncs result=%o', syncs); - return syncs; - }, - -}; - -function outstreamRenderer(bid) { - const rendererConfig = { - width: bid.width, - height: bid.height, - vastTimeout: 5000, - maxAllowedVastTagRedirects: 3, - allowVpaid: false, - autoPlay: true, - preload: true, - mute: false - } - - const renderer = Renderer.install({ - id: bid.adId, - url: DEFAULT_OUTSTREAM_RENDERER_URL, - config: rendererConfig, - loaded: false, - targetId: bid.adUnitCode, - adUnitCode: bid.adUnitCode - }); - - try { - renderer.setRender(function (bid) { - bid.renderer.push(() => { - const { id, config } = bid.renderer; - window.dxOutstreamPlayer(bid, id, config); - }); - }); - } catch (err) { - logWarn('dxkulture: Prebid Error calling setRender on renderer', err); - } - - return renderer; -} - -/* ======================================= - * Util Functions - *======================================= */ - -function hasBannerMediaType(bidRequest) { - return !!deepAccess(bidRequest, 'mediaTypes.banner'); -} - -function hasVideoMediaType(bidRequest) { - return !!deepAccess(bidRequest, 'mediaTypes.video'); -} - -function _validateParams(bidRequest) { - if (!bidRequest.params) { - return false; - } - - if (bidRequest.params.e2etest) { - return true; - } - - if (!bidRequest.params.publisherId) { - logError('dxkulture: Validation failed: publisherId not declared'); - return false; - } - - if (!bidRequest.params.placementId) { - logError('dxkulture: Validation failed: placementId not declared'); - return false; - } - - const mediaTypesExists = hasVideoMediaType(bidRequest) || hasBannerMediaType(bidRequest); - if (!mediaTypesExists) { - return false; - } - - return true; -} - -/** - * Validates banner bid request. If it is not banner media type returns true. - * @param {object} bid, bid to validate - * @return boolean, true if valid, otherwise false - */ -function _validateBanner(bidRequest) { - // If there's no banner no need to validate - if (!hasBannerMediaType(bidRequest)) { - return true; - } - const banner = deepAccess(bidRequest, 'mediaTypes.banner'); - if (!Array.isArray(banner.sizes)) { - return false; - } - - return true; -} - -/** - * Validates video bid request. If it is not video media type returns true. - * @param {object} bid, bid to validate - * @return boolean, true if valid, otherwise false - */ -function _validateVideo(bidRequest) { - // If there's no video no need to validate - if (!hasVideoMediaType(bidRequest)) { - return true; - } - - const videoPlacement = deepAccess(bidRequest, 'mediaTypes.video', {}); - const videoBidderParams = deepAccess(bidRequest, 'params.video', {}); - const params = deepAccess(bidRequest, 'params', {}); - - if (params && params.e2etest) { - return true; - } - - const videoParams = { - ...videoPlacement, - ...videoBidderParams // Bidder Specific overrides - }; - - if (!Array.isArray(videoParams.mimes) || videoParams.mimes.length === 0) { - logError('dxkulture: Validation failed: mimes are invalid'); - return false; - } - - if (!Array.isArray(videoParams.protocols) || videoParams.protocols.length === 0) { - logError('dxkulture: Validation failed: protocols are invalid'); - return false; - } - - if (!videoParams.context) { - logError('dxkulture: Validation failed: context id not declared'); - return false; - } - - if (videoParams.context !== 'instream') { - logError('dxkulture: Validation failed: only context instream is supported '); - return false; - } - - if (typeof videoParams.playerSize === 'undefined' || !Array.isArray(videoParams.playerSize) || !Array.isArray(videoParams.playerSize[0])) { - logError('dxkulture: Validation failed: player size not declared or is not in format [[w,h]]'); - return false; - } - - return true; -} - -registerBidder(spec); diff --git a/modules/dynamicAdBoostRtdProvider.js b/modules/dynamicAdBoostRtdProvider.js deleted file mode 100644 index fe08795f313..00000000000 --- a/modules/dynamicAdBoostRtdProvider.js +++ /dev/null @@ -1,114 +0,0 @@ -/** - * The {@link module:modules/realTimeData} module is required - * @module modules/dynamicAdBoost - * @requires module:modules/realTimeData - */ - -import { submodule } from '../src/hook.js' -import { loadExternalScript } from '../src/adloader.js'; -import { getGlobal } from '../src/prebidGlobal.js'; -import { deepAccess, deepSetValue, isEmptyStr } from '../src/utils.js'; - -const MODULE_NAME = 'dynamicAdBoost'; -const SCRIPT_URL = 'https://adxbid.info'; -const CLIENT_SUPPORTS_IO = window.IntersectionObserver && window.IntersectionObserverEntry && window.IntersectionObserverEntry.prototype && - 'intersectionRatio' in window.IntersectionObserverEntry.prototype; -// Options for the Intersection Observer -const dabOptions = { - threshold: 0.5 // Trigger callback when 50% of the element is visible -}; -let observer; -let dabStartDate; -let dabStartTime; - -// Array of div IDs to track -let dynamicAdBoostAdUnits = {}; - -function init(config, userConsent) { - dabStartDate = new Date(); - dabStartTime = dabStartDate.getTime(); - if (!CLIENT_SUPPORTS_IO) { - return false; - } - // Create an Intersection Observer instance - observer = new IntersectionObserver(dabHandleIntersection, dabOptions); - if (config.params.keyId) { - let keyId = config.params.keyId; - if (keyId && !isEmptyStr(keyId)) { - let dabDivIdsToTrack = config.params.adUnits; - let dabInterval = setInterval(function() { - // Observe each div by its ID - dabDivIdsToTrack.forEach(divId => { - let div = document.getElementById(divId); - if (div) { - observer.observe(div); - } - }); - - let dabDateNow = new Date(); - let dabTimeNow = dabDateNow.getTime(); - let dabElapsedSeconds = Math.floor((dabTimeNow - dabStartTime) / 1000); - let elapsedThreshold = 30; - if (config.params.threshold) { - elapsedThreshold = config.params.threshold; - } - if (dabElapsedSeconds >= elapsedThreshold) { - clearInterval(dabInterval); // Stop - loadLmScript(keyId); - } - }, 1000); - - return true; - } - } - return false; -} - -function loadLmScript(keyId) { - let viewableAdUnits = Object.keys(dynamicAdBoostAdUnits); - let viewableAdUnitsCSV = viewableAdUnits.join(','); - const scriptUrl = `${SCRIPT_URL}/${keyId}.js?viewableAdUnits=${viewableAdUnitsCSV}`; - loadExternalScript(scriptUrl, MODULE_NAME); - observer.disconnect(); -} - -function getBidRequestData(reqBidsConfigObj, callback, config, userConsent) { - const reqAdUnits = reqBidsConfigObj.adUnits || getGlobal().adUnits; - - if (Array.isArray(reqAdUnits)) { - reqAdUnits.forEach(adunit => { - let gptCode = deepAccess(adunit, 'code'); - if (dynamicAdBoostAdUnits.hasOwnProperty(gptCode)) { - // AdUnits has reached target viewablity at some point - deepSetValue(adunit, `ortb2Imp.ext.data.${MODULE_NAME}.${gptCode}`, dynamicAdBoostAdUnits[gptCode]); - } - }); - } - callback(); -} - -let markViewed = (entry, observer) => { - return () => { - observer.unobserve(entry.target); - } -} - -// Callback function when an observed element becomes visible -function dabHandleIntersection(entries) { - entries.forEach(entry => { - if (entry.isIntersecting && entry.intersectionRatio > 0.5) { - dynamicAdBoostAdUnits[entry.target.id] = entry.intersectionRatio; - markViewed(entry, observer) - } - }); -} - -/** @type {RtdSubmodule} */ -export const subModuleObj = { - name: MODULE_NAME, - init, - getBidRequestData, - markViewed -}; - -submodule('realTimeData', subModuleObj); diff --git a/modules/dynamicAdBoostRtdProvider.md b/modules/dynamicAdBoostRtdProvider.md deleted file mode 100644 index 93efe3b3f97..00000000000 --- a/modules/dynamicAdBoostRtdProvider.md +++ /dev/null @@ -1,40 +0,0 @@ -# Overview - -Module Name: Dynamic Ad Boost -Module Type: Track when a adunit is viewable -Maintainer: info@luponmedia.com - -# Description - -Enhance your revenue with the cutting-edge DynamicAdBoost module! By seamlessly integrating the powerful LuponMedia technology, our module retrieves adunits viewability data, providing publishers with valuable insights to optimize their revenue streams. To unlock the full potential of this technology, we provide a customized LuponMedia module tailored to your specific site requirements. Boost your ad revenue and gain unprecedented visibility into your performance with our advanced solution. - -In order to utilize this module, it is essential to collaborate with [LuponMedia](https://www.luponmedia.com/) to create an account and obtain detailed guidelines on configuring your sites. Working hand in hand with LuponMedia will ensure a smooth integration process, enabling you to fully leverage the capabilities of this module on your website. Take the first step towards optimizing your ad revenue and enhancing your site's performance by partnering with LuponMedia for a seamless experience. -Contact info@luponmedia.com for information. - -## Building Prebid with Real-time Data Support - -First, make sure to add the Dynamic AdBoost submodule to your Prebid.js package with: - -`gulp build --modules=rtdModule,dynamicAdBoostRtdProvider` - -The following configuration parameters are available: - -``` -pbjs.setConfig( - ... - realTimeData: { - auctionDelay: 2000, - dataProviders: [ - { - name: "dynamicAdBoost", - params: { - keyId: "[PROVIDED_KEY]", // Your provided Dynamic AdBoost keyId - adUnits: ["allowedAdUnit1", "allowedAdUnit2"], - threshold: 35 // optional - } - } - ] - } - ... -} -``` diff --git a/modules/ebdrBidAdapter.js b/modules/ebdrBidAdapter.js index e830f8a94f7..a03a1ec12ca 100644 --- a/modules/ebdrBidAdapter.js +++ b/modules/ebdrBidAdapter.js @@ -1,4 +1,4 @@ -import {getBidIdParameter, logInfo} from '../src/utils.js'; +import { logInfo, getBidIdParameter } from '../src/utils.js'; import { VIDEO, BANNER } from '../src/mediaTypes.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; const BIDDER_CODE = 'ebdr'; diff --git a/modules/edge226BidAdapter.js b/modules/edge226BidAdapter.js deleted file mode 100644 index 6d1e2466abe..00000000000 --- a/modules/edge226BidAdapter.js +++ /dev/null @@ -1,188 +0,0 @@ -import { isFn, deepAccess, logMessage, logError } from '../src/utils.js'; -import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; - -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; -import { config } from '../src/config.js'; - -const BIDDER_CODE = 'edge226'; -const AD_URL = 'https://ssp.dauup.com/pbjs'; - -function isBidResponseValid(bid) { - if (!bid.requestId || !bid.cpm || !bid.creativeId || !bid.ttl || !bid.currency) { - return false; - } - - switch (bid.mediaType) { - case BANNER: - return Boolean(bid.width && bid.height && bid.ad); - case VIDEO: - return Boolean(bid.vastUrl || bid.vastXml); - case NATIVE: - return Boolean(bid.native && bid.native.impressionTrackers && bid.native.impressionTrackers.length); - default: - return false; - } -} - -function getPlacementReqData(bid) { - const { params, bidId, mediaTypes } = bid; - const schain = bid.schain || {}; - const { placementId, endpointId } = params; - const bidfloor = getBidFloor(bid); - - const placement = { - bidId, - schain, - bidfloor - }; - - if (placementId) { - placement.placementId = placementId; - placement.type = 'publisher'; - } else if (endpointId) { - placement.endpointId = endpointId; - placement.type = 'network'; - } - - if (mediaTypes && mediaTypes[BANNER]) { - placement.adFormat = BANNER; - placement.sizes = mediaTypes[BANNER].sizes; - } else if (mediaTypes && mediaTypes[VIDEO]) { - placement.adFormat = VIDEO; - placement.playerSize = mediaTypes[VIDEO].playerSize; - placement.minduration = mediaTypes[VIDEO].minduration; - placement.maxduration = mediaTypes[VIDEO].maxduration; - placement.mimes = mediaTypes[VIDEO].mimes; - placement.protocols = mediaTypes[VIDEO].protocols; - placement.startdelay = mediaTypes[VIDEO].startdelay; - placement.placement = mediaTypes[VIDEO].placement; - placement.skip = mediaTypes[VIDEO].skip; - placement.skipafter = mediaTypes[VIDEO].skipafter; - placement.minbitrate = mediaTypes[VIDEO].minbitrate; - placement.maxbitrate = mediaTypes[VIDEO].maxbitrate; - placement.delivery = mediaTypes[VIDEO].delivery; - placement.playbackmethod = mediaTypes[VIDEO].playbackmethod; - placement.api = mediaTypes[VIDEO].api; - placement.linearity = mediaTypes[VIDEO].linearity; - } else if (mediaTypes && mediaTypes[NATIVE]) { - placement.native = mediaTypes[NATIVE]; - placement.adFormat = NATIVE; - } - - return placement; -} - -function getBidFloor(bid) { - if (!isFn(bid.getFloor)) { - return deepAccess(bid, 'params.bidfloor', 0); - } - - try { - const bidFloor = bid.getFloor({ - currency: 'USD', - mediaType: '*', - size: '*', - }); - return bidFloor.floor; - } catch (err) { - logError(err); - return 0; - } -} - -export const spec = { - code: BIDDER_CODE, - supportedMediaTypes: [BANNER, VIDEO, NATIVE], - - isBidRequestValid: (bid = {}) => { - const { params, bidId, mediaTypes } = bid; - let valid = Boolean(bidId && params && (params.placementId || params.endpointId)); - - if (mediaTypes && mediaTypes[BANNER]) { - valid = valid && Boolean(mediaTypes[BANNER] && mediaTypes[BANNER].sizes); - } else if (mediaTypes && mediaTypes[VIDEO]) { - valid = valid && Boolean(mediaTypes[VIDEO] && mediaTypes[VIDEO].playerSize); - } else if (mediaTypes && mediaTypes[NATIVE]) { - valid = valid && Boolean(mediaTypes[NATIVE]); - } else { - valid = false; - } - return valid; - }, - - buildRequests: (validBidRequests = [], bidderRequest = {}) => { - // convert Native ORTB definition to old-style prebid native definition - validBidRequests = convertOrtbRequestToProprietaryNative(validBidRequests); - - let deviceWidth = 0; - let deviceHeight = 0; - - let winLocation; - try { - const winTop = window.top; - deviceWidth = winTop.screen.width; - deviceHeight = winTop.screen.height; - winLocation = winTop.location; - } catch (e) { - logMessage(e); - winLocation = window.location; - } - - const refferUrl = bidderRequest.refererInfo && bidderRequest.refererInfo.page; - let refferLocation; - try { - refferLocation = refferUrl && new URL(refferUrl); - } catch (e) { - logMessage(e); - } - // TODO: does the fallback make sense here? - let location = refferLocation || winLocation; - const language = (navigator && navigator.language) ? navigator.language.split('-')[0] : ''; - const host = location.host; - const page = location.pathname; - const secure = location.protocol === 'https:' ? 1 : 0; - const placements = []; - const request = { - deviceWidth, - deviceHeight, - language, - secure, - host, - page, - placements, - coppa: config.getConfig('coppa') === true ? 1 : 0, - ccpa: bidderRequest.uspConsent || undefined, - gdpr: bidderRequest.gdprConsent || undefined, - tmax: bidderRequest.timeout - }; - - const len = validBidRequests.length; - for (let i = 0; i < len; i++) { - const bid = validBidRequests[i]; - placements.push(getPlacementReqData(bid)); - } - - return { - method: 'POST', - url: AD_URL, - data: request - }; - }, - - interpretResponse: (serverResponse) => { - let response = []; - for (let i = 0; i < serverResponse.body.length; i++) { - let resItem = serverResponse.body[i]; - if (isBidResponseValid(resItem)) { - const advertiserDomains = resItem.adomain && resItem.adomain.length ? resItem.adomain : []; - resItem.meta = { ...resItem.meta, advertiserDomains }; - - response.push(resItem); - } - } - return response; - } -}; - -registerBidder(spec); diff --git a/modules/edge226BidAdapter.md b/modules/edge226BidAdapter.md deleted file mode 100644 index b38ff67065f..00000000000 --- a/modules/edge226BidAdapter.md +++ /dev/null @@ -1,79 +0,0 @@ -# Overview - -``` -Module Name: Edge226 Bidder Adapter -Module Type: Edge226 Bidder Adapter -Maintainer: audit@edge226.com -``` - -# Description - -Connects to Edge226 exchange for bids. -Edge226 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: 'edge226', - params: { - placementId: 'testBanner', - } - } - ] - }, - { - code: 'addunit2', - mediaTypes: { - video: { - playerSize: [ [640, 480] ], - context: 'instream', - minduration: 5, - maxduration: 60, - } - }, - bids: [ - { - bidder: 'edge226', - params: { - placementId: 'testVideo', - } - } - ] - }, - { - code: 'addunit3', - mediaTypes: { - native: { - title: { - required: true - }, - body: { - required: true - }, - icon: { - required: true, - size: [64, 64] - } - } - }, - bids: [ - { - bidder: 'edge226', - params: { - placementId: 'testNative', - } - } - ] - } - ]; -``` diff --git a/modules/eplanningBidAdapter.js b/modules/eplanningBidAdapter.js index d57804c04e6..2216ab329b0 100644 --- a/modules/eplanningBidAdapter.js +++ b/modules/eplanningBidAdapter.js @@ -1,9 +1,8 @@ -import {getWindowSelf, isEmpty, parseSizesInput, isGptPubadsDefined} from '../src/utils.js'; +import {getWindowSelf, isEmpty, parseSizesInput, isGptPubadsDefined, isSlotMatchingAdUnitCode} 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'; const BIDDER_CODE = 'eplanning'; export const storage = getStorageManager({bidderCode: BIDDER_CODE}); @@ -268,21 +267,6 @@ function cleanName(name) { return name.replace(/_|\.|-|\//g, '').replace(/\)\(|\(|\)|:/g, '_').replace(/^_+|_+$/g, ''); } -function getFloorStr(bid) { - if (typeof bid.getFloor === 'function') { - let bidFloor = bid.getFloor({ - currency: DOLLAR_CODE, - mediaType: '*', - size: '*' - }); - - if (bidFloor.floor) { - return '|' + encodeURIComponent(bidFloor.floor); - } - } - return ''; -} - function getSpaces(bidRequests, ml) { let impType = bidRequests.reduce((previousBits, bid) => (bid.mediaTypes && bid.mediaTypes[VIDEO]) ? (bid.mediaTypes[VIDEO].context == 'outstream' ? (previousBits | 2) : (previousBits | 1)) : previousBits, 0); // Only one type of auction is supported at a time @@ -302,7 +286,7 @@ function getSpaces(bidRequests, ml) { let sizeVast = firstSize ? firstSize.join('x') : DEFAULT_SIZE_VAST; name = 'video_' + sizeVast + '_' + i; es.map[name] = bid.bidId; - return name + ':' + sizeVast + ';1' + getFloorStr(bid); + return name + ':' + sizeVast + ';1'; } if (ml) { @@ -312,7 +296,7 @@ function getSpaces(bidRequests, ml) { } es.map[name] = bid.bidId; - return name + ':' + getSize(bid) + getFloorStr(bid); + return name + ':' + getSize(bid); }).join('+')).join('+'); return es; } diff --git a/modules/eskimiBidAdapter.js b/modules/eskimiBidAdapter.js index ce01abb9e71..88d8f95b859 100644 --- a/modules/eskimiBidAdapter.js +++ b/modules/eskimiBidAdapter.js @@ -1,12 +1,7 @@ -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} from '../src/utils.js'; - -/** - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - */ const BIDDER_CODE = 'eskimi'; // const ENDPOINT = 'https://hb.eskimi.com/bids' @@ -70,7 +65,7 @@ const CONVERTER = ortbConverter({ imp.secure = Number(window.location.protocol === 'https:'); if (!imp.bidfloor && bidRequest.params.bidFloor) { imp.bidfloor = bidRequest.params.bidFloor; - imp.bidfloorcur = getBidIdParameter('bidFloorCur', bidRequest.params).toUpperCase() || 'USD' + imp.bidfloorcur = utils.getBidIdParameter('bidFloorCur', bidRequest.params).toUpperCase() || 'USD' } if (bidRequest.mediaTypes[VIDEO]) { diff --git a/modules/euidIdSystem.js b/modules/euidIdSystem.js index fa6113250a8..c4e15b11d15 100644 --- a/modules/euidIdSystem.js +++ b/modules/euidIdSystem.js @@ -12,14 +12,7 @@ import {MODULE_TYPE_UID} from '../src/activities/modules.js'; // RE below lint exception: UID2 and EUID are separate modules, but the protocol is the same and shared code makes sense here. // eslint-disable-next-line prebid/validate-imports -import { Uid2GetId, Uid2CodeVersion, extractIdentityFromParams } from './uid2IdSystem_shared.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').euidId} euidId - */ +import { Uid2GetId, Uid2CodeVersion } from './uid2IdSystem_shared.js'; const MODULE_NAME = 'euid'; const MODULE_REVISION = Uid2CodeVersion; @@ -106,27 +99,10 @@ export const euidIdSubmodule = { internalStorage: ADVERTISING_COOKIE }; - if (FEATURES.UID2_CSTG) { - mappedConfig.cstg = { - serverPublicKey: config?.params?.serverPublicKey, - subscriptionId: config?.params?.subscriptionId, - ...extractIdentityFromParams(config?.params ?? {}) - } - } - _logInfo(`EUID configuration loaded and mapped.`, mappedConfig); const result = Uid2GetId(mappedConfig, storage, _logInfo, _logWarn); _logInfo(`EUID getId returned`, result); return result; }, - eids: { - 'euid': { - source: 'euid.eu', - atype: 3, - getValue: function(data) { - return data.id; - } - }, - }, }; function decodeImpl(value) { diff --git a/modules/euidIdSystem.md b/modules/euidIdSystem.md index 72e40b8ce7b..e3e16bce89d 100644 --- a/modules/euidIdSystem.md +++ b/modules/euidIdSystem.md @@ -1,59 +1,9 @@ ## EUID User ID Submodule -The EUID module handles storing, providing, and optionally refreshing tokens. While initial tokens traditionally required server-side generation, the introduction of the *Client-Side Token Generation (CSTG)* mode offers publishers the flexibility to generate EUID tokens directly from the module, eliminating this need. Publishers can choose to operate the module in one of three distinct modes: *Client Refresh* mode, *Server Only* mode and *Client-Side Token Generation* mode. +EUID requires initial tokens to be generated server-side. The EUID module handles storing, providing, and optionally refreshing them. The module can operate in one of two different modes: *Client Refresh* mode or *Server Only* mode. *Server Only* mode was originally referred to as *legacy mode*, but it is a popular mode for new integrations where publishers prefer to handle token refresh server-side. -*Client-Side Token Generation* mode is included in EUID module by default. However, it's important to note that this mode is created and made available recently. For publishers who do not intend to use it, you have the option to instruct the build to exclude the code related to this feature: - -``` - $ gulp build --modules=uid2IdSystem --disable UID2_CSTG -``` -If you do plan to use Client-Side Token Generation (CSTG) mode, please consult the EUID Team first as they will provide required configuration values for you to use (see the Client-Side Token Generation (CSTG) mode section below for details) - -**This mode is created and made available recently. Please consult EUID Team first as they will provide required configuration values for you to use.** - -For publishers seeking a purely client-side integration without the complexities of server-side involvement, the CSTG mode is highly recommended. This mode requires the provision of a public key, subscription ID and [directly identifying information (DII)](https://unifiedid.com/docs/ref-info/glossary-uid#gl-dii) - either emails or phone numbers. In the CSTG mode, the module takes on the responsibility of encrypting the DII, generating the EUID token, and handling token refreshes when necessary. - -To configure the module to use this mode, you must: -1. Set `parmas.serverPublicKey` and `params.subscriptionId` (please reach out to the UID2 team to obtain these values) -2. Provide **ONLY ONE DII** by setting **ONLY ONE** of `params.email`/`params.phone`/`params.emailHash`/`params.phoneHash` - -Below is a table that provides guidance on when to use each directly identifying information (DII) parameter, along with information on whether normalization and hashing are required by the publisher for each parameter. - -| DII param | When to use it | Normalization required by publisher? | Hashing required by publisher? | -|------------------|-------------------------------------------------------|--------------------------------------|--------------------------------| -| params.email | When you have users' email address | No | No | -| params.phone | When you have user's phone number | Yes | No | -| params.emailHash | When you have user's hashed, normalized email address | Yes | Yes | -| params.phoneHash | When you have user's hashed, normalized phone number | Yes | Yes | - - -*Note that setting params.email will normalize email addresses, but params.phone requires phone numbers to be normalized.* - -Refer to [Normalization and Encoding](#normalization-and-encoding) for details on email address normalization, SHA-256 hashing and Base64 encoding. - -### CSTG example - -Configuration: -``` -pbjs.setConfig({ - userSync: { - userIds: [{ - name: 'euid', - params: { - serverPublicKey: '...server public key...', - subscriptionId: '...subcription id...', - email: 'user@email.com', - //phone: '+0000000', - //emailHash: '...email hash...', - //phoneHash: '...phone hash ...' - } - }] - } -}); -``` - ## Client Refresh mode This is the recommended mode for most scenarios. In this mode, the full response body from the EUID Token Generate or Token Refresh endpoint must be provided to the module. As long as the refresh token remains valid, the module will refresh the advertising token as needed. diff --git a/modules/experianRtdProvider.js b/modules/experianRtdProvider.js deleted file mode 100644 index cd415d4b32c..00000000000 --- a/modules/experianRtdProvider.js +++ /dev/null @@ -1,141 +0,0 @@ -import { submodule } from '../src/hook.js'; -import { getStorageManager } from '../src/storageManager.js'; -import { MODULE_TYPE_RTD } from '../src/activities/modules.js'; -import { - deepAccess, - isArray, - isPlainObject, - isStr, - mergeDeep, - safeJSONParse, - timestamp -} from '../src/utils.js'; -import { ajax } from '../src/ajax.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 - */ - -export const SUBMODULE_NAME = 'experian_rtid'; -export const EXPERIAN_RTID_DATA_KEY = 'experian_rtid_data'; -export const EXPERIAN_RTID_EXPIRATION_KEY = 'experian_rtid_expiration'; -export const EXPERIAN_RTID_STALE_KEY = 'experian_rtid_stale'; -export const EXPERIAN_RTID_NO_TRACK_KEY = 'experian_rtid_no_track'; -const EXPERIAN_RTID_URL = 'https://rtid.tapad.com' -const storage = getStorageManager({ moduleType: MODULE_TYPE_RTD, moduleName: SUBMODULE_NAME }); - -export const experianRtdObj = { - /** - * @summary modify bid request data - * @param {Object} reqBidsConfigObj - * @param {function} done - * @param {SubmoduleConfig} config - * @param {UserConsentData} userConsent - */ - getBidRequestData(reqBidsConfigObj, done, config, userConsent) { - const dataEnvelope = storage.getDataFromLocalStorage(EXPERIAN_RTID_DATA_KEY, null); - const stale = storage.getDataFromLocalStorage(EXPERIAN_RTID_STALE_KEY, null); - const expired = storage.getDataFromLocalStorage(EXPERIAN_RTID_EXPIRATION_KEY, null); - const noTrack = storage.getDataFromLocalStorage(EXPERIAN_RTID_NO_TRACK_KEY, null); - const now = timestamp() - if (now > new Date(expired).getTime() || (noTrack == null && dataEnvelope == null)) { - // request data envelope and don't manipulate bids - experianRtdObj.requestDataEnvelope(config, userConsent) - done(); - return false; - } - if (now > new Date(stale).getTime()) { - // request data envelope and manipulate bids - experianRtdObj.requestDataEnvelope(config, userConsent); - } - if (noTrack != null) { - done(); - return false; - } - experianRtdObj.alterBids(reqBidsConfigObj, config); - done() - return true; - }, - - alterBids(reqBidsConfigObj, config) { - const dataEnvelope = safeJSONParse(storage.getDataFromLocalStorage(EXPERIAN_RTID_DATA_KEY, null)); - if (dataEnvelope == null) { - return; - } - deepAccess(config, 'params.bidders').forEach((bidderCode) => { - const bidderData = dataEnvelope.find(({ bidder }) => bidder === bidderCode) - if (bidderData != null) { - mergeDeep(reqBidsConfigObj.ortb2Fragments.bidder, { [bidderCode]: { experianRtidKey: bidderData.data.key, experianRtidData: bidderData.data.data } }) - } - }) - }, - requestDataEnvelope(config, userConsent) { - function storeDataEnvelopeResponse(response) { - const responseJson = safeJSONParse(response); - if (responseJson != null) { - storage.setDataInLocalStorage(EXPERIAN_RTID_STALE_KEY, responseJson.staleAt, null); - storage.setDataInLocalStorage(EXPERIAN_RTID_EXPIRATION_KEY, responseJson.expiresAt, null); - if (responseJson.status === 'no_track') { - storage.setDataInLocalStorage(EXPERIAN_RTID_NO_TRACK_KEY, 'no_track', null); - storage.removeDataFromLocalStorage(EXPERIAN_RTID_DATA_KEY, null); - } else { - storage.setDataInLocalStorage(EXPERIAN_RTID_DATA_KEY, JSON.stringify(responseJson.data), null); - storage.removeDataFromLocalStorage(EXPERIAN_RTID_NO_TRACK_KEY, null); - } - } - } - const queryString = experianRtdObj.extractConsentQueryString(config, userConsent) - const fullUrl = queryString == null ? `${EXPERIAN_RTID_URL}/acc/${deepAccess(config, 'params.accountId')}/ids` : `${EXPERIAN_RTID_URL}/acc/${deepAccess(config, 'params.accountId')}/ids${queryString}` - ajax(fullUrl, storeDataEnvelopeResponse, null, { withCredentials: true, contentType: 'application/json' }) - }, - extractConsentQueryString(config, userConsent) { - const queryObj = {}; - - if (userConsent != null) { - if (userConsent.gdpr != null) { - const { gdprApplies, consentString } = userConsent.gdpr; - mergeDeep(queryObj, {gdpr: gdprApplies, gdpr_consent: consentString}) - } - if (userConsent.uspConsent != null) { - mergeDeep(queryObj, {us_privacy: userConsent.uspConsent}) - } - } - const consentQueryString = Object.entries(queryObj).map(([key, val]) => `${key}=${val}`).join('&'); - - let idsString = ''; - if (deepAccess(config, 'params.ids') != null && isPlainObject(deepAccess(config, 'params.ids'))) { - idsString = Object.entries(deepAccess(config, 'params.ids')).map(([idType, val]) => { - if (isArray(val)) { - return val.map((singleVal) => `id.${idType}=${singleVal}`).join('&') - } else { - return `id.${idType}=${val}` - } - }).join('&') - } - - const combinedString = [consentQueryString, idsString].filter((string) => string !== '').join('&'); - return combinedString !== '' ? `?${combinedString}` : undefined; - }, - /** - * @function - * @summary init sub module - * @name RtdSubmodule#init - * @param {SubmoduleConfig} config - * @param {UserConsentData} userConsent - * @return {boolean} false to remove sub module - */ - init(config, userConsent) { - return isStr(deepAccess(config, 'params.accountId')); - } -} - -/** @type {RtdSubmodule} */ -export const experianRtdSubmodule = { - name: SUBMODULE_NAME, - getBidRequestData: experianRtdObj.getBidRequestData, - init: experianRtdObj.init -} - -submodule('realTimeData', experianRtdSubmodule); diff --git a/modules/experianRtdProvider.md b/modules/experianRtdProvider.md deleted file mode 100644 index ad46e0c3d55..00000000000 --- a/modules/experianRtdProvider.md +++ /dev/null @@ -1,52 +0,0 @@ -# Experian Real-time Data Submodule - -## Overview - - Module Name: Experian Rtd Provider - Module Type: Rtd Provider - Maintainer: team-ui@tapad.com - -## Description - -The Experian RTD module adds encrypted identifier envelope to the bidding object. - -## Usage - -### Build -``` -gulp build --modules="rtdModule,experianRtdProvider,appnexusBidAdapter,..." -``` - -> Note that the global RTD module, `rtdModule`, is a prerequisite of the Experian RTD module. - -### Configuration - -Use `setConfig` to instruct Prebid.js to initialize the Experian RTD module, as specified below. - -This module is configured as part of the `realTimeData.dataProviders` - -```javascript -pbjs.setConfig({ - realTimeData: { - auctionDelay: 300, - dataProviders: [{ - name: 'experian_rtid', - waitForIt: true, - params: { - accountId: 'ZylatYg', - bidders: ['sovrn', 'pubmatic'], - ids: { maid: ['424', '2982'], hem: 'my-hem' } - } - }] - } -}) -``` - -### Parameters -| Name | Type | Description | Default | -|:-----------------|:----------------------------------------|:-----------------------------------------------------------------------------|:-----------------------| -| name | String | Real time data module name | Always 'experian_rtid' | -| waitForIt | Boolean | Set to true to maximize chance for bidder enrichment, used with auctionDelay | `false` | -| params.accountId | String | Your account id issued by Experian | | -| params.bidders | Array | List of bidders for which you would like data to be set | | -| params.ids | Record or string> | Additional identifiers to send to Experian RTID endpoint | | diff --git a/modules/fabrickIdSystem.js b/modules/fabrickIdSystem.js index f62bafcf637..dfc2985b0db 100644 --- a/modules/fabrickIdSystem.js +++ b/modules/fabrickIdSystem.js @@ -10,13 +10,6 @@ import { ajax } from '../src/ajax.js'; import { submodule } from '../src/hook.js'; import { getRefererInfo } from '../src/refererDetection.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 - */ - /** @type {Submodule} */ export const fabrickIdSubmodule = { /** @@ -124,12 +117,6 @@ export const fabrickIdSubmodule = { } catch (e) { logError(`fabrickIdSystem encountered an error`, e); } - }, - eids: { - 'fabrickId': { - source: 'neustar.biz', - atype: 1 - }, } }; diff --git a/modules/feedadBidAdapter.js b/modules/feedadBidAdapter.js index e68a932b726..7b41f0fcc03 100644 --- a/modules/feedadBidAdapter.js +++ b/modules/feedadBidAdapter.js @@ -3,15 +3,6 @@ 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 - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - * @typedef {import('../src/adapters/bidderFactory.js').ServerRequest} ServerRequest - * @typedef {import('../src/adapters/bidderFactory.js').SyncOptions} SyncOptions - * @typedef {import('../src/adapters/bidderFactory.js').BidderSpec} BidderSpec - * @typedef {import('../src/adapters/bidderFactory.js').MediaTypes} MediaTypes - */ - /** * Version of the FeedAd bid adapter * @type {string} diff --git a/modules/fledgeForGpt.js b/modules/fledgeForGpt.js index 7162f4e9230..f29ce7508d5 100644 --- a/modules/fledgeForGpt.js +++ b/modules/fledgeForGpt.js @@ -4,112 +4,50 @@ */ import { config } from '../src/config.js'; import { getHook } from '../src/hook.js'; -import {deepSetValue, logInfo, logWarn, mergeDeep} from '../src/utils.js'; +import { getGptSlotForAdUnitCode, logInfo, logWarn } from '../src/utils.js'; import {IMP, PBS, registerOrtbProcessor, RESPONSE} from '../src/pbjsORTB.js'; -import * as events from '../src/events.js' -import CONSTANTS from '../src/constants.json'; -import {currencyCompare} from '../libraries/currencyUtils/currency.js'; -import {maximum, minimum} from '../src/utils/reducers.js'; -import {getGptSlotForAdUnitCode} from '../libraries/gptUtils/gptUtils.js'; const MODULE = 'fledgeForGpt' -const PENDING = {}; export let isEnabled = false; config.getConfig('fledgeForGpt', config => init(config.fledgeForGpt)); /** - * Module init. - */ + * Module init. + */ export function init(cfg) { if (cfg && cfg.enabled === true) { if (!isEnabled) { getHook('addComponentAuction').before(addComponentAuctionHook); - getHook('makeBidRequests').after(markForFledge); - events.on(CONSTANTS.EVENTS.AUCTION_INIT, onAuctionInit); - events.on(CONSTANTS.EVENTS.AUCTION_END, onAuctionEnd); isEnabled = true; } logInfo(`${MODULE} enabled (browser ${isFledgeSupported() ? 'supports' : 'does NOT support'} fledge)`, cfg); } else { if (isEnabled) { getHook('addComponentAuction').getHooks({hook: addComponentAuctionHook}).remove(); - getHook('makeBidRequests').getHooks({hook: markForFledge}).remove() - events.off(CONSTANTS.EVENTS.AUCTION_INIT, onAuctionInit); - events.off(CONSTANTS.EVENTS.AUCTION_END, onAuctionEnd); isEnabled = false; } logInfo(`${MODULE} disabled`, cfg); } } -function setComponentAuction(adUnitCode, auctionConfigs) { +export function addComponentAuctionHook(next, adUnitCode, componentAuctionConfig) { + const seller = componentAuctionConfig.seller; const gptSlot = getGptSlotForAdUnitCode(adUnitCode); if (gptSlot && gptSlot.setConfig) { gptSlot.setConfig({ - componentAuction: auctionConfigs.map(cfg => ({ - configKey: cfg.seller, - auctionConfig: cfg - })) + componentAuction: [{ + configKey: seller, + auctionConfig: componentAuctionConfig + }] }); - logInfo(MODULE, `register component auction configs for: ${adUnitCode}: ${gptSlot.getAdUnitPath()}`, auctionConfigs); + logInfo(MODULE, `register component auction config for: ${adUnitCode} x ${seller}: ${gptSlot.getAdUnitPath()}`, componentAuctionConfig); } else { - logWarn(MODULE, `unable to register component auction config for ${adUnitCode}`, auctionConfigs); + logWarn(MODULE, `unable to register component auction config for: ${adUnitCode} x ${seller}.`); } -} -function onAuctionInit({auctionId}) { - PENDING[auctionId] = {}; -} - -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; -} - -function onAuctionEnd({auctionId, bidsReceived, bidderRequests}) { - try { - const allReqs = bidderRequests?.flatMap(br => br.bids); - Object.entries(PENDING[auctionId]).forEach(([adUnitCode, auctionConfigs]) => { - const forThisAdUnit = (bid) => bid.adUnitCode === adUnitCode; - const slotSignals = getSlotSignals(bidsReceived?.filter(forThisAdUnit), allReqs?.filter(forThisAdUnit)); - setComponentAuction(adUnitCode, auctionConfigs.map(cfg => mergeDeep({}, slotSignals, cfg))) - }) - } finally { - delete PENDING[auctionId]; - } -} - -function setFPDSignals(auctionConfig, fpd) { - auctionConfig.auctionSignals = mergeDeep({}, {prebid: fpd}, auctionConfig.auctionSignals); -} - -export function addComponentAuctionHook(next, request, componentAuctionConfig) { - const {adUnitCode, auctionId, ortb2, ortb2Imp} = request; - if (PENDING.hasOwnProperty(auctionId)) { - setFPDSignals(componentAuctionConfig, {ortb2, ortb2Imp}); - !PENDING[auctionId].hasOwnProperty(adUnitCode) && (PENDING[auctionId][adUnitCode] = []); - PENDING[auctionId][adUnitCode].push(componentAuctionConfig); - } else { - logWarn(MODULE, `Received component auction config for auction that has closed (auction '${auctionId}', adUnit '${adUnitCode}')`, componentAuctionConfig) - } - next(request, componentAuctionConfig); + next(adUnitCode, componentAuctionConfig); } function isFledgeSupported() { @@ -118,23 +56,16 @@ function isFledgeSupported() { export function markForFledge(next, bidderRequests) { if (isFledgeSupported()) { - const globalFledgeConfig = config.getConfig('fledgeForGpt'); - const bidders = globalFledgeConfig?.bidders ?? []; - bidderRequests.forEach((bidderReq) => { - const useGlobalConfig = globalFledgeConfig?.enabled && (bidders.length === 0 || bidders.includes(bidderReq.bidderCode)); - config.runWithBidder(bidderReq.bidderCode, () => { - const fledgeEnabled = config.getConfig('fledgeEnabled') ?? (useGlobalConfig ? globalFledgeConfig.enabled : undefined); - const defaultForSlots = config.getConfig('defaultForSlots') ?? (useGlobalConfig ? globalFledgeConfig?.defaultForSlots : undefined); - Object.assign(bidderReq, {fledgeEnabled}); - bidderReq.bids.forEach(bidReq => { deepSetValue(bidReq, 'ortb2Imp.ext.ae', bidReq.ortb2Imp?.ext?.ae ?? defaultForSlots) }) - }) - }); + bidderRequests.forEach((req) => { + req.fledgeEnabled = config.runWithBidder(req.bidderCode, () => config.getConfig('fledgeEnabled')) + }) } next(bidderRequests); } +getHook('makeBidRequests').after(markForFledge); export function setImpExtAe(imp, bidRequest, context) { - if (imp.ext?.ae && !context.bidderRequest.fledgeEnabled) { + if (!context.bidderRequest.fledgeEnabled) { delete imp.ext?.ae; } } diff --git a/modules/fledgeForGpt.md b/modules/fledgeForGpt.md index 28f44da6459..3bb86cd5946 100644 --- a/modules/fledgeForGpt.md +++ b/modules/fledgeForGpt.md @@ -15,8 +15,8 @@ This is accomplished by adding the `fledgeForGpt` module to the list of modules gulp build --modules=fledgeForGpt,... ``` -Second, they must enable FLEDGE in their Prebid.js configuration. -This is done through module level configuration, but to provide a high degree of flexiblity for testing, FLEDGE settings also exist at the bidder level and slot level. +Second, they must enable FLEDGE in their Prebid.js configuration. To provide a high degree of flexiblity for testing, FLEDGE +settings exist at the module level, the bidder level, and the slot level. ### Module Configuration This module exposes the following settings: @@ -24,20 +24,15 @@ 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, FLEDGE 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: +As noted above, FLEDGE support is disabled by default. To enable it, set the `enabled` value to `true` for this module +using the `setConfig` method of Prebid.js: ```js pbjs.que.push(function() { pbjs.setConfig({ fledgeForGpt: { - enabled: true, - bidders: ['openx', 'rtbhouse'], - defaultForSlots: 1 + enabled: true } }); }); @@ -49,25 +44,23 @@ This module adds the following setting for bidders: |Name |Type |Description |Notes | | :------------ | :------------ | :------------ |:------------ | | fledgeEnabled | Boolean | Enable/disable a bidder to participate in FLEDGE | Defaults to `false` | -|defaultForSlots | Number |Default value for `imp.ext.ae` in requests for specified bidders |Should be 1| -Individual bidders may be further included or excluded here using the `setBidderConfig` method +In addition to enabling FLEDGE at the module level, individual bidders must also be enabled. This allows publishers to +selectively test with one or more bidders as they desire. To enable one or more bidders, use the `setBidderConfig` method of Prebid.js: ```js pbjs.setBidderConfig({ bidders: ["openx"], config: { - fledgeEnabled: true, - defaultForSlots: 1 + fledgeEnabled: true } }); ``` ### AdUnit Configuration -All adunits can be opted-in to FLEDGE 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. +Enabling an adunit for FLEDGE eligibility is accomplished by setting an attribute of the `ortb2Imp` object for that +adunit. |Name |Type |Description |Notes | | :------------ | :------------ | :------------ |:------------ | diff --git a/modules/flippBidAdapter.js b/modules/flippBidAdapter.js deleted file mode 100644 index 0708c90ac0d..00000000000 --- a/modules/flippBidAdapter.js +++ /dev/null @@ -1,194 +0,0 @@ -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'; - -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse - * @typedef {import('../src/adapters/bidderFactory.js').SyncOptions} SyncOptions - * @typedef {import('../src/adapters/bidderFactory.js').UserSync} UserSync - */ - -const NETWORK_ID = 10922; -const AD_TYPES = [4309, 641]; -const DTX_TYPES = [5061]; -const TARGET_NAME = 'inline'; -const BIDDER_CODE = 'flipp'; -const ENDPOINT = 'https://gateflipp.flippback.com/flyer-locator-service/client_bidding'; -const DEFAULT_TTL = 30; -const DEFAULT_CURRENCY = 'USD'; -const DEFAULT_CREATIVE_TYPE = 'NativeX'; -const VALID_CREATIVE_TYPES = ['DTX', 'NativeX']; -const FLIPP_USER_KEY = 'flipp-uid'; -const COMPACT_DEFAULT_HEIGHT = 600; - -let userKey = null; -export const storage = getStorageManager({bidderCode: BIDDER_CODE}); - -export function getUserKey(options = {}) { - if (userKey) { - return userKey; - } - - // If the partner provides the user key use it, otherwise fallback to cookies - if ('userKey' in options && options.userKey) { - if (isValidUserKey(options.userKey)) { - userKey = options.userKey; - return options.userKey; - } - } - - // Grab from Cookie - const foundUserKey = storage.cookiesAreEnabled(null) && storage.getCookie(FLIPP_USER_KEY, null); - if (foundUserKey && isValidUserKey(foundUserKey)) { - return foundUserKey; - } - - // Generate if none found - userKey = generateUUID(); - - // Set cookie - if (storage.cookiesAreEnabled()) { - storage.setCookie(FLIPP_USER_KEY, userKey); - } - - return userKey; -} - -function isValidUserKey(userKey) { - return typeof userKey === 'string' && !userKey.startsWith('#') && userKey.length > 0; -} - -const generateUUID = () => { - return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => { - const r = (Math.random() * 16) | 0; - const v = c === 'x' ? r : (r & 0x3) | 0x8; - return v.toString(16); - }); -}; - -/** - * Determines if a creativeType is valid - * - * @param {string} creativeType The Creative Type to validate. - * @return string creativeType if this is a valid Creative Type, and 'NativeX' otherwise. - */ -const validateCreativeType = (creativeType) => { - if (creativeType && VALID_CREATIVE_TYPES.includes(creativeType)) { - return creativeType; - } else { - return DEFAULT_CREATIVE_TYPE; - } -}; - -const getAdTypes = (creativeType) => { - if (creativeType === 'DTX') { - return DTX_TYPES; - } - return AD_TYPES; -} - -export const spec = { - code: BIDDER_CODE, - supportedMediaTypes: [BANNER], - /** - * Determines whether or not the given bid request is valid. - * - * @param {BidRequest} bid The bid params to validate. - * @return boolean True if this is a valid bid, and false otherwise. - */ - isBidRequestValid: function(bid) { - return !!(bid.params.siteId) && !!(bid.params.publisherNameIdentifier); - }, - /** - * Make a server request from the list of BidRequests. - * - * @param {BidRequest[]} validBidRequests[] an array of bids - * @param {BidderRequest} bidderRequest master bidRequest object - * @return ServerRequest Info describing the request to the server. - */ - buildRequests: function(validBidRequests, bidderRequest) { - const urlParams = parseUrl(bidderRequest.refererInfo.page).search; - const contentCode = urlParams['flipp-content-code']; - const userKey = getUserKey(validBidRequests[0]?.params); - const placements = validBidRequests.map((bid, index) => { - const options = bid.params.options || {}; - if (!options.hasOwnProperty('startCompact')) { - options.startCompact = true; - } - return { - divName: TARGET_NAME, - networkId: NETWORK_ID, - siteId: bid.params.siteId, - adTypes: getAdTypes(bid.params.creativeType), - count: 1, - ...(!isEmpty(bid.params.zoneIds) && {zoneIds: bid.params.zoneIds}), - properties: { - ...(!isEmpty(contentCode) && {contentCode: contentCode.slice(0, 32)}), - }, - options, - prebid: { - requestId: bid.bidId, - publisherNameIdentifier: bid.params.publisherNameIdentifier, - height: bid.mediaTypes.banner.sizes[index][0], - width: bid.mediaTypes.banner.sizes[index][1], - creativeType: validateCreativeType(bid.params.creativeType), - } - } - }); - return { - method: 'POST', - url: ENDPOINT, - data: { - placements, - url: bidderRequest.refererInfo.page, - user: { - key: userKey, - }, - }, - } - }, - /** - * Unpack the response from the server into a list of bids. - * - * @param {ServerResponse} serverResponse A successful response from the server. - * @param {BidRequest} bidRequest A bid request object - * @return {Bid[]} An array of bids which were nested inside the server. - */ - interpretResponse: function(serverResponse, bidRequest) { - if (!serverResponse?.body) return []; - const placements = bidRequest.data.placements; - const res = serverResponse.body; - if (!isEmpty(res) && !isEmpty(res.decisions) && !isEmpty(res.decisions.inline)) { - return res.decisions.inline.map(decision => { - const placement = placements.find(p => p.prebid.requestId === decision.prebid?.requestId); - const height = placement.options?.startCompact ? COMPACT_DEFAULT_HEIGHT : decision.height; - return { - bidderCode: BIDDER_CODE, - requestId: decision.prebid?.requestId, - cpm: decision.prebid?.cpm, - width: decision.width, - height, - creativeId: decision.adId, - currency: DEFAULT_CURRENCY, - netRevenue: true, - ttl: DEFAULT_TTL, - ad: decision.prebid?.creative, - } - }); - } - return []; - }, - - /** - * Register the user sync pixels which should be dropped after the auction. - * - * @param {SyncOptions} syncOptions Which user syncs are allowed? - * @param {ServerResponse[]} serverResponses List of server's responses. - * @return {UserSync[]} The user syncs which should be dropped. - */ - getUserSyncs: (syncOptions, serverResponses) => [], -} -registerBidder(spec); diff --git a/modules/flippBidAdapter.md b/modules/flippBidAdapter.md deleted file mode 100644 index e823432a60f..00000000000 --- a/modules/flippBidAdapter.md +++ /dev/null @@ -1,45 +0,0 @@ -# Overview - -``` -Module Name: Flipp Bid Adapter -Module Type: Bidder Adapter -Maintainer: prebid@flipp.com -``` - -# Description - -This module connects publishers to Flipp's Shopper Experience via Prebid.js. - - -# Test parameters - -```javascript -var adUnits = [ - { - code: 'flipp-scroll-ad-content', - mediaTypes: { - banner: { - sizes: [ - [300, 600] - ] - } - }, - bids: [ - { - bidder: 'flipp', - params: { - creativeType: 'NativeX', // Optional, can be one of 'NativeX' (default) or 'DTX' - publisherNameIdentifier: 'wishabi-test-publisher', // Required - siteId: 1192075, // Required - zoneIds: [260678], // Optional - userKey: ``, // Optional, but recommended for better user experience. Can be a cookie, session id or any other user identifier - options: { - startCompact: true, // Optional. Height of the experience will be reduced. Default to true - dwellExpand: true // Optional. Auto expand the experience after a certain time passes. Default to true - } - } - } - ] - } -] -``` diff --git a/modules/fluctBidAdapter.js b/modules/fluctBidAdapter.js index f6d97fa7cd8..edb750a6b90 100644 --- a/modules/fluctBidAdapter.js +++ b/modules/fluctBidAdapter.js @@ -2,17 +2,22 @@ import { _each, deepSetValue, isEmpty } from '../src/utils.js'; import { config } from '../src/config.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').validBidRequests} validBidRequests - */ - const BIDDER_CODE = 'fluct'; const END_POINT = 'https://hb.adingo.jp/prebid'; const VERSION = '1.2'; const NET_REVENUE = true; const TTL = 300; +/** + * See modules/userId/eids.js for supported sources + */ +const SUPPORTED_USER_ID_SOURCES = [ + 'adserver.org', + 'criteo.com', + 'intimatemerger.com', + 'liveramp.com', +]; + export const spec = { code: BIDDER_CODE, aliases: ['adingo'], @@ -31,7 +36,6 @@ export const spec = { * Make a server request from the list of BidRequests. * * @param {validBidRequests[]} - an array of bids. - * @param {bidderRequest} bidderRequest bidder request object. * @return ServerRequest Info describing the request to the server. */ buildRequests: (validBidRequests, bidderRequest) => { @@ -39,24 +43,16 @@ export const spec = { const page = bidderRequest.refererInfo.page; _each(validBidRequests, (request) => { - const impExt = request.ortb2Imp?.ext; const data = Object(); data.page = page; data.adUnitCode = request.adUnitCode; data.bidId = request.bidId; + data.transactionId = request.ortb2Imp?.ext?.tid; data.user = { - data: bidderRequest.ortb2?.user?.data ?? [], - eids: [ - ...(request.userIdAsEids ?? []), - ...(bidderRequest.ortb2?.user?.ext?.eids ?? []), - ], + eids: (request.userIdAsEids || []).filter((eid) => SUPPORTED_USER_ID_SOURCES.indexOf(eid.source) !== -1) }; - if (impExt) { - data.transactionId = impExt.tid; - data.gpid = impExt.gpid ?? impExt.data?.pbadslot ?? impExt.data?.adserver?.adslot; - } if (bidderRequest.gdprConsent) { deepSetValue(data, 'regs.gdpr', { consent: bidderRequest.gdprConsent.consentString, diff --git a/modules/freepassBidAdapter.js b/modules/freepassBidAdapter.js deleted file mode 100644 index cdcc3c6a4b0..00000000000 --- a/modules/freepassBidAdapter.js +++ /dev/null @@ -1,117 +0,0 @@ -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'; - -const converter = ortbConverter({ - context: { - netRevenue: true, - ttl: 30 - } -}); - -function prepareUserInfo(user, freepassId) { - let userInfo = user || {}; - let extendedUserInfo = userInfo.ext || {}; - - if (freepassId.userId) { - userInfo.id = freepassId.userId; - } - - if (freepassId.commonId) { - extendedUserInfo.fuid = freepassId.commonId; - } - userInfo.ext = extendedUserInfo; - - return userInfo; -} - -function prepareDeviceInfo(device, freepassId) { - let deviceInfo = device || {}; - let extendedDeviceInfo = deviceInfo.ext || {}; - - extendedDeviceInfo.is_accurate_ip = 0; - if (freepassId.userIp) { - deviceInfo.ip = freepassId.userIp; - extendedDeviceInfo.is_accurate_ip = 1; - } - deviceInfo.ext = extendedDeviceInfo; - - return deviceInfo; -} - -export const spec = { - code: 'freepass', - supportedMediaTypes: [BANNER], - - isBidRequestValid(bid) { - logMessage('Validating bid: ', bid); - return !(!bid.adUnitCode || !bid.params || !bid.params.publisherId); - }, - - buildRequests(validBidRequests, bidderRequest) { - if (validBidRequests.length === 0) { - logMessage('FreePass BidAdapter has no valid bid requests'); - return []; - } - - logMessage('FreePass BidAdapter is preparing bid request: ', validBidRequests); - logMessage('FreePass BidAdapter is using bidder request: ', bidderRequest); - - const data = converter.toORTB({ - bidderRequest: bidderRequest, - bidRequests: validBidRequests, - context: { mediaType: BANNER } - }); - logMessage('FreePass BidAdapter interpreted ORTB bid request as ', data); - - // Only freepassId is supported - let freepassId = (validBidRequests[0].userId && validBidRequests[0].userId.freepassId) || {}; - data.user = prepareUserInfo(data.user, freepassId); - data.device = prepareDeviceInfo(data.device, freepassId); - - // set site.page & site.publisher - data.site = data.site || {}; - data.site.publisher = data.site.publisher || {}; - // set site.publisher.id. from params.publisherId required - data.site.publisher.id = validBidRequests[0].params.publisherId; - // set site.publisher.domain from params.publisherUrl. optional - data.site.publisher.domain = validBidRequests[0].params?.publisherUrl; - - // set source - data.source = data.source || {}; - data.source.fd = 0; - data.source.tid = validBidRequests.ortb2?.source?.tid; - data.source.pchain = ''; - - // set imp.ext - validBidRequests.forEach((bidRequest, index) => { - data.imp[index].tagId = bidRequest.adUnitCode; - }); - - data.test = validBidRequests[0].test || 0; - - logMessage('FreePass BidAdapter augmented ORTB bid request user: ', data.user); - logMessage('FreePass BidAdapter augmented ORTB bid request device: ', data.device); - - return { - method: 'POST', - url: BIDDER_SERVICE_URL, - data, - options: { withCredentials: false } - }; - }, - - 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; - logMessage('FreePass BidAdapter interpreted ORTB bids as ', bids); - - return bids; - }, -}; - -registerBidder(spec); diff --git a/modules/freepassBidAdapter.md b/modules/freepassBidAdapter.md deleted file mode 100644 index 7b56a469583..00000000000 --- a/modules/freepassBidAdapter.md +++ /dev/null @@ -1,34 +0,0 @@ -# Overview - -``` -Module Name: FreePass Bidder Adapter -Module Type: Bidder Adapter -Maintainer: fp-hbidding@freebit.net -``` - -# Description - -Connects to FreePass service for bids. Only BANNER is currently supported. - -This BidAdapter requires the FreePass IdSystem to be configured. Please contact FreePass for proper setup. - -# Test Parameters -```javascript - let adUnits = [ - { - code: 'ad-banner-1', // ad slot HTML element ID - mediaTypes: { - banner: { - sizes: [[1024, 1024]] - } - }, - bids: [{ - bidder: 'freepass', - params: { - publisherId: '12345' - } - }] - } - ]; -``` - diff --git a/modules/freepassIdSystem.js b/modules/freepassIdSystem.js index 419aa9ec414..d52c537e800 100644 --- a/modules/freepassIdSystem.js +++ b/modules/freepassIdSystem.js @@ -1,12 +1,8 @@ import { submodule } from '../src/hook.js'; -import { logMessage } from '../src/utils.js'; -import { getCoreStorageManager } from '../src/storageManager.js'; +import {generateUUID, logMessage} from '../src/utils.js'; const MODULE_NAME = 'freepassId'; -export const FREEPASS_COOKIE_KEY = '_f_UF8cCRlr'; -export const storage = getCoreStorageManager(MODULE_NAME); - export const freepassIdSubmodule = { name: MODULE_NAME, decode: function (value, config) { @@ -19,12 +15,7 @@ export const freepassIdSubmodule = { logMessage('Getting FreePass ID using config: ' + JSON.stringify(config)); const freepassData = config.params !== undefined ? (config.params.freepassData || {}) : {} - const idObject = {}; - - const userId = storage.getCookie(FREEPASS_COOKIE_KEY); - if (userId !== null) { - idObject.userId = userId; - } + let idObject = {userId: generateUUID()}; if (freepassData.commonId !== undefined) { idObject.commonId = config.params.freepassData.commonId; @@ -38,8 +29,8 @@ export const freepassIdSubmodule = { }, extendId: function (config, consent, cachedIdObject) { - const freepassData = config.params.freepassData; - const hasFreepassData = freepassData !== undefined; + let freepassData = config.params.freepassData; + let hasFreepassData = freepassData !== undefined; if (!hasFreepassData) { logMessage('No Freepass Data. CachedIdObject will not be extended: ' + JSON.stringify(cachedIdObject)); return { @@ -47,7 +38,12 @@ export const freepassIdSubmodule = { }; } - const currentCookieId = storage.getCookie(FREEPASS_COOKIE_KEY); + if (freepassData.commonId === cachedIdObject.commonId && freepassData.userIp === cachedIdObject.userIp) { + logMessage('FreePass ID is already up-to-date: ' + JSON.stringify(cachedIdObject)); + return { + id: cachedIdObject + }; + } logMessage('Extending FreePass ID object: ' + JSON.stringify(cachedIdObject)); logMessage('Extending FreePass ID using config: ' + JSON.stringify(config)); @@ -56,8 +52,8 @@ export const freepassIdSubmodule = { id: { commonId: freepassData.commonId, userIp: freepassData.userIp, - userId: currentCookieId - } + userId: cachedIdObject.userId, + }, }; } }; diff --git a/modules/freewheel-sspBidAdapter.js b/modules/freewheel-sspBidAdapter.js index c4653181fd0..cd4785cdc78 100644 --- a/modules/freewheel-sspBidAdapter.js +++ b/modules/freewheel-sspBidAdapter.js @@ -3,13 +3,7 @@ import { BANNER, VIDEO } from '../src/mediaTypes.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { config } from '../src/config.js'; -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - */ - const BIDDER_CODE = 'freewheel-ssp'; -const GVL_ID = 285; const PROTOCOL = getProtocol(); const FREEWHEEL_ADSSETUP = PROTOCOL + '://ads.stickyadstv.com/www/delivery/swfIndex.php'; @@ -188,8 +182,8 @@ function getCampaignId(xmlNode) { } /** - * returns the top most accessible window - */ +* returns the top most accessible window +*/ function getTopMostWindow() { var res = window; @@ -320,25 +314,24 @@ var getOutstreamScript = function(bid) { export const spec = { code: BIDDER_CODE, - gvlid: GVL_ID, supportedMediaTypes: [BANNER, VIDEO], aliases: ['stickyadstv', 'freewheelssp'], // aliases for freewheel-ssp /** - * Determines whether or not the given bid request is valid. - * - * @param {object} bid The bid to validate. - * @return boolean True if this is a valid bid, and false otherwise. - */ + * Determines whether or not the given bid request is valid. + * + * @param {object} bid The bid to validate. + * @return boolean True if this is a valid bid, and false otherwise. + */ isBidRequestValid: function(bid) { return !!(bid.params.zoneId); }, /** - * Make a server request from the list of BidRequests. - * - * @param {BidRequest[]} bidRequests A non-empty list of bid requests which should be sent to the Server. - * @return ServerRequest Info describing the request to the server. - */ + * Make a server request from the list of BidRequests. + * + * @param {BidRequest[]} bidRequests A non-empty list of bid requests which should be sent to the Server. + * @return ServerRequest Info describing the request to the server. + */ buildRequests: function(bidRequests, bidderRequest) { // var currency = config.getConfig(currency); @@ -389,15 +382,6 @@ export const spec = { requestParams.gpp_sid = bidderRequest.ortb2.regs.gpp_sid; } - // Add content object - if (bidderRequest && bidderRequest.ortb2 && bidderRequest.ortb2.site && bidderRequest.ortb2.site.content && typeof bidderRequest.ortb2.site.content === 'object') { - try { - requestParams._fw_prebid_content = JSON.stringify(bidderRequest.ortb2.site.content); - } catch (error) { - logWarn('PREBID - ' + BIDDER_CODE + ': Unable to stringify the content object: ' + error); - } - } - // Add schain object var schain = currentBidRequest.schain; if (schain) { @@ -479,12 +463,12 @@ export const spec = { }, /** - * Unpack the response from the server into a list of bids. - * - * @param {*} serverResponse A successful response from the server. - * @param {object} request: the built request object containing the initial bidRequest. - * @return {Bid[]} An array of bids which were nested inside the server. - */ + * Unpack the response from the server into a list of bids. + * + * @param {*} serverResponse A successful response from the server. + * @param {object} request: the built request object containing the initial bidRequest. + * @return {Bid[]} An array of bids which were nested inside the server. + */ interpretResponse: function(serverResponse, request) { var bidrequest = request.bidRequest; var playerSize = []; @@ -548,11 +532,10 @@ export const spec = { }; if (bidrequest.mediaTypes.video) { + bidResponse.vastXml = serverResponse; bidResponse.mediaType = 'video'; } - bidResponse.vastXml = serverResponse; - bidResponse.ad = formatAdHTML(bidrequest, playerSize); bidResponses.push(bidResponse); } diff --git a/modules/ftrackIdSystem.js b/modules/ftrackIdSystem.js index 1794c3f76f4..5f09a315b34 100644 --- a/modules/ftrackIdSystem.js +++ b/modules/ftrackIdSystem.js @@ -12,13 +12,6 @@ import {uspDataHandler} from '../src/adapterManager.js'; import {loadExternalScript} from '../src/adloader.js'; import {MODULE_TYPE_UID} from '../src/activities/modules.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 = 'ftrackId'; const LOG_PREFIX = 'FTRACK - '; const LOCAL_STORAGE_EXP_DAYS = 30; @@ -228,22 +221,6 @@ export const ftrackIdSubmodule = { if (usPrivacyVersion == 1 && usPrivacyOptOutSale === 'Y') consentValue = false; return consentValue; - }, - eids: { - 'ftrackId': { - source: 'flashtalking.com', - atype: 1, - getValue: function(data) { - let value = ''; - if (data && data.ext && data.ext.DeviceID) { - value = data.ext.DeviceID; - } - return value; - }, - getUidExt: function(data) { - return data && data.ext; - } - }, } }; diff --git a/modules/gammaBidAdapter.js b/modules/gammaBidAdapter.js index 40abdd81930..279eb78812e 100644 --- a/modules/gammaBidAdapter.js +++ b/modules/gammaBidAdapter.js @@ -1,10 +1,5 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - */ - const ENDPOINT = 'https://hb.gammaplatform.com'; const ENDPOINT_USERSYNC = 'https://cm-supply-web.gammaplatform.com'; const BIDDER_CODE = 'gamma'; diff --git a/modules/gdprEnforcement.js b/modules/gdprEnforcement.js index 5b73ec19e08..4bac4b0cf74 100644 --- a/modules/gdprEnforcement.js +++ b/modules/gdprEnforcement.js @@ -5,9 +5,10 @@ import {deepAccess, logError, logWarn} from '../src/utils.js'; import {config} from '../src/config.js'; import adapterManager, {gdprDataHandler} from '../src/adapterManager.js'; +import {find} from '../src/polyfill.js'; import * as events from '../src/events.js'; import CONSTANTS from '../src/constants.json'; -import {GDPR_GVLIDS, VENDORLESS_GVLID, FIRST_PARTY_GVLID} from '../src/consentHandler.js'; +import {GDPR_GVLIDS, VENDORLESS_GVLID} from '../src/consentHandler.js'; import { MODULE_TYPE_ANALYTICS, MODULE_TYPE_BIDDER, @@ -23,65 +24,44 @@ import { import {registerActivityControl} from '../src/activities/rules.js'; import { ACTIVITY_ACCESS_DEVICE, - ACTIVITY_ENRICH_EIDS, ACTIVITY_ENRICH_UFPD, + ACTIVITY_ENRICH_EIDS, ACTIVITY_FETCH_BIDS, ACTIVITY_REPORT_ANALYTICS, - ACTIVITY_SYNC_USER, ACTIVITY_TRANSMIT_EIDS, ACTIVITY_TRANSMIT_PRECISE_GEO, ACTIVITY_TRANSMIT_UFPD + ACTIVITY_SYNC_USER } from '../src/activities/activities.js'; export const STRICT_STORAGE_ENFORCEMENT = 'strictStorageEnforcement'; -export const ACTIVE_RULES = { - purpose: {}, - feature: {} +const TCF2 = { + 'purpose1': {id: 1, name: 'storage'}, + 'purpose2': {id: 2, name: 'basicAds'}, + 'purpose7': {id: 7, name: 'measurement'} }; -const CONSENT_PATHS = { - purpose: 'purpose.consents', - feature: 'specialFeatureOptins' -}; - -const CONFIGURABLE_RULES = { - storage: { - type: 'purpose', - default: { - purpose: 'storage', - enforcePurpose: true, - enforceVendor: true, - vendorExceptions: [] - }, - id: 1, - }, - basicAds: { - type: 'purpose', - id: 2, - default: { - purpose: 'basicAds', - enforcePurpose: true, - enforceVendor: true, - vendorExceptions: [] - } - }, - personalizedAds: { - type: 'purpose', - id: 4, - }, - measurement: { - type: 'purpose', - id: 7, - }, - transmitPreciseGeo: { - type: 'feature', - id: 1, - }, -}; +/* + These rules would be used if `consentManagement.gdpr.rules` is undefined by the publisher. +*/ +const DEFAULT_RULES = [{ + purpose: 'storage', + enforcePurpose: true, + enforceVendor: true, + vendorExceptions: [] +}, { + purpose: 'basicAds', + enforcePurpose: true, + enforceVendor: true, + vendorExceptions: [] +}]; + +export let purpose1Rule; +export let purpose2Rule; +export let purpose7Rule; + +export let enforcementRules; const storageBlocked = new Set(); const biddersBlocked = new Set(); const analyticsBlocked = new Set(); -const ufpdBlocked = new Set(); -const eidsBlocked = new Set(); -const geoBlocked = new Set(); let hooksAdded = false; let strictStorageEnforcement = false; @@ -96,9 +76,6 @@ const GVLID_LOOKUP_PRIORITY = [ const RULE_NAME = 'TCF2'; const RULE_HANDLES = []; -// in JS we do not have access to the GVL; assume that everyone declares legitimate interest for basic ads -const LI_PURPOSES = [2]; - /** * Retrieve a module's GVL ID. */ @@ -111,7 +88,7 @@ export function getGvlid(moduleType, moduleName, fallbackFn) { if (gvlMapping && gvlMapping[moduleName]) { return gvlMapping[moduleName]; } else if (moduleType === MODULE_TYPE_PREBID) { - return moduleName === 'cdep' ? FIRST_PARTY_GVLID : VENDORLESS_GVLID; + return VENDORLESS_GVLID; } else { let {gvlid, modules} = GDPR_GVLIDS.get(moduleName); if (gvlid == null && Object.keys(modules).length > 0) { @@ -163,17 +140,6 @@ export function shouldEnforce(consentData, purpose, name) { return consentData && consentData.gdprApplies; } -function getConsent(consentData, type, id, gvlId) { - let purpose = !!deepAccess(consentData, `vendorData.${CONSENT_PATHS[type]}.${id}`); - let vendor = !!deepAccess(consentData, `vendorData.vendor.consents.${gvlId}`); - - if (type === 'purpose' && LI_PURPOSES.includes(id)) { - purpose ||= !!deepAccess(consentData, `vendorData.purpose.legitimateInterests.${id}`); - vendor ||= !!deepAccess(consentData, `vendorData.vendor.legitimateInterests.${gvlId}`); - } - return {purpose, vendor}; -} - /** * This function takes in a rule and consentData and validates against the consentData provided. Depending on what it returns, * the caller may decide to suppress a TCF-sensitive activity. @@ -184,32 +150,55 @@ function getConsent(consentData, type, id, gvlId) { * @returns {boolean} */ export function validateRules(rule, consentData, currentModule, gvlId) { - const ruleOptions = CONFIGURABLE_RULES[rule.purpose]; + const purposeId = TCF2[Object.keys(TCF2).filter(purposeName => TCF2[purposeName].name === rule.purpose)[0]].id; // return 'true' if vendor present in 'vendorExceptions' if ((rule.vendorExceptions || []).includes(currentModule)) { return true; } - const vendorConsentRequred = rule.enforceVendor && !((gvlId === VENDORLESS_GVLID || (rule.softVendorExceptions || []).includes(currentModule))); - const {purpose, vendor} = getConsent(consentData, ruleOptions.type, ruleOptions.id, gvlId); - - let validation = (!rule.enforcePurpose || purpose) && (!vendorConsentRequred || vendor); - - if (gvlId === FIRST_PARTY_GVLID) { - validation = (!rule.enforcePurpose || !!deepAccess(consentData, `vendorData.publisher.consents.${ruleOptions.id}`)); + const vendorConsentRequred = !((gvlId === VENDORLESS_GVLID || (rule.softVendorExceptions || []).includes(currentModule))); + + // get data from the consent string + const purposeConsent = deepAccess(consentData, `vendorData.purpose.consents.${purposeId}`); + const vendorConsent = vendorConsentRequred ? deepAccess(consentData, `vendorData.vendor.consents.${gvlId}`) : true; + const liTransparency = deepAccess(consentData, `vendorData.purpose.legitimateInterests.${purposeId}`); + + /* + Since vendor exceptions have already been handled, the purpose as a whole is allowed if it's not being enforced + or the user has consented. Similar with vendors. + */ + const purposeAllowed = rule.enforcePurpose === false || purposeConsent === true; + const vendorAllowed = rule.enforceVendor === false || vendorConsent === true; + + /* + Few if any vendors should be declaring Legitimate Interest for Device Access (Purpose 1), but some are claiming + LI for Basic Ads (Purpose 2). Prebid.js can't check to see who's declaring what legal basis, so if LI has been + established for Purpose 2, allow the auction to take place and let the server sort out the legal basis calculation. + */ + if (purposeId === 2) { + return (purposeAllowed && vendorAllowed) || (liTransparency === true); } - return validation; + return purposeAllowed && vendorAllowed; } -function gdprRule(purposeNo, checkConsent, blocked = null, gvlidFallback = () => null) { +/** + * all activity rules follow the same structure: + * if GDPR is in scope, check configuration for a particular purpose, and if that enables enforcement, + * check against consent data for that purpose and vendor + * + * @param purposeNo TCF purpose number to check for this activity + * @param getEnforcementRule getter for gdprEnforcement rule definition to use + * @param blocked optional set to use for collecting denied vendors + * @param gvlidFallback optional factory function for a gvlid falllback function + */ +function gdprRule(purposeNo, getEnforcementRule, blocked = null, gvlidFallback = () => null) { return function (params) { const consentData = gdprDataHandler.getConsentData(); const modName = params[ACTIVITY_PARAM_COMPONENT_NAME]; - if (shouldEnforce(consentData, purposeNo, modName)) { const gvlid = getGvlid(params[ACTIVITY_PARAM_COMPONENT_TYPE], modName, gvlidFallback(params)); - let allow = !!checkConsent(consentData, modName, gvlid); + let allow = !!validateRules(getEnforcementRule(), consentData, modName, gvlid); if (!allow) { blocked && blocked.add(modName); return {allow}; @@ -218,62 +207,30 @@ function gdprRule(purposeNo, checkConsent, blocked = null, gvlidFallback = () => }; } -function singlePurposeGdprRule(purposeNo, blocked = null, gvlidFallback = () => null) { - return gdprRule(purposeNo, (cd, modName, gvlid) => !!validateRules(ACTIVE_RULES.purpose[purposeNo], cd, modName, gvlid), blocked, gvlidFallback); -} +export const accessDeviceRule = ((rule) => { + return function (params) { + // for vendorless (core) storage, do not enforce rules unless strictStorageEnforcement is set + if (params[ACTIVITY_PARAM_COMPONENT_TYPE] === MODULE_TYPE_PREBID && !strictStorageEnforcement) return; + return rule(params); + }; +})(gdprRule(1, () => purpose1Rule, storageBlocked)); -function exceptPrebidModules(ruleFn) { +export const syncUserRule = gdprRule(1, () => purpose1Rule, storageBlocked); +export const enrichEidsRule = gdprRule(1, () => purpose1Rule, storageBlocked); + +export const fetchBidsRule = ((rule) => { return function (params) { - if (params[ACTIVITY_PARAM_COMPONENT_TYPE] === MODULE_TYPE_PREBID) { + if (params[ACTIVITY_PARAM_COMPONENT_TYPE] !== MODULE_TYPE_BIDDER) { // TODO: this special case is for the PBS adapter (componentType is 'prebid') // we should check for generic purpose 2 consent & vendor consent based on the PBS vendor's GVL ID; // that is, however, a breaking change and skipped for now return; } - return ruleFn(params); - }; -} - -export const accessDeviceRule = ((rule) => { - return function (params) { - // for vendorless (core) storage, do not enforce rules unless strictStorageEnforcement is set - if (params[ACTIVITY_PARAM_COMPONENT_TYPE] === MODULE_TYPE_PREBID && !strictStorageEnforcement) return; return rule(params); }; -})(singlePurposeGdprRule(1, storageBlocked)); - -export const syncUserRule = singlePurposeGdprRule(1, storageBlocked); -export const enrichEidsRule = singlePurposeGdprRule(1, storageBlocked); -export const fetchBidsRule = exceptPrebidModules(singlePurposeGdprRule(2, biddersBlocked)); -export const reportAnalyticsRule = singlePurposeGdprRule(7, analyticsBlocked, (params) => getGvlidFromAnalyticsAdapter(params[ACTIVITY_PARAM_COMPONENT_NAME], params[ACTIVITY_PARAM_ANL_CONFIG])); -export const ufpdRule = singlePurposeGdprRule(4, ufpdBlocked); - -export const transmitEidsRule = exceptPrebidModules((() => { - // Transmit EID special case: - // by default, legal basis or vendor exceptions for any purpose between 2 and 10 - // (but disregarding enforcePurpose and enforceVendor config) is enough to allow EIDs through - function check2to10Consent(consentData, modName, gvlId) { - for (let pno = 2; pno <= 10; pno++) { - if (ACTIVE_RULES.purpose[pno]?.vendorExceptions?.includes(modName)) { - return true; - } - const {purpose, vendor} = getConsent(consentData, 'purpose', pno, gvlId); - if (purpose && (vendor || ACTIVE_RULES.purpose[pno]?.softVendorExceptions?.includes(modName))) { - return true; - } - } - return false; - } - - const defaultBehavior = gdprRule('2-10', check2to10Consent, eidsBlocked); - const p4Behavior = singlePurposeGdprRule(4, eidsBlocked); - return function () { - const fn = ACTIVE_RULES.purpose[4]?.eidsRequireP4Consent ? p4Behavior : defaultBehavior; - return fn.apply(this, arguments); - }; -})()); +})(gdprRule(2, () => purpose2Rule, biddersBlocked)); -export const transmitPreciseGeoRule = gdprRule('Special Feature 1', (cd, modName, gvlId) => validateRules(ACTIVE_RULES.feature[1], cd, modName, gvlId), geoBlocked); +export const reportAnalyticsRule = gdprRule(7, () => purpose7Rule, analyticsBlocked, (params) => getGvlidFromAnalyticsAdapter(params[ACTIVITY_PARAM_COMPONENT_NAME], params[ACTIVITY_PARAM_ANL_CONFIG])); /** * Compiles the TCF2.0 enforcement results into an object, which is emitted as an event payload to "tcf2Enforcement" event. @@ -286,57 +243,67 @@ function emitTCF2FinalResults() { const tcf2FinalResults = { storageBlocked: formatSet(storageBlocked), biddersBlocked: formatSet(biddersBlocked), - analyticsBlocked: formatSet(analyticsBlocked), - ufpdBlocked: formatSet(ufpdBlocked), - eidsBlocked: formatSet(eidsBlocked), - geoBlocked: formatSet(geoBlocked) + analyticsBlocked: formatSet(analyticsBlocked) }; events.emit(CONSTANTS.EVENTS.TCF2_ENFORCEMENT, tcf2FinalResults); - [storageBlocked, biddersBlocked, analyticsBlocked, ufpdBlocked, eidsBlocked, geoBlocked].forEach(el => el.clear()); + [storageBlocked, biddersBlocked, analyticsBlocked].forEach(el => el.clear()); } events.on(CONSTANTS.EVENTS.AUCTION_END, emitTCF2FinalResults); +/* + Set of callback functions used to detect presence of a TCF rule, passed as the second argument to find(). +*/ +const hasPurpose1 = (rule) => { + return rule.purpose === TCF2.purpose1.name; +}; +const hasPurpose2 = (rule) => { + return rule.purpose === TCF2.purpose2.name; +}; +const hasPurpose7 = (rule) => { + return rule.purpose === TCF2.purpose7.name; +}; + /** * A configuration function that initializes some module variables, as well as adds hooks * @param {Object} config - GDPR enforcement config object */ export function setEnforcementConfig(config) { - let rules = deepAccess(config, 'gdpr.rules'); + const rules = deepAccess(config, 'gdpr.rules'); if (!rules) { logWarn('TCF2: enforcing P1 and P2 by default'); + enforcementRules = DEFAULT_RULES; + } else { + enforcementRules = rules; } - rules = Object.fromEntries((rules || []).map(r => [r.purpose, r])); strictStorageEnforcement = !!deepAccess(config, STRICT_STORAGE_ENFORCEMENT); - Object.entries(CONFIGURABLE_RULES).forEach(([name, opts]) => { - ACTIVE_RULES[opts.type][opts.id] = rules[name] ?? opts.default; - }); + purpose1Rule = find(enforcementRules, hasPurpose1); + purpose2Rule = find(enforcementRules, hasPurpose2); + purpose7Rule = find(enforcementRules, hasPurpose7); + + if (!purpose1Rule) { + purpose1Rule = DEFAULT_RULES[0]; + } + + if (!purpose2Rule) { + purpose2Rule = DEFAULT_RULES[1]; + } if (!hooksAdded) { - if (ACTIVE_RULES.purpose[1] != null) { + if (purpose1Rule) { hooksAdded = true; RULE_HANDLES.push(registerActivityControl(ACTIVITY_ACCESS_DEVICE, RULE_NAME, accessDeviceRule)); RULE_HANDLES.push(registerActivityControl(ACTIVITY_SYNC_USER, RULE_NAME, syncUserRule)); RULE_HANDLES.push(registerActivityControl(ACTIVITY_ENRICH_EIDS, RULE_NAME, enrichEidsRule)); } - if (ACTIVE_RULES.purpose[2] != null) { + if (purpose2Rule) { RULE_HANDLES.push(registerActivityControl(ACTIVITY_FETCH_BIDS, RULE_NAME, fetchBidsRule)); } - if (ACTIVE_RULES.purpose[4] != null) { - RULE_HANDLES.push( - registerActivityControl(ACTIVITY_TRANSMIT_UFPD, RULE_NAME, ufpdRule), - registerActivityControl(ACTIVITY_ENRICH_UFPD, RULE_NAME, ufpdRule) - ); - } - if (ACTIVE_RULES.purpose[7] != null) { + if (purpose7Rule) { RULE_HANDLES.push(registerActivityControl(ACTIVITY_REPORT_ANALYTICS, RULE_NAME, reportAnalyticsRule)); } - if (ACTIVE_RULES.feature[1] != null) { - RULE_HANDLES.push(registerActivityControl(ACTIVITY_TRANSMIT_PRECISE_GEO, RULE_NAME, transmitPreciseGeoRule)); - } - RULE_HANDLES.push(registerActivityControl(ACTIVITY_TRANSMIT_EIDS, RULE_NAME, transmitEidsRule)); } } diff --git a/modules/genericAnalyticsAdapter.js b/modules/genericAnalyticsAdapter.js index 7f721863912..b52cb7e5464 100644 --- a/modules/genericAnalyticsAdapter.js +++ b/modules/genericAnalyticsAdapter.js @@ -1,6 +1,6 @@ import AnalyticsAdapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import {prefixLog, isPlainObject} from '../src/utils.js'; -import {has as hasEvent} from '../src/events.js'; +import * as CONSTANTS from '../src/constants.json'; import adapterManager from '../src/adapterManager.js'; import {ajaxBuilder} from '../src/ajax.js'; @@ -48,12 +48,12 @@ export function GenericAnalytics() { return false; } for (const [event, handler] of Object.entries(options.events)) { - if (!hasEvent(event)) { + if (!CONSTANTS.EVENTS.hasOwnProperty(event)) { logWarn(`options.events.${event} does not match any known Prebid event`); - } - if (typeof handler !== 'function') { - logError(`options.events.${event} must be a function`); - return false; + if (typeof handler !== 'function') { + logError(`options.events.${event} must be a function`); + return false; + } } } } diff --git a/modules/geoedgeRtdProvider.js b/modules/geoedgeRtdProvider.js index a2ed71a898c..6f910632fbc 100644 --- a/modules/geoedgeRtdProvider.js +++ b/modules/geoedgeRtdProvider.js @@ -17,16 +17,9 @@ import { submodule } from '../src/hook.js'; import { ajax } from '../src/ajax.js'; -import { generateUUID, createInvisibleIframe, insertElement, isEmpty, logError } from '../src/utils.js'; +import { generateUUID, insertElement, isEmpty, logError } from '../src/utils.js'; import * as events from '../src/events.js'; import CONSTANTS from '../src/constants.json'; -import { loadExternalScript } from '../src/adloader.js'; -import { auctionManager } from '../src/auctionManager.js'; -import { getRefererInfo } from '../src/refererDetection.js'; - -/** - * @typedef {import('../modules/rtdModule/index.js').RtdSubmodule} RtdSubmodule - */ /** @type {string} */ const SUBMODULE_NAME = 'geoedge'; @@ -40,13 +33,9 @@ const PV_ID = generateUUID(); /** @type {string} */ const HOST_NAME = 'https://rumcdn.geoedge.be'; /** @type {string} */ -const FILE_NAME_CLIENT = 'grumi.js'; -/** @type {string} */ -const FILE_NAME_INPAGE = 'grumi-ip.js'; -/** @type {function} */ -export let getClientUrl = (key) => `${HOST_NAME}/${key}/${FILE_NAME_CLIENT}`; +const FILE_NAME = 'grumi.js'; /** @type {function} */ -export let getInPageUrl = (key) => `${HOST_NAME}/${key}/${FILE_NAME_INPAGE}`; +export let getClientUrl = (key) => `${HOST_NAME}/${key}/${FILE_NAME}`; /** @type {string} */ export let wrapper /** @type {boolean} */; @@ -74,38 +63,17 @@ export function setWrapper(responseText) { wrapper = responseText; } -export function getInitialParams(key) { - let refererInfo = getRefererInfo(); - let params = { - wver: 'pbjs', - wtype: 'pbjs-module', - key, - meta: { - topUrl: refererInfo.page - }, - site: refererInfo.domain, - pimp: PV_ID, - fsRan: true, - frameApi: true - }; - return params; -} - -export function markAsLoaded() { - preloaded = true; -} - /** * preloads the client - * @param {string} key + * @param {string} key */ export function preloadClient(key) { - let iframe = createInvisibleIframe(); - iframe.id = 'grumiFrame'; - insertElement(iframe); - iframe.contentWindow.grumi = getInitialParams(key); - let url = getClientUrl(key); - loadExternalScript(url, SUBMODULE_NAME, markAsLoaded, iframe.contentDocument); + let link = document.createElement('link'); + link.rel = 'preload'; + link.as = 'script'; + link.href = getClientUrl(key); + link.onload = () => { preloaded = true }; + insertElement(link); } /** @@ -129,7 +97,7 @@ export function wrapHtml(wrapper, html) { * @param {string} key * @return {Object} */ -export function getMacros(bid, key) { +function getMacros(bid, key) { return { '${key}': key, '%%ADUNIT%%': bid.adUnitCode, @@ -142,9 +110,7 @@ export function getMacros(bid, key) { '%_hbadomains': bid.meta && bid.meta.advertiserDomains, '%%PATTERN:hb_pb%%': bid.pbHg, '%%SITE%%': location.hostname, - '%_pimp%': PV_ID, - '%_hbCpm!': bid.cpm, - '%_hbCurrency!': bid.currency + '%_pimp%': PV_ID }; } @@ -211,8 +177,7 @@ function isSupportedBidder(bidder, paramsBidders) { function shouldWrap(bid, params) { let supportedBidder = isSupportedBidder(bid.bidderCode, params.bidders); let donePreload = params.wap ? preloaded : true; - let isGPT = params.gpt; - return wrapperReady && supportedBidder && donePreload && !isGPT; + return wrapperReady && supportedBidder && donePreload; } function conditionallyWrap(bidResponse, config, userConsent) { @@ -222,55 +187,31 @@ function conditionallyWrap(bidResponse, config, userConsent) { } } -function isBillingMessage(data, params) { - return data.key === params.key && data.impression; -} - /** - * Fire billable events when our client sends a message - * Messages will be sent only when: - * a. applicable bids are wrapped - * b. our code laoded and executed sucesfully + * Fire billable events for applicable bids */ function fireBillableEventsForApplicableBids(params) { - window.addEventListener('message', function (message) { - let data = message.data; - if (isBillingMessage(data, params)) { - let winningBid = auctionManager.findBidByAdId(data.adId); + events.on(CONSTANTS.EVENTS.BID_WON, function (winningBid) { + if (shouldWrap(winningBid, params)) { events.emit(CONSTANTS.EVENTS.BILLABLE_EVENT, { vendor: SUBMODULE_NAME, - billingId: data.impressionId, - type: winningBid ? 'impression' : data.type, - transactionId: winningBid?.transactionId || data.transactionId, - auctionId: winningBid?.auctionId || data.auctionId, - bidId: winningBid?.requestId || data.requestId + billingId: generateUUID(), + type: 'impression', + transactionId: winningBid.transactionId, + auctionId: winningBid.auctionId, + bidId: winningBid.requestId }); } }); } -/** - * Loads Geoedge in page script that monitors all ad slots created by GPT - * @param {Object} params - */ -function setupInPage(params) { - window.grumi = params; - window.grumi.fromPrebid = true; - loadExternalScript(getInPageUrl(params.key), SUBMODULE_NAME); -} - function init(config, userConsent) { let params = config.params; if (!params || !params.key) { logError('missing key for geoedge RTD module provider'); return false; } - if (params.gpt) { - setupInPage(params); - } else { - fetchWrapper(setWrapper); - preloadClient(params.key); - } + preloadClient(params.key); fireBillableEventsForApplicableBids(params); return true; } @@ -278,12 +219,17 @@ function init(config, userConsent) { /** @type {RtdSubmodule} */ export const geoedgeSubmodule = { /** - * used to link submodule with realTimeData - * @type {string} - */ + * used to link submodule with realTimeData + * @type {string} + */ name: SUBMODULE_NAME, init, onBidResponseEvent: conditionallyWrap }; -submodule('realTimeData', geoedgeSubmodule); +export function beforeInit() { + fetchWrapper(setWrapper); + submodule('realTimeData', geoedgeSubmodule); +} + +beforeInit(); diff --git a/modules/geoedgeRtdProvider.md b/modules/geoedgeRtdProvider.md index cdf913b8893..5414606612c 100644 --- a/modules/geoedgeRtdProvider.md +++ b/modules/geoedgeRtdProvider.md @@ -5,7 +5,7 @@ Module Type: Rtd Provider Maintainer: guy.books@geoedge.com The Geoedge Realtime module lets publishers block bad ads such as automatic redirects, malware, offensive creatives and landing pages. -To use this module, you'll need to work with [Geoedge](https://www.geoedge.com/publishers-real-time-protection/) to get an account and customer key. +To use this module, you'll need to work with [Geoedge](https://www.geoedge.com/publishers-real-time-protection/) to get an account and cutomer key. ## Integration @@ -49,7 +49,6 @@ Parameters details: |params.key | String | Customer key |Required, contact Geoedge to get your key | |params.bidders | Object | Bidders to monitor |Optional, list of bidder to include / exclude from monitoring. Omitting this will monitor bids from all bidders. | |params.wap |Boolean |Wrap after preload |Optional, defaults to `false`. Set to `true` if you want to monitor only after the module has preloaded the monitoring client. | -|params.gpt |Boolean |Wrap all GPT ad slots |Optional, defaults to `false`. Set to `true` if you want to monitor all Google Publisher Tag ad slots, regaedless if the winning bid comes from Prebid or Google Ad Manager (Direct, Adx, Adesnse, Open Bidding, etc). | ## Example diff --git a/modules/geolocationRtdProvider.js b/modules/geolocationRtdProvider.js deleted file mode 100644 index 6bfed7ee934..00000000000 --- a/modules/geolocationRtdProvider.js +++ /dev/null @@ -1,65 +0,0 @@ -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'; - -let permissionsAvailable = true; -let geolocation; -function getGeolocationData(requestBidsObject, onDone, providerConfig, userConsent) { - let done = false; - if (!permissionsAvailable) { - logWarn('permission for geolocation receiving was denied'); - return complete() - }; - if (!isActivityAllowed(ACTIVITY_TRANSMIT_PRECISE_GEO, activityParams(MODULE_TYPE_RTD, 'geolocation'))) { - logWarn('permission for geolocation receiving was denied by CMP'); - return complete() - }; - const requestPermission = deepAccess(providerConfig, 'params.requestPermission') === true; - navigator.permissions.query({ - name: 'geolocation', - }).then(permission => { - if (permission.state !== 'granted' && !requestPermission) return complete(); - navigator.geolocation.getCurrentPosition(geo => { - geolocation = geo; - complete(); - }); - }); - function complete() { - if (done) return; - done = true; - if (geolocation) { - deepSetValue(requestBidsObject, 'ortb2Fragments.global.device.geo', { - lat: geolocation.coords.latitude, - lon: geolocation.coords.longitude, - lastfix: Math.round((timestamp() - geolocation.timestamp) / 1000), - type: 1 - }); - logInfo('geolocation was successfully received ', requestBidsObject.ortb2Fragments.global.device.geo) - } - onDone(); - } -} -function init(moduleConfig) { - geolocation = void 0; - if (!isFn(navigator?.permissions?.query) || !isFn(navigator?.geolocation?.getCurrentPosition || !navigator?.permissions?.query)) { - logError('geolocation is not defined'); - permissionsAvailable = false; - } else { - permissionsAvailable = true; - } - return permissionsAvailable; -} -export const geolocationSubmodule = { - name: 'geolocation', - gvlid: VENDORLESS_GVLID, - getBidRequestData: getGeolocationData, - init: init, -}; -function registerSubModule() { - submodule('realTimeData', geolocationSubmodule); -} -registerSubModule(); diff --git a/modules/getintentBidAdapter.js b/modules/getintentBidAdapter.js index 7353a1c1f67..25322d81f9b 100644 --- a/modules/getintentBidAdapter.js +++ b/modules/getintentBidAdapter.js @@ -1,11 +1,6 @@ -import {getBidIdParameter, isFn, isInteger} from '../src/utils.js'; +import { getBidIdParameter, isFn, isInteger } from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - */ - const BIDDER_CODE = 'getintent'; const IS_NET_REVENUE = true; const BID_HOST = 'px.adhigh.net'; @@ -43,7 +38,7 @@ export const spec = { * * @param {BidRequest} bid The bid to validate. * @return {boolean} True if this is a valid bid, and false otherwise. - */ + * */ isBidRequestValid: function(bid) { return !!(bid && bid.params && bid.params.pid && bid.params.tid); }, @@ -113,7 +108,7 @@ function buildUrl(bid) { * * @param {BidRequest} bidRequest. * @return {object} GI bid request. - */ + * */ function buildGiBidRequest(bidRequest) { let giBidRequest = { bid_id: bidRequest.bidId, @@ -196,7 +191,7 @@ function addOptional(params, request, props) { /** * @param {String} s The string representing a size (e.g. "300x250"). * @return {Number[]} An array with two elements: [width, height] (e.g.: [300, 250]). - */ + * */ function parseSize(s) { return s.split('x').map(Number); } @@ -205,7 +200,7 @@ function parseSize(s) { * @param {Array} sizes An array of sizes/numbers to be joined into single string. * May be an array (e.g. [300, 250]) or array of arrays (e.g. [[300, 250], [640, 480]]. * @return {String} The string with sizes, e.g. array of sizes [[50, 50], [80, 80]] becomes "50x50,80x80" string. - */ + * */ function produceSize (sizes) { function sizeToStr(s) { if (Array.isArray(s) && s.length === 2 && isInteger(s[0]) && isInteger(s[1])) { diff --git a/modules/gjirafaBidAdapter.js b/modules/gjirafaBidAdapter.js index 9259010ac78..91ed5c9b3fb 100644 --- a/modules/gjirafaBidAdapter.js +++ b/modules/gjirafaBidAdapter.js @@ -2,13 +2,6 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { getStorageManager } from '../src/storageManager.js'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse - * @typedef {import('../src/adapters/bidderFactory.js').validBidRequests} validBidRequests - */ - const BIDDER_CODE = 'gjirafa'; const ENDPOINT_URL = 'https://central.gjirafa.com/bid'; const DIMENSION_SEPARATOR = 'x'; @@ -123,11 +116,11 @@ export const spec = { }; /** - * Generate size param for bid request using sizes array - * - * @param {Array} sizes Possible sizes for the ad unit. - * @return {string} Processed sizes param to be used for the bid request. - */ +* Generate size param for bid request using sizes array +* +* @param {Array} sizes Possible sizes for the ad unit. +* @return {string} Processed sizes param to be used for the bid request. +*/ function generateSizeParam(sizes) { return sizes.map(size => size.join(DIMENSION_SEPARATOR)).join(SIZE_SEPARATOR); } diff --git a/modules/gmosspBidAdapter.js b/modules/gmosspBidAdapter.js index c4b8cd819e0..8c90d0cccfe 100644 --- a/modules/gmosspBidAdapter.js +++ b/modules/gmosspBidAdapter.js @@ -1,25 +1,17 @@ import { createTrackPixelHtml, deepAccess, - deepSetValue, getBidIdParameter, + deepSetValue, + getBidIdParameter, getDNT, getWindowTop, isEmpty, - logError + logError, + tryAppendQueryString } from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {config} from '../src/config.js'; import {BANNER} from '../src/mediaTypes.js'; -import {tryAppendQueryString} from '../libraries/urlUtils/urlUtils.js'; - -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse - * @typedef {import('../src/adapters/bidderFactory.js').SyncOptions} SyncOptions - * @typedef {import('../src/adapters/bidderFactory.js').UserSync} UserSync - * @typedef {import('../src/adapters/bidderFactory.js').validBidRequests} validBidRequests - */ const BIDDER_CODE = 'gmossp'; const ENDPOINT = 'https://sp.gmossp-sp.jp/hb/prebid/query.ad'; diff --git a/modules/gnetBidAdapter.js b/modules/gnetBidAdapter.js index 4718438b9bb..38e96c183b9 100644 --- a/modules/gnetBidAdapter.js +++ b/modules/gnetBidAdapter.js @@ -4,12 +4,6 @@ import { BANNER } from '../src/mediaTypes.js'; import { getStorageManager } from '../src/storageManager.js'; import {ajax} from '../src/ajax.js'; -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - * @typedef {import('../src/adapters/bidderFactory.js').validBidRequests} validBidRequests - */ - const BIDDER_CODE = 'gnet'; const ENDPOINT = 'https://service.gnetrtb.com/api'; const storage = getStorageManager({bidderCode: BIDDER_CODE}); diff --git a/modules/goldbachBidAdapter.js b/modules/goldbachBidAdapter.js index ff394e69e54..4768931950c 100644 --- a/modules/goldbachBidAdapter.js +++ b/modules/goldbachBidAdapter.js @@ -1,9 +1,15 @@ import {Renderer} from '../src/Renderer.js'; import { + chunk, + convertCamelToUnderscore, + convertTypes, createTrackPixelHtml, deepAccess, deepClone, + fill, getBidRequest, + getMaxValueFromArray, + getMinValueFromArray, getParameterByName, isArray, isArrayOfNums, @@ -23,17 +29,9 @@ import {auctionManager} from '../src/auctionManager.js'; import {find, includes} from '../src/polyfill.js'; import {INSTREAM, OUTSTREAM} from '../src/video.js'; import {hasPurpose1Consent} from '../src/utils/gpdr.js'; -import {convertOrtbRequestToProprietaryNative} from '../src/native.js'; -import {APPNEXUS_CATEGORY_MAPPING} from '../libraries/categoryTranslationMapping/index.js'; -import {getANKeywordParam, transformBidderParamKeywords} from '../libraries/appnexusUtils/anKeywords.js'; -import {convertCamelToUnderscore, fill} from '../libraries/appnexusUtils/anUtils.js'; -import {convertTypes} from '../libraries/transformParamsUtils/convertTypes.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 { convertOrtbRequestToProprietaryNative } from '../src/native.js'; +import { APPNEXUS_CATEGORY_MAPPING } from '../libraries/categoryTranslationMapping/index.js'; +import {getANKeywordParam, transformBidderParamKeywords} from '../libraries/appnexusKeywords/anKeywords.js'; const BIDDER_CODE = 'goldbach'; const URL = 'https://ib.adnxs.com/ut/v3/prebid'; @@ -973,7 +971,7 @@ function createAdPodRequest(tags, adPodBid) { const { durationRangeSec, requireExactDuration } = adPodBid.mediaTypes.video; const numberOfPlacements = getAdPodPlacementNumber(adPodBid.mediaTypes.video); - const maxDuration = Math.max(...durationRangeSec); + const maxDuration = getMaxValueFromArray(durationRangeSec); const tagToDuplicate = tags.filter(tag => tag.uuid === adPodBid.bidId); let request = fill(...tagToDuplicate, numberOfPlacements); @@ -999,7 +997,7 @@ function createAdPodRequest(tags, adPodBid) { function getAdPodPlacementNumber(videoParams) { const { adPodDurationSec, durationRangeSec, requireExactDuration } = videoParams; - const minAllowedDuration = Math.min(...durationRangeSec); + const minAllowedDuration = getMinValueFromArray(durationRangeSec); const numberOfPlacements = Math.floor(adPodDurationSec / minAllowedDuration); return requireExactDuration diff --git a/modules/goldfishAdsRtdProvider.js b/modules/goldfishAdsRtdProvider.js deleted file mode 100755 index c595e361968..00000000000 --- a/modules/goldfishAdsRtdProvider.js +++ /dev/null @@ -1,198 +0,0 @@ -import { ajax } from '../src/ajax.js'; -import { submodule } from '../src/hook.js'; -import { getStorageManager } from '../src/storageManager.js'; -import { deepAccess } from '../src/utils.js'; - -/** - * @typedef {import('../modules/rtdModule/index.js').RtdSubmodule} RtdSubmodule - */ - -export const MODULE_NAME = 'goldfishAdsRtd'; -export const MODULE_TYPE = 'realTimeData'; -export const ENDPOINT_URL = 'https://prebid.goldfishads.com/iab-segments'; -export const DATA_STORAGE_KEY = 'goldfishads_data'; -export const DATA_STORAGE_TTL = 1800 * 1000// TTL in seconds - -export const ADAPTER_VERSION = '1.0'; - -export const storage = getStorageManager({ - gvlid: null, - moduleName: MODULE_NAME, - moduleType: MODULE_TYPE, -}); - -/** - * - * @param {{response: string[]} } response - * @returns - */ -export const manageCallbackResponse = (response) => { - try { - const foo = JSON.parse(response.response); - if (!Array.isArray(foo)) throw new Error('Invalid response'); - const enrichedResponse = { - ext: { - segtax: 4 - }, - segment: foo.map((segment) => { return { id: segment } }), - }; - const output = { - name: 'goldfishads.com', - ...enrichedResponse, - }; - return output; - } catch (e) { - throw e; - }; -}; - -/** - * @param {string} key - * @returns { Promise<{name: 'goldfishads.com', ext: { segtag: 4 }, segment: string[]}> } - */ - -const getTargetingDataFromApi = (key) => { - return new Promise((resolve, reject) => { - const requestOptions = { - customHeaders: { - 'Accept': 'application/json' - } - } - const callbacks = { - success(responseText, response) { - try { - const output = manageCallbackResponse(response); - resolve(output); - } catch (e) { - reject(e); - } - }, - error(error) { - reject(error); - } - }; - ajax(`${ENDPOINT_URL}?key=${key}`, callbacks, null, requestOptions) - }) -}; - -/** - * @returns {{ - * name: 'golfishads.com', - * ext: { segtax: 4}, - * segment: string[] - * } | null } - */ -export const getStorageData = () => { - const now = new Date(); - const data = storage.getDataFromLocalStorage(DATA_STORAGE_KEY); - if (data === null) return null; - try { - const foo = JSON.parse(data); - if (now.getTime() > foo.expiry) return null; - return foo.targeting; - } catch (e) { - return null; - } -}; - -/** - * @param { { key: string } } payload - * @returns {Promise<{ - * name: string, - * ext: { segtax: 4}, - * segment: string[] - * }> | null - * } - */ - -const getTargetingData = (payload) => new Promise((resolve) => { - const targeting = getStorageData(); - if (targeting === null) { - getTargetingDataFromApi(payload.key) - .then((response) => { - const now = new Date() - const data = { - targeting: response, - expiry: now.getTime() + DATA_STORAGE_TTL, - }; - storage.setDataInLocalStorage(DATA_STORAGE_KEY, JSON.stringify(data)); - resolve(response); - }) - .catch((e) => { - resolve(null); - }); - } else { - resolve(targeting); - } -}) - -/** - * - * @param {*} config - * @param {*} userConsent - * @returns {boolean} - */ - -const init = (config, userConsent) => { - if (!config.params || !config.params.key) return false; - // return { type: (typeof config.params.key === 'string') }; - if (!(typeof config.params.key === 'string')) return false; - return true; -}; - -/** - * - * @param {{ - * name: string, - * ext: { segtax: 4}, - * segment: {id: string}[] - * } | null } userData - * @param {*} reqBidsConfigObj - * @returns - */ -export const updateUserData = (userData, reqBidsConfigObj) => { - if (userData === null) return; - const bidders = ['appnexus', 'rubicon', 'nexx360']; - for (let i = 0; i < bidders.length; i++) { - const bidderCode = bidders[i]; - const originalConfig = deepAccess(reqBidsConfigObj, `ortb2Fragments.bidder[${bidderCode}].user.data`) || []; - const userConfig = [ - ...originalConfig, - userData, - ]; - reqBidsConfigObj.ortb2Fragments = reqBidsConfigObj.ortb2Fragments || {}; - reqBidsConfigObj.ortb2Fragments.bidder = reqBidsConfigObj.ortb2Fragments.bidder || {}; - reqBidsConfigObj.ortb2Fragments.bidder[bidderCode] = reqBidsConfigObj.ortb2Fragments.bidder[bidderCode] || {}; - reqBidsConfigObj.ortb2Fragments.bidder[bidderCode].user = reqBidsConfigObj.ortb2Fragments.bidder[bidderCode].user = {}; - reqBidsConfigObj.ortb2Fragments.bidder[bidderCode].user.data = reqBidsConfigObj.ortb2Fragments.bidder[bidderCode].user.data || userConfig; - } - return reqBidsConfigObj; -} - -/** - * - * @param {*} reqBidsConfigObj - * @param {*} callback - * @param {*} moduleConfig - * @param {*} userConsent - * @returns {void} - */ -const getBidRequestData = (reqBidsConfigObj, callback, moduleConfig, userConsent) => { - const payload = { - key: moduleConfig.params.key, - }; - getTargetingData(payload) - .then((userData) => { - updateUserData(userData, reqBidsConfigObj); - callback(); - }); -}; - -/** @type {RtdSubmodule} */ -export const goldfishAdsSubModule = { - name: MODULE_NAME, - init, - getBidRequestData, -}; - -submodule(MODULE_TYPE, goldfishAdsSubModule); diff --git a/modules/goldfishAdsRtdProvider.md b/modules/goldfishAdsRtdProvider.md deleted file mode 100755 index 4625c9a7988..00000000000 --- a/modules/goldfishAdsRtdProvider.md +++ /dev/null @@ -1,48 +0,0 @@ -# Goldfish Ads Real-time Data Submodule - -## Overview - - Module Name: Goldfish Ads Rtd Provider - Module Type: Rtd Provider - Maintainer: keith@goldfishads.com - -## Description - -This RTD module provides access to the Goldfish Ads Geograph, which leverages geographic and temporal data on a privcay-first platform. This module works without using cookies, PII, emails, or device IDs across all website traffic, including unauthenticated users, and adds audience data into bid requests to increase scale and yields. - -## Usage - -### Build -``` -gulp build --modules="rtdModule,goldfishAdsRtdProvider,appnexusBidAdapter,..." -``` - -> Note that the global RTD module, `rtdModule`, is a prerequisite of the Goldfish Ads RTD module. - -### Configuration - -Use `setConfig` to instruct Prebid.js to initialize the Goldfish Ads RTD module, as specified below. - -This module is configured as part of the `realTimeData.dataProviders` - -```javascript -pbjs.setConfig({ - realTimeData: { - auctionDelay: 300, - dataProviders: [{ - name: 'goldfishAds', - waitForIt: true, - params: { - key: 'testkey' - } - }] - } -}) -``` - -### Parameters -| Name | Type | Description | Default | -|:-----------------|:----------------------------------------|:-----------------------------------------------------------------------------|:-----------------------| -| name | String | Real time data module name | Always 'goldfishAds' | -| waitForIt | Boolean | Set to true to maximize chance for bidder enrichment, used with auctionDelay | `false` | -| params.key | String | Your key id issued by Goldfish Ads | | diff --git a/modules/gothamadsBidAdapter.js b/modules/gothamadsBidAdapter.js index ab59c6febec..9f44a54460f 100644 --- a/modules/gothamadsBidAdapter.js +++ b/modules/gothamadsBidAdapter.js @@ -4,11 +4,6 @@ import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; import { config } from '../src/config.js'; import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - */ - const BIDDER_CODE = 'gothamads'; const ACCOUNTID_MACROS = '[account_id]'; const URL_ENDPOINT = `https://us-e-node1.gothamads.com/bid?pass=${ACCOUNTID_MACROS}&integration=prebidjs`; diff --git a/modules/gppControl_usnat.js b/modules/gppControl_usnat.js deleted file mode 100644 index b38fc1a9d29..00000000000 --- a/modules/gppControl_usnat.js +++ /dev/null @@ -1,11 +0,0 @@ -import {config} from '../src/config.js'; -import {setupRules} from '../libraries/mspa/activityControls.js'; - -let setupDone = false; - -config.getConfig('consentManagement', (cfg) => { - if (cfg?.consentManagement?.gpp != null && !setupDone) { - setupRules('usnat', [7]); - setupDone = true; - } -}) diff --git a/modules/gppControl_usstates.js b/modules/gppControl_usstates.js deleted file mode 100644 index bc2b434e085..00000000000 --- a/modules/gppControl_usstates.js +++ /dev/null @@ -1,176 +0,0 @@ -import {config} from '../src/config.js'; -import {setupRules} from '../libraries/mspa/activityControls.js'; -import {deepSetValue, prefixLog} from '../src/utils.js'; - -const FIELDS = { - Version: 0, - Gpc: 0, - SharingNotice: 0, - SaleOptOutNotice: 0, - SharingOptOutNotice: 0, - TargetedAdvertisingOptOutNotice: 0, - SensitiveDataProcessingOptOutNotice: 0, - SensitiveDataLimitUseNotice: 0, - SaleOptOut: 0, - SharingOptOut: 0, - TargetedAdvertisingOptOut: 0, - SensitiveDataProcessing: 12, - KnownChildSensitiveDataConsents: 2, - PersonalDataConsents: 0, - MspaCoveredTransaction: 0, - MspaOptOutOptionMode: 0, - MspaServiceProviderMode: 0, -}; - -/** - * Generate a normalization function for converting US state strings to the usnat format. - * - * Scalar fields are copied over if they exist in the input (state) data, or set to null otherwise. - * 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. - * - * @param {Array[String]} nullify? list of fields to force to null - * @param {{}} move? Map from list field name to an index remapping for elements within that field (using 1 as the first index). - * For example, {SensitiveDataProcessing: {1: 2, 2: [1, 3]}} means "rearrange SensitiveDataProcessing by moving - * the first element to the second position, and the second element to both the first and third position." - * @param {({}, {}) => void} fn? an optional function to run once all the processing described above is complete; - * it's passed two arguments, the original (state) data, and its normalized (usnat) version. - * @param fields - * @returns {function({}): {}} - */ -export function normalizer({nullify = [], move = {}, fn}, fields = FIELDS) { - move = Object.fromEntries(Object.entries(move).map(([k, map]) => [k, - Object.fromEntries(Object.entries(map) - .map(([k, v]) => [k, Array.isArray(v) ? v : [v]]) - .map(([k, v]) => [--k, v.map(el => --el)]) - )]) - ); - return function (cd) { - const norm = Object.fromEntries(Object.entries(fields) - .map(([field, len]) => { - let val = null; - if (len > 0) { - val = Array(len).fill(null); - if (Array.isArray(cd[field])) { - const remap = move[field] || {}; - const done = []; - cd[field].forEach((el, i) => { - const [dest, moved] = remap.hasOwnProperty(i) ? [remap[i], true] : [[i], false]; - dest.forEach(d => { - if (d < len && !done.includes(d)) { - val[d] = el; - moved && done.push(d); - } - }); - }); - } - } else if (cd[field] != null) { - val = Array.isArray(cd[field]) ? null : cd[field]; - } - return [field, val]; - })); - nullify.forEach(path => deepSetValue(norm, path, null)); - fn && fn(cd, norm); - return norm; - }; -} - -function scalarMinorsAreChildren(original, normalized) { - normalized.KnownChildSensitiveDataConsents = original.KnownChildSensitiveDataConsents === 0 ? [0, 0] : [1, 1]; -} - -export const NORMALIZATIONS = { - // normalization rules - convert state consent into usnat consent - // https://docs.prebid.org/features/mspa-usnat.html - 7: (consent) => consent, - 8: normalizer({ - move: { - SensitiveDataProcessing: { - 1: 9, - 2: 10, - 3: 8, - 4: [1, 2], - 5: 12, - 8: 3, - 9: 4, - } - }, - fn(original, normalized) { - if (original.KnownChildSensitiveDataConsents.some(el => el !== 0)) { - normalized.KnownChildSensitiveDataConsents = [1, 1]; - } - } - }), - 9: normalizer({fn: scalarMinorsAreChildren}), - 10: normalizer({fn: scalarMinorsAreChildren}), - 11: normalizer({ - move: { - SensitiveDataProcessing: { - 3: 4, - 4: 5, - 5: 3, - } - }, - fn: scalarMinorsAreChildren - }), - 12: normalizer({ - fn(original, normalized) { - const cc = original.KnownChildSensitiveDataConsents; - let repl; - if (!cc.some(el => el !== 0)) { - repl = [0, 0]; - } else if (cc[1] === 2 && cc[2] === 2) { - repl = [2, 1]; - } else { - repl = [1, 1]; - } - normalized.KnownChildSensitiveDataConsents = repl; - } - }) -}; - -export const DEFAULT_SID_MAPPING = { - 8: 'usca', - 9: 'usva', - 10: 'usco', - 11: 'usut', - 12: 'usct' -}; - -export const getSections = (() => { - const allSIDs = Object.keys(DEFAULT_SID_MAPPING).map(Number); - return function ({sections = {}, sids = allSIDs} = {}) { - return sids.map(sid => { - const logger = prefixLog(`Cannot set up MSPA controls for SID ${sid}:`); - const ov = sections[sid] || {}; - const normalizeAs = ov.normalizeAs || sid; - if (!NORMALIZATIONS.hasOwnProperty(normalizeAs)) { - logger.logError(`no normalization rules are known for SID ${normalizeAs}`) - return; - } - const api = ov.name || DEFAULT_SID_MAPPING[sid]; - if (typeof api !== 'string') { - logger.logError(`cannot determine GPP section name`) - return; - } - return [ - api, - [sid], - NORMALIZATIONS[normalizeAs] - ] - }).filter(el => el != null); - } -})(); - -const handles = []; - -config.getConfig('consentManagement', (cfg) => { - const gppConf = cfg.consentManagement?.gpp; - if (gppConf) { - while (handles.length) { - handles.pop()(); - } - getSections(gppConf?.mspa || {}) - .forEach(([api, sids, normalize]) => handles.push(setupRules(api, sids, normalize))); - } -}); diff --git a/modules/gptPreAuction.js b/modules/gptPreAuction.js index bf5b4a55dbb..71884235b38 100644 --- a/modules/gptPreAuction.js +++ b/modules/gptPreAuction.js @@ -1,11 +1,4 @@ -import { - deepAccess, - isAdUnitCodeMatchingSlot, - isGptPubadsDefined, - logInfo, - pick, - deepSetValue -} from '../src/utils.js'; +import {deepAccess, isAdUnitCodeMatchingSlot, isGptPubadsDefined, logInfo, pick} from '../src/utils.js'; import {config} from '../src/config.js'; import {getHook} from '../src/hook.js'; import {find} from '../src/polyfill.js'; @@ -22,8 +15,7 @@ export const appendGptSlots = adUnits => { } const adUnitMap = adUnits.reduce((acc, adUnit) => { - acc[adUnit.code] = acc[adUnit.code] || []; - acc[adUnit.code].push(adUnit); + acc[adUnit.code] = adUnit; return acc; }, {}); @@ -33,13 +25,15 @@ export const appendGptSlots = adUnits => { : isAdUnitCodeMatchingSlot(slot)); if (matchingAdUnitCode) { - const adserver = { - name: 'gam', - adslot: sanitizeSlotPath(slot.getAdUnitPath()) - }; - adUnitMap[matchingAdUnitCode].forEach((adUnit) => { - deepSetValue(adUnit, 'ortb2Imp.ext.data.adserver', Object.assign({}, adUnit.ortb2Imp?.ext?.data?.adserver, adserver)); - }); + const adUnit = adUnitMap[matchingAdUnitCode]; + adUnit.ortb2Imp = adUnit.ortb2Imp || {}; + adUnit.ortb2Imp.ext = adUnit.ortb2Imp.ext || {}; + adUnit.ortb2Imp.ext.data = adUnit.ortb2Imp.ext.data || {}; + + const context = adUnit.ortb2Imp.ext.data; + context.adserver = context.adserver || {}; + context.adserver.name = 'gam'; + context.adserver.adslot = sanitizeSlotPath(slot.getAdUnitPath()); } }); }; diff --git a/modules/gravitoIdSystem.js b/modules/gravitoIdSystem.js index cc02c6a103e..aa25ea7db2c 100644 --- a/modules/gravitoIdSystem.js +++ b/modules/gravitoIdSystem.js @@ -16,16 +16,16 @@ export const cookieKey = 'gravitompId'; export const gravitoIdSystemSubmodule = { /** - * used to link submodule with config - * @type {string} - */ + * used to link submodule with config + * @type {string} + */ name: MODULE_NAME, /** - * performs action to obtain id - * @function - * @returns { {id: {gravitompId: string}} | undefined } - */ + * performs action to obtain id + * @function + * @returns { {id: {gravitompId: string}} | undefined } + */ getId: function() { const newId = storage.getCookie(cookieKey); if (!newId) { @@ -38,11 +38,11 @@ export const gravitoIdSystemSubmodule = { }, /** - * decode the stored id value for passing to bid requests - * @function - * @param { {gravitompId: string} } value - * @returns { {gravitompId: {string} } | undefined } - */ + * decode the stored id value for passing to bid requests + * @function + * @param { {gravitompId: string} } value + * @returns { {gravitompId: {string} } | undefined } + */ decode: function(value) { if (value && typeof value === 'object') { var result = {}; @@ -53,12 +53,6 @@ export const gravitoIdSystemSubmodule = { } return undefined; }, - eids: { - 'gravitompId': { - source: 'gravito.net', - atype: 1 - }, - } } submodule('userId', gravitoIdSystemSubmodule); diff --git a/modules/greenbidsAnalyticsAdapter.js b/modules/greenbidsAnalyticsAdapter.js index 5d1f35f24ff..edc0c9c6c5c 100644 --- a/modules/greenbidsAnalyticsAdapter.js +++ b/modules/greenbidsAnalyticsAdapter.js @@ -2,20 +2,18 @@ import {ajax} from '../src/ajax.js'; import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import CONSTANTS from '../src/constants.json'; import adapterManager from '../src/adapterManager.js'; -import {deepClone, generateUUID, logError, logInfo, logWarn} from '../src/utils.js'; +import {deepClone, logError, logInfo} from '../src/utils.js'; const analyticsType = 'endpoint'; -export const ANALYTICS_VERSION = '2.0.0'; +export const ANALYTICS_VERSION = '1.0.0'; const ANALYTICS_SERVER = 'https://a.greenbids.ai'; const { EVENTS: { - AUCTION_INIT, AUCTION_END, BID_TIMEOUT, - BILLABLE_EVENT, } } = CONSTANTS; @@ -27,53 +25,28 @@ export const BIDDER_STATUS = { const analyticsOptions = {}; -export const isSampled = function(greenbidsId, samplingRate) { - if (samplingRate < 0 || samplingRate > 1) { - logWarn('Sampling rate must be between 0 and 1'); - return true; - } - const hashInt = parseInt(greenbidsId.slice(-4), 16); - - return hashInt < samplingRate * (0xFFFF + 1); -} +export const parseBidderCode = function (bid) { + let bidderCode = bid.bidderCode || bid.bidder; + return bidderCode.toLowerCase(); +}; export const greenbidsAnalyticsAdapter = Object.assign(adapter({ANALYTICS_SERVER, analyticsType}), { cachedAuctions: {}, initConfig(config) { - analyticsOptions.options = deepClone(config.options); /** * Required option: pbuid * @type {boolean} */ - if (typeof analyticsOptions.options.pbuid !== 'string' || analyticsOptions.options.pbuid.length < 1) { + analyticsOptions.options = deepClone(config.options); + if (typeof config.options.pbuid !== 'string' || config.options.pbuid.length < 1) { logError('"options.pbuid" is required.'); return false; } - /** - * Deprecate use of integerated 'sampling' config - * replace by greenbidsSampling - */ - if (typeof analyticsOptions.options.sampling === 'number') { - logWarn('"options.sampling" is deprecated, please use "greenbidsSampling" instead.'); - analyticsOptions.options.greenbidsSampling = analyticsOptions.options.sampling; - // Set sampling to null to prevent prebid analytics integrated sampling to happen - analyticsOptions.options.sampling = null; - } - - /** - * Discourage unsampled analytics - */ - if (typeof analyticsOptions.options.greenbidsSampling !== 'number' || analyticsOptions.options.greenbidsSampling >= 1) { - logWarn('"options.greenbidsSampling" is not set or >=1, using this analytics module unsampled is discouraged.'); - analyticsOptions.options.greenbidsSampling = 1; - } - analyticsOptions.pbuid = config.options.pbuid analyticsOptions.server = ANALYTICS_SERVER; - return true; }, sendEventMessage(endPoint, data) { @@ -84,16 +57,13 @@ export const greenbidsAnalyticsAdapter = Object.assign(adapter({ANALYTICS_SERVER }); }, createCommonMessage(auctionId) { - const cachedAuction = this.getCachedAuction(auctionId); return { version: ANALYTICS_VERSION, auctionId: auctionId, referrer: window.location.href, - sampling: analyticsOptions.options.greenbidsSampling, + sampling: analyticsOptions.options.sampling, prebid: '$prebid.version$', - greenbidsId: cachedAuction.greenbidsId, pbuid: analyticsOptions.pbuid, - billingId: cachedAuction.billingId, adUnits: [], }; }, @@ -126,24 +96,22 @@ export const greenbidsAnalyticsAdapter = Object.assign(adapter({ANALYTICS_SERVER } } }, - createBidMessage(auctionEndArgs) { + createBidMessage(auctionEndArgs, timeoutBids) { + logInfo(auctionEndArgs) const {auctionId, timestamp, auctionEnd, adUnits, bidsReceived, noBids} = auctionEndArgs; - const cachedAuction = this.getCachedAuction(auctionId); const message = this.createCommonMessage(auctionId); - const timeoutBids = cachedAuction.timeoutBids || []; message.auctionElapsed = (auctionEnd - timestamp); adUnits.forEach((adUnit) => { - const adUnitCode = adUnit.code?.toLowerCase() || 'unknown_adunit_code'; + const adUnitCode = adUnit.code.toLowerCase(); 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: [], }); }); @@ -161,26 +129,13 @@ export const greenbidsAnalyticsAdapter = Object.assign(adapter({ANALYTICS_SERVER getCachedAuction(auctionId) { this.cachedAuctions[auctionId] = this.cachedAuctions[auctionId] || { timeoutBids: [], - greenbidsId: null, - billingId: null, - isSampled: true, }; return this.cachedAuctions[auctionId]; }, - handleAuctionInit(auctionInitArgs) { - const cachedAuction = this.getCachedAuction(auctionInitArgs.auctionId); - try { - cachedAuction.greenbidsId = auctionInitArgs.adUnits[0].ortb2Imp.ext.greenbids.greenbidsId; - } catch (e) { - logInfo("Couldn't find Greenbids RTD info, assuming analytics only"); - cachedAuction.greenbidsId = generateUUID(); - } - cachedAuction.isSampled = isSampled(cachedAuction.greenbidsId, analyticsOptions.options.greenbidsSampling); - }, handleAuctionEnd(auctionEndArgs) { const cachedAuction = this.getCachedAuction(auctionEndArgs.auctionId); this.sendEventMessage('/', - this.createBidMessage(auctionEndArgs, cachedAuction) + this.createBidMessage(auctionEndArgs, cachedAuction.timeoutBids) ); }, handleBidTimeout(timeoutBids) { @@ -189,34 +144,14 @@ export const greenbidsAnalyticsAdapter = Object.assign(adapter({ANALYTICS_SERVER cachedAuction.timeoutBids.push(bid); }); }, - handleBillable(billableArgs) { - const cachedAuction = this.getCachedAuction(billableArgs.auctionId); - /* Filter Greenbids Billable Events only */ - if (billableArgs.vendor === 'greenbidsRtdProvider') { - cachedAuction.billingId = billableArgs.billingId || 'unknown_billing_id'; - } - }, track({eventType, args}) { - try { - if (eventType === AUCTION_INIT) { - this.handleAuctionInit(args); - } - - if (this.getCachedAuction(args?.auctionId)?.isSampled ?? true) { - switch (eventType) { - case BID_TIMEOUT: - this.handleBidTimeout(args); - break; - case AUCTION_END: - this.handleAuctionEnd(args); - break; - case BILLABLE_EVENT: - this.handleBillable(args); - break; - } - } - } catch (e) { - logWarn('There was an error handling event ' + eventType); + switch (eventType) { + case BID_TIMEOUT: + this.handleBidTimeout(args); + break; + case AUCTION_END: + this.handleAuctionEnd(args); + break; } }, getAnalyticsOptions() { diff --git a/modules/greenbidsAnalyticsAdapter.md b/modules/greenbidsAnalyticsAdapter.md index 1be2c1741ed..46e3af2c5e2 100644 --- a/modules/greenbidsAnalyticsAdapter.md +++ b/modules/greenbidsAnalyticsAdapter.md @@ -1,24 +1,23 @@ -#### Registration +# Overview -The Greenbids Analytics adapter requires setup and approval from the -Greenbids team. Please reach out to our team for more information [greenbids.ai](https://greenbids.ai). +``` +Module Name: Greenbids Analytics Adapter +Module Type: Analytics Adapter +Maintainer: jb@greenbids.ai +``` -#### Analytics Options +# Description -{: .table .table-bordered .table-striped } -| Name | Scope | Description | Example | Type | -|-------------|---------|--------------------|-----------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------|------------------| -| pbuid | required | The Greenbids Publisher ID | greenbids-publisher-1 | string | -| greenbidsSampling | optional | sampling factor [0-1] (a value of 0.1 will filter 90% of the traffic) | 1.0 | float | +Analytics adapter for Greenbids -### Example Configuration +# Test Parameters -```javascript - pbjs.enableAnalytics({ - provider: 'greenbids', - options: { - pbuid: "greenbids-publisher-1" // please contact Greenbids to get a pbuid for yourself - greenbidsSampling: 1.0 - } - }); -``` \ No newline at end of file +``` +{ + provider: 'greenbids', + options: { + pbuid: "PBUID_FROM_GREENBIDS" + sampling: 1.0 + } +} +``` diff --git a/modules/greenbidsRtdProvider.js b/modules/greenbidsRtdProvider.js index 7fcd163a7c2..ef12326cf18 100644 --- a/modules/greenbidsRtdProvider.js +++ b/modules/greenbidsRtdProvider.js @@ -1,13 +1,12 @@ -import { logError, deepClone, generateUUID, deepSetValue, deepAccess } from '../src/utils.js'; +import { logError } from '../src/utils.js'; import { ajax } from '../src/ajax.js'; import { submodule } from '../src/hook.js'; -import * as events from '../src/events.js'; -import CONSTANTS from '../src/constants.json'; const MODULE_NAME = 'greenbidsRtdProvider'; -const MODULE_VERSION = '2.0.0'; -const ENDPOINT = 'https://t.greenbids.ai'; +const MODULE_VERSION = '1.0.0'; +const ENDPOINT = 'https://europe-west1-greenbids-357713.cloudfunctions.net/partner-selection'; +const auctionInfo = {}; const rtdOptions = {}; function init(moduleConfig) { @@ -17,33 +16,22 @@ function init(moduleConfig) { return false; } else { rtdOptions.pbuid = params?.pbuid; + rtdOptions.targetTPR = params?.targetTPR || 0.99; rtdOptions.timeout = params?.timeout || 200; return true; } } function onAuctionInitEvent(auctionDetails) { - /* Emitting one billing event per auction */ - let defaultId = 'default_id'; - let greenbidsId = deepAccess(auctionDetails.adUnits[0], 'ortb2Imp.ext.greenbids.greenbidsId', defaultId); - /* greenbids was successfully called so we emit the event */ - if (greenbidsId !== defaultId) { - events.emit(CONSTANTS.EVENTS.BILLABLE_EVENT, { - type: 'auction', - billingId: generateUUID(), - auctionId: auctionDetails.auctionId, - vendor: MODULE_NAME - }); - } + auctionInfo.auctionId = auctionDetails.auctionId; } function getBidRequestData(reqBidsConfigObj, callback, config, userConsent) { - let greenbidsId = generateUUID(); - let promise = createPromise(reqBidsConfigObj, greenbidsId); + let promise = createPromise(reqBidsConfigObj); promise.then(callback); } -function createPromise(reqBidsConfigObj, greenbidsId) { +function createPromise(reqBidsConfigObj) { return new Promise((resolve) => { const timeoutId = setTimeout(() => { resolve(reqBidsConfigObj); @@ -52,7 +40,7 @@ function createPromise(reqBidsConfigObj, greenbidsId) { ENDPOINT, { success: (response) => { - processSuccessResponse(response, timeoutId, reqBidsConfigObj, greenbidsId); + processSuccessResponse(response, timeoutId, reqBidsConfigObj); resolve(reqBidsConfigObj); }, error: () => { @@ -60,35 +48,24 @@ function createPromise(reqBidsConfigObj, greenbidsId) { resolve(reqBidsConfigObj); }, }, - createPayload(reqBidsConfigObj, greenbidsId), - { - contentType: 'application/json', - customHeaders: { - 'Greenbids-Pbuid': rtdOptions.pbuid - } - } + createPayload(reqBidsConfigObj), + { contentType: 'application/json' } ); }); } -function processSuccessResponse(response, timeoutId, reqBidsConfigObj, greenbidsId) { +function processSuccessResponse(response, timeoutId, reqBidsConfigObj) { clearTimeout(timeoutId); const responseAdUnits = JSON.parse(response); - updateAdUnitsBasedOnResponse(reqBidsConfigObj.adUnits, responseAdUnits, greenbidsId); + + updateAdUnitsBasedOnResponse(reqBidsConfigObj.adUnits, responseAdUnits); } -function updateAdUnitsBasedOnResponse(adUnits, responseAdUnits, greenbidsId) { +function updateAdUnitsBasedOnResponse(adUnits, responseAdUnits) { adUnits.forEach((adUnit) => { const matchingAdUnit = findMatchingAdUnit(responseAdUnits, adUnit.code); if (matchingAdUnit) { - deepSetValue(adUnit, 'ortb2Imp.ext.greenbids', { - greenbidsId: greenbidsId, - keptInAuction: matchingAdUnit.bidders, - isExploration: matchingAdUnit.isExploration - }); - if (!matchingAdUnit.isExploration) { - removeFalseBidders(adUnit, matchingAdUnit); - } + removeFalseBidders(adUnit, matchingAdUnit); } }); } @@ -108,24 +85,14 @@ function getFalseBidders(bidders) { .map(([bidder]) => bidder); } -function stripAdUnits(adUnits) { - const stripedAdUnits = deepClone(adUnits); - return stripedAdUnits.map(adUnit => { - adUnit.bids = adUnit.bids.map(bid => { - return { bidder: bid.bidder }; - }); - return adUnit; - }); -} - -function createPayload(reqBidsConfigObj, greenbidsId) { +function createPayload(reqBidsConfigObj) { return JSON.stringify({ + auctionId: auctionInfo.auctionId, version: MODULE_VERSION, - ...rtdOptions, referrer: window.location.href, prebid: '$prebid.version$', - greenbidsId: greenbidsId, - adUnits: stripAdUnits(reqBidsConfigObj.adUnits), + rtdOptions: rtdOptions, + adUnits: reqBidsConfigObj.adUnits, }); } @@ -138,7 +105,6 @@ export const greenbidsSubmodule = { findMatchingAdUnit: findMatchingAdUnit, removeFalseBidders: removeFalseBidders, getFalseBidders: getFalseBidders, - stripAdUnits: stripAdUnits, }; submodule('realTimeData', greenbidsSubmodule); diff --git a/modules/greenbidsRtdProvider.md b/modules/greenbidsRtdProvider.md index ab8105a4537..85b8f5a7859 100644 --- a/modules/greenbidsRtdProvider.md +++ b/modules/greenbidsRtdProvider.md @@ -2,7 +2,6 @@ ``` Module Name: Greenbids RTD Provider -Module Version: 2.0.0 Module Type: RTD Provider Maintainer: jb@greenbids.ai ``` @@ -22,6 +21,7 @@ This module is configured as part of the `realTimeData.dataProviders` object. | `waitForIt ` | required (mandatory true value) | Tells prebid auction to wait for the result of this module | `'true'` | `boolean` | | `params` | required | | | `Object` | | `params.pbuid` | required | The client site id provided by Greenbids. | `'TEST_FROM_GREENBIDS'` | `string` | +| `params.targetTPR` | optional (default 0.95) | Target True positive rate for the throttling model | `0.99` | `[0-1]` | | `params.timeout` | optional (default 200) | Maximum amount of milliseconds allowed for module to finish working (has to be <= to the realTimeData.auctionDelay property) | `200` | `number` | #### Example diff --git a/modules/gridBidAdapter.js b/modules/gridBidAdapter.js index 7147864a2e6..9c63e871356 100644 --- a/modules/gridBidAdapter.js +++ b/modules/gridBidAdapter.js @@ -9,22 +9,14 @@ import { isNumber, isStr } from '../src/utils.js'; -import { ajax } from '../src/ajax.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { Renderer } from '../src/Renderer.js'; import { VIDEO, BANNER } from '../src/mediaTypes.js'; import { config } from '../src/config.js'; import { getStorageManager } from '../src/storageManager.js'; -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - * @typedef {import('../src/adapters/bidderFactory.js').ServerRequest} ServerRequest - */ - const BIDDER_CODE = 'grid'; const ENDPOINT_URL = 'https://grid.bidswitch.net/hbjson'; -const USP_DELETE_DATA_HANDLER = 'https://media.grid.bidswitch.net/uspapi_delete_c2s' const SYNC_URL = 'https://x.bidswitch.net/sync?ssp=themediagrid'; const TIME_TO_LIVE = 360; @@ -34,9 +26,10 @@ const RENDERER_URL = 'https://acdn.adnxs.com/video/outstream/ANOutstreamVideo.js export const storage = getStorageManager({ bidderCode: BIDDER_CODE }); const LOG_ERROR_MESS = { - noAdid: 'Bid from response has no adid parameter - ', + noAuid: 'Bid from response has no auid parameter - ', noAdm: 'Bid from response has no adm parameter - ', noBid: 'Array of bid objects is empty', + noPlacementCode: 'Can\'t find in requested bids the bid with auid - ', emptyUids: 'Uids should be not empty', emptySeatbid: 'Seatbid array from response has empty item', emptyResponse: 'Response is empty', @@ -97,7 +90,7 @@ export const spec = { let {bidderRequestId, gdprConsent, uspConsent, timeout, refererInfo, gppConsent} = bidderRequest || {}; const referer = refererInfo ? encodeURIComponent(refererInfo.page) : ''; - const tmax = parseInt(timeout) || null; + const tmax = timeout; const imp = []; const bidsMap = {}; const requests = []; @@ -139,13 +132,20 @@ export const spec = { }; if (ortb2Imp) { if (ortb2Imp.instl) { - impObj.instl = parseInt(ortb2Imp.instl) || null; + impObj.instl = ortb2Imp.instl; } if (ortb2Imp.ext) { - impObj.ext.gpid = ortb2Imp.ext.gpid?.toString() || ortb2Imp.ext.data?.pbadslot?.toString() || ortb2Imp.ext.data?.adserver?.adslot?.toString(); if (ortb2Imp.ext.data) { impObj.ext.data = ortb2Imp.ext.data; + if (impObj.ext.data.adserver && impObj.ext.data.adserver.adslot) { + impObj.ext.gpid = impObj.ext.data.adserver.adslot.toString(); + } else if (ortb2Imp.ext.data.pbadslot) { + impObj.ext.gpid = ortb2Imp.ext.data.pbadslot.toString(); + } + } + if (ortb2Imp.ext.gpid) { + impObj.ext.gpid = ortb2Imp.ext.gpid.toString(); } } } @@ -463,15 +463,6 @@ export const spec = { url: syncUrl + params }; } - }, - - ajaxCall: function(url, cb, data, options) { - options.browsingTopics = false; - return ajax(url, cb, data, options); - }, - - onDataDeletionRequest: function(data) { - spec.ajaxCall(USP_DELETE_DATA_HANDLER, null, null, {method: 'GET'}); } }; @@ -483,7 +474,7 @@ export const spec = { */ function _getFloor (mediaTypes, bid) { const curMediaType = mediaTypes.video ? 'video' : 'banner'; - let floor = parseFloat(bid.params.bidFloor || bid.params.floorcpm || 0) || null; + let floor = bid.params.bidFloor || bid.params.floorcpm || 0; if (typeof bid.getFloor === 'function') { const floorInfo = bid.getFloor({ @@ -516,7 +507,7 @@ function _getBidFromResponse(respItem) { function _addBidResponse(serverBid, bidRequest, bidResponses, RendererConst, bidderCode) { if (!serverBid) return; let errorMessage; - if (!serverBid.adid) errorMessage = LOG_ERROR_MESS.noAdid + JSON.stringify(serverBid); + if (!serverBid.auid) errorMessage = LOG_ERROR_MESS.noAuid + JSON.stringify(serverBid); if (!errorMessage && !serverBid.adm && !serverBid.nurl) errorMessage = LOG_ERROR_MESS.noAdm + JSON.stringify(serverBid); else { const bidObject = bidRequest.bidsMap ? bidRequest.bidsMap[serverBid.impid] : bidRequest.bidObject; @@ -527,7 +518,7 @@ function _addBidResponse(serverBid, bidRequest, bidResponses, RendererConst, bid cpm: serverBid.price, width: serverBid.w, height: serverBid.h, - creativeId: serverBid.adid, + creativeId: serverBid.auid, // bid.bidId currency: 'USD', netRevenue: true, ttl: TIME_TO_LIVE, @@ -593,8 +584,8 @@ function createVideoRequest(videoParams, mediaType, bidSizes) { if (!videoData.w || !videoData.h) return; - const minDur = mind || durationRangeSec[0] || parseInt(videoData.minduration) || null; - const maxDur = maxd || durationRangeSec[1] || parseInt(videoData.maxduration) || null; + const minDur = mind || durationRangeSec[0] || videoData.minduration; + const maxDur = maxd || durationRangeSec[1] || videoData.maxduration; if (minDur) { videoData.minduration = minDur; diff --git a/modules/growadvertisingBidAdapter.js b/modules/growadvertisingBidAdapter.js index f6f7867f0fe..b9b256dbaff 100644 --- a/modules/growadvertisingBidAdapter.js +++ b/modules/growadvertisingBidAdapter.js @@ -1,6 +1,6 @@ 'use strict'; -import {deepAccess, _each, triggerPixel, getBidIdParameter} from '../src/utils.js'; +import { getBidIdParameter, deepAccess, _each, triggerPixel } from '../src/utils.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/growthCodeAnalyticsAdapter.js b/modules/growthCodeAnalyticsAdapter.js index 5c7cc254f1d..a2ab4ddbfac 100644 --- a/modules/growthCodeAnalyticsAdapter.js +++ b/modules/growthCodeAnalyticsAdapter.js @@ -13,7 +13,7 @@ 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' +const ENDPOINT_URL = 'https://p2.gcprivacy.com/v1/pb/analytics' export const storage = getStorageManager({moduleType: MODULE_TYPE_ANALYTICS, moduleName: MODULE_NAME}); @@ -30,8 +30,8 @@ let bidRequestTimeout = 0; let analyticsType = 'endpoint'; let growthCodeAnalyticsAdapter = Object.assign(adapter({url: url, analyticsType}), { - track({eventType, args}) { - let eventData = args ? JSON.parse(JSON.stringify(args)) : {}; + track({eventType, eventData}) { + eventData = eventData ? JSON.parse(JSON.stringify(eventData)) : {}; let data = {}; if (!trackEvents.includes(eventType)) return; switch (eventType) { @@ -98,11 +98,6 @@ let growthCodeAnalyticsAdapter = Object.assign(adapter({url: url, analyticsType} break; } - case CONSTANTS.EVENTS.NO_BID: { - data = eventData - break; - } - default: return; } @@ -138,15 +133,12 @@ growthCodeAnalyticsAdapter.enableAnalytics = function(conf = {}) { function logToServer() { if (pid === DEFAULT_PID) return; - if (eventQueue.length >= 1) { - // Get the correct GCID - let gcid = localStorage.getItem('gcid') - + if (eventQueue.length > 1) { let data = { session: sessionId, pid: pid, - gcid: gcid, timestamp: Date.now(), + timezoneoffset: new Date().getTimezoneOffset(), url: getRefererInfo().page, referer: document.referrer, events: eventQueue @@ -170,7 +162,7 @@ function sendEvent(event) { eventQueue.push(event); logInfo(MODULE_NAME + 'Analytics Event: ' + event); - if ((event.eventType === CONSTANTS.EVENTS.AUCTION_END) || (event.eventType === CONSTANTS.EVENTS.BID_WON)) { + if (event.eventType === CONSTANTS.EVENTS.AUCTION_END) { logToServer(); } } diff --git a/modules/growthCodeAnalyticsAdapter.md b/modules/growthCodeAnalyticsAdapter.md index 6625d492ee6..e45cb2e9c62 100644 --- a/modules/growthCodeAnalyticsAdapter.md +++ b/modules/growthCodeAnalyticsAdapter.md @@ -21,7 +21,13 @@ pbjs.enableAnalytics({ pid: '', trackEvents: [ 'auctionEnd', - 'bidWon'] + 'bidAdjustment', + 'bidTimeout', + 'bidRequested', + 'bidResponse', + 'noBid', + 'bidWon', + 'bidderDone'] } }); ``` diff --git a/modules/growthCodeIdSystem.js b/modules/growthCodeIdSystem.js index 539a20a7302..fae022f1a56 100644 --- a/modules/growthCodeIdSystem.js +++ b/modules/growthCodeIdSystem.js @@ -5,18 +5,11 @@ * @requires module:modules/userId */ -import {logError, logInfo, pick} from '../src/utils.js'; +import {logError, logInfo, tryAppendQueryString} 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 {tryAppendQueryString} from '../libraries/urlUtils/urlUtils.js'; - -/** - * @typedef {import('../modules/userId/index.js').Submodule} Submodule - * @typedef {import('../modules/userId/index.js').SubmoduleConfig} SubmoduleConfig - * @typedef {import('../modules/userId/index.js').IdResponse} IdResponse - */ const MODULE_NAME = 'growthCodeId'; const GC_DATA_KEY = '_gc_data'; @@ -173,25 +166,6 @@ export const growthCodeIdSubmodule = { } }; return { callback: resp }; - }, - eids: { - 'growthCodeId': { - getValue: function(data) { - return data.gc_id - }, - source: 'growthcode.io', - atype: 1, - getUidExt: function(data) { - const extendedData = pick(data, [ - 'h1', - 'h2', - 'h3', - ]); - if (Object.keys(extendedData).length) { - return extendedData; - } - } - }, } }; diff --git a/modules/growthCodeRtdProvider.js b/modules/growthCodeRtdProvider.js index b12b25a0951..370ace9a203 100644 --- a/modules/growthCodeRtdProvider.js +++ b/modules/growthCodeRtdProvider.js @@ -5,11 +5,10 @@ import { submodule } from '../src/hook.js' import { getStorageManager } from '../src/storageManager.js'; import { - logMessage, logError, mergeDeep + logMessage, logError, tryAppendQueryString, mergeDeep } 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'; const MODULE_NAME = 'growthCodeRtd'; const LOG_PREFIX = 'GrowthCodeRtd: '; @@ -60,11 +59,7 @@ function init(config, userConsent) { items = tryParse(storage.getDataFromLocalStorage(RTD_CACHE_KEY, null)); - if (configParams.pid === undefined) { - return true; // Die gracefully - } else { - return callServer(configParams, items, expiresAt, userConsent); - } + return callServer(configParams, items, expiresAt, userConsent); } function callServer(configParams, items, expiresAt, userConsent) { // Expire Cache @@ -81,8 +76,8 @@ function callServer(configParams, items, expiresAt, userConsent) { url = tryAppendQueryString(url, 'pid', configParams.pid); url = tryAppendQueryString(url, 'u', window.location.href); url = tryAppendQueryString(url, 'gcid', gcid); - if ((userConsent !== null) && (userConsent.gdpr !== null) && (userConsent.gdpr.consentString)) { - url = tryAppendQueryString(url, 'tcf', userConsent.gdpr.consentString) + if ((userConsent !== null) && (userConsent.gdpr !== null) && (userConsent.gdpr.consentData.getTCData.tcString)) { + url = tryAppendQueryString(url, 'tcf', userConsent.gdpr.consentData.getTCData.tcString) } ajax.ajaxBuilder()(url, { diff --git a/modules/gumgumBidAdapter.js b/modules/gumgumBidAdapter.js index 15af94494a3..d050af4ac8f 100644 --- a/modules/gumgumBidAdapter.js +++ b/modules/gumgumBidAdapter.js @@ -6,15 +6,6 @@ import {getStorageManager} from '../src/storageManager.js'; import {includes} from '../src/polyfill.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse - * @typedef {import('../src/adapters/bidderFactory.js').SyncOptions} SyncOptions - * @typedef {import('../src/adapters/bidderFactory.js').UserSync} UserSync - * @typedef {import('../src/adapters/bidderFactory.js').validBidRequests} validBidRequests - */ - const BIDDER_CODE = 'gumgum'; const storage = getStorageManager({bidderCode: BIDDER_CODE}); const ALIAS_BIDDER_CODE = ['gg']; @@ -23,7 +14,6 @@ const JCSI = { t: 0, rq: 8, pbv: '$prebid.version$' } const SUPPORTED_MEDIA_TYPES = [BANNER, VIDEO]; const TIME_TO_LIVE = 60; const DELAY_REQUEST_TIME = 1800000; // setting to 30 mins -const pubProvidedIdSources = ['dac.co.jp', 'audigent.com', 'id5-sync.com', 'liveramp.com', 'intentiq.com', 'liveintent.com', 'crwdcntrl.net', 'quantcast.com', 'adserver.org', 'yahoo.com'] let invalidRequestIds = {}; let pageViewId = null; @@ -185,7 +175,6 @@ function _getVidParams(attributes) { linearity: li, startdelay: sd, placement: pt, - plcmt, protocols = [], playerSize = [] } = attributes; @@ -197,7 +186,7 @@ function _getVidParams(attributes) { pr = protocols.join(','); } - const result = { + return { mind, maxd, li, @@ -207,11 +196,6 @@ function _getVidParams(attributes) { viw, vih }; - // Add vplcmt property to the result object if plcmt is available - if (plcmt !== undefined && plcmt !== null) { - result.vplcmt = plcmt; - } - return result; } /** @@ -326,23 +310,7 @@ function buildRequests(validBidRequests, bidderRequest) { // ADTS-174 Removed unnecessary checks to fix failing test data.lt = lt; data.to = to; - function jsoStringifynWithMaxLength(data, maxLength) { - let jsonString = JSON.stringify(data); - if (jsonString.length <= maxLength) { - return jsonString; - } else { - const truncatedData = data.slice(0, Math.floor(data.length * (maxLength / jsonString.length))); - jsonString = JSON.stringify(truncatedData); - return jsonString; - } - } - // Send filtered pubProvidedId's - if (userId && userId.pubProvidedId) { - let filteredData = userId.pubProvidedId.filter(item => pubProvidedIdSources.includes(item.source)); - let maxLength = 1800; // replace this with your desired maximum length - let truncatedJsonString = jsoStringifynWithMaxLength(filteredData, maxLength); - data.pubProvidedId = truncatedJsonString - } + // ADJS-1286 Read id5 id linktype field if (userId && userId.id5id && userId.id5id.uid && userId.id5id.ext) { data.id5Id = userId.id5id.uid || null @@ -354,6 +322,9 @@ function buildRequests(validBidRequests, bidderRequest) { // ADTS-134 Retrieve ID envelopes for (const eid in eids) data[eid] = eids[eid]; + // ADJS-1024 & ADSS-1297 & ADTS-175 + gpid && (data.gpid = gpid); + if (mediaTypes.banner) { sizes = mediaTypes.banner.sizes; } else if (mediaTypes.video) { @@ -361,9 +332,6 @@ function buildRequests(validBidRequests, bidderRequest) { data = _getVidParams(mediaTypes.video); } - // ADJS-1024 & ADSS-1297 & ADTS-175 - gpid && (data.gpid = gpid); - if (pageViewId) { data.pv = pageViewId; } @@ -415,11 +383,15 @@ function buildRequests(validBidRequests, bidderRequest) { data.uspConsent = uspConsent; } if (gppConsent) { - data.gppString = bidderRequest.gppConsent.gppString ? bidderRequest.gppConsent.gppString : '' - data.gppSid = Array.isArray(bidderRequest.gppConsent.applicableSections) ? bidderRequest.gppConsent.applicableSections.join(',') : '' + data.gppConsent = { + gppString: bidderRequest.gppConsent.gppString, + gpp_sid: bidderRequest.gppConsent.applicableSections + } } else if (!gppConsent && bidderRequest?.ortb2?.regs?.gpp) { - data.gppString = bidderRequest.ortb2.regs.gpp - data.gppSid = Array.isArray(bidderRequest.ortb2.regs.gpp_sid) ? bidderRequest.ortb2.regs.gpp_sid.join(',') : '' + data.gppConsent = { + gppString: bidderRequest.ortb2.regs.gpp, + gpp_sid: bidderRequest.ortb2.regs.gpp_sid + }; } if (coppa) { data.coppa = coppa; diff --git a/modules/hadronIdSystem.js b/modules/hadronIdSystem.js index 01b0283d3d1..596bf9611e6 100644 --- a/modules/hadronIdSystem.js +++ b/modules/hadronIdSystem.js @@ -9,17 +9,8 @@ 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 { gdprDataHandler, uspDataHandler, gppDataHandler } from '../src/adapterManager.js'; -/** - * @typedef {import('../modules/userId/index.js').Submodule} Submodule - * @typedef {import('../modules/userId/index.js').SubmoduleConfig} SubmoduleConfig - * @typedef {import('../modules/userId/index.js').IdResponse} IdResponse - */ - -const LOG_PREFIX = '[hadronIdSystem]'; const HADRONID_LOCAL_NAME = 'auHadronId'; const MODULE_NAME = 'hadronId'; const AU_GVLID = 561; @@ -51,8 +42,6 @@ const urlAddParams = (url, params) => { return url + (url.indexOf('?') > -1 ? '&' : '?') + params } -const isDebug = config.getConfig('debug') || false; - /** @type {Submodule} */ export const hadronIdSubmodule = { /** @@ -99,7 +88,7 @@ export const hadronIdSubmodule = { } catch (error) { logError(error); } - logInfo(LOG_PREFIX, `Response from backend is ${response}`, responseObj); + logInfo(`Response from backend is ${responseObj}`); hadronId = responseObj['hadronId']; storage.setDataInLocalStorage(HADRONID_LOCAL_NAME, hadronId); responseObj = {id: {hadronId}}; @@ -111,43 +100,16 @@ export const hadronIdSubmodule = { callback(); } }; - let url = urlAddParams( + logInfo('HadronId not found in storage, calling backend...'); + const url = urlAddParams( // config.params.url and config.params.urlArg are not documented // since their use is for debugging purposes only paramOrDefault(config.params.url, DEFAULT_HADRON_URL_ENDPOINT, config.params.urlArg), - `partner_id=${partnerId}&_it=prebid&t=1&src=id` // src=id => the backend was called from getId + `partner_id=${partnerId}&_it=prebid` ); - if (isDebug) { - url += '&debug=1' - } - const gdprConsent = gdprDataHandler.getConsentData() - if (gdprConsent) { - url += `${gdprConsent.consentString ? '&gdprString=' + encodeURIComponent(gdprConsent.consentString) : ''}`; - url += `&gdpr=${gdprConsent.gdprApplies === true ? 1 : 0}`; - } - - const usPrivacyString = uspDataHandler.getConsentData(); - if (usPrivacyString) { - url += `&us_privacy=${encodeURIComponent(usPrivacyString)}`; - } - - const gppConsent = gppDataHandler.getConsentData(); - if (gppConsent) { - url += `${gppConsent.gppString ? '&gpp=' + encodeURIComponent(gppConsent.gppString) : ''}`; - url += `${gppConsent.applicableSections ? '&gpp_sid=' + encodeURIComponent(gppConsent.applicableSections) : ''}`; - } - - logInfo(LOG_PREFIX, `hadronId not found in storage, calling home (${url})`); - ajax(url, callbacks, undefined, {method: 'GET'}); }; return {callback: resp}; - }, - eids: { - 'hadronId': { - source: 'audigent.com', - atype: 1 - }, } }; diff --git a/modules/hadronIdSystem.md b/modules/hadronIdSystem.md index 212030cbcd9..7521cca06ac 100644 --- a/modules/hadronIdSystem.md +++ b/modules/hadronIdSystem.md @@ -35,4 +35,5 @@ The below parameters apply only to the HadronID User ID Module integration. | value | Optional | Object | Used only if the page has a separate mechanism for storing the Hadron ID. The value is an object containing the values to be sent to the adapters. In this scenario, no URL is called and nothing is added to local storage | `{"hadronId": "eb33b0cb-8d35-4722-b9c0-1a31d4064888"}` | | params | Optional | Object | Used to store params for the id system | | params.partnerId | Required | Number | This is the Audigent Partner ID obtained from Audigent. | `1234` | - | +| params.url | Optional | String | Set an alternate GET url for HadronId with this parameter | +| params.urlArg | Optional | Object | Optional url parameter for params.url | diff --git a/modules/hadronRtdProvider.js b/modules/hadronRtdProvider.js index 5c604709b4b..6fb982815c1 100644 --- a/modules/hadronRtdProvider.js +++ b/modules/hadronRtdProvider.js @@ -14,10 +14,6 @@ import {isFn, isStr, isArray, deepEqual, isPlainObject, logError, logInfo} from import {loadExternalScript} from '../src/adloader.js'; import {MODULE_TYPE_RTD} from '../src/activities/modules.js'; -/** - * @typedef {import('../modules/rtdModule/index.js').RtdSubmodule} RtdSubmodule - */ - const LOG_PREFIX = 'User ID - HadronRtdProvider submodule: '; const MODULE_NAME = 'realTimeData'; const SUBMODULE_NAME = 'hadron'; diff --git a/modules/holidBidAdapter.js b/modules/holidBidAdapter.js index fbcbb9492c7..f41829bd123 100644 --- a/modules/holidBidAdapter.js +++ b/modules/holidBidAdapter.js @@ -1,10 +1,4 @@ -import { - deepAccess, - deepSetValue, getBidIdParameter, - isStr, - logMessage, - triggerPixel, -} from '../src/utils.js'; +import {deepAccess, getBidIdParameter, isStr, logMessage, triggerPixel, } from '../src/utils.js'; import * as events from '../src/events.js'; import CONSTANTS from '../src/constants.json'; import {BANNER} from '../src/mediaTypes.js'; @@ -16,7 +10,6 @@ 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 let wurlMap = {} events.on(CONSTANTS.EVENTS.BID_WON, bidWonHandler) @@ -30,19 +23,12 @@ export const spec = { return !!bid.params.adUnitID }, - buildRequests: function (validBidRequests, bidderRequest) { + buildRequests: function (validBidRequests, _bidderRequest) { return validBidRequests.map((bid) => { const requestData = { ...bid.ortb2, - source: {schain: bid.schain}, - id: bidderRequest.bidderRequestId, + id: _bidderRequest.bidderRequestId, imp: [getImp(bid)], - tmax: TMAX, - ...buildStoredRequest(bid) - } - - if (bid.userIdAsEids) { - deepSetValue(requestData, 'user.ext.eids', bid.userIdAsEids) } return { @@ -64,6 +50,8 @@ export const spec = { serverResponse.body.seatbid.map((response) => { response.bid.map((bid) => { const requestId = bidRequest.bidId + // TODO: fix auctionId leak: https://github.com/prebid/Prebid.js/issues/9781 + const auctionId = bidRequest.auctionId const wurl = deepAccess(bid, 'ext.prebid.events.win') const bidResponse = { requestId, @@ -77,7 +65,7 @@ export const spec = { ttl: TIME_TO_LIVE, } - addWurl(requestId, wurl) + addWurl({ auctionId, requestId, wurl }) bidResponses.push(bidResponse) }) @@ -87,15 +75,11 @@ export const spec = { }, getUserSyncs(optionsType, serverResponse, gdprConsent, uspConsent) { - const syncs = [{ - type: 'image', - url: 'https://track.adform.net/Serving/TrackPoint/?pm=2992097&lid=132720821' - }] - if (!serverResponse || serverResponse.length === 0) { - return syncs + return [] } + const syncs = [] const bidders = getBidders(serverResponse) if (optionsType.iframeEnabled && bidders) { @@ -116,14 +100,24 @@ export const spec = { type: 'iframe', url: COOKIE_SYNC_ENDPOINT + strQueryParams + '&type=iframe', }) + + return syncs } - return syncs + return [] }, } function getImp(bid) { - const imp = buildStoredRequest(bid) + const imp = { + ext: { + prebid: { + storedrequest: { + id: getBidIdParameter('adUnitID', bid.params), + }, + }, + }, + } const sizes = bid.sizes && !Array.isArray(bid.sizes[0]) ? [bid.sizes] : bid.sizes @@ -138,18 +132,6 @@ function getImp(bid) { return imp } -function buildStoredRequest(bid) { - return { - ext: { - prebid: { - storedrequest: { - id: getBidIdParameter('adUnitID', bid.params), - }, - }, - }, - } -} - function getBidders(serverResponse) { const bidders = serverResponse .map((res) => Object.keys(res.body.ext.responsetimemillis || [])) @@ -160,28 +142,28 @@ function getBidders(serverResponse) { } } -function addWurl(requestId, wurl) { - if (isStr(requestId)) { - wurlMap[requestId] = wurl +function addWurl(auctionId, adId, wurl) { + if ([auctionId, adId].every(isStr)) { + wurlMap[`${auctionId}${adId}`] = wurl } } -function removeWurl(requestId) { - delete wurlMap[requestId] +function removeWurl(auctionId, adId) { + delete wurlMap[`${auctionId}${adId}`] } -function getWurl(requestId) { - if (isStr(requestId)) { - return wurlMap[requestId] +function getWurl(auctionId, adId) { + if ([auctionId, adId].every(isStr)) { + return wurlMap[`${auctionId}${adId}`] } } function bidWonHandler(bid) { - const wurl = getWurl(bid.requestId) + const wurl = getWurl(bid.auctionId, bid.adId) if (wurl) { logMessage(`Invoking image pixel for wurl on BID_WIN: "${wurl}"`) triggerPixel(wurl) - removeWurl(bid.requestId) + removeWurl(bid.auctionId, bid.adId) } } diff --git a/modules/hybridBidAdapter.js b/modules/hybridBidAdapter.js index 00e109516e5..f746e69cbba 100644 --- a/modules/hybridBidAdapter.js +++ b/modules/hybridBidAdapter.js @@ -5,13 +5,6 @@ import {BANNER, VIDEO} from '../src/mediaTypes.js'; import {Renderer} from '../src/Renderer.js'; import {find} from '../src/polyfill.js'; -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse - * @typedef {import('../src/adapters/bidderFactory.js').validBidRequests} validBidRequests - */ - const BIDDER_CODE = 'hybrid'; const DSP_ENDPOINT = 'https://hbe198.hybrid.ai/prebidhb'; const TRAFFIC_TYPE_WEB = 1; diff --git a/modules/iasRtdProvider.js b/modules/iasRtdProvider.js index b9de7ef4e46..994be7c0804 100644 --- a/modules/iasRtdProvider.js +++ b/modules/iasRtdProvider.js @@ -1,9 +1,7 @@ -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'; /** @type {string} */ const MODULE_NAME = 'realTimeData'; @@ -78,7 +76,7 @@ function getAdUnitPath(adSlot, bidRequest, adUnitPath) { if (!utils.isEmpty(adSlot)) { p = adSlot.gptSlot; } else { - if (!utils.isEmpty(adUnitPath) && adUnitPath.hasOwnProperty(bidRequest.code)) { + if (!utils.isEmpty(adUnitPath) && utils.hasOwn(adUnitPath, bidRequest.code)) { if (utils.isStr(adUnitPath[bidRequest.code]) && !utils.isEmpty(adUnitPath[bidRequest.code])) { p = adUnitPath[bidRequest.code]; } @@ -88,13 +86,13 @@ function getAdUnitPath(adSlot, bidRequest, adUnitPath) { } function stringifySlot(bidRequest, adUnitPath) { - const sizes = getAdUnitSizes(bidRequest); + const sizes = utils.getAdUnitSizes(bidRequest); const id = bidRequest.code; const ss = stringifySlotSizes(sizes); - const adSlot = getGptSlotInfoForAdUnitCode(bidRequest.code); + const adSlot = utils.getGptSlotInfoForAdUnitCode(bidRequest.code); const p = getAdUnitPath(adSlot, bidRequest, adUnitPath); const slot = { id, ss, p }; - const keyValues = Object.keys(slot).map(function (key) { + const keyValues = utils.getKeys(slot).map(function (key) { return [key, slot[key]].join(':'); }); return '{' + keyValues.join(',') + '}'; diff --git a/modules/id5IdSystem.js b/modules/id5IdSystem.js index b10f3c5d2cf..bb8a21264d0 100644 --- a/modules/id5IdSystem.js +++ b/modules/id5IdSystem.js @@ -15,21 +15,12 @@ import { logWarn, safeJSONParse } from '../src/utils.js'; -import {fetch} from '../src/ajax.js'; +import {ajax} from '../src/ajax.js'; import {submodule} from '../src/hook.js'; import {getRefererInfo} from '../src/refererDetection.js'; import {getStorageManager} from '../src/storageManager.js'; -import {uspDataHandler, gppDataHandler} from '../src/adapterManager.js'; +import {uspDataHandler} from '../src/adapterManager.js'; import {MODULE_TYPE_UID} from '../src/activities/modules.js'; -import { GreedyPromise } from '../src/utils/promise.js'; -import { loadExternalScript } from '../src/adloader.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 = 'id5Id'; const GVLID = 131; @@ -46,70 +37,6 @@ const LEGACY_COOKIE_NAMES = ['pbjs-id5id', 'id5id.1st', 'id5id']; export const storage = getStorageManager({moduleType: MODULE_TYPE_UID, moduleName: MODULE_NAME}); -/** - * @typedef {Object} IdResponse - * @property {string} [universal_uid] - The encrypted ID5 ID to pass to bidders - * @property {Object} [ext] - The extensions object to pass to bidders - * @property {Object} [ab_testing] - A/B testing configuration -*/ - -/** - * @typedef {Object} FetchCallConfig - * @property {string} [url] - The URL for the fetch endpoint - * @property {Object} [overrides] - Overrides to apply to fetch parameters -*/ - -/** - * @typedef {Object} ExtensionsCallConfig - * @property {string} [url] - The URL for the extensions endpoint - * @property {string} [method] - Overrides the HTTP method to use to make the call - * @property {Object} [body] - Specifies a body to pass to the extensions endpoint -*/ - -/** - * @typedef {Object} DynamicConfig - * @property {FetchCallConfig} [fetchCall] - The fetch call configuration - * @property {ExtensionsCallConfig} [extensionsCall] - The extensions call configuration -*/ - -/** - * @typedef {Object} ABTestingConfig - * @property {boolean} enabled - Tells whether A/B testing is enabled for this instance - * @property {number} controlGroupPct - A/B testing probability - */ - -/** - * @typedef {Object} Multiplexing - * @property {boolean} [disabled] - Disable multiplexing (instance will work in single mode) - */ - -/** - * @typedef {Object} Diagnostics - * @property {boolean} [publishingDisabled] - Disable diagnostics publishing - * @property {number} [publishAfterLoadInMsec] - Delay in ms after script load after which collected diagnostics are published - * @property {boolean} [publishBeforeWindowUnload] - When true, diagnostics publishing is triggered on Window 'beforeunload' event - * @property {number} [publishingSampleRatio] - Diagnostics publishing sample ratio - */ - -/** - * @typedef {Object} Segment - * @property {string} [destination] - GVL ID or ID5-XX Partner ID. Mandatory - * @property {Array} [ids] - The segment IDs to push. Must contain at least one segment ID. - */ - -/** - * @typedef {Object} Id5PrebidConfig - * @property {number} partner - The ID5 partner ID - * @property {string} pd - The ID5 partner data string - * @property {ABTestingConfig} abTesting - The A/B testing configuration - * @property {boolean} disableExtensions - Disabled extensions call - * @property {string} [externalModuleUrl] - URL for the id5 prebid external module - * @property {Multiplexing} [multiplexing] - Multiplexing options. Only supported when loading the external module. - * @property {Diagnostics} [diagnostics] - Diagnostics options. Supported only in multiplexing - * @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. - */ - /** @type {Submodule} */ export const id5IdSubmodule = { /** @@ -181,7 +108,7 @@ export const id5IdSubmodule = { * @returns {IdResponse|undefined} */ getId(submoduleConfig, consentData, cacheIdObj) { - if (!validateConfig(submoduleConfig)) { + if (!hasRequiredConfig(submoduleConfig)) { return undefined; } @@ -191,8 +118,7 @@ export const id5IdSubmodule = { } const resp = function (cbFunction) { - const fetchFlow = new IdFetchFlow(submoduleConfig, consentData, cacheIdObj, uspDataHandler.getConsentData(), gppDataHandler.getConsentData()); - fetchFlow.execute() + new IdFetchFlow(submoduleConfig, consentData, cacheIdObj, uspDataHandler.getConsentData()).execute() .then(response => { cbFunction(response) }) @@ -216,133 +142,106 @@ export const id5IdSubmodule = { * @return {(IdResponse|function(callback:function))} A response object that contains id and/or callback. */ extendId(config, consentData, cacheIdObj) { + hasRequiredConfig(config); + if (!hasWriteConsentToLocalStorage(consentData)) { logInfo(LOG_PREFIX + 'No consent given for ID5 local storage writing, skipping nb increment.') return cacheIdObj; } - const partnerId = validateConfig(config) ? config.params.partner : 0; + const partnerId = (config && config.params && config.params.partner) || 0; incrementNb(partnerId); logInfo(LOG_PREFIX + 'using cached ID', cacheIdObj); return cacheIdObj; - }, - eids: { - 'id5id': { - getValue: function(data) { - return data.uid - }, - source: 'id5-sync.com', - atype: 1, - getUidExt: function(data) { - if (data.ext) { - return data.ext; - } - } - }, - }, + } }; -export class IdFetchFlow { - constructor(submoduleConfig, gdprConsentData, cacheIdObj, usPrivacyData, gppData) { +class IdFetchFlow { + constructor(submoduleConfig, gdprConsentData, cacheIdObj, usPrivacyData) { this.submoduleConfig = submoduleConfig this.gdprConsentData = gdprConsentData this.cacheIdObj = cacheIdObj this.usPrivacyData = usPrivacyData - this.gppData = gppData } - /** - * Calls the ID5 Servers to fetch an ID5 ID - * @returns {Promise} The result of calling the server side - */ - async execute() { - const configCallPromise = this.#callForConfig(); - if (this.#isExternalModule()) { - try { - return await this.#externalModuleFlow(configCallPromise); - } catch (error) { - logError(LOG_PREFIX + 'Error while performing ID5 external module flow. Continuing with regular flow.', error); - return this.#regularFlow(configCallPromise); - } - } else { - return this.#regularFlow(configCallPromise); - } - } - - #isExternalModule() { - return typeof this.submoduleConfig.params.externalModuleUrl === 'string'; - } - - // eslint-disable-next-line no-dupe-class-members - async #externalModuleFlow(configCallPromise) { - await loadExternalModule(this.submoduleConfig.params.externalModuleUrl); - const fetchFlowConfig = await configCallPromise; - - return this.#getExternalIntegration().fetchId5Id(fetchFlowConfig, this.submoduleConfig.params, getRefererInfo(), this.gdprConsentData, this.usPrivacyData, this.gppData); - } - - // eslint-disable-next-line no-dupe-class-members - #getExternalIntegration() { - return window.id5Prebid && window.id5Prebid.integration; + execute() { + return this.#callForConfig(this.submoduleConfig) + .then(fetchFlowConfig => { + return this.#callForExtensions(fetchFlowConfig.extensionsCall) + .then(extensionsData => { + return this.#callId5Fetch(fetchFlowConfig.fetchCall, extensionsData) + }) + }) + .then(fetchCallResponse => { + try { + resetNb(this.submoduleConfig.params.partner); + if (fetchCallResponse.privacy) { + storeInLocalStorage(ID5_PRIVACY_STORAGE_NAME, JSON.stringify(fetchCallResponse.privacy), NB_EXP_DAYS); + } + } catch (error) { + logError(LOG_PREFIX + error); + } + return fetchCallResponse; + }) } - // eslint-disable-next-line no-dupe-class-members - async #regularFlow(configCallPromise) { - const fetchFlowConfig = await configCallPromise; - const extensionsData = await this.#callForExtensions(fetchFlowConfig.extensionsCall); - const fetchCallResponse = await this.#callId5Fetch(fetchFlowConfig.fetchCall, extensionsData); - return this.#processFetchCallResponse(fetchCallResponse); + #ajaxPromise(url, data, options) { + return new Promise((resolve, reject) => { + ajax(url, + { + success: function (res) { + resolve(res) + }, + error: function (err) { + reject(err) + } + }, data, options) + }) } // eslint-disable-next-line no-dupe-class-members - async #callForConfig() { - let url = this.submoduleConfig.params.configUrl || ID5_API_CONFIG_URL; // override for debug/test purposes only - const response = await fetch(url, { - method: 'POST', - body: JSON.stringify(this.submoduleConfig) - }); - if (!response.ok) { - throw new Error('Error while calling config endpoint: ', response); - } - const dynamicConfig = await response.json(); - logInfo(LOG_PREFIX + 'config response received from the server', dynamicConfig); - return dynamicConfig; + #callForConfig(submoduleConfig) { + let url = submoduleConfig.params.configUrl || ID5_API_CONFIG_URL; // override for debug/test purposes only + return this.#ajaxPromise(url, JSON.stringify(submoduleConfig), {method: 'POST'}) + .then(response => { + let responseObj = JSON.parse(response); + logInfo(LOG_PREFIX + 'config response received from the server', responseObj); + return responseObj; + }); } // eslint-disable-next-line no-dupe-class-members - async #callForExtensions(extensionsCallConfig) { + #callForExtensions(extensionsCallConfig) { if (extensionsCallConfig === undefined) { - return undefined; + return Promise.resolve(undefined) } - 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 }); - if (!response.ok) { - throw new Error('Error while calling extensions endpoint: ', response); - } - const extensions = await response.json(); - logInfo(LOG_PREFIX + 'extensions response received from the server', extensions); - return extensions; + let extensionsUrl = extensionsCallConfig.url + let method = extensionsCallConfig.method || 'GET' + let data = method === 'GET' ? undefined : JSON.stringify(extensionsCallConfig.body || {}) + return this.#ajaxPromise(extensionsUrl, data, {'method': method}) + .then(response => { + let responseObj = JSON.parse(response); + logInfo(LOG_PREFIX + 'extensions response received from the server', responseObj); + return responseObj; + }) } // eslint-disable-next-line no-dupe-class-members - async #callId5Fetch(fetchCallConfig, extensionsData) { - const fetchUrl = fetchCallConfig.url; - const additionalData = fetchCallConfig.overrides || {}; - const body = JSON.stringify({ + #callId5Fetch(fetchCallConfig, extensionsData) { + let url = fetchCallConfig.url; + let additionalData = fetchCallConfig.overrides || {}; + let data = { ...this.#createFetchRequestData(), ...additionalData, extensions: extensionsData - }); - const response = await fetch(fetchUrl, { method: 'POST', body, credentials: 'include' }); - if (!response.ok) { - throw new Error('Error while calling fetch endpoint: ', response); - } - const fetchResponse = await response.json(); - logInfo(LOG_PREFIX + 'fetch response received from the server', fetchResponse); - return fetchResponse; + }; + return this.#ajaxPromise(url, JSON.stringify(data), {method: 'POST', withCredentials: true}) + .then(response => { + let responseObj = JSON.parse(response); + logInfo(LOG_PREFIX + 'fetch response received from the server', responseObj); + return responseObj; + }); } // eslint-disable-next-line no-dupe-class-members @@ -351,7 +250,7 @@ export class IdFetchFlow { const hasGdpr = (this.gdprConsentData && typeof this.gdprConsentData.gdprApplies === 'boolean' && this.gdprConsentData.gdprApplies) ? 1 : 0; const referer = getRefererInfo(); const signature = (this.cacheIdObj && this.cacheIdObj.signature) ? this.cacheIdObj.signature : getLegacyCookieSignature(); - const nbPage = incrementAndResetNb(params.partner); + const nbPage = incrementNb(params.partner); const data = { 'partner': params.partner, 'gdpr': hasGdpr, @@ -374,11 +273,6 @@ export class IdFetchFlow { if (this.usPrivacyData !== undefined && !isEmpty(this.usPrivacyData) && !isEmptyStr(this.usPrivacyData)) { data.us_privacy = this.usPrivacyData; } - if (this.gppData) { - data.gpp_string = this.gppData.gppString; - data.gpp_sid = this.gppData.applicableSections; - } - if (signature !== undefined && !isEmptyStr(signature)) { data.s = signature; } @@ -397,52 +291,11 @@ export class IdFetchFlow { } return data; } - - // eslint-disable-next-line no-dupe-class-members - #processFetchCallResponse(fetchCallResponse) { - try { - if (fetchCallResponse.privacy) { - storeInLocalStorage(ID5_PRIVACY_STORAGE_NAME, JSON.stringify(fetchCallResponse.privacy), NB_EXP_DAYS); - } - } catch (error) { - logError(LOG_PREFIX + 'Error while writing privacy info into local storage.', error); - } - return fetchCallResponse; - } } -async function loadExternalModule(url) { - return new GreedyPromise((resolve, reject) => { - if (window.id5Prebid) { - // Already loaded - resolve(); - } else { - try { - loadExternalScript(url, 'id5', resolve); - } catch (error) { - reject(error); - } - } - }); -} - -function validateConfig(config) { - if (!config || !config.params || !config.params.partner) { - logError(LOG_PREFIX + 'partner required to be defined'); - return false; - } - - const partner = config.params.partner - if (typeof partner === 'string' || partner instanceof String) { - let parsedPartnerId = parseInt(partner); - if (isNaN(parsedPartnerId) || parsedPartnerId < 0) { - logError(LOG_PREFIX + 'partner required to be a number or a String parsable to a positive integer'); - return false; - } else { - config.params.partner = parsedPartnerId; - } - } else if (typeof partner !== 'number') { - logError(LOG_PREFIX + 'partner required to be a number or a String parsable to a positive integer'); +function hasRequiredConfig(config) { + if (!config || !config.params || !config.params.partner || typeof config.params.partner !== 'number') { + logError(LOG_PREFIX + 'partner required to be defined as a number'); return false; } @@ -486,10 +339,8 @@ function incrementNb(partnerId) { return nb; } -function incrementAndResetNb(partnerId) { - const result = incrementNb(partnerId); +function resetNb(partnerId) { storeNbInCache(partnerId, 0); - return result; } function getLegacyCookieSignature() { @@ -528,7 +379,7 @@ export function getFromLocalStorage(key) { * by default it's not required * @param {string} key * @param {any} value - * @param {number} expDays + * @param {integer} expDays */ export function storeInLocalStorage(key, value, expDays) { storage.setDataInLocalStorage(`${key}_exp`, expDaysStr(expDays)); diff --git a/modules/id5IdSystem.md b/modules/id5IdSystem.md index 592c69056fa..cf90290b1d8 100644 --- a/modules/id5IdSystem.md +++ b/modules/id5IdSystem.md @@ -1,6 +1,6 @@ -# ID5 ID +# ID5 Universal ID -The ID5 ID is a shared, neutral identifier that publishers and ad tech platforms can use to recognise users even in environments where 3rd party cookies are not available. The ID5 ID is designed to respect users' privacy choices and publishers’ preferences throughout the advertising value chain. For more information about the ID5 ID and detailed integration docs, please visit [our documentation](https://wiki.id5.io/en/identitycloud/retrieve-id5-ids/prebid-user-id-module/id5-prebid-user-id-module). +The ID5 ID is a shared, neutral identifier that publishers and ad tech platforms can use to recognise users even in environments where 3rd party cookies are not available. The ID5 ID is designed to respect users' privacy choices and publishers’ preferences throughout the advertising value chain. For more information about the ID5 ID and detailed integration docs, please visit [our documentation](https://support.id5.io/portal/en/kb/articles/prebid-js-user-id-module). ## ID5 ID Registration @@ -25,12 +25,11 @@ pbjs.setConfig({ name: 'id5Id', params: { partner: 173, // change to the Partner Number you received from ID5 - externalModuleUrl: "https://cdn.id5-sync.com/api/1.0/id5PrebidModule.js" // optional but recommended pd: 'MT1iNTBjY...', // optional, see table below for a link to how to generate this abTesting: { // optional enabled: true, // false by default controlGroupPct: 0.1 // valid values are 0.0 - 1.0 (inclusive) - }, + }, disableExtensions: false // optional }, storage: { @@ -50,8 +49,7 @@ pbjs.setConfig({ | 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.pd | Optional | String | Partner-supplied data used for linking ID5 IDs across domains. See [our documentation](https://support.id5.io/portal/en/kb/articles/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` | @@ -70,6 +68,3 @@ pbjs.setConfig({ Publishers may want to test the value of the ID5 ID with their downstream partners. While there are various ways to do this, A/B testing is a standard approach. Instead of publishers manually enabling or disabling the ID5 User ID Module based on their control group settings (which leads to fewer calls to ID5, reducing our ability to recognize the user), we have baked this in to our module directly. To turn on A/B Testing, simply edit the configuration (see above table) to enable it and set what percentage of users you would like to set for the control group. The control group is the set of user where an ID5 ID will not be exposed in to bid adapters or in the various user id functions available on the `pbjs` global. An additional value of `ext.abTestingControlGroup` will be set to `true` or `false` that can be used to inform reporting systems that the user was in the control group or not. It's important to note that the control group is user based, and not request based. In other words, from one page view to another, a user will always be in or out of the control group. - -### A Note on Using Multiple Wrappers -If you or your monetization partners are deploying multiple Prebid wrappers on your websites, you should make sure you add the ID5 ID User ID module to *every* wrapper. Only the bidders configured in the Prebid wrapper where the ID5 ID User ID module is installed and configured will be able to pick up the ID5 ID. Bidders from other Prebid instances will not be able to pick up the ID5 ID. diff --git a/modules/idWardRtdProvider.js b/modules/idWardRtdProvider.js index dd08a132b2d..29dda216fdc 100644 --- a/modules/idWardRtdProvider.js +++ b/modules/idWardRtdProvider.js @@ -9,9 +9,6 @@ import {getStorageManager} from '../src/storageManager.js'; import {submodule} from '../src/hook.js'; import {isPlainObject, mergeDeep, logMessage, logError} from '../src/utils.js'; import {MODULE_TYPE_RTD} from '../src/activities/modules.js'; -/** - * @typedef {import('../modules/rtdModule/index.js').RtdSubmodule} RtdSubmodule - */ const MODULE_NAME = 'realTimeData'; const SUBMODULE_NAME = 'idWard'; @@ -31,9 +28,9 @@ function addRealTimeData(ortb2, rtd) { } /** - * Try parsing stringified array of segment IDs. - * @param {String} data - */ + * Try parsing stringified array of segment IDs. + * @param {String} data + */ function tryParse(data) { try { return JSON.parse(data); @@ -44,12 +41,12 @@ function tryParse(data) { } /** - * Real-time data retrieval from ID Ward - * @param {Object} reqBidsConfigObj - * @param {function} onDone - * @param {Object} rtdConfig - * @param {Object} userConsent - */ + * Real-time data retrieval from ID Ward + * @param {Object} reqBidsConfigObj + * @param {function} onDone + * @param {Object} rtdConfig + * @param {Object} userConsent + */ export function getRealTimeData(reqBidsConfigObj, onDone, rtdConfig, userConsent) { if (rtdConfig && isPlainObject(rtdConfig.params)) { const jsonData = storage.getDataFromLocalStorage(rtdConfig.params.cohortStorageKey) @@ -88,11 +85,11 @@ export function getRealTimeData(reqBidsConfigObj, onDone, rtdConfig, userConsent } /** - * Module init - * @param {Object} provider - * @param {Object} userConsent - * @return {boolean} - */ + * Module init + * @param {Object} provider + * @param {Object} userConsent + * @return {boolean} + */ function init(provider, userConsent) { return true; } diff --git a/modules/identityLinkIdSystem.js b/modules/identityLinkIdSystem.js index 82aa2303e1c..ab10288f38f 100644 --- a/modules/identityLinkIdSystem.js +++ b/modules/identityLinkIdSystem.js @@ -10,21 +10,11 @@ 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 { gppDataHandler } from '../src/adapterManager.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 = 'identityLink'; export const storage = getStorageManager({moduleType: MODULE_TYPE_UID, moduleName: MODULE_NAME}); -const liverampEnvelopeName = '_lr_env'; - /** @type {Submodule} */ export const identityLinkSubmodule = { /** @@ -61,21 +51,18 @@ export const identityLinkSubmodule = { } const hasGdpr = (consentData && typeof consentData.gdprApplies === 'boolean' && consentData.gdprApplies) ? 1 : 0; const gdprConsentString = hasGdpr ? consentData.consentString : ''; + const tcfPolicyV2 = utils.deepAccess(consentData, 'vendorData.tcfPolicyVersion') === 2; // use protocol relative urls for http or https if (hasGdpr && (!gdprConsentString || gdprConsentString === '')) { utils.logInfo('identityLink: Consent string is required to call envelope API.'); return; } - const gppData = gppDataHandler.getConsentData(); - const gppString = gppData && gppData.gppString ? gppData.gppString : false; - const gppSectionId = gppData && gppData.gppString && gppData.applicableSections.length > 0 && gppData.applicableSections[0] !== -1 ? gppData.applicableSections[0] : false; - const hasGpp = gppString && gppSectionId; - const url = `https://api.rlcdn.com/api/identity/envelope?pid=${configParams.pid}${hasGdpr ? '&ct=4&cv=' + gdprConsentString : ''}${hasGpp ? '&gpp=' + gppString + '&gpp_sid=' + gppSectionId : ''}`; + const url = `https://api.rlcdn.com/api/identity/envelope?pid=${configParams.pid}${hasGdpr ? (tcfPolicyV2 ? '&ct=4&cv=' : '&ct=1&cv=') + gdprConsentString : ''}`; let resp; resp = function (callback) { // Check ats during callback so it has a chance to initialise. // If ats library is available, use it to retrieve envelope. If not use standard third party endpoint - if (window.ats && window.ats.retrieveEnvelope) { + if (window.ats) { utils.logInfo('identityLink: ATS exists!'); window.ats.retrieveEnvelope(function (envelope) { if (envelope) { @@ -87,24 +74,11 @@ export const identityLinkSubmodule = { } }); } else { - // try to get envelope directly from storage if ats lib is not present on a page - let envelope = getEnvelopeFromStorage(); - if (envelope) { - utils.logInfo('identityLink: LiveRamp envelope successfully retrieved from storage!'); - callback(JSON.parse(envelope).envelope); - } else { - getEnvelope(url, callback, configParams); - } + getEnvelope(url, callback, configParams); } }; return { callback: resp }; - }, - eids: { - 'idl_env': { - source: 'liveramp.com', - atype: 3 - }, } }; // return envelope from third party endpoint @@ -147,9 +121,4 @@ function setEnvelopeSource(src) { storage.setCookie('_lr_env_src_ats', src, now.toUTCString()); } -export function getEnvelopeFromStorage() { - let rawEnvelope = storage.getCookie(liverampEnvelopeName) || storage.getDataFromLocalStorage(liverampEnvelopeName); - return rawEnvelope ? window.atob(rawEnvelope) : undefined; -} - submodule('userId', identityLinkSubmodule); diff --git a/modules/idxIdSystem.js b/modules/idxIdSystem.js index db545eecd8c..3c00bbde34c 100644 --- a/modules/idxIdSystem.js +++ b/modules/idxIdSystem.js @@ -9,11 +9,6 @@ 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 - * @typedef {import('../modules/userId/index.js').SubmoduleConfig} SubmoduleConfig - */ - const IDX_MODULE_NAME = 'idx'; const IDX_COOKIE_NAME = '_idx'; export const storage = getStorageManager({moduleType: MODULE_TYPE_UID, moduleName: IDX_MODULE_NAME}); @@ -62,12 +57,6 @@ export const idxIdSubmodule = { } } return undefined; - }, - eids: { - 'idx': { - source: 'idx.lat', - atype: 1 - }, } }; submodule('userId', idxIdSubmodule); diff --git a/modules/illuminBidAdapter.js b/modules/illuminBidAdapter.js deleted file mode 100644 index 36ff7bea0a3..00000000000 --- a/modules/illuminBidAdapter.js +++ /dev/null @@ -1,336 +0,0 @@ -import {_each, deepAccess, parseSizesInput, parseUrl, uniques, isFn} from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER, VIDEO} from '../src/mediaTypes.js'; -import {getStorageManager} from '../src/storageManager.js'; -import {config} from '../src/config.js'; - -const DEFAULT_SUB_DOMAIN = 'exchange'; -const BIDDER_CODE = 'illumin'; -const BIDDER_VERSION = '1.0.0'; -const CURRENCY = 'USD'; -const TTL_SECONDS = 60 * 5; -const UNIQUE_DEAL_ID_EXPIRY = 1000 * 60 * 15; -const storage = getStorageManager({bidderCode: BIDDER_CODE}); - -function getTopWindowQueryParams() { - try { - const parsedUrl = parseUrl(window.top.document.URL, {decodeSearchAsString: true}); - return parsedUrl.search; - } catch (e) { - return ''; - } -} - -export function createDomain(subDomain = DEFAULT_SUB_DOMAIN) { - return `https://${subDomain}.illumin.com`; -} - -export function extractCID(params) { - return params.cId; -} - -export function extractPID(params) { - return params.pId; -} - -export function extractSubDomain(params) { - return params.subDomain; -} - -function isBidRequestValid(bid) { - const params = bid.params || {}; - return !!(extractCID(params) && extractPID(params)); -} - -function buildRequest(bid, topWindowUrl, sizes, bidderRequest, bidderTimeout) { - const { - params, - bidId, - userId, - adUnitCode, - schain, - mediaTypes, - ortb2Imp, - bidderRequestId, - bidRequestsCount, - bidderRequestsCount, - bidderWinsCount - } = bid; - let {bidFloor, ext} = params; - const hashUrl = hashCode(topWindowUrl); - const uniqueDealId = getUniqueDealId(hashUrl); - const cId = extractCID(params); - const pId = extractPID(params); - const subDomain = extractSubDomain(params); - - const gpid = deepAccess(bid, 'ortb2Imp.ext.gpid', deepAccess(bid, 'ortb2Imp.ext.data.pbadslot', '')); - - if (isFn(bid.getFloor)) { - const floorInfo = bid.getFloor({ - currency: 'USD', - mediaType: '*', - size: '*' - }); - - if (floorInfo.currency === 'USD') { - bidFloor = floorInfo.floor; - } - } - - let data = { - url: encodeURIComponent(topWindowUrl), - uqs: getTopWindowQueryParams(), - cb: Date.now(), - bidFloor: bidFloor, - bidId: bidId, - referrer: bidderRequest.refererInfo.ref, - adUnitCode: adUnitCode, - publisherId: pId, - sizes: sizes, - uniqueDealId: uniqueDealId, - bidderVersion: BIDDER_VERSION, - prebidVersion: '$prebid.version$', - res: `${screen.width}x${screen.height}`, - schain: schain, - mediaTypes: mediaTypes, - gpid: gpid, - transactionId: ortb2Imp?.ext?.tid, - bidderRequestId: bidderRequestId, - bidRequestsCount: bidRequestsCount, - bidderRequestsCount: bidderRequestsCount, - bidderWinsCount: bidderWinsCount, - bidderTimeout: bidderTimeout - }; - - appendUserIdsToRequestPayload(data, userId); - - const sua = deepAccess(bidderRequest, 'ortb2.device.sua'); - - if (sua) { - data.sua = sua; - } - - if (bidderRequest.gdprConsent) { - if (bidderRequest.gdprConsent.consentString) { - data.gdprConsent = bidderRequest.gdprConsent.consentString; - } - if (bidderRequest.gdprConsent.gdprApplies !== undefined) { - data.gdpr = bidderRequest.gdprConsent.gdprApplies ? 1 : 0; - } - } - if (bidderRequest.uspConsent) { - data.usPrivacy = bidderRequest.uspConsent; - } - - if (bidderRequest.gppConsent) { - data.gppString = bidderRequest.gppConsent.gppString; - data.gppSid = bidderRequest.gppConsent.applicableSections; - } else if (bidderRequest.ortb2?.regs?.gpp) { - data.gppString = bidderRequest.ortb2.regs.gpp; - data.gppSid = bidderRequest.ortb2.regs.gpp_sid; - } - - const dto = { - method: 'POST', - url: `${createDomain(subDomain)}/prebid/multi/${cId}`, - data: data - }; - - _each(ext, (value, key) => { - dto.data['ext.' + key] = value; - }); - - return dto; -} - -function appendUserIdsToRequestPayload(payloadRef, userIds) { - let key; - _each(userIds, (userId, idSystemProviderName) => { - key = `uid.${idSystemProviderName}`; - - switch (idSystemProviderName) { - case 'digitrustid': - payloadRef[key] = deepAccess(userId, 'data.id'); - break; - case 'lipb': - payloadRef[key] = userId.lipbid; - break; - case 'parrableId': - payloadRef[key] = userId.eid; - break; - case 'id5id': - payloadRef[key] = userId.uid; - break; - default: - payloadRef[key] = userId; - } - }); -} - -function buildRequests(validBidRequests, bidderRequest) { - const topWindowUrl = bidderRequest.refererInfo.page || bidderRequest.refererInfo.topmostLocation; - const bidderTimeout = config.getConfig('bidderTimeout'); - const requests = []; - validBidRequests.forEach(validBidRequest => { - const sizes = parseSizesInput(validBidRequest.sizes); - const request = buildRequest(validBidRequest, topWindowUrl, sizes, bidderRequest, bidderTimeout); - requests.push(request); - }); - return requests; -} - -function interpretResponse(serverResponse, request) { - if (!serverResponse || !serverResponse.body) { - return []; - } - const {bidId} = request.data; - const {results} = serverResponse.body; - - let output = []; - - try { - results.forEach(result => { - const { - creativeId, - ad, - price, - exp, - width, - height, - currency, - metaData, - advertiserDomains, - mediaType = BANNER - } = result; - if (!ad || !price) { - return; - } - - const response = { - requestId: bidId, - cpm: price, - width: width, - height: height, - creativeId: creativeId, - currency: currency || CURRENCY, - netRevenue: true, - ttl: exp || TTL_SECONDS, - }; - - if (metaData) { - Object.assign(response, { - meta: metaData - }) - } else { - Object.assign(response, { - meta: { - advertiserDomains: advertiserDomains || [] - } - }) - } - - if (mediaType === BANNER) { - Object.assign(response, { - ad: ad, - }); - } else { - Object.assign(response, { - vastXml: ad, - mediaType: VIDEO - }); - } - output.push(response); - }); - return output; - } catch (e) { - return []; - } -} - -function getUserSyncs(syncOptions, responses, gdprConsent = {}, uspConsent = '') { - let syncs = []; - const {iframeEnabled, pixelEnabled} = syncOptions; - const {gdprApplies, consentString = ''} = gdprConsent; - - const cidArr = responses.filter(resp => deepAccess(resp, 'body.cid')).map(resp => resp.body.cid).filter(uniques); - const params = `?cid=${encodeURIComponent(cidArr.join(','))}&gdpr=${gdprApplies ? 1 : 0}&gdpr_consent=${encodeURIComponent(consentString || '')}&us_privacy=${encodeURIComponent(uspConsent || '')}` - if (iframeEnabled) { - syncs.push({ - type: 'iframe', - url: `https://sync.illumin.com/api/sync/iframe/${params}` - }); - } - if (pixelEnabled) { - syncs.push({ - type: 'image', - url: `https://sync.illumin.com/api/sync/image/${params}` - }); - } - return syncs; -} - -export function hashCode(s, prefix = '_') { - const l = s.length; - let h = 0 - let i = 0; - if (l > 0) { - while (i < l) { - h = (h << 5) - h + s.charCodeAt(i++) | 0; - } - } - return prefix + h; -} - -export function getUniqueDealId(key, expiry = UNIQUE_DEAL_ID_EXPIRY) { - const storageKey = `u_${key}`; - const now = Date.now(); - const data = getStorageItem(storageKey); - let uniqueId; - - if (!data || !data.value || now - data.created > expiry) { - uniqueId = `${key}_${now.toString()}`; - setStorageItem(storageKey, uniqueId); - } else { - uniqueId = data.value; - } - - return uniqueId; -} - -export function getStorageItem(key) { - try { - return tryParseJSON(storage.getDataFromLocalStorage(key)); - } catch (e) { - } - - return null; -} - -export function setStorageItem(key, value, timestamp) { - try { - const created = timestamp || Date.now(); - const data = JSON.stringify({value, created}); - storage.setDataInLocalStorage(key, data); - } catch (e) { - } -} - -export function tryParseJSON(value) { - try { - return JSON.parse(value); - } catch (e) { - return value; - } -} - -export const spec = { - code: BIDDER_CODE, - version: BIDDER_VERSION, - supportedMediaTypes: [BANNER, VIDEO], - isBidRequestValid, - buildRequests, - interpretResponse, - getUserSyncs -}; - -registerBidder(spec); diff --git a/modules/illuminBidAdapter.md b/modules/illuminBidAdapter.md deleted file mode 100644 index 8ca656230e4..00000000000 --- a/modules/illuminBidAdapter.md +++ /dev/null @@ -1,35 +0,0 @@ -# Overview - -**Module Name:** Illumin Bid Adapter - -**Module Type:** Bidder Adapter - -**Maintainer:** integrations@illumin.com - -# Description - -Module that connects to Illumin's demand sources. - -# Test Parameters -```js -var adUnits = [ - { - code: 'test-ad', - sizes: [[300, 250]], - bids: [ - { - bidder: 'illumin', - params: { - cId: '562524b21b1c1f08117fc7f9', - pId: '59ac17c192832d0011283fe3', - bidFloor: 0.0001, - ext: { - param1: 'loremipsum', - param2: 'dolorsitamet' - } - } - } - ] - } -]; -``` diff --git a/modules/imRtdProvider.js b/modules/imRtdProvider.js index 78681c2beda..26d49c49f8c 100644 --- a/modules/imRtdProvider.js +++ b/modules/imRtdProvider.js @@ -20,10 +20,6 @@ import { import {submodule} from '../src/hook.js'; import {MODULE_TYPE_RTD} from '../src/activities/modules.js'; -/** - * @typedef {import('../modules/rtdModule/index.js').RtdSubmodule} RtdSubmodule - */ - export const imUidLocalName = '__im_uid'; export const imVidCookieName = '_im_vid'; export const imRtdLocalName = '__im_sids'; @@ -54,8 +50,8 @@ function getSegments(segments, moduleConfig) { } /** - * @param {string} bidderName - */ +* @param {string} bidderName +*/ export function getBidderFunction(bidderName) { const biddersFunction = { pubmatic: function (bid, data, moduleConfig) { diff --git a/modules/imdsBidAdapter.js b/modules/imdsBidAdapter.js index 4cad1d614c5..545a0bd1ac3 100644 --- a/modules/imdsBidAdapter.js +++ b/modules/imdsBidAdapter.js @@ -1,16 +1,14 @@ 'use strict'; -import {deepAccess, deepSetValue, isFn, isPlainObject, logWarn, mergeDeep} from '../src/utils.js'; +import {deepAccess, deepSetValue, getAdUnitSizes, isFn, isPlainObject, logWarn} from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {BANNER, VIDEO} from '../src/mediaTypes.js'; import {includes} from '../src/polyfill.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 USER_SYNC_HOST = 'https://ad-cdn.technoratimedia.com'; const VIDEO_PARAMS = [ 'minduration', 'maxduration', 'startdelay', 'placement', 'linearity', 'mimes', 'protocols', 'api' ]; const BLOCKED_AD_SIZES = [ '1x1', @@ -40,11 +38,11 @@ export const spec = { return; } const refererInfo = bidderRequest.refererInfo; - // start with some defaults, overridden by anything set in ortb2, if provided. - const openRtbBidRequest = mergeDeep({ + const openRtbBidRequest = { id: bidderRequest.bidderRequestId, site: { - domain: refererInfo.domain, + // TODO: does the fallback make sense here? + domain: refererInfo.domain || location.hostname, page: refererInfo.page, ref: refererInfo.ref }, @@ -52,7 +50,7 @@ export const spec = { ua: navigator.userAgent }, imp: [] - }, bidderRequest.ortb2 || {}); + }; const tmax = bidderRequest.timeout; if (tmax) { @@ -103,17 +101,9 @@ export const spec = { } }); - // Move us_privacy from regs.ext to regs if there isn't already a us_privacy in regs - if (openRtbBidRequest.regs?.ext?.us_privacy && !openRtbBidRequest.regs?.us_privacy) { - deepSetValue(openRtbBidRequest, 'regs.us_privacy', openRtbBidRequest.regs.ext.us_privacy); - } - - // Remove regs.ext.us_privacy - if (openRtbBidRequest.regs?.ext?.us_privacy) { - delete openRtbBidRequest.regs.ext.us_privacy; - if (Object.keys(openRtbBidRequest.regs.ext).length < 1) { - delete openRtbBidRequest.regs.ext; - } + // CCPA + if (bidderRequest.uspConsent) { + deepSetValue(openRtbBidRequest, 'regs.ext.us_privacy', bidderRequest.uspConsent); } // User ID @@ -127,7 +117,7 @@ export const spec = { if (openRtbBidRequest.imp.length && seatId) { return { method: 'POST', - url: `${BID_SCHEME}${seatId}.${BID_DOMAIN}/openrtb/bids/${seatId}?src=pbjs%2F$prebid.version$`, + url: `${BID_SCHEME}${seatId}.${BID_DOMAIN}/openrtb/bids/${seatId}?src=$$REPO_AND_VERSION$$`, data: openRtbBidRequest, options: { contentType: 'application/json', @@ -224,6 +214,7 @@ export const spec = { }; if (!serverResponse.body || typeof serverResponse.body != 'object') { + logWarn('IMDS: server returned empty/non-json response: ' + JSON.stringify(serverResponse.body)); return; } const {id, seatbid: seatbids} = serverResponse.body; @@ -303,31 +294,16 @@ export const spec = { } return bids; }, - getUserSyncs: function(syncOptions, serverResponses, gdprConsent, uspConsent, gppConsent) { + getUserSyncs: function (syncOptions, serverResponses) { const syncs = []; - const queryParams = ['src=pbjs%2F$prebid.version$']; - if (gdprConsent) { - queryParams.push(`gdpr=${Number(gdprConsent.gdprApplies && 1)}&consent=${encodeURIComponent(gdprConsent.consentString || '')}`); - } - if (uspConsent) { - queryParams.push('us_privacy=' + encodeURIComponent(uspConsent)); - } - if (gppConsent) { - queryParams.push('gpp=' + encodeURIComponent(gppConsent.gppString || '') + '&gppsid=' + encodeURIComponent((gppConsent.applicableSections || []).join(','))); - } - if (syncOptions.iframeEnabled) { syncs.push({ type: 'iframe', - url: `${USER_SYNC_IFRAME_URL}?${queryParams.join('&')}` - }); - } else if (syncOptions.pixelEnabled) { - syncs.push({ - type: 'image', - url: `${USER_SYNC_PIXEL_URL}?srv=cs&${queryParams.join('&')}` + url: `${USER_SYNC_HOST}/html/usersync.html?src=$$REPO_AND_VERSION$$` }); + } else { + logWarn('IMDS: Please enable iframe based user sync.'); } - return syncs; } }; diff --git a/modules/imdsBidAdapter.md b/modules/imdsBidAdapter.md index 2a50868d726..15fb407e7ef 100644 --- a/modules/imdsBidAdapter.md +++ b/modules/imdsBidAdapter.md @@ -11,11 +11,11 @@ Maintainer: eng-demand@imds.tv The iMedia Digital Services adapter requires setup and approval from iMedia Digital Services. Please reach out to your account manager for more information. -### Google Ad Manager Video Creative -To use video, setup a `VAST redirect` creative within Google Ad Manager with the following VAST tag URL: +### DFP Video Creative +To use video, setup a `VAST redirect` creative within Google AdManager (DFP) with the following VAST tag URL: -```text -https://track.technoratimedia.com/openrtb/tags?ID=%%PATTERN:hb_uuid_imds%%&AUCTION_PRICE=%%PATTERN:hb_pb_imds%% +``` +https://track.technoratimedia.com/openrtb/tags?ID=%%PATTERN:hb_cache_id_synacorm%%&AUCTION_PRICE=%%PATTERN:hb_pb_synacormedia%% ``` # Test Parameters diff --git a/modules/impactifyBidAdapter.js b/modules/impactifyBidAdapter.js index ea446bd150d..f2bf9aaddcb 100644 --- a/modules/impactifyBidAdapter.js +++ b/modules/impactifyBidAdapter.js @@ -1,18 +1,7 @@ -'use strict'; - -import { deepAccess, deepSetValue, generateUUID } 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'; - -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse - * @typedef {import('../src/adapters/bidderFactory.js').SyncOptions} SyncOptions - * @typedef {import('../src/adapters/bidderFactory.js').UserSync} UserSync - */ +import {deepAccess, deepSetValue, generateUUID} from '../src/utils.js'; +import {registerBidder} from '../src/adapters/bidderFactory.js'; +import {config} from '../src/config.js'; +import {ajax} from '../src/ajax.js'; const BIDDER_CODE = 'impactify'; const BIDDER_ALIAS = ['imp']; @@ -21,125 +10,58 @@ const DEFAULT_VIDEO_WIDTH = 640; const DEFAULT_VIDEO_HEIGHT = 360; const ORIGIN = 'https://sonic.impactify.media'; const LOGGER_URI = 'https://logger.impactify.media'; -const AUCTION_URI = '/bidder'; -const COOKIE_SYNC_URI = '/static/cookie_sync.html'; -const GVL_ID = 606; -const GET_CONFIG = config.getConfig; -export const STORAGE = getStorageManager({ gvlid: GVL_ID, bidderCode: BIDDER_CODE }); -export const STORAGE_KEY = '_im_str' - -/** - * Helpers object - * @type {{getExtParamsFromBid(*): {impactify: {appId}}, createOrtbImpVideoObj(*): {context: string, playerSize: [number,number], id: string, mimes: [string]}, getDeviceType(): (number), createOrtbImpBannerObj(*, *): {format: [], id: string}}} - */ -const helpers = { - getExtParamsFromBid(bid) { - let ext = { - impactify: { - appId: bid.params.appId - }, - }; - - if (typeof bid.params.format == 'string') { - ext.impactify.format = bid.params.format; - } - - if (typeof bid.params.style == 'string') { - ext.impactify.style = bid.params.style; - } - - if (typeof bid.params.container == 'string') { - ext.impactify.container = bid.params.container; - } - - if (typeof bid.params.size == 'string') { - ext.impactify.size = bid.params.size; - } - - return ext; - }, - - getDeviceType() { - // OpenRTB Device type - if ((/ipad|android 3.0|xoom|sch-i800|playbook|tablet|kindle/i.test(navigator.userAgent.toLowerCase()))) { - return 5; - } - if ((/iphone|ipod|android|blackberry|opera|mini|windows\sce|palm|smartphone|iemobile/i.test(navigator.userAgent.toLowerCase()))) { - return 4; - } - return 2; - }, - - createOrtbImpBannerObj(bid, size) { - let sizes = size.split('x'); - - return { - id: 'banner-' + bid.bidId, - format: [{ - w: parseInt(sizes[0]), - h: parseInt(sizes[1]) - }] - } - }, - - createOrtbImpVideoObj(bid) { - return { - id: 'video-' + bid.bidId, - playerSize: [DEFAULT_VIDEO_WIDTH, DEFAULT_VIDEO_HEIGHT], - context: 'outstream', - mimes: ['video/mp4'], - } - }, - - getFloor(bid) { - const floorInfo = bid.getFloor({ - currency: DEFAULT_CURRENCY, - mediaType: '*', - size: '*' - }); - if (typeof floorInfo === 'object' && floorInfo.currency === DEFAULT_CURRENCY && !isNaN(parseFloat(floorInfo.floor))) { - return parseFloat(floorInfo.floor); - } - return null; - }, - - getImStrFromLocalStorage() { - return STORAGE.localStorageIsEnabled(false) ? STORAGE.getDataFromLocalStorage(STORAGE_KEY, false) : ''; +const AUCTIONURI = '/bidder'; +const COOKIESYNCURI = '/static/cookie_sync.html'; +const GVLID = 606; +const GETCONFIG = config.getConfig; + +const getDeviceType = () => { + // OpenRTB Device type + if ((/ipad|android 3.0|xoom|sch-i800|playbook|tablet|kindle/i.test(navigator.userAgent.toLowerCase()))) { + return 5; } + if ((/iphone|ipod|android|blackberry|opera|mini|windows\sce|palm|smartphone|iemobile/i.test(navigator.userAgent.toLowerCase()))) { + return 4; + } + return 2; +}; +const getFloor = (bid) => { + const floorInfo = bid.getFloor({ + currency: DEFAULT_CURRENCY, + mediaType: '*', + size: '*' + }); + if (typeof floorInfo === 'object' && floorInfo.currency === DEFAULT_CURRENCY && !isNaN(parseFloat(floorInfo.floor))) { + return parseFloat(floorInfo.floor); + } + return null; } -/** - * Create an OpenRTB formated object from prebid payload - * @param validBidRequests - * @param bidderRequest - * @returns {{cur: string[], validBidRequests, id, source: {tid}, imp: *[]}} - */ -function createOpenRtbRequest(validBidRequests, bidderRequest) { +const createOpenRtbRequest = (validBidRequests, bidderRequest) => { // Create request and set imp bids inside let request = { id: bidderRequest.bidderRequestId, validBidRequests, cur: [DEFAULT_CURRENCY], imp: [], - source: { tid: bidderRequest.ortb2?.source?.tid } + source: {tid: bidderRequest.ortb2?.source?.tid} }; // Get the url parameters const queryString = window.location.search; const urlParams = new URLSearchParams(queryString); const checkPrebid = urlParams.get('_checkPrebid'); - - // Force impactify debugging parameter if present + // Force impactify debugging parameter if (checkPrebid != null) { request.test = Number(checkPrebid); } - // Set SChain in request + // Set Schain in request let schain = deepAccess(validBidRequests, '0.schain'); if (schain) request.source.ext = { schain: schain }; - // Set Eids + // Set eids let eids = deepAccess(validBidRequests, '0.userIdAsEids'); if (eids && eids.length) { deepSetValue(request, 'user.ext.eids', eids); @@ -151,13 +73,13 @@ function createOpenRtbRequest(validBidRequests, bidderRequest) { request.device = { w: window.innerWidth, h: window.innerHeight, - devicetype: helpers.getDeviceType(), + devicetype: getDeviceType(), ua: navigator.userAgent, js: 1, dnt: (navigator.doNotTrack == 'yes' || navigator.doNotTrack == '1' || navigator.msDoNotTrack == '1') ? 1 : 0, language: ((navigator.language || navigator.userLanguage || '').split('-'))[0] || 'en', }; - request.site = { page: bidderRequest.refererInfo.page }; + request.site = {page: bidderRequest.refererInfo.page}; // Handle privacy settings for GDPR/CCPA/COPPA let gdprApplies = 0; @@ -169,10 +91,9 @@ function createOpenRtbRequest(validBidRequests, bidderRequest) { if (bidderRequest.uspConsent) { deepSetValue(request, 'regs.ext.us_privacy', bidderRequest.uspConsent); - this.syncStore.uspConsent = bidderRequest.uspConsent; } - if (GET_CONFIG('coppa') == true) deepSetValue(request, 'regs.coppa', 1); + if (GETCONFIG('coppa') == true) deepSetValue(request, 'regs.coppa', 1); if (bidderRequest.uspConsent) { deepSetValue(request, 'regs.ext.us_privacy', bidderRequest.uspConsent); @@ -183,47 +104,42 @@ function createOpenRtbRequest(validBidRequests, bidderRequest) { // Create imps with bids validBidRequests.forEach((bid) => { - let bannerObj = deepAccess(bid.mediaTypes, `banner`); - let imp = { id: bid.bidId, bidfloor: bid.params.bidfloor ? bid.params.bidfloor : 0, - ext: helpers.getExtParamsFromBid(bid) + ext: { + impactify: { + appId: bid.params.appId, + format: bid.params.format, + style: bid.params.style + }, + }, + video: { + playerSize: [DEFAULT_VIDEO_WIDTH, DEFAULT_VIDEO_HEIGHT], + context: 'outstream', + mimes: ['video/mp4'], + }, }; - - if (bannerObj && typeof imp.ext.impactify.size == 'string') { - imp.banner = { - ...helpers.createOrtbImpBannerObj(bid, imp.ext.impactify.size) - } - } else { - imp.video = { - ...helpers.createOrtbImpVideoObj(bid) - } + if (bid.params.container) { + imp.ext.impactify.container = bid.params.container; } - if (typeof bid.getFloor === 'function') { - const floor = helpers.getFloor(bid); + const floor = getFloor(bid); if (floor) { imp.bidfloor = floor; } } - request.imp.push(imp); }); return request; -} +}; -/** - * Export BidderSpec type object and register it to Prebid - * @type {{supportedMediaTypes: string[], interpretResponse: ((function(ServerResponse, *): Bid[])|*), code: string, aliases: string[], getUserSyncs: ((function(SyncOptions, ServerResponse[], *, *): UserSync[])|*), buildRequests: (function(*, *): {method: string, data: string, url}), onTimeout: (function(*): boolean), gvlid: number, isBidRequestValid: ((function(BidRequest): (boolean))|*), onBidWon: (function(*): boolean)}} - */ export const spec = { code: BIDDER_CODE, - gvlid: GVL_ID, + gvlid: GVLID, supportedMediaTypes: ['video', 'banner'], aliases: BIDDER_ALIAS, - storageAllowed: true, /** * Determines whether or not the given bid request is valid. @@ -232,16 +148,13 @@ export const spec = { * @return boolean True if this is a valid bid, and false otherwise. */ isBidRequestValid: function (bid) { - if (typeof bid.params.appId != 'string' || !bid.params.appId) { + if (!bid.params.appId || typeof bid.params.appId != 'string' || !bid.params.format || typeof bid.params.format != 'string' || !bid.params.style || typeof bid.params.style != 'string') { return false; } - if (typeof bid.params.format != 'string' || typeof bid.params.style != 'string' || !bid.params.format || !bid.params.style) { + if (bid.params.format != 'screen' && bid.params.format != 'display') { return false; } - if (bid.params.format !== 'screen' && bid.params.format !== 'display') { - return false; - } - if (bid.params.style !== 'inline' && bid.params.style !== 'impact' && bid.params.style !== 'static') { + if (bid.params.style != 'inline' && bid.params.style != 'impact' && bid.params.style != 'static') { return false; } @@ -258,20 +171,11 @@ export const spec = { buildRequests: function (validBidRequests, bidderRequest) { // Create a clean openRTB request let request = createOpenRtbRequest(validBidRequests, bidderRequest); - const imStr = helpers.getImStrFromLocalStorage(); - const options = {} - - if (imStr) { - options.customHeaders = { - 'x-impact': imStr - }; - } return { method: 'POST', - url: ORIGIN + AUCTION_URI, + url: ORIGIN + AUCTIONURI, data: JSON.stringify(request), - options }; }, @@ -361,16 +265,16 @@ export const spec = { return [{ type: 'iframe', - url: ORIGIN + COOKIE_SYNC_URI + params + url: ORIGIN + COOKIESYNCURI + params }]; }, /** * Register bidder specific code, which will execute if a bid from this bidder won the auction * @param {Bid} The bid that won the auction - */ - onBidWon: function (bid) { - ajax(`${LOGGER_URI}/prebid/won`, null, JSON.stringify(bid), { + */ + onBidWon: function(bid) { + ajax(`${LOGGER_URI}/log/bidder/won`, null, JSON.stringify(bid), { method: 'POST', contentType: 'application/json' }); @@ -381,9 +285,9 @@ export const spec = { /** * Register bidder specific code, which will execute if bidder timed out after an auction * @param {data} Containing timeout specific data - */ - onTimeout: function (data) { - ajax(`${LOGGER_URI}/prebid/timeout`, null, JSON.stringify(data[0]), { + */ + onTimeout: function(data) { + ajax(`${LOGGER_URI}/log/bidder/timeout`, null, JSON.stringify(data[0]), { method: 'POST', contentType: 'application/json' }); diff --git a/modules/impactifyBidAdapter.md b/modules/impactifyBidAdapter.md index de3373395dc..3de9a8cfb84 100644 --- a/modules/impactifyBidAdapter.md +++ b/modules/impactifyBidAdapter.md @@ -10,22 +10,14 @@ Maintainer: thomas.destefano@impactify.io Module that connects to the Impactify solution. The impactify bidder need 3 parameters: -- appId : This is your unique publisher identifier -- format : This is the ad format needed, can be : screen or display (Only for video media type) -- style : This is the ad style needed, can be : inline, impact or static (Only for video media type) - -Note : Impactify adapter need storage access to work properly (Do not forget to set storageAllowed to true). + - appId : This is your unique publisher identifier + - format : This is the ad format needed, can be : screen or display + - style : This is the ad style needed, can be : inline, impact or static # Test Parameters ``` - pbjs.bidderSettings = { - impactify: { - storageAllowed: true // Mandatory - } - }; - - var adUnitsVideo = [{ - code: 'your-slot-div-id-video', // This is your slot div id + var adUnits = [{ + code: 'your-slot-div-id', // This is your slot div id mediaTypes: { video: { context: 'outstream' @@ -40,24 +32,4 @@ Note : Impactify adapter need storage access to work properly (Do not forget to } }] }]; - - var adUnitsBanner = [{ - code: 'your-slot-div-id-banner', // This is your slot div id - mediaTypes: { - banner: { - sizes: [ - [728, 90] - ] - } - }, - bids: [{ - bidder: 'impactify', - params: { - appId: 'example.com', - format: 'display', - size: '728x90', - style: 'static' - } - }] - }]; ``` diff --git a/modules/improvedigitalBidAdapter.js b/modules/improvedigitalBidAdapter.js index 3a258dfa327..b56cc56a186 100644 --- a/modules/improvedigitalBidAdapter.js +++ b/modules/improvedigitalBidAdapter.js @@ -7,14 +7,6 @@ import {hasPurpose1Consent} from '../src/utils/gpdr.js'; import {ortbConverter} from '../libraries/ortbConverter/converter.js'; import {loadExternalScript} from '../src/adloader.js'; -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse - * @typedef {import('../src/adapters/bidderFactory.js').SyncOptions} SyncOptions - * @typedef {import('../src/adapters/bidderFactory.js').UserSync} UserSync - */ - const BIDDER_CODE = 'improvedigital'; const CREATIVE_TTL = 300; @@ -190,10 +182,6 @@ export const CONVERTER = ortbConverter({ })(); const bidResponse = buildBidResponse(bid, context); const idExt = deepAccess(bid, `ext.${BIDDER_CODE}`, {}); - // Programmatic guaranteed flag - if (idExt.pg === 1) { - bidResponse.adserverTargeting = { hb_deal_type_improve: 'pg' }; - } Object.assign(bidResponse, { dealId: (typeof idExt.buying_type === 'string' && idExt.buying_type !== 'rtb') ? idExt.line_item_id : undefined, netRevenue: idExt.is_net || false, diff --git a/modules/imuIdSystem.js b/modules/imuIdSystem.js index 1242ca183ea..898f32b27b0 100644 --- a/modules/imuIdSystem.js +++ b/modules/imuIdSystem.js @@ -11,11 +11,6 @@ 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 - * @typedef {import('../modules/userId/index.js').SubmoduleConfig} SubmoduleConfig - */ - const MODULE_NAME = 'imuid'; export const storage = getStorageManager({moduleType: MODULE_TYPE_UID, moduleName: MODULE_NAME}); @@ -165,16 +160,6 @@ export const imuIdSubmodule = { imppid: localData.ppid } }; - }, - eids: { - 'imppid': { - source: 'ppid.intimatemerger.com', - atype: 1 - }, - 'imuid': { - source: 'intimatemerger.com', - atype: 1 - }, } }; diff --git a/modules/incrxBidAdapter.js b/modules/incrxBidAdapter.js index 9b939aff11b..d054309ee40 100644 --- a/modules/incrxBidAdapter.js +++ b/modules/incrxBidAdapter.js @@ -2,12 +2,6 @@ import { parseSizesInput, isEmpty } 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 - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse - */ - const BIDDER_CODE = 'incrementx'; const ENDPOINT_URL = 'https://hb.incrementxserv.com/vzhbidder/bid'; const DEFAULT_CURRENCY = 'USD'; @@ -18,22 +12,22 @@ export const spec = { supportedMediaTypes: [BANNER], /** - * Determines whether or not the given bid request is valid. - * - * @param {BidRequest} bid The bid params to validate. - * @return boolean True if this is a valid bid, and false otherwise. - */ + * Determines whether or not the given bid request is valid. + * + * @param {BidRequest} bid The bid params to validate. + * @return boolean True if this is a valid bid, and false otherwise. + */ isBidRequestValid: function (bid) { return !!(bid.params.placementId); }, /** - * Make a server request from the list of BidRequests. - * - * @param validBidRequests - * @param bidderRequest - * @return Array Info describing the request to the server. - */ + * Make a server request from the list of BidRequests. + * + * @param validBidRequests + * @param bidderRequest + * @return Array Info describing the request to the server. + */ buildRequests: function (validBidRequests, bidderRequest) { return validBidRequests.map(bidRequest => { const sizes = parseSizesInput(bidRequest.params.size || bidRequest.sizes); @@ -59,11 +53,11 @@ export const spec = { }, /** - * Unpack the 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. - */ + * Unpack the 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. + */ interpretResponse: function (serverResponse) { const response = serverResponse.body; const bids = []; diff --git a/modules/insticatorBidAdapter.js b/modules/insticatorBidAdapter.js index 3ea95ef8443..c770ac69dbe 100644 --- a/modules/insticatorBidAdapter.js +++ b/modules/insticatorBidAdapter.js @@ -1,7 +1,7 @@ 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} from '../src/utils.js'; +import {deepAccess, generateUUID, logError, isArray} from '../src/utils.js'; import {getStorageManager} from '../src/storageManager.js'; import {find} from '../src/polyfill.js'; @@ -12,35 +12,6 @@ const USER_ID_COOKIE_EXP = 2592000000; // 30 days const BID_TTL = 300; // 5 minutes const GVLID = 910; -const isSubarray = (arr, target) => { - if (!isArrayOfNums(arr) || arr.length === 0) { - return false; - } - const targetSet = new Set(target); - return arr.every(el => targetSet.has(el)); -}; - -export const OPTIONAL_VIDEO_PARAMS = { - 'minduration': (value) => isInteger(value), - 'maxduration': (value) => isInteger(value), - 'protocols': (value) => isSubarray(value, [2, 3, 5, 6, 7, 8]), // protocols values supported by Inticator, according to the OpenRTB spec - 'startdelay': (value) => isInteger(value), - 'linearity': (value) => isInteger(value) && [1].includes(value), - 'skip': (value) => isInteger(value) && [1, 0].includes(value), - 'skipmin': (value) => isInteger(value), - 'skipafter': (value) => isInteger(value), - 'sequence': (value) => isInteger(value), - 'battr': (value) => isSubarray(value, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17]), - 'maxextended': (value) => isInteger(value), - 'minbitrate': (value) => isInteger(value), - 'maxbitrate': (value) => isInteger(value), - 'playbackmethod': (value) => isSubarray(value, [1, 2, 3, 4]), - 'playbackend': (value) => isInteger(value) && [1, 2, 3].includes(value), - 'delivery': (value) => isSubarray(value, [1, 2, 3]), - 'pos': (value) => isInteger(value) && [0, 1, 2, 3, 4, 5, 6, 7].includes(value), - 'api': (value) => isSubarray(value, [1, 2, 3, 4, 5, 6, 7]), -}; - export const storage = getStorageManager({bidderCode: BIDDER_CODE}); config.setDefaults({ @@ -97,50 +68,17 @@ function buildBanner(bidRequest) { } function buildVideo(bidRequest) { - let w = deepAccess(bidRequest, 'mediaTypes.video.w'); - let h = deepAccess(bidRequest, 'mediaTypes.video.h'); + const w = deepAccess(bidRequest, 'mediaTypes.video.w'); + const h = deepAccess(bidRequest, 'mediaTypes.video.h'); const mimes = deepAccess(bidRequest, 'mediaTypes.video.mimes'); const placement = deepAccess(bidRequest, 'mediaTypes.video.placement') || 3; - const plcmt = deepAccess(bidRequest, 'mediaTypes.video.plcmt') || undefined; - const playerSize = deepAccess(bidRequest, 'mediaTypes.video.playerSize'); - - if (!w && playerSize) { - if (Array.isArray(playerSize[0])) { - w = parseInt(playerSize[0][0], 10); - } else if (typeof playerSize[0] === 'number' && !isNaN(playerSize[0])) { - w = parseInt(playerSize[0], 10); - } - } - if (!h && playerSize) { - if (Array.isArray(playerSize[0])) { - h = parseInt(playerSize[0][1], 10); - } else if (typeof playerSize[1] === 'number' && !isNaN(playerSize[1])) { - h = parseInt(playerSize[1], 10); - } - } - const bidRequestVideo = deepAccess(bidRequest, 'mediaTypes.video'); - const videoBidderParams = deepAccess(bidRequest, 'params.video', {}); - let optionalParams = {}; - for (const param in OPTIONAL_VIDEO_PARAMS) { - if (bidRequestVideo[param]) { - optionalParams[param] = bidRequestVideo[param]; - } - } - - let videoObj = { + return { placement, mimes, w, h, - ...optionalParams, - ...videoBidderParams // bidder specific overrides for video - } - - if (plcmt) { - videoObj['plcmt'] = plcmt; } - return videoObj } function buildImpression(bidRequest) { @@ -168,10 +106,8 @@ function buildImpression(bidRequest) { return imp; } -function buildDevice(bidRequest) { - const ortb2Data = bidRequest?.ortb2 || {}; - const deviceConfig = ortb2Data?.device || {} - +function buildDevice() { + const deviceConfig = config.getConfig('device'); const device = { w: window.innerWidth, h: window.innerHeight, @@ -248,7 +184,7 @@ function buildRequest(validBidRequests, bidderRequest) { page: bidderRequest.refererInfo.page, ref: bidderRequest.refererInfo.ref, }, - device: buildDevice(bidderRequest), + device: buildDevice(), regs: buildRegs(bidderRequest), user: buildUser(validBidRequests[0]), imp: validBidRequests.map((bidRequest) => buildImpression(bidRequest)), @@ -297,11 +233,7 @@ function buildBid(bid, bidderRequest) { meta.advertiserDomains = bid.adomain } - let mediaType = 'banner'; - if (bid.adm && bid.adm.includes(' 0 ? {meta} : {}) }; - - if (mediaType === 'video') { - bidResponse.vastXml = bid.adm; - } - - // Inticator bid adaptor only returns `vastXml` for video bids. No VastUrl or videoCache. - if (!bidResponse.vastUrl && bidResponse.vastXml) { - bidResponse.vastUrl = 'data:text/xml;charset=utf-8;base64,' + window.btoa(bidResponse.vastXml.replace(/\\"/g, '"')); - } - - return bidResponse; } function buildBidSet(seatbid, bidderRequest) { @@ -386,38 +307,15 @@ function validateBanner(bid) { } function validateVideo(bid) { - const videoParams = deepAccess(bid, 'mediaTypes.video', {}); - const videoBidderParams = deepAccess(bid, 'params.video', {}); - let video = { - ...videoParams, - ...videoBidderParams // bidder specific overrides for video - } - // Check if the merged video object is empty - if (Object.keys(video).length === 0 || video === undefined) { - logError('insticator: video object is empty'); - return false; // or handle the case where video parameters are undefined or empty - } + const video = deepAccess(bid, 'mediaTypes.video'); - let w = deepAccess(bid, 'mediaTypes.video.w'); - let h = deepAccess(bid, 'mediaTypes.video.h'); - const playerSize = deepAccess(bid, 'mediaTypes.video.playerSize'); - if (!w && playerSize) { - if (Array.isArray(playerSize[0])) { - w = parseInt(playerSize[0][0], 10); - } else if (typeof playerSize[0] === 'number' && !isNaN(playerSize[0])) { - w = parseInt(playerSize[0], 10); - } - } - if (!h && playerSize) { - if (Array.isArray(playerSize[0])) { - h = parseInt(playerSize[0][1], 10); - } else if (typeof playerSize[1] === 'number' && !isNaN(playerSize[1])) { - h = parseInt(playerSize[1], 10); - } + if (video === undefined) { + return true; } + const videoSize = [ - w, - h, + deepAccess(bid, 'mediaTypes.video.w'), + deepAccess(bid, 'mediaTypes.video.h'), ]; if ( @@ -441,27 +339,6 @@ function validateVideo(bid) { return false; } - const plcmt = deepAccess(bid, 'mediaTypes.video.plcmt'); - - if (typeof plcmt !== 'undefined' && typeof plcmt !== 'number') { - logError('insticator: video plcmt is not a number'); - return false; - } - - for (const param in OPTIONAL_VIDEO_PARAMS) { - if (video[param]) { - if (!OPTIONAL_VIDEO_PARAMS[param](video[param])) { - logError(`insticator: video ${param} is invalid or not supported by insticator`); - return false - } - } - } - - if (video.minduration && video.maxduration && video.minduration > video.maxduration) { - logError('insticator: video minduration is greater than maxduration'); - return false; - } - return true; } @@ -503,6 +380,7 @@ 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'); return []; diff --git a/modules/instreamTracking.js b/modules/instreamTracking.js index ece556d0fd2..ff8305c7fed 100644 --- a/modules/instreamTracking.js +++ b/modules/instreamTracking.js @@ -5,12 +5,6 @@ import { INSTREAM } from '../src/video.js'; import * as events from '../src/events.js'; import CONSTANTS from '../src/constants.json' -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - * @typedef {import('../src/adapters/bidderFactory.js').AdUnit} AdUnit - */ - const {CACHE_ID, UUID} = CONSTANTS.TARGETING_KEYS; const {BID_WON, AUCTION_END} = CONSTANTS.EVENTS; const {RENDERED} = CONSTANTS.BID_STATUS; diff --git a/modules/integr8BidAdapter.js b/modules/integr8BidAdapter.js index 949483ea7bf..a85e9b0a55c 100644 --- a/modules/integr8BidAdapter.js +++ b/modules/integr8BidAdapter.js @@ -3,20 +3,13 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { getStorageManager } from '../src/storageManager.js'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse - * @typedef {import('../src/adapters/bidderFactory.js').validBidRequests} validBidRequests - */ - const BIDDER_CODE = 'integr8'; -const DEFAULT_ENDPOINT_URL = 'https://central.sea.integr8.digital/bid'; +const ENDPOINT_URL = 'https://integr8.central.gjirafa.tech/bid'; const DIMENSION_SEPARATOR = 'x'; const SIZE_SEPARATOR = ';'; -const BISKO_ID = 'integr8Id'; +const BISKO_ID = 'biskoId'; const STORAGE_ID = 'bisko-sid'; -const SEGMENTS = 'integr8Segments'; +const SEGMENTS = 'biskoSegments'; const storage = getStorageManager({bidderCode: BIDDER_CODE}); export const spec = { @@ -38,7 +31,6 @@ export const spec = { * @return ServerRequest Info describing the request to the server. */ buildRequests: function (validBidRequests, bidderRequest) { - let deliveryUrl = ''; const storageId = storage.localStorageIsEnabled() ? storage.getDataFromLocalStorage(STORAGE_ID) || '' : ''; const biskoId = storage.localStorageIsEnabled() ? storage.getDataFromLocalStorage(BISKO_ID) || '' : ''; const segments = storage.localStorageIsEnabled() ? JSON.parse(storage.getDataFromLocalStorage(SEGMENTS)) || [] : []; @@ -63,9 +55,6 @@ export const spec = { if (!pageViewGuid) { pageViewGuid = bidRequest.params.pageViewGuid || ''; } if (!contents.length && bidRequest.params.contents && bidRequest.params.contents.length) { contents = bidRequest.params.contents; } if (!Object.keys(data).length && bidRequest.params.data && Object.keys(bidRequest.params.data).length) { data = bidRequest.params.data; } - if (!deliveryUrl && bidRequest.params && typeof bidRequest.params.deliveryUrl === 'string') { - deliveryUrl = bidRequest.params.deliveryUrl; - } return { sizes: generateSizeParam(bidRequest.sizes), @@ -78,10 +67,6 @@ export const spec = { }; }); - if (!deliveryUrl) { - deliveryUrl = DEFAULT_ENDPOINT_URL; - } - let body = { propertyId: propertyId, pageViewGuid: pageViewGuid, @@ -97,7 +82,7 @@ export const spec = { return [{ method: 'POST', - url: deliveryUrl, + url: ENDPOINT_URL, data: body }]; }, @@ -135,11 +120,11 @@ export const spec = { }; /** - * Generate size param for bid request using sizes array - * - * @param {Array} sizes Possible sizes for the ad unit. - * @return {string} Processed sizes param to be used for the bid request. - */ +* Generate size param for bid request using sizes array +* +* @param {Array} sizes Possible sizes for the ad unit. +* @return {string} Processed sizes param to be used for the bid request. +*/ function generateSizeParam(sizes) { return sizes.map(size => size.join(DIMENSION_SEPARATOR)).join(SIZE_SEPARATOR); } diff --git a/modules/integr8BidAdapter.md b/modules/integr8BidAdapter.md index da52a2164c6..eadab7acdb3 100644 --- a/modules/integr8BidAdapter.md +++ b/modules/integr8BidAdapter.md @@ -3,7 +3,7 @@ Module Name: Integr8 Bidder Adapter Module Type: Bidder Adapter -Maintainer: myhedin@gjirafa.com +Maintainer: arditb@gjirafa.com # Description Integr8 Bidder Adapter for Prebid.js. @@ -23,9 +23,8 @@ var adUnits = [ bids: [{ bidder: 'integr8', params: { - propertyId: '105135', //Required - placementId: '846837', //Required, - deliveryUrl: 'https://central.sea.integr8.digital/bid', //Optional + propertyId: '105109', //Required + placementId: '846835', //Required data: { //Optional catalogs: [{ catalogId: "699229", @@ -49,9 +48,8 @@ var adUnits = [ bids: [{ bidder: 'integr8', params: { - propertyId: '105135', //Required - placementId: '846835', //Required, - deliveryUrl: 'https://central.sea.integr8.digital/bid', //Optional + propertyId: '105109', //Required + placementId: '846830', //Required data: { //Optional catalogs: [{ catalogId: "699229", diff --git a/modules/intentIqIdSystem.js b/modules/intentIqIdSystem.js index 109ea5c39c6..4a6977973f3 100644 --- a/modules/intentIqIdSystem.js +++ b/modules/intentIqIdSystem.js @@ -11,12 +11,6 @@ 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 - * @typedef {import('../modules/userId/index.js').SubmoduleConfig} SubmoduleConfig - * @typedef {import('../modules/userId/index.js').IdResponse} IdResponse - */ - const PCID_EXPIRY = 365; const MODULE_NAME = 'intentIqId'; @@ -65,7 +59,7 @@ export function readData(key) { * @param key * @param {string} value IntentIQ ID value to sintentIqIdSystem_spec.jstore */ -function storeData(key, value, cookieStorageEnabled = false) { +function storeData(key, value) { try { logInfo(MODULE_NAME + ': storing data: key=' + key + ' value=' + value); @@ -74,7 +68,7 @@ function storeData(key, value, cookieStorageEnabled = false) { storage.setDataInLocalStorage(key, value); } const expiresStr = (new Date(Date.now() + (PCID_EXPIRY * (60 * 60 * 24 * 1000)))).toUTCString(); - if (storage.cookiesAreEnabled() && cookieStorageEnabled) { + if (storage.cookiesAreEnabled()) { storage.setCookie(key, value, expiresStr, 'LAX'); } } @@ -125,7 +119,6 @@ export const intentIqIdSubmodule = { logError('User ID - intentIqId submodule requires a valid partner to be defined'); return; } - const cookieStorageEnabled = typeof configParams.enableCookieStorage === 'boolean' ? configParams.enableCookieStorage : false if (!FIRST_PARTY_DATA_KEY.includes(configParams.partner)) { FIRST_PARTY_DATA_KEY += '_' + configParams.partner; } let rrttStrtTime = 0; @@ -134,7 +127,7 @@ export const intentIqIdSubmodule = { if (!firstPartyData || !firstPartyData.pcid || firstPartyData.pcidDate) { const firstPartyId = generateGUID(); firstPartyData = { 'pcid': firstPartyId, 'pcidDate': Date.now() }; - storeData(FIRST_PARTY_KEY, JSON.stringify(firstPartyData), cookieStorageEnabled); + storeData(FIRST_PARTY_KEY, JSON.stringify(firstPartyData)); } let partnerData = tryParse(readData(FIRST_PARTY_DATA_KEY)); @@ -179,8 +172,8 @@ export const intentIqIdSubmodule = { } if (shouldUpdateLs === true) { partnerData.date = Date.now(); - storeData(FIRST_PARTY_KEY, JSON.stringify(firstPartyData), cookieStorageEnabled); - storeData(FIRST_PARTY_DATA_KEY, JSON.stringify(partnerData), cookieStorageEnabled); + storeData(FIRST_PARTY_KEY, JSON.stringify(firstPartyData)); + storeData(FIRST_PARTY_DATA_KEY, JSON.stringify(partnerData)); } callback(respJson.data); } else { @@ -199,12 +192,6 @@ export const intentIqIdSubmodule = { } }; return { callback: resp }; - }, - eids: { - 'intentIqId': { - source: 'intentiq.com', - atype: 1 - }, } }; diff --git a/modules/invamiaBidAdapter.js b/modules/invamiaBidAdapter.js index 96af163ca4f..2d36fb77e16 100644 --- a/modules/invamiaBidAdapter.js +++ b/modules/invamiaBidAdapter.js @@ -1,11 +1,6 @@ import {registerBidder} from '../src/adapters/bidderFactory.js'; import {BANNER} from '../src/mediaTypes.js'; -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse - */ - const BIDDER_CODE = 'invamia'; const ENDPOINT_URL = 'https://ad.invamia.com/delivery/impress'; diff --git a/modules/invibesBidAdapter.js b/modules/invibesBidAdapter.js index 2c37c0edad9..b2444043c22 100644 --- a/modules/invibesBidAdapter.js +++ b/modules/invibesBidAdapter.js @@ -1,12 +1,7 @@ -import {logInfo} from '../src/utils.js'; +import { 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 - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - */ - const CONSTANTS = { BIDDER_CODE: 'invibes', BID_ENDPOINT: '.videostep.com/Bid/VideoAdContent', @@ -14,7 +9,7 @@ const CONSTANTS = { SYNC_ENDPOINT: 'https://k.r66net.com/GetUserSync', TIME_TO_LIVE: 300, DEFAULT_CURRENCY: 'EUR', - PREBID_VERSION: 11, + PREBID_VERSION: 9, METHOD: 'GET', INVIBES_VENDOR_ID: 436, USERID_PROVIDERS: ['pubcid', 'pubProvidedId', 'uid2', 'zeotapIdPlus', 'id5id'], @@ -54,36 +49,14 @@ registerBidder(spec); // some state info is required: cookie info, unique user visit id const topWin = getTopMostWindow(); let invibes = topWin.invibes = topWin.invibes || {}; -invibes.purposes = invibes.purposes || [false, false, false, false, false, false, false, false, false, false, false]; -invibes.legitimateInterests = invibes.legitimateInterests || [false, false, false, false, false, false, false, false, false, false, false]; +invibes.purposes = invibes.purposes || [false, false, false, false, false, false, false, false, false, false]; +invibes.legitimateInterests = invibes.legitimateInterests || [false, false, false, false, false, false, false, false, false, false]; invibes.placementBids = invibes.placementBids || []; invibes.pushedCids = invibes.pushedCids || {}; let preventPageViewEvent = false; -let isInfiniteScrollPage = false; -let isPlacementRefresh = false; let _customUserSync; let _disableUserSyncs; -function updateInfiniteScrollFlag() { - const { scrollHeight } = document.documentElement; - - if (invibes.originalURL === undefined) { - invibes.originalURL = window.location.href; - return; - } - - if (invibes.originalScrollHeight === undefined) { - invibes.originalScrollHeight = scrollHeight; - return; - } - - const currentURL = window.location.href; - - if (scrollHeight > invibes.originalScrollHeight && invibes.originalURL !== currentURL) { - isInfiniteScrollPage = true; - } -} - function isBidRequestValid(bid) { if (typeof bid.params !== 'object') { return false; @@ -114,24 +87,10 @@ function buildRequest(bidRequests, bidderRequest) { const _placementIds = []; const _adUnitCodes = []; let _customEndpoint, _userId, _domainId; - let _ivAuctionStart = Date.now(); - window.invibes = window.invibes || {}; - window.invibes.placementIds = window.invibes.placementIds || []; - - if (isInfiniteScrollPage == false) { - updateInfiniteScrollFlag(); - } + let _ivAuctionStart = bidderRequest.auctionStart || Date.now(); bidRequests.forEach(function (bidRequest) { bidRequest.startTime = new Date().getTime(); - - if (window.invibes.placementIds.includes(bidRequest.params.placementId)) { - isPlacementRefresh = true; - } - - window.invibes.placementIds.push(bidRequest.params.placementId); - - _placementIds.push(bidRequest.params.placementId); _placementIds.push(bidRequest.params.placementId); _adUnitCodes.push(bidRequest.adUnitCode); _domainId = _domainId || bidRequest.params.domainId; @@ -157,7 +116,7 @@ function buildRequest(bidRequests, bidderRequest) { bidParamsJson.userId = userIdModel; } let data = { - location: getDocumentLocation(bidderRequest), + location: getDocumentLocation(topWin), videoAdHtmlId: generateRandomId(), showFallback: currentQueryStringParams['advs'] === '0', ivbsCampIdsLocal: readFromLocalStorage('IvbsCampIdsLocal'), @@ -179,8 +138,6 @@ function buildRequest(bidRequests, bidderRequest) { tc: invibes.gdpr_consent, isLocalStorageEnabled: storage.hasLocalStorage(), preventPageViewEvent: preventPageViewEvent, - isPlacementRefresh: isPlacementRefresh, - isInfiniteScrollPage: isInfiniteScrollPage, }; let lid = readFromLocalStorage('ivbsdid'); @@ -411,13 +368,11 @@ function addMeta(bidModelMeta) { } function generateRandomId() { - return '10000000100040008000100000000000'.replace(/[018]/g, c => - (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16) - ); + return (Math.round(Math.random() * 1e12)).toString(36).substring(0, 10); } -function getDocumentLocation(bidderRequest) { - return bidderRequest.refererInfo.page.substring(0, 300); +function getDocumentLocation(topWin) { + return topWin.location.href.substring(0, 300).split(/[?#]/)[0]; } function getUserIds(bidUserId) { @@ -613,7 +568,7 @@ function readGdprConsent(gdprConsent) { } let legitimateInterests = getLegitimateInterests(gdprConsent.vendorData); - tryCopyValueToArray(legitimateInterests, invibes.legitimateInterests, purposesLength); + tryCopyValueToArray(legitimateInterests, invibes.legitimateInterests, 10); let invibesVendorId = CONSTANTS.INVIBES_VENDOR_ID.toString(10); let vendorConsents = getVendorConsents(gdprConsent.vendorData); @@ -666,10 +621,6 @@ function tryCopyValueToArray(value, target, length) { function getPurposeConsentsCounter(vendorData) { if (vendorData.purpose && vendorData.purpose.consents) { - if (vendorData.tcfPolicyVersion >= 4) { - return 11; - } - return 10; } diff --git a/modules/ipromBidAdapter.js b/modules/ipromBidAdapter.js index 1188af471a7..eaf20ad3ad3 100644 --- a/modules/ipromBidAdapter.js +++ b/modules/ipromBidAdapter.js @@ -3,15 +3,13 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; const BIDDER_CODE = 'iprom'; const ENDPOINT_URL = 'https://core.iprom.net/programmatic'; -const VERSION = 'v1.0.3'; +const VERSION = 'v1.0.2'; const DEFAULT_CURRENCY = 'EUR'; const DEFAULT_NETREVENUE = true; const DEFAULT_TTL = 360; -const IAB_GVL_ID = 811; export const spec = { code: BIDDER_CODE, - gvlid: IAB_GVL_ID, isBidRequestValid: function ({ bidder, params = {} } = {}) { // id parameter checks if (!params.id) { diff --git a/modules/iqmBidAdapter.js b/modules/iqmBidAdapter.js index c94a88748a7..c3808afd225 100644 --- a/modules/iqmBidAdapter.js +++ b/modules/iqmBidAdapter.js @@ -3,10 +3,6 @@ import {registerBidder} from '../src/adapters/bidderFactory.js'; import {BANNER, VIDEO} from '../src/mediaTypes.js'; import {INSTREAM} from '../src/video.js'; -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - */ - const BIDDER_CODE = 'iqm'; const VERSION = 'v.1.0.0'; const VIDEO_ORTB_PARAMS = [ diff --git a/modules/iqxBidAdapter.js b/modules/iqxBidAdapter.js deleted file mode 100644 index 1bef158c4a2..00000000000 --- a/modules/iqxBidAdapter.js +++ /dev/null @@ -1,207 +0,0 @@ -import {config} from '../src/config.js'; -import {BANNER, VIDEO} from '../src/mediaTypes.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {parseSizesInput, isFn, deepAccess, getBidIdParameter, logError, isArray} from '../src/utils.js'; -import {getAdUnitSizes} from '../libraries/sizeUtils/sizeUtils.js'; - -const CUR = 'USD'; -const BIDDER_CODE = 'iqx'; -const ENDPOINT = 'https://pbjs.iqzonertb.live'; - -/** - * Determines whether or not the given bid request is valid. - * - * @param {BidRequest} bid The bid params to validate. - * @return boolean True if this is a valid bid, and false otherwise. - */ -function isBidRequestValid(req) { - if (req && typeof req.params !== 'object') { - logError('Params is not defined or is incorrect in the bidder settings'); - return false; - } - - if (!getBidIdParameter('env', req.params) || !getBidIdParameter('pid', req.params)) { - logError('Env or pid is not present in bidder params'); - return false; - } - - if (deepAccess(req, 'mediaTypes.video') && !isArray(deepAccess(req, 'mediaTypes.video.playerSize'))) { - logError('mediaTypes.video.playerSize is required for video'); - return false; - } - - return true; -} - -/** - * Make a server request from the list of BidRequests. - * - * @param {validBidRequest?pbjs_debug=trues[]} - an array of bids - * @return ServerRequest Info describing the request to the server. - */ -function buildRequests(validBidRequests, bidderRequest) { - const {refererInfo = {}, gdprConsent = {}, uspConsent} = bidderRequest; - const requests = validBidRequests.map(req => { - const request = {}; - request.bidId = req.bidId; - request.banner = deepAccess(req, 'mediaTypes.banner'); - request.auctionId = req.ortb2?.source?.tid; - request.transactionId = req.ortb2Imp?.ext?.tid; - request.sizes = parseSizesInput(getAdUnitSizes(req)); - request.schain = req.schain; - request.location = { - page: refererInfo.page, - location: refererInfo.location, - domain: refererInfo.domain, - whost: window.location.host, - ref: refererInfo.ref, - isAmp: refererInfo.isAmp - }; - request.device = { - ua: navigator.userAgent, - lang: navigator.language - }; - request.env = { - env: req.params.env, - pid: req.params.pid - }; - request.ortb2 = req.ortb2; - request.ortb2Imp = req.ortb2Imp; - request.tz = new Date().getTimezoneOffset(); - request.ext = req.params.ext; - request.bc = req.bidRequestsCount; - request.floor = getBidFloor(req); - - if (req.userIdAsEids && req.userIdAsEids.length !== 0) { - request.userEids = req.userIdAsEids; - } else { - request.userEids = []; - } - if (gdprConsent.gdprApplies) { - request.gdprApplies = Number(gdprConsent.gdprApplies); - request.consentString = gdprConsent.consentString; - } else { - request.gdprApplies = 0; - request.consentString = ''; - } - if (uspConsent) { - request.usPrivacy = uspConsent; - } else { - request.usPrivacy = ''; - } - if (config.getConfig('coppa')) { - request.coppa = 1; - } else { - request.coppa = 0; - } - - const video = deepAccess(req, 'mediaTypes.video'); - if (video) { - request.sizes = parseSizesInput(deepAccess(req, 'mediaTypes.video.playerSize')); - request.video = video; - } - - return request; - }); - - return { - method: 'POST', - url: ENDPOINT + '/bid', - data: JSON.stringify(requests), - withCredentials: true, - bidderRequest, - options: { - contentType: 'application/json', - } - }; -} - -/** - * Unpack the 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. - */ -function interpretResponse(serverResponse, {bidderRequest}) { - const response = []; - if (!isArray(deepAccess(serverResponse, 'body.data'))) { - return response; - } - - serverResponse.body.data.forEach(serverBid => { - const bid = { - requestId: bidderRequest.bidId, - dealId: bidderRequest.dealId || null, - ...serverBid - }; - response.push(bid); - }); - - return response; -} - -/** - * Register the user sync pixels which should be dropped after the auction. - * - * @param {SyncOptions} syncOptions Which user syncs are allowed? - * @param {ServerResponse[]} serverResponses List of server's responses. - * @return {UserSync[]} The user syncs which should be dropped. - */ -function getUserSyncs(syncOptions, serverResponses, gdprConsent = {}, uspConsent = '') { - const syncs = []; - const pixels = deepAccess(serverResponses, '0.body.data.0.ext.pixels'); - - if ((syncOptions.iframeEnabled || syncOptions.pixelEnabled) && isArray(pixels) && pixels.length !== 0) { - const gdprFlag = `&gdpr=${gdprConsent.gdprApplies ? 1 : 0}`; - const gdprString = `&gdpr_consent=${encodeURIComponent((gdprConsent.consentString || ''))}`; - const usPrivacy = `us_privacy=${encodeURIComponent(uspConsent)}`; - - pixels.forEach(pixel => { - const [type, url] = pixel; - const sync = {type, url: `${url}&${usPrivacy}${gdprFlag}${gdprString}`}; - if (type === 'iframe' && syncOptions.iframeEnabled) { - syncs.push(sync) - } else if (type === 'image' && syncOptions.pixelEnabled) { - syncs.push(sync) - } - }); - } - - return syncs; -} - -/** - * Get valid floor value from getFloor fuction. - * - * @param {Object} bid Current bid request. - * @return {null|Number} Returns floor value when bid.getFloor is function and returns valid floor object with USD currency, otherwise returns null. - */ -export function getBidFloor(bid) { - if (!isFn(bid.getFloor)) { - return null; - } - - let floor = bid.getFloor({ - currency: CUR, - mediaType: '*', - size: '*' - }); - - if (typeof floor === 'object' && !isNaN(floor.floor) && floor.currency === CUR) { - return floor.floor; - } - - return null; -} - -export const spec = { - code: BIDDER_CODE, - aliases: ['iqx'], - supportedMediaTypes: [BANNER, VIDEO], - isBidRequestValid, - buildRequests, - interpretResponse, - getUserSyncs -} - -registerBidder(spec); diff --git a/modules/iqxBidAdapter.md b/modules/iqxBidAdapter.md deleted file mode 100644 index c48864c4306..00000000000 --- a/modules/iqxBidAdapter.md +++ /dev/null @@ -1,54 +0,0 @@ -# Overview - -``` -Module Name: IQX Bidder Adapter -Module Type: IQX Bidder Adapter -Maintainer: it@iqzone.com -``` - -# Description - -Module that connects to iqx.com demand sources - -# Test Parameters -``` -var adUnits = [ - { - code: 'test-banner', - mediaTypes: { - banner: { - sizes: [[300, 250]], - } - }, - bids: [ - { - bidder: 'iqx', - params: { - env: 'iqx', - pid: '40', - ext: {} - } - } - ] - }, - { - code: 'test-video', - sizes: [ [ 640, 480 ] ], - mediaTypes: { - video: { - playerSize: [640, 480], - context: 'instream', - skipppable: true - } - }, - bids: [{ - bidder: 'iqx', - params: { - env: 'iqx', - pid: '40', - ext: {} - } - }] - } -]; -``` diff --git a/modules/ivsBidAdapter.js b/modules/ivsBidAdapter.js index 3deebf9bff3..47685fbbe46 100644 --- a/modules/ivsBidAdapter.js +++ b/modules/ivsBidAdapter.js @@ -1,16 +1,9 @@ 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'; -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse - * @typedef {import('../src/adapters/bidderFactory.js').validBidRequests} validBidRequests - */ - const BIDDER_CODE = 'ivs'; const ENDPOINT_URL = 'https://a.ivstracker.net/prod/openrtb/2.5'; diff --git a/modules/ixBidAdapter.js b/modules/ixBidAdapter.js index 4cc6807b670..000131d9e03 100644 --- a/modules/ixBidAdapter.js +++ b/modules/ixBidAdapter.js @@ -1,8 +1,10 @@ import { contains, + convertTypes, deepAccess, deepClone, deepSetValue, + getGptSlotInfoForAdUnitCode, inIframe, isArray, isEmpty, @@ -22,8 +24,6 @@ import { find } from '../src/polyfill.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 {convertTypes} from '../libraries/transformParamsUtils/convertTypes.js'; const BIDDER_CODE = 'ix'; const ALIAS_BIDDER_CODE = 'roundel'; @@ -76,12 +76,7 @@ const SOURCE_RTI_MAPPING = { 'audigent.com': '', // Hadron ID from Audigent, hadronId 'pubcid.org': '', // SharedID, pubcid 'utiq.com': '', // Utiq - 'criteo.com': '', // Criteo - 'euid.eu': '', // EUID - 'intimatemerger.com': '', - '33across.com': '', - 'liveintent.indexexchange.com': '', - 'google.com': '' + 'intimatemerger.com': '' }; const PROVIDERS = [ 'britepoolid', @@ -92,8 +87,7 @@ const PROVIDERS = [ 'connectid', 'tapadId', 'quantcastId', - 'pubProvidedId', - 'pairId' + 'pubProvidedId' ]; const REQUIRED_VIDEO_PARAMS = ['mimes', 'minduration', 'maxduration']; // note: protocol/protocols is also reqd const VIDEO_PARAMS_ALLOW_LIST = [ @@ -110,9 +104,7 @@ let hasRegisteredHandler = false; export const storage = getStorageManager({ bidderCode: BIDDER_CODE }); export const FEATURE_TOGGLES = { // Update with list of CFTs to be requested from Exchange - REQUESTED_FEATURE_TOGGLES: [ - 'pbjs_enable_multiformat' - ], + REQUESTED_FEATURE_TOGGLES: [], featureToggles: {}, isFeatureEnabled: function (ft) { @@ -172,7 +164,6 @@ const MEDIA_TYPES = { function bidToBannerImp(bid) { const imp = bidToImp(bid, BANNER); imp.banner = {}; - imp.adunitCode = bid.adUnitCode; const impSize = deepAccess(bid, 'params.size'); if (impSize) { imp.banner.w = impSize[0]; @@ -186,48 +177,13 @@ function bidToBannerImp(bid) { return imp; } -/** - * Sets imp.displaymanager - * - * @param {object} imp - * @param {object} bid - */ -function setDisplayManager(imp, bid) { - if (deepAccess(bid, 'mediaTypes.video.context') === OUTSTREAM) { - let renderer = deepAccess(bid, 'mediaTypes.video.renderer'); - if (!renderer) { - renderer = deepAccess(bid, 'renderer'); - } - - if (deepAccess(bid, 'schain', false)) { - imp.displaymanager = 'pbjs_wrapper'; - } else if (renderer && typeof (renderer) === 'object') { - if (renderer.url !== undefined) { - let domain = ''; - try { - domain = new URL(renderer.url).hostname - } catch { - return; - } - if (domain.includes('js-sec.indexww')) { - imp.displaymanager = 'ix'; - } else { - imp.displaymanager = renderer.url; - } - } - } else { - imp.displaymanager = 'ix'; - } - } -} - /** * Transform valid bid request config object to video impression object that will be sent to ad server. * * @param {object} bid A valid bid request config object. * @return {object} A impression object that will be sent to ad server. */ -export function bidToVideoImp(bid) { +function bidToVideoImp(bid) { const imp = bidToImp(bid, VIDEO); const videoAdUnitRef = deepAccess(bid, 'mediaTypes.video'); const videoParamRef = deepAccess(bid, 'params.video'); @@ -238,15 +194,10 @@ export function bidToVideoImp(bid) { imp.video = videoParamRef ? deepClone(bid.params.video) : {}; // populate imp level transactionId - let tid = deepAccess(bid, 'ortb2Imp.ext.tid'); - if (tid) { - deepSetValue(imp, 'ext.tid', tid); - } - - setDisplayManager(imp, bid); + imp.ext.tid = deepAccess(bid, 'ortb2Imp.ext.tid'); // AdUnit-Specific First Party Data - addAdUnitFPD(imp, bid); + addAdUnitFPD(imp, bid) // copy all video properties to imp object for (const adUnitProperty in videoAdUnitRef) { @@ -315,7 +266,7 @@ function verifyVideoPlcmt(imp) { * @param {object} bid A valid bid request config object. * @return {object} A impression object that will be sent to ad server. */ -export function bidToNativeImp(bid) { +function bidToNativeImp(bid) { const imp = bidToImp(bid, NATIVE); const request = bid.nativeOrtbRequest @@ -331,10 +282,7 @@ export function bidToNativeImp(bid) { }; // populate imp level transactionId - let tid = deepAccess(bid, 'ortb2Imp.ext.tid'); - if (tid) { - deepSetValue(imp, 'ext.tid', tid); - } + imp.ext.tid = deepAccess(bid, 'ortb2Imp.ext.tid'); // AdUnit-Specific First Party Data addAdUnitFPD(imp, bid) @@ -354,30 +302,27 @@ function bidToImp(bid, mediaType) { imp.id = bid.bidId; - if (isExchangeIdConfigured() && deepAccess(bid, `params.externalId`)) { - deepSetValue(imp, 'ext.externalID', bid.params.externalId); - } + imp.ext = {}; + if (deepAccess(bid, `params.${mediaType}.siteId`) && !isNaN(Number(bid.params[mediaType].siteId))) { switch (mediaType) { case BANNER: - deepSetValue(imp, 'ext.siteID', bid.params.banner.siteId.toString()); + imp.ext.siteID = bid.params.banner.siteId.toString(); break; case VIDEO: - deepSetValue(imp, 'ext.siteID', bid.params.video.siteId.toString()); + imp.ext.siteID = bid.params.video.siteId.toString(); break; case NATIVE: - deepSetValue(imp, 'ext.siteID', bid.params.native.siteId.toString()); + imp.ext.siteID = bid.params.native.siteId.toString(); break; } } else { - if (bid.params.siteId) { - deepSetValue(imp, 'ext.siteID', bid.params.siteId.toString()); - } + imp.ext.siteID = bid.params.siteId.toString(); } // populate imp level sid if (bid.params.hasOwnProperty('id') && (typeof bid.params.id === 'string' || typeof bid.params.id === 'number')) { - deepSetValue(imp, 'ext.sid', String(bid.params.id)); + imp.ext.sid = String(bid.params.id); } return imp; @@ -419,30 +364,14 @@ function _applyFloor(bid, imp, mediaType) { } // Prioritize module floor over bidder.param floor - let setFloor = false; if (moduleFloor) { imp.bidfloor = moduleFloor.floor; imp.bidfloorcur = moduleFloor.currency; - deepSetValue(imp, 'ext.fl', FLOOR_SOURCE.PBJS); - setFloor = true; + imp.ext.fl = FLOOR_SOURCE.PBJS; } else if (adapterFloor) { imp.bidfloor = adapterFloor.floor; imp.bidfloorcur = adapterFloor.currency; - deepSetValue(imp, 'ext.fl', FLOOR_SOURCE.IX); - setFloor = true; - } - - if (setFloor) { - if (mediaType == BANNER) { - deepSetValue(imp, 'banner.ext.bidfloor', imp.bidfloor); - deepSetValue(imp, 'banner.ext.fl', imp.ext.fl); - } else if (mediaType == VIDEO) { - deepSetValue(imp, 'video.ext.bidfloor', imp.bidfloor); - deepSetValue(imp, 'video.ext.fl', imp.ext.fl); - } else { - deepSetValue(imp, 'native.ext.bidfloor', imp.bidfloor); - deepSetValue(imp, 'native.ext.fl', imp.ext.fl); - } + imp.ext.fl = FLOOR_SOURCE.IX; } } @@ -535,7 +464,7 @@ function isValidSize(size) { * Determines whether or not the given size object is an element of the size * array. * - * @param {Array} sizeArray The size array. + * @param {array} sizeArray The size array. * @param {object} size The size object. * @return {boolean} True if the size object is an element of the size array, and false * otherwise. @@ -583,6 +512,7 @@ function checkVideoParams(mediaTypeVideoRef, paramsVideoRef) { if (!(protocolMediaType || protocolsMediaType || protocolVideoRef || protocolsVideoRef)) { errorList.push('IX Bid Adapter: protocol/protcols is not included in either the adunit or params level'); } + return errorList; } @@ -590,7 +520,7 @@ function checkVideoParams(mediaTypeVideoRef, paramsVideoRef) { * Get One size from Size Array * [[250,350]] -> [250, 350] * [250, 350] -> [250, 350] - * @param {Array} sizes array of sizes + * @param {array} sizes array of sizes */ function getFirstSize(sizes = []) { if (isValidSize(sizes)) { @@ -607,7 +537,7 @@ function getFirstSize(sizes = []) { * * @param {number} bidFloor The bidFloor parameter inside bid request config. * @param {number} bidFloorCur The bidFloorCur parameter inside bid request config. - * @return {boolean} True if this is a valid bidFloor parameters format, and false + * @return {bool} True if this is a valid bidFloor parameters format, and false * otherwise. */ function isValidBidFloorParams(bidFloor, bidFloorCur) { @@ -630,7 +560,7 @@ function nativeMediaTypeValid(bid) { * Get bid request object with the associated id. * * @param {*} id Id of the impression. - * @param {Array} impressions List of impressions sent in the request. + * @param {array} impressions List of impressions sent in the request. * @return {object} The impression with the associated id. */ function getBidRequest(id, impressions, validBidRequests) { @@ -648,7 +578,7 @@ function getBidRequest(id, impressions, validBidRequests) { /** * From the userIdAsEids array, filter for the ones our adserver can use, and modify them * for our purposes, e.g. add rtiPartner - * @param {Array} allEids userIdAsEids passed in by prebid + * @param {array} allEids userIdAsEids passed in by prebid * @return {object} contains toSend (eids to send to the adserver) and seenSources (used to filter * identity info from IX Library) */ @@ -676,11 +606,11 @@ function getEidInfo(allEids) { /** * Builds a request object to be sent to the ad server based on bid requests. * - * @param {Array} validBidRequests A list of valid bid request config objects. + * @param {array} validBidRequests A list of valid bid request config objects. * @param {object} bidderRequest An object containing other info like gdprConsent. * @param {object} impressions An object containing a list of impression objects describing the bids for each transaction - * @param {Array} version Endpoint version denoting banner, video or native. - * @return {Array} List of objects describing the request to the server. + * @param {array} version Endpoint version denoting banner, video or native. + * @return {array} List of objects describing the request to the server. * */ function buildRequest(validBidRequests, bidderRequest, impressions, version) { @@ -708,8 +638,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, 'fledgeEnabled') - let ixdiag = buildIXDiag(validBidRequests, fledgeEnabled); + let ixdiag = buildIXDiag(validBidRequests); for (var key in ixdiag) { r.ext.ixdiag[key] = ixdiag[key]; } @@ -719,10 +648,8 @@ function buildRequest(validBidRequests, bidderRequest, impressions, version) { r = applyRegulations(r, bidderRequest); let payload = {}; - if (validBidRequests[0].params.siteId) { - siteID = validBidRequests[0].params.siteId; - payload.s = siteID; - } + siteID = validBidRequests[0].params.siteId; + payload.s = siteID; const impKeys = Object.keys(impressions); let isFpdAdded = false; @@ -753,24 +680,10 @@ function buildRequest(validBidRequests, bidderRequest, impressions, version) { const isLastAdUnit = adUnitIndex === impKeys.length - 1; - r = addDeviceInfo(r); - r = deduplicateImpExtFields(r); - r = removeSiteIDs(r); - if (isLastAdUnit) { - let exchangeUrl = `${baseUrl}?`; - - if (siteID !== 0) { - exchangeUrl += `s=${siteID}`; - } - - if (isExchangeIdConfigured()) { - exchangeUrl += siteID !== 0 ? '&' : ''; - exchangeUrl += `p=${config.getConfig('exchangeId')}`; - } requests.push({ method: 'POST', - url: exchangeUrl, + url: baseUrl + '?s=' + siteID, data: deepClone(r), option: { contentType: 'text/plain', @@ -789,8 +702,8 @@ function buildRequest(validBidRequests, bidderRequest, impressions, version) { /** * addRTI adds RTI info of the partner to retrieved user IDs from prebid ID module. * - * @param {Array} userEids userEids info retrieved from prebid - * @param {Array} eidInfo eidInfo info from prebid + * @param {array} userEids userEids info retrieved from prebid + * @param {array} eidInfo eidInfo info from prebid */ function addRTI(userEids, eidInfo) { let identityInfo = window.headertag.getIdentityInfo(); @@ -809,7 +722,7 @@ function addRTI(userEids, eidInfo) { /** * createRequest creates the base request object - * @param {Array} validBidRequests A list of valid bid request config objects. + * @param {array} validBidRequests A list of valid bid request config objects. * @return {object} Object describing the request to the server. */ function createRequest(validBidRequests) { @@ -849,9 +762,9 @@ function addRequestedFeatureToggles(r, requestedFeatureToggles) { * * @param {object} r Base reuqest object. * @param {object} bidderRequest An object containing other info like gdprConsent. - * @param {Array} impressions A list of impressions to be added to the request. - * @param {Array} validBidRequests A list of valid bid request config objects. - * @param {Array} userEids User ID info retrieved from Prebid ID module. + * @param {array} impressions A list of impressions to be added to the request. + * @param {array} validBidRequests A list of valid bid request config objects. + * @param {array} userEids User ID info retrieved from Prebid ID module. * @return {object} Enriched object describing the request to the server. */ function enrichRequest(r, bidderRequest, impressions, validBidRequests, userEids) { @@ -958,10 +871,10 @@ function applyRegulations(r, bidderRequest) { /** * addImpressions adds impressions to request object * - * @param {Array} impressions List of impressions to be added to the request. - * @param {Array} impKeys List of impression keys. + * @param {array} impressions List of impressions to be added to the request. + * @param {array} impKeys List of impression keys. * @param {object} r Reuqest object. - * @param {number} adUnitIndex Index of the current add unit + * @param {int} adUnitIndex Index of the current add unit * @return {object} Reqyest object with added impressions describing the request to the server. */ function addImpressions(impressions, impKeys, r, adUnitIndex) { @@ -976,135 +889,63 @@ function addImpressions(impressions, impKeys, r, adUnitIndex) { const gpid = impressions[impKeys[adUnitIndex]].gpid; 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 bannerImpressions = impressionObjects.filter(impression => BANNER in impression); - const otherImpressions = impressionObjects.filter(impression => !(BANNER in impression)); - - if (bannerImpressions.length > 0) { - const bannerImpsKeyed = bannerImpressions.reduce((acc, bannerImp) => { - if (!acc[bannerImp.adunitCode]) { - acc[bannerImp.adunitCode] = [] - } - acc[bannerImp.adunitCode].push(bannerImp); - return acc; - }, {}); - for (const impId in bannerImpsKeyed) { - const bannerImps = bannerImpsKeyed[impId]; - const { id, banner: { topframe } } = bannerImps[0]; - let externalID = deepAccess(bannerImps[0], 'ext.externalID'); - const _bannerImpression = { - id, - 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 - if (_bannerImpression.banner.format[i].ext != null) { - if (_bannerImpression.banner.format[i].ext.sid != null) { - delete _bannerImpression.banner.format[i].ext.sid; - } - if (_bannerImpression.banner.format[i].ext.externalID != null) { - delete _bannerImpression.banner.format[i].ext.externalID; - } - } - - // add floor per size - if ('bidfloor' in bannerImps[i]) { - _bannerImpression.banner.format[i].ext.bidfloor = bannerImps[i].bidfloor; - } + const sid = impressions[impKeys[adUnitIndex]].sid + + if (impressionObjects.length && BANNER in impressionObjects[0]) { + const { id, banner: { topframe } } = impressionObjects[0]; + const _bannerImpression = { + id, + banner: { + topframe, + format: impressionObjects.map(({ banner: { w, h }, ext }) => ({ w, h, ext })) + }, + }; - if (JSON.stringify(_bannerImpression.banner.format[i].ext) === '{}') { - delete _bannerImpression.banner.format[i].ext; - } + for (let i = 0; i < _bannerImpression.banner.format.length; i++) { + // We add sid in imp.ext.sid therefore, remove from banner.format[].ext + if (_bannerImpression.banner.format[i].ext != null && _bannerImpression.banner.format[i].ext.sid != null) { + delete _bannerImpression.banner.format[i].ext.sid; } - const position = impressions[impKeys[adUnitIndex]].pos; - if (isInteger(position)) { - _bannerImpression.banner.pos = position; + // add floor per size + if ('bidfloor' in impressionObjects[i]) { + _bannerImpression.banner.format[i].ext.bidfloor = impressionObjects[i].bidfloor; } + } - if (dfpAdUnitCode || gpid || tid || sid || auctionEnvironment || externalID) { - _bannerImpression.ext = {}; - - _bannerImpression.ext.dfp_ad_unit_code = dfpAdUnitCode; - _bannerImpression.ext.gpid = gpid; - _bannerImpression.ext.tid = tid; - _bannerImpression.ext.sid = sid; - _bannerImpression.ext.externalID = externalID; + const position = impressions[impKeys[adUnitIndex]].pos; + if (isInteger(position)) { + _bannerImpression.banner.pos = position; + } - // enable fledge auction - if (auctionEnvironment == 1) { - _bannerImpression.ext.ae = 1; - } - } + if (dfpAdUnitCode || gpid || tid || sid) { + _bannerImpression.ext = {}; + _bannerImpression.ext.dfp_ad_unit_code = dfpAdUnitCode; + _bannerImpression.ext.gpid = gpid; + _bannerImpression.ext.tid = tid; + _bannerImpression.ext.sid = sid; + } - if ('bidfloor' in bannerImps[0]) { - _bannerImpression.bidfloor = bannerImps[0].bidfloor; - } + if ('bidfloor' in impressionObjects[0]) { + _bannerImpression.bidfloor = impressionObjects[0].bidfloor; + } - if ('bidfloorcur' in bannerImps[0]) { - _bannerImpression.bidfloorcur = bannerImps[0].bidfloorcur; - } + if ('bidfloorcur' in impressionObjects[0]) { + _bannerImpression.bidfloorcur = impressionObjects[0].bidfloorcur; + } - const adUnitFPD = impressions[impKeys[adUnitIndex]].adUnitFPD - if (adUnitFPD) { - deepSetValue(_bannerImpression, 'ext.data', adUnitFPD); - } - r.imp.push(_bannerImpression); + const adUnitFPD = impressions[impKeys[adUnitIndex]].adUnitFPD + if (adUnitFPD) { + _bannerImpression.ext.data = adUnitFPD; } + + r.imp.push(_bannerImpression); + } else { + // set imp.ext.gpid to resolved gpid for each imp + impressionObjects.forEach(imp => deepSetValue(imp, 'ext.gpid', gpid)); + r.imp.push(...impressionObjects); } - if (otherImpressions.length > 0) { - // Creates multiformat imp if they have the same ID - // if not same ID, just add the imp to the imp array - // Removes imp.ext.bidfloor - // Sets imp.ext.siteID to one of the other [video/native].ext.siteid if imp.ext.siteID doesnt exist - otherImpressions.forEach(imp => { - if (gpid) { - deepSetValue(imp, 'ext.gpid', gpid); - } - if (r.imp.length > 0) { - let matchFound = false; - r.imp.forEach((rImp, index) => { - if (imp.id === rImp.id && VIDEO in imp) { - rImp.video = imp.video; - rImp.video.ext = Object.assign({}, imp.video.ext, imp.ext); - if (deepAccess(rImp, 'video.ext.bidfloor', false) && deepAccess(rImp, 'bidfloor', false)) { - if (rImp.video.ext.bidfloor < rImp.bidfloor) { - rImp.bidfloor = rImp.video.ext.bidfloor; - } - } - if (!deepAccess(rImp, 'ext.siteID', false) && deepAccess(imp, 'video.ext.siteID')) { - deepSetValue(rImp, 'ext.siteID', imp.video.ext.siteID); - deepSetValue(r, 'ext.ixdiag.usid', true); - } - matchFound = true; - } else if (imp.id === rImp.id && NATIVE in imp) { - rImp.native = imp.native; - rImp.native.ext = Object.assign({}, imp.native.ext, imp.ext); - if (deepAccess(rImp, 'native.ext.bidfloor', false) && deepAccess(rImp, 'bidfloor', false)) { - if (rImp.native.ext.bidfloor < rImp.bidfloor) { - rImp.bidfloor = rImp.native.ext.bidfloor; - } - } - if (!deepAccess(rImp, 'ext.siteID', false) && deepAccess(imp, 'native.ext.siteID', false)) { - deepSetValue(rImp, 'ext.siteID', imp.native.ext.siteID); - deepSetValue(r, 'ext.ixdiag.usid', true); - } - matchFound = true; - } - }); - if (!matchFound) { - r.imp.push(imp); - } - } else { - r.imp.push(imp); - } - }); - } + return r; } @@ -1113,7 +954,7 @@ This function retrieves the page URL and appends first party data query paramete to it without adding duplicate query parameters. Returns original referer URL if no IX FPD exists. @param {Object} bidderRequest - The bidder request object containing information about the bid and the page. @returns {string} - The modified page URL with first party data query parameters appended. - */ +*/ function getIxFirstPartyDataPageUrl (bidderRequest) { // Parse additional runtime configs. const bidderCode = (bidderRequest && bidderRequest.bidderCode) || 'ix'; @@ -1143,7 +984,7 @@ This function appends the provided query parameters to the given URL without add @param {string} url - The base URL to which query parameters will be appended. @param {Object} params - An object containing key-value pairs of query parameters to append. @returns {string} - The modified URL with the provided query parameters appended. - */ +*/ function appendIXQueryParams(bidderRequest, url, params) { let urlObj; try { @@ -1228,10 +1069,10 @@ function addAdUnitFPD(imp, bid) { /** * addIdentifiersInfo adds indentifier info to ixDaig. * - * @param {Array} impressions List of impressions to be added to the request. + * @param {array} impressions List of impressions to be added to the request. * @param {object} r Reuqest object. - * @param {Array} impKeys List of impression keys. - * @param {number} adUnitIndex Index of the current add unit + * @param {array} impKeys List of impression keys. + * @param {int} adUnitIndex Index of the current add unit * @param {object} payload Request payload object. * @param {string} baseUrl Base exchagne URL. * @return {object} Reqyest object with added indentigfier info to ixDiag. @@ -1254,7 +1095,7 @@ function addIdentifiersInfo(impressions, r, impKeys, adUnitIndex, payload, baseU /** * Return an object of user IDs stored by Prebid User ID module * - * @returns {Array} ID providers that are present in userIds + * @returns {array} ID providers that are present in userIds */ function _getUserIds(bidRequest) { const userIds = bidRequest.userId || {}; @@ -1265,16 +1106,15 @@ function _getUserIds(bidRequest) { /** * 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 + * @param {array} validBidRequests The valid bid requests from prebid * @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); - let ixdiag = { + var ixdiag = { mfu: 0, bu: 0, iu: 0, @@ -1285,13 +1125,12 @@ function buildIXDiag(validBidRequests, fledgeEnabled) { version: '$prebid.version$', userIds: _getUserIds(validBidRequests[0]), url: window.location.href.split('?')[0], - vpd: defaultVideoPlacement, - ae: fledgeEnabled + vpd: defaultVideoPlacement }; // create ad unit map and collect the required diag properties - for (let adUnit of adUnitMap) { - let bid = validBidRequests.filter(bidRequest => bidRequest.adUnitCode === adUnit)[0]; + for (let i = 0; i < adUnitMap.length; i++) { + var bid = validBidRequests.filter(bidRequest => bidRequest.adUnitCode === adUnitMap[i])[0]; if (deepAccess(bid, 'mediaTypes')) { if (Object.keys(bid.mediaTypes).length > 1) { @@ -1327,8 +1166,8 @@ function buildIXDiag(validBidRequests, fledgeEnabled) { /** * - * @param {Array} bannerSizeList list of banner sizes - * @param {Array} bannerSize the size to be removed + * @param {array} bannerSizeList list of banner sizes + * @param {array} bannerSize the size to be removed * @return {boolean} true if successfully removed, false if not found */ @@ -1397,7 +1236,7 @@ function createVideoImps(validBidRequest, videoImps) { * @param {object} missingBannerSizes reference to missing banner config sizes * @param {object} bannerImps reference to created banner impressions */ -function createBannerImps(validBidRequest, missingBannerSizes, bannerImps, bidderRequest) { +function createBannerImps(validBidRequest, missingBannerSizes, bannerImps) { let imp = bidToBannerImp(validBidRequest); const bannerSizeDefined = includesSize(deepAccess(validBidRequest, 'mediaTypes.banner.sizes'), deepAccess(validBidRequest, 'params.size')); @@ -1413,21 +1252,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, 'fledgeEnabled') - if (fledgeEnabled) { - const auctionEnvironment = deepAccess(validBidRequest, 'ortb2Imp.ext.ae') - if (auctionEnvironment) { - if (isInteger(auctionEnvironment)) { - bannerImps[validBidRequest.adUnitCode].ae = auctionEnvironment; - } else { - logWarn('error setting auction environment flag - must be an integer') - } - } else if (deepAccess(bidderRequest, 'defaultForSlots') == 1) { - bannerImps[validBidRequest.adUnitCode].ae = 1 - } - } - // AdUnit-Specific First Party Data const adUnitFPD = deepAccess(validBidRequest, 'ortb2Imp.ext.data'); if (adUnitFPD) { @@ -1487,7 +1311,7 @@ function updateMissingSizes(validBidRequest, missingBannerSizes, imp) { /** * @param {object} bid ValidBidRequest object, used to adjust floor * @param {object} imp Impression object to be modified - * @param {Array} newSize The new size to be applied + * @param {array} newSize The new size to be applied * @return {object} newImp Updated impression object */ function createMissingBannerImp(bid, imp, newSize) { @@ -1663,17 +1487,6 @@ function isIndexRendererPreferred(bid) { return !isValid || renderer.backupOnly; } -function isExchangeIdConfigured() { - let exchangeId = config.getConfig('exchangeId'); - if (typeof exchangeId === 'number' && isFinite(exchangeId)) { - return true; - } - if (typeof exchangeId === 'string' && exchangeId.trim() !== '' && isFinite(Number(exchangeId))) { - return true; - } - return false; -} - export const spec = { code: BIDDER_CODE, @@ -1731,21 +1544,14 @@ export const spec = { } } - if (!isExchangeIdConfigured() && bid.params.siteId == undefined) { - logError('IX Bid Adapter: Invalid configuration - either siteId or exchangeId must be configured.'); + if (typeof bid.params.siteId !== 'string' && typeof bid.params.siteId !== 'number') { + logError('IX Bid Adapter: siteId must be string or number type.', { bidder: BIDDER_CODE, code: ERROR_CODES.SITE_ID_INVALID_VALUE }); return false; } - if (bid.params.siteId !== undefined) { - if (typeof bid.params.siteId !== 'string' && typeof bid.params.siteId !== 'number') { - logError('IX Bid Adapter: siteId must be string or number type.', { bidder: BIDDER_CODE, code: ERROR_CODES.SITE_ID_INVALID_VALUE }); - return false; - } - - if (typeof bid.params.siteId !== 'string' && isNaN(Number(bid.params.siteId))) { - logError('IX Bid Adapter: siteId must valid value', { bidder: BIDDER_CODE, code: ERROR_CODES.SITE_ID_INVALID_VALUE }); - return false; - } + if (typeof bid.params.siteId !== 'string' && isNaN(Number(bid.params.siteId))) { + logError('IX Bid Adapter: siteId must valid value', { bidder: BIDDER_CODE, code: ERROR_CODES.SITE_ID_INVALID_VALUE }); + return false; } if (hasBidFloor || hasBidFloorCur) { @@ -1778,15 +1584,10 @@ export const spec = { return nativeMediaTypeValid(bid); }, - // For testing only - resets the siteID to 0 so that it can be set again - resetSiteID: function () { - siteID = 0; - }, - /** * Make a server request from the list of BidRequests. * - * @param {Array} validBidRequests A list of valid bid request config objects. + * @param {array} validBidRequests A list of valid bid request config objects. * @param {object} bidderRequest A object contains bids and other info like gdprConsent. * @return {object} Info describing the request to the server. */ @@ -1805,7 +1606,7 @@ export const spec = { for (const type in adUnitMediaTypes) { switch (adUnitMediaTypes[type]) { case BANNER: - createBannerImps(validBidRequest, missingBannerSizes, bannerImps, bidderRequest); + createBannerImps(validBidRequest, missingBannerSizes, bannerImps); break; case VIDEO: createVideoImps(validBidRequest, videoImps) @@ -1841,31 +1642,17 @@ export const spec = { } } - // Step 3: Build banner, video & native requests - let allImps = []; + // Step 4: Build banner, video & native requests if (Object.keys(bannerImps).length > 0) { - allImps.push(bannerImps); + reqs.push(...buildRequest(validBidRequests, bidderRequest, bannerImps, BANNER_ENDPOINT_VERSION)); } if (Object.keys(videoImps).length > 0) { - allImps.push(videoImps); + reqs.push(...buildRequest(validBidRequests, bidderRequest, videoImps, VIDEO_ENDPOINT_VERSION)); } if (Object.keys(nativeImps).length > 0) { - allImps.push(nativeImps); + reqs.push(...buildRequest(validBidRequests, bidderRequest, nativeImps)); } - if (FEATURE_TOGGLES.isFeatureEnabled('pbjs_enable_multiformat')) { - reqs.push(...buildRequest(validBidRequests, bidderRequest, combineImps(allImps))); - } else { - if (Object.keys(bannerImps).length > 0) { - reqs.push(...buildRequest(validBidRequests, bidderRequest, bannerImps, BANNER_ENDPOINT_VERSION)); - } - if (Object.keys(videoImps).length > 0) { - reqs.push(...buildRequest(validBidRequests, bidderRequest, videoImps, VIDEO_ENDPOINT_VERSION)); - } - if (Object.keys(nativeImps).length > 0) { - reqs.push(...buildRequest(validBidRequests, bidderRequest, nativeImps)); - } - } return reqs; }, @@ -1874,24 +1661,19 @@ export const spec = { * * @param {object} serverResponse A successful response from the server. * @param {object} bidderRequest The bid request sent to the server. - * @return {Array} An array of bids which were nested inside the server. + * @return {array} An array of bids which were nested inside the server. */ interpretResponse: function (serverResponse, bidderRequest) { 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')) { + if (!serverResponse.hasOwnProperty('body') || !serverResponse.body.hasOwnProperty('seatbid')) { + FEATURE_TOGGLES.setFeatureToggles(serverResponse); return bids; } const responseBody = serverResponse.body; - const seatbid = responseBody.seatbid || []; - + const seatbid = responseBody.seatbid; for (let i = 0; i < seatbid.length; i++) { if (!seatbid[i].hasOwnProperty('bid')) { continue; @@ -1927,28 +1709,8 @@ 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, - fledgeAuctionConfigs, - }; - } catch (error) { - logWarn('Error attaching AuctionConfigs', error); - return bids; - } - } else { - return bids; - } + FEATURE_TOGGLES.setFeatureToggles(serverResponse); + return bids; }, /** @@ -1966,8 +1728,8 @@ export const spec = { /** * Determine which user syncs should occur * @param {object} syncOptions - * @param {Array} serverResponses - * @returns {Array} User sync pixels + * @param {array} serverResponses + * @returns {array} User sync pixels */ getUserSyncs: function (syncOptions, serverResponses) { const syncs = []; @@ -2008,11 +1770,11 @@ export const spec = { }; /** - * Build img user sync url - * @param {number} syncsPerBidder number of syncs Per Bidder - * @param {number} index index to pass - * @returns {string} img user sync url - */ + * Build img user sync url + * @param {int} syncsPerBidder number of syncs Per Bidder + * @param {int} index index to pass + * @returns {string} img user sync url + */ function buildImgSyncUrl(syncsPerBidder, index) { let consentString = ''; let gdprApplies = '0'; @@ -2022,176 +1784,8 @@ function buildImgSyncUrl(syncsPerBidder, index) { if (gdprConsent && gdprConsent.hasOwnProperty('consentString')) { consentString = gdprConsent.consentString || ''; } - let siteIdParam = siteID !== 0 ? '&site_id=' + siteID.toString() : ''; - - return IMG_USER_SYNC_URL + siteIdParam + '&p=' + syncsPerBidder.toString() + '&i=' + index.toString() + '&gdpr=' + gdprApplies + '&gdpr_consent=' + consentString + '&us_privacy=' + (usPrivacy || ''); -} - -/** - * Combines all imps into a single object - * @param {Array} imps array of imps - * @returns object - */ -export function combineImps(imps) { - const result = {} - imps.forEach((imp) => { - Object.keys(imp).forEach((key) => { - if (Object.keys(result).includes(key)) { - if (result[key].hasOwnProperty('ixImps') && imp[key].hasOwnProperty('ixImps')) { - result[key].ixImps = [...result[key].ixImps, ...imp[key].ixImps]; - } else if (result[key].hasOwnProperty('missingImps') && imp[key].hasOwnProperty('missingImps')) { - result[key].missingImps = [...result[key].missingImps, ...imp[key].missingImps]; - } else if (imp[key].hasOwnProperty('ixImps')) { - result[key].ixImps = imp[key].ixImps; - } else if (imp[key].hasOwnProperty('missingImps')) { - result[key].missingImps = imp[key].missingImps - } - } else { - result[key] = imp[key]; - } - }); - }); - return result; -} - -/** - * Deduplicates ext fields. For example if imp.ext.tid exists, removes imp.banner.ext.tid - * - * @param {object} r request object - * @returns object - */ -export function deduplicateImpExtFields(r) { - r.imp.forEach((imp, index) => { - const impExt = imp.ext; - if (impExt == undefined) { - return r; - } - if (getFormatCount(imp) < 2) { - return; - } - Object.keys(impExt).forEach((key) => { - if (BANNER in imp) { - const bannerExt = imp.banner.ext; - if (bannerExt !== undefined && bannerExt[key] !== undefined && bannerExt[key] == impExt[key]) { - delete r.imp[index].banner.ext[key]; - } - if (imp.banner.format !== undefined) { - for (let i = 0; i < imp.banner.format.length; i++) { - if (imp.banner.format[i].ext != undefined && imp.banner.format[i].ext[key] != undefined && imp.banner.format[i].ext[key] == impExt[key]) { - delete r.imp[index].banner.format[i].ext[key]; - } - } - } - } - if (VIDEO in imp) { - const videoExt = imp.video.ext; - if (videoExt !== undefined && videoExt[key] !== undefined && videoExt[key] == impExt[key]) { - delete r.imp[index].video.ext[key]; - } - } - if (NATIVE in imp) { - const nativeExt = imp.native.ext; - if (nativeExt !== undefined && nativeExt[key] !== undefined && nativeExt[key] == impExt[key]) { - delete r.imp[index].native.ext[key]; - } - } - }); - }); - return r; -} - -/** - * Removes ext.siteids in multiformat scenario - * Site id will be set only at imp.ext.siteId - * - * @param {object} r request object - * @returns object - */ -export function removeSiteIDs(r) { - r.imp.forEach((imp, index) => { - const impExt = imp.ext; - if (impExt == undefined) { - return r; - } - if (getFormatCount(imp) < 2) { - return; - } - - if (BANNER in imp) { - const bannerExt = imp.banner.ext; - if (bannerExt !== undefined && bannerExt.siteID !== undefined) { - delete r.imp[index].banner.ext.siteID; - } - if (imp.banner.format !== undefined) { - for (let i = 0; i < imp.banner.format.length; i++) { - if (imp.banner.format[i].ext !== undefined && imp.banner.format[i].ext.siteID !== undefined) { - deepSetValue(r.imp[index], 'ext.siteID', imp.banner.format[i].ext.siteID); - deepSetValue(r, 'ext.ixdiag.usid', true); - delete r.imp[index].banner.format[i].ext.siteID; - } - } - } - } - - if (VIDEO in imp) { - const videoExt = imp.video.ext; - if (videoExt !== undefined && videoExt.siteID !== undefined) { - delete r.imp[index].video.ext.siteID; - } - } - - if (NATIVE in imp) { - const nativeExt = imp.native.ext; - if (nativeExt !== undefined && nativeExt.siteID !== undefined) { - delete r.imp[index].native.ext.siteID; - } - } - }); - return r; -} - -/** - * Gets count of banner/video/native formats in imp - * @param {object} imp - * @returns int - */ -function getFormatCount(imp) { - let formatCount = 0; - if (imp.banner !== undefined) { - formatCount += 1; - } - if (imp.video !== undefined) { - formatCount += 1; - } - if (imp.native !== undefined) { - formatCount += 1; - } - 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 - * @returns object - */ -export function addDeviceInfo(r) { - if (r.device == undefined) { - r.device = {}; - } - r.device.h = window.screen.height; - r.device.w = window.screen.width; - - return r; + return IMG_USER_SYNC_URL + '&site_id=' + siteID.toString() + '&p=' + syncsPerBidder.toString() + '&i=' + index.toString() + '&gdpr=' + gdprApplies + '&gdpr_consent=' + consentString + '&us_privacy=' + (usPrivacy || ''); } registerBidder(spec); diff --git a/modules/ixBidAdapter.md b/modules/ixBidAdapter.md index 0705c5932cf..638cb11c5ab 100644 --- a/modules/ixBidAdapter.md +++ b/modules/ixBidAdapter.md @@ -469,11 +469,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 [fledgeForGpt](https://docs.prebid.org/dev-docs/modules/fledgeForGpt.html) module to build and enable Fledge. - Additional Information ====================== diff --git a/modules/jixieBidAdapter.js b/modules/jixieBidAdapter.js index 75268e9d168..b587011c748 100644 --- a/modules/jixieBidAdapter.js +++ b/modules/jixieBidAdapter.js @@ -1,4 +1,4 @@ -import {deepAccess, getDNT, isArray, logWarn, isFn, isPlainObject, logError, logInfo} from '../src/utils.js'; +import {deepAccess, getDNT, isArray, logWarn} from '../src/utils.js'; import {config} from '../src/config.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {getStorageManager} from '../src/storageManager.js'; @@ -7,36 +7,13 @@ import {ajax} from '../src/ajax.js'; import {getRefererInfo} from '../src/refererDetection.js'; import {Renderer} from '../src/Renderer.js'; -const ADAPTER_VERSION = '2.1.0'; -const PREBID_VERSION = '$prebid.version$'; - const BIDDER_CODE = 'jixie'; export const storage = getStorageManager({bidderCode: BIDDER_CODE}); +const EVENTS_URL = 'https://hbtra.jixie.io/sync/hb?'; 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; -/** - * Get bid floor from Price Floors Module - * - * @param {Object} bid - * @returns {float||null} - */ -function getBidFloor(bid) { - if (!isFn(bid.getFloor)) { - return null; - } - let floor = bid.getFloor({ - currency: 'USD', - mediaType: '*', - size: '*' - }); - if (isPlainObject(floor) && !isNaN(floor.floor) && floor.currency === 'USD') { - return floor.floor; - } - return null; -} - /** * Own miscellaneous support functions: */ @@ -61,24 +38,12 @@ function setIds_(clientId, sessionId) { } catch (error) {} } -/** - * fetch some ids from cookie, LS. - * @returns - */ -const defaultGenIds_ = [ - { id: '_jxtoko' }, - { id: '_jxifo' }, - { id: '_jxtdid' }, - { id: '_jxcomp' } -]; - -function fetchIds_(cfg) { +function fetchIds_() { let ret = { client_id_c: '', client_id_ls: '', session_id_c: '', - session_id_ls: '', - jxeids: {} + session_id_ls: '' }; try { let tmp = storage.getCookie('_jxx'); @@ -90,12 +55,8 @@ function fetchIds_(cfg) { if (tmp) ret.client_id_ls = tmp; tmp = storage.getDataFromLocalStorage('_jxxs'); if (tmp) ret.session_id_ls = tmp; - - let arr = cfg.genids ? cfg.genids : defaultGenIds_; - arr.forEach(function(o) { - tmp = storage.getCookie(o.ck ? o.ck : o.id); - if (tmp) ret.jxeids[o.id] = tmp; - }); + tmp = storage.getCookie('_jxtoko'); + if (tmp) ret.jxtoko_id = tmp; } catch (error) {} return ret; } @@ -112,6 +73,14 @@ function getDevice_() { return device; } +function pingTracking_(endpointOverride, qpobj) { + internal.ajax((endpointOverride || EVENTS_URL), null, qpobj, { + withCredentials: true, + method: 'GET', + crossOrigin: true + }); +} + function jxOutstreamRender_(bidAd) { bidAd.renderer.push(() => { window.JixieOutstreamVideo.init({ @@ -163,6 +132,17 @@ function getMiscDims_() { return ret; } +/* function addUserId(eids, id, source, rti) { + if (id) { + if (rti) { + eids.push({ source, id, rti_partner: rti }); + } else { + eids.push({ source, id }); + } + } + return eids; +} */ + // easier for replacement in the unit test export const internal = { getDevice: getDevice_, @@ -173,6 +153,7 @@ export const internal = { export const spec = { code: BIDDER_CODE, + EVENTS_URL: EVENTS_URL, supportedMediaTypes: [BANNER, VIDEO], isBidRequestValid: function(bid) { if (bid.bidder !== BIDDER_CODE || typeof bid.params === 'undefined') { @@ -189,38 +170,39 @@ export const spec = { let bids = []; validBidRequests.forEach(function(one) { - let gpid = deepAccess(one, 'ortb2Imp.ext.gpid', deepAccess(one, 'ortb2Imp.ext.data.pbadslot', '')); - let tmp = { + bids.push({ bidId: one.bidId, adUnitCode: one.adUnitCode, mediaTypes: (one.mediaTypes === 'undefined' ? {} : one.mediaTypes), sizes: (one.sizes === 'undefined' ? [] : one.sizes), params: one.params, - gpid: gpid - }; - let bidFloor = getBidFloor(one); - if (bidFloor) { - tmp.bidFloor = bidFloor; - } - bids.push(tmp); + }); }); - let jxCfg = config.getConfig('jixie') || {}; - let ids = fetchIds_(jxCfg); + let jixieCfgBlob = config.getConfig('jixie'); + if (!jixieCfgBlob) { + jixieCfgBlob = {}; + } + + let ids = fetchIds_(); let eids = []; let miscDims = internal.getMiscDims(); let schain = deepAccess(validBidRequests[0], 'schain'); - let eids1 = validBidRequests[0].userIdAsEids; + let eids1 = validBidRequests[0].userIdAsEids // all available user ids are sent to our backend in the standard array layout: if (eids1 && eids1.length) { eids = eids1; } // we want to send this blob of info to our backend: + let pg = config.getConfig('priceGranularity'); + if (!pg) { + pg = {}; + } + let transformedParams = Object.assign({}, { // TODO: fix auctionId leak: https://github.com/prebid/Prebid.js/issues/9781 - auctionid: bidderRequest.auctionId || '', - aid: jxCfg.aid || '', + auctionid: bidderRequest.auctionId, timeout: bidderRequest.timeout, currency: currency, timestamp: (new Date()).getTime(), @@ -231,10 +213,8 @@ export const spec = { bids: bids, eids: eids, schain: schain, - pricegranularity: (config.getConfig('priceGranularity') || {}), - ver: ADAPTER_VERSION, - pbjsver: PREBID_VERSION, - cfg: jxCfg + pricegranularity: pg, + cfg: jixieCfgBlob }, ids); return Object.assign({}, { method: 'POST', @@ -245,20 +225,48 @@ export const spec = { }, onTimeout: function(timeoutData) { - logError('jixie adapter timed out for the auction.', timeoutData); + let jxCfgBlob = config.getConfig('jixie'); + if (jxCfgBlob && jxCfgBlob.onTimeout == 'off') { + return; + } + let url = null;// default + if (jxCfgBlob && jxCfgBlob.onTimeoutUrl && typeof jxCfgBlob.onTimeoutUrl == 'string') { + url = jxCfgBlob.onTimeoutUrl; + } + let miscDims = internal.getMiscDims(); + pingTracking_(url, // no overriding ping URL . just use default + { + action: 'hbtimeout', + device: miscDims.device, + pageurl: encodeURIComponent(miscDims.pageurl), + domain: encodeURIComponent(miscDims.domain), + auctionid: deepAccess(timeoutData, '0.auctionId'), + timeout: deepAccess(timeoutData, '0.timeout'), + count: timeoutData.length + }); }, onBidWon: function(bid) { + if (bid.notrack) { + return; + } if (bid.trackingUrl) { - internal.ajax(bid.trackingUrl, null, {}, { - withCredentials: true, - method: 'GET', - crossOrigin: true + pingTracking_(bid.trackingUrl, {}); + } else { + let miscDims = internal.getMiscDims(); + pingTracking_((bid.trackingUrlBase ? bid.trackingUrlBase : null), { + action: 'hbbidwon', + device: miscDims.device, + pageurl: encodeURIComponent(miscDims.pageurl), + domain: encodeURIComponent(miscDims.domain), + cid: bid.cid, + cpid: bid.cpid, + jxbidid: bid.jxBidId, + auctionid: bid.auctionId, + cpm: bid.cpm, + requestid: bid.requestId }); } - logInfo( - `jixie adapter won the auction. Bid id: ${bid.bidId}, Ad Unit Id: ${bid.adUnitId}` - ); }, interpretResponse: function(response, bidRequest) { @@ -266,6 +274,7 @@ export const spec = { const bidResponses = []; response.body.bids.forEach(function(oneBid) { let bnd = {}; + Object.assign(bnd, oneBid); if (oneBid.osplayer) { bnd.adResponse = { diff --git a/modules/justIdSystem.js b/modules/justIdSystem.js index a5698023020..15b1c90da4e 100644 --- a/modules/justIdSystem.js +++ b/modules/justIdSystem.js @@ -10,13 +10,6 @@ import { submodule } from '../src/hook.js' import { loadExternalScript } from '../src/adloader.js' import {includes} from '../src/polyfill.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 = 'justId'; const EXTERNAL_SCRIPT_MODULE_CODE = 'justtag'; const LOG_PREFIX = 'User ID - JustId submodule: '; @@ -98,12 +91,6 @@ export const justIdSubmodule = { } } }; - }, - eids: { - 'justId': { - source: 'justtag.com', - atype: 1 - }, } }; diff --git a/modules/jwplayerRtdProvider.js b/modules/jwplayerRtdProvider.js index 573ff391dae..b79843dccfd 100644 --- a/modules/jwplayerRtdProvider.js +++ b/modules/jwplayerRtdProvider.js @@ -16,11 +16,6 @@ import {deepAccess, logError} from '../src/utils.js'; import {find} from '../src/polyfill.js'; import {getGlobal} from '../src/prebidGlobal.js'; -/** - * @typedef {import('../modules/rtdModule/index.js').RtdSubmodule} RtdSubmodule - * @typedef {import('../modules/rtdModule/index.js').adUnit} adUnit - */ - const SUBMODULE_NAME = 'jwplayer'; const JWPLAYER_DOMAIN = SUBMODULE_NAME + '.com'; const segCache = {}; @@ -31,16 +26,16 @@ let resumeBidRequest; /** @type {RtdSubmodule} */ export const jwplayerSubmodule = { /** - * used to link submodule with realTimeData - * @type {string} - */ + * used to link submodule with realTimeData + * @type {string} + */ name: SUBMODULE_NAME, /** - * add targeting data to bids and signal completion to realTimeData module - * @function - * @param {Obj} bidReqConfig - * @param {function} onDone - */ + * add targeting data to bids and signal completion to realTimeData module + * @function + * @param {Obj} bidReqConfig + * @param {function} onDone + */ getBidRequestData: enrichBidRequest, init }; diff --git a/modules/kargoBidAdapter.js b/modules/kargoBidAdapter.js index 9d8c7bc06a1..5b315ef5791 100644 --- a/modules/kargoBidAdapter.js +++ b/modules/kargoBidAdapter.js @@ -24,7 +24,6 @@ const CURRENCY = Object.freeze({ }); const REQUEST_KEYS = Object.freeze({ - USER_DATA: 'ortb2.user.data', SOCIAL_CANVAS: 'params.socialCanvas', SUA: 'ortb2.device.sua', TDID_ADAPTER: 'userId.tdid', @@ -98,26 +97,11 @@ function buildRequests(validBidRequests, bidderRequest) { user: getUserIds(tdidAdapter, bidderRequest.uspConsent, bidderRequest.gdprConsent, firstBidRequest.userIdAsEids, bidderRequest.gppConsent), }); - if (firstBidRequest.ortb2 != null) { - krakenParams.site = { - cat: firstBidRequest.ortb2.site.cat - } - } - - // Add schain - if (firstBidRequest.schain && firstBidRequest.schain.nodes) { - krakenParams.schain = firstBidRequest.schain - } - - // Add user data object if available - krakenParams.user.data = deepAccess(firstBidRequest, REQUEST_KEYS.USER_DATA) || []; - const reqCount = getRequestCount() if (reqCount != null) { krakenParams.requestCount = reqCount; } - // Add currency if not USD if (currency != null && currency != CURRENCY.US_DOLLAR) { krakenParams.cur = currency; } @@ -475,8 +459,8 @@ function getImpression(bid) { imp.bidderWinCount = bid.bidderWinsCount; } - const gpid = deepAccess(bid, 'ortb2Imp.ext.gpid') || deepAccess(bid, 'ortb2Imp.ext.data.pbadslot'); - if (gpid) { + const gpid = getGPID(bid) + if (gpid != null && gpid != '') { imp.fpd = { gpid: gpid } @@ -499,6 +483,29 @@ function getImpression(bid) { return imp } +function getGPID(bid) { + if (bid.ortb2Imp != null) { + if (bid.ortb2Imp.gpid != null && bid.ortb2Imp.gpid != '') { + return bid.ortb2Imp.gpid; + } + + if (bid.ortb2Imp.ext != null && bid.ortb2Imp.ext.data != null) { + if (bid.ortb2Imp.ext.data.pbAdSlot != null && bid.ortb2Imp.ext.data.pbAdSlot != '') { + return bid.ortb2Imp.ext.data.pbAdSlot; + } + + if (bid.ortb2Imp.ext.data.adServer != null && bid.ortb2Imp.ext.data.adServer.adSlot != null && bid.ortb2Imp.ext.data.adServer.adSlot != '') { + return bid.ortb2Imp.ext.data.adServer.adSlot; + } + } + } + + if (bid.adUnitCode != null && bid.adUnitCode != '') { + return bid.adUnitCode; + } + return ''; +} + export const spec = { gvlid: BIDDER.GVLID, code: BIDDER.CODE, diff --git a/modules/kinessoIdSystem.js b/modules/kinessoIdSystem.js index 35b8dcc182d..632f3a669aa 100644 --- a/modules/kinessoIdSystem.js +++ b/modules/kinessoIdSystem.js @@ -10,12 +10,6 @@ import {ajax} from '../src/ajax.js'; import {submodule} from '../src/hook.js'; import {coppaDataHandler, uspDataHandler} from '../src/adapterManager.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 - */ - const MODULE_NAME = 'kpuid'; const ID_SVC = 'https://id.knsso.com/id'; // These values should NEVER change. If @@ -239,13 +233,8 @@ export const kinessoIdSubmodule = { const payloadString = JSON.stringify(kinessoIdPayload); ajax(kinessoSyncUrl(accountId, consentData), syncId(knnsoId), payloadString, {method: 'POST', withCredentials: true}); return {'id': knnsoId}; - }, - eids: { - 'kpuid': { - source: 'kpuid.com', - atype: 3 - }, - }, + } + }; // Register submodule for userId diff --git a/modules/kueezBidAdapter.js b/modules/kueezBidAdapter.js index 5a5536e0c1a..0a868661310 100644 --- a/modules/kueezBidAdapter.js +++ b/modules/kueezBidAdapter.js @@ -1,16 +1,4 @@ -import { - logWarn, - logInfo, - isArray, - isFn, - deepAccess, - isEmpty, - contains, - timestamp, - triggerPixel, - isInteger, - getBidIdParameter -} from '../src/utils.js'; +import { logWarn, logInfo, isArray, isFn, deepAccess, isEmpty, contains, timestamp, getBidIdParameter, triggerPixel, isInteger } from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; import { config } from '../src/config.js'; diff --git a/modules/kueezRtbBidAdapter.js b/modules/kueezRtbBidAdapter.js index 9a336b16136..bb0534d6372 100644 --- a/modules/kueezRtbBidAdapter.js +++ b/modules/kueezRtbBidAdapter.js @@ -251,20 +251,13 @@ function interpretResponse(serverResponse, request) { } } -function getUserSyncs(syncOptions, responses, gdprConsent = {}, uspConsent = '', gppConsent = {}) { +function getUserSyncs(syncOptions, responses, gdprConsent = {}, uspConsent = '') { let syncs = []; const {iframeEnabled, pixelEnabled} = syncOptions; const {gdprApplies, consentString = ''} = gdprConsent; - const {gppString, applicableSections} = gppConsent; const cidArr = responses.filter(resp => deepAccess(resp, 'body.cid')).map(resp => resp.body.cid).filter(uniques); - let params = `?cid=${encodeURIComponent(cidArr.join(','))}&gdpr=${gdprApplies ? 1 : 0}&gdpr_consent=${encodeURIComponent(consentString || '')}&us_privacy=${encodeURIComponent(uspConsent || '')}` - - if (gppString && applicableSections?.length) { - params += '&gpp=' + encodeURIComponent(gppString); - params += '&gpp_sid=' + encodeURIComponent(applicableSections.join(',')); - } - + const params = `?cid=${encodeURIComponent(cidArr.join(','))}&gdpr=${gdprApplies ? 1 : 0}&gdpr_consent=${encodeURIComponent(consentString || '')}&us_privacy=${encodeURIComponent(uspConsent || '')}` if (iframeEnabled) { syncs.push({ type: 'iframe', diff --git a/modules/kulturemediaBidAdapter.js b/modules/kulturemediaBidAdapter.js new file mode 100644 index 00000000000..fb3f6e4e231 --- /dev/null +++ b/modules/kulturemediaBidAdapter.js @@ -0,0 +1,472 @@ +import { + deepAccess, + deepSetValue, + isArray, + isFn, + isNumber, + isPlainObject, + isStr, + logError, + logInfo, + logMessage +} from '../src/utils.js'; +import {registerBidder} from '../src/adapters/bidderFactory.js'; +import {BANNER, VIDEO} from '../src/mediaTypes.js'; + +const BIDDER_CODE = 'kulturemedia'; +const DEFAULT_BID_TTL = 300; +const DEFAULT_CURRENCY = 'USD'; +const DEFAULT_NET_REVENUE = true; +const DEFAULT_NETWORK_ID = 1; +const OPENRTB_VIDEO_PARAMS = [ + 'mimes', + 'minduration', + 'maxduration', + 'placement', + 'protocols', + 'startdelay', + 'skip', + 'skipafter', + 'minbitrate', + 'maxbitrate', + 'delivery', + 'playbackmethod', + 'api', + 'linearity' +]; + +export const spec = { + code: BIDDER_CODE, + VERSION: '1.0.0', + supportedMediaTypes: [BANNER, VIDEO], + ENDPOINT: 'https://ads.kulture.media/pbjs', + + /** + * Determines whether or not the given bid request is valid. + * + * @param {BidRequest} bidRequest The bid params to validate. + * @return boolean True if this is a valid bid, and false otherwise. + */ + isBidRequestValid: function (bid) { + return ( + _validateParams(bid) && + _validateBanner(bid) && + _validateVideo(bid) + ); + }, + + /** + * Make a server request from the list of BidRequests. + * + * @param {BidRequest[]} validBidRequests A non-empty list of bid requests which should be sent to the Server. + * @param {BidderRequest} bidderRequest bidder request object. + * @return ServerRequest Info describing the request to the server. + */ + buildRequests: function (validBidRequests, bidderRequest) { + if (!validBidRequests || !bidderRequest) { + return; + } + + // We need to refactor this to support mixed content when there are both + // banner and video bid requests + let openrtbRequest; + if (hasBannerMediaType(validBidRequests[0])) { + openrtbRequest = buildBannerRequestData(validBidRequests, bidderRequest); + } else if (hasVideoMediaType(validBidRequests[0])) { + openrtbRequest = buildVideoRequestData(validBidRequests[0], bidderRequest); + } + + // adding schain object + if (validBidRequests[0].schain) { + deepSetValue(openrtbRequest, 'source.ext.schain', validBidRequests[0].schain); + } + + // Attaching GDPR Consent Params + if (bidderRequest.gdprConsent) { + deepSetValue(openrtbRequest, 'user.ext.consent', bidderRequest.gdprConsent.consentString); + deepSetValue(openrtbRequest, 'regs.ext.gdpr', (bidderRequest.gdprConsent.gdprApplies ? 1 : 0)); + } + + // CCPA + if (bidderRequest.uspConsent) { + deepSetValue(openrtbRequest, 'regs.ext.us_privacy', bidderRequest.uspConsent); + } + + // EIDS + const eids = deepAccess(validBidRequests[0], 'userIdAsEids'); + if (Array.isArray(eids) && eids.length > 0) { + deepSetValue(openrtbRequest, 'user.ext.eids', eids); + } + + let publisherId = validBidRequests[0].params.publisherId; + let placementId = validBidRequests[0].params.placementId; + const networkId = validBidRequests[0].params.networkId || DEFAULT_NETWORK_ID; + + if (validBidRequests[0].params.e2etest) { + logMessage('E2E test mode enabled'); + publisherId = 'e2etest' + } + let baseEndpoint = spec.ENDPOINT + '?pid=' + publisherId; + + if (placementId) { + baseEndpoint += '&placementId=' + placementId + } + if (networkId) { + baseEndpoint += '&nId=' + networkId + } + + const payloadString = JSON.stringify(openrtbRequest); + return { + method: 'POST', + url: baseEndpoint, + data: payloadString, + }; + }, + + interpretResponse: function (serverResponse) { + const bidResponses = []; + const response = (serverResponse || {}).body; + // response is always one seat (exchange) with (optional) bids for each impression + if (response && response.seatbid && response.seatbid.length === 1 && response.seatbid[0].bid && response.seatbid[0].bid.length) { + response.seatbid[0].bid.forEach(bid => { + if (bid.adm && bid.price) { + bidResponses.push(_createBidResponse(bid)); + } + }) + } else { + logInfo('kulturemedia.interpretResponse :: no valid responses to interpret'); + } + return bidResponses; + }, + + getUserSyncs: function (syncOptions, serverResponses) { + logInfo('kulturemedia.getUserSyncs', 'syncOptions', syncOptions, 'serverResponses', serverResponses); + let syncs = []; + + if (!syncOptions.iframeEnabled && !syncOptions.pixelEnabled) { + return syncs; + } + + serverResponses.forEach(resp => { + const userSync = deepAccess(resp, 'body.ext.usersync'); + if (userSync) { + let syncDetails = []; + Object.keys(userSync).forEach(key => { + const value = userSync[key]; + if (value.syncs && value.syncs.length) { + syncDetails = syncDetails.concat(value.syncs); + } + }); + syncDetails.forEach(syncDetails => { + syncs.push({ + type: syncDetails.type === 'iframe' ? 'iframe' : 'image', + url: syncDetails.url + }); + }); + + if (!syncOptions.iframeEnabled) { + syncs = syncs.filter(s => s.type !== 'iframe') + } + if (!syncOptions.pixelEnabled) { + syncs = syncs.filter(s => s.type !== 'image') + } + } + }); + logInfo('kulturemedia.getUserSyncs result=%o', syncs); + return syncs; + }, + +}; + +/* ======================================= + * Util Functions + *======================================= */ + +/** + * @param {BidRequest} bidRequest bid request + */ +function hasBannerMediaType(bidRequest) { + return !!deepAccess(bidRequest, 'mediaTypes.banner'); +} + +/** + * @param {BidRequest} bidRequest bid request + */ +function hasVideoMediaType(bidRequest) { + return !!deepAccess(bidRequest, 'mediaTypes.video'); +} + +function _validateParams(bidRequest) { + if (!bidRequest.params) { + return false; + } + + if (bidRequest.params.e2etest) { + return true; + } + + if (!bidRequest.params.publisherId) { + logError('Validation failed: publisherId not declared'); + return false; + } + + if (!bidRequest.params.placementId) { + logError('Validation failed: placementId not declared'); + return false; + } + + const mediaTypesExists = hasVideoMediaType(bidRequest) || hasBannerMediaType(bidRequest); + if (!mediaTypesExists) { + return false; + } + + return true; +} + +/** + * Validates banner bid request. If it is not banner media type returns true. + * @param {object} bid, bid to validate + * @return boolean, true if valid, otherwise false + */ +function _validateBanner(bidRequest) { + // If there's no banner no need to validate + if (!hasBannerMediaType(bidRequest)) { + return true; + } + const banner = deepAccess(bidRequest, 'mediaTypes.banner'); + if (!Array.isArray(banner.sizes)) { + return false; + } + + return true; +} + +/** + * Validates video bid request. If it is not video media type returns true. + * @param {object} bid, bid to validate + * @return boolean, true if valid, otherwise false + */ +function _validateVideo(bidRequest) { + // If there's no video no need to validate + if (!hasVideoMediaType(bidRequest)) { + return true; + } + + const videoPlacement = deepAccess(bidRequest, 'mediaTypes.video', {}); + const videoBidderParams = deepAccess(bidRequest, 'params.video', {}); + const params = deepAccess(bidRequest, 'params', {}); + + if (params && params.e2etest) { + return true; + } + + const videoParams = { + ...videoPlacement, + ...videoBidderParams // Bidder Specific overrides + }; + + if (!Array.isArray(videoParams.mimes) || videoParams.mimes.length === 0) { + logError('Validation failed: mimes are invalid'); + return false; + } + + if (!Array.isArray(videoParams.protocols) || videoParams.protocols.length === 0) { + logError('Validation failed: protocols are invalid'); + return false; + } + + if (!videoParams.context) { + logError('Validation failed: context id not declared'); + return false; + } + + if (videoParams.context !== 'instream') { + logError('Validation failed: only context instream is supported '); + return false; + } + + if (typeof videoParams.playerSize === 'undefined' || !Array.isArray(videoParams.playerSize) || !Array.isArray(videoParams.playerSize[0])) { + logError('Validation failed: player size not declared or is not in format [[w,h]]'); + return false; + } + + return true; +} + +/** + * Prepares video request data. + * + * @param bidRequest + * @param bidderRequest + * @returns openrtbRequest + */ +function buildVideoRequestData(bidRequest, bidderRequest) { + const {params} = bidRequest; + + const videoAdUnit = deepAccess(bidRequest, 'mediaTypes.video', {}); + const videoBidderParams = deepAccess(bidRequest, 'params.video', {}); + + const videoParams = { + ...videoAdUnit, + ...videoBidderParams // Bidder Specific overrides + }; + + if (bidRequest.params && bidRequest.params.e2etest) { + videoParams.playerSize = [[640, 480]] + videoParams.conext = 'instream' + } + + const video = { + w: parseInt(videoParams.playerSize[0][0], 10), + h: parseInt(videoParams.playerSize[0][1], 10), + } + + // Obtain all ORTB params related video from Ad Unit + OPENRTB_VIDEO_PARAMS.forEach((param) => { + if (videoParams.hasOwnProperty(param)) { + video[param] = videoParams[param]; + } + }); + + // Placement Inference Rules: + // - If no placement is defined then default to 1 (In Stream) + video.placement = video.placement || 2; + + // - If product is instream (for instream context) then override placement to 1 + if (params.context === 'instream') { + video.startdelay = video.startdelay || 0; + video.placement = 1; + } + + // bid floor + const bidFloorRequest = { + currency: bidRequest.params.cur || 'USD', + mediaType: 'video', + size: '*' + }; + let floorData = bidRequest.params + if (isFn(bidRequest.getFloor)) { + floorData = bidRequest.getFloor(bidFloorRequest); + } else { + if (params.bidfloor) { + floorData = {floor: params.bidfloor, currency: params.currency || 'USD'}; + } + } + + const openrtbRequest = { + id: bidRequest.bidId, + imp: [ + { + id: '1', + video: video, + secure: isSecure() ? 1 : 0, + bidfloor: floorData.floor, + bidfloorcur: floorData.currency + } + ], + site: { + domain: bidderRequest.refererInfo.domain, + page: bidderRequest.refererInfo.page, + ref: bidderRequest.refererInfo.ref, + }, + ext: { + hb: 1, + prebidver: '$prebid.version$', + adapterver: spec.VERSION, + }, + }; + + // content + if (videoParams.content && isPlainObject(videoParams.content)) { + openrtbRequest.site.content = {}; + const contentStringKeys = ['id', 'title', 'series', 'season', 'genre', 'contentrating', 'language', 'url']; + const contentNumberkeys = ['episode', 'prodq', 'context', 'livestream', 'len']; + const contentArrayKeys = ['cat']; + const contentObjectKeys = ['ext']; + for (const contentKey in videoBidderParams.content) { + if ( + (contentStringKeys.indexOf(contentKey) > -1 && isStr(videoParams.content[contentKey])) || + (contentNumberkeys.indexOf(contentKey) > -1 && isNumber(videoParams.content[contentKey])) || + (contentObjectKeys.indexOf(contentKey) > -1 && isPlainObject(videoParams.content[contentKey])) || + (contentArrayKeys.indexOf(contentKey) > -1 && isArray(videoParams.content[contentKey]) && + videoParams.content[contentKey].every(catStr => isStr(catStr)))) { + openrtbRequest.site.content[contentKey] = videoParams.content[contentKey]; + } else { + logMessage('KultureMedia bid adapter validation error: ', contentKey, ' is either not supported is OpenRTB V2.5 or value is undefined'); + } + } + } + + return openrtbRequest; +} + +/** + * Prepares video request data. + * + * @param bidRequest + * @param bidderRequest + * @returns openrtbRequest + */ +function buildBannerRequestData(bidRequests, bidderRequest) { + const impr = bidRequests.map(bidRequest => ({ + id: bidRequest.bidId, + banner: { + format: bidRequest.mediaTypes.banner.sizes.map(sizeArr => ({ + w: sizeArr[0], + h: sizeArr[1] + })) + }, + ext: { + exchange: { + placementId: bidRequest.params.placementId + } + } + })); + + const openrtbRequest = { + id: bidderRequest.bidderRequestId, + imp: impr, + site: { + domain: bidderRequest.refererInfo?.domain, + page: bidderRequest.refererInfo?.page, + ref: bidderRequest.refererInfo?.ref, + }, + ext: {} + }; + return openrtbRequest; +} + +function _createBidResponse(bid) { + const isADomainPresent = + bid.adomain && bid.adomain.length; + const bidResponse = { + requestId: bid.impid, + cpm: bid.price, + width: bid.w, + height: bid.h, + ad: bid.adm, + ttl: typeof bid.exp === 'number' ? bid.exp : DEFAULT_BID_TTL, + creativeId: bid.crid, + netRevenue: DEFAULT_NET_REVENUE, + currency: DEFAULT_CURRENCY, + mediaType: deepAccess(bid, 'ext.prebid.type', BANNER) + } + + if (isADomainPresent) { + bidResponse.meta = { + advertiserDomains: bid.adomain + }; + } + + if (bidResponse.mediaType === VIDEO) { + bidResponse.vastXml = bid.adm; + } + + return bidResponse; +} + +function isSecure() { + return document.location.protocol === 'https:'; +} + +registerBidder(spec); diff --git a/modules/dxkultureBidAdapter.md b/modules/kulturemediaBidAdapter.md similarity index 86% rename from modules/dxkultureBidAdapter.md rename to modules/kulturemediaBidAdapter.md index e31794ef6c6..0bd17e97982 100644 --- a/modules/dxkultureBidAdapter.md +++ b/modules/kulturemediaBidAdapter.md @@ -1,15 +1,15 @@ # Overview ``` -Module Name: DXKulture Bid Adapter +Module Name: Kulture Media Bid Adapter Module Type: Bidder Adapter Maintainer: devops@kulture.media ``` # Description -Module that connects to DXKulture's demand sources. -DXKulture bid adapter supports Banner and Video. +Module that connects to Kulture Media's demand sources. +Kulture Media bid adapter supports Banner and Video. # Test Parameters @@ -26,12 +26,10 @@ var adUnits = [ } }, bids: [{ - bidder: 'dxkulture', + bidder: 'kulturemedia', params: { placementId: 'test', publisherId: 'test', - bidfloor: 2.7, - bidfloorcur: 'USD' } }] } @@ -44,7 +42,7 @@ We support the following OpenRTB params that can be specified in `mediaTypes.vid - 'mimes', - 'minduration', - 'maxduration', -- 'plcmt', +- 'placement', - 'protocols', - 'startdelay', - 'skip', @@ -75,13 +73,13 @@ We support the following OpenRTB params that can be specified in `mediaTypes.vid delivery: [2], minduration: 10, maxduration: 30, - plcmt: 1, + placement: 1, playbackmethod: [1,5], } }, bids: [ { - bidder: 'dxkulture', + bidder: 'kulturemedia', params: { bidfloor: 0.5, publisherId: '12345', @@ -107,7 +105,7 @@ var adUnits = [ } }, bids: [{ - bidder: 'dxkulture', + bidder: 'kulturemedia', params: { e2etest: true } @@ -131,7 +129,7 @@ var adUnits = [ }, bids: [ { - bidder: 'dxkulture', + bidder: 'kulturemedia', params: { e2etest: true } diff --git a/modules/lemmaDigitalBidAdapter.js b/modules/lemmaDigitalBidAdapter.js index dde7c25d9b9..9fa3081a47e 100644 --- a/modules/lemmaDigitalBidAdapter.js +++ b/modules/lemmaDigitalBidAdapter.js @@ -3,15 +3,6 @@ import { config } from '../src/config.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse - * @typedef {import('../src/adapters/bidderFactory.js').SyncOptions} SyncOptions - * @typedef {import('../src/adapters/bidderFactory.js').UserSync} UserSync - * @typedef {import('../src/adapters/bidderFactory.js').validBidRequests} validBidRequests - */ - var BIDDER_CODE = 'lemmadigital'; var LOG_WARN_PREFIX = 'LEMMADIGITAL: '; var ENDPOINT = 'https://bid.lemmadigital.com/lemma/servad'; @@ -35,7 +26,7 @@ export var spec = { * * @param {BidRequest} bid The bid params to validate. * @return boolean True if this is a valid bid, and false otherwise. - */ + **/ isBidRequestValid: (bid) => { if (!bid || !bid.params) { utils.logError(LOG_WARN_PREFIX, 'nil/empty bid object'); @@ -60,11 +51,11 @@ export var spec = { }, /** - * Make a server request from the list of BidRequests. - * - * @param {validBidRequests[]} - an array of bids - * @return ServerRequest Info describing the request to the server. - */ + * Make a server request from the list of BidRequests. + * + * @param {validBidRequests[]} - an array of bids + * @return ServerRequest Info describing the request to the server. + **/ buildRequests: (validBidRequests, bidderRequest) => { if (validBidRequests.length === 0) { return; @@ -88,11 +79,11 @@ export var spec = { }, /** - * Unpack the response from the server into a list of bids. - * - * @param {ServerResponse} response A successful response from the server. - * @return {Bid[]} An array of bids which were nested inside the server. - */ + * Unpack the response from the server into a list of bids. + * + * @param {ServerResponse} response A successful response from the server. + * @return {Bid[]} An array of bids which were nested inside the server. + **/ interpretResponse: (response, request) => { return spec._parseRTBResponse(request, response.body); }, @@ -102,7 +93,7 @@ export var spec = { * @param {SyncOptions} syncOptions Which user syncs are allowed? * @param {ServerResponse[]} serverResponses List of server's responses. * @return {UserSync[]} The user syncs which should be dropped. - */ + **/ getUserSyncs: (syncOptions, serverResponses) => { let syncurl = USER_SYNC + 'pid=' + pubId; if (syncOptions.iframeEnabled) { @@ -124,7 +115,7 @@ export var spec = { /** * parse object - */ + **/ _parseJSON: function (rawPayload) { try { if (rawPayload) { @@ -164,7 +155,7 @@ export var spec = { /** * create IAB standard OpenRTB bid request - */ + **/ _createoRTBRequest: (bidRequests, conf) => { var oRTBObject = {}; try { @@ -211,7 +202,7 @@ export var spec = { /** * create impression array objects - */ + **/ _getImpressionArray: (request) => { var impArray = []; var map = request.map(bid => spec._getImpressionObject(bid)); @@ -227,7 +218,7 @@ export var spec = { /** * create impression (single) object - */ + **/ _getImpressionObject: (bid) => { var impression = {}; var bObj; @@ -286,8 +277,8 @@ export var spec = { }, /** - * set bid floor - */ + * set bid floor + **/ _setFloor: (impObj, bid) => { let bidFloor = -1; // get lowest floor from floorModule @@ -313,8 +304,8 @@ export var spec = { }, /** - * parse Open RTB response - */ + * parse Open RTB response + **/ _parseRTBResponse: (request, response) => { var bidResponses = []; try { @@ -367,8 +358,8 @@ export var spec = { }, /** - * get bid request api end point url - */ + * get bid request api end point url + **/ _endPointURL: (request) => { var params = request && request[0].params ? request[0].params : null; if (params) { @@ -380,8 +371,8 @@ export var spec = { }, /** - * get domain name from url - */ + * get domain name from url + **/ _getDomain: (url) => { var a = document.createElement('a'); a.setAttribute('href', url); @@ -389,8 +380,8 @@ export var spec = { }, /** - * create the site object - */ + * create the site object + **/ _getSiteObject: (request, conf) => { var params = request && request.params ? request.params : null; if (params) { @@ -415,8 +406,8 @@ export var spec = { }, /** - * create the app object - */ + * create the app object + **/ _getAppObject: (request) => { var params = request && request.params ? request.params : null; if (params) { @@ -441,8 +432,8 @@ export var spec = { }, /** - * create the device object - */ + * create the device object + **/ _getDeviceObject: (request) => { var params = request && request.params ? request.params : null; if (params) { @@ -490,8 +481,8 @@ export var spec = { }, /** - * get request ad sizes - */ + * get request ad sizes + **/ _getSizes: (request) => { if (request && request.sizes && utils.isArray(request.sizes[0]) && request.sizes[0].length > 0) { return request.sizes[0]; @@ -500,8 +491,8 @@ export var spec = { }, /** - * create the banner object - */ + * create the banner object + **/ _getBannerRequest: (bid) => { var bObj; var adFormat = []; @@ -540,8 +531,8 @@ export var spec = { }, /** - * create the video object - */ + * create the video object + **/ _getVideoRequest: (bid) => { var vObj; if (utils.deepAccess(bid, 'mediaTypes.video')) { @@ -563,8 +554,8 @@ export var spec = { }, /** - * check media type - */ + * check media type + **/ _checkMediaType: (adm, newBid) => { // Create a regex here to check the strings var videoRegex = new RegExp(/VAST.*version/); diff --git a/modules/lifestreetBidAdapter.js b/modules/lifestreetBidAdapter.js index 5b5eb639fcf..6a8b783ce21 100644 --- a/modules/lifestreetBidAdapter.js +++ b/modules/lifestreetBidAdapter.js @@ -2,10 +2,6 @@ import { isInteger } 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 - */ - const BIDDER_CODE = 'lifestreet'; const ADAPTER_VERSION = '$prebid.version$'; diff --git a/modules/limelightDigitalBidAdapter.js b/modules/limelightDigitalBidAdapter.js index acc76014abe..0eb9e900160 100644 --- a/modules/limelightDigitalBidAdapter.js +++ b/modules/limelightDigitalBidAdapter.js @@ -3,12 +3,6 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; import { ajax } from '../src/ajax.js'; -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse - */ - const BIDDER_CODE = 'limelightDigital'; /** diff --git a/modules/liveIntentIdSystem.js b/modules/liveIntentIdSystem.js index ad71a584e42..3157a4c155e 100644 --- a/modules/liveIntentIdSystem.js +++ b/modules/liveIntentIdSystem.js @@ -8,21 +8,12 @@ import { triggerPixel, logError } from '../src/utils.js'; import { ajaxBuilder } from '../src/ajax.js'; import { submodule } from '../src/hook.js'; import { LiveConnect } from 'live-connect-js'; // eslint-disable-line prebid/validate-imports -import { gdprDataHandler, uspDataHandler, gppDataHandler } from '../src/adapterManager.js'; +import { gdprDataHandler, uspDataHandler } from '../src/adapterManager.js'; import {getStorageManager} from '../src/storageManager.js'; import {MODULE_TYPE_UID} from '../src/activities/modules.js'; -import {UID2_EIDS} from '../libraries/uid2Eids/uid2Eids.js'; -/** - * @typedef {import('../modules/userId/index.js').Submodule} Submodule - * @typedef {import('../modules/userId/index.js').SubmoduleConfig} SubmoduleConfig - * @typedef {import('../modules/userId/index.js').IdResponse} IdResponse - */ - -const DEFAULT_AJAX_TIMEOUT = 5000 const EVENTS_TOPIC = 'pre_lips' const MODULE_NAME = 'liveIntentId'; -const LI_PROVIDER_DOMAIN = 'liveintent.com'; export const storage = getStorageManager({moduleType: MODULE_TYPE_UID, moduleName: MODULE_NAME}); const defaultRequestedAttributes = {'nonId': true} const calls = { @@ -73,7 +64,6 @@ function parseLiveIntentCollectorConfig(collectConfig) { collectConfig.fpiStorageStrategy && (config.storageStrategy = collectConfig.fpiStorageStrategy); collectConfig.fpiExpirationDays && (config.expirationDays = collectConfig.fpiExpirationDays); collectConfig.collectorUrl && (config.collectorUrl = collectConfig.collectorUrl); - config.ajaxTimeout = collectConfig.ajaxTimeout || DEFAULT_AJAX_TIMEOUT; return config; } @@ -108,8 +98,9 @@ function initializeLiveConnect(configParams) { if (configParams.url) { identityResolutionConfig.url = configParams.url } - - identityResolutionConfig.ajaxTimeout = configParams.ajaxTimeout || DEFAULT_AJAX_TIMEOUT; + if (configParams.ajaxTimeout) { + identityResolutionConfig.ajaxTimeout = configParams.ajaxTimeout; + } const liveConnectConfig = parseLiveIntentCollectorConfig(configParams.liCollectConfig); @@ -121,7 +112,6 @@ function initializeLiveConnect(configParams) { } liveConnectConfig.wrapperName = 'prebid'; - liveConnectConfig.trackerVersion = '$prebid.version$'; liveConnectConfig.identityResolutionConfig = identityResolutionConfig; liveConnectConfig.identifiersToResolve = configParams.identifiersToResolve || []; liveConnectConfig.fireEventDelay = configParams.fireEventDelay; @@ -134,11 +124,7 @@ function initializeLiveConnect(configParams) { liveConnectConfig.gdprApplies = gdprConsent.gdprApplies; liveConnectConfig.gdprConsent = gdprConsent.consentString; } - const gppConsent = gppDataHandler.getConsentData(); - if (gppConsent) { - liveConnectConfig.gppString = gppConsent.gppString; - liveConnectConfig.gppApplicableSections = gppConsent.applicableSections; - } + // The second param is the storage object, LS & Cookie manipulation uses PBJS // The third param is the ajax and pixel object, the ajax and pixel use PBJS liveConnect = liveIntentIdSubmodule.getInitializer()(liveConnectConfig, storage, calls); @@ -163,7 +149,7 @@ function tryFireEvent() { /** @type {Submodule} */ export const liveIntentIdSubmodule = { - moduleMode: '$$LIVE_INTENT_MODULE_MODE$$', + moduleMode: process.env.LiveConnectMode, /** * used to link submodule with config * @type {string} @@ -203,35 +189,15 @@ export const liveIntentIdSubmodule = { // As adapters are applied in lexicographical order, we will always // be overwritten by the 'proper' uid2 module if it is present. if (value.uid2) { - result.uid2 = { 'id': value.uid2, ext: { provider: LI_PROVIDER_DOMAIN } } + result.uid2 = { 'id': value.uid2 } } if (value.bidswitch) { - result.bidswitch = { 'id': value.bidswitch, ext: { provider: LI_PROVIDER_DOMAIN } } + result.bidswitch = { 'id': value.bidswitch } } if (value.medianet) { - result.medianet = { 'id': value.medianet, ext: { provider: LI_PROVIDER_DOMAIN } } - } - - if (value.magnite) { - result.magnite = { 'id': value.magnite, ext: { provider: LI_PROVIDER_DOMAIN } } - } - - if (value.index) { - result.index = { 'id': value.index, ext: { provider: LI_PROVIDER_DOMAIN } } - } - - if (value.openx) { - result.openx = { 'id': value.openx, ext: { provider: LI_PROVIDER_DOMAIN } } - } - - if (value.pubmatic) { - result.pubmatic = { 'id': value.pubmatic, ext: { provider: LI_PROVIDER_DOMAIN } } - } - - if (value.sovrn) { - result.sovrn = { 'id': value.sovrn, ext: { provider: LI_PROVIDER_DOMAIN } } + result.medianet = { 'id': value.medianet } } return result @@ -271,107 +237,6 @@ export const liveIntentIdSubmodule = { } return { callback: result }; - }, - eids: { - ...UID2_EIDS, - 'lipb': { - getValue: function(data) { - return data.lipbid; - }, - source: 'liveintent.com', - atype: 3, - getEidExt: function(data) { - if (Array.isArray(data.segments) && data.segments.length) { - return { - segments: data.segments - }; - } - } - }, - 'bidswitch': { - source: 'bidswitch.net', - atype: 3, - getValue: function(data) { - return data.id; - }, - getUidExt: function(data) { - if (data.ext) { - return data.ext; - } - } - }, - 'medianet': { - source: 'media.net', - atype: 3, - getValue: function(data) { - return data.id; - }, - getUidExt: function(data) { - if (data.ext) { - return data.ext; - } - } - }, - 'magnite': { - source: 'rubiconproject.com', - atype: 3, - getValue: function(data) { - return data.id; - }, - getUidExt: function(data) { - if (data.ext) { - return data.ext; - } - } - }, - 'index': { - source: 'liveintent.indexexchange.com', - atype: 3, - getValue: function(data) { - return data.id; - }, - getUidExt: function(data) { - if (data.ext) { - return data.ext; - } - } - }, - 'openx': { - source: 'openx.net', - atype: 3, - getValue: function(data) { - return data.id; - }, - getUidExt: function(data) { - if (data.ext) { - return data.ext; - } - } - }, - 'pubmatic': { - source: 'pubmatic.com', - atype: 3, - getValue: function(data) { - return data.id; - }, - getUidExt: function(data) { - if (data.ext) { - return data.ext; - } - } - }, - 'sovrn': { - source: 'liveintent.sovrn.com', - atype: 3, - getValue: function(data) { - return data.id; - }, - getUidExt: function(data) { - if (data.ext) { - return data.ext; - } - } - } } }; diff --git a/modules/livewrappedAnalyticsAdapter.js b/modules/livewrappedAnalyticsAdapter.js index f3ee81cae7a..fe69220e123 100644 --- a/modules/livewrappedAnalyticsAdapter.js +++ b/modules/livewrappedAnalyticsAdapter.js @@ -168,7 +168,7 @@ livewrappedAnalyticsAdapter.sendEvents = function() { requests: sentRequests.sentRequests, responses: getResponses(sentRequests.gdpr, sentRequests.auctionIds), wins: getWins(sentRequests.gdpr, sentRequests.auctionIds), - timeouts: getTimeouts(sentRequests.gdpr, sentRequests.auctionIds), + timeouts: getTimeouts(sentRequests.auctionIds), bidAdUnits: getbidAdUnits(), rf: getAdRenderFailed(sentRequests.auctionIds), rcv: getAdblockerRecovered() @@ -237,9 +237,27 @@ function getResponses(gdpr, auctionIds) { if (bid.readyToSend && !(bid.sendStatus & RESPONSESENT) && !bid.timeout) { bid.sendStatus |= RESPONSESENT; - let response = getResponseObject(auction, bid, gdprPos, auctionIdPos); - - responses.push(response); + responses.push({ + timeStamp: auction.timeStamp, + adUnit: bid.adUnit, + adUnitId: bid.adUnitId, + bidder: bid.bidder, + width: bid.width, + height: bid.height, + cpm: bid.cpm, + orgCpm: bid.originalCpm, + ttr: bid.ttr, + IsBid: bid.isBid, + mediaType: bid.mediaType, + gdpr: gdprPos, + floor: bid.lwFloor ? bid.lwFloor : (bid.floorData ? bid.floorData.floorValue : undefined), + floorCur: bid.floorData ? bid.floorData.floorCurrency : undefined, + auctionId: auctionIdPos, + auc: bid.auc, + buc: bid.buc, + lw: bid.lw, + meta: bid.meta + }); } }); }); @@ -319,45 +337,27 @@ function getAuctionIdPos(auctionIds, auctionId) { return auctionIdPos; } -function getResponseObject(auction, bid, gdprPos, auctionIdPos) { - return { - timeStamp: auction.timeStamp, - adUnit: bid.adUnit, - adUnitId: bid.adUnitId, - bidder: bid.bidder, - width: bid.width, - height: bid.height, - cpm: bid.cpm, - orgCpm: bid.originalCpm, - ttr: bid.ttr, - IsBid: bid.isBid, - mediaType: bid.mediaType, - gdpr: gdprPos, - floor: bid.lwFloor ? bid.lwFloor : (bid.floorData ? bid.floorData.floorValue : undefined), - floorCur: bid.floorData ? bid.floorData.floorCurrency : undefined, - auctionId: auctionIdPos, - auc: bid.auc, - buc: bid.buc, - lw: bid.lw, - meta: bid.meta - }; -} - -function getTimeouts(gdpr, auctionIds) { +function getTimeouts(auctionIds) { var timeouts = []; Object.keys(cache.auctions).forEach(auctionId => { let auctionIdPos = getAuctionIdPos(auctionIds, auctionId); Object.keys(cache.auctions[auctionId].bids).forEach(bidId => { let auction = cache.auctions[auctionId]; - let gdprPos = getGdprPos(gdpr, auction); let bid = auction.bids[bidId]; if (!(bid.sendStatus & TIMEOUTSENT) && bid.timeout) { bid.sendStatus |= TIMEOUTSENT; - let timeout = getResponseObject(auction, bid, gdprPos, auctionIdPos); - - timeouts.push(timeout); + timeouts.push({ + bidder: bid.bidder, + adUnit: bid.adUnit, + adUnitId: bid.adUnitId, + timeStamp: auction.timeStamp, + auctionId: auctionIdPos, + auc: bid.auc, + buc: bid.buc, + lw: bid.lw + }); } }); }); diff --git a/modules/livewrappedBidAdapter.js b/modules/livewrappedBidAdapter.js index cf1e690d862..82affe40e03 100644 --- a/modules/livewrappedBidAdapter.js +++ b/modules/livewrappedBidAdapter.js @@ -6,11 +6,6 @@ import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js'; import {getStorageManager} from '../src/storageManager.js'; import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - */ - const BIDDER_CODE = 'livewrapped'; export const storage = getStorageManager({bidderCode: BIDDER_CODE}); export const URL = 'https://lwadm.com/ad'; diff --git a/modules/lm_kiviadsBidAdapter.js b/modules/lm_kiviadsBidAdapter.js deleted file mode 100644 index 7c3085047c4..00000000000 --- a/modules/lm_kiviadsBidAdapter.js +++ /dev/null @@ -1,215 +0,0 @@ -import {config} from '../src/config.js'; -import {BANNER, VIDEO} from '../src/mediaTypes.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {parseSizesInput, isFn, deepAccess, getBidIdParameter, logError, isArray} from '../src/utils.js'; -import {getAdUnitSizes} from '../libraries/sizeUtils/sizeUtils.js'; - -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse - * @typedef {import('../src/adapters/bidderFactory.js').SyncOptions} SyncOptions - * @typedef {import('../src/adapters/bidderFactory.js').UserSync} UserSync - */ - -const CUR = 'USD'; -const BIDDER_CODE = 'lm_kiviads'; -const ENDPOINT = 'https://pbjs.kiviads.live'; - -/** - * Determines whether or not the given bid request is valid. - * - * @param {BidRequest} bid The bid params to validate. - * @return boolean True if this is a valid bid, and false otherwise. - */ -function isBidRequestValid(req) { - if (req && typeof req.params !== 'object') { - logError('Params is not defined or is incorrect in the bidder settings'); - return false; - } - - if (!getBidIdParameter('env', req.params) || !getBidIdParameter('pid', req.params)) { - logError('Env or pid is not present in bidder params'); - return false; - } - - if (deepAccess(req, 'mediaTypes.video') && !isArray(deepAccess(req, 'mediaTypes.video.playerSize'))) { - logError('mediaTypes.video.playerSize is required for video'); - return false; - } - - return true; -} - -/** - * Make a server request from the list of BidRequests. - * - * @param {validBidRequest?pbjs_debug=trues[]} - an array of bids - * @return ServerRequest Info describing the request to the server. - */ -function buildRequests(validBidRequests, bidderRequest) { - const {refererInfo = {}, gdprConsent = {}, uspConsent} = bidderRequest; - const requests = validBidRequests.map(req => { - const request = {}; - request.bidId = req.bidId; - request.banner = deepAccess(req, 'mediaTypes.banner'); - request.auctionId = req.ortb2?.source?.tid; - request.transactionId = req.ortb2Imp?.ext?.tid; - request.sizes = parseSizesInput(getAdUnitSizes(req)); - request.schain = req.schain; - request.location = { - page: refererInfo.page, - location: refererInfo.location, - domain: refererInfo.domain, - whost: window.location.host, - ref: refererInfo.ref, - isAmp: refererInfo.isAmp - }; - request.device = { - ua: navigator.userAgent, - lang: navigator.language - }; - request.env = { - env: req.params.env, - pid: req.params.pid - }; - request.ortb2 = req.ortb2; - request.ortb2Imp = req.ortb2Imp; - request.tz = new Date().getTimezoneOffset(); - request.ext = req.params.ext; - request.bc = req.bidRequestsCount; - request.floor = getBidFloor(req); - - if (req.userIdAsEids && req.userIdAsEids.length !== 0) { - request.userEids = req.userIdAsEids; - } else { - request.userEids = []; - } - if (gdprConsent.gdprApplies) { - request.gdprApplies = Number(gdprConsent.gdprApplies); - request.consentString = gdprConsent.consentString; - } else { - request.gdprApplies = 0; - request.consentString = ''; - } - if (uspConsent) { - request.usPrivacy = uspConsent; - } else { - request.usPrivacy = ''; - } - if (config.getConfig('coppa')) { - request.coppa = 1; - } else { - request.coppa = 0; - } - - const video = deepAccess(req, 'mediaTypes.video'); - if (video) { - request.sizes = parseSizesInput(deepAccess(req, 'mediaTypes.video.playerSize')); - request.video = video; - } - - return request; - }); - - return { - method: 'POST', - url: ENDPOINT + '/bid', - data: JSON.stringify(requests), - withCredentials: true, - bidderRequest, - options: { - contentType: 'application/json', - } - }; -} - -/** - * Unpack the 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. - */ -function interpretResponse(serverResponse, {bidderRequest}) { - const response = []; - if (!isArray(deepAccess(serverResponse, 'body.data'))) { - return response; - } - - serverResponse.body.data.forEach(serverBid => { - const bid = { - requestId: bidderRequest.bidId, - dealId: bidderRequest.dealId || null, - ...serverBid - }; - response.push(bid); - }); - - return response; -} - -/** - * Register the user sync pixels which should be dropped after the auction. - * - * @param {SyncOptions} syncOptions Which user syncs are allowed? - * @param {ServerResponse[]} serverResponses List of server's responses. - * @return {UserSync[]} The user syncs which should be dropped. - */ -function getUserSyncs(syncOptions, serverResponses, gdprConsent = {}, uspConsent = '') { - const syncs = []; - const pixels = deepAccess(serverResponses, '0.body.data.0.ext.pixels'); - - if ((syncOptions.iframeEnabled || syncOptions.pixelEnabled) && isArray(pixels) && pixels.length !== 0) { - const gdprFlag = `&gdpr=${gdprConsent.gdprApplies ? 1 : 0}`; - const gdprString = `&gdpr_consent=${encodeURIComponent((gdprConsent.consentString || ''))}`; - const usPrivacy = `us_privacy=${encodeURIComponent(uspConsent)}`; - - pixels.forEach(pixel => { - const [type, url] = pixel; - const sync = {type, url: `${url}&${usPrivacy}${gdprFlag}${gdprString}`}; - if (type === 'iframe' && syncOptions.iframeEnabled) { - syncs.push(sync) - } else if (type === 'image' && syncOptions.pixelEnabled) { - syncs.push(sync) - } - }); - } - - return syncs; -} - -/** - * Get valid floor value from getFloor fuction. - * - * @param {Object} bid Current bid request. - * @return {null|Number} Returns floor value when bid.getFloor is function and returns valid floor object with USD currency, otherwise returns null. - */ -export function getBidFloor(bid) { - if (!isFn(bid.getFloor)) { - return null; - } - - let floor = bid.getFloor({ - currency: CUR, - mediaType: '*', - size: '*' - }); - - if (typeof floor === 'object' && !isNaN(floor.floor) && floor.currency === CUR) { - return floor.floor; - } - - return null; -} - -export const spec = { - code: BIDDER_CODE, - aliases: ['kivi'], - supportedMediaTypes: [BANNER, VIDEO], - isBidRequestValid, - buildRequests, - interpretResponse, - getUserSyncs -} - -registerBidder(spec); diff --git a/modules/lm_kiviadsBidAdapter.md b/modules/lm_kiviadsBidAdapter.md deleted file mode 100644 index fc1b05d1ef7..00000000000 --- a/modules/lm_kiviadsBidAdapter.md +++ /dev/null @@ -1,54 +0,0 @@ -# Overview - -``` -Module Name: lm_kiviads Bidder Adapter -Module Type: lm_kiviads Bidder Adapter -Maintainer: pavlo@xe.works -``` - -# Description - -Module that connects to kiviads.com demand sources - -# Test Parameters -``` -var adUnits = [ - { - code: 'test-banner', - mediaTypes: { - banner: { - sizes: [[300, 250]], - } - }, - bids: [ - { - bidder: 'lm_kiviads', - params: { - env: 'lm_kiviads', - pid: '40', - ext: {} - } - } - ] - }, - { - code: 'test-video', - sizes: [ [ 640, 480 ] ], - mediaTypes: { - video: { - playerSize: [640, 480], - context: 'instream', - skipppable: true - } - }, - bids: [{ - bidder: 'lm_kiviads', - params: { - env: 'lm_kiviads', - pid: '40', - ext: {} - } - }] - } -]; -``` diff --git a/modules/lockerdomeBidAdapter.js b/modules/lockerdomeBidAdapter.js index 5038eadce30..5c38753c1e2 100644 --- a/modules/lockerdomeBidAdapter.js +++ b/modules/lockerdomeBidAdapter.js @@ -1,6 +1,6 @@ +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/lotamePanoramaIdSystem.js b/modules/lotamePanoramaIdSystem.js index 64d631c2469..02b01b8bd9d 100644 --- a/modules/lotamePanoramaIdSystem.js +++ b/modules/lotamePanoramaIdSystem.js @@ -20,13 +20,6 @@ import {getStorageManager} from '../src/storageManager.js'; import { uspDataHandler } from '../src/adapterManager.js'; import {MODULE_TYPE_UID} from '../src/activities/modules.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 KEY_ID = 'panoramaId'; const KEY_EXPIRY = `${KEY_ID}_expiry`; const KEY_PROFILE = '_cc_id'; @@ -375,12 +368,6 @@ export const lotamePanoramaIdSubmodule = { return { callback: resolveIdFunction }; }, - eids: { - lotamePanoramaId: { - source: 'crwdcntrl.net', - atype: 1, - }, - }, }; submodule('userId', lotamePanoramaIdSubmodule); diff --git a/modules/madvertiseBidAdapter.js b/modules/madvertiseBidAdapter.js index 3b031623aef..457ff2409b8 100644 --- a/modules/madvertiseBidAdapter.js +++ b/modules/madvertiseBidAdapter.js @@ -2,11 +2,6 @@ import { parseSizesInput, _each } from '../src/utils.js'; import {config} from '../src/config.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - */ - // use protocol relative urls for http or https const MADVERTISE_ENDPOINT = 'https://mobile.mng-ads.com/'; diff --git a/modules/magniteAnalyticsAdapter.js b/modules/magniteAnalyticsAdapter.js index b9665b93494..b501e3bef5a 100644 --- a/modules/magniteAnalyticsAdapter.js +++ b/modules/magniteAnalyticsAdapter.js @@ -142,10 +142,6 @@ const sendEvent = payload => { ...getTopLevelDetails(), ...payload } - if (window.pbjs?.rp?.eventDispatcher) { - const analyticsEvent = new CustomEvent('beforeSendingMagniteAnalytics', { detail: event }); - window.pbjs.rp.eventDispatcher.dispatchEvent(analyticsEvent); - } ajax( endpoint, null, diff --git a/modules/malltvBidAdapter.js b/modules/malltvBidAdapter.js index 67c8a4aec07..5ac50936ed6 100644 --- a/modules/malltvBidAdapter.js +++ b/modules/malltvBidAdapter.js @@ -2,13 +2,6 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { getStorageManager } from '../src/storageManager.js'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse - * @typedef {import('../src/adapters/bidderFactory.js').validBidRequests} validBidRequests - */ - const BIDDER_CODE = 'malltv'; const ENDPOINT_URL = 'https://central.mall.tv/bid'; const DIMENSION_SEPARATOR = 'x'; @@ -131,11 +124,11 @@ export const spec = { }; /** - * Generate size param for bid request using sizes array - * - * @param {Array} sizes Possible sizes for the ad unit. - * @return {string} Processed sizes param to be used for the bid request. - */ +* Generate size param for bid request using sizes array +* +* @param {Array} sizes Possible sizes for the ad unit. +* @return {string} Processed sizes param to be used for the bid request. +*/ function generateSizeParam(sizes) { return sizes.map(size => size.join(DIMENSION_SEPARATOR)).join(SIZE_SEPARATOR); } diff --git a/modules/mediabramaBidAdapter.js b/modules/mediabramaBidAdapter.js deleted file mode 100644 index caf6854fe03..00000000000 --- a/modules/mediabramaBidAdapter.js +++ /dev/null @@ -1,155 +0,0 @@ -import { - isFn, - isStr, - deepAccess, - getWindowTop, - triggerPixel -} from '../src/utils.js'; -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { BANNER } from '../src/mediaTypes.js'; -import { config } from '../src/config.js'; - -const BIDDER_CODE = 'mediabrama'; -const AD_URL = 'https://prebid.mediabrama.com/pbjs'; -const SYNC_URL = 'https://prebid.mediabrama.com/sync'; - -function isBidResponseValid(bid) { - if (!bid.requestId || !bid.cpm || !bid.creativeId || - !bid.ttl || !bid.currency || !bid.meta) { - return false; - } - - switch (bid.mediaType) { - case BANNER: - return Boolean(bid.width && bid.height && bid.ad); - default: - return false; - } -} - -function getBidFloor(bid) { - if (!isFn(bid.getFloor)) { - return deepAccess(bid, 'params.bidFloor', 0); - } - - try { - const bidFloor = bid.getFloor({ - currency: 'USD', - mediaType: '*', - size: '*', - }); - return bidFloor.floor; - } catch (_) { - return 0 - } -} - -export const spec = { - code: BIDDER_CODE, - supportedMediaTypes: [BANNER], - - isBidRequestValid: (bid) => { - return Boolean(bid.bidId && bid.params && bid.params.placementId); - }, - - buildRequests: (validBidRequests = [], bidderRequest) => { - const winTop = getWindowTop(); - const location = winTop.location; - const placements = []; - - const request = { - deviceWidth: winTop.screen.width, - deviceHeight: winTop.screen.height, - language: (navigator && navigator.language) ? navigator.language.split('-')[0] : '', - host: location.host, - page: location.pathname, - placements: placements - }; - - if (bidderRequest) { - if (bidderRequest.uspConsent) { - request.ccpa = bidderRequest.uspConsent; - } - if (bidderRequest.gdprConsent) { - request.gdpr = bidderRequest.gdprConsent; - } - } - - const len = validBidRequests.length; - for (let i = 0; i < len; i++) { - const bid = validBidRequests[i]; - const placement = { - placementId: bid.params.placementId, - bidId: bid.bidId, - schain: bid.schain || {}, - bidfloor: getBidFloor(bid) - }; - - if (typeof bid.userId !== 'undefined') { - placement.userId = bid.userId; - } - - const mediaType = bid.mediaTypes; - - if (mediaType && mediaType[BANNER] && mediaType[BANNER].sizes) { - placement.sizes = mediaType[BANNER].sizes; - placement.adFormat = BANNER; - } - - placements.push(placement); - } - - return { - method: 'POST', - url: AD_URL, - data: request - }; - }, - - interpretResponse: (serverResponse) => { - let response = []; - for (let i = 0; i < serverResponse.body.length; i++) { - let resItem = serverResponse.body[i]; - if (isBidResponseValid(resItem)) { - const advertiserDomains = resItem.adomain && resItem.adomain.length ? resItem.adomain : []; - resItem.meta = { ...resItem.meta, advertiserDomains }; - - response.push(resItem); - } - } - return response; - }, - - getUserSyncs: (syncOptions, serverResponses, gdprConsent, uspConsent) => { - let syncType = syncOptions.iframeEnabled ? 'iframe' : 'image'; - let syncUrl = SYNC_URL + `/${syncType}?pbjs=1`; - if (gdprConsent && gdprConsent.consentString) { - if (typeof gdprConsent.gdprApplies === 'boolean') { - syncUrl += `&gdpr=${Number(gdprConsent.gdprApplies)}&gdpr_consent=${gdprConsent.consentString}`; - } else { - syncUrl += `&gdpr=0&gdpr_consent=${gdprConsent.consentString}`; - } - } - if (uspConsent && uspConsent.consentString) { - syncUrl += `&ccpa_consent=${uspConsent.consentString}`; - } - - const coppa = config.getConfig('coppa') ? 1 : 0; - syncUrl += `&coppa=${coppa}`; - - return [{ - type: syncType, - url: syncUrl - }]; - }, - - onBidWon: (bid) => { - const cpm = deepAccess(bid, 'adserverTargeting.hb_pb') || ''; - if (isStr(bid.nurl) && bid.nurl !== '') { - bid.nurl = bid.nurl.replace(/\${AUCTION_PRICE}/, cpm); - triggerPixel(bid.nurl); - } - } -}; - -registerBidder(spec); diff --git a/modules/mediabramaBidAdapter.md b/modules/mediabramaBidAdapter.md deleted file mode 100644 index fde0a399852..00000000000 --- a/modules/mediabramaBidAdapter.md +++ /dev/null @@ -1,33 +0,0 @@ -# Overview - -``` -Module Name: MediaBrama Bidder Adapter -Module Type: MediaBrama Bidder Adapter -Maintainer: support@mediabrama.com -``` - -# Description - -Module that connects to mediabrama demand sources - -# Test Parameters -``` - var adUnits = [ - { - code: 'div-prebid', - mediaTypes:{ - banner: { - sizes: [[300, 250]], - } - }, - bids:[ - { - bidder: 'mediabrama', - params: { - placementId: '24428' //test, please replace after test - } - } - ] - }, - ]; -``` diff --git a/modules/mediafilterRtdProvider.js b/modules/mediafilterRtdProvider.js deleted file mode 100644 index 8a082ad4d59..00000000000 --- a/modules/mediafilterRtdProvider.js +++ /dev/null @@ -1,94 +0,0 @@ -/** - * This module adds the Media Filter real-time ad monitoring and protection module. - * - * The {@link module:modules/realTimeData} module is required - * - * For more information, visit {@link https://www.themediatrust.com The Media Trust}. - * - * @author Mirnes Cajlakovic - * @module modules/mediafilterRtdProvider - * @requires module:modules/realTimeData - */ - -import { submodule } from '../src/hook.js'; -import { logError, generateUUID } from '../src/utils.js'; -import { loadExternalScript } from '../src/adloader.js'; -import * as events from '../src/events.js'; -import CONSTANTS from '../src/constants.json'; - -/** The event type for Media Filter. */ -export const MEDIAFILTER_EVENT_TYPE = 'com.mediatrust.pbjs.'; -/** The base URL for Media Filter scripts. */ -export const MEDIAFILTER_BASE_URL = 'https://scripts.webcontentassessor.com/scripts/'; - -export const MediaFilter = { - /** - * Registers the Media Filter as a submodule of real-time data. - */ - register: function() { - submodule('realTimeData', { - 'name': 'mediafilter', - 'init': this.generateInitHandler() - }); - }, - - /** - * Sets up the Media Filter by initializing event listeners and loading the external script. - * @param {object} configuration - The configuration object. - */ - setup: function(configuration) { - this.setupEventListener(configuration.configurationHash); - this.setupScript(configuration.configurationHash); - }, - - /** - * Sets up an event listener for Media Filter messages. - * @param {string} configurationHash - The configuration hash. - */ - setupEventListener: function(configurationHash) { - window.addEventListener('message', this.generateEventHandler(configurationHash)); - }, - - /** - * Loads the Media Filter script based on the provided configuration hash. - * @param {string} configurationHash - The configuration hash. - */ - setupScript: function(configurationHash) { - loadExternalScript(MEDIAFILTER_BASE_URL.concat(configurationHash), 'mediafilter', () => {}); - }, - - /** - * Generates an event handler for Media Filter messages. - * @param {string} configurationHash - The configuration hash. - * @returns {function} The generated event handler. - */ - generateEventHandler: function(configurationHash) { - return (windowEvent) => { - if (windowEvent.data.type === MEDIAFILTER_EVENT_TYPE.concat('.', configurationHash)) { - events.emit(CONSTANTS.EVENTS.BILLABLE_EVENT, { - 'billingId': generateUUID(), - 'configurationHash': configurationHash, - 'type': 'impression', - 'vendor': 'mediafilter', - }); - } - }; - }, - - /** - * Generates an initialization handler for Media Filter. - * @returns {function} The generated init handler. - */ - generateInitHandler: function() { - return (configuration) => { - try { - this.setup(configuration); - } catch (error) { - logError(`Error in initialization: ${error.message}`); - } - }; - } -}; - -// Register the module -MediaFilter.register(); diff --git a/modules/mediafilterRtdProvider.md b/modules/mediafilterRtdProvider.md deleted file mode 100644 index 469479f8d0b..00000000000 --- a/modules/mediafilterRtdProvider.md +++ /dev/null @@ -1,37 +0,0 @@ -## Overview - -**Module:** The Media Filter -**Type: **Real Time Data Module - -As malvertising, scams, and controversial and offensive ad content proliferate across the digital media ecosystem, publishers need advanced controls to both shield audiences from malware attacks and ensure quality site experience. With the market’s fastest and most comprehensive real-time ad quality tool, The Media Trust empowers publisher Ad/Revenue Operations teams to block a wide range of malware, high-risk ad platforms, heavy ads, ads with sensitive or objectionable content, and custom lists (e.g., competitors). Customizable replacement code calls for a new ad to ensure impressions are still monetized. - -[![IMAGE ALT TEXT](http://img.youtube.com/vi/VBHRiirge7s/0.jpg)](http://www.youtube.com/watch?v=VBHRiirge7s "Publishers' Ultimate Avenger: Media Filter") - -To start using this module, please contact [The Media Trust](https://mediatrust.com/how-we-help/media-filter/ "The Media Trust") to get a script and configuration hash for module configuration. - -## Integration - -1. Build Prebid bundle with The Media Filter module included. - -``` -gulp build --modules=mediafilterRtdProvider -``` - -2. Inlcude the bundled script in your application. - -## Configuration - -Add configuration entry to `realTimeData.dataProviders` for The Media Filter module. - -``` -pbjs.setConfig({ - realTimeData: { - dataProviders: [{ - name: 'mediafilter', - params: { - configurationHash: '', - } - }] - } -}); -``` diff --git a/modules/mediaforceBidAdapter.js b/modules/mediaforceBidAdapter.js index 9f899974721..3d33bbf8c12 100644 --- a/modules/mediaforceBidAdapter.js +++ b/modules/mediaforceBidAdapter.js @@ -3,12 +3,6 @@ import {registerBidder} from '../src/adapters/bidderFactory.js'; import {BANNER, NATIVE} from '../src/mediaTypes.js'; import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse - */ - const BIDDER_CODE = 'mediaforce'; const ENDPOINT_URL = 'https://rtb.mfadsrvr.com/header_bid'; const TEST_ENDPOINT_URL = 'https://rtb.mfadsrvr.com/header_bid?debug_key=abcdefghijklmnop'; diff --git a/modules/mediafuseBidAdapter.js b/modules/mediafuseBidAdapter.js index 5e7221583a8..98179c49e0d 100644 --- a/modules/mediafuseBidAdapter.js +++ b/modules/mediafuseBidAdapter.js @@ -1,8 +1,14 @@ import { + chunk, + convertCamelToUnderscore, + convertTypes, createTrackPixelHtml, deepAccess, deepClone, + fill, getBidRequest, + getMaxValueFromArray, + getMinValueFromArray, getParameterByName, isArray, isArrayOfNums, @@ -32,15 +38,7 @@ import { getANKewyordParamFromMaps, getANKeywordParam, transformBidderParamKeywords -} from '../libraries/appnexusUtils/anKeywords.js'; -import {convertCamelToUnderscore, fill} from '../libraries/appnexusUtils/anUtils.js'; -import {convertTypes} from '../libraries/transformParamsUtils/convertTypes.js'; -import {chunk} from '../libraries/chunk/chunk.js'; - -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - */ +} from '../libraries/appnexusKeywords/anKeywords.js'; const BIDDER_CODE = 'mediafuse'; const URL = 'https://ib.adnxs.com/ut/v3/prebid'; @@ -961,7 +959,7 @@ function createAdPodRequest(tags, adPodBid) { const { durationRangeSec, requireExactDuration } = adPodBid.mediaTypes.video; const numberOfPlacements = getAdPodPlacementNumber(adPodBid.mediaTypes.video); - const maxDuration = Math.max(...durationRangeSec); + const maxDuration = getMaxValueFromArray(durationRangeSec); const tagToDuplicate = tags.filter(tag => tag.uuid === adPodBid.bidId); let request = fill(...tagToDuplicate, numberOfPlacements); @@ -987,7 +985,7 @@ function createAdPodRequest(tags, adPodBid) { function getAdPodPlacementNumber(videoParams) { const { adPodDurationSec, durationRangeSec, requireExactDuration } = videoParams; - const minAllowedDuration = Math.min(...durationRangeSec); + const minAllowedDuration = getMinValueFromArray(durationRangeSec); const numberOfPlacements = Math.floor(adPodDurationSec / minAllowedDuration); return requireExactDuration diff --git a/modules/mediagoBidAdapter.js b/modules/mediagoBidAdapter.js index 8f687d30ff3..055fef3bb39 100644 --- a/modules/mediagoBidAdapter.js +++ b/modules/mediagoBidAdapter.js @@ -8,115 +8,39 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; // import { config } from '../src/config.js'; // import { isPubcidEnabled } from './pubCommonId.js'; -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse - * @typedef {import('../src/adapters/bidderFactory.js').mediaType} mediaType - */ - const BIDDER_CODE = 'mediago'; // const PROTOCOL = window.document.location.protocol; -const ENDPOINT_URL = 'https://gbid.mediago.io/api/bid?tn='; -// const COOKY_SYNC_URL = 'https://gtrace.mediago.io/ju/cs/eplist'; -const COOKY_SYNC_IFRAME_URL = 'https://cdn.mediago.io/js/cookieSync.html'; -export const THIRD_PARTY_COOKIE_ORIGIN = 'https://cdn.mediago.io'; - +const ENDPOINT_URL = + // ((PROTOCOL === 'https:') ? 'https' : 'http') + + 'https://rtb-us.mediago.io/api/bid?tn='; const TIME_TO_LIVE = 500; -const GVLID = 1020; // const ENDPOINT_URL = '/api/bid?tn='; -export const storage = getStorageManager({bidderCode: BIDDER_CODE}); +const storage = getStorageManager({bidderCode: BIDDER_CODE}); let globals = {}; let itemMaps = {}; /* ----- mguid:start ------ */ -export const COOKIE_KEY_MGUID = '__mguid_'; -const COOKIE_KEY_PMGUID = '__pmguid_'; -const COOKIE_RETENTION_TIME = 365 * 24 * 60 * 60 * 1000; // 1 year -let reqTimes = 0; -/** - * get page title - * @returns {string} - */ - -export function getPageTitle(win = window) { - try { - const ogTitle = win.top.document.querySelector('meta[property="og:title"]') - return win.top.document.title || (ogTitle && ogTitle.content) || ''; - } catch (e) { - const ogTitle = document.querySelector('meta[property="og:title"]') - return document.title || (ogTitle && ogTitle.content) || ''; - } -} - -/** - * get page description - * - * @returns {string} - */ -export function getPageDescription(win = window) { - let element; - - try { - element = win.top.document.querySelector('meta[name="description"]') || - win.top.document.querySelector('meta[property="og:description"]') - } catch (e) { - element = document.querySelector('meta[name="description"]') || - document.querySelector('meta[property="og:description"]') - } - - return (element && element.content) || ''; -} +const COOKIE_KEY_MGUID = '__mguid_'; /** - * get page keywords - * @returns {string} - */ -export function getPageKeywords(win = window) { - let element; - - try { - element = win.top.document.querySelector('meta[name="keywords"]'); - } catch (e) { - element = document.querySelector('meta[name="keywords"]'); - } - - return (element && element.content) || ''; -} - -/** - * get connection downlink - * @returns {number} - */ -export function getConnectionDownLink(win = window) { - const nav = win.navigator || {}; - return nav && nav.connection && nav.connection.downlink >= 0 ? nav.connection.downlink.toString() : undefined; -} - -/** - * get pmg uid - * čŽˇå–åšļį”Ÿæˆį”¨æˆˇįš„id - * + * čŽˇå–į”¨æˆˇid * @return {string} */ -export const getPmgUID = () => { - if (!storage.cookiesAreEnabled()) return; - - let pmgUid = storage.getCookie(COOKIE_KEY_PMGUID); - if (!pmgUid) { - pmgUid = utils.generateUUID(); - try { - storage.setCookie(COOKIE_KEY_PMGUID, pmgUid, getCurrentTimeToUTCString()); - } catch (e) {} +const getUserID = () => { + const i = storage.getCookie(COOKIE_KEY_MGUID); + + if (i === null) { + const uuid = utils.generateUUID(); + storage.setCookie(COOKIE_KEY_MGUID, uuid); + return uuid; } - return pmgUid; + return i; }; -/* ----- pmguid:end ------ */ +/* ----- mguid:end ------ */ /** * čŽˇå–ä¸€ä¸Ēå¯ščąĄįš„某ä¸Ēå€ŧīŧŒåĻ‚æžœæ˛Ąæœ‰åˆ™čŋ”回įŠē字įŦĻ串 - * * @param {Object} obj å¯ščąĄ * @param {...string} keys 锎名 * @return {any} @@ -148,7 +72,7 @@ function isMobileAndTablet() { '.+mobile|avantgo|bada/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)', '|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone', '|p(ixi|re)/|plucker|pocket|psp|series(4|6)0|symbian|treo|up.(browser|link)|vodafone|wap', - '|windows ce|xda|xiino|android|ipad|playbook|silk' + '|windows ce|xda|xiino|android|ipad|playbook|silk', ].join(''), 'i' ); @@ -172,7 +96,7 @@ function isMobileAndTablet() { '|tim-|t-mo|to(pl|sh)|ts(70|m-|m3|m5)|tx-9|up(.b|g1|si)|utst|', 'v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|-v)', '|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas-', - '|your|zeto|zte-' + '|your|zeto|zte-', ].join(''), 'i' ); @@ -214,7 +138,7 @@ function getBidFloor(bid) { const bidFloor = bid.getFloor({ currency: 'USD', mediaType: '*', - size: '*' + size: '*', }); return bidFloor.floor; } catch (_) { @@ -232,7 +156,11 @@ function transformSizes(requestSizes) { let sizes = []; let sizeObj = {}; - if (utils.isArray(requestSizes) && requestSizes.length === 2 && !utils.isArray(requestSizes[0])) { + if ( + utils.isArray(requestSizes) && + requestSizes.length === 2 && + !utils.isArray(requestSizes[0]) + ) { sizeObj.width = parseInt(requestSizes[0], 10); sizeObj.height = parseInt(requestSizes[1], 10); sizes.push(sizeObj); @@ -259,7 +187,7 @@ const mediagoAdSize = [ { w: 160, h: 600 }, { w: 320, h: 180 }, { w: 320, h: 100 }, - { w: 336, h: 280 } + { w: 336, h: 280 }, ]; /** @@ -279,32 +207,24 @@ function getItems(validBidRequests, bidderRequest) { // įĄŽčŽ¤å°ē寸是åĻįŦĻ合我äģŦčĻæą‚ for (let size of sizes) { - matchSize = mediagoAdSize.find(item => size.width === item.w && size.height === item.h); + matchSize = mediagoAdSize.find( + (item) => size.width === item.w && size.height === item.h + ); if (matchSize) { break; } } if (!matchSize) { - matchSize = sizes[0] ? { h: sizes[0].height || 0, w: sizes[0].width || 0 } : { h: 0, w: 0 }; + matchSize = sizes[0] + ? { h: sizes[0].height || 0, w: sizes[0].width || 0 } + : { h: 0, w: 0 }; } const bidFloor = getBidFloor(req); - const gpid = - utils.deepAccess(req, 'ortb2Imp.ext.gpid') || - utils.deepAccess(req, 'ortb2Imp.ext.data.pbadslot') || - utils.deepAccess(req, 'params.placementId', 0); - - const gdprConsent = {}; - if (bidderRequest && bidderRequest.gdprConsent) { - gdprConsent.consent = bidderRequest.gdprConsent.consentString; - gdprConsent.gdpr = bidderRequest.gdprConsent.gdprApplies ? 1 : 0; - // if (bidderRequest.gdprConsent.addtlConsent && bidderRequest.gdprConsent.addtlConsent.indexOf('~') !== -1) { - // let ac = bidderRequest.gdprConsent.addtlConsent; - // // pull only the ids from the string (after the ~) and convert them to an array of ints - // let acStr = ac.substring(ac.indexOf('~') + 1); - // gdpr_consent.addtl_consent = acStr.split('.').map(id => parseInt(id, 10)); - // } - } + // const gpid = + // utils.deepAccess(req, 'ortb2Imp.ext.gpid') || + // utils.deepAccess(req, 'ortb2Imp.ext.data.pbadslot') || + // utils.deepAccess(req, 'params.placementId', 0); // if (mediaTypes.native) {} // banneråšŋ告įąģ型 @@ -317,21 +237,15 @@ function getItems(validBidRequests, bidderRequest) { h: matchSize.h, w: matchSize.w, pos: 1, - format: sizes + format: sizes, }, ext: { - adUnitCode: req.adUnitCode, - referrer: getReferrer(req, bidderRequest), - ortb2Imp: utils.deepAccess(req, 'ortb2Imp'), // äŧ å…ĨåŽŒæ•´å¯ščąĄīŧŒåˆ†æžæ—Ĩåŋ—数捎 - gpid: gpid, // 加å…Ĩ后无æŗ•čŋ”回åšŋ告 - adslot: utils.deepAccess(req, 'ortb2Imp.ext.data.adserver.adslot', '', ''), - ...gdprConsent // gdpr + // gpid: gpid, // 加å…Ĩ后无æŗ•čŋ”回åšŋ告 }, - tagid: req.params && req.params.tagid }; itemMaps[id] = { req, - ret + ret, }; } @@ -340,31 +254,6 @@ function getItems(validBidRequests, bidderRequest) { return items; } -/** - * @param {BidRequest} bidRequest - * @param bidderRequest - * @returns {string} - */ -function getReferrer(bidRequest = {}, bidderRequest = {}) { - let pageUrl; - if (bidRequest.params && bidRequest.params.referrer) { - pageUrl = bidRequest.params.referrer; - } else { - pageUrl = utils.deepAccess(bidderRequest, 'refererInfo.page'); - } - return pageUrl; -} - -/** - * get current time to UTC string - * @returns utc string - */ -export function getCurrentTimeToUTCString() { - const date = new Date(); - date.setTime(date.getTime() + COOKIE_RETENTION_TIME); - return date.toUTCString(); -} - /** * čŽˇå–rtbč¯ˇæą‚å‚æ•° * @@ -377,14 +266,7 @@ function getParam(validBidRequests, bidderRequest) { const sharedid = utils.deepAccess(validBidRequests[0], 'userId.sharedid.id') || utils.deepAccess(validBidRequests[0], 'userId.pubcid'); - - const bidsUserIdAsEids = validBidRequests[0].userIdAsEids; - const bidsUserid = validBidRequests[0].userId; - const eids = bidsUserIdAsEids || bidsUserid; - const ppuid = bidsUserid && bidsUserid.pubProvidedId; - const content = utils.deepAccess(bidderRequest, 'ortb2.site.content'); - const cat = utils.deepAccess(bidderRequest, 'ortb2.site.cat'); - reqTimes += 1; + const eids = validBidRequests[0].userIdAsEids || validBidRequests[0].userId; let isMobile = isMobileAndTablet() ? 1 : 0; // input test status by Publisher. more frequently for test true req @@ -392,17 +274,14 @@ function getParam(validBidRequests, bidderRequest) { let auctionId = getProperty(bidderRequest, 'auctionId'); let items = getItems(validBidRequests, bidderRequest); - const domain = utils.deepAccess(bidderRequest, 'refererInfo.domain') || document.domain; + const domain = + utils.deepAccess(bidderRequest, 'refererInfo.domain') || document.domain; const location = utils.deepAccess(bidderRequest, 'refererInfo.location'); const page = utils.deepAccess(bidderRequest, 'refererInfo.page'); const referer = utils.deepAccess(bidderRequest, 'refererInfo.ref'); const timeout = bidderRequest.timeout || 2000; const firstPartyData = bidderRequest.ortb2; - const topWindow = window.top; - const title = getPageTitle(); - const desc = getPageDescription(); - const keywords = getPageKeywords(); if (items && items.length) { let c = { @@ -420,32 +299,14 @@ function getParam(validBidRequests, bidderRequest) { // 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 || '', ua: navigator.userAgent, - language: /en/.test(navigator.language) ? 'en' : navigator.language + language: /en/.test(navigator.language) ? 'en' : navigator.language, }, ext: { eids, - bidsUserIdAsEids, - bidsUserid, - ppuid, firstPartyData, - content, - cat, - reqTimes, - pmguid: getPmgUID(), - page: { - title: title ? title.slice(0, 100) : undefined, - desc: desc ? desc.slice(0, 300) : undefined, - keywords: keywords ? keywords.slice(0, 100) : undefined, - hLen: topWindow.history?.length || undefined, - }, - device: { - nbw: getConnectionDownLink(), - hc: topWindow.navigator?.hardwareConcurrency || undefined, - dm: topWindow.navigator?.deviceMemory || undefined, - } }, user: { - buyeruid: storage.getCookie(COOKIE_KEY_MGUID) || undefined, + buyeruid: getUserID(), id: sharedid || pubcid, }, eids, @@ -459,11 +320,11 @@ function getParam(validBidRequests, bidderRequest) { publisher: { // todo id: domain, - name: domain - } + name: domain, + }, }, imp: items, - tmax: timeout + tmax: timeout, }; return c; } else { @@ -473,7 +334,6 @@ function getParam(validBidRequests, bidderRequest) { export const spec = { code: BIDDER_CODE, - gvlid: GVLID, // aliases: ['ex'], // short code /** * Determines whether or not the given bid request is valid. @@ -511,6 +371,7 @@ export const spec = { /** * Unpack the 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. */ @@ -535,7 +396,7 @@ export const spec = { ttl: TIME_TO_LIVE, // referrer: REFERER, ad: getProperty(bid, 'adm'), - nurl: getProperty(bid, 'nurl') + nurl: getProperty(bid, 'nurl'), // adserverTargeting: { // granularityMultiplier: 0.1, // priceGranularity: 'pbHg', @@ -552,45 +413,6 @@ export const spec = { return bidResponses; }, - getUserSyncs: function (syncOptions, serverResponse, gdprConsent, uspConsent, gppConsent) { - const origin = encodeURIComponent(location.origin || `https://${location.host}`); - let syncParamUrl = `dm=${origin}`; - - if (gdprConsent && gdprConsent.consentString) { - if (typeof gdprConsent.gdprApplies === 'boolean') { - syncParamUrl += `&gdpr=${Number(gdprConsent.gdprApplies)}&gdpr_consent=${gdprConsent.consentString}`; - } else { - syncParamUrl += `&gdpr=0&gdpr_consent=${gdprConsent.consentString}`; - } - } - if (uspConsent && uspConsent.consentString) { - syncParamUrl += `&ccpa_consent=${uspConsent.consentString}`; - } - - if (syncOptions.iframeEnabled) { - window.addEventListener('message', function handler(event) { - if (!event.data || event.origin != THIRD_PARTY_COOKIE_ORIGIN) { - return; - } - - this.removeEventListener('message', handler); - - event.stopImmediatePropagation(); - - const response = event.data; - if (!response.optout && response.mguid) { - storage.setCookie(COOKIE_KEY_MGUID, response.mguid, getCurrentTimeToUTCString()); - } - }, true); - return [ - { - type: 'iframe', - url: `${COOKY_SYNC_IFRAME_URL}?${syncParamUrl}` - } - ]; - } - }, - /** * Register bidder specific code, which will execute if bidder timed out after an auction * @param {data} Containing timeout specific data @@ -610,7 +432,7 @@ export const spec = { if (bid['nurl']) { utils.triggerPixel(bid['nurl']); } - } + }, /** * Register bidder specific code, which will execute when the adserver targeting has been set for a bid from this bidder diff --git a/modules/mediakeysBidAdapter.js b/modules/mediakeysBidAdapter.js index f4967fed170..7af43a3c549 100644 --- a/modules/mediakeysBidAdapter.js +++ b/modules/mediakeysBidAdapter.js @@ -119,7 +119,7 @@ function getOS() { * * @param {*} bid a Prebid.js bid (request) object * @param {string} mediaType the mediaType or the wildcard '*' - * @param {string|Array} size the size array or the wildcard '*' + * @param {string|array} size the size array or the wildcard '*' * @returns {number|boolean} */ function getFloor(bid, mediaType, size = '*') { diff --git a/modules/medianetBidAdapter.js b/modules/medianetBidAdapter.js index 6a8a35dbfd4..b9e00f45df9 100644 --- a/modules/medianetBidAdapter.js +++ b/modules/medianetBidAdapter.js @@ -1,6 +1,7 @@ import { buildUrl, deepAccess, + getGptSlotInfoForAdUnitCode, getWindowTop, isArray, isEmpty, @@ -17,18 +18,9 @@ import {getRefererInfo} from '../src/refererDetection.js'; import {Renderer} from '../src/Renderer.js'; import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; import {getGlobal} from '../src/prebidGlobal.js'; -import {getGptSlotInfoForAdUnitCode} from '../libraries/gptUtils/gptUtils.js'; - -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - * @typedef {import('../src/adapters/bidderFactory.js').TimedOutBid} TimedOutBid - */ const BIDDER_CODE = 'medianet'; -const TRUSTEDSTACK_CODE = 'trustedstack'; const BID_URL = 'https://prebid.media.net/rtb/prebid'; -const TRUSTEDSTACK_URL = 'https://prebid.trustedstack.com/rtb/trustedstack'; const PLAYER_URL = 'https://prebid.media.net/video/bundle.js'; const SLOT_VISIBILITY = { NOT_DETERMINED: 0, @@ -57,7 +49,7 @@ mnData.urlData = { }; const aliases = [ - { code: TRUSTEDSTACK_CODE }, + { code: 'aax', gvlid: 720 }, ]; getGlobal().medianetGlobals = getGlobal().medianetGlobals || {}; @@ -331,9 +323,8 @@ function normalizeCoordinates(coordinates) { } } -function getBidderURL(bidderCode, cid) { - const url = (bidderCode === TRUSTEDSTACK_CODE) ? TRUSTEDSTACK_URL : BID_URL; - return url + '?cid=' + encodeURIComponent(cid); +function getBidderURL(cid) { + return BID_URL + '?cid=' + encodeURIComponent(cid); } function generatePayload(bidRequests, bidderRequests) { @@ -472,7 +463,7 @@ export const spec = { let payload = generatePayload(bidRequests, bidderRequests); return { method: 'POST', - url: getBidderURL(bidderRequests.bidderCode, payload.ext.customer_id), + url: getBidderURL(payload.ext.customer_id), data: JSON.stringify(payload) }; }, diff --git a/modules/mediasniperBidAdapter.js b/modules/mediasniperBidAdapter.js index 5cf0ceaba18..378a804487a 100644 --- a/modules/mediasniperBidAdapter.js +++ b/modules/mediasniperBidAdapter.js @@ -1,7 +1,8 @@ import { deepAccess, deepClone, - deepSetValue, getBidIdParameter, + deepSetValue, + getBidIdParameter, inIframe, isArray, isEmpty, @@ -241,7 +242,7 @@ function createImp(bid) { * * @param {*} bid a Prebid.js bid (request) object * @param {string} mediaType the mediaType or the wildcard '*' - * @param {string|Array} size the size array or the wildcard '*' + * @param {string|array} size the size array or the wildcard '*' * @returns {number|boolean} */ function getFloor(bid, mediaType, size = '*') { diff --git a/modules/mediasquareBidAdapter.js b/modules/mediasquareBidAdapter.js index 550c715e741..720063253de 100644 --- a/modules/mediasquareBidAdapter.js +++ b/modules/mediasquareBidAdapter.js @@ -6,15 +6,6 @@ import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; import {Renderer} from '../src/Renderer.js'; import { getRefererInfo } from '../src/refererDetection.js'; -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse - * @typedef {import('../src/adapters/bidderFactory.js').SyncOptions} SyncOptions - * @typedef {import('../src/adapters/bidderFactory.js').UserSync} UserSync - * @typedef {import('../src/adapters/bidderFactory.js').validBidRequests} validBidRequests - */ - const BIDDER_CODE = 'mediasquare'; const BIDDER_URL_PROD = 'https://pbs-front.mediasquare.fr/' const BIDDER_URL_TEST = 'https://bidder-test.mediasquare.fr/' @@ -29,20 +20,20 @@ export const spec = { aliases: ['msq'], // short code supportedMediaTypes: [BANNER, NATIVE, VIDEO], /** - * Determines whether or not the given bid request is valid. - * - * @param {BidRequest} bid The bid params to validate. - * @return boolean True if this is a valid bid, and false otherwise. - */ + * Determines whether or not the given bid request is valid. + * + * @param {BidRequest} bid The bid params to validate. + * @return boolean True if this is a valid bid, and false otherwise. + */ isBidRequestValid: function(bid) { return !!(bid.params.owner && bid.params.code); }, /** - * Make a server request from the list of BidRequests. - * - * @param {validBidRequests[]} - an array of bids - * @return ServerRequest Info describing the request to the server. - */ + * Make a server request from the list of BidRequests. + * + * @param {validBidRequests[]} - an array of bids + * @return ServerRequest Info describing the request to the server. + */ buildRequests: function(validBidRequests, bidderRequest) { // convert Native ORTB definition to old-style prebid native definition validBidRequests = convertOrtbRequestToProprietaryNative(validBidRequests); @@ -105,11 +96,11 @@ export const spec = { }; }, /** - * Unpack the 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. - */ + * Unpack the 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. + */ interpretResponse: function(serverResponse, bidRequest) { const serverBody = serverResponse.body; // const headerValue = serverResponse.headers.get('some-response-header'); @@ -129,17 +120,23 @@ export const spec = { netRevenue: value['net_revenue'], ttl: value['ttl'], ad: value['ad'], - mediasquare: {}, + mediasquare: { + 'bidder': value['bidder'], + 'code': value['code'] + }, meta: { 'advertiserDomains': value['adomain'] } }; - let paramsToSearchFor = ['bidder', 'code', 'match', 'hasConsent', 'context', 'increment', 'ova']; - paramsToSearchFor.forEach(param => { - if (param in value) { - bidResponse['mediasquare'][param] = value[param]; - } - }); + if ('context' in value) { + bidResponse['mediasquare']['context'] = value['context']; + } + if ('match' in value) { + bidResponse['mediasquare']['match'] = value['match']; + } + if ('hasConsent' in value) { + bidResponse['mediasquare']['hasConsent'] = value['hasConsent']; + } if ('native' in value) { bidResponse['native'] = value['native']; bidResponse['mediaType'] = 'native'; @@ -157,12 +154,12 @@ export const spec = { }, /** - * Register the user sync pixels which should be dropped after the auction. - * - * @param {SyncOptions} syncOptions Which user syncs are allowed? - * @param {ServerResponse[]} serverResponses List of server's responses. - * @return {UserSync[]} The user syncs which should be dropped. - */ + * Register the user sync pixels which should be dropped after the auction. + * + * @param {SyncOptions} syncOptions Which user syncs are allowed? + * @param {ServerResponse[]} serverResponses List of server's responses. + * @return {UserSync[]} The user syncs which should be dropped. + */ getUserSyncs: function(syncOptions, serverResponses, gdprConsent, uspConsent) { if (typeof serverResponses === 'object' && serverResponses != null && serverResponses.length > 0 && serverResponses[0].hasOwnProperty('body') && serverResponses[0].body.hasOwnProperty('cookies') && typeof serverResponses[0].body.cookies === 'object') { @@ -173,9 +170,9 @@ export const spec = { }, /** - * Register bidder specific code, which will execute if a bid from this bidder won the auction - * @param {Bid} The bid that won the auction - */ + * Register bidder specific code, which will execute if a bid from this bidder won the auction + * @param {Bid} The bid that won the auction + */ onBidWon: function(bid) { // fires a pixel to confirm a winning bid if (bid.hasOwnProperty('mediaType') && bid.mediaType == 'video') { @@ -183,26 +180,21 @@ export const spec = { } let params = { pbjs: '$prebid.version$', referer: encodeURIComponent(getRefererInfo().page || getRefererInfo().topmostLocation) }; let endpoint = document.location.search.match(/msq_test=true/) ? BIDDER_URL_TEST : BIDDER_URL_PROD; - let paramsToSearchFor = ['bidder', 'code', 'match', 'hasConsent', 'context', 'increment', 'ova']; + let paramsToSearchFor = ['bidder', 'code', 'match', 'hasConsent', 'context']; if (bid.hasOwnProperty('mediasquare')) { - paramsToSearchFor.forEach(param => { - if (bid['mediasquare'].hasOwnProperty(param)) { - params[param] = bid['mediasquare'][param]; - if (typeof params[param] == 'number') { - params[param] = params[param].toString(); - } + for (let i = 0; i < paramsToSearchFor.length; i++) { + if (bid['mediasquare'].hasOwnProperty(paramsToSearchFor[i])) { + params[paramsToSearchFor[i]] = bid['mediasquare'][paramsToSearchFor[i]]; } - }); + } }; paramsToSearchFor = ['cpm', 'size', 'mediaType', 'currency', 'creativeId', 'adUnitCode', 'timeToRespond', 'requestId', 'auctionId', 'originalCpm', 'originalCurrency']; - paramsToSearchFor.forEach(param => { - if (bid.hasOwnProperty(param)) { - params[param] = bid[param]; - if (typeof params[param] == 'number') { - params[param] = params[param].toString(); - } + for (let i = 0; i < paramsToSearchFor.length; i++) { + if (bid.hasOwnProperty(paramsToSearchFor[i])) { + params[paramsToSearchFor[i]] = bid[paramsToSearchFor[i]]; + if (typeof params[paramsToSearchFor[i]] == 'number') { params[paramsToSearchFor[i]] = params[paramsToSearchFor[i]].toString() } } - }); + } 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 3f3a90c3c49..c522d588970 100644 --- a/modules/merkleIdSystem.js +++ b/modules/merkleIdSystem.js @@ -11,13 +11,6 @@ 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 - * @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 = 'merkleId'; const ID_URL = 'https://prebid.sv.rkdms.com/identity/'; const DEFAULT_REFRESH = 7 * 3600; @@ -94,17 +87,17 @@ function generateId(configParams, configStorage) { /** @type {Submodule} */ export const merkleIdSubmodule = { /** - * used to link submodule with config - * @type {string} - */ + * used to link submodule with config + * @type {string} + */ name: MODULE_NAME, /** - * decode the stored id value for passing to bid requests - * @function - * @param {string} value - * @returns {{eids:arrayofields}} - */ + * decode the stored id value for passing to bid requests + * @function + * @param {string} value + * @returns {{eids:arrayofields}} + */ decode(value) { // Legacy support for a single id const id = (value && value.pam_id && typeof value.pam_id.id === 'string') ? value.pam_id : undefined; @@ -122,12 +115,12 @@ export const merkleIdSubmodule = { }, /** - * performs action to obtain id and return a value in the callback's response argument - * @function - * @param {SubmoduleConfig} [config] - * @param {ConsentData} [consentData] - * @returns {IdResponse|undefined} - */ + * performs action to obtain id and return a value in the callback's response argument + * @function + * @param {SubmoduleConfig} [config] + * @param {ConsentData} [consentData] + * @returns {IdResponse|undefined} + */ getId(config, consentData) { logInfo('User ID - merkleId generating id'); @@ -203,30 +196,6 @@ export const merkleIdSubmodule = { logInfo('User ID - merkleId not refreshed'); return {id: storedId}; - }, - eids: { - 'merkleId': { - atype: 3, - getSource: function(data) { - if (data?.ext?.ssp) { - return `${data.ext.ssp}.merkleinc.com` - } - return 'merkleinc.com' - }, - getValue: function(data) { - return data.id; - }, - getUidExt: function(data) { - if (data.keyID) { - return { - keyID: data.keyID - } - } - if (data.ext) { - return data.ext; - } - } - }, } }; diff --git a/modules/mgidBidAdapter.js b/modules/mgidBidAdapter.js index fb3990e97f1..8e889261e52 100644 --- a/modules/mgidBidAdapter.js +++ b/modules/mgidBidAdapter.js @@ -9,10 +9,11 @@ import { isEmpty, triggerPixel, logWarn, + getBidIdParameter, isFn, isNumber, isBoolean, - isInteger, deepSetValue, getBidIdParameter, + isInteger, deepSetValue, } from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {BANNER, NATIVE} from '../src/mediaTypes.js'; @@ -21,12 +22,6 @@ import { getStorageManager } from '../src/storageManager.js'; import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; import {USERSYNC_DEFAULT_CONFIG} from '../src/userSync.js'; -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse - */ - const GVLID = 358; const DEFAULT_CUR = 'USD'; const BIDDER_CODE = 'mgid'; diff --git a/modules/mgidRtdProvider.js b/modules/mgidRtdProvider.js index 059be4e9103..fd2c0bbe6fd 100644 --- a/modules/mgidRtdProvider.js +++ b/modules/mgidRtdProvider.js @@ -5,10 +5,6 @@ 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 - */ - const MODULE_NAME = 'realTimeData'; const SUBMODULE_NAME = 'mgid'; const MGID_RTD_API_URL = 'https://servicer.mgid.com/sda'; diff --git a/modules/mgidXBidAdapter.js b/modules/mgidXBidAdapter.js index 7e084977cf1..5789f0d8b95 100644 --- a/modules/mgidXBidAdapter.js +++ b/modules/mgidXBidAdapter.js @@ -166,15 +166,10 @@ export const spec = { placements, coppa: config.getConfig('coppa') === true ? 1 : 0, ccpa: bidderRequest.uspConsent || undefined, + gdpr: bidderRequest.gdprConsent || undefined, tmax: config.getConfig('bidderTimeout') }; - if (bidderRequest.gdprConsent) { - request.gdpr = { - consentString: bidderRequest.gdprConsent.consentString - }; - } - const len = validBidRequests.length; for (let i = 0; i < len; i++) { const bid = validBidRequests[i]; diff --git a/modules/minutemediaBidAdapter.js b/modules/minutemediaBidAdapter.js index a4158e2abfa..85c1386957d 100644 --- a/modules/minutemediaBidAdapter.js +++ b/modules/minutemediaBidAdapter.js @@ -1,16 +1,4 @@ -import { - logWarn, - logInfo, - isArray, - isFn, - deepAccess, - isEmpty, - contains, - timestamp, - triggerPixel, - isInteger, - getBidIdParameter -} from '../src/utils.js'; +import { logWarn, logInfo, isArray, isFn, deepAccess, isEmpty, contains, timestamp, getBidIdParameter, triggerPixel, isInteger } from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {BANNER, VIDEO} from '../src/mediaTypes.js'; import {config} from '../src/config.js'; @@ -19,7 +7,7 @@ const SUPPORTED_AD_TYPES = [BANNER, VIDEO]; const BIDDER_CODE = 'minutemedia'; const ADAPTER_VERSION = '6.0.0'; const TTL = 360; -const DEFAULT_CURRENCY = 'USD'; +const CURRENCY = 'USD'; const SELLER_ENDPOINT = 'https://hb.minutemedia-prebid.com/'; const MODES = { PRODUCTION: 'hb-mm-multi', @@ -72,7 +60,7 @@ export const spec = { const bidResponse = { requestId: adUnit.requestId, cpm: adUnit.cpm, - currency: adUnit.currency || DEFAULT_CURRENCY, + currency: adUnit.currency || CURRENCY, width: adUnit.width, height: adUnit.height, ttl: adUnit.ttl || TTL, @@ -141,16 +129,16 @@ registerBidder(spec); * @param bid {bid} * @returns {Number} */ -function getFloor(bid, mediaType, currency) { +function getFloor(bid, mediaType) { if (!isFn(bid.getFloor)) { return 0; } let floorResult = bid.getFloor({ - currency: currency, + currency: CURRENCY, mediaType: mediaType, size: '*' }); - return floorResult.currency === currency && floorResult.floor ? floorResult.floor : 0; + return floorResult.currency === CURRENCY && floorResult.floor ? floorResult.floor : 0; } /** @@ -286,7 +274,6 @@ function generateBidParameters(bid, bidderRequest) { const {params} = bid; const mediaType = isBanner(bid) ? BANNER : VIDEO; const sizesArray = getSizesArray(bid, mediaType); - const currency = params.currency || config.getConfig('currency.adServerCurrency') || DEFAULT_CURRENCY; // fix floor price in case of NAN if (isNaN(params.floorPrice)) { @@ -297,13 +284,12 @@ function generateBidParameters(bid, bidderRequest) { mediaType, adUnitCode: getBidIdParameter('adUnitCode', bid), sizes: sizesArray, - currency: currency, - floorPrice: Math.max(getFloor(bid, mediaType, currency), params.floorPrice), + floorPrice: Math.max(getFloor(bid, mediaType), params.floorPrice), bidId: getBidIdParameter('bidId', bid), loop: getBidIdParameter('bidderRequestsCount', bid), bidderRequestId: getBidIdParameter('bidderRequestId', bid), transactionId: bid.ortb2Imp?.ext?.tid || '', - coppa: 0, + coppa: 0 }; const pos = deepAccess(bid, `mediaTypes.${mediaType}.pos`); @@ -321,15 +307,6 @@ function generateBidParameters(bid, bidderRequest) { bidObject.placementId = placementId; } - const mimes = deepAccess(bid, `mediaTypes.${mediaType}.mimes`); - if (mimes) { - bidObject.mimes = mimes; - } - const api = deepAccess(bid, `mediaTypes.${mediaType}.api`); - if (api) { - bidObject.api = api; - } - const sua = deepAccess(bid, `ortb2.device.sua`); if (sua) { bidObject.sua = sua; @@ -381,11 +358,6 @@ function generateBidParameters(bid, bidderRequest) { bidObject.linearity = linearity; } - const protocols = deepAccess(bid, `mediaTypes.video.protocols`); - if (protocols) { - bidObject.protocols = protocols; - } - const plcmt = deepAccess(bid, `mediaTypes.video.plcmt`); if (plcmt) { bidObject.plcmt = plcmt; @@ -426,8 +398,7 @@ function generateGeneralParams(generalObject, bidderRequest) { dnt: (navigator.doNotTrack == 'yes' || navigator.doNotTrack == '1' || navigator.msDoNotTrack == '1') ? 1 : 0, device_type: getDeviceType(navigator.userAgent), ua: navigator.userAgent, - is_wrapper: !!generalBidParams.isWrapper, - session_id: generalBidParams.sessionId || getBidIdParameter('bidderRequestId', generalObject), + session_id: getBidIdParameter('bidderRequestId', generalObject), tmax: timeout } @@ -470,7 +441,7 @@ function generateGeneralParams(generalObject, bidderRequest) { if (bidderRequest && bidderRequest.refererInfo) { generalParams.referrer = deepAccess(bidderRequest, 'refererInfo.ref'); - generalParams.page_url = deepAccess(bidderRequest, 'refererInfo.page') || deepAccess(window, 'location.href'); + generalParams.page_url = deepAccess(bidderRequest, 'refererInfo.page') || window.location.href } return generalParams diff --git a/modules/minutemediaBidAdapter.md b/modules/minutemediaBidAdapter.md index fdfdf1b32bf..66b54adaf0e 100644 --- a/modules/minutemediaBidAdapter.md +++ b/modules/minutemediaBidAdapter.md @@ -24,7 +24,6 @@ The adapter supports Video(instream) & Banner. | `floorPrice` | optional | Number | Minimum price in USD. Misuse of this parameter can impact revenue | 2.00 | `placementId` | optional | String | A unique placement identifier | "12345678" | `testMode` | optional | Boolean | This activates the test mode | false -| `currency` | optional | String | 3 letters currency | "EUR" # Test Parameters ```javascript diff --git a/modules/minutemediaplusBidAdapter.js b/modules/minutemediaplusBidAdapter.js index 146d437b1fa..33f3634e17f 100644 --- a/modules/minutemediaplusBidAdapter.js +++ b/modules/minutemediaplusBidAdapter.js @@ -252,20 +252,13 @@ function interpretResponse(serverResponse, request) { } } -function getUserSyncs(syncOptions, responses, gdprConsent = {}, uspConsent = '', gppConsent = {}) { +function getUserSyncs(syncOptions, responses, gdprConsent = {}, uspConsent = '') { let syncs = []; const {iframeEnabled, pixelEnabled} = syncOptions; const {gdprApplies, consentString = ''} = gdprConsent; - const {gppString, applicableSections} = gppConsent; const cidArr = responses.filter(resp => deepAccess(resp, 'body.cid')).map(resp => resp.body.cid).filter(uniques); - let params = `?cid=${encodeURIComponent(cidArr.join(','))}&gdpr=${gdprApplies ? 1 : 0}&gdpr_consent=${encodeURIComponent(consentString || '')}&us_privacy=${encodeURIComponent(uspConsent || '')}` - - if (gppString && applicableSections?.length) { - params += '&gpp=' + encodeURIComponent(gppString); - params += '&gpp_sid=' + encodeURIComponent(applicableSections.join(',')); - } - + const params = `?cid=${encodeURIComponent(cidArr.join(','))}&gdpr=${gdprApplies ? 1 : 0}&gdpr_consent=${encodeURIComponent(consentString || '')}&us_privacy=${encodeURIComponent(uspConsent || '')}` if (iframeEnabled) { syncs.push({ type: 'iframe', diff --git a/modules/missenaBidAdapter.js b/modules/missenaBidAdapter.js index 527b6704146..33fa6857e85 100644 --- a/modules/missenaBidAdapter.js +++ b/modules/missenaBidAdapter.js @@ -1,46 +1,12 @@ -import { - buildUrl, - formatQS, - isFn, - logInfo, - safeJSONParse, - triggerPixel, -} from '../src/utils.js'; -import { config } from '../src/config.js'; +import { buildUrl, formatQS, logInfo, triggerPixel } from '../src/utils.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 - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse - * @typedef {import('../src/adapters/bidderFactory.js').validBidRequests} validBidRequests - */ const BIDDER_CODE = 'missena'; const ENDPOINT_URL = 'https://bid.missena.io/'; const EVENTS_DOMAIN = 'events.missena.io'; const EVENTS_DOMAIN_DEV = 'events.staging.missena.xyz'; -export const storage = getStorageManager({ bidderCode: BIDDER_CODE }); - -/* Get Floor price information */ -function getFloor(bidRequest) { - if (!isFn(bidRequest.getFloor)) { - return {}; - } - - const bidFloors = bidRequest.getFloor({ - currency: 'USD', - mediaType: BANNER, - }); - - if (!isNaN(bidFloors.floor)) { - return bidFloors; - } -} - export const spec = { aliases: ['msna'], code: BIDDER_CODE, @@ -64,18 +30,6 @@ export const spec = { * @return ServerRequest Info describing the request to the server. */ buildRequests: function (validBidRequests, bidderRequest) { - const capKey = `missena.missena.capper.remove-bubble.${validBidRequests[0]?.params.apiKey}`; - const capping = safeJSONParse(storage.getDataFromLocalStorage(capKey)); - const referer = bidderRequest?.refererInfo?.topmostLocation; - if ( - typeof capping?.expiry === 'number' && - new Date().getTime() < capping?.expiry && - (!capping?.referer || capping?.referer == referer) - ) { - logInfo('Missena - Capped'); - return []; - } - return validBidRequests.map((bidRequest) => { const payload = { adunit: bidRequest.adUnitCode, @@ -106,17 +60,7 @@ export const spec = { if (bidRequest.params.isInternal) { payload.is_internal = bidRequest.params.isInternal; } - if (bidRequest.ortb2?.device?.ext?.cdep) { - payload.cdep = bidRequest.ortb2?.device?.ext?.cdep; - } payload.userEids = bidRequest.userIdAsEids || []; - payload.version = '$prebid.version$'; - - const bidFloor = getFloor(bidRequest); - payload.floor = bidFloor?.floor; - payload.floor_currency = bidFloor?.currency; - payload.currency = config.getConfig('currency.adServerCurrency') || 'EUR'; - return { method: 'POST', url: baseUrl + '?' + formatQS({ t: bidRequest.params.apiKey }), @@ -145,7 +89,7 @@ export const spec = { syncOptions, serverResponses, gdprConsent, - uspConsent, + uspConsent ) { if (!syncOptions.iframeEnabled) { return []; @@ -184,13 +128,8 @@ export const spec = { protocol: 'https', hostname, pathname: '/v1/bidsuccess', - search: { - t: bid.params[0].apiKey, - provider: bid.meta?.networkName, - cpm: bid.originalCpm, - currency: bid.originalCurrency, - }, - }), + search: { t: bid.params[0].apiKey, provider: bid.meta?.networkName, cpm: bid.cpm, currency: bid.currency }, + }) ); logInfo('Missena - Bid won', bid); }, diff --git a/modules/mobfoxpbBidAdapter.js b/modules/mobfoxpbBidAdapter.js index 35e9b03c031..9ff50e2531f 100644 --- a/modules/mobfoxpbBidAdapter.js +++ b/modules/mobfoxpbBidAdapter.js @@ -71,15 +71,6 @@ export const spec = { if (bidderRequest.gdprConsent) { request.gdpr = bidderRequest.gdprConsent; } - - // Add GPP consent - if (bidderRequest.gppConsent) { - request.gpp = bidderRequest.gppConsent.gppString; - request.gpp_sid = bidderRequest.gppConsent.applicableSections; - } else if (bidderRequest.ortb2?.regs?.gpp) { - request.gpp = bidderRequest.ortb2.regs.gpp; - request.gpp_sid = bidderRequest.ortb2.regs.gpp_sid; - } } const len = validBidRequests.length; diff --git a/modules/multibid/index.js b/modules/multibid/index.js index 27b88d47cf7..df77a157bee 100644 --- a/modules/multibid/index.js +++ b/modules/multibid/index.js @@ -45,10 +45,10 @@ config.getConfig(MODULE_NAME, conf => { }); /** - * @summary validates multibid configuration entries - * @param {Object[]} multibid - example [{bidder: 'bidderA', maxbids: 2, prefix: 'bidA'}, {bidder: 'bidderB', maxbids: 2}] - * @return {Boolean} - */ + * @summary validates multibid configuration entries + * @param {Object[]} multibid - example [{bidder: 'bidderA', maxbids: 2, prefix: 'bidA'}, {bidder: 'bidderB', maxbids: 2}] + * @return {Boolean} +*/ export function validateMultibid(conf) { let check = true; let duplicate = conf.filter(entry => { @@ -77,10 +77,10 @@ export function validateMultibid(conf) { } /** - * @summary addBidderRequests before hook - * @param {Function} fn reference to original function (used by hook logic) - * @param {Object[]} array containing copy of each bidderRequest object - */ + * @summary addBidderRequests before hook + * @param {Function} fn reference to original function (used by hook logic) + * @param {Object[]} array containing copy of each bidderRequest object +*/ export function adjustBidderRequestsHook(fn, bidderRequests) { bidderRequests.map(bidRequest => { // Loop through bidderRequests and check if bidderCode exists in multiconfig @@ -95,11 +95,11 @@ export function adjustBidderRequestsHook(fn, bidderRequests) { } /** - * @summary addBidResponse before hook - * @param {Function} fn reference to original function (used by hook logic) - * @param {String} ad unit code for bid - * @param {Object} bid object - */ + * @summary addBidResponse before hook + * @param {Function} fn reference to original function (used by hook logic) + * @param {String} ad unit code for bid + * @param {Object} bid object +*/ export const addBidResponseHook = timedBidResponseHook('multibid', function addBidResponseHook(fn, adUnitCode, bid, reject) { let floor = deepAccess(bid, 'floorData.floorValue'); @@ -146,9 +146,9 @@ export const addBidResponseHook = timedBidResponseHook('multibid', function addB }); /** - * A descending sort function that will sort the list of objects based on the following: - * - bids without dynamic aliases are sorted before bids with dynamic aliases - */ +* A descending sort function that will sort the list of objects based on the following: +* - bids without dynamic aliases are sorted before bids with dynamic aliases +*/ export function sortByMultibid(a, b) { if (a.bidder !== a.bidderCode && b.bidder === b.bidderCode) { return 1; @@ -162,13 +162,13 @@ export function sortByMultibid(a, b) { } /** - * @summary getHighestCpmBidsFromBidPool before hook - * @param {Function} fn reference to original function (used by hook logic) - * @param {Object[]} array of objects containing all bids from bid pool - * @param {Function} function to reduce to only highest cpm value for each bidderCode - * @param {Number} adUnit bidder targeting limit, default set to 0 - * @param {Boolean} default set to false, this hook modifies targeting and sets to true - */ + * @summary getHighestCpmBidsFromBidPool before hook + * @param {Function} fn reference to original function (used by hook logic) + * @param {Object[]} array of objects containing all bids from bid pool + * @param {Function} function to reduce to only highest cpm value for each bidderCode + * @param {Number} adUnit bidder targeting limit, default set to 0 + * @param {Boolean} default set to false, this hook modifies targeting and sets to true +*/ export function targetBidPoolHook(fn, bidsReceived, highestCpmCallback, adUnitBidLimit = 0, hasModified = false) { if (!config.getConfig('multibid')) resetMultiConfig(); if (hasMultibid) { @@ -216,18 +216,18 @@ export function targetBidPoolHook(fn, bidsReceived, highestCpmCallback, adUnitBi } /** - * Resets globally stored multibid configuration - */ +* Resets globally stored multibid configuration +*/ export const resetMultiConfig = () => { hasMultibid = false; multiConfig = {}; }; /** - * Resets globally stored multibid ad unit bids - */ +* Resets globally stored multibid ad unit bids +*/ export const resetMultibidUnits = () => multibidUnits = {}; /** - * Set up hooks on init - */ +* Set up hooks on init +*/ function init() { // TODO: does this reset logic make sense - what about simultaneous auctions? events.on(CONSTANTS.EVENTS.AUCTION_INIT, resetMultibidUnits); diff --git a/modules/mwOpenLinkIdSystem.js b/modules/mwOpenLinkIdSystem.js index c06f61ff82f..9b1035cbf18 100644 --- a/modules/mwOpenLinkIdSystem.js +++ b/modules/mwOpenLinkIdSystem.js @@ -11,11 +11,6 @@ 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 - * @typedef {import('../modules/userId/index.js').SubmoduleParams} SubmoduleParams - */ - const openLinkID = { name: 'mwol', cookie_expiration: (86400 * 1000 * 365 * 1) // 1 year @@ -117,37 +112,31 @@ export { writeCookie }; /** @type {Submodule} */ export const mwOpenLinkIdSubModule = { /** - * used to link submodule with config - * @type {string} - */ + * used to link submodule with config + * @type {string} + */ name: 'mwOpenLinkId', /** - * decode the stored id value for passing to bid requests - * @function - * @param {MwOlId} mwOlId - * @return {(Object|undefined} - */ + * decode the stored id value for passing to bid requests + * @function + * @param {MwOlId} mwOlId + * @return {(Object|undefined} + */ decode(mwOlId) { const id = mwOlId && isPlainObject(mwOlId) ? mwOlId.eid : undefined; return id ? { 'mwOpenLinkId': id } : undefined; }, /** - * performs action to obtain id and return a value in the callback's response argument - * @function - * @param {SubmoduleParams} [submoduleParams] - * @returns {id:MwOlId | undefined} - */ + * performs action to obtain id and return a value in the callback's response argument + * @function + * @param {SubmoduleParams} [submoduleParams] + * @returns {id:MwOlId | undefined} + */ getId(submoduleConfig) { const submoduleConfigParams = (submoduleConfig && submoduleConfig.params) || {}; if (!isValidConfig(submoduleConfigParams)) return undefined; return setID(submoduleConfigParams); - }, - eids: { - 'mwOpenLinkId': { - source: 'mediawallahscript.com', - atype: 1 - }, } }; diff --git a/modules/mygaruIdSystem.js b/modules/mygaruIdSystem.js deleted file mode 100644 index 9133480477b..00000000000 --- a/modules/mygaruIdSystem.js +++ /dev/null @@ -1,104 +0,0 @@ -/** - * This module adds MyGaru Real Time User Sync to the User ID module - * The {@link module:modules/userId} module is required - * @module modules/mygaruIdSystem - * @requires module:modules/userId - */ - -import { ajax } from '../src/ajax.js'; -import { submodule } from '../src/hook.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 - */ - -const bidderCode = 'mygaruId'; -const syncUrl = 'https://ident.mygaru.com/v2/id'; - -export function buildUrl(opts) { - const queryPairs = []; - for (let key in opts) { - if (opts[key] !== undefined) { - queryPairs.push(`${key}=${encodeURIComponent(opts[key])}`); - } - } - return `${syncUrl}?${queryPairs.join('&')}`; -} - -function requestRemoteIdAsync(url) { - return new Promise((resolve) => { - ajax( - url, - { - success: response => { - try { - const jsonResponse = JSON.parse(response); - const { iuid } = jsonResponse; - resolve(iuid); - } catch (e) { - resolve(); - } - }, - error: () => { - resolve(); - }, - }, - undefined, - { - method: 'GET', - contentType: 'application/json' - } - ); - }); -} - -/** @type {Submodule} */ -export const mygaruIdSubmodule = { - /** - * used to link submodule with config - * @type {string} - */ - name: bidderCode, - /** - * decode the stored id value for passing to bid requests - * @function - * @returns {{id: string} | null} - */ - decode(id) { - return id; - }, - /** - * get the MyGaru Id from local storages and initiate a new user sync - * @function - * @param {SubmoduleConfig} [config] - * @param {ConsentData} [consentData] - * @returns {{id: string | undefined}} - */ - getId(config, consentData) { - const gdprApplies = consentData && typeof consentData.gdprApplies === 'boolean' && consentData.gdprApplies ? 1 : 0; - const gdprConsentString = gdprApplies ? consentData.consentString : undefined; - const url = buildUrl({ - gdprApplies, - gdprConsentString - }); - - return { - url, - callback: function (done) { - return requestRemoteIdAsync(url).then((id) => { - done({ mygaruId: id }); - }) - } - } - }, - eids: { - 'mygaruId': { - source: 'mygaru.com', - atype: 1 - }, - } -}; - -submodule('userId', mygaruIdSubmodule); diff --git a/modules/mygaruIdSystem.md b/modules/mygaruIdSystem.md deleted file mode 100644 index 92724f99469..00000000000 --- a/modules/mygaruIdSystem.md +++ /dev/null @@ -1,24 +0,0 @@ -## Mygaru User ID Submodule - -MyGaru provides single use tokens as a UserId for SSPs and DSP that consume telecom DMP data. - -## Building Prebid with Mygaru ID Support - -First, make sure to add submodule to your Prebid.js package with: - -``` -gulp build --modules=userId,mygaruIdSystem -``` -Params configuration is not required. -Also mygaru is async, in order to get ids for initial ad auctions you need to add auctionDelay param to userSync config. - -```javascript -pbjs.setConfig({ - userSync: { - auctionDelay: 100, - userIds: [{ - name: 'mygaruId', - }] - } -}); -``` diff --git a/modules/nativoBidAdapter.js b/modules/nativoBidAdapter.js index 69a270247cd..c62a74e6d6c 100644 --- a/modules/nativoBidAdapter.js +++ b/modules/nativoBidAdapter.js @@ -2,20 +2,7 @@ import { deepAccess, isEmpty } from '../src/utils.js' import { registerBidder } from '../src/adapters/bidderFactory.js' import { BANNER } from '../src/mediaTypes.js' import { getGlobal } from '../src/prebidGlobal.js' -import { ortbConverter } from '../libraries/ortbConverter/converter.js' - -const converter = ortbConverter({ - context: { - // `netRevenue` and `ttl` are required properties of bid responses - provide a default for them - netRevenue: true, // or false if your adapter should set bidResponse.netRevenue = false - ttl: 30 // default bidResponse.ttl (when not specified in ORTB response.seatbid[].bid[].exp) - }, - imp(buildImp, bidRequest, context) { - const imp = buildImp(bidRequest, context); - imp.tagid = bidRequest.adUnitCode - return imp; - } -}); +// import { config } from 'src/config' const BIDDER_CODE = 'nativo' const BIDDER_ENDPOINT = 'https://exchange.postrelease.com/prebid' @@ -149,10 +136,6 @@ export const spec = { * @return ServerRequest Info describing the request to the server. */ buildRequests: function (validBidRequests, bidderRequest) { - // Get OpenRTB Data - const openRTBData = converter.toORTB({bidRequests: validBidRequests, bidderRequest}) - const openRTBDataString = JSON.stringify(openRTBData) - const requestData = new RequestData() requestData.addBidRequestDataSource(new UserEIDs()) @@ -288,9 +271,8 @@ export const spec = { const requestUrl = buildRequestUrl(BIDDER_ENDPOINT, qsParamStrings) let serverRequest = { - method: 'POST', - url: requestUrl, - data: openRTBDataString, + method: 'GET', + url: requestUrl } return serverRequest diff --git a/modules/naveggIdSystem.js b/modules/naveggIdSystem.js index 42c6b113566..878ae7fadb2 100644 --- a/modules/naveggIdSystem.js +++ b/modules/naveggIdSystem.js @@ -10,11 +10,6 @@ import { ajax } from '../src/ajax.js'; import {getStorageManager} from '../src/storageManager.js'; import {MODULE_TYPE_UID} from '../src/activities/modules.js'; -/** - * @typedef {import('../modules/userId/index.js').Submodule} Submodule - * @typedef {import('../modules/userId/index.js').SubmoduleConfig} SubmoduleConfig - */ - const MODULE_NAME = 'naveggId'; const OLD_NAVEGG_ID = 'nid'; const NAVEGG_ID = 'nvggid'; @@ -79,16 +74,16 @@ function readnavIDFromCookie() { /** @type {Submodule} */ export const naveggIdSubmodule = { /** - * used to link submodule with config - * @type {string} - */ + * used to link submodule with config + * @type {string} + */ name: MODULE_NAME, /** - * decode the stored id value for passing to bid requests - * @function - * @param { Object | string | undefined } value - * @return { Object | string | undefined } - */ + * decode the stored id value for passing to bid requests + * @function + * @param { Object | string | undefined } value + * @return { Object | string | undefined } + */ decode(value) { const naveggIdVal = value ? isStr(value) ? value : isPlainObject(value) ? value.id : undefined : undefined; return naveggIdVal ? { @@ -96,11 +91,11 @@ export const naveggIdSubmodule = { } : undefined; }, /** - * performs action to obtain id and return a value in the callback's response argument - * @function - * @param {SubmoduleConfig} config - * @return {{id: string | undefined } | undefined} - */ + * performs action to obtain id and return a value in the callback's response argument + * @function + * @param {SubmoduleConfig} config + * @return {{id: string | undefined } | undefined} + */ getId() { const naveggIdString = readnaveggIdFromLocalStorage() || readnaveggIDFromCookie() || getNaveggIdFromApi() || readoldnaveggIDFromCookie() || readnvgIDFromCookie() || readnavIDFromCookie(); @@ -112,12 +107,6 @@ export const naveggIdSubmodule = { } } return undefined; - }, - eids: { - 'naveggId': { - source: 'navegg.com', - atype: 1 - }, } }; submodule('userId', naveggIdSubmodule); diff --git a/modules/netIdSystem.js b/modules/netIdSystem.js index 4765f892a97..90c8735c993 100644 --- a/modules/netIdSystem.js +++ b/modules/netIdSystem.js @@ -7,13 +7,6 @@ import {submodule} from '../src/hook.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 - */ - /** @type {Submodule} */ export const netIdSubmodule = { /** @@ -41,12 +34,6 @@ export const netIdSubmodule = { getId(config) { /* currently not possible */ return {}; - }, - eids: { - 'netId': { - source: 'netid.de', - atype: 1 - }, } }; diff --git a/modules/neuwoRtdProvider.js b/modules/neuwoRtdProvider.js index 7c594e2a1c3..00a3c59b4a6 100644 --- a/modules/neuwoRtdProvider.js +++ b/modules/neuwoRtdProvider.js @@ -10,14 +10,14 @@ const SEGTAX_IAB = 6 // IAB - Content Taxonomy version 2 const RESPONSE_IAB_TIER_1 = 'marketing_categories.iab_tier_1' const RESPONSE_IAB_TIER_2 = 'marketing_categories.iab_tier_2' -function init(config, userConsent) { - // config.params = config.params || {} +function init(config = {}, userConsent) { + config.params = config.params || {} // ignore module if publicToken is missing (module setup failure) - if (!config || !config.params || !config.params.publicToken) { + if (!config.params.publicToken) { logError('publicToken missing', 'NeuwoRTDModule', 'config.params.publicToken') return false; } - if (!config || !config.params || !config.params.apiUrl) { + if (!config.params.apiUrl) { logError('apiUrl missing', 'NeuwoRTDModule', 'config.params.apiUrl') return false; } @@ -25,14 +25,14 @@ function init(config, userConsent) { } export function getBidRequestData(reqBidsConfigObj, callback, config, userConsent) { - const confParams = config.params || {}; + config.params = config.params || {}; logInfo('NeuwoRTDModule', 'starting getBidRequestData') - const wrappedArgUrl = encodeURIComponent(confParams.argUrl || getRefererInfo().page); + const wrappedArgUrl = encodeURIComponent(config.params.argUrl || getRefererInfo().page); /* adjust for pages api.url?prefix=test (to add params with '&') as well as api.url (to add params with '?') */ - const joiner = confParams.apiUrl.indexOf('?') < 0 ? '?' : '&' - const url = confParams.apiUrl + joiner + [ - 'token=' + confParams.publicToken, + const joiner = config.params.apiUrl.indexOf('?') < 0 ? '?' : '&' + const url = config.params.apiUrl + joiner + [ + 'token=' + config.params.publicToken, 'url=' + wrappedArgUrl ].join('&') const billingId = generateUUID(); @@ -71,7 +71,7 @@ export function addFragment(base, path, addition) { /** * Concatenate a base array and an array within an object * non-array bases will be arrays, non-arrays at object key will be discarded - * @param {Array} base base array to add to + * @param {array} base base array to add to * @param {object} source object to get an array from * @param {string} key dot-notated path to array within object * @returns base + source[key] if that's an array diff --git a/modules/neuwoRtdProvider.md b/modules/neuwoRtdProvider.md index fb52734d451..2adead66d4e 100644 --- a/modules/neuwoRtdProvider.md +++ b/modules/neuwoRtdProvider.md @@ -33,8 +33,6 @@ pbjs.setConfig({realTimeData: { dataProviders: [ neuwoDataProvider ]}}) # Testing -`gulp test --modules=rtdModule,neuwoRtdProvider` - ## Add development tools if necessary - Install node for npm diff --git a/modules/newspassid.md b/modules/newspassid.md deleted file mode 100644 index 6fa709e5ba6..00000000000 --- a/modules/newspassid.md +++ /dev/null @@ -1,76 +0,0 @@ ---- -Module Name: NewspassId Bidder Adapter -Module Type: Bidder Adapter -Maintainer: techsupport@newspassid.com -layout: bidder -title: Newspass ID -description: LMC Newspass ID Prebid JS Bidder Adapter -biddercode: newspassid -gdpr_supported: false -gvl_id: none -usp_supported: true -coppa_supported: false -schain_supported: true -dchain_supported: false -userIds: criteo, id5Id, tdid, identityLink, liveIntentId, parrableId, pubCommonId, lotamePanoramaId, sharedId, fabrickId -media_types: banner -safeframes_ok: true -deals_supported: true -floors_supported: false -fpd_supported: false -pbjs: true -pbs: false -prebid_member: false -multiformat_supported: will-bid-on-any ---- - -### Description - -LMC Newspass ID Prebid JS Bidder Adapter that connects to the NewspassId demand source(s). - -The Newspass bid adapter supports Banner mediaTypes ONLY. -This is intended for USA audiences only, and does not support GDPR - - -### Bid Params - -{: .table .table-bordered .table-striped } - -| Name | Scope | Description | Example | Type | -|-----------|----------|---------------------------|------------|----------| -| `siteId` | required | The site ID. | `"NPID0000001"` | `string` | -| `publisherId` | required | The publisher ID. | `"4204204201"` | `string` | -| `placementId` | required | The placement ID. | `"0420420421"` | `string` | -| `customData` | optional | publisher key-values used for targeting | `[{"settings":{},"targeting":{"key1": "value1", "key2": "value2"}}], ` | `array` | - -### Test Parameters - - -A test ad unit that will consistently return test creatives: - -``` - -//Banner adUnit - -adUnits = [{ - code: 'id-of-your-banner-div', - mediaTypes: { - banner: { - sizes: [[300, 250], [300,600]] - } - }, - bids: [{ - bidder: 'newspassid', - params: { - publisherId: 'NEWSPASS0001', /* an ID to identify the publisher account - required */ - siteId: '4204204201', /* An ID used to identify a site within a publisher account - required */ - placementId: '8000000015', /* an ID used to identify the piece of inventory - required - for appnexus test use 13144370. */ - customData: [{"settings": {}, "targeting": {"key": "value", "key2": ["value1", "value2"]}}],/* optional array with 'targeting' placeholder for passing publisher specific key-values for targeting. */ - } - }] - }]; -``` - -### Note: - -Please contact us at techsupport@newspassid.com for any assistance testing your implementation before going live into production. diff --git a/modules/newspassidBidAdapter.js b/modules/newspassidBidAdapter.js index 2a4b2da186b..b440edc2beb 100644 --- a/modules/newspassidBidAdapter.js +++ b/modules/newspassidBidAdapter.js @@ -1,24 +1,15 @@ -import { - logInfo, - logError, - deepAccess, - logWarn, - deepSetValue, - isArray, - contains, - parseUrl, - generateUUID -} from '../src/utils.js'; -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { BANNER, NATIVE } from '../src/mediaTypes.js'; +import {contains, deepAccess, deepSetValue, isArray, logError, logInfo, logWarn, parseUrl} 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 {getPriceBucketString} from '../src/cpmBucketManager.js'; import {getRefererInfo} from '../src/refererDetection.js'; + const BIDDER_CODE = 'newspassid'; const ORIGIN = 'https://bidder.newspassid.com' // applies only to auction & cookie const AUCTIONURI = '/openrtb2/auction'; const NEWSPASSCOOKIESYNC = '/static/load-cookie.html'; -const NEWSPASSVERSION = '1.1.4'; +const NEWSPASSVERSION = '1.0.1'; export const spec = { version: NEWSPASSVERSION, code: BIDDER_CODE, @@ -164,7 +155,7 @@ export const spec = { let placementId = placementIdOverrideFromGetParam || this.getPlacementId(npBidRequest); // prefer to use a valid override param, else the bidRequest placement Id obj.id = npBidRequest.bidId; // this causes an error if we change it to something else, even if you update the bidRequest object: "WARNING: Bidder newspass made bid for unknown request ID: mb7953.859498327448. Ignoring." obj.tagid = placementId; - let parsed = parseUrl(this.getRefererInfo().page); + let parsed = parseUrl(getRefererInfo().page); obj.secure = parsed.protocol === 'https' ? 1 : 0; let arrBannerSizes = []; if (!npBidRequest.hasOwnProperty('mediaTypes')) { @@ -198,6 +189,7 @@ export const spec = { deepSetValue(obj, 'ext.prebid', {'storedrequest': {'id': placementId}}); obj.ext['newspassid'] = {}; obj.ext['newspassid'].adUnitCode = npBidRequest.adUnitCode; // eg. 'mpu' + obj.ext['newspassid'].transactionId = npBidRequest.transactionId; // this is the transactionId PER adUnit, common across bidders for this unit if (npBidRequest.params.hasOwnProperty('customData')) { obj.ext['newspassid'].customData = npBidRequest.params.customData; } @@ -224,10 +216,6 @@ export const spec = { if (!schain && deepAccess(npBidRequest, 'schain')) { schain = npBidRequest.schain; } - let gpid = deepAccess(npBidRequest, 'ortb2Imp.ext.gpid'); - if (gpid) { - deepSetValue(obj, 'ext.gpid', gpid); - } return obj; }); let extObj = {}; @@ -252,7 +240,7 @@ export const spec = { let userExtEids = deepAccess(validBidRequests, '0.userIdAsEids', []); // generate the UserIDs in the correct format for UserId module npRequest.site = { 'publisher': {'id': htmlParams.publisherId}, - 'page': this.getRefererInfo().page, + 'page': getRefererInfo().page, 'id': htmlParams.siteId }; npRequest.test = config.getConfig('debug') ? 1 : 0; @@ -271,9 +259,12 @@ export const spec = { } if (singleRequest) { logInfo('buildRequests starting to generate response for a single request'); - npRequest.id = generateUUID(); // Unique ID of the bid request, provided by the exchange. (REQUIRED) + // TODO: fix auctionId & transactionId leak: https://github.com/prebid/Prebid.js/issues/9781 + npRequest.id = bidderRequest.auctionId; // Unique ID of the bid request, provided by the exchange. + npRequest.auctionId = bidderRequest.auctionId; // not sure if this should be here? npRequest.imp = tosendtags; npRequest.ext = extObj; + deepSetValue(npRequest, 'source.tid', bidderRequest.auctionId);// RTB 2.5 : tid is Transaction ID that must be common across all participants in this bid request (e.g., potentially multiple exchanges). deepSetValue(npRequest, 'user.ext.eids', userExtEids); var ret = { method: 'POST', @@ -289,9 +280,12 @@ export const spec = { let arrRet = tosendtags.map(imp => { logInfo('buildRequests starting to generate non-single response, working on imp : ', imp); let npRequestSingle = Object.assign({}, npRequest); - npRequestSingle.id = generateUUID(); + imp.ext['newspassid'].pageAuctionId = bidderRequest['auctionId']; // make a note in the ext object of what the original auctionId was, in the bidderRequest object + npRequestSingle.id = imp.ext['newspassid'].transactionId; // Unique ID of the bid request, provided by the exchange. + npRequestSingle.auctionId = imp.ext['newspassid'].transactionId; // not sure if this should be here? npRequestSingle.imp = [imp]; npRequestSingle.ext = extObj; + deepSetValue(npRequestSingle, 'source.tid', bidderRequest.auctionId);// RTB 2.5 : tid is Transaction ID that must be common across all participants in this bid request (e.g., potentially multiple exchanges). deepSetValue(npRequestSingle, 'user.ext.eids', userExtEids); logInfo('buildRequests RequestSingle (for non-single) = ', npRequestSingle); return { @@ -311,7 +305,6 @@ export const spec = { logInfo(`interpretResponse time: ${startTime}. buildRequests done -> interpretResponse start was ${startTime - this.propertyBag.buildRequestsEnd}ms`); logInfo(`serverResponse, request`, JSON.parse(JSON.stringify(serverResponse)), JSON.parse(JSON.stringify(request))); serverResponse = serverResponse.body || {}; - let aucId = serverResponse.id; // this will be correct for single requests and non-single if (!serverResponse.hasOwnProperty('seatbid')) { return []; } @@ -358,7 +351,7 @@ export const spec = { logInfo(`newspassid.enhancedAdserverTargeting is set to false, no per-bid keys will be sent to adserver.`); } let {seat: winningSeat, bid: winningBid} = this.getWinnerForRequestBid(thisBid.bidId, serverResponse.seatbid); - adserverTargeting['np_auc_id'] = String(aucId); + adserverTargeting['np_auc_id'] = String(request.bidderRequest.auctionId); adserverTargeting['np_winner'] = String(winningSeat); adserverTargeting['np_bid'] = 'true'; if (enhancedAdserverTargeting) { @@ -497,29 +490,10 @@ export const spec = { return null; }, getGetParametersAsObject() { - let parsed = parseUrl(this.getRefererInfo().location); // was getRefererInfo().page but this is not backwards compatible + let parsed = parseUrl(getRefererInfo().page); logInfo('getGetParametersAsObject found:', parsed.search); return parsed.search; }, - getRefererInfo() { - if (getRefererInfo().hasOwnProperty('location')) { - logInfo('FOUND location on getRefererInfo OK (prebid >= 7); will use getRefererInfo for location & page'); - return getRefererInfo(); - } else { - logInfo('DID NOT FIND location on getRefererInfo (prebid < 7); will use legacy code that ALWAYS worked reliably to get location & page ;-)'); - try { - return { - page: top.location.href, - location: top.location.href - }; - } catch (e) { - return { - page: window.location.href, - location: window.location.href - }; - } - } - }, blockTheRequest() { let npRequest = config.getConfig('newspassid.np_request'); if (typeof npRequest == 'boolean' && !npRequest) { diff --git a/modules/nextMillenniumBidAdapter.js b/modules/nextMillenniumBidAdapter.js index 0af2129de61..cb660ad9fd6 100644 --- a/modules/nextMillenniumBidAdapter.js +++ b/modules/nextMillenniumBidAdapter.js @@ -2,7 +2,6 @@ import { _each, createTrackPixelHtml, deepAccess, - deepSetValue, getBidIdParameter, getDefinedParams, getWindowTop, @@ -14,7 +13,6 @@ import { triggerPixel, } from '../src/utils.js'; -import {getGlobal} from '../src/prebidGlobal.js'; import CONSTANTS from '../src/constants.json'; import {BANNER, VIDEO} from '../src/mediaTypes.js'; import {config} from '../src/config.js'; @@ -23,34 +21,17 @@ import * as events from '../src/events.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {getRefererInfo} from '../src/refererDetection.js'; -const NM_VERSION = '3.1.0'; -const GVLID = 1060; const BIDDER_CODE = 'nextMillennium'; const ENDPOINT = 'https://pbs.nextmillmedia.com/openrtb2/auction'; const TEST_ENDPOINT = 'https://test.pbs.nextmillmedia.com/openrtb2/auction'; -const SYNC_ENDPOINT = 'https://cookies.nextmillmedia.com/sync?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}}&type={{.TYPE_PIXEL}}'; +const SYNC_ENDPOINT = 'https://cookies.nextmillmedia.com/sync?'; const REPORT_ENDPOINT = 'https://report2.hb.brainlyads.com/statistics/metric'; const TIME_TO_LIVE = 360; -const DEFAULT_CURRENCY = 'USD'; - const VIDEO_PARAMS = [ - 'api', - 'linearity', - 'maxduration', - 'mimes', - 'minduration', - 'placement', - 'playbackmethod', - 'protocols', - 'startdelay', -]; - -const ALLOWED_ORTB2_PARAMETERS = [ - 'site.pagecat', - 'site.content.cat', - 'site.content.language', - 'device.sua', + 'api', 'linearity', 'maxduration', 'mimes', 'minduration', 'placement', + 'playbackmethod', 'protocols', 'startdelay' ]; +const GVLID = 1060; const sendingDataStatistic = initSendingDataStatistic(); events.on(CONSTANTS.EVENTS.AUCTION_INIT, auctionInitHandler); @@ -81,39 +62,85 @@ export const spec = { const id = getPlacementId(bid); const auctionId = bid.auctionId; const bidId = bid.bidId; + let sizes = bid.sizes; + if (sizes && !Array.isArray(sizes[0])) sizes = [sizes]; const site = getSiteObj(); const device = getDeviceObj(); - const {cur, mediaTypes} = getCurrency(bid); const postBody = { - id: bidderRequest?.bidderRequestId, - cur, - ext: { - prebid: { - storedrequest: { - id, - }, + 'id': bidderRequest?.bidderRequestId, + 'ext': { + 'prebid': { + 'storedrequest': { + 'id': id + } }, - nextMillennium: { - nm_version: NM_VERSION, - pbjs_version: getGlobal()?.version || undefined, - refresh_count: window.nmmRefreshCounts[bid.adUnitCode]++, - elOffsets: getBoundingClient(bid), - scrollTop: window.pageYOffset || document.documentElement.scrollTop, - }, + 'nextMillennium': { + 'refresh_count': window.nmmRefreshCounts[bid.adUnitCode]++, + 'elOffsets': getBoundingClient(bid), + 'scrollTop': window.pageYOffset || document.documentElement.scrollTop + } }, device, site, - imp: [], + imp: [] }; - postBody.imp.push(getImp(bid, id, mediaTypes)); - setConsentStrings(postBody, bidderRequest); - setOrtb2Parameters(postBody, bidderRequest?.ortb2); - setEids(postBody, bid); + const imp = { + id: bid.adUnitCode, + ext: { + prebid: { + storedrequest: {id} + } + } + }; + + if (deepAccess(bid, 'mediaTypes.banner')) { + imp.banner = { + format: (sizes || []).map(s => { return {w: s[0], h: s[1]} }) + }; + }; + + const video = deepAccess(bid, 'mediaTypes.video'); + if (video) { + imp.video = getDefinedParams(video, VIDEO_PARAMS); + if (video.playerSize) { + imp.video = Object.assign( + imp.video, parseGPTSingleSizeArrayToRtbSize(video.playerSize[0]) || {} + ); + } else if (video.w && video.h) { + imp.video.w = video.w; + imp.video.h = video.h; + }; + }; + + postBody.imp.push(imp); + + const gdprConsent = bidderRequest && bidderRequest.gdprConsent; + const uspConsent = bidderRequest && bidderRequest.uspConsent; + + if (gdprConsent || uspConsent) { + postBody.regs = { ext: {} }; + + if (uspConsent) { + postBody.regs.ext.us_privacy = uspConsent; + }; + + if (gdprConsent) { + if (typeof gdprConsent.gdprApplies !== 'undefined') { + postBody.regs.ext.gdpr = gdprConsent.gdprApplies ? 1 : 0; + }; + + if (typeof gdprConsent.consentString !== 'undefined') { + postBody.user = { + ext: { consent: gdprConsent.consentString } + }; + }; + }; + }; const urlParameters = parseUrl(getWindowTop().location.href).search; const isTest = urlParameters['pbs'] && urlParameters['pbs'] === 'test'; @@ -125,7 +152,7 @@ export const spec = { data: JSON.stringify(postBody), options: { contentType: 'text/plain', - withCredentials: true, + withCredentials: true }, bidId, @@ -147,7 +174,6 @@ export const spec = { const params = bidRequest.params; const auctionId = bidRequest.auctionId; const wurl = deepAccess(bid, 'ext.prebid.events.win'); - // TODO: fix auctionId leak: https://github.com/prebid/Prebid.js/issues/9781 addWurl({auctionId, requestId, wurl}); @@ -160,12 +186,12 @@ export const spec = { width: bid.w, height: bid.h, creativeId: bid.adid, - currency: response.cur || DEFAULT_CURRENCY, + currency: response.cur, netRevenue: true, ttl: TIME_TO_LIVE, meta: { - advertiserDomains: bid.adomain || [], - }, + advertiserDomains: bid.adomain || [] + } }; if (vastUrl || vastXml) { @@ -185,29 +211,38 @@ export const spec = { return bidResponses; }, - getUserSyncs: function (syncOptions, responses, gdprConsent, uspConsent, gppConsent) { - if (!syncOptions.iframeEnabled && !syncOptions.pixelEnabled) return []; - + getUserSyncs: function (syncOptions, responses, gdprConsent, uspConsent) { const pixels = []; - 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) }; - const setPixelImages = getSetPixelsFunc('image'); - const setPixelIframes = getSetPixelsFunc('iframe'); if (isArray(responses)) { responses.forEach(response => { - if (syncOptions.pixelEnabled) setPixelImages(response); - if (syncOptions.iframeEnabled) setPixelIframes(response); + if (syncOptions.pixelEnabled) { + deepAccess(response, 'body.ext.sync.image', []).forEach(imgUrl => { + pixels.push({ + type: 'image', + url: replaceUsersyncMacros(imgUrl, gdprConsent, uspConsent) + }); + }) + } + + if (syncOptions.iframeEnabled) { + deepAccess(response, 'body.ext.sync.iframe', []).forEach(iframeUrl => { + pixels.push({ + type: 'iframe', + url: replaceUsersyncMacros(iframeUrl, gdprConsent, uspConsent) + }); + }) + } }) } if (!pixels.length) { - if (syncOptions.pixelEnabled) setPixel('image', SYNC_ENDPOINT); - if (syncOptions.iframeEnabled) setPixel('iframe', SYNC_ENDPOINT); + let syncUrl = SYNC_ENDPOINT; + if (gdprConsent && gdprConsent.gdprApplies) syncUrl += 'gdpr=1&gdpr_consent=' + gdprConsent.consentString + '&'; + if (uspConsent) syncUrl += 'us_privacy=' + uspConsent + '&'; + if (syncOptions.iframeEnabled) pixels.push({type: 'iframe', url: syncUrl + 'type=iframe'}); + if (syncOptions.pixelEnabled) pixels.push({type: 'image', url: syncUrl + 'type=image'}); } - return pixels; }, @@ -247,131 +282,27 @@ export const spec = { }, }; -export function getImp(bid, id, mediaTypes) { - const {banner, video} = mediaTypes; - const imp = { - id: bid.adUnitCode, - ext: { - prebid: { - storedrequest: { - id, - }, - }, - }, - }; - - if (banner) { - if (banner.bidfloorcur) imp.bidfloorcur = banner.bidfloorcur; - if (banner.bidfloor) imp.bidfloor = banner.bidfloor; +function replaceUsersyncMacros(url, gdprConsent, uspConsent) { + const { consentString, gdprApplies } = gdprConsent || {}; - imp.banner = { - format: (banner.data?.sizes || []).map(s => { return {w: s[0], h: s[1]} }), - }; - }; - - if (video) { - if (video.bidfloorcur) imp.bidfloorcur = video.bidfloorcur; - if (video.bidfloor) imp.bidfloor = video.bidfloor; - - imp.video = getDefinedParams(video, VIDEO_PARAMS); - if (video.data.playerSize) { - imp.video = Object.assign(imp.video, parseGPTSingleSizeArrayToRtbSize(video.data.playerSize) || {}); - } else if (video.w && video.h) { - imp.video.w = video.w; - imp.video.h = video.h; - }; - }; + if (gdprApplies) { + const gdpr = Number(gdprApplies); + url = url.replace('{{.GDPR}}', gdpr); - return imp; -}; - -export function setConsentStrings(postBody = {}, bidderRequest) { - const gdprConsent = bidderRequest?.gdprConsent; - const uspConsent = bidderRequest?.uspConsent; - let gppConsent = bidderRequest?.gppConsent?.gppString && bidderRequest?.gppConsent; - if (!gppConsent && bidderRequest?.ortb2?.regs?.gpp) gppConsent = bidderRequest?.ortb2?.regs; - - if (gdprConsent || uspConsent || gppConsent) { - postBody.regs = { ext: {} }; - - if (uspConsent) { - postBody.regs.ext.us_privacy = uspConsent; - }; - - if (gppConsent) { - postBody.regs.gpp = gppConsent?.gppString || gppConsent?.gpp; - postBody.regs.gpp_sid = bidderRequest.gppConsent?.applicableSections || gppConsent?.gpp_sid; - }; - - if (gdprConsent) { - if (typeof gdprConsent.gdprApplies !== 'undefined') { - postBody.regs.ext.gdpr = gdprConsent.gdprApplies ? 1 : 0; - }; - - if (typeof gdprConsent.consentString !== 'undefined') { - postBody.user = { - ext: { consent: gdprConsent.consentString }, - }; - }; - }; - }; -}; - -export function setOrtb2Parameters(postBody, ortb2 = {}) { - for (let parameter of ALLOWED_ORTB2_PARAMETERS) { - const value = deepAccess(ortb2, parameter); - if (value) deepSetValue(postBody, parameter, value); + if (gdpr == 1 && consentString && consentString.length > 0) { + url = url.replace('{{.GDPRConsent}}', consentString); + } + } else { + url = url.replace('{{.GDPR}}', 0); + url = url.replace('{{.GDPRConsent}}', ''); } -} -export function setEids(postBody, bid) { - if (!isArray(bid.userIdAsEids) || !bid.userIdAsEids.length) return; - - deepSetValue(postBody, 'user.eids', bid.userIdAsEids); -} - -export function replaceUsersyncMacros(url, gdprConsent = {}, uspConsent = '', gppConsent = {}, type = '') { - const { consentString = '', gdprApplies = false } = gdprConsent; - const gdpr = Number(gdprApplies); - url = url - .replace('{{.GDPR}}', gdpr) - .replace('{{.GDPRConsent}}', consentString) - .replace('{{.USPrivacy}}', uspConsent) - .replace('{{.GPP}}', gppConsent.gppString || '') - .replace('{{.GPPSID}}', (gppConsent.applicableSections || []).join(',')) - .replace('{{.TYPE_PIXEL}}', type); + if (uspConsent) { + url = url.replace('{{.USPrivacy}}', uspConsent); + } return url; -} - -function getCurrency(bid = {}) { - const currency = config?.getConfig('currency')?.adServerCurrency || DEFAULT_CURRENCY; - const cur = []; - const types = ['banner', 'video']; - const mediaTypes = {}; - for (const mediaType of types) { - const mediaTypeData = deepAccess(bid, `mediaTypes.${mediaType}`); - if (mediaTypeData) { - mediaTypes[mediaType] = {data: mediaTypeData}; - } else { - continue; - }; - - if (typeof bid.getFloor === 'function') { - let floorInfo = bid.getFloor({currency, mediaType, size: '*'}); - mediaTypes[mediaType].bidfloorcur = floorInfo.currency; - mediaTypes[mediaType].bidfloor = floorInfo.floor; - } else { - mediaTypes[mediaType].bidfloorcur = currency; - }; - - if (cur.includes(mediaTypes[mediaType].bidfloorcur)) cur.push(mediaTypes[mediaType].bidfloorcur); - }; - - if (!cur.length) cur.push(DEFAULT_CURRENCY); - - return {cur, mediaTypes}; -} +}; function getAdEl(bid) { // best way I could think of to get El, is by matching adUnitCode to google slots... @@ -438,7 +369,7 @@ function getAd(bid) { } else if (bid.nurl) { adUrl = bid.nurl; }; - }; + } return {ad, adUrl, vastXml, vastUrl}; } @@ -446,21 +377,10 @@ function getAd(bid) { function getSiteObj() { const refInfo = (getRefererInfo && getRefererInfo()) || {}; - let language = navigator.language; - let content; - if (language) { - // get ISO-639-1-alpha-2 (2 character language) - language = language.split('-')[0]; - content = { - language, - }; - }; - return { page: refInfo.page, ref: refInfo.ref, - domain: refInfo.domain, - content, + domain: refInfo.domain }; } @@ -468,19 +388,6 @@ function getDeviceObj() { return { w: window.innerWidth || window.document.documentElement.clientWidth || window.document.body.clientWidth || 0, h: window.innerHeight || window.document.documentElement.clientHeight || window.document.body.clientHeight || 0, - ua: window.navigator.userAgent || undefined, - sua: getSua(), - }; -} - -function getSua() { - let {brands, mobile, platform} = (window?.navigator?.userAgentData || {}); - if (!(brands && platform)) return undefined; - - return { - brands, - mobile: Number(!!mobile), - platform: (platform && {brand: platform}) || undefined, }; } diff --git a/modules/nextrollBidAdapter.js b/modules/nextrollBidAdapter.js index 8a41efe4dcc..0dd4b334f6e 100644 --- a/modules/nextrollBidAdapter.js +++ b/modules/nextrollBidAdapter.js @@ -1,5 +1,6 @@ import { - deepAccess, getBidIdParameter, + deepAccess, + getBidIdParameter, isArray, isFn, isNumber, @@ -14,12 +15,6 @@ import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; import {find} from '../src/polyfill.js'; -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse - * @typedef {import('../src/adapters/bidderFactory.js').validBidRequests} validBidRequests - */ const BIDDER_CODE = 'nextroll'; const BIDDER_ENDPOINT = 'https://d.adroll.com/bid/prebid/'; const ADAPTER_VERSION = 5; diff --git a/modules/nexx360BidAdapter.js b/modules/nexx360BidAdapter.js index baadaa272e6..671cc800980 100644 --- a/modules/nexx360BidAdapter.js +++ b/modules/nexx360BidAdapter.js @@ -8,21 +8,12 @@ import {getGlobal} from '../src/prebidGlobal.js'; import {ortbConverter} from '../libraries/ortbConverter/converter.js' import { INSTREAM, OUTSTREAM } from '../src/video.js'; -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse - * @typedef {import('../src/adapters/bidderFactory.js').SyncOptions} SyncOptions - * @typedef {import('../src/adapters/bidderFactory.js').UserSync} UserSync - * @typedef {import('../src/adapters/bidderFactory.js').validBidRequests} validBidRequests - */ - const OUTSTREAM_RENDERER_URL = 'https://acdn.adnxs.com/video/outstream/ANOutstreamVideo.js'; const BIDDER_CODE = 'nexx360'; const REQUEST_URL = 'https://fast.nexx360.io/booster'; const PAGE_VIEW_ID = generateUUID(); -const BIDDER_VERSION = '3.0'; +const BIDDER_VERSION = '2.0'; const GVLID = 965; const NEXXID_KEY = 'nexx360_storage'; @@ -199,15 +190,9 @@ function interpretResponse(serverResponse) { demandSource: bid.ext.ssp, }, }; - if (allowAlternateBidderCodes) response.bidderCode = `n360_${bid.ext.ssp}`; + if (allowAlternateBidderCodes) response.bidderCode = `n360-${bid.ext.ssp}`; - if (bid.ext.mediaType === BANNER) { - if (bid.adm) { - response.ad = bid.adm; - } else { - response.adUrl = bid.ext.adUrl; - } - } + if (bid.ext.mediaType === BANNER) response.adUrl = bid.ext.adUrl; if ([INSTREAM, OUTSTREAM].includes(bid.ext.mediaType)) response.vastXml = bid.ext.vastXml; if (bid.ext.mediaType === OUTSTREAM) { diff --git a/modules/nobidAnalyticsAdapter.js b/modules/nobidAnalyticsAdapter.js deleted file mode 100644 index 3a7912c37e1..00000000000 --- a/modules/nobidAnalyticsAdapter.js +++ /dev/null @@ -1,242 +0,0 @@ -import {deepClone, logError, getParameterByName} from '../src/utils.js'; -import {ajax} from '../src/ajax.js'; -import {getStorageManager} from '../src/storageManager.js'; -import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; -import CONSTANTS from '../src/constants.json'; -import adapterManager from '../src/adapterManager.js'; -import {MODULE_TYPE_ANALYTICS} from '../src/activities/modules.js'; - -const VERSION = '1.1.0'; -const MODULE_NAME = 'nobidAnalyticsAdapter'; -const ANALYTICS_OPT_FLUSH_TIMEOUT_SECONDS = 5 * 1000; -const RETENTION_SECONDS = 1 * 24 * 3600; -const TEST_ALLOCATION_PERCENTAGE = 5; // dont block 5% of the time; -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 { - EVENTS: { - AUCTION_INIT, - BID_REQUESTED, - BID_TIMEOUT, - BID_RESPONSE, - BID_WON, - AUCTION_END, - AD_RENDER_SUCCEEDED - } -} = CONSTANTS; -function log (msg) { - // eslint-disable-next-line no-console - console.log(`%cNoBid Analytics ${VERSION}`, 'padding: 2px 8px 2px 8px; background-color:#f50057; color: white', msg); -} -function isJson (str) { - return str && str.startsWith('{') && str.endsWith('}'); -} -function isExpired (data, retentionSeconds) { - retentionSeconds = retentionSeconds || RETENTION_SECONDS; - if (data.ts + retentionSeconds * 1000 < Date.now()) return true; - return false; -} -function sendEvent (event, eventType) { - function resolveEndpoint() { - var ret = 'https://carbon-nv.servenobids.com/admin/status'; - var env = (typeof getParameterByName === 'function') && (getParameterByName('nobid-env')); - env = window.location.href.indexOf('nobid-env=dev') > 0 ? 'dev' : env; - if (!env) ret = 'https://carbon-nv.servenobids.com'; - else if (env == 'dev') ret = 'https://localhost:8383'; - return ret; - } - if (!nobidAnalytics.initOptions || !nobidAnalytics.initOptions.siteId || !event) return; - if (nobidAnalytics.isAnalyticsDisabled()) { - log('NoBid Analytics is Disabled'); - return; - } - try { - const endpoint = `${resolveEndpoint()}/event/${eventType}?pubid=${nobidAnalytics.initOptions.siteId}`; - ajax(endpoint, - function (response) { - try { - nobidAnalytics.processServerResponse(response); - } catch (e) { - logError(e); - } - }, - JSON.stringify(event), - { - contentType: 'application/json', - method: 'POST' - } - ); - } catch (err) { - log(`Sending event error ${err}`); - } -} -function cleanupObjectAttributes (obj, attributes) { - if (!obj) return; - if (Array.isArray(obj)) { - obj.forEach(item => { - Object.keys(item).forEach(attr => { if (!attributes.includes(attr)) delete item[attr] }); - }); - } else Object.keys(obj).forEach(attr => { if (!attributes.includes(attr)) delete obj[attr] }); -} -function sendBidWonEvent (event, eventType) { - const data = deepClone(event); - cleanupObjectAttributes(data, ['bidderCode', 'size', 'statusMessage', 'adId', 'requestId', 'mediaType', 'adUnitCode', 'cpm', 'timeToRespond']); - if (nobidAnalytics.topLocation) data.topLocation = nobidAnalytics.topLocation; - sendEvent(data, eventType); -} -function sendAuctionEndEvent (event, eventType) { - if (event?.bidderRequests?.length > 0 && event?.bidderRequests[0]?.refererInfo?.topmostLocation) { - nobidAnalytics.topLocation = event.bidderRequests[0].refererInfo.topmostLocation; - } - const data = deepClone(event); - - cleanupObjectAttributes(data, ['timestamp', 'timeout', 'auctionId', 'bidderRequests', 'bidsReceived']); - if (data) cleanupObjectAttributes(data.bidderRequests, ['bidderCode', 'bidderRequestId', 'bids', 'refererInfo']); - if (data) cleanupObjectAttributes(data.bidsReceived, ['bidderCode', 'width', 'height', 'adUnitCode', 'statusMessage', 'requestId', 'mediaType', 'cpm']); - if (data) cleanupObjectAttributes(data.noBids, ['bidder', 'sizes', 'bidId']); - if (data.bidderRequests) cleanupObjectAttributes(data.bidderRequests.bids, ['mediaTypes', 'adUnitCode', 'sizes', 'bidId']); - if (data.bidderRequests) cleanupObjectAttributes(data.bidderRequests.refererInfo, ['topmostLocation']); - sendEvent(data, eventType); -} -function auctionInit (event) { - if (event?.bidderRequests?.length > 0 && event?.bidderRequests[0]?.refererInfo?.topmostLocation) { - nobidAnalytics.topLocation = event.bidderRequests[0].refererInfo.topmostLocation; - } -} -let nobidAnalytics = Object.assign(adapter({url, analyticsType}), { - track({ eventType, args }) { - switch (eventType) { - case AUCTION_INIT: - auctionInit(args); - break; - case BID_REQUESTED: - break; - case BID_RESPONSE: - break; - case BID_WON: - sendBidWonEvent(args, eventType); - break; - case BID_TIMEOUT: - break; - case AUCTION_END: - sendAuctionEndEvent(args, eventType); - break; - case AD_RENDER_SUCCEEDED: - break; - default: - break; - } - } -}); - -nobidAnalytics = { - ...nobidAnalytics, - originEnableAnalytics: nobidAnalytics.enableAnalytics, // save the base class function - enableAnalytics: function (config) { // override enableAnalytics so we can get access to the config passed in from the page - if (!config.options.siteId) { - logError('NoBid Analytics - siteId parameter is not defined. Analytics won\'t work'); - return; - } - this.initOptions = config.options; - this.originEnableAnalytics(config); // call the base class function - }, - retentionSeconds: RETENTION_SECONDS, - isExpired (data) { - return isExpired(data, this.retentionSeconds); - }, - isAnalyticsDisabled () { - let stored = storage.getDataFromLocalStorage(this.ANALYTICS_DATA_NAME); - if (!isJson(stored)) return false; - stored = JSON.parse(stored); - if (this.isExpired(stored)) return false; - return stored.disabled; - }, - processServerResponse (response) { - if (!isJson(response)) return; - const resp = JSON.parse(response); - storage.setDataInLocalStorage(this.ANALYTICS_DATA_NAME, JSON.stringify({ ...resp, ts: Date.now() })); - }, - ANALYTICS_DATA_NAME: 'analytics.nobid.io', - ANALYTICS_OPT_NAME: 'analytics.nobid.io.optData' -} - -adapterManager.registerAnalyticsAdapter({ - adapter: nobidAnalytics, - code: 'nobidAnalytics', - gvlid: GVLID -}); -nobidAnalytics.originalAdUnits = {}; -window.nobidCarbonizer = { - getStoredLocalData: function () { - const a = storage.getDataFromLocalStorage(nobidAnalytics.ANALYTICS_DATA_NAME); - const b = storage.getDataFromLocalStorage(nobidAnalytics.ANALYTICS_OPT_NAME); - const ret = {}; - if (a) ret[nobidAnalytics.ANALYTICS_DATA_NAME] = a; - if (b) ret[nobidAnalytics.ANALYTICS_OPT_NAME] = b - return ret; - }, - isActive: function () { - let stored = storage.getDataFromLocalStorage(nobidAnalytics.ANALYTICS_DATA_NAME); - if (!isJson(stored)) return false; - stored = JSON.parse(stored); - if (isExpired(stored, nobidAnalytics.retentionSeconds)) return false; - return stored.carbonizer_active || false; - }, - carbonizeAdunits: function (adunits, skipTestGroup) { - function processBlockedBidders (blockedBidders) { - function sendOptimizerData() { - let optData = storage.getDataFromLocalStorage(nobidAnalytics.ANALYTICS_OPT_NAME); - storage.removeDataFromLocalStorage(nobidAnalytics.ANALYTICS_OPT_NAME); - if (isJson(optData)) { - optData = JSON.parse(optData); - if (Object.getOwnPropertyNames(optData).length > 0) { - const event = { o_bidders: optData }; - if (nobidAnalytics.topLocation) event.topLocation = nobidAnalytics.topLocation; - sendEvent(event, 'optData'); - } - } - } - if (blockedBidders && blockedBidders.length > 0) { - let optData = storage.getDataFromLocalStorage(nobidAnalytics.ANALYTICS_OPT_NAME); - optData = isJson(optData) ? JSON.parse(optData) : {}; - const bidders = blockedBidders.map(rec => rec.bidder); - if (bidders && bidders.length > 0) { - bidders.forEach(bidder => { - if (!optData[bidder]) optData[bidder] = 1; - else optData[bidder] += 1; - }); - storage.setDataInLocalStorage(nobidAnalytics.ANALYTICS_OPT_NAME, JSON.stringify(optData)); - if (window.nobidAnalyticsOptTimer) return; - window.nobidAnalyticsOptTimer = setInterval(sendOptimizerData, ANALYTICS_OPT_FLUSH_TIMEOUT_SECONDS); - } - } - } - function carbonizeAdunit (adunit) { - let stored = storage.getDataFromLocalStorage(nobidAnalytics.ANALYTICS_DATA_NAME); - if (!isJson(stored)) return; - stored = JSON.parse(stored); - if (isExpired(stored, nobidAnalytics.retentionSeconds)) return; - const carbonizerBidders = stored.bidders || []; - let originalAdUnit = null; - if (nobidAnalytics.originalAdUnits && nobidAnalytics.originalAdUnits[adunit.code]) originalAdUnit = nobidAnalytics.originalAdUnits[adunit.code]; - const allowedBidders = originalAdUnit.bids.filter(rec => carbonizerBidders.includes(rec.bidder)); - const blockedBidders = originalAdUnit.bids.filter(rec => !carbonizerBidders.includes(rec.bidder)); - processBlockedBidders(blockedBidders); - adunit.bids = allowedBidders; - } - for (const adunit of adunits) { - if (!nobidAnalytics.originalAdUnits[adunit.code]) nobidAnalytics.originalAdUnits[adunit.code] = JSON.parse(JSON.stringify(adunit)); - }; - if (this.isActive()) { - // 5% of the time do not block; - if (!skipTestGroup && Math.floor(Math.random() * 101) <= TEST_ALLOCATION_PERCENTAGE) return; - for (const adunit of adunits) { - carbonizeAdunit(adunit); - }; - } - } -}; -export default nobidAnalytics; diff --git a/modules/nobidAnalyticsAdapter.md b/modules/nobidAnalyticsAdapter.md deleted file mode 100644 index 92b9bdbb3cb..00000000000 --- a/modules/nobidAnalyticsAdapter.md +++ /dev/null @@ -1,38 +0,0 @@ -# Overview -Module Name: NoBid Analytics Adapter - -Module Type: Analytics Adapter - -Maintainer: [nobid.io](https://nobid.io) - - -# NoBid Analytics Registration - -The NoBid Analytics Adapter is free to use during our Beta period, but requires a simple registration with NoBid. Please visit [www.nobid.io](https://www.nobid.io/contact-1/) to sign up and request your NoBid Site ID to get started. If you're already using the NoBid Prebid Adapter, you may use your existing Site ID with the NoBid Analytics Adapter. - -The NoBid privacy policy is at [nobid.io/privacy-policy](https://www.nobid.io/privacy-policy/). - -## NoBid Analytics Configuration - -First, make sure to add the NoBid Analytics submodule to your Prebid.js package with: - -``` -gulp build --modules=...,nobidAnalyticsAdapter... -``` - -The following configuration parameters are available: - -```javascript -pbjs.enableAnalytics({ - provider: 'nobidAnalytics', - options: { - siteId: 123 // change to the Site ID you received from NoBid - } -}); -``` - -{: .table .table-bordered .table-striped } -| Parameter | Scope | Type | Description | Example | -| --- | --- | --- | --- | --- | -| provider | Required | String | The name of this module: `nobidAnalytics` | `nobidAnalytics` | -| options.siteId | Required | Number | This is the NoBid Site ID Number obtained from registering with NoBid. | `1234` | diff --git a/modules/nobidBidAdapter.js b/modules/nobidBidAdapter.js index 28fb38e14e5..7bf1c4b80db 100644 --- a/modules/nobidBidAdapter.js +++ b/modules/nobidBidAdapter.js @@ -3,16 +3,7 @@ import { config } from '../src/config.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; import { getStorageManager } from '../src/storageManager.js'; -import { hasPurpose1Consent } from '../src/utils/gpdr.js'; - -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse - * @typedef {import('../src/adapters/bidderFactory.js').SyncOptions} SyncOptions - * @typedef {import('../src/adapters/bidderFactory.js').UserSync} UserSync - * @typedef {import('../src/adapters/bidderFactory.js').validBidRequests} validBidRequests - */ +import {hasPurpose1Consent} from '../src/utils/gpdr.js'; const GVLID = 816; const BIDDER_CODE = 'nobid'; @@ -72,19 +63,6 @@ function nobidBuildRequests(bids, bidderRequest) { } return uspConsent; } - var gppConsent = function(bidderRequest) { - let gppConsent = null; - if (bidderRequest?.gppConsent?.gppString && bidderRequest?.gppConsent?.applicableSections) { - gppConsent = {}; - gppConsent.gpp = bidderRequest.gppConsent.gppString; - gppConsent.gpp_sid = Array.isArray(bidderRequest.gppConsent.applicableSections) ? bidderRequest.gppConsent.applicableSections : []; - } else if (bidderRequest?.ortb2?.regs?.gpp && bidderRequest?.ortb2.regs?.gpp_sid) { - gppConsent = {}; - gppConsent.gpp = bidderRequest.ortb2.regs.gpp; - gppConsent.gpp_sid = Array.isArray(bidderRequest.ortb2.regs.gpp_sid) ? bidderRequest.ortb2.regs.gpp_sid : []; - } - return gppConsent; - } var schain = function(bids) { if (bids && bids.length > 0) { return bids[0].schain @@ -167,9 +145,6 @@ function nobidBuildRequests(bids, bidderRequest) { if (cop) state['coppa'] = cop; const eids = getEIDs(deepAccess(bids, '0.userIdAsEids')); if (eids && eids.length > 0) state['eids'] = eids; - const gpp = gppConsent(bidderRequest); - if (gpp?.gpp) state['gpp'] = gpp.gpp; - if (gpp?.gpp_sid) state['gpp_sid'] = gpp.gpp_sid; if (bidderRequest && bidderRequest.ortb2) state['ortb2'] = bidderRequest.ortb2; return state; }; @@ -255,9 +230,9 @@ function nobidBuildRequests(bids, bidderRequest) { siteId = (typeof bid.params['siteId'] != 'undefined' && bid.params['siteId']) ? bid.params['siteId'] : siteId; var placementId = bid.params['placementId']; - let adType = 'banner'; + var adType = 'banner'; const videoMediaType = deepAccess(bid, 'mediaTypes.video'); - const context = deepAccess(bid, 'mediaTypes.video.context') || ''; + const context = deepAccess(bid, 'mediaTypes.video.context'); if (bid.mediaType === VIDEO || (videoMediaType && (context === 'instream' || context === 'outstream'))) { adType = 'video'; } @@ -271,8 +246,7 @@ function nobidBuildRequests(bids, bidderRequest) { placementId: placementId, ad_type: adType, params: bid.params, - floor: floor, - ctx: context + floor: floor }, adunits); } @@ -376,26 +350,25 @@ export const spec = { ], supportedMediaTypes: [BANNER, VIDEO], /** - * Determines whether or not the given bid request is valid. - * - * @param {BidRequest} bid The bid params to validate. - * @return boolean True if this is a valid bid, and false otherwise. - */ + * Determines whether or not the given bid request is valid. + * + * @param {BidRequest} bid The bid params to validate. + * @return boolean True if this is a valid bid, and false otherwise. + */ isBidRequestValid: function(bid) { log('isBidRequestValid', bid); return !!bid.params.siteId; }, /** - * Make a server request from the list of BidRequests. - * - * @param {validBidRequests[]} - an array of bids - * @return ServerRequest Info describing the request to the server. - */ + * Make a server request from the list of BidRequests. + * + * @param {validBidRequests[]} - an array of bids + * @return ServerRequest Info describing the request to the server. + */ buildRequests: function(validBidRequests, bidderRequest) { function resolveEndpoint() { var ret = 'https://ads.servenobid.com/'; var env = (typeof getParameterByName === 'function') && (getParameterByName('nobid-env')); - env = window.location.href.indexOf('nobid-env=dev') > 0 ? 'dev' : env; if (!env) ret = 'https://ads.servenobid.com/'; else if (env == 'beta') ret = 'https://beta.servenobid.com/'; else if (env == 'dev') ret = '//localhost:8282/'; @@ -430,11 +403,11 @@ export const spec = { }; }, /** - * Unpack the 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. - */ + * Unpack the 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. + */ interpretResponse: function(serverResponse, bidRequest) { log('interpretResponse -> serverResponse', serverResponse); log('interpretResponse -> bidRequest', bidRequest); @@ -442,13 +415,13 @@ export const spec = { }, /** - * Register the user sync pixels which should be dropped after the auction. - * - * @param {SyncOptions} syncOptions Which user syncs are allowed? - * @param {ServerResponse[]} serverResponses List of server's responses. - * @return {UserSync[]} The user syncs which should be dropped. - */ - getUserSyncs: function(syncOptions, serverResponses, gdprConsent, usPrivacy, gppConsent) { + * Register the user sync pixels which should be dropped after the auction. + * + * @param {SyncOptions} syncOptions Which user syncs are allowed? + * @param {ServerResponse[]} serverResponses List of server's responses. + * @return {UserSync[]} The user syncs which should be dropped. + */ + getUserSyncs: function(syncOptions, serverResponses, gdprConsent, usPrivacy) { if (syncOptions.iframeEnabled) { let params = ''; if (gdprConsent && typeof gdprConsent.consentString === 'string') { @@ -464,12 +437,6 @@ export const spec = { else params += '?'; params += 'usp_consent=' + usPrivacy; } - if (gppConsent?.gppString && gppConsent?.applicableSections?.length) { - if (params.length > 0) params += '&'; - else params += '?'; - params += 'gpp=' + encodeURIComponent(gppConsent.gppString); - params += 'gpp_sid=' + encodeURIComponent(gppConsent.applicableSections.join(',')); - } return [{ type: 'iframe', url: 'https://public.servenobid.com/sync.html' + params @@ -492,9 +459,9 @@ export const spec = { }, /** - * Register bidder specific code, which will execute if bidder timed out after an auction - * @param {data} Containing timeout specific data - */ + * Register bidder specific code, which will execute if bidder timed out after an auction + * @param {data} Containing timeout specific data + */ onTimeout: function(data) { window.nobid.timeoutTotal++; log('Timeout total: ' + window.nobid.timeoutTotal, data); diff --git a/modules/nobidBidAdapter.md b/modules/nobidBidAdapter.md index e2f1c75e782..4449ad5c88b 100644 --- a/modules/nobidBidAdapter.md +++ b/modules/nobidBidAdapter.md @@ -7,7 +7,6 @@ hide: true media_types: banner, video gdpr_supported: true usp_supported: true -gpp_supported: true --- ### Bid Params diff --git a/modules/novatiqIdSystem.js b/modules/novatiqIdSystem.js index b6eab776df2..7a801a945ae 100644 --- a/modules/novatiqIdSystem.js +++ b/modules/novatiqIdSystem.js @@ -11,20 +11,15 @@ 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 - * @typedef {import('../modules/userId/index.js').SubmoduleConfig} SubmoduleConfig - */ - const MODULE_NAME = 'novatiq'; /** @type {Submodule} */ export const novatiqIdSubmodule = { /** - * used to link submodule with config - * @type {string} - */ + * used to link submodule with config + * @type {string} + */ name: MODULE_NAME, /** * used to specify vendor id @@ -33,10 +28,10 @@ export const novatiqIdSubmodule = { gvlid: 1119, /** - * decode the stored id value for passing to bid requests - * @function - * @returns {novatiq: {snowflake: string}} - */ + * decode the stored id value for passing to bid requests + * @function + * @returns {novatiq: {snowflake: string}} + */ decode(novatiqId, config) { let responseObj = { novatiq: { @@ -57,11 +52,11 @@ export const novatiqIdSubmodule = { }, /** - * performs action to obtain id and return a value in the callback's response argument - * @function - * @param {SubmoduleConfig} config - * @returns {id: string} - */ + * performs action to obtain id and return a value in the callback's response argument + * @function + * @param {SubmoduleConfig} config + * @returns {id: string} + */ getId(config) { const configParams = config.params || {}; const urlParams = this.getUrlParams(configParams); @@ -264,18 +259,6 @@ export const novatiqIdSubmodule = { srcId = configParams.sourceid; } return srcId; - }, - eids: { - 'novatiq': { - getValue: function(data) { - if (data.snowflake.id === undefined) { - return data.snowflake; - } - - return data.snowflake.id; - }, - source: 'novatiq.com', - }, } }; submodule('userId', novatiqIdSubmodule); diff --git a/modules/oguryBidAdapter.js b/modules/oguryBidAdapter.js index 9937391f6e7..718ae44c83c 100644 --- a/modules/oguryBidAdapter.js +++ b/modules/oguryBidAdapter.js @@ -1,10 +1,9 @@ 'use strict'; import {BANNER} from '../src/mediaTypes.js'; -import {getWindowSelf, getWindowTop, isFn, logWarn} from '../src/utils.js'; +import {getAdUnitSizes, getWindowSelf, getWindowTop, isFn, logWarn} from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {ajax} from '../src/ajax.js'; -import {getAdUnitSizes} from '../libraries/sizeUtils/sizeUtils.js'; const BIDDER_CODE = 'ogury'; const GVLID = 31; @@ -12,7 +11,7 @@ const DEFAULT_TIMEOUT = 1000; const BID_HOST = 'https://mweb-hb.presage.io/api/header-bidding-request'; const TIMEOUT_MONITORING_HOST = 'https://ms-ads-monitoring-events.presage.io'; const MS_COOKIE_SYNC_DOMAIN = 'https://ms-cookie-sync.presage.io'; -const ADAPTER_VERSION = '1.6.0'; +const ADAPTER_VERSION = '1.4.1'; function getClientWidth() { const documentElementClientWidth = window.top.document.documentElement.clientWidth @@ -47,35 +46,22 @@ function isBidRequestValid(bid) { } function getUserSyncs(syncOptions, serverResponses, gdprConsent, uspConsent) { - const consent = (gdprConsent && gdprConsent.consentString) || ''; + if (!syncOptions.pixelEnabled) return []; - if (syncOptions.iframeEnabled) { - return [ - { - type: 'iframe', - url: `${MS_COOKIE_SYNC_DOMAIN}/user-sync.html?gdpr_consent=${consent}&source=prebid` - } - ]; - } - - if (syncOptions.pixelEnabled) { - return [ - { - type: 'image', - url: `${MS_COOKIE_SYNC_DOMAIN}/v1/init-sync/bid-switch?iab_string=${consent}&source=prebid` - }, - { - type: 'image', - url: `${MS_COOKIE_SYNC_DOMAIN}/ttd/init-sync?iab_string=${consent}&source=prebid` - }, - { - type: 'image', - url: `${MS_COOKIE_SYNC_DOMAIN}/xandr/init-sync?iab_string=${consent}&source=prebid` - } - ]; - } - - return []; + return [ + { + type: 'image', + url: `${MS_COOKIE_SYNC_DOMAIN}/v1/init-sync/bid-switch?iab_string=${(gdprConsent && gdprConsent.consentString) || ''}&source=prebid` + }, + { + type: 'image', + url: `${MS_COOKIE_SYNC_DOMAIN}/ttd/init-sync?iab_string=${(gdprConsent && gdprConsent.consentString) || ''}&source=prebid` + }, + { + type: 'image', + url: `${MS_COOKIE_SYNC_DOMAIN}/xandr/init-sync?iab_string=${(gdprConsent && gdprConsent.consentString) || ''}&source=prebid` + } + ] } function buildRequests(validBidRequests, bidderRequest) { @@ -122,13 +108,6 @@ function buildRequests(validBidRequests, bidderRequest) { openRtbBidRequestBanner.site.id = bidRequest.params.assetKey; const floor = getFloor(bidRequest); - if (bidRequest.userId) { - openRtbBidRequestBanner.user.ext.uids = bidRequest.userId - } - if (bidRequest.userIdAsEids) { - openRtbBidRequestBanner.user.ext.eids = bidRequest.userIdAsEids - } - openRtbBidRequestBanner.imp.push({ id: bidRequest.bidId, tagid: bidRequest.params.adUnitId, diff --git a/modules/oneKeyIdSystem.js b/modules/oneKeyIdSystem.js index 8765a72a1af..8d8d594b533 100644 --- a/modules/oneKeyIdSystem.js +++ b/modules/oneKeyIdSystem.js @@ -8,13 +8,6 @@ import {submodule} from '../src/hook.js'; import { logError, logMessage } from '../src/utils.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 - */ - // Pre-init OneKey if it has not load yet. window.OneKey = window.OneKey || {}; window.OneKey.queue = window.OneKey.queue || []; @@ -54,57 +47,31 @@ const getIdsAndPreferences = (callback) => { /** @type {Submodule} */ export const oneKeyIdSubmodule = { /** - * used to link submodule with config - * @type {string} - */ + * used to link submodule with config + * @type {string} + */ name: 'oneKeyData', /** - * decode the stored data value for passing to bid requests - * @function decode - * @param {(Object|string)} value - * @returns {(Object|undefined)} - */ + * decode the stored data value for passing to bid requests + * @function decode + * @param {(Object|string)} value + * @returns {(Object|undefined)} + */ decode(data) { return { oneKeyData: data }; }, /** - * performs action to obtain id and return a value in the callback's response argument - * @function - * @param {SubmoduleConfig} [config] - * @param {ConsentData} [consentData] - * @param {(Object|undefined)} cacheIdObj - * @returns {IdResponse|undefined} - */ + * performs action to obtain id and return a value in the callback's response argument + * @function + * @param {SubmoduleConfig} [config] + * @param {ConsentData} [consentData] + * @param {(Object|undefined)} cacheIdObj + * @returns {IdResponse|undefined} + */ getId(config) { return { callback: getIdsAndPreferences }; - }, - eids: { - 'oneKeyData': { - getValue: function(data) { - if (data && Array.isArray(data.identifiers) && data.identifiers[0]) { - return data.identifiers[0].value; - } - }, - source: 'paf', - atype: 1, - getEidExt: function(data) { - if (data && data.preferences) { - return {preferences: data.preferences}; - } - }, - getUidExt: function(data) { - if (data && Array.isArray(data.identifiers) && data.identifiers[0]) { - const id = data.identifiers[0]; - return { - version: id.version, - type: id.type, - source: id.source - }; - } - } - } } }; diff --git a/modules/oneKeyRtdProvider.js b/modules/oneKeyRtdProvider.js index 19915609820..27511017676 100644 --- a/modules/oneKeyRtdProvider.js +++ b/modules/oneKeyRtdProvider.js @@ -3,10 +3,6 @@ import { submodule } from '../src/hook.js'; import { mergeDeep, logError, logMessage, deepSetValue, generateUUID } from '../src/utils.js'; import { getGlobal } from '../src/prebidGlobal.js'; -/** - * @typedef {import('../modules/rtdModule/index.js').RtdSubmodule} RtdSubmodule - */ - const SUBMODULE_NAME = 'oneKey'; const prefixLog = 'OneKey.RTD-module' diff --git a/modules/onetagBidAdapter.js b/modules/onetagBidAdapter.js index eb9fa2eb536..724a53a3095 100644 --- a/modules/onetagBidAdapter.js +++ b/modules/onetagBidAdapter.js @@ -8,11 +8,6 @@ import { getStorageManager } from '../src/storageManager.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { deepClone, logError, deepAccess } from '../src/utils.js'; -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').validBidRequests} validBidRequests - */ - const ENDPOINT = 'https://onetag-sys.com/prebid-request'; const USER_SYNC_ENDPOINT = 'https://onetag-sys.com/usync/'; const BIDDER_CODE = 'onetag'; @@ -75,9 +70,6 @@ function buildRequests(validBidRequests, bidderRequest) { if (bidderRequest && bidderRequest.uspConsent) { payload.usPrivacy = bidderRequest.uspConsent; } - if (bidderRequest && bidderRequest.ortb2) { - payload.ortb2 = bidderRequest.ortb2; - } if (validBidRequests && validBidRequests.length !== 0 && validBidRequests[0].userIdAsEids) { payload.userId = validBidRequests[0].userIdAsEids; } @@ -92,7 +84,6 @@ function buildRequests(validBidRequests, bidderRequest) { const connection = navigator.connection || navigator.webkitConnection; payload.networkConnectionType = (connection && connection.type) ? connection.type : null; payload.networkEffectiveConnectionType = (connection && connection.effectiveType) ? connection.effectiveType : null; - payload.fledgeEnabled = Boolean(bidderRequest && bidderRequest.fledgeEnabled) return { method: 'POST', url: ENDPOINT, @@ -107,10 +98,10 @@ 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 => { + body.bids.forEach(bid => { const responseBid = { requestId: bid.requestId, cpm: bid.cpm, @@ -147,16 +138,7 @@ function interpretResponse(serverResponse, bidderRequest) { } bids.push(responseBid); }); - - if (body.fledgeAuctionConfigs && Array.isArray(body.fledgeAuctionConfigs)) { - const fledgeAuctionConfigs = body.fledgeAuctionConfigs - return { - bids, - fledgeAuctionConfigs, - } - } else { - return bids; - } + return bids; } function createRenderer(bid, rendererOptions = {}) { @@ -282,8 +264,9 @@ function setGeneralInfo(bidRequest) { this['adUnitCode'] = bidRequest.adUnitCode; this['bidId'] = bidRequest.bidId; this['bidderRequestId'] = bidRequest.bidderRequestId; - this['auctionId'] = deepAccess(bidRequest, 'ortb2.source.tid'); - this['transactionId'] = deepAccess(bidRequest, 'ortb2Imp.ext.tid'); + // TODO: fix auctionId leak: https://github.com/prebid/Prebid.js/issues/9781 + this['auctionId'] = bidRequest.auctionId; + this['transactionId'] = bidRequest.ortb2Imp?.ext?.tid; this['gpid'] = deepAccess(bidRequest, 'ortb2Imp.ext.gpid') || deepAccess(bidRequest, 'ortb2Imp.ext.data.pbadslot'); this['pubId'] = params.pubId; this['ext'] = params.ext; diff --git a/modules/onomagicBidAdapter.js b/modules/onomagicBidAdapter.js index 78f00153a8b..edab625e541 100644 --- a/modules/onomagicBidAdapter.js +++ b/modules/onomagicBidAdapter.js @@ -1,6 +1,7 @@ import { _each, - createTrackPixelHtml, getBidIdParameter, + createTrackPixelHtml, + getBidIdParameter, getUniqueIdentifierStr, getWindowSelf, getWindowTop, diff --git a/modules/open8BidAdapter.js b/modules/open8BidAdapter.js index 49523926c0e..5fa1dd0a143 100644 --- a/modules/open8BidAdapter.js +++ b/modules/open8BidAdapter.js @@ -1,9 +1,8 @@ import { Renderer } from '../src/Renderer.js'; import {ajax} from '../src/ajax.js'; -import {createTrackPixelHtml, getBidIdParameter, logError, logWarn} from '../src/utils.js'; +import { createTrackPixelHtml, getBidIdParameter, logError, logWarn, tryAppendQueryString } 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'; const BIDDER_CODE = 'open8'; const URL = 'https://as.vt.open8.com/v1/control/prebid'; diff --git a/modules/openwebBidAdapter.js b/modules/openwebBidAdapter.js index 547447039da..296bfc682f1 100644 --- a/modules/openwebBidAdapter.js +++ b/modules/openwebBidAdapter.js @@ -1,9 +1,8 @@ -import {deepAccess, flatten, isArray, isNumber, parseSizesInput} from '../src/utils.js'; +import {convertTypes, deepAccess, flatten, isArray, isNumber, 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 {find} from '../src/polyfill.js'; -import {convertTypes} from '../libraries/transformParamsUtils/convertTypes.js'; const ENDPOINT = 'https://ghb.spotim.market/v2/auction'; const BIDDER_CODE = 'openweb'; diff --git a/modules/openxBidAdapter.js b/modules/openxBidAdapter.js index 0f8bee213f7..03423a028b4 100644 --- a/modules/openxBidAdapter.js +++ b/modules/openxBidAdapter.js @@ -4,7 +4,6 @@ import * as utils from '../src/utils.js'; import {mergeDeep} from '../src/utils.js'; import {BANNER, VIDEO} from '../src/mediaTypes.js'; import {ortbConverter} from '../libraries/ortbConverter/converter.js'; -import {convertTypes} from '../libraries/transformParamsUtils/convertTypes.js'; const bidderConfig = 'hb_pb_ortb'; const bidderVersion = '2.0'; @@ -13,7 +12,6 @@ export const SYNC_URL = 'https://u.openx.net/w/1.0/pd'; export const DEFAULT_PH = '2d1251ae-7f3a-47cf-bd2a-2f288854a0ba'; export const spec = { code: 'openx', - gvlid: 69, supportedMediaTypes: [BANNER, VIDEO], isBidRequestValid, buildRequests, @@ -50,8 +48,7 @@ const converter = ortbConverter({ mergeDeep(req, { at: 1, ext: { - bc: `${bidderConfig}_${bidderVersion}`, - pv: '$prebid.version$' + bc: `${bidderConfig}_${bidderVersion}` } }) const bid = context.bidRequests[0]; @@ -152,7 +149,7 @@ const converter = ortbConverter({ }); function transformBidParams(params, isOpenRtb) { - return convertTypes({ + return utils.convertTypes({ 'unit': 'string', 'customFloor': 'number' }, params); diff --git a/modules/operaadsBidAdapter.js b/modules/operaadsBidAdapter.js index 957192d1bec..e721fb85fd7 100644 --- a/modules/operaadsBidAdapter.js +++ b/modules/operaadsBidAdapter.js @@ -18,13 +18,6 @@ import {Renderer} from '../src/Renderer.js'; import {OUTSTREAM} from '../src/video.js'; import {convertOrtbRequestToProprietaryNative} from '../src/native.js'; -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse - * @typedef {import('../src/adapters/bidderFactory.js').SyncOptions} SyncOptions - * @typedef {import('../src/adapters/bidderFactory.js').UserSync} UserSync - */ const BIDDER_CODE = 'operaads'; const ENDPOINT = 'https://s.adx.opera.com/ortb/v2/'; @@ -77,7 +70,7 @@ const NATIVE_DEFAULTS = { export const spec = { code: BIDDER_CODE, - gvlid: 1135, + // short code aliases: ['opera'], @@ -239,6 +232,13 @@ function buildOpenRtbBidRequest(bidRequest, bidderRequest) { test: config.getConfig('debug') ? 1 : 0, imp: createImp(bidRequest), device: getDevice(), + site: { + id: String(deepAccess(bidRequest, 'params.publisherId')), + // TODO: does the fallback make sense here? + domain: bidderRequest?.refererInfo?.domain || window.location.host, + page: bidderRequest?.refererInfo?.page, + ref: bidderRequest?.refererInfo?.ref || '', + }, at: 1, bcat: getBcat(bidRequest), cur: [DEFAULT_CURRENCY], @@ -250,7 +250,6 @@ function buildOpenRtbBidRequest(bidRequest, bidderRequest) { buyeruid: getUserId(bidRequest) } } - fulfillInventoryInfo(payload, bidRequest, bidderRequest); const gdprConsent = deepAccess(bidderRequest, 'gdprConsent'); if (!!gdprConsent && gdprConsent.gdprApplies) { @@ -681,11 +680,6 @@ function mapNativeImage(image, type) { * @returns {String} userId */ function getUserId(bidRequest) { - let operaId = deepAccess(bidRequest, 'userId.operaId'); - if (operaId) { - return operaId; - } - let sharedId = deepAccess(bidRequest, 'userId.sharedid.id'); if (sharedId) { return sharedId; @@ -765,38 +759,6 @@ function getDevice() { return device; } -/** - * Fulfill inventory info - * - * @param payload - * @param bidRequest - * @param bidderRequest - */ -function fulfillInventoryInfo(payload, bidRequest, bidderRequest) { - let info = deepAccess(bidRequest, 'params.site'); - // 1.If the inventory info for site specified, use the site object provided in params. - let key = 'site'; - if (!isPlainObject(info)) { - info = deepAccess(bidRequest, 'params.app'); - if (isPlainObject(info)) { - // 2.If the inventory info for app specified, use the app object provided in params. - key = 'app'; - } else { - // 3.Otherwise, we use site by default. - info = {}; - } - } - // Fulfill key parameters. - info.id = String(deepAccess(bidRequest, 'params.publisherId')); - info.domain = info.domain || bidderRequest?.refererInfo?.domain || window.location.host; - if (key === 'site') { - info.ref = info.ref || bidderRequest?.refererInfo?.ref || ''; - info.page = info.page || bidderRequest?.refererInfo?.page; - } - - payload[key] = info; -} - /** * Get browser language * diff --git a/modules/operaadsBidAdapter.md b/modules/operaadsBidAdapter.md index 6f13eebd7d5..709c67a04a7 100644 --- a/modules/operaadsBidAdapter.md +++ b/modules/operaadsBidAdapter.md @@ -14,43 +14,41 @@ Module that connects to OperaAds's demand sources ## Bid Parameters -| Name | Scope | Type | Description | Example | -|---------------|----------|--------------------|-----------------------------------------|-------------------------------------------------| -| `placementId` | required | String | The Placement Id provided by Opera Ads. | `s5340077725248` | -| `endpointId` | required | String | The Endpoint Id provided by Opera Ads. | `ep3425464070464` | -| `publisherId` | required | String | The Publisher Id provided by Opera Ads. | `pub3054952966336` | -| `bcat` | optional | String or String[] | The bcat value. | `IAB9-31` | -| `site` | optional | Object | The site information. | `{"name": "my_site", "domain": "www.test.com"}` | -| `app` | optional | Object | The app information. | `{"name": "my_app", "ver": "1.1.0"}` | +| Name | Scope | Type | Description | Example +| ---- | ----- | ---- | ----------- | ------- +| `placementId` | required | String | The Placement Id provided by Opera Ads. | `s5340077725248` +| `endpointId` | required | String | The Endpoint Id provided by Opera Ads. | `ep3425464070464` +| `publisherId` | required | String | The Publisher Id provided by Opera Ads. | `pub3054952966336` +| `bcat` | optional | String or String[] | The bcat value. | `IAB9-31` ### Bid Video Parameters Set these parameters to `bid.mediaTypes.video`. -| Name | Scope | Type | Description | Example | -|------------------|----------|------------------------|------------------------------------------------------------------------------------------|----------------------------| -| `context` | optional | String | `instream` or `outstream`. | `instream` | -| `mimes` | optional | String[] | Content MIME types supported. | `['video/mp4']` | -| `playerSize` | optional | Number[] or Number[][] | Video player size in device independent pixels | `[[640, 480]]` | -| `protocols` | optional | Number[] | Array of supported video protocls. | `[1, 2, 3, 4, 5, 6, 7, 8]` | -| `startdelay` | optional | Number | Indicates the start delay in seconds for pre-roll, mid-roll, or post-roll ad placements. | `0` | -| `skip` | optional | Number | Indicates if the player will allow the video to be skipped, where 0 = no, 1 = yes. | `1` | -| `playbackmethod` | optional | Number[] | Playback methods that may be in use. | `[2]` | -| `delivery` | optional | Number[] | Supported delivery methods. | `[1]` | -| `api` | optional | Number[] | List of supported API frameworks for this impression. | `[1, 2, 5]` | +| Name | Scope | Type | Description | Example +| ---- | ----- | ---- | ----------- | ------- +| `context` | optional | String | `instream` or `outstream`. | `instream` +| `mimes` | optional | String[] | Content MIME types supported. | `['video/mp4']` +| `playerSize` | optional | Number[] or Number[][] | Video player size in device independent pixels | `[[640, 480]]` +| `protocols` | optional | Number[] | Array of supported video protocls. | `[1, 2, 3, 4, 5, 6, 7, 8]` +| `startdelay` | optional | Number | Indicates the start delay in seconds for pre-roll, mid-roll, or post-roll ad placements. | `0` +| `skip` | optional | Number | Indicates if the player will allow the video to be skipped, where 0 = no, 1 = yes. | `1` +| `playbackmethod` | optional | Number[] | Playback methods that may be in use. | `[2]` +| `delivery` | optional | Number[] | Supported delivery methods. | `[1]` +| `api` | optional | Number[] | List of supported API frameworks for this impression. | `[1, 2, 5]` ### Bid Native Parameters Set these parameters to `bid.nativeParams` or `bid.mediaTypes.native`. -| Name | Scope | Type | Description | Example | -|---------------|----------|--------|--------------------------------------|------------------------------------------------------------------------------------------------------------------------------| -| `title` | optional | Object | Config for native asset title. | `{required: true, len: 25}` | -| `image` | optional | Object | Config for native asset image. | `{required: true, sizes: [[300, 250]], aspect_ratios: [{min_width: 300, min_height: 250, ratio_width: 1, ratio_height: 1}]}` | -| `icon` | optional | Object | Config for native asset icon. | `{required: true, sizes: [[60, 60]], aspect_ratios: [{min_width: 60, min_height: 60, ratio_width: 1, ratio_height: 1}]}}` | -| `sponsoredBy` | optional | Object | Config for native asset sponsoredBy. | `{required: true, len: 20}` | -| `body` | optional | Object | Config for native asset body. | `{required: true, len: 200}` | -| `cta` | optional | Object | Config for native asset cta. | `{required: true, len: 20}` | +| Name | Scope | Type | Description | Example +| ---- | ----- | ---- | ----------- | ------- +| `title` | optional | Object | Config for native asset title. | `{required: true, len: 25}` +| `image` | optional | Object | Config for native asset image. | `{required: true, sizes: [[300, 250]], aspect_ratios: [{min_width: 300, min_height: 250, ratio_width: 1, ratio_height: 1}]}` +| `icon` | optional | Object | Config for native asset icon. | `{required: true, sizes: [[60, 60]], aspect_ratios: [{min_width: 60, min_height: 60, ratio_width: 1, ratio_height: 1}]}}` +| `sponsoredBy` | optional | Object | Config for native asset sponsoredBy. | `{required: true, len: 20}` +| `body` | optional | Object | Config for native asset body. | `{required: true, len: 200}` +| `cta` | optional | Object | Config for native asset cta. | `{required: true, len: 20}` ## Example @@ -129,9 +127,7 @@ var adUnits = [{ params: { placementId: 's5340077725248', endpointId: 'ep3425464070464', - publisherId: 'pub3054952966336', - // You might want to specify some application information here if the bid requests are from an application instead of a browser. - app: { 'name': 'my_app', 'bundle': 'test_bundle', 'store_url': 'www.some-store.com', 'ver': '1.1.0' } + publisherId: 'pub3054952966336' } }] }]; @@ -139,18 +135,18 @@ var adUnits = [{ ### User Ids -Opera Ads Bid Adapter uses `operaId`, please refer to [`Opera ID System`](./operaadsIdSystem.md). +Opera Ads Bid Adapter uses `sharedId`, `pubcid` or `tdid`, please config at least one. ```javascript pbjs.setConfig({ ..., userSync: { userIds: [{ - name: 'operaId', + name: 'sharedId', storage: { - name: 'operaId', - type: 'html5', - expires: 14 + name: '_sharedID', // name of the 1st party cookie + type: 'cookie', + expires: 30 } }] } diff --git a/modules/operaadsIdSystem.js b/modules/operaadsIdSystem.js deleted file mode 100644 index 7cf5e2ce5e1..00000000000 --- a/modules/operaadsIdSystem.js +++ /dev/null @@ -1,111 +0,0 @@ -/** - * This module adds operaId to the User ID module - * The {@link module:modules/userId} module is required - * @module modules/operaadsIdSystem - * @requires module:modules/userId - */ -import * as ajax from '../src/ajax.js'; -import { submodule } from '../src/hook.js'; -import { logMessage, logError } from '../src/utils.js'; - -/** - * @typedef {import('../modules/userId/index.js').SubmoduleConfig} SubmoduleConfig - * @typedef {import('../modules/userId/index.js').IdResponse} IdResponse - */ - -const MODULE_NAME = 'operaId'; -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'}; - -function constructUrl(pairs) { - const queries = []; - for (let key in pairs) { - queries.push(`${key}=${encodeURIComponent(pairs[key])}`); - } - return `${SYNC_URL}?${queries.join('&')}`; -} - -function asyncRequest(url, cb) { - ajax.ajaxBuilder(AJAX_TIMEOUT)( - url, - { - success: response => { - try { - const jsonResponse = JSON.parse(response); - const { uid: operaId } = jsonResponse; - cb(operaId); - return; - } catch (e) { - logError(`${MODULE_NAME}: invalid response`, response); - } - cb(); - }, - error: (err) => { - logError(`${MODULE_NAME}: ID error response`, err); - cb(); - } - }, - null, - AJAX_OPTIONS - ); -} - -export const operaIdSubmodule = { - /** - * used to link submodule with config - * @type {string} - */ - name: MODULE_NAME, - - /** - * @type {string} - */ - version, - - /** - * decode the stored id value for passing to bid requests - * @function - * @param {string} id - * @returns {{'operaId': string}} - */ - decode: (id) => - id != null && id.length > 0 - ? { [ID_KEY]: id } - : undefined, - - /** - * performs action to obtain id and return a value in the callback's response argument - * @function - * @param {SubmoduleConfig} [config] - * @returns {IdResponse|undefined} - */ - getId(config, consentData) { - logMessage(`${MODULE_NAME}: start synchronizing opera uid`); - const params = (config && config.params) || {}; - if (typeof params.pid !== 'string' || params.pid.length == 0) { - logError(`${MODULE_NAME}: submodule requires a publisher ID to be defined`); - return; - } - - const { pid, syncUrl = SYNC_URL } = params; - const url = constructUrl(syncUrl, { publisherId: pid }); - - return { - callback: (cb) => { - asyncRequest(url, cb); - } - } - }, - - eids: { - 'operaId': { - source: 't.adx.opera.com', - atype: 1 - }, - } -}; - -submodule('userId', operaIdSubmodule); diff --git a/modules/operaadsIdSystem.md b/modules/operaadsIdSystem.md deleted file mode 100644 index 288fb960b96..00000000000 --- a/modules/operaadsIdSystem.md +++ /dev/null @@ -1,52 +0,0 @@ -# Opera ID System - -For help adding this module, please contact [adtech-prebid-group@opera.com](adtech-prebid-group@opera.com). - -### Prebid Configuration - -You should configure this module under your `userSync.userIds[]` configuration: - -```javascript -pbjs.setConfig({ - userSync: { - userIds: [ - { - name: "operaId", - storage: { - name: "operaId", - type: "html5", - expires: 14 - }, - params: { - pid: "your-pulisher-ID-here" - } - } - ] - } -}) -``` -
- -| Param under `userSync.userIds[]` | Scope | Type | Description | Example | -| -------------------------------- | -------- | ------ | ----------------------------- | ----------------------------------------- | -| name | Required | string | ID for the operaId module | `"operaId"` | -| storage | Optional | Object | Settings for operaId storage | See [storage settings](#storage-settings) | -| params | Required | Object | Parameters for opreaId module | See [params](#params) | -
- -### Params - -| Param under `params` | Scope | Type | Description | Example | -| -------------------- | -------- | ------ | ------------------------------ | --------------- | -| pid | Required | string | Publisher ID assigned by Opera | `"pub12345678"` | -
- -### Storage Settings - -The following settings are suggested for the `storage` property in the `userSync.userIds[]` object: - -| Param under `storage` | Type | Description | Example | -| --------------------- | ------------- | -------------------------------------------------------------------------------- | ----------- | -| name | String | Where the ID will be stored | `"operaId"` | -| type | String | For best performance, this should be `"html5"` | `"html5"` | -| expires | Number <= 30 | number of days until the stored ID expires. **Must be less than or equal to 30** | `14` | \ No newline at end of file diff --git a/modules/optidigitalBidAdapter.js b/modules/optidigitalBidAdapter.js index 27b858c84fe..a0fa641a424 100755 --- a/modules/optidigitalBidAdapter.js +++ b/modules/optidigitalBidAdapter.js @@ -1,34 +1,23 @@ -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER} from '../src/mediaTypes.js'; -import {deepAccess, parseSizesInput} from '../src/utils.js'; -import {getAdUnitSizes} from '../libraries/sizeUtils/sizeUtils.js'; - -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse - * @typedef {import('../src/adapters/bidderFactory.js').SyncOptions} SyncOptions - * @typedef {import('../src/adapters/bidderFactory.js').UserSync} UserSync - * @typedef {import('../src/adapters/bidderFactory.js').validBidRequests} validBidRequests - */ +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER } from '../src/mediaTypes.js'; +import { deepAccess, parseSizesInput, getAdUnitSizes } from '../src/utils.js'; const BIDDER_CODE = 'optidigital'; const GVL_ID = 915; const ENDPOINT_URL = 'https://pbs.optidigital.com/bidder'; const USER_SYNC_URL_IFRAME = 'https://scripts.opti-digital.com/js/presync.html?endpoint=optidigital'; let CUR = 'USD'; -let isSynced = false; export const spec = { code: BIDDER_CODE, gvlid: GVL_ID, supportedMediaTypes: [BANNER], /** - * Determines whether or not the given bid request is valid. - * - * @param {BidRequest} bid The bid params to validate. - * @return boolean True if this is a valid bid, and false otherwise. - */ + * Determines whether or not the given bid request is valid. + * + * @param {BidRequest} bid The bid params to validate. + * @return boolean True if this is a valid bid, and false otherwise. + */ isBidRequestValid: function(bid) { let isValid = false; if (typeof bid.params !== 'undefined' && bid.params.placementId && bid.params.publisherId) { @@ -38,11 +27,11 @@ export const spec = { return isValid; }, /** - * Make a server request from the list of BidRequests. - * - * @param {validBidRequests[]} - an array of bids - * @return ServerRequest Info describing the request to the server. - */ + * Make a server request from the list of BidRequests. + * + * @param {validBidRequests[]} - an array of bids + * @return ServerRequest Info describing the request to the server. + */ buildRequests: function(validBidRequests, bidderRequest) { if (!validBidRequests || validBidRequests.length === 0 || !bidderRequest || !bidderRequest.bids) { return []; @@ -57,6 +46,8 @@ export const spec = { referrer: (bidderRequest.refererInfo && bidderRequest.refererInfo.page) ? bidderRequest.refererInfo.page : '', hb_version: '$prebid.version$', deviceWidth: document.documentElement.clientWidth, + // TODO: fix auctionId leak: https://github.com/prebid/Prebid.js/issues/9781 + auctionId: deepAccess(validBidRequests[0], 'auctionId'), bidderRequestId: deepAccess(validBidRequests[0], 'bidderRequestId'), publisherId: deepAccess(validBidRequests[0], 'params.publisherId'), imp: validBidRequests.map(bidRequest => buildImp(bidRequest, ortb2)), @@ -65,10 +56,6 @@ export const spec = { bapp: deepAccess(validBidRequests[0], 'params.bapp') || [] } - if (validBidRequests[0].auctionId) { - payload.auctionId = validBidRequests[0].auctionId; - } - if (validBidRequests[0].params.pageTemplate && validBidRequests[0].params.pageTemplate !== '') { payload.pageTemplate = validBidRequests[0].params.pageTemplate; } @@ -100,12 +87,6 @@ export const spec = { payload.uspConsent = bidderRequest.uspConsent; } - if (_getEids(validBidRequests[0])) { - payload.user = { - eids: _getEids(validBidRequests[0]) - } - } - const payloadObject = JSON.stringify(payload); return { method: 'POST', @@ -114,11 +95,11 @@ export const spec = { }; }, /** - * Unpack the 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. - */ + * Unpack the 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. + */ interpretResponse: function(serverResponse, bidRequest) { const bidResponses = []; serverResponse = serverResponse.body; @@ -147,31 +128,29 @@ export const spec = { }, /** - * Register the user sync pixels which should be dropped after the auction. - * - * @param {SyncOptions} syncOptions Which user syncs are allowed? - * @param {ServerResponse[]} serverResponses List of server's responses. - * @return {UserSync[]} The user syncs which should be dropped. - */ + * Register the user sync pixels which should be dropped after the auction. + * + * @param {SyncOptions} syncOptions Which user syncs are allowed? + * @param {ServerResponse[]} serverResponses List of server's responses. + * @return {UserSync[]} The user syncs which should be dropped. + */ getUserSyncs: function(syncOptions, serverResponses, gdprConsent, uspConsent) { let syncurl = ''; - if (!isSynced) { - // Attaching GDPR Consent Params in UserSync url - if (gdprConsent) { - syncurl += '&gdpr=' + (gdprConsent.gdprApplies ? 1 : 0); - syncurl += '&gdpr_consent=' + encodeURIComponent(gdprConsent.consentString || ''); - } - if (uspConsent && uspConsent.consentString) { - syncurl += `&ccpa_consent=${uspConsent.consentString}`; - } - if (syncOptions.iframeEnabled) { - isSynced = true; - return [{ - type: 'iframe', - url: USER_SYNC_URL_IFRAME + syncurl - }]; - } + // Attaching GDPR Consent Params in UserSync url + if (gdprConsent) { + syncurl += '&gdpr=' + (gdprConsent.gdprApplies ? 1 : 0); + syncurl += '&gdpr_consent=' + encodeURIComponent(gdprConsent.consentString || ''); + } + if (uspConsent && uspConsent.consentString) { + syncurl += `&ccpa_consent=${uspConsent.consentString}`; + } + + if (syncOptions.iframeEnabled) { + return [{ + type: 'iframe', + url: USER_SYNC_URL_IFRAME + syncurl + }]; } }, }; @@ -239,14 +218,4 @@ function _getFloor (bid, sizes, currency) { return floor !== null ? floor : bid.params.floor; } -function _getEids(bidRequest) { - if (deepAccess(bidRequest, 'userIdAsEids')) { - return bidRequest.userIdAsEids; - } -} - -export function resetSync() { - isSynced = false; -} - registerBidder(spec); diff --git a/modules/optidigitalBidAdapter.md b/modules/optidigitalBidAdapter.md index 327e7a27c75..466dfb3bef2 100755 --- a/modules/optidigitalBidAdapter.md +++ b/modules/optidigitalBidAdapter.md @@ -37,13 +37,10 @@ Bidder Adapter for Prebid.js. ``` pbjs.setConfig({ -  userSync: { -    filterSettings: { -      iframe: { -        bidders: '*', // '*' represents all bidders -        filter: 'include' -      } -    } -  } + userSync: { + iframeEnabled: true, + syncEnabled: true, + syncDelay: 3000 + } }); ``` diff --git a/modules/optimeraRtdProvider.js b/modules/optimeraRtdProvider.js index bd564e3a260..dfe8f1bfcf2 100644 --- a/modules/optimeraRtdProvider.js +++ b/modules/optimeraRtdProvider.js @@ -16,17 +16,12 @@ * @property {string} clientID * @property {string} optimeraKeyName * @property {string} device - * @property {string} apiVersion */ import { logInfo, logError } from '../src/utils.js'; import { submodule } from '../src/hook.js'; import { ajaxBuilder } from '../src/ajax.js'; -/** - * @typedef {import('../modules/rtdModule/index.js').RtdSubmodule} RtdSubmodule - */ - /** @type {ModuleParams} */ let _moduleParams = {}; @@ -34,8 +29,7 @@ let _moduleParams = {}; * Default Optimera Key Name * This can default to hb_deal_optimera for publishers * who used the previous Optimera Bidder Adapter. - * @type {string} - */ + * @type {string} */ export let optimeraKeyName = 'hb_deal_optimera'; /** @@ -44,10 +38,7 @@ export let optimeraKeyName = 'hb_deal_optimera'; * the targeting values. * @type {string} */ -export const scoresBaseURL = { - v0: 'https://dyv1bugovvq1g.cloudfront.net/', - v1: 'https://v1.oapi26b.com/', -}; +export const scoresBaseURL = 'https://dyv1bugovvq1g.cloudfront.net/'; /** * Optimera Score File URL. @@ -67,12 +58,6 @@ export let clientID; */ export let device = 'default'; -/** - * Optional apiVersion parameter. - * @type {string} - */ -export let apiVersion = 'v0'; - /** * Targeting object for all ad positions. * @type {string} @@ -142,7 +127,6 @@ export function onAuctionInit(auctionDetails, config, userConsent) { /** * Initialize the Module. - * moduleConfig.params.apiVersion can be either v0 or v1. */ export function init(moduleConfig) { _moduleParams = moduleConfig.params; @@ -154,9 +138,6 @@ export function init(moduleConfig) { if (_moduleParams.device) { device = _moduleParams.device; } - if (_moduleParams.apiVersion) { - apiVersion = (_moduleParams.apiVersion.includes('v1', 'v0')) ? _moduleParams.apiVersion : 'v0'; - } setScoresURL(); scoreFileRequest(); return true; @@ -181,15 +162,7 @@ export function init(moduleConfig) { export function setScoresURL() { const optimeraHost = window.location.host; const optimeraPathName = window.location.pathname; - const baseUrl = scoresBaseURL[apiVersion] ? scoresBaseURL[apiVersion] : scoresBaseURL.v0; - let newScoresURL; - - if (apiVersion === 'v1') { - newScoresURL = `${baseUrl}api/products/scores?c=${clientID}&h=${optimeraHost}&p=${optimeraPathName}&s=${device}`; - } else { - newScoresURL = `${baseUrl}${clientID}/${optimeraHost}${optimeraPathName}.js`; - } - + const newScoresURL = `${scoresBaseURL}${clientID}/${optimeraHost}${optimeraPathName}.js`; if (scoresURL !== newScoresURL) { scoresURL = newScoresURL; fetchScoreFile = true; diff --git a/modules/optimeraRtdProvider.md b/modules/optimeraRtdProvider.md index 8b66deb5ad5..610dec537e0 100644 --- a/modules/optimeraRtdProvider.md +++ b/modules/optimeraRtdProvider.md @@ -1,6 +1,6 @@ # Overview ``` -Module Name: Optimera Real Time Data Module +Module Name: Optimera Real Time Date Module Module Type: RTD Module Maintainer: mcallari@optimera.nyc ``` @@ -26,8 +26,7 @@ Configuration example for using RTD module with `optimera` provider params: { clientID: '9999', optimeraKeyName: 'optimera', - device: 'de', - apiVersion: 'v0', + device: 'de' } } ] @@ -43,4 +42,3 @@ Contact Optimera to get assistance with the params. | clientID | string | required | Optimera Client ID | | optimeraKeyName | string | optional | GAM key name for Optimera. If migrating from the Optimera bidder adapter this will default to hb_deal_optimera and can be ommitted from the configuration. | | device | string | optional | Device type code for mobile, tablet, or desktop. Either mo, tb, de | -| apiVersion | string | optional | Optimera API Versions. Either v0, or v1. ** Note: v1 wll need to be enabled specifically for your account, otherwise use v0. \ No newline at end of file diff --git a/modules/optimonAnalyticsAdapter.js b/modules/optimonAnalyticsAdapter.js index 68baf007563..82bc18f605d 100644 --- a/modules/optimonAnalyticsAdapter.js +++ b/modules/optimonAnalyticsAdapter.js @@ -1,12 +1,12 @@ /** - * - ********************************************************* - * - * Optimon.io Prebid Analytics Adapter - * - ********************************************************* - * - */ +* +********************************************************* +* +* Optimon.io Prebid Analytics Adapter +* +********************************************************* +* +*/ import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; diff --git a/modules/orbidderBidAdapter.js b/modules/orbidderBidAdapter.js index 0f912384db7..f135ebb2bd1 100644 --- a/modules/orbidderBidAdapter.js +++ b/modules/orbidderBidAdapter.js @@ -1,15 +1,11 @@ import { isFn, isPlainObject } from '../src/utils.js'; -import { registerBidder } from '../src/adapters/bidderFactory.js'; +import {registerBidder} from '../src/adapters/bidderFactory.js'; import { getStorageManager } from '../src/storageManager.js'; import { BANNER, NATIVE } from '../src/mediaTypes.js'; import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; -import { getGlobal } from '../src/prebidGlobal.js'; +import {getGlobal} from '../src/prebidGlobal.js'; -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - */ -const storageManager = getStorageManager({ bidderCode: 'orbidder' }); +const storageManager = getStorageManager({bidderCode: 'orbidder'}); /** * Determines whether or not the given bid response is valid. @@ -73,7 +69,7 @@ export const spec = { return !!(bid.sizes && bid.bidId && bid.params && (bid.params.accountId && (typeof bid.params.accountId === 'string')) && (bid.params.placementId && (typeof bid.params.placementId === 'string')) && - ((typeof bid.params.keyValues === 'undefined') || (typeof bid.params.keyValues === 'object'))); + ((typeof bid.params.profile === 'undefined') || (typeof bid.params.profile === 'object'))); }, /** @@ -103,7 +99,15 @@ export const spec = { data: { v: getGlobal().version, pageUrl: referer, - ...bidRequest // get all data provided by bid request + bidId: bidRequest.bidId, + auctionId: bidRequest.auctionId, + // TODO: fix auctionId leak: https://github.com/prebid/Prebid.js/issues/9781 + transactionId: bidRequest.ortb2Imp?.ext?.tid, + adUnitCode: bidRequest.adUnitCode, + bidRequestCount: bidRequest.bidRequestCount, + params: bidRequest.params, + sizes: bidRequest.sizes, + mediaTypes: bidRequest.mediaTypes } }; diff --git a/modules/orbitsoftBidAdapter.js b/modules/orbitsoftBidAdapter.js index f55c7ff9917..4c3f2e38c58 100644 --- a/modules/orbitsoftBidAdapter.js +++ b/modules/orbitsoftBidAdapter.js @@ -1,6 +1,5 @@ import * as utils from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {getBidIdParameter} from '../src/utils.js'; const BIDDER_CODE = 'orbitsoft'; let styleParamsMap = { @@ -46,10 +45,10 @@ export const spec = { for (let i = 0; i < validBidRequests.length; i++) { bidRequest = validBidRequests[i]; let bidRequestParams = bidRequest.params; - let placementId = getBidIdParameter('placementId', bidRequestParams); - let requestUrl = getBidIdParameter('requestUrl', bidRequestParams); - let referrer = getBidIdParameter('ref', bidRequestParams); - let location = getBidIdParameter('loc', bidRequestParams); + let placementId = utils.getBidIdParameter('placementId', bidRequestParams); + let requestUrl = utils.getBidIdParameter('requestUrl', bidRequestParams); + let referrer = utils.getBidIdParameter('ref', bidRequestParams); + let location = utils.getBidIdParameter('loc', bidRequestParams); // Append location & referrer if (location === '') { location = utils.getWindowLocation(); @@ -59,7 +58,7 @@ export const spec = { } // Styles params - let stylesParams = getBidIdParameter('style', bidRequestParams); + let stylesParams = utils.getBidIdParameter('style', bidRequestParams); let stylesParamsArray = {}; for (let currentValue in stylesParams) { if (stylesParams.hasOwnProperty(currentValue)) { @@ -75,7 +74,7 @@ export const spec = { } } // Custom params - let customParams = getBidIdParameter('customParams', bidRequestParams); + let customParams = utils.getBidIdParameter('customParams', bidRequestParams); let customParamsArray = {}; for (let customField in customParams) { if (customParams.hasOwnProperty(customField)) { diff --git a/modules/otmBidAdapter.js b/modules/otmBidAdapter.js index 7d4049e3054..6125cee6593 100644 --- a/modules/otmBidAdapter.js +++ b/modules/otmBidAdapter.js @@ -2,13 +2,14 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { logInfo, logError, + getBidIdParameter, _each, getValue, isFn, isPlainObject, isArray, isStr, - isNumber, getBidIdParameter, + isNumber, } from '../src/utils.js'; import { BANNER } from '../src/mediaTypes.js'; diff --git a/modules/outbrainBidAdapter.js b/modules/outbrainBidAdapter.js index 6015ff37e08..0637d680912 100644 --- a/modules/outbrainBidAdapter.js +++ b/modules/outbrainBidAdapter.js @@ -3,7 +3,6 @@ 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, isArray, logWarn, replaceAuctionPrice} from '../src/utils.js'; import {ajax} from '../src/ajax.js'; @@ -24,9 +23,6 @@ const NATIVE_PARAMS = { cta: { id: 1, type: 12, name: 'data' } }; 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 spec = { code: BIDDER_CODE, @@ -134,11 +130,6 @@ export const spec = { request.test = 1; } - const obUserToken = storage.getDataFromLocalStorage(OB_USER_TOKEN_KEY) - if (obUserToken) { - deepSetValue(request, 'user.ext.obusertoken', obUserToken) - } - if (deepAccess(bidderRequest, 'gdprConsent.gdprApplies')) { deepSetValue(request, 'user.ext.consent', bidderRequest.gdprConsent.consentString) deepSetValue(request, 'regs.ext.gdpr', bidderRequest.gdprConsent.gdprApplies & 1) @@ -149,13 +140,6 @@ export const spec = { if (config.getConfig('coppa') === true) { deepSetValue(request, 'regs.coppa', config.getConfig('coppa') & 1) } - if (bidderRequest.gppConsent) { - deepSetValue(request, 'regs.ext.gpp', bidderRequest.gppConsent.gppString) - deepSetValue(request, 'regs.ext.gpp_sid', bidderRequest.gppConsent.applicableSections) - } else if (deepAccess(bidderRequest, 'ortb2.regs.gpp')) { - deepSetValue(request, 'regs.ext.gpp', bidderRequest.ortb2.regs.gpp) - deepSetValue(request, 'regs.ext.gpp_sid', bidderRequest.ortb2.regs.gpp_sid) - } if (eids) { deepSetValue(request, 'user.ext.eids', eids); @@ -219,7 +203,7 @@ export const spec = { } }).filter(Boolean); }, - getUserSyncs: (syncOptions, responses, gdprConsent, uspConsent, gppConsent) => { + getUserSyncs: (syncOptions, responses, gdprConsent, uspConsent) => { const syncs = []; let syncUrl = config.getConfig('outbrain.usersyncUrl'); @@ -232,10 +216,6 @@ export const spec = { if (uspConsent) { query.push('us_privacy=' + encodeURIComponent(uspConsent)); } - if (gppConsent) { - query.push('gpp=' + encodeURIComponent(gppConsent.gppString)); - query.push('gpp_sid=' + encodeURIComponent(gppConsent.applicableSections.join(','))); - } syncs.push({ type: 'image', diff --git a/modules/oxxionAnalyticsAdapter.js b/modules/oxxionAnalyticsAdapter.js index 25732d440ff..c4a51bfb7fa 100644 --- a/modules/oxxionAnalyticsAdapter.js +++ b/modules/oxxionAnalyticsAdapter.js @@ -21,9 +21,8 @@ let saveEvents = {} let allEvents = {} let auctionEnd = {} let initOptions = {} -let mode = {}; let endpoint = 'https://default' -let requestsAttributes = ['adUnitCode', 'auctionId', 'bidder', 'bidderCode', 'bidId', 'cpm', 'creativeId', 'currency', 'width', 'height', 'mediaType', 'netRevenue', 'originalCpm', 'originalCurrency', 'requestId', 'size', 'source', 'status', 'timeToRespond', 'transactionId', 'ttl', 'sizes', 'mediaTypes', 'src', 'params', 'userId', 'labelAny', 'bids', 'adId', 'ova']; +let requestsAttributes = ['adUnitCode', 'auctionId', 'bidder', 'bidderCode', 'bidId', 'cpm', 'creativeId', 'currency', 'width', 'height', 'mediaType', 'netRevenue', 'originalCpm', 'originalCurrency', 'requestId', 'size', 'source', 'status', 'timeToRespond', 'transactionId', 'ttl', 'sizes', 'mediaTypes', 'src', 'params', 'userId', 'labelAny', 'bids', 'adId']; function getAdapterNameForAlias(aliasName) { return adapterManager.aliasRegistry[aliasName] || aliasName; @@ -42,27 +41,16 @@ function filterAttributes(arg, removead) { } if (typeof arg['gdprConsent'] != 'undefined') { response['gdprConsent'] = {}; - if (typeof arg['gdprConsent']['consentString'] != 'undefined') { - response['gdprConsent']['consentString'] = arg['gdprConsent']['consentString']; - } + if (typeof arg['gdprConsent']['consentString'] != 'undefined') { response['gdprConsent']['consentString'] = arg['gdprConsent']['consentString']; } } - if (typeof arg['meta'] == 'object') { - response['meta'] = {}; - if (typeof arg['meta']['advertiserDomains'] != 'undefined') { - response['meta']['advertiserDomains'] = arg['meta']['advertiserDomains']; - } - if (typeof arg['meta']['demandSource'] == 'string') { - response['meta']['demandSource'] = arg['meta']['demandSource']; - } + if (typeof arg['meta'] == 'object' && typeof arg['meta']['advertiserDomains'] != 'undefined') { + response['meta'] = {'advertiserDomains': arg['meta']['advertiserDomains']}; } requestsAttributes.forEach((attr) => { if (typeof arg[attr] != 'undefined') { response[attr] = arg[attr]; } }); - if (typeof response['creativeId'] == 'number') { - response['creativeId'] = response['creativeId'].toString(); - } + if (typeof response['creativeId'] == 'number') { response['creativeId'] = response['creativeId'].toString(); } } - response['oxxionMode'] = mode; return response; } @@ -89,8 +77,7 @@ function cleanAuctionEnd(args) { } function cleanCreatives(args) { - let stringArgs = JSON.parse(dereferenceWithoutRenderer(args)); - return filterAttributes(stringArgs, false); + return filterAttributes(args, false); } function enhanceMediaType(arg) { @@ -106,7 +93,7 @@ function enhanceMediaType(arg) { function addBidResponse(args) { let eventType = BID_RESPONSE; - let argsCleaned = cleanCreatives(args); ; + let argsCleaned = cleanCreatives(JSON.parse(JSON.stringify(args))); ; if (allEvents[eventType] == undefined) { allEvents[eventType] = [] } allEvents[eventType].push(argsCleaned); } @@ -123,9 +110,7 @@ function addTimeout(args) { if (saveEvents[eventType] == undefined) { saveEvents[eventType] = [] } saveEvents[eventType].push(args); let argsCleaned = []; - let argsDereferenced = {}; - let stringArgs = JSON.parse(dereferenceWithoutRenderer(args)); - argsDereferenced = stringArgs; + let argsDereferenced = JSON.parse(JSON.stringify(args)); argsDereferenced.forEach((attr) => { argsCleaned.push(filterAttributes(JSON.parse(JSON.stringify(attr)), false)); }); @@ -133,42 +118,17 @@ function addTimeout(args) { auctionEnd[eventType].push(argsCleaned); } -export const dereferenceWithoutRenderer = function(args) { - if (args.renderer) { - let tmp = args.renderer; - delete args.renderer; - let stringified = JSON.stringify(args); - args['renderer'] = tmp; - return stringified; - } - if (args.bidsReceived) { - let tmp = {} - for (let key in args.bidsReceived) { - if (args.bidsReceived[key].renderer) { - tmp[key] = args.bidsReceived[key].renderer; - delete args.bidsReceived[key].renderer; - } - } - let stringified = JSON.stringify(args); - for (let key in tmp) { - args.bidsReceived[key].renderer = tmp[key]; - } - return stringified; - } - return JSON.stringify(args); -} - function addAuctionEnd(args) { let eventType = AUCTION_END; if (saveEvents[eventType] == undefined) { saveEvents[eventType] = [] } saveEvents[eventType].push(args); - let argsCleaned = cleanAuctionEnd(JSON.parse(dereferenceWithoutRenderer(args))); + let argsCleaned = cleanAuctionEnd(JSON.parse(JSON.stringify(args))); if (auctionEnd[eventType] == undefined) { auctionEnd[eventType] = [] } auctionEnd[eventType].push(argsCleaned); } function handleBidWon(args) { - args = enhanceMediaType(filterAttributes(JSON.parse(dereferenceWithoutRenderer(args)), true)); + args = enhanceMediaType(filterAttributes(JSON.parse(JSON.stringify(args)), true)); let increment = args['cpm']; if (typeof saveEvents['auctionEnd'] == 'object') { saveEvents['auctionEnd'].forEach((auction) => { @@ -183,15 +143,6 @@ function handleBidWon(args) { } }); } - if (auction['auctionId'] == args['auctionId'] && typeof auction['bidderRequests'] == 'object') { - auction['bidderRequests'].forEach((req) => { - req.bids.forEach((bid) => { - if (bid['bidId'] == args['requestId'] && bid['transactionId'] == args['transactionId']) { - args['ova'] = bid['ova']; - } - }); - }); - } }); } args['cpmIncrement'] = increment; @@ -241,8 +192,7 @@ let oxxionAnalytics = Object.assign(adapter({url, analyticsType}), { addTimeout(args); break; } - } -}); + }}); // save the base class function oxxionAnalytics.originEnableAnalytics = oxxionAnalytics.enableAnalytics; @@ -251,12 +201,7 @@ oxxionAnalytics.originEnableAnalytics = oxxionAnalytics.enableAnalytics; oxxionAnalytics.enableAnalytics = function (config) { oxxionAnalytics.originEnableAnalytics(config); // call the base class function initOptions = config.options; - if (initOptions.domain) { - endpoint = 'https://' + initOptions.domain; - } - if (window.OXXION_MODE) { - mode = window.OXXION_MODE; - } + if (initOptions.domain) { endpoint = 'https://' + initOptions.domain; } }; adapterManager.registerAnalyticsAdapter({ diff --git a/modules/oxxionRtdProvider.js b/modules/oxxionRtdProvider.js index a0476d8ca0f..4b5a7547088 100644 --- a/modules/oxxionRtdProvider.js +++ b/modules/oxxionRtdProvider.js @@ -1,155 +1,97 @@ import { submodule } from '../src/hook.js' -import { logInfo, logError } from '../src/utils.js' -import { ajax } from '../src/ajax.js'; -import adapterManager from '../src/adapterManager.js'; - -/** - * @typedef {import('../modules/rtdModule/index.js').RtdSubmodule} RtdSubmodule - */ +import { deepAccess, logInfo } from '../src/utils.js' +const oxxionRtdSearchFor = [ 'adUnitCode', 'auctionId', 'bidder', 'bidderCode', 'bidId', 'cpm', 'creativeId', 'currency', 'width', 'height', 'mediaType', 'netRevenue', 'originalCpm', 'originalCurrency', 'requestId', 'size', 'source', 'status', 'timeToRespond', 'transactionId', 'ttl', 'sizes', 'mediaTypes', 'src', 'userId', 'labelAny', 'adId' ]; const LOG_PREFIX = 'oxxionRtdProvider submodule: '; -const bidderAliasRegistry = adapterManager.aliasRegistry || {}; +const allAdUnits = []; /** @type {RtdSubmodule} */ export const oxxionSubmodule = { name: 'oxxionRtd', init: init, getBidRequestData: getAdUnits, - getRequestsList: getRequestsList, - getFilteredAdUnitsOnBidRates: getFilteredAdUnitsOnBidRates, + onBidResponseEvent: insertVideoTracking, }; function init(config, userConsent) { - if (!config.params || !config.params.domain) { return false } - if (typeof config.params.threshold != 'undefined' && typeof config.params.samplingRate == 'number') { return true } - return false; + if (!config.params || !config.params.domain || !config.params.contexts || !Array.isArray(config.params.contexts) || config.params.contexts.length == 0) { + return false + } + return true; } function getAdUnits(reqBidsConfigObj, callback, config, userConsent) { - const moduleStarted = new Date(); - logInfo(LOG_PREFIX + 'started with ', config); - if (typeof config.params.threshold != 'undefined' && typeof config.params.samplingRate == 'number') { - let filteredBids; - const requests = getRequestsList(reqBidsConfigObj); - const gdpr = userConsent && userConsent.gdpr ? userConsent.gdpr.consentString : null; - const payload = { - gdpr, - requests - }; - const endpoint = 'https://' + config.params.domain + '.oxxion.io/analytics/bid_rate_interests'; - getPromisifiedAjax(endpoint, JSON.stringify(payload), { - method: 'POST', - withCredentials: true - }).then(bidsRateInterests => { - if (bidsRateInterests.length) { - [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}), { - method: 'POST', - withCredentials: true - }); - } - if (typeof callback == 'function') { callback(); } - 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}); + const reqAdUnits = reqBidsConfigObj.adUnits; + if (Array.isArray(reqAdUnits)) { + reqAdUnits.forEach(adunit => { + if (config.params.contexts.includes(deepAccess(adunit, 'mediaTypes.video.context'))) { + allAdUnits.push(adunit); } - }).catch(error => logError(LOG_PREFIX, 'bidInterestError', error)); + }); } } -function getPromisifiedAjax (url, data = {}, options = {}) { - return new Promise((resolve, reject) => { - const callbacks = { - success(responseText, { response }) { - resolve(JSON.parse(response)); - }, - error(error) { - reject(error); +function insertVideoTracking(bidResponse, config, userConsent) { + // this should only be do for video bids + if (bidResponse.mediaType === 'video') { + let maxCpm = 0; + const trackingUrl = getImpUrl(config, bidResponse, maxCpm); + if (!trackingUrl) { + return; + } + // Vast Impression URL + if (bidResponse.vastUrl) { + bidResponse.vastImpUrl = bidResponse.vastImpUrl + ? trackingUrl + '&url=' + encodeURI(bidResponse.vastImpUrl) + : trackingUrl; + logInfo(LOG_PREFIX + 'insert into vastImpUrl for adId ' + bidResponse.adId); + } + // Vast XML document + if (bidResponse.vastXml !== undefined) { + const doc = new DOMParser().parseFromString(bidResponse.vastXml, 'text/xml'); + const wrappers = doc.querySelectorAll('VAST Ad Wrapper, VAST Ad InLine'); + let hasAltered = false; + if (wrappers.length) { + wrappers.forEach(wrapper => { + const impression = doc.createElement('Impression'); + impression.appendChild(doc.createCDATASection(trackingUrl)); + wrapper.appendChild(impression) + }); + bidResponse.vastXml = new XMLSerializer().serializeToString(doc); + hasAltered = true; + } + if (hasAltered) { + logInfo(LOG_PREFIX + 'insert into vastXml for adId ' + bidResponse.adId); } - }; - ajax(url, callbacks, data, options); - }) + } + } } -function getFilteredAdUnitsOnBidRates (bidsRateInterests, adUnits, params, useSampling) { - const { threshold, samplingRate } = params; - const sampling = getRandomNumber(100) < samplingRate && useSampling; - const filteredBids = []; - // Separate bidsRateInterests in two groups against threshold & samplingRate - const { interestingBidsRates, uninterestingBidsRates, sampledBidsRates } = bidsRateInterests.reduce((acc, interestingBid) => { - const isBidRateUpper = typeof threshold == 'number' ? interestingBid.rate === true || interestingBid.rate > threshold : interestingBid.suggestion; - const isBidInteresting = isBidRateUpper || sampling; - const key = isBidInteresting ? 'interestingBidsRates' : 'uninterestingBidsRates'; - acc[key].push(interestingBid); - if (!isBidRateUpper && sampling) { - acc['sampledBidsRates'].push(interestingBid); +function getImpUrl(config, data, maxCpm) { + const adUnitCode = data.adUnitCode; + const adUnits = allAdUnits.find(adunit => adunit.code === adUnitCode && + 'mediaTypes' in adunit && + 'video' in adunit.mediaTypes && + typeof adunit.mediaTypes.video.context === 'string'); + const context = adUnits !== undefined + ? adUnits.mediaTypes.video.context + : 'unknown'; + if (!config.params.contexts.includes(context)) { + return false; + } + let trackingImpUrl = 'https://' + config.params.domain + '.oxxion.io/analytics/vast_imp?'; + trackingImpUrl += oxxionRtdSearchFor.reduce((acc, param) => { + switch (typeof data[param]) { + case 'string': + case 'number': + acc += param + '=' + data[param] + '&' + break; } return acc; - }, { - interestingBidsRates: [], - uninterestingBidsRates: [], // Do something with later - sampledBidsRates: [] - }); - logInfo(LOG_PREFIX, 'getFilteredAdUnitsOnBidRates()', interestingBidsRates, uninterestingBidsRates); - // Filter bids and adUnits against interesting bids rates - const newAdUnits = adUnits.filter(({ bids = [] }, adUnitIndex) => { - adUnits[adUnitIndex].bids = bids.filter((bid, bidIndex) => { - if (!params.bidders || params.bidders.includes(bid.bidder)) { - const index = interestingBidsRates.findIndex(({ id }) => id === bid._id); - if (index == -1) { - let tmpBid = bid; - tmpBid['code'] = adUnits[adUnitIndex].code; - tmpBid['mediaTypes'] = adUnits[adUnitIndex].mediaTypes; - tmpBid['originalBidder'] = bidderAliasRegistry[bid.bidder] || bid.bidder; - if (tmpBid.floorData) { - delete tmpBid.floorData; - } - filteredBids.push(tmpBid); - adUnits[adUnitIndex].bids[bidIndex]['ova'] = 'filtered'; - } else { - if (sampledBidsRates.findIndex(({ id }) => id === bid._id) == -1) { - adUnits[adUnitIndex].bids[bidIndex]['ova'] = 'cleared'; - } else { - adUnits[adUnitIndex].bids[bidIndex]['ova'] = 'sampled'; - logInfo(LOG_PREFIX + ' sampled ! '); - } - } - delete bid._id; - return index !== -1; - } else { - adUnits[adUnitIndex].bids[bidIndex]['ova'] = 'protected'; - return true; - } - }); - return !!adUnits[adUnitIndex].bids.length; - }); - return [newAdUnits, filteredBids]; -} - -function getRandomNumber (max = 10) { - return Math.round(Math.random() * max); -} - -function getRequestsList(reqBidsConfigObj) { - let count = 0; - return reqBidsConfigObj.adUnits.flatMap(({ - bids = [], - mediaTypes = {}, - code = '' - }) => bids.reduce((acc, { bidder = '', params = {} }, index) => { - const id = count++; - bids[index]._id = id; - return acc.concat({ - id, - adUnit: code, - bidder, - mediaTypes, - }); - }, [])); + }, ''); + const cpmIncrement = 0.0; + return trackingImpUrl + 'cpmIncrement=' + cpmIncrement + '&context=' + context; } submodule('realTimeData', oxxionSubmodule); diff --git a/modules/oxxionRtdProvider.md b/modules/oxxionRtdProvider.md index bfdbfae1fa9..061acdbe11a 100644 --- a/modules/oxxionRtdProvider.md +++ b/modules/oxxionRtdProvider.md @@ -7,12 +7,12 @@ Maintainer: tech@oxxion.io # Oxxion Real-Time-Data submodule Oxxion helps you to understand how your prebid stack performs. -This Rtd module purpose is to filter bidders requested. +This Rtd module is to use in order to improve video events tracking. # Integration Make sure to have the following modules listed while building prebid : `rtdModule,oxxionRtdProvider` -`rtdModule` is required to activate real-time-data submodules. +`rtbModule` is required to activate real-time-data submodules. For example : ``` gulp build --modules=schain,priceFloors,currency,consentManagement,appnexusBidAdapter,rubiconBidAdapter,rtdModule,oxxionRtdProvider @@ -23,15 +23,14 @@ Then add the oxxion Rtd module to your prebid configuration : pbjs.setConfig( ... realTimeData: { - auctionDelay: 300, + auctionDelay: 200, dataProviders: [ { name: "oxxionRtd", waitForIt: true, params: { domain: "test.endpoint", - threshold: false, - samplingRate: 10, + contexts: ["instream"], } } ] @@ -40,17 +39,10 @@ pbjs.setConfig( ) ``` -# setConfig Parameters General +# setConfig Parameters | Name | Type | Description | |:---------------------------------|:---------|:------------------------------------------------------------------------------------------------------------| | domain | String | This string identifies yourself in Oxxion's systems and is provided to you by your Oxxion representative. | - -# setConfig Parameters for bidder filtering - -| Name | Type | Description | -|:---------------------------------|:-----------|:------------------------------------------------------------------------------------------------------------| -| threshold | Float/Bool | False or minimum expected bid rate to call a bidder (ex: 1.0 for 1% bid rate). | -| samplingRate | Integer | Percentage of request not meeting the criterias to run anyway in order to check for any change. | -| bidders | Array | Optional: If set, filtering will only be applied to bidders listed. +| contexts | Array | Array defining which video contexts to add tracking events into. Values can be instream and/or outstream. | diff --git a/modules/ozoneBidAdapter.js b/modules/ozoneBidAdapter.js index 0d921f57cda..1b725ce3a05 100644 --- a/modules/ozoneBidAdapter.js +++ b/modules/ozoneBidAdapter.js @@ -7,8 +7,7 @@ import { isArray, contains, mergeDeep, - parseUrl, - generateUUID + parseUrl } from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; @@ -17,15 +16,15 @@ import {getPriceBucketString} from '../src/cpmBucketManager.js'; import { Renderer } from '../src/Renderer.js'; import {getRefererInfo} from '../src/refererDetection.js'; const BIDDER_CODE = 'ozone'; -const ORIGIN = 'https://elb.the-ozone-project.com' // applies only to auction & cookie +const ORIGIN = 'https://elb.the-ozone-project.com'; // applies only to auction & cookie const AUCTIONURI = '/openrtb2/auction'; const OZONECOOKIESYNC = '/static/load-cookie.html'; const OZONE_RENDERER_URL = 'https://prebid.the-ozone-project.com/ozone-renderer.js'; const ORIGIN_DEV = 'https://test.ozpr.net'; -const OZONEVERSION = '2.9.1'; +const OZONEVERSION = '2.8.0'; export const spec = { gvlid: 524, - aliases: [{code: 'lmc', gvlid: 524}, {code: 'venatus', gvlid: 524}], + aliases: [{code: 'lmc', gvlid: 524}], version: OZONEVERSION, code: BIDDER_CODE, supportedMediaTypes: [VIDEO, BANNER], @@ -37,8 +36,7 @@ export const spec = { 'keyPrefix': 'oz', 'auctionUrl': ORIGIN + AUCTIONURI, 'cookieSyncUrl': ORIGIN + OZONECOOKIESYNC, - 'rendererUrl': OZONE_RENDERER_URL, - 'batchRequests': false /* you can change this to true OR override it in the config: config.ozone.batchRequests */ + 'rendererUrl': OZONE_RENDERER_URL }, loadWhitelabelData(bid) { if (this.propertyBag.whitelabel) { return; } @@ -75,12 +73,6 @@ export const spec = { this.propertyBag.whitelabel.auctionUrl = bidderConfig.endpointOverride.auctionUrl; } } - if (bidderConfig.hasOwnProperty('batchRequests')) { - this.propertyBag.whitelabel.batchRequests = bidderConfig.batchRequests; - } - if (arr.hasOwnProperty('batchRequests')) { - this.propertyBag.whitelabel.batchRequests = true; - } try { if (arr.hasOwnProperty('auction') && arr.auction === 'dev') { logInfo('GET: auction=dev'); @@ -102,15 +94,11 @@ export const spec = { getRendererUrl() { return this.propertyBag.whitelabel.rendererUrl; }, - isBatchRequests() { - logInfo('isBatchRequests going to return ', this.propertyBag.whitelabel.batchRequests); - return this.propertyBag.whitelabel.batchRequests; - }, isBidRequestValid(bid) { this.loadWhitelabelData(bid); logInfo('isBidRequestValid : ', config.getConfig(), bid); let adUnitCode = bid.adUnitCode; // adunit[n].code - let err1 = 'VALIDATION FAILED : missing {param} : siteId, placementId and publisherId are REQUIRED' + let err1 = 'VALIDATION FAILED : missing {param} : siteId, placementId and publisherId are REQUIRED'; if (!(bid.params.hasOwnProperty('placementId'))) { logError(err1.replace('{param}', 'placementId'), adUnitCode); return false; @@ -211,7 +199,7 @@ export const spec = { let placementId = placementIdOverrideFromGetParam || this.getPlacementId(ozoneBidRequest); // prefer to use a valid override param, else the bidRequest placement Id obj.id = ozoneBidRequest.bidId; // this causes an error if we change it to something else, even if you update the bidRequest object: "WARNING: Bidder ozone made bid for unknown request ID: mb7953.859498327448. Ignoring." obj.tagid = placementId; - let parsed = parseUrl(this.getRefererInfo().page); + let parsed = parseUrl(getRefererInfo().page); obj.secure = parsed.protocol === 'https' ? 1 : 0; let arrBannerSizes = []; if (!ozoneBidRequest.hasOwnProperty('mediaTypes')) { @@ -274,7 +262,9 @@ export const spec = { obj.placementId = placementId; deepSetValue(obj, 'ext.prebid', {'storedrequest': {'id': placementId}}); obj.ext[whitelabelBidder] = {}; + // TODO: fix auctionId/transactionID leak: https://github.com/prebid/Prebid.js/issues/9781 obj.ext[whitelabelBidder].adUnitCode = ozoneBidRequest.adUnitCode; // eg. 'mpu' + obj.ext[whitelabelBidder].transactionId = ozoneBidRequest.transactionId; // this is the transactionId PER adUnit, common across bidders for this unit if (ozoneBidRequest.params.hasOwnProperty('customData')) { obj.ext[whitelabelBidder].customData = ozoneBidRequest.params.customData; } @@ -301,10 +291,6 @@ export const spec = { if (!schain && deepAccess(ozoneBidRequest, 'schain')) { schain = ozoneBidRequest.schain; } - let gpid = deepAccess(ozoneBidRequest, 'ortb2Imp.ext.gpid'); - if (gpid) { - deepSetValue(obj, 'ext.gpid', gpid); - } return obj; }); let extObj = {}; @@ -340,7 +326,7 @@ export const spec = { let userExtEids = deepAccess(validBidRequests, '0.userIdAsEids', []); // generate the UserIDs in the correct format for UserId module ozoneRequest.site = { 'publisher': {'id': htmlParams.publisherId}, - 'page': this.getRefererInfo().page, + 'page': getRefererInfo().page, 'id': htmlParams.siteId }; ozoneRequest.test = config.getConfig('debug') ? 1 : 0; @@ -369,33 +355,13 @@ export const spec = { if (config.getConfig('coppa') === true) { deepSetValue(ozoneRequest, 'regs.coppa', 1); } - let ozUuid = generateUUID(); - if (this.isBatchRequests()) { - logInfo('going to batch the requests'); - let arrRet = []; // return an array of objects containing data describing max 10 bids - for (let i = 0; i < tosendtags.length; i += 10) { - ozoneRequest.id = ozUuid; // Unique ID of the bid request, provided by the exchange. (REQUIRED) - ozoneRequest.imp = tosendtags.slice(i, i + 10); - ozoneRequest.ext = extObj; - deepSetValue(ozoneRequest, 'user.ext.eids', userExtEids); - if (ozoneRequest.imp.length > 0) { - arrRet.push({ - method: 'POST', - url: this.getAuctionUrl(), - data: JSON.stringify(ozoneRequest), - bidderRequest: bidderRequest - }); - } - } - logInfo('batch request going to return : ', arrRet); - return arrRet; - } - logInfo('requests will not be batched.'); if (singleRequest) { logInfo('buildRequests starting to generate response for a single request'); - ozoneRequest.id = ozUuid; // Unique ID of the bid request, provided by the exchange. (REQUIRED) + ozoneRequest.id = bidderRequest.auctionId; // Unique ID of the bid request, provided by the exchange. + ozoneRequest.auctionId = bidderRequest.auctionId; // not sure if this should be here? ozoneRequest.imp = tosendtags; ozoneRequest.ext = extObj; + deepSetValue(ozoneRequest, 'source.tid', bidderRequest.auctionId);// RTB 2.5 : tid is Transaction ID that must be common across all participants in this bid request (e.g., potentially multiple exchanges). deepSetValue(ozoneRequest, 'user.ext.eids', userExtEids); var ret = { method: 'POST', @@ -411,9 +377,12 @@ export const spec = { let arrRet = tosendtags.map(imp => { logInfo('buildRequests starting to generate non-single response, working on imp : ', imp); let ozoneRequestSingle = Object.assign({}, ozoneRequest); - ozoneRequestSingle.id = generateUUID(); // Unique ID of the bid request, provided by the exchange. (REQUIRED) + imp.ext[whitelabelBidder].pageAuctionId = bidderRequest['auctionId']; // make a note in the ext object of what the original auctionId was, in the bidderRequest object + ozoneRequestSingle.id = imp.ext[whitelabelBidder].transactionId; // Unique ID of the bid request, provided by the exchange. + ozoneRequestSingle.auctionId = imp.ext[whitelabelBidder].transactionId; // not sure if this should be here? ozoneRequestSingle.imp = [imp]; ozoneRequestSingle.ext = extObj; + deepSetValue(ozoneRequestSingle, 'source.tid', bidderRequest.auctionId);// RTB 2.5 : tid is Transaction ID that must be common across all participants in this bid request (e.g., potentially multiple exchanges). deepSetValue(ozoneRequestSingle, 'user.ext.eids', userExtEids); logInfo('buildRequests RequestSingle (for non-single) = ', ozoneRequestSingle); return { @@ -455,7 +424,6 @@ export const spec = { logInfo(`interpretResponse time: ${startTime} . Time between buildRequests done and interpretResponse start was ${startTime - this.propertyBag.buildRequestsEnd}ms`); logInfo(`serverResponse, request`, JSON.parse(JSON.stringify(serverResponse)), JSON.parse(JSON.stringify(request))); serverResponse = serverResponse.body || {}; - let aucId = serverResponse.id; // this will be correct for single requests and non-single if (!serverResponse.hasOwnProperty('seatbid')) { return []; } @@ -550,7 +518,7 @@ export const spec = { } } let {seat: winningSeat, bid: winningBid} = ozoneGetWinnerForRequestBid(thisBid.bidId, serverResponse.seatbid); - adserverTargeting[whitelabelPrefix + '_auc_id'] = String(aucId); // was request.bidderRequest.auctionId + adserverTargeting[whitelabelPrefix + '_auc_id'] = String(request.bidderRequest.auctionId); adserverTargeting[whitelabelPrefix + '_winner'] = String(winningSeat); adserverTargeting[whitelabelPrefix + '_bid'] = 'true'; adserverTargeting[whitelabelPrefix + '_cache_id'] = deepAccess(thisBid, 'ext.prebid.targeting.hb_cache_id', 'no-id'); @@ -718,29 +686,10 @@ export const spec = { return null; }, getGetParametersAsObject() { - let parsed = parseUrl(this.getRefererInfo().location); + let parsed = parseUrl(getRefererInfo().page); logInfo('getGetParametersAsObject found:', parsed.search); return parsed.search; }, - getRefererInfo() { - if (getRefererInfo().hasOwnProperty('location')) { - logInfo('FOUND location on getRefererInfo OK (prebid >= 7); will use getRefererInfo for location & page'); - return getRefererInfo(); - } else { - logInfo('DID NOT FIND location on getRefererInfo (prebid < 7); will use legacy code that ALWAYS worked reliably to get location & page ;-)'); - try { - return { - page: top.location.href, - location: top.location.href - }; - } catch (e) { - return { - page: window.location.href, - location: window.location.href - }; - } - } - }, blockTheRequest() { let ozRequest = this.getWhitelabelConfigItem('ozone.oz_request'); if (typeof ozRequest == 'boolean' && !ozRequest) { diff --git a/modules/ozoneBidAdapter.md b/modules/ozoneBidAdapter.md index 9787e069283..6f4cf752f22 100644 --- a/modules/ozoneBidAdapter.md +++ b/modules/ozoneBidAdapter.md @@ -73,8 +73,6 @@ adUnits = [{ }]; ``` - -``` //Instream Video adUnit adUnits = [{ @@ -110,4 +108,4 @@ adUnits = [{ } }] }; -``` \ No newline at end of file +``` diff --git a/modules/pairIdSystem.js b/modules/pairIdSystem.js index dbff4c6a402..f90e25fdd0f 100644 --- a/modules/pairIdSystem.js +++ b/modules/pairIdSystem.js @@ -10,10 +10,6 @@ import {getStorageManager} from '../src/storageManager.js' import { logInfo } from '../src/utils.js'; import {MODULE_TYPE_UID} from '../src/activities/modules.js'; -/** - * @typedef {import('../modules/userId/index.js').Submodule} Submodule - */ - const MODULE_NAME = 'pairId'; const PAIR_ID_KEY = 'pairId'; const DEFAULT_LIVERAMP_PAIR_ID_KEY = '_lr_pairId'; @@ -31,29 +27,29 @@ function pairIdFromCookie(key) { /** @type {Submodule} */ export const pairIdSubmodule = { /** - * used to link submodule with config - * @type {string} - */ + * used to link submodule with config + * @type {string} + */ name: MODULE_NAME, /** - * used to specify vendor id - * @type {number} - */ + * used to specify vendor id + * @type {number} + */ gvlid: 755, /** - * decode the stored id value for passing to bid requests - * @function - * @param { string | undefined } value - * @returns {{pairId:string} | undefined } - */ + * decode the stored id value for passing to bid requests + * @function + * @param { string | undefined } value + * @returns {{pairId:string} | undefined } + */ decode(value) { return value && Array.isArray(value) ? {'pairId': value} : undefined }, /** - * performs action to obtain id and return a value in the callback's response argument - * @function - * @returns {id: string | undefined } - */ + * performs action to obtain id and return a value in the callback's response argument + * @function + * @returns {id: string | undefined } + */ getId(config) { const pairIdsString = pairIdFromLocalStorage(PAIR_ID_KEY) || pairIdFromCookie(PAIR_ID_KEY) let ids = [] @@ -82,12 +78,6 @@ export const pairIdSubmodule = { return undefined; } return {'id': ids}; - }, - eids: { - 'pairId': { - source: 'google.com', - atype: 571187 - }, } }; diff --git a/modules/pangleBidAdapter.js b/modules/pangleBidAdapter.js deleted file mode 100644 index f4a52168743..00000000000 --- a/modules/pangleBidAdapter.js +++ /dev/null @@ -1,178 +0,0 @@ -// ver V1.0.4 -import { BANNER, VIDEO } from '../src/mediaTypes.js'; -import { ortbConverter } from '../libraries/ortbConverter/converter.js' -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { deepSetValue, generateUUID, timestamp, deepAccess } from '../src/utils.js'; -import { getStorageManager } from '../src/storageManager.js'; -import { MODULE_TYPE_RTD } from '../src/activities/modules.js'; - -import { Renderer } from '../src/Renderer.js'; - -const BIDDER_CODE = 'pangle'; -const ENDPOINT = 'https://pangle.pangleglobal.com/api/ad/union/web_js/common/get_ads'; - -const OUTSTREAM_RENDERER_URL = 'https://sf16-static.i18n-pglstatp.com/obj/ad-pattern-sg/pangle/web/ads/video.js'; - -const DEFAULT_BID_TTL = 30; -const DEFAULT_CURRENCY = 'USD'; -const DEFAULT_NET_REVENUE = true; -const PANGLE_COOKIE = '_pangle_id'; -const COOKIE_EXP = 86400 * 1000 * 365 * 1; // 1 year -const MEDIA_TYPES = { - Banner: 1, - Video: 2 -}; - -export const storage = getStorageManager({ moduleType: MODULE_TYPE_RTD, moduleName: BIDDER_CODE }) - -export function isValidUuid(uuid) { - return /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test( - uuid - ); -} - -function getPangleCookieId() { - let sid = storage.cookiesAreEnabled() && storage.getCookie(PANGLE_COOKIE); - - if (!sid || !isValidUuid(sid)) { - sid = generateUUID(); - setPangleCookieId(sid); - } - - return sid; -} - -function setPangleCookieId(sid) { - if (storage.cookiesAreEnabled()) { - const expires = new Date(timestamp() + COOKIE_EXP).toGMTString(); - - storage.setCookie(PANGLE_COOKIE, sid, expires); - } -} - -function createRequest(bidRequests, bidderRequest, mediaType) { - const data = converter.toORTB({ - bidRequests, - bidderRequest, - context: { mediaType }, - }); - const devicetype = spec.getDeviceType(navigator.userAgent); - deepSetValue(data, 'device.devicetype', devicetype); - if (bidderRequest.userId && typeof bidderRequest.userId === 'object') { - const pangleId = getPangleCookieId(); - // add pangle cookie - const _eids = data.user?.ext?.eids ?? []; - deepSetValue(data, 'user.ext.eids', [ - ..._eids, - { - source: document.location.host, - uids: [ - { - id: pangleId, - atype: 1, - }, - ], - }, - ]); - } - bidRequests.forEach((item, idx) => { - deepSetValue(data.imp[idx], 'ext.networkids', item.params); - deepSetValue(data.imp[idx], 'banner.api', [5]); - deepSetValue(data, 'test', item.params.test ?? 0) - }); - return { - method: 'POST', - url: ENDPOINT, - data, - options: { contentType: 'application/json', withCredentials: true } - } -} - -function isVideoBid(bid) { - return !!deepAccess(bid, 'mediaTypes.video'); -} - -function isBannerBid(bid) { - return !!deepAccess(bid, 'mediaTypes.banner'); -} - -function renderOutstream(bid) { - bid.renderer.push(() => { - window.outstreamPlayer({ bid, codeId: bid.adUnitCode }); - }); -} - -const converter = ortbConverter({ - context: { - netRevenue: DEFAULT_NET_REVENUE, - ttl: DEFAULT_BID_TTL, - currency: DEFAULT_CURRENCY, - }, - bidResponse(buildBidResponse, bid, context) { - const { bidRequest } = context; - let bidResponse; - if (bid.mtype === MEDIA_TYPES.Video) { - 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}); - renderer.setRender(renderOutstream); - bidResponse.renderer = renderer; - } - } - if (bid.mtype === MEDIA_TYPES.Banner) { - context.mediaType = BANNER; - bidResponse = buildBidResponse(bid, context); - } - return bidResponse; - }, -}); - -export const spec = { - code: BIDDER_CODE, - supportedMediaTypes: [BANNER, VIDEO], - - getDeviceType: function (ua) { - if ( - /ipad|android 3.0|xoom|sch-i800|playbook|tablet|kindle/i.test( - ua.toLowerCase() - ) - ) { - return 5; // 'tablet' - } - if ( - /iphone|ipod|android|blackberry|opera|mini|windows\sce|palm|smartphone|iemobile/i.test( - ua.toLowerCase() - ) - ) { - return 4; // 'mobile' - } - return 2; // 'desktop' - }, - - isBidRequestValid: function (bid) { - return Boolean(bid.params.token); - }, - - buildRequests(bidRequests, bidderRequest) { - const videoBids = bidRequests.filter((bid) => isVideoBid(bid)); - const bannerBids = bidRequests.filter((bid) => isBannerBid(bid)); - let requests = bannerBids.length - ? [createRequest(bannerBids, bidderRequest, BANNER)] - : []; - videoBids.forEach((bid) => { - requests.push(createRequest([bid], bidderRequest, VIDEO)); - }); - return requests; - }, - - interpretResponse(response, request) { - const bids = converter.fromORTB({ - response: response.body, - request: request.data, - }).bids; - return bids; - }, -}; - -registerBidder(spec); diff --git a/modules/pangleBidAdapter.md b/modules/pangleBidAdapter.md deleted file mode 100644 index 8fc628dcc89..00000000000 --- a/modules/pangleBidAdapter.md +++ /dev/null @@ -1,32 +0,0 @@ -# Overview - -Module Name: pangle Bidder Adapter -Module Type: Bidder Adapter -Maintainer: - -# Description - -An adapter to get a bid from pangle DSP. - -# Test Parameters - -```javascript -var adUnits = [{ - // banner - code: 'test1', - mediaTypes: { - banner: { - sizes: [[300, 250]] - } - }, - - bids: [{ - bidder: 'pangle', - params: { - token: 'aass', - appid: 612, - placementid: 123, - } - }] -}]; -``` diff --git a/modules/parrableIdSystem.js b/modules/parrableIdSystem.js index 5651bdf0434..01ffec3c249 100644 --- a/modules/parrableIdSystem.js +++ b/modules/parrableIdSystem.js @@ -7,17 +7,7 @@ // ci trigger: 1 -import { - contains, - deepClone, - inIframe, - isEmpty, - isPlainObject, - logError, - logWarn, - pick, - timestamp -} from '../src/utils.js'; +import {contains, deepClone, inIframe, isEmpty, isPlainObject, logError, logWarn, timestamp} from '../src/utils.js'; import {find} from '../src/polyfill.js'; import {ajax} from '../src/ajax.js'; import {submodule} from '../src/hook.js'; @@ -26,12 +16,6 @@ import {uspDataHandler} from '../src/adapterManager.js'; import {getStorageManager} from '../src/storageManager.js'; import {MODULE_TYPE_UID} from '../src/activities/modules.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 - */ - const PARRABLE_URL = 'https://h.parrable.com/prebid'; const PARRABLE_COOKIE_NAME = '_parrable_id'; const PARRABLE_GVLID = 928; @@ -384,33 +368,7 @@ export const parrableIdSubmodule = { getId(config, gdprConsentData, currentStoredId) { const configParams = (config && config.params) || {}; return fetchId(configParams, gdprConsentData); - }, - eids: { - 'parrableId': { - source: 'parrable.com', - atype: 1, - getValue: function(parrableId) { - if (parrableId.eid) { - return parrableId.eid; - } - if (parrableId.ccpaOptout) { - // If the EID was suppressed due to a non consenting ccpa optout then - // we still wish to provide this as a reason to the adapters - return ''; - } - return null; - }, - getUidExt: function(parrableId) { - const extendedData = pick(parrableId, [ - 'ibaOptout', - 'ccpaOptout' - ]); - if (Object.keys(extendedData).length) { - return extendedData; - } - } - }, - }, + } }; submodule('userId', parrableIdSubmodule); diff --git a/modules/permutiveRtdProvider.js b/modules/permutiveRtdProvider.js index 5a63990f84f..697d7721205 100644 --- a/modules/permutiveRtdProvider.js +++ b/modules/permutiveRtdProvider.js @@ -12,10 +12,6 @@ import {deepAccess, deepSetValue, isFn, logError, mergeDeep, isPlainObject, safe import {includes} from '../src/polyfill.js'; import {MODULE_TYPE_RTD} from '../src/activities/modules.js'; -/** - * @typedef {import('../modules/rtdModule/index.js').RtdSubmodule} RtdSubmodule - */ - const MODULE_NAME = 'permutive' const logger = prefixLog('[PermutiveRTD]') diff --git a/modules/pgamsspBidAdapter.js b/modules/pgamsspBidAdapter.js deleted file mode 100644 index b38131c1c18..00000000000 --- a/modules/pgamsspBidAdapter.js +++ /dev/null @@ -1,229 +0,0 @@ -import { isFn, deepAccess, logMessage, logError } from '../src/utils.js'; -import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; - -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; -import { config } from '../src/config.js'; - -const BIDDER_CODE = 'pgamssp'; -const AD_URL = 'https://us-east.pgammedia.com/pbjs'; -const SYNC_URL = 'https://cs.pgammedia.com'; - -function isBidResponseValid(bid) { - if (!bid.requestId || !bid.cpm || !bid.creativeId || !bid.ttl || !bid.currency) { - return false; - } - - switch (bid.mediaType) { - case BANNER: - return Boolean(bid.width && bid.height && bid.ad); - case VIDEO: - return Boolean(bid.vastUrl || bid.vastXml); - case NATIVE: - return Boolean(bid.native && bid.native.impressionTrackers && bid.native.impressionTrackers.length); - default: - return false; - } -} - -function getPlacementReqData(bid) { - const { params, bidId, mediaTypes } = bid; - const schain = bid.schain || {}; - const { placementId, endpointId } = params; - const bidfloor = getBidFloor(bid); - - const placement = { - bidId, - schain, - bidfloor, - eids: [] - }; - - if (bid.userId) { - getUserId(placement.eids, bid.userId.uid2 && bid.userId.uid2.id, 'uidapi.com'); - } - - if (placementId) { - placement.placementId = placementId; - placement.type = 'publisher'; - } else if (endpointId) { - placement.endpointId = endpointId; - placement.type = 'network'; - } - - if (mediaTypes && mediaTypes[BANNER]) { - placement.adFormat = BANNER; - placement.sizes = mediaTypes[BANNER].sizes; - } else if (mediaTypes && mediaTypes[VIDEO]) { - placement.adFormat = VIDEO; - placement.playerSize = mediaTypes[VIDEO].playerSize; - placement.minduration = mediaTypes[VIDEO].minduration; - placement.maxduration = mediaTypes[VIDEO].maxduration; - placement.mimes = mediaTypes[VIDEO].mimes; - placement.protocols = mediaTypes[VIDEO].protocols; - placement.startdelay = mediaTypes[VIDEO].startdelay; - placement.placement = mediaTypes[VIDEO].placement; - placement.skip = mediaTypes[VIDEO].skip; - placement.skipafter = mediaTypes[VIDEO].skipafter; - placement.minbitrate = mediaTypes[VIDEO].minbitrate; - placement.maxbitrate = mediaTypes[VIDEO].maxbitrate; - placement.delivery = mediaTypes[VIDEO].delivery; - placement.playbackmethod = mediaTypes[VIDEO].playbackmethod; - placement.api = mediaTypes[VIDEO].api; - placement.linearity = mediaTypes[VIDEO].linearity; - } else if (mediaTypes && mediaTypes[NATIVE]) { - placement.native = mediaTypes[NATIVE]; - placement.adFormat = NATIVE; - } - - return placement; -} - -function getBidFloor(bid) { - if (!isFn(bid.getFloor)) { - return deepAccess(bid, 'params.bidfloor', 0); - } - - try { - const bidFloor = bid.getFloor({ - currency: 'USD', - mediaType: '*', - size: '*', - }); - return bidFloor.floor; - } catch (err) { - logError(err); - return 0; - } -} -function getUserId(eids, id, source, uidExt) { - if (id) { - var uid = { id }; - if (uidExt) { - uid.ext = uidExt; - } - eids.push({ - source, - uids: [ uid ] - }); - } -} - -export const spec = { - code: BIDDER_CODE, - supportedMediaTypes: [BANNER, VIDEO, NATIVE], - - isBidRequestValid: (bid = {}) => { - const { params, bidId, mediaTypes } = bid; - let valid = Boolean(bidId && params && (params.placementId || params.endpointId)); - - if (mediaTypes && mediaTypes[BANNER]) { - valid = valid && Boolean(mediaTypes[BANNER] && mediaTypes[BANNER].sizes); - } else if (mediaTypes && mediaTypes[VIDEO]) { - valid = valid && Boolean(mediaTypes[VIDEO] && mediaTypes[VIDEO].playerSize); - } else if (mediaTypes && mediaTypes[NATIVE]) { - valid = valid && Boolean(mediaTypes[NATIVE]); - } else { - valid = false; - } - return valid; - }, - - buildRequests: (validBidRequests = [], bidderRequest = {}) => { - // convert Native ORTB definition to old-style prebid native definition - validBidRequests = convertOrtbRequestToProprietaryNative(validBidRequests); - - let deviceWidth = 0; - let deviceHeight = 0; - - let winLocation; - try { - const winTop = window.top; - deviceWidth = winTop.screen.width; - deviceHeight = winTop.screen.height; - winLocation = winTop.location; - } catch (e) { - logMessage(e); - winLocation = window.location; - } - - const refferUrl = bidderRequest.refererInfo && bidderRequest.refererInfo.page; - let refferLocation; - try { - refferLocation = refferUrl && new URL(refferUrl); - } catch (e) { - logMessage(e); - } - // TODO: does the fallback make sense here? - let location = refferLocation || winLocation; - const language = (navigator && navigator.language) ? navigator.language.split('-')[0] : ''; - const host = location.host; - const page = location.pathname; - const secure = location.protocol === 'https:' ? 1 : 0; - const placements = []; - const request = { - deviceWidth, - deviceHeight, - language, - secure, - host, - page, - placements, - coppa: config.getConfig('coppa') === true ? 1 : 0, - ccpa: bidderRequest.uspConsent || undefined, - gdpr: bidderRequest.gdprConsent || undefined, - tmax: bidderRequest.timeout - }; - - const len = validBidRequests.length; - for (let i = 0; i < len; i++) { - const bid = validBidRequests[i]; - placements.push(getPlacementReqData(bid)); - } - - return { - method: 'POST', - url: AD_URL, - data: request - }; - }, - - interpretResponse: (serverResponse) => { - let response = []; - for (let i = 0; i < serverResponse.body.length; i++) { - let resItem = serverResponse.body[i]; - if (isBidResponseValid(resItem)) { - const advertiserDomains = resItem.adomain && resItem.adomain.length ? resItem.adomain : []; - resItem.meta = { ...resItem.meta, advertiserDomains }; - - response.push(resItem); - } - } - return response; - }, - - getUserSyncs: (syncOptions, serverResponses, gdprConsent, uspConsent) => { - let syncType = syncOptions.iframeEnabled ? 'iframe' : 'image'; - let syncUrl = SYNC_URL + `/${syncType}?pbjs=1`; - if (gdprConsent && gdprConsent.consentString) { - if (typeof gdprConsent.gdprApplies === 'boolean') { - syncUrl += `&gdpr=${Number(gdprConsent.gdprApplies)}&gdpr_consent=${gdprConsent.consentString}`; - } else { - syncUrl += `&gdpr=0&gdpr_consent=${gdprConsent.consentString}`; - } - } - if (uspConsent && uspConsent.consentString) { - syncUrl += `&ccpa_consent=${uspConsent.consentString}`; - } - - const coppa = config.getConfig('coppa') ? 1 : 0; - syncUrl += `&coppa=${coppa}`; - - return [{ - type: syncType, - url: syncUrl - }]; - } -}; - -registerBidder(spec); diff --git a/modules/pgamsspBidAdapter.md b/modules/pgamsspBidAdapter.md deleted file mode 100644 index c162ec33053..00000000000 --- a/modules/pgamsspBidAdapter.md +++ /dev/null @@ -1,79 +0,0 @@ -# Overview - -``` -Module Name: PGAMSSP Bidder Adapter -Module Type: PGAMSSP Bidder Adapter -Maintainer: info@pgammedia.com -``` - -# Description - -Connects to PGAMSSP exchange for bids. -PGAMSSP 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: 'pgamssp', - params: { - placementId: 'testBanner', - } - } - ] - }, - { - code: 'addunit2', - mediaTypes: { - video: { - playerSize: [ [640, 480] ], - context: 'instream', - minduration: 5, - maxduration: 60, - } - }, - bids: [ - { - bidder: 'pgamssp', - params: { - placementId: 'testVideo', - } - } - ] - }, - { - code: 'addunit3', - mediaTypes: { - native: { - title: { - required: true - }, - body: { - required: true - }, - icon: { - required: true, - size: [64, 64] - } - } - }, - bids: [ - { - bidder: 'pgamssp', - params: { - placementId: 'testNative', - } - } - ] - } - ]; -``` \ No newline at end of file diff --git a/modules/pilotxBidAdapter.js b/modules/pilotxBidAdapter.js index 0fb39e19076..335c461e3d9 100644 --- a/modules/pilotxBidAdapter.js +++ b/modules/pilotxBidAdapter.js @@ -1,12 +1,4 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; - -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse - * @typedef {import('../src/adapters/bidderFactory.js').validBidRequests} validBidRequests - */ - const BIDDER_CODE = 'pilotx'; const ENDPOINT_URL = '//adn.pilotx.tv/hb' export const spec = { @@ -14,11 +6,11 @@ export const spec = { supportedMediaTypes: ['banner', 'video'], aliases: ['pilotx'], // short code /** - * Determines whether or not the given bid request is valid. - * - * @param {BidRequest} bid The bid params to validate. - * @return boolean True if this is a valid bid, and false otherwise. - */ + * Determines whether or not the given bid request is valid. + * + * @param {BidRequest} bid The bid params to validate. + * @return boolean True if this is a valid bid, and false otherwise. + */ isBidRequestValid: function (bid) { let sizesCheck = !!bid.sizes let paramSizesCheck = !!bid.params.sizes @@ -43,11 +35,11 @@ export const spec = { return !!(bid.params.placementId); }, /** - * Make a server request from the list of BidRequests. - * - * @param {validBidRequests[]} - an array of bids - * @return ServerRequest Info describing the request to the server. - */ + * Make a server request from the list of BidRequests. + * + * @param {validBidRequests[]} - an array of bids + * @return ServerRequest Info describing the request to the server. + */ buildRequests: function (validBidRequests, bidderRequest) { let payloadItems = {}; validBidRequests.forEach(bidRequest => { @@ -92,11 +84,11 @@ export const spec = { }; }, /** - * Unpack the 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. - */ + * Unpack the 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. + */ interpretResponse: function (serverResponse, bidRequest) { const serverBody = serverResponse.body; const bidResponses = []; diff --git a/modules/pixfutureBidAdapter.js b/modules/pixfutureBidAdapter.js index 1c3f9b8da1a..608ba20aa5f 100644 --- a/modules/pixfutureBidAdapter.js +++ b/modules/pixfutureBidAdapter.js @@ -3,11 +3,10 @@ import {getStorageManager} from '../src/storageManager.js'; import {BANNER} from '../src/mediaTypes.js'; import {config} from '../src/config.js'; import {find, includes} from '../src/polyfill.js'; -import {deepAccess, isArray, isFn, isNumber, isPlainObject} from '../src/utils.js'; +import {convertCamelToUnderscore, deepAccess, isArray, isFn, isNumber, isPlainObject} from '../src/utils.js'; import {auctionManager} from '../src/auctionManager.js'; import {getGlobal} from '../src/prebidGlobal.js'; -import {getANKeywordParam} from '../libraries/appnexusUtils/anKeywords.js'; -import {convertCamelToUnderscore} from '../libraries/appnexusUtils/anUtils.js'; +import {getANKeywordParam} from '../libraries/appnexusKeywords/anKeywords.js'; const SOURCE = 'pbjs'; const storageManager = getStorageManager({bidderCode: 'pixfuture'}); diff --git a/modules/prebidServerBidAdapter/config.js b/modules/prebidServerBidAdapter/config.js index 87274504f64..f6b8ac9f86a 100644 --- a/modules/prebidServerBidAdapter/config.js +++ b/modules/prebidServerBidAdapter/config.js @@ -1,11 +1,11 @@ // accountId and bidders params are not included here, should be configured by end-user export const S2S_VENDORS = { - 'appnexuspsp': { + 'appnexus': { adapter: 'prebidServer', enabled: true, endpoint: { - p1Consent: 'https://ib.adnxs.com/openrtb2/prebid', - noP1Consent: 'https://ib.adnxs-simple.com/openrtb2/prebid' + p1Consent: 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction', + noP1Consent: 'https://prebid.adnxs-simple.com/pbs/v1/openrtb2/auction' }, syncEndpoint: { p1Consent: 'https://prebid.adnxs.com/pbs/v1/cookie_sync', @@ -13,6 +13,15 @@ export const S2S_VENDORS = { }, timeout: 1000 }, + 'appnexuspsp': { + adapter: 'prebidServer', + enabled: true, + endpoint: { + p1Consent: 'https://ib.adnxs.com/openrtb2/prebid', + noP1Consent: 'https://ib.adnxs-simple.com/openrtb2/prebid' + }, + timeout: 1000 + }, 'rubicon': { adapter: 'prebidServer', enabled: true, @@ -38,14 +47,5 @@ export const S2S_VENDORS = { noP1Consent: 'https://prebid.openx.net/cookie_sync' }, timeout: 1000 - }, - 'openwrap': { - adapter: 'prebidServer', - enabled: true, - endpoint: { - p1Consent: 'https://ow.pubmatic.com/openrtb2/auction?source=pbjs', - noP1Consent: 'https://ow.pubmatic.com/openrtb2/auction?source=pbjs' - }, - timeout: 500 } } diff --git a/modules/prebidServerBidAdapter/index.js b/modules/prebidServerBidAdapter/index.js index 6e4aec8ad92..3cc38923d57 100644 --- a/modules/prebidServerBidAdapter/index.js +++ b/modules/prebidServerBidAdapter/index.js @@ -1,6 +1,6 @@ import Adapter from '../../src/adapter.js'; import { - deepAccess, + bind, deepClone, flatten, generateUUID, @@ -15,9 +15,10 @@ import { logWarn, triggerPixel, uniques, + deepAccess, } from '../../src/utils.js'; import CONSTANTS from '../../src/constants.json'; -import adapterManager, {s2sActivityParams} from '../../src/adapterManager.js'; +import adapterManager from '../../src/adapterManager.js'; import {config} from '../../src/config.js'; import {addComponentAuction, isValid} from '../../src/adapters/bidderFactory.js'; import * as events from '../../src/events.js'; @@ -28,8 +29,6 @@ import {hook} from '../../src/hook.js'; import {hasPurpose1Consent} from '../../src/utils/gpdr.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'; const getConfig = config.getConfig; @@ -207,7 +206,7 @@ getConfig('s2sConfig', ({s2sConfig}) => setS2sConfig(s2sConfig)); /** * resets the _synced variable back to false, primiarily used for testing purposes - */ +*/ export function resetSyncedStatus() { _syncCount = 0; } @@ -296,7 +295,7 @@ function doAllSyncs(bidders, s2sConfig) { // if PBS reports this bidder doesn't have an ID, then call the sync and recurse to the next sync entry if (thisSync.no_cookie) { - doPreBidderSync(thisSync.usersync.type, thisSync.usersync.url, thisSync.bidder, doAllSyncs.bind(null, bidders, s2sConfig), s2sConfig); + doPreBidderSync(thisSync.usersync.type, thisSync.usersync.url, thisSync.bidder, bind.call(doAllSyncs, null, bidders, s2sConfig), s2sConfig); } else { // bidder already has an ID, so just recurse to the next sync entry doAllSyncs(bidders, s2sConfig); @@ -355,7 +354,8 @@ function doClientSideSyncs(bidders, gdprConsent, uspConsent, gppConsent) { if (clientAdapter && clientAdapter.registerSyncs) { config.runWithBidder( bidder, - clientAdapter.registerSyncs.bind( + bind.call( + clientAdapter.registerSyncs, clientAdapter, [], gdprConsent, @@ -478,14 +478,10 @@ export function PrebidServer() { adapterMetrics }); } - done(false); + done(); doClientSideSyncs(requestedBidders, gdprConsent, uspConsent, gppConsent); }, - onError(msg, error) { - logError(`Prebid server call failed: '${msg}'`, error); - bidRequests.forEach(bidderRequest => events.emit(CONSTANTS.EVENTS.BIDDER_ERROR, {error, bidderRequest})); - done(error.timedOut); - }, + onError: done, onBid: function ({adUnit, bid}) { const metrics = bid.metrics = s2sBidRequest.metrics.fork().renameWith(); metrics.checkpoint('addBidResponse'); @@ -503,8 +499,8 @@ export function PrebidServer() { } } }, - onFledge: (params) => { - addComponentAuction({auctionId: bidRequests[0].auctionId, ...params}, params.config); + onFledge: ({adUnitCode, config}) => { + addComponentAuction(adUnitCode, config); } }) } @@ -575,11 +571,7 @@ export const processPBSRequest = hook('sync', function (s2sBidRequest, bidReques } }, requestJson, - { - contentType: 'text/plain', - withCredentials: true, - browsingTopics: isActivityAllowed(ACTIVITY_TRANSMIT_UFPD, s2sActivityParams(s2sBidRequest.s2sConfig)) - } + {contentType: 'text/plain', withCredentials: true} ); } else { logError('PBS request not made. Check endpoints.'); @@ -593,7 +585,7 @@ function shouldEmitNonbids(s2sConfig, response) { /** * Global setter that sets eids permissions for bidders * This setter is to be used by userId module when included - * @param {Array} newEidPermissions + * @param {array} newEidPermissions */ function setEidPermissions(newEidPermissions) { eidPermissions = newEidPermissions; diff --git a/modules/prebidServerBidAdapter/ortbConverter.js b/modules/prebidServerBidAdapter/ortbConverter.js index 7aeb4302280..9d29b66cfc8 100644 --- a/modules/prebidServerBidAdapter/ortbConverter.js +++ b/modules/prebidServerBidAdapter/ortbConverter.js @@ -17,14 +17,13 @@ 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 {beConvertCurrency} from '../../src/utils/currency.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'; const DEFAULT_S2S_TTL = 60; const DEFAULT_S2S_CURRENCY = 'USD'; @@ -142,7 +141,6 @@ const PBS_CONVERTER = ortbConverter({ bidfloor(orig, imp, proxyBidRequest, context) { // for bid floors, we pass each bidRequest associated with this imp through normal bidfloor processing, // and aggregate all of them into a single, minimum floor to put in the request - const getMin = minimum(currencyCompare(floor => [floor.bidfloor, floor.bidfloorcur])); let min; for (const req of context.actualBidRequests.values()) { const floor = {}; @@ -151,8 +149,14 @@ const PBS_CONVERTER = ortbConverter({ if (floor.bidfloorcur == null || floor.bidfloor == null) { min = null; break; + } else if (min == null) { + min = floor; + } else { + const value = beConvertCurrency(floor.bidfloor, floor.bidfloorcur, min.bidfloorcur); + if (value != null && value < min.bidfloor) { + min = floor; + } } - min = min == null ? floor : getMin(min, floor); } if (min != null) { Object.assign(imp, min); @@ -164,10 +168,6 @@ const PBS_CONVERTER = ortbConverter({ // FPD is handled different for PBS - the base request will only contain global FPD; // bidder-specific values are set in ext.prebid.bidderconfig - if (context.transmitTids) { - deepSetValue(ortbRequest, 'source.tid', proxyBidderRequest.auctionId); - } - mergeDeep(ortbRequest, context.s2sBidRequest.ortb2Fragments?.global); // also merge in s2sConfig.extPrebid @@ -240,16 +240,7 @@ const PBS_CONVERTER = ortbConverter({ }, fledgeAuctionConfigs(orig, response, ortbResponse, context) { const configs = Object.values(context.impContext) - .flatMap((impCtx) => (impCtx.fledgeConfigs || []).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, - config: cfg.config - }; - })); + .flatMap((impCtx) => (impCtx.fledgeConfigs || []).map(cfg => ({adUnitCode: impCtx.adUnit.code, config: cfg.config}))); if (configs.length > 0) { response.fledgeAuctionConfigs = configs; } diff --git a/modules/precisoBidAdapter.js b/modules/precisoBidAdapter.js deleted file mode 100644 index 9125f6f3911..00000000000 --- a/modules/precisoBidAdapter.js +++ /dev/null @@ -1,212 +0,0 @@ -import { logMessage, isFn, deepAccess, logInfo } 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'; - -const BIDDER_CODE = 'preciso'; -const AD_URL = 'https://ssp-bidder.mndtrk.com/bid_request/openrtb'; -const URL_SYNC = 'https://ck.2trk.info/rtb/user/usersync.aspx?'; -const SUPPORTED_MEDIA_TYPES = [BANNER, NATIVE, VIDEO]; -const GVLID = 874; -let userId = 'NA'; - -export const spec = { - code: BIDDER_CODE, - supportedMediaTypes: SUPPORTED_MEDIA_TYPES, - gvlid: GVLID, - - isBidRequestValid: (bid) => { - return Boolean(bid.bidId && bid.params && !isNaN(bid.params.publisherId) && bid.params.host == 'prebid'); - }, - - buildRequests: (validBidRequests = [], bidderRequest) => { - // convert Native ORTB definition to old-style prebid native definition - validBidRequests = convertOrtbRequestToProprietaryNative(validBidRequests); - // userId = validBidRequests[0].userId.pubcid; - let winTop = window; - let location; - var offset = new Date().getTimezoneOffset(); - logInfo('timezone ' + offset); - var city = Intl.DateTimeFormat().resolvedOptions().timeZone; - logInfo('location test' + city) - - const countryCode = getCountryCodeByTimezone(city); - logInfo(`The country code for ${city} is ${countryCode}`); - - // TODO: this odd try-catch block was copied in several adapters; it doesn't seem to be correct for cross-origin - try { - location = new URL(bidderRequest.refererInfo.page) - winTop = window.top; - } catch (e) { - location = winTop.location; - logMessage(e); - }; - - let request = { - id: validBidRequests[0].bidderRequestId, - - imp: validBidRequests.map(request => { - const { bidId, sizes, mediaType, ortb2 } = request - const item = { - id: bidId, - region: request.params.region, - traffic: mediaType, - bidFloor: getBidFloor(request), - ortb2: ortb2 - - } - - if (request.mediaTypes.banner) { - item.banner = { - format: (request.mediaTypes.banner.sizes || sizes).map(size => { - return { w: size[0], h: size[1] } - }), - } - } - - if (request.schain) { - item.schain = request.schain; - } - - if (request.floorData) { - item.bidFloor = request.floorData.floorMin; - } - return item - }), - auctionId: validBidRequests[0].auctionId, - 'deviceWidth': winTop.screen.width, - 'deviceHeight': winTop.screen.height, - 'language': (navigator && navigator.language) ? navigator.language : '', - geo: navigator.geolocation.getCurrentPosition(position => { - const { latitude, longitude } = position.coords; - return { - latitude: latitude, - longitude: longitude - } - // Show a map centered at latitude / longitude. - }) || { utcoffset: new Date().getTimezoneOffset() }, - city: city, - 'host': location.host, - 'page': location.pathname, - 'coppa': config.getConfig('coppa') === true ? 1 : 0 - // userId: validBidRequests[0].userId - }; - - request.language.indexOf('-') != -1 && (request.language = request.language.split('-')[0]) - if (bidderRequest) { - if (bidderRequest.uspConsent) { - request.ccpa = bidderRequest.uspConsent; - } - if (bidderRequest.gdprConsent) { - request.gdpr = bidderRequest.gdprConsent - } - if (bidderRequest.gppConsent) { - request.gpp = bidderRequest.gppConsent; - } - } - - return { - method: 'POST', - url: AD_URL, - data: request, - - }; - }, - - interpretResponse: function (serverResponse) { - const response = serverResponse.body - - const bids = [] - - response.seatbid.forEach(seat => { - seat.bid.forEach(bid => { - bids.push({ - requestId: bid.impid, - cpm: bid.price, - width: bid.w, - height: bid.h, - creativeId: bid.crid, - ad: bid.adm, - currency: 'USD', - netRevenue: true, - ttl: 300, - meta: { - advertiserDomains: bid.adomain || [], - }, - }) - }) - }) - - return bids - }, - - getUserSyncs: (syncOptions, serverResponses = [], gdprConsent = {}, uspConsent = '', gppConsent = '') => { - let syncs = []; - let { gdprApplies, consentString = '' } = gdprConsent; - - if (serverResponses.length > 0) { - logInfo('preciso bidadapter getusersync serverResponses:' + serverResponses.toString); - } - if (syncOptions.iframeEnabled) { - syncs.push({ - type: 'iframe', - url: `${URL_SYNC}id=${userId}&gdpr=${gdprApplies ? 1 : 0}&gdpr_consent=${consentString}&us_privacy=${uspConsent}&t=4` - }); - } else { - syncs.push({ - type: 'image', - url: `${URL_SYNC}&gdpr=${gdprApplies ? 1 : 0}&gdpr_consent=${consentString}&us_privacy=${uspConsent}&t=2` - }); - } - - return syncs - } - -}; - -function getCountryCodeByTimezone(city) { - try { - const now = new Date(); - const options = { - timeZone: city, - timeZoneName: 'long', - }; - const [timeZoneName] = new Intl.DateTimeFormat('en-US', options) - .formatToParts(now) - .filter((part) => part.type === 'timeZoneName'); - - if (timeZoneName) { - // Extract the country code from the timezone name - const parts = timeZoneName.value.split('-'); - if (parts.length >= 2) { - return parts[1]; - } - } - } catch (error) { - // Handle errors, such as an invalid timezone city - logInfo(error); - } - - // Handle the case where the city is not found or an error occurred - return 'Unknown'; -} - -function getBidFloor(bid) { - if (!isFn(bid.getFloor)) { - return deepAccess(bid, 'params.bidFloor', 0); - } - - try { - const bidFloor = bid.getFloor({ - currency: 'USD', - mediaType: '*', - size: '*', - }); - return bidFloor.floor; - } catch (_) { - return 0 - } -} - -registerBidder(spec); diff --git a/modules/precisoBidAdapter.md b/modules/precisoBidAdapter.md deleted file mode 100644 index b1fb0d062da..00000000000 --- a/modules/precisoBidAdapter.md +++ /dev/null @@ -1,84 +0,0 @@ -# Overview - -``` -Module Name: Preciso Bidder Adapter -Module Type: Bidder Adapter -Maintainer: tech@preciso.net -``` - -# Description - -Module that connects to preciso' demand sources - -# Parameters - -| Name | Scope | Description | Example | -| :------------ | :------- | :------------------------ | :------------------- | -| `region` | required (for prebid.js) | region | "prebid-eu" | -| `publisherId` | required (for prebid-server) | partner ID | "1901" | -| `traffic` | optional (for prebid.js) | Configures the mediaType that should be used. Values can be banner, native or video | "banner" | - -# Test Parameters -``` - var adUnits = [ - // Will return static native ad. Assets are stored through user UI for each placement separetly - { - code: 'placementId_0', - mediaTypes: { - native: {} - }, - bids: [ - { - bidder: 'preciso', - params: { - host: 'prebid', - publisherId: '0', - region: 'prebid-eu', - traffic: 'native' - } - } - ] - }, - // Will return static test banner - { - code: 'placementId_0', - mediaTypes: { - banner: { - sizes: [[300, 250]], - } - }, - bids: [ - { - bidder: 'preciso', - params: { - host: 'prebid', - publisherId: '0', - region: 'prebid-eu', - traffic: 'banner' - } - } - ] - }, - // Will return test vast xml. All video params are stored under placement in publishers UI - { - code: 'placementId_0', - mediaTypes: { - video: { - playerSize: [640, 480], - context: 'instream' - } - }, - bids: [ - { - bidder: 'preciso', - params: { - host: 'prebid', - publisherId: '0', - region: 'prebid-eu', - traffic: 'video' - } - } - ] - } - ]; -``` \ No newline at end of file diff --git a/modules/priceFloors.js b/modules/priceFloors.js index 9c3869c480a..37167fff691 100644 --- a/modules/priceFloors.js +++ b/modules/priceFloors.js @@ -4,6 +4,7 @@ import { deepClone, deepSetValue, generateUUID, + getGptSlotInfoForAdUnitCode, getParameterByName, isNumber, logError, @@ -12,8 +13,7 @@ import { mergeDeep, parseGPTSingleSizeArray, parseUrl, - pick, - deepEqual + pick } from '../src/utils.js'; import {getGlobal} from '../src/prebidGlobal.js'; import {config} from '../src/config.js'; @@ -27,9 +27,8 @@ 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 {beConvertCurrency} from '../src/utils/currency.js'; import {adjustCpm} from '../src/utils/cpm.js'; -import {getGptSlotInfoForAdUnitCode} from '../libraries/gptUtils/gptUtils.js'; -import {convertCurrency} from '../libraries/currencyUtils/currency.js'; /** * @summary This Module is intended to provide users with the ability to dynamically set and enforce price floors on a per auction basis. @@ -41,22 +40,19 @@ const MODULE_NAME = 'Price Floors'; */ const ajax = ajaxBuilder(10000); -// eslint-disable-next-line symbol-description -const SYN_FIELD = Symbol(); - /** * @summary Allowed fields for rules to have */ -export let allowedFields = [SYN_FIELD, 'gptSlot', 'adUnitCode', 'size', 'domain', 'mediaType']; +export let allowedFields = ['gptSlot', 'adUnitCode', 'size', 'domain', 'mediaType']; /** * @summary This is a flag to indicate if a AJAX call is processing for a floors request - */ +*/ let fetching = false; /** * @summary so we only register for our hooks once - */ +*/ let addedFloorsHook = false; /** @@ -108,7 +104,6 @@ function getAdUnitCode(request, response, {index = auctionManager.index} = {}) { * @summary floor field types with their matching functions to resolve the actual matched value */ export let fieldMatchingFunctions = { - [SYN_FIELD]: () => '*', 'size': (bidRequest, bidResponse) => parseGPTSingleSizeArray(bidResponse.size) || '*', 'mediaType': (bidRequest, bidResponse) => bidResponse.mediaType || 'banner', 'gptSlot': (bidRequest, bidResponse) => getGptSlotFromAdUnit((bidRequest || bidResponse).transactionId) || getGptSlotInfoForAdUnitCode(getAdUnitCode(bidRequest, bidResponse)).gptSlot, @@ -122,7 +117,6 @@ export let fieldMatchingFunctions = { * Returns array of Tuple [exact match, catch all] for each field in rules file */ function enumeratePossibleFieldValues(floorFields, bidObject, responseObject) { - if (!floorFields.length) return []; // generate combination of all exact matches and catch all for each field type return floorFields.reduce((accum, field) => { let exactMatch = fieldMatchingFunctions[field](bidObject, responseObject) || '*'; @@ -138,9 +132,7 @@ function enumeratePossibleFieldValues(floorFields, bidObject, responseObject) { */ export function getFirstMatchingFloor(floorData, bidObject, responseObject = {}) { let fieldValues = enumeratePossibleFieldValues(deepAccess(floorData, 'schema.fields') || [], bidObject, responseObject); - if (!fieldValues.length) { - return {matchingFloor: undefined} - } + if (!fieldValues.length) return { matchingFloor: floorData.default }; // look to see if a request for this context was made already let matchingInput = fieldValues.map(field => field[0]).join('-'); @@ -154,9 +146,9 @@ export function getFirstMatchingFloor(floorData, bidObject, responseObject = {}) let matchingData = { floorMin: floorData.floorMin || 0, - floorRuleValue: floorData.values[matchingRule], + floorRuleValue: isNaN(floorData.values[matchingRule]) ? floorData.default : floorData.values[matchingRule], matchingData: allPossibleMatches[0], // the first possible match is an "exact" so contains all data relevant for anlaytics adapters - matchingRule: matchingRule === floorData.meta?.defaultRule ? undefined : matchingRule + matchingRule }; // use adUnit floorMin as priority! const floorMin = deepAccess(bidObject, 'ortb2Imp.ext.prebid.floors.floorMin'); @@ -308,20 +300,14 @@ function normalizeRulesForAuction(floorData, adUnitCode) { * Only called if no set config or fetch level data has returned */ export function getFloorDataFromAdUnits(adUnits) { - const schemaAu = adUnits.find(au => au.floors?.schema != null); return adUnits.reduce((accum, adUnit) => { - if (adUnit.floors?.schema != null && !deepEqual(adUnit.floors.schema, schemaAu?.floors?.schema)) { - 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) - if (isFloorsDataValid(floors)) { + if (isFloorsDataValid(adUnit.floors)) { // if values already exist we want to not overwrite them if (!accum.values) { - accum = getFloorsDataForAuction(floors, adUnit.code); + accum = getFloorsDataForAuction(adUnit.floors, adUnit.code); accum.location = 'adUnit'; } else { - let newRules = getFloorsDataForAuction(floors, adUnit.code).values; + let newRules = getFloorsDataForAuction(adUnit.floors, adUnit.code).values; // copy over the new rules into our values object Object.assign(accum.values, newRules); } @@ -332,29 +318,13 @@ export function getFloorDataFromAdUnits(adUnits) { }, {}); } -function getNoFloorSignalBidersArray(floorData) { - const { data, enforcement } = floorData - // The data.noFloorSignalBidders higher priority then the enforcment - if (data?.noFloorSignalBidders?.length > 0) { - return data.noFloorSignalBidders - } else if (enforcement?.noFloorSignalBidders?.length > 0) { - return enforcement.noFloorSignalBidders - } - return [] -} - /** * @summary This function takes the adUnits for the auction and update them accordingly as well as returns the rules hashmap for the auction */ export function updateAdUnitsForAuction(adUnits, floorData, auctionId) { - const noFloorSignalBiddersArray = getNoFloorSignalBidersArray(floorData) - adUnits.forEach((adUnit) => { adUnit.bids.forEach(bid => { - // check if the bidder is in the no signal list - const isNoFloorSignaled = noFloorSignalBiddersArray.some(bidderName => bidderName === bid.bidder) - if (floorData.skipped || isNoFloorSignaled) { - isNoFloorSignaled && logInfo(`noFloorSignal to ${bid.bidder}`) + if (floorData.skipped) { delete bid.getFloor; } else { bid.getFloor = getFloor; @@ -362,9 +332,8 @@ export function updateAdUnitsForAuction(adUnits, floorData, auctionId) { // information for bid and analytics adapters bid.auctionId = auctionId; bid.floorData = { - noFloorSignaled: isNoFloorSignaled, skipped: floorData.skipped, - skipRate: deepAccess(floorData, 'data.skipRate') ?? floorData.skipRate, + skipRate: floorData.skipRate, floorMin: floorData.floorMin, modelVersion: deepAccess(floorData, 'data.modelVersion'), modelWeight: deepAccess(floorData, 'data.modelWeight'), @@ -413,7 +382,7 @@ export function createFloorsDataForAuction(adUnits, auctionId) { resolvedFloorsData.skipped = true; } else { // determine the skip rate now - const auctionSkipRate = getParameterByName('pbjs_skipRate') || (deepAccess(resolvedFloorsData, 'data.skipRate') ?? resolvedFloorsData.skipRate); + const auctionSkipRate = getParameterByName('pbjs_skipRate') || resolvedFloorsData.skipRate; const isSkipped = Math.random() * 100 < parseFloat(auctionSkipRate); resolvedFloorsData.skipped = isSkipped; } @@ -448,7 +417,7 @@ function validateSchemaFields(fields) { if (Array.isArray(fields) && fields.length > 0 && fields.every(field => allowedFields.indexOf(field) !== -1)) { return true; } - logError(`${MODULE_NAME}: Fields received do not match allowed fields`); + logError(`${MODULE_NAME}: Fields recieved do not match allowed fields`); return false; } @@ -474,26 +443,7 @@ function validateRules(floorsData, numFields, delimiter) { return Object.keys(floorsData.values).length > 0; } -export function normalizeDefault(model) { - if (isNumber(model.default)) { - let defaultRule = '*'; - const numFields = (model.schema?.fields || []).length; - if (!numFields) { - deepSetValue(model, 'schema.fields', [SYN_FIELD]); - } else { - defaultRule = Array(numFields).fill('*').join(model.schema?.delimiter || '|'); - } - model.values = model.values || {}; - if (model.values[defaultRule] == null) { - model.values[defaultRule] = model.default; - model.meta = {defaultRule}; - } - } - return model; -} - function modelIsValid(model) { - model = normalizeDefault(model); // schema.fields has only allowed attributes if (!validateSchemaFields(deepAccess(model, 'schema.fields'))) { return false; @@ -633,7 +583,7 @@ function handleFetchError(status) { } /** - * This function handles sending and receiving the AJAX call for a floors fetch + * This function handles sending and recieving the AJAX call for a floors fetch * @param {object} floorsConfig the floors config coming from setConfig */ export function generateAndHandleFetch(floorEndpoint) { @@ -680,8 +630,7 @@ export function handleSetFloorsConfig(config) { 'enforceJS', enforceJS => enforceJS !== false, // defaults to true 'enforcePBS', enforcePBS => enforcePBS === true, // defaults to false 'floorDeals', floorDeals => floorDeals === true, // defaults to false - 'bidAdjustment', bidAdjustment => bidAdjustment !== false, // defaults to true, - 'noFloorSignalBidders', noFloorSignalBidders => noFloorSignalBidders || [] + 'bidAdjustment', bidAdjustment => bidAdjustment !== false, // defaults to true ]), 'additionalSchemaFields', additionalSchemaFields => typeof additionalSchemaFields === 'object' && Object.keys(additionalSchemaFields).length > 0 ? addFieldOverrides(additionalSchemaFields) : undefined, 'data', data => (data && parseFloorData(data, 'setConfig')) || undefined @@ -766,7 +715,7 @@ export const addBidResponseHook = timedBidResponseHook('priceFloors', function a let 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); + logWarn(`${MODULE_NAME}: unable to determine a matching price floor for bidResponse`, bid); return fn.call(this, adUnitCode, bid, reject); } @@ -845,8 +794,8 @@ export function setImpExtPrebidFloors(imp, bidRequest, context) { if (floorMinCur == null) { floorMinCur = imp.bidfloorcur } const ortb2ImpFloorCur = imp.ext?.prebid?.floors?.floorMinCur || imp.ext?.prebid?.floorMinCur || floorMinCur; const ortb2ImpFloorMin = imp.ext?.prebid?.floors?.floorMin || imp.ext?.prebid?.floorMin; - const convertedFloorMinValue = convertCurrency(imp.bidfloor, imp.bidfloorcur, floorMinCur); - const convertedOrtb2ImpFloorMinValue = ortb2ImpFloorMin && ortb2ImpFloorCur ? convertCurrency(ortb2ImpFloorMin, ortb2ImpFloorCur, floorMinCur) : false; + const convertedFloorMinValue = beConvertCurrency(imp.bidfloor, imp.bidfloorcur, floorMinCur); + const convertedOrtb2ImpFloorMinValue = ortb2ImpFloorMin && ortb2ImpFloorCur ? beConvertCurrency(ortb2ImpFloorMin, ortb2ImpFloorCur, floorMinCur) : false; const lowestImpFloorMin = convertedOrtb2ImpFloorMinValue && convertedOrtb2ImpFloorMinValue < convertedFloorMinValue ? convertedOrtb2ImpFloorMinValue diff --git a/modules/prismaBidAdapter.js b/modules/prismaBidAdapter.js index 4b4b5677cb3..7c9108f60b1 100644 --- a/modules/prismaBidAdapter.js +++ b/modules/prismaBidAdapter.js @@ -2,16 +2,7 @@ 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'; - -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse - * @typedef {import('../src/adapters/bidderFactory.js').SyncOptions} SyncOptions - * @typedef {import('../src/adapters/bidderFactory.js').UserSync} UserSync - * @typedef {import('../src/adapters/bidderFactory.js').validBidRequests} validBidRequests - */ +import {getANKeywordParam} from '../libraries/appnexusKeywords/anKeywords.js'; const BIDDER_CODE = 'prisma'; const BIDDER_URL = 'https://prisma.nexx360.io/prebid'; @@ -53,20 +44,20 @@ export const spec = { aliases: ['prismadirect'], // short code supportedMediaTypes: [BANNER, VIDEO], /** - * Determines whether or not the given bid request is valid. - * - * @param {BidRequest} bid The bid params to validate. - * @return boolean True if this is a valid bid, and false otherwise. - */ + * Determines whether or not the given bid request is valid. + * + * @param {BidRequest} bid The bid params to validate. + * @return boolean True if this is a valid bid, and false otherwise. + */ isBidRequestValid: function(bid) { return !!(bid.params.account && bid.params.tagId); }, /** - * Make a server request from the list of BidRequests. - * - * @param {validBidRequests[]} - an array of bids - * @return ServerRequest Info describing the request to the server. - */ + * Make a server request from the list of BidRequests. + * + * @param {validBidRequests[]} - an array of bids + * @return ServerRequest Info describing the request to the server. + */ buildRequests: function(validBidRequests, bidderRequest) { const adUnits = []; const test = config.getConfig('debug') ? 1 : 0; @@ -118,11 +109,11 @@ export const spec = { }; }, /** - * Unpack the 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. - */ + * Unpack the 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. + */ interpretResponse: function(serverResponse, bidRequest) { const serverBody = serverResponse.body; const bidResponses = []; @@ -172,12 +163,12 @@ export const spec = { }, /** - * Register the user sync pixels which should be dropped after the auction. - * - * @param {SyncOptions} syncOptions Which user syncs are allowed? - * @param {ServerResponse[]} serverResponses List of server's responses. - * @return {UserSync[]} The user syncs which should be dropped. - */ + * Register the user sync pixels which should be dropped after the auction. + * + * @param {SyncOptions} syncOptions Which user syncs are allowed? + * @param {ServerResponse[]} serverResponses List of server's responses. + * @return {UserSync[]} The user syncs which should be dropped. + */ getUserSyncs: function(syncOptions, serverResponses, gdprConsent, uspConsent) { if (typeof serverResponses === 'object' && serverResponses != null && serverResponses.length > 0 && serverResponses[0].hasOwnProperty('body') && serverResponses[0].body.hasOwnProperty('cookies') && typeof serverResponses[0].body.cookies === 'object') { @@ -188,9 +179,9 @@ export const spec = { }, /** - * Register bidder specific code, which will execute if a bid from this bidder won the auction - * @param {Bid} The bid that won the auction - */ + * Register bidder specific code, which will execute if a bid from this bidder won the auction + * @param {Bid} The bid that won the auction + */ onBidWon: function(bid) { // fires a pixel to confirm a winning bid const params = { type: 'prebid', mediatype: 'banner' }; diff --git a/modules/programmaticaBidAdapter.js b/modules/programmaticaBidAdapter.js deleted file mode 100644 index 7d52e305189..00000000000 --- a/modules/programmaticaBidAdapter.js +++ /dev/null @@ -1,153 +0,0 @@ -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { BANNER, VIDEO } from '../src/mediaTypes.js'; -import { hasPurpose1Consent } from '../src/utils/gpdr.js'; -import { deepAccess, parseSizesInput, isArray } from '../src/utils.js'; - -const BIDDER_CODE = 'programmatica'; -const DEFAULT_ENDPOINT = 'asr.programmatica.com'; -const SYNC_ENDPOINT = 'sync.programmatica.com'; -const ADOMAIN = 'programmatica.com'; -const TIME_TO_LIVE = 360; - -export const spec = { - code: BIDDER_CODE, - - isBidRequestValid: function(bid) { - let valid = bid.params.siteId && bid.params.placementId; - - return !!valid; - }, - - buildRequests: function(validBidRequests, bidderRequest) { - let requests = []; - for (const bid of validBidRequests) { - let endpoint = bid.params.endpoint || DEFAULT_ENDPOINT; - - requests.push({ - method: 'GET', - url: `https://${endpoint}/get`, - data: { - site_id: bid.params.siteId, - placement_id: bid.params.placementId, - prebid: true, - }, - bidRequest: bid, - }); - } - - return requests; - }, - - interpretResponse: function(serverResponse, request) { - if (!serverResponse?.body?.content?.data) { - return []; - } - - const bidResponses = []; - const body = serverResponse.body; - - let mediaType = BANNER; - let ad, vastXml; - let width; - let height; - - let sizes = getSize(body.size); - if (isArray(sizes)) { - [width, height] = sizes; - } - - if (body.type.format != '') { - // banner - ad = body.content.data; - if (body.content.imps?.length) { - for (const imp of body.content.imps) { - ad += ``; - } - } - } else { - // video - vastXml = body.content.data; - mediaType = VIDEO; - - if (!width || !height) { - const pSize = deepAccess(request.bidRequest, 'mediaTypes.video.playerSize'); - const reqSize = getSize(pSize); - if (isArray(reqSize)) { - [width, height] = reqSize; - } - } - } - - const bidResponse = { - requestId: request.bidRequest.bidId, - cpm: body.cpm, - currency: body.currency || 'USD', - width: parseInt(width), - height: parseInt(height), - creativeId: body.id, - netRevenue: true, - ttl: TIME_TO_LIVE, - ad: ad, - mediaType: mediaType, - vastXml: vastXml, - meta: { - advertiserDomains: [ADOMAIN], - } - }; - - if ((mediaType === VIDEO && request.bidRequest.mediaTypes?.video) || (mediaType === BANNER && request.bidRequest.mediaTypes?.banner)) { - bidResponses.push(bidResponse); - } - - return bidResponses; - }, - - getUserSyncs: function(syncOptions, serverResponses, gdprConsent, uspConsent) { - const syncs = [] - - if (!hasPurpose1Consent(gdprConsent)) { - return syncs; - } - - let params = `usp=${uspConsent ?? ''}&consent=${gdprConsent?.consentString ?? ''}`; - if (typeof gdprConsent?.gdprApplies === 'boolean') { - params += `&gdpr=${Number(gdprConsent.gdprApplies)}`; - } - - if (syncOptions.iframeEnabled) { - syncs.push({ - type: 'iframe', - url: `//${SYNC_ENDPOINT}/match/sp.ifr?${params}` - }); - } - - if (syncOptions.pixelEnabled) { - syncs.push({ - type: 'image', - url: `//${SYNC_ENDPOINT}/match/sp?${params}` - }); - } - - return syncs; - }, - - onTimeout: function(timeoutData) {}, - onBidWon: function(bid) {}, - onSetTargeting: function(bid) {}, - onBidderError: function() {}, - supportedMediaTypes: [ BANNER, VIDEO ] -} - -registerBidder(spec); - -function getSize(paramSizes) { - const parsedSizes = parseSizesInput(paramSizes); - const sizes = parsedSizes.map(size => { - const [width, height] = size.split('x'); - const w = parseInt(width, 10); - const h = parseInt(height, 10); - return [w, h]; - }); - - return sizes[0] || null; -} diff --git a/modules/programmaticaBidAdapter.md b/modules/programmaticaBidAdapter.md deleted file mode 100644 index 5982edf143e..00000000000 --- a/modules/programmaticaBidAdapter.md +++ /dev/null @@ -1,46 +0,0 @@ -# Overview - -``` -Module Name: Programmatica Bid Adapter -Module Type: Bidder Adapter -Maintainer: tech@programmatica.com -``` - -# Description -Connects to Programmatica server for bids. -Module supports banner and video mediaType. - -# Test Parameters - -``` - var adUnits = [{ - code: '/test/div', - mediaTypes: { - banner: { - sizes: [[300, 250]] - } - }, - bids: [{ - bidder: 'programmatica', - params: { - siteId: 'cga9l34ipgja79esubrg', - placementId: 'cgim20sipgj0vj1cb510' - } - }] - }, - { - code: '/test/div', - mediaTypes: { - video: { - playerSize: [[640, 360]] - } - }, - bids: [{ - bidder: 'programmatica', - params: { - siteId: 'cga9l34ipgja79esubrg', - placementId: 'cioghpcipgj8r721e9ag' - } - }] - },]; -``` diff --git a/modules/pubCircleBidAdapter.js b/modules/pubCircleBidAdapter.js deleted file mode 100644 index 54224fd0403..00000000000 --- a/modules/pubCircleBidAdapter.js +++ /dev/null @@ -1,231 +0,0 @@ -import { isFn, deepAccess, logMessage, logError } from '../src/utils.js'; -import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; - -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; -import { config } from '../src/config.js'; - -const BIDDER_CODE = 'pubcircle'; -const AD_URL = 'https://ml.pubcircle.ai/pbjs'; -const SYNC_URL = 'https://cs.pubcircle.ai'; - -function isBidResponseValid(bid) { - if (!bid.requestId || !bid.cpm || !bid.creativeId || !bid.ttl || !bid.currency) { - return false; - } - - switch (bid.mediaType) { - case BANNER: - return Boolean(bid.width && bid.height && bid.ad); - case VIDEO: - return Boolean(bid.vastUrl || bid.vastXml); - case NATIVE: - return Boolean(bid.native && bid.native.impressionTrackers && bid.native.impressionTrackers.length); - default: - return false; - } -} - -function getPlacementReqData(bid) { - const { params, bidId, mediaTypes } = bid; - const schain = bid.schain || {}; - const { placementId } = params; - const bidfloor = getBidFloor(bid); - - const placement = { - bidId, - schain, - bidfloor - }; - - placement.placementId = placementId; - placement.type = 'publisher'; - - if (bid.userId) { - getUserId(placement.eids, bid.userId.uid2 && bid.userId.uid2.id, 'uidapi.com'); - getUserId(placement.eids, bid.userId.lotamePanoramaId, 'lotame.com'); - getUserId(placement.eids, bid.userId.idx, 'idx.lat'); - getUserId(placement.eids, bid.userId.idl_env, 'liveramp.com'); - } - - if (mediaTypes && mediaTypes[BANNER]) { - placement.adFormat = BANNER; - placement.sizes = mediaTypes[BANNER].sizes; - } else if (mediaTypes && mediaTypes[VIDEO]) { - placement.adFormat = VIDEO; - placement.playerSize = mediaTypes[VIDEO].playerSize; - placement.minduration = mediaTypes[VIDEO].minduration; - placement.maxduration = mediaTypes[VIDEO].maxduration; - placement.mimes = mediaTypes[VIDEO].mimes; - placement.protocols = mediaTypes[VIDEO].protocols; - placement.startdelay = mediaTypes[VIDEO].startdelay; - placement.placement = mediaTypes[VIDEO].placement; - placement.skip = mediaTypes[VIDEO].skip; - placement.skipafter = mediaTypes[VIDEO].skipafter; - placement.minbitrate = mediaTypes[VIDEO].minbitrate; - placement.maxbitrate = mediaTypes[VIDEO].maxbitrate; - placement.delivery = mediaTypes[VIDEO].delivery; - placement.playbackmethod = mediaTypes[VIDEO].playbackmethod; - placement.api = mediaTypes[VIDEO].api; - placement.linearity = mediaTypes[VIDEO].linearity; - } else if (mediaTypes && mediaTypes[NATIVE]) { - placement.native = mediaTypes[NATIVE]; - placement.adFormat = NATIVE; - } - - return placement; -} - -function getBidFloor(bid) { - if (!isFn(bid.getFloor)) { - return deepAccess(bid, 'params.bidfloor', 0); - } - - try { - const bidFloor = bid.getFloor({ - currency: 'USD', - mediaType: '*', - size: '*', - }); - return bidFloor.floor; - } catch (err) { - logError(err); - return 0; - } -} - -function getUserId(eids, id, source, uidExt) { - if (id) { - var uid = { id }; - if (uidExt) { - uid.ext = uidExt; - } - eids.push({ - source, - uids: [ uid ] - }); - } -} - -export const spec = { - code: BIDDER_CODE, - supportedMediaTypes: [BANNER, VIDEO, NATIVE], - - isBidRequestValid: (bid = {}) => { - const { params, bidId, mediaTypes } = bid; - let valid = Boolean(bidId && params && params.placementId); - - if (mediaTypes && mediaTypes[BANNER]) { - valid = valid && Boolean(mediaTypes[BANNER] && mediaTypes[BANNER].sizes); - } else if (mediaTypes && mediaTypes[VIDEO]) { - valid = valid && Boolean(mediaTypes[VIDEO] && mediaTypes[VIDEO].playerSize); - } else if (mediaTypes && mediaTypes[NATIVE]) { - valid = valid && Boolean(mediaTypes[NATIVE]); - } else { - valid = false; - } - return valid; - }, - - buildRequests: (validBidRequests = [], bidderRequest = {}) => { - // convert Native ORTB definition to old-style prebid native definition - validBidRequests = convertOrtbRequestToProprietaryNative(validBidRequests); - - let deviceWidth = 0; - let deviceHeight = 0; - - let winLocation; - try { - const winTop = window.top; - deviceWidth = winTop.screen.width; - deviceHeight = winTop.screen.height; - winLocation = winTop.location; - } catch (e) { - logMessage(e); - winLocation = window.location; - } - - const refferUrl = bidderRequest.refererInfo && bidderRequest.refererInfo.page; - let refferLocation; - try { - refferLocation = refferUrl && new URL(refferUrl); - } catch (e) { - logMessage(e); - } - // TODO: does the fallback make sense here? - let location = refferLocation || winLocation; - const language = (navigator && navigator.language) ? navigator.language.split('-')[0] : ''; - const host = location.host; - const page = location.pathname; - const secure = location.protocol === 'https:' ? 1 : 0; - const placements = []; - const request = { - deviceWidth, - deviceHeight, - language, - secure, - host, - page, - placements, - coppa: config.getConfig('coppa') === true ? 1 : 0, - ccpa: bidderRequest.uspConsent || undefined, - gdpr: bidderRequest.gdprConsent || undefined, - tmax: bidderRequest.timeout - }; - - const len = validBidRequests.length; - for (let i = 0; i < len; i++) { - const bid = validBidRequests[i]; - placements.push(getPlacementReqData(bid)); - } - - return { - method: 'POST', - url: AD_URL, - data: request - }; - }, - - interpretResponse: (serverResponse) => { - let response = []; - for (let i = 0; i < serverResponse.body.length; i++) { - let resItem = serverResponse.body[i]; - if (isBidResponseValid(resItem)) { - const advertiserDomains = resItem.adomain && resItem.adomain.length ? resItem.adomain : []; - resItem.meta = { ...resItem.meta, advertiserDomains }; - - response.push(resItem); - } - } - return response; - }, - - getUserSyncs: (syncOptions, serverResponses, gdprConsent, uspConsent) => { - let syncType = syncOptions.iframeEnabled ? 'iframe' : 'image'; - let syncUrl = SYNC_URL + `/${syncType}?pbjs=1`; - if (gdprConsent && gdprConsent.consentString) { - if (typeof gdprConsent.gdprApplies === 'boolean') { - syncUrl += `&gdpr=${Number(gdprConsent.gdprApplies)}&gdpr_consent=${gdprConsent.consentString}`; - } else { - syncUrl += `&gdpr=0&gdpr_consent=${gdprConsent.consentString}`; - } - } - if (uspConsent && uspConsent.consentString) { - syncUrl += `&ccpa_consent=${uspConsent.consentString}`; - } - - const coppa = config.getConfig('coppa') ? 1 : 0; - syncUrl += `&coppa=${coppa}`; - - return [{ - type: syncType, - url: syncUrl - }]; - }, - - onBidViewable: function (bid) { - // to do : we need to implement js tag to fire pixel with viewability counter - } -}; - -registerBidder(spec); diff --git a/modules/pubCircleBidAdapter.md b/modules/pubCircleBidAdapter.md deleted file mode 100644 index 4fc114bf20c..00000000000 --- a/modules/pubCircleBidAdapter.md +++ /dev/null @@ -1,79 +0,0 @@ -# Overview - -``` -Module Name: PubCirlce Bidder Adapter -Module Type: PubCirlce Bidder Adapter -Maintainer: system@smartyads.com -``` - -# Description - -Connects to PubCirlce exchange for bids. -PubCirlce bid adapter supports Banner, Video (instream and outstream) and Native. - -# Test Parameters -``` - var adUnits = [ - // Will return static test banner - { - code: 'addunit1', - mediaTypes: { - banner: { - sizes: [ [300, 250], [320, 50] ], - } - }, - bids: [ - { - bidder: 'pubcircle', - params: { - placementId: 'testBanner', - } - } - ] - }, - { - code: 'addunit2', - mediaTypes: { - video: { - playerSize: [ [640, 480] ], - context: 'instream', - minduration: 5, - maxduration: 60, - } - }, - bids: [ - { - bidder: 'pubcircle', - params: { - placementId: 'testVideo', - } - } - ] - }, - { - code: 'addunit3', - mediaTypes: { - native: { - title: { - required: true - }, - body: { - required: true - }, - icon: { - required: true, - size: [64, 64] - } - } - }, - bids: [ - { - bidder: 'pubcircle', - params: { - placementId: 'testNative', - } - } - ] - } - ]; -``` diff --git a/modules/pubProvidedIdSystem.js b/modules/pubProvidedIdSystem.js index d23d992e495..baffd997443 100644 --- a/modules/pubProvidedIdSystem.js +++ b/modules/pubProvidedIdSystem.js @@ -9,11 +9,6 @@ import {submodule} from '../src/hook.js'; import { logInfo, isArray } from '../src/utils.js'; import {VENDORLESS_GVLID} from '../src/consentHandler.js'; -/** - * @typedef {import('../modules/userId/index.js').Submodule} Submodule - * @typedef {import('../modules/userId/index.js').SubmoduleConfig} SubmoduleConfig - */ - const MODULE_NAME = 'pubProvidedId'; /** @type {Submodule} */ @@ -30,7 +25,7 @@ export const pubProvidedIdSubmodule = { * decode the stored id value for passing to bid request * @function * @param {string} value - * @returns {{pubProvidedId: Array}} or undefined if value doesn't exists + * @returns {{pubProvidedId: array}} or undefined if value doesn't exists */ decode(value) { const res = value ? {pubProvidedId: value} : undefined; @@ -42,7 +37,7 @@ export const pubProvidedIdSubmodule = { * performs action to obtain id and return a value. * @function * @param {SubmoduleConfig} [config] - * @returns {{id: Array}} + * @returns {{id: array}} */ getId(config) { const configParams = (config && config.params) || {}; diff --git a/modules/publinkIdSystem.js b/modules/publinkIdSystem.js index e8eb90cd02a..6a5504b5ba0 100644 --- a/modules/publinkIdSystem.js +++ b/modules/publinkIdSystem.js @@ -12,19 +12,10 @@ import { parseUrl, buildUrl, logError } from '../src/utils.js'; import {uspDataHandler} from '../src/adapterManager.js'; import {MODULE_TYPE_UID} from '../src/activities/modules.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 = 'publinkId'; const GVLID = 24; const PUBLINK_COOKIE = '_publink'; 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}); @@ -32,9 +23,10 @@ function isHex(s) { return /^[A-F0-9]+$/i.test(s); } -function publinkIdUrl(params, consentData, storedId) { - let url = parseUrl('https://proc.ad.cpe.dotomi.com' + PUBLINK_REFRESH_PATH); +function publinkIdUrl(params, consentData) { + let url = parseUrl('https://proc.ad.cpe.dotomi.com/cvx/client/sync/publink'); url.search = { + deh: params.e, mpn: 'Prebid.js', mpv: '$prebid.version$', }; @@ -44,21 +36,9 @@ function publinkIdUrl(params, consentData, storedId) { url.search.gdpr_consent = consentData.consentString; } - if (params) { - if (params.e) { - // if there's an email parameter call the request path - url.search.deh = params.e; - url.pathname = PUBLINK_REQUEST_PATH; - } + if (params.site_id) { url.search.sid = params.site_id; } - if (params.site_id) { url.search.sid = params.site_id; } - - if (params.api_key) { url.search.apikey = params.api_key; } - } - - if (storedId) { - url.search.publink = storedId; - } + if (params.api_key) { url.search.apikey = params.api_key; } const usPrivacyString = uspDataHandler.getConsentData(); if (usPrivacyString && typeof usPrivacyString === 'string') { @@ -68,7 +48,7 @@ function publinkIdUrl(params, consentData, storedId) { return buildUrl(url); } -function makeCallback(config = {}, consentData, storedId) { +function makeCallback(config = {}, consentData) { return function(prebidCallback) { const options = {method: 'GET', withCredentials: true}; let handleResponse = function(responseText, xhr) { @@ -79,12 +59,15 @@ function makeCallback(config = {}, consentData, storedId) { } } }; - if ((config.params && config.params.e && isHex(config.params.e)) || storedId) { - ajax(publinkIdUrl(config.params, consentData, storedId), handleResponse, undefined, options); - } else if (config.params.e) { - logError('params.e must be a hex string'); + + if (config.params && config.params.e) { + if (isHex(config.params.e)) { + ajax(publinkIdUrl(config.params, consentData), handleResponse, undefined, options); + } else { + logError('params.e must be a hex string'); + } } - } + }; } function getlocalValue() { @@ -154,13 +137,9 @@ export const publinkIdSubmodule = { if (localValue) { return {id: localValue}; } - return {callback: makeCallback(config, consentData, storedId)}; - }, - eids: { - 'publinkId': { - source: 'epsilon.com', - atype: 3 - }, - }, + if (!storedId) { + return {callback: makeCallback(config, consentData)}; + } + } }; submodule('userId', publinkIdSubmodule); diff --git a/modules/pubmaticAnalyticsAdapter.js b/modules/pubmaticAnalyticsAdapter.js index ad2a06ea86d..acae93c57be 100755 --- a/modules/pubmaticAnalyticsAdapter.js +++ b/modules/pubmaticAnalyticsAdapter.js @@ -1,15 +1,13 @@ -import {_each, isArray, isStr, logError, logWarn, pick, generateUUID} from '../src/utils.js'; +import { _each, pick, logWarn, isStr, isArray, logError, getGptSlotInfoForAdUnitCode } from '../src/utils.js'; import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; import CONSTANTS from '../src/constants.json'; -import {ajax} from '../src/ajax.js'; -import {config} from '../src/config.js'; -import {getGlobal} from '../src/prebidGlobal.js'; -import {getGptSlotInfoForAdUnitCode} from '../libraries/gptUtils/gptUtils.js'; +import { ajax } from '../src/ajax.js'; +import { config } from '../src/config.js'; +import { getGlobal } from '../src/prebidGlobal.js'; /// /////////// CONSTANTS ////////////// const ADAPTER_CODE = 'pubmatic'; -const VENDOR_OPENWRAP = 'openwrap'; const SEND_TIMEOUT = 2000; const END_POINT_HOST = 'https://t.pubmatic.com/'; const END_POINT_BID_LOGGER = END_POINT_HOST + 'wl?'; @@ -24,7 +22,6 @@ const ERROR = 'error'; const REQUEST_ERROR = 'request-error'; const TIMEOUT_ERROR = 'timeout-error'; const EMPTY_STRING = ''; -const OPEN_AUCTION_DEAL_ID = '-1'; const MEDIA_TYPE_BANNER = 'banner'; const CURRENCY_USD = 'USD'; const BID_PRECISION = 2; @@ -94,7 +91,7 @@ function copyRequiredBidDetails(bid) { 'bidderCode', 'adapterCode', 'bidId', - 'status', () => NO_BID, // default a bid to NO_BID until response is received or bid is timed out + 'status', () => NO_BID, // default a bid to NO_BID until response is recieved or bid is timed out 'finalSource as source', 'params', 'floorData', @@ -259,27 +256,12 @@ function isS2SBidder(bidder) { return (s2sBidders.indexOf(bidder) > -1) ? 1 : 0 } -function isOWPubmaticBid(adapterName) { - let s2sConf = config.getConfig('s2sConfig'); - let s2sConfArray = isArray(s2sConf) ? s2sConf : [s2sConf]; - return s2sConfArray.some(conf => { - if (adapterName === ADAPTER_CODE && conf.defaultVendor === VENDOR_OPENWRAP && - conf.bidders.indexOf(ADAPTER_CODE) > -1) { - return true; - } - }) -} - function gatherPartnerBidsForAdUnitForLogger(adUnit, adUnitId, highestBid) { highestBid = (highestBid && highestBid.length > 0) ? highestBid[0] : null; return Object.keys(adUnit.bids).reduce(function(partnerBids, bidId) { adUnit.bids[bidId].forEach(function(bid) { - let adapterName = getAdapterNameForAlias(bid.adapterCode || bid.bidder); - if (isOWPubmaticBid(adapterName) && isS2SBidder(bid.bidder)) { - return; - } partnerBids.push({ - 'pn': adapterName, + 'pn': getAdapterNameForAlias(bid.adapterCode || bid.bidder), 'bc': bid.bidderCode || bid.bidder, 'bidid': bid.bidId || bidId, 'db': bid.bidResponse ? 0 : 1, @@ -288,10 +270,9 @@ function gatherPartnerBidsForAdUnitForLogger(adUnit, adUnitId, highestBid) { 'psz': bid.bidResponse ? (bid.bidResponse.dimensions.width + 'x' + bid.bidResponse.dimensions.height) : '0x0', 'eg': bid.bidResponse ? bid.bidResponse.bidGrossCpmUSD : 0, 'en': bid.bidResponse ? bid.bidResponse.bidPriceUSD : 0, - 'di': bid.bidResponse ? (bid.bidResponse.dealId || OPEN_AUCTION_DEAL_ID) : OPEN_AUCTION_DEAL_ID, + 'di': bid.bidResponse ? (bid.bidResponse.dealId || EMPTY_STRING) : EMPTY_STRING, 'dc': bid.bidResponse ? (bid.bidResponse.dealChannel || EMPTY_STRING) : EMPTY_STRING, - 'l1': bid.bidResponse ? bid.partnerTimeToRespond : 0, - 'ol1': bid.bidResponse ? bid.clientLatencyTimeMs : 0, + 'l1': bid.bidResponse ? bid.clientLatencyTimeMs : 0, 'l2': 0, 'adv': bid.bidResponse ? getAdDomain(bid.bidResponse) || undefined : undefined, 'ss': isS2SBidder(bid.bidder), @@ -302,7 +283,7 @@ function gatherPartnerBidsForAdUnitForLogger(adUnit, adUnitId, highestBid) { 'ocpm': bid.bidResponse ? (bid.bidResponse.originalCpm || 0) : 0, 'ocry': bid.bidResponse ? (bid.bidResponse.originalCurrency || CURRENCY_USD) : CURRENCY_USD, 'piid': bid.bidResponse ? (bid.bidResponse.partnerImpId || EMPTY_STRING) : EMPTY_STRING, - 'frv': bid.bidResponse ? bid.bidResponse.floorData?.floorRuleValue : undefined, + 'frv': (s2sBidders.indexOf(bid.bidder) > -1) ? undefined : (bid.bidResponse ? (bid.bidResponse.floorData ? bid.bidResponse.floorData.floorRuleValue : undefined) : undefined), 'md': bid.bidResponse ? getMetadata(bid.bidResponse.meta) : undefined }); }); @@ -338,24 +319,11 @@ function getTgId() { return 0; } -function getFloorFetchStatus(floorData) { - if (!floorData?.floorRequestData) { - return false; - } - const { location, fetchStatus } = floorData?.floorRequestData; - const isDataValid = location !== CONSTANTS.FLOOR_VALUES.NO_DATA; - const isFetchSuccessful = location === CONSTANTS.FLOOR_VALUES.FETCH && fetchStatus === CONSTANTS.FLOOR_VALUES.SUCCESS; - const isAdUnitOrSetConfig = location === CONSTANTS.FLOOR_VALUES.AD_UNIT || location === CONSTANTS.FLOOR_VALUES.SET_CONFIG; - return isDataValid && (isAdUnitOrSetConfig || isFetchSuccessful); -} - function executeBidsLoggerCall(e, highestCpmBids) { let auctionId = e.auctionId; let referrer = config.getConfig('pageUrl') || cache.auctions[auctionId].referer || ''; let auctionCache = cache.auctions[auctionId]; - let wiid = auctionCache?.wiid || auctionId; - let floorData = auctionCache?.floorData; - let floorFetchStatus = getFloorFetchStatus(auctionCache?.floorData); + let floorData = auctionCache.floorData; let outputObj = { s: [] }; let pixelURL = END_POINT_BID_LOGGER; @@ -369,7 +337,7 @@ function executeBidsLoggerCall(e, highestCpmBids) { pixelURL += 'pubid=' + publisherId; outputObj['pubid'] = '' + publisherId; - outputObj['iid'] = '' + wiid; + outputObj['iid'] = '' + auctionId; outputObj['to'] = '' + auctionCache.timeout; outputObj['purl'] = referrer; outputObj['orig'] = getDomainFromUrl(referrer); @@ -378,9 +346,8 @@ function executeBidsLoggerCall(e, highestCpmBids) { outputObj['pdvid'] = '' + profileVersionId; outputObj['dvc'] = {'plt': getDevicePlatform()}; outputObj['tgid'] = getTgId(); - outputObj['pbv'] = getGlobal()?.version || '-1'; - if (floorData && floorFetchStatus) { + if (floorData) { outputObj['fmv'] = floorData.floorRequestData ? floorData.floorRequestData.modelVersion || undefined : undefined; outputObj['ft'] = floorData.floorResponseData ? (floorData.floorResponseData.enforcements.enforceJS == false ? 0 : 1) : undefined; } @@ -395,25 +362,8 @@ function executeBidsLoggerCall(e, highestCpmBids) { 'mt': getAdUnitAdFormats(origAdUnit), 'sz': getSizesForAdUnit(adUnit, adUnitId), 'ps': gatherPartnerBidsForAdUnitForLogger(adUnit, adUnitId, highestCpmBids.filter(bid => bid.adUnitCode === adUnitId)), - 'fskp': floorData && floorFetchStatus ? (floorData.floorRequestData ? (floorData.floorRequestData.skipped == false ? 0 : 1) : undefined) : undefined, - 'sid': generateUUID() + 'fskp': floorData ? (floorData.floorRequestData ? (floorData.floorRequestData.skipped == false ? 0 : 1) : undefined) : undefined, }; - if (floorData?.floorRequestData) { - const { location, fetchStatus, floorProvider } = floorData?.floorRequestData; - slotObject.ffs = { - [CONSTANTS.FLOOR_VALUES.SUCCESS]: 1, - [CONSTANTS.FLOOR_VALUES.ERROR]: 2, - [CONSTANTS.FLOOR_VALUES.TIMEOUT]: 4, - undefined: 0 - }[fetchStatus]; - slotObject.fsrc = { - [CONSTANTS.FLOOR_VALUES.FETCH]: 2, - [CONSTANTS.FLOOR_VALUES.NO_DATA]: 2, - [CONSTANTS.FLOOR_VALUES.AD_UNIT]: 1, - [CONSTANTS.FLOOR_VALUES.SET_CONFIG]: 1 - }[location]; - slotObject.fp = floorProvider; - } slotsArray.push(slotObject); return slotsArray; }, []); @@ -435,24 +385,16 @@ function executeBidsLoggerCall(e, highestCpmBids) { function executeBidWonLoggerCall(auctionId, adUnitId) { const winningBidId = cache.auctions[auctionId].adUnitCodes[adUnitId].bidWon; const winningBids = cache.auctions[auctionId].adUnitCodes[adUnitId].bids[winningBidId]; - if (!winningBids) { - logWarn(LOG_PRE_FIX + 'Could not find winningBids for : ', auctionId); - return; - } - let winningBid = winningBids[0]; + if (winningBids.length > 1) { winningBid = winningBids.filter(bid => bid.adId === cache.auctions[auctionId].adUnitCodes[adUnitId].bidWonAdId)[0]; } const adapterName = getAdapterNameForAlias(winningBid.adapterCode || winningBid.bidder); - if (isOWPubmaticBid(adapterName) && isS2SBidder(winningBid.bidder)) { - return; - } let origAdUnit = getAdUnit(cache.auctions[auctionId].origAdUnits, adUnitId) || {}; let auctionCache = cache.auctions[auctionId]; let floorData = auctionCache.floorData; - let wiid = cache.auctions[auctionId]?.wiid || auctionId; let referrer = config.getConfig('pageUrl') || cache.auctions[auctionId].referer || ''; let adv = winningBid.bidResponse ? getAdDomain(winningBid.bidResponse) || undefined : undefined; let fskp = floorData ? (floorData.floorRequestData ? (floorData.floorRequestData.skipped == false ? 0 : 1) : undefined) : undefined; @@ -461,7 +403,7 @@ function executeBidWonLoggerCall(auctionId, adUnitId) { pixelURL += 'pubid=' + publisherId; pixelURL += '&purl=' + enc(config.getConfig('pageUrl') || cache.auctions[auctionId].referer || ''); pixelURL += '&tst=' + Math.round((new window.Date()).getTime() / 1000); - pixelURL += '&iid=' + enc(wiid); + pixelURL += '&iid=' + enc(auctionId); pixelURL += '&bidid=' + enc(winningBidId); pixelURL += '&pid=' + enc(profileId); pixelURL += '&pdvid=' + enc(profileVersionId); @@ -473,7 +415,6 @@ function executeBidWonLoggerCall(auctionId, adUnitId) { pixelURL += '&eg=' + enc(winningBid.bidResponse.bidGrossCpmUSD); pixelURL += '&kgpv=' + enc(getValueForKgpv(winningBid, adUnitId)); pixelURL += '&piid=' + enc(winningBid.bidResponse.partnerImpId || EMPTY_STRING); - pixelURL += '&di=' + enc(winningBid?.bidResponse?.dealId || OPEN_AUCTION_DEAL_ID); pixelURL += '&plt=' + enc(getDevicePlatform()); pixelURL += '&psz=' + enc((winningBid?.bidResponse?.dimensions?.width || '0') + 'x' + @@ -502,10 +443,7 @@ function executeBidWonLoggerCall(auctionId, adUnitId) { function auctionInitHandler(args) { s2sBidders = (function() { let s2sConf = config.getConfig('s2sConfig'); - let s2sBidders = []; - (s2sConf || []) && - isArray(s2sConf) ? s2sConf.map(conf => s2sBidders.push(...conf.bidders)) : s2sBidders.push(...s2sConf.bidders); - return s2sBidders || []; + return (s2sConf && isArray(s2sConf.bidders)) ? s2sConf.bidders : []; }()); let cacheEntry = pick(args, [ 'timestamp', @@ -528,9 +466,6 @@ function bidRequestedHandler(args) { dimensions: bid.sizes }; } - if (bid.bidder === 'pubmatic' && !!bid?.params?.wiid) { - cache.auctions[args.auctionId].wiid = bid.params.wiid; - } cache.auctions[args.auctionId].adUnitCodes[bid.adUnitCode].bids[bid.bidId] = [copyRequiredBidDetails(bid)]; if (bid.floorData) { cache.auctions[args.auctionId].floorData['floorRequestData'] = bid.floorData; @@ -539,10 +474,6 @@ function bidRequestedHandler(args) { } function bidResponseHandler(args) { - if (!args.requestId) { - logWarn(LOG_PRE_FIX + 'Got null requestId in bidResponseHandler'); - return; - } let bid = cache.auctions[args.auctionId].adUnitCodes[args.adUnitCode].bids[args.requestId][0]; if (!bid) { logError(LOG_PRE_FIX + 'Could not find associated bid request for bid response with requestId: ', args.requestId); @@ -561,24 +492,10 @@ function bidResponseHandler(args) { bid.adId = args.adId; bid.source = formatSource(bid.source || args.source); setBidStatus(bid, args); - const latency = args?.timeToRespond || Date.now() - cache.auctions[args.auctionId].timestamp; - const auctionTime = cache.auctions[args.auctionId].timeout; - // Check if latency is greater than auctiontime+150, then log auctiontime+150 to avoid large numbers - bid.partnerTimeToRespond = latency > (auctionTime + 150) ? (auctionTime + 150) : latency; bid.clientLatencyTimeMs = Date.now() - cache.auctions[args.auctionId].timestamp; bid.bidResponse = parseBidResponse(args); } -function bidRejectedHandler(args) { - // If bid is rejected due to floors value did not met - // make cpm as 0, status as bidRejected and forward the bid for logging - if (args.rejectionReason === CONSTANTS.REJECTION_REASON.FLOOR_NOT_MET) { - args.cpm = 0; - args.status = CONSTANTS.BID_STATUS.BID_REJECTED; - bidResponseHandler(args); - } -} - function bidderDoneHandler(args) { cache.auctions[args.auctionId].bidderDonePendingCount--; args.bids.forEach(bid => { @@ -607,7 +524,7 @@ function auctionEndHandler(args) { let highestCpmBids = getGlobal().getHighestCpmBids() || []; setTimeout(() => { executeBidsLoggerCall.call(this, args, highestCpmBids); - }, (cache.auctions[args.auctionId]?.bidderDonePendingCount === 0 ? 500 : SEND_TIMEOUT)); + }, (cache.auctions[args.auctionId].bidderDonePendingCount === 0 ? 500 : SEND_TIMEOUT)); } function bidTimeoutHandler(args) { @@ -677,9 +594,6 @@ let pubmaticAdapter = Object.assign({}, baseAdapter, { case CONSTANTS.EVENTS.BID_RESPONSE: bidResponseHandler(args); break; - case CONSTANTS.EVENTS.BID_REJECTED: - bidRejectedHandler(args) - break; case CONSTANTS.EVENTS.BIDDER_DONE: bidderDoneHandler(args); break; diff --git a/modules/pubmaticBidAdapter.js b/modules/pubmaticBidAdapter.js index d25627a7b90..eb2ad8ccb03 100644 --- a/modules/pubmaticBidAdapter.js +++ b/modules/pubmaticBidAdapter.js @@ -1,17 +1,10 @@ -import { getBidRequest, logWarn, isBoolean, isStr, isArray, inIframe, mergeDeep, deepAccess, isNumber, deepSetValue, logInfo, logError, deepClone, uniques, isPlainObject, isInteger, generateUUID } from '../src/utils.js'; +import { getBidRequest, logWarn, isBoolean, isStr, isArray, inIframe, mergeDeep, deepAccess, isNumber, deepSetValue, logInfo, logError, deepClone, convertTypes, uniques, isPlainObject, isInteger } from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER, VIDEO, NATIVE, ADPOD } from '../src/mediaTypes.js'; import { config } from '../src/config.js'; import { Renderer } from '../src/Renderer.js'; import { bidderSettings } from '../src/bidderSettings.js'; import CONSTANTS from '../src/constants.json'; -import {convertTypes} from '../libraries/transformParamsUtils/convertTypes.js'; - -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - * @typedef {import('../src/adapters/bidderFactory.js').validBidRequests} validBidRequests - */ const BIDDER_CODE = 'pubmatic'; const LOG_WARN_PREFIX = 'PubMatic: '; @@ -740,9 +733,9 @@ function _addImpressionFPD(imp, bid) { const ortb2 = {...deepAccess(bid, 'ortb2Imp.ext.data')}; Object.keys(ortb2).forEach(prop => { /** - * Prebid AdSlot - * @type {(string|undefined)} - */ + * Prebid AdSlot + * @type {(string|undefined)} + */ if (prop === 'pbadslot') { if (typeof ortb2[prop] === 'string' && ortb2[prop]) deepSetValue(imp, 'ext.data.pbadslot', ortb2[prop]); } else if (prop === 'adserver') { @@ -764,9 +757,6 @@ function _addImpressionFPD(imp, bid) { deepSetValue(imp, `ext.data.${prop}`, ortb2[prop]); } }); - - const gpid = deepAccess(bid, 'ortb2Imp.ext.gpid'); - gpid && deepSetValue(imp, `ext.gpid`, gpid); } function _addFloorFromFloorModule(impObj, bid) { @@ -1017,11 +1007,11 @@ export const spec = { gvlid: 76, supportedMediaTypes: [BANNER, VIDEO, NATIVE], /** - * Determines whether or not the given bid request is valid. Valid bid request must have placementId and hbid - * - * @param {BidRequest} bid The bid params to validate. - * @return boolean True if this is a valid bid, and false otherwise. - */ + * Determines whether or not the given bid request is valid. Valid bid request must have placementId and hbid + * + * @param {BidRequest} bid The bid params to validate. + * @return boolean True if this is a valid bid, and false otherwise. + */ isBidRequestValid: bid => { if (bid && bid.params) { if (!isStr(bid.params.publisherId)) { @@ -1087,10 +1077,8 @@ export const spec = { var bid; var blockedIabCategories = []; var allowedIabCategories = []; - var wiid = generateUUID(); validBidRequests.forEach(originalBid => { - originalBid.params.wiid = originalBid.params.wiid || bidderRequest.auctionId || wiid; bid = deepClone(originalBid); bid.params.adSlot = bid.params.adSlot || ''; _parseAdSlot(bid); @@ -1162,7 +1150,10 @@ export const spec = { payload.user.gender = (conf.gender ? conf.gender.trim() : UNDEFINED); payload.user.geo = {}; // TODO: fix lat and long to only come from request object, not params + payload.user.geo.lat = _parseSlotParam('lat', 0); + payload.user.geo.lon = _parseSlotParam('lon', 0); payload.user.yob = _parseSlotParam('yob', conf.yob); + payload.device.geo = payload.user.geo; payload.site.page = conf.kadpageurl.trim() || payload.site.page.trim(); payload.site.domain = _getDomainFromURL(payload.site.page); @@ -1203,15 +1194,6 @@ export const spec = { deepSetValue(payload, 'regs.ext.us_privacy', bidderRequest.uspConsent); } - // Attaching GPP Consent Params - if (bidderRequest?.gppConsent?.gppString) { - deepSetValue(payload, 'regs.gpp', bidderRequest.gppConsent.gppString); - deepSetValue(payload, 'regs.gpp_sid', bidderRequest.gppConsent.applicableSections); - } else if (bidderRequest?.ortb2?.regs?.gpp) { - deepSetValue(payload, 'regs.gpp', bidderRequest.ortb2.regs.gpp); - deepSetValue(payload, 'regs.gpp_sid', bidderRequest.ortb2.regs.gpp_sid); - } - // coppa compliance if (config.getConfig('coppa') === true) { deepSetValue(payload, 'regs.coppa', 1); @@ -1221,39 +1203,22 @@ export const spec = { // First Party Data const commonFpd = (bidderRequest && bidderRequest.ortb2) || {}; - const { user, device, site, bcat, badv } = commonFpd; - if (site) { + if (commonFpd.site) { const { page, domain, ref } = payload.site; - mergeDeep(payload, {site: site}); + mergeDeep(payload, {site: commonFpd.site}); payload.site.page = page; payload.site.domain = domain; payload.site.ref = ref; } - if (user) { - mergeDeep(payload, {user: user}); - } - if (badv) { - mergeDeep(payload, {badv: badv}); + if (commonFpd.user) { + mergeDeep(payload, {user: commonFpd.user}); } - if (bcat) { - blockedIabCategories = blockedIabCategories.concat(bcat); + if (commonFpd.bcat) { + blockedIabCategories = blockedIabCategories.concat(commonFpd.bcat); } // check if fpd ortb2 contains device property with sua object - if (device?.sua) { - payload.device.sua = device?.sua; - } - - if (device?.ext?.cdep) { - deepSetValue(payload, 'device.ext.cdep', device.ext.cdep); - } - - if (user?.geo && device?.geo) { - payload.device.geo = { ...payload.device.geo, ...device.geo }; - payload.user.geo = { ...payload.user.geo, ...user.geo }; - } else { - if (user?.geo || device?.geo) { - payload.user.geo = payload.device.geo = (user?.geo ? { ...payload.user.geo, ...user.geo } : { ...payload.user.geo, ...device.geo }); - } + if (commonFpd.device?.sua) { + payload.device.sua = commonFpd.device?.sua; } if (commonFpd.ext?.prebid?.bidderparams?.[bidderRequest.bidderCode]?.acat) { @@ -1378,21 +1343,6 @@ export const spec = { }); }); } - let fledgeAuctionConfigs = deepAccess(response.body, 'ext.fledge_auction_configs'); - if (fledgeAuctionConfigs) { - fledgeAuctionConfigs = Object.entries(fledgeAuctionConfigs).map(([bidId, cfg]) => { - return { - bidId, - config: Object.assign({ - auctionSignals: {}, - }, cfg) - } - }); - return { - bids: bidResponses, - fledgeAuctionConfigs, - } - } } catch (error) { logError(error); } @@ -1402,7 +1352,7 @@ export const spec = { /** * Register User Sync. */ - getUserSyncs: (syncOptions, responses, gdprConsent, uspConsent, gppConsent) => { + getUserSyncs: (syncOptions, responses, gdprConsent, uspConsent) => { let syncurl = '' + publisherId; // Attaching GDPR Consent Params in UserSync url @@ -1416,12 +1366,6 @@ export const spec = { syncurl += '&us_privacy=' + encodeURIComponent(uspConsent); } - // GPP Consent - if (gppConsent?.gppString && gppConsent?.applicableSections?.length) { - syncurl += '&gpp=' + encodeURIComponent(gppConsent.gppString); - syncurl += '&gpp_sid=' + encodeURIComponent(gppConsent?.applicableSections?.join(',')); - } - // coppa compliance if (config.getConfig('coppa') === true) { syncurl += '&coppa=1'; diff --git a/modules/pubwiseBidAdapter.js b/modules/pubwiseBidAdapter.js index 507df4a2bb0..6a5d866c76d 100644 --- a/modules/pubwiseBidAdapter.js +++ b/modules/pubwiseBidAdapter.js @@ -6,13 +6,6 @@ import { config } from '../src/config.js'; import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; import { OUTSTREAM, INSTREAM } from '../src/video.js'; -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse - * @typedef {import('../src/adapters/bidderFactory.js').validBidRequests} validBidRequests - */ - const VERSION = '0.3.0'; const GVLID = 842; const NET_REVENUE = true; diff --git a/modules/pubxBidAdapter.js b/modules/pubxBidAdapter.js index 60e5be2a321..ee28d549475 100644 --- a/modules/pubxBidAdapter.js +++ b/modules/pubxBidAdapter.js @@ -55,8 +55,8 @@ export const spec = { /** * Determine which user syncs should occur * @param {object} syncOptions - * @param {Array} serverResponses - * @returns {Array} User sync pixels + * @param {array} serverResponses + * @returns {array} User sync pixels */ getUserSyncs: function (syncOptions, serverResponses) { const kwTag = document.getElementsByName('keywords'); diff --git a/modules/pubxaiAnalyticsAdapter.js b/modules/pubxaiAnalyticsAdapter.js index e97e5505768..19a3c236942 100644 --- a/modules/pubxaiAnalyticsAdapter.js +++ b/modules/pubxaiAnalyticsAdapter.js @@ -1,10 +1,9 @@ -import { deepAccess, parseSizesInput, getWindowLocation, buildUrl } from '../src/utils.js'; +import { deepAccess, getGptSlotInfoForAdUnitCode, parseSizesInput, getWindowLocation, buildUrl } from '../src/utils.js'; import { ajax } from '../src/ajax.js'; import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; import CONSTANTS from '../src/constants.json'; import {getGlobal} from '../src/prebidGlobal.js'; -import {getGptSlotInfoForAdUnitCode} from '../libraries/gptUtils/gptUtils.js'; const emptyUrl = ''; const analyticsType = 'endpoint'; diff --git a/modules/pulsepointBidAdapter.js b/modules/pulsepointBidAdapter.js index 516254b358b..015e80d5692 100644 --- a/modules/pulsepointBidAdapter.js +++ b/modules/pulsepointBidAdapter.js @@ -1,10 +1,21 @@ -import { ortbConverter } from '../libraries/ortbConverter/converter.js'; -import {isArray} from '../src/utils.js'; +import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; +/* eslint dot-notation:0, quote-props:0 */ +import {convertTypes, deepAccess, isArray, isFn, logError} from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {convertTypes} from '../libraries/transformParamsUtils/convertTypes.js'; +import {Renderer} from '../src/Renderer.js'; +const NATIVE_DEFAULTS = { + TITLE_LEN: 100, + DESCR_LEN: 200, + SPONSORED_BY_LEN: 50, + IMG_MIN: 150, + ICON_MIN: 50, +}; + +const DEFAULT_BID_TTL = 20; const DEFAULT_CURRENCY = 'USD'; -const KNOWN_PARAMS = ['cp', 'ct', 'cf', 'battr', 'deals']; +const DEFAULT_NET_REVENUE = true; +const KNOWN_PARAMS = ['cp', 'ct', 'cf', 'video', 'battr', 'bcat', 'badv', 'bidfloor']; const DEFAULT_TMAX = 500; /** @@ -30,21 +41,33 @@ export const spec = { ), buildRequests: (bidRequests, bidderRequest) => { - const data = converter.toORTB({bidRequests, bidderRequest}); + // convert Native ORTB definition to old-style prebid native definition + bidRequests = convertOrtbRequestToProprietaryNative(bidRequests); + + const request = { + id: bidRequests[0].bidderRequestId, + imp: bidRequests.map(slot => impression(slot)), + site: site(bidRequests, bidderRequest), + app: app(bidRequests), + device: device(), + bcat: deepAccess(bidderRequest.ortb2Imp, 'bcat') || bidRequests[0].params.bcat, + badv: bidRequests[0].params.badv, + user: user(bidRequests[0], bidderRequest), + regs: regs(bidderRequest), + source: source(bidRequests[0].schain), + tmax: bidderRequest.timeout || DEFAULT_TMAX, + }; return { method: 'POST', url: 'https://bid.contextweb.com/header/ortb?src=prebid', - data, + data: request, bidderRequest }; }, - interpretResponse: (response, request) => { - if (response.body) { - return converter.fromORTB({response: response.body, request: request.data}).bids; - } - return []; - }, + interpretResponse: (response, request) => ( + bidResponseAvailable(request, response) + ), getUserSyncs: syncOptions => { if (syncOptions.iframeEnabled) { @@ -59,7 +82,7 @@ export const spec = { }]; } }, - transformBidParams: function(params) { + transformBidParams: function(params, isOpenRtb) { return convertTypes({ 'cf': 'string', 'cp': 'number', @@ -68,61 +91,124 @@ export const spec = { } }; -const converter = ortbConverter({ - context: { - netRevenue: true, - ttl: 300, - currency: 'USD' - }, - - imp(buildImp, bidRequest, context) { - const imp = buildImp(bidRequest, context); - // tagid - imp.tagid = bidRequest.params.ct.toString(); - // unknown params - const unknownParams = slotUnknownParams(bidRequest); - if (imp.ext || unknownParams) { - imp.ext = Object.assign({}, imp.ext, unknownParams); - } - // battr - if (bidRequest.params.battr) { - ['banner', 'video', 'audio', 'native'].forEach(k => { - if (imp[k]) { - imp[k].battr = bidRequest.params.battr; - } - }); - } - // deals - if (bidRequest.params.deals && isArray(bidRequest.params.deals)) { - imp.pmp = { - private_auction: 0, - deals: bidRequest.params.deals +/** + * Callback for bids, after the call to PulsePoint completes. + */ +function bidResponseAvailable(request, response) { + const idToImpMap = {}; + const idToBidMap = {}; + const idToSlotConfig = {}; + const bidResponse = response.body; + // extract the request bids and the response bids, keyed by impr-id + const ortbRequest = request.data; + ortbRequest.imp.forEach(imp => { + idToImpMap[imp.id] = imp; + }); + if (bidResponse) { + bidResponse.seatbid.forEach(seatBid => seatBid.bid.forEach(bid => { + idToBidMap[bid.impid] = bid; + })); + } + if (request.bidderRequest && request.bidderRequest.bids) { + request.bidderRequest.bids.forEach(bid => { + idToSlotConfig[bid.bidId] = bid; + }); + } + const bids = []; + Object.keys(idToImpMap).forEach(id => { + if (idToBidMap[id]) { + const bid = { + requestId: id, + cpm: idToBidMap[id].price, + creative_id: idToBidMap[id].crid, + creativeId: idToBidMap[id].crid, + adId: id, + ttl: idToBidMap[id].exp || DEFAULT_BID_TTL, + netRevenue: DEFAULT_NET_REVENUE, + currency: bidResponse.cur || DEFAULT_CURRENCY, + meta: { advertiserDomains: idToBidMap[id].adomain || [] } }; + if (idToImpMap[id].video) { + // for outstream, a renderer is specified + if (idToSlotConfig[id] && deepAccess(idToSlotConfig[id], 'mediaTypes.video.context') === 'outstream') { + bid.renderer = outstreamRenderer(deepAccess(idToSlotConfig[id], 'renderer.options'), deepAccess(idToBidMap[id], 'ext.outstream')); + } + bid.vastXml = idToBidMap[id].adm; + bid.mediaType = 'video'; + bid.width = idToBidMap[id].w; + bid.height = idToBidMap[id].h; + } else if (idToImpMap[id].banner) { + bid.ad = idToBidMap[id].adm; + bid.width = idToBidMap[id].w || idToImpMap[id].banner.w; + bid.height = idToBidMap[id].h || idToImpMap[id].banner.h; + } else if (idToImpMap[id]['native']) { + bid['native'] = nativeResponse(idToImpMap[id], idToBidMap[id]); + bid.mediaType = 'native'; + } + bids.push(bid); } - return imp; - }, + }); + return bids; +} - request(buildRequest, imps, bidderRequest, context) { - const request = buildRequest(imps, bidderRequest, context); - // publisher id - const siteOrApp = request.site || request.app; - const pubId = context.bidRequests && context.bidRequests.length > 0 ? context.bidRequests[0].params.cp : '0'; - if (siteOrApp) { - siteOrApp.publisher = Object.assign({}, siteOrApp.publisher, { - id: pubId.toString() - }); - } - // tmax - request.tmax = request.tmax || DEFAULT_TMAX; - return request; - }, +/** + * Produces an OpenRTBImpression from a slot config. + */ +function impression(slot) { + var firstPartyData = slot.ortb2Imp?.ext || {}; + var ext = Object.assign({}, firstPartyData, slotUnknownParams(slot)); + return { + id: slot.bidId, + banner: banner(slot), + 'native': nativeImpression(slot), + tagid: slot.params.ct.toString(), + video: video(slot), + bidfloor: bidFloor(slot), + ext: Object.keys(ext).length > 0 ? ext : null, + }; +} - bidResponse(buildBidResponse, bid, context) { - const bidResponse = buildBidResponse(bid, context); - bidResponse.cur = bidResponse.cur || DEFAULT_CURRENCY; - return bidResponse; - }, -}); +/** + * Produces an OpenRTB Banner object for the slot given. + */ +function banner(slot) { + const sizes = parseSizes(slot); + const size = adSize(slot, sizes); + return (slot.mediaTypes && slot.mediaTypes.banner) ? { + w: size[0], + h: size[1], + battr: slot.params.battr, + format: sizes + } : null; +} + +/** + * Produce openrtb format objects based on the sizes configured for the slot. + */ +function parseSizes(slot) { + const sizes = deepAccess(slot, 'mediaTypes.banner.sizes'); + if (sizes && isArray(sizes)) { + return sizes.filter(sz => isArray(sz) && sz.length === 2).map(sz => ({ + w: sz[0], + h: sz[1] + })); + } + return null; +} + +/** + * Produces an OpenRTB Video object for the slot given + */ +function video(slot) { + if (slot.params.video) { + return Object.assign({}, + slot.params.video, // previously supported as bidder param + slot.mediaTypes && slot.mediaTypes.video ? slot.mediaTypes.video : {}, // params on mediaTypes.video + {battr: slot.params.battr} + ); + } + return null; +} /** * Unknown params are captured and sent on ext @@ -139,4 +225,278 @@ function slotUnknownParams(slot) { return Object.keys(ext).length > 0 ? { prebid: ext } : null; } +/** + * Sets up the renderer on the bid, for outstream bid responses. + */ +function outstreamRenderer(rendererOptions, outstreamExtOptions) { + const renderer = Renderer.install({ + url: outstreamExtOptions.rendererUrl, + config: { + defaultOptions: outstreamExtOptions.config, + rendererOptions, + type: outstreamExtOptions.type + }, + loaded: false, + }); + renderer.setRender((bid) => { + bid.renderer.push(() => { + const config = bid.renderer.getConfig(); + new window.PulsePointOutstreamRenderer().render({ + adUnitCode: bid.adUnitCode, + vastXml: bid.vastXml, + type: config.type, + defaultOptions: config.defaultOptions, + rendererOptions + }); + }); + }); + return renderer; +} + +/** + * Produces an OpenRTB Native object for the slot given. + */ +function nativeImpression(slot) { + if (slot.nativeParams) { + const assets = []; + addAsset(assets, titleAsset(assets.length + 1, slot.nativeParams.title, NATIVE_DEFAULTS.TITLE_LEN)); + addAsset(assets, dataAsset(assets.length + 1, slot.nativeParams.body, 2, NATIVE_DEFAULTS.DESCR_LEN)); + addAsset(assets, dataAsset(assets.length + 1, slot.nativeParams.sponsoredBy, 1, NATIVE_DEFAULTS.SPONSORED_BY_LEN)); + addAsset(assets, imageAsset(assets.length + 1, slot.nativeParams.icon, 1, NATIVE_DEFAULTS.ICON_MIN, NATIVE_DEFAULTS.ICON_MIN)); + addAsset(assets, imageAsset(assets.length + 1, slot.nativeParams.image, 3, NATIVE_DEFAULTS.IMG_MIN, NATIVE_DEFAULTS.IMG_MIN)); + return { + request: JSON.stringify({ assets }), + ver: '1.1', + battr: slot.params.battr, + }; + } + return null; +} + +/** + * Helper method to add an asset to the assets list. + */ +function addAsset(assets, asset) { + if (asset) { + assets.push(asset); + } +} + +/** + * Produces a Native Title asset for the configuration given. + */ +function titleAsset(id, params, defaultLen) { + if (params) { + return { + id, + required: params.required ? 1 : 0, + title: { + len: params.len || defaultLen, + }, + }; + } + return null; +} + +/** + * Produces a Native Image asset for the configuration given. + */ +function imageAsset(id, params, type, defaultMinWidth, defaultMinHeight) { + return params ? { + id, + required: params.required ? 1 : 0, + img: { + type, + wmin: params.wmin || defaultMinWidth, + hmin: params.hmin || defaultMinHeight, + } + } : null; +} + +/** + * Produces a Native Data asset for the configuration given. + */ +function dataAsset(id, params, type, defaultLen) { + return params ? { + id, + required: params.required ? 1 : 0, + data: { + type, + len: params.len || defaultLen, + } + } : null; +} + +/** + * Produces an OpenRTB site object. + */ +function site(bidRequests, bidderRequest) { + const pubId = bidRequests && bidRequests.length > 0 ? bidRequests[0].params.cp : '0'; + const appParams = bidRequests[0].params.app; + if (!appParams) { + // use the first party data if available, and override only publisher/ref/page properties + var firstPartyData = bidderRequest?.ortb2?.site || {}; + return Object.assign({}, firstPartyData, { + publisher: { + id: pubId.toString(), + }, + // TODO: does the fallback make sense here? + ref: bidderRequest?.refererInfo?.ref || window.document.referrer, + page: bidderRequest?.refererInfo?.page || '' + }); + } + return null; +} + +/** + * Produces an OpenRTB App object. + */ +function app(bidderRequest) { + const pubId = bidderRequest && bidderRequest.length > 0 ? bidderRequest[0].params.cp : '0'; + const appParams = bidderRequest[0].params.app; + if (appParams) { + return { + publisher: { + id: pubId.toString(), + }, + bundle: appParams.bundle, + storeurl: appParams.storeUrl, + domain: appParams.domain, + } + } + return null; +} + +/** + * Produces an OpenRTB Device object. + */ +function device() { + return { + ua: navigator.userAgent, + language: (navigator.language || navigator.browserLanguage || navigator.userLanguage || navigator.systemLanguage), + }; +} + +/** + * Safely parses the input given. Returns null on + * parsing failure. + */ +function parse(rawResponse) { + try { + if (rawResponse) { + return JSON.parse(rawResponse); + } + } catch (ex) { + logError('pulsepointLite.safeParse', 'ERROR', ex); + } + return null; +} + +/** + * Determines the AdSize for the slot. + */ +function adSize(slot, sizes) { + if (slot.params.cf) { + const size = slot.params.cf.toUpperCase().split('X'); + const width = parseInt(slot.params.cw || size[0], 10); + const height = parseInt(slot.params.ch || size[1], 10); + return [width, height]; + } else if (sizes && sizes.length > 0) { + return [sizes[0].w, sizes[0].h]; + } + return [1, 1]; +} + +/** + * Handles the user level attributes and produces + * an openrtb User object. + */ +function user(bidRequest, bidderRequest) { + var user = bidderRequest?.ortb2?.user || { ext: {} }; + var ext = user.ext; + if (bidderRequest) { + if (bidderRequest.gdprConsent) { + ext.consent = bidderRequest.gdprConsent.consentString; + } + } + if (bidRequest) { + let eids = bidRequest.userIdAsEids; + if (eids) { + ext.eids = eids; + } + } + return user; +} + +/** + * Produces the regulations ortb object + */ +function regs(bidderRequest) { + if (bidderRequest.gdprConsent || bidderRequest.uspConsent) { + var ext = {}; + // GDPR applies attribute (actual consent value is in user object) + if (bidderRequest.gdprConsent) { + ext.gdpr = bidderRequest.gdprConsent.gdprApplies ? 1 : 0; + } + // CCPA + if (bidderRequest.uspConsent) { + ext.us_privacy = bidderRequest.uspConsent; + } + return { ext }; + } + return null; +} + +/** + * Creates source object with supply chain + */ +function source(schain) { + if (schain) { + return { + ext: { schain } + }; + } + return null; +} + +/** + * Parses the native response from the Bid given. + */ +function nativeResponse(imp, bid) { + if (imp['native']) { + const nativeAd = parse(bid.adm); + const keys = {}; + if (nativeAd && nativeAd['native'] && nativeAd['native'].assets) { + nativeAd['native'].assets.forEach(asset => { + keys.title = asset.title ? asset.title.text : keys.title; + keys.body = asset.data && asset.data.type === 2 ? asset.data.value : keys.body; + keys.sponsoredBy = asset.data && asset.data.type === 1 ? asset.data.value : keys.sponsoredBy; + keys.image = asset.img && asset.img.type === 3 ? asset.img.url : keys.image; + keys.icon = asset.img && asset.img.type === 1 ? asset.img.url : keys.icon; + }); + if (nativeAd['native'].link) { + keys.clickUrl = encodeURIComponent(nativeAd['native'].link.url); + } + keys.impressionTrackers = nativeAd['native'].imptrackers; + return keys; + } + } + return null; +} + +function bidFloor(slot) { + let floor = slot.params.bidfloor; + if (isFn(slot.getFloor)) { + const floorData = slot.getFloor({ + mediaType: slot.mediaTypes.banner ? 'banner' : slot.mediaTypes.video ? 'video' : 'Native', + size: '*', + currency: DEFAULT_CURRENCY, + }); + if (floorData && floorData.floor) { + floor = floorData.floor; + } + } + return floor; +} + registerBidder(spec); diff --git a/modules/pulsepointBidAdapter.md b/modules/pulsepointBidAdapter.md index 899c277f92f..7f4b7e6b611 100644 --- a/modules/pulsepointBidAdapter.md +++ b/modules/pulsepointBidAdapter.md @@ -2,7 +2,7 @@ **Module Name**: PulsePoint Bidder Adapter **Module Type**: Bidder Adapter -**Maintainer**: ExchangeTeam@pulsepoint.com +**Maintainer**: ExchangeTeam@pulsepoint.com # Description @@ -18,49 +18,55 @@ Please use ```pulsepoint``` as the bidder code. sizes: [[300, 250]], bids: [{ bidder: 'pulsepoint', - params: { + params: { + cf: '300X250', cp: 512379, ct: 486653 } }] },{ - code: 'native-1-slot', - mediaTypes: { - native: { - ortb: { - assets: [{ - id: 1, - required: 1, - img: { - type: 3, - w: 150, - h: 50, - } - }, - { - id: 2, - required: 1, - title: { - len: 80 - } - }, - { - id: 3, - required: 1, - data: { - type: 1 - } - }] - } - } - }, - bids: [{ - bidder: 'pulsepoint', - params: { - cp: 512379, - ct: 694973 - } - }] + code: 'native-ad-div', + sizes: [[1, 1]], + nativeParams: { + title: { required: true, len: 75 }, + image: { required: true }, + body: { len: 200 }, + sponsoredBy: { len: 20 } + }, + bids: [{ + bidder: 'pulsepoint', + params: { + cp: 512379, + ct: 505642 + } + }] + },{ + code: 'outstream-div', + mediaTypes: { + video: { + playerSize: [640, 480], + context: 'outstream', + h: 300, + w: 400, + minduration: 1, + maxduration: 210, + linearity: 1, + mimes: ["video/mp4", "video/ogg", "video/webm"], + pos: 3 + } + }, + bids: [{ + bidder: 'pulsepoint', + params: { + cp: 512379, + ct: 505642 + } + }], + renderer: { + options: { + text: "PulsePoint Outstream" + } + } },{ code: 'instream', mediaTypes: { diff --git a/modules/pxyzBidAdapter.js b/modules/pxyzBidAdapter.js index 8b9dbea339b..1ab432496a3 100644 --- a/modules/pxyzBidAdapter.js +++ b/modules/pxyzBidAdapter.js @@ -2,11 +2,6 @@ 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 - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - */ - const BIDDER_CODE = 'pxyz'; const URL = 'https://ads.playground.xyz/host-config/prebid?v=2'; const DEFAULT_CURRENCY = 'USD'; diff --git a/modules/qortexRtdProvider.js b/modules/qortexRtdProvider.js deleted file mode 100644 index 7aa30334756..00000000000 --- a/modules/qortexRtdProvider.js +++ /dev/null @@ -1,165 +0,0 @@ -import { submodule } from '../src/hook.js'; -import { ajax } from '../src/ajax.js'; -import { logWarn, mergeDeep, logMessage, generateUUID } from '../src/utils.js'; -import { loadExternalScript } from '../src/adloader.js'; -import * as events from '../src/events.js'; -import CONSTANTS from '../src/constants.json'; - -let requestUrl; -let bidderArray; -let impressionIds; -let currentSiteContext; - -/** - * Init if module configuration is valid - * @param {Object} config Module configuration - * @returns {Boolean} - */ -function init (config) { - if (!config?.params?.groupId?.length > 0) { - logWarn('Qortex RTD module config does not contain valid groupId parameter. Config params: ' + JSON.stringify(config.params)) - return false; - } else { - initializeModuleData(config); - } - if (config?.params?.tagConfig) { - loadScriptTag(config) - } - return true; -} - -/** - * Processess prebid request and attempts to add context to ort2b fragments - * @param {Object} reqBidsConfig Bid request configuration object - * @param {Function} callback Called on completion - */ -function getBidRequestData (reqBidsConfig, callback) { - if (reqBidsConfig?.adUnits?.length > 0) { - getContext() - .then(contextData => { - setContextData(contextData) - addContextToRequests(reqBidsConfig) - callback(); - }) - .catch((e) => { - logWarn(e?.message); - callback(); - }); - } else { - logWarn('No adunits found on request bids configuration: ' + JSON.stringify(reqBidsConfig)) - callback(); - } -} - -/** - * determines whether to send a request to context api and does so if necessary - * @returns {Promise} ortb Content object - */ -export function getContext () { - if (!currentSiteContext) { - logMessage('Requesting new context data'); - return new Promise((resolve, reject) => { - const callbacks = { - success(text, data) { - const result = data.status === 200 ? JSON.parse(data.response)?.content : null; - resolve(result); - }, - error(error) { - reject(new Error(error)); - } - } - ajax(requestUrl, callbacks) - }) - } else { - logMessage('Adding Content object from existing context data'); - return new Promise(resolve => resolve(currentSiteContext)); - } -} - -/** - * Updates bidder configs with the response from Qortex context services - * @param {Object} reqBidsConfig Bid request configuration object - * @param {string[]} bidders Bidders specified in module's configuration - */ -export function addContextToRequests (reqBidsConfig) { - if (currentSiteContext === null) { - logWarn('No context data received at this time'); - } else { - const fragment = { site: {content: currentSiteContext} } - if (bidderArray?.length > 0) { - bidderArray.forEach(bidder => mergeDeep(reqBidsConfig.ortb2Fragments.bidder, {[bidder]: fragment})) - } else if (!bidderArray) { - mergeDeep(reqBidsConfig.ortb2Fragments.global, fragment); - } else { - logWarn('Config contains an empty bidders array, unable to determine which bids to enrich'); - } - } -} - -/** - * Loads Qortex header tag using data passed from module config object - * @param {Object} config module config obtained during init - */ -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 tc = config.params.tagConfig - - Object.keys(tc).forEach(p => { - attr[`data-${p.replace(/([A-Z])/g, (m) => `-${m.toLowerCase()}`)}`] = tc[p] - }) - - addEventListener('qortex-rtd', (e) => { - const billableEvent = { - vendor: code, - billingId: generateUUID(), - type: e?.detail?.type, - accountId: groupId - } - switch (e?.detail?.type) { - case 'qx-impression': - const {uid} = e.detail; - if (!uid || impressionIds.has(uid)) { - logWarn(`received invalid billable event due to ${!uid ? 'missing' : 'duplicate'} uid: qx-impression`) - return; - } else { - logMessage('received billable event: qx-impression') - impressionIds.add(uid) - billableEvent.transactionId = e.detail.uid; - events.emit(CONSTANTS.EVENTS.BILLABLE_EVENT, billableEvent); - break; - } - default: - logWarn(`received invalid billable event: ${e.detail?.type}`) - } - }) - - loadExternalScript(src, code, undefined, undefined, attr); -} - -/** - * Helper function to set initial values when they are obtained by init - * @param {Object} config module config obtained during init - */ -export function initializeModuleData(config) { - const DEFAULT_API_URL = 'https://demand.qortex.ai'; - const {apiUrl, groupId, bidders} = config.params; - requestUrl = `${apiUrl || DEFAULT_API_URL}/api/v1/analyze/${groupId}/prebid`; - bidderArray = bidders; - impressionIds = new Set(); - currentSiteContext = null; -} - -export function setContextData(value) { - currentSiteContext = value -} - -export const qortexSubmodule = { - name: 'qortex', - init, - getBidRequestData -} - -submodule('realTimeData', qortexSubmodule); diff --git a/modules/qortexRtdProvider.md b/modules/qortexRtdProvider.md deleted file mode 100644 index 312696068cd..00000000000 --- a/modules/qortexRtdProvider.md +++ /dev/null @@ -1,69 +0,0 @@ -# Qortex Real-time Data Submodule - -## Overview - -``` -Module Name: Qortex RTD Provider -Module Type: RTD Provider -Maintainer: mannese@qortex.ai -``` - -## Description - -The Qortex RTD module appends contextual segments to the bidding object based on the content of a page using the Qortex API. - -Upon load, the Qortex context API will analyze the bidder page (video, text, image, etc.) and will return a [Content object](https://www.iab.com/wp-content/uploads/2016/03/OpenRTB-API-Specification-Version-2-5-FINAL.pdf#page=26). The module will then merge that object into the appropriate bidders' `ortb2.site.content`, which can be used by prebid adapters that use `site.content` data. - - -## Build -``` -gulp build --modules="rtdModule,qortexRtdProvider,qortexBidAdapter,..." -``` - -> `rtdModule` is a required module to use Qortex RTD module. - -## Configuration - -Please refer to [Prebid Documentation](https://docs.prebid.org/dev-docs/publisher-api-reference/setConfig.html#setConfig-realTimeData) on RTD module configuration for details on required and optional parameters of `realTimeData` - -When configuring Qortex as a data provider, refer to the template below to add the necessary information to ensure the proper connection is made. - -### RTD Module Setup - -```javascript -pbjs.setConfig({ - realTimeData: { - auctionDelay: 1000, - dataProviders: [{ - name: 'qortex', - waitForIt: true, - params: { - groupId: 'ABC123', //required - bidders: ['qortex', 'adapter2'], //optional (see below) - tagConfig: { // optional, please reach out to your account manager for configuration reccommendation - videoContainer: 'string', - htmlContainer: 'string', - attachToTop: 'string', - esm6Mod: 'string', - continuousLoad: 'string' - } - } - }] - } -}); -``` - -### Paramter Details - -#### `groupId` - Required -- The Qortex groupId linked to the publisher, this is required to make a request using this adapter - -#### `bidders` - optional -- If this parameter is included, it must be an array of the strings that match the bidder code of the prebid adapters you would like this module to impact. `ortb2.site.content` will be updated *only* for adapters in this array - -- If this parameter is omitted, the RTD module will default to updating `ortb2.site.content` on *all* bid adapters being used on the page - -#### `tagConfig` - optional -- This optional parameter is an object containing the config settings that could be usedto initialize the Qortex integration on your page. A preconfigured object for this step will be provided to you by the Qortex team. - -- If this parameter is not present, the Qortex integration can still be configured and loaded manually on your page outside of prebid. The RTD module will continue to initialize and operate as normal. \ No newline at end of file diff --git a/modules/quantcastBidAdapter.js b/modules/quantcastBidAdapter.js index 1ba23302367..2c721a61616 100644 --- a/modules/quantcastBidAdapter.js +++ b/modules/quantcastBidAdapter.js @@ -6,11 +6,6 @@ import {registerBidder} from '../src/adapters/bidderFactory.js'; import {find} from '../src/polyfill.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; diff --git a/modules/quantcastIdSystem.js b/modules/quantcastIdSystem.js index d980f5316e5..6a07082b61c 100644 --- a/modules/quantcastIdSystem.js +++ b/modules/quantcastIdSystem.js @@ -11,10 +11,6 @@ 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; @@ -218,12 +214,6 @@ export const quantcastIdSubmodule = { } return { id: fpa ? { quantcastId: fpa } : undefined }; - }, - eids: { - 'quantcastId': { - source: 'quantcast.com', - atype: 1 - }, } }; diff --git a/modules/r2b2BidAdapter.js b/modules/r2b2BidAdapter.js deleted file mode 100644 index 15a65e3924c..00000000000 --- a/modules/r2b2BidAdapter.js +++ /dev/null @@ -1,309 +0,0 @@ -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'; -const GVL_ID = 1235; - -const DEFAULT_CURRENCY = 'USD'; -const DEFAULT_TTL = 360; -const DEFAULT_NET_REVENUE = true; -const DEBUG_PARAM = 'pbjs_test_r2b2'; -const RENDERER_URL = 'https://delivery.r2b2.io/static/rendering.js'; - -const ENDPOINT = bidderSettings.get(BIDDER_CODE, 'endpoint') || 'hb.r2b2.cz'; -const SERVER_URL = 'https://' + ENDPOINT; -const URL_BID = SERVER_URL + '/openrtb2/bid'; -const URL_SYNC = SERVER_URL + '/cookieSync'; -const URL_EVENT = SERVER_URL + '/event'; - -const URL_EVENT_ON_BIDDER_ERROR = URL_EVENT + '/bidError'; -const URL_EVENT_ON_TIMEOUT = URL_EVENT + '/timeout'; - -const R2B2_TEST_UNIT = 'selfpromo'; - -export const internal = { - placementsToSync: [], - mappedParams: {} -} - -let r2b2Error = function(message, params) { - logError(message, params, BIDDER_CODE) -} - -function getIdParamsFromPID(pid) { - // selfpromo test creative - if (pid === R2B2_TEST_UNIT) { - return { d: 'test', g: 'test', p: 'selfpromo', m: 0, selfpromo: 1 } - } - if (!isNaN(pid)) { - return { pid: Number(pid) } - } - if (typeof pid === 'string') { - const params = pid.split('/'); - if (params.length === 3 || params.length === 4) { - const paramNames = ['d', 'g', 'p', 'm']; - return paramNames.reduce((p, paramName, index) => { - let param = params[index]; - if (paramName === 'm') { - param = ['desktop', 'classic', '0'].includes(param) ? 0 : Number(!!param) - } - p[paramName] = param; - return p - }, {}); - } - } -} - -function pickIdFromParams(params) { - if (!params) return null; - const { d, g, p, m, pid } = params; - return d ? { d, g, p, m } : { pid }; -} - -function getIdsFromBids(bids) { - return bids.reduce((ids, bid) => { - const params = internal.mappedParams[bid.bidId]; - const id = pickIdFromParams(params); - if (id) { - ids.push(id); - } - return ids - }, []); -} - -function triggerEvent(eventUrl, ids) { - if (ids && !ids.length) return; - const timeStamp = new Date().getTime(); - const symbol = (eventUrl.indexOf('?') === -1 ? '?' : '&'); - const url = eventUrl + symbol + `p=${btoa(JSON.stringify(ids))}&cb=${timeStamp}`; - triggerPixel(url) -} - -const converter = ortbConverter({ - imp(buildImp, bidRequest, context) { - const imp = buildImp(bidRequest, context); - const idParams = getIdParamsFromPID(bidRequest.params.pid); - deepSetValue(imp, 'ext.r2b2', idParams); - internal.placementsToSync.push(idParams); - internal.mappedParams[imp.id] = Object.assign({}, bidRequest.params, idParams); - return imp; - }, - request(buildRequest, imps, bidderRequest, context) { - const request = buildRequest(imps, bidderRequest, context); - deepSetValue(request, 'ext.version', ADAPTER_VERSION); - request.cur = [DEFAULT_CURRENCY]; - const test = getParameterByName(DEBUG_PARAM) === '1' ? 1 : 0; - deepSetValue(request, 'test', test); - return request; - }, - context: { - netRevenue: DEFAULT_NET_REVENUE, - ttl: DEFAULT_TTL - }, - processors: pbsExtensions -}); - -function setUpRenderer(adUnitCode, bid) { - // let renderer load once in main window, but pass the renderDocument - let renderDoc; - const config = { - documentResolver: (bid, sourceDocument, renderDocument) => { - renderDoc = renderDocument; - return sourceDocument; - } - } - let renderer = Renderer.install({ - url: RENDERER_URL, - config: config, - id: bid.requestId, - adUnitCode - }); - - renderer.setRender(function (bid, doc) { - doc = renderDoc || doc; - window.R2B2 = window.R2B2 || {}; - let main = window.R2B2; - main.HB = main.HB || {}; - main.HB.Render = main.HB.Render || {}; - main.HB.Render.queue = main.HB.Render.queue || []; - main.HB.Render.queue.push(() => { - const id = pickIdFromParams(internal.mappedParams[bid.requestId]) - main.HB.Renderer.render(id, bid, null, doc) - }) - }) - - return renderer -} - -function getExtMediaType(bidMediaType, responseBid) { - switch (bidMediaType) { - case BANNER: - return { - type: 'banner', - settings: { - chd: null, - width: responseBid.w, - height: responseBid.h, - ad: { - type: 'content', - data: responseBid.adm - } - } - }; - case NATIVE: - break; - case VIDEO: - break; - default: - break; - } -} - -function createPrebidResponseBid(requestImp, bidResponse, serverResponse, bids) { - const bidId = requestImp.id; - const adUnitCode = bids[0].adUnitCode; - const mediaType = bidResponse.ext.prebid.type; - let bidOut = { - requestId: bidId, - cpm: bidResponse.price, - creativeId: bidResponse.crid, - width: bidResponse.w, - height: bidResponse.h, - ttl: bidResponse.ttl ?? DEFAULT_TTL, - netRevenue: serverResponse.netRevenue ?? DEFAULT_NET_REVENUE, - currency: serverResponse.cur ?? DEFAULT_CURRENCY, - ad: bidResponse.adm, - mediaType: mediaType, - winUrl: bidResponse.nurl, - ext: { - cid: bidResponse.ext?.r2b2?.cid, - cdid: bidResponse.ext?.r2b2?.cdid, - mediaType: getExtMediaType(mediaType, bidResponse), - adUnit: adUnitCode, - dgpm: internal.mappedParams[bidId], - events: bidResponse.ext?.r2b2?.events - } - }; - if (bidResponse.ext?.r2b2?.useRenderer) { - bidOut.renderer = setUpRenderer(adUnitCode, bidOut); - } - return bidOut; -} - -export const spec = { - code: BIDDER_CODE, - gvlid: GVL_ID, - supportedMediaTypes: [BANNER], - - isBidRequestValid: function(bid) { - if (!bid.params || !bid.params.pid) { - logWarn('Bad params, "pid" required.'); - return false - } - const id = getIdParamsFromPID(bid.params.pid); - if (!id || !(id.pid || (id.d && id.g && id.p))) { - logWarn('Bad params, "pid" has to be either a number or a correctly assembled string.'); - return false - } - return true - }, - buildRequests: function(validBidRequests, bidderRequest) { - const data = converter.toORTB({ - bidRequests: validBidRequests, - bidderRequest - }); - return [{ - method: 'POST', - url: URL_BID, - data, - bids: bidderRequest.bids - }] - }, - - interpretResponse: function(serverResponse, request) { - // r2b2Error('error message', {params: 1}); - let prebidResponses = []; - - const response = serverResponse.body; - if (!response || !response.seatbid || !response.seatbid[0] || !response.seatbid[0].bid) { - return prebidResponses; - } - let requestImps = request.data.imp || []; - try { - response.seatbid.forEach(seat => { - let bids = seat.bid; - - for (let responseBid of bids) { - let responseImpId = responseBid.impid; - let requestCurrentImp = requestImps.find((requestImp) => requestImp.id === responseImpId); - if (!requestCurrentImp) { - 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}); - } - return prebidResponses; - }, - getUserSyncs: function(syncOptions, serverResponses, gdprConsent, uspConsent) { - const syncs = []; - - if (!syncOptions.iframeEnabled) { - logWarn('Please enable iframe based user sync.'); - return syncs; - } - - let plString; - try { - plString = btoa(JSON.stringify(internal.placementsToSync || [])); - } catch (e) { - logWarn('User sync failed: ' + e.message); - return syncs - } - - let url = URL_SYNC + `?p=${plString}`; - - if (gdprConsent) { - url += `&gdpr=${Number(gdprConsent.gdprApplies)}&gdpr_consent=${gdprConsent.consentString}` - } - - if (uspConsent) { - url += `&us_privacy=${uspConsent}` - } - - syncs.push({ - type: 'iframe', - url: url - }) - return syncs; - }, - onBidWon: function(bid) { - const url = bid.ext?.events?.onBidWon; - if (url) { - triggerEvent(url) - } - }, - onSetTargeting: function(bid) { - const url = bid.ext?.events?.onSetTargeting; - if (url) { - triggerEvent(url) - } - }, - onTimeout: function(bids) { - triggerEvent(URL_EVENT_ON_TIMEOUT, getIdsFromBids(bids)) - }, - onBidderError: function(params) { - let { bidderRequest } = params; - triggerEvent(URL_EVENT_ON_BIDDER_ERROR, getIdsFromBids(bidderRequest.bids)) - } -} -registerBidder(spec); diff --git a/modules/r2b2BidAdapter.md b/modules/r2b2BidAdapter.md deleted file mode 100644 index 43b59133215..00000000000 --- a/modules/r2b2BidAdapter.md +++ /dev/null @@ -1,37 +0,0 @@ -# Overview - -``` -Module Name: R2B2 Bid Adapter -Module Type: Bidder Adapter -Maintainer: dev@r2b2.cz -``` - -## Description - -Module that integrates R2B2 demand sources. To get your bidder configuration reach out to our account team on partner@r2b2.io - - - -## Test unit - -```javascript - var adUnits = [ - { - code: 'test-r2b2', - mediaTypes: { - banner: { - sizes: [[300, 250], [300, 600]], - } - }, - bids: [{ - bidder: 'r2b2', - params: { - pid: 'selfpromo' - } - }] - } - ]; -``` -## Rendering - -Our adapter can feature a custom renderer specifically for display ads, tailored to enhance ad presentation and functionality. This is particularly beneficial for non-standard ad formats that require more complex logic. It's important to note that our rendering process operates outside of SafeFrames. For additional information, not limited to rendering aspects, please feel free to contact us at partner@r2b2.io diff --git a/modules/radsBidAdapter.js b/modules/radsBidAdapter.js index faa35ee51f7..ae16bcf9d83 100644 --- a/modules/radsBidAdapter.js +++ b/modules/radsBidAdapter.js @@ -2,10 +2,6 @@ import {deepAccess} 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 - */ - const BIDDER_CODE = 'rads'; const ENDPOINT_URL = 'https://rads.recognified.net/md.request.php'; const ENDPOINT_URL_DEV = 'https://dcradn1.online-solution.biz/md.request.php'; diff --git a/modules/rasBidAdapter.js b/modules/rasBidAdapter.js index 4e93f2aa8eb..a7aceb107b9 100644 --- a/modules/rasBidAdapter.js +++ b/modules/rasBidAdapter.js @@ -1,8 +1,7 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER } from '../src/mediaTypes.js'; -import { isEmpty, parseSizesInput, deepAccess } from '../src/utils.js'; +import { isEmpty, getAdUnitSizes, parseSizesInput, deepAccess } from '../src/utils.js'; import {getAllOrtbKeywords} from '../libraries/keywords/keywords.js'; -import {getAdUnitSizes} from '../libraries/sizeUtils/sizeUtils.js'; const BIDDER_CODE = 'ras'; const VERSION = '1.0'; @@ -130,36 +129,6 @@ const getGdprParams = (bidderRequest) => { return queryString; }; -const parseAuctionConfigs = (serverResponse, bidRequest) => { - if (isEmpty(bidRequest)) { - return null; - } - const auctionConfigs = []; - const gctx = serverResponse && serverResponse.body?.gctx; - - bidRequest.bidIds.filter(bid => bid.fledgeEnabled).forEach((bid) => { - auctionConfigs.push({ - 'bidId': bid.bidId, - 'config': { - 'seller': 'https://csr.onet.pl', - 'decisionLogicUrl': `https://csr.onet.pl/${encodeURIComponent(bid.params.network)}/v1/protected-audience-api/decision-logic.js`, - 'interestGroupBuyers': ['https://csr.onet.pl'], - 'auctionSignals': { - 'params': bid.params, - 'sizes': bid.sizes, - 'gctx': gctx - } - } - }); - }); - - if (auctionConfigs.length === 0) { - return null; - } else { - return auctionConfigs; - } -} - export const spec = { code: BIDDER_CODE, supportedMediaTypes: [BANNER], @@ -176,16 +145,8 @@ export const spec = { const slotsQuery = getSlots(bidRequests); const contextQuery = getContextParams(bidRequests, bidderRequest); const gdprQuery = getGdprParams(bidderRequest); - const fledgeEligible = Boolean(bidderRequest && bidderRequest.fledgeEnabled); + const bidIds = bidRequests.map((bid) => ({ slot: bid.params.slot, bidId: bid.bidId })); const network = bidRequests[0].params.network; - const bidIds = bidRequests.map((bid) => ({ - slot: bid.params.slot, - bidId: bid.bidId, - sizes: getAdUnitSizes(bid), - params: bid.params, - fledgeEnabled: fledgeEligible - })); - return [{ method: 'GET', url: getEndpoint(network) + contextQuery + slotsQuery + gdprQuery, @@ -195,16 +156,10 @@ export const spec = { interpretResponse: function (serverResponse, bidRequest) { const response = serverResponse.body; - - const fledgeAuctionConfigs = parseAuctionConfigs(serverResponse, bidRequest); - const bids = (!response || !response.ads || response.ads.length === 0) ? [] : response.ads.map(buildBid).filter((bid) => !isEmpty(bid)); - - if (fledgeAuctionConfigs) { - // Return a tuple of bids and auctionConfigs. It is possible that bids could be null. - return {bids, fledgeAuctionConfigs}; - } else { - return bids; + if (!response || !response.ads || response.ads.length === 0) { + return []; } + return response.ads.map(buildBid).filter((bid) => !isEmpty(bid)); } }; diff --git a/modules/reconciliationRtdProvider.js b/modules/reconciliationRtdProvider.js index 5671b2021d8..9b6a3d7aca3 100644 --- a/modules/reconciliationRtdProvider.js +++ b/modules/reconciliationRtdProvider.js @@ -21,10 +21,6 @@ import {ajaxBuilder} from '../src/ajax.js'; import {generateUUID, isGptPubadsDefined, logError, timestamp} from '../src/utils.js'; import {find} from '../src/polyfill.js'; -/** - * @typedef {import('../modules/rtdModule/index.js').RtdSubmodule} RtdSubmodule - */ - /** @type {Object} */ const MessageType = { IMPRESSION_REQUEST: 'rsdk:impression:req', diff --git a/modules/relaidoBidAdapter.js b/modules/relaidoBidAdapter.js index 751e8fa442c..b2961b09eb5 100644 --- a/modules/relaidoBidAdapter.js +++ b/modules/relaidoBidAdapter.js @@ -1,14 +1,4 @@ -import { - deepAccess, - logWarn, - parseQueryStringParameters, - triggerPixel, - generateUUID, - isArray, - isNumber, - parseSizesInput, - getBidIdParameter -} from '../src/utils.js'; +import { deepAccess, logWarn, getBidIdParameter, parseQueryStringParameters, triggerPixel, generateUUID, isArray, isNumber, parseSizesInput } from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; import { Renderer } from '../src/Renderer.js'; @@ -104,8 +94,7 @@ function buildRequests(validBidRequests, bidderRequest) { width: width, height: height, banner_sizes: getBannerSizes(bidRequest), - media_type: mediaType, - userIdAsEids: bidRequest.userIdAsEids || {}, + media_type: mediaType }); } @@ -118,7 +107,6 @@ function buildRequests(validBidRequests, bidderRequest) { uuid: getUuid(), pv: '$prebid.version$', imuid: imuid, - canonical_url: bidderRequest.refererInfo?.canonicalUrl || null, canonical_url_hash: getCanonicalUrlHash(bidderRequest.refererInfo), ref: bidderRequest.refererInfo.page }); @@ -144,7 +132,6 @@ function interpretResponse(serverResponse, bidRequest) { const playerUrl = res.playerUrl || bidRequest.player || body.playerUrl; let bidResponse = { requestId: res.bidId, - placementId: res.placementId, width: res.width, height: res.height, cpm: res.price, diff --git a/modules/relayBidAdapter.js b/modules/relayBidAdapter.js deleted file mode 100644 index af145a5e163..00000000000 --- a/modules/relayBidAdapter.js +++ /dev/null @@ -1,99 +0,0 @@ -import { isNumber, logMessage } 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 { ortbConverter } from '../libraries/ortbConverter/converter.js' - -const BIDDER_CODE = 'relay'; -const METHOD = 'POST'; -const ENDPOINT_URL = 'https://e.relay.bid/p/openrtb2'; - -// The default impl from the prebid docs. -const CONVERTER = - ortbConverter({ - context: { - netRevenue: true, - ttl: 30 - } - }); - -function buildRequests(bidRequests, bidderRequest) { - const prebidVersion = config.getConfig('prebid_version') || 'v8.1.0'; - // Group bids by accountId param - const groupedByAccountId = bidRequests.reduce((accu, item) => { - const accountId = ((item || {}).params || {}).accountId; - if (!accu[accountId]) { accu[accountId] = []; }; - accu[accountId].push(item); - return accu; - }, {}); - // Send one overall request with all grouped bids per accountId - let reqs = []; - for (const [accountId, accountBidRequests] of Object.entries(groupedByAccountId)) { - const url = `${ENDPOINT_URL}?a=${accountId}&pb=1&pbv=${prebidVersion}`; - const data = CONVERTER.toORTB({ bidRequests: accountBidRequests, bidderRequest }) - const req = { - method: METHOD, - url, - data - }; - reqs.push(req); - } - return reqs; -}; - -function interpretResponse(response, request) { - return CONVERTER.fromORTB({ response: response.body, request: request.data }).bids; -}; - -function isBidRequestValid(bid) { - return isNumber((bid.params || {}).accountId); -}; - -function getUserSyncs(syncOptions, serverResponses, gdprConsent, uspConsent) { - let syncs = [] - for (const response of serverResponses) { - const responseSyncs = ((((response || {}).body || {}).ext || {}).user_syncs || []) - // Relay returns user_syncs in the format expected by prebid. If for any - // reason the request/response failed to properly capture the GDPR settings - // -- fallback to those identified by Prebid. - for (const sync of responseSyncs) { - const syncUrl = new URL(sync.url); - const missingGdpr = !syncUrl.searchParams.has('gdpr'); - const missingGdprConsent = !syncUrl.searchParams.has('gdpr_consent'); - if (missingGdpr) { - syncUrl.searchParams.set('gdpr', Number(gdprConsent.gdprApplies)) - sync.url = syncUrl.toString(); - } - if (missingGdprConsent) { - syncUrl.searchParams.set('gdpr_consent', gdprConsent.consentString); - sync.url = syncUrl.toString(); - } - if (syncOptions.iframeEnabled && sync.type === 'iframe') { - syncs.push(sync); - } else if (syncOptions.pixelEnabled && sync.type === 'image') { - syncs.push(sync); - } - } - } - - return syncs; -} - -export const spec = { - code: BIDDER_CODE, - isBidRequestValid, - buildRequests, - interpretResponse, - getUserSyncs, - onTimeout: function (timeoutData) { - logMessage('Timeout: ', timeoutData); - }, - onBidWon: function (bid) { - logMessage('Bid won: ', bid); - }, - onBidderError: function ({ error, bidderRequest }) { - logMessage('Error: ', error, bidderRequest); - }, - supportedMediaTypes: [BANNER, VIDEO, NATIVE] -} -registerBidder(spec); diff --git a/modules/relayBidAdapter.md b/modules/relayBidAdapter.md deleted file mode 100644 index 882e04b7b13..00000000000 --- a/modules/relayBidAdapter.md +++ /dev/null @@ -1,79 +0,0 @@ -# Overview - -``` -Module Name: Relay Bid Adapter -Module Type: Bid Adapter -Maintainer: relay@kevel.co -``` - -# Description - -Connects to Relay exchange API for bids. -Supports Banner, Video and Native. - -# Test Parameters - -``` -var adUnits = [ - // Banner with minimal bid configuration - { - code: 'minimal', - mediaTypes: { - banner: { - sizes: [[300, 250]] - } - }, - bids: [ - { - bidder: 'relay', - params: { - accountId: 1234 - }, - ortb2imp: { - ext: { - relay: { - bidders: { - bidderA: { - param: 1234 - } - } - } - } - } - } - ] - }, - // Minimal video - { - code: 'video-minimal', - mediaTypes: { - video: { - maxduration: 30, - api: [1, 3], - mimes: ['video/mp4'], - placement: 3, - protocols: [2,3,5,6] - } - }, - bids: [ - { - bidder: 'relay', - params: { - accountId: 1234 - }, - ortb2imp: { - ext: { - relay: { - bidders: { - bidderA: { - param: 'example' - } - } - } - } - } - } - ] - } -]; -``` diff --git a/modules/relevadRtdProvider.js b/modules/relevadRtdProvider.js index 613eaa71a1f..a7d0305da62 100644 --- a/modules/relevadRtdProvider.js +++ b/modules/relevadRtdProvider.js @@ -110,7 +110,6 @@ function composeOrtb2Data(rtdData, prefix) { !isEmpty(categories.cat) && deepSetValue(addOrtb2, prefix + '.cat', categories.cat); !isEmpty(categories.pagecat) && deepSetValue(addOrtb2, prefix + '.pagecat', categories.pagecat); !isEmpty(categories.sectioncat) && deepSetValue(addOrtb2, prefix + '.sectioncat', categories.sectioncat); - !isEmpty(categories.sectioncat) && deepSetValue(addOrtb2, prefix + '.ext.data.relevad_rtd', categories.sectioncat); !isEmpty(categories.cattax) && deepSetValue(addOrtb2, prefix + '.cattax', categories.cattax); if (!isEmpty(content) && !isEmpty(content.segs) && content.segtax) { @@ -135,8 +134,7 @@ function setBidderSiteAndContent(bidderOrtbFragment, bidder, rtdData) { try { let addOrtb2 = composeOrtb2Data(rtdData, 'site'); !isEmpty(rtdData.segments) && deepSetValue(addOrtb2, 'user.ext.data.relevad_rtd', rtdData.segments); - !isEmpty(rtdData.segments) && deepSetValue(addOrtb2, 'user.ext.data.segments', rtdData.segments); - !isEmpty(rtdData.categories) && deepSetValue(addOrtb2, 'user.ext.data.contextual_categories', rtdData.categories.pagecat); + !isEmpty(rtdData.categories?.sectioncat) && deepSetValue(addOrtb2, 'site.ext.data.relevad_rtd', rtdData.categories.sectioncat); if (isEmpty(addOrtb2)) { return; } @@ -182,7 +180,7 @@ function getFiltered(data, minscore) { const cats = filterByScore(data.cats, 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; + const cattax = data.cattax ? data.cattax : CATTAX_IAB; relevadData.categories = {cat: cats, pagecat: pcats, sectioncat: scats, cattax: cattax}; const contsegs = filterByScore(data.contsegs, minscore); @@ -270,12 +268,15 @@ export function addRtdData(reqBids, data, moduleConfig) { } if (wb && !isEmpty(relevadList)) { setBidderSiteAndContent(reqBids.ortb2Fragments?.bidder, bid.bidder, relevadData); - setBidderSiteAndContent(bid, 'ortb2', relevadData); deepSetValue(bid, 'params.keywords.relevad_rtd', relevadList); - !(bid.params?.target || '').includes('relevad_rtd=') && deepSetValue(bid, 'params.target', [].concat(bid.params?.target ? [bid.params.target] : []).concat(relevadList.map(entry => { return 'relevad_rtd=' + entry; })).join(';')); + deepSetValue(bid, 'params.target', [].concat(bid.params?.target ? [bid.params.target] : []).concat(relevadList.map(entry => { return 'relevad_rtd=' + entry; })).join(';')); let firstPartyData = {}; firstPartyData[bid.bidder] = { firstPartyData: { relevad_rtd: relevadList } }; config.setConfig(firstPartyData); + !isEmpty(relevadData.segments) && deepSetValue(bid, 'ortb2.user.ext.data.segments', relevadData.segments); + !isEmpty(relevadData.categories) && deepSetValue(bid, 'ortb2.user.ext.data.contextual_categories', relevadData.categories.pagecat); + !isEmpty(relevadData.categories) && deepSetValue(bid, 'ortb2.site.ext.data.relevad_rtd', relevadData.categories.pagecat); + !isEmpty(relevadData.segments) && deepSetValue(bid, 'ortb2.user.ext.data.relevad_rtd', relevadData.segments); } } } catch (e) { diff --git a/modules/relevantdigitalBidAdapter.js b/modules/relevantdigitalBidAdapter.js index 8d1265075f9..ad9ee5e1e14 100644 --- a/modules/relevantdigitalBidAdapter.js +++ b/modules/relevantdigitalBidAdapter.js @@ -94,18 +94,11 @@ export const spec = { gvlid: 1100, supportedMediaTypes: [BANNER, VIDEO, NATIVE], - /** We need both params.placementId + a complete configuration (pbsHost + accountId) to continue */ + /** We need both params.placementId + a complete configuration (pbsHost + accountId) to continue **/ isBidRequestValid: (bid) => bid.params?.placementId && getBidderConfig([bid]).complete, /** Trigger impression-pixel */ - onBidWon(bid) { - if (bid.pbsWurl) { - triggerPixel(bid.pbsWurl) - } - if (bid.burl) { - triggerPixel(bid.burl) - } - }, + onBidWon: ({pbsWurl}) => pbsWurl && triggerPixel(pbsWurl), /** Build BidRequest for PBS */ buildRequests(bidRequests, bidderRequest) { @@ -200,24 +193,6 @@ export const spec = { }); return syncs; }, - - /** If server side, transform bid params if needed */ - transformBidParams(params, isOrtb, adUnit, bidRequests) { - if (!params.placementId) { - return; - } - const bid = bidRequests.flatMap(req => req.adUnitsS2SCopy || []).flatMap((adUnit) => adUnit.bids).find((bid) => bid.params?.placementId === params.placementId); - if (!bid) { - return; - } - const cfg = getBidderConfig([bid]); - FIELDS.forEach(({ name }) => { - if (cfg[name] && !params[name]) { - params[name] = cfg[name]; - } - }); - return params; - }, }; registerBidder(spec); diff --git a/modules/resetdigitalBidAdapter.js b/modules/resetdigitalBidAdapter.js index 2bac6c6dcba..8264e0cc9cc 100644 --- a/modules/resetdigitalBidAdapter.js +++ b/modules/resetdigitalBidAdapter.js @@ -1,29 +1,25 @@ import { timestamp, deepAccess, isStr, deepClone } from '../src/utils.js'; import { getOrigin } from '../libraries/getOrigin/index.js'; import { config } from '../src/config.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 = 'resetdigital'; const CURRENCY = 'USD'; export const spec = { code: BIDDER_CODE, - supportedMediaTypes: ['banner', 'video'], - isBidRequestValid: function (bid) { - return !!(bid.params.pubId || bid.params.zoneId); + supportedMediaTypes: [ 'banner', 'video' ], + isBidRequestValid: function(bid) { + return (!!(bid.params.pubId || bid.params.zoneId)); }, - buildRequests: function (validBidRequests, bidderRequest) { - let stack = - bidderRequest.refererInfo && bidderRequest.refererInfo.stack - ? bidderRequest.refererInfo.stack - : []; - - let spb = - config.getConfig('userSync') && - config.getConfig('userSync').syncsPerBidder - ? config.getConfig('userSync').syncsPerBidder - : 5; + buildRequests: function(validBidRequests, bidderRequest) { + let stack = (bidderRequest.refererInfo && + bidderRequest.refererInfo.stack ? bidderRequest.refererInfo.stack + : []) + + let spb = (config.getConfig('userSync') && config.getConfig('userSync').syncsPerBidder) + ? config.getConfig('userSync').syncsPerBidder : 5 const payload = { start_time: timestamp(), @@ -33,19 +29,19 @@ export const spec = { iframe: !bidderRequest.refererInfo.reachedTop, // TODO: the last element in refererInfo.stack is window.location.href, that's unlikely to have been the intent here url: stack && stack.length > 0 ? [stack.length - 1] : null, - https: window.location.protocol === 'https:', + https: (window.location.protocol === 'https:'), // TODO: is 'page' the right value here? - referrer: bidderRequest.refererInfo.page, + referrer: bidderRequest.refererInfo.page }, imps: [], user_ids: validBidRequests[0].userId, - sync_limit: spb, + sync_limit: spb }; if (bidderRequest && bidderRequest.gdprConsent) { payload.gdpr = { applies: bidderRequest.gdprConsent.gdprApplies, - consent: bidderRequest.gdprConsent.consentString, + consent: bidderRequest.gdprConsent.consentString }; } @@ -54,16 +50,10 @@ export const spec = { } function getOrtb2Keywords(ortb2Obj) { - const fields = [ - 'site.keywords', - 'site.content.keywords', - 'user.keywords', - 'app.keywords', - 'app.content.keywords', - ]; + const fields = ['site.keywords', 'site.content.keywords', 'user.keywords', 'app.keywords', 'app.content.keywords']; let result = []; - fields.forEach((path) => { + fields.forEach(path => { let keyStr = deepAccess(ortb2Obj, path); if (isStr(keyStr)) result.push(keyStr); }); @@ -89,26 +79,18 @@ export const spec = { const floorInfo = req.getFloor({ currency: CURRENCY, mediaType: BANNER, - size: '*', + size: '*' }); - if ( - typeof floorInfo === 'object' && - floorInfo.currency === CURRENCY && - !isNaN(parseFloat(floorInfo.floor)) - ) { + if (typeof floorInfo === 'object' && floorInfo.currency === CURRENCY && !isNaN(parseFloat(floorInfo.floor))) { bidFloor = parseFloat(floorInfo.floor); bidFloorCur = CURRENCY; } } // get param kewords (if it exists) - let paramsKeywords = req.params.keywords - ? req.params.keywords.split(',') - : []; + let paramsKeywords = req.params.keywords ? req.params.keywords.split(',') : []; // merge all keywords - let keywords = ortb2KeywordsList - .concat(paramsKeywords) - .concat(metaKeywords); + let keywords = ortb2KeywordsList.concat(paramsKeywords).concat(metaKeywords); payload.imps.push({ pub_id: req.params.pubId, @@ -128,32 +110,32 @@ export const spec = { sizes: req.sizes, force_bid: req.params.forceBid, coppa: config.getConfig('coppa') === true ? 1 : 0, - media_types: deepAccess(req, 'mediaTypes'), + media_types: deepAccess(req, 'mediaTypes') }); } - let params = validBidRequests[0].params; - let url = params.endpoint ? params.endpoint : '//ads.resetsrv.com'; + let params = validBidRequests[0].params + let url = params.endpoint ? params.endpoint : '//ads.resetsrv.com' return { method: 'POST', url: url, data: JSON.stringify(payload), - bids: validBidRequests, + bids: validBidRequests }; }, - interpretResponse: function (serverResponse, bidRequest) { + interpretResponse: function(serverResponse, bidRequest) { const bidResponses = []; if (!serverResponse || !serverResponse.body) { - return bidResponses; + return bidResponses } let res = serverResponse.body; if (!res.bids || !res.bids.length) { - return []; + return [] } for (let x = 0; x < serverResponse.body.bids.length; x++) { - let bid = serverResponse.body.bids[x]; + let bid = serverResponse.body.bids[x] bidResponses.push({ requestId: bid.bid_id, @@ -170,45 +152,47 @@ export const spec = { netRevenue: true, currency: 'USD', meta: { - advertiserDomains: bid.adomain, - }, - }); + advertiserDomains: bid.adomain + } + }) } return bidResponses; }, - getUserSyncs: function (syncOptions, serverResponses, gdprConsent) { - const syncs = []; + getUserSyncs: function(syncOptions, serverResponses, gdprConsent) { + const syncs = [] if (!serverResponses.length || !serverResponses[0].body) { - return syncs; + return syncs } - let pixels = serverResponses[0].body.pixels; + let pixels = serverResponses[0].body.pixels if (!pixels || !pixels.length) { - return syncs; + return syncs } - let gdprParams = ''; + let gdprParams = null if (gdprConsent) { if (typeof gdprConsent.gdprApplies === 'boolean') { - gdprParams = `gdpr=${Number(gdprConsent.gdprApplies)}&gdpr_consent=${ - gdprConsent.consentString - }`; + gdprParams = `gdpr=${Number(gdprConsent.gdprApplies)}&gdpr_consent=${gdprConsent.consentString}` } else { - gdprParams = `gdpr_consent=${gdprConsent.consentString}`; + gdprParams = `gdpr_consent=${gdprConsent.consentString}` } } - if ((syncOptions.iframeEnabled || syncOptions.pixelEnabled)) { - return [ - { - type: 'iframe', - url: 'https://media.reset-digital.com/prebid/async_usersync.html?' + gdprParams.length ? gdprParams : '', - }, - ]; + for (let x = 0; x < pixels.length; x++) { + let pixel = pixels[x] + + if ((pixel.type === 'iframe' && syncOptions.iframeEnabled) || + (pixel.type === 'image' && syncOptions.pixelEnabled)) { + if (gdprParams && gdprParams.length) { + pixel = (pixel.indexOf('?') === -1 ? '?' : '&') + gdprParams + } + syncs.push(pixel) + } } - }, + return syncs; + } }; registerBidder(spec); diff --git a/modules/retailspotBidAdapter.js b/modules/retailspotBidAdapter.js index 557dd617274..616b638e840 100644 --- a/modules/retailspotBidAdapter.js +++ b/modules/retailspotBidAdapter.js @@ -2,11 +2,6 @@ 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 - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - */ - const BIDDER_CODE = 'retailspot'; const DEFAULT_SUBDOMAIN = 'ssp'; const PREPROD_SUBDOMAIN = 'ssp-preprod'; @@ -33,7 +28,7 @@ export const spec = { /** * Make a server request from the list of BidRequests. * - * @param {BidRequests} - bidRequests.bids[] is an array of AdUnits and bids + * @param {bidRequests} - bidRequests.bids[] is an array of AdUnits and bids * @return ServerRequest Info describing the request to the server. */ buildRequests: function (bidRequests, bidderRequest) { diff --git a/modules/revcontentBidAdapter.js b/modules/revcontentBidAdapter.js index f1d5521f780..5bf7dd691e7 100644 --- a/modules/revcontentBidAdapter.js +++ b/modules/revcontentBidAdapter.js @@ -3,10 +3,9 @@ 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 {_map, deepAccess, getAdUnitSizes, 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 NATIVE_PARAMS = { diff --git a/modules/richaudienceBidAdapter.js b/modules/richaudienceBidAdapter.js index 36513aeda47..1625912ddb8 100755 --- a/modules/richaudienceBidAdapter.js +++ b/modules/richaudienceBidAdapter.js @@ -1,4 +1,4 @@ -import {deepAccess, isStr, triggerPixel} from '../src/utils.js'; +import {deepAccess, isStr} from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {config} from '../src/config.js'; import {BANNER, VIDEO} from '../src/mediaTypes.js'; @@ -183,13 +183,6 @@ export const spec = { } return syncs }, - - onTimeout: function (data) { - let url = raiGetTimeoutURL(data); - if (url) { - triggerPixel(url); - } - } }; registerBidder(spec); @@ -339,15 +332,3 @@ function raiGetFloor(bid, config) { return 0 } } - -function raiGetTimeoutURL(data) { - let {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) - url = url.replace('[placement_hash]', params[0].pid) - if (REFERER != null) { - url = url.replace('[domain]', document.location.host) - } - return url -} diff --git a/modules/riseBidAdapter.js b/modules/riseBidAdapter.js index 78854129f8a..d5c6469db12 100644 --- a/modules/riseBidAdapter.js +++ b/modules/riseBidAdapter.js @@ -1,16 +1,4 @@ -import { - logWarn, - logInfo, - isArray, - isFn, - deepAccess, - isEmpty, - contains, - timestamp, - triggerPixel, - isInteger, - getBidIdParameter -} from '../src/utils.js'; +import { logWarn, logInfo, isArray, isFn, deepAccess, isEmpty, contains, timestamp, getBidIdParameter, triggerPixel, isInteger } from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {BANNER, VIDEO} from '../src/mediaTypes.js'; import {config} from '../src/config.js'; @@ -19,8 +7,7 @@ const SUPPORTED_AD_TYPES = [BANNER, VIDEO]; const BIDDER_CODE = 'rise'; const ADAPTER_VERSION = '6.0.0'; const TTL = 360; -const DEFAULT_CURRENCY = 'USD'; -const DEFAULT_GVLID = 1043; +const CURRENCY = 'USD'; const DEFAULT_SELLER_ENDPOINT = 'https://hb.yellowblue.io/'; const MODES = { PRODUCTION: 'hb-multi', @@ -33,11 +20,7 @@ const SUPPORTED_SYNC_METHODS = { export const spec = { code: BIDDER_CODE, - aliases: [ - { code: 'risexchange', gvlid: DEFAULT_GVLID }, - { code: 'openwebxchange', gvlid: 280 } - ], - gvlid: DEFAULT_GVLID, + gvlid: 1043, version: ADAPTER_VERSION, supportedMediaTypes: SUPPORTED_AD_TYPES, isBidRequestValid: function (bidRequest) { @@ -78,7 +61,7 @@ export const spec = { const bidResponse = { requestId: adUnit.requestId, cpm: adUnit.cpm, - currency: adUnit.currency || DEFAULT_CURRENCY, + currency: adUnit.currency || CURRENCY, width: adUnit.width, height: adUnit.height, ttl: adUnit.ttl || TTL, @@ -145,20 +128,18 @@ registerBidder(spec); /** * Get floor price * @param bid {bid} - * @param mediaType {string} - * @param currency {string} * @returns {Number} */ -function getFloor(bid, mediaType, currency) { +function getFloor(bid, mediaType) { if (!isFn(bid.getFloor)) { return 0; } let floorResult = bid.getFloor({ - currency: currency, + currency: CURRENCY, mediaType: mediaType, size: '*' }); - return floorResult.currency === currency && floorResult.floor ? floorResult.floor : 0; + return floorResult.currency === CURRENCY && floorResult.floor ? floorResult.floor : 0; } /** @@ -296,7 +277,7 @@ function generateBidParameters(bid, bidderRequest) { const {params} = bid; const mediaType = isBanner(bid) ? BANNER : VIDEO; const sizesArray = getSizesArray(bid, mediaType); - const currency = params.currency || config.getConfig('currency.adServerCurrency') || DEFAULT_CURRENCY; + // fix floor price in case of NAN if (isNaN(params.floorPrice)) { params.floorPrice = 0; @@ -306,13 +287,12 @@ function generateBidParameters(bid, bidderRequest) { mediaType, adUnitCode: getBidIdParameter('adUnitCode', bid), sizes: sizesArray, - currency: currency, - floorPrice: Math.max(getFloor(bid, mediaType, currency), params.floorPrice), + floorPrice: Math.max(getFloor(bid, mediaType), params.floorPrice), bidId: getBidIdParameter('bidId', bid), bidderRequestId: getBidIdParameter('bidderRequestId', bid), loop: getBidIdParameter('bidderRequestsCount', bid), transactionId: bid.ortb2Imp?.ext?.tid, - coppa: 0, + coppa: 0 }; const pos = deepAccess(bid, `mediaTypes.${mediaType}.pos`); diff --git a/modules/riseBidAdapter.md b/modules/riseBidAdapter.md index ac0ea559c88..f0837cb5508 100644 --- a/modules/riseBidAdapter.md +++ b/modules/riseBidAdapter.md @@ -26,7 +26,6 @@ The adapter supports Video(instream). | `testMode` | optional | Boolean | This activates the test mode | false | `rtbDomain` | optional | String | Sets the seller end point | "www.test.com" | `is_wrapper` | private | Boolean | Please don't use unless your account manager asked you to | false -| `currency` | optional | String | 3 letters currency | "EUR" # Test Parameters diff --git a/modules/rtbhouseBidAdapter.js b/modules/rtbhouseBidAdapter.js index 5f94ec3dea4..5d687552201 100644 --- a/modules/rtbhouseBidAdapter.js +++ b/modules/rtbhouseBidAdapter.js @@ -10,9 +10,6 @@ const BIDDER_CODE = 'rtbhouse'; const REGIONS = ['prebid-eu', 'prebid-us', 'prebid-asia']; const ENDPOINT_URL = 'creativecdn.com/bidder/prebid/bids'; const FLEDGE_ENDPOINT_URL = 'creativecdn.com/bidder/prebidfledge/bids'; -const FLEDGE_SELLER_URL = 'https://fledge-ssp.creativecdn.com'; -const FLEDGE_DECISION_LOGIC_URL = 'https://fledge-ssp.creativecdn.com/component-seller-prebid.js'; - const DEFAULT_CURRENCY_ARR = ['USD']; // NOTE - USD is the only supported currency right now; Hardcoded for bids const SUPPORTED_MEDIA_TYPES = [BANNER, NATIVE]; const TTL = 55; @@ -97,12 +94,8 @@ export const spec = { let computedEndpointUrl = ENDPOINT_URL; - if (bidderRequest.fledgeEnabled) { - const fledgeConfig = config.getConfig('fledgeConfig') || { - seller: FLEDGE_SELLER_URL, - decisionLogicUrl: FLEDGE_DECISION_LOGIC_URL, - sellerTimeout: 500 - }; + const fledgeConfig = config.getConfig('fledgeConfig'); + if (bidderRequest.fledgeEnabled && fledgeConfig) { mergeDeep(request, { ext: { fledge_config: fledgeConfig } }); computedEndpointUrl = FLEDGE_ENDPOINT_URL; } @@ -195,7 +188,7 @@ registerBidder(spec); /** * @param {object} slot Ad Unit Params by Prebid - * @returns {number} floor by imp type + * @returns {int} floor by imp type */ function applyFloor(slot) { const floors = []; @@ -350,7 +343,7 @@ function mapNative(slot) { /** * @param {object} slot Slot config by Prebid - * @returns {Array} Request Assets by OpenRTB Native Ads 1.1 §4.2 + * @returns {array} Request Assets by OpenRTB Native Ads 1.1 §4.2 */ function mapNativeAssets(slot) { const params = slot.nativeParams || deepAccess(slot, 'mediaTypes.native'); @@ -413,7 +406,7 @@ function mapNativeAssets(slot) { /** * @param {object} image Prebid native.image/icon - * @param {number} type Image or icon code + * @param {int} type Image or icon code * @returns {object} Request Image by OpenRTB Native Ads 1.1 §4.4 */ function mapNativeImage(image, type) { diff --git a/modules/rtbhouseBidAdapter.md b/modules/rtbhouseBidAdapter.md index 338ba6b4df4..31e9377cb96 100644 --- a/modules/rtbhouseBidAdapter.md +++ b/modules/rtbhouseBidAdapter.md @@ -81,30 +81,11 @@ The following steps should be taken to setup Protected Audience for RTB House: 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.setBidderConfig({ - bidders: ["rtbhouse"], - config: { - fledgeEnabled: 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.setBidderConfig({ - bidders: ["rtbhouse"], - config: { - fledgeEnabled: 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. +```javascript +pbjs.setBidderConfig({ + bidders: ["rtbhouse"], + config: { + fledgeEnabled: true + } +}); +``` diff --git a/modules/rtbsapeBidAdapter.js b/modules/rtbsapeBidAdapter.js index 502b62c8799..5b1a92b02a0 100644 --- a/modules/rtbsapeBidAdapter.js +++ b/modules/rtbsapeBidAdapter.js @@ -4,14 +4,6 @@ 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 - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse - * @typedef {import('../src/adapters/bidderFactory.js').SyncOptions} SyncOptions - * @typedef {import('../src/adapters/bidderFactory.js').UserSync} UserSync - */ - const BIDDER_CODE = 'rtbsape'; const ENDPOINT = 'https://ssp-rtb.sape.ru/prebid'; const RENDERER_SRC = 'https://cdn-rtb.sape.ru/js/player.js'; diff --git a/modules/rtdModule/index.js b/modules/rtdModule/index.js index 8968d4c795a..633c4f4cdc1 100644 --- a/modules/rtdModule/index.js +++ b/modules/rtdModule/index.js @@ -215,8 +215,7 @@ const setEventsListeners = (function () { [CONSTANTS.EVENTS.AUCTION_INIT]: ['onAuctionInitEvent'], [CONSTANTS.EVENTS.AUCTION_END]: ['onAuctionEndEvent', getAdUnitTargeting], [CONSTANTS.EVENTS.BID_RESPONSE]: ['onBidResponseEvent'], - [CONSTANTS.EVENTS.BID_REQUESTED]: ['onBidRequestEvent'], - [CONSTANTS.EVENTS.BID_ACCEPTED]: ['onBidAcceptedEvent'] + [CONSTANTS.EVENTS.BID_REQUESTED]: ['onBidRequestEvent'] }).forEach(([ev, [handler, preprocess]]) => { events.on(ev, (args) => { preprocess && preprocess(args); @@ -386,7 +385,7 @@ export function getAdUnitTargeting(auction) { /** * deep merge array of objects - * @param {Array} arr - objects array + * @param {array} arr - objects array * @return {Object} merged object */ export function deepMerge(arr) { diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index daaf9a14b9f..5fad246f27d 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -7,6 +7,7 @@ import { find } from '../src/polyfill.js'; import { getGlobal } from '../src/prebidGlobal.js'; import { Renderer } from '../src/Renderer.js'; import { + convertTypes, deepAccess, deepSetValue, formatQS, @@ -17,22 +18,16 @@ import { logMessage, logWarn, mergeDeep, - parseSizesInput, - pick, - _each + parseSizesInput, _each } from '../src/utils.js'; import {getAllOrtbKeywords} from '../libraries/keywords/keywords.js'; -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - */ - 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') || {}; +let rubiConf = {}; // we are saving these as global to this module so that if a pub accidentally overwrites the entire // rubicon object, then we do not lose other data config.getConfig('rubicon', config => { @@ -129,7 +124,6 @@ var sizeMap = { 278: '320x500', 282: '320x400', 288: '640x380', - 484: '720x1280', 524: '1x2', 548: '500x1000', 550: '980x480', @@ -145,9 +139,7 @@ var sizeMap = { 576: '610x877', 578: '980x552', 580: '505x656', - 622: '192x160', - 632: '1200x450', - 634: '340x450' + 622: '192x160' }; _each(sizeMap, (item, key) => sizeMap[item] = key); @@ -204,14 +196,10 @@ export const converter = ortbConverter({ if (config.getConfig('s2sConfig.defaultTtl')) { imp.exp = config.getConfig('s2sConfig.defaultTtl'); }; - bidRequest.params.position === 'atf' && imp.video && (imp.video.pos = 1); - bidRequest.params.position === 'btf' && imp.video && (imp.video.pos = 3); + bidRequest.params.position === 'atf' && (imp.video.pos = 1); + bidRequest.params.position === 'btf' && (imp.video.pos = 3); delete imp.ext?.prebid?.storedrequest; - if (bidRequest.params.bidonmultiformat === true && bidRequestType.length > 1) { - deepSetValue(imp, 'ext.prebid.bidder.rubicon.formats', bidRequestType); - } - setBidFloors(bidRequest, imp); return imp; @@ -330,7 +318,7 @@ export const spec = { ) ); }); - if (rubiConf.singleRequest !== true) { + if (config.getConfig('rubicon.singleRequest') !== true) { // bids are not grouped if single request mode is not enabled requests = filteredHttpRequest.concat(bannerBidRequests.map(bidRequest => { const bidParams = spec.createSlotParams(bidRequest, bidderRequest); @@ -395,8 +383,6 @@ export const spec = { 'gdpr', 'gdpr_consent', 'us_privacy', - 'gpp', - 'gpp_sid', 'rp_schain', ].concat(Object.keys(params).filter(item => containsUId.test(item))) .concat([ @@ -413,8 +399,6 @@ export const spec = { 'x_source.tid', 'l_pb_bid_id', 'p_screen_res', - 'o_ae', - 'o_cdep', 'rp_floor', 'rp_secure', 'tk_user_key' @@ -488,7 +472,6 @@ export const spec = { 'x_source.tid': bidderRequest.ortb2?.source?.tid, 'x_imp.ext.tid': bidRequest.ortb2Imp?.ext?.tid, 'l_pb_bid_id': bidRequest.bidId, - 'o_cdep': bidRequest.ortb2?.device?.ext?.cdep, 'p_screen_res': _getScreenResolution(), 'tk_user_key': params.userId, 'p_geo.latitude': isNaN(parseFloat(latitude)) ? undefined : parseFloat(latitude).toFixed(4), @@ -512,11 +495,6 @@ export const spec = { data['rp_hard_floor'] = typeof floorInfo === 'object' && floorInfo.currency === 'USD' && !isNaN(parseInt(floorInfo.floor)) ? floorInfo.floor : undefined; } - // Send multiformat data if requested - if (params.bidonmultiformat === true && deepAccess(bidRequest, 'mediaTypes') && Object.keys(bidRequest.mediaTypes).length > 1) { - data['p_formats'] = Object.keys(bidRequest.mediaTypes).join(','); - } - // 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 let posMapping = {1: 'atf', 3: 'btf'}; @@ -528,10 +506,6 @@ export const spec = { if (configUserId) { data['ppuid'] = configUserId; } - - if (bidRequest?.ortb2Imp?.ext?.ae) { - data['o_ae'] = 1; - } // loop through userIds and add to request if (bidRequest.userIdAsEids) { bidRequest.userIdAsEids.forEach(eid => { @@ -552,9 +526,7 @@ export const spec = { data['eid_id5-sync.com'] = `${eid.uids[0].id}^${eid.uids[0].atype}^${(eid.uids[0].ext && eid.uids[0].ext.linkType) || ''}`; } else { // add anything else with this generic format - // if rubicon drop ^ - const id = eid.source === 'rubiconproject.com' ? eid.uids[0].id : `${eid.uids[0].id}^${eid.uids[0].atype || ''}` - data[`eid_${eid.source}`] = id; + data[`eid_${eid.source}`] = `${eid.uids[0].id}^${eid.uids[0].atype || ''}`; } // send AE "ppuid" signal if exists, and hasn't already been sent if (!data['ppuid']) { @@ -582,11 +554,6 @@ export const spec = { data['us_privacy'] = encodeURIComponent(bidderRequest.uspConsent); } - if (bidderRequest.gppConsent?.gppString) { - data['gpp'] = bidderRequest.gppConsent.gppString; - data['gpp_sid'] = bidderRequest.gppConsent?.applicableSections?.toString(); - } - data['rp_maxbids'] = bidderRequest.bidLimit || 1; applyFPD(bidRequest, BANNER, data); @@ -631,7 +598,7 @@ export const spec = { * @param {*} responseObj * @param {BidRequest|Object.} request - if request was SRA the bidRequest argument will be a keyed BidRequest array object, * non-SRA responses return a plain BidRequest object - * @return {{fledgeAuctionConfigs: *, bids: *}} An array of bids which + * @return {Bid[]} An array of bids which */ interpretResponse: function (responseObj, request) { responseObj = responseObj.body; @@ -641,6 +608,7 @@ export const spec = { if (!responseObj || typeof responseObj !== 'object') { return []; } + // Response from PBS Java openRTB if (responseObj.seatbid) { const responseErrors = deepAccess(responseObj, 'ext.errors.rubicon'); @@ -666,7 +634,7 @@ export const spec = { return []; } - let bids = ads.reduce((bids, ad, i) => { + return ads.reduce((bids, ad, i) => { (ad.impression_id && lastImpId === ad.impression_id) ? multibid++ : lastImpId = ad.impression_id; if (ad.status !== 'ok') { @@ -727,18 +695,8 @@ export const spec = { }, []).sort((adA, adB) => { return (adB.cpm || 0.0) - (adA.cpm || 0.0); }); - - let fledgeAuctionConfigs = responseObj.component_auction_config?.map(config => { - return { config, bidId: config.bidId } - }); - - if (fledgeAuctionConfigs) { - return { bids, fledgeAuctionConfigs }; - } else { - return bids; - } }, - getUserSyncs: function (syncOptions, responses, gdprConsent, uspConsent, gppConsent) { + getUserSyncs: function (syncOptions, responses, gdprConsent, uspConsent) { if (!hasSynced && syncOptions.iframeEnabled) { // data is only assigned if params are available to pass to syncEndpoint let params = {}; @@ -756,11 +714,6 @@ export const spec = { params['us_privacy'] = encodeURIComponent(uspConsent); } - if (gppConsent?.gppString) { - params['gpp'] = gppConsent.gppString; - params['gpp_sid'] = gppConsent.applicableSections?.toString(); - } - params = Object.keys(params).length ? `?${formatQS(params)}` : ''; hasSynced = true; @@ -769,6 +722,19 @@ export const spec = { url: `https://${rubiConf.syncHost || 'eus'}.rubiconproject.com/usync.html` + params }; } + }, + /** + * Covert bid param types for S2S + * @param {Object} params bid params + * @param {Boolean} isOpenRtb boolean to check openrtb2 protocol + * @return {Object} params bid params + */ + transformBidParams: function(params, isOpenRtb) { + return convertTypes({ + 'accountId': 'number', + 'siteId': 'number', + 'zoneId': 'number' + }, params); } }; @@ -965,33 +931,6 @@ function applyFPD(bidRequest, mediaType, data) { if (data['tg_i.pbadslot']) { delete data['tg_i.dfp_ad_unit_code']; } - - // High Entropy stuff -> sua object is the ORTB standard (default to pass unless specifically disabled) - const clientHints = deepAccess(fpd, 'device.sua'); - if (clientHints && rubiConf.chEnabled !== false) { - // pick out client hints we want to send (any that are undefined or empty will NOT be sent) - pick(clientHints, [ - 'architecture', arch => data.m_ch_arch = arch, - 'bitness', bitness => data.m_ch_bitness = bitness, - 'browsers', browsers => { - if (!Array.isArray(browsers)) return; - // reduce down into ua and full version list attributes - const [ua, fullVer] = browsers.reduce((accum, browserData) => { - accum[0].push(`"${browserData?.brand}"|v="${browserData?.version?.[0]}"`); - accum[1].push(`"${browserData?.brand}"|v="${browserData?.version?.join?.('.')}"`); - return accum; - }, [[], []]); - data.m_ch_ua = ua?.join?.(','); - data.m_ch_full_ver = fullVer?.join?.(','); - }, - 'mobile', isMobile => data.m_ch_mobile = `?${isMobile}`, - 'model', model => data.m_ch_model = model, - 'platform', platform => { - data.m_ch_platform = platform?.brand; - data.m_ch_platform_ver = platform?.version?.join?.('.'); - } - ]) - } } else { if (Object.keys(impExt).length) { mergeDeep(data.imp[0].ext, impExt); @@ -1173,7 +1112,8 @@ export function hasValidVideoParams(bid) { var requiredParams = { mimes: arrayType, protocols: arrayType, - linearity: numberType + linearity: numberType, + api: arrayType } // loop through each param and verify it has the correct Object.keys(requiredParams).forEach(function(param) { @@ -1188,7 +1128,7 @@ export function hasValidVideoParams(bid) { /** * Make sure the required params are present * @param {Object} schain - * @param {boolean} + * @param {Bool} */ export function hasValidSupplyChainParams(schain) { let isValid = false; diff --git a/modules/schain.js b/modules/schain.js index 726679b133f..2991bb5b3d5 100644 --- a/modules/schain.js +++ b/modules/schain.js @@ -1,17 +1,16 @@ -import {config} from '../src/config.js'; +import { config } from '../src/config.js'; import adapterManager from '../src/adapterManager.js'; import { - _each, - deepAccess, - deepClone, - deepSetValue, - isArray, - isInteger, isNumber, - isPlainObject, isStr, + isArray, + isPlainObject, + hasOwn, logError, - logWarn + isInteger, + _each, + logWarn, + deepAccess, deepSetValue, deepClone } from '../src/utils.js'; import {registerOrtbProcessor, REQUEST} from '../src/pbjsORTB.js'; @@ -64,7 +63,7 @@ export function isSchainObjectValid(schainObject, returnOnError) { } // ext: Object [optional] - if (schainObject.hasOwnProperty('ext')) { + if (hasOwn(schainObject, 'ext')) { if (!isPlainObject(schainObject.ext)) { appendFailMsg(`schain.config.ext` + shouldBeAnObject); } @@ -93,28 +92,28 @@ export function isSchainObjectValid(schainObject, returnOnError) { } // rid: String [Optional] - if (node.hasOwnProperty('rid')) { + if (hasOwn(node, 'rid')) { if (!isStr(node.rid)) { appendFailMsg(`schain.config.nodes[${index}].rid` + shouldBeAString); } } // name: String [Optional] - if (node.hasOwnProperty('name')) { + if (hasOwn(node, 'name')) { if (!isStr(node.name)) { appendFailMsg(`schain.config.nodes[${index}].name` + shouldBeAString); } } // domain: String [Optional] - if (node.hasOwnProperty('domain')) { + if (hasOwn(node, 'domain')) { if (!isStr(node.domain)) { appendFailMsg(`schain.config.nodes[${index}].domain` + shouldBeAString); } } // ext: Object [Optional] - if (node.hasOwnProperty('ext')) { + if (hasOwn(node, 'ext')) { if (!isPlainObject(node.ext)) { appendFailMsg(`schain.config.nodes[${index}].ext` + shouldBeAnObject); } diff --git a/modules/seedtagBidAdapter.js b/modules/seedtagBidAdapter.js index 6f36c8a191e..7ac7d048c50 100644 --- a/modules/seedtagBidAdapter.js +++ b/modules/seedtagBidAdapter.js @@ -3,15 +3,6 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { VIDEO, BANNER } from '../src/mediaTypes.js'; import { config } from '../src/config.js'; -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse - * @typedef {import('../src/adapters/bidderFactory.js').SyncOptions} SyncOptions - * @typedef {import('../src/adapters/bidderFactory.js').UserSync} UserSync - * @typedef {import('../src/adapters/bidderFactory.js').validBidRequests} validBidRequests - */ - const BIDDER_CODE = 'seedtag'; const SEEDTAG_ALIAS = 'st'; const SEEDTAG_SSP_ENDPOINT = 'https://s.seedtag.com/c/hb/bid'; @@ -116,6 +107,7 @@ function buildBidRequest(validBidRequest) { return mediaTypesMap[pbjsType]; } ); + const bidRequest = { id: validBidRequest.bidId, transactionId: validBidRequest.ortb2Imp?.ext?.tid, @@ -123,7 +115,6 @@ function buildBidRequest(validBidRequest) { supplyTypes: mediaTypes, adUnitId: params.adUnitId, adUnitCode: validBidRequest.adUnitCode, - geom: geom(validBidRequest.adUnitCode), placement: params.placement, requestCount: validBidRequest.bidderRequestsCount || 1, // FIXME : in unit test the parameter bidderRequestsCount is undefined }; @@ -207,27 +198,6 @@ function ttfb() { return ttfb >= 0 && ttfb <= performance.now() ? ttfb : 0; } -function geom(adunitCode) { - const slot = document.getElementById(adunitCode); - if (slot) { - const scrollY = window.scrollY; - const { top, left, width, height } = slot.getBoundingClientRect(); - const viewport = { - width: window.innerWidth, - height: window.innerHeight, - }; - - return { - scrollY, - top, - left, - width, - height, - viewport, - }; - } -} - export function getTimeoutUrl(data) { let queryParams = ''; if ( diff --git a/modules/sharedIdSystem.js b/modules/sharedIdSystem.js index fa8b5e3bfdb..ef56e10870b 100644 --- a/modules/sharedIdSystem.js +++ b/modules/sharedIdSystem.js @@ -13,15 +13,7 @@ import {VENDORLESS_GVLID} from '../src/consentHandler.js'; import {MODULE_TYPE_UID} from '../src/activities/modules.js'; import {domainOverrideToRootDomain} from '../libraries/domainOverrideToRootDomain/index.js'; -/** - * @typedef {import('../modules/userId/index.js').Submodule} Submodule - * @typedef {import('../modules/userId/index.js').SubmoduleConfig} SubmoduleConfig - * @typedef {import('../modules/userId/index.js').SubmoduleParams} SubmoduleParams - * @typedef {import('../modules/userId/index.js').ConsentData} ConsentData - * @typedef {import('../modules/userId/index.js').IdResponse} IdResponse - */ - -export const storage = getStorageManager({moduleType: MODULE_TYPE_UID, moduleName: 'sharedId'}); +export const storage = getStorageManager({moduleType: MODULE_TYPE_UID, moduleName: 'pubCommonId'}); const COOKIE = 'cookie'; const LOCAL_STORAGE = 'html5'; const OPTOUT_NAME = '_pubcid_optout'; @@ -182,12 +174,6 @@ export const sharedIdSystemSubmodule = { }, domainOverride: domainOverrideToRootDomain(storage, 'sharedId'), - eids: { - 'pubcid': { - source: 'pubcid.org', - atype: 1 - }, - } }; submodule('userId', sharedIdSystemSubmodule); diff --git a/modules/sharethroughAnalyticsAdapter.js b/modules/sharethroughAnalyticsAdapter.js index dc621e8da92..6502c7e3a53 100644 --- a/modules/sharethroughAnalyticsAdapter.js +++ b/modules/sharethroughAnalyticsAdapter.js @@ -1,6 +1,6 @@ +import { tryAppendQueryString } from '../src/utils.js'; import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; -import {tryAppendQueryString} from '../libraries/urlUtils/urlUtils.js'; const emptyUrl = ''; const analyticsType = 'endpoint'; diff --git a/modules/sharethroughBidAdapter.js b/modules/sharethroughBidAdapter.js index 53cb67c4e6d..d3f4b456aeb 100644 --- a/modules/sharethroughBidAdapter.js +++ b/modules/sharethroughBidAdapter.js @@ -1,7 +1,7 @@ +import { deepAccess, generateUUID, inIframe } 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 { deepAccess, generateUUID, inIframe } from '../src/utils.js'; const VERSION = '4.3.0'; const BIDDER_CODE = 'sharethrough'; @@ -18,14 +18,14 @@ export const sharethroughAdapterSpec = { code: BIDDER_CODE, supportedMediaTypes: [VIDEO, BANNER], gvlid: 80, - isBidRequestValid: (bid) => !!bid.params.pkey && bid.bidder === BIDDER_CODE, + isBidRequestValid: bid => !!bid.params.pkey && bid.bidder === BIDDER_CODE, buildRequests: (bidRequests, bidderRequest) => { const timeout = bidderRequest.timeout; const firstPartyData = bidderRequest.ortb2 || {}; const nonHttp = sharethroughInternal.getProtocol().indexOf('http') < 0; - const secure = nonHttp || sharethroughInternal.getProtocol().indexOf('https') > -1; + const secure = nonHttp || (sharethroughInternal.getProtocol().indexOf('https') > -1); const req = { id: generateUUID(), @@ -45,7 +45,6 @@ export const sharethroughAdapterSpec = { dnt: navigator.doNotTrack === '1' ? 1 : 0, h: window.screen.height, w: window.screen.width, - ext: {}, }, regs: { coppa: config.getConfig('coppa') === true ? 1 : 0, @@ -64,10 +63,6 @@ export const sharethroughAdapterSpec = { test: 0, }; - if (bidderRequest.ortb2?.device?.ext?.cdep) { - req.device.ext['cdep'] = bidderRequest.ortb2.device.ext.cdep; - } - req.user = nullish(firstPartyData.user, {}); if (!req.user.ext) req.user.ext = {}; req.user.ext.eids = bidRequests[0].userIdAsEids || []; @@ -84,79 +79,64 @@ export const sharethroughAdapterSpec = { req.regs.ext.us_privacy = bidderRequest.uspConsent; } - if (bidderRequest?.gppConsent?.gppString) { - req.regs.gpp = bidderRequest.gppConsent.gppString; - req.regs.gpp_sid = bidderRequest.gppConsent.applicableSections; - } else if (bidderRequest?.ortb2?.regs?.gpp) { - req.regs.ext.gpp = bidderRequest.ortb2.regs.gpp; - req.regs.ext.gpp_sid = bidderRequest.ortb2.regs.gpp_sid; - } + const imps = bidRequests.map(bidReq => { + const impression = { ext: {} }; + + // mergeDeep(impression, bidReq.ortb2Imp); // leaving this out for now as we may want to leave stuff out on purpose + const tid = deepAccess(bidReq, 'ortb2Imp.ext.tid'); + if (tid) impression.ext.tid = tid; + const gpid = deepAccess(bidReq, 'ortb2Imp.ext.gpid', deepAccess(bidReq, 'ortb2Imp.ext.data.pbadslot')); + if (gpid) impression.ext.gpid = gpid; - const imps = bidRequests - .map((bidReq) => { - const impression = { ext: {} }; - - // mergeDeep(impression, bidReq.ortb2Imp); // leaving this out for now as we may want to leave stuff out on purpose - const tid = deepAccess(bidReq, 'ortb2Imp.ext.tid'); - if (tid) impression.ext.tid = tid; - const gpid = deepAccess(bidReq, 'ortb2Imp.ext.gpid', deepAccess(bidReq, 'ortb2Imp.ext.data.pbadslot')); - if (gpid) impression.ext.gpid = gpid; - - const videoRequest = deepAccess(bidReq, 'mediaTypes.video'); - - if (videoRequest) { - // default playerSize, only change this if we know width and height are properly defined in the request - let [w, h] = [640, 360]; - if ( - videoRequest.playerSize && - videoRequest.playerSize[0] && - videoRequest.playerSize[0][0] && - videoRequest.playerSize[0][1] - ) { - [w, h] = videoRequest.playerSize[0]; - } - - impression.video = { - pos: nullish(videoRequest.pos, 0), - topframe: inIframe() ? 0 : 1, - skip: nullish(videoRequest.skip, 0), - linearity: nullish(videoRequest.linearity, 1), - minduration: nullish(videoRequest.minduration, 5), - maxduration: nullish(videoRequest.maxduration, 60), - playbackmethod: videoRequest.playbackmethod || [2], - api: getVideoApi(videoRequest), - mimes: videoRequest.mimes || ['video/mp4'], - protocols: getVideoProtocols(videoRequest), - w, - h, - startdelay: nullish(videoRequest.startdelay, 0), - skipmin: nullish(videoRequest.skipmin, 0), - skipafter: nullish(videoRequest.skipafter, 0), - placement: videoRequest.context === 'instream' ? 1 : +deepAccess(videoRequest, 'placement', 4), - }; - - if (videoRequest.delivery) impression.video.delivery = videoRequest.delivery; - if (videoRequest.companiontype) impression.video.companiontype = videoRequest.companiontype; - if (videoRequest.companionad) impression.video.companionad = videoRequest.companionad; - } else { - impression.banner = { - pos: deepAccess(bidReq, 'mediaTypes.banner.pos', 0), - topframe: inIframe() ? 0 : 1, - format: bidReq.sizes.map((size) => ({ w: +size[0], h: +size[1] })), - }; + const videoRequest = deepAccess(bidReq, 'mediaTypes.video'); + + if (videoRequest) { + // default playerSize, only change this if we know width and height are properly defined in the request + let [w, h] = [640, 360]; + if (videoRequest.playerSize && videoRequest.playerSize[0] && videoRequest.playerSize[0][0] && videoRequest.playerSize[0][1]) { + [w, h] = videoRequest.playerSize[0]; } - return { - id: bidReq.bidId, - tagid: String(bidReq.params.pkey), - secure: secure ? 1 : 0, - bidfloor: getBidRequestFloor(bidReq), - ...impression, + impression.video = { + pos: nullish(videoRequest.pos, 0), + topframe: inIframe() ? 0 : 1, + skip: nullish(videoRequest.skip, 0), + linearity: nullish(videoRequest.linearity, 1), + minduration: nullish(videoRequest.minduration, 5), + maxduration: nullish(videoRequest.maxduration, 60), + playbackmethod: videoRequest.playbackmethod || [2], + api: getVideoApi(videoRequest), + mimes: videoRequest.mimes || ['video/mp4'], + protocols: getVideoProtocols(videoRequest), + w, + h, + startdelay: nullish(videoRequest.startdelay, 0), + skipmin: nullish(videoRequest.skipmin, 0), + skipafter: nullish(videoRequest.skipafter, 0), + placement: videoRequest.context === 'instream' ? 1 : +deepAccess(videoRequest, 'placement', 4), }; - }) - .filter((imp) => !!imp); - return imps.map((impression) => { + if (videoRequest.delivery) impression.video.delivery = videoRequest.delivery; + if (videoRequest.companiontype) impression.video.companiontype = videoRequest.companiontype; + if (videoRequest.companionad) impression.video.companionad = videoRequest.companionad; + } else { + impression.banner = { + pos: deepAccess(bidReq, 'mediaTypes.banner.pos', 0), + topframe: inIframe() ? 0 : 1, + format: bidReq.sizes.map(size => ({ w: +size[0], h: +size[1] })), + }; + } + + return { + id: bidReq.bidId, + tagid: String(bidReq.params.pkey), + secure: secure ? 1 : 0, + bidfloor: getBidRequestFloor(bidReq), + ...impression, + }; + }).filter(imp => !!imp); + + return imps.map(impression => { return { method: 'POST', url: STR_ENDPOINT, @@ -169,17 +149,11 @@ export const sharethroughAdapterSpec = { }, interpretResponse: ({ body }, req) => { - if ( - !body || - !body.seatbid || - body.seatbid.length === 0 || - !body.seatbid[0].bid || - body.seatbid[0].bid.length === 0 - ) { + if (!body || !body.seatbid || body.seatbid.length === 0 || !body.seatbid[0].bid || body.seatbid[0].bid.length === 0) { return []; } - return body.seatbid[0].bid.map((bid) => { + return body.seatbid[0].bid.map(bid => { // Spec: https://docs.prebid.org/dev-docs/bidder-adaptor.html#interpreting-the-response const response = { requestId: bid.impid, @@ -222,20 +196,24 @@ export const sharethroughAdapterSpec = { }, getUserSyncs: (syncOptions, serverResponses) => { - const shouldCookieSync = - syncOptions.pixelEnabled && deepAccess(serverResponses, '0.body.cookieSyncUrls') !== undefined; + const shouldCookieSync = syncOptions.pixelEnabled && deepAccess(serverResponses, '0.body.cookieSyncUrls') !== undefined; - return shouldCookieSync ? serverResponses[0].body.cookieSyncUrls.map((url) => ({ type: 'image', url: url })) : []; + return shouldCookieSync + ? serverResponses[0].body.cookieSyncUrls.map(url => ({ type: 'image', url: url })) + : []; }, // Empty implementation for prebid core to be able to find it - onTimeout: (data) => {}, + onTimeout: (data) => { + }, // Empty implementation for prebid core to be able to find it - onBidWon: (bid) => {}, + onBidWon: (bid) => { + }, // Empty implementation for prebid core to be able to find it - onSetTargeting: (bid) => {}, + onSetTargeting: (bid) => { + }, }; function getVideoApi({ api }) { @@ -262,7 +240,7 @@ function getBidRequestFloor(bid) { const floorInfo = bid.getFloor({ currency: 'USD', mediaType: bid.mediaTypes && bid.mediaTypes.video ? 'video' : 'banner', - size: bid.sizes.map((size) => ({ w: size[0], h: size[1] })), + size: bid.sizes.map(size => ({ w: size[0], h: size[1] })), }); if (typeof floorInfo === 'object' && floorInfo.currency === 'USD' && !isNaN(parseFloat(floorInfo.floor))) { floor = parseFloat(floorInfo.floor); diff --git a/modules/shinezBidAdapter.js b/modules/shinezBidAdapter.js index 47fca317de2..96b6d281fdc 100644 --- a/modules/shinezBidAdapter.js +++ b/modules/shinezBidAdapter.js @@ -1,16 +1,4 @@ -import { - logWarn, - logInfo, - isArray, - isFn, - deepAccess, - isEmpty, - contains, - timestamp, - triggerPixel, - isInteger, - getBidIdParameter -} from '../src/utils.js'; +import { logWarn, logInfo, isArray, isFn, deepAccess, isEmpty, contains, timestamp, getBidIdParameter, triggerPixel, isInteger } from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {BANNER, VIDEO} from '../src/mediaTypes.js'; import {config} from '../src/config.js'; diff --git a/modules/shinezRtbBidAdapter.js b/modules/shinezRtbBidAdapter.js deleted file mode 100644 index d1d9f36a569..00000000000 --- a/modules/shinezRtbBidAdapter.js +++ /dev/null @@ -1,336 +0,0 @@ -import {_each, deepAccess, parseSizesInput, parseUrl, uniques, isFn} from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER, VIDEO} from '../src/mediaTypes.js'; -import {getStorageManager} from '../src/storageManager.js'; -import {config} from '../src/config.js'; - -const DEFAULT_SUB_DOMAIN = 'exchange'; -const BIDDER_CODE = 'shinezRtb'; -const BIDDER_VERSION = '1.0.0'; -const CURRENCY = 'USD'; -const TTL_SECONDS = 60 * 5; -const UNIQUE_DEAL_ID_EXPIRY = 1000 * 60 * 15; -const storage = getStorageManager({bidderCode: BIDDER_CODE}); - -function getTopWindowQueryParams() { - try { - const parsedUrl = parseUrl(window.top.document.URL, {decodeSearchAsString: true}); - return parsedUrl.search; - } catch (e) { - return ''; - } -} - -export function createDomain(subDomain = DEFAULT_SUB_DOMAIN) { - return `https://${subDomain}.sweetgum.io`; -} - -export function extractCID(params) { - return params.cId || params.CID || params.cID || params.CId || params.cid || params.ciD || params.Cid || params.CiD; -} - -export function extractPID(params) { - return params.pId || params.PID || params.pID || params.PId || params.pid || params.piD || params.Pid || params.PiD; -} - -export function extractSubDomain(params) { - return params.subDomain || params.SubDomain || params.Subdomain || params.subdomain || params.SUBDOMAIN || params.subDOMAIN; -} - -function isBidRequestValid(bid) { - const params = bid.params || {}; - return !!(extractCID(params) && extractPID(params)); -} - -function buildRequest(bid, topWindowUrl, sizes, bidderRequest, bidderTimeout) { - const { - params, - bidId, - userId, - adUnitCode, - schain, - mediaTypes, - ortb2Imp, - bidderRequestId, - bidRequestsCount, - bidderRequestsCount, - bidderWinsCount - } = bid; - let {bidFloor, ext} = params; - const hashUrl = hashCode(topWindowUrl); - const uniqueDealId = getUniqueDealId(hashUrl); - const cId = extractCID(params); - const pId = extractPID(params); - const subDomain = extractSubDomain(params); - - const gpid = deepAccess(bid, 'ortb2Imp.ext.gpid', deepAccess(bid, 'ortb2Imp.ext.data.pbadslot', '')); - - if (isFn(bid.getFloor)) { - const floorInfo = bid.getFloor({ - currency: 'USD', - mediaType: '*', - size: '*' - }); - - if (floorInfo.currency === 'USD') { - bidFloor = floorInfo.floor; - } - } - - let data = { - url: encodeURIComponent(topWindowUrl), - uqs: getTopWindowQueryParams(), - cb: Date.now(), - bidFloor: bidFloor, - bidId: bidId, - referrer: bidderRequest.refererInfo.ref, - adUnitCode: adUnitCode, - publisherId: pId, - sizes: sizes, - uniqueDealId: uniqueDealId, - bidderVersion: BIDDER_VERSION, - prebidVersion: '$prebid.version$', - res: `${screen.width}x${screen.height}`, - schain: schain, - mediaTypes: mediaTypes, - gpid: gpid, - transactionId: ortb2Imp?.ext?.tid, - bidderRequestId: bidderRequestId, - bidRequestsCount: bidRequestsCount, - bidderRequestsCount: bidderRequestsCount, - bidderWinsCount: bidderWinsCount, - bidderTimeout: bidderTimeout - }; - - appendUserIdsToRequestPayload(data, userId); - - const sua = deepAccess(bidderRequest, 'ortb2.device.sua'); - - if (sua) { - data.sua = sua; - } - - if (bidderRequest.gdprConsent) { - if (bidderRequest.gdprConsent.consentString) { - data.gdprConsent = bidderRequest.gdprConsent.consentString; - } - if (bidderRequest.gdprConsent.gdprApplies !== undefined) { - data.gdpr = bidderRequest.gdprConsent.gdprApplies ? 1 : 0; - } - } - if (bidderRequest.uspConsent) { - data.usPrivacy = bidderRequest.uspConsent; - } - - if (bidderRequest.gppConsent) { - data.gppString = bidderRequest.gppConsent.gppString; - data.gppSid = bidderRequest.gppConsent.applicableSections; - } else if (bidderRequest.ortb2?.regs?.gpp) { - data.gppString = bidderRequest.ortb2.regs.gpp; - data.gppSid = bidderRequest.ortb2.regs.gpp_sid; - } - - const dto = { - method: 'POST', - url: `${createDomain(subDomain)}/prebid/multi/${cId}`, - data: data - }; - - _each(ext, (value, key) => { - dto.data['ext.' + key] = value; - }); - - return dto; -} - -function appendUserIdsToRequestPayload(payloadRef, userIds) { - let key; - _each(userIds, (userId, idSystemProviderName) => { - key = `uid.${idSystemProviderName}`; - - switch (idSystemProviderName) { - case 'digitrustid': - payloadRef[key] = deepAccess(userId, 'data.id'); - break; - case 'lipb': - payloadRef[key] = userId.lipbid; - break; - case 'parrableId': - payloadRef[key] = userId.eid; - break; - case 'id5id': - payloadRef[key] = userId.uid; - break; - default: - payloadRef[key] = userId; - } - }); -} - -function buildRequests(validBidRequests, bidderRequest) { - const topWindowUrl = bidderRequest.refererInfo.page || bidderRequest.refererInfo.topmostLocation; - const bidderTimeout = config.getConfig('bidderTimeout'); - const requests = []; - validBidRequests.forEach(validBidRequest => { - const sizes = parseSizesInput(validBidRequest.sizes); - const request = buildRequest(validBidRequest, topWindowUrl, sizes, bidderRequest, bidderTimeout); - requests.push(request); - }); - return requests; -} - -function interpretResponse(serverResponse, request) { - if (!serverResponse || !serverResponse.body) { - return []; - } - const {bidId} = request.data; - const {results} = serverResponse.body; - - let output = []; - - try { - results.forEach(result => { - const { - creativeId, - ad, - price, - exp, - width, - height, - currency, - metaData, - advertiserDomains, - mediaType = BANNER - } = result; - if (!ad || !price) { - return; - } - - const response = { - requestId: bidId, - cpm: price, - width: width, - height: height, - creativeId: creativeId, - currency: currency || CURRENCY, - netRevenue: true, - ttl: exp || TTL_SECONDS, - }; - - if (metaData) { - Object.assign(response, { - meta: metaData - }) - } else { - Object.assign(response, { - meta: { - advertiserDomains: advertiserDomains || [] - } - }) - } - - if (mediaType === BANNER) { - Object.assign(response, { - ad: ad, - }); - } else { - Object.assign(response, { - vastXml: ad, - mediaType: VIDEO - }); - } - output.push(response); - }); - return output; - } catch (e) { - return []; - } -} - -function getUserSyncs(syncOptions, responses, gdprConsent = {}, uspConsent = '') { - let syncs = []; - const {iframeEnabled, pixelEnabled} = syncOptions; - const {gdprApplies, consentString = ''} = gdprConsent; - - const cidArr = responses.filter(resp => deepAccess(resp, 'body.cid')).map(resp => resp.body.cid).filter(uniques); - const params = `?cid=${encodeURIComponent(cidArr.join(','))}&gdpr=${gdprApplies ? 1 : 0}&gdpr_consent=${encodeURIComponent(consentString || '')}&us_privacy=${encodeURIComponent(uspConsent || '')}` - if (iframeEnabled) { - syncs.push({ - type: 'iframe', - url: `https://sync.sweetgum.io/api/sync/iframe/${params}` - }); - } - if (pixelEnabled) { - syncs.push({ - type: 'image', - url: `https://sync.sweetgum.io/api/sync/image/${params}` - }); - } - return syncs; -} - -export function hashCode(s, prefix = '_') { - const l = s.length; - let h = 0 - let i = 0; - if (l > 0) { - while (i < l) { - h = (h << 5) - h + s.charCodeAt(i++) | 0; - } - } - return prefix + h; -} - -export function getUniqueDealId(key, expiry = UNIQUE_DEAL_ID_EXPIRY) { - const storageKey = `u_${key}`; - const now = Date.now(); - const data = getStorageItem(storageKey); - let uniqueId; - - if (!data || !data.value || now - data.created > expiry) { - uniqueId = `${key}_${now.toString()}`; - setStorageItem(storageKey, uniqueId); - } else { - uniqueId = data.value; - } - - return uniqueId; -} - -export function getStorageItem(key) { - try { - return tryParseJSON(storage.getDataFromLocalStorage(key)); - } catch (e) { - } - - return null; -} - -export function setStorageItem(key, value, timestamp) { - try { - const created = timestamp || Date.now(); - const data = JSON.stringify({value, created}); - storage.setDataInLocalStorage(key, data); - } catch (e) { - } -} - -export function tryParseJSON(value) { - try { - return JSON.parse(value); - } catch (e) { - return value; - } -} - -export const spec = { - code: BIDDER_CODE, - version: BIDDER_VERSION, - supportedMediaTypes: [BANNER, VIDEO], - isBidRequestValid, - buildRequests, - interpretResponse, - getUserSyncs -}; - -registerBidder(spec); diff --git a/modules/shinezRtbBidAdapter.md b/modules/shinezRtbBidAdapter.md deleted file mode 100644 index e9190c2a9c4..00000000000 --- a/modules/shinezRtbBidAdapter.md +++ /dev/null @@ -1,35 +0,0 @@ -# Overview - -**Module Name:** Shinez RTB Bid Adapter - -**Module Type:** Bidder Adapter - -**Maintainer:** tech-team@shinez.io - -# Description - -Module that connects to Shinez RTB demand sources. - -# Test Parameters -```js -var adUnits = [ - { - code: 'test-ad', - sizes: [[300, 250]], - bids: [ - { - bidder: 'shinezRtb', - params: { - cId: '562524b21b1c1f08117fc7f9', - pId: '59ac17c192832d0011283fe3', - bidFloor: 0.0001, - ext: { - param1: 'loremipsum', - param2: 'dolorsitamet' - } - } - } - ] - } -]; -``` diff --git a/modules/showheroes-bsBidAdapter.js b/modules/showheroes-bsBidAdapter.js index bd2706a21d5..a241cb71a5d 100644 --- a/modules/showheroes-bsBidAdapter.js +++ b/modules/showheroes-bsBidAdapter.js @@ -1,9 +1,10 @@ import { deepAccess, + getBidIdParameter, getWindowTop, triggerPixel, logInfo, - logError, getBidIdParameter + logError } from '../src/utils.js'; import { config } from '../src/config.js'; import { Renderer } from '../src/Renderer.js'; @@ -28,11 +29,8 @@ function getEnvURLs(isStage) { } } -const GVLID = 111; - export const spec = { code: BIDDER_CODE, - gvlid: GVLID, aliases: ['showheroesBs'], supportedMediaTypes: [VIDEO, BANNER], isBidRequestValid: function(bid) { diff --git a/modules/silverpushBidAdapter.js b/modules/silverpushBidAdapter.js index 5403f3bd88c..df81e144380 100644 --- a/modules/silverpushBidAdapter.js +++ b/modules/silverpushBidAdapter.js @@ -12,7 +12,7 @@ const bidderConfig = 'sp_pb_ortb'; const bidderVersion = '1.0.0'; const DEFAULT_CURRENCY = 'USD'; -export const REQUEST_URL = 'https://prebid.chocolateplatform.co/bidder/?identifier=prebidchoc'; +export const REQUEST_URL = 'https://apac.chocolateplatform.com/bidder/?identifier=prebidchoc'; export const SP_OUTSTREAM_PLAYER_URL = 'https://xaido.sgp1.cdn.digitaloceanspaces.com/prebid/spoutstream.min.js'; const VIDEO_ORTB_PARAMS = [ @@ -110,7 +110,7 @@ export const CONVERTER = ortbConverter({ bidResponse.meta.paf.content_id = utils.deepAccess(bid, 'ext.paf.content_id'); } - bidResponse = buildVideoVastResponse(bidResponse); + bidResponse = buildVideoVastResponse(bidResponse) bidResponse = buildVideoOutstreamResponse(bidResponse, context) return bidResponse; diff --git a/modules/sizeMappingV2.js b/modules/sizeMappingV2.js index 5ddb2e410cb..d212d98f50b 100644 --- a/modules/sizeMappingV2.js +++ b/modules/sizeMappingV2.js @@ -63,7 +63,7 @@ export function isUsingNewSizeMapping(adUnits) { does not recognize. @params {Array} adUnits @returns {Array} validateAdUnits - Unrecognized properties are deleted. - */ +*/ export function checkAdUnitSetupHook(adUnits) { const validateSizeConfig = function (mediaType, sizeConfig, adUnitCode) { let isValid = true; diff --git a/modules/slimcutBidAdapter.js b/modules/slimcutBidAdapter.js index 250c1ebb19e..447e314958f 100644 --- a/modules/slimcutBidAdapter.js +++ b/modules/slimcutBidAdapter.js @@ -1,17 +1,10 @@ -import {getBidIdParameter, getValue, parseSizesInput} from '../src/utils.js'; +import { getValue, parseSizesInput, getBidIdParameter } from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { ajax } from '../src/ajax.js'; - -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - * @typedef {import('../src/adapters/bidderFactory.js').validBidRequests} validBidRequests - */ - const BIDDER_CODE = 'slimcut'; const ENDPOINT_URL = 'https://sb.freeskreen.com/pbr'; export const spec = { @@ -20,11 +13,11 @@ export const spec = { aliases: [{ code: 'scm', gvlid: 102 }], supportedMediaTypes: ['video', 'banner'], /** - * Determines whether or not the given bid request is valid. - * - * @param {BidRequest} bid The bid params to validate. - * @return boolean True if this is a valid bid, and false otherwise. - */ + * Determines whether or not the given bid request is valid. + * + * @param {BidRequest} bid The bid params to validate. + * @return boolean True if this is a valid bid, and false otherwise. + */ isBidRequestValid: function(bid) { let isValid = false; if (typeof bid.params !== 'undefined' && !isNaN(parseInt(getValue(bid.params, 'placementId'))) && parseInt(getValue(bid.params, 'placementId')) > 0) { @@ -33,11 +26,11 @@ export const spec = { return isValid; }, /** - * Make a server request from the list of BidRequests. - * - * @param {validBidRequests[]} an array of bids - * @return ServerRequest Info describing the request to the server. - */ + * Make a server request from the list of BidRequests. + * + * @param {validBidRequests[]} an array of bids + * @return ServerRequest Info describing the request to the server. + */ buildRequests: function(validBidRequests, bidderRequest) { const bids = validBidRequests.map(buildRequestObject); const payload = { @@ -62,11 +55,11 @@ export const spec = { }; }, /** - * 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. - */ + * 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, request) { const bidResponses = []; serverResponse = serverResponse.body; diff --git a/modules/smaatoBidAdapter.js b/modules/smaatoBidAdapter.js index ac0422842d5..1b50e033074 100644 --- a/modules/smaatoBidAdapter.js +++ b/modules/smaatoBidAdapter.js @@ -1,20 +1,22 @@ -import {deepAccess, deepSetValue, getDNT, isEmpty, isNumber, logError, logInfo} from '../src/utils.js'; +import { + chunk, + deepAccess, + deepSetValue, + fill, + getAdUnitSizes, + getDNT, + getMaxValueFromArray, + getMinValueFromArray, + isEmpty, + isNumber, + logError, + logInfo +} from '../src/utils.js'; import {find} from '../src/polyfill.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {config} from '../src/config.js'; import {ADPOD, BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js'; import CONSTANTS from '../src/constants.json'; -import {getAdUnitSizes} from '../libraries/sizeUtils/sizeUtils.js'; -import {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 - * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse - * @typedef {import('../src/adapters/bidderFactory.js').SyncOptions} SyncOptions - * @typedef {import('../src/adapters/bidderFactory.js').UserSync} UserSync - */ const { NATIVE_IMAGE_TYPES } = CONSTANTS; const BIDDER_CODE = 'smaato'; @@ -464,7 +466,7 @@ function createAdPodImp(bidRequest, videoMediaType) { }); } else { // all maxdurations should be the same - const maxDuration = Math.max(...durationRangeSec); + const maxDuration = getMaxValueFromArray(durationRangeSec); imps.map((imp, index) => { const sequence = index + 1; imp.video.maxduration = maxDuration @@ -479,7 +481,7 @@ function createAdPodImp(bidRequest, videoMediaType) { function getAdPodNumberOfPlacements(videoMediaType) { const {adPodDurationSec, durationRangeSec, requireExactDuration} = videoMediaType - const minAllowedDuration = Math.min(...durationRangeSec) + const minAllowedDuration = getMinValueFromArray(durationRangeSec) const numberOfPlacements = Math.floor(adPodDurationSec / minAllowedDuration) return requireExactDuration diff --git a/modules/smartadserverBidAdapter.js b/modules/smartadserverBidAdapter.js index 9146bba6514..ca43c26ffd7 100644 --- a/modules/smartadserverBidAdapter.js +++ b/modules/smartadserverBidAdapter.js @@ -1,14 +1,8 @@ -import { deepAccess, deepClone, isArrayOfNums, isFn, isInteger, isPlainObject, logError } from '../src/utils.js'; +import { deepAccess, deepClone, logError, isFn, isPlainObject } from '../src/utils.js'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; import { config } from '../src/config.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - * @typedef {import('../src/adapters/bidderFactory.js').ServerRequest} ServerRequest - */ - const BIDDER_CODE = 'smartadserver'; const GVL_ID = 45; const DEFAULT_FLOOR = 0.0; @@ -61,53 +55,20 @@ export const spec = { * Fills the payload with specific video attributes. * * @param {*} payload Payload that will be sent in the ServerRequest - * @param {*} videoMediaType Video media type + * @param {*} videoMediaType Video media type. */ fillPayloadForVideoBidRequest: function(payload, videoMediaType, videoParams) { const playerSize = videoMediaType.playerSize[0]; - const map = { - maxbitrate: 'vbrmax', - maxduration: 'vdmax', - minbitrate: 'vbrmin', - minduration: 'vdmin', - placement: 'vpt', - plcmt: 'vplcmt', - skip: 'skip' - }; - - payload.mediaType = VIDEO; payload.isVideo = videoMediaType.context === 'instream'; - payload.videoData = {}; - - for (const [key, value] of Object.entries(map)) { - payload.videoData = { - ...payload.videoData, - ...this.getValuableProperty(value, videoMediaType[key]) - }; - } - + payload.mediaType = VIDEO; payload.videoData = { - ...payload.videoData, - ...this.getValuableProperty('playerWidth', playerSize[0]), - ...this.getValuableProperty('playerHeight', playerSize[1]), - ...this.getValuableProperty('adBreak', this.getStartDelayForVideoBidRequest(videoMediaType, videoParams)), - ...this.getValuableProperty('videoProtocol', this.getProtocolForVideoBidRequest(videoMediaType, videoParams)), - ...(isArrayOfNums(videoMediaType.api) && videoMediaType.api.length ? { iabframeworks: videoMediaType.api.toString() } : {}), - ...(isArrayOfNums(videoMediaType.playbackmethod) && videoMediaType.playbackmethod.length ? { vpmt: videoMediaType.playbackmethod } : {}) + videoProtocol: this.getProtocolForVideoBidRequest(videoMediaType, videoParams), + playerWidth: playerSize[0], + playerHeight: playerSize[1], + adBreak: this.getStartDelayForVideoBidRequest(videoMediaType, videoParams) }; }, - /** - * Gets a property object if the value not falsy - * @param {string} property - * @param {number} value - * @returns object with the property or empty - */ - getValuableProperty: function(property, value) { - return typeof property === 'string' && isInteger(value) && value - ? { [property]: value } : {}; - }, - /** * Gets the protocols from either videoParams or VideoMediaType * @param {*} videoMediaType diff --git a/modules/smartxBidAdapter.js b/modules/smartxBidAdapter.js index 8394814365c..d91b62729bc 100644 --- a/modules/smartxBidAdapter.js +++ b/modules/smartxBidAdapter.js @@ -1,17 +1,4 @@ -import { - logError, - deepAccess, - isArray, - getDNT, - generateUUID, - isEmpty, - _each, - logMessage, - logWarn, - isFn, - isPlainObject, - getBidIdParameter -} from '../src/utils.js'; +import { logError, deepAccess, isArray, getBidIdParameter, getDNT, generateUUID, isEmpty, _each, logMessage, logWarn, isFn, isPlainObject } from '../src/utils.js'; import { Renderer } from '../src/Renderer.js'; @@ -21,12 +8,6 @@ import { import { VIDEO } from '../src/mediaTypes.js'; - -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - */ - const BIDDER_CODE = 'smartx'; const URL = 'https://bid.sxp.smartclip.net/bid/1000'; const GVLID = 115; diff --git a/modules/smartxBidAdapter.md b/modules/smartxBidAdapter.md index 50f78660458..853f06d6baf 100644 --- a/modules/smartxBidAdapter.md +++ b/modules/smartxBidAdapter.md @@ -3,7 +3,7 @@ ``` Module Name: smartclip Bidder Adapter Module Type: Bidder Adapter -Maintainer: bidding@smartclip.tv +Maintainer: adtech@smartclip.tv ``` # Description @@ -170,4 +170,4 @@ This adapter requires setup and approval from the smartclip team. } }], }]; -``` +``` \ No newline at end of file diff --git a/modules/smartyadsBidAdapter.js b/modules/smartyadsBidAdapter.js index 2409bebbc59..89749aed433 100644 --- a/modules/smartyadsBidAdapter.js +++ b/modules/smartyadsBidAdapter.js @@ -3,16 +3,9 @@ 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 { ajax } from '../src/ajax.js'; const BIDDER_CODE = 'smartyads'; -const GVLID = 534; -const adUrls = { - US_EAST: 'https://n1.smartyads.com/?c=o&m=prebid&secret_key=prebid_js', - EU: 'https://n2.smartyads.com/?c=o&m=prebid&secret_key=prebid_js', - SGP: 'https://n6.smartyads.com/?c=o&m=prebid&secret_key=prebid_js' -} - +const AD_URL = 'https://n1.smartyads.com/?c=o&m=prebid&secret_key=prebid_js'; const URL_SYNC = 'https://as.ck-ie.com/prebidjs?p=7c47322e527cf8bdeb7facc1bb03387a'; function isBidResponseValid(bid) { @@ -32,28 +25,8 @@ function isBidResponseValid(bid) { } } -function getAdUrlByRegion(bid) { - let adUrl; - - if (bid.params.region && adUrls[bid.params.region]) { - adUrl = adUrls[bid.params.region]; - } else { - try { - const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone; - const region = timezone.split('/')[0]; - if (region === 'Europe') adUrl = adUrls['EU']; - else adUrl = adUrls['US_EAST']; - } catch (err) { - adUrl = adUrls['US_EAST']; - } - } - - return adUrl; -} - export const spec = { code: BIDDER_CODE, - gvlid: GVLID, supportedMediaTypes: [BANNER, VIDEO, NATIVE], isBidRequestValid: (bid) => { @@ -93,17 +66,11 @@ export const spec = { if (bidderRequest.gdprConsent) { request.gdpr = bidderRequest.gdprConsent } - if (bidderRequest.gppConsent) { - request.gpp = bidderRequest.gppConsent; - } } const len = validBidRequests.length; - let adUrl; - for (let i = 0; i < len; i++) { let bid = validBidRequests[i]; - if (i === 0) adUrl = getAdUrlByRegion(bid); let traff = bid.params.traffic || BANNER placements.push({ placementId: bid.params.sourceid, @@ -116,12 +83,11 @@ export const spec = { placements.schain = bid.schain; } } - return { method: 'POST', - url: adUrl, + url: AD_URL, data: request - } + }; }, interpretResponse: (serverResponse) => { @@ -136,46 +102,24 @@ export const spec = { return response; }, - getUserSyncs: (syncOptions, serverResponses = [], gdprConsent = {}, uspConsent = '', gppConsent = '') => { + getUserSyncs: (syncOptions, serverResponses = [], gdprConsent = {}, uspConsent = '') => { let syncs = []; let { gdprApplies, consentString = '' } = gdprConsent; if (syncOptions.iframeEnabled) { syncs.push({ type: 'iframe', - url: `${URL_SYNC}&gdpr=${gdprApplies ? 1 : 0}&gdpr_consent=${consentString}&type=iframe&us_privacy=${uspConsent}&gpp=${gppConsent}` + url: `${URL_SYNC}&gdpr=${gdprApplies ? 1 : 0}&gdpr_consent=${consentString}&type=iframe&us_privacy=${uspConsent}` }); } else { syncs.push({ type: 'image', - url: `${URL_SYNC}&gdpr=${gdprApplies ? 1 : 0}&gdpr_consent=${consentString}&type=image&us_privacy=${uspConsent}&gpp=${gppConsent}` + url: `${URL_SYNC}&gdpr=${gdprApplies ? 1 : 0}&gdpr_consent=${consentString}&type=image&us_privacy=${uspConsent}` }); } return syncs - }, - - onBidWon: function(bid) { - if (bid.winUrl) { - ajax(bid.winUrl, () => {}, JSON.stringify(bid)); - } else { - if (bid?.postData && bid?.postData[0] && bid?.postData[0].params && bid?.postData[0].params[0].host == 'prebid') { - ajax('https://et-nd43.itdsmr.com/?c=o&m=prebid&secret_key=prebid_js&winTest=1', () => {}, JSON.stringify(bid)); - } - } - }, - - onTimeout: function(bid) { - if (bid?.postData && bid?.postData[0] && bid?.postData[0].params && bid?.postData[0].params[0].host == 'prebid') { - ajax('https://et-nd43.itdsmr.com/?c=o&m=prebid&secret_key=prebid_js&bidTimeout=1', () => {}, JSON.stringify(bid)); - } - }, - - onBidderError: function(bid) { - if (bid?.postData && bid?.postData[0] && bid?.postData[0].params && bid?.postData[0].params[0].host == 'prebid') { - ajax('https://et-nd43.itdsmr.com/?c=o&m=prebid&secret_key=prebid_js&bidderError=1', () => {}, JSON.stringify(bid)); - } - }, + } }; diff --git a/modules/smartyadsBidAdapter.md b/modules/smartyadsBidAdapter.md index 443d5ab5978..e0d6023a794 100644 --- a/modules/smartyadsBidAdapter.md +++ b/modules/smartyadsBidAdapter.md @@ -14,11 +14,10 @@ Module that connects to SmartyAds' demand sources | Name | Scope | Description | Example | | :------------ | :------- | :------------------------ | :------------------- | -| `sourceid` | required (for prebid.js) | Placement ID | "0" | -| `host` | required (for prebid-server) | Const value, set to "prebid" | "prebid" | -| `accountid` | required (for prebid-server) | Partner ID | "1901" | +| `sourceid` | required (for prebid.js) | placement ID | "0" | +| `host` | required (for prebid-server) | const value, set to "prebid" | "prebid" | +| `accountid` | required (for prebid-server) | partner ID | "1901" | | `traffic` | optional (for prebid.js) | Configures the mediaType that should be used. Values can be banner, native or video | "banner" | -| `region` | optional (for prebid.js) | Prefix of the region to which prebid must send requests. Possible values: "US_EAST", "EU" | "US_EAST" | # Test Parameters ``` @@ -36,9 +35,7 @@ Module that connects to SmartyAds' demand sources host: 'prebid', sourceid: '0', accountid: '0', - traffic: 'native', - region: 'US_EAST' - + traffic: 'native' } } ] @@ -58,8 +55,7 @@ Module that connects to SmartyAds' demand sources host: 'prebid', sourceid: '0', accountid: '0', - traffic: 'banner', - region: 'US_EAST' + traffic: 'banner' } } ] @@ -80,9 +76,7 @@ Module that connects to SmartyAds' demand sources host: 'prebid', sourceid: '0', accountid: '0', - traffic: 'video', - region: 'US_EAST' - + traffic: 'video' } } ] diff --git a/modules/smilewantedBidAdapter.js b/modules/smilewantedBidAdapter.js index 515aae0e092..b7a25ba58df 100644 --- a/modules/smilewantedBidAdapter.js +++ b/modules/smilewantedBidAdapter.js @@ -1,65 +1,32 @@ -import {deepAccess, deepClone, isArray, isFn, isPlainObject, logError, 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 {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js'; -import {INSTREAM, OUTSTREAM} from '../src/video.js'; -import {convertOrtbRequestToProprietaryNative, toOrtbNativeRequest, toLegacyResponse} from '../src/native.js'; - -const BIDDER_CODE = 'smilewanted'; - -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - */ - -const GVL_ID = 639; +import { isArray, logError, logWarn, isFn, isPlainObject } from '../src/utils.js'; +import { Renderer } from '../src/Renderer.js'; +import { config } from '../src/config.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; export const spec = { - code: BIDDER_CODE, - gvlid: GVL_ID, + code: 'smilewanted', aliases: ['smile', 'sw'], - supportedMediaTypes: [BANNER, VIDEO, NATIVE], + supportedMediaTypes: [BANNER, VIDEO], /** * Determines whether or not the given bid request is valid. * - * @param {BidRequest} bid The bid to validate. + * @param {object} bid The bid to validate. * @return boolean True if this is a valid bid, and false otherwise. */ isBidRequestValid: function(bid) { - if (!bid.params || !bid.params.zoneId) { - return false; - } - - if (deepAccess(bid, 'mediaTypes.video')) { - const videoMediaTypesParams = deepAccess(bid, 'mediaTypes.video', {}); - const videoBidderParams = deepAccess(bid, 'params.video', {}); - - const videoParams = { - ...videoMediaTypesParams, - ...videoBidderParams - }; - - if (!videoParams.context || ![INSTREAM, OUTSTREAM].includes(videoParams.context)) { - return false; - } - } - - return true; + return !!(bid.params && bid.params.zoneId); }, /** * Make a server request from the list of BidRequests. * * @param {BidRequest[]} validBidRequests A non-empty list of valid bid requests that should be sent to the Server. - * @param {BidderRequest} bidderRequest bidder request object. * @return ServerRequest Info describing the request to the server. */ buildRequests: function(validBidRequests, bidderRequest) { - validBidRequests = convertOrtbRequestToProprietaryNative(validBidRequests); - return validBidRequests.map(bid => { - const payload = { + var payload = { zoneId: bid.params.zoneId, currencyCode: config.getConfig('currency.adServerCurrency') || 'EUR', tagId: bid.adUnitCode, @@ -70,13 +37,11 @@ export const spec = { transactionId: bid.ortb2Imp?.ext?.tid, timeout: bidderRequest?.timeout, bidId: bid.bidId, - /** - positionType is undocumented + /** positionType is undocumented It is unclear what this parameter means. If it means the same as pos in openRTB, It should read from openRTB object - or from mediaTypes.banner.pos - */ + or from mediaTypes.banner.pos */ positionType: bid.params.positionType || '', prebidVersion: '$prebid.version$' }; @@ -90,41 +55,20 @@ export const spec = { payload.bidfloor = bid.params.bidfloor; } - if (bidderRequest?.refererInfo) { + if (bidderRequest && bidderRequest.refererInfo) { payload.pageDomain = bidderRequest.refererInfo.page || ''; } - if (bidderRequest?.gdprConsent) { + if (bidderRequest && bidderRequest.gdprConsent) { payload.gdpr_consent = bidderRequest.gdprConsent.consentString; payload.gdpr = bidderRequest.gdprConsent.gdprApplies; // we're handling the undefined case server side } - payload.eids = bid?.userIdAsEids; - - const videoMediaType = deepAccess(bid, 'mediaTypes.video'); - const context = deepAccess(bid, 'mediaTypes.video.context'); - - if (bid.mediaType === 'video' || (videoMediaType && context === INSTREAM) || (videoMediaType && context === OUTSTREAM)) { - payload.context = context; - payload.videoParams = deepClone(videoMediaType); - } - - const nativeMediaType = deepAccess(bid, 'mediaTypes.native'); - - if (nativeMediaType) { - payload.context = 'native'; - payload.nativeParams = nativeMediaType; - let sizes = deepAccess(bid, 'mediaTypes.native.image.sizes', []); - - if (sizes.length > 0) { - const size = Array.isArray(sizes[0]) ? sizes[0] : sizes; - - payload.width = size[0] || payload.width; - payload.height = size[1] || payload.height; - } + if (bid && bid.userIdAsEids) { + payload.eids = bid.userIdAsEids; } - const payloadString = JSON.stringify(payload); + var payloadString = JSON.stringify(payload); return { method: 'POST', url: 'https://prebid.smilewanted.com', @@ -136,21 +80,18 @@ export const spec = { /** * Unpack the response from the server into a list of bids. * - * @param {ServerResponse} serverResponse A successful response from the server. - * @param {BidRequest} bidRequest + * @param {*} serverResponse A successful response from the server. * @return {Bid[]} An array of bids which were nested inside the server. */ interpretResponse: function(serverResponse, bidRequest) { - if (!serverResponse.body) return []; const bidResponses = []; + var response = serverResponse.body; try { - const response = serverResponse.body; - const bidRequestData = JSON.parse(bidRequest.data); if (response) { const dealId = response.dealId || ''; const bidResponse = { - requestId: bidRequestData.bidId, + requestId: JSON.parse(bidRequest.data).bidId, cpm: response.cpm, width: response.width, height: response.height, @@ -162,21 +103,14 @@ export const spec = { ad: response.ad, }; - if (response.formatTypeSw === 'video_instream' || response.formatTypeSw === 'video_outstream') { + if (response.formatTypeSw == 'video_instream' || response.formatTypeSw == 'video_outstream') { bidResponse['mediaType'] = 'video'; bidResponse['vastUrl'] = response.ad; bidResponse['ad'] = null; - - if (response.formatTypeSw === 'video_outstream') { - bidResponse['renderer'] = newRenderer(bidRequestData, response); - } } - if (response.formatTypeSw === 'native') { - const nativeAdResponse = JSON.parse(response.ad); - const ortbNativeRequest = toOrtbNativeRequest(bidRequestData.nativeParams); - bidResponse['mediaType'] = 'native'; - bidResponse['native'] = toLegacyResponse(nativeAdResponse, ortbNativeRequest); + if (response.formatTypeSw == 'video_outstream') { + bidResponse['renderer'] = newRenderer(JSON.parse(bidRequest.data), response); } if (dealId.length > 0) { @@ -184,7 +118,7 @@ export const spec = { } bidResponse.meta = {}; - if (response.meta?.advertiserDomains && isArray(response.meta.advertiserDomains)) { + if (response.meta && response.meta.advertiserDomains && isArray(response.meta.advertiserDomains)) { bidResponse.meta.advertiserDomains = response.meta.advertiserDomains; } bidResponses.push(bidResponse); @@ -192,18 +126,15 @@ export const spec = { } catch (error) { logError('Error while parsing smilewanted response', error); } - return bidResponses; }, /** - * Register the user sync pixels which should be dropped after the auction. + * User syncs. * - * @param {SyncOptions} syncOptions Which user syncs are allowed? - * @param {ServerResponse[]} responses List of server's responses. - * @param {Object} gdprConsent The GDPR consent parameters - * @param {Object} uspConsent The USP consent parameters - * @return {UserSync[]} The user syncs which should be dropped. + * @param {*} syncOptions Publisher prebid configuration. + * @param {*} serverResponses A successful response from the server. + * @return {Syncs[]} An array of syncs that should be executed. */ getUserSyncs: function(syncOptions, responses, gdprConsent, uspConsent) { let params = ''; @@ -236,8 +167,7 @@ export const spec = { /** * Create SmileWanted renderer - * @param bidRequest - * @param bidResponse + * @param requestId * @returns {*} */ function newRenderer(bidRequest, bidResponse) { diff --git a/modules/snigelBidAdapter.js b/modules/snigelBidAdapter.js index 6d32d8f97a2..f41fb98d436 100644 --- a/modules/snigelBidAdapter.js +++ b/modules/snigelBidAdapter.js @@ -1,7 +1,7 @@ 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, getDNT} from '../src/utils.js'; +import {deepAccess, isArray, isFn, isPlainObject} from '../src/utils.js'; import {hasPurpose1Consent} from '../src/utils/gpdr.js'; import {getGlobal} from '../src/prebidGlobal.js'; @@ -13,7 +13,6 @@ const DEFAULT_CURRENCIES = ['USD']; const FLOOR_MATCH_ALL_SIZES = '*'; const getConfig = config.getConfig; -const refreshes = {}; export const spec = { code: BIDDER_CODE, @@ -30,15 +29,12 @@ export const spec = { method: 'POST', url: getEndpoint(), data: JSON.stringify({ - id: bidderRequest.auctionId, - accountId: deepAccess(bidRequests, '0.params.accountId'), - site: deepAccess(bidRequests, '0.params.site'), + id: bidderRequest.bidderRequestId, cur: getCurrencies(), test: getTestFlag(), + devw: window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth, + devh: window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight, version: getGlobal().version, - gpp: deepAccess(bidderRequest, 'gppConsent.gppString') || deepAccess(bidderRequest, 'ortb2.regs.gpp'), - gpp_sid: - deepAccess(bidderRequest, 'gppConsent.applicableSections') || deepAccess(bidderRequest, 'ortb2.regs.gpp_sid'), gdprApplies: gdprApplies, gdprConsentString: gdprApplies === true ? deepAccess(bidderRequest, 'gdprConsent.consentString') : undefined, gdprConsentProv: gdprApplies === true ? deepAccess(bidderRequest, 'gdprConsent.addtlConsent') : undefined, @@ -47,24 +43,12 @@ export const spec = { eids: deepAccess(bidRequests, '0.userIdAsEids'), schain: deepAccess(bidRequests, '0.schain'), page: getPage(bidderRequest), - topframe: inIframe() === true ? 0 : 1, - device: { - w: window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth, - h: window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight, - dnt: getDNT() ? 1 : 0, - language: getLanguage(), - }, placements: bidRequests.map((r) => { return { - id: r.adUnitCode, - tid: r.transactionId, - gpid: deepAccess(r, 'ortb2Imp.ext.gpid'), - pbadslot: deepAccess(r, 'ortb2Imp.ext.data.pbadslot') || deepAccess(r, 'ortb2Imp.ext.gpid'), + uuid: r.bidId, name: r.params.placement, sizes: r.sizes, floor: getPriceFloor(r, BANNER, FLOOR_MATCH_ALL_SIZES), - refresh: getRefreshInformation(r.adUnitCode), - params: r.params.additionalParams, }; }), }), @@ -72,14 +56,14 @@ export const spec = { }; }, - interpretResponse: function (serverResponse, bidRequest) { + interpretResponse: function (serverResponse) { if (!serverResponse.body || !serverResponse.body.bids) { return []; } return serverResponse.body.bids.map((bid) => { return { - requestId: mapIdToRequestId(bid.id, bidRequest), + requestId: bid.uuid, cpm: bid.price, creativeId: bid.crid, currency: serverResponse.body.cur, @@ -93,9 +77,9 @@ export const spec = { }); }, - getUserSyncs: function (syncOptions, responses, gdprConsent, uspConsent, gppConsent) { + getUserSyncs: function (syncOptions, responses, gdprConsent, uspConsent) { const syncUrl = getSyncUrl(responses || []); - if (syncUrl && syncOptions.iframeEnabled && hasSyncConsent(gdprConsent, uspConsent, gppConsent)) { + if (syncUrl && syncOptions.iframeEnabled && hasSyncConsent(gdprConsent, uspConsent)) { return [{type: 'iframe', url: getSyncEndpoint(syncUrl, gdprConsent)}]; } }, @@ -105,7 +89,7 @@ registerBidder(spec); function getPage(bidderRequest) { return ( - getConfig(`${BIDDER_CODE}.page`) || deepAccess(bidderRequest, 'refererInfo.page') || window.location.href + getConfig(`${BIDDER_CODE}.page`) || deepAccess(bidderRequest, 'refererInfo.canonicalUrl') || window.location.href ); } @@ -117,14 +101,6 @@ function getTestFlag() { return getConfig(`${BIDDER_CODE}.test`) === true; } -function getLanguage() { - return navigator && navigator.language - ? navigator.language.indexOf('-') != -1 - ? navigator.language.split('-')[0] - : navigator.language - : undefined; -} - function getCurrencies() { const currencyOverrides = getConfig(`${BIDDER_CODE}.cur`); if (currencyOverrides !== undefined && (!isArray(currencyOverrides) || currencyOverrides.length === 0)) { @@ -154,43 +130,14 @@ function getPriceFloor(bidRequest, mediaType, size) { } } -function getRefreshInformation(adUnitCode) { - const refresh = refreshes[adUnitCode]; - if (!refresh) { - refreshes[adUnitCode] = { - count: 0, - previousTime: new Date(), - }; - return undefined; +function hasSyncConsent(gdprConsent, uspConsent) { + if (gdprConsent?.gdprApplies && !hasPurpose1Consent(gdprConsent)) { + return false; + } else if (uspConsent && uspConsent[1] === 'Y' && uspConsent[2] === 'Y') { + return false; + } else { + return true; } - - const currentTime = new Date(); - const timeDifferenceSeconds = Math.floor((currentTime - refresh.previousTime) / 1000); - refresh.count += 1; - refresh.previousTime = currentTime; - return { - count: refresh.count, - time: timeDifferenceSeconds, - }; -} - -function mapIdToRequestId(id, bidRequest) { - return bidRequest.bidderRequest.bids.filter((bid) => bid.adUnitCode === id)[0].bidId; -} - -function hasUspConsent(uspConsent) { - return typeof uspConsent !== 'string' || !(uspConsent[0] === '1' && uspConsent[2] === 'Y'); -} - -function hasGppConsent(gppConsent) { - return ( - !(gppConsent && Array.isArray(gppConsent.applicableSections)) || - gppConsent.applicableSections.every((section) => typeof section === 'number' && section <= 5) - ); -} - -function hasSyncConsent(gdprConsent, uspConsent, gppConsent) { - return hasPurpose1Consent(gdprConsent) && hasUspConsent(uspConsent) && hasGppConsent(gppConsent); } function getSyncUrl(responses) { diff --git a/modules/snigelBidAdapter.md b/modules/snigelBidAdapter.md index f9bb1951d21..a83e133144f 100644 --- a/modules/snigelBidAdapter.md +++ b/modules/snigelBidAdapter.md @@ -15,13 +15,9 @@ Please reach out to us [through our contact form](https://snigel.com/get-in-touc # Parameters -| Name | Required | Description | -| :-------- | :------- | :------------------- | -| accountId | Yes | Account identifier | -| site | Yes | Site identifier | -| placement | Yes | Placement identifier | - -Snigel will provide all of these parameters to you. +| Name | Required | Description | Example | +| :--- | :-------- | :---------- | :------ | +| placement | Yes | Placement identifier | top_leaderboard | # Test @@ -41,8 +37,6 @@ var adUnits = [ { bidder: "snigel", params: { - accountId: "1000", - site: "test.com", placement: "prebid_test_placement", }, }, diff --git a/modules/sonobiAnalyticsAdapter.js b/modules/sonobiAnalyticsAdapter.js index 04a855b5be6..0057944b201 100644 --- a/modules/sonobiAnalyticsAdapter.js +++ b/modules/sonobiAnalyticsAdapter.js @@ -6,7 +6,7 @@ import {ajaxBuilder} from '../src/ajax.js'; let ajax = ajaxBuilder(0); -export const DEFAULT_EVENT_URL = 'apex.go.sonobi.com/keymaker'; +const DEFAULT_EVENT_URL = 'apex.go.sonobi.com/keymaker'; const analyticsType = 'endpoint'; const QUEUE_TIMEOUT_DEFAULT = 200; const { diff --git a/modules/sonobiBidAdapter.js b/modules/sonobiBidAdapter.js index 1ce7665ddfc..c99d0ab011d 100644 --- a/modules/sonobiBidAdapter.js +++ b/modules/sonobiBidAdapter.js @@ -1,18 +1,11 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { parseSizesInput, logError, generateUUID, isEmpty, deepAccess, logWarn, logMessage, isFn, isPlainObject } from '../src/utils.js'; +import { parseSizesInput, logError, generateUUID, isEmpty, deepAccess, logWarn, logMessage, getGptSlotInfoForAdUnitCode, isFn, isPlainObject } from '../src/utils.js'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; import { config } from '../src/config.js'; import { Renderer } from '../src/Renderer.js'; import { userSync } from '../src/userSync.js'; import { bidderSettings } from '../src/bidderSettings.js'; -import { getAllOrtbKeywords } from '../libraries/keywords/keywords.js'; -import { getGptSlotInfoForAdUnitCode } from '../libraries/gptUtils/gptUtils.js'; - -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - */ - +import {getAllOrtbKeywords} from '../libraries/keywords/keywords.js'; const BIDDER_CODE = 'sonobi'; const STR_ENDPOINT = 'https://apex.go.sonobi.com/trinity.json'; const PAGEVIEW_ID = generateUUID(); @@ -46,8 +39,8 @@ export const spec = { return false; } } else if (deepAccess(bid, 'mediaTypes.video')) { - if (deepAccess(bid, 'mediaTypes.video.context') === 'outstream' && !deepAccess(bid, 'mediaTypes.video.playerSize')) { - // playerSize is required for outstream video adUnits + if (deepAccess(bid, 'mediaTypes.video.context') === 'outstream' && !bid.params.sizes) { + // bids.params.sizes is required for outstream video adUnits return false; } if (deepAccess(bid, 'mediaTypes.video.context') === 'instream' && !deepAccess(bid, 'mediaTypes.video.playerSize')) { @@ -67,15 +60,23 @@ export const spec = { */ buildRequests: (validBidRequests, bidderRequest) => { const bids = validBidRequests.map(bid => { + let mediaType; + + if (deepAccess(bid, 'mediaTypes.video')) { + mediaType = 'video'; + } else if (deepAccess(bid, 'mediaTypes.banner')) { + mediaType = 'display'; + } + let slotIdentifier = _validateSlot(bid); if (/^[\/]?[\d]+[[\/].+[\/]?]?$/.test(slotIdentifier)) { slotIdentifier = slotIdentifier.charAt(0) === '/' ? slotIdentifier : '/' + slotIdentifier; return { - [`${slotIdentifier}|${bid.bidId}`]: `${_validateSize(bid)}|${_validateFloor(bid)}${_validateGPID(bid)}${_validateMediaType(bid)}` + [`${slotIdentifier}|${bid.bidId}`]: `${_validateSize(bid)}|${_validateFloor(bid)}${_validateGPID(bid)}${_validateMediaType(mediaType)}` } } else if (/^[0-9a-fA-F]{20}$/.test(slotIdentifier) && slotIdentifier.length === 20) { return { - [bid.bidId]: `${slotIdentifier}|${_validateSize(bid)}|${_validateFloor(bid)}${_validateGPID(bid)}${_validateMediaType(bid)}` + [bid.bidId]: `${slotIdentifier}|${_validateSize(bid)}|${_validateFloor(bid)}${_validateGPID(bid)}${_validateMediaType(mediaType)}` } } else { logError(`The ad unit code or Sonobi Placement id for slot ${bid.bidId} is invalid`); @@ -156,11 +157,6 @@ export const spec = { payload.coppa = 0; } - if (deepAccess(bidderRequest, 'ortb2.experianRtidData') && deepAccess(bidderRequest, 'ortb2.experianRtidKey')) { - payload.expData = deepAccess(bidderRequest, 'ortb2.experianRtidData'); - payload.expKey = deepAccess(bidderRequest, 'ortb2.experianRtidKey'); - } - // If there is no key_maker data, then don't make the request. if (isEmpty(data)) { return null; @@ -252,7 +248,10 @@ export const spec = { bidRequest, 'renderer.options' )); - let videoSize = deepAccess(bidRequest, 'mediaTypes.video.playerSize'); + let videoSize = deepAccess(bidRequest, 'params.sizes'); + if (Array.isArray(videoSize) && Array.isArray(videoSize[0])) { // handle case of multiple sizes + videoSize = videoSize[0]; // Only take the first size for outstream + } if (videoSize) { bids.width = videoSize[0]; bids.height = videoSize[1]; @@ -340,28 +339,10 @@ function _validateGPID(bid) { return '' } -function _validateMediaType(bidRequest) { - let mediaType; - if (deepAccess(bidRequest, 'mediaTypes.video')) { - mediaType = 'video'; - } else if (deepAccess(bidRequest, 'mediaTypes.banner')) { - mediaType = 'display'; - } - +function _validateMediaType(mediaType) { let mediaTypeValidation = ''; if (mediaType === 'video') { mediaTypeValidation = 'c=v,'; - if (deepAccess(bidRequest, 'mediaTypes.video.playbackmethod')) { - mediaTypeValidation = `${mediaTypeValidation}pm=${deepAccess(bidRequest, 'mediaTypes.video.playbackmethod').join(':')},`; - } - if (deepAccess(bidRequest, 'mediaTypes.video.placement')) { - let placement = deepAccess(bidRequest, 'mediaTypes.video.placement'); - mediaTypeValidation = `${mediaTypeValidation}p=${placement},`; - } - if (deepAccess(bidRequest, 'mediaTypes.video.plcmt')) { - let plcmt = deepAccess(bidRequest, 'mediaTypes.video.plcmt'); - mediaTypeValidation = `${mediaTypeValidation}pl=${plcmt},`; - } } else if (mediaType === 'display') { mediaTypeValidation = 'c=d,'; } diff --git a/modules/sovrnAnalyticsAdapter.md b/modules/sovrnAnalyticsAdapter.md index b4fe7c971a2..80bc6d7f6b1 100644 --- a/modules/sovrnAnalyticsAdapter.md +++ b/modules/sovrnAnalyticsAdapter.md @@ -3,7 +3,7 @@ ``` Module Name: Sovrn Analytics Adapter Module Type: Analytics Adapter -Maintainer: exchange@sovrn.com +Maintainer: jrosendahl@sovrn.com ``` # Description diff --git a/modules/sovrnBidAdapter.js b/modules/sovrnBidAdapter.js index e786095874e..f21c4d1752a 100644 --- a/modules/sovrnBidAdapter.js +++ b/modules/sovrnBidAdapter.js @@ -1,12 +1,13 @@ import { _each, + getBidIdParameter, isArray, getUniqueIdentifierStr, deepSetValue, logError, deepAccess, isInteger, - logWarn, getBidIdParameter + logWarn } from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js' import { @@ -15,10 +16,6 @@ import { VIDEO } from '../src/mediaTypes.js' -/** - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - */ - const ORTB_VIDEO_PARAMS = { 'mimes': (value) => Array.isArray(value) && value.length > 0 && value.every(v => typeof v === 'string'), 'minduration': (value) => isInteger(value), @@ -48,6 +45,7 @@ const ORTB_VIDEO_PARAMS = { const REQUIRED_VIDEO_PARAMS = { context: (value) => value !== ADPOD, mimes: ORTB_VIDEO_PARAMS.mimes, + minduration: ORTB_VIDEO_PARAMS.minduration, maxduration: ORTB_VIDEO_PARAMS.maxduration, protocols: ORTB_VIDEO_PARAMS.protocols } @@ -171,16 +169,6 @@ export const spec = { }; } - const tid = deepAccess(bidderRequest, 'ortb2.source.tid') - if (tid) { - deepSetValue(sovrnBidReq, 'source.tid', tid) - } - - const coppa = deepAccess(bidderRequest, 'ortb2.regs.coppa'); - if (coppa) { - deepSetValue(sovrnBidReq, 'regs.coppa', 1); - } - if (bidderRequest.gdprConsent) { deepSetValue(sovrnBidReq, 'regs.ext.gdpr', +bidderRequest.gdprConsent.gdprApplies); deepSetValue(sovrnBidReq, 'user.ext.consent', bidderRequest.gdprConsent.consentString) @@ -218,7 +206,7 @@ export const spec = { * Format Sovrn responses as Prebid bid responses * @param {id, seatbid} sovrnResponse A successful response from Sovrn. * @return {Bid[]} An array of formatted bids. - */ + */ interpretResponse: function({ body: {id, seatbid} }) { if (!id || !seatbid || !Array.isArray(seatbid)) return [] @@ -255,7 +243,7 @@ export const spec = { } }, - getUserSyncs: function(syncOptions, serverResponses, gdprConsent, uspConsent, gppConsent) { + getUserSyncs: function(syncOptions, serverResponses, gdprConsent, uspConsent) { try { const tracks = [] if (serverResponses && serverResponses.length !== 0) { @@ -269,10 +257,6 @@ export const spec = { if (uspConsent) { params.push(['us_privacy', uspConsent]); } - if (gppConsent) { - params.push(['gpp', gppConsent.gppString]); - params.push(['gpp_sid', gppConsent.applicableSections]) - } if (iidArr[0]) { params.push(['informer', iidArr[0]]); diff --git a/modules/sovrnBidAdapter.md b/modules/sovrnBidAdapter.md index ce131269eee..53e3158024d 100644 --- a/modules/sovrnBidAdapter.md +++ b/modules/sovrnBidAdapter.md @@ -3,7 +3,7 @@ ``` Module Name: Sovrn Bid Adapter Module Type: Bidder Adapter -Maintainer: exchange@sovrn.com +Maintainer: jrosendahl@sovrn.com ``` # Description diff --git a/modules/sparteoBidAdapter.js b/modules/sparteoBidAdapter.js deleted file mode 100644 index 0bccc1ec140..00000000000 --- a/modules/sparteoBidAdapter.js +++ /dev/null @@ -1,170 +0,0 @@ -import { deepAccess, deepSetValue, logError, parseSizesInput, triggerPixel } 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' - -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - */ - -const BIDDER_CODE = 'sparteo'; -const GVLID = 1028; -const TTL = 60; -const HTTP_METHOD = 'POST'; -const REQUEST_URL = 'https://bid.sparteo.com/auction'; -const USER_SYNC_URL_IFRAME = 'https://sync.sparteo.com/sync/iframe.html?from=prebidjs'; -let isSynced = window.sparteoCrossfire?.started || false; - -const converter = ortbConverter({ - context: { - // `netRevenue` and `ttl` are required properties of bid responses - provide a default for them - netRevenue: true, // or false if your adapter should set bidResponse.netRevenue = false - ttl: TTL // default bidResponse.ttl (when not specified in ORTB response.seatbid[].bid[].exp) - }, - request(buildRequest, imps, bidderRequest, context) { - const request = buildRequest(imps, bidderRequest, context); - - if (bidderRequest.bids[0].params.networkId) { - deepSetValue(request, 'site.publisher.ext.params.networkId', bidderRequest.bids[0].params.networkId); - } - - if (bidderRequest.bids[0].params.publisherId) { - deepSetValue(request, 'site.publisher.ext.params.publisherId', bidderRequest.bids[0].params.publisherId); - } - - return request; - }, - imp(buildImp, bidRequest, context) { - const imp = buildImp(bidRequest, context); - - deepSetValue(imp, 'ext.sparteo.params', bidRequest.params); - - return imp; - }, - bidResponse(buildBidResponse, bid, context) { - context.mediaType = deepAccess(bid, 'ext.prebid.type'); - - const response = buildBidResponse(bid, context); - - if (context.mediaType == 'video') { - response.nurl = bid.nurl; - response.vastUrl = deepAccess(bid, 'ext.prebid.cache.vastXml.url') ?? null; - } - - return response; - } -}); - -export const spec = { - code: BIDDER_CODE, - gvlid: GVLID, - supportedMediaTypes: [BANNER, VIDEO], - - /** - * Determines whether or not the given bid request is valid. - * - * @param {BidRequest} bid The bid params to validate. - * @return boolean True if this is a valid bid, and false otherwise. - */ - isBidRequestValid: function (bid) { - let bannerParams = deepAccess(bid, 'mediaTypes.banner'); - let videoParams = deepAccess(bid, 'mediaTypes.video'); - - if (!bid.params) { - logError('The bid params are missing'); - return false; - } - - if (!bid.params.networkId && !bid.params.publisherId) { - logError('The networkId or publisherId is required'); - return false; - } - - if (!bannerParams && !videoParams) { - logError('The placement must be of banner or video type'); - return false; - } - - /** - * BANNER checks - */ - - if (bannerParams) { - let sizes = bannerParams.sizes; - - if (!sizes || parseSizesInput(sizes).length == 0) { - logError('mediaTypes.banner.sizes must be set for banner placement at the right format.'); - return false; - } - } - - /** - * VIDEO checks - */ - - if (videoParams) { - if (parseSizesInput(videoParams.playerSize).length == 0) { - logError('mediaTypes.video.playerSize must be set for video placement at the right format.'); - return false; - } - } - - return true; - }, - - buildRequests: function (bidRequests, bidderRequest) { - const payload = converter.toORTB({bidRequests, bidderRequest}) - - return { - method: HTTP_METHOD, - url: bidRequests[0].params.endpoint ? bidRequests[0].params.endpoint : REQUEST_URL, - data: payload - }; - }, - - interpretResponse: function (serverResponse, requests) { - const bids = converter.fromORTB({response: serverResponse.body, request: requests.data}).bids; - - return bids; - }, - - getUserSyncs: function (syncOptions, serverResponses, gdprConsent, uspConsent) { - let syncurl = ''; - - if (!isSynced && !window.sparteoCrossfire?.started) { - // Attaching GDPR Consent Params in UserSync url - if (gdprConsent) { - syncurl += '&gdpr=' + (gdprConsent.gdprApplies ? 1 : 0); - syncurl += '&gdpr_consent=' + encodeURIComponent(gdprConsent.consentString || ''); - } - if (uspConsent && uspConsent.consentString) { - syncurl += `&usp_consent=${uspConsent.consentString}`; - } - - if (syncOptions.iframeEnabled) { - isSynced = true; - - window.sparteoCrossfire = { - started: true - }; - - return [{ - type: 'iframe', - url: USER_SYNC_URL_IFRAME + syncurl - }]; - } - } - }, - - onTimeout: function (timeoutData) {}, - - onBidWon: function (bid) { - if (bid && bid.nurl) { - triggerPixel(bid.nurl, null); - } - }, - - onSetTargeting: function (bid) {} -}; - -registerBidder(spec); diff --git a/modules/sparteoBidAdapter.md b/modules/sparteoBidAdapter.md deleted file mode 100644 index 774d9211d9d..00000000000 --- a/modules/sparteoBidAdapter.md +++ /dev/null @@ -1,35 +0,0 @@ -# Overview - -``` -Module Name: Sparteo Bidder Adapter -Module Type: Bidder Adapter -Maintainer: prebid@sparteo.com -``` - -# Description - -Module that connects to Sparteo's demand sources - -# Test Parameters -``` - var adUnits = [ - { - code: 'test-div', - mediaTypes: { - banner: { - sizes: [ - [1, 1] - ] - } - }, - bids: [ - { - bidder: 'sparteo', - params: { - networkId: '1234567a-eb1b-1fae-1d23-e1fbaef234cf' - } - } - ] - } - ]; -``` \ No newline at end of file diff --git a/modules/spotxBidAdapter.js b/modules/spotxBidAdapter.js index c1f1c5159fc..874207adcf8 100644 --- a/modules/spotxBidAdapter.js +++ b/modules/spotxBidAdapter.js @@ -1,32 +1,10 @@ -import { - logError, - deepAccess, - isArray, - getDNT, - deepSetValue, - isEmpty, - _each, - logMessage, - logWarn, - isBoolean, - isNumber, - isPlainObject, - isFn, - setScriptAttributes, - getBidIdParameter -} from '../src/utils.js'; +import { logError, deepAccess, isArray, getBidIdParameter, getDNT, deepSetValue, isEmpty, _each, logMessage, logWarn, isBoolean, isNumber, isPlainObject, isFn, setScriptAttributes } from '../src/utils.js'; import { config } from '../src/config.js'; import { Renderer } from '../src/Renderer.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { VIDEO } from '../src/mediaTypes.js'; import { loadExternalScript } from '../src/adloader.js'; -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - * @typedef {import('../src/adapters/bidderFactory.js').ServerRequest} ServerRequest - */ - const BIDDER_CODE = 'spotx'; const URL = 'https://search.spotxchange.com/openrtb/2.3/dados/'; const ORTB_VERSION = '2.3'; diff --git a/modules/ssmasBidAdapter.js b/modules/ssmasBidAdapter.js deleted file mode 100644 index 0b70a80e757..00000000000 --- a/modules/ssmasBidAdapter.js +++ /dev/null @@ -1,133 +0,0 @@ -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'; - -export const SSMAS_CODE = 'ssmas'; -const SSMAS_SERVER = 'ads.ssmas.com'; -export const SSMAS_ENDPOINT = `https://${SSMAS_SERVER}/ortb`; -const SYNC_URL = `https://sync.ssmas.com/user_sync`; -export const SSMAS_REQUEST_METHOD = 'POST'; -const GDPR_VENDOR_ID = 1183; - -export const ssmasOrtbConverter = ortbConverter({ - context: { - netRevenue: true, - ttl: 300, - mediaType: BANNER, - }, - imp(buildImp, bidRequest, context) { - const imp = buildImp(bidRequest, context); - deepSetValue(imp, 'ext.placementId', bidRequest.params.placementId); - return imp; - }, -}); - -export const spec = { - code: SSMAS_CODE, - supportedMediaTypes: [BANNER], - gvlid: GDPR_VENDOR_ID, - - isBidRequestValid: (bid) => { - return !!bid.params.placementId && !!bid.bidId && bid.bidder === SSMAS_CODE; - }, - - buildRequests: (bidRequests, bidderRequest) => { - const data = ssmasOrtbConverter.toORTB({ bidRequests, bidderRequest }); - - const options = { - contentType: 'application/json', - withCredentials: false, - }; - - data.imp && data.imp.forEach(imp => { - if (imp.ext && imp.ext.placementId) { - imp.tagId = imp.ext.placementId; - } - }); - - data.regs = data.regs || {}; - data.regs.ext = data.regs.ext || {}; - - if (bidderRequest.gdprConsent) { - data.regs.ext.consent = bidderRequest.gdprConsent.consentString; - data.regs.ext.gdpr = bidderRequest.gdprConsent.gdprApplies ? 1 : 0; - } - if (bidderRequest.uspConsent) { - data.regs.ext.consent = bidderRequest.uspConsent.consentString; - data.regs.ext.ccpa = 1; - } - if (config.getConfig('coppa') === true) { - data.regs.coppa = 1; - } - - return [ - { - method: SSMAS_REQUEST_METHOD, - url: SSMAS_ENDPOINT, - data, - options, - }, - ]; - }, - - interpretResponse: (serverResponse, bidRequest) => { - const bids = ssmasOrtbConverter.fromORTB({ - response: serverResponse.body, - request: bidRequest.data, - }).bids; - - return bids.filter((bid) => { - return bid.cpm > 0; - }); - }, - - onBidWon: (bid) => { - if (bid.burl) { - triggerPixel(bid.burl); - } - }, - - getUserSyncs: ( - syncOptions, - serverResponses, - gdprConsent, - uspConsent - ) => { - const syncs = []; - - let params = ['pbjs=1']; - - if (gdprConsent) { - if (typeof gdprConsent.gdprApplies === 'boolean') { - params.push(`gdpr=${Boolean(gdprConsent.gdprApplies)}&gdpr_consent=${ - gdprConsent.consentString - }`); - } else { - params.push(`gdpr_consent=${gdprConsent.consentString}`); - } - } - - if (uspConsent && uspConsent.consentString) { - params.push(`ccpa_consent=${uspConsent.consentString}`); - } - - if (syncOptions.iframeEnabled && serverResponses.length > 0) { - syncs.push({ - type: 'iframe', - url: `${SYNC_URL}/iframe?${params.join('&')}` - }); - } - - // if (syncOptions.pixelEnabled && serverResponses.length > 0) { - // syncs.push({ - // type: 'image', - // url: `${SYNC_URL}/image?${params.join('&')}` - // }); - // } - return syncs; - }, -}; - -registerBidder(spec); diff --git a/modules/ssmasBidAdapter.md b/modules/ssmasBidAdapter.md deleted file mode 100644 index 1b15764a54d..00000000000 --- a/modules/ssmasBidAdapter.md +++ /dev/null @@ -1,35 +0,0 @@ -# Overview - -Module Name: SSMas Bidder Adapter -Module Type: Bidder Adapter -Maintainer: hzchen.work@gmail.com - -# Description - -Module that connects to Sem Seo & Mas header bidding endpoint to fetch bids. -Supports Banner -Supported currencies: EUR - -Required parameters: -- placement id - -# Test Parameters -``` -var adUnits = [ - // Banner adUnit - { - code: 'banner-div', - mediaTypes: { - banner: { - sizes: [[300, 250]] - } - }, - bids: [{ - bidder: 'ssmas', - params: { - placementId: "10336" - } - }] - } -]; -``` diff --git a/modules/sspBCBidAdapter.js b/modules/sspBCBidAdapter.js index c351b76d7ea..93ce26e7a48 100644 --- a/modules/sspBCBidAdapter.js +++ b/modules/sspBCBidAdapter.js @@ -12,14 +12,13 @@ const SYNC_URL = 'https://ssp.wp.pl/bidder/usersync'; const NOTIFY_URL = 'https://ssp.wp.pl/bidder/notify'; const GVLID = 676; const TMAX = 450; -const BIDDER_VERSION = '5.92'; +const BIDDER_VERSION = '5.8'; const DEFAULT_CURRENCY = 'PLN'; const W = window; const { navigator } = W; const oneCodeDetection = {}; const adUnitsCalled = {}; const adSizesCalled = {}; -const bidderRequestsMap = {}; const pageView = {}; var consentApiVersion; @@ -38,7 +37,7 @@ var nativeAssetMap = { /** * return native asset type, based on asset id - * @param {number} id - native asset id + * @param {int} id - native asset id * @returns {string} asset type */ const getNativeAssetType = id => { @@ -94,17 +93,19 @@ const getNotificationPayload = bidData => { const bids = isArray(bidData) ? bidData : [bidData]; if (bids.length > 0) { let result = { + requestId: undefined, siteId: [], slotId: [], tagid: [], } bids.forEach(bid => { - const { adUnitCode, cpm, creativeId, meta, mediaType, params: bidParams, bidderRequestId, requestId, timeout } = bid; + const { adUnitCode, auctionId, cpm, creativeId, meta, params: bidParams, requestId, timeout } = bid; const params = unpackParams(bidParams); // basic notification data const bidBasicData = { - requestId: bidderRequestId || bidderRequestsMap[requestId], + // TODO: fix auctionId leak: https://github.com/prebid/Prebid.js/issues/9781 + requestId: auctionId || result.requestId, timeout: timeout || result.timeout, pvid: pageView.id, } @@ -126,14 +127,12 @@ const getNotificationPayload = bidData => { if (cpm) { // non-empty bid data - const { advertiserDomains = [], networkName, pricepl } = meta; const bidNonEmptyData = { cpm, - cpmpl: pricepl, + cpmpl: meta && meta.pricepl, creativeId, - adomain: advertiserDomains[0], - adtype: mediaType, - networkName, + adomain: meta && meta.advertiserDomains && meta.advertiserDomains[0], + networkName: meta && meta.networkName, } result = { ...result, ...bidNonEmptyData } } @@ -164,7 +163,7 @@ const applyClientHints = ortbRequest => { Check / generate page view id Should be generated dureing first call to applyClientHints(), and re-generated if pathname has changed - */ + */ if (!pageView.id || location.pathname !== pageView.path) { pageView.path = location.pathname; pageView.id = Math.floor(1E20 * Math.random()).toString(); @@ -199,22 +198,6 @@ const applyClientHints = ortbRequest => { ortbRequest.user = { ...ortbRequest.user, ...ch }; }; -const applyTopics = (validBidRequest, ortbRequest) => { - const userData = validBidRequest.ortb2?.user?.data || []; - const topicsData = userData.filter(dataObj => { - const segtax = dataObj.ext?.segtax; - return segtax >= 600 && segtax <= 609; - })[0]; - - // format topics obj for exchange - if (topicsData) { - topicsData.id = `${topicsData.ext.segtax}`; - topicsData.name = 'topics'; - delete (topicsData.ext); - ortbRequest.user.data.push(topicsData); - } -}; - const applyUserIds = (validBidRequest, ortbRequest) => { const eids = validBidRequest.userIdAsEids if (eids && eids.length) { @@ -464,15 +447,10 @@ var mapVideo = (slot, videoFromBid) => { }; const mapImpression = slot => { - const { adUnitCode, bidderRequestId, bidId, params = {}, ortb2Imp = {} } = slot; + const { adUnitCode, bidId, params = {}, ortb2Imp = {} } = slot; const { id, siteId, video } = params; const { ext = {} } = ortb2Imp; - /* - store bidId <-> bidderRequestId mapping for bidWon notification - */ - bidderRequestsMap[bidId] = bidderRequestId; - /* check max size for this imp, and check/store number this size was called (for current view) send this info as ext.pbsize @@ -506,7 +484,7 @@ const mapImpression = slot => { } const isVideoAd = bid => { - const xmlTester = new RegExp(/^<\?xml| { return bid.admNative || (bid.adm && bid.adm.match(xmlTester)); } -const parseNative = (nativeData, adUnitCode) => { +const parseNative = (nativeData) => { const { link = {}, imptrackers: impressionTrackers, jstracker } = nativeData; const { url: clickUrl, clicktrackers: clickTrackers = [] } = link; - const macroReplacer = tracker => tracker.replace(new RegExp('%native_dom_id%', 'g'), adUnitCode); - let javascriptTrackers = isArray(jstracker) ? jstracker : jstracker && [jstracker]; - - // replace known macros in js trackers - javascriptTrackers = javascriptTrackers && javascriptTrackers.map(macroReplacer); const result = { clickUrl, clickTrackers, impressionTrackers, - javascriptTrackers, + javascriptTrackers: isArray(jstracker) ? jstracker : jstracker && [jstracker], }; nativeData.assets.forEach(asset => { @@ -654,8 +627,6 @@ const spec = { return true; }, buildRequests(validBidRequests, bidderRequest) { - logWarn('DEBUG: buildRequests', bidderRequest.auctionId, bidderRequest.bidderRequestId); - // convert Native ORTB definition to old-style prebid native definition validBidRequests = convertOrtbRequestToProprietaryNative(validBidRequests); @@ -673,7 +644,7 @@ const spec = { const ref = bidderRequest.refererInfo.ref; const payload = { - id: bidderRequest.bidderRequestId, + id: bidderRequest.auctionId, site: { id: siteId ? `${siteId}` : undefined, publisher: publisherId ? { id: publisherId } : undefined, @@ -698,7 +669,6 @@ const spec = { applyGdpr(bidderRequest, payload); applyClientHints(payload); applyUserIds(validBidRequests[0], payload); - applyTopics(bidderRequest, payload); return { method: 'POST', @@ -755,7 +725,6 @@ const spec = { if (bidRequest && site.id && !strIncludes(site.id, 'bidid')) { // found a matching request; add this bid - const { adUnitCode } = bidRequest; // store site data for future notification oneCodeDetection[bidId] = [site.id, site.slot]; @@ -791,7 +760,7 @@ const spec = { // check native object try { const nativeData = serverBid.admNative || JSON.parse(serverBid.adm).native; - bid.native = parseNative(nativeData, adUnitCode); + bid.native = parseNative(nativeData); bid.width = 1; bid.height = 1; } catch (err) { @@ -838,15 +807,6 @@ const spec = { } }, - onBidViewable(bid) { - const payload = getNotificationPayload(bid); - if (payload) { - payload.event = 'bidViewable'; - sendNotification(payload); - return payload; - } - }, - onBidWon(bid) { const payload = getNotificationPayload(bid); if (payload) { diff --git a/modules/stroeerCoreBidAdapter.js b/modules/stroeerCoreBidAdapter.js index 307a50c7f78..0737bec5a12 100644 --- a/modules/stroeerCoreBidAdapter.js +++ b/modules/stroeerCoreBidAdapter.js @@ -1,4 +1,4 @@ -import { buildUrl, deepAccess, generateUUID, getWindowSelf, getWindowTop, isEmpty, isStr, logWarn } from '../src/utils.js'; +import {buildUrl, deepAccess, getWindowSelf, getWindowTop, isEmpty, isStr, logWarn} from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {BANNER, VIDEO} from '../src/mediaTypes.js'; import {find} from '../src/polyfill.js'; @@ -50,7 +50,8 @@ export const spec = { const refererInfo = bidderRequest.refererInfo; const basePayload = { - id: generateUUID(), + // TODO: fix auctionId leak: https://github.com/prebid/Prebid.js/issues/9781 + id: bidderRequest.auctionId, ref: refererInfo.ref, ssl: isSecureWindow(), mpa: isMainPageAccessible(), diff --git a/modules/stvBidAdapter.js b/modules/stvBidAdapter.js index 5cffc5853b5..8d368b996fc 100644 --- a/modules/stvBidAdapter.js +++ b/modules/stvBidAdapter.js @@ -3,10 +3,6 @@ import {registerBidder} from '../src/adapters/bidderFactory.js'; import {BANNER, VIDEO} from '../src/mediaTypes.js'; import {includes} from '../src/polyfill.js'; -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - */ - const BIDDER_CODE = 'stv'; const ENDPOINT_URL = 'https://ads.smartstream.tv/r/'; const ENDPOINT_URL_DEV = 'https://ads.smartstream.tv/r/'; @@ -55,7 +51,6 @@ export const spec = { bid_id: bidId, pbver: '$prebid.version$', schain: '', - uids: '', }; if (!isVideoRequest(bidRequest)) { payload._f = 'html'; @@ -66,11 +61,6 @@ export const spec = { delete payload.schain; } - payload.uids = serializeUids(bidRequest); - if (payload.uids == '') { - delete payload.uids; - } - payload.pfilter = { ...params }; delete payload.pfilter.placement; if (params.bcat !== undefined) { delete payload.pfilter.bcat; } @@ -211,7 +201,7 @@ function objectToQueryString(obj, prefix) { let v = obj[p]; str.push((v !== null && typeof v === 'object') ? objectToQueryString(v, k) - : (k == 'schain' || k == 'uids' ? k + '=' + v : encodeURIComponent(k) + '=' + encodeURIComponent(v))); + : (k == 'schain' ? k + '=' + v : encodeURIComponent(k) + '=' + encodeURIComponent(v))); } } return str.join('&'); @@ -237,60 +227,13 @@ function serializeSChain(schain) { ret += encodeURIComponent(node.name ?? ''); ret += ','; ret += encodeURIComponent(node.domain ?? ''); - if (node.ext) { - ret += ','; - ret += encodeURIComponent(node.ext ?? ''); - } + ret += ','; + ret += encodeURIComponent(node.ext ?? ''); } return ret; } -function serializeUids(bidRequest) { - let uids = []; - - let id5 = deepAccess(bidRequest, 'userId.id5id.uid'); - if (id5) { - uids.push(encodeURIComponent('id5:' + id5)); - let id5Linktype = deepAccess(bidRequest, 'userId.id5id.ext.linkType'); - if (id5Linktype) { - uids.push(encodeURIComponent('id5_linktype:' + id5Linktype)); - } - } - let netId = deepAccess(bidRequest, 'userId.netId'); - if (netId) { - uids.push(encodeURIComponent('netid:' + netId)); - } - let uId2 = deepAccess(bidRequest, 'userId.uid2.id'); - if (uId2) { - uids.push(encodeURIComponent('uid2:' + uId2)); - } - let sharedId = deepAccess(bidRequest, 'userId.sharedid.id'); - if (sharedId) { - uids.push(encodeURIComponent('sharedid:' + sharedId)); - } - let liverampId = deepAccess(bidRequest, 'userId.idl_env'); - if (liverampId) { - uids.push(encodeURIComponent('liverampid:' + liverampId)); - } - let criteoId = deepAccess(bidRequest, 'userId.criteoId'); - if (criteoId) { - uids.push(encodeURIComponent('criteoid:' + criteoId)); - } - // documentation missing... - let utiqId = deepAccess(bidRequest, 'userId.utiq.id'); - if (utiqId) { - uids.push(encodeURIComponent('utiq:' + utiqId)); - } else { - utiqId = deepAccess(bidRequest, 'userId.utiq'); - if (utiqId) { - uids.push(encodeURIComponent('utiq:' + utiqId)); - } - } - - return uids.join(','); -} - /** * Check if it's a banner bid request * diff --git a/modules/sublimeBidAdapter.js b/modules/sublimeBidAdapter.js index a29265ce9cd..db2b02aaef1 100644 --- a/modules/sublimeBidAdapter.js +++ b/modules/sublimeBidAdapter.js @@ -2,12 +2,6 @@ import { logInfo, generateUUID, formatQS, triggerPixel, deepAccess } from '../sr import { registerBidder } from '../src/adapters/bidderFactory.js'; import { config } from '../src/config.js'; -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - * @typedef {import('../src/adapters/bidderFactory.js').ServerRequest} ServerRequest - */ - const BIDDER_CODE = 'sublime'; const BIDDER_GVLID = 114; const DEFAULT_BID_HOST = 'pbjs.sskzlabs.com'; diff --git a/modules/taboolaBidAdapter.js b/modules/taboolaBidAdapter.js index 3b83514b6fd..8a42013b0be 100644 --- a/modules/taboolaBidAdapter.js +++ b/modules/taboolaBidAdapter.js @@ -3,23 +3,18 @@ import {registerBidder} from '../src/adapters/bidderFactory.js'; import {BANNER} from '../src/mediaTypes.js'; import {config} from '../src/config.js'; -import {deepAccess, deepSetValue, getWindowSelf, replaceAuctionPrice} from '../src/utils.js'; +import {deepAccess, getWindowSelf, replaceAuctionPrice} from '../src/utils.js'; import {getStorageManager} from '../src/storageManager.js'; import {ajax} from '../src/ajax.js'; -import {ortbConverter} from '../libraries/ortbConverter/converter.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 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'; const STORAGE_KEY = `taboola global:${USER_ID}`; const COOKIE_KEY = 'trc_cookie_storage'; -const TGID_COOKIE_KEY = 't_gid'; -const TBLA_ID_COOKIE_KEY = 'tbla_id'; -export const EVENT_ENDPOINT = 'https://beacon.bidder.taboola.com'; /** * extract User Id by that order: @@ -43,18 +38,10 @@ export const userData = { const {cookiesAreEnabled, getCookie} = userData.storageManager; if (cookiesAreEnabled()) { const cookieData = getCookie(COOKIE_KEY); - let userId = userData.getCookieDataByKey(cookieData, USER_ID); + const userId = userData.getCookieDataByKey(cookieData, USER_ID); if (userId) { return userId; } - userId = getCookie(TGID_COOKIE_KEY); - if (userId) { - return userId; - } - const tblaId = getCookie(TBLA_ID_COOKIE_KEY); - if (tblaId) { - return tblaId; - } } }, getCookieDataByKey(cookieData, key) { @@ -82,30 +69,6 @@ export const internal = { } } -const converter = ortbConverter({ - context: { - netRevenue: true, - mediaType: BANNER, - ttl: 300 - }, - imp(buildImp, bidRequest, context) { - let imp = buildImp(bidRequest, context); - fillTaboolaImpData(bidRequest, imp); - return imp; - }, - request(buildRequest, imps, bidderRequest, context) { - const reqData = buildRequest(imps, bidderRequest, context); - fillTaboolaReqData(bidderRequest, context.bidRequests[0], reqData) - return reqData; - }, - bidResponse(buildBidResponse, bid, context) { - const bidResponse = buildBidResponse(bid, context); - bidResponse.nurl = bid.nurl; - bidResponse.ad = replaceAuctionPrice(bid.adm, bid.price); - return bidResponse - } -}); - export const spec = { supportedMediaTypes: [BANNER], gvlid: GVLID, @@ -118,35 +81,85 @@ export const spec = { }, buildRequests: (validBidRequests, bidderRequest) => { const [bidRequest] = validBidRequests; - const data = converter.toORTB({bidderRequest: bidderRequest, bidRequests: validBidRequests}); + const {refererInfo, gdprConsent = {}, uspConsent} = bidderRequest; const {publisherId} = bidRequest.params; - const url = END_POINT_URL + '?publisher=' + publisherId; + const site = getSiteProperties(bidRequest.params, refererInfo, bidderRequest.ortb2); + const device = {ua: navigator.userAgent}; + const imps = getImps(validBidRequests); + const user = { + buyeruid: userData.getUserId(gdprConsent, uspConsent), + ext: {} + }; + const regs = { + coppa: 0, + ext: {} + }; + + if (gdprConsent.gdprApplies) { + user.ext.consent = bidderRequest.gdprConsent.consentString; + regs.ext.gdpr = 1; + } + + if (uspConsent) { + regs.ext.us_privacy = uspConsent; + } + + if (bidderRequest.ortb2?.regs?.gpp) { + regs.ext.gpp = bidderRequest.ortb2.regs.gpp; + regs.ext.gpp_sid = bidderRequest.ortb2.regs.gpp_sid; + } + + if (config.getConfig('coppa')) { + regs.coppa = 1; + } + + const ortb2 = bidderRequest.ortb2 || { + bcat: [], + badv: [], + wlang: [] + }; + + const request = { + id: bidderRequest.bidderRequestId, + imp: imps, + site, + device, + source: {fd: 1}, + tmax: bidderRequest.timeout, + bcat: ortb2.bcat || bidRequest.params.bcat || [], + badv: ortb2.badv || bidRequest.params.badv || [], + wlang: ortb2.wlang || bidRequest.params.wlang || [], + user, + regs, + ext: { + pageType: ortb2?.ext?.data?.pageType || ortb2?.ext?.data?.section || bidRequest.params.pageType + } + }; + + const url = [END_POINT_URL, publisherId].join('/'); return { url, method: 'POST', - data: data, + data: JSON.stringify(request), bids: validBidRequests, options: { withCredentials: false }, }; }, - interpretResponse: (serverResponse, request) => { - if (!request || !request.bids || !request.data) { + interpretResponse: (serverResponse, {bids}) => { + if (!bids) { return []; } - if (!serverResponse || !serverResponse.body) { - return []; - } + const {bidResponses, cur: currency} = getBidResponses(serverResponse); - if (!serverResponse.body.seatbid || !serverResponse.body.seatbid.length || !serverResponse.body.seatbid[0].bid || !serverResponse.body.seatbid[0].bid.length) { + if (!bidResponses) { return []; } - const bids = converter.fromORTB({response: serverResponse.body, request: request.data}).bids; - return bids; + return bidResponses.map((bidResponse) => getBid(bids, currency, bidResponse)).filter(Boolean); }, onBidWon: (bid) => { if (bid.nurl) { @@ -169,13 +182,6 @@ export const spec = { queryParams.push('gpp=' + encodeURIComponent(gppConsent)); } - if (syncOptions.iframeEnabled) { - syncs.push({ - type: 'iframe', - url: USER_SYNC_IFRAME_URL + (queryParams.length ? '?' + queryParams.join('&') : '') - }); - } - if (syncOptions.pixelEnabled) { syncs.push({ type: 'image', @@ -184,13 +190,6 @@ export const spec = { } return syncs; }, - onTimeout: (timeoutData) => { - ajax(EVENT_ENDPOINT + '/timeout', null, JSON.stringify(timeoutData), {method: 'POST'}); - }, - - onBidderError: ({ error, bidderRequest }) => { - ajax(EVENT_ENDPOINT + '/bidError', null, JSON.stringify(error, bidderRequest), {method: 'POST'}); - }, }; function getSiteProperties({publisherId}, refererInfo, ortb2) { @@ -210,76 +209,34 @@ function getSiteProperties({publisherId}, refererInfo, ortb2) { } } -function fillTaboolaReqData(bidderRequest, bidRequest, data) { - const {refererInfo, gdprConsent = {}, uspConsent} = bidderRequest; - const site = getSiteProperties(bidRequest.params, refererInfo, bidderRequest.ortb2); - const device = {ua: navigator.userAgent}; - const user = { - buyeruid: userData.getUserId(gdprConsent, uspConsent), - ext: {} - }; - const regs = { - coppa: 0, - ext: {} - }; - - if (gdprConsent.gdprApplies) { - user.ext.consent = bidderRequest.gdprConsent.consentString; - regs.ext.gdpr = 1; - } - - if (uspConsent) { - regs.ext.us_privacy = uspConsent; - } - - if (bidderRequest.ortb2?.regs?.gpp) { - regs.ext.gpp = bidderRequest.ortb2.regs.gpp; - regs.ext.gpp_sid = bidderRequest.ortb2.regs.gpp_sid; - } - - if (config.getConfig('coppa')) { - regs.coppa = 1; - } - - const ortb2 = bidderRequest.ortb2 || { - bcat: [], - badv: [], - wlang: [] - }; - - data.id = bidderRequest.bidderRequestId; - data.site = site; - data.device = device; - data.source = {fd: 1}; - data.tmax = (bidderRequest.timeout == undefined) ? undefined : parseInt(bidderRequest.timeout); - data.bcat = ortb2.bcat || bidRequest.params.bcat || []; - data.badv = ortb2.badv || bidRequest.params.badv || []; - data.wlang = ortb2.wlang || bidRequest.params.wlang || []; - data.user = user; - data.regs = regs; - deepSetValue(data, 'ext.pageType', ortb2?.ext?.data?.pageType || ortb2?.ext?.data?.section || bidRequest.params.pageType); -} - -function fillTaboolaImpData(bid, imp) { - const {tagId, position} = bid.params; - imp.banner = getBanners(bid, position); - imp.tagid = tagId; - - if (typeof bid.getFloor === 'function') { - const floorInfo = bid.getFloor({ - currency: CURRENCY, - size: '*' - }); - if (typeof floorInfo === 'object' && floorInfo.currency === CURRENCY && !isNaN(parseFloat(floorInfo.floor))) { - imp.bidfloor = parseFloat(floorInfo.floor); - imp.bidfloorcur = CURRENCY; +function getImps(validBidRequests) { + return validBidRequests.map((bid, id) => { + const {tagId, position} = bid.params; + const imp = { + id: id + 1, + banner: getBanners(bid, position), + tagid: tagId } - } else { - const {bidfloor = null, bidfloorcur = CURRENCY} = bid.params; - imp.bidfloor = bidfloor; - imp.bidfloorcur = bidfloorcur; - } - deepSetValue(imp, 'ext.gpid', deepAccess(bid, 'ortb2Imp.ext.gpid')); + if (typeof bid.getFloor === 'function') { + const floorInfo = bid.getFloor({ + currency: CURRENCY, + mediaType: BANNER, + size: '*' + }); + if (typeof floorInfo === 'object' && floorInfo.currency === CURRENCY && !isNaN(parseFloat(floorInfo.floor))) { + imp.bidfloor = parseFloat(floorInfo.floor); + imp.bidfloorcur = CURRENCY; + } + } else { + const {bidfloor = null, bidfloorcur = CURRENCY} = bid.params; + imp.bidfloor = bidfloor; + imp.bidfloorcur = bidfloorcur; + } + imp['ext'] = { + gpid: deepAccess(bid, 'ortb2Imp.ext.gpid') + } + return imp; + }); } function getBanners(bid, pos) { @@ -300,4 +257,51 @@ function getSizes(sizes) { } } +function getBidResponses({body}) { + if (!body) { + return []; + } + + const {seatbid, cur} = body; + + if (!seatbid.length || !seatbid[0].bid || !seatbid[0].bid.length) { + return []; + } + + return { + bidResponses: seatbid[0].bid, + cur + }; +} + +function getBid(bids, currency, bidResponse) { + if (!bidResponse) { + return; + } + let { + price: cpm, nurl, crid: creativeId, adm: ad, w: width, h: height, exp: ttl, adomain: advertiserDomains, meta = {} + } = bidResponse; + let requestId = bids[bidResponse.impid - 1].bidId; + if (advertiserDomains && advertiserDomains.length > 0) { + meta.advertiserDomains = advertiserDomains + } + + ad = replaceAuctionPrice(ad, cpm); + + return { + requestId, + ttl, + mediaType: BANNER, + cpm, + creativeId, + currency, + ad, + width, + height, + meta, + nurl, + netRevenue: true + }; +} + registerBidder(spec); diff --git a/modules/tagorasBidAdapter.js b/modules/tagorasBidAdapter.js deleted file mode 100644 index 0138ba3daf9..00000000000 --- a/modules/tagorasBidAdapter.js +++ /dev/null @@ -1,342 +0,0 @@ -import {_each, deepAccess, parseSizesInput, parseUrl, uniques, isFn} from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER, VIDEO} from '../src/mediaTypes.js'; -import {getStorageManager} from '../src/storageManager.js'; -import {config} from '../src/config.js'; - -const DEFAULT_SUB_DOMAIN = 'exchange'; -const BIDDER_CODE = 'tagoras'; -const BIDDER_VERSION = '1.0.0'; -const CURRENCY = 'USD'; -const TTL_SECONDS = 60 * 5; -const UNIQUE_DEAL_ID_EXPIRY = 1000 * 60 * 15; -const storage = getStorageManager({bidderCode: BIDDER_CODE}); - -function getTopWindowQueryParams() { - try { - const parsedUrl = parseUrl(window.top.document.URL, {decodeSearchAsString: true}); - return parsedUrl.search; - } catch (e) { - return ''; - } -} - -export function createDomain(subDomain = DEFAULT_SUB_DOMAIN) { - return `https://${subDomain}.tagoras.io`; -} - -export function extractCID(params) { - return params.cId || params.CID || params.cID || params.CId || params.cid || params.ciD || params.Cid || params.CiD; -} - -export function extractPID(params) { - return params.pId || params.PID || params.pID || params.PId || params.pid || params.piD || params.Pid || params.PiD; -} - -export function extractSubDomain(params) { - return params.subDomain || params.SubDomain || params.Subdomain || params.subdomain || params.SUBDOMAIN || params.subDOMAIN; -} - -function isBidRequestValid(bid) { - const params = bid.params || {}; - return !!(extractCID(params) && extractPID(params)); -} - -function buildRequest(bid, topWindowUrl, sizes, bidderRequest, bidderTimeout) { - const { - params, - bidId, - userId, - adUnitCode, - schain, - mediaTypes, - ortb2Imp, - bidderRequestId, - bidRequestsCount, - bidderRequestsCount, - bidderWinsCount - } = bid; - let {bidFloor, ext} = params; - const hashUrl = hashCode(topWindowUrl); - const uniqueDealId = getUniqueDealId(hashUrl); - const cId = extractCID(params); - const pId = extractPID(params); - const subDomain = extractSubDomain(params); - - const gpid = deepAccess(bid, 'ortb2Imp.ext.gpid', deepAccess(bid, 'ortb2Imp.ext.data.pbadslot', '')); - - if (isFn(bid.getFloor)) { - const floorInfo = bid.getFloor({ - currency: 'USD', - mediaType: '*', - size: '*' - }); - - if (floorInfo.currency === 'USD') { - bidFloor = floorInfo.floor; - } - } - - let data = { - url: encodeURIComponent(topWindowUrl), - uqs: getTopWindowQueryParams(), - cb: Date.now(), - bidFloor: bidFloor, - bidId: bidId, - referrer: bidderRequest.refererInfo.ref, - adUnitCode: adUnitCode, - publisherId: pId, - sizes: sizes, - uniqueDealId: uniqueDealId, - bidderVersion: BIDDER_VERSION, - prebidVersion: '$prebid.version$', - res: `${screen.width}x${screen.height}`, - schain: schain, - mediaTypes: mediaTypes, - gpid: gpid, - transactionId: ortb2Imp?.ext?.tid, - bidderRequestId: bidderRequestId, - bidRequestsCount: bidRequestsCount, - bidderRequestsCount: bidderRequestsCount, - bidderWinsCount: bidderWinsCount, - bidderTimeout: bidderTimeout - }; - - appendUserIdsToRequestPayload(data, userId); - - const sua = deepAccess(bidderRequest, 'ortb2.device.sua'); - - if (sua) { - data.sua = sua; - } - - if (bidderRequest.gdprConsent) { - if (bidderRequest.gdprConsent.consentString) { - data.gdprConsent = bidderRequest.gdprConsent.consentString; - } - if (bidderRequest.gdprConsent.gdprApplies !== undefined) { - data.gdpr = bidderRequest.gdprConsent.gdprApplies ? 1 : 0; - } - } - if (bidderRequest.uspConsent) { - data.usPrivacy = bidderRequest.uspConsent; - } - - if (bidderRequest.gppConsent) { - data.gppString = bidderRequest.gppConsent.gppString; - data.gppSid = bidderRequest.gppConsent.applicableSections; - } else if (bidderRequest.ortb2?.regs?.gpp) { - data.gppString = bidderRequest.ortb2.regs.gpp; - data.gppSid = bidderRequest.ortb2.regs.gpp_sid; - } - - const dto = { - method: 'POST', - url: `${createDomain(subDomain)}/prebid/multi/${cId}`, - data: data - }; - - _each(ext, (value, key) => { - dto.data['ext.' + key] = value; - }); - - return dto; -} - -function appendUserIdsToRequestPayload(payloadRef, userIds) { - let key; - _each(userIds, (userId, idSystemProviderName) => { - key = `uid.${idSystemProviderName}`; - - switch (idSystemProviderName) { - case 'digitrustid': - payloadRef[key] = deepAccess(userId, 'data.id'); - break; - case 'lipb': - payloadRef[key] = userId.lipbid; - break; - case 'parrableId': - payloadRef[key] = userId.eid; - break; - case 'id5id': - payloadRef[key] = userId.uid; - break; - default: - payloadRef[key] = userId; - } - }); -} - -function buildRequests(validBidRequests, bidderRequest) { - const topWindowUrl = bidderRequest.refererInfo.page || bidderRequest.refererInfo.topmostLocation; - const bidderTimeout = config.getConfig('bidderTimeout'); - const requests = []; - validBidRequests.forEach(validBidRequest => { - const sizes = parseSizesInput(validBidRequest.sizes); - const request = buildRequest(validBidRequest, topWindowUrl, sizes, bidderRequest, bidderTimeout); - requests.push(request); - }); - return requests; -} - -function interpretResponse(serverResponse, request) { - if (!serverResponse || !serverResponse.body) { - return []; - } - const {bidId} = request.data; - const {results} = serverResponse.body; - - let output = []; - - try { - results.forEach(result => { - const { - creativeId, - ad, - price, - exp, - width, - height, - currency, - metaData, - advertiserDomains, - mediaType = BANNER - } = result; - if (!ad || !price) { - return; - } - - const response = { - requestId: bidId, - cpm: price, - width: width, - height: height, - creativeId: creativeId, - currency: currency || CURRENCY, - netRevenue: true, - ttl: exp || TTL_SECONDS, - }; - - if (metaData) { - Object.assign(response, { - meta: metaData - }) - } else { - Object.assign(response, { - meta: { - advertiserDomains: advertiserDomains || [] - } - }) - } - - if (mediaType === BANNER) { - Object.assign(response, { - ad: ad, - }); - } else { - Object.assign(response, { - vastXml: ad, - mediaType: VIDEO - }); - } - output.push(response); - }); - return output; - } catch (e) { - return []; - } -} - -function getUserSyncs(syncOptions, responses, gdprConsent = {}, uspConsent = '', gppConsent = {}) { - let syncs = []; - const {iframeEnabled, pixelEnabled} = syncOptions; - const {gdprApplies, consentString = ''} = gdprConsent; - const {gppString, applicableSections} = gppConsent; - - const cidArr = responses.filter(resp => deepAccess(resp, 'body.cid')).map(resp => resp.body.cid).filter(uniques); - let params = `?cid=${encodeURIComponent(cidArr.join(','))}&gdpr=${gdprApplies ? 1 : 0}&gdpr_consent=${encodeURIComponent(consentString || '')}&us_privacy=${encodeURIComponent(uspConsent || '')}` - - if (gppString && applicableSections?.length) { - params += '&gpp=' + encodeURIComponent(gppString); - params += '&gpp_sid=' + encodeURIComponent(applicableSections.join(',')); - } - - if (iframeEnabled) { - syncs.push({ - type: 'iframe', - url: `https://sync.tagoras.io/api/sync/iframe/${params}` - }); - } else if (pixelEnabled) { - syncs.push({ - type: 'image', - url: `https://sync.tagoras.io/api/sync/image/${params}` - }); - } - return syncs; -} - -export function hashCode(s, prefix = '_') { - const l = s.length; - let h = 0 - let i = 0; - if (l > 0) { - while (i < l) { - h = (h << 5) - h + s.charCodeAt(i++) | 0; - } - } - return prefix + h; -} - -export function getUniqueDealId(key, expiry = UNIQUE_DEAL_ID_EXPIRY) { - const storageKey = `u_${key}`; - const now = Date.now(); - const data = getStorageItem(storageKey); - let uniqueId; - - if (!data || !data.value || now - data.created > expiry) { - uniqueId = `${key}_${now.toString()}`; - setStorageItem(storageKey, uniqueId); - } else { - uniqueId = data.value; - } - - return uniqueId; -} - -export function getStorageItem(key) { - try { - return tryParseJSON(storage.getDataFromLocalStorage(key)); - } catch (e) { - } - - return null; -} - -export function setStorageItem(key, value, timestamp) { - try { - const created = timestamp || Date.now(); - const data = JSON.stringify({value, created}); - storage.setDataInLocalStorage(key, data); - } catch (e) { - } -} - -export function tryParseJSON(value) { - try { - return JSON.parse(value); - } catch (e) { - return value; - } -} - -export const spec = { - code: BIDDER_CODE, - version: BIDDER_VERSION, - supportedMediaTypes: [BANNER, VIDEO], - isBidRequestValid, - buildRequests, - interpretResponse, - getUserSyncs -}; - -registerBidder(spec); diff --git a/modules/tagorasBidAdapter.md b/modules/tagorasBidAdapter.md deleted file mode 100644 index 83290bff525..00000000000 --- a/modules/tagorasBidAdapter.md +++ /dev/null @@ -1,35 +0,0 @@ -# Overview - -**Module Name:** Tagoras Bidder Adapter - -**Module Type:** Bidder Adapter - -**Maintainer:** prebid@tagoras.io - -# Description - -Module that connects to Tagoras's demand sources. - -# Test Parameters -```js -var adUnits = [ - { - code: 'test-ad', - sizes: [[300, 250]], - bids: [ - { - bidder: 'tagoras', - params: { - cId: '562524b21b1c1f08117fc7f9', - pId: '59ac17c192832d0011283fe3', - bidFloor: 0.0001, - ext: { - param1: 'loremipsum', - param2: 'dolorsitamet' - } - } - } - ] - } -]; -``` diff --git a/modules/tapadIdSystem.js b/modules/tapadIdSystem.js index 2c24f062791..149ba22eb92 100644 --- a/modules/tapadIdSystem.js +++ b/modules/tapadIdSystem.js @@ -56,12 +56,6 @@ export const tapadIdSubmodule = { ); } } - }, - eids: { - 'tapadId': { - source: 'tapad.com', - atype: 1 - }, } } submodule('userId', tapadIdSubmodule); diff --git a/modules/tappxBidAdapter.js b/modules/tappxBidAdapter.js index f0c275acfb6..13a16224a79 100644 --- a/modules/tappxBidAdapter.js +++ b/modules/tappxBidAdapter.js @@ -8,15 +8,11 @@ import { Renderer } from '../src/Renderer.js'; import {parseDomain} from '../src/refererDetection.js'; import {getGlobal} from '../src/prebidGlobal.js'; -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - */ - const BIDDER_CODE = 'tappx'; const GVLID_CODE = 628; const TTL = 360; const CUR = 'USD'; -const TAPPX_BIDDER_VERSION = '0.1.3'; +const TAPPX_BIDDER_VERSION = '0.1.2'; const TYPE_CNN = 'prebidjs'; const LOG_PREFIX = '[TAPPX]: '; const VIDEO_SUPPORT = ['instream', 'outstream']; @@ -57,7 +53,7 @@ export const spec = { * * @param {BidRequest} bid The bid params to validate. * @return boolean True if this is a valid bid, and false otherwise. - */ + */ isBidRequestValid: function(bid) { // bid.params.host if ((new RegExp(`^(vz.*|zz.*)\\.*$`, 'i')).test(bid.params.host)) { // New endpoint @@ -150,22 +146,22 @@ function validBasic(bid) { return false; } - if (!bid.params.tappxkey) { + if (bid.params.tappxkey == null) { logWarn(LOG_PREFIX, 'Please review the mandatory Tappxkey parameter.'); return false; } - if (!bid.params.host) { + if (bid.params.host == null) { logWarn(LOG_PREFIX, 'Please review the mandatory Host parameter.'); return false; } - let classicEndpoint = true; + let classicEndpoint = true if ((new RegExp(`^(vz.*|zz.*)\\.*$`, 'i')).test(bid.params.host)) { - classicEndpoint = false; + classicEndpoint = false } - if (classicEndpoint && !bid.params.endpoint) { + if (classicEndpoint && bid.params.endpoint == null) { logWarn(LOG_PREFIX, 'Please review the mandatory endpoint Tappx parameters.'); return false; } @@ -195,7 +191,7 @@ function validMediaType(bid) { */ function interpretBid(serverBid, request) { let bidReturned = { - requestId: request.bids?.bidId, + requestId: request.bids.bidId, cpm: serverBid.price, currency: serverBid.cur ? serverBid.cur : CUR, width: serverBid.w, @@ -210,7 +206,7 @@ function interpretBid(serverBid, request) { if (typeof serverBid.nurl != 'undefined') { bidReturned.nurl = serverBid.nurl } if (typeof serverBid.burl != 'undefined') { bidReturned.burl = serverBid.burl } - if (typeof request.bids?.mediaTypes !== 'undefined' && typeof request.bids?.mediaTypes.video !== 'undefined') { + if (typeof request.bids.mediaTypes !== 'undefined' && typeof request.bids.mediaTypes.video !== 'undefined') { bidReturned.vastXml = serverBid.adm; bidReturned.vastUrl = serverBid.lurl; bidReturned.ad = serverBid.adm; @@ -218,12 +214,13 @@ function interpretBid(serverBid, request) { bidReturned.width = serverBid.w; bidReturned.height = serverBid.h; - if (request.bids?.mediaTypes.video.context === 'outstream') { - if (!serverBid.ext.purl) { + if (request.bids.mediaTypes.video.context === 'outstream') { + const url = (serverBid.ext.purl) ? serverBid.ext.purl : false; + if (typeof url === 'undefined') { logWarn(LOG_PREFIX, 'Error getting player outstream from tappx'); return false; } - bidReturned.renderer = createRenderer(bidReturned, request, serverBid.ext.purl); + bidReturned.renderer = createRenderer(bidReturned, request, url); } } else { bidReturned.ad = serverBid.adm; @@ -231,19 +228,19 @@ function interpretBid(serverBid, request) { } if (typeof bidReturned.adomain !== 'undefined' || bidReturned.adomain !== null) { - bidReturned.meta = { advertiserDomains: request.bids?.adomain }; + bidReturned.meta = { advertiserDomains: request.bids.adomain }; } return bidReturned; } /** - * Build and makes the request - * - * @param {*} validBidRequests - * @param {*} bidderRequest - * @return response ad - */ +* Build and makes the request +* +* @param {*} validBidRequests +* @param {*} bidderRequest +* @return response ad +*/ function buildOneRequest(validBidRequests, bidderRequest) { let hostInfo = _getHostInfo(validBidRequests); const ENDPOINT = hostInfo.endpoint; @@ -255,7 +252,6 @@ function buildOneRequest(validBidRequests, bidderRequest) { const BIDEXTRA = deepAccess(validBidRequests, 'params.ext'); const bannerMediaType = deepAccess(validBidRequests, 'mediaTypes.banner'); const videoMediaType = deepAccess(validBidRequests, 'mediaTypes.video'); - const ORTB2 = deepAccess(validBidRequests, 'ortb2'); // let requests = []; let payload = {}; @@ -280,11 +276,7 @@ function buildOneRequest(validBidRequests, bidderRequest) { site.name = bundle; site.page = bidderRequest?.refererInfo?.page || deepAccess(validBidRequests, 'params.site.page') || bidderRequest?.refererInfo?.topmostLocation || window.location.href || bundle; site.domain = bundle; - try { - site.ref = bidderRequest?.refererInfo?.ref || window.top.document.referrer || ''; - } catch (e) { - site.ref = bidderRequest?.refererInfo?.ref || window.document.referrer || ''; - } + site.ref = bidderRequest?.refererInfo?.ref || window.top.document.referrer || ''; site.ext = {}; site.ext.is_amp = bidderRequest?.refererInfo?.isAmp || 0; site.ext.page_da = deepAccess(validBidRequests, 'params.site.page') || '-'; @@ -312,8 +304,6 @@ function buildOneRequest(validBidRequests, bidderRequest) { let h; if (bannerMediaType) { - if (!Array.isArray(bannerMediaType.sizes)) { logWarn(LOG_PREFIX, 'Banner sizes array not found.'); } - let banner = {}; w = bannerMediaType.sizes[0][0]; h = bannerMediaType.sizes[0][1]; @@ -351,9 +341,7 @@ function buildOneRequest(validBidRequests, bidderRequest) { } if ((video.w === undefined || video.w == null || video.w <= 0) || - (video.h === undefined || video.h == null || video.h <= 0)) { - if (!Array.isArray(videoMediaType.playerSize)) { logWarn(LOG_PREFIX, 'Video playerSize array not found.'); } - + (video.h === undefined || video.h == null || video.h <= 0)) { w = videoMediaType.playerSize[0][0]; h = videoMediaType.playerSize[0][1]; video.w = w; @@ -422,14 +410,6 @@ function buildOneRequest(validBidRequests, bidderRequest) { let geo = {}; geo.country = deepAccess(validBidRequests, 'params.geo.country'); // < Device object - let configGeo = {}; - configGeo.country = ORTB2?.device?.geo; - - if (typeof configGeo.country !== 'undefined') { - device.geo = configGeo; - } else if (typeof geo.country !== 'undefined') { - device.geo = geo; - }; // > GDPR let user = {}; @@ -511,7 +491,7 @@ function getLanguage() { function getOs() { let ua = navigator.userAgent; - if (ua.match(/Android/)) { return 'Android'; } else if (ua.match(/(iPhone|iPod|iPad)/)) { return 'iOS'; } else if (ua.indexOf('Mac OS X') != -1) { return 'macOS'; } else if (ua.indexOf('Windows') != -1) { return 'Windows'; } else if (ua.indexOf('Linux') != -1) { return 'Linux'; } else { return 'Unknown'; } + if (ua.indexOf('Windows') != -1) { return 'Windows'; } else if (ua.indexOf('Mac OS X') != -1) { return 'macOS'; } else if (ua.match(/Android/)) { return 'Android'; } else if (ua.match(/(iPhone|iPod|iPad)/)) { return 'iOS'; } else if (ua.indexOf('Linux') != -1) { return 'Linux'; } else { return 'Unknown'; } } function getVendor() { diff --git a/modules/tappxBidAdapter.md b/modules/tappxBidAdapter.md index 55f18531f28..677718c261c 100644 --- a/modules/tappxBidAdapter.md +++ b/modules/tappxBidAdapter.md @@ -92,20 +92,3 @@ Ads sizes available: [300,250], [320,50], [320,480], [480,320], [728,90], [768,1 } ]; ``` -### Configuration - -Use `setConfig` to configure this submodule ortb2.device.geo, this will allow geolocation -`Geo` object to bring First Party Information. - -```javascript -var TIMEOUT = 1000; -pbjs.setConfig({ - ortb2:{ - device:{ - geo:{ - country:'US' - } - } - } -}); -``` diff --git a/modules/targetVideoBidAdapter.js b/modules/targetVideoBidAdapter.js index 282f322c36a..1977686dd23 100644 --- a/modules/targetVideoBidAdapter.js +++ b/modules/targetVideoBidAdapter.js @@ -3,11 +3,6 @@ import {getBidRequest} from '../src/utils.js'; import {BANNER, VIDEO} from '../src/mediaTypes.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - */ - const SOURCE = 'pbjs'; const BIDDER_CODE = 'targetVideo'; const ENDPOINT_URL = 'https://ib.adnxs.com/ut/v3/prebid'; diff --git a/modules/teadsBidAdapter.js b/modules/teadsBidAdapter.js index 1c12b0e3968..6e1006ecf00 100644 --- a/modules/teadsBidAdapter.js +++ b/modules/teadsBidAdapter.js @@ -1,12 +1,7 @@ -import {getValue, logError, deepAccess, parseSizesInput, isArray, getBidIdParameter} from '../src/utils.js'; +import { getValue, logError, deepAccess, getBidIdParameter, parseSizesInput, isArray } 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 - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - */ - const BIDDER_CODE = 'teads'; const GVL_ID = 132; const ENDPOINT_URL = 'https://a.teads.tv/hb/bid-request'; @@ -50,23 +45,14 @@ export const spec = { */ buildRequests: function(validBidRequests, bidderRequest) { const bids = validBidRequests.map(buildRequestObject); - const topWindow = window.top; const payload = { referrer: getReferrerInfo(bidderRequest), pageReferrer: document.referrer, - pageTitle: getPageTitle().slice(0, 300), - pageDescription: getPageDescription().slice(0, 300), networkBandwidth: getConnectionDownLink(window.navigator), timeToFirstByte: getTimeToFirstByte(window), data: bids, deviceWidth: screen.width, - screenOrientation: screen.orientation?.type, - historyLength: topWindow.history?.length, - viewportHeight: topWindow.visualViewport?.height, - viewportWidth: topWindow.visualViewport?.width, - hardwareConcurrency: topWindow.navigator?.hardwareConcurrency, - deviceMemory: topWindow.navigator?.deviceMemory, hb_version: '$prebid.version$', ...getSharedViewerIdParameters(validBidRequests), ...getFirstPartyTeadsIdParameter(validBidRequests) @@ -184,32 +170,6 @@ function getReferrerInfo(bidderRequest) { return ref; } -function getPageTitle() { - try { - const ogTitle = window.top.document.querySelector('meta[property="og:title"]') - - return window.top.document.title || (ogTitle && ogTitle.content) || ''; - } catch (e) { - const ogTitle = document.querySelector('meta[property="og:title"]') - - return document.title || (ogTitle && ogTitle.content) || ''; - } -} - -function getPageDescription() { - let element; - - try { - element = window.top.document.querySelector('meta[name="description"]') || - window.top.document.querySelector('meta[property="og:description"]') - } catch (e) { - element = document.querySelector('meta[name="description"]') || - document.querySelector('meta[property="og:description"]') - } - - return (element && element.content) || ''; -} - function getConnectionDownLink(nav) { return nav && nav.connection && nav.connection.downlink >= 0 ? nav.connection.downlink.toString() : ''; } @@ -260,7 +220,6 @@ function buildRequestObject(bid) { let placementId = getValue(bid.params, 'placementId'); let pageId = getValue(bid.params, 'pageId'); const gpid = deepAccess(bid, 'ortb2Imp.ext.gpid'); - const videoPlcmt = deepAccess(bid, 'mediaTypes.video.plcmt'); reqObj.sizes = getSizes(bid); reqObj.bidId = getBidIdParameter('bidId', bid); @@ -270,7 +229,6 @@ function buildRequestObject(bid) { reqObj.adUnitCode = getBidIdParameter('adUnitCode', bid); reqObj.transactionId = bid.ortb2Imp?.ext?.tid || ''; if (gpid) { reqObj.gpid = gpid; } - if (videoPlcmt) { reqObj.videoPlcmt = videoPlcmt; } return reqObj; } diff --git a/modules/teadsIdSystem.js b/modules/teadsIdSystem.js index 8026fe77f87..b4067bf75c3 100644 --- a/modules/teadsIdSystem.js +++ b/modules/teadsIdSystem.js @@ -12,13 +12,6 @@ import {getStorageManager} from '../src/storageManager.js'; import {uspDataHandler} from '../src/adapterManager.js'; import {MODULE_TYPE_UID} from '../src/activities/modules.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 = 'teadsId'; const GVL_ID = 132; const FP_TEADS_ID_COOKIE_NAME = '_tfpvi'; @@ -41,9 +34,9 @@ export const storage = getStorageManager({moduleType: MODULE_TYPE_UID, moduleNam /** @type {Submodule} */ export const teadsIdSubmodule = { /** - * used to link submodule with config - * @type {string} - */ + * used to link submodule with config + * @type {string} + */ name: MODULE_NAME, /** * Vendor id of Teads @@ -51,21 +44,21 @@ export const teadsIdSubmodule = { */ gvlid: GVL_ID, /** - * decode the stored id value for passing to bid requests - * @function - * @param {string} value - * @returns {{teadsId:string}} - */ + * decode the stored id value for passing to bid requests + * @function + * @param {string} value + * @returns {{teadsId:string}} + */ decode(value) { return {teadsId: value} }, /** - * performs action to obtain id and return a value in the callback's response argument - * @function - * @param {SubmoduleConfig} [submoduleConfig] - * @param {ConsentData} [consentData] - * @returns {IdResponse|undefined} - */ + * performs action to obtain id and return a value in the callback's response argument + * @function + * @param {SubmoduleConfig} [submoduleConfig] + * @param {ConsentData} [consentData] + * @returns {IdResponse|undefined} + */ getId(submoduleConfig, consentData) { const resp = function (callback) { const url = buildAnalyticsTagUrl(submoduleConfig, consentData); diff --git a/modules/temedyaBidAdapter.js b/modules/temedyaBidAdapter.js index 0e48768b605..4eac0bc641f 100644 --- a/modules/temedyaBidAdapter.js +++ b/modules/temedyaBidAdapter.js @@ -3,14 +3,6 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER, NATIVE } from '../src/mediaTypes.js'; import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse - * @typedef {import('../src/adapters/bidderFactory.js').ServerRequest} ServerRequest - * @typedef {import('../src/adapters/bidderFactory.js').validBidRequests} validBidRequests - */ - const BIDDER_CODE = 'temedya'; const ENDPOINT_URL = 'https://adm.vidyome.com/'; const ENDPOINT_METHOD = 'GET'; @@ -20,20 +12,20 @@ export const spec = { code: BIDDER_CODE, supportedMediaTypes: [BANNER, NATIVE], /** - * Determines whether or not the given bid request is valid. - * - * @param {BidRequest} bid The bid params to validate. - * @return boolean True if this is a valid bid, and false otherwise. - */ + * Determines whether or not the given bid request is valid. + * + * @param {BidRequest} bid The bid params to validate. + * @return boolean True if this is a valid bid, and false otherwise. + */ isBidRequestValid: function (bid) { return !!(bid.params.widgetId); }, /** - * Make a server request from the list of BidRequests. - * - * @param {validBidRequests[]} - an array of bids - * @return ServerRequest Info describing the request to the server. - */ + * Make a server request from the list of BidRequests. + * + * @param {validBidRequests[]} - an array of bids + * @return ServerRequest Info describing the request to the server. + */ buildRequests: function (validBidRequests, bidderRequest) { // convert Native ORTB definition to old-style prebid native definition validBidRequests = convertOrtbRequestToProprietaryNative(validBidRequests); @@ -62,11 +54,11 @@ export const spec = { }); }, /** - * Unpack the 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. - */ + * Unpack the 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. + */ interpretResponse: function (serverResponse, bidRequest) { try { const bidResponse = serverResponse.body; diff --git a/modules/theAdxBidAdapter.js b/modules/theAdxBidAdapter.js index f19f7cfe515..03f303498a1 100644 --- a/modules/theAdxBidAdapter.js +++ b/modules/theAdxBidAdapter.js @@ -9,15 +9,6 @@ import { } from '../src/adapters/bidderFactory.js'; import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse - * @typedef {import('../src/adapters/bidderFactory.js').SyncOptions} SyncOptions - * @typedef {import('../src/adapters/bidderFactory.js').UserSync} UserSync - * @typedef {import('../src/adapters/bidderFactory.js').validBidRequests} validBidRequests - */ - const BIDDER_CODE = 'theadx'; const ENDPOINT_URL = 'https://ssp.theadx.com/request'; @@ -168,6 +159,7 @@ export const spec = { withCredentials: true, }, bidder: 'theadx', + // TODO: is 'page' the right value here? referrer: encodeURIComponent(bidderRequest.refererInfo.page || ''), data: generatePayload(bidRequest, bidderRequest), mediaTypes: bidRequest['mediaTypes'], @@ -269,7 +261,6 @@ export const spec = { ad: creative, ttl: ttl || 3000, creativeId: bid.crid, - dealId: bid.dealid || null, netRevenue: true, currency: responseBody.cur, mediaType: mediaType, @@ -475,19 +466,11 @@ let generateImpBody = (bidRequest, bidderRequest) => { } else if (mediaTypes && mediaTypes.native) { native = generateNativeComponent(bidRequest, bidderRequest); } + const result = { id: bidRequest.index, tagid: bidRequest.params.tagId + '', }; - - // deals support - if (bidRequest.params.deals && Array.isArray(bidRequest.params.deals) && bidRequest.params.deals.length > 0) { - result.pmp = { - deals: bidRequest.params.deals, - private_auction: 0, - }; - } - if (banner) { result['banner'] = banner; } diff --git a/modules/theAdxBidAdapter.md b/modules/theAdxBidAdapter.md index cdc7b8410a8..2392bfaa819 100644 --- a/modules/theAdxBidAdapter.md +++ b/modules/theAdxBidAdapter.md @@ -26,9 +26,9 @@ Module that connects to TheAdx demand sources { bidder: "theadx", params: { - pid: 1, // publisher id - wid: 7, //website id - tagId: 19, //zone id + pid: 1000, // publisher id + wid: 2000, //website id + tagId: 5000, //zone id } } ] @@ -43,10 +43,9 @@ Module that connects to TheAdx demand sources { bidder: "theadx", params: { - pid: 1, // publisher id - wid: 7, //website id - tagId: 18, //zone id - deals:[{"id":"theadx:137"}] //optional + pid: 1000, // publisher id + wid: 2000, //website id + tagId: 5000, //zone id } } ] @@ -81,9 +80,9 @@ Module that connects to TheAdx demand sources { bidder: "theadx", params: { - pid: 1, // publisher id - wid: 7, //website id - tagId: 20, //zone id + pid: 1000, // publisher id + wid: 2000, //website id + tagId: 5000, //zone id } } ] diff --git a/modules/timeoutRtdProvider.js b/modules/timeoutRtdProvider.js index a46a39a2c2b..323a5291e2d 100644 --- a/modules/timeoutRtdProvider.js +++ b/modules/timeoutRtdProvider.js @@ -4,10 +4,6 @@ import * as ajax from '../src/ajax.js'; import { logInfo, deepAccess, logError } from '../src/utils.js'; import { getGlobal } from '../src/prebidGlobal.js'; -/** - * @typedef {import('../modules/rtdModule/index.js').RtdSubmodule} RtdSubmodule - */ - const SUBMODULE_NAME = 'timeout'; // this allows the stubbing of functions during testing @@ -71,7 +67,7 @@ function getConnectionSpeed() { * Calculate the time to be added to the timeout * @param {Array} adUnits * @param {Object} rules - * @return {number} + * @return {int} */ function calculateTimeoutModifier(adUnits, rules) { logInfo('Timeout rules', rules); diff --git a/modules/tncIdSystem.js b/modules/tncIdSystem.js index 59254a9430f..24e3c79d4df 100644 --- a/modules/tncIdSystem.js +++ b/modules/tncIdSystem.js @@ -57,12 +57,6 @@ export const tncidSubModule = { return { callback: function (cb) { return tncCallback(cb); } } - }, - eids: { - 'tncid': { - source: 'thenewco.it', - atype: 3 - }, } } diff --git a/modules/topicsFpdModule.js b/modules/topicsFpdModule.js index 617ce0ad6f4..4cb11af48fc 100644 --- a/modules/topicsFpdModule.js +++ b/modules/topicsFpdModule.js @@ -1,32 +1,24 @@ -import {isEmpty, logError, logWarn, mergeDeep, safeJSONParse} from '../src/utils.js'; +import {logError, logWarn, mergeDeep, isEmpty, safeJSONParse, logInfo, hasDeviceAccess} from '../src/utils.js'; import {getRefererInfo} from '../src/refererDetection.js'; import {submodule} from '../src/hook.js'; import {GreedyPromise} from '../src/utils/promise.js'; import {config} from '../src/config.js'; import {getCoreStorageManager} from '../src/storageManager.js'; import {includes} from '../src/polyfill.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 {gdprDataHandler} from '../src/adapterManager.js'; const MODULE_NAME = 'topicsFpd'; const DEFAULT_EXPIRATION_DAYS = 21; -const DEFAULT_FETCH_RATE_IN_DAYS = 1; +const TCF_REQUIRED_PURPOSES = ['1', '2', '3', '4']; +let HAS_GDPR_CONSENT = true; let LOAD_TOPICS_INITIALISE = false; - -export function reset() { - LOAD_TOPICS_INITIALISE = false; -} +const HAS_DEVICE_ACCESS = hasDeviceAccess(); const bidderIframeList = { - maxTopicCaller: 2, + maxTopicCaller: 1, bidders: [{ bidder: 'pubmatic', iframeURL: 'https://ads.pubmatic.com/AdServer/js/topics/topics_frame.html' - }, { - bidder: 'rtbhouse', - iframeURL: 'https://topics.authorizedvault.com/topicsapi.html' }] } export const coreStorage = getCoreStorageManager(MODULE_NAME); @@ -86,21 +78,15 @@ export function getTopicsData(name, topics, taxonomies = TAXONOMIES) { if (name != null) { datum.name = name; } - return datum; }) ); } -function isTopicsSupported(doc = document) { - return 'browsingTopics' in doc && doc.featurePolicy.allowsFeature('browsing-topics') -} - export function getTopics(doc = document) { let topics = null; - try { - if (isTopicsSupported(doc)) { + if ('browsingTopics' in doc && doc.featurePolicy.allowsFeature('browsing-topics')) { topics = GreedyPromise.resolve(doc.browsingTopics()); } } catch (e) { @@ -109,7 +95,6 @@ export function getTopics(doc = document) { if (topics == null) { topics = GreedyPromise.resolve([]); } - return topics; } @@ -138,16 +123,19 @@ export function processFpd(config, {global}, {data = topicsData} = {}) { */ export function getCachedTopics() { let cachedTopicData = []; + if (!HAS_GDPR_CONSENT || !HAS_DEVICE_ACCESS) { + return cachedTopicData; + } const topics = config.getConfig('userSync.topics') || bidderIframeList; const bidderList = topics.bidders || []; let 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 - let bidderConfigObj = bidderList.find(({bidder}) => cachedBidder === bidder) - if (bidderConfigObj && isActivityAllowed(ACTIVITY_ENRICH_UFPD, activityParams(MODULE_TYPE_BIDDER, cachedBidder))) { + let bidderConfigObj = bidderList.find(({bidder}) => cachedBidder == bidder) + if (bidderConfigObj) { if (!isCachedDataExpired(value[lastUpdated], bidderConfigObj?.expiry || DEFAULT_EXPIRATION_DAYS)) { Object.keys(value).forEach((segData) => { - segData !== lastUpdated && cachedTopicData.push(value[segData]); + segData != lastUpdated && cachedTopicData.push(value[segData]); }) } else { // delete the specific bidder map from the store and store the updated maps @@ -160,7 +148,7 @@ export function getCachedTopics() { } /** - * Receive messages from iframe loaded for bidders to fetch topic + * Recieve messages from iframe loaded for bidders to fetch topic * @param {MessageEvent} evt */ export function receiveMessage(evt) { @@ -169,7 +157,7 @@ export function receiveMessage(evt) { let data = safeJSONParse(evt.data); if (includes(getLoadedIframeURL(), evt.origin) && data && data.segment && !isEmpty(data.segment.topics)) { const {domain, topics, bidder} = data.segment; - const iframeTopicsData = getTopicsData(domain, topics); + const iframeTopicsData = getTopicsData(domain, topics)[0]; iframeTopicsData && storeInLocalStorage(bidder, iframeTopicsData); } } catch (err) { } @@ -177,20 +165,18 @@ export function receiveMessage(evt) { } /** -Function to store Topics data received from iframe in storage(name: "prebid:topics") - * @param {Topics} topics - */ +Function to store Topics data recieved from iframe in storage(name: "prebid:topics") +* @param {Topics} topics +*/ export function storeInLocalStorage(bidder, topics) { const storedSegments = new Map(safeJSONParse(coreStorage.getDataFromLocalStorage(topicStorageName))); - const topicsObj = { - [lastUpdated]: new Date().getTime() - }; - - topics.forEach((topic) => { - topicsObj[topic.ext.segclass] = topic; - }); - - storedSegments.set(bidder, topicsObj); + if (storedSegments.has(bidder)) { + storedSegments.get(bidder)[topics['ext']['segclass']] = topics; + storedSegments.get(bidder)[lastUpdated] = new Date().getTime(); + storedSegments.set(bidder, storedSegments.get(bidder)); + } else { + storedSegments.set(bidder, {[topics.ext.segclass]: topics, [lastUpdated]: new Date().getTime()}) + } coreStorage.setDataInLocalStorage(topicStorageName, JSON.stringify([...storedSegments])); } @@ -202,8 +188,8 @@ function isCachedDataExpired(storedTime, cacheTime) { } /** - * Function to get random bidders based on count passed with array of bidders - */ +* Function to get random bidders based on count passed with array of bidders +**/ function getRandomBidders(arr, count) { return ([...arr].sort(() => 0.5 - Math.random())).slice(0, count) } @@ -215,43 +201,55 @@ function listenMessagesFromTopicIframe() { window.addEventListener('message', receiveMessage, false); } +function checkTCFv2(vendorData, requiredPurposes = TCF_REQUIRED_PURPOSES) { + const {gdprApplies, purpose} = vendorData; + if (!gdprApplies || !purpose) { + return true; + } + return requiredPurposes.map((purposeNo) => { + const purposeConsent = purpose.consents ? purpose.consents[purposeNo] : false; + if (purposeConsent) { + return true; + } + return false; + }).reduce((a, b) => a && b, true); +} + +export function hasGDPRConsent() { + // Check for GDPR consent for purpose 1,2,3,4 and return false if consent has not been given + const gdprConsent = gdprDataHandler.getConsentData(); + const hasGdpr = (gdprConsent && typeof gdprConsent.gdprApplies === 'boolean' && gdprConsent.gdprApplies) ? 1 : 0; + const gdprConsentString = hasGdpr ? gdprConsent.consentString : ''; + if (hasGdpr) { + if ((!gdprConsentString || gdprConsentString === '') || !gdprConsent.vendorData) { + return false; + } + return checkTCFv2(gdprConsent.vendorData); + } + return true; +} + /** * function to load the iframes of the bidder to load the topics data */ -export function loadTopicsForBidders(doc = document) { - if (!isTopicsSupported(doc)) return; +function loadTopicsForBidders() { + HAS_GDPR_CONSENT = hasGDPRConsent(); + if (!HAS_GDPR_CONSENT || !HAS_DEVICE_ACCESS) { + logInfo('Topics Module : Consent string is required to fetch the topics from third party domains.'); + return; + } const topics = config.getConfig('userSync.topics') || bidderIframeList; - if (topics) { listenMessagesFromTopicIframe(); const randomBidders = getRandomBidders(topics.bidders || [], topics.maxTopicCaller || 1) - randomBidders && randomBidders.forEach(({ bidder, iframeURL, fetchUrl, fetchRate }) => { + randomBidders && randomBidders.forEach(({ bidder, iframeURL }) => { if (bidder && iframeURL) { - let ifrm = doc.createElement('iframe'); + let ifrm = document.createElement('iframe'); ifrm.name = 'ifrm_'.concat(bidder); ifrm.src = ''.concat(iframeURL, '?bidder=').concat(bidder); ifrm.style.display = 'none'; setLoadedIframeURL(new URL(iframeURL).origin); - iframeURL && doc.documentElement.appendChild(ifrm); - } - - if (bidder && fetchUrl) { - let storedSegments = new Map(safeJSONParse(coreStorage.getDataFromLocalStorage(topicStorageName))); - const bidderLsEntry = storedSegments.get(bidder); - - if (!bidderLsEntry || (bidderLsEntry && isCachedDataExpired(bidderLsEntry[lastUpdated], fetchRate || DEFAULT_FETCH_RATE_IN_DAYS))) { - 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 fetchTopicsData = getTopicsData(domain, topics); - fetchTopicsData && storeInLocalStorage(bidder, fetchTopicsData); - } - }); - } + iframeURL && window.document.documentElement.appendChild(ifrm); } }) } else { diff --git a/modules/topicsFpdModule.md b/modules/topicsFpdModule.md index 6ef0bf241dd..b1bc3cb0d5b 100644 --- a/modules/topicsFpdModule.md +++ b/modules/topicsFpdModule.md @@ -36,10 +36,6 @@ pbjs.setConfig({ bidder: 'pubmatic', iframeURL: 'https://ads.pubmatic.com/AdServer/js/topics/topics_frame.html', expiry: 7 // Configurable expiry days - },{ - bidder: 'rtbhouse', - iframeURL: 'https://topics.authorizedvault.com/topicsapi.html', - expiry: 7 // Configurable expiry days },{ bidder: 'rubicon', iframeURL: 'https://rubicon.com:8080/topics/fpd/topic.html', // dummy URL diff --git a/modules/tpmnBidAdapter.js b/modules/tpmnBidAdapter.js index 3edc89c90ae..bac99d578c5 100644 --- a/modules/tpmnBidAdapter.js +++ b/modules/tpmnBidAdapter.js @@ -1,245 +1,164 @@ /* eslint-disable no-tabs */ import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { ortbConverter } from '../libraries/ortbConverter/converter.js'; +import { parseUrl, deepAccess } from '../src/utils.js'; import { getStorageManager } from '../src/storageManager.js'; -import { BANNER, VIDEO } from '../src/mediaTypes.js'; -import { Renderer } from '../src/Renderer.js'; +import { BANNER } from '../src/mediaTypes.js'; import { config } from '../src/config.js'; -import * as utils from '../src/utils.js'; - -/** - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - */ +export const ADAPTER_VERSION = '1'; +const SUPPORTED_AD_TYPES = [BANNER]; const BIDDER_CODE = 'tpmn'; -const DEFAULT_BID_TTL = 500; -const DEFAULT_CURRENCY = 'USD'; -const SUPPORTED_AD_TYPES = [BANNER, VIDEO]; -// const BIDDER_ENDPOINT_URL = 'http://localhost:8081/ortb/pbjs_bidder'; -const BIDDER_ENDPOINT_URL = 'https://gat.tpmn.io/ortb/pbjs_bidder'; -const IFRAMESYNC = 'https://gat.tpmn.io/sync/iframe'; -const COMMON_PARAMS = [ - 'battr' -]; - -export const VIDEO_RENDERER_URL = 'https://acdn.adnxs.com/video/outstream/ANOutstreamVideo.js'; -export const ADAPTER_VERSION = '2.0'; +const URL = 'https://ad.tpmn.co.kr/prebidhb.tpmn'; +const IFRAMESYNC = 'https://ad.tpmn.co.kr/sync.tpmn?type=iframe'; export const storage = getStorageManager({bidderCode: BIDDER_CODE}); + export const spec = { code: BIDDER_CODE, supportedMediaTypes: SUPPORTED_AD_TYPES, - isBidRequestValid, - buildRequests, - interpretResponse, - getUserSyncs, /** - * Register bidder specific code, which will execute if a bid from this bidder won the auction - * @param {Bid} bid The bid that won the auction + *Determines whether or not the given bid request is valid. + * + * @param {object} bid The bid to validate. + * @return boolean True if this is a valid bid, and false otherwise. */ - onBidWon: function (bid) { - if (bid.burl) { - utils.triggerPixel(bid.burl); - } - } -} - -function isBidRequestValid(bid) { - return (isValidInventoryId(bid) && (isValidBannerRequest(bid) || isValidVideoRequest(bid))); -} - -function isValidInventoryId(bid) { - return 'params' in bid && 'inventoryId' in bid.params && utils.isNumber(bid.params.inventoryId); -} - -function isValidBannerRequest(bid) { - const bannerSizes = utils.deepAccess(bid, `mediaTypes.${BANNER}.sizes`); - return utils.isArray(bannerSizes) && bannerSizes.length > 0 && bannerSizes.every(size => utils.isNumber(size[0]) && utils.isNumber(size[1])); -} - -function isValidVideoRequest(bid) { - const videoSizes = utils.deepAccess(bid, `mediaTypes.${VIDEO}.playerSize`); - const videoMimes = utils.deepAccess(bid, `mediaTypes.${VIDEO}.mimes`); - - const isValidVideoSize = utils.isArray(videoSizes) && videoSizes.length > 0 && videoSizes.every(size => utils.isNumber(size[0]) && utils.isNumber(size[1])); - const isValidVideoMimes = utils.isArray(videoMimes) && videoMimes.length > 0; - return isValidVideoSize && isValidVideoMimes; -} - -function buildRequests(validBidRequests, bidderRequest) { - let requests = []; - try { - if (validBidRequests.length === 0 || !bidderRequest) return []; - let bannerBids = validBidRequests.filter(bid => utils.deepAccess(bid, 'mediaTypes.banner')); - let videoBids = validBidRequests.filter(bid => utils.deepAccess(bid, 'mediaTypes.video')); - - bannerBids.forEach(bid => { - requests.push(createRequest([bid], bidderRequest, BANNER)); - }); - - videoBids.forEach(bid => { - requests.push(createRequest([bid], bidderRequest, VIDEO)); - }); - } catch (err) { - utils.logWarn('buildRequests', err); - } - - return requests; -} - -function createRequest(bidRequests, bidderRequest, mediaType) { - const rtbData = CONVERTER.toORTB({ bidRequests, bidderRequest, context: { mediaType } }) - - const bid = bidRequests.find((b) => b.params.inventoryId) - - if (bid.params.inventoryId) rtbData.ext = {}; - if (bid.params.inventoryId) rtbData.ext.inventoryId = bid.params.inventoryId - - const ortb2Data = bidderRequest?.ortb2 || {}; - const bcat = ortb2Data?.bcat || bid.params.bcat || []; - const badv = ortb2Data?.badv || bid.params.badv || []; - const bapp = ortb2Data?.bapp || bid.params.bapp || []; - - if (bcat.length > 0) { - rtbData.bcat = bcat; - } - if (badv.length > 0) { - rtbData.badv = badv; - } - if (badv.length > 0) { - rtbData.bapp = bapp; - } - - return { - method: 'POST', - url: BIDDER_ENDPOINT_URL + '?v=' + ADAPTER_VERSION, - data: rtbData - } -} - -function interpretResponse(response, request) { - return CONVERTER.fromORTB({ request: request.data, response: response.body }).bids; -} - -registerBidder(spec); + isBidRequestValid: function (bid) { + return 'params' in bid && + 'inventoryId' in bid.params && + 'publisherId' in bid.params && + !isNaN(Number(bid.params.inventoryId)) && + bid.params.inventoryId > 0 && + (typeof bid.mediaTypes.banner.sizes != 'undefined'); // only accepting appropriate sizes + }, -const CONVERTER = ortbConverter({ - context: { - netRevenue: true, - ttl: DEFAULT_BID_TTL, - currency: DEFAULT_CURRENCY + /** + * @param {BidRequest[]} bidRequests + * @param {*} bidderRequest + * @return {ServerRequest} + */ + buildRequests: (bidRequests, bidderRequest) => { + if (bidRequests.length === 0) { + return []; + } + const bids = bidRequests.map(bidToRequest); + const bidderApiUrl = URL; + const payload = { + 'bids': [...bids], + 'site': createSite(bidderRequest.refererInfo) + }; + return [{ + method: 'POST', + url: bidderApiUrl, + data: payload + }]; }, - imp(buildImp, bidRequest, context) { - let imp = buildImp(bidRequest, context); - if (!imp.bidfloor && bidRequest.params.bidFloor) { - imp.bidfloor = bidRequest.params.bidFloor; + /** + * Unpack the 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. + */ + interpretResponse: function (serverResponse, serverRequest) { + if (!Array.isArray(serverResponse.body)) { + return []; } - [VIDEO, BANNER].forEach(namespace => { - COMMON_PARAMS.forEach(param => { - if (bidRequest.params.hasOwnProperty(param)) { - utils.deepSetValue(imp, `${namespace}.${param}`, bidRequest.params[param]) - } - }) - }) - return imp; + // server response body is an array of bid results + const bidResults = serverResponse.body; + // our server directly returns the format needed by prebid.js so no more + // transformation is needed here. + return bidResults; }, - bidResponse(buildBidResponse, bid, context) { - const {bidRequest} = context; - const bidResponse = buildBidResponse(bid, context); - if (bidResponse.mediaType === BANNER) { - bidResponse.ad = bid.adm; - } else if (bidResponse.mediaType === VIDEO) { - if (bidRequest.mediaTypes.video.context === 'outstream') { - bidResponse.rendererUrl = VIDEO_RENDERER_URL; - bidResponse.renderer = createRenderer(bidRequest); + + getUserSyncs: function (syncOptions, serverResponses, gdprConsent, uspConsent) { + const syncArr = []; + if (syncOptions.iframeEnabled) { + let policyParam = ''; + if (gdprConsent && gdprConsent.consentString) { + if (typeof gdprConsent.gdprApplies === 'boolean') { + policyParam += `&gdpr=${Number(gdprConsent.gdprApplies)}&gdpr_consent=${gdprConsent.consentString}`; + } else { + policyParam += `&gdpr=0&gdpr_consent=${gdprConsent.consentString}`; + } } + if (uspConsent && uspConsent.consentString) { + policyParam += `&ccpa_consent=${uspConsent.consentString}`; + } + const coppa = config.getConfig('coppa') ? 1 : 0; + policyParam += `&coppa=${coppa}`; + syncArr.push({ + type: 'iframe', + url: IFRAMESYNC + policyParam + }); + } else { + syncArr.push({ + type: 'image', + url: 'https://x.bidswitch.net/sync?ssp=tpmn' + }); + syncArr.push({ + type: 'image', + url: 'https://gocm.c.appier.net/tpmn' + }); + syncArr.push({ + type: 'image', + url: 'https://info.mmnneo.com/getGuidRedirect.info?url=https%3A%2F%2Fad.tpmn.co.kr%2Fcookiesync.tpmn%3Ftpmn_nid%3Dbf91e8b3b9d3f1af3fc1d657f090b4fb%26tpmn_buid%3D' + }); + syncArr.push({ + type: 'image', + url: 'https://sync.aralego.com/idSync?redirect=https%3A%2F%2Fad.tpmn.co.kr%2FpixelCt.tpmn%3Ftpmn_nid%3Dde91e8b3b9d3f1af3fc1d657f090b815%26tpmn_buid%3DSspCookieUserId' + }); } - return bidResponse; + return syncArr; }, - overrides: { - imp: { - video(orig, imp, bidRequest, context) { - let videoParams = bidRequest.mediaTypes[VIDEO]; - if (videoParams) { - videoParams = Object.assign({}, videoParams, bidRequest.params.video); - bidRequest = {...bidRequest, mediaTypes: {[VIDEO]: videoParams}} - } - orig(imp, bidRequest, context); - }, - }, - } -}); +}; -function createRenderer(bid) { - const renderer = Renderer.install({ - id: bid.bidId, - url: VIDEO_RENDERER_URL, - config: utils.deepAccess(bid, 'renderer.options'), - loaded: false, - adUnitCode: bid.adUnitCode - }); +registerBidder(spec); - try { - renderer.setRender(outstreamRender); - } catch (err) { - utils.logWarn('Prebid Error calling setRender on renderer', err); +/** + * Creates site description object + */ +function createSite(refInfo) { + let url = parseUrl(refInfo.page || ''); + let site = { + 'domain': url.hostname, + 'page': url.protocol + '://' + url.hostname + url.pathname + }; + if (refInfo.ref) { + site.ref = refInfo.ref } - return renderer; + let keywords = document.getElementsByTagName('meta')['keywords']; + if (keywords && keywords.content) { + site.keywords = keywords.content; + } + return site; } -function outstreamRender(bid, doc) { - bid.renderer.push(() => { - const win = (doc) ? doc.defaultView : window; - win.ANOutstreamVideo.renderAd({ - sizes: [bid.playerWidth, bid.playerHeight], - targetId: bid.adUnitCode, - rendererOptions: bid.renderer.getConfig(), - adResponse: { content: bid.vastXml } +function parseSize(size) { + let sizeObj = {} + sizeObj.width = parseInt(size[0], 10); + sizeObj.height = parseInt(size[1], 10); + return sizeObj; +} - }, handleOutstreamRendererEvents.bind(null, bid)); - }); +function parseSizes(sizes) { + if (Array.isArray(sizes[0])) { // is there several sizes ? (ie. [[728,90],[200,300]]) + return sizes.map(size => parseSize(size)); + } + return [parseSize(sizes)]; // or a single one ? (ie. [728,90]) } -function handleOutstreamRendererEvents(bid, id, eventName) { - bid.renderer.handleVideoEvent({ id, eventName }); +function getBannerSizes(bidRequest) { + return parseSizes(deepAccess(bidRequest, 'mediaTypes.banner.sizes') || bidRequest.sizes); } -function getUserSyncs(syncOptions, serverResponses, gdprConsent, uspConsent) { - const syncArr = []; - if (syncOptions.iframeEnabled) { - let policyParam = ''; - if (gdprConsent && gdprConsent.consentString) { - if (typeof gdprConsent.gdprApplies === 'boolean') { - policyParam += `&gdpr=${Number(gdprConsent.gdprApplies)}&gdpr_consent=${gdprConsent.consentString}`; - } else { - policyParam += `&gdpr=0&gdpr_consent=${gdprConsent.consentString}`; - } - } - if (uspConsent && uspConsent.consentString) { - policyParam += `&ccpa_consent=${uspConsent.consentString}`; - } - const coppa = config.getConfig('coppa') ? 1 : 0; - policyParam += `&coppa=${coppa}`; - syncArr.push({ - type: 'iframe', - url: IFRAMESYNC + '?' + policyParam - }); - } else { - syncArr.push({ - type: 'image', - url: 'https://x.bidswitch.net/sync?ssp=tpmn' - }); - syncArr.push({ - type: 'image', - url: 'https://gocm.c.appier.net/tpmn' - }); - syncArr.push({ - type: 'image', - url: 'https://info.mmnneo.com/getGuidRedirect.info?url=https%3A%2F%2Fad.tpmn.co.kr%2Fcookiesync.tpmn%3Ftpmn_nid%3Dbf91e8b3b9d3f1af3fc1d657f090b4fb%26tpmn_buid%3D' - }); - syncArr.push({ - type: 'image', - url: 'https://sync.aralego.com/idSync?redirect=https%3A%2F%2Fad.tpmn.co.kr%2FpixelCt.tpmn%3Ftpmn_nid%3Dde91e8b3b9d3f1af3fc1d657f090b815%26tpmn_buid%3DSspCookieUserId' - }); - } - return syncArr; +function bidToRequest(bid) { + const bidObj = {}; + bidObj.sizes = getBannerSizes(bid); + + bidObj.inventoryId = bid.params.inventoryId; + bidObj.publisherId = bid.params.publisherId; + bidObj.bidId = bid.bidId; + bidObj.adUnitCode = bid.adUnitCode; + // TODO: fix auctionId leak: https://github.com/prebid/Prebid.js/issues/9781 + bidObj.auctionId = bid.auctionId; + + return bidObj; } diff --git a/modules/tpmnBidAdapter.md b/modules/tpmnBidAdapter.md index 3b016d7e5b2..8387528bb0f 100644 --- a/modules/tpmnBidAdapter.md +++ b/modules/tpmnBidAdapter.md @@ -11,27 +11,10 @@ Maintainer: develop@tpmn.co.kr Connects to TPMN exchange for bids. NOTE: -- TPMN bid adapter only supports MediaType BANNER, VIDEO. +- TPMN bid adapter only supports Banner at the moment. - Multi-currency is not supported. -- Please contact the TPMN sales team via email for "inventoryId" issuance. - -# Bid Parameters - -## bids.params (Banner, Video) -***Pay attention to the case sensitivity.*** - -{: .table .table-bordered .table-striped } -| Name | Scope | Description | Example | Type | -| -------------- | ----------- | ------------------------------------------ | ------------- | ------------ | -| `inventoryId` | required | Ad Inventory id TPMN | 123 | Number | -| `bidFloor` | recommended | Minimum price in USD. bidFloor applies to a specific unit. | 1.50 | Number | -| `bcat` | optional | IAB 5.1 Content Categories | ['IAB7-39'] | [String] | -| `badv` | optional | IAB Block list of advertisers by their domains | ['example.com'] | [String] | -| `bapp` | optional | IAB Block list of applications | ['com.blocked'] | [String] | - - -# Banner Ad Unit Config +# Sample Ad Unit Config ``` var adUnits = [{ // Banner adUnit @@ -39,77 +22,16 @@ NOTE: mediaTypes: { banner: { sizes: [[300, 250], [320, 50]], // banner size - battr: [1,2,3] // optional } }, bids: [ { bidder: 'tpmn', params: { - inventoryId: 1, // required - bidFloor: 2.0, // recommended - ... // bcat, badv, bapp // optional + inventoryId: '1', + publisherId: 'TPMN' } } ] }]; -``` - - -# mediaTypes Parameters - -## mediaTypes.banner - -The following banner parameters are supported here so publishers may fully declare their banner inventory: - -{: .table .table-bordered .table-striped } -| Name | Scope | Description | Example | Type | -| --------- | ------------| ----------------------------------------------------------------- | --------- | --------- | -| `sizes` | required | Avalaible sizes supported for banner ad unit | [ [300, 250], [300, 600] ] | [[Integer, Integer], [Integer, Integer]] | -| `battr` | optional | IAB 5.3 Creative Attributes | [1,2,3] | [Number] | -## mediaTypes.video - -We support the following OpenRTB params that can be specified in `mediaTypes.video` or in `bids[].params.video` - -{: .table .table-bordered .table-striped } -| Name | Scope | Description | Example | Type | -| --------- | ------------| ----------------------------------------------------------------- | --------- | --------- | -| `context` | required | instream or outstream |'outstream' | string | -| `playerSize` | required | Avalaible sizes supported for video ad unit. | [[300, 250]] | [Integer, Integer] | -| `mimes` | required | List of content MIME types supported by the player. | ['video/mp4']| [String]| -| `protocols` | optional | Supported video bid response protocol values. | [2,3,5,6] | [integers]| -| `api` | optional | Supported API framework values. | [2] | [integers] | -| `maxduration` | optional | Maximum video ad duration in seconds. | 30 | Integer | -| `minduration` | optional | Minimum video ad duration in seconds. | 6 | Integer | -| `startdelay` | optional | Indicates the start delay in seconds for pre-roll, mid-roll, or post-roll ad placements. | 0 | Integer | -| `placement` | optional | Placement type for the impression. | 1 | Integer | -| `minbitrate` | optional | Minimum bit rate in Kbps. | 300 | Integer | -| `maxbitrate` | optional | Maximum bit rate in Kbps. | 9600 | Integer | -| `playbackmethod` | optional | Playback methods that may be in use. Only one method is typically used in practice. | [2]| [Integers] | -| `linearity` | optional | OpenRTB2 linearity. in-strea,overlay... | 1 | Integer | -| `skip` | optional | Indicates if the player will allow the video to be skipped, where 0 = no, 1 = yes . | 1 | Integer | -| `battr` | optional | IAB 5.3 Creative Attributes | [1,2,3] | [Number] | - - -# Video Ad Unit Config -``` - var adUnits = [{ - code: 'video-div', - mediaTypes: { - video: { - context: 'instream', // required - mimes: ['video/mp4'], // required - playerSize: [[ 640, 480 ]], // required - ... // skippable, startdelay, battr.. // optional - } - }, - bids: [{ - bidder: 'tpmn', - params: { - inventoryId: 2, // required - bidFloor: 2.0, // recommended - ... // bcat, badv, bapp // optional - } - }] - }]; ``` \ No newline at end of file diff --git a/modules/trafficgateBidAdapter.js b/modules/trafficgateBidAdapter.js index fcd84306099..6665c397fe9 100644 --- a/modules/trafficgateBidAdapter.js +++ b/modules/trafficgateBidAdapter.js @@ -1,150 +1,95 @@ -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 {convertTypes} from '../libraries/transformParamsUtils/convertTypes.js'; +import { getWindowLocation, deepAccess } 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'; const BIDDER_CODE = 'trafficgate'; -const URL = 'https://[HOST].bc-plugin.com/prebidjs' +const URL = 'https://[HOST].bc-plugin.com/?c=o&m=multi' export const spec = { code: BIDDER_CODE, - supportedMediaTypes: [BANNER, VIDEO], - isBidRequestValid, - buildRequests, - interpretResponse, - transformBidParams, - isBannerBid -}; + supportedMediaTypes: [BANNER, VIDEO, NATIVE], -registerBidder(spec) - -const converter = ortbConverter({ - context: { - netRevenue: true, - ttl: 300 - }, - imp(buildImp, bidRequest, context) { - const imp = buildImp(bidRequest, context); - mergeDeep(imp, { - ext: { - bidder: { - placementId: bidRequest.params.placementId, - host: bidRequest.params.host - } - } - }); - if (bidRequest.params.customFloor && !imp.bidfloor) { - imp.bidfloor = bidRequest.params.customFloor; - } - return imp; + isBidRequestValid: function (bid) { + return !!(bid.bidId && bid.params && parseInt(bid.params.placementId) && bid.params.host) }, - request(buildRequest, imps, bidderRequest, context) { - const req = buildRequest(imps, bidderRequest, context); - mergeDeep(req, { - at: 1, + /** + * Make a server request from the list of BidRequests. + * + * @param {BidRequest[]} validBidRequests A non-empty list of valid bid requests that should be sent to the Server. + * @return ServerRequest Info describing the request to the server. + */ + buildRequests: function (validBidRequests) { + // convert Native ORTB definition to old-style prebid native definition + validBidRequests = convertOrtbRequestToProprietaryNative(validBidRequests); + if (validBidRequests && validBidRequests.length === 0) return []; + + const location = getWindowLocation() + const placements = [] + + validBidRequests.forEach((bidReq) => { + placements.push({ + placementId: bidReq.params.placementId, + bidId: bidReq.bidId, + traffic: getMediatype(bidReq) + }) }) - const bid = context.bidRequests[0]; - if (bid.params.test) { - req.test = 1 - } - return req; - }, - bidResponse(buildBidResponse, bid, context) { - const bidResponse = buildBidResponse(bid, context); - if (bid.ext) { - bidResponse.meta.networkId = bid.ext.networkId; - bidResponse.meta.advertiserDomains = bid.ext.advertiserDomains; + + return { + method: 'POST', + url: URL.replace('[HOST]', validBidRequests[0].params.host), + data: { + language: (navigator && navigator.language) ? navigator.language : '', + secure: +(location.protocol === 'https:'), + host: location.hostname, + page: location.pathname, + placements: placements + } } - return bidResponse; - }, - response(buildResponse, bidResponses, ortbResponse, context) { - const response = buildResponse(bidResponses, ortbResponse, context); - return response.bids }, - overrides: { - imp: { - bidfloor(setBidFloor, imp, bidRequest, context) { - const floor = {}; - setBidFloor(floor, bidRequest, {...context, currency: 'USD'}); - if (floor.bidfloorcur === 'USD') { - Object.assign(imp, floor); - } - }, - video(orig, imp, bidRequest, context) { - if (FEATURES.VIDEO) { - let videoParams = bidRequest.mediaTypes[VIDEO]; - if (videoParams) { - videoParams = Object.assign({}, videoParams, bidRequest.params.video); - bidRequest = {...bidRequest, mediaTypes: {[VIDEO]: videoParams}} - } - orig(imp, bidRequest, context); - if (imp.video && videoParams?.context === 'outstream') { - imp.video.placement = imp.video.placement || 4; - } - } + + interpretResponse: function (opts) { + const body = opts.body + const response = [] + + for (let i = 0; i < body.length; i++) { + const item = body[i] + if (isBidResponseValid(item)) { + response.push(item) } } - } -}); -function transformBidParams(params, isOpenRtb) { - return convertTypes({ - 'customFloor': 'number', - 'placementId': 'number', - 'host': 'string' - }, params); + return response + } } -function isBidRequestValid(bidRequest) { - const isValid = bidRequest.params.placementId && bidRequest.params.host; - if (!isValid) { +registerBidder(spec) + +function isBidResponseValid (bid) { + if (!bid.requestId || !bid.cpm || !bid.creativeId || + !bid.ttl || !bid.currency) { return false } - if (isBannerBid(bidRequest)) { - return deepAccess(bidRequest, 'mediaTypes.banner.sizes.length') > 0; + switch (bid['mediaType']) { + case BANNER: + return Boolean(bid.width && bid.height && bid.ad) + case VIDEO: + return Boolean(bid.vastUrl) + case NATIVE: + return Boolean(bid.title && bid.image && bid.impressionTrackers) + default: + return false } - return true } -function buildRequests(bids, bidderRequest) { - let videoBids = bids.filter(bid => isVideoBid(bid)); - let bannerBids = bids.filter(bid => isBannerBid(bid)); - let requests = bannerBids.length ? [createRequest(bannerBids, bidderRequest, BANNER)] : []; - videoBids.forEach(bid => { - requests.push(createRequest([bid], bidderRequest, VIDEO)); - }); - return requests; -} - -function createRequest(bidRequests, bidderRequest, mediaType) { - return { - method: 'POST', - url: URL.replace('[HOST]', bidRequests[0].params.host), - data: converter.toORTB({bidRequests, bidderRequest, context: {mediaType}}) +function getMediatype(bidRequest) { + if (deepAccess(bidRequest, 'mediaTypes.banner')) { + return BANNER; } -} - -function isVideoBid(bid) { - return !!deepAccess(bid, 'mediaTypes.video'); -} - -function isBannerBid(bid) { - return !!deepAccess(bid, 'mediaTypes.banner'); -} - -function interpretResponse(resp, req) { - if (!resp.body) { - resp.body = {nbr: 0}; + if (deepAccess(bidRequest, 'mediaTypes.video')) { + return VIDEO; + } + if (deepAccess(bidRequest, 'mediaTypes.native')) { + return NATIVE; } - return converter.fromORTB({request: req.data, response: resp.body}); -} - -export const spec2 = { - code: BIDDER_CODE, - supportedMediaTypes: [BANNER, VIDEO], - - isBidRequestValid: function (bid) { - return !!(bid.bidId && bid.params && parseInt(bid.params.placementId) && bid.params.host) - }, } diff --git a/modules/trionBidAdapter.js b/modules/trionBidAdapter.js index e976396c71c..b6375243a5a 100644 --- a/modules/trionBidAdapter.js +++ b/modules/trionBidAdapter.js @@ -1,7 +1,6 @@ -import {getBidIdParameter, parseSizesInput} from '../src/utils.js'; +import { getBidIdParameter, parseSizesInput, tryAppendQueryString } from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import { getStorageManager } from '../src/storageManager.js'; -import {tryAppendQueryString} from '../libraries/urlUtils/urlUtils.js'; const BID_REQUEST_BASE_URL = 'https://in-appadvertising.com/api/bidRequest'; const USER_SYNC_URL = 'https://in-appadvertising.com/api/userSync.html'; diff --git a/modules/tripleliftBidAdapter.js b/modules/tripleliftBidAdapter.js index bfbf1409c1b..df00495b88b 100644 --- a/modules/tripleliftBidAdapter.js +++ b/modules/tripleliftBidAdapter.js @@ -1,9 +1,8 @@ -import { logMessage, logError, isEmpty, isStr, isPlainObject, isArray, logWarn } from '../src/utils.js'; +import { tryAppendQueryString, logMessage, logError, isEmpty, isStr, isPlainObject, isArray, logWarn } 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 { getStorageManager } from '../src/storageManager.js'; -import {tryAppendQueryString} from '../libraries/urlUtils/urlUtils.js'; const GVLID = 28; const BIDDER_CODE = 'triplelift'; @@ -57,10 +56,6 @@ export const tripleliftAdapterSpec = { tlCall = tryAppendQueryString(tlCall, 'us_privacy', bidderRequest.uspConsent); } - if (bidderRequest && bidderRequest.fledgeEnabled) { - tlCall = tryAppendQueryString(tlCall, 'fledge', bidderRequest.fledgeEnabled); - } - if (config.getConfig('coppa') === true) { tlCall = tryAppendQueryString(tlCall, 'coppa', true); } @@ -80,29 +75,12 @@ export const tripleliftAdapterSpec = { 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, - fledgeAuctionConfigs - }; - } else { - return bids; - } + return bids.map(function(bid) { + return _buildResponseObject(bidderRequest, bid); + }); }, - getUserSyncs: function(syncOptions, responses, gdprConsent, usPrivacy, gppConsent) { + getUserSyncs: function(syncOptions, responses, gdprConsent, usPrivacy) { let syncType = _getSyncType(syncOptions); if (!syncType) return; @@ -122,15 +100,6 @@ export const tripleliftAdapterSpec = { syncEndpoint = tryAppendQueryString(syncEndpoint, 'us_privacy', usPrivacy); } - if (gppConsent) { - if (gppConsent.gppString) { - syncEndpoint = tryAppendQueryString(syncEndpoint, 'gpp', gppConsent.gppString); - } - if (gppConsent.applicableSections && gppConsent.applicableSections.length !== 0) { - syncEndpoint = tryAppendQueryString(syncEndpoint, 'gpp_sid', _filterSid(gppConsent.applicableSections)); - } - } - return [{ type: syncType, url: syncEndpoint @@ -144,13 +113,6 @@ function _getSyncType(syncOptions) { if (syncOptions.pixelEnabled) return 'image'; } -function _filterSid(sid) { - return sid.filter(element => { - return Number.isInteger(element); - }) - .join(','); -} - function _buildPostBody(bidRequests, bidderRequest) { let data = {}; let { schain } = bidRequests[0]; @@ -207,11 +169,6 @@ function _buildPostBody(bidRequests, bidderRequest) { if (bidderRequest?.ortb2?.regs?.gpp) { data.regs = Object.assign({}, bidderRequest.ortb2.regs); } - - if (bidderRequest?.ortb2) { - data.ext.ortb2 = Object.assign({}, bidderRequest.ortb2); - } - return data; } @@ -240,12 +197,7 @@ function _getORTBVideo(bidRequest) { } catch (err) { logWarn('Video size not defined', err); } - // honor existing publisher settings - if (video.context === 'instream') { - if (!video.placement) { - video.placement = 1; - } - } + if (video.context === 'instream') video.placement = 1; if (video.context === 'outstream') { if (!video.placement) { video.placement = 3 diff --git a/modules/ttdBidAdapter.js b/modules/ttdBidAdapter.js index d7705f2f5df..bbe207abc9e 100644 --- a/modules/ttdBidAdapter.js +++ b/modules/ttdBidAdapter.js @@ -4,16 +4,7 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; import {isNumber} from '../src/utils.js'; -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse - * @typedef {import('../src/adapters/bidderFactory.js').ServerRequest} ServerRequest - * @typedef {import('../src/adapters/bidderFactory.js').SyncOptions} SyncOptions - * @typedef {import('../src/adapters/bidderFactory.js').UserSync} UserSync - */ - -const BIDADAPTERVERSION = 'TTD-PREBID-2023.09.05'; +const BIDADAPTERVERSION = 'TTD-PREBID-2022.06.28'; const BIDDER_CODE = 'ttd'; const BIDDER_CODE_LONG = 'thetradedesk'; const BIDDER_ENDPOINT = 'https://direct.adsrvr.org/bid/bidder/'; @@ -415,7 +406,7 @@ export const spec = { buildRequests: function (validBidRequests, bidderRequest) { const firstPartyData = bidderRequest.ortb2 || {}; let topLevel = { - id: bidderRequest.bidderRequestId, + id: bidderRequest.auctionId, imp: validBidRequests.map(bidRequest => getImpression(bidRequest)), site: getSite(bidderRequest, firstPartyData), device: getDevice(firstPartyData), diff --git a/modules/ucfunnelBidAdapter.js b/modules/ucfunnelBidAdapter.js index 19b933a8666..2225deaa900 100644 --- a/modules/ucfunnelBidAdapter.js +++ b/modules/ucfunnelBidAdapter.js @@ -5,12 +5,6 @@ import { getStorageManager } from '../src/storageManager.js'; import { config } from '../src/config.js'; import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - * @typedef {import('../src/adapters/bidderFactory.js').ServerRequest} ServerRequest - */ - const COOKIE_NAME = 'ucf_uid'; const VER = 'ADGENT_PREBID-2018011501'; const BIDDER_CODE = 'ucfunnel'; @@ -70,10 +64,11 @@ export const spec = { * Format ucfunnel responses as Prebid bid responses * @param {ucfunnelResponseObj} ucfunnelResponse A successful response from ucfunnel. * @return {Bid[]} An array of formatted bids. - */ + */ interpretResponse: function (ucfunnelResponseObj, request) { const bidRequest = request.bidRequest; const ad = ucfunnelResponseObj ? ucfunnelResponseObj.body : {}; + const videoPlayerSize = parseSizes(bidRequest); let bid = { requestId: bidRequest.bidId, @@ -122,10 +117,10 @@ export const spec = { vastXml: ad.vastXml }); - if (bidRequest.sizes && bidRequest.sizes.length > 0) { + if (videoPlayerSize && videoPlayerSize.length === 2) { Object.assign(bid, { - width: bidRequest.sizes[0][0], - height: bidRequest.sizes[0][1] + width: videoPlayerSize[0], + height: videoPlayerSize[1] }); } break; @@ -133,8 +128,8 @@ export const spec = { default: var size = parseSizes(bidRequest); Object.assign(bid, { - width: ad.width || size[0][0], - height: ad.height || size[0][1], + width: ad.width || size[0], + height: ad.height || size[1], ad: ad.adm || '' }); } @@ -161,6 +156,12 @@ export const spec = { }; registerBidder(spec); +function transformSizes(requestSizes) { + if (typeof requestSizes === 'object' && requestSizes.length) { + return requestSizes[0]; + } +} + function getCookieSyncParameter(gdprApplies, apiVersion, consentString, uspConsent) { let param = '?'; if (gdprApplies == '1') { @@ -186,10 +187,11 @@ function parseSizes(bid) { params.video.playerWidth, params.video.playerHeight ]; - return [size]; + return size; } } - return bid.sizes; + + return transformSizes(bid.sizes); } function getSupplyChain(schain) { @@ -242,20 +244,6 @@ function getFloor(bid, size, mediaTypes) { return undefined; } -function addBidData(bidData, key, value) { - if (value) { - bidData[key] = value; - } -} - -function getFormat(size) { - let formatList = [] - for (var i = 0; i < size.length; i++) { - formatList.push(size[i].join(',')); - } - return (formatList.length > 0) ? formatList.join(';') : ''; -} - function getRequestData(bid, bidderRequest) { const size = parseSizes(bid); const language = navigator.language; @@ -276,8 +264,14 @@ function getRequestData(bid, bidderRequest) { schain: supplyChain }; - addBidData(bidData, 'fp', bidFloor); - addBidData(bidData, 'gpid', gpid); + if (bidFloor) { + bidData.fp = bidFloor; + } + + if (gpid) { + bidData.gpid = gpid; + } + addUserId(bidData, bid.userId); bidData.u = bidderRequest.refererInfo.page || bidderRequest.refererInfo.topmostLocation; @@ -299,11 +293,10 @@ function getRequestData(bid, bidderRequest) { } } - if (size != undefined && size.length > 0 && size[0].length == 2) { - bidData.w = size[0][0]; - bidData.h = size[0][1]; + if (size != undefined && size.length == 2) { + bidData.w = size[0]; + bidData.h = size[1]; } - addBidData(bidData, 'format', getFormat(size)); if (bidderRequest && bidderRequest.uspConsent) { Object.assign(bidData, { diff --git a/modules/uid2IdSystem.js b/modules/uid2IdSystem.js index 32d2322e9bd..f8178364e58 100644 --- a/modules/uid2IdSystem.js +++ b/modules/uid2IdSystem.js @@ -7,21 +7,13 @@ */ import { logInfo, logWarn } from '../src/utils.js'; -import { submodule } from '../src/hook.js'; +import {submodule} from '../src/hook.js'; import {getStorageManager} from '../src/storageManager.js'; import {MODULE_TYPE_UID} from '../src/activities/modules.js'; // RE below lint exception: UID2 and EUID are separate modules, but the protocol is the same and shared code makes sense here. // eslint-disable-next-line prebid/validate-imports -import { Uid2GetId, Uid2CodeVersion, extractIdentityFromParams } from './uid2IdSystem_shared.js'; -import {UID2_EIDS} from '../libraries/uid2Eids/uid2Eids.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').uid2Id} uid2Id - */ +import { Uid2GetId, Uid2CodeVersion } from './uid2IdSystem_shared.js'; const MODULE_NAME = 'uid2'; const MODULE_REVISION = Uid2CodeVersion; @@ -40,7 +32,6 @@ function createLogger(logger, prefix) { logger(prefix + ' ', ...strings); } } - const _logInfo = createLogger(logInfo, LOG_PRE_FIX); const _logWarn = createLogger(logWarn, LOG_PRE_FIX); @@ -87,20 +78,11 @@ export const uid2IdSubmodule = { clientId: UID2_CLIENT_ID, internalStorage: ADVERTISING_COOKIE } - - if (FEATURES.UID2_CSTG) { - mappedConfig.cstg = { - serverPublicKey: config?.params?.serverPublicKey, - subscriptionId: config?.params?.subscriptionId, - ...extractIdentityFromParams(config?.params ?? {}) - } - } _logInfo(`UID2 configuration loaded and mapped.`, mappedConfig); const result = Uid2GetId(mappedConfig, storage, _logInfo, _logWarn); _logInfo(`UID2 getId returned`, result); return result; }, - eids: UID2_EIDS }; function decodeImpl(value) { diff --git a/modules/uid2IdSystem.md b/modules/uid2IdSystem.md index e546f6eafe1..a795d9b1aa1 100644 --- a/modules/uid2IdSystem.md +++ b/modules/uid2IdSystem.md @@ -1,63 +1,11 @@ ## UID2 User ID Submodule -The UID2 module handles storing, providing, and optionally refreshing tokens. While initial tokens traditionally required server-side generation, the introduction of the *Client-Side Token Generation (CSTG)* mode offers publishers the flexibility to generate UID2 tokens directly from the module, eliminating this need. Publishers can choose to operate the module in one of three distinct modes: *Client Refresh* mode, *Server Only* mode and *Client-Side Token Generation* mode. +UID2 requires initial tokens to be generated server-side. The UID2 module handles storing, providing, and optionally refreshing them. The module can operate in one of two different modes: *Client Refresh* mode or *Server Only* mode. *Server Only* mode was originally referred to as *legacy mode*, but it is a popular mode for new integrations where publishers prefer to handle token refresh server-side. -*Client-Side Token Generation* mode is included in UID2 module by default. However, it's important to note that this mode is created and made available recently. For publishers who do not intend to use it, you have the option to instruct the build to exclude the code related to this feature: - -``` - $ gulp build --modules=uid2IdSystem --disable UID2_CSTG -``` -If you do plan to use Client-Side Token Generation (CSTG) mode, please consult the UID2 Team first as they will provide required configuration values for you to use (see the Client-Side Token Generation (CSTG) mode section below for details) - **Important information:** UID2 is not designed to be used where GDPR applies. The module checks the passed-in consent data and will not operate if the `gdprApplies` flag is true. -## Client-Side Token Generation (CSTG) mode - -**This mode is created and made available recently. Please consult UID2 Team first as they will provide required configuration values for you to use.** - -For publishers seeking a purely client-side integration without the complexities of server-side involvement, the CSTG mode is highly recommended. This mode requires the provision of a public key, subscription ID and [directly identifying information (DII)](https://unifiedid.com/docs/ref-info/glossary-uid#gl-dii) - either emails or phone numbers. In the CSTG mode, the module takes on the responsibility of encrypting the DII, generating the UID2 token, and handling token refreshes when necessary. - -To configure the module to use this mode, you must: -1. Set `parmas.serverPublicKey` and `params.subscriptionId` (please reach out to the UID2 team to obtain these values) -2. Provide **ONLY ONE DII** by setting **ONLY ONE** of `params.email`/`params.phone`/`params.emailHash`/`params.phoneHash` - -Below is a table that provides guidance on when to use each directly identifying information (DII) parameter, along with information on whether normalization and hashing are required by the publisher for each parameter. - -| DII param | When to use it | Normalization required by publisher? | Hashing required by publisher? | -|------------------|-------------------------------------------------------|--------------------------------------|--------------------------------| -| params.email | When you have users' email address | No | No | -| params.phone | When you have user's phone number | Yes | No | -| params.emailHash | When you have user's hashed, normalized email address | Yes | Yes | -| params.phoneHash | When you have user's hashed, normalized phone number | Yes | Yes | - - -*Note that setting params.email will normalize email addresses, but params.phone requires phone numbers to be normalized.* - -Refer to [Normalization and Encoding](#normalization-and-encoding) for details on email address normalization, SHA-256 hashing and Base64 encoding. - -### CSTG example - -Configuration: -``` -pbjs.setConfig({ - userSync: { - userIds: [{ - name: 'uid2', - params: { - serverPublicKey: '...server public key...', - subscriptionId: '...subcription id...', - email: 'user@email.com', - //phone: '+0000000', - //emailHash: '...email hash...', - //phoneHash: '...phone hash ...' - } - }] - } -}); -``` - ## Client Refresh mode This is the recommended mode for most scenarios. In this mode, the full response body from the UID2 Token Generate or Token Refresh endpoint must be provided to the module. As long as the refresh token remains valid, the module will refresh the advertising token as needed. @@ -185,80 +133,3 @@ The below parameters apply only to the UID2 User ID Module integration. | params.uid2Cookie | Optional, Client refresh | String | The name of a cookie which holds the initial UID2 token, set by the server. The cookie should contain JSON in the same format as the uid2Token param. **If uid2Token is supplied, this param is ignored.** | See the sample token above. | | params.uid2ApiBase | Optional, Client refresh | String | Overrides the default UID2 API endpoint. | `"https://prod.uidapi.com"` _(default)_| | params.storage | Optional, Client refresh | String | Specify whether to use `cookie` or `localStorage` for module-internal storage. It is recommended to not provide this and allow the module to use the default. | `localStorage` _(default)_ | -| params.serverPublicKey | Optional, Client-side token generation | String | A public key for encrypting the DII payload for the Operator's CSTG endpoint. **This is required for client-side token generation.** | - | -| params.subscriptionId | Optional, Client-side token generation | String | A publisher Identifier. **This is required for client-side token generation.** | - | -| params.email | Optional, Client-side token generation | String | The user's email address. Provide this parameter if using email as the DII. | `"test@example.com"` | -| params.emailHash | Optional, Client-side token generation | String | A hashed, normalized representation of the user's email. Provide this parameter if using emailHash as the DII. | `"tMmiiTI7IaAcPpQPFQ65uMVCWH8av9jw4cwf/F5HVRQ="` | -| params.phone | Optional, Client-side token generation | String | The user's phone number. Provide this parameter if using phone as the DII. | `"+15555555555"` | -| params.phoneHash | Optional, Client-side token generation | String | A hashed, normalized representation of the user's phone number. Provide this parameter if using phoneHash as the DII. | `"tMmiiTI7IaAcPpQPFQ65uMVCWH8av9jw4cwf/F5HVRQ="` | - -# Normalization and Encoding - -This section provides information about normalizing and encoding [directly Identifying information (DII)](https://unifiedid.com/docs/ref-info/glossary-uid#gl-dii). It's important that, in working with UID2, normalizing and encoding are performed correctly. - -## Introduction -When you're taking user information such as an email address, and following the steps to create a raw UID2 and/or a UID2 advertising token, it's very important that you follow all the required steps. Whether you normalize the information or not, whether you hash it or not, follow the steps exactly. By doing so, you can ensure that the UID2 value you create can be securely and anonymously matched up with other instances of online behavior by the same user. - ->Note: Raw UID2s, and their associated UID2 tokens, are case sensitive. When working with UID2, it's important to pass all IDs and tokens without changing the case. Mismatched IDs can cause ID parsing or token decryption errors. - -## Types of Directly Identifying Information -UID2 supports the following types of directly identifying information (DII): -- Email address -- Phone number - -## Email Address Normalization - -If you send unhashed email addresses to the UID2 Operator Service, the service normalizes the email addresses and then hashes them. If you want to hash the email addresses yourself before sending them, you must normalize them before you hash them. - -> IMPORTANT: Normalizing before hashing ensures that the generated UID2 value will always be the same, so that the data can be matched. If you do not normalize before hashing, this might result in a different UID2, reducing the effectiveness of targeted advertising. - -To normalize an email address, complete the following steps: - -1. Remove leading and trailing spaces. -2. Convert all ASCII characters to lowercase. -3. In `gmail.com` email addresses, remove the following characters from the username part of the email address: - 1. The period (`.` (ASCII code 46)).
For example, normalize `jane.doe@gmail.com` to `janedoe@gmail.com`. - 2. The plus sign (`+` (ASCII code 43)) and all subsequent characters.
For example, normalize `janedoe+home@gmail.com` to `janedoe@gmail.com`. - -## Email Address Hash Encoding - -An email hash is a Base64-encoded SHA-256 hash of a normalized email address. The email address is first normalized, then hashed using the SHA-256 hashing algorithm, and then the resulting bytes of the hash value are encoded using Base64 encoding. Note that the bytes of the hash value are encoded, not the hex-encoded string representation. - -| Type | Example | Comments and Usage | -| :--- | :--- | :--- | -| Normalized email address | `user@example.com` | Normalization is always the first step. | -| SHA-256 hash of normalized email address | `b4c9a289323b21a01c3e940f150eb9b8c542587f1abfd8f0e1cc1ffc5e475514` | This 64-character string is a hex-encoded representation of the 32-byte SHA-256.| -| Hex to Base64 SHA-256 encoding of normalized email address | `tMmiiTI7IaAcPpQPFQ65uMVCWH8av9jw4cwf/F5HVRQ=` | This 44-character string is a Base64-encoded representation of the 32-byte SHA-256.
WARNING: The SHA-256 hash string in the example above is a hex-encoded representation of the hash value. You must Base64-encode the raw bytes of the hash or use a Base64 encoder that takes a hex-encoded value as input.
Use this encoding for `email_hash` values sent in the request body. | - ->WARNING: When applying Base64 encoding, be sure to Base64-encode the raw bytes of the hash or use a Base64 encoder that takes a hex-encoded value as input. - -## Phone Number Normalization - -If you send unhashed phone numbers to the UID2 Operator Service, the service normalizes the phone numbers and then hashes them. If you want to hash the phone numbers yourself before sending them, you must normalize them before you hash them. - -> IMPORTANT: Normalization before hashing ensures that the generated UID2 value will always be the same, so that the data can be matched. If you do not normalize before hashing, this might result in a different UID2, reducing the effectiveness of targeted advertising. - -Here's what you need to know about phone number normalization rules: - -- The UID2 Operator accepts phone numbers in the [E.164](https://en.wikipedia.org/wiki/E.164) format, which is the international phone number format that ensures global uniqueness. -- E.164 phone numbers can have a maximum of 15 digits. -- Normalized E.164 phone numbers use the following syntax, with no spaces, hyphens, parentheses, or other special characters:
- `[+] [country code] [subscriber number including area code]` - Examples: - - US: `1 (123) 456-7890` is normalized to `+11234567890`. - - Singapore: `65 1243 5678` is normalized to `+6512345678`. - - Sydney, Australia: `(02) 1234 5678` is normalized to drop the leading zero for the city plus include the country code: `+61212345678`. - -## Phone Number Hash Encoding - -A phone number hash is a Base64-encoded SHA-256 hash of a normalized phone number. The phone number is first normalized, then hashed using the SHA-256 hashing algorithm, and the resulting hex value is encoded using Base64 encoding. - -The example below shows a simple input phone number, and the result as each step is applied to arrive at a secure, opaque, URL-safe value. - -| Type | Example | Comments and Usage | -| :--- | :--- | :--- | -| Normalized phone number | `+12345678901` | Normalization is always the first step. | -| SHA-256 hash of normalized phone number | `10e6f0b47054a83359477dcb35231db6de5c69fb1816e1a6b98e192de9e5b9ee` |This 64-character string is a hex-encoded representation of the 32-byte SHA-256. | -| Hex to Base64 SHA-256 encoding of normalized and hashed phone number | `EObwtHBUqDNZR33LNSMdtt5cafsYFuGmuY4ZLenlue4=` | This 44-character string is a Base64-encoded representation of the 32-byte SHA-256.
NOTE: The SHA-256 hash is a hexadecimal value. You must use a Base64 encoder that takes a hex value as input. Use this encoding for `phone_hash` values sent in the request body. | - ->WARNING: When applying Base64 encoding, be sure to use a function that takes a hex value as input. If you use a function that takes text as input, the result is a longer string which is invalid for the purposes of UID2. diff --git a/modules/uid2IdSystem_shared.js b/modules/uid2IdSystem_shared.js index acc440eafc5..0f6894a9d3e 100644 --- a/modules/uid2IdSystem_shared.js +++ b/modules/uid2IdSystem_shared.js @@ -1,7 +1,4 @@ /* eslint-disable no-console */ -import { ajax } from '../src/ajax.js'; -import { cyrb53Hash } from '../src/utils.js'; - export const Uid2CodeVersion = '1.1'; function isValidIdentity(identity) { @@ -16,7 +13,6 @@ export class Uid2ApiClient { this._logInfo = logInfo; this._logWarn = logWarn; } - createArrayBuffer(text) { const arrayBuffer = new Uint8Array(text.length); for (let i = 0; i < text.length; i++) { @@ -40,58 +36,49 @@ export class Uid2ApiClient { } callRefreshApi(refreshDetails) { const url = this._baseUrl + '/v2/token/refresh'; + const req = new XMLHttpRequest(); + req.overrideMimeType('text/plain'); + req.open('POST', url, true); + req.setRequestHeader('X-UID2-Client-Version', this._clientVersion); let resolvePromise; let rejectPromise; const promise = new Promise((resolve, reject) => { resolvePromise = resolve; rejectPromise = reject; }); - this._logInfo('Sending refresh request', refreshDetails); - ajax(url, { - success: (responseText) => { - try { - if (!refreshDetails.refresh_response_key) { - this._logInfo('No response decryption key available, assuming unencrypted JSON'); - const response = JSON.parse(responseText); - const result = this.ResponseToRefreshResult(response); - if (typeof result === 'string') { rejectPromise(result); } else { resolvePromise(result); } - } else { - this._logInfo('Decrypting refresh API response'); - const encodeResp = this.createArrayBuffer(atob(responseText)); - window.crypto.subtle.importKey('raw', this.createArrayBuffer(atob(refreshDetails.refresh_response_key)), { name: 'AES-GCM' }, false, ['decrypt']).then((key) => { - this._logInfo('Imported decryption key') - // returns the symmetric key - window.crypto.subtle.decrypt({ - name: 'AES-GCM', - iv: encodeResp.slice(0, 12), - tagLength: 128, // The tagLength you used to encrypt (if any) - }, key, encodeResp.slice(12)).then((decrypted) => { - const decryptedResponse = String.fromCharCode(...new Uint8Array(decrypted)); - this._logInfo('Decrypted to:', decryptedResponse); - const response = JSON.parse(decryptedResponse); - const result = this.ResponseToRefreshResult(response); - if (typeof result === 'string') { rejectPromise(result); } else { resolvePromise(result); } - }, (reason) => this._logWarn(`Call to UID2 API failed`, reason)); - }, (reason) => this._logWarn(`Call to UID2 API failed`, reason)); - } - } catch (_err) { - rejectPromise(responseText); - } - }, - error: (error, xhr) => { - try { - this._logInfo('Error status, assuming unencrypted JSON'); - const response = JSON.parse(xhr.responseText); + req.onreadystatechange = () => { + if (req.readyState !== req.DONE) { return; } + try { + if (!refreshDetails.refresh_response_key || req.status !== 200) { + this._logInfo('Error status OR no response decryption key available, assuming unencrypted JSON'); + const response = JSON.parse(req.responseText); const result = this.ResponseToRefreshResult(response); if (typeof result === 'string') { rejectPromise(result); } else { resolvePromise(result); } - } catch (_e) { - rejectPromise(error) + } else { + this._logInfo('Decrypting refresh API response'); + const encodeResp = this.createArrayBuffer(atob(req.responseText)); + window.crypto.subtle.importKey('raw', this.createArrayBuffer(atob(refreshDetails.refresh_response_key)), { name: 'AES-GCM' }, false, ['decrypt']).then((key) => { + this._logInfo('Imported decryption key') + // returns the symmetric key + window.crypto.subtle.decrypt({ + name: 'AES-GCM', + iv: encodeResp.slice(0, 12), + tagLength: 128, // The tagLength you used to encrypt (if any) + }, key, encodeResp.slice(12)).then((decrypted) => { + const decryptedResponse = String.fromCharCode(...new Uint8Array(decrypted)); + this._logInfo('Decrypted to:', decryptedResponse); + const response = JSON.parse(decryptedResponse); + const result = this.ResponseToRefreshResult(response); + if (typeof result === 'string') { rejectPromise(result); } else { resolvePromise(result); } + }, (reason) => this._logWarn(`Call to UID2 API failed`, reason)); + }, (reason) => this._logWarn(`Call to UID2 API failed`, reason)); } + } catch (err) { + rejectPromise(err); } - }, refreshDetails.refresh_token, { method: 'POST', - customHeaders: { - 'X-UID2-Client-Version': this._clientVersion - } }); + }; + this._logInfo('Sending refresh request', refreshDetails); + req.send(refreshDetails.refresh_token); return promise; } } @@ -173,507 +160,18 @@ function refreshTokenAndStore(baseUrl, token, clientId, storageManager, _logInfo originalToken: token, latestToken: response.identity, }; - let storedTokens = storageManager.getStoredValueWithFallback(); - if (storedTokens?.originalIdentity) tokens.originalIdentity = storedTokens.originalIdentity; storageManager.storeValue(tokens); return tokens; }); } -let clientSideTokenGenerator; -if (FEATURES.UID2_CSTG) { - const SERVER_PUBLIC_KEY_PREFIX_LENGTH = 9; - - clientSideTokenGenerator = { - isCSTGOptionsValid(maybeOpts, _logWarn) { - if (typeof maybeOpts !== 'object' || maybeOpts === null) { - _logWarn('CSTG opts must be an object'); - return false; - } - - const opts = maybeOpts; - if (typeof opts.serverPublicKey !== 'string') { - _logWarn('CSTG opts.serverPublicKey must be a string'); - return false; - } - const serverPublicKeyPrefix = /^UID2-X-[A-Z]-.+/; - if (!serverPublicKeyPrefix.test(opts.serverPublicKey)) { - _logWarn( - `CSTG opts.serverPublicKey must match the regular expression ${serverPublicKeyPrefix}` - ); - return false; - } - // We don't do any further validation of the public key, as we will find out - // later if it's valid by using importKey. - - if (typeof opts.subscriptionId !== 'string') { - _logWarn('CSTG opts.subscriptionId must be a string'); - return false; - } - if (opts.subscriptionId.length === 0) { - _logWarn('CSTG opts.subscriptionId is empty'); - return false; - } - return true; - }, - - getValidIdentity(opts, _logWarn) { - if (opts.emailHash) { - if (!UID2DiiNormalization.isBase64Hash(opts.emailHash)) { - _logWarn('CSTG opts.emailHash is invalid'); - return; - } - return { email_hash: opts.emailHash }; - } - - if (opts.phoneHash) { - if (!UID2DiiNormalization.isBase64Hash(opts.phoneHash)) { - _logWarn('CSTG opts.phoneHash is invalid'); - return; - } - return { phone_hash: opts.phoneHash }; - } - - if (opts.email) { - const normalizedEmail = UID2DiiNormalization.normalizeEmail(opts.email); - if (normalizedEmail === undefined) { - _logWarn('CSTG opts.email is invalid'); - return; - } - return { email: normalizedEmail }; - } - - if (opts.phone) { - if (!UID2DiiNormalization.isNormalizedPhone(opts.phone)) { - _logWarn('CSTG opts.phone is invalid'); - return; - } - return { phone: opts.phone }; - } - }, - - isStoredTokenInvalid(cstgIdentity, storedTokens, _logInfo, _logWarn) { - if (storedTokens) { - const identity = Object.values(cstgIdentity)[0]; - if (!this.isStoredTokenFromSameIdentity(storedTokens, identity)) { - _logInfo( - 'CSTG supplied new identity - ignoring stored value.', - storedTokens.originalIdentity, - cstgIdentity - ); - // Stored token wasn't originally sourced from the provided identity - ignore the stored value. A new user has logged in? - return true; - } - } - return false; - }, - - async generateTokenAndStore( - baseUrl, - cstgOpts, - cstgIdentity, - storageManager, - _logInfo, - _logWarn - ) { - _logInfo('UID2 cstg opts provided: ', JSON.stringify(cstgOpts)); - const client = new UID2CstgApiClient( - { baseUrl, cstg: cstgOpts }, - _logInfo, - _logWarn - ); - const response = await client.generateToken(cstgIdentity); - _logInfo('CSTG endpoint responded with:', response); - const tokens = { - originalIdentity: this.encodeOriginalIdentity(cstgIdentity), - latestToken: response.identity, - }; - storageManager.storeValue(tokens); - return tokens; - }, - - isStoredTokenFromSameIdentity(storedTokens, identity) { - if (!storedTokens.originalIdentity) return false; - return ( - cyrb53Hash(identity, storedTokens.originalIdentity.salt) === - storedTokens.originalIdentity.identity - ); - }, - - encodeOriginalIdentity(identity) { - const identityValue = Object.values(identity)[0]; - const salt = Math.floor(Math.random() * Math.pow(2, 32)); - return { - identity: cyrb53Hash(identityValue, salt), - salt, - }; - }, - }; - - class UID2DiiNormalization { - static EMAIL_EXTENSION_SYMBOL = '+'; - static EMAIL_DOT = '.'; - static GMAIL_DOMAIN = 'gmail.com'; - - static isBase64Hash(value) { - if (!(value && value.length === 44)) { - return false; - } - - try { - return btoa(atob(value)) === value; - } catch (err) { - return false; - } - } - - static isNormalizedPhone(phone) { - return /^\+[0-9]{10,15}$/.test(phone); - } - - static normalizeEmail(email) { - if (!email || !email.length) return; - - const parsedEmail = email.trim().toLowerCase(); - if (parsedEmail.indexOf(' ') > 0) return; - - const emailParts = this.splitEmailIntoAddressAndDomain(parsedEmail); - if (!emailParts) return; - - const { address, domain } = emailParts; - - const emailIsGmail = this.isGmail(domain); - const parsedAddress = this.normalizeAddressPart( - address, - emailIsGmail, - emailIsGmail - ); - - return parsedAddress ? `${parsedAddress}@${domain}` : undefined; - } - - static splitEmailIntoAddressAndDomain(email) { - const parts = email.split('@'); - if ( - parts.length !== 2 || - parts.some((part) => part === '') - ) { return; } - - return { - address: parts[0], - domain: parts[1], - }; - } - - static isGmail(domain) { - return domain === this.GMAIL_DOMAIN; - } - - static dropExtension(address, extensionSymbol = this.EMAIL_EXTENSION_SYMBOL) { - return address.split(extensionSymbol)[0]; - } - - static normalizeAddressPart(address, shouldRemoveDot, shouldDropExtension) { - let parsedAddress = address; - if (shouldRemoveDot) { parsedAddress = parsedAddress.replaceAll(this.EMAIL_DOT, ''); } - if (shouldDropExtension) parsedAddress = this.dropExtension(parsedAddress); - return parsedAddress; - } - } - - class UID2CstgApiClient { - constructor(opts, logInfo, logWarn) { - this._baseUrl = opts.baseUrl; - this._serverPublicKey = opts.cstg.serverPublicKey; - this._subscriptionId = opts.cstg.subscriptionId; - this._logInfo = logInfo; - this._logWarn = logWarn; - } - - hasStatusResponse(response) { - return typeof response === 'object' && response && response.status; - } - - isCstgApiSuccessResponse(response) { - return ( - this.hasStatusResponse(response) && - response.status === 'success' && - isValidIdentity(response.body) - ); - } - - isCstgApiClientErrorResponse(response) { - return ( - this.hasStatusResponse(response) && - response.status === 'client_error' && - typeof response.message === 'string' - ); - } - - isCstgApiForbiddenResponse(response) { - return ( - this.hasStatusResponse(response) && - response.status === 'invalid_http_origin' && - typeof response.message === 'string' - ); - } - - stripPublicKeyPrefix(serverPublicKey) { - return serverPublicKey.substring(SERVER_PUBLIC_KEY_PREFIX_LENGTH); - } - - async generateCstgRequest(cstgIdentity) { - if ('email_hash' in cstgIdentity || 'phone_hash' in cstgIdentity) { - return cstgIdentity; - } - if ('email' in cstgIdentity) { - const emailHash = await UID2CstgCrypto.hash(cstgIdentity.email); - return { email_hash: emailHash }; - } - if ('phone' in cstgIdentity) { - const phoneHash = await UID2CstgCrypto.hash(cstgIdentity.phone); - return { phone_hash: phoneHash }; - } - } - - async generateToken(cstgIdentity) { - const request = await this.generateCstgRequest(cstgIdentity); - this._logInfo('Building CSTG request for', request); - const box = await UID2CstgBox.build( - this.stripPublicKeyPrefix(this._serverPublicKey) - ); - const encoder = new TextEncoder(); - const now = Date.now(); - const { iv, ciphertext } = await box.encrypt( - encoder.encode(JSON.stringify(request)), - encoder.encode(JSON.stringify([now])) - ); - - const exportedPublicKey = await UID2CstgCrypto.exportPublicKey( - box.clientPublicKey - ); - const requestBody = { - payload: UID2CstgCrypto.bytesToBase64(new Uint8Array(ciphertext)), - iv: UID2CstgCrypto.bytesToBase64(new Uint8Array(iv)), - public_key: UID2CstgCrypto.bytesToBase64( - new Uint8Array(exportedPublicKey) - ), - timestamp: now, - subscription_id: this._subscriptionId, - }; - return this.callCstgApi(requestBody, box); - } - - async callCstgApi(requestBody, box) { - const url = this._baseUrl + '/v2/token/client-generate'; - let resolvePromise; - let rejectPromise; - const promise = new Promise((resolve, reject) => { - resolvePromise = resolve; - rejectPromise = reject; - }); - - this._logInfo('Sending CSTG request', requestBody); - ajax( - url, - { - success: async (responseText, xhr) => { - try { - const encodedResp = UID2CstgCrypto.base64ToBytes(responseText); - const decrypted = await box.decrypt( - encodedResp.slice(0, 12), - encodedResp.slice(12) - ); - const decryptedResponse = new TextDecoder().decode(decrypted); - const response = JSON.parse(decryptedResponse); - if (this.isCstgApiSuccessResponse(response)) { - resolvePromise({ - status: 'success', - identity: response.body, - }); - } else { - // A 200 should always be a success response. - // Something has gone wrong. - rejectPromise( - `API error: Response body was invalid for HTTP status 200: ${decryptedResponse}` - ); - } - } catch (err) { - rejectPromise(err); - } - }, - error: (error, xhr) => { - try { - if (xhr.status === 400) { - const response = JSON.parse(xhr.responseText); - if (this.isCstgApiClientErrorResponse(response)) { - rejectPromise(`Client error: ${response.message}`); - } else { - // A 400 should always be a client error. - // Something has gone wrong. - rejectPromise( - `API error: Response body was invalid for HTTP status 400: ${xhr.responseText}` - ); - } - } else if (xhr.status === 403) { - const response = JSON.parse(xhr.responseText); - if (this.isCstgApiForbiddenResponse(xhr)) { - rejectPromise(`Forbidden: ${response.message}`); - } else { - // A 403 should always be a forbidden response. - // Something has gone wrong. - rejectPromise( - `API error: Response body was invalid for HTTP status 403: ${xhr.responseText}` - ); - } - } else { - rejectPromise( - `API error: Unexpected HTTP status ${xhr.status}: ${error}` - ); - } - } catch (_e) { - rejectPromise(error); - } - }, - }, - JSON.stringify(requestBody), - { method: 'POST' } - ); - return promise; - } - } - - class UID2CstgBox { - static _namedCurve = 'P-256'; - constructor(clientPublicKey, sharedKey) { - this._clientPublicKey = clientPublicKey; - this._sharedKey = sharedKey; - } - - static async build(serverPublicKey) { - const clientKeyPair = await UID2CstgCrypto.generateKeyPair( - UID2CstgBox._namedCurve - ); - const importedServerPublicKey = await UID2CstgCrypto.importPublicKey( - serverPublicKey, - this._namedCurve - ); - const sharedKey = await UID2CstgCrypto.deriveKey( - importedServerPublicKey, - clientKeyPair.privateKey - ); - return new UID2CstgBox(clientKeyPair.publicKey, sharedKey); - } - - async encrypt(plaintext, additionalData) { - const iv = window.crypto.getRandomValues(new Uint8Array(12)); - const ciphertext = await window.crypto.subtle.encrypt( - { - name: 'AES-GCM', - iv, - additionalData, - }, - this._sharedKey, - plaintext - ); - return { iv, ciphertext }; - } - - async decrypt(iv, ciphertext) { - return window.crypto.subtle.decrypt( - { - name: 'AES-GCM', - iv, - }, - this._sharedKey, - ciphertext - ); - } - - get clientPublicKey() { - return this._clientPublicKey; - } - } - - class UID2CstgCrypto { - static base64ToBytes(base64) { - const binString = atob(base64); - return Uint8Array.from(binString, (m) => m.codePointAt(0)); - } - - static bytesToBase64(bytes) { - const binString = Array.from(bytes, (x) => String.fromCodePoint(x)).join( - '' - ); - return btoa(binString); - } - - static async generateKeyPair(namedCurve) { - const params = { - name: 'ECDH', - namedCurve: namedCurve, - }; - return window.crypto.subtle.generateKey(params, false, ['deriveKey']); - } - - static async importPublicKey(publicKey, namedCurve) { - const params = { - name: 'ECDH', - namedCurve: namedCurve, - }; - return window.crypto.subtle.importKey( - 'spki', - this.base64ToBytes(publicKey), - params, - false, - [] - ); - } - - static exportPublicKey(publicKey) { - return window.crypto.subtle.exportKey('spki', publicKey); - } - - static async deriveKey(serverPublicKey, clientPrivateKey) { - return window.crypto.subtle.deriveKey( - { - name: 'ECDH', - public: serverPublicKey, - }, - clientPrivateKey, - { - name: 'AES-GCM', - length: 256, - }, - false, - ['encrypt', 'decrypt'] - ); - } - - static async hash(value) { - const hash = await window.crypto.subtle.digest( - 'SHA-256', - new TextEncoder().encode(value) - ); - return this.bytesToBase64(new Uint8Array(hash)); - } - } -} - export function Uid2GetId(config, prebidStorageManager, _logInfo, _logWarn) { let suppliedToken = null; const preferLocalStorage = (config.storage !== 'cookie'); const storageManager = new Uid2StorageManager(prebidStorageManager, preferLocalStorage, config.internalStorage, _logInfo); _logInfo(`Module is using ${preferLocalStorage ? 'local storage' : 'cookies'} for internal storage.`); - const isCstgEnabled = - clientSideTokenGenerator && - clientSideTokenGenerator.isCSTGOptionsValid(config.cstg, _logWarn); - if (isCstgEnabled) { - _logInfo(`Module is using client-side token generation.`); - // Ignores config.paramToken and config.serverCookieName if any is provided - suppliedToken = null; - } else if (config.paramToken) { + if (config.paramToken) { suppliedToken = config.paramToken; _logInfo('Read token from params', suppliedToken); } else if (config.serverCookieName) { @@ -686,7 +184,7 @@ export function Uid2GetId(config, prebidStorageManager, _logInfo, _logWarn) { if (storedTokens && typeof storedTokens === 'string') { // Stored value is a plain token - if no token is supplied, just use the stored value. - if (!suppliedToken && !isCstgEnabled) { + if (!suppliedToken) { _logInfo('Returning legacy cookie value.'); return { id: storedTokens }; } @@ -702,31 +200,11 @@ export function Uid2GetId(config, prebidStorageManager, _logInfo, _logWarn) { storedTokens = null; } } - - if (FEATURES.UID2_CSTG && isCstgEnabled) { - const cstgIdentity = clientSideTokenGenerator.getValidIdentity(config.cstg, _logWarn); - if (cstgIdentity) { - if (storedTokens && clientSideTokenGenerator.isStoredTokenInvalid(cstgIdentity, storedTokens, _logInfo, _logWarn)) { - storedTokens = null; - } - - 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); - }); - } }; - } - } - } - + // At this point, any legacy values or superseded stored tokens have been nulled out. const useSuppliedToken = !(storedTokens?.latestToken) || (suppliedToken && suppliedToken.identity_expires > storedTokens.latestToken.identity_expires); const newestAvailableToken = useSuppliedToken ? suppliedToken : storedTokens.latestToken; _logInfo('UID2 module selected latest token', useSuppliedToken, newestAvailableToken); - if ((!newestAvailableToken || Date.now() > newestAvailableToken.refresh_expires)) { + if (!newestAvailableToken || Date.now() > newestAvailableToken.refresh_expires) { _logInfo('Newest available token is expired and not refreshable.'); return { id: null }; } @@ -749,21 +227,6 @@ export function Uid2GetId(config, prebidStorageManager, _logInfo, _logWarn) { originalToken: suppliedToken ?? storedTokens?.originalToken, latestToken: newestAvailableToken, }; - if (FEATURES.UID2_CSTG && isCstgEnabled) { - tokens.originalIdentity = storedTokens?.originalIdentity; - } storageManager.storeValue(tokens); return { id: tokens }; } - -export function extractIdentityFromParams(params) { - const keysToCheck = ['emailHash', 'phoneHash', 'email', 'phone']; - - for (let key of keysToCheck) { - if (params.hasOwnProperty(key)) { - return { [key]: params[key] }; - } - } - - return {}; -} diff --git a/modules/underdogmediaBidAdapter.js b/modules/underdogmediaBidAdapter.js index 54b74c7ccd4..7561cdb60de 100644 --- a/modules/underdogmediaBidAdapter.js +++ b/modules/underdogmediaBidAdapter.js @@ -4,6 +4,7 @@ import { getWindowSelf, getWindowTop, isGptPubadsDefined, + isSlotMatchingAdUnitCode, logInfo, logMessage, logWarn, @@ -11,7 +12,6 @@ import { } from '../src/utils.js'; import {config} from '../src/config.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {isSlotMatchingAdUnitCode} from '../libraries/gptUtils/gptUtils.js'; const BIDDER_CODE = 'underdogmedia'; const UDM_ADAPTER_VERSION = '7.30V'; @@ -71,7 +71,7 @@ export const spec = { let data = { dt: 10, gdpr: {}, - pbTimeout: +config.getConfig('bidderTimeout') || 3001, // KP: convert to number and if NaN we default to 3001. Particular value to let us know that there was a problem in converting pbTimeout + pbTimeout: config.getConfig('bidderTimeout'), pbjsVersion: prebidVersion, placements: [], ref: deepAccess(bidderRequest, 'refererInfo.page') ? bidderRequest.refererInfo.page : undefined, diff --git a/modules/undertoneBidAdapter.js b/modules/undertoneBidAdapter.js index c7e8102ffc9..9dcbefd374b 100644 --- a/modules/undertoneBidAdapter.js +++ b/modules/undertoneBidAdapter.js @@ -139,7 +139,6 @@ export const spec = { domain: domain, placementId: bidReq.params.placementId != undefined ? bidReq.params.placementId : null, publisherId: bidReq.params.publisherId, - gpid: deepAccess(bidReq, 'ortb2Imp.ext.gpid', deepAccess(bidReq, 'ortb2Imp.ext.data.pbadslot', '')), sizes: bidReq.sizes, params: bidReq.params }; diff --git a/modules/undertoneBidAdapter.md b/modules/undertoneBidAdapter.md index 1cfc912e360..8e0b234fd7a 100644 --- a/modules/undertoneBidAdapter.md +++ b/modules/undertoneBidAdapter.md @@ -23,7 +23,7 @@ Module that connects to Undertone's demand sources { bidder: "undertone", params: { - placementId: 1234, + placementId: '10433394', publisherId: 12345 } } diff --git a/modules/unicornBidAdapter.js b/modules/unicornBidAdapter.js index 43eb943f6d5..66aaf4a17e5 100644 --- a/modules/unicornBidAdapter.js +++ b/modules/unicornBidAdapter.js @@ -3,11 +3,6 @@ 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 - * @typedef {import('../src/adapters/bidderFactory.js').ServerRequest} ServerRequest - */ - const BIDDER_CODE = 'unicorn'; const UNICORN_ENDPOINT = 'https://ds.uncn.jp/pb/0/bid.json'; const UNICORN_DEFAULT_CURRENCY = 'JPY'; @@ -92,33 +87,10 @@ function buildOpenRtbBidRequestPayload(validBidRequests, bidderRequest) { accountId: deepAccess(validBidRequests[0], 'params.accountId') } }; - const eids = initializeEids(validBidRequests[0]); - if (eids.length > 0) { - request.user.eids = eids; - } - logInfo('[UNICORN] OpenRTB Formatted Request:', request); return JSON.stringify(request); } -const initializeEids = (bidRequest) => { - let eids = []; - - let id5 = deepAccess(bidRequest, 'userId.id5id.uid'); - if (id5) { - eids.push({ - source: 'id5-sync.com', - uids: [ - { - id: id5 - } - ] - }); - } - - return eids; -} - const interpretResponse = (serverResponse, request) => { logInfo('[UNICORN] interpretResponse.serverResponse:', serverResponse); logInfo('[UNICORN] interpretResponse.request:', request); diff --git a/modules/unifiedIdSystem.js b/modules/unifiedIdSystem.js index e88aec3a90f..8ec5fcd3f90 100644 --- a/modules/unifiedIdSystem.js +++ b/modules/unifiedIdSystem.js @@ -9,12 +9,6 @@ import { logError } from '../src/utils.js'; import {ajax} from '../src/ajax.js'; import {submodule} from '../src/hook.js' -/** - * @typedef {import('../modules/userId/index.js').Submodule} Submodule - * @typedef {import('../modules/userId/index.js').SubmoduleConfig} SubmoduleConfig - * @typedef {import('../modules/userId/index.js').IdResponse} IdResponse - */ - const MODULE_NAME = 'unifiedId'; /** @type {Submodule} */ @@ -73,17 +67,6 @@ export const unifiedIdSubmodule = { ajax(url, callbacks, undefined, {method: 'GET', withCredentials: true}); }; return {callback: resp}; - }, - eids: { - 'tdid': { - source: 'adserver.org', - atype: 1, - getUidExt: function() { - return { - rtiPartner: 'TDID' - }; - } - }, } }; diff --git a/modules/unrulyBidAdapter.js b/modules/unrulyBidAdapter.js index b825003f36f..d893bfd3038 100644 --- a/modules/unrulyBidAdapter.js +++ b/modules/unrulyBidAdapter.js @@ -56,12 +56,6 @@ 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 invalidBidsCount = bidderRequest.bids.length - validBidRequests.length; @@ -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); }); @@ -80,14 +73,7 @@ const getRequests = (conf, validBidRequests, bidderRequest) => { Object.keys(requestBySiteId).forEach((key) => { let data = { - bidderRequest: Object.assign({}, - { - bids: requestBySiteId[key], - invalidBidsCount, - prebidVersion: '$prebid.version$', - ...bidderRequestData - } - ) + bidderRequest: Object.assign({}, {bids: requestBySiteId[key], invalidBidsCount, ...bidderRequestData}) }; request.push(Object.assign({}, {data, ...conf})); @@ -220,49 +206,21 @@ export const adapter = { endPoint = deepAccess(validBidRequests[0], 'params.endpoint') || endPoint; } - return getRequests({ - 'url': endPoint, - 'method': 'POST', - 'options': { - 'contentType': 'application/json' - }, - 'protectedAudienceEnabled': bidderRequest.fledgeEnabled - }, validBidRequests, bidderRequest); + const url = endPoint; + const method = 'POST'; + const options = {contentType: 'application/json'}; + return getRequests({url, method, options}, validBidRequests, bidderRequest); }, - interpretResponse: function (serverResponse) { - if (!(serverResponse && serverResponse.body && (serverResponse.body.auctionConfigs || serverResponse.body.bids))) { - return []; - } - + interpretResponse: function (serverResponse = {}) { const serverResponseBody = serverResponse.body; - let bids = []; - let fledgeAuctionConfigs = null; - if (serverResponseBody.bids.length) { - bids = handleBidResponseByMediaType(serverResponseBody.bids); - } - if (serverResponseBody.auctionConfigs) { - let auctionConfigs = serverResponseBody.auctionConfigs; - let bidIdList = Object.keys(auctionConfigs); - if (bidIdList.length) { - bidIdList.forEach((bidId) => { - fledgeAuctionConfigs = [{ - 'bidId': bidId, - 'config': auctionConfigs[bidId] - }]; - }) - } - } + const noBidsResponse = []; + const isInvalidResponse = !serverResponseBody || !serverResponseBody.bids; - if (!fledgeAuctionConfigs) { - return bids; - } - - return { - bids, - fledgeAuctionConfigs - }; + return isInvalidResponse + ? noBidsResponse + : handleBidResponseByMediaType(serverResponseBody.bids); } }; diff --git a/modules/userId/eids.js b/modules/userId/eids.js index e5f7e3b8fb2..e8e83ca19a0 100644 --- a/modules/userId/eids.js +++ b/modules/userId/eids.js @@ -1,10 +1,421 @@ -import {deepAccess, deepClone, isFn, isPlainObject, isStr} from '../../src/utils.js'; +import { pick, isFn, isStr, isPlainObject, deepAccess } from '../../src/utils.js'; -export const EID_CONFIG = new Map(); +// Each user-id sub-module is expected to mention respective config here +export const USER_IDS_CONFIG = { + + // key-name : {config} + + // GrowthCode + 'growthCodeId': { + getValue: function(data) { + return data.gc_id + }, + source: 'growthcode.io', + atype: 1, + getUidExt: function(data) { + const extendedData = pick(data, [ + 'h1', + 'h2', + 'h3', + ]); + if (Object.keys(extendedData).length) { + return extendedData; + } + } + }, + + // utiq + 'utiq': { + source: 'utiq.com', + atype: 1, + getValue: function (data) { + return data; + }, + }, + + // intentIqId + 'intentIqId': { + source: 'intentiq.com', + atype: 1 + }, + + // naveggId + 'naveggId': { + source: 'navegg.com', + atype: 1 + }, + + // pairId + 'pairId': { + source: 'google.com', + atype: 571187 + }, + + // justId + 'justId': { + source: 'justtag.com', + atype: 1 + }, + + // pubCommonId + 'pubcid': { + source: 'pubcid.org', + atype: 1 + }, + + // unifiedId + 'tdid': { + source: 'adserver.org', + atype: 1, + getUidExt: function() { + return { + rtiPartner: 'TDID' + }; + } + }, + + // id5Id + 'id5id': { + getValue: function(data) { + return data.uid + }, + source: 'id5-sync.com', + atype: 1, + getUidExt: function(data) { + if (data.ext) { + return data.ext; + } + } + }, + + // ftrack + 'ftrackId': { + source: 'flashtalking.com', + atype: 1, + getValue: function(data) { + let value = ''; + if (data && data.ext && data.ext.DeviceID) { + value = data.ext.DeviceID; + } + return value; + }, + getUidExt: function(data) { + return data && data.ext; + } + }, + + // parrableId + 'parrableId': { + source: 'parrable.com', + atype: 1, + getValue: function(parrableId) { + if (parrableId.eid) { + return parrableId.eid; + } + if (parrableId.ccpaOptout) { + // If the EID was suppressed due to a non consenting ccpa optout then + // we still wish to provide this as a reason to the adapters + return ''; + } + return null; + }, + getUidExt: function(parrableId) { + const extendedData = pick(parrableId, [ + 'ibaOptout', + 'ccpaOptout' + ]); + if (Object.keys(extendedData).length) { + return extendedData; + } + } + }, + + // identityLink + 'idl_env': { + source: 'liveramp.com', + atype: 3 + }, + + // liveIntentId + 'lipb': { + getValue: function(data) { + return data.lipbid; + }, + source: 'liveintent.com', + atype: 3, + getEidExt: function(data) { + if (Array.isArray(data.segments) && data.segments.length) { + return { + segments: data.segments + }; + } + } + }, + + // bidswitchId + 'bidswitch': { + source: 'bidswitch.net', + atype: 3, + getValue: function(data) { + return data.id; + } + }, + + // medianetId + 'medianet': { + source: 'media.net', + atype: 3, + getValue: function(data) { + return data.id; + } + }, + + // britepoolId + 'britepoolid': { + source: 'britepool.com', + atype: 3 + }, + + // dmdId + 'dmdId': { + source: 'hcn.health', + atype: 3 + }, + + // lotamePanoramaId + lotamePanoramaId: { + source: 'crwdcntrl.net', + atype: 1, + }, + + // criteo + 'criteoId': { + source: 'criteo.com', + atype: 1 + }, + + // merkleId + 'merkleId': { + atype: 3, + getSource: function(data) { + if (data?.ext?.ssp) { + return `${data.ext.ssp}.merkleinc.com` + } + return 'merkleinc.com' + }, + getValue: function(data) { + return data.id; + }, + getUidExt: function(data) { + if (data.keyID) { + return { + keyID: data.keyID + } + } + if (data.ext) { + return data.ext; + } + } + }, + + // NetId + 'netId': { + source: 'netid.de', + atype: 1 + }, + + // zeotapIdPlus + 'IDP': { + source: 'zeotap.com', + atype: 1 + }, + + // hadronId + 'hadronId': { + source: 'audigent.com', + atype: 1 + }, + + // quantcastId + 'quantcastId': { + source: 'quantcast.com', + atype: 1 + }, + + // IDx + 'idx': { + source: 'idx.lat', + atype: 1 + }, + + // Verizon Media ConnectID + 'connectid': { + source: 'verizonmedia.com', + atype: 3 + }, + + // Neustar Fabrick + 'fabrickId': { + source: 'neustar.biz', + atype: 1 + }, + + // MediaWallah OpenLink + 'mwOpenLinkId': { + source: 'mediawallahscript.com', + atype: 1 + }, + + 'tapadId': { + source: 'tapad.com', + atype: 1 + }, + + // Novatiq Snowflake + 'novatiq': { + getValue: function(data) { + if (data.snowflake.id === undefined) { + return data.snowflake; + } + + return data.snowflake.id; + }, + source: 'novatiq.com', + }, + + 'uid2': { + source: 'uidapi.com', + atype: 3, + getValue: function(data) { + return data.id; + } + }, + + 'euid': { + source: 'euid.eu', + atype: 3, + getValue: function(data) { + return data.id; + } + }, + + 'deepintentId': { + source: 'deepintent.com', + atype: 3 + }, + + // Admixer Id + 'admixerId': { + source: 'admixer.net', + atype: 3 + }, + + // Adtelligent Id + 'adtelligentId': { + source: 'adtelligent.com', + atype: 3 + }, + + amxId: { + source: 'amxdt.net', + atype: 1, + }, + + 'publinkId': { + source: 'epsilon.com', + atype: 3 + }, + + 'kpuid': { + source: 'kpuid.com', + atype: 3 + }, + + 'imppid': { + source: 'ppid.intimatemerger.com', + atype: 1 + }, + + 'imuid': { + source: 'intimatemerger.com', + atype: 1 + }, + + // Yahoo ConnectID + 'connectId': { + source: 'yahoo.com', + atype: 3 + }, + + // Adquery ID + 'qid': { + source: 'adquery.io', + atype: 1 + }, + + // DAC ID + 'dacId': { + source: 'impact-ad.jp', + atype: 1 + }, + + // 33across ID + '33acrossId': { + source: '33across.com', + atype: 1, + getValue: function(data) { + return data.envelope; + } + }, + + // tncId + 'tncid': { + source: 'thenewco.it', + atype: 3 + }, + + // Gravito MP ID + 'gravitompId': { + source: 'gravito.net', + atype: 1 + }, + + // czechAdId + 'czechAdId': { + source: 'czechadid.cz', + atype: 1 + }, + + // OneKey Data + 'oneKeyData': { + getValue: function(data) { + if (data && Array.isArray(data.identifiers) && data.identifiers[0]) { + return data.identifiers[0].value; + } + }, + source: 'paf', + atype: 1, + getEidExt: function(data) { + if (data && data.preferences) { + return {preferences: data.preferences}; + } + }, + getUidExt: function(data) { + if (data && Array.isArray(data.identifiers) && data.identifiers[0]) { + const id = data.identifiers[0]; + return { + version: id.version, + type: id.type, + source: id.source + }; + } + } + } +}; // this function will create an eid object for the given UserId sub-module function createEidObject(userIdData, subModuleKey) { - const conf = EID_CONFIG.get(subModuleKey); + const conf = USER_IDS_CONFIG[subModuleKey]; if (conf && userIdData) { let eid = {}; eid.source = isFn(conf['getSource']) ? conf['getSource'](userIdData) : conf['source']; @@ -32,23 +443,34 @@ function createEidObject(userIdData, subModuleKey) { return null; } +// this function will generate eids array for all available IDs in bidRequest.userId +// this function will be called by userId module +// if any adapter does not want any particular userId to be passed then adapter can use Array.filter(e => e.source != 'tdid') export function createEidsArray(bidRequestUserId) { - const allEids = {}; - function collect(eid) { - const key = JSON.stringify([eid.source?.toLowerCase(), eid.ext]); - if (allEids.hasOwnProperty(key)) { - allEids[key].uids.push(...eid.uids); - } else { - allEids[key] = eid; + let eids = []; + + for (const subModuleKey in bidRequestUserId) { + if (bidRequestUserId.hasOwnProperty(subModuleKey)) { + if (subModuleKey === 'pubProvidedId') { + eids = eids.concat(bidRequestUserId['pubProvidedId']); + } else if (Array.isArray(bidRequestUserId[subModuleKey])) { + bidRequestUserId[subModuleKey].forEach((config, index, arr) => { + const eid = createEidObject(config, subModuleKey); + + if (eid) { + eids.push(eid); + } + }) + } else { + const eid = createEidObject(bidRequestUserId[subModuleKey], subModuleKey); + if (eid) { + eids.push(eid); + } + } } } - Object.entries(bidRequestUserId).forEach(([name, values]) => { - values = Array.isArray(values) ? values : [values]; - const eids = name === 'pubProvidedId' ? deepClone(values) : values.map(value => createEidObject(value, name)); - eids.filter(eid => eid != null).forEach(collect); - }) - return Object.values(allEids); + return eids; } /** @@ -59,12 +481,11 @@ export function buildEidPermissions(submodules) { submodules.filter(i => isPlainObject(i.idObj) && Object.keys(i.idObj).length) .forEach(i => { Object.keys(i.idObj).forEach(key => { - const eidConf = EID_CONFIG.get(key) || {}; if (deepAccess(i, 'config.bidders') && Array.isArray(i.config.bidders) && - eidConf.source) { + deepAccess(USER_IDS_CONFIG, key + '.source')) { eidPermissions.push( { - source: eidConf.source, + source: USER_IDS_CONFIG[key].source, bidders: i.config.bidders } ); diff --git a/modules/userId/eids.md b/modules/userId/eids.md index c8d27cc9d52..90052cd6301 100644 --- a/modules/userId/eids.md +++ b/modules/userId/eids.md @@ -77,7 +77,6 @@ userIdAsEids = [ uids: [{ id: 'the-ids-object-stringified', atype: 1 - }] }, { @@ -111,76 +110,15 @@ userIdAsEids = [ source: 'bidswitch.net', uids: [{ id: 'some-random-id-value', - atype: 3, - ext: { - provider: 'liveintent.com' - } + atype: 3 }] }, - { - source: 'liveintent.indexexchange.com', - uids: [{ - id: 'some-random-id-value', - atype: 3, - ext: { - provider: 'liveintent.com' - } - }] - }, - - { - source: 'liveintent.sovrn.com'', - uids: [{ - id: 'some-random-id-value', - atype: 3, - ext: { - provider: 'liveintent.com' - } - }] - }, - - { - source: 'openx.net'', - uids: [{ - id: 'some-random-id-value', - atype: 3, - ext: { - provider: 'liveintent.com' - } - }] - }, - - { - source: 'pubmatic.com'', - uids: [{ - id: 'some-random-id-value', - atype: 3, - ext: { - provider: 'liveintent.com' - } - }] - }, - { source: 'media.net', uids: [{ id: 'some-random-id-value', - atype: 3, - ext: { - provider: 'liveintent.com' - } - }] - }, - - { - source: 'rubiconproject.com', - uids: [{ - id: 'some-random-id-value', - atype: 3, - ext: { - provider: 'liveintent.com' - } + atype: 3 }] }, @@ -317,20 +255,6 @@ userIdAsEids = [ id: 'some-random-id-value', atype: 3 }] - }, - { - source: 'euid.eu', - uids: [{ - id: 'some-random-id-value', - atype: 3 - }] - }, - { - source: 'mygaru.com', - uids: [{ - id: 'some-random-id-value', - atype: 1 - }] } ] ``` diff --git a/modules/userId/index.js b/modules/userId/index.js index 5a088b27319..91654981c22 100644 --- a/modules/userId/index.js +++ b/modules/userId/index.js @@ -133,7 +133,7 @@ import {getGlobal} from '../../src/prebidGlobal.js'; import adapterManager, {gdprDataHandler} from '../../src/adapterManager.js'; import CONSTANTS from '../../src/constants.json'; import {module, ready as hooksReady} from '../../src/hook.js'; -import {buildEidPermissions, createEidsArray, EID_CONFIG} from './eids.js'; +import {buildEidPermissions, createEidsArray, USER_IDS_CONFIG} from './eids.js'; import { getCoreStorageManager, getStorageManager, @@ -141,6 +141,7 @@ import { STORAGE_TYPE_LOCALSTORAGE } from '../../src/storageManager.js'; import { + cyrb53Hash, deepAccess, deepSetValue, delayExecution, @@ -161,7 +162,7 @@ import {defer, GreedyPromise} from '../../src/utils/promise.js'; import {registerOrtbProcessor, REQUEST} from '../../src/pbjsORTB.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 {GDPR_GVLIDS} from '../../src/consentHandler.js'; import {MODULE_TYPE_UID} from '../../src/activities/modules.js'; import {isActivityAllowed} from '../../src/activities/rules.js'; import {ACTIVITY_ENRICH_EIDS} from '../../src/activities/activities.js'; @@ -172,6 +173,10 @@ const COOKIE = STORAGE_TYPE_COOKIES; const LOCAL_STORAGE = STORAGE_TYPE_LOCALSTORAGE; const DEFAULT_SYNC_DELAY = 500; const NO_AUCTION_DELAY = 0; +const CONSENT_DATA_COOKIE_STORAGE_CONFIG = { + name: '_pbjs_userid_consent_data', + expires: 30 // 30 days expiration, which should match how often consent is refreshed by CMPs +}; export const PBJS_USER_ID_OPTOUT_NAME = '_pbjs_id_optout'; export const coreStorage = getCoreStorageManager('userId'); export const dep = { @@ -190,9 +195,6 @@ let initializedSubmodules; /** @type {SubmoduleConfig[]} */ let configRegistry = []; -/** @type {Object} */ -let idPriority = {}; - /** @type {Submodule[]} */ let submoduleRegistry = []; @@ -227,7 +229,6 @@ function submoduleMetrics(moduleName) { /** @param {Submodule[]} submodules */ export function setSubmoduleRegistry(submodules) { submoduleRegistry = submodules; - updateEIDConfig(submodules); } function cookieSetter(submodule, storageMgr) { @@ -256,13 +257,11 @@ export function setStoredValue(submodule, value) { if (storage.type === COOKIE) { const setCookie = cookieSetter(submodule); setCookie(null, valueStr, expiresStr); - setCookie('_cst', getConsentHash(), expiresStr); if (typeof storage.refreshInSeconds === 'number') { setCookie('_last', new Date().toUTCString(), expiresStr); } } else if (storage.type === LOCAL_STORAGE) { mgr.setDataInLocalStorage(`${storage.name}_exp`, expiresStr); - mgr.setDataInLocalStorage(`${storage.name}_cst`, getConsentHash()); mgr.setDataInLocalStorage(storage.name, encodeURIComponent(valueStr)); if (typeof storage.refreshInSeconds === 'number') { mgr.setDataInLocalStorage(`${storage.name}_last`, new Date().toUTCString()); @@ -280,11 +279,11 @@ export function deleteStoredValue(submodule) { const setCookie = cookieSetter(submodule, coreStorage); const expiry = (new Date(Date.now() - 1000 * 60 * 60 * 24)).toUTCString(); deleter = (suffix) => setCookie(suffix, '', expiry) - suffixes = ['', '_last', '_cst']; + suffixes = ['', '_last']; break; case LOCAL_STORAGE: deleter = (suffix) => coreStorage.removeDataFromLocalStorage(submodule.config.storage.name + suffix) - suffixes = ['', '_last', '_exp', '_cst']; + suffixes = ['', '_last', '_exp']; break; } if (deleter) { @@ -339,11 +338,75 @@ function getStoredValue(submodule, key = undefined) { return storedValue; } +/** + * makes an object that can be stored with only the keys we need to check. + * excluding the vendorConsents object since the consentString is enough to know + * if consent has changed without needing to have all the details in an object + * @param consentData + * @returns {{apiVersion: number, gdprApplies: boolean, consentString: string}} + */ +function makeStoredConsentDataHash(consentData) { + const storedConsentData = { + consentString: '', + gdprApplies: false, + apiVersion: 0 + }; + + if (consentData) { + storedConsentData.consentString = consentData.consentString; + storedConsentData.gdprApplies = consentData.gdprApplies; + storedConsentData.apiVersion = consentData.apiVersion; + } + + return cyrb53Hash(JSON.stringify(storedConsentData)); +} + +/** + * puts the current consent data into cookie storage + * @param consentData + */ +export function setStoredConsentData(consentData) { + try { + const expiresStr = (new Date(Date.now() + (CONSENT_DATA_COOKIE_STORAGE_CONFIG.expires * (60 * 60 * 24 * 1000)))).toUTCString(); + coreStorage.setCookie(CONSENT_DATA_COOKIE_STORAGE_CONFIG.name, makeStoredConsentDataHash(consentData), expiresStr, 'Lax'); + } catch (error) { + logError(error); + } +} + +/** + * get the stored consent data from local storage, if any + * @returns {string} + */ +function getStoredConsentData() { + try { + return coreStorage.getCookie(CONSENT_DATA_COOKIE_STORAGE_CONFIG.name); + } catch (e) { + logError(e); + } +} + +/** + * test if the consent object stored locally matches the current consent data. if they + * don't match or there is nothing stored locally, it means a refresh of the user id + * submodule is needed + * @param storedConsentData + * @param consentData + * @returns {boolean} + */ +function storedConsentDataMatchesConsentData(storedConsentData, consentData) { + return ( + typeof storedConsentData !== 'undefined' && + storedConsentData !== null && + storedConsentData === makeStoredConsentDataHash(consentData) + ); +} + /** * @param {SubmoduleContainer[]} submodules * @param {function} cb - callback for after processing is done. */ -function processSubmoduleCallbacks(submodules, cb, allModules) { +function processSubmoduleCallbacks(submodules, cb) { cb = uidMetrics().fork().startTiming('userId.callbacks.total').stopBefore(cb); const done = delayExecution(() => { clearTimeout(timeoutID); @@ -359,7 +422,7 @@ function processSubmoduleCallbacks(submodules, cb, allModules) { } // cache decoded value (this is copied to every adUnit bid) submodule.idObj = submodule.submodule.decode(idObj, submodule.config); - updatePPID(getCombinedSubmoduleIds(allModules)); + updatePPID(submodule.idObj); } else { logInfo(`${MODULE_NAME}: ${submodule.submodule.name} - request id responded with an empty value`); } @@ -384,7 +447,14 @@ function getCombinedSubmoduleIds(submodules) { if (!Array.isArray(submodules) || !submodules.length) { return {}; } - return getPrioritizedCombinedSubmoduleIds(submodules) + const combinedSubmoduleIds = submodules.filter(i => isPlainObject(i.idObj) && Object.keys(i.idObj).length).reduce((carry, i) => { + Object.keys(i.idObj).forEach(key => { + carry[key] = i.idObj[key]; + }); + return carry; + }, {}); + + return combinedSubmoduleIds; } /** @@ -396,14 +466,9 @@ function getSubmoduleId(submodules, sourceName) { if (!Array.isArray(submodules) || !submodules.length) { return {}; } - - const prioritisedIds = getPrioritizedCombinedSubmoduleIds(submodules); - const eligibleIdName = Object.keys(prioritisedIds).find(idName => { - const config = EID_CONFIG.get(idName); - return config?.source === sourceName || (isFn(config?.getSource) && config.getSource() === sourceName); - }); - - return eligibleIdName ? {[eligibleIdName]: prioritisedIds[eligibleIdName]} : []; + const submodule = submodules.filter(sub => isPlainObject(sub.idObj) && + Object.keys(sub.idObj).length && USER_IDS_CONFIG[Object.keys(sub.idObj)[0]]?.source === sourceName); + return !isEmpty(submodule) ? submodule[0].idObj : []; } /** @@ -415,39 +480,15 @@ function getCombinedSubmoduleIdsForBidder(submodules, bidder) { if (!Array.isArray(submodules) || !submodules.length || !bidder) { return {}; } - const eligibleSubmodules = submodules + return submodules .filter(i => !i.config.bidders || !isArray(i.config.bidders) || includes(i.config.bidders, bidder)) - - return getPrioritizedCombinedSubmoduleIds(eligibleSubmodules); -} - -function collectByPriority(submodules, getIds, getName) { - return Object.fromEntries(Object.entries(submodules.reduce((carry, submod) => { - const ids = getIds(submod); - ids && Object.keys(ids).forEach(key => { - const maybeCurrentIdPriority = idPriority[key]?.indexOf(getName(submod)); - const currentIdPriority = isNumber(maybeCurrentIdPriority) ? maybeCurrentIdPriority : -1; - const currentIdState = {priority: currentIdPriority, value: ids[key]}; - if (carry[key]) { - const winnerIdState = currentIdState.priority > carry[key].priority ? currentIdState : carry[key]; - carry[key] = winnerIdState; - } else { - carry[key] = currentIdState; - } - }); - return carry; - }, {})).map(([k, v]) => [k, v.value])); -} - -/** - * @param {SubmoduleContainer[]} submodules - */ -function getPrioritizedCombinedSubmoduleIds(submodules) { - return collectByPriority( - submodules.filter(i => isPlainObject(i.idObj) && Object.keys(i.idObj).length), - (submod) => submod.idObj, - (submod) => submod.submodule.name - ) + .filter(i => isPlainObject(i.idObj) && Object.keys(i.idObj).length) + .reduce((carry, i) => { + Object.keys(i.idObj).forEach(key => { + carry[key] = i.idObj[key]; + }); + return carry; + }, {}); } /** @@ -506,21 +547,21 @@ function idSystemInitializer({delay = GreedyPromise.timeout} = {}) { } } - function timeConsent() { - return allConsent.promise.finally(initMetrics.startTiming('userId.init.consent')) + function timeGdpr() { + return gdprDataHandler.promise.finally(initMetrics.startTiming('userId.init.gdpr')); } let done = cancelAndTry( GreedyPromise.all([hooksReady, startInit.promise]) - .then(timeConsent) - .then(checkRefs(() => { - initSubmodules(initModules, allModules); + .then(timeGdpr) + .then(checkRefs((consentData) => { + initSubmodules(initModules, allModules, consentData); })) .then(() => startCallbacks.promise.finally(initMetrics.startTiming('userId.callbacks.pending'))) .then(checkRefs(() => { const modWithCb = initModules.filter(item => isFn(item.callback)); if (modWithCb.length) { - return new GreedyPromise((resolve) => processSubmoduleCallbacks(modWithCb, resolve, initModules)); + return new GreedyPromise((resolve) => processSubmoduleCallbacks(modWithCb, resolve)); } })) ); @@ -548,17 +589,18 @@ function idSystemInitializer({delay = GreedyPromise.timeout} = {}) { done = cancelAndTry( done .catch(() => null) - .then(timeConsent) // fetch again in case a refresh was forced before this was resolved - .then(checkRefs(() => { + .then(timeGdpr) // fetch again in case a refresh was forced before this was resolved + .then(checkRefs((consentData) => { const cbModules = initSubmodules( initModules, allModules.filter((sm) => submoduleNames == null || submoduleNames.includes(sm.submodule.name)), + consentData, true ).filter((sm) => { return sm.callback != null; }); if (cbModules.length) { - return new GreedyPromise((resolve) => processSubmoduleCallbacks(cbModules, resolve, initModules)); + return new GreedyPromise((resolve) => processSubmoduleCallbacks(cbModules, resolve)); } })) ); @@ -668,20 +710,20 @@ function encryptSignals(signals, version = 1) { } /** - * This function will be exposed in the global-name-space so that publisher can register the signals-ESP. - */ +* This function will be exposed in the global-name-space so that publisher can register the signals-ESP. +*/ function registerSignalSources() { if (!isGptPubadsDefined()) { return; } - window.googletag.secureSignalProviders = window.googletag.secureSignalProviders || []; + window.googletag.encryptedSignalProviders = window.googletag.encryptedSignalProviders || []; const encryptedSignalSources = config.getConfig('userSync.encryptedSignalSources'); if (encryptedSignalSources) { const registerDelay = encryptedSignalSources.registerDelay || 0; setTimeout(() => { encryptedSignalSources['sources'] && encryptedSignalSources['sources'].forEach(({ source, encrypt, customFunc }) => { source.forEach((src) => { - window.googletag.secureSignalProviders.push({ + window.googletag.encryptedSignalProviders.push({ id: src, collectorFunction: () => getEncryptedEidsForSource(src, encrypt, customFunc) }); @@ -741,27 +783,7 @@ function getUserIdsAsync() { ); } -export function getConsentHash() { - // transform decimal string into base64 to save some space on cookies - let hash = Number(allConsent.hash); - const bytes = []; - while (hash > 0) { - bytes.push(String.fromCharCode(hash & 255)); - hash = hash >>> 8; - } - return btoa(bytes.join()); -} - -function consentChanged(submodule) { - const storedConsent = getStoredValue(submodule, 'cst'); - return !storedConsent || storedConsent !== getConsentHash(); -} - -function populateSubmoduleId(submodule, forceRefresh, allSubmodules) { - // TODO: the ID submodule API only takes GDPR consent; it should be updated now that GDPR - // is only a tiny fraction of a vast consent universe - const gdprConsent = gdprDataHandler.getConsentData(); - +function populateSubmoduleId(submodule, consentData, storedConsentData, forceRefresh) { // There are two submodule configuration types to handle: storage or value // 1. storage: retrieve user id data from cookie/html storage or with the submodule's getId method // 2. value: pass directly to bids @@ -775,12 +797,12 @@ function populateSubmoduleId(submodule, forceRefresh, allSubmodules) { refreshNeeded = storedDate && (Date.now() - storedDate.getTime() > submodule.config.storage.refreshInSeconds * 1000); } - if (!storedId || refreshNeeded || forceRefresh || consentChanged(submodule)) { + if (!storedId || refreshNeeded || forceRefresh || !storedConsentDataMatchesConsentData(storedConsentData, consentData)) { // No id previously saved, or a refresh is needed, or consent has changed. Request a new id from the submodule. - response = submodule.submodule.getId(submodule.config, gdprConsent, storedId); + response = submodule.submodule.getId(submodule.config, consentData, storedId); } else if (typeof submodule.submodule.extendId === 'function') { // If the id exists already, give submodule a chance to decide additional actions that need to be taken - response = submodule.submodule.extendId(submodule.config, gdprConsent, storedId); + response = submodule.submodule.extendId(submodule.config, consentData, storedId); } if (isPlainObject(response)) { @@ -804,13 +826,13 @@ function populateSubmoduleId(submodule, forceRefresh, allSubmodules) { // cache decoded value (this is copied to every adUnit bid) submodule.idObj = submodule.config.value; } else { - const response = submodule.submodule.getId(submodule.config, gdprConsent, undefined); + const response = submodule.submodule.getId(submodule.config, consentData, undefined); if (isPlainObject(response)) { if (typeof response.callback === 'function') { submodule.callback = response.callback; } if (response.id) { submodule.idObj = submodule.submodule.decode(response.id, submodule.config); } } } - updatePPID(getCombinedSubmoduleIds(allSubmodules)); + updatePPID(submodule.idObj); } function updatePPID(userIds = getUserIds()) { @@ -830,7 +852,7 @@ function updatePPID(userIds = getUserIds()) { } } -function initSubmodules(dest, submodules, forceRefresh = false) { +function initSubmodules(dest, submodules, consentData, forceRefresh = false) { return uidMetrics().fork().measureTime('userId.init.modules', function () { if (!submodules.length) return []; // to simplify log messages from here on @@ -850,10 +872,14 @@ function initSubmodules(dest, submodules, forceRefresh = false) { return []; } + // we always want the latest consentData stored, even if we don't execute any submodules + const storedConsentData = getStoredConsentData(); + setStoredConsentData(consentData); + const initialized = submodules.reduce((carry, submodule) => { return submoduleMetrics(submodule.submodule.name).measureTime('init', () => { try { - populateSubmoduleId(submodule, forceRefresh, submodules); + populateSubmoduleId(submodule, consentData, storedConsentData, forceRefresh); carry.push(submodule); } catch (e) { logError(`Error in userID module '${submodule.submodule.name}':`, e); @@ -886,12 +912,14 @@ function updateInitializedSubmodules(dest, submodule) { /** * list of submodule configurations with valid 'storage' or 'value' obj definitions - * storage config: contains values for storing/retrieving User ID data in browser storage - * value config: object properties that are copied to bids (without saving to storage) + * * storage config: contains values for storing/retrieving User ID data in browser storage + * * value config: object properties that are copied to bids (without saving to storage) * @param {SubmoduleConfig[]} configRegistry + * @param {Submodule[]} submoduleRegistry + * @param {string[]} activeStorageTypes * @returns {SubmoduleConfig[]} */ -function getValidSubmoduleConfigs(configRegistry) { +function getValidSubmoduleConfigs(configRegistry, submoduleRegistry) { if (!Array.isArray(configRegistry)) { return []; } @@ -942,21 +970,11 @@ function canUseStorage(submodule) { return false; } -function updateEIDConfig(submodules) { - EID_CONFIG.clear(); - Object.entries(collectByPriority( - submodules, - (mod) => mod.eids, - (mod) => mod.name - )).forEach(([id, conf]) => EID_CONFIG.set(id, conf)); -} - /** * update submodules by validating against existing configs and storage types */ function updateSubmodules() { - updateEIDConfig(submoduleRegistry); - const configs = getValidSubmoduleConfigs(configRegistry); + const configs = getValidSubmoduleConfigs(configRegistry, submoduleRegistry); if (!configs.length) { return; } @@ -992,25 +1010,6 @@ function updateSubmodules() { } } -/** - * This function will update the idPriority according to the provided configuration - * @param {Object} idPriorityConfig - * @param {SubmoduleContainer[]} submodules - */ -function updateIdPriority(idPriorityConfig, submodules) { - if (idPriorityConfig) { - const result = {}; - const aliasToName = new Map(submodules.map(s => s.submodule.aliasName ? [s.submodule.aliasName, s.submodule.name] : [])); - Object.keys(idPriorityConfig).forEach(key => { - const priority = isArray(idPriorityConfig[key]) ? [...idPriorityConfig[key]].reverse() : [] - result[key] = priority.map(s => aliasToName.has(s) ? aliasToName.get(s) : s); - }); - idPriority = result; - } else { - idPriority = {}; - } -} - export function requestDataDeletion(next, ...args) { logInfo('UserID: received data deletion request; deleting all stored IDs...') submodules.forEach(submodule => { @@ -1070,16 +1069,13 @@ export function init(config, {delay = GreedyPromise.timeout} = {}) { configListener = config.getConfig('userSync', conf => { // Note: support for 'usersync' was dropped as part of Prebid.js 4.0 const userSync = conf.userSync; - if (userSync) { - ppidSource = userSync.ppid; - if (userSync.userIds) { - configRegistry = userSync.userIds; - syncDelay = isNumber(userSync.syncDelay) ? userSync.syncDelay : DEFAULT_SYNC_DELAY; - auctionDelay = isNumber(userSync.auctionDelay) ? userSync.auctionDelay : NO_AUCTION_DELAY; - updateSubmodules(); - updateIdPriority(userSync.idPriority, submodules); - initIdSystem({ready: true}); - } + ppidSource = userSync.ppid; + if (userSync && userSync.userIds) { + configRegistry = userSync.userIds; + syncDelay = isNumber(userSync.syncDelay) ? userSync.syncDelay : DEFAULT_SYNC_DELAY; + auctionDelay = isNumber(userSync.auctionDelay) ? userSync.auctionDelay : NO_AUCTION_DELAY; + updateSubmodules(); + initIdSystem({ready: true}); } }); diff --git a/modules/userId/userId.md b/modules/userId/userId.md index 7a01e128814..15f609ddced 100644 --- a/modules/userId/userId.md +++ b/modules/userId/userId.md @@ -5,9 +5,6 @@ Example showing `cookie` storage for user id data for each of the submodules ``` pbjs.setConfig({ userSync: { - idPriority: { - uid2: ['uid2', 'liveIntentId'] - } userIds: [{ name: "33acrossId", storage: { @@ -158,9 +155,6 @@ pbjs.setConfig({ }, { name: "gravitompId" - }, - { - name: "mygaruId" } ], syncDelay: 5000, diff --git a/modules/utiqSystem.js b/modules/utiqSystem.js index 473dc5854a9..441bba5660f 100644 --- a/modules/utiqSystem.js +++ b/modules/utiqSystem.js @@ -124,15 +124,6 @@ export const utiqSubmodule = { return { callback: result }; } }, - eids: { - 'utiq': { - source: 'utiq.com', - atype: 1, - getValue: function (data) { - return data; - }, - }, - } }; submodule('userId', utiqSubmodule); diff --git a/modules/vdoaiBidAdapter.js b/modules/vdoaiBidAdapter.js index ada843a6e45..fa1e2898a4a 100644 --- a/modules/vdoaiBidAdapter.js +++ b/modules/vdoaiBidAdapter.js @@ -1,12 +1,6 @@ +import {getAdUnitSizes} from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {BANNER, VIDEO} from '../src/mediaTypes.js'; -import {getAdUnitSizes} from '../libraries/sizeUtils/sizeUtils.js'; - -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse - */ const BIDDER_CODE = 'vdoai'; const ENDPOINT_URL = 'https://prebid.vdo.ai/auction'; diff --git a/modules/ventesBidAdapter.js b/modules/ventesBidAdapter.js index 78c580c4116..73ae0a7e5f1 100644 --- a/modules/ventesBidAdapter.js +++ b/modules/ventesBidAdapter.js @@ -1,9 +1,8 @@ import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js'; -import {isArray, isNumber, isPlainObject, isStr, replaceAuctionPrice} from '../src/utils.js'; +import {convertCamelToUnderscore, isArray, isNumber, isPlainObject, isStr, replaceAuctionPrice} from '../src/utils.js'; import {find} from '../src/polyfill.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; -import {convertCamelToUnderscore} from '../libraries/appnexusUtils/anUtils.js'; const BID_METHOD = 'POST'; const BIDDER_URL = 'https://ad.ventesavenues.in/va/ad'; diff --git a/modules/verizonMediaIdSystem.js b/modules/verizonMediaIdSystem.js index 26fa89cfe03..27577ad0de4 100644 --- a/modules/verizonMediaIdSystem.js +++ b/modules/verizonMediaIdSystem.js @@ -10,13 +10,6 @@ import {submodule} from '../src/hook.js'; import {formatQS, logError} from '../src/utils.js'; import {includes} from '../src/polyfill.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 = 'verizonMediaId'; const VENDOR_ID = 25; const PLACEHOLDER = '__PIXEL_ID__'; @@ -106,12 +99,6 @@ export const verizonMediaIdSubmodule = { */ getAjaxFn() { return ajax; - }, - eids: { - 'connectid': { - source: 'verizonmedia.com', - atype: 3 - }, } }; diff --git a/modules/viantOrtbBidAdapter.js b/modules/viantOrtbBidAdapter.js deleted file mode 100644 index 0f7953a192a..00000000000 --- a/modules/viantOrtbBidAdapter.js +++ /dev/null @@ -1,113 +0,0 @@ -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'; - -const BIDDER_CODE = 'viant'; -const ENDPOINT = 'https://bidders-us-east-1.adelphic.net/d/rtb/v25/prebid/bidder' - -const DEFAULT_BID_TTL = 300; -const DEFAULT_CURRENCY = 'USD'; -const DEFAULT_NET_REVENUE = true; - -export const spec = { - code: BIDDER_CODE, - supportedMediaTypes: [BANNER, VIDEO, NATIVE], - - isBidRequestValid: function (bid) { - if (bid && typeof bid.params !== 'object') { - logError(BIDDER_CODE + ': params is not defined or is incorrect in the bidder settings.'); - return false; - } - if (!getBidIdParameter('publisherId', bid.params)) { - logError(BIDDER_CODE + ': publisherId is not present in bidder params.'); - return false; - } - const mediaTypesBanner = deepAccess(bid, 'mediaTypes.banner'); - const mediaTypesVideo = deepAccess(bid, 'mediaTypes.video'); - const mediaTypesNative = deepAccess(bid, 'mediaTypes.native'); - if (!mediaTypesBanner && !mediaTypesVideo && !mediaTypesNative) { - utils.logWarn(BIDDER_CODE + ': one of mediaTypes.banner or mediaTypes.video or mediaTypes.native must be passed'); - return false; - } - return true; - }, - - buildRequests, - - interpretResponse(response, request) { - if (!response.body) { - response.body = {nbr: 0}; - } - const bids = converter.fromORTB({request: request.data, response: response.body}).bids; - return bids; - }, - - /** - * Register bidder specific code, which will execute if a bid from this bidder won the auction - * @param {Bid} bid The bid that won the auction - */ - onBidWon: function (bid) { - if (bid.burl) { - utils.triggerPixel(bid.burl); - } else if (bid.nurl) { - utils.triggerPixel(bid.nurl); - } - } -} - -function buildRequests(bids, bidderRequest) { - let videoBids = bids.filter(bid => isVideoBid(bid)); - let bannerBids = bids.filter(bid => isBannerBid(bid)); - let nativeBids = bids.filter(bid => isNativeBid(bid)); - let requests = bannerBids.length ? [createRequest(bannerBids, bidderRequest, BANNER)] : []; - videoBids.forEach(bid => { - requests.push(createRequest([bid], bidderRequest, VIDEO)); - }); - nativeBids.forEach(bid => { - requests.push(createRequest([bid], bidderRequest, NATIVE)); - }); - return requests; -} - -function createRequest(bidRequests, bidderRequest, 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 = {}; - data.regs.ext.gdpr = bidderRequest.gdprConsent.gdprApplies ? 1 : 0; - } - if (bidderRequest.uspConsent) { - if (!data.regs) data.regs = {}; - if (!data.regs.ext) data.regs.ext = {}; - data.regs.ext.us_privacy = bidderRequest.uspConsent; - } - return { - method: 'POST', - url: ENDPOINT, - data: data - } -} - -function isVideoBid(bid) { - return deepAccess(bid, 'mediaTypes.video'); -} - -function isBannerBid(bid) { - return deepAccess(bid, 'mediaTypes.banner'); -} - -function isNativeBid(bid) { - return deepAccess(bid, 'mediaTypes.native'); -} - -const converter = ortbConverter({ - context: { - netRevenue: DEFAULT_NET_REVENUE, - ttl: DEFAULT_BID_TTL, - currency: DEFAULT_CURRENCY - } -}); - -registerBidder(spec); diff --git a/modules/viantOrtbBidAdapter.md b/modules/viantOrtbBidAdapter.md deleted file mode 100644 index def93722b7b..00000000000 --- a/modules/viantOrtbBidAdapter.md +++ /dev/null @@ -1,54 +0,0 @@ -# Overview - -Module Name: VIANT Bidder Adapter -Module Type: Bidder Adapter -Maintainer: Marketplace@adelphic.com - -# Description - -An adapter to get a bid from VIANT DSP. - -# Test Parameters -```javascript - var adUnits = [ // Banner adUnit with only required parameters - { - code: 'test-div-minimal', - mediaTypes: { - banner: { - sizes: [[728, 90]] - } - }, - bids: [ - { - bidder: 'viant', - params: { - supplySourceId: 'supplier', - publisherId: '464' - } - } - ] - }, - { - code: 'test-div-minimal-video', - mediaTypes: { - video: { - playerSize: [640, 480], - context: 'outstream' - } - }, - bids: [ - { - bidder: 'viant', - params: { - supplySourceId: 'supplier', - publisherId: '464' // required - } - } - ] - } - ]; -``` - -Where: - -* placementId - Placement ID of the ad unit (required) diff --git a/modules/vibrantmediaBidAdapter.js b/modules/vibrantmediaBidAdapter.js index 8809aae32bd..7368967ff3f 100644 --- a/modules/vibrantmediaBidAdapter.js +++ b/modules/vibrantmediaBidAdapter.js @@ -12,13 +12,6 @@ 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 - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse - * @typedef {import('../src/adapters/bidderFactory.js').BidderSpec} BidderSpec - */ - const BIDDER_CODE = 'vibrantmedia'; const VIBRANT_MEDIA_PREBID_URL = 'https://prebid.intellitxt.com/prebid'; const VALID_PIXEL_URL_REGEX = /^https?:\/\/[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)+([/?].*)?$/; diff --git a/modules/vidazooBidAdapter.js b/modules/vidazooBidAdapter.js index b5323181c6c..8341eb332d2 100644 --- a/modules/vidazooBidAdapter.js +++ b/modules/vidazooBidAdapter.js @@ -1,10 +1,9 @@ -import {_each, deepAccess, isFn, parseSizesInput, parseUrl, uniques, isArray} from '../src/utils.js'; +import {_each, deepAccess, parseSizesInput, parseUrl, uniques, isFn} from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {BANNER, VIDEO} from '../src/mediaTypes.js'; import {getStorageManager} from '../src/storageManager.js'; import {bidderSettings} from '../src/bidderSettings.js'; import {config} from '../src/config.js'; -import {chunk} from '../libraries/chunk/chunk.js'; const GVLID = 744; const DEFAULT_SUB_DOMAIN = 'prebid'; @@ -49,7 +48,7 @@ function isBidRequestValid(bid) { return !!(extractCID(params) && extractPID(params)); } -function buildRequestData(bid, topWindowUrl, sizes, bidderRequest, bidderTimeout) { +function buildRequest(bid, topWindowUrl, sizes, bidderRequest, bidderTimeout) { const { params, bidId, @@ -57,6 +56,7 @@ function buildRequestData(bid, topWindowUrl, sizes, bidderRequest, bidderTimeout adUnitCode, schain, mediaTypes, + auctionId, ortb2Imp, bidderRequestId, bidRequestsCount, @@ -69,7 +69,9 @@ function buildRequestData(bid, topWindowUrl, sizes, bidderRequest, bidderTimeout const dealId = getNextDealId(hashUrl); const uniqueDealId = getUniqueDealId(hashUrl); const sId = getVidazooSessionId(); + const cId = extractCID(params); const pId = extractPID(params); + const subDomain = extractSubDomain(params); const ptrace = getCacheOpt(); const isStorageAllowed = bidderSettings.get(BIDDER_CODE, 'storageAllowed'); @@ -112,6 +114,8 @@ function buildRequestData(bid, topWindowUrl, sizes, bidderRequest, bidderTimeout gpid: gpid, cat: cat, pagecat: pagecat, + // TODO: fix auctionId leak: https://github.com/prebid/Prebid.js/issues/9781 + auctionId: auctionId, transactionId: ortb2Imp?.ext?.tid, bidderRequestId: bidderRequestId, bidRequestsCount: bidRequestsCount, @@ -149,46 +153,17 @@ function buildRequestData(bid, topWindowUrl, sizes, bidderRequest, bidderTimeout data.gppSid = bidderRequest.ortb2.regs.gpp_sid; } - _each(ext, (value, key) => { - data['ext.' + key] = value; - }); - - return data; -} - -function buildRequest(bid, topWindowUrl, sizes, bidderRequest, bidderTimeout) { - const {params} = bid; - const cId = extractCID(params); - const subDomain = extractSubDomain(params); - const data = buildRequestData(bid, topWindowUrl, sizes, bidderRequest, bidderTimeout); const dto = { method: 'POST', url: `${createDomain(subDomain)}/prebid/multi/${cId}`, data: data }; - return dto; -} -function buildSingleRequest(bidRequests, bidderRequest, topWindowUrl, bidderTimeout) { - const {params} = bidRequests[0]; - const cId = extractCID(params); - const subDomain = extractSubDomain(params); - const data = bidRequests.map(bid => { - const sizes = parseSizesInput(bid.sizes); - return buildRequestData(bid, topWindowUrl, sizes, bidderRequest, bidderTimeout) - }); - const chunkSize = Math.min(20, config.getConfig('vidazoo.chunkSize') || 10); - - const chunkedData = chunk(data, chunkSize); - return chunkedData.map(chunk => { - return { - method: 'POST', - url: `${createDomain(subDomain)}/prebid/multi/${cId}`, - data: { - bids: chunk - } - }; + _each(ext, (value, key) => { + dto.data['ext.' + key] = value; }); + + return dto; } function appendUserIdsToRequestPayload(payloadRef, userIds) { @@ -218,34 +193,12 @@ function buildRequests(validBidRequests, bidderRequest) { // TODO: does the fallback make sense here? const topWindowUrl = bidderRequest.refererInfo.page || bidderRequest.refererInfo.topmostLocation; const bidderTimeout = config.getConfig('bidderTimeout'); - - const singleRequestMode = config.getConfig('vidazoo.singleRequest'); - const requests = []; - - if (singleRequestMode) { - // banner bids are sent as a single request - const bannerBidRequests = validBidRequests.filter(bid => isArray(bid.mediaTypes) ? bid.mediaTypes.includes(BANNER) : bid.mediaTypes[BANNER] !== undefined); - if (bannerBidRequests.length > 0) { - const singleRequests = buildSingleRequest(bannerBidRequests, bidderRequest, topWindowUrl, bidderTimeout); - requests.push(...singleRequests); - } - - // video bids are sent as a single request for each bid - - const videoBidRequests = validBidRequests.filter(bid => bid.mediaTypes[VIDEO] !== undefined); - videoBidRequests.forEach(validBidRequest => { - const sizes = parseSizesInput(validBidRequest.sizes); - const request = buildRequest(validBidRequest, topWindowUrl, sizes, bidderRequest, bidderTimeout); - requests.push(request); - }); - } else { - validBidRequests.forEach(validBidRequest => { - const sizes = parseSizesInput(validBidRequest.sizes); - const request = buildRequest(validBidRequest, topWindowUrl, sizes, bidderRequest, bidderTimeout); - requests.push(request); - }); - } + validBidRequests.forEach(validBidRequest => { + const sizes = parseSizesInput(validBidRequest.sizes); + const request = buildRequest(validBidRequest, topWindowUrl, sizes, bidderRequest, bidderTimeout); + requests.push(request); + }); return requests; } @@ -253,15 +206,13 @@ function interpretResponse(serverResponse, request) { if (!serverResponse || !serverResponse.body) { return []; } - - const singleRequestMode = config.getConfig('vidazoo.singleRequest'); - const reqBidId = deepAccess(request, 'data.bidId'); + const {bidId} = request.data; const {results} = serverResponse.body; let output = []; try { - results.forEach((result, i) => { + results.forEach(result => { const { creativeId, ad, @@ -270,7 +221,6 @@ function interpretResponse(serverResponse, request) { width, height, currency, - bidId, advertiserDomains, metaData, mediaType = BANNER @@ -280,7 +230,7 @@ function interpretResponse(serverResponse, request) { } const response = { - requestId: (singleRequestMode && bidId) ? bidId : reqBidId, + requestId: bidId, cpm: price, width: width, height: height, @@ -314,27 +264,19 @@ function interpretResponse(serverResponse, request) { } output.push(response); }); - return output; } catch (e) { return []; } } -function getUserSyncs(syncOptions, responses, gdprConsent = {}, uspConsent = '', gppConsent = {}) { +function getUserSyncs(syncOptions, responses, gdprConsent = {}, uspConsent = '') { let syncs = []; const {iframeEnabled, pixelEnabled} = syncOptions; const {gdprApplies, consentString = ''} = gdprConsent; - const {gppString, applicableSections} = gppConsent; const cidArr = responses.filter(resp => deepAccess(resp, 'body.cid')).map(resp => resp.body.cid).filter(uniques); - let params = `?cid=${encodeURIComponent(cidArr.join(','))}&gdpr=${gdprApplies ? 1 : 0}&gdpr_consent=${encodeURIComponent(consentString || '')}&us_privacy=${encodeURIComponent(uspConsent || '')}`; - - if (gppString && applicableSections?.length) { - params += '&gpp=' + encodeURIComponent(gppString); - params += '&gpp_sid=' + encodeURIComponent(applicableSections.join(',')); - } - + const params = `?cid=${encodeURIComponent(cidArr.join(','))}&gdpr=${gdprApplies ? 1 : 0}&gdpr_consent=${encodeURIComponent(consentString || '')}&us_privacy=${encodeURIComponent(uspConsent || '')}` if (iframeEnabled) { syncs.push({ type: 'iframe', diff --git a/modules/videobyteBidAdapter.js b/modules/videobyteBidAdapter.js index 8cedf9ac16a..c4dae78e862 100644 --- a/modules/videobyteBidAdapter.js +++ b/modules/videobyteBidAdapter.js @@ -2,14 +2,6 @@ import { logMessage, logError, deepAccess, isFn, isPlainObject, isStr, isNumber, import {registerBidder} from '../src/adapters/bidderFactory.js'; import {VIDEO} from '../src/mediaTypes.js'; -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse - * @typedef {import('../src/adapters/bidderFactory.js').SyncOptions} SyncOptions - * @typedef {import('../src/adapters/bidderFactory.js').UserSync} UserSync - */ - const BIDDER_CODE = 'videobyte'; const DEFAULT_BID_TTL = 300; const DEFAULT_CURRENCY = 'USD'; diff --git a/modules/videoheroesBidAdapter.js b/modules/videoheroesBidAdapter.js index ee2c2deef8b..2f02734a31f 100644 --- a/modules/videoheroesBidAdapter.js +++ b/modules/videoheroesBidAdapter.js @@ -4,11 +4,6 @@ import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; import { config } from '../src/config.js'; import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - */ - const BIDDER_CODE = 'videoheroes'; const DEFAULT_CUR = 'USD'; const ENDPOINT_URL = `https://point.contextualadv.com/?t=2&partner=hash`; diff --git a/modules/videojsVideoProvider.js b/modules/videojsVideoProvider.js index 7764e8af995..23a23447faa 100644 --- a/modules/videojsVideoProvider.js +++ b/modules/videojsVideoProvider.js @@ -201,15 +201,8 @@ export function VideojsProvider(providerConfig, vjs_, adState_, timeState_, call return; } - // The VideoJS IMA plugin version 1.11.0 will throw when the ad is empty. - try { - player.ima.changeAdTag(adTagUrl); - player.ima.requestAds(); - } catch (e) { - /* - Handling is not required; ad errors are emitted automatically by video.js - */ - } + player.ima.changeAdTag(adTagUrl); + player.ima.requestAds(); } function onEvent(type, callback, payload) { diff --git a/modules/videonowBidAdapter.js b/modules/videonowBidAdapter.js index 563f692693a..bfbc07fdff1 100644 --- a/modules/videonowBidAdapter.js +++ b/modules/videonowBidAdapter.js @@ -2,12 +2,6 @@ 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 - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse - */ - const BIDDER_CODE = 'videonow'; const RTB_URL = 'https://adx.videonow.ru/yhb' const DEFAULT_CURRENCY = 'RUB' diff --git a/modules/videoreachBidAdapter.js b/modules/videoreachBidAdapter.js index 8835398d7cc..9fd5853c75e 100644 --- a/modules/videoreachBidAdapter.js +++ b/modules/videoreachBidAdapter.js @@ -1,4 +1,4 @@ -import {getBidIdParameter, getValue} from '../src/utils.js'; +import { getValue, getBidIdParameter } 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/viqeoBidAdapter.js b/modules/viqeoBidAdapter.js index 28f4de1fd52..5762a794c8e 100644 --- a/modules/viqeoBidAdapter.js +++ b/modules/viqeoBidAdapter.js @@ -3,14 +3,6 @@ import {logError, logInfo, _each, mergeDeep, isFn, isNumber, isPlainObject} from import {VIDEO} from '../src/mediaTypes.js'; import {Renderer} from '../src/Renderer.js'; -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse - * @typedef {import('../src/adapters/bidderFactory.js').ServerRequest} ServerRequest - * @typedef {import('../src/adapters/bidderFactory.js').BidderSpec} BidderSpec - */ - const BIDDER_CODE = 'viqeo'; const DEFAULT_MIMES = ['application/javascript']; const VIQEO_ENDPOINT = 'https://ads.betweendigital.com/openrtb_bid'; diff --git a/modules/visxBidAdapter.js b/modules/visxBidAdapter.js index a86c958392e..a066d93d69e 100644 --- a/modules/visxBidAdapter.js +++ b/modules/visxBidAdapter.js @@ -1,11 +1,8 @@ -import {deepAccess, logError, parseSizesInput, 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 { triggerPixel, parseSizesInput, deepAccess, logError, getGptSlotInfoForAdUnitCode } 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'; const BIDDER_CODE = 'visx'; const GVLID = 154; const BASE_URL = 'https://t.visx.net'; @@ -15,7 +12,6 @@ const TIME_TO_LIVE = 360; const DEFAULT_CUR = 'EUR'; const ADAPTER_SYNC_PATH = '/push_sync'; const TRACK_TIMEOUT_PATH = '/track/bid_timeout'; -const RUNTIME_STATUS_RESPONSE_TIME = 999000; const LOG_ERROR_MESS = { noAuid: 'Bid from response has no auid parameter - ', noAdm: 'Bid from response has no adm parameter - ', @@ -33,8 +29,6 @@ const LOG_ERROR_MESS = { videoMissing: 'Bid request videoType property is missing - ' }; const currencyWhiteList = ['EUR', 'USD', 'GBP', 'PLN']; -export const storage = getStorageManager({bidderCode: BIDDER_CODE}); -const _bidResponseTimeLogged = []; export const spec = { code: BIDDER_CODE, gvlid: GVLID, @@ -123,13 +117,10 @@ export const spec = { ...(payloadSchain && { schain: payloadSchain }) } }; - - const vads = _getUserId(); const user = { ext: { ...(payloadUserEids && { eids: payloadUserEids }), - ...(payload.gdpr_consent && { consent: payload.gdpr_consent }), - ...(vads && { vads }) + ...(payload.gdpr_consent && { consent: payload.gdpr_consent }) } }; const regs = ('gdpr_applies' in payload) && { @@ -210,12 +201,6 @@ export const spec = { if (bid.ext && bid.ext.events && bid.ext.events.win) { triggerPixel(bid.ext.events.win); } - // Call 'track/runtime' with the corresponding bid.requestId - only once per auction - if (bid.ext && bid.ext.events && bid.ext.events.runtime && !_bidResponseTimeLogged.includes(bid.auctionId)) { - _bidResponseTimeLogged.push(bid.auctionId); - const _roundedTime = _roundResponseTime(bid.timeToRespond, 50); - triggerPixel(bid.ext.events.runtime.replace('{STATUS_CODE}', RUNTIME_STATUS_RESPONSE_TIME + _roundedTime)); - } }, onTimeout: function(timeoutData) { // Call '/track/bid_timeout' with timeout data @@ -329,10 +314,6 @@ function _addBidResponse(serverBid, bidsMap, currency, bidResponses) { if (serverBid.ext && serverBid.ext.prebid) { bidResponse.ext = serverBid.ext.prebid; - if (serverBid.ext.visx && serverBid.ext.visx.events) { - const prebidExtEvents = bidResponse.ext.events || {}; - bidResponse.ext.events = Object.assign(prebidExtEvents, serverBid.ext.visx.events); - } } const visxTargeting = deepAccess(serverBid, 'ext.prebid.targeting'); @@ -401,60 +382,4 @@ function _isAdSlotExists(adUnitCode) { return false; } -// Generate user id (25 chars) with NanoID -// https://github.com/ai/nanoid/ -function _generateUserId() { - for ( - var t = 'useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict', - e = new Date().getTime() % 1073741824, - i = '', - o = 0; - o < 5; - o++ - ) { - i += t[e % 64]; - e = Math.floor(e / 64); - } - for (o = 20; o--;) i += t[(64 * Math.random()) | 0]; - return i; -} - -function _getUserId() { - const USER_ID_KEY = '__vads'; - let vads; - - if (storage.cookiesAreEnabled()) { - vads = storage.getCookie(USER_ID_KEY); - } else if (storage.localStorageIsEnabled()) { - vads = storage.getDataFromLocalStorage(USER_ID_KEY); - } - - if (vads && vads.length) { - return vads; - } - - vads = _generateUserId(); - if (storage.cookiesAreEnabled()) { - const expires = new Date(Date.now() + 2592e6).toUTCString(); - storage.setCookie(USER_ID_KEY, vads, expires); - return vads; - } else if (storage.localStorageIsEnabled()) { - storage.setDataInLocalStorage(USER_ID_KEY, vads); - return vads; - } - - return null; -} - -function _roundResponseTime(time, timeRange) { - if (time <= 0) { - return 0; // Special code for scriptLoadTime of 0 ms or less - } else if (time > 5000) { - return 100; // Constant code for scriptLoadTime greater than 5000 ms - } else { - const roundedValue = Math.floor((time - 1) / timeRange) + 1; - return roundedValue; - } -} - registerBidder(spec); diff --git a/modules/voxBidAdapter.js b/modules/voxBidAdapter.js index f1670de76d0..acf1cc9579c 100644 --- a/modules/voxBidAdapter.js +++ b/modules/voxBidAdapter.js @@ -4,39 +4,20 @@ import {BANNER, VIDEO} from '../src/mediaTypes.js'; import {find} from '../src/polyfill.js'; import {auctionManager} from '../src/auctionManager.js'; import {Renderer} from '../src/Renderer.js'; -import {config} from '../src/config.js' - -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse - * @typedef {import('../src/adapters/bidderFactory.js').validBidRequests} validBidRequests - */ - -const { getConfig } = config; const BIDDER_CODE = 'vox'; const SSP_ENDPOINT = 'https://ssp.hybrid.ai/auction/prebid'; const VIDEO_RENDERER_URL = 'https://acdn.adnxs.com/video/outstream/ANOutstreamVideo.js'; const TTL = 60; -const GVLID = 206; function buildBidRequests(validBidRequests) { - return _map(validBidRequests, function(bid) { - const currency = getConfig('currency.adServerCurrency'); - const floorInfo = bid.getFloor ? bid.getFloor({ - currency: currency || 'USD' - }) : {}; - - const params = bid.params; + return _map(validBidRequests, function(validBidRequest) { + const params = validBidRequest.params; const bidRequest = { - floorInfo, - schain: bid.schain, - userId: bid.userId, - bidId: bid.bidId, + bidId: validBidRequest.bidId, // TODO: fix transactionId leak: https://github.com/prebid/Prebid.js/issues/9781 - transactionId: bid.transactionId, - sizes: bid.sizes, + transactionId: validBidRequest.transactionId, + sizes: validBidRequest.sizes, placement: params.placement, placeId: params.placementId, imageUrl: params.imageUrl @@ -191,7 +172,6 @@ function wrapBanner(bid, bidData) { export const spec = { code: BIDDER_CODE, - gvlid: GVLID, supportedMediaTypes: [BANNER, VIDEO], /** diff --git a/modules/vrtcalBidAdapter.js b/modules/vrtcalBidAdapter.js index 07dc35525c3..ff7839d0e02 100644 --- a/modules/vrtcalBidAdapter.js +++ b/modules/vrtcalBidAdapter.js @@ -1,16 +1,11 @@ import {registerBidder} from '../src/adapters/bidderFactory.js'; import { BANNER } from '../src/mediaTypes.js'; import {ajax} from '../src/ajax.js'; +import {isFn, isPlainObject} from '../src/utils.js'; import { config } from '../src/config.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`; -const VRTCAL_USER_SYNC_URL_REDIRECT = `https://usync.vrtcal.com/i?ssp=1804&synctype=redirect`; export const spec = { code: 'vrtcal', - gvlid: GVLID, supportedMediaTypes: [BANNER], isBidRequestValid: function (bid) { return true; @@ -67,14 +62,13 @@ export const spec = { site: { id: 'VRTCAL_FILLED', name: 'VRTCAL_FILLED', - cat: deepAccess(bid, 'ortb2.site.cat', []), - domain: decodeURIComponent(window.location.href).replace('https://', '').replace('http://', '').split('/')[0], - page: window.location.href + cat: ['VRTCAL_FILLED'], + domain: decodeURIComponent(window.location.href).replace('https://', '').replace('http://', '').split('/')[0] + }, device: { - language: navigator.language, - ua: navigator.userAgent, - ip: deepAccess(bid, 'params.bidOverride.device.ip') || deepAccess(bid, 'params.ext.ip') || undefined + ua: 'VRTCAL_FILLED', + ip: 'VRTCAL_FILLED' }, regs: { coppa: coppa, @@ -150,34 +144,7 @@ export const spec = { ); ajax(winUrl, null); return true; - }, - - getUserSyncs: function(syncOptions, serverResponses, gdprConsent = {}, uspConsent = '', gppConsent = {}) { - const syncs = []; - const gdprFlag = `&gdpr=${gdprConsent.gdprApplies ? 1 : 0}`; - const gdprString = `&gdpr_consent=${encodeURIComponent((gdprConsent.consentString || ''))}`; - const usPrivacy = `&us_privacy=${encodeURIComponent(uspConsent)}`; - const gpp = gppConsent.gppString ? gppConsent.gppString : ''; - const gppSid = Array.isArray(gppConsent.applicableSections) ? gppConsent.applicableSections.join(',') : ''; - let vrtcalSyncURL = '' - - if (syncOptions.iframeEnabled) { - vrtcalSyncURL = `${VRTCAL_USER_SYNC_URL_IFRAME}${usPrivacy}${gdprFlag}${gdprString}&gpp=${gpp}&gpp_sid=${gppSid}&surl=`; - syncs.push({ - type: 'iframe', - url: vrtcalSyncURL - }); - } else { - vrtcalSyncURL = `${VRTCAL_USER_SYNC_URL_REDIRECT}${usPrivacy}${gdprFlag}${gdprString}&gpp=${gpp}&gpp_sid=${gppSid}&surl=`; - syncs.push({ - type: 'image', - url: vrtcalSyncURL - }); - } - - return syncs; } - }; registerBidder(spec); diff --git a/modules/weboramaRtdProvider.js b/modules/weboramaRtdProvider.js index a0963b844e1..6ba502d2c8b 100644 --- a/modules/weboramaRtdProvider.js +++ b/modules/weboramaRtdProvider.js @@ -7,29 +7,25 @@ * @requires module:modules/realTimeData */ -/** - * profile metadata +/** profile metadata * @typedef dataCallbackMetadata * @property {boolean} user if true it is user-centric data * @property {string} source describe the source of data, if 'contextual' or 'wam' * @property {boolean} isDefault if true it the default profile defined in the configuration */ -/** - * profile from contextual, wam or sfbx +/** profile from contextual, wam or sfbx * @typedef {Object.} Profile */ -/** - * onData callback type +/** onData callback type * @callback dataCallback * @param {Profile} data profile data * @param {dataCallbackMetadata} meta metadata * @returns {void} */ -/** - * setPrebidTargeting callback type +/** setPrebidTargeting callback type * @callback setPrebidTargetingCallback * @param {string} adUnitCode * @param {Profile} data @@ -37,8 +33,7 @@ * @returns {boolean} */ -/** - * sendToBidders callback type +/** sendToBidders callback type * @callback sendToBiddersCallback * @param {Object} bid * @param {string} adUnitCode @@ -95,8 +90,7 @@ * @property {?boolean} enabled if false, will ignore this configuration */ -/** - * common configuration between contextual, wam and sfbx +/** common configuration between contextual, wam and sfbx * @typedef {WeboCtxConf|WeboUserDataConf|SfbxLiteDataConf} CommonConf */ @@ -115,7 +109,8 @@ import { isBoolean, isPlainObject, logWarn, - mergeDeep + mergeDeep, + tryAppendQueryString } from '../src/utils.js'; import { submodule @@ -128,7 +123,6 @@ import { } from '../src/storageManager.js'; import adapterManager from '../src/adapterManager.js'; import {MODULE_TYPE_RTD} from '../src/activities/modules.js'; -import {tryAppendQueryString} from '../libraries/urlUtils/urlUtils.js'; /** @type {string} */ const MODULE_NAME = 'realTimeData'; @@ -194,8 +188,7 @@ class WeboramaRtdProvider { constructor(components) { this.#components = components; } - /** - * Initialize module + /** Initialize module * @method * @param {Object} moduleConfig * @param {?ModuleParams} moduleConfig.params @@ -226,8 +219,7 @@ class WeboramaRtdProvider { return Object.values(this.#components).some((c) => c.initialized); } - /** - * function that will allow RTD sub-modules to modify the AdUnit object for each auction + /** function that will allow RTD sub-modules to modify the AdUnit object for each auction * @method * @param {Object} reqBidsConfigObj * @param {doneCallback} onDone @@ -261,8 +253,7 @@ class WeboramaRtdProvider { }); } - /** - * function that provides ad server targeting data to RTD-core + /** function that provides ad server targeting data to RTD-core * @method * @param {string[]} adUnitsCodes * @param {Object} moduleConfig @@ -303,8 +294,7 @@ class WeboramaRtdProvider { } } - /** - * Initialize subsection module + /** Initialize subsection module * @method * @private * @param {ModuleParams} moduleParams @@ -340,8 +330,7 @@ class WeboramaRtdProvider { return true; } - /** - * normalize submodule configuration + /** normalize submodule configuration * @method * @private * @param {ModuleParams} moduleParams @@ -374,8 +363,7 @@ class WeboramaRtdProvider { } } - /** - * coerce setPrebidTargeting to a callback + /** coerce setPrebidTargeting to a callback * @method * @private * @param {CommonConf} submoduleParams @@ -391,8 +379,7 @@ class WeboramaRtdProvider { } } - /** - * coerce sendToBidders to a callback + /** coerce sendToBidders to a callback * @method * @private * @param {CommonConf} submoduleParams @@ -439,8 +426,7 @@ class WeboramaRtdProvider { * @typedef {Object} AdUnit * @property {Object[]} bids */ - /** - * function that handles bid request data + /** function that handles bid request data * @method * @private * @param {Object} reqBidsConfigObj @@ -490,21 +476,18 @@ class WeboramaRtdProvider { }); } - /** - * onSuccess callback type + /** onSuccess callback type * @callback successCallback * @param {?Object} data * @returns {void} */ - /** - * onDone callback type + /** onDone callback type * @callback doneCallback * @returns {void} */ - /** - * Fetch Bigsea Contextual Profile + /** Fetch Bigsea Contextual Profile * @method * @private * @param {WeboCtxConf} weboCtxConf @@ -583,8 +566,7 @@ class WeboramaRtdProvider { ajax(urlProfileAPI, callback, null, options); } - /** - * set bigsea contextual profile on module state + /** set bigsea contextual profile on module state * @method * @private * @param {?Object} data @@ -597,8 +579,7 @@ class WeboramaRtdProvider { } } - /** - * function that provides data handlers based on the configuration + /** function that provides data handlers based on the configuration * @method * @private * @param {ModuleParams} moduleParams @@ -686,8 +667,7 @@ class WeboramaRtdProvider { onData: dataConf.onData, }; } - /** - * handle individual bid + /** handle individual bid * @method * @private * @param {Object} reqBidsConfigObj @@ -714,8 +694,7 @@ class WeboramaRtdProvider { } } - /** - * function that handles bid request data + /** function that handles bid request data * @method * @private * @param {ProfileHandler} ph profile handler @@ -726,8 +705,7 @@ class WeboramaRtdProvider { return [deepClone(ph.data), deepClone(ph.metadata)]; } - /** - * handle appnexus/xandr bid + /** handle appnexus/xandr bid * @method * @private * @param {Object} reqBidsConfigObj @@ -745,8 +723,7 @@ class WeboramaRtdProvider { // this.#setBidderOrtb2(reqBidsConfigObj.ortb2Fragments?.bidder, bid.bidder, base, profile); } - /** - * handle generic bid via ortb2 arbitrary data + /** handle generic bid via ortb2 arbitrary data * @method * @private * @param {Object} reqBidsConfigObj @@ -867,8 +844,7 @@ export function isValidProfile(profile) { * @returns {buildProfileHandlerCallback} */ function getContextualProfile(component /* equivalent to this */) { - /** - * return contextual profile + /** return contextual profile * @param {WeboCtxConf} weboCtxConf * @returns {[Profile,boolean]} contextual profile + isDefault boolean flag */ @@ -889,8 +865,7 @@ function getContextualProfile(component /* equivalent to this */) { * @returns {buildProfileHandlerCallback} */ function getWeboUserDataProfile(component /* equivalent to this */) { - /** - * return weboUserData profile + /** return weboUserData profile * @param {WeboUserDataConf} weboUserDataConf * @returns {[Profile,boolean]} weboUserData profile + isDefault boolean flag */ @@ -910,8 +885,7 @@ function getWeboUserDataProfile(component /* equivalent to this */) { * @returns {buildProfileHandlerCallback} */ function getSfbxLiteDataProfile(component /* equivalent to this */) { - /** - * return weboUserData profile + /** return weboUserData profile * @param {SfbxLiteDataConf} sfbxLiteDataConf * @returns {[Profile,boolean]} sfbxLiteData profile + isDefault boolean flag */ @@ -935,8 +909,7 @@ function getSfbxLiteDataProfile(component /* equivalent to this */) { * @returns {void} */ -/** - * return generic webo data profile +/** return generic webo data profile * @param {WeboUserDataConf|SfbxLiteDataConf} weboDataConf * @param {cacheGetCallback} cacheGet * @param {cacheSetCallback} cacheSet diff --git a/modules/welectBidAdapter.js b/modules/welectBidAdapter.js index 533e6401cd5..d88a3f4c3e2 100644 --- a/modules/welectBidAdapter.js +++ b/modules/welectBidAdapter.js @@ -1,13 +1,6 @@ import { deepAccess } from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse - * @typedef {import('../src/adapters/bidderFactory.js').validBidRequests} validBidRequests - */ - const BIDDER_CODE = 'welect'; const DEFAULT_DOMAIN = 'www.welect.de'; diff --git a/modules/winrBidAdapter.js b/modules/winrBidAdapter.js index cf1158474b4..5cc063d16b4 100644 --- a/modules/winrBidAdapter.js +++ b/modules/winrBidAdapter.js @@ -1,4 +1,6 @@ import { + convertCamelToUnderscore, + convertTypes, deepAccess, getBidRequest, getParameterByName, @@ -14,14 +16,7 @@ import {BANNER} from '../src/mediaTypes.js'; import {find, includes} from '../src/polyfill.js'; import {getStorageManager} from '../src/storageManager.js'; import {hasPurpose1Consent} from '../src/utils/gpdr.js'; -import {getANKeywordParam, transformBidderParamKeywords} from '../libraries/appnexusUtils/anKeywords.js'; -import {convertCamelToUnderscore} from '../libraries/appnexusUtils/anUtils.js'; -import {convertTypes} from '../libraries/transformParamsUtils/convertTypes.js'; - -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - */ +import {getANKeywordParam, transformBidderParamKeywords} from '../libraries/appnexusKeywords/anKeywords.js'; const BIDDER_CODE = 'winr'; const URL = 'https://ib.adnxs.com/ut/v3/prebid'; diff --git a/modules/xeBidAdapter.js b/modules/xeBidAdapter.js index a813b9aa2a3..94a66e9980e 100644 --- a/modules/xeBidAdapter.js +++ b/modules/xeBidAdapter.js @@ -1,16 +1,7 @@ import { config } from '../src/config.js'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; -import {parseSizesInput, isFn, deepAccess, logError, isArray, getBidIdParameter} from '../src/utils.js'; -import {getAdUnitSizes} from '../libraries/sizeUtils/sizeUtils.js'; - -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse - * @typedef {import('../src/adapters/bidderFactory.js').SyncOptions} SyncOptions - * @typedef {import('../src/adapters/bidderFactory.js').UserSync} UserSync - */ +import { getAdUnitSizes, parseSizesInput, isFn, deepAccess, getBidIdParameter, logError, isArray } from '../src/utils.js'; const CUR = 'USD'; const BIDDER_CODE = 'xe'; @@ -150,12 +141,12 @@ function interpretResponse(serverResponse, { bidderRequest }) { } /** - * Register the user sync pixels which should be dropped after the auction. - * - * @param {SyncOptions} syncOptions Which user syncs are allowed? - * @param {ServerResponse[]} serverResponses List of server's responses. - * @return {UserSync[]} The user syncs which should be dropped. - */ +* Register the user sync pixels which should be dropped after the auction. +* +* @param {SyncOptions} syncOptions Which user syncs are allowed? +* @param {ServerResponse[]} serverResponses List of server's responses. +* @return {UserSync[]} The user syncs which should be dropped. +*/ function getUserSyncs(syncOptions, serverResponses, gdprConsent = {}, uspConsent = '') { const syncs = []; const pixels = deepAccess(serverResponses, '0.body.data.0.ext.pixels'); @@ -180,11 +171,11 @@ function getUserSyncs(syncOptions, serverResponses, gdprConsent = {}, uspConsent } /** - * Get valid floor value from getFloor fuction. - * - * @param {Object} bid Current bid request. - * @return {null|Number} Returns floor value when bid.getFloor is function and returns valid floor object with USD currency, otherwise returns null. - */ +* Get valid floor value from getFloor fuction. +* +* @param {Object} bid Current bid request. +* @return {null|Number} Returns floor value when bid.getFloor is function and returns valid floor object with USD currency, otherwise returns null. +*/ export function getBidFloor(bid) { if (!isFn(bid.getFloor)) { return null; diff --git a/modules/yahoosspBidAdapter.js b/modules/yahoosspBidAdapter.js index 0453350a85a..7a414c0d2ee 100644 --- a/modules/yahoosspBidAdapter.js +++ b/modules/yahoosspBidAdapter.js @@ -6,10 +6,9 @@ import { Renderer } from '../src/Renderer.js'; import {hasPurpose1Consent} from '../src/utils/gpdr.js'; const INTEGRATION_METHOD = 'prebid.js'; -const BIDDER_CODE = 'yahooAds'; -const BIDDER_ALIASES = ['yahoossp', 'yahooAdvertising'] +const BIDDER_CODE = 'yahoossp'; const GVLID = 25; -const ADAPTER_VERSION = '1.1.0'; +const ADAPTER_VERSION = '1.0.2'; const PREBID_VERSION = '$prebid.version$'; const DEFAULT_BID_TTL = 300; const TEST_MODE_DCN = '8a969516017a7a396ec539d97f540011'; @@ -50,17 +49,13 @@ const SUPPORTED_USER_ID_SOURCES = [ 'quantcast.com', 'tapad.com', 'uidapi.com', + 'verizonmedia.com', 'yahoo.com', 'zeotap.com' ]; /* Utility functions */ -function getConfigValue(bid, key) { - const bidderCode = bid.bidder || bid.bidderCode; - return config.getConfig(`${bidderCode}.${key}`); -} - function getSize(size) { return { w: parseInt(size[0]), @@ -111,7 +106,7 @@ function extractUserSyncUrls(syncOptions, pixels) { * @param {object} consentData * @param {object} consentData.gpp * @param {string} consentData.gpp.gppConsent - * @param {Array} consentData.gpp.applicableSections + * @param {array} consentData.gpp.applicableSections * @param {object} consentData.gdpr * @param {object} consentData.gdpr.consentString * @param {object} consentData.gdpr.gdprApplies @@ -119,12 +114,11 @@ function extractUserSyncUrls(syncOptions, pixels) { */ function updateConsentQueryParams(url, consentData) { const parameterMap = { - 'gdpr_consent': consentData.gdpr ? consentData.gdpr.consentString : '', - 'gdpr': consentData.gdpr && consentData.gdpr.gdprApplies ? '1' : '0', - 'us_privacy': consentData.uspConsent ? consentData.uspConsent : '', - 'gpp': consentData.gpp ? consentData.gpp.gppString : '', - 'gpp_sid': consentData.gpp && Array.isArray(consentData.gpp.applicableSections) - ? consentData.gpp.applicableSections.join(',') : '' + 'gdpr_consent': consentData.gdpr.consentString, + 'gdpr': consentData.gdpr.gdprApplies ? '1' : '0', + 'us_privacy': consentData.uspConsent, + 'gpp': consentData.gpp.gppString, + 'gpp_sid': consentData.gpp.applicableSections ? consentData.gpp.applicableSections.join(',') : '' } const existingUrl = new URL(url); @@ -161,8 +155,8 @@ function getPubIdMode(bid) { return pubIdMode; }; -function getAdapterMode(bid) { - let adapterMode = getConfigValue(bid, 'mode'); +function getAdapterMode() { + let adapterMode = config.getConfig('yahoossp.mode'); adapterMode = adapterMode ? adapterMode.toLowerCase() : undefined; if (typeof adapterMode === 'undefined' || adapterMode === BANNER) { return BANNER; @@ -183,7 +177,7 @@ function getResponseFormat(bid) { }; function getFloorModuleData(bid) { - const adapterMode = getAdapterMode(bid); + const adapterMode = getAdapterMode(); const getFloorRequestObject = { currency: deepAccess(bid, 'params.bidOverride.cur') || DEFAULT_CURRENCY, mediaType: adapterMode, @@ -193,7 +187,7 @@ function getFloorModuleData(bid) { }; function filterBidRequestByMode(validBidRequests) { - const mediaTypesMode = getAdapterMode(validBidRequests[0]); + const mediaTypesMode = getAdapterMode(); let result = []; if (mediaTypesMode === BANNER) { result = validBidRequests.filter(bid => { @@ -250,7 +244,7 @@ function validateAppendObject(validationType, allowedKeys, inputObject, appendTo }; function getTtl(bidderRequest) { - const globalTTL = getConfigValue(bidderRequest, 'ttl'); + const globalTTL = config.getConfig('yahoossp.ttl'); return globalTTL ? validateTTL(globalTTL) : validateTTL(deepAccess(bidderRequest, 'params.ttl')); }; @@ -282,8 +276,8 @@ function generateOpenRtbObject(bidderRequest, bid) { ext: { 'us_privacy': bidderRequest.uspConsent ? bidderRequest.uspConsent : '', gdpr: bidderRequest.gdprConsent && bidderRequest.gdprConsent.gdprApplies ? 1 : 0, - gpp: bidderRequest.gppConsent ? bidderRequest.gppConsent.gppString : '', - gpp_sid: bidderRequest.gppConsent ? bidderRequest.gppConsent.applicableSections : [] + gpp: bidderRequest.gppConsent.gppString, + gpp_sid: bidderRequest.gppConsent.applicableSections } }, source: { @@ -338,7 +332,7 @@ function generateOpenRtbObject(bidderRequest, bid) { }; function appendImpObject(bid, openRtbObject) { - const mediaTypeMode = getAdapterMode(bid); + const mediaTypeMode = getAdapterMode(); if (openRtbObject && bid) { const impObject = { @@ -500,21 +494,20 @@ function appendFirstPartyData(outBoundBidRequest, bid) { function generateServerRequest({payload, requestOptions, bidderRequest}) { const pubIdMode = getPubIdMode(bidderRequest); - const overrideEndpoint = getConfigValue(bidderRequest, 'endpoint'); - let sspEndpoint = overrideEndpoint || SSP_ENDPOINT_DCN_POS; + let sspEndpoint = config.getConfig('yahoossp.endpoint') || SSP_ENDPOINT_DCN_POS; if (pubIdMode === true) { - sspEndpoint = overrideEndpoint || SSP_ENDPOINT_PUBID; + sspEndpoint = config.getConfig('yahoossp.endpoint') || SSP_ENDPOINT_PUBID; }; if (deepAccess(bidderRequest, 'params.testing.e2etest') === true) { - logInfo('Adapter e2etest mode is active'); + logInfo('yahoossp adapter e2etest mode is active'); requestOptions.withCredentials = false; if (pubIdMode === true) { payload.site.id = TEST_MODE_PUBID_DCN; } else { - const mediaTypeMode = getAdapterMode(bidderRequest); + const mediaTypeMode = getAdapterMode(); payload.site.id = TEST_MODE_DCN; payload.imp.forEach(impObject => { impObject.ext.e2eTestMode = true; @@ -523,9 +516,8 @@ function generateServerRequest({payload, requestOptions, bidderRequest}) { } else if (mediaTypeMode === VIDEO) { impObject.tagid = TEST_MODE_VIDEO_POS; // video passback } else { - const bidderCode = bidderRequest.bidderCode; - logWarn(`e2etest mode does not support ${bidderCode}.mode="all". \n Please specify either "banner" or "video"`); - logWarn(`Adapter e2etest mode: Please make sure your adUnit matches the ${bidderCode}.mode video or banner`); + logWarn('yahoossp adapter e2etest mode does not support yahoossp.mode="all". \n Please specify either "banner" or "video"'); + logWarn('yahoossp adapter e2etest mode: Please make sure your adUnit matches the yahoossp.mode video or banner'); } }); } @@ -536,7 +528,7 @@ function generateServerRequest({payload, requestOptions, bidderRequest}) { method: 'POST', data: payload, options: requestOptions, - bidderRequest // Additional data for use in interpretResponse() + bidderRequest: bidderRequest }; }; @@ -555,7 +547,7 @@ function createRenderer(bidderRequest, bidResponse) { }, deepAccess(bidderRequest, 'params.testing.renderer.setTimeout') || DEFAULT_RENDERER_TIMEOUT); }); } catch (error) { - logWarn('Renderer error: setRender() failed', error); + logWarn('yahoossp renderer error: setRender() failed', error); } return renderer; } @@ -565,7 +557,7 @@ function createRenderer(bidderRequest, bidResponse) { export const spec = { code: BIDDER_CODE, gvlid: GVLID, - aliases: BIDDER_ALIASES, + aliases: [], supportedMediaTypes: [BANNER, VIDEO], isBidRequestValid: function(bid) { @@ -578,14 +570,14 @@ export const spec = { ) { return true; } else { - logWarn('Bidder params missing or incorrect, please pass object with either: dcn & pos OR pubId'); + logWarn('yahoossp bidder params missing or incorrect, please pass object with either: dcn & pos OR pubId'); return false; } }, buildRequests: function(validBidRequests, bidderRequest) { if (isEmpty(validBidRequests) || isEmpty(bidderRequest)) { - logWarn('buildRequests called with either empty "validBidRequests" or "bidderRequest"'); + logWarn('yahoossp Adapter: buildRequests called with either empty "validBidRequests" or "bidderRequest"'); return undefined; }; @@ -600,12 +592,13 @@ export const spec = { const filteredBidRequests = filterBidRequestByMode(validBidRequests); - if (getConfigValue(bidderRequest, 'singleRequestMode') === true) { + if (config.getConfig('yahoossp.singleRequestMode') === true) { const payload = generateOpenRtbObject(bidderRequest, filteredBidRequests[0]); filteredBidRequests.forEach(bid => { appendImpObject(bid, payload); }); - return [generateServerRequest({payload, requestOptions, bidderRequest})]; + + return generateServerRequest({payload, requestOptions, bidderRequest}); } return filteredBidRequests.map(bid => { @@ -615,11 +608,12 @@ export const spec = { }); }, - interpretResponse: function(serverResponse, { bidderRequest }) { + interpretResponse: function(serverResponse, { data, bidderRequest }) { const response = []; if (!serverResponse.body || !Array.isArray(serverResponse.body.seatbid)) { return response; } + let seatbids = serverResponse.body.seatbid; seatbids.forEach(seatbid => { let bid; @@ -634,6 +628,7 @@ export const spec = { let bidResponse = { adId: deepAccess(bid, 'adId') ? bid.adId : bid.impid || bid.crid, + adUnitCode: bidderRequest.adUnitCode, requestId: bid.impid, cpm: cpm, width: bid.w, diff --git a/modules/yahoosspBidAdapter.md b/modules/yahoosspBidAdapter.md index 62fe0f22a55..35a640b92b1 100644 --- a/modules/yahoosspBidAdapter.md +++ b/modules/yahoosspBidAdapter.md @@ -1,10 +1,10 @@ # Overview -**Module Name:** Yahoo Advertising Bid Adapter +**Module Name:** yahoossp Bid Adapter **Module Type:** Bidder Adapter -**Maintainer:** prebid-tech-team@yahooinc.com +**Maintainer:** hb-fe-tech@yahooinc.com # Description -The Yahoo Advertising Bid Adapter is an OpenRTB interface that consolidates all previous "Oath.inc" adapters such as: "aol", "oneMobile", "oneDisplay" & "oneVideo" supply-side platforms. +The Yahoo SSP Bid Adapter is an OpenRTB interface that consolidates all previous "Oath.inc" adapters such as: "aol", "oneMobile", "oneDisplay" & "oneVideo" supply-side platforms. # Supported Features: * Media Types: Banner & Video @@ -19,63 +19,51 @@ The Yahoo Advertising Bid Adapter is an OpenRTB interface that consolidates all * First Party Data (ortb2 & ortb2Imp) * Custom TTL (time to live) -# 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: - -```javascript -pbjs.setConfig({ - yahoossp: { - mode: 'banner' // 'all', 'video', 'banner' (default) - } -}); -``` # Adapter Request mode -Since the Yahoo Advertising bid adapter supports both Banner and Video adUnits, a controller was needed to allow you to define when the adapter should generate a bid-requests to the Yahoo bid endpoint. +Since the yahoossp adapter now supports both Banner and Video adUnits a controller was needed to allow you to define when the adapter should generate a bid-requests to our Yahoo SSP. **Important!** By default the adapter mode is set to "banner" only. -This means that you do not need to explicitly declare the `yahooAds.mode` property in the global config to initiate banner adUnit requests. +This means that you do not need to explicitly declare the yahoossp.mode in the Global config to initiate banner adUnit requests. ## Request modes: * **undefined** - (Default) Will generate bid-requests for "Banner" formats only. * **banner** - Will generate bid-requests for "Banner" formats only (Explicit declaration). * **video** - Will generate bid-requests for "Video" formats only (Explicit declaration). -* **all** - Will generate bid-requests for both "Banner" & "Video" formats. +* **all** - Will generate bid-requests for both "Banner" & "Video" formats -**Important!** When setting `yahooAds.mode` to `'all'`, make sure your Yahoo Placement (pos id) supports both Banner & Video placements. -If it does not, the Yahoo bid server will respond only in the format it is set too. +**Important!** When setting yahoossp.mode = 'all' Make sure your Yahoo SSP Placement (pos id) supports both Banner & Video placements. +If it does not, the Yahoo SSP will respond only in the format it is set too. -### Example: explicitly setting the request mode ```javascript pbjs.setConfig({ - yahooAds: { + yahoossp: { mode: 'banner' // 'all', 'video', 'banner' (default) } }); ``` - # Integration Options -The Yahoo Advertising bid adapter supports 2 types of integration: +The `yahoossp` bid adapter supports 2 types of integration: 1. **dcn & pos** DEFAULT (Site/App & Position targeting) - For Display partners/publishers. -2. **pubId** (Publisher ID) - For legacy "oneVideo" AND new partners/publishers. -**Important:** pubId integration (option 2) is only possible when your seller account is setup for "Inventory Mapping". +2. **pubId** (Publisher ID) - For legacy "oneVideo" AND New partners/publishers. +**Important:** pubId integration (option 2) is only possible when your Seller account is setup for "Inventory Mapping". **Please Note:** Most examples in this file are using dcn & pos. ## Who is currently eligible for "pubId" integration At this time, only the following partners/publishers are eligble for pubId integration: -1. New partners/publishers that do not have an existing account with Yahoo Advertising (aka: aol, oneMobile, oneDisplay). +1. New partners/publishers that do not have any existing accounts on Yahoo SSP (aka: aol, oneMobile, oneDisplay). 2. Video SSP (oneVideo) partners/publishers that A. Do not have any display/banner inventory. - B. Do not have an existing account with Yahoo Advertising (aka: aol, oneMobile, oneDisplay). + B. Do not have any existing accounts on Yahoo SSP (aka: aol, oneMobile, oneDisplay). # Mandatory Bidder Parameters ## dcn & pos (DEFAULT) -The minimal requirements for the Yahoo Advertising bid adapter to generate an outbound bid-request to Yahoo's bid endpoint are: +The minimal requirements for the 'yahoossp' bid adapter to generate an outbound bid-request to our Yahoo SSP are: 1. At least 1 adUnit including mediaTypes: banner or video 2. **bidder.params** object must include: - A. **dcn:** Yahoo Advertising Site/App inventory parameter. - B. **pos:** Yahoo Advertising position inventory parameter. + A. **dcn:** Yahoo SSP Site/App inventory parameter. + B. **pos:** Yahoo SSP position inventory parameter. ### Example: dcn & pos Mandatory Parameters (Single banner adUnit) ```javascript @@ -88,10 +76,10 @@ const adUnits = [{ }, bids: [ { - bidder: 'yahooAds', + bidder: 'yahoossp', params: { - dcn: '8a969516017a7a396ec539d97f540011', // Site/App ID provided by Yahoo Advertising - pos: '8a969978017a7aaabab4ab0bc01a0009' // Placement ID provided by Yahoo Advertising + dcn: '8a969516017a7a396ec539d97f540011', // Site/App ID provided from SSP + pos: '8a969978017a7aaabab4ab0bc01a0009' // Placement ID provided from SSP } } ] @@ -99,10 +87,10 @@ const adUnits = [{ ``` ## pubId -The minimal requirements for the Yahoo Advertising bid adapter to generate an outbound bid-request to Yahoo's bid endpoint are: +The minimal requirements for the 'yahoossp' bid adapter to generate an outbound bid-request to our Yahoo SSP are: 1. At least 1 adUnit including mediaTypes: banner or video 2. **bidder.params** object must include: - A. **pubId:** Yahoo Advertising Publisher ID (AKA oneVideo pubId/Exchange name) + A. **pubId:** Yahoo SSP Publisher ID (AKA oneVideo pubId/Exchange name) ### Example: pubId Mandatory Parameters (Single banner adUnit) ```javascript @@ -115,15 +103,14 @@ const adUnits = [{ }, bids: [ { - bidder: 'yahooAds', + bidder: 'yahoossp', params: { - pubId: 'DemoPublisher', // Publisher defined external ID as configured by Yahoo Advertising. + pubId: 'DemoPublisher', // Publisher External ID provided from Yahoo SSP. } } ] }]; ``` - # Advanced adUnit Examples: ## Banner ```javascript @@ -137,22 +124,21 @@ const adUnits = [{ } }, bids: [{ - bidder: 'yahooAds', + bidder: 'yahoossp', params: { - dcn: '8a969516017a7a396ec539d97f540011', // Site/App ID provided by Yahoo Advertising - pos: '8a969978017a7aaabab4ab0bc01a0009', // Placement ID provided by Yahoo Advertising + dcn: '8a969516017a7a396ec539d97f540011', // Site/App ID provided from Yahoo SSP + pos: '8a969978017a7aaabab4ab0bc01a0009', // Placement ID provided from Yahoo SSP } } }] }]; ``` - ## Video Instream -**Important!** Make sure that the Yahoo Advertising Placement type (in-stream) matches the adUnit video inventory type. +**Important!** Make sure that the Yahoo SSP Placement type (in-stream) matches the adUnit video inventory type. **Note:** Make sure to set the adapter mode to allow video requests by setting it to mode: 'video' OR mode: 'all'. ```javascript pbjs.setConfig({ - yahooAds: { + yahoossp: { mode: 'video' } }); @@ -170,21 +156,20 @@ const adUnits = [{ } }, bids: [{ - bidder: 'yahooAds', + bidder: 'yahoossp', params: { - dcn: '8a969516017a7a396ec539d97f540011', // Site/App ID provided by Yahoo Advertising - pos: '8a96958a017a7a57ac375d50c0c700cc', // Placement ID provided by Yahoo Advertising + dcn: '8a969516017a7a396ec539d97f540011', // Site/App ID provided from Yahoo SSP + pos: '8a96958a017a7a57ac375d50c0c700cc', // Placement ID provided from Yahoo SSP } }] }]; - ``` ## Video Outstream -**Important!** Make sure that the Yahoo Advertsing placement type (in-feed/ in-article) matches the adUnit video inventory type. +**Important!** Make sure that the Yahoo SSP Placement type (in-feed/ in-article) matches the adUnit video inventory type. **Note:** Make sure to set the adapter mode to allow video requests by setting it to mode: 'video' OR mode: 'all' ```javascript pbjs.setConfig({ - yahooAds: { + yahoossp: { mode: 'video' } }); @@ -202,22 +187,22 @@ const adUnits = [{ } }, bids: [{ - bidder: 'yahooAds', + bidder: 'yahoossp', params: { - dcn: '8a969516017a7a396ec539d97f540011', // Site/App ID provided by Yahoo Advertising - pos: '8a96958a017a7a57ac375d50c0c700cc', // Placement ID provided by Yahoo Advertising + dcn: '8a969516017a7a396ec539d97f540011', // Site/App ID provided from Yahoo SSP + pos: '8a96958a017a7a57ac375d50c0c700cc', // Placement ID provided from Yahoo SSP } }] }]; - ``` ## Multi-Format -**Important!** If you intend to use the Yahoo Advertising bidder for both Banner and Video formats please make sure: -1. Set the adapter as mode: 'all' - to configure the bid adapter to call the bid endpoint for both banner & video formats. -2. Make sure the Yahoo Advertising placement (pos id) supports both banner & video format requests. +**Important!** If you intend to use the yahoossp bidder for both Banner and Video formats please make sure: +1. Set the adapter as mode: 'all' - to call the Yahoo SSP for both banner & video formats. +2. Make sure the Yahoo SSP placement (pos id) supports both banner & video format requests. + ```javascript pbjs.setConfig({ - yahooAds: { + yahoossp: { mode: 'all' } }); @@ -238,18 +223,19 @@ const adUnits = [{ } }, bids: [{ - bidder: 'yahooAds', + bidder: 'yahoossp', params: { - dcn: '8a969516017a7a396ec539d97f540011', // Site/App ID provided by Yahoo Advertising - pos: '8a96958a017a7a57ac375d50c0c700cc', // Placement ID provided by Yahoo Advertising + dcn: '8a969516017a7a396ec539d97f540011', // Site/App ID provided from Yahoo SSP + pos: '8a96958a017a7a57ac375d50c0c700cc', // Placement ID provided from Yahoo SSP } }] }]; ``` # Optional: Schain module support -The Yahoo Advertising bid adapter supports the Prebid.org Schain module and will pass it through to our bid endpoint. +The yahoossp adapter supports the Prebid.org Schain module and will pass it through to our Yahoo SSP For further details please see, https://docs.prebid.org/dev-docs/modules/schain.html + ## Global Schain Example: ```javascript pbjs.setConfig({ @@ -270,7 +256,7 @@ For further details please see, https://docs.prebid.org/dev-docs/modules/schain. ## Bidder Specific Schain Example: ```javascript pbjs.setBidderConfig({ - "bidders": ['yahooAds'], // can list more bidders here if they share the same config + "bidders": ['yahoossp'], // can list more bidders here if they share the same config "config": { "schain": { "validation": "strict", @@ -289,10 +275,12 @@ For further details please see, https://docs.prebid.org/dev-docs/modules/schain. ``` # Optional: Price floors module & bidfloor -The Yahoo Advertising bid adapter supports the Prebid.org Price Floors module and will use it to define the outbound bidfloor and currency, if the relevant floors have been defined in the configuration. -A cusom method for defining bid floors is also supported, this can be enabled by setting the `params.bidOverride.imp.bidfloor` bidder parameter. +The yahoossp adapter supports the Prebid.org Price Floors module and will use it to define the outbound bidfloor and currency. +By default the adapter will always check the existance of Module price floor. +If a module price floor does not exist you can set a custom bid floor for your impression using "params.bidOverride.imp.bidfloor". **Note:** All override params apply to all requests generated using this configuration regardless of format type. + ```javascript const adUnits = [{ code: 'override-pricefloor', @@ -304,10 +292,10 @@ const adUnits = [{ } }, bids: [{ - bidder: 'yahooAds', + bidder: 'yahoossp', params: { - dcn: '8a969516017a7a396ec539d97f540011', // Site/App ID provided by Yahoo Advertising - pos: '8a969978017a7aaabab4ab0bc01a0009', // Placement ID provided by Yahoo Advertising + dcn: '8a969516017a7a396ec539d97f540011', // Site/App ID provided from Yahoo SSP + pos: '8a969978017a7aaabab4ab0bc01a0009', // Placement ID provided from Yahoo SSP bidOverride :{ imp: { bidfloor: 5.00 // bidOverride priceFloor @@ -322,21 +310,21 @@ const adUnits = [{ For further details please see, https://docs.prebid.org/dev-docs/modules/floors.html # Optional: Self-served E2E testing mode -If you want to see how the Yahoo Advertising bid adapter works and loads you are invited to try it out using our testing mode. +If you want to see how the yahoossp adapter works and loads you are invited to try it out using our testing mode. This is useful for integration testing and response parsing when checking banner vs video capabilities. ## How to use E2E test mode: -1. Set the `yahooAds` global config mode to either `'banner'` or `'video'` - depending on the adUnit you want to test. +1. Set the yahoossp global config mode to either 'banner' or 'video' - depending on the adUnit you want to test. 2. Add params.testing.e2etest: true to your adUnit bidder config - See examples below. **Note:** When using E2E Test Mode you do not need to pass mandatory bidder params dcn or pos. -**Important!** E2E Testing Mode only works when the Bidder Request Mode is set explicitly to either `'banner'` or `'video'`. +**Important!** E2E Testing Mode only works when the Bidder Request Mode is set explicitly to either 'banner' or 'video'. ## Activating E2E Test for "Banner" ```javascript pbjs.setConfig({ - yahooAds: { + yahoossp: { mode: 'banner' // select 'banner' or 'video' to define what response to load } }); @@ -350,7 +338,7 @@ const adUnits = [{ }, bids: [ { - bidder: 'yahooAds', + bidder: 'yahoossp', params: { testing: { e2etest: true // Activate E2E Test mode @@ -365,7 +353,7 @@ const adUnits = [{ **Note:** We recommend using Video Outstream as it would load the video response using our Outstream Renderer feature ```javascript pbjs.setConfig({ - yahooAds: { + yahoossp: { mode: 'video' } }); @@ -383,7 +371,7 @@ const adUnits = [{ } }, bids: [{ - bidder: 'yahooAds', + bidder: 'yahoossp', params: { testing: { e2etest: true // Activate E2E Test mode @@ -394,8 +382,8 @@ const adUnits = [{ ``` # Optional: First Party Data -The Yahoo Advertising bid adapter supports first party data passed via: -1. Global ortb2 object using `pbjs.setConfig()` +The yahoossp adapter now supports first party data passed via: +1. Global ortb2 object using pbjs.setConfig() 2. adUnit ortb2Imp object declared within an adUnit. For further details please see, https://docs.prebid.org/features/firstPartyData.html ## Global First Party Data "ortb2" @@ -404,15 +392,15 @@ For further details please see, https://docs.prebid.org/features/firstPartyData. pbjs.setConfig({ ortb2: { site: { - name: 'Yahoo Advertising', - domain: 'yahooadvertising.com', + name: 'yahooAdTech', + domain: 'yahooadtech.com', cat: ['IAB2'], sectioncat: ['IAB2-2'], pagecat: ['IAB2-2'], - page: 'https://page.yahooadvertising.com.com/here.html', - ref: 'https://ref.yahooadvertising.com.com/there.html', + page: 'https://page.yahooadtech.com/here.html', + ref: 'https://ref.yahooadtech.com/there.html', keywords:'yahoo, ad, tech', - search: 'header bidding', + search: 'SSP', content: { id: '1234', title: 'Title', @@ -533,7 +521,7 @@ pbjs.setConfig({ ## AdUnit First Party Data "ortb2Imp" Most DSPs are adopting the Global Placement ID (GPID). -Please pass your placement specific GPID value by setting `adUnit.ortb2Imp.ext.data.pbadslot`. +Please pass your placement specific GPID value to Yahoo SSP using `adUnit.ortb2Imp.ext.data.pbadslot`. ```javascript const adUnits = [{ code: 'placement', @@ -553,7 +541,7 @@ const adUnits = [{ } }, bids: [{ - bidder: 'yahooAds', + bidder: 'yahoossp', params: { pubdId: 'DemoPublisher' } @@ -563,7 +551,7 @@ const adUnits = [{ ``` # Optional: Bidder bidOverride Parameters -The Yahoo Advertising bid adapter allows passing override data to the outbound bid-request that overrides First Party Data. +The yahoossp adapter allows passing override data to the outbound bid-request in that overrides First Party Data. **Important!** We highly recommend using prebid modules to pass data instead of bidder speicifc overrides. The use of these parameters are a last resort to force a specific feature or use case in your implementation. @@ -589,6 +577,7 @@ Currently the bidOverride object only accepts the following: * device * ip + ```javascript const adUnits = [{ code: 'bidOverride-adUnit', @@ -601,7 +590,7 @@ const adUnits = [{ } }, bids: [{ - bidder: 'yahooAds', + bidder: 'yahoossp', params: { dcn: '8a969516017a7a396ec539d97f540011', pos: '8a96958a017a7a57ac375d50c0c700cc', @@ -626,7 +615,7 @@ const adUnits = [{ } }, site: { - page: 'https://yahooAdvertising-bid-adapter.com', + page: 'https://yahoossp-bid-adapter.com', }, device: { ip: "1.2.3.4" @@ -641,7 +630,7 @@ const adUnits = [{ Custom key-value paris can be used for both inventory targeting and reporting. You must set up key-value pairs in the Yahoo SSP before sending them via the adapter otherwise the Ad Server will not be listening and picking them up. -Important! Key-value pairs can only contain values of the following data types: String, Number, Array of strings OR Array of numbers +Important! Key-value pairs can only contain values of types: String, Number, Array of strings OR Array of numbers ```javascript const adUnits = [{ @@ -655,7 +644,7 @@ const adUnits = [{ } }, bids: [{ - bidder: 'yahooAds', + bidder: 'yahoossp', params: { dcn: '8a969516017a7a396ec539d97f540011', pos: '8a96958a017a7a57ac375d50c0c700cc', @@ -671,14 +660,14 @@ const adUnits = [{ ``` # Optional: Custom Cache Time To Live (ttl): -The Yahoo Advertising bid adapter supports passing of "Time To Live" (ttl) to indicate to prebid how long the bid response from Yahoo Advertising should be retained by Prebid for. This configuration value must be a Number in seconds, with the valid range being 1 - 3600 inclusive. -The setting can be defined globally using `setConfig` or within the adUnit.params. -Global level `setConfig` overrides adUnit.params. +The yahoossp adapter supports passing of "Time To Live" (ttl) that indicates to prebid chache for how long to keep the chaced winning bid alive. Value is Number in seconds and you can enter any number between 1 - 3600 (seconds). +The setting can be defined globally using setConfig or within the adUnit.params. +Global level setConfig overrides adUnit.params. If no value is being passed default is 300 seconds. ## Global TTL ```javascript pbjs.setConfig({ - yahooAds: { + yahoossp: { ttl: 300 } }); @@ -694,7 +683,7 @@ const adUnits = [{ } }, bids: [{ - bidder: 'yahooAds', + bidder: 'yahoossp', params: { dcn: '8a969516017a7a396ec539d97f540011', pos: '8a96958a017a7a57ac375d50c0c700cc', @@ -713,7 +702,7 @@ const adUnits = [{ } }, bids: [{ - bidder: 'yahooAds', + bidder: 'yahoossp', params: { dcn: '8a969516017a7a396ec539d97f540011', pos: '8a96958a017a7a57ac375d50c0c700cc', @@ -721,11 +710,10 @@ const adUnits = [{ } }] }] - ``` # Optional: Video Features ## Rewarded video flag -To indicate to Yahoo Advertising that this adUnit is a rewarded video you can set the `params.bidOverride.imp.video.rewarded` property to `1` +To indicate to Yahoo SSP that this adUnit is a rewarded video you can pass the following in the params.bidOverride.imp.video.rewarded: 1 ```javascript const adUnits = [{ @@ -739,7 +727,7 @@ const adUnits = [{ } }, bids: [{ - bidder: 'yahooAds', + bidder: 'yahoossp', params: { dcn: '8a969516017a7a396ec539d97f540011', pos: '8a96958a017a7a57ac375d50c0c700cc', @@ -756,7 +744,7 @@ const adUnits = [{ ``` ## Site/App Targeting for "pubId" Inventory Mapping -To target your adUnit explicitly to a specific Site/App Object in Yahoo Advertising, you can pass one of the following: +To target your adUnit explicitly to a specific Site/App Object in Yahoo SSP, you can pass one of the following: 1. params.siteId = External Site ID || Video SSP RTBIS Id (in String format). 2. params.bidOverride.site.id = External Site ID || Video SSP RTBIS Id (in String format). **Important:** Site override is a only supported when using "pubId" mode. @@ -774,7 +762,7 @@ const adUnits = [{ } }, bids: [{ - bidder: 'yahooAds', + bidder: 'yahoossp', params: { pubId: 'DemoPublisher', siteId: '1234567'; @@ -784,13 +772,13 @@ const adUnits = [{ ``` ## Placement Targeting for "pubId" Inventory Mapping -To target your adUnit explicitly to a specific Placement within a Site/App Object in Yahoo Advertising, you can pass the following params.placementId = External Placement ID || Placement Alias +To target your adUnit explicitly to a specific Placement within a Site/App Object in Yahoo SSP, you can pass the following params.placementId = External Placement ID || Placement Alias **Important!** Placement override is a only supported when using "pubId" mode. -**Important!** It is highly recommended that you pass both `siteId` AND `placementId` together to avoid inventory mismatching. +**Important!** It is highly recommended that you pass both `siteId` AND `placementId` together to avoid inventory miss matching. ### Site & Placement override -**Important!** If the placement ID does not reside under the defined Site/App object, the request will not resolve and no response will be sent back from the bid-server. +**Important!** If the placement ID does not reside under the defined Site/App object, the request will not resolve and no response will be sent back from the ad-server. ```javascript const adUnits = [{ code: 'pubId-site-targeting-adUnit', @@ -803,7 +791,7 @@ const adUnits = [{ } }, bids: [{ - bidder: 'yahooAds', + bidder: 'yahoossp', params: { pubId: 'DemoPublisher', siteId: '1234567', @@ -813,7 +801,7 @@ const adUnits = [{ }] ``` ### Placement only override -**Important!** Using this method is not advised if you have multiple Site/Apps that are broken out of a Run Of Network (RON) Site/App. If the placement ID does not reside under a matching Site/App object, the request will not resolve and no response will be sent back from the bid-server. +**Important!** Using this method is not advised if you have multiple Site/Apps that are broken out of a Run Of Network (RON) Site/App. If the placement ID does not reside under a matching Site/App object, the request will not resolve and no response will be sent back from the ad-server. ```javascript const adUnits = [{ code: 'pubId-site-targeting-adUnit', @@ -826,7 +814,7 @@ const adUnits = [{ } }, bids: [{ - bidder: 'yahooAds', + bidder: 'yahoossp', params: { pubId: 'DemoPublisher', placementId: 'header-250x300' @@ -836,8 +824,9 @@ const adUnits = [{ ``` # Optional: Legacy override Parameters -This adapter does not support passing legacy overrides via `bidder.params.ext` since most of the data should be passed using prebid modules (First Party Data, Schain, Price Floors etc.). +This adapter does not support passing legacy overrides via 'bidder.params.ext' since most of the data should be passed using prebid modules (First Party Data, Schain, Price Floors etc.). If you do not know how to pass a custom parameter that you previously used, please contact us using the information provided above. Thank you, -Yahoo Advertsing +Yahoo SSP + diff --git a/modules/yandexBidAdapter.js b/modules/yandexBidAdapter.js index cb1cbd89c84..2870229f1db 100644 --- a/modules/yandexBidAdapter.js +++ b/modules/yandexBidAdapter.js @@ -1,58 +1,12 @@ -import { formatQS, deepAccess, deepSetValue, triggerPixel, _each, _map } from '../src/utils.js'; +import { formatQS, deepAccess, triggerPixel, _each, _map } 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 { config } from '../src/config.js'; - -/** - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').BidderSpec} BidderSpec - * @typedef {import('../src/adapters/bidderFactory.js').ServerRequest} ServerRequest - * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse - * @typedef {import('../src/adapters/bidderFactory.js').SyncOptions} SyncOptions - * @typedef {import('../src/adapters/bidderFactory.js').UserSync} UserSync - * @typedef {import('../src/auction.js').BidderRequest} BidderRequest - * @typedef {import('../src/mediaTypes.js').MediaType} MediaType - * @typedef {import('../src/utils.js').MediaTypes} MediaTypes - * @typedef {import('../modules/priceFloors.js').getFloor} GetFloor - */ - -/** - * @typedef {Object} CustomServerRequestFields - * @property {BidRequest} bidRequest - */ - -/** - * @typedef {ServerRequest & CustomServerRequestFields} YandexServerRequest - */ - -/** - * Yandex bidder-specific params which the publisher used in their bid request. - * - * @typedef {Object} YandexBidRequestParams - * @property {string} placementId Possible formats: `R-I-123456-2`, `R-123456-1`, `123456-789`. - * @property {number} [pageId] Deprecated. Please use `placementId` instead. - * @property {number} [impId] Deprecated. Please use `placementId` instead. - */ - -/** - * @typedef {Object} AdditionalBidRequestFields - * @property {GetFloor} [getFloor] - * @property {MediaTypes} [mediaTypes] - */ - -/** - * @typedef {BidRequest & AdditionalBidRequestFields} ExtendedBidRequest - */ const BIDDER_CODE = 'yandex'; const BIDDER_URL = 'https://bs.yandex.ru/prebid'; const DEFAULT_TTL = 180; const DEFAULT_CURRENCY = 'EUR'; -/** - * @type {MediaType[]} - */ const SUPPORTED_MEDIA_TYPES = [ BANNER, NATIVE ]; const SSP_ID = 10500; @@ -87,18 +41,11 @@ export const NATIVE_ASSETS = { const NATIVE_ASSETS_IDS = {}; _each(NATIVE_ASSETS, (asset, key) => { NATIVE_ASSETS_IDS[asset[0]] = key }); -/** @type {BidderSpec} */ export const spec = { code: BIDDER_CODE, aliases: ['ya'], // short code supportedMediaTypes: SUPPORTED_MEDIA_TYPES, - /** - * Determines whether or not the given bid request is valid. - * - * @param {BidRequest} bid The bid request to validate. - * @returns {boolean} True if this is a valid bid, and false otherwise. - */ isBidRequestValid: function(bid) { const { params } = bid; if (!params) { @@ -111,13 +58,6 @@ export const spec = { return true; }, - /** - * Make a server request from the list of BidRequests. - * - * @param {ExtendedBidRequest[]} validBidRequests An array of bids. - * @param {BidderRequest} bidderRequest Bidder request object. - * @returns {YandexServerRequest[]} Objects describing the requests to the server. - */ buildRequests: function(validBidRequests, bidderRequest) { validBidRequests = convertOrtbRequestToProprietaryNative(validBidRequests); @@ -136,11 +76,9 @@ export const spec = { timeout = bidderRequest.timeout; } - const adServerCurrency = config.getConfig('currency.adServerCurrency'); - return validBidRequests.map((bidRequest) => { const { params } = bidRequest; - const { targetRef, withCredentials = true, cur } = params; + const { targetRef, withCredentials = true } = params; const { pageId, impId } = extractPlacementIds(params); @@ -169,37 +107,20 @@ export const spec = { imp.bidfloorcur = bidfloor.currency; } - const currency = cur || adServerCurrency; - if (currency) { - queryParams['ssp-cur'] = currency; - } - - const data = { - id: bidRequest.bidId, - imp: [imp], - site: { - ref: referrer, - page, - domain, - }, - tmax: timeout, - }; - - const eids = deepAccess(bidRequest, 'userIdAsEids'); - if (eids && eids.length) { - deepSetValue(data, 'user.ext.eids', eids); - } - - const userData = deepAccess(bidRequest, 'ortb2.user.data'); - if (userData && userData.length) { - deepSetValue(data, 'user.data', userData); - } - const queryParamsString = formatQS(queryParams); return { method: 'POST', url: BIDDER_URL + `/${pageId}?${queryParamsString}`, - data, + data: { + id: bidRequest.bidId, + imp: [imp], + site: { + ref: referrer, + page, + domain, + }, + tmax: timeout, + }, options: { withCredentials, }, @@ -210,24 +131,28 @@ export const spec = { interpretResponse: interpretResponse, - /** - * Register bidder specific code, which will execute if a bid from this bidder won the auction. - * @param {Bid} bid The bid that won the auction. - */ - onBidWon: function(bid) { - const nurl = addRTT(bid['nurl'], bid.timeToRespond); + onBidWon: function (bid) { + let nurl = bid['nurl']; if (!nurl) { return; } - triggerPixel(nurl); + let cpm, currency; + if (bid.hasOwnProperty('originalCurrency') && bid.hasOwnProperty('originalCpm')) { + cpm = bid.originalCpm; + currency = bid.originalCurrency; + } else { + cpm = bid.cpm; + currency = bid.currency; + } + cpm = deepAccess(bid, 'adserverTargeting.hb_pb') || cpm; + + const pixel = replaceAuctionPrice(nurl, cpm, currency); + triggerPixel(pixel); } } -/** - * @param {YandexBidRequestParams} bidRequestParams - */ function extractPlacementIds(bidRequestParams) { const { placementId } = bidRequestParams; const result = { pageId: null, impId: null }; @@ -262,9 +187,6 @@ function extractPlacementIds(bidRequestParams) { return result; } -/** - * @param {ExtendedBidRequest} bidRequest - */ function getBidfloor(bidRequest) { const floors = []; @@ -284,9 +206,6 @@ function getBidfloor(bidRequest) { return floors.sort((a, b) => b.floor - a.floor)[0]; } -/** - * @param {ExtendedBidRequest} bidRequest - */ function mapBanner(bidRequest) { if (deepAccess(bidRequest, 'mediaTypes.banner')) { const sizes = bidRequest.sizes || bidRequest.mediaTypes.banner.sizes; @@ -304,9 +223,6 @@ function mapBanner(bidRequest) { } } -/** - * @param {ExtendedBidRequest} bidRequest - */ function mapNative(bidRequest) { const adUnitNativeAssets = deepAccess(bidRequest, 'mediaTypes.native'); if (adUnitNativeAssets) { @@ -379,13 +295,6 @@ function mapImageAsset(adUnitImageAssetParams, nativeAssetType) { return img; } -/** - * Unpack the response from the server into a list of bids. - * - * @param {ServerResponse} serverResponse A successful response from the server. - * @param {YandexServerRequest} yandexServerRequest - * @return {Bid[]} An array of bids which were nested inside the server. - */ function interpretResponse(serverResponse, { bidRequest }) { let response = serverResponse.body; if (!response.seatbid) { @@ -400,7 +309,6 @@ function interpretResponse(serverResponse, { bidRequest }) { return bidsReceived.map(bidReceived => { const price = bidReceived.price; - /** @type {Bid} */ let prBid = { requestId: bidRequest.bidId, cpm: price, @@ -408,7 +316,7 @@ function interpretResponse(serverResponse, { bidRequest }) { width: bidReceived.w, height: bidReceived.h, creativeId: bidReceived.adid, - nurl: replaceAuctionPrice(bidReceived.nurl, price, currency), + nurl: bidReceived.nurl, netRevenue: true, ttl: DEFAULT_TTL, @@ -473,24 +381,4 @@ function replaceAuctionPrice(url, price, currency) { .replace(/\${AUCTION_CURRENCY}/, currency); } -function addRTT(url, rtt) { - if (!url) return; - - if (url.indexOf(`\${RTT}`) > -1) { - return url.replace(/\${RTT}/, rtt ?? -1); - } - - const urlObj = new URL(url); - - if (Number.isInteger(rtt)) { - urlObj.searchParams.set('rtt', rtt); - } else { - urlObj.searchParams.delete('rtt'); - } - - url = urlObj.toString(); - - return url; -} - registerBidder(spec); diff --git a/modules/yieldlabBidAdapter.js b/modules/yieldlabBidAdapter.js index e4fba5beb62..b0136cd21ea 100644 --- a/modules/yieldlabBidAdapter.js +++ b/modules/yieldlabBidAdapter.js @@ -5,15 +5,6 @@ import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; import { Renderer } from '../src/Renderer.js'; import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse - * @typedef {import('../src/adapters/bidderFactory.js').ServerRequest} ServerRequest - * @typedef {import('../src/adapters/bidderFactory.js').SyncOptions} SyncOptions - * @typedef {import('../src/adapters/bidderFactory.js').UserSync} UserSync - */ - const ENDPOINT = 'https://ad.yieldlab.net'; const BIDDER_CODE = 'yieldlab'; const BID_RESPONSE_TTL_SEC = 300; diff --git a/modules/yieldloveBidAdapter.js b/modules/yieldloveBidAdapter.js deleted file mode 100644 index 4568206b20a..00000000000 --- a/modules/yieldloveBidAdapter.js +++ /dev/null @@ -1,149 +0,0 @@ -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import * as utils from '../src/utils.js'; -import {BANNER} from '../src/mediaTypes.js'; - -const ENDPOINT_URL = 'https://s2s.yieldlove-ad-serving.net/openrtb2/auction'; - -const DEFAULT_BID_TTL = 300; /* 5 minutes */ -const DEFAULT_CURRENCY = 'EUR'; - -const participatedBidders = [] - -export const spec = { - gvlid: 251, - code: 'yieldlove', - aliases: [], - supportedMediaTypes: [BANNER], - - isBidRequestValid: function (bid) { - return !!(bid.params.pid && bid.params.rid) - }, - - buildRequests: function (validBidRequests, bidderRequest) { - const anyValidBidRequest = validBidRequests[0] - - const impressions = validBidRequests.map(bidRequest => { - return { - ext: { - prebid: { - storedrequest: { - id: bidRequest.params.pid?.toString() - } - } - }, - banner: { - format: bidRequest.sizes.map(sizeArr => ({ - w: sizeArr[0], - h: sizeArr[1], - })) - }, - secure: 1, - id: bidRequest.bidId - } - }) - - const s2sRequest = { - device: { - ua: window.navigator.userAgent, - w: window.innerWidth, - h: window.innerHeight, - }, - site: { - ver: '1.9.0', - publisher: { - id: anyValidBidRequest.params.rid - }, - page: window.location.href, - domain: anyValidBidRequest.params.rid - }, - ext: { - prebid: { - targeting: {}, - cache: { - bids: {} - }, - storedrequest: { - id: anyValidBidRequest.params.rid - }, - } - }, - user: { - ext: { - consent: bidderRequest.gdprConsent?.consentString - }, - }, - id: utils.generateUUID(), - imp: impressions, - regs: { - ext: { - gdpr: 1 - } - } - } - - return { - method: 'POST', - url: ENDPOINT_URL, - data: s2sRequest, - options: { - contentType: 'text/plain', - withCredentials: true - }, - }; - }, - - interpretResponse: function (serverResponse) { - const bidResponses = [] - const seatBids = serverResponse.body?.seatbid || [] - seatBids.reduce((bids, cur) => { - if (cur.bid && cur.bid.length > 0) bids = bids.concat(cur.bid) - return bids - }, []).forEach(bid => { - bidResponses.push({ - requestId: bid.impid, - cpm: bid.price, - width: bid.w, - height: bid.h, - ad: bid.adm, - ttl: DEFAULT_BID_TTL, - creativeId: bid.crid, - netRevenue: true, - currency: DEFAULT_CURRENCY - }) - }) - - const bidders = serverResponse.body?.ext.responsetimemillis || {} - Object.keys(bidders).forEach(bidder => { - if (!participatedBidders.includes(bidder)) participatedBidders.push(bidder) - }) - - if (bidResponses.length === 0) { - utils.logInfo('interpretResponse :: no bid'); - } - - return bidResponses; - }, - - getUserSyncs: function (syncOptions, serverResponses, gdprConsent, uspConsent) { - const syncs = [] - - let gdprParams = '' - gdprParams = `gdpr=${Number(gdprConsent?.gdprApplies)}&` - gdprParams += `gdpr_consent=${gdprConsent?.consentString || ''}` - - let bidderParams = '' - if (participatedBidders.length > 0) { - bidderParams = `bidders=${participatedBidders.join(',')}` - } - - syncs.push({ - type: 'iframe', - url: `https://cdn-a.yieldlove.com/load-cookie.html?endpoint=yieldlove&max_sync_count=100&${gdprParams}&${bidderParams}` - }) - - return syncs - }, - -}; - -registerBidder(spec); diff --git a/modules/yieldloveBidAdapter.md b/modules/yieldloveBidAdapter.md deleted file mode 100644 index 8fd71b55f88..00000000000 --- a/modules/yieldloveBidAdapter.md +++ /dev/null @@ -1,38 +0,0 @@ -# Overview - -``` -Module Name: Yieldlove Bid Adapter -Module Type: Bidder Adapter -Maintainer: adapter@yieldlove.com -``` - - -# Description - -Connects to **[Yieldlove](https://www.yieldlove.com/)**s S2S platform for bids. - -```js -const adUnits = [ - { - code: 'test-div', - mediaTypes: { banner: { sizes: [[300, 250]] }}, - bids: [ - { - bidder: 'yieldlove', - params: { - pid: 34437, - rid: 'website.com' - } - } - ] - } -] -``` - - -# Bid Parameters - -| Name | Scope | Description | Example | Type | -|---------------|--------------|---------------------------------------------------------|----------------------------|--------------| -| rid | **required** | Publisher ID on the Yieldlove platform | `website.com` | String | -| pid | **required** | Placement ID on the Yieldlove platform | `34437` | Number | diff --git a/modules/yieldmoBidAdapter.js b/modules/yieldmoBidAdapter.js index 78c2e7430e7..40276aa7a6c 100644 --- a/modules/yieldmoBidAdapter.js +++ b/modules/yieldmoBidAdapter.js @@ -18,13 +18,6 @@ import {registerBidder} from '../src/adapters/bidderFactory.js'; import {Renderer} from '../src/Renderer.js'; import {find, includes} from '../src/polyfill.js'; -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse - * @typedef {import('../src/adapters/bidderFactory.js').ServerRequest} ServerRequest - */ - const BIDDER_CODE = 'yieldmo'; const GVLID = 173; const CURRENCY = 'USD'; @@ -36,7 +29,7 @@ const VIDEO_PATH = '/exchange/prebidvideo'; const STAGE_DOMAIN = 'https://ads-stg.yieldmo.com'; const PROD_DOMAIN = 'https://ads.yieldmo.com'; const OUTSTREAM_VIDEO_PLAYER_URL = 'https://prebid-outstream.yieldmo.com/bundle.js'; -const OPENRTB_VIDEO_BIDPARAMS = ['mimes', 'startdelay', 'placement', 'plcmt', 'skipafter', 'protocols', 'api', +const OPENRTB_VIDEO_BIDPARAMS = ['mimes', 'startdelay', 'placement', 'startdelay', 'skipafter', 'protocols', 'api', 'playbackmethod', 'maxduration', 'minduration', 'pos', 'skip', 'skippable']; const OPENRTB_VIDEO_SITEPARAMS = ['name', 'domain', 'cat', 'keywords']; const LOCAL_WINDOW = getWindowTop(); @@ -85,17 +78,14 @@ export const spec = { bust: new Date().getTime().toString(), dnt: getDNT(), description: getPageDescription(), - tmax: bidderRequest.timeout || 400, userConsent: JSON.stringify({ // case of undefined, stringify will remove param - gdprApplies: - deepAccess(bidderRequest, 'gdprConsent.gdprApplies') || '', + gdprApplies: deepAccess(bidderRequest, 'gdprConsent.gdprApplies') || '', cmp: deepAccess(bidderRequest, 'gdprConsent.consentString') || '', gpp: deepAccess(bidderRequest, 'gppConsent.gppString') || '', - gpp_sid: - deepAccess(bidderRequest, 'gppConsent.applicableSections') || [], + gpp_sid: deepAccess(bidderRequest, 'gppConsent.applicableSections') || [] }), - us_privacy: deepAccess(bidderRequest, 'uspConsent') || '', + us_privacy: deepAccess(bidderRequest, 'uspConsent') || '' }; if (canAccessTopWindow()) { @@ -442,12 +432,12 @@ function openRtbImpression(bidRequest) { } }; - const mediaTypesParams = deepAccess(bidRequest, 'mediaTypes.video', {}); + const mediaTypesParams = deepAccess(bidRequest, 'mediaTypes.video'); Object.keys(mediaTypesParams) .filter(param => includes(OPENRTB_VIDEO_BIDPARAMS, param)) .forEach(param => imp.video[param] = mediaTypesParams[param]); - const videoParams = deepAccess(bidRequest, 'params.video', {}); + const videoParams = deepAccess(bidRequest, 'params.video'); Object.keys(videoParams) .filter(param => includes(OPENRTB_VIDEO_BIDPARAMS, param)) .forEach(param => imp.video[param] = videoParams[param]); @@ -456,7 +446,7 @@ function openRtbImpression(bidRequest) { imp.video.skip = 1; delete imp.video.skippable; } - if (imp.video.plcmt !== 1 || imp.video.placement !== 1) { + if (imp.video.placement !== 1) { imp.video.startdelay = DEFAULT_START_DELAY; imp.video.playbackmethod = [ DEFAULT_PLAYBACK_METHOD ]; } @@ -617,14 +607,8 @@ function validateVideoParams(bid) { } validate('video.protocols', val => isDefined(val), paramRequired); - validate( - 'video.protocols', - (val) => - isArrayOfNums(val) && - val.every((v) => v >= 1 && v <= 12 && v != 9 && v != 10), // 9 and 10 are for DAST which are not supported. - paramInvalid, - 'array of numbers between 1 and 12 except for 9 or 10 , ex: [2,3, 7, 11]' - ); + validate('video.protocols', val => isArrayOfNums(val) && val.every(v => (v >= 1 && v <= 6)), + paramInvalid, 'array of numbers, ex: [2,3]'); validate('video.api', val => isDefined(val), paramRequired); validate('video.api', val => isArrayOfNums(val) && val.every(v => (v >= 1 && v <= 6)), diff --git a/modules/zeotapIdPlusIdSystem.js b/modules/zeotapIdPlusIdSystem.js index 32b7a5b77ad..4cb827cbdff 100644 --- a/modules/zeotapIdPlusIdSystem.js +++ b/modules/zeotapIdPlusIdSystem.js @@ -9,11 +9,6 @@ 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 - * @typedef {import('../modules/userId/index.js').SubmoduleConfig} SubmoduleConfig - */ - const ZEOTAP_COOKIE_NAME = 'IDP'; const ZEOTAP_VENDOR_ID = 301; const ZEOTAP_MODULE_NAME = 'zeotapIdPlus'; @@ -65,12 +60,6 @@ export const zeotapIdPlusSubmodule = { getId() { const id = readCookie() || readFromLocalStorage(); return id ? { id } : undefined; - }, - eids: { - 'IDP': { - source: 'zeotap.com', - atype: 1 - }, } }; submodule('userId', zeotapIdPlusSubmodule); diff --git a/modules/zetaBidAdapter.js b/modules/zetaBidAdapter.js index cc77d53d30d..527030efc9a 100644 --- a/modules/zetaBidAdapter.js +++ b/modules/zetaBidAdapter.js @@ -2,14 +2,6 @@ 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 - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse - * @typedef {import('../src/adapters/bidderFactory.js').SyncOptions} SyncOptions - * @typedef {import('../src/adapters/bidderFactory.js').UserSync} UserSync - */ - const BIDDER_CODE = 'zeta_global'; const PREBID_DEFINER_ID = '44253' const ENDPOINT_URL = 'https://prebid.rfihub.com/prebid'; @@ -23,11 +15,11 @@ export const spec = { supportedMediaTypes: [BANNER], /** - * Determines whether or not the given bid request is valid. - * - * @param {BidRequest} bid The bid params to validate. - * @return boolean True if this is a valid bid, and false otherwise. - */ + * Determines whether or not the given bid request is valid. + * + * @param {BidRequest} bid The bid params to validate. + * @return boolean True if this is a valid bid, and false otherwise. + */ isBidRequestValid: function(bid) { // check for all required bid fields if (!(bid && @@ -58,12 +50,12 @@ export const spec = { }, /** - * Make a server request from the list of BidRequests. - * - * @param {Bids[]} validBidRequests - an array of bidRequest objects - * @param {BidderRequest} bidderRequest - master bidRequest object - * @return ServerRequest Info describing the request to the server. - */ + * Make a server request from the list of BidRequests. + * + * @param {Bids[]} validBidRequests - an array of bidRequest objects + * @param {BidderRequest} bidderRequest - master bidRequest object + * @return ServerRequest Info describing the request to the server. + */ buildRequests: function(validBidRequests, bidderRequest) { const secure = 1; // treat all requests as secure const request = validBidRequests[0]; @@ -125,12 +117,12 @@ export const spec = { }, /** - * Unpack the response from the server into a list of bids. - * - * @param {ServerResponse} serverResponse A successful response from the server. - * @param bidRequest The payload from the server's response. - * @return {Bid[]} An array of bids which were nested inside the server. - */ + * Unpack the response from the server into a list of bids. + * + * @param {ServerResponse} serverResponse A successful response from the server. + * @param bidRequest The payload from the server's response. + * @return {Bid[]} An array of bids which were nested inside the server. + */ interpretResponse: function(serverResponse, bidRequest) { let bidResponse = []; if (Object.keys(serverResponse.body).length !== 0) { diff --git a/modules/zeta_global_sspAnalyticsAdapter.js b/modules/zeta_global_sspAnalyticsAdapter.js index 751b55ec673..ee3bb9cd5d6 100644 --- a/modules/zeta_global_sspAnalyticsAdapter.js +++ b/modules/zeta_global_sspAnalyticsAdapter.js @@ -1,4 +1,4 @@ -import {logInfo, logError} from '../src/utils.js'; +import { logInfo, logError } from '../src/utils.js'; import { ajax } from '../src/ajax.js'; import adapterManager from '../src/adapterManager.js'; import CONSTANTS from '../src/constants.json'; @@ -10,10 +10,6 @@ const ADAPTER_CODE = 'zeta_global_ssp'; const BASE_URL = 'https://ssp.disqus.com/prebid/event'; const LOG_PREFIX = 'ZetaGlobalSsp-Analytics: '; -const cache = { - auctions: {} -}; - /// /////////// VARIABLES //////////////////////////////////// let publisherId; // int @@ -28,87 +24,20 @@ function sendEvent(eventType, event) { ); } -function getZetaParams(event) { - if (event.adUnits) { - for (const i in event.adUnits) { - const unit = event.adUnits[i]; - if (unit.bids) { - for (const j in unit.bids) { - const bid = unit.bids[j]; - if (bid.bidder === ADAPTER_CODE && bid.params) { - return bid.params; - } - } - } - } - } - return null; -} - /// /////////// ADAPTER EVENT HANDLER FUNCTIONS ////////////// function adRenderSucceededHandler(args) { let eventType = CONSTANTS.EVENTS.AD_RENDER_SUCCEEDED logInfo(LOG_PREFIX + 'handle ' + eventType + ' event'); - const event = { - adId: args.adId, - bid: { - adId: args.bid?.adId, - auctionId: args.bid?.auctionId, - adUnitCode: args.bid?.adUnitCode, - bidId: args.bid?.bidId, - requestId: args.bid?.requestId, - bidderCode: args.bid?.bidderCode, - mediaTypes: args.bid?.mediaTypes, - sizes: args.bid?.sizes, - adserverTargeting: args.bid?.adserverTargeting, - cpm: args.bid?.cpm, - creativeId: args.bid?.creativeId, - mediaType: args.bid?.mediaType, - renderer: args.bid?.renderer, - size: args.bid?.size, - timeToRespond: args.bid?.timeToRespond, - params: args.bid?.params - }, - doc: { - location: args.doc?.location - } - } - - // set zetaParams from cache - if (event.bid && event.bid.auctionId) { - const zetaParams = cache.auctions[event.bid.auctionId]; - if (zetaParams) { - event.bid.params = [ zetaParams ]; - } - } - - sendEvent(eventType, event); + sendEvent(eventType, args); } function auctionEndHandler(args) { let eventType = CONSTANTS.EVENTS.AUCTION_END; logInfo(LOG_PREFIX + 'handle ' + eventType + ' event'); - const event = { - adUnitCodes: args.adUnitCodes, - adUnits: args.adUnits, - auctionEnd: args.auctionEnd, - auctionId: args.auctionId, - bidderRequests: args.bidderRequests, - bidsReceived: args.bidsReceived, - noBids: args.noBids, - winningBids: args.winningBids - } - - // save zetaParams to cache - const zetaParams = getZetaParams(event); - if (zetaParams && event.auctionId) { - cache.auctions[event.auctionId] = zetaParams; - } - - sendEvent(eventType, event); + sendEvent(eventType, args); } /// /////////// ADAPTER DEFINITION /////////////////////////// diff --git a/modules/zeta_global_sspBidAdapter.js b/modules/zeta_global_sspBidAdapter.js index 68cf7c4e4cd..687afb6c692 100644 --- a/modules/zeta_global_sspBidAdapter.js +++ b/modules/zeta_global_sspBidAdapter.js @@ -5,12 +5,6 @@ import {config} from '../src/config.js'; import {parseDomain} from '../src/refererDetection.js'; import {ajax} from '../src/ajax.js'; -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - * @typedef {import('../src/adapters/bidderFactory.js').ServerResponse} ServerResponse - */ - const BIDDER_CODE = 'zeta_global_ssp'; const ENDPOINT_URL = 'https://ssp.disqus.com/bid/prebid'; const TIMEOUT_URL = 'https://ssp.disqus.com/timeout/prebid'; @@ -51,7 +45,7 @@ export const spec = { supportedMediaTypes: [BANNER, VIDEO], /** - * Determines whether the given bid request is valid. + * Determines whether or not the given bid request is valid. * * @param {BidRequest} bid The bid params to validate. * @return boolean True if this is a valid bid, and false otherwise. @@ -60,8 +54,7 @@ export const spec = { // check for all required bid fields if (!(bid && bid.bidId && - bid.params && - bid.params.sid)) { + bid.params)) { logWarn('Invalid bid request - missing required bid data'); return false; } @@ -169,8 +162,7 @@ export const spec = { } provideEids(validBidRequests[0], payload); - provideSegments(bidderRequest, payload); - const url = params.sid ? ENDPOINT_URL.concat('?sid=', params.sid) : ENDPOINT_URL; + const url = params.shortname ? ENDPOINT_URL.concat('?shortname=', params.shortname) : ENDPOINT_URL; return { method: 'POST', url: url, @@ -342,25 +334,6 @@ function provideEids(request, payload) { } } -function provideSegments(bidderRequest, payload) { - const data = bidderRequest.ortb2?.user?.data; - if (isArray(data)) { - const segments = data.filter(d => d?.segment).map(d => d.segment).filter(s => isArray(s)).flatMap(s => s).filter(s => s?.id); - if (segments.length > 0) { - if (!payload.user) { - payload.user = {}; - } - if (!isArray(payload.user.data)) { - payload.user.data = []; - } - const payloadData = { - segment: segments - }; - payload.user.data.push(payloadData); - } - } -} - function provideMediaType(zetaBid, bid, bidRequest) { if (zetaBid.ext && zetaBid.ext.prebid && zetaBid.ext.prebid.type) { bid.mediaType = zetaBid.ext.prebid.type === VIDEO ? VIDEO : BANNER; diff --git a/modules/zmaticooBidAdapter.js b/modules/zmaticooBidAdapter.js deleted file mode 100644 index 8566d58c6de..00000000000 --- a/modules/zmaticooBidAdapter.js +++ /dev/null @@ -1,141 +0,0 @@ -import {deepAccess, logWarn} from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER} from '../src/mediaTypes.js'; - -const BIDDER_CODE = 'zmaticoo'; -const ENDPOINT_URL = 'https://bid.zmaticoo.com/prebid/bid'; -const DEFAULT_CUR = 'USD'; -const TTL = 200; -const NET_REV = true; - -export const spec = { - code: BIDDER_CODE, - supportedMediaTypes: [BANNER], - - /** - * Determines whether or not the given bid request is valid. - * - * @param {BidRequest} bid The bid params to validate. - * @return boolean True if this is a valid bid, and false otherwise. - */ - isBidRequestValid: function (bid) { - // check for all required bid fields - if (!(bid && bid.bidId && bid.params)) { - logWarn('Invalid bid request - missing required bid data'); - return false; - } - - if (!(bid.params.pubId)) { - logWarn('Invalid bid request - missing required field pubId'); - return false; - } - - if (!(bid.params.device && bid.params.device.ip)) { - logWarn('Invalid bid request - missing required device data'); - return false; - } - return true; - }, - - /** - * Make a server request from the list of BidRequests. - * - * @param {Bids[]} validBidRequests - an array of bidRequest objects - * @param {BidderRequest} bidderRequest - master bidRequest object - * @return ServerRequest Info describing the request to the server. - */ - buildRequests: function (validBidRequests, bidderRequest) { - const secure = 1; - const request = validBidRequests[0]; - const params = request.params; - let impData = { - id: request.bidId, - secure: secure, - banner: buildBanner(request), - ext: { - bidder: { - pubId: params.pubId - } - } - }; - let payload = { - id: bidderRequest.bidderRequestId, - imp: [impData], - site: params.site ? params.site : {}, - app: params.app ? params.app : {}, - device: params.device ? params.device : {}, - user: params.user ? params.user : {}, - at: params.at, - tmax: params.tmax, - wseat: params.wseat, - bseat: params.bseat, - allimps: params.allimps, - cur: [DEFAULT_CUR], - wlang: params.wlang, - bcat: deepAccess(bidderRequest.ortb2Imp, 'bcat') || params.bcat, - badv: params.badv, - bapp: params.bapp, - source: params.source ? params.source : {}, - regs: params.regs ? params.regs : {}, - ext: params.ext ? params.ext : {} - }; - - payload.device.ua = navigator.userAgent; - payload.device.ip = navigator.ip; - payload.site.page = bidderRequest.refererInfo.page; - payload.site.mobile = /(ios|ipod|ipad|iphone|android)/i.test(navigator.userAgent) ? 1 : 0; - if (params.test) { - payload.test = params.test; - } - if (request.gdprConsent) { - payload.regs.ext = Object.assign(payload.regs.ext, {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}); - } - const postUrl = ENDPOINT_URL; - return { - method: 'POST', url: postUrl, data: JSON.stringify(payload), - }; - }, - - /** - * Unpack the response from the server into a list of bids. - * - * @param {ServerResponse} serverResponse A successful response from the server. - * @param bidRequest The payload from the server's response. - * @return {Bid[]} An array of bids which were nested inside the server. - */ - interpretResponse: function (serverResponse, bidRequest) { - let bidResponse = []; - if (Object.keys(serverResponse.body).length !== 0) { - let zresponse = serverResponse.body; - let zbid = zresponse.seatbid[0].bid[0]; - let bid = { - requestId: zbid.impid, - cpm: zbid.price, - currency: zbid.cur, - width: zbid.w, - height: zbid.h, - ad: zbid.adm, - ttl: TTL, - creativeId: zbid.crid, - netRevenue: NET_REV - }; - bidResponse.push(bid); - } - return bidResponse; - } -} - -function buildBanner(request) { - let sizes = request.sizes; - if (request.mediaTypes && request.mediaTypes.banner && request.mediaTypes.banner.sizes) { - sizes = request.mediaTypes.banner.sizes; - } - return { - w: sizes[0][0], h: sizes[0][1] - }; -} - -registerBidder(spec); diff --git a/modules/zmaticooBidAdapter.md b/modules/zmaticooBidAdapter.md deleted file mode 100644 index 95959ef218c..00000000000 --- a/modules/zmaticooBidAdapter.md +++ /dev/null @@ -1,45 +0,0 @@ -# Overview - -``` -Module Name: zMaticoo Bidder Adapter -Module Type: Bidder Adapter -Maintainer: adam.li@eclicktech.com.cn -``` - -# Description - -zMaticoo Bidder Adapter for Prebid.js. - -# Test Parameters - -``` - var adUnits = [ - { - mediaTypes: { - banner: { - sizes: [[320, 50]], // a display size - } - }, - bids: [ - { - bidder: 'zmaticoo', - bidId: '12345', - params: { - user: { - uid: '12345', - buyeruid: '12345' - }, - device: { - ip: '111.222.33.44', - geo: { - country: 'USA' - } - }, - pubId: 'prebid-fgh', - test: 1 - } - } - ] - } - ]; -``` diff --git a/package-lock.json b/package-lock.json index 87110627d26..af7eda1ea6b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "prebid.js", - "version": "8.34.0-pre", + "version": "8.1.0-pre", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "prebid.js", - "version": "8.32.0-pre", + "version": "7.51.0-pre", "license": "Apache-2.0", "dependencies": { "@babel/core": "^7.16.7", @@ -16,13 +16,13 @@ "core-js": "^3.13.0", "core-js-pure": "^3.13.0", "criteo-direct-rsa-validate": "^1.1.0", - "crypto-js": "^4.2.0", + "crypto-js": "^3.3.0", "dlv": "1.1.3", "dset": "3.1.2", "express": "^4.15.4", "fun-hooks": "^0.9.9", "just-clone": "^1.0.2", - "live-connect-js": "^6.3.4" + "live-connect-js": "^5.0.0" }, "devDependencies": { "@babel/eslint-parser": "^7.16.5", @@ -47,7 +47,6 @@ "eslint": "^7.27.0", "eslint-config-standard": "^10.2.1", "eslint-plugin-import": "^2.20.2", - "eslint-plugin-jsdoc": "^38.1.6", "eslint-plugin-node": "^11.1.0", "eslint-plugin-prebid": "file:./plugins/eslint", "eslint-plugin-promise": "^5.1.0", @@ -91,7 +90,6 @@ "lodash": "^4.17.21", "mocha": "^10.0.0", "morgan": "^1.10.0", - "node-html-parser": "^6.1.5", "opn": "^5.4.0", "resolve-from": "^5.0.0", "sinon": "^4.1.3", @@ -129,12 +127,11 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.22.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", - "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", + "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", "dependencies": { - "@babel/highlight": "^7.22.13", - "chalk": "^2.4.2" + "@babel/highlight": "^7.18.6" }, "engines": { "node": ">=6.9.0" @@ -196,13 +193,12 @@ } }, "node_modules/@babel/generator": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", - "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.1.tgz", + "integrity": "sha512-u1dMdBUmA7Z0rBB97xh8pIhviK7oItYOkjbsCxTWMknyvbQRBwX7/gn4JXurRdirWMFh+ZtYARqkA6ydogVZpg==", "dependencies": { - "@babel/types": "^7.23.0", + "@babel/types": "^7.20.0", "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" }, "engines": { @@ -314,9 +310,9 @@ } }, "node_modules/@babel/helper-environment-visitor": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", - "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", + "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", "engines": { "node": ">=6.9.0" } @@ -333,23 +329,23 @@ } }, "node_modules/@babel/helper-function-name": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", - "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz", + "integrity": "sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==", "dependencies": { - "@babel/template": "^7.22.15", - "@babel/types": "^7.23.0" + "@babel/template": "^7.18.10", + "@babel/types": "^7.19.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-hoist-variables": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", - "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", + "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", "dependencies": { - "@babel/types": "^7.22.5" + "@babel/types": "^7.18.6" }, "engines": { "node": ">=6.9.0" @@ -469,28 +465,28 @@ } }, "node_modules/@babel/helper-split-export-declaration": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", - "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", + "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", "dependencies": { - "@babel/types": "^7.22.5" + "@babel/types": "^7.18.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-string-parser": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", - "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", + "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", - "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", "engines": { "node": ">=6.9.0" } @@ -531,12 +527,12 @@ } }, "node_modules/@babel/highlight": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", - "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", + "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", "dependencies": { - "@babel/helper-validator-identifier": "^7.22.20", - "chalk": "^2.4.2", + "@babel/helper-validator-identifier": "^7.18.6", + "chalk": "^2.0.0", "js-tokens": "^4.0.0" }, "engines": { @@ -544,9 +540,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", - "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.1.tgz", + "integrity": "sha512-hp0AYxaZJhxULfM1zyp7Wgr+pSUKBcP3M+PHnSzWGdXOzg/kHWIgiUWARvubhUKGOEw3xqY4x+lyZ9ytBVcELw==", "bin": { "parser": "bin/babel-parser.js" }, @@ -1616,31 +1612,31 @@ } }, "node_modules/@babel/template": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", - "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", + "integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==", "dependencies": { - "@babel/code-frame": "^7.22.13", - "@babel/parser": "^7.22.15", - "@babel/types": "^7.22.15" + "@babel/code-frame": "^7.18.6", + "@babel/parser": "^7.18.10", + "@babel/types": "^7.18.10" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", - "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", - "dependencies": { - "@babel/code-frame": "^7.22.13", - "@babel/generator": "^7.23.0", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.23.0", - "@babel/types": "^7.23.0", + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.1.tgz", + "integrity": "sha512-d3tN8fkVJwFLkHkBN479SOsw4DMZnz8cdbL/gvuDuzy3TS6Nfw80HuQqhw1pITbIruHyh7d1fMA47kWzmcUEGA==", + "dependencies": { + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.20.1", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/parser": "^7.20.1", + "@babel/types": "^7.20.0", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -1649,12 +1645,12 @@ } }, "node_modules/@babel/types": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", - "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.0.tgz", + "integrity": "sha512-Jlgt3H0TajCW164wkTOTzHkZb075tMQMULzrLUoUeKmO7eFL96GgDxf7/Axhc5CAuKE3KFyVW1p6ysKsi2oXAg==", "dependencies": { - "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.20", + "@babel/helper-string-parser": "^7.19.4", + "@babel/helper-validator-identifier": "^7.19.1", "to-fast-properties": "^2.0.0" }, "engines": { @@ -1670,20 +1666,6 @@ "node": ">=0.1.90" } }, - "node_modules/@es-joy/jsdoccomment": { - "version": "0.22.2", - "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.22.2.tgz", - "integrity": "sha512-pM6WQKcuAtdYoqCsXSvVSu3Ij8K0HY50L8tIheOKHDl0wH1uA4zbP88etY8SIeP16NVCMCTFU+Q2DahSKheGGQ==", - "dev": true, - "dependencies": { - "comment-parser": "1.3.1", - "esquery": "^1.4.0", - "jsdoc-type-pratt-parser": "~2.2.5" - }, - "engines": { - "node": "^12 || ^14 || ^16 || ^17" - } - }, "node_modules/@eslint/eslintrc": { "version": "0.4.3", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", @@ -6507,12 +6489,6 @@ "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==", "dev": true }, - "node_modules/boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", - "dev": true - }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -6871,9 +6847,9 @@ } }, "node_modules/cac/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", "dev": true, "bin": { "semver": "bin/semver" @@ -7504,15 +7480,6 @@ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "dev": true }, - "node_modules/comment-parser": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.3.1.tgz", - "integrity": "sha512-B52sN2VNghyq5ofvUsqZjmk6YkihBX5vMSChmSK9v4ShjKf3Vk5Xcmgpw4o+iIgtrnM/u5FiMpz9VKb8lpBveA==", - "dev": true, - "engines": { - "node": ">= 12.0.0" - } - }, "node_modules/commondir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", @@ -7871,9 +7838,9 @@ } }, "node_modules/crypto-js": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", - "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==" + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-3.3.0.tgz", + "integrity": "sha512-DIT51nX0dCfKltpRiXV+/TVZq+Qq2NgF4644+K7Ttnla7zEzqc+kjJyiB96BHNyUTBxyjzRcZYpUdZa+QAqi6Q==" }, "node_modules/css": { "version": "3.0.0", @@ -7886,22 +7853,6 @@ "source-map-resolve": "^0.6.0" } }, - "node_modules/css-select": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", - "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", - "dev": true, - "dependencies": { - "boolbase": "^1.0.0", - "css-what": "^6.1.0", - "domhandler": "^5.0.2", - "domutils": "^3.0.1", - "nth-check": "^2.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, "node_modules/css-shorthand-properties": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/css-shorthand-properties/-/css-shorthand-properties-1.1.1.tgz", @@ -7914,18 +7865,6 @@ "integrity": "sha512-FUV3xaJ63buRLgHrLQVlVgQnQdR4yqdLGaDu7g8CQcWjInDfM9plBTPI9FRfpahju1UBSaMckeb2/46ApS/V1Q==", "dev": true }, - "node_modules/css-what": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", - "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", - "dev": true, - "engines": { - "node": ">= 6" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, "node_modules/css/node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -8752,67 +8691,12 @@ "void-elements": "^2.0.0" } }, - "node_modules/dom-serializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", - "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", - "dev": true, - "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.2", - "entities": "^4.2.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" - } - }, "node_modules/dom-walk": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.2.tgz", "integrity": "sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==", "dev": true }, - "node_modules/domelementtype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", - "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ] - }, - "node_modules/domhandler": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", - "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", - "dev": true, - "dependencies": { - "domelementtype": "^2.3.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/domutils": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", - "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", - "dev": true, - "dependencies": { - "dom-serializer": "^2.0.0", - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } - }, "node_modules/dset": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/dset/-/dset-3.1.2.tgz", @@ -9073,18 +8957,6 @@ "integrity": "sha512-GHrMyVZQWvTIdDtpiEXdHZnFQKzeO09apj8Cbl4pKWy4i0Oprcq17usfDt5aO63swf0JOeMWjWQE/LzgSRuWpA==", "dev": true }, - "node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "dev": true, - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, "node_modules/errno": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", @@ -9586,55 +9458,6 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true }, - "node_modules/eslint-plugin-jsdoc": { - "version": "38.1.6", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-38.1.6.tgz", - "integrity": "sha512-n4s95oYlg0L43Bs8C0dkzIldxYf8pLCutC/tCbjIdF7VDiobuzPI+HZn9Q0BvgOvgPNgh5n7CSStql25HUG4Tw==", - "dev": true, - "dependencies": { - "@es-joy/jsdoccomment": "~0.22.1", - "comment-parser": "1.3.1", - "debug": "^4.3.4", - "escape-string-regexp": "^4.0.0", - "esquery": "^1.4.0", - "regextras": "^0.8.0", - "semver": "^7.3.5", - "spdx-expression-parse": "^3.0.1" - }, - "engines": { - "node": "^12 || ^14 || ^16 || ^17" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" - } - }, - "node_modules/eslint-plugin-jsdoc/node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint-plugin-jsdoc/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/eslint-plugin-node": { "version": "11.1.0", "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz", @@ -9830,9 +9653,9 @@ } }, "node_modules/eslint/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -10080,9 +9903,9 @@ } }, "node_modules/execa/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", "dev": true, "bin": { "semver": "bin/semver" @@ -11009,9 +10832,9 @@ } }, "node_modules/follow-redirects": { - "version": "1.15.4", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz", - "integrity": "sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==", + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", "dev": true, "funding": [ { @@ -12267,9 +12090,9 @@ } }, "node_modules/gulp-cli/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", "dev": true, "bin": { "semver": "bin/semver" @@ -12609,9 +12432,9 @@ } }, "node_modules/gulp-eslint/node_modules/cross-spawn/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", "dev": true, "bin": { "semver": "bin/semver" @@ -15683,15 +15506,6 @@ "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", "dev": true }, - "node_modules/jsdoc-type-pratt-parser": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-2.2.5.tgz", - "integrity": "sha512-2a6eRxSxp1BW040hFvaJxhsCMI9lT8QB8t14t+NY5tC5rckIR0U9cr2tjOeaFirmEOy6MHvmJnY7zTBHq431Lw==", - "dev": true, - "engines": { - "node": ">=12.0.0" - } - }, "node_modules/jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", @@ -15982,9 +15796,9 @@ } }, "node_modules/karma-coverage-istanbul-reporter/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", "dev": true, "bin": { "semver": "bin/semver" @@ -16418,23 +16232,23 @@ "dev": true }, "node_modules/live-connect-common": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/live-connect-common/-/live-connect-common-3.0.3.tgz", - "integrity": "sha512-ZPycT04ROBUvPiksnLTunrKC3ROhBSeO99fQ+4qMIkgKwP2CvS44L7fK+0WFV4nAi+65KbzSng7JWcSlckfw8w==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/live-connect-common/-/live-connect-common-1.0.0.tgz", + "integrity": "sha512-LBZsvykcGeVRYI1eqqXrrNZsoBdL2a8cpyrYPIiGAF/CpixbyRbvqGslaFw511lH294QB16J3fYYg21aYuaM2Q==", "engines": { - "node": ">=18" + "node": ">=8" } }, "node_modules/live-connect-js": { - "version": "6.3.4", - "resolved": "https://registry.npmjs.org/live-connect-js/-/live-connect-js-6.3.4.tgz", - "integrity": "sha512-lg2XeCaj/eEbK66QGGDEdz9IdT/K3ExZ83Qo6xGVLdP5XJ33xAUCk/gds34rRTmpIwUfAnboOpyj3UoYtS3QUQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/live-connect-js/-/live-connect-js-5.0.0.tgz", + "integrity": "sha512-Bv0wQQ+/1VU0/YczEpObbWtHbuXwaHGxwg1+Pe7ZlDgBLb334CrqSQvOL1uyZw3//zs+fSO94yYaQzjjkTd5OQ==", "dependencies": { - "live-connect-common": "^v3.0.3", + "live-connect-common": "^1.0.0", "tiny-hashes": "1.0.1" }, "engines": { - "node": ">=18" + "node": ">=8" } }, "node_modules/livereload-js": { @@ -18892,16 +18706,6 @@ } } }, - "node_modules/node-html-parser": { - "version": "6.1.6", - "resolved": "https://registry.npmjs.org/node-html-parser/-/node-html-parser-6.1.6.tgz", - "integrity": "sha512-C/MGDQ2NjdjzUq41bW9kW00MPZecAe/oo89vZEFLDfWoQVDk/DdML1yuxVVKLDMFIFax2VTq6Vpfzyn7z5yYgQ==", - "dev": true, - "dependencies": { - "css-select": "^5.1.0", - "he": "1.2.0" - } - }, "node_modules/node-releases": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", @@ -18935,9 +18739,9 @@ } }, "node_modules/normalize-package-data/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -19003,18 +18807,6 @@ "node": ">=4" } }, - "node_modules/nth-check": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", - "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", - "dev": true, - "dependencies": { - "boolbase": "^1.0.0" - }, - "funding": { - "url": "https://github.com/fb55/nth-check?sponsor=1" - } - }, "node_modules/number-is-nan": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", @@ -20649,15 +20441,6 @@ "node": ">=4" } }, - "node_modules/regextras": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/regextras/-/regextras-0.8.0.tgz", - "integrity": "sha512-k519uI04Z3SaY0fLX843MRXnDeG2+vHOFsyhiPZvNLe7r8rD2YNRjq4BQLZZ0oAr2NrtvZlICsXysGNFPGa3CQ==", - "dev": true, - "engines": { - "node": ">=0.1.14" - } - }, "node_modules/regjsgen": { "version": "0.7.1", "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.7.1.tgz", @@ -21251,9 +21034,9 @@ } }, "node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "bin": { "semver": "bin/semver.js" } @@ -25121,9 +24904,9 @@ } }, "node_modules/word-wrap": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.4.tgz", - "integrity": "sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", "dev": true, "engines": { "node": ">=0.10.0" @@ -25506,12 +25289,11 @@ } }, "@babel/code-frame": { - "version": "7.22.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", - "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", + "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", "requires": { - "@babel/highlight": "^7.22.13", - "chalk": "^2.4.2" + "@babel/highlight": "^7.18.6" } }, "@babel/compat-data": { @@ -25553,13 +25335,12 @@ } }, "@babel/generator": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", - "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.1.tgz", + "integrity": "sha512-u1dMdBUmA7Z0rBB97xh8pIhviK7oItYOkjbsCxTWMknyvbQRBwX7/gn4JXurRdirWMFh+ZtYARqkA6ydogVZpg==", "requires": { - "@babel/types": "^7.23.0", + "@babel/types": "^7.20.0", "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" }, "dependencies": { @@ -25640,9 +25421,9 @@ } }, "@babel/helper-environment-visitor": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", - "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==" + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", + "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==" }, "@babel/helper-explode-assignable-expression": { "version": "7.18.6", @@ -25653,20 +25434,20 @@ } }, "@babel/helper-function-name": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", - "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz", + "integrity": "sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==", "requires": { - "@babel/template": "^7.22.15", - "@babel/types": "^7.23.0" + "@babel/template": "^7.18.10", + "@babel/types": "^7.19.0" } }, "@babel/helper-hoist-variables": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", - "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", + "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", "requires": { - "@babel/types": "^7.22.5" + "@babel/types": "^7.18.6" } }, "@babel/helper-member-expression-to-functions": { @@ -25753,22 +25534,22 @@ } }, "@babel/helper-split-export-declaration": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", - "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", + "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", "requires": { - "@babel/types": "^7.22.5" + "@babel/types": "^7.18.6" } }, "@babel/helper-string-parser": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", - "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==" + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", + "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==" }, "@babel/helper-validator-identifier": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", - "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==" + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==" }, "@babel/helper-validator-option": { "version": "7.18.6", @@ -25797,19 +25578,19 @@ } }, "@babel/highlight": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", - "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", + "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", "requires": { - "@babel/helper-validator-identifier": "^7.22.20", - "chalk": "^2.4.2", + "@babel/helper-validator-identifier": "^7.18.6", + "chalk": "^2.0.0", "js-tokens": "^4.0.0" } }, "@babel/parser": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", - "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==" + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.1.tgz", + "integrity": "sha512-hp0AYxaZJhxULfM1zyp7Wgr+pSUKBcP3M+PHnSzWGdXOzg/kHWIgiUWARvubhUKGOEw3xqY4x+lyZ9ytBVcELw==" }, "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { "version": "7.18.6", @@ -26501,39 +26282,39 @@ } }, "@babel/template": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", - "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", + "integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==", "requires": { - "@babel/code-frame": "^7.22.13", - "@babel/parser": "^7.22.15", - "@babel/types": "^7.22.15" + "@babel/code-frame": "^7.18.6", + "@babel/parser": "^7.18.10", + "@babel/types": "^7.18.10" } }, "@babel/traverse": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", - "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", - "requires": { - "@babel/code-frame": "^7.22.13", - "@babel/generator": "^7.23.0", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.23.0", - "@babel/types": "^7.23.0", + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.1.tgz", + "integrity": "sha512-d3tN8fkVJwFLkHkBN479SOsw4DMZnz8cdbL/gvuDuzy3TS6Nfw80HuQqhw1pITbIruHyh7d1fMA47kWzmcUEGA==", + "requires": { + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.20.1", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/parser": "^7.20.1", + "@babel/types": "^7.20.0", "debug": "^4.1.0", "globals": "^11.1.0" } }, "@babel/types": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", - "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.0.tgz", + "integrity": "sha512-Jlgt3H0TajCW164wkTOTzHkZb075tMQMULzrLUoUeKmO7eFL96GgDxf7/Axhc5CAuKE3KFyVW1p6ysKsi2oXAg==", "requires": { - "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.20", + "@babel/helper-string-parser": "^7.19.4", + "@babel/helper-validator-identifier": "^7.19.1", "to-fast-properties": "^2.0.0" } }, @@ -26543,17 +26324,6 @@ "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", "dev": true }, - "@es-joy/jsdoccomment": { - "version": "0.22.2", - "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.22.2.tgz", - "integrity": "sha512-pM6WQKcuAtdYoqCsXSvVSu3Ij8K0HY50L8tIheOKHDl0wH1uA4zbP88etY8SIeP16NVCMCTFU+Q2DahSKheGGQ==", - "dev": true, - "requires": { - "comment-parser": "1.3.1", - "esquery": "^1.4.0", - "jsdoc-type-pratt-parser": "~2.2.5" - } - }, "@eslint/eslintrc": { "version": "0.4.3", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", @@ -30473,12 +30243,6 @@ } } }, - "boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", - "dev": true - }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -30751,9 +30515,9 @@ } }, "semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", "dev": true }, "strip-ansi": { @@ -31225,12 +30989,6 @@ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "dev": true }, - "comment-parser": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.3.1.tgz", - "integrity": "sha512-B52sN2VNghyq5ofvUsqZjmk6YkihBX5vMSChmSK9v4ShjKf3Vk5Xcmgpw4o+iIgtrnM/u5FiMpz9VKb8lpBveA==", - "dev": true - }, "commondir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", @@ -31520,9 +31278,9 @@ } }, "crypto-js": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", - "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==" + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-3.3.0.tgz", + "integrity": "sha512-DIT51nX0dCfKltpRiXV+/TVZq+Qq2NgF4644+K7Ttnla7zEzqc+kjJyiB96BHNyUTBxyjzRcZYpUdZa+QAqi6Q==" }, "css": { "version": "3.0.0", @@ -31543,19 +31301,6 @@ } } }, - "css-select": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", - "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", - "dev": true, - "requires": { - "boolbase": "^1.0.0", - "css-what": "^6.1.0", - "domhandler": "^5.0.2", - "domutils": "^3.0.1", - "nth-check": "^2.0.1" - } - }, "css-shorthand-properties": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/css-shorthand-properties/-/css-shorthand-properties-1.1.1.tgz", @@ -31568,12 +31313,6 @@ "integrity": "sha512-FUV3xaJ63buRLgHrLQVlVgQnQdR4yqdLGaDu7g8CQcWjInDfM9plBTPI9FRfpahju1UBSaMckeb2/46ApS/V1Q==", "dev": true }, - "css-what": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", - "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", - "dev": true - }, "custom-event": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz", @@ -32185,49 +31924,12 @@ "void-elements": "^2.0.0" } }, - "dom-serializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", - "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", - "dev": true, - "requires": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.2", - "entities": "^4.2.0" - } - }, "dom-walk": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.2.tgz", "integrity": "sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==", "dev": true }, - "domelementtype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", - "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", - "dev": true - }, - "domhandler": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", - "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", - "dev": true, - "requires": { - "domelementtype": "^2.3.0" - } - }, - "domutils": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", - "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", - "dev": true, - "requires": { - "dom-serializer": "^2.0.0", - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3" - } - }, "dset": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/dset/-/dset-3.1.2.tgz", @@ -32458,12 +32160,6 @@ "integrity": "sha512-GHrMyVZQWvTIdDtpiEXdHZnFQKzeO09apj8Cbl4pKWy4i0Oprcq17usfDt5aO63swf0JOeMWjWQE/LzgSRuWpA==", "dev": true }, - "entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "dev": true - }, "errno": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", @@ -32834,9 +32530,9 @@ "dev": true }, "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -32970,39 +32666,6 @@ } } }, - "eslint-plugin-jsdoc": { - "version": "38.1.6", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-38.1.6.tgz", - "integrity": "sha512-n4s95oYlg0L43Bs8C0dkzIldxYf8pLCutC/tCbjIdF7VDiobuzPI+HZn9Q0BvgOvgPNgh5n7CSStql25HUG4Tw==", - "dev": true, - "requires": { - "@es-joy/jsdoccomment": "~0.22.1", - "comment-parser": "1.3.1", - "debug": "^4.3.4", - "escape-string-regexp": "^4.0.0", - "esquery": "^1.4.0", - "regextras": "^0.8.0", - "semver": "^7.3.5", - "spdx-expression-parse": "^3.0.1" - }, - "dependencies": { - "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true - }, - "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - } - } - }, "eslint-plugin-node": { "version": "11.1.0", "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz", @@ -33237,9 +32900,9 @@ "dev": true }, "semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", "dev": true }, "shebang-command": { @@ -34011,9 +33674,9 @@ } }, "follow-redirects": { - "version": "1.15.4", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz", - "integrity": "sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==", + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", "dev": true }, "for-each": { @@ -35018,9 +34681,9 @@ } }, "semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", "dev": true }, "string-width": { @@ -35306,9 +34969,9 @@ }, "dependencies": { "semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", "dev": true } } @@ -37628,12 +37291,6 @@ "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", "dev": true }, - "jsdoc-type-pratt-parser": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-2.2.5.tgz", - "integrity": "sha512-2a6eRxSxp1BW040hFvaJxhsCMI9lT8QB8t14t+NY5tC5rckIR0U9cr2tjOeaFirmEOy6MHvmJnY7zTBHq431Lw==", - "dev": true - }, "jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", @@ -37928,9 +37585,9 @@ } }, "semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", "dev": true }, "source-map": { @@ -38226,16 +37883,16 @@ "dev": true }, "live-connect-common": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/live-connect-common/-/live-connect-common-3.0.3.tgz", - "integrity": "sha512-ZPycT04ROBUvPiksnLTunrKC3ROhBSeO99fQ+4qMIkgKwP2CvS44L7fK+0WFV4nAi+65KbzSng7JWcSlckfw8w==" + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/live-connect-common/-/live-connect-common-1.0.0.tgz", + "integrity": "sha512-LBZsvykcGeVRYI1eqqXrrNZsoBdL2a8cpyrYPIiGAF/CpixbyRbvqGslaFw511lH294QB16J3fYYg21aYuaM2Q==" }, "live-connect-js": { - "version": "6.3.4", - "resolved": "https://registry.npmjs.org/live-connect-js/-/live-connect-js-6.3.4.tgz", - "integrity": "sha512-lg2XeCaj/eEbK66QGGDEdz9IdT/K3ExZ83Qo6xGVLdP5XJ33xAUCk/gds34rRTmpIwUfAnboOpyj3UoYtS3QUQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/live-connect-js/-/live-connect-js-5.0.0.tgz", + "integrity": "sha512-Bv0wQQ+/1VU0/YczEpObbWtHbuXwaHGxwg1+Pe7ZlDgBLb334CrqSQvOL1uyZw3//zs+fSO94yYaQzjjkTd5OQ==", "requires": { - "live-connect-common": "^v3.0.3", + "live-connect-common": "^1.0.0", "tiny-hashes": "1.0.1" } }, @@ -40077,16 +39734,6 @@ "whatwg-url": "^5.0.0" } }, - "node-html-parser": { - "version": "6.1.6", - "resolved": "https://registry.npmjs.org/node-html-parser/-/node-html-parser-6.1.6.tgz", - "integrity": "sha512-C/MGDQ2NjdjzUq41bW9kW00MPZecAe/oo89vZEFLDfWoQVDk/DdML1yuxVVKLDMFIFax2VTq6Vpfzyn7z5yYgQ==", - "dev": true, - "requires": { - "css-select": "^5.1.0", - "he": "1.2.0" - } - }, "node-releases": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", @@ -40114,9 +39761,9 @@ }, "dependencies": { "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -40162,15 +39809,6 @@ } } }, - "nth-check": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", - "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", - "dev": true, - "requires": { - "boolbase": "^1.0.0" - } - }, "number-is-nan": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", @@ -41389,12 +41027,6 @@ "unicode-match-property-value-ecmascript": "^2.0.0" } }, - "regextras": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/regextras/-/regextras-0.8.0.tgz", - "integrity": "sha512-k519uI04Z3SaY0fLX843MRXnDeG2+vHOFsyhiPZvNLe7r8rD2YNRjq4BQLZZ0oAr2NrtvZlICsXysGNFPGa3CQ==", - "dev": true - }, "regjsgen": { "version": "0.7.1", "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.7.1.tgz", @@ -41863,9 +41495,9 @@ } }, "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" }, "semver-greatest-satisfied-range": { "version": "1.1.0", @@ -44926,9 +44558,9 @@ } }, "word-wrap": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.4.tgz", - "integrity": "sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", "dev": true }, "wordwrap": { diff --git a/package.json b/package.json index 38697197d0b..4f0f512bd8b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "8.34.0-pre", + "version": "8.1.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { @@ -58,7 +58,6 @@ "eslint": "^7.27.0", "eslint-config-standard": "^10.2.1", "eslint-plugin-import": "^2.20.2", - "eslint-plugin-jsdoc": "^38.1.6", "eslint-plugin-node": "^11.1.0", "eslint-plugin-prebid": "file:./plugins/eslint", "eslint-plugin-promise": "^5.1.0", @@ -102,7 +101,6 @@ "lodash": "^4.17.21", "mocha": "^10.0.0", "morgan": "^1.10.0", - "node-html-parser": "^6.1.5", "opn": "^5.4.0", "resolve-from": "^5.0.0", "sinon": "^4.1.3", @@ -128,13 +126,13 @@ "core-js": "^3.13.0", "core-js-pure": "^3.13.0", "criteo-direct-rsa-validate": "^1.1.0", - "crypto-js": "^4.2.0", + "crypto-js": "^3.3.0", "dlv": "1.1.3", "dset": "3.1.2", "express": "^4.15.4", "fun-hooks": "^0.9.9", "just-clone": "^1.0.2", - "live-connect-js": "^6.3.4" + "live-connect-js": "^5.0.0" }, "optionalDependencies": { "fsevents": "^2.3.2" diff --git a/plugins/pbjsGlobals.js b/plugins/pbjsGlobals.js index 62d29b567ed..6d1eeb0c57d 100644 --- a/plugins/pbjsGlobals.js +++ b/plugins/pbjsGlobals.js @@ -34,8 +34,7 @@ module.exports = function(api, options) { '$$PREBID_GLOBAL$$': pbGlobal, '$$DEFINE_PREBID_GLOBAL$$': defineGlobal, '$$REPO_AND_VERSION$$': `${prebid.repository.url.split('/')[3]}_prebid_${prebid.version}`, - '$$PREBID_DIST_URL_BASE$$': options.prebidDistUrlBase || `https://cdn.jsdelivr.net/npm/prebid.js@${getNpmVersion(prebid.version)}/dist/`, - '$$LIVE_INTENT_MODULE_MODE$$': (process && process.env && process.env.LiveConnectMode) || 'standard' + '$$PREBID_DIST_URL_BASE$$': options.prebidDistUrlBase || `https://cdn.jsdelivr.net/npm/prebid.js@${getNpmVersion(prebid.version)}/dist/` }; let identifierToStringLiteral = [ diff --git a/src/activities/params.js b/src/activities/params.js index 036a6657cf8..ff181bb55a4 100644 --- a/src/activities/params.js +++ b/src/activities/params.js @@ -1,5 +1,4 @@ import {MODULE_TYPE_BIDDER} from './modules.js'; -import {hook} from '../hook.js'; /** * Component ID - who is trying to perform the activity? @@ -55,8 +54,6 @@ export function activityParamsBuilder(resolveAlias) { if (moduleType === MODULE_TYPE_BIDDER) { defaults[ACTIVITY_PARAM_ADAPTER_CODE] = resolveAlias(moduleName); } - return buildActivityParams(Object.assign(defaults, params)); + return Object.assign(defaults, params); } } - -export const buildActivityParams = hook('sync', params => params); diff --git a/src/activities/redactor.js b/src/activities/redactor.js index 694a96b2b14..d50df72648c 100644 --- a/src/activities/redactor.js +++ b/src/activities/redactor.js @@ -8,17 +8,7 @@ import { ACTIVITY_TRANSMIT_UFPD } from './activities.js'; -export const ORTB_UFPD_PATHS = [ - 'data', - 'ext.data', - 'yob', - 'gender', - 'keywords', - 'kwarray', - 'id', - 'buyeruid', - 'customdata' -].map(f => `user.${f}`).concat('device.ext.cdep'); +export const ORTB_UFPD_PATHS = ['user.data', 'user.ext.data']; 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']; diff --git a/src/adRendering.js b/src/adRendering.js index f8fe0044f9b..0a847d7cc25 100644 --- a/src/adRendering.js +++ b/src/adRendering.js @@ -1,39 +1,33 @@ -import {deepAccess, logError, logWarn, replaceMacros} from './utils.js'; +import {logError} from './utils.js'; import * as events from './events.js'; -import constants from './constants.json'; -import {config} from './config.js'; -import {executeRenderer, isRendererRequired} from './Renderer.js'; -import {VIDEO} from './mediaTypes.js'; -import {auctionManager} from './auctionManager.js'; +import CONSTANTS from './constants.json'; -const {AD_RENDER_FAILED, AD_RENDER_SUCCEEDED, STALE_RENDER, BID_WON} = constants.EVENTS; +const {AD_RENDER_FAILED, AD_RENDER_SUCCEEDED} = CONSTANTS.EVENTS; /** * Emit the AD_RENDER_FAILED event. * - * @param {Object} data - * @param data.reason one of the values in CONSTANTS.AD_RENDER_FAILED_REASON - * @param data.message failure description - * @param [data.bid] bid response object that failed to render - * @param [data.id] adId that failed to render + * @param reason one of the values in CONSTANTS.AD_RENDER_FAILED_REASON + * @param message failure description + * @param bid? bid response object that failed to render + * @param id? adId that failed to render */ export function emitAdRenderFail({ reason, message, bid, id }) { const data = { reason, message }; if (bid) data.bid = bid; if (id) data.adId = id; - logError(`Error rendering ad (id: ${id}): ${message}`); + logError(message); events.emit(AD_RENDER_FAILED, data); } /** * Emit the AD_RENDER_SUCCEEDED event. * (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.) - * @param {Object} data - * @param data.doc document object that was used to `.write` the ad. Should be `null` if unavailable (e.g. for documents in + * @param doc document object that was used to `.write` the ad. Should be `null` if unavailable (e.g. for documents in * a cross-origin frame). - * @param [data.bid] bid response object for the ad that was rendered - * @param [data.id] adId that was rendered. + * @param bid bid response object for the ad that was rendered + * @param id adId that was rendered. */ export function emitAdRenderSucceeded({ doc, bid, id }) { const data = { doc }; @@ -42,61 +36,3 @@ export function emitAdRenderSucceeded({ doc, bid, id }) { events.emit(AD_RENDER_SUCCEEDED, data); } - -export function handleRender(renderFn, {adId, options, bidResponse, doc}) { - if (bidResponse == null) { - emitAdRenderFail({ - reason: constants.AD_RENDER_FAILED_REASON.CANNOT_FIND_AD, - message: `Cannot find ad '${adId}'`, - id: adId - }); - return; - } - if (bidResponse.status === constants.BID_STATUS.RENDERED) { - logWarn(`Ad id ${adId} has been rendered before`); - events.emit(STALE_RENDER, bidResponse); - if (deepAccess(config.getConfig('auctionOptions'), 'suppressStaleRender')) { - return; - } - } - try { - const {adId, ad, adUrl, width, height, renderer, cpm, originalCpm, mediaType} = bidResponse; - // rendering for outstream safeframe - if (isRendererRequired(renderer)) { - executeRenderer(renderer, bidResponse, doc); - } else if (adId) { - if (mediaType === VIDEO) { - emitAdRenderFail({ - reason: constants.AD_RENDER_FAILED_REASON.PREVENT_WRITING_ON_MAIN_DOCUMENT, - message: 'Cannot render video ad', - bid: bidResponse, - id: adId - }); - return; - } - const repl = { - AUCTION_PRICE: originalCpm || cpm, - CLICKTHROUGH: options?.clickUrl || '' - }; - renderFn({ - ad: replaceMacros(ad, repl), - adUrl: replaceMacros(adUrl, repl), - adId, - width, - height - }); - } - } catch (e) { - emitAdRenderFail({ - reason: constants.AD_RENDER_FAILED_REASON.EXCEPTION, - message: e.message, - id: adId, - bid: bidResponse - }); - return; - } - // save winning bids - auctionManager.addWinningBid(bidResponse); - - events.emit(BID_WON, bidResponse); -} diff --git a/src/adServerManager.js b/src/adServerManager.js index 7e1290b3983..af8fe34920e 100644 --- a/src/adServerManager.js +++ b/src/adServerManager.js @@ -34,7 +34,7 @@ const prebid = getGlobal(); /** * @typedef {Object} VideoSupport * - * @property {VideoAdUrlBuilder} buildVideoAdUrl + * @function {VideoAdUrlBuilder} buildVideoAdUrl */ /** diff --git a/src/adapterManager.js b/src/adapterManager.js index 189fbf31f2e..b600802b522 100644 --- a/src/adapterManager.js +++ b/src/adapterManager.js @@ -1,6 +1,8 @@ /** @module adaptermanger */ import { + _each, + bind, deepAccess, deepClone, flatten, @@ -10,8 +12,7 @@ import { getUniqueIdentifierStr, getUserConfiguredParams, groupBy, - isArray, - isPlainObject, + isArray, isPlainObject, isValidMediaTypes, logError, logInfo, @@ -29,7 +30,7 @@ import {hook} from './hook.js'; import {find, includes} from './polyfill.js'; import {adunitCounter} from './adUnits.js'; import {getRefererInfo} from './refererDetection.js'; -import {GDPR_GVLIDS, gdprDataHandler, gppDataHandler, uspDataHandler, } from './consentHandler.js'; +import {GDPR_GVLIDS, GdprConsentHandler, GppConsentHandler, UspConsentHandler} from './consentHandler.js'; import * as events from './events.js'; import CONSTANTS from './constants.json'; import {useMetrics} from './utils/perfMetrics.js'; @@ -40,8 +41,6 @@ import {ACTIVITY_FETCH_BIDS, ACTIVITY_REPORT_ANALYTICS} from './activities/activ import {ACTIVITY_PARAM_ANL_CONFIG, ACTIVITY_PARAM_S2S_NAME, activityParamsBuilder} from './activities/params.js'; import {redactor} from './activities/redactor.js'; -export {gdprDataHandler, gppDataHandler, uspDataHandler, coppaDataHandler} from './consentHandler.js'; - export const PBS_ADAPTER_NAME = 'pbsBidAdapter'; export const PARTITIONS = { CLIENT: 'client', @@ -193,6 +192,16 @@ function getAdUnitCopyForClientAdapters(adUnits) { return adUnitsClientCopy; } +export let gdprDataHandler = new GdprConsentHandler(); +export let uspDataHandler = new UspConsentHandler(); +export let gppDataHandler = new GppConsentHandler(); + +export let coppaDataHandler = { + getCoppa: function() { + return !!(config.getConfig('coppa')) + } +}; + /** * Filter and/or modify media types for ad units based on the given labels. * @@ -373,13 +382,13 @@ adapterManager.callBids = (adUnits, bidRequests, addBidResponse, doneCb, request return; } - let [clientBidderRequests, serverBidderRequests] = bidRequests.reduce((partitions, bidRequest) => { + let [clientBidRequests, serverBidRequests] = bidRequests.reduce((partitions, bidRequest) => { partitions[Number(typeof bidRequest.src !== 'undefined' && bidRequest.src === CONSTANTS.S2S.SRC)].push(bidRequest); return partitions; }, [[], []]); var uniqueServerBidRequests = []; - serverBidderRequests.forEach(serverBidRequest => { + serverBidRequests.forEach(serverBidRequest => { var index = -1; for (var i = 0; i < uniqueServerBidRequests.length; ++i) { if (serverBidRequest.uniquePbsTid === uniqueServerBidRequests[i].uniquePbsTid) { @@ -406,19 +415,14 @@ adapterManager.callBids = (adUnits, bidRequests, addBidResponse, doneCb, request let uniquePbsTid = uniqueServerBidRequests[counter].uniquePbsTid; let adUnitsS2SCopy = uniqueServerBidRequests[counter].adUnitsS2SCopy; - let uniqueServerRequests = serverBidderRequests.filter(serverBidRequest => serverBidRequest.uniquePbsTid === uniquePbsTid); + let uniqueServerRequests = serverBidRequests.filter(serverBidRequest => serverBidRequest.uniquePbsTid === uniquePbsTid); if (s2sAdapter) { let s2sBidRequest = {'ad_units': adUnitsS2SCopy, s2sConfig, ortb2Fragments}; if (s2sBidRequest.ad_units.length) { let doneCbs = uniqueServerRequests.map(bidRequest => { bidRequest.start = timestamp(); - return function (timedOut) { - if (!timedOut) { - onTimelyResponse(bidRequest.bidderRequestId); - } - doneCb.apply(bidRequest, arguments); - } + return doneCb.bind(bidRequest); }); const bidders = getBidderCodes(s2sBidRequest.ad_units).filter((bidder) => adaptersServerSide.includes(bidder)); @@ -433,9 +437,9 @@ adapterManager.callBids = (adUnits, bidRequests, addBidResponse, doneCb, request // make bid requests s2sAdapter.callBids( s2sBidRequest, - serverBidderRequests, + serverBidRequests, addBidResponse, - (timedOut) => doneCbs.forEach(done => done(timedOut)), + () => doneCbs.forEach(done => done()), s2sAjax ); } @@ -447,34 +451,35 @@ adapterManager.callBids = (adUnits, bidRequests, addBidResponse, doneCb, request }); // handle client adapter requests - clientBidderRequests.forEach(bidderRequest => { - bidderRequest.start = timestamp(); + clientBidRequests.forEach(bidRequest => { + bidRequest.start = timestamp(); // TODO : Do we check for bid in pool from here and skip calling adapter again ? - const adapter = _bidderRegistry[bidderRequest.bidderCode]; - config.runWithBidder(bidderRequest.bidderCode, () => { + const adapter = _bidderRegistry[bidRequest.bidderCode]; + config.runWithBidder(bidRequest.bidderCode, () => { logMessage(`CALLING BIDDER`); - events.emit(CONSTANTS.EVENTS.BID_REQUESTED, bidderRequest); + events.emit(CONSTANTS.EVENTS.BID_REQUESTED, bidRequest); }); let ajax = ajaxBuilder(requestBidsTimeout, requestCallbacks ? { - request: requestCallbacks.request.bind(null, bidderRequest.bidderCode), + request: requestCallbacks.request.bind(null, bidRequest.bidderCode), done: requestCallbacks.done } : undefined); - const adapterDone = doneCb.bind(bidderRequest); + const adapterDone = doneCb.bind(bidRequest); try { config.runWithBidder( - bidderRequest.bidderCode, - adapter.callBids.bind( + bidRequest.bidderCode, + bind.call( + adapter.callBids, adapter, - bidderRequest, + bidRequest, addBidResponse, adapterDone, ajax, - () => onTimelyResponse(bidderRequest.bidderRequestId), - config.callbackWithBidder(bidderRequest.bidderCode) + onTimelyResponse, + config.callbackWithBidder(bidRequest.bidderCode) ) ); } catch (e) { - logError(`${bidderRequest.bidderCode} Bid Adapter emitted an uncaught error when parsing their bidRequest`, {e, bidRequest: bidderRequest}); + logError(`${bidRequest.bidderCode} Bid Adapter emitted an uncaught error when parsing their bidRequest`, {e, bidRequest}); adapterDone(); } }); @@ -542,9 +547,6 @@ adapterManager.aliasBidAdapter = function (bidderCode, alias, options) { } else { let spec = bidAdapter.getSpec(); let gvlid = options && options.gvlid; - if (spec.gvlid != null && gvlid == null) { - logWarn(`Alias '${alias}' will NOT re-use the GVL ID of the original adapter ('${spec.code}', gvlid: ${spec.gvlid}). Functionality that requires TCF consent may not work as expected.`) - } let skipPbsAliasing = options && options.skipPbsAliasing; newAdapter = newBidder(Object.assign({}, spec, { code: alias, gvlid, skipPbsAliasing })); _aliasRegistry[alias] = bidderCode; @@ -591,7 +593,7 @@ adapterManager.enableAnalytics = function (config) { config = [config]; } - config.forEach(adapterConfig => { + _each(config, 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}))) { diff --git a/src/adapters/bidderFactory.js b/src/adapters/bidderFactory.js index 3d55f2c06af..c8f03bcfc94 100644 --- a/src/adapters/bidderFactory.js +++ b/src/adapters/bidderFactory.js @@ -15,7 +15,7 @@ import { logError, logWarn, memoize, parseQueryStringParameters, - parseSizesInput, pick, + parseSizesInput, uniques } from '../utils.js'; import {hook} from '../hook.js'; @@ -25,12 +25,7 @@ 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'; - -/** - * @typedef {import('../mediaTypes.js').MediaType} MediaType - * @typedef {import('../Renderer.js').Renderer} Renderer - */ +import {ACTIVITY_TRANSMIT_TID} from '../activities/activities.js'; /** * This file aims to support Adapters during the Prebid 0.x -> 1.x transition. @@ -62,7 +57,7 @@ import {ACTIVITY_TRANSMIT_TID, ACTIVITY_TRANSMIT_UFPD} from '../activities/activ * @property {string} code A code which will be used to uniquely identify this bidder. This should be the same * one as is used in the call to registerBidAdapter * @property {string[]} [aliases] A list of aliases which should also resolve to this bidder. - * @property {MediaType[]} [supportedMediaTypes] A list of Media Types which the adapter supports. + * @property {MediaType[]} [supportedMediaTypes]: A list of Media Types which the adapter supports. * @property {function(object): boolean} isBidRequestValid Determines whether or not the given bid has all the params * needed to make a valid request. * @property {function(BidRequest[], bidderRequest): ServerRequest|ServerRequest[]} buildRequests Build the request to the Server @@ -110,7 +105,7 @@ import {ACTIVITY_TRANSMIT_TID, ACTIVITY_TRANSMIT_UFPD} from '../activities/activ * * @property {*} body The response body. If this is legal JSON, then it will be parsed. Otherwise it'll be a * string with the body's content. - * @property {{get: function(string): string}} headers The response headers. + * @property {{get: function(string): string} headers The response headers. * Call this like `ServerResponse.headers.get("Content-Type")` */ @@ -131,7 +126,7 @@ import {ACTIVITY_TRANSMIT_TID, ACTIVITY_TRANSMIT_UFPD} from '../activities/activ * @property {object} [video] Object for storing video response data * @property {object} [meta] Object for storing bid meta data * @property {string} [meta.primaryCatId] The IAB primary category ID - * @property {Renderer} renderer A Renderer which can be used as a default for this bid, + * @property [Renderer] renderer A Renderer which can be used as a default for this bid, * if the publisher doesn't override it. This is only relevant for Outstream Video bids. */ @@ -155,7 +150,6 @@ import {ACTIVITY_TRANSMIT_TID, ACTIVITY_TRANSMIT_UFPD} from '../activities/activ // common params for all mediaTypes const COMMON_BID_RESPONSE_KEYS = ['cpm', 'ttl', 'creativeId', 'netRevenue', 'currency']; -const TIDS = ['auctionId', 'transactionId']; /** * Register a bidder with prebid, using the given spec. @@ -190,7 +184,7 @@ export function registerBidder(spec) { } } -export function guardTids(bidderCode) { +function guardTids(bidderCode) { if (isActivityAllowed(ACTIVITY_TRANSMIT_TID, activityParams(MODULE_TYPE_BIDDER, bidderCode))) { return { bidRequest: (br) => br, @@ -198,20 +192,12 @@ export function guardTids(bidderCode) { }; } function get(target, prop, receiver) { - if (TIDS.includes(prop)) { + if (['transactionId', 'auctionId'].includes(prop)) { return null; } return Reflect.get(target, prop, receiver); } - function privateAccessProxy(target, handler) { - const proxy = new Proxy(target, handler); - // always allow methods (such as getFloor) private access to TIDs - Object.entries(target) - .filter(([_, v]) => typeof v === 'function') - .forEach(([prop, fn]) => proxy[prop] = fn.bind(target)); - return proxy; - } - const bidRequest = memoize((br) => privateAccessProxy(br, {get}), (arg) => arg.bidId); + const bidRequest = memoize((br) => new Proxy(br, {get}), (arg) => arg.bidId) /** * Return a view on bidd(er) requests where auctionId/transactionId are nulled if the bidder is not allowed `transmitTid`. * @@ -221,7 +207,7 @@ export function guardTids(bidderCode) { */ return { bidRequest, - bidderRequest: (br) => privateAccessProxy(br, { + bidderRequest: (br) => new Proxy(br, { get(target, prop, receiver) { if (prop === 'bids') return br.bids.map(bidRequest); return get(target, prop, receiver); @@ -293,11 +279,14 @@ export function newBidder(spec) { onTimelyResponse(spec.code); responses.push(resp) }, + /** Process eventual BidderAuctionResponse.fledgeAuctionConfig field in response. + * @param {Array} fledgeAuctionConfigs + */ onFledgeAuctionConfigs: (fledgeAuctionConfigs) => { fledgeAuctionConfigs.forEach((fledgeAuctionConfig) => { const bidRequest = bidRequestMap[fledgeAuctionConfig.bidId]; if (bidRequest) { - addComponentAuction(bidRequest, fledgeAuctionConfig.config); + addComponentAuction(bidRequest.adUnitCode, fledgeAuctionConfig.config); } else { logWarn('Received fledge auction configuration for an unknown bidId', fledgeAuctionConfig); } @@ -305,9 +294,7 @@ export function newBidder(spec) { }, // If the server responds with an error, there's not much we can do beside logging. onError: (errorMessage, error) => { - if (!error.timedOut) { - onTimelyResponse(spec.code); - } + onTimelyResponse(spec.code); adapterManager.callBidderError(spec.code, error, bidderRequest) events.emit(CONSTANTS.EVENTS.BIDDER_ERROR, { error, bidderRequest }); logError(`Server call for ${spec.code} failed: ${errorMessage} ${error.status}. Continuing without bids.`); @@ -325,7 +312,7 @@ export function newBidder(spec) { bid.originalCpm = bid.cpm; bid.originalCurrency = bid.currency; bid.meta = bid.meta || Object.assign({}, bid[bidRequest.bidder]); - const prebidBid = Object.assign(createBid(CONSTANTS.STATUS.GOOD, bidRequest), bid, pick(bidRequest, TIDS)); + const prebidBid = Object.assign(createBid(CONSTANTS.STATUS.GOOD, bidRequest), bid); addBidWithCode(bidRequest.adUnitCode, prebidBid); } else { logWarn(`Bidder ${spec.code} made bid for unknown request ID: ${bid.requestId}. Ignoring.`); @@ -458,15 +445,6 @@ export const processBidderRequests = hook('sync', function (spec, bids, bidderRe onRequest(request); const networkDone = requestMetrics.startTiming('net'); - - function getOptions(defaults) { - const ro = request.options; - return Object.assign(defaults, ro, { - browsingTopics: ro?.hasOwnProperty('browsingTopics') && !ro.browsingTopics - ? false - : (bidderSettings.get(spec.code, 'topicsHeader') ?? true) && isActivityAllowed(ACTIVITY_TRANSMIT_UFPD, activityParams(MODULE_TYPE_BIDDER, spec.code)) - }) - } switch (request.method) { case 'GET': ajax( @@ -476,10 +454,10 @@ export const processBidderRequests = hook('sync', function (spec, bids, bidderRe error: onFailure }, undefined, - getOptions({ + Object.assign({ method: 'GET', withCredentials: true - }) + }, request.options) ); break; case 'POST': @@ -490,11 +468,11 @@ export const processBidderRequests = hook('sync', function (spec, bids, bidderRe error: onFailure }, typeof request.data === 'string' ? request.data : JSON.stringify(request.data), - getOptions({ + Object.assign({ method: 'POST', contentType: 'text/plain', withCredentials: true - }) + }, request.options) ); break; default: @@ -532,7 +510,7 @@ export const registerSyncInner = hook('async', function(spec, responses, gdprCon } }, 'registerSyncs') -export const addComponentAuction = hook('sync', (request, fledgeAuctionConfig) => { +export const addComponentAuction = hook('sync', (adUnitCode, fledgeAuctionConfig) => { }, 'addComponentAuction'); // check that the bid has a width and height set diff --git a/src/adloader.js b/src/adloader.js index f60955736bd..3aae348d106 100644 --- a/src/adloader.js +++ b/src/adloader.js @@ -24,14 +24,7 @@ const _approvedLoadExternalJSList = [ 'confiant', 'arcspan', 'airgrid', - 'clean.io', - 'a1Media', - 'geoedge', - 'mediafilter', - 'qortex', - 'dynamicAdBoost', - 'contxtful', - 'id5' + 'clean.io' ] /** @@ -41,7 +34,7 @@ const _approvedLoadExternalJSList = [ * @param {string} moduleCode bidderCode or module code of the module requesting this resource * @param {function} [callback] callback function to be called after the script is loaded * @param {Document} [doc] the context document, in which the script will be loaded, defaults to loaded document - * @param {object} attributes an object of attributes to be added to the script with setAttribute by [key] and [value]; Only the attributes passed in the first request of a url will be added. + * @param {object} an object of attributes to be added to the script with setAttribute by [key] and [value]; Only the attributes passed in the first request of a url will be added. */ export function loadExternalScript(url, moduleCode, callback, doc, attributes) { if (!moduleCode || !url) { diff --git a/src/ajax.js b/src/ajax.js index ef4c2e4bcb4..5e926f3210d 100644 --- a/src/ajax.js +++ b/src/ajax.js @@ -1,148 +1,99 @@ -import {config} from './config.js'; -import {buildUrl, logError, parseUrl} from './utils.js'; +import { config } from './config.js'; +import { logMessage, logError, parseUrl, buildUrl, _each } from './utils.js'; -export const dep = { - fetch: window.fetch.bind(window), - makeRequest: (r, o) => new Request(r, o), - timeout(timeout, resource) { - const ctl = new AbortController(); - let cancelTimer = setTimeout(() => { - ctl.abort(); - logError(`Request timeout after ${timeout}ms`, resource); - cancelTimer = null; - }, timeout); - return { - signal: ctl.signal, - done() { - cancelTimer && clearTimeout(cancelTimer) - } - } - } -} - -const GET = 'GET'; -const POST = 'POST'; -const CTYPE = 'Content-Type'; +const XHR_DONE = 4; /** - * transform legacy `ajax` parameters into a fetch request. - * @returns {Request} - */ -export function toFetchRequest(url, data, options = {}) { - const method = options.method || (data ? POST : GET); - if (method === GET && data) { - const urlInfo = parseUrl(url, options); - Object.assign(urlInfo.search, data); - url = buildUrl(urlInfo); - } - const headers = new Headers(options.customHeaders); - headers.set(CTYPE, options.contentType || 'text/plain'); - const rqOpts = { - method, - headers - } - if (method !== GET && data) { - rqOpts.body = data; - } - if (options.withCredentials) { - rqOpts.credentials = 'include'; - } - if (options.browsingTopics && isSecureContext) { - // the Request constructor will throw an exception if the browser supports topics - // but we're not in a secure context - rqOpts.browsingTopics = true; - } - return dep.makeRequest(url, rqOpts); -} - -/** - * Return a version of `fetch` that automatically cancels requests after `timeout` milliseconds. - * - * If provided, `request` and `done` should be functions accepting a single argument. - * `request` is invoked at the beginning of each request, and `done` at the end; both are passed its origin. + * Simple IE9+ and cross-browser ajax request function + * Note: x-domain requests in IE9 do not support the use of cookies * - * @returns {function(*, {}?): Promise} + * @param url string url + * @param callback {object | function} callback + * @param data mixed data + * @param options object */ -export function fetcherFactory(timeout = 3000, {request, done} = {}) { - 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); - } - let pm = dep.fetch(resource, options); - if (to?.done != null) pm = pm.finally(to.done); - return pm; - }; +export const ajax = ajaxBuilder(); - if (request != null || done != null) { - fetcher = ((fetch) => function (resource, options) { - const origin = new URL(resource?.url == null ? resource : resource.url, document.location).origin; - let req = fetch(resource, options); - request && request(origin); - if (done) req = req.finally(() => done(origin)); - return req; - })(fetcher); - } - return fetcher; -} +export function ajaxBuilder(timeout = 3000, {request, done} = {}) { + return function(url, callback, data, options = {}) { + try { + let x; + let method = options.method || (data ? 'POST' : 'GET'); + let parser = document.createElement('a'); + parser.href = url; + + let callbacks = typeof callback === 'object' && callback !== null ? callback : { + success: function() { + logMessage('xhr success'); + }, + error: function(e) { + logError('xhr error', null, e); + } + }; -function toXHR({status, statusText = '', headers, url}, responseText) { - let xml = 0; - function getXML(onError) { - if (xml === 0) { - try { - xml = new DOMParser().parseFromString(responseText, headers?.get(CTYPE)?.split(';')?.[0]) - } catch (e) { - xml = null; - onError && onError(e) + if (typeof callback === 'function') { + callbacks.success = callback; } - } - return xml; - } - return { - readyState: XMLHttpRequest.DONE, - status, - statusText, - responseText, - response: responseText, - responseType: '', - responseURL: url, - get responseXML() { - return getXML(logError); - }, - getResponseHeader: (header) => headers?.has(header) ? headers.get(header) : null, - toJSON() { - return Object.assign({responseXML: getXML()}, this) - }, - timedOut: false - } -} -/** - * attach legacy `ajax` callbacks to a fetch promise. - */ -export function attachCallbacks(fetchPm, callback) { - const {success, error} = typeof callback === 'object' && callback != null ? callback : { - success: typeof callback === 'function' ? callback : () => null, - error: (e, x) => logError('Network error', e, x) - }; - fetchPm.then(response => response.text().then((responseText) => [response, responseText])) - .then(([response, responseText]) => { - 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'})) - ); -} + x = new window.XMLHttpRequest(); -export function ajaxBuilder(timeout = 3000, {request, done} = {}) { - const fetcher = fetcherFactory(timeout, {request, done}); - return function (url, callback, data, options = {}) { - attachCallbacks(fetcher(toFetchRequest(url, data, options)), callback); - }; -} + x.onreadystatechange = function () { + if (x.readyState === XHR_DONE) { + if (typeof done === 'function') { + done(parser.origin); + } + let status = x.status; + if ((status >= 200 && status < 300) || status === 304) { + callbacks.success(x.responseText, x); + } else { + callbacks.error(x.statusText, x); + } + } + }; -export const ajax = ajaxBuilder(); -export const fetch = fetcherFactory(); + // Disabled timeout temporarily to avoid xhr failed requests. https://github.com/prebid/Prebid.js/issues/2648 + if (!config.getConfig('disableAjaxTimeout')) { + x.ontimeout = function () { + logError(' xhr timeout after ', x.timeout, 'ms'); + }; + } + + if (method === 'GET' && data) { + let urlInfo = parseUrl(url, options); + Object.assign(urlInfo.search, data); + url = buildUrl(urlInfo); + } + + x.open(method, url, true); + // IE needs timeout to be set after open - see #1410 + // Disabled timeout temporarily to avoid xhr failed requests. https://github.com/prebid/Prebid.js/issues/2648 + if (!config.getConfig('disableAjaxTimeout')) { + x.timeout = timeout; + } + + if (options.withCredentials) { + x.withCredentials = true; + } + _each(options.customHeaders, (value, header) => { + x.setRequestHeader(header, value); + }); + if (options.preflight) { + x.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); + } + x.setRequestHeader('Content-Type', options.contentType || 'text/plain'); + + if (typeof request === 'function') { + request(parser.origin); + } + + if (method === 'POST' && data) { + x.send(data); + } else { + x.send(); + } + } catch (error) { + logError('xhr construction', error); + typeof callback === 'object' && callback !== null && callback.error(error); + } + } +} diff --git a/src/auction.js b/src/auction.js index 37bb2604167..69febccc94e 100644 --- a/src/auction.js +++ b/src/auction.js @@ -4,26 +4,19 @@ * In Prebid 0.x, $$PREBID_GLOBAL$$ had _bidsRequested and _bidsReceived as public properties. * Starting 1.0, Prebid will support concurrent auctions. Each auction instance will store private properties, bidsRequested and bidsReceived. * - * AuctionManager will create an instance of auction and will store all the auctions. + * AuctionManager will create instance of auction and will store all the auctions. * */ /** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - * @typedef {import('../src/config.js').MediaTypePriceGranularity} MediaTypePriceGranularity - * @typedef {import('../src/mediaTypes.js').MediaType} MediaType - */ - -/** - * @typedef {Object} AdUnit An object containing the adUnit configuration. - * - * @property {string} code A code which will be used to uniquely identify this bidder. This should be the same - * one as is used in the call to registerBidAdapter - * @property {Array.} sizes A list of size for adUnit. - * @property {object} params Any bidder-specific params which the publisher used in their bid request. - * This is guaranteed to have passed the spec.areParamsValid() test. - */ + * @typedef {Object} AdUnit An object containing the adUnit configuration. + * + * @property {string} code A code which will be used to uniquely identify this bidder. This should be the same + * one as is used in the call to registerBidAdapter + * @property {Array.} sizes A list of size for adUnit. + * @property {object} params Any bidder-specific params which the publisher used in their bid request. + * This is guaranteed to have passed the spec.areParamsValid() test. + */ /** * @typedef {Array.} size @@ -65,8 +58,11 @@ */ import { - callBurl, + _each, + adUnitsFilter, + bind, deepAccess, + flatten, generateUUID, getValue, isEmpty, @@ -94,7 +90,7 @@ import {bidderSettings} from './bidderSettings.js'; import * as events from './events.js'; import adapterManager from './adapterManager.js'; import CONSTANTS from './constants.json'; -import {defer, GreedyPromise} from './utils/promise.js'; +import {GreedyPromise} from './utils/promise.js'; import {useMetrics} from './utils/perfMetrics.js'; import {adjustCpm} from './utils/cpm.js'; import {getGlobal} from './prebidGlobal.js'; @@ -126,20 +122,19 @@ export function resetAuctionState() { } /** - * Creates new auction instance - * - * @param {Object} requestConfig - * @param {AdUnit} requestConfig.adUnits - * @param {AdUnitCode} requestConfig.adUnitCodes - * @param {function():void} requestConfig.callback - * @param {number} requestConfig.cbTimeout - * @param {Array.} requestConfig.labels - * @param {string} requestConfig.auctionId - * @param {{global: {}, bidder: {}}} requestConfig.ortb2Fragments first party data, separated into global - * (from getConfig('ortb2') + requestBids({ortb2})) and bidder (a map from bidderCode to ortb2) - * @param {Object} requestConfig.metrics - * @returns {Auction} auction instance - */ + * Creates new auction instance + * + * @param {Object} requestConfig + * @param {AdUnit} requestConfig.adUnits + * @param {AdUnitCode} requestConfig.adUnitCodes + * @param {function():void} requestConfig.callback + * @param {number} requestConfig.cbTimeout + * @param {Array.} requestConfig.labels + * @param {string} requestConfig.auctionId + * @param {{global: {}, bidder: {}}} ortb2Fragments first party data, separated into global + * (from getConfig('ortb2') + requestBids({ortb2})) and bidder (a map from bidderCode to ortb2) + * @returns {Auction} auction instance + */ export function newAuction({adUnits, adUnitCodes, callback, cbTimeout, labels, auctionId, ortb2Fragments, metrics}) { metrics = useMetrics(metrics); const _adUnits = adUnits; @@ -147,8 +142,7 @@ export function newAuction({adUnits, adUnitCodes, callback, cbTimeout, labels, a const _adUnitCodes = adUnitCodes; const _auctionId = auctionId || generateUUID(); const _timeout = cbTimeout; - const _timelyRequests = new Set(); - const done = defer(); + const _timelyBidders = new Set(); let _bidsRejected = []; let _callback = callback; let _bidderRequests = []; @@ -157,7 +151,7 @@ export function newAuction({adUnits, adUnitCodes, callback, cbTimeout, labels, a let _winningBids = []; let _auctionStart; let _auctionEnd; - let _timeoutTimer; + let _timer; let _auctionStatus; let _nonBids = []; @@ -188,22 +182,25 @@ export function newAuction({adUnits, adUnitCodes, callback, cbTimeout, labels, a } function startAuctionTimer() { - _timeoutTimer = setTimeout(() => executeCallback(true), _timeout); + const timedOut = true; + const timeoutCallback = executeCallback.bind(null, timedOut); + let timer = setTimeout(timeoutCallback, _timeout); + _timer = timer; } - function executeCallback(timedOut) { - if (!timedOut) { - clearTimeout(_timeoutTimer); - } else { - events.emit(CONSTANTS.EVENTS.AUCTION_TIMEOUT, getProperties()); + function executeCallback(timedOut, cleartimer) { + // clear timer when done calls executeCallback + if (cleartimer) { + clearTimeout(_timer); } + if (_auctionEnd === undefined) { - let timedOutRequests = []; + let timedOutBidders = []; if (timedOut) { logMessage(`Auction ${_auctionId} timedOut`); - timedOutRequests = _bidderRequests.filter(rq => !_timelyRequests.has(rq.bidderRequestId)).flatMap(br => br.bids) - if (timedOutRequests.length) { - events.emit(CONSTANTS.EVENTS.BID_TIMEOUT, timedOutRequests); + timedOutBidders = getTimedOutBids(_bidderRequests, _timelyBidders); + if (timedOutBidders.length) { + events.emit(CONSTANTS.EVENTS.BID_TIMEOUT, timedOutBidders); } } @@ -212,14 +209,14 @@ export function newAuction({adUnits, adUnitCodes, callback, cbTimeout, labels, a metrics.checkpoint('auctionEnd'); metrics.timeBetween('requestBids', 'auctionEnd', 'requestBids.total'); metrics.timeBetween('callBids', 'auctionEnd', 'requestBids.callBids'); - done.resolve(); events.emit(CONSTANTS.EVENTS.AUCTION_END, getProperties()); bidsBackCallback(_adUnits, function () { try { if (_callback != null) { + const adUnitCodes = _adUnitCodes; const bids = _bidsReceived - .filter(bid => _adUnitCodes.includes(bid.adUnitCode)) + .filter(bind.call(adUnitsFilter, this, adUnitCodes)) .reduce(groupByPlacement, {}); _callback.apply(pbjsInstance, [bids, timedOut, _auctionId]); _callback = null; @@ -228,8 +225,8 @@ export function newAuction({adUnits, adUnitCodes, callback, cbTimeout, labels, a logError('Error executing bidsBackHandler', null, e); } finally { // Calling timed out bidders - if (timedOutRequests.length) { - adapterManager.callTimedOutBidders(adUnits, timedOutRequests, _timeout); + if (timedOutBidders.length) { + adapterManager.callTimedOutBidders(adUnits, timedOutBidders, _timeout); } // Only automatically sync if the publisher has not chosen to "enableOverride" let userSyncConfig = config.getConfig('userSync') || {}; @@ -247,11 +244,11 @@ export function newAuction({adUnits, adUnitCodes, callback, cbTimeout, labels, a // when all bidders have called done callback atleast once it means auction is complete logInfo(`Bids Received for Auction with id: ${_auctionId}`, _bidsReceived); _auctionStatus = AUCTION_COMPLETED; - executeCallback(false); + executeCallback(false, true); } - function onTimelyResponse(bidderRequestId) { - _timelyRequests.add(bidderRequestId); + function onTimelyResponse(bidderCode) { + _timelyBidders.add(bidderCode); } function callBids() { @@ -371,7 +368,6 @@ export function newAuction({adUnits, adUnitCodes, callback, cbTimeout, labels, a function addWinningBid(winningBid) { const winningAd = adUnits.find(adUnit => adUnit.transactionId === winningBid.transactionId); _winningBids = _winningBids.concat(winningBid); - callBurl(winningBid); adapterManager.callBidWonBidder(winningBid.adapterCode || winningBid.bidder, winningBid, adUnits); if (winningAd && !winningAd.deferBilling) adapterManager.callBidBillableBidder(winningBid); } @@ -390,12 +386,12 @@ export function newAuction({adUnits, adUnitCodes, callback, cbTimeout, labels, a addBidReceived, addBidRejected, addNoBid, + executeCallback, callBids, addWinningBid, setBidTargeting, getWinningBids: () => _winningBids, getAuctionStart: () => _auctionStart, - getAuctionEnd: () => _auctionEnd, getTimeout: () => _timeout, getAuctionId: () => _auctionId, getAuctionStatus: () => _auctionStatus, @@ -407,30 +403,20 @@ export function newAuction({adUnits, adUnitCodes, callback, cbTimeout, labels, a getNonBids: () => _nonBids, getFPD: () => ortb2Fragments, getMetrics: () => metrics, - end: done.promise }; } /** * Hook into this to intercept bids before they are added to an auction. * - * @type {Function} * @param adUnitCode * @param bid - * @param {function(String): void} reject a function that, when called, rejects `bid` with the given reason. + * @param {function(String)} reject: a function that, when called, rejects `bid` with the given reason. */ export const addBidResponse = hook('sync', function(adUnitCode, bid, reject) { this.dispatch.call(null, adUnitCode, bid); }, 'addBidResponse'); -/** - * Delay hook for adapter responses. - * - * `ready` is a promise; auctions wait for it to resolve before closing. Modules can hook into this - * to delay the end of auctions while they perform initialization that does not need to delay their start. - */ -export const responsesReady = hook('sync', (ready) => ready, 'responsesReady'); - export const addBidderRequests = hook('sync', function(bidderRequests) { this.dispatch.call(this.context, bidderRequests); }, 'addBidderRequests'); @@ -446,6 +432,32 @@ export function auctionCallbacks(auctionDone, auctionInstance, {index = auctionM let allAdapterCalledDone = false; let bidderRequestsDone = new Set(); let bidResponseMap = {}; + const ready = {}; + + function waitFor(requestId, result) { + if (ready[requestId] == null) { + ready[requestId] = GreedyPromise.resolve(); + } + ready[requestId] = ready[requestId].then(() => GreedyPromise.resolve(result).catch(() => {})) + } + + function guard(bidderRequest, fn) { + let timeout = bidderRequest.timeout; + if (timeout == null || timeout > auctionInstance.getTimeout()) { + timeout = auctionInstance.getTimeout(); + } + const timeRemaining = auctionInstance.getAuctionStart() + timeout - Date.now(); + const wait = ready[bidderRequest.bidderRequestId]; + const orphanWait = ready['']; // also wait for "orphan" responses that are not associated with any request + if ((wait != null || orphanWait != null) && timeRemaining > 0) { + GreedyPromise.race([ + GreedyPromise.timeout(timeRemaining), + GreedyPromise.resolve(orphanWait).then(() => wait) + ]).then(fn); + } else { + fn(); + } + } function afterBidAdded() { outstandingBidsAdded--; @@ -464,7 +476,7 @@ export function auctionCallbacks(auctionDone, auctionInstance, {index = auctionM function acceptBidResponse(adUnitCode, bid) { handleBidResponse(adUnitCode, bid, (done) => { let bidResponse = getPreparedBidForAuction(bid); - events.emit(CONSTANTS.EVENTS.BID_ACCEPTED, bidResponse); + if (FEATURES.VIDEO && bidResponse.mediaType === VIDEO) { tryAddVideoBid(auctionInstance, bidResponse, done); } else { @@ -520,7 +532,8 @@ export function auctionCallbacks(auctionDone, auctionInstance, {index = auctionM return { addBidResponse: (function () { function addBid(adUnitCode, bid) { - addBidResponse.call({ + const bidderRequest = index.getBidderRequest(bid); + waitFor((bidderRequest && bidderRequest.bidderRequestId) || '', addBidResponse.call({ dispatch: acceptBidResponse, }, adUnitCode, bid, (() => { let rejected = false; @@ -530,17 +543,23 @@ export function auctionCallbacks(auctionDone, auctionInstance, {index = auctionM rejected = true; } } - })()) + })())); } addBid.reject = rejectBidResponse; return addBid; })(), adapterDone: function () { - responsesReady(GreedyPromise.resolve()).finally(() => adapterDone.call(this)); + guard(this, adapterDone.bind(this)) } } } +export function doCallbacksIfTimedout(auctionInstance, bidResponse) { + if (bidResponse.timeToRespond > auctionInstance.getTimeout() + config.getConfig('timeoutBuffer')) { + auctionInstance.executeCallback(true); + } +} + // Add a bid to the auction. export function addBidToAuction(auctionInstance, bidResponse) { setupBidTargeting(bidResponse); @@ -548,6 +567,8 @@ export function addBidToAuction(auctionInstance, bidResponse) { useMetrics(bidResponse.metrics).timeSince('addBidResponse', 'addBidResponse.total'); auctionInstance.addBidReceived(bidResponse); events.emit(CONSTANTS.EVENTS.BID_RESPONSE, bidResponse); + + doCallbacksIfTimedout(auctionInstance, bidResponse); } // Video bids may fail if the cache is down, or there's trouble on the network. @@ -594,11 +615,16 @@ const _storeInCache = (batch) => { const { auctionInstance, bidResponse, afterBidAdded } = batch[i]; if (error) { logWarn(`Failed to save to the video cache: ${error}. Video bid must be discarded.`); + + doCallbacksIfTimedout(auctionInstance, bidResponse); } else { if (cacheId.uuid === '') { logWarn(`Supplied video cache key was already in use by Prebid Cache; caching attempt was rejected. Video bid must be discarded.`); + + doCallbacksIfTimedout(auctionInstance, bidResponse); } else { bidResponse.videoCacheKey = cacheId.uuid; + if (!bidResponse.vastUrl) { bidResponse.vastUrl = getCacheUrl(bidResponse.videoCacheKey); } @@ -759,9 +785,8 @@ export function getMediaTypeGranularity(mediaType, mediaTypes, mediaTypePriceGra /** * This function returns the price granularity defined. It can be either publisher defined or default value - * @param {Bid} bid bid response object - * @param {object} obj - * @param {object} obj.index + * @param bid bid response object + * @param index * @returns {string} granularity */ export const getPriceGranularity = (bid, {index = auctionManager.index} = {}) => { @@ -795,16 +820,6 @@ export const getPriceByGranularity = (granularity) => { } } -/** - * This function returns a function to get crid from bid response - * @returns {function} - */ -export const getCreativeId = () => { - return (bid) => { - return (bid.creativeId) ? bid.creativeId : ''; - } -} - /** * This function returns a function to get first advertiser domain from bid response meta * @returns {function} @@ -815,16 +830,6 @@ export const getAdvertiserDomain = () => { } } -/** - * This function returns a function to get dsp name or id from bid response meta - * @returns {function} - */ -export const getDSP = () => { - return (bid) => { - return (bid.meta && (bid.meta.networkId || bid.meta.networkName)) ? deepAccess(bid, 'meta.networkName') || deepAccess(bid, 'meta.networkId') : ''; - } -} - /** * This function returns a function to get the primary category id from bid response meta * @returns {function} @@ -861,14 +866,13 @@ function defaultAdserverTargeting() { createKeyVal(TARGETING_KEYS.FORMAT, 'mediaType'), createKeyVal(TARGETING_KEYS.ADOMAIN, getAdvertiserDomain()), createKeyVal(TARGETING_KEYS.ACAT, getPrimaryCatId()), - createKeyVal(TARGETING_KEYS.DSP, getDSP()), - createKeyVal(TARGETING_KEYS.CRID, getCreativeId()), ] } /** * @param {string} mediaType * @param {string} bidderCode + * @param {BidRequest} bidReq * @returns {*} */ export function getStandardBidderSettings(mediaType, bidderCode) { @@ -935,7 +939,7 @@ function setKeys(keyValues, bidderSettings, custBidObj, bidReq) { var targeting = bidderSettings[CONSTANTS.JSON_MAPPING.ADSERVER_TARGETING]; custBidObj.size = custBidObj.getSize(); - (targeting || []).forEach(function (kvPair) { + _each(targeting, function (kvPair) { var key = kvPair.key; var value = kvPair.val; @@ -953,7 +957,7 @@ function setKeys(keyValues, bidderSettings, custBidObj, bidReq) { if ( ((typeof bidderSettings.suppressEmptyKeys !== 'undefined' && bidderSettings.suppressEmptyKeys === true) || - key === CONSTANTS.TARGETING_KEYS.DEAL || key === CONSTANTS.TARGETING_KEYS.ACAT || key === CONSTANTS.TARGETING_KEYS.DSP || key === CONSTANTS.TARGETING_KEYS.CRID) && // hb_deal & hb_acat are suppressed automatically if not set + key === CONSTANTS.TARGETING_KEYS.DEAL || key === CONSTANTS.TARGETING_KEYS.ACAT) && // hb_deal & hb_acat are suppressed automatically if not set ( isEmptyStr(value) || value === null || @@ -988,3 +992,24 @@ function groupByPlacement(bidsByPlacement, bid) { bidsByPlacement[bid.adUnitCode].bids.push(bid); return bidsByPlacement; } + +/** + * Returns a list of bids that we haven't received a response yet where the bidder did not call done + * @param {BidRequest[]} bidderRequests List of bids requested for auction instance + * @param {Set} timelyBidders Set of bidders which responded in time + * + * @typedef {Object} TimedOutBid + * @property {string} bidId The id representing the bid + * @property {string} bidder The string name of the bidder + * @property {string} adUnitCode The code used to uniquely identify the ad unit on the publisher's page + * @property {string} auctionId The id representing the auction + * + * @return {Array} List of bids that Prebid hasn't received a response for + */ +function getTimedOutBids(bidderRequests, timelyBidders) { + const timedOutBids = bidderRequests + .map(bid => (bid.bids || []).filter(bid => !timelyBidders.has(bid.bidder))) + .reduce(flatten, []); + + return timedOutBids; +} diff --git a/src/auctionIndex.js b/src/auctionIndex.js index bb3f535b41a..bdd2b42f9c6 100644 --- a/src/auctionIndex.js +++ b/src/auctionIndex.js @@ -1,30 +1,24 @@ -/** - * @typedef {Object} AuctionIndex - * - * @property {function({ auctionId: * }): *} getAuction Returns auction instance for `auctionId` - * @property {function({ transactionId: * }): *} getAdUnit Returns `adUnit` object for `transactionId`. - * You should prefer `getMediaTypes` for looking up bid media types. - * @property {function({ transactionId: *, requestId: * }): *} getMediaTypes Returns mediaTypes object from bidRequest (through `requestId`) falling back to the adUnit (through `transactionId`). - * The bidRequest is given precedence because its mediaTypes can differ from the adUnit's (if bidder-specific labels are in use). - * Bids that have no associated request do not have labels either, and use the adUnit's mediaTypes. - * @property {function({ requestId: *, bidderRequestId: * }): *} getBidderRequest Returns bidderRequest that matches both requestId and bidderRequestId (if either or both are provided). - * Bid responses are not guaranteed to have a corresponding request. - * @property {function({ requestId: * }): *} getBidRequest Returns bidRequest object for requestId. - * Bid responses are not guaranteed to have a corresponding request. - */ - /** * Retrieves request-related bid data. * All methods are designed to work with Bid (response) objects returned by bid adapters. */ export function AuctionIndex(getAuctions) { Object.assign(this, { + /** + * @param auctionId + * @returns {*} Auction instance for `auctionId` + */ getAuction({auctionId}) { if (auctionId != null) { return getAuctions() .find(auction => auction.getAuctionId() === auctionId); } }, + /** + * NOTE: you should prefer {@link #getMediaTypes} for looking up bid media types. + * @param transactionId + * @returns adUnit object for `transactionId` + */ getAdUnit({transactionId}) { if (transactionId != null) { return getAuctions() @@ -32,6 +26,14 @@ export function AuctionIndex(getAuctions) { .find(au => au.transactionId === transactionId); } }, + /** + * @param transactionId + * @param requestId? + * @returns {*} mediaTypes object from bidRequest (through requestId) falling back to the adUnit (through transactionId). + * + * The bidRequest is given precedence because its mediaTypes can differ from the adUnit's (if bidder-specific labels are in use). + * Bids that have no associated request do not have labels either, and use the adUnit's mediaTypes. + */ getMediaTypes({transactionId, requestId}) { if (requestId != null) { const req = this.getBidRequest({requestId}); @@ -45,6 +47,13 @@ export function AuctionIndex(getAuctions) { } } }, + /** + * @param requestId? + * @param bidderRequestId? + * @returns {*} bidderRequest that matches both requestId and bidderRequestId (if either or both are provided). + * + * NOTE: Bid responses are not guaranteed to have a corresponding request. + */ getBidderRequest({requestId, bidderRequestId}) { if (requestId != null || bidderRequestId != null) { let bers = getAuctions().flatMap(a => a.getBidRequests()); @@ -58,6 +67,12 @@ export function AuctionIndex(getAuctions) { } } }, + /** + * @param requestId + * @returns {*} bidRequest object for requestId + * + * NOTE: Bid responses are not guaranteed to have a corresponding request. + */ getBidRequest({requestId}) { if (requestId != null) { return getAuctions() diff --git a/src/auctionManager.js b/src/auctionManager.js index 8f3cbb56333..90d5fb543e2 100644 --- a/src/auctionManager.js +++ b/src/auctionManager.js @@ -17,19 +17,14 @@ * @property {function(): Object} getStandardBidderAdServerTargeting - returns standard bidder targeting for all the adapters. Refer http://prebid.org/dev-docs/publisher-api-reference.html#module_pbjs.bidderSettings for more details * @property {function(Object): void} addWinningBid - add a winning bid to an auction based on auctionId * @property {function(): void} clearAllAuctions - clear all auctions for testing - * @property {AuctionIndex} index */ -import { uniques, logWarn } from './utils.js'; +import { uniques, flatten, logWarn } from './utils.js'; import { newAuction, getStandardBidderSettings, AUCTION_COMPLETED } from './auction.js'; +import {find} from './polyfill.js'; import {AuctionIndex} from './auctionIndex.js'; import CONSTANTS from './constants.json'; import {useMetrics} from './utils/perfMetrics.js'; -import {ttlCollection} from './utils/ttlCollection.js'; -import {getTTL, onTTLBufferChange} from './bidTTL.js'; -import {config} from './config.js'; - -const CACHE_TTL_SETTING = 'minBidCacheTTL'; /** * Creates new instance of auctionManager. There will only be one instance of auctionManager but @@ -38,42 +33,15 @@ const CACHE_TTL_SETTING = 'minBidCacheTTL'; * @returns {AuctionManager} auctionManagerInstance */ export function newAuctionManager() { - let minCacheTTL = null; - - const _auctions = ttlCollection({ - startTime: (au) => au.end.then(() => au.getAuctionEnd()), - ttl: (au) => minCacheTTL == null ? null : au.end.then(() => { - return Math.max(minCacheTTL, ...au.getBidsReceived().map(getTTL)) * 1000 - }), - }); - - onTTLBufferChange(() => { - if (minCacheTTL != null) _auctions.refresh(); - }) - - config.getConfig(CACHE_TTL_SETTING, (cfg) => { - const prev = minCacheTTL; - minCacheTTL = cfg?.[CACHE_TTL_SETTING]; - minCacheTTL = typeof minCacheTTL === 'number' ? minCacheTTL : null; - if (prev !== minCacheTTL) { - _auctions.refresh(); - } - }) - + const _auctions = []; const auctionManager = {}; - function getAuction(auctionId) { - for (const auction of _auctions) { - if (auction.getAuctionId() === auctionId) return auction; - } - } - auctionManager.addWinningBid = function(bid) { const metrics = useMetrics(bid.metrics); metrics.checkpoint('bidWon'); metrics.timeBetween('auctionEnd', 'bidWon', 'render.pending'); metrics.timeBetween('requestBids', 'bidWon', 'render.e2e'); - const auction = getAuction(bid.auctionId); + const auction = find(_auctions, auction => auction.getAuctionId() === bid.auctionId); if (auction) { bid.status = CONSTANTS.BID_STATUS.RENDERED; auction.addWinningBid(bid); @@ -82,44 +50,48 @@ export function newAuctionManager() { } }; - Object.entries({ - getAllWinningBids: { - name: 'getWinningBids', - }, - getBidsRequested: { - name: 'getBidRequests' - }, - getNoBids: {}, - getAdUnits: {}, - getBidsReceived: { - pre(auction) { - return auction.getAuctionStatus() === AUCTION_COMPLETED; - } - }, - getAdUnitCodes: { - post: uniques, - } - }).forEach(([mgrMethod, {name = mgrMethod, pre, post}]) => { - const mapper = pre == null - ? (auction) => auction[name]() - : (auction) => pre(auction) ? auction[name]() : []; - const filter = post == null - ? (items) => items - : (items) => items.filter(post) - auctionManager[mgrMethod] = () => { - return filter(_auctions.toArray().flatMap(mapper)); - } - }) + auctionManager.getAllWinningBids = function() { + return _auctions.map(auction => auction.getWinningBids()) + .reduce(flatten, []); + }; - function allBidsReceived() { - return _auctions.toArray().flatMap(au => au.getBidsReceived()) - } + auctionManager.getBidsRequested = function() { + return _auctions.map(auction => auction.getBidRequests()) + .reduce(flatten, []); + }; + + auctionManager.getNoBids = function() { + return _auctions.map(auction => auction.getNoBids()) + .reduce(flatten, []); + }; + + auctionManager.getBidsReceived = function() { + return _auctions.map((auction) => { + if (auction.getAuctionStatus() === AUCTION_COMPLETED) { + return auction.getBidsReceived(); + } + }).reduce(flatten, []) + .filter(bid => bid); + }; auctionManager.getAllBidsForAdUnitCode = function(adUnitCode) { - return allBidsReceived() + return _auctions.map((auction) => { + return auction.getBidsReceived(); + }).reduce(flatten, []) .filter(bid => bid && bid.adUnitCode === adUnitCode) }; + auctionManager.getAdUnits = function() { + return _auctions.map(auction => auction.getAdUnits()) + .reduce(flatten, []); + }; + + auctionManager.getAdUnitCodes = function() { + return _auctions.map(auction => auction.getAdUnitCodes()) + .reduce(flatten, []) + .filter(uniques); + }; + auctionManager.createAuction = function(opts) { const auction = newAuction(opts); _addAuction(auction); @@ -127,8 +99,7 @@ export function newAuctionManager() { }; auctionManager.findBidByAdId = function(adId) { - return allBidsReceived() - .find(bid => bid.adId === adId); + return find(_auctions.map(auction => auction.getBidsReceived()).reduce(flatten, []), bid => bid.adId === adId); }; auctionManager.getStandardBidderAdServerTargeting = function() { @@ -140,25 +111,24 @@ export function newAuctionManager() { if (bid) bid.status = status; if (bid && status === CONSTANTS.BID_STATUS.BID_TARGETING_SET) { - const auction = getAuction(bid.auctionId); + const auction = find(_auctions, auction => auction.getAuctionId() === bid.auctionId); if (auction) auction.setBidTargeting(bid); } } auctionManager.getLastAuctionId = function() { - const auctions = _auctions.toArray(); - return auctions.length && auctions[auctions.length - 1].getAuctionId() + return _auctions.length && _auctions[_auctions.length - 1].getAuctionId() }; auctionManager.clearAllAuctions = function() { - _auctions.clear(); + _auctions.length = 0; } function _addAuction(auction) { - _auctions.add(auction); + _auctions.push(auction); } - auctionManager.index = new AuctionIndex(() => _auctions.toArray()); + auctionManager.index = new AuctionIndex(() => _auctions); return auctionManager; } diff --git a/src/bidTTL.js b/src/bidTTL.js deleted file mode 100644 index 55ba0c026b0..00000000000 --- a/src/bidTTL.js +++ /dev/null @@ -1,25 +0,0 @@ -import {config} from './config.js'; -import {logError} from './utils.js'; -let TTL_BUFFER = 1; - -const listeners = []; - -config.getConfig('ttlBuffer', (cfg) => { - if (typeof cfg.ttlBuffer === 'number') { - const prev = TTL_BUFFER; - TTL_BUFFER = cfg.ttlBuffer; - if (prev !== TTL_BUFFER) { - listeners.forEach(l => l(TTL_BUFFER)) - } - } else { - logError('Invalid value for ttlBuffer', cfg.ttlBuffer); - } -}) - -export function getTTL(bid) { - return bid.ttl - (bid.hasOwnProperty('ttlBuffer') ? bid.ttlBuffer : TTL_BUFFER); -} - -export function onTTLBufferChange(listener) { - listeners.push(listener); -} diff --git a/src/config.js b/src/config.js index e3bb5f146ed..48909d677e9 100644 --- a/src/config.js +++ b/src/config.js @@ -12,20 +12,11 @@ * @property {(string|Object)} [video-outstream] */ -import {isValidPriceConfig} from './cpmBucketManager.js'; -import {arrayFrom as from, find, includes} from './polyfill.js'; +import { isValidPriceConfig } from './cpmBucketManager.js'; +import {find, includes, arrayFrom as from} from './polyfill.js'; import { - deepAccess, - deepClone, - getParameterByName, - isArray, - isBoolean, - isPlainObject, - isStr, - logError, - logMessage, - logWarn, - mergeDeep + mergeDeep, deepClone, getParameterByName, isPlainObject, logMessage, logWarn, logError, + isArray, isStr, isBoolean, deepAccess, bind } from './utils.js'; import CONSTANTS from './constants.json'; @@ -59,6 +50,13 @@ const GRANULARITY_OPTIONS = { const ALL_TOPICS = '*'; +/** + * @typedef {object} PrebidConfig + * + * @property {string} cache.url Set a url if we should use prebid-cache to store video bids before adding + * bids to the auction. **NOTE** This must be set if you want to use the dfpAdServerVideo module. + */ + export function newConfig() { let listeners = []; let defaults; @@ -507,7 +505,7 @@ export function newConfig() { return function(cb) { return function(...args) { if (typeof cb === 'function') { - return runWithBidder(bidder, cb.bind(this, ...args)) + return runWithBidder(bidder, bind.call(cb, this, ...args)) } else { logWarn('config.callbackWithBidder callback is not a function'); } @@ -544,8 +542,4 @@ export function newConfig() { }; } -/** - * Set a `cache.url` if we should use prebid-cache to store video bids before adding bids to the auction. - * This must be set if you want to use the dfpAdServerVideo module. - */ export const config = newConfig(); diff --git a/src/consentHandler.js b/src/consentHandler.js index 5b5d8b805cd..4776a8ece02 100644 --- a/src/consentHandler.js +++ b/src/consentHandler.js @@ -1,6 +1,5 @@ -import {cyrb53Hash, isStr, timestamp} from './utils.js'; +import {isStr, timestamp} from './utils.js'; import {defer, GreedyPromise} from './utils/promise.js'; -import {config} from './config.js'; /** * Placeholder gvlid for when vendor consent is not required. When this value is used as gvlid, the gdpr @@ -10,23 +9,12 @@ import {config} from './config.js'; */ export const VENDORLESS_GVLID = Object.freeze({}); -/** - * Placeholder gvlid for when device.ext.cdep is present (Privacy Sandbox cookie deprecation label). When this value is used as gvlid, the gdpr - * enforcement module will look to see that publisher consent was given. - * - * see https://github.com/prebid/Prebid.js/issues/10516 - */ -export const FIRST_PARTY_GVLID = Object.freeze({}); - export class ConsentHandler { #enabled; #data; #defer; #ready; - #dirty = true; - #hash; generatedTime; - hashFields; constructor() { this.reset(); @@ -86,24 +74,15 @@ export class ConsentHandler { setConsentData(data, time = timestamp()) { this.generatedTime = time; - this.#dirty = true; this.#resolve(data); } getConsentData() { return this.#data; } - - get hash() { - if (this.#dirty) { - this.#hash = cyrb53Hash(JSON.stringify(this.#data && this.hashFields ? this.hashFields.map(f => this.#data[f]) : this.#data)) - this.#dirty = false; - } - return this.#hash; - } } -class UspConsentHandler extends ConsentHandler { +export class UspConsentHandler extends ConsentHandler { getConsentMeta() { const consentData = this.getConsentData(); if (consentData && this.generatedTime) { @@ -115,8 +94,7 @@ class UspConsentHandler extends ConsentHandler { } } -class GdprConsentHandler extends ConsentHandler { - hashFields = ['gdprApplies', 'consentString'] +export class GdprConsentHandler extends ConsentHandler { getConsentMeta() { const consentData = this.getConsentData(); if (consentData && consentData.vendorData && this.generatedTime) { @@ -130,8 +108,7 @@ class GdprConsentHandler extends ConsentHandler { } } -class GppConsentHandler extends ConsentHandler { - hashFields = ['applicableSections', 'gppString']; +export class GppConsentHandler extends ConsentHandler { getConsentMeta() { const consentData = this.getConsentData(); if (consentData && this.generatedTime) { @@ -182,55 +159,4 @@ export function gvlidRegistry() { } } -export const gdprDataHandler = new GdprConsentHandler(); -export const uspDataHandler = new UspConsentHandler(); -export const gppDataHandler = new GppConsentHandler(); -export const coppaDataHandler = (() => { - function getCoppa() { - return !!(config.getConfig('coppa')) - } - return { - getCoppa, - getConsentData: getCoppa, - getConsentMeta: getCoppa, - reset() {}, - get promise() { - return GreedyPromise.resolve(getCoppa()) - }, - get hash() { - return getCoppa() ? '1' : '0' - } - } -})(); - export const GDPR_GVLIDS = gvlidRegistry(); - -const ALL_HANDLERS = { - gdpr: gdprDataHandler, - usp: uspDataHandler, - gpp: gppDataHandler, - coppa: coppaDataHandler, -} - -export function multiHandler(handlers = ALL_HANDLERS) { - handlers = Object.entries(handlers); - function collector(method) { - return function () { - return Object.fromEntries(handlers.map(([name, handler]) => [name, handler[method]()])) - } - } - return Object.assign( - { - get promise() { - return GreedyPromise.all(handlers.map(([name, handler]) => handler.promise.then(val => [name, val]))) - .then(entries => Object.fromEntries(entries)); - }, - get hash() { - return cyrb53Hash(handlers.map(([_, handler]) => handler.hash).join(':')); - } - }, - Object.fromEntries(['getConsentData', 'getConsentMeta', 'reset'].map(n => [n, collector(n)])), - ) -} - -export const allConsent = multiHandler(); diff --git a/src/constants.json b/src/constants.json index 24882c160f8..afc4503cbfb 100644 --- a/src/constants.json +++ b/src/constants.json @@ -23,7 +23,6 @@ }, "EVENTS": { "AUCTION_INIT": "auctionInit", - "AUCTION_TIMEOUT": "auctionTimeout", "AUCTION_END": "auctionEnd", "BID_ADJUSTMENT": "bidAdjustment", "BID_TIMEOUT": "bidTimeout", @@ -46,8 +45,7 @@ "AUCTION_DEBUG": "auctionDebug", "BID_VIEWABLE": "bidViewable", "STALE_RENDER": "staleRender", - "BILLABLE_EVENT": "billableEvent", - "BID_ACCEPTED": "bidAccepted" + "BILLABLE_EVENT": "billableEvent" }, "AD_RENDER_FAILED_REASON": { "PREVENT_WRITING_ON_MAIN_DOCUMENT": "preventWritingOnMainDocument", @@ -79,9 +77,7 @@ "CACHE_ID": "hb_cache_id", "CACHE_HOST": "hb_cache_host", "ADOMAIN": "hb_adomain", - "ACAT": "hb_acat", - "CRID": "hb_crid", - "DSP": "hb_dsp" + "ACAT": "hb_acat" }, "DEFAULT_TARGETING_KEYS": { "BIDDER": "hb_bidder", @@ -165,20 +161,11 @@ "MAIN": 3 }, "NATIVE_KEYS_THAT_ARE_NOT_ASSETS": [ - "privacyIcon", + "privacyLink", "clickUrl", "sendTargetingKeys", "adTemplate", "rendererUrl", "type" - ], - "FLOOR_VALUES": { - "NO_DATA": "noData", - "AD_UNIT": "adUnit", - "SET_CONFIG": "setConfig", - "FETCH": "fetch", - "SUCCESS": "success", - "ERROR": "error", - "TIMEOUT": "timeout" - } + ] } diff --git a/src/events.js b/src/events.js index d98991180bf..62f8c070deb 100644 --- a/src/events.js +++ b/src/events.js @@ -3,38 +3,23 @@ */ import * as utils from './utils.js' import CONSTANTS from './constants.json'; -import {ttlCollection} from './utils/ttlCollection.js'; -import {config} from './config.js'; -const TTL_CONFIG = 'eventHistoryTTL'; -let eventTTL = null; - -// keep a record of all events fired -const eventsFired = ttlCollection({ - monotonic: true, - ttl: () => eventTTL, -}) - -config.getConfig(TTL_CONFIG, (val) => { - const previous = eventTTL; - val = val?.[TTL_CONFIG]; - eventTTL = typeof val === 'number' ? val * 1000 : null; - if (previous !== eventTTL) { - eventsFired.refresh(); - } -}); - -let slice = Array.prototype.slice; -let push = Array.prototype.push; +var slice = Array.prototype.slice; +var push = Array.prototype.push; // define entire events -let allEvents = Object.values(CONSTANTS.EVENTS); +// var allEvents = ['bidRequested','bidResponse','bidWon','bidTimeout']; +var allEvents = utils._map(CONSTANTS.EVENTS, function (v) { + return v; +}); -const idPaths = CONSTANTS.EVENT_ID_PATHS; +var idPaths = CONSTANTS.EVENT_ID_PATHS; +// keep a record of all events fired +var eventsFired = []; const _public = (function () { - let _handlers = {}; - let _public = {}; + var _handlers = {}; + var _public = {}; /** * @@ -45,30 +30,31 @@ const _public = (function () { function _dispatch(eventString, args) { utils.logMessage('Emitting event for: ' + eventString); - let eventPayload = args[0] || {}; - let idPath = idPaths[eventString]; - let key = eventPayload[idPath]; - let event = _handlers[eventString] || { que: [] }; - var eventKeys = Object.keys(event); + var eventPayload = args[0] || {}; + var idPath = idPaths[eventString]; + var key = eventPayload[idPath]; + var event = _handlers[eventString] || { que: [] }; + var eventKeys = utils._map(event, function (v, k) { + return k; + }); - let callbacks = []; + var callbacks = []; // record the event: - eventsFired.add({ + eventsFired.push({ eventType: eventString, args: eventPayload, id: key, elapsedTime: utils.getPerformanceNow(), }); - /** - * Push each specific callback to the `callbacks` array. + /** Push each specific callback to the `callbacks` array. * If the `event` map has a key that matches the value of the * event payload id path, e.g. `eventPayload[idPath]`, then apply * each function in the `que` array as an argument to push to the * `callbacks` array - */ - if (key && eventKeys.includes(key)) { + * */ + if (key && utils.contains(eventKeys, key)) { push.apply(callbacks, event[key].que); } @@ -76,26 +62,24 @@ const _public = (function () { push.apply(callbacks, event.que); /** call each of the callbacks */ - (callbacks || []).forEach(function (fn) { + utils._each(callbacks, function (fn) { if (!fn) return; try { fn.apply(null, args); } catch (e) { - utils.logError('Error executing handler:', 'events.js', e, eventString); + utils.logError('Error executing handler:', 'events.js', e); } }); } function _checkAvailableEvent(event) { - return allEvents.includes(event) + return utils.contains(allEvents, event); } - _public.has = _checkAvailableEvent; - _public.on = function (eventString, handler, id) { // check whether available event or not if (_checkAvailableEvent(eventString)) { - let event = _handlers[eventString] || { que: [] }; + var event = _handlers[eventString] || { que: [] }; if (id) { event[id] = event[id] || { que: [] }; @@ -111,12 +95,12 @@ const _public = (function () { }; _public.emit = function (event) { - let args = slice.call(arguments, 1); + var args = slice.call(arguments, 1); _dispatch(event, args); }; _public.off = function (eventString, handler, id) { - let event = _handlers[eventString]; + var event = _handlers[eventString]; if (utils.isEmpty(event) || (utils.isEmpty(event.que) && utils.isEmpty(event[id]))) { return; @@ -127,15 +111,15 @@ const _public = (function () { } if (id) { - (event[id].que || []).forEach(function (_handler) { - let que = event[id].que; + utils._each(event[id].que, function (_handler) { + var que = event[id].que; if (_handler === handler) { que.splice(que.indexOf(_handler), 1); } }); } else { - (event.que || []).forEach(function (_handler) { - let que = event.que; + utils._each(event.que, function (_handler) { + var que = event.que; if (_handler === handler) { que.splice(que.indexOf(_handler), 1); } @@ -158,7 +142,13 @@ const _public = (function () { * @return {Array} array of events fired */ _public.getEvents = function () { - return eventsFired.toArray().map(val => Object.assign({}, val)) + var arrayCopy = []; + utils._each(eventsFired, function (value) { + var newProp = Object.assign({}, value); + arrayCopy.push(newProp); + }); + + return arrayCopy; }; return _public; @@ -166,8 +156,8 @@ 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} = _public; export function clearEvents() { - eventsFired.clear(); + eventsFired.length = 0; } diff --git a/src/fpd/enrichment.js b/src/fpd/enrichment.js index 911509455e0..f812d8435d9 100644 --- a/src/fpd/enrichment.js +++ b/src/fpd/enrichment.js @@ -6,10 +6,6 @@ import {config} from '../config.js'; import {getHighEntropySUA, getLowEntropySUA} from './sua.js'; import {GreedyPromise} 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'; export const dep = { getRefererInfo, @@ -28,10 +24,8 @@ const oneClient = clientSectionChecker('FPD') * @returns: {Promise[{}]}: a promise to an enriched ortb2 object. */ export const enrichFPD = hook('sync', (fpd) => { - const promArr = [fpd, getSUA().catch(() => null), tryToGetCdepLabel().catch(() => null)]; - - return GreedyPromise.all(promArr) - .then(([ortb2, sua, cdep]) => { + return GreedyPromise.all([fpd, getSUA().catch(() => null)]) + .then(([ortb2, sua]) => { const ri = dep.getRefererInfo(); mergeLegacySetConfigs(ortb2); Object.entries(ENRICHMENTS).forEach(([section, getEnrichments]) => { @@ -40,18 +34,9 @@ export const enrichFPD = hook('sync', (fpd) => { ortb2[section] = mergeDeep({}, data, ortb2[section]); } }); - if (sua) { deepSetValue(ortb2, 'device.sua', Object.assign({}, sua, ortb2.device.sua)); } - - if (cdep) { - const ext = { - cdep - } - deepSetValue(ortb2, 'device.ext', Object.assign({}, ext, ortb2.device.ext)); - } - ortb2 = oneClient(ortb2); for (let section of CLIENT_SECTIONS) { if (hasSection(ortb2, section)) { @@ -59,7 +44,6 @@ export const enrichFPD = hook('sync', (fpd) => { break; } } - return ortb2; }); }); @@ -94,10 +78,6 @@ function removeUndef(obj) { return getDefinedParams(obj, Object.keys(obj)) } -function tryToGetCdepLabel() { - return GreedyPromise.resolve('cookieDeprecationLabel' in navigator && isActivityAllowed(ACTIVITY_ACCESS_DEVICE, activityParams(MODULE_TYPE_PREBID, 'cdep')) && navigator.cookieDeprecationLabel.getValue()); -} - const ENRICHMENTS = { site(ortb2, ri) { if (CLIENT_SECTIONS.filter(p => p !== 'site').some(hasSection.bind(null, ortb2))) { @@ -113,7 +93,6 @@ const ENRICHMENTS = { return winFallback((win) => { const w = win.innerWidth || win.document.documentElement.clientWidth || win.document.body.clientWidth; const h = win.innerHeight || win.document.documentElement.clientHeight || win.document.body.clientHeight; - return { w, h, diff --git a/src/fpd/sua.js b/src/fpd/sua.js index 565c3e1fd52..30b2be4c13b 100644 --- a/src/fpd/sua.js +++ b/src/fpd/sua.js @@ -15,12 +15,6 @@ export const HIGH_ENTROPY_HINTS = [ 'fullVersionList' ] -export const LOW_ENTROPY_HINTS = [ - 'brands', - 'mobile', - 'platform' -] - /** * Returns low entropy UA client hints encoded as an ortb2.6 device.sua object; or null if no UA client hints are available. */ @@ -38,7 +32,7 @@ export const getLowEntropySUA = lowEntropySUAAccessor(); export const getHighEntropySUA = highEntropySUAAccessor(); export function lowEntropySUAAccessor(uaData = window.navigator?.userAgentData) { - const sua = (uaData && LOW_ENTROPY_HINTS.some(h => typeof uaData[h] !== 'undefined')) ? Object.freeze(uaDataToSUA(SUA_SOURCE_LOW_ENTROPY, uaData)) : null; + const sua = isEmpty(uaData) ? null : Object.freeze(uaDataToSUA(SUA_SOURCE_LOW_ENTROPY, uaData)); return function () { return sua; } @@ -91,7 +85,7 @@ export function uaDataToSUA(source, uaData) { if (uaData.fullVersionList || uaData.brands) { sua.browsers = (uaData.fullVersionList || uaData.brands).map(({brand, version}) => toBrandVersion(brand, version)); } - if (typeof uaData['mobile'] !== 'undefined') { + if (uaData.hasOwnProperty('mobile')) { sua.mobile = uaData.mobile ? 1 : 0; } ['model', 'bitness', 'architecture'].forEach(prop => { diff --git a/src/mediaTypes.js b/src/mediaTypes.js index 2afa2aefaf9..eea286f7af5 100644 --- a/src/mediaTypes.js +++ b/src/mediaTypes.js @@ -10,11 +10,11 @@ * @typedef {('adpod')} VideoContext */ -/** @type {MediaType} */ +/** @type MediaType */ export const NATIVE = 'native'; -/** @type {MediaType} */ +/** @type MediaType */ export const VIDEO = 'video'; -/** @type {MediaType} */ +/** @type MediaType */ export const BANNER = 'banner'; -/** @type {VideoContext} */ +/** @type VideoContext */ export const ADPOD = 'adpod'; diff --git a/src/native.js b/src/native.js index affdc855353..927423c8d72 100644 --- a/src/native.js +++ b/src/native.js @@ -1,6 +1,7 @@ import { deepAccess, deepClone, + getKeyByValue, insertHtmlIntoIframe, isArray, isBoolean, @@ -16,11 +17,6 @@ import {auctionManager} from './auctionManager.js'; import CONSTANTS from './constants.json'; import {NATIVE} from './mediaTypes.js'; -/** - * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest - * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid - */ - export const nativeAdapters = []; export const NATIVE_TARGETING_KEYS = Object.keys(CONSTANTS.NATIVE_KEYS).map( @@ -426,14 +422,12 @@ function assetsMessage(data, adObject, keys, {index = auctionManager.index} = {} return message; } -const NATIVE_KEYS_INVERTED = Object.fromEntries(Object.entries(CONSTANTS.NATIVE_KEYS).map(([k, v]) => [v, k])); - /** * Constructs a message object containing asset values for each of the * requested data keys. */ export function getAssetMessage(data, adObject) { - const keys = data.assets.map((k) => NATIVE_KEYS_INVERTED[k]); + const keys = data.assets.map((k) => getKeyByValue(CONSTANTS.NATIVE_KEYS, k)); return assetsMessage(data, adObject, keys); } @@ -486,11 +480,6 @@ export function toOrtbNativeRequest(legacyNativeAssets) { continue; } - if (key === 'privacyLink') { - ortb.privacy = 1; - continue; - } - const asset = legacyNativeAssets[key]; let required = 0; if (asset.required && isBoolean(asset.required)) { @@ -634,9 +623,6 @@ export function fromOrtbNativeRequest(openRTBRequest) { oldNativeObject[prebidAssetName].len = asset.data.len; } } - if (openRTBRequest.privacy) { - oldNativeObject.privacyLink = { required: false }; - } // video was not supported by old prebid assets } return oldNativeObject; @@ -710,11 +696,8 @@ export function legacyPropertiesToOrtbNative(legacyNative) { // in general, native trackers seem to be neglected and/or broken response.jstracker = Array.isArray(value) ? value.join('') : value; break; - case 'privacyLink': - response.privacy = value; - break; } - }); + }) return response; } @@ -797,8 +780,8 @@ export function toLegacyResponse(ortbResponse, ortbRequest) { legacyResponse.impressionTrackers = []; let jsTrackers = []; - if (ortbResponse.imptrackers) { - legacyResponse.impressionTrackers.push(...ortbResponse.imptrackers); + if (ortbRequest?.imptrackers) { + legacyResponse.impressionTrackers.push(...ortbRequest.imptrackers); } for (const eventTracker of ortbResponse?.eventtrackers || []) { if (eventTracker.event === TRACKER_EVENTS.impression && eventTracker.method === TRACKER_METHODS.img) { diff --git a/src/prebid.js b/src/prebid.js index df224ab83cf..b949ece65ea 100644 --- a/src/prebid.js +++ b/src/prebid.js @@ -2,11 +2,19 @@ import {getGlobal} from './prebidGlobal.js'; import { + adUnitsFilter, + bind, + callBurl, + contains, + createInvisibleIframe, deepAccess, deepClone, deepSetValue, flatten, generateUUID, + getHighestCpm, + inIframe, + insertElement, isArray, isArrayOfNums, isEmpty, @@ -18,6 +26,8 @@ import { logMessage, logWarn, mergeDeep, + replaceAuctionPrice, + replaceClickThrough, transformAdServerTargetingObj, uniques, unsupportedBidderMessage @@ -31,24 +41,23 @@ import {hook, wrapHook} from './hook.js'; import {loadSession} from './debugging.js'; import {includes} from './polyfill.js'; import {adunitCounter} from './adUnits.js'; +import {executeRenderer, isRendererRequired} from './Renderer.js'; import {createBid} from './bidfactory.js'; import {storageCallbacks} from './storageManager.js'; -import {default as adapterManager, getS2SBidderSet} from './adapterManager.js'; +import {emitAdRenderFail, emitAdRenderSucceeded} from './adRendering.js'; +import {default as adapterManager, gdprDataHandler, getS2SBidderSet, gppDataHandler, uspDataHandler} from './adapterManager.js'; import CONSTANTS from './constants.json'; import * as events from './events.js'; import {newMetrics, useMetrics} from './utils/perfMetrics.js'; import {defer, GreedyPromise} from './utils/promise.js'; import {enrichFPD} from './fpd/enrichment.js'; -import {allConsent} from './consentHandler.js'; -import {renderAdDirect} from '../libraries/creativeRender/direct.js'; -import {getHighestCpm} from './utils/reducers.js'; -import {fillVideoDefaults} from './video.js'; const pbjsInstance = getGlobal(); const { triggerUserSyncs } = userSync; /* private variables */ -const { ADD_AD_UNITS, REQUEST_BIDS, SET_TARGETING } = CONSTANTS.EVENTS; +const { ADD_AD_UNITS, BID_WON, REQUEST_BIDS, SET_TARGETING, STALE_RENDER } = CONSTANTS.EVENTS; +const { PREVENT_WRITING_ON_MAIN_DOCUMENT, NO_AD, EXCEPTION, CANNOT_FIND_AD, MISSING_DOC_OR_ADID } = CONSTANTS.AD_RENDER_FAILED_REASON; const eventValidators = { bidWon: checkDefinedPlacement @@ -80,7 +89,7 @@ function checkDefinedPlacement(id) { .reduce(flatten) .filter(uniques); - if (!adUnitCodes.includes(id)) { + if (!contains(adUnitCodes, id)) { logError('The "' + id + '" placement is not defined.'); return; } @@ -88,6 +97,13 @@ function checkDefinedPlacement(id) { return true; } +function setRenderSize(doc, width, height) { + if (doc.defaultView && doc.defaultView.frameElement) { + doc.defaultView.frameElement.width = width; + doc.defaultView.frameElement.height = height; + } +} + function validateSizes(sizes, targLength) { let cleanSizes = []; if (isArray(sizes) && ((targLength) ? sizes.length === targLength : sizes.length > 0)) { @@ -252,12 +268,6 @@ export const checkAdUnitSetup = hook('sync', function (adUnits) { return validatedAdUnits; }, 'checkAdUnitSetup'); -function fillAdUnitDefaults(adUnits) { - if (FEATURES.VIDEO) { - adUnits.forEach(au => fillVideoDefaults(au)) - } -} - /// /////////////////////////////// // // // Start Public APIs // @@ -320,14 +330,28 @@ pbjsInstance.getAdserverTargeting = function (adUnitCode) { return targeting.getAllTargeting(adUnitCode); }; +/** + * returns all consent data + * @return {Object} Map of consent types and data + * @alias module:pbjs.getConsentData + */ +function getConsentMetadata() { + return { + gdpr: gdprDataHandler.getConsentMeta(), + usp: uspDataHandler.getConsentMeta(), + gpp: gppDataHandler.getConsentMeta(), + coppa: !!(config.getConfig('coppa')) + } +} + pbjsInstance.getConsentMetadata = function () { logInfo('Invoking $$PREBID_GLOBAL$$.getConsentMetadata'); - return allConsent.getConsentMeta() + return getConsentMetadata(); }; function getBids(type) { const responses = auctionManager[type]() - .filter(bid => auctionManager.getAdUnitCodes().includes(bid.adUnitCode)) + .filter(bind.call(adUnitsFilter, this, auctionManager.getAdUnitCodes())); // find the last auction id to get responses for most recent auction only const currentAuctionId = auctionManager.getLastAuctionId(); @@ -443,6 +467,19 @@ pbjsInstance.setTargetingForAst = function (adUnitCodes) { events.emit(SET_TARGETING, targeting.getAllTargeting()); }; +/** + * This function will check for presence of given node in given parent. If not present - will inject it. + * @param {Node} node node, whose existance is in question + * @param {Document} doc document element do look in + * @param {string} tagName tag name to look in + */ +function reinjectNodeIfRemoved(node, doc, tagName) { + const injectionNode = doc.querySelector(tagName); + if (!node.parentNode || node.parentNode !== injectionNode) { + insertElement(node, doc, tagName); + } +} + /** * This function will render the ad (based on params) in the given iframe document passed through. * Note that doc SHOULD NOT be the parent document page as we can't doc.write() asynchronously @@ -453,7 +490,103 @@ pbjsInstance.setTargetingForAst = function (adUnitCodes) { pbjsInstance.renderAd = hook('async', function (doc, id, options) { logInfo('Invoking $$PREBID_GLOBAL$$.renderAd', arguments); logMessage('Calling renderAd with adId :' + id); - renderAdDirect(doc, id, options); + + if (!id) { + const message = `Error trying to write ad Id :${id} to the page. Missing adId`; + emitAdRenderFail({ reason: MISSING_DOC_OR_ADID, message, id }); + return; + } + + try { + // lookup ad by ad Id + const bid = auctionManager.findBidByAdId(id); + if (!bid) { + const message = `Error trying to write ad. Cannot find ad by given id : ${id}`; + emitAdRenderFail({ reason: CANNOT_FIND_AD, message, id }); + return; + } + + if (bid.status === CONSTANTS.BID_STATUS.RENDERED) { + logWarn(`Ad id ${bid.adId} has been rendered before`); + events.emit(STALE_RENDER, bid); + if (deepAccess(config.getConfig('auctionOptions'), 'suppressStaleRender')) { + return; + } + } + + // replace macros according to openRTB with price paid = bid.cpm + bid.ad = replaceAuctionPrice(bid.ad, bid.originalCpm || bid.cpm); + bid.adUrl = replaceAuctionPrice(bid.adUrl, bid.originalCpm || bid.cpm); + // replacing clickthrough if submitted + if (options && options.clickThrough) { + const {clickThrough} = options; + bid.ad = replaceClickThrough(bid.ad, clickThrough); + bid.adUrl = replaceClickThrough(bid.adUrl, clickThrough); + } + + // save winning bids + auctionManager.addWinningBid(bid); + + // emit 'bid won' event here + events.emit(BID_WON, bid); + + const {height, width, ad, mediaType, adUrl, renderer} = bid; + + // video module + if (FEATURES.VIDEO) { + const adUnitCode = bid.adUnitCode; + const adUnit = pbjsInstance.adUnits.filter(adUnit => adUnit.code === adUnitCode); + const videoModule = pbjsInstance.videoModule; + if (adUnit.video && videoModule) { + videoModule.renderBid(adUnit.video.divId, bid); + return; + } + } + + if (!doc) { + const message = `Error trying to write ad Id :${id} to the page. Missing document`; + emitAdRenderFail({ reason: MISSING_DOC_OR_ADID, message, id }); + return; + } + + const creativeComment = document.createComment(`Creative ${bid.creativeId} served by ${bid.bidder} Prebid.js Header Bidding`); + insertElement(creativeComment, doc, 'html'); + + if (isRendererRequired(renderer)) { + executeRenderer(renderer, bid, doc); + reinjectNodeIfRemoved(creativeComment, doc, 'html'); + emitAdRenderSucceeded({ doc, bid, id }); + } else if ((doc === document && !inIframe()) || mediaType === 'video') { + const message = `Error trying to write ad. Ad render call ad id ${id} was prevented from writing to the main document.`; + emitAdRenderFail({reason: PREVENT_WRITING_ON_MAIN_DOCUMENT, message, bid, id}); + } else if (ad) { + doc.write(ad); + doc.close(); + setRenderSize(doc, width, height); + reinjectNodeIfRemoved(creativeComment, doc, 'html'); + callBurl(bid); + emitAdRenderSucceeded({ doc, bid, id }); + } else if (adUrl) { + const iframe = createInvisibleIframe(); + iframe.height = height; + iframe.width = width; + iframe.style.display = 'inline'; + iframe.style.overflow = 'hidden'; + iframe.src = adUrl; + + insertElement(iframe, doc, 'body'); + setRenderSize(doc, width, height); + reinjectNodeIfRemoved(creativeComment, doc, 'html'); + callBurl(bid); + emitAdRenderSucceeded({ doc, bid, id }); + } else { + const message = `Error trying to write ad. No ad for bid response id: ${id}`; + emitAdRenderFail({reason: NO_AD, message, bid, id}); + } + } catch (e) { + const message = `Error trying to write ad Id :${id} to the page:${e.message}`; + emitAdRenderFail({ reason: EXCEPTION, message, id }); + } }); /** @@ -538,7 +671,6 @@ pbjsInstance.requestBids = (function() { export const startAuction = hook('async', function ({ bidsBackHandler, timeout: cbTimeout, adUnits, ttlBuffer, adUnitCodes, labels, auctionId, ortb2Fragments, metrics, defer } = {}) { const s2sBidders = getS2SBidderSet(config.getConfig('s2sConfig') || []); - fillAdUnitDefaults(adUnits); adUnits = useMetrics(metrics).measureTime('requestBids.validate', () => checkAdUnitSetup(adUnits)); function auctionDone(bids, timedOut, auctionId) { diff --git a/src/secureCreatives.js b/src/secureCreatives.js index 0ea93e7e4fb..c719bc191f2 100644 --- a/src/secureCreatives.js +++ b/src/secureCreatives.js @@ -6,24 +6,26 @@ import * as events from './events.js'; import {fireNativeTrackers, getAllAssetsMessage, getAssetMessage} from './native.js'; import constants from './constants.json'; -import {isApnGetTagDefined, isGptPubadsDefined, logError, logWarn} from './utils.js'; +import {deepAccess, isApnGetTagDefined, isGptPubadsDefined, logError, logWarn, replaceAuctionPrice} from './utils.js'; import {auctionManager} from './auctionManager.js'; import {find, includes} from './polyfill.js'; -import {emitAdRenderFail, emitAdRenderSucceeded, handleRender} from './adRendering.js'; -import {PREBID_EVENT, PREBID_NATIVE, PREBID_REQUEST, PREBID_RESPONSE} from '../libraries/creativeRender/constants.js'; +import {executeRenderer, isRendererRequired} from './Renderer.js'; +import {config} from './config.js'; +import {emitAdRenderFail, emitAdRenderSucceeded} from './adRendering.js'; const BID_WON = constants.EVENTS.BID_WON; +const STALE_RENDER = constants.EVENTS.STALE_RENDER; const WON_AD_IDS = new WeakSet(); const HANDLER_MAP = { - [PREBID_REQUEST]: handleRenderRequest, - [PREBID_EVENT]: handleEventRequest, -}; + 'Prebid Request': handleRenderRequest, + 'Prebid Event': handleEventRequest, +} if (FEATURES.NATIVE) { Object.assign(HANDLER_MAP, { - [PREBID_NATIVE]: handleNativeRequest, - }); + 'Prebid Native': handleNativeRequest, + }) } export function listenMessagesFromCreative() { @@ -33,18 +35,18 @@ export function listenMessagesFromCreative() { export function getReplier(ev) { if (ev.origin == null && ev.ports.length === 0) { return function () { - const msg = 'Cannot post message to a frame with null origin. Please update creatives to use MessageChannel, see https://github.com/prebid/Prebid.js/issues/7870'; - logError(msg); + const msg = 'Cannot post message to a frame with null origin. Please update creatives to use MessageChannel, see https://github.com/prebid/Prebid.js/issues/7870' + logError(msg) throw new Error(msg); - }; + } } else if (ev.ports.length > 0) { return function (message) { ev.ports[0].postMessage(JSON.stringify(message)); - }; + } } else { return function (message) { ev.source.postMessage(JSON.stringify(message), ev.origin); - }; + } } } @@ -67,13 +69,39 @@ export function receiveMessage(ev) { } } -function handleRenderRequest(reply, message, bidResponse) { - handleRender(function (adData) { - resizeRemoteCreative(bidResponse); - reply(Object.assign({ - message: PREBID_RESPONSE, - }, adData)); - }, {options: message.options, adId: message.adId, bidResponse}); +function handleRenderRequest(reply, data, adObject) { + if (adObject == null) { + emitAdRenderFail({ + reason: constants.AD_RENDER_FAILED_REASON.CANNOT_FIND_AD, + message: `Cannot find ad for cross-origin render request: '${data.adId}'`, + id: data.adId + }); + return; + } + if (adObject.status === constants.BID_STATUS.RENDERED) { + logWarn(`Ad id ${adObject.adId} has been rendered before`); + events.emit(STALE_RENDER, adObject); + if (deepAccess(config.getConfig('auctionOptions'), 'suppressStaleRender')) { + return; + } + } + + try { + _sendAdToCreative(adObject, reply); + } catch (e) { + emitAdRenderFail({ + reason: constants.AD_RENDER_FAILED_REASON.EXCEPTION, + message: e.message, + id: data.adId, + bid: adObject + }); + return; + } + + // save winning bids + auctionManager.addWinningBid(adObject); + + events.emit(BID_WON, adObject); } function handleNativeRequest(reply, data, adObject) { @@ -136,11 +164,29 @@ function handleEventRequest(reply, data, adObject) { }); break; default: - logError(`Received x-origin event request for unsupported event: '${data.event}' (adId: '${data.adId}')`); + logError(`Received x-origin event request for unsupported event: '${data.event}' (adId: '${data.adId}')`) + } +} + +export function _sendAdToCreative(adObject, reply) { + const { adId, ad, adUrl, width, height, renderer, cpm, originalCpm } = adObject; + // rendering for outstream safeframe + if (isRendererRequired(renderer)) { + executeRenderer(renderer, adObject); + } else if (adId) { + resizeRemoteCreative(adObject); + reply({ + message: 'Prebid Response', + ad: replaceAuctionPrice(ad, originalCpm || cpm), + adUrl: replaceAuctionPrice(adUrl, originalCpm || cpm), + adId, + width, + height + }); } } -export function resizeRemoteCreative({adId, adUnitCode, width, height}) { +function resizeRemoteCreative({ adId, adUnitCode, width, height }) { // resize both container div + iframe ['div', 'iframe'].forEach(elmType => { // not select element that gets removed after dfp render @@ -162,9 +208,9 @@ export function resizeRemoteCreative({adId, adUnitCode, width, height}) { function getElementIdBasedOnAdServer(adId, adUnitCode) { if (isGptPubadsDefined()) { - return getDfpElementId(adId); + return getDfpElementId(adId) } else if (isApnGetTagDefined()) { - return getAstElementId(adUnitCode); + return getAstElementId(adUnitCode) } else { return adUnitCode; } diff --git a/src/targeting.js b/src/targeting.js index ddbc3cebaf3..a75c9a2b52f 100644 --- a/src/targeting.js +++ b/src/targeting.js @@ -1,6 +1,8 @@ import { deepAccess, deepClone, + getHighestCpm, + getOldestHighestCpmBid, groupBy, isAdUnitCodeMatchingSlot, isArray, @@ -22,12 +24,19 @@ import {hook} from './hook.js'; import {bidderSettings} from './bidderSettings.js'; import {find, includes} from './polyfill.js'; import CONSTANTS from './constants.json'; -import {getHighestCpm, getOldestHighestCpmBid} from './utils/reducers.js'; -import {getTTL} from './bidTTL.js'; var pbTargetingKeys = []; const MAX_DFP_KEYLENGTH = 20; +let DEFAULT_TTL_BUFFER = 1; + +config.getConfig('ttlBuffer', (cfg) => { + if (typeof cfg.ttlBuffer === 'number') { + DEFAULT_TTL_BUFFER = cfg.ttlBuffer; + } else { + logError('Invalid value for ttlBuffer', cfg.ttlBuffer); + } +}) const CFG_ALLOW_TARGETING_KEYS = `targetingControls.allowTargetingKeys`; const CFG_ADD_TARGETING_KEYS = `targetingControls.addTargetingKeys`; @@ -38,7 +47,7 @@ export const TARGETING_KEYS = Object.keys(CONSTANTS.TARGETING_KEYS).map( ); // return unexpired bids -const isBidNotExpired = (bid) => (bid.responseTimestamp + getTTL(bid) * 1000) > timestamp(); +const isBidNotExpired = (bid) => (bid.responseTimestamp + (bid.ttl - (bid.hasOwnProperty('ttlBuffer') ? bid.ttlBuffer : DEFAULT_TTL_BUFFER)) * 1000) > timestamp(); // return bids whose status is not set. Winning bids can only have a status of `rendered`. const isUnusedBid = (bid) => bid && ((bid.status && !includes([CONSTANTS.BID_STATUS.RENDERED], bid.status)) || !bid.status); @@ -85,26 +94,26 @@ export const getHighestCpmBidsFromBidPool = hook('sync', function(bidsReceived, }); /** - * A descending sort function that will sort the list of objects based on the following two dimensions: - * - bids with a deal are sorted before bids w/o a deal - * - then sort bids in each grouping based on the hb_pb value - * eg: the following list of bids would be sorted like: - * [{ - * "hb_adid": "vwx", - * "hb_pb": "28", - * "hb_deal": "7747" - * }, { - * "hb_adid": "jkl", - * "hb_pb": "10", - * "hb_deal": "9234" - * }, { - * "hb_adid": "stu", - * "hb_pb": "50" - * }, { - * "hb_adid": "def", - * "hb_pb": "2" - * }] - */ +* A descending sort function that will sort the list of objects based on the following two dimensions: +* - bids with a deal are sorted before bids w/o a deal +* - then sort bids in each grouping based on the hb_pb value +* eg: the following list of bids would be sorted like: +* [{ +* "hb_adid": "vwx", +* "hb_pb": "28", +* "hb_deal": "7747" +* }, { +* "hb_adid": "jkl", +* "hb_pb": "10", +* "hb_deal": "9234" +* }, { +* "hb_adid": "stu", +* "hb_pb": "50" +* }, { +* "hb_adid": "def", +* "hb_pb": "2" +* }] +*/ export function sortByDealAndPriceBucketOrCpm(useCpm = false) { return function(a, b) { if (a.adserverTargeting.hb_deal !== undefined && b.adserverTargeting.hb_deal === undefined) { @@ -470,12 +479,6 @@ export function newTargeting(auctionManager) { .filter(bid => deepAccess(bid, 'video.context') !== ADPOD) .filter(isBidUsable); - bidsReceived - .forEach(bid => { - bid.latestTargetedAuctionId = latestAuctionForAdUnit[bid.adUnitCode]; - return bid; - }); - return getHighestCpmBidsFromBidPool(bidsReceived, getOldestHighestCpmBid); } @@ -497,7 +500,7 @@ export function newTargeting(auctionManager) { }; /** - * @param {(string|string[])} adUnitCodes adUnitCode or array of adUnitCodes + * @param {(string|string[])} adUnitCode adUnitCode or array of adUnitCodes * Sets targeting for AST */ targeting.setTargetingForAst = function(adUnitCodes) { @@ -530,7 +533,7 @@ export function newTargeting(auctionManager) { /** * Get targeting key value pairs for winning bid. - * @param {string[]} adUnitCodes code array + * @param {string[]} AdUnit code array * @return {targetingArray} winning bids targeting */ function getWinningBidTargeting(adUnitCodes, bidsReceived) { @@ -630,7 +633,7 @@ export function newTargeting(auctionManager) { /** * Get custom targeting key value pairs for bids. - * @param {string[]} adUnitCodes code array + * @param {string[]} AdUnit code array * @return {targetingArray} bids with custom targeting defined in bidderSettings */ function getCustomBidTargeting(adUnitCodes, bidsReceived) { @@ -644,7 +647,7 @@ export function newTargeting(auctionManager) { /** * Get targeting key value pairs for non-winning bids. - * @param {string[]} adUnitCodes code array + * @param {string[]} AdUnit code array * @return {targetingArray} all non-winning bids targeting */ function getBidLandscapeTargeting(adUnitCodes, bidsReceived) { diff --git a/src/userSync.js b/src/userSync.js index 1b684de6de0..936836eb12e 100644 --- a/src/userSync.js +++ b/src/userSync.js @@ -182,8 +182,8 @@ export function newUserSync(deps) { * @function incrementAdapterBids * @summary Increment the count of user syncs queue for the adapter * @private - * @param {object} numAdapterBids The object contain counts for all adapters - * @param {string} bidder The name of the bidder adding a sync + * @params {object} numAdapterBids The object contain counts for all adapters + * @params {string} bidder The name of the bidder adding a sync * @returns {object} The updated version of numAdapterBids */ function incrementAdapterBids(numAdapterBids, bidder) { @@ -199,9 +199,10 @@ export function newUserSync(deps) { * @function registerSync * @summary Add sync for this bidder to a queue to be fired later * @public - * @param {string} type The type of the sync including image, iframe - * @param {string} bidder The name of the adapter. e.g. "rubicon" - * @param {string} url Either the pixel url or iframe url depending on the type + * @params {string} type The type of the sync including image, iframe + * @params {string} bidder The name of the adapter. e.g. "rubicon" + * @params {string} url Either the pixel url or iframe url depending on the type + * @example Using Image Sync * // registerSync(type, adapter, pixelUrl) * userSync.registerSync('image', 'rubicon', 'http://example.com/pixel') @@ -243,7 +244,7 @@ export function newUserSync(deps) { * @param {string} type The type of the sync; either image or iframe * @param {string} bidder The name of the adapter. e.g. "rubicon" * @returns {boolean} true => bidder is not allowed to register; false => bidder can register - */ + */ function shouldBidderBeBlocked(type, bidder) { let filterConfig = usConfig.filterSettings; @@ -308,7 +309,7 @@ export function newUserSync(deps) { * @function syncUsers * @summary Trigger all the user syncs based on publisher-defined timeout * @public - * @param {number} timeout The delay in ms before syncing data - default 0 + * @params {int} timeout The delay in ms before syncing data - default 0 */ publicApi.syncUsers = (timeout = 0) => { if (timeout) { @@ -357,7 +358,7 @@ export const userSync = newUserSync(Object.defineProperties({ * * @property {boolean} enableOverride * @property {boolean} syncEnabled - * @property {number} syncsPerBidder + * @property {int} syncsPerBidder * @property {string[]} enabledBidders * @property {Object} filterSettings */ diff --git a/src/utils.js b/src/utils.js index 67ed7339675..ece29732723 100644 --- a/src/utils.js +++ b/src/utils.js @@ -1,6 +1,6 @@ import {config} from './config.js'; import clone from 'just-clone'; -import {includes} from './polyfill.js'; +import {find, includes} from './polyfill.js'; import CONSTANTS from './constants.json'; import {GreedyPromise} from './utils/promise.js'; import {getGlobal} from './prebidGlobal.js'; @@ -8,6 +8,7 @@ import {getGlobal} from './prebidGlobal.js'; export { default as deepAccess } from 'dlv/index.js'; export { dset as deepSetValue } from 'dset'; +var tArr = 'Array'; var tStr = 'String'; var tFn = 'Function'; var tNumb = 'Number'; @@ -63,6 +64,17 @@ export function getPrebidInternal() { return prebidInternal; } +var uniqueRef = {}; +export let bind = function(a, b) { return b; }.bind(null, 1, uniqueRef)() === uniqueRef + ? Function.prototype.bind + : function(bind) { + var self = this; + var args = Array.prototype.slice.call(arguments, 1); + return function() { + return self.apply(bind, args.concat(Array.prototype.slice.call(arguments))); + }; + }; + /* utility method to get incremental integer starting from 1 */ var getIncrementalInteger = (function () { var count = 0; @@ -102,7 +114,19 @@ function _getRandomData() { } export function getBidIdParameter(key, paramsObj) { - return paramsObj?.[key] || ''; + if (paramsObj && paramsObj[key]) { + return paramsObj[key]; + } + + return ''; +} + +export function tryAppendQueryString(existingUrl, key, value) { + if (value) { + return existingUrl + key + '=' + encodeURIComponent(value) + '&'; + } + + return existingUrl; } // parse a query string object passed in bid params @@ -121,30 +145,84 @@ export function parseQueryStringParameters(queryObj) { export function transformAdServerTargetingObj(targeting) { // we expect to receive targeting for a single slot at a time if (targeting && Object.getOwnPropertyNames(targeting).length > 0) { - return Object.keys(targeting) - .map(key => `${key}=${encodeURIComponent(targeting[key])}`).join('&'); + return getKeys(targeting) + .map(key => `${key}=${encodeURIComponent(getValue(targeting, key))}`).join('&'); } else { return ''; } } +/** + * Read an adUnit object and return the sizes used in an [[728, 90]] format (even if they had [728, 90] defined) + * Preference is given to the `adUnit.mediaTypes.banner.sizes` object over the `adUnit.sizes` + * @param {object} adUnit one adUnit object from the normal list of adUnits + * @returns {Array.} array of arrays containing numeric sizes + */ +export function getAdUnitSizes(adUnit) { + if (!adUnit) { + return; + } + + let sizes = []; + if (adUnit.mediaTypes && adUnit.mediaTypes.banner && Array.isArray(adUnit.mediaTypes.banner.sizes)) { + let bannerSizes = adUnit.mediaTypes.banner.sizes; + if (Array.isArray(bannerSizes[0])) { + sizes = bannerSizes; + } else { + sizes.push(bannerSizes); + } + // TODO - remove this else block when we're ready to deprecate adUnit.sizes for bidders + } else if (Array.isArray(adUnit.sizes)) { + if (Array.isArray(adUnit.sizes[0])) { + sizes = adUnit.sizes; + } else { + sizes.push(adUnit.sizes); + } + } + return sizes; +} + /** * Parse a GPT-Style general size Array like `[[300, 250]]` or `"300x250,970x90"` into an array of sizes `["300x250"]` or '['300x250', '970x90']' * @param {(Array.|Array.)} sizeObj Input array or double array [300,250] or [[300,250], [728,90]] * @return {Array.} Array of strings like `["300x250"]` or `["300x250", "728x90"]` */ export function parseSizesInput(sizeObj) { + var parsedSizes = []; + + // if a string for now we can assume it is a single size, like "300x250" if (typeof sizeObj === 'string') { // multiple sizes will be comma-separated - return sizeObj.split(',').filter(sz => sz.match(/^(\d)+x(\d)+$/i)) + var sizes = sizeObj.split(','); + + // regular expression to match strigns like 300x250 + // start of line, at least 1 number, an "x" , then at least 1 number, and the then end of the line + var sizeRegex = /^(\d)+x(\d)+$/i; + if (sizes) { + for (var curSizePos in sizes) { + if (hasOwn(sizes, curSizePos) && sizes[curSizePos].match(sizeRegex)) { + parsedSizes.push(sizes[curSizePos]); + } + } + } } else if (typeof sizeObj === 'object') { - if (sizeObj.length === 2 && typeof sizeObj[0] === 'number' && typeof sizeObj[1] === 'number') { - return [parseGPTSingleSizeArray(sizeObj)]; - } else { - return sizeObj.map(parseGPTSingleSizeArray) + var sizeArrayLength = sizeObj.length; + + // don't process empty array + if (sizeArrayLength > 0) { + // if we are a 2 item array of 2 numbers, we must be a SingleSize array + if (sizeArrayLength === 2 && typeof sizeObj[0] === 'number' && typeof sizeObj[1] === 'number') { + parsedSizes.push(parseGPTSingleSizeArray(sizeObj)); + } else { + // otherwise, we must be a MultiSize array + for (var i = 0; i < sizeArrayLength; i++) { + parsedSizes.push(parseGPTSingleSizeArray(sizeObj[i])); + } + } } } - return []; + + return parsedSizes; } // Parse a GPT style single size array, (i.e [300, 250]) @@ -267,9 +345,6 @@ export function createInvisibleIframe() { f.frameBorder = '0'; f.src = 'about:blank'; f.style.display = 'none'; - f.style.height = '0px'; - f.style.width = '0px'; - f.allowtransparency = 'true'; return f; } @@ -300,7 +375,9 @@ export function isStr(object) { return isA(object, tStr); } -export const isArray = Array.isArray.bind(Array); +export function isArray(object) { + return isA(object, tArr); +} export function isNumber(object) { return isA(object, tNumb); @@ -325,7 +402,12 @@ export function isEmpty(object) { if (isArray(object) || isStr(object)) { return !(object.length > 0); } - return Object.keys(object).length <= 0; + + for (var k in object) { + if (hasOwnProperty.call(object, k)) return false; + } + + return true; } /** @@ -344,12 +426,38 @@ export function isEmptyStr(str) { * @param {Function(value, key, object)} fn */ export function _each(object, fn) { - if (isFn(object?.forEach)) return object.forEach(fn, this); - Object.entries(object || {}).forEach(([k, v]) => fn.call(this, v, k)); + if (isEmpty(object)) return; + if (isFn(object.forEach)) return object.forEach(fn, this); + + var k = 0; + var l = object.length; + + if (l > 0) { + for (; k < l; k++) fn(object[k], k, object); + } else { + for (k in object) { + if (hasOwnProperty.call(object, k)) fn.call(this, object[k], k); + } + } } export function contains(a, obj) { - return isFn(a?.includes) && a.includes(obj); + if (isEmpty(a)) { + return false; + } + + if (isFn(a.indexOf)) { + return a.indexOf(obj) !== -1; + } + + var i = a.length; + while (i--) { + if (a[i] === obj) { + return true; + } + } + + return false; } /** @@ -360,10 +468,24 @@ export function contains(a, obj) { * @return {Array} */ export function _map(object, callback) { - if (isFn(object?.map)) return object.map(callback); - return Object.entries(object || {}).map(([k, v]) => callback(v, k, object)) + if (isEmpty(object)) return []; + if (isFn(object.map)) return object.map(callback); + var output = []; + _each(object, function (value, key) { + output.push(callback(value, key, object)); + }); + + return output; } +export function hasOwn(objectToCheck, propertyToCheckFor) { + if (objectToCheck.hasOwnProperty) { + return objectToCheck.hasOwnProperty(propertyToCheckFor); + } else { + return (typeof objectToCheck[propertyToCheckFor] !== 'undefined') && (objectToCheck.constructor.prototype[propertyToCheckFor] !== objectToCheck[propertyToCheckFor]); + } +}; + /* * Inserts an element(elm) as targets child, by default as first child * @param {HTMLElement} elm @@ -446,14 +568,27 @@ export function insertHtmlIntoIframe(htmlCode) { if (!htmlCode) { return; } - const iframe = createInvisibleIframe(); + + let iframe = document.createElement('iframe'); + iframe.id = getUniqueIdentifierStr(); + iframe.width = 0; + iframe.height = 0; + iframe.hspace = '0'; + iframe.vspace = '0'; + iframe.marginWidth = '0'; + iframe.marginHeight = '0'; + iframe.style.display = 'none'; + iframe.style.height = '0px'; + iframe.style.width = '0px'; + iframe.scrolling = 'no'; + iframe.frameBorder = '0'; + iframe.allowtransparency = 'true'; + internal.insertElement(iframe, document, 'body'); - ((doc) => { - doc.open(); - doc.write(htmlCode); - doc.close(); - })(iframe.contentWindow.document); + iframe.contentWindow.document.open(); + iframe.contentWindow.document.write(htmlCode); + iframe.contentWindow.document.close(); } /** @@ -519,6 +654,19 @@ export function createTrackPixelIframeHtml(url, encodeUri = true, sandbox = '') `; } +export function getValueString(param, val, defaultValue) { + if (val === undefined || val === null) { + return defaultValue; + } + if (isStr(val)) { + return val; + } + if (isNumber(val)) { + return val.toString(); + } + internal.logWarn('Unsuported type for param: ' + param + ' required type: String'); +} + export function uniques(value, index, arry) { return arry.indexOf(value) === index; } @@ -531,14 +679,38 @@ export function getBidRequest(id, bidderRequests) { if (!id) { return; } - return bidderRequests.flatMap(br => br.bids) - .find(bid => ['bidId', 'adId', 'bid_id'].some(prop => bid[prop] === id)) + let bidRequest; + bidderRequests.some(bidderRequest => { + let result = find(bidderRequest.bids, bid => ['bidId', 'adId', 'bid_id'].some(type => bid[type] === id)); + if (result) { + bidRequest = result; + } + return result; + }); + return bidRequest; +} + +export function getKeys(obj) { + return Object.keys(obj); } export function getValue(obj, key) { return obj[key]; } +/** + * Get the key of an object for a given value + */ +export function getKeyByValue(obj, value) { + for (let prop in obj) { + if (obj.hasOwnProperty(prop)) { + if (obj[prop] === value) { + return prop; + } + } + } +} + export function getBidderCodes(adUnits = pbjsInstance.adUnits) { // this could memoize adUnits return adUnits.map(unit => unit.bids.map(bid => bid.bidder) @@ -557,6 +729,26 @@ export function isApnGetTagDefined() { } } +// This function will get highest cpm value bid, in case of tie it will return the bid with lowest timeToRespond +export const getHighestCpm = getHighestCpmCallback('timeToRespond', (previous, current) => previous > current); + +// This function will get the oldest hightest cpm value bid, in case of tie it will return the bid which came in first +// Use case for tie: https://github.com/prebid/Prebid.js/issues/2448 +export const getOldestHighestCpmBid = getHighestCpmCallback('responseTimestamp', (previous, current) => previous > current); + +// This function will get the latest hightest cpm value bid, in case of tie it will return the bid which came in last +// Use case for tie: https://github.com/prebid/Prebid.js/issues/2539 +export const getLatestHighestCpmBid = getHighestCpmCallback('responseTimestamp', (previous, current) => previous < current); + +function getHighestCpmCallback(useTieBreakerProperty, tieBreakerCallback) { + return (previous, current) => { + if (previous.cpm === current.cpm) { + return tieBreakerCallback(previous[useTieBreakerProperty], current[useTieBreakerProperty]) ? current : previous; + } + return previous.cpm < current.cpm ? current : previous; + } +} + /** * Fisher–Yates shuffle * http://stackoverflow.com/a/6274398 @@ -583,6 +775,10 @@ export function shuffle(array) { return array; } +export function adUnitsFilter(filter, bid) { + return includes(filter, bid && bid.adUnitCode); +} + export function deepClone(obj) { return clone(obj); } @@ -599,15 +795,9 @@ export function isSafariBrowser() { return /^((?!chrome|android|crios|fxios).)*safari/i.test(navigator.userAgent); } -export function replaceMacros(str, subs) { - if (!str) return; - return Object.entries(subs).reduce((str, [key, val]) => { - return str.replace(new RegExp('\\$\\{' + key + '\\}', 'g'), val || ''); - }, str); -} - export function replaceAuctionPrice(str, cpm) { - return replaceMacros(str, {AUCTION_PRICE: cpm}) + if (!str) return; + return str.replace(/\$\{AUCTION_PRICE\}/g, cpm); } export function replaceClickThrough(str, clicktag) { @@ -653,7 +843,7 @@ export function checkCookieSupport() { * * @param {function} func The function which should be executed, once the returned function has been executed * numRequiredCalls times. - * @param {number} numRequiredCalls The number of times which the returned function needs to be called before + * @param {int} numRequiredCalls The number of times which the returned function needs to be called before * func is. */ export function delayExecution(func, numRequiredCalls) { @@ -672,7 +862,7 @@ export function delayExecution(func, numRequiredCalls) { /** * https://stackoverflow.com/a/34890276/428704 * @export - * @param {Array} xs + * @param {array} xs * @param {string} key * @returns {Object} {${key_value}: ${groupByArray}, key_value: {groupByArray}} */ @@ -735,7 +925,8 @@ export function isValidMediaTypes(mediaTypes) { export function getUserConfiguredParams(adUnits, adUnitCode, bidder) { return adUnits .filter(adUnit => adUnit.code === adUnitCode) - .flatMap((adUnit) => adUnit.bids) + .map((adUnit) => adUnit.bids) + .reduce(flatten, []) .filter((bidderData) => bidderData.bidder === bidder) .map((bidderData) => bidderData.params || {}); } @@ -747,7 +938,7 @@ export function getDNT() { return navigator.doNotTrack === '1' || window.doNotTrack === '1' || navigator.msDoNotTrack === '1' || navigator.doNotTrack === 'yes'; } -export const compareCodeAndSlot = (slot, adUnitCode) => slot.getAdUnitPath() === adUnitCode || slot.getSlotElementId() === adUnitCode; +const compareCodeAndSlot = (slot, adUnitCode) => slot.getAdUnitPath() === adUnitCode || slot.getSlotElementId() === adUnitCode; /** * Returns filter function to match adUnitCode in slot @@ -758,6 +949,41 @@ export function isAdUnitCodeMatchingSlot(slot) { return (adUnitCode) => compareCodeAndSlot(slot, adUnitCode); } +/** + * Returns filter function to match adUnitCode in slot + * @param {string} adUnitCode AdUnit code + * @return {function} filter function + */ +export function isSlotMatchingAdUnitCode(adUnitCode) { + return (slot) => compareCodeAndSlot(slot, adUnitCode); +} + +/** + * @summary Uses the adUnit's code in order to find a matching gpt slot object on the page + */ +export function getGptSlotForAdUnitCode(adUnitCode) { + let matchingSlot; + if (isGptPubadsDefined()) { + // find the first matching gpt slot on the page + matchingSlot = find(window.googletag.pubads().getSlots(), isSlotMatchingAdUnitCode(adUnitCode)); + } + return matchingSlot; +}; + +/** + * @summary Uses the adUnit's code in order to find a matching gptSlot on the page + */ +export function getGptSlotInfoForAdUnitCode(adUnitCode) { + const matchingSlot = getGptSlotForAdUnitCode(adUnitCode); + if (matchingSlot) { + return { + gptSlot: matchingSlot.getAdUnitPath(), + divId: matchingSlot.getSlotElementId() + } + } + return {}; +}; + /** * Constructs warning message for when unsupported bidders are dropped from an adunit * @param {Object} adUnit ad unit from which the bidder is being dropped @@ -779,14 +1005,33 @@ export function unsupportedBidderMessage(adUnit, bidder) { * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isInteger * @param {*} value */ -export const isInteger = Number.isInteger.bind(Number); +export function isInteger(value) { + if (Number.isInteger) { + return Number.isInteger(value); + } else { + return typeof value === 'number' && isFinite(value) && Math.floor(value) === value; + } +} + +/** + * Converts a string value in camel-case to underscore eg 'placementId' becomes 'placement_id' + * @param {string} value string value to convert + */ +export function convertCamelToUnderscore(value) { + return value.replace(/(?:^|\.?)([A-Z])/g, function (x, y) { return '_' + y.toLowerCase() }).replace(/^_/, ''); +} /** * Returns a new object with undefined properties removed from given object * @param obj the object to clean */ export function cleanObj(obj) { - return Object.fromEntries(Object.entries(obj).filter(([_, v]) => typeof v !== 'undefined')) + return Object.keys(obj).reduce((newObj, key) => { + if (typeof obj[key] !== 'undefined') { + newObj[key] = obj[key]; + } + return newObj; + }, {}) } /** @@ -823,10 +1068,104 @@ export function pick(obj, properties) { }, {}); } +/** + * Try to convert a value to a type. + * If it can't be done, the value will be returned. + * + * @param {string} typeToConvert The target type. e.g. "string", "number", etc. + * @param {*} value The value to be converted into typeToConvert. + */ +function tryConvertType(typeToConvert, value) { + if (typeToConvert === 'string') { + return value && value.toString(); + } else if (typeToConvert === 'number') { + return Number(value); + } else { + return value; + } +} + +export function convertTypes(types, params) { + Object.keys(types).forEach(key => { + if (params[key]) { + if (isFn(types[key])) { + params[key] = types[key](params[key]); + } else { + params[key] = tryConvertType(types[key], params[key]); + } + + // don't send invalid values + if (isNaN(params[key])) { + delete params.key; + } + } + }); + return params; +} + export function isArrayOfNums(val, size) { return (isArray(val)) && ((size) ? val.length === size : true) && (val.every(v => isInteger(v))); } +/** + * Creates an array of n length and fills each item with the given value + */ +export function fill(value, length) { + let newArray = []; + + for (let i = 0; i < length; i++) { + let valueToPush = isPlainObject(value) ? deepClone(value) : value; + newArray.push(valueToPush); + } + + return newArray; +} + +/** + * http://npm.im/chunk + * Returns an array with *size* chunks from given array + * + * Example: + * ['a', 'b', 'c', 'd', 'e'] chunked by 2 => + * [['a', 'b'], ['c', 'd'], ['e']] + */ +export function chunk(array, size) { + let newArray = []; + + for (let i = 0; i < Math.ceil(array.length / size); i++) { + let start = i * size; + let end = start + size; + newArray.push(array.slice(start, end)); + } + + return newArray; +} + +export function getMinValueFromArray(array) { + return Math.min(...array); +} + +export function getMaxValueFromArray(array) { + return Math.max(...array); +} + +/** + * This function will create compare function to sort on object property + * @param {string} property + * @returns {function} compare function to be used in sorting + */ +export function compareOn(property) { + return function compare(a, b) { + if (a[property] < b[property]) { + return 1; + } + if (a[property] > b[property]) { + return -1; + } + return 0; + } +} + export function parseQS(query) { return !query ? {} : query .replace(/^\?/, '') @@ -989,6 +1328,15 @@ export function cyrb53Hash(str, seed = 0) { return (4294967296 * (2097151 & h2) + (h1 >>> 0)).toString(); } +/** + * returns a window object, which holds the provided document or null + * @param {Document} doc + * @returns {Window} + */ +export function getWindowFromDocument(doc) { + return (doc) ? doc.defaultView : null; +} + /** * returns the result of `JSON.parse(data)`, or undefined if that throws an error. * @param data @@ -1027,33 +1375,36 @@ export function memoize(fn, key = function (arg) { return arg; }) { * @param {object} attributes */ export function setScriptAttributes(script, attributes) { - Object.entries(attributes).forEach(([k, v]) => script.setAttribute(k, v)) + for (let key in attributes) { + if (attributes.hasOwnProperty(key)) { + script.setAttribute(key, attributes[key]); + } + } } /** - * Perform a binary search for `el` on an ordered array `arr`. - * - * @returns the lowest nonnegative integer I that satisfies: - * key(arr[i]) >= key(el) for each i between I and arr.length - * - * (if one or more matches are found for `el`, returns the index of the first; - * if the element is not found, return the index of the first element that's greater; - * if no greater element exists, return `arr.length`) + * Encode a string for inclusion in HTML. + * See https://pragmaticwebsecurity.com/articles/spasecurity/json-stringify-xss.html and + * https://codeql.github.com/codeql-query-help/javascript/js-bad-code-sanitization/ + * @return {string} */ -export function binarySearch(arr, el, key = (el) => el) { - let left = 0; - let right = arr.length && arr.length - 1; - const target = key(el); - while (right - left > 1) { - const middle = left + Math.round((right - left) / 2); - if (target > key(arr[middle])) { - left = middle; - } else { - right = middle; - } - } - while (arr.length > left && target > key(arr[left])) { - left++; +export const escapeUnsafeChars = (() => { + const escapes = { + '<': '\\u003C', + '>': '\\u003E', + '/': '\\u002F', + '\\': '\\\\', + '\b': '\\b', + '\f': '\\f', + '\n': '\\n', + '\r': '\\r', + '\t': '\\t', + '\0': '\\0', + '\u2028': '\\u2028', + '\u2029': '\\u2029' + }; + + return function(str) { + return str.replace(/[<>\b\f\n\r\t\0\u2028\u2029\\]/g, x => escapes[x]) } - return left; -} +})(); diff --git a/src/utils/currency.js b/src/utils/currency.js new file mode 100644 index 00000000000..ab7e5faa1ea --- /dev/null +++ b/src/utils/currency.js @@ -0,0 +1,16 @@ +import {getGlobal} from '../prebidGlobal.js'; + +/** + * "best effort" wrapper around currency conversion; always returns an amount that may or may not be correct. + */ +export function beConvertCurrency(amount, from, to) { + if (from === to) return amount; + let result = amount; + if (typeof getGlobal().convertCurrency === 'function') { + try { + result = getGlobal().convertCurrency(amount, from, to); + } catch (e) { + } + } + return result; +} diff --git a/src/utils/reducers.js b/src/utils/reducers.js deleted file mode 100644 index 28851be8aaa..00000000000 --- a/src/utils/reducers.js +++ /dev/null @@ -1,44 +0,0 @@ -export function simpleCompare(a, b) { - if (a === b) return 0; - return a < b ? -1 : 1; -} - -export function keyCompare(key = (item) => item) { - return (a, b) => simpleCompare(key(a), key(b)) -} - -export function reverseCompare(compare = simpleCompare) { - return (a, b) => -compare(a, b) || 0; -} - -export function tiebreakCompare(...compares) { - return function (a, b) { - for (const cmp of compares) { - const val = cmp(a, b); - if (val !== 0) return val; - } - return 0; - } -} - -export function minimum(compare = simpleCompare) { - return (min, item) => compare(item, min) < 0 ? item : min; -} - -export function maximum(compare = simpleCompare) { - return minimum(reverseCompare(compare)); -} - -const cpmCompare = keyCompare((bid) => bid.cpm); -const timestampCompare = keyCompare((bid) => bid.responseTimestamp); - -// This function will get highest cpm value bid, in case of tie it will return the bid with lowest timeToRespond -export const getHighestCpm = maximum(tiebreakCompare(cpmCompare, reverseCompare(keyCompare((bid) => bid.timeToRespond)))) - -// This function will get the oldest hightest cpm value bid, in case of tie it will return the bid which came in first -// Use case for tie: https://github.com/prebid/Prebid.js/issues/2448 -export const getOldestHighestCpmBid = maximum(tiebreakCompare(cpmCompare, reverseCompare(timestampCompare))) - -// This function will get the latest hightest cpm value bid, in case of tie it will return the bid which came in last -// Use case for tie: https://github.com/prebid/Prebid.js/issues/2539 -export const getLatestHighestCpmBid = maximum(tiebreakCompare(cpmCompare, timestampCompare)) diff --git a/src/utils/ttlCollection.js b/src/utils/ttlCollection.js deleted file mode 100644 index 392ed1c9ad7..00000000000 --- a/src/utils/ttlCollection.js +++ /dev/null @@ -1,139 +0,0 @@ -import {GreedyPromise} from './promise.js'; -import {binarySearch, timestamp} from '../utils.js'; - -/** - * Create a set-like collection that automatically forgets items after a certain time. - * - * @param {({}) => Number|Promise} startTime? a function taking an item added to this collection, - * and returning (a promise to) a timestamp to be used as the starting time for the item - * (the item will be dropped after `ttl(item)` milliseconds have elapsed since this timestamp). - * Defaults to the time the item was added to the collection. - * @param {({}) => Number|void|Promise} ttl a function taking an item added to this collection, - * and returning (a promise to) the duration (in milliseconds) the item should be kept in it. - * May return null to indicate that the item should be persisted indefinitely. - * @param {boolean} monotonic? set to true for better performance, but only if, given any two items A and B in this collection: - * if A was added before B, then: - * - startTime(A) + ttl(A) <= startTime(B) + ttl(B) - * - Promise.all([startTime(A), ttl(A)]) never resolves later than Promise.all([startTime(B), ttl(B)]) - * @param {number} slack? maximum duration (in milliseconds) that an item is allowed to persist - * once past its TTL. This is also roughly the interval between "garbage collection" sweeps. - */ -export function ttlCollection( - { - startTime = timestamp, - ttl = () => null, - monotonic = false, - slack = 5000 - } = {} -) { - const items = new Map(); - const pendingPurge = []; - const markForPurge = monotonic - ? (entry) => pendingPurge.push(entry) - : (entry) => pendingPurge.splice(binarySearch(pendingPurge, entry, (el) => el.expiry), 0, entry) - let nextPurge, task; - - function reschedulePurge() { - task && clearTimeout(task); - if (pendingPurge.length > 0) { - const now = timestamp(); - nextPurge = Math.max(now, pendingPurge[0].expiry + slack); - task = setTimeout(() => { - const now = timestamp(); - let cnt = 0; - for (const entry of pendingPurge) { - if (entry.expiry > now) break; - items.delete(entry.item) - cnt++; - } - pendingPurge.splice(0, cnt); - task = null; - reschedulePurge(); - }, nextPurge - now); - } else { - task = null; - } - } - - function mkEntry(item) { - const values = {}; - const thisCohort = currentCohort; - let expiry; - - function update() { - if (thisCohort === currentCohort && values.start != null && values.delta != null) { - expiry = values.start + values.delta; - markForPurge(entry); - if (task == null || nextPurge > expiry + slack) { - reschedulePurge(); - } - } - } - - const [init, refresh] = Object.entries({ - start: startTime, - delta: ttl - }).map(([field, getter]) => { - let currentCall; - return function() { - const thisCall = currentCall = {}; - GreedyPromise.resolve(getter(item)).then((val) => { - if (thisCall === currentCall) { - values[field] = val; - update(); - } - }); - } - }) - - const entry = { - item, - refresh, - get expiry() { - return expiry; - }, - }; - - init(); - refresh(); - return entry; - } - - let currentCohort = {}; - - return { - [Symbol.iterator]: () => items.keys(), - /** - * Add an item to this collection. - * @param item - */ - add(item) { - !items.has(item) && items.set(item, mkEntry(item)); - }, - /** - * Clear this collection. - */ - clear() { - pendingPurge.length = 0; - reschedulePurge(); - items.clear(); - currentCohort = {}; - }, - /** - * @returns {[]} all the items in this collection, in insertion order. - */ - toArray() { - return Array.from(items.keys()); - }, - /** - * Refresh the TTL for each item in this collection. - */ - refresh() { - pendingPurge.length = 0; - reschedulePurge(); - for (const entry of items.values()) { - entry.refresh(); - } - }, - }; -} diff --git a/src/video.js b/src/video.js index ff137892a2b..7930e318874 100644 --- a/src/video.js +++ b/src/video.js @@ -1,21 +1,25 @@ -import {deepAccess, logError} from './utils.js'; -import {config} from '../src/config.js'; -import {hook} from './hook.js'; +import adapterManager from './adapterManager.js'; +import { deepAccess, logError } from './utils.js'; +import { config } from '../src/config.js'; +import {includes} from './polyfill.js'; +import { hook } from './hook.js'; import {auctionManager} from './auctionManager.js'; +const VIDEO_MEDIA_TYPE = 'video'; export const OUTSTREAM = 'outstream'; export const INSTREAM = 'instream'; -export function fillVideoDefaults(adUnit) { - const video = adUnit?.mediaTypes?.video; - if (video != null && video.plcmt == null) { - if (video.context === OUTSTREAM || [2, 3, 4].includes(video.placement)) { - video.plcmt = 4; - } else if (video.context !== OUTSTREAM && [2, 6].includes(video.playbackmethod)) { - video.plcmt = 2; - } - } -} +/** + * Helper functions for working with video-enabled adUnits + */ +export const videoAdUnit = adUnit => { + const mediaType = adUnit.mediaType === VIDEO_MEDIA_TYPE; + const mediaTypes = deepAccess(adUnit, 'mediaTypes.video'); + return mediaType || mediaTypes; +}; +export const videoBidder = bid => includes(adapterManager.videoAdapters, bid.bidder); +export const hasNonVideoBidder = adUnit => + adUnit.bids.filter(bid => !videoBidder(bid)).length; /** * @typedef {object} VideoBid diff --git a/test/mocks/xhr.js b/test/mocks/xhr.js index e7b1d96f0a4..424100f870c 100644 --- a/test/mocks/xhr.js +++ b/test/mocks/xhr.js @@ -1,236 +1,12 @@ import {getUniqueIdentifierStr} from '../../src/utils.js'; -import {GreedyPromise} from '../../src/utils/promise.js'; -import {fakeXhr} from 'nise'; -import {dep} from 'src/ajax.js'; -export const xhr = sinon.useFakeXMLHttpRequest(); -export const server = mockFetchServer(); +export let server = sinon.createFakeServer(); +export let xhr = global.XMLHttpRequest; -/** - * An (incomplete) replica of nise's fakeServer, but backing fetch used in ajax.js (rather than XHR). - */ -function mockFetchServer() { - const sandbox = sinon.createSandbox(); - const bodies = new WeakMap(); - const requests = []; - const {DONE, UNSENT} = XMLHttpRequest; - - function makeRequest(resource, options) { - const requestBody = options?.body || bodies.get(resource); - const request = new Request(resource, options); - bodies.set(request, requestBody); - return request; - } - - function mockXHR(resource, options) { - let resolve, reject; - const promise = new GreedyPromise((res, rej) => { - resolve = res; - reject = rej; - }); - - function error(reason = new TypeError('Failed to fetch')) { - mockReq.status = 0; - reject(reason); - } - - const request = makeRequest(resource, options); - request.signal.onabort = () => error(new DOMException('The user aborted a request')); - let responseHeaders; - - const mockReq = { - fetch: { - request, - requestBody: bodies.get(request), - promise, - }, - readyState: UNSENT, - url: request.url, - method: request.method, - requestBody: bodies.get(request), - status: 0, - statusText: '', - requestHeaders: new Proxy(request.headers, { - get(target, prop) { - return typeof prop === 'string' && target.has(prop) ? target.get(prop) : {}[prop]; - }, - has(target, prop) { - return typeof prop === 'string' && target.has(prop); - }, - ownKeys(target) { - return Array.from(target.keys()); - }, - getOwnPropertyDescriptor(target, prop) { - if (typeof prop === 'string' && target.has(prop)) { - return { - enumerable: true, - configurable: true, - writable: false, - value: target.get(prop) - } - } - } - }), - withCredentials: request.credentials === 'include', - setStatus(status) { - // nise replaces invalid status with 200 - status = typeof status === 'number' ? status : 200; - mockReq.status = status; - mockReq.statusText = fakeXhr.FakeXMLHttpRequest.statusCodes[status] || ''; - }, - setResponseHeaders(headers) { - responseHeaders = headers; - }, - setResponseBody(body) { - if (mockReq.status === 0) { - error(); - return; - } - const resp = Object.defineProperties(new Response(body, { - status: mockReq.status, - statusText: mockReq.statusText, - headers: responseHeaders || {}, - }), { - url: { - get: () => mockReq.fetch.request.url, - } - }); - mockReq.readyState = DONE; - // tests expect respond() to run everything immediately, - // so make body available syncronously - resp.text = () => GreedyPromise.resolve(body || ''); - Object.assign(mockReq.fetch, { - response: resp, - responseBody: body || '' - }) - resolve(resp); - }, - respond(status = 200, headers, body) { - mockReq.setStatus(status); - mockReq.setResponseHeaders(headers); - mockReq.setResponseBody(body); - }, - error - }; - return mockReq; - } - - let enabled = false; - let timeoutsEnabled = false; - - function enable() { - if (!enabled) { - sandbox.stub(dep, 'fetch').callsFake((resource, options) => { - const req = mockXHR(resource, options); - requests.push(req); - return req.fetch.promise; - }); - sandbox.stub(dep, 'makeRequest').callsFake(makeRequest); - const timeout = dep.timeout; - sandbox.stub(dep, 'timeout').callsFake(function () { - if (timeoutsEnabled) { - return timeout.apply(null, arguments); - } else { - return {}; - } - }); - enabled = true; - } - } - - enable(); - - const responders = []; - - function respondWith() { - let response, urlMatcher, methodMatcher; - urlMatcher = methodMatcher = () => true; - switch (arguments.length) { - case 1: - ([response] = arguments); - break; - case 2: - ([urlMatcher, response] = arguments); - break; - case 3: - ([methodMatcher, urlMatcher, response] = arguments); - methodMatcher = ((toMatch) => (method) => method === toMatch)(methodMatcher); - break; - default: - throw new Error('Invalid respondWith invocation'); - } - if (typeof urlMatcher.exec === 'function') { - urlMatcher = ((rx) => (url) => rx.exec(url)?.slice(1))(urlMatcher); - } else if (typeof urlMatcher === 'string') { - urlMatcher = ((toMatch) => (url) => url === toMatch)(urlMatcher); - } - responders.push((req) => { - if (req.readyState !== DONE && methodMatcher(req.method)) { - const arg = urlMatcher(req.url); - if (arg) { - if (typeof response === 'function') { - response(req, ...(Array.isArray(arg) ? arg : [])); - } else if (typeof response === 'string') { - req.respond(200, null, response); - } else { - req.respond.apply(req, response); - } - } - } - }); - } - - function resetState() { - requests.length = 0; - responders.length = 0; - timeoutsEnabled = false; - } - - return { - requests, - enable, - restore() { - resetState(); - sandbox.restore(); - enabled = false; - }, - reset() { - sandbox.resetHistory(); - resetState(); - }, - respondWith, - respond() { - if (arguments.length > 0) { - respondWith.apply(null, arguments); - } - requests.forEach(req => { - for (let i = responders.length - 1; i >= 0; i--) { - responders[i](req); - if (req.readyState === DONE) break; - } - if (req.readyState !== DONE) { - req.respond(404, {}, ''); - } - }); - }, - /** - * the timeout mechanism is quite different between XHR and fetch - * by default, mocked fetch does not time out - to reflect fakeServer XHRs - * note that many tests will fire requests without caring or waiting for their response - - * if they are timed out later, during unrelated tests, the log messages might interfere with their - * assertions - */ - get autoTimeout() { - return timeoutsEnabled; - }, - set autoTimeout(val) { - timeoutsEnabled = !!val; - } - }; -} - -beforeEach(function () { - server.reset(); +beforeEach(function() { + server.restore(); + server = sinon.createFakeServer(); + xhr = global.XMLHttpRequest; }); const bid = getUniqueIdentifierStr().substring(4); @@ -244,35 +20,12 @@ afterEach(function () { return (s) => s.split('\n').map(s => `${preamble} ${s}`).join('\n'); })(); - function format(obj, body = null) { - if (obj == null) return obj; - const fmt = {}; - let node = obj; - while (node != null) { - Object.keys(node).forEach((k) => { - const val = obj[k]; - if (typeof val !== 'function' && !fmt.hasOwnProperty(k)) { - fmt[k] = val; - } - }); - node = Object.getPrototypeOf(node); - } - if (obj.headers != null) { - fmt.headers = Object.fromEntries(obj.headers.entries()) - } - fmt.body = body; - return fmt; - } - - console.log(prepend(`XHR mock state after failure (for test '${this.currentTest.fullTitle()}'): ${server.requests.length} requests`)); + console.log(prepend(`XHR mock state after failure (for test '${this.currentTest.fullTitle()}'): ${server.requests.length} requests`)) server.requests.forEach((req, i) => { console.log(prepend(`Request #${i}:`)); - console.log(prepend(JSON.stringify({ - request: format(req.fetch.request, req.fetch.requestBody), - response: format(req.fetch.response, req.fetch.responseBody) - }, null, 2))); - }); + console.log(prepend(JSON.stringify(req, null, 2))); + }) } }); /* eslint-enable */ diff --git a/test/spec/appnexusKeywords_spec.js b/test/spec/appnexusKeywords_spec.js index 68faeff0b82..9bf567a27c5 100644 --- a/test/spec/appnexusKeywords_spec.js +++ b/test/spec/appnexusKeywords_spec.js @@ -1,4 +1,4 @@ -import {transformBidderParamKeywords} from '../../libraries/appnexusUtils/anKeywords.js'; +import {transformBidderParamKeywords} from '../../libraries/appnexusKeywords/anKeywords.js'; import {expect} from 'chai/index.js'; import * as utils from '../../src/utils.js'; diff --git a/test/spec/auctionmanager_spec.js b/test/spec/auctionmanager_spec.js index 137d009bd18..dd59b16d492 100644 --- a/test/spec/auctionmanager_spec.js +++ b/test/spec/auctionmanager_spec.js @@ -5,7 +5,7 @@ import { adjustBids, getMediaTypeGranularity, getPriceByGranularity, - addBidResponse, resetAuctionState, responsesReady + addBidResponse } from 'src/auction.js'; import CONSTANTS from 'src/constants.json'; import * as auctionModule from 'src/auction.js'; @@ -23,7 +23,6 @@ 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'; var assert = require('assert'); @@ -49,7 +48,6 @@ function mockBid(opts) { let bidderCode = opts && opts.bidderCode; return { - adUnitCode: opts?.adUnitCode || ADUNIT_CODE, 'ad': 'creative', 'cpm': '1.99', 'width': 300, @@ -70,11 +68,6 @@ function mockBid(opts) { transactionId: this.transactionId, auctionId: this.auctionId } - }, - _ctx: { - adUnits: opts?.adUnits, - src: opts?.src, - uniquePbsTid: opts?.uniquePbsTid, } }; } @@ -103,9 +96,6 @@ function mockBidRequest(bid, opts) { 'bidderCode': bidderCode || bid.bidderCode, 'auctionId': opts && opts.auctionId, 'bidderRequestId': requestId, - src: bid?._ctx?.src, - adUnitsS2SCopy: bid?._ctx?.src === CONSTANTS.S2S.SRC ? bid?._ctx?.adUnits : undefined, - uniquePbsTid: bid?._ctx?.src === CONSTANTS.S2S.SRC ? bid?._ctx?.uniquePbsTid : undefined, 'bids': [ { 'bidder': bidderCode || bid.bidderCode, @@ -118,8 +108,7 @@ function mockBidRequest(bid, opts) { 'bidId': bid.requestId, 'bidderRequestId': requestId, 'auctionId': opts && opts.auctionId, - 'mediaTypes': mediaType, - src: bid?._ctx?.src + 'mediaTypes': mediaType } ], 'auctionStart': 1505250713622, @@ -171,7 +160,6 @@ describe('auctionmanager.js', function () { indexAuctions = []; indexStub = sinon.stub(auctionManager, 'index'); indexStub.get(() => new AuctionIndex(() => indexAuctions)); - resetAuctionState(); }); afterEach(() => { @@ -197,11 +185,9 @@ describe('auctionmanager.js', function () { adId: '1adId', source: 'client', mediaType: 'banner', - creativeId: 'monkeys', meta: { advertiserDomains: ['adomain'], - primaryCatId: 'IAB-test', - networkId: '123987' + primaryCatId: 'IAB-test' } }; @@ -216,8 +202,6 @@ describe('auctionmanager.js', function () { expected[ CONSTANTS.TARGETING_KEYS.FORMAT ] = bid.mediaType; expected[ CONSTANTS.TARGETING_KEYS.ADOMAIN ] = bid.meta.advertiserDomains[0]; expected[ CONSTANTS.TARGETING_KEYS.ACAT ] = bid.meta.primaryCatId; - expected[ CONSTANTS.TARGETING_KEYS.DSP ] = bid.meta.networkId; - expected[ CONSTANTS.TARGETING_KEYS.CRID ] = bid.creativeId; if (bid.mediaType === 'video') { expected[ CONSTANTS.TARGETING_KEYS.UUID ] = bid.videoCacheKey; expected[ CONSTANTS.TARGETING_KEYS.CACHE_ID ] = bid.videoCacheKey; @@ -320,18 +304,6 @@ describe('auctionmanager.js', function () { return bidResponse.meta.advertiserDomains[0]; } }, - { - key: CONSTANTS.TARGETING_KEYS.CRID, - val: function (bidResponse) { - return bidResponse.creativeId; - } - }, - { - key: CONSTANTS.TARGETING_KEYS.DSP, - val: function (bidResponse) { - return bidResponse.meta.networkId; - } - }, { key: CONSTANTS.TARGETING_KEYS.ACAT, val: function (bidResponse) { @@ -416,18 +388,6 @@ describe('auctionmanager.js', function () { return bidResponse.meta.advertiserDomains[0]; } }, - { - key: CONSTANTS.TARGETING_KEYS.CRID, - val: function (bidResponse) { - return bidResponse.creativeId; - } - }, - { - key: CONSTANTS.TARGETING_KEYS.DSP, - val: function (bidResponse) { - return bidResponse.meta.networkId; - } - }, { key: CONSTANTS.TARGETING_KEYS.ACAT, val: function (bidResponse) { @@ -527,7 +487,7 @@ describe('auctionmanager.js', function () { s2sConfig: { accountId: '1', enabled: true, - defaultVendor: 'appnexuspsp', + defaultVendor: 'appnexus', bidders: ['appnexus'], timeout: 1000, adapter: 'prebidServer' @@ -775,10 +735,9 @@ describe('auctionmanager.js', function () { }); describe('createAuction', () => { - let adUnits, stubMakeBidRequests, stubCallAdapters, bids; + let adUnits, stubMakeBidRequests, stubCallAdapters beforeEach(() => { - bids = []; stubMakeBidRequests = sinon.stub(adapterManager, 'makeBidRequests').returns([{ bidderCode: BIDDER_CODE, bids: [{ @@ -786,7 +745,6 @@ describe('auctionmanager.js', function () { }] }]); stubCallAdapters = sinon.stub(adapterManager, 'callBids').callsFake((au, reqs, addBid, done) => { - bids.forEach(bid => addBid(bid.adUnitCode, bid)); reqs.forEach(r => done.apply(r)); }); adUnits = [{ @@ -801,7 +759,6 @@ describe('auctionmanager.js', function () { afterEach(() => { stubMakeBidRequests.restore(); stubCallAdapters.restore(); - auctionManager.clearAllAuctions(); }); it('passes global and bidder ortb2 to the auction', () => { @@ -829,79 +786,6 @@ describe('auctionmanager.js', function () { }); expect(auction.getNonBids()[0]).to.equal('test'); }); - - describe('stale auctions', () => { - let clock, auction; - beforeEach(() => { - clock = sinon.useFakeTimers(); - auction = auctionManager.createAuction({adUnits}); - indexAuctions.push(auction); - }); - afterEach(() => { - clock.restore(); - config.resetConfig(); - }); - - it('are dropped after their last bid becomes stale (if minBidCacheTTL is set)', () => { - config.setConfig({ - minBidCacheTTL: 0 - }); - bids = [ - { - adUnitCode: ADUNIT_CODE, - transactionId: ADUNIT_CODE, - ttl: 10 - }, { - adUnitCode: ADUNIT_CODE, - transactionId: ADUNIT_CODE, - ttl: 100 - } - ]; - auction.callBids(); - return auction.end.then(() => { - clock.tick(50 * 1000); - expect(auctionManager.getBidsReceived().length).to.equal(2); - clock.tick(56 * 1000); - expect(auctionManager.getBidsReceived()).to.eql([]); - }); - }); - - it('are dropped after `minBidCacheTTL` seconds if they had no bid', () => { - auction.callBids(); - config.setConfig({ - minBidCacheTTL: 2 - }); - return auction.end.then(() => { - expect(auctionManager.getNoBids().length).to.eql(1); - clock.tick(10 * 10000); - expect(auctionManager.getNoBids().length).to.eql(0); - }) - }); - - Object.entries({ - 'bids': { - bd: [{ - adUnitCode: ADUNIT_CODE, - transactionId: ADUNIT_CODE, - ttl: 10 - }], - entries: () => auctionManager.getBidsReceived() - }, - 'no bids': { - bd: [], - entries: () => auctionManager.getNoBids() - } - }).forEach(([t, {bd, entries}]) => { - it(`with ${t} are never dropped if minBidCacheTTL is not set`, () => { - bids = bd; - auction.callBids(); - return auction.end.then(() => { - clock.tick(100 * 1000); - expect(entries().length > 0).to.be.true; - }) - }) - }); - }) }); describe('addBidResponse #1', function () { @@ -927,11 +811,6 @@ describe('auctionmanager.js', function () { beforeEach(function () { ajaxStub = sinon.stub(ajaxLib, 'ajaxBuilder').callsFake(mockAjaxBuilder); adUnits = [{ - mediaTypes: { - banner: { - sizes: [] - } - }, code: ADUNIT_CODE, transactionId: ADUNIT_CODE, bids: [ @@ -1117,47 +996,36 @@ describe('auctionmanager.js', function () { assert.strictEqual(addedBid.renderer.url, myBid.renderer.url); }); - describe('bid for a regular unit and a video unit', () => { - beforeEach(() => { - const renderer = { - url: 'renderer.js', - render: (bid) => bid - }; - Object.assign(adUnits[0], {renderer}); - // make sure that if the renderer is only on the second ad unit, prebid - // still correctly uses it - let bid = mockBid(); - let bidRequests = [mockBidRequest(bid, {auctionId: auction.getAuctionId()})]; - - bidRequests[0].bids[1] = Object.assign({ - bidId: utils.getUniqueIdentifierStr() - }, bidRequests[0].bids[0]); - Object.assign(bidRequests[0].bids[0], { - adUnitCode: ADUNIT_CODE1, - transactionId: ADUNIT_CODE1, - }); - - makeRequestsStub.returns(bidRequests); - - // this should correspond with the second bid in the bidReq because of the ad unit code - bid.mediaType = 'video-outstream'; - spec.interpretResponse.returns(bid); + it('bid for a regular unit and a video unit', function() { + let renderer = { + url: 'renderer.js', + render: (bid) => bid + }; + Object.assign(adUnits[0], {renderer}); + // make sure that if the renderer is only on the second ad unit, prebid + // still correctly uses it + let bid = mockBid(); + let bidRequests = [mockBidRequest(bid, {auctionId: auction.getAuctionId()})]; + + bidRequests[0].bids[1] = Object.assign({ + bidId: utils.getUniqueIdentifierStr() + }, bidRequests[0].bids[0]); + Object.assign(bidRequests[0].bids[0], { + adUnitCode: ADUNIT_CODE1, + transactionId: ADUNIT_CODE1, }); - it('should use renderers on bid response', () => { - auction.callBids(); + makeRequestsStub.returns(bidRequests); - const addedBid = find(auction.getBidsReceived(), bid => bid.adUnitCode === ADUNIT_CODE); - assert.equal(addedBid.renderer.url, 'renderer.js'); - }); + // this should correspond with the second bid in the bidReq because of the ad unit code + bid.mediaType = 'video-outstream'; + spec.interpretResponse.returns(bid); - it('should resolve .end', () => { - auction.callBids(); - return auction.end.then(() => { - expect(auction.getBidsReceived().length).to.eql(1); - }) - }); - }) + auction.callBids(); + + const addedBid = find(auction.getBidsReceived(), bid => bid.adUnitCode == ADUNIT_CODE); + assert.equal(addedBid.renderer.url, 'renderer.js'); + }); it('sets bidResponse.ttlBuffer from adUnit.ttlBuffer', () => { adUnits[0].ttlBuffer = 0; @@ -1167,19 +1035,12 @@ describe('auctionmanager.js', function () { }); describe('when auction timeout is 20', function () { - let eventsEmitSpy, auctionDone; + let eventsEmitSpy; - function respondToRequest(requestIndex) { - server.requests[requestIndex].respond(200, {}, 'response body'); - } - - function runAuction() { - let bidRequests = bids.map(bid => mockBidRequest(bid, {auctionId: auction.getAuctionId()})); + function setupBids(auctionId) { + bids = [mockBid(), mockBid({ bidderCode: BIDDER_CODE1 })]; + let bidRequests = bids.map(bid => mockBidRequest(bid, {auctionId})); makeRequestsStub.returns(bidRequests); - return new Promise((resolve) => { - auctionDone = resolve; - auction.callBids(); - }) } beforeEach(function () { @@ -1188,156 +1049,86 @@ describe('auctionmanager.js', function () { transactionId: ADUNIT_CODE, bids: [ {bidder: BIDDER_CODE, params: {placementId: 'id'}}, - {bidder: BIDDER_CODE1, params: {placementId: 'id'}}, ] }]; adUnitCodes = [ADUNIT_CODE]; eventsEmitSpy = sinon.spy(events, 'emit'); - bids = [mockBid(), mockBid({ bidderCode: BIDDER_CODE1 })]; - const spec1 = mockBidder(BIDDER_CODE, [bids[0]]); - registerBidder(spec1); - const spec2 = mockBidder(BIDDER_CODE1, [bids[1]]); - registerBidder(spec2); - auction = auctionModule.newAuction({adUnits, adUnitCodes, callback: () => auctionDone(), cbTimeout: 20}); - indexAuctions = [auction]; }); - afterEach(function () { events.emit.restore(); }); - it('resolves .end on timeout', () => { - let endResolved = false; - auction.end.then(() => { - endResolved = true; - }) - const pm = runAuction().then(() => { - expect(endResolved).to.be.true; - }); - respondToRequest(0); - return pm; - }); - - describe('AUCTION_TIMEOUT event', () => { - let handler; - beforeEach(() => { - handler = sinon.spy(); - events.on(CONSTANTS.EVENTS.AUCTION_TIMEOUT, handler); - }) - afterEach(() => { - events.off(CONSTANTS.EVENTS.AUCTION_TIMEOUT, handler); - }); - - Object.entries({ - 'is fired on timeout': [true, [0]], - 'is NOT fired otherwise': [false, [0, 1]], - }).forEach(([t, [shouldFire, respond]]) => { - it(t, () => { - const pm = runAuction().then(() => { - if (shouldFire) { - sinon.assert.calledWith(handler, sinon.match({auctionId: auction.getAuctionId()})) - } else { - sinon.assert.notCalled(handler); - } - }); - respond.forEach(respondToRequest); - return pm; - }) - }); - }); + it('should emit BID_TIMEOUT and AUCTION_END for timed out bids', function (done) { + const spec1 = mockBidder(BIDDER_CODE, [bids[0]]); + registerBidder(spec1); + const spec2 = mockBidder(BIDDER_CODE1, [bids[1]]); + registerBidder(spec2); - it('should emit BID_TIMEOUT and AUCTION_END for timed out bids', function () { - const pm = runAuction().then(() => { + function respondToRequest(requestIndex) { + server.requests[requestIndex].respond(200, {}, 'response body'); + } + function auctionCallback() { const bidTimeoutCall = eventsEmitSpy.withArgs(CONSTANTS.EVENTS.BID_TIMEOUT).getCalls()[0]; const timedOutBids = bidTimeoutCall.args[1]; assert.equal(timedOutBids.length, 1); assert.equal(timedOutBids[0].bidder, BIDDER_CODE1); // Check that additional properties are available - assert.equal(timedOutBids[0].params[0].placementId, 'id'); + assert.equal(timedOutBids[0].params.placementId, 'id'); const auctionEndCall = eventsEmitSpy.withArgs(CONSTANTS.EVENTS.AUCTION_END).getCalls()[0]; const auctionProps = auctionEndCall.args[1]; assert.equal(auctionProps.adUnits, adUnits); assert.equal(auctionProps.timeout, 20); assert.equal(auctionProps.auctionStatus, AUCTION_COMPLETED) - }); + done(); + } + auction = auctionModule.newAuction({adUnits, adUnitCodes, callback: auctionCallback, cbTimeout: 20}); + setupBids(auction.getAuctionId()); + + auction.callBids(); respondToRequest(0); - return pm; }); + it('should NOT emit BID_TIMEOUT when all bidders responded in time', function (done) { + const spec1 = mockBidder(BIDDER_CODE, [bids[0]]); + registerBidder(spec1); + const spec2 = mockBidder(BIDDER_CODE1, [bids[1]]); + registerBidder(spec2); - it('should NOT emit BID_TIMEOUT when all bidders responded in time', function () { - const pm = runAuction().then(() => { + function respondToRequest(requestIndex) { + server.requests[requestIndex].respond(200, {}, 'response body'); + } + function auctionCallback() { assert.ok(eventsEmitSpy.withArgs(CONSTANTS.EVENTS.BID_TIMEOUT).notCalled, 'did not emit event BID_TIMEOUT'); - }); + done(); + } + auction = auctionModule.newAuction({adUnits, adUnitCodes, callback: auctionCallback, cbTimeout: 20}); + setupBids(auction.getAuctionId()); + auction.callBids(); respondToRequest(0); respondToRequest(1); - return pm; }); + it('should NOT emit BID_TIMEOUT for bidders which responded in time but with an empty bid', function (done) { + const spec1 = mockBidder(BIDDER_CODE, []); + registerBidder(spec1); + const spec2 = mockBidder(BIDDER_CODE1, []); + registerBidder(spec2); - it('should NOT emit BID_TIMEOUT for bidders which responded in time but with an empty bid', function () { - const pm = runAuction().then(() => { + function respondToRequest(requestIndex) { + server.requests[requestIndex].respond(200, {}, 'response body'); + } + function auctionCallback() { const bidTimeoutCall = eventsEmitSpy.withArgs(CONSTANTS.EVENTS.BID_TIMEOUT).getCalls()[0]; const timedOutBids = bidTimeoutCall.args[1]; assert.equal(timedOutBids.length, 1); assert.equal(timedOutBids[0].bidder, BIDDER_CODE1); - }); + done(); + } + auction = auctionModule.newAuction({adUnits, adUnitCodes, callback: auctionCallback, cbTimeout: 20}); + setupBids(auction.getAuctionId()); + auction.callBids(); respondToRequest(0); - return pm; }); - - it('should NOT emit BID_TIMEOUT for bidders that replied through S2S', () => { - adapterManager.registerBidAdapter(new PrebidServer(), 'pbs'); - config.setConfig({ - s2sConfig: [{ - accountId: '1', - enabled: true, - defaultVendor: 'appnexuspsp', - bidders: ['mock-s2s-1'], - adapter: 'pbs' - }, { - accountId: '1', - enabled: true, - defaultVendor: 'rubicon', - bidders: ['mock-s2s-2'], - adapter: 'pbs' - }] - }) - adUnits[0].bids.push({bidder: 'mock-s2s-1'}, {bidder: 'mock-s2s-2'}) - const s2sAdUnits = deepClone(adUnits); - bids.unshift( - mockBid({bidderCode: 'mock-s2s-1', src: CONSTANTS.S2S.SRC, adUnits: s2sAdUnits, uniquePbsTid: '1'}), - mockBid({bidderCode: 'mock-s2s-2', src: CONSTANTS.S2S.SRC, adUnits: s2sAdUnits, uniquePbsTid: '2'}) - ); - Object.assign(s2sAdUnits[0], { - mediaTypes: { - banner: { - sizes: [[300, 250], [300, 600]] - } - }, - bids: [ - { - bidder: 'mock-s2s-1', - bid_id: bids[0].requestId - }, - { - bidder: 'mock-s2s-2', - bid_id: bids[1].requestId - } - ] - }) - - const pm = runAuction().then(() => { - const toBids = eventsEmitSpy.withArgs(CONSTANTS.EVENTS.BID_TIMEOUT).getCalls()[0].args[1] - expect(toBids.map(bid => bid.bidder)).to.eql([ - 'mock-s2s-2', - BIDDER_CODE, - BIDDER_CODE1, - ]) - }); - respondToRequest(1); - return pm; - }) }); }); @@ -1832,54 +1623,116 @@ describe('auctionmanager.js', function () { sinon.assert.calledWith(auction.addBidReceived, sinon.match({cpm: 1.23})); }) - describe('when responsesReady defers', () => { - let resolve, reject, promise, callbacks, bids; + describe('when addBidResponse hook returns promises', () => { + let resolvers, callbacks, bids; - function hook(next, ready) { - next(ready.then(() => promise)); + function hook(next, ...args) { + next.bail(new Promise((resolve, reject) => { + resolvers.resolve.push(resolve); + resolvers.reject.push(reject); + }).finally(() => next(...args))); } - before(() => { - responsesReady.before(hook); - }); + function invokeCallbacks() { + bids.forEach((bid) => callbacks.addBidResponse(ADUNIT_CODE, bid)); + bidRequests.forEach(bidRequest => callbacks.adapterDone.call(bidRequest)); + } - after(() => { - responsesReady.getHooks({hook}).remove(); - }); + function delay(ms = 0) { + return new Promise((resolve) => { + setTimeout(resolve, ms) + }); + } beforeEach(() => { - // eslint-disable-next-line promise/param-names - promise = new Promise((rs, rj) => { - resolve = rs; - reject = rj; - }); bids = [ mockBid({bidderCode: BIDDER_CODE1}), mockBid({bidderCode: BIDDER_CODE}) ] bidRequests = bids.map((b) => mockBidRequest(b)); + resolvers = {resolve: [], reject: []}; + addBidResponse.before(hook); callbacks = auctionCallbacks(doneSpy, auction); Object.assign(auction, { addNoBid: sinon.spy() }); }); + afterEach(() => { + addBidResponse.getHooks({hook: hook}).remove(); + }); + + it('should wait for bids without a request bids before calling auctionDone', () => { + callbacks.addBidResponse(ADUNIT_CODE, Object.assign(mockBid(), {requestId: null})); + invokeCallbacks(); + resolvers.resolve.slice(1, 3).forEach((fn) => fn()); + return delay().then(() => { + expect(doneSpy.called).to.be.false; + resolvers.resolve[0](); + return delay(); + }).then(() => { + expect(doneSpy.called).to.be.true; + }); + }); + Object.entries({ - 'resolve': () => resolve(), - 'reject': () => reject(), - }).forEach(([t, resolver]) => { - it(`should wait for responsesReady to ${t} before calling auctionDone`, (done) => { - bidRequests.forEach(bidRequest => callbacks.adapterDone.call(bidRequest)); - setTimeout(() => { - sinon.assert.notCalled(doneSpy); - resolver(); - setTimeout(() => { - sinon.assert.called(doneSpy); - done(); - }) - }) + 'all succeed': ['resolve', 'resolve'], + 'some fail': ['resolve', 'reject'], + 'all fail': ['reject', 'reject'] + }).forEach(([test, results]) => { + describe(`(and ${test})`, () => { + it('should wait for them to complete before calling auctionDone', () => { + invokeCallbacks(); + return delay().then(() => { + expect(doneSpy.called).to.be.false; + expect(auction.addNoBid.called).to.be.false; + resolvers[results[0]][0](); + return delay(); + }).then(() => { + expect(doneSpy.called).to.be.false; + expect(auction.addNoBid.called).to.be.false; + resolvers[results[1]][1](); + return delay(); + }).then(() => { + expect(doneSpy.called).to.be.true; + }); + }); }); }); + + Object.entries({ + bidder: (timeout) => { + bidRequests.forEach((r) => r.timeout = timeout); + auction.getTimeout = () => timeout + 10000 + }, + auction: (timeout) => { + auction.getTimeout = () => timeout; + bidRequests.forEach((r) => r.timeout = timeout + 10000) + } + }).forEach(([test, setTimeout]) => { + it(`should respect ${test} timeout if they never complete`, () => { + const start = Date.now() - 2900; + auction.getAuctionStart = () => start; + setTimeout(3000); + invokeCallbacks(); + return delay().then(() => { + expect(doneSpy.called).to.be.false; + return delay(100); + }).then(() => { + expect(doneSpy.called).to.be.true; + }); + }); + + it(`should not wait if ${test} has already timed out`, () => { + const start = Date.now() - 2000; + auction.getAuctionStart = () => start; + setTimeout(1000); + invokeCallbacks(); + return delay().then(() => { + expect(doneSpy.called).to.be.true; + }); + }); + }) }); describe('when bids are rejected', () => { diff --git a/test/spec/creative/crossDomainCreative_spec.js b/test/spec/creative/crossDomainCreative_spec.js deleted file mode 100644 index 765d5e5311a..00000000000 --- a/test/spec/creative/crossDomainCreative_spec.js +++ /dev/null @@ -1,182 +0,0 @@ -import {renderer} from '../../../libraries/creativeRender/crossDomain.js'; -import { - AD_RENDER_FAILED, AD_RENDER_SUCCEEDED, EXCEPTION, NO_AD, - PREBID_EVENT, - PREBID_REQUEST, - PREBID_RESPONSE -} from '../../../libraries/creativeRender/constants.js'; - -describe('cross-domain creative', () => { - let win, renderAd, messages, mkIframe, listeners; - - beforeEach(() => { - messages = []; - listeners = []; - mkIframe = sinon.stub(); - win = { - document: { - body: { - appendChild: sinon.stub(), - }, - createElement: sinon.stub().callsFake(tagname => { - switch (tagname.toLowerCase()) { - case 'a': - return document.createElement('a') - case 'iframe': { - return mkIframe(); - } - } - }) - }, - addEventListener: sinon.stub().callsFake((_, listener) => listeners.push(listener)), - parent: { - postMessage: sinon.stub().callsFake((payload, targetOrigin, transfer) => { - messages.push({payload: JSON.parse(payload), targetOrigin, transfer}); - }) - } - }; - renderAd = renderer(win); - }) - - it('derives postMessage target origin from pubUrl ', () => { - renderAd({pubUrl: 'https://domain.com:123/path'}); - expect(messages[0].targetOrigin).to.eql('https://domain.com:123') - }); - - it('generates request message with adId and clickUrl', () => { - renderAd({adId: '123', clickUrl: 'https://click-url.com'}); - expect(messages[0].payload).to.eql({ - message: PREBID_REQUEST, - adId: '123', - options: { - clickUrl: 'https://click-url.com' - } - }) - }) - - Object.entries({ - 'MessageChannel': (msg) => messages[0].transfer[0].postMessage(msg), - 'message listener': (msg) => listeners.forEach((fn) => fn({data: msg})) - }).forEach(([t, transport]) => { - describe(`when using ${t}`, () => { - function reply(msg) { - transport(JSON.stringify(msg)) - }; - - it('ignores messages that are not a prebid response message', () => { - renderAd({adId: '123'}); - reply({adId: '123', ad: 'markup'}); - sinon.assert.notCalled(mkIframe); - }) - - describe('signals AD_RENDER_FAILED', () => { - it('on exception', (done) => { - mkIframe.callsFake(() => { throw new Error('error message') }); - renderAd({adId: '123'}); - reply({message: PREBID_RESPONSE, adId: '123', ad: 'markup'}); - setTimeout(() => { - expect(messages[1].payload).to.eql({ - message: PREBID_EVENT, - adId: '123', - event: AD_RENDER_FAILED, - info: { - reason: EXCEPTION, - message: 'error message' - } - }) - done(); - }, 100) - }); - - it('on missing ad', (done) => { - renderAd({adId: '123'}); - reply({message: PREBID_RESPONSE, adId: '123'}); - setTimeout(() => { - sinon.assert.match(messages[1].payload, { - message: PREBID_EVENT, - adId: '123', - event: AD_RENDER_FAILED, - info: { - reason: NO_AD, - } - }) - done(); - }, 100) - }) - }); - - describe('rendering', () => { - let iframe; - - beforeEach(() => { - iframe = { - attrs: {}, - setAttribute: sinon.stub().callsFake((k, v) => iframe.attrs[k.toLowerCase()] = v), - contentDocument: { - open: sinon.stub(), - write: sinon.stub(), - close: sinon.stub(), - } - } - mkIframe.callsFake(() => iframe); - }); - - it('renders adUrl as iframe src', (done) => { - renderAd({adId: '123'}); - reply({message: PREBID_RESPONSE, adId: '123', adUrl: 'some-url'}); - setTimeout(() => { - sinon.assert.calledWith(win.document.body.appendChild, iframe); - expect(iframe.attrs.src).to.eql('some-url'); - done(); - }, 100) - }); - - it('renders ad through document.write', (done) => { - renderAd({adId: '123'}); - reply({message: PREBID_RESPONSE, adId: '123', ad: 'some-markup'}); - setTimeout(() => { - sinon.assert.calledWith(win.document.body.appendChild, iframe); - sinon.assert.called(iframe.contentDocument.open); - sinon.assert.calledWith(iframe.contentDocument.write, 'some-markup'); - sinon.assert.called(iframe.contentDocument.close); - done(); - }, 100) - }); - - Object.entries({ - adUrl: 'mock-ad-url', - ad: 'mock-ad-markup' - }).forEach(([prop, propValue]) => { - describe(`when message has ${prop}`, () => { - beforeEach((done) => { - renderAd({adId: '123'}); - reply({ - message: PREBID_RESPONSE, - adId: '123', - [prop]: propValue, - width: 100, - height: 200 - }); - setTimeout(done, 100); - }); - - it('emits AD_RENDER_SUCCEEDED', () => { - expect(messages[1].payload).to.eql({ - message: PREBID_EVENT, - adId: '123', - event: AD_RENDER_SUCCEEDED - }) - }); - - it('sets iframe height / width to ad height / width', () => { - sinon.assert.match(iframe.attrs, { - width: 100, - height: 200 - }) - }) - }) - }) - }); - }); - }); -}); diff --git a/test/spec/e2e/modules/e2e_consent_mgt_gdpr.spec.js b/test/spec/e2e/modules/e2e_consent_mgt_gdpr.spec.js index d1803c9784a..5b5ea2ef2cd 100644 --- a/test/spec/e2e/modules/e2e_consent_mgt_gdpr.spec.js +++ b/test/spec/e2e/modules/e2e_consent_mgt_gdpr.spec.js @@ -1,4 +1,4 @@ -/* +/** TODO: old CMP no longer works; see if we can fix this with https://github.com/prebid/Prebid.js/issues/6377 const expect = require('chai').expect; const { testPageURL, switchFrame, waitForElement } = require('../../../helpers/testing-utils'); @@ -59,4 +59,4 @@ describe('Prebid.js GDPR Ad Unit Test', function () { expect(ele.isExisting()).to.be.true; }); }); - */ +**/ diff --git a/test/spec/fpd/enrichment_spec.js b/test/spec/fpd/enrichment_spec.js index 40692360dca..328846ca081 100644 --- a/test/spec/fpd/enrichment_spec.js +++ b/test/spec/fpd/enrichment_spec.js @@ -3,10 +3,7 @@ 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 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'; describe('FPD enrichment', () => { let sandbox; @@ -216,7 +213,7 @@ describe('FPD enrichment', () => { ua: 'ua' }) }) - }); + }) }); }); @@ -278,13 +275,7 @@ describe('FPD enrichment', () => { describe('sua', () => { it('does not set device.sua if resolved sua is null', () => { - sandbox.stub(dep, 'getHighEntropySUA').returns(Promise.resolve()); - // Add hints so it will attempt to retrieve high entropy values - config.setConfig({ - firstPartyData: { - uaHints: ['bitness'], - } - }); + sandbox.stub(dep, 'getHighEntropySUA').returns(Promise.resolve()) return fpd().then(ortb2 => { expect(ortb2.device.sua).to.not.exist; }) @@ -313,71 +304,6 @@ describe('FPD enrichment', () => { }); }); - describe('privacy sandbox cookieDeprecationLabel', () => { - let isAllowed, cdep, shouldCleanupNav = false; - - before(() => { - if (!navigator.cookieDeprecationLabel) { - navigator.cookieDeprecationLabel = {}; - shouldCleanupNav = true; - } - }); - - after(() => { - if (shouldCleanupNav) { - delete navigator.cookieDeprecationLabel; - } - }); - - beforeEach(() => { - isAllowed = true; - sandbox.stub(activities, 'isActivityAllowed').callsFake((activity, params) => { - if (activity === ACTIVITY_ACCESS_DEVICE && params[ACTIVITY_PARAM_COMPONENT] === 'prebid.cdep') { - return isAllowed; - } else { - throw new Error('Unexpected activity check'); - } - }); - sandbox.stub(window.navigator, 'cookieDeprecationLabel').value({ - getValue: sinon.stub().callsFake(() => cdep) - }) - }) - - it('enrichment sets device.ext.cdep when allowed and navigator.getCookieDeprecationLabel exists', () => { - cdep = Promise.resolve('example-test-label'); - return fpd().then(ortb2 => { - expect(ortb2.device.ext.cdep).to.eql('example-test-label'); - }) - }); - - Object.entries({ - 'not allowed'() { - isAllowed = false; - }, - 'not supported'() { - delete navigator.cookieDeprecationLabel - } - }).forEach(([t, setup]) => { - it(`if ${t}, the navigator API is not called and no enrichment happens`, () => { - setup(); - cdep = Promise.resolve('example-test-label'); - return fpd().then(ortb2 => { - expect(ortb2.device.ext).to.not.exist; - if (navigator.cookieDeprecationLabel) { - sinon.assert.notCalled(navigator.cookieDeprecationLabel.getValue); - } - }) - }); - }) - - it('if the navigator API returns a promise that rejects, the enrichment does not halt forever', () => { - cdep = Promise.reject(new Error('oops, something went wrong')); - return fpd().then(ortb2 => { - expect(ortb2.device.ext).to.not.exist; - }) - }); - }); - it('leaves only one of app, site, dooh', () => { return fpd({ app: {p: 'val'}, diff --git a/test/spec/fpd/sua_spec.js b/test/spec/fpd/sua_spec.js index 63e0068d0ef..431f47268d3 100644 --- a/test/spec/fpd/sua_spec.js +++ b/test/spec/fpd/sua_spec.js @@ -165,14 +165,6 @@ describe('uaDataToSUA', () => { }); describe('lowEntropySUAAccessor', () => { - // Set up a mock data with readonly property - class MockUserAgentData {} - Object.defineProperty(MockUserAgentData.prototype, 'mobile', { - value: false, - writable: false, - enumerable: true - }); - function getSUA(uaData) { return lowEntropySUAAccessor(uaData)(); } @@ -189,10 +181,6 @@ describe('lowEntropySUAAccessor', () => { it('should return null if uaData is empty', () => { expect(getSUA({})).to.eql(null); }) - - it('should return mobile and source', () => { - expect(getSUA(new MockUserAgentData())).to.eql({mobile: 0, source: 1}) - }) }); describe('highEntropySUAAccessor', () => { diff --git a/test/spec/libraries/cmp/cmpClient_spec.js b/test/spec/libraries/cmp/cmpClient_spec.js deleted file mode 100644 index adbbbf5cb1d..00000000000 --- a/test/spec/libraries/cmp/cmpClient_spec.js +++ /dev/null @@ -1,296 +0,0 @@ -import {cmpClient, MODE_CALLBACK, MODE_RETURN} from '../../../../libraries/cmp/cmpClient.js'; - -describe('cmpClient', () => { - function mockWindow(props = {}) { - let listeners = []; - const win = { - addEventListener: sinon.stub().callsFake((evt, listener) => { - evt === 'message' && listeners.push(listener) - }), - removeEventListener: sinon.stub().callsFake((evt, listener) => { - evt === 'message' && (listeners = listeners.filter((l) => l !== listener)); - }), - postMessage: sinon.stub().callsFake((msg) => { - listeners.forEach(ln => ln({data: msg})) - }), - ...props, - }; - win.top = win.parent?.top || win; - return win; - } - - it('should return undefined when there is no CMP', () => { - 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; - }) - - describe('direct access', () => { - let mockApiFn; - beforeEach(() => { - mockApiFn = sinon.stub(); - }) - Object.entries({ - '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) - }); - - it('should mark client function as direct', () => { - expect(mkClient().isDirect).to.equal(true); - }); - - it('should find and call the CMP api function', () => { - mkClient()({command: 'mockCmd'}); - sinon.assert.calledWith(mockApiFn, 'mockCmd'); - }); - - describe('should return a promise that', () => { - let cbResult; - beforeEach(() => { - cbResult = []; - mockApiFn.callsFake((cmd, callback) => { - if (typeof callback === 'function') { - callback.apply(this, cbResult); - } - return 'val' - }) - }) - - Object.entries({ - callback: [sinon.stub(), 'undefined', undefined], - 'callback, mode = MODE_CALLBACK': [sinon.stub(), 'undefined', undefined, MODE_CALLBACK], - 'callback, mode = MODE_RETURN': [sinon.stub(), 'api return value', 'val', MODE_RETURN], - 'no callback': [undefined, 'api return value', 'val'], - 'no callback, mode = MODE_CALLBACK': [undefined, 'callback arg', 'cbVal', MODE_CALLBACK], - 'no callback, mode = MODE_RETURN': [undefined, 'api return value', 'val', MODE_RETURN], - }).forEach(([t, [callback, tResult, expectedResult, mode]]) => { - describe(`when ${t} is provided`, () => { - Object.entries({ - 'no success flag': undefined, - 'success is set': true - }).forEach(([t, success]) => { - it(`resolves to ${tResult} (${t})`, (done) => { - cbResult = ['cbVal', success]; - mkClient({mode})({callback}).then((val) => { - expect(val).to.equal(expectedResult); - done(); - }) - }); - - it('should pass either a function or undefined as callback', () => { - mkClient({mode})({callback}); - sinon.assert.calledWith(mockApiFn, sinon.match.any, sinon.match(arg => typeof arg === 'undefined' || typeof arg === 'function')) - }) - }); - }) - }); - - it('rejects to undefined when callback is provided and success = false', (done) => { - cbResult = ['cbVal', false]; - mkClient()({callback: sinon.stub()}).catch(val => { - expect(val).to.not.exist; - done(); - }) - }); - - 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 => { - expect(val).to.eql('cbVal'); - done(); - }) - }) - - it('rejects when CMP api throws', (done) => { - mockApiFn.reset(); - const e = new Error(); - mockApiFn.throws(e); - mkClient()({}).catch(val => { - expect(val).to.equal(e); - done(); - }); - }); - }) - - it('should use apiArgs to choose and order the arguments to pass to the API fn', () => { - mkClient({apiArgs: ['parameter', 'command']})({ - command: 'mockCmd', - parameter: 'mockParam', - callback() {} - }); - sinon.assert.calledWith(mockApiFn, 'mockParam', 'mockCmd'); - }); - - it('should not choke on .close()', () => { - mkClient({}).close(); - }) - }) - }) - }) - - describe('postMessage access', () => { - let messenger, win, response; - beforeEach(() => { - response = {}; - messenger = sinon.stub().callsFake((msg) => { - if (msg.mockApiCall) { - win.postMessage({mockApiReturn: {callId: msg.mockApiCall.callId, ...response}}); - } - }); - }); - - function mkClient(options) { - return cmpClient(Object.assign({apiName: 'mockApi'}, options), win); - } - - Object.entries({ - 'on same frame': () => { - win = mockWindow({frames: {mockApiLocator: true}}); - win.addEventListener('message', (evt) => messenger(evt.data)); - }, - 'on parent frame': () => { - win = mockWindow({parent: mockWindow({frames: {mockApiLocator: true}})}) - win.parent.addEventListener('message', evt => messenger(evt.data)) - } - }).forEach(([t, setup]) => { - describe(t, () => { - beforeEach(setup); - - it('should mark client as not direct', () => { - expect(mkClient().isDirect).to.equal(false); - }); - - it('should find and message the CMP frame', () => { - mkClient()({command: 'mockCmd', parameter: 'param'}); - sinon.assert.calledWithMatch(messenger, { - mockApiCall: { - command: 'mockCmd', - parameter: 'param' - } - }) - }); - - it('should use apiArgs to choose what to include in the message payload', () => { - mkClient({apiArgs: ['command']})({ - command: 'cmd', - parameter: 'param' - }); - sinon.assert.calledWithMatch(messenger, sinon.match((arg) => { - return arg.mockApiCall.command === 'cmd' && - !arg.mockApiCall.hasOwnProperty('parameter'); - })) - }); - - it('should not include callback in the payload, but still run it on response', () => { - const cb = sinon.stub(); - mkClient({apiArgs: ['command', 'callback']})({ - command: 'cmd', - callback: cb - }); - sinon.assert.calledWithMatch(messenger, sinon.match(arg => !arg.mockApiCall.hasOwnProperty('callback'))); - sinon.assert.called(cb); - }); - - 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}); - sinon.assert.calledWith(cb, 'one', 'two'); - }) - - describe('should return a promise that', () => { - beforeEach(() => { - response = {returnValue: 'val'} - }) - Object.entries({ - 'callback': [sinon.stub(), 'undefined', undefined], - 'callback, mode = MODE_RETURN': [sinon.stub(), 'undefined', undefined, MODE_RETURN], - 'callback, mode = MODE_CALLBACK': [sinon.stub(), 'undefined', undefined, MODE_CALLBACK], - 'no callback': [undefined, 'response returnValue', 'val'], - 'no callback, mode = MODE_RETURN': [undefined, 'undefined', undefined, MODE_RETURN], - 'no callback, mode = MODE_CALLBACK': [undefined, 'response returnValue', 'val', MODE_CALLBACK], - }).forEach(([t, [callback, tResult, expectedResult, mode]]) => { - describe(`when ${t} is provided`, () => { - Object.entries({ - 'no success flag': {}, - 'with success flag': {success: true} - }).forEach(([t, resp]) => { - it(`resolves to ${tResult} (${t})`, () => { - Object.assign(response, resp); - mkClient({mode})({callback}).then((val) => { - expect(val).to.equal(expectedResult); - }) - }) - }); - - 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) => { - expect(err).to.equal(expectedResult); - done(); - }); - }); - } - }) - }); - }); - - describe('messages with same callID', () => { - let callback, callId; - - function runCallback(returnValue) { - win.postMessage({mockApiReturn: {callId, returnValue}}); - } - - beforeEach(() => { - callId = null; - messenger.reset(); - messenger.callsFake((msg) => { - if (msg.mockApiCall) callId = msg.mockApiCall.callId; - }); - callback = sinon.stub(); - }); - - it('should re-use callback for messages with same callId', () => { - mkClient()({callback}); - expect(callId).to.exist; - runCallback('a'); - runCallback('b'); - sinon.assert.calledWith(callback, 'a'); - sinon.assert.calledWith(callback, 'b'); - }); - - it('should NOT re-use callback if once = true', () => { - mkClient()({callback}, true); - expect(callId).to.exist; - runCallback('a'); - runCallback('b'); - sinon.assert.calledWith(callback, 'a'); - sinon.assert.calledOnce(callback); - }); - - it('should NOT fire again after .close()', () => { - const client = mkClient(); - client({callback}); - runCallback('a'); - client.close(); - runCallback('b'); - sinon.assert.calledWith(callback, 'a'); - sinon.assert.calledOnce(callback); - }) - }); - }); - }); - }); -}); diff --git a/test/spec/libraries/currencyUtils_spec.js b/test/spec/libraries/currencyUtils_spec.js deleted file mode 100644 index 9d3d73e6a5f..00000000000 --- a/test/spec/libraries/currencyUtils_spec.js +++ /dev/null @@ -1,113 +0,0 @@ -import {getGlobal} from 'src/prebidGlobal.js'; -import {convertCurrency, currencyCompare, currencyNormalizer} from 'libraries/currencyUtils/currency.js'; - -describe('currency utils', () => { - let sandbox; - before(() => { - if (!getGlobal().convertCurrency) { - getGlobal().convertCurrency = () => null; - getGlobal().convertCurrency.mock = true; - } - }); - - after(() => { - if (getGlobal().convertCurrency.mock) { - delete getGlobal().convertCurrency; - } - }) - - beforeEach(() => { - sandbox = sinon.sandbox.create(); - }); - - afterEach(() => { - sandbox.restore(); - }); - - describe('convertCurrency', () => { - Object.entries({ - 'not available': () => sandbox.stub(getGlobal(), 'convertCurrency').value(undefined), - 'throwing errors': () => sandbox.stub(getGlobal(), 'convertCurrency').callsFake(() => { throw new Error(); }), - }).forEach(([t, setup]) => { - describe(`when currency module is ${t}`, () => { - beforeEach(setup); - - it('should "convert" to the same currency', () => { - expect(convertCurrency(123, 'mock', 'mock', false)).to.eql(123); - }); - - it('should throw when suppressErrors = false', () => { - expect(() => convertCurrency(123, 'c1', 'c2', false)).to.throw(); - }); - - it('should return input value when suppressErrors = true', () => { - expect(convertCurrency(123, 'c1', 'c2', true)).to.eql(123); - }) - }) - }); - - describe('when currency module is working', () => { - beforeEach(() => { - sandbox.stub(getGlobal(), 'convertCurrency').callsFake((amt) => amt * 10) - }); - - it('should be used for actual conversions', () => { - expect(convertCurrency(123, 'c1', 'c2')).to.eql(1230); - sinon.assert.calledWith(getGlobal().convertCurrency, 123, 'c1', 'c2'); - }); - - it('should NOT be used when no conversion is necessary', () => { - expect(convertCurrency(123, 'cur', 'cur')).to.eql(123); - sinon.assert.notCalled(getGlobal().convertCurrency); - }) - }) - }); - - describe('Currency normalization', () => { - let mockConvert; - beforeEach(() => { - mockConvert = sinon.stub().callsFake((amt, from, to) => { - if (from === to) return amt; - return amt / from * to - }) - }); - - describe('currencyNormalizer', () => { - it('converts to toCurrency if set', () => { - const normalize = currencyNormalizer(10, true, mockConvert); - expect(normalize(1, 1)).to.eql(10); - expect(normalize(10, 100)).to.eql(1); - }); - - it('converts to first currency if toCurrency is not set', () => { - const normalize = currencyNormalizer(null, true, mockConvert); - expect(normalize(1, 1)).to.eql(1); - expect(normalize(1, 10)).to.eql(0.1); - }); - - [true, false].forEach(bestEffort => { - it(`passes bestEffort = ${bestEffort} to convert`, () => { - currencyNormalizer(null, bestEffort, mockConvert)(1, 1); - sinon.assert.calledWith(mockConvert, 1, 1, 1, bestEffort); - }) - }) - }); - - describe('currencyCompare', () => { - let compare - beforeEach(() => { - 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], - ].forEach(([a, b, expected]) => { - it(`should compare ${a.amount}/${a.cur} and ${b.amount}/${b.cur}`, () => { - expect(compare(a, b)).to.equal(expected); - expect(compare(b, a)).to.equal(-expected); - }); - }); - }) - }) -}) diff --git a/test/spec/libraries/mspa/activityControls_spec.js b/test/spec/libraries/mspa/activityControls_spec.js deleted file mode 100644 index f232dc2563f..00000000000 --- a/test/spec/libraries/mspa/activityControls_spec.js +++ /dev/null @@ -1,256 +0,0 @@ -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) { - return Object.assign({ - // not covered, opt in to targeted, sale, and share, all notices given, opt into precise geo - Gpc: 0, - KnownChildSensitiveDataConsents: [0, 0], - MspaCoveredTransaction: 2, - MspaOptOutOptionMode: 0, - MspaServiceProviderMode: 0, - PersonalDataConsents: 0, - SaleOptOut: 2, - SaleOptOutNotice: 1, - SensitiveDataLimitUseNotice: 1, - SensitiveDataProcessing: [0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0], - SensitiveDataProcessingOptOutNotice: 1, - SharingNotice: 1, - SharingOptOut: 2, - SharingOptOutNotice: 1, - TargetedAdvertisingOptOut: 2, - TargetedAdvertisingOptOutNotice: 1, - Version: 1 - }, flags) - } - describe('isBasicConsentDenied', () => { - it('should be false (basic consent conditions pass) with variety of notice and opt in', () => { - const result = isBasicConsentDenied(mkConsent()); - expect(result).to.equal(false); - }); - it('should be true (basic consent conditions do not pass) with personal data consent set to true (invalid state)', () => { - const result = isBasicConsentDenied(mkConsent({ - PersonalDataConsents: 2 - })); - expect(result).to.equal(true); - }); - it('should be true (basic consent conditions do not pass) with covered set to zero (invalid state)', () => { - const result = isBasicConsentDenied(mkConsent({ - MspaCoveredTransaction: 0 - })); - expect(result).to.equal(true); - }); - it('should not deny when consent for under-13 is null', () => { - expect(isBasicConsentDenied(mkConsent({ - KnownChildSensitiveDataConsents: [0, null] - }))).to.be.false; - }) - }); - - describe('isConsentDenied', () => { - it('should be false (consent given personalized ads / sale / share) with variety of notice and opt in', () => { - const result = isConsentDenied(mkConsent()); - expect(result).to.equal(false); - }); - it('should be true (no consent) on opt out of targeted ads via TargetedAdvertisingOptOut', () => { - const result = isConsentDenied(mkConsent({ - TargetedAdvertisingOptOut: 1 - })); - expect(result).to.equal(true); - }); - it('should be true (no consent) on opt out of targeted ads via no TargetedAdvertisingOptOutNotice', () => { - const result = isConsentDenied(mkConsent({ - TargetedAdvertisingOptOutNotice: 2 - })); - expect(result).to.equal(true); - }); - it('should be true (no consent) if TargetedAdvertisingOptOutNotice is 0 and TargetedAdvertisingOptOut is 2', () => { - const result = isConsentDenied(mkConsent({ - TargetedAdvertisingOptOutNotice: 0 - })); - expect(result).to.equal(true); - }); - it('requires also SharingNotice to accept opt-in for Sharing', () => { - expect(isConsentDenied(mkConsent({ - SharingNotice: 0 - }))).to.be.true; - }) - }); - - describe('isTransmitUfpdConsentDenied', () => { - it('should be false (consent given to add ufpd) with variety of notice and opt in', () => { - const result = isTransmitUfpdConsentDenied(mkConsent()); - expect(result).to.equal(false); - }); - Object.entries({ - 'health information': 2, - 'biometric data': 6, - }).forEach(([t, flagNo]) => { - it(`'should be true (consent denied to add ufpd) if no consent to process ${t}'`, () => { - const consent = mkConsent(); - consent.SensitiveDataProcessing[flagNo] = 1; - expect(isTransmitUfpdConsentDenied(consent)).to.be.true; - }) - }); - - ['SharingNotice', 'SensitiveDataLimitUseNotice'].forEach(flag => { - it(`should be true (consent denied to add ufpd) without ${flag}`, () => { - expect(isTransmitUfpdConsentDenied(mkConsent({ - [flag]: 2 - }))).to.be.true; - }) - }); - - ['SaleOptOut', 'TargetedAdvertisingOptOut'].forEach(flag => { - it(`should be true (consent denied to add ufpd) with ${flag}`, () => { - expect(isTransmitUfpdConsentDenied(mkConsent({ - [flag]: 1 - }))).to.be.true; - }) - }); - - it('should be true (basic consent conditions do not pass) with sensitive opt in but no notice', () => { - const cd = mkConsent({ - SensitiveDataLimitUseNotice: 0 - }); - cd.SensitiveDataProcessing[0] = 2; - expect(isTransmitUfpdConsentDenied(cd)).to.be.true; - }); - - it('should deny when sensitive notice is missing', () => { - const result = isTransmitUfpdConsentDenied(mkConsent({ - SensitiveDataLimitUseNotice: 2 - })); - expect(result).to.equal(true); - }); - - it('should not deny when biometric data opt-out is null', () => { - const cd = mkConsent(); - cd.SensitiveDataProcessing[6] = null; - expect(isTransmitUfpdConsentDenied(cd)).to.be.false; - }) - }); - - describe('isTransmitGeoConsentDenied', () => { - function geoConsent(geoOptOut, flags) { - const consent = mkConsent(flags); - consent.SensitiveDataProcessing[7] = geoOptOut; - return consent; - } - it('should be true (consent denied to add precise geo) -- sensitive flag denied', () => { - const result = isTransmitGeoConsentDenied(geoConsent(1)); - expect(result).to.equal(true); - }); - it('should be true (consent denied to add precise geo) -- sensitive data limit usage not given', () => { - const result = isTransmitGeoConsentDenied(geoConsent(1, { - SensitiveDataLimitUseNotice: 0 - })); - expect(result).to.equal(true); - }); - it('should be false (consent given to add precise geo) -- sensitive position 8 (index 7) is true', () => { - const result = isTransmitGeoConsentDenied(geoConsent(2)); - expect(result).to.equal(false); - }); - }) -}); - -describe('mspaRule', () => { - it('does not apply if SID is not applicable', () => { - const rule = mspaRule([1, 2], () => null, () => true, () => [3, 4]); - expect(rule()).to.not.exist; - }); - - it('does not apply when no SID is applicable', () => { - const rule = mspaRule([1], () => null, () => true, () => []); - expect(rule()).to.not.exist; - }); - - describe('when SID is applicable', () => { - let consent, denies; - function mkRule() { - return mspaRule([1, 2], () => consent, denies, () => [2]) - } - - beforeEach(() => { - consent = null; - denies = sinon.stub(); - }); - - it('should deny when no consent is available', () => { - expect(mkRule()().allow).to.equal(false); - }); - - Object.entries({ - 'denies': true, - 'allows': false - }).forEach(([t, denied]) => { - it(`should check if deny fn ${t}`, () => { - denies.returns(denied); - consent = {mock: 'value'}; - const result = mkRule()(); - sinon.assert.calledWith(denies, consent); - if (denied) { - expect(result.allow).to.equal(false); - } else { - expect(result).to.not.exist; - } - }) - }) - }) -}); - -describe('setupRules', () => { - let rules, registerRule, isAllowed, consent; - beforeEach(() => { - rules = { - mockActivity: sinon.stub().returns(true) - }; - ([registerRule, isAllowed] = ruleRegistry()); - consent = { - applicableSections: [1], - parsedSections: { - mockApi: [ - { - mock: 'consent' - } - ] - } - }; - }); - - function runSetup(api, sids, normalize) { - return setupRules(api, sids, normalize, rules, registerRule, () => consent) - } - - it('should use flatten section data for the given api', () => { - runSetup('mockApi', [1]); - expect(isAllowed('mockActivity', {})).to.equal(false); - sinon.assert.calledWith(rules.mockActivity, {mock: 'consent'}) - }); - - it('should not choke when no consent data is available', () => { - consent = null; - runSetup('mockApi', [1]); - expect(isAllowed('mockActivity', {})).to.equal(true); - }); - - it('should check applicableSections against given SIDs', () => { - runSetup('mockApi', [2]); - expect(isAllowed('mockActivity', {})).to.equal(true); - }); - - it('should pass flattened consent through normalizeConsent', () => { - const normalize = sinon.stub().returns({normalized: 'consent'}) - runSetup('mockApi', [1], normalize); - expect(isAllowed('mockActivity', {})).to.equal(false); - sinon.assert.calledWith(normalize, {mock: 'consent'}); - sinon.assert.calledWith(rules.mockActivity, {normalized: 'consent'}); - }); - - it('should return a function that unregisters activity controls', () => { - const dereg = runSetup('mockApi', [1]); - dereg(); - expect(isAllowed('mockActivity', {})).to.equal(true); - }); -}) diff --git a/test/spec/libraries/sizeUtils_spec.js b/test/spec/libraries/sizeUtils_spec.js deleted file mode 100644 index 1c954c6accf..00000000000 --- a/test/spec/libraries/sizeUtils_spec.js +++ /dev/null @@ -1,30 +0,0 @@ -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 () { - let sizes = getAdUnitSizes(); - expect(sizes).to.be.undefined; - }); - - it('returns an empty array when invalid data is present in adUnit object', function () { - let 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]}); - expect(sizes).to.deep.equal([[300, 250]]); - - 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]}}}); - expect(sizes).to.deep.equal([[300, 250]]); - - sizes = getAdUnitSizes({mediaTypes: {banner: {sizes: [[300, 250], [300, 600]]}}}); - expect(sizes).to.deep.equal([[300, 250], [300, 600]]); - }); -}); diff --git a/test/spec/libraries/urlUtils_spec.js b/test/spec/libraries/urlUtils_spec.js deleted file mode 100644 index 9dd66b05407..00000000000 --- a/test/spec/libraries/urlUtils_spec.js +++ /dev/null @@ -1,24 +0,0 @@ -import {tryAppendQueryString} from '../../../libraries/urlUtils/urlUtils.js'; -import assert from 'assert'; - -describe('tryAppendQueryString', function () { - it('should append query string to existing url', function () { - var url = 'www.a.com?'; - var key = 'b'; - var value = 'c'; - - var output = tryAppendQueryString(url, key, value); - - var expectedResult = url + key + '=' + encodeURIComponent(value) + '&'; - assert.equal(output, expectedResult); - }); - - it('should return existing url, if the value is empty', function () { - var url = 'www.a.com?'; - var key = 'b'; - var value = ''; - - var output = tryAppendQueryString(url, key, value); - assert.equal(output, url); - }); -}); diff --git a/test/spec/modules/33acrossAnalyticsAdapter_spec.js b/test/spec/modules/33acrossAnalyticsAdapter_spec.js deleted file mode 100644 index 9e0d928cd97..00000000000 --- a/test/spec/modules/33acrossAnalyticsAdapter_spec.js +++ /dev/null @@ -1,1163 +0,0 @@ -// @ts-nocheck -import analyticsAdapter from 'modules/33acrossAnalyticsAdapter.js'; -import { log } from 'modules/33acrossAnalyticsAdapter.js'; -import * as mockGpt from 'test/spec/integration/faker/googletag.js'; -import * as events from 'src/events.js'; -import * as faker from 'faker'; -import CONSTANTS from 'src/constants.json'; -import { gdprDataHandler, gppDataHandler, uspDataHandler } from '../../../src/adapterManager'; -import { DEFAULT_ENDPOINT, POST_GAM_TIMEOUT, locals } from '../../../modules/33acrossAnalyticsAdapter'; -const { EVENTS, BID_STATUS } = CONSTANTS; - -describe('33acrossAnalyticsAdapter:', function () { - let sandbox; - let assert = getLocalAssert(); - - beforeEach(function () { - mockGpt.reset(); - - sandbox = sinon.createSandbox({ - useFakeTimers: { - now: new Date(2023, 3, 3, 0, 1, 33, 425), - }, - }); - - sandbox.stub(events, 'getEvents').returns([]); - - sandbox.spy(log, 'info'); - sandbox.spy(log, 'warn'); - sandbox.spy(log, 'error'); - - sandbox.stub(navigator, 'sendBeacon').callsFake(function (url, data) { - const json = JSON.parse(data); - assert.isValidAnalyticsReport(json); - - return true; - }); - }); - - afterEach(function () { - analyticsAdapter.disableAnalytics(); - mockGpt.enable(); - sandbox.restore(); - }); - - describe('enableAnalytics:', function () { - context('When pid is given', function () { - context('but endpoint is not', function () { - it('uses the default endpoint', function () { - analyticsAdapter.enableAnalytics({ - options: { - pid: 'test-pid', - }, - }); - - assert.equal(analyticsAdapter.getUrl(), DEFAULT_ENDPOINT); - }); - }); - - context('but the endpoint is invalid', function () { - it('logs an info message', function () { - analyticsAdapter.enableAnalytics({ - options: { - pid: 'test-pid', - endpoint: 'foo' - }, - }); - - assert.calledWithExactly(log.info, 'Invalid endpoint provided for "options.endpoint". Using default endpoint.'); - }); - }); - }); - - context('When endpoint is given', function () { - context('but pid is not', function () { - it('logs an error message', function () { - analyticsAdapter.enableAnalytics({ - options: { - endpoint: faker.internet.url() - }, - }); - - assert.calledWithExactly(log.error, 'No partnerId provided for "options.pid". No analytics will be sent.'); - }); - }); - }); - - context('When pid and endpoint are given', function () { - context('and an invalid timeout config value is given', function () { - it('logs an info message', function () { - [null, 'foo', -1].forEach(timeout => { - analyticsAdapter.enableAnalytics({ - options: { - pid: 'test-pid', - endpoint: 'http://test-endpoint', - timeout - }, - }); - analyticsAdapter.disableAnalytics(); - - assert.calledWithExactly(log.info, 'Invalid timeout provided for "options.timeout". Using default timeout of 10000ms.'); - log.info.resetHistory(); - }); - }); - }); - }); - }); - - // check that upcoming tests are derived from a valid report - describe('Report Mocks', function () { - it('the report should have the correct format', function () { - assert.isValidAnalyticsReport(createReportWithThreeBidWonEvents()); - }); - }); - - describe('Event Handling', function () { - beforeEach(function () { - this.defaultTimeout = 10000; - this.enableAnalytics = (options) => { - analyticsAdapter.enableAnalytics({ - options: { - endpoint: 'http://test-endpoint', - pid: 'test-pid', - timeout: this.defaultTimeout, - ...options - }, - }); - window.googletag.cmd.forEach(cmd => cmd()); - } - }); - - context('when an auction is complete', function () { - context('and the AnalyticsReport is sent successfully to the given endpoint', function () { - it('calls "sendBeacon" with all won bids', function () { - const endpoint = faker.internet.url(); - this.enableAnalytics({ endpoint }); - - navigator.sendBeacon - .withArgs(endpoint, JSON.stringify(createReportWithThreeBidWonEvents())); - - performStandardAuction(); - sandbox.clock.tick(this.defaultTimeout + 1000); - - const [url, jsonString] = navigator.sendBeacon.firstCall.args; - const { auctions } = JSON.parse(jsonString); - - assert.lengthOf(mapToBids(auctions).filter(bid => bid.hasWon), 3); - }); - - it('calls "sendBeacon" with the correct report string', function () { - const endpoint = faker.internet.url(); - this.enableAnalytics({ endpoint }); - - navigator.sendBeacon - .withArgs(endpoint, JSON.stringify(createReportWithThreeBidWonEvents())); - - performStandardAuction(); - sandbox.clock.tick(this.defaultTimeout + 1000); - - assert.calledOnceWithStringJsonEquivalent(navigator.sendBeacon, endpoint, createReportWithThreeBidWonEvents()); - }); - - it('logs an info message containing the report', function () { - const endpoint = faker.internet.url(); - this.enableAnalytics({ endpoint }); - - navigator.sendBeacon - .withArgs(endpoint, JSON.stringify(createReportWithThreeBidWonEvents())) - .returns(true); - - performStandardAuction(); - sandbox.clock.tick(this.defaultTimeout + 1000); - - assert.calledWithExactly(log.info, `Analytics report sent to ${endpoint}`, createReportWithThreeBidWonEvents()); - }); - - it('calls "sendBeacon" as soon as all values are available (before timeout)', function () { - const endpoint = faker.internet.url(); - this.enableAnalytics({ endpoint }); - - navigator.sendBeacon - .withArgs(endpoint, JSON.stringify(createReportWithThreeBidWonEvents())); - - performStandardAuction(); - sandbox.clock.tick(1); - - assert.calledOnceWithStringJsonEquivalent(navigator.sendBeacon, endpoint, createReportWithThreeBidWonEvents()); - }); - }); - - context('and a valid US Privacy configuration is present', function () { - ['1YNY', '1---', '1NY-', '1Y--', '1--Y', '1N--', '1--N', '1NNN'].forEach(consent => { - it(`calls "sendBeacon" with a report containing the "${consent}" privacy string`, function () { - sandbox.stub(uspDataHandler, 'getConsentData').returns(consent); - this.enableAnalytics(); - - const reportWithConsent = { - ...createReportWithThreeBidWonEvents(), - usPrivacy: consent - }; - navigator.sendBeacon - .withArgs('http://test-endpoint', reportWithConsent); - - performStandardAuction(); - sandbox.clock.tick(this.defaultTimeout + 1000); - - assert.calledOnceWithStringJsonEquivalent(navigator.sendBeacon, 'http://test-endpoint', reportWithConsent); - }); - }); - }); - - context('and a GDPR Privacy configuration is present', function () { - it('it calls "sendBeacon" with a report containing the GDPR consent string', function () { - sandbox.stub(gdprDataHandler, 'getConsentData').returns({ - consentString: 'foo', - gdprApplies: true - }); - this.enableAnalytics(); - - const reportWithConsent = { - ...createReportWithThreeBidWonEvents(), - gdpr: 1, - gdprConsent: 'foo' - }; - navigator.sendBeacon - .withArgs('http://test-endpoint', reportWithConsent); - - performStandardAuction(); - sandbox.clock.tick(this.defaultTimeout + 1); - - assert.calledOnceWithStringJsonEquivalent(navigator.sendBeacon, 'http://test-endpoint', reportWithConsent); - }); - }); - }); - - context('when an auction is complete and a GPP configuration is present', function () { - it('it calls "sendBeacon" with a report containing the GPP consent string', function () { - sandbox.stub(gppDataHandler, 'getConsentData').returns({ - gppString: 'gppString', - applicableSections: [7] - }); - this.enableAnalytics(); - - const reportWithConsent = { - ...createReportWithThreeBidWonEvents(), - gpp: 'gppString', - gppSid: [7] - }; - navigator.sendBeacon - .withArgs('http://test-endpoint', reportWithConsent); - - performStandardAuction(); - sandbox.clock.tick(this.defaultTimeout + 1); - - assert.calledOnceWithStringJsonEquivalent(navigator.sendBeacon, 'http://test-endpoint', reportWithConsent); - }); - }); - - context('when an error occurs while sending the AnalyticsReport', function () { - it('logs an error', function () { - this.enableAnalytics(); - navigator.sendBeacon.returns(false); - - performStandardAuction(); - sandbox.clock.tick(this.defaultTimeout + 1000); - - assert.calledWithExactly(log.error, 'Analytics report exceeded User-Agent data limits and was not sent.', createReportWithThreeBidWonEvents()); - }); - }); - - context('when an auction report was already sent', function () { - context('and a new bid won event is returned after the report completes', function () { - it('finishes the auction without error', function () { - const incompleteAnalyticsReport = createReportWithThreeBidWonEvents(); - incompleteAnalyticsReport.auctions.forEach(auction => { - auction.adUnits.forEach(adUnit => { - adUnit.bids.forEach(bid => { - delete bid.bidResponse; - bid.hasWon = 0; - bid.status = 'pending'; - }); - }); - }); - - this.enableAnalytics(); - const { prebid: [auction] } = getMockEvents(); - - events.emit(EVENTS.AUCTION_INIT, auction.AUCTION_INIT); - for (let bidRequestedEvent of auction.BID_REQUESTED) { - events.emit(EVENTS.BID_REQUESTED, bidRequestedEvent); - }; - - sandbox.clock.tick(this.defaultTimeout + 1000); - - for (let bidResponseEvent of auction.BID_RESPONSE) { - events.emit(EVENTS.BID_RESPONSE, bidResponseEvent); - }; - for (let bidWonEvent of auction.BID_WON) { - events.emit(EVENTS.BID_WON, bidWonEvent); - }; - - events.emit(EVENTS.AUCTION_END, auction.AUCTION_END); - - sandbox.clock.tick(1); - - assert.calledOnceWithStringJsonEquivalent(navigator.sendBeacon, 'http://test-endpoint', incompleteAnalyticsReport); - }); - }); - - context('and another auction completes after that', function () { - it('sends the new report', function () { - const endpoint = faker.internet.url(); - this.enableAnalytics({ endpoint }); - - navigator.sendBeacon - .withArgs(endpoint, JSON.stringify(createReportWithThreeBidWonEvents())); - - performStandardAuction(); - sandbox.clock.tick(this.defaultTimeout + 1000); - - performStandardAuction(); - sandbox.clock.tick(this.defaultTimeout + 1000); - - assert.calledTwice(navigator.sendBeacon); - }); - }); - }); - - context('when two auctions overlap', function() { - it('sends a report for each auction', function () { - const endpoint = faker.internet.url(); - this.enableAnalytics({ endpoint }); - - navigator.sendBeacon - .withArgs(endpoint, JSON.stringify(createReportWithThreeBidWonEvents())); - - performStandardAuction(); - performStandardAuction(); - sandbox.clock.tick(this.defaultTimeout + 1000); - - assert.calledTwice(navigator.sendBeacon); - }); - }); - - context('when an AUCTION_END event is received before BID_WON events', function () { - it('sends a report with the bids that have won after all bids are won', function () { - const endpoint = faker.internet.url(); - this.enableAnalytics({ endpoint }); - - navigator.sendBeacon - .withArgs(endpoint, JSON.stringify(createReportWithThreeBidWonEvents())); - - const { prebid: [auction] } = getMockEvents(); - - performStandardAuction({ exclude: [EVENTS.BID_WON] }); - - assert.notCalled(navigator.sendBeacon); - for (let bidWon of auction.BID_WON) { - events.emit(EVENTS.BID_WON, bidWon); - } - assert.calledOnceWithStringJsonEquivalent(navigator.sendBeacon, endpoint, createReportWithThreeBidWonEvents()); - }); - }); - - context('when a BID_WON event is received', function () { - context('and there is no record of that bid being requested', function () { - it('logs a warning message', function () { - this.enableAnalytics(); - - const mockEvents = getMockEvents(); - const { prebid } = mockEvents; - const [auction] = prebid; - - events.emit(EVENTS.AUCTION_INIT, auction.AUCTION_INIT); - - const fakeBidWonEvent = Object.assign(auction.BID_WON[0], { - transactionId: 'foo' - }) - - events.emit(EVENTS.BID_WON, fakeBidWonEvent); - - const { auctionId, requestId } = fakeBidWonEvent; - assert.calledWithExactly(log.error, `Cannot find bid "${requestId}" in auction "${auctionId}".`); - }); - }); - }); - - context('when a BID_REJECTED event is received', function () { - it(`marks the rejected bid as "rejected"`, function () { - this.enableAnalytics(); - - const auction = getMockEvents().prebid[0]; - - // Start the auction - events.emit(EVENTS.AUCTION_INIT, auction.AUCTION_INIT); - for (let bidRequestedEvent of auction.BID_REQUESTED) { - events.emit(EVENTS.BID_REQUESTED, bidRequestedEvent); - }; - - // Reject first bid - const bidToReject = auction.BID_REQUESTED[0].bids[0]; - events.emit(EVENTS.BID_REJECTED, auction.BID_REJECTED[0]); - - // Accept remaining bids - for (let i = 1; i < auction.BID_RESPONSE.length; ++i) { - events.emit(EVENTS.BID_RESPONSE, auction.BID_RESPONSE[i]); - }; - - // Complete the auction - events.emit(EVENTS.AUCTION_END, auction.AUCTION_END); - - sandbox.clock.tick(this.defaultTimeout + 1); - - // Verify that we detected that the first bid was rejected - const expectedRejectedBid = JSON.parse(navigator.sendBeacon.firstCall.args[1]).auctions[0].adUnits[0].bids[0]; - assert.strictEqual(expectedRejectedBid.status, 'rejected'); - }); - }); - - context('when a transaction does not reach its complete state', function () { - context('and a timeout config value has been given', function () { - context('and the timeout value has elapsed', function () { - it('logs a warning', function () { - const timeout = 2000; - this.enableAnalytics({ timeout }); - - performStandardAuction({exclude: ['bidWon', 'slotRenderEnded', 'auctionEnd']}); - - sandbox.clock.tick(timeout + 1000); - - assert.calledWithExactly(log.warn, 'Timed out waiting for ad transactions to complete. Sending report.'); - }); - - it(`marks timed out bids as "timeout"`, function () { - const timeout = 2000; - this.enableAnalytics({ timeout }); - const request = getMockEvents().prebid[0].BID_REQUESTED[0]; - const bidToTimeout = request.bids[0]; - - performStandardAuction({exclude: ['bidWon', 'slotRenderEnded', 'auctionEnd']}); - sandbox.clock.tick(1); - events.emit(EVENTS.BID_TIMEOUT, [{ - auctionId: request.auctionId, - bidId: bidToTimeout.bidId, - transactionId: bidToTimeout.transactionId, - }]); - sandbox.clock.tick(timeout + 1000); - - const timeoutBid = JSON.parse(navigator.sendBeacon.firstCall.args[1]).auctions[0].adUnits[0].bids[0]; - assert.strictEqual(timeoutBid.status, 'timeout'); - }); - }); - }); - - context('and a timeout config value has not been given', function () { - context('and the default timeout has elapsed', function () { - it('logs an error', function () { - this.enableAnalytics(); - - performStandardAuction({exclude: ['bidWon', 'slotRenderEnded', 'auctionEnd']}); - - sandbox.clock.tick(this.defaultTimeout + 1000); - - assert.calledWithExactly(log.warn, 'Timed out waiting for ad transactions to complete. Sending report.'); - }); - }) - }); - - context('and the `slotRenderEnded` event fired for all bids, but not all bids have won', function () { - context('and the GAM slot IDs are configured as the ad unit codes', function () { - it('sends a report after the all `slotRenderEnded` events have fired and timed out', function () { - const timeout = POST_GAM_TIMEOUT + 2000; - this.enableAnalytics({ timeout }); - - performStandardAuction({exclude: ['bidWon', 'auctionEnd']}); - sandbox.clock.tick(POST_GAM_TIMEOUT + 1); - - assert.strictEqual(navigator.sendBeacon.callCount, 1); - }); - }); - - context('and the slot element IDs are configured as the ad unit codes', function () { - it('sends a report after the all `slotRenderEnded` events have fired and timed out', function () { - const timeout = POST_GAM_TIMEOUT + 2000; - this.enableAnalytics({ timeout }); - - performStandardAuction({exclude: ['bidWon', 'auctionEnd'], useSlotElementIds: true}); - sandbox.clock.tick(POST_GAM_TIMEOUT + 1); - - assert.strictEqual(navigator.sendBeacon.callCount, 1); - }); - }); - - it('does NOT send a report if not all `slotRenderEnded` events have timed out', function () { - const timeout = POST_GAM_TIMEOUT + 2000; - this.enableAnalytics({ timeout }); - - performStandardAuction({exclude: ['bidWon', 'auctionEnd']}); - sandbox.clock.tick(POST_GAM_TIMEOUT - 1); - - assert.strictEqual(navigator.sendBeacon.callCount, 0); - }); - }); - - context('and the `slotRenderEnded` event has fired for an unknown slot code', function () { - it('logs a warning message', function () { - this.enableAnalytics(); - - const { prebid: [auction], gam } = getMockEvents(); - auction.AUCTION_INIT.adUnits[0].code = 'INVALID_AD_UNIT_CODE'; - - const slotRenderEnded = gam.slotRenderEnded[0]; - events.emit(EVENTS.AUCTION_INIT, auction.AUCTION_INIT); - events.emit(EVENTS.BID_REQUESTED, auction.BID_REQUESTED[0]); - mockGpt.emitEvent('slotRenderEnded', slotRenderEnded); - - sandbox.clock.tick(POST_GAM_TIMEOUT + 1); - - assert.calledWithExactly(log.warn, - 'Could not find configured ad unit matching GAM render of slot:', - { slotName: `${adUnitCodes[0]} - ${adSlotElementIds[0]}` }); - }); - }); - - context('and the incomplete report has been sent successfully', function () { - it('sends a report string with any bids with rendered status set to hasWon: 1', function () { - navigator.sendBeacon.returns(true); - - this.enableAnalytics(); - - performStandardAuction({exclude: ['auctionEnd']}); - sandbox.clock.tick(this.defaultTimeout + 1000); - - const incompleteSentBid = JSON.parse(navigator.sendBeacon.firstCall.args[1]).auctions[0].adUnits[1].bids[0]; - assert.strictEqual(incompleteSentBid.hasWon, 1); - }); - - it('reports bids with only targetingSet status as hasWon: 0', function () { - navigator.sendBeacon.returns(true); - - this.enableAnalytics(); - - 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]; - assert.strictEqual(incompleteSentBid.hasWon, 0); - }); - - it('logs an info message', function () { - navigator.sendBeacon.returns(true); - - const endpoint = faker.internet.url(); - this.enableAnalytics({ endpoint }); - - performStandardAuction({exclude: ['bidWon', 'auctionEnd']}); - sandbox.clock.tick(this.defaultTimeout + 1000); - - assert.calledWith(log.info, `Analytics report sent to ${endpoint}`); - }); - }); - }); - - context('when the transaction manager has open transactions', function () { - it('reports those transactions as pending', function () { - this.enableAnalytics(); - - const { prebid: [auction] } = getMockEvents(); - events.emit(EVENTS.AUCTION_INIT, auction.AUCTION_INIT); - events.emit(EVENTS.BID_REQUESTED, auction.BID_REQUESTED[0]); - - const manager = locals.transactionManagers[auction.AUCTION_INIT.auctionId]; - assert.equal(manager.status().pending.length, auction.BID_REQUESTED[0].bids.length); - }); - - context('and a single bidWon event has triggered', function () { - it('completes the transaction', function () { - this.enableAnalytics(); - - const { prebid: [auction] } = getMockEvents(); - events.emit(EVENTS.AUCTION_INIT, auction.AUCTION_INIT); - events.emit(EVENTS.BID_REQUESTED, auction.BID_REQUESTED[0]); - events.emit(EVENTS.BID_WON, auction.BID_WON[0]); - - const manager = locals.transactionManagers[auction.AUCTION_INIT.auctionId]; - assert.deepEqual({ - completed: manager.status().completed.length, - pending: manager.status().pending.length - }, { - completed: 1, - pending: auction.BID_REQUESTED[0].bids.length - 1 - }); - }); - }); - - context('and a single slotRenderEnded event has triggered', function () { - context('and the Google Ad Manager timeout has not elapsed', function () { - it('does NOT complete the transaction', function () { - this.enableAnalytics(); - - const { prebid: [auction], gam } = getMockEvents(); - const slotRenderEnded = gam.slotRenderEnded[0]; - events.emit(EVENTS.AUCTION_INIT, auction.AUCTION_INIT); - events.emit(EVENTS.BID_REQUESTED, auction.BID_REQUESTED[0]); - mockGpt.emitEvent('slotRenderEnded', slotRenderEnded); - - const manager = locals.transactionManagers[auction.AUCTION_INIT.auctionId]; - assert.deepEqual({ - completed: manager.status().completed.length, - pending: manager.status().pending.length - }, { - completed: 0, - pending: auction.BID_REQUESTED[0].bids.length - }); - }); - }); - - context('and the Google Ad Manager timeout has elapsed', function () { - it('completes the transaction', function () { - const timeout = POST_GAM_TIMEOUT + 2000; - this.enableAnalytics({timeout}); - - const { prebid: [auction], gam } = getMockEvents(); - const slotRenderEnded = gam.slotRenderEnded[0]; - events.emit(EVENTS.AUCTION_INIT, auction.AUCTION_INIT); - events.emit(EVENTS.BID_REQUESTED, auction.BID_REQUESTED[0]); - mockGpt.emitEvent('slotRenderEnded', slotRenderEnded); - - sandbox.clock.tick(POST_GAM_TIMEOUT + 1); - const manager = locals.transactionManagers[auction.AUCTION_INIT.auctionId]; - assert.deepEqual({ - completed: manager.status().completed.length, - pending: manager.status().pending.length - }, { - completed: 1, - pending: auction.BID_REQUESTED[0].bids.length - 1 - }); - }); - }); - }); - }); - }); -}); - -const adUnitCodes = ['/19968336/header-bid-tag-0', '/19968336/header-bid-tag-1', '/17118521/header-bid-tag-2']; -const adSlotElementIds = ['ad-slot-div-0', 'ad-slot-div-1', 'ad-slot-div-2']; - -function performStandardAuction({ exclude = [], useSlotElementIds = false } = {}) { - const mockEvents = getMockEvents(); - const { prebid, gam } = mockEvents; - const [auction] = prebid; - - if (!exclude.includes(EVENTS.AUCTION_INIT)) { - if (useSlotElementIds) { - // With this option, identify the ad units by slot element IDs instead of GAM paths - auction.AUCTION_INIT.adUnits.forEach((adUnit, i) => { - adUnit.code = adSlotElementIds[i]; - }); - } - events.emit(EVENTS.AUCTION_INIT, auction.AUCTION_INIT); - } - - if (!exclude.includes(EVENTS.BID_REQUESTED)) { - for (let bidRequestedEvent of auction.BID_REQUESTED) { - events.emit(EVENTS.BID_REQUESTED, bidRequestedEvent); - }; - } - - if (!exclude.includes(EVENTS.BID_RESPONSE)) { - for (let bidResponseEvent of auction.BID_RESPONSE) { - events.emit(EVENTS.BID_RESPONSE, bidResponseEvent); - }; - } - - if (!exclude.includes(EVENTS.AUCTION_END)) { - events.emit(EVENTS.AUCTION_END, auction.AUCTION_END); - } - - if (!exclude.includes('slotRenderEnded')) { - for (let gEvent of gam.slotRenderEnded) { - mockGpt.emitEvent('slotRenderEnded', gEvent); - } - } - - if (!exclude.includes(EVENTS.BID_WON)) { - for (let bidWonEvent of auction.BID_WON) { - events.emit(EVENTS.BID_WON, bidWonEvent); - }; - } -} - -function mapToBids(auctions) { - return auctions.flatMap( - auction => auction.adUnits.flatMap( - au => au.bids - ) - ); -} - -function getLocalAssert() { - function isValidAnalyticsReport(report) { - assert.containsAllKeys(report, ['analyticsVersion', 'pid', 'src', 'pbjsVersion', 'auctions']); - if ('usPrivacy' in report) { - assert.match(report.usPrivacy, /[0|1][Y|N|-]{3}/); - } - if ('gdpr' in report) { - assert.oneOf(report.gdpr, [0, 1]); - } - if (report.gdpr === 1) { - assert.isString(report.gdprConsent); - } - if ('gpp' in report) { - assert.isString(report.gpp); - assert.isArray(report.gppSid); - } - if ('coppa' in report) { - assert.oneOf(report.coppa, [0, 1]); - } - - assert.equal(report.analyticsVersion, '1.0.0'); - assert.isString(report.pid); - assert.isString(report.src); - assert.equal(report.pbjsVersion, '$prebid.version$'); - assert.isArray(report.auctions); - assert.isAbove(report.auctions.length, 0); - report.auctions.forEach(isValidAuction); - } - function isValidAuction(auction) { - assert.hasAllKeys(auction, ['adUnits', 'auctionId', 'userIds']); - assert.isArray(auction.adUnits); - assert.isString(auction.auctionId); - assert.isArray(auction.userIds); - auction.adUnits.forEach(isValidAdUnit); - } - function isValidAdUnit(adUnit) { - assert.hasAllKeys(adUnit, ['transactionId', 'adUnitCode', 'slotId', 'mediaTypes', 'sizes', 'bids']); - assert.isString(adUnit.transactionId); - assert.isString(adUnit.adUnitCode); - assert.isString(adUnit.slotId); - assert.isArray(adUnit.mediaTypes); - assert.isArray(adUnit.sizes); - assert.isArray(adUnit.bids); - adUnit.mediaTypes.forEach(isValidMediaType); - adUnit.sizes.forEach(isValidSizeString); - adUnit.bids.forEach(isValidBid); - } - function isValidBid(bid) { - assert.containsAllKeys(bid, ['bidder', 'bidId', 'source', 'status', 'hasWon']); - if ('bidResponse' in bid) { - isValidBidResponse(bid.bidResponse); - } - assert.isString(bid.bidder); - assert.isString(bid.bidId); - assert.isString(bid.source); - assert.oneOf(bid.status, ['pending', 'timeout', 'targetingSet', 'rendered', 'success', 'rejected', 'no-bid', 'error']); - assert.oneOf(bid.hasWon, [0, 1]); - } - function isValidBidResponse(bidResponse) { - assert.containsAllKeys(bidResponse, ['mediaType', 'size', 'cur', 'cpm', 'cpmFloor']); - if ('cpmOrig' in bidResponse) { - assert.isNumber(bidResponse.cpmOrig); - } - isValidMediaType(bidResponse.mediaType); - isValidSizeString(bidResponse.size); - assert.isString(bidResponse.cur); - assert.isNumber(bidResponse.cpm); - assert.isNumber(bidResponse.cpmFloor); - } - function isValidMediaType(mediaType) { - assert.oneOf(mediaType, ['banner', 'video', 'native']); - } - function isValidSizeString(size) { - assert.match(size, /[0-9]+x[0-9]+/); - } - - function calledOnceWithStringJsonEquivalent(sinonSpy, ...args) { - sinon.assert.calledOnce(sinonSpy); - args.forEach((arg, i) => { - const stubCallArgs = sinonSpy.firstCall.args[i] - - if (typeof arg === 'object') { - assert.deepEqual(JSON.parse(stubCallArgs), arg); - } else { - assert.strictEqual(stubCallArgs, arg); - } - }); - } - - sinon.assert.expose(assert, { prefix: '' }); - return { - ...assert, - calledOnceWithStringJsonEquivalent, - isValidAnalyticsReport, - isValidAuction, - isValidAdUnit, - isValidBid, - isValidBidResponse, - isValidMediaType, - isValidSizeString, - } -}; - -function createReportWithThreeBidWonEvents() { - return { - pid: 'test-pid', - src: 'pbjs', - analyticsVersion: '1.0.0', - pbjsVersion: '$prebid.version$', - auctions: [{ - adUnits: [{ - transactionId: 'ef947609-7b55-4420-8407-599760d0e373', - adUnitCode: adUnitCodes[0], - slotId: adUnitCodes[0], - mediaTypes: ['banner'], - sizes: ['300x250', '300x600'], - bids: [{ - bidder: 'bidder0', - bidId: '20661fc5fbb5d9b', - source: 'client', - status: 'rendered', - bidResponse: { - cpm: 1.5, - cur: 'USD', - cpmOrig: 1.5, - cpmFloor: 1, - mediaType: 'banner', - size: '300x250' - }, - hasWon: 1 - }] - }, { - transactionId: 'abab4423-d962-41aa-adc7-0681f686c330', - adUnitCode: adUnitCodes[1], - slotId: adUnitCodes[1], - mediaTypes: ['banner'], - sizes: ['728x90', '970x250'], - bids: [{ - bidder: 'bidder0', - bidId: '21ad295f40dd7ab', - source: 'client', - status: 'rendered', - bidResponse: { - cpm: 1.5, - cur: 'USD', - cpmOrig: 1.5, - cpmFloor: 1, - mediaType: 'banner', - size: '728x90' - }, - hasWon: 1 - }] - }, { - transactionId: 'b43e7487-0a52-4689-a0f7-d139d08b1f9f', - adUnitCode: adUnitCodes[2], - slotId: adUnitCodes[2], - mediaTypes: ['banner'], - sizes: ['300x250'], - bids: [{ - bidder: 'bidder0', - bidId: '22108ac7b778717', - source: 'client', - status: 'rendered', - bidResponse: { - cpm: 1.5, - cur: 'USD', - cpmOrig: 1.5, - cpmFloor: 1, - mediaType: 'banner', - size: '728x90' - }, - hasWon: 1 - }] - }], - auctionId: 'auction-000', - userIds: ['33acrossId'] - }], - }; -} - -function getMockEvents() { - const auctionId = 'auction-000'; - const userId = { - '33acrossId': { - envelope: 'v1.0014', - }, - }; - - return { - gam: { - slotRenderEnded: [ - { - serviceName: 'publisher_ads', - slot: mockGpt.makeSlot({ code: adUnitCodes[0], divId: adSlotElementIds[0] }), - isEmpty: true, - slotContentChanged: true, - size: null, - advertiserId: null, - campaignId: null, - creativeId: null, - creativeTemplateId: null, - labelIds: null, - lineItemId: null, - isBackfill: false, - }, - { - serviceName: 'publisher_ads', - slot: mockGpt.makeSlot({ code: adUnitCodes[1], divId: adSlotElementIds[1] }), - isEmpty: false, - slotContentChanged: true, - size: [1, 1], - advertiserId: 12345, - campaignId: 400000001, - creativeId: 6789, - creativeTemplateId: null, - labelIds: null, - lineItemId: 1011, - isBackfill: false, - yieldGroupIds: null, - companyIds: null, - }, - { - serviceName: 'publisher_ads', - slot: mockGpt.makeSlot({ code: adUnitCodes[2], divId: adSlotElementIds[2] }), - isEmpty: false, - slotContentChanged: true, - size: [728, 90], - advertiserId: 12346, - campaignId: 299999000, - creativeId: 6790, - creativeTemplateId: null, - labelIds: null, - lineItemId: 1012, - isBackfill: false, - yieldGroupIds: null, - companyIds: null, - }, - ], - }, - prebid: [{ - AUCTION_INIT: { - auctionId, - adUnits: [ - { - code: adUnitCodes[0], - mediaTypes: { - banner: { - sizes: [ - [300, 250], - [300, 600], - ], - }, - }, - bids: [ - { - bidder: 'bidder0', - userId, - }, - ], - sizes: [ - [300, 250], - [300, 600], - ], - transactionId: 'ef947609-7b55-4420-8407-599760d0e373', - ortb2Imp: { - ext: { - gpid: adUnitCodes[0], - }, - }, - }, - { - code: adUnitCodes[1], - mediaTypes: { - banner: { - sizes: [ - [728, 90], - [970, 250], - ], - }, - }, - bids: [ - { - bidder: 'bidder0', - userId, - }, - ], - sizes: [ - [728, 90], - [970, 250], - ], - transactionId: 'abab4423-d962-41aa-adc7-0681f686c330', - ortb2Imp: { - ext: { - gpid: adUnitCodes[1], - }, - }, - }, - { - code: adUnitCodes[2], - mediaTypes: { - banner: { - sizes: [[300, 250]], - }, - }, - bids: [ - { - bidder: '33across', - userId, - }, - { - bidder: 'bidder0', - userId, - }, - ], - sizes: [[300, 250]], - transactionId: 'b43e7487-0a52-4689-a0f7-d139d08b1f9f', - ortb2Imp: { - ext: { - gpid: adUnitCodes[2], - }, - }, - }, - ], - bidderRequests: [ - { - bids: [ - { userId }, - ], - } - ], - }, - BID_REQUESTED: [ - { - auctionId, - bids: [ - { - bidder: 'bidder0', - transactionId: 'ef947609-7b55-4420-8407-599760d0e373', - bidId: '20661fc5fbb5d9b', - src: 'client', - }, - { - bidder: 'bidder0', - transactionId: 'abab4423-d962-41aa-adc7-0681f686c330', - bidId: '21ad295f40dd7ab', - src: 'client', - }, - { - bidder: 'bidder0', - transactionId: 'b43e7487-0a52-4689-a0f7-d139d08b1f9f', - bidId: '22108ac7b778717', - src: 'client', - }, - ], - }], - BID_RESPONSE: [{ - auctionId, - cpm: 1.5, - currency: 'USD', - floorData: { - floorValue: 1 - }, - mediaType: 'banner', - originalCpm: 1.5, - requestId: '20661fc5fbb5d9b', - size: '300x250', - source: 'client', - status: 'targetingSet' - }, - { - auctionId, - cpm: 1.5, - currency: 'USD', - floorData: { - floorValue: 1 - }, - mediaType: 'banner', - originalCpm: 1.5, - requestId: '21ad295f40dd7ab', - size: '728x90', - source: 'client', - status: 'targetingSet', - }, - { - auctionId, - cpm: 1.5, - currency: 'USD', - floorData: { - floorValue: 1 - }, - mediaType: 'banner', - originalCpm: 1.5, - requestId: '22108ac7b778717', - size: '728x90', - source: 'client', - status: 'targetingSet', - }], - BID_WON: [{ - auctionId, - cpm: 1.5, - currency: 'USD', - floorData: { - floorValue: 1 - }, - mediaType: 'banner', - originalCpm: 1.5, - requestId: '20661fc5fbb5d9b', - size: '300x250', - source: 'client', - status: 'rendered', - transactionId: 'ef947609-7b55-4420-8407-599760d0e373', - }, - { - auctionId, - cpm: 1.5, - currency: 'USD', - floorData: { - floorValue: 1 - }, - mediaType: 'banner', - originalCpm: 1.5, - requestId: '21ad295f40dd7ab', - size: '728x90', - source: 'client', - status: 'rendered', - transactionId: 'abab4423-d962-41aa-adc7-0681f686c330', - }, - { - auctionId, - cpm: 1.5, - currency: 'USD', - floorData: { - floorValue: 1 - }, - mediaType: 'banner', - originalCpm: 1.5, - requestId: '22108ac7b778717', - size: '728x90', - source: 'client', - status: 'rendered', - transactionId: 'b43e7487-0a52-4689-a0f7-d139d08b1f9f', - }], - BID_REJECTED: [{ - auctionId, - cpm: 1.5, - currency: 'USD', - floorData: { - floorValue: 2 - }, - mediaType: 'banner', - originalCpm: 1.5, - requestId: '20661fc5fbb5d9b', - width: 300, - height: 250, - source: 'client', - transactionId: 'ef947609-7b55-4420-8407-599760d0e373', - statusMessage: 'Bid available', - rejectionReason: 'Bid does not meet price floor', - }], - AUCTION_END: { - auctionId, - }, - }], - }; -} diff --git a/test/spec/modules/33acrossBidAdapter_spec.js b/test/spec/modules/33acrossBidAdapter_spec.js index 9cc038428bc..aa2621f1fa0 100644 --- a/test/spec/modules/33acrossBidAdapter_spec.js +++ b/test/spec/modules/33acrossBidAdapter_spec.js @@ -200,29 +200,6 @@ describe('33acrossBidAdapter:', function () { return this; }; - this.withCoppa = coppaValue => { - Object.assign(ttxRequest.regs, { - coppa: coppaValue - }); - - return this; - }; - - this.withGppConsent = (consentString, applicableSections) => { - Object.assign(ttxRequest, { - regs: { - gpp: consentString, - gpp_sid: applicableSections, - ext: Object.assign( - {}, - ttxRequest.regs.ext - ) - } - }); - - return this; - }; - this.withSite = site => { Object.assign(ttxRequest, { site }); return this; @@ -1072,11 +1049,11 @@ describe('33acrossBidAdapter:', function () { }); it('returns corresponding test server requests with gdpr consent data', function() { - sandbox.stub(config, 'getConfig') - .withArgs('ttxSettings') - .returns({ + sandbox.stub(config, 'getConfig').callsFake(() => { + return { 'url': 'https://foo.com/hb/' - }); + } + }); const ttxRequest = new TtxRequestBuilder() .withBanner() @@ -1108,11 +1085,11 @@ describe('33acrossBidAdapter:', function () { }); it('returns corresponding test server requests with default gdpr consent data', function() { - sandbox.stub(config, 'getConfig') - .withArgs('ttxSettings') - .returns({ + sandbox.stub(config, 'getConfig').callsFake(() => { + return { 'url': 'https://foo.com/hb/' - }); + } + }); const ttxRequest = new TtxRequestBuilder() .withBanner() @@ -1151,11 +1128,11 @@ describe('33acrossBidAdapter:', function () { }); it('returns corresponding test server requests with us_privacy consent data', function() { - sandbox.stub(config, 'getConfig') - .withArgs('ttxSettings') - .returns({ + sandbox.stub(config, 'getConfig').callsFake(() => { + return { 'url': 'https://foo.com/hb/' - }); + } + }); const ttxRequest = new TtxRequestBuilder() .withBanner() @@ -1187,102 +1164,15 @@ describe('33acrossBidAdapter:', function () { }); it('returns corresponding test server requests with default us_privacy consent data', function() { - sandbox.stub(config, 'getConfig') - .withArgs('ttxSettings') - .returns({ + sandbox.stub(config, 'getConfig').callsFake(() => { + return { 'url': 'https://foo.com/hb/' - }); - - const ttxRequest = new TtxRequestBuilder() - .withBanner() - .withProduct() - .build(); - const serverRequest = new ServerRequestBuilder() - .withData(ttxRequest) - .withUrl('https://foo.com/hb/') - .build(); - const [ builtServerRequest ] = spec.buildRequests(bidRequests, bidderRequest); - - validateBuiltServerRequest(builtServerRequest, serverRequest); - }); - }); - - context('when coppa has been enabled', function() { - beforeEach(function() { - sandbox.stub(config, 'getConfig').withArgs('coppa').returns(true); - }); - - it('returns corresponding server requests with coppa: 1', function() { - const ttxRequest = new TtxRequestBuilder() - .withBanner() - .withProduct() - .withCoppa(1) - .build(); - - const serverRequest = new ServerRequestBuilder() - .withData(ttxRequest) - .build(); - const [ builtServerRequest ] = spec.buildRequests(bidRequests, bidderRequest); - - validateBuiltServerRequest(builtServerRequest, serverRequest); - }); - }); - - context('when coppa has been disabled', function() { - beforeEach(function() { - sandbox.stub(config, 'getConfig').withArgs('coppa').returns(false); - }); - - it('returns corresponding server requests with coppa: 0', function() { - const ttxRequest = new TtxRequestBuilder() - .withBanner() - .withProduct() - .withCoppa(0) - .build(); - const serverRequest = new ServerRequestBuilder() - .withData(ttxRequest) - .build(); - const [ builtServerRequest ] = spec.buildRequests(bidRequests, bidderRequest); - - validateBuiltServerRequest(builtServerRequest, serverRequest); - }); - }); - - context('when GPP consent data exists', function() { - beforeEach(function() { - bidderRequest = { - ...bidderRequest, - gppConsent: { - gppString: 'foo', - applicableSections: '123' } - } - }); - - it('returns corresponding server requests with GPP consent data', function() { - const ttxRequest = new TtxRequestBuilder() - .withBanner() - .withProduct() - .withGppConsent('foo', '123') - .build(); - const serverRequest = new ServerRequestBuilder() - .withData(ttxRequest) - .build(); - const [ builtServerRequest ] = spec.buildRequests(bidRequests, bidderRequest); - - validateBuiltServerRequest(builtServerRequest, serverRequest); - }); - - it('returns corresponding test server requests with GPP consent data', function() { - sandbox.stub(config, 'getConfig').withArgs('ttxSettings') - .returns({ - 'url': 'https://foo.com/hb/' - }); + }); const ttxRequest = new TtxRequestBuilder() .withBanner() .withProduct() - .withGppConsent('foo', '123') .build(); const serverRequest = new ServerRequestBuilder() .withData(ttxRequest) @@ -1857,11 +1747,11 @@ describe('33acrossBidAdapter:', function () { context('when SRA mode is enabled', function() { it('builds a single request with multiple imps corresponding to each group {siteId, productId}', function() { - sandbox.stub(config, 'getConfig') - .withArgs('ttxSettings') - .returns({ + sandbox.stub(config, 'getConfig').callsFake(() => { + return { enableSRAMode: true - }); + } + }); const bidRequests = new BidRequestsBuilder() .addBid() @@ -2329,11 +2219,11 @@ describe('33acrossBidAdapter:', function () { const expectedSyncs = [ { type: 'iframe', - url: `${syncs[0].url}&gdpr_consent=undefined&us_privacy=undefined&gpp=&gpp_sid=` + url: `${syncs[0].url}&gdpr_consent=undefined&us_privacy=undefined` }, { type: 'iframe', - url: `${syncs[1].url}&gdpr_consent=undefined&us_privacy=undefined&gpp=&gpp_sid=` + url: `${syncs[1].url}&gdpr_consent=undefined&us_privacy=undefined` } ] @@ -2349,11 +2239,11 @@ describe('33acrossBidAdapter:', function () { const expectedSyncs = [ { type: 'iframe', - url: `${syncs[0].url}&gdpr_consent=undefined&us_privacy=undefined&gpp=&gpp_sid=&gdpr=1` + url: `${syncs[0].url}&gdpr_consent=undefined&us_privacy=undefined&gdpr=1` }, { type: 'iframe', - url: `${syncs[1].url}&gdpr_consent=undefined&us_privacy=undefined&gpp=&gpp_sid=&gdpr=1` + url: `${syncs[1].url}&gdpr_consent=undefined&us_privacy=undefined&gdpr=1` } ]; @@ -2369,11 +2259,11 @@ describe('33acrossBidAdapter:', function () { const expectedSyncs = [ { type: 'iframe', - url: `${syncs[0].url}&gdpr_consent=consent123A&us_privacy=undefined&gpp=&gpp_sid=&gdpr=1` + url: `${syncs[0].url}&gdpr_consent=consent123A&us_privacy=undefined&gdpr=1` }, { type: 'iframe', - url: `${syncs[1].url}&gdpr_consent=consent123A&us_privacy=undefined&gpp=&gpp_sid=&gdpr=1` + url: `${syncs[1].url}&gdpr_consent=consent123A&us_privacy=undefined&gdpr=1` } ]; @@ -2389,11 +2279,11 @@ describe('33acrossBidAdapter:', function () { const expectedSyncs = [ { type: 'iframe', - url: `${syncs[0].url}&gdpr_consent=undefined&us_privacy=undefined&gpp=&gpp_sid=&gdpr=0` + url: `${syncs[0].url}&gdpr_consent=undefined&us_privacy=undefined&gdpr=0` }, { type: 'iframe', - url: `${syncs[1].url}&gdpr_consent=undefined&us_privacy=undefined&gpp=&gpp_sid=&gdpr=0` + url: `${syncs[1].url}&gdpr_consent=undefined&us_privacy=undefined&gdpr=0` } ]; expect(syncResults).to.deep.equal(expectedSyncs); @@ -2408,11 +2298,11 @@ describe('33acrossBidAdapter:', function () { const expectedSyncs = [ { type: 'iframe', - url: `${syncs[0].url}&gdpr_consent=consent123A&us_privacy=undefined&gpp=&gpp_sid=` + url: `${syncs[0].url}&gdpr_consent=consent123A&us_privacy=undefined` }, { type: 'iframe', - url: `${syncs[1].url}&gdpr_consent=consent123A&us_privacy=undefined&gpp=&gpp_sid=` + url: `${syncs[1].url}&gdpr_consent=consent123A&us_privacy=undefined` } ]; expect(syncResults).to.deep.equal(expectedSyncs); @@ -2427,11 +2317,11 @@ describe('33acrossBidAdapter:', function () { const expectedSyncs = [ { type: 'iframe', - url: `${syncs[0].url}&gdpr_consent=consent123A&us_privacy=undefined&gpp=&gpp_sid=&gdpr=0` + url: `${syncs[0].url}&gdpr_consent=consent123A&us_privacy=undefined&gdpr=0` }, { type: 'iframe', - url: `${syncs[1].url}&gdpr_consent=consent123A&us_privacy=undefined&gpp=&gpp_sid=&gdpr=0` + url: `${syncs[1].url}&gdpr_consent=consent123A&us_privacy=undefined&gdpr=0` } ]; expect(syncResults).to.deep.equal(expectedSyncs); @@ -2446,11 +2336,11 @@ describe('33acrossBidAdapter:', function () { const expectedSyncs = [ { type: 'iframe', - url: `${syncs[0].url}&gdpr_consent=undefined&us_privacy=undefined&gpp=&gpp_sid=` + url: `${syncs[0].url}&gdpr_consent=undefined&us_privacy=undefined` }, { type: 'iframe', - url: `${syncs[1].url}&gdpr_consent=undefined&us_privacy=undefined&gpp=&gpp_sid=` + url: `${syncs[1].url}&gdpr_consent=undefined&us_privacy=undefined` } ] @@ -2466,54 +2356,11 @@ describe('33acrossBidAdapter:', function () { const expectedSyncs = [ { type: 'iframe', - url: `${syncs[0].url}&gdpr_consent=undefined&us_privacy=foo&gpp=&gpp_sid=` - }, - { - type: 'iframe', - url: `${syncs[1].url}&gdpr_consent=undefined&us_privacy=foo&gpp=&gpp_sid=` - } - ]; - - expect(syncResults).to.deep.equal(expectedSyncs); - }); - }); - - context('when there is no GPP data', function() { - it('returns sync urls with empty GPP params', function() { - spec.buildRequests(bidRequests); - - const syncResults = spec.getUserSyncs(syncOptions, {}); - const expectedSyncs = [ - { - type: 'iframe', - url: `${syncs[0].url}&gdpr_consent=undefined&us_privacy=undefined&gpp=&gpp_sid=` - }, - { - type: 'iframe', - url: `${syncs[1].url}&gdpr_consent=undefined&us_privacy=undefined&gpp=&gpp_sid=` - } - ] - - expect(syncResults).to.deep.equal(expectedSyncs); - }) - }); - - context('when there is GPP data', function() { - it('returns sync urls with GPP consent string & GPP Section ID as params', function() { - spec.buildRequests(bidRequests); - - const syncResults = spec.getUserSyncs(syncOptions, {}, {}, undefined, { - gppString: 'foo', - applicableSections: ['123', '456'] - }); - const expectedSyncs = [ - { - type: 'iframe', - url: `${syncs[0].url}&gdpr_consent=undefined&us_privacy=undefined&gpp=foo&gpp_sid=123%2C456` + url: `${syncs[0].url}&gdpr_consent=undefined&us_privacy=foo` }, { type: 'iframe', - url: `${syncs[1].url}&gdpr_consent=undefined&us_privacy=undefined&gpp=foo&gpp_sid=123%2C456` + url: `${syncs[1].url}&gdpr_consent=undefined&us_privacy=foo` } ]; diff --git a/test/spec/modules/33acrossIdSystem_spec.js b/test/spec/modules/33acrossIdSystem_spec.js index 4f6d7c4a6c5..5070d2b8845 100644 --- a/test/spec/modules/33acrossIdSystem_spec.js +++ b/test/spec/modules/33acrossIdSystem_spec.js @@ -2,7 +2,7 @@ import { thirthyThreeAcrossIdSubmodule } from 'modules/33acrossIdSystem.js'; import * as utils from 'src/utils.js'; import { server } from 'test/mocks/xhr.js'; -import { uspDataHandler, coppaDataHandler, gppDataHandler } from 'src/adapterManager.js'; +import { uspDataHandler } from 'src/adapterManager.js'; describe('33acrossIdSystem', () => { describe('name', () => { @@ -67,35 +67,41 @@ describe('33acrossIdSystem', () => { expect(request.url).to.contain('gdpr=1'); }); - }); - context('when GDPR doesn\'t apply', () => { - it('should call endpoint with \'gdpr=0\'', () => { - const completeCallback = () => {}; - const { callback } = thirthyThreeAcrossIdSubmodule.getId({ - params: { - pid: '12345' - } - }, { - gdprApplies: false + context('and the consent string is given', () => { + it('should call endpoint with the GDPR consent string', () => { + [ + { consentString: '', expected: '' }, + { consentString: undefined, expected: '' }, + { consentString: 'foo', expected: 'foo' } + ].forEach(({ consentString, expected }, index) => { + const completeCallback = () => {}; + const { callback } = thirthyThreeAcrossIdSubmodule.getId({ + params: { + pid: '12345' + } + }, { + gdprApplies: true, + consentString + }); + + callback(completeCallback); + + expect(server.requests[index].url).to.contain(`gdpr_consent=${expected}`); + }); }); - - callback(completeCallback); - - const [request] = server.requests; - - expect(request.url).to.contain('gdpr=0'); }); }); - context('when the GDPR consent string is given', () => { - it('should call endpoint with the GDPR consent string', () => { + context('when GDPR doesn\'t apply', () => { + it('should call endpoint with \'gdpr=0\' and no GDPR consent string parameter', () => { const completeCallback = () => {}; const { callback } = thirthyThreeAcrossIdSubmodule.getId({ params: { pid: '12345' } }, { + gdprApplies: false, consentString: 'foo' }); @@ -103,7 +109,8 @@ describe('33acrossIdSystem', () => { const [request] = server.requests; - expect(request.url).to.contain('gdpr_consent=foo'); + expect(request.url).to.contain('gdpr=0'); + expect(request.url).not.to.contain('gdpr_consent'); }); }); @@ -150,108 +157,6 @@ describe('33acrossIdSystem', () => { }); }); - context('when coppa is enabled', () => { - it('should call endpoint with an enabled coppa signal', () => { - const completeCallback = () => {}; - const { callback } = thirthyThreeAcrossIdSubmodule.getId({ - params: { - pid: '12345' - } - }); - - sinon.stub(coppaDataHandler, 'getCoppa').returns(true); - - callback(completeCallback); - - const [request] = server.requests; - - expect(request.url).to.contain('coppa=1'); - - coppaDataHandler.getCoppa.restore(); - }); - }); - - context('when coppa is not enabled', () => { - it('should call endpoint with coppa signal not enabled', () => { - const completeCallback = () => {}; - const { callback } = thirthyThreeAcrossIdSubmodule.getId({ - params: { - pid: '12345' - } - }); - - sinon.stub(coppaDataHandler, 'getCoppa').returns(false); - - callback(completeCallback); - - const [request] = server.requests; - - expect(request.url).to.contain('coppa=0'); - - coppaDataHandler.getCoppa.restore(); - }); - }); - - context('when a GPP consent string is given', () => { - beforeEach(() => { - sinon.stub(gppDataHandler, 'getConsentData'); - }); - - afterEach(() => { - gppDataHandler.getConsentData.restore(); - }); - - it('should call endpoint with the GPP consent string', () => { - [ - { gppString: '', expected: '' }, - { gppString: undefined, expected: '' }, - { gppString: 'foo', expected: 'foo' }, - ].forEach(({ gppString, expected }, index) => { - const completeCallback = () => {}; - const { callback } = thirthyThreeAcrossIdSubmodule.getId({ - params: { - pid: '12345' - } - }); - - gppDataHandler.getConsentData.onCall(index).returns({ - gppString - }); - - callback(completeCallback); - - expect(server.requests[index].url).to.contain(`gpp=${expected}`); - }); - }); - - it('should call endpoint with the GPP applicable sections', () => { - const gppString = 'foo'; - - [ - { applicableSections: [], expected: '' }, - { applicableSections: undefined, expected: '' }, - { applicableSections: ['1'], expected: '1' }, - { applicableSections: ['1', '2'], expected: '1%2C2' }, - ].forEach(({ applicableSections, expected }, index) => { - const completeCallback = () => {}; - const { callback } = thirthyThreeAcrossIdSubmodule.getId({ - params: { - pid: '12345' - } - }); - - gppDataHandler.getConsentData.onCall(index).returns({ - gppString: 'foo', - applicableSections - }); - - callback(completeCallback); - - expect(server.requests[index].url).to.contain(`gpp_sid=${expected}`); - }); - }); - }); - context('when the partner ID is not given', () => { it('should log an error', () => { const logErrorSpy = sinon.spy(utils, 'logError'); diff --git a/test/spec/modules/a1MediaBidAdapter_spec.js b/test/spec/modules/a1MediaBidAdapter_spec.js deleted file mode 100644 index e1db2b9ad8d..00000000000 --- a/test/spec/modules/a1MediaBidAdapter_spec.js +++ /dev/null @@ -1,248 +0,0 @@ -import { spec } from 'modules/a1MediaBidAdapter.js'; -import { config } from 'src/config.js'; -import { BANNER, VIDEO, NATIVE } from 'src/mediaTypes.js'; -import 'modules/currency.js'; -import 'modules/priceFloors.js'; -import { replaceAuctionPrice } from '../../../src/utils'; - -const ortbBlockParams = { - battr: [ 13 ], - bcat: ['IAB1-1'] -}; -const getBidderRequest = (isMulti = false) => { - return { - bidderCode: 'a1media', - auctionId: 'ba87bfdf-493e-4a88-8e26-17b4cbc9adbd', - bidderRequestId: '104e8d2392bd6f', - bids: [ - { - bidder: 'a1media', - params: {}, - auctionId: 'ba87bfdf-493e-4a88-8e26-17b4cbc9adbd', - mediaTypes: { - banner: { - sizes: [ - [ 320, 100 ], - ] - }, - ...(isMulti && { - video: { - mimes: ['video/mp4'] - }, - native: { - title: { - required: true, - }} - }) - }, - ...(isMulti && { - nativeOrtbRequest: { - ver: '1.2', - assets: [ - { - id: 0, - required: 1, - title: { - len: 140 - } - } - ] - } - }), - adUnitCode: 'test-div', - transactionId: 'cab00498-028b-4061-8f9d-a8d66c8cb91d', - bidId: '2e9f38ea93bb9e', - bidderRequestId: '104e8d2392bd6f', - } - ], - } -}; -const getConvertedBidReq = () => { - return { - cur: [ - 'JPY' - ], - imp: [ - { - banner: { - format: [ - { - h: 100, - w: 320 - }, - ], - topframe: 0 - }, - bidfloor: 0, - bidfloorcur: 'JPY', - id: '2e9f38ea93bb9e' - } - ], - test: 0, - } -}; - -const getBidderResponse = () => { - return { - body: { - id: 'bid-response', - cur: 'JPY', - seatbid: [ - { - bid: [{ - impid: '2e9f38ea93bb9e', - crid: 'creative-id', - cur: 'JPY', - price: 9, - }] - } - ] - } - } -} -const bannerAdm = '
'; -const videoAdm = 'testvast1'; -const nativeAdm = '{"ver":"1.2","link":{"url":"test_url"},"assets":[{"id":1,"required":1,"title":{"text":"native_title"}}]}'; -const macroAdm = '
'; -const macroNurl = 'https://d11.contentsfeed.com/dsp/win/example.com/SITE/a1/${AUCTION_PRICE}'; -const interpretedNurl = `
`; - -describe('a1MediaBidAdapter', function() { - describe('isValidRequest', function() { - const bid = { - bidder: 'a1media', - }; - - it('should return true always', function () { - expect(spec.isBidRequestValid(bid)).to.equal(true); - }); - }); - - describe('buildRequests', function() { - let bidderRequest, convertedRequest; - beforeEach(function() { - bidderRequest = getBidderRequest(); - convertedRequest = getConvertedBidReq(); - }); - - it('should return expected request object', function() { - const bidRequest = spec.buildRequests(bidderRequest.bids, bidderRequest); - convertedRequest.id = bidRequest.data.id; - - expect(bidRequest.method).equal('POST'); - expect(bidRequest.url).equal('https://d11.contentsfeed.com/dsp/breq/a1'); - expect(bidRequest.data).deep.equal(convertedRequest); - }); - it('should set ortb blocking using params', function() { - bidderRequest.bids[0].params = ortbBlockParams; - - const bidRequest = spec.buildRequests(bidderRequest.bids, bidderRequest); - convertedRequest.id = bidRequest.data.id; - convertedRequest.bcat = ortbBlockParams.bcat; - convertedRequest.imp[0].banner.battr = ortbBlockParams.battr; - - expect(bidRequest.data).deep.equal(convertedRequest); - }); - - it('should set bidfloor when getFloor is available', function() { - bidderRequest.bids[0].getFloor = () => ({ currency: 'USD', floor: 999 }); - const bidRequest = spec.buildRequests(bidderRequest.bids, bidderRequest); - - expect(bidRequest.data.imp[0].bidfloor).equal(999); - expect(bidRequest.data.imp[0].bidfloorcur).equal('USD'); - }); - - it('should set cur when currency config is configured', function() { - config.setConfig({ - currency: { - adServerCurrency: 'USD', - } - }); - const bidRequest = spec.buildRequests(bidderRequest.bids, bidderRequest); - - expect(bidRequest.data.cur[0]).equal('USD'); - }); - - it('should set bidfloor and currency using params when modules not available', function() { - bidderRequest.bids[0].params.currency = 'USD'; - bidderRequest.bids[0].params.bidfloor = 0.99; - - const bidRequest = spec.buildRequests(bidderRequest.bids, bidderRequest); - convertedRequest.id = bidRequest.data.id; - convertedRequest.imp[0].bidfloor = 0.99; - convertedRequest.imp[0].bidfloorcur = 'USD'; - convertedRequest.cur[0] = 'USD'; - - expect(bidRequest.data).deep.equal(convertedRequest); - }); - }); - - describe('interpretResponse', function() { - describe('when request mediaType is single', function() { - let bidRequest, bidderResponse; - beforeEach(function() { - const bidderRequest = getBidderRequest(); - bidRequest = spec.buildRequests(bidderRequest.bids, bidderRequest); - bidderResponse = getBidderResponse(); - }); - it('should set cpm using price attribute', function() { - const bidResPrice = 9; - bidderResponse.body.seatbid[0].bid[0].price = bidResPrice; - const interpretedRes = spec.interpretResponse(bidderResponse, bidRequest); - expect(interpretedRes[0].cpm).equal(bidResPrice); - }); - it('should set mediaType using request mediaTypes', function() { - const interpretedRes = spec.interpretResponse(bidderResponse, bidRequest); - expect(interpretedRes[0].mediaType).equal(BANNER); - }); - }); - - describe('when request mediaType is multi', function() { - let bidRequest, bidderResponse; - beforeEach(function() { - const bidderRequest = getBidderRequest(true); - bidRequest = spec.buildRequests(bidderRequest.bids, bidderRequest); - bidderResponse = getBidderResponse(); - }); - it('should set mediaType to video', function() { - bidderResponse.body.seatbid[0].bid[0].adm = videoAdm; - const interpretedRes = spec.interpretResponse(bidderResponse, bidRequest); - expect(interpretedRes[0].mediaType).equal(VIDEO); - }); - it('should set mediaType to native', function() { - bidderResponse.body.seatbid[0].bid[0].adm = nativeAdm; - const interpretedRes = spec.interpretResponse(bidderResponse, bidRequest); - expect(interpretedRes[0].mediaType).equal(NATIVE); - }); - it('should set mediaType to banner when adm is neither native or video', function() { - bidderResponse.body.seatbid[0].bid[0].adm = bannerAdm; - const interpretedRes = spec.interpretResponse(bidderResponse, bidRequest); - expect(interpretedRes[0].mediaType).equal(BANNER); - }); - }); - - describe('resolve the AUCTION_PRICE macro', function() { - let bidRequest; - beforeEach(function() { - const bidderRequest = getBidderRequest(true); - bidRequest = spec.buildRequests(bidderRequest.bids, bidderRequest); - }); - it('should return empty array when bid response has not contents', function() { - const emptyResponse = { body: '' }; - const interpretedRes = spec.interpretResponse(emptyResponse, bidRequest); - expect(interpretedRes.length).equal(0); - }); - it('should replace macro keyword if is exist', function() { - const bidderResponse = getBidderResponse(); - bidderResponse.body.seatbid[0].bid[0].adm = macroAdm; - bidderResponse.body.seatbid[0].bid[0].nurl = macroNurl; - const interpretedRes = spec.interpretResponse(bidderResponse, bidRequest); - - const expectedResPrice = 9; - const expectedAd = replaceAuctionPrice(macroAdm, expectedResPrice) + replaceAuctionPrice(interpretedNurl, expectedResPrice); - - expect(interpretedRes[0].ad).equal(expectedAd); - }); - }); - }); -}) diff --git a/test/spec/modules/a1MediaRtdProvider_spec.js b/test/spec/modules/a1MediaRtdProvider_spec.js deleted file mode 100644 index 2630e83fcf5..00000000000 --- a/test/spec/modules/a1MediaRtdProvider_spec.js +++ /dev/null @@ -1,105 +0,0 @@ -import { subModuleObj } from 'modules/a1MediaRtdProvider.js'; -import { loadExternalScript } from '../../../src/adloader.js'; -import { A1_AUD_KEY, A1_SEG_KEY, getStorageData, storage } from '../../../modules/a1MediaRtdProvider.js'; -import { expect } from 'chai'; - -const configWithParams = { - name: 'a1Media', - waitForIt: true, - params: { - tagId: 'lb4test.min.js', - }, -}; -const configWithoutParams = { - name: 'a1Media', - waitForIt: true, - params: { - }, -}; - -const reqBidsConfigObj = { - ortb2Fragments: { - global: {} - } -}; -const a1TestOrtbObj = { - user: { - data: [ - { - name: 'a1mediagroup.com', - ext: { - segtax: 900 - }, - segment: [{id: 'test'}] - } - ], - ext: { - eids: [ - { - source: 'a1mediagroup.com', - uids: [ - { - id: 'tester', - atype: 1 - } - ] - } - ] - } - } -}; - -describe('a1MediaRtdProvider', function() { - describe('init', function() { - describe('initialize with expected params', function() { - it('successfully initialize with load script', function() { - expect(subModuleObj.init(configWithParams)).to.be.true; - expect(window.linkback.l).to.be.true; - expect(loadExternalScript.called).to.be.true; - expect(loadExternalScript.args[0][0]).to.deep.equal('https://linkback.contentsfeed.com/src/lb4test.min.js'); - }) - - it('successfully initialize but script is already exist', function() { - const linkback = { l: true }; - - expect(subModuleObj.init(configWithParams)).to.be.true; - expect(loadExternalScript.called).to.be.false; - }) - }); - - describe('initialize without expected params', function() { - afterEach(function() { - storage.setCookie(A1_SEG_KEY, '', 0); - }) - - it('successfully initialize when publisher side segment is exist in cookie', function() { - storage.setCookie(A1_SEG_KEY, 'test'); - expect(subModuleObj.init(configWithoutParams)).to.be.true; - expect(getStorageData(A1_SEG_KEY)).to.not.equal(''); - }) - it('fails initalize publisher sied segment is not exist', function() { - expect(subModuleObj.init(configWithoutParams)).to.be.false; - expect(getStorageData(A1_SEG_KEY)).to.equal(''); - }) - }) - }); - - describe('alterBidRequests', function() { - const callback = sinon.stub(); - - before(function() { - storage.setCookie(A1_SEG_KEY, 'test'); - storage.setDataInLocalStorage(A1_AUD_KEY, 'tester'); - }) - after(function() { - storage.setCookie(A1_SEG_KEY, '', 0); - storage.removeDataFromLocalStorage(A1_AUD_KEY); - }) - - it('alterBidRequests', function() { - subModuleObj.getBidRequestData(reqBidsConfigObj, callback); - expect(reqBidsConfigObj.ortb2Fragments.global).to.deep.include(a1TestOrtbObj); - expect(callback.calledOnce).to.be.true; - }) - }); -}) diff --git a/test/spec/modules/pubCircleBidAdapter_spec.js b/test/spec/modules/acuityAdsBidAdapter_spec.js similarity index 96% rename from test/spec/modules/pubCircleBidAdapter_spec.js rename to test/spec/modules/acuityAdsBidAdapter_spec.js index 8aaa023ee1c..05c59036ff3 100644 --- a/test/spec/modules/pubCircleBidAdapter_spec.js +++ b/test/spec/modules/acuityAdsBidAdapter_spec.js @@ -1,11 +1,11 @@ import { expect } from 'chai'; -import { spec } from '../../../modules/pubCircleBidAdapter'; +import { spec } from '../../../modules/acuityAdsBidAdapter'; import { BANNER, VIDEO, NATIVE } from '../../../src/mediaTypes.js'; import { getUniqueIdentifierStr } from '../../../src/utils.js'; -const bidder = 'pubcircle' +const bidder = 'acuityads' -describe('PubCircleBidAdapter', function () { +describe('AcuityAdsBidAdapter', function () { const bids = [ { bidId: getUniqueIdentifierStr(), @@ -104,7 +104,7 @@ describe('PubCircleBidAdapter', function () { }); it('Returns valid URL', function () { - expect(serverRequest.url).to.equal('https://ml.pubcircle.ai/pbjs'); + expect(serverRequest.url).to.equal('https://prebid.admanmedia.com/pbjs'); }); it('Returns general data valid', function () { @@ -382,7 +382,7 @@ describe('PubCircleBidAdapter', function () { 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://cs.pubcircle.ai/image?pbjs=1&gdpr=1&gdpr_consent=ALL&coppa=0') + 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({}, {}, {}, { @@ -393,7 +393,7 @@ describe('PubCircleBidAdapter', function () { 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://cs.pubcircle.ai/image?pbjs=1&ccpa_consent=1---&coppa=0') + expect(syncData[0].url).to.equal('https://cs.admanmedia.com/image?pbjs=1&ccpa_consent=1---&coppa=0') }); }); }); diff --git a/test/spec/modules/acuityadsBidAdapter_spec.js b/test/spec/modules/acuityadsBidAdapter_spec.js deleted file mode 100644 index 31ef9dd6466..00000000000 --- a/test/spec/modules/acuityadsBidAdapter_spec.js +++ /dev/null @@ -1,428 +0,0 @@ -import { expect } from 'chai'; -import { spec } from '../../../modules/acuityadsBidAdapter'; -import { BANNER, VIDEO, NATIVE } from '../../../src/mediaTypes.js'; -import { getUniqueIdentifierStr } from '../../../src/utils.js'; - -const bidder = 'acuityads' - -describe('AcuityAdsBidAdapter', function () { - const bids = [ - { - bidId: getUniqueIdentifierStr(), - bidder: bidder, - mediaTypes: { - [BANNER]: { - sizes: [[300, 250]] - } - }, - params: { - placementId: 'testBanner', - } - }, - { - bidId: getUniqueIdentifierStr(), - bidder: bidder, - mediaTypes: { - [VIDEO]: { - playerSize: [[300, 300]], - minduration: 5, - maxduration: 60 - } - }, - params: { - placementId: 'testVideo', - } - }, - { - bidId: getUniqueIdentifierStr(), - bidder: bidder, - mediaTypes: { - [NATIVE]: { - native: { - title: { - required: true - }, - body: { - required: true - }, - icon: { - required: true, - size: [64, 64] - } - } - } - }, - params: { - placementId: 'testNative', - } - } - ]; - - const invalidBid = { - bidId: getUniqueIdentifierStr(), - bidder: bidder, - mediaTypes: { - [BANNER]: { - sizes: [[300, 250]] - } - }, - params: { - - } - } - - const bidderRequest = { - uspConsent: '1---', - gdprConsent: 'COvFyGBOvFyGBAbAAAENAPCAAOAAAAAAAAAAAEEUACCKAAA.IFoEUQQgAIQwgIwQABAEAAAAOIAACAIAAAAQAIAgEAACEAAAAAgAQBAAAAAAAGBAAgAAAAAAAFAAECAAAgAAQARAEQAAAAAJAAIAAgAAAYQEAAAQmAgBC3ZAYzUw', - refererInfo: { - referer: 'https://test.com' - }, - timeout: 500, - ortb2: {} - }; - - 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://prebid.admanmedia.com/pbjs'); - }); - - it('Returns general data valid', function () { - let data = serverRequest.data; - expect(data).to.be.an('object'); - expect(data).to.have.all.keys('deviceWidth', - 'deviceHeight', - 'language', - 'secure', - 'host', - 'page', - 'placements', - 'coppa', - 'ccpa', - 'gdpr', - 'tmax' - ); - 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('string'); - 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'); - - 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); - let data = serverRequest.data; - expect(data.gdpr).to.exist; - expect(data.gdpr).to.be.a('string'); - expect(data.gdpr).to.equal(bidderRequest.gdprConsent); - 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); - let 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('Returns data with gppConsent', function () { - it('bidderRequest.gppConsent', () => { - bidderRequest.gppConsent = { - gppString: 'abc123', - applicableSections: [8] - }; - - serverRequest = spec.buildRequests(bids, bidderRequest); - let 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.regs = bidderRequest.ortb2.regs || {}; - bidderRequest.ortb2.regs.gpp = 'abc123'; - bidderRequest.ortb2.regs.gpp_sid = [8]; - - serverRequest = spec.buildRequests(bids, bidderRequest); - let data = serverRequest.data; - expect(data).to.be.an('object'); - expect(data).to.have.property('gpp'); - expect(data).to.have.property('gpp_sid'); - }) - }); - - it('Returns empty data if no valid requests are passed', function () { - serverRequest = spec.buildRequests([], bidderRequest); - let data = serverRequest.data; - expect(data.placements).to.be.an('array').that.is.empty; - }); - }); - - 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 - } - }] - }; - let bannerResponses = spec.interpretResponse(banner); - expect(bannerResponses).to.be.an('array').that.is.not.empty; - let dataItem = bannerResponses[0]; - expect(dataItem).to.have.all.keys('requestId', 'cpm', 'width', 'height', 'ad', 'ttl', 'creativeId', - 'netRevenue', 'currency', 'dealId', '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 - } - }] - }; - let videoResponses = spec.interpretResponse(video); - expect(videoResponses).to.be.an('array').that.is.not.empty; - - let dataItem = videoResponses[0]; - expect(dataItem).to.have.all.keys('requestId', 'cpm', 'vastUrl', 'ttl', 'creativeId', - 'netRevenue', 'currency', 'dealId', '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 - } - }] - }; - let nativeResponses = spec.interpretResponse(native); - expect(nativeResponses).to.be.an('array').that.is.not.empty; - - let dataItem = nativeResponses[0]; - expect(dataItem).to.have.keys('requestId', 'cpm', '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' - }] - }; - - let serverResponses = spec.interpretResponse(invBanner); - expect(serverResponses).to.be.an('array').that.is.empty; - }); - it('Should return an empty array if invalid video response is passed', function () { - const invVideo = { - body: [{ - mediaType: 'video', - cpm: 0.5, - requestId: '23fhj33i987f', - ttl: 120, - creativeId: '2', - netRevenue: true, - currency: 'USD', - dealId: '1' - }] - }; - let serverResponses = spec.interpretResponse(invVideo); - expect(serverResponses).to.be.an('array').that.is.empty; - }); - it('Should return an empty array if invalid native response is passed', function () { - const invNative = { - body: [{ - mediaType: 'native', - clickUrl: 'test.com', - title: 'Test', - impressionTrackers: ['test.com'], - ttl: 120, - requestId: '23fhj33i987f', - creativeId: '2', - netRevenue: true, - currency: 'USD', - }] - }; - let serverResponses = spec.interpretResponse(invNative); - expect(serverResponses).to.be.an('array').that.is.empty; - }); - it('Should return an empty array if invalid response is passed', function () { - const invalid = { - body: [{ - ttl: 120, - creativeId: '2', - netRevenue: true, - currency: 'USD', - dealId: '1' - }] - }; - let serverResponses = spec.interpretResponse(invalid); - expect(serverResponses).to.be.an('array').that.is.empty; - }); - }); - - describe('getUserSyncs', function() { - it('Should return array of objects with proper sync config , include GDPR', function() { - const syncData = spec.getUserSyncs({}, {}, { - consentString: 'ALL', - gdprApplies: true, - }, {}); - 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://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---' - }); - 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://cs.admanmedia.com/image?pbjs=1&ccpa_consent=1---&coppa=0') - }); - }); -}); diff --git a/test/spec/modules/ad2ictionBidAdapter_spec.js b/test/spec/modules/ad2ictionBidAdapter_spec.js deleted file mode 100644 index 99800c6dd01..00000000000 --- a/test/spec/modules/ad2ictionBidAdapter_spec.js +++ /dev/null @@ -1,223 +0,0 @@ -import { expect } from 'chai'; -import { - spec, - API_ENDPOINT, - API_VERSION_NUMBER, -} from 'modules/ad2ictionBidAdapter.js'; -import { newBidder } from 'src/adapters/bidderFactory.js'; - -describe('ad2ictionBidAdapter', 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 bid = { - bidder: 'ad2iction', - params: { id: '11ab384c-e936-11ed-a6a7-f23c9173ed43' }, - mediaTypes: { - banner: { - sizes: [ - [300, 250], - [336, 280], - ], - }, - }, - adUnitCode: 'adunit-code', - sizes: [ - [300, 250], - [336, 280], - ], - bidId: '2a7a3b48778a1b', - bidderRequestId: '1e6509293abe6b', - }; - - it('should return true when required params found', function () { - expect(spec.isBidRequestValid(bid)).to.equal(true); - }); - - it('should return false when params id is not valid (letters)', function () { - const mockBid = { - ...bid, - params: { id: 1234 }, - }; - - expect(spec.isBidRequestValid(mockBid)).to.equal(false); - }); - - it('should return false when params id is not exist', function () { - const mockBid = { - ...bid, - }; - delete mockBid.params.id; - - expect(spec.isBidRequestValid(mockBid)).to.equal(false); - }); - }); - - describe('buildRequests', function () { - const mockValidBidRequests = [ - { - bidder: 'ad2iction', - params: { id: '11ab384c-e936-11ed-a6a7-f23c9173ed43' }, - adUnitCode: 'adunit-code', - sizes: [ - [300, 250], - [336, 280], - ], - bidId: '57ffc0667379e1', - bidderRequestId: '4ddea14478a651', - }, - ]; - - const mockBidderRequest = { - bidderCode: 'ad2iction', - bidderRequestId: '4ddea14478a651', - bids: [ - { - bidder: 'ad2iction', - params: { id: '11ab384c-e936-11ed-a6a7-f23c9173ed43' }, - adUnitCode: 'adunit-code', - transactionId: null, - sizes: [ - [300, 250], - [336, 280], - ], - bidId: '57ffc0667379e1', - bidderRequestId: '4ddea14478a651', - }, - ], - timeout: 1200, - refererInfo: { - ref: 'https://example.com/referer.html', - }, - ortb2: { - source: {}, - site: { - ref: 'https://example.com/referer.html', - }, - device: { - w: 390, - h: 844, - language: 'zh', - }, - }, - start: 1702526505498, - }; - - it('should send bid request to API_ENDPOINT via POST', function () { - const request = spec.buildRequests( - mockValidBidRequests, - mockBidderRequest - ); - - expect(request.url).to.equal(API_ENDPOINT); - expect(request.method).to.equal('POST'); - }); - - it('should send bid request with API version', function () { - const request = spec.buildRequests( - mockValidBidRequests, - mockBidderRequest - ); - - expect(request.data.v).to.equal(API_VERSION_NUMBER); - }); - - it('should send bid request with dada fields', function () { - const request = spec.buildRequests( - mockValidBidRequests, - mockBidderRequest - ); - - expect(request.data).to.include.all.keys('udid', '_'); - expect(request.data).to.have.property('refererInfo'); - expect(request.data).to.have.property('ortb2'); - }); - }); - - describe('interpretResponse', function () { - it('should return an empty array to indicate no valid bids', function () { - const mockServerResponse = {}; - - const bidResponses = spec.interpretResponse(mockServerResponse); - - expect(bidResponses).is.an('array').that.is.empty; - }); - - it('should return a valid bid response', function () { - const MOCK_AD_DOM = "
" - const mockServerResponse = { - body: [ - { - requestId: '23a3d87fb6bde9', - cpm: 1.61, - currency: 'USD', - width: '336', - height: '280', - creativeId: '46271', - netRevenue: 'false', - ad: MOCK_AD_DOM, - meta: { - advertiserDomains: [''], - }, - ttl: 360, - }, - { - requestId: '3ce3efc40c890b', - cpm: 1.61, - currency: 'USD', - width: '336', - height: '280', - creativeId: '46271', - netRevenue: 'false', - ad: MOCK_AD_DOM, - meta: { - advertiserDomains: [''], - }, - ttl: 360, - }, - ], - }; - - const exceptServerResponse = [ - { - requestId: '23a3d87fb6bde9', - cpm: 1.61, - currency: 'USD', - width: '336', - height: '280', - creativeId: '46271', - netRevenue: 'false', - ad: MOCK_AD_DOM, - meta: { - advertiserDomains: [''], - }, - ttl: 360, - }, - { - requestId: '3ce3efc40c890b', - cpm: 1.61, - currency: 'USD', - width: '336', - height: '280', - creativeId: '46271', - netRevenue: 'false', - ad: MOCK_AD_DOM, - meta: { - advertiserDomains: [''], - }, - ttl: 360, - }, - ] - - const bidResponses = spec.interpretResponse(mockServerResponse); - - expect(bidResponses).to.eql(exceptServerResponse); - }); - }); -}); diff --git a/test/spec/modules/adagioAnalyticsAdapter_spec.js b/test/spec/modules/adagioAnalyticsAdapter_spec.js index 5ffd7b0b685..581f3cb1b87 100644 --- a/test/spec/modules/adagioAnalyticsAdapter_spec.js +++ b/test/spec/modules/adagioAnalyticsAdapter_spec.js @@ -1,14 +1,12 @@ import adagioAnalyticsAdapter from 'modules/adagioAnalyticsAdapter.js'; import { expect } from 'chai'; import * as utils from 'src/utils.js'; -import { getGlobal } from 'src/prebidGlobal.js'; -import { server } from 'test/mocks/xhr.js'; let adapterManager = require('src/adapterManager').default; let events = require('src/events'); let constants = require('src/constants.json'); -describe('adagio analytics adapter - adagio.js', () => { +describe('adagio analytics adapter', () => { let sandbox; let adagioQueuePushSpy; @@ -176,658 +174,3 @@ describe('adagio analytics adapter - adagio.js', () => { }); }); }); - -const AUCTION_ID = '25c6d7f5-699a-4bfc-87c9-996f915341fa'; -const AUCTION_ID_ADAGIO = '6fc53663-bde5-427b-ab63-baa9ed296f47' -const AUCTION_ID_CACHE = 'b43d24a0-13d4-406d-8176-3181402bafc4'; -const AUCTION_ID_CACHE_ADAGIO = 'a9cae98f-efb5-477e-9259-27350044f8db'; - -const BID_ADAGIO = Object.assign({}, BID_ADAGIO, { - bidder: 'adagio', - auctionId: AUCTION_ID, - adUnitCode: '/19968336/header-bid-tag-1', - bidId: '3bd4ebb1c900e2', - partnerImpId: 'partnerImpressionID-2', - adId: 'fake_ad_id_2', - requestId: '3bd4ebb1c900e2', - width: 728, - height: 90, - mediaType: 'banner', - cpm: 1.42, - currency: 'USD', - originalCpm: 1.42, - originalCurrency: 'USD', - dealId: 'the-deal-id', - dealChannel: 'PMP', - mi: 'matched-impression', - seatBidId: 'aaaa-bbbb-cccc-dddd', - adserverTargeting: { - 'hb_bidder': 'another', - 'hb_adid': '3bd4ebb1c900e2', - 'hb_pb': '1.500', - 'hb_size': '728x90', - 'hb_source': 'server' - }, - meta: { - advertiserDomains: ['example.com'] - }, - pba: { - sid: '42', - e_pba_test: true - } -}); - -const BID_ANOTHER = Object.assign({}, BID_ANOTHER, { - bidder: 'another', - auctionId: AUCTION_ID, - adUnitCode: '/19968336/header-bid-tag-1', - bidId: '3bd4ebb1c900e2', - partnerImpId: 'partnerImpressionID-2', - adId: 'fake_ad_id_2', - requestId: '3bd4ebb1c900e2', - width: 728, - height: 90, - mediaType: 'banner', - cpm: 1.71, - currency: 'EUR', - originalCpm: 1.62, - originalCurrency: 'GBP', - dealId: 'the-deal-id', - dealChannel: 'PMP', - mi: 'matched-impression', - seatBidId: 'aaaa-bbbb-cccc-dddd', - adserverTargeting: { - 'hb_bidder': 'another', - 'hb_adid': '3bd4ebb1c900e2', - 'hb_pb': '1.500', - 'hb_size': '728x90', - 'hb_source': 'server' - }, - meta: { - advertiserDomains: ['example.com'] - } -}); - -const BID_CACHED = Object.assign({}, BID_ADAGIO, { - auctionId: AUCTION_ID_CACHE, - latestTargetedAuctionId: BID_ADAGIO.auctionId, -}); - -const PARAMS_ADG = { - organizationId: '1001', - site: 'test-com', - pageviewId: 'a68e6d70-213b-496c-be0a-c468ff387106', - environment: 'desktop', - pagetype: 'article', - placement: 'pave_top', - testName: 'test', - testVersion: 'version', -}; - -const AUCTION_INIT_ANOTHER = { - 'auctionId': AUCTION_ID, - 'timestamp': 1519767010567, - 'auctionStatus': 'inProgress', - 'adUnits': [ { - 'code': '/19968336/header-bid-tag-1', - 'mediaTypes': { - 'banner': { - 'sizes': [ - [ - 640, - 480 - ], - [ - 640, - 100 - ] - ] - } - }, - 'sizes': [[640, 480]], - 'bids': [ { - 'bidder': 'another', - 'params': { - 'publisherId': '1001' - }, - }, { - 'bidder': 'nobid', - 'params': { - 'publisherId': '1002' - }, - }, { - 'bidder': 'adagio', - 'params': { - ...PARAMS_ADG - }, - }, ], - 'transactionId': 'ca4af27a-6d02-4f90-949d-d5541fa12014' - }, { - 'code': '/19968336/footer-bid-tag-1', - 'mediaTypes': { - 'banner': { - 'sizes': [ - [ - 640, - 480 - ] - ] - } - }, - 'sizes': [[640, 480]], - 'bids': [ { - 'bidder': 'another', - 'params': { - 'publisherId': '1001' - }, - } ], - 'transactionId': 'ca4af27a-6d02-4f90-949d-d5541fa12014' - } ], - 'adUnitCodes': ['/19968336/header-bid-tag-1', '/19968336/footer-bid-tag-1'], - 'bidderRequests': [ { - 'bidderCode': 'another', - 'auctionId': AUCTION_ID, - 'bidderRequestId': '1be65d7958826a', - 'bids': [ { - 'bidder': 'another', - 'params': { - 'publisherId': '1001', - }, - 'mediaTypes': { - 'banner': { - 'sizes': [[640, 480]] - } - }, - 'adUnitCode': '/19968336/header-bid-tag-1', - 'transactionId': 'ca4af27a-6d02-4f90-949d-d5541fa12014', - 'sizes': [[640, 480]], - 'bidId': '2ecff0db240757', - 'bidderRequestId': '1be65d7958826a', - 'auctionId': AUCTION_ID, - 'src': 'client', - 'bidRequestsCount': 1 - }, { - 'bidder': 'another', - 'params': { - 'publisherId': '1001' - }, - 'mediaTypes': { - 'banner': { - 'sizes': [[640, 480]] - } - }, - 'adUnitCode': '/19968336/footer-bid-tag-1', - 'transactionId': 'ca4af27a-6d02-4f90-949d-d5541fa12014', - 'sizes': [[640, 480]], - 'bidId': '2ecff0db240757', - 'bidderRequestId': '1be65d7958826a', - 'auctionId': AUCTION_ID, - 'src': 'client', - 'bidRequestsCount': 1 - }, { - 'bidder': 'nobid', - 'params': { - 'publisherId': '1001' - }, - 'mediaTypes': { - 'banner': { - 'sizes': [[640, 480]] - } - }, - 'adUnitCode': '/19968336/footer-bid-tag-1', - 'transactionId': 'ca4af27a-6d02-4f90-949d-d5541fa12014', - 'sizes': [[640, 480]], - 'bidId': '2ecff0db240757', - 'bidderRequestId': '1be65d7958826a', - 'auctionId': AUCTION_ID, - 'src': 'client', - 'bidRequestsCount': 1 - } - ], - 'timeout': 3000, - 'refererInfo': { - 'topmostLocation': 'http://www.test.com/page.html', 'reachedTop': true, 'numIframes': 0, 'stack': ['http://www.test.com/page.html'] - } - }, { - 'bidderCode': 'adagio', - 'auctionId': AUCTION_ID, - 'bidderRequestId': '1be65d7958826a', - 'bids': [ { - 'bidder': 'adagio', - 'params': { - ...PARAMS_ADG, - adagioAuctionId: AUCTION_ID_ADAGIO - }, - 'mediaTypes': { - 'banner': { - 'sizes': [[640, 480]] - } - }, - 'adUnitCode': '/19968336/header-bid-tag-1', - 'transactionId': 'ca4af27a-6d02-4f90-949d-d5541fa12014', - 'sizes': [[640, 480]], - 'bidId': '2ecff0db240757', - 'bidderRequestId': '1be65d7958826a', - 'auctionId': AUCTION_ID, - 'src': 'client', - 'bidRequestsCount': 1 - } - ], - 'timeout': 3000, - 'refererInfo': { - 'topmostLocation': 'http://www.test.com/page.html', 'reachedTop': true, 'numIframes': 0, 'stack': ['http://www.test.com/page.html'] - } - } - ], - 'bidsReceived': [], - 'winningBids': [], - 'timeout': 3000 -}; - -const AUCTION_INIT_CACHE = { - 'auctionId': AUCTION_ID_CACHE, - 'timestamp': 1519767010567, - 'auctionStatus': 'inProgress', - 'adUnits': [ { - 'code': '/19968336/header-bid-tag-1', - 'mediaTypes': { - 'banner': { - 'sizes': [ - [ - 640, - 480 - ], - [ - 640, - 100 - ] - ] - } - }, - 'sizes': [[640, 480]], - 'bids': [ { - 'bidder': 'another', - 'params': { - 'publisherId': '1001' - }, - }, { - 'bidder': 'adagio', - 'params': { - ...PARAMS_ADG - }, - }, ], - 'transactionId': 'ca4af27a-6d02-4f90-949d-d5541fa12014' - }, { - 'code': '/19968336/footer-bid-tag-1', - 'mediaTypes': { - 'banner': { - 'sizes': [ - [ - 640, - 480 - ] - ] - } - }, - 'sizes': [[640, 480]], - 'bids': [ { - 'bidder': 'another', - 'params': { - 'publisherId': '1001' - }, - } ], - 'transactionId': 'ca4af27a-6d02-4f90-949d-d5541fa12014' - } ], - 'adUnitCodes': ['/19968336/header-bid-tag-1', '/19968336/footer-bid-tag-1'], - 'bidderRequests': [ { - 'bidderCode': 'another', - 'auctionId': AUCTION_ID_CACHE, - 'bidderRequestId': '1be65d7958826a', - 'bids': [ { - 'bidder': 'another', - 'params': { - 'publisherId': '1001', - }, - 'mediaTypes': { - 'banner': { - 'sizes': [[640, 480]] - } - }, - 'adUnitCode': '/19968336/header-bid-tag-1', - 'transactionId': 'ca4af27a-6d02-4f90-949d-d5541fa12014', - 'sizes': [[640, 480]], - 'bidId': '2ecff0db240757', - 'bidderRequestId': '1be65d7958826a', - 'auctionId': AUCTION_ID_CACHE, - 'src': 'client', - 'bidRequestsCount': 1 - }, { - 'bidder': 'another', - 'params': { - 'publisherId': '1001' - }, - 'mediaTypes': { - 'banner': { - 'sizes': [[640, 480]] - } - }, - 'adUnitCode': '/19968336/footer-bid-tag-1', - 'transactionId': 'ca4af27a-6d02-4f90-949d-d5541fa12014', - 'sizes': [[640, 480]], - 'bidId': '2ecff0db240757', - 'bidderRequestId': '1be65d7958826a', - 'auctionId': AUCTION_ID_CACHE, - 'src': 'client', - 'bidRequestsCount': 1 - } - ], - 'timeout': 3000, - 'refererInfo': { - 'topmostLocation': 'http://www.test.com/page.html', 'reachedTop': true, 'numIframes': 0, 'stack': ['http://www.test.com/page.html'] - } - }, { - 'bidderCode': 'adagio', - 'auctionId': AUCTION_ID_CACHE, - 'bidderRequestId': '1be65d7958826a', - 'bids': [ { - 'bidder': 'adagio', - 'params': { - ...PARAMS_ADG, - adagioAuctionId: AUCTION_ID_CACHE_ADAGIO - }, - 'mediaTypes': { - 'banner': { - 'sizes': [[640, 480]] - } - }, - 'adUnitCode': '/19968336/header-bid-tag-1', - 'transactionId': 'ca4af27a-6d02-4f90-949d-d5541fa12014', - 'sizes': [[640, 480]], - 'bidId': '2ecff0db240757', - 'bidderRequestId': '1be65d7958826a', - 'auctionId': AUCTION_ID_CACHE, - 'src': 'client', - 'bidRequestsCount': 1 - } - ], - 'timeout': 3000, - 'refererInfo': { - 'topmostLocation': 'http://www.test.com/page.html', 'reachedTop': true, 'numIframes': 0, 'stack': ['http://www.test.com/page.html'] - } - } - ], - 'bidsReceived': [], - 'winningBids': [], - 'timeout': 3000 -}; - -const AUCTION_END_ANOTHER = Object.assign({}, AUCTION_INIT_ANOTHER, { - bidsReceived: [BID_ANOTHER, BID_ADAGIO] -}); - -const AUCTION_END_ANOTHER_NOBID = Object.assign({}, AUCTION_INIT_ANOTHER, { - bidsReceived: [] -}); - -const MOCK = { - SET_TARGETING: { - [BID_ADAGIO.adUnitCode]: BID_ADAGIO.adserverTargeting, - [BID_ANOTHER.adUnitCode]: BID_ANOTHER.adserverTargeting - }, - AUCTION_INIT: { - another: AUCTION_INIT_ANOTHER, - bidcached: AUCTION_INIT_CACHE - }, - BID_RESPONSE: { - adagio: BID_ADAGIO, - another: BID_ANOTHER - }, - AUCTION_END: { - another: AUCTION_END_ANOTHER, - another_nobid: AUCTION_END_ANOTHER_NOBID - }, - BID_WON: { - adagio: Object.assign({}, BID_ADAGIO, { - 'status': 'rendered' - }), - another: Object.assign({}, BID_ANOTHER, { - 'status': 'rendered' - }), - bidcached: Object.assign({}, BID_CACHED, { - 'status': 'rendered' - }), - }, - AD_RENDER_SUCCEEDED: { - another: { - ad: '
ad
', - adId: 'fake_ad_id_2', - bid: BID_ANOTHER - }, - bidcached: { - ad: '
ad
', - adId: 'fake_ad_id_2', - bid: BID_CACHED - } - }, - AD_RENDER_FAILED: { - bidcached: { - adId: 'fake_ad_id_2', - bid: BID_CACHED - } - } -}; - -describe('adagio analytics adapter', () => { - let sandbox; - - beforeEach(() => { - sandbox = sinon.sandbox.create(); - - sandbox.stub(events, 'getEvents').returns([]); - - adapterManager.registerAnalyticsAdapter({ - code: 'adagio', - adapter: adagioAnalyticsAdapter - }); - }); - - afterEach(() => { - sandbox.restore(); - }); - - describe('track', () => { - beforeEach(() => { - adapterManager.enableAnalytics({ - provider: 'adagio' - }); - }); - - afterEach(() => { - adagioAnalyticsAdapter.disableAnalytics(); - }); - - it('builds and sends auction data', () => { - getGlobal().convertCurrency = (cpm, from, to) => { - const convKeys = { - 'GBP-EUR': 0.7, - 'EUR-GBP': 1.3, - 'USD-EUR': 0.8, - 'EUR-USD': 1.2, - 'USD-GBP': 0.6, - 'GBP-USD': 1.6, - }; - return cpm * (convKeys[`${from}-${to}`] || 1); - }; - - events.emit(constants.EVENTS.AUCTION_INIT, MOCK.AUCTION_INIT.another); - events.emit(constants.EVENTS.BID_RESPONSE, MOCK.BID_RESPONSE.adagio); - events.emit(constants.EVENTS.BID_RESPONSE, MOCK.BID_RESPONSE.another); - events.emit(constants.EVENTS.AUCTION_END, MOCK.AUCTION_END.another); - events.emit(constants.EVENTS.BID_WON, MOCK.BID_WON.another); - events.emit(constants.EVENTS.AD_RENDER_SUCCEEDED, MOCK.AD_RENDER_SUCCEEDED.another); - - expect(server.requests.length).to.equal(3, 'requests count'); - { - const { protocol, hostname, pathname, search } = utils.parseUrl(server.requests[0].url); - expect(protocol).to.equal('https'); - expect(hostname).to.equal('c.4dex.io'); - expect(pathname).to.equal('/pba.gif'); - expect(search.v).to.equal('1'); - expect(search.pbjsv).to.equal('$prebid.version$'); - expect(search.auct_id).to.equal(AUCTION_ID_ADAGIO); - expect(search.adu_code).to.equal('/19968336/header-bid-tag-1'); - expect(search.org_id).to.equal('1001'); - expect(search.site).to.equal('test-com'); - expect(search.pv_id).to.equal('a68e6d70-213b-496c-be0a-c468ff387106'); - expect(search.url_dmn).to.equal(window.location.hostname); - expect(search.pgtyp).to.equal('article'); - expect(search.plcmt).to.equal('pave_top'); - expect(search.mts).to.equal('ban'); - expect(search.ban_szs).to.equal('640x100,640x480'); - expect(search.bdrs).to.equal('adagio,another,nobid'); - expect(search.adg_mts).to.equal('ban'); - } - - { - const { protocol, hostname, pathname, search } = utils.parseUrl(server.requests[1].url); - expect(protocol).to.equal('https'); - expect(hostname).to.equal('c.4dex.io'); - expect(pathname).to.equal('/pba.gif'); - expect(search.v).to.equal('2'); - expect(search.e_sid).to.equal('42'); - expect(search.e_pba_test).to.equal('true'); - expect(search.bdrs_bid).to.equal('1,1,0'); - } - - { - const { protocol, hostname, pathname, search } = utils.parseUrl(server.requests[2].url); - expect(protocol).to.equal('https'); - expect(hostname).to.equal('c.4dex.io'); - expect(pathname).to.equal('/pba.gif'); - expect(search.v).to.equal('3'); - expect(search.auct_id).to.equal(AUCTION_ID_ADAGIO); - expect(search.adu_code).to.equal('/19968336/header-bid-tag-1'); - expect(search.win_bdr).to.equal('another'); - expect(search.win_mt).to.equal('ban'); - expect(search.win_ban_sz).to.equal('728x90'); - expect(search.win_cpm).to.equal('1.71'); - expect(search.cur).to.equal('EUR'); - expect(search.cur_rate).to.equal('1.2'); - expect(search.og_cpm).to.equal('1.62'); - expect(search.og_cur).to.equal('GBP'); - expect(search.og_cur_rate).to.equal('1.6'); - } - }); - - it('builds and sends auction data with a cached bid win', () => { - getGlobal().convertCurrency = (cpm, from, to) => { - const convKeys = { - 'GBP-EUR': 0.7, - 'EUR-GBP': 1.3, - 'USD-EUR': 0.8, - 'EUR-USD': 1.2, - 'USD-GBP': 0.6, - 'GBP-USD': 1.6, - }; - return cpm * (convKeys[`${from}-${to}`] || 1); - }; - - events.emit(constants.EVENTS.AUCTION_INIT, MOCK.AUCTION_INIT.bidcached); - events.emit(constants.EVENTS.AUCTION_INIT, MOCK.AUCTION_INIT.another); - events.emit(constants.EVENTS.BID_RESPONSE, MOCK.BID_RESPONSE.adagio); - events.emit(constants.EVENTS.BID_RESPONSE, MOCK.BID_RESPONSE.another); - events.emit(constants.EVENTS.AUCTION_END, MOCK.AUCTION_END.another_nobid); - events.emit(constants.EVENTS.BID_WON, MOCK.BID_WON.bidcached); - events.emit(constants.EVENTS.AD_RENDER_FAILED, MOCK.AD_RENDER_FAILED.bidcached); - - expect(server.requests.length).to.equal(5, 'requests count'); - { - const { protocol, hostname, pathname, search } = utils.parseUrl(server.requests[0].url); - expect(protocol).to.equal('https'); - expect(hostname).to.equal('c.4dex.io'); - expect(pathname).to.equal('/pba.gif'); - expect(search.v).to.equal('1'); - expect(search.pbjsv).to.equal('$prebid.version$'); - expect(search.auct_id).to.equal(AUCTION_ID_CACHE_ADAGIO); - expect(search.adu_code).to.equal('/19968336/header-bid-tag-1'); - expect(search.org_id).to.equal('1001'); - expect(search.site).to.equal('test-com'); - expect(search.pv_id).to.equal('a68e6d70-213b-496c-be0a-c468ff387106'); - expect(search.url_dmn).to.equal(window.location.hostname); - expect(search.pgtyp).to.equal('article'); - expect(search.plcmt).to.equal('pave_top'); - expect(search.mts).to.equal('ban'); - expect(search.ban_szs).to.equal('640x100,640x480'); - expect(search.bdrs).to.equal('adagio,another'); - expect(search.adg_mts).to.equal('ban'); - expect(search.t_n).to.equal('test'); - expect(search.t_v).to.equal('version'); - } - - { - const { protocol, hostname, pathname, search } = utils.parseUrl(server.requests[1].url); - expect(protocol).to.equal('https'); - expect(hostname).to.equal('c.4dex.io'); - expect(pathname).to.equal('/pba.gif'); - expect(search.v).to.equal('1'); - expect(search.pbjsv).to.equal('$prebid.version$'); - expect(search.auct_id).to.equal(AUCTION_ID_ADAGIO); - expect(search.adu_code).to.equal('/19968336/header-bid-tag-1'); - expect(search.org_id).to.equal('1001'); - expect(search.site).to.equal('test-com'); - expect(search.pv_id).to.equal('a68e6d70-213b-496c-be0a-c468ff387106'); - expect(search.url_dmn).to.equal(window.location.hostname); - expect(search.pgtyp).to.equal('article'); - expect(search.plcmt).to.equal('pave_top'); - expect(search.mts).to.equal('ban'); - expect(search.ban_szs).to.equal('640x100,640x480'); - expect(search.bdrs).to.equal('adagio,another,nobid'); - expect(search.adg_mts).to.equal('ban'); - } - - { - const { protocol, hostname, pathname, search } = utils.parseUrl(server.requests[2].url); - expect(protocol).to.equal('https'); - expect(hostname).to.equal('c.4dex.io'); - expect(pathname).to.equal('/pba.gif'); - expect(search.v).to.equal('2'); - expect(search.e_sid).to.equal('42'); - expect(search.e_pba_test).to.equal('true'); - expect(search.bdrs_bid).to.equal('0,0,0'); - } - - { - const { protocol, hostname, pathname, search } = utils.parseUrl(server.requests[3].url); - expect(protocol).to.equal('https'); - expect(hostname).to.equal('c.4dex.io'); - expect(pathname).to.equal('/pba.gif'); - expect(search.v).to.equal('3'); - expect(search.auct_id).to.equal(AUCTION_ID_ADAGIO); - expect(search.auct_id_c).to.equal(AUCTION_ID_CACHE_ADAGIO); - expect(search.adu_code).to.equal('/19968336/header-bid-tag-1'); - expect(search.win_bdr).to.equal('adagio'); - expect(search.win_mt).to.equal('ban'); - expect(search.win_ban_sz).to.equal('728x90'); - expect(search.win_cpm).to.equal('1.42'); - expect(search.cur).to.equal('USD'); - expect(search.cur_rate).to.equal('1'); - expect(search.og_cpm).to.equal('1.42'); - expect(search.og_cur).to.equal('USD'); - expect(search.og_cur_rate).to.equal('1'); - expect(search.rndr).to.not.exist; - } - - { - const { protocol, hostname, pathname, search } = utils.parseUrl(server.requests[4].url); - expect(protocol).to.equal('https'); - expect(hostname).to.equal('c.4dex.io'); - expect(pathname).to.equal('/pba.gif'); - expect(search.v).to.equal('4'); - expect(search.auct_id).to.equal(AUCTION_ID_ADAGIO); - expect(search.auct_id_c).to.equal(AUCTION_ID_CACHE_ADAGIO); - expect(search.adu_code).to.equal('/19968336/header-bid-tag-1'); - expect(search.rndr).to.equal('0'); - } - }); - }); -}); diff --git a/test/spec/modules/adagioBidAdapter_spec.js b/test/spec/modules/adagioBidAdapter_spec.js index 744f3c69e83..2fb40070184 100644 --- a/test/spec/modules/adagioBidAdapter_spec.js +++ b/test/spec/modules/adagioBidAdapter_spec.js @@ -256,6 +256,7 @@ describe('Adagio bid adapter', () => { describe('buildRequests()', function() { const expectedDataKeys = [ + 'id', 'organizationId', 'secure', 'device', @@ -268,8 +269,7 @@ describe('Adagio bid adapter', () => { 'prebidVersion', 'featuresVersion', 'data', - 'usIfr', - 'adgjs', + 'usIfr' ]; it('groups requests by organizationId', function() { @@ -322,21 +322,6 @@ describe('Adagio bid adapter', () => { expect(requests[0].data.adUnits[0].transactionId).to.not.exist; }); - it('should enrich prebid bid requests params', function() { - const expectedAuctionId = '373bcda7-9794-4f1c-be2c-0d223d11d579' - const expectedPageviewId = '56befc26-8cf0-472d-b105-73896df8eb89'; - sandbox.stub(utils, 'generateUUID').returns(expectedAuctionId); - sandbox.stub(adagio, 'getPageviewId').returns(expectedPageviewId); - - const bid01 = new BidRequestBuilder().withParams().build(); - const bidderRequest = new BidderRequestBuilder().build(); - - spec.buildRequests([bid01], bidderRequest); - - expect(bid01.params.adagioAuctionId).eq(expectedAuctionId); - expect(bid01.params.pageviewId).eq(expectedPageviewId); - }); - it('should enqueue computed features for collect usage', function() { sandbox.stub(Date, 'now').returns(12345); @@ -712,95 +697,6 @@ describe('Adagio bid adapter', () => { }); }); - describe('with GPP', function() { - const bid01 = new BidRequestBuilder().withParams().build(); - - const regsGpp = 'regs_gpp_consent_string'; - const regsApplicableSections = [2]; - - const ortb2Gpp = 'ortb2_gpp_consent_string'; - const ortb2GppSid = [1]; - - context('When GPP in regs module', function() { - it('send gpp and gppSid to the server', function() { - const bidderRequest = new BidderRequestBuilder({ - gppConsent: { - gppString: regsGpp, - applicableSections: regsApplicableSections, - } - }).build(); - - const requests = spec.buildRequests([bid01], bidderRequest); - - expect(requests[0].data.regs.gpp).to.equal(regsGpp); - expect(requests[0].data.regs.gppSid).to.equal(regsApplicableSections); - }); - }); - - context('When GPP partially defined in regs module', function() { - it('send gpp and gppSid coming from ortb2 to the server', function() { - const bidderRequest = new BidderRequestBuilder({ - gppConsent: { - gppString: regsGpp, - }, - ortb2: { - regs: { - gpp: ortb2Gpp, - gpp_sid: ortb2GppSid, - } - } - }).build(); - - const requests = spec.buildRequests([bid01], bidderRequest); - - expect(requests[0].data.regs.gpp).to.equal(ortb2Gpp); - expect(requests[0].data.regs.gppSid).to.equal(ortb2GppSid); - }); - - it('send empty gpp and gppSid if no ortb2 fields to the server', function() { - const bidderRequest = new BidderRequestBuilder({ - gppConsent: { - gppString: regsGpp, - } - }).build(); - - const requests = spec.buildRequests([bid01], bidderRequest); - - expect(requests[0].data.regs.gpp).to.equal(''); - expect(requests[0].data.regs.gppSid).to.be.empty; - }); - }); - - context('When GPP defined in ortb2 module', function() { - it('send gpp and gppSid coming from ortb2 to the server', function() { - const bidderRequest = new BidderRequestBuilder({ - ortb2: { - regs: { - gpp: ortb2Gpp, - gpp_sid: ortb2GppSid, - } - } - }).build(); - - const requests = spec.buildRequests([bid01], bidderRequest); - - expect(requests[0].data.regs.gpp).to.equal(ortb2Gpp); - expect(requests[0].data.regs.gppSid).to.equal(ortb2GppSid); - }); - }); - - context('When GPP not defined in any modules', function() { - it('send empty gpp and gppSid', function() { - const bidderRequest = new BidderRequestBuilder({}).build(); - - const requests = spec.buildRequests([bid01], bidderRequest); - - expect(requests[0].data.regs.gpp).to.equal(''); - expect(requests[0].data.regs.gppSid).to.be.empty; - }); - }); - }); - describe('with userID modules', function() { const userIdAsEids = [{ 'source': 'pubcid.org', @@ -861,6 +757,11 @@ describe('Adagio bid adapter', () => { } const requests = spec.buildRequests([bid01], bidderRequest); + expect(requests[0].data.adUnits[0].floors.length).to.equal(3); + expect(requests[0].data.adUnits[0].floors[0]).to.deep.equal({f: 1, mt: 'banner', s: '300x250'}); + expect(requests[0].data.adUnits[0].floors[1]).to.deep.equal({f: 1, mt: 'banner', s: '300x600'}); + expect(requests[0].data.adUnits[0].floors[2]).to.deep.equal({f: 1, mt: 'video', s: '600x480'}); + 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}); @@ -885,6 +786,10 @@ describe('Adagio bid adapter', () => { } const requests = spec.buildRequests([bid01], bidderRequest); + expect(requests[0].data.adUnits[0].floors.length).to.equal(2); + expect(requests[0].data.adUnits[0].floors[0]).to.deep.equal({f: 1, mt: 'video'}); + expect(requests[0].data.adUnits[0].floors[1]).to.deep.equal({f: 1, mt: 'native'}); + expect(requests[0].data.adUnits[0].mediaTypes.video.floor).to.equal(1); expect(requests[0].data.adUnits[0].mediaTypes.native.floor).to.equal(1); }); @@ -904,6 +809,8 @@ describe('Adagio bid adapter', () => { } const requests = spec.buildRequests([bid01], bidderRequest); + expect(requests[0].data.adUnits[0].floors.length).to.equal(1); + expect(requests[0].data.adUnits[0].floors[0]).to.deep.equal({mt: 'video'}); expect(requests[0].data.adUnits[0].mediaTypes.video.floor).to.be.undefined; }); }); @@ -947,34 +854,6 @@ describe('Adagio bid adapter', () => { expect(requests[0].data.usIfr).to.equal(false); }); }); - - describe('with GPID', function () { - const gpid = '/12345/my-gpt-tag-0'; - - it('should add preferred gpid to the request', function () { - const bid01 = new BidRequestBuilder().withParams().build(); - bid01.ortb2Imp = { - ext: { - gpid: gpid - } - }; - const bidderRequest = new BidderRequestBuilder().build(); - const requests = spec.buildRequests([bid01], bidderRequest); - expect(requests[0].data.adUnits[0].gpid).to.exist.and.equal(gpid); - }); - - it('should add backup gpid to the request', function () { - const bid01 = new BidRequestBuilder().withParams().build(); - bid01.ortb2Imp = { - ext: { - data: { pbadslot: gpid } - } - }; - const bidderRequest = new BidderRequestBuilder().build(); - const requests = spec.buildRequests([bid01], bidderRequest); - expect(requests[0].data.adUnits[0].gpid).to.exist.and.equal(gpid); - }); - }); }); describe('interpretResponse()', function() { diff --git a/test/spec/modules/adfBidAdapter_spec.js b/test/spec/modules/adfBidAdapter_spec.js index c1acff522c0..88f595b6c00 100644 --- a/test/spec/modules/adfBidAdapter_spec.js +++ b/test/spec/modules/adfBidAdapter_spec.js @@ -1,5 +1,4 @@ // jshint esversion: 6, es3: false, node: true -/* eslint-disable no-console */ import { assert } from 'chai'; import { spec } from 'modules/adfBidAdapter.js'; import { config } from 'src/config.js'; @@ -447,52 +446,6 @@ 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 ]; - config.setConfig({ currency: { adServerCurrency: 'DKK' } }); - let validBidRequests = expectedFloors.map(getBidWithFloorTest); - getRequestImps(validBidRequests); - assert.deepEqual(result, { currency: 'DKK', size: '*', mediaType: '*' }); - - mediaTypes = { banner: { - sizes: [ [100, 200], [300, 400] ] - }}; - validBidRequests = expectedFloors.map(getBidWithFloorTest); - getRequestImps(validBidRequests); - - assert.deepEqual(result, { currency: 'DKK', size: '*', mediaType: '*' }); - - mediaTypes = { native: {} }; - validBidRequests = expectedFloors.map(getBidWithFloorTest); - getRequestImps(validBidRequests); - - assert.deepEqual(result, { currency: 'DKK', size: '*', mediaType: '*' }); - - mediaTypes = {}; - validBidRequests = expectedFloors.map(getBidWithFloorTest); - getRequestImps(validBidRequests); - - assert.deepEqual(result, { currency: 'DKK', size: '*', mediaType: '*' }); - - function getBidWithFloorTest(floor) { - return { - params: { mid: 1 }, - mediaTypes: mediaTypes, - getFloor: (args) => { - result = args; - return { - currency: 'DKK', - floor - }; - } - }; - } - }); - function getBidWithFloor(floor) { return { params: { mid: 1 }, diff --git a/test/spec/modules/adfusionBidAdapter_spec.js b/test/spec/modules/adfusionBidAdapter_spec.js deleted file mode 100644 index 82705b727b4..00000000000 --- a/test/spec/modules/adfusionBidAdapter_spec.js +++ /dev/null @@ -1,130 +0,0 @@ -import { expect } from 'chai'; -import { spec } from 'modules/adfusionBidAdapter'; -import 'modules/priceFloors.js'; -import 'modules/currency.js'; -import { newBidder } from 'src/adapters/bidderFactory'; - -describe('adfusionBidAdapter', 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 bid = { - bidder: 'adfusion', - params: { - accountId: 1234, - }, - adUnitCode: '/adunit-code/test-path', - bidId: 'test-bid-id-1', - bidderRequestId: 'test-bid-request-1', - auctionId: 'test-auction-1', - transactionId: 'test-transactionId-1', - }; - - it('should return true when required params are found', function () { - expect(spec.isBidRequestValid(bid)).to.equal(true); - }); - - it('should return false when params.accountID is missing', function () { - let localbid = Object.assign({}, bid); - delete localbid.params.accountId; - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - }); - - describe('buildRequests', function () { - let bidRequests, bannerBidRequest, bidderRequest; - beforeEach(function () { - bidRequests = [ - { - bidder: 'adfusion', - params: { - accountId: 1234, - }, - mediaTypes: { - banner: { - sizes: [ - [300, 250], - [300, 600], - ], - }, - }, - adUnitCode: '/adunit-code/test-path', - bidId: 'test-bid-id-1', - bidderRequestId: 'test-bid-request-1', - auctionId: 'test-auction-1', - transactionId: 'test-transactionId-1', - }, - { - bidder: 'adfusion', - params: { - accountId: 1234, - }, - adUnitCode: 'adunit-code', - mediaTypes: { - video: { - playerSize: [640, 480], - }, - }, - bidId: 'test-bid-id-2', - bidderRequestId: 'test-bid-request-2', - auctionId: 'test-auction-2', - transactionId: 'test-transactionId-2', - }, - ]; - bannerBidRequest = { - bidder: 'adfusion', - params: { - accountId: 1234, - }, - mediaTypes: { - banner: { - sizes: [ - [300, 250], - [300, 600], - ], - }, - }, - adUnitCode: '/adunit-code/test-path', - bidId: 'test-bid-id-1', - bidderRequestId: 'test-bid-request-1', - auctionId: 'test-auction-1', - transactionId: 'test-transactionId-1', - }; - bidderRequest = { refererInfo: {} }; - }); - - it('should return an empty array when no bid requests', function () { - const bidRequest = spec.buildRequests([], bidderRequest); - expect(bidRequest).to.be.an('array'); - expect(bidRequest.length).to.equal(0); - }); - - it('should return a valid bid request object', function () { - const request = spec.buildRequests(bidRequests, bidderRequest); - expect(request).to.be.an('array'); - expect(request[0].data).to.be.an('object'); - expect(request[0].method).to.equal('POST'); - expect(request[0].currency).to.not.equal('USD'); - expect(request[0].url).to.not.equal(''); - expect(request[0].url).to.not.equal(undefined); - expect(request[0].url).to.not.equal(null); - }); - - it('should add bid floor', function () { - let bidRequest = Object.assign({}, bannerBidRequest); - let payload = spec.buildRequests([bidRequest], bidderRequest)[0].data; - expect(payload.imp[0].bidfloorcur).to.not.exist; - - let getFloorResponse = { currency: 'USD', floor: 3 }; - bidRequest.getFloor = () => getFloorResponse; - payload = spec.buildRequests([bidRequest], bidderRequest)[0].data; - expect(payload.imp[0].bidfloor).to.equal(3); - expect(payload.imp[0].bidfloorcur).to.equal('USD'); - }); - }); -}); diff --git a/test/spec/modules/adgenerationBidAdapter_spec.js b/test/spec/modules/adgenerationBidAdapter_spec.js index adfd38d22cc..de8463731e0 100644 --- a/test/spec/modules/adgenerationBidAdapter_spec.js +++ b/test/spec/modules/adgenerationBidAdapter_spec.js @@ -184,12 +184,12 @@ describe('AdgenerationAdapter', function () { } }; const data = { - banner: `posall=SSPLOC&id=58278&sdktype=0&hb=true&t=json3&sizes=300x250%2C320x100¤cy=JPY&pbver=${prebid.version}&sdkname=prebidjs&adapterver=1.6.2&imark=1&tp=https%3A%2F%2Fexample.com`, - bannerUSD: `posall=SSPLOC&id=58278&sdktype=0&hb=true&t=json3&sizes=300x250%2C320x100¤cy=USD&pbver=${prebid.version}&sdkname=prebidjs&adapterver=1.6.2&imark=1&tp=https%3A%2F%2Fexample.com`, - native: `posall=SSPLOC&id=58278&sdktype=0&hb=true&t=json3&sizes=1x1¤cy=JPY&pbver=${prebid.version}&sdkname=prebidjs&adapterver=1.6.2&tp=https%3A%2F%2Fexample.com`, - bannerWithHyperId: `posall=SSPLOC&id=58278&sdktype=0&hb=true&t=json3&sizes=320x100¤cy=JPY&pbver=${prebid.version}&sdkname=prebidjs&adapterver=1.6.2&imark=1&tp=https%3A%2F%2Fexample.com&hyper_id=novatiqId`, - bannerWithAdgextCriteoId: `posall=SSPLOC&id=58278&sdktype=0&hb=true&t=json3&sizes=320x100¤cy=JPY&pbver=${prebid.version}&sdkname=prebidjs&adapterver=1.6.2&adgext_criteo_id=criteo-id-test-1234567890&imark=1&tp=https%3A%2F%2Fexample.com`, - bannerWithAdgextIds: `posall=SSPLOC&id=58278&sdktype=0&hb=true&t=json3&sizes=320x100¤cy=JPY&pbver=${prebid.version}&sdkname=prebidjs&adapterver=1.6.2&adgext_id5_id=id5-id-test-1234567890&adgext_id5_id_link_type=2&adgext_imuid=i.KrAH6ZAZTJOnH5S4N2sogA&adgext_uid2=AgAAAAVacu1uAxgAxH%2BHJ8%2BnWlS2H4uVqr6i%2BHBDCNREHD8WKsio%2Fx7D8xXFuq1cJycUU86yXfTH9Xe%2F4C8KkH%2B7UCiU7uQxhyD7Qxnv251pEs6K8oK%2BBPLYR%2B8BLY%2FsJKesa%2FkoKwx1FHgUzIBum582tSy2Oo%2B7C6wYUaaV4QcLr%2F4LPA%3D&gpid=%2F1111%2Fhomepage%23300x250&uach=%7B%22source%22%3A2%2C%22platform%22%3A%7B%22brand%22%3A%22macOS%22%7D%2C%22browsers%22%3A%5B%7B%22brand%22%3A%22Chromium%22%2C%22version%22%3A%5B%22112%22%5D%7D%2C%7B%22brand%22%3A%22Google%20Chrome%22%2C%22version%22%3A%5B%22112%22%5D%7D%2C%7B%22brand%22%3A%22Not%3AA-Brand%22%2C%22version%22%3A%5B%2299%22%5D%7D%5D%2C%22mobile%22%3A0%7D&schain=%7B%22ver%22%3A%221.0%22%2C%22complete%22%3A1%2C%22nodes%22%3A%5B%7B%22asi%22%3A%22indirectseller.com%22%2C%22sid%22%3A%2200001%22%2C%22hp%22%3A1%7D%5D%7D&imark=1&tp=https%3A%2F%2Fexample.com`, + banner: `posall=SSPLOC&id=58278&sdktype=0&hb=true&t=json3&sizes=300x250%2C320x100¤cy=JPY&pbver=${prebid.version}&sdkname=prebidjs&adapterver=1.6.0&imark=1&tp=https%3A%2F%2Fexample.com`, + bannerUSD: `posall=SSPLOC&id=58278&sdktype=0&hb=true&t=json3&sizes=300x250%2C320x100¤cy=USD&pbver=${prebid.version}&sdkname=prebidjs&adapterver=1.6.0&imark=1&tp=https%3A%2F%2Fexample.com`, + native: `posall=SSPLOC&id=58278&sdktype=0&hb=true&t=json3&sizes=1x1¤cy=JPY&pbver=${prebid.version}&sdkname=prebidjs&adapterver=1.6.0&tp=https%3A%2F%2Fexample.com`, + bannerWithHyperId: `posall=SSPLOC&id=58278&sdktype=0&hb=true&t=json3&sizes=320x100¤cy=JPY&pbver=${prebid.version}&sdkname=prebidjs&adapterver=1.6.0&imark=1&tp=https%3A%2F%2Fexample.com&hyper_id=novatiqId`, + bannerWithAdgextCriteoId: `posall=SSPLOC&id=58278&sdktype=0&hb=true&t=json3&sizes=320x100¤cy=JPY&pbver=${prebid.version}&sdkname=prebidjs&adapterver=1.6.0&adgext_criteo_id=criteo-id-test-1234567890&imark=1&tp=https%3A%2F%2Fexample.com`, + bannerWithAdgextIds: `posall=SSPLOC&id=58278&sdktype=0&hb=true&t=json3&sizes=320x100¤cy=JPY&pbver=${prebid.version}&sdkname=prebidjs&adapterver=1.6.0&adgext_id5_id=id5-id-test-1234567890&adgext_id5_id_link_type=2&adgext_imuid=i.KrAH6ZAZTJOnH5S4N2sogA&adgext_uid2=%5Bobject%20Object%5D&gpid=%252F1111%252Fhomepage%2523300x250&uach=%7B%22source%22%3A2%2C%22platform%22%3A%7B%22brand%22%3A%22macOS%22%7D%2C%22browsers%22%3A%5B%7B%22brand%22%3A%22Chromium%22%2C%22version%22%3A%5B%22112%22%5D%7D%2C%7B%22brand%22%3A%22Google%20Chrome%22%2C%22version%22%3A%5B%22112%22%5D%7D%2C%7B%22brand%22%3A%22Not%3AA-Brand%22%2C%22version%22%3A%5B%2299%22%5D%7D%5D%2C%22mobile%22%3A0%7D&schain=%257B%2522ver%2522%253A%25221.0%2522%252C%2522complete%2522%253A1%252C%2522nodes%2522%253A%255B%257B%2522asi%2522%253A%2522indirectseller.com%2522%252C%2522sid%2522%253A%252200001%2522%252C%2522hp%2522%253A1%257D%255D%257D&imark=1&tp=https%3A%2F%2Fexample.com`, }; it('sends bid request to ENDPOINT via GET', function () { const request = spec.buildRequests(bidRequests, bidderRequest)[0]; diff --git a/test/spec/modules/adkernelAdnBidAdapter_spec.js b/test/spec/modules/adkernelAdnBidAdapter_spec.js index cfee5693cf5..ff7ed9f145d 100644 --- a/test/spec/modules/adkernelAdnBidAdapter_spec.js +++ b/test/spec/modules/adkernelAdnBidAdapter_spec.js @@ -426,7 +426,8 @@ describe('AdkernelAdn adapter', function () { describe('adapter configuration', () => { it('should have aliases', () => { - expect(spec.aliases).to.be.an('array'); + expect(spec.aliases).to.have.lengthOf(1); + expect(spec.aliases[0]).to.be.equal('engagesimply'); }); }); }); diff --git a/test/spec/modules/adkernelBidAdapter_spec.js b/test/spec/modules/adkernelBidAdapter_spec.js index ade34478c20..45498d2734a 100644 --- a/test/spec/modules/adkernelBidAdapter_spec.js +++ b/test/spec/modules/adkernelBidAdapter_spec.js @@ -15,13 +15,11 @@ describe('Adkernel adapter', function () { auctionId: 'auc-001', mediaTypes: { banner: { - sizes: [[300, 250], [300, 200]], - pos: 1 + sizes: [[300, 250], [300, 200]] } }, ortb2Imp: { - battr: [6, 7, 9], - pos: 2 + battr: [6, 7, 9] } }, bid2_zone2 = { bidder: 'adkernel', @@ -105,11 +103,7 @@ describe('Adkernel adapter', function () { video: { context: 'instream', playerSize: [[640, 480]], - api: [1, 2], - placement: 1, - plcmt: 1, - skip: 1, - pos: 1 + api: [1, 2] } }, adUnitCode: 'ad-unit-1' @@ -250,31 +244,6 @@ describe('Adkernel adapter', function () { }], bidid: 'pTuOlf5KHUo', cur: 'EUR' - }, - multiformat_response = { - id: '47ce4badcf7482', - seatbid: [{ - bid: [{ - id: 'sZSYq5zYMxo_0', - impid: 'Bid_01b__mf', - crid: '100_003', - price: 0.00145, - adid: '158801', - adm: '', - nurl: 'https://rtb.com/win?i=sZSYq5zYMxo_0&f=nurl', - cid: '16855' - }, { - id: 'sZSYq5zYMxo_1', - impid: 'Bid_01v__mf', - crid: '100_003', - price: 0.25, - adid: '158801', - nurl: 'https://rtb.com/win?i=sZSYq5zYMxo_1&f=nurl', - cid: '16855' - }] - }], - bidid: 'pTuOlf5KHUo', - cur: 'USD' }; var sandbox; @@ -377,11 +346,6 @@ describe('Adkernel adapter', function () { expect(bidRequest.imp[0].banner.battr).to.be.eql([6, 7, 9]); }); - it('should respect mediatypes attributes over FPD', function() { - expect(bidRequest.imp[0].banner).to.have.property('pos'); - expect(bidRequest.imp[0].banner.pos).to.be.eql(1); - }); - it('shouldn\'t contain gdpr nor ccpa information for default request', function () { let [_, bidRequests] = buildRequest([bid1_zone1]); expect(bidRequests[0]).to.not.have.property('regs'); @@ -390,16 +354,11 @@ describe('Adkernel adapter', function () { it('should contain gdpr-related information if consent is configured', function () { let [_, bidRequests] = buildRequest([bid1_zone1], - buildBidderRequest('https://example.com/index.html', { - gdprConsent: {gdprApplies: true, consentString: 'test-consent-string', vendorData: {}}, - uspConsent: '1YNN', - gppConsent: {gppString: 'DBABMA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA', applicableSections: [2]}} - )); + buildBidderRequest('https://example.com/index.html', + {gdprConsent: {gdprApplies: true, consentString: 'test-consent-string', vendorData: {}}, uspConsent: '1YNN'})); let bidRequest = bidRequests[0]; expect(bidRequest).to.have.property('regs'); 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'}); }); @@ -474,40 +433,24 @@ describe('Adkernel adapter', function () { }); it('should have openrtb video impression parameters', function() { - let video = bidRequests[0].imp[0].video; - expect(video).to.have.property('api'); - expect(video.api).to.be.eql([1, 2]); - expect(video.placement).to.be.eql(1); - expect(video.plcmt).to.be.eql(1); - expect(video.skip).to.be.eql(1); - expect(video.pos).to.be.eql(1); + expect(bidRequests[0].imp[0].video).to.have.property('api'); + expect(bidRequests[0].imp[0].video.api).to.be.eql([1, 2]); }); }); describe('multiformat request building', function () { - let pbRequests, bidRequests; + let _, bidRequests; before(function () { - [pbRequests, bidRequests] = buildRequest([bid_multiformat]); + [_, bidRequests] = buildRequest([bid_multiformat]); }); it('should contain single request', function () { expect(bidRequests).to.have.length(1); + expect(bidRequests[0].imp).to.have.length(1); }); - it('should contain both impression', function () { - expect(bidRequests[0].imp).to.have.length(2); + it('should contain banner-only impression', function () { + expect(bidRequests[0].imp).to.have.length(1); expect(bidRequests[0].imp[0]).to.have.property('banner'); - expect(bidRequests[0].imp[1]).to.have.property('video'); - // check that splitted imps do not share same impid - expect(bidRequests[0].imp[0].id).to.be.not.eql('Bid_01'); - expect(bidRequests[0].imp[1].id).to.be.not.eql('Bid_01'); - expect(bidRequests[0].imp[1].id).to.be.not.eql(bidRequests[0].imp[0].id); - }); - it('x', function() { - let 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'); - expect(bids[1].requestId).to.be.eql('Bid_01'); - expect(bids[1].mediaType).to.be.eql('video'); + expect(bidRequests[0].imp[0]).to.not.have.property('video'); }); }); diff --git a/test/spec/modules/adlooxAdServerVideo_spec.js b/test/spec/modules/adlooxAdServerVideo_spec.js index 58277bc830d..a071c6bbe3f 100644 --- a/test/spec/modules/adlooxAdServerVideo_spec.js +++ b/test/spec/modules/adlooxAdServerVideo_spec.js @@ -1,11 +1,11 @@ import adapterManager from 'src/adapterManager.js'; import analyticsAdapter from 'modules/adlooxAnalyticsAdapter.js'; +import { ajax } from 'src/ajax.js'; import { buildVideoUrl } from 'modules/adlooxAdServerVideo.js'; 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'; const analyticsAdapterName = 'adloox'; @@ -199,9 +199,11 @@ describe('Adloox Ad Server Video', function () { }); describe('process VAST', function () { + let server = null; let BID = null; let getWinningBidsStub; beforeEach(function () { + server = sinon.createFakeServer(); BID = utils.deepClone(bid); getWinningBidsStub = sinon.stub(targeting, 'getWinningBids') getWinningBidsStub.withArgs(adUnit.code).returns([ BID ]); @@ -210,6 +212,8 @@ describe('Adloox Ad Server Video', function () { getWinningBidsStub.restore(); getWinningBidsStub = undefined; BID = null; + server.restore(); + server = null; }); it('should return URL unchanged for non-VAST', function (done) { diff --git a/test/spec/modules/adlooxRtdProvider_spec.js b/test/spec/modules/adlooxRtdProvider_spec.js index 0e26ef1afdb..5b99789981f 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 { config as _config } from 'src/config.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'; const analyticsAdapterName = 'adloox'; @@ -139,12 +139,16 @@ describe('Adloox RTD Provider', function () { expect(analyticsAdapter.context).is.null; }); + let server = null; let CONFIG = null; beforeEach(function () { + server = sinon.createFakeServer(); CONFIG = utils.deepClone(config); }); afterEach(function () { CONFIG = null; + server.restore(); + server = null; }); it('should fetch segments', function (done) { diff --git a/test/spec/modules/admanBidAdapter_spec.js b/test/spec/modules/admanBidAdapter_spec.js index a9413860072..f1d9a7047d3 100644 --- a/test/spec/modules/admanBidAdapter_spec.js +++ b/test/spec/modules/admanBidAdapter_spec.js @@ -106,11 +106,8 @@ describe('AdmanAdapter', function () { let placements = data['placements']; for (let i = 0; i < placements.length; i++) { let placement = placements[i]; - expect(placement).to.have.all.keys('placementId', 'eids', 'bidId', 'traffic', 'sizes', 'schain', 'bidFloor', 'ext'); + expect(placement).to.have.all.keys('placementId', 'eids', 'bidId', 'traffic', 'sizes', 'schain', 'bidFloor'); expect(placement.schain).to.be.an('object') - expect(placement.ext).to.be.an('object') - expect(placement.ext).to.have.key('tid') - expect(placement.ext.tid).to.equal(bidBanner.transactionId); expect(placement.placementId).to.be.a('number'); expect(placement.bidId).to.be.a('string'); expect(placement.traffic).to.be.a('string'); @@ -135,10 +132,7 @@ describe('AdmanAdapter', function () { let placement = placements[i]; expect(placement).to.have.all.keys('placementId', 'eids', 'bidId', 'traffic', 'schain', 'bidFloor', 'playerSize', 'minduration', 'maxduration', 'mimes', 'protocols', 'startdelay', 'placement', 'skip', - 'skipafter', 'minbitrate', 'maxbitrate', 'delivery', 'playbackmethod', 'api', 'linearity', 'ext'); - expect(placement.ext).to.be.an('object') - expect(placement.ext).to.have.key('tid') - expect(placement.ext.tid).to.equal(bidBanner.transactionId); + 'skipafter', 'minbitrate', 'maxbitrate', 'delivery', 'playbackmethod', 'api', 'linearity'); expect(placement.schain).to.be.an('object') expect(placement.placementId).to.be.a('number'); expect(placement.bidId).to.be.a('string'); diff --git a/test/spec/modules/admaticBidAdapter_spec.js b/test/spec/modules/admaticBidAdapter_spec.js index b4d84634962..1d2fb1e79cb 100644 --- a/test/spec/modules/admaticBidAdapter_spec.js +++ b/test/spec/modules/admaticBidAdapter_spec.js @@ -1,553 +1,12 @@ -import { expect } from 'chai'; -import { spec } from 'modules/admaticBidAdapter.js'; -import { newBidder } from 'src/adapters/bidderFactory.js'; -import { config } from 'src/config.js'; +import {expect} from 'chai'; +import {spec, storage} from 'modules/admaticBidAdapter.js'; +import {newBidder} from 'src/adapters/bidderFactory.js'; +import {getStorageManager} from 'src/storageManager'; const ENDPOINT = 'https://layer.serve.admatic.com.tr/pb'; describe('admaticBidAdapter', () => { const adapter = newBidder(spec); - let validRequest = [ { - 'refererInfo': { - 'page': 'https://www.admatic.com.tr', - 'domain': 'https://www.admatic.com.tr', - }, - 'bidder': 'admatic', - 'params': { - 'networkId': 10433394, - 'host': 'layer.serve.admatic.com.tr' - }, - 'ortb2Imp': { 'ext': { 'instl': 1 } }, - 'ortb2': { 'badv': ['admatic.com.tr'] }, - 'mediaTypes': { - 'banner': { - 'sizes': [[300, 250], [728, 90]] - }, - 'native': { - }, - 'video': { - } - }, - getFloor: inputParams => { - if (inputParams.mediaType === BANNER && inputParams.size[0] === 300 && inputParams.size[1] === 250) { - return { - currency: 'USD', - floor: 1.0 - }; - } else if (inputParams.mediaType === BANNER && inputParams.size[0] === 728 && inputParams.size[1] === 90) { - return { - currency: 'USD', - floor: 2.0 - }; - } else if (inputParams.mediaType === VIDEO) { - return { - currency: 'USD', - floor: 1.0 - }; - } else if (inputParams.mediaType === NATIVE) { - return { - currency: 'USD', - floor: 1.0 - }; - } else { - return {} - } - }, - 'schain': { - 'ver': '1.0', - 'complete': 1, - 'nodes': [ - { - 'asi': 'pixad.com.tr', - 'sid': 'px-pub-3000856707', - 'hp': 1 - } - ] - }, - 'at': 1, - 'tmax': 1000, - 'user': { - 'ext': { - 'eids': [ - { - 'source': 'id5-sync.com', - 'uids': [ - { - 'id': '0', - 'atype': 1, - 'ext': { - 'linkType': 0, - 'pba': 'wMh3sAXcnhDq7CfSa6ji1g==' - } - } - ] - }, - { - 'source': 'pubcid.org', - 'uids': [ - { - 'id': '5a49273f-a424-454b-b478-169c3551aa72', - 'atype': 1 - } - ] - } - ] - } - }, - 'ortb': { - 'badv': [], - 'bcat': [], - 'site': { - 'page': 'http://localhost:8888/admatic.html', - 'ref': 'http://localhost:8888', - 'publisher': { - 'name': 'localhost' - } - }, - 'device': { - 'ua': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36' - } - }, - 'site': { - 'page': 'http://localhost:8888/admatic.html', - 'ref': 'http://localhost:8888', - 'publisher': { - 'name': 'localhost', - 'publisherId': 12321312 - } - }, - 'imp': [ - { - 'size': [ - { - 'w': 300, - 'h': 250 - }, - { - 'w': 728, - 'h': 90 - } - ], - 'mediatype': {}, - 'type': 'banner', - 'id': '2205da7a81846b', - 'floors': { - 'banner': { - '300x250': { 'currency': 'USD', 'floor': 1 }, - '728x90': { 'currency': 'USD', 'floor': 2 } - } - } - }, - { - 'size': [ - { - 'w': 338, - 'h': 280 - } - ], - 'type': 'video', - 'mediatype': { - 'context': 'instream', - 'mimes': [ - 'video/mp4' - ], - 'maxduration': 240, - 'api': [ - 1, - 2 - ], - 'playerSize': [ - [ - 338, - 280 - ] - ], - 'protocols': [ - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8 - ], - 'skip': 1, - 'playbackmethod': [ - 2 - ], - 'linearity': 1, - 'placement': 2 - }, - 'floors': { - 'video': { - '338x280': { 'currency': 'USD', 'floor': 1 } - } - }, - 'id': '45e86fc7ce7fc93' - }, - { - 'size': [ - { - 'w': 1, - 'h': 1 - } - ], - 'type': 'native', - 'mediatype': { - 'title': { - 'required': true, - 'len': 120 - }, - 'image': { - 'required': true - }, - 'icon': { - 'required': false, - 'sizes': [ - 640, - 480 - ] - }, - 'sponsoredBy': { - 'required': false - }, - 'body': { - 'required': false - }, - 'clickUrl': { - 'required': false - }, - 'displayUrl': { - 'required': false - } - }, - 'ext': { - 'instl': 0, - 'gpid': 'native-INS_b1b1269f-9570-fe3c-9bf4-f187827ec94a', - 'data': { - 'pbadslot': 'native-INS_b1b1269f-9570-fe3c-9bf4-f187827ec94a' - } - }, - 'floors': { - 'native': { - '*': { 'currency': 'USD', 'floor': 1 } - } - }, - 'id': '16e0c8982318f91' - } - ], - 'ext': { - 'cur': 'USD', - 'bidder': 'admatic' - } - } ]; - let bidderRequest = { - 'refererInfo': { - 'page': 'https://www.admatic.com.tr', - 'domain': 'https://www.admatic.com.tr', - }, - 'bidder': 'admatic', - 'params': { - 'networkId': 10433394, - 'host': 'layer.serve.admatic.com.tr' - }, - 'ortb2Imp': { 'ext': { 'instl': 1 } }, - 'ortb2': { 'badv': ['admatic.com.tr'] }, - 'mediaTypes': { - 'banner': { - 'sizes': [[300, 250], [728, 90]] - }, - 'native': { - }, - 'video': { - 'playerSize': [ - 336, - 280 - ] - } - }, - 'userId': { - 'id5id': { - 'uid': '0', - 'ext': { - 'linkType': 0, - 'pba': 'wMh3sAXcnhDq7CfSa6ji1g==' - } - }, - 'pubcid': '5a49273f-a424-454b-b478-169c3551aa72' - }, - 'userIdAsEids': [ - { - 'source': 'id5-sync.com', - 'uids': [ - { - 'id': '0', - 'atype': 1, - 'ext': { - 'linkType': 0, - 'pba': 'wMh3sAXcnhDq7CfSa6ji1g==' - } - } - ] - }, - { - 'source': 'pubcid.org', - 'uids': [ - { - 'id': '5a49273f-a424-454b-b478-169c3551aa72', - 'atype': 1 - } - ] - } - ], - getFloor: inputParams => { - if (inputParams.mediaType === BANNER && inputParams.size[0] === 300 && inputParams.size[1] === 250) { - return { - currency: 'USD', - floor: 1.0 - }; - } else if (inputParams.mediaType === BANNER && inputParams.size[0] === 728 && inputParams.size[1] === 90) { - return { - currency: 'USD', - floor: 2.0 - }; - } else if (inputParams.mediaType === VIDEO) { - return { - currency: 'USD', - floor: 1.0 - }; - } else if (inputParams.mediaType === NATIVE) { - return { - currency: 'USD', - floor: 1.0 - }; - } else { - return {} - } - }, - 'schain': { - 'ver': '1.0', - 'complete': 1, - 'nodes': [ - { - 'asi': 'pixad.com.tr', - 'sid': 'px-pub-3000856707', - 'hp': 1 - } - ] - }, - 'at': 1, - 'tmax': 1000, - 'user': { - 'ext': { - 'eids': [ - { - 'source': 'id5-sync.com', - 'uids': [ - { - 'id': '0', - 'atype': 1, - 'ext': { - 'linkType': 0, - 'pba': 'wMh3sAXcnhDq7CfSa6ji1g==' - } - } - ] - }, - { - 'source': 'pubcid.org', - 'uids': [ - { - 'id': '5a49273f-a424-454b-b478-169c3551aa72', - 'atype': 1 - } - ] - } - ] - } - }, - 'ortb': { - 'source': {}, - 'site': { - 'domain': 'localhost:8888', - 'publisher': { - 'domain': 'localhost:8888' - }, - 'page': 'http://localhost:8888/', - 'name': 'http://localhost:8888' - }, - 'badv': [], - 'bcat': [], - 'device': { - 'w': 896, - 'h': 979, - 'dnt': 0, - 'ua': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36', - 'language': 'tr', - 'sua': { - 'source': 1, - 'platform': { - 'brand': 'macOS' - }, - 'browsers': [ - { - 'brand': 'Google Chrome', - 'version': [ - '119' - ] - }, - { - 'brand': 'Chromium', - 'version': [ - '119' - ] - }, - { - 'brand': 'Not?A_Brand', - 'version': [ - '24' - ] - } - ], - 'mobile': 0 - } - } - }, - 'site': { - 'page': 'http://localhost:8888/admatic.html', - 'ref': 'http://localhost:8888', - 'publisher': { - 'name': 'localhost', - 'publisherId': 12321312 - } - }, - 'imp': [ - { - 'size': [ - { - 'w': 300, - 'h': 250 - }, - { - 'w': 728, - 'h': 90 - } - ], - 'id': '2205da7a81846b', - 'mediatype': {}, - 'type': 'banner', - 'floors': { - 'banner': { - '300x250': { 'currency': 'USD', 'floor': 1 }, - '728x90': { 'currency': 'USD', 'floor': 2 } - } - } - }, - { - 'size': [ - { - 'w': 338, - 'h': 280 - } - ], - 'type': 'video', - 'mediatype': { - 'context': 'instream', - 'mimes': [ - 'video/mp4' - ], - 'maxduration': 240, - 'api': [ - 1, - 2 - ], - 'playerSize': [ - [ - 338, - 280 - ] - ], - 'protocols': [ - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8 - ], - 'skip': 1, - 'playbackmethod': [ - 2 - ], - 'linearity': 1, - 'placement': 2 - }, - 'floors': { - 'video': { - '338x280': { 'currency': 'USD', 'floor': 1 } - } - }, - 'id': '45e86fc7ce7fc93' - }, - { - 'size': [ - { - 'w': 1, - 'h': 1 - } - ], - 'type': 'native', - 'mediatype': { - 'title': { - 'required': true, - 'len': 120 - }, - 'image': { - 'required': true - }, - 'icon': { - 'required': false, - 'sizes': [ - 640, - 480 - ] - }, - 'sponsoredBy': { - 'required': false - }, - 'body': { - 'required': false - }, - 'clickUrl': { - 'required': false - }, - 'displayUrl': { - 'required': false - } - }, - 'ext': { - 'instl': 0, - 'gpid': 'native-INS_b1b1269f-9570-fe3c-9bf4-f187827ec94a', - 'data': { - 'pbadslot': 'native-INS_b1b1269f-9570-fe3c-9bf4-f187827ec94a' - } - }, - 'floors': { - 'native': { - '*': { 'currency': 'USD', 'floor': 1 } - } - }, - 'id': '16e0c8982318f91' - } - ], - 'ext': { - 'cur': 'USD', - 'bidder': 'admatic' - } - }; describe('inherited functions', () => { it('exists and is a function', () => { @@ -557,10 +16,6 @@ describe('admaticBidAdapter', () => { describe('isBidRequestValid', function() { let bid = { - 'refererInfo': { - 'page': 'https://www.admatic.com.tr', - 'domain': 'https://www.admatic.com.tr', - }, 'bidder': 'admatic', 'params': { 'networkId': 10433394, @@ -573,7 +28,6 @@ describe('admaticBidAdapter', () => { 'bidderRequestId': '22edbae2733bf6', 'auctionId': '1d1a030790a475', 'creativeId': 'er2ee', - 'ortb2Imp': { 'ext': { 'instl': 1 } }, 'ortb2': { 'badv': ['admatic.com.tr'] } }; @@ -592,147 +46,252 @@ describe('admaticBidAdapter', () => { describe('buildRequests', function () { it('sends bid request to ENDPOINT via POST', function () { - const request = spec.buildRequests(validRequest, bidderRequest); - expect(request.url).to.equal(ENDPOINT); - expect(request.method).to.equal('POST'); - }); - - it('should not populate GDPR if for non-EEA users', function () { - let bidRequest = Object.assign([], validRequest); - const request = spec.buildRequests( - bidRequest, - Object.assign({}, bidderRequest, { - gdprConsent: { - gdprApplies: true, - consentString: 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A==' + let validRequest = [ { + 'bidder': 'admatic', + 'params': { + 'networkId': 10433394, + 'host': 'layer.serve.admatic.com.tr' + }, + 'ortb2': { 'badv': ['admatic.com.tr'] }, + 'mediaTypes': { + 'banner': { + 'sizes': [[300, 250], [728, 90]] } - }) - ); - expect(request.data.regs.ext.gdpr).to.equal(1); - expect(request.data.regs.ext.consent).to.equal('BOJ8RZsOJ8RZsABAB8AAAAAZ-A'); - }); - - it('should populate GDPR and empty consent string if available for EEA users without consent string but with consent', function () { - let bidRequest = Object.assign([], validRequest); - const request = spec.buildRequests( - bidRequest, - Object.assign({}, bidderRequest, { - gdprConsent: { - gdprApplies: true + }, + getFloor: inputParams => { + if (inputParams.mediaType === BANNER && inputParams.size[0] === 300 && inputParams.size[1] === 250) { + return { + currency: 'USD', + floor: 1.0 + }; + } else if (inputParams.mediaType === BANNER && inputParams.size[0] === 728 && inputParams.size[1] === 90) { + return { + currency: 'USD', + floor: 2.0 + }; + } else { + return {} } - }) - ); - expect(request.data.regs.ext.gdpr).to.equal(1); - expect(request.data.regs.ext.consent).to.equal(''); - }); - - it('should properly build a request when coppa flag is true', function () { - let bidRequest = Object.assign([], validRequest); - const request = spec.buildRequests( - bidRequest, - Object.assign({}, bidderRequest, { - coppa: true - }) - ); - expect(request.data.regs.ext.coppa).to.not.be.undefined; - expect(request.data.regs.ext.coppa).to.equal(1); - }); - - it('should properly build a request with gpp consent field', function () { - let bidRequest = Object.assign([], validRequest); - const ortb2 = { - regs: { - gpp: 'gpp_consent_string', - gpp_sid: [0, 1, 2] - } - }; - const request = spec.buildRequests(bidRequest, { ...bidderRequest, ortb2 }); - expect(request.data.regs.ext.gpp).to.equal('gpp_consent_string'); - expect(request.data.regs.ext.gpp_sid).to.deep.equal([0, 1, 2]); - }); - - it('should properly build a request with ccpa consent field', function () { - let bidRequest = Object.assign([], validRequest); - const request = spec.buildRequests( - bidRequest, - Object.assign({}, bidderRequest, { - uspConsent: '1---' - }) - ); - expect(request.data.regs.ext.uspIab).to.not.be.null; - expect(request.data.regs.ext.uspIab).to.equal('1---'); - }); - - it('should properly forward eids', function () { - const bidRequests = [ - { - bidder: 'admatic', - adUnitCode: 'bid-123', - transactionId: 'transaction-123', - mediaTypes: { - banner: { - sizes: [[728, 90]] + }, + 'user': { + 'ua': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36' + }, + 'blacklist': [], + 'site': { + 'page': 'http://localhost:8888/admatic.html', + 'ref': 'http://localhost:8888', + 'publisher': { + 'name': 'localhost', + 'publisherId': 12321312 + } + }, + 'imp': [ + { + 'size': [ + { + 'w': 300, + 'h': 250 + }, + { + 'w': 728, + 'h': 90 + } + ], + 'mediatype': {}, + 'type': 'banner', + 'id': '2205da7a81846b', + 'floors': { + 'banner': { + '300x250': { 'currency': 'USD', 'floor': 1 }, + '728x90': { 'currency': 'USD', 'floor': 2 } + } } }, - userIdAsEids: [ - { - source: 'admatic.com.tr', - uids: [{ - id: 'abc', - atype: 1 - }] - } - ], - params: {} - }, - ]; - const request = spec.buildRequests(bidRequests, bidderRequest); - const ortbRequest = request.data; - expect(ortbRequest.user.ext.eids).to.deep.equal([ - { - source: 'admatic.com.tr', - uids: [{ - id: 'abc', - atype: 1 - }] + { + 'size': [ + { + 'w': 338, + 'h': 280 + } + ], + 'type': 'video', + 'mediatype': { + 'context': 'instream', + 'mimes': [ + 'video/mp4' + ], + 'maxduration': 240, + 'api': [ + 1, + 2 + ], + 'playerSize': [ + [ + 338, + 280 + ] + ], + 'protocols': [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8 + ], + 'skip': 1, + 'playbackmethod': [ + 2 + ], + 'linearity': 1, + 'placement': 2 + }, + 'id': '45e86fc7ce7fc93' + } + ], + 'ext': { + 'cur': 'USD', + 'bidder': 'admatic' } - ]); - }); - - it('should properly build a banner request with floors', function () { - const request = spec.buildRequests(validRequest, bidderRequest); - request.data.imp[0].floors = { - 'banner': { - '300x250': { 'currency': 'USD', 'floor': 1 }, - '728x90': { 'currency': 'USD', 'floor': 2 } + } ]; + let bidderRequest = { + 'bidder': 'admatic', + 'params': { + 'networkId': 10433394, + 'host': 'layer.serve.admatic.com.tr' + }, + 'ortb2': { 'badv': ['admatic.com.tr'] }, + 'mediaTypes': { + 'banner': { + 'sizes': [[300, 250], [728, 90]] + } + }, + getFloor: inputParams => { + if (inputParams.mediaType === BANNER && inputParams.size[0] === 300 && inputParams.size[1] === 250) { + return { + currency: 'USD', + floor: 1.0 + }; + } else if (inputParams.mediaType === BANNER && inputParams.size[0] === 728 && inputParams.size[1] === 90) { + return { + currency: 'USD', + floor: 2.0 + }; + } else { + return {} + } + }, + 'user': { + 'ua': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36' + }, + 'blacklist': [], + 'site': { + 'page': 'http://localhost:8888/admatic.html', + 'ref': 'http://localhost:8888', + 'publisher': { + 'name': 'localhost', + 'publisherId': 12321312 + } + }, + 'imp': [ + { + 'size': [ + { + 'w': 300, + 'h': 250 + }, + { + 'w': 728, + 'h': 90 + } + ], + 'id': '2205da7a81846b', + 'mediatype': {}, + 'type': 'banner', + 'floors': { + 'banner': { + '300x250': { 'currency': 'USD', 'floor': 1 }, + '728x90': { 'currency': 'USD', 'floor': 2 } + } + } + }, + { + 'size': [ + { + 'w': 338, + 'h': 280 + } + ], + 'type': 'video', + 'mediatype': { + 'context': 'instream', + 'mimes': [ + 'video/mp4' + ], + 'maxduration': 240, + 'api': [ + 1, + 2 + ], + 'playerSize': [ + [ + 338, + 280 + ] + ], + 'protocols': [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8 + ], + 'skip': 1, + 'playbackmethod': [ + 2 + ], + 'linearity': 1, + 'placement': 2 + }, + 'id': '45e86fc7ce7fc93' + } + ], + 'ext': { + 'cur': 'USD', + 'bidder': 'admatic' } }; + const request = spec.buildRequests(validRequest, bidderRequest); + expect(request.url).to.equal(ENDPOINT); + expect(request.method).to.equal('POST'); }); - it('should properly build a video request with several player sizes with floors', function () { - const bidRequests = [ + it('should properly build a banner request with floors', function () { + let bidRequests = [ { 'bidder': 'admatic', - 'adUnitCode': 'bid-123', - 'transactionId': 'transaction-123', - 'mediaTypes': { - 'video': { - 'playerSize': [[300, 250], [728, 90]] - } - }, - 'ortb2Imp': { 'ext': { 'instl': 1 } }, - 'ortb2': { 'badv': ['admatic.com.tr'] }, 'params': { 'networkId': 10433394, 'host': 'layer.serve.admatic.com.tr' }, + 'mediaTypes': { + 'banner': { + 'sizes': [[300, 250], [728, 90]] + } + }, + 'ortb2': { 'badv': ['admatic.com.tr'] }, getFloor: inputParams => { - if (inputParams.mediaType === VIDEO && inputParams.size[0] === 300 && inputParams.size[1] === 250) { + if (inputParams.mediaType === BANNER && inputParams.size[0] === 300 && inputParams.size[1] === 250) { return { currency: 'USD', floor: 1.0 }; - } else if (inputParams.mediaType === VIDEO && inputParams.size[0] === 728 && inputParams.size[1] === 90) { + } else if (inputParams.mediaType === BANNER && inputParams.size[0] === 728 && inputParams.size[1] === 90) { return { currency: 'USD', floor: 2.0 @@ -743,50 +302,32 @@ describe('admaticBidAdapter', () => { } }, ]; - const bidderRequest = { - 'refererInfo': { - 'page': 'https://www.admatic.com.tr', - 'domain': 'https://www.admatic.com.tr', + let bidderRequest = { + 'bidder': 'admatic', + 'params': { + 'networkId': 10433394, + 'host': 'layer.serve.admatic.com.tr' + }, + 'ortb2': { 'badv': ['admatic.com.tr'] }, + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 250], [728, 90]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + 'creativeId': 'er2ee', + 'mediaTypes': { + 'banner': { + 'sizes': [[300, 250], [728, 90]] + } } }; const request = spec.buildRequests(bidRequests, bidderRequest); - }); - - it('should properly build a native request with floors', function () { - const bidRequests = [ - { - 'bidder': 'admatic', - 'adUnitCode': 'bid-123', - 'transactionId': 'transaction-123', - 'mediaTypes': { - 'native': { - } - }, - 'ortb2Imp': { 'ext': { 'instl': 1 } }, - 'ortb2': { 'badv': ['admatic.com.tr'] }, - 'params': { - 'networkId': 10433394, - 'host': 'layer.serve.admatic.com.tr' - }, - getFloor: inputParams => { - if (inputParams.mediaType === NATIVE) { - return { - currency: 'USD', - floor: 1.0 - }; - } else { - return {} - } - } - }, - ]; - const bidderRequest = { - 'refererInfo': { - 'page': 'https://www.admatic.com.tr', - 'domain': 'https://www.admatic.com.tr', + request.data.imp[0].floors = { + 'banner': { + '300x250': { 'currency': 'USD', 'floor': 1 }, + '728x90': { 'currency': 'USD', 'floor': 2 } } }; - const request = spec.buildRequests(bidRequests, bidderRequest); }); }); @@ -802,7 +343,6 @@ describe('admaticBidAdapter', () => { 'price': 0.01, 'type': 'banner', 'bidder': 'admatic', - 'mime_type': 'iframe', 'adomain': ['admatic.com.tr'], 'party_tag': '
', 'iurl': 'https://www.admatic.com.tr' @@ -814,7 +354,6 @@ describe('admaticBidAdapter', () => { 'height': 250, 'price': 0.01, 'type': 'video', - 'mime_type': 'iframe', 'bidder': 'admatic', 'adomain': ['admatic.com.tr'], 'party_tag': '', @@ -827,24 +366,10 @@ describe('admaticBidAdapter', () => { 'height': 250, 'price': 0.01, 'type': 'video', - 'mime_type': 'iframe', 'bidder': 'admatic', 'adomain': ['admatic.com.tr'], 'party_tag': 'https://www.admatic.com.tr', 'iurl': 'https://www.admatic.com.tr' - }, - { - 'id': 4, - 'creative_id': '3742', - 'width': 1, - 'height': 1, - 'price': 0.01, - 'type': 'native', - 'mime_type': 'iframe', - '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' } ], 'queryId': 'cdnbh24rlv0hhkpfpln0', @@ -863,7 +388,6 @@ describe('admaticBidAdapter', () => { ad: '
', creativeId: '374', meta: { - model: 'iframe', advertiserDomains: ['admatic.com.tr'] }, ttl: 60, @@ -881,7 +405,6 @@ describe('admaticBidAdapter', () => { vastXml: '', creativeId: '3741', meta: { - model: 'iframe', advertiserDomains: ['admatic.com.tr'] }, ttl: 60, @@ -899,41 +422,6 @@ describe('admaticBidAdapter', () => { vastXml: 'https://www.admatic.com.tr', creativeId: '3741', meta: { - model: 'iframe', - advertiserDomains: ['admatic.com.tr'] - }, - ttl: 60, - bidder: 'admatic' - }, - { - requestId: 4, - cpm: 0.01, - width: 1, - height: 1, - currency: 'TRY', - mediaType: 'native', - netRevenue: true, - native: { - 'clickUrl': 'https://www.admatic.com.tr', - 'impressionTrackers': ['https://www.admatic.com.tr'], - 'title': 'title', - 'body': 'body', - 'sponsoredBy': 'sponsored', - 'cta': 'cta', - 'image': { - 'url': 'https://www.admatic.com.tr', - 'width': 1200, - 'height': 628 - }, - 'icon': { - 'url': 'https://www.admatic.com.tr', - 'width': 640, - 'height': 480 - } - }, - creativeId: '3742', - meta: { - model: 'iframe', advertiserDomains: ['admatic.com.tr'] }, ttl: 60, diff --git a/test/spec/modules/admixerBidAdapter_spec.js b/test/spec/modules/admixerBidAdapter_spec.js index 85538efc957..8cf433460b7 100644 --- a/test/spec/modules/admixerBidAdapter_spec.js +++ b/test/spec/modules/admixerBidAdapter_spec.js @@ -4,12 +4,11 @@ import {newBidder} from 'src/adapters/bidderFactory.js'; import {config} from '../../../src/config.js'; const BIDDER_CODE = 'admixer'; -const WL_BIDDER_CODE = 'admixerwl' +const BIDDER_CODE_ADX = 'admixeradx'; const ENDPOINT_URL = 'https://inv-nets.admixer.net/prebid.1.2.aspx'; const ENDPOINT_URL_CUSTOM = 'https://custom.admixer.net/prebid.aspx'; +const ENDPOINT_URL_ADX = 'https://inv-nets.admixer.net/adxprebid.1.2.aspx'; const ZONE_ID = '2eb6bd58-865c-47ce-af7f-a918108c3fd2'; -const CLIENT_ID = 5124; -const ENDPOINT_ID = 81264; describe('AdmixerAdapter', function () { const adapter = newBidder(spec); @@ -37,28 +36,9 @@ describe('AdmixerAdapter', function () { auctionId: '1d1a030790a475', }; - let wlBid = { - bidder: WL_BIDDER_CODE, - params: { - clientId: CLIENT_ID, - endpointId: ENDPOINT_ID, - }, - 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 params required by WL found', function () { - expect(spec.isBidRequestValid(wlBid)).to.equal(true); - }); it('should return false when required params are not passed', function () { let bid = Object.assign({}, bid); @@ -68,14 +48,6 @@ describe('AdmixerAdapter', function () { }; expect(spec.isBidRequestValid(bid)).to.equal(false); }); - it('should return false when params required by WL are not passed', function () { - let wlBid = Object.assign({}, wlBid); - delete wlBid.params; - wlBid.params = { - clientId: 0, - }; - expect(spec.isBidRequestValid(wlBid)).to.equal(false); - }); }); describe('buildRequests', function () { @@ -133,10 +105,7 @@ describe('AdmixerAdapter', function () { validRequest: [ { bidder: bidder, - params: bidder === 'admixerwl' ? { - clientId: CLIENT_ID, - endpointId: ENDPOINT_ID - } : { + params: { zone: ZONE_ID, }, adUnitCode: 'adunit-code', @@ -199,12 +168,6 @@ describe('AdmixerAdapter', function () { expect(request.url).to.equal('https://inv-nets.admixer.net/adxprebid.1.2.aspx'); expect(request.method).to.equal('POST'); }); - it('build request for admixerwl', function () { - const requestParams = requestParamsFor('admixerwl'); - const request = spec.buildRequests(requestParams.validRequest, requestParams.bidderRequest); - expect(request.url).to.equal(`https://inv-nets-adxwl.admixer.com/adxwlprebid.aspx?client=${CLIENT_ID}`); - expect(request.method).to.equal('POST'); - }); }); describe('checkFloorGetting', function () { diff --git a/test/spec/modules/adnuntiusBidAdapter_spec.js b/test/spec/modules/adnuntiusBidAdapter_spec.js index e109ca1829c..b1657ed1596 100644 --- a/test/spec/modules/adnuntiusBidAdapter_spec.js +++ b/test/spec/modules/adnuntiusBidAdapter_spec.js @@ -10,10 +10,9 @@ import {getGlobal} from '../../../src/prebidGlobal'; describe('adnuntiusBidAdapter', function() { const URL = 'https://ads.adnuntius.delivery/i?tzo='; const EURO_URL = 'https://europe.delivery.adnuntius.com/i?tzo='; + const GVLID = 855; const usi = utils.generateUUID() - - const meta = [{key: 'valueless'}, {value: 'keyless'}, {key: 'voidAuIds'}, {key: 'voidAuIds', value: [{auId: '11118b6bc', exp: misc.getUnixTimestamp()}, {exp: misc.getUnixTimestamp(1)}]}, {key: 'valid', value: 'also-valid', exp: misc.getUnixTimestamp(1)}, {key: 'expired', value: 'fwefew', exp: misc.getUnixTimestamp()}, {key: 'usi', value: 'should be skipped because timestamp', exp: misc.getUnixTimestamp()}, {key: 'usi', value: usi, exp: misc.getUnixTimestamp(100)}, {key: 'usi', value: 'should be skipped because timestamp', exp: misc.getUnixTimestamp()}] - let storage; + const meta = [{ key: 'usi', value: usi }] before(() => { getGlobal().bidderSettings = { @@ -21,11 +20,8 @@ describe('adnuntiusBidAdapter', function() { storageAllowed: true } }; - storage = getStorageManager({bidderCode: 'adnuntius'}); - }); - - beforeEach(() => { - storage.setDataInLocalStorage('adn.metaData', JSON.stringify(meta)); + const storage = getStorageManager({ bidderCode: 'adnuntius' }) + storage.setDataInLocalStorage('adn.metaData', JSON.stringify(meta)) }); after(() => { @@ -42,7 +38,7 @@ describe('adnuntiusBidAdapter', function() { const ENDPOINT_URL_VIDEO = `${ENDPOINT_URL_BASE}&userId=${usi}&tt=vast4`; const ENDPOINT_URL_NOCOOKIE = `${ENDPOINT_URL_BASE}&userId=${usi}&noCookies=true`; const ENDPOINT_URL_SEGMENTS = `${ENDPOINT_URL_BASE}&segments=segment1,segment2,segment3&userId=${usi}`; - const ENDPOINT_URL_CONSENT = `${EURO_URL}${tzo}&format=json&consentString=consentString&gdpr=1&userId=${usi}`; + const ENDPOINT_URL_CONSENT = `${EURO_URL}${tzo}&format=json&consentString=consentString&userId=${usi}`; const adapter = newBidder(spec); const bidderRequests = [ @@ -51,7 +47,6 @@ describe('adnuntiusBidAdapter', function() { bidder: 'adnuntius', params: { auId: '000000000008b6bc', - targetId: '123', network: 'adnuntius', maxDeals: 1 }, @@ -104,75 +99,9 @@ describe('adnuntiusBidAdapter', function() { const videoBidRequest = { bid: videoBidderRequest, - bidder: 'adnuntius', - params: { - bidType: 'justsomestuff-error-handling' - } + bidder: 'adnuntius' } - const deals = [ - { - 'destinationUrlEsc': 'http%3A%2F%2Fads.adnuntius.delivery%2Fc%2FyQtMUwYBn5P4v72WJMqLW4z7uJOBFXJTfjoRyz0z_wsAAAAQCtjQz9kbGWD4nuZy3q6HaCYxq6Lckz2kThplNb227EJdQ5032jcIGkf-UrPmXCU2EbXVaQ3Ok6_FNLuIDTONJyx6ZZCB10wGqA3OaSe1EqwQp84u1_5iQZAWDk73UYf7_vcIypn7ev-SICZ3qaevb2jYSRqTVZx6AiBZQQGlzlOOrbZU9AU1F-JwTds-YV3qtJHGlxI2peWFIuxFlOYyeX9Kzg%3Fct%3D673%26r%3Dhttp%253A%252F%252Fadnuntius.com', - 'assets': { - 'Image': { - 'cdnId': 'https://cdn.adnuntius.com/cdn/iBgqruUNbaUb2hmD3vws7WTi84jg_WB_-VOF_FeOZ7A.png', - 'width': '640', - 'height': '480' - } - }, - 'text': {}, - 'choices': {}, - 'clickUrl': 'https://ads.adnuntius.delivery/c/yQtMUwYBn5P4v72WJMqLW4z7uJOBFXJTfjoRyz0z_wsAAAAQCtjQz9kbGWD4nuZy3q6HaCYxq6Lckz2kThplNb227EJdQ5032jcIGkf-UrPmXCU2EbXVaQ3Ok6_FNLuIDTONJyx6ZZCB10wGqA3OaSe1EqwQp84u1_5iQZAWDk73UYf7_vcIypn7ev-SICZ3qaevb2jYSRqTVZx6AiBZQQGlzlOOrbZU9AU1F-JwTds-YV3qtJHGlxI2peWFIuxFlOYyeX9Kzg', - 'urls': { - 'destination': 'https://ads.adnuntius.delivery/c/yQtMUwYBn5P4v72WJMqLW4z7uJOBFXJTfjoRyz0z_wsAAAAQCtjQz9kbGWD4nuZy3q6HaCYxq6Lckz2kThplNb227EJdQ5032jcIGkf-UrPmXCU2EbXVaQ3Ok6_FNLuIDTONJyx6ZZCB10wGqA3OaSe1EqwQp84u1_5iQZAWDk73UYf7_vcIypn7ev-SICZ3qaevb2jYSRqTVZx6AiBZQQGlzlOOrbZU9AU1F-JwTds-YV3qtJHGlxI2peWFIuxFlOYyeX9Kzg?ct=673&r=http%3A%2F%2Fadnuntius.com' - }, - 'urlsEsc': { - 'destination': 'https%3A%2F%2Fads.adnuntius.delivery%2Fc%2FyQtMUwYBn5P4v72WJMqLW4z7uJOBFXJTfjoRyz0z_wsAAAAQCtjQz9kbGWD4nuZy3q6HaCYxq6Lckz2kThplNb227EJdQ5032jcIGkf-UrPmXCU2EbXVaQ3Ok6_FNLuIDTONJyx6ZZCB10wGqA3OaSe1EqwQp84u1_5iQZAWDk73UYf7_vcIypn7ev-SICZ3qaevb2jYSRqTVZx6AiBZQQGlzlOOrbZU9AU1F-JwTds-YV3qtJHGlxI2peWFIuxFlOYyeX9Kzg%3Fct%3D673%26r%3Dhttp%253A%252F%252Fadnuntius.com' - }, - 'destinationUrls': { - 'destination': 'https://adnuntius.com' - }, - 'cpm': { - 'amount': 9, - 'currency': 'USD' - }, - 'bid': { - 'amount': 0.009, - 'currency': 'USD' - }, - 'grossBid': { - 'amount': 0.009, - 'currency': 'USD' - }, - 'netBid': { - 'amount': 0.0081, - 'currency': 'USD' - }, - 'dealId': 'abc123xyz', - 'impressionTrackingUrls': [], - 'impressionTrackingUrlsEsc': [], - 'adId': 'adn-id-1064238860', - 'selectedColumn': '0', - 'selectedColumnPosition': '0', - 'renderedPixel': 'https://ads.adnuntius.delivery/b/yQtMUwYBn5P4v72WJMqLW4z7uJOBFXJTfjoRyz0z_wsAAAAQCtjQz9kbGWD4nuZy3q6HaCYxq6Lckz2kThplNb227EJdQ5032jcIGkf-UrPmXCU2EbXVaQ3Ok6_FNLuIDTONJyx6ZZCB10wGqA3OaSe1EqwQp84u1_5iQZAWDk73UYf7_vcIypn7ev-SICZ3qaevb2jYSRqTVZx6AiBZQQGlzlOOrbZU9AU1F-JwTds-YV3qtJHGlxI2peWFIuxFlOYyeX9Kzg.html', - 'renderedPixelEsc': 'http%3A%2F%2Fads.adnuntius.delivery%2Fb%2FyQtMUwYBn5P4v72WJMqLW4z7uJOBFXJTfjoRyz0z_wsAAAAQCtjQz9kbGWD4nuZy3q6HaCYxq6Lckz2kThplNb227EJdQ5032jcIGkf-UrPmXCU2EbXVaQ3Ok6_FNLuIDTONJyx6ZZCB10wGqA3OaSe1EqwQp84u1_5iQZAWDk73UYf7_vcIypn7ev-SICZ3qaevb2jYSRqTVZx6AiBZQQGlzlOOrbZU9AU1F-JwTds-YV3qtJHGlxI2peWFIuxFlOYyeX9Kzg.html', - 'visibleUrl': 'https://ads.adnuntius.delivery/s?rt=yQtMUwYBn5P4v72WJMqLW4z7uJOBFXJTfjoRyz0z_wsAAAAQCtjQz9kbGWD4nuZy3q6HaCYxq6Lckz2kThplNb227EJdQ5032jcIGkf-UrPmXCU2EbXVaQ3Ok6_FNLuIDTONJyx6ZZCB10wGqA3OaSe1EqwQp84u1_5iQZAWDk73UYf7_vcIypn7ev-SICZ3qaevb2jYSRqTVZx6AiBZQQGlzlOOrbZU9AU1F-JwTds-YV3qtJHGlxI2peWFIuxFlOYyeX9Kzg', - 'visibleUrlEsc': 'http%3A%2F%2Fads.adnuntius.delivery%2Fs%3Frt%3DyQtMUwYBn5P4v72WJMqLW4z7uJOBFXJTfjoRyz0z_wsAAAAQCtjQz9kbGWD4nuZy3q6HaCYxq6Lckz2kThplNb227EJdQ5032jcIGkf-UrPmXCU2EbXVaQ3Ok6_FNLuIDTONJyx6ZZCB10wGqA3OaSe1EqwQp84u1_5iQZAWDk73UYf7_vcIypn7ev-SICZ3qaevb2jYSRqTVZx6AiBZQQGlzlOOrbZU9AU1F-JwTds-YV3qtJHGlxI2peWFIuxFlOYyeX9Kzg', - 'viewUrl': 'https://ads.adnuntius.delivery/v?rt=yQtMUwYBn5P4v72WJMqLW4z7uJOBFXJTfjoRyz0z_wsAAAAQCtjQz9kbGWD4nuZy3q6HaCYxq6Lckz2kThplNb227EJdQ5032jcIGkf-UrPmXCU2EbXVaQ3Ok6_FNLuIDTONJyx6ZZCB10wGqA3OaSe1EqwQp84u1_5iQZAWDk73UYf7_vcIypn7ev-SICZ3qaevb2jYSRqTVZx6AiBZQQGlzlOOrbZU9AU1F-JwTds-YV3qtJHGlxI2peWFIuxFlOYyeX9Kzg', - 'viewUrlEsc': 'http%3A%2F%2Fads.adnuntius.delivery%2Fv%3Frt%3DyQtMUwYBn5P4v72WJMqLW4z7uJOBFXJTfjoRyz0z_wsAAAAQCtjQz9kbGWD4nuZy3q6HaCYxq6Lckz2kThplNb227EJdQ5032jcIGkf-UrPmXCU2EbXVaQ3Ok6_FNLuIDTONJyx6ZZCB10wGqA3OaSe1EqwQp84u1_5iQZAWDk73UYf7_vcIypn7ev-SICZ3qaevb2jYSRqTVZx6AiBZQQGlzlOOrbZU9AU1F-JwTds-YV3qtJHGlxI2peWFIuxFlOYyeX9Kzg', - 'rt': 'yQtMUwYBn5P4v72WJMqLW4z7uJOBFXJTfjoRyz0z_wsAAAAQCtjQz9kbGWD4nuZy3q6HaCYxq6Lckz2kThplNb227EJdQ5032jcIGkf-UrPmXCU2EbXVaQ3Ok6_FNLuIDTONJyx6ZZCB10wGqA3OaSe1EqwQp84u1_5iQZAWDk73UYf7_vcIypn7ev-SICZ3qaevb2jYSRqTVZx6AiBZQQGlzlOOrbZU9AU1F-JwTds-YV3qtJHGlxI2peWFIuxFlOYyeX9Kzg', - 'creativeWidth': '640', - 'creativeHeight': '480', - 'creativeId': 's90t0q03pm', - 'lineItemId': 'cr3hnkkxhnkw9ldy', - 'layoutId': 'buyers_network_image_layout_1', - 'layoutName': 'Image', - 'layoutExternalReference': '', - 'html': "\n\n\n \n \n \n\n\n
\n
\"\"/
\n
\n \n\n", - 'renderTemplate': '' - } - ]; - const serverResponse = { body: { 'adUnits': [ @@ -182,7 +111,68 @@ describe('adnuntiusBidAdapter', function() { 'html': "\n\n\n \n \n \n\n\n
\n
\"\"/
\n
\n \n\n", 'matchedAdCount': 1, 'responseId': 'adn-rsp--229633088', - 'deals': deals, + 'deals': [ + { + 'destinationUrlEsc': 'http%3A%2F%2Fads.adnuntius.delivery%2Fc%2FyQtMUwYBn5P4v72WJMqLW4z7uJOBFXJTfjoRyz0z_wsAAAAQCtjQz9kbGWD4nuZy3q6HaCYxq6Lckz2kThplNb227EJdQ5032jcIGkf-UrPmXCU2EbXVaQ3Ok6_FNLuIDTONJyx6ZZCB10wGqA3OaSe1EqwQp84u1_5iQZAWDk73UYf7_vcIypn7ev-SICZ3qaevb2jYSRqTVZx6AiBZQQGlzlOOrbZU9AU1F-JwTds-YV3qtJHGlxI2peWFIuxFlOYyeX9Kzg%3Fct%3D673%26r%3Dhttp%253A%252F%252Fadnuntius.com', + 'assets': { + 'Image': { + 'cdnId': 'https://cdn.adnuntius.com/cdn/iBgqruUNbaUb2hmD3vws7WTi84jg_WB_-VOF_FeOZ7A.png', + 'width': '640', + 'height': '480' + } + }, + 'text': {}, + 'choices': {}, + 'clickUrl': 'https://ads.adnuntius.delivery/c/yQtMUwYBn5P4v72WJMqLW4z7uJOBFXJTfjoRyz0z_wsAAAAQCtjQz9kbGWD4nuZy3q6HaCYxq6Lckz2kThplNb227EJdQ5032jcIGkf-UrPmXCU2EbXVaQ3Ok6_FNLuIDTONJyx6ZZCB10wGqA3OaSe1EqwQp84u1_5iQZAWDk73UYf7_vcIypn7ev-SICZ3qaevb2jYSRqTVZx6AiBZQQGlzlOOrbZU9AU1F-JwTds-YV3qtJHGlxI2peWFIuxFlOYyeX9Kzg', + 'urls': { + 'destination': 'https://ads.adnuntius.delivery/c/yQtMUwYBn5P4v72WJMqLW4z7uJOBFXJTfjoRyz0z_wsAAAAQCtjQz9kbGWD4nuZy3q6HaCYxq6Lckz2kThplNb227EJdQ5032jcIGkf-UrPmXCU2EbXVaQ3Ok6_FNLuIDTONJyx6ZZCB10wGqA3OaSe1EqwQp84u1_5iQZAWDk73UYf7_vcIypn7ev-SICZ3qaevb2jYSRqTVZx6AiBZQQGlzlOOrbZU9AU1F-JwTds-YV3qtJHGlxI2peWFIuxFlOYyeX9Kzg?ct=673&r=http%3A%2F%2Fadnuntius.com' + }, + 'urlsEsc': { + 'destination': 'https%3A%2F%2Fads.adnuntius.delivery%2Fc%2FyQtMUwYBn5P4v72WJMqLW4z7uJOBFXJTfjoRyz0z_wsAAAAQCtjQz9kbGWD4nuZy3q6HaCYxq6Lckz2kThplNb227EJdQ5032jcIGkf-UrPmXCU2EbXVaQ3Ok6_FNLuIDTONJyx6ZZCB10wGqA3OaSe1EqwQp84u1_5iQZAWDk73UYf7_vcIypn7ev-SICZ3qaevb2jYSRqTVZx6AiBZQQGlzlOOrbZU9AU1F-JwTds-YV3qtJHGlxI2peWFIuxFlOYyeX9Kzg%3Fct%3D673%26r%3Dhttp%253A%252F%252Fadnuntius.com' + }, + 'destinationUrls': { + 'destination': 'https://adnuntius.com' + }, + 'cpm': { + 'amount': 9, + 'currency': 'USD' + }, + 'bid': { + 'amount': 0.009, + 'currency': 'USD' + }, + 'grossBid': { + 'amount': 0.009, + 'currency': 'USD' + }, + 'netBid': { + 'amount': 0.0081, + 'currency': 'USD' + }, + 'dealId': 'abc123xyz', + 'impressionTrackingUrls': [], + 'impressionTrackingUrlsEsc': [], + 'adId': 'adn-id-1064238860', + 'selectedColumn': '0', + 'selectedColumnPosition': '0', + 'renderedPixel': 'https://ads.adnuntius.delivery/b/yQtMUwYBn5P4v72WJMqLW4z7uJOBFXJTfjoRyz0z_wsAAAAQCtjQz9kbGWD4nuZy3q6HaCYxq6Lckz2kThplNb227EJdQ5032jcIGkf-UrPmXCU2EbXVaQ3Ok6_FNLuIDTONJyx6ZZCB10wGqA3OaSe1EqwQp84u1_5iQZAWDk73UYf7_vcIypn7ev-SICZ3qaevb2jYSRqTVZx6AiBZQQGlzlOOrbZU9AU1F-JwTds-YV3qtJHGlxI2peWFIuxFlOYyeX9Kzg.html', + 'renderedPixelEsc': 'http%3A%2F%2Fads.adnuntius.delivery%2Fb%2FyQtMUwYBn5P4v72WJMqLW4z7uJOBFXJTfjoRyz0z_wsAAAAQCtjQz9kbGWD4nuZy3q6HaCYxq6Lckz2kThplNb227EJdQ5032jcIGkf-UrPmXCU2EbXVaQ3Ok6_FNLuIDTONJyx6ZZCB10wGqA3OaSe1EqwQp84u1_5iQZAWDk73UYf7_vcIypn7ev-SICZ3qaevb2jYSRqTVZx6AiBZQQGlzlOOrbZU9AU1F-JwTds-YV3qtJHGlxI2peWFIuxFlOYyeX9Kzg.html', + 'visibleUrl': 'https://ads.adnuntius.delivery/s?rt=yQtMUwYBn5P4v72WJMqLW4z7uJOBFXJTfjoRyz0z_wsAAAAQCtjQz9kbGWD4nuZy3q6HaCYxq6Lckz2kThplNb227EJdQ5032jcIGkf-UrPmXCU2EbXVaQ3Ok6_FNLuIDTONJyx6ZZCB10wGqA3OaSe1EqwQp84u1_5iQZAWDk73UYf7_vcIypn7ev-SICZ3qaevb2jYSRqTVZx6AiBZQQGlzlOOrbZU9AU1F-JwTds-YV3qtJHGlxI2peWFIuxFlOYyeX9Kzg', + 'visibleUrlEsc': 'http%3A%2F%2Fads.adnuntius.delivery%2Fs%3Frt%3DyQtMUwYBn5P4v72WJMqLW4z7uJOBFXJTfjoRyz0z_wsAAAAQCtjQz9kbGWD4nuZy3q6HaCYxq6Lckz2kThplNb227EJdQ5032jcIGkf-UrPmXCU2EbXVaQ3Ok6_FNLuIDTONJyx6ZZCB10wGqA3OaSe1EqwQp84u1_5iQZAWDk73UYf7_vcIypn7ev-SICZ3qaevb2jYSRqTVZx6AiBZQQGlzlOOrbZU9AU1F-JwTds-YV3qtJHGlxI2peWFIuxFlOYyeX9Kzg', + 'viewUrl': 'https://ads.adnuntius.delivery/v?rt=yQtMUwYBn5P4v72WJMqLW4z7uJOBFXJTfjoRyz0z_wsAAAAQCtjQz9kbGWD4nuZy3q6HaCYxq6Lckz2kThplNb227EJdQ5032jcIGkf-UrPmXCU2EbXVaQ3Ok6_FNLuIDTONJyx6ZZCB10wGqA3OaSe1EqwQp84u1_5iQZAWDk73UYf7_vcIypn7ev-SICZ3qaevb2jYSRqTVZx6AiBZQQGlzlOOrbZU9AU1F-JwTds-YV3qtJHGlxI2peWFIuxFlOYyeX9Kzg', + 'viewUrlEsc': 'http%3A%2F%2Fads.adnuntius.delivery%2Fv%3Frt%3DyQtMUwYBn5P4v72WJMqLW4z7uJOBFXJTfjoRyz0z_wsAAAAQCtjQz9kbGWD4nuZy3q6HaCYxq6Lckz2kThplNb227EJdQ5032jcIGkf-UrPmXCU2EbXVaQ3Ok6_FNLuIDTONJyx6ZZCB10wGqA3OaSe1EqwQp84u1_5iQZAWDk73UYf7_vcIypn7ev-SICZ3qaevb2jYSRqTVZx6AiBZQQGlzlOOrbZU9AU1F-JwTds-YV3qtJHGlxI2peWFIuxFlOYyeX9Kzg', + 'rt': 'yQtMUwYBn5P4v72WJMqLW4z7uJOBFXJTfjoRyz0z_wsAAAAQCtjQz9kbGWD4nuZy3q6HaCYxq6Lckz2kThplNb227EJdQ5032jcIGkf-UrPmXCU2EbXVaQ3Ok6_FNLuIDTONJyx6ZZCB10wGqA3OaSe1EqwQp84u1_5iQZAWDk73UYf7_vcIypn7ev-SICZ3qaevb2jYSRqTVZx6AiBZQQGlzlOOrbZU9AU1F-JwTds-YV3qtJHGlxI2peWFIuxFlOYyeX9Kzg', + 'creativeWidth': '640', + 'creativeHeight': '480', + 'creativeId': 's90t0q03pm', + 'lineItemId': 'cr3hnkkxhnkw9ldy', + 'layoutId': 'buyers_network_image_layout_1', + 'layoutName': 'Image', + 'layoutExternalReference': '', + 'html': "\n\n\n \n \n \n\n\n
\n
\"\"/
\n
\n \n\n", + 'renderTemplate': '' + } + ], 'ads': [ { 'destinationUrlEsc': 'http%3A%2F%2Fads.adnuntius.delivery%2Fc%2F5Mu-vFVsaf4dWWx8uyZoV7Mz0sPkF1_j9bSupMwX8dMAAAAQCtjQz9kbGWD4nuZy3q6HaCYxqqTYxj2sS0FkMriztxcORshj3zRbT0KsV7XnDXE0F-OsHX7Ok6_FNLuIDTOMJyx6ZZCB10wGqA3OaSe1Eq9D85h8gP1gGsobC0KsAISm_PYNkJ6ve6qZLnB79fX6XHLYSRqTBM8sBCRXQAetnVzeo7AHoQhkFeouS444YA_q4JCTlRI2peWFIuxFlOYyeX9Kzg%3Fct%3D673%26r%3Dhttp%253A%252F%252Fadnuntius.com', @@ -464,78 +454,7 @@ describe('adnuntiusBidAdapter', function() { expect(request[0]).to.have.property('url'); expect(request[0].url).to.equal(ENDPOINT_URL); expect(request[0]).to.have.property('data'); - 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]]}],"metaData":{"valid":"also-valid"}}'); - }); - - 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); - expect(request[0]).to.have.property('bid'); - const bid = request[0].bid[0] - expect(bid).to.have.property('bidId'); - expect(request[0]).to.have.property('url'); - expect(request[0].url).to.equal(ENDPOINT_URL_BASE); - expect(request[0]).to.have.property('data'); - 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]]}]}'); - - localStorage.removeItem('adn.metaData'); - const request2 = spec.buildRequests(bidderRequests, {}); - expect(request2.length).to.equal(1); - expect(request2[0]).to.have.property('url'); - expect(request2[0].url).to.equal(ENDPOINT_URL_BASE); - }); - - it('Test request changes for voided au ids', function() { - storage.setDataInLocalStorage('adn.metaData', JSON.stringify([{key: 'voidAuIds', value: [{auId: '11118b6bc', exp: misc.getUnixTimestamp(1)}, {auId: '0000000000000023', exp: misc.getUnixTimestamp(1)}]}])); - const bRequests = bidderRequests.concat([{ - bidId: 'adn-11118b6bc', - bidder: 'adnuntius', - params: { - auId: '11118b6bc', - network: 'adnuntius', - }, - mediaTypes: { - banner: { - sizes: [[1640, 1480], [1600, 1400]], - } - }, - }]); - bRequests.push({ - bidId: 'adn-23', - bidder: 'adnuntius', - params: { - auId: '23', - network: 'adnuntius', - }, - mediaTypes: { - banner: { - sizes: [[1640, 1480], [1600, 1400]], - } - }, - }); - bRequests.push({ - bidId: 'adn-13', - bidder: 'adnuntius', - params: { - auId: '13', - network: 'adnuntius', - }, - mediaTypes: { - banner: { - sizes: [[164, 140], [10, 1400]], - } - }, - }); - const request = spec.buildRequests(bRequests, {}); - expect(request.length).to.equal(1); - expect(request[0]).to.have.property('bid'); - const bid = request[0].bid[0] - expect(bid).to.have.property('bidId'); - expect(request[0]).to.have.property('url'); - expect(request[0].url).to.equal(ENDPOINT_URL_BASE); - expect(request[0]).to.have.property('data'); - 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]]}]}'); + expect(request[0].data).to.equal('{"adUnits":[{"auId":"000000000008b6bc","targetId":"adn-000000000008b6bc","maxDeals":1,"dimensions":[[640,480],[600,400]]},{"auId":"0000000000000551","targetId":"adn-0000000000000551","maxDeals":0,"dimensions":[[1640,1480],[1600,1400]]}],"metaData":{"usi":"' + usi + '"}}'); }); it('Test Video requests', function() { @@ -553,7 +472,7 @@ describe('adnuntiusBidAdapter', function() { user: { data: [{ name: 'adnuntius', - segment: [{id: 'segment1'}, {id: 'segment2'}, {invalidSegment: 'invalid'}, {id: 123}, {id: ['3332']}] + segment: [{id: 'segment1'}, {id: 'segment2'}] }, { name: 'other', @@ -736,7 +655,7 @@ describe('adnuntiusBidAdapter', function() { expect(bidderRequests[0].params.maxDeals).to.equal(1); expect(data.adUnits[0].maxDeals).to.equal(bidderRequests[0].params.maxDeals); expect(bidderRequests[1].params).to.not.have.property('maxBids'); - expect(data.adUnits[1].maxDeals).to.equal(undefined); + expect(data.adUnits[1].maxDeals).to.equal(0); }); it('Should allow a maximum of 5 deals.', function() { config.setBidderConfig({ @@ -782,7 +701,7 @@ describe('adnuntiusBidAdapter', function() { expect(request[0]).to.have.property('data'); const data = JSON.parse(request[0].data); expect(data.adUnits.length).to.equal(1); - expect(data.adUnits[0].maxDeals).to.equal(undefined); + expect(data.adUnits[0].maxDeals).to.equal(0); }); it('Should set max deals using bidder config.', function() { config.setBidderConfig({ @@ -828,20 +747,12 @@ describe('adnuntiusBidAdapter', function() { describe('interpretResponse', function() { it('should return valid response when passed valid server response', function() { - config.setBidderConfig({ - bidders: ['adnuntius'], - config: { - bidType: 'netBid', - maxDeals: 1 - } - }); - - const interpretedResponse = config.runWithBidder('adnuntius', () => spec.interpretResponse(serverResponse, singleBidRequest)); + const interpretedResponse = spec.interpretResponse(serverResponse, singleBidRequest); expect(interpretedResponse).to.have.lengthOf(2); const deal = serverResponse.body.adUnits[0].deals[0]; expect(interpretedResponse[0].bidderCode).to.equal('adnuntius'); - expect(interpretedResponse[0].cpm).to.equal(deal.netBid.amount * 1000); + expect(interpretedResponse[0].cpm).to.equal(deal.bid.amount * 1000); expect(interpretedResponse[0].width).to.equal(Number(deal.creativeWidth)); expect(interpretedResponse[0].height).to.equal(Number(deal.creativeHeight)); expect(interpretedResponse[0].creativeId).to.equal(deal.creativeId); @@ -857,7 +768,7 @@ describe('adnuntiusBidAdapter', function() { const ad = serverResponse.body.adUnits[0].ads[0]; expect(interpretedResponse[1].bidderCode).to.equal('adnuntius'); - expect(interpretedResponse[1].cpm).to.equal(ad.netBid.amount * 1000); + expect(interpretedResponse[1].cpm).to.equal(ad.bid.amount * 1000); expect(interpretedResponse[1].width).to.equal(Number(ad.creativeWidth)); expect(interpretedResponse[1].height).to.equal(Number(ad.creativeHeight)); expect(interpretedResponse[1].creativeId).to.equal(ad.creativeId); @@ -870,87 +781,6 @@ describe('adnuntiusBidAdapter', function() { expect(interpretedResponse[1].ttl).to.equal(360); expect(interpretedResponse[1].dealId).to.equal('not-in-deal-array-here'); expect(interpretedResponse[1].dealCount).to.equal(0); - - const results = JSON.parse(storage.getDataFromLocalStorage('adn.metaData')); - const usiEntry = results.find(entry => entry.key === 'usi'); - expect(usiEntry.key).to.equal('usi'); - expect(usiEntry.value).to.equal('from-api-server dude'); - expect(usiEntry.exp).to.be.greaterThan(misc.getUnixTimestamp(90)); - - const voidAuIdsEntry = results.find(entry => entry.key === 'voidAuIds'); - expect(voidAuIdsEntry.key).to.equal('voidAuIds'); - expect(voidAuIdsEntry.exp).to.equal(undefined); - expect(voidAuIdsEntry.value[0].auId).to.equal('00000000000abcde'); - expect(voidAuIdsEntry.value[0].exp).to.be.greaterThan(misc.getUnixTimestamp()); - expect(voidAuIdsEntry.value[0].exp).to.be.lessThan(misc.getUnixTimestamp(2)); - expect(voidAuIdsEntry.value[1].auId).to.equal('00000000000fffff'); - expect(voidAuIdsEntry.value[1].exp).to.be.greaterThan(misc.getUnixTimestamp()); - expect(voidAuIdsEntry.value[1].exp).to.be.lessThan(misc.getUnixTimestamp(2)); - - const validEntry = results.find(entry => entry.key === 'valid'); - expect(validEntry.key).to.equal('valid'); - expect(validEntry.value).to.equal('also-valid'); - expect(validEntry.exp).to.be.greaterThan(misc.getUnixTimestamp()); - expect(validEntry.exp).to.be.lessThan(misc.getUnixTimestamp(2)); - - const randomApiEntry = results.find(entry => entry.key === 'randomApiKey'); - expect(randomApiEntry.key).to.equal('randomApiKey'); - expect(randomApiEntry.value).to.equal('randomApiValue'); - expect(randomApiEntry.exp).to.be.greaterThan(misc.getUnixTimestamp(90)); - }); - - it('should not process valid response when passed alt bidder that is an adndeal', function() { - const altBidder = { - bid: [ - { - bidder: 'adndeal1', - bidId: 'adn-0000000000000551', - } - ] - }; - serverResponse.body.adUnits[0].deals = []; - delete serverResponse.body.metaData.voidAuIds; // test response with no voidAuIds - - const interpretedResponse = spec.interpretResponse(serverResponse, altBidder); - expect(interpretedResponse).to.have.lengthOf(0); - - serverResponse.body.adUnits[0].deals = deals; - }); - - it('should return valid response when passed alt bidder', function() { - const altBidder = { - bid: [ - { - bidder: 'adn-alt', - bidId: 'adn-0000000000000551', - params: { - bidType: 'netBid' - } - } - ] - }; - serverResponse.body.adUnits[0].deals = []; - - const interpretedResponse = spec.interpretResponse(serverResponse, altBidder); - expect(interpretedResponse).to.have.lengthOf(1); - - const ad = serverResponse.body.adUnits[0].ads[0]; - expect(interpretedResponse[0].bidderCode).to.equal('adn-alt'); - expect(interpretedResponse[0].cpm).to.equal(ad.netBid.amount * 1000); - expect(interpretedResponse[0].width).to.equal(Number(ad.creativeWidth)); - expect(interpretedResponse[0].height).to.equal(Number(ad.creativeHeight)); - expect(interpretedResponse[0].creativeId).to.equal(ad.creativeId); - expect(interpretedResponse[0].currency).to.equal(ad.bid.currency); - expect(interpretedResponse[0].netRevenue).to.equal(false); - expect(interpretedResponse[0].meta).to.have.property('advertiserDomains'); - expect(interpretedResponse[0].meta.advertiserDomains).to.have.lengthOf(1); - expect(interpretedResponse[0].meta.advertiserDomains[0]).to.equal('adnuntius.com'); - expect(interpretedResponse[0].ad).to.equal(serverResponse.body.adUnits[0].html); - expect(interpretedResponse[0].ttl).to.equal(360); - expect(interpretedResponse[0].dealId).to.equal('not-in-deal-array-here'); - expect(interpretedResponse[0].dealCount).to.equal(0); - - serverResponse.body.adUnits[0].deals = deals; }); }); diff --git a/test/spec/modules/adpod_spec.js b/test/spec/modules/adpod_spec.js index 14e530c1a9b..a6164f919ef 100644 --- a/test/spec/modules/adpod_spec.js +++ b/test/spec/modules/adpod_spec.js @@ -47,6 +47,7 @@ describe('adpod.js', function () { addBidToAuctionStub = sinon.stub(auction, 'addBidToAuction').callsFake(function (auctionInstance, bid) { auctionBids.push(bid); }); + doCallbacksIfTimedoutStub = sinon.stub(auction, 'doCallbacksIfTimedout'); clock = sinon.useFakeTimers(); config.setConfig({ cache: { @@ -60,6 +61,7 @@ describe('adpod.js', function () { logWarnStub.restore(); logInfoStub.restore(); addBidToAuctionStub.restore(); + doCallbacksIfTimedoutStub.restore(); clock.restore(); config.resetConfig(); auctionBids = []; @@ -631,6 +633,7 @@ describe('adpod.js', function () { callPrebidCacheHook(callbackFn, auctionInstance, bidResponse1, afterBidAddedSpy, videoMT); callPrebidCacheHook(callbackFn, auctionInstance, bidResponse2, afterBidAddedSpy, videoMT); + expect(doCallbacksIfTimedoutStub.calledTwice).to.equal(true); expect(logWarnStub.calledOnce).to.equal(true); expect(auctionBids.length).to.equal(0); }); diff --git a/test/spec/modules/adqueryBidAdapter_spec.js b/test/spec/modules/adqueryBidAdapter_spec.js index e9286329d57..75b012aac77 100644 --- a/test/spec/modules/adqueryBidAdapter_spec.js +++ b/test/spec/modules/adqueryBidAdapter_spec.js @@ -80,11 +80,7 @@ describe('adqueryBidAdapter', function () { }) describe('buildRequests', function () { - let req; - beforeEach(() => { - req = spec.buildRequests([ bidRequest ], { refererInfo: { } })[0] - }) - + let req = spec.buildRequests([ bidRequest ], { refererInfo: { } })[0] let rdata it('should return request object', function () { diff --git a/test/spec/modules/adqueryIdSystem_spec.js b/test/spec/modules/adqueryIdSystem_spec.js index 0a2cd60d89e..a6b4e9d1529 100644 --- a/test/spec/modules/adqueryIdSystem_spec.js +++ b/test/spec/modules/adqueryIdSystem_spec.js @@ -1,6 +1,5 @@ -import {adqueryIdSubmodule, storage} from 'modules/adqueryIdSystem.js'; -import {server} from 'test/mocks/xhr.js'; -import sinon from 'sinon'; +import { adqueryIdSubmodule, storage } from 'modules/adqueryIdSystem.js'; +import { server } from 'test/mocks/xhr.js'; const config = { storage: { @@ -19,10 +18,10 @@ describe('AdqueryIdSystem', function () { }); }); - describe('getId', function () { + describe('getId', function() { let getDataFromLocalStorageStub; - beforeEach(function () { + beforeEach(function() { getDataFromLocalStorageStub = sinon.stub(storage, 'getDataFromLocalStorage'); }); @@ -30,7 +29,7 @@ describe('AdqueryIdSystem', function () { getDataFromLocalStorageStub.restore(); }); - it('gets a adqueryId', function () { + it('gets a adqueryId', function() { const config = { params: {} }; @@ -38,24 +37,36 @@ describe('AdqueryIdSystem', function () { const callback = adqueryIdSubmodule.getId(config).callback; callback(callbackSpy); const request = server.requests[0]; - expect(request.url).to.contains(`https://bidder.adquery.io/prebid/qid?qid=`); - request.respond(200, {'Content-Type': 'application/json'}, JSON.stringify({qid: '6dd9eab7dfeab7df6dd9ea'})); - expect(callbackSpy.lastCall.lastArg).to.deep.equal('6dd9eab7dfeab7df6dd9ea'); + expect(request.url).to.eq(`https://bidder.adquery.io/prebid/qid`); + request.respond(200, { 'Content-Type': 'application/json' }, JSON.stringify({ qid: 'qid' })); + expect(callbackSpy.lastCall.lastArg).to.deep.equal({qid: 'qid'}); }); - it('allows configurable id url', function () { + it('gets a cached adqueryId', function() { + const config = { + params: {} + }; + getDataFromLocalStorageStub.withArgs('qid').returns('qid'); + + const callbackSpy = sinon.spy(); + const callback = adqueryIdSubmodule.getId(config).callback; + callback(callbackSpy); + expect(callbackSpy.lastCall.lastArg).to.deep.equal({qid: 'qid'}); + }); + + it('allows configurable id url', function() { const config = { params: { - url: 'https://another_bidder.adquery.io/qid' + url: 'https://bidder.adquery.io' } }; const callbackSpy = sinon.spy(); const callback = adqueryIdSubmodule.getId(config).callback; callback(callbackSpy); const request = server.requests[0]; - expect(request.url).to.contains('https://another_bidder.adquery.io/qid'); - request.respond(200, {'Content-Type': 'application/json'}, JSON.stringify({qid: 'testqid'})); - expect(callbackSpy.lastCall.lastArg).to.deep.equal('testqid'); + expect(request.url).to.eq('https://bidder.adquery.io'); + request.respond(200, { 'Content-Type': 'application/json' }, JSON.stringify({ qid: 'testqid' })); + expect(callbackSpy.lastCall.lastArg).to.deep.equal({qid: 'testqid'}); }); }); }); diff --git a/test/spec/modules/adstirBidAdapter_spec.js b/test/spec/modules/adstirBidAdapter_spec.js deleted file mode 100644 index 290a6822f69..00000000000 --- a/test/spec/modules/adstirBidAdapter_spec.js +++ /dev/null @@ -1,412 +0,0 @@ -import { expect } from 'chai'; -import { spec } from '../../../modules/adstirBidAdapter.js'; -import * as utils from 'src/utils.js'; -import { config } from 'src/config.js'; - -describe('AdstirAdapter', function () { - describe('isBidRequestValid', function () { - it('should return true if appId is non-empty string and adSpaceNo is integer', function () { - const bid = { - params: { - appId: 'MEDIA-XXXXXX', - adSpaceNo: 6, - } - } - expect(spec.isBidRequestValid(bid)).to.equal(true); - }); - it('should return false if appId is non-empty string, but adSpaceNo is not integer', function () { - const bid = { - params: { - appId: 'MEDIA-XXXXXX', - adSpaceNo: 'a', - } - } - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - it('should return false if appId is non-empty string, but adSpaceNo is null', function () { - const bid = { - params: { - appId: 'MEDIA-XXXXXX', - adSpaceNo: null, - } - } - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - it('should return false if appId is non-empty string, but adSpaceNo is undefined', function () { - const bid = { - params: { - appId: 'MEDIA-XXXXXX' - } - } - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - it('should return false if adSpaceNo is integer, but appId is empty string', function () { - const bid = { - params: { - appId: '', - adSpaceNo: 6, - } - } - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - it('should return false if adSpaceNo is integer, but appId is not string', function () { - const bid = { - params: { - appId: 123, - adSpaceNo: 6, - } - } - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - it('should return false if adSpaceNo is integer, but appId is null', function () { - const bid = { - params: { - appId: null, - adSpaceNo: 6, - } - } - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - it('should return false if adSpaceNo is integer, but appId is undefined', function () { - const bid = { - params: { - adSpaceNo: 6, - } - } - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - it('should return false if params is empty', function () { - const bid = { - params: {} - } - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - }); - - describe('buildRequests', function () { - const validBidRequests = [ - { - auctionId: 'b06c5141-fe8f-4cdf-9d7d-54415490a917', - bidder: 'adstir', - bidId: 'bidid1111', - params: { - appId: 'MEDIA-XXXXXX', - adSpaceNo: 1, - }, - transactionId: 'transactionid-1111', - mediaTypes: { - banner: { - sizes: [ - [300, 250], - [336, 280], - ], - } - }, - sizes: [ - [300, 250], - [336, 280], - ], - }, - { - auctionId: 'b06c5141-fe8f-4cdf-9d7d-54415490a917', - bidder: 'adstir', - bidId: 'bidid2222', - params: { - appId: 'MEDIA-XXXXXX', - adSpaceNo: 2, - }, - transactionId: 'transactionid-2222', - mediaTypes: { - banner: { - sizes: [ - [320, 50], - [320, 100], - ], - } - }, - sizes: [ - [320, 50], - [320, 100], - ], - }, - ]; - - const bidderRequest = { - auctionId: 'b06c5141-fe8f-4cdf-9d7d-54415490a917', - refererInfo: { - page: 'https://ad-stir.com/contact', - topmostLocation: 'https://ad-stir.com/contact', - reachedTop: true, - ref: 'https://test.example/q=adstir', - isAmp: false, - numIframes: 0, - stack: [ - 'https://ad-stir.com/contact', - ], - }, - }; - - it('one entry in validBidRequests corresponds to one server request object.', function () { - const requests = spec.buildRequests(validBidRequests, bidderRequest); - expect(requests.length).to.equal(validBidRequests.length); - requests.forEach(function (r, i) { - expect(r.method).to.equal('POST'); - expect(r.url).to.equal('https://ad.ad-stir.com/prebid'); - const d = JSON.parse(r.data); - expect(d.auctionId).to.equal('b06c5141-fe8f-4cdf-9d7d-54415490a917'); - - const v = validBidRequests[i]; - expect(d.appId).to.equal(v.params.appId); - expect(d.adSpaceNo).to.equal(v.params.adSpaceNo); - expect(d.bidId).to.equal(v.bidId); - expect(d.transactionId).to.equal(v.transactionId); - expect(d.mediaTypes).to.deep.equal(v.mediaTypes); - expect(d.sizes).to.deep.equal(v.sizes); - expect(d.ref.page).to.equal(bidderRequest.refererInfo.page); - expect(d.ref.tloc).to.equal(bidderRequest.refererInfo.topmostLocation); - expect(d.ref.referrer).to.equal(bidderRequest.refererInfo.ref); - expect(d.sua).to.equal(null); - expect(d.gdpr).to.equal(false); - expect(d.usp).to.equal(false); - expect(d.schain).to.equal(null); - expect(d.eids).to.deep.equal([]); - }); - }); - - it('ref.page, ref.tloc and ref.referrer correspond to refererInfo', function () { - const [ request ] = spec.buildRequests([validBidRequests[0]], { - auctionId: 'b06c5141-fe8f-4cdf-9d7d-54415490a917', - refererInfo: { - page: null, - topmostLocation: 'https://adserver.example/iframe1.html', - reachedTop: false, - ref: null, - isAmp: false, - numIframes: 2, - stack: [ - null, - 'https://adserver.example/iframe1.html', - 'https://adserver.example/iframe2.html' - ], - }, - }); - - const { ref } = JSON.parse(request.data); - expect(ref.page).to.equal(null); - expect(ref.tloc).to.equal('https://adserver.example/iframe1.html'); - expect(ref.referrer).to.equal(null); - }); - - it('when config.pageUrl is not set, ref.topurl equals to refererInfo.reachedTop', function () { - let bidderRequestClone = utils.deepClone(bidderRequest); - [true, false].forEach(function (reachedTop) { - bidderRequestClone.refererInfo.reachedTop = reachedTop; - const requests = spec.buildRequests(validBidRequests, bidderRequestClone); - const d = JSON.parse(requests[0].data); - expect(d.ref.topurl).to.equal(reachedTop); - }); - }); - - describe('when config.pageUrl is set, ref.topurl is always false', function () { - before(function () { - config.setConfig({ pageUrl: 'https://ad-stir.com/register' }); - }); - after(function () { - config.resetConfig(); - }); - - it('ref.topurl should be false', function () { - let bidderRequestClone = utils.deepClone(bidderRequest); - [true, false].forEach(function (reachedTop) { - bidderRequestClone.refererInfo.reachedTop = reachedTop; - const requests = spec.buildRequests(validBidRequests, bidderRequestClone); - const d = JSON.parse(requests[0].data); - expect(d.ref.topurl).to.equal(false); - }); - }); - }); - - it('gdprConsent.gdprApplies is sent', function () { - let bidderRequestClone = utils.deepClone(bidderRequest); - [true, false].forEach(function (gdprApplies) { - bidderRequestClone.gdprConsent = { gdprApplies }; - const requests = spec.buildRequests(validBidRequests, bidderRequestClone); - const d = JSON.parse(requests[0].data); - expect(d.gdpr).to.equal(gdprApplies); - }); - }); - - it('includes in the request parameters whether CCPA applies', function () { - let bidderRequestClone = utils.deepClone(bidderRequest); - const cases = [ - { uspConsent: '1---', expected: false }, - { uspConsent: '1YYY', expected: true }, - { uspConsent: '1YNN', expected: true }, - { uspConsent: '1NYN', expected: true }, - { uspConsent: '1-Y-', expected: true }, - ]; - cases.forEach(function ({ uspConsent, expected }) { - bidderRequestClone.uspConsent = uspConsent; - const requests = spec.buildRequests(validBidRequests, bidderRequestClone); - const d = JSON.parse(requests[0].data); - expect(d.usp).to.equal(expected); - }); - }); - - it('should add schain if available', function() { - const schain = { - 'ver': '1.0', - 'complete': 1, - 'nodes': [ - { - 'asi': 'exchange1.example', - 'sid': '1234!abcd', - 'hp': 1, - 'rid': 'bid-request-1', - 'name': 'publisher, Inc.', - 'domain': 'publisher.example' - }, - { - 'asi': 'exchange2.example', - 'sid': 'abcd', - 'hp': 1, - 'rid': 'bid-request-2', - 'name': 'intermediary', - 'domain': 'intermediary.example' - } - ] - }; - 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]), { schain })], bidderRequest); - const d = JSON.parse(request.data); - expect(d.schain).to.deep.equal(serializedSchain); - }); - - it('should add schain even if some nodes params are blank', function() { - const schain = { - 'ver': '1.0', - 'complete': 1, - 'nodes': [ - { - 'asi': 'exchange1.example', - 'sid': '1234!abcd', - 'hp': 1, - }, - { - }, - { - 'asi': 'exchange2.example', - 'sid': 'abcd', - 'hp': 1, - }, - ] - }; - const serializedSchain = '1.0,1!exchange1.example,1234%21abcd,1,,,!,,,,,!exchange2.example,abcd,1,,,'; - - const [ request ] = spec.buildRequests([utils.mergeDeep(utils.deepClone(validBidRequests[0]), { schain })], bidderRequest); - const d = JSON.parse(request.data); - expect(d.schain).to.deep.equal(serializedSchain); - }); - - it('should add UA client hints to payload if available', function () { - const sua = { - browsers: [ - { - brand: 'Not?A_Brand', - version: [ - '8', - '0', - '0', - '0' - ] - }, - { - version: [ - '108', - '0', - '5359', - '40' - ] - }, - { - brand: 'Google Chrome', - version: [ - '108', - '0', - '5359', - '40' - ] - } - ], - platform: { - brand: 'Android', - version: [ - '11' - ] - }, - mobile: 1, - architecture: '', - bitness: '64', - model: 'Pixel 5', - source: 2 - } - - const validBidRequestsClone = utils.deepClone(validBidRequests); - validBidRequestsClone[0] = utils.mergeDeep(validBidRequestsClone[0], { - ortb2: { - device: { sua }, - } - }); - - const requests = spec.buildRequests(validBidRequestsClone, bidderRequest); - requests.forEach(function (r) { - const d = JSON.parse(r.data); - expect(d.sua).to.deep.equal(sua); - }); - }); - }); - - describe('interpretResponse', function () { - it('return empty array when no content', function () { - const bids = spec.interpretResponse({ body: '' }); - expect(bids).to.deep.equal([]); - }); - it('return empty array when seatbid empty', function () { - const bids = spec.interpretResponse({ body: { seatbid: [] } }); - expect(bids).to.deep.equal([]); - }); - it('return valid bids when serverResponse is valid', function () { - const serverResponse = { - 'body': { - 'seatbid': [ - { - 'bid': { - 'ad': '
test response
', - 'cpm': 5250, - 'creativeId': '5_1234ABCD', - 'currency': 'JPY', - 'height': 250, - 'meta': { - 'advertiserDomains': [ - 'adv.example' - ], - 'mediaType': 'banner', - 'networkId': 5 - }, - 'netRevenue': true, - 'requestId': '22a9457aed98a4', - 'transactionId': 'f18c078e-4d2a-4ecb-a886-2a0c52187213', - 'ttl': 60, - 'width': 300, - } - } - ] - }, - 'headers': {} - }; - const bids = spec.interpretResponse(serverResponse); - expect(bids[0]).to.deep.equal(serverResponse.body.seatbid[0].bid); - }); - }); -}); diff --git a/test/spec/modules/adtelligentBidAdapter_spec.js b/test/spec/modules/adtelligentBidAdapter_spec.js index 0acbaa06f5b..26284d539d4 100644 --- a/test/spec/modules/adtelligentBidAdapter_spec.js +++ b/test/spec/modules/adtelligentBidAdapter_spec.js @@ -11,13 +11,18 @@ const EXPECTED_ENDPOINTS = [ 'https://ghb.adtelligent.com/v2/auction/' ]; const aliasEP = { - 'janet_publisherSuffix': 'https://ghb.bidder.jmgads.com/v2/auction/', + 'appaloosa': 'https://ghb.hb.appaloosa.media/v2/auction/', + 'appaloosa_publisherSuffix': 'https://ghb.hb.appaloosa.media/v2/auction/', + 'onefiftytwomedia': 'https://ghb.ads.152media.com/v2/auction/', + 'navelix': 'https://ghb.hb.navelix.com/v2/auction/', + 'bidsxchange': 'https://ghb.hbd.bidsxchange.com/v2/auction/', 'streamkey': 'https://ghb.hb.streamkey.net/v2/auction/', 'janet': 'https://ghb.bidder.jmgads.com/v2/auction/', + 'pgam': 'https://ghb.pgamssp.com/v2/auction/', 'ocm': 'https://ghb.cenarius.orangeclickmedia.com/v2/auction/', + 'vidcrunchllc': 'https://ghb.platform.vidcrunch.com/v2/auction/', '9dotsmedia': 'https://ghb.platform.audiodots.com/v2/auction/', 'copper6': 'https://ghb.app.copper6.com/v2/auction/', - 'indicue': 'https://ghb.console.indicue.com/v2/auction/', }; const DEFAULT_ADATPER_REQ = { bidderCode: 'adtelligent' }; @@ -151,10 +156,6 @@ const displayBidderRequestWithConsents = { gdprApplies: true, consentString: 'test' }, - gppConsent: { - gppString: 'abc12345234', - applicableSections: [7, 8] - }, uspConsent: 'iHaveIt' }; @@ -363,10 +364,6 @@ describe('adtelligentBidAdapter', () => { expect(bidRequestWithPubSettingsData.GDPR).to.be.equal(1); expect(bidRequestWithPubSettingsData.GDPRConsent).to.be.equal(displayBidderRequestWithConsents.gdprConsent.consentString); }); - it('sets GPP flags', () => { - expect(bidRequestWithPubSettingsData.GPP).to.be.equal(displayBidderRequestWithConsents.gppConsent.gppString); - expect(bidRequestWithPubSettingsData.GPPSid).to.be.equal('7,8'); - }); it('sets USP', () => { expect(bidRequestWithPubSettingsData.USP).to.be.equal(displayBidderRequestWithConsents.uspConsent); }) diff --git a/test/spec/modules/adxcgBidAdapter_spec.js b/test/spec/modules/adxcgBidAdapter_spec.js index e07e3a6e5d4..65c7584b428 100644 --- a/test/spec/modules/adxcgBidAdapter_spec.js +++ b/test/spec/modules/adxcgBidAdapter_spec.js @@ -1,19 +1,835 @@ // jshint esversion: 6, es3: false, node: true -import { assert } from 'chai'; -import { spec } from 'modules/adxcgBidAdapter.js'; -import { config } from 'src/config.js'; -import { createEidsArray } from 'modules/userId/eids.js'; -/* eslint dot-notation:0, quote-props:0 */ -import { expect } from 'chai'; - -import { syncAddFPDToBidderRequest } from '../../helpers/fpd.js'; -import { deepClone } from '../../../src/utils'; - +import {assert} from 'chai'; +import {spec} from 'modules/adxcgBidAdapter.js'; +import {config} from 'src/config.js'; +import {createEidsArray} from 'modules/userId/eids.js'; const utils = require('src/utils'); describe('Adxcg adapter', function () { let bids = []; + describe('isBidRequestValid', function () { + let bid = { + 'bidder': 'adxcg', + 'params': { + 'adzoneid': '19910113' + } + }; + + it('should return true when required params found', function () { + assert(spec.isBidRequestValid(bid)); + + bid.params = { + adzoneid: 4332, + }; + assert(spec.isBidRequestValid(bid)); + }); + + it('should return false when required params are missing', function () { + bid.params = {}; + assert.isFalse(spec.isBidRequestValid(bid)); + + bid.params = { + mname: 'some-placement' + }; + assert.isFalse(spec.isBidRequestValid(bid)); + + bid.params = { + inv: 1234 + }; + assert.isFalse(spec.isBidRequestValid(bid)); + }); + }); + + describe('buildRequests', function () { + beforeEach(function () { + config.resetConfig(); + }); + it('should send request with correct structure', function () { + let validBidRequests = [{ + bidId: 'bidId', + params: { + adzoneid: '19910113' + } + }]; + let request = spec.buildRequests(validBidRequests, {refererInfo: {page: 'page', domain: 'localhost'}}); + + assert.equal(request.method, 'POST'); + assert.equal(request.url, 'https://pbc.adxcg.net/rtb/ortb/pbc?adExchangeId=1'); + assert.deepEqual(request.options, {contentType: 'application/json'}); + assert.ok(request.data); + }); + + describe('user privacy', function () { + it('should send GDPR Consent data to exchange if gdprApplies', function () { + let validBidRequests = [{bidId: 'bidId', params: {test: 1}}]; + let bidderRequest = { + gdprConsent: {gdprApplies: true, consentString: 'consentDataString'}, + refererInfo: {referer: 'page'} + }; + let request = JSON.parse(spec.buildRequests(validBidRequests, bidderRequest).data); + + assert.equal(request.user.ext.consent, bidderRequest.gdprConsent.consentString); + assert.equal(request.regs.ext.gdpr, bidderRequest.gdprConsent.gdprApplies); + assert.equal(typeof request.regs.ext.gdpr, 'number'); + }); + + it('should send gdpr as number', function () { + let validBidRequests = [{bidId: 'bidId', params: {test: 1}}]; + let bidderRequest = { + gdprConsent: {gdprApplies: true, consentString: 'consentDataString'}, + refererInfo: {referer: 'page'} + }; + let request = JSON.parse(spec.buildRequests(validBidRequests, bidderRequest).data); + + assert.equal(typeof request.regs.ext.gdpr, 'number'); + assert.equal(request.regs.ext.gdpr, 1); + }); + + it('should send CCPA Consent data to exchange', function () { + let validBidRequests = [{bidId: 'bidId', params: {test: 1}}]; + let bidderRequest = {uspConsent: '1YA-', refererInfo: {referer: 'page'}}; + let request = JSON.parse(spec.buildRequests(validBidRequests, bidderRequest).data); + + assert.equal(request.regs.ext.us_privacy, '1YA-'); + + bidderRequest = { + uspConsent: '1YA-', + gdprConsent: {gdprApplies: true, consentString: 'consentDataString'}, + refererInfo: {referer: 'page'} + }; + request = JSON.parse(spec.buildRequests(validBidRequests, bidderRequest).data); + + assert.equal(request.regs.ext.us_privacy, '1YA-'); + assert.equal(request.user.ext.consent, 'consentDataString'); + assert.equal(request.regs.ext.gdpr, 1); + }); + + it('should not send GDPR Consent data to adxcg if gdprApplies is undefined', function () { + let validBidRequests = [{ + bidId: 'bidId', + params: {siteId: 'siteId'} + }]; + let bidderRequest = { + gdprConsent: {gdprApplies: false, consentString: 'consentDataString'}, + refererInfo: {referer: 'page'} + }; + let request = JSON.parse(spec.buildRequests(validBidRequests, bidderRequest).data); + + assert.equal(request.user.ext.consent, 'consentDataString'); + assert.equal(request.regs.ext.gdpr, 0); + + bidderRequest = {gdprConsent: {consentString: 'consentDataString'}, refererInfo: {referer: 'page'}}; + request = JSON.parse(spec.buildRequests(validBidRequests, bidderRequest).data); + + assert.equal(request.user, undefined); + assert.equal(request.regs, undefined); + }); + it('should send default GDPR Consent data to exchange', function () { + let validBidRequests = [{ + bidId: 'bidId', + params: {siteId: 'siteId'} + }]; + let request = JSON.parse(spec.buildRequests(validBidRequests, {refererInfo: {referer: 'page'}}).data); + + assert.equal(request.user, undefined); + assert.equal(request.regs, undefined); + }); + }); + + it('should add test and is_debug to request, if test is set in parameters', function () { + let validBidRequests = [{ + bidId: 'bidId', + params: {test: 1} + }]; + let request = JSON.parse(spec.buildRequests(validBidRequests, {refererInfo: {referer: 'page'}}).data); + + assert.ok(request.is_debug); + assert.equal(request.test, 1); + }); + + it('should have default request structure', function () { + let keys = 'site,geo,device,source,ext,imp'.split(','); + let validBidRequests = [{ + bidId: 'bidId', + params: {siteId: 'siteId'} + }]; + let request = JSON.parse(spec.buildRequests(validBidRequests, {refererInfo: {referer: 'page'}}).data); + let data = Object.keys(request); + + assert.deepEqual(keys, data); + }); + + it('should set request keys correct values', function () { + let validBidRequests = [{ + bidId: 'bidId', + params: {siteId: 'siteId'}, + }]; + let request = JSON.parse(spec.buildRequests(validBidRequests, { + refererInfo: {referer: 'page'}, + ortb2: {source: {tid: 'tid'}} + }).data); + + assert.equal(request.source.tid, 'tid'); + assert.equal(request.source.fd, 1); + }); + + it('should send info about device', function () { + config.setConfig({ + device: {w: 100, h: 100} + }); + let validBidRequests = [{ + bidId: 'bidId', + params: {adzoneid: '1000'} + }]; + let request = JSON.parse(spec.buildRequests(validBidRequests, {refererInfo: {page: 'page', domain: 'localhost'}}).data); + + assert.equal(request.device.ua, navigator.userAgent); + assert.equal(request.device.w, 100); + assert.equal(request.device.h, 100); + }); + + it('should send app info', function () { + config.setConfig({ + app: {id: 'appid'}, + }); + const ortb2 = {app: {name: 'appname'}} + let validBidRequests = [{ + bidId: 'bidId', + params: {adzoneid: '1000'}, + ortb2 + }]; + let request = JSON.parse(spec.buildRequests(validBidRequests, {refererInfo: {referer: 'page'}, ortb2}).data); + + assert.equal(request.app.id, 'appid'); + assert.equal(request.app.name, 'appname'); + assert.equal(request.site, undefined); + }); + + it('should send info about the site', function () { + config.setConfig({ + site: { + id: '123123', + publisher: { + domain: 'publisher.domain.com' + } + }, + }); + const ortb2 = { + site: { + publisher: { + id: 4441, + name: 'publisher\'s name' + } + } + }; + + let validBidRequests = [{ + bidId: 'bidId', + params: {adzoneid: '1000'}, + ortb2 + }]; + let refererInfo = {page: 'page', domain: 'localhost'}; + let request = JSON.parse(spec.buildRequests(validBidRequests, {refererInfo, ortb2}).data); + + assert.deepEqual(request.site, { + domain: 'localhost', + id: '123123', + page: refererInfo.page, + publisher: { + domain: 'publisher.domain.com', + id: 4441, + name: 'publisher\'s name' + } + }); + }); + + it('should pass extended ids', function () { + let validBidRequests = [{ + bidId: 'bidId', + params: {}, + userIdAsEids: createEidsArray({ + tdid: 'TTD_ID_FROM_USER_ID_MODULE', + pubcid: 'pubCommonId_FROM_USER_ID_MODULE' + }) + }]; + + let request = JSON.parse(spec.buildRequests(validBidRequests, {refererInfo: {referer: 'page'}}).data); + assert.deepEqual(request.user.ext.eids, [ + {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}]} + ]); + }); + + it('should send currency if defined', function () { + config.setConfig({currency: {adServerCurrency: 'EUR'}}); + let validBidRequests = [{params: {}}]; + let refererInfo = {referer: 'page'}; + let request = JSON.parse(spec.buildRequests(validBidRequests, {refererInfo}).data); + + assert.deepEqual(request.cur, ['EUR']); + }); + + it('should pass supply chain object', function () { + let validBidRequests = [{ + bidId: 'bidId', + params: {}, + schain: { + validation: 'strict', + config: { + ver: '1.0' + } + } + }]; + + let request = JSON.parse(spec.buildRequests(validBidRequests, {refererInfo: {referer: 'page'}}).data); + assert.deepEqual(request.source.ext.schain, { + validation: 'strict', + config: { + ver: '1.0' + } + }); + }); + + describe('bids', function () { + it('should add more than one bid to the request', function () { + let validBidRequests = [{ + bidId: 'bidId', + params: {siteId: 'siteId'} + }, { + bidId: 'bidId2', + params: {siteId: 'siteId'} + }]; + let request = JSON.parse(spec.buildRequests(validBidRequests, {refererInfo: {referer: 'page'}}).data); + + assert.equal(request.imp.length, 2); + }); + it('should add incrementing values of id', function () { + let validBidRequests = [{ + bidId: 'bidId', + params: {adzoneid: '1000'}, + mediaTypes: {video: {}} + }, { + bidId: 'bidId2', + params: {adzoneid: '1000'}, + mediaTypes: {video: {}} + }, { + bidId: 'bidId3', + params: {adzoneid: '1000'}, + mediaTypes: {video: {}} + }]; + let imps = JSON.parse(spec.buildRequests(validBidRequests, {refererInfo: {referer: 'page'}}).data).imp; + + for (let i = 0; i < 3; i++) { + assert.equal(imps[i].id, i + 1); + } + }); + + it('should add adzoneid', function () { + let validBidRequests = [{bidId: 'bidId', params: {adzoneid: 1000}, mediaTypes: {video: {}}}, + {bidId: 'bidId2', params: {adzoneid: 1001}, mediaTypes: {video: {}}}, + {bidId: 'bidId3', params: {adzoneid: 1002}, mediaTypes: {video: {}}}]; + let imps = JSON.parse(spec.buildRequests(validBidRequests, {refererInfo: {referer: 'page'}}).data).imp; + for (let i = 0; i < 3; i++) { + assert.equal(imps[i].tagid, validBidRequests[i].params.adzoneid); + } + }); + + describe('price floors', function () { + it('should not add if floors module not configured', function () { + const validBidRequests = [{bidId: 'bidId', params: {adzoneid: 1000}, mediaTypes: {video: {}}}]; + let imp = getRequestImps(validBidRequests)[0]; + + assert.equal(imp.bidfloor, undefined); + assert.equal(imp.bidfloorcur, undefined); + }); + + it('should not add if floor price not defined', function () { + const validBidRequests = [getBidWithFloor()]; + let imp = getRequestImps(validBidRequests)[0]; + + assert.equal(imp.bidfloor, undefined); + assert.equal(imp.bidfloorcur, 'USD'); + }); + + it('should request floor price in adserver currency', function () { + config.setConfig({currency: {adServerCurrency: 'DKK'}}); + const validBidRequests = [getBidWithFloor()]; + let imp = getRequestImps(validBidRequests)[0]; + + assert.equal(imp.bidfloor, undefined); + assert.equal(imp.bidfloorcur, 'DKK'); + }); + + it('should add correct floor values', function () { + const expectedFloors = [1, 1.3, 0.5]; + const validBidRequests = expectedFloors.map(getBidWithFloor); + let imps = getRequestImps(validBidRequests); + + expectedFloors.forEach((floor, index) => { + assert.equal(imps[index].bidfloor, floor); + assert.equal(imps[index].bidfloorcur, 'USD'); + }); + }); + + function getBidWithFloor(floor) { + return { + params: {adzoneid: 1}, + mediaTypes: {video: {}}, + getFloor: ({currency}) => { + return { + currency: currency, + floor + }; + } + }; + } + }); + + describe('multiple media types', function () { + it('should use all configured media types for bidding', function () { + let validBidRequests = [{ + bidId: 'bidId', + params: {adzoneid: 1000}, + mediaTypes: { + banner: { + sizes: [[100, 100], [200, 300]] + }, + video: {} + } + }, { + bidId: 'bidId1', + params: {adzoneid: 1000}, + mediaTypes: { + video: {}, + native: {} + } + }, { + bidId: 'bidId2', + params: {adzoneid: 1000}, + nativeParams: { + title: {required: true, len: 140} + }, + mediaTypes: { + banner: { + sizes: [[100, 100], [200, 300]] + }, + native: {}, + video: {} + } + }]; + let [first, second, third] = JSON.parse(spec.buildRequests(validBidRequests, {refererInfo: {referer: 'page'}}).data).imp; + + assert.ok(first.banner); + assert.ok(first.video); + assert.equal(first.native, undefined); + + assert.ok(second.video); + assert.equal(second.banner, undefined); + assert.equal(second.native, undefined); + + assert.ok(third.native); + assert.ok(third.video); + assert.ok(third.banner); + }); + }); + + describe('banner', function () { + it('should convert sizes to openrtb format', function () { + let validBidRequests = [{ + bidId: 'bidId', + params: {adzoneid: 1000}, + mediaTypes: { + banner: { + sizes: [[100, 100], [200, 300]] + } + } + }]; + let {banner} = JSON.parse(spec.buildRequests(validBidRequests, {refererInfo: {referer: 'page'}}).data).imp[0]; + assert.deepEqual(banner, { + format: [{w: 100, h: 100}, {w: 200, h: 300}] + }); + }); + }); + + describe('video', function () { + it('should pass video mediatype config', function () { + let validBidRequests = [{ + bidId: 'bidId', + params: {adzoneid: 1000}, + mediaTypes: { + video: { + playerSize: [640, 480], + context: 'outstream', + mimes: ['video/mp4'] + } + } + }]; + let {video} = JSON.parse(spec.buildRequests(validBidRequests, {refererInfo: {referer: 'page'}}).data).imp[0]; + assert.deepEqual(video, { + playerSize: [640, 480], + context: 'outstream', + mimes: ['video/mp4'] + }); + }); + }); + + describe('native', function () { + describe('assets', function () { + it('should set correct asset id', function () { + let validBidRequests = [{ + bidId: 'bidId', + params: {adzoneid: 1000}, + nativeParams: { + title: {required: true, len: 140}, + image: {required: false, wmin: 836, hmin: 627, w: 325, h: 300, mimes: ['image/jpg', 'image/gif']}, + body: {len: 140} + } + }]; + let nativeRequest = JSON.parse(spec.buildRequests(validBidRequests, {refererInfo: {referer: 'page'}}).data).imp[0].native.request; + let assets = JSON.parse(nativeRequest).assets; + + assert.equal(assets[0].id, 0); + assert.equal(assets[1].id, 3); + assert.equal(assets[2].id, 4); + }); + it('should add required key if it is necessary', function () { + let validBidRequests = [{ + bidId: 'bidId', + params: {adzoneid: 1000}, + nativeParams: { + title: {required: true, len: 140}, + image: {required: false, wmin: 836, hmin: 627, w: 325, h: 300, mimes: ['image/jpg', 'image/gif']}, + body: {len: 140}, + sponsoredBy: {required: true, len: 140} + } + }]; + + let nativeRequest = JSON.parse(spec.buildRequests(validBidRequests, {refererInfo: {referer: 'page'}}).data).imp[0].native.request; + let assets = JSON.parse(nativeRequest).assets; + + assert.equal(assets[0].required, 1); + assert.ok(!assets[1].required); + assert.ok(!assets[2].required); + assert.equal(assets[3].required, 1); + }); + + it('should map img and data assets', function () { + let validBidRequests = [{ + bidId: 'bidId', + params: {adzoneid: 1000}, + nativeParams: { + title: {required: true, len: 140}, + image: {required: true, sizes: [150, 50]}, + icon: {required: false, sizes: [50, 50]}, + body: {required: false, len: 140}, + sponsoredBy: {required: true}, + cta: {required: false}, + clickUrl: {required: false} + } + }]; + + let nativeRequest = JSON.parse(spec.buildRequests(validBidRequests, {refererInfo: {referer: 'page'}}).data).imp[0].native.request; + let assets = JSON.parse(nativeRequest).assets; + assert.ok(assets[0].title); + assert.equal(assets[0].title.len, 140); + assert.deepEqual(assets[1].img, {type: 3, w: 150, h: 50}); + assert.deepEqual(assets[2].img, {type: 1, w: 50, h: 50}); + assert.deepEqual(assets[3].data, {type: 2, len: 140}); + assert.deepEqual(assets[4].data, {type: 1}); + assert.deepEqual(assets[5].data, {type: 12}); + assert.ok(!assets[6]); + }); + + describe('icon/image sizing', function () { + it('should flatten sizes and utilise first pair', function () { + const validBidRequests = [{ + bidId: 'bidId', + params: {adzoneid: 1000}, + nativeParams: { + image: { + sizes: [[200, 300], [100, 200]] + }, + } + }]; + + let nativeRequest = JSON.parse(spec.buildRequests(validBidRequests, {refererInfo: {referer: 'page'}}).data).imp[0].native.request; + let assets = JSON.parse(nativeRequest).assets; + assert.ok(assets[0].img); + assert.equal(assets[0].img.w, 200); + assert.equal(assets[0].img.h, 300); + }); + }); + + it('should utilise aspect_ratios', function () { + const validBidRequests = [{ + bidId: 'bidId', + params: {adzoneid: 1000}, + nativeParams: { + image: { + aspect_ratios: [{ + min_width: 100, + ratio_height: 3, + ratio_width: 1 + }] + }, + icon: { + aspect_ratios: [{ + min_width: 10, + ratio_height: 5, + ratio_width: 2 + }] + } + } + }]; + + let nativeRequest = JSON.parse(spec.buildRequests(validBidRequests, {refererInfo: {referer: 'page'}}).data).imp[0].native.request; + let assets = JSON.parse(nativeRequest).assets; + assert.ok(assets[0].img); + assert.equal(assets[0].img.wmin, 100); + assert.equal(assets[0].img.hmin, 300); + + assert.ok(assets[1].img); + assert.equal(assets[1].img.wmin, 10); + assert.equal(assets[1].img.hmin, 25); + }); + + it('should not throw error if aspect_ratios config is not defined', function () { + const validBidRequests = [{ + bidId: 'bidId', + params: {adzoneid: 1000}, + nativeParams: { + image: { + aspect_ratios: [] + }, + icon: { + aspect_ratios: [] + } + } + }]; + + assert.doesNotThrow(() => spec.buildRequests(validBidRequests, {refererInfo: {referer: 'page'}})); + }); + }); + + it('should expect any dimensions if min_width not passed', function () { + const validBidRequests = [{ + bidId: 'bidId', + params: {adzoneid: 1000}, + nativeParams: { + image: { + aspect_ratios: [{ + ratio_height: 3, + ratio_width: 1 + }] + } + } + }]; + + let nativeRequest = JSON.parse(spec.buildRequests(validBidRequests, {refererInfo: {referer: 'page'}}).data).imp[0].native.request; + let assets = JSON.parse(nativeRequest).assets; + assert.ok(assets[0].img); + assert.equal(assets[0].img.wmin, 0); + assert.equal(assets[0].img.hmin, 0); + assert.ok(!assets[1]); + }); + }); + }); + + function getRequestImps(validBidRequests) { + return JSON.parse(spec.buildRequests(validBidRequests, {refererInfo: {referer: 'page'}}).data).imp; + } + }); + + describe('interpretResponse', function () { + it('should return if no body in response', function () { + let serverResponse = {}; + let bidRequest = {}; + + assert.ok(!spec.interpretResponse(serverResponse, bidRequest)); + }); + it('should return more than one bids', function () { + let serverResponse = { + body: { + seatbid: [{ + bid: [{ + impid: '1', + native: {ver: '1.1', link: {url: 'link'}, assets: [{id: 1, title: {text: 'Asset title text'}}]} + }] + }, { + bid: [{ + impid: '2', + native: {ver: '1.1', link: {url: 'link'}, assets: [{id: 1, data: {value: 'Asset title text'}}]} + }] + }] + } + }; + let bidRequest = { + data: {}, + bids: [ + { + bidId: 'bidId1', + params: {adzoneid: 1000}, + nativeParams: { + title: {required: true, len: 140}, + image: {required: false, wmin: 836, hmin: 627, w: 325, h: 300, mimes: ['image/jpg', 'image/gif']}, + body: {len: 140} + } + }, + { + bidId: 'bidId2', + params: {adzoneid: 1000}, + nativeParams: { + title: {required: true, len: 140}, + image: {required: false, wmin: 836, hmin: 627, w: 325, h: 300, mimes: ['image/jpg', 'image/gif']}, + body: {len: 140} + } + } + ] + }; + + bids = spec.interpretResponse(serverResponse, bidRequest); + assert.equal(spec.interpretResponse(serverResponse, bidRequest).length, 2); + }); + + it('should set correct values to bid', function () { + let nativeExample1 = { + assets: [], + link: {url: 'link'}, + imptrackers: ['imptrackers url1', 'imptrackers url2'] + } + + let serverResponse = { + body: { + id: null, + bidid: null, + seatbid: [{ + bid: [ + { + impid: '1', + price: 93.1231, + crid: '12312312', + adm: JSON.stringify(nativeExample1), + dealid: 'deal-id', + adomain: ['demo.com'], + ext: { + crType: 'native', + advertiser_id: 'adv1', + advertiser_name: 'advname', + agency_name: 'agname', + mediaType: 'native' + } + } + ] + }], + cur: 'EUR' + } + }; + let bidRequest = { + data: {}, + bids: [ + { + bidId: 'bidId1', + params: {adzoneid: 1000}, + nativeParams: { + title: {required: true, len: 140}, + image: {required: false, wmin: 836, hmin: 627, w: 325, h: 300, mimes: ['image/jpg', 'image/gif']}, + body: {len: 140} + } + } + ] + }; + + const bids = spec.interpretResponse(serverResponse, bidRequest); + const bid = serverResponse.body.seatbid[0].bid[0]; + assert.deepEqual(bids[0].requestId, bidRequest.bids[0].bidId); + assert.deepEqual(bids[0].cpm, bid.price); + assert.deepEqual(bids[0].creativeId, bid.crid); + assert.deepEqual(bids[0].ttl, 300); + assert.deepEqual(bids[0].netRevenue, false); + assert.deepEqual(bids[0].currency, serverResponse.body.cur); + assert.deepEqual(bids[0].mediaType, 'native'); + assert.deepEqual(bids[0].meta.mediaType, 'native'); + assert.deepEqual(bids[0].meta.advertiserDomains, ['demo.com']); + + assert.deepEqual(bids[0].meta.advertiserName, 'advname'); + assert.deepEqual(bids[0].meta.agencyName, 'agname'); + + assert.deepEqual(bids[0].dealId, 'deal-id'); + }); + + it('should return empty when there is no bids in response', function () { + const serverResponse = { + body: { + id: null, + bidid: null, + seatbid: [{bid: []}], + cur: 'EUR' + } + }; + let bidRequest = { + data: {}, + bids: [{bidId: 'bidId1'}] + }; + const result = spec.interpretResponse(serverResponse, bidRequest)[0]; + assert.ok(!result); + }); + + describe('banner', function () { + it('should set ad content on response', function () { + let serverResponse = { + body: { + seatbid: [{ + bid: [{impid: '1', adm: '', ext: {crType: 'banner'}}] + }] + } + }; + let bidRequest = { + data: {}, + bids: [ + { + bidId: 'bidId1', + params: {adzoneid: 1000} + } + ] + }; + + bids = spec.interpretResponse(serverResponse, bidRequest); + assert.equal(bids.length, 1); + assert.equal(bids[0].ad, ''); + assert.equal(bids[0].mediaType, 'banner'); + assert.equal(bids[0].meta.mediaType, 'banner'); + }); + }); + + describe('video', function () { + it('should set vastXml on response', function () { + let serverResponse = { + body: { + seatbid: [{ + bid: [{impid: '1', adm: '', ext: {crType: 'video'}}] + }] + } + }; + let bidRequest = { + data: {}, + bids: [ + { + bidId: 'bidId1', + params: {adzoneid: 1000} + } + ] + }; + + bids = spec.interpretResponse(serverResponse, bidRequest); + assert.equal(bids.length, 1); + assert.equal(bids[0].vastXml, ''); + assert.equal(bids[0].mediaType, 'video'); + assert.equal(bids[0].meta.mediaType, 'video'); + }); + }); + }); + describe('getUserSyncs', function () { const usersyncUrl = 'https://usersync-url.com'; beforeEach(() => { @@ -30,55 +846,55 @@ describe('Adxcg adapter', function () { }) it('should return user sync if pixel enabled with adxcg config', function () { - const ret = spec.getUserSyncs({ pixelEnabled: true }) - expect(ret).to.deep.equal([{ type: 'image', url: usersyncUrl }]) + const ret = spec.getUserSyncs({pixelEnabled: true}) + expect(ret).to.deep.equal([{type: 'image', url: usersyncUrl}]) }) 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 pass GDPR consent', function () { - expect(spec.getUserSyncs({ pixelEnabled: true }, {}, { gdprApplies: true, consentString: 'foo' }, undefined)).to.deep.equal([{ + it('should pass GDPR consent', function() { + expect(spec.getUserSyncs({ pixelEnabled: true }, {}, {gdprApplies: true, consentString: 'foo'}, undefined)).to.deep.equal([{ type: 'image', url: `${usersyncUrl}?gdpr=1&gdpr_consent=foo` }]); - expect(spec.getUserSyncs({ pixelEnabled: true }, {}, { gdprApplies: false, consentString: 'foo' }, undefined)).to.deep.equal([{ + expect(spec.getUserSyncs({ pixelEnabled: true }, {}, {gdprApplies: false, consentString: 'foo'}, undefined)).to.deep.equal([{ type: 'image', url: `${usersyncUrl}?gdpr=0&gdpr_consent=foo` }]); - expect(spec.getUserSyncs({ pixelEnabled: true }, {}, { gdprApplies: true, consentString: undefined }, undefined)).to.deep.equal([{ + expect(spec.getUserSyncs({ pixelEnabled: true }, {}, {gdprApplies: true, consentString: undefined}, undefined)).to.deep.equal([{ type: 'image', url: `${usersyncUrl}?gdpr=1&gdpr_consent=` }]); }); - it('should pass US consent', function () { + it('should pass US consent', function() { expect(spec.getUserSyncs({ pixelEnabled: true }, {}, undefined, '1NYN')).to.deep.equal([{ type: 'image', url: `${usersyncUrl}?us_privacy=1NYN` }]); }); - it('should pass GDPR and US consent', function () { - expect(spec.getUserSyncs({ pixelEnabled: true }, {}, { gdprApplies: true, consentString: 'foo' }, '1NYN')).to.deep.equal([{ + it('should pass GDPR and US consent', function() { + expect(spec.getUserSyncs({ pixelEnabled: true }, {}, {gdprApplies: true, consentString: 'foo'}, '1NYN')).to.deep.equal([{ type: 'image', url: `${usersyncUrl}?gdpr=1&gdpr_consent=foo&us_privacy=1NYN` }]); }); }); - describe('onBidWon', function () { - beforeEach(function () { + describe('onBidWon', function() { + beforeEach(function() { sinon.stub(utils, 'triggerPixel'); }); - afterEach(function () { + afterEach(function() { utils.triggerPixel.restore(); }); - it('Should trigger pixel if bid nurl', function () { + it('Should trigger pixel if bid nurl', function() { const bid = { nurl: 'http://example.com/win/${AUCTION_PRICE}', cpm: 2.1, @@ -88,510 +904,4 @@ describe('Adxcg adapter', function () { expect(utils.triggerPixel.callCount).to.equal(1) }) }) - - it('should return just to have at least 1 karma test ok', function () { - assert(true); - }); -}); - -describe('adxcg v8 oRtbConverter Adapter Tests', function () { - const slotConfigs = [{ - placementCode: '/DfpAccount1/slot1', - mediaTypes: { - banner: { - sizes: [[728, 90], [160, 600]] - } - }, - bidId: 'bid12345', - params: { - cp: 'p10000', - ct: 't10000', - cf: '300x250', - adzoneid: '77' - } - }, { - placementCode: '/DfpAccount2/slot2', - mediaTypes: { - banner: { - sizes: [[728, 90]] - } - }, - bidId: 'bid23456', - params: { - cp: 'p10000', - ct: 't20000', - cf: '728x90', - adzoneid: '77' - } - }]; - const nativeOrtbRequest = { - assets: [{ - id: 1, - required: 1, - img: { - type: 3, - w: 150, - h: 50, - } - }, - { - id: 2, - required: 1, - title: { - len: 80 - } - }, - { - id: 3, - required: 0, - data: { - type: 1 - } - }] - }; - const nativeSlotConfig = [{ - placementCode: '/DfpAccount1/slot3', - bidId: 'bid12345', - mediaTypes: { - native: { - sendTargetingKeys: false, - ortb: nativeOrtbRequest - } - }, - nativeOrtbRequest, - params: { - cp: 'p10000', - ct: 't10000', - adzoneid: '77' - } - }]; - const videoSlotConfig = [{ - placementCode: '/DfpAccount1/slotVideo', - bidId: 'bid12345', - mediaTypes: { - video: { - playerSize: [400, 300], - w: 400, - h: 300, - minduration: 5, - maxduration: 10, - startdelay: 0, - skip: 1, - minbitrate: 200, - protocols: [1, 2, 4] - } - }, - params: { - cp: 'p10000', - ct: 't10000', - adzoneid: '77' - } - }]; - const additionalParamsConfig = [{ - placementCode: '/DfpAccount1/slot1', - mediaTypes: { - banner: { - sizes: [[1, 1]] - } - }, - bidId: 'bid12345', - params: { - cp: 'p10000', - ct: 't10000', - cf: '1x1', - adzoneid: '77', - extra_key1: 'extra_val1', - extra_key2: 12345, - extra_key3: { - key1: 'val1', - key2: 23456, - }, - extra_key4: [1, 2, 3] - } - }]; - - const schainParamsSlotConfig = [{ - placementCode: '/DfpAccount1/slot1', - mediaTypes: { - banner: { - sizes: [[1, 1]] - } - }, - bidId: 'bid12345', - params: { - cp: 'p10000', - ct: 't10000', - cf: '1x1', - adzoneid: '77', - bcat: ['IAB-1', 'IAB-20'], - battr: [1, 2, 3], - bidfloor: 1.5, - badv: ['cocacola.com', 'lays.com'] - }, - 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 = { - refererInfo: { - page: 'https://publisher.com/home', - ref: 'https://referrer' - } - }; - - it('Verify build request', function () { - const request = spec.buildRequests(slotConfigs, syncAddFPDToBidderRequest(bidderRequest)); - expect(request.url).to.equal('https://pbc.adxcg.net/rtb/ortb/pbc?adExchangeId=1'); - expect(request.method).to.equal('POST'); - const ortbRequest = request.data; - // site object - expect(ortbRequest.site).to.not.equal(null); - expect(ortbRequest.site.publisher).to.not.equal(null); - // expect(ortbRequest.site.publisher.id).to.equal('p10000'); - expect(ortbRequest.site.page).to.equal('https://publisher.com/home'); - expect(ortbRequest.imp).to.have.lengthOf(2); - // device object - expect(ortbRequest.device).to.not.equal(null); - expect(ortbRequest.device.ua).to.equal(navigator.userAgent); - // 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 }]); - // 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 }]); - }); - - it('Verify parse response', function () { - const request = spec.buildRequests(slotConfigs, syncAddFPDToBidderRequest(bidderRequest)); - const ortbRequest = request.data; - const ortbResponse = { - seatbid: [{ - bid: [{ - impid: ortbRequest.imp[0].id, - price: 1.25, - adm: 'This is an Ad', - crid: 'Creative#123', - mtype: 1, - w: 300, - h: 250, - exp: 20, - adomain: ['advertiser.com'] - }] - }] - }; - const bids = spec.interpretResponse({ body: ortbResponse }, request); - expect(bids).to.have.lengthOf(1); - // verify first bid - const bid = bids[0]; - expect(bid.cpm).to.equal(1.25); - expect(bid.ad).to.equal('This is an Ad'); - expect(bid.width).to.equal(300); - expect(bid.height).to.equal(250); - expect(bid.creative_id).to.equal('Creative#123'); - expect(bid.creativeId).to.equal('Creative#123'); - expect(bid.netRevenue).to.equal(true); - expect(bid.currency).to.equal('EUR'); - expect(bid.ttl).to.equal(20); - expect(bid.meta).to.not.be.null; - expect(bid.meta.advertiserDomains).to.eql(['advertiser.com']); - }); - - it('Verify full passback', function () { - const request = spec.buildRequests(slotConfigs, bidderRequest); - const bids = spec.interpretResponse({ body: null }, request) - expect(bids).to.have.lengthOf(0); - }); - - if (FEATURES.NATIVE) { - it('Verify Native request', function () { - const request = spec.buildRequests(nativeSlotConfig, syncAddFPDToBidderRequest(bidderRequest)); - expect(request.url).to.equal('https://pbc.adxcg.net/rtb/ortb/pbc?adExchangeId=1'); - expect(request.method).to.equal('POST'); - const ortbRequest = request.data; - // native impression - expect(ortbRequest.imp[0].tagid).to.equal('77'); - expect(ortbRequest.imp[0].banner).to.be.undefined; - const nativePart = ortbRequest.imp[0]['native']; - expect(nativePart).to.not.equal(null); - expect(nativePart.request).to.not.equal(null); - // native request assets - const nativeRequest = JSON.parse(ortbRequest.imp[0]['native'].request); - expect(nativeRequest).to.not.equal(null); - expect(nativeRequest.assets).to.have.lengthOf(3); - // image asset - expect(nativeRequest.assets[0].id).to.equal(1); - expect(nativeRequest.assets[0].required).to.equal(1); - expect(nativeRequest.assets[0].title).to.be.undefined; - expect(nativeRequest.assets[0].img).to.not.equal(null); - expect(nativeRequest.assets[0].img.w).to.equal(150); - expect(nativeRequest.assets[0].img.h).to.equal(50); - expect(nativeRequest.assets[0].img.type).to.equal(3); - // title asset - expect(nativeRequest.assets[1].id).to.equal(2); - expect(nativeRequest.assets[1].required).to.equal(1); - expect(nativeRequest.assets[1].title).to.not.equal(null); - expect(nativeRequest.assets[1].title.len).to.equal(80); - // data asset - expect(nativeRequest.assets[2].id).to.equal(3); - expect(nativeRequest.assets[2].required).to.equal(0); - expect(nativeRequest.assets[2].title).to.be.undefined; - expect(nativeRequest.assets[2].data).to.not.equal(null); - expect(nativeRequest.assets[2].data.type).to.equal(1); - }); - - it('Verify Native response', function () { - const request = spec.buildRequests(nativeSlotConfig, syncAddFPDToBidderRequest(bidderRequest)); - expect(request.url).to.equal('https://pbc.adxcg.net/rtb/ortb/pbc?adExchangeId=1'); - expect(request.method).to.equal('POST'); - 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' } } - ], - link: { url: 'https://brand.clickme.com/' }, - imptrackers: ['https://imp1.trackme.com/', 'https://imp1.contextweb.com/'] - - }; - const ortbResponse = { - seatbid: [{ - bid: [{ - impid: ortbRequest.imp[0].id, - price: 1.25, - adm: JSON.stringify(nativeResponse), - mtype: 4 - }] - }] - }; - const bids = spec.interpretResponse({ body: ortbResponse }, request); - // verify bid - const bid = bids[0]; - expect(bid.cpm).to.equal(1.25); - expect(bid.requestId).to.equal('bid12345'); - expect(bid.ad).to.be.undefined; - expect(bid.mediaType).to.equal('native'); - expect(bid['native']).to.not.be.null; - expect(bid['native'].ortb).to.not.be.null; - const nativeBid = bid['native'].ortb; - expect(nativeBid.assets).to.have.lengthOf(3); - expect(nativeBid.assets[0].id).to.equal(1); - expect(nativeBid.assets[0].img).to.not.be.null; - expect(nativeBid.assets[0].img.type).to.equal(3); - expect(nativeBid.assets[0].img.url).to.equal('https://images.cdn.brand.com/123'); - expect(nativeBid.assets[1].id).to.equal(2); - expect(nativeBid.assets[1].title).to.not.be.null; - expect(nativeBid.assets[1].title.text).to.equal('Ad Title'); - expect(nativeBid.assets[2].id).to.equal(3); - expect(nativeBid.assets[2].data).to.not.be.null; - expect(nativeBid.assets[2].data.type).to.equal(1); - expect(nativeBid.assets[2].data.value).to.equal('Sponsored By: Brand'); - expect(nativeBid.link).to.not.be.null; - expect(nativeBid.link.url).to.equal('https://brand.clickme.com/'); - expect(nativeBid.imptrackers).to.have.lengthOf(2); - expect(nativeBid.imptrackers[0]).to.equal('https://imp1.trackme.com/'); - expect(nativeBid.imptrackers[1]).to.equal('https://imp1.contextweb.com/'); - }); - } - - it('Verifies bidder code', function () { - expect(spec.code).to.equal('adxcg'); - }); - - it('Verifies bidder aliases', function () { - expect(spec.aliases).to.have.lengthOf(1); - expect(spec.aliases[0]).to.equal('mediaopti'); - }); - - it('Verifies supported media types', function () { - expect(spec.supportedMediaTypes).to.have.lengthOf(3); - expect(spec.supportedMediaTypes[0]).to.equal('banner'); - expect(spec.supportedMediaTypes[1]).to.equal('native'); - expect(spec.supportedMediaTypes[2]).to.equal('video'); - }); - - if (FEATURES.VIDEO) { - it('Verify Video request', function () { - const request = spec.buildRequests(videoSlotConfig, syncAddFPDToBidderRequest(bidderRequest)); - expect(request.url).to.equal('https://pbc.adxcg.net/rtb/ortb/pbc?adExchangeId=1'); - expect(request.method).to.equal('POST'); - const ortbRequest = request.data; - expect(ortbRequest).to.not.equal(null); - expect(ortbRequest.imp).to.have.lengthOf(1); - expect(ortbRequest.imp[0].video).to.not.be.null; - expect(ortbRequest.imp[0].native).to.be.undefined; - expect(ortbRequest.imp[0].banner).to.be.undefined; - expect(ortbRequest.imp[0].video.w).to.equal(400); - expect(ortbRequest.imp[0].video.h).to.equal(300); - expect(ortbRequest.imp[0].video.minduration).to.equal(5); - expect(ortbRequest.imp[0].video.maxduration).to.equal(10); - expect(ortbRequest.imp[0].video.startdelay).to.equal(0); - expect(ortbRequest.imp[0].video.skip).to.equal(1); - expect(ortbRequest.imp[0].video.minbitrate).to.equal(200); - expect(ortbRequest.imp[0].video.protocols).to.eql([1, 2, 4]); - }); - } - - it('Verify extra parameters', function () { - let request = spec.buildRequests(additionalParamsConfig, syncAddFPDToBidderRequest(bidderRequest)); - let ortbRequest = request.data; - expect(ortbRequest).to.not.equal(null); - expect(ortbRequest.imp).to.have.lengthOf(1); - expect(ortbRequest.imp[0].ext).to.not.equal(null); - expect(ortbRequest.imp[0].ext.prebid).to.not.equal(null); - expect(ortbRequest.imp[0].ext.prebid).to.not.be.null; - expect(ortbRequest.imp[0].ext.prebid.extra_key1).to.equal('extra_val1'); - expect(ortbRequest.imp[0].ext.prebid.extra_key2).to.equal(12345); - expect(ortbRequest.imp[0].ext.prebid.extra_key3).to.not.be.null; - expect(ortbRequest.imp[0].ext.prebid.extra_key3.key1).to.equal('val1'); - expect(ortbRequest.imp[0].ext.prebid.extra_key3.key2).to.equal(23456); - expect(ortbRequest.imp[0].ext.prebid.extra_key4).to.eql([1, 2, 3]); - expect(Object.keys(ortbRequest.imp[0].ext.prebid)).to.eql(['adzoneid', 'extra_key1', 'extra_key2', 'extra_key3', 'extra_key4']); - // attempting with a configuration with no unknown params. - request = spec.buildRequests(videoSlotConfig, bidderRequest); - ortbRequest = request.data; - expect(ortbRequest).to.not.equal(null); - expect(ortbRequest.imp).to.have.lengthOf(1); - // expect(ortbRequest.imp[0].ext).to.be.undefined; - }); - - it('Verify user level first party data', function () { - const bidderRequest = { - refererInfo: { - page: 'https://publisher.com/home', - ref: 'https://referrer' - }, - gdprConsent: { - gdprApplies: true, - consentString: 'serialized_gpdr_data' - }, - ortb2: { - user: { - yob: 1985, - gender: 'm', - ext: { - data: { - registered: true, - interests: ['cars'] - } - } - } - } - }; - let request = spec.buildRequests(slotConfigs, syncAddFPDToBidderRequest(bidderRequest)); - let ortbRequest = request.data; - expect(ortbRequest).to.not.equal(null); - expect(ortbRequest.user).to.not.equal(null); - }); - - it('Verify site level first party data', function () { - const bidderRequest = { - ortb2: { - site: { - content: { - data: [{ - name: 'www.iris.com', - ext: { - segtax: 500, - cids: ['iris_c73g5jq96mwso4d8'] - } - }] - }, - page: 'http://pub.com/news', - ref: 'http://google.com', - publisher: { - domain: 'pub.com' - } - } - } - }; - let request = spec.buildRequests(slotConfigs, syncAddFPDToBidderRequest(bidderRequest)); - let ortbRequest = request.data; - expect(ortbRequest).to.not.equal(null); - expect(ortbRequest.site).to.not.equal(null); - expect(ortbRequest.site).to.deep.equal({ - content: { - data: [{ - name: 'www.iris.com', - ext: { - segtax: 500, - cids: ['iris_c73g5jq96mwso4d8'] - } - }] - }, - page: 'http://pub.com/news', - ref: 'http://google.com', - publisher: { - // id: 'p10000', - domain: 'pub.com' - } - }); - }); - - it('Verify impression/slot level first party data', function () { - const bidderRequests = [{ - placementCode: '/DfpAccount1/slot1', - mediaTypes: { - banner: { - sizes: [[1, 1]] - } - }, - bidId: 'bid12345', - params: { - cp: 'p10000', - ct: 't10000', - adzoneid: '77', - extra_key1: 'extra_val1', - extra_key2: 12345 - }, - ortb2Imp: { - ext: { - data: { - pbadslot: 'homepage-top-rect', - adUnitSpecificAttribute: '123' - } - } - } - }]; - let request = spec.buildRequests(bidderRequests, bidderRequest); - let ortbRequest = request.data; - expect(ortbRequest).to.not.equal(null); - expect(ortbRequest.imp).to.not.equal(null); - expect(ortbRequest.imp).to.have.lengthOf(1); - expect(ortbRequest.imp[0].ext).to.not.equal(null); - expect(ortbRequest.imp[0].ext).to.deep.equal({ - prebid: { - adzoneid: '77', - extra_key1: 'extra_val1', - extra_key2: 12345 - }, - data: { - pbadslot: 'homepage-top-rect', - adUnitSpecificAttribute: '123' - } - }); - }); - - it('Verify bid request timeouts', function () { - const mkRequest = (bidderRequest) => spec.buildRequests(slotConfigs, bidderRequest).data; - // assert default is used when no bidderRequest.timeout value is available - expect(mkRequest(bidderRequest).tmax).to.equal(500) - - // assert bidderRequest value is used when available - expect(mkRequest(Object.assign({}, { timeout: 6000 }, bidderRequest)).tmax).to.equal(6000) - }); }); diff --git a/test/spec/modules/adyoulikeBidAdapter_spec.js b/test/spec/modules/adyoulikeBidAdapter_spec.js index ffd6729397a..7310f736f7e 100644 --- a/test/spec/modules/adyoulikeBidAdapter_spec.js +++ b/test/spec/modules/adyoulikeBidAdapter_spec.js @@ -2,7 +2,6 @@ import { expect } from 'chai'; import { spec } from 'modules/adyoulikeBidAdapter.js'; import { newBidder } from 'src/adapters/bidderFactory.js'; -import { config } from 'src/config.js'; describe('Adyoulike Adapter', function () { const canonicalUrl = 'https://canonical.url/?t=%26'; @@ -708,21 +707,32 @@ describe('Adyoulike Adapter', function () { expect(payload.gdprConsent.consentRequired).to.be.null; }); - it('should add eids eids information to the request', function () { - let bidRequest = bidRequestWithSinglePlacement; - bidRequest[0].userIdAsEids = [{ - 'source': 'pubcid.org', - 'uids': [{ - 'atype': 1, - 'id': '01EAJWWNEPN3CYMM5N8M5VXY22' - }] - }] - - const request = spec.buildRequests(bidRequest, bidderRequest); + it('should add userid eids information to the request', function () { + let bidderRequest = { + 'auctionId': '1d1a030790a475', + 'bidderRequestId': '22edbae2733bf6', + 'timeout': 3000, + 'userIdAsEids': + [ + { + 'source': 'pubcid.org', + 'uids': [ + { + 'atype': 1, + 'id': '01EAJWWNEPN3CYMM5N8M5VXY22' + } + ] + } + ] + }; + + bidderRequest.bids = bidRequestWithSinglePlacement; + + const request = spec.buildRequests(bidRequestWithSinglePlacement, bidderRequest); const payload = JSON.parse(request.data); - expect(payload.eids).to.exist; - expect(payload.eids).to.deep.equal(bidRequest[0].userIdAsEids); + expect(payload.userId).to.exist; + expect(payload.userId).to.deep.equal(bidderRequest.userIdAsEids); }); it('sends bid request to endpoint with single placement', function () { @@ -888,115 +898,4 @@ describe('Adyoulike Adapter', function () { expect(spec.gvlid).to.equal(259) }) }); - - describe('getUserSyncs', function () { - const syncurl_iframe = 'https://visitor.omnitagjs.com/visitor/isync?uid=19340f4f097d16f41f34fc0274981ca4'; - - const emptySync = []; - - describe('with iframe enabled', function() { - const userSyncConfig = { iframeEnabled: true }; - - it('should not add parameters if not provided', function() { - expect(spec.getUserSyncs(userSyncConfig, {}, undefined, undefined)).to.deep.equal([{ - type: 'iframe', url: `${syncurl_iframe}` - }]); - }); - - it('should add GDPR parameters if provided', function() { - 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([{ - type: 'iframe', url: `${syncurl_iframe}&gdpr=1&gdpr_consent=foo%3F` - }]); - expect(spec.getUserSyncs(userSyncConfig, {}, {gdprApplies: false, consentString: 'bar'}, undefined)).to.deep.equal([{ - type: 'iframe', url: `${syncurl_iframe}&gdpr=0&gdpr_consent=bar` - }]); - }); - - it('should add CCPA parameters if provided', function() { - expect(spec.getUserSyncs(userSyncConfig, {}, undefined, 'foo?')).to.deep.equal([{ - type: 'iframe', url: `${syncurl_iframe}&us_privacy=foo%3F` - }]); - }); - - describe('COPPA', function() { - let sandbox; - - this.beforeEach(function() { - sandbox = sinon.sandbox.create(); - }); - - this.afterEach(function() { - sandbox.restore(); - }); - - it('should add coppa parameters if provided', function() { - sandbox.stub(config, 'getConfig').callsFake(key => { - const config = { - 'coppa': true - }; - return config[key]; - }); - - expect(spec.getUserSyncs(userSyncConfig, {}, undefined, undefined)).to.deep.equal([{ - type: 'iframe', url: `${syncurl_iframe}&coppa=1` - }]); - }); - }); - - describe('GPP', function() { - it('should not apply if not gppConsent.gppString', function() { - const gppConsent = { gppString: '', applicableSections: [123] }; - const result = spec.getUserSyncs(userSyncConfig, {}, undefined, undefined, gppConsent); - expect(result).to.deep.equal([{ - type: 'iframe', url: `${syncurl_iframe}` - }]); - }); - - it('should not apply if not gppConsent.applicableSections', function() { - const gppConsent = { gppString: '', applicableSections: undefined }; - const result = spec.getUserSyncs(userSyncConfig, {}, undefined, undefined, gppConsent); - expect(result).to.deep.equal([{ - type: 'iframe', url: `${syncurl_iframe}` - }]); - }); - - it('should not apply if empty gppConsent.applicableSections', function() { - const gppConsent = { gppString: '', applicableSections: [] }; - const result = spec.getUserSyncs(userSyncConfig, {}, undefined, undefined, gppConsent); - expect(result).to.deep.equal([{ - type: 'iframe', url: `${syncurl_iframe}` - }]); - }); - - it('should apply if all above are available', function() { - const gppConsent = { gppString: 'foo?', applicableSections: [123] }; - const result = spec.getUserSyncs(userSyncConfig, {}, undefined, undefined, gppConsent); - expect(result).to.deep.equal([{ - type: 'iframe', url: `${syncurl_iframe}&gpp=foo%3F&gpp_sid=123` - }]); - }); - - it('should support multiple sections', function() { - const gppConsent = { gppString: 'foo', applicableSections: [123, 456] }; - const result = spec.getUserSyncs(userSyncConfig, {}, undefined, undefined, gppConsent); - expect(result).to.deep.equal([{ - type: 'iframe', url: `${syncurl_iframe}&gpp=foo&gpp_sid=123%2C456` - }]); - }); - }); - }); - - describe('with iframe disabled', function() { - const userSyncConfig = { iframeEnabled: false }; - - 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); - }); - }); - }); }); diff --git a/test/spec/modules/agmaAnalyticsAdapter_spec.js b/test/spec/modules/agmaAnalyticsAdapter_spec.js deleted file mode 100644 index ba71624e3b3..00000000000 --- a/test/spec/modules/agmaAnalyticsAdapter_spec.js +++ /dev/null @@ -1,388 +0,0 @@ -import adapterManager from '../../../src/adapterManager.js'; -import agmaAnalyticsAdapter, { - getTiming, - getOrtb2Data, - getPayload, -} from '../../../modules/agmaAnalyticsAdapter.js'; -import { gdprDataHandler } from '../../../src/adapterManager.js'; -import { expect } from 'chai'; -import * as events from '../../../src/events.js'; -import constants from '../../../src/constants.json'; -import { generateUUID } from '../../../src/utils.js'; -import { server } from '../../mocks/xhr.js'; -import { config } from 'src/config.js'; - -const INGEST_URL = 'https://pbc.agma-analytics.de/v1'; -const extendedKey = [ - 'auctionIds', - 'code', - 'domain', - 'extended', - 'gdprApplies', - 'gdprConsentString', - 'language', - 'ortb2', - 'pageUrl', - 'pageViewId', - 'prebidVersion', - 'referrer', - 'screenHeight', - 'screenWidth', - 'scriptVersion', - 'timestamp', - 'timezoneOffset', - 'timing', - 'triggerEvent', - 'userIdsAsEids', -]; -const nonExtendedKey = [ - 'auctionIds', - 'code', - 'domain', - 'gdprApplies', - 'ortb2', - 'pageUrl', - 'pageViewId', - 'prebidVersion', - 'scriptVersion', - 'timing', - 'triggerEvent', -]; - -describe('AGMA Analytics Adapter', () => { - let agmaConfig, sandbox, clock; - - beforeEach(() => { - sandbox = sinon.sandbox.create(); - clock = sandbox.useFakeTimers(); - sandbox.stub(events, 'getEvents').returns([]); - agmaConfig = { - options: { - code: 'test', - }, - }; - }); - - afterEach(() => { - sandbox.restore(); - }); - - describe('configuration', () => { - it('registers itself with the adapter manager', () => { - const adapter = adapterManager.getAnalyticsAdapter('agma'); - expect(adapter).to.exist; - expect(adapter.gvlid).to.equal(1122); - }); - }); - - describe('getPayload', () => { - it('should use non extended payload with no consent info', () => { - sandbox.stub(gdprDataHandler, 'getConsentData').callsFake(() => null) - const payload = getPayload([generateUUID()], { - code: 'test', - }); - - expect(payload).to.have.all.keys([...nonExtendedKey, 'debug']); - }); - - it('should use non extended payload when agma is not in the TC String', () => { - sandbox.stub(gdprDataHandler, 'getConsentData').callsFake(() => ({ - vendorData: { - vendor: { - consents: { - 1122: false, - }, - }, - }, - })); - const payload = getPayload([generateUUID()], { - code: 'test', - }); - expect(payload).to.have.all.keys([...nonExtendedKey, 'debug']); - }); - - it('should use extended payload when agma is in the TC String', () => { - sandbox.stub(gdprDataHandler, 'getConsentData').callsFake(() => ({ - vendorData: { - vendor: { - consents: { - 1122: true, - }, - }, - }, - })); - const payload = getPayload([generateUUID()], { - code: 'test', - }); - expect(payload).to.have.all.keys([...extendedKey, 'debug']); - }); - }); - - describe('getTiming', () => { - let originalPerformance; - let originalWindowPerformanceNow; - - beforeEach(() => { - originalPerformance = global.performance; - originalWindowPerformanceNow = window.performance.now; - }); - - afterEach(() => { - global.performance = originalPerformance; - window.performance.now = originalWindowPerformanceNow; - }); - - it('returns TTFB using Timing API V2', () => { - global.performance = { - getEntriesByType: sinon - .stub() - .returns([{ responseStart: 100, startTime: 50 }]), - now: sinon.stub().returns(150), - }; - - const result = getTiming(); - - expect(result).to.deep.equal({ ttfb: 50, elapsedTime: 150 }); - }); - - it('returns TTFB using Timing API V1 when V2 is not available', () => { - global.performance = { - getEntriesByType: sinon.stub().throws(), - timing: { responseStart: 150, fetchStart: 50 }, - now: sinon.stub().returns(200), - }; - - const result = getTiming(); - - expect(result).to.deep.equal({ ttfb: 100, elapsedTime: 200 }); - }); - - it('returns null when Timing API is not available', () => { - global.performance = { - getEntriesByType: sinon.stub().throws(), - timing: undefined, - }; - - const result = getTiming(); - - expect(result).to.be.null; - }); - - it('returns ttfb as 0 if calculated value is negative', () => { - global.performance = { - getEntriesByType: sinon - .stub() - .returns([{ responseStart: 50, startTime: 150 }]), - now: sinon.stub().returns(200), - }; - - const result = getTiming(); - - expect(result).to.deep.equal({ ttfb: 0, elapsedTime: 200 }); - }); - - it('returns ttfb as 0 if calculated value exceeds performance.now()', () => { - global.performance = { - getEntriesByType: sinon - .stub() - .returns([{ responseStart: 50, startTime: 0 }]), - now: sinon.stub().returns(40), - }; - - const result = getTiming(); - - expect(result).to.deep.equal({ ttfb: 0, elapsedTime: 40 }); - }); - }); - - describe('getOrtb2Data', () => { - it('returns site and user from options when available', () => { - sandbox.stub(config, 'getConfig').callsFake((key) => { - return {}; - }); - - const ortb2 = { - user: 'user', - site: 'site', - }; - - const result = getOrtb2Data({ - ortb2, - }); - - expect(result).to.deep.equal(ortb2); - }); - - it('returns a combination of data from options and pGlobal.readConfig', () => { - sandbox.stub(config, 'getConfig').callsFake((key) => { - return { - ortb2: { - site: { - foo: 'bar', - }, - }, - }; - }); - - const ortb2 = { - user: 'user', - }; - const result = getOrtb2Data({ - ortb2, - }); - - expect(result).to.deep.equal({ - site: { - foo: 'bar', - }, - user: 'user', - }); - }); - }); - - describe('Event Payload', () => { - beforeEach(() => { - agmaAnalyticsAdapter.enableAnalytics({ - ...agmaConfig, - }); - server.respondWith('POST', INGEST_URL, [ - 200, - { - 'Content-Type': 'application/json', - 'Access-Control-Allow-Origin': '*', - }, - '', - ]); - }); - - afterEach(() => { - agmaAnalyticsAdapter.auctionIds = []; - if (agmaAnalyticsAdapter.timer) { - clearTimeout(agmaAnalyticsAdapter.timer); - } - agmaAnalyticsAdapter.disableAnalytics(); - }); - - it('should only send once per minute', () => { - sandbox.stub(gdprDataHandler, 'getConsentData').callsFake(() => ({ - gdprApplies: true, - consentString: 'consentDataString', - vendorData: { - vendor: { - consents: { - 1122: true, - }, - }, - }, - })); - const auction = { - auctionId: generateUUID(), - }; - - events.emit(constants.EVENTS.AUCTION_INIT, { - auctionId: generateUUID('1'), - auction, - }); - - clock.tick(200); - - events.emit(constants.EVENTS.AUCTION_INIT, { - auctionId: generateUUID('2'), - auction, - }); - events.emit(constants.EVENTS.AUCTION_INIT, { - auctionId: generateUUID('3'), - auction, - }); - events.emit(constants.EVENTS.AUCTION_INIT, { - auctionId: generateUUID('4'), - auction, - }); - - clock.tick(900); - - const [request] = server.requests; - const requestBody = JSON.parse(request.requestBody); - expect(request.url).to.equal(INGEST_URL); - expect(requestBody).to.have.all.keys(extendedKey); - expect(requestBody.triggerEvent).to.equal(constants.EVENTS.AUCTION_INIT); - expect(server.requests).to.have.length(1); - }); - - it('should send the extended payload with consent', () => { - sandbox.stub(gdprDataHandler, 'getConsentData').callsFake(() => ({ - gdprApplies: true, - consentString: 'consentDataString', - vendorData: { - vendor: { - consents: { - 1122: true, - }, - }, - }, - })); - const auction = { - auctionId: generateUUID(), - }; - - events.emit(constants.EVENTS.AUCTION_INIT, auction); - clock.tick(1100); - - const [request] = server.requests; - const requestBody = JSON.parse(request.requestBody); - expect(request.url).to.equal(INGEST_URL); - expect(requestBody).to.have.all.keys(extendedKey); - expect(requestBody.triggerEvent).to.equal(constants.EVENTS.AUCTION_INIT); - expect(server.requests).to.have.length(1); - expect(agmaAnalyticsAdapter.auctionIds).to.have.length(0); - }); - - it('should send the non extended payload with no explicit consent', () => { - sandbox.stub(gdprDataHandler, 'getConsentData').callsFake(() => ({ - gdprApplies: true, - consentString: 'consentDataString', - })); - - const auction = { - auctionId: generateUUID(), - }; - - events.emit(constants.EVENTS.AUCTION_INIT, auction); - clock.tick(1000); - - const [request] = server.requests; - const requestBody = JSON.parse(request.requestBody); - expect(request.url).to.equal(INGEST_URL); - expect(requestBody.triggerEvent).to.equal(constants.EVENTS.AUCTION_INIT); - expect(server.requests).to.have.length(1); - expect(agmaAnalyticsAdapter.auctionIds).to.have.length(0); - }); - - it('should set the trigger Event', () => { - sandbox.stub(gdprDataHandler, 'getConsentData').callsFake(() => null); - agmaAnalyticsAdapter.disableAnalytics(); - agmaAnalyticsAdapter.enableAnalytics({ - provider: 'agma', - options: { - code: 'test', - triggerEvent: constants.EVENTS.AUCTION_END - }, - }); - const auction = { - auctionId: generateUUID(), - }; - - events.emit(constants.EVENTS.AUCTION_INIT, auction); - events.emit(constants.EVENTS.AUCTION_END, auction); - clock.tick(1000); - - const [request] = server.requests; - const requestBody = JSON.parse(request.requestBody); - expect(request.url).to.equal(INGEST_URL); - expect(requestBody.auctionIds).to.have.length(1); - expect(requestBody.triggerEvent).to.equal(constants.EVENTS.AUCTION_END); - expect(server.requests).to.have.length(1); - expect(agmaAnalyticsAdapter.auctionIds).to.have.length(0); - }); - }); -}); diff --git a/test/spec/modules/aidemBidAdapter_spec.js b/test/spec/modules/aidemBidAdapter_spec.js index 3de348197b2..8b401491ba0 100644 --- a/test/spec/modules/aidemBidAdapter_spec.js +++ b/test/spec/modules/aidemBidAdapter_spec.js @@ -155,9 +155,9 @@ const DEFAULT_VALID_BANNER_REQUESTS = [ { adUnitCode: 'test-div', auctionId: 'b06c5141-fe8f-4cdf-9d7d-54415490a917', - bidId: '2705bfae8ea667', + bidId: '22c4871113f461', bidder: 'aidem', - bidderRequestId: '1bbb7854dfa0d8', + bidderRequestId: '15246a574e859f', mediaTypes: { banner: { sizes: [ @@ -171,7 +171,11 @@ const DEFAULT_VALID_BANNER_REQUESTS = [ placementId: '13144370' }, src: 'client', - transactionId: 'db739693-9b4a-4669-9945-8eab938783cc' + ortb2Imp: { + ext: { + tid: '54a58774-7a41-494e-9aaf-fa7b79164f0c', + }, + }, } ]; @@ -179,9 +183,9 @@ const DEFAULT_VALID_VIDEO_REQUESTS = [ { adUnitCode: 'test-div', auctionId: 'b06c5141-fe8f-4cdf-9d7d-54415490a917', - bidId: '2705bfae8ea667', + bidId: '22c4871113f461', bidder: 'aidem', - bidderRequestId: '1bbb7854dfa0d8', + bidderRequestId: '15246a574e859f', mediaTypes: { video: { minduration: 7, @@ -196,23 +200,18 @@ const DEFAULT_VALID_VIDEO_REQUESTS = [ placementId: '13144370' }, src: 'client', - transactionId: 'db739693-9b4a-4669-9945-8eab938783cc' + ortb2Imp: { + ext: { + tid: '54a58774-7a41-494e-9aaf-fa7b79164f0c', + } + }, } ]; const VALID_BIDDER_REQUEST = { - auctionId: '19c97f22-5bd1-4b16-a128-80f75fb0a8a0', + auctionId: '6e9b46c3-65a8-46ea-89f4-c5071110c85c', bidderCode: 'aidem', - bidderRequestId: '1bbb7854dfa0d8', - bids: [ - { - params: { - placementId: '13144370', - siteId: '23434', - publisherId: '7689670753' - }, - } - ], + bidderRequestId: '170ea5d2b1d073', refererInfo: { page: 'test-page', domain: 'test-domain', @@ -220,68 +219,182 @@ const VALID_BIDDER_REQUEST = { }, } +// Add mediatype const SERVER_RESPONSE_BANNER = { - 'id': '19c97f22-5bd1-4b16-a128-80f75fb0a8a0', - 'seatbid': [ - { - 'bid': [ - { - 'id': 'beeswax/aidem', - 'impid': '2705bfae8ea667', - 'price': 0.00875, - 'burl': 'imp_burl', - 'adm': 'creativity_banner', - 'adid': '2:64:162:1001', - 'adomain': [ - 'aidem.com' - ], - 'cid': '64', - 'crid': 'aidem-1001', - 'cat': [], - 'w': 300, - 'h': 250, - 'mtype': 1 + body: { + id: 'efa1930a-bc3e-4fd0-8368-08bc40236b4f', + bid: [ + // BANNER + { + 'id': '2e614be960ee1d', + 'impid': '2e614be960ee1d', + 'price': 7.91, + 'mediatype': 'banner', + 'adid': '24277955', + 'adm': 'creativity_banner', + 'adomain': [ + 'aidem.com' + ], + 'iurl': 'http://www.aidem.com', + 'cat': [], + 'cid': '4193561', + 'crid': '24277955', + 'w': 300, + 'h': 250, + 'ext': { + 'dspid': 85, + 'advbrandid': 1246, + 'advbrand': 'AIDEM' } - ], - 'seat': 'aidemdsp', - 'group': 0 - } - ], - 'cur': 'USD' + }, + ], + cur: 'USD' + }, } const SERVER_RESPONSE_VIDEO = { - 'id': '19c97f22-5bd1-4b16-a128-80f75fb0a8a0', - 'seatbid': [ - { - 'bid': [ - { - 'id': 'beeswax/aidem', - 'impid': '2705bfae8ea667', - 'price': 0.00875, - 'burl': 'imp_burl', - 'adm': 'creativity_banner', - 'adid': '2:64:162:1001', - 'adomain': [ - 'aidem.com' - ], - 'cid': '64', - 'crid': 'aidem-1001', - 'cat': [], - 'w': 300, - 'h': 250, - 'mtype': 2 + body: { + id: 'efa1930a-bc3e-4fd0-8368-08bc40236b4f', + bid: [ + // VIDEO + { + 'id': '2876a29392a47c', + 'impid': '2876a29392a47c', + 'price': 7.93, + 'mediatype': 'video', + 'adid': '24277955', + 'adm': 'https://hermes.aidemsrv.com/vast-tag/cl9mzhhd502uq09l720uegb02?auction_id={{AUCTION_ID}}&cachebuster={{CACHEBUSTER}}', + 'adomain': [ + 'aidem.com' + ], + 'iurl': 'http://www.aidem.com', + 'cat': [], + 'cid': '4193561', + 'crid': '24277955', + 'w': 640, + 'h': 480, + 'ext': { + 'dspid': 85, + 'advbrandid': 1246, + 'advbrand': 'AIDEM' } - ], - 'seat': 'aidemdsp', - 'group': 0 + } + ], + cur: 'USD' + }, +} + +const WIN_NOTICE_WEB = { + 'adId': '3a20ee5dc78c1e', + 'adUnitCode': 'div-gpt-ad-1460505748561-0', + 'creativeId': '24277955', + 'cpm': 1, + 'netRevenue': false, + 'adserverTargeting': { + 'hb_bidder': 'aidem', + 'hb_adid': '3a20ee5dc78c1e', + 'hb_pb': '1.00', + 'hb_size': '300x250', + 'hb_source': 'client', + 'hb_format': 'banner', + 'hb_adomain': 'example.com' + }, + + 'auctionId': '85864730-6cbc-4e56-bc3c-a4a6596dca5b', + 'currency': [ + 'USD' + ], + 'mediaType': 'banner', + 'meta': { + 'advertiserDomains': [ + 'cloudflare.com' + ], + 'ext': {} + }, + 'size': '300x250', + 'params': [ + { + 'placementId': '13144370', + 'siteId': '23434', + 'publisherId': '7689670753' } ], - 'cur': 'USD' + 'width': 300, + 'height': 250, + 'status': 'rendered', + 'transactionId': 'ce089116-4251-45c3-bdbb-3a03cb13816b', + 'ttl': 300, + 'requestTimestamp': 1666796241007, + 'responseTimestamp': 1666796241021, + metrics: { + getMetrics() { + return { + + } + } + } } -const WIN_NOTICE = { - burl: 'burl' +const WIN_NOTICE_APP = { + 'adId': '3a20ee5dc78c1e', + 'adUnitCode': 'div-gpt-ad-1460505748561-0', + 'creativeId': '24277955', + 'cpm': 1, + 'netRevenue': false, + 'adserverTargeting': { + 'hb_bidder': 'aidem', + 'hb_adid': '3a20ee5dc78c1e', + 'hb_pb': '1.00', + 'hb_size': '300x250', + 'hb_source': 'client', + 'hb_format': 'banner', + 'hb_adomain': 'example.com' + }, + + 'auctionId': '85864730-6cbc-4e56-bc3c-a4a6596dca5b', + 'currency': [ + 'USD' + ], + 'mediaType': 'banner', + 'meta': { + 'advertiserDomains': [ + 'cloudflare.com' + ], + 'ext': { + 'app': { + 'app_bundle': '{{APP_BUNDLE}}', + 'app_id': '{{APP_ID}}', + 'app_name': '{{APP_NAME}}', + 'app_store_url': '{{APP_STORE_URL}}', + 'inventory_source': '{{INVENTORY_SOURCE}}' + }, + 'win_notice_ext': { + 'seatid': '{{SEAT_ID}}' + } + } + }, + 'size': '300x250', + 'params': [ + { + 'placementId': '13144370', + 'siteId': '23434', + 'publisherId': '7689670753' + } + ], + 'width': 300, + 'height': 250, + 'status': 'rendered', + 'transactionId': 'ce089116-4251-45c3-bdbb-3a03cb13816b', + 'ttl': 300, + 'requestTimestamp': 1666796241007, + 'responseTimestamp': 1666796241021, + metrics: { + getMetrics() { + return { + + } + } + } } const ERROR_NOTICE = { @@ -399,95 +512,109 @@ describe('Aidem adapter', () => { const requests = spec.buildRequests(DEFAULT_VALID_BANNER_REQUESTS, VALID_BIDDER_REQUEST); expect(requests).to.be.an('object'); expect(requests.method).to.be.a('string') - expect(requests.data).to.be.a('object') + expect(requests.data).to.be.a('string') expect(requests.options).to.be.an('object').that.have.a.property('withCredentials') }); it('should have a well formatted banner payload', () => { - 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' + const requests = spec.buildRequests(DEFAULT_VALID_BANNER_REQUESTS, VALID_BIDDER_REQUEST); + const payload = JSON.parse(requests.data) + expect(payload).to.be.a('object').that.has.all.keys( + 'id', 'imp', 'device', 'cur', 'tz', 'regs', 'site', 'environment', 'at' ) - expect(data.imp).to.be.a('array').that.has.lengthOf(DEFAULT_VALID_BANNER_REQUESTS.length) + expect(payload.imp).to.be.a('array').that.has.lengthOf(DEFAULT_VALID_BANNER_REQUESTS.length) - expect(data.imp[0]).to.be.a('object').that.has.all.keys( - 'banner', 'id', 'tagId' + expect(payload.imp[0]).to.be.a('object').that.has.all.keys( + 'banner', 'id', 'mediatype', 'imp_ext', 'tid', 'tagid' ) - expect(data.imp[0].banner).to.be.a('object').that.has.all.keys( + expect(payload.imp[0].banner).to.be.a('object').that.has.all.keys( 'format', 'topframe' ) }); - if (FEATURES.VIDEO) { - it('should have a well formatted video payload', () => { - 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' - ) - expect(data.imp).to.be.a('array').that.has.lengthOf(DEFAULT_VALID_VIDEO_REQUESTS.length) + it('should have a well formatted video payload', () => { + const requests = spec.buildRequests(DEFAULT_VALID_VIDEO_REQUESTS, VALID_BIDDER_REQUEST); + const payload = JSON.parse(requests.data) + expect(payload).to.be.a('object').that.has.all.keys( + 'id', 'imp', 'device', 'cur', 'tz', 'regs', 'site', 'environment', 'at' + ) + expect(payload.imp).to.be.a('array').that.has.lengthOf(DEFAULT_VALID_VIDEO_REQUESTS.length) - expect(data.imp[0]).to.be.a('object').that.has.all.keys( - 'video', 'id', 'tagId' - ) - expect(data.imp[0].video).to.be.a('object').that.has.all.keys( - 'mimes', 'minduration', 'maxduration', 'protocols', 'w', 'h' - ) - }); - } + expect(payload.imp[0]).to.be.a('object').that.has.all.keys( + 'video', 'id', 'mediatype', 'imp_ext', 'tid', 'tagid' + ) + expect(payload.imp[0].video).to.be.a('object').that.has.all.keys( + 'format', 'mimes', 'minDuration', 'maxDuration', 'protocols' + ) + }); + + it('should have a well formatted bid floor payload if configured', () => { + const validBannerRequests = utils.deepClone(DEFAULT_VALID_BANNER_REQUESTS) + validBannerRequests[0].params.floor = { + value: 1.98, + currency: 'USD' + } + const requests = spec.buildRequests(validBannerRequests, VALID_BIDDER_REQUEST); + const payload = JSON.parse(requests.data) + const { floor } = payload.imp[0] + expect(floor).to.be.a('object').that.has.all.keys( + 'value', 'currency' + ) + }); it('should hav wpar keys in environment object', function () { - 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') + const requests = spec.buildRequests(DEFAULT_VALID_VIDEO_REQUESTS, VALID_BIDDER_REQUEST); + const payload = JSON.parse(requests.data) + expect(payload).to.have.property('environment') + expect(payload.environment).to.be.a('object').that.have.property('wpar') + expect(payload.environment.wpar).to.be.a('object').that.has.keys('innerWidth', 'innerHeight') }); }) 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 }) - expect(bids).to.be.a('array').that.has.lengthOf(1) - bids.forEach(value => { + const response = utils.deepClone(SERVER_RESPONSE_BANNER) + const interpreted = spec.interpretResponse(response) + expect(interpreted).to.be.a('array').that.has.lengthOf(1) + interpreted.forEach(value => { expect(value).to.be.a('object').that.has.all.keys( - 'ad', 'cpm', 'creativeId', 'currency', 'height', 'mediaType', 'meta', 'netRevenue', 'requestId', 'ttl', 'width', 'burl', 'seatBidId', 'creative_id' + 'ad', 'cpm', 'creativeId', 'currency', 'height', 'mediaType', 'meta', 'netRevenue', 'requestId', 'ttl', 'width', 'dealId' ) }) }); - 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 }) - expect(bids).to.be.a('array').that.has.lengthOf(1) - bids.forEach(value => { - expect(value).to.be.a('object').that.has.all.keys( - 'vastUrl', 'vastXml', 'playerHeight', 'playerWidth', 'cpm', 'creativeId', 'currency', 'height', 'mediaType', 'meta', 'netRevenue', 'requestId', 'ttl', 'width', 'burl', 'seatBidId', 'creative_id' - ) - }) - }); - } + it('should return a valid bid array with a banner bid', () => { + const response = utils.deepClone(SERVER_RESPONSE_VIDEO) + const interpreted = spec.interpretResponse(response) + expect(interpreted).to.be.a('array').that.has.lengthOf(1) + interpreted.forEach(value => { + expect(value).to.be.a('object').that.has.all.keys( + 'vastUrl', 'cpm', 'creativeId', 'currency', 'height', 'mediaType', 'meta', 'netRevenue', 'requestId', 'ttl', 'width', 'dealId' + ) + }) + }); 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 }) - expect(bids).to.be.a('array').that.has.lengthOf(1) - expect(bids[0].netRevenue).to.be.true - }); - - // it('should return an empty bid array if one of seatbid entry is missing price property', () => { - // const response = utils.deepClone(SERVER_RESPONSE_BANNER) - // delete response.body.bid[0].price - // const interpreted = spec.interpretResponse(response) - // expect(interpreted).to.be.a('array').that.has.lengthOf(0) - // }); - - // it('should return an empty bid array if one of seatbid entry is missing adm property', () => { - // const response = utils.deepClone(SERVER_RESPONSE_BANNER) - // delete response.body.bid[0].adm - // const interpreted = spec.interpretResponse(response) - // expect(interpreted).to.be.a('array').that.has.lengthOf(0) - // }); + const response = utils.deepClone(SERVER_RESPONSE_BANNER) + response.body.bid[0].isNet = true + const interpreted = spec.interpretResponse(response) + expect(interpreted).to.be.a('array').that.has.lengthOf(1) + expect(interpreted[0].netRevenue).to.be.true + }); + + it('should return an empty bid array if one of seatbid entry is missing price property', () => { + const response = utils.deepClone(SERVER_RESPONSE_BANNER) + delete response.body.bid[0].price + const interpreted = spec.interpretResponse(response) + expect(interpreted).to.be.a('array').that.has.lengthOf(0) + }); + + it('should return an empty bid array if one of seatbid entry is missing adm property', () => { + const response = utils.deepClone(SERVER_RESPONSE_BANNER) + delete response.body.bid[0].adm + const interpreted = spec.interpretResponse(response) + expect(interpreted).to.be.a('array').that.has.lengthOf(0) + }); }) describe('onBidWon', () => { @@ -495,29 +622,34 @@ describe('Aidem adapter', () => { expect(spec.onBidWon).to.exist.and.to.be.a('function') }); - it(`should send a win notice`, function () { - spec.onBidWon(WIN_NOTICE); + it(`should send a valid bid won notice from web environment`, function () { + spec.onBidWon(WIN_NOTICE_WEB); + expect(server.requests.length).to.equal(1); + }); + + it(`should send a valid bid won notice from app environment`, function () { + spec.onBidWon(WIN_NOTICE_APP); expect(server.requests.length).to.equal(1); }); }); - // describe('onBidderError', () => { - // it(`should exists and type function`, function () { - // expect(spec.onBidderError).to.exist.and.to.be.a('function') - // }); - // - // it(`should send a valid error notice`, function () { - // spec.onBidderError({ bidderRequest: ERROR_NOTICE }) - // expect(server.requests.length).to.equal(1); - // const body = JSON.parse(server.requests[0].requestBody) - // expect(body).to.be.a('object').that.has.all.keys('message', 'auctionId', 'bidderRequestId', 'url', 'metrics') - // // const { bids } = JSON.parse(server.requests[0].requestBody) - // // expect(bids).to.be.a('array').that.has.lengthOf(1) - // // _each(bids, (bid) => { - // // expect(bid).to.be.a('object').that.has.all.keys('adUnitCode', 'auctionId', 'bidId', 'bidderRequestId', 'transactionId', 'metrics') - // // }) - // }); - // }); + describe('onBidderError', () => { + it(`should exists and type function`, function () { + expect(spec.onBidderError).to.exist.and.to.be.a('function') + }); + + it(`should send a valid error notice`, function () { + spec.onBidderError({ bidderRequest: ERROR_NOTICE }) + expect(server.requests.length).to.equal(1); + const body = JSON.parse(server.requests[0].requestBody) + expect(body).to.be.a('object').that.has.all.keys('message', 'auctionId', 'bidderRequestId', 'url', 'metrics') + // const { bids } = JSON.parse(server.requests[0].requestBody) + // expect(bids).to.be.a('array').that.has.lengthOf(1) + // _each(bids, (bid) => { + // expect(bid).to.be.a('object').that.has.all.keys('adUnitCode', 'auctionId', 'bidId', 'bidderRequestId', 'transactionId', 'metrics') + // }) + }); + }); describe('setEndPoints', () => { it(`should exists and type function`, function () { @@ -527,35 +659,64 @@ describe('Aidem adapter', () => { it(`should not modify default endpoints`, function () { const endpoints = setEndPoints() const requestURL = new URL(endpoints.request) + const winNoticeURL = new URL(endpoints.notice.win) + const timeoutNoticeURL = new URL(endpoints.notice.timeout) + const errorNoticeURL = new URL(endpoints.notice.error) + expect(requestURL.host).to.equal('zero.aidemsrv.com') - expect(decodeURIComponent(requestURL.pathname)).to.equal('/prebidjs/ortb/v2.6/bid/request') + expect(winNoticeURL.host).to.equal('zero.aidemsrv.com') + expect(timeoutNoticeURL.host).to.equal('zero.aidemsrv.com') + expect(errorNoticeURL.host).to.equal('zero.aidemsrv.com') + + expect(decodeURIComponent(requestURL.pathname)).to.equal('/bid/request') + expect(decodeURIComponent(winNoticeURL.pathname)).to.equal('/notice/win') + expect(decodeURIComponent(timeoutNoticeURL.pathname)).to.equal('/notice/timeout') + expect(decodeURIComponent(errorNoticeURL.pathname)).to.equal('/notice/error') }); it(`should not change request endpoint`, function () { const endpoints = setEndPoints('default') const requestURL = new URL(endpoints.request) - expect(decodeURIComponent(requestURL.pathname)).to.equal('/prebidjs/ortb/v2.6/bid/request') + expect(decodeURIComponent(requestURL.pathname)).to.equal('/bid/request') }); it(`should change to local env`, function () { const endpoints = setEndPoints('local') const requestURL = new URL(endpoints.request) + const winNoticeURL = new URL(endpoints.notice.win) + const timeoutNoticeURL = new URL(endpoints.notice.timeout) + const errorNoticeURL = new URL(endpoints.notice.error) expect(requestURL.host).to.equal('127.0.0.1:8787') + expect(winNoticeURL.host).to.equal('127.0.0.1:8787') + expect(timeoutNoticeURL.host).to.equal('127.0.0.1:8787') + expect(errorNoticeURL.host).to.equal('127.0.0.1:8787') }); it(`should add a path prefix`, function () { const endpoints = setEndPoints('local', '/path') const requestURL = new URL(endpoints.request) + const winNoticeURL = new URL(endpoints.notice.win) + const timeoutNoticeURL = new URL(endpoints.notice.timeout) + const errorNoticeURL = new URL(endpoints.notice.error) - expect(decodeURIComponent(requestURL.pathname)).to.equal('/path/prebidjs/ortb/v2.6/bid/request') + expect(decodeURIComponent(requestURL.pathname)).to.equal('/path/bid/request') + expect(decodeURIComponent(winNoticeURL.pathname)).to.equal('/path/notice/win') + expect(decodeURIComponent(timeoutNoticeURL.pathname)).to.equal('/path/notice/timeout') + expect(decodeURIComponent(errorNoticeURL.pathname)).to.equal('/path/notice/error') }); it(`should add a path prefix and change request endpoint`, function () { const endpoints = setEndPoints('local', '/path') const requestURL = new URL(endpoints.request) + const winNoticeURL = new URL(endpoints.notice.win) + const timeoutNoticeURL = new URL(endpoints.notice.timeout) + const errorNoticeURL = new URL(endpoints.notice.error) - expect(decodeURIComponent(requestURL.pathname)).to.equal('/path/prebidjs/ortb/v2.6/bid/request') + expect(decodeURIComponent(requestURL.pathname)).to.equal('/path/bid/request') + expect(decodeURIComponent(winNoticeURL.pathname)).to.equal('/path/notice/win') + expect(decodeURIComponent(timeoutNoticeURL.pathname)).to.equal('/path/notice/timeout') + expect(decodeURIComponent(errorNoticeURL.pathname)).to.equal('/path/notice/error') }); }); @@ -597,7 +758,8 @@ describe('Aidem adapter', () => { coppa: true }); const { data } = spec.buildRequests(DEFAULT_VALID_BANNER_REQUESTS, VALID_BIDDER_REQUEST); - expect(data.regs.coppa_applies).to.equal(true) + const request = JSON.parse(data) + expect(request.regs.coppa_applies).to.equal(true) }); it(`should set gdpr to true`, function () { @@ -609,7 +771,8 @@ describe('Aidem adapter', () => { } }); const { data } = spec.buildRequests(DEFAULT_VALID_BANNER_REQUESTS, VALID_BIDDER_REQUEST); - expect(data.regs.gdpr_applies).to.equal(true) + const request = JSON.parse(data) + expect(request.regs.gdpr_applies).to.equal(true) }); it(`should set usp_consent string`, function () { @@ -626,7 +789,8 @@ describe('Aidem adapter', () => { } }); const { data } = spec.buildRequests(DEFAULT_VALID_BANNER_REQUESTS, VALID_BIDDER_REQUEST); - expect(data.regs.us_privacy).to.equal('1YYY') + const request = JSON.parse(data) + expect(request.regs.us_privacy).to.equal('1YYY') }); it(`should not set usp_consent string`, function () { @@ -643,7 +807,8 @@ describe('Aidem adapter', () => { } }); const { data } = spec.buildRequests(DEFAULT_VALID_BANNER_REQUESTS, VALID_BIDDER_REQUEST); - expect(data.regs.us_privacy).to.undefined + const request = JSON.parse(data) + expect(request.regs.us_privacy).to.undefined }); }); }); diff --git a/test/spec/modules/ajaBidAdapter_spec.js b/test/spec/modules/ajaBidAdapter_spec.js index 3137c9dc24e..7cf5698f7d4 100644 --- a/test/spec/modules/ajaBidAdapter_spec.js +++ b/test/spec/modules/ajaBidAdapter_spec.js @@ -62,17 +62,8 @@ describe('AjaAdapter', function () { model: 'SM-G955U', bitness: '64', architecture: '' - }, - ext: { - cdep: 'example_label_1' } } - }, - ortb2Imp: { - ext: { - tid: 'cea1eb09-d970-48dc-8585-634d3a7b0544', - gpid: '/1111/homepage#300x250' - } } } ]; @@ -87,7 +78,7 @@ describe('AjaAdapter', function () { const requests = spec.buildRequests(bidRequests, bidderRequest); expect(requests[0].url).to.equal(ENDPOINT); expect(requests[0].method).to.equal('GET'); - expect(requests[0].data).to.equal('asi=123456&skt=5&gpid=%2F1111%2Fhomepage%23300x250&tid=cea1eb09-d970-48dc-8585-634d3a7b0544&cdep=example_label_1&prebid_id=30b31c1838de1e&prebid_ver=$prebid.version$&page_url=https%3A%2F%2Fhoge.com&ad_format_ids=2&sua=%7B%22source%22%3A2%2C%22platform%22%3A%7B%22brand%22%3A%22Android%22%2C%22version%22%3A%5B%228%22%2C%220%22%2C%220%22%5D%7D%2C%22browsers%22%3A%5B%7B%22brand%22%3A%22Not_A%20Brand%22%2C%22version%22%3A%5B%2299%22%2C%220%22%2C%220%22%2C%220%22%5D%7D%2C%7B%22brand%22%3A%22Google%20Chrome%22%2C%22version%22%3A%5B%22109%22%2C%220%22%2C%225414%22%2C%22119%22%5D%7D%2C%7B%22brand%22%3A%22Chromium%22%2C%22version%22%3A%5B%22109%22%2C%220%22%2C%225414%22%2C%22119%22%5D%7D%5D%2C%22mobile%22%3A1%2C%22model%22%3A%22SM-G955U%22%2C%22bitness%22%3A%2264%22%2C%22architecture%22%3A%22%22%7D&'); + expect(requests[0].data).to.equal('asi=123456&skt=5&prebid_id=30b31c1838de1e&prebid_ver=$prebid.version$&page_url=https%3A%2F%2Fhoge.com&sua=%7B%22source%22%3A2%2C%22platform%22%3A%7B%22brand%22%3A%22Android%22%2C%22version%22%3A%5B%228%22%2C%220%22%2C%220%22%5D%7D%2C%22browsers%22%3A%5B%7B%22brand%22%3A%22Not_A%20Brand%22%2C%22version%22%3A%5B%2299%22%2C%220%22%2C%220%22%2C%220%22%5D%7D%2C%7B%22brand%22%3A%22Google%20Chrome%22%2C%22version%22%3A%5B%22109%22%2C%220%22%2C%225414%22%2C%22119%22%5D%7D%2C%7B%22brand%22%3A%22Chromium%22%2C%22version%22%3A%5B%22109%22%2C%220%22%2C%225414%22%2C%22119%22%5D%7D%5D%2C%22mobile%22%3A1%2C%22model%22%3A%22SM-G955U%22%2C%22bitness%22%3A%2264%22%2C%22architecture%22%3A%22%22%7D&'); }); }); @@ -125,7 +116,7 @@ describe('AjaAdapter', function () { const requests = spec.buildRequests(bidRequests, bidderRequest); expect(requests[0].url).to.equal(ENDPOINT); expect(requests[0].method).to.equal('GET'); - expect(requests[0].data).to.equal('asi=123456&skt=5&prebid_id=30b31c1838de1e&prebid_ver=$prebid.version$&page_url=https%3A%2F%2Fhoge.com&ad_format_ids=2&eids=%7B%22eids%22%3A%5B%7B%22source%22%3A%22pubcid.org%22%2C%22uids%22%3A%5B%7B%22id%22%3A%22some-random-id-value%22%2C%22atype%22%3A1%7D%5D%7D%5D%7D&'); + expect(requests[0].data).to.equal('asi=123456&skt=5&prebid_id=30b31c1838de1e&prebid_ver=$prebid.version$&page_url=https%3A%2F%2Fhoge.com&eids=%7B%22eids%22%3A%5B%7B%22source%22%3A%22pubcid.org%22%2C%22uids%22%3A%5B%7B%22id%22%3A%22some-random-id-value%22%2C%22atype%22%3A1%7D%5D%7D%5D%7D&'); }); }); @@ -182,6 +173,138 @@ describe('AjaAdapter', function () { expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); }); + it('handles video responses', function () { + let response = { + 'is_ad_return': true, + 'ad': { + 'ad_type': 3, + 'prebid_id': '51ef8751f9aead', + 'price': 12.34, + 'currency': 'JPY', + 'creative_id': '123abc', + 'video': { + 'w': 300, + 'h': 250, + 'vtag': '', + 'purl': 'https://cdn/player', + 'progress': true, + 'loop': false, + 'inread': false, + 'adomain': [ + 'www.example.com' + ] + } + }, + 'syncs': [ + 'https://example.com' + ] + }; + + let bidderRequest; + let result = spec.interpretResponse({ body: response }, {bidderRequest}); + expect(result[0]).to.have.property('vastXml'); + expect(result[0]).to.have.property('renderer'); + expect(result[0]).to.have.property('mediaType', 'video'); + }); + + it('handles native response', function () { + let response = { + 'is_ad_return': true, + 'ad': { + 'ad_type': 2, + 'prebid_id': '51ef8751f9aead', + 'price': 12.34, + 'currency': 'JPY', + 'creative_id': '123abc', + 'native': { + 'template_and_ads': { + 'head': '', + 'body_wrapper': '', + 'body': '', + 'ads': [ + { + 'ad_format_id': 10, + 'assets': { + 'ad_spot_id': '123abc', + 'index': 0, + 'adchoice_url': 'https://aja-kk.co.jp/optout', + 'cta_text': 'cta', + 'img_icon': 'https://example.com/img_icon', + 'img_icon_width': '50', + 'img_icon_height': '50', + 'img_main': 'https://example.com/img_main', + 'img_main_width': '200', + 'img_main_height': '100', + 'lp_link': 'https://example.com/lp?k=v', + 'sponsor': 'sponsor', + 'title': 'ad_title', + 'description': 'ad_desc' + }, + 'imps': [ + 'https://example.com/imp' + ], + 'inviews': [ + 'https://example.com/inview' + ], + 'jstracker': '', + 'disable_trimming': false, + 'adomain': [ + 'www.example.com' + ] + } + ] + } + } + }, + 'syncs': [ + 'https://example.com' + ] + }; + + let expectedResponse = [ + { + 'requestId': '51ef8751f9aead', + 'cpm': 12.34, + 'creativeId': '123abc', + 'dealId': undefined, + 'mediaType': 'native', + 'currency': 'JPY', + 'ttl': 300, + 'netRevenue': true, + 'native': { + 'title': 'ad_title', + 'body': 'ad_desc', + 'cta': 'cta', + 'sponsoredBy': 'sponsor', + 'image': { + 'url': 'https://example.com/img_main', + 'width': 200, + 'height': 100 + }, + 'icon': { + 'url': 'https://example.com/img_icon', + 'width': 50, + 'height': 50 + }, + 'clickUrl': 'https://example.com/lp?k=v', + 'impressionTrackers': [ + 'https://example.com/imp' + ], + 'privacyLink': 'https://aja-kk.co.jp/optout' + }, + 'meta': { + 'advertiserDomains': [ + 'www.example.com' + ] + } + } + ]; + + let bidderRequest; + let result = spec.interpretResponse({ body: response }, {bidderRequest}) + expect(result).to.deep.equal(expectedResponse) + }); + it('handles nobid responses', function () { let response = { 'is_ad_return': false, diff --git a/test/spec/modules/alkimiBidAdapter_spec.js b/test/spec/modules/alkimiBidAdapter_spec.js index 90a9e409e69..a396e5b8139 100644 --- a/test/spec/modules/alkimiBidAdapter_spec.js +++ b/test/spec/modules/alkimiBidAdapter_spec.js @@ -14,7 +14,8 @@ const REQUEST = { }, 'params': { bidFloor: 0.1, - token: 'e64782a4-8e68-4c38-965b-80ccf115d46f' + token: 'e64782a4-8e68-4c38-965b-80ccf115d46f', + pos: 7 }, 'userIdAsEids': [{ 'source': 'criteo.com', @@ -68,7 +69,7 @@ const BIDDER_VIDEO_RESPONSE = { 'ttl': 200, 'creativeId': 2, 'netRevenue': true, - 'winUrl': 'http://test.com?price=${AUCTION_PRICE}', + 'winUrl': 'http://test.com', 'mediaType': 'video', 'adomain': ['test.com'] }] @@ -95,6 +96,10 @@ describe('alkimiBidAdapter', function () { delete bid.params.token expect(spec.isBidRequestValid(bid)).to.equal(false) + bid = Object.assign({}, REQUEST) + delete bid.params.bidFloor + expect(spec.isBidRequestValid(bid)).to.equal(false) + bid = Object.assign({}, REQUEST) delete bid.params expect(spec.isBidRequestValid(bid)).to.equal(false) @@ -104,6 +109,7 @@ describe('alkimiBidAdapter', function () { describe('buildRequests', function () { let bidRequests = [REQUEST] let requestData = { + auctionId: '123', refererInfo: { page: 'http://test.com/path.html' }, @@ -112,15 +118,7 @@ describe('alkimiBidAdapter', function () { vendorData: {}, gdprApplies: true }, - uspConsent: 'uspConsent', - ortb2: { - site: { - keywords: 'test1, test2' - }, - at: 2, - bcat: ['BSW1', 'BSW2'], - wseat: ['16', '165'] - } + uspConsent: 'uspConsent' } const bidderRequest = spec.buildRequests(bidRequests, requestData) @@ -139,14 +137,13 @@ describe('alkimiBidAdapter', function () { it('sends bid request to ENDPOINT via POST', function () { expect(bidderRequest.method).to.equal('POST') - expect(bidderRequest.data.requestId).to.not.equal(undefined) + 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: 'alkimi-onboarding.com', sid: '00001', hp: 1 }] }) - expect(bidderRequest.data.signRequest.bids).to.deep.contains({ token: 'e64782a4-8e68-4c38-965b-80ccf115d46f', bidFloor: 0.1, sizes: [{ width: 300, height: 250 }], playerSizes: [], impMediaTypes: ['Banner'], adUnitCode: 'bannerAdUnitCode', instl: undefined, exp: undefined, banner: { sizes: [[300, 250]] }, video: undefined }) + expect(bidderRequest.data.signRequest.bids).to.deep.contains({ token: 'e64782a4-8e68-4c38-965b-80ccf115d46f', pos: 7, bidFloor: 0.1, sizes: [{width: 300, height: 250}], playerSizes: [], impMediaTypes: ['Banner'], adUnitCode: 'bannerAdUnitCode' }) expect(bidderRequest.data.signRequest.randomUUID).to.equal(undefined) expect(bidderRequest.data.bidIds).to.deep.contains('456') expect(bidderRequest.data.signature).to.equal(undefined) - expect(bidderRequest.data.ortb2).to.deep.contains({ at: 2, wseat: ['16', '165'], bcat: ['BSW1', 'BSW2'], site: { keywords: 'test1, test2' }, }) expect(bidderRequest.options.customHeaders).to.deep.equal({ 'Rtb-Direct': true }) expect(bidderRequest.options.contentType).to.equal('application/json') expect(bidderRequest.url).to.equal(ENDPOINT) @@ -195,9 +192,9 @@ describe('alkimiBidAdapter', function () { expect(result[0]).to.have.property('ttl').equal(200) expect(result[0]).to.have.property('creativeId').equal(2) expect(result[0]).to.have.property('netRevenue').equal(true) - expect(result[0]).to.have.property('winUrl').equal('http://test.com?price=${AUCTION_PRICE}') + expect(result[0]).to.have.property('winUrl').equal('http://test.com') expect(result[0]).to.have.property('mediaType').equal('video') - expect(result[0]).to.have.property('vastUrl').equal('http://test.com?price=800.4') + expect(result[0]).to.have.property('vastXml').equal('vast') expect(result[0].meta).to.exist.property('advertiserDomains') expect(result[0].meta).to.have.property('advertiserDomains').lengthOf(1) }) diff --git a/test/spec/modules/ampliffyBidAdapter_spec.js b/test/spec/modules/ampliffyBidAdapter_spec.js deleted file mode 100644 index 5b86f692d7e..00000000000 --- a/test/spec/modules/ampliffyBidAdapter_spec.js +++ /dev/null @@ -1,453 +0,0 @@ -import { - parseXML, - isAllowedToBidUp, - spec, - getDefaultParams, - mergeParams, - paramsToQueryString, setCurrentURL -} from 'modules/ampliffyBidAdapter.js'; -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); - - describe('inherited functions', function () { - it('exists and is a function', function () { - expect(adapter.callBids).to.exist.and.to.be.a('function'); - }); - }); - // Global definitions for all tests - const xmlStr = ` - - - - ]]> - - - - ES - `; - const xml = new window.DOMParser().parseFromString(xmlStr, 'text/xml'); - let companion = xml.getElementsByTagName('Companion')[0]; - let htmlResource = companion.getElementsByTagName('HTMLResource')[0]; - let htmlContent = document.createElement('html'); - htmlContent.innerHTML = htmlResource.textContent; - - describe('Is allowed to bid up', function () { - it('Should return true using a URL that is in domainMap', () => { - let allowedToBidUp = isAllowedToBidUp(htmlContent, 'https://testSports.com?id=131313&text=aaaaa&foo=foo'); - expect(allowedToBidUp).to.be.true; - }) - - it('Should return false using an url that is not in domainMap', () => { - let allowedToBidUp = isAllowedToBidUp(htmlContent, 'https://test.com'); - expect(allowedToBidUp).to.be.false; - }) - - it('Should return false using an url that is excluded.', () => { - let allowedToBidUp = isAllowedToBidUp(htmlContent, 'https://www.no-allowed.com/busqueda/sexo/sexo?test=1#item1'); - expect(allowedToBidUp).to.be.false; - }) - }) - - describe('Helper functions', function () { - it('Should default params not to be null', () => { - const defaultParams = getDefaultParams(); - - expect(defaultParams).not.to.be.null; - }) - it('Should the merge two object params into a new object', () => { - const params1 = { - 'hello': 'world', - 'ampTest': 'this will be replaced' - } - const params2 = { - 'test': 1, - 'ampTest': 'This will be replace the param with the same name in other array' - } - const allParams = mergeParams(params1, params2); - - const paramsComplete = - { - 'hello': 'world', - 'ampTest': 'This will be replace the param with the same name in other array', - 'test': 1, - } - expect(allParams).not.to.be.null; - expect(JSON.stringify(allParams)).to.equal(JSON.stringify(paramsComplete)); - }) - it('Params to QueryString', () => { - const params = { - 'test': 1, - 'ampTest': 'ret', - 'empty': null, - 'quoteMark': '?', - 'test1': undefined - } - const queryString = paramsToQueryString(params); - - expect(queryString).not.to.be.null; - expect(queryString).to.equal('test=1&Test=ret&empty"eMark=%3F'); - }) - }) - - describe('isBidRequestValid', function () { - it('Should return true when required params found', function () { - const bidRequest = { - bidder: 'ampliffy', - params: { - server: 'bidder.ampliffy.com', - placementId: 1235465798, - format: 'all' - }, - mediaTypes: { - banner: { - sizes: [1, 1] - } - }, - } - expect(spec.isBidRequestValid(bidRequest)).to.be.true; - }) - it('Should return false when param format is display but mediaTypes are for video', function () { - const bidRequest = { - bidder: 'ampliffy', - params: { - server: 'bidder.ampliffy.com', - placementId: 1235465798, - format: 'display' - }, - mediaTypes: { - video: { - sizes: [1, 1] - } - }, - } - expect(spec.isBidRequestValid(bidRequest)).to.be.false; - }) - it('Should return false when param format is video but mediaTypes are for banner', function () { - const bidRequest = { - bidder: 'ampliffy', - params: { - server: 'bidder.ampliffy.com', - placementId: 1235465798, - format: 'video' - }, - mediaTypes: { - banner: { - sizes: [1, 1] - } - }, - } - expect(spec.isBidRequestValid(bidRequest)).to.be.false; - }) - it('Should return true when param format is video and mediaTypes are for video', function () { - const bidRequest = { - bidder: 'ampliffy', - params: { - server: 'bidder.ampliffy.com', - placementId: 1235465798, - format: 'video' - }, - mediaTypes: { - video: { - sizes: [1, 1] - } - }, - } - expect(spec.isBidRequestValid(bidRequest)).to.be.true; - }) - it('Should return true when param format is display and mediaTypes are for banner', function () { - const bidRequest = { - bidder: 'ampliffy', - params: { - server: 'bidder.ampliffy.com', - placementId: 1235465798, - format: 'display' - }, - mediaTypes: { - banner: { - sizes: [1, 1] - } - }, - } - expect(spec.isBidRequestValid(bidRequest)).to.be.true; - }) - it('Should return true when param format is all and mediaTypes are for banner', function () { - const bidRequest = { - bidder: 'ampliffy', - params: { - server: 'bidder.ampliffy.com', - placementId: 1235465798, - format: 'all' - }, - mediaTypes: { - banner: { - sizes: [1, 1] - } - }, - } - expect(spec.isBidRequestValid(bidRequest)).to.be.true; - }) - it('Should return true when param format is all and mediaTypes are for video', function () { - const bidRequest = { - bidder: 'ampliffy', - params: { - server: 'bidder.ampliffy.com', - placementId: 1235465798, - format: 'all' - }, - mediaTypes: { - video: { - sizes: [1, 1] - } - }, - } - expect(spec.isBidRequestValid(bidRequest)).to.be.true; - }) - it('Should return false without placementId param', function () { - const bidRequest = { - bidder: 'ampliffy', - params: {} - } - expect(spec.isBidRequestValid(bidRequest)).to.be.false; - }) - it('Should return false without param object', function () { - const bidRequest = { - bidder: 'ampliffy', - } - expect(spec.isBidRequestValid(bidRequest)).to.be.false; - }) - }); - - describe('Build request function', function () { - const bidderRequest = { - 'bidderCode': 'ampliffy', - 'auctionId': 'c4a771bf-1791-4513-82b3-96c48d19ddff', - 'bidderRequestId': '1134bdcbe47f25', - 'bids': [{ - 'bidder': 'ampliffy', - 'params': { - 'placementId': 1235465798, - 'type': 'bidder.', - 'region': 'alan-development.k8s.', - 'adnetwork': 'ampliffy.com', - 'SERVER': 'bidder.ampliffy.com' - }, - 'crumbs': {'pubcid': '29844d69-c4e5-4b00-8602-6dd09815363a'}, - 'ortb2Imp': {'ext': {'data': {'pbadslot': 'video1'}}}, - 'mediaTypes': { - 'video': { - 'context': 'instream', - 'playerSize': [[640, 480]], - 'mimes': ['video/mp4'], - 'protocols': [1, 2, 3, 4, 5, 6, 7, 8], - 'playbackmethod': [2], - 'skip': 1 - } - }, - 'adUnitCode': 'video1', - 'transactionId': 'f85c1b10-bad3-4c3f-a2bb-2c484c405bc9', - 'sizes': [[640, 480]], - 'bidId': '2bc71d9c058842', - 'bidderRequestId': '1134bdcbe47f25', - 'auctionId': 'c4a771bf-1791-4513-82b3-96c48d19ddff', - 'src': 'client', - 'bidRequestsCount': 1, - 'bidderRequestsCount': 1, - 'bidderWinsCount': 0 - }], - 'auctionStart': 1644029483655, - 'timeout': 3000, - 'refererInfo': { - 'referer': 'http://localhost:9999/integrationExamples/gpt/hello_world_video.html?pbjs_debug=true', - 'reachedTop': true, - 'isAmp': false, - 'numIframes': 0, - 'stack': ['http://localhost:9999/integrationExamples/gpt/hello_world_video.html?pbjs_debug=true'], - 'canonicalUrl': null - }, - 'start': 1644029483708 - } - const validBidRequests = [ - { - 'bidder': 'ampliffy', - 'params': { - 'placementId': 1235465798, - 'type': 'bidder.', - 'region': 'alan-development.k8s.', - 'adnetwork': 'ampliffy.com', - 'SERVER': 'bidder.ampliffy.com' - }, - 'crumbs': {'pubcid': '29844d69-c4e5-4b00-8602-6dd09815363a'}, - 'ortb2Imp': {'ext': {'data': {'pbadslot': 'video1'}}}, - 'mediaTypes': { - 'video': { - 'context': 'instream', - 'playerSize': [[640, 480]], - 'mimes': ['video/mp4'], - 'protocols': [1, 2, 3, 4, 5, 6, 7, 8], - 'playbackmethod': [2], - 'skip': 1 - } - }, - 'adUnitCode': 'video1', - 'transactionId': 'f85c1b10-bad3-4c3f-a2bb-2c484c405bc9', - 'sizes': [[640, 480]], - 'bidId': '2bc71d9c058842', - 'bidderRequestId': '1134bdcbe47f25', - 'auctionId': 'c4a771bf-1791-4513-82b3-96c48d19ddff', - 'src': 'client', - 'bidRequestsCount': 1, - 'bidderRequestsCount': 1, - 'bidderWinsCount': 0 - } - ]; - it('Should return one or more bid requests', function () { - expect(spec.buildRequests(validBidRequests, bidderRequest).length).to.be.greaterThan(0); - }); - }) - describe('Interpret response', function () { - let bidRequest = { - bidRequest: { - adUnitCode: 'div-gpt-ad-1460505748561-0', - auctionId: '469bb2e2-351f-4d01-b782-cdbca5e3e0ed', - bidId: '2d40b8dcd02ade', - bidRequestsCount: 1, - bidder: 'ampliffy', - bidderRequestId: '128c07edc4680f', - bidderRequestsCount: 1, - bidderWinsCount: 0, - crumbs: { - pubcid: '29844d69-c4e5-4b00-8602-6dd09815363a' - }, - mediaTypes: { - banner: { - sizes: [ - [300, 250], - [300, 600] - ] - } - }, - ortb2Imp: {ext: {}}, - params: {placementId: 13144370}, - sizes: [ - [300, 250], - [300, 600] - ], - src: 'client', - transactionId: '103b2b58-6ed1-45e9-9486-c942d6042e3' - }, - data: {bidId: '2d40b8dcd02ade'}, - method: 'GET', - url: 'https://test.com', - }; - - it('Should extract a CPM and currency from the xml', () => { - let cpmData = parseXML(xml); - expect(cpmData).to.not.be.a('null'); - expect(cpmData.cpm).to.equal('.23'); - expect(cpmData.currency).to.equal('USD'); - }); - - it('It should return no ads when the CPM is less than zero.', () => { - const xmlStr1 = ` - - - - - - - - -
-
-
-
- - - ]]> -
-
- - ES -
-
-
`; - let serverResponse = { - 'body': xmlStr1, - } - const bidResponses = spec.interpretResponse(serverResponse, bidRequest); - expect(bidResponses.length).to.equal(0); - }) - - it('It should return no ads when the creative url is not in the xml', () => { - const xmlStr1 = ` - - - - - - - - -
-
-
-
- - ]]> - - - ES - - - `; - let serverResponse = { - 'body': xmlStr1, - } - const bidResponses = spec.interpretResponse(serverResponse, bidRequest); - expect(bidResponses.length).to.equal(0); - }) - it('It should return a banner ad.', () => { - let serverResponse = { - 'body': xmlStr, - } - setCurrentURL('https://www.sports.com'); - const bidResponses = spec.interpretResponse(serverResponse, bidRequest); - expect(bidResponses.length).greaterThan(0); - expect(bidResponses[0].mediaType).to.be.equal(BANNER); - expect(bidResponses[0].ad).not.to.be.null; - }) - it('It should return a video ad.', () => { - let serverResponse = { - 'body': xmlStr, - } - setCurrentURL('https://www.sports.com'); - bidRequest.bidRequest.mediaTypes = { - video: { - sizes: [ - [300, 250], - [300, 600] - ] - } - } - const bidResponses = spec.interpretResponse(serverResponse, bidRequest); - expect(bidResponses.length).greaterThan(0); - expect(bidResponses[0].mediaType).to.be.equal(VIDEO); - expect(bidResponses[0].vastUrl).not.to.be.null; - }) - }); -}); diff --git a/test/spec/modules/amxBidAdapter_spec.js b/test/spec/modules/amxBidAdapter_spec.js index 21fa2e2617c..6f69e57d8bc 100644 --- a/test/spec/modules/amxBidAdapter_spec.js +++ b/test/spec/modules/amxBidAdapter_spec.js @@ -3,7 +3,6 @@ import { spec } from 'modules/amxBidAdapter.js'; import { createEidsArray } from 'modules/userId/eids.js'; import { BANNER, VIDEO } from 'src/mediaTypes.js'; import { config } from 'src/config.js'; -import { server } from 'test/mocks/xhr.js'; import * as utils from 'src/utils.js'; const sampleRequestId = '82c91e127a9b93e'; @@ -12,7 +11,7 @@ const sampleDisplayCRID = '78827819'; // minimal example vast const sampleVideoAd = (addlImpression) => ` -00:00:15${addlImpression} +00:00:15${addlImpression} `.replace(/\n+/g, ''); const sampleFPD = { @@ -38,11 +37,9 @@ const sampleBidderRequest = { }, gppConsent: { gppString: 'example', - applicableSections: 'example', + applicableSections: 'example' }, - - auctionId: null, - + auctionId: utils.getUniqueIdentifierStr(), uspConsent: '1YYY', refererInfo: { reachedTop: true, @@ -93,8 +90,7 @@ const sampleBidRequestBase = { adUnitCode: 'div-gpt-ad-example', transactionId: utils.getUniqueIdentifierStr(), bidId: sampleRequestId, - - auctionId: null, + auctionId: utils.getUniqueIdentifierStr(), }; const schainConfig = { @@ -210,12 +206,10 @@ describe('AmxBidAdapter', () => { describe('getUserSync', () => { it('Will perform an iframe sync even if there is no server response..', () => { const syncs = spec.getUserSyncs({ iframeEnabled: true }); - expect(syncs).to.eql([ - { - type: 'iframe', - url: 'https://prebid.a-mo.net/isyn?gdpr_consent=&gdpr=0&us_privacy=&gpp=&gpp_sid=', - }, - ]); + expect(syncs).to.eql([{ + type: 'iframe', + url: 'https://prebid.a-mo.net/isyn?gdpr_consent=&gdpr=0&us_privacy=&gpp=&gpp_sid=' + }]); }); it('will return valid syncs from a server response', () => { @@ -279,13 +273,8 @@ describe('AmxBidAdapter', () => { }); it('will attach additional referrer info data', () => { - const { data } = spec.buildRequests( - [sampleBidRequestBase], - sampleBidderRequest - ); - expect(data.ri.r).to.equal( - sampleBidderRequest.refererInfo.topmostLocation - ); + const { data } = spec.buildRequests([sampleBidRequestBase], sampleBidderRequest); + expect(data.ri.r).to.equal(sampleBidderRequest.refererInfo.topmostLocation); expect(data.ri.t).to.equal(sampleBidderRequest.refererInfo.reachedTop); expect(data.ri.l).to.equal(sampleBidderRequest.refererInfo.numIframes); expect(data.ri.s).to.equal(sampleBidderRequest.refererInfo.stack); @@ -323,7 +312,7 @@ describe('AmxBidAdapter', () => { [sampleBidRequestBase], sampleBidderRequest ); - delete data.m; // don't deal with 'm' in this test + delete data.m; // don't deal with "m" in this test expect(data.gs).to.equal(sampleBidderRequest.gdprConsent.gdprApplies); expect(data.gc).to.equal(sampleBidderRequest.gdprConsent.consentString); expect(data.usp).to.equal(sampleBidderRequest.uspConsent); @@ -351,8 +340,10 @@ describe('AmxBidAdapter', () => { }); it('will attach sync configuration', () => { - const request = () => - spec.buildRequests([sampleBidRequestBase], sampleBidderRequest); + const request = () => spec.buildRequests( + [sampleBidRequestBase], + sampleBidderRequest + ); const setConfig = (filterSettings) => config.setConfig({ @@ -361,73 +352,56 @@ describe('AmxBidAdapter', () => { syncDelay: 2300, syncEnabled: true, filterSettings, - }, + } }); const test = (filterSettings) => { setConfig(filterSettings); return request().data.sync; - }; + } const base = { d: 2300, l: 2, e: true }; - const tests = [ - [undefined, { ...base, t: 0 }], - [ - { - image: { - bidders: '*', - filter: 'include', - }, - iframe: { - bidders: '*', - filter: 'include', - }, - }, - { ...base, t: 3 }, - ], - [ - { - image: { - bidders: ['amx'], - }, - iframe: { - bidders: '*', - filter: 'include', - }, - }, - { ...base, t: 3 }, - ], - [ - { - image: { - bidders: ['other'], - }, - iframe: { - bidders: '*', - }, - }, - { ...base, t: 2 }, - ], - [ - { - image: { - bidders: ['amx'], - }, - iframe: { - bidders: ['amx'], - filter: 'exclude', - }, - }, - { ...base, t: 1 }, - ], - ]; + const tests = [[ + undefined, + { ...base, t: 0 } + ], [{ + image: { + bidders: '*', + filter: 'include' + }, + iframe: { + bidders: '*', + filter: 'include' + } + }, { ...base, t: 3 }], [{ + image: { + bidders: ['amx'], + }, + iframe: { + bidders: '*', + filter: 'include' + } + }, { ...base, t: 3 }], [{ + image: { + bidders: ['other'], + }, + iframe: { + bidders: '*' + } + }, { ...base, t: 2 }], [{ + image: { + bidders: ['amx'] + }, + iframe: { + bidders: ['amx'], + filter: 'exclude' + } + }, { ...base, t: 1 }]] for (let i = 0, l = tests.length; i < l; i++) { const [result, expected] = tests[i]; - expect(test(result), `input: ${JSON.stringify(result)}`).to.deep.equal( - expected - ); + expect(test(result), `input: ${JSON.stringify(result)}`).to.deep.equal(expected); } }); @@ -520,15 +494,7 @@ describe('AmxBidAdapter', () => { it('can build a video request', () => { const { data } = spec.buildRequests( - [ - { - ...sampleBidRequestVideo, - params: { - ...sampleBidRequestVideo.params, - adUnitId: 'custom-auid', - }, - }, - ], + [{...sampleBidRequestVideo, params: { ...sampleBidRequestVideo.params, adUnitId: 'custom-auid' }}], sampleBidderRequest ); expect(Object.keys(data.m).length).to.equal(1); @@ -690,49 +656,15 @@ describe('AmxBidAdapter', () => { }); it('will log an event for timeout', () => { - // this will use sendBeacon.. - spec.onTimeout([ - { - bidder: 'example', - bidId: 'test-bid-id', - adUnitCode: 'div-gpt-ad', - ortb2: { - site: { - ref: 'https://example.com', - }, - }, - params: { - tagId: 'tag-id', - }, - timeout: 300, - auctionId: utils.getUniqueIdentifierStr(), - }, - ]); - - const [request] = server.requests; - request.respond(204, {'Content-Type': 'text/html'}, null); - expect(request.url).to.equal('https://1x1.a-mo.net/e'); - - if (typeof Request !== 'undefined' && 'keepalive' in Request.prototype) { - expect(request.fetch.request.keepalive).to.equal(true); - } - - const {c: common, e: events} = JSON.parse(request.requestBody) - expect(common).to.deep.equal({ - V: '$prebid.version$', - vg: '$$PREBID_GLOBAL$$', - U: null, - re: 'https://example.com', + spec.onTimeout({ + bidder: 'example', + bidId: 'test-bid-id', + adUnitCode: 'div-gpt-ad', + timeout: 300, + auctionId: utils.getUniqueIdentifierStr(), }); - - expect(events.length).to.equal(1); - const [event] = events; - expect(event.n).to.equal('g_pbto') - expect(event.A).to.equal('example'); - expect(event.mid).to.equal('tag-id'); - expect(event.cn).to.equal(300); - expect(event.bid).to.equal('test-bid-id'); - expect(event.a).to.equal('div-gpt-ad'); + expect(firedPixels.length).to.equal(1); + expect(firedPixels[0]).to.match(/\/hbx\/g_pbto/); }); it('will log an event for prebid win', () => { diff --git a/test/spec/modules/appnexusBidAdapter_spec.js b/test/spec/modules/appnexusBidAdapter_spec.js index c44ef475290..13ef31a68d4 100644 --- a/test/spec/modules/appnexusBidAdapter_spec.js +++ b/test/spec/modules/appnexusBidAdapter_spec.js @@ -1093,18 +1093,7 @@ describe('AppNexusAdapter', function () { expect(payload.tags[0].use_pmt_rule).to.equal(true); }); - it('should add preferred gpid to the request', function () { - let testGpid = '/12345/my-gpt-tag-0'; - let bidRequest = deepClone(bidRequests[0]); - bidRequest.ortb2Imp = { ext: { gpid: testGpid } }; - - const request = spec.buildRequests([bidRequest]); - const payload = JSON.parse(request.data); - - expect(payload.tags[0].gpid).to.exist.and.equal(testGpid) - }); - - it('should add backup gpid to the request', function () { + it('should add gpid to the request', function () { let testGpid = '/12345/my-gpt-tag-0'; let bidRequest = deepClone(bidRequests[0]); bidRequest.ortb2Imp = { ext: { data: { pbadslot: testGpid } } }; @@ -1204,46 +1193,6 @@ describe('AppNexusAdapter', function () { expect(payload.privacy.gpp_sid).to.deep.equal([7]); }); - it('should add dsa information to the request via bidderRequest.ortb2.regs.ext.dsa', function () { - let bidderRequest = { - 'bidderCode': 'appnexus', - 'auctionId': '1d1a030790a475', - 'bidderRequestId': '22edbae2733bf6', - 'timeout': 3000, - 'ortb2': { - 'regs': { - 'ext': { - 'dsa': { - 'required': 1, - 'pubrender': 0, - 'datatopub': 1, - 'transparency': [{ - 'domain': 'good-domain', - 'params': [1, 2] - }, { - 'domain': 'bad-setup', - 'params': ['1', 3] - }] - } - } - } - } - }; - bidderRequest.bids = bidRequests; - - const request = spec.buildRequests(bidRequests, bidderRequest); - const payload = JSON.parse(request.data); - - expect(payload.dsa).to.exist; - expect(payload.dsa.dsainfo).to.equal(1); - expect(payload.dsa.pubrender).to.equal(0); - expect(payload.dsa.datatopub).to.equal(1); - expect(payload.dsa.transparency).to.deep.equal([{ - 'domain': 'good-domain', - 'params': [1, 2] - }]); - }); - it('supports sending hybrid mobile app parameters', function () { let appRequest = Object.assign({}, bidRequests[0], @@ -1615,15 +1564,6 @@ describe('AppNexusAdapter', function () { 'viewability': { 'config': '' }, - 'dsa': { - 'behalf': 'test-behalf', - 'paid': 'test-paid', - 'transparency': [{ - 'domain': 'good-domain', - 'params': [1, 2, 3] - }], - 'adrender': 1 - }, 'rtb': { 'banner': { 'content': '', @@ -1672,15 +1612,6 @@ describe('AppNexusAdapter', function () { 'nodes': [{ 'bsid': '958' }] - }, - 'dsa': { - 'behalf': 'test-behalf', - 'paid': 'test-paid', - 'transparency': [{ - 'domain': 'good-domain', - 'params': [1, 2, 3] - }], - 'adrender': 1 } } } diff --git a/test/spec/modules/asteriobidAnalyticsAdapter_spec.js b/test/spec/modules/asteriobidAnalyticsAdapter_spec.js deleted file mode 100644 index 9be6c1dedac..00000000000 --- a/test/spec/modules/asteriobidAnalyticsAdapter_spec.js +++ /dev/null @@ -1,151 +0,0 @@ -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'; - -let events = require('src/events'); -let constants = require('src/constants.json'); - -describe('AsterioBid Analytics Adapter', function () { - let bidWonEvent = { - 'bidderCode': 'appnexus', - 'width': 300, - 'height': 250, - 'adId': '1ebb82ec35375e', - 'mediaType': 'banner', - 'cpm': 0.5, - 'requestId': '1582271863760569973', - 'creative_id': '96846035', - 'creativeId': '96846035', - 'ttl': 60, - 'currency': 'USD', - 'netRevenue': true, - 'auctionId': '9c7b70b9-b6ab-4439-9e71-b7b382797c18', - 'responseTimestamp': 1537521629657, - 'requestTimestamp': 1537521629331, - 'bidder': 'appnexus', - 'adUnitCode': 'div-gpt-ad-1460505748561-0', - 'timeToRespond': 326, - 'size': '300x250', - 'status': 'rendered', - 'eventType': 'bidWon', - 'ad': 'some ad', - 'adUrl': 'ad url' - }; - - describe('AsterioBid Analytic tests', function () { - beforeEach(function () { - sinon.stub(events, 'getEvents').returns([]); - }); - - afterEach(function () { - asteriobidAnalytics.disableAnalytics(); - events.getEvents.restore(); - }); - - it('support custom endpoint', function () { - let custom_url = 'custom url'; - asteriobidAnalytics.enableAnalytics({ - provider: 'asteriobid', - options: { - url: custom_url, - bundleId: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' - } - }); - - expect(asteriobidAnalytics.getOptions().url).to.equal(custom_url); - }); - - it('bid won event', function() { - let bundleId = 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'; - asteriobidAnalytics.enableAnalytics({ - provider: 'asteriobid', - options: { - bundleId: bundleId - } - }); - - events.emit(constants.EVENTS.BID_WON, bidWonEvent); - asteriobidAnalytics.flush(); - - expect(server.requests.length).to.equal(1); - expect(server.requests[0].url).to.equal('https://endpt.asteriobid.com/endpoint'); - expect(server.requests[0].requestBody.substring(0, 2)).to.equal('1:'); - - const pmEvents = JSON.parse(server.requests[0].requestBody.substring(2)); - expect(pmEvents.pageViewId).to.exist; - expect(pmEvents.bundleId).to.equal(bundleId); - expect(pmEvents.ver).to.equal(1); - expect(pmEvents.events.length).to.equal(1); - expect(pmEvents.events[0].eventType).to.equal('bidWon'); - expect(pmEvents.events[0].ad).to.be.undefined; - expect(pmEvents.events[0].adUrl).to.be.undefined; - }); - - it('track event without errors', function () { - sinon.spy(asteriobidAnalytics, 'track'); - - asteriobidAnalytics.enableAnalytics({ - provider: 'asteriobid', - options: { - bundleId: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' - } - }); - - expectEvents().to.beTrackedBy(asteriobidAnalytics.track); - }); - }); - - describe('build utm tag data', function () { - let getDataFromLocalStorageStub; - this.timeout(4000) - beforeEach(function () { - getDataFromLocalStorageStub = sinon.stub(storage, 'getDataFromLocalStorage'); - getDataFromLocalStorageStub.withArgs('pm_utm_source').returns('utm_source'); - getDataFromLocalStorageStub.withArgs('pm_utm_medium').returns('utm_medium'); - getDataFromLocalStorageStub.withArgs('pm_utm_campaign').returns('utm_camp'); - getDataFromLocalStorageStub.withArgs('pm_utm_term').returns(''); - getDataFromLocalStorageStub.withArgs('pm_utm_content').returns(''); - }); - afterEach(function () { - getDataFromLocalStorageStub.restore(); - asteriobidAnalytics.disableAnalytics() - }); - it('should build utm data from local storage', function () { - asteriobidAnalytics.enableAnalytics({ - provider: 'asteriobid', - options: { - bundleId: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' - } - }); - - const pmEvents = JSON.parse(server.requests[0].requestBody.substring(2)); - - expect(pmEvents.utmTags.utm_source).to.equal('utm_source'); - expect(pmEvents.utmTags.utm_medium).to.equal('utm_medium'); - expect(pmEvents.utmTags.utm_campaign).to.equal('utm_camp'); - expect(pmEvents.utmTags.utm_term).to.equal(''); - expect(pmEvents.utmTags.utm_content).to.equal(''); - }); - }); - - describe('build page info', function () { - afterEach(function () { - asteriobidAnalytics.disableAnalytics() - }); - it('should build page info', function () { - asteriobidAnalytics.enableAnalytics({ - provider: 'asteriobid', - options: { - bundleId: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' - } - }); - - const pmEvents = JSON.parse(server.requests[0].requestBody.substring(2)); - - expect(pmEvents.pageInfo.domain).to.equal(window.location.hostname); - expect(pmEvents.pageInfo.referrerDomain).to.equal(utils.parseUrl(document.referrer).hostname); - }); - }); -}); diff --git a/test/spec/modules/automatadAnalyticsAdapter_spec.js b/test/spec/modules/automatadAnalyticsAdapter_spec.js deleted file mode 100644 index e591f7e8e95..00000000000 --- a/test/spec/modules/automatadAnalyticsAdapter_spec.js +++ /dev/null @@ -1,533 +0,0 @@ -import * as events from 'src/events'; -import * as utils from 'src/utils.js'; - -import spec, {self as exports} from 'modules/automatadAnalyticsAdapter.js'; - -import CONSTANTS from 'src/constants.json'; -import { expect } from 'chai'; - -const { - AUCTION_DEBUG, - BID_REQUESTED, - BID_REJECTED, - AUCTION_INIT, - BIDDER_DONE, - BID_RESPONSE, - BID_TIMEOUT, - BID_WON, - NO_BID -} = CONSTANTS.EVENTS - -const CONFIG_WITH_DEBUG = { - provider: 'atmtdAnalyticsAdapter', - options: { - publisherID: '230', - siteID: '421' - }, - includeEvents: [AUCTION_DEBUG, AUCTION_INIT, BIDDER_DONE, BID_RESPONSE, BID_TIMEOUT, NO_BID, BID_WON, BID_REQUESTED, BID_REJECTED] -} - -describe('Automatad Analytics Adapter', () => { - var sandbox, clock; - - describe('Adapter Setup Configuration', () => { - beforeEach(() => { - sandbox = sinon.createSandbox(); - sandbox.stub(utils, 'logMessage') - sandbox.stub(events, 'getEvents').returns([]); - sandbox.stub(utils, 'logError'); - }); - afterEach(() => { - sandbox.restore(); - }); - - it('Should log error and return false if nothing is passed as the param in the enable analytics call', () => { - spec.enableAnalytics() - - expect(utils.logError.called).to.equal(true) - }); - - it('Should log error and return false if object type is not passed as the param in the enable analytics call', () => { - spec.enableAnalytics('hello world') - - expect(utils.logError.called).to.equal(true) - }); - - it('Should log error and return false if options is not defined in the enable analytics call', () => { - spec.enableAnalytics({ - provider: 'atmtdAnalyticsAdapter' - }) - - expect(utils.logError.called).to.equal(true) - }); - it('Should log error and return false if pub id is not defined in the enable analytics call', () => { - spec.enableAnalytics({ - provider: 'atmtdAnalyticsAdapter', - options: { - siteID: '230' - } - }) - - expect(utils.logError.called).to.equal(true) - }); - it('Should log error and return false if pub id is not defined in the enable analytics call', () => { - spec.enableAnalytics({ - provider: 'atmtdAnalyticsAdapter', - options: { - publisherID: '230' - } - }) - - expect(utils.logError.called).to.equal(true) - }); - it('Should successfully configure the adapter and set global log debug messages flag to false', () => { - spec.enableAnalytics({ - provider: 'atmtdAnalyticsAdapter', - options: { - publisherID: '230', - siteID: '421', - logDebug: false - } - }); - expect(utils.logError.called).to.equal(false) - expect(utils.logMessage.called).to.equal(true) - spec.disableAnalytics(); - }); - it('Should successfully configure the adapter and set global log debug messages flag to true', () => { - sandbox.stub(exports, 'initializeQueue').callsFake(() => {}); - sandbox.stub(exports, 'addGPTHandlers').callsFake(() => {}); - const config = { - provider: 'atmtdAnalyticsAdapter', - options: { - publisherID: '230', - siteID: '410', - logDebug: true - } - } - - spec.enableAnalytics(config) - expect(utils.logError.called).to.equal(false) - expect(exports.initializeQueue.called).to.equal(true) - expect(exports.addGPTHandlers.called).to.equal(true) - expect(utils.logMessage.called).to.equal(true) - spec.disableAnalytics(); - }); - }); - - describe('Behaviour of the adapter when the sdk has loaded', () => { - before(() => { - spec.enableAnalytics(CONFIG_WITH_DEBUG); - const obj = { - auctionInitHandler: (args) => {}, - bidResponseHandler: (args) => {}, - bidderDoneHandler: (args) => {}, - bidWonHandler: (args) => {}, - noBidHandler: (args) => {}, - auctionDebugHandler: (args) => {}, - bidderTimeoutHandler: (args) => {}, - bidRequestedHandler: (args) => {}, - bidRejectedHandler: (args) => {} - } - - global.window.atmtdAnalytics = obj - - Object.keys(obj).forEach((fn) => sandbox.spy(global.window.atmtdAnalytics, fn)) - }) - beforeEach(() => { - sandbox = sinon.createSandbox(); - sandbox.stub(events, 'getEvents').returns([]); - sandbox.stub(utils, 'logMessage'); - sandbox.stub(utils, 'logError'); - }); - afterEach(() => { - sandbox.restore(); - }); - after(() => { - global.window.atmtdAnalytics = undefined; - spec.disableAnalytics(); - }) - - it('Should call the auctionInitHandler when the auction init event is fired', () => { - 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}) - 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}) - 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}) - 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}) - 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}) - 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}) - 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}) - 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}) - expect(global.window.atmtdAnalytics.auctionDebugHandler.called).to.equal(true) - }); - }); - - describe('Behaviour of the adapter when the SDK has not loaded', () => { - before(() => { - spec.enableAnalytics(CONFIG_WITH_DEBUG); - }) - beforeEach(() => { - sandbox = sinon.createSandbox(); - sandbox.stub(events, 'getEvents').returns([]); - sandbox.stub(utils, 'logMessage'); - sandbox.stub(utils, 'logError'); - - global.window.atmtdAnalytics = undefined - exports.__atmtdAnalyticsQueue.length = 0 - sandbox.stub(exports.__atmtdAnalyticsQueue, 'push').callsFake((args) => { - Array.prototype.push.apply(exports.__atmtdAnalyticsQueue, [args]); - }) - }); - afterEach(() => { - sandbox.restore(); - }); - after(() => { - spec.disableAnalytics(); - }) - - it('Should push to the que when the auctionInit event is fired', () => { - 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) - expect(exports.__atmtdAnalyticsQueue[0][0]).to.equal(AUCTION_INIT) - expect(exports.__atmtdAnalyticsQueue[0][1].type).to.equal(AUCTION_INIT) - }); - - it('Should push to the que when the bidResponse event is fired', () => { - 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) - expect(exports.__atmtdAnalyticsQueue[0][0]).to.equal(BID_RESPONSE) - expect(exports.__atmtdAnalyticsQueue[0][1].type).to.equal(BID_RESPONSE) - }); - - it('Should push to the que when the bidRequested event is fired', () => { - 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) - expect(exports.__atmtdAnalyticsQueue[0][0]).to.equal(BID_REQUESTED) - expect(exports.__atmtdAnalyticsQueue[0][1].type).to.equal(BID_REQUESTED) - }); - - it('Should push to the que when the bidRejected event is fired', () => { - 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) - expect(exports.__atmtdAnalyticsQueue[0][0]).to.equal(BID_REJECTED) - expect(exports.__atmtdAnalyticsQueue[0][1].type).to.equal(BID_REJECTED) - }); - - it('Should push to the que when the bidderDone event is fired', () => { - 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) - expect(exports.__atmtdAnalyticsQueue[0][0]).to.equal(BIDDER_DONE) - expect(exports.__atmtdAnalyticsQueue[0][1].type).to.equal(BIDDER_DONE) - }); - - it('Should push to the que when the bidWon event is fired', () => { - 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) - expect(exports.__atmtdAnalyticsQueue[0][0]).to.equal(BID_WON) - expect(exports.__atmtdAnalyticsQueue[0][1].type).to.equal(BID_WON) - }); - - it('Should push to the que when the noBid event is fired', () => { - 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) - expect(exports.__atmtdAnalyticsQueue[0][0]).to.equal(NO_BID) - expect(exports.__atmtdAnalyticsQueue[0][1].type).to.equal(NO_BID) - }); - - it('Should push to the que when the auctionDebug is fired', () => { - 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) - expect(exports.__atmtdAnalyticsQueue[0][0]).to.equal(AUCTION_DEBUG) - expect(exports.__atmtdAnalyticsQueue[0][1].type).to.equal(AUCTION_DEBUG) - }); - - it('Should push to the que when the bidderTimeout event is fired', () => { - 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) - expect(exports.__atmtdAnalyticsQueue[0][0]).to.equal(BID_TIMEOUT) - expect(exports.__atmtdAnalyticsQueue[0][1].type).to.equal(BID_TIMEOUT) - }); - }); - - describe('Process Events from Que when SDK still has not loaded', () => { - before(() => { - spec.enableAnalytics({ - provider: 'atmtdAnalyticsAdapter', - options: { - publisherID: '230', - siteID: '421' - } - }); - global.window.atmtdAnalytics = undefined - - sandbox.stub(exports.__atmtdAnalyticsQueue, 'push').callsFake((args) => { - Array.prototype.push.apply(exports.__atmtdAnalyticsQueue, [args]); - }) - }) - beforeEach(() => { - sandbox = sinon.createSandbox(); - sandbox.stub(events, 'getEvents').returns([]); - sandbox.spy(exports, 'prettyLog') - sandbox.spy(exports, 'processEvents') - - clock = sandbox.useFakeTimers(); - exports.__atmtdAnalyticsQueue.length = 0 - }); - afterEach(() => { - sandbox.restore(); - exports.queuePointer = 0; - exports.retryCount = 0; - exports.__atmtdAnalyticsQueue = [] - spec.disableAnalytics(); - }) - - 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}]] - exports.__atmtdAnalyticsQueue.push(que[0]) - exports.processEvents() - expect(exports.prettyLog.getCall(0).args[0]).to.equal('status') - expect(exports.prettyLog.getCall(0).args[1]).to.equal(`Que has been inactive for a while. Adapter starting to process que now... Trial Count = 1`) - expect(exports.prettyLog.getCall(1).args[0]).to.equal('warn') - expect(exports.prettyLog.getCall(1).args[1]).to.equal(`Adapter failed to process event as aggregator has not loaded. Retrying in 1500ms ...`) - clock.tick(1510) - expect(exports.prettyLog.getCall(2).args[0]).to.equal('status') - expect(exports.prettyLog.getCall(2).args[1]).to.equal(`Que has been inactive for a while. Adapter starting to process que now... Trial Count = 2`) - expect(exports.prettyLog.getCall(3).args[0]).to.equal('warn') - expect(exports.prettyLog.getCall(3).args[1]).to.equal(`Adapter failed to process event as aggregator has not loaded. Retrying in 3000ms ...`) - clock.tick(3010) - expect(exports.prettyLog.getCall(4).args[0]).to.equal('status') - expect(exports.prettyLog.getCall(4).args[1]).to.equal(`Que has been inactive for a while. Adapter starting to process que now... Trial Count = 3`) - expect(exports.prettyLog.getCall(5).args[0]).to.equal('warn') - expect(exports.prettyLog.getCall(5).args[1]).to.equal(`Adapter failed to process event as aggregator has not loaded. Retrying in 5000ms ...`) - clock.tick(5010) - expect(exports.prettyLog.getCall(6).args[0]).to.equal('status') - expect(exports.prettyLog.getCall(6).args[1]).to.equal(`Que has been inactive for a while. Adapter starting to process que now... Trial Count = 4`) - expect(exports.prettyLog.getCall(7).args[0]).to.equal('warn') - expect(exports.prettyLog.getCall(7).args[1]).to.equal(`Adapter failed to process event as aggregator has not loaded. Retrying in 10000ms ...`) - clock.tick(10010) - expect(exports.prettyLog.getCall(8).args[0]).to.equal('error') - expect(exports.prettyLog.getCall(8).args[1]).to.equal(`Aggregator still hasn't loaded. Processing que stopped`) - expect(exports.queuePointer).to.equal(0) - expect(exports.processEvents.callCount).to.equal(5) - }) - - 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'}]] - exports.__atmtdAnalyticsQueue.push(que[0]) - exports.processEvents() - expect(exports.prettyLog.getCall(0).args[0]).to.equal('status') - expect(exports.prettyLog.getCall(0).args[1]).to.equal(`Que has been inactive for a while. Adapter starting to process que now... Trial Count = 1`) - expect(exports.prettyLog.getCall(1).args[0]).to.equal('warn') - expect(exports.prettyLog.getCall(1).args[1]).to.equal(`Adapter failed to process event as aggregator has not loaded. Retrying in 1500ms ...`) - clock.tick(1510) - expect(exports.prettyLog.getCall(2).args[0]).to.equal('status') - expect(exports.prettyLog.getCall(2).args[1]).to.equal(`Que has been inactive for a while. Adapter starting to process que now... Trial Count = 2`) - expect(exports.prettyLog.getCall(3).args[0]).to.equal('warn') - expect(exports.prettyLog.getCall(3).args[1]).to.equal(`Adapter failed to process event as aggregator has not loaded. Retrying in 3000ms ...`) - clock.tick(3010) - expect(exports.prettyLog.getCall(4).args[0]).to.equal('status') - expect(exports.prettyLog.getCall(4).args[1]).to.equal(`Que has been inactive for a while. Adapter starting to process que now... Trial Count = 3`) - expect(exports.prettyLog.getCall(5).args[0]).to.equal('warn') - expect(exports.prettyLog.getCall(5).args[1]).to.equal(`Adapter failed to process event as aggregator has not loaded. Retrying in 5000ms ...`) - clock.tick(5010) - expect(exports.prettyLog.getCall(6).args[0]).to.equal('status') - expect(exports.prettyLog.getCall(6).args[1]).to.equal(`Que has been inactive for a while. Adapter starting to process que now... Trial Count = 4`) - expect(exports.prettyLog.getCall(7).args[0]).to.equal('warn') - expect(exports.prettyLog.getCall(7).args[1]).to.equal(`Adapter failed to process event as aggregator has not loaded. Retrying in 10000ms ...`) - clock.tick(10010) - expect(exports.prettyLog.getCall(8).args[0]).to.equal('error') - expect(exports.prettyLog.getCall(8).args[1]).to.equal(`Aggregator still hasn't loaded. Processing que stopped`) - expect(exports.queuePointer).to.equal(0) - expect(exports.processEvents.callCount).to.equal(5) - }) - - 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'}]] - exports.__atmtdAnalyticsQueue.push(que[0]) - exports.processEvents() - expect(exports.prettyLog.getCall(0).args[0]).to.equal('status') - expect(exports.prettyLog.getCall(0).args[1]).to.equal(`Que has been inactive for a while. Adapter starting to process que now... Trial Count = 1`) - expect(exports.prettyLog.getCall(1).args[0]).to.equal('warn') - expect(exports.prettyLog.getCall(1).args[1]).to.equal(`Adapter failed to process event as aggregator has not loaded. Retrying in 1500ms ...`) - clock.tick(1510) - expect(exports.prettyLog.getCall(2).args[0]).to.equal('status') - expect(exports.prettyLog.getCall(2).args[1]).to.equal(`Que has been inactive for a while. Adapter starting to process que now... Trial Count = 2`) - expect(exports.prettyLog.getCall(3).args[0]).to.equal('warn') - expect(exports.prettyLog.getCall(3).args[1]).to.equal(`Adapter failed to process event as aggregator has not loaded. Retrying in 3000ms ...`) - clock.tick(3010) - expect(exports.prettyLog.getCall(4).args[0]).to.equal('status') - expect(exports.prettyLog.getCall(4).args[1]).to.equal(`Que has been inactive for a while. Adapter starting to process que now... Trial Count = 3`) - expect(exports.prettyLog.getCall(5).args[0]).to.equal('warn') - expect(exports.prettyLog.getCall(5).args[1]).to.equal(`Adapter failed to process event as aggregator has not loaded. Retrying in 5000ms ...`) - clock.tick(5010) - expect(exports.prettyLog.getCall(6).args[0]).to.equal('status') - expect(exports.prettyLog.getCall(6).args[1]).to.equal(`Que has been inactive for a while. Adapter starting to process que now... Trial Count = 4`) - expect(exports.prettyLog.getCall(7).args[0]).to.equal('warn') - expect(exports.prettyLog.getCall(7).args[1]).to.equal(`Adapter failed to process event as aggregator has not loaded. Retrying in 10000ms ...`) - clock.tick(10010) - expect(exports.prettyLog.getCall(8).args[0]).to.equal('error') - expect(exports.prettyLog.getCall(8).args[1]).to.equal(`Aggregator still hasn't loaded. Processing que stopped`) - expect(exports.queuePointer).to.equal(0) - expect(exports.processEvents.callCount).to.equal(5) - }) - }); - - describe('Process Events from Que when SDK has loaded', () => { - before(() => { - spec.enableAnalytics({ - provider: 'atmtdAnalyticsAdapter', - options: { - publisherID: '230', - siteID: '421' - } - }); - sandbox = sinon.createSandbox(); - sandbox.reset() - const obj = { - auctionInitHandler: (args) => {}, - bidResponseHandler: (args) => {}, - bidderDoneHandler: (args) => {}, - bidWonHandler: (args) => {}, - noBidHandler: (args) => {}, - auctionDebugHandler: (args) => {}, - bidderTimeoutHandler: (args) => {}, - impressionViewableHandler: (args) => {}, - slotRenderEndedGPTHandler: (args) => {}, - bidRequestedHandler: (args) => {}, - bidRejectedHandler: (args) => {} - } - - global.window.atmtdAnalytics = obj; - - Object.keys(obj).forEach((fn) => sandbox.spy(global.window.atmtdAnalytics, fn)) - sandbox.stub(events, 'getEvents').returns([]); - sandbox.spy(exports, 'prettyLog') - 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'}] - ] - }); - after(() => { - sandbox.restore(); - spec.disableAnalytics(); - }) - - it('Should make calls to appropriate SDK event handlers', () => { - exports.processEvents() - expect(exports.prettyLog.getCall(0).args[0]).to.equal('status') - expect(exports.prettyLog.getCall(0).args[1]).to.equal(`Que has been inactive for a while. Adapter starting to process que now... Trial Count = 1`) - expect(exports.retryCount).to.equal(0) - expect(exports.prettyLog.callCount).to.equal(1) - expect(exports.queuePointer).to.equal(exports.__atmtdAnalyticsQueue.length) - expect(global.window.atmtdAnalytics.auctionInitHandler.calledOnce).to.equal(true) - expect(global.window.atmtdAnalytics.bidResponseHandler.calledOnce).to.equal(true) - expect(global.window.atmtdAnalytics.bidRejectedHandler.calledOnce).to.equal(true) - expect(global.window.atmtdAnalytics.bidRequestedHandler.calledOnce).to.equal(true) - expect(global.window.atmtdAnalytics.noBidHandler.calledOnce).to.equal(true) - expect(global.window.atmtdAnalytics.bidWonHandler.calledOnce).to.equal(true) - expect(global.window.atmtdAnalytics.auctionDebugHandler.calledOnce).to.equal(true) - expect(global.window.atmtdAnalytics.bidderTimeoutHandler.calledOnce).to.equal(true) - expect(global.window.atmtdAnalytics.bidderDoneHandler.calledOnce).to.equal(true) - expect(global.window.atmtdAnalytics.slotRenderEndedGPTHandler.calledOnce).to.equal(true) - expect(global.window.atmtdAnalytics.impressionViewableHandler.calledOnce).to.equal(true) - }) - }); - - describe('Prettylog fn tests', () => { - beforeEach(() => { - sandbox = sinon.createSandbox() - sandbox.spy(utils, 'logInfo') - sandbox.spy(utils, 'logError') - exports.isLoggingEnabled = true - }) - - afterEach(() => { - sandbox.restore() - }) - - it('Should call logMessage once in normal mode', () => { - exports.prettyLog('status', 'Hello world') - expect(utils.logInfo.callCount).to.equal(1) - }) - - it('Should call logMessage twice in group mode and have the cb called', () => { - const spy = sandbox.spy() - exports.prettyLog('status', 'Hello world', true, spy) - expect(utils.logInfo.callCount).to.equal(2) - expect(spy.called).to.equal(true) - }) - - it('Should call logMessage twice in group mode and have the cb which throws an error', () => { - const spy = sandbox.stub().throws() - exports.prettyLog('status', 'Hello world', true, spy) - expect(utils.logInfo.callCount).to.equal(2) - expect(utils.logError.called).to.equal(true) - }) - }); -}); diff --git a/test/spec/modules/axisBidAdapter_spec.js b/test/spec/modules/axisBidAdapter_spec.js deleted file mode 100644 index 083f05f5c0a..00000000000 --- a/test/spec/modules/axisBidAdapter_spec.js +++ /dev/null @@ -1,414 +0,0 @@ -import { expect } from 'chai'; -import { spec } from '../../../modules/axisBidAdapter.js'; -import { BANNER, VIDEO, NATIVE } from '../../../src/mediaTypes.js'; -import { getUniqueIdentifierStr } from '../../../src/utils.js'; - -const bidder = 'axis' - -describe('AxisBidAdapter', function () { - const bids = [ - { - bidId: getUniqueIdentifierStr(), - bidder: bidder, - mediaTypes: { - [BANNER]: { - sizes: [[300, 250]], - pos: 1 - } - }, - params: { - integration: '000000', - token: '000000' - } - }, - { - bidId: getUniqueIdentifierStr(), - bidder: bidder, - mediaTypes: { - [VIDEO]: { - playerSize: [[300, 300]], - minduration: 5, - maxduration: 60, - pos: 1 - } - }, - params: { - integration: '000000', - token: '000000' - } - }, - { - bidId: getUniqueIdentifierStr(), - bidder: bidder, - mediaTypes: { - [NATIVE]: { - native: { - title: { - required: true - }, - body: { - required: true - }, - icon: { - required: true, - size: [64, 64] - } - } - } - }, - params: { - integration: '000000', - token: '000000' - } - } - ]; - - const invalidBid = { - bidId: getUniqueIdentifierStr(), - bidder: bidder, - mediaTypes: { - [BANNER]: { - sizes: [[300, 250]] - } - }, - params: { - - } - } - - const bidderRequest = { - uspConsent: '1---', - gdprConsent: 'COvFyGBOvFyGBAbAAAENAPCAAOAAAAAAAAAAAEEUACCKAAA.IFoEUQQgAIQwgIwQABAEAAAAOIAACAIAAAAQAIAgEAACEAAAAAgAQBAAAAAAAGBAAgAAAAAAAFAAECAAAgAAQARAEQAAAAAJAAIAAgAAAYQEAAAQmAgBC3ZAYzUw', - refererInfo: { - referer: 'https://test.com' - }, - ortb2: { - site: { - cat: ['IAB24'] - } - } - }; - - 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://prebid.axis-marketplace.com/pbjs'); - }); - - it('Returns general data valid', function () { - let data = serverRequest.data; - expect(data).to.be.an('object'); - expect(data).to.have.all.keys('deviceWidth', - 'deviceHeight', - 'language', - 'secure', - 'host', - 'page', - 'placements', - 'iabCat', - 'coppa', - 'ccpa', - 'gdpr', - 'tmax' - ); - 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('string'); - expect(data.ccpa).to.be.a('string'); - expect(data.tmax).to.be.a('number'); - expect(data.iabCat).to.have.lengthOf(1); - 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.adFormat).to.be.oneOf([BANNER, VIDEO, NATIVE]); - expect(placement.bidId).to.be.a('string'); - expect(placement.integration).to.be.a('string'); - expect(placement.token).to.be.a('string'); - expect(placement.schain).to.be.an('object'); - expect(placement.bidfloor).to.exist.and.to.equal(0); - - if (placement.adFormat === BANNER) { - expect(placement.sizes).to.be.an('array'); - } - switch (placement.adFormat) { - case BANNER: - expect(placement.sizes).to.be.an('array'); - expect(placement.pos).to.be.within(0, 7); - break; - case VIDEO: - expect(placement.playerSize).to.be.an('array'); - expect(placement.minduration).to.be.an('number'); - expect(placement.maxduration).to.be.an('number'); - expect(placement.pos).to.be.within(0, 7); - 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); - let data = serverRequest.data; - expect(data.gdpr).to.exist; - expect(data.gdpr).to.be.a('string'); - expect(data.gdpr).to.equal(bidderRequest.gdprConsent); - 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); - let 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; - }); - - it('Returns empty data if no valid requests are passed', function () { - serverRequest = spec.buildRequests([], bidderRequest); - let data = serverRequest.data; - expect(data.placements).to.be.an('array').that.is.empty; - }); - }); - - 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 - } - }] - }; - let bannerResponses = spec.interpretResponse(banner); - expect(bannerResponses).to.be.an('array').that.is.not.empty; - let dataItem = bannerResponses[0]; - expect(dataItem).to.have.all.keys('requestId', 'cpm', 'width', 'height', 'ad', 'ttl', 'creativeId', - 'netRevenue', 'currency', 'dealId', '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.property('advertiserDomains'); - }); - it('Should interpret video response', function () { - const video = { - body: [{ - vastUrl: 'test.com', - mediaType: 'video', - cpm: 0.5, - requestId: '23fhj33i987f', - width: 300, - height: 250, - ttl: 120, - creativeId: '2', - netRevenue: true, - currency: 'USD', - dealId: '1', - meta: { - advertiserDomains: ['google.com'], - advertiserId: 1234 - } - }] - }; - let videoResponses = spec.interpretResponse(video); - expect(videoResponses).to.be.an('array').that.is.not.empty; - - let dataItem = videoResponses[0]; - expect(dataItem).to.have.all.keys('requestId', 'cpm', 'vastUrl', 'ttl', 'creativeId', - 'netRevenue', 'currency', 'dealId', 'mediaType', 'meta', 'width', 'height'); - 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.property('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 - } - }] - }; - let nativeResponses = spec.interpretResponse(native); - expect(nativeResponses).to.be.an('array').that.is.not.empty; - - let dataItem = nativeResponses[0]; - expect(dataItem).to.have.keys('requestId', 'cpm', '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.property('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' - }] - }; - - let serverResponses = spec.interpretResponse(invBanner); - expect(serverResponses).to.be.an('array').that.is.empty; - }); - it('Should return an empty array if invalid video response is passed', function () { - const invVideo = { - body: [{ - mediaType: 'video', - cpm: 0.5, - requestId: '23fhj33i987f', - ttl: 120, - creativeId: '2', - netRevenue: true, - currency: 'USD', - dealId: '1' - }] - }; - let serverResponses = spec.interpretResponse(invVideo); - expect(serverResponses).to.be.an('array').that.is.empty; - }); - it('Should return an empty array if invalid native response is passed', function () { - const invNative = { - body: [{ - mediaType: 'native', - clickUrl: 'test.com', - title: 'Test', - impressionTrackers: ['test.com'], - ttl: 120, - requestId: '23fhj33i987f', - creativeId: '2', - netRevenue: true, - currency: 'USD', - }] - }; - let serverResponses = spec.interpretResponse(invNative); - expect(serverResponses).to.be.an('array').that.is.empty; - }); - it('Should return an empty array if invalid response is passed', function () { - const invalid = { - body: [{ - ttl: 120, - creativeId: '2', - netRevenue: true, - currency: 'USD', - dealId: '1' - }] - }; - let serverResponses = spec.interpretResponse(invalid); - expect(serverResponses).to.be.an('array').that.is.empty; - }); - }); - - describe('getUserSyncs', function() { - it('Should return array of objects with proper sync config , include GDPR', function() { - const syncData = spec.getUserSyncs({}, {}, { - consentString: 'ALL', - gdprApplies: true, - }, {}); - 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://cs.axis-marketplace.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---' - }); - 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://cs.axis-marketplace.com/image?pbjs=1&ccpa=1---&coppa=0') - }); - }); -}); diff --git a/test/spec/modules/beachfrontBidAdapter_spec.js b/test/spec/modules/beachfrontBidAdapter_spec.js index c0994985aae..4e30b822a61 100644 --- a/test/spec/modules/beachfrontBidAdapter_spec.js +++ b/test/spec/modules/beachfrontBidAdapter_spec.js @@ -1,6 +1,7 @@ import { expect } from 'chai'; import { spec, VIDEO_ENDPOINT, BANNER_ENDPOINT, OUTSTREAM_SRC, DEFAULT_MIMES } from 'modules/beachfrontBidAdapter.js'; -import { parseUrl } from 'src/utils.js'; +import { config } from 'src/config.js'; +import { parseUrl, deepAccess } from 'src/utils.js'; describe('BeachfrontAdapter', function () { let bidRequests; @@ -295,23 +296,6 @@ describe('BeachfrontAdapter', function () { expect(data.user.ext.consent).to.equal(consentString); }); - it('must add GPP consent data to the request', function () { - const bidRequest = bidRequests[0]; - bidRequest.mediaTypes = { video: {} }; - const gppString = 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A=='; - const applicableSections = [1, 2, 3]; - const bidderRequest = { - gppConsent: { - gppString, - applicableSections - } - }; - 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); - }); - it('must add schain data to the request', () => { const schain = { ver: '1.0', @@ -533,23 +517,6 @@ describe('BeachfrontAdapter', function () { expect(data.gdprConsent).to.equal(consentString); }); - it('must add GPP consent data to the request', function () { - const bidRequest = bidRequests[0]; - bidRequest.mediaTypes = { banner: {} }; - const gppString = 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A=='; - const applicableSections = [1, 2, 3]; - const bidderRequest = { - gppConsent: { - gppString, - applicableSections - } - }; - const requests = spec.buildRequests([ bidRequest ], bidderRequest); - const data = requests[0].data; - expect(data.gpp).to.equal(gppString); - expect(data.gppSid).to.deep.equal(applicableSections); - }); - it('must add schain data to the request', () => { const schain = { ver: '1.0', diff --git a/test/spec/modules/beopBidAdapter_spec.js b/test/spec/modules/beopBidAdapter_spec.js index c77e304e539..22c48c2d182 100644 --- a/test/spec/modules/beopBidAdapter_spec.js +++ b/test/spec/modules/beopBidAdapter_spec.js @@ -130,15 +130,15 @@ describe('BeOp Bid Adapter tests', () => { expect(payload.url).to.exist; // check that the protocol is added correctly expect(payload.url).to.equal('http://test.te'); + expect(payload.psegs).to.not.exist; }); - it('should call the endpoint with psegs and bpsegs (stringified) data if any or [] if none', function () { + it('should call the endpoint with psegs data if any', function () { let bidderRequest = { 'ortb2': { 'user': { 'ext': { - 'bpsegs': ['axed', 'axec', 1234], 'data': { 'permutive': [1234, 5678, 910] } @@ -154,22 +154,6 @@ describe('BeOp Bid Adapter tests', () => { expect(payload.psegs).to.include(5678); expect(payload.psegs).to.include(910); expect(payload.psegs).to.not.include(1); - expect(payload.bpsegs).to.exist; - expect(payload.bpsegs).to.include('axed'); - expect(payload.bpsegs).to.include('axec'); - expect(payload.bpsegs).to.include('1234'); - - let bidderRequest2 = - { - 'ortb2': {} - }; - - const request2 = spec.buildRequests(bidRequests, bidderRequest2); - const payload2 = JSON.parse(request2.data); - expect(payload2.psegs).to.exist; - expect(payload2.psegs).to.be.empty; - expect(payload2.bpsegs).to.exist; - expect(payload2.bpsegs).to.be.empty; }); it('should not prepend the protocol in page url if already present', function () { diff --git a/test/spec/modules/bidViewability_spec.js b/test/spec/modules/bidViewability_spec.js index 2d2e51abbe1..a822d86f852 100644 --- a/test/spec/modules/bidViewability_spec.js +++ b/test/spec/modules/bidViewability_spec.js @@ -245,31 +245,18 @@ describe('#bidViewability', function() { let logWinningBidNotFoundSpy; let callBidViewableBidderSpy; let winningBidsArray; - let callBidBillableBidderSpy; - let adUnits = [ - { - 'code': 'abc123', - 'bids': [ - { - 'bidder': 'pubmatic' - } - ] - } - ]; beforeEach(function() { sandbox = sinon.sandbox.create(); triggerPixelSpy = sandbox.spy(utils, ['triggerPixel']); eventsEmitSpy = sandbox.spy(events, ['emit']); callBidViewableBidderSpy = sandbox.spy(adapterManager, ['callBidViewableBidder']); - callBidBillableBidderSpy = sandbox.spy(adapterManager, ['callBidBillableBidder']); // mocking winningBidsArray winningBidsArray = []; sandbox.stub(prebidGlobal, 'getGlobal').returns({ getAllWinningBids: function (number) { return winningBidsArray; - }, - adUnits + } }); }); @@ -306,23 +293,5 @@ describe('#bidViewability', function() { // CONSTANTS.EVENTS.BID_VIEWABLE is NOT triggered expect(eventsEmitSpy.callCount).to.equal(0); }); - - it('should call the callBidBillableBidder function if the viewable bid is associated with an ad unit with deferBilling set to true', function() { - let moduleConfig = {}; - const deferredBillingAdUnit = { - 'code': '/harshad/Jan/2021/', - 'deferBilling': true, - 'bids': [ - { - 'bidder': 'pubmatic' - } - ] - }; - adUnits.push(deferredBillingAdUnit); - winningBidsArray.push(PBJS_WINNING_BID); - bidViewability.impressionViewableHandler(moduleConfig, GPT_SLOT, null); - expect(callBidBillableBidderSpy.callCount).to.equal(1); - sinon.assert.calledWith(callBidBillableBidderSpy, PBJS_WINNING_BID); - }); }); }); diff --git a/test/spec/modules/bizzclickBidAdapter_spec.js b/test/spec/modules/bizzclickBidAdapter_spec.js index f8e66caf657..f80051b0a50 100644 --- a/test/spec/modules/bizzclickBidAdapter_spec.js +++ b/test/spec/modules/bizzclickBidAdapter_spec.js @@ -1,102 +1,6 @@ import { expect } from 'chai'; -import { spec } from 'modules/bizzclickBidAdapter'; -import 'modules/priceFloors.js'; -import { newBidder } from 'src/adapters/bidderFactory'; -import { config } from '../../../src/config.js'; -import { syncAddFPDToBidderRequest } from '../../helpers/fpd.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/consentManagement.js'; -import 'modules/consentManagementUsp.js'; -import 'modules/schain.js'; - -const SIMPLE_BID_REQUEST = { - bidder: 'bizzclick', - params: { - accountId: 'testAccountId', - sourceId: 'testSourceId', - host: 'USE', - }, - mediaTypes: { - banner: { - sizes: [ - [320, 250], - [300, 600], - ], - }, - }, - adUnitCode: 'div-gpt-ad-1499748733608-0', - transactionId: 'f183e871-fbed-45f0-a427-c8a63c4c01eb', - bidId: '33e9500b21129f', - bidderRequestId: '2772c1e566670b', - auctionId: '192721e36a0239', - sizes: [[300, 250], [160, 600]], - gdprConsent: { - apiVersion: 2, - consentString: 'CONSENT', - vendorData: { purpose: { consents: { 1: true } } }, - gdprApplies: true, - addtlConsent: '1~1.35.41.101', - }, -} - -const BANNER_BID_REQUEST = { - bidder: 'bizzclick', - params: { - accountId: 'testAccountId', - sourceId: 'testSourceId', - host: 'USE', - }, - mediaTypes: { - banner: { - sizes: [ - [300, 250], - [300, 600], - ], - }, - }, - adUnitCode: '/adunit-code/test-path', - bidId: 'test-bid-id-1', - bidderRequestId: 'test-bid-request-1', - auctionId: 'test-auction-1', - transactionId: 'test-transactionId-1', - code: 'banner_example', - timeout: 1000, -} - -const VIDEO_BID_REQUEST = { - placementCode: '/DfpAccount1/slotVideo', - bidId: 'test-bid-id-2', - mediaTypes: { - video: { - playerSize: [400, 300], - w: 400, - h: 300, - minduration: 5, - maxduration: 10, - startdelay: 0, - skip: 1, - minbitrate: 200, - protocols: [1, 2, 4] - } - }, - bidder: 'bizzclick', - params: { - accountId: '123', - sourceId: '123', - host: 'USE', - }, - adUnitCode: '/adunit-code/test-path', - bidderRequestId: 'test-bid-request-1', - auctionId: 'test-auction-1', - transactionId: 'test-transactionId-1', - timeout: 1000, -} +import { spec } from 'modules/bizzclickBidAdapter.js'; +import {config} from 'src/config.js'; const NATIVE_BID_REQUEST = { code: 'native_example', @@ -130,179 +34,386 @@ const NATIVE_BID_REQUEST = { }, bidder: 'bizzclick', params: { - accountId: 'testAccountId', - sourceId: 'testSourceId', - host: 'USE', + placementId: 'hash', + accountId: 'accountId' + }, + timeout: 1000 + +}; + +const BANNER_BID_REQUEST = { + code: 'banner_example', + mediaTypes: { + banner: { + sizes: [[300, 250], [300, 600]] + } + }, + schain: { + ver: '1.0', + complete: 1, + nodes: [ + { + asi: 'example.com', + sid: '164', + hp: 1 + } + ] + }, + bidder: 'bizzclick', + params: { + placementId: 'hash', + accountId: 'accountId' }, - adUnitCode: '/adunit-code/test-path', - bidId: 'test-bid-id-1', - bidderRequestId: 'test-bid-request-1', - auctionId: 'test-auction-1', - transactionId: 'test-transactionId-1', timeout: 1000, + gdprConsent: { + consentString: 'BOEFEAyOEFEAyAHABDENAI4AAAB9vABAASA', + gdprApplies: 1, + }, uspConsent: 'uspConsent' -}; +} -const bidderRequest = { +const bidRequest = { refererInfo: { - page: 'https://publisher.com/home', - ref: 'https://referrer' + referer: 'test.com' } -}; +} + +const VIDEO_BID_REQUEST = { + code: 'video1', + sizes: [640, 480], + mediaTypes: { video: { + minduration: 0, + maxduration: 999, + boxingallowed: 1, + skip: 0, + mimes: [ + 'application/javascript', + 'video/mp4' + ], + w: 1920, + h: 1080, + protocols: [ + 2 + ], + linearity: 1, + api: [ + 1, + 2 + ] + } + }, + + bidder: 'bizzclick', + params: { + placementId: 'hash', + accountId: 'accountId' + }, + timeout: 1000 -const gdprConsent = { - apiVersion: 2, - consentString: 'CONSENT', - vendorData: { purpose: { consents: { 1: true } } }, - gdprApplies: true, - addtlConsent: '1~1.35.41.101', } -describe('bizzclickAdapter', 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'); - }); - }); +const BANNER_BID_RESPONSE = { + id: 'request_id', + bidid: 'request_imp_id', + seatbid: [{ + bid: [{ + id: 'bid_id', + impid: 'request_imp_id', + price: 5, + adomain: ['example.com'], + adm: 'admcode', + crid: 'crid', + ext: { + mediaType: 'banner' + } + }], + }], +}; - describe('with user privacy regulations', function () { - it('should send the Coppa "required" flag set to "1" in the request', function () { +const VIDEO_BID_RESPONSE = { + id: 'request_id', + bidid: 'request_imp_id', + seatbid: [{ + bid: [{ + id: 'bid_id', + impid: 'request_imp_id', + price: 5, + adomain: ['example.com'], + adm: 'admcode', + crid: 'crid', + ext: { + mediaType: 'video', + vastUrl: 'http://example.vast', + } + }], + }], +}; + +let imgData = { + url: `https://example.com/image`, + w: 1200, + h: 627 +}; + +const NATIVE_BID_RESPONSE = { + id: 'request_id', + bidid: 'request_imp_id', + seatbid: [{ + bid: [{ + id: 'bid_id', + impid: 'request_imp_id', + price: 5, + adomain: ['example.com'], + adm: { native: + { + assets: [ + {id: 0, title: 'dummyText'}, + {id: 3, image: imgData}, + { + id: 5, + data: {value: 'organization.name'} + } + ], + link: {url: 'example.com'}, + imptrackers: ['tracker1.com', 'tracker2.com', 'tracker3.com'], + jstracker: 'tracker1.com' + } + }, + crid: 'crid', + ext: { + mediaType: 'native' + } + }], + }], +}; + +describe('BizzclickAdapter', function() { + describe('with COPPA', function() { + beforeEach(function() { sinon.stub(config, 'getConfig') .withArgs('coppa') .returns(true); - const serverRequest = spec.buildRequests([SIMPLE_BID_REQUEST], syncAddFPDToBidderRequest(bidderRequest)); - expect(serverRequest.data.regs.coppa).to.equal(1); + }); + afterEach(function() { config.getConfig.restore(); }); - it('should send the GDPR Consent data in the request', function () { - const serverRequest = spec.buildRequests([SIMPLE_BID_REQUEST], syncAddFPDToBidderRequest({ ...bidderRequest, gdprConsent })); - expect(serverRequest.data.regs.ext.gdpr).to.exist.and.to.equal(1); - expect(serverRequest.data.user.ext.consent).to.equal('CONSENT'); + it('should send the Coppa "required" flag set to "1" in the request', function () { + let serverRequest = spec.buildRequests([BANNER_BID_REQUEST]); + expect(serverRequest.data[0].regs.coppa).to.equal(1); }); + }); - it('should send the CCPA data in the request', function () { - const serverRequest = spec.buildRequests([SIMPLE_BID_REQUEST], syncAddFPDToBidderRequest({...bidderRequest, ...{ uspConsent: '1YYY' }})); - expect(serverRequest.data.regs.ext.us_privacy).to.equal('1YYY'); + describe('isBidRequestValid', function() { + it('should return true when required params found', function () { + expect(spec.isBidRequestValid(NATIVE_BID_REQUEST)).to.equal(true); + }); + + it('should return false when required params are not passed', function () { + let bid = Object.assign({}, NATIVE_BID_REQUEST); + delete bid.params; + bid.params = { + 'IncorrectParam': 0 + }; + expect(spec.isBidRequestValid(bid)).to.equal(false); }); }); - describe('isBidRequestValid', function () { - it('should return true when required params found', function () { - expect(spec.isBidRequestValid(BANNER_BID_REQUEST)).to.equal(true); + describe('build Native Request', function () { + const request = spec.buildRequests([NATIVE_BID_REQUEST], bidRequest); + + it('Creates a ServerRequest object with method, URL and data', function () { + expect(request).to.exist; + expect(request.method).to.exist; + expect(request.url).to.exist; + expect(request.data).to.exist; }); - it('should return false when accountID/sourceId is missing', function () { - let localbid = Object.assign({}, BANNER_BID_REQUEST); - delete localbid.params.accountId; - delete localbid.params.sourceId; - expect(spec.isBidRequestValid(BANNER_BID_REQUEST)).to.equal(false); + it('sends bid request to our endpoint via POST', function () { + expect(request.method).to.equal('POST'); + }); + + it('Returns valid URL', function () { + expect(request.url).to.equal('https://us-e-node1.bizzclick.com/bid?rtb_seat_id=prebidjs&secret_key=accountId'); + }); + + it('Returns empty data if no valid requests are passed', function () { + let serverRequest = spec.buildRequests([]); + expect(serverRequest).to.be.an('array').that.is.empty; }); }); - describe('build request', function () { - it('should return an empty array when no bid requests', function () { - const bidRequest = spec.buildRequests([], syncAddFPDToBidderRequest(bidderRequest)); - expect(bidRequest).to.be.an('array'); - expect(bidRequest.length).to.equal(0); + describe('build Banner Request', function () { + const request = spec.buildRequests([BANNER_BID_REQUEST]); + + it('Creates a ServerRequest object with method, URL and data', function () { + expect(request).to.exist; + expect(request.method).to.exist; + expect(request.url).to.exist; + expect(request.data).to.exist; }); - it('should return a valid bid request object', function () { - const request = spec.buildRequests([SIMPLE_BID_REQUEST], syncAddFPDToBidderRequest(bidderRequest)); - expect(request).to.not.equal('array'); - expect(request.data).to.be.an('object'); + it('sends bid request to our endpoint via POST', function () { expect(request.method).to.equal('POST'); - expect(request.url).to.not.equal(''); - expect(request.url).to.not.equal(undefined); - expect(request.url).to.not.equal(null); - - expect(request.data.site).to.have.property('page'); - expect(request.data.site).to.have.property('domain'); - expect(request.data).to.have.property('id'); - expect(request.data).to.have.property('imp'); - expect(request.data).to.have.property('device'); }); - it('should return a valid bid BANNER request object', function () { - const request = spec.buildRequests([BANNER_BID_REQUEST], syncAddFPDToBidderRequest(bidderRequest)); - expect(request.data.imp[0].banner).to.exist; - expect(request.data.imp[0].banner.format[0].w).to.be.an('number'); - expect(request.data.imp[0].banner.format[0].h).to.be.an('number'); + it('check consent and ccpa string is set properly', function() { + expect(request.data[0].regs.ext.gdpr).to.equal(1); + expect(request.data[0].user.ext.consent).to.equal(BANNER_BID_REQUEST.gdprConsent.consentString); + expect(request.data[0].regs.ext.us_privacy).to.equal(BANNER_BID_REQUEST.uspConsent); + }) + + it('check schain is set properly', function() { + expect(request.data[0].source.ext.schain.complete).to.equal(1); + expect(request.data[0].source.ext.schain.ver).to.equal('1.0'); + }) + + it('Returns valid URL', function () { + expect(request.url).to.equal('https://us-e-node1.bizzclick.com/bid?rtb_seat_id=prebidjs&secret_key=accountId'); }); + }); - if (FEATURES.VIDEO) { - it('should return a valid bid VIDEO request object', function () { - const request = spec.buildRequests([VIDEO_BID_REQUEST], syncAddFPDToBidderRequest(bidderRequest)); - expect(request.data.imp[0].video).to.exist; - expect(request.data.imp[0].video.w).to.be.an('number'); - expect(request.data.imp[0].video.h).to.be.an('number'); - }); - } + describe('build Video Request', function () { + const request = spec.buildRequests([VIDEO_BID_REQUEST]); - it('should return a valid bid NATIVE request object', function () { - const request = spec.buildRequests([NATIVE_BID_REQUEST], syncAddFPDToBidderRequest(bidderRequest)); - expect(request.data.imp[0]).to.be.an('object'); + it('Creates a ServerRequest object with method, URL and data', function () { + expect(request).to.exist; + expect(request.method).to.exist; + expect(request.url).to.exist; + expect(request.data).to.exist; }); - }) - describe('interpretResponse', function () { - let bidRequests, bidderRequest; - beforeEach(function () { - bidRequests = [{ - 'bidId': '28ffdk2B952532', - 'bidder': 'bizzclick', - 'userId': { - 'freepassId': { - 'userIp': '172.21.0.1', - 'userId': '123', - 'commonId': 'commonIdValue' - } - }, - 'adUnitCode': 'adunit-code', - 'params': { - 'publisherId': 'publisherIdValue' - } - }]; - bidderRequest = {}; + it('sends bid request to our endpoint via POST', function () { + expect(request.method).to.equal('POST'); }); - it('Empty response must return empty array', function () { + it('Returns valid URL', function () { + expect(request.url).to.equal('https://us-e-node1.bizzclick.com/bid?rtb_seat_id=prebidjs&secret_key=accountId'); + }); + }); + + describe('interpretResponse', function () { + it('Empty response must return empty array', function() { const emptyResponse = null; - let response = spec.interpretResponse(emptyResponse, BANNER_BID_REQUEST); + let response = spec.interpretResponse(emptyResponse); expect(response).to.be.an('array').that.is.empty; }) it('Should interpret banner response', function () { - const serverResponse = { - body: { - 'cur': 'USD', - 'seatbid': [{ - 'bid': [{ - 'impid': '28ffdk2B952532', - 'price': 97, - 'adm': '', - 'w': 300, - 'h': 250, - 'crid': 'creative0' - }] - }] - } - }; - it('should interpret server response', function () { - const bidRequest = spec.buildRequests(bidRequests, syncAddFPDToBidderRequest(bidderRequest)); - const bids = spec.interpretResponse(serverResponse, bidRequest); - expect(bids).to.be.an('array'); - const bid = bids[0]; - expect(bid).to.be.an('object'); - expect(bid.currency).to.equal('USD'); - expect(bid.cpm).to.equal(97); - expect(bid.ad).to.equal(ad) - expect(bid.width).to.equal(300); - expect(bid.height).to.equal(250); - expect(bid.creativeId).to.equal('creative0'); - }); - }) + const bannerResponse = { + body: [BANNER_BID_RESPONSE] + } + + const expectedBidResponse = { + requestId: BANNER_BID_RESPONSE.id, + cpm: BANNER_BID_RESPONSE.seatbid[0].bid[0].price, + width: BANNER_BID_RESPONSE.seatbid[0].bid[0].w, + height: BANNER_BID_RESPONSE.seatbid[0].bid[0].h, + ttl: BANNER_BID_RESPONSE.ttl || 1200, + currency: BANNER_BID_RESPONSE.cur || 'USD', + netRevenue: true, + creativeId: BANNER_BID_RESPONSE.seatbid[0].bid[0].crid, + dealId: BANNER_BID_RESPONSE.seatbid[0].bid[0].dealid, + + meta: {advertiserDomains: BANNER_BID_RESPONSE.seatbid[0].bid[0].adomain}, + mediaType: 'banner', + ad: BANNER_BID_RESPONSE.seatbid[0].bid[0].adm + } + + let bannerResponses = spec.interpretResponse(bannerResponse); + + expect(bannerResponses).to.be.an('array').that.is.not.empty; + let dataItem = bannerResponses[0]; + expect(dataItem).to.have.all.keys('requestId', 'cpm', 'width', 'height', 'ad', 'ttl', 'creativeId', + 'netRevenue', 'currency', 'dealId', 'meta', 'mediaType'); + expect(dataItem.requestId).to.equal(expectedBidResponse.requestId); + expect(dataItem.cpm).to.equal(expectedBidResponse.cpm); + expect(dataItem.ad).to.equal(expectedBidResponse.ad); + expect(dataItem.ttl).to.equal(expectedBidResponse.ttl); + expect(dataItem.meta.advertiserDomains).to.equal(expectedBidResponse.meta.advertiserDomains); + expect(dataItem.creativeId).to.equal(expectedBidResponse.creativeId); + expect(dataItem.netRevenue).to.be.true; + expect(dataItem.currency).to.equal(expectedBidResponse.currency); + expect(dataItem.width).to.equal(expectedBidResponse.width); + expect(dataItem.height).to.equal(expectedBidResponse.height); + }); + + it('Should interpret video response', function () { + const videoResponse = { + body: [VIDEO_BID_RESPONSE] + } + + const expectedBidResponse = { + requestId: VIDEO_BID_RESPONSE.id, + cpm: VIDEO_BID_RESPONSE.seatbid[0].bid[0].price, + width: VIDEO_BID_RESPONSE.seatbid[0].bid[0].w, + height: VIDEO_BID_RESPONSE.seatbid[0].bid[0].h, + ttl: VIDEO_BID_RESPONSE.ttl || 1200, + currency: VIDEO_BID_RESPONSE.cur || 'USD', + netRevenue: true, + creativeId: VIDEO_BID_RESPONSE.seatbid[0].bid[0].crid, + dealId: VIDEO_BID_RESPONSE.seatbid[0].bid[0].dealid, + mediaType: 'video', + vastXml: VIDEO_BID_RESPONSE.seatbid[0].bid[0].adm, + meta: {advertiserDomains: VIDEO_BID_RESPONSE.seatbid[0].bid[0].adomain}, + vastUrl: VIDEO_BID_RESPONSE.seatbid[0].bid[0].ext.vastUrl + } + + let videoResponses = spec.interpretResponse(videoResponse); + + expect(videoResponses).to.be.an('array').that.is.not.empty; + let dataItem = videoResponses[0]; + expect(dataItem).to.have.all.keys('requestId', 'cpm', 'width', 'height', 'vastXml', 'vastUrl', 'ttl', 'creativeId', + 'netRevenue', 'currency', 'dealId', 'meta', 'mediaType'); + expect(dataItem.requestId).to.equal(expectedBidResponse.requestId); + expect(dataItem.cpm).to.equal(expectedBidResponse.cpm); + expect(dataItem.vastXml).to.equal(expectedBidResponse.vastXml) + expect(dataItem.ttl).to.equal(expectedBidResponse.ttl); + expect(dataItem.creativeId).to.equal(expectedBidResponse.creativeId); + expect(dataItem.meta.advertiserDomains).to.equal(expectedBidResponse.meta.advertiserDomains); + expect(dataItem.netRevenue).to.be.true; + expect(dataItem.currency).to.equal(expectedBidResponse.currency); + expect(dataItem.width).to.equal(expectedBidResponse.width); + expect(dataItem.height).to.equal(expectedBidResponse.height); + }); + + it('Should interpret native response', function () { + const nativeResponse = { + body: [NATIVE_BID_RESPONSE] + } + + const expectedBidResponse = { + requestId: NATIVE_BID_RESPONSE.id, + cpm: NATIVE_BID_RESPONSE.seatbid[0].bid[0].price, + width: NATIVE_BID_RESPONSE.seatbid[0].bid[0].w, + height: NATIVE_BID_RESPONSE.seatbid[0].bid[0].h, + ttl: NATIVE_BID_RESPONSE.ttl || 1200, + currency: NATIVE_BID_RESPONSE.cur || 'USD', + netRevenue: true, + creativeId: NATIVE_BID_RESPONSE.seatbid[0].bid[0].crid, + dealId: NATIVE_BID_RESPONSE.seatbid[0].bid[0].dealid, + mediaType: 'native', + meta: {advertiserDomains: NATIVE_BID_RESPONSE.seatbid[0].bid[0].adomain}, + native: {clickUrl: NATIVE_BID_RESPONSE.seatbid[0].bid[0].adm.native.link.url} + } + + let nativeResponses = spec.interpretResponse(nativeResponse); + + expect(nativeResponses).to.be.an('array').that.is.not.empty; + let dataItem = nativeResponses[0]; + expect(dataItem).to.have.all.keys('requestId', 'cpm', 'width', 'height', 'native', 'ttl', 'creativeId', + 'netRevenue', 'currency', 'dealId', 'mediaType', 'meta'); + expect(dataItem.requestId).to.equal(expectedBidResponse.requestId); + expect(dataItem.cpm).to.equal(expectedBidResponse.cpm); + expect(dataItem.meta.advertiserDomains).to.equal(expectedBidResponse.meta.advertiserDomains); + expect(dataItem.native.clickUrl).to.equal(expectedBidResponse.native.clickUrl) + expect(dataItem.ttl).to.equal(expectedBidResponse.ttl); + expect(dataItem.creativeId).to.equal(expectedBidResponse.creativeId); + expect(dataItem.netRevenue).to.be.true; + expect(dataItem.currency).to.equal(expectedBidResponse.currency); + expect(dataItem.width).to.equal(expectedBidResponse.width); + expect(dataItem.height).to.equal(expectedBidResponse.height); + }); }); -}); +}) diff --git a/test/spec/modules/bliinkBidAdapter_spec.js b/test/spec/modules/bliinkBidAdapter_spec.js index 3db97a17d88..8e96bd76940 100644 --- a/test/spec/modules/bliinkBidAdapter_spec.js +++ b/test/spec/modules/bliinkBidAdapter_spec.js @@ -1,16 +1,5 @@ -import { expect } from 'chai'; -import { - spec, - buildBid, - BLIINK_ENDPOINT_ENGINE, - getMetaList, - BLIINK_ENDPOINT_COOKIE_SYNC_IFRAME, - getEffectiveConnectionType, - getUserIds, - getDomLoadingDuration, - GVL_ID, -} from 'modules/bliinkBidAdapter.js'; -import { config } from 'src/config.js'; +import { expect } from 'chai' +import { spec, buildBid, BLIINK_ENDPOINT_ENGINE, getMetaList, BLIINK_ENDPOINT_COOKIE_SYNC_IFRAME } from 'modules/bliinkBidAdapter.js' /** * @description Mockup bidRequest @@ -31,9 +20,6 @@ import { config } from 'src/config.js'; * crumbs: {pubcid: string}, * ortb2Imp: {ext: {data: {pbadslot: string}}}}} */ - -const connectionType = getEffectiveConnectionType(); -const domLoadingDuration = getDomLoadingDuration().toString(); const getConfigBid = (placement) => { return { adUnitCode: '/19968336/test', @@ -45,33 +31,31 @@ const getConfigBid = (placement) => { bidderRequestsCount: 1, bidderWinsCount: 0, crumbs: { - pubcid: '55ffadc5-051f-428d-8ecc-dc585e0bde0d', + pubcid: '55ffadc5-051f-428d-8ecc-dc585e0bde0d' }, sizes: [[300, 250]], mediaTypes: { banner: { - sizes: [[300, 250]], - }, + sizes: [ + [300, 250] + ] + } }, ortb2Imp: { ext: { data: { - pbadslot: '/19968336/test', - }, - }, + pbadslot: '/19968336/test' + } + } }, - domLoadingDuration, - ect: connectionType, params: { placement: placement, - tagId: '14f30eca-85d2-11e8-9eed-0242ac120007', - videoUrl: 'https://www.example.com/advideo.mp4', - imageUrl: 'https://www.example.com/adimage.jpg', + tagId: '14f30eca-85d2-11e8-9eed-0242ac120007' }, src: 'client', - transactionId: 'cc6678c4-9746-4082-b9e2-d8065d078ebf', - }; -}; + transactionId: 'cc6678c4-9746-4082-b9e2-d8065d078ebf' + } +} const getConfigBannerBid = () => { return { creative: { @@ -92,13 +76,14 @@ const getConfigBannerBid = () => { transaction_id: '2def0c5b2a7f6e', }, currency: 'EUR', - }; -}; + } +} const getConfigVideoBid = () => { return { creative: { video: { - content: '', + content: + '', height: 250, width: 300, }, @@ -114,8 +99,8 @@ const getConfigVideoBid = () => { transaction_id: '2def0c5b2a7f6e', }, currency: 'EUR', - }; -}; + } +} /** * @description Mockup response from engine.bliink.io/xxxx @@ -134,7 +119,7 @@ const getConfigVideoBid = () => { * } * } * } - * } +* } */ const getConfigCreative = () => { return { @@ -147,8 +132,8 @@ const getConfigCreative = () => { height: 250, ttl: 300, netRevenue: true, - }; -}; + } +} const getConfigCreativeVideo = (isNoVast) => { return { @@ -162,8 +147,8 @@ const getConfigCreativeVideo = (isNoVast) => { height: 250, ttl: 300, netRevenue: true, - }; -}; + } +} /** * @description Mockup BuildRequest function @@ -181,19 +166,19 @@ const getConfigBuildRequest = (placement) => { reachedTop: true, page: 'http://localhost:9999/integrationExamples/gpt/bliink-adapter.html?pbjs_debug=true', }, - }; + } if (!placement) { - return buildRequest; + return buildRequest } return Object.assign(buildRequest, { params: { bids: [getConfigBid(placement)], - placement: placement, + placement: placement }, - }); -}; + }) +} /** * @description Mockup response from API @@ -204,8 +189,8 @@ const getConfigInterpretResponse = (noAd = false) => { if (noAd) { return { message: 'invalid tag', - mode: 'no-ad', - }; + mode: 'no-ad' + } } return { @@ -213,12 +198,11 @@ const getConfigInterpretResponse = (noAd = false) => { ...getConfigCreative(), mode: 'ad', transactionId: '2def0c5b2a7f6e', - token: - 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MjgxNzA4MzEsImlhdCI6MTYyNzU2NjAzMSwiaXNzIjoiYmxpaW5rIiwiZGF0YSI6eyJ0eXBlIjoiYWQtc2VydmVyIiwidHJhbnNhY3Rpb25JZCI6IjM1YmU1NDNjLTNkZTQtNGQ1Yy04N2NjLWIzYzEyOGZiYzU0MCIsIm5ldHdvcmtJZCI6MjEsInNpdGVJZCI6NTksInRhZ0lkIjo1OSwiY29va2llSWQiOiJjNGU4MWVhOS1jMjhmLTQwZDItODY1ZC1hNjQzZjE1OTcyZjUiLCJldmVudElkIjozLCJ0YXJnZXRpbmciOnsicGxhdGZvcm0iOiJXZWJzaXRlIiwiaXAiOiI3OC4xMjIuNzUuNzIiLCJ0aW1lIjoxNjI3NTY2MDMxLCJsb2NhdGlvbiI6eyJsYXRpdHVkZSI6NDguOTczOSwibG9uZ2l0dWRlIjozLjMxMTMsInJlZ2lvbiI6IkhERiIsImNvdW50cnkiOiJGUiIsImNpdHkiOiJTYXVsY2hlcnkiLCJ6aXBDb2RlIjoiMDIzMTAiLCJkZXBhcnRtZW50IjoiMDIifSwiY2l0eSI6IlNhdWxjaGVyeSIsImNvdW50cnkiOiJGUiIsImRldmljZU9zIjoibWFjT1MiLCJkZXZpY2VQbGF0Zm9ybSI6IldlYnNpdGUiLCJyYXdVc2VyQWdlbnQiOiJNb3ppbGxhLzUuMCAoTWFjaW50b3NoOyBJbnRlbCBNYWMgT1MgWCAxMF8xNV83KSBBcHBsZVdlYktpdC81MzcuMzYgKEtIVE1MLCBsaWtlIEdlY2tvKSBDaHJvbWUvOTEuMC40NDcyLjEyNCBTYWZhcmkvNTM3LjM2In0sImdkcHIiOnsiaGFzQ29uc2VudCI6dHJ1ZX0sIndpbiI6ZmFsc2UsImFkSWQiOjU2NDgsImFkdmVydGlzZXJJZCI6MSwiY2FtcGFpZ25JZCI6MSwiY3JlYXRpdmVJZCI6MjgyNSwiZXJyb3IiOmZhbHNlfX0.-UefQH4G0k-RJGemBYffs-KL7EEwma2Wuwgk2xnpij8', + token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MjgxNzA4MzEsImlhdCI6MTYyNzU2NjAzMSwiaXNzIjoiYmxpaW5rIiwiZGF0YSI6eyJ0eXBlIjoiYWQtc2VydmVyIiwidHJhbnNhY3Rpb25JZCI6IjM1YmU1NDNjLTNkZTQtNGQ1Yy04N2NjLWIzYzEyOGZiYzU0MCIsIm5ldHdvcmtJZCI6MjEsInNpdGVJZCI6NTksInRhZ0lkIjo1OSwiY29va2llSWQiOiJjNGU4MWVhOS1jMjhmLTQwZDItODY1ZC1hNjQzZjE1OTcyZjUiLCJldmVudElkIjozLCJ0YXJnZXRpbmciOnsicGxhdGZvcm0iOiJXZWJzaXRlIiwiaXAiOiI3OC4xMjIuNzUuNzIiLCJ0aW1lIjoxNjI3NTY2MDMxLCJsb2NhdGlvbiI6eyJsYXRpdHVkZSI6NDguOTczOSwibG9uZ2l0dWRlIjozLjMxMTMsInJlZ2lvbiI6IkhERiIsImNvdW50cnkiOiJGUiIsImNpdHkiOiJTYXVsY2hlcnkiLCJ6aXBDb2RlIjoiMDIzMTAiLCJkZXBhcnRtZW50IjoiMDIifSwiY2l0eSI6IlNhdWxjaGVyeSIsImNvdW50cnkiOiJGUiIsImRldmljZU9zIjoibWFjT1MiLCJkZXZpY2VQbGF0Zm9ybSI6IldlYnNpdGUiLCJyYXdVc2VyQWdlbnQiOiJNb3ppbGxhLzUuMCAoTWFjaW50b3NoOyBJbnRlbCBNYWMgT1MgWCAxMF8xNV83KSBBcHBsZVdlYktpdC81MzcuMzYgKEtIVE1MLCBsaWtlIEdlY2tvKSBDaHJvbWUvOTEuMC40NDcyLjEyNCBTYWZhcmkvNTM3LjM2In0sImdkcHIiOnsiaGFzQ29uc2VudCI6dHJ1ZX0sIndpbiI6ZmFsc2UsImFkSWQiOjU2NDgsImFkdmVydGlzZXJJZCI6MSwiY2FtcGFpZ25JZCI6MSwiY3JlYXRpdmVJZCI6MjgyNSwiZXJyb3IiOmZhbHNlfX0.-UefQH4G0k-RJGemBYffs-KL7EEwma2Wuwgk2xnpij8' }, headers: {}, - }; -}; + } +} /** * @description Mockup response from API for RTB creative @@ -229,8 +213,8 @@ const getConfigInterpretResponseRTB = (noAd = false, isInvalidVast = false) => { if (noAd) { return { message: 'invalid tag', - mode: 'no-ad', - }; + mode: 'no-ad' + } } const validVast = ` @@ -245,43 +229,41 @@ const getConfigInterpretResponseRTB = (noAd = false, isInvalidVast = false) => { - `; + ` const invalidVast = ` - `; + ` return { - body: { - bids: [ - { - creative: { - video: { - content: isInvalidVast ? invalidVast : validVast, - height: 250, - width: 300, - }, - media_type: 'video', - creativeId: 0, - }, - price: 0, - id: '8121', - token: 'token', - mode: 'rtb', - extras: { - deal_id: '34567ertyaza', - transaction_id: '2def0c5b2a7f6e', + body: { bids: [ + { + 'creative': { + 'video': { + 'content': isInvalidVast ? invalidVast : validVast, + 'height': 250, + 'width': 300 }, - currency: 'EUR', + 'media_type': 'video', + 'creativeId': 0, }, - ], - userSyncs: [], - }, - }; -}; + 'price': 0, + 'id': '8121', + 'token': 'token', + 'mode': 'rtb', + 'extras': { + 'deal_id': '34567ertyaza', + 'transaction_id': '2def0c5b2a7f6e' + }, + 'currency': 'EUR' + } + ], + userSyncs: []} + } +} /** * @@ -297,9 +279,9 @@ const testsGetMetaList = [ { title: 'Should return empty array if there are no parameters', args: { - fn: getMetaList(), + fn: getMetaList() }, - want: [], + want: [] }, { title: 'Should return list of metas with name associated', @@ -331,63 +313,18 @@ const testsGetMetaList = [ key: 'property', value: `'article:${'test'}'`, }, - ], - }, -]; - -describe('BLIINK Adapter getMetaList', function () { - for (const test of testsGetMetaList) { - it(test.title, () => { - const res = test.args.fn; - expect(res).to.eql(test.want); - }); + ] } -}); -const GetUserIds = [ - { - title: 'Should return undefined if there are no parameters', - args: { - fn: getUserIds(), - }, - want: undefined, - }, - { - title: 'Should return eids if exists', - args: { - fn: getUserIds([{ userIdAsEids: [ - { - 'source': 'criteo.com', - 'uids': [ - { - 'id': 'testId', - 'atype': 1 - } - ] - } - ] }]), - }, - want: [ - { - 'source': 'criteo.com', - 'uids': [ - { - 'id': 'testId', - 'atype': 1 - } - ] - } - ], - }, -]; +] -describe('BLIINK Adapter getUserIds', function () { - for (const test of GetUserIds) { +describe('BLIINK Adapter getMetaList', function() { + for (const test of testsGetMetaList) { it(test.title, () => { - const res = test.args.fn; - expect(res).to.eql(test.want); - }); + const res = test.args.fn + expect(res).to.eql(test.want) + }) } -}); +}) /** * @description Array of tests used in describe function below @@ -412,142 +349,127 @@ const testsIsBidRequestValid = [ { title: 'isBidRequestValid format not valid', args: { - fn: spec.isBidRequestValid({}), + fn: spec.isBidRequestValid({}) }, want: false, }, { title: 'isBidRequestValid does not receive any bid', args: { - fn: spec.isBidRequestValid(), + fn: spec.isBidRequestValid() }, want: false, }, { title: 'isBidRequestValid Receive a valid bid', args: { - fn: spec.isBidRequestValid(getConfigBid('banner')), + fn: spec.isBidRequestValid(getConfigBid('banner')) }, want: true, - }, -]; + } +] -describe('BLIINK Adapter isBidRequestValid', function () { +describe('BLIINK Adapter isBidRequestValid', function() { for (const test of testsIsBidRequestValid) { it(test.title, () => { - const res = test.args.fn; - expect(res).to.eql(test.want); - }); + const res = test.args.fn + expect(res).to.eql(test.want) + }) } -}); +}) -const vastXml = - getConfigInterpretResponseRTB().body.bids[0].creative.video.content; +const vastXml = getConfigInterpretResponseRTB().body.bids[0].creative.video.content const testsInterpretResponse = [ { title: 'Should construct bid for video instream', args: { - fn: spec.interpretResponse(getConfigInterpretResponseRTB(false)), + fn: spec.interpretResponse(getConfigInterpretResponseRTB(false)) }, - want: [ - { - cpm: 0, - currency: 'EUR', - height: 250, - width: 300, - creativeId: '34567ertyaza', - mediaType: 'video', - netRevenue: true, - requestId: '2def0c5b2a7f6e', - ttl: 300, - vastXml, - vastUrl: - 'data:text/xml;charset=utf-8;base64,' + - btoa(vastXml.replace(/\\"/g, '"')), - }, - ], + want: [{ + cpm: 0, + currency: 'EUR', + height: 250, + width: 300, + creativeId: '34567ertyaza', + mediaType: 'video', + netRevenue: true, + requestId: '2def0c5b2a7f6e', + ttl: 300, + vastXml, + vastUrl: 'data:text/xml;charset=utf-8;base64,' + btoa(vastXml.replace(/\\"/g, '"')) + }] }, { title: 'ServerResponse with message: invalid tag, return empty array', args: { - fn: spec.interpretResponse(getConfigInterpretResponse(true)), + fn: spec.interpretResponse(getConfigInterpretResponse(true)) }, - want: [], + want: [] }, { title: 'ServerResponse with mediaType banner', args: { - fn: spec.interpretResponse({ body: { bids: [getConfigBannerBid()] } }), + fn: spec.interpretResponse({body: {bids: [getConfigBannerBid()]}}), }, - want: [ - { - ad: '', - cpm: 1, - creativeId: '34567erty', - currency: 'EUR', - height: 250, - mediaType: 'banner', - netRevenue: true, - requestId: '2def0c5b2a7f6e', - ttl: 300, - width: 300, - }, - ], + want: [{ + ad: '', + cpm: 1, + creativeId: '34567erty', + currency: 'EUR', + height: 250, + mediaType: 'banner', + netRevenue: true, + requestId: '2def0c5b2a7f6e', + ttl: 300, + width: 300 + }] }, { title: 'ServerResponse with unhandled mediaType, return empty array', args: { - fn: spec.interpretResponse({ - body: { - bids: [ - { - ...getConfigBannerBid(), - creative: { - unknown: { - adm: '', - height: 250, - width: 300, - }, - media_type: 'unknown', - creativeId: 125, - requestId: '2def0c5b2a7f6e', - }, - }, - ], - }, - }), + fn: spec.interpretResponse({body: {bids: [{...getConfigBannerBid(), + creative: { + unknown: { + adm: '', + height: 250, + width: 300, + }, + media_type: 'unknown', + creativeId: 125, + requestId: '2def0c5b2a7f6e', + }}]}}), }, - want: [], + want: [] }, -]; +] -describe('BLIINK Adapter interpretResponse', function () { +describe('BLIINK Adapter interpretResponse', function() { for (const test of testsInterpretResponse) { it(test.title, () => { - const res = test.args.fn; + const res = test.args.fn if (res) { - expect(res).to.eql(test.want); + expect(res).to.eql(test.want) } - }); + }) } -}); +}) /** * @description Array of tests used in describe function below * @type {[ * {args: * {fn: { - * cpm: number, - * netRevenue: boolean, - * ad, requestId, - * meta: {mediaType}, - * width: number, - * currency: string, - * ttl: number, - * creativeId: number, - * height: number + * cpm: number, + * netRevenue: boolean, + * ad, requestId, + * meta: {mediaType}, + * width: number, + * currency: string, + * ttl: number, + * creativeId: number, + * height: number * } * }, want, title: string}]} */ @@ -556,26 +478,21 @@ const testsBuildBid = [ { title: 'Should return null if no bid passed in parameters', args: { - fn: buildBid(), + fn: buildBid() }, - want: null, + want: null }, { title: 'Input data must respect the output model', args: { - fn: buildBid( - { id: 1, test: '123' }, - { id: 2, test: '345' }, - false, - false - ), + fn: buildBid({ id: 1, test: '123' }, { id: 2, test: '345' }, false, false) }, - want: null, + want: null }, { title: 'input data respect the output model for video', args: { - fn: buildBid(getConfigVideoBid('video'), getConfigCreativeVideo()), + fn: buildBid(getConfigVideoBid('video'), getConfigCreativeVideo()) }, want: { requestId: getConfigBid('video').bidId, @@ -587,31 +504,25 @@ const testsBuildBid = [ creativeId: getConfigVideoBid().extras.deal_id, netRevenue: true, vastXml: getConfigCreativeVideo().vastXml, - vastUrl: - 'data:text/xml;charset=utf-8;base64,' + - btoa(getConfigCreativeVideo().vastXml.replace(/\\"/g, '"')), + vastUrl: 'data:text/xml;charset=utf-8;base64,' + btoa(getConfigCreativeVideo().vastXml.replace(/\\"/g, '"')), ttl: 300, - }, + } }, { title: 'use default height width output model for video', args: { - fn: buildBid( - { - ...getConfigVideoBid('video'), - creative: { - video: { - content: '', - height: null, - width: null, - }, - media_type: 'video', - creativeId: getConfigVideoBid().extras.deal_id, - requestId: '2def0c5b2a7f6e', + fn: buildBid({...getConfigVideoBid('video'), + creative: { + video: { + content: + '', + height: null, + width: null, }, - }, - getConfigCreativeVideo() - ), + media_type: 'video', + creativeId: getConfigVideoBid().extras.deal_id, + requestId: '2def0c5b2a7f6e', + }}, getConfigCreativeVideo()) }, want: { requestId: getConfigBid('video').bidId, @@ -623,16 +534,14 @@ const testsBuildBid = [ creativeId: getConfigVideoBid().extras.deal_id, netRevenue: true, vastXml: getConfigCreativeVideo().vastXml, - vastUrl: - 'data:text/xml;charset=utf-8;base64,' + - btoa(getConfigCreativeVideo().vastXml.replace(/\\"/g, '"')), + vastUrl: 'data:text/xml;charset=utf-8;base64,' + btoa(getConfigCreativeVideo().vastXml.replace(/\\"/g, '"')), ttl: 300, - }, + } }, { title: 'input data respect the output model for banner', args: { - fn: buildBid(getConfigBannerBid()), + fn: buildBid(getConfigBannerBid()) }, want: { requestId: getConfigBid('banner').bidId, @@ -645,18 +554,18 @@ const testsBuildBid = [ ad: getConfigBannerBid().creative.banner.adm, ttl: 300, netRevenue: true, - }, - }, -]; + } + } +] -describe('BLIINK Adapter buildBid', function () { +describe('BLIINK Adapter buildBid', function() { for (const test of testsBuildBid) { it(test.title, () => { - const res = test.args.fn; - expect(res).to.eql(test.want); - }); + const res = test.args.fn + expect(res).to.eql(test.want) + }) } -}); +}) /** * @description Array of tests used in describe function below @@ -666,33 +575,28 @@ const testsBuildRequests = [ { title: 'Should not build request, no bidder request exist', args: { - fn: spec.buildRequests(), + fn: spec.buildRequests() }, - want: null, + want: null }, { title: 'Should build request if bidderRequest exist', args: { - fn: spec.buildRequests([], getConfigBuildRequest('banner')), + fn: spec.buildRequests([], getConfigBuildRequest('banner')) }, want: { method: 'POST', url: BLIINK_ENDPOINT_ENGINE, data: { - domLoadingDuration, - ect: connectionType, keywords: '', pageDescription: '', pageTitle: '', - pageUrl: - 'http://localhost:9999/integrationExamples/gpt/bliink-adapter.html', + pageUrl: 'http://localhost:9999/integrationExamples/gpt/bliink-adapter.html?pbjs_debug=true', tags: [ { transactionId: '2def0c5b2a7f6e', - refresh: window.bliinkBid['14f30eca-85d2-11e8-9eed-0242ac120007'] || undefined, id: '14f30eca-85d2-11e8-9eed-0242ac120007', - imageUrl: 'https://www.example.com/adimage.jpg', - videoUrl: 'https://www.example.com/advideo.mp4', + imageUrl: '', mediaTypes: ['banner'], sizes: [ { @@ -701,90 +605,35 @@ const testsBuildRequests = [ }, ], }, - ], - }, - }, + ] + } + } }, { title: 'Should build request width GDPR configuration', args: { - fn: spec.buildRequests( - [], - Object.assign(getConfigBuildRequest('banner'), { - gdprConsent: { - gdprApplies: true, - consentString: 'XXXX', - }, - }) - ), - }, - want: { - method: 'POST', - url: BLIINK_ENDPOINT_ENGINE, - data: { - domLoadingDuration, - ect: connectionType, - gdpr: true, - gdprConsent: 'XXXX', - pageDescription: '', - pageTitle: '', - keywords: '', - pageUrl: - 'http://localhost:9999/integrationExamples/gpt/bliink-adapter.html', - tags: [ - { - transactionId: '2def0c5b2a7f6e', - refresh: window.bliinkBid['14f30eca-85d2-11e8-9eed-0242ac120007'] || undefined, - id: '14f30eca-85d2-11e8-9eed-0242ac120007', - imageUrl: 'https://www.example.com/adimage.jpg', - videoUrl: 'https://www.example.com/advideo.mp4', - mediaTypes: ['banner'], - sizes: [ - { - h: 250, - w: 300, - }, - ], - }, - ], - }, - }, - }, - { - title: 'Should build request width uspConsent if exists', - args: { - fn: spec.buildRequests( - [], - Object.assign(getConfigBuildRequest('banner'), { - gdprConsent: { - gdprApplies: true, - consentString: 'XXXX', - }, - uspConsent: 'uspConsent', - }) - ), + fn: spec.buildRequests([], Object.assign(getConfigBuildRequest('banner'), { + gdprConsent: { + gdprApplies: true, + consentString: 'XXXX' + }, + })) }, want: { method: 'POST', url: BLIINK_ENDPOINT_ENGINE, data: { - domLoadingDuration, - ect: connectionType, gdpr: true, - uspConsent: 'uspConsent', gdprConsent: 'XXXX', pageDescription: '', pageTitle: '', keywords: '', - pageUrl: - 'http://localhost:9999/integrationExamples/gpt/bliink-adapter.html', + pageUrl: 'http://localhost:9999/integrationExamples/gpt/bliink-adapter.html?pbjs_debug=true', tags: [ { transactionId: '2def0c5b2a7f6e', - refresh: window.bliinkBid['14f30eca-85d2-11e8-9eed-0242ac120007'] || undefined, id: '14f30eca-85d2-11e8-9eed-0242ac120007', - imageUrl: 'https://www.example.com/adimage.jpg', - videoUrl: 'https://www.example.com/advideo.mp4', + imageUrl: '', mediaTypes: ['banner'], sizes: [ { @@ -793,68 +642,52 @@ const testsBuildRequests = [ }, ], }, - ], - }, - }, + ] + } + } }, { title: 'Should build request width schain if exists', args: { - fn: spec.buildRequests( - [ - { - schain: { - ver: '1.0', - complete: 1, - nodes: [ - { - asi: 'ssp.test', - sid: '00001', - hp: 1, - }, - ], - }, - }, - ], - Object.assign(getConfigBuildRequest('banner'), { - gdprConsent: { - gdprApplies: true, - consentString: 'XXXX', - }, - }) - ), + fn: spec.buildRequests([{schain: { + ver: '1.0', + complete: 1, + nodes: [{ + asi: 'ssp.test', + sid: '00001', + hp: 1 + }] + }}], Object.assign(getConfigBuildRequest('banner'), { + gdprConsent: { + gdprApplies: true, + consentString: 'XXXX' + }, + })) }, want: { method: 'POST', url: BLIINK_ENDPOINT_ENGINE, data: { - domLoadingDuration, - ect: connectionType, gdpr: true, gdprConsent: 'XXXX', pageDescription: '', pageTitle: '', keywords: '', - pageUrl: - 'http://localhost:9999/integrationExamples/gpt/bliink-adapter.html', + pageUrl: 'http://localhost:9999/integrationExamples/gpt/bliink-adapter.html?pbjs_debug=true', schain: { ver: '1.0', complete: 1, - nodes: [ - { - asi: 'ssp.test', - sid: '00001', - hp: 1, - }, - ], + nodes: [{ + asi: 'ssp.test', + sid: '00001', + hp: 1 + }] }, tags: [ { transactionId: '2def0c5b2a7f6e', - refresh: window.bliinkBid['14f30eca-85d2-11e8-9eed-0242ac120007'] || undefined, id: '14f30eca-85d2-11e8-9eed-0242ac120007', - imageUrl: 'https://www.example.com/adimage.jpg', - videoUrl: 'https://www.example.com/advideo.mp4', + imageUrl: '', mediaTypes: ['banner'], sizes: [ { @@ -863,313 +696,107 @@ const testsBuildRequests = [ }, ], }, - ], - }, - }, - }, - { - title: 'Should build request with eids if exists', - args: { - fn: spec.buildRequests( - [ - { - userIdAsEids: [ - { - 'source': 'criteo.com', - 'uids': [ - { - 'id': 'vG4RRF93V05LRlJUTVVOQTJJJTJGbG1rZWxEeDVvc0NXWE42TzJqU2hG', - 'atype': 1 - } - ] - }, - { - 'source': 'netid.de', - 'uids': [ - { - 'id': 'fH5A3n2O8_CZZyPoJVD-eabc6ECb7jhxCicsds7qSg', - 'atype': 1 - } - ] - } - ], - }, - ], - Object.assign(getConfigBuildRequest('banner'), { - gdprConsent: { - gdprApplies: true, - consentString: 'XXXX', - }, - }) - ), - }, - want: { - method: 'POST', - url: BLIINK_ENDPOINT_ENGINE, - data: { - domLoadingDuration, - ect: connectionType, - gdpr: true, - gdprConsent: 'XXXX', - pageDescription: '', - pageTitle: '', - keywords: '', - pageUrl: - 'http://localhost:9999/integrationExamples/gpt/bliink-adapter.html', - eids: [ - { - 'source': 'criteo.com', - 'uids': [ - { - 'id': 'vG4RRF93V05LRlJUTVVOQTJJJTJGbG1rZWxEeDVvc0NXWE42TzJqU2hG', - 'atype': 1 - } - ] - }, - { - 'source': 'netid.de', - 'uids': [ - { - 'id': 'fH5A3n2O8_CZZyPoJVD-eabc6ECb7jhxCicsds7qSg', - 'atype': 1 - } - ] - } - ], - tags: [ - { - transactionId: '2def0c5b2a7f6e', - refresh: window.bliinkBid['14f30eca-85d2-11e8-9eed-0242ac120007'] || undefined, - id: '14f30eca-85d2-11e8-9eed-0242ac120007', - imageUrl: 'https://www.example.com/adimage.jpg', - videoUrl: 'https://www.example.com/advideo.mp4', - mediaTypes: ['banner'], - sizes: [ - { - h: 250, - w: 300, - }, - ], - }, - ], - }, - }, - }, -]; + ] + } + } + } +] -describe('BLIINK Adapter buildRequests', function () { +describe('BLIINK Adapter buildRequests', function() { for (const test of testsBuildRequests) { it(test.title, () => { - const res = test.args.fn; - expect(res).to.eql(test.want); - test.args.after; - }); + const res = test.args.fn + expect(res).to.eql(test.want) + }) } -}); +}) const getSyncOptions = (pixelEnabled = true, iframeEnabled = false) => { return { pixelEnabled, - iframeEnabled, - }; -}; + iframeEnabled + } +} const getServerResponses = () => { return [ { - body: { - bids: [], - userSyncs: [ - { - type: 'script', - url: 'https://prg.smartadserver.com/ac?out=js&nwid=3392&siteid=305791&pgname=rg&fmtid=81127&tgt=[sas_target]&visit=m&tmstp=[timestamp]&clcturl=[countgo]', - }, - { - type: 'image', - url: 'https://sync.smartadserver.com/getuid?nwid=3392&consentString=XXX&url=https%3A%2F%2Fcookiesync.api.bliink.io%2Fcookiesync%3Fpartner%3Dsmart%26uid%3D%5Bsas_uid%5D', - }, - ], - }, - }, - ]; -}; + body: {bids: [], + userSyncs: [ { + type: 'script', + url: 'https://prg.smartadserver.com/ac?out=js&nwid=3392&siteid=305791&pgname=rg&fmtid=81127&tgt=[sas_target]&visit=m&tmstp=[timestamp]&clcturl=[countgo]' + }, + { + type: 'image', + url: 'https://sync.smartadserver.com/getuid?nwid=3392&consentString=XXX&url=https%3A%2F%2Fcookiesync.api.bliink.io%2Fcookiesync%3Fpartner%3Dsmart%26uid%3D%5Bsas_uid%5D' + }]}, + } + ] +} const getGdprConsent = () => { return { gdprApplies: 1, consentString: 'XXX', - apiVersion: 2, - }; -}; + apiVersion: 2 + } +} const testsGetUserSyncs = [ { title: 'Should not have gdprConsent exist', args: { - fn: spec.getUserSyncs( - getSyncOptions(), - getServerResponses(), - getGdprConsent() - ), + fn: spec.getUserSyncs(getSyncOptions(), getServerResponses(), getGdprConsent()) }, want: [ { type: 'script', - url: 'https://prg.smartadserver.com/ac?out=js&nwid=3392&siteid=305791&pgname=rg&fmtid=81127&tgt=[sas_target]&visit=m&tmstp=[timestamp]&clcturl=[countgo]', + url: 'https://prg.smartadserver.com/ac?out=js&nwid=3392&siteid=305791&pgname=rg&fmtid=81127&tgt=[sas_target]&visit=m&tmstp=[timestamp]&clcturl=[countgo]' }, { type: 'image', - url: 'https://sync.smartadserver.com/getuid?nwid=3392&consentString=XXX&url=https%3A%2F%2Fcookiesync.api.bliink.io%2Fcookiesync%3Fpartner%3Dsmart%26uid%3D%5Bsas_uid%5D', - }, - ], + url: 'https://sync.smartadserver.com/getuid?nwid=3392&consentString=XXX&url=https%3A%2F%2Fcookiesync.api.bliink.io%2Fcookiesync%3Fpartner%3Dsmart%26uid%3D%5Bsas_uid%5D' + } + ] }, { title: 'Should return iframe cookie sync if iframeEnabled', args: { - fn: spec.getUserSyncs( - getSyncOptions(true, true), - getServerResponses(), - getGdprConsent() - ), + fn: spec.getUserSyncs(getSyncOptions(true, true), getServerResponses(), getGdprConsent()) }, want: [ { type: 'iframe', - url: `${BLIINK_ENDPOINT_COOKIE_SYNC_IFRAME}?gdpr=${ - getGdprConsent().gdprApplies - }&coppa=0&gdprConsent=${getGdprConsent().consentString}&apiVersion=${ - getGdprConsent().apiVersion - }`, + url: `${BLIINK_ENDPOINT_COOKIE_SYNC_IFRAME}?gdpr=${getGdprConsent().gdprApplies}&coppa=0&gdprConsent=${getGdprConsent().consentString}&apiVersion=${getGdprConsent().apiVersion}` }, - ], + ] }, { title: 'ccpa', args: { - fn: spec.getUserSyncs( - getSyncOptions(true, true), - getServerResponses(), - getGdprConsent(), - 'ccpa-consent' - ), + fn: spec.getUserSyncs(getSyncOptions(true, true), getServerResponses(), getGdprConsent(), 'ccpa-consent') }, want: [ { type: 'iframe', - url: `${BLIINK_ENDPOINT_COOKIE_SYNC_IFRAME}?gdpr=${ - getGdprConsent().gdprApplies - }&coppa=0&uspConsent=ccpa-consent&gdprConsent=${ - getGdprConsent().consentString - }&apiVersion=${getGdprConsent().apiVersion}`, + url: `${BLIINK_ENDPOINT_COOKIE_SYNC_IFRAME}?gdpr=${getGdprConsent().gdprApplies}&coppa=0&uspConsent=ccpa-consent&gdprConsent=${getGdprConsent().consentString}&apiVersion=${getGdprConsent().apiVersion}` }, - ], + ] }, { title: 'Should output sync if no gdprConsent', args: { - fn: spec.getUserSyncs(getSyncOptions(), getServerResponses()), + fn: spec.getUserSyncs(getSyncOptions(), getServerResponses()) }, - want: getServerResponses()[0].body.userSyncs, - }, - { - title: 'Should output empty array if no pixelEnabled', - args: { - fn: spec.getUserSyncs({}, getServerResponses()), - }, - want: [], - }, -]; + want: getServerResponses()[0].body.userSyncs + } +] -describe('BLIINK Adapter getUserSyncs', function () { +describe('BLIINK Adapter getUserSyncs', function() { for (const test of testsGetUserSyncs) { it(test.title, () => { - const res = test.args.fn; - expect(res).to.eql(test.want); - }); + const res = test.args.fn + expect(res).to.eql(test.want) + }) } -}); - -describe('BLIINK Adapter keywords & coppa true', function () { - it('Should build request with keyword and coppa true if exist', () => { - const metaElement = document.createElement('meta'); - metaElement.name = 'keywords'; - metaElement.content = 'Bliink, Saber, Prebid'; - sinon.stub(config, 'getConfig').withArgs('coppa').returns(true); - - const querySelectorStub = sinon - .stub(document, 'querySelector') - .returns(metaElement); - expect( - spec.buildRequests( - [], - Object.assign(getConfigBuildRequest('banner'), { - gdprConsent: { - gdprApplies: true, - consentString: 'XXXX', - }, - }) - ) - ).to.eql({ - method: 'POST', - url: BLIINK_ENDPOINT_ENGINE, - data: { - domLoadingDuration, - ect: connectionType, - gdpr: true, - coppa: 1, - gdprConsent: 'XXXX', - pageDescription: 'Bliink, Saber, Prebid', - pageTitle: '', - keywords: 'Bliink,Saber,Prebid', - pageUrl: - 'http://localhost:9999/integrationExamples/gpt/bliink-adapter.html', - tags: [ - { - transactionId: '2def0c5b2a7f6e', - id: '14f30eca-85d2-11e8-9eed-0242ac120007', - imageUrl: 'https://www.example.com/adimage.jpg', - videoUrl: 'https://www.example.com/advideo.mp4', - mediaTypes: ['banner'], - refresh: window.bliinkBid['14f30eca-85d2-11e8-9eed-0242ac120007'] || undefined, - sizes: [ - { - h: 250, - w: 300, - }, - ], - }, - ], - }, - }); - querySelectorStub.restore(); - config.getConfig.restore(); - }); -}); - -describe('getEffectiveConnectionType', () => { - let navigatorStub; - - beforeEach(() => { - if ('connection' in navigator) { - navigatorStub = sinon.stub(navigator, 'connection').value({ - effectiveType: undefined, - }); - } - }); - - afterEach(() => { - if (navigatorStub) { - navigatorStub.restore(); - } - }); - if (navigatorStub) { - it('should return "unsupported" when effective connection type is undefined', () => { - const result = getEffectiveConnectionType(); - expect(result).to.equal('unsupported'); - }); - } -}); - -it('should expose gvlid', function () { - expect(spec.gvlid).to.equal(GVL_ID); -}); +}) diff --git a/test/spec/modules/boldwinBidAdapter_spec.js b/test/spec/modules/boldwinBidAdapter_spec.js index 9a7b16c0914..5b51183ea6d 100644 --- a/test/spec/modules/boldwinBidAdapter_spec.js +++ b/test/spec/modules/boldwinBidAdapter_spec.js @@ -19,8 +19,7 @@ describe('BoldwinBidAdapter', function () { const bidderRequest = { refererInfo: { referer: 'test.com' - }, - ortb2: {} + } }; describe('isBidRequestValid', function () { @@ -111,36 +110,6 @@ describe('BoldwinBidAdapter', function () { expect(data.placements).to.be.an('array').that.is.empty; }); }); - - describe('gpp consent', function () { - it('bidderRequest.gppConsent', () => { - bidderRequest.gppConsent = { - gppString: 'abc123', - applicableSections: [8] - }; - - let serverRequest = spec.buildRequests([bid], bidderRequest); - let 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.regs = bidderRequest.ortb2.regs || {}; - bidderRequest.ortb2.regs.gpp = 'abc123'; - bidderRequest.ortb2.regs.gpp_sid = [8]; - - let serverRequest = spec.buildRequests([bid], bidderRequest); - let 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 = { @@ -314,7 +283,7 @@ describe('BoldwinBidAdapter', function () { expect(userSync[0].type).to.exist; expect(userSync[0].url).to.exist; expect(userSync[0].type).to.be.equal('image'); - expect(userSync[0].url).to.be.equal('https://sync.videowalldirect.com'); + expect(userSync[0].url).to.be.equal('https://cs.videowalldirect.com'); }); }); }); diff --git a/test/spec/modules/brandmetricsRtdProvider_spec.js b/test/spec/modules/brandmetricsRtdProvider_spec.js index 72a2e4b029c..879ec7e1c7a 100644 --- a/test/spec/modules/brandmetricsRtdProvider_spec.js +++ b/test/spec/modules/brandmetricsRtdProvider_spec.js @@ -1,7 +1,5 @@ import * as brandmetricsRTD from '../../../modules/brandmetricsRtdProvider.js'; import {config} from 'src/config.js'; -import * as events from '../../../src/events'; -import * as sinon from 'sinon'; const VALID_CONFIG = { name: 'brandmetrics', @@ -67,8 +65,6 @@ const NO_USP_CONSENT = { usp: '1NYY' }; -const UNDEFINED_USER_CONSENT = {}; - function mockSurveyLoaded(surveyConf) { const commands = window._brandmetrics || []; commands.forEach(command => { @@ -81,16 +77,14 @@ function mockSurveyLoaded(surveyConf) { }); } -function mockCreativeInView(creativeInViewConf) { - const commands = window._brandmetrics || []; - commands.forEach(command => { - if (command.cmd === '_addeventlistener') { - const conf = command.val; - if (conf.event === 'creative_in_view') { - conf.handler(creativeInViewConf); - } +function scriptTagExists(url) { + const tags = document.getElementsByTagName('script'); + for (let i = 0; i < tags.length; i++) { + if (tags[i].src === url) { + return true; } - }) + } + return false; } describe('BrandmetricsRTD module', () => { @@ -122,10 +116,6 @@ describe('BrandmetricsRTD module', () => { it('should not init when there is no usp- consent', () => { expect(brandmetricsRTD.brandmetricsSubmodule.init(VALID_CONFIG, NO_USP_CONSENT)).to.equal(false); }); - - it('should init if there are no consent- objects defined', () => { - expect(brandmetricsRTD.brandmetricsSubmodule.init(VALID_CONFIG, UNDEFINED_USER_CONSENT)).to.equal(true); - }); }); describe('getBidRequestData', () => { @@ -198,62 +188,4 @@ describe('getBidRequestData', () => { expect(bidderOrtb2[exp].user.ext.data.brandmetrics_survey).to.equal('mockMeasurementId') }) }); - - describe('billable events', () => { - let sandbox; - let eventsEmitSpy; - - before(() => { - sandbox = sinon.sandbox.create(); - eventsEmitSpy = sandbox.spy(events, ['emit']); - }); - - beforeEach(() => { - eventsEmitSpy.resetHistory(); - }) - - afterEach(() => { - sandbox.restore(); - }); - - it('should emit billable event from prebid events', () => { - const expectedEvent = { - vendor: 'brandmetrics', - type: 'creative_in_view', - measurementId: 'mockMeasurementId', - auctionId: 'mockAuctionId', - transactionId: 'mockTransactionId' - }; - - mockCreativeInView({ - mid: expectedEvent.measurementId, - source: { - type: 'pbj', - data: { - auctionId: expectedEvent.auctionId, - transactionId: expectedEvent.transactionId - }, - } - }); - - expect(eventsEmitSpy.callCount).to.equal(1); - - const event = eventsEmitSpy.getCalls()[0].args[1]; - delete event['billingId']; - - expect(event).to.deep.equal(expectedEvent); - }); - - it('should not emit billable event from non prebid- sources', () => { - mockCreativeInView({ - mid: 'mockMeasurementId', - source: { - type: 'gpt', - data: {}, - } - }); - - expect(eventsEmitSpy.callCount).to.equal(0); - }); - }); }); diff --git a/test/spec/modules/bridBidAdapter_spec.js b/test/spec/modules/bridBidAdapter_spec.js deleted file mode 100644 index 7503c748999..00000000000 --- a/test/spec/modules/bridBidAdapter_spec.js +++ /dev/null @@ -1,129 +0,0 @@ -import { spec } from '../../../modules/bridBidAdapter.js' - -describe('Brid Bid Adapter', function() { - const videoRequest = [{ - bidder: 'brid', - params: { - placementId: 12345, - }, - mediaTypes: { - video: { - playerSize: [[640, 360]], - context: 'instream', - playbackmethod: [1, 2, 3, 4] - } - } - }]; - - it('Test the bid validation function', function() { - const validBid = spec.isBidRequestValid(videoRequest[0]); - const invalidBid = spec.isBidRequestValid(null); - - expect(validBid).to.be.true; - expect(invalidBid).to.be.false; - }); - - it('Test the request processing function', function () { - const request = spec.buildRequests(videoRequest, videoRequest[0]); - expect(request).to.not.be.empty; - - const payload = JSON.parse(request[0].data); - expect(payload).to.not.be.empty; - expect(payload.sdk).to.deep.equal({ - source: 'pbjs', - version: '$prebid.version$' - }); - expect(payload.imp[0].ext.prebid.storedrequest.id).to.equal(12345); - }); - - it('Test nobid responses', function () { - const responseBody = { - 'id': 'test-id', - 'cur': 'USD', - 'seatbid': [], - 'nbr': 0 - }; - const bidderRequest = null; - - const bidResponse = spec.interpretResponse({ body: responseBody }, {bidderRequest}); - - expect(bidResponse.length).to.equal(0); - }); - - it('Test the response parsing function', function () { - const responseBody = { - 'id': 'test-id', - 'cur': 'USD', - 'seatbid': [{ - 'bid': [{ - 'id': '5044997188309660254', - 'price': 5, - 'adm': 'test ad', - 'adid': '97517771', - 'crid': '97517771', - 'adomain': ['domain.com'], - 'w': 640, - 'h': 480 - }], - 'seat': 'bidder' - }] - }; - const bidderRequest = { - bidderCode: 'brid', - bidderRequestId: '22edbae2733bf6', - bids: videoRequest - }; - - const bidResponse = spec.interpretResponse({ body: responseBody }, {bidderRequest}); - expect(bidResponse).to.not.be.empty; - - const bid = bidResponse[0]; - expect(bid).to.not.be.empty; - expect(bid.ad).to.equal('test ad'); - expect(bid.cpm).to.equal(5); - expect(bid.width).to.equal(640); - expect(bid.height).to.equal(480); - expect(bid.currency).to.equal('USD'); - }); - - it('Test GDPR and USP consents are present in the request', function () { - let gdprConsentString = 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A=='; - let uspConsentString = '1YA-'; - let bidderRequest = { - 'bidderCode': 'brid', - 'bidderRequestId': '22edbae2733bf6', - 'timeout': 3000, - 'uspConsent': uspConsentString, - 'gdprConsent': { - consentString: gdprConsentString, - gdprApplies: true, - addtlConsent: '1~7.12.35.62.66.70.89.93.108' - } - }; - bidderRequest.bids = videoRequest; - - const request = spec.buildRequests(videoRequest, bidderRequest); - const payload = JSON.parse(request[0].data); - - expect(payload.user.ext.consent).to.equal(gdprConsentString); - expect(payload.regs.ext.us_privacy).to.equal(uspConsentString); - expect(payload.regs.ext.gdpr).to.equal(1); - }); - - it('Test GDPR is not present', function () { - let uspConsentString = '1YA-'; - let bidderRequest = { - 'bidderCode': 'brid', - 'bidderRequestId': '22edbae2733bf6', - 'timeout': 3000, - 'uspConsent': uspConsentString - }; - bidderRequest.bids = videoRequest; - - const request = spec.buildRequests(videoRequest, bidderRequest); - const payload = JSON.parse(request[0].data); - - expect(payload.regs.ext.gdpr).to.be.undefined; - expect(payload.regs.ext.us_privacy).to.equal(uspConsentString); - }); -}); diff --git a/test/spec/modules/browsiRtdProvider_spec.js b/test/spec/modules/browsiRtdProvider_spec.js index 5fcc78f4322..75120aa7505 100644 --- a/test/spec/modules/browsiRtdProvider_spec.js +++ b/test/spec/modules/browsiRtdProvider_spec.js @@ -89,6 +89,12 @@ describe('browsi Real time data sub module', function () { expect(browsiRTD.browsiSubmodule.getTargetingData([], null, null, auction)).to.eql({}); }); + it('should return NA if no prediction for ad unit', function () { + makeSlot({code: 'adMock', divId: 'browsiAd_2'}); + browsiRTD.setData({}); + expect(browsiRTD.browsiSubmodule.getTargetingData(['adMock'], null, null, auction)).to.eql({adMock: {bv: 'NA'}}); + }); + it('should return prediction from server', function () { makeSlot({code: 'hasPrediction', divId: 'hasPrediction'}); const data = { diff --git a/test/spec/modules/byDataAnalyticsAdapter_spec.js b/test/spec/modules/byDataAnalyticsAdapter_spec.js index c680c687a71..1346284695c 100644 --- a/test/spec/modules/byDataAnalyticsAdapter_spec.js +++ b/test/spec/modules/byDataAnalyticsAdapter_spec.js @@ -96,7 +96,7 @@ let expectedDataArgs = { aus: '300x250', bidadv: 'appnexus', bid: '14480e9832f2d2b', - inb: 1, + inb: 0, ito: 0, ipwb: 0, iwb: 0, @@ -107,7 +107,7 @@ let expectedDataArgs = { aus: '250x250', bidadv: 'appnexus', bid: '14480e9832f2d2b', - inb: 1, + inb: 0, ito: 0, ipwb: 0, iwb: 0, @@ -167,13 +167,11 @@ describe('byData Analytics Adapter ', () => { }); describe('track-events', function () { - before(() => { - ascAdapter.enableAnalytics(initOptions) - // Step 1: Initialize adapter - adapterManager.enableAnalytics({ - provider: 'bydata', - options: initOptions - }); + ascAdapter.enableAnalytics(initOptions) + // Step 1: Initialize adapter + adapterManager.enableAnalytics({ + provider: 'bydata', + options: initOptions }); it('sends and formatted auction data ', function () { events.emit(constants.EVENTS.BID_TIMEOUT, bidTimeoutArgs); diff --git a/test/spec/modules/cadentApertureMXBidAdapter_spec.js b/test/spec/modules/cadentApertureMXBidAdapter_spec.js index 3ccb5405552..091a8105354 100644 --- a/test/spec/modules/cadentApertureMXBidAdapter_spec.js +++ b/test/spec/modules/cadentApertureMXBidAdapter_spec.js @@ -1,8 +1,7 @@ -import * as utils from 'src/utils.js'; - import { expect } from 'chai'; -import { newBidder } from 'src/adapters/bidderFactory.js'; import { spec } from 'modules/cadentApertureMXBidAdapter.js'; +import * as utils from 'src/utils.js'; +import { newBidder } from 'src/adapters/bidderFactory.js'; describe('cadent_aperture_mx Adapter', function () { describe('callBids', function () { @@ -237,312 +236,228 @@ describe('cadent_aperture_mx Adapter', function () { 'bidId': '30b31c2501de1e', 'auctionId': 'e19f1eff-8b27-42a6-888d-9674e5a6130c', 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', - 'ortb2Imp': { - 'ext': { - 'tid': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ed', - }, - }, }] }; let request = spec.buildRequests(bidderRequest.bids, bidderRequest); - describe('non-gpp tests', function() { - it('sends bid request to ENDPOINT via POST', function () { - expect(request.method).to.equal('POST'); - }); - - it('contains the correct options', function () { - expect(request.options.withCredentials).to.equal(true); - }); - - it('contains a properly formatted endpoint url', function () { - const url = request.url.split('?'); - const queryParams = url[1].split('&'); - expect(queryParams[0]).to.match(new RegExp('^t=\d*', 'g')); - expect(queryParams[1]).to.match(new RegExp('^ts=\d*', 'g')); - }); - - it('builds bidfloor value from bid param when getFloor function does not exist', function () { - const bidRequestWithFloor = utils.deepClone(bidderRequest.bids); - bidRequestWithFloor[0].params.bidfloor = 1; - const requestWithFloor = spec.buildRequests(bidRequestWithFloor, bidderRequest); - const data = JSON.parse(requestWithFloor.data); - expect(data.imp[0].bidfloor).to.equal(bidRequestWithFloor[0].params.bidfloor); - }); - - it('builds bidfloor value from getFloor function when it exists', function () { - const floorResponse = { currency: 'USD', floor: 3 }; - const bidRequestWithGetFloor = utils.deepClone(bidderRequest.bids); - bidRequestWithGetFloor[0].getFloor = () => floorResponse; - const requestWithGetFloor = spec.buildRequests(bidRequestWithGetFloor, bidderRequest); - const data = JSON.parse(requestWithGetFloor.data); - expect(data.imp[0].bidfloor).to.equal(3); - }); - - it('builds bidfloor value from getFloor when both floor and getFloor function exists', function () { - const floorResponse = { currency: 'USD', floor: 3 }; - const bidRequestWithBothFloors = utils.deepClone(bidderRequest.bids); - bidRequestWithBothFloors[0].params.bidfloor = 1; - bidRequestWithBothFloors[0].getFloor = () => floorResponse; - const requestWithBothFloors = spec.buildRequests(bidRequestWithBothFloors, bidderRequest); - const data = JSON.parse(requestWithBothFloors.data); - expect(data.imp[0].bidfloor).to.equal(3); - }); + it('sends bid request to ENDPOINT via POST', function () { + expect(request.method).to.equal('POST'); + }); - it('empty bidfloor value when floor and getFloor is not defined', function () { - const bidRequestWithoutFloor = utils.deepClone(bidderRequest.bids); - const requestWithoutFloor = spec.buildRequests(bidRequestWithoutFloor, bidderRequest); - const data = JSON.parse(requestWithoutFloor.data); - expect(data.imp[0].bidfloor).to.not.exist; - }); + it('contains the correct options', function () { + expect(request.options.withCredentials).to.equal(true); + }); - it('builds request properly', function () { - const data = JSON.parse(request.data); - expect(Array.isArray(data.imp)).to.equal(true); - expect(data.id).to.equal(bidderRequest.auctionId); - expect(data.imp.length).to.equal(1); - expect(data.imp[0].id).to.equal('30b31c2501de1e'); - expect(data.imp[0].tid).to.equal('d7b773de-ceaa-484d-89ca-d9f51b8d61ed'); - expect(data.imp[0].tagid).to.equal('25251'); - expect(data.imp[0].secure).to.equal(0); - expect(data.imp[0].vastXml).to.equal(undefined); - }); + it('contains a properly formatted endpoint url', function () { + const url = request.url.split('?'); + const queryParams = url[1].split('&'); + expect(queryParams[0]).to.match(new RegExp('^t=\d*', 'g')); + expect(queryParams[1]).to.match(new RegExp('^ts=\d*', 'g')); + }); - it('populates id even when auctionId is not available', function () { - // addressing https://github.com/prebid/Prebid.js/issues/9781 - bidderRequest.auctionId = null; - request = spec.buildRequests(bidderRequest.bids, bidderRequest); + it('builds bidfloor value from bid param when getFloor function does not exist', function () { + const bidRequestWithFloor = utils.deepClone(bidderRequest.bids); + bidRequestWithFloor[0].params.bidfloor = 1; + const requestWithFloor = spec.buildRequests(bidRequestWithFloor, bidderRequest); + const data = JSON.parse(requestWithFloor.data); + expect(data.imp[0].bidfloor).to.equal(bidRequestWithFloor[0].params.bidfloor); + }); - const data = JSON.parse(request.data); - expect(data.id).not.to.be.null; - expect(data.id).not.to.equal(bidderRequest.auctionId); - }); + it('builds bidfloor value from getFloor function when it exists', function () { + const floorResponse = { currency: 'USD', floor: 3 }; + const bidRequestWithGetFloor = utils.deepClone(bidderRequest.bids); + bidRequestWithGetFloor[0].getFloor = () => floorResponse; + const requestWithGetFloor = spec.buildRequests(bidRequestWithGetFloor, bidderRequest); + const data = JSON.parse(requestWithGetFloor.data); + expect(data.imp[0].bidfloor).to.equal(3); + }); - it('properly sends site information and protocol', function () { - request = spec.buildRequests(bidderRequest.bids, bidderRequest); - request = JSON.parse(request.data); - expect(request.site).to.have.property('domain', 'example.com'); - expect(request.site).to.have.property('page', 'https://example.com/index.html?pbjs_debug=true'); - expect(request.site).to.have.property('ref', 'https://referrer.com'); - }); + it('builds bidfloor value from getFloor when both floor and getFloor function exists', function () { + const floorResponse = { currency: 'USD', floor: 3 }; + const bidRequestWithBothFloors = utils.deepClone(bidderRequest.bids); + bidRequestWithBothFloors[0].params.bidfloor = 1; + bidRequestWithBothFloors[0].getFloor = () => floorResponse; + const requestWithBothFloors = spec.buildRequests(bidRequestWithBothFloors, bidderRequest); + const data = JSON.parse(requestWithBothFloors.data); + expect(data.imp[0].bidfloor).to.equal(3); + }); - it('builds correctly formatted request banner object', function () { - let bidRequestWithBanner = utils.deepClone(bidderRequest.bids); - let request = spec.buildRequests(bidRequestWithBanner, bidderRequest); - const data = JSON.parse(request.data); - expect(data.imp[0].video).to.equal(undefined); - expect(data.imp[0].banner).to.exist.and.to.be.a('object'); - expect(data.imp[0].banner.w).to.equal(bidRequestWithBanner[0].mediaTypes.banner.sizes[0][0]); - expect(data.imp[0].banner.h).to.equal(bidRequestWithBanner[0].mediaTypes.banner.sizes[0][1]); - expect(data.imp[0].banner.format[0].w).to.equal(bidRequestWithBanner[0].mediaTypes.banner.sizes[0][0]); - expect(data.imp[0].banner.format[0].h).to.equal(bidRequestWithBanner[0].mediaTypes.banner.sizes[0][1]); - expect(data.imp[0].banner.format[1].w).to.equal(bidRequestWithBanner[0].mediaTypes.banner.sizes[1][0]); - expect(data.imp[0].banner.format[1].h).to.equal(bidRequestWithBanner[0].mediaTypes.banner.sizes[1][1]); - }); + it('empty bidfloor value when floor and getFloor is not defined', function () { + const bidRequestWithoutFloor = utils.deepClone(bidderRequest.bids); + const requestWithoutFloor = spec.buildRequests(bidRequestWithoutFloor, bidderRequest); + const data = JSON.parse(requestWithoutFloor.data); + expect(data.imp[0].bidfloor).to.not.exist; + }); - it('builds correctly formatted request video object for instream', function () { - let bidRequestWithVideo = utils.deepClone(bidderRequest.bids); - bidRequestWithVideo[0].mediaTypes = { - video: { - context: 'instream', - playerSize: [[640, 480]] - }, - }; - bidRequestWithVideo[0].params.video = {}; - let request = spec.buildRequests(bidRequestWithVideo, bidderRequest); - const data = JSON.parse(request.data); - expect(data.imp[0].video).to.exist.and.to.be.a('object'); - expect(data.imp[0].video.w).to.equal(bidRequestWithVideo[0].mediaTypes.video.playerSize[0][0]); - expect(data.imp[0].video.h).to.equal(bidRequestWithVideo[0].mediaTypes.video.playerSize[0][1]); - }); + it('builds request properly', function () { + const data = JSON.parse(request.data); + expect(Array.isArray(data.imp)).to.equal(true); + expect(data.id).to.equal(bidderRequest.auctionId); + expect(data.imp.length).to.equal(1); + expect(data.imp[0].id).to.equal('30b31c2501de1e'); + expect(data.imp[0].tid).to.equal('d7b773de-ceaa-484d-89ca-d9f51b8d61ec'); + expect(data.imp[0].tagid).to.equal('25251'); + expect(data.imp[0].secure).to.equal(0); + expect(data.imp[0].vastXml).to.equal(undefined); + }); - it('builds correctly formatted request video object for outstream', function () { - let bidRequestWithOutstreamVideo = utils.deepClone(bidderRequest.bids); - bidRequestWithOutstreamVideo[0].mediaTypes = { - video: { - context: 'outstream', - playerSize: [[640, 480]] - }, - }; - bidRequestWithOutstreamVideo[0].params.video = {}; - let request = spec.buildRequests(bidRequestWithOutstreamVideo, bidderRequest); - const data = JSON.parse(request.data); - expect(data.imp[0].video).to.exist.and.to.be.a('object'); - expect(data.imp[0].video.w).to.equal(bidRequestWithOutstreamVideo[0].mediaTypes.video.playerSize[0][0]); - expect(data.imp[0].video.h).to.equal(bidRequestWithOutstreamVideo[0].mediaTypes.video.playerSize[0][1]); - }); + it('properly sends site information and protocol', function () { + request = spec.buildRequests(bidderRequest.bids, bidderRequest); + request = JSON.parse(request.data); + expect(request.site).to.have.property('domain', 'example.com'); + expect(request.site).to.have.property('page', 'https://example.com/index.html?pbjs_debug=true'); + expect(request.site).to.have.property('ref', 'https://referrer.com'); + }); - it('shouldn\'t contain a user obj without GDPR information', function () { - let request = spec.buildRequests(bidderRequest.bids, bidderRequest) - request = JSON.parse(request.data) - expect(request).to.not.have.property('user'); - }); + it('builds correctly formatted request banner object', function () { + let bidRequestWithBanner = utils.deepClone(bidderRequest.bids); + let request = spec.buildRequests(bidRequestWithBanner, bidderRequest); + const data = JSON.parse(request.data); + expect(data.imp[0].video).to.equal(undefined); + expect(data.imp[0].banner).to.exist.and.to.be.a('object'); + expect(data.imp[0].banner.w).to.equal(bidRequestWithBanner[0].mediaTypes.banner.sizes[0][0]); + expect(data.imp[0].banner.h).to.equal(bidRequestWithBanner[0].mediaTypes.banner.sizes[0][1]); + expect(data.imp[0].banner.format[0].w).to.equal(bidRequestWithBanner[0].mediaTypes.banner.sizes[0][0]); + expect(data.imp[0].banner.format[0].h).to.equal(bidRequestWithBanner[0].mediaTypes.banner.sizes[0][1]); + expect(data.imp[0].banner.format[1].w).to.equal(bidRequestWithBanner[0].mediaTypes.banner.sizes[1][0]); + expect(data.imp[0].banner.format[1].h).to.equal(bidRequestWithBanner[0].mediaTypes.banner.sizes[1][1]); + }); - it('should have the right gdpr info when enabled', function () { - let consentString = 'OIJSZsOAFsABAB8EMXZZZZZ+A=='; - const gdprBidderRequest = utils.deepClone(bidderRequest); - gdprBidderRequest.gdprConsent = { - 'consentString': consentString, - 'gdprApplies': true - }; - let request = spec.buildRequests(gdprBidderRequest.bids, gdprBidderRequest); - - request = JSON.parse(request.data) - expect(request.regs.ext).to.have.property('gdpr', 1); - expect(request.user.ext).to.have.property('consent', consentString); - }); + it('builds correctly formatted request video object for instream', function () { + let bidRequestWithVideo = utils.deepClone(bidderRequest.bids); + bidRequestWithVideo[0].mediaTypes = { + video: { + context: 'instream', + playerSize: [[640, 480]] + }, + }; + bidRequestWithVideo[0].params.video = {}; + let request = spec.buildRequests(bidRequestWithVideo, bidderRequest); + const data = JSON.parse(request.data); + expect(data.imp[0].video).to.exist.and.to.be.a('object'); + expect(data.imp[0].video.w).to.equal(bidRequestWithVideo[0].mediaTypes.video.playerSize[0][0]); + expect(data.imp[0].video.h).to.equal(bidRequestWithVideo[0].mediaTypes.video.playerSize[0][1]); + }); - it('should\'t contain consent string if gdpr isn\'t applied', function () { - const nonGdprBidderRequest = utils.deepClone(bidderRequest); - nonGdprBidderRequest.gdprConsent = { - 'gdprApplies': false - }; - let request = spec.buildRequests(nonGdprBidderRequest.bids, nonGdprBidderRequest); - request = JSON.parse(request.data) - expect(request.regs.ext).to.have.property('gdpr', 0); - expect(request).to.not.have.property('user'); - }); + it('builds correctly formatted request video object for outstream', function () { + let bidRequestWithOutstreamVideo = utils.deepClone(bidderRequest.bids); + bidRequestWithOutstreamVideo[0].mediaTypes = { + video: { + context: 'outstream', + playerSize: [[640, 480]] + }, + }; + bidRequestWithOutstreamVideo[0].params.video = {}; + let request = spec.buildRequests(bidRequestWithOutstreamVideo, bidderRequest); + const data = JSON.parse(request.data); + expect(data.imp[0].video).to.exist.and.to.be.a('object'); + expect(data.imp[0].video.w).to.equal(bidRequestWithOutstreamVideo[0].mediaTypes.video.playerSize[0][0]); + expect(data.imp[0].video.h).to.equal(bidRequestWithOutstreamVideo[0].mediaTypes.video.playerSize[0][1]); + }); - it('should add us privacy info to request', function() { - const uspBidderRequest = utils.deepClone(bidderRequest); - let consentString = '1YNN'; - uspBidderRequest.uspConsent = consentString; - let request = spec.buildRequests(uspBidderRequest.bids, uspBidderRequest); - request = JSON.parse(request.data); - expect(request.us_privacy).to.exist; - expect(request.us_privacy).to.exist.and.to.equal(consentString); - }); + it('shouldn\'t contain a user obj without GDPR information', function () { + let request = spec.buildRequests(bidderRequest.bids, bidderRequest) + request = JSON.parse(request.data) + expect(request).to.not.have.property('user'); + }); - it('should add schain object to request', function() { - const schainBidderRequest = utils.deepClone(bidderRequest); - schainBidderRequest.bids[0].schain = { - 'complete': 1, - 'ver': '1.0', - 'nodes': [ - { - 'asi': 'testing.com', - 'sid': 'abc', - 'hp': 1 - } - ] - }; - let request = spec.buildRequests(schainBidderRequest.bids, schainBidderRequest); - request = JSON.parse(request.data); - expect(request.source.ext.schain).to.exist; - expect(request.source.ext.schain).to.have.property('complete', 1); - expect(request.source.ext.schain).to.have.property('ver', '1.0'); - expect(request.source.ext.schain.nodes[0].asi).to.equal(schainBidderRequest.bids[0].schain.nodes[0].asi); - }); + it('should have the right gdpr info when enabled', function () { + let consentString = 'OIJSZsOAFsABAB8EMXZZZZZ+A=='; + const gdprBidderRequest = utils.deepClone(bidderRequest); + gdprBidderRequest.gdprConsent = { + 'consentString': consentString, + 'gdprApplies': true + }; + let request = spec.buildRequests(gdprBidderRequest.bids, gdprBidderRequest); - it('should add liveramp identitylink id to request', () => { - const idl_env = '123'; - const bidRequestWithID = utils.deepClone(bidderRequest); - bidRequestWithID.userId = { idl_env }; - let requestWithID = spec.buildRequests(bidRequestWithID.bids, bidRequestWithID); - requestWithID = JSON.parse(requestWithID.data); - expect(requestWithID.user.ext.eids[0]).to.deep.equal({ - source: 'liveramp.com', - uids: [{ - id: idl_env, - ext: { - rtiPartner: 'idl' - } - }] - }); - }); + request = JSON.parse(request.data) + expect(request.regs.ext).to.have.property('gdpr', 1); + expect(request.user.ext).to.have.property('consent', consentString); + }); - it('should add gpid to request if present', () => { - const gpid = '/12345/my-gpt-tag-0'; - let bid = utils.deepClone(bidderRequest.bids[0]); - bid.ortb2Imp = { ext: { data: { adserver: { adslot: gpid } } } }; - bid.ortb2Imp = { ext: { data: { pbadslot: gpid } } }; - let requestWithGPID = spec.buildRequests([bid], bidderRequest); - requestWithGPID = JSON.parse(requestWithGPID.data); - expect(requestWithGPID.imp[0].ext.gpid).to.exist.and.equal(gpid); - }); + it('should\'t contain consent string if gdpr isn\'t applied', function () { + const nonGdprBidderRequest = utils.deepClone(bidderRequest); + nonGdprBidderRequest.gdprConsent = { + 'gdprApplies': false + }; + let request = spec.buildRequests(nonGdprBidderRequest.bids, nonGdprBidderRequest); + request = JSON.parse(request.data) + expect(request.regs.ext).to.have.property('gdpr', 0); + expect(request).to.not.have.property('user'); + }); - it('should add UID 2.0 to request', () => { - const uid2 = { id: '456' }; - const bidRequestWithUID = utils.deepClone(bidderRequest); - bidRequestWithUID.userId = { uid2 }; - let requestWithUID = spec.buildRequests(bidRequestWithUID.bids, bidRequestWithUID); - requestWithUID = JSON.parse(requestWithUID.data); - expect(requestWithUID.user.ext.eids[0]).to.deep.equal({ - source: 'uidapi.com', - uids: [{ - id: uid2.id, - ext: { - rtiPartner: 'UID2' - } - }] - }); - }); + it('should add us privacy info to request', function() { + const uspBidderRequest = utils.deepClone(bidderRequest); + let consentString = '1YNN'; + uspBidderRequest.uspConsent = consentString; + let request = spec.buildRequests(uspBidderRequest.bids, uspBidderRequest); + request = JSON.parse(request.data); + expect(request.us_privacy).to.exist; + expect(request.us_privacy).to.exist.and.to.equal(consentString); }); - describe('gpp tests', function() { - describe('when gppConsent is not present on bid request', () => { - it('should return request with no gpp or gpp_sid properties', function() { - const gppCompliantBidderRequest = utils.deepClone(bidderRequest); + it('should add schain object to request', function() { + const schainBidderRequest = utils.deepClone(bidderRequest); + schainBidderRequest.bids[0].schain = { + 'complete': 1, + 'ver': '1.0', + 'nodes': [ + { + 'asi': 'testing.com', + 'sid': 'abc', + 'hp': 1 + } + ] + }; + let request = spec.buildRequests(schainBidderRequest.bids, schainBidderRequest); + request = JSON.parse(request.data); + expect(request.source.ext.schain).to.exist; + expect(request.source.ext.schain).to.have.property('complete', 1); + expect(request.source.ext.schain).to.have.property('ver', '1.0'); + expect(request.source.ext.schain.nodes[0].asi).to.equal(schainBidderRequest.bids[0].schain.nodes[0].asi); + }); - let request = spec.buildRequests(gppCompliantBidderRequest.bids, gppCompliantBidderRequest); - request = JSON.parse(request.data); - expect(request?.regs?.gpp).to.be.undefined; - expect(request?.regs?.gpp_sid).to.be.undefined; - }); + it('should add liveramp identitylink id to request', () => { + const idl_env = '123'; + const bidRequestWithID = utils.deepClone(bidderRequest); + bidRequestWithID.userId = { idl_env }; + let requestWithID = spec.buildRequests(bidRequestWithID.bids, bidRequestWithID); + requestWithID = JSON.parse(requestWithID.data); + expect(requestWithID.user.ext.eids[0]).to.deep.equal({ + source: 'liveramp.com', + uids: [{ + id: idl_env, + ext: { + rtiPartner: 'idl' + } + }] }); + }); - describe('when gppConsent is present on bid request', () => { - describe('gppString', () => { - describe('is not defined on request', () => { - it('should return request with gpp undefined', () => { - const gppCompliantBidderRequest = utils.deepClone(bidderRequest); - - let request = spec.buildRequests(gppCompliantBidderRequest.bids, gppCompliantBidderRequest); - request = JSON.parse(request.data); - expect(request?.regs?.gpp).to.be.undefined; - }); - }); - - describe('is defined on request', () => { - it('should return request with gpp set correctly', () => { - const gppCompliantBidderRequest = utils.deepClone(bidderRequest); - const gppString = 'abcdefgh'; - gppCompliantBidderRequest.gppConsent = { - gppString - } - - let request = spec.buildRequests(gppCompliantBidderRequest.bids, gppCompliantBidderRequest); - request = JSON.parse(request.data); - expect(request.regs.gpp).to.exist.and.to.equal(gppString); - }); - }); - }); + it('should add gpid to request if present', () => { + const gpid = '/12345/my-gpt-tag-0'; + let bid = utils.deepClone(bidderRequest.bids[0]); + bid.ortb2Imp = { ext: { data: { adserver: { adslot: gpid } } } }; + bid.ortb2Imp = { ext: { data: { pbadslot: gpid } } }; + let requestWithGPID = spec.buildRequests([bid], bidderRequest); + requestWithGPID = JSON.parse(requestWithGPID.data); + expect(requestWithGPID.imp[0].ext.gpid).to.exist.and.equal(gpid); + }); - describe('applicableSections', () => { - describe('is not defined on request', () => { - it('should return request with gpp_sid undefined', () => { - const gppCompliantBidderRequest = utils.deepClone(bidderRequest); - - let request = spec.buildRequests(gppCompliantBidderRequest.bids, gppCompliantBidderRequest); - request = JSON.parse(request.data); - expect(request?.regs?.gpp_sid).to.be.undefined; - }); - }); - - describe('is defined on request', () => { - it('should return request with gpp_sid set correctly', () => { - const gppCompliantBidderRequest = utils.deepClone(bidderRequest); - const applicableSections = [8]; - gppCompliantBidderRequest.gppConsent = { - applicableSections - } - - let request = spec.buildRequests(gppCompliantBidderRequest.bids, gppCompliantBidderRequest); - request = JSON.parse(request.data); - expect(request.regs.gpp_sid).to.deep.equal(applicableSections); - }); - }); - }); + it('should add UID 2.0 to request', () => { + const uid2 = { id: '456' }; + const bidRequestWithUID = utils.deepClone(bidderRequest); + bidRequestWithUID.userId = { uid2 }; + let requestWithUID = spec.buildRequests(bidRequestWithUID.bids, bidRequestWithUID); + requestWithUID = JSON.parse(requestWithUID.data); + expect(requestWithUID.user.ext.eids[0]).to.deep.equal({ + source: 'uidapi.com', + uids: [{ + id: uid2.id, + ext: { + rtiPartner: 'UID2' + } + }] }); }); }); @@ -784,7 +699,7 @@ describe('cadent_aperture_mx Adapter', function () { it('should not throw an error when decoding an improperly encoded adm', function () { const badAdmServerResponse = utils.deepClone(serverResponse); - badAdmServerResponse.seatbid[0].bid[0].adm = '\\<\\/script\\>'; + badAdmServerResponse.seatbid[0].bid[0].adm = '\\<\\/script\\>'; badAdmServerResponse.seatbid[1].bid[0].adm = '%3F%%3Dcadent%3C3prebid'; assert.doesNotThrow(() => spec.interpretResponse({ @@ -849,38 +764,5 @@ describe('cadent_aperture_mx Adapter', function () { expect(syncs[0].url).to.contains('usp=test'); expect(syncs[0].url).to.equal('https://biddr.brealtime.com/check.html?gdpr=1&gdpr_consent=test&usp=test') }); - - it('should pass gpp string and section id', function() { - let syncs = spec.getUserSyncs({iframeEnabled: true}, {}, {}, {}, { - gppString: 'abcdefgs', - applicableSections: [1, 2, 4] - }); - expect(syncs).to.not.be.an('undefined'); - expect(syncs[0].url).to.contains('gpp=abcdefgs') - expect(syncs[0].url).to.contains('gpp_sid=1,2,4') - }); - - it('should pass us_privacy and gdpr string and gpp string', function () { - let syncs = spec.getUserSyncs({ iframeEnabled: true }, {}, - { - gdprApplies: true, - consentString: 'test' - }, - { - consentString: 'test' - }, - { - gppString: 'abcdefgs', - applicableSections: [1, 2, 4] - } - ); - expect(syncs).to.not.be.an('undefined'); - expect(syncs).to.have.lengthOf(1); - expect(syncs[0].type).to.equal('iframe'); - expect(syncs[0].url).to.contains('gdpr=1'); - expect(syncs[0].url).to.contains('usp=test'); - expect(syncs[0].url).to.contains('gpp=abcdefgs'); - expect(syncs[0].url).to.equal('https://biddr.brealtime.com/check.html?gdpr=1&gdpr_consent=test&usp=test&gpp=abcdefgs&gpp_sid=1,2,4'); - }); }); }); diff --git a/test/spec/modules/categoryTranslation_spec.js b/test/spec/modules/categoryTranslation_spec.js index d4f6aa66c7d..2301d6aab1b 100644 --- a/test/spec/modules/categoryTranslation_spec.js +++ b/test/spec/modules/categoryTranslation_spec.js @@ -2,16 +2,18 @@ import { getAdserverCategoryHook, initTranslation, storage } from 'modules/categ 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 fakeTranslationServer; let getLocalStorageStub; beforeEach(function () { + fakeTranslationServer = sinon.fakeServer.create(); getLocalStorageStub = sinon.stub(storage, 'getDataFromLocalStorage'); }); afterEach(function() { + fakeTranslationServer.reset(); getLocalStorageStub.restore(); config.resetConfig(); }); @@ -71,7 +73,7 @@ describe('category translation', function () { } })); initTranslation(); - expect(server.requests.length).to.equal(0); + expect(fakeTranslationServer.requests.length).to.equal(0); clock.restore(); }); @@ -84,15 +86,15 @@ describe('category translation', function () { } })); initTranslation(); - expect(server.requests.length).to.equal(1); + expect(fakeTranslationServer.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/'); + expect(fakeTranslationServer.requests.length).to.equal(1); + expect(fakeTranslationServer.requests[0].url).to.equal('http://sample.com'); }); it('should use publisher defined mapping file', function () { @@ -103,7 +105,7 @@ describe('category translation', function () { }); 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/'); + expect(fakeTranslationServer.requests.length).to.equal(2); + expect(fakeTranslationServer.requests[0].url).to.equal('http://sample.com'); }); }); diff --git a/test/spec/modules/chtnwBidAdapter_spec.js b/test/spec/modules/chtnwBidAdapter_spec.js index 0fdd1a3f19b..3875de6c4fa 100644 --- a/test/spec/modules/chtnwBidAdapter_spec.js +++ b/test/spec/modules/chtnwBidAdapter_spec.js @@ -39,10 +39,7 @@ describe('ChtnwAdapter', function () { ], }]; - let request; - before(() => { - request = spec.buildRequests(bidRequests); - }) + const request = spec.buildRequests(bidRequests); it('Returns POST method', function () { expect(request.method).to.equal('POST'); diff --git a/test/spec/modules/concertBidAdapter_spec.js b/test/spec/modules/concertBidAdapter_spec.js index 0a76ed3e62d..96c98e5e5a2 100644 --- a/test/spec/modules/concertBidAdapter_spec.js +++ b/test/spec/modules/concertBidAdapter_spec.js @@ -94,7 +94,7 @@ describe('ConcertAdapter', function () { }); describe('spec.isBidRequestValid', function() { - it('should return when it received all the required params', function() { + it('should return when it recieved all the required params', function() { const bid = bidRequests[0]; expect(spec.isBidRequestValid(bid)).to.equal(true); }); @@ -116,20 +116,7 @@ describe('ConcertAdapter', function () { expect(payload).to.have.property('meta'); expect(payload).to.have.property('slots'); - const metaRequiredFields = [ - 'prebidVersion', - 'pageUrl', - 'screen', - 'debug', - 'uid', - 'optedOut', - 'adapterVersion', - 'uspConsent', - 'gdprConsent', - 'gppConsent', - 'browserLanguage', - 'tdid' - ]; + const metaRequiredFields = ['prebidVersion', 'pageUrl', 'screen', 'debug', 'uid', 'optedOut', 'adapterVersion', 'uspConsent', 'gdprConsent', 'gppConsent']; const slotsRequiredFields = ['name', 'bidId', 'transactionId', 'sizes', 'partnerId', 'slotType']; metaRequiredFields.forEach(function(field) { @@ -212,31 +199,6 @@ describe('ConcertAdapter', function () { expect(slot.offsetCoordinates.x).to.equal(100) expect(slot.offsetCoordinates.y).to.equal(0) }) - - it('should not pass along tdid if the user has opted out', function() { - storage.setDataInLocalStorage('c_nap', 'true'); - const request = spec.buildRequests(bidRequests, bidRequest); - const payload = JSON.parse(request.data); - - expect(payload.meta.tdid).to.be.null; - }); - - it('should not pass along tdid if USP consent disallows', function() { - storage.removeDataFromLocalStorage('c_nap'); - const request = spec.buildRequests(bidRequests, { ...bidRequest, uspConsent: '1YY' }); - const payload = JSON.parse(request.data); - - expect(payload.meta.tdid).to.be.null; - }); - - it('should pass along tdid if the user has not opted out', function() { - storage.removeDataFromLocalStorage('c_nap', 'true'); - const tdid = '123abc'; - const bidRequestsWithTdid = [{ ...bidRequests[0], userId: { tdid } }] - const request = spec.buildRequests(bidRequestsWithTdid, bidRequest); - const payload = JSON.parse(request.data); - expect(payload.meta.tdid).to.equal(tdid); - }); }); describe('spec.interpretResponse', function() { diff --git a/test/spec/modules/connatixBidAdapter_spec.js b/test/spec/modules/connatixBidAdapter_spec.js deleted file mode 100644 index 78f6a9d410d..00000000000 --- a/test/spec/modules/connatixBidAdapter_spec.js +++ /dev/null @@ -1,350 +0,0 @@ -import { expect } from 'chai'; -import { - spec, - getBidFloor as connatixGetBidFloor -} from '../../../modules/connatixBidAdapter.js'; -import { BANNER } from '../../../src/mediaTypes.js'; - -describe('connatixBidAdapter', function () { - let bid; - - function mockBidRequest() { - const mediaTypes = { - banner: { - sizes: [16, 9], - } - }; - return { - bidId: 'testing', - bidder: 'connatix', - params: { - placementId: '30e91414-545c-4f45-a950-0bec9308ff22' - }, - mediaTypes - }; - }; - - describe('isBidRequestValid', function () { - this.beforeEach(function () { - bid = mockBidRequest(); - }); - - it('Should return true if all required fileds are present', function () { - expect(spec.isBidRequestValid(bid)).to.be.true; - }); - it('Should return false if bidder does not correspond', function () { - bid.bidder = 'abc'; - expect(spec.isBidRequestValid(bid)).to.be.false; - }); - it('Should return false if bidId is missing', function () { - delete bid.bidId; - expect(spec.isBidRequestValid(bid)).to.be.false; - }); - it('Should return false if params object is missing', function () { - delete bid.params; - expect(spec.isBidRequestValid(bid)).to.be.false; - }); - it('Should return false if placementId is missing from params', function () { - delete bid.params.placementId; - expect(spec.isBidRequestValid(bid)).to.be.false; - }); - it('Should return false if mediaTypes is missing', function () { - delete bid.mediaTypes; - expect(spec.isBidRequestValid(bid)).to.be.false; - }); - it('Should return false if banner is missing from mediaTypes ', function () { - delete bid.mediaTypes.banner; - expect(spec.isBidRequestValid(bid)).to.be.false; - }); - it('Should return false if sizes is missing from banner object', function () { - delete bid.mediaTypes.banner.sizes; - expect(spec.isBidRequestValid(bid)).to.be.false; - }); - it('Should return false if sizes is not an array', function () { - bid.mediaTypes.banner.sizes = 'test'; - expect(spec.isBidRequestValid(bid)).to.be.false; - }); - it('Should return false if sizes is an empty array', function () { - bid.mediaTypes.banner.sizes = []; - expect(spec.isBidRequestValid(bid)).to.be.false; - }); - it('Should return true if add an extra field was added to the bidRequest', function () { - bid.params.test = 1; - expect(spec.isBidRequestValid(bid)).to.be.true; - }); - }); - - describe('buildRequests', function () { - let serverRequest; - let bidderRequest = { - refererInfo: { - canonicalUrl: '', - numIframes: 0, - reachedTop: true, - referer: 'http://example.com', - stack: ['http://example.com'] - }, - gdprConsent: { - consentString: 'BOJ/P2HOJ/P2HABABMAAAAAZ+A==', - vendorData: {}, - gdprApplies: true - }, - uspConsent: '1YYY', - ortb2: { - site: { - data: { - pageType: 'article' - } - } - } - }; - - this.beforeEach(function () { - bid = mockBidRequest(); - serverRequest = spec.buildRequests([bid], 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://capi.connatix.com/rtb/hba'); - }); - it('Returns request payload', function () { - expect(serverRequest.data).to.not.empty; - }); - it('Validate request payload', function () { - expect(serverRequest.data.bidRequests[0].bidId).to.equal(bid.bidId); - expect(serverRequest.data.bidRequests[0].placementId).to.equal(bid.params.placementId); - expect(serverRequest.data.bidRequests[0].floor).to.equal(0); - expect(serverRequest.data.bidRequests[0].mediaTypes).to.equal(bid.mediaTypes); - expect(serverRequest.data.bidRequests[0].sizes).to.equal(bid.mediaTypes.sizes); - expect(serverRequest.data.refererInfo).to.equal(bidderRequest.refererInfo); - expect(serverRequest.data.gdprConsent).to.equal(bidderRequest.gdprConsent); - expect(serverRequest.data.uspConsent).to.equal(bidderRequest.uspConsent); - expect(serverRequest.data.ortb2).to.equal(bidderRequest.ortb2); - }); - }); - - 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}; - - let serverResponse; - this.beforeEach(function () { - serverResponse = { - body: { - Bids: [ Bid ] - }, - headers: function() { } - }; - }); - - it('Should return an empty array if Bids is null', function () { - serverResponse.body.Bids = null; - - const response = spec.interpretResponse(serverResponse); - expect(response).to.be.an('array').that.is.empty; - }); - - it('Should return an empty array if Bids is empty array', function () { - serverResponse.body.Bids = []; - const response = spec.interpretResponse(serverResponse); - expect(response).to.be.an('array').that.is.empty; - }); - - it('Should return one bid response for one bid', function() { - const bidResponses = spec.interpretResponse(serverResponse); - expect(bidResponses.length).to.equal(1); - }); - - it('Should contains the same values as in the serverResponse', function() { - const bidResponses = spec.interpretResponse(serverResponse); - - 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); - expect(bidResponse.currency).to.equal('USD'); - expect(bidResponse.mediaType).to.equal(BANNER); - expect(bidResponse.netRevenue).to.be.true; - }); - - it('Should return n bid responses for n bids', function() { - serverResponse.body.Bids = [ { ...Bid }, { ...Bid } ]; - - const firstBidCpm = 4; - serverResponse.body.Bids[0].Cpm = firstBidCpm; - - const secondBidCpm = 13; - serverResponse.body.Bids[1].Cpm = secondBidCpm; - - const bidResponses = spec.interpretResponse(serverResponse); - expect(bidResponses.length).to.equal(2); - - expect(bidResponses[0].cpm).to.equal(firstBidCpm); - expect(bidResponses[1].cpm).to.equal(secondBidCpm); - }); - }); - - describe('getUserSyncs', function() { - const CustomerId = '99f20d18-c4b4-4a28-3d8e-d43e2c8cb4ac'; - const PlayerId = 'e4984e88-9ff4-45a3-8b9d-33aabcad634f'; - const UserSyncEndpoint = 'https://connatix.com/sync' - const Bid = {Cpm: 0.1, RequestId: '2f897340c4eaa3', Ttl: 86400, CustomerId, PlayerId}; - - const serverResponse = { - body: { - UserSyncEndpoint, - Bids: [ Bid ] - }, - headers: function() { } - }; - - it('Should return an empty array when iframeEnabled: false', function () { - 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; - }); - 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; - }); - 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; - }); - 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; - }); - 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 { 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}, - [serverResponse], - {}, - undefined, - undefined - ); - const { url } = userSyncList[0]; - expect(url).to.equal(`${UserSyncEndpoint}?gdpr=0`); - }); - 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}, - [serverResponse], - {gdprApplies: true}, - undefined, - undefined - ); - const { url } = userSyncList[0]; - expect(url).to.equal(`${UserSyncEndpoint}?gdpr=1`); - }); - it('Should append gdpr_consent if gdprConsent object is present and have gdprApplies field', function () { - const userSyncList = spec.getUserSyncs( - {iframeEnabled: true, pixelEnabled: true}, - [serverResponse], - {gdprApplies: true, consentString: 'alabala'}, - undefined, - undefined - ); - const { url } = userSyncList[0]; - expect(url).to.equal(`${UserSyncEndpoint}?gdpr=1&gdpr_consent=alabala`); - }); - it('Should encodeURI gdpr_consent corectly', function () { - const userSyncList = spec.getUserSyncs( - {iframeEnabled: true, pixelEnabled: true}, - [serverResponse], - {gdprApplies: true, consentString: 'test&2'}, - undefined, - undefined - ); - const { url } = userSyncList[0]; - expect(url).to.equal(`${UserSyncEndpoint}?gdpr=1&gdpr_consent=test%262`); - }); - it('Should append usp_consent to the url if uspConsent is provided', function () { - const userSyncList = spec.getUserSyncs( - {iframeEnabled: true, pixelEnabled: true}, - [serverResponse], - {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 () { - const userSyncList = spec.getUserSyncs( - {iframeEnabled: true, pixelEnabled: true}, - [serverResponse], - {gdprApplies: true, consentString: 'test&2'}, - '1YYYN', - {consent: '1'} - ); - const { url } = userSyncList[0]; - expect(url).to.equal(`${UserSyncEndpoint}?gdpr=1&gdpr_consent=test%262&us_privacy=1YYYN`); - }); - }); - - describe('getBidFloor', function () { - this.beforeEach(function () { - bid = mockBidRequest(); - }); - - it('Should return 0 if both getFloor method and bidfloor param from bid are absent.', function () { - const floor = connatixGetBidFloor(bid); - expect(floor).to.equal(0); - }); - - it('Should return the value of the bidfloor parameter if the getFloor method is not defined but the bidfloor parameter is defined', function () { - const floorValue = 3; - bid.params.bidfloor = floorValue; - - const floor = connatixGetBidFloor(bid); - expect(floor).to.equal(floorValue); - }); - - it('Should return the value of the getFloor method if the getFloor method is defined but the bidfloor parameter is not defined', function () { - const floorValue = 7; - bid.getFloor = function() { - return { floor: floorValue }; - }; - - const floor = connatixGetBidFloor(bid); - expect(floor).to.equal(floorValue); - }); - - it('Should return the value of the getFloor method if both getFloor method and bidfloor parameter are defined', function () { - const floorParamValue = 3; - bid.params.bidfloor = floorParamValue; - - const floorMethodValue = 7; - bid.getFloor = function() { - return { floor: floorMethodValue }; - }; - - const floor = connatixGetBidFloor(bid); - expect(floor).to.equal(floorMethodValue); - }); - - it('Should return 0 if the getFloor method is defined and it crash when call it', function () { - bid.getFloor = function() { - throw new Error('error'); - }; - const floor = connatixGetBidFloor(bid); - expect(floor).to.equal(0); - }); - }); -}); diff --git a/test/spec/modules/connectIdSystem_spec.js b/test/spec/modules/connectIdSystem_spec.js index 686c3d63a63..5376ba60886 100644 --- a/test/spec/modules/connectIdSystem_spec.js +++ b/test/spec/modules/connectIdSystem_spec.js @@ -3,7 +3,6 @@ import {connectIdSubmodule, storage} from 'modules/connectIdSystem.js'; import {server} from '../../mocks/xhr'; import {parseQS, parseUrl} from 'src/utils.js'; import {uspDataHandler, gppDataHandler} from 'src/adapterManager.js'; -import * as refererDetection from '../../../src/refererDetection'; const TEST_SERVER_URL = 'http://localhost:9876/'; @@ -289,79 +288,6 @@ describe('Yahoo ConnectID Submodule', () => { expect(setCookieStub.firstCall.args[2]).to.equal(expiryDelta.toUTCString()); }); - it('returns an object with the stored ID from cookies and syncs because of expired TTL', () => { - 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}; - getCookieStub.withArgs(STORAGE_KEY).returns(JSON.stringify(cookieData)); - - let result = invokeGetIdAPI({ - he: HASHED_EMAIL, - pixelId: PIXEL_ID - }, consentData); - - expect(result).to.be.an('object').that.has.all.keys('id', 'callback'); - expect(result.id).to.deep.equal(cookieData); - expect(typeof result.callback).to.equal('function'); - }); - - it('returns an object with the stored ID from cookies and not syncs because of valid TTL', () => { - 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}; - getCookieStub.withArgs(STORAGE_KEY).returns(JSON.stringify(cookieData)); - - let result = invokeGetIdAPI({ - he: HASHED_EMAIL, - pixelId: PIXEL_ID - }, consentData); - - expect(result).to.be.an('object').that.has.all.keys('id'); - cookieData.lastUsed = result.id.lastUsed; - expect(result.id).to.deep.equal(cookieData); - }); - - it('returns an object with the stored ID from cookies and not syncs because of valid TTL with provided puid', () => { - 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}; - getCookieStub.withArgs(STORAGE_KEY).returns(JSON.stringify(cookieData)); - - let result = invokeGetIdAPI({ - he: HASHED_EMAIL, - pixelId: PIXEL_ID, - puid: '9' - }, consentData); - - expect(result).to.be.an('object').that.has.all.keys('id'); - cookieData.lastUsed = result.id.lastUsed; - expect(result.id).to.deep.equal(cookieData); - }); - - it('returns an object with the stored ID from cookies and syncs because is O&O traffic', () => { - 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}; - getCookieStub.withArgs(STORAGE_KEY).returns(JSON.stringify(cookieData)); - const getRefererInfoStub = sinon.stub(refererDetection, 'getRefererInfo'); - getRefererInfoStub.returns({ - ref: 'https://dev.fc.yahoo.com?test' - }); - let result = invokeGetIdAPI({ - he: HASHED_EMAIL, - pixelId: PIXEL_ID - }, consentData); - getRefererInfoStub.restore(); - - expect(result).to.be.an('object').that.has.all.keys('id', 'callback'); - expect(result.id).to.deep.equal(cookieData); - expect(typeof result.callback).to.equal('function'); - }); - it('Makes an ajax GET request to the production API endpoint with stored puid when id is stale', () => { const last15Days = Date.now() - (60 * 60 * 24 * 1000 * 15); const last29Days = Date.now() - (60 * 60 * 24 * 1000 * 29); diff --git a/test/spec/modules/consentManagementGpp_spec.js b/test/spec/modules/consentManagementGpp_spec.js index 93a876d0233..1170f418caf 100644 --- a/test/spec/modules/consentManagementGpp_spec.js +++ b/test/spec/modules/consentManagementGpp_spec.js @@ -1,23 +1,12 @@ -import { - consentTimeout, - GPPClient, - requestBidsHook, - resetConsentData, - setConsentConfig, - userCMP -} from 'modules/consentManagementGpp.js'; -import {gppDataHandler} from 'src/adapterManager.js'; +import { setConsentConfig, requestBidsHook, resetConsentData, userCMP, consentTimeout, staticConsentData } from 'modules/consentManagementGpp.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'; -import {MODE_CALLBACK, MODE_MIXED} from '../../../libraries/cmp/cmpClient.js'; -import {GreedyPromise} from '../../../src/utils/promise.js'; let expect = require('chai').expect; describe('consentManagementGpp', function () { - beforeEach(resetConsentData); - describe('setConsentConfig tests:', function () { describe('empty setConsentConfig value', function () { beforeEach(function () { @@ -110,7 +99,7 @@ describe('consentManagementGpp', function () { config.resetConfig(); }); - it('results in user settings overriding system defaults', () => { + it('results in user settings overriding system defaults for v2 spec', () => { let staticConfig = { gpp: { cmpApi: 'static', @@ -120,621 +109,22 @@ describe('consentManagementGpp', function () { gppString: 'ABCDEFG1234', gppVersion: 1, sectionId: 3, - sectionList: [], - parsedSections: { - usnat: [ - { - MockUsnatParsedFlag: true - }, - ] - }, + sectionList: [] } } }; setConsentConfig(staticConfig); expect(userCMP).to.be.equal('static'); + expect(consentTimeout).to.be.equal(0); // should always return without a timeout when config is used const consent = gppDataHandler.getConsentData(); expect(consent.gppString).to.eql(staticConfig.gpp.consentData.gppString); - expect(consent.gppData).to.eql(staticConfig.gpp.consentData); - expect(consent.sectionData).to.eql(staticConfig.gpp.sectionData); - }); - }); - }); - describe('GPPClient.ping', () => { - function mkPingData(gppVersion) { - return { - gppVersion - } - } - Object.entries({ - 'unknown': { - expectedMode: MODE_CALLBACK, - pingData: mkPingData(), - apiVersion: '1.1', - client({callback}) { - callback(this.pingData); - } - }, - '1.0': { - expectedMode: MODE_MIXED, - pingData: mkPingData('1.0'), - apiVersion: '1.0', - client() { - return this.pingData; - } - }, - '1.1 that runs callback immediately': { - expectedMode: MODE_CALLBACK, - pingData: mkPingData('1.1'), - apiVersion: '1.1', - client({callback}) { - callback(this.pingData); - } - }, - '1.1 that defers callback': { - expectedMode: MODE_CALLBACK, - pingData: mkPingData('1.1'), - apiVersion: '1.1', - client({callback}) { - setTimeout(() => callback(this.pingData), 10); - } - }, - '> 1.1': { - expectedMode: MODE_CALLBACK, - pingData: mkPingData('1.2'), - apiVersion: '1.1', - client({callback}) { - setTimeout(() => callback(this.pingData), 10); - } - } - }).forEach(([t, scenario]) => { - describe(`using CMP version ${t}`, () => { - let clients, mkClient; - beforeEach(() => { - clients = []; - mkClient = ({mode}) => { - const mockClient = function (args) { - if (args.command === 'ping') { - return Promise.resolve(scenario.client(args)); - } - } - mockClient.mode = mode; - mockClient.close = sinon.stub(); - clients.push(mockClient); - return mockClient; - } - }); - - it('should resolve to client with the correct mode', () => { - return GPPClient.ping(mkClient).then(([client]) => { - expect(client.cmp.mode).to.eql(scenario.expectedMode); - }); - }); - - it('should resolve to pingData', () => { - return GPPClient.ping(mkClient).then(([_, pingData]) => { - expect(pingData).to.eql(scenario.pingData); - }); - }); - - it('should .close the probing client', () => { - return GPPClient.ping(mkClient).then(([client]) => { - sinon.assert.called(clients[0].close); - sinon.assert.notCalled(client.cmp.close); - }) - }); - - it('should .tag the client with version', () => { - return GPPClient.ping(mkClient).then(([client]) => { - expect(client.apiVersion).to.eql(scenario.apiVersion); - }) - }) - }) - }); - - it('should reject when mkClient returns null (CMP not found)', () => { - return GPPClient.ping(() => null).catch((err) => { - expect(err.message).to.match(/not found/); - }); - }); - - it('should reject when client rejects', () => { - const err = {some: 'prop'}; - const mockClient = () => Promise.reject(err); - mockClient.close = sinon.stub(); - return GPPClient.ping(() => mockClient).catch((result) => { - expect(result).to.eql(err); - sinon.assert.called(mockClient.close); - }); - }); - - it('should reject when callback is invoked with success = false', () => { - const err = 'error'; - const mockClient = ({callback}) => callback(err, false); - mockClient.close = sinon.stub(); - return GPPClient.ping(() => mockClient).catch((result) => { - expect(result).to.eql(err); - sinon.assert.called(mockClient.close); - }) - }) - }); - - describe('GPPClient.init', () => { - let makeCmp, cmpCalls, cmpResult; - - beforeEach(() => { - cmpResult = {signalStatus: 'ready', gppString: 'mock-str'}; - cmpCalls = []; - makeCmp = sinon.stub().callsFake(() => { - function mockCmp(args) { - cmpCalls.push(args); - return GreedyPromise.resolve(cmpResult); - } - mockCmp.close = sinon.stub(); - return mockCmp; - }); - }); - - it('should re-use same client', (done) => { - GPPClient.init(makeCmp).then(([client]) => { - GPPClient.init(makeCmp).then(([client2, consentPm]) => { - expect(client2).to.equal(client); - expect(cmpCalls.filter((el) => el.command === 'ping').length).to.equal(2) // recycled client should be refreshed - consentPm.then((consent) => { - expect(consent.gppString).to.eql('mock-str'); - done() - }) - }); - }); - }); - - it('should not re-use errors', (done) => { - cmpResult = GreedyPromise.reject(new Error()); - GPPClient.init(makeCmp).catch(() => { - cmpResult = {signalStatus: 'ready'}; - return GPPClient.init(makeCmp).then(([client]) => { - expect(client).to.exist; - done() - }) - }) - }) - }) - - describe('GPP client', () => { - const CHANGE_EVENTS = ['sectionChange', 'signalStatus']; - - let gppClient, gppData, cmpReady, eventListener; - - function mockClient(apiVersion = '1.1', cmpVersion = '1.1') { - const mockCmp = sinon.stub().callsFake(function ({command, callback}) { - if (command === 'addEventListener') { - eventListener = callback; - } else { - throw new Error('unexpected command: ' + command); - } - }) - const client = new GPPClient(cmpVersion, mockCmp); - client.apiVersion = apiVersion; - client.getGPPData = sinon.stub().callsFake(() => Promise.resolve(gppData)); - client.isCMPReady = sinon.stub().callsFake(() => cmpReady); - client.events = CHANGE_EVENTS; - return client; - } - - beforeEach(() => { - gppDataHandler.reset(); - eventListener = null; - cmpReady = true; - gppData = { - applicableSections: [7], - gppString: 'mock-string', - parsedSections: { - usnat: [ - { - Field: 'val' - }, - { - SubsectionType: 1, - Gpc: false - } - ] - } - }; - gppClient = mockClient(); - }); - - describe('updateConsent', () => { - it('should update data handler with consent data', () => { - return gppClient.updateConsent().then(data => { - sinon.assert.match(data, gppData); - sinon.assert.match(gppDataHandler.getConsentData(), gppData); - expect(gppDataHandler.ready).to.be.true; - }); - }); - - Object.entries({ - 'emtpy': {}, - 'missing': null - }).forEach(([t, data]) => { - it(`should not update, and reject promise, when gpp data is ${t}`, (done) => { - gppData = data; - gppClient.updateConsent().catch(err => { - expect(err.message).to.match(/empty/); - expect(err.args).to.eql(data == null ? [] : [data]); - expect(gppDataHandler.ready).to.be.false; - done() - }) - }); - }) - - it('should not update when gpp data rejects', (done) => { - gppData = Promise.reject(new Error('err')); - gppClient.updateConsent().catch(err => { - expect(gppDataHandler.ready).to.be.false; - expect(err.message).to.eql('err'); - done(); - }) - }); - - describe('consent data validation', () => { - Object.entries({ - applicableSections: { - 'not an array': 'not-an-array', - }, - gppString: { - 'not a string': 234 - }, - parsedSections: { - 'not an object': 'not-an-object' - } - }).forEach(([prop, tests]) => { - describe(`validation: when ${prop} is`, () => { - Object.entries(tests).forEach(([t, value]) => { - describe(t, () => { - it('should not update', (done) => { - Object.assign(gppData, {[prop]: value}); - gppClient.updateConsent().catch(err => { - expect(err.message).to.match(/unexpected/); - expect(err.args).to.eql([gppData]); - expect(gppDataHandler.ready).to.be.false; - done(); - }); - }); - }) - }); - }); - }); - }); - }); - - describe('init', () => { - beforeEach(() => { - gppClient.isCMPReady = function (pingData) { - return pingData.ready; - } - gppClient.getGPPData = function (pingData) { - return Promise.resolve(pingData); - } - }) - - it('does not use initial pingData if CMP is not ready', () => { - gppClient.init({...gppData, ready: false}); - 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, ready: true}).then(data => { - expect(eventListener).to.exist; - sinon.assert.match(data, gppData); - sinon.assert.match(gppDataHandler.getConsentData(), gppData); - }) - }); - - it('rejects promise when CMP errors out', (done) => { - gppClient.init({ready: false}).catch((err) => { - expect(err.message).to.match(/error/); - expect(err.args).to.eql(['error']) - done(); - }); - eventListener('error', false); + expect(consent.fullGppData).to.eql(staticConfig.gpp.consentData); + expect(staticConsentData).to.be.equal(staticConfig.gpp.consentData); }); - - Object.entries({ - 'empty': {}, - 'null': null, - 'irrelevant': {eventName: 'irrelevant'} - }).forEach(([t, evt]) => { - it(`ignores ${t} events`, () => { - let pm = gppClient.init({ready: false}).catch((err) => err.args[0] !== 'done' && Promise.reject(err)); - eventListener(evt); - eventListener('done', false); - return pm; - }) - }); - - it('rejects the promise when cmpStatus is "error"', (done) => { - const evt = {eventName: 'other', pingData: {cmpStatus: 'error'}}; - gppClient.init({ready: false}).catch(err => { - expect(err.message).to.match(/error/); - expect(err.args).to.eql([evt]); - done(); - }); - eventListener(evt); - }) - - CHANGE_EVENTS.forEach(evt => { - describe(`event: ${evt}`, () => { - function makeEvent(pingData) { - return { - eventName: evt, - pingData - } - } - - let gppData2 - beforeEach(() => { - gppData2 = Object.assign(gppData, {gppString: '2nd'}); - }); - - it('does not fire consent data updates if the CMP is not ready', (done) => { - gppClient.init({ready: false}).catch(() => { - expect(gppDataHandler.ready).to.be.false; - done(); - }); - eventListener({...gppData2, ready: false}); - eventListener('done', false); - }) - - it('fires consent data updates (and resolves promise) if CMP is ready', (done) => { - gppClient.init({ready: false}).then(data => { - sinon.assert.match(data, gppData2); - done() - }); - cmpReady = true; - eventListener(makeEvent({...gppData2, ready: true})); - }); - - it('keeps updating consent data on new events', () => { - let pm = gppClient.init({ready: false}).then(data => { - sinon.assert.match(data, gppData); - sinon.assert.match(gppDataHandler.getConsentData(), gppData); - }); - eventListener(makeEvent({...gppData, ready: true})); - return pm.then(() => { - eventListener(makeEvent({...gppData2, ready: true})) - }).then(() => { - sinon.assert.match(gppDataHandler.getConsentData(), gppData2); - }); - }); - }) - }) }); }); - describe('GPP 1.0 protocol', () => { - let mockCmp, gppClient; - beforeEach(() => { - mockCmp = sinon.stub(); - gppClient = new (GPPClient.getClient('1.0'))('1.0', mockCmp); - }); - - describe('isCMPReady', () => { - Object.entries({ - 'loaded': [true, 'loaded'], - 'other': [false, 'other'], - 'undefined': [false, undefined] - }).forEach(([t, [expected, cmpStatus]]) => { - it(`should be ${expected} when cmpStatus is ${t}`, () => { - expect(gppClient.isCMPReady(Object.assign({}, {cmpStatus}))).to.equal(expected); - }); - }); - }); - - describe('getGPPData', () => { - let gppData, pingData; - beforeEach(() => { - gppData = { - gppString: 'mock-string', - supportedAPIs: ['usnat'], - applicableSections: [7, 8] - } - pingData = { - supportedAPIs: gppData.supportedAPIs - }; - }); - - function mockCmpCommands(commands) { - mockCmp.callsFake(({command, parameter}) => { - if (commands.hasOwnProperty((command))) { - return Promise.resolve(commands[command](parameter)); - } else { - return Promise.reject(new Error(`unrecognized command ${command}`)) - } - }) - } - - it('should retrieve consent string and applicableSections', () => { - mockCmpCommands({ - getGPPData: () => gppData - }) - return gppClient.getGPPData(pingData).then(data => { - sinon.assert.match(data, gppData); - }) - }); - - it('should reject when getGPPData rejects', (done) => { - mockCmpCommands({ - getGPPData: () => Promise.reject(new Error('err')) - }); - gppClient.getGPPData(pingData).catch(err => { - expect(err.message).to.eql('err'); - done(); - }); - }); - - it('should not choke if supportedAPIs is missing', () => { - [gppData, pingData].forEach(ob => { delete ob.supportedAPIs; }) - mockCmpCommands({ - getGPPData: () => gppData - }); - return gppClient.getGPPData(pingData).then(res => { - expect(res.gppString).to.eql(gppData.gppString); - expect(res.parsedSections).to.eql({}); - }) - }) - - describe('section data', () => { - let usnat, parsedUsnat; - - function mockSections(sections) { - mockCmpCommands({ - getGPPData: () => gppData, - getSection: (api) => (sections[api]) - }); - }; - - beforeEach(() => { - usnat = { - MockField: 'val', - OtherField: 'o', - Gpc: true - }; - parsedUsnat = [ - { - MockField: 'val', - OtherField: 'o' - }, - { - SubsectionType: 1, - Gpc: true - } - ] - }); - - it('retrieves section data', () => { - mockSections({usnat}); - return gppClient.getGPPData(pingData).then(data => { - expect(data.parsedSections).to.eql({usnat: parsedUsnat}) - }); - }); - - it('does not choke if a section is missing', () => { - mockSections({usnat}); - gppData.supportedAPIs = ['usnat', 'missing']; - return gppClient.getGPPData(pingData).then(data => { - expect(data.parsedSections).to.eql({usnat: parsedUsnat}); - }) - }); - - it('does not choke if a section fails', () => { - mockSections({usnat, err: Promise.reject(new Error('err'))}); - gppData.supportedAPIs = ['usnat', 'err']; - return gppClient.getGPPData(pingData).then(data => { - expect(data.parsedSections).to.eql({usnat: parsedUsnat}); - }) - }); - }) - }); - }); - - describe('GPP 1.1 protocol', () => { - let mockCmp, gppClient; - beforeEach(() => { - mockCmp = sinon.stub(); - gppClient = new (GPPClient.getClient('1.1'))('1.1', mockCmp); - }); - - describe('isCMPReady', () => { - Object.entries({ - 'ready': [true, 'ready'], - 'not ready': [false, 'not ready'], - 'undefined': [false, undefined] - }).forEach(([t, [expected, signalStatus]]) => { - it(`should be ${expected} when signalStatus is ${t}`, () => { - expect(gppClient.isCMPReady(Object.assign({}, {signalStatus}))).to.equal(expected); - }); - }); - }); - - it('gets GPPData from pingData', () => { - mockCmp.throws(new Error()); - const pingData = { - 'gppVersion': '1.1', - 'cmpStatus': 'loaded', - 'cmpDisplayStatus': 'disabled', - 'supportedAPIs': [ - '5:tcfcav1', - '7:usnat', - '8:usca', - '9:usva', - '10:usco', - '11:usut', - '12:usct' - ], - 'signalStatus': 'ready', - 'cmpId': 31, - 'sectionList': [ - 7 - ], - 'applicableSections': [ - 7 - ], - 'gppString': 'DBABL~BAAAAAAAAgA.QA', - 'parsedSections': { - 'usnat': [ - { - 'Version': 1, - 'SharingNotice': 0, - 'SaleOptOutNotice': 0, - 'SharingOptOutNotice': 0, - 'TargetedAdvertisingOptOutNotice': 0, - 'SensitiveDataProcessingOptOutNotice': 0, - 'SensitiveDataLimitUseNotice': 0, - 'SaleOptOut': 0, - 'SharingOptOut': 0, - 'TargetedAdvertisingOptOut': 0, - 'SensitiveDataProcessing': [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0 - ], - 'KnownChildSensitiveDataConsents': [ - 0, - 0 - ], - 'PersonalDataConsents': 0, - 'MspaCoveredTransaction': 2, - 'MspaOptOutOptionMode': 0, - 'MspaServiceProviderMode': 0 - }, - { - 'SubsectionType': 1, - 'Gpc': false - } - ] - } - }; - return gppClient.getGPPData(pingData).then((gppData) => { - sinon.assert.match(gppData, { - gppString: pingData.gppString, - applicableSections: pingData.applicableSections, - parsedSections: pingData.parsedSections - }) - }) - }) - }) - describe('requestBidsHook tests:', function () { let goodConfig = { gpp: { @@ -838,13 +228,13 @@ describe('consentManagementGpp', function () { }); it('should continue the auction immediately, without consent data, if timeout is 0', (done) => { - window.__gpp = function () {}; setConsentConfig({ gpp: { cmpApi: 'iab', timeout: 0 } }); + window.__gpp = function () {}; try { requestBidsHook(() => { const consent = gppDataHandler.getConsentData(); @@ -861,16 +251,14 @@ describe('consentManagementGpp', function () { describe('already known consentData:', function () { let cmpStub = sinon.stub(); - function mockCMP(pingData) { - return function (command, callback) { - switch (command) { - case 'addEventListener': - // eslint-disable-next-line standard/no-callback-literal - callback({eventName: 'sectionChange', pingData}) - break; - case 'ping': - callback(pingData) - break; + function mockCMP(cmpResponse) { + return function (...args) { + if (args[0] === 'addEventListener') { + args[1](({ + eventName: 'sectionChange' + })); + } else if (args[0] === 'getGPPData') { + return cmpResponse; } } } @@ -893,7 +281,7 @@ describe('consentManagementGpp', function () { gppString: 'xyz', }; - cmpStub = sinon.stub(window, '__gpp').callsFake(mockCMP({...testConsentData, signalStatus: 'ready'})); + cmpStub = sinon.stub(window, '__gpp').callsFake(mockCMP(testConsentData)); setConsentConfig(goodConfig); requestBidsHook(() => {}, {}); cmpStub.reset(); @@ -909,5 +297,281 @@ describe('consentManagementGpp', function () { sinon.assert.notCalled(cmpStub); }); }); + + describe('iframe tests', function () { + let cmpPostMessageCb = () => {}; + let stringifyResponse; + + function createIFrameMarker(frameName) { + let ifr = document.createElement('iframe'); + ifr.width = 0; + ifr.height = 0; + ifr.name = frameName; + document.body.appendChild(ifr); + return ifr; + } + + function creatCmpMessageHandler(prefix, returnEvtValue, returnGPPValue) { + return function (event) { + if (event && event.data) { + let data = event.data; + if (data[`${prefix}Call`]) { + let callId = data[`${prefix}Call`].callId; + let response; + if (data[`${prefix}Call`].command === 'addEventListener') { + response = { + [`${prefix}Return`]: { + callId, + returnValue: returnEvtValue, + success: true + } + } + } else if (data[`${prefix}Call`].command === 'getGPPData') { + response = { + [`${prefix}Return`]: { + callId, + returnValue: returnGPPValue, + success: true + } + } + } + event.source.postMessage(stringifyResponse ? JSON.stringify(response) : response, '*'); + } + } + } + } + + function testIFramedPage(testName, messageFormatString, tarConsentString, tarSections) { + it(`should return the consent string from a postmessage + addEventListener response - ${testName}`, (done) => { + stringifyResponse = messageFormatString; + setConsentConfig(goodConfig); + requestBidsHook(() => { + let consent = gppDataHandler.getConsentData(); + sinon.assert.notCalled(utils.logError); + expect(consent.gppString).to.equal(tarConsentString); + expect(consent.applicableSections).to.deep.equal(tarSections); + done(); + }, {}); + }); + } + + beforeEach(function () { + sinon.stub(utils, 'logError'); + sinon.stub(utils, 'logWarn'); + }); + + afterEach(function () { + utils.logError.restore(); + utils.logWarn.restore(); + config.resetConfig(); + resetConsentData(); + }); + + describe('v2 CMP workflow for iframe pages:', function () { + stringifyResponse = false; + let ifr2 = null; + + beforeEach(function () { + ifr2 = createIFrameMarker('__gppLocator'); + cmpPostMessageCb = creatCmpMessageHandler('__gpp', { + eventName: 'sectionChange' + }, { + gppString: 'abc12345234', + applicableSections: [7] + }); + window.addEventListener('message', cmpPostMessageCb, false); + }); + + afterEach(function () { + delete window.__gpp; // deletes the local copy made by the postMessage CMP call function + document.body.removeChild(ifr2); + window.removeEventListener('message', cmpPostMessageCb); + }); + + testIFramedPage('with/JSON response', false, 'abc12345234', [7]); + testIFramedPage('with/String response', true, 'abc12345234', [7]); + }); + }); + + describe('direct calls to CMP API tests', function () { + let cmpStub = sinon.stub(); + + beforeEach(function () { + didHookReturn = false; + sinon.stub(utils, 'logError'); + sinon.stub(utils, 'logWarn'); + }); + + afterEach(function () { + config.resetConfig(); + cmpStub.restore(); + utils.logError.restore(); + utils.logWarn.restore(); + resetConsentData(); + }); + + describe('v2 CMP workflow for normal pages:', function () { + beforeEach(function () { + window.__gpp = function () {}; + }); + + afterEach(function () { + delete window.__gpp; + }); + + it('performs lookup check and stores consentData for a valid existing user', function () { + let testConsentData = { + gppString: 'abc12345234', + applicableSections: [7] + }; + cmpStub = sinon.stub(window, '__gpp').callsFake((...args) => { + if (args[0] === 'addEventListener') { + args[1]({ + eventName: 'sectionChange' + }); + } else if (args[0] === 'getGPPData') { + return testConsentData; + } + }); + + setConsentConfig(goodConfig); + + requestBidsHook(() => { + didHookReturn = true; + }, {}); + let consent = gppDataHandler.getConsentData(); + sinon.assert.notCalled(utils.logError); + expect(didHookReturn).to.be.true; + expect(consent.gppString).to.equal(testConsentData.gppString); + expect(consent.applicableSections).to.deep.equal(testConsentData.applicableSections); + }); + + it('produces gdpr metadata', function () { + let testConsentData = { + gppString: 'abc12345234', + applicableSections: [7] + }; + cmpStub = sinon.stub(window, '__gpp').callsFake((...args) => { + if (args[0] === 'addEventListener') { + args[1]({ + eventName: 'sectionChange' + }); + } else if (args[0] === 'getGPPData') { + return testConsentData; + } + }); + + setConsentConfig(goodConfig); + + requestBidsHook(() => { + didHookReturn = true; + }, {}); + let consentMeta = gppDataHandler.getConsentMeta(); + sinon.assert.notCalled(utils.logError); + expect(consentMeta.generatedAt).to.be.above(1644367751709); + }); + + it('throws an error when processCmpData check fails + does not call requestBids callback', function () { + let testConsentData = {}; + let bidsBackHandlerReturn = false; + + cmpStub = sinon.stub(window, '__gpp').callsFake((...args) => { + if (args[0] === 'addEventListener') { + args[1]({ + eventName: 'sectionChange' + }); + } else if (args[0] === 'getGPPData') { + return testConsentData; + } + }); + + setConsentConfig(goodConfig); + + sinon.assert.notCalled(utils.logWarn); + sinon.assert.notCalled(utils.logError); + + [utils.logWarn, utils.logError].forEach((stub) => stub.reset()); + + requestBidsHook(() => { + didHookReturn = true; + }, { + bidsBackHandler: () => bidsBackHandlerReturn = true + }); + let consent = gppDataHandler.getConsentData(); + + sinon.assert.calledOnce(utils.logError); + sinon.assert.notCalled(utils.logWarn); + expect(didHookReturn).to.be.false; + expect(bidsBackHandlerReturn).to.be.true; + expect(consent).to.be.null; + expect(gppDataHandler.ready).to.be.true; + }); + + describe('when proper consent is not available', () => { + let gppStub; + + function runAuction() { + setConsentConfig({ + gpp: { + cmpApi: 'iab', + timeout: 10, + } + }); + return new Promise((resolve, reject) => { + requestBidsHook(() => { + didHookReturn = true; + }, {}); + setTimeout(() => didHookReturn ? resolve() : reject(new Error('Auction did not run')), 20); + }) + } + + function mockGppCmp(gppdata) { + gppStub.callsFake((api, cb) => { + if (api === 'addEventListener') { + // eslint-disable-next-line standard/no-callback-literal + cb({ + pingData: { + cmpStatus: 'loaded' + } + }, true); + } + if (api === 'getGPPData') { + return gppdata; + } + }); + } + + beforeEach(() => { + gppStub = sinon.stub(window, '__gpp'); + }); + + afterEach(() => { + gppStub.restore(); + }) + + it('should continue auction with null consent when CMP is unresponsive', () => { + return runAuction().then(() => { + const consent = gppDataHandler.getConsentData(); + expect(consent.applicableSections).to.deep.equal([]); + expect(consent.gppString).to.be.undefined; + expect(gppDataHandler.ready).to.be.true; + }); + }); + + it('should use consent provided by events other than sectionChange', () => { + mockGppCmp({ + gppString: 'mock-consent-string', + applicableSections: [7] + }); + return runAuction().then(() => { + const consent = gppDataHandler.getConsentData(); + expect(consent.applicableSections).to.deep.equal([7]); + expect(consent.gppString).to.equal('mock-consent-string'); + expect(gppDataHandler.ready).to.be.true; + }); + }); + }); + }); + }); }); }); diff --git a/test/spec/modules/consentManagementUsp_spec.js b/test/spec/modules/consentManagementUsp_spec.js index c372c66f7f0..e98486754ab 100644 --- a/test/spec/modules/consentManagementUsp_spec.js +++ b/test/spec/modules/consentManagementUsp_spec.js @@ -522,19 +522,6 @@ describe('consentManagement', function () { setConsentConfig(goodConfig); expect(uspDataHandler.getConsentData()).to.eql('string'); }); - - it('does not invoke registerDeletion if the CMP calls back with an error', () => { - sandbox.stub(window, '__uspapi').callsFake((cmd, _, cb) => { - if (cmd === 'registerDeletion') { - cb(null, false); - } else { - // eslint-disable-next-line standard/no-callback-literal - cb({uspString: 'string'}, true); - } - }); - setConsentConfig(goodConfig); - sinon.assert.notCalled(adapterManager.callDataDeletionRequest); - }) }); }); }); diff --git a/test/spec/modules/consentManagement_spec.js b/test/spec/modules/consentManagement_spec.js index c1ed042a2c8..2cd3d011e1d 100644 --- a/test/spec/modules/consentManagement_spec.js +++ b/test/spec/modules/consentManagement_spec.js @@ -1,17 +1,18 @@ import { - actionTimeout, - consentTimeout, - gdprScope, - loadConsentData, + setConsentConfig, requestBidsHook, resetConsentData, - setConsentConfig, + userCMP, + consentTimeout, + actionTimeout, staticConsentData, - userCMP + gdprScope, + loadConsentData, + setActionTimeout } from 'modules/consentManagement.js'; -import {gdprDataHandler} from 'src/adapterManager.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'; let expect = require('chai').expect; @@ -486,6 +487,15 @@ describe('consentManagement', function () { testIFramedPage('with/JSON response', false, 'abc12345234', 2); testIFramedPage('with/String response', true, 'abc12345234', 2); + + it('should contain correct v2 CMP definition', (done) => { + setConsentConfig(goodConfig); + requestBidsHook(() => { + const nbArguments = window.__tcfapi.toString().split('\n')[0].split(', ').length; + expect(nbArguments).to.equal(4); + done(); + }, {}); + }); }); }); diff --git a/test/spec/modules/consumableBidAdapter_spec.js b/test/spec/modules/consumableBidAdapter_spec.js index d8e75454245..556dce447b9 100644 --- a/test/spec/modules/consumableBidAdapter_spec.js +++ b/test/spec/modules/consumableBidAdapter_spec.js @@ -53,10 +53,6 @@ const BIDDER_REQUEST_1 = { consentString: 'consent-test', gdprApplies: false }, - gppConsent: { - applicableSections: [1, 2], - gppString: 'consent-string' - }, refererInfo: { referer: 'http://example.com/page.html', reachedTop: true, @@ -651,30 +647,10 @@ describe('Consumable BidAdapter', function () { expect(opts[0].url).to.equal('https://sync.serverbid.com/ss/730181.html?gdpr=0&gdpr_consent=GDPR_CONSENT_STRING'); }) - it('should return a sync url if iframe syncs are enabled and has GPP consent with applicable sections', function () { - let gppConsent = { - applicableSections: [1, 2], - gppString: 'GPP_CONSENT_STRING' - } - let opts = spec.getUserSyncs(syncOptions, [AD_SERVER_RESPONSE], {}, '', gppConsent); - - expect(opts.length).to.equal(1); - expect(opts[0].url).to.equal('https://sync.serverbid.com/ss/730181.html?gpp=GPP_CONSENT_STRING&gpp_sid=1%2C2'); - }) - - it('should return a sync url if iframe syncs are enabled and has GPP consent without applicable sections', function () { - let gppConsent = { - applicableSections: [], - gppString: 'GPP_CONSENT_STRING' - } - let opts = spec.getUserSyncs(syncOptions, [AD_SERVER_RESPONSE], {}, '', gppConsent); - - expect(opts.length).to.equal(1); - expect(opts[0].url).to.equal('https://sync.serverbid.com/ss/730181.html?gpp=GPP_CONSENT_STRING'); - }) - it('should return a sync url if iframe syncs are enabled and USP applies', function () { - let uspConsent = 'USP_CONSENT_STRING'; + let uspConsent = { + consentString: 'USP_CONSENT_STRING', + } let opts = spec.getUserSyncs(syncOptions, [AD_SERVER_RESPONSE], {}, uspConsent); expect(opts.length).to.equal(1); @@ -686,7 +662,9 @@ describe('Consumable BidAdapter', function () { consentString: 'GDPR_CONSENT_STRING', gdprApplies: true, } - let uspConsent = 'USP_CONSENT_STRING'; + let uspConsent = { + consentString: 'USP_CONSENT_STRING', + } let opts = spec.getUserSyncs(syncOptions, [AD_SERVER_RESPONSE], gdprConsent, uspConsent); expect(opts.length).to.equal(1); @@ -711,22 +689,50 @@ describe('Consumable BidAdapter', function () { sandbox.restore(); }); - it('Request should have EIDs', function() { + it('Request should have unifiedId config params', function() { bidderRequest.bidRequest[0].userId = {}; bidderRequest.bidRequest[0].userId.tdid = 'TTD_ID'; - bidderRequest.bidRequest[0].userIdAsEids = [{ + bidderRequest.bidRequest[0].userIdAsEids = createEidsArray(bidderRequest.bidRequest[0].userId); + let request = spec.buildRequests(bidderRequest.bidRequest, BIDDER_REQUEST_1); + let data = JSON.parse(request.data); + expect(data.user.eids).to.deep.equal([{ 'source': 'adserver.org', 'uids': [{ - 'id': 'TTD_ID_FROM_USER_ID_MODULE', + 'id': 'TTD_ID', 'atype': 1, 'ext': { 'rtiPartner': 'TDID' } }] - }]; + }]); + }); + + it('Request should have adsrvrOrgId from UserId Module if config and userId module both have TTD ID', function() { + sandbox.stub(config, 'getConfig').callsFake((key) => { + var config = { + adsrvrOrgId: { + 'TDID': 'TTD_ID_FROM_CONFIG', + 'TDID_LOOKUP': 'TRUE', + 'TDID_CREATED_AT': '2022-06-21T09:47:00' + } + }; + return config[key]; + }); + bidderRequest.bidRequest[0].userId = {}; + bidderRequest.bidRequest[0].userId.tdid = 'TTD_ID'; + bidderRequest.bidRequest[0].userIdAsEids = createEidsArray(bidderRequest.bidRequest[0].userId); let request = spec.buildRequests(bidderRequest.bidRequest, BIDDER_REQUEST_1); let data = JSON.parse(request.data); - expect(data.user.eids).to.deep.equal(bidderRequest.bidRequest[0].userIdAsEids); + expect(data.user.eids).to.deep.equal([{ + 'source': 'adserver.org', + 'uids': [{ + 'id': 'TTD_ID', + 'atype': 1, + 'ext': { + 'rtiPartner': 'TDID' + } + }] + }]); }); it('Request should NOT have adsrvrOrgId params if userId is NOT object', function() { diff --git a/test/spec/modules/contxtfulRtdProvider_spec.js b/test/spec/modules/contxtfulRtdProvider_spec.js deleted file mode 100644 index 541c0e6e6dd..00000000000 --- a/test/spec/modules/contxtfulRtdProvider_spec.js +++ /dev/null @@ -1,200 +0,0 @@ -import { contxtfulSubmodule } from '../../../modules/contxtfulRtdProvider.js'; -import { expect } from 'chai'; -import { loadExternalScriptStub } from 'test/mocks/adloaderStub.js'; - -import * as events from '../../../src/events'; - -const _ = null; -const VERSION = 'v1'; -const CUSTOMER = 'CUSTOMER'; -const CONTXTFUL_CONNECTOR_ENDPOINT = `https://api.receptivity.io/${VERSION}/prebid/${CUSTOMER}/connector/p.js`; -const INITIAL_RECEPTIVITY = { ReceptivityState: 'INITIAL_RECEPTIVITY' }; -const INITIAL_RECEPTIVITY_EVENT = new CustomEvent('initialReceptivity', { detail: INITIAL_RECEPTIVITY }); - -const CONTXTFUL_API = { GetReceptivity: sinon.stub() } -const RX_ENGINE_IS_READY_EVENT = new CustomEvent('rxEngineIsReady', {detail: CONTXTFUL_API}); - -function buildInitConfig(version, customer) { - return { - name: 'contxtful', - params: { - version, - customer, - }, - }; -} - -describe('contxtfulRtdProvider', function () { - let sandbox = sinon.sandbox.create(); - let loadExternalScriptTag; - let eventsEmitSpy; - - beforeEach(() => { - loadExternalScriptTag = document.createElement('script'); - loadExternalScriptStub.callsFake((_url, _moduleName) => loadExternalScriptTag); - - CONTXTFUL_API.GetReceptivity.reset(); - - eventsEmitSpy = sandbox.spy(events, ['emit']); - }); - - afterEach(function () { - delete window.Contxtful; - sandbox.restore(); - }); - - describe('extractParameters with invalid configuration', () => { - const { - params: { customer, version }, - } = buildInitConfig(VERSION, CUSTOMER); - const theories = [ - [ - null, - 'params.version should be a non-empty string', - 'null object for config', - ], - [ - {}, - 'params.version should be a non-empty string', - 'empty object for config', - ], - [ - { customer }, - 'params.version should be a non-empty string', - 'customer only in config', - ], - [ - { version }, - 'params.customer should be a non-empty string', - 'version only in config', - ], - [ - { customer, version: '' }, - 'params.version should be a non-empty string', - 'empty string for version', - ], - [ - { customer: '', version }, - 'params.customer should be a non-empty string', - 'empty string for customer', - ], - [ - { customer: '', version: '' }, - 'params.version should be a non-empty string', - 'empty string for version & customer', - ], - ]; - - theories.forEach(([params, expectedErrorMessage, _description]) => { - const config = { name: 'contxtful', params }; - it('throws the expected error', () => { - expect(() => contxtfulSubmodule.extractParameters(config)).to.throw( - expectedErrorMessage - ); - }); - }); - }); - - describe('initialization with invalid config', function () { - it('returns false', () => { - expect(contxtfulSubmodule.init({})).to.be.false; - }); - }); - - describe('initialization with valid config', function () { - it('returns true when initializing', () => { - const config = buildInitConfig(VERSION, CUSTOMER); - expect(contxtfulSubmodule.init(config)).to.be.true; - }); - - it('loads contxtful module script asynchronously', (done) => { - contxtfulSubmodule.init(buildInitConfig(VERSION, CUSTOMER)); - - setTimeout(() => { - expect(loadExternalScriptStub.calledOnce).to.be.true; - expect(loadExternalScriptStub.args[0][0]).to.equal( - CONTXTFUL_CONNECTOR_ENDPOINT - ); - done(); - }, 10); - }); - }); - - describe('load external script return falsy', function () { - it('returns true when initializing', () => { - loadExternalScriptStub.callsFake(() => {}); - const config = buildInitConfig(VERSION, CUSTOMER); - expect(contxtfulSubmodule.init(config)).to.be.true; - }); - }); - - describe('rxEngine from external script', function () { - it('use rxEngine api to get receptivity', () => { - contxtfulSubmodule.init(buildInitConfig(VERSION, CUSTOMER)); - loadExternalScriptTag.dispatchEvent(RX_ENGINE_IS_READY_EVENT); - - contxtfulSubmodule.getTargetingData(['ad-slot']); - - expect(CONTXTFUL_API.GetReceptivity.calledOnce).to.be.true; - }); - }); - - describe('initial receptivity is not dispatched', function () { - it('does not initialize receptivity value', () => { - contxtfulSubmodule.init(buildInitConfig(VERSION, CUSTOMER)); - - let targetingData = contxtfulSubmodule.getTargetingData(['ad-slot']); - expect(targetingData).to.deep.equal({}); - }); - }); - - describe('initial receptivity is invalid', function () { - const theories = [ - [new Event('initialReceptivity'), 'event without details'], - [new CustomEvent('initialReceptivity', { }), 'custom event without details'], - [new CustomEvent('initialReceptivity', { detail: {} }), 'custom event with invalid details'], - [new CustomEvent('initialReceptivity', { detail: { ReceptivityState: '' } }), 'custom event with details without ReceptivityState'], - ]; - - theories.forEach(([initialReceptivityEvent, _description]) => { - it('does not initialize receptivity value', () => { - contxtfulSubmodule.init(buildInitConfig(VERSION, CUSTOMER)); - loadExternalScriptTag.dispatchEvent(initialReceptivityEvent); - - let targetingData = contxtfulSubmodule.getTargetingData(['ad-slot']); - expect(targetingData).to.deep.equal({}); - }); - }) - }); - - describe('getTargetingData', function () { - const theories = [ - [undefined, {}, 'undefined ad-slots'], - [[], {}, 'empty ad-slots'], - [ - ['ad-slot'], - { 'ad-slot': { ReceptivityState: 'INITIAL_RECEPTIVITY' } }, - 'single ad-slot', - ], - [ - ['ad-slot-1', 'ad-slot-2'], - { - 'ad-slot-1': { ReceptivityState: 'INITIAL_RECEPTIVITY' }, - 'ad-slot-2': { ReceptivityState: 'INITIAL_RECEPTIVITY' }, - }, - 'many ad-slots', - ], - ]; - - theories.forEach(([adUnits, expected, _description]) => { - it('adds "ReceptivityState" to the adUnits', function () { - contxtfulSubmodule.init(buildInitConfig(VERSION, CUSTOMER)); - loadExternalScriptTag.dispatchEvent(INITIAL_RECEPTIVITY_EVENT); - - expect(contxtfulSubmodule.getTargetingData(adUnits)).to.deep.equal( - expected - ); - }); - }); - }); -}); diff --git a/test/spec/modules/conversantAnalyticsAdapter_spec.js b/test/spec/modules/conversantAnalyticsAdapter_spec.js index f425535ce73..ce134f7f6af 100644 --- a/test/spec/modules/conversantAnalyticsAdapter_spec.js +++ b/test/spec/modules/conversantAnalyticsAdapter_spec.js @@ -3,7 +3,6 @@ import {expect} from 'chai'; import {default as conversantAnalytics, CNVR_CONSTANTS, cnvrHelper} from 'modules/conversantAnalyticsAdapter'; import * as utils from 'src/utils.js'; import * as prebidGlobal from 'src/prebidGlobal'; -import {server} from '../../mocks/xhr.js'; import constants from 'src/constants.json' @@ -11,13 +10,14 @@ let events = require('src/events'); describe('Conversant analytics adapter tests', function() { let sandbox; // sinon sandbox to make restoring all stubbed objects easier + let xhr; // xhr stub from sinon for capturing data sent via ajax let clock; // clock stub from sinon to mock our cache cleanup interval let logInfoStub; const PREBID_VERSION = '1.2'; const SITE_ID = 108060; - let requests; + let requests = []; const DATESTAMP = Date.now(); const VALID_CONFIGURATION = { @@ -36,9 +36,10 @@ describe('Conversant analytics adapter tests', function() { }; beforeEach(function () { - requests = server.requests; sandbox = sinon.sandbox.create(); sandbox.stub(events, 'getEvents').returns([]); // need to stub this otherwise unwanted events seem to get fired during testing + xhr = sandbox.useFakeXMLHttpRequest(); // allows us to capture ajax requests + xhr.onCreate = function (req) { requests.push(req); }; // save ajax requests in a private array for testing purposes let getGlobalStub = { version: PREBID_VERSION, getUserIds: function() { // userIdTargeting.js init() gets called on AUCTION_END so we need to mock this function. @@ -59,6 +60,7 @@ describe('Conversant analytics adapter tests', function() { afterEach(function () { sandbox.restore(); + requests = []; // clean up any requests in our ajax request capture array. conversantAnalytics.disableAnalytics(); }); diff --git a/test/spec/modules/conversantBidAdapter_spec.js b/test/spec/modules/conversantBidAdapter_spec.js index 73d1978a9d9..59ebefa2d60 100644 --- a/test/spec/modules/conversantBidAdapter_spec.js +++ b/test/spec/modules/conversantBidAdapter_spec.js @@ -2,20 +2,13 @@ import {expect} from 'chai'; import {spec, storage} from 'modules/conversantBidAdapter.js'; import * as utils from 'src/utils.js'; import {createEidsArray} from 'modules/userId/eids.js'; +import { config } from '../../../src/config.js'; import {deepAccess} from 'src/utils'; -// load modules that register ORTB processors -import 'src/prebid.js' -import 'modules/currency.js'; -import 'modules/userId/index.js'; // handles eids -import 'modules/priceFloors.js'; -import 'modules/consentManagement.js'; -import 'modules/consentManagementUsp.js'; -import 'modules/schain.js'; // handles schain -import {hook} from '../../../src/hook.js' describe('Conversant adapter tests', function() { const siteId = '108060'; const versionPattern = /^\d+\.\d+\.\d+(.)*$/; + const bidRequests = [ // banner with single size { @@ -26,18 +19,13 @@ describe('Conversant adapter tests', function() { tag_id: 'tagid-1', bidfloor: 0.5 }, - mediaTypes: { - banner: { - sizes: [[300, 250]], - } - }, placementCode: 'pcode000', transactionId: 'tx000', + sizes: [[300, 250]], bidId: 'bid000', bidderRequestId: '117d765b87bed38', auctionId: 'req000' }, - // banner with sizes in mediaTypes.banner.sizes { bidder: 'conversant', @@ -63,13 +51,9 @@ describe('Conversant adapter tests', function() { position: 2, tag_id: '' }, - mediaTypes: { - banner: { - sizes: [[300, 600], [160, 600]], - } - }, placementCode: 'pcode002', transactionId: 'tx002', + sizes: [[300, 600], [160, 600]], bidId: 'bid002', bidderRequestId: '117d765b87bed38', auctionId: 'req000' @@ -93,6 +77,7 @@ describe('Conversant adapter tests', function() { }, placementCode: 'pcode003', transactionId: 'tx003', + sizes: [640, 480], bidId: 'bid003', bidderRequestId: '117d765b87bed38', auctionId: 'req000' @@ -140,15 +125,16 @@ describe('Conversant adapter tests', function() { bidderRequestId: '117d765b87bed38', auctionId: 'req000' }, - // banner with first party data + // video with first party data { bidder: 'conversant', params: { site_id: siteId }, mediaTypes: { - banner: { - sizes: [[300, 600], [160, 600]], + video: { + context: 'instream', + mimes: ['video/mp4', 'video/x-flv'] } }, ortb2Imp: { @@ -164,6 +150,23 @@ describe('Conversant adapter tests', function() { bidId: 'bid006', bidderRequestId: '117d765b87bed38', auctionId: 'req000' + }, + { + bidder: 'conversant', + params: { + site_id: siteId + }, + mediaTypes: { + banner: { + sizes: [[728, 90], [468, 60]], + pos: 5 + } + }, + placementCode: 'pcode001', + transactionId: 'tx001', + bidId: 'bid007', + bidderRequestId: '117d765b87bed38', + auctionId: 'req000' } ]; @@ -214,14 +217,7 @@ describe('Conversant adapter tests', function() { }] }] }, - headers: {} - }; - - before(() => { - // ortbConverter depends on other modules to be setup to work as expected so run hook.ready to register some - // submodules so functions like setOrtbSourceExtSchain and setOrtbUserExtEids are available - hook.ready(); - }); + headers: {}}; it('Verify basic properties', function() { expect(spec.code).to.equal('conversant'); @@ -236,9 +232,12 @@ describe('Conversant adapter tests', function() { expect(spec.isBidRequestValid({})).to.be.false; 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; - }); + expect(spec.isBidRequestValid(bidRequests[0])).to.be.true; + expect(spec.isBidRequestValid(bidRequests[1])).to.be.true; + expect(spec.isBidRequestValid(bidRequests[2])).to.be.true; + expect(spec.isBidRequestValid(bidRequests[3])).to.be.true; + expect(spec.isBidRequestValid(bidRequests[4])).to.be.true; + expect(spec.isBidRequestValid(bidRequests[5])).to.be.true; const simpleVideo = JSON.parse(JSON.stringify(bidRequests[3])); simpleVideo.params.site_id = 123; @@ -252,171 +251,152 @@ describe('Conversant adapter tests', function() { expect(spec.isBidRequestValid(simpleVideo)).to.be.true; }); - describe('Verify buildRequest', function() { - let page, bidderRequest, request, payload; - before(() => { - page = 'http://test.com?a=b&c=123'; - // ortbConverter uses the site/device information from the ortb2 object passed in the bidderRequest object - bidderRequest = { - refererInfo: { - page: page - }, - ortb2: { - source: { - tid: 'tid000' - }, - site: { - mobile: 0, - page: page, - }, - device: { - w: screen.width, - h: screen.height, - dnt: 0, - ua: navigator.userAgent - } + it('Verify buildRequest', function() { + const page = 'http://test.com?a=b&c=123'; + const bidderRequest = { + refererInfo: { + page: page + }, + ortb2: { + source: { + tid: 'tid000' } - }; - request = spec.buildRequests(bidRequests, bidderRequest); - payload = request.data; - }); - - it('Verify common elements', function() { - expect(request.method).to.equal('POST'); - expect(request.url).to.equal('https://web.hb.ad.cpe.dotomi.com/cvx/client/hb/ortb/25'); - - expect(payload).to.have.property('id'); - expect(payload.source).to.have.property('tid', 'tid000'); - expect(payload).to.have.property('at', 1); - expect(payload).to.have.property('imp'); - expect(payload.imp).to.be.an('array').with.lengthOf(bidRequests.length); - - expect(payload).to.have.property('site'); - expect(payload.site).to.have.property('id', siteId); - expect(payload.site).to.have.property('mobile').that.is.oneOf([0, 1]); - - expect(payload.site).to.have.property('page', page); - - expect(payload).to.have.property('device'); - expect(payload.device).to.have.property('w', screen.width); - expect(payload.device).to.have.property('h', screen.height); - expect(payload.device).to.have.property('dnt').that.is.oneOf([0, 1]); - expect(payload.device).to.have.property('ua', navigator.userAgent); - - expect(payload).to.not.have.property('user'); // there should be no user by default - expect(payload).to.not.have.property('tmax'); // there should be no user by default - }); - - it('Simple banner', () => { - expect(payload.imp[0]).to.have.property('id', 'bid000'); - expect(payload.imp[0]).to.have.property('secure', 1); - expect(payload.imp[0]).to.have.property('bidfloor', 0.5); - expect(payload.imp[0]).to.have.property('displaymanager', 'Prebid.js'); - expect(payload.imp[0]).to.have.property('displaymanagerver').that.matches(versionPattern); - expect(payload.imp[0]).to.have.property('tagid', 'tagid-1'); - expect(payload.imp[0]).to.have.property('banner'); - expect(payload.imp[0].banner).to.have.property('pos', 1); - 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]).to.not.have.property('video'); - }); - - it('Banner multiple sizes', () => { - expect(payload.imp[1]).to.have.property('id', 'bid001'); - expect(payload.imp[1]).to.have.property('secure', 1); - expect(payload.imp[1]).to.have.property('bidfloor', 0); - expect(payload.imp[1]).to.have.property('displaymanager', 'Prebid.js'); - expect(payload.imp[1]).to.have.property('displaymanagerver').that.matches(versionPattern); - expect(payload.imp[1]).to.not.have.property('tagid'); - expect(payload.imp[1]).to.have.property('banner'); - expect(payload.imp[1].banner).to.not.have.property('pos'); - expect(payload.imp[1].banner).to.have.property('format'); - expect(payload.imp[1].banner.format).to.deep.equal([{w: 728, h: 90}, {w: 468, h: 60}]); - }); - - it('Banner with tagid and position', () => { - expect(payload.imp[2]).to.have.property('id', 'bid002'); - expect(payload.imp[2]).to.have.property('secure', 1); - expect(payload.imp[2]).to.have.property('bidfloor', 0); - expect(payload.imp[2]).to.have.property('displaymanager', 'Prebid.js'); - expect(payload.imp[2]).to.have.property('displaymanagerver').that.matches(versionPattern); - expect(payload.imp[2]).to.have.property('banner'); - expect(payload.imp[2].banner).to.have.property('pos', 2); - expect(payload.imp[2].banner).to.have.property('format'); - expect(payload.imp[2].banner.format).to.deep.equal([{w: 300, h: 600}, {w: 160, h: 600}]); - }); - - if (FEATURES.VIDEO) { - it('Simple video', () => { - expect(payload.imp[3]).to.have.property('id', 'bid003'); - expect(payload.imp[3]).to.have.property('secure', 1); - expect(payload.imp[3]).to.have.property('bidfloor', 0); - expect(payload.imp[3]).to.have.property('displaymanager', 'Prebid.js'); - expect(payload.imp[3]).to.have.property('displaymanagerver').that.matches(versionPattern); - expect(payload.imp[3]).to.not.have.property('tagid'); - expect(payload.imp[3]).to.have.property('video'); - expect(payload.imp[3].video).to.have.property('pos', 3); - expect(payload.imp[3].video).to.have.property('w', 632); - expect(payload.imp[3].video).to.have.property('h', 499); - expect(payload.imp[3].video).to.have.property('mimes'); - expect(payload.imp[3].video.mimes).to.deep.equal(['video/mp4', 'video/x-flv']); - expect(payload.imp[3].video).to.have.property('protocols'); - expect(payload.imp[3].video.protocols).to.deep.equal([1, 2]); - expect(payload.imp[3].video).to.have.property('api'); - expect(payload.imp[3].video.api).to.deep.equal([2]); - expect(payload.imp[3].video).to.have.property('maxduration', 30); - expect(payload.imp[3]).to.not.have.property('banner'); - }); - - it('Video with playerSize', () => { - expect(payload.imp[4]).to.have.property('id', 'bid004'); - expect(payload.imp[4]).to.have.property('secure', 1); - expect(payload.imp[4]).to.have.property('bidfloor', 0); - expect(payload.imp[4]).to.have.property('displaymanager', 'Prebid.js'); - expect(payload.imp[4]).to.have.property('displaymanagerver').that.matches(versionPattern); - expect(payload.imp[4]).to.not.have.property('tagid'); - expect(payload.imp[4]).to.have.property('video'); - expect(payload.imp[4].video).to.not.have.property('pos'); - expect(payload.imp[4].video).to.have.property('w', 1024); - expect(payload.imp[4].video).to.have.property('h', 768); - expect(payload.imp[4].video).to.have.property('mimes'); - expect(payload.imp[4].video.mimes).to.deep.equal(['video/mp4', 'video/x-flv']); - expect(payload.imp[4].video).to.have.property('protocols'); - expect(payload.imp[4].video.protocols).to.deep.equal([1, 2, 3]); - expect(payload.imp[4].video).to.have.property('api'); - expect(payload.imp[4].video.api).to.deep.equal([1, 2, 3]); - expect(payload.imp[4].video).to.have.property('maxduration', 30); - }); - - it('Video without sizes', () => { - expect(payload.imp[5]).to.have.property('id', 'bid005'); - expect(payload.imp[5]).to.have.property('secure', 1); - expect(payload.imp[5]).to.have.property('bidfloor', 0); - expect(payload.imp[5]).to.have.property('displaymanager', 'Prebid.js'); - expect(payload.imp[5]).to.have.property('displaymanagerver').that.matches(versionPattern); - expect(payload.imp[5]).to.not.have.property('tagid'); - expect(payload.imp[5]).to.have.property('video'); - expect(payload.imp[5].video).to.have.property('pos', 2); - expect(payload.imp[5].video).to.not.have.property('w'); - expect(payload.imp[5].video).to.not.have.property('h'); - expect(payload.imp[5].video).to.have.property('mimes'); - expect(payload.imp[5].video.mimes).to.deep.equal(['video/mp4', 'video/x-flv']); - expect(payload.imp[5].video).to.not.have.property('protocols'); - expect(payload.imp[5].video).to.not.have.property('api'); - expect(payload.imp[5].video).to.not.have.property('maxduration'); - expect(payload.imp[5]).to.not.have.property('banner'); - }); - } + } + }; + const request = spec.buildRequests(bidRequests, bidderRequest); + expect(request.method).to.equal('POST'); + expect(request.url).to.equal('https://web.hb.ad.cpe.dotomi.com/cvx/client/hb/ortb/25'); + const payload = request.data; - it('With FPD', () => { - expect(payload.imp[6]).to.have.property('id', 'bid006'); - expect(payload.imp[6]).to.have.property('banner'); - expect(payload.imp[6]).to.not.have.property('video'); - expect(payload.imp[6]).to.have.property('instl'); - expect(payload.imp[6]).to.have.property('ext'); - expect(payload.imp[6].ext).to.have.property('data'); - expect(payload.imp[6].ext.data).to.have.property('pbadslot'); - }); + expect(payload).to.have.property('id'); + expect(payload.source).to.have.property('tid', 'tid000'); + expect(payload).to.have.property('at', 1); + expect(payload).to.have.property('imp'); + expect(payload.imp).to.be.an('array').with.lengthOf(8); + + expect(payload.imp[0]).to.have.property('id', 'bid000'); + expect(payload.imp[0]).to.have.property('secure', 1); + expect(payload.imp[0]).to.have.property('bidfloor', 0.5); + expect(payload.imp[0]).to.have.property('displaymanager', 'Prebid.js'); + expect(payload.imp[0]).to.have.property('displaymanagerver').that.matches(versionPattern); + expect(payload.imp[0]).to.have.property('tagid', 'tagid-1'); + expect(payload.imp[0]).to.have.property('banner'); + expect(payload.imp[0].banner).to.have.property('pos', 1); + 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]).to.not.have.property('video'); + + expect(payload.imp[1]).to.have.property('id', 'bid001'); + expect(payload.imp[1]).to.have.property('secure', 1); + expect(payload.imp[1]).to.have.property('bidfloor', 0); + expect(payload.imp[1]).to.have.property('displaymanager', 'Prebid.js'); + expect(payload.imp[1]).to.have.property('displaymanagerver').that.matches(versionPattern); + expect(payload.imp[1]).to.not.have.property('tagid'); + expect(payload.imp[1]).to.have.property('banner'); + expect(payload.imp[1].banner).to.not.have.property('pos'); + expect(payload.imp[1].banner).to.have.property('format'); + expect(payload.imp[1].banner.format).to.deep.equal([{w: 728, h: 90}, {w: 468, h: 60}]); + + expect(payload.imp[2]).to.have.property('id', 'bid002'); + expect(payload.imp[2]).to.have.property('secure', 1); + expect(payload.imp[2]).to.have.property('bidfloor', 0); + expect(payload.imp[2]).to.have.property('displaymanager', 'Prebid.js'); + expect(payload.imp[2]).to.have.property('displaymanagerver').that.matches(versionPattern); + expect(payload.imp[2]).to.have.property('banner'); + expect(payload.imp[2].banner).to.have.property('pos', 2); + expect(payload.imp[2].banner).to.have.property('format'); + expect(payload.imp[2].banner.format).to.deep.equal([{w: 300, h: 600}, {w: 160, h: 600}]); + + expect(payload.imp[3]).to.have.property('id', 'bid003'); + expect(payload.imp[3]).to.have.property('secure', 1); + expect(payload.imp[3]).to.have.property('bidfloor', 0); + expect(payload.imp[3]).to.have.property('displaymanager', 'Prebid.js'); + expect(payload.imp[3]).to.have.property('displaymanagerver').that.matches(versionPattern); + expect(payload.imp[3]).to.not.have.property('tagid'); + expect(payload.imp[3]).to.have.property('video'); + expect(payload.imp[3].video).to.have.property('pos', 3); + expect(payload.imp[3].video).to.have.property('w', 632); + expect(payload.imp[3].video).to.have.property('h', 499); + expect(payload.imp[3].video).to.have.property('mimes'); + expect(payload.imp[3].video.mimes).to.deep.equal(['video/mp4', 'video/x-flv']); + expect(payload.imp[3].video).to.have.property('protocols'); + expect(payload.imp[3].video.protocols).to.deep.equal([1, 2]); + expect(payload.imp[3].video).to.have.property('api'); + expect(payload.imp[3].video.api).to.deep.equal([2]); + expect(payload.imp[3].video).to.have.property('maxduration', 30); + expect(payload.imp[3]).to.not.have.property('banner'); + + expect(payload.imp[4]).to.have.property('id', 'bid004'); + expect(payload.imp[4]).to.have.property('secure', 1); + expect(payload.imp[4]).to.have.property('bidfloor', 0); + expect(payload.imp[4]).to.have.property('displaymanager', 'Prebid.js'); + expect(payload.imp[4]).to.have.property('displaymanagerver').that.matches(versionPattern); + expect(payload.imp[4]).to.not.have.property('tagid'); + expect(payload.imp[4]).to.have.property('video'); + expect(payload.imp[4].video).to.not.have.property('pos'); + expect(payload.imp[4].video).to.have.property('w', 1024); + expect(payload.imp[4].video).to.have.property('h', 768); + expect(payload.imp[4].video).to.have.property('mimes'); + expect(payload.imp[4].video.mimes).to.deep.equal(['video/mp4', 'video/x-flv']); + expect(payload.imp[4].video).to.have.property('protocols'); + expect(payload.imp[4].video.protocols).to.deep.equal([1, 2, 3]); + expect(payload.imp[4].video).to.have.property('api'); + expect(payload.imp[4].video.api).to.deep.equal([2, 3]); + expect(payload.imp[4].video).to.have.property('maxduration', 30); + expect(payload.imp[4]).to.not.have.property('banner'); + + expect(payload.imp[5]).to.have.property('id', 'bid005'); + expect(payload.imp[5]).to.have.property('secure', 1); + expect(payload.imp[5]).to.have.property('bidfloor', 0); + expect(payload.imp[5]).to.have.property('displaymanager', 'Prebid.js'); + expect(payload.imp[5]).to.have.property('displaymanagerver').that.matches(versionPattern); + expect(payload.imp[5]).to.not.have.property('tagid'); + expect(payload.imp[5]).to.have.property('video'); + expect(payload.imp[5].video).to.have.property('pos', 2); + expect(payload.imp[5].video).to.not.have.property('w'); + expect(payload.imp[5].video).to.not.have.property('h'); + expect(payload.imp[5].video).to.have.property('mimes'); + expect(payload.imp[5].video.mimes).to.deep.equal(['video/mp4', 'video/x-flv']); + expect(payload.imp[5].video).to.not.have.property('protocols'); + expect(payload.imp[5].video).to.not.have.property('api'); + expect(payload.imp[5].video).to.not.have.property('maxduration'); + expect(payload.imp[5]).to.not.have.property('banner'); + + expect(payload.imp[6]).to.have.property('id', 'bid006'); + expect(payload.imp[6]).to.have.property('video'); + expect(payload.imp[6].video).to.have.property('mimes'); + expect(payload.imp[6].video.mimes).to.deep.equal(['video/mp4', 'video/x-flv']); + expect(payload.imp[6]).to.not.have.property('banner'); + expect(payload.imp[6]).to.have.property('instl'); + expect(payload.imp[6]).to.have.property('ext'); + expect(payload.imp[6].ext).to.have.property('data'); + expect(payload.imp[6].ext.data).to.have.property('pbadslot'); + + expect(payload.imp[7]).to.have.property('id', 'bid007'); + expect(payload.imp[7]).to.have.property('secure', 1); + expect(payload.imp[7]).to.have.property('bidfloor', 0); + expect(payload.imp[7]).to.have.property('displaymanager', 'Prebid.js'); + expect(payload.imp[7]).to.have.property('displaymanagerver').that.matches(versionPattern); + expect(payload.imp[7]).to.not.have.property('tagid'); + expect(payload.imp[7]).to.have.property('banner'); + expect(payload.imp[7].banner).to.have.property('pos', 5); + expect(payload.imp[7].banner).to.have.property('format'); + expect(payload.imp[7].banner.format).to.deep.equal([{w: 728, h: 90}, {w: 468, h: 60}]); + + expect(payload).to.have.property('site'); + expect(payload.site).to.have.property('id', siteId); + expect(payload.site).to.have.property('mobile').that.is.oneOf([0, 1]); + + expect(payload.site).to.have.property('page', page); + + expect(payload).to.have.property('device'); + expect(payload.device).to.have.property('w', screen.width); + expect(payload.device).to.have.property('h', screen.height); + expect(payload.device).to.have.property('dnt').that.is.oneOf([0, 1]); + expect(payload.device).to.have.property('ua', navigator.userAgent); + + expect(payload).to.not.have.property('user'); // there should be no user by default + expect(payload).to.not.have.property('tmax'); // there should be no user by default }); it('Verify timeout', () => { @@ -460,62 +440,59 @@ describe('Conversant adapter tests', function() { expect(request.url).to.equal(testUrl); }); - describe('Verify interpretResponse', function() { - let bid, request, response; - - before(() => { - request = spec.buildRequests(bidRequests, {}); - response = spec.interpretResponse(bidResponses, request).bids; - }); - - it('Banner', function() { - expect(response).to.be.an('array').with.lengthOf(4); - bid = response[0]; - expect(bid).to.have.property('requestId', 'bid000'); - expect(bid).to.have.property('cpm', 0.99); - expect(bid).to.have.property('creativeId', '1000'); - expect(bid).to.have.property('width', 300); - expect(bid).to.have.property('height', 250); - expect(bid.meta.advertiserDomains).to.deep.equal(['https://example.com']); - expect(bid).to.have.property('ad', 'markup000
'); - expect(bid).to.have.property('ttl', 300); - expect(bid).to.have.property('netRevenue', true); - }); + it('Verify interpretResponse', function() { + const request = spec.buildRequests(bidRequests, {}); + const response = spec.interpretResponse(bidResponses, request); + expect(response).to.be.an('array').with.lengthOf(4); + + let bid = response[0]; + expect(bid).to.have.property('requestId', 'bid000'); + expect(bid).to.have.property('currency', 'USD'); + expect(bid).to.have.property('cpm', 0.99); + expect(bid).to.have.property('creativeId', '1000'); + expect(bid).to.have.property('width', 300); + expect(bid).to.have.property('height', 250); + expect(bid.meta.advertiserDomains).to.deep.equal(['https://example.com']); + expect(bid).to.have.property('ad', 'markup000'); + expect(bid).to.have.property('ttl', 300); + expect(bid).to.have.property('netRevenue', true); // There is no bid001 because cpm is $0 - it('Banner multiple sizes', function() { - bid = response[1]; - expect(bid).to.have.property('requestId', 'bid002'); - expect(bid).to.have.property('cpm', 2.99); - expect(bid).to.have.property('creativeId', '1002'); - expect(bid).to.have.property('width', 300); - expect(bid).to.have.property('height', 600); - expect(bid).to.have.property('ad', 'markup002
'); - expect(bid).to.have.property('ttl', 300); - expect(bid).to.have.property('netRevenue', true); - }); - - if (FEATURES.VIDEO) { - it('Video', function () { - bid = response[2]; - expect(bid).to.have.property('requestId', 'bid003'); - expect(bid).to.have.property('cpm', 3.99); - expect(bid).to.have.property('creativeId', '1003'); - expect(bid).to.have.property('playerWidth', 632); - expect(bid).to.have.property('playerHeight', 499); - expect(bid).to.have.property('vastUrl', 'notify003'); - expect(bid).to.have.property('vastXml', 'markup003'); - expect(bid).to.have.property('mediaType', 'video'); - expect(bid).to.have.property('ttl', 300); - expect(bid).to.have.property('netRevenue', true); - }); + bid = response[1]; + expect(bid).to.have.property('requestId', 'bid002'); + expect(bid).to.have.property('currency', 'USD'); + expect(bid).to.have.property('cpm', 2.99); + expect(bid).to.have.property('creativeId', '1002'); + expect(bid).to.have.property('width', 300); + expect(bid).to.have.property('height', 600); + expect(bid).to.have.property('ad', 'markup002'); + expect(bid).to.have.property('ttl', 300); + expect(bid).to.have.property('netRevenue', true); + + bid = response[2]; + expect(bid).to.have.property('requestId', 'bid003'); + expect(bid).to.have.property('currency', 'USD'); + expect(bid).to.have.property('cpm', 3.99); + expect(bid).to.have.property('creativeId', '1003'); + expect(bid).to.have.property('width', 632); + expect(bid).to.have.property('height', 499); + expect(bid).to.have.property('vastUrl', 'markup003'); + expect(bid).to.have.property('mediaType', 'video'); + expect(bid).to.have.property('ttl', 300); + expect(bid).to.have.property('netRevenue', true); + + bid = response[3]; + expect(bid).to.have.property('vastXml', ''); + }); - it('Empty Video', function() { - bid = response[3]; - expect(bid).to.have.property('vastXml', ''); - }); - } + it('Verify handling of bad responses', function() { + let response = spec.interpretResponse({}, {}); + expect(response).to.be.an('array').with.lengthOf(0); + response = spec.interpretResponse({id: '123'}, {}); + expect(response).to.be.an('array').with.lengthOf(0); + response = spec.interpretResponse({id: '123', seatbid: []}, {}); + expect(response).to.be.an('array').with.lengthOf(0); }); it('Verify publisher commond id support', function() { @@ -547,23 +524,79 @@ describe('Conversant adapter tests', function() { expect(payload).to.not.have.nested.property('user.ext.eids'); }); + it('Verify GDPR bid request', function() { + // add gdpr info + const bidderRequest = { + gdprConsent: { + consentString: 'BOJObISOJObISAABAAENAA4AAAAAoAAA', + gdprApplies: true + } + }; + + const payload = spec.buildRequests(bidRequests, bidderRequest).data; + expect(payload).to.have.deep.nested.property('user.ext.consent', 'BOJObISOJObISAABAAENAA4AAAAAoAAA'); + expect(payload).to.have.deep.nested.property('regs.ext.gdpr', 1); + }); + + it('Verify GDPR bid request without gdprApplies', function() { + // add gdpr info + const bidderRequest = { + gdprConsent: { + consentString: '' + } + }; + + const payload = spec.buildRequests(bidRequests, bidderRequest).data; + expect(payload).to.have.deep.nested.property('user.ext.consent', ''); + expect(payload).to.not.have.deep.nested.property('regs.ext.gdpr'); + }); + + describe('CCPA', function() { + it('should have us_privacy', function() { + const bidderRequest = { + uspConsent: '1NYN' + }; + + const payload = spec.buildRequests(bidRequests, bidderRequest).data; + expect(payload).to.have.deep.nested.property('regs.ext.us_privacy', '1NYN'); + expect(payload).to.not.have.deep.nested.property('regs.ext.gdpr'); + }); + + it('should have no us_privacy', function() { + const payload = spec.buildRequests(bidRequests, {}).data; + expect(payload).to.not.have.deep.nested.property('regs.ext.us_privacy'); + }); + + it('should have both gdpr and us_privacy', function() { + const bidderRequest = { + gdprConsent: { + consentString: 'BOJObISOJObISAABAAENAA4AAAAAoAAA', + gdprApplies: true + }, + uspConsent: '1NYN' + }; + + const payload = spec.buildRequests(bidRequests, bidderRequest).data; + expect(payload).to.have.deep.nested.property('user.ext.consent', 'BOJObISOJObISAABAAENAA4AAAAAoAAA'); + expect(payload).to.have.deep.nested.property('regs.ext.gdpr', 1); + expect(payload).to.have.deep.nested.property('regs.ext.us_privacy', '1NYN'); + }); + }); + describe('Extended ID', function() { it('Verify unifiedid and liveramp', function() { // clone bidRequests let requests = utils.deepClone(bidRequests); - const uid = {pubcid: '112233', idl_env: '334455'}; - const eidArray = [{'source': 'pubcid.org', 'uids': [{'id': '112233', 'atype': 1}]}, {'source': 'liveramp.com', 'uids': [{'id': '334455', 'atype': 3}]}]; - // add pubcid to every entry requests.forEach((unit) => { - Object.assign(unit, {userId: uid}); - Object.assign(unit, {userIdAsEids: eidArray}); + Object.assign(unit, {userId: {pubcid: '112233', tdid: '223344', idl_env: '334455'}}); + Object.assign(unit, {userIdAsEids: createEidsArray(unit.userId)}); }); // construct http post payload const payload = spec.buildRequests(requests, {}).data; expect(payload).to.have.deep.nested.property('user.ext.eids', [ - {source: 'pubcid.org', uids: [{id: '112233', atype: 1}]}, + {source: 'adserver.org', uids: [{id: '223344', atype: 1, ext: {rtiPartner: 'TDID'}}]}, {source: 'liveramp.com', uids: [{id: '334455', atype: 3}]} ]); }); diff --git a/test/spec/modules/criteoBidAdapter_spec.js b/test/spec/modules/criteoBidAdapter_spec.js index 5dc6d74d90e..b2f3d64a156 100755 --- a/test/spec/modules/criteoBidAdapter_spec.js +++ b/test/spec/modules/criteoBidAdapter_spec.js @@ -9,12 +9,11 @@ import { } 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'; describe('The Criteo bidding adapter', function () { - let utilsMock, sandbox, ajaxStub; + let utilsMock, sandbox; beforeEach(function () { $$PREBID_GLOBAL$$.bidderSettings = { @@ -27,7 +26,6 @@ describe('The Criteo bidding adapter', function () { utilsMock = sinon.mock(utils); sandbox = sinon.sandbox.create(); - ajaxStub = sandbox.stub(ajax, 'ajax'); }); afterEach(function () { @@ -35,7 +33,6 @@ describe('The Criteo bidding adapter', function () { global.Criteo = undefined; utilsMock.restore(); sandbox.restore(); - ajaxStub.restore(); }); describe('getUserSyncs', function () { @@ -59,9 +56,7 @@ describe('The Criteo bidding adapter', function () { cookiesAreEnabledStub, localStorageIsEnabledStub, getCookieStub, - setCookieStub, - getDataFromLocalStorageStub, - removeDataFromLocalStorageStub; + getDataFromLocalStorageStub; beforeEach(function () { getConfigStub = sinon.stub(config, 'getConfig'); @@ -80,10 +75,8 @@ describe('The Criteo bidding adapter', function () { localStorageIsEnabledStub = sinon.stub(storage, 'localStorageIsEnabled'); localStorageIsEnabledStub.returns(true); - getCookieStub = sinon.stub(storage, 'getCookie'); - setCookieStub = sinon.stub(storage, 'setCookie'); + getCookieStub = sinon.stub(storage, 'getCookie') getDataFromLocalStorageStub = sinon.stub(storage, 'getDataFromLocalStorage'); - removeDataFromLocalStorageStub = sinon.stub(storage, 'removeDataFromLocalStorage'); }); afterEach(function () { @@ -93,9 +86,7 @@ describe('The Criteo bidding adapter', function () { cookiesAreEnabledStub.restore(); localStorageIsEnabledStub.restore(); getCookieStub.restore(); - setCookieStub.restore(); getDataFromLocalStorageStub.restore(); - removeDataFromLocalStorageStub.restore(); }); it('should not trigger sync if publisher is using fast bid', function () { @@ -207,46 +198,6 @@ describe('The Criteo bidding adapter', function () { url: `https://gum.criteo.com/syncframe?origin=criteoPrebidAdapter&topUrl=www.abc.com&us_privacy=ABC#${JSON.stringify(expectedHash).replace(/"/g, '%22')}` }]); }); - - it('should delete user data when calling onDataDeletionRequest', () => { - const cookieData = { - 'cto_bundle': 'a' - }; - const lsData = { - 'cto_bundle': 'a' - } - getCookieStub.callsFake(cookieName => cookieData[cookieName]); - setCookieStub.callsFake((cookieName, value, expires) => cookieData[cookieName] = value); - getDataFromLocalStorageStub.callsFake(name => lsData[name]); - removeDataFromLocalStorageStub.callsFake(name => lsData[name] = ''); - spec.onDataDeletionRequest([]); - expect(getCookieStub.calledOnce).to.equal(true); - expect(setCookieStub.calledOnce).to.equal(true); - expect(getDataFromLocalStorageStub.calledOnce).to.equal(true); - expect(removeDataFromLocalStorageStub.calledOnce).to.equal(true); - expect(cookieData.cto_bundle).to.equal(''); - expect(lsData.cto_bundle).to.equal(''); - expect(ajaxStub.calledOnce).to.equal(true); - }); - - it('should not call API when calling onDataDeletionRequest with no id', () => { - const cookieData = { - 'cto_bundle': '' - }; - const lsData = { - 'cto_bundle': '' - } - getCookieStub.callsFake(cookieName => cookieData[cookieName]); - setCookieStub.callsFake((cookieName, value, expires) => cookieData[cookieName] = value); - getDataFromLocalStorageStub.callsFake(name => lsData[name]); - removeDataFromLocalStorageStub.callsFake(name => lsData[name] = ''); - spec.onDataDeletionRequest([]); - expect(getCookieStub.calledOnce).to.be.true; - expect(setCookieStub.called).to.be.false; - expect(getDataFromLocalStorageStub.calledOnce).to.be.true - expect(removeDataFromLocalStorageStub.called).to.be.false; - expect(ajaxStub.called).to.be.false; - }); }); describe('isBidRequestValid', function () { @@ -364,6 +315,93 @@ describe('The Criteo bidding adapter', function () { }); it('should return false when given an invalid video bid request', function () { + expect(spec.isBidRequestValid({ + bidder: 'criteo', + mediaTypes: { + video: { + mimes: ['video/mpeg'], + playerSize: [640, 480], + protocols: [5, 6], + maxduration: 30, + api: [1, 2] + } + }, + params: { + networkId: 456, + video: { + skip: 1, + placement: 1, + playbackmethod: 1 + } + }, + })).to.equal(false); + + expect(spec.isBidRequestValid({ + bidder: 'criteo', + mediaTypes: { + video: { + context: 'instream', + mimes: ['video/mpeg'], + playerSize: [640, 480], + protocols: [5, 6], + maxduration: 30, + api: [1, 2] + } + }, + params: { + networkId: 456, + video: { + skip: 1, + placement: 2, + playbackmethod: 1 + } + }, + })).to.equal(false); + + expect(spec.isBidRequestValid({ + bidder: 'criteo', + mediaTypes: { + video: { + context: 'outstream', + mimes: ['video/mpeg'], + playerSize: [640, 480], + protocols: [5, 6], + maxduration: 30, + api: [1, 2] + } + }, + params: { + networkId: 456, + video: { + skip: 1, + placement: 1, + playbackmethod: 1 + } + }, + })).to.equal(false); + + expect(spec.isBidRequestValid({ + bidder: 'criteo', + mediaTypes: { + video: { + context: 'adpod', + mimes: ['video/mpeg'], + playerSize: [640, 480], + protocols: [5, 6], + maxduration: 30, + api: [1, 2] + } + }, + params: { + networkId: 456, + video: { + skip: 1, + placement: 1, + playbackmethod: 1 + } + }, + })).to.equal(false); + expect(spec.isBidRequestValid({ bidder: 'criteo', mediaTypes: { @@ -616,33 +654,6 @@ describe('The Criteo bidding adapter', function () { expect(ortbRequest.source.tid).to.equal('abc'); }); - it('should properly transmit bidId if available', function () { - const bidderRequest = { - ortb2: { - source: { - tid: 'abc' - } - } - }; - const bidRequests = [ - { - bidId: 'bidId', - bidder: 'criteo', - adUnitCode: 'bid-123', - transactionId: 'transaction-123', - mediaTypes: { - banner: { - sizes: [[728, 90]] - } - }, - params: {} - }, - ]; - const request = spec.buildRequests(bidRequests, bidderRequest); - const ortbRequest = request.data; - expect(ortbRequest.slots[0].slotid).to.equal('bidId'); - }); - it('should properly build a request if refererInfo is not provided', function () { const bidderRequest = {}; const bidRequests = [ @@ -1035,30 +1046,6 @@ describe('The Criteo bidding adapter', function () { expect(request.data.user.uspIab).to.equal('1YNY'); }); - it('should properly build a request with site and app ortb fields', function () { - const bidRequests = []; - let app = { - publisher: { - id: 'appPublisherId' - } - }; - let site = { - publisher: { - id: 'sitePublisherId' - } - }; - const bidderRequest = { - ortb2: { - app: app, - site: site - } - }; - const request = spec.buildRequests(bidRequests, bidderRequest); - - expect(request.data.app).to.equal(app); - expect(request.data.site).to.equal(site); - }); - it('should properly build a request with device sua field', function () { const sua = {} const bidRequests = [ @@ -1263,25 +1250,12 @@ describe('The Criteo bidding adapter', function () { sizes: [[640, 480]], mediaTypes: { video: { - context: 'instream', playerSize: [640, 480], mimes: ['video/mp4', 'video/x-flv'], maxduration: 30, api: [1, 2], protocols: [2, 3], - plcmt: 3, - w: 640, - h: 480, - linearity: 1, - skipmin: 30, - skipafter: 30, - minbitrate: 10000, - maxbitrate: 48000, - delivery: [1, 2, 3], - pos: 1, - playbackend: 1, - adPodDurationSec: 30, - durationRangeSec: [1, 30], + plcmt: 3 } }, params: { @@ -1300,7 +1274,6 @@ describe('The Criteo bidding adapter', function () { expect(request.url).to.match(/^https:\/\/bidder\.criteo\.com\/cdb\?profileId=207&av=\d+&wv=[^&]+&cb=\d/); expect(request.method).to.equal('POST'); const ortbRequest = request.data; - expect(ortbRequest.slots[0].video.context).to.equal('instream'); expect(ortbRequest.slots[0].video.mimes).to.deep.equal(['video/mp4', 'video/x-flv']); expect(ortbRequest.slots[0].sizes).to.deep.equal([]); expect(ortbRequest.slots[0].video.playersizes).to.deep.equal(['640x480']); @@ -1313,18 +1286,6 @@ describe('The Criteo bidding adapter', function () { expect(ortbRequest.slots[0].video.playbackmethod).to.deep.equal([1, 3]); expect(ortbRequest.slots[0].video.placement).to.equal(2); expect(ortbRequest.slots[0].video.plcmt).to.equal(3); - expect(ortbRequest.slots[0].video.w).to.equal(640); - expect(ortbRequest.slots[0].video.h).to.equal(480); - expect(ortbRequest.slots[0].video.linearity).to.equal(1); - expect(ortbRequest.slots[0].video.skipmin).to.equal(30); - expect(ortbRequest.slots[0].video.skipafter).to.equal(30); - expect(ortbRequest.slots[0].video.minbitrate).to.equal(10000); - expect(ortbRequest.slots[0].video.maxbitrate).to.equal(48000); - expect(ortbRequest.slots[0].video.delivery).to.deep.equal([1, 2, 3]); - expect(ortbRequest.slots[0].video.pos).to.equal(1); - expect(ortbRequest.slots[0].video.playbackend).to.equal(1); - expect(ortbRequest.slots[0].video.adPodDurationSec).to.equal(30); - expect(ortbRequest.slots[0].video.durationRangeSec).to.deep.equal([1, 30]); }); it('should properly build a video request with more than one player size', function () { @@ -1842,86 +1803,6 @@ describe('The Criteo bidding adapter', function () { const request = spec.buildRequests(bidRequests, bidderRequest); expect(request.data.slots[0].rwdd).to.be.undefined; }); - - it('should properly build a request when FLEDGE is enabled', function () { - const bidderRequest = { - fledgeEnabled: true, - }; - const bidRequests = [ - { - bidder: 'criteo', - adUnitCode: 'bid-123', - transactionId: 'transaction-123', - mediaTypes: { - banner: { - sizes: [[728, 90]] - } - }, - params: { - zoneId: 123, - ext: { - bidfloor: 0.75 - } - }, - ortb2Imp: { - ext: { - ae: 1 - } - } - }, - ]; - - const request = spec.buildRequests(bidRequests, bidderRequest); - expect(request.data.slots[0].ext.ae).to.equal(1); - }); - - it('should properly build a request when FLEDGE is disabled', function () { - const bidderRequest = { - fledgeEnabled: false, - }; - const bidRequests = [ - { - bidder: 'criteo', - adUnitCode: 'bid-123', - transactionId: 'transaction-123', - mediaTypes: { - banner: { - sizes: [[728, 90]] - } - }, - params: { - zoneId: 123, - ext: { - bidfloor: 0.75 - } - }, - ortb2Imp: { - ext: { - ae: 1 - } - } - }, - ]; - - const request = spec.buildRequests(bidRequests, bidderRequest); - expect(request.data.slots[0].ext).to.not.have.property('ae'); - }); - - it('should properly transmit device.ext.cdep if available', function () { - const bidderRequest = { - ortb2: { - device: { - ext: { - cdep: 'cookieDeprecationLabel' - } - } - } - }; - const bidRequests = []; - const request = spec.buildRequests(bidRequests, bidderRequest); - const ortbRequest = request.data; - expect(ortbRequest.device.ext.cdep).to.equal('cookieDeprecationLabel'); - }); }); describe('interpretResponse', function () { @@ -1956,11 +1837,6 @@ describe('The Criteo bidding adapter', function () { bidRequests: [{ adUnitCode: 'test-requestId', bidId: 'test-bidId', - mediaTypes: { - banner: { - sizes: [[728, 90]] - } - }, params: { networkId: 456, } @@ -1979,202 +1855,6 @@ describe('The Criteo bidding adapter', function () { expect(bids[0].meta.networkName).to.equal('Criteo'); }); - it('should properly parse a bid response with a networkId with twin ad unit banner win', function () { - const response = { - body: { - slots: [{ - impid: 'test-requestId', - cpm: 1.23, - creative: 'test-ad', - creativecode: 'test-crId', - width: 728, - height: 90, - deal: 'myDealCode', - adomain: ['criteo.com'], - ext: { - meta: { - networkName: 'Criteo' - } - } - }], - }, - }; - const request = { - bidRequests: [{ - adUnitCode: 'test-requestId', - bidId: 'test-bidId', - mediaTypes: { - video: { - context: 'instream', - mimes: ['video/mpeg'], - playerSize: [640, 480], - protocols: [5, 6], - maxduration: 30, - api: [1, 2] - } - }, - params: { - networkId: 456, - }, - }, { - adUnitCode: 'test-requestId', - bidId: 'test-bidId2', - mediaTypes: { - banner: { - sizes: [[728, 90]] - } - }, - params: { - networkId: 456, - } - }] - }; - const bids = spec.interpretResponse(response, request); - expect(bids).to.have.lengthOf(1); - expect(bids[0].requestId).to.equal('test-bidId2'); - expect(bids[0].cpm).to.equal(1.23); - expect(bids[0].ad).to.equal('test-ad'); - expect(bids[0].creativeId).to.equal('test-crId'); - expect(bids[0].width).to.equal(728); - expect(bids[0].height).to.equal(90); - expect(bids[0].dealId).to.equal('myDealCode'); - expect(bids[0].meta.advertiserDomains[0]).to.equal('criteo.com'); - expect(bids[0].meta.networkName).to.equal('Criteo'); - }); - - it('should properly parse a bid response with a networkId with twin ad unit video win', function () { - const response = { - body: { - slots: [{ - impid: 'test-requestId', - bidId: 'abc123', - cpm: 1.23, - displayurl: 'http://test-ad', - width: 728, - height: 90, - zoneid: 123, - video: true, - ext: { - meta: { - networkName: 'Criteo' - } - } - }], - }, - }; - const request = { - bidRequests: [{ - adUnitCode: 'test-requestId', - bidId: 'test-bidId', - mediaTypes: { - video: { - context: 'instream', - mimes: ['video/mpeg'], - playerSize: [728, 90], - protocols: [5, 6], - maxduration: 30, - api: [1, 2] - } - }, - params: { - networkId: 456, - }, - }, { - adUnitCode: 'test-requestId', - bidId: 'test-bidId2', - mediaTypes: { - banner: { - sizes: [[728, 90]] - } - }, - params: { - networkId: 456, - } - }] - }; - const bids = spec.interpretResponse(response, request); - expect(bids).to.have.lengthOf(1); - expect(bids[0].requestId).to.equal('test-bidId'); - expect(bids[0].cpm).to.equal(1.23); - expect(bids[0].vastUrl).to.equal('http://test-ad'); - expect(bids[0].mediaType).to.equal(VIDEO); - }); - - it('should properly parse a bid response with a networkId with twin ad unit native win', function () { - const response = { - body: { - slots: [{ - impid: 'test-requestId', - cpm: 1.23, - creative: 'test-ad', - creativecode: 'test-crId', - width: 728, - height: 90, - deal: 'myDealCode', - adomain: ['criteo.com'], - native: { - 'products': [{ - 'sendTargetingKeys': false, - 'title': 'Product title', - 'description': 'Product desc', - 'price': '100', - 'click_url': 'https://product.click', - 'image': { - 'url': 'https://publisherdirect.criteo.com/publishertag/preprodtest/creative.png', - 'height': 300, - 'width': 300 - }, - 'call_to_action': 'Try it now!' - }], - 'advertiser': { - 'description': 'sponsor', - 'domain': 'criteo.com', - 'logo': { 'url': 'https://www.criteo.com/images/criteo-logo.svg', 'height': 300, 'width': 300 } - }, - 'privacy': { - 'optout_click_url': 'https://info.criteo.com/privacy/informations', - 'optout_image_url': 'https://static.criteo.net/flash/icon/nai_small.png', - }, - 'impression_pixels': [{ 'url': 'https://my-impression-pixel/test/impression' }, { 'url': 'https://cas.com/lg.com' }] - }, - ext: { - meta: { - networkName: 'Criteo' - } - } - }], - }, - }; - const request = { - bidRequests: [{ - adUnitCode: 'test-requestId', - bidId: 'test-bidId', - mediaTypes: { - native: {} - }, - params: { - networkId: 456, - }, - }, { - adUnitCode: 'test-requestId', - bidId: 'test-bidId2', - mediaTypes: { - banner: { - sizes: [[728, 90]] - } - }, - params: { - networkId: 456, - } - }] - }; - const bids = spec.interpretResponse(response, request); - expect(bids).to.have.lengthOf(1); - expect(bids[0].requestId).to.equal('test-bidId'); - expect(bids[0].cpm).to.equal(1.23); - expect(bids[0].mediaType).to.equal(NATIVE); - }); - it('should properly parse a bid response with a zoneId', function () { const response = { body: { @@ -2453,144 +2133,6 @@ describe('The Criteo bidding adapter', function () { expect(bids[0].height).to.equal(90); }); - it('should properly parse a bid response with FLEDGE auction configs', function () { - const response = { - body: { - ext: { - igbid: [{ - impid: 'test-bidId', - igbuyer: [{ - origin: 'https://first-buyer-domain.com', - buyerdata: { - foo: 'bar', - }, - }, { - origin: 'https://second-buyer-domain.com', - buyerdata: { - foo: 'baz', - }, - }] - }, { - impid: 'test-bidId-2', - igbuyer: [{ - origin: 'https://first-buyer-domain.com', - buyerdata: { - foo: 'bar', - }, - }, { - origin: 'https://second-buyer-domain.com', - buyerdata: { - foo: 'baz', - }, - }] - }], - seller: 'https://seller-domain.com', - sellerTimeout: 500, - sellerSignals: { - foo: 'bar', - }, - sellerSignalsPerImp: { - 'test-bidId': { - foo2: 'bar2', - } - }, - }, - }, - }; - const bidderRequest = { - ortb2: { - source: { - tid: 'abc' - } - } - }; - 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 request = spec.buildRequests(bidRequests, bidderRequest); - const interpretedResponse = spec.interpretResponse(response, request); - expect(interpretedResponse).to.have.property('bids'); - expect(interpretedResponse).to.have.property('fledgeAuctionConfigs'); - expect(interpretedResponse.bids).to.have.lengthOf(0); - expect(interpretedResponse.fledgeAuctionConfigs).to.have.lengthOf(2); - expect(interpretedResponse.fledgeAuctionConfigs[0]).to.deep.equal({ - bidId: 'test-bidId', - config: { - 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' - }, - }, - seller: 'https://seller-domain.com', - sellerTimeout: 500, - sellerSignals: { - foo: 'bar', - foo2: 'bar2', - floor: 1, - sellerCurrency: 'EUR', - }, - }, - }); - expect(interpretedResponse.fledgeAuctionConfigs[1]).to.deep.equal({ - bidId: 'test-bidId-2', - config: { - 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' - }, - }, - seller: 'https://seller-domain.com', - sellerTimeout: 500, - sellerSignals: { - foo: 'bar', - floor: 1, - sellerCurrency: 'EUR', - }, - }, - }); - }); - [{ hasBidResponseLevelPafData: true, hasBidResponseBidLevelPafData: true, diff --git a/test/spec/modules/criteoIdSystem_spec.js b/test/spec/modules/criteoIdSystem_spec.js index 975271738e5..aaf63873d93 100644 --- a/test/spec/modules/criteoIdSystem_spec.js +++ b/test/spec/modules/criteoIdSystem_spec.js @@ -52,21 +52,17 @@ describe('CriteoId module', function () { }); const storageTestCases = [ - { submoduleConfig: undefined, cookie: 'bidId', localStorage: 'bidId2', expected: 'bidId' }, - { submoduleConfig: undefined, cookie: 'bidId', localStorage: undefined, expected: 'bidId' }, - { submoduleConfig: undefined, cookie: undefined, localStorage: 'bidId', expected: 'bidId' }, - { submoduleConfig: undefined, cookie: undefined, localStorage: undefined, expected: undefined }, - { submoduleConfig: { storage: { type: 'cookie' } }, cookie: 'bidId', localStorage: 'bidId2', expected: 'bidId' }, - { submoduleConfig: { storage: { type: 'cookie' } }, cookie: undefined, localStorage: 'bidId2', expected: undefined }, - { submoduleConfig: { storage: { type: 'html5' } }, cookie: 'bidId', localStorage: 'bidId2', expected: 'bidId2' }, - { submoduleConfig: { storage: { type: 'html5' } }, cookie: 'bidId', localStorage: undefined, expected: undefined }, + { cookie: 'bidId', localStorage: 'bidId2', expected: 'bidId' }, + { cookie: 'bidId', localStorage: undefined, expected: 'bidId' }, + { cookie: undefined, localStorage: 'bidId', expected: 'bidId' }, + { cookie: undefined, localStorage: undefined, expected: undefined }, ] - storageTestCases.forEach(testCase => it('getId() should return the user id depending on the storage type enabled and the data available', function () { + storageTestCases.forEach(testCase => it('getId() should return the bidId when it exists in local storages', function () { getCookieStub.withArgs('cto_bidid').returns(testCase.cookie); getLocalStorageStub.withArgs('cto_bidid').returns(testCase.localStorage); - const result = criteoIdSubmodule.getId(testCase.submoduleConfig); + const result = criteoIdSubmodule.getId(); expect(result.id).to.be.deep.equal(testCase.expected ? { criteoId: testCase.expected } : undefined); expect(result.callback).to.be.a('function'); })) @@ -99,24 +95,22 @@ describe('CriteoId module', function () { }); const responses = [ - { submoduleConfig: undefined, shouldWriteCookie: true, shouldWriteLocalStorage: true, bundle: 'bundle', bidId: 'bidId', acwsUrl: 'acwsUrl' }, - { submoduleConfig: undefined, shouldWriteCookie: true, shouldWriteLocalStorage: true, bundle: 'bundle', bidId: undefined, acwsUrl: 'acwsUrl' }, - { submoduleConfig: undefined, shouldWriteCookie: true, shouldWriteLocalStorage: true, bundle: 'bundle', bidId: 'bidId', acwsUrl: undefined }, - { submoduleConfig: undefined, shouldWriteCookie: true, shouldWriteLocalStorage: true, bundle: undefined, bidId: 'bidId', acwsUrl: 'acwsUrl' }, - { submoduleConfig: undefined, shouldWriteCookie: true, shouldWriteLocalStorage: true, bundle: 'bundle', bidId: undefined, acwsUrl: undefined }, - { submoduleConfig: undefined, shouldWriteCookie: true, shouldWriteLocalStorage: true, bundle: undefined, bidId: 'bidId', acwsUrl: undefined }, - { submoduleConfig: undefined, shouldWriteCookie: true, shouldWriteLocalStorage: true, bundle: undefined, bidId: undefined, acwsUrl: 'acwsUrl' }, - { submoduleConfig: undefined, shouldWriteCookie: true, shouldWriteLocalStorage: true, bundle: undefined, bidId: undefined, acwsUrl: ['acwsUrl', 'acwsUrl2'] }, - { submoduleConfig: undefined, shouldWriteCookie: true, shouldWriteLocalStorage: true, bundle: undefined, bidId: undefined, acwsUrl: undefined }, - { submoduleConfig: { storage: { type: 'cookie' } }, shouldWriteCookie: true, shouldWriteLocalStorage: false, bundle: 'bundle', bidId: 'bidId', acwsUrl: undefined }, - { submoduleConfig: { storage: { type: 'html5' } }, shouldWriteCookie: false, shouldWriteLocalStorage: true, bundle: 'bundle', bidId: 'bidId', acwsUrl: undefined }, + { bundle: 'bundle', bidId: 'bidId', acwsUrl: 'acwsUrl' }, + { bundle: 'bundle', bidId: undefined, acwsUrl: 'acwsUrl' }, + { bundle: 'bundle', bidId: 'bidId', acwsUrl: undefined }, + { bundle: undefined, bidId: 'bidId', acwsUrl: 'acwsUrl' }, + { bundle: 'bundle', bidId: undefined, acwsUrl: undefined }, + { bundle: undefined, bidId: 'bidId', acwsUrl: undefined }, + { bundle: undefined, bidId: undefined, acwsUrl: 'acwsUrl' }, + { bundle: undefined, bidId: undefined, acwsUrl: ['acwsUrl', 'acwsUrl2'] }, + { bundle: undefined, bidId: undefined, acwsUrl: undefined }, ] responses.forEach(response => describe('test user sync response behavior', function () { const expirationTs = new Date(nowTimestamp + cookiesMaxAge).toString(); it('should save bidId if it exists', function () { - const result = criteoIdSubmodule.getId(response.submoduleConfig); + const result = criteoIdSubmodule.getId(); result.callback((id) => { expect(id).to.be.deep.equal(response.bidId ? { criteoId: response.bidId } : undefined); }); @@ -133,35 +127,16 @@ describe('CriteoId module', function () { expect(setCookieStub.calledWith('cto_bundle')).to.be.false; expect(setLocalStorageStub.calledWith('cto_bundle')).to.be.false; } else if (response.bundle) { - if (response.shouldWriteCookie) { - expect(setCookieStub.calledWith('cto_bundle', response.bundle, expirationTs, null, '.com')).to.be.true; - expect(setCookieStub.calledWith('cto_bundle', response.bundle, expirationTs, null, '.testdev.com')).to.be.true; - } else { - expect(setCookieStub.calledWith('cto_bundle', response.bundle, expirationTs, null, '.com')).to.be.false; - expect(setCookieStub.calledWith('cto_bundle', response.bundle, expirationTs, null, '.testdev.com')).to.be.false; - } - - if (response.shouldWriteLocalStorage) { - expect(setLocalStorageStub.calledWith('cto_bundle', response.bundle)).to.be.true; - } else { - expect(setLocalStorageStub.calledWith('cto_bundle', response.bundle)).to.be.false; - } + expect(setCookieStub.calledWith('cto_bundle', response.bundle, expirationTs, null, '.com')).to.be.true; + expect(setCookieStub.calledWith('cto_bundle', response.bundle, expirationTs, null, '.testdev.com')).to.be.true; + expect(setLocalStorageStub.calledWith('cto_bundle', response.bundle)).to.be.true; expect(triggerPixelStub.called).to.be.false; } if (response.bidId) { - if (response.shouldWriteCookie) { - expect(setCookieStub.calledWith('cto_bidid', response.bidId, expirationTs, null, '.com')).to.be.true; - expect(setCookieStub.calledWith('cto_bidid', response.bidId, expirationTs, null, '.testdev.com')).to.be.true; - } else { - expect(setCookieStub.calledWith('cto_bidid', response.bidId, expirationTs, null, '.com')).to.be.false; - expect(setCookieStub.calledWith('cto_bidid', response.bidId, expirationTs, null, '.testdev.com')).to.be.false; - } - if (response.shouldWriteLocalStorage) { - expect(setLocalStorageStub.calledWith('cto_bidid', response.bidId)).to.be.true; - } else { - expect(setLocalStorageStub.calledWith('cto_bidid', response.bidId)).to.be.false; - } + expect(setCookieStub.calledWith('cto_bidid', response.bidId, expirationTs, null, '.com')).to.be.true; + expect(setCookieStub.calledWith('cto_bidid', response.bidId, expirationTs, null, '.testdev.com')).to.be.true; + expect(setLocalStorageStub.calledWith('cto_bidid', response.bidId)).to.be.true; } else { expect(setCookieStub.calledWith('cto_bidid', '', pastDateString, null, '.com')).to.be.true; expect(setCookieStub.calledWith('cto_bidid', '', pastDateString, null, '.testdev.com')).to.be.true; diff --git a/test/spec/modules/currency_spec.js b/test/spec/modules/currency_spec.js index fa44b7daa7a..88c640e38cc 100644 --- a/test/spec/modules/currency_spec.js +++ b/test/spec/modules/currency_spec.js @@ -10,12 +10,10 @@ import { addBidResponseHook, currencySupportEnabled, currencyRates, - responseReady + ready } from 'modules/currency.js'; import {createBid} from '../../../src/bidfactory.js'; import CONSTANTS from '../../../src/constants.json'; -import {server} from '../../mocks/xhr.js'; -import * as events from 'src/events.js'; var assert = require('chai').assert; var expect = require('chai').expect; @@ -32,10 +30,12 @@ describe('currency', function () { } beforeEach(function () { - fakeCurrencyFileServer = server; + fakeCurrencyFileServer = sinon.fakeServer.create(); + ready.reset(); }); afterEach(function () { + fakeCurrencyFileServer.restore(); setConfig({}); }); @@ -259,19 +259,6 @@ describe('currency', function () { expect(innerBid.getCpmInNewCurrency('JPY')).to.equal('100.000'); }); - it('does not block auctions if rates do not need to be fetched', () => { - sandbox.stub(responseReady, 'resolve'); - setConfig({ - adServerCurrency: 'USD', - rates: { - USD: { - JPY: 100 - } - } - }); - sinon.assert.called(responseReady.resolve); - }) - it('uses rates specified in json when provided and consider boosted bid', function () { setConfig({ adServerCurrency: 'USD', @@ -300,56 +287,32 @@ describe('currency', function () { expect(innerBid.getCpmInNewCurrency('JPY')).to.equal('1000.000'); }); - describe('when rates fail to load', () => { - let bid, addBidResponse, reject; - beforeEach(() => { - bid = makeBid({cpm: 100, currency: 'JPY', bidder: 'rubicoin'}); - addBidResponse = sinon.spy(); - reject = sinon.spy(); - }) - it('uses default rates if specified', function () { - setConfig({ - adServerCurrency: 'USD', - defaultRates: { - USD: { - JPY: 100 - } - } - }); + it('uses default rates when currency file fails to load', function () { + setConfig({}); - // default response is 404 - addBidResponseHook(addBidResponse, 'au', bid); - fakeCurrencyFileServer.respond(); - sinon.assert.calledWith(addBidResponse, 'au', sinon.match(innerBid => { - expect(innerBid.cpm).to.equal('1.0000'); - expect(typeof innerBid.getCpmInNewCurrency).to.equal('function'); - expect(innerBid.getCpmInNewCurrency('JPY')).to.equal('100.000'); - return true; - })); + setConfig({ + adServerCurrency: 'USD', + defaultRates: { + USD: { + JPY: 100 + } + } }); - it('rejects bids if no default rates are specified', () => { - setConfig({ - adServerCurrency: 'USD', - }); - addBidResponseHook(addBidResponse, 'au', bid, reject); - fakeCurrencyFileServer.respond(); - sinon.assert.notCalled(addBidResponse); - sinon.assert.calledWith(reject, CONSTANTS.REJECTION_REASON.CANNOT_CONVERT_CURRENCY); - }); + // default response is 404 + fakeCurrencyFileServer.respond(); - it('attempts to load rates again on the next auction', () => { - setConfig({ - adServerCurrency: 'USD', - }); - fakeCurrencyFileServer.respond(); - fakeCurrencyFileServer.respondWith(JSON.stringify(getCurrencyRates())); - events.emit(CONSTANTS.EVENTS.AUCTION_INIT, {}); - addBidResponseHook(addBidResponse, 'au', bid, reject); - fakeCurrencyFileServer.respond(); - sinon.assert.calledWith(addBidResponse, 'au', bid, reject); - }) - }) + var bid = { cpm: 100, currency: 'JPY', bidder: 'rubicon' }; + var innerBid; + + addBidResponseHook(function(adCodeId, bid) { + innerBid = bid; + }, 'elementId', bid); + + expect(innerBid.cpm).to.equal('1.0000'); + expect(typeof innerBid.getCpmInNewCurrency).to.equal('function'); + expect(innerBid.getCpmInNewCurrency('JPY')).to.equal('100.000'); + }); }); describe('currency.addBidResponseDecorator bidResponseQueue', function () { @@ -358,26 +321,29 @@ describe('currency', function () { fakeCurrencyFileServer.respondWith(JSON.stringify(getCurrencyRates())); - const bid = { 'cpm': 1, 'currency': 'USD' }; + var bid = { 'cpm': 1, 'currency': 'USD' }; setConfig({ 'adServerCurrency': 'JPY' }); - let responseAdded = false; - let isReady = false; - responseReady.promise.then(() => { isReady = true }); - + var marker = false; + let promiseResolved = false; addBidResponseHook(Object.assign(function() { - responseAdded = true; + marker = true; + }, { + bail: function (promise) { + promise.then(() => promiseResolved = true); + } }), 'elementId', bid); + expect(marker).to.equal(false); + setTimeout(() => { - expect(responseAdded).to.equal(false); - expect(isReady).to.equal(false); + expect(promiseResolved).to.be.false; fakeCurrencyFileServer.respond(); setTimeout(() => { - expect(responseAdded).to.equal(true); - expect(isReady).to.equal(true); + expect(marker).to.equal(true); + expect(promiseResolved).to.be.true; done(); }); }); @@ -453,23 +419,6 @@ describe('currency', function () { expect(reject.calledOnce).to.be.true; }); - 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'}); - const reject = sinon.spy(); - const addBidResponse = sinon.spy(); - addBidResponseHook(addBidResponse, 'au', bid, reject); - addBidResponseHook(addBidResponse, 'au', noConversionBid, reject); - events.emit(CONSTANTS.EVENTS.AUCTION_TIMEOUT, {auctionId: 'aid'}); - fakeCurrencyFileServer.respond(); - sinon.assert.calledOnce(addBidResponse); - sinon.assert.calledWith(addBidResponse, 'au', noConversionBid, reject); - sinon.assert.calledOnce(reject); - sinon.assert.calledWith(reject, CONSTANTS.REJECTION_REASON.CANNOT_CONVERT_CURRENCY); - }) - it('should return 1 when currency support is enabled and same currency code is requested as is set to adServerCurrency', function () { fakeCurrencyFileServer.respondWith(JSON.stringify(getCurrencyRates())); setConfig({ 'adServerCurrency': 'JPY' }); diff --git a/test/spec/modules/cwireBidAdapter_spec.js b/test/spec/modules/cwireBidAdapter_spec.js index 8eedcdb4a07..88c54212aff 100644 --- a/test/spec/modules/cwireBidAdapter_spec.js +++ b/test/spec/modules/cwireBidAdapter_spec.js @@ -293,51 +293,4 @@ describe('C-WIRE bid adapter', () => { expect(bids[0].ad).to.exist; }) }); - - describe('add user-syncs', function () { - it('empty user-syncs if no consent given', function () { - const userSyncs = spec.getUserSyncs({}, {}, {}, {}); - - expect(userSyncs).to.be.empty - }) - it('empty user-syncs if no syncOption enabled', function () { - let gdprConsent = { - vendorData: { - purpose: { - consents: 1 - } - }}; - const userSyncs = spec.getUserSyncs({}, {}, gdprConsent, {}); - - expect(userSyncs).to.be.empty - }) - - it('user-syncs with enabled pixel option', function () { - let gdprConsent = { - vendorData: { - purpose: { - consents: 1 - } - }}; - let synOptions = {pixelEnabled: true, iframeEnabled: true}; - const userSyncs = spec.getUserSyncs(synOptions, {}, gdprConsent, {}); - - expect(userSyncs[0].type).to.equal('image'); - expect(userSyncs[0].url).to.equal('https://ib.adnxs.com/getuid?https://prebid.cwi.re/v1/cookiesync?xandrId=$UID'); - }) - - it('user-syncs with enabled iframe option', function () { - let gdprConsent = { - vendorData: { - purpose: { - consents: 1 - } - }}; - let synOptions = {iframeEnabled: true}; - const userSyncs = spec.getUserSyncs(synOptions, {}, gdprConsent, {}); - - expect(userSyncs[0].type).to.equal('iframe'); - expect(userSyncs[0].url).to.equal('https://ib.adnxs.com/getuid?https://prebid.cwi.re/v1/cookiesync?xandrId=$UID'); - }) - }) }); diff --git a/test/spec/modules/datablocksBidAdapter_spec.js b/test/spec/modules/datablocksBidAdapter_spec.js index 811aaab6ebb..537b82b0e2e 100644 --- a/test/spec/modules/datablocksBidAdapter_spec.js +++ b/test/spec/modules/datablocksBidAdapter_spec.js @@ -2,7 +2,6 @@ import { expect } from 'chai'; import { spec } from '../../../modules/datablocksBidAdapter.js'; import { BotClientTests } from '../../../modules/datablocksBidAdapter.js'; import { getStorageManager } from '../../../src/storageManager.js'; -import {deepClone} from '../../../src/utils.js'; const bid = { bidId: '2dd581a2b6281d', @@ -410,7 +409,7 @@ describe('DatablocksAdapter', function() { expect(spec.isBidRequestValid(bid)).to.be.true; }); it('Should return false when host/source_id is not set', function() { - let moddedBid = deepClone(bid); + let moddedBid = Object.assign({}, bid); delete moddedBid.params.source_id; expect(spec.isBidRequestValid(moddedBid)).to.be.false; }); @@ -443,12 +442,9 @@ describe('DatablocksAdapter', function() { }); describe('buildRequests', function() { - let request; - before(() => { - request = spec.buildRequests([bid, bid2, nativeBid], bidderRequest); - expect(request).to.exist; - }) + let request = spec.buildRequests([bid, bid2, nativeBid], bidderRequest); + expect(request).to.exist; it('Returns POST method', function() { expect(request.method).to.exist; expect(request.method).to.equal('POST'); diff --git a/test/spec/modules/dfpAdServerVideo_spec.js b/test/spec/modules/dfpAdServerVideo_spec.js index 39713c2b51a..89485adf28b 100644 --- a/test/spec/modules/dfpAdServerVideo_spec.js +++ b/test/spec/modules/dfpAdServerVideo_spec.js @@ -1,61 +1,43 @@ -import {expect} from 'chai'; +import { expect } from 'chai'; import parse from 'url-parse'; -import {buildAdpodVideoUrl, buildDfpVideoUrl, dep} from 'modules/dfpAdServerVideo.js'; -import AD_UNIT from 'test/fixtures/video/adUnit.json'; +import {buildDfpVideoUrl, buildAdpodVideoUrl, dep} from 'modules/dfpAdServerVideo.js'; +import adUnit 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, uspDataHandler} from 'src/adapterManager.js'; +import { config } from 'src/config.js'; +import { targeting } from 'src/targeting.js'; +import { auctionManager } from 'src/auctionManager.js'; +import { gdprDataHandler, uspDataHandler } from 'src/adapterManager.js'; import * as adpod from 'modules/adpod.js'; -import {server} from 'test/mocks/xhr.js'; +import { server } from 'test/mocks/xhr.js'; import * as adServer from 'src/adserver.js'; +import {deepClone} from 'src/utils.js'; import {hook} from '../../../src/hook.js'; -import {stubAuctionIndex} from '../../helpers/indexStub.js'; -import {AuctionIndex} from '../../../src/auctionIndex.js'; +import {getRefererInfo} from '../../../src/refererDetection.js'; + +const bid = { + videoCacheKey: 'abc', + adserverTargeting: { + hb_uuid: 'abc', + hb_cache_id: 'abc', + }, +}; describe('The DFP video support module', function () { before(() => { hook.ready(); }); - let sandbox, bid, adUnit; + let sandbox; beforeEach(() => { sandbox = sinon.sandbox.create(); - bid = { - videoCacheKey: 'abc', - adserverTargeting: { - hb_uuid: 'abc', - hb_cache_id: 'abc', - }, - }; - adUnit = deepClone(AD_UNIT); }); afterEach(() => { sandbox.restore(); }); - function getURL(options) { - return parse(buildDfpVideoUrl(Object.assign({ - adUnit: adUnit, - bid: bid, - params: { - 'iu': 'my/adUnit' - } - }, options))) - } - function getQueryParams(options) { - return utils.parseQS(getURL(options).query); - } - - function getCustomParams(options) { - return utils.parseQS('?' + decodeURIComponent(getQueryParams(options).cust_params)); - } - Object.entries({ params: { params: { @@ -69,25 +51,27 @@ describe('The DFP video support module', function () { describe(`when using ${t}`, () => { it('should use page location as default for description_url', () => { 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'})); - 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'); - }); - }); + const url = parse(buildDfpVideoUrl(Object.assign({ + adUnit: adUnit, + bid: bid, + }, options))); + const prm = utils.parseQS(url.query); + expect(prm.description_url).to.eql('example.com'); + }) + }) }) it('should make a legal request URL when given the required params', function () { - const url = getURL({ + const url = parse(buildDfpVideoUrl({ + adUnit: adUnit, + bid: bid, params: { 'iu': 'my/adUnit', 'description_url': 'someUrl.com', } - }) + })); + expect(url.protocol).to.equal('https:'); expect(url.host).to.equal('securepubads.g.doubleclick.net'); @@ -104,10 +88,15 @@ describe('The DFP video support module', function () { }); it('can take an adserver url as a parameter', function () { - bid.vastUrl = 'vastUrl.example'; - const url = getURL({ + const bidCopy = utils.deepClone(bid); + bidCopy.vastUrl = 'vastUrl.example'; + + const url = parse(buildDfpVideoUrl({ + adUnit: adUnit, + bid: bidCopy, url: 'https://video.adserver.example/', - }) + })); + expect(url.host).to.equal('video.adserver.example'); }); @@ -121,64 +110,161 @@ describe('The DFP video support module', function () { }); it('overwrites url params when both url and params object are given', function () { - const params = getQueryParams({ + const url = parse(buildDfpVideoUrl({ + adUnit: adUnit, + bid: bid, url: 'https://video.adserver.example/ads?sz=640x480&iu=/123/aduniturl&impl=s', params: { iu: 'my/adUnit' } - }); + })); - expect(params.iu).to.equal('my/adUnit'); + const queryObject = utils.parseQS(url.query); + expect(queryObject.iu).to.equal('my/adUnit'); }); it('should override param defaults with user-provided ones', function () { - const params = getQueryParams({ + const url = parse(buildDfpVideoUrl({ + adUnit: adUnit, + bid: bid, params: { + 'iu': 'my/adUnit', 'output': 'vast', } - }); - expect(params.output).to.equal('vast'); + })); + + expect(utils.parseQS(url.query)).to.have.property('output', 'vast'); }); it('should include the cache key and adserver targeting in cust_params', function () { - bid.adserverTargeting = Object.assign(bid.adserverTargeting, { + const bidCopy = utils.deepClone(bid); + bidCopy.adserverTargeting = Object.assign(bidCopy.adserverTargeting, { hb_adid: 'ad_id', }); - const customParams = getCustomParams() + const url = parse(buildDfpVideoUrl({ + adUnit: adUnit, + bid: bidCopy, + params: { + 'iu': 'my/adUnit' + } + })); + const queryObject = utils.parseQS(url.query); + const customParams = utils.parseQS('?' + decodeURIComponent(queryObject.cust_params)); expect(customParams).to.have.property('hb_adid', 'ad_id'); expect(customParams).to.have.property('hb_uuid', bid.videoCacheKey); expect(customParams).to.have.property('hb_cache_id', bid.videoCacheKey); }); + it('should include the us_privacy key when USP Consent is available', function () { + let uspDataHandlerStub = sinon.stub(uspDataHandler, 'getConsentData'); + uspDataHandlerStub.returns('1YYY'); + + const bidCopy = utils.deepClone(bid); + bidCopy.adserverTargeting = Object.assign(bidCopy.adserverTargeting, { + hb_adid: 'ad_id', + }); + + const url = parse(buildDfpVideoUrl({ + adUnit: adUnit, + bid: bidCopy, + params: { + 'iu': 'my/adUnit' + } + })); + const queryObject = utils.parseQS(url.query); + expect(queryObject.us_privacy).to.equal('1YYY'); + uspDataHandlerStub.restore(); + }); + + it('should not include the us_privacy key when USP Consent is not available', function () { + const bidCopy = utils.deepClone(bid); + bidCopy.adserverTargeting = Object.assign(bidCopy.adserverTargeting, { + hb_adid: 'ad_id', + }); + + const url = parse(buildDfpVideoUrl({ + adUnit: adUnit, + bid: bidCopy, + params: { + 'iu': 'my/adUnit' + } + })); + const queryObject = utils.parseQS(url.query); + expect(queryObject.us_privacy).to.equal(undefined); + }); + it('should include the GDPR keys when GDPR Consent is available', function () { - sandbox.stub(gdprDataHandler, 'getConsentData').returns({ + let gdprDataHandlerStub = sinon.stub(gdprDataHandler, 'getConsentData'); + gdprDataHandlerStub.returns({ gdprApplies: true, consentString: 'consent', addtlConsent: 'moreConsent' }); - const queryObject = getQueryParams(); + + const bidCopy = utils.deepClone(bid); + bidCopy.adserverTargeting = Object.assign(bidCopy.adserverTargeting, { + hb_adid: 'ad_id', + }); + + const url = parse(buildDfpVideoUrl({ + adUnit: adUnit, + bid: bidCopy, + params: { + 'iu': 'my/adUnit' + } + })); + const queryObject = utils.parseQS(url.query); expect(queryObject.gdpr).to.equal('1'); expect(queryObject.gdpr_consent).to.equal('consent'); expect(queryObject.addtl_consent).to.equal('moreConsent'); + gdprDataHandlerStub.restore(); }); it('should not include the GDPR keys when GDPR Consent is not available', function () { - const queryObject = getQueryParams() + const bidCopy = utils.deepClone(bid); + bidCopy.adserverTargeting = Object.assign(bidCopy.adserverTargeting, { + hb_adid: 'ad_id', + }); + + const url = parse(buildDfpVideoUrl({ + adUnit: adUnit, + bid: bidCopy, + params: { + 'iu': 'my/adUnit' + } + })); + const queryObject = utils.parseQS(url.query); expect(queryObject.gdpr).to.equal(undefined); expect(queryObject.gdpr_consent).to.equal(undefined); expect(queryObject.addtl_consent).to.equal(undefined); }); it('should only include the GDPR keys for GDPR Consent fields with values', function () { - sandbox.stub(gdprDataHandler, 'getConsentData').returns({ + let gdprDataHandlerStub = sinon.stub(gdprDataHandler, 'getConsentData'); + gdprDataHandlerStub.returns({ gdprApplies: true, consentString: 'consent', }); - const queryObject = getQueryParams() + + const bidCopy = utils.deepClone(bid); + bidCopy.adserverTargeting = Object.assign(bidCopy.adserverTargeting, { + hb_adid: 'ad_id', + }); + + const url = parse(buildDfpVideoUrl({ + adUnit: adUnit, + bid: bidCopy, + params: { + 'iu': 'my/adUnit' + } + })); + const queryObject = utils.parseQS(url.query); expect(queryObject.gdpr).to.equal('1'); expect(queryObject.gdpr_consent).to.equal('consent'); expect(queryObject.addtl_consent).to.equal(undefined); + gdprDataHandlerStub.restore(); }); + describe('GAM PPID', () => { let ppid; let getPPIDStub; @@ -194,283 +280,29 @@ describe('The DFP video support module', function () { 'url': {url: 'https://video.adserver.mock/', params: {'iu': 'mock/unit'}} }).forEach(([t, opts]) => { describe(`when using ${t}`, () => { + function buildUrlAndGetParams() { + const url = parse(buildDfpVideoUrl(Object.assign({ + adUnit: adUnit, + bid: deepClone(bid), + }, opts))); + return utils.parseQS(url.query); + } + it('should be included if available', () => { ppid = 'mockPPID'; - const q = getQueryParams(opts); + const q = buildUrlAndGetParams(); expect(q.ppid).to.equal('mockPPID'); }); it('should not be included if not available', () => { ppid = undefined; - const q = getQueryParams(opts); + const q = buildUrlAndGetParams(); expect(q.hasOwnProperty('ppid')).to.be.false; }) }) }) }) - describe('ORTB video parameters', () => { - Object.entries({ - plcmt: [ - { - video: { - plcmt: 1 - }, - expected: '1' - } - ], - min_ad_duration: [ - { - video: { - minduration: 123 - }, - expected: '123000' - } - ], - max_ad_duration: [ - { - video: { - maxduration: 321 - }, - expected: '321000' - } - ], - vpos: [ - { - video: { - startdelay: 0 - }, - expected: 'preroll' - }, - { - video: { - startdelay: -1 - }, - expected: 'midroll' - }, - { - video: { - startdelay: -2 - }, - expected: 'postroll' - }, - { - video: { - startdelay: 10 - }, - expected: 'midroll' - } - ], - vconp: [ - { - video: { - playbackmethod: [7] - }, - expected: '2' - }, - { - video: { - playbackmethod: [7, 1] - }, - expected: undefined - } - ], - vpa: [ - { - video: { - playbackmethod: [1, 2, 4, 5, 6, 7] - }, - expected: 'auto' - }, - { - video: { - playbackmethod: [3, 7], - }, - expected: 'click' - }, - { - video: { - playbackmethod: [1, 3], - }, - expected: undefined - } - ], - vpmute: [ - { - video: { - playbackmethod: [1, 3, 4, 5, 7] - }, - expected: '0' - }, - { - video: { - playbackmethod: [2, 6, 7], - }, - expected: '1' - }, - { - video: { - playbackmethod: [1, 2] - }, - expected: undefined - } - ] - }).forEach(([param, cases]) => { - describe(param, () => { - cases.forEach(({video, expected}) => { - describe(`when mediaTypes.video has ${JSON.stringify(video)}`, () => { - it(`fills in ${param} = ${expected}`, () => { - Object.assign(adUnit.mediaTypes.video, video); - expect(getQueryParams()[param]).to.eql(expected); - }); - it(`does not override pub-provided params.${param}`, () => { - Object.assign(adUnit.mediaTypes.video, video); - expect(getQueryParams({ - params: { - [param]: 'OG' - } - })[param]).to.eql('OG'); - }); - it('does not fill if param has no value', () => { - expect(getQueryParams().hasOwnProperty(param)).to.be.false; - }) - }) - }) - }) - }) - }); - - describe('ppsj', () => { - let ortb2; - beforeEach(() => { - ortb2 = null; - }) - - function getSignals() { - const ppsj = JSON.parse(atob(getQueryParams().ppsj)); - return Object.fromEntries(ppsj.PublisherProvidedTaxonomySignals.map(sig => [sig.taxonomy, sig.values])); - } - - Object.entries({ - 'FPD from bid request'() { - bid.requestId = 'req-id'; - sandbox.stub(auctionManager, 'index').get(() => stubAuctionIndex({ - bidRequests: [ - { - bidId: 'req-id', - ortb2 - } - ] - })); - }, - 'global FPD from auction'() { - bid.auctionId = 'auid'; - sandbox.stub(auctionManager, 'index').get(() => new AuctionIndex(() => [{ - getAuctionId: () => 'auid', - getFPD: () => ({ - global: ortb2 - }) - }])); - } - }).forEach(([t, setup]) => { - describe(`using ${t}`, () => { - beforeEach(setup); - it('does not fill if there\'s no segments in segtax 4 or 6', () => { - ortb2 = { - site: { - content: { - data: [ - { - segment: [ - {id: '1'}, - {id: '2'} - ] - }, - ] - } - }, - user: { - data: [ - { - ext: { - segtax: 1, - }, - segment: [ - {id: '3'} - ] - } - ] - } - } - expect(getQueryParams().ppsj).to.not.exist; - }); - - const SEGMENTS = [ - { - ext: { - segtax: 4, - }, - segment: [ - {id: '4-1'}, - {id: '4-2'} - ] - }, - { - ext: { - segtax: 4, - }, - segment: [ - {id: '4-2'}, - {id: '4-3'} - ] - }, - { - ext: { - segtax: 6, - }, - segment: [ - {id: '6-1'}, - {id: '6-2'} - ] - }, - { - ext: { - segtax: 6, - }, - segment: [ - {id: '6-2'}, - {id: '6-3'} - ] - }, - ] - - it('collects user.data segments with segtax = 4 into IAB_AUDIENCE_1_1', () => { - ortb2 = { - user: { - data: SEGMENTS - } - } - expect(getSignals()).to.eql({ - IAB_AUDIENCE_1_1: ['4-1', '4-2', '4-3'] - }) - }) - - it('collects site.content.data segments with segtax = 6 into IAB_CONTENT_2_2', () => { - ortb2 = { - site: { - content: { - data: SEGMENTS - } - } - } - expect(getSignals()).to.eql({ - IAB_CONTENT_2_2: ['6-1', '6-2', '6-3'] - }) - }) - }) - }) - }) - describe('special targeting unit test', function () { const allTargetingData = { 'hb_format': 'video', @@ -797,6 +629,7 @@ describe('The DFP video support module', function () { 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('us_privacy', '1YYY'); expect(queryParams).to.have.property('gdpr', '1'); expect(queryParams).to.have.property('gdpr_consent', 'consent'); expect(queryParams).to.have.property('addtl_consent', 'moreConsent'); diff --git a/test/spec/modules/dgkeywordRtdProvider_spec.js b/test/spec/modules/dgkeywordRtdProvider_spec.js index ff88ea0512f..754740b7a64 100644 --- a/test/spec/modules/dgkeywordRtdProvider_spec.js +++ b/test/spec/modules/dgkeywordRtdProvider_spec.js @@ -91,22 +91,6 @@ describe('Digital Garage Keyword Module', function () { expect(dgRtd.getTargetBidderOfDgKeywords(adUnits_no_target)).an('array') .that.is.empty; }); - it('convertKeywordsToString method unit test', function () { - const keywordsTest = [ - { keywords: { param1: 'keywords1' }, result: 'param1=keywords1' }, - { keywords: { param1: 'keywords1', param2: 'keywords2' }, result: 'param1=keywords1,param2=keywords2' }, - { keywords: { p1: 'k1', p2: 'k2', p: 'k' }, result: 'p1=k1,p2=k2,p=k' }, - { keywords: { p1: 'k1', p2: 'k2', p: ['k'] }, result: 'p1=k1,p2=k2,p=k' }, - { keywords: { p1: 'k1', p2: ['k21', 'k22'], p: ['k'] }, result: 'p1=k1,p2=k21,p2=k22,p=k' }, - { keywords: { p1: ['k11', 'k12', 'k13'], p2: ['k21', 'k22'], p: ['k'] }, result: 'p1=k11,p1=k12,p1=k13,p2=k21,p2=k22,p=k' }, - { keywords: { p1: [], p2: ['', ''], p: [''] }, result: 'p1,p2,p' }, - { keywords: { p1: 1, p2: [1, 'k2'], p: '' }, result: 'p1,p2=k2,p' }, - { keywords: { p1: ['k1', 2, 'k3'], p2: [1, 2], p: 3 }, result: 'p1=k1,p1=k3,p2,p' }, - ]; - for (const test of keywordsTest) { - expect(dgRtd.convertKeywordsToString(test.keywords)).equal(test.result); - } - }) it('should have targets', function () { const adUnits_targets = [ { @@ -258,16 +242,16 @@ describe('Digital Garage Keyword Module', function () { expect(targets[1].bidder).to.be.equal('dg2'); expect(targets[1].params.placementId).to.be.equal(99999998); expect(targets[1].params.dgkeyword).to.be.an('undefined'); - expect(targets[1].params.ortb2Imp).to.be.an('undefined'); + expect(targets[1].params.keywords).to.be.an('undefined'); targets = pbjs.adUnits[1].bids; expect(targets[0].bidder).to.be.equal('dg'); expect(targets[0].params.placementId).to.be.equal(99999996); expect(targets[0].params.dgkeyword).to.be.an('undefined'); - expect(targets[0].params.ortb2Imp).to.be.an('undefined'); + expect(targets[0].params.keywords).to.be.an('undefined'); expect(targets[2].bidder).to.be.equal('dg3'); expect(targets[2].params.placementId).to.be.equal(99999994); expect(targets[2].params.dgkeyword).to.be.an('undefined'); - expect(targets[2].params.ortb2Imp).to.be.an('undefined'); + expect(targets[2].params.keywords).to.be.an('undefined'); expect(pbjs.getBidderConfig()).to.be.deep.equal({}); @@ -291,16 +275,16 @@ describe('Digital Garage Keyword Module', function () { expect(targets[1].bidder).to.be.equal('dg2'); expect(targets[1].params.placementId).to.be.equal(99999998); expect(targets[1].params.dgkeyword).to.be.an('undefined'); - expect(targets[1].params.ortb2Imp).to.be.an('undefined'); + expect(targets[1].params.keywords).to.be.an('undefined'); targets = pbjs.adUnits[1].bids; expect(targets[0].bidder).to.be.equal('dg'); expect(targets[0].params.placementId).to.be.equal(99999996); expect(targets[0].params.dgkeyword).to.be.an('undefined'); - expect(targets[0].params.ortb2Imp).to.be.an('undefined'); + expect(targets[0].params.keywords).to.be.an('undefined'); expect(targets[2].bidder).to.be.equal('dg3'); expect(targets[2].params.placementId).to.be.equal(99999994); expect(targets[2].params.dgkeyword).to.be.an('undefined'); - expect(targets[2].params.ortb2Imp).to.be.an('undefined'); + expect(targets[2].params.keywords).to.be.an('undefined'); expect(pbjs.getBidderConfig()).to.be.deep.equal({}); @@ -334,16 +318,16 @@ describe('Digital Garage Keyword Module', function () { expect(targets[1].bidder).to.be.equal('dg2'); expect(targets[1].params.placementId).to.be.equal(99999998); expect(targets[1].params.dgkeyword).to.be.an('undefined'); - expect(targets[1].ortb2Imp.ext.data.keywords).to.be.deep.equal(dgRtd.convertKeywordsToString(SUCCESS_RESULT)); + expect(targets[1].params.keywords).to.be.deep.equal(SUCCESS_RESULT); targets = pbjs.adUnits[1].bids; expect(targets[0].bidder).to.be.equal('dg'); expect(targets[0].params.placementId).to.be.equal(99999996); expect(targets[0].params.dgkeyword).to.be.an('undefined'); - expect(targets[0].ortb2Imp.ext.data.keywords).to.be.deep.equal(dgRtd.convertKeywordsToString(SUCCESS_RESULT)); + expect(targets[0].params.keywords).to.be.deep.equal(SUCCESS_RESULT); expect(targets[2].bidder).to.be.equal('dg3'); expect(targets[2].params.placementId).to.be.equal(99999994); expect(targets[2].params.dgkeyword).to.be.an('undefined'); - expect(targets[2].ortb2Imp).to.be.an('undefined'); + expect(targets[2].params.keywords).to.be.an('undefined'); if (!IGNORE_SET_ORTB2) { expect(pbjs.getBidderConfig()).to.be.deep.equal({ diff --git a/test/spec/modules/discoveryBidAdapter_spec.js b/test/spec/modules/discoveryBidAdapter_spec.js index 961ccb33c4f..078add73046 100644 --- a/test/spec/modules/discoveryBidAdapter_spec.js +++ b/test/spec/modules/discoveryBidAdapter_spec.js @@ -1,17 +1,5 @@ import { expect } from 'chai'; -import { - spec, - getPmgUID, - storage, - getPageTitle, - getPageDescription, - getPageKeywords, - getConnectionDownLink, - THIRD_PARTY_COOKIE_ORIGIN, - COOKIE_KEY_MGUID, - getCurrentTimeToUTCString -} from 'modules/discoveryBidAdapter.js'; -import * as utils from 'src/utils.js'; +import { spec } from 'modules/discoveryBidAdapter.js'; describe('discovery:BidAdapterTests', function () { let bidRequestData = { @@ -23,59 +11,12 @@ describe('discovery:BidAdapterTests', function () { bidder: 'discovery', params: { token: 'd0f4902b616cc5c38cbe0a08676d0ed9', - siteId: 'siteId_01', - zoneId: 'zoneId_01', - publisher: '52', - position: 'left', - referrer: 'https://discovery.popin.cc', - }, - refererInfo: { - page: 'https://discovery.popin.cc', - stack: [ - 'a.com', - 'b.com' - ] }, mediaTypes: { banner: { sizes: [[300, 250]], - pos: 'left', - }, - }, - ortb2: { - user: { - ext: { - data: { - CxSegments: [] - } - } - }, - site: { - domain: 'discovery.popin.cc', - publisher: { - domain: 'discovery.popin.cc' - }, - page: 'https://discovery.popin.cc', - cat: ['IAB-19', 'IAB-20'], }, }, - ortb2Imp: { - ext: { - gpid: 'adslot_gpid', - tid: 'tid_01', - data: { - browsi: { - browsiViewability: 'NA', - }, - adserver: { - name: 'adserver_name', - adslot: 'adslot_name', - }, - keywords: ['travel', 'sport'], - pbadslot: '202309999' - } - } - }, adUnitCode: 'regular_iframe', transactionId: 'd163f9e2-7ecd-4c2c-a3bd-28ceb52a60ee', sizes: [[300, 250]], @@ -91,77 +32,6 @@ describe('discovery:BidAdapterTests', function () { }; let request = []; - let bidRequestDataNoParams = { - bidderCode: 'discovery', - auctionId: 'ff66e39e-4075-4d18-9854-56fde9b879ac', - bidderRequestId: '4fec04e87ad785', - bids: [ - { - bidder: 'discovery', - params: { - referrer: 'https://discovery.popin.cc', - }, - refererInfo: { - page: 'https://discovery.popin.cc', - stack: [ - 'a.com', - 'b.com' - ] - }, - mediaTypes: { - banner: { - sizes: [[300, 250]], - pos: 'left', - }, - }, - ortb2: { - user: { - ext: { - data: { - CxSegments: [] - } - } - }, - site: { - domain: 'discovery.popin.cc', - publisher: { - domain: 'discovery.popin.cc' - }, - page: 'https://discovery.popin.cc', - cat: ['IAB-19', 'IAB-20'], - }, - }, - ortb2Imp: { - ext: { - gpid: 'adslot_gpid', - tid: 'tid_01', - data: { - browsi: { - browsiViewability: 'NA', - }, - adserver: { - name: 'adserver_name', - adslot: 'adslot_name', - }, - keywords: ['travel', 'sport'], - pbadslot: '202309999' - } - } - }, - adUnitCode: 'regular_iframe', - transactionId: 'd163f9e2-7ecd-4c2c-a3bd-28ceb52a60ee', - sizes: [[300, 250]], - bidId: '276092a19e05eb', - bidderRequestId: '1fadae168708b', - auctionId: 'ff66e39e-4075-4d18-9854-56fde9b879ac', - src: 'client', - bidRequestsCount: 1, - bidderRequestsCount: 1, - bidderWinsCount: 0, - }, - ], - }; - it('discovery:validate_pub_params', function () { expect( spec.isBidRequestValid({ @@ -175,62 +45,12 @@ describe('discovery:BidAdapterTests', function () { ).to.equal(true); }); - it('isBidRequestValid:no_params', function () { - expect( - spec.isBidRequestValid({ - bidder: 'discovery', - params: {}, - }) - ).to.equal(true); - }); - it('discovery:validate_generated_params', function () { request = spec.buildRequests(bidRequestData.bids, bidRequestData); let req_data = JSON.parse(request.data); expect(req_data.imp).to.have.lengthOf(1); }); - describe('discovery: buildRequests', function() { - describe('getPmgUID function', function() { - let sandbox; - - beforeEach(() => { - sandbox = sinon.sandbox.create(); - sandbox.stub(storage, 'getCookie'); - sandbox.stub(storage, 'setCookie'); - sandbox.stub(utils, 'generateUUID').returns('new-uuid'); - sandbox.stub(storage, 'cookiesAreEnabled'); - }) - - afterEach(() => { - sandbox.restore(); - }); - - it('should generate new UUID and set cookie if not exists', () => { - storage.cookiesAreEnabled.callsFake(() => true); - storage.getCookie.callsFake(() => null); - const uid = getPmgUID(); - expect(uid).to.equal('new-uuid'); - expect(storage.setCookie.calledOnce).to.be.true; - }); - - it('should return existing UUID from cookie', () => { - storage.cookiesAreEnabled.callsFake(() => true); - storage.getCookie.callsFake(() => 'existing-uuid'); - const uid = getPmgUID(); - expect(uid).to.equal('existing-uuid'); - expect(storage.setCookie.called).to.be.false; - }); - - it('should not set new UUID when cookies are not enabled', () => { - storage.cookiesAreEnabled.callsFake(() => false); - storage.getCookie.callsFake(() => null); - getPmgUID(); - expect(storage.setCookie.calledOnce).to.be.false; - }); - }) - }); - it('discovery:validate_response_params', function () { let tempAdm = '' tempAdm += '%3Cscr'; @@ -270,324 +90,4 @@ describe('discovery:BidAdapterTests', function () { expect(bid.height).to.equal(250); expect(bid.currency).to.equal('USD'); }); - - describe('discovery: getUserSyncs', function() { - const COOKY_SYNC_IFRAME_URL = 'https://asset.popin.cc/js/cookieSync.html'; - const IFRAME_ENABLED = { - iframeEnabled: true, - pixelEnabled: false, - }; - const IFRAME_DISABLED = { - iframeEnabled: false, - pixelEnabled: false, - }; - const GDPR_CONSENT = { - consentString: 'gdprConsentString', - gdprApplies: true - }; - const USP_CONSENT = { - consentString: 'uspConsentString' - } - - let syncParamUrl = `dm=${encodeURIComponent(location.origin || `https://${location.host}`)}`; - syncParamUrl += '&gdpr=1&gdpr_consent=gdprConsentString&ccpa_consent=uspConsentString'; - const expectedIframeSyncs = [ - { - type: 'iframe', - url: `${COOKY_SYNC_IFRAME_URL}?${syncParamUrl}` - } - ]; - - it('should return nothing if iframe is disabled', () => { - const userSyncs = spec.getUserSyncs(IFRAME_DISABLED, undefined, GDPR_CONSENT, USP_CONSENT, undefined); - expect(userSyncs).to.be.undefined; - }); - - it('should do userSyncs if iframe is enabled', () => { - const userSyncs = spec.getUserSyncs(IFRAME_ENABLED, undefined, GDPR_CONSENT, USP_CONSENT, undefined); - expect(userSyncs).to.deep.equal(expectedIframeSyncs); - }); - }); -}); - -describe('discovery Bid Adapter Tests', function () { - describe('buildRequests', () => { - describe('getPageTitle function', function() { - let sandbox; - - beforeEach(() => { - sandbox = sinon.createSandbox(); - }); - - afterEach(() => { - sandbox.restore(); - }); - - it('should return the top document title if available', function() { - const fakeTopDocument = { - title: 'Top Document Title', - querySelector: () => ({ content: 'Top Document Title test' }) - }; - const fakeTopWindow = { - document: fakeTopDocument - }; - const result = getPageTitle({ top: fakeTopWindow }); - expect(result).to.equal('Top Document Title'); - }); - - it('should return the content of top og:title meta tag if title is empty', function() { - const ogTitleContent = 'Top OG Title Content'; - const fakeTopWindow = { - document: { - title: '', - querySelector: sandbox.stub().withArgs('meta[property="og:title"]').returns({ content: ogTitleContent }) - } - }; - - const result = getPageTitle({ top: fakeTopWindow }); - expect(result).to.equal(ogTitleContent); - }); - - it('should return the document title if no og:title meta tag is present', function() { - document.title = 'Test Page Title'; - sandbox.stub(document, 'querySelector').withArgs('meta[property="og:title"]').returns(null); - - const result = getPageTitle({ top: undefined }); - expect(result).to.equal('Test Page Title'); - }); - - it('should return the content of og:title meta tag if present', function() { - document.title = ''; - const ogTitleContent = 'Top OG Title Content'; - sandbox.stub(document, 'querySelector').withArgs('meta[property="og:title"]').returns({ content: ogTitleContent }); - const result = getPageTitle({ top: undefined }); - expect(result).to.equal(ogTitleContent); - }); - - it('should return an empty string if no title or og:title meta tag is found', function() { - document.title = ''; - sandbox.stub(document, 'querySelector').withArgs('meta[property="og:title"]').returns(null); - const result = getPageTitle({ top: undefined }); - expect(result).to.equal(''); - }); - - it('should handle exceptions when accessing top.document and fallback to current document', function() { - const fakeWindow = { - get top() { - throw new Error('Access denied'); - } - }; - const ogTitleContent = 'Current OG Title Content'; - document.title = 'Current Document Title'; - sandbox.stub(document, 'querySelector').withArgs('meta[property="og:title"]').returns({ content: ogTitleContent }); - const result = getPageTitle(fakeWindow); - expect(result).to.equal('Current Document Title'); - }); - }); - - describe('getPageDescription function', function() { - let sandbox; - - beforeEach(() => { - sandbox = sinon.createSandbox(); - }); - - afterEach(() => { - sandbox.restore(); - }); - - it('should return the top document description if available', function() { - const descriptionContent = 'Top Document Description'; - const fakeTopDocument = { - querySelector: sandbox.stub().withArgs('meta[name="description"]').returns({ content: descriptionContent }) - }; - const fakeTopWindow = { document: fakeTopDocument }; - const result = getPageDescription({ top: fakeTopWindow }); - expect(result).to.equal(descriptionContent); - }); - - it('should return the top document og:description if description is not present', function() { - const ogDescriptionContent = 'Top OG Description'; - const fakeTopDocument = { - querySelector: sandbox.stub().withArgs('meta[property="og:description"]').returns({ content: ogDescriptionContent }) - }; - const fakeTopWindow = { document: fakeTopDocument }; - const result = getPageDescription({ top: fakeTopWindow }); - expect(result).to.equal(ogDescriptionContent); - }); - - it('should return the current document description if top document is not accessible', function() { - const descriptionContent = 'Current Document Description'; - sandbox.stub(document, 'querySelector') - .withArgs('meta[name="description"]').returns({ content: descriptionContent }) - const fakeWindow = { - get top() { - throw new Error('Access denied'); - } - }; - const result = getPageDescription(fakeWindow); - expect(result).to.equal(descriptionContent); - }); - - it('should return the current document og:description if description is not present and top document is not accessible', function() { - const ogDescriptionContent = 'Current OG Description'; - sandbox.stub(document, 'querySelector') - .withArgs('meta[property="og:description"]').returns({ content: ogDescriptionContent }); - - const fakeWindow = { - get top() { - throw new Error('Access denied'); - } - }; - const result = getPageDescription(fakeWindow); - expect(result).to.equal(ogDescriptionContent); - }); - }); - - describe('getPageKeywords function', function() { - let sandbox; - - beforeEach(() => { - sandbox = sinon.createSandbox(); - }); - - afterEach(() => { - sandbox.restore(); - }); - - it('should return the top document keywords if available', function() { - const keywordsContent = 'keyword1, keyword2, keyword3'; - const fakeTopDocument = { - querySelector: sandbox.stub() - .withArgs('meta[name="keywords"]').returns({ content: keywordsContent }) - }; - const fakeTopWindow = { document: fakeTopDocument }; - - const result = getPageKeywords({ top: fakeTopWindow }); - expect(result).to.equal(keywordsContent); - }); - - it('should return the current document keywords if top document is not accessible', function() { - const keywordsContent = 'keyword1, keyword2, keyword3'; - sandbox.stub(document, 'querySelector') - .withArgs('meta[name="keywords"]').returns({ content: keywordsContent }); - - // æ¨Ąæ‹ŸéĄļåą‚įĒ—åŖčŽŋ问åŧ‚常 - const fakeWindow = { - get top() { - throw new Error('Access denied'); - } - }; - - const result = getPageKeywords(fakeWindow); - expect(result).to.equal(keywordsContent); - }); - - it('should return an empty string if no keywords meta tag is found', function() { - sandbox.stub(document, 'querySelector').withArgs('meta[name="keywords"]').returns(null); - - const result = getPageKeywords(); - expect(result).to.equal(''); - }); - }); - describe('getConnectionDownLink function', function() { - let sandbox; - - beforeEach(() => { - sandbox = sinon.createSandbox(); - }); - - afterEach(() => { - sandbox.restore(); - }); - - it('should return the downlink value as a string if available', function() { - const downlinkValue = 2.5; - const fakeNavigator = { - connection: { - downlink: downlinkValue - } - }; - - const result = getConnectionDownLink({ navigator: fakeNavigator }); - expect(result).to.equal(downlinkValue.toString()); - }); - - it('should return undefined if downlink is not available', function() { - const fakeNavigator = { - connection: {} - }; - - const result = getConnectionDownLink({ navigator: fakeNavigator }); - expect(result).to.be.undefined; - }); - - it('should return undefined if connection is not available', function() { - const fakeNavigator = {}; - - const result = getConnectionDownLink({ navigator: fakeNavigator }); - expect(result).to.be.undefined; - }); - - it('should handle cases where navigator is not defined', function() { - const result = getConnectionDownLink({}); - expect(result).to.be.undefined; - }); - }); - - describe('getUserSyncs with message event listener', function() { - function messageHandler(event) { - if (!event.data || event.origin !== THIRD_PARTY_COOKIE_ORIGIN) { - return; - } - - window.removeEventListener('message', messageHandler, true); - event.stopImmediatePropagation(); - - const response = event.data; - if (!response.optout && response.mguid) { - storage.setCookie(COOKIE_KEY_MGUID, response.mguid, getCurrentTimeToUTCString()); - } - } - - let sandbox; - - beforeEach(() => { - sandbox = sinon.createSandbox(); - sandbox.stub(storage, 'setCookie'); - sandbox.stub(window, 'removeEventListener'); - }); - - afterEach(() => { - sandbox.restore(); - }); - - it('should set a cookie when a valid message is received', () => { - const fakeEvent = { - data: { optout: '', mguid: '12345' }, - origin: THIRD_PARTY_COOKIE_ORIGIN, - stopImmediatePropagation: sinon.spy() - }; - - messageHandler(fakeEvent); - - expect(fakeEvent.stopImmediatePropagation.calledOnce).to.be.true; - expect(window.removeEventListener.calledWith('message', messageHandler, true)).to.be.true; - expect(storage.setCookie.calledWith(COOKIE_KEY_MGUID, '12345', sinon.match.string)).to.be.true; - }); - it('should not do anything when an invalid message is received', () => { - const fakeEvent = { - data: null, - origin: 'http://invalid-origin.com', - stopImmediatePropagation: sinon.spy() - }; - - messageHandler(fakeEvent); - - expect(fakeEvent.stopImmediatePropagation.notCalled).to.be.true; - expect(window.removeEventListener.notCalled).to.be.true; - expect(storage.setCookie.notCalled).to.be.true; - }); - }); - }); }); diff --git a/test/spec/modules/dmdIdSystem_spec.js b/test/spec/modules/dmdIdSystem_spec.js index 16c32f184a3..3096a8e55f5 100644 --- a/test/spec/modules/dmdIdSystem_spec.js +++ b/test/spec/modules/dmdIdSystem_spec.js @@ -60,7 +60,7 @@ describe('Dmd ID System', function () { it('Should invoke callback with response from API call', function () { const callbackSpy = sinon.spy(); - const domain = utils.getWindowLocation().href; + const domain = utils.getWindowLocation() const callback = dmdIdSubmodule.getId(config).callback; callback(callbackSpy); const request = server.requests[0]; @@ -73,7 +73,7 @@ describe('Dmd ID System', function () { it('Should log error if API response is not valid', function () { const callbackSpy = sinon.spy(); - const domain = utils.getWindowLocation().href; + const domain = utils.getWindowLocation() const callback = dmdIdSubmodule.getId(config).callback; callback(callbackSpy); const request = server.requests[0]; diff --git a/test/spec/modules/docereeAdManagerBidAdapter_spec.js b/test/spec/modules/docereeAdManagerBidAdapter_spec.js deleted file mode 100644 index 26b054f4e29..00000000000 --- a/test/spec/modules/docereeAdManagerBidAdapter_spec.js +++ /dev/null @@ -1,125 +0,0 @@ -import { expect } from 'chai'; -import { spec } from '../../../modules/docereeAdManagerBidAdapter.js'; -import { config } from '../../../src/config.js'; - -describe('docereeadmanager', function () { - config.setConfig({ - docereeadmanager: { - user: { - data: { - email: '', - firstname: '', - lastname: '', - mobile: '', - specialization: '', - organization: '', - hcpid: '', - dob: '', - gender: '', - city: '', - state: '', - country: '', - hashedhcpid: '', - hashedemail: '', - hashedmobile: '', - userid: '', - zipcode: '', - userconsent: '', - }, - }, - }, - }); - let bid = { - bidId: 'testing', - bidder: 'docereeadmanager', - params: { - placementId: 'DOC-19-1', - gdpr: '1', - gdprconsent: - 'CPQfU1jPQfU1jG0AAAENAwCAAAAAAAAAAAAAAAAAAAAA.IGLtV_T9fb2vj-_Z99_tkeYwf95y3p-wzhheMs-8NyZeH_B4Wv2MyvBX4JiQKGRgksjLBAQdtHGlcTQgBwIlViTLMYk2MjzNKJrJEilsbO2dYGD9Pn8HT3ZCY70-vv__7v3ff_3g', - }, - }; - - describe('isBidRequestValid', function () { - it('Should return true if placementId is present', function () { - expect(spec.isBidRequestValid(bid)).to.be.true; - }); - it('Should return false if placementId is not present', function () { - delete bid.params.placementId; - expect(spec.isBidRequestValid(bid)).to.be.false; - }); - }); - - describe('isGdprConsentPresent', function () { - it('Should return true if gdpr consent is present', function () { - expect(spec.isGdprConsentPresent(bid)).to.be.true; - }); - }); - - describe('buildRequests', function () { - let serverRequest = spec.buildRequests([bid]); - serverRequest = serverRequest[0]; - 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://dai.doceree.com/drs/quest'); - }); - }); - describe('interpretResponse', function () { - it('Should interpret banner response', function () { - const banner = { - body: { - cpm: 3.576, - currency: 'USD', - width: 250, - height: 300, - ad: '

I am an ad

', - ttl: 30, - creativeId: 'div-1', - netRevenue: false, - bidderCode: '123', - dealId: 232, - requestId: '123', - meta: { - brandId: null, - advertiserDomains: ['https://dai.doceree.com/drs/quest'], - }, - }, - }; - let bannerResponses = spec.interpretResponse(banner); - expect(bannerResponses).to.be.an('array').that.is.not.empty; - let dataItem = bannerResponses[0]; - expect(dataItem).to.have.all.keys( - 'requestId', - 'cpm', - 'width', - 'height', - 'ad', - 'ttl', - 'netRevenue', - 'currency', - 'mediaType', - 'creativeId', - 'meta' - ); - expect(dataItem.requestId).to.equal('123'); - expect(dataItem.cpm).to.equal(3.576); - expect(dataItem.width).to.equal(250); - expect(dataItem.height).to.equal(300); - expect(dataItem.ad).to.equal('

I am an ad

'); - expect(dataItem.ttl).to.equal(30); - expect(dataItem.netRevenue).to.be.true; - expect(dataItem.currency).to.equal('USD'); - expect(dataItem.creativeId).to.equal('div-1'); - expect(dataItem.meta.advertiserDomains).to.be.an('array').that.is.not - .empty; - }); - }); -}); diff --git a/test/spec/modules/docereeBidAdapter_spec.js b/test/spec/modules/docereeBidAdapter_spec.js index 25da8b256fc..dadbb56b0c0 100644 --- a/test/spec/modules/docereeBidAdapter_spec.js +++ b/test/spec/modules/docereeBidAdapter_spec.js @@ -1,7 +1,6 @@ import {expect} from 'chai'; import {spec} from '../../../modules/docereeBidAdapter.js'; import { config } from '../../../src/config.js'; -import * as utils from 'src/utils.js'; describe('BidlabBidAdapter', function () { config.setConfig({ @@ -103,36 +102,4 @@ describe('BidlabBidAdapter', function () { expect(dataItem.meta.advertiserDomains[0]).to.equal('doceree.com') }); }) - describe('onBidWon', function () { - beforeEach(function() { - sinon.stub(utils, 'triggerPixel'); - }); - afterEach(function() { - utils.triggerPixel.restore(); - }); - it('exists and is a function', () => { - expect(spec.onBidWon).to.exist.and.to.be.a('function'); - }); - it('should return nothing', function () { - var response = spec.onBidWon({}); - expect(response).to.be.an('undefined') - expect(utils.triggerPixel.called).to.equal(true); - }); - }); - describe('onTimeout', function () { - beforeEach(function() { - sinon.stub(utils, 'triggerPixel'); - }); - afterEach(function() { - utils.triggerPixel.restore(); - }); - it('exists and is a function', () => { - expect(spec.onTimeout).to.exist.and.to.be.a('function'); - }); - it('should return nothing', function () { - var response = spec.onBidWon([]); - expect(response).to.be.an('undefined') - expect(utils.triggerPixel.called).to.equal(true); - }); - }); }); diff --git a/test/spec/modules/dsp_genieeBidAdapter_spec.js b/test/spec/modules/dsp_genieeBidAdapter_spec.js deleted file mode 100644 index 94ec1011fbf..00000000000 --- a/test/spec/modules/dsp_genieeBidAdapter_spec.js +++ /dev/null @@ -1,173 +0,0 @@ -import { expect } from 'chai'; -import { spec } from 'modules/dsp_genieeBidAdapter.js'; -import { config } from 'src/config'; - -describe('Geniee adapter tests', () => { - const validBidderRequest = { - code: 'sample_request', - bids: [{ - bidId: 'bid-id', - bidder: 'dsp_geniee', - params: { - test: 1 - } - }], - gdprConsent: { - gdprApplies: false - }, - uspConsent: '1YNY' - }; - - describe('isBidRequestValid function test', () => { - it('valid', () => { - expect(spec.isBidRequestValid(validBidderRequest.bids[0])).equal(true); - }); - }); - describe('buildRequests function test', () => { - it('auction', () => { - const request = spec.buildRequests(validBidderRequest.bids, validBidderRequest); - const auction_id = request.data.id; - expect(request).deep.equal({ - method: 'POST', - url: 'https://rt.gsspat.jp/prebid_auction', - data: { - at: 1, - id: auction_id, - imp: [ - { - ext: { - test: 1 - }, - id: 'bid-id' - } - ], - test: 1 - }, - }); - }); - it('uncomfortable (gdpr)', () => { - validBidderRequest.gdprConsent.gdprApplies = true; - const request = spec.buildRequests(validBidderRequest.bids, validBidderRequest); - expect(request).deep.equal({ - method: 'GET', - url: 'https://rt.gsspat.jp/prebid_uncomfortable', - }); - validBidderRequest.gdprConsent.gdprApplies = false; - }); - it('uncomfortable (usp)', () => { - validBidderRequest.uspConsent = '1YYY'; - const request = spec.buildRequests(validBidderRequest.bids, validBidderRequest); - expect(request).deep.equal({ - method: 'GET', - url: 'https://rt.gsspat.jp/prebid_uncomfortable', - }); - validBidderRequest.uspConsent = '1YNY'; - }); - it('uncomfortable (coppa)', () => { - config.setConfig({ coppa: true }); - const request = spec.buildRequests(validBidderRequest.bids, validBidderRequest); - expect(request).deep.equal({ - method: 'GET', - url: 'https://rt.gsspat.jp/prebid_uncomfortable', - }); - config.resetConfig(); - }); - it('uncomfortable (currency)', () => { - config.setConfig({ currency: { adServerCurrency: 'TWD' } }); - const request = spec.buildRequests(validBidderRequest.bids, validBidderRequest); - expect(request).deep.equal({ - method: 'GET', - url: 'https://rt.gsspat.jp/prebid_uncomfortable', - }); - config.resetConfig(); - }); - }); - describe('interpretResponse function test', () => { - it('sample bid', () => { - const request = spec.buildRequests(validBidderRequest.bids, validBidderRequest); - const auction_id = request.data.id; - const adm = "\n"; - const serverResponse = { - body: { - id: auction_id, - cur: 'JPY', - seatbid: [{ - bid: [{ - id: '7b77235d599e06d289e58ddfa9390443e22d7071', - impid: 'bid-id', - price: 0.6666000000000001, - adid: '8405715', - adm: adm, - adomain: ['geniee.co.jp'], - iurl: 'http://img.gsspat.jp/e/068c8e1eafbf0cb6ac1ee95c36152bd2/04f4bd4e6b71f978d343d84ecede3877.png', - cid: '8405715', - crid: '1383823', - cat: ['IAB1'], - w: 300, - h: 250, - mtype: 1 - }] - }] - } - }; - const bids = spec.interpretResponse(serverResponse, request); - expect(bids).deep.equal([{ - ad: adm, - cpm: 0.6666000000000001, - creativeId: '1383823', - creative_id: '1383823', - height: 250, - width: 300, - currency: 'JPY', - mediaType: 'banner', - meta: { - advertiserDomains: ['geniee.co.jp'] - }, - netRevenue: true, - requestId: 'bid-id', - seatBidId: '7b77235d599e06d289e58ddfa9390443e22d7071', - ttl: 300 - }]); - }); - it('no bid', () => { - const serverResponse = {}; - const bids = spec.interpretResponse(serverResponse, validBidderRequest); - expect(bids).deep.equal([]); - }); - }); - describe('getUserSyncs function test', () => { - it('sync enabled', () => { - const syncOptions = { - iframeEnabled: true, - pixelEnabled: true - }; - const serverResponses = []; - const syncs = spec.getUserSyncs(syncOptions, serverResponses); - expect(syncs).deep.equal([{ - type: 'image', - url: 'https://rt.gsspat.jp/prebid_cs' - }]); - }); - it('sync disabled (option false)', () => { - const syncOptions = { - iframeEnabled: false, - pixelEnabled: false - }; - const serverResponses = []; - const syncs = spec.getUserSyncs(syncOptions, serverResponses); - expect(syncs).deep.equal([]); - }); - it('sync disabled (gdpr)', () => { - const syncOptions = { - iframeEnabled: true, - pixelEnabled: true - }; - const serverResponses = []; - const gdprConsent = { - gdprApplies: true - }; - const syncs = spec.getUserSyncs(syncOptions, serverResponses, gdprConsent); - expect(syncs).deep.equal([]); - }); - }); -}); diff --git a/test/spec/modules/dxkultureBidAdapter_spec.js b/test/spec/modules/dxkultureBidAdapter_spec.js deleted file mode 100644 index a752c81cb6e..00000000000 --- a/test/spec/modules/dxkultureBidAdapter_spec.js +++ /dev/null @@ -1,649 +0,0 @@ -import {expect} from 'chai'; -import {spec, SYNC_URL} from 'modules/dxkultureBidAdapter.js'; -import {BANNER, VIDEO} from 'src/mediaTypes.js'; - -const getBannerRequest = () => { - return { - bidderCode: 'dxkulture', - auctionId: 'ba87bfdf-493e-4a88-8e26-17b4cbc9adbd', - bidderRequestId: 'bidderRequestId', - bids: [ - { - bidder: 'dxkulture', - params: { - placementId: 123456, - publisherId: 'publisherId', - bidfloor: 10, - }, - auctionId: 'auctionId-56a2-4f71-9098-720a68f2f708', - placementCode: 'div-gpt-dummy-placement-code', - mediaTypes: { - banner: { - sizes: [ - [ 300, 250 ], - ] - } - }, - bidId: '2e9f38ea93bb9e', - bidderRequestId: 'bidderRequestId', - } - ], - start: 1487883186070, - auctionStart: 1487883186069, - timeout: 3000 - } -}; - -const getVideoRequest = () => { - return { - bidderCode: 'dxkulture', - auctionId: 'e158486f-8c7f-472f-94ce-b0cbfbb50ab4', - bidderRequestId: '34feaad34lkj2', - bids: [{ - mediaTypes: { - video: { - context: 'instream', - playerSize: [[640, 480]], - } - }, - bidder: 'dxkulture', - sizes: [640, 480], - bidId: '30b3efwfwe1e', - adUnitCode: 'video1', - params: { - video: { - playerWidth: 640, - playerHeight: 480, - mimes: ['video/mp4', 'application/javascript'], - protocols: [2, 5], - api: [2], - position: 1, - delivery: [2], - sid: 134, - rewarded: 1, - placement: 1, - plcmt: 1, - hp: 1, - inventoryid: 123 - }, - site: { - id: 1, - page: 'https://test.com', - referrer: 'http://test.com' - }, - publisherId: 'km123', - bidfloor: 10, - } - }, { - mediaTypes: { - video: { - context: 'instream', - playerSize: [[640, 480]], - } - }, - bidder: 'dxkulture', - sizes: [640, 480], - bidId: '30b3efwfwe2e', - adUnitCode: 'video1', - params: { - video: { - playerWidth: 640, - playerHeight: 480, - mimes: ['video/mp4', 'application/javascript'], - protocols: [2, 5], - api: [2], - position: 1, - delivery: [2], - sid: 134, - rewarded: 1, - placement: 1, - plcmt: 1, - hp: 1, - inventoryid: 123 - }, - site: { - id: 1, - page: 'https://test.com', - referrer: 'http://test.com' - }, - publisherId: 'km123', - bidfloor: 10, - } - }], - auctionStart: 1520001292880, - timeout: 5000, - start: 1520001292884, - doneCbCallCount: 0, - refererInfo: { - numIframes: 1, - reachedTop: true, - referer: 'test.com' - } - }; -}; - -const getBidderResponse = () => { - return { - headers: null, - body: { - id: 'bid-response', - seatbid: [ - { - bid: [ - { - id: '2e9f38ea93bb9e', - impid: '2e9f38ea93bb9e', - price: 0.18, - adm: '', - adid: '144762342', - adomain: [ - 'https://dummydomain.com' - ], - iurl: 'iurl', - cid: '109', - crid: 'creativeId', - cat: [], - w: 300, - h: 250, - ext: { - prebid: { - type: 'banner' - }, - bidder: { - appnexus: { - brand_id: 334553, - auction_id: 514667951122925701, - bidder_id: 2, - bid_ad_type: 0 - } - } - } - } - ], - seat: 'dxkulture' - } - ], - ext: { - usersync: { - sovrn: { - status: 'none', - syncs: [ - { - url: 'urlsovrn', - type: 'iframe' - } - ] - }, - appnexus: { - status: 'none', - syncs: [ - { - url: 'urlappnexus', - type: 'pixel' - } - ] - } - }, - responsetimemillis: { - appnexus: 127 - } - } - } - }; -} - -describe('dxkultureBidAdapter', function() { - let videoBidRequest; - - const VIDEO_REQUEST = { - 'bidderCode': 'dxkulture', - 'auctionId': 'e158486f-8c7f-472f-94ce-b0cbfbb50ab4', - 'bidderRequestId': '34feaad34lkj2', - 'bids': videoBidRequest, - 'auctionStart': 1520001292880, - 'timeout': 3000, - 'start': 1520001292884, - 'doneCbCallCount': 0, - 'refererInfo': { - 'numIframes': 1, - 'reachedTop': true, - 'referer': 'test.com' - } - }; - - beforeEach(function () { - videoBidRequest = { - mediaTypes: { - video: { - context: 'instream', - playerSize: [[640, 480]], - } - }, - bidder: 'dxkulture', - sizes: [640, 480], - bidId: '30b3efwfwe1e', - adUnitCode: 'video1', - params: { - video: { - playerWidth: 640, - playerHeight: 480, - mimes: ['video/mp4', 'application/javascript'], - protocols: [2, 5], - api: [2], - position: 1, - delivery: [2], - sid: 134, - rewarded: 1, - placement: 1, - plcmt: 1, - hp: 1, - inventoryid: 123 - }, - site: { - id: 1, - page: 'https://test.com', - referrer: 'http://test.com' - }, - publisherId: 'km123', - bidfloor: 0 - } - }; - }); - - describe('isValidRequest', function() { - let bidderRequest; - - beforeEach(function() { - bidderRequest = getBannerRequest(); - }); - - it('should accept request if placementId and publisherId are passed', function () { - expect(spec.isBidRequestValid(bidderRequest.bids[0])).to.be.true; - }); - - it('reject requests without params', function () { - bidderRequest.bids[0].params = {}; - expect(spec.isBidRequestValid(bidderRequest.bids[0])).to.be.false; - }); - - it('returns false when banner mediaType does not exist', function () { - bidderRequest.bids[0].mediaTypes = {} - expect(spec.isBidRequestValid(bidderRequest.bids[0])).to.be.false; - }); - }); - - describe('buildRequests', function() { - let bidderRequest; - - beforeEach(function() { - bidderRequest = getBannerRequest(); - }); - - it('should return expected request object', function() { - const bidRequest = spec.buildRequests(bidderRequest.bids, bidderRequest); - expect(bidRequest.url).equal('https://ads.dxkulture.com/pbjs?pid=publisherId&placementId=123456'); - expect(bidRequest.method).equal('POST'); - }); - }); - - context('banner validation', function () { - let bidderRequest; - - beforeEach(function() { - bidderRequest = getBannerRequest(); - }); - - it('returns true when banner sizes are defined', function () { - const bid = { - bidder: 'dxkulture', - mediaTypes: { - banner: { - sizes: [[250, 300]] - } - }, - params: { - placementId: 'placementId', - publisherId: 'publisherId', - } - }; - - expect(spec.isBidRequestValid(bidderRequest.bids[0])).to.be.true; - }); - - it('returns false when banner sizes are invalid', function () { - const invalidSizes = [ - undefined, - '2:1', - 123, - 'test' - ]; - - invalidSizes.forEach((sizes) => { - const bid = { - bidder: 'dxkulture', - mediaTypes: { - banner: { - sizes - } - }, - params: { - placementId: 'placementId', - publisherId: 'publisherId', - } - }; - - expect(spec.isBidRequestValid(bid)).to.be.false; - }); - }); - }); - - context('video validation', function () { - beforeEach(function () { - // Basic Valid BidRequest - this.bid = { - bidder: 'dxkulture', - mediaTypes: { - video: { - playerSize: [[300, 50]], - context: 'instream', - mimes: ['foo', 'bar'], - protocols: [1, 2] - } - }, - params: { - placementId: 'placementId', - publisherId: 'publisherId', - } - }; - }); - - it('should return true (skip validations) when e2etest = true', function () { - this.bid.params = { - e2etest: true - }; - expect(spec.isBidRequestValid(this.bid)).to.equal(true); - }); - - it('returns false when video context is not defined', function () { - delete this.bid.mediaTypes.video.context; - - expect(spec.isBidRequestValid(this.bid)).to.be.false; - }); - - it('returns false when video playserSize is invalid', function () { - const invalidSizes = [ - undefined, - '2:1', - 123, - 'test' - ]; - - invalidSizes.forEach((playerSize) => { - this.bid.mediaTypes.video.playerSize = playerSize; - expect(spec.isBidRequestValid(this.bid)).to.be.false; - }); - }); - - it('returns false when video mimes is invalid', function () { - const invalidMimes = [ - undefined, - 'test', - 1, - [] - ] - - invalidMimes.forEach((mimes) => { - this.bid.mediaTypes.video.mimes = mimes; - expect(spec.isBidRequestValid(this.bid)).to.be.false; - }) - }); - - it('returns false when video protocols is invalid', function () { - const invalidMimes = [ - undefined, - 'test', - 1, - [] - ] - - invalidMimes.forEach((protocols) => { - this.bid.mediaTypes.video.protocols = protocols; - expect(spec.isBidRequestValid(this.bid)).to.be.false; - }) - }); - }); - - describe('buildRequests', function () { - let bidderBannerRequest; - let bidRequestsWithMediaTypes; - let mockBidderRequest; - - beforeEach(function() { - bidderBannerRequest = getBannerRequest(); - - mockBidderRequest = {refererInfo: {}}; - - bidRequestsWithMediaTypes = [{ - bidder: 'dxkulture', - params: { - publisherId: 'km123', - }, - adUnitCode: '/adunit-code/test-path', - mediaTypes: { - banner: { - sizes: [[300, 250], [300, 600]] - } - }, - bidId: 'test-bid-id-1', - bidderRequestId: 'test-bid-request-1', - auctionId: 'test-auction-1', - transactionId: 'test-transactionId-1', - ortb2Imp: { - ext: { - ae: 2 - } - } - }, { - bidder: 'dxkulture', - params: { - publisherId: 'km123', - }, - adUnitCode: 'adunit-code', - mediaTypes: { - video: { - playerSize: [640, 480], - placement: 1, - plcmt: 1, - } - }, - bidId: 'test-bid-id-2', - bidderRequestId: 'test-bid-request-2', - auctionId: 'test-auction-2', - transactionId: 'test-transactionId-2' - }]; - }); - - context('when mediaType is banner', function () { - it('creates request data', function () { - let request = spec.buildRequests(bidderBannerRequest.bids, bidderBannerRequest) - - expect(request).to.exist.and.to.be.a('object'); - const payload = request.data; - expect(payload.imp[0]).to.have.property('id', bidderBannerRequest.bids[0].bidId); - }); - - it('has gdpr data if applicable', function () { - const req = Object.assign({}, getBannerRequest(), { - gdprConsent: { - consentString: 'consentString', - gdprApplies: true, - } - }); - let request = spec.buildRequests(bidderBannerRequest.bids, req); - - const payload = request.data; - expect(payload.user.ext).to.have.property('consent', req.gdprConsent.consentString); - expect(payload.regs.ext).to.have.property('gdpr', 1); - }); - }); - - if (FEATURES.VIDEO) { - context('video', function () { - it('should create a POST request for every bid', function () { - const requests = spec.buildRequests(bidRequestsWithMediaTypes, mockBidderRequest); - expect(requests.method).to.equal('POST'); - expect(requests.url.trim()).to.equal(spec.ENDPOINT + '?pid=' + videoBidRequest.params.publisherId); - }); - - it('should attach request data', function () { - const requests = spec.buildRequests(bidRequestsWithMediaTypes, mockBidderRequest); - const data = requests.data; - const [width, height] = videoBidRequest.sizes; - const VERSION = '1.0.0'; - - expect(data.imp[1].video.w).to.equal(width); - expect(data.imp[1].video.h).to.equal(height); - expect(data.imp[1].bidfloor).to.equal(videoBidRequest.params.bidfloor); - expect(data.imp[1]['video']['placement']).to.equal(videoBidRequest.params.video['placement']); - expect(data.imp[1]['video']['plcmt']).to.equal(videoBidRequest.params.video['plcmt']); - expect(data.ext.prebidver).to.equal('$prebid.version$'); - expect(data.ext.adapterver).to.equal(spec.VERSION); - }); - - it('should set pubId to e2etest when bid.params.e2etest = true', function () { - bidRequestsWithMediaTypes[0].params.e2etest = true; - const requests = spec.buildRequests(bidRequestsWithMediaTypes, mockBidderRequest); - expect(requests.method).to.equal('POST'); - expect(requests.url).to.equal(spec.ENDPOINT + '?pid=e2etest'); - }); - - it('should attach End 2 End test data', function () { - bidRequestsWithMediaTypes[1].params.e2etest = true; - const requests = spec.buildRequests(bidRequestsWithMediaTypes, mockBidderRequest); - const data = requests.data; - expect(data.imp[1].bidfloor).to.equal(0); - expect(data.imp[1].video.w).to.equal(640); - expect(data.imp[1].video.h).to.equal(480); - }); - }); - } - }); - - describe('interpretResponse', function() { - context('when mediaType is banner', function() { - let bidRequest, bidderResponse; - beforeEach(function() { - const bidderRequest = getBannerRequest(); - bidRequest = spec.buildRequests(bidderRequest.bids, bidderRequest); - bidderResponse = getBidderResponse(); - }); - - it('handles empty response', function () { - const EMPTY_RESP = Object.assign({}, bidderResponse, {'body': {}}); - const bids = spec.interpretResponse(EMPTY_RESP, bidRequest); - - expect(bids).to.be.empty; - }); - - it('have bids', function () { - let bids = spec.interpretResponse(bidderResponse, bidRequest); - expect(bids).to.be.an('array').that.is.not.empty; - validateBidOnIndex(0); - - function validateBidOnIndex(index) { - expect(bids[index]).to.have.property('currency', 'USD'); - expect(bids[index]).to.have.property('requestId', getBidderResponse().body.seatbid[0].bid[index].impid); - expect(bids[index]).to.have.property('cpm', getBidderResponse().body.seatbid[0].bid[index].price); - expect(bids[index]).to.have.property('width', getBidderResponse().body.seatbid[0].bid[index].w); - expect(bids[index]).to.have.property('height', getBidderResponse().body.seatbid[0].bid[index].h); - expect(bids[index]).to.have.property('ad', getBidderResponse().body.seatbid[0].bid[index].adm); - expect(bids[index]).to.have.property('creativeId', getBidderResponse().body.seatbid[0].bid[index].crid); - expect(bids[index].meta).to.have.property('advertiserDomains'); - expect(bids[index]).to.have.property('ttl', 300); - expect(bids[index]).to.have.property('netRevenue', true); - } - }); - }); - - context('when mediaType is video', function () { - let bidRequest, bidderResponse; - beforeEach(function() { - const bidderRequest = getVideoRequest(); - bidRequest = spec.buildRequests(bidderRequest.bids, bidderRequest); - bidderResponse = getBidderResponse(); - }); - - it('handles empty response', function () { - 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 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 bids = spec.interpretResponse(SERVER_RESP, bidRequest); - expect(bids.length).to.equal(0); - }); - }); - }); - - describe('getUserSyncs', function () { - let bidRequest, bidderResponse; - beforeEach(function() { - const bidderRequest = getVideoRequest(); - bidRequest = spec.buildRequests(bidderRequest.bids, bidderRequest); - bidderResponse = getBidderResponse(); - }); - - it('handles no parameters', function () { - let opts = spec.getUserSyncs({}); - expect(opts).to.be.an('array').that.is.empty; - }); - it('returns non if sync is not allowed', function () { - let opts = spec.getUserSyncs({iframeEnabled: false, pixelEnabled: false}); - - expect(opts).to.be.an('array').that.is.empty; - }); - - it('iframe sync enabled should return results', function () { - let opts = spec.getUserSyncs({iframeEnabled: true, pixelEnabled: false}, [bidderResponse]); - - expect(opts.length).to.equal(1); - expect(opts[0].type).to.equal('iframe'); - expect(opts[0].url).to.equal(bidderResponse.body.ext.usersync['sovrn'].syncs[0].url); - }); - - it('pixel sync enabled should return results', function () { - let opts = spec.getUserSyncs({iframeEnabled: false, pixelEnabled: true}, [bidderResponse]); - - expect(opts.length).to.equal(1); - expect(opts[0].type).to.equal('image'); - expect(opts[0].url).to.equal(bidderResponse.body.ext.usersync['appnexus'].syncs[0].url); - }); - - it('all sync enabled should prioritize iframe', function () { - let opts = spec.getUserSyncs({iframeEnabled: true, pixelEnabled: true}, [bidderResponse]); - - expect(opts.length).to.equal(1); - }); - }); -}); diff --git a/test/spec/modules/dynamicAdBoostRtdProvider_spec.js b/test/spec/modules/dynamicAdBoostRtdProvider_spec.js deleted file mode 100644 index 66c24435589..00000000000 --- a/test/spec/modules/dynamicAdBoostRtdProvider_spec.js +++ /dev/null @@ -1,77 +0,0 @@ -import { subModuleObj as rtdProvider } from 'modules/dynamicAdBoostRtdProvider.js'; -import { loadExternalScript } from '../../../src/adloader.js'; -import { expect } from 'chai'; - -const configWithParams = { - params: { - keyId: 'dynamic', - adUnits: ['gpt-123'], - threshold: 1 - } -}; - -const configWithoutRequiredParams = { - params: { - keyId: '' - } -}; - -describe('dynamicAdBoost', function() { - let clock; - let sandbox; - beforeEach(function () { - sandbox = sinon.sandbox.create(); - clock = sandbox.useFakeTimers(Date.now()); - }); - afterEach(function () { - sandbox.restore(); - }); - describe('init', function() { - describe('initialize without expected params', function() { - it('fails initalize when keyId is not present', function() { - expect(rtdProvider.init(configWithoutRequiredParams)).to.be.false; - }) - }) - - describe('initialize with expected params', function() { - it('successfully initialize with load script', function() { - expect(rtdProvider.init(configWithParams)).to.be.true; - clock.tick(1000); - expect(loadExternalScript.called).to.be.true; - }) - }); - }); -}) - -describe('markViewed tests', function() { - let sandbox; - const mockObserver = { - unobserve: sinon.spy() - }; - const makeElement = (id) => { - const el = document.createElement('div'); - el.setAttribute('id', id); - return el; - } - const mockEntry = { - target: makeElement('target_id') - }; - - beforeEach(function() { - sandbox = sinon.sandbox.create(); - }) - - afterEach(function() { - sandbox.restore() - }) - - it('markViewed returns a function', function() { - expect(rtdProvider.markViewed(mockEntry, mockObserver)).to.be.a('function') - }); - - it('markViewed unobserves', function() { - const func = rtdProvider.markViewed(mockEntry, mockObserver); - func(); - expect(mockObserver.unobserve.calledOnce).to.be.true; - }); -}) diff --git a/test/spec/modules/edge226BidAdapter_spec.js b/test/spec/modules/edge226BidAdapter_spec.js deleted file mode 100644 index 4819d8d4a4e..00000000000 --- a/test/spec/modules/edge226BidAdapter_spec.js +++ /dev/null @@ -1,373 +0,0 @@ -import { expect } from 'chai'; -import { spec } from '../../../modules/edge226BidAdapter.js'; -import { BANNER, VIDEO, NATIVE } from '../../../src/mediaTypes.js'; -import { getUniqueIdentifierStr } from '../../../src/utils.js'; - -const bidder = 'edge226' - -describe('Edge226BidAdapter', function () { - const bids = [ - { - bidId: getUniqueIdentifierStr(), - bidder: bidder, - mediaTypes: { - [BANNER]: { - sizes: [[300, 250]] - } - }, - params: { - placementId: 'testBanner', - } - }, - { - bidId: getUniqueIdentifierStr(), - bidder: bidder, - mediaTypes: { - [VIDEO]: { - playerSize: [[300, 300]], - minduration: 5, - maxduration: 60 - } - }, - params: { - placementId: 'testVideo', - } - }, - { - bidId: getUniqueIdentifierStr(), - bidder: bidder, - mediaTypes: { - [NATIVE]: { - native: { - title: { - required: true - }, - body: { - required: true - }, - icon: { - required: true, - size: [64, 64] - } - } - } - }, - params: { - placementId: 'testNative', - } - } - ]; - - const invalidBid = { - bidId: getUniqueIdentifierStr(), - bidder: bidder, - mediaTypes: { - [BANNER]: { - sizes: [[300, 250]] - } - }, - params: { - - } - } - - const bidderRequest = { - uspConsent: '1---', - gdprConsent: 'COvFyGBOvFyGBAbAAAENAPCAAOAAAAAAAAAAAEEUACCKAAA.IFoEUQQgAIQwgIwQABAEAAAAOIAACAIAAAAQAIAgEAACEAAAAAgAQBAAAAAAAGBAAgAAAAAAAFAAECAAAgAAQARAEQAAAAAJAAIAAgAAAYQEAAAQmAgBC3ZAYzUw', - refererInfo: { - referer: 'https://test.com' - }, - 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://ssp.dauup.com/pbjs'); - }); - - it('Returns general data valid', function () { - let data = serverRequest.data; - expect(data).to.be.an('object'); - expect(data).to.have.all.keys('deviceWidth', - 'deviceHeight', - 'language', - 'secure', - 'host', - 'page', - 'placements', - 'coppa', - 'ccpa', - 'gdpr', - 'tmax' - ); - 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('string'); - 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'); - - 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); - let data = serverRequest.data; - expect(data.gdpr).to.exist; - expect(data.gdpr).to.be.a('string'); - expect(data.gdpr).to.equal(bidderRequest.gdprConsent); - 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); - let 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; - }); - - it('Returns empty data if no valid requests are passed', function () { - serverRequest = spec.buildRequests([], bidderRequest); - let data = serverRequest.data; - expect(data.placements).to.be.an('array').that.is.empty; - }); - }); - - 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 - } - }] - }; - let bannerResponses = spec.interpretResponse(banner); - expect(bannerResponses).to.be.an('array').that.is.not.empty; - let dataItem = bannerResponses[0]; - expect(dataItem).to.have.all.keys('requestId', 'cpm', 'width', 'height', 'ad', 'ttl', 'creativeId', - 'netRevenue', 'currency', 'dealId', '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 - } - }] - }; - let videoResponses = spec.interpretResponse(video); - expect(videoResponses).to.be.an('array').that.is.not.empty; - - let dataItem = videoResponses[0]; - expect(dataItem).to.have.all.keys('requestId', 'cpm', 'vastUrl', 'ttl', 'creativeId', - 'netRevenue', 'currency', 'dealId', '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 - } - }] - }; - let nativeResponses = spec.interpretResponse(native); - expect(nativeResponses).to.be.an('array').that.is.not.empty; - - let dataItem = nativeResponses[0]; - expect(dataItem).to.have.keys('requestId', 'cpm', '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' - }] - }; - - let serverResponses = spec.interpretResponse(invBanner); - expect(serverResponses).to.be.an('array').that.is.empty; - }); - it('Should return an empty array if invalid video response is passed', function () { - const invVideo = { - body: [{ - mediaType: 'video', - cpm: 0.5, - requestId: '23fhj33i987f', - ttl: 120, - creativeId: '2', - netRevenue: true, - currency: 'USD', - dealId: '1' - }] - }; - let serverResponses = spec.interpretResponse(invVideo); - expect(serverResponses).to.be.an('array').that.is.empty; - }); - it('Should return an empty array if invalid native response is passed', function () { - const invNative = { - body: [{ - mediaType: 'native', - clickUrl: 'test.com', - title: 'Test', - impressionTrackers: ['test.com'], - ttl: 120, - requestId: '23fhj33i987f', - creativeId: '2', - netRevenue: true, - currency: 'USD', - }] - }; - let serverResponses = spec.interpretResponse(invNative); - expect(serverResponses).to.be.an('array').that.is.empty; - }); - it('Should return an empty array if invalid response is passed', function () { - const invalid = { - body: [{ - ttl: 120, - creativeId: '2', - netRevenue: true, - currency: 'USD', - dealId: '1' - }] - }; - let serverResponses = spec.interpretResponse(invalid); - expect(serverResponses).to.be.an('array').that.is.empty; - }); - }); -}); diff --git a/test/spec/modules/eids_spec.js b/test/spec/modules/eids_spec.js index 25e70f12ced..725d631cd99 100644 --- a/test/spec/modules/eids_spec.js +++ b/test/spec/modules/eids_spec.js @@ -187,24 +187,6 @@ describe('eids array generation for known sub-modules', function() { }); }); - it('bidswitch with ext', function() { - const userId = { - bidswitch: {'id': 'sample_id', 'ext': {'provider': 'some.provider.com'}} - }; - const newEids = createEidsArray(userId); - expect(newEids.length).to.equal(1); - expect(newEids[0]).to.deep.equal({ - source: 'bidswitch.net', - uids: [{ - id: 'sample_id', - atype: 3, - ext: { - provider: 'some.provider.com' - } - }] - }); - }); - it('medianet', function() { const userId = { medianet: {'id': 'sample_id'} @@ -220,189 +202,6 @@ describe('eids array generation for known sub-modules', function() { }); }); - it('medianet with ext', function() { - const userId = { - medianet: {'id': 'sample_id', 'ext': {'provider': 'some.provider.com'}} - }; - const newEids = createEidsArray(userId); - expect(newEids.length).to.equal(1); - expect(newEids[0]).to.deep.equal({ - source: 'media.net', - uids: [{ - id: 'sample_id', - atype: 3, - ext: { - provider: 'some.provider.com' - } - }] - }); - }); - - it('sovrn', function() { - const userId = { - sovrn: {'id': 'sample_id'} - }; - const newEids = createEidsArray(userId); - expect(newEids.length).to.equal(1); - expect(newEids[0]).to.deep.equal({ - source: 'liveintent.sovrn.com', - uids: [{ - id: 'sample_id', - atype: 3 - }] - }); - }); - - it('sovrn with ext', function() { - const userId = { - sovrn: {'id': 'sample_id', 'ext': {'provider': 'some.provider.com'}} - }; - const newEids = createEidsArray(userId); - expect(newEids.length).to.equal(1); - expect(newEids[0]).to.deep.equal({ - source: 'liveintent.sovrn.com', - uids: [{ - id: 'sample_id', - atype: 3, - ext: { - provider: 'some.provider.com' - } - }] - }); - }); - - it('magnite', function() { - const userId = { - magnite: {'id': 'sample_id'} - }; - const newEids = createEidsArray(userId); - expect(newEids.length).to.equal(1); - expect(newEids[0]).to.deep.equal({ - source: 'rubiconproject.com', - uids: [{ - id: 'sample_id', - atype: 3 - }] - }); - }); - - it('magnite with ext', function() { - const userId = { - magnite: {'id': 'sample_id', 'ext': {'provider': 'some.provider.com'}} - }; - const newEids = createEidsArray(userId); - expect(newEids.length).to.equal(1); - expect(newEids[0]).to.deep.equal({ - source: 'rubiconproject.com', - uids: [{ - id: 'sample_id', - atype: 3, - ext: { - provider: 'some.provider.com' - } - }] - }); - }); - - it('index', function() { - const userId = { - index: {'id': 'sample_id'} - }; - const newEids = createEidsArray(userId); - expect(newEids.length).to.equal(1); - expect(newEids[0]).to.deep.equal({ - source: 'liveintent.indexexchange.com', - uids: [{ - id: 'sample_id', - atype: 3 - }] - }); - }); - - it('index with ext', function() { - const userId = { - index: {'id': 'sample_id', 'ext': {'provider': 'some.provider.com'}} - }; - const newEids = createEidsArray(userId); - expect(newEids.length).to.equal(1); - expect(newEids[0]).to.deep.equal({ - source: 'liveintent.indexexchange.com', - uids: [{ - id: 'sample_id', - atype: 3, - ext: { - provider: 'some.provider.com' - } - }] - }); - }); - - it('openx', function () { - const userId = { - openx: { 'id': 'sample_id' } - }; - const newEids = createEidsArray(userId); - expect(newEids.length).to.equal(1); - expect(newEids[0]).to.deep.equal({ - source: 'openx.net', - uids: [{ - id: 'sample_id', - atype: 3 - }] - }); - }); - - it('openx with ext', function () { - const userId = { - openx: { 'id': 'sample_id', 'ext': { 'provider': 'some.provider.com' } } - }; - const newEids = createEidsArray(userId); - expect(newEids.length).to.equal(1); - expect(newEids[0]).to.deep.equal({ - source: 'openx.net', - uids: [{ - id: 'sample_id', - atype: 3, - ext: { - provider: 'some.provider.com' - } - }] - }); - }); - - it('pubmatic', function() { - const userId = { - pubmatic: {'id': 'sample_id'} - }; - const newEids = createEidsArray(userId); - expect(newEids.length).to.equal(1); - expect(newEids[0]).to.deep.equal({ - source: 'pubmatic.com', - uids: [{ - id: 'sample_id', - atype: 3 - }] - }); - }); - - it('pubmatic with ext', function() { - const userId = { - pubmatic: {'id': 'sample_id', 'ext': {'provider': 'some.provider.com'}} - }; - const newEids = createEidsArray(userId); - expect(newEids.length).to.equal(1); - expect(newEids[0]).to.deep.equal({ - source: 'pubmatic.com', - uids: [{ - id: 'sample_id', - atype: 3, - ext: { - provider: 'some.provider.com' - } - }] - }); - }); - it('liveIntentId; getValue call and NO ext', function() { const userId = { lipb: { @@ -549,24 +348,6 @@ describe('eids array generation for known sub-modules', function() { }); }); - it('uid2 with ext', function() { - const userId = { - uid2: {'id': 'Sample_AD_Token', 'ext': {'provider': 'some.provider.com'}} - }; - const newEids = createEidsArray(userId); - expect(newEids.length).to.equal(1); - expect(newEids[0]).to.deep.equal({ - source: 'uidapi.com', - uids: [{ - id: 'Sample_AD_Token', - atype: 3, - ext: { - provider: 'some.provider.com' - } - }] - }); - }); - it('euid', function() { const userId = { euid: {'id': 'Sample_AD_Token'} @@ -679,21 +460,6 @@ describe('eids array generation for known sub-modules', function() { }); }); - it('operaId', function() { - const userId = { - operaId: 'some-random-id-value' - }; - const newEids = createEidsArray(userId); - expect(newEids.length).to.equal(1); - expect(newEids[0]).to.deep.equal({ - source: 't.adx.opera.com', - uids: [{ - id: 'some-random-id-value', - atype: 1 - }] - }); - }); - it('33acrossId', function() { const userId = { '33acrossId': { diff --git a/test/spec/modules/eplanningBidAdapter_spec.js b/test/spec/modules/eplanningBidAdapter_spec.js index a381d7644a1..1a6cfd7afe4 100644 --- a/test/spec/modules/eplanningBidAdapter_spec.js +++ b/test/spec/modules/eplanningBidAdapter_spec.js @@ -74,53 +74,6 @@ describe('E-Planning Adapter', function () { }, 'sizes': [[300, 250], [300, 600]], }; - const validBidSpaceNameWithBidFloor = { - bidder: 'eplanning', - 'bidId': BID_ID, - params: { - 'ci': CI, - 'sn': SN, - }, - getFloor: () => ({ currency: 'USD', floor: 1.16 }), - 'sizes': [[300, 250], [300, 600]], - }; - const validBidSpaceOutstreamWithBidFloor = { - 'bidder': 'eplanning', - 'bidId': BID_ID, - 'params': { - 'ci': CI, - 'sn': SN, - }, - getFloor: () => ({ currency: 'USD', floor: 1.16 }), - 'mediaTypes': { - 'video': { - 'context': 'outstream', - 'playerSize': [300, 600], - 'mimes': ['video/mp4'], - 'protocols': [1, 2, 3, 4, 5, 6], - 'playbackmethod': [2], - 'skip': 1 - } - }, - }; - const validBidSpaceInstreamWithBidFloor = { - 'bidder': 'eplanning', - 'bidId': BID_ID, - 'params': { - 'ci': CI, - 'sn': SN, - }, - getFloor: () => ({ currency: 'USD', floor: 1.16 }), - 'mediaTypes': { - 'video': { - 'context': 'instream', - 'mimes': ['video/mp4'], - 'protocols': [1, 2, 3, 4, 5, 6], - 'playbackmethod': [2], - 'skip': 1 - } - }, - }; const validBidSpaceOutstream = { 'bidder': 'eplanning', 'bidId': BID_ID, @@ -620,26 +573,6 @@ describe('E-Planning Adapter', function () { expect(e).to.equal(SN + ':300x250,300x600'); }); - it('should return e parameter with space name attribute with value according to the adunit sizes and bidFloor', function () { - const e = spec.buildRequests([validBidSpaceNameWithBidFloor], bidderRequest).data.e; - expect(e).to.equal(SN + ':300x250,300x600|' + validBidSpaceNameWithBidFloor.getFloor().floor); - }); - - it('should return correct e parameter with support vast with one space with size outstream and bidFloor', function () { - const data = spec.buildRequests([validBidSpaceOutstreamWithBidFloor], bidderRequest).data; - expect(data.e).to.equal('video_300x600_0:300x600;1|' + validBidSpaceOutstreamWithBidFloor.getFloor().floor); - expect(data.vctx).to.equal(2); - expect(data.vv).to.equal(3); - }); - - it('should return correct e parameter with support vast with one space with size instream with bidFloor', function () { - let bidRequests = [validBidSpaceInstreamWithBidFloor]; - const data = spec.buildRequests(bidRequests, bidderRequest).data; - expect(data.e).to.equal('video_640x480_0:640x480;1|' + validBidSpaceInstreamWithBidFloor.getFloor().floor); - expect(data.vctx).to.equal(1); - expect(data.vv).to.equal(3); - }); - it('should return correct e parameter with more than one adunit', function () { const NEW_CODE = ADUNIT_CODE + '2'; const CLEAN_NEW_CODE = CLEAN_ADUNIT_CODE + '2'; diff --git a/test/spec/modules/euidIdSystem_spec.js b/test/spec/modules/euidIdSystem_spec.js index 98770fa80bc..9a016b0facd 100644 --- a/test/spec/modules/euidIdSystem_spec.js +++ b/test/spec/modules/euidIdSystem_spec.js @@ -1,13 +1,15 @@ -import {coreStorage, init, setSubmoduleRegistry} from 'modules/userId/index.js'; +import {coreStorage, init, setSubmoduleRegistry, requestBidsHook} from 'modules/userId/index.js'; import {config} from 'src/config.js'; -import {euidIdSubmodule} from 'modules/euidIdSystem.js'; +import * as utils from 'src/utils.js'; +import { euidIdSubmodule } from 'modules/euidIdSystem.js'; import 'modules/consentManagement.js'; import 'src/prebid.js'; -import * as utils from 'src/utils.js'; -import {apiHelpers, cookieHelpers, runAuction, setGdprApplies} from './uid2IdSystem_helpers.js'; +import { getGlobal } from 'src/prebidGlobal.js'; +import { server } from 'test/mocks/xhr.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 uninstallGdprEnforcement} from 'modules/gdprEnforcement.js'; -import {server} from 'test/mocks/xhr'; let expect = require('chai').expect; @@ -23,45 +25,29 @@ const auctionDelayMs = 10; const makeEuidIdentityContainer = (token) => ({euid: {id: token}}); const useLocalStorage = true; - const makePrebidConfig = (params = null, extraSettings = {}, debug = false) => ({ 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' } -const clientSideGeneratedToken = 'client-side-generated-advertising-token'; - const apiUrl = 'https://prod.euid.eu/v2/token/refresh'; -const cstgApiUrl = 'https://prod.euid.eu/v2/token/client-generate'; const headers = { 'Content-Type': 'application/json' }; -const makeSuccessResponseBody = (token) => btoa(JSON.stringify({ status: 'success', body: { ...apiHelpers.makeTokenResponse(initialToken), advertising_token: token } })); +const makeSuccessResponseBody = () => btoa(JSON.stringify({ status: 'success', body: { ...apiHelpers.makeTokenResponse(initialToken), advertising_token: refreshedToken } })); +const configureEuidResponse = (httpStatus, response) => server.respondWith('POST', apiUrl, (xhr) => xhr.respond(httpStatus, headers, response)); const expectToken = (bid, token) => expect(bid?.userId ?? {}).to.deep.include(makeEuidIdentityContainer(token)); const expectNoIdentity = (bid) => expect(bid).to.not.haveOwnProperty('userId'); describe('EUID module', function() { - let suiteSandbox, restoreSubtleToUndefined = false; - - const configureEuidResponse = (httpStatus, response) => server.respondWith('POST', apiUrl, (xhr) => xhr.respond(httpStatus, headers, response)); - const configureEuidCstgResponse = (httpStatus, response) => server.respondWith('POST', cstgApiUrl, (xhr) => xhr.respond(httpStatus, headers, response)); - + let suiteSandbox, testSandbox, timerSpy, fullTestTitle, restoreSubtleToUndefined = false; before(function() { uninstallGdprEnforcement(); hook.ready(); suiteSandbox = sinon.sandbox.create(); if (typeof window.crypto.subtle === 'undefined') { restoreSubtleToUndefined = true; - window.crypto.subtle = { importKey: () => {}, digest: () => {}, decrypt: () => {}, deriveKey: () => {}, encrypt: () => {}, generateKey: () => {}, exportKey: () => {} }; + window.crypto.subtle = { importKey: () => {}, decrypt: () => {} }; } suiteSandbox.stub(window.crypto.subtle, 'importKey').callsFake(() => Promise.resolve()); - suiteSandbox.stub(window.crypto.subtle, 'digest').callsFake(() => Promise.resolve('hashed_value')); suiteSandbox.stub(window.crypto.subtle, 'decrypt').callsFake((settings, key, data) => Promise.resolve(new Uint8Array([...settings.iv, ...data]))); - suiteSandbox.stub(window.crypto.subtle, 'deriveKey').callsFake(() => Promise.resolve()); - suiteSandbox.stub(window.crypto.subtle, 'exportKey').callsFake(() => Promise.resolve()); - suiteSandbox.stub(window.crypto.subtle, 'encrypt').callsFake(() => Promise.resolve(new ArrayBuffer())); - suiteSandbox.stub(window.crypto.subtle, 'generateKey').callsFake(() => Promise.resolve({ - privateKey: {}, - publicKey: {} - })); }); after(function() { suiteSandbox.restore(); @@ -128,23 +114,10 @@ describe('EUID module', function() { it('When an expired token is provided and the API responds in time, the refreshed token is provided to the auction.', async function() { setGdprApplies(true); const euidToken = apiHelpers.makeTokenResponse(initialToken, true, true); - configureEuidResponse(200, makeSuccessResponseBody(refreshedToken)); + configureEuidResponse(200, makeSuccessResponseBody()); config.setConfig(makePrebidConfig({euidToken})); - apiHelpers.respondAfterDelay(1, server); + apiHelpers.respondAfterDelay(1); const bid = await runAuction(); expectToken(bid, refreshedToken); }); - - if (FEATURES.UID2_CSTG) { - it('Should use client side generated EUID token in the auction.', async function() { - setGdprApplies(true); - const euidToken = apiHelpers.makeTokenResponse(initialToken, true, true); - configureEuidCstgResponse(200, makeSuccessResponseBody(clientSideGeneratedToken)); - config.setConfig(makePrebidConfig({ euidToken, ...cstgConfigParams, email: 'test@test.com' })); - apiHelpers.respondAfterDelay(1, server); - - const bid = await runAuction(); - expectToken(bid, clientSideGeneratedToken); - }); - } }); diff --git a/test/spec/modules/experianRtdProvider_spec.js b/test/spec/modules/experianRtdProvider_spec.js deleted file mode 100644 index fd104674d70..00000000000 --- a/test/spec/modules/experianRtdProvider_spec.js +++ /dev/null @@ -1,365 +0,0 @@ -import { - EXPERIAN_RTID_DATA_KEY, - EXPERIAN_RTID_EXPIRATION_KEY, - EXPERIAN_RTID_STALE_KEY, - SUBMODULE_NAME, - experianRtdObj, - experianRtdSubmodule, EXPERIAN_RTID_NO_TRACK_KEY -} from '../../../modules/experianRtdProvider.js'; -import { getStorageManager } from '../../../src/storageManager.js'; -import { MODULE_TYPE_RTD } from '../../../src/activities/modules'; -import { safeJSONParse, timestamp } from '../../../src/utils'; -import {server} from '../../mocks/xhr.js'; - -describe('Experian realtime module', () => { - const sandbox = sinon.createSandbox(); - let requests; - - const storage = getStorageManager({ moduleType: MODULE_TYPE_RTD, moduleName: SUBMODULE_NAME }) - beforeEach(() => { - requests = server.requests; - storage.removeDataFromLocalStorage(EXPERIAN_RTID_DATA_KEY, null) - storage.removeDataFromLocalStorage(EXPERIAN_RTID_EXPIRATION_KEY, null) - storage.removeDataFromLocalStorage(EXPERIAN_RTID_STALE_KEY, null) - storage.removeDataFromLocalStorage(EXPERIAN_RTID_NO_TRACK_KEY, null) - }) - afterEach(() => { - sandbox.restore(); - }) - // Bid request config - const reqBidsConfigObj = { - adUnits: [{ - bids: [ - { bidder: 'appnexus' } - ] - }] - }; - describe('init', () => { - it('succeeds when params have accountId', () => { - const initResult = experianRtdSubmodule.init({ params: { accountId: 'ZylatYg' } }) - expect(initResult).to.be.true; - }) - - it('fails when params don\'t have accountId', () => { - const initResult = experianRtdSubmodule.init({ }) - expect(initResult).to.be.false; - }) - }) - - describe('getBidRequestData', () => { - describe('when local storage has data, isn\'t no track, isn\'t stale and isn\'t expired', () => { - beforeEach(() => { - const now = timestamp() - storage.setDataInLocalStorage(EXPERIAN_RTID_DATA_KEY, JSON.stringify([ - { - bidder: 'pubmatic', - data: { - key: 'pubmatic-encryption-key-1', - data: 'IkhlbGxvLCB3b3JsZC4gSGVsbG8sIHdvcmxkLiBIZWxsbywgd29ybGQuIg==' - } - }, - { - bidder: 'sovrn', - data: { - key: 'sovrn-encryption-key-1', - data: 'IkhlbGxvLCB3b3JsZC4gSGVsbG8sIHdvcmxkLiBIZWxsbywgd29ybGQuIg==' - } - } - ]), null) - - storage.setDataInLocalStorage(EXPERIAN_RTID_EXPIRATION_KEY, new Date(now + 100000).toISOString(), null) - storage.setDataInLocalStorage(EXPERIAN_RTID_STALE_KEY, new Date(now + 50000).toISOString(), null) - }) - it('doesn\'t request data envelope, and alters bids', () => { - const bidsConfig = { - ortb2Fragments: { - bidder: {} - } - } - const moduleConfig = { params: { accountId: 'ZylatYg', bidders: ['pubmatic', 'sovrn'] } } - const dataEnvelopeSpy = sandbox.spy(experianRtdObj, 'requestDataEnvelope') - const alterBidsSpy = sandbox.spy(experianRtdObj, 'alterBids') - experianRtdSubmodule.getBidRequestData(bidsConfig, sinon.stub, moduleConfig) - sandbox.assert.calledWithExactly(alterBidsSpy, bidsConfig, moduleConfig) - expect(dataEnvelopeSpy.called).to.be.false; - }) - }) - - describe('when local storage has data but it is stale', () => { - beforeEach(() => { - const now = timestamp() - storage.setDataInLocalStorage(EXPERIAN_RTID_DATA_KEY, JSON.stringify([ - { - bidder: 'pubmatic', - data: { - key: 'pubmatic-encryption-key-1', - data: 'IkhlbGxvLCB3b3JsZC4gSGVsbG8sIHdvcmxkLiBIZWxsbywgd29ybGQuIg==' - } - }, - { - bidder: 'sovrn', - data: { - key: 'sovrn-encryption-key-1', - data: 'IkhlbGxvLCB3b3JsZC4gSGVsbG8sIHdvcmxkLiBIZWxsbywgd29ybGQuIg==' - } - } - ]), null) - - storage.setDataInLocalStorage(EXPERIAN_RTID_EXPIRATION_KEY, new Date(now + 100000).toISOString(), null) - storage.setDataInLocalStorage(EXPERIAN_RTID_STALE_KEY, new Date(now - 50000).toISOString(), null) - }) - it('it requests data envelope and alters bids', () => { - const bidsConfig = { - ortb2Fragments: { - bidder: {} - } - } - const userConsent = {gdpr: {}, uspConsent: {}} - const moduleConfig = { params: { accountId: 'ZylatYg', bidders: ['pubmatic', 'sovrn'] } } - const dataEnvelopeSpy = sandbox.spy(experianRtdObj, 'requestDataEnvelope') - const alterBidsSpy = sandbox.spy(experianRtdObj, 'alterBids') - experianRtdSubmodule.getBidRequestData(bidsConfig, sinon.stub, moduleConfig, userConsent) - sandbox.assert.calledWithExactly(alterBidsSpy, bidsConfig, moduleConfig) - sandbox.assert.calledWithExactly(dataEnvelopeSpy, moduleConfig, userConsent) - }) - }) - describe('when local storage has data but it is expired', () => { - beforeEach(() => { - const now = timestamp() - storage.setDataInLocalStorage(EXPERIAN_RTID_DATA_KEY, JSON.stringify([ - { - bidder: 'pubmatic', - data: { - key: 'pubmatic-encryption-key-1', - data: 'IkhlbGxvLCB3b3JsZC4gSGVsbG8sIHdvcmxkLiBIZWxsbywgd29ybGQuIg==' - } - }, - { - bidder: 'sovrn', - data: { - key: 'sovrn-encryption-key-1', - data: 'IkhlbGxvLCB3b3JsZC4gSGVsbG8sIHdvcmxkLiBIZWxsbywgd29ybGQuIg==' - } - } - ]), null) - - storage.setDataInLocalStorage(EXPERIAN_RTID_EXPIRATION_KEY, new Date(now - 50000).toISOString(), null) - storage.setDataInLocalStorage(EXPERIAN_RTID_STALE_KEY, new Date(now - 100000).toISOString(), null) - }) - it('requests data envelope, and doesn\'t alter bids', () => { - const bidsConfig = { - ortb2Fragments: { - bidder: {} - } - } - const userConsent = {gdpr: {}, uspConsent: {}} - const moduleConfig = { params: { accountId: 'ZylatYg', bidders: ['pubmatic', 'sovrn'] } } - const dataEnvelopeSpy = sandbox.spy(experianRtdObj, 'requestDataEnvelope') - const alterBidsSpy = sandbox.spy(experianRtdObj, 'alterBids') - experianRtdSubmodule.getBidRequestData(bidsConfig, sinon.stub, moduleConfig, userConsent) - sandbox.assert.calledWithExactly(dataEnvelopeSpy, moduleConfig, userConsent) - expect(alterBidsSpy.called).to.be.false; - }) - }) - describe('when local storage has no data envelope', () => { - it('requests data envelope, and doesn\'t alter bids', () => { - const bidsConfig = { - ortb2Fragments: { - bidder: {} - } - } - const userConsent = {gdpr: {}, uspConsent: {}} - const moduleConfig = { params: { accountId: 'ZylatYg', bidders: ['pubmatic', 'sovrn'] } } - const dataEnvelopeSpy = sandbox.spy(experianRtdObj, 'requestDataEnvelope') - const alterBidsSpy = sandbox.spy(experianRtdObj, 'alterBids') - experianRtdSubmodule.getBidRequestData(bidsConfig, sinon.stub, moduleConfig, userConsent) - sandbox.assert.calledWithExactly(dataEnvelopeSpy, moduleConfig, userConsent) - expect(alterBidsSpy.called).to.be.false; - }) - }) - describe('when local storage has no track and is expired', () => { - beforeEach(() => { - const now = timestamp() - storage.setDataInLocalStorage(EXPERIAN_RTID_NO_TRACK_KEY, 'no_track', null) - - storage.setDataInLocalStorage(EXPERIAN_RTID_EXPIRATION_KEY, new Date(now - 50000).toISOString(), null) - storage.setDataInLocalStorage(EXPERIAN_RTID_STALE_KEY, new Date(now - 100000).toISOString(), null) - }) - it('requests data envelope, and doesn\'t alter bids', () => { - const bidsConfig = { - ortb2Fragments: { - bidder: {} - } - } - const userConsent = {gdpr: {}, uspConsent: {}} - const moduleConfig = { params: { accountId: 'ZylatYg', bidders: ['pubmatic', 'sovrn'] } } - const dataEnvelopeSpy = sandbox.spy(experianRtdObj, 'requestDataEnvelope') - const alterBidsSpy = sandbox.spy(experianRtdObj, 'alterBids') - experianRtdSubmodule.getBidRequestData(bidsConfig, sinon.stub, moduleConfig, userConsent) - sandbox.assert.calledWithExactly(dataEnvelopeSpy, moduleConfig, userConsent) - expect(alterBidsSpy.called).to.be.false; - }) - }) - - describe('when local storage has no track and is stale', () => { - beforeEach(() => { - const now = timestamp() - storage.setDataInLocalStorage(EXPERIAN_RTID_NO_TRACK_KEY, 'no_track', null) - - storage.setDataInLocalStorage(EXPERIAN_RTID_EXPIRATION_KEY, new Date(now + 100000).toISOString(), null) - storage.setDataInLocalStorage(EXPERIAN_RTID_STALE_KEY, new Date(now - 50000).toISOString(), null) - }) - it('requests data envelope, and doesn\'t alter bids', () => { - const bidsConfig = { - ortb2Fragments: { - bidder: {} - } - } - const userConsent = {gdpr: {}, uspConsent: {}} - const moduleConfig = { params: { accountId: 'ZylatYg', bidders: ['pubmatic', 'sovrn'] } } - const dataEnvelopeSpy = sandbox.spy(experianRtdObj, 'requestDataEnvelope') - const alterBidsSpy = sandbox.spy(experianRtdObj, 'alterBids') - experianRtdSubmodule.getBidRequestData(bidsConfig, sinon.stub, moduleConfig, userConsent) - sandbox.assert.calledWithExactly(dataEnvelopeSpy, moduleConfig, userConsent) - expect(alterBidsSpy.called).to.be.false; - }) - }) - - describe('when local storage has no track and isn\'t expired or stale', () => { - beforeEach(() => { - const now = timestamp() - storage.setDataInLocalStorage(EXPERIAN_RTID_NO_TRACK_KEY, 'no_track', null) - - storage.setDataInLocalStorage(EXPERIAN_RTID_EXPIRATION_KEY, new Date(now + 100000).toISOString(), null) - storage.setDataInLocalStorage(EXPERIAN_RTID_STALE_KEY, new Date(now + 50000).toISOString(), null) - }) - it('doesn\'t alter bids and doesn\'t request data envelope', () => { - const bidsConfig = { - ortb2Fragments: { - bidder: {} - } - } - const userConsent = {gdpr: {}, uspConsent: {}} - const moduleConfig = { params: { accountId: 'ZylatYg', bidders: ['pubmatic', 'sovrn'] } } - const dataEnvelopeSpy = sandbox.spy(experianRtdObj, 'requestDataEnvelope') - const alterBidsSpy = sandbox.spy(experianRtdObj, 'alterBids') - experianRtdSubmodule.getBidRequestData(bidsConfig, sinon.stub, moduleConfig, userConsent) - expect(alterBidsSpy.called).to.be.false; - expect(dataEnvelopeSpy.called).to.be.false; - }) - }) - }) - - describe('alterBids', () => { - describe('data envelope has every bidder from config', () => { - beforeEach(() => { - const now = timestamp() - storage.setDataInLocalStorage(EXPERIAN_RTID_DATA_KEY, JSON.stringify([ - { - bidder: 'pubmatic', - data: { - key: 'pubmatic-encryption-key-1', - data: 'IkhlbGxvLCB3b3JsZC4gSGVsbG8sIHdvcmxkLiBIZWxsbywgd29ybGQuIg==' - } - }, - { - bidder: 'sovrn', - data: { - key: 'sovrn-encryption-key-1', - data: 'IkhlbGxvLCB3b3JsZC4gSGVsbG8sIHdvcmxkLiBIZWxsbywgd29ybGQuIg==' - } - } - ]), null) - - storage.setDataInLocalStorage(EXPERIAN_RTID_EXPIRATION_KEY, new Date(now + 100000).toISOString(), null) - storage.setDataInLocalStorage(EXPERIAN_RTID_STALE_KEY, new Date(now + 50000).toISOString(), null) - }) - - it('alters bids for the bidders in the module config', () => { - const bidsConfig = { - ortb2Fragments: { - bidder: {} - } - } - 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==' - }}) - }) - }) - describe('data envelope is missing bidders from config', () => { - beforeEach(() => { - const now = timestamp() - storage.setDataInLocalStorage(EXPERIAN_RTID_DATA_KEY, JSON.stringify([ - { - bidder: 'sovrn', - data: { - key: 'sovrn-encryption-key-1', - data: 'IkhlbGxvLCB3b3JsZC4gSGVsbG8sIHdvcmxkLiBIZWxsbywgd29ybGQuIg==' - } - } - ]), null) - - storage.setDataInLocalStorage(EXPERIAN_RTID_EXPIRATION_KEY, new Date(now + 100000).toISOString(), null) - storage.setDataInLocalStorage(EXPERIAN_RTID_STALE_KEY, new Date(now + 50000).toISOString(), null) - }) - - it('alters bids for the bidders in the module config', () => { - const bidsConfig = { - ortb2Fragments: { - bidder: {} - } - } - const moduleConfig = { params: { accountId: 'ZylatYg', bidders: ['pubmatic', 'sovrn'] } } - experianRtdObj.alterBids(bidsConfig, moduleConfig); - expect(bidsConfig.ortb2Fragments.bidder).to.deep.equal({ - sovrn: { - experianRtidKey: 'sovrn-encryption-key-1', - experianRtidData: 'IkhlbGxvLCB3b3JsZC4gSGVsbG8sIHdvcmxkLiBIZWxsbywgd29ybGQuIg==' - }}) - }) - }) - }) - - describe('requestDataEnvelope', () => { - it('sends request to experian rtd and stores response', () => { - const moduleConfig = { params: { accountId: 'ZylatYg', bidders: ['pubmatic', 'sovrn'] } } - experianRtdObj.requestDataEnvelope(moduleConfig, { gdpr: { gdprApplies: 0, consentString: 'wow' }, uspConsent: '1YYY' }) - requests[0].respond( - 200, - { 'Content-Type': 'application/json' }, - '{"staleAt":"2023-06-01T00:00:00","expiresAt":"2023-06-03T00:00:00","status":"ok","data":[{"bidder":"pubmatic","data":{"key":"pubmatic-encryption-key-1","data":"IkhlbGxvLCB3b3JsZC4gSGVsbG8sIHdvcmxkLiBIZWxsbywgd29ybGQuIg=="}},{"bidder":"sovrn","data":{"key":"sovrn-encryption-key-1","data":"IkhlbGxvLCB3b3JsZC4gSGVsbG8sIHdvcmxkLiBIZWxsbywgd29ybGQuIg=="}}]}' - ) - - 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(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') - }) - }) - - describe('extractConsentQueryString', () => { - describe('when userConsent is empty', () => { - it('returns undefined', () => { - expect(experianRtdObj.extractConsentQueryString({})).to.be.undefined - }) - }) - - describe('when userConsent exists', () => { - it('builds query string', () => { - expect( - experianRtdObj.extractConsentQueryString({}, { gdpr: { gdprApplies: 1, consentString: 'this-is-something' }, uspConsent: '1YYY' }) - ).to.equal('?gdpr=1&gdpr_consent=this-is-something&us_privacy=1YYY') - }) - }) - - describe('when config.ids exists', () => { - it('builds query string', () => { - expect(experianRtdObj.extractConsentQueryString({ params: { accountId: 'ZylatYg', ids: { maid: ['424', '2982'], hem: 'my-hem' } } }, { gdpr: { gdprApplies: 1, consentString: 'this-is-something' }, uspConsent: '1YYY' })) - .to.equal('?gdpr=1&gdpr_consent=this-is-something&us_privacy=1YYY&id.maid=424&id.maid=2982&id.hem=my-hem') - }) - }) - }) -}) diff --git a/test/spec/modules/feedadBidAdapter_spec.js b/test/spec/modules/feedadBidAdapter_spec.js index cb81c6f06de..152adba9d00 100644 --- a/test/spec/modules/feedadBidAdapter_spec.js +++ b/test/spec/modules/feedadBidAdapter_spec.js @@ -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;charset=utf-8'}); }) }); }); diff --git a/test/spec/modules/fledgeForGpt_spec.js b/test/spec/modules/fledgeForGpt_spec.js deleted file mode 100644 index 60a8e196ae0..00000000000 --- a/test/spec/modules/fledgeForGpt_spec.js +++ /dev/null @@ -1,419 +0,0 @@ -import { - expect -} from 'chai'; -import * as fledge from 'modules/fledgeForGpt.js'; -import {config} from '../../../src/config.js'; -import adapterManager from '../../../src/adapterManager.js'; -import * as utils from '../../../src/utils.js'; -import * as gptUtils from '../../../libraries/gptUtils/gptUtils.js'; -import {hook} from '../../../src/hook.js'; -import 'modules/appnexusBidAdapter.js'; -import 'modules/rubiconBidAdapter.js'; -import {parseExtPrebidFledge, setImpExtAe, setResponseFledgeConfigs} from 'modules/fledgeForGpt.js'; -import * as events from 'src/events.js'; -import CONSTANTS from 'src/constants.json'; -import {getGlobal} from '../../../src/prebidGlobal.js'; - -describe('fledgeForGpt module', () => { - let sandbox; - - beforeEach(() => { - sandbox = sinon.sandbox.create(); - }); - afterEach(() => { - sandbox.restore(); - }); - describe('addComponentAuction', function () { - before(() => { - fledge.init({enabled: true}); - }); - - const fledgeAuctionConfig = { - seller: 'bidder', - mock: 'config' - }; - - describe('addComponentAuctionHook', function () { - let nextFnSpy, mockGptSlot; - beforeEach(function () { - nextFnSpy = sinon.spy(); - mockGptSlot = { - setConfig: sinon.stub(), - getAdUnitPath: () => 'mock/gpt/au' - }; - sandbox.stub(gptUtils, 'getGptSlotForAdUnitCode').callsFake(() => mockGptSlot); - }); - - it('should call next()', function () { - const request = {auctionId: 'aid', adUnitCode: 'auc'}; - fledge.addComponentAuctionHook(nextFnSpy, request, fledgeAuctionConfig); - sinon.assert.calledWith(nextFnSpy, request, fledgeAuctionConfig); - }); - - it('should collect auction configs and route them to GPT at end of auction', () => { - events.emit(CONSTANTS.EVENTS.AUCTION_INIT, {auctionId: 'aid'}); - const cf1 = {...fledgeAuctionConfig, id: 1, seller: 'b1'}; - const cf2 = {...fledgeAuctionConfig, id: 2, seller: 'b2'}; - fledge.addComponentAuctionHook(nextFnSpy, {auctionId: 'aid', adUnitCode: 'au1'}, cf1); - fledge.addComponentAuctionHook(nextFnSpy, {auctionId: 'aid', adUnitCode: 'au2'}, cf2); - events.emit(CONSTANTS.EVENTS.AUCTION_END, {auctionId: 'aid'}); - sinon.assert.calledWith(gptUtils.getGptSlotForAdUnitCode, 'au1'); - sinon.assert.calledWith(gptUtils.getGptSlotForAdUnitCode, 'au2'); - sinon.assert.calledWith(mockGptSlot.setConfig, { - componentAuction: [{ - configKey: 'b1', - auctionConfig: cf1, - }] - }); - sinon.assert.calledWith(mockGptSlot.setConfig, { - componentAuction: [{ - configKey: 'b2', - auctionConfig: cf2, - }] - }); - }); - - it('should drop auction configs after end of auction', () => { - events.emit(CONSTANTS.EVENTS.AUCTION_INIT, {auctionId: 'aid'}); - events.emit(CONSTANTS.EVENTS.AUCTION_END, {auctionId: 'aid'}); - fledge.addComponentAuctionHook(nextFnSpy, {auctionId: 'aid', adUnitCode: 'au'}, fledgeAuctionConfig); - events.emit(CONSTANTS.EVENTS.AUCTION_END, {auctionId: 'aid'}); - sinon.assert.notCalled(mockGptSlot.setConfig); - }); - - it('should augment auctionSignals with FPD', () => { - events.emit(CONSTANTS.EVENTS.AUCTION_INIT, {auctionId: 'aid'}); - fledge.addComponentAuctionHook(nextFnSpy, {auctionId: 'aid', adUnitCode: 'au1', ortb2: {fpd: 1}, ortb2Imp: {fpd: 2}}, fledgeAuctionConfig); - events.emit(CONSTANTS.EVENTS.AUCTION_END, {auctionId: 'aid'}); - sinon.assert.calledWith(mockGptSlot.setConfig, { - componentAuction: [{ - configKey: 'bidder', - auctionConfig: { - ...fledgeAuctionConfig, - auctionSignals: { - prebid: { - ortb2: {fpd: 1}, - ortb2Imp: {fpd: 2} - } - } - }, - }] - }) - }) - - 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: 'aid'}; - setup(payload, values); - }); - - it('should populate bidfloor/bidfloorcur', () => { - events.emit(CONSTANTS.EVENTS.AUCTION_INIT, {auctionId: 'aid'}); - fledge.addComponentAuctionHook(nextFnSpy, {auctionId: 'aid', adUnitCode: 'au'}, fledgeAuctionConfig); - events.emit(CONSTANTS.EVENTS.AUCTION_END, payload); - sinon.assert.calledWith(mockGptSlot.setConfig, sinon.match(arg => { - return arg.componentAuction.some(au => au.auctionConfig.auctionSignals?.prebid?.bidfloor === bidfloor && au.auctionConfig.auctionSignals?.prebid?.bidfloorcur === bidfloorcur) - })) - }) - }); - }); - }) - }) - }); - }); - }); - - describe('fledgeEnabled', function () { - const navProps = Object.fromEntries(['runAdAuction', 'joinAdInterestGroup'].map(p => [p, navigator[p]])); - - 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(); - }); - - after(function () { - Object.entries(navProps).forEach(([p, orig]) => navigator[p] = orig); - }); - - afterEach(function () { - config.resetConfig(); - }); - - const adUnits = [{ - 'code': '/19968336/header-bid-tag1', - 'mediaTypes': { - 'banner': { - 'sizes': [[728, 90]] - }, - }, - 'bids': [ - { - 'bidder': 'appnexus', - }, - { - 'bidder': 'rubicon', - }, - ] - }]; - function expectFledgeFlags(...enableFlags) { - const bidRequests = adapterManager.makeBidRequests( - adUnits, - Date.now(), - utils.getUniqueIdentifierStr(), - function callback() { - }, - [] - ); - - expect(bidRequests[0].bids[0].bidder).equals('appnexus'); - expect(bidRequests[0].fledgeEnabled).to.eql(enableFlags[0].enabled) - bidRequests[0].bids.forEach(bid => expect(bid.ortb2Imp.ext.ae).to.eql(enableFlags[0].ae)) - - expect(bidRequests[1].bids[0].bidder).equals('rubicon'); - expect(bidRequests[1].fledgeEnabled).to.eql(enableFlags[1].enabled) - bidRequests[1].bids.forEach(bid => expect(bid.ortb2Imp?.ext?.ae).to.eql(enableFlags[1].ae)); - } - - describe('with setBidderConfig()', () => { - it('should set fledgeEnabled correctly per bidder', function () { - config.setConfig({bidderSequence: 'fixed'}); - config.setBidderConfig({ - bidders: ['appnexus'], - config: { - fledgeEnabled: true, - defaultForSlots: 1, - } - }); - expectFledgeFlags({enabled: true, ae: 1}, {enabled: void 0, ae: void 0}); - }); - }); - - describe('with setConfig()', () => { - it('should set fledgeEnabled correctly per bidder', function () { - config.setConfig({ - bidderSequence: 'fixed', - fledgeForGpt: { - enabled: true, - bidders: ['appnexus'], - defaultForSlots: 1, - } - }); - expectFledgeFlags({enabled: true, ae: 1}, {enabled: void 0, ae: void 0}); - }); - - it('should set fledgeEnabled correctly for all bidders', function () { - config.setConfig({ - bidderSequence: 'fixed', - fledgeForGpt: { - enabled: true, - defaultForSlots: 1, - } - }); - expectFledgeFlags({enabled: true, ae: 1}, {enabled: true, ae: 1}); - }); - - it('should not override pub-defined ext.ae', () => { - config.setConfig({ - bidderSequence: 'fixed', - fledgeForGpt: { - enabled: true, - defaultForSlots: 1, - } - }); - Object.assign(adUnits[0], {ortb2Imp: {ext: {ae: 0}}}); - expectFledgeFlags({enabled: true, ae: 0}, {enabled: true, ae: 0}); - }) - }); - }); - - describe('ortb processors for fledge', () => { - it('imp.ext.ae should be removed if fledge is not enabled', () => { - const imp = {ext: {ae: 1}}; - setImpExtAe(imp, {}, {bidderRequest: {}}); - expect(imp.ext.ae).to.not.exist; - }); - it('imp.ext.ae should be left intact if fledge is enabled', () => { - const imp = {ext: {ae: 2}}; - setImpExtAe(imp, {}, {bidderRequest: {fledgeEnabled: true}}); - expect(imp.ext.ae).to.equal(2); - }); - describe('parseExtPrebidFledge', () => { - function packageConfigs(configs) { - return { - ext: { - prebid: { - fledge: { - auctionconfigs: configs - } - } - } - }; - } - - function generateImpCtx(fledgeFlags) { - return Object.fromEntries(Object.entries(fledgeFlags).map(([impid, fledgeEnabled]) => [impid, {imp: {ext: {ae: fledgeEnabled}}}])); - } - - function generateCfg(impid, ...ids) { - return ids.map((id) => ({impid, config: {id}})); - } - - function extractResult(ctx) { - return Object.fromEntries( - Object.entries(ctx) - .map(([impid, ctx]) => [impid, ctx.fledgeConfigs?.map(cfg => cfg.config.id)]) - .filter(([_, val]) => val != null) - ); - } - - it('should collect fledge 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))) - ); - parseExtPrebidFledge({}, resp, ctx); - expect(extractResult(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)); - parseExtPrebidFledge({}, resp, ctx); - expect(extractResult(ctx.impContext)).to.eql({}); - }); - }); - describe('setResponseFledgeConfigs', () => { - it('should set fledgeAuctionConfigs paired with their corresponding bid id', () => { - const ctx = { - impContext: { - 1: { - bidRequest: {bidId: 'bid1'}, - fledgeConfigs: [{config: {id: 1}}, {config: {id: 2}}] - }, - 2: { - bidRequest: {bidId: 'bid2'}, - fledgeConfigs: [{config: {id: 3}}] - }, - 3: { - bidRequest: {bidId: 'bid3'} - } - } - }; - const resp = {}; - setResponseFledgeConfigs(resp, {}, ctx); - expect(resp.fledgeAuctionConfigs).to.eql([ - {bidId: 'bid1', config: {id: 1}}, - {bidId: 'bid1', config: {id: 2}}, - {bidId: 'bid2', config: {id: 3}}, - ]); - }); - it('should not set fledgeAuctionConfigs if none exist', () => { - const resp = {}; - setResponseFledgeConfigs(resp, {}, { - impContext: { - 1: { - fledgeConfigs: [] - }, - 2: {} - } - }); - expect(resp).to.eql({}); - }); - }); - }); -}); diff --git a/test/spec/modules/fledge_spec.js b/test/spec/modules/fledge_spec.js new file mode 100644 index 00000000000..a81ff05596e --- /dev/null +++ b/test/spec/modules/fledge_spec.js @@ -0,0 +1,208 @@ +import { + expect +} from 'chai'; +import * as fledge from 'modules/fledgeForGpt.js'; +import {config} from '../../../src/config.js'; +import adapterManager from '../../../src/adapterManager.js'; +import * as utils from '../../../src/utils.js'; +import {hook} from '../../../src/hook.js'; +import 'modules/appnexusBidAdapter.js'; +import 'modules/rubiconBidAdapter.js'; +import {parseExtPrebidFledge, setImpExtAe, setResponseFledgeConfigs} from 'modules/fledgeForGpt.js'; + +const CODE = 'sampleBidder'; +const AD_UNIT_CODE = 'mock/placement'; + +describe('fledgeForGpt module', function() { + let nextFnSpy; + fledge.init({enabled: true}) + + const bidRequest = { + adUnitCode: AD_UNIT_CODE, + bids: [{ + bidId: '1', + bidder: CODE, + auctionId: 'first-bid-id', + adUnitCode: AD_UNIT_CODE, + transactionId: 'au', + }] + }; + const fledgeAuctionConfig = { + bidId: '1', + } + + describe('addComponentAuctionHook', function() { + beforeEach(function() { + nextFnSpy = sinon.spy(); + }); + + it('should call next() when a proper adUnitCode and fledgeAuctionConfig are provided', function() { + fledge.addComponentAuctionHook(nextFnSpy, bidRequest.adUnitCode, fledgeAuctionConfig); + expect(nextFnSpy.called).to.be.true; + }); + }); +}); + +describe('fledgeEnabled', function () { + const navProps = Object.fromEntries(['runAdAuction', 'joinAdInterestGroup'].map(p => [p, navigator[p]])) + + 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(); + }); + + after(function() { + Object.entries(navProps).forEach(([p, orig]) => navigator[p] = orig); + }) + + afterEach(function () { + config.resetConfig(); + }); + + it('should set fledgeEnabled correctly per bidder', function () { + config.setConfig({bidderSequence: 'fixed'}) + config.setBidderConfig({ + bidders: ['appnexus'], + config: { + fledgeEnabled: true, + } + }); + + const adUnits = [{ + 'code': '/19968336/header-bid-tag1', + 'mediaTypes': { + 'banner': { + 'sizes': [[728, 90]] + }, + }, + 'bids': [ + { + 'bidder': 'appnexus', + }, + { + 'bidder': 'rubicon', + }, + ] + }]; + + const bidRequests = adapterManager.makeBidRequests( + adUnits, + Date.now(), + utils.getUniqueIdentifierStr(), + function callback() {}, + [] + ); + + expect(bidRequests[0].bids[0].bidder).equals('appnexus'); + expect(bidRequests[0].fledgeEnabled).to.be.true; + + expect(bidRequests[1].bids[0].bidder).equals('rubicon'); + expect(bidRequests[1].fledgeEnabled).to.be.undefined; + }); +}); + +describe('ortb processors for fledge', () => { + describe('imp.ext.ae', () => { + it('should be removed if fledge is not enabled', () => { + const imp = {ext: {ae: 1}}; + setImpExtAe(imp, {}, {bidderRequest: {}}); + expect(imp.ext.ae).to.not.exist; + }) + it('should be left intact if fledge is enabled', () => { + const imp = {ext: {ae: false}}; + setImpExtAe(imp, {}, {bidderRequest: {fledgeEnabled: true}}); + expect(imp.ext.ae).to.equal(false); + }); + }); + describe('parseExtPrebidFledge', () => { + function packageConfigs(configs) { + return { + ext: { + prebid: { + fledge: { + auctionconfigs: configs + } + } + } + } + } + + function generateImpCtx(fledgeFlags) { + return Object.fromEntries(Object.entries(fledgeFlags).map(([impid, fledgeEnabled]) => [impid, {imp: {ext: {ae: fledgeEnabled}}}])); + } + + function generateCfg(impid, ...ids) { + return ids.map((id) => ({impid, config: {id}})); + } + + function extractResult(ctx) { + return Object.fromEntries( + Object.entries(ctx) + .map(([impid, ctx]) => [impid, ctx.fledgeConfigs?.map(cfg => cfg.config.id)]) + .filter(([_, val]) => val != null) + ); + } + + it('should collect fledge 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))) + ); + parseExtPrebidFledge({}, resp, ctx); + expect(extractResult(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)); + parseExtPrebidFledge({}, resp, ctx); + expect(extractResult(ctx.impContext)).to.eql({}); + }); + }); + describe('setResponseFledgeConfigs', () => { + it('should set fledgeAuctionConfigs paired with their corresponding bid id', () => { + const ctx = { + impContext: { + 1: { + bidRequest: {bidId: 'bid1'}, + fledgeConfigs: [{config: {id: 1}}, {config: {id: 2}}] + }, + 2: { + bidRequest: {bidId: 'bid2'}, + fledgeConfigs: [{config: {id: 3}}] + }, + 3: { + bidRequest: {bidId: 'bid3'} + } + } + }; + const resp = {}; + setResponseFledgeConfigs(resp, {}, ctx); + expect(resp.fledgeAuctionConfigs).to.eql([ + {bidId: 'bid1', config: {id: 1}}, + {bidId: 'bid1', config: {id: 2}}, + {bidId: 'bid2', config: {id: 3}}, + ]); + }); + it('should not set fledgeAuctionConfigs if none exist', () => { + const resp = {}; + setResponseFledgeConfigs(resp, {}, { + impContext: { + 1: { + fledgeConfigs: [] + }, + 2: {} + } + }); + expect(resp).to.eql({}); + }); + }); +}); diff --git a/test/spec/modules/flippBidAdapter_spec.js b/test/spec/modules/flippBidAdapter_spec.js deleted file mode 100644 index 518052ad91e..00000000000 --- a/test/spec/modules/flippBidAdapter_spec.js +++ /dev/null @@ -1,170 +0,0 @@ -import {expect} from 'chai'; -import {spec} from 'modules/flippBidAdapter'; -import {newBidder} from 'src/adapters/bidderFactory'; -const ENDPOINT = 'https://gateflipp.flippback.com/flyer-locator-service/client_bidding'; -describe('flippAdapter', 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 bid = { - bidder: 'flipp', - params: { - publisherNameIdentifier: 'random', - siteId: 1234, - zoneIds: [1, 2, 3, 4], - } - }; - it('should return true when required params found', function () { - expect(spec.isBidRequestValid(bid)).to.equal(true); - }); - - it('should return false when required params are not passed', function () { - let invalidBid = Object.assign({}, bid); - invalidBid.params = { siteId: 1234 } - expect(spec.isBidRequestValid(invalidBid)).to.equal(false); - }); - }); - - describe('buildRequests', function () { - const bidRequests = [{ - bidder: 'flipp', - params: { - siteId: 1234, - }, - adUnitCode: '/10000/unit_code', - sizes: [[300, 600]], - mediaTypes: {banner: {sizes: [[300, 600]]}}, - bidId: '237f4d1a293f99', - bidderRequestId: '1a857fa34c1c96', - auctionId: 'a297d1aa-7900-4ce4-a0aa-caa8d46c4af7', - transactionId: '00b2896c-2731-4f01-83e4-7a3ad5da13b6', - }]; - const bidderRequest = { - refererInfo: { - referer: 'http://example.com' - } - }; - - it('sends bid request to ENDPOINT via POST', function () { - const request = spec.buildRequests(bidRequests, bidderRequest); - expect(request.method).to.equal('POST'); - }); - - it('sends bid request to ENDPOINT with query parameter', function () { - const request = spec.buildRequests(bidRequests, bidderRequest); - expect(request.url).to.equal(ENDPOINT); - }); - }); - - describe('interpretResponse', function() { - it('should get correct bid response', function() { - const bidRequest = { - method: 'POST', - url: ENDPOINT, - data: { - placements: [{ - divName: 'slot', - networkId: 12345, - siteId: 12345, - adTypes: [12345], - count: 1, - prebid: { - requestId: '237f4d1a293f99', - publisherNameIdentifier: 'bid.params.publisherNameIdentifier', - height: 600, - width: 300, - }, - user: '10462725-da61-4d3a-beff-6d05239e9a6e"', - }], - url: 'http://example.com', - }, - }; - - const serverResponse = { - body: { - 'decisions': { - 'inline': [{ - 'bidCpm': 1, - 'adId': 262838368, - 'height': 600, - 'width': 300, - 'storefront': { 'flyer_id': 5435567 }, - 'prebid': { - 'requestId': '237f4d1a293f99', - 'cpm': 1.11, - 'creative': 'Returned from server', - } - }] - }, - 'location': {'city': 'Oakville'}, - }, - }; - - const expectedResponse = [ - { - bidderCode: 'flipp', - requestId: '237f4d1a293f99', - currency: 'USD', - cpm: 1.11, - netRevenue: true, - width: 300, - height: 600, - creativeId: 262838368, - ttl: 30, - ad: 'Returned from server', - } - ]; - - const result = spec.interpretResponse(serverResponse, bidRequest); - expect(result).to.have.lengthOf(1); - expect(result).to.deep.have.same.members(expectedResponse); - }); - - it('should get empty bid response when no ad is returned', function() { - const bidRequest = { - method: 'POST', - url: ENDPOINT, - data: { - placements: [{ - divName: 'slot', - networkId: 12345, - siteId: 12345, - adTypes: [12345], - count: 1, - prebid: { - requestId: '237f4d1a293f99', - publisherNameIdentifier: 'bid.params.publisherNameIdentifier', - height: 600, - width: 300, - }, - user: '10462725-da61-4d3a-beff-6d05239e9a6e"', - }], - url: 'http://example.com', - }, - }; - - const serverResponse = { - body: { - 'decisions': { - 'inline': [] - }, - 'location': {'city': 'Oakville'}, - }, - }; - - const result = spec.interpretResponse(serverResponse, bidRequest); - expect(result).to.have.lengthOf(0); - expect(result).to.deep.have.same.members([]); - }) - - it('should get empty response when bid server returns 204', function() { - expect(spec.interpretResponse({})).to.be.empty; - }); - }); -}); diff --git a/test/spec/modules/fluctBidAdapter_spec.js b/test/spec/modules/fluctBidAdapter_spec.js index ff6f8562a4e..d970f70ad85 100644 --- a/test/spec/modules/fluctBidAdapter_spec.js +++ b/test/spec/modules/fluctBidAdapter_spec.js @@ -99,79 +99,6 @@ describe('fluctAdapter', function () { expect(request.data.page).to.eql('http://example.com'); }); - it('sends no transactionId by default', function () { - const request = spec.buildRequests(bidRequests, bidderRequest)[0]; - expect(request.data.transactionId).to.eql(undefined); - }); - - it('sends ortb2Imp.ext.tid as transactionId', function () { - const request = spec.buildRequests(bidRequests.map((req) => ({ - ...req, - ortb2Imp: { - ext: { - tid: 'tid', - } - }, - })), bidderRequest)[0]; - expect(request.data.transactionId).to.eql('tid'); - }); - - it('sends no gpid by default', function () { - const request = spec.buildRequests(bidRequests, bidderRequest)[0]; - expect(request.data.gpid).to.eql(undefined); - }); - - it('sends ortb2Imp.ext.gpid as gpid', function () { - const request = spec.buildRequests(bidRequests.map((req) => ({ - ...req, - ortb2Imp: { - ext: { - gpid: 'gpid', - data: { - pbadslot: 'data-pbadslot', - adserver: { - adslot: 'data-adserver-adslot', - }, - }, - }, - }, - })), bidderRequest)[0]; - expect(request.data.gpid).to.eql('gpid'); - }); - - it('sends ortb2Imp.ext.data.pbadslot as gpid', function () { - const request = spec.buildRequests(bidRequests.map((req) => ({ - ...req, - ortb2Imp: { - ext: { - data: { - pbadslot: 'data-pbadslot', - adserver: { - adslot: 'data-adserver-adslot', - }, - }, - }, - }, - })), bidderRequest)[0]; - expect(request.data.gpid).to.eql('data-pbadslot'); - }); - - it('sends ortb2Imp.ext.data.adserver.adslot as gpid', function () { - const request = spec.buildRequests(bidRequests.map((req) => ({ - ...req, - ortb2Imp: { - ext: { - data: { - adserver: { - adslot: 'data-adserver-adslot', - }, - }, - }, - }, - })), bidderRequest)[0]; - expect(request.data.gpid).to.eql('data-adserver-adslot'); - }); - it('includes data.user.eids = [] by default', function () { const request = spec.buildRequests(bidRequests, bidderRequest)[0]; expect(request.data.user.eids).to.eql([]); @@ -192,14 +119,14 @@ describe('fluctAdapter', function () { expect(request.data.regs).to.eql(undefined); }); - it('includes filtered user.eids if any exists', function () { + it('includes filtered user.eids if any exist', function () { const bidRequests2 = bidRequests.map( - (bidReq) => Object.assign({}, bidReq, { + (bidReq) => Object.assign(bidReq, { userIdAsEids: [ { source: 'foobar.com', uids: [ - { id: 'foobar-id' }, + { id: 'foobar-id' } ], }, { @@ -211,19 +138,19 @@ describe('fluctAdapter', function () { { source: 'criteo.com', uids: [ - { id: 'criteo-id' }, + { id: 'criteo-id' } ], }, { source: 'intimatemerger.com', uids: [ - { id: 'imuid' }, + { id: 'imuid' } ], }, { source: 'liveramp.com', uids: [ - { id: 'idl-env' }, + { id: 'idl-env' } ], }, ], @@ -231,96 +158,36 @@ describe('fluctAdapter', function () { ); const request = spec.buildRequests(bidRequests2, bidderRequest)[0]; expect(request.data.user.eids).to.eql([ - { - source: 'foobar.com', - uids: [ - { id: 'foobar-id' }, - ], - }, { source: 'adserver.org', uids: [ - { id: 'tdid' }, + { id: 'tdid' } ], }, { source: 'criteo.com', uids: [ - { id: 'criteo-id' }, + { id: 'criteo-id' } ], }, { source: 'intimatemerger.com', uids: [ - { id: 'imuid' }, + { id: 'imuid' } ], }, { source: 'liveramp.com', uids: [ - { id: 'idl-env' }, + { id: 'idl-env' } ], }, ]); }); - it('includes user.data if any exists', function () { - const bidderRequest2 = Object.assign({}, bidderRequest, { - ortb2: { - user: { - data: [ - { - name: 'a1mediagroup.com', - ext: { - segtax: 900, - }, - segment: [ - { id: 'seg-1' }, - { id: 'seg-2' }, - ], - }, - ], - ext: { - eids: [ - { - source: 'a1mediagroup.com', - uids: [ - { id: 'aud-1' } - ], - }, - ], - }, - }, - }, - }); - const request = spec.buildRequests(bidRequests, bidderRequest2)[0]; - expect(request.data.user).to.eql({ - data: [ - { - name: 'a1mediagroup.com', - ext: { - segtax: 900, - }, - segment: [ - { id: 'seg-1' }, - { id: 'seg-2' }, - ], - }, - ], - eids: [ - { - source: 'a1mediagroup.com', - uids: [ - { id: 'aud-1' } - ], - }, - ], - }); - }); - it('includes data.params.kv if any exists', function () { const bidRequests2 = bidRequests.map( - (bidReq) => Object.assign({}, bidReq, { + (bidReq) => Object.assign(bidReq, { params: { kv: { imsids: ['imsid1', 'imsid2'] @@ -337,7 +204,7 @@ describe('fluctAdapter', function () { it('includes data.schain if any exists', function () { // this should be done by schain.js const bidRequests2 = bidRequests.map( - (bidReq) => Object.assign({}, bidReq, { + (bidReq) => Object.assign(bidReq, { schain: { ver: '1.0', complete: 1, @@ -404,7 +271,7 @@ describe('fluctAdapter', function () { }); }); - describe('should interpretResponse', function() { + describe('interpretResponse', function() { const callBeaconSnippet = '', + 'adid': '144762342', + 'adomain': [ + 'https://dummydomain.com' + ], + 'iurl': 'iurl', + 'cid': '109', + 'crid': 'creativeId', + 'cat': [], + 'w': 300, + 'h': 250, + 'ext': { + 'prebid': { + 'type': 'banner' + }, + 'bidder': { + 'appnexus': { + 'brand_id': 334553, + 'auction_id': 514667951122925701, + 'bidder_id': 2, + 'bid_ad_type': 0 + } + } + } + }, + { + 'id': 'bidId2', + 'impid': 'bidId2', + 'price': 0.1, + 'adm': '', + 'adid': '144762342', + 'adomain': [ + 'https://dummydomain.com' + ], + 'iurl': 'iurl', + 'cid': '109', + 'crid': 'creativeId', + 'cat': [], + 'w': 300, + 'h': 250, + 'ext': { + 'prebid': { + 'type': 'banner' + }, + 'bidder': { + 'appnexus': { + 'brand_id': 386046, + 'auction_id': 517067951122925501, + 'bidder_id': 2, + 'bid_ad_type': 0 + } + } + } + } + ], + 'seat': 'kulturemedia' + } + ], + 'ext': { + 'usersync': { + 'sovrn': { + 'status': 'none', + 'syncs': [ + { + 'url': 'urlsovrn', + 'type': 'iframe' + } + ] + }, + 'appnexus': { + 'status': 'none', + 'syncs': [ + { + 'url': 'urlappnexus', + 'type': 'pixel' + } + ] + } + }, + 'responsetimemillis': { + 'appnexus': 127 + } + } + } +}; + +const DEFAULT_NETWORK_ID = 1; + +describe('kulturemediaBidAdapter:', function () { + let videoBidRequest; + + const VIDEO_REQUEST = { + 'bidderCode': 'kulturemedia', + 'auctionId': 'e158486f-8c7f-472f-94ce-b0cbfbb50ab4', + 'bidderRequestId': '34feaad34lkj2', + 'bids': videoBidRequest, + 'auctionStart': 1520001292880, + 'timeout': 3000, + 'start': 1520001292884, + 'doneCbCallCount': 0, + 'refererInfo': { + 'numIframes': 1, + 'reachedTop': true, + 'referer': 'test.com' + } + }; + + beforeEach(function () { + videoBidRequest = { + mediaTypes: { + video: { + context: 'instream', + playerSize: [[640, 480]], + } + }, + bidder: 'kulturemedia', + sizes: [640, 480], + bidId: '30b3efwfwe1e', + adUnitCode: 'video1', + params: { + video: { + playerWidth: 640, + playerHeight: 480, + mimes: ['video/mp4', 'application/javascript'], + protocols: [2, 5], + api: [2], + position: 1, + delivery: [2], + sid: 134, + rewarded: 1, + placement: 1, + hp: 1, + inventoryid: 123 + }, + site: { + id: 1, + page: 'https://test.com', + referrer: 'http://test.com' + }, + publisherId: 'km123' + } + }; + }); + + describe('isBidRequestValid', function () { + context('basic validation', function () { + beforeEach(function () { + // Basic Valid BidRequest + this.bid = { + bidder: 'kulturemedia', + mediaTypes: { + banner: { + sizes: [[250, 300]] + } + }, + params: { + placementId: 'placementId', + publisherId: 'publisherId', + } + }; + }); + + it('should accept request if placementId and publisherId are passed', function () { + expect(spec.isBidRequestValid(this.bid)).to.be.true; + }); + + it('reject requests without params', function () { + this.bid.params = {}; + expect(spec.isBidRequestValid(this.bid)).to.be.false; + }); + + it('returns false when banner mediaType does not exist', function () { + this.bid.mediaTypes = {} + expect(spec.isBidRequestValid(this.bid)).to.be.false; + }); + }); + + context('banner validation', function () { + it('returns true when banner sizes are defined', function () { + const bid = { + bidder: 'kulturemedia', + mediaTypes: { + banner: { + sizes: [[250, 300]] + } + }, + params: { + placementId: 'placementId', + publisherId: 'publisherId', + } + }; + + expect(spec.isBidRequestValid(bid)).to.be.true; + }); + + it('returns false when banner sizes are invalid', function () { + const invalidSizes = [ + undefined, + '2:1', + 123, + 'test' + ]; + + invalidSizes.forEach((sizes) => { + const bid = { + bidder: 'kulturemedia', + mediaTypes: { + banner: { + sizes + } + }, + params: { + placementId: 'placementId', + publisherId: 'publisherId', + } + }; + + expect(spec.isBidRequestValid(bid)).to.be.false; + }); + }); + }); + + context('video validation', function () { + beforeEach(function () { + // Basic Valid BidRequest + this.bid = { + bidder: 'kulturemedia', + mediaTypes: { + video: { + playerSize: [[300, 50]], + context: 'instream', + mimes: ['foo', 'bar'], + protocols: [1, 2] + } + }, + params: { + placementId: 'placementId', + publisherId: 'publisherId', + } + }; + }); + + it('should return true (skip validations) when e2etest = true', function () { + this.bid.params = { + e2etest: true + }; + expect(spec.isBidRequestValid(this.bid)).to.equal(true); + }); + + it('returns false when video context is not defined', function () { + delete this.bid.mediaTypes.video.context; + + expect(spec.isBidRequestValid(this.bid)).to.be.false; + }); + + it('returns false when video playserSize is invalid', function () { + const invalidSizes = [ + undefined, + '2:1', + 123, + 'test' + ]; + + invalidSizes.forEach((playerSize) => { + this.bid.mediaTypes.video.playerSize = playerSize; + expect(spec.isBidRequestValid(this.bid)).to.be.false; + }); + }); + + it('returns false when video mimes is invalid', function () { + const invalidMimes = [ + undefined, + 'test', + 1, + [] + ] + + invalidMimes.forEach((mimes) => { + this.bid.mediaTypes.video.mimes = mimes; + expect(spec.isBidRequestValid(this.bid)).to.be.false; + }) + }); + + it('returns false when video protocols is invalid', function () { + const invalidMimes = [ + undefined, + 'test', + 1, + [] + ] + + invalidMimes.forEach((protocols) => { + this.bid.mediaTypes.video.protocols = protocols; + expect(spec.isBidRequestValid(this.bid)).to.be.false; + }) + }); + }); + }); + + describe('buildRequests', function () { + context('when mediaType is banner', function () { + it('creates request data', function () { + let request = spec.buildRequests(BANNER_REQUEST.bidRequest, BANNER_REQUEST); + + expect(request).to.exist.and.to.be.a('object'); + const payload = JSON.parse(request.data); + expect(payload.imp[0]).to.have.property('id', BANNER_REQUEST.bidRequest[0].bidId); + expect(payload.imp[1]).to.have.property('id', BANNER_REQUEST.bidRequest[1].bidId); + }); + + it('has gdpr data if applicable', function () { + const req = Object.assign({}, BANNER_REQUEST, { + gdprConsent: { + consentString: 'consentString', + gdprApplies: true, + } + }); + let request = spec.buildRequests(BANNER_REQUEST.bidRequest, req); + + const payload = JSON.parse(request.data); + expect(payload.user.ext).to.have.property('consent', req.gdprConsent.consentString); + expect(payload.regs.ext).to.have.property('gdpr', 1); + }); + + it('should properly forward eids parameters', function () { + const req = Object.assign({}, BANNER_REQUEST); + req.bidRequest[0].userIdAsEids = [ + { + source: 'dummy.com', + uids: [ + { + id: 'd6d0a86c-20c6-4410-a47b-5cba383a698a', + atype: 1 + } + ] + }]; + let request = spec.buildRequests(req.bidRequest, req); + + const payload = JSON.parse(request.data); + expect(payload.user.ext.eids[0].source).to.equal('dummy.com'); + expect(payload.user.ext.eids[0].uids[0].id).to.equal('d6d0a86c-20c6-4410-a47b-5cba383a698a'); + expect(payload.user.ext.eids[0].uids[0].atype).to.equal(1); + }); + }); + + context('when mediaType is video', function () { + it('should create a POST request for every bid', function () { + const requests = spec.buildRequests([videoBidRequest], VIDEO_REQUEST); + expect(requests.method).to.equal('POST'); + expect(requests.url.trim()).to.equal(spec.ENDPOINT + '?pid=' + videoBidRequest.params.publisherId + '&nId=' + DEFAULT_NETWORK_ID); + }); + + it('should attach request data', function () { + const requests = spec.buildRequests([videoBidRequest], VIDEO_REQUEST); + const data = JSON.parse(requests.data); + const [width, height] = videoBidRequest.sizes; + const VERSION = '1.0.0'; + expect(data.imp[0].video.w).to.equal(width); + expect(data.imp[0].video.h).to.equal(height); + expect(data.imp[0].bidfloor).to.equal(videoBidRequest.params.bidfloor); + expect(data.ext.prebidver).to.equal('$prebid.version$'); + expect(data.ext.adapterver).to.equal(spec.VERSION); + }); + + it('should set pubId to e2etest when bid.params.e2etest = true', function () { + videoBidRequest.params.e2etest = true; + const requests = spec.buildRequests([videoBidRequest], VIDEO_REQUEST); + expect(requests.method).to.equal('POST'); + expect(requests.url).to.equal(spec.ENDPOINT + '?pid=e2etest&nId=' + DEFAULT_NETWORK_ID); + }); + + it('should attach End 2 End test data', function () { + videoBidRequest.params.e2etest = true; + const requests = spec.buildRequests([videoBidRequest], VIDEO_REQUEST); + const data = JSON.parse(requests.data); + expect(data.imp[0].bidfloor).to.not.exist; + expect(data.imp[0].video.w).to.equal(640); + expect(data.imp[0].video.h).to.equal(480); + }); + }); + }); + + describe('interpretResponse', function () { + context('when mediaType is banner', function () { + it('have bids', function () { + let bids = spec.interpretResponse(RESPONSE, BANNER_REQUEST); + expect(bids).to.be.an('array').that.is.not.empty; + validateBidOnIndex(0); + validateBidOnIndex(1); + + function validateBidOnIndex(index) { + expect(bids[index]).to.have.property('currency', 'USD'); + expect(bids[index]).to.have.property('requestId', RESPONSE.body.seatbid[0].bid[index].impid); + expect(bids[index]).to.have.property('cpm', RESPONSE.body.seatbid[0].bid[index].price); + expect(bids[index]).to.have.property('width', RESPONSE.body.seatbid[0].bid[index].w); + expect(bids[index]).to.have.property('height', RESPONSE.body.seatbid[0].bid[index].h); + expect(bids[index]).to.have.property('ad', RESPONSE.body.seatbid[0].bid[index].adm); + expect(bids[index]).to.have.property('creativeId', RESPONSE.body.seatbid[0].bid[index].crid); + expect(bids[index].meta).to.have.property('advertiserDomains', RESPONSE.body.seatbid[0].bid[index].adomain); + expect(bids[index]).to.have.property('ttl', 300); + expect(bids[index]).to.have.property('netRevenue', true); + } + }); + + it('handles empty response', function () { + const EMPTY_RESP = Object.assign({}, RESPONSE, {'body': {}}); + const bids = spec.interpretResponse(EMPTY_RESP, BANNER_REQUEST); + + expect(bids).to.be.empty; + }); + }); + + context('when mediaType is video', function () { + it('should return no bids if the response is not valid', function () { + const bidResponse = spec.interpretResponse({ + body: null + }, { + videoBidRequest + }); + expect(bidResponse.length).to.equal(0); + }); + + it('should return no bids if the response "nurl" and "adm" are missing', function () { + const serverResponse = { + seatbid: [{ + bid: [{ + price: 6.01 + }] + }] + }; + const bidResponse = spec.interpretResponse({ + body: serverResponse + }, { + videoBidRequest + }); + expect(bidResponse.length).to.equal(0); + }); + + it('should return no bids if the response "price" is missing', function () { + const serverResponse = { + seatbid: [{ + bid: [{ + adm: '' + }] + }] + }; + const bidResponse = spec.interpretResponse({ + body: serverResponse + }, { + videoBidRequest + }); + expect(bidResponse.length).to.equal(0); + }); + + it('should return a valid video bid response with just "adm"', function () { + const serverResponse = { + id: '123', + seatbid: [{ + bid: [{ + id: 1, + adid: 123, + impid: 456, + crid: 2, + price: 6.01, + adm: '', + adomain: [ + 'kulturemedia.com' + ], + w: 640, + h: 480, + ext: { + prebid: { + type: 'video' + }, + } + }] + }], + cur: 'USD' + }; + const bidResponse = spec.interpretResponse({ + body: serverResponse + }, { + videoBidRequest + }); + let o = { + requestId: serverResponse.seatbid[0].bid[0].impid, + ad: '', + cpm: serverResponse.seatbid[0].bid[0].price, + creativeId: serverResponse.seatbid[0].bid[0].crid, + vastXml: serverResponse.seatbid[0].bid[0].adm, + width: 640, + height: 480, + mediaType: 'video', + currency: 'USD', + ttl: 300, + netRevenue: true, + meta: { + advertiserDomains: ['kulturemedia.com'] + } + }; + expect(bidResponse[0]).to.deep.equal(o); + }); + + it('should default ttl to 300', function () { + const serverResponse = { + seatbid: [{bid: [{id: 1, adid: 123, crid: 2, price: 6.01, adm: ''}]}], + cur: 'USD' + }; + const bidResponse = spec.interpretResponse({body: serverResponse}, {videoBidRequest}); + expect(bidResponse[0].ttl).to.equal(300); + }); + it('should not allow ttl above 3601, default to 300', function () { + videoBidRequest.params.video.ttl = 3601; + const serverResponse = { + seatbid: [{bid: [{id: 1, adid: 123, crid: 2, price: 6.01, adm: ''}]}], + cur: 'USD' + }; + const bidResponse = spec.interpretResponse({body: serverResponse}, {videoBidRequest}); + expect(bidResponse[0].ttl).to.equal(300); + }); + it('should not allow ttl below 1, default to 300', function () { + videoBidRequest.params.video.ttl = 0; + const serverResponse = { + seatbid: [{bid: [{id: 1, adid: 123, crid: 2, price: 6.01, adm: ''}]}], + cur: 'USD' + }; + const bidResponse = spec.interpretResponse({body: serverResponse}, {videoBidRequest}); + expect(bidResponse[0].ttl).to.equal(300); + }); + }); + }); + + describe('getUserSyncs', function () { + it('handles no parameters', function () { + let opts = spec.getUserSyncs({}); + expect(opts).to.be.an('array').that.is.empty; + }); + it('returns non if sync is not allowed', function () { + let opts = spec.getUserSyncs({iframeEnabled: false, pixelEnabled: false}); + + expect(opts).to.be.an('array').that.is.empty; + }); + + it('iframe sync enabled should return results', function () { + let opts = spec.getUserSyncs({iframeEnabled: true, pixelEnabled: false}, [RESPONSE]); + + expect(opts.length).to.equal(1); + expect(opts[0].type).to.equal('iframe'); + expect(opts[0].url).to.equal(RESPONSE.body.ext.usersync['sovrn'].syncs[0].url); + }); + + it('pixel sync enabled should return results', function () { + let opts = spec.getUserSyncs({iframeEnabled: false, pixelEnabled: true}, [RESPONSE]); + + expect(opts.length).to.equal(1); + expect(opts[0].type).to.equal('image'); + expect(opts[0].url).to.equal(RESPONSE.body.ext.usersync['appnexus'].syncs[0].url); + }); + + it('all sync enabled should return all results', function () { + let opts = spec.getUserSyncs({iframeEnabled: true, pixelEnabled: true}, [RESPONSE]); + + expect(opts.length).to.equal(2); + }); + }); +}) +; diff --git a/test/spec/modules/lassoBidAdapter_spec.js b/test/spec/modules/lassoBidAdapter_spec.js index 3695889aca0..5825927a931 100644 --- a/test/spec/modules/lassoBidAdapter_spec.js +++ b/test/spec/modules/lassoBidAdapter_spec.js @@ -79,12 +79,10 @@ describe('lassoBidAdapter', function () { }); describe('buildRequests', function () { - let validBidRequests, bidRequest; - before(() => { - validBidRequests = spec.buildRequests([bid], bidderRequest); - expect(validBidRequests).to.be.an('array').that.is.not.empty; - bidRequest = validBidRequests[0]; - }) + const validBidRequests = spec.buildRequests([bid], bidderRequest); + expect(validBidRequests).to.be.an('array').that.is.not.empty; + + const bidRequest = validBidRequests[0]; it('Returns valid bidRequest', function () { expect(bidRequest).to.exist; diff --git a/test/spec/modules/liveIntentIdMinimalSystem_spec.js b/test/spec/modules/liveIntentIdMinimalSystem_spec.js index ad21d7b6763..5640a6d36cb 100644 --- a/test/spec/modules/liveIntentIdMinimalSystem_spec.js +++ b/test/spec/modules/liveIntentIdMinimalSystem_spec.js @@ -73,7 +73,7 @@ describe('LiveIntentMinimalId', function() { expect(callBackSpy.calledOnce).to.be.true; }); - it('should call the Identity Exchange endpoint with the provided distributorId', function() { + it('should call the Identity Exchange endpoint with the privided distributorId', function() { getCookieStub.returns(null); let callBackSpy = sinon.spy(); let submoduleCallback = liveIntentIdSubmodule.getId({ params: { fireEventDelay: 1, distributorId: 'did-1111' } }).callback; @@ -87,7 +87,7 @@ describe('LiveIntentMinimalId', function() { expect(callBackSpy.calledOnceWith({})).to.be.true; }); - it('should call the Identity Exchange endpoint without the provided distributorId when appId is provided', function() { + it('should call the Identity Exchange endpoint without the privided distributorId when appId is provided', function() { getCookieStub.returns(null); let callBackSpy = sinon.spy(); let submoduleCallback = liveIntentIdSubmodule.getId({ params: { fireEventDelay: 1, distributorId: 'did-1111', liCollectConfig: { appId: 'a-0001' } } }).callback; @@ -243,47 +243,12 @@ describe('LiveIntentMinimalId', function() { it('should decode a uid2 to a seperate 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'}}); }); it('should decode values with uid2 but no nonId', function() { const result = liveIntentIdSubmodule.decode({ uid2: 'bar' }); - expect(result).to.eql({'uid2': {'id': 'bar', 'ext': {'provider': 'liveintent.com'}}}); - }); - - it('should decode a bidswitch id to a seperate 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'}}}); - }); - - it('should decode a medianet id to a seperate 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'}}}); - }); - - it('should decode a sovrn id to a seperate 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'}}}); - }); - - it('should decode a magnite id to a seperate 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'}}}); - }); - - it('should decode an index id to a seperate 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'}}}); - }); - - it('should decode an openx id to a seperate 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'}}}); - }); - - it('should decode an pubmatic id to a seperate 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({'uid2': {'id': 'bar'}}); }); it('should allow disabling nonId resolution', function() { diff --git a/test/spec/modules/liveIntentIdSystem_spec.js b/test/spec/modules/liveIntentIdSystem_spec.js index 3af598c5d4e..859db423eac 100644 --- a/test/spec/modules/liveIntentIdSystem_spec.js +++ b/test/spec/modules/liveIntentIdSystem_spec.js @@ -1,6 +1,6 @@ import { liveIntentIdSubmodule, reset as resetLiveIntentIdSubmodule, storage } from 'modules/liveIntentIdSystem.js'; import * as utils from 'src/utils.js'; -import { gdprDataHandler, uspDataHandler, gppDataHandler } from '../../../src/adapterManager.js'; +import { gdprDataHandler, uspDataHandler } from '../../../src/adapterManager.js'; import { server } from 'test/mocks/xhr.js'; resetLiveIntentIdSubmodule(); liveIntentIdSubmodule.setModuleMode('standard') @@ -12,7 +12,6 @@ describe('LiveIntentId', function() { let logErrorStub; let uspConsentDataStub; let gdprConsentDataStub; - let gppConsentDataStub; let getCookieStub; let getDataFromLocalStorageStub; let imgStub; @@ -25,7 +24,6 @@ describe('LiveIntentId', function() { logErrorStub = sinon.stub(utils, 'logError'); uspConsentDataStub = sinon.stub(uspDataHandler, 'getConsentData'); gdprConsentDataStub = sinon.stub(gdprDataHandler, 'getConsentData'); - gppConsentDataStub = sinon.stub(gppDataHandler, 'getConsentData'); }); afterEach(function() { @@ -35,7 +33,6 @@ describe('LiveIntentId', function() { logErrorStub.restore(); uspConsentDataStub.restore(); gdprConsentDataStub.restore(); - gppConsentDataStub.restore(); resetLiveIntentIdSubmodule(); }); @@ -45,15 +42,11 @@ describe('LiveIntentId', function() { gdprApplies: true, consentString: 'consentDataString' }) - gppConsentDataStub.returns({ - gppString: 'gppConsentDataString', - applicableSections: [1, 2] - }) let callBackSpy = sinon.spy(); let submoduleCallback = liveIntentIdSubmodule.getId(defaultConfigParams).callback; submoduleCallback(callBackSpy); let request = server.requests[0]; - expect(request.url).to.match(/.*us_privacy=1YNY.*&gdpr=1&n3pc=1&gdpr_consent=consentDataString.*&gpp_s=gppConsentDataString&gpp_as=1%2C2.*/); + expect(request.url).to.match(/.*us_privacy=1YNY.*&gdpr=1&n3pc=1&gdpr_consent=consentDataString.*/); const response = { unifiedId: 'a_unified_id', segments: [123, 234] @@ -72,13 +65,9 @@ describe('LiveIntentId', function() { gdprApplies: true, consentString: 'consentDataString' }) - gppConsentDataStub.returns({ - gppString: 'gppConsentDataString', - applicableSections: [1] - }) liveIntentIdSubmodule.getId(defaultConfigParams); setTimeout(() => { - expect(server.requests[0].url).to.match(/https:\/\/rp.liadm.com\/j\?.*&us_privacy=1YNY.*&wpn=prebid.*&gdpr=1&n3pc=1&n3pct=1&nb=1&gdpr_consent=consentDataString&gpp_s=gppConsentDataString&gpp_as=1.*/); + expect(server.requests[0].url).to.match(/https:\/\/rp.liadm.com\/j\?.*&us_privacy=1YNY.*&wpn=prebid.*&gdpr=1&n3pc=1&n3pct=1&nb=1&gdpr_consent=consentDataString.*/); done(); }, 200); }); @@ -94,16 +83,6 @@ describe('LiveIntentId', function() { }, 200); }); - it('should initialize LiveConnect and forward the prebid version when decode and emit an event', function(done) { - liveIntentIdSubmodule.decode({}, { params: { - ...defaultConfigParams - }}); - setTimeout(() => { - expect(server.requests[0].url).to.contain('tv=$prebid.version$') - done(); - }, 200); - }); - it('should initialize LiveConnect with the config params when decode and emit an event', function (done) { liveIntentIdSubmodule.decode({}, { params: { ...defaultConfigParams.params, @@ -144,13 +123,9 @@ describe('LiveIntentId', function() { gdprApplies: false, consentString: 'consentDataString' }) - gppConsentDataStub.returns({ - gppString: 'gppConsentDataString', - applicableSections: [1] - }) liveIntentIdSubmodule.decode({}, defaultConfigParams); setTimeout(() => { - expect(server.requests[0].url).to.match(/.*us_privacy=1YNY.*&gdpr=0&gdpr_consent=consentDataString.*&gpp_s=gppConsentDataString&gpp_as=1.*/); + expect(server.requests[0].url).to.match(/.*us_privacy=1YNY.*&gdpr=0&gdpr_consent=consentDataString.*/); done(); }, 200); }); @@ -196,7 +171,7 @@ describe('LiveIntentId', function() { let submoduleCallback = liveIntentIdSubmodule.getId({ params: {...defaultConfigParams.params, ...{'url': 'https://dummy.liveintent.com/idex'}} }).callback; submoduleCallback(callBackSpy); let request = server.requests[0]; - expect(request.url).to.be.eq('https://dummy.liveintent.com/idex/prebid/89899?cd=.localhost&resolve=nonId'); + expect(request.url).to.be.eq('https://dummy.liveintent.com/idex/prebid/89899?resolve=nonId'); request.respond( 204, responseHeader @@ -204,13 +179,13 @@ describe('LiveIntentId', function() { expect(callBackSpy.calledOnceWith({})).to.be.true; }); - it('should call the Identity Exchange endpoint with the provided distributorId', function() { + it('should call the Identity Exchange endpoint with the privided distributorId', function() { getCookieStub.returns(null); let callBackSpy = sinon.spy(); let submoduleCallback = liveIntentIdSubmodule.getId({ params: { fireEventDelay: 1, distributorId: 'did-1111' } }).callback; submoduleCallback(callBackSpy); let request = server.requests[0]; - expect(request.url).to.be.eq('https://idx.liadm.com/idex/did-1111/any?did=did-1111&cd=.localhost&resolve=nonId'); + expect(request.url).to.be.eq('https://idx.liadm.com/idex/did-1111/any?did=did-1111&resolve=nonId'); request.respond( 204, responseHeader @@ -218,13 +193,13 @@ describe('LiveIntentId', function() { expect(callBackSpy.calledOnceWith({})).to.be.true; }); - it('should call the Identity Exchange endpoint without the provided distributorId when appId is provided', function() { + it('should call the Identity Exchange endpoint without the privided distributorId when appId is provided', function() { getCookieStub.returns(null); let callBackSpy = sinon.spy(); let submoduleCallback = liveIntentIdSubmodule.getId({ params: { fireEventDelay: 1, distributorId: 'did-1111', liCollectConfig: { appId: 'a-0001' } } }).callback; submoduleCallback(callBackSpy); let request = server.requests[0]; - expect(request.url).to.be.eq('https://idx.liadm.com/idex/prebid/any?cd=.localhost&resolve=nonId'); + expect(request.url).to.be.eq('https://idx.liadm.com/idex/prebid/any?resolve=nonId'); request.respond( 204, responseHeader @@ -244,7 +219,7 @@ describe('LiveIntentId', function() { } }).callback; submoduleCallback(callBackSpy); let request = server.requests[0]; - expect(request.url).to.be.eq('https://dummy.liveintent.com/idex/rubicon/89899?cd=.localhost&resolve=nonId'); + expect(request.url).to.be.eq('https://dummy.liveintent.com/idex/rubicon/89899?resolve=nonId'); request.respond( 200, responseHeader, @@ -259,7 +234,7 @@ describe('LiveIntentId', function() { let submoduleCallback = liveIntentIdSubmodule.getId(defaultConfigParams).callback; submoduleCallback(callBackSpy); let request = server.requests[0]; - expect(request.url).to.be.eq('https://idx.liadm.com/idex/prebid/89899?cd=.localhost&resolve=nonId'); + expect(request.url).to.be.eq('https://idx.liadm.com/idex/prebid/89899?resolve=nonId'); request.respond( 200, responseHeader, @@ -274,7 +249,7 @@ describe('LiveIntentId', function() { let submoduleCallback = liveIntentIdSubmodule.getId(defaultConfigParams).callback; submoduleCallback(callBackSpy); let request = server.requests[0]; - expect(request.url).to.be.eq('https://idx.liadm.com/idex/prebid/89899?cd=.localhost&resolve=nonId'); + expect(request.url).to.be.eq('https://idx.liadm.com/idex/prebid/89899?resolve=nonId'); request.respond( 503, responseHeader, @@ -291,7 +266,7 @@ describe('LiveIntentId', function() { let submoduleCallback = liveIntentIdSubmodule.getId(defaultConfigParams).callback; submoduleCallback(callBackSpy); let request = server.requests[0]; - expect(request.url).to.be.eq(`https://idx.liadm.com/idex/prebid/89899?duid=${oldCookie}&cd=.localhost&resolve=nonId`); + expect(request.url).to.be.eq(`https://idx.liadm.com/idex/prebid/89899?duid=${oldCookie}&resolve=nonId`); request.respond( 200, responseHeader, @@ -314,7 +289,7 @@ describe('LiveIntentId', function() { let submoduleCallback = liveIntentIdSubmodule.getId(configParams).callback; submoduleCallback(callBackSpy); let request = server.requests[0]; - expect(request.url).to.be.eq(`https://idx.liadm.com/idex/prebid/89899?duid=${oldCookie}&cd=.localhost&_thirdPC=third-pc&resolve=nonId`); + expect(request.url).to.be.eq(`https://idx.liadm.com/idex/prebid/89899?duid=${oldCookie}&_thirdPC=third-pc&resolve=nonId`); request.respond( 200, responseHeader, @@ -336,7 +311,7 @@ describe('LiveIntentId', function() { let submoduleCallback = liveIntentIdSubmodule.getId(configParams).callback; submoduleCallback(callBackSpy); let request = server.requests[0]; - expect(request.url).to.be.eq('https://idx.liadm.com/idex/prebid/89899?cd=.localhost&_thirdPC=%7B%22key%22%3A%22value%22%7D&resolve=nonId'); + expect(request.url).to.be.eq('https://idx.liadm.com/idex/prebid/89899?_thirdPC=%7B%22key%22%3A%22value%22%7D&resolve=nonId'); request.respond( 200, responseHeader, @@ -369,7 +344,7 @@ describe('LiveIntentId', function() { } }).callback; submoduleCallback(callBackSpy); let request = server.requests[0]; - expect(request.url).to.be.eq(`https://idx.liadm.com/idex/prebid/89899?cd=.localhost&resolve=nonId&resolve=foo`); + expect(request.url).to.be.eq(`https://idx.liadm.com/idex/prebid/89899?resolve=nonId&resolve=foo`); request.respond( 200, responseHeader, @@ -378,49 +353,24 @@ describe('LiveIntentId', function() { expect(callBackSpy.calledOnce).to.be.true; }); - it('should decode a uid2 to a separate object when present', function() { + it('should decode a uid2 to a seperate 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'}}}); - }); - - it('should decode values with uid2 but no nonId', function() { - const result = liveIntentIdSubmodule.decode({ uid2: 'bar' }); - expect(result).to.eql({'uid2': {'id': 'bar', 'ext': {'provider': 'liveintent.com'}}}); + expect(result).to.eql({'lipb': {'lipbid': 'foo', 'nonId': 'foo', 'uid2': 'bar'}, 'uid2': {'id': 'bar'}}); }); - it('should decode a bidswitch id to a separate object when present', function() { + it('should decode a bidswitch id to a seperate 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'}}); }); - it('should decode a medianet id to a separate object when present', function() { + it('should decode a medianet id to a seperate 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'}}}); - }); - - 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'}}}); - }); - - 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', 'medianet': 'bar'}, 'medianet': {'id': 'bar'}}); }); - 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'}}}); - }); - - 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'}}}); - }); - - 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'}}}); + it('should decode values with uid2 but no nonId', function() { + const result = liveIntentIdSubmodule.decode({ uid2: 'bar' }); + expect(result).to.eql({'uid2': {'id': 'bar'}}); }); it('should allow disabling nonId resolution', function() { @@ -431,7 +381,7 @@ describe('LiveIntentId', function() { } }).callback; submoduleCallback(callBackSpy); let request = server.requests[0]; - expect(request.url).to.be.eq(`https://idx.liadm.com/idex/prebid/89899?cd=.localhost&resolve=uid2`); + expect(request.url).to.be.eq(`https://idx.liadm.com/idex/prebid/89899?resolve=uid2`); request.respond( 200, responseHeader, diff --git a/test/spec/modules/lm_kiviadsBidAdapter_spec.js b/test/spec/modules/lm_kiviadsBidAdapter_spec.js deleted file mode 100644 index 68ac73289cd..00000000000 --- a/test/spec/modules/lm_kiviadsBidAdapter_spec.js +++ /dev/null @@ -1,455 +0,0 @@ -import {expect} from 'chai'; -import {config} from 'src/config.js'; -import {spec, getBidFloor} from 'modules/lm_kiviadsBidAdapter.js'; -import {deepClone} from 'src/utils'; - -const ENDPOINT = 'https://pbjs.kiviads.live'; - -const defaultRequest = { - adUnitCode: 'test', - bidId: '1', - requestId: 'qwerty', - ortb2: { - source: { - tid: 'auctionId' - } - }, - ortb2Imp: { - ext: { - tid: 'tr1', - } - }, - mediaTypes: { - banner: { - sizes: [ - [300, 250], - [300, 200] - ] - } - }, - bidder: 'lm_kiviads', - params: { - env: 'lm_kiviads', - pid: '40', - ext: {} - }, - bidRequestsCount: 1 -}; - -const defaultRequestVideo = deepClone(defaultRequest); -defaultRequestVideo.mediaTypes = { - video: { - playerSize: [640, 480], - context: 'instream', - skipppable: true - } -}; -describe('lm_kiviadsBidAdapter', () => { - 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 env param is missing', function () { - const invalidRequest = deepClone(defaultRequest); - delete invalidRequest.params.env; - 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('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('gdprApplies').and.to.equal(0); - expect(request).to.have.property('consentString').and.to.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('coppa').and.to.equal(0); - 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({ - env: 'lm_kiviads', - pid: '40' - }); - 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); - schainRequest.schain = { - validation: 'strict', - config: { - ver: '1.0' - } - }; - const request = JSON.parse(spec.buildRequests([schainRequest], {}).data)[0]; - expect(request).to.have.property('schain').and.to.deep.equal({ - validation: 'strict', - config: { - 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 gdpr consent data if applies', function () { - const bidderRequest = { - gdprConsent: { - gdprApplies: true, - consentString: 'qwerty' - } - }; - const request = JSON.parse(spec.buildRequests([defaultRequest], bidderRequest).data)[0]; - expect(request).to.have.property('gdprApplies').and.equals(1); - expect(request).to.have.property('consentString').and.equals('qwerty'); - }); - - 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 coppa 1', function () { - config.setConfig({ - coppa: true - }); - const request = JSON.parse(spec.buildRequests([defaultRequest], {}).data)[0]; - expect(request).to.have.property('coppa').and.equals(1); - }); - - 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', - skipppable: 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: ['lm_kiviads'] - }, - ext: { - pixels: [ - ['iframe', 'surl1'], - ['image', 'surl2'], - ] - } - }] - } - }; - - 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'); - 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: ['lm_kiviads']}); - }); - - 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: 'xe-demo-banner', - ad: 'ad', - meta: {} - }] - } - }; - - 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'); - expect(bid.creativeId).to.equal('xe-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: 'xe-demo-video', - ad: 'vast-xml', - meta: {} - }] - } - }; - - 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'); - expect(bid.creativeId).to.equal('xe-demo-video'); - expect(bid.ad).to.equal('vast-xml'); - }); - }); - - describe('getUserSyncs', function () { - it('shoukd 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/magniteAnalyticsAdapter_spec.js b/test/spec/modules/magniteAnalyticsAdapter_spec.js index 0864a976d7d..ae63f19f46b 100644 --- a/test/spec/modules/magniteAnalyticsAdapter_spec.js +++ b/test/spec/modules/magniteAnalyticsAdapter_spec.js @@ -550,7 +550,7 @@ describe('magnite analytics adapter', function () { expect(server.requests.length).to.equal(1); let request = server.requests[0]; - expect(request.url).to.match(/\/\/localhost:9999\/event/); + expect(request.url).to.equal('//localhost:9999/event'); let message = JSON.parse(request.requestBody); @@ -724,7 +724,7 @@ describe('magnite analytics adapter', function () { expect(server.requests.length).to.equal(1); let request = server.requests[0]; - expect(request.url).to.match(/\/\/localhost:9999\/event/); + expect(request.url).to.equal('//localhost:9999/event'); let message = JSON.parse(request.requestBody); @@ -1492,40 +1492,6 @@ describe('magnite analytics adapter', function () { expect(message).to.deep.equal(expectedMessage); }); - describe('when eventDispatcher is present', () => { - beforeEach(() => { - window.pbjs = window.pbjs || {}; - pbjs.rp = pbjs.rp || {}; - pbjs.rp.eventDispatcher = pbjs.rp.eventDispatcher || document.createElement('fakeElem'); - }); - - afterEach(() => { - delete pbjs.rp.eventDispatcher; - delete pbjs.rp; - }); - - it('should dispatch beforeSendingMagniteAnalytics if possible', () => { - pbjs.rp.eventDispatcher.addEventListener('beforeSendingMagniteAnalytics', (data) => { - data.detail.test = 'testData'; - }); - - performStandardAuction(); - - expect(server.requests.length).to.equal(1); - let request = server.requests[0]; - - expect(request.url).to.equal('http://localhost:9999/event'); - - let message = JSON.parse(request.requestBody); - - const AnalyticsMessageWithCustomData = { - ...ANALYTICS_MESSAGE, - test: 'testData' - } - expect(message).to.deep.equal(AnalyticsMessageWithCustomData); - }); - }) - describe('when handling bid caching', () => { let auctionInits, bidRequests, bidResponses, bidsWon; beforeEach(function () { diff --git a/test/spec/modules/mediabramaBidAdapter_spec.js b/test/spec/modules/mediabramaBidAdapter_spec.js deleted file mode 100644 index d7341e02f17..00000000000 --- a/test/spec/modules/mediabramaBidAdapter_spec.js +++ /dev/null @@ -1,256 +0,0 @@ -import {expect} from 'chai'; -import {spec} from '../../../modules/mediabramaBidAdapter.js'; -import { BANNER } from '../../../src/mediaTypes.js'; -import * as utils from '../../../src/utils.js'; - -describe('MediaBramaBidAdapter', function () { - const bid = { - bidId: '23dc19818e5293', - bidder: 'mediabrama', - mediaTypes: { - [BANNER]: { - sizes: [[300, 250]] - } - }, - params: { - placementId: 24428, - } - }; - - const bidderRequest = { - refererInfo: { - referer: 'test.com' - } - }; - - describe('isBidRequestValid', function () { - it('Should return true if there are bidId, params and key parameters present', function () { - expect(spec.isBidRequestValid(bid)).to.be.true; - }); - it('Should return false if at least one of parameters is not present', function () { - delete bid.params.placementId; - expect(spec.isBidRequestValid(bid)).to.be.false; - }); - }); - - describe('buildRequests', function () { - let serverRequest = spec.buildRequests([bid], 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://prebid.mediabrama.com/pbjs'); - }); - it('Returns valid data if array of bids is valid', function () { - let data = serverRequest.data; - expect(data).to.be.an('object'); - expect(data).to.have.all.keys('deviceWidth', 'deviceHeight', 'language', 'host', 'page', 'placements'); - expect(data.deviceWidth).to.be.a('number'); - expect(data.deviceHeight).to.be.a('number'); - expect(data.language).to.be.a('string'); - expect(data.host).to.be.a('string'); - expect(data.page).to.be.a('string'); - expect(data.gdpr).to.not.exist; - expect(data.ccpa).to.not.exist; - let placement = data['placements'][0]; - expect(placement).to.have.keys('placementId', 'bidId', 'adFormat', 'sizes', 'schain', 'bidfloor'); - expect(placement.placementId).to.equal(24428); - expect(placement.bidId).to.equal('23dc19818e5293'); - expect(placement.adFormat).to.equal(BANNER); - expect(placement.schain).to.be.an('object'); - expect(placement.sizes).to.be.an('array'); - expect(placement.bidfloor).to.exist.and.to.equal(0); - }); - - it('Returns data with gdprConsent and without uspConsent', function () { - bidderRequest.gdprConsent = 'test'; - serverRequest = spec.buildRequests([bid], bidderRequest); - let data = serverRequest.data; - expect(data.gdpr).to.exist; - expect(data.gdpr).to.be.a('string'); - expect(data.gdpr).to.equal(bidderRequest.gdprConsent); - expect(data.ccpa).to.not.exist; - delete bidderRequest.gdprConsent; - }); - - it('Returns data with uspConsent and without gdprConsent', function () { - bidderRequest.uspConsent = 'test'; - serverRequest = spec.buildRequests([bid], bidderRequest); - let 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; - }); - - it('Returns empty data if no valid requests are passed', function () { - serverRequest = spec.buildRequests([]); - let data = serverRequest.data; - expect(data.placements).to.be.an('array').that.is.empty; - }); - }); - describe('interpretResponse', function () { - it('Should interpret banner response', function () { - const banner = { - body: [{ - mediaType: 'banner', - width: 300, - height: 250, - cpm: 0.4, - ad: 'Test', - requestId: '23dc19818e5293', - ttl: 120, - creativeId: '2', - netRevenue: true, - currency: 'USD', - dealId: '1', - meta: {} - }] - }; - let bannerResponses = spec.interpretResponse(banner); - expect(bannerResponses).to.be.an('array').that.is.not.empty; - let dataItem = bannerResponses[0]; - expect(dataItem).to.have.all.keys('requestId', 'cpm', 'width', 'height', 'ad', 'ttl', 'creativeId', - 'netRevenue', 'currency', 'dealId', 'mediaType', 'meta'); - expect(dataItem.requestId).to.equal('23dc19818e5293'); - expect(dataItem.cpm).to.equal(0.4); - expect(dataItem.width).to.equal(300); - expect(dataItem.height).to.equal(250); - expect(dataItem.ad).to.equal('Test'); - expect(dataItem.ttl).to.equal(120); - expect(dataItem.creativeId).to.equal('2'); - expect(dataItem.netRevenue).to.be.true; - expect(dataItem.currency).to.equal('USD'); - 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: '23dc19818e5293', - ttl: 120, - creativeId: '2', - netRevenue: true, - currency: 'USD', - dealId: '1' - }] - }; - - let serverResponses = spec.interpretResponse(invBanner); - expect(serverResponses).to.be.an('array').that.is.empty; - }); - it('Should return an empty array if invalid response is passed', function () { - const invalid = { - body: [{ - ttl: 120, - creativeId: '2', - netRevenue: true, - currency: 'USD', - dealId: '1' - }] - }; - let serverResponses = spec.interpretResponse(invalid); - expect(serverResponses).to.be.an('array').that.is.empty; - }); - }); - - describe('getUserSyncs', function () { - it('should do nothing on getUserSyncs', function () { - const syncData = spec.getUserSyncs({}, {}, { - consentString: 'ALL', - gdprApplies: true - }, {}); - 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://prebid.mediabrama.com/sync/image?pbjs=1&gdpr=1&gdpr_consent=ALL&coppa=0') - }); - }); - - describe('on bidWon', function () { - beforeEach(function() { - sinon.stub(utils, 'triggerPixel'); - }); - afterEach(function() { - utils.triggerPixel.restore(); - }); - it('should replace nurl for banner', function () { - const nurl = 'nurl/?ap=${' + 'AUCTION_PRICE}'; - const bid = { - 'bidderCode': 'mediabrama', - 'width': 300, - 'height': 250, - 'statusMessage': 'Bid available', - 'adId': '5691dd18ba6ab6', - 'requestId': '23dc19818e5293', - 'transactionId': '948c716b-bf64-4303-bcf4-395c2f6a9770', - 'auctionId': 'a6b7c61f-15a9-481b-8f64-e859787e9c07', - 'mediaType': 'banner', - 'source': 'client', - 'ad': "
\n", - 'cpm': 0.61, - 'nurl': nurl, - 'creativeId': 'test', - 'currency': 'USD', - 'dealId': '', - 'meta': { - 'advertiserDomains': [], - 'dchain': { - 'ver': '1.0', - 'complete': 0, - 'nodes': [ - { - 'name': 'mediabrama' - } - ] - } - }, - 'netRevenue': true, - 'ttl': 185, - 'metrics': {}, - 'adapterCode': 'mediabrama', - 'originalCpm': 0.61, - 'originalCurrency': 'USD', - 'responseTimestamp': 1668162732297, - 'requestTimestamp': 1668162732292, - 'bidder': 'mediabrama', - 'adUnitCode': 'div-prebid', - 'timeToRespond': 5, - 'pbLg': '0.50', - 'pbMg': '0.60', - 'pbHg': '0.61', - 'pbAg': '0.61', - 'pbDg': '0.61', - 'pbCg': '', - 'size': '300x250', - 'adserverTargeting': { - 'hb_bidder': 'mediabrama', - 'hb_adid': '5691dd18ba6ab6', - 'hb_pb': '0.61', - 'hb_size': '300x250', - 'hb_source': 'client', - 'hb_format': 'banner', - 'hb_adomain': '' - }, - 'status': 'rendered', - 'params': [ - { - 'placementId': 24428 - } - ] - }; - spec.onBidWon(bid); - expect(bid.nurl).to.deep.equal('nurl/?ap=0.61'); - }); - }); -}); diff --git a/test/spec/modules/mediafilterRtdProvider_spec.js b/test/spec/modules/mediafilterRtdProvider_spec.js deleted file mode 100644 index 3395c7be691..00000000000 --- a/test/spec/modules/mediafilterRtdProvider_spec.js +++ /dev/null @@ -1,147 +0,0 @@ -import * as utils from '../../../src/utils.js'; -import * as hook from '../../../src/hook.js' -import * as events from '../../../src/events.js'; -import CONSTANTS from '../../../src/constants.json'; - -import { - MediaFilter, - MEDIAFILTER_EVENT_TYPE, - MEDIAFILTER_BASE_URL -} from '../../../modules/mediafilterRtdProvider.js'; - -describe('The Media Filter RTD module', function () { - describe('register()', function() { - let submoduleSpy, generateInitHandlerSpy; - - beforeEach(function () { - submoduleSpy = sinon.spy(hook, 'submodule'); - generateInitHandlerSpy = sinon.spy(MediaFilter, 'generateInitHandler'); - }); - - afterEach(function () { - submoduleSpy.restore(); - generateInitHandlerSpy.restore(); - }); - - it('should register and call the submodule function(s)', function () { - MediaFilter.register(); - - expect(submoduleSpy.calledOnceWithExactly('realTimeData', sinon.match.object)).to.be.true; - expect(submoduleSpy.called).to.be.true; - expect(generateInitHandlerSpy.called).to.be.true; - }); - }); - - describe('setup()', function() { - let setupEventListenerSpy, setupScriptSpy; - - beforeEach(function() { - setupEventListenerSpy = sinon.spy(MediaFilter, 'setupEventListener'); - setupScriptSpy = sinon.spy(MediaFilter, 'setupScript'); - }); - - afterEach(function() { - setupEventListenerSpy.restore(); - setupScriptSpy.restore(); - }); - - it('should call setupEventListener and setupScript function(s)', function() { - MediaFilter.setup({ configurationHash: 'abc123' }); - - expect(setupEventListenerSpy.called).to.be.true; - expect(setupScriptSpy.called).to.be.true; - }); - }); - - describe('setupEventListener()', function() { - let setupEventListenerSpy, addEventListenerSpy; - - beforeEach(function() { - setupEventListenerSpy = sinon.spy(MediaFilter, 'setupEventListener'); - addEventListenerSpy = sinon.spy(window, 'addEventListener'); - }); - - afterEach(function() { - setupEventListenerSpy.restore(); - addEventListenerSpy.restore(); - }); - - it('should call addEventListener function(s)', function() { - MediaFilter.setupEventListener(); - expect(addEventListenerSpy.called).to.be.true; - expect(addEventListenerSpy.calledWith('message', sinon.match.func)).to.be.true; - }); - }); - - describe('generateInitHandler()', function() { - let generateInitHandlerSpy, setupMock, logErrorSpy; - - beforeEach(function() { - generateInitHandlerSpy = sinon.spy(MediaFilter, 'generateInitHandler'); - setupMock = sinon.stub(MediaFilter, 'setup').throws(new Error('Mocked error!')); - logErrorSpy = sinon.spy(utils, 'logError'); - }); - - afterEach(function() { - generateInitHandlerSpy.restore(); - setupMock.restore(); - logErrorSpy.restore(); - }); - - it('should handle errors in the catch block when setup throws an error', function() { - const initHandler = MediaFilter.generateInitHandler(); - initHandler({}); - - expect(logErrorSpy.calledWith('Error in initialization: Mocked error!')).to.be.true; - }); - }); - - describe('generateEventHandler()', function() { - let generateEventHandlerSpy, eventsEmitSpy; - - beforeEach(function() { - generateEventHandlerSpy = sinon.spy(MediaFilter, 'generateEventHandler'); - eventsEmitSpy = sinon.spy(events, 'emit'); - }); - - afterEach(function() { - generateEventHandlerSpy.restore(); - eventsEmitSpy.restore(); - }); - - it('should emit a billable event when the event type matches', function() { - const configurationHash = 'abc123'; - const eventHandler = MediaFilter.generateEventHandler(configurationHash); - - const mockEvent = { - data: { - type: MEDIAFILTER_EVENT_TYPE.concat('.', configurationHash) - } - }; - - eventHandler(mockEvent); - - expect(eventsEmitSpy.calledWith(CONSTANTS.EVENTS.BILLABLE_EVENT, { - 'billingId': sinon.match.string, - 'configurationHash': configurationHash, - 'type': 'impression', - 'vendor': 'mediafilter', - })).to.be.true; - }); - - it('should not emit a billable event when the event type does not match', function() { - const configurationHash = 'abc123'; - const eventHandler = MediaFilter.generateEventHandler(configurationHash); - - const mockEvent = { - data: { - type: 'differentEventType' - } - }; - - eventHandler(mockEvent); - - expect(eventsEmitSpy.called).to.be.false; - }); - }); -}); diff --git a/test/spec/modules/mediagoBidAdapter_spec.js b/test/spec/modules/mediagoBidAdapter_spec.js index 6e58217b3d3..e77af544429 100644 --- a/test/spec/modules/mediagoBidAdapter_spec.js +++ b/test/spec/modules/mediagoBidAdapter_spec.js @@ -1,17 +1,5 @@ import { expect } from 'chai'; -import { - spec, - getPmgUID, - storage, - getPageTitle, - getPageDescription, - getPageKeywords, - getConnectionDownLink, - THIRD_PARTY_COOKIE_ORIGIN, - COOKIE_KEY_MGUID, - getCurrentTimeToUTCString -} from 'modules/mediagoBidAdapter.js'; -import * as utils from 'src/utils.js'; +import { spec } from 'modules/mediagoBidAdapter.js'; describe('mediago:BidAdapterTests', function () { let bidRequestData = { @@ -23,49 +11,12 @@ describe('mediago:BidAdapterTests', function () { bidder: 'mediago', params: { token: '85a6b01e41ac36d49744fad726e3655d', - siteId: 'siteId_01', - zoneId: 'zoneId_01', - publisher: '52', - position: 'left', - referrer: 'https://trace.mediago.io', bidfloor: 0.01, - ortb2Imp: { - ext: { - gpid: 'adslot_gpid', - tid: 'tid_01', - data: { - browsi: { - browsiViewability: 'NA' - }, - adserver: { - name: 'adserver_name', - adslot: 'adslot_name' - }, - pbadslot: '/12345/my-gpt-tag-0' - } - } - } }, mediaTypes: { banner: { sizes: [[300, 250]], - pos: 'left' - } - }, - ortb2: { - site: { - cat: ['IAB2'], - keywords: 'power tools, drills, tools=industrial', - content: { - keywords: 'video, source=streaming' - }, - }, - user: { - ext: { - data: {} - } - } }, adUnitCode: 'regular_iframe', transactionId: '7b26fdae-96e6-4c35-a18b-218dda11397d', @@ -76,83 +27,9 @@ describe('mediago:BidAdapterTests', function () { src: 'client', bidRequestsCount: 1, bidderRequestsCount: 1, - bidderWinsCount: 0 - } - ], - gdprConsent: { - consentString: 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A==', - gdprApplies: true, - apiVersion: 2, - vendorData: { - purpose: { - consents: { - 1: false - } - } - } - }, - userId: { - tdid: 'sample-userid', - uid2: { id: 'sample-uid2-value' }, - criteoId: 'sample-criteo-userid', - netId: 'sample-netId-userid', - idl_env: 'sample-idl-userid', - pubProvidedId: [ - { - source: 'puburl.com', - uids: [ - { - id: 'pubid2', - atype: 1, - ext: { - stype: 'ppuid' - } - } - ] - }, - { - source: 'puburl2.com', - uids: [ - { - id: 'pubid2' - }, - { - id: 'pubid2-123' - } - ] - } - ] - }, - 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' }] + bidderWinsCount: 0, }, - { - source: 'uidapi.com', - uids: [{ id: 'sample-uid2-value' }] - }, - { - source: 'puburl.com', - uids: [{ id: 'pubid1' }] - }, - { - source: 'puburl2.com', - uids: [{ id: 'pubid2' }, { id: 'pubid2-123' }] - } - ] + ], }; let request = []; @@ -161,8 +38,8 @@ describe('mediago:BidAdapterTests', function () { spec.isBidRequestValid({ bidder: 'mediago', params: { - token: ['85a6b01e41ac36d49744fad726e3655d'] - } + token: ['85a6b01e41ac36d49744fad726e3655d'], + }, }) ).to.equal(true); }); @@ -173,54 +50,11 @@ describe('mediago:BidAdapterTests', function () { expect(req_data.imp).to.have.lengthOf(1); }); - describe('mediago: buildRequests', function() { - describe('getPmgUID function', function() { - let sandbox; - - beforeEach(() => { - sandbox = sinon.sandbox.create(); - sandbox.stub(storage, 'getCookie'); - sandbox.stub(storage, 'setCookie'); - sandbox.stub(utils, 'generateUUID').returns('new-uuid'); - sandbox.stub(storage, 'cookiesAreEnabled'); - }) - - afterEach(() => { - sandbox.restore(); - }); - - it('should generate new UUID and set cookie if not exists', () => { - storage.cookiesAreEnabled.callsFake(() => true); - storage.getCookie.callsFake(() => null); - const uid = getPmgUID(); - expect(uid).to.equal('new-uuid'); - expect(storage.setCookie.calledOnce).to.be.true; - }); - - it('should return existing UUID from cookie', () => { - storage.cookiesAreEnabled.callsFake(() => true); - storage.getCookie.callsFake(() => 'existing-uuid'); - const uid = getPmgUID(); - expect(uid).to.equal('existing-uuid'); - expect(storage.setCookie.called).to.be.false; - }); - - it('should not set new UUID when cookies are not enabled', () => { - storage.cookiesAreEnabled.callsFake(() => false); - storage.getCookie.callsFake(() => null); - getPmgUID(); - expect(storage.setCookie.calledOnce).to.be.false; - }); - }) - }); - it('mediago:validate_response_params', function () { - let adm = - ''; + let adm = ""; let temp = '%3Cscr'; temp += 'ipt%3E'; - temp += - '!function()%7B%22use%20strict%22%3Bfunction%20f(t)%7Breturn(f%3D%22function%22%3D%3Dtypeof%20Symbol%26%26%22symbol%22%3D%3Dtypeof%20Symbol.iterator%3Ffunction(t)%7Breturn%20typeof%20t%7D%3Afunction(t)%7Breturn%20t%26%26%22function%22%3D%3Dtypeof%20Symbol%26%26t.constructor%3D%3D%3DSymbol%26%26t!%3D%3DSymbol.prototype%3F%22symbol%22%3Atypeof%20t%7D)(t)%7Dfunction%20l(t)%7Bvar%20e%3D0%3Carguments.length%26%26void%200!%3D%3Dt%3Ft%3A%7B%7D%3Btry%7Be.random_t%3D(new%20Date).getTime()%2Cg(function(t)%7Bvar%20e%3D1%3Carguments.length%26%26void%200!%3D%3Darguments%5B1%5D%3Farguments%5B1%5D%3A%22%22%3Bif(%22object%22!%3D%3Df(t))return%20e%3Bvar%20n%3Dfunction(t)%7Bfor(var%20e%2Cn%3D%5B%5D%2Co%3D0%2Ci%3DObject.keys(t)%3Bo%3Ci.length%3Bo%2B%2B)e%3Di%5Bo%5D%2Cn.push(%22%22.concat(e%2C%22%3D%22).concat(t%5Be%5D))%3Breturn%20n%7D(t).join(%22%26%22)%2Co%3De.indexOf(%22%23%22)%2Ci%3De%2Ct%3D%22%22%3Breturn-1!%3D%3Do%26%26(i%3De.slice(0%2Co)%2Ct%3De.slice(o))%2Cn%26%26(i%26%26-1!%3D%3Di.indexOf(%22%3F%22)%3Fi%2B%3D%22%26%22%2Bn%3Ai%2B%3D%22%3F%22%2Bn)%2Ci%2Bt%7D(e%2C%22https%3A%2F%2Ftrace.mediago.io%2Fapi%2Flog%2Ftrack%22))%7Dcatch(t)%7B%7D%7Dfunction%20g(t%2Ce%2Cn)%7B(t%3Dt%3Ft.split(%22%3B%3B%3B%22)%3A%5B%5D).map(function(t)%7Btry%7B0%3C%3Dt.indexOf(%22%2Fapi%2Fbidder%2Ftrack%22)%26%26n%26%26(t%2B%3D%22%26inIframe%3D%22.concat(!(!self.frameElement%7C%7C%22IFRAME%22!%3Dself.frameElement.tagName)%7C%7Cwindow.frames.length!%3Dparent.frames.length%7C%7Cself!%3Dtop)%2Ct%2B%3D%22%26pos_x%3D%22.concat(n.left%2C%22%26pos_y%3D%22).concat(n.top%2C%22%26page_w%3D%22).concat(n.page_width%2C%22%26page_h%3D%22).concat(n.page_height))%7Dcatch(t)%7Bl(%7Btn%3As%2Cwinloss%3A1%2Cfe%3A2%2Cpos_err_c%3A1002%2Cpos_err_m%3At.toString()%7D)%7Dvar%20e%3Dnew%20Image%3Be.src%3Dt%2Ce.style.display%3D%22none%22%2Ce.style.visibility%3D%22hidden%22%2Ce.width%3D0%2Ce.height%3D0%2Cdocument.body.appendChild(e)%7D)%7Dvar%20d%3D%5B%22https%3A%2F%2Ftrace.mediago.io%2Fapi%2Fbidder%2Ftrack%3Ftn%3D39934c2bda4debbe4c680be1dd02f5d3%26price%3DdjUJcggeuWWfbm28q4WXHdgMFkO28DrGw49FnubQ0Bk%26evt%3D101%26rid%3D6e28cfaf115a354ea1ad8e1304d6d7b8%26campaignid%3D1339145%26impid%3D44-300x250-1%26offerid%3D24054386%26test%3D0%26time%3D1660789795%26cp%3DjZDh1xu6_QqJLlKVtCkiHIP_TER6gL9jeTrlHCBoxOM%26acid%3D599%26trackingid%3D99afea272c2b0e8626489674ddb7a0bb%26uid%3Da865b9ae-fa9e-4c09-8204-2db99ac7c8f7%26bm%3D2%26la%3Den%26cn%3Dus%26cid%3D3998296%26info%3DSi3oM-qfCbw2iZRYs01BkUWyH6c5CQWHrA8CQLE0VHcXAcf4ljY9dyLzQ4vAlTWd6-j_ou4ySor3e70Ll7wlKiiauQKaUkZqNoTizHm73C4FK8DYJSTP3VkhJV8RzrYk%26sid%3D128__110__1__12__28__38__163__96__58__24__47__99%26sp%3DdjUJcggeuWWfbm28q4WXHdgMFkO28DrGw49FnubQ0Bk%26scp%3DzK0DRYY1UV-syqSpmcMYBpOebtoQJV9ZEJT0JFqbTQg%26acu%3DUSD%26scu%3DUSD%26sgcp%3DzK0DRYY1UV-syqSpmcMYBpOebtoQJV9ZEJT0JFqbTQg%26gprice%3DdjUJcggeuWWfbm28q4WXHdgMFkO28DrGw49FnubQ0Bk%26gcp%3DzK0DRYY1UV-syqSpmcMYBpOebtoQJV9ZEJT0JFqbTQg%26ah%3D%26de%3Dwjh.popin.cc%26iv%3D0%22%2C%22%24%7BITRACKER2%7D%22%2C%22%24%7BITRACKER3%7D%22%2C%22%24%7BITRACKER4%7D%22%2C%22%24%7BITRACKER5%7D%22%2C%22%24%7BITRACKER6%7D%22%5D%2Cp%3D%5B%22https%3A%2F%2Ftrace.mediago.io%2Fapi%2Fbidder%2Ftrack%3Ftn%3D39934c2bda4debbe4c680be1dd02f5d3%26price%3DdjUJcggeuWWfbm28q4WXHdgMFkO28DrGw49FnubQ0Bk%26evt%3D104%26rid%3D6e28cfaf115a354ea1ad8e1304d6d7b8%26campaignid%3D1339145%26impid%3D44-300x250-1%26offerid%3D24054386%26test%3D0%26time%3D1660789795%26cp%3DjZDh1xu6_QqJLlKVtCkiHIP_TER6gL9jeTrlHCBoxOM%26acid%3D599%26trackingid%3D99afea272c2b0e8626489674ddb7a0bb%26uid%3Da865b9ae-fa9e-4c09-8204-2db99ac7c8f7%26sid%3D128__110__1__12__28__38__163__96__58__24__47__99%26format%3D%26crid%3Dff32b6f9b3bbc45c00b78b6674a2952e%26bm%3D2%26la%3Den%26cn%3Dus%26cid%3D3998296%26info%3DSi3oM-qfCbw2iZRYs01BkUWyH6c5CQWHrA8CQLE0VHcXAcf4ljY9dyLzQ4vAlTWd6-j_ou4ySor3e70Ll7wlKiiauQKaUkZqNoTizHm73C4FK8DYJSTP3VkhJV8RzrYk%26sp%3DdjUJcggeuWWfbm28q4WXHdgMFkO28DrGw49FnubQ0Bk%26scp%3DzK0DRYY1UV-syqSpmcMYBpOebtoQJV9ZEJT0JFqbTQg%26acu%3DUSD%26scu%3DUSD%26sgcp%3DzK0DRYY1UV-syqSpmcMYBpOebtoQJV9ZEJT0JFqbTQg%26gprice%3DdjUJcggeuWWfbm28q4WXHdgMFkO28DrGw49FnubQ0Bk%26gcp%3DzK0DRYY1UV-syqSpmcMYBpOebtoQJV9ZEJT0JFqbTQg%26ah%3D%26de%3Dwjh.popin.cc%26iv%3D0%22%2C%22%24%7BVTRACKER2%7D%22%2C%22%24%7BVTRACKER3%7D%22%2C%22%24%7BVTRACKER4%7D%22%2C%22%24%7BVTRACKER5%7D%22%2C%22%24%7BVTRACKER6%7D%22%5D%2Cs%3D%22f9f2b1ef23fe2759c2cad0953029a94b%22%2Cn%3Ddocument.getElementById(%22mgcontainer-99afea272c2b0e8626489674ddb7a0bb%22)%3Bn%26%26function()%7Bvar%20a%3Dn.getElementsByClassName(%22mediago-placement-track%22)%3Bif(a%26%26a.length)%7Bvar%20t%2Ce%3Dfunction(t)%7Bvar%20e%2Cn%2Co%2Ci%2Cc%2Cr%3B%22object%22%3D%3D%3Df(r%3Da%5Bt%5D)%26%26(e%3Dfunction(t)%7Btry%7Bvar%20e%3Dt.getBoundingClientRect()%2Cn%3De%26%26e.top%7C%7C-1%2Co%3De%26%26e.left%7C%7C-1%2Ci%3Ddocument.body.scrollWidth%7C%7C-1%2Ce%3Ddocument.body.scrollHeight%7C%7C-1%3Breturn%7Btop%3An.toFixed(0)%2Cleft%3Ao.toFixed(0)%2Cpage_width%3Ai%2Cpage_height%3Ae%7D%7Dcatch(o)%7Breturn%20l(%7Btn%3As%2Cwinloss%3A1%2Cfe%3A2%2Cpos_err_c%3A1001%2Cpos_err_m%3Ao.toString()%7D)%2C%7Btop%3A%22-1%22%2Cleft%3A%22-1%22%2Cpage_width%3A%22-1%22%2Cpage_height%3A%22-1%22%7D%7D%7D(r)%2C(n%3Dd%5Bt%5D)%26%26g(n%2C0%2Ce)%2Co%3Dp%5Bt%5D%2Ci%3D!1%2C(c%3Dfunction()%7BsetTimeout(function()%7Bvar%20t%2Ce%3B!i%26%26(t%3Dr%2Ce%3Dwindow.innerHeight%7C%7Cdocument.documentElement.clientHeight%7C%7Cdocument.body.clientHeight%2C(t.getBoundingClientRect()%26%26t.getBoundingClientRect().top)%3C%3De-.75*(t.offsetHeight%7C%7Ct.clientHeight))%3F(i%3D!0%2Co%26%26g(o))%3Ac()%7D%2C500)%7D)())%7D%3Bfor(t%20in%20a)e(t)%7D%7D()%7D()'; + temp += '!function()%7B%22use%20strict%22%3Bfunction%20f(t)%7Breturn(f%3D%22function%22%3D%3Dtypeof%20Symbol%26%26%22symbol%22%3D%3Dtypeof%20Symbol.iterator%3Ffunction(t)%7Breturn%20typeof%20t%7D%3Afunction(t)%7Breturn%20t%26%26%22function%22%3D%3Dtypeof%20Symbol%26%26t.constructor%3D%3D%3DSymbol%26%26t!%3D%3DSymbol.prototype%3F%22symbol%22%3Atypeof%20t%7D)(t)%7Dfunction%20l(t)%7Bvar%20e%3D0%3Carguments.length%26%26void%200!%3D%3Dt%3Ft%3A%7B%7D%3Btry%7Be.random_t%3D(new%20Date).getTime()%2Cg(function(t)%7Bvar%20e%3D1%3Carguments.length%26%26void%200!%3D%3Darguments%5B1%5D%3Farguments%5B1%5D%3A%22%22%3Bif(%22object%22!%3D%3Df(t))return%20e%3Bvar%20n%3Dfunction(t)%7Bfor(var%20e%2Cn%3D%5B%5D%2Co%3D0%2Ci%3DObject.keys(t)%3Bo%3Ci.length%3Bo%2B%2B)e%3Di%5Bo%5D%2Cn.push(%22%22.concat(e%2C%22%3D%22).concat(t%5Be%5D))%3Breturn%20n%7D(t).join(%22%26%22)%2Co%3De.indexOf(%22%23%22)%2Ci%3De%2Ct%3D%22%22%3Breturn-1!%3D%3Do%26%26(i%3De.slice(0%2Co)%2Ct%3De.slice(o))%2Cn%26%26(i%26%26-1!%3D%3Di.indexOf(%22%3F%22)%3Fi%2B%3D%22%26%22%2Bn%3Ai%2B%3D%22%3F%22%2Bn)%2Ci%2Bt%7D(e%2C%22https%3A%2F%2Ftrace.mediago.io%2Fapi%2Flog%2Ftrack%22))%7Dcatch(t)%7B%7D%7Dfunction%20g(t%2Ce%2Cn)%7B(t%3Dt%3Ft.split(%22%3B%3B%3B%22)%3A%5B%5D).map(function(t)%7Btry%7B0%3C%3Dt.indexOf(%22%2Fapi%2Fbidder%2Ftrack%22)%26%26n%26%26(t%2B%3D%22%26inIframe%3D%22.concat(!(!self.frameElement%7C%7C%22IFRAME%22!%3Dself.frameElement.tagName)%7C%7Cwindow.frames.length!%3Dparent.frames.length%7C%7Cself!%3Dtop)%2Ct%2B%3D%22%26pos_x%3D%22.concat(n.left%2C%22%26pos_y%3D%22).concat(n.top%2C%22%26page_w%3D%22).concat(n.page_width%2C%22%26page_h%3D%22).concat(n.page_height))%7Dcatch(t)%7Bl(%7Btn%3As%2Cwinloss%3A1%2Cfe%3A2%2Cpos_err_c%3A1002%2Cpos_err_m%3At.toString()%7D)%7Dvar%20e%3Dnew%20Image%3Be.src%3Dt%2Ce.style.display%3D%22none%22%2Ce.style.visibility%3D%22hidden%22%2Ce.width%3D0%2Ce.height%3D0%2Cdocument.body.appendChild(e)%7D)%7Dvar%20d%3D%5B%22https%3A%2F%2Ftrace.mediago.io%2Fapi%2Fbidder%2Ftrack%3Ftn%3D39934c2bda4debbe4c680be1dd02f5d3%26price%3DdjUJcggeuWWfbm28q4WXHdgMFkO28DrGw49FnubQ0Bk%26evt%3D101%26rid%3D6e28cfaf115a354ea1ad8e1304d6d7b8%26campaignid%3D1339145%26impid%3D44-300x250-1%26offerid%3D24054386%26test%3D0%26time%3D1660789795%26cp%3DjZDh1xu6_QqJLlKVtCkiHIP_TER6gL9jeTrlHCBoxOM%26acid%3D599%26trackingid%3D99afea272c2b0e8626489674ddb7a0bb%26uid%3Da865b9ae-fa9e-4c09-8204-2db99ac7c8f7%26bm%3D2%26la%3Den%26cn%3Dus%26cid%3D3998296%26info%3DSi3oM-qfCbw2iZRYs01BkUWyH6c5CQWHrA8CQLE0VHcXAcf4ljY9dyLzQ4vAlTWd6-j_ou4ySor3e70Ll7wlKiiauQKaUkZqNoTizHm73C4FK8DYJSTP3VkhJV8RzrYk%26sid%3D128__110__1__12__28__38__163__96__58__24__47__99%26sp%3DdjUJcggeuWWfbm28q4WXHdgMFkO28DrGw49FnubQ0Bk%26scp%3DzK0DRYY1UV-syqSpmcMYBpOebtoQJV9ZEJT0JFqbTQg%26acu%3DUSD%26scu%3DUSD%26sgcp%3DzK0DRYY1UV-syqSpmcMYBpOebtoQJV9ZEJT0JFqbTQg%26gprice%3DdjUJcggeuWWfbm28q4WXHdgMFkO28DrGw49FnubQ0Bk%26gcp%3DzK0DRYY1UV-syqSpmcMYBpOebtoQJV9ZEJT0JFqbTQg%26ah%3D%26de%3Dwjh.popin.cc%26iv%3D0%22%2C%22%24%7BITRACKER2%7D%22%2C%22%24%7BITRACKER3%7D%22%2C%22%24%7BITRACKER4%7D%22%2C%22%24%7BITRACKER5%7D%22%2C%22%24%7BITRACKER6%7D%22%5D%2Cp%3D%5B%22https%3A%2F%2Ftrace.mediago.io%2Fapi%2Fbidder%2Ftrack%3Ftn%3D39934c2bda4debbe4c680be1dd02f5d3%26price%3DdjUJcggeuWWfbm28q4WXHdgMFkO28DrGw49FnubQ0Bk%26evt%3D104%26rid%3D6e28cfaf115a354ea1ad8e1304d6d7b8%26campaignid%3D1339145%26impid%3D44-300x250-1%26offerid%3D24054386%26test%3D0%26time%3D1660789795%26cp%3DjZDh1xu6_QqJLlKVtCkiHIP_TER6gL9jeTrlHCBoxOM%26acid%3D599%26trackingid%3D99afea272c2b0e8626489674ddb7a0bb%26uid%3Da865b9ae-fa9e-4c09-8204-2db99ac7c8f7%26sid%3D128__110__1__12__28__38__163__96__58__24__47__99%26format%3D%26crid%3Dff32b6f9b3bbc45c00b78b6674a2952e%26bm%3D2%26la%3Den%26cn%3Dus%26cid%3D3998296%26info%3DSi3oM-qfCbw2iZRYs01BkUWyH6c5CQWHrA8CQLE0VHcXAcf4ljY9dyLzQ4vAlTWd6-j_ou4ySor3e70Ll7wlKiiauQKaUkZqNoTizHm73C4FK8DYJSTP3VkhJV8RzrYk%26sp%3DdjUJcggeuWWfbm28q4WXHdgMFkO28DrGw49FnubQ0Bk%26scp%3DzK0DRYY1UV-syqSpmcMYBpOebtoQJV9ZEJT0JFqbTQg%26acu%3DUSD%26scu%3DUSD%26sgcp%3DzK0DRYY1UV-syqSpmcMYBpOebtoQJV9ZEJT0JFqbTQg%26gprice%3DdjUJcggeuWWfbm28q4WXHdgMFkO28DrGw49FnubQ0Bk%26gcp%3DzK0DRYY1UV-syqSpmcMYBpOebtoQJV9ZEJT0JFqbTQg%26ah%3D%26de%3Dwjh.popin.cc%26iv%3D0%22%2C%22%24%7BVTRACKER2%7D%22%2C%22%24%7BVTRACKER3%7D%22%2C%22%24%7BVTRACKER4%7D%22%2C%22%24%7BVTRACKER5%7D%22%2C%22%24%7BVTRACKER6%7D%22%5D%2Cs%3D%22f9f2b1ef23fe2759c2cad0953029a94b%22%2Cn%3Ddocument.getElementById(%22mgcontainer-99afea272c2b0e8626489674ddb7a0bb%22)%3Bn%26%26function()%7Bvar%20a%3Dn.getElementsByClassName(%22mediago-placement-track%22)%3Bif(a%26%26a.length)%7Bvar%20t%2Ce%3Dfunction(t)%7Bvar%20e%2Cn%2Co%2Ci%2Cc%2Cr%3B%22object%22%3D%3D%3Df(r%3Da%5Bt%5D)%26%26(e%3Dfunction(t)%7Btry%7Bvar%20e%3Dt.getBoundingClientRect()%2Cn%3De%26%26e.top%7C%7C-1%2Co%3De%26%26e.left%7C%7C-1%2Ci%3Ddocument.body.scrollWidth%7C%7C-1%2Ce%3Ddocument.body.scrollHeight%7C%7C-1%3Breturn%7Btop%3An.toFixed(0)%2Cleft%3Ao.toFixed(0)%2Cpage_width%3Ai%2Cpage_height%3Ae%7D%7Dcatch(o)%7Breturn%20l(%7Btn%3As%2Cwinloss%3A1%2Cfe%3A2%2Cpos_err_c%3A1001%2Cpos_err_m%3Ao.toString()%7D)%2C%7Btop%3A%22-1%22%2Cleft%3A%22-1%22%2Cpage_width%3A%22-1%22%2Cpage_height%3A%22-1%22%7D%7D%7D(r)%2C(n%3Dd%5Bt%5D)%26%26g(n%2C0%2Ce)%2Co%3Dp%5Bt%5D%2Ci%3D!1%2C(c%3Dfunction()%7BsetTimeout(function()%7Bvar%20t%2Ce%3B!i%26%26(t%3Dr%2Ce%3Dwindow.innerHeight%7C%7Cdocument.documentElement.clientHeight%7C%7Cdocument.body.clientHeight%2C(t.getBoundingClientRect()%26%26t.getBoundingClientRect().top)%3C%3De-.75*(t.offsetHeight%7C%7Ct.clientHeight))%3F(i%3D!0%2Co%26%26g(o))%3Ac()%7D%2C500)%7D)())%7D%3Bfor(t%20in%20a)e(t)%7D%7D()%7D()'; temp += '%3B%3C%2Fscri'; temp += 'pt%3E'; adm += decodeURIComponent(temp); @@ -238,13 +72,13 @@ describe('mediago:BidAdapterTests', function () { cid: '1339145', crid: 'ff32b6f9b3bbc45c00b78b6674a2952e', w: 300, - h: 250 - } - ] - } + h: 250, + }, + ], + }, ], - cur: 'USD' - } + cur: 'USD', + }, }; let bids = spec.interpretResponse(serverResponse); @@ -260,324 +94,4 @@ describe('mediago:BidAdapterTests', function () { expect(bid.height).to.equal(250); expect(bid.currency).to.equal('USD'); }); - - describe('mediago: getUserSyncs', function() { - const COOKY_SYNC_IFRAME_URL = 'https://cdn.mediago.io/js/cookieSync.html'; - const IFRAME_ENABLED = { - iframeEnabled: true, - pixelEnabled: false, - }; - const IFRAME_DISABLED = { - iframeEnabled: false, - pixelEnabled: false, - }; - const GDPR_CONSENT = { - consentString: 'gdprConsentString', - gdprApplies: true - }; - const USP_CONSENT = { - consentString: 'uspConsentString' - } - - let syncParamUrl = `dm=${encodeURIComponent(location.origin || `https://${location.host}`)}`; - syncParamUrl += '&gdpr=1&gdpr_consent=gdprConsentString&ccpa_consent=uspConsentString'; - const expectedIframeSyncs = [ - { - type: 'iframe', - url: `${COOKY_SYNC_IFRAME_URL}?${syncParamUrl}` - } - ]; - - it('should return nothing if iframe is disabled', () => { - const userSyncs = spec.getUserSyncs(IFRAME_DISABLED, undefined, GDPR_CONSENT, USP_CONSENT, undefined); - expect(userSyncs).to.be.undefined; - }); - - it('should do userSyncs if iframe is enabled', () => { - const userSyncs = spec.getUserSyncs(IFRAME_ENABLED, undefined, GDPR_CONSENT, USP_CONSENT, undefined); - expect(userSyncs).to.deep.equal(expectedIframeSyncs); - }); - }); -}); - -describe('mediago Bid Adapter Tests', function () { - describe('buildRequests', () => { - describe('getPageTitle function', function() { - let sandbox; - - beforeEach(() => { - sandbox = sinon.createSandbox(); - }); - - afterEach(() => { - sandbox.restore(); - }); - - it('should return the top document title if available', function() { - const fakeTopDocument = { - title: 'Top Document Title', - querySelector: () => ({ content: 'Top Document Title test' }) - }; - const fakeTopWindow = { - document: fakeTopDocument - }; - const result = getPageTitle({ top: fakeTopWindow }); - expect(result).to.equal('Top Document Title'); - }); - - it('should return the content of top og:title meta tag if title is empty', function() { - const ogTitleContent = 'Top OG Title Content'; - const fakeTopWindow = { - document: { - title: '', - querySelector: sandbox.stub().withArgs('meta[property="og:title"]').returns({ content: ogTitleContent }) - } - }; - - const result = getPageTitle({ top: fakeTopWindow }); - expect(result).to.equal(ogTitleContent); - }); - - it('should return the document title if no og:title meta tag is present', function() { - document.title = 'Test Page Title'; - sandbox.stub(document, 'querySelector').withArgs('meta[property="og:title"]').returns(null); - - const result = getPageTitle({ top: undefined }); - expect(result).to.equal('Test Page Title'); - }); - - it('should return the content of og:title meta tag if present', function() { - document.title = ''; - const ogTitleContent = 'Top OG Title Content'; - sandbox.stub(document, 'querySelector').withArgs('meta[property="og:title"]').returns({ content: ogTitleContent }); - const result = getPageTitle({ top: undefined }); - expect(result).to.equal(ogTitleContent); - }); - - it('should return an empty string if no title or og:title meta tag is found', function() { - document.title = ''; - sandbox.stub(document, 'querySelector').withArgs('meta[property="og:title"]').returns(null); - const result = getPageTitle({ top: undefined }); - expect(result).to.equal(''); - }); - - it('should handle exceptions when accessing top.document and fallback to current document', function() { - const fakeWindow = { - get top() { - throw new Error('Access denied'); - } - }; - const ogTitleContent = 'Current OG Title Content'; - document.title = 'Current Document Title'; - sandbox.stub(document, 'querySelector').withArgs('meta[property="og:title"]').returns({ content: ogTitleContent }); - const result = getPageTitle(fakeWindow); - expect(result).to.equal('Current Document Title'); - }); - }); - - describe('getPageDescription function', function() { - let sandbox; - - beforeEach(() => { - sandbox = sinon.createSandbox(); - }); - - afterEach(() => { - sandbox.restore(); - }); - - it('should return the top document description if available', function() { - const descriptionContent = 'Top Document Description'; - const fakeTopDocument = { - querySelector: sandbox.stub().withArgs('meta[name="description"]').returns({ content: descriptionContent }) - }; - const fakeTopWindow = { document: fakeTopDocument }; - const result = getPageDescription({ top: fakeTopWindow }); - expect(result).to.equal(descriptionContent); - }); - - it('should return the top document og:description if description is not present', function() { - const ogDescriptionContent = 'Top OG Description'; - const fakeTopDocument = { - querySelector: sandbox.stub().withArgs('meta[property="og:description"]').returns({ content: ogDescriptionContent }) - }; - const fakeTopWindow = { document: fakeTopDocument }; - const result = getPageDescription({ top: fakeTopWindow }); - expect(result).to.equal(ogDescriptionContent); - }); - - it('should return the current document description if top document is not accessible', function() { - const descriptionContent = 'Current Document Description'; - sandbox.stub(document, 'querySelector') - .withArgs('meta[name="description"]').returns({ content: descriptionContent }) - const fakeWindow = { - get top() { - throw new Error('Access denied'); - } - }; - const result = getPageDescription(fakeWindow); - expect(result).to.equal(descriptionContent); - }); - - it('should return the current document og:description if description is not present and top document is not accessible', function() { - const ogDescriptionContent = 'Current OG Description'; - sandbox.stub(document, 'querySelector') - .withArgs('meta[property="og:description"]').returns({ content: ogDescriptionContent }); - - const fakeWindow = { - get top() { - throw new Error('Access denied'); - } - }; - const result = getPageDescription(fakeWindow); - expect(result).to.equal(ogDescriptionContent); - }); - }); - - describe('getPageKeywords function', function() { - let sandbox; - - beforeEach(() => { - sandbox = sinon.createSandbox(); - }); - - afterEach(() => { - sandbox.restore(); - }); - - it('should return the top document keywords if available', function() { - const keywordsContent = 'keyword1, keyword2, keyword3'; - const fakeTopDocument = { - querySelector: sandbox.stub() - .withArgs('meta[name="keywords"]').returns({ content: keywordsContent }) - }; - const fakeTopWindow = { document: fakeTopDocument }; - - const result = getPageKeywords({ top: fakeTopWindow }); - expect(result).to.equal(keywordsContent); - }); - - it('should return the current document keywords if top document is not accessible', function() { - const keywordsContent = 'keyword1, keyword2, keyword3'; - sandbox.stub(document, 'querySelector') - .withArgs('meta[name="keywords"]').returns({ content: keywordsContent }); - - // æ¨Ąæ‹ŸéĄļåą‚įĒ—åŖčŽŋ问åŧ‚常 - const fakeWindow = { - get top() { - throw new Error('Access denied'); - } - }; - - const result = getPageKeywords(fakeWindow); - expect(result).to.equal(keywordsContent); - }); - - it('should return an empty string if no keywords meta tag is found', function() { - sandbox.stub(document, 'querySelector').withArgs('meta[name="keywords"]').returns(null); - - const result = getPageKeywords(); - expect(result).to.equal(''); - }); - }); - describe('getConnectionDownLink function', function() { - let sandbox; - - beforeEach(() => { - sandbox = sinon.createSandbox(); - }); - - afterEach(() => { - sandbox.restore(); - }); - - it('should return the downlink value as a string if available', function() { - const downlinkValue = 2.5; - const fakeNavigator = { - connection: { - downlink: downlinkValue - } - }; - - const result = getConnectionDownLink({ navigator: fakeNavigator }); - expect(result).to.equal(downlinkValue.toString()); - }); - - it('should return undefined if downlink is not available', function() { - const fakeNavigator = { - connection: {} - }; - - const result = getConnectionDownLink({ navigator: fakeNavigator }); - expect(result).to.be.undefined; - }); - - it('should return undefined if connection is not available', function() { - const fakeNavigator = {}; - - const result = getConnectionDownLink({ navigator: fakeNavigator }); - expect(result).to.be.undefined; - }); - - it('should handle cases where navigator is not defined', function() { - const result = getConnectionDownLink({}); - expect(result).to.be.undefined; - }); - }); - - describe('getUserSyncs with message event listener', function() { - function messageHandler(event) { - if (!event.data || event.origin !== THIRD_PARTY_COOKIE_ORIGIN) { - return; - } - - window.removeEventListener('message', messageHandler, true); - event.stopImmediatePropagation(); - - const response = event.data; - if (!response.optout && response.mguid) { - storage.setCookie(COOKIE_KEY_MGUID, response.mguid, getCurrentTimeToUTCString()); - } - } - - let sandbox; - - beforeEach(() => { - sandbox = sinon.createSandbox(); - sandbox.stub(storage, 'setCookie'); - sandbox.stub(window, 'removeEventListener'); - }); - - afterEach(() => { - sandbox.restore(); - }); - - it('should set a cookie when a valid message is received', () => { - const fakeEvent = { - data: { optout: '', mguid: '12345' }, - origin: THIRD_PARTY_COOKIE_ORIGIN, - stopImmediatePropagation: sinon.spy() - }; - - messageHandler(fakeEvent); - - expect(fakeEvent.stopImmediatePropagation.calledOnce).to.be.true; - expect(window.removeEventListener.calledWith('message', messageHandler, true)).to.be.true; - expect(storage.setCookie.calledWith(COOKIE_KEY_MGUID, '12345', sinon.match.string)).to.be.true; - }); - it('should not do anything when an invalid message is received', () => { - const fakeEvent = { - data: null, - origin: 'http://invalid-origin.com', - stopImmediatePropagation: sinon.spy() - }; - - messageHandler(fakeEvent); - - expect(fakeEvent.stopImmediatePropagation.notCalled).to.be.true; - expect(window.removeEventListener.notCalled).to.be.true; - expect(storage.setCookie.notCalled).to.be.true; - }); - }); - }); }); diff --git a/test/spec/modules/medianetBidAdapter_spec.js b/test/spec/modules/medianetBidAdapter_spec.js index 4a221e97444..bb90eded230 100644 --- a/test/spec/modules/medianetBidAdapter_spec.js +++ b/test/spec/modules/medianetBidAdapter_spec.js @@ -915,24 +915,24 @@ let VALID_BID_REQUEST = [{ cid: '8CUV090' } }, - VALID_PARAMS_TS = { - bidder: 'trustedstack', + VALID_PARAMS_AAX = { + bidder: 'aax', params: { - cid: 'TS012345' + cid: 'AAXG123' } }, PARAMS_MISSING = { bidder: 'medianet', }, - PARAMS_MISSING_TS = { - bidder: 'trustedstack', + PARAMS_MISSING_AAX = { + bidder: 'aax', }, PARAMS_WITHOUT_CID = { bidder: 'medianet', params: {} }, - PARAMS_WITHOUT_CID_TS = { - bidder: 'trustedstack', + PARAMS_WITHOUT_CID_AAX = { + bidder: 'aax', params: {} }, PARAMS_WITH_INTEGER_CID = { @@ -941,8 +941,8 @@ let VALID_BID_REQUEST = [{ cid: 8867587 } }, - PARAMS_WITH_INTEGER_CID_TS = { - bidder: 'trustedstack', + PARAMS_WITH_INTEGER_CID_AAX = { + bidder: 'aax', params: { cid: 8867587 } @@ -953,8 +953,8 @@ let VALID_BID_REQUEST = [{ cid: '' } }, - PARAMS_WITH_EMPTY_CID_TS = { - bidder: 'trustedstack', + PARAMS_WITH_EMPTY_CID_AAX = { + bidder: 'aax', params: { cid: '' } @@ -1311,118 +1311,6 @@ let VALID_BID_REQUEST = [{ } }], 'tmax': 3000, - }, - VALID_BIDDER_REQUEST_WITH_GPP_IN_ORTB2 = { - ortb2: { - regs: { - gpp: 'DBACNYA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA~1YNN', - gpp_sid: [5, 7] - } - }, - 'timeout': 3000, - refererInfo: { - referer: 'http://media.net/prebidtest', - stack: ['http://media.net/prebidtest'], - page: 'http://media.net/page', - domain: 'media.net', - topmostLocation: 'http://media.net/topmost', - reachedTop: true - } - }, - VALID_PAYLOAD_FOR_GPP_ORTB2 = { - 'site': { - 'page': 'http://media.net/prebidtest', - 'domain': 'media.net', - 'ref': 'http://media.net/prebidtest', - 'topMostLocation': 'http://media.net/topmost', - 'isTop': true - }, - 'ext': { - 'customer_id': 'customer_id', - 'prebid_version': $$PREBID_GLOBAL$$.version, - 'gdpr_applies': false, - 'usp_applies': false, - 'coppa_applies': false, - 'screen': { - 'w': 1000, - 'h': 1000 - } - }, - 'id': 'aafabfd0-28c0-4ac0-aa09-99689e88b81d', - 'imp': [{ - 'id': '28f8f8130a583e', - 'transactionId': '277b631f-92f5-4844-8b19-ea13c095d3f1', - ortb2Imp: VALID_BID_REQUEST[0].ortb2Imp, - 'ext': { - 'dfp_id': 'div-gpt-ad-1460505748561-0', - 'visibility': 1, - 'viewability': 1, - 'coordinates': { - 'top_left': { - x: 50, - y: 50 - }, - 'bottom_right': { - x: 100, - y: 100 - } - }, - 'display_count': 1 - }, - 'banner': [{ - 'w': 300, - 'h': 250 - }], - 'all': { - 'cid': 'customer_id', - 'site': { - 'page': 'http://media.net/prebidtest', - 'domain': 'media.net', - 'ref': 'http://media.net/prebidtest', - 'isTop': true - } - } - }, { - 'id': '3f97ca71b1e5c2', - 'transactionId': 'c52a5c62-3c2b-4b90-9ff8-ec1487754822', - ortb2Imp: VALID_BID_REQUEST[1].ortb2Imp, - 'ext': { - 'dfp_id': 'div-gpt-ad-1460505748561-123', - 'visibility': 1, - 'viewability': 1, - 'coordinates': { - 'top_left': { - x: 50, - y: 50 - }, - 'bottom_right': { - x: 100, - y: 100 - } - }, - 'display_count': 1 - }, - 'banner': [{ - 'w': 300, - 'h': 251 - }], - 'all': { - 'cid': 'customer_id', - 'site': { - 'page': 'http://media.net/prebidtest', - 'domain': 'media.net', - 'ref': 'http://media.net/prebidtest', - 'isTop': true - } - } - }], - 'ortb2': { - 'regs': { - 'gpp': 'DBACNYA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA~1YNN', - 'gpp_sid': [5, 7], - } - }, - 'tmax': config.getConfig('bidderTimeout') }; describe('Media.net bid adapter', function () { let sandbox; @@ -1505,11 +1393,6 @@ describe('Media.net bid adapter', function () { expect(JSON.parse(bidReq.data)).to.deep.equal(VALID_PAYLOAD_FOR_GDPR); }); - it('should have gpp params in ortb2', function () { - let bidReq = spec.buildRequests(VALID_BID_REQUEST, VALID_BIDDER_REQUEST_WITH_GPP_IN_ORTB2); - expect(JSON.parse(bidReq.data)).to.deep.equal(VALID_PAYLOAD_FOR_GPP_ORTB2); - }); - it('should parse params for native request', function () { let bidReq = spec.buildRequests(VALID_NATIVE_BID_REQUEST, VALID_AUCTIONDATA); expect(JSON.parse(bidReq.data)).to.deep.equal(VALID_PAYLOAD_NATIVE); @@ -1783,34 +1666,34 @@ describe('Media.net bid adapter', function () { }); }); - describe('isBidRequestValid trustedstack', function () { + describe('isBidRequestValid aax', function () { it('should accept valid bid params', function () { - let isValid = spec.isBidRequestValid(VALID_PARAMS_TS); + let isValid = spec.isBidRequestValid(VALID_PARAMS_AAX); expect(isValid).to.equal(true); }); it('should reject bid if cid is not present', function () { - let isValid = spec.isBidRequestValid(PARAMS_WITHOUT_CID_TS); + let isValid = spec.isBidRequestValid(PARAMS_WITHOUT_CID_AAX); expect(isValid).to.equal(false); }); it('should reject bid if cid is not a string', function () { - let isValid = spec.isBidRequestValid(PARAMS_WITH_INTEGER_CID_TS); + let isValid = spec.isBidRequestValid(PARAMS_WITH_INTEGER_CID_AAX); expect(isValid).to.equal(false); }); it('should reject bid if cid is a empty string', function () { - let isValid = spec.isBidRequestValid(PARAMS_WITH_EMPTY_CID_TS); + let isValid = spec.isBidRequestValid(PARAMS_WITH_EMPTY_CID_AAX); expect(isValid).to.equal(false); }); it('should have missing params', function () { - let isValid = spec.isBidRequestValid(PARAMS_MISSING_TS); + let isValid = spec.isBidRequestValid(PARAMS_MISSING_AAX); expect(isValid).to.equal(false); }); }); - describe('interpretResponse trustedstack', function () { + describe('interpretResponse aax', function () { it('should not push response if no-bid', function () { let validBids = []; let bids = spec.interpretResponse(SERVER_RESPONSE_NOBID, []); diff --git a/test/spec/modules/mediasquareBidAdapter_spec.js b/test/spec/modules/mediasquareBidAdapter_spec.js index d7984c05967..4e131024d84 100644 --- a/test/spec/modules/mediasquareBidAdapter_spec.js +++ b/test/spec/modules/mediasquareBidAdapter_spec.js @@ -1,6 +1,5 @@ import {expect} from 'chai'; import {spec} from 'modules/mediasquareBidAdapter.js'; -import { server } from 'test/mocks/xhr.js'; describe('MediaSquare bid adapter tests', function () { var DEFAULT_PARAMS = [{ @@ -100,8 +99,6 @@ describe('MediaSquare bid adapter tests', function () { 'bid_id': 'aaaa1234', 'adomain': ['test.com'], 'context': 'instream', - 'increment': 1.0, - 'ova': 'cleared', }], }}; @@ -169,10 +166,7 @@ describe('MediaSquare bid adapter tests', function () { expect(bid.mediasquare.bidder).to.equal('msqClassic'); expect(bid.mediasquare.context).to.exist; expect(bid.mediasquare.context).to.equal('instream'); - expect(bid.mediasquare.increment).to.exist; - expect(bid.mediasquare.increment).to.equal(1.0); expect(bid.mediasquare.code).to.equal([DEFAULT_PARAMS[0].params.owner, DEFAULT_PARAMS[0].params.code].join('/')); - expect(bid.mediasquare.ova).to.exist.and.to.equal('cleared'); expect(bid.meta).to.exist; expect(bid.meta.advertiserDomains).to.exist; expect(bid.meta.advertiserDomains).to.have.lengthOf(1); @@ -211,11 +205,6 @@ describe('MediaSquare bid adapter tests', function () { 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); - let message = JSON.parse(server.requests[0].requestBody); - expect(message).to.have.property('increment').exist; - expect(message).to.have.property('increment').and.to.equal('1'); - expect(message).to.have.property('ova').and.to.equal('cleared'); }); it('Verifies user sync without cookie in bid response', function () { var syncs = spec.getUserSyncs({}, [BID_RESPONSE], DEFAULT_OPTIONS.gdprConsent, DEFAULT_OPTIONS.uspConsent); diff --git a/test/spec/modules/mgidRtdProvider_spec.js b/test/spec/modules/mgidRtdProvider_spec.js index 996875649b6..4f70b4d8b7c 100644 --- a/test/spec/modules/mgidRtdProvider_spec.js +++ b/test/spec/modules/mgidRtdProvider_spec.js @@ -1,14 +1,16 @@ import { mgidSubmodule, storage } from '../../../modules/mgidRtdProvider.js'; import {expect} from 'chai'; import * as refererDetection from '../../../src/refererDetection'; -import {server} from '../../mocks/xhr.js'; describe('Mgid RTD submodule', () => { + let server; let clock; let getRefererInfoStub; let getDataFromLocalStorageStub; beforeEach(() => { + server = sinon.fakeServer.create(); + clock = sinon.useFakeTimers(); getRefererInfoStub = sinon.stub(refererDetection, 'getRefererInfo'); @@ -20,6 +22,7 @@ describe('Mgid RTD submodule', () => { }); afterEach(() => { + server.restore(); clock.restore(); getRefererInfoStub.restore(); getDataFromLocalStorageStub.restore(); @@ -306,6 +309,7 @@ describe('Mgid RTD submodule', () => { server.requests[0].respond( 204, {'Content-Type': 'application/json'}, + '{}' ); assert.deepEqual(reqBidsConfigObj.ortb2Fragments.global, {}); diff --git a/test/spec/modules/mgidXBidAdapter_spec.js b/test/spec/modules/mgidXBidAdapter_spec.js index 978ca3de036..14619e9c0e1 100644 --- a/test/spec/modules/mgidXBidAdapter_spec.js +++ b/test/spec/modules/mgidXBidAdapter_spec.js @@ -76,10 +76,7 @@ describe('MGIDXBidAdapter', function () { const bidderRequest = { uspConsent: '1---', - gdprConsent: { - consentString: 'COvFyGBOvFyGBAbAAAENAPCAAOAAAAAAAAAAAEEUACCKAAA.IFoEUQQgAIQwgIwQABAEAAAAOIAACAIAAAAQAIAgEAACEAAAAAgAQBAAAAAAAGBAAgAAAAAAAFAAECAAAgAAQARAEQAAAAAJAAIAAgAAAYQEAAAQmAgBC3ZAYzUw', - vendorData: {} - }, + gdprConsent: 'COvFyGBOvFyGBAbAAAENAPCAAOAAAAAAAAAAAEEUACCKAAA.IFoEUQQgAIQwgIwQABAEAAAAOIAACAIAAAAQAIAgEAACEAAAAAgAQBAAAAAAAGBAAgAAAAAAAFAAECAAAgAAQARAEQAAAAAJAAIAAgAAAYQEAAAQmAgBC3ZAYzUw', refererInfo: { referer: 'https://test.com' } @@ -134,7 +131,7 @@ describe('MGIDXBidAdapter', function () { 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.gdpr).to.be.a('string'); expect(data.ccpa).to.be.a('string'); expect(data.tmax).to.be.a('number'); expect(data.placements).to.have.lengthOf(3); @@ -175,10 +172,8 @@ describe('MGIDXBidAdapter', function () { serverRequest = spec.buildRequests(bids, bidderRequest); let 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.gdpr).to.be.a('string'); + expect(data.gdpr).to.equal(bidderRequest.gdprConsent); expect(data.ccpa).to.not.exist; delete bidderRequest.gdprConsent; }); diff --git a/test/spec/modules/minutemediaBidAdapter_spec.js b/test/spec/modules/minutemediaBidAdapter_spec.js index 7e22114aeed..0d12e422cd7 100644 --- a/test/spec/modules/minutemediaBidAdapter_spec.js +++ b/test/spec/modules/minutemediaBidAdapter_spec.js @@ -104,9 +104,6 @@ describe('minutemediaAdapter', function () { bidderCode: 'minutemedia', } const placementId = '12345678'; - const api = [1, 2]; - const mimes = ['application/javascript', 'video/mp4', 'video/quicktime']; - const protocols = [2, 3, 5, 6]; it('sends the placementId to ENDPOINT via POST', function () { bidRequests[0].params.placementId = placementId; @@ -114,24 +111,17 @@ describe('minutemediaAdapter', function () { expect(request.data.bids[0].placementId).to.equal(placementId); }); - it('sends the plcmt to ENDPOINT via POST', function () { - const request = spec.buildRequests(bidRequests, bidderRequest); - expect(request.data.bids[0].plcmt).to.equal(1); - }); - - it('sends the is_wrapper parameter to ENDPOINT via POST', function() { - const request = spec.buildRequests(bidRequests, bidderRequest); - expect(request.data.params).to.be.an('object'); - expect(request.data.params).to.have.property('is_wrapper'); - expect(request.data.params.is_wrapper).to.equal(false); - }); - it('sends bid request to ENDPOINT via POST', function () { const request = spec.buildRequests(bidRequests, bidderRequest); expect(request.url).to.equal(ENDPOINT); expect(request.method).to.equal('POST'); }); + it('sends the plcmt to ENDPOINT via POST', function () { + const request = spec.buildRequests(bidRequests, bidderRequest); + expect(request.data.bids[0].plcmt).to.equal(1); + }); + it('sends bid request to TEST ENDPOINT via POST', function () { const request = spec.buildRequests(testModeBidRequests, bidderRequest); expect(request.url).to.equal(TEST_ENDPOINT); @@ -143,27 +133,6 @@ describe('minutemediaAdapter', function () { expect(request.data.bids[0].bidId).to.equal('299ffc8cca0b87'); }); - it('should send the correct supported api array', function () { - bidRequests[0].mediaTypes.video.api = api; - const request = spec.buildRequests(bidRequests, bidderRequest); - expect(request.data.bids[0].api).to.be.an('array'); - expect(request.data.bids[0].api).to.eql([1, 2]); - }); - - it('should send the correct mimes array', function () { - bidRequests[1].mediaTypes.banner.mimes = mimes; - const request = spec.buildRequests(bidRequests, bidderRequest); - expect(request.data.bids[1].mimes).to.be.an('array'); - expect(request.data.bids[1].mimes).to.eql(['application/javascript', 'video/mp4', 'video/quicktime']); - }); - - it('should send the correct protocols array', function () { - bidRequests[0].mediaTypes.video.protocols = protocols; - const request = spec.buildRequests(bidRequests, bidderRequest); - expect(request.data.bids[0].protocols).to.be.an('array'); - expect(request.data.bids[0].protocols).to.eql([2, 3, 5, 6]); - }); - it('should send the correct sizes array', function () { const request = spec.buildRequests(bidRequests, bidderRequest); expect(request.data.bids[0].sizes).to.be.an('array'); @@ -178,16 +147,6 @@ describe('minutemediaAdapter', function () { expect(request.data.bids[1].mediaType).to.equal(BANNER) }); - it('should send the correct currency in bid request', function () { - const bid = utils.deepClone(bidRequests[0]); - bid.params = { - 'currency': 'EUR' - }; - const expectedCurrency = bid.params.currency; - const request = spec.buildRequests([bid], bidderRequest); - expect(request.data.bids[0].currency).to.equal(expectedCurrency); - }); - it('should respect syncEnabled option', function() { config.setConfig({ userSync: { diff --git a/test/spec/modules/minutemediaplusBidAdapter_spec.js b/test/spec/modules/minutemediaplusBidAdapter_spec.js index 5101f015b0e..33ec194c61f 100644 --- a/test/spec/modules/minutemediaplusBidAdapter_spec.js +++ b/test/spec/modules/minutemediaplusBidAdapter_spec.js @@ -423,25 +423,6 @@ describe('MinuteMediaPlus Bid Adapter', function () { 'type': 'image' }]); }) - - 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.minutemedia-prebid.com/api/sync/image/?cid=testcid123&gdpr=1&gdpr_consent=consent_string&us_privacy=usp_string&gpp=gpp_string&gpp_sid=7', - 'type': 'image' - }]); - }); }); describe('interpret response', function () { diff --git a/test/spec/modules/missenaBidAdapter_spec.js b/test/spec/modules/missenaBidAdapter_spec.js index f84981352ab..f61987298e8 100644 --- a/test/spec/modules/missenaBidAdapter_spec.js +++ b/test/spec/modules/missenaBidAdapter_spec.js @@ -1,70 +1,23 @@ import { expect } from 'chai'; -import { spec, storage } from 'modules/missenaBidAdapter.js'; -import { BANNER } from '../../../src/mediaTypes.js'; - -const REFERRER = 'https://referer'; -const REFERRER2 = 'https://referer2'; -const COOKIE_DEPRECATION_LABEL = 'test'; +import { spec, _getPlatform } from 'modules/missenaBidAdapter.js'; +import { newBidder } from 'src/adapters/bidderFactory.js'; describe('Missena Adapter', function () { - $$PREBID_GLOBAL$$.bidderSettings = { - missena: { - storageAllowed: true, - }, - }; + const adapter = newBidder(spec); const bidId = 'abc'; + const bid = { bidder: 'missena', bidId: bidId, sizes: [[1, 1]], - mediaTypes: { banner: { sizes: [[1, 1]] } }, - ortb2: { - device: { - ext: { cdep: COOKIE_DEPRECATION_LABEL }, - }, - }, params: { apiKey: 'PA-34745704', placement: 'sticky', formats: ['sticky-banner'], }, - getFloor: (inputParams) => { - if (inputParams.mediaType === BANNER) { - return { - currency: 'EUR', - floor: 3.5, - }; - } else { - return {}; - } - }, }; - const bidWithoutFloor = { - bidder: 'missena', - bidId: bidId, - sizes: [[1, 1]], - mediaTypes: { banner: { sizes: [[1, 1]] } }, - params: { - apiKey: 'PA-34745704', - placement: 'sticky', - formats: ['sticky-banner'], - }, - }; - const consentString = 'AAAAAAAAA=='; - const bidderRequest = { - gdprConsent: { - consentString: consentString, - gdprApplies: true, - }, - refererInfo: { - topmostLocation: REFERRER, - canonicalUrl: 'https://canonical', - }, - }; - - const bids = [bid, bidWithoutFloor]; describe('codes', function () { it('should return a bidder code of missena', function () { expect(spec.code).to.equal('missena'); @@ -78,27 +31,34 @@ describe('Missena Adapter', function () { it('should return false if the apiKey is missing', function () { expect( - spec.isBidRequestValid(Object.assign(bid, { params: {} })), + spec.isBidRequestValid(Object.assign(bid, { params: {} })) ).to.equal(false); }); it('should return false if the apiKey is an empty string', function () { expect( - spec.isBidRequestValid(Object.assign(bid, { params: { apiKey: '' } })), + spec.isBidRequestValid(Object.assign(bid, { params: { apiKey: '' } })) ).to.equal(false); }); }); describe('buildRequests', function () { - let getDataFromLocalStorageStub = sinon.stub( - storage, - 'getDataFromLocalStorage', - ); + const consentString = 'AAAAAAAAA=='; - const requests = spec.buildRequests(bids, bidderRequest); + const bidderRequest = { + gdprConsent: { + consentString: consentString, + gdprApplies: true, + }, + refererInfo: { + topmostLocation: 'https://referer', + canonicalUrl: 'https://canonical', + }, + }; + + const requests = spec.buildRequests([bid, bid], bidderRequest); const request = requests[0]; const payload = JSON.parse(request.data); - const payloadNoFloor = JSON.parse(requests[1].data); it('should return as many server requests as bidder requests', function () { expect(requests.length).to.equal(2); @@ -121,7 +81,7 @@ describe('Missena Adapter', function () { }); it('should send referer information to the request', function () { - expect(payload.referer).to.equal(REFERRER); + expect(payload.referer).to.equal('https://referer'); expect(payload.referer_canonical).to.equal('https://canonical'); }); @@ -129,74 +89,6 @@ describe('Missena Adapter', function () { expect(payload.consent_string).to.equal(consentString); expect(payload.consent_required).to.equal(true); }); - it('should send floor data', function () { - expect(payload.floor).to.equal(3.5); - expect(payload.floor_currency).to.equal('EUR'); - }); - it('should not send floor data if not available', function () { - expect(payloadNoFloor.floor).to.equal(undefined); - expect(payloadNoFloor.floor_currency).to.equal(undefined); - }); - - getDataFromLocalStorageStub.restore(); - getDataFromLocalStorageStub = sinon.stub( - storage, - 'getDataFromLocalStorage', - ); - const localStorageData = { - [`missena.missena.capper.remove-bubble.${bid.params.apiKey}`]: - JSON.stringify({ - expiry: new Date().getTime() + 600_000, // 10 min into the future - }), - }; - getDataFromLocalStorageStub.callsFake((key) => localStorageData[key]); - const cappedRequests = spec.buildRequests(bids, bidderRequest); - - it('should not participate if capped', function () { - expect(cappedRequests.length).to.equal(0); - }); - - const localStorageDataSamePage = { - [`missena.missena.capper.remove-bubble.${bid.params.apiKey}`]: - JSON.stringify({ - expiry: new Date().getTime() + 600_000, // 10 min into the future - referer: REFERRER, - }), - }; - - getDataFromLocalStorageStub.callsFake( - (key) => localStorageDataSamePage[key], - ); - const cappedRequestsSamePage = spec.buildRequests(bids, bidderRequest); - - it('should not participate if capped on same page', function () { - expect(cappedRequestsSamePage.length).to.equal(0); - }); - - const localStorageDataOtherPage = { - [`missena.missena.capper.remove-bubble.${bid.params.apiKey}`]: - JSON.stringify({ - expiry: new Date().getTime() + 600_000, // 10 min into the future - referer: REFERRER2, - }), - }; - - getDataFromLocalStorageStub.callsFake( - (key) => localStorageDataOtherPage[key], - ); - const cappedRequestsOtherPage = spec.buildRequests(bids, bidderRequest); - - it('should participate if capped on a different page', function () { - expect(cappedRequestsOtherPage.length).to.equal(2); - }); - - it('should send the prebid version', function () { - expect(payload.version).to.equal('$prebid.version$'); - }); - - it('should send cookie deprecation', function () { - expect(payload.cdep).to.equal(COOKIE_DEPRECATION_LABEL); - }); }); describe('interpretResponse', function () { @@ -229,14 +121,14 @@ describe('Missena Adapter', function () { expect(result.length).to.equal(1); expect(Object.keys(result[0])).to.have.members( - Object.keys(serverResponse), + Object.keys(serverResponse) ); }); it('should return an empty response when the server answers with a timeout', function () { const result = spec.interpretResponse( { body: serverTimeoutResponse }, - bid, + bid ); expect(result).to.deep.equal([]); }); @@ -244,7 +136,7 @@ describe('Missena Adapter', function () { it('should return an empty response when the server answers with an empty ad', function () { const result = spec.interpretResponse( { body: serverEmptyAdResponse }, - bid, + bid ); expect(result).to.deep.equal([]); }); diff --git a/test/spec/modules/mobfoxpbBidAdapter_spec.js b/test/spec/modules/mobfoxpbBidAdapter_spec.js index a4e58afbd1b..766f8d1a848 100644 --- a/test/spec/modules/mobfoxpbBidAdapter_spec.js +++ b/test/spec/modules/mobfoxpbBidAdapter_spec.js @@ -20,8 +20,7 @@ describe('MobfoxHBBidAdapter', function () { const bidderRequest = { refererInfo: { referer: 'test.com' - }, - ortb2: {} + } }; describe('isBidRequestValid', function () { @@ -144,36 +143,6 @@ describe('MobfoxHBBidAdapter', function () { expect(data.placements).to.be.an('array').that.is.empty; }); }); - - describe('gpp consent', function () { - it('bidderRequest.gppConsent', () => { - bidderRequest.gppConsent = { - gppString: 'abc123', - applicableSections: [8] - }; - - let serverRequest = spec.buildRequests([bid], bidderRequest); - let 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.regs = bidderRequest.ortb2.regs || {}; - bidderRequest.ortb2.regs.gpp = 'abc123'; - bidderRequest.ortb2.regs.gpp_sid = [8]; - - let serverRequest = spec.buildRequests([bid], bidderRequest); - let 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 = { diff --git a/test/spec/modules/multibid_spec.js b/test/spec/modules/multibid_spec.js index c11113473ce..eaf8fa33a66 100644 --- a/test/spec/modules/multibid_spec.js +++ b/test/spec/modules/multibid_spec.js @@ -1,15 +1,16 @@ import {expect} from 'chai'; import { - addBidResponseHook, + validateMultibid, adjustBidderRequestsHook, + addBidResponseHook, resetMultibidUnits, - resetMultiConfig, sortByMultibid, targetBidPoolHook, - validateMultibid + resetMultiConfig } from 'modules/multibid/index.js'; +import {parse as parseQuery} from 'querystring'; import {config} from 'src/config.js'; -import {getHighestCpm} from '../../../src/utils/reducers.js'; +import * as utils from 'src/utils.js'; describe('multibid adapter', function () { let bidArray = [{ @@ -544,7 +545,7 @@ describe('multibid adapter', function () { it('it does not run filter on bidsReceived if no multibid configuration found', function () { let bids = [{...bidArray[0]}, {...bidArray[1]}]; - targetBidPoolHook(callbackFn, bids, getHighestCpm); + targetBidPoolHook(callbackFn, bids, utils.getHighestCpm); expect(result).to.not.equal(null); expect(result.bidsReceived).to.not.equal(null); @@ -561,7 +562,7 @@ describe('multibid adapter', function () { config.setConfig({multibid: [{bidder: 'bidderA', maxBids: 2}]}); - targetBidPoolHook(callbackFn, bids, getHighestCpm); + targetBidPoolHook(callbackFn, bids, utils.getHighestCpm); bids.pop(); expect(result).to.not.equal(null); @@ -583,7 +584,7 @@ describe('multibid adapter', function () { config.setConfig({multibid: [{bidder: 'bidderA', maxBids: 2, targetBiddercodePrefix: 'bidA'}]}); - targetBidPoolHook(callbackFn, modifiedBids, getHighestCpm); + targetBidPoolHook(callbackFn, modifiedBids, utils.getHighestCpm); expect(result).to.not.equal(null); expect(result.bidsReceived).to.not.equal(null); @@ -608,7 +609,7 @@ describe('multibid adapter', function () { config.setConfig({multibid: [{bidder: 'bidderA', maxBids: 2, targetBiddercodePrefix: 'bidA'}]}); - targetBidPoolHook(callbackFn, modifiedBids, getHighestCpm); + targetBidPoolHook(callbackFn, modifiedBids, utils.getHighestCpm); expect(result).to.not.equal(null); expect(result.bidsReceived).to.not.equal(null); @@ -641,7 +642,7 @@ describe('multibid adapter', function () { config.setConfig({ multibid: [{bidder: 'bidderA', maxBids: 2, targetBiddercodePrefix: 'bidA'}] }); - targetBidPoolHook(callbackFn, modifiedBids, getHighestCpm, 3); + targetBidPoolHook(callbackFn, modifiedBids, utils.getHighestCpm, 3); expect(result).to.not.equal(null); expect(result.bidsReceived).to.not.equal(null); @@ -669,7 +670,7 @@ describe('multibid adapter', function () { expect(bidPool.length).to.equal(6); - targetBidPoolHook(callbackFn, bidPool, getHighestCpm); + targetBidPoolHook(callbackFn, bidPool, utils.getHighestCpm); expect(result).to.not.equal(null); expect(result.bidsReceived).to.not.equal(null); diff --git a/test/spec/modules/mygaruIdSystem_spec.js b/test/spec/modules/mygaruIdSystem_spec.js deleted file mode 100644 index 2bfb5fdd4af..00000000000 --- a/test/spec/modules/mygaruIdSystem_spec.js +++ /dev/null @@ -1,62 +0,0 @@ -import { mygaruIdSubmodule } from 'modules/mygaruIdSystem.js'; -import { server } from '../../mocks/xhr'; - -describe('MygaruID module', function () { - it('should respond with async callback and get valid id', async () => { - const callBackSpy = sinon.spy(); - const expectedUrl = `https://ident.mygaru.com/v2/id?gdprApplies=0`; - const result = mygaruIdSubmodule.getId({}); - - expect(result.callback).to.be.an('function'); - const promise = result.callback(callBackSpy); - - const request = server.requests[0]; - expect(request.url).to.be.eq(expectedUrl); - - request.respond( - 200, - { 'Content-Type': 'application/json' }, - JSON.stringify({ iuid: '123' }) - ); - await promise; - - expect(callBackSpy.calledOnce).to.be.true; - expect(callBackSpy.calledWith({mygaruId: '123'})).to.be.true; - }); - it('should not fail on error', async () => { - const callBackSpy = sinon.spy(); - const expectedUrl = `https://ident.mygaru.com/v2/id?gdprApplies=0`; - const result = mygaruIdSubmodule.getId({}); - - expect(result.callback).to.be.an('function'); - const promise = result.callback(callBackSpy); - - const request = server.requests[0]; - expect(request.url).to.be.eq(expectedUrl); - - request.respond( - 500, - {}, - '' - ); - await promise; - - expect(callBackSpy.calledOnce).to.be.true; - expect(callBackSpy.calledWith({mygaruId: undefined})).to.be.true; - }); - - it('should not modify while decoding', () => { - const id = '222'; - const newId = mygaruIdSubmodule.decode(id) - - expect(id).to.eq(newId); - }) - it('should buildUrl with consent data', () => { - const result = mygaruIdSubmodule.getId({}, { - gdprApplies: true, - consentString: 'consentString' - }); - - expect(result.url).to.eq('https://ident.mygaru.com/v2/id?gdprApplies=1&gdprConsentString=consentString'); - }) -}); diff --git a/test/spec/modules/nativoBidAdapter_spec.js b/test/spec/modules/nativoBidAdapter_spec.js index 75fb357b196..51e78d1f6d6 100644 --- a/test/spec/modules/nativoBidAdapter_spec.js +++ b/test/spec/modules/nativoBidAdapter_spec.js @@ -112,7 +112,7 @@ describe('nativoBidAdapterTests', function () { bidRequests = [JSON.parse(bidRequestString)] }) - it('Request should be POST, with JSON string payload and QS params should be added to the url', function () { + it('url should contain query string parameters', function () { const request = spec.buildRequests(bidRequests, { bidderRequestId: 123456, refererInfo: { @@ -120,11 +120,6 @@ describe('nativoBidAdapterTests', function () { }, }) - expect(request.method).to.equal('POST') - - expect(request.data).to.exist - expect(request.data).to.be.a('string') - expect(request.url).to.exist expect(request.url).to.be.a('string') diff --git a/test/spec/modules/newspassidBidAdapter_spec.js b/test/spec/modules/newspassidBidAdapter_spec.js index 6468d4f530a..bec6eea7bf2 100644 --- a/test/spec/modules/newspassidBidAdapter_spec.js +++ b/test/spec/modules/newspassidBidAdapter_spec.js @@ -1667,12 +1667,6 @@ describe('newspassid Adapter', function () { expect(result[0]['price']).to.equal(0.9); expect(result[0]['adserverTargeting']['np_npappnexus_adId']).to.equal('2899ec066a91ff8-0-np-1'); }); - it('should add np_auc_id (response id value)', function () { - const request = spec.buildRequests(validBidRequests, validBidderRequest); - let validres = JSON.parse(JSON.stringify(validBidResponse1adWith2Bidders)); - const result = spec.interpretResponse(validres, request); - expect(utils.deepAccess(result[0].adserverTargeting, 'np_auc_id')).to.equal(validBidResponse1adWith2Bidders.body.id); - }); it('should correctly process an auction with 2 adunits & multiple bidders one of which bids for both adslots', function() { let validres = JSON.parse(JSON.stringify(multiResponse1)); let request = spec.buildRequests(multiRequest1, multiBidderRequest1.bidderRequest); diff --git a/test/spec/modules/nextMillenniumBidAdapter_spec.js b/test/spec/modules/nextMillenniumBidAdapter_spec.js index 169ab7b01e4..564788c8b56 100644 --- a/test/spec/modules/nextMillenniumBidAdapter_spec.js +++ b/test/spec/modules/nextMillenniumBidAdapter_spec.js @@ -1,528 +1,32 @@ import { expect } from 'chai'; -import { - getImp, - replaceUsersyncMacros, - setConsentStrings, - setOrtb2Parameters, - setEids, - spec, -} from 'modules/nextMillenniumBidAdapter.js'; - -describe('nextMillenniumBidAdapterTests', () => { - describe('function getImp', () => { - const dataTests = [ - { - title: 'imp - banner', - data: { - id: '123', - bid: { - mediaTypes: {banner: {sizes: [[300, 250], [320, 250]]}}, - adUnitCode: 'test-banner-1', - }, - - mediaTypes: { - banner: { - data: {sizes: [[300, 250], [320, 250]]}, - bidfloorcur: 'EUR', - bidfloor: 1.11, - }, - }, - }, - - expected: { - id: 'test-banner-1', - bidfloorcur: 'EUR', - bidfloor: 1.11, - ext: {prebid: {storedrequest: {id: '123'}}}, - banner: {format: [{w: 300, h: 250}, {w: 320, h: 250}]}, - }, - }, - - { - title: 'imp - video', - data: { - id: '234', - bid: { - mediaTypes: {video: {playerSize: [400, 300]}}, - adUnitCode: 'test-video-1', - }, - - mediaTypes: { - video: { - data: {playerSize: [400, 300]}, - bidfloorcur: 'USD', - }, - }, - }, - - expected: { - id: 'test-video-1', - bidfloorcur: 'USD', - ext: {prebid: {storedrequest: {id: '234'}}}, - video: {w: 400, h: 300}, - }, - }, - ]; - - for (let {title, data, expected} of dataTests) { - it(title, () => { - const {bid, id, mediaTypes} = data; - const imp = getImp(bid, id, mediaTypes); - expect(imp).to.deep.equal(expected); - }); - } - }); - - describe('function setConsentStrings', () => { - const dataTests = [ - { - title: 'full: uspConsent, gdprConsent and gppConsent', - data: { - postBody: {}, - bidderRequest: { - uspConsent: '1---', - gppConsent: {gppString: 'DBACNYA~CPXxRfAPXxR', applicableSections: [7]}, - gdprConsent: {consentString: 'kjfdniwjnifwenrif3', gdprApplies: true}, - ortb2: {regs: {gpp: 'DSFHFHWEUYVDC', gpp_sid: [8, 9, 10]}}, - }, - }, - - expected: { - user: {ext: {consent: 'kjfdniwjnifwenrif3'}}, - regs: { - gpp: 'DBACNYA~CPXxRfAPXxR', - gpp_sid: [7], - ext: {gdpr: 1, us_privacy: '1---'}, - }, - }, - }, - - { - title: 'gdprConsent(false) and ortb2(gpp)', - data: { - postBody: {}, - bidderRequest: { - gdprConsent: {consentString: 'ewtewbefbawyadexv', gdprApplies: false}, - ortb2: {regs: {gpp: 'DSFHFHWEUYVDC', gpp_sid: [8, 9, 10]}}, - }, - }, - - expected: { - user: {ext: {consent: 'ewtewbefbawyadexv'}}, - regs: { - gpp: 'DSFHFHWEUYVDC', - gpp_sid: [8, 9, 10], - ext: {gdpr: 0}, - }, - }, - }, - - { - title: 'gdprConsent(false)', - data: { - postBody: {}, - bidderRequest: {gdprConsent: {gdprApplies: false}}, - }, - - expected: { - regs: {ext: {gdpr: 0}}, - }, - }, - - { - title: 'empty', - data: { - postBody: {}, - bidderRequest: {}, - }, - - expected: {}, - }, - ]; - - for (let {title, data, expected} of dataTests) { - it(title, () => { - const {postBody, bidderRequest} = data; - setConsentStrings(postBody, bidderRequest); - expect(postBody).to.deep.equal(expected); - }); - } - }); - - describe('function replaceUsersyncMacros', () => { - const dataTests = [ - { - title: 'url with all macroses - consents full: uspConsent, gdprConsent and gppConsent', - 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}, - type: 'image', - }, - - expected: 'https://some.url?gdpr=1&gdpr_consent=kjfdniwjnifwenrif3&us_privacy=1---&gpp=DBACNYA~CPXxRfAPXxR&gpp_sid=7,8&type=image', - }, - - { - title: 'url with some macroses - consents full: uspConsent, gdprConsent and gppConsent', - 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}, - type: 'iframe', - }, - - expected: 'https://some.url?gdpr=0&gdpr_consent=kjfdniwjnifwenrif3&type=iframe', - }, - - { - title: 'url without macroses - consents full: uspConsent, gdprConsent and gppConsent', - data: { - url: 'https://some.url?param1=value1¶m2=value2', - uspConsent: '1---', - gppConsent: {gppString: 'DBACNYA~CPXxRfAPXxR', applicableSections: [7, 8]}, - gdprConsent: {consentString: 'kjfdniwjnifwenrif3', gdprApplies: false}, - type: 'iframe', - }, - - expected: 'https://some.url?param1=value1¶m2=value2', - }, - - { - title: 'url with all macroses - consents are empty', - data: { - url: 'https://some.url?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}}&type={{.TYPE_PIXEL}}', - }, - - expected: 'https://some.url?gdpr=0&gdpr_consent=&us_privacy=&gpp=&gpp_sid=&type=', - }, - ]; - - for (let {title, data, expected} of dataTests) { - it(title, () => { - const {url, gdprConsent, uspConsent, gppConsent, type} = data; - const newUrl = replaceUsersyncMacros(url, gdprConsent, uspConsent, gppConsent, type); - expect(newUrl).to.equal(expected); - }); - } - }); - - describe('function spec.getUserSyncs', () => { - const dataTests = [ - { - title: 'pixels from responses ({iframeEnabled: true, pixelEnabled: true})', - data: { - 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: { - 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}}', - ], - }}}}, - ], +import { spec } from 'modules/nextMillenniumBidAdapter.js'; - uspConsent: '1---', - 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'}, - ], - }, - - { - title: 'pixels from responses ({iframeEnabled: true, pixelEnabled: false})', - data: { - 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}}', - ], - }}}}, - ], - - uspConsent: '1---', - 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---'}, - ], - }, - - { - title: 'pixels from responses ({iframeEnabled: false, pixelEnabled: true})', - data: { - 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}}', - ], - }}}}, - ], - - uspConsent: '1---', - 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'}, - ], - }, - - { - title: 'pixels - responses is empty ({iframeEnabled: true, pixelEnabled: true})', - data: { - syncOptions: {iframeEnabled: true, pixelEnabled: true}, - responses: [], - uspConsent: '1---', - 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'}, - ], - }, - - { - title: 'pixels - responses is empty ({iframeEnabled: true, pixelEnabled: false})', - data: { - syncOptions: {iframeEnabled: true, pixelEnabled: false}, - uspConsent: '1---', - 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'}, - ], - }, - - { - title: 'pixels - responses is empty ({iframeEnabled: false, pixelEnabled: false})', - data: { - syncOptions: {iframeEnabled: false, pixelEnabled: false}, - uspConsent: '1---', - gppConsent: {gppString: 'DBACNYA~CPXxRfAPXxR', applicableSections: [7, 8]}, - gdprConsent: {consentString: 'kjfdniwjnifwenrif3', gdprApplies: true}, - }, - - expected: [], - }, - ]; - - for (let {title, data, expected} of dataTests) { - it(title, () => { - const {syncOptions, responses, gdprConsent, uspConsent, gppConsent} = data; - const pixels = spec.getUserSyncs(syncOptions, responses, gdprConsent, uspConsent, gppConsent); - expect(pixels).to.deep.equal(expected); - }); - } - }); - - describe('function setOrtb2Parameters', () => { - const dataTests = [ - { - title: 'site.pagecat, site.content.cat and site.content.language', - data: { - postBody: {}, - ortb2: {site: { - pagecat: ['IAB2-11', 'IAB2-12', 'IAB2-14'], - content: {cat: ['IAB2-11', 'IAB2-12', 'IAB2-14'], language: 'EN'}, - }}, - }, - - expected: {site: { - pagecat: ['IAB2-11', 'IAB2-12', 'IAB2-14'], - content: {cat: ['IAB2-11', 'IAB2-12', 'IAB2-14'], language: 'EN'}, - }}, - }, - - { - title: 'only site.content.language', - data: { - postBody: {site: {domain: 'some.domain'}}, - ortb2: {site: { - content: {language: 'EN'}, - }}, - }, - - expected: {site: { - domain: 'some.domain', - content: {language: 'EN'}, - }}, - }, - - { - title: 'object ortb2 is empty', - data: { - postBody: {imp: []}, - }, - - expected: {imp: []}, - }, - ]; - - for (let {title, data, expected} of dataTests) { - it(title, () => { - const {postBody, ortb2} = data; - setOrtb2Parameters(postBody, ortb2); - expect(postBody).to.deep.equal(expected); - }); - }; - }); - - describe('function setEids', () => { - const dataTests = [ - { - title: 'setEids - userIdAsEids is empty', - data: { - postBody: {}, - bid: { - userIdAsEids: undefined, - }, - }, - - expected: {}, - }, - - { - title: 'setEids - userIdAsEids - array is empty', - data: { - postBody: {}, - bid: { - userIdAsEids: [], - }, - }, - - expected: {}, +describe('nextMillenniumBidAdapterTests', function() { + const bidRequestData = [ + { + adUnitCode: 'test-div', + bidId: 'bid1234', + auctionId: 'b06c5141-fe8f-4cdf-9d7d-54415490a917', + bidder: 'nextMillennium', + params: { placement_id: '-1' }, + sizes: [[300, 250]], + uspConsent: '1---', + gdprConsent: { + consentString: 'kjfdniwjnifwenrif3', + gdprApplies: true }, - - { - title: 'setEids - userIdAsEids is', - data: { - postBody: {}, - bid: { - userIdAsEids: [ - { - source: '33across.com', - uids: [{id: 'some-random-id-value', atype: 1}], - }, - - { - source: 'utiq.com', - uids: [{id: 'some-random-id-value', atype: 1}], - }, - ], - }, + ortb2: { + device: { + w: 1500, + h: 1000 }, - - expected: { - user: { - eids: [ - { - source: '33across.com', - uids: [{id: 'some-random-id-value', atype: 1}], - }, - - { - source: 'utiq.com', - uids: [{id: 'some-random-id-value', atype: 1}], - }, - ], - }, - }, - }, - ]; - - for (let { title, data, expected } of dataTests) { - it(title, () => { - const { postBody, bid } = data; - setEids(postBody, bid); - expect(postBody).to.deep.equal(expected); - }); - } - }); - - const bidRequestData = [{ - adUnitCode: 'test-div', - bidId: 'bid1234', - auctionId: 'b06c5141-fe8f-4cdf-9d7d-54415490a917', - bidder: 'nextMillennium', - params: { placement_id: '-1' }, - sizes: [[300, 250]], - uspConsent: '1---', - gppConsent: {gppString: 'DBACNYA~CPXxRfAPXxR', applicableSections: [7]}, - gdprConsent: { - consentString: 'kjfdniwjnifwenrif3', - gdprApplies: true - }, - - ortb2: { - device: { - w: 1500, - h: 1000 - }, - - site: { - domain: 'example.com', - page: 'http://example.com' + site: { + domain: 'example.com', + page: 'http://example.com' + } } } - }]; + ]; const serverResponse = { body: { @@ -545,7 +49,7 @@ describe('nextMillenniumBidAdapterTests', () => { cur: 'USD', ext: { sync: { - image: ['urlA?gdpr={{.GDPR}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}}'], + image: ['urlA?gdpr={{.GDPR}}'], iframe: ['urlB'], } } @@ -613,6 +117,61 @@ describe('nextMillenniumBidAdapterTests', () => { }, ]; + it('Request params check with GDPR and USP Consent', function () { + const request = spec.buildRequests(bidRequestData, bidRequestData[0]); + expect(JSON.parse(request[0].data).user.ext.consent).to.equal('kjfdniwjnifwenrif3'); + expect(JSON.parse(request[0].data).regs.ext.us_privacy).to.equal('1---'); + expect(JSON.parse(request[0].data).regs.ext.gdpr).to.equal(1); + }); + + it('Test getUserSyncs function', function () { + const syncOptions = { + 'iframeEnabled': false, + 'pixelEnabled': true + } + let userSync = spec.getUserSyncs(syncOptions, [serverResponse], bidRequestData[0].gdprConsent, bidRequestData[0].uspConsent); + expect(userSync).to.be.an('array').with.lengthOf(1); + expect(userSync[0].type).to.equal('image'); + expect(userSync[0].url).to.equal('urlA?gdpr=1'); + + syncOptions.iframeEnabled = true; + syncOptions.pixelEnabled = false; + userSync = spec.getUserSyncs(syncOptions, [serverResponse], bidRequestData[0].gdprConsent, bidRequestData[0].uspConsent); + expect(userSync).to.be.an('array').with.lengthOf(1); + expect(userSync[0].type).to.equal('iframe'); + expect(userSync[0].url).to.equal('urlB'); + }); + + it('Test getUserSyncs with no response', function () { + const syncOptions = { + 'iframeEnabled': true, + 'pixelEnabled': false + } + let userSync = spec.getUserSyncs(syncOptions, [], bidRequestData[0].gdprConsent, bidRequestData[0].uspConsent); + expect(userSync).to.be.an('array') + expect(userSync[0].type).to.equal('iframe') + expect(userSync[0].url).to.equal('https://cookies.nextmillmedia.com/sync?gdpr=1&gdpr_consent=kjfdniwjnifwenrif3&us_privacy=1---&type=iframe') + }) + + it('Test getUserSyncs function if GDPR is undefined', function () { + const syncOptions = { + 'iframeEnabled': false, + 'pixelEnabled': true + } + + let userSync = spec.getUserSyncs(syncOptions, [serverResponse], undefined, bidRequestData[0].uspConsent); + expect(userSync).to.be.an('array').with.lengthOf(1); + expect(userSync[0].type).to.equal('image'); + expect(userSync[0].url).to.equal('urlA?gdpr=0'); + }); + + it('Request params check without GDPR Consent', function () { + delete bidRequestData[0].gdprConsent + const request = spec.buildRequests(bidRequestData, bidRequestData[0]); + expect(JSON.parse(request[0].data).regs.ext.gdpr).to.be.undefined; + expect(JSON.parse(request[0].data).regs.ext.us_privacy).to.equal('1---'); + }); + it('validate_generated_params', function() { const request = spec.buildRequests(bidRequestData, {bidderRequestId: 'mock-uuid'}); expect(request[0].bidId).to.equal('bid1234'); @@ -631,7 +190,7 @@ describe('nextMillenniumBidAdapterTests', () => { it('Check if refresh_count param is incremented', function() { const request = spec.buildRequests(bidRequestData); - expect(JSON.parse(request[0].data).ext.nextMillennium.refresh_count).to.equal(1); + expect(JSON.parse(request[0].data).ext.nextMillennium.refresh_count).to.equal(3); }); it('Check if domain was added', function() { diff --git a/test/spec/modules/nexx360BidAdapter_spec.js b/test/spec/modules/nexx360BidAdapter_spec.js index 7091bb56631..7c2cea99a46 100644 --- a/test/spec/modules/nexx360BidAdapter_spec.js +++ b/test/spec/modules/nexx360BidAdapter_spec.js @@ -374,7 +374,7 @@ describe('Nexx360 bid adapter tests', function () { expect(requestContent.imp[1].tagid).to.be.eql('div-2-abcd'); expect(requestContent.imp[1].ext.adUnitCode).to.be.eql('div-2-abcd'); expect(requestContent.imp[1].ext.divId).to.be.eql('div-2-abcd'); - expect(requestContent.ext.bidderVersion).to.be.eql('3.0'); + expect(requestContent.ext.bidderVersion).to.be.eql('2.0'); expect(requestContent.ext.source).to.be.eql('prebid.js'); }); @@ -434,7 +434,7 @@ describe('Nexx360 bid adapter tests', function () { const output = spec.interpretResponse(response); expect(output.length).to.be.eql(0); }); - it('banner responses with adUrl only', function() { + it('banner responses', function() { const response = { body: { 'id': 'a8d3a675-a4ba-4d26-807f-c8f2fad821e0', @@ -479,53 +479,6 @@ describe('Nexx360 bid adapter tests', function () { expect(output[0].currency).to.be.eql(response.body.cur); expect(output[0].cpm).to.be.eql(response.body.seatbid[0].bid[0].price); }); - it('banner responses with adm', function() { - 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); - expect(output[0].ad).to.be.eql(response.body.seatbid[0].bid[0].adm); - expect(output[0].adUrl).to.be.eql(undefined); - expect(output[0].mediaType).to.be.eql(response.body.seatbid[0].bid[0].ext.mediaType); - expect(output[0].currency).to.be.eql(response.body.cur); - expect(output[0].cpm).to.be.eql(response.body.seatbid[0].bid[0].price); - }); it('instream responses', function() { const response = { body: { diff --git a/test/spec/modules/nobidAnalyticsAdapter_spec.js b/test/spec/modules/nobidAnalyticsAdapter_spec.js deleted file mode 100644 index 06a39ffd020..00000000000 --- a/test/spec/modules/nobidAnalyticsAdapter_spec.js +++ /dev/null @@ -1,498 +0,0 @@ -import nobidAnalytics from 'modules/nobidAnalyticsAdapter.js'; -import {expect} from 'chai'; -import {server} from 'test/mocks/xhr.js'; -let events = require('src/events'); -let adapterManager = require('src/adapterManager').default; -let constants = require('src/constants.json'); - -const TOP_LOCATION = 'https://www.somesite.com'; -const SITE_ID = 1234; - -describe('NoBid Prebid Analytic', function () { - var clock; - describe('enableAnalytics', function () { - beforeEach(function () { - sinon.stub(events, 'getEvents').returns([]); - clock = sinon.useFakeTimers(Date.now()); - }); - - afterEach(function () { - events.getEvents.restore(); - clock.restore(); - }); - - after(function () { - nobidAnalytics.disableAnalytics(); - }); - - it('auctionInit test', function (done) { - const initOptions = { - options: { - /* siteId: SITE_ID */ - } - }; - - nobidAnalytics.enableAnalytics(initOptions); - expect(nobidAnalytics.initOptions).to.equal(undefined); - - initOptions.options.siteId = SITE_ID; - nobidAnalytics.enableAnalytics(initOptions); - expect(nobidAnalytics.initOptions.siteId).to.equal(SITE_ID); - - // Step 1: Initialize adapter - adapterManager.enableAnalytics({ - provider: 'nobid', - options: initOptions - }); - - // Step 2: Send init auction event - events.emit(constants.EVENTS.AUCTION_INIT, {config: initOptions, - auctionId: '13', - timestamp: Date.now(), - bidderRequests: [{refererInfo: {topmostLocation: TOP_LOCATION}}]}); - expect(nobidAnalytics.initOptions).to.have.property('siteId', SITE_ID); - expect(nobidAnalytics).to.have.property('topLocation', TOP_LOCATION); - - const data = { ts: Date.now() }; - clock.tick(5000); - const expired = nobidAnalytics.isExpired(data); - expect(expired).to.equal(false); - - done(); - }); - - it('BID_REQUESTED/BID_RESPONSE/BID_TIMEOUT/AD_RENDER_SUCCEEDED test', function (done) { - const initOptions = { - options: { - siteId: SITE_ID - } - }; - - nobidAnalytics.enableAnalytics(initOptions); - - // Step 1: Initialize adapter - adapterManager.enableAnalytics({ - provider: 'nobid', - options: initOptions - }); - - // Step 2: Send init auction event - events.emit(constants.EVENTS.AUCTION_INIT, {config: initOptions, - auctionId: '13', - timestamp: Date.now(), - bidderRequests: [{refererInfo: {topmostLocation: TOP_LOCATION}}]}); - events.emit(constants.EVENTS.BID_WON, {}); - clock.tick(5000); - expect(server.requests).to.have.length(1); - - events.emit(constants.EVENTS.BID_REQUESTED, {}); - clock.tick(5000); - expect(server.requests).to.have.length(1); - - events.emit(constants.EVENTS.BID_RESPONSE, {}); - clock.tick(5000); - expect(server.requests).to.have.length(1); - - events.emit(constants.EVENTS.BID_TIMEOUT, {}); - clock.tick(5000); - expect(server.requests).to.have.length(1); - - events.emit(constants.EVENTS.AD_RENDER_SUCCEEDED, {}); - clock.tick(5000); - expect(server.requests).to.have.length(1); - - done(); - }); - - it('bidWon test', function (done) { - const initOptions = { - options: { - siteId: SITE_ID - } - }; - - nobidAnalytics.enableAnalytics(initOptions); - - const TOP_LOCATION = 'https://www.somesite.com'; - - const requestIncoming = { - bidderCode: 'nobid', - width: 728, - height: 9, - statusMessage: 'Bid available', - adId: '106d14b7d06b607', - requestId: '67a7f0e7ea55c4', - transactionId: 'd58cbeae-92c8-4262-ba8d-0e649cbf5470', - auctionId: 'd758cce5-d178-408c-b777-8cac605ef7ca', - mediaType: 'banner', - source: 'client', - cpm: 6.4, - creativeId: 'TEST', - dealId: '', - currency: 'USD', - netRevenue: true, - ttl: 300, - ad: 'AD HERE', - meta: { - advertiserDomains: ['advertiser_domain.com'] - }, - metrics: { - 'requestBids.usp': 0 - }, - adapterCode: 'nobid', - originalCpm: 6.44, - originalCurrency: 'USD', - responseTimestamp: 1692156287517, - requestTimestamp: 1692156286972, - bidder: 'nobid', - adUnitCode: 'leaderboard', - timeToRespond: 545, - pbCg: '', - size: '728x90', - adserverTargeting: { - hb_bidder: 'nobid', - hb_adid: '106d14b7d06b607', - hb_pb: '6.40', - hb_size: '728x90', - hb_source: 'client', - hb_format: 'banner', - hb_adomain: 'advertiser_domain.com', - 'hb_crid': 'TEST' - }, - status: 'rendered', - params: [ - { - siteId: SITE_ID - } - ] - }; - - const requestOutgoing = { - bidderCode: 'nobid', - statusMessage: 'Bid available', - adId: '106d14b7d06b607', - requestId: '67a7f0e7ea55c4', - mediaType: 'banner', - cpm: 6.4, - adUnitCode: 'leaderboard', - timeToRespond: 545, - size: '728x90', - topLocation: TOP_LOCATION - }; - - // Step 1: Initialize adapter - adapterManager.enableAnalytics({ - provider: 'nobid', - options: initOptions - }); - - // Step 2: Send init auction event - events.emit(constants.EVENTS.AUCTION_INIT, {config: initOptions, - auctionId: '13', - timestamp: Date.now(), - bidderRequests: [{refererInfo: {topmostLocation: TOP_LOCATION}}]}); - - // Step 3: Send bid won event - events.emit(constants.EVENTS.BID_WON, requestIncoming); - clock.tick(5000); - expect(server.requests).to.have.length(1); - const bidWonRequest = JSON.parse(server.requests[0].requestBody); - expect(bidWonRequest).to.have.property('bidderCode', requestOutgoing.bidderCode); - expect(bidWonRequest).to.have.property('statusMessage', requestOutgoing.statusMessage); - expect(bidWonRequest).to.have.property('adId', requestOutgoing.adId); - expect(bidWonRequest).to.have.property('requestId', requestOutgoing.requestId); - expect(bidWonRequest).to.have.property('mediaType', requestOutgoing.mediaType); - expect(bidWonRequest).to.have.property('cpm', requestOutgoing.cpm); - expect(bidWonRequest).to.have.property('adUnitCode', requestOutgoing.adUnitCode); - expect(bidWonRequest).to.have.property('timeToRespond', requestOutgoing.timeToRespond); - expect(bidWonRequest).to.have.property('size', requestOutgoing.size); - expect(bidWonRequest).to.have.property('topLocation', requestOutgoing.topLocation); - expect(bidWonRequest).to.not.have.property('pbCg'); - - done(); - }); - - it('auctionEnd test', function (done) { - const initOptions = { - options: { - siteId: SITE_ID - } - }; - - nobidAnalytics.enableAnalytics(initOptions); - - const TOP_LOCATION = 'https://www.somesite.com'; - - const requestIncoming = { - auctionId: '4c056b3c-f1a6-46bd-8d82-58c15b22fcfa', - timestamp: 1692224437573, - auctionEnd: 1692224437986, - auctionStatus: 'completed', - adUnits: [ - { - code: 'leaderboard', - sizes: [[728, 90]], - sizeConfig: [ - { minViewPort: [0, 0], sizes: [[300, 250]] }, - { minViewPort: [750, 0], sizes: [[728, 90]] } - ], - adunit: '/111111/adunit', - bids: [{ bidder: 'nobid', params: { siteId: SITE_ID } }] - } - ], - adUnitCodes: ['leaderboard'], - bidderRequests: [ - { - bidderCode: 'nobid', - auctionId: '4c056b3c-f1a6-46bd-8d82-58c15b22fcfa', - bidderRequestId: '5beedb9f99ad98', - bids: [ - { - bidder: 'nobid', - params: { siteId: SITE_ID }, - mediaTypes: { banner: { sizes: [[728, 90]] } }, - adUnitCode: 'leaderboard', - transactionId: 'bcda424d-f4f4-419b-acf9-1808d2dd22b1', - sizes: [[728, 90]], - bidId: '6ef0277f36c8df', - bidderRequestId: '5beedb9f99ad98', - auctionId: '4c056b3c-f1a6-46bd-8d82-58c15b22fcfa', - src: 'client', - bidRequestsCount: 1, - bidderRequestsCount: 1, - bidderWinsCount: 0, - ortb2: { - site: { - domain: 'site.me', - publisher: { - domain: 'site.me' - }, - page: TOP_LOCATION - }, - device: { - w: 2605, - h: 895, - dnt: 0, - ua: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36', - language: 'en', - } - } - } - ], - auctionStart: 1692224437573, - timeout: 3000, - refererInfo: { - topmostLocation: TOP_LOCATION, - location: TOP_LOCATION, - page: TOP_LOCATION, - domain: 'site.me', - ref: null, - } - } - ], - noBids: [ - ], - bidsReceived: [ - { - bidderCode: 'nobid', - width: 728, - height: 90, - statusMessage: 'Bid available', - adId: '95781b6ae5ef2f', - requestId: '6ef0277f36c8df', - transactionId: 'bcda424d-f4f4-419b-acf9-1808d2dd22b1', - auctionId: '4c056b3c-f1a6-46bd-8d82-58c15b22fcfa', - mediaType: 'banner', - source: 'client', - cpm: 6.44, - creativeId: 'TEST', - dealId: '', - currency: 'USD', - netRevenue: true, - ttl: 300, - ad: '', - meta: { - advertiserDomains: [ - 'advertiser_domain.com' - ] - }, - adapterCode: 'nobid', - originalCpm: 6.44, - originalCurrency: 'USD', - responseTimestamp: 1692224437982, - requestTimestamp: 1692224437576, - bidder: 'nobid', - adUnitCode: 'leaderboard', - timeToRespond: 0, - pbLg: 5.00, - pbCg: '', - size: '728x90', - adserverTargeting: { hb_bidder: 'nobid', hb_pb: '6.40' }, - status: 'targetingSet' - } - ], - bidsRejected: [], - winningBids: [], - timeout: 3000 - }; - - const requestOutgoing = { - auctionId: '4c056b3c-f1a6-46bd-8d82-58c15b22fcfa', - bidderRequests: [ - { - bidderCode: 'nobid', - bidderRequestId: '7c1940bb285731', - bids: [ - { - bidder: 'nobid', - params: { siteId: SITE_ID }, - mediaTypes: { banner: { sizes: [[728, 90]] } }, - adUnitCode: 'leaderboard', - sizes: [[728, 90]], - src: 'client', - bidRequestsCount: 1, - bidderRequestsCount: 1 - } - ], - refererInfo: { - topmostLocation: TOP_LOCATION - } - } - ], - bidsReceived: [ - { - bidderCode: 'nobid', - width: 728, - height: 90, - mediaType: 'banner', - cpm: 6.44, - adUnitCode: 'leaderboard' - } - ] - }; - - // Step 1: Initialize adapter - adapterManager.enableAnalytics({ - provider: 'nobid', - options: initOptions - }); - - // Step 2: Send init auction event - events.emit(constants.EVENTS.AUCTION_INIT, {config: initOptions, - auctionId: '13', - timestamp: Date.now(), - bidderRequests: [{refererInfo: {topmostLocation: `${TOP_LOCATION}_something`}}]}); - - // Step 3: Send bid won event - events.emit(constants.EVENTS.AUCTION_END, requestIncoming); - clock.tick(5000); - expect(server.requests).to.have.length(1); - const auctionEndRequest = JSON.parse(server.requests[0].requestBody); - expect(auctionEndRequest).to.have.property('auctionId', requestOutgoing.auctionId); - expect(auctionEndRequest.bidderRequests).to.have.length(1); - expect(auctionEndRequest.bidderRequests[0]).to.have.property('bidderCode', requestOutgoing.bidderRequests[0].bidderCode); - expect(auctionEndRequest.bidderRequests[0].bids).to.have.length(1); - expect(auctionEndRequest.bidderRequests[0].bids[0]).to.have.property('bidder', requestOutgoing.bidderRequests[0].bids[0].bidder); - expect(auctionEndRequest.bidderRequests[0].bids[0]).to.have.property('adUnitCode', requestOutgoing.bidderRequests[0].bids[0].adUnitCode); - expect(auctionEndRequest.bidderRequests[0].bids[0].params).to.have.property('siteId', requestOutgoing.bidderRequests[0].bids[0].params.siteId); - expect(auctionEndRequest.bidderRequests[0].refererInfo).to.have.property('topmostLocation', requestOutgoing.bidderRequests[0].refererInfo.topmostLocation); - - done(); - }); - - it('Analytics disabled test', function (done) { - let disabled; - nobidAnalytics.processServerResponse(JSON.stringify({disabled: false})); - disabled = nobidAnalytics.isAnalyticsDisabled(); - expect(disabled).to.equal(false); - events.emit(constants.EVENTS.AUCTION_END, {auctionId: '1234567890'}); - clock.tick(1000); - expect(server.requests).to.have.length(1); - events.emit(constants.EVENTS.AUCTION_END, {auctionId: '12345678901'}); - clock.tick(1000); - expect(server.requests).to.have.length(2); - - nobidAnalytics.processServerResponse('disabled: true'); - events.emit(constants.EVENTS.AUCTION_END, {auctionId: '12345678902'}); - clock.tick(1000); - expect(server.requests).to.have.length(3); - - nobidAnalytics.processServerResponse(JSON.stringify({disabled: true})); - disabled = nobidAnalytics.isAnalyticsDisabled(); - expect(disabled).to.equal(true); - events.emit(constants.EVENTS.AUCTION_END, {auctionId: '12345678902'}); - clock.tick(5000); - expect(server.requests).to.have.length(3); - - nobidAnalytics.retentionSeconds = 5; - nobidAnalytics.processServerResponse(JSON.stringify({disabled: true})); - clock.tick(1000); - disabled = nobidAnalytics.isAnalyticsDisabled(); - expect(disabled).to.equal(true); - clock.tick(6000); - disabled = nobidAnalytics.isAnalyticsDisabled(); - expect(disabled).to.equal(false); - - done(); - }); - }); - - describe('NoBid Carbonizer', function () { - beforeEach(function () { - sinon.stub(events, 'getEvents').returns([]); - clock = sinon.useFakeTimers(Date.now()); - }); - - afterEach(function () { - events.getEvents.restore(); - clock.restore(); - }); - - after(function () { - nobidAnalytics.disableAnalytics(); - }); - - it('Carbonizer test', function (done) { - let active = nobidCarbonizer.isActive(); - expect(active).to.equal(false); - - active = nobidCarbonizer.isActive(JSON.stringify({carbonizer_active: false})); - expect(active).to.equal(false); - - 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})); - let stored = nobidCarbonizer.getStoredLocalData(); - expect(stored[nobidAnalytics.ANALYTICS_DATA_NAME]).to.contain(`{"carbonizer_active":true,"ts":`); - clock.tick(5000); - active = nobidCarbonizer.isActive(adunits, true); - expect(active).to.equal(false); - - nobidAnalytics.retentionSeconds = previousRetention; - nobidAnalytics.processServerResponse(JSON.stringify({carbonizer_active: true})); - active = nobidCarbonizer.isActive(adunits, true); - expect(active).to.equal(true); - - let adunits = [ - { - bids: [ - { bidder: 'bidder1' }, - { bidder: 'bidder2' } - ] - } - ] - nobidCarbonizer.carbonizeAdunits(adunits, true); - stored = nobidCarbonizer.getStoredLocalData(); - expect(stored[nobidAnalytics.ANALYTICS_DATA_NAME]).to.contain('{"carbonizer_active":true,"ts":'); - expect(stored[nobidAnalytics.ANALYTICS_OPT_NAME]).to.contain('{"bidder1":1,"bidder2":1}'); - clock.tick(5000); - expect(adunits[0].bids.length).to.equal(0); - - done(); - }); - }); -}); diff --git a/test/spec/modules/nobidBidAdapter_spec.js b/test/spec/modules/nobidBidAdapter_spec.js index b1e303bde6e..8328aae33d8 100644 --- a/test/spec/modules/nobidBidAdapter_spec.js +++ b/test/spec/modules/nobidBidAdapter_spec.js @@ -39,6 +39,8 @@ describe('Nobid Adapter', function () { it('should FLoor = 1', function () { spec.buildRequests(bidRequests, bidderRequest); const request = spec.buildRequests(bidRequests, bidderRequest); + /* eslint-disable no-console */ + console.log('request.data:', request.data); const payload = JSON.parse(request.data); expect(payload.a[0].floor).to.equal(1); }); @@ -143,61 +145,6 @@ describe('Nobid Adapter', function () { }); }); - describe('Request with GPP', function () { - const SITE_ID = 2; - const REFERER = 'https://www.examplereferer.com'; - const BIDDER_CODE = 'duration'; - let bidRequests = [ - { - 'bidder': BIDDER_CODE, - 'params': { - 'siteId': SITE_ID - }, - 'adUnitCode': 'adunit-code', - 'sizes': [[300, 250]], - 'bidId': '30b31c1838de1e', - 'bidderRequestId': '22edbae2733bf6', - 'auctionId': '1d1a030790a475' - } - ]; - - const GPP = 'DBACNYA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA~1YNN'; - const GPP_SID = [1, 3]; - - const bidderRequest = { - refererInfo: {page: REFERER}, - bidderCode: BIDDER_CODE, - gppConsent: {gppString: GPP, applicableSections: GPP_SID} - } - - it('gpp should match', function () { - const request = spec.buildRequests(bidRequests, bidderRequest); - let payload = JSON.parse(request.data); - payload = JSON.parse(JSON.stringify(payload)); - expect(payload.gpp).to.equal(GPP); - expect(payload.gpp_sid.join(',')).to.equal(GPP_SID.join(',')); - }); - - it('gpp should not be set', function () { - delete bidderRequest.gppConsent.applicableSections; - const request = spec.buildRequests(bidRequests, bidderRequest); - let payload = JSON.parse(request.data); - payload = JSON.parse(JSON.stringify(payload)); - expect(typeof payload.gpp).to.equal('undefined'); - expect(typeof payload.gpp_sid).to.equal('undefined'); - }); - - it('gpp ortb2 should match', function () { - delete bidderRequest.gppConsent; - 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)); - expect(payload.gpp).to.equal(GPP); - expect(payload.gpp_sid.join(',')).to.equal(GPP_SID.join(',')); - }); - }); - describe('isDurationBidRequestValid', function () { const SITE_ID = 2; const REFERER = 'https://www.examplereferer.com'; diff --git a/test/spec/modules/oguryBidAdapter_spec.js b/test/spec/modules/oguryBidAdapter_spec.js index aad753571a8..d25363de9c9 100644 --- a/test/spec/modules/oguryBidAdapter_spec.js +++ b/test/spec/modules/oguryBidAdapter_spec.js @@ -1,7 +1,6 @@ import { expect } from 'chai'; import { spec } from 'modules/oguryBidAdapter'; import * as utils from 'src/utils.js'; -import {server} from '../../mocks/xhr.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' @@ -42,21 +41,7 @@ describe('OguryBidAdapter', function () { return floorResult; }, - transactionId: 'transactionId', - userId: { - pubcid: '2abb10e5-c4f6-4f70-9f45-2200e4487714' - }, - userIdAsEids: [ - { - source: 'pubcid.org', - uids: [ - { - id: '2abb10e5-c4f6-4f70-9f45-2200e4487714', - atype: 1 - } - ] - } - ] + transactionId: 'transactionId' }, { adUnitCode: 'adUnitCode2', @@ -128,232 +113,132 @@ describe('OguryBidAdapter', function () { let syncOptions, gdprConsent; beforeEach(() => { + syncOptions = {pixelEnabled: true}; gdprConsent = { gdprApplies: true, consentString: 'CPJl4C8PJl4C8OoAAAENAwCMAP_AAH_AAAAAAPgAAAAIAPgAAAAIAAA.IGLtV_T9fb2vj-_Z99_tkeYwf95y3p-wzhheMs-8NyZeH_B4Wv2MyvBX4JiQKGRgksjLBAQdtHGlcTQgBwIlViTLMYk2MjzNKJrJEilsbO2dYGD9Pn8HT3ZCY70-vv__7v3ff_3g' }; }); - describe('pixel', () => { - beforeEach(() => { - syncOptions = { pixelEnabled: true }; - }); - - it('should return syncs array with three elements of type image', () => { - const userSyncs = spec.getUserSyncs(syncOptions, [], gdprConsent); - - expect(userSyncs).to.have.lengthOf(3); - expect(userSyncs[0].type).to.equal('image'); - expect(userSyncs[0].url).to.contain('https://ms-cookie-sync.presage.io/v1/init-sync/bid-switch'); - expect(userSyncs[1].type).to.equal('image'); - expect(userSyncs[1].url).to.contain('https://ms-cookie-sync.presage.io/ttd/init-sync'); - expect(userSyncs[2].type).to.equal('image'); - expect(userSyncs[2].url).to.contain('https://ms-cookie-sync.presage.io/xandr/init-sync'); - }); - - it('should set the source as query param', () => { - const userSyncs = spec.getUserSyncs(syncOptions, [], gdprConsent); - expect(userSyncs[0].url).to.contain('source=prebid'); - expect(userSyncs[1].url).to.contain('source=prebid'); - expect(userSyncs[2].url).to.contain('source=prebid'); - }); - - it('should set the tcString as query param', () => { - const userSyncs = spec.getUserSyncs(syncOptions, [], gdprConsent); - expect(userSyncs[0].url).to.contain(`iab_string=${gdprConsent.consentString}`); - expect(userSyncs[1].url).to.contain(`iab_string=${gdprConsent.consentString}`); - expect(userSyncs[2].url).to.contain(`iab_string=${gdprConsent.consentString}`); - }); - - it('should return an empty array when pixel is disable', () => { - syncOptions.pixelEnabled = false; - expect(spec.getUserSyncs(syncOptions, [], gdprConsent)).to.have.lengthOf(0); - }); - - it('should return syncs array with three elements of type image when consentString is undefined', () => { - gdprConsent = { - gdprApplies: true, - consentString: undefined - }; + it('should return syncs array with three elements of type image', () => { + const userSyncs = spec.getUserSyncs(syncOptions, [], gdprConsent); - const userSyncs = spec.getUserSyncs(syncOptions, [], gdprConsent); - expect(userSyncs).to.have.lengthOf(3); - expect(userSyncs[0].type).to.equal('image'); - expect(userSyncs[0].url).to.equal('https://ms-cookie-sync.presage.io/v1/init-sync/bid-switch?iab_string=&source=prebid') - expect(userSyncs[1].type).to.equal('image'); - expect(userSyncs[1].url).to.equal('https://ms-cookie-sync.presage.io/ttd/init-sync?iab_string=&source=prebid') - expect(userSyncs[2].type).to.equal('image'); - expect(userSyncs[2].url).to.equal('https://ms-cookie-sync.presage.io/xandr/init-sync?iab_string=&source=prebid') - }); - - it('should return syncs array with three elements of type image when consentString is null', () => { - gdprConsent = { - gdprApplies: true, - consentString: null - }; - - const userSyncs = spec.getUserSyncs(syncOptions, [], gdprConsent); - expect(userSyncs).to.have.lengthOf(3); - expect(userSyncs[0].type).to.equal('image'); - expect(userSyncs[0].url).to.equal('https://ms-cookie-sync.presage.io/v1/init-sync/bid-switch?iab_string=&source=prebid') - expect(userSyncs[1].type).to.equal('image'); - expect(userSyncs[1].url).to.equal('https://ms-cookie-sync.presage.io/ttd/init-sync?iab_string=&source=prebid') - expect(userSyncs[2].type).to.equal('image'); - expect(userSyncs[2].url).to.equal('https://ms-cookie-sync.presage.io/xandr/init-sync?iab_string=&source=prebid') - }); - - it('should return syncs array with three elements of type image when gdprConsent is undefined', () => { - gdprConsent = undefined; - - const userSyncs = spec.getUserSyncs(syncOptions, [], gdprConsent); - expect(userSyncs).to.have.lengthOf(3); - expect(userSyncs[0].type).to.equal('image'); - expect(userSyncs[0].url).to.equal('https://ms-cookie-sync.presage.io/v1/init-sync/bid-switch?iab_string=&source=prebid') - expect(userSyncs[1].type).to.equal('image'); - expect(userSyncs[1].url).to.equal('https://ms-cookie-sync.presage.io/ttd/init-sync?iab_string=&source=prebid') - expect(userSyncs[2].type).to.equal('image'); - expect(userSyncs[2].url).to.equal('https://ms-cookie-sync.presage.io/xandr/init-sync?iab_string=&source=prebid') - }); - - it('should return syncs array with three elements of type image when gdprConsent is null', () => { - gdprConsent = null; - - const userSyncs = spec.getUserSyncs(syncOptions, [], gdprConsent); - expect(userSyncs).to.have.lengthOf(3); - expect(userSyncs[0].type).to.equal('image'); - expect(userSyncs[0].url).to.equal('https://ms-cookie-sync.presage.io/v1/init-sync/bid-switch?iab_string=&source=prebid') - expect(userSyncs[1].type).to.equal('image'); - expect(userSyncs[1].url).to.equal('https://ms-cookie-sync.presage.io/ttd/init-sync?iab_string=&source=prebid') - expect(userSyncs[2].type).to.equal('image'); - expect(userSyncs[2].url).to.equal('https://ms-cookie-sync.presage.io/xandr/init-sync?iab_string=&source=prebid') - }); - - it('should return syncs array with three elements of type image when gdprConsent is null and gdprApplies is false', () => { - gdprConsent = { - gdprApplies: false, - consentString: null - }; - - const userSyncs = spec.getUserSyncs(syncOptions, [], gdprConsent); - expect(userSyncs).to.have.lengthOf(3); - expect(userSyncs[0].type).to.equal('image'); - expect(userSyncs[0].url).to.equal('https://ms-cookie-sync.presage.io/v1/init-sync/bid-switch?iab_string=&source=prebid') - expect(userSyncs[1].type).to.equal('image'); - expect(userSyncs[1].url).to.equal('https://ms-cookie-sync.presage.io/ttd/init-sync?iab_string=&source=prebid') - expect(userSyncs[2].type).to.equal('image'); - expect(userSyncs[2].url).to.equal('https://ms-cookie-sync.presage.io/xandr/init-sync?iab_string=&source=prebid') - }); - - it('should return syncs array with three elements of type image when gdprConsent is empty string and gdprApplies is false', () => { - gdprConsent = { - gdprApplies: false, - consentString: '' - }; - - const userSyncs = spec.getUserSyncs(syncOptions, [], gdprConsent); - expect(userSyncs).to.have.lengthOf(3); - expect(userSyncs[0].type).to.equal('image'); - expect(userSyncs[0].url).to.equal('https://ms-cookie-sync.presage.io/v1/init-sync/bid-switch?iab_string=&source=prebid') - expect(userSyncs[1].type).to.equal('image'); - expect(userSyncs[1].url).to.equal('https://ms-cookie-sync.presage.io/ttd/init-sync?iab_string=&source=prebid') - expect(userSyncs[2].type).to.equal('image'); - expect(userSyncs[2].url).to.equal('https://ms-cookie-sync.presage.io/xandr/init-sync?iab_string=&source=prebid') - }); + expect(userSyncs).to.have.lengthOf(3); + expect(userSyncs[0].type).to.equal('image'); + expect(userSyncs[0].url).to.contain('https://ms-cookie-sync.presage.io/v1/init-sync/bid-switch'); + expect(userSyncs[1].type).to.equal('image'); + expect(userSyncs[1].url).to.contain('https://ms-cookie-sync.presage.io/ttd/init-sync'); + expect(userSyncs[2].type).to.equal('image'); + expect(userSyncs[2].url).to.contain('https://ms-cookie-sync.presage.io/xandr/init-sync'); }); - describe('iframe', () => { - beforeEach(() => { - syncOptions = { iframeEnabled: true }; - }); - - it('should return syncs array with one element of type iframe', () => { - const userSyncs = spec.getUserSyncs(syncOptions, [], gdprConsent); - - expect(userSyncs).to.have.lengthOf(1); - expect(userSyncs[0].type).to.equal('iframe'); - expect(userSyncs[0].url).to.contain('https://ms-cookie-sync.presage.io/user-sync.html'); - }); - - it('should set the source as query param', () => { - const userSyncs = spec.getUserSyncs(syncOptions, [], gdprConsent); - expect(userSyncs[0].url).to.contain('source=prebid'); - }); - - it('should set the tcString as query param', () => { - const userSyncs = spec.getUserSyncs(syncOptions, [], gdprConsent); - expect(userSyncs[0].url).to.contain(`gdpr_consent=${gdprConsent.consentString}`); - }); - - it('should return an empty array when iframe is disable', () => { - syncOptions.iframeEnabled = false; - expect(spec.getUserSyncs(syncOptions, [], gdprConsent)).to.have.lengthOf(0); - }); + it('should set the source as query param', () => { + const userSyncs = spec.getUserSyncs(syncOptions, [], gdprConsent); + expect(userSyncs[0].url).to.contain('source=prebid'); + expect(userSyncs[1].url).to.contain('source=prebid'); + expect(userSyncs[2].url).to.contain('source=prebid'); + }); - it('should return syncs array with one element of type iframe when consentString is undefined', () => { - gdprConsent = { - gdprApplies: true, - consentString: undefined - }; + it('should set the tcString as query param', () => { + const userSyncs = spec.getUserSyncs(syncOptions, [], gdprConsent); + expect(userSyncs[0].url).to.contain(`iab_string=${gdprConsent.consentString}`); + expect(userSyncs[1].url).to.contain(`iab_string=${gdprConsent.consentString}`); + expect(userSyncs[2].url).to.contain(`iab_string=${gdprConsent.consentString}`); + }); - const userSyncs = spec.getUserSyncs(syncOptions, [], gdprConsent); - expect(userSyncs).to.have.lengthOf(1); - expect(userSyncs[0].type).to.equal('iframe'); - expect(userSyncs[0].url).to.equal('https://ms-cookie-sync.presage.io/user-sync.html?gdpr_consent=&source=prebid') - }); + it('should return an empty array when pixel is disable', () => { + syncOptions.pixelEnabled = false; + expect(spec.getUserSyncs(syncOptions, [], gdprConsent)).to.have.lengthOf(0); + }); - it('should return syncs array with one element of type iframe when consentString is null', () => { - gdprConsent = { - gdprApplies: true, - consentString: null - }; + it('should return syncs array with three elements of type image when consentString is undefined', () => { + gdprConsent = { + gdprApplies: true, + consentString: undefined + }; - const userSyncs = spec.getUserSyncs(syncOptions, [], gdprConsent); - expect(userSyncs).to.have.lengthOf(1); - expect(userSyncs[0].type).to.equal('iframe'); - expect(userSyncs[0].url).to.equal('https://ms-cookie-sync.presage.io/user-sync.html?gdpr_consent=&source=prebid') - }); + const userSyncs = spec.getUserSyncs(syncOptions, [], gdprConsent); + expect(userSyncs).to.have.lengthOf(3); + expect(userSyncs[0].type).to.equal('image'); + expect(userSyncs[0].url).to.equal('https://ms-cookie-sync.presage.io/v1/init-sync/bid-switch?iab_string=&source=prebid') + expect(userSyncs[1].type).to.equal('image'); + expect(userSyncs[1].url).to.equal('https://ms-cookie-sync.presage.io/ttd/init-sync?iab_string=&source=prebid') + expect(userSyncs[2].type).to.equal('image'); + expect(userSyncs[2].url).to.equal('https://ms-cookie-sync.presage.io/xandr/init-sync?iab_string=&source=prebid') + }); - it('should return syncs array with one element of type iframe when gdprConsent is undefined', () => { - gdprConsent = undefined; + it('should return syncs array with three elements of type image when consentString is null', () => { + gdprConsent = { + gdprApplies: true, + consentString: null + }; - const userSyncs = spec.getUserSyncs(syncOptions, [], gdprConsent); - expect(userSyncs).to.have.lengthOf(1); - expect(userSyncs[0].type).to.equal('iframe'); - expect(userSyncs[0].url).to.equal('https://ms-cookie-sync.presage.io/user-sync.html?gdpr_consent=&source=prebid') - }); + const userSyncs = spec.getUserSyncs(syncOptions, [], gdprConsent); + expect(userSyncs).to.have.lengthOf(3); + expect(userSyncs[0].type).to.equal('image'); + expect(userSyncs[0].url).to.equal('https://ms-cookie-sync.presage.io/v1/init-sync/bid-switch?iab_string=&source=prebid') + expect(userSyncs[1].type).to.equal('image'); + expect(userSyncs[1].url).to.equal('https://ms-cookie-sync.presage.io/ttd/init-sync?iab_string=&source=prebid') + expect(userSyncs[2].type).to.equal('image'); + expect(userSyncs[2].url).to.equal('https://ms-cookie-sync.presage.io/xandr/init-sync?iab_string=&source=prebid') + }); - it('should return syncs array with one element of type iframe when gdprConsent is null', () => { - gdprConsent = null; + it('should return syncs array with three elements of type image when gdprConsent is undefined', () => { + gdprConsent = undefined; + + const userSyncs = spec.getUserSyncs(syncOptions, [], gdprConsent); + expect(userSyncs).to.have.lengthOf(3); + expect(userSyncs[0].type).to.equal('image'); + expect(userSyncs[0].url).to.equal('https://ms-cookie-sync.presage.io/v1/init-sync/bid-switch?iab_string=&source=prebid') + expect(userSyncs[1].type).to.equal('image'); + expect(userSyncs[1].url).to.equal('https://ms-cookie-sync.presage.io/ttd/init-sync?iab_string=&source=prebid') + expect(userSyncs[2].type).to.equal('image'); + expect(userSyncs[2].url).to.equal('https://ms-cookie-sync.presage.io/xandr/init-sync?iab_string=&source=prebid') + }); - const userSyncs = spec.getUserSyncs(syncOptions, [], gdprConsent); - expect(userSyncs).to.have.lengthOf(1); - expect(userSyncs[0].type).to.equal('iframe'); - expect(userSyncs[0].url).to.equal('https://ms-cookie-sync.presage.io/user-sync.html?gdpr_consent=&source=prebid') - }); + it('should return syncs array with three elements of type image when gdprConsent is null', () => { + gdprConsent = null; + + const userSyncs = spec.getUserSyncs(syncOptions, [], gdprConsent); + expect(userSyncs).to.have.lengthOf(3); + expect(userSyncs[0].type).to.equal('image'); + expect(userSyncs[0].url).to.equal('https://ms-cookie-sync.presage.io/v1/init-sync/bid-switch?iab_string=&source=prebid') + expect(userSyncs[1].type).to.equal('image'); + expect(userSyncs[1].url).to.equal('https://ms-cookie-sync.presage.io/ttd/init-sync?iab_string=&source=prebid') + expect(userSyncs[2].type).to.equal('image'); + expect(userSyncs[2].url).to.equal('https://ms-cookie-sync.presage.io/xandr/init-sync?iab_string=&source=prebid') + }); - it('should return syncs array with one element of type iframe when gdprConsent is null and gdprApplies is false', () => { - gdprConsent = { - gdprApplies: false, - consentString: null - }; + it('should return syncs array with three elements of type image when gdprConsent is null and gdprApplies is false', () => { + gdprConsent = { + gdprApplies: false, + consentString: null + }; - const userSyncs = spec.getUserSyncs(syncOptions, [], gdprConsent); - expect(userSyncs).to.have.lengthOf(1); - expect(userSyncs[0].type).to.equal('iframe'); - expect(userSyncs[0].url).to.equal('https://ms-cookie-sync.presage.io/user-sync.html?gdpr_consent=&source=prebid') - }); + const userSyncs = spec.getUserSyncs(syncOptions, [], gdprConsent); + expect(userSyncs).to.have.lengthOf(3); + expect(userSyncs[0].type).to.equal('image'); + expect(userSyncs[0].url).to.equal('https://ms-cookie-sync.presage.io/v1/init-sync/bid-switch?iab_string=&source=prebid') + expect(userSyncs[1].type).to.equal('image'); + expect(userSyncs[1].url).to.equal('https://ms-cookie-sync.presage.io/ttd/init-sync?iab_string=&source=prebid') + expect(userSyncs[2].type).to.equal('image'); + expect(userSyncs[2].url).to.equal('https://ms-cookie-sync.presage.io/xandr/init-sync?iab_string=&source=prebid') + }); - it('should return syncs array with one element of type iframe when gdprConsent is empty string and gdprApplies is false', () => { - gdprConsent = { - gdprApplies: false, - consentString: '' - }; + it('should return syncs array with three elements of type image when gdprConsent is empty string and gdprApplies is false', () => { + gdprConsent = { + gdprApplies: false, + consentString: '' + }; - const userSyncs = spec.getUserSyncs(syncOptions, [], gdprConsent); - expect(userSyncs).to.have.lengthOf(1); - expect(userSyncs[0].type).to.equal('iframe'); - expect(userSyncs[0].url).to.equal('https://ms-cookie-sync.presage.io/user-sync.html?gdpr_consent=&source=prebid') - }); + const userSyncs = spec.getUserSyncs(syncOptions, [], gdprConsent); + expect(userSyncs).to.have.lengthOf(3); + expect(userSyncs[0].type).to.equal('image'); + expect(userSyncs[0].url).to.equal('https://ms-cookie-sync.presage.io/v1/init-sync/bid-switch?iab_string=&source=prebid') + expect(userSyncs[1].type).to.equal('image'); + expect(userSyncs[1].url).to.equal('https://ms-cookie-sync.presage.io/ttd/init-sync?iab_string=&source=prebid') + expect(userSyncs[2].type).to.equal('image'); + expect(userSyncs[2].url).to.equal('https://ms-cookie-sync.presage.io/xandr/init-sync?iab_string=&source=prebid') }); }); @@ -421,26 +306,12 @@ describe('OguryBidAdapter', function () { }, user: { ext: { - consent: bidderRequest.gdprConsent.consentString, - uids: { - pubcid: '2abb10e5-c4f6-4f70-9f45-2200e4487714' - }, - eids: [ - { - source: 'pubcid.org', - uids: [ - { - id: '2abb10e5-c4f6-4f70-9f45-2200e4487714', - atype: 1 - } - ] - } - ], + consent: bidderRequest.gdprConsent.consentString }, }, ext: { prebidversion: '$prebid.version$', - adapterversion: '1.6.0' + adapterversion: '1.4.1' }, device: { w: stubbedWidth, @@ -665,9 +536,7 @@ describe('OguryBidAdapter', function () { }, user: { ext: { - consent: '', - uids: expectedRequestObject.user.ext.uids, - eids: expectedRequestObject.user.ext.eids + consent: '' }, } }; @@ -693,9 +562,7 @@ describe('OguryBidAdapter', function () { }, user: { ext: { - consent: '', - uids: expectedRequestObject.user.ext.uids, - eids: expectedRequestObject.user.ext.eids + consent: '' }, } }; @@ -721,9 +588,7 @@ describe('OguryBidAdapter', function () { }, user: { ext: { - consent: '', - uids: expectedRequestObject.user.ext.uids, - eids: expectedRequestObject.user.ext.eids + consent: '' }, } }; @@ -735,48 +600,6 @@ describe('OguryBidAdapter', function () { expect(request.data.regs.ext.gdpr).to.be.a('number'); }); - it('should should not add uids infos if userId is undefined', () => { - const expectedRequestWithUndefinedUserId = { - ...expectedRequestObject, - user: { - ext: { - consent: expectedRequestObject.user.ext.consent, - eids: expectedRequestObject.user.ext.eids - } - } - }; - - const validBidRequests = utils.deepClone(bidRequests); - validBidRequests[0] = { - ...validBidRequests[0], - userId: undefined - }; - - const request = spec.buildRequests(validBidRequests, bidderRequest); - expect(request.data).to.deep.equal(expectedRequestWithUndefinedUserId); - }); - - it('should should not add uids infos if userIdAsEids is undefined', () => { - const expectedRequestWithUndefinedUserIdAsEids = { - ...expectedRequestObject, - user: { - ext: { - consent: expectedRequestObject.user.ext.consent, - uids: expectedRequestObject.user.ext.uids - } - } - }; - - const validBidRequests = utils.deepClone(bidRequests); - validBidRequests[0] = { - ...validBidRequests[0], - userIdAsEids: undefined - }; - - const request = spec.buildRequests(validBidRequests, bidderRequest); - expect(request.data).to.deep.equal(expectedRequestWithUndefinedUserIdAsEids); - }); - it('should handle bidFloor undefined', () => { const expectedRequestWithUndefinedFloor = { ...expectedRequestObject @@ -890,7 +713,7 @@ describe('OguryBidAdapter', function () { advertiserDomains: openRtbBidResponse.body.seatbid[0].bid[0].adomain }, nurl: openRtbBidResponse.body.seatbid[0].bid[0].nurl, - adapterVersion: '1.6.0', + adapterVersion: '1.4.1', prebidVersion: '$prebid.version$' }, { requestId: openRtbBidResponse.body.seatbid[0].bid[1].impid, @@ -907,7 +730,7 @@ describe('OguryBidAdapter', function () { advertiserDomains: openRtbBidResponse.body.seatbid[0].bid[1].adomain }, nurl: openRtbBidResponse.body.seatbid[0].bid[1].nurl, - adapterVersion: '1.6.0', + adapterVersion: '1.4.1', prebidVersion: '$prebid.version$' }] @@ -928,11 +751,20 @@ describe('OguryBidAdapter', function () { }); describe('onBidWon', function() { - const nurl = 'https://fakewinurl.test/'; + const nurl = 'https://fakewinurl.test'; + let xhr; let requests; beforeEach(function() { - requests = server.requests; + xhr = sinon.useFakeXMLHttpRequest(); + requests = []; + xhr.onCreate = (xhr) => { + requests.push(xhr); + }; + }) + + afterEach(function() { + xhr.restore() }) it('Should not create nurl request if bid is undefined', function() { @@ -1000,15 +832,21 @@ describe('OguryBidAdapter', function () { }) describe('onTimeout', function () { + let xhr; let requests; beforeEach(function() { - requests = server.requests; - server.onCreate = (xhr) => { + xhr = sinon.useFakeXMLHttpRequest(); + requests = []; + xhr.onCreate = (xhr) => { requests.push(xhr); }; }) + afterEach(function() { + xhr.restore() + }) + it('should send on bid timeout notification', function() { const bid = { ad: 'cookies', diff --git a/test/spec/modules/onetagBidAdapter_spec.js b/test/spec/modules/onetagBidAdapter_spec.js index 7960574c1a4..701fee5f6d9 100644 --- a/test/spec/modules/onetagBidAdapter_spec.js +++ b/test/spec/modules/onetagBidAdapter_spec.js @@ -15,14 +15,9 @@ describe('onetag', function () { 'bidId': '30b31c1838de1e', 'bidderRequestId': '22edbae2733bf6', 'auctionId': '1d1a030790a475', - 'ortb2Imp': { - 'ext': { - 'tid': '0000' - } - }, - 'ortb2': { - 'source': { - 'tid': '1111' + ortb2Imp: { + ext: { + tid: 'qwerty123' } }, 'schain': { @@ -77,12 +72,9 @@ describe('onetag', function () { return createInstreamVideoBid(createBannerBid()); } - let bannerBid, instreamVideoBid, outstreamVideoBid; - beforeEach(() => { - bannerBid = createBannerBid(); - instreamVideoBid = createInstreamVideoBid(); - outstreamVideoBid = createOutstreamVideoBid(); - }) + const bannerBid = createBannerBid(); + const instreamVideoBid = createInstreamVideoBid(); + const outstreamVideoBid = createOutstreamVideoBid(); describe('isBidRequestValid', function () { it('Should return true when required params are found', function () { @@ -98,11 +90,8 @@ describe('onetag', function () { }); describe('banner bidRequest', function () { it('Should return false when the sizes array is empty', function () { - // TODO (dgirardi): this test used to pass because `bannerBid` was global state - // and other test code made it invalid for reasons other than sizes. - // cleaning up the setup code, it now (correctly) fails. bannerBid.sizes = []; - // expect(spec.isBidRequestValid(bannerBid)).to.be.false; + expect(spec.isBidRequestValid(bannerBid)).to.be.false; }); }); describe('video bidRequest', function () { @@ -169,12 +158,7 @@ describe('onetag', function () { }); describe('buildRequests', function () { - let serverRequest, data; - before(() => { - serverRequest = spec.buildRequests([bannerBid, instreamVideoBid]); - data = JSON.parse(serverRequest.data); - }); - + let serverRequest = spec.buildRequests([bannerBid, instreamVideoBid]); it('Creates a ServerRequest object with method, URL and data', function () { expect(serverRequest).to.exist; expect(serverRequest.method).to.exist; @@ -187,73 +171,77 @@ describe('onetag', function () { it('Returns valid URL', function () { expect(serverRequest.url).to.equal('https://onetag-sys.com/prebid-request'); }); - it('Should contain all keys', function () { - expect(data).to.be.an('object'); - expect(data).to.include.all.keys('location', 'referrer', 'stack', 'numIframes', 'sHeight', 'sWidth', 'docHeight', 'wHeight', 'wWidth', 'oHeight', 'oWidth', 'aWidth', 'aHeight', 'sLeft', 'sTop', 'hLength', 'bids', 'docHidden', 'xOffset', 'yOffset', 'networkConnectionType', 'networkEffectiveConnectionType', 'timing', 'version', 'fledgeEnabled'); - expect(data.location).to.satisfy(function (value) { - return value === null || typeof value === 'string'; - }); - expect(data.referrer).to.satisfy(referrer => referrer === null || typeof referrer === 'string'); - expect(data.stack).to.be.an('array'); - expect(data.numIframes).to.be.a('number'); - expect(data.sHeight).to.be.a('number'); - expect(data.sWidth).to.be.a('number'); - expect(data.wWidth).to.be.a('number'); - expect(data.wHeight).to.be.a('number'); - expect(data.oHeight).to.be.a('number'); - expect(data.oWidth).to.be.a('number'); - expect(data.aWidth).to.be.a('number'); - expect(data.aHeight).to.be.a('number'); - expect(data.sLeft).to.be.a('number'); - expect(data.sTop).to.be.a('number'); - expect(data.hLength).to.be.a('number'); - expect(data.networkConnectionType).to.satisfy(function (value) { - return value === null || typeof value === 'string' - }); - expect(data.networkEffectiveConnectionType).to.satisfy(function (value) { - return value === null || typeof value === 'string' - }); - expect(data.fledgeEnabled).to.be.a('boolean'); - expect(data.bids).to.be.an('array'); - expect(data.version).to.have.all.keys('prebid', 'adapter'); - const bids = data['bids']; - for (let i = 0; i < bids.length; i++) { - const bid = bids[i]; - if (hasTypeVideo(bid)) { - expect(bid).to.have.all.keys( - 'adUnitCode', - 'auctionId', - 'bidId', - 'bidderRequestId', - 'pubId', - 'transactionId', - 'context', - 'playerSize', - 'mediaTypeInfo', - 'type', - 'priceFloors' - ); - } else if (isValid(BANNER, bid)) { - expect(bid).to.have.all.keys( - 'adUnitCode', - 'auctionId', - 'bidId', - 'bidderRequestId', - 'pubId', - 'transactionId', - 'mediaTypeInfo', - 'sizes', - 'type', - 'priceFloors' - ); - } - if (bid.schain && isSchainValid(bid.schain)) { - expect(data).to.have.all.keys('schain'); + + const d = serverRequest.data; + try { + const data = JSON.parse(d); + it('Should contain all keys', function () { + expect(data).to.be.an('object'); + expect(data).to.include.all.keys('location', 'referrer', 'stack', 'numIframes', 'sHeight', 'sWidth', 'docHeight', 'wHeight', 'wWidth', 'oHeight', 'oWidth', 'aWidth', 'aHeight', 'sLeft', 'sTop', 'hLength', 'bids', 'docHidden', 'xOffset', 'yOffset', 'networkConnectionType', 'networkEffectiveConnectionType', 'timing', 'version'); + expect(data.location).to.satisfy(function (value) { + return value === null || typeof value === 'string'; + }); + expect(data.referrer).to.satisfy(referrer => referrer === null || typeof referrer === 'string'); + expect(data.stack).to.be.an('array'); + expect(data.numIframes).to.be.a('number'); + expect(data.sHeight).to.be.a('number'); + expect(data.sWidth).to.be.a('number'); + expect(data.wWidth).to.be.a('number'); + expect(data.wHeight).to.be.a('number'); + expect(data.oHeight).to.be.a('number'); + expect(data.oWidth).to.be.a('number'); + expect(data.aWidth).to.be.a('number'); + expect(data.aHeight).to.be.a('number'); + expect(data.sLeft).to.be.a('number'); + expect(data.sTop).to.be.a('number'); + expect(data.hLength).to.be.a('number'); + expect(data.networkConnectionType).to.satisfy(function (value) { + return value === null || typeof value === 'string' + }); + expect(data.networkEffectiveConnectionType).to.satisfy(function (value) { + return value === null || typeof value === 'string' + }); + expect(data.bids).to.be.an('array'); + expect(data.version).to.have.all.keys('prebid', 'adapter'); + const bids = data['bids']; + for (let i = 0; i < bids.length; i++) { + const bid = bids[i]; + if (hasTypeVideo(bid)) { + expect(bid).to.have.all.keys( + 'adUnitCode', + 'auctionId', + 'bidId', + 'bidderRequestId', + 'pubId', + 'transactionId', + 'context', + 'playerSize', + 'mediaTypeInfo', + 'type', + 'priceFloors' + ); + } else if (isValid(BANNER, bid)) { + expect(bid).to.have.all.keys( + 'adUnitCode', + 'auctionId', + 'bidId', + 'bidderRequestId', + 'pubId', + 'transactionId', + 'mediaTypeInfo', + 'sizes', + 'type', + 'priceFloors' + ); + } + if (bid.schain && isSchainValid(bid.schain)) { + expect(data).to.have.all.keys('schain'); + } + expect(bid.bidId).to.be.a('string'); + expect(bid.pubId).to.be.a('string'); } - expect(bid.bidId).to.be.a('string'); - expect(bid.pubId).to.be.a('string'); - } - }); + }); + } catch (e) { } it('Returns empty data if no valid requests are passed', function () { serverRequest = spec.buildRequests([]); let dataString = serverRequest.data; @@ -262,15 +250,6 @@ describe('onetag', function () { expect(dataObj.bids).to.be.an('array').that.is.empty; } catch (e) { } }); - it('Should pick each bid\'s auctionId and transactionId from ortb2 related fields', function () { - const serverRequest = spec.buildRequests([bannerBid]); - const payload = JSON.parse(serverRequest.data); - - expect(payload).to.exist; - expect(payload.bids).to.exist.and.to.have.length(1); - expect(payload.bids[0].auctionId).to.equal(bannerBid.ortb2.source.tid); - expect(payload.bids[0].transactionId).to.equal(bannerBid.ortb2Imp.ext.tid); - }); it('should send GDPR consent data', function () { let consentString = 'consentString'; let bidderRequest = { @@ -327,131 +306,14 @@ describe('onetag', function () { expect(payload.usPrivacy).to.exist; expect(payload.usPrivacy).to.exist.and.to.equal(consentString); }); - it('Should send FPD (ortb2 field)', function () { - const firtPartyData = { - // this is where the contextual data is placed - site: { - name: 'example', - domain: 'page.example.com', - // OpenRTB 2.5 spec / Content Taxonomy - cat: ['IAB2'], - sectioncat: ['IAB2-2'], - pagecat: ['IAB2-2'], - page: 'https://page.example.com/here.html', - ref: 'https://ref.example.com', - keywords: 'power tools, drills', - search: 'drill', - content: { - userrating: '4', - data: [{ - name: 'www.dataprovider1.com', // who resolved the segments - ext: { - segtax: 7, // taxonomy used to encode the segments - 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' } ] - }] - }, - ext: { - data: { // fields that aren't part of openrtb 2.6 - pageType: 'article', - category: 'repair' - } - } - }, - // this is where the user data is placed - user: { - keywords: 'a,b', - data: [{ - name: 'dataprovider.com', - ext: { - segtax: 4 - }, - segment: [{ - id: '1' - }] - }], - ext: { - data: { - registered: true, - interests: ['cars'] - } - } - }, - regs: { - gpp: 'abc1234', - gpp_sid: [7] - } - }; - let bidderRequest = { - 'bidderCode': 'onetag', - 'auctionId': '1d1a030790a475', - 'bidderRequestId': '22edbae2733bf6', - 'timeout': 3000, - 'ortb2': firtPartyData - } - let serverRequest = spec.buildRequests([bannerBid], bidderRequest); - const payload = JSON.parse(serverRequest.data); - expect(payload.ortb2).to.exist; - expect(payload.ortb2).to.exist.and.to.deep.equal(firtPartyData); - }); - }); - it('Should send FLEDGE eligibility flag when FLEDGE is enabled', function () { - let bidderRequest = { - 'bidderCode': 'onetag', - 'auctionId': '1d1a030790a475', - 'bidderRequestId': '22edbae2733bf6', - 'timeout': 3000, - 'fledgeEnabled': true - }; - let 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.fledgeEnabled); - }); - it('Should send FLEDGE eligibility flag when FLEDGE is not enabled', function () { - let bidderRequest = { - 'bidderCode': 'onetag', - 'auctionId': '1d1a030790a475', - 'bidderRequestId': '22edbae2733bf6', - 'timeout': 3000, - 'fledgeEnabled': false - }; - let 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.fledgeEnabled); - }); - it('Should send FLEDGE eligibility flag set to false when fledgeEnabled is not defined', function () { - let bidderRequest = { - 'bidderCode': 'onetag', - 'auctionId': '1d1a030790a475', - 'bidderRequestId': '22edbae2733bf6', - 'timeout': 3000, - }; - let serverRequest = spec.buildRequests([bannerBid], bidderRequest); - const payload = JSON.parse(serverRequest.data); - - expect(payload.fledgeEnabled).to.exist; - expect(payload.fledgeEnabled).to.exist.and.to.equal(false); }); describe('interpretResponse', function () { const request = getBannerVideoRequest(); const response = getBannerVideoResponse(); - const fledgeResponse = getFledgeBannerResponse(); 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.fledgeAuctionConfigs).to.be.an('array').that.is.not.empty; for (let i = 0; i < interpretedResponse.length; i++) { let dataItem = interpretedResponse[i]; expect(dataItem).to.include.all.keys('requestId', 'cpm', 'width', 'height', 'ttl', 'creativeId', 'netRevenue', 'currency', 'meta', 'dealId'); @@ -649,24 +511,6 @@ function getBannerVideoResponse() { }; } -function getFledgeBannerResponse() { - const bannerVideoResponse = getBannerVideoResponse(); - bannerVideoResponse.body.fledgeAuctionConfigs = [ - { - bidId: 'fledge', - config: { - seller: 'https://onetag-sys.com', - decisionLogicUrl: - 'https://onetag-sys.com/paapi/decision_logic.js', - interestGroupBuyers: [ - 'https://onetag-sys.com' - ], - } - } - ] - return bannerVideoResponse; -} - function getBannerVideoRequest() { return { data: JSON.stringify({ diff --git a/test/spec/modules/ooloAnalyticsAdapter_spec.js b/test/spec/modules/ooloAnalyticsAdapter_spec.js index 1224c3f0740..2515c713b14 100644 --- a/test/spec/modules/ooloAnalyticsAdapter_spec.js +++ b/test/spec/modules/ooloAnalyticsAdapter_spec.js @@ -663,7 +663,7 @@ describe('oolo Prebid Analytic', () => { events.emit(constants.EVENTS.AUCTION_INIT, { ...auctionInit }); - expect(server.requests[3].url).to.equal('https://pbjs.com/') + expect(server.requests[3].url).to.equal('https://pbjs.com') }) it('should send raw events based on server configuration', () => { diff --git a/test/spec/modules/openxBidAdapter_spec.js b/test/spec/modules/openxBidAdapter_spec.js index 1af0fce103d..f2cff7f470c 100644 --- a/test/spec/modules/openxBidAdapter_spec.js +++ b/test/spec/modules/openxBidAdapter_spec.js @@ -14,7 +14,6 @@ import 'modules/consentManagement.js'; import 'modules/consentManagementUsp.js'; import 'modules/schain.js'; import {deepClone} from 'src/utils.js'; -import {version} from 'package.json'; import {syncAddFPDToBidderRequest} from '../../helpers/fpd.js'; import {hook} from '../../../src/hook.js'; @@ -317,7 +316,6 @@ describe('OpenxRtbAdapter', function () { const request = spec.buildRequests(bidRequestsWithMediaTypes, mockBidderRequest); expect(request[0].url).to.equal(REQUEST_URL); expect(request[0].method).to.equal('POST'); - expect(request[0].data.ext.pv).to.equal(version); }); it('should send delivery domain, if available', function () { diff --git a/test/spec/modules/operaadsBidAdapter_spec.js b/test/spec/modules/operaadsBidAdapter_spec.js index 9a8981235d5..37d4a2c7bc0 100644 --- a/test/spec/modules/operaadsBidAdapter_spec.js +++ b/test/spec/modules/operaadsBidAdapter_spec.js @@ -266,95 +266,6 @@ describe('Opera Ads Bid Adapter', function () { } }); - describe('test fulfilling inventory information', function () { - const bidRequest = { - adUnitCode: 'test-div', - auctionId: 'b06c5141-fe8f-4cdf-9d7d-54415490a917', - bidId: '22c4871113f461', - bidder: 'operaads', - bidderRequestId: '15246a574e859f', - mediaTypes: { - banner: {sizes: [[300, 250]]} - }, - params: { - placementId: 's12345678', - publisherId: 'pub12345678', - endpointId: 'ep12345678' - } - } - - const getRequest = function () { - let reqs; - expect(function () { - reqs = spec.buildRequests([bidRequest], bidderRequest); - }).to.not.throw(); - return JSON.parse(reqs[0].data); - } - - it('test default case', function () { - let requestData = getRequest(); - expect(requestData.site).to.be.an('object'); - expect(requestData.site.id).to.equal(bidRequest.params.publisherId); - expect(requestData.site.domain).to.not.be.empty; - expect(requestData.site.page).to.equal(bidderRequest.refererInfo.page); - }); - - it('test a case with site information specified', function () { - bidRequest.params = { - placementId: 's12345678', - publisherId: 'pub12345678', - endpointId: 'ep12345678', - site: { - name: 'test-site-1', - domain: 'www.test.com' - } - } - let requestData = getRequest(); - expect(requestData.site).to.be.an('object'); - expect(requestData.site.id).to.equal(bidRequest.params.publisherId); - expect(requestData.site.name).to.equal('test-site-1'); - expect(requestData.site.domain).to.equal('www.test.com'); - expect(requestData.site.page).to.equal(bidderRequest.refererInfo.page); - }); - - it('test a case with app information specified', function () { - bidRequest.params = { - placementId: 's12345678', - publisherId: 'pub12345678', - endpointId: 'ep12345678', - app: { - name: 'test-app-1' - } - } - let requestData = getRequest(); - expect(requestData.app).to.be.an('object'); - expect(requestData.app.id).to.equal(bidRequest.params.publisherId); - expect(requestData.app.name).to.equal('test-app-1'); - expect(requestData.app.domain).to.not.be.empty; - }); - - it('test a case with both site and app information specified', function () { - bidRequest.params = { - placementId: 's12345678', - publisherId: 'pub12345678', - endpointId: 'ep12345678', - site: { - name: 'test-site-2', - page: 'test-page' - }, - app: { - name: 'test-app-1' - } - } - let requestData = getRequest(); - expect(requestData.site).to.be.an('object'); - expect(requestData.site.id).to.equal(bidRequest.params.publisherId); - expect(requestData.site.name).to.equal('test-site-2'); - expect(requestData.site.page).to.equal('test-page'); - expect(requestData.site.domain).to.not.be.empty; - }); - }); - it('test getBidFloor', function() { const bidRequests = [ { diff --git a/test/spec/modules/operaadsIdSystem_spec.js b/test/spec/modules/operaadsIdSystem_spec.js deleted file mode 100644 index d81f643d62f..00000000000 --- a/test/spec/modules/operaadsIdSystem_spec.js +++ /dev/null @@ -1,53 +0,0 @@ -import { operaIdSubmodule } from 'modules/operaadsIdSystem' -import * as ajaxLib from 'src/ajax.js' - -const TEST_ID = 'opera-test-id'; -const operaIdRemoteResponse = { uid: TEST_ID }; - -describe('operaId submodule properties', () => { - it('should expose a "name" property equal to "operaId"', () => { - expect(operaIdSubmodule.name).to.equal('operaId'); - }); -}); - -function fakeRequest(fn) { - const ajaxBuilderStub = sinon.stub(ajaxLib, 'ajaxBuilder').callsFake(() => { - return (url, cbObj) => { - cbObj.success(JSON.stringify(operaIdRemoteResponse)); - } - }); - fn(); - ajaxBuilderStub.restore(); -} - -describe('operaId submodule getId', function() { - it('request to the fake server to correctly extract test ID', function() { - fakeRequest(() => { - const moduleIdCallbackResponse = operaIdSubmodule.getId({ params: { pid: 'pub123' } }); - moduleIdCallbackResponse.callback((id) => { - expect(id).to.equal(operaIdRemoteResponse.operaId); - }); - }); - }); - - it('request to the fake server without publiser ID', function() { - fakeRequest(() => { - const moduleIdCallbackResponse = operaIdSubmodule.getId({ params: {} }); - expect(moduleIdCallbackResponse).to.equal(undefined); - }); - }); -}); - -describe('operaId submodule decode', function() { - it('should respond with an object containing "operaId" as key with the value', () => { - expect(operaIdSubmodule.decode(TEST_ID)).to.deep.equal({ - operaId: TEST_ID - }); - }); - - it('should respond with undefined if the value is not a string or an empty string', () => { - [1, 2.0, null, undefined, NaN, [], {}].forEach((value) => { - expect(operaIdSubmodule.decode(value)).to.equal(undefined); - }); - }); -}); diff --git a/test/spec/modules/optidigitalBidAdapter_spec.js b/test/spec/modules/optidigitalBidAdapter_spec.js index 30e72452c39..caa12483ea9 100755 --- a/test/spec/modules/optidigitalBidAdapter_spec.js +++ b/test/spec/modules/optidigitalBidAdapter_spec.js @@ -1,5 +1,5 @@ import { expect } from 'chai'; -import { spec, resetSync } from 'modules/optidigitalBidAdapter.js'; +import { spec } from 'modules/optidigitalBidAdapter.js'; import * as utils from 'src/utils.js'; const ENDPOINT = 'https://pbs.optidigital.com/bidder'; @@ -479,28 +479,6 @@ describe('optidigitalAdapterTests', function () { expect(payload.imp[0].bidFloor).to.exist; }); - it('should add userEids to payload', function() { - const userIdAsEids = [{ - source: 'pubcid.org', - uids: [{ - id: '121213434342343', - atype: 1 - }] - }]; - validBidRequests[0].userIdAsEids = userIdAsEids; - bidderRequest.userIdAsEids = userIdAsEids; - const request = spec.buildRequests(validBidRequests, bidderRequest); - const payload = JSON.parse(request.data); - expect(payload.user.eids).to.deep.equal(userIdAsEids); - }); - - it('should not add userIdAsEids to payload when userIdAsEids is not present', function() { - validBidRequests[0].userIdAsEids = undefined; - const request = spec.buildRequests(validBidRequests, bidderRequest); - const payload = JSON.parse(request.data); - expect(payload.user).to.deep.equal(undefined); - }); - function returnBannerSizes(mediaTypes, expectedSizes) { const bidRequest = Object.assign(validBidRequests[0], mediaTypes); const request = spec.buildRequests([bidRequest], bidderRequest); @@ -519,7 +497,6 @@ describe('optidigitalAdapterTests', function () { let test; beforeEach(function () { test = sinon.sandbox.create(); - resetSync(); }); afterEach(function() { test.restore(); @@ -531,22 +508,16 @@ describe('optidigitalAdapterTests', function () { }]); }); - it('should return appropriate URL with GDPR equals to 1 and GDPR consent', function() { + it('should return appropriate URL', function() { 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([{ 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([{ type: 'iframe', url: `${syncurlIframe}&gdpr=1&gdpr_consent=` }]); - }); - it('should return appropriate URL with GDPR equals to 1, GDPR consent and CCPA consent', function() { expect(spec.getUserSyncs({ iframeEnabled: true }, {}, {gdprApplies: true, consentString: 'foo'}, {consentString: 'fooUsp'})).to.deep.equal([{ type: 'iframe', url: `${syncurlIframe}&gdpr=1&gdpr_consent=foo&ccpa_consent=fooUsp` }]); diff --git a/test/spec/modules/optimeraRtdProvider_spec.js b/test/spec/modules/optimeraRtdProvider_spec.js index 8a9f000bbb9..aec8b79045e 100644 --- a/test/spec/modules/optimeraRtdProvider_spec.js +++ b/test/spec/modules/optimeraRtdProvider_spec.js @@ -21,80 +21,13 @@ describe('Optimera RTD sub module', () => { }); }); -describe('Optimera RTD score file URL is properly set for v0', () => { - it('should properly set the score file URL', () => { - const conf = { - dataProviders: [{ - name: 'optimeraRTD', - params: { - clientID: '9999', - optimeraKeyName: 'optimera', - device: 'de', - apiVersion: 'v0', - } - }] - }; - optimeraRTD.init(conf.dataProviders[0]); - optimeraRTD.setScores(); - expect(optimeraRTD.apiVersion).to.equal('v0'); - expect(optimeraRTD.scoresURL).to.equal('https://dyv1bugovvq1g.cloudfront.net/9999/localhost:9876/context.html.js'); - }); - - it('should properly set the score file URL without apiVersion set', () => { - const conf = { - dataProviders: [{ - name: 'optimeraRTD', - params: { - clientID: '9999', - optimeraKeyName: 'optimera', - device: 'de', - } - }] - }; - optimeraRTD.init(conf.dataProviders[0]); - optimeraRTD.setScores(); - expect(optimeraRTD.apiVersion).to.equal('v0'); - expect(optimeraRTD.scoresURL).to.equal('https://dyv1bugovvq1g.cloudfront.net/9999/localhost:9876/context.html.js'); - }); - - it('should properly set the score file URL with an api version other than v0 or v1', () => { - const conf = { - dataProviders: [{ - name: 'optimeraRTD', - params: { - clientID: '9999', - optimeraKeyName: 'optimera', - device: 'de', - apiVersion: 'v15', - } - }] - }; - optimeraRTD.init(conf.dataProviders[0]); +describe('Optimera RTD score file url is properly set', () => { + it('Proerly set the score file url', () => { optimeraRTD.setScores(); expect(optimeraRTD.scoresURL).to.equal('https://dyv1bugovvq1g.cloudfront.net/9999/localhost:9876/context.html.js'); }); }); -describe('Optimera RTD score file URL is properly set for v1', () => { - it('should properly set the score file URL', () => { - const conf = { - dataProviders: [{ - name: 'optimeraRTD', - params: { - clientID: '9999', - optimeraKeyName: 'optimera', - device: 'de', - apiVersion: 'v1', - } - }] - }; - optimeraRTD.init(conf.dataProviders[0]); - optimeraRTD.setScores(); - expect(optimeraRTD.apiVersion).to.equal('v1'); - expect(optimeraRTD.scoresURL).to.equal('https://v1.oapi26b.com/api/products/scores?c=9999&h=localhost:9876&p=/context.html&s=de'); - }); -}); - describe('Optimera RTD score file properly sets targeting values', () => { const scores = { 'div-0': ['A1', 'A2'], diff --git a/test/spec/modules/orbidderBidAdapter_spec.js b/test/spec/modules/orbidderBidAdapter_spec.js index cf58d35e636..e7a0c8becfb 100644 --- a/test/spec/modules/orbidderBidAdapter_spec.js +++ b/test/spec/modules/orbidderBidAdapter_spec.js @@ -1,6 +1,6 @@ -import { expect } from 'chai'; -import { spec } from 'modules/orbidderBidAdapter.js'; -import { newBidder } from 'src/adapters/bidderFactory.js'; +import {expect} from 'chai'; +import {spec} from 'modules/orbidderBidAdapter.js'; +import {newBidder} from 'src/adapters/bidderFactory.js'; import * as _ from 'lodash'; import { BANNER, NATIVE } from '../../../src/mediaTypes.js'; @@ -9,57 +9,39 @@ describe('orbidderBidAdapter', () => { const defaultBidRequestBanner = { bidId: 'd66fa86787e0b0ca900a96eacfd5f0bb', auctionId: 'ccc4c7cdfe11cfbd74065e6dd28413d8', - transactionId: 'd58851660c0c4461e4aa06344fc9c0c6', + ortb2Imp: { + ext: { + tid: 'd58851660c0c4461e4aa06344fc9c0c6', + } + }, bidRequestCount: 1, adUnitCode: 'adunit-code', sizes: [[300, 250], [300, 600]], params: { 'accountId': 'string1', - 'placementId': 'string2', - 'bidfloor': 1.23 + 'placementId': 'string2' }, mediaTypes: { banner: { - sizes: [[300, 250], [300, 600]] - } - }, - userId: { - 'id5id': { - 'uid': 'ID5*XXXXXXXXXXXXX', - 'ext': { - 'linkType': 2, - 'pba': 'XXXXXXXXXXXX==' - } + sizes: [[300, 250], [300, 600]], } - }, - userIdAsEids: [ - { - 'source': 'id5-sync.com', - 'uids': [ - { - 'id': 'ID5*XXXXXXXXXXXXX', - 'atype': 1, - 'ext': { - 'linkType': 2, - 'pba': 'XXXXXXXXXXXX==' - } - } - ] - } - ] + } }; const defaultBidRequestNative = { bidId: 'd66fa86787e0b0ca900a96eacfd5f0bc', auctionId: 'ccc4c7cdfe11cfbd74065e6dd28413d9', - transactionId: 'd58851660c0c4461e4aa06344fc9c0c6', + ortb2Imp: { + ext: { + tid: 'd58851660c0c4461e4aa06344fc9c0c7', + } + }, bidRequestCount: 1, adUnitCode: 'adunit-code-native', sizes: [], params: { 'accountId': 'string3', - 'placementId': 'string4', - 'bidfloor': 2.34 + 'placementId': 'string4' }, mediaTypes: { native: { @@ -74,34 +56,10 @@ describe('orbidderBidAdapter', () => { required: true } } - }, - userId: { - 'id5id': { - 'uid': 'ID5*YYYYYYYYYYYYYYY', - 'ext': { - 'linkType': 2, - 'pba': 'YYYYYYYYYYYYY==' - } - } - }, - userIdAsEids: [ - { - 'source': 'id5-sync.com', - 'uids': [ - { - 'id': 'ID5*YYYYYYYYYYYYYYY', - 'atype': 1, - 'ext': { - 'linkType': 2, - 'pba': 'YYYYYYYYYYYYY==' - } - } - ] - } - ] + } }; - const deepClone = function(val) { + const deepClone = function (val) { return JSON.parse(JSON.stringify(val)); }; @@ -133,15 +91,15 @@ describe('orbidderBidAdapter', () => { expect(spec.isBidRequestValid(defaultBidRequestNative)).to.equal(true); }); - it('banner: accepts optional keyValues object', () => { + it('banner: accepts optional profile object', () => { const bidRequest = deepClone(defaultBidRequestBanner); - bidRequest.params.keyValues = { 'key': 'value' }; + bidRequest.params.profile = {'key': 'value'}; expect(spec.isBidRequestValid(bidRequest)).to.equal(true); }); - it('native: accepts optional keyValues object', () => { + it('native: accepts optional profile object', () => { const bidRequest = deepClone(defaultBidRequestNative); - bidRequest.params.keyValues = { 'key': 'value' }; + bidRequest.params.profile = {'key': 'value'}; expect(spec.isBidRequestValid(bidRequest)).to.equal(true); }); @@ -157,15 +115,15 @@ describe('orbidderBidAdapter', () => { expect(spec.isBidRequestValid(bidRequest)).to.equal(false); }); - it('banner: doesn\'t accept malformed keyValues', () => { + it('banner: doesn\'t accept malformed profile', () => { const bidRequest = deepClone(defaultBidRequestBanner); - bidRequest.params.keyValues = 'another not usable string'; + bidRequest.params.profile = 'another not usable string'; expect(spec.isBidRequestValid(bidRequest)).to.equal(false); }); - it('native: doesn\'t accept malformed keyValues', () => { + it('native: doesn\'t accept malformed profile', () => { const bidRequest = deepClone(defaultBidRequestNative); - bidRequest.params.keyValues = 'another not usable string'; + bidRequest.params.profile = 'another not usable string'; expect(spec.isBidRequestValid(bidRequest)).to.equal(false); }); @@ -201,11 +159,8 @@ describe('orbidderBidAdapter', () => { }); describe('buildRequests', () => { - let request, nativeRequest; - before(() => { - request = buildRequest(defaultBidRequestBanner); - nativeRequest = buildRequest(defaultBidRequestNative); - }) + const request = buildRequest(defaultBidRequestBanner); + const nativeRequest = buildRequest(defaultBidRequestNative); it('sends bid request to endpoint via https using post', () => { expect(request.method).to.equal('POST'); @@ -221,20 +176,34 @@ describe('orbidderBidAdapter', () => { // we add two, because we add pageUrl and version from bidderRequest object expect(Object.keys(request.data).length).to.equal(Object.keys(defaultBidRequestBanner).length + 2); - const expectedBidRequest = deepClone(defaultBidRequestBanner); - expectedBidRequest.pageUrl = 'https://localhost:9876/'; - expectedBidRequest.v = $$PREBID_GLOBAL$$.version; - expect(request.data).to.deep.equal(expectedBidRequest); + expect(request.data.bidId).to.equal(defaultBidRequestBanner.bidId); + expect(request.data.auctionId).to.equal(defaultBidRequestBanner.auctionId); + expect(request.data.transactionId).to.equal(defaultBidRequestBanner.ortb2Imp.ext.tid); + expect(request.data.bidRequestCount).to.equal(defaultBidRequestBanner.bidRequestCount); + expect(request.data.adUnitCode).to.equal(defaultBidRequestBanner.adUnitCode); + expect(request.data.pageUrl).to.equal('https://localhost:9876/'); + expect(request.data.v).to.equal($$PREBID_GLOBAL$$.version); + expect(request.data.sizes).to.equal(defaultBidRequestBanner.sizes); + + expect(_.isEqual(request.data.params, defaultBidRequestBanner.params)).to.be.true; + expect(_.isEqual(request.data.mediaTypes, defaultBidRequestBanner.mediaTypes)).to.be.true; }); it('native: sends correct bid parameters', () => { // we add two, because we add pageUrl and version from bidderRequest object expect(Object.keys(nativeRequest.data).length).to.equal(Object.keys(defaultBidRequestNative).length + 2); - const expectedBidRequest = deepClone(defaultBidRequestNative); - expectedBidRequest.pageUrl = 'https://localhost:9876/'; - expectedBidRequest.v = $$PREBID_GLOBAL$$.version; - expect(nativeRequest.data).to.deep.equal(expectedBidRequest); + expect(nativeRequest.data.bidId).to.equal(defaultBidRequestNative.bidId); + expect(nativeRequest.data.auctionId).to.equal(defaultBidRequestNative.auctionId); + expect(nativeRequest.data.transactionId).to.equal(defaultBidRequestNative.ortb2Imp.ext.tid); + expect(nativeRequest.data.bidRequestCount).to.equal(defaultBidRequestNative.bidRequestCount); + expect(nativeRequest.data.adUnitCode).to.equal(defaultBidRequestNative.adUnitCode); + expect(nativeRequest.data.pageUrl).to.equal('https://localhost:9876/'); + expect(nativeRequest.data.v).to.equal($$PREBID_GLOBAL$$.version); + expect(nativeRequest.data.sizes).to.be.empty; + + expect(_.isEqual(nativeRequest.data.params, defaultBidRequestNative.params)).to.be.true; + expect(_.isEqual(nativeRequest.data.mediaTypes, defaultBidRequestNative.mediaTypes)).to.be.true; }); it('banner: handles empty gdpr object', () => { @@ -322,7 +291,7 @@ describe('orbidderBidAdapter', () => { }); }); - it('buildRequests with price floor module', () => { + describe('buildRequests with price floor module', () => { const bidRequest = deepClone(defaultBidRequestBanner); bidRequest.params.bidfloor = 1; bidRequest.getFloor = (floorObj) => { @@ -375,7 +344,7 @@ describe('orbidderBidAdapter', () => { } ]; - const result = spec.interpretResponse({ body: serverResponse }); + const result = spec.interpretResponse({body: serverResponse}); expect(result.length).to.equal(expectedResponse.length); expect(_.isEqual(expectedResponse, serverResponse)).to.be.true; }); @@ -415,7 +384,7 @@ describe('orbidderBidAdapter', () => { } ]; - 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) => { @@ -482,7 +451,7 @@ describe('orbidderBidAdapter', () => { } ]; - const result = spec.interpretResponse({ body: serverResponse }); + const result = spec.interpretResponse({body: serverResponse}); expect(result.length).to.equal(expectedResponse.length); expect(_.isEqual(expectedResponse, serverResponse)).to.be.true; @@ -502,7 +471,7 @@ describe('orbidderBidAdapter', () => { 'netRevenue': true, } ]; - const result = spec.interpretResponse({ body: serverResponse }); + const result = spec.interpretResponse({body: serverResponse}); expect(result.length).to.equal(0); }); @@ -520,7 +489,7 @@ describe('orbidderBidAdapter', () => { 'creativeId': '29681110', } ]; - const result = spec.interpretResponse({ body: serverResponse }); + const result = spec.interpretResponse({body: serverResponse}); expect(result.length).to.equal(0); }); @@ -546,13 +515,13 @@ describe('orbidderBidAdapter', () => { } } ]; - 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/outbrainBidAdapter_spec.js b/test/spec/modules/outbrainBidAdapter_spec.js index e6abb5e9caa..ab72ff85ab0 100644 --- a/test/spec/modules/outbrainBidAdapter_spec.js +++ b/test/spec/modules/outbrainBidAdapter_spec.js @@ -1,7 +1,9 @@ import { expect } from 'chai'; -import { spec, storage } from 'modules/outbrainBidAdapter.js'; +import { spec } from 'modules/outbrainBidAdapter.js'; import { config } from 'src/config.js'; import { server } from 'test/mocks/xhr'; +import { createEidsArray } from 'modules/userId/eids.js'; +import * as utils from 'src/utils.js'; describe('Outbrain Adapter', function () { describe('Bid request and response', function () { @@ -213,18 +215,15 @@ describe('Outbrain Adapter', function () { }) describe('buildRequests', function () { - let getDataFromLocalStorageStub; - before(() => { - getDataFromLocalStorageStub = sinon.stub(storage, 'getDataFromLocalStorage') config.setConfig({ outbrain: { bidderUrl: 'https://bidder-url.com', } - }) + } + ) }) after(() => { - getDataFromLocalStorageStub.restore() config.resetConfig() }) @@ -427,7 +426,7 @@ describe('Outbrain Adapter', function () { expect(resData.badv).to.deep.equal(['bad-advertiser']) }); - it('should pass first party data', function () { + it('first party data', function () { const bidRequest = { ...commonBidRequest, ...nativeBidRequestParams, @@ -508,35 +507,13 @@ describe('Outbrain Adapter', function () { config.resetConfig() }); - it('should pass gpp information', function () { - const bidRequest = { - ...commonBidRequest, - ...nativeBidRequestParams, - }; - const bidderRequest = { - ...commonBidderRequest, - 'gppConsent': { - 'gppString': 'abc12345', - 'applicableSections': [8] - } - } - - const res = spec.buildRequests([bidRequest], bidderRequest); - const resData = JSON.parse(res.data); - - expect(resData.regs.ext.gpp).to.exist; - expect(resData.regs.ext.gpp_sid).to.exist; - expect(resData.regs.ext.gpp).to.equal('abc12345'); - expect(resData.regs.ext.gpp_sid).to.deep.equal([8]); - }); - it('should pass extended ids', function () { let bidRequest = { bidId: 'bidId', params: {}, - userIdAsEids: [ - { source: 'liveramp.com', uids: [{ id: 'id-value', atype: 3 }] } - ], + userIdAsEids: createEidsArray({ + idl_env: 'id-value', + }), ...commonBidRequest, }; @@ -547,22 +524,6 @@ describe('Outbrain Adapter', function () { ]); }); - it('should pass OB user token', function () { - getDataFromLocalStorageStub.returns('12345'); - - let bidRequest = { - bidId: 'bidId', - params: {}, - ...commonBidRequest, - }; - - let res = spec.buildRequests([bidRequest], commonBidderRequest); - const resData = JSON.parse(res.data) - expect(resData.user.ext.obusertoken).to.equal('12345') - expect(getDataFromLocalStorageStub.called).to.be.true; - sinon.assert.calledWith(getDataFromLocalStorageStub, 'OB-USER-TOKEN'); - }); - it('should pass bidfloor', function () { const bidRequest = { ...commonBidRequest, @@ -883,12 +844,6 @@ describe('Outbrain Adapter', function () { type: 'image', url: `${usersyncUrl}?gdpr=1&gdpr_consent=foo&us_privacy=1NYN` }]); }); - - it('should pass gpp consent', function () { - expect(spec.getUserSyncs({ pixelEnabled: true }, {}, undefined, '', { gppString: 'abc12345', applicableSections: [1, 2] })).to.deep.equal([{ - type: 'image', url: `${usersyncUrl}?gpp=abc12345&gpp_sid=1%2C2` - }]); - }); }) describe('onBidWon', function () { diff --git a/test/spec/modules/oxxionAnalyticsAdapter_spec.js b/test/spec/modules/oxxionAnalyticsAdapter_spec.js index 9d06be24f68..63e929d7160 100644 --- a/test/spec/modules/oxxionAnalyticsAdapter_spec.js +++ b/test/spec/modules/oxxionAnalyticsAdapter_spec.js @@ -1,5 +1,4 @@ import oxxionAnalytics from 'modules/oxxionAnalyticsAdapter.js'; -import {dereferenceWithoutRenderer} from 'modules/oxxionAnalyticsAdapter.js'; import { expect } from 'chai'; import { server } from 'test/mocks/xhr.js'; let adapterManager = require('src/adapterManager').default; @@ -86,21 +85,20 @@ describe('Oxxion Analytics', function () { } }, 'adUnitCode': 'tag_200124_banner', - 'transactionId': '8b2a8629-d1ea-4bb1-aff0-e335b96dd002', + 'transactionId': 'de664ccb-e18b-4436-aeb0-362382eb1b40', 'sizes': [ [ 300, 600 ] ], - 'bidId': '2bd3e8ff8a113f', + 'bidId': '34a63e5d5378a3', 'bidderRequestId': '11dc6ff6378de7', 'auctionId': '1e8b993d-8f0a-4232-83eb-3639ddf3a44b', 'src': 'client', 'bidRequestsCount': 1, 'bidderRequestsCount': 1, - 'bidderWinsCount': 0, - 'ova': 'cleared' + 'bidderWinsCount': 0 } ], 'auctionStart': 1647424261187, @@ -150,14 +148,14 @@ describe('Oxxion Analytics', function () { 'bidsReceived': [ { 'bidderCode': 'appnexus', - 'width': 970, - 'height': 250, + 'width': 300, + 'height': 600, 'statusMessage': 'Bid available', - 'adId': '65d16ef039a97a', - 'requestId': '2bd3e8ff8a113f', - 'transactionId': '8b2a8629-d1ea-4bb1-aff0-e335b96dd002', + 'adId': '7a4ced80f33d33', + 'requestId': '34a63e5d5378a3', + 'transactionId': 'de664ccb-e18b-4436-aeb0-362382eb1b40', 'auctionId': '1e8b993d-8f0a-4232-83eb-3639ddf3a44b', - 'mediaType': 'video', + 'mediaType': 'banner', 'source': 'client', 'cpm': 27.4276, 'creativeId': '158534630', @@ -168,10 +166,8 @@ describe('Oxxion Analytics', function () { 'meta': { 'advertiserDomains': [ 'example.com' - ], - 'demandSource': 'something' + ] }, - 'renderer': 'something', 'originalCpm': 25.02521, 'originalCurrency': 'EUR', 'responseTimestamp': 1647424261559, @@ -188,7 +184,7 @@ describe('Oxxion Analytics', function () { 'size': '300x600', 'adserverTargeting': { 'hb_bidder': 'appnexus', - 'hb_adid': '65d16ef039a97a', + 'hb_adid': '7a4ced80f33d33', 'hb_pb': '20.000000', 'hb_size': '300x600', 'hb_source': 'client', @@ -225,7 +221,6 @@ describe('Oxxion Analytics', function () { 'example.com' ] }, - 'renderer': 'something', 'originalCpm': 25.02521, 'originalCurrency': 'EUR', 'responseTimestamp': 1647424261558, @@ -272,24 +267,7 @@ describe('Oxxion Analytics', function () { oxxionAnalytics.disableAnalytics(); oxxionAnalytics.track.restore(); }); - it('test dereferenceWithoutRenderer', function () { - adapterManager.registerAnalyticsAdapter({ - code: 'oxxion', - adapter: oxxionAnalytics - }); - adapterManager.enableAnalytics({ - provider: 'oxxion', - options: { - domain: 'test' - } - }); - let resultBidWon = JSON.parse(dereferenceWithoutRenderer(bidWon)); - expect(resultBidWon).not.to.have.property('renderer'); - let resultBid = JSON.parse(dereferenceWithoutRenderer(auctionEnd)); - expect(resultBid).to.have.property('bidsReceived').and.to.have.lengthOf(1); - expect(resultBid.bidsReceived[0]).not.to.have.property('renderer'); - }); it('test auctionEnd', function () { adapterManager.registerAnalyticsAdapter({ code: 'oxxion', @@ -315,16 +293,14 @@ describe('Oxxion Analytics', function () { expect(message.auctionEnd[0].bidsReceived[0]).not.to.have.property('ad'); expect(message.auctionEnd[0].bidsReceived[0]).to.have.property('meta'); expect(message.auctionEnd[0].bidsReceived[0].meta).to.have.property('advertiserDomains'); - expect(message.auctionEnd[0].bidsReceived[0].meta).to.have.property('demandSource'); expect(message.auctionEnd[0].bidsReceived[0]).to.have.property('adId'); expect(message.auctionEnd[0]).to.have.property('bidderRequests').and.to.have.lengthOf(1); expect(message.auctionEnd[0].bidderRequests[0]).to.have.property('gdprConsent'); expect(message.auctionEnd[0].bidderRequests[0].gdprConsent).not.to.have.property('vendorData'); - expect(message.auctionEnd[0].bidderRequests[0]).to.have.property('oxxionMode'); + sinon.assert.callCount(oxxionAnalytics.track, 5); }); it('test bidWon', function() { - window.OXXION_MODE = {'abtest': true}; adapterManager.registerAnalyticsAdapter({ code: 'oxxion', adapter: oxxionAnalytics @@ -342,8 +318,7 @@ describe('Oxxion Analytics', function () { expect(message).not.to.have.property('ad'); expect(message).to.have.property('adId') expect(message).to.have.property('cpmIncrement').and.to.equal(27.4276); - expect(message).to.have.property('oxxionMode').and.to.have.property('abtest').and.to.equal(true); - expect(message).to.have.property('ova').and.to.equal('cleared'); + // sinon.assert.callCount(oxxionAnalytics.track, 1); }); }); }); diff --git a/test/spec/modules/oxxionRtdProvider_spec.js b/test/spec/modules/oxxionRtdProvider_spec.js index 2a8024f3565..5bd8756bb6f 100644 --- a/test/spec/modules/oxxionRtdProvider_spec.js +++ b/test/spec/modules/oxxionRtdProvider_spec.js @@ -6,10 +6,7 @@ const utils = require('src/utils.js'); const moduleConfig = { params: { domain: 'test.endpoint', - contexts: ['instream', 'outstream'], - samplingRate: 10, - threshold: false, - bidders: ['appnexus', 'mediasquare'], + contexts: ['instream', 'outstream'] } }; @@ -34,14 +31,6 @@ let request = { ], 'transactionId': 'de664ccb-e18b-4436-aeb0-362382eb1b41' }, - { - 'code': 'msq_tag_200125_banner', - 'mediaTypes': { 'banner': { 'sizes': [[300, 250]] } }, - 'bids': [ - {'bidder': 'appnexusAst', 'params': {'placementId': 345678}}, - ], - 'transactionId': 'de664ccb-e18b-4436-aeb0-362382eb1b41' - } ] }; @@ -113,22 +102,6 @@ let bids = [{ }, ]; -let 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}, -]; - -const userConsent = { - 'gdpr': { - 'consentString': 'consent_hash' - }, - 'usp': null, - 'gpp': null, - 'coppa': false -}; - describe('oxxionRtdProvider', () => { describe('Oxxion RTD sub module', () => { it('should init, return true, and set the params', () => { @@ -141,20 +114,30 @@ describe('oxxionRtdProvider', () => { auctionEnd.bidsReceived = bids; it('call everything', function() { oxxionSubmodule.getBidRequestData(request, null, moduleConfig); + oxxionSubmodule.onBidResponseEvent(auctionEnd.bidsReceived[0], moduleConfig); + oxxionSubmodule.onBidResponseEvent(auctionEnd.bidsReceived[1], moduleConfig); + }); + it('check vastImpUrl', function() { + expect(auctionEnd.bidsReceived[0]).to.have.property('vastImpUrl'); + let expectVastImpUrl = 'https://' + moduleConfig.params.domain + '.oxxion.io/analytics/vast_imp?'; + expect(auctionEnd.bidsReceived[1].vastImpUrl).to.contain(expectVastImpUrl); + expect(auctionEnd.bidsReceived[1].vastImpUrl).to.contain(encodeURI('https://some.tracking-url.com')); + }); + it('check vastXml', function() { + expect(auctionEnd.bidsReceived[0]).to.have.property('vastXml'); + let vastWrapper = new DOMParser().parseFromString(auctionEnd.bidsReceived[0].vastXml, 'text/xml'); + let impressions = vastWrapper.querySelectorAll('VAST Ad Wrapper Impression'); + expect(impressions.length).to.equal(2); + expect(auctionEnd.bidsReceived[1]).to.have.property('vastXml'); + expect(auctionEnd.bidsReceived[1].adId).to.equal('4b2e1581c0ca1a'); + let vastInline = new DOMParser().parseFromString(auctionEnd.bidsReceived[1].vastXml, 'text/xml'); + let inline = vastInline.querySelectorAll('VAST Ad InLine'); + expect(inline).to.have.lengthOf(1); + let inlineImpressions = vastInline.querySelectorAll('VAST Ad InLine Impression'); + expect(inlineImpressions).to.have.lengthOf.above(0); }); - it('check bid filtering', function() { - let requestsList = oxxionSubmodule.getRequestsList(request); - expect(requestsList.length).to.equal(4); - expect(requestsList[0]).to.have.property('id'); - expect(request.adUnits[0].bids[0]).to.have.property('_id'); - expect(requestsList[0].id).to.equal(request.adUnits[0].bids[0]._id); - const [filteredBiddderRequests, filteredBids] = oxxionSubmodule.getFilteredAdUnitsOnBidRates(bidInterests, request.adUnits, moduleConfig.params, false); - expect(filteredBids.length).to.equal(1); - expect(filteredBiddderRequests.length).to.equal(3); - expect(filteredBiddderRequests[0]).to.have.property('bids'); - expect(filteredBiddderRequests[0].bids.length).to.equal(1); - expect(filteredBiddderRequests[1]).to.have.property('bids'); - expect(filteredBiddderRequests[1].bids.length).to.equal(1); + it('check cpmIncrement', function() { + expect(auctionEnd.bidsReceived[1].vastImpUrl).to.contain(encodeURI('cpmIncrement=0')); }); }); }); diff --git a/test/spec/modules/ozoneBidAdapter_spec.js b/test/spec/modules/ozoneBidAdapter_spec.js index 73df2fba8fd..4c7e330b237 100644 --- a/test/spec/modules/ozoneBidAdapter_spec.js +++ b/test/spec/modules/ozoneBidAdapter_spec.js @@ -6,6 +6,7 @@ import {getGranularityKeyName, getGranularityObject} from '../../../modules/ozon import * as utils from '../../../src/utils.js'; const OZONEURI = 'https://elb.the-ozone-project.com/openrtb2/auction'; const BIDDER_CODE = 'ozone'; + var validBidRequests = [ { adUnitCode: 'div-gpt-ad-1460505748561-0', @@ -2048,40 +2049,6 @@ describe('ozone Adapter', function () { expect(data.imp[0].ext.ozone.customData).to.be.an('array'); expect(data.imp[0].ext.ozone.customData[0].targeting.oztestmode).to.equal('mytestvalue_123'); }); - it('should pass gpid to auction if it is present (gptPreAuction adapter sets this)', function () { - var specMock = utils.deepClone(spec); - let br = JSON.parse(JSON.stringify(validBidRequests)); - utils.deepSetValue(br[0], 'ortb2Imp.ext.gpid', '/22037345/projectozone'); - const request = specMock.buildRequests(br, validBidderRequest); - const data = JSON.parse(request.data); - expect(data.imp[0].ext.gpid).to.equal('/22037345/projectozone'); - }); - it('should batch into 10s if config is set', function () { - config.setConfig({ozone: {'batchRequests': true}}); - var specMock = utils.deepClone(spec); - let arrReq = []; - for (let i = 0; i < 25; i++) { - let b = validBidRequests[0]; - b.adUnitCode += i; - arrReq.push(b); - } - const request = specMock.buildRequests(arrReq, validBidderRequest); - expect(request.length).to.equal(3); - config.resetConfig(); - }); - it('should not batch into 10s if config is set to false and singleRequest is true', function () { - config.setConfig({ozone: {'batchRequests': false, 'singleRequest': true}}); - var specMock = utils.deepClone(spec); - let arrReq = []; - for (let i = 0; i < 15; i++) { - let b = validBidRequests[0]; - b.adUnitCode += i; - arrReq.push(b); - } - const request = specMock.buildRequests(arrReq, validBidderRequest); - expect(request.method).to.equal('POST'); - config.resetConfig(); - }); it('should use GET values auction=dev & cookiesync=dev if set', function() { var specMock = utils.deepClone(spec); specMock.getGetParametersAsObject = function() { @@ -2462,12 +2429,6 @@ describe('ozone Adapter', function () { const result = spec.interpretResponse(validres, request); expect(utils.deepAccess(result[0].adserverTargeting, 'oz_ozappnexus_sid')).to.equal(result[0].cid); }); - it('should add oz_auc_id (response id value)', function () { - const request = spec.buildRequests(validBidRequestsMulti, validBidderRequest); - let validres = JSON.parse(JSON.stringify(validBidResponse1adWith2Bidders)); - const result = spec.interpretResponse(validres, request); - expect(utils.deepAccess(result[0].adserverTargeting, 'oz_auc_id')).to.equal(validBidResponse1adWith2Bidders.body.id); - }); it('should add unique adId values to each bid', function() { const request = spec.buildRequests(validBidRequests, validBidderRequest); let validres = JSON.parse(JSON.stringify(validResponse2BidsSameAdunit)); diff --git a/test/spec/modules/pangleBidAdapter_spec.js b/test/spec/modules/pangleBidAdapter_spec.js deleted file mode 100644 index 6d8f9a66bcf..00000000000 --- a/test/spec/modules/pangleBidAdapter_spec.js +++ /dev/null @@ -1,387 +0,0 @@ -import { expect } from 'chai'; -import { spec } from 'modules/pangleBidAdapter.js'; - -const REQUEST = [{ - adUnitCode: 'adUnitCode1', - bidId: 'bidId1', - auctionId: 'auctionId-56a2-4f71-9098-720a68f2f708', - ortb2Imp: { - ext: { - tid: 'cccc1234', - } - }, - mediaTypes: { - banner: { - sizes: [ - [300, 250] - ] - } - }, - bidder: 'pangle', - params: { - placementid: 999, - appid: 111, - }, -}, -{ - adUnitCode: 'adUnitCode2', - bidId: 'bidId2', - auctionId: 'auctionId-56a2-4f71-9098-720a68f2f708', - ortb2Imp: { - ext: { - tid: 'cccc1234', - } - }, - mediaTypes: { - banner: { - sizes: [ - [300, 250] - ] - } - }, - bidder: 'pangle', - params: { - placementid: 999, - appid: 111, - }, -}]; - -const DEFAULT_OPTIONS = { - userId: { - britepoolid: 'pangle-britepool', - criteoId: 'pangle-criteo', - digitrustid: { data: { id: 'pangle-digitrust' } }, - id5id: { uid: 'pangle-id5' }, - idl_env: 'pangle-idl-env', - lipb: { lipbid: 'pangle-liveintent' }, - netId: 'pangle-netid', - parrableId: { eid: 'pangle-parrable' }, - pubcid: 'pangle-pubcid', - tdid: 'pangle-ttd', - } -}; - -const RESPONSE = { - 'headers': null, - 'body': { - 'id': 'requestId', - 'seatbid': [ - { - 'bid': [ - { - 'id': 'bidId1', - 'impid': 'bidId1', - 'price': 0.18, - 'adm': '', - 'adid': '144762342', - 'adomain': [ - 'https://dummydomain.com' - ], - 'iurl': 'iurl', - 'cid': '109', - 'crid': 'creativeId', - 'cat': [], - 'w': 300, - 'h': 250, - 'mtype': 1, - 'ext': { - 'prebid': { - 'type': 'banner' - }, - 'bidder': { - 'pangle': { - 'brand_id': 334553, - 'auction_id': 514667951122925701, - 'bidder_id': 2, - 'bid_ad_type': 0 - } - } - } - } - ], - 'seat': 'seat' - } - ] - } -}; - -describe('pangle bid adapter', function () { - describe('isBidRequestValid', function () { - it('should accept request if placementid and appid is passed', function () { - let bid = { - bidder: 'pangle', - params: { - token: 'xxx', - } - }; - expect(spec.isBidRequestValid(bid)).to.equal(true); - }); - it('reject requests without params', function () { - let bid = { - bidder: 'pangle', - params: {} - }; - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - }); - - describe('buildRequests', function () { - it('creates request data', function () { - let request = spec.buildRequests(REQUEST, DEFAULT_OPTIONS)[0]; - expect(request).to.exist.and.to.be.a('object'); - const payload = request.data; - expect(payload.imp[0]).to.have.property('id', REQUEST[0].bidId); - expect(payload.imp[1]).to.have.property('id', REQUEST[1].bidId); - }); - }); - - describe('interpretResponse', function () { - it('has bids', function () { - let request = spec.buildRequests(REQUEST, DEFAULT_OPTIONS)[0]; - let bids = spec.interpretResponse(RESPONSE, request); - expect(bids).to.be.an('array').that.is.not.empty; - validateBidOnIndex(0); - - function validateBidOnIndex(index) { - expect(bids[index]).to.have.property('currency', 'USD'); - expect(bids[index]).to.have.property('requestId', RESPONSE.body.seatbid[0].bid[index].id); - expect(bids[index]).to.have.property('cpm', RESPONSE.body.seatbid[0].bid[index].price); - expect(bids[index]).to.have.property('width', RESPONSE.body.seatbid[0].bid[index].w); - expect(bids[index]).to.have.property('height', RESPONSE.body.seatbid[0].bid[index].h); - expect(bids[index]).to.have.property('ad', RESPONSE.body.seatbid[0].bid[index].adm); - expect(bids[index]).to.have.property('creativeId', RESPONSE.body.seatbid[0].bid[index].crid); - expect(bids[index]).to.have.property('ttl', 30); - expect(bids[index]).to.have.property('netRevenue', true); - } - }); - - it('handles empty response', function () { - let request = spec.buildRequests(REQUEST, DEFAULT_OPTIONS)[0]; - const EMPTY_RESP = Object.assign({}, RESPONSE, { 'body': {} }); - const bids = spec.interpretResponse(EMPTY_RESP, request); - expect(bids).to.be.empty; - }); - }); - - describe('parseUserAgent', function () { - let desktop, mobile, tablet; - beforeEach(function () { - desktop = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36'; - mobile = 'Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1'; - tablet = 'Apple iPad: Mozilla/5.0 (iPad; CPU OS 13_5_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1.1 Mobile/15E148 Safari/605.1.15'; - }); - - it('should return correct device type: tablet', function () { - let deviceType = spec.getDeviceType(tablet); - expect(deviceType).to.equal(5); - }); - - it('should return correct device type: mobile', function () { - let deviceType = spec.getDeviceType(mobile); - expect(deviceType).to.equal(4); - }); - - it('should return correct device type: desktop', function () { - let deviceType = spec.getDeviceType(desktop); - expect(deviceType).to.equal(2); - }); - }); -}); - -describe('Pangle Adapter with video', function() { - const videoBidRequest = [ - { - bidId: '2820132fe18114', - mediaTypes: { video: { context: 'outstream', playerSize: [[300, 250]] } }, - params: { token: 'test-token' } - } - ]; - const bidderRequest = { - refererInfo: { - referer: 'https://example.com' - } - }; - const serverResponse = { - 'headers': null, - 'body': { - 'id': '233f1693-68d1-470a-ad85-c156c3faaf6f', - 'seatbid': [ - { - 'bid': [ - { - 'id': '2820132fe18114', - 'impid': '2820132fe18114', - 'price': 0.03294, - 'nurl': 'https://api16-event-sg2.pangle.io/api/ad/union/openrtb/win/?req_id=233f1693-68d1-470a-ad85-c156c3faaf6fu1450&ttdsp_adx_index=256&rit=980589944&extra=oqveoB%2Bg4%2ByNz9L8wwu%2Fy%2FwKxQsGaKsJHuB4NMK77uqZ9%2FJKpnsVZculJX8%2FxrRBAtaktU1DRN%2Fy6TKAqibCbj%2FM3%2BZ6biAKQG%2BCyt4eIV0KVvri9jCCnaajbkN7YNJWJJw2lW6cJ6Va3SuJG9H7a%2FAJd2PMbhK7fXWhoW72TwgOcKHKBgjM6sNDISBKbWlZyY3L1PhKSX%2FM8LOvL6qahsb%2FDpEObIx24vhQLNWp28XY1L4UqeibuRjam3eCvN7nXoQq74KkJ45QQsTgvV4j6I6EbLOdjOi%2FURhWMDjUD1VCMpqUT%2B6L8ZROgrX9Tp53eJ3bFOczmSTOmDSazKMHa%2B3uZZ7JHcSx32eoY4hfYc99NOJmYBKXNKCmoXyJvS3PCM3PlAz97hKrDMGnVv1wAQ7QGDCbittF0vZwtsRAvvx2mWINNIB3%2FUB2PjhxFsoDA%2BWE2urVZwEdyu%2FJrCznJsMwenXjcbMD5jmUF5vDkkLS%2B7TMDIEawJPJKZ62pK35enrwGxCs6ePXi21rJJkA0bF8tgAdl4mU1illBIVO4kCL%2ByRASskHPjgg%2FcdFe9HP%2Fi8byjAprH%2BhRerN%2FRKFxC3xv8b75x2pb1g7dY%2FTj9IjT0evsBSPVwFNqtKmPId35IcY%2FSXiqPHh%2FrAHZzr5BPsTT19P49SlNMR9UZYTzViX1iJpcCL1UFjuDdrdff%2BhHCviXxo%2FkRmufEF3umHZwxbdDOPAghuZ0DtRCY6S1rnb%2FK9BbpsVKSndOtgfCwMHFwiPmdw1XjEXGc1eOWXY6qfSp90PIfL6WS7Neh3ba2qMv6WxG3HSOBYvrcCqVTsNxk4UdVm3qb1J0CMVByweTMo45usSkCTdvX3JuEB7tVA6%2BrEk57b3XJd5Phf2AN8hon%2F7lmcXE41kwMQuXq89ViwQmW0G247UFWOQx4t1cmBqFiP6qNA%2F%2BunkZDno1pmAsGnTv7Mz9xtpOaIqKl8BKrVQSTopZ9WcUVzdBUutF19mn1f43BvyA9gIEhcDJHOj&win_price=${AUCTION_PRICE}&auction_mwb=${AUCTION_BID_TO_WIN}&use_pb=1', - 'lurl': 'https://api16-event-sg2.pangle.io/api/ad/union/openrtb/loss/?req_id=233f1693-68d1-470a-ad85-c156c3faaf6fu1450&ttdsp_adx_index=256&rit=980589944&extra=oqveoB%2Bg4%2ByNz9L8wwu%2Fy%2FwKxQsGaKsJHuB4NMK77uqZ9%2FJKpnsVZculJX8%2FxrRBAtaktU1DRN%2Fy6TKAqibCbj%2FM3%2BZ6biAKQG%2BCyt4eIV0KVvri9jCCnaajbkN7YNJWJJw2lW6cJ6Va3SuJG9H7a%2FAJd2PMbhK7fXWhoW72TwgOcKHKBgjM6sNDISBKbWlZyY3L1PhKSX%2FM8LOvL6qahsb%2FDpEObIx24vhQLNWp28XY1L4UqeibuRjam3eCvN7nXoQq74KkJ45QQsTgvV4j6I6EbLOdjOi%2FURhWMDjUD1VCMpqUT%2B6L8ZROgrX9Tp53eJ3bFOczmSTOmDSazKMHa%2B3uZZ7JHcSx32eoY4hfYc99NOJmYBKXNKCmoXyJvS3PCM3PlAz97hKrDMGnVv1wAQ7QGDCbittF0vZwtsRAvvx2mWINNIB3%2FUB2PjhxFsoDA%2BWE2urVZwEdyu%2FJrCznJsMwenXjcbMD5jmUF5vDkkLS%2B7TMDIEawJPJKZ62pK35enrwGxCs6ePXi21rJJkA0bF8tgAdl4mU1illBIVO4kCL%2ByRASskHPjgg%2FcdFe9HP%2Fi8byjAprH%2BhRerN%2FRKFxC3xv8b75x2pb1g7dY%2FTj9IjT0evsBSPVwFNqtKmPId35IcY%2FSXiqPHh%2FrAHZzr5BPsTT19P49SlNMR9UZYTzViX1iJpcCL1UFjuDdrdff%2BhHCviXxo%2FkRmufEF3umHZwxbdDOPAghuZ0DtRCY6S1rnb%2FK9BbpsVKSndOtgfCwMHFwiPmdw1XjEXGc1eOWXY6qfSp90PIfL6WS7Neh3ba2qMv6WxG3HSOBYvrcCqVTsNxk4UdVm3qb1J0CMVByweTMo45usSkCTdvX3JuEB7tVA6%2BrEk57b3XJd5Phf2AN8hon%2F7lmcXE41kwMQuXq89ViwQmW0G247UFWOQx4t1cmBqFiP6qNA%2F%2BunkZDno1pmAsGnTv7Mz9xtpOaIqKl8BKrVQSTopZ9WcUVzdBUutF19mn1f43BvyA9gIEhcDJHOj&reason=${AUCTION_LOSS}&ad_slot_type=8&auction_mwb=${AUCTION_PRICE}&use_pb=1', - 'adm': '', - 'adid': '1780626232977441', - 'adomain': [ - 'swi.esxcmnb.com' - ], - 'iurl': 'https://p16-ttam-va.ibyteimg.com/origin/ad-site-i18n-sg/202310245d0d598b3ff5993c4f129a8b', - 'cid': '1780626232977441', - 'crid': '1780626232977441', - 'attr': [ - 4 - ], - 'w': 640, - 'h': 640, - 'mtype': 1, - 'ext': { - 'pangle': { - 'adtype': 8 - }, - 'event_notification_token': { - 'payload': '980589944:8:1450:7492' - } - } - } - ], - 'seat': 'pangle' - } - ] - } - }; - - describe('Video: buildRequests', function() { - it('should create a POST request for video bid', function() { - const requests = spec.buildRequests(videoBidRequest, bidderRequest); - expect(requests[0].method).to.equal('POST'); - }); - - it('should have a valid URL and payload for an out-stream video bid', function () { - const requests = spec.buildRequests(videoBidRequest, bidderRequest); - expect(requests[0].url).to.equal('https://pangle.pangleglobal.com/api/ad/union/web_js/common/get_ads'); - expect(requests[0].data).to.exist; - }); - }); - - describe('interpretResponse: Video', function () { - it('should get correct bid response', function () { - const request = spec.buildRequests(videoBidRequest, bidderRequest)[0]; - const interpretedResponse = spec.interpretResponse(serverResponse, request); - expect(interpretedResponse).to.be.an('array'); - const bid = interpretedResponse[0]; - expect(bid).to.exist; - expect(bid.requestId).to.exist; - expect(bid.cpm).to.be.above(0); - expect(bid.ttl).to.exist; - expect(bid.creativeId).to.exist; - if (bid.renderer) { - expect(bid.renderer.render).to.exist; - } - }); - }); -}); - -describe('pangle multi-format ads', function () { - const bidderRequest = { - refererInfo: { - referer: 'https://example.com' - } - }; - const multiRequest = [ - { - bidId: '2820132fe18114', - mediaTypes: { banner: { sizes: [[300, 250]] }, video: { context: 'outstream', playerSize: [[300, 250]] } }, - params: { token: 'test-token' } - } - ]; - const videoResponse = { - 'headers': null, - 'body': { - 'id': '233f1693-68d1-470a-ad85-c156c3faaf6f', - 'seatbid': [ - { - 'bid': [ - { - 'id': '2820132fe18114', - 'impid': '2820132fe18114', - 'price': 0.03294, - 'nurl': 'https://api16-event-sg2.pangle.io/api/ad/union/openrtb/win/?req_id=233f1693-68d1-470a-ad85-c156c3faaf6fu1450&ttdsp_adx_index=256&rit=980589944&extra=oqveoB%2Bg4%2ByNz9L8wwu%2Fy%2FwKxQsGaKsJHuB4NMK77uqZ9%2FJKpnsVZculJX8%2FxrRBAtaktU1DRN%2Fy6TKAqibCbj%2FM3%2BZ6biAKQG%2BCyt4eIV0KVvri9jCCnaajbkN7YNJWJJw2lW6cJ6Va3SuJG9H7a%2FAJd2PMbhK7fXWhoW72TwgOcKHKBgjM6sNDISBKbWlZyY3L1PhKSX%2FM8LOvL6qahsb%2FDpEObIx24vhQLNWp28XY1L4UqeibuRjam3eCvN7nXoQq74KkJ45QQsTgvV4j6I6EbLOdjOi%2FURhWMDjUD1VCMpqUT%2B6L8ZROgrX9Tp53eJ3bFOczmSTOmDSazKMHa%2B3uZZ7JHcSx32eoY4hfYc99NOJmYBKXNKCmoXyJvS3PCM3PlAz97hKrDMGnVv1wAQ7QGDCbittF0vZwtsRAvvx2mWINNIB3%2FUB2PjhxFsoDA%2BWE2urVZwEdyu%2FJrCznJsMwenXjcbMD5jmUF5vDkkLS%2B7TMDIEawJPJKZ62pK35enrwGxCs6ePXi21rJJkA0bF8tgAdl4mU1illBIVO4kCL%2ByRASskHPjgg%2FcdFe9HP%2Fi8byjAprH%2BhRerN%2FRKFxC3xv8b75x2pb1g7dY%2FTj9IjT0evsBSPVwFNqtKmPId35IcY%2FSXiqPHh%2FrAHZzr5BPsTT19P49SlNMR9UZYTzViX1iJpcCL1UFjuDdrdff%2BhHCviXxo%2FkRmufEF3umHZwxbdDOPAghuZ0DtRCY6S1rnb%2FK9BbpsVKSndOtgfCwMHFwiPmdw1XjEXGc1eOWXY6qfSp90PIfL6WS7Neh3ba2qMv6WxG3HSOBYvrcCqVTsNxk4UdVm3qb1J0CMVByweTMo45usSkCTdvX3JuEB7tVA6%2BrEk57b3XJd5Phf2AN8hon%2F7lmcXE41kwMQuXq89ViwQmW0G247UFWOQx4t1cmBqFiP6qNA%2F%2BunkZDno1pmAsGnTv7Mz9xtpOaIqKl8BKrVQSTopZ9WcUVzdBUutF19mn1f43BvyA9gIEhcDJHOj&win_price=${AUCTION_PRICE}&auction_mwb=${AUCTION_BID_TO_WIN}&use_pb=1', - 'lurl': 'https://api16-event-sg2.pangle.io/api/ad/union/openrtb/loss/?req_id=233f1693-68d1-470a-ad85-c156c3faaf6fu1450&ttdsp_adx_index=256&rit=980589944&extra=oqveoB%2Bg4%2ByNz9L8wwu%2Fy%2FwKxQsGaKsJHuB4NMK77uqZ9%2FJKpnsVZculJX8%2FxrRBAtaktU1DRN%2Fy6TKAqibCbj%2FM3%2BZ6biAKQG%2BCyt4eIV0KVvri9jCCnaajbkN7YNJWJJw2lW6cJ6Va3SuJG9H7a%2FAJd2PMbhK7fXWhoW72TwgOcKHKBgjM6sNDISBKbWlZyY3L1PhKSX%2FM8LOvL6qahsb%2FDpEObIx24vhQLNWp28XY1L4UqeibuRjam3eCvN7nXoQq74KkJ45QQsTgvV4j6I6EbLOdjOi%2FURhWMDjUD1VCMpqUT%2B6L8ZROgrX9Tp53eJ3bFOczmSTOmDSazKMHa%2B3uZZ7JHcSx32eoY4hfYc99NOJmYBKXNKCmoXyJvS3PCM3PlAz97hKrDMGnVv1wAQ7QGDCbittF0vZwtsRAvvx2mWINNIB3%2FUB2PjhxFsoDA%2BWE2urVZwEdyu%2FJrCznJsMwenXjcbMD5jmUF5vDkkLS%2B7TMDIEawJPJKZ62pK35enrwGxCs6ePXi21rJJkA0bF8tgAdl4mU1illBIVO4kCL%2ByRASskHPjgg%2FcdFe9HP%2Fi8byjAprH%2BhRerN%2FRKFxC3xv8b75x2pb1g7dY%2FTj9IjT0evsBSPVwFNqtKmPId35IcY%2FSXiqPHh%2FrAHZzr5BPsTT19P49SlNMR9UZYTzViX1iJpcCL1UFjuDdrdff%2BhHCviXxo%2FkRmufEF3umHZwxbdDOPAghuZ0DtRCY6S1rnb%2FK9BbpsVKSndOtgfCwMHFwiPmdw1XjEXGc1eOWXY6qfSp90PIfL6WS7Neh3ba2qMv6WxG3HSOBYvrcCqVTsNxk4UdVm3qb1J0CMVByweTMo45usSkCTdvX3JuEB7tVA6%2BrEk57b3XJd5Phf2AN8hon%2F7lmcXE41kwMQuXq89ViwQmW0G247UFWOQx4t1cmBqFiP6qNA%2F%2BunkZDno1pmAsGnTv7Mz9xtpOaIqKl8BKrVQSTopZ9WcUVzdBUutF19mn1f43BvyA9gIEhcDJHOj&reason=${AUCTION_LOSS}&ad_slot_type=8&auction_mwb=${AUCTION_PRICE}&use_pb=1', - 'adm': '', - 'adid': '1780626232977441', - 'adomain': [ - 'swi.esxcmnb.com' - ], - 'iurl': 'https://p16-ttam-va.ibyteimg.com/origin/ad-site-i18n-sg/202310245d0d598b3ff5993c4f129a8b', - 'cid': '1780626232977441', - 'crid': '1780626232977441', - 'attr': [ - 4 - ], - 'w': 640, - 'h': 640, - 'mtype': 2, - 'ext': { - 'pangle': { - 'adtype': 8 - }, - 'event_notification_token': { - 'payload': '980589944:8:1450:7492' - } - } - } - ], - 'seat': 'pangle' - } - ] - } - }; - const bannerResponse = { - 'headers': null, - 'body': { - 'id': '233f1693-68d1-470a-ad85-c156c3faaf6f', - 'seatbid': [ - { - 'bid': [ - { - 'id': '2820132fe18114', - 'impid': '2820132fe18114', - 'price': 0.03294, - 'nurl': 'https://api16-event-sg2.pangle.io/api/ad/union/openrtb/win/?req_id=233f1693-68d1-470a-ad85-c156c3faaf6fu1450&ttdsp_adx_index=256&rit=980589944&extra=oqveoB%2Bg4%2ByNz9L8wwu%2Fy%2FwKxQsGaKsJHuB4NMK77uqZ9%2FJKpnsVZculJX8%2FxrRBAtaktU1DRN%2Fy6TKAqibCbj%2FM3%2BZ6biAKQG%2BCyt4eIV0KVvri9jCCnaajbkN7YNJWJJw2lW6cJ6Va3SuJG9H7a%2FAJd2PMbhK7fXWhoW72TwgOcKHKBgjM6sNDISBKbWlZyY3L1PhKSX%2FM8LOvL6qahsb%2FDpEObIx24vhQLNWp28XY1L4UqeibuRjam3eCvN7nXoQq74KkJ45QQsTgvV4j6I6EbLOdjOi%2FURhWMDjUD1VCMpqUT%2B6L8ZROgrX9Tp53eJ3bFOczmSTOmDSazKMHa%2B3uZZ7JHcSx32eoY4hfYc99NOJmYBKXNKCmoXyJvS3PCM3PlAz97hKrDMGnVv1wAQ7QGDCbittF0vZwtsRAvvx2mWINNIB3%2FUB2PjhxFsoDA%2BWE2urVZwEdyu%2FJrCznJsMwenXjcbMD5jmUF5vDkkLS%2B7TMDIEawJPJKZ62pK35enrwGxCs6ePXi21rJJkA0bF8tgAdl4mU1illBIVO4kCL%2ByRASskHPjgg%2FcdFe9HP%2Fi8byjAprH%2BhRerN%2FRKFxC3xv8b75x2pb1g7dY%2FTj9IjT0evsBSPVwFNqtKmPId35IcY%2FSXiqPHh%2FrAHZzr5BPsTT19P49SlNMR9UZYTzViX1iJpcCL1UFjuDdrdff%2BhHCviXxo%2FkRmufEF3umHZwxbdDOPAghuZ0DtRCY6S1rnb%2FK9BbpsVKSndOtgfCwMHFwiPmdw1XjEXGc1eOWXY6qfSp90PIfL6WS7Neh3ba2qMv6WxG3HSOBYvrcCqVTsNxk4UdVm3qb1J0CMVByweTMo45usSkCTdvX3JuEB7tVA6%2BrEk57b3XJd5Phf2AN8hon%2F7lmcXE41kwMQuXq89ViwQmW0G247UFWOQx4t1cmBqFiP6qNA%2F%2BunkZDno1pmAsGnTv7Mz9xtpOaIqKl8BKrVQSTopZ9WcUVzdBUutF19mn1f43BvyA9gIEhcDJHOj&win_price=${AUCTION_PRICE}&auction_mwb=${AUCTION_BID_TO_WIN}&use_pb=1', - 'lurl': 'https://api16-event-sg2.pangle.io/api/ad/union/openrtb/loss/?req_id=233f1693-68d1-470a-ad85-c156c3faaf6fu1450&ttdsp_adx_index=256&rit=980589944&extra=oqveoB%2Bg4%2ByNz9L8wwu%2Fy%2FwKxQsGaKsJHuB4NMK77uqZ9%2FJKpnsVZculJX8%2FxrRBAtaktU1DRN%2Fy6TKAqibCbj%2FM3%2BZ6biAKQG%2BCyt4eIV0KVvri9jCCnaajbkN7YNJWJJw2lW6cJ6Va3SuJG9H7a%2FAJd2PMbhK7fXWhoW72TwgOcKHKBgjM6sNDISBKbWlZyY3L1PhKSX%2FM8LOvL6qahsb%2FDpEObIx24vhQLNWp28XY1L4UqeibuRjam3eCvN7nXoQq74KkJ45QQsTgvV4j6I6EbLOdjOi%2FURhWMDjUD1VCMpqUT%2B6L8ZROgrX9Tp53eJ3bFOczmSTOmDSazKMHa%2B3uZZ7JHcSx32eoY4hfYc99NOJmYBKXNKCmoXyJvS3PCM3PlAz97hKrDMGnVv1wAQ7QGDCbittF0vZwtsRAvvx2mWINNIB3%2FUB2PjhxFsoDA%2BWE2urVZwEdyu%2FJrCznJsMwenXjcbMD5jmUF5vDkkLS%2B7TMDIEawJPJKZ62pK35enrwGxCs6ePXi21rJJkA0bF8tgAdl4mU1illBIVO4kCL%2ByRASskHPjgg%2FcdFe9HP%2Fi8byjAprH%2BhRerN%2FRKFxC3xv8b75x2pb1g7dY%2FTj9IjT0evsBSPVwFNqtKmPId35IcY%2FSXiqPHh%2FrAHZzr5BPsTT19P49SlNMR9UZYTzViX1iJpcCL1UFjuDdrdff%2BhHCviXxo%2FkRmufEF3umHZwxbdDOPAghuZ0DtRCY6S1rnb%2FK9BbpsVKSndOtgfCwMHFwiPmdw1XjEXGc1eOWXY6qfSp90PIfL6WS7Neh3ba2qMv6WxG3HSOBYvrcCqVTsNxk4UdVm3qb1J0CMVByweTMo45usSkCTdvX3JuEB7tVA6%2BrEk57b3XJd5Phf2AN8hon%2F7lmcXE41kwMQuXq89ViwQmW0G247UFWOQx4t1cmBqFiP6qNA%2F%2BunkZDno1pmAsGnTv7Mz9xtpOaIqKl8BKrVQSTopZ9WcUVzdBUutF19mn1f43BvyA9gIEhcDJHOj&reason=${AUCTION_LOSS}&ad_slot_type=8&auction_mwb=${AUCTION_PRICE}&use_pb=1', - 'adm': '', - 'adid': '1780626232977441', - 'adomain': [ - 'swi.esxcmnb.com' - ], - 'iurl': 'https://p16-ttam-va.ibyteimg.com/origin/ad-site-i18n-sg/202310245d0d598b3ff5993c4f129a8b', - 'cid': '1780626232977441', - 'crid': '1780626232977441', - 'attr': [ - 4 - ], - 'w': 640, - 'h': 640, - 'mtype': 1, - 'ext': { - 'pangle': { - 'adtype': 8 - }, - 'event_notification_token': { - 'payload': '980589944:8:1450:7492' - } - } - } - ], - 'seat': 'pangle' - } - ] - } - }; - it('should set mediaType to banner', function() { - const request = spec.buildRequests(multiRequest, bidderRequest)[0]; - const interpretedResponse = spec.interpretResponse(bannerResponse, request); - const bid = interpretedResponse[0]; - expect(bid.mediaType).to.equal('banner'); - }) - it('should set mediaType to video', function() { - const request = spec.buildRequests(multiRequest, bidderRequest)[0]; - const interpretedResponse = spec.interpretResponse(videoResponse, request); - const bid = interpretedResponse[0]; - expect(bid.mediaType).to.equal('video'); - }) -}); diff --git a/test/spec/modules/pgamsspBidAdapter_spec.js b/test/spec/modules/pgamsspBidAdapter_spec.js deleted file mode 100644 index 0766219eda8..00000000000 --- a/test/spec/modules/pgamsspBidAdapter_spec.js +++ /dev/null @@ -1,400 +0,0 @@ -import { expect } from 'chai'; -import { spec } from '../../../modules/pgamsspBidAdapter.js'; -import { BANNER, VIDEO, NATIVE } from '../../../src/mediaTypes.js'; -import { getUniqueIdentifierStr } from '../../../src/utils.js'; - -const bidder = 'pgamssp' - -describe('PGAMBidAdapter', function () { - const bids = [ - { - bidId: getUniqueIdentifierStr(), - bidder: bidder, - mediaTypes: { - [BANNER]: { - sizes: [[300, 250]] - } - }, - params: { - placementId: 'testBanner', - } - }, - { - bidId: getUniqueIdentifierStr(), - bidder: bidder, - mediaTypes: { - [VIDEO]: { - playerSize: [[300, 300]], - minduration: 5, - maxduration: 60 - } - }, - params: { - placementId: 'testVideo', - } - }, - { - bidId: getUniqueIdentifierStr(), - bidder: bidder, - mediaTypes: { - [NATIVE]: { - native: { - title: { - required: true - }, - body: { - required: true - }, - icon: { - required: true, - size: [64, 64] - } - } - } - }, - params: { - placementId: 'testNative', - } - } - ]; - - const invalidBid = { - bidId: getUniqueIdentifierStr(), - bidder: bidder, - mediaTypes: { - [BANNER]: { - sizes: [[300, 250]] - } - }, - params: { - - } - } - - const bidderRequest = { - uspConsent: '1---', - gdprConsent: 'COvFyGBOvFyGBAbAAAENAPCAAOAAAAAAAAAAAEEUACCKAAA.IFoEUQQgAIQwgIwQABAEAAAAOIAACAIAAAAQAIAgEAACEAAAAAgAQBAAAAAAAGBAAgAAAAAAAFAAECAAAgAAQARAEQAAAAAJAAIAAgAAAYQEAAAQmAgBC3ZAYzUw', - refererInfo: { - referer: 'https://test.com' - }, - 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://us-east.pgammedia.com/pbjs'); - }); - - it('Returns general data valid', function () { - let data = serverRequest.data; - expect(data).to.be.an('object'); - expect(data).to.have.all.keys('deviceWidth', - 'deviceHeight', - 'language', - 'secure', - 'host', - 'page', - 'placements', - 'coppa', - 'ccpa', - 'gdpr', - 'tmax' - ); - 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('string'); - 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.an('array'); - - 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); - let data = serverRequest.data; - expect(data.gdpr).to.exist; - expect(data.gdpr).to.be.a('string'); - expect(data.gdpr).to.equal(bidderRequest.gdprConsent); - 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); - let 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; - }); - - it('Returns empty data if no valid requests are passed', function () { - serverRequest = spec.buildRequests([], bidderRequest); - let data = serverRequest.data; - expect(data.placements).to.be.an('array').that.is.empty; - }); - }); - - 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 - } - }] - }; - let bannerResponses = spec.interpretResponse(banner); - expect(bannerResponses).to.be.an('array').that.is.not.empty; - let dataItem = bannerResponses[0]; - expect(dataItem).to.have.all.keys('requestId', 'cpm', 'width', 'height', 'ad', 'ttl', 'creativeId', - 'netRevenue', 'currency', 'dealId', '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 - } - }] - }; - let videoResponses = spec.interpretResponse(video); - expect(videoResponses).to.be.an('array').that.is.not.empty; - - let dataItem = videoResponses[0]; - expect(dataItem).to.have.all.keys('requestId', 'cpm', 'vastUrl', 'ttl', 'creativeId', - 'netRevenue', 'currency', 'dealId', '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 - } - }] - }; - let nativeResponses = spec.interpretResponse(native); - expect(nativeResponses).to.be.an('array').that.is.not.empty; - - let dataItem = nativeResponses[0]; - expect(dataItem).to.have.keys('requestId', 'cpm', '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' - }] - }; - - let serverResponses = spec.interpretResponse(invBanner); - expect(serverResponses).to.be.an('array').that.is.empty; - }); - it('Should return an empty array if invalid video response is passed', function () { - const invVideo = { - body: [{ - mediaType: 'video', - cpm: 0.5, - requestId: '23fhj33i987f', - ttl: 120, - creativeId: '2', - netRevenue: true, - currency: 'USD', - dealId: '1' - }] - }; - let serverResponses = spec.interpretResponse(invVideo); - expect(serverResponses).to.be.an('array').that.is.empty; - }); - it('Should return an empty array if invalid native response is passed', function () { - const invNative = { - body: [{ - mediaType: 'native', - clickUrl: 'test.com', - title: 'Test', - impressionTrackers: ['test.com'], - ttl: 120, - requestId: '23fhj33i987f', - creativeId: '2', - netRevenue: true, - currency: 'USD', - }] - }; - let serverResponses = spec.interpretResponse(invNative); - expect(serverResponses).to.be.an('array').that.is.empty; - }); - it('Should return an empty array if invalid response is passed', function () { - const invalid = { - body: [{ - ttl: 120, - creativeId: '2', - netRevenue: true, - currency: 'USD', - dealId: '1' - }] - }; - let serverResponses = spec.interpretResponse(invalid); - expect(serverResponses).to.be.an('array').that.is.empty; - }); - }); - - describe('getUserSyncs', function() { - it('Should return array of objects with proper sync config , include GDPR', function() { - const syncData = spec.getUserSyncs({}, {}, { - consentString: 'ALL', - gdprApplies: true, - }, {}); - 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://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---' - }); - 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://cs.pgammedia.com/image?pbjs=1&ccpa_consent=1---&coppa=0') - }); - }); -}); diff --git a/test/spec/modules/prebidServerBidAdapter_spec.js b/test/spec/modules/prebidServerBidAdapter_spec.js index d88d4411e33..895c3754876 100644 --- a/test/spec/modules/prebidServerBidAdapter_spec.js +++ b/test/spec/modules/prebidServerBidAdapter_spec.js @@ -6,7 +6,7 @@ import { resetWurlMap, s2sDefaultConfig } from 'modules/prebidServerBidAdapter/index.js'; -import adapterManager, {PBS_ADAPTER_NAME} from 'src/adapterManager.js'; +import adapterManager from 'src/adapterManager.js'; import * as utils from 'src/utils.js'; import {deepAccess, deepClone, mergeDeep} from 'src/utils.js'; import {ajax} from 'src/ajax.js'; @@ -14,6 +14,7 @@ import {config} from 'src/config.js'; import * as events from 'src/events.js'; import CONSTANTS from 'src/constants.json'; import {server} from 'test/mocks/xhr.js'; +import {createEidsArray} from 'modules/userId/eids.js'; import 'modules/appnexusBidAdapter.js'; // appnexus alias test import 'modules/rubiconBidAdapter.js'; // rubicon alias test import 'src/prebid.js'; // $$PREBID_GLOBAL$$.aliasBidder test @@ -26,17 +27,14 @@ import 'modules/consentManagementUsp.js'; import 'modules/schain.js'; import 'modules/fledgeForGpt.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 {addComponentAuction, registerBidder} from 'src/adapters/bidderFactory.js'; import {getGlobal} from '../../../src/prebidGlobal.js'; -import {syncAddFPDToBidderRequest} from '../../helpers/fpd.js'; +import {syncAddFPDEnrichments, syncAddFPDToBidderRequest} 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'; let CONFIG = { accountId: '1', @@ -709,8 +707,8 @@ describe('S2S Adapter', function () { beforeEach(() => { s2sReq = { ...REQUEST, - ortb2Fragments: {global: {}}, - ad_units: REQUEST.ad_units.map(au => ({...au, ortb2Imp: {ext: {tid: 'mock-tid'}}})), + ortb2Fragments: {global: {source: {tid: 'mock-tid'}}}, + ad_units: REQUEST.ad_units.map(au => ({...au, ortb2Imp: {ext: {tid: 'mock-tid'}}})) }; BID_REQUESTS[0].bids[0].ortb2Imp = {ext: {tid: 'mock-tid'}}; }); @@ -723,52 +721,26 @@ describe('S2S Adapter', function () { it('should not be set when transmitTid is not allowed, with ext.prebid.createtids: false', () => { config.setConfig({ s2sConfig: CONFIG, enableTIDs: false }); const req = makeRequest(); - expect(req.source?.tid).to.not.exist; - expect(req.imp[0].ext?.tid).to.not.exist; + expect(req.source.tid).to.not.exist; + expect(req.imp[0].ext.tid).to.not.exist; expect(req.ext.prebid.createtids).to.equal(false); }); - it('should be set to auction ID otherwise', () => { + it('should be picked from FPD otherwise', () => { config.setConfig({s2sConfig: CONFIG, enableTIDs: true}); const req = makeRequest(); - expect(req.source.tid).to.eql(BID_REQUESTS[0].auctionId); + expect(req.source.tid).to.eql('mock-tid'); expect(req.imp[0].ext.tid).to.eql('mock-tid'); }) }) - describe('browsingTopics', () => { - const sandbox = sinon.createSandbox(); - afterEach(() => { - sandbox.restore() - }); - Object.entries({ - 'allowed': true, - 'not allowed': false, - }).forEach(([t, allow]) => { - it(`should be set to ${allow} when transmitUfpd is ${t}`, () => { - sandbox.stub(activityRules, 'isActivityAllowed').callsFake((activity, params) => { - if (activity === ACTIVITY_TRANSMIT_UFPD && params.component === `${MODULE_TYPE_PREBID}.${PBS_ADAPTER_NAME}`) { - return allow; - } - return false; - }); - 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({ - browsingTopics: allow - })); - }); - }); - }) - it('should set tmax to s2sConfig.timeout', () => { const cfg = {...CONFIG, timeout: 123}; config.setConfig({s2sConfig: cfg}); adapter.callBids({...REQUEST, s2sConfig: cfg}, BID_REQUESTS, addBidResponse, done, ajax); const req = JSON.parse(server.requests[0].requestBody); expect(req.tmax).to.eql(123); - }); + }) it('should block request if config did not define p1Consent URL in endpoint object config', function () { let badConfig = utils.deepClone(CONFIG); @@ -876,7 +848,7 @@ describe('S2S Adapter', function () { expect(adapter.callBids).to.exist.and.to.be.a('function'); }); - function mockTCF({applies = true, hasP1Consent = true} = {}) { + function mockConsent({applies = true, hasP1Consent = true} = {}) { return { consentString: 'mockConsent', gdprApplies: applies, @@ -894,7 +866,7 @@ describe('S2S Adapter', function () { config.setConfig(consentConfig); let gdprBidRequest = utils.deepClone(BID_REQUESTS); - gdprBidRequest[0].gdprConsent = mockTCF(); + gdprBidRequest[0].gdprConsent = mockConsent(); adapter.callBids(addFpdEnrichmentsToS2SRequest(REQUEST, gdprBidRequest), gdprBidRequest, addBidResponse, done, ajax); let requestBid = JSON.parse(server.requests[0].requestBody); @@ -917,7 +889,7 @@ describe('S2S Adapter', function () { config.setConfig(consentConfig); let gdprBidRequest = utils.deepClone(BID_REQUESTS); - gdprBidRequest[0].gdprConsent = Object.assign(mockTCF(), { + gdprBidRequest[0].gdprConsent = Object.assign(mockConsent(), { addtlConsent: 'superduperconsent', }); @@ -937,6 +909,69 @@ describe('S2S Adapter', function () { expect(requestBid.regs).to.not.exist; expect(requestBid.user).to.not.exist; }); + + it('check gdpr info gets added into cookie_sync request: have consent data', function () { + let cookieSyncConfig = utils.deepClone(CONFIG); + cookieSyncConfig.syncEndpoint = { p1Consent: 'https://prebid.adnxs.com/pbs/v1/cookie_sync' }; + + let consentConfig = { consentManagement: { cmpApi: 'iab' }, s2sConfig: cookieSyncConfig }; + config.setConfig(consentConfig); + + let gdprBidRequest = utils.deepClone(BID_REQUESTS); + + gdprBidRequest[0].gdprConsent = mockConsent(); + + const s2sBidRequest = utils.deepClone(REQUEST); + s2sBidRequest.s2sConfig = cookieSyncConfig; + + adapter.callBids(s2sBidRequest, gdprBidRequest, addBidResponse, done, ajax); + let requestBid = JSON.parse(server.requests[0].requestBody); + + expect(requestBid.gdpr).is.equal(1); + expect(requestBid.gdpr_consent).is.equal('mockConsent'); + expect(requestBid.bidders).to.contain('appnexus').and.to.have.lengthOf(1); + expect(requestBid.account).is.equal('1'); + }); + + it('check gdpr info gets added into cookie_sync request: have consent data but gdprApplies is false', function () { + let cookieSyncConfig = utils.deepClone(CONFIG); + cookieSyncConfig.syncEndpoint = { p1Consent: 'https://prebid.adnxs.com/pbs/v1/cookie_sync' }; + + let consentConfig = { consentManagement: { cmpApi: 'iab' }, s2sConfig: cookieSyncConfig }; + config.setConfig(consentConfig); + + const s2sBidRequest = utils.deepClone(REQUEST); + s2sBidRequest.s2sConfig = cookieSyncConfig; + + let gdprBidRequest = utils.deepClone(BID_REQUESTS); + gdprBidRequest[0].gdprConsent = mockConsent({applies: false}); + + adapter.callBids(s2sBidRequest, gdprBidRequest, addBidResponse, done, ajax); + let requestBid = JSON.parse(server.requests[0].requestBody); + + expect(requestBid.gdpr).is.equal(0); + expect(requestBid.gdpr_consent).is.undefined; + }); + + it('checks gdpr info gets added to cookie_sync request: applies is false', function () { + let cookieSyncConfig = utils.deepClone(CONFIG); + cookieSyncConfig.syncEndpoint = { p1Consent: 'https://prebid.adnxs.com/pbs/v1/cookie_sync' }; + + let consentConfig = { consentManagement: { cmpApi: 'iab' }, s2sConfig: cookieSyncConfig }; + config.setConfig(consentConfig); + + let gdprBidRequest = utils.deepClone(BID_REQUESTS); + gdprBidRequest[0].gdprConsent = mockConsent({applies: false}); + + const s2sBidRequest = utils.deepClone(REQUEST); + s2sBidRequest.s2sConfig = cookieSyncConfig; + + adapter.callBids(s2sBidRequest, gdprBidRequest, addBidResponse, done, ajax); + let requestBid = JSON.parse(server.requests[0].requestBody); + + expect(requestBid.gdpr).is.equal(0); + expect(requestBid.gdpr_consent).is.undefined; + }); }); describe('us_privacy (ccpa) consent data', function () { @@ -963,6 +998,25 @@ describe('S2S Adapter', function () { expect(requestBid.regs).to.not.exist; }); + + it('is added to cookie_sync request when in bidRequest', function () { + let cookieSyncConfig = utils.deepClone(CONFIG); + cookieSyncConfig.syncEndpoint = { p1Consent: 'https://prebid.adnxs.com/pbs/v1/cookie_sync' }; + config.setConfig({ s2sConfig: cookieSyncConfig }); + + let uspBidRequest = utils.deepClone(BID_REQUESTS); + uspBidRequest[0].uspConsent = '1YNN'; + + const s2sBidRequest = utils.deepClone(REQUEST); + s2sBidRequest.s2sConfig = cookieSyncConfig; + + adapter.callBids(s2sBidRequest, uspBidRequest, addBidResponse, done, ajax); + let requestBid = JSON.parse(server.requests[0].requestBody); + + expect(requestBid.us_privacy).is.equal('1YNN'); + expect(requestBid.bidders).to.contain('appnexus').and.to.have.lengthOf(1); + expect(requestBid.account).is.equal('1'); + }); }); describe('gdpr and us_privacy (ccpa) consent data', function () { @@ -975,7 +1029,7 @@ describe('S2S Adapter', function () { let consentBidRequest = utils.deepClone(BID_REQUESTS); consentBidRequest[0].uspConsent = '1NYN'; - consentBidRequest[0].gdprConsent = mockTCF(); + consentBidRequest[0].gdprConsent = mockConsent(); adapter.callBids(addFpdEnrichmentsToS2SRequest(REQUEST, consentBidRequest), consentBidRequest, addBidResponse, done, ajax); let requestBid = JSON.parse(server.requests[0].requestBody); @@ -1001,7 +1055,7 @@ describe('S2S Adapter', function () { let consentBidRequest = utils.deepClone(BID_REQUESTS); consentBidRequest[0].uspConsent = '1YNN'; - consentBidRequest[0].gdprConsent = mockTCF(); + consentBidRequest[0].gdprConsent = mockConsent(); const s2sBidRequest = utils.deepClone(REQUEST); s2sBidRequest.s2sConfig = cookieSyncConfig @@ -1758,177 +1812,170 @@ describe('S2S Adapter', function () { }]); }); - describe('cookie sync', () => { - let s2sConfig, bidderReqs; + describe('filterSettings', function () { + const getRequestBid = userSync => { + let cookieSyncConfig = utils.deepClone(CONFIG); + const s2sBidRequest = utils.deepClone(REQUEST); + cookieSyncConfig.syncEndpoint = { p1Consent: 'https://prebid.adnxs.com/pbs/v1/cookie_sync' }; + s2sBidRequest.s2sConfig = cookieSyncConfig; - beforeEach(() => { - bidderReqs = utils.deepClone(BID_REQUESTS); - s2sConfig = utils.deepClone(CONFIG); - s2sConfig.syncEndpoint = { p1Consent: 'https://prebid.adnxs.com/pbs/v1/cookie_sync' }; - }) + config.setConfig({ userSync, s2sConfig: cookieSyncConfig }); - function callCookieSync() { - const s2sBidRequest = utils.deepClone(REQUEST); - s2sBidRequest.s2sConfig = s2sConfig; - config.setConfig({ s2sConfig: s2sConfig }); - adapter.callBids(s2sBidRequest, bidderReqs, addBidResponse, done, ajax); + let bidRequest = utils.deepClone(BID_REQUESTS); + adapter.callBids(s2sBidRequest, bidRequest, addBidResponse, done, ajax); return JSON.parse(server.requests[0].requestBody); } - describe('filterSettings', function () { - it('correctly adds filterSettings to the cookie_sync request if userSync.filterSettings is present in the config and only the all key is present in userSync.filterSettings', function () { - config.setConfig({ - userSync: { - filterSettings: { - all: { - bidders: ['appnexus', 'rubicon', 'pubmatic'], - filter: 'exclude' - } - } + it('correctly adds filterSettings to the cookie_sync request if userSync.filterSettings is present in the config and only the all key is present in userSync.filterSettings', function () { + const userSync = { + filterSettings: { + all: { + bidders: ['appnexus', 'rubicon', 'pubmatic'], + filter: 'exclude' } - }); - expect(callCookieSync().filterSettings).to.deep.equal({ - 'image': { - 'bidders': ['appnexus', 'rubicon', 'pubmatic'], - 'filter': 'exclude' - }, - 'iframe': { - 'bidders': ['appnexus', 'rubicon', 'pubmatic'], - 'filter': 'exclude' - } - }); + } + }; + const requestBid = getRequestBid(userSync); + + expect(requestBid.filterSettings).to.deep.equal({ + 'image': { + 'bidders': ['appnexus', 'rubicon', 'pubmatic'], + 'filter': 'exclude' + }, + 'iframe': { + 'bidders': ['appnexus', 'rubicon', 'pubmatic'], + 'filter': 'exclude' + } }); + }); - it('correctly adds filterSettings to the cookie_sync request if userSync.filterSettings is present in the config and only the iframe key is present in userSync.filterSettings', function () { - config.setConfig({ - userSync: { - filterSettings: { - iframe: { - bidders: ['rubicon', 'pubmatic'], - filter: 'include' - } - } + it('correctly adds filterSettings to the cookie_sync request if userSync.filterSettings is present in the config and only the iframe key is present in userSync.filterSettings', function () { + const userSync = { + filterSettings: { + iframe: { + bidders: ['rubicon', 'pubmatic'], + filter: 'include' } - }) + } + }; + const requestBid = getRequestBid(userSync); - expect(callCookieSync().filterSettings).to.deep.equal({ - 'image': { - 'bidders': '*', - 'filter': 'include' - }, - 'iframe': { - 'bidders': ['rubicon', 'pubmatic'], - 'filter': 'include' - } - }); + expect(requestBid.filterSettings).to.deep.equal({ + 'image': { + 'bidders': '*', + 'filter': 'include' + }, + 'iframe': { + 'bidders': ['rubicon', 'pubmatic'], + 'filter': 'include' + } }); + }); - it('correctly adds filterSettings to the cookie_sync request if userSync.filterSettings is present in the config and the image and iframe keys are both present in userSync.filterSettings', function () { - config.setConfig({ - userSync: { - filterSettings: { - image: { - bidders: ['triplelift', 'appnexus'], - filter: 'include' - }, - iframe: { - bidders: ['pulsepoint', 'triplelift', 'appnexus', 'rubicon'], - filter: 'exclude' - } - } - } - }) - - expect(callCookieSync().filterSettings).to.deep.equal({ - 'image': { - 'bidders': ['triplelift', 'appnexus'], - 'filter': 'include' + it('correctly adds filterSettings to the cookie_sync request if userSync.filterSettings is present in the config and the image and iframe keys are both present in userSync.filterSettings', function () { + const userSync = { + filterSettings: { + image: { + bidders: ['triplelift', 'appnexus'], + filter: 'include' }, - 'iframe': { - 'bidders': ['pulsepoint', 'triplelift', 'appnexus', 'rubicon'], - 'filter': 'exclude' + iframe: { + bidders: ['pulsepoint', 'triplelift', 'appnexus', 'rubicon'], + filter: 'exclude' } - }); - }); + } + }; + const requestBid = getRequestBid(userSync); - it('correctly adds filterSettings to the cookie_sync request if userSync.filterSettings is present in the config and the all and iframe keys are both present in userSync.filterSettings', function () { - config.setConfig({ - userSync: { - filterSettings: { - all: { - bidders: ['triplelift', 'appnexus'], - filter: 'include' - }, - iframe: { - bidders: ['pulsepoint', 'triplelift', 'appnexus', 'rubicon'], - filter: 'exclude' - } - } - } - }) + expect(requestBid.filterSettings).to.deep.equal({ + 'image': { + 'bidders': ['triplelift', 'appnexus'], + 'filter': 'include' + }, + 'iframe': { + 'bidders': ['pulsepoint', 'triplelift', 'appnexus', 'rubicon'], + 'filter': 'exclude' + } + }); + }); - expect(callCookieSync().filterSettings).to.deep.equal({ - 'image': { - 'bidders': ['triplelift', 'appnexus'], - 'filter': 'include' + it('correctly adds filterSettings to the cookie_sync request if userSync.filterSettings is present in the config and the all and iframe keys are both present in userSync.filterSettings', function () { + const userSync = { + filterSettings: { + all: { + bidders: ['triplelift', 'appnexus'], + filter: 'include' }, - 'iframe': { - 'bidders': ['pulsepoint', 'triplelift', 'appnexus', 'rubicon'], - 'filter': 'exclude' + iframe: { + bidders: ['pulsepoint', 'triplelift', 'appnexus', 'rubicon'], + filter: 'exclude' } - }); + } + }; + const requestBid = getRequestBid(userSync); + + expect(requestBid.filterSettings).to.deep.equal({ + 'image': { + 'bidders': ['triplelift', 'appnexus'], + 'filter': 'include' + }, + 'iframe': { + 'bidders': ['pulsepoint', 'triplelift', 'appnexus', 'rubicon'], + 'filter': 'exclude' + } }); }); + }); - describe('limit', () => { - it('is added to request if userSyncLimit is greater than 0', function () { - s2sConfig.userSyncLimit = 1; - const req = callCookieSync(); - expect(req.limit).is.equal(1); - }); + it('adds limit to the cookie_sync request if userSyncLimit is greater than 0', function () { + let cookieSyncConfig = utils.deepClone(CONFIG); + cookieSyncConfig.syncEndpoint = { p1Consent: 'https://prebid.adnxs.com/pbs/v1/cookie_sync' }; + cookieSyncConfig.userSyncLimit = 1; - Object.entries({ - 'missing': () => null, - '0': () => { s2sConfig.userSyncLimit = 0; } - }).forEach(([t, setup]) => { - it(`is not added to request if userSyncLimit is ${t}`, () => { - setup(); - const req = callCookieSync(); - expect(req.limit).to.not.exist; - }); - }); - }); + const s2sBidRequest = utils.deepClone(REQUEST); + s2sBidRequest.s2sConfig = cookieSyncConfig; - describe('gdpr data is set', () => { - it('when we have consent data', function () { - bidderReqs[0].gdprConsent = mockTCF(); - const req = callCookieSync(); - expect(req.gdpr).is.equal(1); - expect(req.gdpr_consent).is.equal('mockConsent'); - }); + config.setConfig({ s2sConfig: cookieSyncConfig }); - it('when gdprApplies is false', () => { - bidderReqs[0].gdprConsent = mockTCF({applies: false}); - const req = callCookieSync(); - expect(req.gdpr).is.equal(0); - expect(req.gdpr_consent).is.undefined; - }); - }); + let bidRequest = utils.deepClone(BID_REQUESTS); + adapter.callBids(s2sBidRequest, bidRequest, addBidResponse, done, ajax); + let requestBid = JSON.parse(server.requests[0].requestBody); - it('adds USP data from bidder request', () => { - bidderReqs[0].uspConsent = '1YNN'; - expect(callCookieSync().us_privacy).to.equal('1YNN'); - }); + expect(requestBid.bidders).to.contain('appnexus').and.to.have.lengthOf(1); + expect(requestBid.account).is.equal('1'); + expect(requestBid.limit).is.equal(1); + }); - it('adds GPP data from bidder requests', () => { - bidderReqs[0].gppConsent = { - applicableSections: [1, 2, 3], - gppString: 'mock-string' - }; - const req = callCookieSync(); - expect(req.gpp).to.eql('mock-string'); - expect(req.gpp_sid).to.eql('1,2,3'); - }); + it('does not add limit to cooke_sync request if userSyncLimit is missing or 0', function () { + let cookieSyncConfig = utils.deepClone(CONFIG); + cookieSyncConfig.syncEndpoint = { p1Consent: 'https://prebid.adnxs.com/pbs/v1/cookie_sync' }; + config.setConfig({ s2sConfig: cookieSyncConfig }); + + const s2sBidRequest = utils.deepClone(REQUEST); + s2sBidRequest.s2sConfig = cookieSyncConfig; + + let bidRequest = utils.deepClone(BID_REQUESTS); + adapter.callBids(s2sBidRequest, bidRequest, addBidResponse, done, ajax); + let requestBid = JSON.parse(server.requests[0].requestBody); + + expect(requestBid.bidders).to.contain('appnexus').and.to.have.lengthOf(1); + expect(requestBid.account).is.equal('1'); + expect(requestBid.limit).is.undefined; + + cookieSyncConfig.userSyncLimit = 0; + config.resetConfig(); + config.setConfig({ s2sConfig: cookieSyncConfig }); + + const s2sBidRequest2 = utils.deepClone(REQUEST); + s2sBidRequest2.s2sConfig = cookieSyncConfig; + + bidRequest = utils.deepClone(BID_REQUESTS); + adapter.callBids(s2sBidRequest2, bidRequest, addBidResponse, done, ajax); + requestBid = JSON.parse(server.requests[0].requestBody); + + expect(requestBid.bidders).to.contain('appnexus').and.to.have.lengthOf(1); + expect(requestBid.account).is.equal('1'); + expect(requestBid.limit).is.undefined; }); it('adds s2sConfig adapterOptions to request for ORTB', function () { @@ -2035,12 +2082,30 @@ describe('S2S Adapter', function () { } } }; - userIdBidRequest[0].bids[0].userIdAsEids = [{id: 1}, {id: 2}]; + userIdBidRequest[0].bids[0].userIdAsEids = createEidsArray(userIdBidRequest[0].bids[0].userId); adapter.callBids(REQUEST, userIdBidRequest, addBidResponse, done, ajax); let requestBid = JSON.parse(server.requests[0].requestBody); expect(typeof requestBid.user.ext.eids).is.equal('object'); - expect(requestBid.user.ext.eids).to.eql([{id: 1}, {id: 2}]); + expect(Array.isArray(requestBid.user.ext.eids)).to.be.true; + expect(requestBid.user.ext.eids.filter(eid => eid.source === 'adserver.org')).is.not.empty; + expect(requestBid.user.ext.eids.filter(eid => eid.source === 'adserver.org')[0].uids[0].id).is.equal('abc123'); + expect(requestBid.user.ext.eids.filter(eid => eid.source === 'criteo.com')).is.not.empty; + expect(requestBid.user.ext.eids.filter(eid => eid.source === 'criteo.com')[0].uids[0].id).is.equal('44VmRDeUE3ZGJ5MzRkRVJHU3BIUlJ6TlFPQUFU'); + expect(requestBid.user.ext.eids.filter(eid => eid.source === 'pubcid.org')).is.not.empty; + expect(requestBid.user.ext.eids.filter(eid => eid.source === 'pubcid.org')[0].uids[0].id).is.equal('1234'); + expect(requestBid.user.ext.eids.filter(eid => eid.source === 'parrable.com')).is.not.empty; + expect(requestBid.user.ext.eids.filter(eid => eid.source === 'parrable.com')[0].uids[0].id).is.equal('01.1563917337.test-eid'); + expect(requestBid.user.ext.eids.filter(eid => eid.source === 'liveintent.com')).is.not.empty; + expect(requestBid.user.ext.eids.filter(eid => eid.source === 'liveintent.com')[0].uids[0].id).is.equal('li-xyz'); + expect(requestBid.user.ext.eids.filter(eid => eid.source === 'liveintent.com')[0].ext.segments.length).is.equal(2); + expect(requestBid.user.ext.eids.filter(eid => eid.source === 'liveintent.com')[0].ext.segments[0]).is.equal('segA'); + expect(requestBid.user.ext.eids.filter(eid => eid.source === 'liveintent.com')[0].ext.segments[1]).is.equal('segB'); + expect(requestBid.user.ext.eids.filter(eid => eid.source === 'id5-sync.com')).is.not.empty; + expect(requestBid.user.ext.eids.filter(eid => eid.source === 'id5-sync.com')[0].uids[0].id).is.equal('11111'); + expect(requestBid.user.ext.eids.filter(eid => eid.source === 'id5-sync.com')[0].uids[0].ext.linkType).is.equal('some-link-type'); + // LiveRamp should exist + expect(requestBid.user.ext.eids.filter(eid => eid.source === 'liveramp.com')[0].uids[0].id).is.equal('0000-1111-2222-3333'); }); it('when config \'currency.adServerCurrency\' value is a string: ORTB has property \'cur\' value set to a single item array', function () { @@ -2051,7 +2116,7 @@ describe('S2S Adapter', function () { const bidRequests = utils.deepClone(BID_REQUESTS); adapter.callBids(REQUEST, bidRequests, addBidResponse, done, ajax); - const parsedRequestBody = JSON.parse(server.requests.find(req => req.method === 'POST').requestBody); + const parsedRequestBody = JSON.parse(server.requests[1].requestBody); expect(parsedRequestBody.cur).to.deep.equal(['NZ']); }); @@ -2873,41 +2938,6 @@ describe('S2S Adapter', function () { events.emit.restore(); }); - it('triggers BIDDER_ERROR on server error', () => { - config.setConfig({ s2sConfig: CONFIG }); - adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, ajax); - server.requests[0].respond(400, {}, {}); - BID_REQUESTS.forEach(bidderRequest => { - sinon.assert.calledWith(events.emit, CONSTANTS.EVENTS.BIDDER_ERROR, sinon.match({bidderRequest})) - }) - }) - - describe('calls done', () => { - let success, error; - beforeEach(() => { - const mockAjax = function (_, callback) { - ({success, error} = callback); - } - config.setConfig({ s2sConfig: CONFIG }); - adapter.callBids(REQUEST, BID_REQUESTS, addBidResponse, done, mockAjax); - }) - - it('passing timedOut = false on succcess', () => { - success({}); - sinon.assert.calledWith(done, false); - }); - - Object.entries({ - 'timeouts': true, - 'other errors': false - }).forEach(([t, timedOut]) => { - it(`passing timedOut = ${timedOut} on ${t}`, () => { - error('', {timedOut}); - sinon.assert.calledWith(done, timedOut); - }) - }) - }) - // TODO: test dependent on pbjs_api_spec. Needs to be isolated it('does not call addBidResponse and calls done when ad unit not set', function () { config.setConfig({ s2sConfig: CONFIG }); @@ -3448,6 +3478,29 @@ describe('S2S Adapter', function () { }); }); describe('when the response contains ext.prebid.fledge', () => { + let fledgeStub, request, bidderRequests; + + function fledgeHook(next, ...args) { + fledgeStub(...args); + } + + before(() => { + addComponentAuction.before(fledgeHook); + }); + + after(() => { + addComponentAuction.getHooks({hook: fledgeHook}).remove(); + }) + + beforeEach(function () { + fledgeStub = sinon.stub(); + config.setConfig({CONFIG}); + request = deepClone(REQUEST); + request.ad_units.forEach(au => deepSetValue(au, 'ortb2Imp.ext.ae', 1)); + bidderRequests = deepClone(BID_REQUESTS); + bidderRequests.forEach(req => req.fledgeEnabled = true); + }); + const AU = 'div-gpt-ad-1460505748561-0'; const FLEDGE_RESP = { ext: { @@ -3456,14 +3509,12 @@ describe('S2S Adapter', function () { auctionconfigs: [ { impid: AU, - bidder: 'appnexus', config: { id: 1 } }, { impid: AU, - bidder: 'other', config: { id: 2 } @@ -3474,62 +3525,20 @@ describe('S2S Adapter', function () { } } - let fledgeStub, request, bidderRequests; - - function fledgeHook(next, ...args) { - fledgeStub(...args); - } - - before(() => { - addComponentAuction.before(fledgeHook); - }); - - after(() => { - addComponentAuction.getHooks({hook: fledgeHook}).remove(); - }) - - beforeEach(function () { - fledgeStub = sinon.stub(); - config.setConfig({CONFIG}); - bidderRequests = deepClone(BID_REQUESTS); - AU - bidderRequests.forEach(req => { - Object.assign(req, { - fledgeEnabled: 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}), {id: 1}) - sinon.assert.calledWith(fledgeStub, sinon.match({auctionId, adUnitCode: AU, ortb2: undefined, ortb2Imp: undefined}), {id: 2}) - } - it('calls addComponentAuction 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(); + sinon.assert.calledWith(fledgeStub, AU, {id: 1}); + sinon.assert.calledWith(fledgeStub, AU, {id: 2}); }); it('calls addComponentAuction 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(); + sinon.assert.calledWith(fledgeStub, AU, {id: 1}); + sinon.assert.calledWith(fledgeStub, AU, {id: 2}); }) }); }); @@ -3679,11 +3688,11 @@ describe('S2S Adapter', function () { sinon.assert.calledOnce(logErrorSpy); }); - it('should configure the s2sConfig object with appnexuspsp vendor defaults unless specified by user', function () { + it('should configure the s2sConfig object with appnexus vendor defaults unless specified by user', function () { const options = { accountId: '123', bidders: ['appnexus'], - defaultVendor: 'appnexuspsp', + defaultVendor: 'appnexus', timeout: 750 }; @@ -3696,8 +3705,8 @@ describe('S2S Adapter', function () { expect(vendorConfig.bidders).to.deep.equal(['appnexus']); expect(vendorConfig.enabled).to.be.true; expect(vendorConfig.endpoint).to.deep.equal({ - p1Consent: 'https://ib.adnxs.com/openrtb2/prebid', - noP1Consent: 'https://ib.adnxs-simple.com/openrtb2/prebid' + p1Consent: 'https://prebid.adnxs.com/pbs/v1/openrtb2/auction', + noP1Consent: 'https://prebid.adnxs-simple.com/pbs/v1/openrtb2/auction' }); expect(vendorConfig.syncEndpoint).to.deep.equal({ p1Consent: 'https://prebid.adnxs.com/pbs/v1/cookie_sync', @@ -3706,6 +3715,30 @@ describe('S2S Adapter', function () { expect(vendorConfig).to.have.property('timeout', 750); }); + it('should configure the s2sConfig object with appnexuspsp vendor defaults unless specified by user', function () { + const options = { + accountId: '123', + bidders: ['appnexus'], + defaultVendor: 'appnexuspsp', + timeout: 750 + }; + + config.setConfig({ s2sConfig: options }); + sinon.assert.notCalled(logErrorSpy); + + let vendorConfig = config.getConfig('s2sConfig'); + expect(vendorConfig).to.have.property('accountId', '123'); + expect(vendorConfig).to.have.property('adapter', 'prebidServer'); + expect(vendorConfig.bidders).to.deep.equal(['appnexus']); + expect(vendorConfig.enabled).to.be.true; + expect(vendorConfig.endpoint).to.deep.equal({ + p1Consent: 'https://ib.adnxs.com/openrtb2/prebid', + noP1Consent: 'https://ib.adnxs-simple.com/openrtb2/prebid' + }); + expect(vendorConfig.syncEndpoint).to.be.undefined; + expect(vendorConfig).to.have.property('timeout', 750); + }); + it('should configure the s2sConfig object with rubicon vendor defaults unless specified by user', function () { const options = { accountId: 'abc', @@ -3787,74 +3820,6 @@ describe('S2S Adapter', function () { }) }); - it('should configure the s2sConfig object with openwrap vendor defaults unless specified by user', function () { - const options = { - accountId: '1234', - bidders: ['pubmatic'], - defaultVendor: 'openwrap' - }; - - config.setConfig({ s2sConfig: options }); - sinon.assert.notCalled(logErrorSpy); - - let vendorConfig = config.getConfig('s2sConfig'); - expect(vendorConfig).to.have.property('accountId', '1234'); - expect(vendorConfig).to.have.property('adapter', 'prebidServer'); - expect(vendorConfig.bidders).to.deep.equal(['pubmatic']); - expect(vendorConfig.enabled).to.be.true; - expect(vendorConfig.endpoint).to.deep.equal({ - p1Consent: 'https://ow.pubmatic.com/openrtb2/auction?source=pbjs', - noP1Consent: 'https://ow.pubmatic.com/openrtb2/auction?source=pbjs' - }); - expect(vendorConfig).to.have.property('timeout', 500); - }); - - it('should return proper defaults', function () { - const options = { - accountId: '1234', - bidders: ['pubmatic'], - defaultVendor: 'openwrap', - timeout: 500 - }; - - config.setConfig({ s2sConfig: options }); - expect(config.getConfig('s2sConfig')).to.deep.equal({ - 'accountId': '1234', - 'adapter': 'prebidServer', - 'bidders': ['pubmatic'], - 'defaultVendor': 'openwrap', - 'enabled': true, - 'endpoint': { - p1Consent: 'https://ow.pubmatic.com/openrtb2/auction?source=pbjs', - noP1Consent: 'https://ow.pubmatic.com/openrtb2/auction?source=pbjs' - }, - 'timeout': 500 - }) - }); - - it('should return default adapterOptions if not set', function () { - config.setConfig({ - s2sConfig: { - accountId: '1234', - bidders: ['pubmatic'], - defaultVendor: 'openwrap', - timeout: 500 - } - }); - expect(config.getConfig('s2sConfig')).to.deep.equal({ - enabled: true, - timeout: 500, - adapter: 'prebidServer', - accountId: '1234', - bidders: ['pubmatic'], - defaultVendor: 'openwrap', - endpoint: { - p1Consent: 'https://ow.pubmatic.com/openrtb2/auction?source=pbjs', - noP1Consent: 'https://ow.pubmatic.com/openrtb2/auction?source=pbjs' - }, - }) - }); - it('should set adapterOptions', function () { config.setConfig({ s2sConfig: { diff --git a/test/spec/modules/precisoBidAdapter_spec.js b/test/spec/modules/precisoBidAdapter_spec.js deleted file mode 100644 index 78a1615a02e..00000000000 --- a/test/spec/modules/precisoBidAdapter_spec.js +++ /dev/null @@ -1,162 +0,0 @@ -import { expect } from 'chai'; -import { spec } from '../../../modules/precisoBidAdapter.js'; -import { config } from '../../../src/config.js'; - -const DEFAULT_PRICE = 1 -const DEFAULT_CURRENCY = 'USD' -const DEFAULT_BANNER_WIDTH = 300 -const DEFAULT_BANNER_HEIGHT = 250 -const BIDDER_CODE = 'preciso'; - -describe('PrecisoAdapter', function () { - let bid = { - bidId: '23fhj33i987f', - bidder: 'preciso', - mediaTypes: { - banner: { - sizes: [[DEFAULT_BANNER_WIDTH, DEFAULT_BANNER_HEIGHT]] - } - }, - params: { - host: 'prebid', - sourceid: '0', - publisherId: '0', - mediaType: 'banner', - region: 'prebid-eu' - - }, - userId: { - pubcid: '12355454test' - - }, - geo: 'NA', - city: 'Asia,delhi' - }; - - describe('isBidRequestValid', function () { - it('Should return true if there are bidId, params and sourceid parameters present', function () { - expect(spec.isBidRequestValid(bid)).to.be.true; - }); - it('Should return false if at least one of parameters is not present', function () { - delete bid.params.publisherId; - expect(spec.isBidRequestValid(bid)).to.be.false; - }); - }); - - describe('buildRequests', function () { - let serverRequest = spec.buildRequests([bid]); - it('Creates a ServerRequest object with method, URL and data', function () { - expect(serverRequest).to.exist; - expect(serverRequest.method).to.exist; - expect(serverRequest.url).to.exist; - expect(serverRequest.data).to.exist; - }); - it('Returns POST method', function () { - expect(serverRequest.method).to.equal('POST'); - }); - it('Returns valid URL', function () { - expect(serverRequest.url).to.equal('https://ssp-bidder.mndtrk.com/bid_request/openrtb'); - }); - it('Returns valid data if array of bids is valid', function () { - let data = serverRequest.data; - // expect(data).to.be.an('object'); - - // expect(data).to.have.all.keys('bidId', 'imp', 'site', 'deviceWidth', 'deviceHeight', 'language', 'secure', 'host', 'page', 'placements', 'coppa'); - - expect(data.deviceWidth).to.be.a('number'); - expect(data.deviceHeight).to.be.a('number'); - expect(data.coppa).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.city).to.be.a('string'); - expect(data.geo).to.be.a('object'); - // expect(data.userId).to.be.a('string'); - // expect(data.imp).to.be.a('object'); - }); - // it('Returns empty data if no valid requests are passed', function () { - /// serverRequest = spec.buildRequests([]); - // let data = serverRequest.data; - // expect(data.imp).to.be.an('array').that.is.empty; - // }); - }); - - describe('with COPPA', function () { - beforeEach(function () { - sinon.stub(config, 'getConfig') - .withArgs('coppa') - .returns(true); - }); - afterEach(function () { - config.getConfig.restore(); - }); - - it('should send the Coppa "required" flag set to "1" in the request', function () { - let serverRequest = spec.buildRequests([bid]); - expect(serverRequest.data.coppa).to.equal(1); - }); - }); - - describe('interpretResponse', function () { - it('should get correct bid response', function () { - let response = { - - bidderRequestId: 'f6adb85f-4e19-45a0-b41e-2a5b9a48f23a', - - seatbid: [ - { - bid: [ - { - id: '123', - impid: 'b4f290d7-d4ab-4778-ab94-2baf06420b22', - price: DEFAULT_PRICE, - adm: 'hi', - cid: 'test_cid', - crid: 'test_banner_crid', - w: DEFAULT_BANNER_WIDTH, - h: DEFAULT_BANNER_HEIGHT, - adomain: [], - } - ], - seat: BIDDER_CODE - } - ], - } - - let expectedResponse = [ - { - requestId: 'b4f290d7-d4ab-4778-ab94-2baf06420b22', - cpm: DEFAULT_PRICE, - width: DEFAULT_BANNER_WIDTH, - height: DEFAULT_BANNER_HEIGHT, - creativeId: 'test_banner_crid', - ad: 'hi', - currency: DEFAULT_CURRENCY, - netRevenue: true, - ttl: 300, - meta: { advertiserDomains: [] }, - } - ] - let result = spec.interpretResponse({ body: response }) - - expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])) - }) - }) - describe('getUserSyncs', function () { - const syncUrl = 'https://ck.2trk.info/rtb/user/usersync.aspx?id=NA&gdpr=0&gdpr_consent=&us_privacy=&t=4'; - const syncOptions = { - iframeEnabled: true - }; - let userSync = spec.getUserSyncs(syncOptions); - it('Returns valid URL and type', function () { - expect(userSync).to.be.an('array').with.lengthOf(1); - expect(userSync[0].type).to.exist; - expect(userSync[0].url).to.exist; - expect(userSync).to.deep.equal([ - { type: 'iframe', url: syncUrl } - ]); - }); - }); -}); diff --git a/test/spec/modules/priceFloors_spec.js b/test/spec/modules/priceFloors_spec.js index a31f07fecef..e9ce4c8ccd3 100644 --- a/test/spec/modules/priceFloors_spec.js +++ b/test/spec/modules/priceFloors_spec.js @@ -12,7 +12,7 @@ import { isFloorsDataValid, addBidResponseHook, fieldMatchingFunctions, - allowedFields, parseFloorData, normalizeDefault, getFloorDataFromAdUnits, updateAdUnitsForAuction, createFloorsDataForAuction + allowedFields } from 'modules/priceFloors.js'; import * as events from 'src/events.js'; import * as mockGpt from '../integration/faker/googletag.js'; @@ -20,9 +20,6 @@ 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 * as activities from '../../../src/activities/rules.js'; -import {server} from '../../mocks/xhr.js'; describe('the price floors module', function () { let logErrorSpy; @@ -123,7 +120,7 @@ describe('the price floors module', function () { return { code, mediaTypes: {banner: { sizes: [[300, 200], [300, 600]] }, native: {}}, - bids: [{bidder: 'someBidder', adUnitCode: code}, {bidder: 'someOtherBidder', adUnitCode: code}] + bids: [{bidder: 'someBidder'}, {bidder: 'someOtherBidder'}] }; } beforeEach(function() { @@ -143,76 +140,6 @@ describe('the price floors module', function () { getGlobal().bidderSettings = {}; }); - describe('parseFloorData', () => { - it('should accept just a default floor', () => { - const fd = parseFloorData({ - default: 1.23 - }); - expect(getFirstMatchingFloor(fd, {}, {}).matchingFloor).to.eql(1.23); - }); - }); - - describe('getFloorDataFromAdUnits', () => { - let adUnits; - - function setFloorValues(rule) { - adUnits.forEach((au, i) => { - au.floors = { - values: { - [rule]: i + 1 - } - } - }) - } - - beforeEach(() => { - adUnits = ['au1', 'au2', 'au3'].map(getAdUnitMock); - }) - - it('should use one schema for all adUnits', () => { - setFloorValues('*;*') - adUnits[1].floors.schema = { - fields: ['mediaType', 'gptSlot'], - delimiter: ';' - } - sinon.assert.match(getFloorDataFromAdUnits(adUnits), { - schema: { - fields: ['adUnitCode', 'mediaType', 'gptSlot'], - delimiter: ';' - }, - values: { - 'au1;*;*': 1, - 'au2;*;*': 2, - 'au3;*;*': 3 - } - }) - }); - it('should ignore adUnits that declare different schema', () => { - setFloorValues('*|*'); - adUnits[0].floors.schema = { - fields: ['mediaType', 'gptSlot'] - }; - adUnits[2].floors.schema = { - fields: ['gptSlot', 'mediaType'] - }; - expect(getFloorDataFromAdUnits(adUnits).values).to.eql({ - 'au1|*|*': 1, - 'au2|*|*': 2 - }) - }); - it('should ignore adUnits that declare no values', () => { - setFloorValues('*'); - adUnits[0].floors.schema = { - fields: ['mediaType'] - }; - delete adUnits[2].floors.values; - expect(getFloorDataFromAdUnits(adUnits).values).to.eql({ - 'au1|*': 1, - 'au2|*': 2, - }) - }) - }) - describe('getFloorsDataForAuction', function () { it('converts basic input floor data into a floorData map for the auction correctly', function () { // basic input where nothing needs to be updated @@ -303,8 +230,8 @@ describe('the price floors module', function () { }); describe('getFirstMatchingFloor', function () { - it('uses a 0 floor as override', function () { - let inputFloorData = normalizeDefault({ + it('uses a 0 floor as overrite', function () { + let inputFloorData = { currency: 'USD', schema: { delimiter: '|', @@ -315,7 +242,7 @@ describe('the price floors module', function () { 'test_div_2': 2 }, default: 0.5 - }); + }; expect(getFirstMatchingFloor(inputFloorData, basicBidRequest, {mediaType: 'banner', size: '*'})).to.deep.equal({ floorMin: 0, @@ -504,7 +431,7 @@ describe('the price floors module', function () { }); }); it('selects the right floor for more complex rules', function () { - let inputFloorData = normalizeDefault({ + let inputFloorData = { currency: 'USD', schema: { delimiter: '^', @@ -518,7 +445,7 @@ describe('the price floors module', function () { 'weird_div^*^300x250': 5.5 }, default: 0.5 - }); + }; // banner with 300x250 size expect(getFirstMatchingFloor(inputFloorData, basicBidRequest, {mediaType: 'banner', size: [300, 250]})).to.deep.equal({ floorMin: 0, @@ -560,8 +487,10 @@ describe('the price floors module', function () { 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); + inputFloorData = { default: 5.0 }; + expect(getFirstMatchingFloor(inputFloorData, basicBidRequest, {mediaType: 'banner', size: '*'})).to.deep.equal({ + matchingFloor: 5.0 + }); }); describe('with gpt enabled', function () { let gptFloorData; @@ -648,90 +577,6 @@ describe('the price floors module', function () { }); }); }); - - describe('updateAdUnitsForAuction', function() { - let inputFloorData; - let adUnits; - - beforeEach(function() { - adUnits = [getAdUnitMock()]; - inputFloorData = utils.deepClone(minFloorConfigLow); - inputFloorData.skipRate = 0.5; - }); - - it('should set the skipRate to the skipRate from the data property before using the skipRate from floorData directly', function() { - utils.deepSetValue(inputFloorData, 'data', { - skipRate: 0.7 - }); - updateAdUnitsForAuction(adUnits, inputFloorData, 'id'); - - const skipRate = utils.deepAccess(adUnits, '0.bids.0.floorData.skipRate'); - expect(skipRate).to.equal(0.7); - }); - - it('should set the skipRate to the skipRate from floorData directly if it does not exist in the data property of floorData', function() { - updateAdUnitsForAuction(adUnits, inputFloorData, 'id'); - - const skipRate = utils.deepAccess(adUnits, '0.bids.0.floorData.skipRate'); - expect(skipRate).to.equal(0.5); - }); - - it('should set the skipRate in the bid floorData to undefined if both skipRate and skipRate in the data property are undefined', function() { - inputFloorData.skipRate = undefined; - utils.deepSetValue(inputFloorData, 'data', { - skipRate: undefined, - }); - updateAdUnitsForAuction(adUnits, inputFloorData, 'id'); - - const skipRate = utils.deepAccess(adUnits, '0.bids.0.floorData.skipRate'); - expect(skipRate).to.equal(undefined); - }); - }); - - describe('createFloorsDataForAuction', function() { - let adUnits; - let floorConfig; - - beforeEach(function() { - adUnits = [getAdUnitMock()]; - floorConfig = utils.deepClone(basicFloorConfig); - }); - - it('should return skipRate as 0 if both skipRate and skipRate in the data property are undefined', function() { - floorConfig.skipRate = undefined; - floorConfig.data.skipRate = undefined; - handleSetFloorsConfig(floorConfig); - - const floorData = createFloorsDataForAuction(adUnits, 'id'); - - expect(floorData.skipRate).to.equal(0); - expect(floorData.skipped).to.equal(false); - }); - - it('should properly set skipRate if it is available in the data property', function() { - // this will force skipped to be true - floorConfig.skipRate = 101; - floorConfig.data.skipRate = 201; - handleSetFloorsConfig(floorConfig); - - const floorData = createFloorsDataForAuction(adUnits, 'id'); - - expect(floorData.data.skipRate).to.equal(201); - expect(floorData.skipped).to.equal(true); - }); - - it('should should use the skipRate if its not available in the data property ', function() { - // this will force skipped to be true - floorConfig.skipRate = 101; - handleSetFloorsConfig(floorConfig); - - const floorData = createFloorsDataForAuction(adUnits, 'id'); - - expect(floorData.skipRate).to.equal(101); - expect(floorData.skipped).to.equal(true); - }); - }); - describe('pre-auction tests', function () { let exposedAdUnits; const validateBidRequests = (getFloorExpected, FloorDataExpected) => { @@ -746,11 +591,16 @@ describe('the price floors module', function () { adUnits, }); }; + let fakeFloorProvider; let actualAllowedFields = allowedFields; let actualFieldMatchingFunctions = fieldMatchingFunctions; const defaultAllowedFields = [...allowedFields]; const defaultMatchingFunctions = {...fieldMatchingFunctions}; + beforeEach(function() { + fakeFloorProvider = sinon.fakeServer.create(); + }); afterEach(function() { + fakeFloorProvider.restore(); exposedAdUnits = undefined; actualAllowedFields = [...defaultAllowedFields]; actualFieldMatchingFunctions = {...defaultMatchingFunctions}; @@ -773,124 +623,6 @@ describe('the price floors module', function () { floorProvider: undefined }); }); - it('should not do floor stuff if floors.data is defined by noFloorSignalBidders[]', function() { - handleSetFloorsConfig({ - ...basicFloorConfig, - data: { - ...basicFloorDataLow, - noFloorSignalBidders: ['someBidder', 'someOtherBidder'] - }}); - runStandardAuction(); - validateBidRequests(false, { - skipped: false, - floorMin: undefined, - modelVersion: 'basic model', - modelWeight: 10, - modelTimestamp: undefined, - location: 'setConfig', - skipRate: 0, - fetchStatus: undefined, - floorProvider: undefined, - noFloorSignaled: true - }) - }); - it('should not do floor stuff if floors.enforcement is defined by noFloorSignalBidders[]', function() { - handleSetFloorsConfig({ ...basicFloorConfig, - enforcement: { - enforceJS: true, - noFloorSignalBidders: ['someBidder', 'someOtherBidder'] - }, - data: basicFloorDataLow - }); - runStandardAuction(); - validateBidRequests(false, { - skipped: false, - floorMin: undefined, - modelVersion: 'basic model', - modelWeight: 10, - modelTimestamp: undefined, - location: 'setConfig', - skipRate: 0, - fetchStatus: undefined, - floorProvider: undefined, - noFloorSignaled: true - }) - }); - it('should not do floor stuff and use first floors.data.noFloorSignalBidders if its defined betwen enforcement.noFloorSignalBidders', function() { - handleSetFloorsConfig({ ...basicFloorConfig, - enforcement: { - enforceJS: true, - noFloorSignalBidders: ['someBidder'] - }, - data: { - ...basicFloorDataLow, - noFloorSignalBidders: ['someBidder', 'someOtherBidder'] - } - }); - runStandardAuction(); - validateBidRequests(false, { - skipped: false, - floorMin: undefined, - modelVersion: 'basic model', - modelWeight: 10, - modelTimestamp: undefined, - location: 'setConfig', - skipRate: 0, - fetchStatus: undefined, - floorProvider: undefined, - noFloorSignaled: true - }) - }); - it('it shouldn`t return floor stuff for bidder in the noFloorSignalBidders list', function() { - handleSetFloorsConfig({ ...basicFloorConfig, - enforcement: { - enforceJS: true, - }, - data: { - ...basicFloorDataLow, - noFloorSignalBidders: ['someBidder'] - } - }); - runStandardAuction() - const bidRequestData = exposedAdUnits[0].bids.find(bid => bid.bidder === 'someBidder'); - expect(bidRequestData.hasOwnProperty('getFloor')).to.equal(false); - sinon.assert.match(bidRequestData.floorData, { - skipped: false, - floorMin: undefined, - modelVersion: 'basic model', - modelWeight: 10, - modelTimestamp: undefined, - location: 'setConfig', - skipRate: 0, - fetchStatus: undefined, - floorProvider: undefined, - noFloorSignaled: true - }); - }) - it('it should return floor stuff if we defined wrong bidder name in data.noFloorSignalBidders', function() { - handleSetFloorsConfig({ ...basicFloorConfig, - enforcement: { - enforceJS: true, - }, - data: { - ...basicFloorDataLow, - noFloorSignalBidders: ['randomBiider'] - } - }); - runStandardAuction(); - validateBidRequests(true, { - skipped: false, - floorMin: undefined, - modelVersion: 'basic model', - modelWeight: 10, - modelTimestamp: undefined, - location: 'setConfig', - skipRate: 0, - fetchStatus: undefined, - floorProvider: undefined, - noFloorSignaled: false - }) - }); it('should use adUnit level data if not setConfig or fetch has occured', function () { handleSetFloorsConfig({ ...basicFloorConfig, @@ -963,95 +695,6 @@ describe('the price floors module', function () { floorProvider: undefined }); }); - describe('default floor', () => { - let adUnits; - beforeEach(() => { - adUnits = ['au1', 'au2'].map(getAdUnitMock); - }) - function expectFloors(floors) { - runStandardAuction(adUnits); - adUnits.forEach((au, i) => { - au.bids.forEach(bid => { - expect(bid.getFloor().floor).to.eql(floors[i]); - }) - }) - } - describe('should be sufficient by itself', () => { - it('globally', () => { - handleSetFloorsConfig({ - ...basicFloorConfig, - data: { - default: 1.23 - } - }); - expectFloors([1.23, 1.23]) - }); - it('on adUnits', () => { - handleSetFloorsConfig({ - ...basicFloorConfig, - data: undefined - }); - adUnits[0].floors = {default: 1}; - adUnits[1].floors = {default: 2}; - expectFloors([1, 2]) - }); - it('on an adUnit with hidden schema', () => { - handleSetFloorsConfig({ - ...basicFloorConfig, - data: undefined - }); - adUnits[0].floors = { - schema: { - fields: ['mediaType', 'gptSlot'], - }, - default: 1 - } - adUnits[1].floors = { - default: 2 - } - expectFloors([1, 2]); - }) - }); - describe('should NOT be used when a star rule exists', () => { - it('globally', () => { - handleSetFloorsConfig({ - ...basicFloorConfig, - data: { - schema: { - fields: ['mediaType', 'gptSlot'], - }, - values: { - '*|*': 2 - }, - default: 3, - } - }); - expectFloors([2, 2]); - }); - it('on adUnits', () => { - handleSetFloorsConfig({ - ...basicFloorConfig, - data: undefined - }); - adUnits[0].floors = { - schema: { - fields: ['mediaType', 'gptSlot'], - }, - values: { - '*|*': 1 - }, - default: 3 - }; - adUnits[1].floors = { - values: { - '*|*': 2 - }, - default: 4 - } - expectFloors([1, 2]); - }) - }); - }) it('bidRequests should have getFloor function and flooring meta data when setConfig occurs', function () { handleSetFloorsConfig({...basicFloorConfig, floorProvider: 'floorprovider'}); runStandardAuction(); @@ -1341,7 +984,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(); @@ -1368,7 +1011,7 @@ describe('the price floors module', function () { fetchStatus: 'timeout', floorProvider: undefined }); - server.respond(); + fakeFloorProvider.respond(); }); it('It should fetch if config has url and bidRequests have fetch level flooring meta data', function () { // init the fake server with response stuff @@ -1376,14 +1019,14 @@ describe('the price floors module', function () { ...basicFloorData, modelVersion: 'fetch model name', // change the model name }; - server.respondWith(JSON.stringify(fetchFloorData)); + fakeFloorProvider.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); - expect(server.requests[0].url).to.equal('http://www.fakefloorprovider.json/'); + expect(fakeFloorProvider.requests.length).to.equal(1); + expect(fakeFloorProvider.requests[0].url).to.equal('http://www.fakeFloorProvider.json'); // start the auction it should delay and not immediately call `continueAuction` runStandardAuction(); @@ -1392,7 +1035,7 @@ describe('the price floors module', function () { expect(exposedAdUnits).to.be.undefined; // make the fetch respond - server.respond(); + fakeFloorProvider.respond(); expect(exposedAdUnits).to.not.be.undefined; // the exposedAdUnits should be from the fetch not setConfig level data @@ -1416,14 +1059,14 @@ describe('the price floors module', function () { floorProvider: 'floorProviderD', // change the floor provider modelVersion: 'fetch model name', // change the model name }; - server.respondWith(JSON.stringify(fetchFloorData)); + fakeFloorProvider.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); - expect(server.requests[0].url).to.equal('http://www.fakefloorprovider.json/'); + expect(fakeFloorProvider.requests.length).to.equal(1); + expect(fakeFloorProvider.requests[0].url).to.equal('http://www.fakeFloorProvider.json'); // start the auction it should delay and not immediately call `continueAuction` runStandardAuction(); @@ -1432,7 +1075,7 @@ describe('the price floors module', function () { expect(exposedAdUnits).to.be.undefined; // make the fetch respond - server.respond(); + fakeFloorProvider.respond(); // the exposedAdUnits should be from the fetch not setConfig level data // and fetchStatus is success since fetch worked @@ -1457,14 +1100,14 @@ describe('the price floors module', function () { modelVersion: 'fetch model name', // change the model name }; fetchFloorData.skipRate = 95; - server.respondWith(JSON.stringify(fetchFloorData)); + fakeFloorProvider.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); - expect(server.requests[0].url).to.equal('http://www.fakefloorprovider.json/'); + expect(fakeFloorProvider.requests.length).to.equal(1); + expect(fakeFloorProvider.requests[0].url).to.equal('http://www.fakeFloorProvider.json'); // start the auction it should delay and not immediately call `continueAuction` runStandardAuction(); @@ -1473,7 +1116,7 @@ describe('the price floors module', function () { expect(exposedAdUnits).to.be.undefined; // make the fetch respond - server.respond(); + fakeFloorProvider.respond(); expect(exposedAdUnits).to.not.be.undefined; // the exposedAdUnits should be from the fetch not setConfig level data @@ -1492,10 +1135,10 @@ 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(); + fakeFloorProvider.respond(); runStandardAuction(); // error should have been called for fetch error @@ -1515,13 +1158,13 @@ describe('the price floors module', function () { }); }); it('Should not break if floor provider returns non json', function () { - server.respondWith('Not valid response'); + fakeFloorProvider.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(); + fakeFloorProvider.respond(); runStandardAuction(); // error should have been called for response floor data not being valid @@ -1542,27 +1185,27 @@ 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/'}}); + fakeFloorProvider.respondWith(JSON.stringify(basicFloorData)); + 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); - expect(server.requests.length).to.equal(1); - expect(server.requests[0].url).to.equal('http://www.fakefloorprovider.json/'); + expect(fakeFloorProvider.requests.length).to.equal(1); + expect(fakeFloorProvider.requests[0].url).to.equal('http://www.fakeFloorProvider.json'); // 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/'}}); - server.respond(); + fakeFloorProvider.respond(); + handleSetFloorsConfig({...basicFloorConfig, auctionDelay: 250, endpoint: {url: 'http://www.fakeFloorProvider.json'}}); + fakeFloorProvider.respond(); // now warn still only called once and server called twice expect(logWarnSpy.calledOnce).to.equal(true); - expect(server.requests.length).to.equal(2); + expect(fakeFloorProvider.requests.length).to.equal(2); // 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 () { @@ -1726,22 +1369,10 @@ describe('the price floors module', function () { floor: 2.5 }); }); - - it('works when TIDs are disabled', () => { - sandbox.stub(activities, 'isActivityAllowed').returns(false); - const req = utils.deepClone(bidRequest); - _floorDataForAuction[req.auctionId] = utils.deepClone(basicFloorConfig); - - expect(guardTids('mock-bidder').bidRequest(req).getFloor({})).to.deep.equal({ - currency: 'USD', - floor: 1.0 - }); - }); - it('picks the right rule with more complex rules', function () { _floorDataForAuction[bidRequest.auctionId] = { ...basicFloorConfig, - data: normalizeDefault({ + data: { currency: 'USD', schema: { fields: ['mediaType', 'size'], delimiter: '|' }, values: { @@ -1753,7 +1384,7 @@ describe('the price floors module', function () { 'video|*': 5.5 }, default: 10.0 - }) + } }; // assumes banner * @@ -2213,12 +1844,6 @@ describe('the price floors module', function () { expect(returnedBidResponse).to.not.haveOwnProperty('floorData'); expect(logWarnSpy.calledOnce).to.equal(true); }); - it('if it finds a rule with a floor price of zero it should not call log warn', function () { - _floorDataForAuction[AUCTION_ID] = utils.deepClone(basicFloorConfig); - _floorDataForAuction[AUCTION_ID].data.values = { '*': 0 }; - runBidResponse(); - expect(logWarnSpy.calledOnce).to.equal(false); - }); it('if it finds a rule and floors should update the bid accordingly', function () { _floorDataForAuction[AUCTION_ID] = utils.deepClone(basicFloorConfig); _floorDataForAuction[AUCTION_ID].data.values = { 'banner': 1.0 }; diff --git a/test/spec/modules/programmaticaBidAdapter_spec.js b/test/spec/modules/programmaticaBidAdapter_spec.js deleted file mode 100644 index 247d20752c3..00000000000 --- a/test/spec/modules/programmaticaBidAdapter_spec.js +++ /dev/null @@ -1,263 +0,0 @@ -import { expect } from 'chai'; -import { spec } from 'modules/programmaticaBidAdapter.js'; -import { deepClone } from 'src/utils.js'; - -describe('programmaticaBidAdapterTests', function () { - let bidRequestData = { - bids: [ - { - bidId: 'testbid', - bidder: 'programmatica', - params: { - siteId: 'testsite', - placementId: 'testplacement', - }, - sizes: [[300, 250]] - } - ] - }; - let request = []; - - it('validate_pub_params', function () { - expect( - spec.isBidRequestValid({ - bidder: 'programmatica', - params: { - siteId: 'testsite', - placementId: 'testplacement', - } - }) - ).to.equal(true); - }); - - it('validate_generated_url', function () { - const request = spec.buildRequests(deepClone(bidRequestData.bids), { timeout: 1234 }); - let req_url = request[0].url; - - expect(req_url).to.equal('https://asr.programmatica.com/get'); - }); - - it('validate_response_params', function () { - let serverResponse = { - body: { - 'id': 'crid', - 'type': { - 'format': 'Image', - 'source': 'passback', - 'dspId': '', - 'dspCreativeId': '' - }, - 'content': { - 'data': 'test ad', - 'imps': null, - 'click': { - 'url': '', - 'track': null - } - }, - 'size': '300x250', - 'matching': '', - 'cpm': 10, - 'currency': 'USD' - } - }; - - const bidRequest = deepClone(bidRequestData.bids) - bidRequest[0].mediaTypes = { - banner: {} - } - - const request = spec.buildRequests(bidRequest); - let bids = spec.interpretResponse(serverResponse, request[0]); - expect(bids).to.have.lengthOf(1); - - let bid = bids[0]; - expect(bid.ad).to.equal('test ad'); - expect(bid.cpm).to.equal(10); - expect(bid.currency).to.equal('USD'); - expect(bid.width).to.equal(300); - expect(bid.height).to.equal(250); - expect(bid.creativeId).to.equal('crid'); - expect(bid.meta.advertiserDomains).to.deep.equal(['programmatica.com']); - }); - - it('validate_response_params_imps', function () { - let serverResponse = { - body: { - 'id': 'crid', - 'type': { - 'format': 'Image', - 'source': 'passback', - 'dspId': '', - 'dspCreativeId': '' - }, - 'content': { - 'data': 'test ad', - 'imps': [ - 'testImp' - ], - 'click': { - 'url': '', - 'track': null - } - }, - 'size': '300x250', - 'matching': '', - 'cpm': 10, - 'currency': 'USD' - } - }; - - const bidRequest = deepClone(bidRequestData.bids) - bidRequest[0].mediaTypes = { - banner: {} - } - - const request = spec.buildRequests(bidRequest); - let bids = spec.interpretResponse(serverResponse, request[0]); - expect(bids).to.have.lengthOf(1); - - let bid = bids[0]; - expect(bid.ad).to.equal('test ad'); - expect(bid.cpm).to.equal(10); - expect(bid.currency).to.equal('USD'); - expect(bid.width).to.equal(300); - expect(bid.height).to.equal(250); - expect(bid.creativeId).to.equal('crid'); - expect(bid.meta.advertiserDomains).to.deep.equal(['programmatica.com']); - }) - - it('validate_invalid_response', function () { - let serverResponse = { - body: {} - }; - - const bidRequest = deepClone(bidRequestData.bids) - bidRequest[0].mediaTypes = { - banner: {} - } - - const request = spec.buildRequests(bidRequest); - let bids = spec.interpretResponse(serverResponse, request[0]); - expect(bids).to.have.lengthOf(0); - }) - - it('video_bid', function () { - const bidRequest = deepClone(bidRequestData.bids); - bidRequest[0].mediaTypes = { - video: { - playerSize: [234, 765] - } - }; - - const request = spec.buildRequests(bidRequest, { timeout: 1234 }); - const vastXml = ''; - let serverResponse = { - body: { - 'id': 'cki2n3n6snkuulqutpf0', - 'type': { - 'format': '', - 'source': 'rtb', - 'dspId': '1' - }, - 'content': { - 'data': vastXml, - 'imps': [ - 'https://asr.dev.programmatica.com/track/imp' - ], - 'click': { - 'url': '', - 'track': null - } - }, - 'size': '', - 'matching': '', - 'cpm': 70, - 'currency': 'RUB' - } - }; - - let bids = spec.interpretResponse(serverResponse, request[0]); - expect(bids).to.have.lengthOf(1); - - let bid = bids[0]; - expect(bid.mediaType).to.equal('video'); - expect(bid.vastXml).to.equal(vastXml); - expect(bid.width).to.equal(234); - expect(bid.height).to.equal(765); - }); -}); - -describe('getUserSyncs', function() { - it('returns empty sync array', function() { - const syncOptions = {}; - - expect(spec.getUserSyncs(syncOptions)).to.deep.equal([]); - }); - - it('Should return array of objects with proper sync config , include CCPA', function() { - const syncData = spec.getUserSyncs({ - pixelEnabled: true, - }, {}, {}, '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('//sync.programmatica.com/match/sp?usp=1---&consent=') - }); - - it('Should return array of objects with proper sync config , include GDPR', function() { - const syncData = spec.getUserSyncs({ - iframeEnabled: true, - }, {}, { - gdprApplies: true, - consentString: 'COvFyGBOvFyGBAbAAAENAPCAAOAAAAAAAAAAAEEUACCKAAA.IFoEUQQgAIQwgIwQABAEAAAAOIAACAIAAAAQAIAgEAACEAAAAAgAQBAAAAAAAGBAAgAAAAAAAFAAECAAAgAAQARAEQAAAAAJAAIAAgAAAYQEAAAQmAgBC3ZAYzUw', - vendorData: { - purpose: { - consents: { - 1: true - }, - }, - } - }, ''); - 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('iframe') - expect(syncData[0].url).to.be.a('string') - expect(syncData[0].url).to.equal('//sync.programmatica.com/match/sp.ifr?usp=&consent=COvFyGBOvFyGBAbAAAENAPCAAOAAAAAAAAAAAEEUACCKAAA.IFoEUQQgAIQwgIwQABAEAAAAOIAACAIAAAAQAIAgEAACEAAAAAgAQBAAAAAAAGBAAgAAAAAAAFAAECAAAgAAQARAEQAAAAAJAAIAAgAAAYQEAAAQmAgBC3ZAYzUw&gdpr=1') - }); - - it('Should return array of objects with proper sync config , include GDPR, no purpose', function() { - const syncData = spec.getUserSyncs({ - iframeEnabled: true, - }, {}, { - gdprApplies: true, - consentString: 'COvFyGBOvFyGBAbAAAENAPCAAOAAAAAAAAAAAEEUACCKAAA.IFoEUQQgAIQwgIwQABAEAAAAOIAACAIAAAAQAIAgEAACEAAAAAgAQBAAAAAAAGBAAgAAAAAAAFAAECAAAgAAQARAEQAAAAAJAAIAAgAAAYQEAAAQmAgBC3ZAYzUw', - vendorData: { - purpose: { - consents: { - 1: false - }, - }, - } - }, ''); - expect(syncData).is.empty; - }); - - it('Should return array of objects with proper sync config , GDPR not applies', function() { - const syncData = spec.getUserSyncs({ - iframeEnabled: true, - }, {}, { - gdprApplies: false, - consentString: 'COvFyGBOvFyGBAbAAAENAPCAAOAAAAAAAAAAAEEUACCKAAA.IFoEUQQgAIQwgIwQABAEAAAAOIAACAIAAAAQAIAgEAACEAAAAAgAQBAAAAAAAGBAAgAAAAAAAFAAECAAAgAAQARAEQAAAAAJAAIAAgAAAYQEAAAQmAgBC3ZAYzUw', - }, ''); - 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('iframe') - expect(syncData[0].url).to.be.a('string') - expect(syncData[0].url).to.equal('//sync.programmatica.com/match/sp.ifr?usp=&consent=COvFyGBOvFyGBAbAAAENAPCAAOAAAAAAAAAAAEEUACCKAAA.IFoEUQQgAIQwgIwQABAEAAAAOIAACAIAAAAQAIAgEAACEAAAAAgAQBAAAAAAAGBAAgAAAAAAAFAAECAAAgAAQARAEQAAAAAJAAIAAgAAAYQEAAAQmAgBC3ZAYzUw&gdpr=0') - }); -}) diff --git a/test/spec/modules/publinkIdSystem_spec.js b/test/spec/modules/publinkIdSystem_spec.js index 5ad58ea1a37..7d98b724bd8 100644 --- a/test/spec/modules/publinkIdSystem_spec.js +++ b/test/spec/modules/publinkIdSystem_spec.js @@ -72,6 +72,11 @@ describe('PublinkIdSystem', () => { expect(result.callback).to.be.a('function'); }); + it('Use local copy', () => { + const result = publinkIdSubmodule.getId({}, undefined, TEST_COOKIE_VALUE); + expect(result).to.be.undefined; + }); + describe('callout for id', () => { let callbackSpy = sinon.spy(); @@ -79,44 +84,6 @@ describe('PublinkIdSystem', () => { callbackSpy.resetHistory(); }); - it('Has cached id', () => { - const config = {storage: {type: 'cookie'}}; - let submoduleCallback = publinkIdSubmodule.getId(config, undefined, TEST_COOKIE_VALUE).callback; - submoduleCallback(callbackSpy); - - const request = server.requests[0]; - const parsed = parseUrl(request.url); - - expect(parsed.hostname).to.equal('proc.ad.cpe.dotomi.com'); - expect(parsed.pathname).to.equal('/cvx/client/sync/publink/refresh'); - expect(parsed.search.mpn).to.equal('Prebid.js'); - expect(parsed.search.mpv).to.equal('$prebid.version$'); - expect(parsed.search.publink).to.equal(TEST_COOKIE_VALUE); - - request.respond(200, {}, JSON.stringify(serverResponse)); - expect(callbackSpy.calledOnce).to.be.true; - expect(callbackSpy.lastCall.lastArg).to.equal(serverResponse.publink); - }); - - it('Request path has priority', () => { - const config = {storage: {type: 'cookie'}, params: {e: 'ca11c0ca7', site_id: '102030'}}; - let submoduleCallback = publinkIdSubmodule.getId(config, undefined, TEST_COOKIE_VALUE).callback; - submoduleCallback(callbackSpy); - - const request = server.requests[0]; - const parsed = parseUrl(request.url); - - expect(parsed.hostname).to.equal('proc.ad.cpe.dotomi.com'); - expect(parsed.pathname).to.equal('/cvx/client/sync/publink'); - expect(parsed.search.mpn).to.equal('Prebid.js'); - expect(parsed.search.mpv).to.equal('$prebid.version$'); - expect(parsed.search.publink).to.equal(TEST_COOKIE_VALUE); - - request.respond(200, {}, JSON.stringify(serverResponse)); - expect(callbackSpy.calledOnce).to.be.true; - expect(callbackSpy.lastCall.lastArg).to.equal(serverResponse.publink); - }); - it('Fetch with consent data', () => { const config = {storage: {type: 'cookie'}, params: {e: 'ca11c0ca7', site_id: '102030'}}; const consentData = {gdprApplies: 1, consentString: 'myconsentstring'}; @@ -153,7 +120,7 @@ describe('PublinkIdSystem', () => { expect(parsed.search.mpn).to.equal('Prebid.js'); expect(parsed.search.mpv).to.equal('$prebid.version$'); - request.respond(204); + request.respond(204, {}, JSON.stringify(serverResponse)); expect(callbackSpy.called).to.be.false; }); diff --git a/test/spec/modules/pubmaticAnalyticsAdapter_spec.js b/test/spec/modules/pubmaticAnalyticsAdapter_spec.js index c6447905ecd..c56ed565c43 100755 --- a/test/spec/modules/pubmaticAnalyticsAdapter_spec.js +++ b/test/spec/modules/pubmaticAnalyticsAdapter_spec.js @@ -2,10 +2,10 @@ import pubmaticAnalyticsAdapter, { getMetadata } from 'modules/pubmaticAnalytics import adapterManager from 'src/adapterManager.js'; import CONSTANTS from 'src/constants.json'; import { config } from 'src/config.js'; -import { setConfig } from 'modules/currency.js'; -import { server } from '../../mocks/xhr.js'; -import 'src/prebid.js'; -import { getGlobal } from 'src/prebidGlobal'; +import { + setConfig, + addBidResponseHook, +} from 'modules/currency.js'; let events = require('src/events'); let ajax = require('src/ajax'); @@ -23,7 +23,6 @@ const { AUCTION_END, BID_REQUESTED, BID_RESPONSE, - BID_REJECTED, BIDDER_DONE, BID_WON, BID_TIMEOUT, @@ -109,9 +108,6 @@ const BID2 = Object.assign({}, BID, { } }); -const BID3 = Object.assign({}, BID2, { - rejectionReason: CONSTANTS.REJECTION_REASON.FLOOR_NOT_MET -}) const MOCK = { SET_TARGETING: { [BID.adUnitCode]: BID.adserverTargeting, @@ -241,9 +237,6 @@ const MOCK = { BID, BID2 ], - REJECTED_BID: [ - BID3 - ], AUCTION_END: { 'auctionId': '25c6d7f5-699a-4bfc-87c9-996f915341fa' }, @@ -280,6 +273,7 @@ function getLoggerJsonFromRequest(requestBody) { describe('pubmatic analytics adapter', function () { let sandbox; + let xhr; let requests; let oldScreen; let clock; @@ -288,7 +282,9 @@ describe('pubmatic analytics adapter', function () { setUADefault(); sandbox = sinon.sandbox.create(); - requests = server.requests; + xhr = sandbox.useFakeXMLHttpRequest(); + requests = []; + xhr.onCreate = request => requests.push(request); sandbox.stub(events, 'getEvents').returns([]); @@ -316,208 +312,6 @@ describe('pubmatic analytics adapter', function () { expect(utils.logError.called).to.equal(true); }); - describe('OW S2S', function() { - this.beforeEach(function() { - pubmaticAnalyticsAdapter.enableAnalytics({ - options: { - publisherId: 9999, - profileId: 1111, - profileVersionId: 20 - } - }); - config.setConfig({ - s2sConfig: { - accountId: '1234', - bidders: ['pubmatic'], - defaultVendor: 'openwrap', - timeout: 500 - } - }); - }); - - this.afterEach(function() { - pubmaticAnalyticsAdapter.disableAnalytics(); - }); - - it('Pubmatic Won: No tracker fired', function() { - this.timeout(5000) - - sandbox.stub($$PREBID_GLOBAL$$, 'getHighestCpmBids').callsFake((key) => { - return [MOCK.BID_RESPONSE[0], MOCK.BID_RESPONSE[1]] - }); - - config.setConfig({ - testGroupId: 15 - }); - - events.emit(AUCTION_INIT, MOCK.AUCTION_INIT); - events.emit(BID_REQUESTED, MOCK.BID_REQUESTED); - events.emit(BID_RESPONSE, MOCK.BID_RESPONSE[0]); - events.emit(BIDDER_DONE, MOCK.BIDDER_DONE); - events.emit(AUCTION_END, MOCK.AUCTION_END); - events.emit(SET_TARGETING, MOCK.SET_TARGETING); - events.emit(BID_WON, MOCK.BID_WON[0]); - - clock.tick(2000 + 1000); - expect(requests.length).to.equal(1); // only logger is fired - let request = requests[0]; - expect(request.url).to.equal('https://t.pubmatic.com/wl?pubid=9999'); - let data = getLoggerJsonFromRequest(request.requestBody); - expect(data.pubid).to.equal('9999'); - expect(data.pid).to.equal('1111'); - expect(data.pdvid).to.equal('20'); - }); - - it('Non-pubmatic won: logger, tracker fired', function() { - const APPNEXUS_BID = Object.assign({}, BID, { - 'bidder': 'appnexus', - 'adserverTargeting': { - 'hb_bidder': 'appnexus', - 'hb_adid': '2ecff0db240757', - 'hb_pb': 1.20, - 'hb_size': '640x480', - 'hb_source': 'server' - } - }); - - const MOCK_AUCTION_INIT_APPNEXUS = { - 'auctionId': '25c6d7f5-699a-4bfc-87c9-996f915341fa', - 'timestamp': 1519767010567, - 'auctionStatus': 'inProgress', - 'adUnits': [ { - 'code': '/19968336/header-bid-tag-1', - 'sizes': [[640, 480]], - 'bids': [ { - 'bidder': 'appnexus', - 'params': { - 'publisherId': '1001' - } - } ], - 'transactionId': 'ca4af27a-6d02-4f90-949d-d5541fa12014' - } - ], - 'adUnitCodes': ['/19968336/header-bid-tag-1'], - 'bidderRequests': [ { - 'bidderCode': 'appnexus', - 'auctionId': '25c6d7f5-699a-4bfc-87c9-996f915341fa', - 'bidderRequestId': '1be65d7958826a', - 'bids': [ { - 'bidder': 'appnexus', - 'params': { - 'publisherId': '1001', - 'kgpv': 'this-is-a-kgpv' - }, - 'mediaTypes': { - 'banner': { - 'sizes': [[640, 480]] - } - }, - 'adUnitCode': '/19968336/header-bid-tag-1', - 'transactionId': 'ca4af27a-6d02-4f90-949d-d5541fa12014', - 'sizes': [[640, 480]], - 'bidId': '2ecff0db240757', - 'bidderRequestId': '1be65d7958826a', - 'auctionId': '25c6d7f5-699a-4bfc-87c9-996f915341fa', - 'src': 'client', - 'bidRequestsCount': 1 - } - ], - 'timeout': 3000, - 'refererInfo': { - 'topmostLocation': 'http://www.test.com/page.html', 'reachedTop': true, 'numIframes': 0, 'stack': ['http://www.test.com/page.html'] - } - } - ], - 'bidsReceived': [], - 'winningBids': [], - 'timeout': 3000 - }; - - const MOCK_BID_REQUESTED_APPNEXUS = { - 'bidder': 'appnexus', - 'auctionId': '25c6d7f5-699a-4bfc-87c9-996f915341fa', - 'bidderRequestId': '1be65d7958826a', - 'bids': [ - { - 'bidder': 'appnexus', - 'adapterCode': 'appnexus', - 'bidderCode': 'appnexus', - 'params': { - 'publisherId': '1001', - 'video': { - 'minduration': 30, - 'skippable': true - } - }, - 'mediaType': 'video', - 'adUnitCode': '/19968336/header-bid-tag-0', - 'transactionId': 'ca4af27a-6d02-4f90-949d-d5541fa12014', - 'sizes': [[640, 480]], - 'bidId': '2ecff0db240757', - 'bidderRequestId': '1be65d7958826a', - 'auctionId': '25c6d7f5-699a-4bfc-87c9-996f915341fa' - } - ], - 'auctionStart': 1519149536560, - 'timeout': 5000, - 'start': 1519149562216, - 'refererInfo': { - 'topmostLocation': 'http://www.test.com/page.html', 'reachedTop': true, 'numIframes': 0, 'stack': ['http://www.test.com/page.html'] - }, - 'gdprConsent': { - 'consentString': 'here-goes-gdpr-consent-string', - 'gdprApplies': true - } - }; - - this.timeout(5000) - - sandbox.stub($$PREBID_GLOBAL$$, 'getHighestCpmBids').callsFake((key) => { - return [APPNEXUS_BID] - }); - - events.emit(AUCTION_INIT, MOCK_AUCTION_INIT_APPNEXUS); - events.emit(BID_REQUESTED, MOCK_BID_REQUESTED_APPNEXUS); - events.emit(BID_RESPONSE, APPNEXUS_BID); - events.emit(BIDDER_DONE, { - 'bidderCode': 'appnexus', - 'bids': [ - APPNEXUS_BID, - Object.assign({}, APPNEXUS_BID, { - 'serverResponseTimeMs': 42, - }) - ] - }); - events.emit(AUCTION_END, MOCK.AUCTION_END); - events.emit(SET_TARGETING, { - [APPNEXUS_BID.adUnitCode]: APPNEXUS_BID.adserverTargeting, - }); - events.emit(BID_WON, Object.assign({}, APPNEXUS_BID, { - 'status': 'rendered' - })); - - clock.tick(2000 + 1000); - expect(requests.length).to.equal(2); // logger as well as tracker is fired - let request = requests[1]; // logger is executed late, trackers execute first - expect(request.url).to.equal('https://t.pubmatic.com/wl?pubid=9999'); - let data = getLoggerJsonFromRequest(request.requestBody); - expect(data.pubid).to.equal('9999'); - expect(data.pid).to.equal('1111'); - expect(data.pdvid).to.equal('20'); - - let firstTracker = requests[0].url; - expect(firstTracker.split('?')[0]).to.equal('https://t.pubmatic.com/wt'); - firstTracker.split('?')[1].split('&').map(e => e.split('=')).forEach(e => data[e[0]] = e[1]); - expect(data.pubid).to.equal('9999'); - expect(decodeURIComponent(data.purl)).to.equal('http://www.test.com/page.html'); - - expect(data.s).to.be.an('array'); - expect(data.s.length).to.equal(1); - expect(data.s[0].ps[0].pn).to.equal('appnexus'); - expect(data.s[0].ps[0].bc).to.equal('appnexus'); - }) - }); - describe('when handling events', function() { beforeEach(function () { pubmaticAnalyticsAdapter.enableAnalytics({ @@ -570,20 +364,15 @@ describe('pubmatic analytics adapter', function () { expect(data.tgid).to.equal(15); expect(data.fmv).to.equal('floorModelTest'); expect(data.ft).to.equal(1); - expect(data.pbv).to.equal(getGlobal()?.version || '-1'); expect(data.s).to.be.an('array'); expect(data.s.length).to.equal(2); // slot 1 expect(data.s[0].sn).to.equal('/19968336/header-bid-tag-0'); expect(data.s[0].fskp).to.equal(0); - expect(data.s[0].sid).not.to.be.undefined; - expect(data.s[0].ffs).to.equal(1); - expect(data.s[0].fsrc).to.equal(2); - expect(data.s[0].fp).to.equal('pubmatic'); expect(data.s[0].sz).to.deep.equal(['640x480']); expect(data.s[0].ps).to.be.an('array'); - expect(data.s[0].au).to.equal('/19968336/header-bid-tag-0'); - expect(data.s[0].ps.length).to.equal(1); + expect(data.s[0].au).to.equal('/19968336/header-bid-tag-0'); + expect(data.s[0].ps.length).to.equal(1); expect(data.s[0].ps[0].pn).to.equal('pubmatic'); expect(data.s[0].ps[0].bc).to.equal('pubmatic'); expect(data.s[0].ps[0].bidid).to.equal('2ecff0db240757'); @@ -594,10 +383,9 @@ describe('pubmatic analytics adapter', function () { expect(data.s[0].ps[0].psz).to.equal('640x480'); expect(data.s[0].ps[0].eg).to.equal(1.23); expect(data.s[0].ps[0].en).to.equal(1.23); - expect(data.s[0].ps[0].di).to.equal('-1'); + expect(data.s[0].ps[0].di).to.equal(''); expect(data.s[0].ps[0].dc).to.equal(''); - expect(data.s[0].ps[0].l1).to.equal(944); - expect(data.s[0].ps[0].ol1).to.equal(3214); + expect(data.s[0].ps[0].l1).to.equal(3214); expect(data.s[0].ps[0].l2).to.equal(0); expect(data.s[0].ps[0].ss).to.equal(1); expect(data.s[0].ps[0].t).to.equal(0); @@ -605,14 +393,10 @@ describe('pubmatic analytics adapter', function () { expect(data.s[0].ps[0].af).to.equal('video'); expect(data.s[0].ps[0].ocpm).to.equal(1.23); expect(data.s[0].ps[0].ocry).to.equal('USD'); - expect(data.s[0].ps[0].frv).to.equal(1.1); + expect(data.s[0].ps[0].frv).to.equal(undefined); // slot 2 expect(data.s[1].sn).to.equal('/19968336/header-bid-tag-1'); expect(data.s[1].fskp).to.equal(0); - expect(data.s[1].sid).not.to.be.undefined; - expect(data.s[1].ffs).to.equal(1); - expect(data.s[1].fsrc).to.equal(2); - expect(data.s[1].fp).to.equal('pubmatic'); expect(data.s[1].sz).to.deep.equal(['1000x300', '970x250', '728x90']); expect(data.s[1].ps).to.be.an('array'); expect(data.s[1].ps.length).to.equal(1); @@ -630,8 +414,7 @@ describe('pubmatic analytics adapter', function () { expect(data.s[1].ps[0].dc).to.equal('PMP'); expect(data.s[1].ps[0].mi).to.equal('matched-impression'); expect(data.s[1].ps[0].adv).to.equal('example.com'); - expect(data.s[0].ps[0].l1).to.equal(944); - expect(data.s[0].ps[0].ol1).to.equal(3214); + expect(data.s[1].ps[0].l1).to.equal(3214); expect(data.s[1].ps[0].l2).to.equal(0); expect(data.s[1].ps[0].ss).to.equal(1); expect(data.s[1].ps[0].t).to.equal(0); @@ -639,7 +422,7 @@ describe('pubmatic analytics adapter', function () { expect(data.s[1].ps[0].af).to.equal('banner'); expect(data.s[1].ps[0].ocpm).to.equal(1.52); expect(data.s[1].ps[0].ocry).to.equal('USD'); - expect(data.s[1].ps[0].frv).to.equal(1.1); + expect(data.s[1].ps[0].frv).to.equal(undefined); // tracker slot1 let firstTracker = requests[0].url; @@ -669,94 +452,6 @@ describe('pubmatic analytics adapter', function () { expect(data.af).to.equal('video'); }); - it('Logger : do not log floor fields when prebids floor shows noData in location property', function() { - const BID_REQUESTED_COPY = utils.deepClone(MOCK.BID_REQUESTED); - BID_REQUESTED_COPY['bids'][1]['floorData']['location'] = 'noData'; - - this.timeout(5000) - - sandbox.stub($$PREBID_GLOBAL$$, 'getHighestCpmBids').callsFake((key) => { - return [MOCK.BID_RESPONSE[0], MOCK.BID_RESPONSE[1]] - }); - - config.setConfig({ - testGroupId: 15 - }); - - events.emit(AUCTION_INIT, MOCK.AUCTION_INIT); - events.emit(BID_REQUESTED, BID_REQUESTED_COPY); - events.emit(BID_RESPONSE, MOCK.BID_RESPONSE[0]); - events.emit(BID_RESPONSE, MOCK.BID_RESPONSE[1]); - events.emit(BIDDER_DONE, MOCK.BIDDER_DONE); - events.emit(AUCTION_END, MOCK.AUCTION_END); - events.emit(SET_TARGETING, MOCK.SET_TARGETING); - events.emit(BID_WON, MOCK.BID_WON[0]); - events.emit(BID_WON, MOCK.BID_WON[1]); - - clock.tick(2000 + 1000); - expect(requests.length).to.equal(3); // 1 logger and 2 win-tracker - let request = requests[2]; // logger is executed late, trackers execute first - expect(request.url).to.equal('https://t.pubmatic.com/wl?pubid=9999'); - - let data = getLoggerJsonFromRequest(request.requestBody); - - expect(data.pubid).to.equal('9999'); - expect(data.fmv).to.equal(undefined); - - // slot 1 - expect(data.s[0].sn).to.equal('/19968336/header-bid-tag-0'); - expect(data.s[0].au).to.equal('/19968336/header-bid-tag-0'); - - // slot 2 - expect(data.s[1].sn).to.equal('/19968336/header-bid-tag-1'); - expect(data.s[1].au).to.equal('/19968336/header-bid-tag-1'); - expect(data.s[1].fskp).to.equal(undefined); - }); - - it('Logger: log floor fields when prebids floor shows setConfig in location property', function() { - const BID_REQUESTED_COPY = utils.deepClone(MOCK.BID_REQUESTED); - BID_REQUESTED_COPY['bids'][1]['floorData']['location'] = 'setConfig'; - - this.timeout(5000) - - sandbox.stub($$PREBID_GLOBAL$$, 'getHighestCpmBids').callsFake((key) => { - return [MOCK.BID_RESPONSE[0], MOCK.BID_RESPONSE[1]] - }); - - config.setConfig({ - testGroupId: 15 - }); - - events.emit(AUCTION_INIT, MOCK.AUCTION_INIT); - events.emit(BID_REQUESTED, BID_REQUESTED_COPY); - events.emit(BID_RESPONSE, MOCK.BID_RESPONSE[0]); - events.emit(BID_RESPONSE, MOCK.BID_RESPONSE[1]); - events.emit(BIDDER_DONE, MOCK.BIDDER_DONE); - events.emit(AUCTION_END, MOCK.AUCTION_END); - events.emit(SET_TARGETING, MOCK.SET_TARGETING); - events.emit(BID_WON, MOCK.BID_WON[0]); - events.emit(BID_WON, MOCK.BID_WON[1]); - - clock.tick(2000 + 1000); - expect(requests.length).to.equal(3); // 1 logger and 2 win-tracker - let request = requests[2]; // logger is executed late, trackers execute first - expect(request.url).to.equal('https://t.pubmatic.com/wl?pubid=9999'); - - let data = getLoggerJsonFromRequest(request.requestBody); - - expect(data.pubid).to.equal('9999'); - expect(data.fmv).to.equal('floorModelTest'); - - // slot 1 - expect(data.s[0].sn).to.equal('/19968336/header-bid-tag-0'); - expect(data.s[0].au).to.equal('/19968336/header-bid-tag-0'); - - // slot 2 - expect(data.s[1].sn).to.equal('/19968336/header-bid-tag-1'); - expect(data.s[1].au).to.equal('/19968336/header-bid-tag-1'); - expect(data.s[1].fskp).to.equal(0); - }); - it('bidCpmAdjustment: USD: Logger: best case + win tracker', function() { const bidCopy = utils.deepClone(BID); bidCopy.cpm = bidCopy.originalCpm * 2; // bidCpmAdjustment => bidCpm * 2 @@ -786,20 +481,15 @@ describe('pubmatic analytics adapter', function () { expect(data.pid).to.equal('1111'); expect(data.fmv).to.equal('floorModelTest'); expect(data.ft).to.equal(1); - expect(data.pbv).to.equal(getGlobal()?.version || '-1'); expect(data.s).to.be.an('array'); expect(data.s.length).to.equal(2); expect(data.tgid).to.equal(0); // slot 1 expect(data.s[0].sn).to.equal('/19968336/header-bid-tag-0'); expect(data.s[0].fskp).to.equal(0); - expect(data.s[0].sid).not.to.be.undefined; - expect(data.s[0].ffs).to.equal(1); - expect(data.s[0].fsrc).to.equal(2); - expect(data.s[0].fp).to.equal('pubmatic'); expect(data.s[0].sz).to.deep.equal(['640x480']); expect(data.s[0].ps).to.be.an('array'); - expect(data.s[0].au).to.equal('/19968336/header-bid-tag-0'); + expect(data.s[0].au).to.equal('/19968336/header-bid-tag-0'); expect(data.s[0].ps.length).to.equal(1); expect(data.s[0].ps[0].pn).to.equal('pubmatic'); expect(data.s[0].ps[0].bc).to.equal('pubmatic'); @@ -811,7 +501,7 @@ describe('pubmatic analytics adapter', function () { expect(data.s[0].ps[0].af).to.equal('video'); expect(data.s[0].ps[0].ocpm).to.equal(1.23); expect(data.s[0].ps[0].ocry).to.equal('USD'); - expect(data.s[1].ps[0].frv).to.equal(1.1); + expect(data.s[1].ps[0].frv).to.equal(undefined); // tracker slot1 let firstTracker = requests[0].url; expect(firstTracker.split('?')[0]).to.equal('https://t.pubmatic.com/wt'); @@ -865,7 +555,6 @@ describe('pubmatic analytics adapter', function () { expect(data.tgid).to.equal(0);// test group id should be between 0-15 else set to 0 expect(data.fmv).to.equal('floorModelTest'); expect(data.ft).to.equal(1); - expect(data.pbv).to.equal(getGlobal()?.version || '-1'); expect(data.s).to.be.an('array'); expect(data.s.length).to.equal(2); // slot 1 @@ -873,7 +562,7 @@ describe('pubmatic analytics adapter', function () { expect(data.s[0].sz).to.deep.equal(['640x480']); expect(data.s[0].ps).to.be.an('array'); expect(data.s[0].ps.length).to.equal(1); - expect(data.s[0].au).to.equal('/19968336/header-bid-tag-0'); + expect(data.s[0].au).to.equal('/19968336/header-bid-tag-0'); expect(data.s[0].ps[0].pn).to.equal('pubmatic'); expect(data.s[0].ps[0].bc).to.equal('pubmatic'); expect(data.s[0].ps[0].bidid).to.equal('2ecff0db240757'); @@ -916,13 +605,6 @@ describe('pubmatic analytics adapter', function () { expect(data.tgid).to.equal(0);// test group id should be an INT between 0-15 else set to 0 expect(data.s[1].sn).to.equal('/19968336/header-bid-tag-1'); expect(data.s[1].fskp).to.equal(0); - - expect(data.s[1].sid).not.to.be.undefined; - - expect(data.s[1].ffs).to.equal(1); - expect(data.s[1].fsrc).to.equal(2); - expect(data.s[1].fp).to.equal('pubmatic'); - expect(data.s[1].sz).to.deep.equal(['1000x300', '970x250', '728x90']); expect(data.s[1].ps).to.be.an('array'); expect(data.s[1].ps.length).to.equal(1); @@ -935,7 +617,7 @@ describe('pubmatic analytics adapter', function () { expect(data.s[1].ps[0].psz).to.equal('0x0'); expect(data.s[1].ps[0].eg).to.equal(0); expect(data.s[1].ps[0].en).to.equal(0); - expect(data.s[1].ps[0].di).to.equal('-1'); + expect(data.s[1].ps[0].di).to.equal(''); expect(data.s[1].ps[0].dc).to.equal(''); expect(data.s[1].ps[0].mi).to.equal(undefined); expect(data.s[1].ps[0].l1).to.equal(0); @@ -973,7 +655,7 @@ describe('pubmatic analytics adapter', function () { expect(data.s[1].ps[0].psz).to.equal('0x0'); expect(data.s[1].ps[0].eg).to.equal(0); expect(data.s[1].ps[0].en).to.equal(0); - expect(data.s[1].ps[0].di).to.equal('-1'); + expect(data.s[1].ps[0].di).to.equal(''); expect(data.s[1].ps[0].dc).to.equal(''); expect(data.s[1].ps[0].mi).to.equal(undefined); expect(data.s[1].ps[0].l1).to.equal(0); @@ -1005,10 +687,6 @@ describe('pubmatic analytics adapter', function () { let data = getLoggerJsonFromRequest(request.requestBody); expect(data.s[1].sn).to.equal('/19968336/header-bid-tag-1'); expect(data.s[1].fskp).to.equal(0); - expect(data.s[1].sid).not.to.be.undefined; - expect(data.s[1].ffs).to.equal(1); - expect(data.s[1].fsrc).to.equal(2); - expect(data.s[1].fp).to.equal('pubmatic'); expect(data.s[1].sz).to.deep.equal(['1000x300', '970x250', '728x90']); expect(data.s[1].ps).to.be.an('array'); expect(data.s[1].ps.length).to.equal(1); @@ -1025,8 +703,7 @@ describe('pubmatic analytics adapter', function () { expect(data.s[1].ps[0].dc).to.equal('PMP'); expect(data.s[1].ps[0].mi).to.equal('matched-impression'); expect(data.s[1].ps[0].adv).to.equal('example.com'); - expect(data.s[0].ps[0].l1).to.equal(0); - expect(data.s[0].ps[0].ol1).to.equal(0); + expect(data.s[1].ps[0].l1).to.equal(3214); expect(data.s[1].ps[0].l2).to.equal(0); expect(data.s[1].ps[0].ss).to.equal(1); expect(data.s[1].ps[0].t).to.equal(1); @@ -1034,7 +711,7 @@ describe('pubmatic analytics adapter', function () { expect(data.s[1].ps[0].af).to.equal('banner'); expect(data.s[1].ps[0].ocpm).to.equal(1.52); expect(data.s[1].ps[0].ocry).to.equal('USD'); - expect(data.s[1].ps[0].frv).to.equal(1.1); + expect(data.s[1].ps[0].frv).to.equal(undefined); }); it('Logger: currency conversion check', function() { @@ -1071,7 +748,6 @@ describe('pubmatic analytics adapter', function () { expect(data.s[1].sn).to.equal('/19968336/header-bid-tag-1'); expect(data.s[1].sz).to.deep.equal(['1000x300', '970x250', '728x90']); expect(data.s[1].ps).to.be.an('array'); - expect(data.s[1].sid).not.to.be.undefined; expect(data.s[1].ps.length).to.equal(1); expect(data.s[1].ps[0].pn).to.equal('pubmatic'); expect(data.s[1].ps[0].bc).to.equal('pubmatic'); @@ -1086,8 +762,7 @@ describe('pubmatic analytics adapter', function () { expect(data.s[1].ps[0].dc).to.equal('PMP'); expect(data.s[1].ps[0].mi).to.equal('matched-impression'); expect(data.s[1].ps[0].adv).to.equal('example.com'); - expect(data.s[0].ps[0].l1).to.equal(944); - expect(data.s[0].ps[0].ol1).to.equal(3214); + expect(data.s[1].ps[0].l1).to.equal(3214); expect(data.s[1].ps[0].l2).to.equal(0); expect(data.s[1].ps[0].ss).to.equal(1); expect(data.s[1].ps[0].t).to.equal(0); @@ -1119,10 +794,6 @@ describe('pubmatic analytics adapter', function () { let data = getLoggerJsonFromRequest(request.requestBody); expect(data.s[1].sn).to.equal('/19968336/header-bid-tag-1'); expect(data.s[1].fskp).to.equal(0); - expect(data.s[1].sid).not.to.be.undefined; - expect(data.s[1].ffs).to.equal(1); - expect(data.s[1].fsrc).to.equal(2); - expect(data.s[1].fp).to.equal('pubmatic'); expect(data.s[1].sz).to.deep.equal(['1000x300', '970x250', '728x90']); expect(data.s[1].ps).to.be.an('array'); expect(data.s[1].ps.length).to.equal(1); @@ -1139,8 +810,7 @@ describe('pubmatic analytics adapter', function () { expect(data.s[1].ps[0].dc).to.equal('PMP'); expect(data.s[1].ps[0].mi).to.equal('matched-impression'); expect(data.s[1].ps[0].adv).to.equal('example.com'); - expect(data.s[0].ps[0].l1).to.equal(944); - expect(data.s[0].ps[0].ol1).to.equal(3214); + expect(data.s[1].ps[0].l1).to.equal(3214); expect(data.s[1].ps[0].l2).to.equal(0); expect(data.s[1].ps[0].ss).to.equal(1); expect(data.s[1].ps[0].t).to.equal(0); @@ -1148,7 +818,7 @@ describe('pubmatic analytics adapter', function () { expect(data.s[1].ps[0].af).to.equal('banner'); expect(data.s[1].ps[0].ocpm).to.equal(1.52); expect(data.s[1].ps[0].ocry).to.equal('USD'); - expect(data.s[1].ps[0].frv).to.equal(1.1); + expect(data.s[1].ps[0].frv).to.equal(undefined); expect(data.dvc).to.deep.equal({'plt': 2}); // respective tracker slot let firstTracker = requests[1].url; @@ -1182,7 +852,6 @@ describe('pubmatic analytics adapter', function () { let data = getLoggerJsonFromRequest(request.requestBody); expect(data.s[1].sn).to.equal('/19968336/header-bid-tag-1'); expect(data.s[1].sz).to.deep.equal(['1000x300', '970x250', '728x90']); - expect(data.s[1].sid).not.to.be.undefined; expect(data.s[1].ps).to.be.an('array'); expect(data.s[1].ps.length).to.equal(1); expect(data.s[1].ps[0].pn).to.equal('pubmatic'); @@ -1198,8 +867,7 @@ describe('pubmatic analytics adapter', function () { expect(data.s[1].ps[0].dc).to.equal('PMP'); expect(data.s[1].ps[0].mi).to.equal('matched-impression'); expect(data.s[1].ps[0].adv).to.equal('example.com'); - expect(data.s[0].ps[0].l1).to.equal(944); - expect(data.s[0].ps[0].ol1).to.equal(3214); + expect(data.s[1].ps[0].l1).to.equal(3214); expect(data.s[1].ps[0].l2).to.equal(0); expect(data.s[1].ps[0].ss).to.equal(1); expect(data.s[1].ps[0].t).to.equal(0); @@ -1208,7 +876,7 @@ describe('pubmatic analytics adapter', function () { expect(data.s[1].ps[0].ocpm).to.equal(1.52); expect(data.s[1].ps[0].ocry).to.equal('USD'); expect(data.dvc).to.deep.equal({'plt': 1}); - expect(data.s[1].ps[0].frv).to.equal(1.1); + expect(data.s[1].ps[0].frv).to.equal(undefined); // respective tracker slot let firstTracker = requests[1].url; expect(firstTracker.split('?')[0]).to.equal('https://t.pubmatic.com/wt'); @@ -1237,10 +905,6 @@ describe('pubmatic analytics adapter', function () { let data = getLoggerJsonFromRequest(request.requestBody); expect(data.s[1].sn).to.equal('/19968336/header-bid-tag-1'); expect(data.s[1].fskp).to.equal(0); - expect(data.s[1].sid).not.to.be.undefined; - expect(data.s[1].ffs).to.equal(1); - expect(data.s[1].fsrc).to.equal(2); - expect(data.s[1].fp).to.equal('pubmatic'); expect(data.s[1].sz).to.deep.equal(['1000x300', '970x250', '728x90']); expect(data.s[1].ps).to.be.an('array'); expect(data.s[1].ps.length).to.equal(1); @@ -1257,8 +921,7 @@ describe('pubmatic analytics adapter', function () { expect(data.s[1].ps[0].dc).to.equal('PMP'); expect(data.s[1].ps[0].mi).to.equal('matched-impression'); expect(data.s[1].ps[0].adv).to.equal('example.com'); - expect(data.s[0].ps[0].l1).to.equal(944); - expect(data.s[0].ps[0].ol1).to.equal(3214); + expect(data.s[1].ps[0].l1).to.equal(3214); expect(data.s[1].ps[0].l2).to.equal(0); expect(data.s[1].ps[0].ss).to.equal(1); expect(data.s[1].ps[0].t).to.equal(0); @@ -1266,7 +929,7 @@ describe('pubmatic analytics adapter', function () { expect(data.s[1].ps[0].af).to.equal('banner'); expect(data.s[1].ps[0].ocpm).to.equal(1.52); expect(data.s[1].ps[0].ocry).to.equal('USD'); - expect(data.s[1].ps[0].frv).to.equal(1.1); + expect(data.s[1].ps[0].frv).to.equal(undefined); // respective tracker slot let firstTracker = requests[1].url; expect(firstTracker.split('?')[0]).to.equal('https://t.pubmatic.com/wt'); @@ -1298,7 +961,6 @@ describe('pubmatic analytics adapter', function () { let data = getLoggerJsonFromRequest(request.requestBody); expect(data.s[1].sn).to.equal('/19968336/header-bid-tag-1'); expect(data.s[1].sz).to.deep.equal(['1000x300', '970x250', '728x90']); - expect(data.s[1].sid).not.to.be.undefined; expect(data.s[1].ps).to.be.an('array'); expect(data.s[1].ps.length).to.equal(1); expect(data.s[1].ps[0].pn).to.equal('pubmatic'); @@ -1314,8 +976,7 @@ describe('pubmatic analytics adapter', function () { expect(data.s[1].ps[0].dc).to.equal('PMP'); expect(data.s[1].ps[0].mi).to.equal('matched-impression'); expect(data.s[1].ps[0].adv).to.equal('example.com'); - expect(data.s[0].ps[0].l1).to.equal(944); - expect(data.s[0].ps[0].ol1).to.equal(3214); + expect(data.s[1].ps[0].l1).to.equal(3214); expect(data.s[1].ps[0].l2).to.equal(0); expect(data.s[1].ps[0].ss).to.equal(1); expect(data.s[1].ps[0].t).to.equal(0); @@ -1331,65 +992,6 @@ describe('pubmatic analytics adapter', function () { expect(data.kgpv).to.equal('*'); }); - it('Logger: to handle floor rejected bids', function() { - this.timeout(5000) - - sandbox.stub($$PREBID_GLOBAL$$, 'getHighestCpmBids').callsFake((key) => { - return [MOCK.BID_RESPONSE[0], MOCK.BID_RESPONSE[1]] - }); - - events.emit(AUCTION_INIT, MOCK.AUCTION_INIT); - events.emit(BID_REQUESTED, MOCK.BID_REQUESTED); - events.emit(BID_RESPONSE, MOCK.BID_RESPONSE[0]); - events.emit(BID_REJECTED, MOCK.REJECTED_BID[0]); - events.emit(BIDDER_DONE, MOCK.BIDDER_DONE); - events.emit(AUCTION_END, MOCK.AUCTION_END); - events.emit(SET_TARGETING, MOCK.SET_TARGETING); - events.emit(BID_WON, MOCK.BID_WON[0]); - - clock.tick(2000 + 1000); - expect(requests.length).to.equal(2); // 1 logger and 1 win-tracker - let request = requests[1]; // logger is executed late, trackers execute first - expect(request.url).to.equal('https://t.pubmatic.com/wl?pubid=9999'); - let data = getLoggerJsonFromRequest(request.requestBody); - - // slot 2 - // Testing only for rejected bid as other scenarios will be covered under other TCs - expect(data.s[1].sn).to.equal('/19968336/header-bid-tag-1'); - expect(data.s[1].fskp).to.equal(0); - expect(data.s[1].ffs).to.equal(1); - expect(data.s[1].fsrc).to.equal(2); - expect(data.s[1].fp).to.equal('pubmatic'); - expect(data.s[1].sz).to.deep.equal(['1000x300', '970x250', '728x90']); - expect(data.s[1].sid).not.to.be.undefined; - expect(data.s[1].ps).to.be.an('array'); - expect(data.s[1].ps.length).to.equal(1); - expect(data.s[1].ps[0].pn).to.equal('pubmatic'); - expect(data.s[0].ps[0].bc).to.equal('pubmatic'); - expect(data.s[1].ps[0].bidid).to.equal('3bd4ebb1c900e2'); - expect(data.s[1].ps[0].piid).to.equal('partnerImpressionID-2'); - expect(data.s[1].ps[0].db).to.equal(0); - expect(data.s[1].ps[0].kgpv).to.equal('this-is-a-kgpv'); - expect(data.s[1].ps[0].kgpsv).to.equal('this-is-a-kgpv'); - expect(data.s[1].ps[0].psz).to.equal('728x90'); - expect(data.s[1].ps[0].eg).to.equal(1.52); - expect(data.s[1].ps[0].en).to.equal(0); // Net CPM is market as 0 due to bid rejection - expect(data.s[1].ps[0].di).to.equal('the-deal-id'); - expect(data.s[1].ps[0].dc).to.equal('PMP'); - expect(data.s[1].ps[0].mi).to.equal('matched-impression'); - expect(data.s[1].ps[0].adv).to.equal('example.com'); - expect(data.s[0].ps[0].l1).to.equal(944); - expect(data.s[0].ps[0].ol1).to.equal(3214); - expect(data.s[1].ps[0].l2).to.equal(0); - expect(data.s[1].ps[0].ss).to.equal(1); - expect(data.s[1].ps[0].t).to.equal(0); - expect(data.s[1].ps[0].wb).to.equal(1); - expect(data.s[1].ps[0].af).to.equal('banner'); - expect(data.s[1].ps[0].ocpm).to.equal(1.52); - expect(data.s[1].ps[0].ocry).to.equal('USD'); - expect(data.s[1].ps[0].frv).to.equal(1.1); - }); - it('Logger: best case + win tracker in case of Bidder Aliases', function() { MOCK.BID_REQUESTED['bids'][0]['bidder'] = 'pubmatic_alias'; MOCK.BID_REQUESTED['bids'][0]['bidderCode'] = 'pubmatic_alias'; @@ -1428,7 +1030,6 @@ describe('pubmatic analytics adapter', function () { expect(data.tst).to.equal(1519767016); expect(data.tgid).to.equal(15); expect(data.fmv).to.equal('floorModelTest'); - expect(data.pbv).to.equal(getGlobal()?.version || '-1'); expect(data.ft).to.equal(1); expect(data.s).to.be.an('array'); expect(data.s.length).to.equal(2); @@ -1436,13 +1037,9 @@ describe('pubmatic analytics adapter', function () { // slot 1 expect(data.s[0].sn).to.equal('/19968336/header-bid-tag-0'); expect(data.s[0].fskp).to.equal(0); - expect(data.s[0].ffs).to.equal(1); - expect(data.s[0].fsrc).to.equal(2); - expect(data.s[0].fp).to.equal('pubmatic'); expect(data.s[0].sz).to.deep.equal(['640x480']); - expect(data.s[0].sid).not.to.be.undefined; expect(data.s[0].ps).to.be.an('array'); - expect(data.s[0].au).to.equal('/19968336/header-bid-tag-0'); + expect(data.s[0].au).to.equal('/19968336/header-bid-tag-0'); expect(data.s[0].ps.length).to.equal(1); expect(data.s[0].ps[0].pn).to.equal('pubmatic'); expect(data.s[0].ps[0].bc).to.equal('pubmatic_alias'); @@ -1454,10 +1051,9 @@ describe('pubmatic analytics adapter', function () { expect(data.s[0].ps[0].psz).to.equal('640x480'); expect(data.s[0].ps[0].eg).to.equal(1.23); expect(data.s[0].ps[0].en).to.equal(1.23); - expect(data.s[0].ps[0].di).to.equal('-1'); + expect(data.s[0].ps[0].di).to.equal(''); expect(data.s[0].ps[0].dc).to.equal(''); - expect(data.s[0].ps[0].l1).to.equal(944); - expect(data.s[0].ps[0].ol1).to.equal(3214); + expect(data.s[0].ps[0].l1).to.equal(3214); expect(data.s[0].ps[0].l2).to.equal(0); expect(data.s[0].ps[0].ss).to.equal(0); expect(data.s[0].ps[0].t).to.equal(0); @@ -1470,11 +1066,7 @@ describe('pubmatic analytics adapter', function () { // slot 2 expect(data.s[1].sn).to.equal('/19968336/header-bid-tag-1'); expect(data.s[1].fskp).to.equal(0); - expect(data.s[1].ffs).to.equal(1); - expect(data.s[1].fsrc).to.equal(2); - expect(data.s[1].fp).to.equal('pubmatic'); expect(data.s[1].sz).to.deep.equal(['1000x300', '970x250', '728x90']); - expect(data.s[1].sid).not.to.be.undefined; expect(data.s[1].ps).to.be.an('array'); expect(data.s[1].ps.length).to.equal(1); expect(data.s[1].ps[0].pn).to.equal('pubmatic'); @@ -1491,8 +1083,7 @@ describe('pubmatic analytics adapter', function () { expect(data.s[1].ps[0].dc).to.equal('PMP'); expect(data.s[1].ps[0].mi).to.equal('matched-impression'); expect(data.s[1].ps[0].adv).to.equal('example.com'); - expect(data.s[0].ps[0].l1).to.equal(944); - expect(data.s[0].ps[0].ol1).to.equal(3214); + expect(data.s[1].ps[0].l1).to.equal(3214); expect(data.s[1].ps[0].l2).to.equal(0); expect(data.s[1].ps[0].ss).to.equal(1); expect(data.s[1].ps[0].t).to.equal(0); @@ -1500,7 +1091,7 @@ describe('pubmatic analytics adapter', function () { expect(data.s[1].ps[0].af).to.equal('banner'); expect(data.s[1].ps[0].ocpm).to.equal(1.52); expect(data.s[1].ps[0].ocry).to.equal('USD'); - expect(data.s[1].ps[0].frv).to.equal(1.1); + expect(data.s[1].ps[0].frv).to.equal(undefined); // tracker slot1 let firstTracker = requests[0].url; @@ -1558,7 +1149,6 @@ describe('pubmatic analytics adapter', function () { expect(data.tst).to.equal(1519767016); expect(data.tgid).to.equal(15); expect(data.fmv).to.equal('floorModelTest'); - expect(data.pbv).to.equal(getGlobal()?.version || '-1'); expect(data.ft).to.equal(1); expect(data.s).to.be.an('array'); expect(data.s.length).to.equal(2); @@ -1566,13 +1156,9 @@ describe('pubmatic analytics adapter', function () { // slot 1 expect(data.s[0].sn).to.equal('/19968336/header-bid-tag-0'); expect(data.s[0].fskp).to.equal(0); - expect(data.s[0].ffs).to.equal(1); - expect(data.s[0].fsrc).to.equal(2); - expect(data.s[0].fp).to.equal('pubmatic'); expect(data.s[0].sz).to.deep.equal(['640x480']); - expect(data.s[0].sid).not.to.be.undefined; expect(data.s[0].ps).to.be.an('array'); - expect(data.s[0].au).to.equal('/19968336/header-bid-tag-0'); + expect(data.s[0].au).to.equal('/19968336/header-bid-tag-0'); expect(data.s[0].ps.length).to.equal(1); expect(data.s[0].ps[0].pn).to.equal('pubmatic'); expect(data.s[0].ps[0].bc).to.equal('groupm'); @@ -1584,10 +1170,9 @@ describe('pubmatic analytics adapter', function () { expect(data.s[0].ps[0].psz).to.equal('640x480'); expect(data.s[0].ps[0].eg).to.equal(1.23); expect(data.s[0].ps[0].en).to.equal(1.23); - expect(data.s[0].ps[0].di).to.equal('-1'); + expect(data.s[0].ps[0].di).to.equal(''); expect(data.s[0].ps[0].dc).to.equal(''); - expect(data.s[0].ps[0].l1).to.equal(944); - expect(data.s[0].ps[0].ol1).to.equal(3214); + expect(data.s[0].ps[0].l1).to.equal(3214); expect(data.s[0].ps[0].l2).to.equal(0); expect(data.s[0].ps[0].ss).to.equal(0); expect(data.s[0].ps[0].t).to.equal(0); @@ -1600,7 +1185,6 @@ describe('pubmatic analytics adapter', function () { // slot 2 expect(data.s[1].sn).to.equal('/19968336/header-bid-tag-1'); expect(data.s[1].sz).to.deep.equal(['1000x300', '970x250', '728x90']); - expect(data.s[1].sid).not.to.be.undefined; expect(data.s[1].ps).to.be.an('array'); expect(data.s[1].ps.length).to.equal(1); expect(data.s[1].ps[0].pn).to.equal('pubmatic'); @@ -1617,8 +1201,7 @@ describe('pubmatic analytics adapter', function () { expect(data.s[1].ps[0].dc).to.equal('PMP'); expect(data.s[1].ps[0].mi).to.equal('matched-impression'); expect(data.s[1].ps[0].adv).to.equal('example.com'); - expect(data.s[0].ps[0].l1).to.equal(944); - expect(data.s[0].ps[0].ol1).to.equal(3214); + expect(data.s[1].ps[0].l1).to.equal(3214); expect(data.s[1].ps[0].l2).to.equal(0); expect(data.s[1].ps[0].ss).to.equal(1); expect(data.s[1].ps[0].t).to.equal(0); diff --git a/test/spec/modules/pubmaticBidAdapter_spec.js b/test/spec/modules/pubmaticBidAdapter_spec.js index 5d59ff99a89..5d8d762e793 100644 --- a/test/spec/modules/pubmaticBidAdapter_spec.js +++ b/test/spec/modules/pubmaticBidAdapter_spec.js @@ -82,7 +82,6 @@ describe('PubMatic adapter', function () { ortb2Imp: { ext: { tid: '92489f71-1bf2-49a0-adf9-000cea934729', - gpid: '/1111/homepage-leftnav' } }, schain: schainConfig @@ -104,7 +103,6 @@ describe('PubMatic adapter', function () { params: { publisherId: '5890', adSlot: 'Div1@0x0', // ad_id or tagid - wiid: 'new-unique-wiid', video: { mimes: ['video/mp4', 'video/x-flv'], skippable: true, @@ -155,7 +153,6 @@ describe('PubMatic adapter', function () { params: { publisherId: '5890', adSlot: 'Div1@640x480', // ad_id or tagid - wiid: '1234567890', video: { mimes: ['video/mp4', 'video/x-flv'], skippable: true, @@ -215,7 +212,6 @@ describe('PubMatic adapter', function () { params: { publisherId: '5670', adSlot: '/43743431/NativeAutomationPrebid@1x1', - wiid: 'new-unique-wiid' }, bidId: '2a5571261281d4', requestId: 'B68287E1-DC39-4B38-9790-FE4F179739D6', @@ -281,7 +277,6 @@ describe('PubMatic adapter', function () { params: { publisherId: '5670', adSlot: '/43743431/NativeAutomationPrebid@1x1', - wiid: 'new-unique-wiid' }, bidId: '2a5571261281d4', requestId: 'B68287E1-DC39-4B38-9790-FE4F179739D6', @@ -308,7 +303,6 @@ describe('PubMatic adapter', function () { params: { publisherId: '5670', adSlot: '/43743431/NativeAutomationPrebid@1x1', - wiid: 'new-unique-wiid' } }]; @@ -349,7 +343,6 @@ describe('PubMatic adapter', function () { params: { publisherId: '5670', adSlot: '/43743431/NativeAutomationPrebid@1x1', - wiid: 'new-unique-wiid' } }]; @@ -508,7 +501,6 @@ describe('PubMatic adapter', function () { params: { publisherId: '301', adSlot: '/15671365/DMDemo@300x250:0', - wiid: 'new-unique-wiid', video: { mimes: ['video/mp4', 'video/x-flv'], skippable: true, @@ -579,7 +571,6 @@ describe('PubMatic adapter', function () { params: { publisherId: '301', adSlot: '/15671365/DMDemo@300x250:0', - wiid: 'new-unique-wiid', video: { mimes: ['video/mp4', 'video/x-flv'], skippable: true, @@ -1142,19 +1133,7 @@ describe('PubMatic adapter', function () { ortb2: { source: { tid: 'source-tid' - }, - device: { - geo: { - lat: '36.5189', - lon: '-76.4063' - } - }, - user: { - geo: { - lat: '26.8915', - lon: '-56.6340' - } - }, + } } }); let data = JSON.parse(request.data); @@ -1165,10 +1144,10 @@ describe('PubMatic adapter', function () { expect(data.site.publisher.id).to.equal(bidRequests[0].params.publisherId); // publisher Id expect(data.user.yob).to.equal(parseInt(bidRequests[0].params.yob)); // YOB expect(data.user.gender).to.equal(bidRequests[0].params.gender); // Gender - expect(data.device.geo.lat).to.equal('36.5189'); // Latitude - expect(data.device.geo.lon).to.equal('-76.4063'); // Lognitude - expect(data.user.geo.lat).to.equal('26.8915'); // Latitude - expect(data.user.geo.lon).to.equal('-56.6340'); // Lognitude + expect(data.device.geo.lat).to.not.equal(parseFloat(bidRequests[0].params.lat)); // Latitude + expect(data.device.geo.lon).to.not.equal(parseFloat(bidRequests[0].params.lon)); // Lognitude + expect(data.user.geo.lat).to.not.equal(parseFloat(bidRequests[0].params.lat)); // Latitude + expect(data.user.geo.lon).to.not.equal(parseFloat(bidRequests[0].params.lon)); // Lognitude expect(data.ext.wrapper.wv).to.equal($$REPO_AND_VERSION$$); // Wrapper Version expect(data.ext.wrapper.transactionId).to.equal(bidRequests[0].ortb2Imp.ext.tid); // Prebid TransactionId expect(data.source.tid).to.equal('source-tid'); // Prebid TransactionId @@ -1181,7 +1160,6 @@ describe('PubMatic adapter', function () { expect(data.imp[0].tagid).to.equal('/15671365/DMDemo'); // tagid expect(data.imp[0].banner.w).to.equal(300); // width expect(data.imp[0].banner.h).to.equal(250); // height - expect(data.imp[0].ext.gpid).to.equal(bidRequests[0].ortb2Imp.ext.gpid); expect(data.imp[0].ext.pmZoneId).to.equal(bidRequests[0].params.pmzoneid.split(',').slice(0, 50).map(id => id.trim()).join()); // pmzoneid expect(data.imp[0].ext.key_val).to.exist.and.to.equal(bidRequests[0].params.dctr); expect(data.imp[0].bidfloorcur).to.equal(bidRequests[0].params.currency); @@ -1409,21 +1387,7 @@ describe('PubMatic adapter', function () { it('Request params check: without adSlot', function () { delete bidRequests[0].params.adSlot; let request = spec.buildRequests(bidRequests, { - auctionId: 'new-auction-id', - ortb2: { - device: { - geo: { - lat: '36.5189', - lon: '-76.4063' - } - }, - user: { - geo: { - lat: '26.8915', - lon: '-56.6340' - } - }, - } + auctionId: 'new-auction-id' }); let data = JSON.parse(request.data); expect(data.at).to.equal(1); // auction type @@ -1433,10 +1397,10 @@ describe('PubMatic adapter', function () { expect(data.site.publisher.id).to.equal(bidRequests[0].params.publisherId); // publisher Id expect(data.user.yob).to.equal(parseInt(bidRequests[0].params.yob)); // YOB expect(data.user.gender).to.equal(bidRequests[0].params.gender); // Gender - expect(data.device.geo.lat).to.equal('36.5189'); // Latitude - expect(data.device.geo.lon).to.equal('-76.4063'); // Lognitude - expect(data.user.geo.lat).to.equal('26.8915'); // Latitude - expect(data.user.geo.lon).to.equal('-56.6340'); // Lognitude + expect(data.device.geo.lat).to.not.equal(parseFloat(bidRequests[0].params.lat)); // Latitude + expect(data.device.geo.lon).to.not.equal(parseFloat(bidRequests[0].params.lon)); // Lognitude + expect(data.user.geo.lat).to.not.equal(parseFloat(bidRequests[0].params.lat)); // Latitude + expect(data.user.geo.lon).to.not.equal(parseFloat(bidRequests[0].params.lon)); // Lognitude expect(data.ext.wrapper.wv).to.equal($$REPO_AND_VERSION$$); // Wrapper Version expect(data.ext.wrapper.transactionId).to.equal(bidRequests[0].ortb2Imp.ext.tid); // Prebid TransactionId expect(data.ext.wrapper.wiid).to.equal(bidRequests[0].params.wiid); // OpenWrap: Wrapper Impression ID @@ -1449,7 +1413,6 @@ describe('PubMatic adapter', function () { expect(data.imp[0].banner.w).to.equal(728); // width expect(data.imp[0].banner.h).to.equal(90); // height expect(data.imp[0].banner.format).to.deep.equal([{w: 160, h: 600}]); - expect(data.imp[0].ext.gpid).to.equal(bidRequests[0].ortb2Imp.ext.gpid); expect(data.imp[0].ext.key_val).to.exist.and.to.equal(bidRequests[0].params.dctr); expect(data.imp[0].ext.pmZoneId).to.equal(bidRequests[0].params.pmzoneid.split(',').slice(0, 50).map(id => id.trim()).join()); // pmzoneid expect(data.imp[0].bidfloorcur).to.equal(bidRequests[0].params.currency); @@ -1633,21 +1596,7 @@ describe('PubMatic adapter', function () { it('Pass auctiondId as wiid if wiid is not passed in params', function () { let bidRequest = { - auctionId: 'new-auction-id', - ortb2: { - device: { - geo: { - lat: '36.5189', - lon: '-76.4063' - } - }, - user: { - geo: { - lat: '26.8915', - lon: '-56.6340' - } - }, - } + auctionId: 'new-auction-id' }; delete bidRequests[0].params.wiid; let request = spec.buildRequests(bidRequests, bidRequest); @@ -1659,10 +1608,10 @@ describe('PubMatic adapter', function () { expect(data.site.publisher.id).to.equal(bidRequests[0].params.publisherId); // publisher Id expect(data.user.yob).to.equal(parseInt(bidRequests[0].params.yob)); // YOB expect(data.user.gender).to.equal(bidRequests[0].params.gender); // Gender - expect(data.device.geo.lat).to.equal('36.5189'); // Latitude - expect(data.device.geo.lon).to.equal('-76.4063'); // Lognitude - expect(data.user.geo.lat).to.equal('26.8915'); // Latitude - expect(data.user.geo.lon).to.equal('-56.6340'); // Lognitude + expect(data.device.geo.lat).to.not.equal(parseFloat(bidRequests[0].params.lat)); // Latitude + expect(data.device.geo.lon).to.not.equal(parseFloat(bidRequests[0].params.lon)); // Longitude + expect(data.user.geo.lat).to.not.equal(parseFloat(bidRequests[0].params.lat)); // Latitude + expect(data.user.geo.lon).to.not.equal(parseFloat(bidRequests[0].params.lon)); // Longitude expect(data.ext.wrapper.wv).to.equal($$REPO_AND_VERSION$$); // Wrapper Version expect(data.ext.wrapper.transactionId).to.equal(bidRequests[0].ortb2Imp.ext.tid); // Prebid TransactionId expect(data.ext.wrapper.wiid).to.equal('new-auction-id'); // OpenWrap: Wrapper Impression ID @@ -1674,7 +1623,6 @@ describe('PubMatic adapter', function () { expect(data.imp[0].tagid).to.equal('/15671365/DMDemo'); // tagid expect(data.imp[0].banner.w).to.equal(300); // width expect(data.imp[0].banner.h).to.equal(250); // height - expect(data.imp[0].ext.gpid).to.equal(bidRequests[0].ortb2Imp.ext.gpid); expect(data.imp[0].ext.pmZoneId).to.equal(bidRequests[0].params.pmzoneid.split(',').slice(0, 50).map(id => id.trim()).join()); // pmzoneid }); @@ -1683,20 +1631,6 @@ describe('PubMatic adapter', function () { gdprConsent: { consentString: 'kjfdniwjnifwenrif3', gdprApplies: true - }, - ortb2: { - device: { - geo: { - lat: '36.5189', - lon: '-76.4063' - } - }, - user: { - geo: { - lat: '26.8915', - lon: '-56.6340' - } - }, } }; let request = spec.buildRequests(bidRequests, bidRequest); @@ -1710,10 +1644,10 @@ describe('PubMatic adapter', function () { expect(data.site.publisher.id).to.equal(bidRequests[0].params.publisherId); // publisher Id expect(data.user.yob).to.equal(parseInt(bidRequests[0].params.yob)); // YOB expect(data.user.gender).to.equal(bidRequests[0].params.gender); // Gender - expect(data.device.geo.lat).to.equal('36.5189'); // Latitude - expect(data.device.geo.lon).to.equal('-76.4063'); // Lognitude - expect(data.user.geo.lat).to.equal('26.8915'); // Latitude - expect(data.user.geo.lon).to.equal('-56.6340'); // Lognitude + expect(data.device.geo.lat).to.not.equal(parseFloat(bidRequests[0].params.lat)); // Latitude + expect(data.device.geo.lon).to.not.equal(parseFloat(bidRequests[0].params.lon)); // Lognitude + expect(data.user.geo.lat).to.not.equal(parseFloat(bidRequests[0].params.lat)); // Latitude + expect(data.user.geo.lon).to.not.equal(parseFloat(bidRequests[0].params.lon)); // Lognitude expect(data.ext.wrapper.wv).to.equal($$REPO_AND_VERSION$$); // Wrapper Version expect(data.ext.wrapper.transactionId).to.equal(bidRequests[0].ortb2Imp.ext.tid); // Prebid TransactionId expect(data.ext.wrapper.wiid).to.equal(bidRequests[0].params.wiid); // OpenWrap: Wrapper Impression ID @@ -1723,7 +1657,6 @@ describe('PubMatic adapter', function () { expect(data.imp[0].id).to.equal(bidRequests[0].bidId); // Prebid bid id is passed as id expect(data.imp[0].bidfloor).to.equal(parseFloat(bidRequests[0].params.kadfloor)); // kadfloor expect(data.imp[0].tagid).to.equal('/15671365/DMDemo'); // tagid - expect(data.imp[0].ext.gpid).to.equal(bidRequests[0].ortb2Imp.ext.gpid); expect(data.imp[0].banner.w).to.equal(300); // width expect(data.imp[0].banner.h).to.equal(250); // height expect(data.imp[0].ext.pmZoneId).to.equal(bidRequests[0].params.pmzoneid.split(',').slice(0, 50).map(id => id.trim()).join()); // pmzoneid @@ -1731,21 +1664,7 @@ describe('PubMatic adapter', function () { it('Request params check with USP/CCPA Consent', function () { let bidRequest = { - uspConsent: '1NYN', - ortb2: { - device: { - geo: { - lat: '36.5189', - lon: '-76.4063' - } - }, - user: { - geo: { - lat: '26.8915', - lon: '-56.6340' - } - }, - } + uspConsent: '1NYN' }; let request = spec.buildRequests(bidRequests, bidRequest); let data = JSON.parse(request.data); @@ -1757,10 +1676,10 @@ describe('PubMatic adapter', function () { expect(data.site.publisher.id).to.equal(bidRequests[0].params.publisherId); // publisher Id expect(data.user.yob).to.equal(parseInt(bidRequests[0].params.yob)); // YOB expect(data.user.gender).to.equal(bidRequests[0].params.gender); // Gender - expect(data.device.geo.lat).to.equal('36.5189'); // Latitude - expect(data.device.geo.lon).to.equal('-76.4063'); // Lognitude - expect(data.user.geo.lat).to.equal('26.8915'); // Latitude - expect(data.user.geo.lon).to.equal('-56.6340'); // Lognitude + expect(data.device.geo.lat).to.not.equal(parseFloat(bidRequests[0].params.lat)); // Latitude + expect(data.device.geo.lon).to.not.equal(parseFloat(bidRequests[0].params.lon)); // Lognitude + expect(data.user.geo.lat).to.not.equal(parseFloat(bidRequests[0].params.lat)); // Latitude + expect(data.user.geo.lon).to.not.equal(parseFloat(bidRequests[0].params.lon)); // Lognitude expect(data.ext.wrapper.wv).to.equal($$REPO_AND_VERSION$$); // Wrapper Version expect(data.ext.wrapper.transactionId).to.equal(bidRequests[0].ortb2Imp.ext.tid); // Prebid TransactionId expect(data.ext.wrapper.wiid).to.equal(bidRequests[0].params.wiid); // OpenWrap: Wrapper Impression ID @@ -1772,7 +1691,6 @@ describe('PubMatic adapter', function () { expect(data.imp[0].tagid).to.equal('/15671365/DMDemo'); // tagid expect(data.imp[0].banner.w).to.equal(300); // width expect(data.imp[0].banner.h).to.equal(250); // height - expect(data.imp[0].ext.gpid).to.equal(bidRequests[0].ortb2Imp.ext.gpid); expect(data.imp[0].ext.pmZoneId).to.equal(bidRequests[0].params.pmzoneid.split(',').slice(0, 50).map(id => id.trim()).join()); // pmzoneid // second request without USP/CCPA @@ -1922,43 +1840,7 @@ describe('PubMatic adapter', function () { expect(data.user.yob).to.equal(1985); }); - it('ortb2.badv should be merged in the request', function() { - const ortb2 = { - badv: ['example.com'] - }; - const request = spec.buildRequests(bidRequests, {ortb2}); - let data = JSON.parse(request.data); - expect(data.badv).to.deep.equal(['example.com']); - }); - describe('ortb2Imp', function() { - describe('ortb2Imp.ext.gpid', function() { - beforeEach(function () { - if (bidRequests[0].hasOwnProperty('ortb2Imp')) { - delete bidRequests[0].ortb2Imp; - } - }); - - it('should send gpid if imp[].ext.gpid is specified', function() { - bidRequests[0].ortb2Imp = { - ext: { - gpid: 'ortb2Imp.ext.gpid' - } - }; - const request = spec.buildRequests(bidRequests, {}); - let data = JSON.parse(request.data); - expect(data.imp[0].ext).to.have.property('gpid'); - expect(data.imp[0].ext.gpid).to.equal('ortb2Imp.ext.gpid'); - }); - - it('should not send if imp[].ext.gpid is not specified', function() { - bidRequests[0].ortb2Imp = { ext: { } }; - const request = spec.buildRequests(bidRequests, {}); - let data = JSON.parse(request.data); - expect(data.imp[0].ext).to.not.have.property('gpid'); - }); - }); - describe('ortb2Imp.ext.data.pbadslot', function() { beforeEach(function () { if (bidRequests[0].hasOwnProperty('ortb2Imp')) { @@ -2278,7 +2160,7 @@ describe('PubMatic adapter', function () { sandbox.restore(); }); - describe('userIdAsEids', function() { + describe('AdsrvrOrgId from userId module', function() { let sandbox; beforeEach(() => { sandbox = sinon.sandbox.create(); @@ -2288,10 +2170,13 @@ describe('PubMatic adapter', function () { sandbox.restore(); }); - it('Request should have EIDs', function() { + it('Request should have AdsrvrOrgId config params', function() { bidRequests[0].userId = {}; bidRequests[0].userId.tdid = 'TTD_ID_FROM_USER_ID_MODULE'; - bidRequests[0].userIdAsEids = [{ + bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); + let request = spec.buildRequests(bidRequests, {}); + let data = JSON.parse(request.data); + expect(data.user.eids).to.deep.equal([{ 'source': 'adserver.org', 'uids': [{ 'id': 'TTD_ID_FROM_USER_ID_MODULE', @@ -2300,19 +2185,383 @@ describe('PubMatic adapter', function () { 'rtiPartner': 'TDID' } }] - }]; + }]); + }); + + it('Request should have adsrvrOrgId from UserId Module if config and userId module both have TTD ID', function() { + sandbox.stub(config, 'getConfig').callsFake((key) => { + var config = { + adsrvrOrgId: { + 'TDID': 'TTD_ID_FROM_CONFIG', + 'TDID_LOOKUP': 'TRUE', + 'TDID_CREATED_AT': '2018-10-01T07:05:40' + } + }; + return config[key]; + }); + bidRequests[0].userId = {}; + bidRequests[0].userId.tdid = 'TTD_ID_FROM_USER_ID_MODULE'; + bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); + let request = spec.buildRequests(bidRequests, {}); + let data = JSON.parse(request.data); + expect(data.user.eids).to.deep.equal([{ + 'source': 'adserver.org', + 'uids': [{ + 'id': 'TTD_ID_FROM_USER_ID_MODULE', + 'atype': 1, + 'ext': { + 'rtiPartner': 'TDID' + } + }] + }]); + }); + + it('Request should NOT have adsrvrOrgId params if userId is NOT object', function() { let request = spec.buildRequests(bidRequests, {}); let data = JSON.parse(request.data); - expect(data.user.eids).to.deep.equal(bidRequests[0].userIdAsEids); + expect(data.user.eids).to.deep.equal(undefined); }); - it('Request should NOT have EIDs userIdAsEids is NOT object', function() { + it('Request should NOT have adsrvrOrgId params if userId.tdid is NOT string', function() { + bidRequests[0].userId = { + tdid: 1234 + }; let request = spec.buildRequests(bidRequests, {}); let data = JSON.parse(request.data); expect(data.user.eids).to.deep.equal(undefined); }); }); + describe('UserIds from request', function() { + describe('pubcommon Id', function() { + it('send the pubcommon id if it is present', function() { + bidRequests[0].userId = {}; + bidRequests[0].userId.pubcid = 'pub_common_user_id'; + bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); + let request = spec.buildRequests(bidRequests, {}); + let data = JSON.parse(request.data); + expect(data.user.eids).to.deep.equal([{ + 'source': 'pubcid.org', + 'uids': [{ + 'id': 'pub_common_user_id', + 'atype': 1 + }] + }]); + }); + + it('do not pass if not string', function() { + bidRequests[0].userId = {}; + bidRequests[0].userId.pubcid = 1; + bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); + let request = spec.buildRequests(bidRequests, {}); + let data = JSON.parse(request.data); + expect(data.user.eids).to.equal(undefined); + bidRequests[0].userId.pubcid = []; + bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); + request = spec.buildRequests(bidRequests, {}); + data = JSON.parse(request.data); + expect(data.user.eids).to.equal(undefined); + bidRequests[0].userId.pubcid = null; + bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); + request = spec.buildRequests(bidRequests, {}); + data = JSON.parse(request.data); + expect(data.user.eids).to.equal(undefined); + bidRequests[0].userId.pubcid = {}; + bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); + request = spec.buildRequests(bidRequests, {}); + data = JSON.parse(request.data); + expect(data.user.eids).to.equal(undefined); + }); + }); + + describe('ID5 Id', function() { + it('send the id5 id if it is present', function() { + bidRequests[0].userId = {}; + bidRequests[0].userId.id5id = { uid: 'id5-user-id' }; + bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); + let request = spec.buildRequests(bidRequests, {}); + let data = JSON.parse(request.data); + expect(data.user.eids).to.deep.equal([{ + 'source': 'id5-sync.com', + 'uids': [{ + 'id': 'id5-user-id', + 'atype': 1 + }] + }]); + }); + + it('do not pass if not string', function() { + bidRequests[0].userId = {}; + bidRequests[0].userId.id5id = { uid: 1 }; + bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); + let request = spec.buildRequests(bidRequests, {}); + let data = JSON.parse(request.data); + expect(data.user.eids).to.equal(undefined); + bidRequests[0].userId.id5id = { uid: [] }; + bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); + request = spec.buildRequests(bidRequests, {}); + data = JSON.parse(request.data); + expect(data.user.eids).to.equal(undefined); + bidRequests[0].userId.id5id = { uid: null }; + bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); + request = spec.buildRequests(bidRequests, {}); + data = JSON.parse(request.data); + expect(data.user.eids).to.equal(undefined); + bidRequests[0].userId.id5id = { uid: {} }; + bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); + request = spec.buildRequests(bidRequests, {}); + data = JSON.parse(request.data); + expect(data.user.eids).to.equal(undefined); + }); + }); + + describe('Criteo Id', function() { + it('send the criteo id if it is present', function() { + bidRequests[0].userId = {}; + bidRequests[0].userId.criteoId = 'criteo-user-id'; + bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); + let request = spec.buildRequests(bidRequests, {}); + let data = JSON.parse(request.data); + expect(data.user.eids).to.deep.equal([{ + 'source': 'criteo.com', + 'uids': [{ + 'id': 'criteo-user-id', + 'atype': 1 + }] + }]); + }); + + it('do not pass if not string', function() { + bidRequests[0].userId = {}; + bidRequests[0].userId.criteoId = 1; + bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); + let request = spec.buildRequests(bidRequests, {}); + let data = JSON.parse(request.data); + expect(data.user.eids).to.equal(undefined); + bidRequests[0].userId.criteoId = []; + bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); + request = spec.buildRequests(bidRequests, {}); + data = JSON.parse(request.data); + expect(data.user.eids).to.equal(undefined); + bidRequests[0].userId.criteoId = null; + bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); + request = spec.buildRequests(bidRequests, {}); + data = JSON.parse(request.data); + expect(data.user.eids).to.equal(undefined); + bidRequests[0].userId.criteoId = {}; + bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); + request = spec.buildRequests(bidRequests, {}); + data = JSON.parse(request.data); + expect(data.user.eids).to.equal(undefined); + }); + }); + + describe('IdentityLink Id', function() { + it('send the identity-link id if it is present', function() { + bidRequests[0].userId = {}; + bidRequests[0].userId.idl_env = 'identity-link-user-id'; + bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); + let request = spec.buildRequests(bidRequests, {}); + let data = JSON.parse(request.data); + expect(data.user.eids).to.deep.equal([{ + 'source': 'liveramp.com', + 'uids': [{ + 'id': 'identity-link-user-id', + 'atype': 3 + }] + }]); + }); + + it('do not pass if not string', function() { + bidRequests[0].userId = {}; + bidRequests[0].userId.idl_env = 1; + bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); + let request = spec.buildRequests(bidRequests, {}); + let data = JSON.parse(request.data); + expect(data.user.eids).to.equal(undefined); + bidRequests[0].userId.idl_env = []; + bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); + request = spec.buildRequests(bidRequests, {}); + data = JSON.parse(request.data); + expect(data.user.eids).to.equal(undefined); + bidRequests[0].userId.idl_env = null; + bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); + request = spec.buildRequests(bidRequests, {}); + data = JSON.parse(request.data); + expect(data.user.eids).to.equal(undefined); + bidRequests[0].userId.idl_env = {}; + bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); + request = spec.buildRequests(bidRequests, {}); + data = JSON.parse(request.data); + expect(data.user.eids).to.equal(undefined); + }); + }); + + describe('LiveIntent Id', function() { + it('send the LiveIntent id if it is present', function() { + bidRequests[0].userId = {}; + bidRequests[0].userId.lipb = { lipbid: 'live-intent-user-id' }; + bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); + let request = spec.buildRequests(bidRequests, {}); + let data = JSON.parse(request.data); + expect(data.user.eids).to.deep.equal([{ + 'source': 'liveintent.com', + 'uids': [{ + 'id': 'live-intent-user-id', + 'atype': 3 + }] + }]); + }); + + it('do not pass if not string', function() { + bidRequests[0].userId = {}; + bidRequests[0].userId.lipb = { lipbid: 1 }; + bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); + let request = spec.buildRequests(bidRequests, {}); + let data = JSON.parse(request.data); + expect(data.user.eids).to.equal(undefined); + bidRequests[0].userId.lipb.lipbid = []; + bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); + request = spec.buildRequests(bidRequests, {}); + data = JSON.parse(request.data); + expect(data.user.eids).to.equal(undefined); + bidRequests[0].userId.lipb.lipbid = null; + bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); + request = spec.buildRequests(bidRequests, {}); + data = JSON.parse(request.data); + expect(data.user.eids).to.equal(undefined); + bidRequests[0].userId.lipb.lipbid = {}; + bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); + request = spec.buildRequests(bidRequests, {}); + data = JSON.parse(request.data); + expect(data.user.eids).to.equal(undefined); + }); + }); + + describe('Parrable Id', function() { + it('send the Parrable id if it is present', function() { + bidRequests[0].userId = {}; + bidRequests[0].userId.parrableId = { eid: 'parrable-user-id' }; + bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); + let request = spec.buildRequests(bidRequests, {}); + let data = JSON.parse(request.data); + expect(data.user.eids).to.deep.equal([{ + 'source': 'parrable.com', + 'uids': [{ + 'id': 'parrable-user-id', + 'atype': 1 + }] + }]); + }); + + it('do not pass if not object with eid key', function() { + bidRequests[0].userId = {}; + bidRequests[0].userId.parrableid = 1; + bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); + let request = spec.buildRequests(bidRequests, {}); + let data = JSON.parse(request.data); + expect(data.user.eids).to.equal(undefined); + bidRequests[0].userId.parrableid = []; + bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); + request = spec.buildRequests(bidRequests, {}); + data = JSON.parse(request.data); + expect(data.user.eids).to.equal(undefined); + bidRequests[0].userId.parrableid = null; + bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); + request = spec.buildRequests(bidRequests, {}); + data = JSON.parse(request.data); + expect(data.user.eids).to.equal(undefined); + bidRequests[0].userId.parrableid = {}; + bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); + request = spec.buildRequests(bidRequests, {}); + data = JSON.parse(request.data); + expect(data.user.eids).to.equal(undefined); + }); + }); + + describe('Britepool Id', function() { + it('send the Britepool id if it is present', function() { + bidRequests[0].userId = {}; + bidRequests[0].userId.britepoolid = 'britepool-user-id'; + bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); + let request = spec.buildRequests(bidRequests, {}); + let data = JSON.parse(request.data); + expect(data.user.eids).to.deep.equal([{ + 'source': 'britepool.com', + 'uids': [{ + 'id': 'britepool-user-id', + 'atype': 3 + }] + }]); + }); + + it('do not pass if not string', function() { + bidRequests[0].userId = {}; + bidRequests[0].userId.britepoolid = 1; + bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); + let request = spec.buildRequests(bidRequests, {}); + let data = JSON.parse(request.data); + expect(data.user.eids).to.equal(undefined); + bidRequests[0].userId.britepoolid = []; + bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); + request = spec.buildRequests(bidRequests, {}); + data = JSON.parse(request.data); + expect(data.user.eids).to.equal(undefined); + bidRequests[0].userId.britepoolid = null; + bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); + request = spec.buildRequests(bidRequests, {}); + data = JSON.parse(request.data); + expect(data.user.eids).to.equal(undefined); + bidRequests[0].userId.britepoolid = {}; + bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); + request = spec.buildRequests(bidRequests, {}); + data = JSON.parse(request.data); + expect(data.user.eids).to.equal(undefined); + }); + }); + + describe('NetId', function() { + it('send the NetId if it is present', function() { + bidRequests[0].userId = {}; + bidRequests[0].userId.netId = 'netid-user-id'; + bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); + let request = spec.buildRequests(bidRequests, {}); + let data = JSON.parse(request.data); + expect(data.user.eids).to.deep.equal([{ + 'source': 'netid.de', + 'uids': [{ + 'id': 'netid-user-id', + 'atype': 1 + }] + }]); + }); + + it('do not pass if not string', function() { + bidRequests[0].userId = {}; + bidRequests[0].userId.netId = 1; + bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); + let request = spec.buildRequests(bidRequests, {}); + let data = JSON.parse(request.data); + expect(data.user.eids).to.equal(undefined); + bidRequests[0].userId.netId = []; + bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); + request = spec.buildRequests(bidRequests, {}); + data = JSON.parse(request.data); + expect(data.user.eids).to.equal(undefined); + bidRequests[0].userId.netId = null; + bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); + request = spec.buildRequests(bidRequests, {}); + data = JSON.parse(request.data); + expect(data.user.eids).to.equal(undefined); + bidRequests[0].userId.netId = {}; + bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); + request = spec.buildRequests(bidRequests, {}); + data = JSON.parse(request.data); + expect(data.user.eids).to.equal(undefined); + }); + }); + }); + it('should pass device.sua if present in bidderRequest fpd ortb2 object', function () { const suaObject = {'source': 2, 'platform': {'brand': 'macOS', 'version': ['12', '4', '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': 0, 'model': '', 'bitness': '64', 'architecture': 'x86'}; let request = spec.buildRequests(multipleMediaRequests, { @@ -2328,23 +2577,6 @@ describe('PubMatic adapter', function () { expect(data.device.sua).to.deep.equal(suaObject); }); - it('should pass device.ext.cdep if present in bidderRequest fpd ortb2 object', function () { - const cdepObj = { - cdep: 'example_label_1' - }; - let request = spec.buildRequests(multipleMediaRequests, { - auctionId: 'new-auction-id', - ortb2: { - device: { - ext: cdepObj - } - } - }); - let data = JSON.parse(request.data); - expect(data.device.ext.cdep).to.exist.and.to.be.an('string'); - expect(data.device.ext).to.deep.equal(cdepObj); - }); - it('Request params should have valid native bid request for all valid params', function () { let request = spec.buildRequests(nativeBidRequests, { auctionId: 'new-auction-id' @@ -2460,21 +2692,7 @@ describe('PubMatic adapter', function () { it('Request params check for 1 banner and 1 video ad', function () { let request = spec.buildRequests(multipleMediaRequests, { - auctionId: 'new-auction-id', - ortb2: { - device: { - geo: { - lat: '36.5189', - lon: '-76.4063' - } - }, - user: { - geo: { - lat: '26.8915', - lon: '-56.6340' - } - }, - } + auctionId: 'new-auction-id' }); let data = JSON.parse(request.data); @@ -2488,10 +2706,10 @@ describe('PubMatic adapter', function () { expect(data.site.publisher.id).to.equal(multipleMediaRequests[0].params.publisherId); // publisher Id expect(data.user.yob).to.equal(parseInt(multipleMediaRequests[0].params.yob)); // YOB expect(data.user.gender).to.equal(multipleMediaRequests[0].params.gender); // Gender - expect(data.device.geo.lat).to.equal('36.5189'); // Latitude - expect(data.device.geo.lon).to.equal('-76.4063'); // Lognitude - expect(data.user.geo.lat).to.equal('26.8915'); // Latitude - expect(data.user.geo.lon).to.equal('-56.6340'); // Lognitude + expect(data.device.geo.lat).to.not.equal(parseFloat(multipleMediaRequests[0].params.lat)); // Latitude + expect(data.device.geo.lon).to.not.equal(parseFloat(multipleMediaRequests[0].params.lon)); // Lognitude + expect(data.user.geo.lat).to.not.equal(parseFloat(multipleMediaRequests[0].params.lat)); // Latitude + expect(data.user.geo.lon).to.not.equal(parseFloat(multipleMediaRequests[0].params.lon)); // Lognitude expect(data.ext.wrapper.wv).to.equal($$REPO_AND_VERSION$$); // Wrapper Version expect(data.ext.wrapper.transactionId).to.equal(multipleMediaRequests[0].transactionId); // Prebid TransactionId expect(data.ext.wrapper.wiid).to.equal(multipleMediaRequests[0].params.wiid); // OpenWrap: Wrapper Impression ID @@ -2839,76 +3057,6 @@ describe('PubMatic adapter', function () { }); } - describe('GPP', function() { - it('Request params check with GPP Consent', function () { - let bidRequest = { - gppConsent: { - 'gppString': 'DBACNYA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA~1YNN', - 'fullGppData': { - 'sectionId': 3, - 'gppVersion': 1, - 'sectionList': [ - 5, - 7 - ], - 'applicableSections': [ - 5 - ], - 'gppString': 'DBACNYA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA~1YNN', - 'pingData': { - 'cmpStatus': 'loaded', - 'gppVersion': '1.0', - 'cmpDisplayStatus': 'visible', - 'supportedAPIs': [ - 'tcfca', - 'usnat', - 'usca', - 'usva', - 'usco', - 'usut', - 'usct' - ], - 'cmpId': 31 - }, - 'eventName': 'sectionChange' - }, - 'applicableSections': [ - 5 - ], - 'apiVersion': 1 - } - }; - let request = spec.buildRequests(bidRequests, bidRequest); - let data = JSON.parse(request.data); - expect(data.regs.gpp).to.equal('DBACNYA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA~1YNN'); - expect(data.regs.gpp_sid[0]).to.equal(5); - }); - - it('Request params check without GPP Consent', function () { - let bidRequest = {}; - let request = spec.buildRequests(bidRequests, bidRequest); - let data = JSON.parse(request.data); - expect(data.regs).to.equal(undefined); - }); - - it('Request params check with GPP Consent read from ortb2', function () { - let bidRequest = { - ortb2: { - regs: { - 'gpp': 'DBACNYA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA~1YNN', - 'gpp_sid': [ - 5 - ] - } - } - }; - let request = spec.buildRequests(bidRequests, bidRequest); - let data = JSON.parse(request.data); - expect(data.regs.gpp).to.equal('DBACNYA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA~1YNN'); - expect(data.regs.gpp_sid[0]).to.equal(5); - }); - }); - describe('Fledge', function() { it('should not send imp.ext.ae when FLEDGE is disabled, ', function () { let bidRequest = Object.assign([], bidRequests); @@ -3668,89 +3816,6 @@ describe('PubMatic adapter', function () { } }); - describe('Fledge Auction config Response', function () { - let response; - let bidRequestConfigs = [ - { - bidder: 'pubmatic', - mediaTypes: { - banner: { - sizes: [[728, 90], [160, 600]] - } - }, - params: { - publisherId: '5670', - adSlot: '/15671365/DMDemo@300x250:0', - kadfloor: '1.2', - pmzoneid: 'aabc, ddef', - kadpageurl: 'www.publisher.com', - yob: '1986', - gender: 'M', - lat: '12.3', - lon: '23.7', - wiid: '1234567890', - profId: '100', - verId: '200', - currency: 'AUD', - dctr: 'key1:val1,val2|key2:val1' - }, - placementCode: '/19968336/header-bid-tag-1', - sizes: [[300, 250], [300, 600]], - bidId: 'test_bid_id', - requestId: '0fb4905b-9456-4152-86be-c6f6d259ba99', - bidderRequestId: '1c56ad30b9b8ca8', - ortb2Imp: { - ext: { - tid: '92489f71-1bf2-49a0-adf9-000cea934729', - ae: 1 - } - }, - } - ]; - - let bidRequest = spec.buildRequests(bidRequestConfigs, {}); - let bidResponse = { - seatbid: [{ - bid: [{ - impid: 'test_bid_id', - price: 2, - w: 728, - h: 250, - crid: 'test-creative-id', - dealid: 'test-deal-id', - adm: 'test-ad-markup' - }] - }], - cur: 'AUS', - ext: { - fledge_auction_configs: { - 'test_bid_id': { - seller: 'ads.pubmatic.com', - interestGroupBuyers: ['dsp1.com'], - sellerTimeout: 0, - perBuyerSignals: { - 'dsp1.com': { - bid_macros: 0.1, - disallowed_adv_ids: [ - '5678', - '5890' - ], - } - } - } - } - } - }; - - response = spec.interpretResponse({ body: bidResponse }, bidRequest); - it('should return FLEDGE auction_configs alongside bids', function () { - expect(response).to.have.property('bids'); - expect(response).to.have.property('fledgeAuctionConfigs'); - expect(response.fledgeAuctionConfigs.length).to.equal(1); - expect(response.fledgeAuctionConfigs[0].bidId).to.equal('test_bid_id'); - }); - }); - describe('Preapare metadata', function () { it('Should copy all fields from ext to meta', function () { const bid = { @@ -3926,55 +3991,6 @@ describe('PubMatic adapter', function () { type: 'image', url: `${syncurl_image}&gdpr=1&gdpr_consent=foo&us_privacy=1NYN&coppa=1` }]); }); - - describe('GPP', function() { - it('should return userSync url without Gpp consent if gppConsent is undefined', () => { - const result = spec.getUserSyncs({iframeEnabled: true}, undefined, undefined, undefined, undefined); - expect(result).to.deep.equal([{ - type: 'iframe', url: `${syncurl_iframe}` - }]); - }); - - it('should return userSync url without Gpp consent if gppConsent.gppString is undefined', () => { - const gppConsent = { applicableSections: ['5'] }; - const result = spec.getUserSyncs({iframeEnabled: true}, undefined, undefined, undefined, gppConsent); - expect(result).to.deep.equal([{ - type: 'iframe', url: `${syncurl_iframe}` - }]); - }); - - it('should return userSync url without Gpp consent if gppConsent.applicableSections is undefined', () => { - const gppConsent = { gppString: 'DBACNYA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA~1YNN' }; - const result = spec.getUserSyncs({iframeEnabled: true}, undefined, undefined, undefined, gppConsent); - expect(result).to.deep.equal([{ - type: 'iframe', url: `${syncurl_iframe}` - }]); - }); - - it('should return userSync url without Gpp consent if gppConsent.applicableSections is an empty array', () => { - const gppConsent = { gppString: 'DBACNYA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA~1YNN', applicableSections: [] }; - const result = spec.getUserSyncs({iframeEnabled: true}, undefined, undefined, undefined, gppConsent); - expect(result).to.deep.equal([{ - type: 'iframe', url: `${syncurl_iframe}` - }]); - }); - - it('should concatenate gppString and applicableSections values in the returned userSync iframe url', () => { - const gppConsent = { gppString: 'DBACNYA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA~1YNN', applicableSections: [5] }; - const result = spec.getUserSyncs({iframeEnabled: true}, undefined, undefined, undefined, gppConsent); - expect(result).to.deep.equal([{ - type: 'iframe', url: `${syncurl_iframe}&gpp=${encodeURIComponent(gppConsent.gppString)}&gpp_sid=${encodeURIComponent(gppConsent.applicableSections)}` - }]); - }); - - it('should concatenate gppString and applicableSections values in the returned userSync image url', () => { - const gppConsent = { gppString: 'DBACNYA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA~1YNN', applicableSections: [5] }; - const result = spec.getUserSyncs({iframeEnabled: false}, undefined, undefined, undefined, gppConsent); - expect(result).to.deep.equal([{ - type: 'image', url: `${syncurl_image}&gpp=${encodeURIComponent(gppConsent.gppString)}&gpp_sid=${encodeURIComponent(gppConsent.applicableSections)}` - }]); - }); - }); }); if (FEATURES.VIDEO) { diff --git a/test/spec/modules/pubwiseAnalyticsAdapter_spec.js b/test/spec/modules/pubwiseAnalyticsAdapter_spec.js index 92d5972cc13..e14582edc39 100644 --- a/test/spec/modules/pubwiseAnalyticsAdapter_spec.js +++ b/test/spec/modules/pubwiseAnalyticsAdapter_spec.js @@ -1,7 +1,6 @@ import {expect} from 'chai'; import pubwiseAnalytics from 'modules/pubwiseAnalyticsAdapter.js'; import {expectEvents} from '../../helpers/analytics.js'; -import {server} from '../../mocks/xhr.js'; let events = require('src/events'); let adapterManager = require('src/adapterManager').default; @@ -10,6 +9,7 @@ let constants = require('src/constants.json'); describe('PubWise Prebid Analytics', function () { let requests; let sandbox; + let xhr; let clock; let mock = {}; @@ -38,7 +38,9 @@ describe('PubWise Prebid Analytics', function () { clock = sandbox.useFakeTimers(); sandbox.stub(events, 'getEvents').returns([]); - requests = server.requests; + xhr = sandbox.useFakeXMLHttpRequest(); + requests = []; + xhr.onCreate = request => requests.push(request); }); afterEach(function () { @@ -48,6 +50,10 @@ describe('PubWise Prebid Analytics', function () { }); describe('enableAnalytics', function () { + beforeEach(function () { + requests = []; + }); + it('should catch all events', function () { pubwiseAnalytics.enableAnalytics(mock.DEFAULT_PW_CONFIG); diff --git a/test/spec/modules/pubxaiAnalyticsAdapter_spec.js b/test/spec/modules/pubxaiAnalyticsAdapter_spec.js index e0f4497a8c8..1dce87e4b8e 100644 --- a/test/spec/modules/pubxaiAnalyticsAdapter_spec.js +++ b/test/spec/modules/pubxaiAnalyticsAdapter_spec.js @@ -1,9 +1,13 @@ -import pubxaiAnalyticsAdapter, {getBrowser, getDeviceType, getOS} from 'modules/pubxaiAnalyticsAdapter.js'; -import {expect} from 'chai'; +import pubxaiAnalyticsAdapter from 'modules/pubxaiAnalyticsAdapter.js'; +import { getDeviceType, getBrowser, getOS } from 'modules/pubxaiAnalyticsAdapter.js'; +import { + expect +} from 'chai'; import adapterManager from 'src/adapterManager.js'; import * as utils from 'src/utils.js'; -import {server} from 'test/mocks/xhr.js'; -import {getGptSlotInfoForAdUnitCode} from '../../../libraries/gptUtils/gptUtils.js'; +import { + server +} from 'test/mocks/xhr.js'; let events = require('src/events'); let constants = require('src/constants.json'); @@ -523,7 +527,7 @@ describe('pubxai analytics adapter', function() { 'bidderCode': 'appnexus', 'bidId': '248f9a4489835e', 'adUnitCode': '/19968336/header-bid-tag-1', - 'gptSlotCode': getGptSlotInfoForAdUnitCode('/19968336/header-bid-tag-1').gptSlot || null, + 'gptSlotCode': utils.getGptSlotInfoForAdUnitCode('/19968336/header-bid-tag-1').gptSlot || null, 'auctionId': 'bc3806e4-873e-453c-8ae5-204f35e923b4', 'sizes': '300x250', 'renderStatus': 2, @@ -592,7 +596,7 @@ describe('pubxai analytics adapter', function() { let expectedAfterBidWon = { 'winningBid': { 'adUnitCode': '/19968336/header-bid-tag-1', - 'gptSlotCode': getGptSlotInfoForAdUnitCode('/19968336/header-bid-tag-1').gptSlot || null, + 'gptSlotCode': utils.getGptSlotInfoForAdUnitCode('/19968336/header-bid-tag-1').gptSlot || null, 'auctionId': 'bc3806e4-873e-453c-8ae5-204f35e923b4', 'bidderCode': 'appnexus', 'bidId': '248f9a4489835e', diff --git a/test/spec/modules/pulsepointBidAdapter_spec.js b/test/spec/modules/pulsepointBidAdapter_spec.js index 8db7e909771..60dca9e6da0 100644 --- a/test/spec/modules/pulsepointBidAdapter_spec.js +++ b/test/spec/modules/pulsepointBidAdapter_spec.js @@ -1,8 +1,7 @@ /* eslint dot-notation:0, quote-props:0 */ import {expect} from 'chai'; import {spec} from 'modules/pulsepointBidAdapter.js'; -import {syncAddFPDToBidderRequest} from '../../helpers/fpd.js'; -import {deepClone} from '../../../src/utils'; +import {deepClone} from 'src/utils.js'; describe('PulsePoint Adapter Tests', function () { const slotConfigs = [{ @@ -32,52 +31,39 @@ describe('PulsePoint Adapter Tests', function () { cf: '728x90' } }]; - const nativeOrtbRequest = { - assets: [{ - id: 1, - required: 1, - img: { - type: 3, - w: 150, - h: 50, - } - }, - { - id: 2, - required: 1, - title: { - len: 80 - } - }, - { - id: 3, - required: 0, - data: { - type: 1 - } - }] - }; const nativeSlotConfig = [{ placementCode: '/DfpAccount1/slot3', bidId: 'bid12345', - mediaTypes: { - native: { - sendTargetingKeys: false, - ortb: nativeOrtbRequest - } + nativeParams: { + title: { required: true, len: 200 }, + image: { wmin: 100 }, + sponsoredBy: { } }, - nativeOrtbRequest, params: { cp: 'p10000', ct: 't10000' } }]; + const appSlotConfig = [{ + placementCode: '/DfpAccount1/slot3', + bidId: 'bid12345', + params: { + cp: 'p10000', + ct: 't10000', + app: { + bundle: 'com.pulsepoint.apps', + storeUrl: 'https://pulsepoint.com/apps', + domain: 'pulsepoint.com', + } + } + }]; const videoSlotConfig = [{ placementCode: '/DfpAccount1/slotVideo', bidId: 'bid12345', - mediaTypes: { + params: { + cp: 'p10000', + ct: 't10000', video: { - playerSize: [400, 300], w: 400, h: 300, minduration: 5, @@ -87,10 +73,6 @@ describe('PulsePoint Adapter Tests', function () { minbitrate: 200, protocols: [1, 2, 4] } - }, - params: { - cp: 'p10000', - ct: 't10000' } }]; const additionalParamsConfig = [{ @@ -115,6 +97,68 @@ describe('PulsePoint Adapter Tests', function () { } }]; + const ortbParamsSlotConfig = [{ + placementCode: '/DfpAccount1/slot1', + mediaTypes: { + banner: { + sizes: [[1, 1]] + } + }, + bidId: 'bid12345', + params: { + cp: 'p10000', + ct: 't10000', + cf: '1x1', + bcat: ['IAB-1', 'IAB-20'], + battr: [1, 2, 3], + bidfloor: 1.5, + badv: ['cocacola.com', 'lays.com'] + } + }, { + placementCode: '/DfpAccount1/slotVideo', + bidId: 'bid12345', + params: { + cp: 'p10000', + ct: 't10000', + video: { + w: 400, + h: 300, + minduration: 5, + maxduration: 10, + }, + battr: [2, 3, 4], + bidfloor: 2.5, + } + }]; + + const outstreamSlotConfig = [{ + placementCode: '/DfpAccount1/slot1', + mediaTypes: { + video: { + playerSize: [640, 480], + context: 'outstream' + } + }, + bidId: 'bid12345', + params: { + cp: 'p10000', + ct: 't10000', + cf: '1x1', + video: { + h: 300, + w: 400, + minduration: 1, + maxduration: 210, + linearity: 1, + } + }, + renderer: { + options: { + text: 'PulsePoint Outstream' + } + } + }]; + const schainParamsSlotConfig = [{ placementCode: '/DfpAccount1/slot1', mediaTypes: { @@ -156,7 +200,7 @@ describe('PulsePoint Adapter Tests', function () { }; it('Verify build request', function () { - const request = spec.buildRequests(slotConfigs, syncAddFPDToBidderRequest(bidderRequest)); + const request = spec.buildRequests(slotConfigs, bidderRequest); expect(request.url).to.equal('https://bid.contextweb.com/header/ortb?src=prebid'); expect(request.method).to.equal('POST'); const ortbRequest = request.data; @@ -164,6 +208,7 @@ describe('PulsePoint Adapter Tests', function () { expect(ortbRequest.site).to.not.equal(null); expect(ortbRequest.site.publisher).to.not.equal(null); expect(ortbRequest.site.publisher.id).to.equal('p10000'); + expect(ortbRequest.site.ref).to.equal(bidderRequest.refererInfo.ref); expect(ortbRequest.site.page).to.equal('https://publisher.com/home'); expect(ortbRequest.imp).to.have.lengthOf(2); // device object @@ -172,15 +217,19 @@ 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.w).to.equal(300); + expect(ortbRequest.imp[0].banner.h).to.equal(250); // 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.w).to.equal(728); + expect(ortbRequest.imp[1].banner.h).to.equal(90); + // tmax + expect(ortbRequest.tmax).to.equal(500); }); it('Verify parse response', function () { - const request = spec.buildRequests(slotConfigs, syncAddFPDToBidderRequest(bidderRequest)); + const request = spec.buildRequests(slotConfigs, bidderRequest); const ortbRequest = request.data; const ortbResponse = { seatbid: [{ @@ -188,16 +237,11 @@ describe('PulsePoint Adapter Tests', function () { impid: ortbRequest.imp[0].id, price: 1.25, adm: 'This is an Ad', - crid: 'Creative#123', - mtype: 1, - w: 300, - h: 250, - exp: 20, - adomain: ['advertiser.com'] + crid: 'Creative#123' }] }] }; - 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]; @@ -205,112 +249,140 @@ describe('PulsePoint Adapter Tests', function () { expect(bid.ad).to.equal('This is an Ad'); expect(bid.width).to.equal(300); expect(bid.height).to.equal(250); + expect(bid.adId).to.equal('bid12345'); expect(bid.creative_id).to.equal('Creative#123'); expect(bid.creativeId).to.equal('Creative#123'); expect(bid.netRevenue).to.equal(true); expect(bid.currency).to.equal('USD'); expect(bid.ttl).to.equal(20); + }); + + it('Verify ttl/currency/adomain applied to bid', function () { + const request = spec.buildRequests(slotConfigs, bidderRequest); + const ortbRequest = request.data; + const ortbResponse = { + seatbid: [{ + bid: [{ + impid: ortbRequest.imp[0].id, + price: 1.25, + adm: 'This is an Ad#1', + crid: 'Creative#123', + exp: 50, + adomain: ['advertiser.com'] + }, { + impid: ortbRequest.imp[1].id, + price: 1.25, + adm: 'This is an Ad#2', + crid: 'Creative#123' + }] + }], + cur: 'GBP' + }; + const bids = spec.interpretResponse({ body: ortbResponse }, request); + expect(bids).to.have.lengthOf(2); + // verify first bid + const bid = bids[0]; + expect(bid.cpm).to.equal(1.25); + expect(bid.ad).to.equal('This is an Ad#1'); + expect(bid.ttl).to.equal(50); + expect(bid.currency).to.equal('GBP'); expect(bid.meta).to.not.be.null; expect(bid.meta.advertiserDomains).to.eql(['advertiser.com']); + const secondBid = bids[1]; + expect(secondBid.cpm).to.equal(1.25); + expect(secondBid.ad).to.equal('This is an Ad#2'); + expect(secondBid.ttl).to.equal(20); + expect(secondBid.currency).to.equal('GBP'); + expect(secondBid.meta).to.not.be.null; + expect(secondBid.meta.advertiserDomains).to.eql([]); }); 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); }); - if (FEATURES.NATIVE) { - it('Verify Native request', function () { - const request = spec.buildRequests(nativeSlotConfig, syncAddFPDToBidderRequest(bidderRequest)); - expect(request.url).to.equal('https://bid.contextweb.com/header/ortb?src=prebid'); - expect(request.method).to.equal('POST'); - const ortbRequest = request.data; - // native impression - expect(ortbRequest.imp[0].tagid).to.equal('t10000'); - expect(ortbRequest.imp[0].banner).to.be.undefined; - const nativePart = ortbRequest.imp[0]['native']; - expect(nativePart).to.not.equal(null); - expect(nativePart.request).to.not.equal(null); - // native request assets - const nativeRequest = JSON.parse(ortbRequest.imp[0]['native'].request); - expect(nativeRequest).to.not.equal(null); - expect(nativeRequest.assets).to.have.lengthOf(3); - // image asset - expect(nativeRequest.assets[0].id).to.equal(1); - expect(nativeRequest.assets[0].required).to.equal(1); - expect(nativeRequest.assets[0].title).to.be.undefined; - expect(nativeRequest.assets[0].img).to.not.equal(null); - expect(nativeRequest.assets[0].img.w).to.equal(150); - expect(nativeRequest.assets[0].img.h).to.equal(50); - expect(nativeRequest.assets[0].img.type).to.equal(3); - // title asset - expect(nativeRequest.assets[1].id).to.equal(2); - expect(nativeRequest.assets[1].required).to.equal(1); - expect(nativeRequest.assets[1].title).to.not.equal(null); - expect(nativeRequest.assets[1].title.len).to.equal(80); - // data asset - expect(nativeRequest.assets[2].id).to.equal(3); - expect(nativeRequest.assets[2].required).to.equal(0); - expect(nativeRequest.assets[2].title).to.be.undefined; - expect(nativeRequest.assets[2].data).to.not.equal(null); - expect(nativeRequest.assets[2].data.type).to.equal(1); - }); + it('Verify Native request', function () { + const request = spec.buildRequests(nativeSlotConfig, bidderRequest); + expect(request.url).to.equal('https://bid.contextweb.com/header/ortb?src=prebid'); + expect(request.method).to.equal('POST'); + const ortbRequest = request.data; + // native impression + expect(ortbRequest.imp[0].tagid).to.equal('t10000'); + expect(ortbRequest.imp[0].banner).to.equal(null); + const nativePart = ortbRequest.imp[0]['native']; + expect(nativePart).to.not.equal(null); + expect(nativePart.ver).to.equal('1.1'); + expect(nativePart.request).to.not.equal(null); + // native request assets + const nativeRequest = JSON.parse(ortbRequest.imp[0]['native'].request); + expect(nativeRequest).to.not.equal(null); + expect(nativeRequest.assets).to.have.lengthOf(3); + // title asset + expect(nativeRequest.assets[0].id).to.equal(1); + expect(nativeRequest.assets[0].required).to.equal(1); + expect(nativeRequest.assets[0].title).to.not.equal(null); + expect(nativeRequest.assets[0].title.len).to.equal(200); + // data asset + expect(nativeRequest.assets[1].id).to.equal(2); + expect(nativeRequest.assets[1].required).to.equal(0); + expect(nativeRequest.assets[1].title).to.be.undefined; + expect(nativeRequest.assets[1].data).to.not.equal(null); + expect(nativeRequest.assets[1].data.type).to.equal(1); + expect(nativeRequest.assets[1].data.len).to.equal(50); + // image asset + expect(nativeRequest.assets[2].id).to.equal(3); + expect(nativeRequest.assets[2].required).to.equal(0); + expect(nativeRequest.assets[2].title).to.be.undefined; + expect(nativeRequest.assets[2].img).to.not.equal(null); + expect(nativeRequest.assets[2].img.wmin).to.equal(100); + expect(nativeRequest.assets[2].img.hmin).to.equal(150); + expect(nativeRequest.assets[2].img.type).to.equal(3); + }); - it('Verify Native response', function () { - const request = spec.buildRequests(nativeSlotConfig, syncAddFPDToBidderRequest(bidderRequest)); - expect(request.url).to.equal('https://bid.contextweb.com/header/ortb?src=prebid'); - expect(request.method).to.equal('POST'); - const ortbRequest = request.data; - const nativeResponse = { + it('Verify Native response', function () { + const request = spec.buildRequests(nativeSlotConfig, bidderRequest); + expect(request.url).to.equal('https://bid.contextweb.com/header/ortb?src=prebid'); + expect(request.method).to.equal('POST'); + const ortbRequest = request.data; + const nativeResponse = { + 'native': { 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'}} + { title: { text: 'Ad Title' } }, + { data: { type: 1, value: 'Sponsored By: Brand' } }, + { img: { type: 3, url: 'https://images.cdn.brand.com/123' } } ], - link: {url: 'https://brand.clickme.com/'}, + link: { url: 'https://brand.clickme.com/' }, imptrackers: ['https://imp1.trackme.com/', 'https://imp1.contextweb.com/'] - - }; - const ortbResponse = { - seatbid: [{ - bid: [{ - impid: ortbRequest.imp[0].id, - price: 1.25, - adm: JSON.stringify(nativeResponse), - mtype: 4 - }] + } + }; + const ortbResponse = { + seatbid: [{ + bid: [{ + impid: ortbRequest.imp[0].id, + price: 1.25, + adm: JSON.stringify(nativeResponse) }] - }; - const bids = spec.interpretResponse({body: ortbResponse}, request); - // verify bid - const bid = bids[0]; - expect(bid.cpm).to.equal(1.25); - expect(bid.requestId).to.equal('bid12345'); - expect(bid.ad).to.be.undefined; - expect(bid.mediaType).to.equal('native'); - expect(bid['native']).to.not.be.null; - expect(bid['native'].ortb).to.not.be.null; - const nativeBid = bid['native'].ortb; - expect(nativeBid.assets).to.have.lengthOf(3); - expect(nativeBid.assets[0].id).to.equal(1); - expect(nativeBid.assets[0].img).to.not.be.null; - expect(nativeBid.assets[0].img.type).to.equal(3); - expect(nativeBid.assets[0].img.url).to.equal('https://images.cdn.brand.com/123'); - expect(nativeBid.assets[1].id).to.equal(2); - expect(nativeBid.assets[1].title).to.not.be.null; - expect(nativeBid.assets[1].title.text).to.equal('Ad Title'); - expect(nativeBid.assets[2].id).to.equal(3); - expect(nativeBid.assets[2].data).to.not.be.null; - expect(nativeBid.assets[2].data.type).to.equal(1); - expect(nativeBid.assets[2].data.value).to.equal('Sponsored By: Brand'); - expect(nativeBid.link).to.not.be.null; - expect(nativeBid.link.url).to.equal('https://brand.clickme.com/'); - expect(nativeBid.imptrackers).to.have.lengthOf(2); - expect(nativeBid.imptrackers[0]).to.equal('https://imp1.trackme.com/'); - expect(nativeBid.imptrackers[1]).to.equal('https://imp1.contextweb.com/'); - }); - } + }] + }; + const bids = spec.interpretResponse({ body: ortbResponse }, request); + // verify bid + const bid = bids[0]; + expect(bid.cpm).to.equal(1.25); + expect(bid.adId).to.equal('bid12345'); + expect(bid.ad).to.be.undefined; + expect(bid.mediaType).to.equal('native'); + const nativeBid = bid['native']; + expect(nativeBid).to.not.equal(null); + expect(nativeBid.title).to.equal('Ad Title'); + expect(nativeBid.sponsoredBy).to.equal('Sponsored By: Brand'); + expect(nativeBid.image).to.equal('https://images.cdn.brand.com/123'); + expect(nativeBid.clickUrl).to.equal(encodeURIComponent('https://brand.clickme.com/')); + expect(nativeBid.impressionTrackers).to.have.lengthOf(2); + expect(nativeBid.impressionTrackers[0]).to.equal('https://imp1.trackme.com/'); + expect(nativeBid.impressionTrackers[1]).to.equal('https://imp1.contextweb.com/'); + }); it('Verifies bidder code', function () { expect(spec.code).to.equal('pulsepoint'); @@ -358,6 +430,19 @@ describe('PulsePoint Adapter Tests', function () { expect(options[0].url).to.equal('https://bh.contextweb.com/visitormatch/prebid'); }); + it('Verify app requests', function () { + const request = spec.buildRequests(appSlotConfig, bidderRequest); + const ortbRequest = request.data; + // site object + expect(ortbRequest.site).to.equal(null); + expect(ortbRequest.app).to.not.be.null; + expect(ortbRequest.app.publisher).to.not.equal(null); + expect(ortbRequest.app.publisher.id).to.equal('p10000'); + expect(ortbRequest.app.bundle).to.equal('com.pulsepoint.apps'); + expect(ortbRequest.app.storeurl).to.equal('https://pulsepoint.com/apps'); + expect(ortbRequest.app.domain).to.equal('pulsepoint.com'); + }); + it('Verify GDPR', function () { const bidderRequestGdpr = { gdprConsent: { @@ -365,7 +450,7 @@ describe('PulsePoint Adapter Tests', function () { consentString: 'serialized_gpdr_data' } }; - const request = spec.buildRequests(slotConfigs, syncAddFPDToBidderRequest(Object.assign({}, bidderRequest, bidderRequestGdpr))); + const request = spec.buildRequests(slotConfigs, Object.assign({}, bidderRequest, bidderRequestGdpr)); expect(request.url).to.equal('https://bid.contextweb.com/header/ortb?src=prebid'); expect(request.method).to.equal('POST'); const ortbRequest = request.data; @@ -383,8 +468,7 @@ describe('PulsePoint Adapter Tests', function () { const bidderRequestUSPrivacy = { uspConsent: '1YYY' }; - const request = spec.buildRequests(slotConfigs, - syncAddFPDToBidderRequest(Object.assign({}, bidderRequest, bidderRequestUSPrivacy))); + const request = spec.buildRequests(slotConfigs, Object.assign({}, bidderRequest, bidderRequestUSPrivacy)); expect(request.url).to.equal('https://bid.contextweb.com/header/ortb?src=prebid'); expect(request.method).to.equal('POST'); const ortbRequest = request.data; @@ -394,54 +478,52 @@ describe('PulsePoint Adapter Tests', function () { expect(ortbRequest.regs.ext.us_privacy).to.equal('1YYY'); }); - if (FEATURES.VIDEO) { - it('Verify Video request', function () { - const request = spec.buildRequests(videoSlotConfig, syncAddFPDToBidderRequest(bidderRequest)); - expect(request.url).to.equal('https://bid.contextweb.com/header/ortb?src=prebid'); - expect(request.method).to.equal('POST'); - const ortbRequest = request.data; - expect(ortbRequest).to.not.equal(null); - expect(ortbRequest.imp).to.have.lengthOf(1); - expect(ortbRequest.imp[0].video).to.not.be.null; - expect(ortbRequest.imp[0].native).to.be.undefined; - expect(ortbRequest.imp[0].banner).to.be.undefined; - expect(ortbRequest.imp[0].video.w).to.equal(400); - expect(ortbRequest.imp[0].video.h).to.equal(300); - expect(ortbRequest.imp[0].video.minduration).to.equal(5); - expect(ortbRequest.imp[0].video.maxduration).to.equal(10); - expect(ortbRequest.imp[0].video.startdelay).to.equal(0); - expect(ortbRequest.imp[0].video.skip).to.equal(1); - expect(ortbRequest.imp[0].video.minbitrate).to.equal(200); - expect(ortbRequest.imp[0].video.protocols).to.eql([1, 2, 4]); - }); + it('Verify Video request', function () { + const request = spec.buildRequests(videoSlotConfig, bidderRequest); + expect(request.url).to.equal('https://bid.contextweb.com/header/ortb?src=prebid'); + expect(request.method).to.equal('POST'); + const ortbRequest = request.data; + expect(ortbRequest).to.not.equal(null); + expect(ortbRequest.imp).to.have.lengthOf(1); + expect(ortbRequest.imp[0].video).to.not.be.null; + expect(ortbRequest.imp[0].native).to.be.null; + expect(ortbRequest.imp[0].banner).to.be.null; + expect(ortbRequest.imp[0].video.w).to.equal(400); + expect(ortbRequest.imp[0].video.h).to.equal(300); + expect(ortbRequest.imp[0].video.minduration).to.equal(5); + expect(ortbRequest.imp[0].video.maxduration).to.equal(10); + expect(ortbRequest.imp[0].video.startdelay).to.equal(0); + expect(ortbRequest.imp[0].video.skip).to.equal(1); + expect(ortbRequest.imp[0].video.minbitrate).to.equal(200); + expect(ortbRequest.imp[0].video.protocols).to.eql([1, 2, 4]); + }); - it('Verify Video response', function () { - const request = spec.buildRequests(videoSlotConfig, bidderRequest); - expect(request.url).to.equal('https://bid.contextweb.com/header/ortb?src=prebid'); - expect(request.method).to.equal('POST'); - const ortbRequest = request.data; - const ortbResponse = { - seatbid: [{ - bid: [{ - impid: ortbRequest.imp[0].id, - price: 1.25, - adm: 'https//pulsepoint.video.mp4', - mtype: 2 - }] + it('Verify Video response', function () { + const request = spec.buildRequests(videoSlotConfig, bidderRequest); + expect(request.url).to.equal('https://bid.contextweb.com/header/ortb?src=prebid'); + expect(request.method).to.equal('POST'); + const ortbRequest = request.data; + const ortbResponse = { + seatbid: [{ + bid: [{ + impid: ortbRequest.imp[0].id, + price: 1.25, + adm: 'https//pulsepoint.video.mp4' }] - }; - const bids = spec.interpretResponse({ body: ortbResponse }, request); - const bid = bids[0]; - expect(bid.cpm).to.equal(1.25); - expect(bid.ad).to.be.undefined; - expect(bid['native']).to.be.undefined; - expect(bid.mediaType).to.equal('video'); - expect(bid.vastXml).to.equal(ortbResponse.seatbid[0].bid[0].adm); - }); - } + }] + }; + const bids = spec.interpretResponse({ body: ortbResponse }, request); + const bid = bids[0]; + expect(bid.cpm).to.equal(1.25); + expect(bid.adId).to.equal('bid12345'); + expect(bid.ad).to.be.undefined; + expect(bid['native']).to.be.undefined; + expect(bid.mediaType).to.equal('video'); + expect(bid.vastXml).to.equal(ortbResponse.seatbid[0].bid[0].adm); + }); it('Verify extra parameters', function () { - let request = spec.buildRequests(additionalParamsConfig, syncAddFPDToBidderRequest(bidderRequest)); + let request = spec.buildRequests(additionalParamsConfig, bidderRequest); let ortbRequest = request.data; expect(ortbRequest).to.not.equal(null); expect(ortbRequest.imp).to.have.lengthOf(1); @@ -456,15 +538,31 @@ describe('PulsePoint Adapter Tests', function () { expect(ortbRequest.imp[0].ext.prebid.extra_key4).to.eql([1, 2, 3]); expect(Object.keys(ortbRequest.imp[0].ext.prebid)).to.eql(['extra_key1', 'extra_key2', 'extra_key3', 'extra_key4']); // attempting with a configuration with no unknown params. - request = spec.buildRequests(videoSlotConfig, bidderRequest); + request = spec.buildRequests(outstreamSlotConfig, bidderRequest); ortbRequest = request.data; expect(ortbRequest).to.not.equal(null); expect(ortbRequest.imp).to.have.lengthOf(1); - expect(ortbRequest.imp[0].ext).to.be.undefined; + expect(ortbRequest.imp[0].ext).to.equal(null); + }); + + it('Verify ortb parameters', function () { + const request = spec.buildRequests(ortbParamsSlotConfig, bidderRequest); + const ortbRequest = request.data; + expect(ortbRequest).to.not.equal(null); + expect(ortbRequest.bcat).to.eql(['IAB-1', 'IAB-20']); + expect(ortbRequest.badv).to.eql(['cocacola.com', 'lays.com']); + expect(ortbRequest.imp).to.have.lengthOf(2); + expect(ortbRequest.imp[0].bidfloor).to.equal(1.5); + expect(ortbRequest.imp[0].banner.battr).to.eql([1, 2, 3]); + expect(ortbRequest.imp[0].ext).to.be.null; + // slot 2 + expect(ortbRequest.imp[1].bidfloor).to.equal(2.5); + expect(ortbRequest.imp[1].video.battr).to.eql([2, 3, 4]); + expect(ortbRequest.imp[1].ext).to.be.null; }); it('Verify schain parameters', function () { - const request = spec.buildRequests(schainParamsSlotConfig, syncAddFPDToBidderRequest(bidderRequest)); + const request = spec.buildRequests(schainParamsSlotConfig, bidderRequest); const ortbRequest = request.data; expect(ortbRequest).to.not.equal(null); expect(ortbRequest.source).to.not.equal(null); @@ -482,6 +580,42 @@ describe('PulsePoint Adapter Tests', function () { expect(ortbRequest.source.ext.schain.nodes[0].domain).to.equal('publisher.com'); }); + it('Verify outstream renderer', function () { + const bidderRequestOutstream = Object.assign({}, bidderRequest, {bids: [outstreamSlotConfig[0]]}); + const request = spec.buildRequests(outstreamSlotConfig, bidderRequestOutstream); + const ortbRequest = request.data; + expect(ortbRequest).to.not.be.null; + expect(ortbRequest.imp[0]).to.not.be.null; + expect(ortbRequest.imp[0].video).to.not.be.null; + const ortbResponse = { + seatbid: [{ + bid: [{ + impid: ortbRequest.imp[0].id, + price: 1.25, + adm: 'https//pulsepoint.video.mp4', + ext: { + outstream: { + type: 'Inline', + config: { + text: 'ADVERTISEMENT', + skipaftersec: 5 + }, + rendererUrl: 'https://tag.contextweb.com/hb-outstr-renderer.js' + } + } + }] + }] + }; + const bids = spec.interpretResponse({ body: ortbResponse }, request); + const bid = bids[0]; + expect(bid.cpm).to.equal(1.25); + expect(bid.renderer).to.not.be.null; + expect(bid.renderer.url).to.equal('https://tag.contextweb.com/hb-outstr-renderer.js'); + expect(bid.renderer.getConfig()).to.not.be.null; + expect(bid.renderer.getConfig().defaultOptions).to.eql(ortbResponse.seatbid[0].bid[0].ext.outstream.config); + expect(bid.renderer.getConfig().rendererOptions).to.eql(outstreamSlotConfig[0].renderer.options); + expect(bid.renderer.getConfig().type).to.equal('Inline'); + }); it('Verify common id parameters', function () { const bidRequests = deepClone(slotConfigs); bidRequests[0].userIdAsEids = [{ @@ -499,17 +633,169 @@ describe('PulsePoint Adapter Tests', function () { }] } ]; - const request = spec.buildRequests(bidRequests, syncAddFPDToBidderRequest(bidderRequest)); + const request = spec.buildRequests(bidRequests, bidderRequest); expect(request).to.be.not.null; - expect(request.data).to.be.not.null; const ortbRequest = request.data; + expect(request.data).to.be.not.null; // user object expect(ortbRequest.user).to.not.be.undefined; expect(ortbRequest.user.ext).to.not.be.undefined; expect(ortbRequest.user.ext.eids).to.not.be.undefined; expect(ortbRequest.user.ext.eids).to.deep.equal(bidRequests[0].userIdAsEids); }); - + it('Verify multiple adsizes', function () { + const bidRequests = deepClone(slotConfigs); + const request = spec.buildRequests(bidRequests, bidderRequest); + expect(request).to.be.not.null; + expect(request.data).to.be.not.null; + const ortbRequest = request.data; + expect(ortbRequest.imp).to.have.lengthOf(2); + // first impression has multi sizes + expect(ortbRequest.imp[0].banner).to.not.be.null; + expect(ortbRequest.imp[0].banner.w).to.equal(300); + expect(ortbRequest.imp[0].banner.h).to.equal(250); + expect(ortbRequest.imp[0].banner.format).to.not.be.null; + expect(ortbRequest.imp[0].banner.format).to.have.lengthOf(2); + expect(ortbRequest.imp[0].banner.format[0].w).to.equal(728); + expect(ortbRequest.imp[0].banner.format[0].h).to.equal(90); + expect(ortbRequest.imp[0].banner.format[1].w).to.equal(160); + expect(ortbRequest.imp[0].banner.format[1].h).to.equal(600); + // slot 2 + expect(ortbRequest.imp[1].banner).to.not.be.null; + expect(ortbRequest.imp[1].banner.w).to.equal(728); + expect(ortbRequest.imp[1].banner.h).to.equal(90); + expect(ortbRequest.imp[1].banner.format).to.not.be.null; + expect(ortbRequest.imp[1].banner.format).to.have.lengthOf(1); + expect(ortbRequest.imp[1].banner.format[0].w).to.equal(728); + expect(ortbRequest.imp[1].banner.format[0].h).to.equal(90); + // adsize on response + const ortbResponse = { + seatbid: [{ + bid: [{ + impid: ortbRequest.imp[0].id, + price: 1.25, + adm: 'This is an Ad', + crid: 'Creative#123', + w: 728, + h: 90 + }] + }] + }; + const bids = spec.interpretResponse({ body: ortbResponse }, request); + expect(bids).to.have.lengthOf(1); + const bid = bids[0]; + expect(bid.width).to.equal(728); + expect(bid.height).to.equal(90); + }); + it('Verify multi-format response', function () { + const bidRequests = deepClone(slotConfigs); + bidRequests[0].mediaTypes['native'] = { + title: { + required: true + }, + image: { + required: true + }, + sponsoredBy: { + required: true + } + }; + bidRequests[1].params.video = { + w: 400, + h: 300, + minduration: 5, + maxduration: 10, + }; + const request = spec.buildRequests(bidRequests, bidderRequest); + expect(request).to.be.not.null; + expect(request.data).to.be.not.null; + const ortbRequest = request.data; + expect(ortbRequest.imp).to.have.lengthOf(2); + // adsize on response + const ortbResponse = { + seatbid: [{ + bid: [{ + impid: ortbRequest.imp[0].id, + price: 1.25, + adm: 'This is an Ad', + crid: 'Creative#123', + w: 728, + h: 90 + }, { + impid: ortbRequest.imp[1].id, + price: 2.5, + adm: '', + crid: 'Creative#234', + w: 728, + h: 90 + }] + }] + }; + // request has both types - banner and native, response is parsed as banner. + // for impression#2, response is parsed as video + const bids = spec.interpretResponse({ body: ortbResponse }, request); + expect(bids).to.have.lengthOf(2); + const bid = bids[0]; + expect(bid.width).to.equal(728); + expect(bid.height).to.equal(90); + const secondBid = bids[1]; + expect(secondBid.vastXml).to.equal(''); + }); + it('Verify bid floor', function () { + const bidRequests = deepClone(slotConfigs); + bidRequests[0].params.bidfloor = 1.05; + let request = spec.buildRequests(bidRequests, bidderRequest); + let ortbRequest = request.data; + expect(ortbRequest).to.not.equal(null); + expect(ortbRequest.imp[0].bidfloor).to.equal(1.05); + expect(ortbRequest.imp[1].bidfloor).to.be.undefined; + let floorArg = null; + // publisher uses the floor module + bidRequests[0].getFloor = (arg) => { + floorArg = arg; + return { floor: 1.25 }; + }; + bidRequests[1].getFloor = () => { + return { floor: 2.05 }; + }; + request = spec.buildRequests(bidRequests, bidderRequest); + ortbRequest = request.data; + expect(ortbRequest).to.not.equal(null); + expect(ortbRequest.imp[0].bidfloor).to.equal(1.25); + expect(ortbRequest.imp[1].bidfloor).to.equal(2.05); + expect(floorArg).to.not.be.null; + expect(floorArg.mediaType).to.equal('banner'); + expect(floorArg.currency).to.equal('USD'); + expect(floorArg.size).to.equal('*'); + }); + it('Verify Video params on mediaTypes.video', function () { + const bidRequests = deepClone(videoSlotConfig); + bidRequests[0].mediaTypes = { + video: { + w: 600, + h: 400, + minduration: 15, + maxduration: 20, + startdelay: 10, + skip: 0, + } + }; + const request = spec.buildRequests(bidRequests, bidderRequest); + const ortbRequest = request.data; + expect(ortbRequest).to.not.equal(null); + expect(ortbRequest.imp).to.have.lengthOf(1); + expect(ortbRequest.imp[0].video).to.not.be.null; + expect(ortbRequest.imp[0].native).to.be.null; + expect(ortbRequest.imp[0].banner).to.be.null; + expect(ortbRequest.imp[0].video.w).to.equal(600); + expect(ortbRequest.imp[0].video.h).to.equal(400); + expect(ortbRequest.imp[0].video.minduration).to.equal(15); + expect(ortbRequest.imp[0].video.maxduration).to.equal(20); + expect(ortbRequest.imp[0].video.startdelay).to.equal(10); + expect(ortbRequest.imp[0].video.skip).to.equal(0); + expect(ortbRequest.imp[0].video.minbitrate).to.equal(200); + expect(ortbRequest.imp[0].video.protocols).to.eql([1, 2, 4]); + }); it('Verify user level first party data', function () { const bidderRequest = { refererInfo: { @@ -533,7 +819,7 @@ describe('PulsePoint Adapter Tests', function () { } } }; - let request = spec.buildRequests(slotConfigs, syncAddFPDToBidderRequest(bidderRequest)); + let request = spec.buildRequests(slotConfigs, bidderRequest); let ortbRequest = request.data; expect(ortbRequest).to.not.equal(null); expect(ortbRequest.user).to.not.equal(null); @@ -549,9 +835,12 @@ describe('PulsePoint Adapter Tests', function () { } }); }); - it('Verify site level first party data', function () { const bidderRequest = { + refererInfo: { + page: 'https://publisher.com/home', + ref: 'https://referrer' + }, ortb2: { site: { content: { @@ -564,14 +853,11 @@ describe('PulsePoint Adapter Tests', function () { }] }, page: 'http://pub.com/news', - ref: 'http://google.com', - publisher: { - domain: 'pub.com' - } + ref: 'http://google.com' } } }; - let request = spec.buildRequests(slotConfigs, syncAddFPDToBidderRequest(bidderRequest)); + let request = spec.buildRequests(slotConfigs, bidderRequest); let ortbRequest = request.data; expect(ortbRequest).to.not.equal(null); expect(ortbRequest.site).to.not.equal(null); @@ -585,15 +871,13 @@ describe('PulsePoint Adapter Tests', function () { } }] }, - page: 'http://pub.com/news', - ref: 'http://google.com', + page: 'https://publisher.com/home', + ref: 'https://referrer', publisher: { - id: 'p10000', - domain: 'pub.com' + id: 'p10000' } }); }); - it('Verify impression/slot level first party data', function () { const bidderRequests = [{ placementCode: '/DfpAccount1/slot1', @@ -644,27 +928,4 @@ describe('PulsePoint Adapter Tests', function () { // assert bidderRequest value is used when available expect(mkRequest(Object.assign({}, { timeout: 6000 }, bidderRequest)).tmax).to.equal(6000) }); - - it('Verify deals', function () { - const bidRequests = deepClone(slotConfigs); - const deals = [{ - id: 'DEAL_ONE', - bidfloor: 1.1 - }, { - id: 'DEAL_TWO', - bidfloor: 2.2 - }]; - bidRequests[0].params.deals = deals; - const request = spec.buildRequests(bidRequests, syncAddFPDToBidderRequest(bidderRequest)); - expect(request.url).to.equal('https://bid.contextweb.com/header/ortb?src=prebid'); - expect(request.method).to.equal('POST'); - const ortbRequest = request.data; - // slot 1 - expect(ortbRequest.imp[0].tagid).to.equal('t10000'); - expect(ortbRequest.imp[0].pmp).to.not.be.undefined; - expect(ortbRequest.imp[0].pmp).to.deep.equal({ - private_auction: 0, - deals - }); - }) }); diff --git a/test/spec/modules/qortexRtdProvider_spec.js b/test/spec/modules/qortexRtdProvider_spec.js deleted file mode 100644 index 9baa526e4cc..00000000000 --- a/test/spec/modules/qortexRtdProvider_spec.js +++ /dev/null @@ -1,333 +0,0 @@ -import * as utils from 'src/utils'; -import * as ajax from 'src/ajax.js'; -import * as events from 'src/events.js'; -import CONSTANTS from '../../../src/constants.json'; -import {loadExternalScript} from 'src/adloader.js'; -import { - qortexSubmodule as module, - getContext, - addContextToRequests, - setContextData, - initializeModuleData, - loadScriptTag -} from '../../../modules/qortexRtdProvider'; -import {server} from '../../mocks/xhr.js'; -import { cloneDeep } from 'lodash'; - -describe('qortexRtdProvider', () => { - let logWarnSpy; - let ortb2Stub; - - const defaultApiHost = 'https://demand.qortex.ai'; - const defaultGroupId = 'test'; - - const validBidderArray = ['qortex', 'test']; - const validTagConfig = { - videoContainer: 'my-video-container' - } - - const validModuleConfig = { - params: { - groupId: defaultGroupId, - apiUrl: defaultApiHost, - bidders: validBidderArray - } - }, - emptyModuleConfig = { - params: {} - } - - const validImpressionEvent = { - detail: { - uid: 'uid123', - type: 'qx-impression' - } - }, - validImpressionEvent2 = { - detail: { - uid: 'uid1234', - type: 'qx-impression' - } - }, - missingIdImpressionEvent = { - detail: { - type: 'qx-impression' - } - }, - invalidTypeQortexEvent = { - detail: { - type: 'invalid-type' - } - } - - const responseHeaders = { - 'content-type': 'application/json', - 'access-control-allow-origin': '*' - }; - - const responseObj = { - content: { - id: '123456', - episode: 15, - title: 'test episode', - series: 'test show', - season: '1', - url: 'https://example.com/file.mp4' - } - }; - - const apiResponse = JSON.stringify(responseObj); - - const reqBidsConfig = { - adUnits: [{ - bids: [ - { bidder: 'qortex' } - ] - }], - ortb2Fragments: { - bidder: {}, - global: {} - } - } - - beforeEach(() => { - ortb2Stub = sinon.stub(reqBidsConfig, 'ortb2Fragments').value({bidder: {}, global: {}}) - logWarnSpy = sinon.spy(utils, 'logWarn'); - }) - - afterEach(() => { - logWarnSpy.restore(); - ortb2Stub.restore(); - setContextData(null); - }) - - describe('init', () => { - it('returns true for valid config object', () => { - expect(module.init(validModuleConfig)).to.be.true; - }) - - it('returns false and logs error for missing groupId', () => { - expect(module.init(emptyModuleConfig)).to.be.false; - expect(logWarnSpy.calledOnce).to.be.true; - expect(logWarnSpy.calledWith('Qortex RTD module config does not contain valid groupId parameter. Config params: {}')).to.be.ok; - }) - - it('loads Qortex script if tagConfig is present in module config params', () => { - const config = cloneDeep(validModuleConfig); - config.params.tagConfig = validTagConfig; - expect(module.init(config)).to.be.true; - expect(loadExternalScript.calledOnce).to.be.true; - }) - }) - - describe('loadScriptTag', () => { - let addEventListenerSpy; - let billableEvents = []; - - let config = cloneDeep(validModuleConfig); - config.params.tagConfig = validTagConfig; - - events.on(CONSTANTS.EVENTS.BILLABLE_EVENT, (e) => { - billableEvents.push(e); - }) - - beforeEach(() => { - initializeModuleData(config); - addEventListenerSpy = sinon.spy(window, 'addEventListener'); - }) - - afterEach(() => { - addEventListenerSpy.restore(); - billableEvents = []; - }) - - it('adds event listener', () => { - loadScriptTag(config); - expect(addEventListenerSpy.calledOnce).to.be.true; - }) - - it('parses incoming qortex-impression events', () => { - loadScriptTag(config); - dispatchEvent(new CustomEvent('qortex-rtd', validImpressionEvent)); - expect(billableEvents.length).to.be.equal(1); - expect(billableEvents[0].type).to.be.equal(validImpressionEvent.detail.type); - expect(billableEvents[0].transactionId).to.be.equal(validImpressionEvent.detail.uid); - }) - - it('will emit two events for impressions with two different ids', () => { - loadScriptTag(config); - dispatchEvent(new CustomEvent('qortex-rtd', validImpressionEvent)); - dispatchEvent(new CustomEvent('qortex-rtd', validImpressionEvent2)); - expect(billableEvents.length).to.be.equal(2); - expect(billableEvents[0].transactionId).to.be.equal(validImpressionEvent.detail.uid); - expect(billableEvents[1].transactionId).to.be.equal(validImpressionEvent2.detail.uid); - }) - - it('will not allow multiple events with the same id', () => { - loadScriptTag(config); - dispatchEvent(new CustomEvent('qortex-rtd', validImpressionEvent)); - dispatchEvent(new CustomEvent('qortex-rtd', validImpressionEvent)); - expect(billableEvents.length).to.be.equal(1); - expect(logWarnSpy.calledWith('received invalid billable event due to duplicate uid: qx-impression')).to.be.ok; - }) - - it('will not allow events with missing uid', () => { - loadScriptTag(config); - dispatchEvent(new CustomEvent('qortex-rtd', missingIdImpressionEvent)); - expect(billableEvents.length).to.be.equal(0); - expect(logWarnSpy.calledWith('received invalid billable event due to missing uid: qx-impression')).to.be.ok; - }) - - it('will not allow events with unavailable type', () => { - loadScriptTag(config); - dispatchEvent(new CustomEvent('qortex-rtd', invalidTypeQortexEvent)); - expect(billableEvents.length).to.be.equal(0); - expect(logWarnSpy.calledWith('received invalid billable event: invalid-type')).to.be.ok; - }) - }) - - describe('getBidRequestData', () => { - let callbackSpy; - - beforeEach(() => { - initializeModuleData(validModuleConfig); - callbackSpy = sinon.spy(); - }) - - afterEach(() => { - initializeModuleData(emptyModuleConfig); - callbackSpy.resetHistory(); - }) - - it('will call callback immediately if no adunits', () => { - const reqBidsConfigNoBids = { adUnits: [] }; - module.getBidRequestData(reqBidsConfigNoBids, callbackSpy); - expect(callbackSpy.calledOnce).to.be.true; - expect(logWarnSpy.calledWith('No adunits found on request bids configuration: ' + JSON.stringify(reqBidsConfigNoBids))).to.be.ok; - }) - - it('will call callback if getContext does not throw', () => { - const cb = function () { - expect(logWarnSpy.calledOnce).to.be.false; - done(); - } - module.getBidRequestData(reqBidsConfig, cb); - server.requests[0].respond(200, responseHeaders, apiResponse); - }) - - it('will catch and log error and fire callback', (done) => { - const a = sinon.stub(ajax, 'ajax').throws(new Error('test')); - const cb = function () { - expect(logWarnSpy.calledWith('test')).to.be.eql(true); - done(); - } - module.getBidRequestData(reqBidsConfig, cb); - a.restore(); - }) - }) - - describe('getContext', () => { - beforeEach(() => { - initializeModuleData(validModuleConfig); - }) - - afterEach(() => { - initializeModuleData(emptyModuleConfig); - }) - - it('returns a promise', (done) => { - const result = getContext(); - expect(result).to.be.a('promise'); - done(); - }) - - it('uses request url generated from initialize function in config and resolves to content object data', (done) => { - let requestUrl = `${validModuleConfig.params.apiUrl}/api/v1/analyze/${validModuleConfig.params.groupId}/prebid`; - const ctx = getContext() - expect(server.requests.length).to.be.eql(1); - expect(server.requests[0].url).to.be.eql(requestUrl); - server.requests[0].respond(200, responseHeaders, apiResponse); - ctx.then(response => { - expect(response).to.be.eql(responseObj.content); - done(); - }); - }) - - it('will return existing context data instead of ajax call if the source was not updated', (done) => { - setContextData(responseObj.content); - const ctx = getContext(); - expect(server.requests.length).to.be.eql(0); - ctx.then(response => { - expect(response).to.be.eql(responseObj.content); - done(); - }); - }) - - it('returns null for non erroring api responses other than 200', (done) => { - const nullContentResponse = { content: null } - const ctx = getContext() - server.requests[0].respond(200, responseHeaders, JSON.stringify(nullContentResponse)) - ctx.then(response => { - expect(response).to.be.null; - expect(server.requests.length).to.be.eql(1); - expect(logWarnSpy.called).to.be.false; - done(); - }); - }) - }) - - describe(' addContextToRequests', () => { - it('logs error if no data was retrieved from get context call', () => { - initializeModuleData(validModuleConfig); - addContextToRequests(reqBidsConfig); - expect(logWarnSpy.calledOnce).to.be.true; - expect(logWarnSpy.calledWith('No context data received at this time')).to.be.ok; - expect(reqBidsConfig.ortb2Fragments.global).to.be.eql({}); - expect(reqBidsConfig.ortb2Fragments.bidder).to.be.eql({}); - }) - - it('adds site.content only to global ortb2 when bidders array is omitted', () => { - const omittedBidderArrayConfig = cloneDeep(validModuleConfig); - delete omittedBidderArrayConfig.params.bidders; - initializeModuleData(omittedBidderArrayConfig); - setContextData(responseObj.content); - addContextToRequests(reqBidsConfig); - expect(reqBidsConfig.ortb2Fragments.global).to.have.property('site'); - expect(reqBidsConfig.ortb2Fragments.global.site).to.have.property('content'); - expect(reqBidsConfig.ortb2Fragments.global.site.content).to.be.eql(responseObj.content); - expect(reqBidsConfig.ortb2Fragments.bidder).to.be.eql({}); - }) - - it('adds site.content only to bidder ortb2 when bidders array is included', () => { - initializeModuleData(validModuleConfig); - setContextData(responseObj.content); - addContextToRequests(reqBidsConfig); - - const qortexOrtb2Fragment = reqBidsConfig.ortb2Fragments.bidder['qortex'] - expect(qortexOrtb2Fragment).to.not.be.null; - expect(qortexOrtb2Fragment).to.have.property('site'); - expect(qortexOrtb2Fragment.site).to.have.property('content'); - expect(qortexOrtb2Fragment.site.content).to.be.eql(responseObj.content); - - const testOrtb2Fragment = reqBidsConfig.ortb2Fragments.bidder['test'] - expect(testOrtb2Fragment).to.not.be.null; - expect(testOrtb2Fragment).to.have.property('site'); - expect(testOrtb2Fragment.site).to.have.property('content'); - expect(testOrtb2Fragment.site.content).to.be.eql(responseObj.content); - - expect(reqBidsConfig.ortb2Fragments.global).to.be.eql({}); - }) - - it('logs error if there is an empty bidder array', () => { - const invalidBidderArrayConfig = cloneDeep(validModuleConfig); - invalidBidderArrayConfig.params.bidders = []; - initializeModuleData(invalidBidderArrayConfig); - setContextData(responseObj.content) - addContextToRequests(reqBidsConfig); - - expect(logWarnSpy.calledWith('Config contains an empty bidders array, unable to determine which bids to enrich')).to.be.ok; - expect(reqBidsConfig.ortb2Fragments.global).to.be.eql({}); - expect(reqBidsConfig.ortb2Fragments.bidder).to.be.eql({}); - }) - }) -}) diff --git a/test/spec/modules/r2b2BidAdapter_spec.js b/test/spec/modules/r2b2BidAdapter_spec.js deleted file mode 100644 index b94b400a71d..00000000000 --- a/test/spec/modules/r2b2BidAdapter_spec.js +++ /dev/null @@ -1,689 +0,0 @@ -import {expect} from 'chai'; -import {spec, internal as r2b2, internal} from 'modules/r2b2BidAdapter.js'; -import * as utils from '../../../src/utils'; -import 'modules/schain.js'; -import 'modules/userId/index.js'; - -function encodePlacementIds (ids) { - return btoa(JSON.stringify(ids)); -} - -describe('R2B2 adapter', function () { - let serverResponse, requestForInterpretResponse; - let bidderRequest; - let bids = []; - let gdprConsent = { - gdprApplies: true, - consentString: 'consent-string', - }; - let schain = { - ver: '1.0', - complete: 1, - nodes: [{ - asi: 'example.com', - sid: '00001', - hp: 1 - }] - }; - const usPrivacyString = '1YNN'; - const impId = 'impID'; - const price = 10.6; - const ad = 'adm'; - const creativeId = 'creativeID'; - const cid = 41849; - const cdid = 595121; - const unitCode = 'unitCode'; - const bidId1 = '1'; - const bidId2 = '2'; - const bidId3 = '3'; - const bidId4 = '4'; - const bidId5 = '5'; - const bidWonUrl = 'url1'; - const setTargetingUrl = 'url2'; - const bidder = 'r2b2'; - const foreignBidder = 'differentBidder'; - const id1 = { pid: 'd/g/p' }; - const id1Object = { d: 'd', g: 'g', p: 'p', m: 0 }; - 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 fakeTime = 1234567890; - const cacheBusterRegex = /[\?&]cb=([^&]+)/; - let bidStub, time; - - beforeEach(function () { - bids = [{ - bidder: 'r2b2', - params: { - pid: 'example.com/generic/300x250/1' - }, - mediaTypes: { - banner: { - sizes: [ - [300, 250] - ] - } - }, - adUnitCode: unitCode, - transactionId: '29c408b9-65ce-48b1-9167-18a57791f908', - sizes: [ - [300, 250] - ], - bidId: '20917a54ee9858', - bidderRequestId: '15270d403778d', - auctionId: '36acef1b-f635-4f57-b693-5cc55ee16346', - src: 'client', - ortb2: { - regs: { - ext: { - gdpr: 1, - us_privacy: '1YYY' - } - }, - user: { - ext: { - consent: 'consent-string' - } - }, - site: {}, - device: {} - }, - schain - }, { - bidder: 'r2b2', - params: { - pid: 'example.com/generic/300x600/0' - }, - mediaTypes: { - banner: { - sizes: [ - [300, 600] - ] - } - }, - adUnitCode: unitCode, - transactionId: '29c408b9-65ce-48b1-9167-18a57791f908', - sizes: [ - [300, 600] - ], - bidId: '3dd53d30c691fe', - bidderRequestId: '15270d403778d', - auctionId: '36acef1b-f635-4f57-b693-5cc55ee16346', - src: 'client', - ortb2: { - regs: { - ext: { - gdpr: 1, - us_privacy: '1YYY' - } - }, - user: { - ext: { - consent: 'consent-string' - } - }, - site: {}, - device: {} - }, - schain - }]; - bidderRequest = { - bidderCode: 'r2b2', - auctionId: '36acef1b-f635-4f57-b693-5cc55ee16346', - bidderRequestId: '15270d403778d', - bids: bids, - ortb2: { - regs: { - ext: { - gdpr: 1, - us_privacy: '1YYY' - } - }, - user: { - ext: { - consent: 'consent-string' - } - }, - site: {}, - device: {} - }, - gdprConsent: { - consentString: 'consent-string', - vendorData: {}, - gdprApplies: true, - apiVersion: 2 - }, - uspConsent: '1YYY', - }; - serverResponse = { - id: 'a66a6e32-2a7d-4ed3-bb13-6f3c9bdcf6a1', - seatbid: [{ - bid: [{ - id: '4756cc9e9b504fd0bd39fdd594506545', - impid: impId, - price: price, - adm: ad, - crid: creativeId, - w: 300, - h: 250, - ext: { - prebid: { - meta: { - adaptercode: 'r2b2' - }, - type: 'banner' - }, - r2b2: { - cdid: cdid, - cid: cid, - useRenderer: true - } - } - }], - seat: 'seat' - }] - }; - requestForInterpretResponse = { - data: { - imp: [ - {id: impId} - ] - }, - bids - }; - }); - - describe('isBidRequestValid', function () { - let bid = {}; - - it('should return false when missing required "pid" param', function () { - bid.params = {random: 'param'}; - expect(spec.isBidRequestValid(bid)).to.equal(false); - 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'}; - expect(spec.isBidRequestValid(bid)).to.equal(false); - bid.params = {pid: '///'}; - expect(spec.isBidRequestValid(bid)).to.equal(false); - bid.params = {pid: '/g/p/m'}; - expect(spec.isBidRequestValid(bid)).to.equal(false); - bid.params = {pid: 'd//p/m'}; - expect(spec.isBidRequestValid(bid)).to.equal(false); - bid.params = {pid: 'd/g//m'}; - expect(spec.isBidRequestValid(bid)).to.equal(false); - bid.params = {pid: 'd/p/'}; - expect(spec.isBidRequestValid(bid)).to.equal(false); - 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'}; - expect(spec.isBidRequestValid(bid)).to.equal(true); - }); - it('should return true when type is blank', function () { - 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'}; - expect(spec.isBidRequestValid(bid)).to.equal(true); - }); - it('should return true when "pid" is a number', function () { - 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'}; - expect(spec.isBidRequestValid(bid)).to.equal(true); - }); - it('should return true for selfpromo unit', function () { - bid.params = {pid: 'selfpromo'}; - expect(spec.isBidRequestValid(bid)).to.equal(true) - }); - }); - - describe('buildRequests', function () { - beforeEach(function () { - r2b2.placementsToSync = []; - r2b2.mappedParams = {}; - }); - - it('should set correct request method and url and pass bids', function () { - let requests = spec.buildRequests([bids[0]], bidderRequest); - expect(requests).to.be.an('array').that.has.lengthOf(1); - let request = requests[0] - expect(request.method).to.equal('POST'); - expect(request.url).to.equal('https://hb.r2b2.cz/openrtb2/bid'); - expect(request.data).to.be.an('object'); - expect(request.bids).to.deep.equal(bids); - }); - - it('should pass correct parameters', function () { - let requests = spec.buildRequests([bids[0]], bidderRequest); - let {data} = requests[0]; - let {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'); - expect(source).to.be.an('object'); - expect(cur).to.deep.equal(['USD']); - expect(ext.version).to.equal('1.0.0'); - expect(test).to.equal(0); - }); - - it('should pass correct imp', function () { - let requests = spec.buildRequests([bids[0]], bidderRequest); - let {data} = requests[0]; - let {imp} = data; - expect(imp).to.be.an('array').that.has.lengthOf(1); - expect(imp[0]).to.be.an('object'); - let bid = imp[0]; - expect(bid.id).to.equal('20917a54ee9858'); - 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}); - }); - - it('should map type correctly', function () { - let result, bid; - let requestWithId = function(id) { - let b = bids[0]; - b.params.pid = id; - let passedBids = [b]; - bidderRequest.bids = passedBids; - return spec.buildRequests(passedBids, bidderRequest); - }; - - result = requestWithId('example.com/generic/300x250/mobile'); - bid = result[0].data.imp[0]; - expect(bid.ext.r2b2.m).to.be.a('number').that.is.equal(1); - - result = requestWithId('example.com/generic/300x250/desktop'); - bid = result[0].data.imp[0]; - expect(bid.ext.r2b2.m).to.be.a('number').that.is.equal(0); - - result = requestWithId('example.com/generic/300x250/1'); - bid = result[0].data.imp[0]; - expect(bid.ext.r2b2.m).to.be.a('number').that.is.equal(1); - - result = requestWithId('example.com/generic/300x250/0'); - bid = result[0].data.imp[0]; - expect(bid.ext.r2b2.m).to.be.a('number').that.is.equal(0); - - result = requestWithId('example.com/generic/300x250/m'); - bid = result[0].data.imp[0]; - expect(bid.ext.r2b2.m).to.be.a('number').that.is.equal(1); - - result = requestWithId('example.com/generic/300x250'); - bid = result[0].data.imp[0]; - expect(bid.ext.r2b2.m).to.be.a('number').that.is.equal(0); - }); - - it('should pass correct parameters for test ad', function () { - let testAdBid = bids[0]; - testAdBid.params = {pid: 'selfpromo'}; - let requests = spec.buildRequests([testAdBid], bidderRequest); - let {data} = requests[0]; - let {imp} = data; - expect(imp).to.be.an('array').that.has.lengthOf(1); - expect(imp[0]).to.be.an('object'); - let 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}); - }); - - it('should pass multiple bids', function () { - let requests = spec.buildRequests(bids, bidderRequest); - expect(requests).to.be.an('array').that.has.lengthOf(1); - let {data} = requests[0]; - let {imp} = data; - expect(imp).to.be.an('array').that.has.lengthOf(bids.length); - let bid1 = imp[0]; - expect(bid1.ext.r2b2).to.deep.equal({d: 'example.com', g: 'generic', p: '300x250', m: 1}); - let bid2 = imp[1]; - expect(bid2.ext.r2b2).to.deep.equal({d: 'example.com', g: 'generic', p: '300x600', m: 0}); - }); - - it('should set up internal variables', function () { - let requests = spec.buildRequests(bids, bidderRequest); - let bid1Id = bids[0].bidId; - let 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).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'}); - }); - - it('should pass gdpr properties', function () { - let requests = spec.buildRequests(bids, bidderRequest); - let {data} = requests[0]; - let {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'); - expect(regs.ext.gdpr).to.equal(1); - }); - - it('should pass us privacy properties', function () { - let requests = spec.buildRequests(bids, bidderRequest); - let {data} = requests[0]; - let {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 () { - let requests = spec.buildRequests(bids, bidderRequest); - let {data} = requests[0]; - let {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'} - ], - ver: '1.0' - }) - }); - - it('should pass extended ids', function () { - let eidsArray = [ - { - source: 'adserver.org', - uids: [ - { - atype: 1, - ext: { - rtiPartner: 'TDID', - }, - id: 'TTD_ID_FROM_USER_ID_MODULE', - }, - ], - }, - { - source: 'pubcid.org', - uids: [ - { - atype: 1, - id: 'pubCommonId_FROM_USER_ID_MODULE', - }, - ], - }, - ]; - bids[0].userIdAsEids = eidsArray; - let requests = spec.buildRequests(bids, bidderRequest); - let request = requests[0]; - let eids = request.data.user.ext.eids; - - expect(eids).to.deep.equal(eidsArray); - }); - }); - - describe('interpretResponse', function () { - it('should respond with empty response when there are no bids', function () { - let result = spec.interpretResponse({ body: {} }, {}); - 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: [ {} ] } }, {}); - expect(result).to.be.an('array').that.has.lengthOf(0); - result = spec.interpretResponse({ body: { seatbid: [ { bids: [] } ] } }, {}); - expect(result).to.be.an('array').that.has.lengthOf(0); - }); - - it('should map params correctly', function () { - let result = spec.interpretResponse({ body: serverResponse }, requestForInterpretResponse); - expect(result).to.be.an('array').that.has.lengthOf(1); - let bid = result[0]; - expect(bid.requestId).to.equal(impId); - expect(bid.cpm).to.equal(price); - expect(bid.ad).to.equal(ad); - expect(bid.currency).to.equal('USD'); - expect(bid.mediaType).to.equal('banner'); - expect(bid.width).to.equal(300); - expect(bid.height).to.equal(250); - expect(bid.netRevenue).to.equal(true); - expect(bid.ttl).to.equal(360); - expect(bid.creativeId).to.equal(creativeId); - }); - - it('should set up renderer on bid', function () { - let result = spec.interpretResponse({ body: serverResponse }, requestForInterpretResponse); - expect(result).to.be.an('array').that.has.lengthOf(1); - let bid = result[0]; - expect(bid.renderer).to.be.an('object'); - expect(bid.renderer).to.have.property('render').that.is.a('function'); - expect(bid.renderer).to.have.property('url').that.is.a('string'); - }); - - it('should map ext params correctly', function() { - let dgpm = {something: 'something'}; - r2b2.mappedParams = {}; - r2b2.mappedParams[impId] = dgpm; - let result = spec.interpretResponse({ body: serverResponse }, requestForInterpretResponse); - expect(result).to.be.an('array').that.has.lengthOf(1); - let bid = result[0]; - expect(bid.ext).to.be.an('object'); - let { ext } = bid; - expect(ext.dgpm).to.deep.equal(dgpm); - expect(ext.cid).to.equal(cid); - expect(ext.cdid).to.equal(cdid); - expect(ext.adUnit).to.equal(unitCode); - expect(ext.mediaType).to.deep.equal({ - type: 'banner', - settings: { - chd: null, - width: 300, - height: 250, - ad: { - type: 'content', - data: ad - } - } - }); - }); - - it('should handle multiple bids', function() { - const impId2 = '123456'; - const price2 = 12; - const ad2 = 'gaeouho'; - const w2 = 300; - const h2 = 600; - let b = serverResponse.seatbid[0].bid[0]; - let b2 = Object.assign({}, b); - b2.impid = impId2; - b2.price = price2; - b2.adm = ad2; - b2.w = w2; - b2.h = h2; - serverResponse.seatbid[0].bid.push(b2); - requestForInterpretResponse.data.imp.push({id: impId2}); - let result = spec.interpretResponse({ body: serverResponse }, requestForInterpretResponse); - expect(result).to.be.an('array').that.has.lengthOf(2); - let firstBid = result[0]; - let secondBid = result[1]; - expect(firstBid.requestId).to.equal(impId); - expect(firstBid.ad).to.equal(ad); - expect(firstBid.cpm).to.equal(price); - expect(firstBid.width).to.equal(300); - expect(firstBid.height).to.equal(250); - expect(secondBid.requestId).to.equal(impId2); - expect(secondBid.ad).to.equal(ad2); - expect(secondBid.cpm).to.equal(price2); - expect(secondBid.width).to.equal(w2); - expect(secondBid.height).to.equal(h2); - }); - }); - - describe('getUserSyncs', function() { - const syncOptions = { - iframeEnabled: true, - pixelEnabled: true - }; - - it('should return an array with a sync for all bids', function() { - r2b2.placementsToSync = [id1Object, id2Object]; - const expectedEncodedIds = encodePlacementIds(r2b2.placementsToSync); - const syncs = spec.getUserSyncs(syncOptions); - expect(syncs).to.be.an('array').that.has.lengthOf(1); - const sync = syncs[0]; - expect(sync).to.be.an('object'); - expect(sync.type).to.equal('iframe'); - expect(sync.url).to.include(`?p=${expectedEncodedIds}`); - }); - - it('should return the sync and include gdpr and usp parameters in the url', function() { - r2b2.placementsToSync = [id1Object, id2Object]; - const syncs = spec.getUserSyncs(syncOptions, {}, gdprConsent, usPrivacyString); - const sync = syncs[0]; - expect(sync).to.be.an('object'); - expect(sync.url).to.include(`&gdpr=1`); - expect(sync.url).to.include(`&gdpr_consent=${gdprConsent.consentString}`); - expect(sync.url).to.include(`&us_privacy=${usPrivacyString}`); - }); - }); - - describe('events', function() { - beforeEach(function() { - time = sinon.useFakeTimers(fakeTime); - sinon.stub(utils, 'triggerPixel'); - r2b2.mappedParams = {}; - r2b2.mappedParams[bidId1] = id1Object; - r2b2.mappedParams[bidId2] = id2Object; - bidStub = { - adserverTargeting: { hb_bidder: bidder, hb_pb: '10.00', hb_size: '300x300' }, - cpm: 10, - currency: 'USD', - ext: { - dgpm: { d: 'r2b2.cz', g: 'generic', m: 1, p: '300x300', pid: 'r2b2.cz/generic/300x300/1' } - }, - params: [ { pid: 'r2b2.cz/generic/300x300/1' } ], - }; - }); - afterEach(function() { - utils.triggerPixel.restore(); - time.restore(); - }); - - describe('onBidWon', function () { - it('exists and is a function', () => { - expect(spec.onBidWon).to.exist.and.to.be.a('function'); - }); - it('should return nothing and trigger a pixel with passed url', function () { - bidStub.ext.events = { - onBidWon: bidWonUrl, - onSetTargeting: setTargetingUrl - }; - const response = spec.onBidWon(bidStub); - expect(response).to.be.an('undefined'); - expect(utils.triggerPixel.called).to.equal(true); - expect(utils.triggerPixel.callCount).to.equal(1); - expect(utils.triggerPixel.calledWithMatch(bidWonUrl)).to.equal(true); - }); - it('should not trigger a pixel if url is not available', function () { - bidStub.ext.events = null; - spec.onBidWon(bidStub); - expect(utils.triggerPixel.callCount).to.equal(0); - bidStub.ext.events = { - onBidWon: '', - onSetTargeting: '', - }; - spec.onBidWon(bidStub); - expect(utils.triggerPixel.callCount).to.equal(0); - }); - }); - - describe('onSetTargeting', function () { - it('exists and is a function', () => { - expect(spec.onSetTargeting).to.exist.and.to.be.a('function'); - }); - it('should return nothing and trigger a pixel with passed url', function () { - bidStub.ext.events = { - onBidWon: bidWonUrl, - onSetTargeting: setTargetingUrl - }; - const response = spec.onSetTargeting(bidStub); - expect(response).to.be.an('undefined'); - expect(utils.triggerPixel.called).to.equal(true); - expect(utils.triggerPixel.callCount).to.equal(1); - expect(utils.triggerPixel.calledWithMatch(setTargetingUrl)).to.equal(true); - }); - it('should not trigger a pixel if url is not available', function () { - bidStub.ext.events = null; - spec.onSetTargeting(bidStub); - expect(utils.triggerPixel.callCount).to.equal(0); - bidStub.ext.events = { - onBidWon: '', - onSetTargeting: '', - }; - spec.onSetTargeting(bidStub); - expect(utils.triggerPixel.callCount).to.equal(0); - }); - }); - - describe('onTimeout', function () { - it('exists and is a function', () => { - expect(spec.onTimeout).to.exist.and.to.be.a('function'); - }); - it('should return nothing and trigger a pixel', function () { - const bids = [bid1, bid2]; - const response = spec.onTimeout(bids); - expect(response).to.be.an('undefined'); - expect(utils.triggerPixel.callCount).to.equal(1); - }); - it('should not trigger a pixel if no bids available', function () { - const bids = []; - spec.onTimeout(bids); - expect(utils.triggerPixel.callCount).to.equal(0); - }); - it('should trigger a pixel with correct ids and a cache buster', function () { - const bids = [bid1, bidForeign1, bidForeign2, bid2, bidWithBadSetup]; - const expectedIds = [id1Object, id2Object]; - const expectedEncodedIds = encodePlacementIds(expectedIds); - spec.onTimeout(bids); - expect(utils.triggerPixel.callCount).to.equal(1); - const triggeredUrl = utils.triggerPixel.args[0][0]; - expect(triggeredUrl).to.include(`p=${expectedEncodedIds}`); - expect(triggeredUrl.match(cacheBusterRegex)).to.exist; - }); - }); - - describe('onBidderError', function () { - it('exists and is a function', () => { - expect(spec.onBidderError).to.exist.and.to.be.a('function'); - }); - it('should return nothing and trigger a pixel', function () { - const bidderRequest = { bids: [bid1, bid2] }; - const response = spec.onBidderError({ bidderRequest }); - expect(response).to.be.an('undefined') - expect(utils.triggerPixel.callCount).to.equal(1); - }); - it('should not trigger a pixel if no bids available', function () { - const bidderRequest = { bids: [] }; - spec.onBidderError({ bidderRequest }); - expect(utils.triggerPixel.callCount).to.equal(0); - }); - it('should call triggerEvent with correct ids and a cache buster', function () { - const bids = [bid1, bid2, bidWithBadSetup] - const bidderRequest = { bids }; - const expectedIds = [id1Object, id2Object]; - const expectedEncodedIds = encodePlacementIds(expectedIds); - spec.onBidderError({ bidderRequest }); - expect(utils.triggerPixel.callCount).to.equal(1); - const triggeredUrl = utils.triggerPixel.args[0][0]; - expect(triggeredUrl).to.include(`p=${expectedEncodedIds}`); - expect(triggeredUrl.match(cacheBusterRegex)).to.exist; - }); - }); - }); -}); diff --git a/test/spec/modules/rasBidAdapter_spec.js b/test/spec/modules/rasBidAdapter_spec.js index 719e15ad695..bfa72a2510e 100644 --- a/test/spec/modules/rasBidAdapter_spec.js +++ b/test/spec/modules/rasBidAdapter_spec.js @@ -1,7 +1,6 @@ import { expect } from 'chai'; import { spec } from 'modules/rasBidAdapter.js'; import { newBidder } from 'src/adapters/bidderFactory.js'; -import {getAdUnitSizes} from '../../../src/utils'; const CSR_ENDPOINT = 'https://csr.onet.pl/4178463/csr-006/csr.json?nid=4178463&'; @@ -193,56 +192,5 @@ describe('rasBidAdapter', function () { const resp = spec.interpretResponse({ body: res }, {}); expect(resp).to.deep.equal([]); }); - - it('should generate auctionConfig when fledge is enabled', function () { - let bidRequest = { - method: 'GET', - url: 'https://example.com', - bidIds: [{ - slot: 'top', - bidId: '123', - network: 'testnetwork', - sizes: ['300x250'], - params: { - site: 'testsite', - area: 'testarea', - network: 'testnetwork' - }, - fledgeEnabled: true - }, - { - slot: 'top', - bidId: '456', - network: 'testnetwork', - sizes: ['300x250'], - params: { - site: 'testsite', - area: 'testarea', - network: 'testnetwork' - }, - fledgeEnabled: false - }] - }; - - let auctionConfigs = [{ - 'bidId': '123', - 'config': { - 'seller': 'https://csr.onet.pl', - 'decisionLogicUrl': 'https://csr.onet.pl/testnetwork/v1/protected-audience-api/decision-logic.js', - 'interestGroupBuyers': ['https://csr.onet.pl'], - 'auctionSignals': { - 'params': { - site: 'testsite', - area: 'testarea', - network: 'testnetwork' - }, - 'sizes': ['300x250'], - 'gctx': '1234567890' - } - } - }]; - const resp = spec.interpretResponse({body: {gctx: '1234567890'}}, bidRequest); - expect(resp).to.deep.equal({bids: [], fledgeAuctionConfigs: auctionConfigs}); - }); }); }); diff --git a/test/spec/modules/relaidoBidAdapter_spec.js b/test/spec/modules/relaidoBidAdapter_spec.js index f0d019913e8..36d03c01138 100644 --- a/test/spec/modules/relaidoBidAdapter_spec.js +++ b/test/spec/modules/relaidoBidAdapter_spec.js @@ -7,6 +7,9 @@ import {getCoreStorageManager} from '../../../src/storageManager.js'; const UUID_KEY = 'relaido_uuid'; const relaido_uuid = 'hogehoge'; +const storage = getCoreStorageManager(); +storage.setCookie(UUID_KEY, relaido_uuid); + describe('RelaidoAdapter', function () { let bidRequest; let bidderRequest; @@ -15,10 +18,6 @@ describe('RelaidoAdapter', function () { let serverRequest; let generateUUIDStub; let triggerPixelStub; - before(() => { - const storage = getCoreStorageManager(); - storage.setCookie(UUID_KEY, relaido_uuid); - }); beforeEach(function () { generateUUIDStub = sinon.stub(utils, 'generateUUID').returns(relaido_uuid); @@ -239,7 +238,6 @@ describe('RelaidoAdapter', function () { const request = data.bids[0]; expect(bidRequests.method).to.equal('POST'); expect(bidRequests.url).to.equal('https://api.relaido.jp/bid/v1/sprebid'); - expect(data.canonical_url).to.equal('https://publisher.com/home'); expect(data.canonical_url_hash).to.equal('e6092f44a0044903ae3764126eedd6187c1d9f04'); expect(data.ref).to.equal(bidderRequest.refererInfo.page); expect(data.timeout_ms).to.equal(bidderRequest.timeout); @@ -318,23 +316,6 @@ describe('RelaidoAdapter', function () { expect(data.bids).to.have.lengthOf(1); expect(data.imuid).to.equal('i.tjHcK_7fTcqnbrS_YA2vaw'); }); - - it('should get userIdAsEids', function () { - const userIdAsEids = [ - { - source: 'hogehoge.com', - uids: { - atype: 1, - id: 'hugahuga' - } - } - ] - bidRequest.userIdAsEids = userIdAsEids - const bidRequests = spec.buildRequests([bidRequest], bidderRequest); - const data = JSON.parse(bidRequests.data); - expect(data.bids[0].userIdAsEids).to.have.lengthOf(1); - expect(data.bids[0].userIdAsEids[0].source).to.equal('hogehoge.com'); - }); }); describe('spec.interpretResponse', function () { @@ -343,7 +324,6 @@ describe('RelaidoAdapter', function () { expect(bidResponses).to.have.lengthOf(1); const response = bidResponses[0]; expect(response.requestId).to.equal(serverRequest.data.bids[0].bidId); - expect(response.placementId).to.equal(serverResponse.body.ads[0].placementId); expect(response.width).to.equal(serverRequest.data.bids[0].width); expect(response.height).to.equal(serverRequest.data.bids[0].height); expect(response.cpm).to.equal(serverResponse.body.ads[0].price); @@ -362,7 +342,6 @@ describe('RelaidoAdapter', function () { expect(bidResponses).to.have.lengthOf(1); const response = bidResponses[0]; expect(response.requestId).to.equal(serverRequest.data.bids[0].bidId); - expect(response.placementId).to.equal(serverResponse.body.ads[0].placementId); expect(response.width).to.equal(serverRequest.data.bids[0].width); expect(response.height).to.equal(serverRequest.data.bids[0].height); expect(response.cpm).to.equal(serverResponse.body.ads[0].price); @@ -380,7 +359,6 @@ describe('RelaidoAdapter', function () { expect(bidResponses).to.have.lengthOf(1); const response = bidResponses[0]; expect(response.requestId).to.equal(serverRequest.data.bids[0].bidId); - expect(response.placementId).to.equal(serverResponseBanner.body.ads[0].placementId); expect(response.cpm).to.equal(serverResponseBanner.body.ads[0].price); expect(response.currency).to.equal(serverResponseBanner.body.ads[0].currency); expect(response.creativeId).to.equal(serverResponseBanner.body.ads[0].creativeId); diff --git a/test/spec/modules/relayBidAdapter_spec.js b/test/spec/modules/relayBidAdapter_spec.js deleted file mode 100644 index 38a3cfc9b97..00000000000 --- a/test/spec/modules/relayBidAdapter_spec.js +++ /dev/null @@ -1,131 +0,0 @@ -import { expect } from 'chai'; -import { spec } from '../../../modules/relayBidAdapter.js'; -import { BANNER, VIDEO, NATIVE } from '../../../src/mediaTypes.js'; -import { getUniqueIdentifierStr } from '../../../src/utils.js'; - -const bidder = 'relay' -const endpoint = 'https://e.relay.bid/p/openrtb2'; - -describe('RelayBidAdapter', function () { - const bids = [ - { - bidId: getUniqueIdentifierStr(), - bidder, - mediaTypes: { [BANNER]: { sizes: [[300, 250]] } }, - params: { - accountId: 15000, - }, - ortb2Imp: { - ext: { - relay: { - bidders: { - bidderA: { - theId: 'abc123' - }, - bidderB: { - theId: 'xyz789' - } - } - } - } - } - }, - { - bidId: getUniqueIdentifierStr(), - bidder, - mediaTypes: { [BANNER]: { sizes: [[300, 250]] } }, - params: { - accountId: 30000, - }, - ortb2Imp: { - ext: { - relay: { - bidders: { - bidderA: { - theId: 'def456' - }, - bidderB: { - theId: 'uvw101112' - } - } - } - } - } - } - ]; - - const invalidBid = { - bidId: getUniqueIdentifierStr(), - bidder: bidder, - mediaTypes: { - [BANNER]: { - sizes: [[300, 250]] - } - }, - params: {} - } - - const bidderRequest = {}; - - describe('isBidRequestValid', function () { - it('Valid bids have a params.accountId.', function () { - expect(spec.isBidRequestValid(bids[0])).to.be.true; - }); - it('Invalid bids do not have a params.accountId.', function () { - expect(spec.isBidRequestValid(invalidBid)).to.be.false; - }); - }); - - describe('buildRequests', function () { - const requests = spec.buildRequests(bids, bidderRequest); - const firstRequest = requests[0]; - const secondRequest = requests[1]; - - it('Creates two requests', function () { - expect(firstRequest).to.exist; - expect(firstRequest.data).to.exist; - expect(firstRequest.method).to.exist; - expect(firstRequest.method).to.equal('POST'); - expect(firstRequest.url).to.exist; - expect(firstRequest.url).to.equal(`${endpoint}?a=15000&pb=1&pbv=v8.1.0`); - - expect(secondRequest).to.exist; - expect(secondRequest.data).to.exist; - expect(secondRequest.method).to.exist; - expect(secondRequest.method).to.equal('POST'); - expect(secondRequest.url).to.exist; - expect(secondRequest.url).to.equal(`${endpoint}?a=30000&pb=1&pbv=v8.1.0`); - }); - - it('Does not generate requests when there are no bids', function () { - const request = spec.buildRequests([], bidderRequest); - expect(request).to.be.an('array').that.is.empty; - }); - }); - - describe('getUserSyncs', function () { - it('Uses Prebid consent values if incoming sync URLs lack consent.', function () { - const syncOpts = { - iframeEnabled: true, - pixelEnabled: true - }; - const test_gdpr_applies = true; - const test_gdpr_consent_str = 'TEST_GDPR_CONSENT_STRING'; - const responses = [{ - body: { - ext: { - user_syncs: [ - { type: 'image', url: 'https://image-example.com' }, - { type: 'iframe', url: 'https://iframe-example.com' } - ] - } - } - }]; - - const sync_urls = spec.getUserSyncs(syncOpts, responses, { gdprApplies: test_gdpr_applies, consentString: test_gdpr_consent_str }); - expect(sync_urls).to.be.an('array'); - expect(sync_urls[0].url).to.equal('https://image-example.com/?gdpr=1&gdpr_consent=TEST_GDPR_CONSENT_STRING'); - expect(sync_urls[1].url).to.equal('https://iframe-example.com/?gdpr=1&gdpr_consent=TEST_GDPR_CONSENT_STRING'); - }); - }); -}); diff --git a/test/spec/modules/relevantdigitalBidAdapter_spec.js b/test/spec/modules/relevantdigitalBidAdapter_spec.js index 0e21453c8ba..b2a5495b3cb 100644 --- a/test/spec/modules/relevantdigitalBidAdapter_spec.js +++ b/test/spec/modules/relevantdigitalBidAdapter_spec.js @@ -1,10 +1,5 @@ import {spec, resetBidderConfigs} from 'modules/relevantdigitalBidAdapter.js'; import { parseUrl, deepClone } from 'src/utils.js'; -import { config } from 'src/config.js'; -import CONSTANTS from 'src/constants.json'; - -import adapterManager, { -} from 'src/adapterManager.js'; const expect = require('chai').expect; @@ -14,29 +9,14 @@ const ACCOUNT_ID = 'example_account_id'; const TEST_DOMAIN = 'example.com'; const TEST_PAGE = `https://${TEST_DOMAIN}/page.html`; -const CONFIG = { - enabled: true, - endpoint: CONSTANTS.S2S.DEFAULT_ENDPOINT, - timeout: 1000, - maxBids: 1, - adapter: 'prebidServer', - bidders: ['relevantdigital'], - accountId: 'abc' -}; - -const ADUNIT_CODE = '/19968336/header-bid-tag-0'; - -const BID_PARAMS = { +const BID_REQUEST = +{ + 'bidder': 'relevantdigital', 'params': { 'placementId': PLACEMENT_ID, 'accountId': ACCOUNT_ID, - 'pbsHost': PBS_HOST - } -}; - -const BID_REQUEST = { - 'bidder': 'relevantdigital', - ...BID_PARAMS, + 'pbsHost': PBS_HOST, + }, 'ortb2Imp': { 'ext': { 'tid': 'e13391ea-00f3-495d-99a6-d937990d73a9' @@ -52,7 +32,7 @@ const BID_REQUEST = { ] } }, - 'adUnitCode': ADUNIT_CODE, + 'adUnitCode': '/19968336/header-bid-tag-0', 'transactionId': 'e13391ea-00f3-495d-99a6-d937990d73a9', 'sizes': [ [ @@ -312,64 +292,4 @@ describe('Relevant Digital Bid Adaper', function () { expect(allSyncs).to.deep.equal(expectedResult) }); }); - describe('transformBidParams', function () { - beforeEach(() => { - config.setConfig({ - s2sConfig: CONFIG, - }); - }); - afterEach(() => { - config.resetConfig(); - }); - - const adUnit = (params) => ({ - code: ADUNIT_CODE, - bids: [ - { - bidder: 'relevantdigital', - adUnitCode: ADUNIT_CODE, - params, - } - ] - }); - - const request = (params) => adapterManager.makeBidRequests([adUnit(params)], 123, 'auction-id', 123, [], {})[0]; - - it('transforms adunit bid params and config params correctly', function () { - config.setConfig({ - relevantdigital: { - pbsHost: PBS_HOST, - accountId: ACCOUNT_ID, - }, - }); - const adUnitParams = { placementId: PLACEMENT_ID }; - const expextedTransformedBidParams = { - ...BID_PARAMS.params, pbsHost: `https://${BID_PARAMS.params.pbsHost}`, 'pbsBufferMs': 250 - }; - expect(spec.transformBidParams(adUnitParams, null, null, [request(adUnitParams)])).to.deep.equal(expextedTransformedBidParams); - }); - it('transforms adunit bid params correctly', function () { - const adUnitParams = { ...BID_PARAMS.params, pbsHost: 'host.relevant-digital.com', pbsBufferMs: 500 }; - const expextedTransformedBidParams = { - ...BID_PARAMS.params, pbsHost: 'host.relevant-digital.com', pbsBufferMs: 500 - }; - expect(spec.transformBidParams(adUnitParams, null, null, [request(adUnitParams)])).to.deep.equal(expextedTransformedBidParams); - }); - it('transforms adunit bid params correctly', function () { - const adUnitParams = { ...BID_PARAMS.params, pbsHost: 'host.relevant-digital.com', pbsBufferMs: 500 }; - const expextedTransformedBidParams = { - ...BID_PARAMS.params, pbsHost: 'host.relevant-digital.com', pbsBufferMs: 500 - }; - expect(spec.transformBidParams(adUnitParams, null, null, [request(adUnitParams)])).to.deep.equal(expextedTransformedBidParams); - }); - it('does not transform bid params if placementId is missing', function () { - const adUnitParams = { ...BID_PARAMS.params, placementId: null }; - expect(spec.transformBidParams(adUnitParams, null, null, [request(adUnitParams)])).to.equal(undefined); - }); - it('does not transform bid params s2s config is missing', function () { - config.resetConfig(); - const adUnitParams = BID_PARAMS.params; - expect(spec.transformBidParams(adUnitParams, null, null, [request(adUnitParams)])).to.equal(undefined); - }); - }) }); diff --git a/test/spec/modules/richaudienceBidAdapter_spec.js b/test/spec/modules/richaudienceBidAdapter_spec.js index 20c60ca328a..ea45ff7e0b0 100644 --- a/test/spec/modules/richaudienceBidAdapter_spec.js +++ b/test/spec/modules/richaudienceBidAdapter_spec.js @@ -4,8 +4,6 @@ import { spec } from 'modules/richaudienceBidAdapter.js'; import {config} from 'src/config.js'; -import * as utils from 'src/utils.js'; -import sinon from 'sinon'; describe('Richaudience adapter tests', function () { var DEFAULT_PARAMS_NEW_SIZES = [{ @@ -66,30 +64,6 @@ describe('Richaudience adapter tests', function () { user: {} }]; - var DEFAULT_PARAMS_VIDEO_TIMEOUT = [{ - adUnitCode: 'test-div', - bidId: '2c7c8e9c900244', - mediaTypes: { - video: { - context: 'instream', - playerSize: [640, 480], - mimes: ['video/mp4'] - } - }, - bidder: 'richaudience', - params: [{ - bidfloor: 0.5, - pid: 'ADb1f40rmi', - supplyType: 'site' - }], - timeout: 3000, - auctionId: '0cb3144c-d084-4686-b0d6-f5dbe917c563', - bidRequestsCount: 1, - bidderRequestId: '1858b7382993ca', - transactionId: '29df2112-348b-4961-8863-1b33684d95e6', - user: {} - }] - var DEFAULT_PARAMS_VIDEO_IN = [{ adUnitCode: 'test-div', bidId: '2c7c8e9c900244', @@ -905,24 +879,6 @@ describe('Richaudience adapter tests', function () { expect(requestContent).to.have.property('gpid').and.to.equal('/19968336/header-bid-tag-1#example-2'); }) - describe('onTimeout', function () { - beforeEach(function() { - sinon.stub(utils, 'triggerPixel'); - }); - - afterEach(function() { - utils.triggerPixel.restore(); - }); - it('onTimeout exist as a function', () => { - expect(spec.onTimeout).to.exist.and.to.be.a('function'); - }); - it('should send timeout', function () { - spec.onTimeout(DEFAULT_PARAMS_VIDEO_TIMEOUT); - expect(utils.triggerPixel.called).to.equal(true); - expect(utils.triggerPixel.firstCall.args[0]).to.equal('https://s.richaudience.com/err/?ec=6&ev=3000&pla=ADb1f40rmi&int=PREBID&pltfm=&node=&dm=localhost:9876'); - }); - }); - describe('userSync', function () { it('Verifies user syncs iframe include', function () { config.setConfig({ diff --git a/test/spec/modules/riseBidAdapter_spec.js b/test/spec/modules/riseBidAdapter_spec.js index 28bd123cb5d..eed8d74f271 100644 --- a/test/spec/modules/riseBidAdapter_spec.js +++ b/test/spec/modules/riseBidAdapter_spec.js @@ -22,12 +22,6 @@ describe('riseAdapter', function () { }); }); - describe('bid adapter', function () { - it('should have aliases', function () { - expect(spec.aliases).to.be.an('array').that.is.not.empty; - }); - }); - describe('isBidRequestValid', function () { const bid = { 'bidder': spec.code, @@ -59,7 +53,7 @@ describe('riseAdapter', function () { 'adUnitCode': 'adunit-code', 'sizes': [[640, 480]], 'params': { - 'org': 'jdye8weeyirk00000001', + 'org': 'jdye8weeyirk00000001' }, 'bidId': '299ffc8cca0b87', 'loop': 1, @@ -201,16 +195,6 @@ describe('riseAdapter', function () { expect(request.data.bids[1].mediaType).to.equal(BANNER) }); - it('should send the correct currency in bid request', function () { - const bid = utils.deepClone(bidRequests[0]); - bid.params = { - 'currency': 'EUR' - }; - const expectedCurrency = bid.params.currency; - const request = spec.buildRequests([bid], bidderRequest); - expect(request.data.bids[0].currency).to.equal(expectedCurrency); - }); - it('should respect syncEnabled option', function() { config.setConfig({ userSync: { diff --git a/test/spec/modules/rtbhouseBidAdapter_spec.js b/test/spec/modules/rtbhouseBidAdapter_spec.js index 0b944dcb077..78c700f3804 100644 --- a/test/spec/modules/rtbhouseBidAdapter_spec.js +++ b/test/spec/modules/rtbhouseBidAdapter_spec.js @@ -318,41 +318,6 @@ describe('RTBHouseAdapter', () => { expect(request.method).to.equal('POST'); }); - it('sets default fledgeConfig object values when none available from config', function () { - let bidRequest = Object.assign([], bidRequests); - delete bidRequest[0].params.test; - - config.setConfig({ fledgeConfig: false }); - const request = spec.buildRequests(bidRequest, { ...bidderRequest, fledgeEnabled: true }); - const data = JSON.parse(request.data); - expect(data.ext).to.exist.and.to.be.a('object'); - expect(data.ext.fledge_config).to.exist.and.to.be.a('object'); - expect(data.ext.fledge_config).to.contain.keys('seller', 'decisionLogicUrl', 'sellerTimeout'); - expect(data.ext.fledge_config.seller).to.equal('https://fledge-ssp.creativecdn.com'); - expect(data.ext.fledge_config.decisionLogicUrl).to.equal('https://fledge-ssp.creativecdn.com/component-seller-prebid.js'); - expect(data.ext.fledge_config.sellerTimeout).to.equal(500); - }); - - it('sets a fledgeConfig object values when available from config', function () { - let bidRequest = Object.assign([], bidRequests); - delete bidRequest[0].params.test; - - config.setConfig({ - fledgeConfig: { - seller: 'https://sellers.domain', - decisionLogicUrl: 'https://sellers.domain/decision.url' - } - }); - const request = spec.buildRequests(bidRequest, { ...bidderRequest, fledgeEnabled: true }); - const data = JSON.parse(request.data); - expect(data.ext).to.exist.and.to.be.a('object'); - expect(data.ext.fledge_config).to.exist.and.to.be.a('object'); - expect(data.ext.fledge_config).to.contain.keys('seller', 'decisionLogicUrl'); - expect(data.ext.fledge_config.seller).to.equal('https://sellers.domain'); - expect(data.ext.fledge_config.decisionLogicUrl).to.equal('https://sellers.domain/decision.url'); - expect(data.ext.fledge_config.sellerTimeout).to.not.exist; - }); - it('when FLEDGE is disabled, should not send imp.ext.ae', function () { let bidRequest = Object.assign([], bidRequests); delete bidRequest[0].params.test; diff --git a/test/spec/modules/rubiconBidAdapter_spec.js b/test/spec/modules/rubiconBidAdapter_spec.js index f0e33ce940e..d9c3555bf03 100644 --- a/test/spec/modules/rubiconBidAdapter_spec.js +++ b/test/spec/modules/rubiconBidAdapter_spec.js @@ -270,81 +270,7 @@ describe('the rubicon adapter', function () { }], criteoId: '1111', }; - bid.userIdAsEids = [ - { - 'source': 'liveintent.com', - 'uids': [ - { - 'id': '0000-1111-2222-3333', - 'atype': 3 - } - ], - 'ext': { - 'segments': [ - 'segA', - 'segB' - ] - } - }, - { - 'source': 'liveramp.com', - 'uids': [ - { - 'id': '1111-2222-3333-4444', - 'atype': 3 - } - ] - }, - { - 'source': 'adserver.org', - 'uids': [ - { - 'id': '3000', - 'atype': 1, - 'ext': { - 'rtiPartner': 'TDID' - } - } - ] - }, - { - 'source': 'pubcid.org', - 'uids': [ - { - 'id': '4000', - 'atype': 1 - } - ] - }, - { - 'source': 'example.com', - 'uids': [ - { - 'id': '333333', - 'ext': { - 'stype': 'ppuid' - } - } - ] - }, - { - 'source': 'id-partner.com', - 'uids': [ - { - 'id': '4444444' - } - ] - }, - { - 'source': 'criteo.com', - 'uids': [ - { - 'id': '1111', - 'atype': 1 - } - ] - } - ]; + bid.userIdAsEids = createEidsArray(bid.userId); return bidderRequest; } @@ -696,16 +622,6 @@ describe('the rubicon adapter', function () { expect(data['p_pos']).to.equal('atf;;btf;;'); }); - it('should correctly send cdep signal when requested', () => { - var badposRequest = utils.deepClone(bidderRequest); - badposRequest.bids[0].ortb2 = {device: {ext: {cdep: 3}}}; - - let [request] = spec.buildRequests(badposRequest.bids, badposRequest); - let data = parseQuery(request.data); - - expect(data['o_cdep']).to.equal('3'); - }); - it('ad engine query params should be ordered correctly', function () { sandbox.stub(Math, 'random').callsFake(() => 0.1); let [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); @@ -909,29 +825,6 @@ describe('the rubicon adapter', function () { }); }); - describe('GPP Consent', function () { - it('should send gpp information if bidderRequest has a value for gppConsent', function () { - bidderRequest.gppConsent = { - gppString: 'consent', - applicableSections: 2 - }; - let [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); - let data = parseQuery(request.data); - delete bidderRequest.gppConsent; - - expect(data['gpp']).to.equal('consent'); - expect(data['gpp_sid']).to.equal('2'); - }); - - it('should not send gpp information if bidderRequest does not have a value for gppConsent', function () { - let [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); - let data = parseQuery(request.data); - - expect(data['gpp']).to.equal(undefined); - expect(data['gpp_sid']).to.equal(undefined); - }); - }); - describe('first party data', function () { it('should not have any tg_v or tg_i params if all are undefined', function () { let params = { @@ -1239,30 +1132,6 @@ describe('the rubicon adapter', function () { }); }); - it('should still use single request if other rubicon configs are set after', function () { - // set single request to true - config.setConfig({ rubicon: { singleRequest: true } }); - - // execute some other rubicon setConfig - config.setConfig({ rubicon: { netRevenue: true } }); - - const bidCopy = utils.deepClone(bidderRequest.bids[0]); - bidderRequest.bids.push(bidCopy); - bidderRequest.bids.push(bidCopy); - bidderRequest.bids.push(bidCopy); - - let serverRequests = spec.buildRequests(bidderRequest.bids, bidderRequest); - - // should have 1 request only - expect(serverRequests).that.is.an('array').of.length(1); - - // get the built query - let data = parseQuery(serverRequests[0].data); - - // num slots should be 4 - expect(data.slots).to.equal('4'); - }); - it('should not group bid requests if singleRequest does not equal true', function () { config.setConfig({rubicon: {singleRequest: false}}); @@ -1332,20 +1201,7 @@ describe('the rubicon adapter', function () { clonedBid.userId = { tdid: 'abcd-efgh-ijkl-mnop-1234' }; - clonedBid.userIdAsEids = [ - { - 'source': 'adserver.org', - 'uids': [ - { - 'id': 'abcd-efgh-ijkl-mnop-1234', - 'atype': 1, - 'ext': { - 'rtiPartner': 'TDID' - } - } - ] - } - ]; + clonedBid.userIdAsEids = createEidsArray(clonedBid.userId); let [request] = spec.buildRequests([clonedBid], bidderRequest); let data = parseQuery(request.data); @@ -1362,23 +1218,7 @@ describe('the rubicon adapter', function () { segments: ['segA', 'segB'] } }; - clonedBid.userIdAsEids = [ - { - 'source': 'liveintent.com', - 'uids': [ - { - 'id': '0000-1111-2222-3333', - 'atype': 3 - } - ], - 'ext': { - 'segments': [ - 'segA', - 'segB' - ] - } - } - ]; + clonedBid.userIdAsEids = createEidsArray(clonedBid.userId); let [request] = spec.buildRequests([clonedBid], bidderRequest); let data = parseQuery(request.data); @@ -1395,23 +1235,7 @@ describe('the rubicon adapter', function () { segments: ['segD', 'segE'] } }; - clonedBid.userIdAsEids = [ - { - 'source': 'liveintent.com', - 'uids': [ - { - 'id': '1111-2222-3333-4444', - 'atype': 3 - } - ], - 'ext': { - 'segments': [ - 'segD', - 'segE' - ] - } - } - ] + clonedBid.userIdAsEids = createEidsArray(clonedBid.userId); let [request] = spec.buildRequests([clonedBid], bidderRequest); const unescapedData = unescape(request.data); @@ -1426,17 +1250,7 @@ describe('the rubicon adapter', function () { clonedBid.userId = { idl_env: '1111-2222-3333-4444' }; - clonedBid.userIdAsEids = [ - { - 'source': 'liveramp.com', - 'uids': [ - { - 'id': '1111-2222-3333-4444', - 'atype': 3 - } - ] - } - ] + clonedBid.userIdAsEids = createEidsArray(clonedBid.userId); let [request] = spec.buildRequests([clonedBid], bidderRequest); let data = parseQuery(request.data); @@ -1450,17 +1264,7 @@ describe('the rubicon adapter', function () { clonedBid.userId = { pubcid: '1111' }; - clonedBid.userIdAsEids = [ - { - 'source': 'pubcid.org', - 'uids': [ - { - 'id': '1111', - 'atype': 1 - } - ] - } - ] + clonedBid.userIdAsEids = createEidsArray(clonedBid.userId); let [request] = spec.buildRequests([clonedBid], bidderRequest); let data = parseQuery(request.data); @@ -1474,17 +1278,7 @@ describe('the rubicon adapter', function () { clonedBid.userId = { criteoId: '1111' }; - clonedBid.userIdAsEids = [ - { - 'source': 'criteo.com', - 'uids': [ - { - 'id': '1111', - 'atype': 1 - } - ] - } - ] + clonedBid.userIdAsEids = createEidsArray(clonedBid.userId); let [request] = spec.buildRequests([clonedBid], bidderRequest); let data = parseQuery(request.data); @@ -1511,27 +1305,7 @@ describe('the rubicon adapter', function () { }] }] }; - clonedBid.userIdAsEids = [ - { - 'source': 'example.com', - 'uids': [ - { - 'id': '11111', - 'ext': { - 'stype': 'ppuid' - } - } - ] - }, - { - 'source': 'id-partner.com', - 'uids': [ - { - 'id': '222222' - } - ] - } - ]; + clonedBid.userIdAsEids = createEidsArray(clonedBid.userId); let [request] = spec.buildRequests([clonedBid], bidderRequest); let data = parseQuery(request.data); @@ -1550,20 +1324,7 @@ describe('the rubicon adapter', function () { } } }; - clonedBid.userIdAsEids = [ - { - 'source': 'id5-sync.com', - 'uids': [ - { - 'id': '11111', - 'atype': 1, - 'ext': { - 'linkType': '22222' - } - } - ] - } - ]; + clonedBid.userIdAsEids = createEidsArray(clonedBid.userId); let [request] = spec.buildRequests([clonedBid], bidderRequest); let data = parseQuery(request.data); @@ -1587,22 +1348,6 @@ describe('the rubicon adapter', function () { expect(data['eid_catchall']).to.equal('11111^2'); }); - - it('should send rubiconproject special case', function () { - const clonedBid = utils.deepClone(bidderRequest.bids[0]); - // Hardcoding userIdAsEids since createEidsArray returns empty array if source not found in eids.js - clonedBid.userIdAsEids = [{ - source: 'rubiconproject.com', - uids: [{ - id: 'some-cool-id', - atype: 3 - }] - }] - let [request] = spec.buildRequests([clonedBid], bidderRequest); - let data = parseQuery(request.data); - - expect(data['eid_rubiconproject.com']).to.equal('some-cool-id'); - }); }); describe('Config user.id support', function () { @@ -1818,126 +1563,6 @@ describe('the rubicon adapter', function () { expect(data['tg_i.dfp_ad_unit_code']).to.equal('/a/b/c'); }); }); - - describe('client hints', function () { - let standardSuaObject; - beforeEach(function () { - standardSuaObject = { - source: 2, - platform: { - brand: 'macOS', - version: [ - '12', - '6', - '0' - ] - }, - browsers: [ - { - brand: 'Not.A/Brand', - version: [ - '8', - '0', - '0', - '0' - ] - }, - { - brand: 'Chromium', - version: [ - '114', - '0', - '5735', - '198' - ] - }, - { - brand: 'Google Chrome', - version: [ - '114', - '0', - '5735', - '198' - ] - } - ], - mobile: 0, - model: '', - bitness: '64', - architecture: 'x86' - } - }); - it('should send m_ch_* params if ortb2.device.sua object is there', function () { - let bidRequestSua = utils.deepClone(bidderRequest); - bidRequestSua.bids[0].ortb2 = { device: { sua: standardSuaObject } }; - - // How should fastlane query be constructed with default SUA - let expectedValues = { - m_ch_arch: 'x86', - m_ch_bitness: '64', - m_ch_ua: `"Not.A/Brand"|v="8","Chromium"|v="114","Google Chrome"|v="114"`, - m_ch_full_ver: `"Not.A/Brand"|v="8.0.0.0","Chromium"|v="114.0.5735.198","Google Chrome"|v="114.0.5735.198"`, - m_ch_mobile: '?0', - m_ch_platform: 'macOS', - m_ch_platform_ver: '12.6.0' - } - - // Build Fastlane call - let [request] = spec.buildRequests(bidRequestSua.bids, bidRequestSua); - let data = parseQuery(request.data); - - // Loop through expected values and if they do not match push an error - const errors = Object.entries(expectedValues).reduce((accum, [key, val]) => { - if (data[key] !== val) accum.push(`${key} - expect: ${val} - got: ${data[key]}`) - return accum; - }, []); - - // should be no errors - expect(errors).to.deep.equal([]); - }); - it('should not send invalid values for m_ch_*', function () { - let bidRequestSua = utils.deepClone(bidderRequest); - - // Alter input SUA object - // send model - standardSuaObject.model = 'Suface Duo'; - // send mobile = 1 - standardSuaObject.mobile = 1; - - // make browsers not an array - standardSuaObject.browsers = 'My Browser'; - - // make platform not have version - delete standardSuaObject.platform.version; - - // delete architecture - delete standardSuaObject.architecture; - - // add SUA to bid - bidRequestSua.bids[0].ortb2 = { device: { sua: standardSuaObject } }; - - // Build Fastlane request - let [request] = spec.buildRequests(bidRequestSua.bids, bidRequestSua); - let data = parseQuery(request.data); - - // should show new names - expect(data.m_ch_model).to.equal('Suface Duo'); - expect(data.m_ch_mobile).to.equal('?1'); - - // should still send platform - expect(data.m_ch_platform).to.equal('macOS'); - - // platform version not sent - expect(data).to.not.haveOwnProperty('m_ch_platform_ver'); - - // both ua and full_ver not sent because browsers not array - expect(data).to.not.haveOwnProperty('m_ch_ua'); - expect(data).to.not.haveOwnProperty('m_ch_full_ver'); - - // arch not sent - expect(data).to.not.haveOwnProperty('m_ch_arch'); - }); - }); }); if (FEATURES.VIDEO) { @@ -2381,6 +2006,16 @@ describe('the rubicon adapter', function () { bidderRequest = createVideoBidderRequest(); delete bidderRequest.bids[0].mediaTypes.video.linearity; expect(spec.isBidRequestValid(bidderRequest.bids[0])).to.equal(false); + + // change api to an string, no good + bidderRequest = createVideoBidderRequest(); + bidderRequest.bids[0].mediaTypes.video.api = 'string'; + expect(spec.isBidRequestValid(bidderRequest.bids[0])).to.equal(false); + + // delete api, no good + bidderRequest = createVideoBidderRequest(); + delete bidderRequest.bids[0].mediaTypes.video.api; + expect(spec.isBidRequestValid(bidderRequest.bids[0])).to.equal(false); }); it('bid request is valid when video context is outstream', function () { @@ -2709,15 +2344,6 @@ describe('the rubicon adapter', function () { const slotParams = spec.createSlotParams(bidderRequest.bids[0], bidderRequest); expect(slotParams.kw).to.equal('a,b,c'); }); - - it('should pass along o_ae param when fledge is enabled', () => { - const localBidRequest = Object.assign({}, bidderRequest.bids[0]); - localBidRequest.ortb2Imp.ext.ae = true; - - const slotParams = spec.createSlotParams(localBidRequest, bidderRequest); - - expect(slotParams['o_ae']).to.equal(1) - }); }); describe('classifiedAsVideo', function () { @@ -2781,18 +2407,6 @@ describe('the rubicon adapter', function () { expect(request.data.imp).to.have.nested.property('[0].native'); }); - it('should not break if position is set and no video MT', function () { - const bidReq = addNativeToBidRequest(bidderRequest); - delete bidReq.bids[0].mediaTypes.banner; - bidReq.bids[0].params = { - position: 'atf' - } - let [request] = spec.buildRequests(bidReq.bids, bidReq); - expect(request.method).to.equal('POST'); - expect(request.url).to.equal('https://prebid-server.rubiconproject.com/openrtb2/auction'); - expect(request.data.imp).to.have.nested.property('[0].native'); - }); - describe('that contains also a banner mediaType', function () { it('should send the banner to fastlane BUT NOT the native bid because missing params.video', function() { const bidReq = addNativeToBidRequest(bidderRequest); @@ -2844,35 +2458,6 @@ describe('the rubicon adapter', function () { expect(pbsRequest.data.imp).to.have.nested.property('[0].native'); expect(fastlanteRequest.url).to.equal('https://fastlane.rubiconproject.com/a/api/fastlane.json'); }); - - it('should include multiformat data in the pbs request', () => { - const bidReq = addNativeToBidRequest(bidderRequest); - // add second mediaType - bidReq.bids[0].mediaTypes = { - ...bidReq.bids[0].mediaTypes, - banner: { - sizes: [[300, 250]] - } - }; - bidReq.bids[0].params.bidonmultiformat = true; - let [pbsRequest, fastlanteRequest] = spec.buildRequests(bidReq.bids, bidReq); - expect(pbsRequest.data.imp[0].ext.prebid.bidder.rubicon.formats).to.deep.equal(['native', 'banner']); - }); - - it('should include multiformat data in the fastlane request', () => { - const bidReq = addNativeToBidRequest(bidderRequest); - // add second mediaType - bidReq.bids[0].mediaTypes = { - ...bidReq.bids[0].mediaTypes, - banner: { - sizes: [[300, 250]] - } - }; - bidReq.bids[0].params.bidonmultiformat = true; - let [pbsRequest, fastlanteRequest] = spec.buildRequests(bidReq.bids, bidReq); - let formatsIncluded = fastlanteRequest.data.indexOf('formats=native%2Cbanner') !== -1; - expect(formatsIncluded).to.equal(true); - }); }); describe('with bidonmultiformat === false', () => { it('should send only banner request because there\'s no params.video', () => { @@ -3438,43 +3023,6 @@ describe('the rubicon adapter', function () { expect(bids).to.be.lengthOf(0); }); - it('Should support recieving an auctionConfig and pass it along to Prebid', function () { - let response = { - 'status': 'ok', - 'account_id': 14062, - 'site_id': 70608, - 'zone_id': 530022, - 'size_id': 15, - 'alt_size_ids': [ - 43 - ], - 'tracking': '', - 'inventory': {}, - 'ads': [{ - 'status': 'ok', - 'cpm': 0, - 'size_id': 15 - }], - 'component_auction_config': [{ - 'random': 'value', - 'bidId': '5432' - }, - { - 'random': 'string', - 'bidId': '6789' - }] - }; - - let {bids, fledgeAuctionConfigs} = spec.interpretResponse({body: response}, { - bidRequest: bidderRequest.bids[0] - }); - - expect(bids).to.be.lengthOf(1); - expect(fledgeAuctionConfigs[0].bidId).to.equal('5432'); - expect(fledgeAuctionConfigs[0].config.random).to.equal('value'); - expect(fledgeAuctionConfigs[1].bidId).to.equal('6789'); - }); - it('should handle an error', function () { let response = { 'status': 'ok', @@ -4105,24 +3653,6 @@ describe('the rubicon adapter', function () { type: 'iframe', url: `${emilyUrl}?gdpr=1&gdpr_consent=foo&us_privacy=1NYN` }); }); - - it('should pass gpp params when gppConsent is present', function () { - expect(spec.getUserSyncs({iframeEnabled: true}, {}, {}, undefined, { - gppString: 'foo', - applicableSections: [2] - })).to.deep.equal({ - type: 'iframe', url: `${emilyUrl}?gpp=foo&gpp_sid=2` - }); - }); - - it('should pass multiple sid\'s when multiple are present', function () { - expect(spec.getUserSyncs({iframeEnabled: true}, {}, {}, undefined, { - gppString: 'foo', - applicableSections: [2, 5] - })).to.deep.equal({ - type: 'iframe', url: `${emilyUrl}?gpp=foo&gpp_sid=2,5` - }); - }); }); describe('get price granularity', function () { diff --git a/test/spec/modules/seedtagBidAdapter_spec.js b/test/spec/modules/seedtagBidAdapter_spec.js index 516c5ec933a..fb666e89f73 100644 --- a/test/spec/modules/seedtagBidAdapter_spec.js +++ b/test/spec/modules/seedtagBidAdapter_spec.js @@ -2,24 +2,10 @@ import { expect } from 'chai'; import { spec, getTimeoutUrl } from 'modules/seedtagBidAdapter.js'; import * as utils from 'src/utils.js'; import { config } from '../../../src/config.js'; -import * as mockGpt from 'test/spec/integration/faker/googletag.js'; 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, @@ -39,7 +25,7 @@ function getSlotConfigs(mediaTypes, params) { tid: 'd704d006-0d6e-4a09-ad6c-179e7e758096', } }, - adUnitCode: adUnitCode, + adUnitCode: 'adunit-code', }; } @@ -60,13 +46,6 @@ const createBannerSlotConfig = (placement, mediatypes) => { }; describe('Seedtag Adapter', function () { - beforeEach(function () { - mockGpt.reset(); - }); - - afterEach(function () { - mockGpt.enable(); - }); describe('isBidRequestValid method', function () { describe('returns true', function () { describe('when banner slot config has all mandatory params', () => { @@ -298,7 +277,7 @@ describe('Seedtag Adapter', function () { expect(data.auctionStart).to.be.greaterThanOrEqual(now); expect(data.ttfb).to.be.greaterThanOrEqual(0); - expect(data.bidRequests[0].adUnitCode).to.equal(adUnitCode); + expect(data.bidRequests[0].adUnitCode).to.equal('adunit-code'); }); describe('GDPR params', function () { @@ -395,35 +374,6 @@ describe('Seedtag Adapter', function () { expect(videoBid.sizes[1][1]).to.equal(600); expect(videoBid.requestCount).to.equal(1); }); - - it('should have geom parameters if slot is available', function() { - const request = spec.buildRequests(validBidRequests, bidderRequest); - const data = JSON.parse(request.data); - 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') - } - }) }); describe('COPPA param', function () { diff --git a/test/spec/modules/sharethroughBidAdapter_spec.js b/test/spec/modules/sharethroughBidAdapter_spec.js index 6a63ae681e7..4989dcb1098 100644 --- a/test/spec/modules/sharethroughBidAdapter_spec.js +++ b/test/spec/modules/sharethroughBidAdapter_spec.js @@ -1,6 +1,6 @@ import { expect } from 'chai'; -import { sharethroughAdapterSpec, sharethroughInternal } from 'modules/sharethroughBidAdapter.js'; import * as sinon from 'sinon'; +import { sharethroughAdapterSpec, sharethroughInternal } from 'modules/sharethroughBidAdapter.js'; import { newBidder } from 'src/adapters/bidderFactory.js'; import { config } from 'src/config'; import * as utils from 'src/utils'; @@ -73,10 +73,7 @@ describe('sharethrough adapter spec', function () { bidder: 'sharethrough', bidId: 'bidId1', transactionId: 'transactionId1', - sizes: [ - [300, 250], - [300, 600], - ], + sizes: [[300, 250], [300, 600]], params: { pkey: 'aaaa1111', bcat: ['cat1', 'cat2'], @@ -98,104 +95,104 @@ describe('sharethrough adapter spec', function () { }, userIdAsEids: [ { - source: 'pubcid.org', - uids: [ + 'source': 'pubcid.org', + 'uids': [ { - atype: 1, - id: 'fake-pubcid', + 'atype': 1, + 'id': 'fake-pubcid' }, - ], + ] }, { - source: 'liveramp.com', - uids: [ + 'source': 'liveramp.com', + 'uids': [ { - atype: 1, - id: 'fake-identity-link', - }, - ], + 'atype': 1, + 'id': 'fake-identity-link' + } + ] }, { - source: 'id5-sync.com', - uids: [ + 'source': 'id5-sync.com', + 'uids': [ { - atype: 1, - id: 'fake-id5id', - }, - ], + 'atype': 1, + 'id': 'fake-id5id' + } + ] }, { - source: 'adserver.org', - uids: [ + 'source': 'adserver.org', + 'uids': [ { - atype: 1, - id: 'fake-tdid', - }, - ], + 'atype': 1, + 'id': 'fake-tdid' + } + ] }, { - source: 'criteo.com', - uids: [ + 'source': 'criteo.com', + 'uids': [ { - atype: 1, - id: 'fake-criteo', - }, - ], + 'atype': 1, + 'id': 'fake-criteo' + } + ] }, { - source: 'britepool.com', - uids: [ + 'source': 'britepool.com', + 'uids': [ { - atype: 1, - id: 'fake-britepool', - }, - ], + 'atype': 1, + 'id': 'fake-britepool' + } + ] }, { - source: 'liveintent.com', - uids: [ + 'source': 'liveintent.com', + 'uids': [ { - atype: 1, - id: 'fake-lipbid', - }, - ], + 'atype': 1, + 'id': 'fake-lipbid' + } + ] }, { - source: 'intentiq.com', - uids: [ + 'source': 'intentiq.com', + 'uids': [ { - atype: 1, - id: 'fake-intentiq', - }, - ], + 'atype': 1, + 'id': 'fake-intentiq' + } + ] }, { - source: 'crwdcntrl.net', - uids: [ + 'source': 'crwdcntrl.net', + 'uids': [ { - atype: 1, - id: 'fake-lotame', - }, - ], + 'atype': 1, + 'id': 'fake-lotame' + } + ] }, { - source: 'parrable.com', - uids: [ + 'source': 'parrable.com', + 'uids': [ { - atype: 1, - id: 'fake-parrable', - }, - ], + 'atype': 1, + 'id': 'fake-parrable' + } + ] }, { - source: 'netid.de', - uids: [ + 'source': 'netid.de', + 'uids': [ { - atype: 1, - id: 'fake-netid', - }, - ], - }, + 'atype': 1, + 'id': 'fake-netid' + } + ] + } ], crumbs: { pubcid: 'fake-pubcid-in-crumbs-obj', @@ -253,10 +250,10 @@ describe('sharethrough adapter spec', function () { }, ortb2: { source: { - tid: 'auction-id', - }, + tid: 'auction-id' + } }, - timeout: 242, + timeout: 242 }; }); @@ -315,12 +312,6 @@ describe('sharethrough adapter spec', function () { expect(eid.uids[0].atype).to.be.ok; } - // expect(openRtbReq.regs.gpp).to.equal(bidderRequest.gppConsent.gppString); - // expect(openRtbReq.regs.gpp_sid).to.equal(bidderRequest.gppConsent.applicableSections); - - // expect(openRtbReq.regs.ext.gpp).to.equal(bidderRequest.ortb2.regs.gpp); - // expect(openRtbReq.regs.ext.gpp_sid).to.equal(bidderRequest.ortb2.regs.gpp_sid); - expect(openRtbReq.device.ua).to.equal(navigator.userAgent); expect(openRtbReq.regs.coppa).to.equal(1); @@ -404,25 +395,6 @@ describe('sharethrough adapter spec', function () { expect(openRtbReq.regs.coppa).to.equal(0); }); }); - - describe('gpp', () => { - it('should properly attach GPP information to the request when applicable', () => { - bidderRequest.gppConsent = { - gppString: 'some-gpp-string', - applicableSections: [3, 5], - }; - - const openRtbReq = spec.buildRequests(bidRequests, bidderRequest)[0].data; - expect(openRtbReq.regs.gpp).to.equal(bidderRequest.gppConsent.gppString); - expect(openRtbReq.regs.gpp_sid).to.equal(bidderRequest.gppConsent.applicableSections); - }); - - it('should populate request accordingly when gpp explicitly does not apply', function () { - const openRtbReq = spec.buildRequests(bidRequests, {})[0].data; - - expect(openRtbReq.regs.gpp).to.be.undefined; - }); - }); }); describe('transaction id at the impression level', () => { @@ -483,10 +455,7 @@ describe('sharethrough adapter spec', function () { const bannerImp = builtRequest.data.imp[0].banner; expect(bannerImp.pos).to.equal(1); expect(bannerImp.topframe).to.equal(1); - expect(bannerImp.format).to.deep.equal([ - { w: 300, h: 250 }, - { w: 300, h: 600 }, - ]); + expect(bannerImp.format).to.deep.equal([{ w: 300, h: 250 }, { w: 300, h: 600 }]); }); it('should default to pos 0 if not provided', () => { @@ -588,43 +557,6 @@ describe('sharethrough adapter spec', function () { }); }); - describe('cookie deprecation', () => { - it('should not add cdep if we do not get it in an impression request', () => { - const builtRequests = spec.buildRequests(bidRequests, { - auctionId: 'new-auction-id', - ortb2: { - device: { - ext: { - propThatIsNotCdep: 'value-we-dont-care-about', - }, - }, - }, - }); - const noCdep = builtRequests.every((builtRequest) => { - const ourCdepValue = builtRequest.data.device?.ext?.cdep; - return ourCdepValue === undefined; - }); - expect(noCdep).to.be.true; - }); - - it('should add cdep if we DO get it in an impression request', () => { - const builtRequests = spec.buildRequests(bidRequests, { - auctionId: 'new-auction-id', - ortb2: { - device: { - ext: { - cdep: 'cdep-value', - }, - }, - }, - }); - const cdepPresent = builtRequests.every((builtRequest) => { - return builtRequest.data.device.ext.cdep === 'cdep-value'; - }); - expect(cdepPresent).to.be.true; - }); - }); - describe('first party data', () => { const firstPartyData = { site: { @@ -653,14 +585,10 @@ describe('sharethrough adapter spec', function () { }, bcat: ['IAB1', 'IAB2-1'], badv: ['domain1.com', 'domain2.com'], - regs: { - gpp: 'gpp_string', - gpp_sid: [7], - }, }; it('should include first party data in open rtb request, site section', () => { - const openRtbReq = spec.buildRequests(bidRequests, { ...bidderRequest, ortb2: firstPartyData })[0].data; + const openRtbReq = spec.buildRequests(bidRequests, {...bidderRequest, ortb2: firstPartyData})[0].data; expect(openRtbReq.site.name).to.equal(firstPartyData.site.name); expect(openRtbReq.site.keywords).to.equal(firstPartyData.site.keywords); @@ -684,13 +612,6 @@ describe('sharethrough adapter spec', function () { expect(openRtbReq.bcat).to.deep.equal(firstPartyData.bcat); expect(openRtbReq.badv).to.deep.equal(firstPartyData.badv); }); - - it('should include first party data in open rtb request, regulation section', () => { - const openRtbReq = spec.buildRequests(bidRequests, { ...bidderRequest, ortb2: firstPartyData })[0].data; - - expect(openRtbReq.regs.ext.gpp).to.equal(firstPartyData.regs.gpp); - expect(openRtbReq.regs.ext.gpp_sid).to.equal(firstPartyData.regs.gpp_sid); - }); }); }); @@ -703,31 +624,26 @@ describe('sharethrough adapter spec', function () { request = spec.buildRequests(bidRequests, bidderRequest)[0]; response = { body: { - seatbid: [ - { - bid: [ - { - id: '123', - impid: 'bidId1', - w: 300, - h: 250, - price: 42, - crid: 'creative', - dealid: 'deal', - adomain: ['domain.com'], - adm: 'markup', - }, - { - id: '456', - impid: 'bidId2', - w: 640, - h: 480, - price: 42, - adm: 'vastTag', - }, - ], - }, - ], + seatbid: [{ + bid: [{ + id: '123', + impid: 'bidId1', + w: 300, + h: 250, + price: 42, + crid: 'creative', + dealid: 'deal', + adomain: ['domain.com'], + adm: 'markup', + }, { + id: '456', + impid: 'bidId2', + w: 640, + h: 480, + price: 42, + adm: 'vastTag', + }], + }], }, }; }); @@ -757,20 +673,16 @@ describe('sharethrough adapter spec', function () { request = spec.buildRequests(bidRequests, bidderRequest)[1]; response = { body: { - seatbid: [ - { - bid: [ - { - id: '456', - impid: 'bidId2', - w: 640, - h: 480, - price: 42, - adm: 'vastTag', - }, - ], - }, - ], + seatbid: [{ + bid: [{ + id: '456', + impid: 'bidId2', + w: 640, + h: 480, + price: 42, + adm: 'vastTag', + }], + }], }, }; }); @@ -800,28 +712,24 @@ describe('sharethrough adapter spec', function () { request = spec.buildRequests(bidRequests, bidderRequest)[0]; response = { body: { - seatbid: [ - { - bid: [ - { - id: '123', - impid: 'bidId1', - w: 300, - h: 250, - price: 42, - crid: 'creative', - dealid: 'deal', - adomain: ['domain.com'], - adm: 'markup', - }, - ], - }, - ], + seatbid: [{ + bid: [{ + id: '123', + impid: 'bidId1', + w: 300, + h: 250, + price: 42, + crid: 'creative', + dealid: 'deal', + adomain: ['domain.com'], + adm: 'markup', + }], + }], }, }; }); - it("should have null optional fields when the response's optional seatbid[].bid[].ext field is empty", () => { + it('should have null optional fields when the response\'s optional seatbid[].bid[].ext field is empty', () => { const bid = spec.interpretResponse(response, request)[0]; expect(bid.meta.networkId).to.be.null; @@ -839,7 +747,7 @@ describe('sharethrough adapter spec', function () { expect(bid.meta.mediaType).to.be.null; }); - it("should have populated fields when the response's optional seatbid[].bid[].ext fields are filled", () => { + it('should have populated fields when the response\'s optional seatbid[].bid[].ext fields are filled', () => { response.body.seatbid[0].bid[0].ext = { networkId: 'my network id', networkName: 'my network name', @@ -884,8 +792,8 @@ describe('sharethrough adapter spec', function () { expect(syncArray).to.deep.equal([ { type: 'image', url: 'cookieUrl1' }, { type: 'image', url: 'cookieUrl2' }, - { type: 'image', url: 'cookieUrl3' }, - ]); + { type: 'image', url: 'cookieUrl3' }], + ); }); it('returns an empty array if serverResponses is empty', function () { diff --git a/test/spec/modules/shinezRtbBidAdapter_spec.js b/test/spec/modules/shinezRtbBidAdapter_spec.js deleted file mode 100644 index 3965cd69c5f..00000000000 --- a/test/spec/modules/shinezRtbBidAdapter_spec.js +++ /dev/null @@ -1,639 +0,0 @@ -import {expect} from 'chai'; -import { - spec as adapter, - createDomain, - hashCode, - extractPID, - extractCID, - extractSubDomain, - getStorageItem, - setStorageItem, - tryParseJSON, - getUniqueDealId, -} from 'modules/shinezRtbBidAdapter'; -import * as utils from 'src/utils.js'; -import {version} from 'package.json'; -import {useFakeTimers} from 'sinon'; -import {BANNER, VIDEO} from '../../../src/mediaTypes'; -import {config} from '../../../src/config'; -import {deepAccess} from 'src/utils.js'; - -export const TEST_ID_SYSTEMS = ['britepoolid', 'criteoId', 'id5id', 'idl_env', 'lipb', 'netId', 'parrableId', 'pubcid', 'tdid', 'pubProvidedId', 'digitrustid']; - -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', - 'sizes': [[300, 250], [300, 600]], - 'bidderRequestId': '1fdb5ff1b6eaa7', - 'bidRequestsCount': 4, - 'bidderRequestsCount': 3, - 'bidderWinsCount': 1, - 'requestId': 'b0777d85-d061-450e-9bc7-260dd54bbb7a', - 'schain': 'a0819c69-005b-41ed-af06-1be1e0aefefc', - 'mediaTypes': [BANNER], - 'ortb2Imp': { - 'ext': { - 'gpid': '0123456789', - 'tid': '56e184c6-bde9-497b-b9b9-cf47a61381ee' - } - } -}; - -const VIDEO_BID = { - 'bidId': '2d52001cabd527', - 'adUnitCode': '63550ad1ff6642d368cba59dh5884270560', - 'bidderRequestId': '12a8ae9ada9c13', - '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 - } - }, - 'ortb2Imp': { - 'ext': { - 'gpid': '0123456789', - 'tid': '56e184c6-bde9-497b-b9b9-cf47a61381ee' - } - } -} - -const BIDDER_REQUEST = { - 'gdprConsent': { - 'consentString': 'consent_string', - 'gdprApplies': true - }, - 'gppConsent': { - 'gppString': 'gpp_string', - 'applicableSections': [7] - }, - 'uspConsent': 'consent_string', - 'refererInfo': { - 'page': 'https://www.greatsite.com', - 'ref': 'https://www.somereferrer.com' - }, - 'ortb2': { - 'regs': { - 'gpp': 'gpp_string', - 'gpp_sid': [7] - }, - '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': '' - } - } - } -}; - -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': ['sweetgum.io'], - 'exp': 60, - 'width': 545, - 'height': 307, - 'mediaType': 'video', - 'creativeId': '12610997325162499419', - 'price': 2, - 'cookies': [] - }] - } -}; - -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('ShinezRtbBidAdapter', function () { - describe('validtae spec', function () { - it('exists and is a function', function () { - expect(adapter.isBidRequestValid).to.exist.and.to.be.a('function'); - }); - - it('exists and is a function', 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 () { - $$PREBID_GLOBAL$$.bidderSettings = { - shinezRtb: { - storageAllowed: true - } - }; - sandbox = sinon.sandbox.create(); - sandbox.stub(Date, 'now').returns(1000); - }); - - it('should build video request', function () { - const hashUrl = hashCode(BIDDER_REQUEST.refererInfo.page); - config.setConfig({ - bidderTimeout: 3000, - enableTIDs: true - }); - 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], - transactionId: '56e184c6-bde9-497b-b9b9-cf47a61381ee', - prebidVersion: version, - 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': '' - }, - 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: '0123456789' - } - }); - }); - - it('should build banner request for each size', function () { - const hashUrl = hashCode(BIDDER_REQUEST.refererInfo.page); - config.setConfig({ - bidderTimeout: 3000, - enableTIDs: true - }); - 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', - bidRequestsCount: 4, - bidderRequestsCount: 3, - bidderWinsCount: 1, - bidderTimeout: 3000, - bidderRequestId: '1fdb5ff1b6eaa7', - transactionId: '56e184c6-bde9-497b-b9b9-cf47a61381ee', - 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': '' - }, - 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', - } - }); - }); - - after(function () { - $$PREBID_GLOBAL$$.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.sweetgum.io/api/sync/iframe/?cid=testcid123&gdpr=0&gdpr_consent=&us_privacy=' - }]); - }); - - 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.sweetgum.io/api/sync/iframe/?cid=testcid123&gdpr=0&gdpr_consent=&us_privacy=' - }]); - }); - - 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.sweetgum.io/api/sync/image/?cid=testcid123&gdpr=0&gdpr_consent=&us_privacy=', - '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: ['sweetgum.io'], - agencyName: 'Agency Name', - }; - const responses = adapter.interpretResponse(serverResponse, REQUEST); - expect(responses[0].meta).to.deep.equal({ - advertiserDomains: ['sweetgum.io'], - 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: ['sweetgum.io'] - } - }); - }); - - 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 'digitrustid': - return {data: {id}}; - case 'lipb': - return {lipbid: id}; - case 'parrableId': - return {eid: 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); - }); - }); - }); - - 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 () { - $$PREBID_GLOBAL$$.bidderSettings = { - shinezRtb: { - storageAllowed: true - } - }; - }); - after(function () { - $$PREBID_GLOBAL$$.bidderSettings = {}; - }); - const key = 'myKey'; - let uniqueDealId; - beforeEach(() => { - uniqueDealId = getUniqueDealId(key, 0); - }) - - it('should get current unique deal id', function (done) { - // waiting some time so `now` will become past - setTimeout(() => { - const current = getUniqueDealId(key); - expect(current).to.be.equal(uniqueDealId); - done(); - }, 200); - }); - - it('should get new unique deal id on expiration', function (done) { - setTimeout(() => { - const current = getUniqueDealId(key, 100); - expect(current).to.not.be.equal(uniqueDealId); - done(); - }, 200) - }); - }); - - describe('storage utils', function () { - before(function () { - $$PREBID_GLOBAL$$.bidderSettings = { - shinezRtb: { - storageAllowed: true - } - }; - }); - after(function () { - $$PREBID_GLOBAL$$.bidderSettings = {}; - }); - it('should get value from storage with create param', function () { - const now = Date.now(); - const clock = useFakeTimers({ - shouldAdvanceTime: true, - now - }); - setStorageItem('myKey', 2020); - const {value, created} = getStorageItem('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('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/smaatoBidAdapter_spec.js b/test/spec/modules/smaatoBidAdapter_spec.js index 185dee2430f..61a877f329d 100644 --- a/test/spec/modules/smaatoBidAdapter_spec.js +++ b/test/spec/modules/smaatoBidAdapter_spec.js @@ -1095,15 +1095,17 @@ describe('smaatoBidAdapterTest', () => { criteoId: '123456', tdid: '89145' }, - userIdAsEids: [ - {id: 1}, {id: 2} - ] + userIdAsEids: createEidsArray({ + criteoId: '123456', + tdid: '89145' + }) }; const reqs = spec.buildRequests([userIdBidRequest], defaultBidderRequest); const req = extractPayloadOfFirstAndOnlyRequest(reqs); - expect(req.user.ext.eids).to.eql(userIdBidRequest.userIdAsEids); + expect(req.user.ext.eids).to.exist; + expect(req.user.ext.eids).to.have.length(2); }); }); diff --git a/test/spec/modules/smartadserverBidAdapter_spec.js b/test/spec/modules/smartadserverBidAdapter_spec.js index 58b4cd8c0d0..9daa6a87826 100644 --- a/test/spec/modules/smartadserverBidAdapter_spec.js +++ b/test/spec/modules/smartadserverBidAdapter_spec.js @@ -786,7 +786,7 @@ describe('Smart bid adapter tests', function () { expect(request[0]).to.have.property('method').and.to.equal('POST'); const requestContent = JSON.parse(request[0].data); expect(requestContent).to.have.property('videoData'); - expect(requestContent.videoData).not.to.have.property('videoProtocol').eq(true); + expect(requestContent.videoData).to.have.property('videoProtocol').and.to.equal(null); expect(requestContent.videoData).to.have.property('adBreak').and.to.equal(2); }); @@ -833,73 +833,6 @@ describe('Smart bid adapter tests', function () { expect(requestContent.videoData).to.have.property('videoProtocol').and.to.equal(6); expect(requestContent.videoData).to.have.property('adBreak').and.to.equal(3); }); - - it('should pass additional parameters', function () { - const request = spec.buildRequests([{ - bidder: 'smartadserver', - mediaTypes: { - video: { - context: 'instream', - api: [1, 2, 3], - maxbitrate: 50, - minbitrate: 20, - maxduration: 30, - minduration: 5, - placement: 3, - playbackmethod: [2, 4], - playerSize: [[640, 480]], - plcmt: 1, - skip: 0 - } - }, - params: { - siteId: '123' - } - }]); - const requestContent = JSON.parse(request[0].data); - - expect(requestContent.videoData).to.have.property('iabframeworks').and.to.equal('1,2,3'); - expect(requestContent.videoData).not.to.have.property('skip'); - expect(requestContent.videoData).to.have.property('vbrmax').and.to.equal(50); - expect(requestContent.videoData).to.have.property('vbrmin').and.to.equal(20); - expect(requestContent.videoData).to.have.property('vdmax').and.to.equal(30); - expect(requestContent.videoData).to.have.property('vdmin').and.to.equal(5); - expect(requestContent.videoData).to.have.property('vplcmt').and.to.equal(1); - expect(requestContent.videoData).to.have.property('vpmt').and.to.have.lengthOf(2); - expect(requestContent.videoData.vpmt[0]).to.equal(2); - expect(requestContent.videoData.vpmt[1]).to.equal(4); - expect(requestContent.videoData).to.have.property('vpt').and.to.equal(3); - }); - - it('should not pass not valuable parameters', function () { - const request = spec.buildRequests([{ - bidder: 'smartadserver', - mediaTypes: { - video: { - context: 'instream', - maxbitrate: 20, - minbitrate: null, - maxduration: 0, - playbackmethod: [], - playerSize: [[640, 480]], - plcmt: 1 - } - }, - params: { - siteId: '123' - } - }]); - const requestContent = JSON.parse(request[0].data); - - expect(requestContent.videoData).not.to.have.property('iabframeworks'); - expect(requestContent.videoData).to.have.property('vbrmax').and.to.equal(20); - expect(requestContent.videoData).not.to.have.property('vbrmin'); - expect(requestContent.videoData).not.to.have.property('vdmax'); - expect(requestContent.videoData).not.to.have.property('vdmin'); - expect(requestContent.videoData).to.have.property('vplcmt').and.to.equal(1); - expect(requestContent.videoData).not.to.have.property('vpmt'); - expect(requestContent.videoData).not.to.have.property('vpt'); - }); }); }); @@ -1096,7 +1029,7 @@ describe('Smart bid adapter tests', function () { expect(request[0]).to.have.property('method').and.to.equal('POST'); const requestContent = JSON.parse(request[0].data); expect(requestContent).to.have.property('videoData'); - expect(requestContent.videoData).not.to.have.property('videoProtocol').eq(true); + expect(requestContent.videoData).to.have.property('videoProtocol').and.to.equal(null); expect(requestContent.videoData).to.have.property('adBreak').and.to.equal(2); }); @@ -1460,41 +1393,4 @@ describe('Smart bid adapter tests', function () { expect(requestContent).to.have.property('gpid').and.to.equal(gpid); }); }); - - describe('#getValuableProperty method', function () { - it('should return an object when calling with a number value', () => { - const obj = spec.getValuableProperty('prop', 3); - expect(obj).to.deep.equal({ prop: 3 }); - }); - - it('should return an empty object when calling with a string value', () => { - const obj = spec.getValuableProperty('prop', 'str'); - expect(obj).to.deep.equal({}); - }); - - it('should return an empty object when calling with a number property', () => { - const obj = spec.getValuableProperty(7, 'str'); - expect(obj).to.deep.equal({}); - }); - - it('should return an empty object when calling with a null value', () => { - const obj = spec.getValuableProperty('prop', null); - expect(obj).to.deep.equal({}); - }); - - it('should return an empty object when calling with an object value', () => { - const obj = spec.getValuableProperty('prop', {}); - expect(obj).to.deep.equal({}); - }); - - it('should return an empty object when calling with a 0 value', () => { - const obj = spec.getValuableProperty('prop', 0); - expect(obj).to.deep.equal({}); - }); - - it('should return an empty object when calling without the value argument', () => { - const obj = spec.getValuableProperty('prop'); - expect(obj).to.deep.equal({}); - }); - }); }); diff --git a/test/spec/modules/smartyadsBidAdapter_spec.js b/test/spec/modules/smartyadsBidAdapter_spec.js index 458ccc37759..3474753c838 100644 --- a/test/spec/modules/smartyadsBidAdapter_spec.js +++ b/test/spec/modules/smartyadsBidAdapter_spec.js @@ -1,7 +1,6 @@ import {expect} from 'chai'; import {spec} from '../../../modules/smartyadsBidAdapter.js'; import { config } from '../../../src/config.js'; -import {server} from '../../mocks/xhr'; describe('SmartyadsAdapter', function () { let bid = { @@ -15,21 +14,6 @@ describe('SmartyadsAdapter', function () { } }; - let bidResponse = { - width: 300, - height: 250, - mediaType: 'banner', - ad: `test mode`, - requestId: '23fhj33i987f', - cpm: 0.1, - ttl: 120, - creativeId: '123', - netRevenue: true, - currency: 'USD', - dealId: 'HASH', - sid: 1234 - }; - describe('isBidRequestValid', function () { it('Should return true if there are bidId, params and sourceid parameters present', function () { expect(spec.isBidRequestValid(bid)).to.be.true; @@ -52,11 +36,7 @@ describe('SmartyadsAdapter', function () { expect(serverRequest.method).to.equal('POST'); }); it('Returns valid URL', function () { - expect(serverRequest.url).to.be.oneOf([ - 'https://n1.smartyads.com/?c=o&m=prebid&secret_key=prebid_js', - 'https://n2.smartyads.com/?c=o&m=prebid&secret_key=prebid_js', - 'https://n6.smartyads.com/?c=o&m=prebid&secret_key=prebid_js' - ]); + expect(serverRequest.url).to.equal('https://n1.smartyads.com/?c=o&m=prebid&secret_key=prebid_js'); }); it('Returns valid data if array of bids is valid', function () { let data = serverRequest.data; @@ -263,7 +243,7 @@ describe('SmartyadsAdapter', function () { }); }); describe('getUserSyncs', function () { - const syncUrl = 'https://as.ck-ie.com/prebidjs?p=7c47322e527cf8bdeb7facc1bb03387a&gdpr=0&gdpr_consent=&type=iframe&us_privacy=&gpp='; + const syncUrl = 'https://as.ck-ie.com/prebidjs?p=7c47322e527cf8bdeb7facc1bb03387a&gdpr=0&gdpr_consent=&type=iframe&us_privacy='; const syncOptions = { iframeEnabled: true }; @@ -277,79 +257,4 @@ describe('SmartyadsAdapter', function () { ]); }); }); - - describe('onBidWon', function () { - it('should exists', function () { - expect(spec.onBidWon).to.exist.and.to.be.a('function'); - }); - - it('should send a valid bid won notice', function () { - const bid = { - 'c': 'o', - 'm': 'prebid', - 'secret_key': 'prebid_js', - 'winTest': '1', - 'postData': [{ - 'bidder': 'smartyads', - 'params': [ - {'host': 'prebid', - 'accountid': '123', - 'sourceid': '12345' - }] - }] - }; - spec.onBidWon(bid); - expect(server.requests.length).to.equal(1); - }); - }); - - describe('onTimeout', function () { - it('should exists', function () { - expect(spec.onTimeout).to.exist.and.to.be.a('function'); - }); - - it('should send a valid bid timeout notice', function () { - const bid = { - 'c': 'o', - 'm': 'prebid', - 'secret_key': 'prebid_js', - 'bidTimeout': '1', - 'postData': [{ - 'bidder': 'smartyads', - 'params': [ - {'host': 'prebid', - 'accountid': '123', - 'sourceid': '12345' - }] - }] - }; - spec.onTimeout(bid); - expect(server.requests.length).to.equal(1); - }); - }); - - describe('onBidderError', function () { - it('should exists', function () { - expect(spec.onBidderError).to.exist.and.to.be.a('function'); - }); - - it('should send a valid bidder error notice', function () { - const bid = { - 'c': 'o', - 'm': 'prebid', - 'secret_key': 'prebid_js', - 'bidderError': '1', - 'postData': [{ - 'bidder': 'smartyads', - 'params': [ - {'host': 'prebid', - 'accountid': '123', - 'sourceid': '12345' - }] - }] - }; - spec.onBidderError(bid); - expect(server.requests.length).to.equal(1); - }); - }); }); diff --git a/test/spec/modules/smilewantedBidAdapter_spec.js b/test/spec/modules/smilewantedBidAdapter_spec.js index 99c4034610f..22221dbe1ef 100644 --- a/test/spec/modules/smilewantedBidAdapter_spec.js +++ b/test/spec/modules/smilewantedBidAdapter_spec.js @@ -93,24 +93,7 @@ const BID_RESPONSE_DISPLAY = { const VIDEO_INSTREAM_REQUEST = [{ code: 'video1', mediaTypes: { - video: { - context: 'instream', - mimes: ['video/mp4'], - minduration: 0, - maxduration: 120, - protocols: [1, 2, 3, 4, 5, 6, 7, 8], - startdelay: 0, - placement: 1, - skip: 1, - skipafter: 10, - minbitrate: 10, - maxbitrate: 10, - delivery: [1], - playbackmethod: [2], - api: [1, 2], - linearity: 1, - playerSize: [640, 480] - } + video: {} }, sizes: [ [640, 480] @@ -180,99 +163,6 @@ const BID_RESPONSE_VIDEO_OUTSTREAM = { } }; -const NATIVE_REQUEST = [{ - adUnitCode: 'native_300x250', - code: '/19968336/prebid_native_example_1', - bidId: '12345', - sizes: [ - [300, 250] - ], - mediaTypes: { - native: { - sendTargetingKeys: false, - title: { - required: true, - len: 140 - }, - image: { - required: true, - sizes: [300, 250] - }, - icon: { - required: false, - sizes: [50, 50] - }, - sponsoredBy: { - required: true - }, - body: { - required: true - }, - clickUrl: { - required: false - }, - privacyLink: { - required: false - }, - cta: { - required: false - }, - rating: { - required: false - }, - likes: { - required: false - }, - downloads: { - required: false - }, - price: { - required: false - }, - salePrice: { - required: false - }, - phone: { - required: false - }, - address: { - required: false - }, - desc2: { - required: false - }, - displayUrl: { - required: false - } - } - }, - bidder: 'smilewanted', - params: { - zoneId: 4, - }, - requestId: 'request_abcd1234', - ortb2Imp: { - ext: { - tid: 'trans_abcd1234', - } - }, -}]; - -const BID_RESPONSE_NATIVE = { - body: { - cpm: 3, - width: 300, - height: 250, - creativeId: 'crea_sw_1', - currency: 'EUR', - isNetCpm: true, - ttl: 300, - ad: '{"link":{"url":"https://www.smilewanted.com"},"assets":[{"id":0,"required":1,"title":{"len":50}},{"id":1,"required":1,"img":{"type":3,"w":150,"h":50,"ext":{"aspectratios":["2:1"]}}},{"id":2,"required":0,"img":{"type":1,"w":50,"h":50,"ext":{"aspectratios":["2:1"]}}},{"id":3,"required":1,"data":{"type":1,"value":"Smilewanted sponsor"}},{"id":4,"required":1,"data":{"type":2,"value":"Smilewanted Description"}}]}', - cSyncUrl: 'https://csync.smilewanted.com', - formatTypeSw: 'native' - } -}; - // Default params with optional ones describe('smilewantedBidAdapterTests', function () { it('SmileWanted - Verify build request', function () { @@ -305,23 +195,6 @@ describe('smilewantedBidAdapterTests', function () { expect(requestVideoInstreamContent.sizes[0]).to.have.property('w').and.to.equal(640); expect(requestVideoInstreamContent.sizes[0]).to.have.property('h').and.to.equal(480); expect(requestVideoInstreamContent).to.have.property('transactionId').and.to.not.equal(null).and.to.not.be.undefined; - expect(requestVideoInstreamContent).to.have.property('videoParams'); - expect(requestVideoInstreamContent.videoParams).to.have.property('context').and.to.equal('instream').and.to.not.be.undefined; - expect(requestVideoInstreamContent.videoParams).to.have.property('mimes').to.be.an('array').that.include('video/mp4').and.to.not.be.undefined; - expect(requestVideoInstreamContent.videoParams).to.have.property('minduration').and.to.equal(0).and.to.not.be.undefined; - expect(requestVideoInstreamContent.videoParams).to.have.property('maxduration').and.to.equal(120).and.to.not.be.undefined; - expect(requestVideoInstreamContent.videoParams).to.have.property('protocols').to.be.an('array').that.include.members([1, 2, 3, 4, 5, 6, 7, 8]).and.to.not.be.undefined; - expect(requestVideoInstreamContent.videoParams).to.have.property('startdelay').and.to.equal(0).and.to.not.be.undefined; - expect(requestVideoInstreamContent.videoParams).to.have.property('placement').and.to.equal(1).and.to.not.be.undefined; - expect(requestVideoInstreamContent.videoParams).to.have.property('skip').and.to.equal(1).and.to.not.be.undefined; - expect(requestVideoInstreamContent.videoParams).to.have.property('skipafter').and.to.equal(10).and.to.not.be.undefined; - expect(requestVideoInstreamContent.videoParams).to.have.property('minbitrate').and.to.equal(10).and.to.not.be.undefined; - expect(requestVideoInstreamContent.videoParams).to.have.property('maxbitrate').and.to.equal(10).and.to.not.be.undefined; - expect(requestVideoInstreamContent.videoParams).to.have.property('delivery').to.be.an('array').that.include(1).and.to.not.be.undefined; - expect(requestVideoInstreamContent.videoParams).to.have.property('playbackmethod').to.be.an('array').that.include(2).and.to.not.be.undefined; - expect(requestVideoInstreamContent.videoParams).to.have.property('api').to.be.an('array').that.include.members([1, 2]).and.to.not.be.undefined; - expect(requestVideoInstreamContent.videoParams).to.have.property('linearity').and.to.equal(1).and.to.not.be.undefined; - expect(requestVideoInstreamContent.videoParams).to.have.property('playerSize').to.be.an('array').that.include.members([640, 480]).and.to.not.be.undefined; const requestVideoOutstream = spec.buildRequests(VIDEO_OUTSTREAM_REQUEST); expect(requestVideoOutstream[0]).to.have.property('url').and.to.equal('https://prebid.smilewanted.com'); @@ -333,39 +206,6 @@ describe('smilewantedBidAdapterTests', function () { expect(requestVideoOutstreamContent.sizes[0]).to.have.property('w').and.to.equal(640); expect(requestVideoOutstreamContent.sizes[0]).to.have.property('h').and.to.equal(480); expect(requestVideoOutstreamContent).to.have.property('transactionId').and.to.not.equal(null).and.to.not.be.undefined; - - const requestNative = spec.buildRequests(NATIVE_REQUEST); - expect(requestNative[0]).to.have.property('url').and.to.equal('https://prebid.smilewanted.com'); - expect(requestNative[0]).to.have.property('method').and.to.equal('POST'); - const requestNativeContent = JSON.parse(requestNative[0].data); - expect(requestNativeContent).to.have.property('zoneId').and.to.equal(4); - expect(requestNativeContent).to.have.property('currencyCode').and.to.equal('EUR'); - expect(requestNativeContent).to.have.property('sizes'); - expect(requestNativeContent.sizes[0]).to.have.property('w').and.to.equal(300); - expect(requestNativeContent.sizes[0]).to.have.property('h').and.to.equal(250); - expect(requestNativeContent).to.have.property('transactionId').and.to.not.equal(null).and.to.not.be.undefined; - expect(requestNativeContent).to.have.property('context').and.to.equal('native').and.to.not.be.undefined; - expect(requestNativeContent).to.have.property('nativeParams'); - expect(requestNativeContent.nativeParams.title).to.have.property('required').and.to.equal(true); - expect(requestNativeContent.nativeParams.title).to.have.property('len').and.to.equal(140); - expect(requestNativeContent.nativeParams.image).to.have.property('required').and.to.equal(true); - expect(requestNativeContent.nativeParams.image).to.have.property('sizes').to.be.an('array').that.include.members([300, 250]).and.to.not.be.undefined; - expect(requestNativeContent.nativeParams.icon).to.have.property('required').and.to.equal(false); - expect(requestNativeContent.nativeParams.icon).to.have.property('sizes').to.be.an('array').that.include.members([50, 50]).and.to.not.be.undefined; - expect(requestNativeContent.nativeParams.sponsoredBy).to.have.property('required').and.to.equal(true); - expect(requestNativeContent.nativeParams.body).to.have.property('required').and.to.equal(true); - expect(requestNativeContent.nativeParams.clickUrl).to.have.property('required').and.to.equal(false); - expect(requestNativeContent.nativeParams.privacyLink).to.have.property('required').and.to.equal(false); - expect(requestNativeContent.nativeParams.cta).to.have.property('required').and.to.equal(false); - expect(requestNativeContent.nativeParams.rating).to.have.property('required').and.to.equal(false); - expect(requestNativeContent.nativeParams.likes).to.have.property('required').and.to.equal(false); - expect(requestNativeContent.nativeParams.downloads).to.have.property('required').and.to.equal(false); - expect(requestNativeContent.nativeParams.price).to.have.property('required').and.to.equal(false); - expect(requestNativeContent.nativeParams.salePrice).to.have.property('required').and.to.equal(false); - expect(requestNativeContent.nativeParams.phone).to.have.property('required').and.to.equal(false); - expect(requestNativeContent.nativeParams.address).to.have.property('required').and.to.equal(false); - expect(requestNativeContent.nativeParams.desc2).to.have.property('required').and.to.equal(false); - expect(requestNativeContent.nativeParams.displayUrl).to.have.property('required').and.to.equal(false); }); it('SmileWanted - Verify build request with referrer', function () { @@ -497,7 +337,7 @@ describe('smilewantedBidAdapterTests', function () { }).to.not.throw(); }); - it('SmileWanted - Verify parse response - Video Outstream', function () { + it('SmileWanted - Verify parse response - Video Oustream', function () { const request = spec.buildRequests(VIDEO_OUTSTREAM_REQUEST); const bids = spec.interpretResponse(BID_RESPONSE_VIDEO_OUTSTREAM, request[0]); expect(bids).to.have.lengthOf(1); @@ -520,28 +360,6 @@ describe('smilewantedBidAdapterTests', function () { }).to.not.throw(); }); - it('SmileWanted - Verify parse response - Native', function () { - const request = spec.buildRequests(NATIVE_REQUEST); - const bids = spec.interpretResponse(BID_RESPONSE_NATIVE, request[0]); - expect(bids).to.have.lengthOf(1); - const bid = bids[0]; - expect(bid.cpm).to.equal(3); - expect(bid.ad).to.equal('{"link":{"url":"https://www.smilewanted.com"},"assets":[{"id":0,"required":1,"title":{"len":50}},{"id":1,"required":1,"img":{"type":3,"w":150,"h":50,"ext":{"aspectratios":["2:1"]}}},{"id":2,"required":0,"img":{"type":1,"w":50,"h":50,"ext":{"aspectratios":["2:1"]}}},{"id":3,"required":1,"data":{"type":1,"value":"Smilewanted sponsor"}},{"id":4,"required":1,"data":{"type":2,"value":"Smilewanted Description"}}]}'); - expect(bid.width).to.equal(300); - expect(bid.height).to.equal(250); - expect(bid.creativeId).to.equal('crea_sw_1'); - expect(bid.currency).to.equal('EUR'); - expect(bid.netRevenue).to.equal(true); - expect(bid.ttl).to.equal(300); - expect(bid.requestId).to.equal(NATIVE_REQUEST[0].bidId); - - expect(function () { - spec.interpretResponse(BID_RESPONSE_NATIVE, { - data: 'invalid Json' - }) - }).to.not.throw(); - }); - it('SmileWanted - Verify bidder code', function () { expect(spec.code).to.equal('smilewanted'); }); diff --git a/test/spec/modules/snigelBidAdapter_spec.js b/test/spec/modules/snigelBidAdapter_spec.js index 3ba84228872..3fc09493f03 100644 --- a/test/spec/modules/snigelBidAdapter_spec.js +++ b/test/spec/modules/snigelBidAdapter_spec.js @@ -20,10 +20,8 @@ const makeBidRequest = function (overrides) { }; const BASE_BIDDER_REQUEST = { - auctionId: 'test', bidderRequestId: 'test', refererInfo: { - page: 'https://localhost', canonicalUrl: 'https://localhost', }, }; @@ -55,8 +53,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', params: {placement: 'top_leaderboard'}}), + makeBidRequest({bidId: 'b', params: {placement: 'bottom_leaderboard'}}), ]; const request = spec.buildRequests(bidRequests, bidderRequest); @@ -72,9 +70,9 @@ describe('snigelBidAdapter', function () { expect(data).to.have.property('page').and.to.equal('https://localhost'); expect(data).to.have.property('placements'); expect(data.placements.length).to.equal(2); - expect(data.placements[0].id).to.equal('au_a'); + expect(data.placements[0].uuid).to.equal('a'); expect(data.placements[0].name).to.equal('top_leaderboard'); - expect(data.placements[1].id).to.equal('au_b'); + expect(data.placements[1].uuid).to.equal('b'); expect(data.placements[1].name).to.equal('bottom_leaderboard'); }); @@ -129,56 +127,6 @@ describe('snigelBidAdapter', function () { const data = JSON.parse(request.data); expect(data).to.have.property('coppa').and.to.equal(true); }); - - 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'}); - - // first auction, no refresh - let request = spec.buildRequests([topLeaderboard, bottomLeaderboard], bidderRequest); - expect(request).to.have.property('data'); - let data = JSON.parse(request.data); - expect(data).to.have.property('placements'); - expect(data.placements.length).to.equal(2); - expect(data.placements[0].id).to.equal('top_leaderboard'); - expect(data.placements[0].refresh).to.be.undefined; - expect(data.placements[1].id).to.equal('bottom_leaderboard'); - expect(data.placements[1].refresh).to.be.undefined; - - // second auction for top leaderboard, was refreshed - request = spec.buildRequests([topLeaderboard, sidebar], bidderRequest); - expect(request).to.have.property('data'); - data = JSON.parse(request.data); - expect(data).to.have.property('placements'); - expect(data.placements.length).to.equal(2); - expect(data.placements[0].id).to.equal('top_leaderboard'); - expect(data.placements[0].refresh).to.not.be.undefined; - expect(data.placements[0].refresh.count).to.equal(1); - expect(data.placements[0].refresh.time).to.be.greaterThanOrEqual(0); - expect(data.placements[1].id).to.equal('sidebar'); - expect(data.placements[1].refresh).to.be.undefined; - - // third auction, all units refreshed at some point - request = spec.buildRequests([topLeaderboard, bottomLeaderboard, sidebar], bidderRequest); - expect(request).to.have.property('data'); - data = JSON.parse(request.data); - expect(data).to.have.property('placements'); - expect(data.placements.length).to.equal(3); - expect(data.placements[0].id).to.equal('top_leaderboard'); - expect(data.placements[0].refresh).to.not.be.undefined; - expect(data.placements[0].refresh.count).to.equal(2); - expect(data.placements[0].refresh.time).to.be.greaterThanOrEqual(0); - expect(data.placements[1].id).to.equal('bottom_leaderboard'); - expect(data.placements[1].refresh).to.not.be.undefined; - expect(data.placements[1].refresh.count).to.equal(1); - expect(data.placements[1].refresh.time).to.be.greaterThanOrEqual(0); - expect(data.placements[2].id).to.equal('sidebar'); - expect(data.placements[2].refresh).to.not.be.undefined; - expect(data.placements[2].refresh.count).to.equal(1); - expect(data.placements[2].refresh.time).to.be.greaterThanOrEqual(0); - }); }); describe('interpretResponse', function () { @@ -198,7 +146,7 @@ describe('snigelBidAdapter', function () { cur: 'USD', bids: [ { - id: BASE_BID_REQUEST.adUnitCode, + uuid: BASE_BID_REQUEST.bidId, price: 0.0575, ad: '

Test Ad

', width: 728, @@ -212,7 +160,7 @@ describe('snigelBidAdapter', function () { }, }; - const bids = spec.interpretResponse(serverResponse, {bidderRequest: {bids: [BASE_BID_REQUEST]}}); + const bids = spec.interpretResponse(serverResponse, {}); expect(bids.length).to.equal(1); const bid = bids[0]; expect(isValid(BASE_BID_REQUEST.adUnitCode, bid)).to.be.true; diff --git a/test/spec/modules/sonobiAnalyticsAdapter_spec.js b/test/spec/modules/sonobiAnalyticsAdapter_spec.js index ed8ccd22eea..76ff88836d4 100644 --- a/test/spec/modules/sonobiAnalyticsAdapter_spec.js +++ b/test/spec/modules/sonobiAnalyticsAdapter_spec.js @@ -1,4 +1,4 @@ -import sonobiAnalytics, {DEFAULT_EVENT_URL} from 'modules/sonobiAnalyticsAdapter.js'; +import sonobiAnalytics from 'modules/sonobiAnalyticsAdapter.js'; import {expect} from 'chai'; import {server} from 'test/mocks/xhr.js'; let events = require('src/events'); @@ -76,8 +76,8 @@ describe('Sonobi Prebid Analytic', function () { events.emit(constants.EVENTS.AUCTION_END, {auctionId: '13', bidsReceived: [bid]}); clock.tick(5000); - const req = server.requests.find(req => req.url.indexOf(DEFAULT_EVENT_URL) !== -1); - expect(JSON.parse(req.requestBody)).to.have.length(3) + expect(server.requests).to.have.length(1); + expect(JSON.parse(server.requests[0].requestBody)).to.have.length(3) done(); }); }); diff --git a/test/spec/modules/sonobiBidAdapter_spec.js b/test/spec/modules/sonobiBidAdapter_spec.js index 83db7c0a812..b9bd0dc4d9f 100644 --- a/test/spec/modules/sonobiBidAdapter_spec.js +++ b/test/spec/modules/sonobiBidAdapter_spec.js @@ -1,9 +1,9 @@ -import { expect } from 'chai'; -import { _getPlatform, spec } from 'modules/sonobiBidAdapter.js'; -import { newBidder } from 'src/adapters/bidderFactory.js'; +import { expect } from 'chai' +import { spec, _getPlatform } from 'modules/sonobiBidAdapter.js' +import { newBidder } from 'src/adapters/bidderFactory.js' import { userSync } from '../../../src/userSync.js'; import { config } from 'src/config.js'; -import * as gptUtils from '../../../libraries/gptUtils/gptUtils.js'; +import * as utils from '../../../src/utils.js'; describe('SonobiBidAdapter', function () { const adapter = newBidder(spec) @@ -193,7 +193,7 @@ describe('SonobiBidAdapter', function () { }); describe('outstream', () => { - it('should return false if there is no playerSize', () => { + it('should return false if there is no param sizes', () => { const bid = { 'bidder': 'sonobi', 'adUnitCode': 'adunit-code', @@ -203,6 +203,7 @@ describe('SonobiBidAdapter', function () { 'mediaTypes': { video: { context: 'outstream', + playerSize: [300, 250] } }, 'bidId': '30b31c1838de1e', @@ -212,7 +213,7 @@ describe('SonobiBidAdapter', function () { expect(spec.isBidRequestValid(bid)).to.equal(false); }); - it('should return true if there is playerSize', () => { + it('should return true if there is param sizes', () => { const bid = { 'bidder': 'sonobi', 'adUnitCode': 'adunit-code', @@ -223,8 +224,7 @@ describe('SonobiBidAdapter', function () { }, 'mediaTypes': { video: { - context: 'outstream', - playerSize: [640, 480] + context: 'outstream' } }, 'bidId': '30b31c1838de1e', @@ -248,13 +248,13 @@ describe('SonobiBidAdapter', function () { let sandbox; beforeEach(function () { sinon.stub(userSync, 'canBidderRegisterSync'); - sinon.stub(gptUtils, 'getGptSlotInfoForAdUnitCode') + sinon.stub(utils, 'getGptSlotInfoForAdUnitCode') .onFirstCall().returns({ gptSlot: '/123123/gpt_publisher/adunit-code-3', divId: 'adunit-code-3-div-id' }); sandbox = sinon.createSandbox(); }); afterEach(function () { userSync.canBidderRegisterSync.restore(); - gptUtils.getGptSlotInfoForAdUnitCode.restore(); + utils.getGptSlotInfoForAdUnitCode.restore(); sandbox.restore(); }); let bidRequest = [{ @@ -294,11 +294,8 @@ describe('SonobiBidAdapter', function () { }, mediaTypes: { video: { - playerSize: [640, 480], - context: 'outstream', - playbackmethod: [1, 2, 3], - plcmt: 3, - placement: 2 + sizes: [[300, 250], [300, 600]], + context: 'outstream' } } }, @@ -342,7 +339,7 @@ describe('SonobiBidAdapter', function () { }]; let keyMakerData = { - '30b31c1838de1f': '1a2b3c4d5e6f1a2b3c4d|640x480|f=1.25,gpid=/123123/gpt_publisher/adunit-code-1,c=v,pm=1:2:3,p=2,pl=3,', + '30b31c1838de1f': '1a2b3c4d5e6f1a2b3c4d|300x250,300x600|f=1.25,gpid=/123123/gpt_publisher/adunit-code-1,c=v,', '30b31c1838de1d': '1a2b3c4d5e6f1a2b3c4e|300x250,300x600|f=0.42,gpid=/123123/gpt_publisher/adunit-code-3,c=d,', '/7780971/sparks_prebid_LB|30b31c1838de1e': '300x250,300x600|gpid=/7780971/sparks_prebid_LB,c=d,', }; @@ -359,9 +356,7 @@ describe('SonobiBidAdapter', function () { 'page': 'https://example.com', 'stack': ['https://example.com'] }, - uspConsent: 'someCCPAString', - ortb2: {} - + uspConsent: 'someCCPAString' }; it('should set fpd if there is any data in ortb2', function () { @@ -495,14 +490,6 @@ describe('SonobiBidAdapter', function () { expect(bidRequests.data.hfa).to.equal('hfakey') }) - it('should return a properly formatted request with expData and expKey', function () { - bidderRequests.ortb2.experianRtidData = 'IkhlbGxvLCB3b3JsZC4gSGVsbG8sIHdvcmxkLiBIZWxsbywgd29ybGQuIg=='; - bidderRequests.ortb2.experianRtidKey = 'sovrn-encryption-key-1'; - const bidRequests = spec.buildRequests(bidRequest, bidderRequests) - expect(bidRequests.data.expData).to.equal('IkhlbGxvLCB3b3JsZC4gSGVsbG8sIHdvcmxkLiBIZWxsbywgd29ybGQuIg=='); - expect(bidRequests.data.expKey).to.equal('sovrn-encryption-key-1'); - }) - it('should return null if there is nothing to bid on', function () { const bidRequests = spec.buildRequests([{ params: {} }], bidderRequests) expect(bidRequests).to.equal(null); @@ -648,12 +635,12 @@ describe('SonobiBidAdapter', function () { bidder: 'sonobi', mediaTypes: { video: { - context: 'outstream', - playerSize: [640, 480] + context: 'outstream' } }, params: { - placement_id: '92e95368e86639dbd86d' + placement_id: '92e95368e86639dbd86d', + sizes: [[640, 480]] } } ] @@ -671,7 +658,7 @@ describe('SonobiBidAdapter', function () { 'sbi_adomain': 'sonobi.com' }, '30b31c1838de1e': { - 'sbi_size': '640x480', + 'sbi_size': '300x250', 'sbi_apoc': 'remnant', 'sbi_aid': '30292e432662bd5f86d90774b944b038', 'sbi_mouse': 1.25, @@ -681,7 +668,7 @@ describe('SonobiBidAdapter', function () { }, '/7780971/sparks_prebid_LB_OUTSTREAM|30b31c1838de1g': { - 'sbi_size': '640x480', + 'sbi_size': '300x600', 'sbi_apoc': 'remnant', 'sbi_crid': '1234abcd', 'sbi_aid': '30292e432662bd5f86d90774b944b038', @@ -733,8 +720,8 @@ describe('SonobiBidAdapter', function () { { 'requestId': '30b31c1838de1e', 'cpm': 1.25, - 'width': 640, - 'height': 480, + 'width': 300, + 'height': 250, 'vastUrl': 'https://mco-1-apex.go.sonobi.com/vast.xml?vid=30292e432662bd5f86d90774b944b038&ref=https%3A%2F%2Flocalhost%2F', 'ttl': 500, 'creativeId': '30292e432662bd5f86d90774b944b038', @@ -750,8 +737,8 @@ describe('SonobiBidAdapter', function () { { 'requestId': '30b31c1838de1g', 'cpm': 1.07, - 'width': 640, - 'height': 480, + 'width': 300, + 'height': 600, 'ad': ``, 'ttl': 500, 'creativeId': '1234abcd', diff --git a/test/spec/modules/sovrnAnalyticsAdapter_spec.js b/test/spec/modules/sovrnAnalyticsAdapter_spec.js index 973e90abd5a..68552eb3d8a 100644 --- a/test/spec/modules/sovrnAnalyticsAdapter_spec.js +++ b/test/spec/modules/sovrnAnalyticsAdapter_spec.js @@ -12,7 +12,7 @@ let constants = require('src/constants.json'); /** * Emit analytics events - * @param {Array} eventArr - array of objects to define the events that will fire + * @param {array} eventArr - array of objects to define the events that will fire * @param {object} eventObj - key is eventType, value is event * @param {string} auctionId - the auction id to attached to the events */ diff --git a/test/spec/modules/sovrnBidAdapter_spec.js b/test/spec/modules/sovrnBidAdapter_spec.js index f165a6da6d1..c84013d1963 100644 --- a/test/spec/modules/sovrnBidAdapter_spec.js +++ b/test/spec/modules/sovrnBidAdapter_spec.js @@ -64,29 +64,6 @@ describe('sovrnBidAdapter', function() { expect(spec.isBidRequestValid(bidRequest)).to.equal(false) }) - - it('should return true when minduration is not passed', function() { - const width = 300 - const height = 250 - const mimes = ['video/mp4', 'application/javascript'] - const protocols = [2, 5] - const maxduration = 60 - const startdelay = 0 - const videoBidRequest = { - ...baseBidRequest, - mediaTypes: { - video: { - mimes, - protocols, - playerSize: [[width, height], [360, 240]], - maxduration, - startdelay - } - } - } - - expect(spec.isBidRequestValid(videoBidRequest)).to.equal(true) - }) }) describe('buildRequests', function () { @@ -206,40 +183,6 @@ describe('sovrnBidAdapter', function() { expect(payload.tmax).to.equal(3000) }) - it('forwards auction level tid', function() { - const bidderRequest = { - ...baseBidderRequest, - ortb2: { - source: { - tid: '1d1a030790a475' - } - }, - bids: [baseBidRequest] - } - - const payload = JSON.parse(spec.buildRequests([baseBidRequest], bidderRequest).data) - expect(payload.source?.tid).to.equal('1d1a030790a475') - }) - - it('forwards impression level tid', function() { - const bidRequest = { - ...baseBidRequest, - ortb2Imp: { - ext: { - tid: '1a2c032473f4983' - } - }, - } - - const bidderRequest = { - ...baseBidderRequest, - bids: [bidRequest] - } - - const payload = JSON.parse(spec.buildRequests([bidRequest], bidderRequest).data) - expect(payload.imp[0]?.ext?.tid).to.equal('1a2c032473f4983') - }) - it('includes the ad unit code in the request', function() { const impression = payload.imp[0] expect(impression.adunitcode).to.equal('adunit-code') @@ -318,41 +261,6 @@ describe('sovrnBidAdapter', function() { expect(data.regs.ext['us_privacy']).to.equal(bidderRequest.uspConsent) }) - it('should not set coppa when coppa is undefined', function () { - const bidderRequest = { - ...baseBidderRequest, - bidderCode: 'sovrn', - auctionId: '1d1a030790a475', - bidderRequestId: '22edbae2733bf6', - timeout: 3000, - bids: [baseBidRequest], - gdprConsent: { - consentString: 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A==', - gdprApplies: true - }, - } - const {regs} = JSON.parse(spec.buildRequests([baseBidRequest], bidderRequest).data) - expect(regs.coppa).to.be.undefined - }) - - it('should set coppa to 1 when coppa is provided with value true', function () { - const bidderRequest = { - ...baseBidderRequest, - ortb2: { - regs: { - coppa: true - } - }, - bidderCode: 'sovrn', - auctionId: '1d1a030790a475', - bidderRequestId: '22edbae2733bf6', - timeout: 3000, - bids: [baseBidRequest] - } - const {regs} = JSON.parse(spec.buildRequests([baseBidRequest], bidderRequest).data) - expect(regs.coppa).to.equal(1) - }) - it('should send gpp info in OpenRTB 2.6 location when gppConsent defined', function () { const bidderRequest = { ...baseBidderRequest, @@ -909,7 +817,7 @@ describe('sovrnBidAdapter', function() { url: `https://ap.lijit.com/beacon?gdpr_consent=${gdprConsent.consentString}&informer=13487408`, } - const returnStatement = spec.getUserSyncs(syncOptions, serverResponse, gdprConsent, '', null) + const returnStatement = spec.getUserSyncs(syncOptions, serverResponse, gdprConsent, '') expect(returnStatement[0]).to.deep.equal(expectedReturnStatement) }) @@ -921,22 +829,7 @@ describe('sovrnBidAdapter', function() { url: `https://ap.lijit.com/beacon?us_privacy=${uspString}&informer=13487408`, } - const returnStatement = spec.getUserSyncs(syncOptions, serverResponse, null, uspString, null) - - expect(returnStatement[0]).to.deep.equal(expectedReturnStatement) - }) - - it('should include gpp consent string if present', function() { - const gppConsent = { - applicableSections: [1, 2], - gppString: 'DBACNYA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA~1YNN' - } - const expectedReturnStatement = { - type: 'iframe', - url: `https://ap.lijit.com/beacon?gpp=${gppConsent.gppString}&gpp_sid=${gppConsent.applicableSections}&informer=13487408`, - } - - const returnStatement = spec.getUserSyncs(syncOptions, serverResponse, null, '', gppConsent) + const returnStatement = spec.getUserSyncs(syncOptions, serverResponse, null, uspString) expect(returnStatement[0]).to.deep.equal(expectedReturnStatement) }) @@ -947,17 +840,12 @@ describe('sovrnBidAdapter', function() { consentString: 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A==' } const uspString = '1NYN' - const gppConsent = { - applicableSections: [1, 2], - gppString: 'DBACNYA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA~1YNN' - } - const expectedReturnStatement = { type: 'iframe', - url: `https://ap.lijit.com/beacon?gdpr_consent=${gdprConsent.consentString}&us_privacy=${uspString}&gpp=${gppConsent.gppString}&gpp_sid=${gppConsent.applicableSections}&informer=13487408`, + url: `https://ap.lijit.com/beacon?gdpr_consent=${gdprConsent.consentString}&us_privacy=${uspString}&informer=13487408`, } - const returnStatement = spec.getUserSyncs(syncOptions, serverResponse, gdprConsent, uspString, gppConsent) + const returnStatement = spec.getUserSyncs(syncOptions, serverResponse, gdprConsent, uspString) expect(returnStatement[0]).to.deep.equal(expectedReturnStatement) }) diff --git a/test/spec/modules/sparteoBidAdapter_spec.js b/test/spec/modules/sparteoBidAdapter_spec.js deleted file mode 100644 index 293f7da30a1..00000000000 --- a/test/spec/modules/sparteoBidAdapter_spec.js +++ /dev/null @@ -1,467 +0,0 @@ -import {expect} from 'chai'; -import { deepClone, mergeDeep } from 'src/utils'; -import {spec as adapter} from 'modules/sparteoBidAdapter'; - -const CURRENCY = 'EUR'; -const TTL = 60; -const HTTP_METHOD = 'POST'; -const REQUEST_URL = 'https://bid.sparteo.com/auction'; -const USER_SYNC_URL_IFRAME = 'https://sync.sparteo.com/sync/iframe.html?from=prebidjs'; - -const VALID_BID_BANNER = { - bidder: 'sparteo', - bidId: '1a2b3c4d', - adUnitCode: 'id-1234', - params: { - networkId: '1234567a-eb1b-1fae-1d23-e1fbaef234cf', - formats: ['corner'] - }, - mediaTypes: { - banner: { - sizes: [ - [1, 1] - ] - } - } -}; - -const VALID_BID_VIDEO = { - bidder: 'sparteo', - bidId: '5e6f7g8h', - adUnitCode: 'id-5678', - params: { - networkId: '1234567a-eb1b-1fae-1d23-e1fbaef234cf' - }, - mediaTypes: { - video: { - playerSize: [640, 360], - protocols: [1, 2, 3, 4, 5, 6, 7, 8], - api: [1, 2], - mimes: ['video/mp4'], - skip: 1, - startdelay: 0, - placement: 1, - linearity: 1, - minduration: 5, - maxduration: 30, - context: 'instream' - } - }, - ortb2Imp: { - ext: { - pbadslot: 'video' - } - } -}; - -const VALID_REQUEST_BANNER = { - method: HTTP_METHOD, - url: REQUEST_URL, - data: { - 'imp': [{ - 'id': '1a2b3c4d', - 'banner': { - 'format': [{ - 'h': 1, - 'w': 1 - }], - 'topframe': 0 - }, - 'ext': { - 'sparteo': { - 'params': { - 'networkId': '1234567a-eb1b-1fae-1d23-e1fbaef234cf', - 'formats': ['corner'] - } - } - } - }], - 'site': { - 'publisher': { - 'ext': { - 'params': { - 'networkId': '1234567a-eb1b-1fae-1d23-e1fbaef234cf' - } - } - } - }, - 'test': 0 - } -}; - -const VALID_REQUEST_VIDEO = { - method: HTTP_METHOD, - url: REQUEST_URL, - data: { - 'imp': [{ - 'id': '5e6f7g8h', - 'video': { - 'w': 640, - 'h': 360, - 'protocols': [1, 2, 3, 4, 5, 6, 7, 8], - 'api': [1, 2], - 'mimes': ['video/mp4'], - 'skip': 1, - 'startdelay': 0, - 'placement': 1, - 'linearity': 1, - 'minduration': 5, - 'maxduration': 30, - }, - 'ext': { - 'pbadslot': 'video', - 'sparteo': { - 'params': { - 'networkId': '1234567a-eb1b-1fae-1d23-e1fbaef234cf' - } - } - } - }], - 'site': { - 'publisher': { - 'ext': { - 'params': { - 'networkId': '1234567a-eb1b-1fae-1d23-e1fbaef234cf' - } - } - } - }, - 'test': 0 - } -}; - -const VALID_REQUEST = { - method: HTTP_METHOD, - url: REQUEST_URL, - data: { - 'imp': [{ - 'id': '1a2b3c4d', - 'banner': { - 'format': [{ - 'h': 1, - 'w': 1 - }], - 'topframe': 0 - }, - 'ext': { - 'sparteo': { - 'params': { - 'networkId': '1234567a-eb1b-1fae-1d23-e1fbaef234cf', - 'formats': ['corner'] - } - } - } - }, { - 'id': '5e6f7g8h', - 'video': { - 'w': 640, - 'h': 360, - 'protocols': [1, 2, 3, 4, 5, 6, 7, 8], - 'api': [1, 2], - 'mimes': ['video/mp4'], - 'skip': 1, - 'startdelay': 0, - 'placement': 1, - 'linearity': 1, - 'minduration': 5, - 'maxduration': 30, - }, - 'ext': { - 'pbadslot': 'video', - 'sparteo': { - 'params': { - 'networkId': '1234567a-eb1b-1fae-1d23-e1fbaef234cf' - } - } - } - }], - 'site': { - 'publisher': { - 'ext': { - 'params': { - 'networkId': '1234567a-eb1b-1fae-1d23-e1fbaef234cf' - } - } - } - }, - 'test': 0 - } -}; - -const BIDDER_REQUEST = { - bids: [VALID_BID_BANNER, VALID_BID_VIDEO] -} - -const BIDDER_REQUEST_BANNER = { - bids: [VALID_BID_BANNER] -} - -const BIDDER_REQUEST_VIDEO = { - bids: [VALID_BID_VIDEO] -} - -describe('SparteoAdapter', function () { - describe('isBidRequestValid', function () { - describe('Check method return', function () { - it('should return true', function () { - expect(adapter.isBidRequestValid(VALID_BID_BANNER)).to.equal(true); - expect(adapter.isBidRequestValid(VALID_BID_VIDEO)).to.equal(true); - }); - - it('should return false because the networkId is missing', function () { - let wrongBid = deepClone(VALID_BID_BANNER); - delete wrongBid.params.networkId; - - expect(adapter.isBidRequestValid(wrongBid)).to.equal(false); - }); - - it('should return false because the banner size is missing', function () { - let wrongBid = deepClone(VALID_BID_BANNER); - - wrongBid.mediaTypes.banner.sizes = '123456'; - expect(adapter.isBidRequestValid(wrongBid)).to.equal(false); - - delete wrongBid.mediaTypes.banner.sizes; - expect(adapter.isBidRequestValid(wrongBid)).to.equal(false); - }); - - it('should return false because the video player size paramater is missing', function () { - let wrongBid = deepClone(VALID_BID_VIDEO); - - wrongBid.mediaTypes.video.playerSize = '123456'; - expect(adapter.isBidRequestValid(wrongBid)).to.equal(false); - - delete wrongBid.mediaTypes.video.playerSize; - expect(adapter.isBidRequestValid(wrongBid)).to.equal(false); - }); - }); - }); - - describe('buildRequests', function () { - describe('Check method return', function () { - if (FEATURES.VIDEO) { - it('should return the right formatted requests', function() { - const request = adapter.buildRequests([VALID_BID_BANNER, VALID_BID_VIDEO], BIDDER_REQUEST); - delete request.data.id; - - expect(request).to.deep.equal(VALID_REQUEST); - }); - } - - it('should return the right formatted banner requests', function() { - const request = adapter.buildRequests([VALID_BID_BANNER], BIDDER_REQUEST_BANNER); - delete request.data.id; - - expect(request).to.deep.equal(VALID_REQUEST_BANNER); - }); - - if (FEATURES.VIDEO) { - it('should return the right formatted video requests', function() { - const request = adapter.buildRequests([VALID_BID_VIDEO], BIDDER_REQUEST_VIDEO); - delete request.data.id; - - expect(request).to.deep.equal(VALID_REQUEST_VIDEO); - }); - } - - it('should return the right formatted request with endpoint test', function() { - let endpoint = 'https://bid-test.sparteo.com/auction'; - - let bids = mergeDeep(deepClone([VALID_BID_BANNER, VALID_BID_VIDEO]), { - params: { - endpoint: endpoint - } - }); - - let requests = mergeDeep(deepClone(VALID_REQUEST)); - - const request = adapter.buildRequests(bids, BIDDER_REQUEST); - requests.url = endpoint; - delete request.data.id; - - expect(requests).to.deep.equal(requests); - }); - }); - }); - - describe('interpretResponse', function() { - describe('Check method return', function () { - it('should return the right formatted response', function() { - let response = { - body: { - 'id': '63f4d300-6896-4bdc-8561-0932f73148b1', - 'cur': 'EUR', - 'seatbid': [ - { - 'seat': 'sparteo', - 'group': 0, - 'bid': [ - { - 'id': 'cdbb6982-a269-40c7-84e5-04797f11d87a', - 'impid': '1a2b3c4d', - 'price': 4.5, - 'ext': { - 'prebid': { - 'type': 'banner' - } - }, - 'adm': 'script', - 'crid': 'crid', - 'w': 1, - 'h': 1, - 'nurl': 'https://t.bidder.sparteo.com/img' - } - ] - } - ] - } - }; - - if (FEATURES.VIDEO) { - response.body.seatbid[0].bid.push({ - 'id': 'cdbb6982-a269-40c7-84e5-04797f11d87b', - 'impid': '5e6f7g8h', - 'price': 5, - 'ext': { - 'prebid': { - 'type': 'video', - 'cache': { - 'vastXml': { - 'url': 'https://pbs.tet.com/cache?uuid=1234' - } - } - } - }, - 'adm': 'tag', - 'crid': 'crid', - 'w': 640, - 'h': 480, - 'nurl': 'https://t.bidder.sparteo.com/img' - }); - } - - let formattedReponse = [ - { - requestId: '1a2b3c4d', - seatBidId: 'cdbb6982-a269-40c7-84e5-04797f11d87a', - cpm: 4.5, - width: 1, - height: 1, - creativeId: 'crid', - creative_id: 'crid', - currency: CURRENCY, - netRevenue: true, - ttl: TTL, - mediaType: 'banner', - meta: {}, - ad: 'script
' - } - ]; - - if (FEATURES.VIDEO) { - formattedReponse.push({ - requestId: '5e6f7g8h', - seatBidId: 'cdbb6982-a269-40c7-84e5-04797f11d87b', - cpm: 5, - width: 640, - height: 480, - playerWidth: 640, - playerHeight: 360, - creativeId: 'crid', - creative_id: 'crid', - currency: CURRENCY, - netRevenue: true, - ttl: TTL, - mediaType: 'video', - meta: {}, - nurl: 'https://t.bidder.sparteo.com/img', - vastUrl: 'https://pbs.tet.com/cache?uuid=1234', - vastXml: 'tag' - }); - } - - if (FEATURES.VIDEO) { - const request = adapter.buildRequests([VALID_BID_BANNER, VALID_BID_VIDEO], BIDDER_REQUEST); - expect(adapter.interpretResponse(response, request)).to.deep.equal(formattedReponse); - } else { - const request = adapter.buildRequests([VALID_BID_BANNER], BIDDER_REQUEST_BANNER); - expect(adapter.interpretResponse(response, request)).to.deep.equal(formattedReponse); - } - }); - }); - }); - - describe('onBidWon', function() { - describe('Check methods succeed', function () { - it('should not throw error', function() { - let bids = [ - { - requestId: '1a2b3c4d', - seatBidId: 'cdbb6982-a269-40c7-84e5-04797f11d87a', - cpm: 4.5, - width: 1, - height: 1, - creativeId: 'crid', - creative_id: 'crid', - currency: CURRENCY, - netRevenue: true, - ttl: TTL, - mediaType: 'banner', - meta: {}, - ad: 'script
', - nurl: [ - 'win.domain.com' - ] - }, - { - requestId: '2570', - seatBidId: 'cdbb6982-a269-40c7-84e5-04797f11d87b', - id: 'id-5678', - cpm: 5, - width: 640, - height: 480, - creativeId: 'crid', - currency: CURRENCY, - netRevenue: true, - ttl: TTL, - mediaType: 'video', - meta: {}, - vastXml: 'vast xml', - nurl: [ - 'win.domain2.com' - ] - } - ]; - - bids.forEach(function(bid) { - expect(adapter.onBidWon.bind(adapter, bid)).to.not.throw(); - }); - }); - }); - }); - - describe('getUserSyncs', function() { - describe('Check methods succeed', function () { - it('should return the sync url', function() { - const syncOptions = { - 'iframeEnabled': true, - 'pixelEnabled': false - }; - const gdprConsent = { - gdprApplies: 1, - consentString: 'tcfv2' - }; - const uspConsent = { - consentString: '1Y---' - }; - - const syncUrls = [{ - type: 'iframe', - url: USER_SYNC_URL_IFRAME + '&gdpr=1&gdpr_consent=tcfv2&usp_consent=1Y---' - }]; - - expect(adapter.getUserSyncs(syncOptions, null, gdprConsent, uspConsent)).to.deep.equal(syncUrls); - }); - }); - }); -}); diff --git a/test/spec/modules/ssmasBidAdapter_spec.js b/test/spec/modules/ssmasBidAdapter_spec.js deleted file mode 100644 index 26c6f60da4b..00000000000 --- a/test/spec/modules/ssmasBidAdapter_spec.js +++ /dev/null @@ -1,244 +0,0 @@ -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 * as utils from 'src/utils.js'; - -describe('ssmasBidAdapter', function () { - const bid = { - bidder: SSMAS_CODE, - adUnitCode: 'adunit-code', - sizes: [[300, 250]], - bidId: '30b31c1838de1e', - bidderRequestId: '22edbae2733bf6', - auctionId: '1d1a030790a475', - params: { - placementId: '1' - } - }; - - const bidderRequest = { - 'bidderCode': SSMAS_CODE, - 'auctionId': 'd912faa2-174f-4636-b755-7396a0a964d8', - 'bidderRequestId': '109db5a5f5c6788', - 'bids': [ - bid - ], - 'auctionStart': 1684799653734, - 'timeout': 20000, - 'metrics': {}, - 'ortb2': { - 'site': { - 'domain': 'localhost:9999', - 'publisher': { - 'domain': 'localhost:9999' - }, - 'page': 'http://localhost:9999/integrationExamples/noadserver/basic_noadserver.html', - 'ref': 'http://localhost:9999/integrationExamples/noadserver/' - }, - 'device': { - 'w': 1536, - 'h': 711, - 'dnt': 0, - 'ua': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/113.0', - 'language': 'es' - } - }, - 'start': 1684799653737 - }; - - describe('Build Requests', () => { - it('Check bid request', function () { - const request = spec.buildRequests([bid], bidderRequest); - expect(request[0].method).to.equal(SSMAS_REQUEST_METHOD); - expect(request[0].url).to.equal(SSMAS_ENDPOINT); - }); - }); - - describe('register adapter functions', () => { - const adapter = newBidder(spec); - it('is registered', () => { - expect(adapter.callBids).to.exist.and.to.be.a('function'); - }); - }); - - describe('isBidRequestValid', function () { - it('validate bid request building', function () { - expect(spec.isBidRequestValid(bid)).to.be.true; - }); - - it('test bad bid request', function () { - // empty bid - expect(spec.isBidRequestValid({bidId: '', params: {}})).to.be.false; - - // empty bidId - bid.bidId = ''; - expect(spec.isBidRequestValid(bid)).to.be.false; - - // empty placementId - bid.bidId = '1231'; - bid.params.placementId = ''; - expect(spec.isBidRequestValid(bid)).to.be.false; - }); - - it('check bid request bidder is Sem Seo & Mas', function() { - const invalidBid = { - ...bid, bidder: 'invalidBidder' - }; - expect(spec.isBidRequestValid(invalidBid)).to.be.false; - }); - }); - - describe('interpretResponse', function () { - let bidOrtbResponse = { - 'id': 'aa02e2fe-56d9-4713-88f9-d8672ceae8ab', - 'seatbid': [ - { - 'bid': [ - { - 'id': '0001', - 'impid': '3919400af0b73e8', - 'price': 7.01, - 'adid': null, - 'nurl': null, - 'adm': '', - 'adomain': [ - 'ssmas.com' - ], - 'iurl': null, - 'cid': null, - 'crid': '3547894', - 'attr': [], - 'api': 0, - 'protocol': 0, - 'dealid': null, - 'h': 600, - 'w': 300, - 'cat': null, - 'ext': null, - 'builder': { - 'id': '0001', - 'adid': null, - 'impid': '3919400af0b73e8', - 'adomainList': [ - 'ssmas.com' - ], - 'attrList': [] - }, - 'adomainList': [ - 'ssmas.com' - ], - 'attrList': [] - } - ], - 'seat': null, - 'group': 0 - } - ], - 'bidid': '408731cc-c018-4976-bfc6-89f9c61e97a0', - 'cur': 'EUR', - 'nbr': -1 - }; - let bidResponse = { - 'mediaType': 'banner', - 'ad': '', - 'requestId': '37c658fe8ba57b', - 'seatBidId': '0001', - 'cpm': 10, - 'currency': 'EUR', - 'width': 300, - 'height': 250, - 'dealId': null, - 'creative_id': '3547894', - 'creativeId': '3547894', - 'ttl': 30, - 'netRevenue': true, - 'meta': { - 'advertiserDomains': [ - 'ssmas.com' - ] - } - }; - let bidRequest = { - 'imp': [ - { - 'ext': { - 'tid': '937db9c3-c22d-4454-b786-fcad76a349e5', - 'data': { - 'pbadslot': 'test-div' - } - }, - 'id': '3919400af0b73e8', - 'banner': { - 'topframe': 1, - 'format': [ - { - 'w': 300, - 'h': 600 - } - ] - } - }, - { - 'ext': { - 'tid': '0c0d3d1b-0ad0-4786-896d-24c15fc6531d', - 'data': { - 'pbadslot': 'test-div2' - } - }, - 'id': '3919400af0b73e8', - 'banner': { - 'topframe': 1, - 'format': [ - { - 'w': 300, - 'h': 600 - } - ] - } - } - ], - 'site': { - 'domain': 'localhost:9999', - 'publisher': { - 'domain': 'localhost:9999' - }, - 'page': 'http://localhost:9999/integrationExamples/noadserver/basic_noadserver.html', - 'ref': 'http://localhost:9999/integrationExamples/noadserver/', - 'id': 1, - 'ext': { - 'placementId': 13144370 - } - }, - 'device': { - 'w': 1536, - 'h': 711, - 'dnt': 0, - 'ua': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/113.0', - 'language': 'es' - }, - 'id': '8cc2f4b0-084d-4f40-acfa-5bec2023b1ab', - 'test': 0, - 'tmax': 20000, - 'source': { - 'tid': '8cc2f4b0-084d-4f40-acfa-5bec2023b1ab' - } - } - }); - - describe('test onBidWon function', function () { - beforeEach(function() { - sinon.stub(utils, 'triggerPixel'); - }); - afterEach(function() { - utils.triggerPixel.restore(); - }); - it('exists and is a function', () => { - expect(spec.onBidWon).to.exist.and.to.be.a('function'); - }); - it('should return nothing', function () { - var response = spec.onBidWon({}); - expect(response).to.be.an('undefined') - expect(utils.triggerPixel.called).to.equal(false); - }); - }); -}); diff --git a/test/spec/modules/sspBCBidAdapter_spec.js b/test/spec/modules/sspBCBidAdapter_spec.js index 71619424e4b..a95f08314b5 100644 --- a/test/spec/modules/sspBCBidAdapter_spec.js +++ b/test/spec/modules/sspBCBidAdapter_spec.js @@ -38,7 +38,7 @@ describe('SSPBC adapter', function () { }, auctionId, bidderRequestId, - bidId: bidderRequestId + '1', + bidId: auctionId + '1', transactionId, }, { @@ -60,7 +60,7 @@ describe('SSPBC adapter', function () { }, auctionId, bidderRequestId, - bidId: bidderRequestId + '2', + bidId: auctionId + '2', transactionId, } ]; @@ -83,7 +83,7 @@ describe('SSPBC adapter', function () { ], auctionId, bidderRequestId, - bidId: bidderRequestId + '1', + bidId: auctionId + '1', transactionId, }; const bid_native = { @@ -122,7 +122,7 @@ describe('SSPBC adapter', function () { ], auctionId, bidderRequestId, - bidId: bidderRequestId + '1', + bidId: auctionId + '1', transactionId, }; const bid_video = { @@ -144,7 +144,7 @@ describe('SSPBC adapter', function () { ], auctionId, bidderRequestId, - bidId: bidderRequestId + '1', + bidId: auctionId + '1', transactionId, }; const bids_timeouted = [{ @@ -155,7 +155,7 @@ describe('SSPBC adapter', function () { siteId: '8816', }], auctionId, - bidId: bidderRequestId + '1', + bidId: auctionId + '1', timeout: 100, }, { @@ -166,7 +166,7 @@ describe('SSPBC adapter', function () { siteId: '8816', }], auctionId, - bidId: bidderRequestId + '2', + bidId: auctionId + '2', timeout: 100, } ]; @@ -198,7 +198,7 @@ describe('SSPBC adapter', function () { }, auctionId, bidderRequestId, - bidId: bidderRequestId + '1', + bidId: auctionId + '1', transactionId, }]; const bidRequest = { @@ -293,7 +293,7 @@ describe('SSPBC adapter', function () { }; const serverResponse = { 'body': { - 'id': bidderRequestId, + 'id': auctionId, 'seatbid': [{ 'bid': [{ 'id': '3347324c-6889-46d2-a800-ae78a5214c06', @@ -333,7 +333,7 @@ describe('SSPBC adapter', function () { }; const serverResponseSingle = { 'body': { - 'id': bidderRequestId, + 'id': auctionId, 'seatbid': [{ 'bid': [{ 'id': '3347324c-6889-46d2-a800-ae78a5214c06', @@ -358,11 +358,11 @@ describe('SSPBC adapter', function () { }; const serverResponseOneCode = { 'body': { - 'id': bidderRequestId, + 'id': auctionId, 'seatbid': [{ 'bid': [{ 'id': '3347324c-6889-46d2-a800-ae78a5214c06', - 'impid': 'bidid-' + bidderRequestId + '1', + 'impid': 'bidid-' + auctionId + '1', 'price': 1, 'adid': 'lxHWkB7OnZeso3QiN1N4', 'nurl': '', @@ -385,11 +385,11 @@ describe('SSPBC adapter', function () { }; const serverResponseVideo = { 'body': { - 'id': bidderRequestId, + 'id': auctionId, 'seatbid': [{ 'bid': [{ 'id': '3347324c-6889-46d2-a800-ae78a5214c06', - 'impid': 'bidid-' + bidderRequestId + '1', + 'impid': 'bidid-' + auctionId + '1', 'price': 1, 'adid': 'lxHWkB7OnZeso3QiN1N4', 'nurl': '', @@ -413,11 +413,11 @@ describe('SSPBC adapter', function () { }; const serverResponseNative = { 'body': { - 'id': bidderRequestId, + 'id': auctionId, 'seatbid': [{ 'bid': [{ 'id': '3347324c-6889-46d2-a800-ae78a5214c06', - 'impid': 'bidid-' + bidderRequestId + '1', + 'impid': 'bidid-' + auctionId + '1', 'price': 1, 'adid': 'lxHWkB7OnZeso3QiN1N4', 'nurl': '', @@ -438,7 +438,7 @@ describe('SSPBC adapter', function () { }; const emptyResponse = { 'body': { - 'id': bidderRequestId, + 'id': auctionId, } } return { @@ -696,7 +696,7 @@ describe('SSPBC adapter', function () { let notificationPayload = spec.onBidWon(bid); expect(notificationPayload).to.have.property('event').that.equals('bidWon'); - expect(notificationPayload).to.have.property('requestId').that.equals(bid.bidderRequestId); + expect(notificationPayload).to.have.property('requestId').that.equals(bid.auctionId); expect(notificationPayload).to.have.property('tagid').that.deep.equals([bid.adUnitCode]); expect(notificationPayload).to.have.property('siteId').that.is.an('array'); expect(notificationPayload).to.have.property('slotId').that.is.an('array'); @@ -717,6 +717,7 @@ describe('SSPBC adapter', function () { let notificationPayload = spec.onTimeout(bids_timeouted); expect(notificationPayload).to.have.property('event').that.equals('timeout'); + expect(notificationPayload).to.have.property('requestId').that.equals(bids_timeouted[0].auctionId); expect(notificationPayload).to.have.property('tagid').that.deep.equals([bids_timeouted[0].adUnitCode, bids_timeouted[1].adUnitCode]); }); }); diff --git a/test/spec/modules/stroeerCoreBidAdapter_spec.js b/test/spec/modules/stroeerCoreBidAdapter_spec.js index 2ed5f80f152..55d79804a38 100644 --- a/test/spec/modules/stroeerCoreBidAdapter_spec.js +++ b/test/spec/modules/stroeerCoreBidAdapter_spec.js @@ -3,7 +3,6 @@ import {spec} from 'modules/stroeerCoreBidAdapter.js'; import * as utils from 'src/utils.js'; import {BANNER, VIDEO} from '../../../src/mediaTypes.js'; import {find} from 'src/polyfill.js'; -import sinon from 'sinon'; describe('stroeerCore bid adapter', function () { let sandbox; @@ -52,6 +51,8 @@ describe('stroeerCore bid adapter', function () { assert.notProperty(bidObject, 'ad'); } + const AUCTION_ID = utils.getUniqueIdentifierStr(); + // Vendor user ids and associated data const userIds = Object.freeze({ criteoId: 'criteo-user-id', @@ -71,6 +72,7 @@ describe('stroeerCore bid adapter', function () { }); const buildBidderRequest = () => ({ + auctionId: AUCTION_ID, bidderRequestId: 'bidder-request-id-123', bidderCode: 'stroeerCore', timeout: 5000, @@ -392,10 +394,6 @@ describe('stroeerCore bid adapter', function () { clock.tick(13500); const bidReq = buildBidderRequest(); - const UUID = 'fb6a39e3-083f-424c-9046-f1095e15f3d5'; - - const generateUUIDStub = sinon.stub(utils, 'generateUUID').returns(UUID); - const serverRequestInfo = spec.buildRequests(bidReq.bids, bidReq); const expectedTimeout = bidderRequest.timeout - (13500 - bidderRequest.auctionStart); @@ -403,7 +401,7 @@ describe('stroeerCore bid adapter', function () { assert.equal(expectedTimeout, 1500); const expectedJsonPayload = { - 'id': UUID, + 'id': AUCTION_ID, 'timeout': expectedTimeout, 'ref': 'https://www.example.com/?search=monkey', 'mpa': true, @@ -431,9 +429,8 @@ describe('stroeerCore bid adapter', function () { // trim away fields with undefined const actualJsonPayload = JSON.parse(JSON.stringify(serverRequestInfo.data)); - assert.deepEqual(actualJsonPayload, expectedJsonPayload); - generateUUIDStub.restore(); + assert.deepEqual(actualJsonPayload, expectedJsonPayload); }); describe('video bids', () => { diff --git a/test/spec/modules/stvBidAdapter_spec.js b/test/spec/modules/stvBidAdapter_spec.js index 3ef865ed2f1..41f29cced34 100644 --- a/test/spec/modules/stvBidAdapter_spec.js +++ b/test/spec/modules/stvBidAdapter_spec.js @@ -71,24 +71,6 @@ describe('stvAdapter', function() { 'hp': 1 } ] - }, - 'userId': { - 'id5id': { - 'uid': '1234', - 'ext': { - 'linkType': 'abc' - } - }, - 'netId': '2345', - 'uid2': { - 'id': '3456', - }, - 'sharedid': { - 'id': '4567', - }, - 'idl_env': '5678', - 'criteoId': '6789', - 'utiq': '7890', } }, { @@ -102,27 +84,7 @@ describe('stvAdapter', function() { ], 'bidId': '30b31c1838de1e2', 'bidderRequestId': '22edbae2733bf62', - 'auctionId': '1d1a030790a476', - 'userId': { // with other utiq variant - 'id5id': { - 'uid': '1234', - 'ext': { - 'linkType': 'abc' - } - }, - 'netId': '2345', - 'uid2': { - 'id': '3456', - }, - 'sharedid': { - 'id': '4567', - }, - 'idl_env': '5678', - 'criteoId': '6789', - 'utiq': { - 'id': '7890' - }, - } + 'auctionId': '1d1a030790a476' }, { 'bidder': 'stv', 'params': { @@ -219,7 +181,7 @@ describe('stvAdapter', function() { expect(request1.method).to.equal('GET'); expect(request1.url).to.equal(ENDPOINT_URL); let data = request1.data.replace(/rnd=\d+\&/g, '').replace(/ref=.*\&bid/g, 'bid').replace(/pbver=.*?&/g, 'pbver=test&'); - expect(data).to.equal('_f=html&alternative=prebid_js&_ps=6682&srw=300&srh=250&idt=100&bid_id=30b31c1838de1e1&pbver=test&schain=1.0,0!reseller.com,aaaaa,1,BidRequest4,,&uids=id5%3A1234,id5_linktype%3Aabc,netid%3A2345,uid2%3A3456,sharedid%3A4567,liverampid%3A5678,criteoid%3A6789,utiq%3A7890&pfilter%5Bfloorprice%5D=1000000&pfilter%5Bgeo%5D%5Bcountry%5D=DE&gdpr_consent=BOJ%2FP2HOJ%2FP2HABABMAAAAAZ%2BA%3D%3D&gdpr=true&bcat=IAB2%2CIAB4&dvt=desktop&pbcode=testDiv1&media_types%5Bbanner%5D=300x250'); + expect(data).to.equal('_f=html&alternative=prebid_js&_ps=6682&srw=300&srh=250&idt=100&bid_id=30b31c1838de1e1&pbver=test&schain=1.0,0!reseller.com,aaaaa,1,BidRequest4,,,&pfilter%5Bfloorprice%5D=1000000&pfilter%5Bgeo%5D%5Bcountry%5D=DE&gdpr_consent=BOJ%2FP2HOJ%2FP2HABABMAAAAAZ%2BA%3D%3D&gdpr=true&bcat=IAB2%2CIAB4&dvt=desktop&pbcode=testDiv1&media_types%5Bbanner%5D=300x250'); }); var request2 = spec.buildRequests([bidRequests[1]], bidderRequest)[0]; @@ -227,7 +189,7 @@ describe('stvAdapter', function() { expect(request2.method).to.equal('GET'); expect(request2.url).to.equal(ENDPOINT_URL); let data = request2.data.replace(/rnd=\d+\&/g, '').replace(/ref=.*\&bid/g, 'bid').replace(/pbver=.*?&/g, 'pbver=test&'); - expect(data).to.equal('_f=html&alternative=prebid_js&_ps=101&srw=300&srh=250&idt=100&bid_id=30b31c1838de1e2&pbver=test&uids=id5%3A1234,id5_linktype%3Aabc,netid%3A2345,uid2%3A3456,sharedid%3A4567,liverampid%3A5678,criteoid%3A6789,utiq%3A7890&gdpr_consent=BOJ%2FP2HOJ%2FP2HABABMAAAAAZ%2BA%3D%3D&gdpr=true&prebidDevMode=1&media_types%5Bbanner%5D=300x250'); + expect(data).to.equal('_f=html&alternative=prebid_js&_ps=101&srw=300&srh=250&idt=100&bid_id=30b31c1838de1e2&pbver=test&gdpr_consent=BOJ%2FP2HOJ%2FP2HABABMAAAAAZ%2BA%3D%3D&gdpr=true&prebidDevMode=1&media_types%5Bbanner%5D=300x250'); }); // Without gdprConsent diff --git a/test/spec/modules/taboolaBidAdapter_spec.js b/test/spec/modules/taboolaBidAdapter_spec.js index dd91c410d08..7d31e291667 100644 --- a/test/spec/modules/taboolaBidAdapter_spec.js +++ b/test/spec/modules/taboolaBidAdapter_spec.js @@ -1,5 +1,5 @@ import {expect} from 'chai'; -import {spec, internal, END_POINT_URL, userData, EVENT_ENDPOINT} from 'modules/taboolaBidAdapter.js'; +import {spec, internal, END_POINT_URL, userData} from 'modules/taboolaBidAdapter.js'; import {config} from '../../../src/config' import * as utils from '../../../src/utils' import {server} from '../../mocks/xhr' @@ -113,50 +113,6 @@ describe('Taboola Adapter', function () { }); }); - describe('onTimeout', function () { - it('onTimeout exist as a function', () => { - expect(spec.onTimeout).to.exist.and.to.be.a('function'); - }); - it('should send timeout', function () { - const timeoutData = [{ - bidder: 'taboola', - bidId: 'da43860a-4644-442a-b5e0-93f268cf8d19', - params: [{ - publisherId: 'publisherId' - }], - adUnitCode: 'adUnit-code', - timeout: 3000, - auctionId: '12a34b56c' - }] - spec.onTimeout(timeoutData); - expect(server.requests[0].method).to.equal('POST'); - expect(server.requests[0].url).to.equal(EVENT_ENDPOINT + '/timeout'); - expect(JSON.parse(server.requests[0].requestBody)).to.deep.equal(timeoutData); - }); - }); - - describe('onBidderError', function () { - it('onBidderError exist as a function', () => { - expect(spec.onBidderError).to.exist.and.to.be.a('function'); - }); - it('should send bidder error', function () { - const error = { - status: 204, - statusText: 'No Content' - }; - const bidderRequest = { - bidder: 'taboola', - params: { - publisherId: 'publisherId' - } - } - spec.onBidderError({error, bidderRequest}); - expect(server.requests[0].method).to.equal('POST'); - expect(server.requests[0].url).to.equal(EVENT_ENDPOINT + '/bidError'); - expect(JSON.parse(server.requests[0].requestBody)).to.deep.equal(error, bidderRequest); - }); - }); - describe('buildRequests', function () { const defaultBidRequest = { ...createBidRequest(), @@ -173,10 +129,10 @@ describe('Taboola Adapter', function () { } it('should build display request', function () { - const res = spec.buildRequests([defaultBidRequest], commonBidderRequest); const expectedData = { + id: 'mock-uuid', 'imp': [{ - 'id': res.data.imp[0].id, + 'id': 1, 'banner': { format: [{ w: displayBidRequestParams.sizes[0][0], @@ -193,8 +149,6 @@ describe('Taboola Adapter', function () { 'bidfloorcur': 'USD', 'ext': {} }], - id: 'mock-uuid', - 'test': 0, 'site': { 'id': commonBidRequest.params.publisherId, 'name': commonBidRequest.params.publisherId, @@ -217,8 +171,10 @@ describe('Taboola Adapter', function () { 'ext': {} }; - expect(res.url).to.equal(`${END_POINT_URL}?publisher=${commonBidRequest.params.publisherId}`); - expect(JSON.stringify(res.data)).to.deep.equal(JSON.stringify(expectedData)); + const res = spec.buildRequests([defaultBidRequest], commonBidderRequest); + + expect(res.url).to.equal(`${END_POINT_URL}/${commonBidRequest.params.publisherId}`); + expect(res.data).to.deep.equal(JSON.stringify(expectedData)); }); it('should pass optional parameters in request', function () { @@ -233,8 +189,9 @@ describe('Taboola Adapter', function () { }; const res = spec.buildRequests([bidRequest], commonBidderRequest); - expect(res.data.imp[0].bidfloor).to.deep.equal(0.25); - expect(res.data.imp[0].bidfloorcur).to.deep.equal('EUR'); + const resData = JSON.parse(res.data); + expect(resData.imp[0].bidfloor).to.deep.equal(0.25); + expect(resData.imp[0].bidfloorcur).to.deep.equal('EUR'); }); it('should pass bid floor', function () { @@ -249,8 +206,9 @@ describe('Taboola Adapter', function () { } }; const res = spec.buildRequests([bidRequest], commonBidderRequest); - expect(res.data.imp[0].bidfloor).to.deep.equal(2.7); - expect(res.data.imp[0].bidfloorcur).to.deep.equal('USD'); + const resData = JSON.parse(res.data); + expect(resData.imp[0].bidfloor).to.deep.equal(2.7); + expect(resData.imp[0].bidfloorcur).to.deep.equal('USD'); }); it('should pass bid floor even if it is a bid floor param', function () { @@ -270,8 +228,9 @@ describe('Taboola Adapter', function () { } }; const res = spec.buildRequests([bidRequest], commonBidderRequest); - expect(res.data.imp[0].bidfloor).to.deep.equal(2.7); - expect(res.data.imp[0].bidfloorcur).to.deep.equal('USD'); + const resData = JSON.parse(res.data); + expect(resData.imp[0].bidfloor).to.deep.equal(2.7); + expect(resData.imp[0].bidfloorcur).to.deep.equal('USD'); }); it('should pass impression position', function () { @@ -285,7 +244,8 @@ describe('Taboola Adapter', function () { }; const res = spec.buildRequests([bidRequest], commonBidderRequest); - expect(res.data.imp[0].banner.pos).to.deep.equal(2); + const resData = JSON.parse(res.data); + expect(resData.imp[0].banner.pos).to.deep.equal(2); }); it('should pass gpid if configured', function () { @@ -301,23 +261,8 @@ describe('Taboola Adapter', function () { }; const res = spec.buildRequests([bidRequest], commonBidderRequest); - expect(res.data.imp[0].ext.gpid).to.deep.equal('/homepage/#1'); - }); - - it('should pass new parameter to imp ext', function () { - const ortb2Imp = { - ext: { - example: 'example' - } - } - const bidRequest = { - ...defaultBidRequest, - ortb2Imp: ortb2Imp, - params: {...commonBidRequest.params} - }; - - const res = spec.buildRequests([bidRequest], commonBidderRequest); - expect(res.data.imp[0].ext.example).to.deep.equal('example'); + const resData = JSON.parse(res.data); + expect(resData.imp[0].ext.gpid).to.deep.equal('/homepage/#1'); }); it('should pass bidder timeout', function () { @@ -326,25 +271,8 @@ describe('Taboola Adapter', function () { timeout: 500 } const res = spec.buildRequests([defaultBidRequest], bidderRequest); - expect(res.data.tmax).to.equal(500); - }); - - it('should pass bidder tmax as int', function () { - const bidderRequest = { - ...commonBidderRequest, - timeout: '500' - } - const res = spec.buildRequests([defaultBidRequest], bidderRequest); - expect(res.data.tmax).to.equal(500); - }); - - it('should pass bidder timeout as null', function () { - const bidderRequest = { - ...commonBidderRequest, - timeout: null - } - const res = spec.buildRequests([defaultBidRequest], bidderRequest); - expect(res.data.tmax).to.equal(undefined); + const resData = JSON.parse(res.data); + expect(resData.tmax).to.equal(500); }); describe('first party data', function () { @@ -358,9 +286,10 @@ describe('Taboola Adapter', function () { } } const res = spec.buildRequests([defaultBidRequest], bidderRequest); - expect(res.data.bcat).to.deep.equal(bidderRequest.ortb2.bcat) - expect(res.data.badv).to.deep.equal(bidderRequest.ortb2.badv) - expect(res.data.wlang).to.deep.equal(bidderRequest.ortb2.wlang) + const resData = JSON.parse(res.data); + expect(resData.bcat).to.deep.equal(bidderRequest.ortb2.bcat) + expect(resData.badv).to.deep.equal(bidderRequest.ortb2.badv) + expect(resData.wlang).to.deep.equal(bidderRequest.ortb2.wlang) }); it('should pass pageType if exists in ortb2', function () { @@ -375,20 +304,8 @@ describe('Taboola Adapter', function () { } } const res = spec.buildRequests([defaultBidRequest], bidderRequest); - expect(res.data.ext.pageType).to.deep.equal(bidderRequest.ortb2.ext.data.pageType); - }); - - it('should pass additional parameter in request', function () { - const bidderRequest = { - ...commonBidderRequest, - ortb2: { - ext: { - example: 'example' - } - } - } - const res = spec.buildRequests([defaultBidRequest], bidderRequest); - expect(res.data.ext.example).to.deep.equal(bidderRequest.ortb2.ext.example); + const resData = JSON.parse(res.data); + expect(resData.ext.pageType).to.deep.equal(bidderRequest.ortb2.ext.data.pageType); }); }); @@ -405,8 +322,9 @@ describe('Taboola Adapter', function () { }; const res = spec.buildRequests([defaultBidRequest], bidderRequest) - expect(res.data.user.ext.consent).to.equal('consentString') - expect(res.data.regs.ext.gdpr).to.equal(1) + const resData = JSON.parse(res.data) + expect(resData.user.ext.consent).to.equal('consentString') + expect(resData.regs.ext.gdpr).to.equal(1) }); it('should pass GPP consent if exist in ortb2', function () { @@ -418,8 +336,9 @@ describe('Taboola Adapter', function () { } const res = spec.buildRequests([defaultBidRequest], {...commonBidderRequest, ortb2}) - expect(res.data.regs.ext.gpp).to.equal('testGpp') - expect(res.data.regs.ext.gpp_sid).to.deep.equal([1, 2, 3]) + const resData = JSON.parse(res.data) + expect(resData.regs.ext.gpp).to.equal('testGpp') + expect(resData.regs.ext.gpp_sid).to.deep.equal([1, 2, 3]) }); it('should pass us privacy consent', function () { @@ -430,14 +349,16 @@ describe('Taboola Adapter', function () { uspConsent: 'consentString' } const res = spec.buildRequests([defaultBidRequest], bidderRequest); - expect(res.data.regs.ext.us_privacy).to.equal('consentString'); + const resData = JSON.parse(res.data); + expect(resData.regs.ext.us_privacy).to.equal('consentString'); }); it('should pass coppa consent', function () { config.setConfig({coppa: true}) const res = spec.buildRequests([defaultBidRequest], commonBidderRequest) - expect(res.data.regs.coppa).to.equal(1) + const resData = JSON.parse(res.data); + expect(resData.regs.coppa).to.equal(1) config.resetConfig() }); @@ -454,7 +375,8 @@ describe('Taboola Adapter', function () { timeout: 500 } const res = spec.buildRequests([defaultBidRequest], bidderRequest); - expect(res.data.user.buyeruid).to.equal(51525152); + const resData = JSON.parse(res.data); + expect(resData.user.buyeruid).to.equal(51525152); }); it('should get user id from cookie if local storage isn`t defined', function () { @@ -468,22 +390,9 @@ describe('Taboola Adapter', function () { ...commonBidderRequest }; const res = spec.buildRequests([defaultBidRequest], bidderRequest); - expect(res.data.user.buyeruid).to.equal('12121212'); - }); - - it('should get user id from tgid cookie if local storage isn`t defined', function () { - getDataFromLocalStorage.returns(51525152); - hasLocalStorage.returns(false); - localStorageIsEnabled.returns(false); - cookiesAreEnabled.returns(true); - getCookie.returns('d966c5be-c49f-4f73-8cd1-37b6b5790653-tuct9f7bf10'); - - const bidderRequest = { - ...commonBidderRequest - }; - const res = spec.buildRequests([defaultBidRequest], bidderRequest); + const resData = JSON.parse(res.data); - expect(res.data.user.buyeruid).to.equal('d966c5be-c49f-4f73-8cd1-37b6b5790653-tuct9f7bf10'); + expect(resData.user.buyeruid).to.equal('12121212'); }); it('should get user id from TRC if local storage and cookie isn`t defined', function () { @@ -499,7 +408,8 @@ describe('Taboola Adapter', function () { ...commonBidderRequest } const res = spec.buildRequests([defaultBidRequest], bidderRequest); - expect(res.data.user.buyeruid).to.equal(window.TRC.user_id); + const resData = JSON.parse(res.data); + expect(resData.user.buyeruid).to.equal(window.TRC.user_id); delete window.TRC; }); @@ -512,7 +422,8 @@ describe('Taboola Adapter', function () { ...commonBidderRequest } const res = spec.buildRequests([defaultBidRequest], bidderRequest); - expect(res.data.user.buyeruid).to.equal(0); + const resData = JSON.parse(res.data); + expect(resData.user.buyeruid).to.equal(0); }); it('should set buyeruid to be 0 if it`s a new user', function () { @@ -520,29 +431,13 @@ describe('Taboola Adapter', function () { ...commonBidderRequest } const res = spec.buildRequests([defaultBidRequest], bidderRequest); - expect(res.data.user.buyeruid).to.equal(0); + const resData = JSON.parse(res.data); + expect(resData.user.buyeruid).to.equal(0); }); }); }) describe('interpretResponse', function () { - const defaultBidRequest = { - ...createBidRequest(), - ...displayBidRequestParams, - }; - const commonBidderRequest = { - bidderRequestId: 'mock-uuid', - refererInfo: { - page: 'https://example.com/ref', - ref: 'https://ref', - domain: 'example.com', - } - }; - const bidderRequest = { - ...commonBidderRequest - }; - const request = spec.buildRequests([defaultBidRequest], bidderRequest); - const serverResponse = { body: { 'id': '49ffg4d58ef9a163a69fhgfghd4fad03621b9e036f24f7_15', @@ -551,7 +446,7 @@ describe('Taboola Adapter', function () { 'bid': [ { 'id': '0b3dd94348-134b-435f-8db5-6bf5afgfc39e86c', - 'impid': request.data.imp[0].id, + 'impid': '1', 'price': 0.342068, 'adid': '2785119545551083381', 'adm': '\u003chtml\u003e\n\u003chead\u003e\n\u003cmeta charset\u003d"UTF-8"\u003e\n\u003cmeta http-equiv\u003d"Content-Type" content\u003d"text/html; charset\u003dutf-8"/\u003e\u003c/head\u003e\n\u003cbody style\u003d"margin: 0px; overflow:hidden;"\u003e \n\u003cscript type\u003d"text/javascript"\u003e\nwindow.tbl_trc_domain \u003d \u0027us-trc.taboola.com\u0027;\nwindow._taboola \u003d window._taboola || [];\n_taboola.push({article:\u0027auto\u0027});\n!function (e, f, u, i) {\nif (!document.getElementById(i)){\ne.async \u003d 1;\ne.src \u003d u;\ne.id \u003d i;\nf.parentNode.insertBefore(e, f);\n}\n}(document.createElement(\u0027script\u0027),\ndocument.getElementsByTagName(\u0027script\u0027)[0],\n\u0027//cdn.taboola.com/libtrc/wattpad-placement-255/loader.js\u0027,\n\u0027tb_loader_script\u0027);\nif(window.performance \u0026\u0026 typeof window.performance.mark \u003d\u003d \u0027function\u0027)\n{window.performance.mark(\u0027tbl_ic\u0027);}\n\u003c/script\u003e\n\n\u003cdiv id\u003d"taboola-below-article-thumbnails" style\u003d"height: 250px; width: 300px;"\u003e\u003c/div\u003e\n\u003cscript type\u003d"text/javascript"\u003e\nwindow._taboola \u003d window._taboola || [];\n_taboola.push({\nmode: \u0027Rbox_300x250_1x1\u0027,\ncontainer: \u0027taboola-below-article-thumbnails\u0027,\nplacement: \u0027wattpad.com_P18694_S257846_W300_H250_N1_TB\u0027,\ntarget_type: \u0027mix\u0027,\n"rtb-win":{ \nbi:\u002749ff4d58ef9a163a696d4fad03621b9e036f24f7_15\u0027,\ncu:\u0027USD\u0027,\nwp:\u0027${AUCTION_PRICE:BF}\u0027,\nwcb:\u0027~!audex-display-impression!~\u0027,\nrt:\u00271643227025284\u0027,\nrdc:\u0027us.taboolasyndication.com\u0027,\nti:\u00274212\u0027,\nex:\u0027MagniteSCoD\u0027,\nbs:\u0027xapi:257846:lvvSm6Ak7_wE\u0027,\nbp:\u002718694\u0027,\nbd:\u0027wattpad.com\u0027,\nsi:\u00279964\u0027\n} \n,\nrec: {"trc":{"si":"a69c7df43b2334f0aa337c37e2d80c21","sd":"v2_a69c7df43b2334f0aa337c37e2d80c21_3c70f7c7d64a65b15e4a4175c9a2cfa51072f04bMagniteSCoD_1643227025_1643227025_CJS1tQEQ5NdWGPLA0d76xo-9ngEgASgEMCY4iegHQIroB0iB09kDUKPPB1gAYABop-G2i_Hl-eVucAA","ui":"3c70f7c7d64a65b15e4a4175c9a2cfa51072f04bMagniteSCoD","plc":"PHON","wi":"-643136642229425433","cc":"CA","route":"US:US:V","el2r":["bulk-metrics","debug","social","metrics","perf"],"uvpw":"1","pi":"1420260","cpb":"GNO629MGIJz__________wEqGXVzLnRhYm9vbGFzeW5kaWNhdGlvbi5jb20yC3RyYy1zY29kMTI5OIDwmrUMQInoB0iK6AdQgdPZA1ijzwdjCN3__________wEQ3f__________ARgjZGMI3AoQoBAYFmRjCNIDEOAGGAhkYwiWFBCcHBgYZGMI9AUQiwoYC2RjCNkUEPkcGB1kYwj0FBCeHRgfZGorNDlmZjRkNThlZjlhMTYzYTY5NmQ0ZmFkMDM2MjFiOWUwMzZmMjRmN18xNXgCgAHpbIgBrPvTxQE","dcga":{"pubConfigOverride":{"border-color":"black","font-weight":"bold","inherit-title-color":"true","module-name":"cta-lazy-module","enable-call-to-action-creative-component":"true","disable-cta-on-custom-module":"true"}},"tslt":{"p-video-overlay":{"cancel":"סגור","goto":"×ĸבור לד×Ŗ"},"read-more":{"DEFAULT_CAPTION":"%D7%A7%D7%A8%D7%90%20%D7%A2%D7%95%D7%93"},"next-up":{"BTN_TEXT":"לקריא×Ē ה×Ēוכן הבא"},"time-ago":{"now":"×ĸכשיו","today":"היום","yesterday":"א×Ēמול","minutes":"לפני {0} דקו×Ē","hour":"לפני ׊×ĸה","hours":"לפני {0} ׊×ĸו×Ē","days":"לפני {0} ימים"},"explore-more":{"TITLE_TEXT":"המשיכו לקרוא","POPUP_TEXT":"אל ×Ēפספסו הזדמנו×Ē לקרוא ×ĸוד ×Ēוכן מ×ĸולה, רג×ĸ לפני ׊×Ē×ĸזבו"}},"evh":"-1964913910","vl":[{"ri":"185db6d274ce94b27caaabd9eed7915b","uip":"wattpad.com_P18694_S257846_W300_H250_N1_TB","ppb":"COIF","estimation_method":"EcpmEstimationMethodType_ESTIMATION","baseline_variant":"false","original_ecpm":"0.4750949889421463","v":[{"thumbnail":"https://cdn.taboola.com/libtrc/static/thumbnails/a2b272be514ca3ebe3f97a4a32a41db5.jpg","all-thumbnails":"https://cdn.taboola.com/libtrc/static/thumbnails/a2b272be514ca3ebe3f97a4a32a41db5.jpg!-#@1600x1000","origin":"default","thumb-size":"1600x1000","title":"Get Roofing Services At Prices You Can Afford In Edmonton","type":"text","published-date":"1641997069","branding-text":"Roofing Services | Search Ads","url":"https://inneth-conded.xyz/9ad2e613-8777-4fe7-9a52-386c88879289?site\u003dwattpad-placement-255\u0026site_id\u003d1420260\u0026title\u003dGet+Roofing+Services+At+Prices+You+Can+Afford+In+Edmonton\u0026platform\u003dSmartphone\u0026campaign_id\u003d15573949\u0026campaign_item_id\u003d3108610633\u0026thumbnail\u003dhttp%3A%2F%2Fcdn.taboola.com%2Flibtrc%2Fstatic%2Fthumbnails%2Fa2b272be514ca3ebe3f97a4a32a41db5.jpg\u0026cpc\u003d{cpc}\u0026click_id\u003dGiCIypnAQogsMTFL3e_mPaVM2qLvK3KRU6LWzEMUgeB6piCit1Uox6CNr5v5n-x1\u0026tblci\u003dGiCIypnAQogsMTFL3e_mPaVM2qLvK3KRU6LWzEMUgeB6piCit1Uox6CNr5v5n-x1#tblciGiCIypnAQogsMTFL3e_mPaVM2qLvK3KRU6LWzEMUgeB6piCit1Uox6CNr5v5n-x1","duration":"0","sig":"328243c4127ff16e3fdcd7270bab908f6f3fc5b4c98d","item-id":"~~V1~~2785119550041083381~~PnBkfBE9JnQxpahv0adkcuIcmMhroRAHXwLZd-7zhunTxvAnL2wqac4MyzR7uD46gj3kUkbS3FhelBtnsiJV6MhkDZRZzzIqDobN6rWmCPA3hYz5D3PLat6nhIftiT1lwdxwdlxkeV_Mfb3eos_TQavImGhxk0e7psNAZxHJ9RKL2w3lppALGgQJoy2o6lkf-pOqODtX1VkgWpEEM4WsVoWOnUTAwdyGd-8yrze8CWNp752y28hl7lleicyO1vByRdbgwlJdnqyroTPEQNNEn1JRxBOSYSWt-Xm3vkPm-G4","uploader":"","is-syndicated":"true","publisher":"search","id":"~~V1~~2785119550041083381~~PnBkfBE9JnQxpahv0adkcuIcmMhroRAHXwLZd-7zhunTxvAnL2wqac4MyzR7uD46gj3kUkbS3FhelBtnsiJV6MhkDZRZzzIqDobN6rWmCPA3hYz5D3PLat6nhIftiT1lwdxwdlxkeV_Mfb3eos_TQavImGhxk0e7psNAZxHJ9RKL2w3lppALGgQJoy2o6lkf-pOqODtX1VkgWpEEM4WsVoWOnUTAwdyGd-8yrze8CWNp752y28hl7lleicyO1vByRdbgwlJdnqyroTPEQNNEn1JRxBOSYSWt-Xm3vkPm-G4","category":"home","views":"0","itp":[{"u":"https://trc.taboola.com/1326786/log/3/unip?en\u003dclickersusa","t":"c"}],"description":""}]}],"cpcud":{"upc":"0.0","upr":"0.0"}}}\n});\n\u003c/script\u003e\n\n\u003cscript type\u003d"text/javascript"\u003e\nwindow._taboola \u003d window._taboola || [];\n_taboola.push({flush: true});\n\u003c/script\u003e\n\n\u003c/body\u003e\n\u003c/html\u003e', @@ -576,6 +471,15 @@ describe('Taboola Adapter', function () { } }; + const request = { + bids: [ + { + ...commonBidRequest, + ...displayBidRequestParams + } + ] + } + it('should return empty array if no valid bids', function () { const res = spec.interpretResponse(serverResponse, []) expect(res).to.be.an('array').that.is.empty @@ -609,7 +513,18 @@ describe('Taboola Adapter', function () { }); it('should interpret multi impression request', function () { - const multiRequest = spec.buildRequests([defaultBidRequest, defaultBidRequest], bidderRequest); + const multiRequest = { + bids: [ + { + ...createBidRequest(), + ...displayBidRequestParams + }, + { + ...createBidRequest(), + ...displayBidRequestParams + } + ] + } const multiServerResponse = { body: { @@ -619,7 +534,7 @@ describe('Taboola Adapter', function () { 'bid': [ { 'id': '0b3dd94348-134b-435f-8db5-6bf5afgfc39e86c', - 'impid': multiRequest.data.imp[0].id, + 'impid': '2', 'price': 0.342068, 'adid': '2785119545551083381', 'adm': 'ADM2', @@ -636,7 +551,7 @@ describe('Taboola Adapter', function () { }, { 'id': '0b3dd94348-134b-435f-8db5-6bf5afgfc39e86c', - 'impid': multiRequest.data.imp[1].id, + 'impid': '1', 'price': 0.342068, 'adid': '2785119545551083381', 'adm': 'ADM1', @@ -667,8 +582,6 @@ describe('Taboola Adapter', function () { requestId: multiRequest.bids[1].bidId, cpm: bid.price, creativeId: bid.crid, - creative_id: bid.crid, - seatBidId: multiServerResponse.body.seatbid[0].bid[0].id, ttl: 60, netRevenue: true, currency: multiServerResponse.body.cur, @@ -685,8 +598,6 @@ describe('Taboola Adapter', function () { requestId: multiRequest.bids[0].bidId, cpm: bid.price, creativeId: bid.crid, - creative_id: bid.crid, - seatBidId: multiServerResponse.body.seatbid[0].bid[1].id, ttl: 60, netRevenue: true, currency: multiServerResponse.body.cur, @@ -710,10 +621,8 @@ describe('Taboola Adapter', function () { const expectedRes = [ { requestId: request.bids[0].bidId, - seatBidId: serverResponse.body.seatbid[0].bid[0].id, cpm: bid.price, creativeId: bid.crid, - creative_id: bid.crid, ttl: 60, netRevenue: true, currency: serverResponse.body.cur, @@ -739,10 +648,8 @@ describe('Taboola Adapter', function () { const expectedRes = [ { requestId: request.bids[0].bidId, - seatBidId: serverResponse.body.seatbid[0].bid[0].id, cpm: bid.price, creativeId: bid.crid, - creative_id: bid.crid, ttl: 125, netRevenue: true, currency: serverResponse.body.cur, @@ -761,7 +668,18 @@ describe('Taboola Adapter', function () { }); it('should replace AUCTION_PRICE macro in adm', function () { - const multiRequest = spec.buildRequests([defaultBidRequest, defaultBidRequest], bidderRequest); + const multiRequest = { + bids: [ + { + ...createBidRequest(), + ...displayBidRequestParams + }, + { + ...createBidRequest(), + ...displayBidRequestParams + } + ] + } const multiServerResponseWithMacro = { body: { 'id': '49ffg4d58ef9a163a69fhgfghd4fad03621b9e036f24f7_15', @@ -770,7 +688,7 @@ describe('Taboola Adapter', function () { 'bid': [ { 'id': '0b3dd94348-134b-435f-8db5-6bf5afgfc39e86c', - 'impid': multiRequest.data.imp[0].id, + 'impid': '2', 'price': 0.34, 'adid': '2785119545551083381', 'adm': 'ADM2,\\nwp:\'${AUCTION_PRICE}\'', @@ -787,7 +705,7 @@ describe('Taboola Adapter', function () { }, { 'id': '0b3dd94348-134b-435f-8db5-6bf5afgfc39e86c', - 'impid': multiRequest.data.imp[1].id, + 'impid': '1', 'price': 0.35, 'adid': '2785119545551083381', 'adm': 'ADM2,\\nwp:\'${AUCTION_PRICE}\'', @@ -817,8 +735,6 @@ describe('Taboola Adapter', function () { requestId: multiRequest.bids[1].bidId, cpm: multiServerResponseWithMacro.body.seatbid[0].bid[0].price, creativeId: bid.crid, - creative_id: bid.crid, - seatBidId: multiServerResponseWithMacro.body.seatbid[0].bid[0].id, ttl: 60, netRevenue: true, currency: multiServerResponseWithMacro.body.cur, @@ -835,8 +751,6 @@ describe('Taboola Adapter', function () { requestId: multiRequest.bids[0].bidId, cpm: multiServerResponseWithMacro.body.seatbid[0].bid[1].price, creativeId: bid.crid, - creative_id: bid.crid, - seatBidId: multiServerResponseWithMacro.body.seatbid[0].bid[1].id, ttl: 60, netRevenue: true, currency: multiServerResponseWithMacro.body.cur, @@ -857,28 +771,17 @@ describe('Taboola Adapter', function () { describe('getUserSyncs', function () { const usersyncUrl = 'https://trc.taboola.com/sg/prebidJS/1/cm'; - const iframeUrl = 'https://cdn.taboola.com/scripts/prebid_iframe_sync.html'; - it('should not return user sync if pixelEnabled is false and iframe disabled', function () { - const res = spec.getUserSyncs({pixelEnabled: false, iframeEnabled: false}); + it('should not return user sync if pixelEnabled is false', function () { + const res = spec.getUserSyncs({pixelEnabled: false}); expect(res).to.be.an('array').that.is.empty; }); it('should return user sync if pixelEnabled is true', function () { - const res = spec.getUserSyncs({pixelEnabled: true, iframeEnabled: false}); + const res = spec.getUserSyncs({pixelEnabled: true}); expect(res).to.deep.equal([{type: 'image', url: usersyncUrl}]); }); - it('should return user sync if iframeEnabled is true', function () { - const res = spec.getUserSyncs({iframeEnabled: true, pixelEnabled: false}); - expect(res).to.deep.equal([{type: 'iframe', url: iframeUrl}]); - }); - - it('should return both user syncs if iframeEnabled is true and pixelEnabled is true', function () { - const res = spec.getUserSyncs({iframeEnabled: true, pixelEnabled: true}); - expect(res).to.deep.equal([{type: 'iframe', url: iframeUrl}, {type: 'image', url: usersyncUrl}]); - }); - it('should pass consent tokens values', function() { expect(spec.getUserSyncs({ pixelEnabled: true }, {}, {gdprApplies: true, consentString: 'GDPR_CONSENT'}, 'USP_CONSENT')).to.deep.equal([{ type: 'image', url: `${usersyncUrl}?gdpr=1&gdpr_consent=GDPR_CONSENT&us_privacy=USP_CONSENT` diff --git a/test/spec/modules/tagorasBidAdapter_spec.js b/test/spec/modules/tagorasBidAdapter_spec.js deleted file mode 100644 index 7559567dcff..00000000000 --- a/test/spec/modules/tagorasBidAdapter_spec.js +++ /dev/null @@ -1,651 +0,0 @@ -import {expect} from 'chai'; -import { - spec as adapter, - createDomain, - hashCode, - extractPID, - extractCID, - extractSubDomain, - getStorageItem, - setStorageItem, - tryParseJSON, - getUniqueDealId, -} from 'modules/tagorasBidAdapter'; -import * as utils from 'src/utils.js'; -import {version} from 'package.json'; -import {useFakeTimers} from 'sinon'; -import {BANNER, VIDEO} from '../../../src/mediaTypes'; -import {config} from '../../../src/config'; - -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', - 'sizes': [[300, 250], [300, 600]], - 'bidderRequestId': '1fdb5ff1b6eaa7', - 'bidRequestsCount': 4, - 'bidderRequestsCount': 3, - 'bidderWinsCount': 1, - 'requestId': 'b0777d85-d061-450e-9bc7-260dd54bbb7a', - 'schain': 'a0819c69-005b-41ed-af06-1be1e0aefefc', - 'mediaTypes': [BANNER], - 'ortb2Imp': { - 'ext': { - 'gpid': '0123456789', - 'tid': 'c881914b-a3b5-4ecf-ad9c-1c2f37c6aabf' - } - } -}; - -const VIDEO_BID = { - 'bidId': '2d52001cabd527', - 'adUnitCode': '63550ad1ff6642d368cba59dh5884270560', - 'bidderRequestId': '12a8ae9ada9c13', - 'transactionId': '56e184c6-bde9-497b-b9b9-cf47a61381ee', - '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 - } - }, - 'ortb2Imp': { - 'ext': { - 'tid': '56e184c6-bde9-497b-b9b9-cf47a61381ee' - } - } -} - -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': { - 'regs': { - 'gpp': 'gpp_string', - 'gpp_sid': [7] - }, - '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': '' - } - } - } -}; - -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': ['tagoras.io'], - 'exp': 60, - 'width': 545, - 'height': 307, - 'mediaType': 'video', - 'creativeId': '12610997325162499419', - 'price': 2, - 'cookies': [] - }] - } -}; - -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('TagorasBidAdapter', function () { - describe('validtae spec', function () { - it('exists and is a function', function () { - expect(adapter.isBidRequestValid).to.exist.and.to.be.a('function'); - }); - - it('exists and is a function', 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 () { - $$PREBID_GLOBAL$$.bidderSettings = { - tagoras: { - storageAllowed: true - } - }; - sandbox = sinon.sandbox.create(); - 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', - 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': '' - }, - 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: '' - } - }); - }); - - 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', - 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': '' - }, - 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', - } - }); - }); - - after(function () { - $$PREBID_GLOBAL$$.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.tagoras.io/api/sync/iframe/?cid=testcid123&gdpr=0&gdpr_consent=&us_privacy=' - }]); - }); - - 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.tagoras.io/api/sync/iframe/?cid=testcid123&gdpr=0&gdpr_consent=&us_privacy=' - }]); - }); - - 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.tagoras.io/api/sync/image/?cid=testcid123&gdpr=0&gdpr_consent=&us_privacy=', - 'type': 'image' - }]); - }); - - 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.tagoras.io/api/sync/image/?cid=testcid123&gdpr=1&gdpr_consent=consent_string&us_privacy=usp_string&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: ['tagoras.io'], - agencyName: 'Agency Name', - }; - const responses = adapter.interpretResponse(serverResponse, REQUEST); - expect(responses[0].meta).to.deep.equal({ - advertiserDomains: ['tagoras.io'], - 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: ['tagoras.io'] - } - }); - }); - - 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 'parrableId': - return {eid: 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); - }); - }); - }); - - 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 () { - $$PREBID_GLOBAL$$.bidderSettings = { - tagoras: { - storageAllowed: true - } - }; - }); - after(function () { - $$PREBID_GLOBAL$$.bidderSettings = {}; - }); - const key = 'myKey'; - let uniqueDealId; - beforeEach(() => { - uniqueDealId = getUniqueDealId(key, 0); - }) - - it('should get current unique deal id', function (done) { - // waiting some time so `now` will become past - setTimeout(() => { - const current = getUniqueDealId(key); - expect(current).to.be.equal(uniqueDealId); - done(); - }, 200); - }); - - it('should get new unique deal id on expiration', function (done) { - setTimeout(() => { - const current = getUniqueDealId(key, 100); - expect(current).to.not.be.equal(uniqueDealId); - done(); - }, 200) - }); - }); - - describe('storage utils', function () { - before(function () { - $$PREBID_GLOBAL$$.bidderSettings = { - tagoras: { - storageAllowed: true - } - }; - }); - after(function () { - $$PREBID_GLOBAL$$.bidderSettings = {}; - }); - it('should get value from storage with create param', function () { - const now = Date.now(); - const clock = useFakeTimers({ - shouldAdvanceTime: true, - now - }); - setStorageItem('myKey', 2020); - const {value, created} = getStorageItem('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('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/tappxBidAdapter_spec.js b/test/spec/modules/tappxBidAdapter_spec.js index 46fac8de1e2..58a62fb2869 100644 --- a/test/spec/modules/tappxBidAdapter_spec.js +++ b/test/spec/modules/tappxBidAdapter_spec.js @@ -466,23 +466,4 @@ describe('Tappx bid adapter', function () { assert.isString(_extractPageUrl(validBidRequests, bidderRequest)); }); }) - - describe('Empty params values from bid tests', function() { - let validBidRequest = JSON.parse(JSON.stringify(c_BIDREQUEST)); - - it('should return false when tappxkey is empty', function () { - validBidRequest.bids[0].params.tappxkey = ''; - assert.isFalse(spec.isBidRequestValid(validBidRequest.bids[0])); - }); - - it('should return false when host is empty', function () { - validBidRequest.bids[0].params.host = ''; - assert.isFalse(spec.isBidRequestValid(validBidRequest.bids[0])); - }); - - it('should return false when endpoint is empty', function () { - validBidRequest.bids[0].params.endpoint = ''; - assert.isFalse(spec.isBidRequestValid(validBidRequest.bids[0])); - }); - }); }); diff --git a/test/spec/modules/teadsBidAdapter_spec.js b/test/spec/modules/teadsBidAdapter_spec.js index 98f706f5193..a4b94135bad 100644 --- a/test/spec/modules/teadsBidAdapter_spec.js +++ b/test/spec/modules/teadsBidAdapter_spec.js @@ -1,21 +1,23 @@ import {expect} from 'chai'; import {spec, storage} from 'modules/teadsBidAdapter.js'; import {newBidder} from 'src/adapters/bidderFactory.js'; -import { off } from '../../../src/events'; +import {getStorageManager} from 'src/storageManager'; const ENDPOINT = 'https://a.teads.tv/hb/bid-request'; const AD_SCRIPT = '"'; describe('teadsBidAdapter', () => { const adapter = newBidder(spec); - let sandbox; + let cookiesAreEnabledStub, getCookieStub; beforeEach(function () { - sandbox = sinon.sandbox.create(); + cookiesAreEnabledStub = sinon.stub(storage, 'cookiesAreEnabled'); + getCookieStub = sinon.stub(storage, 'getCookie'); }); afterEach(function () { - sandbox.restore(); + cookiesAreEnabledStub.restore(); + getCookieStub.restore(); }); describe('inherited functions', () => { @@ -106,21 +108,21 @@ describe('teadsBidAdapter', () => { } ]; - let bidderRequestDefault = { + let bidderResquestDefault = { 'auctionId': '1d1a030790a475', 'bidderRequestId': '22edbae2733bf6', 'timeout': 3000 }; it('should send bid request to ENDPOINT via POST', function() { - const request = spec.buildRequests(bidRequests, bidderRequestDefault); + const request = spec.buildRequests(bidRequests, bidderResquestDefault); expect(request.url).to.equal(ENDPOINT); expect(request.method).to.equal('POST'); }); it('should not send auctionId in bid request ', function() { - const request = spec.buildRequests(bidRequests, bidderRequestDefault); + const request = spec.buildRequests(bidRequests, bidderResquestDefault); const payload = JSON.parse(request.data); expect(payload.data[0].auctionId).to.not.exist @@ -167,55 +169,6 @@ describe('teadsBidAdapter', () => { expect(payload.gdpr_iab.apiVersion).to.equal(2); }); - it('should add videoPlcmt to payload', function () { - let bidRequestWithVideoPlcmt = Object.assign({}, bidRequests[0], { - mediaTypes: { - video: { - plcmt: 1 - } - } - }); - - const request = spec.buildRequests([bidRequestWithVideoPlcmt], bidderRequestDefault); - const payload = JSON.parse(request.data); - - expect(payload.data[0].videoPlcmt).to.exist; - expect(payload.data[0].videoPlcmt).to.equal(1); - }); - - it('should not add videoPlcmt to payload if empty', function () { - let bidRequestWithNullVideoPlcmt = Object.assign({}, bidRequests[0], { - mediaTypes: { - video: { - plcmt: null - } - } - }); - - let bidRequestWithEmptyVideoPlcmt = Object.assign({}, bidRequests[0], { - mediaTypes: { - video: { - plcmt: '' - } - } - }); - - const request1 = spec.buildRequests([bidRequestWithNullVideoPlcmt], bidderRequestDefault); - const request2 = spec.buildRequests([bidRequestWithEmptyVideoPlcmt], bidderRequestDefault); - const payload1 = JSON.parse(request1.data); - const payload2 = JSON.parse(request2.data); - - expect(payload1.data[0].videoPlcmt).to.not.exist; - expect(payload2.data[0].videoPlcmt).to.not.exist; - }); - - it('should not add videoPlcmt to payload if it is not in bid request', function () { - const request = spec.buildRequests(bidRequests, bidderRequestDefault); - const payload = JSON.parse(request.data); - - expect(payload.data[0].videoPlcmt).to.not.exist; - }); - it('should add referer info to payload', function () { const bidRequest = Object.assign({}, bidRequests[0]) const bidderRequest = { @@ -233,7 +186,7 @@ describe('teadsBidAdapter', () => { }); it('should add networkBandwidth info to payload', function () { - const request = spec.buildRequests(bidRequests, bidderRequestDefault); + const request = spec.buildRequests(bidRequests, bidderResquestDefault); const payload = JSON.parse(request.data); const bandwidth = window.navigator && window.navigator.connection && window.navigator.connection.downlink; @@ -248,167 +201,15 @@ describe('teadsBidAdapter', () => { }); it('should add pageReferrer info to payload', function () { - const request = spec.buildRequests(bidRequests, bidderRequestDefault); + const request = spec.buildRequests(bidRequests, bidderResquestDefault); const payload = JSON.parse(request.data); expect(payload.pageReferrer).to.exist; expect(payload.pageReferrer).to.deep.equal(document.referrer); }); - it('should add screenOrientation info to payload', function () { - const request = spec.buildRequests(bidRequests, bidderRequestDefault); - const payload = JSON.parse(request.data); - const screenOrientation = window.top.screen.orientation?.type - - if (screenOrientation) { - expect(payload.screenOrientation).to.exist; - expect(payload.screenOrientation).to.deep.equal(screenOrientation); - } else expect(payload.screenOrientation).to.not.exist; - }); - - it('should add historyLength info to payload', function () { - const request = spec.buildRequests(bidRequests, bidderRequestDefault); - const payload = JSON.parse(request.data); - - expect(payload.historyLength).to.exist; - expect(payload.historyLength).to.deep.equal(window.top.history.length); - }); - - it('should add viewportHeight info to payload', function () { - const request = spec.buildRequests(bidRequests, bidderRequestDefault); - const payload = JSON.parse(request.data); - - expect(payload.viewportHeight).to.exist; - expect(payload.viewportHeight).to.deep.equal(window.top.visualViewport.height); - }); - - it('should add viewportWidth info to payload', function () { - const request = spec.buildRequests(bidRequests, bidderRequestDefault); - const payload = JSON.parse(request.data); - - expect(payload.viewportWidth).to.exist; - expect(payload.viewportWidth).to.deep.equal(window.top.visualViewport.width); - }); - - it('should add hardwareConcurrency info to payload', function () { - const request = spec.buildRequests(bidRequests, bidderRequestDefault); - const payload = JSON.parse(request.data); - const hardwareConcurrency = window.top.navigator?.hardwareConcurrency - - if (hardwareConcurrency) { - expect(payload.hardwareConcurrency).to.exist; - expect(payload.hardwareConcurrency).to.deep.equal(hardwareConcurrency); - } else expect(payload.hardwareConcurrency).to.not.exist - }); - - it('should add deviceMemory info to payload', function () { - const request = spec.buildRequests(bidRequests, bidderRequestDefault); - const payload = JSON.parse(request.data); - const deviceMemory = window.top.navigator.deviceMemory - - if (deviceMemory) { - expect(payload.deviceMemory).to.exist; - expect(payload.deviceMemory).to.deep.equal(deviceMemory); - } else expect(payload.deviceMemory).to.not.exist; - }); - - describe('pageTitle', function () { - it('should add pageTitle info to payload based on document title', function () { - const testText = 'This is a title'; - sandbox.stub(window.top.document, 'title').value(testText); - - const request = spec.buildRequests(bidRequests, bidderRequestDefault); - const payload = JSON.parse(request.data); - - expect(payload.pageTitle).to.exist; - expect(payload.pageTitle).to.deep.equal(testText); - }); - - it('should add pageTitle info to payload based on open-graph title', function () { - const testText = 'This is a title from open-graph'; - sandbox.stub(window.top.document, 'title').value(''); - sandbox.stub(window.top.document, 'querySelector').withArgs('meta[property="og:title"]').returns({ content: testText }); - - const request = spec.buildRequests(bidRequests, bidderRequestDefault); - const payload = JSON.parse(request.data); - - expect(payload.pageTitle).to.exist; - expect(payload.pageTitle).to.deep.equal(testText); - }); - - it('should add pageTitle info to payload sliced on 300 first characters', function () { - const testText = Array(500).join('a'); - sandbox.stub(window.top.document, 'title').value(testText); - - const request = spec.buildRequests(bidRequests, bidderRequestDefault); - const payload = JSON.parse(request.data); - - expect(payload.pageTitle).to.exist; - expect(payload.pageTitle).to.have.length(300); - }); - - it('should add pageTitle info to payload when fallbacking from window.top', function () { - const testText = 'This is a fallback title'; - sandbox.stub(window.top.document, 'querySelector').throws(); - sandbox.stub(document, 'title').value(testText); - - const request = spec.buildRequests(bidRequests, bidderRequestDefault); - const payload = JSON.parse(request.data); - - expect(payload.pageTitle).to.exist; - expect(payload.pageTitle).to.deep.equal(testText); - }); - }); - - describe('pageDescription', function () { - it('should add pageDescription info to payload based on open-graph description', function () { - const testText = 'This is a description'; - sandbox.stub(window.top.document, 'querySelector').withArgs('meta[name="description"]').returns({ content: testText }); - - const request = spec.buildRequests(bidRequests, bidderRequestDefault); - const payload = JSON.parse(request.data); - - expect(payload.pageDescription).to.exist; - expect(payload.pageDescription).to.deep.equal(testText); - }); - - it('should add pageDescription info to payload based on open-graph description', function () { - const testText = 'This is a description from open-graph'; - sandbox.stub(window.top.document, 'querySelector').withArgs('meta[property="og:description"]').returns({ content: testText }); - - const request = spec.buildRequests(bidRequests, bidderRequestDefault); - const payload = JSON.parse(request.data); - - expect(payload.pageDescription).to.exist; - expect(payload.pageDescription).to.deep.equal(testText); - }); - - it('should add pageDescription info to payload sliced on 300 first characters', function () { - const testText = Array(500).join('a'); - sandbox.stub(window.top.document, 'querySelector').withArgs('meta[name="description"]').returns({ content: testText }); - - const request = spec.buildRequests(bidRequests, bidderRequestDefault); - const payload = JSON.parse(request.data); - - expect(payload.pageDescription).to.exist; - expect(payload.pageDescription).to.have.length(300); - }); - - it('should add pageDescription info to payload when fallbacking from window.top', function () { - const testText = 'This is a fallback description'; - sandbox.stub(window.top.document, 'querySelector').throws(); - sandbox.stub(document, 'querySelector').withArgs('meta[name="description"]').returns({ content: testText }); - - const request = spec.buildRequests(bidRequests, bidderRequestDefault); - const payload = JSON.parse(request.data); - - expect(payload.pageDescription).to.exist; - expect(payload.pageDescription).to.deep.equal(testText); - }); - }); - it('should add timeToFirstByte info to payload', function () { - const request = spec.buildRequests(bidRequests, bidderRequestDefault); + const request = spec.buildRequests(bidRequests, bidderResquestDefault); const payload = JSON.parse(request.data); const performance = window.performance || window.webkitPerformance || window.msPerformance || window.mozPerformance; @@ -609,7 +410,7 @@ describe('teadsBidAdapter', () => { } }); - const request = spec.buildRequests([bidRequest], bidderRequestDefault); + const request = spec.buildRequests([bidRequest], bidderResquestDefault); const payload = JSON.parse(request.data); expect(payload.schain).to.exist; @@ -657,7 +458,7 @@ describe('teadsBidAdapter', () => { } }); - const requestWithUserAgentClientHints = spec.buildRequests([bidRequest], bidderRequestDefault); + const requestWithUserAgentClientHints = spec.buildRequests([bidRequest], bidderResquestDefault); const payload = JSON.parse(requestWithUserAgentClientHints.data); expect(payload.userAgentClientHints).to.exist; @@ -688,7 +489,7 @@ describe('teadsBidAdapter', () => { } ); - const defaultRequest = spec.buildRequests(bidRequests, bidderRequestDefault); + const defaultRequest = spec.buildRequests(bidRequests, bidderResquestDefault); expect(JSON.parse(defaultRequest.data).userAgentClientHints).to.not.exist; }); @@ -765,7 +566,7 @@ describe('teadsBidAdapter', () => { userId: {} // no property -> assumption that the system is disabled }; - const request = spec.buildRequests([bidRequest], bidderRequestDefault); + const request = spec.buildRequests([bidRequest], bidderResquestDefault); const payload = JSON.parse(request.data); for (const userId in userIdModules) { @@ -774,7 +575,7 @@ describe('teadsBidAdapter', () => { }); it(`should not add param to payload if user id field is absent`, function () { - const request = spec.buildRequests([baseBidRequest], bidderRequestDefault); + const request = spec.buildRequests([baseBidRequest], bidderResquestDefault); const payload = JSON.parse(request.data); for (const userId in userIdModules) { @@ -791,7 +592,7 @@ describe('teadsBidAdapter', () => { } }; - const request = spec.buildRequests([bidRequest], bidderRequestDefault); + const request = spec.buildRequests([bidRequest], bidderResquestDefault); const payload = JSON.parse(request.data); expect(payload).not.to.have.property('liveRampId'); @@ -811,7 +612,7 @@ describe('teadsBidAdapter', () => { userId: userIdObject }; - const request = spec.buildRequests([bidRequest], bidderRequestDefault); + const request = spec.buildRequests([bidRequest], bidderResquestDefault); const payload = JSON.parse(request.data); expect(payload['unifiedId2']).to.equal('unifiedId2-id'); @@ -831,7 +632,7 @@ describe('teadsBidAdapter', () => { describe('First-party cookie Teads ID', function () { it('should not add firstPartyCookieTeadsId param to payload if cookies are not enabled' + ' and teads user id not available', function () { - sandbox.stub(storage, 'cookiesAreEnabled').returns(false); + cookiesAreEnabledStub.returns(false); const bidRequest = { ...baseBidRequest, @@ -840,7 +641,7 @@ describe('teadsBidAdapter', () => { } }; - const request = spec.buildRequests([bidRequest], bidderRequestDefault); + const request = spec.buildRequests([bidRequest], bidderResquestDefault); const payload = JSON.parse(request.data); expect(payload).not.to.have.property('firstPartyCookieTeadsId'); @@ -848,8 +649,8 @@ describe('teadsBidAdapter', () => { it('should not add firstPartyCookieTeadsId param to payload if cookies are enabled ' + 'but first-party cookie and teads user id are not available', function () { - sandbox.stub(storage, 'cookiesAreEnabled').returns(true); - sandbox.stub(storage, 'getCookie').withArgs('_tfpvi').returns(undefined); + cookiesAreEnabledStub.returns(true); + getCookieStub.withArgs('_tfpvi').returns(undefined); const bidRequest = { ...baseBidRequest, @@ -858,7 +659,7 @@ describe('teadsBidAdapter', () => { } }; - const request = spec.buildRequests([bidRequest], bidderRequestDefault); + const request = spec.buildRequests([bidRequest], bidderResquestDefault); const payload = JSON.parse(request.data); expect(payload).not.to.have.property('firstPartyCookieTeadsId'); @@ -866,8 +667,8 @@ describe('teadsBidAdapter', () => { it('should add firstPartyCookieTeadsId from cookie if it\'s available ' + 'and teads user id is not', function () { - sandbox.stub(storage, 'cookiesAreEnabled').returns(true); - sandbox.stub(storage, 'getCookie').withArgs('_tfpvi').returns('my-teads-id'); + cookiesAreEnabledStub.returns(true); + getCookieStub.withArgs('_tfpvi').returns('my-teads-id'); const bidRequest = { ...baseBidRequest, @@ -876,7 +677,7 @@ describe('teadsBidAdapter', () => { } }; - const request = spec.buildRequests([bidRequest], bidderRequestDefault); + const request = spec.buildRequests([bidRequest], bidderResquestDefault); const payload = JSON.parse(request.data); @@ -885,8 +686,8 @@ describe('teadsBidAdapter', () => { it('should add firstPartyCookieTeadsId from user id module if it\'s available ' + 'even if cookie is available too', function () { - sandbox.stub(storage, 'cookiesAreEnabled').returns(true); - sandbox.stub(storage, 'getCookie').withArgs('_tfpvi').returns('my-teads-id'); + cookiesAreEnabledStub.returns(true); + getCookieStub.withArgs('_tfpvi').returns('my-teads-id'); const bidRequest = { ...baseBidRequest, @@ -896,7 +697,7 @@ describe('teadsBidAdapter', () => { } }; - const request = spec.buildRequests([bidRequest], bidderRequestDefault); + const request = spec.buildRequests([bidRequest], bidderResquestDefault); const payload = JSON.parse(request.data); @@ -949,7 +750,7 @@ describe('teadsBidAdapter', () => { }; } ); - const request = spec.buildRequests(updatedBidRequests, bidderRequestDefault); + const request = spec.buildRequests(updatedBidRequests, bidderResquestDefault); const payload = JSON.parse(request.data); expect(payload.data[0].gpid).to.equal('1111/home-left-0'); @@ -966,7 +767,7 @@ describe('teadsBidAdapter', () => { } })); - const request = spec.buildRequests(updatedBidRequests, bidderRequestDefault); + const request = spec.buildRequests(updatedBidRequests, bidderResquestDefault); const payload = JSON.parse(request.data); return payload.data.forEach(bid => { @@ -983,7 +784,7 @@ describe('teadsBidAdapter', () => { } })); - const request = spec.buildRequests(updatedBidRequests, bidderRequestDefault); + const request = spec.buildRequests(updatedBidRequests, bidderResquestDefault); const payload = JSON.parse(request.data); return payload.data.forEach(bid => { @@ -994,7 +795,7 @@ describe('teadsBidAdapter', () => { function checkMediaTypesSizes(mediaTypes, expectedSizes) { const bidRequestWithBannerSizes = Object.assign(bidRequests[0], mediaTypes); - const requestWithBannerSizes = spec.buildRequests([bidRequestWithBannerSizes], bidderRequestDefault); + const requestWithBannerSizes = spec.buildRequests([bidRequestWithBannerSizes], bidderResquestDefault); const payloadWithBannerSizes = JSON.parse(requestWithBannerSizes.data); return payloadWithBannerSizes.data.forEach(bid => { diff --git a/test/spec/modules/teadsIdSystem_spec.js b/test/spec/modules/teadsIdSystem_spec.js index 1959b990957..7b977e2fb2b 100644 --- a/test/spec/modules/teadsIdSystem_spec.js +++ b/test/spec/modules/teadsIdSystem_spec.js @@ -218,7 +218,7 @@ describe('TeadsIdSystem', function () { callback(callbackSpy); const request = server.requests[0]; expect(request.url).to.include(teadsUrl); - request.respond(204); + request.respond(204, null, 'Unavailable'); expect(logInfoStub.calledOnce).to.be.true; }); diff --git a/test/spec/modules/theAdxBidAdapter_spec.js b/test/spec/modules/theAdxBidAdapter_spec.js index eb00834421a..99e5156190c 100644 --- a/test/spec/modules/theAdxBidAdapter_spec.js +++ b/test/spec/modules/theAdxBidAdapter_spec.js @@ -446,78 +446,6 @@ describe('TheAdxAdapter', function () { expect(processedBid.currency).to.equal(responseCurrency); }); - it('returns a valid deal bid response on sucessful banner request with deal', function () { - let incomingRequestId = 'XXtestingXX'; - let responsePrice = 3.14 - - let responseCreative = 'sample_creative&{FOR_COVARAGE}'; - - let responseCreativeId = '274'; - let responseCurrency = 'TRY'; - - let responseWidth = 300; - let responseHeight = 250; - let responseTtl = 213; - let dealId = 'theadx_deal_id'; - - let sampleResponse = { - id: '66043f5ca44ecd8f8769093b1615b2d9', - seatbid: [{ - bid: [{ - id: 'c21bab0e-7668-4d8f-908a-63e094c09197', - dealid: 'theadx_deal_id', - impid: '1', - price: responsePrice, - adid: responseCreativeId, - crid: responseCreativeId, - adm: responseCreative, - adomain: [ - 'www.domain.com' - ], - cid: '274', - attr: [], - w: responseWidth, - h: responseHeight, - ext: { - ttl: responseTtl - } - }], - seat: '201', - group: 0 - }], - bidid: 'c21bab0e-7668-4d8f-908a-63e094c09197', - cur: responseCurrency - }; - - let sampleRequest = { - bidId: incomingRequestId, - mediaTypes: { - banner: {} - }, - requestId: incomingRequestId, - deals: [{id: dealId}] - }; - let serverResponse = { - body: sampleResponse - } - let result = spec.interpretResponse(serverResponse, sampleRequest); - - expect(result.length).to.equal(1); - - let processedBid = result[0]; - - // expect(processedBid.requestId).to.equal(incomingRequestId); - expect(processedBid.cpm).to.equal(responsePrice); - expect(processedBid.width).to.equal(responseWidth); - expect(processedBid.height).to.equal(responseHeight); - expect(processedBid.ad).to.equal(responseCreative); - expect(processedBid.ttl).to.equal(responseTtl); - expect(processedBid.creativeId).to.equal(responseCreativeId); - expect(processedBid.netRevenue).to.equal(true); - expect(processedBid.currency).to.equal(responseCurrency); - expect(processedBid.dealId).to.equal(dealId); - }); - it('returns an valid bid response on sucessful video request', function () { let incomingRequestId = 'XXtesting-275XX'; let responsePrice = 6 diff --git a/test/spec/modules/topicsFpdModule_spec.js b/test/spec/modules/topicsFpdModule_spec.js index bc7df85db0d..958489f2728 100644 --- a/test/spec/modules/topicsFpdModule_spec.js +++ b/test/spec/modules/topicsFpdModule_spec.js @@ -1,313 +1,289 @@ import { - getCachedTopics, getTopics, getTopicsData, - loadTopicsForBidders, processFpd, + hasGDPRConsent, + getCachedTopics, receiveMessage, - reset, topicStorageName } from '../../../modules/topicsFpdModule.js'; -import {config} from 'src/config.js'; import {deepClone, safeJSONParse} from '../../../src/utils.js'; +import {gdprDataHandler} from 'src/adapterManager.js'; import {getCoreStorageManager} from 'src/storageManager.js'; -import * as activities from '../../../src/activities/rules.js'; -import {ACTIVITY_ENRICH_UFPD} from '../../../src/activities/activities.js'; - -describe('topics', () => { - beforeEach(() => { - reset(); - }); - - describe('getTopicsData', () => { - function makeTopic(topic, modelv, taxv = '1') { - return { - topic, - taxonomyVersion: taxv, - modelVersion: modelv - }; - } - function byTaxClass(segments) { - return segments.reduce((memo, segment) => { - memo[`${segment.ext.segtax}:${segment.ext.segclass}`] = segment; - return memo; - }, {}); - } - - [ - { - t: 'no topics', - topics: [], - expected: [] - }, - { - t: 'single topic', - topics: [makeTopic(123, 'm1')], - expected: [ - { - ext: { - segtax: 600, - segclass: 'm1' - }, - segment: [ - {id: '123'} - ] - } - ] - }, - { - t: 'multiple topics with the same model version', - topics: [makeTopic(123, 'm1'), makeTopic(321, 'm1')], - expected: [ - { - ext: { - segtax: 600, - segclass: 'm1' - }, - segment: [ - {id: '123'}, - {id: '321'} - ] - } - ] - }, - { - t: 'multiple topics with different model versions', - topics: [makeTopic(1, 'm1'), makeTopic(2, 'm1'), makeTopic(3, 'm2')], - expected: [ - { - ext: { - segtax: 600, - segclass: 'm1' - }, - segment: [ - {id: '1'}, - {id: '2'} - ] +describe('getTopicsData', () => { + function makeTopic(topic, modelv, taxv = '1') { + return { + topic, + taxonomyVersion: taxv, + modelVersion: modelv + }; + } + + function byTaxClass(segments) { + return segments.reduce((memo, segment) => { + memo[`${segment.ext.segtax}:${segment.ext.segclass}`] = segment; + return memo; + }, {}); + } + + [ + { + t: 'no topics', + topics: [], + expected: [] + }, + { + t: 'single topic', + topics: [makeTopic(123, 'm1')], + expected: [ + { + ext: { + segtax: 600, + segclass: 'm1' }, - { - ext: { - segtax: 600, - segclass: 'm2' - }, - segment: [ - {id: '3'} - ] - } - ] - }, - { - t: 'multiple topics, some with a taxonomy version other than "1"', - topics: [makeTopic(123, 'm1'), makeTopic(321, 'm1', 'other')], - expected: [ - { - ext: { - segtax: 600, - segclass: 'm1' - }, - segment: [ - {id: '123'} - ] - } - ] + segment: [ + {id: '123'} + ] + } + ] + }, + { + t: 'multiple topics with the same model version', + topics: [makeTopic(123, 'm1'), makeTopic(321, 'm1')], + expected: [ + { + ext: { + segtax: 600, + segclass: 'm1' + }, + segment: [ + {id: '123'}, + {id: '321'} + ] + } + ] + }, + { + t: 'multiple topics with different model versions', + topics: [makeTopic(1, 'm1'), makeTopic(2, 'm1'), makeTopic(3, 'm2')], + expected: [ + { + ext: { + segtax: 600, + segclass: 'm1' + }, + segment: [ + {id: '1'}, + {id: '2'} + ] + }, + { + ext: { + segtax: 600, + segclass: 'm2' + }, + segment: [ + {id: '3'} + ] + } + ] + }, + { + t: 'multiple topics, some with a taxonomy version other than "1"', + topics: [makeTopic(123, 'm1'), makeTopic(321, 'm1', 'other')], + expected: [ + { + ext: { + segtax: 600, + segclass: 'm1' + }, + segment: [ + {id: '123'} + ] + } + ] + }, + { + t: 'multiple topics in multiple taxonomies', + taxonomies: { + '1': 600, + '2': 601 }, - { - t: 'multiple topics in multiple taxonomies', - taxonomies: { - '1': 600, - '2': 601 + topics: [ + makeTopic(123, 'm1', '1'), + makeTopic(321, 'm1', '2'), + makeTopic(213, 'm2', '1'), + ], + expected: [ + { + ext: { + segtax: 600, + segclass: 'm1' + }, + segment: [ + {id: '123'} + ] }, - topics: [ - makeTopic(123, 'm1', '1'), - makeTopic(321, 'm1', '2'), - makeTopic(213, 'm2', '1'), - ], - expected: [ - { - ext: { - segtax: 600, - segclass: 'm1' - }, - segment: [ - {id: '123'} - ] + { + ext: { + segtax: 601, + segclass: 'm1', }, - { - ext: { - segtax: 601, - segclass: 'm1', - }, - segment: [ - {id: '321'} - ] + segment: [ + {id: '321'} + ] + }, + { + ext: { + segtax: 600, + segclass: 'm2' }, - { - ext: { - segtax: 600, - segclass: 'm2' - }, - segment: [ - {id: '213'} - ] - } - ] - } - ].forEach(({t, topics, expected, taxonomies}) => { - describe(`on ${t}`, () => { - it('should convert topics to user.data segments correctly', () => { - const actual = getTopicsData('mockName', topics, taxonomies); - expect(actual.length).to.eql(expected.length); - expected = byTaxClass(expected); - Object.entries(byTaxClass(actual)).forEach(([key, datum]) => { - sinon.assert.match(datum, expected[key]); - expect(datum.name).to.equal('mockName'); - }); + segment: [ + {id: '213'} + ] + } + ] + } + ].forEach(({t, topics, expected, taxonomies}) => { + describe(`on ${t}`, () => { + it('should convert topics to user.data segments correctly', () => { + const actual = getTopicsData('mockName', topics, taxonomies); + expect(actual.length).to.eql(expected.length); + expected = byTaxClass(expected); + Object.entries(byTaxClass(actual)).forEach(([key, datum]) => { + sinon.assert.match(datum, expected[key]); + expect(datum.name).to.equal('mockName'); }); + }); - it('should not set name if null', () => { - getTopicsData(null, topics).forEach((data) => { - expect(data.hasOwnProperty('name')).to.be.false; - }); + it('should not set name if null', () => { + getTopicsData(null, topics).forEach((data) => { + expect(data.hasOwnProperty('name')).to.be.false; }); }); }); }); +}); - describe('getTopics', () => { - Object.entries({ - 'document with no browsingTopics': {}, - 'document that disallows topics': { - featurePolicy: { - allowsFeature: sinon.stub().returns(false) - } - }, - 'document that throws on featurePolicy': { - browsingTopics: sinon.stub(), - get featurePolicy() { - throw new Error(); - } - }, - 'document that throws on browsingTopics': { - browsingTopics: sinon.stub().callsFake(() => { - throw new Error(); - }), - featurePolicy: { - allowsFeature: sinon.stub().returns(true) - } - }, - }).forEach(([t, doc]) => { - it(`should resolve to an empty list on ${t}`, () => { - return getTopics(doc).then((topics) => { - expect(topics).to.eql([]); - }); +describe('getTopics', () => { + Object.entries({ + 'document with no browsingTopics': {}, + 'document that disallows topics': { + featurePolicy: { + allowsFeature: sinon.stub().returns(false) + } + }, + 'document that throws on featurePolicy': { + browsingTopics: sinon.stub(), + get featurePolicy() { + throw new Error(); + } + }, + 'document that throws on browsingTopics': { + browsingTopics: sinon.stub().callsFake(() => { + throw new Error(); + }), + featurePolicy: { + allowsFeature: sinon.stub().returns(true) + } + }, + }).forEach(([t, doc]) => { + it(`should resolve to an empty list on ${t}`, () => { + return getTopics(doc).then((topics) => { + expect(topics).to.eql([]); }); }); + }); - it('should call `document.browsingTopics` when allowed', () => { - const topics = ['t1', 't2']; - return getTopics({ - browsingTopics: sinon.stub().returns(Promise.resolve(topics)), - featurePolicy: { - allowsFeature: sinon.stub().returns(true) - } - }).then((actual) => { - expect(actual).to.eql(topics); - }); + it('should call `document.browsingTopics` when allowed', () => { + const topics = ['t1', 't2']; + return getTopics({ + browsingTopics: sinon.stub().returns(Promise.resolve(topics)), + featurePolicy: { + allowsFeature: sinon.stub().returns(true) + } + }).then((actual) => { + expect(actual).to.eql(topics); }); }); +}); - describe('processFpd', () => { - const mockData = [ - { - name: 'domain', - segment: [{id: 123}] - }, - { - name: 'domain', - segment: [{id: 321}] +describe('processFpd', () => { + const mockData = [ + { + name: 'domain', + segment: [{id: 123}] + }, + { + name: 'domain', + segment: [{id: 321}] + } + ]; + + it('should add topics data', () => { + return processFpd({}, {global: {}}, {data: Promise.resolve(mockData)}) + .then(({global}) => { + expect(global.user.data).to.eql(mockData); + }); + }); + + it('should apppend to existing user.data', () => { + const global = { + user: { + data: [ + {name: 'preexisting'}, + ] } - ]; + }; + 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 add topics data', () => { - return processFpd({}, {global: {}}, {data: Promise.resolve(mockData)}) - .then(({global}) => { - expect(global.user.data).to.eql(mockData); - }); - }); + it('should not modify fpd when there is no data', () => { + return processFpd({}, {global: {}}, {data: Promise.resolve([])}) + .then((data) => { + expect(data.global).to.eql({}); + }); + }); +}); - it('should apppend to existing user.data', () => { - const global = { - user: { - data: [ - {name: 'preexisting'}, - ] - } - }; - return processFpd({}, {global: deepClone(global)}, {data: Promise.resolve(mockData)}) - .then((data) => { - expect(data.global.user.data).to.eql(global.user.data.concat(mockData)); - }); - }); +describe('Topics Module GDPR consent check', () => { + let gdprDataHdlrStub; + beforeEach(() => { + gdprDataHdlrStub = sinon.stub(gdprDataHandler, 'getConsentData'); + }); - it('should not modify fpd when there is no data', () => { - return processFpd({}, {global: {}}, {data: Promise.resolve([])}) - .then((data) => { - expect(data.global).to.eql({}); - }); - }); + afterEach(() => { + gdprDataHdlrStub.restore(); }); - describe('loadTopicsForBidders', () => { - beforeEach(() => { - config.setConfig({ - userSync: { - topics: { - bidders: [{ - bidder: 'mockBidder', - iframeURL: 'https://mock.iframe' - }] - } - } - }) - }); - afterEach(() => { - config.resetConfig(); - }) + it('should return false when GDPR is applied but consent string is not present', () => { + const consentString = ''; + const consentConfig = { + consentString: consentString, + gdprApplies: true, + vendorData: {} + }; + gdprDataHdlrStub.returns(consentConfig); + expect(hasGDPRConsent()).to.equal(false); + }); - Object.entries({ - 'support': {}, - 'allow': { - browsingTopics: true, - featurePolicy: { - allowsFeature(feature) { - return feature !== 'browsing-topics'; - } - } - }, - }).forEach(([t, doc]) => { - it(`does not attempt to load frames if browser does not ${t} topics`, () => { - doc.createElement = sinon.stub(); - loadTopicsForBidders(doc); - sinon.assert.notCalled(doc.createElement); - }); - }); + it('should return true when GDPR doesn\'t apply', () => { + const consentString = 'CPi8wgAPi8wgAADABBENCrCsAP_AAH_AAAAAISNB7D=='; + const consentConfig = { + consentString: consentString, + gdprApplies: false, + vendorData: {} + }; + + gdprDataHdlrStub.returns(consentConfig); + expect(hasGDPRConsent()).to.equal(true); }); - describe('getCachedTopics()', () => { - const storage = getCoreStorageManager('topicsFpd'); - const expected = [{ - ext: { - segtax: 600, - segclass: '2206021246' - }, - segment: [{ - 'id': '243' - }, { - 'id': '265' - }], - name: 'ads.pubmatic.com' - }]; + it('should return true when GDPR is applied and purpose consent is true for all purpose[1,2,3,4]', () => { const consentString = 'CPi8wgAPi8wgAADABBENCrCsAP_AAH_AAAAAISNB7D=='; const consentConfig = { consentString: consentString, @@ -325,159 +301,93 @@ describe('topics', () => { } } }; - const mockData = [ - { - name: 'domain', - segment: [{id: 123}] - }, - { - name: 'domain', - segment: [{id: 321}], - } - ]; - - const evt = { - data: '{"segment":{"domain":"ads.pubmatic.com","topics":[{"configVersion":"chrome.1","modelVersion":"2206021246","taxonomyVersion":"1","topic":165,"version":"chrome.1:1:2206021246"}],"bidder":"pubmatic"},"date":1669743901858}', - origin: 'https://ads.pubmatic.com' - }; - - afterEach(() => { - storage.removeDataFromLocalStorage(topicStorageName); - }); - - describe('when cached data is available and not expired', () => { - let sandbox; - beforeEach(() => { - sandbox = sinon.sandbox.create(); - const storedSegments = JSON.stringify( - [['pubmatic', { - '2206021246': { - 'ext': {'segtax': 600, 'segclass': '2206021246'}, - 'segment': [{'id': '243'}, {'id': '265'}], - 'name': 'ads.pubmatic.com' - }, - 'lastUpdated': new Date().getTime() - }]] - ); - storage.setDataInLocalStorage(topicStorageName, storedSegments); - }); - afterEach(() => { - sandbox.restore(); - }); - - it('should return segments for bidder if transmitUfpd is allowed', () => { - assert.deepEqual(getCachedTopics(), expected); - }); - - it('should NOT return segments for bidder if enrichUfpd is NOT allowed', () => { - sandbox.stub(activities, 'isActivityAllowed').callsFake((activity, params) => { - return !(activity === ACTIVITY_ENRICH_UFPD && params.component === 'bidder.pubmatic'); - }); - expect(getCachedTopics()).to.eql([]); - }); - }) - it('should return empty segments for bidder if there is cached segments stored which is expired', () => { - let storedSegments = '[["pubmatic",{"2206021246":{"ext":{"segtax":600,"segclass":"2206021246"},"segment":[{"id":"243"},{"id":"265"}],"name":"ads.pubmatic.com"},"lastUpdated":10}]]'; - storage.setDataInLocalStorage(topicStorageName, storedSegments); - assert.deepEqual(getCachedTopics(), []); - }); + gdprDataHdlrStub.returns(consentConfig); + expect(hasGDPRConsent()).to.equal(true); + }); - describe('cross-frame messages', () => { - beforeEach(() => { - // init iframe logic so that the receiveMessage origin check passes - loadTopicsForBidders({ - browsingTopics: true, - featurePolicy: { - allowsFeature() { return true } - }, - createElement: sinon.stub().callsFake(() => ({style: {}})), - documentElement: { - appendChild() {} + it('should return false when GDPR is applied and purpose consent is false for one of the purpose[1,2,3,4]', () => { + const consentString = 'CPi8wgAPi8wgAADABBENCrCsAP_AAH_AAAAAISNB7D=='; + const consentConfig = { + consentString: consentString, + gdprApplies: true, + vendorData: { + metadata: consentString, + gdprApplies: true, + purpose: { + consents: { + 1: true, + 2: true, + 3: true, + 4: false } - }); - }); - - it('should store segments if receiveMessage event is triggered with segment data', () => { - receiveMessage(evt); - let segments = new Map(safeJSONParse(storage.getDataFromLocalStorage(topicStorageName))); - expect(segments.has('pubmatic')).to.equal(true); - }); + } + } + }; - it('should update stored segments if receiveMessage event is triggerred with segment data', () => { - let storedSegments = '[["pubmatic",{"2206021246":{"ext":{"segtax":600,"segclass":"2206021246"},"segment":[{"id":"243"},{"id":"265"}],"name":"ads.pubmatic.com"},"lastUpdated":1669719242027}]]'; - storage.setDataInLocalStorage(topicStorageName, storedSegments); - receiveMessage(evt); - let segments = new Map(safeJSONParse(storage.getDataFromLocalStorage(topicStorageName))); - expect(segments.get('pubmatic')[2206021246].segment.length).to.equal(1); - }); - }); + gdprDataHdlrStub.returns(consentConfig); + expect(hasGDPRConsent()).to.equal(false); }); }); -describe('handles fetch request for topics api headers', () => { - let stubbedFetch; +describe('getCachedTopics()', () => { const storage = getCoreStorageManager('topicsFpd'); + const expected = [{ + ext: { + segtax: 600, + segclass: '2206021246' + }, + segment: [{ + 'id': '243' + }, { + 'id': '265' + }], + name: 'ads.pubmatic.com' + }]; + const consentString = 'CPi8wgAPi8wgAADABBENCrCsAP_AAH_AAAAAISNB7D=='; + const consentConfig = { + consentString: consentString, + gdprApplies: true, + vendorData: { + metadata: consentString, + gdprApplies: true, + purpose: { + consents: { + 1: true, + 2: true, + 3: true, + 4: true + } + } + } + }; + const mockData = [ + { + name: 'domain', + segment: [{id: 123}] + }, + { + name: 'domain', + segment: [{id: 321}], + } + ]; + + const evt = { + data: '{"segment":{"domain":"ads.pubmatic.com","topics":[{"configVersion":"chrome.1","modelVersion":"2206021246","taxonomyVersion":"1","topic":165,"version":"chrome.1:1:2206021246"}],"bidder":"pubmatic"},"date":1669743901858}', + origin: 'https://ads.pubmatic.com' + }; + let gdprDataHdlrStub; beforeEach(() => { - stubbedFetch = sinon.stub(window, 'fetch'); + gdprDataHdlrStub = sinon.stub(gdprDataHandler, 'getConsentData'); }); afterEach(() => { - stubbedFetch.restore(); storage.removeDataFromLocalStorage(topicStorageName); + gdprDataHdlrStub.restore(); }); - it('should make a fetch call when a fetchUrl is present for a selected bidder', () => { - config.setConfig({ - userSync: { - topics: { - maxTopicCaller: 3, - bidders: [ - { - bidder: 'pubmatic', - fetchUrl: 'http://localhost:3000/topics-server.js' - } - ], - }, - } - }); - - stubbedFetch.returns(Promise.resolve(true)); - loadTopicsForBidders({ - browsingTopics: true, - featurePolicy: { - allowsFeature() { return true } - } - }); - sinon.assert.calledOnce(stubbedFetch); - stubbedFetch.calledWith('http://localhost:3000/topics-server.js'); - }); - - it('should not make a fetch call when a fetchUrl is not present for a selected bidder', () => { - config.setConfig({ - userSync: { - topics: { - maxTopicCaller: 3, - bidders: [ - { - bidder: 'pubmatic' - } - ], - }, - } - }); - - loadTopicsForBidders({ - browsingTopics: true, - featurePolicy: { - allowsFeature() { return true } - } - }); - sinon.assert.notCalled(stubbedFetch); - }); - - it('a fetch request should not be made if the configured fetch rate duration has not yet passed', () => { + it('should return segments for bidder if GDPR consent is true and there is cached segments stored which is not expired', () => { const storedSegments = JSON.stringify( [['pubmatic', { '2206021246': { @@ -488,30 +398,35 @@ describe('handles fetch request for topics api headers', () => { 'lastUpdated': new Date().getTime() }]] ); + storage.setDataInLocalStorage(topicStorageName, storedSegments); + gdprDataHdlrStub.returns(consentConfig); + assert.deepEqual(getCachedTopics(), expected); + }); + it('should return empty segments for bidder if GDPR consent is true and there is cached segments stored which is expired', () => { + let storedSegments = '[["pubmatic",{"2206021246":{"ext":{"segtax":600,"segclass":"2206021246"},"segment":[{"id":"243"},{"id":"265"}],"name":"ads.pubmatic.com"},"lastUpdated":10}]]'; storage.setDataInLocalStorage(topicStorageName, storedSegments); + gdprDataHdlrStub.returns(consentConfig); + assert.deepEqual(getCachedTopics(), []); + }); - config.setConfig({ - userSync: { - topics: { - maxTopicCaller: 3, - bidders: [ - { - bidder: 'pubmatic', - fetchUrl: 'http://localhost:3000/topics-server.js', - fetchRate: 1 // in days. 1 fetch per day - } - ], - }, - } - }); + it('should stored segments if receiveMessage event is triggerred with segment data', () => { + return processFpd({}, {global: {}}, {data: Promise.resolve(mockData)}) + .then(({global}) => { + receiveMessage(evt); + let segments = new Map(safeJSONParse(storage.getDataFromLocalStorage(topicStorageName))); + expect(segments.has('pubmatic')).to.equal(true); + }); + }); - loadTopicsForBidders({ - browsingTopics: true, - featurePolicy: { - allowsFeature() { return true } - } - }); - sinon.assert.notCalled(stubbedFetch); + it('should update stored segments if receiveMessage event is triggerred with segment data', () => { + let storedSegments = '[["pubmatic",{"2206021246":{"ext":{"segtax":600,"segclass":"2206021246"},"segment":[{"id":"243"},{"id":"265"}],"name":"ads.pubmatic.com"},"lastUpdated":1669719242027}]]'; + storage.setDataInLocalStorage(topicStorageName, storedSegments); + return processFpd({}, {global: {}}, {data: Promise.resolve(mockData)}) + .then(({global}) => { + receiveMessage(evt); + let segments = new Map(safeJSONParse(storage.getDataFromLocalStorage(topicStorageName))); + expect(segments.get('pubmatic')[2206021246].segment.length).to.equal(1); + }); }); }); diff --git a/test/spec/modules/tpmnBidAdapter_spec.js b/test/spec/modules/tpmnBidAdapter_spec.js index 505bc9d878f..e2b14b18f61 100644 --- a/test/spec/modules/tpmnBidAdapter_spec.js +++ b/test/spec/modules/tpmnBidAdapter_spec.js @@ -1,130 +1,16 @@ /* eslint-disable no-tabs */ -import { spec, storage, VIDEO_RENDERER_URL, ADAPTER_VERSION } from 'modules/tpmnBidAdapter.js'; -import { generateUUID } from '../../../src/utils.js'; -import { expect } from 'chai'; -import * as utils from 'src/utils'; +import {expect} from 'chai'; +import {spec, storage} from 'modules/tpmnBidAdapter.js'; +import {generateUUID} from '../../../src/utils.js'; +import {newBidder} from '../../../src/adapters/bidderFactory'; import * as sinon from 'sinon'; -import 'modules/consentManagement.js'; -import {syncAddFPDToBidderRequest} from '../../helpers/fpd.js'; -import {mockGdprConsent} from '../../helpers/consentData.js'; - -const BIDDER_CODE = 'tpmn'; -const BANNER_BID = { - bidder: BIDDER_CODE, - params: { - inventoryId: 1 - }, - mediaTypes: { - banner: { - sizes: [ - [300, 250] - ], - }, - }, - adUnitCode: 'adUnitCode1', - bidId: 'bidId', - bidderRequestId: 'bidderRequestId', - auctionId: 'auctionId-56a2-4f71-9098-720a68f2f708', -}; - -const VIDEO_BID = { - bidder: BIDDER_CODE, - params: { - inventoryId: 1 - }, - mediaTypes: { - video: { - context: 'outstream', - api: [1, 2, 4, 6], - mimes: ['video/mp4'], - playbackmethod: [2, 4, 6], - playerSize: [[1024, 768]], - protocols: [3, 4, 7, 8, 10], - placement: 1, - plcmt: 1, - minduration: 0, - maxduration: 60, - startdelay: 0 - }, - }, - adUnitCode: 'adUnitCode1', - bidId: 'bidId', - bidderRequestId: 'bidderRequestId', - auctionId: 'auctionId-56a2-4f71-9098-720a68f2f708', -}; - -const BIDDER_REQUEST = { - auctionId: 'auctionId-56a2-4f71-9098-720a68f2f708', - bidderRequestId: 'bidderRequestId', - timeout: 500, - refererInfo: { - page: 'https://hello-world-page.com/', - domain: 'hello-world-page.com', - ref: 'http://example-domain.com/foo', - } -}; - -const BANNER_BID_RESPONSE = { - 'id': 'bidderRequestId', - 'bidId': 'bidid', - 'seatbid': [ - { - 'bid': [ - { - 'id': 'id', - 'impid': 'bidId', - 'price': 0.18, - 'adm': '', - 'adid': '144762342', - 'burl': 'http://0.0.0.0:8181/burl', - 'adomain': [ - 'https://dummydomain.com' - ], - 'cid': 'cid', - 'crid': 'crid', - 'iurl': 'iurl', - 'cat': [], - 'w': 300, - 'h': 250 - } - ] - } - ], - 'cur': 'USD' -}; - -const VIDEO_BID_RESPONSE = { - 'id': 'bidderRequestId', - 'bidid': 'bidid', - 'seatbid': [ - { - 'bid': [ - { - 'id': 'id', - 'impid': 'bidId', - 'price': 1.09, - 'adid': '144762342', - 'burl': 'http://0.0.0.0:8181/burl', - 'adm': '', - 'adomain': [ - 'https://dummydomain.com' - ], - 'cid': 'cid', - 'crid': 'crid', - 'iurl': 'iurl', - 'cat': [], - 'h': 768, - 'w': 1024 - } - ] - } - ], - 'cur': 'USD' -}; describe('tpmnAdapterTests', function () { + const adapter = newBidder(spec); + const BIDDER_CODE = 'tpmn'; let sandbox = sinon.sandbox.create(); let getCookieStub; + beforeEach(function () { $$PREBID_GLOBAL$$.bidderSettings = { tpmn: { @@ -141,277 +27,152 @@ describe('tpmnAdapterTests', function () { $$PREBID_GLOBAL$$.bidderSettings = {}; }); - describe('isBidRequestValid()', function () { - it('should accept request if placementId is passed', function () { - let bid = { - bidder: BIDDER_CODE, - params: { - inventoryId: 123 - }, - mediaTypes: { - banner: { - sizes: [[300, 250]] - } - } - }; - expect(spec.isBidRequestValid(bid)).to.equal(true); - }); - - it('should reject requests without params', function () { - let bid = { - bidder: BIDDER_CODE, - params: {} - }; - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - - it('should return true when required params found', () => { - expect(spec.isBidRequestValid(BANNER_BID)).to.equal(true); - expect(spec.isBidRequestValid(VIDEO_BID)).to.equal(true); - }); + describe('inherited functions', function () { + it('exists and is a function', function () { + expect(adapter.callBids).to.exist.and.to.be.a('function') + }) }); - describe('buildRequests()', function () { - it('should have gdpr data if applicable', function () { - const bid = utils.deepClone(BANNER_BID); - - const req = syncAddFPDToBidderRequest(Object.assign({}, BIDDER_REQUEST, { - gdprConsent: { - consentString: 'consentString', - gdprApplies: true, + describe('isBidRequestValid', function () { + let bid = { + adUnitCode: 'temp-unitcode', + bidder: 'tpmn', + params: { + inventoryId: '1', + publisherId: 'TPMN' + }, + bidId: '29092404798c9', + bidderRequestId: 'a01', + auctionId: 'da1d7a33-0260-4e83-a621-14674116f3f9', + mediaTypes: { + banner: { + sizes: [[300, 250]] } - })); - let request = spec.buildRequests([bid], req)[0]; + } + }; - const payload = request.data; - expect(payload.user.ext).to.have.property('consent', req.gdprConsent.consentString); - expect(payload.regs.ext).to.have.property('gdpr', 1); + it('should return true if a bid is valid banner bid request', function () { + expect(spec.isBidRequestValid(bid)).to.be.equal(true); }); - 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'], battr: [1] }, - mediaTypes: { banner: { battr: [1] } } - }); - - let [request] = spec.buildRequests([bid], BIDDER_REQUEST); - - expect(request).to.exist.and.to.be.an('object'); - const payload = request.data; - expect(payload).to.have.deep.property('bcat', ['IAB1-1']); - expect(payload).to.have.deep.property('badv', ['example.com']); - expect(payload).to.have.deep.property('bapp', ['com.example']); - expect(payload.imp[0].banner).to.have.deep.property('battr', [1]); + it('should return false where requried param is missing', function () { + let bid = Object.assign({}, bid); + bid.params = {}; + expect(spec.isBidRequestValid(bid)).to.be.equal(false); }); - context('when mediaType is banner', function () { - it('should build correct request for banner bid with both w, h', () => { - const bid = utils.deepClone(BANNER_BID); + it('should return false when required param values have invalid type', function () { + let bid = Object.assign({}, bid); + bid.params = { + 'inventoryId': null, + 'publisherId': null + }; + expect(spec.isBidRequestValid(bid)).to.be.equal(false); + }); + }); - const [request] = spec.buildRequests([bid], BIDDER_REQUEST); - const requestData = request.data; - // expect(requestData.imp[0].banner).to.equal(null); - expect(requestData.imp[0].banner.format[0].w).to.equal(300); - expect(requestData.imp[0].banner.format[0].h).to.equal(250); + describe('buildRequests', function () { + it('should return an empty list if there are no bid requests', function () { + const emptyBidRequests = []; + const bidderRequest = {}; + const request = spec.buildRequests(emptyBidRequests, bidderRequest); + expect(request).to.be.an('array').that.is.empty; + }); + it('should generate a POST server request with bidder API url, data', function () { + const bid = { + adUnitCode: 'temp-unitcode', + bidder: 'tpmn', + params: { + inventoryId: '1', + publisherId: 'TPMN' + }, + bidId: '29092404798c9', + bidderRequestId: 'a01', + auctionId: 'da1d7a33-0260-4e83-a621-14674116f3f9', + mediaTypes: { + banner: { + sizes: [[300, 250]] + } + } + }; + const tempBidRequests = [bid]; + const tempBidderRequest = { + refererInfo: { + page: 'http://localhost/test', + site: { + domain: 'localhost', + page: 'http://localhost/test' + } + } + }; + const builtRequest = spec.buildRequests(tempBidRequests, tempBidderRequest); + + expect(builtRequest).to.have.lengthOf(1); + expect(builtRequest[0].method).to.equal('POST'); + expect(builtRequest[0].url).to.match(/ad.tpmn.co.kr\/prebidhb.tpmn/); + const apiRequest = builtRequest[0].data; + expect(apiRequest.site).to.deep.equal({ + domain: 'localhost', + page: 'http://localhost/test' }); - - it('should create request data', function () { - const bid = utils.deepClone(BANNER_BID); - - let [request] = spec.buildRequests([bid], BIDDER_REQUEST); - expect(request).to.exist.and.to.be.a('object'); - const payload = request.data; - expect(payload.imp[0]).to.have.property('id', bid.bidId); + expect(apiRequest.bids).to.have.lengthOf('1'); + expect(apiRequest.bids[0].inventoryId).to.equal('1'); + expect(apiRequest.bids[0].publisherId).to.equal('TPMN'); + expect(apiRequest.bids[0].bidId).to.equal('29092404798c9'); + expect(apiRequest.bids[0].adUnitCode).to.equal('temp-unitcode'); + expect(apiRequest.bids[0].auctionId).to.equal('da1d7a33-0260-4e83-a621-14674116f3f9'); + expect(apiRequest.bids[0].sizes).to.have.lengthOf('1'); + expect(apiRequest.bids[0].sizes[0]).to.deep.equal({ + width: 300, + height: 250 }); }); + }); - context('when mediaType is video', function () { - if (FEATURES.VIDEO) { - it('should return false when there is no video in mediaTypes', () => { - const bid = utils.deepClone(VIDEO_BID); - delete bid.mediaTypes.video; - - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - } - - if (FEATURES.VIDEO) { - it('should reutrn false if player size is not set', () => { - const bid = utils.deepClone(VIDEO_BID); - delete bid.mediaTypes.video.playerSize; - - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - } - if (FEATURES.VIDEO) { - it('when mediaType is Video - check', () => { - const bid = utils.deepClone(VIDEO_BID); - const check = { - w: 1024, - h: 768, - mimes: ['video/mp4'], - playbackmethod: [2, 4, 6], - api: [1, 2, 4, 6], - protocols: [3, 4, 7, 8, 10], - placement: 1, - minduration: 0, - maxduration: 60, - startdelay: 0, - plcmt: 1 - }; - 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}); - }); - } - - if (FEATURES.VIDEO) { - it('when mediaType New Video', () => { - const NEW_VIDEO_BID = { - 'bidder': 'tpmn', - 'params': {'inventoryId': 2, 'bidFloor': 2}, - 'userId': {'pubcid': '88a49ee6-beeb-4dd6-92ac-3b6060e127e1'}, - 'mediaTypes': { - 'video': { - 'context': 'outstream', - 'mimes': ['video/mp4'], - 'playerSize': [[1024, 768]], - 'playbackmethod': [2, 4, 6], - 'protocols': [3, 4], - 'api': [1, 2, 3, 6], - 'placement': 1, - 'minduration': 0, - 'maxduration': 30, - 'startdelay': 0, - 'skip': 1, - 'plcmt': 4 - } - }, - }; - - const check = { - w: 1024, - h: 768, - mimes: [ 'video/mp4' ], - playbackmethod: [2, 4, 6], - api: [1, 2, 3, 6], - protocols: [3, 4], - placement: 1, - minduration: 0, - maxduration: 30, - startdelay: 0, - skip: 1, - plcmt: 4 - } - - expect(spec.isBidRequestValid(NEW_VIDEO_BID)).to.equal(true); - let requests = spec.buildRequests([NEW_VIDEO_BID], BIDDER_REQUEST); - const request = requests[0].data; - expect(request.imp[0].video.w).to.equal(check.w); - expect(request.imp[0].video.h).to.equal(check.h); - expect(request.imp[0].video.placement).to.equal(check.placement); - expect(request.imp[0].video.minduration).to.equal(check.minduration); - expect(request.imp[0].video.maxduration).to.equal(check.maxduration); - expect(request.imp[0].video.startdelay).to.equal(check.startdelay); - expect(request.imp[0].video.skip).to.equal(check.skip); - expect(request.imp[0].video.plcmt).to.equal(check.plcmt); - expect(request.imp[0].video.mimes).to.deep.have.same.members(check.mimes); - expect(request.imp[0].video.playbackmethod).to.deep.have.same.members(check.playbackmethod); - expect(request.imp[0].video.api).to.deep.have.same.members(check.api); - expect(request.imp[0].video.protocols).to.deep.have.same.members(check.protocols); - }); + describe('interpretResponse', function () { + const bid = { + adUnitCode: 'temp-unitcode', + bidder: 'tpmn', + params: { + inventoryId: '1', + publisherId: 'TPMN' + }, + bidId: '29092404798c9', + bidderRequestId: 'a01', + auctionId: 'da1d7a33-0260-4e83-a621-14674116f3f9', + mediaTypes: { + banner: { + sizes: [[300, 250]] + } } + }; + const tempBidRequests = [bid]; - if (FEATURES.VIDEO) { - it('should use bidder video params if they are set', () => { - let bid = utils.deepClone(VIDEO_BID); - const check = { - api: [1, 2], - mimes: ['video/mp4', 'video/x-flv'], - playbackmethod: [3, 4], - protocols: [5, 6], - placement: 1, - plcmt: 1, - minduration: 0, - maxduration: 30, - startdelay: 0, - w: 640, - h: 480 - - }; - 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}); - }); - } + it('should return an empty aray to indicate no valid bids', function () { + const emptyServerResponse = {}; + const bidResponses = spec.interpretResponse(emptyServerResponse, tempBidRequests); + expect(bidResponses).is.an('array').that.is.empty; }); - }); - - describe('interpretResponse()', function () { - context('when mediaType is banner', function () { - it('should correctly interpret valid banner response', function () { - const bid = utils.deepClone(BANNER_BID); - const [request] = spec.buildRequests([bid], BIDDER_REQUEST); - const response = utils.deepClone(BANNER_BID_RESPONSE); - - const bids = spec.interpretResponse({ body: response }, request); - expect(bids).to.be.an('array').that.is.not.empty; - - expect(bids[0].mediaType).to.equal('banner'); - expect(bids[0].burl).to.equal(BANNER_BID_RESPONSE.seatbid[0].bid[0].burl); - expect(bids[0].currency).to.equal('USD'); - expect(bids[0].requestId).to.equal(BANNER_BID_RESPONSE.seatbid[0].bid[0].impid); - expect(bids[0].cpm).to.equal(BANNER_BID_RESPONSE.seatbid[0].bid[0].price); - expect(bids[0].width).to.equal(BANNER_BID_RESPONSE.seatbid[0].bid[0].w); - expect(bids[0].height).to.equal(BANNER_BID_RESPONSE.seatbid[0].bid[0].h); - expect(bids[0].ad).to.equal(BANNER_BID_RESPONSE.seatbid[0].bid[0].adm); - expect(bids[0].creativeId).to.equal(BANNER_BID_RESPONSE.seatbid[0].bid[0].crid); - expect(bids[0].meta.advertiserDomains[0]).to.equal('https://dummydomain.com'); - expect(bids[0].ttl).to.equal(500); - expect(bids[0].netRevenue).to.equal(true); - }); - - it('should handle empty bid response', function () { - const bid = utils.deepClone(BANNER_BID); - - let request = spec.buildRequests([bid], BIDDER_REQUEST)[0]; - const EMPTY_RESP = Object.assign({}, BANNER_BID_RESPONSE, { 'body': {} }); - const bids = spec.interpretResponse(EMPTY_RESP, request); - expect(bids).to.be.empty; - }); + it('should return an empty array to indicate no valid bids', function () { + const mockBidResult = { + requestId: '9cf19229-34f6-4d06-bc1d-0e44e8d616c8', + cpm: 10.0, + creativeId: '1', + width: 300, + height: 250, + netRevenue: true, + currency: 'USD', + ttl: 1800, + ad: '', + adType: 'banner' + }; + const testServerResponse = { + headers: [], + body: [mockBidResult] + }; + const bidResponses = spec.interpretResponse(testServerResponse, tempBidRequests); + expect(bidResponses).deep.equal([mockBidResult]); }); - if (FEATURES.VIDEO) { - context('when mediaType is video', function () { - it('should correctly interpret valid instream video response', () => { - const bid = utils.deepClone(VIDEO_BID); - - const [request] = spec.buildRequests([bid], BIDDER_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'); - expect(bids[0].burl).to.equal(VIDEO_BID_RESPONSE.seatbid[0].bid[0].burl); - expect(bids[0].currency).to.equal('USD'); - expect(bids[0].requestId).to.equal(VIDEO_BID_RESPONSE.seatbid[0].bid[0].impid); - expect(bids[0].cpm).to.equal(VIDEO_BID_RESPONSE.seatbid[0].bid[0].price); - expect(bids[0].width).to.equal(VIDEO_BID_RESPONSE.seatbid[0].bid[0].w); - expect(bids[0].height).to.equal(VIDEO_BID_RESPONSE.seatbid[0].bid[0].h); - expect(bids[0].vastXml).to.equal(VIDEO_BID_RESPONSE.seatbid[0].bid[0].adm); - expect(bids[0].rendererUrl).to.equal(VIDEO_RENDERER_URL); - expect(bids[0].creativeId).to.equal(VIDEO_BID_RESPONSE.seatbid[0].bid[0].crid); - expect(bids[0].meta.advertiserDomains[0]).to.equal('https://dummydomain.com'); - expect(bids[0].ttl).to.equal(500); - expect(bids[0].netRevenue).to.equal(true); - }); - }); - } }); describe('getUserSync', function () { diff --git a/test/spec/modules/trafficgateBidAdapter_spec.js b/test/spec/modules/trafficgateBidAdapter_spec.js index 11ff547cc78..ecea0e69ca1 100644 --- a/test/spec/modules/trafficgateBidAdapter_spec.js +++ b/test/spec/modules/trafficgateBidAdapter_spec.js @@ -1,1373 +1,223 @@ -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 'src/prebid.js' -import 'modules/currency.js'; -import 'modules/userId/index.js'; -import 'modules/multibid/index.js'; -import 'modules/priceFloors.js'; -import 'modules/consentManagement.js'; -import 'modules/consentManagementUsp.js'; -import 'modules/schain.js'; -import {deepClone} from 'src/utils.js'; -import {syncAddFPDToBidderRequest} from '../../helpers/fpd.js'; -import {hook} from '../../../src/hook.js'; - -const BidRequestBuilder = function BidRequestBuilder(options) { - const defaults = { - request: { - auctionId: '4fd1ca2d-846c-4211-b9e5-321dfe1709c9', - adUnitCode: 'adunit-code', - bidder: 'trafficgate' - }, +import { expect } from 'chai' +import { spec } from '../../../modules/trafficgateBidAdapter' +import { deepStrictEqual, notStrictEqual, ok, strictEqual } from 'assert' + +describe('TrafficGateAdapter', () => { + const bid = { + bidId: '9ec5b177515ee2e5', + bidder: 'trafficgate', params: { - placementId: '98765', + placementId: 1, host: 'example' }, - sizes: [[300, 250], [300, 600]], - }; - - const request = { - ...defaults.request, - ...options - }; - - this.withParams = (options) => { - request.params = { - ...defaults.params, - ...options - }; - return this; - }; - - this.build = () => request; -}; - -const BidderRequestBuilder = function BidderRequestBuilder(options) { - const defaults = { - bidderCode: 'trafficgate', - auctionId: '4fd1ca2d-846c-4211-b9e5-321dfe1709c9', - bidderRequestId: '7g36s867Tr4xF90X', - timeout: 3000, - refererInfo: { - numIframes: 0, - reachedTop: true, - referer: 'http://test.io/index.html?pbjs_debug=true' + mediaTypes: { + banner: { + sizes: [[300, 250]] + } } - }; - - const request = { - ...defaults, - ...options - }; - - this.build = () => request; -}; - -describe('TrafficgateOpenxRtbAdapter', function () { - before(() => { - hook.ready(); - }); - - 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 () { - describe('when request is for a banner ad', function () { - let bannerBid; - beforeEach(function () { - bannerBid = { - bidder: 'trafficgate', - params: {}, - adUnitCode: 'adunit-code', - mediaTypes: {banner: {}}, - sizes: [[300, 250], [300, 600]], - bidId: '30b31c1838de1e', - bidderRequestId: '22edbae2733bf6', - auctionId: '1d1a030790a475' - }; - }); - - it('should return false when there is placementId only', function () { - 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'} - }); - - it('should return false when there is no placementId and size', function () { - expect(spec.isBidRequestValid(bannerBid)).to.equal(false); - }); - - it('should return false if there is an placementId without sizes', function () { - bannerBid.params.placementId = '98765'; - expect(spec.isBidRequestValid(bannerBid)).to.equal(false); - }); - - it('should return false if there is no placementId and sizes are defined', function () { - bannerBid.mediaTypes.banner.sizes = [720, 90]; - expect(spec.isBidRequestValid(bannerBid)).to.equal(false); - }); - - it('should return false if no sizes are defined ', function () { - expect(spec.isBidRequestValid(bannerBid)).to.equal(false); - }); - - it('should return false if sizes empty ', function () { - bannerBid.mediaTypes.banner.sizes = []; - expect(spec.isBidRequestValid(bannerBid)).to.equal(false); - }); - - it('should return true if there is placementId and sizes are defined', function () { - bannerBid.params.placementId = '98765'; - bannerBid.mediaTypes.banner.sizes = [720, 90]; - expect(spec.isBidRequestValid(bannerBid)).to.equal(true); - }); - }); - }); - - describe('when request is for a multiformat ad', function () { - describe('and request config uses mediaTypes video and banner', () => { - const multiformatBid = { - bidder: 'trafficgate', - params: { - placementId: '98765', - host: 'example' - }, - adUnitCode: 'adunit-code', - mediaTypes: { - banner: { - sizes: [[300, 250]] - }, - video: { - playerSize: [300, 250] - } - }, - bidId: '30b31c1838de1e', - bidderRequestId: '22edbae2733bf6', - auctionId: '1d1a030790a475', - transactionId: '4008d88a-8137-410b-aa35-fbfdabcb478e' - }; - it('should return true multisize when required params found', function () { - expect(spec.isBidRequestValid(multiformatBid)).to.equal(true); - }); - }); - }); - - describe('when request is for a video ad', function () { - describe('and request config uses mediaTypes', () => { - const videoBidWithMediaTypes = { - bidder: 'trafficgate', - params: { - placementId: '98765', - host: 'example' - }, - adUnitCode: 'adunit-code', - mediaTypes: { - video: { - playerSize: [640, 480] - } - }, - bidId: '30b31c1838de1e', - bidderRequestId: '22edbae2733bf6', - auctionId: '1d1a030790a475', - transactionId: '4008d88a-8137-410b-aa35-fbfdabcb478e' - }; - it('should return false when isBannerBid', function () { - expect(spec.isBannerBid(videoBidWithMediaTypes)).to.equal(false); - }); - - it('should return true when required params found', function () { - expect(spec.isBidRequestValid(videoBidWithMediaTypes)).to.equal(true); - }); - - it('should return false when required params are not passed', function () { - let videoBidWithMediaTypes = Object.assign({}, videoBidWithMediaTypes); - videoBidWithMediaTypes.params = {}; - expect(spec.isBidRequestValid(videoBidWithMediaTypes)).to.equal(false); - }); - }); - describe('and request config uses both host and platform', () => { - const videoBidWithHostAndPlacement = { - bidder: 'trafficgate', - params: { - placementId: '98765', - host: 'example' - }, - adUnitCode: 'adunit-code', - mediaTypes: { - video: { - playerSize: [640, 480] - } - }, - bidId: '30b31c1838de1e', - bidderRequestId: '22edbae2733bf6', - auctionId: '1d1a030790a475', - transactionId: '4008d88a-8137-410b-aa35-fbfdabcb478e' - }; - it('should return false when isBannerBid', function () { - expect(spec.isBannerBid(videoBidWithHostAndPlacement)).to.equal(false); - }); - - it('should return true when required params found', function () { - expect(spec.isBidRequestValid(videoBidWithHostAndPlacement)).to.equal(true); - }); - - it('should return false when required params are not passed', function () { - let videoBidWithMediaTypes = Object.assign({}, videoBidWithHostAndPlacement); - videoBidWithMediaTypes.params = {}; - expect(spec.isBidRequestValid(videoBidWithMediaTypes)).to.equal(false); - }); - }); - describe('and request config uses mediaType', () => { - const videoBidWithMediaType = { - 'bidder': 'trafficgate', - 'params': { - 'placementId': '98765', - 'host': 'example' - }, - 'adUnitCode': 'adunit-code', - 'mediaType': 'video', - 'sizes': [640, 480], - 'bidId': '30b31c1838de1e', - 'bidderRequestId': '22edbae2733bf6', - 'auctionId': '1d1a030790a475', - 'transactionId': '4008d88a-8137-410b-aa35-fbfdabcb478e' - }; - it('should return true when required params found', function () { - expect(spec.isBidRequestValid(videoBidWithMediaType)).to.equal(true); - }); - - it('should return false when required params are not passed', function () { - let videoBidWithMediaType = Object.assign({}, videoBidWithMediaType); - delete videoBidWithMediaType.params; - videoBidWithMediaType.params = {}; - expect(spec.isBidRequestValid(videoBidWithMediaType)).to.equal(false); - }); - }); - }); - }); - - describe('buildRequests()', function () { - let bidRequestsWithMediaTypes; - let bidRequestsWithPlatform; - let mockBidderRequest; - - beforeEach(function () { - mockBidderRequest = {refererInfo: {}}; - - bidRequestsWithMediaTypes = [{ - bidder: 'trafficgate', - params: { - placementId: '11', - host: 'example', - }, - adUnitCode: '/adunit-code/test-path', - mediaTypes: { - banner: { - sizes: [[300, 250], [300, 600]] + } + + describe('isBidRequestValid', () => { + it('Should return true if there are bidId, params and placementId parameters present', () => { + strictEqual(true, spec.isBidRequestValid(bid)) + }) + + it('Should return false if at least one of parameters is not present', () => { + const b = { ...bid } + delete b.params.placementId + strictEqual(false, spec.isBidRequestValid(b)) + }) + + it('Should return false if at least one of parameters is not present', () => { + const b = { ...bid } + delete b.params.host + strictEqual(false, spec.isBidRequestValid(b)) + }) + }) + + describe('buildRequests', () => { + const serverRequest = spec.buildRequests([bid]) + + it('Creates a ServerRequest object with method, URL and data', () => { + ok(serverRequest) + ok(serverRequest.method) + ok(serverRequest.url) + ok(serverRequest.data) + }) + + it('Returns POST method', () => { + strictEqual('POST', serverRequest.method) + }) + + it('Returns valid URL', () => { + strictEqual('https://example.bc-plugin.com/?c=o&m=multi', serverRequest.url) + }) + + it('Returns valid data if array of bids is valid', () => { + const { data } = serverRequest + strictEqual('object', typeof data) + deepStrictEqual(['language', 'secure', 'host', 'page', 'placements'], Object.keys(data)) + strictEqual('string', typeof data.language) + strictEqual('string', typeof data.host) + strictEqual('string', typeof data.page) + notStrictEqual(-1, [0, 1].indexOf(data.secure)) + + const placement = data.placements[0] + deepStrictEqual(['placementId', 'bidId', 'traffic'], Object.keys(placement)) + strictEqual(1, placement.placementId) + strictEqual('9ec5b177515ee2e5', placement.bidId) + strictEqual('banner', placement.traffic) + }) + + it('Returns empty data if no valid requests are passed', () => { + deepStrictEqual([], spec.buildRequests([])) + }) + }) + + describe('interpretResponse', () => { + const validData = [ + { + body: [{ + mediaType: 'banner', + width: 300, + height: 250, + cpm: 0.4, + ad: 'Test', + requestId: '9ec5b177515ee2e5', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1', + meta: { + advertiserDomains: ['test.com'] } - }, - bidId: 'test-bid-id-1', - bidderRequestId: 'test-bid-request-1', - auctionId: 'test-auction-1', - transactionId: 'test-transactionId-1', - ortb2Imp: { - ext: { - ae: 2 + }] + }, + { + body: [{ + vastUrl: 'example.com', + mediaType: 'video', + cpm: 0.5, + requestId: '9ec5b177515ee2e5', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1', + meta: { + advertiserDomains: ['test.com'] } - } - }, { - bidder: 'trafficgate', - params: { - placementId: '22', - host: 'example', - }, - adUnitCode: 'adunit-code', - mediaTypes: { - video: { - playerSize: [640, 480] + }] + }, + { + body: [{ + mediaType: 'native', + clickUrl: 'example.com', + title: 'Test', + image: 'example.com', + creativeId: '2', + impressionTrackers: ['example.com'], + ttl: 120, + cpm: 0.4, + requestId: '9ec5b177515ee2e5', + netRevenue: true, + currency: 'USD', + meta: { + advertiserDomains: ['test.com'] } - }, - bidId: 'test-bid-id-2', - bidderRequestId: 'test-bid-request-2', - auctionId: 'test-auction-2', - transactionId: 'test-transactionId-2' - }]; - }); - - context('common requests checks', function() { - it('should be able to handle multiformat requests', () => { - const multiformat = utils.deepClone(bidRequestsWithMediaTypes[0]); - multiformat.mediaTypes.video = { - context: 'outstream', - playerSize: [640, 480] - } - const requests = spec.buildRequests([multiformat], mockBidderRequest); - const outgoingFormats = requests.flatMap(rq => rq.data.imp.flatMap(imp => ['banner', 'video'].filter(k => imp[k] != null))); - const expected = FEATURES.VIDEO ? ['banner', 'video'] : ['banner'] - expect(outgoingFormats).to.have.members(expected); - }) - - it('should send bid request to trafficgate url via POST', function () { - const request = spec.buildRequests(bidRequestsWithMediaTypes, mockBidderRequest); - expect(request[0].url).to.equal('https://example.bc-plugin.com/prebidjs'); - expect(request[0].method).to.equal('POST'); - }); - - it('should send delivery domain, if available', function () { - const request = spec.buildRequests(bidRequestsWithMediaTypes, mockBidderRequest); - expect(request[0].data.imp[0].ext.bidder.host).to.equal(bidRequestsWithMediaTypes[0].params.host); - expect(request[1].data.imp[0].ext.bidder.host).to.equal(bidRequestsWithMediaTypes[1].params.host); - }); - - it('should send placementId', function () { - const request = spec.buildRequests(bidRequestsWithMediaTypes, mockBidderRequest); - expect(request[0].data.imp[0].ext.bidder.placementId).to.equal(bidRequestsWithMediaTypes[0].params.placementId); - expect(request[1].data.imp[0].ext.bidder.placementId).to.equal(bidRequestsWithMediaTypes[1].params.placementId); - }); - - describe('floors', function () { - it('should send out custom floors on bids that have customFloors, no currency as account currency is used', function () { - const bidRequest = Object.assign({}, - bidRequestsWithMediaTypes[0], - { - params: { - placementId: '98765', - host: 'example', - customFloor: 1.500 - } - } - ); - - const request = spec.buildRequests([bidRequest], mockBidderRequest); - expect(request[0].data.imp[0].bidfloor).to.equal(bidRequest.params.customFloor); - expect(request[0].data.imp[0].bidfloorcur).to.equal(undefined); - }); - - context('with floors module', function () { - let adServerCurrencyStub; - - beforeEach(function () { - adServerCurrencyStub = sinon - .stub(config, 'getConfig') - .withArgs('currency.adServerCurrency') - }); - - afterEach(function () { - config.getConfig.restore(); - }); + }] + } + ] - it('should send out floors on bids in USD', function () { - const bidRequest = Object.assign({}, - bidRequestsWithMediaTypes[0], - { - getFloor: () => { - return { - currency: 'USD', - floor: 9.99 - } - } - } - ); + for (const obj of validData) { + const { mediaType } = obj.body[0] - const request = spec.buildRequests([bidRequest], mockBidderRequest); - expect(request[0].data.imp[0].bidfloor).to.equal(9.99); - expect(request[0].data.imp[0].bidfloorcur).to.equal('USD'); - }); + it(`Should interpret ${mediaType} response`, () => { + const response = spec.interpretResponse(obj) - it('should send not send floors', function () { - adServerCurrencyStub.returns('EUR'); - const bidRequest = Object.assign({}, - bidRequestsWithMediaTypes[0], - { - getFloor: () => { - return { - currency: 'BTC', - floor: 9.99 - } - } - } - ); + expect(response).to.be.an('array') + strictEqual(1, response.length) - const request = spec.buildRequests([bidRequest], mockBidderRequest); - expect(request[0].data.imp[0].bidfloor).to.equal(undefined) - expect(request[0].data.imp[0].bidfloorcur).to.equal(undefined) - }); - }) + const copy = { ...obj.body[0] } + deepStrictEqual(copy, response[0]) }) - - describe('FPD', function() { - let bidRequests; - const mockBidderRequest = {refererInfo: {}}; - - beforeEach(function () { - bidRequests = [{ - bidder: 'trafficgate', - params: { - placementId: '98765-banner', - host: 'example' - }, - adUnitCode: 'adunit-code', - mediaTypes: { - banner: { - sizes: [[300, 250], [300, 600]] - } - }, - bidId: 'test-bid-id', - bidderRequestId: 'test-bidder-request-id', - auctionId: 'test-auction-id', - transactionId: 'test-transaction-id-1' - }, { - bidder: 'trafficgate', - mediaTypes: { - video: { - playerSize: [640, 480] - } - }, - params: { - placementId: '98765-video', - host: 'example' - }, - 'adUnitCode': 'adunit-code', - - bidId: 'test-bid-id', - bidderRequestId: 'test-bidder-request-id', - auctionId: 'test-auction-id', - transactionId: 'test-transaction-id-2' - }]; - }); - - it('ortb2.site should be merged in the request', function() { - const request = spec.buildRequests(bidRequests, { - ...mockBidderRequest, - 'ortb2': { - site: { - domain: 'page.example.com', - cat: ['IAB2'], - sectioncat: ['IAB2-2'] - } - } - }); - let data = request[0].data; - expect(data.site.domain).to.equal('page.example.com'); - expect(data.site.cat).to.deep.equal(['IAB2']); - expect(data.site.sectioncat).to.deep.equal(['IAB2-2']); - }); - - it('ortb2.user should be merged in the request', function() { - const request = spec.buildRequests(bidRequests, { - ...mockBidderRequest, - 'ortb2': { - user: { - yob: 1985 - } - } - }); - let data = request[0].data; - expect(data.user.yob).to.equal(1985); - }); - - describe('ortb2Imp', function() { - describe('ortb2Imp.ext.data.pbadslot', function() { - beforeEach(function () { - if (bidRequests[0].hasOwnProperty('ortb2Imp')) { - delete bidRequests[0].ortb2Imp; - } - }); - - it('should not send if imp[].ext.data object is invalid', function() { - bidRequests[0].ortb2Imp = { - ext: {} - }; - const request = spec.buildRequests(bidRequests, mockBidderRequest); - let data = request[0].data; - expect(data.imp[0].ext).to.not.have.property('data'); - }); - - it('should not send if imp[].ext.data.pbadslot is undefined', function() { - bidRequests[0].ortb2Imp = { - ext: { - data: { - } - } - }; - const request = spec.buildRequests(bidRequests, mockBidderRequest); - let data = request[0].data; - if (data.imp[0].ext.data) { - expect(data.imp[0].ext.data).to.not.have.property('pbadslot'); - } else { - expect(data.imp[0].ext).to.not.have.property('data'); - } - }); - - it('should send if imp[].ext.data.pbadslot is string', function() { - bidRequests[0].ortb2Imp = { - ext: { - data: { - pbadslot: 'abcd' - } - } - }; - const request = spec.buildRequests(bidRequests, mockBidderRequest); - let data = request[0].data; - expect(data.imp[0].ext.data).to.have.property('pbadslot'); - expect(data.imp[0].ext.data.pbadslot).to.equal('abcd'); - }); - }); - - describe('ortb2Imp.ext.data.adserver', function() { - beforeEach(function () { - if (bidRequests[0].hasOwnProperty('ortb2Imp')) { - delete bidRequests[0].ortb2Imp; - } - }); - - it('should not send if imp[].ext.data object is invalid', function() { - bidRequests[0].ortb2Imp = { - ext: {} - }; - const request = spec.buildRequests(bidRequests, mockBidderRequest); - let data = request[0].data; - expect(data.imp[0].ext).to.not.have.property('data'); - }); - - it('should not send if imp[].ext.data.adserver is undefined', function() { - bidRequests[0].ortb2Imp = { - ext: { - data: { - } - } - }; - const request = spec.buildRequests(bidRequests, mockBidderRequest); - let data = request[0].data; - if (data.imp[0].ext.data) { - expect(data.imp[0].ext.data).to.not.have.property('adserver'); - } else { - expect(data.imp[0].ext).to.not.have.property('data'); - } - }); - - it('should send', function() { - let adSlotValue = 'abc'; - bidRequests[0].ortb2Imp = { - ext: { - data: { - adserver: { - name: 'GAM', - adslot: adSlotValue - } - } - } - }; - const request = spec.buildRequests(bidRequests, mockBidderRequest); - let data = request[0].data; - expect(data.imp[0].ext.data.adserver.name).to.equal('GAM'); - expect(data.imp[0].ext.data.adserver.adslot).to.equal(adSlotValue); - }); - }); - - describe('ortb2Imp.ext.data.other', function() { - beforeEach(function () { - if (bidRequests[0].hasOwnProperty('ortb2Imp')) { - delete bidRequests[0].ortb2Imp; - } - }); - - it('should not send if imp[].ext.data object is invalid', function() { - bidRequests[0].ortb2Imp = { - ext: {} - }; - const request = spec.buildRequests(bidRequests, mockBidderRequest); - let data = request[0].data; - expect(data.imp[0].ext).to.not.have.property('data'); - }); - - it('should not send if imp[].ext.data.other is undefined', function() { - bidRequests[0].ortb2Imp = { - ext: { - data: { - } - } - }; - const request = spec.buildRequests(bidRequests, mockBidderRequest); - let data = request[0].data; - if (data.imp[0].ext.data) { - expect(data.imp[0].ext.data).to.not.have.property('other'); - } else { - expect(data.imp[0].ext).to.not.have.property('data'); - } - }); - - it('ortb2Imp.ext.data.other', function() { - bidRequests[0].ortb2Imp = { - ext: { - data: { - other: 1234 - } - } - }; - const request = spec.buildRequests(bidRequests, mockBidderRequest); - let data = request[0].data; - expect(data.imp[0].ext.data.other).to.equal(1234); - }); - }); - }); - - describe('with user agent client hints', function () { - it('should add device.sua if available', function () { - const bidderRequestWithUserAgentClientHints = { refererInfo: {}, - ortb2: { - device: { - sua: { - source: 2, - platform: { - brand: 'macOS', - version: [ '12', '4', '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: 'Pro', - bitness: '64', - 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: {}}; - request = spec.buildRequests(bidRequests, bidderRequestWithoutUserAgentClientHints); - expect(request[0].data.device?.sua).to.not.exist; - }); - }); - }); - - context('when there is a consent management framework', function () { - let bidRequests; - let mockConfig; - let bidderRequest; - - beforeEach(function () { - bidRequests = [{ - bidder: 'trafficgate', - params: { - placementId: '98765-banner', - host: 'example' - }, - adUnitCode: 'adunit-code', - mediaTypes: { - banner: { - sizes: [[300, 250], [300, 600]] - } - }, - bidId: 'test-bid-id', - bidderRequestId: 'test-bidder-request-id', - auctionId: 'test-auction-id', - transactionId: 'test-transaction-id-1' - }, { - bidder: 'trafficgate', - mediaTypes: { - video: { - playerSize: [640, 480] - } - }, - params: { - placementId: '98765-video', - host: 'example' - }, - 'adUnitCode': 'adunit-code', - - bidId: 'test-bid-id', - bidderRequestId: 'test-bidder-request-id', - auctionId: 'test-auction-id', - transactionId: 'test-transaction-id-2' - }]; - }); - - describe('us_privacy', function () { - beforeEach(function () { - bidderRequest = { - uspConsent: '1YYN', - refererInfo: {} - }; - - sinon.stub(config, 'getConfig').callsFake((key) => { - return utils.deepAccess(mockConfig, key); - }); - }); - - afterEach(function () { - config.getConfig.restore(); - }); - - it('should send a signal to specify that US Privacy applies to this request', function () { - const request = spec.buildRequests(bidRequests, syncAddFPDToBidderRequest(bidderRequest)); - expect(request[0].data.regs.ext.us_privacy).to.equal('1YYN'); - expect(request[1].data.regs.ext.us_privacy).to.equal('1YYN'); - }); - - it('should not send the regs object, when consent string is undefined', function () { - delete bidderRequest.uspConsent; - const request = spec.buildRequests(bidRequests, syncAddFPDToBidderRequest(bidderRequest)); - expect(request[0].data.regs?.us_privacy).to.not.exist; - }); - }); - - describe('GDPR', function () { - beforeEach(function () { - bidderRequest = { - gdprConsent: { - consentString: 'test-gdpr-consent-string', - addtlConsent: 'test-addtl-consent-string', - gdprApplies: true - }, - refererInfo: {} - }; - - mockConfig = { - consentManagement: { - cmpApi: 'iab', - timeout: 1111, - allowAuctionWithoutConsent: 'cancel' - } - }; - - sinon.stub(config, 'getConfig').callsFake((key) => { - return utils.deepAccess(mockConfig, key); - }); - }); - - afterEach(function () { - config.getConfig.restore(); - }); - - it('should send a signal to specify that GDPR applies to this request', function () { - bidderRequest.bids = bidRequests; - const request = spec.buildRequests(bidRequests, syncAddFPDToBidderRequest(bidderRequest)); - expect(request[0].data.regs.ext.gdpr).to.equal(1); - expect(request[1].data.regs.ext.gdpr).to.equal(1); - }); - - it('should send the consent string', function () { - bidderRequest.bids = bidRequests; - const request = spec.buildRequests(bidRequests, syncAddFPDToBidderRequest(bidderRequest)); - expect(request[0].data.user.ext.consent).to.equal(bidderRequest.gdprConsent.consentString); - expect(request[1].data.user.ext.consent).to.equal(bidderRequest.gdprConsent.consentString); - }); - - it('should send the addtlConsent string', function () { - bidderRequest.bids = bidRequests; - const request = spec.buildRequests(bidRequests, syncAddFPDToBidderRequest(bidderRequest)); - expect(request[0].data.user.ext.ConsentedProvidersSettings.consented_providers).to.equal(bidderRequest.gdprConsent.addtlConsent); - expect(request[1].data.user.ext.ConsentedProvidersSettings.consented_providers).to.equal(bidderRequest.gdprConsent.addtlConsent); - }); - - it('should send a signal to specify that GDPR does not apply to this request', function () { - bidderRequest.gdprConsent.gdprApplies = false; - bidderRequest.bids = bidRequests; - const request = spec.buildRequests(bidRequests, syncAddFPDToBidderRequest(bidderRequest)); - expect(request[0].data.regs.ext.gdpr).to.equal(0); - expect(request[1].data.regs.ext.gdpr).to.equal(0); - }); - - it('when GDPR application is undefined, should not send a signal to specify whether GDPR applies to this request, ' + - 'but can send consent data, ', function () { - delete bidderRequest.gdprConsent.gdprApplies; - bidderRequest.bids = bidRequests; - const request = spec.buildRequests(bidRequests, syncAddFPDToBidderRequest(bidderRequest)); - expect(request[0].data.regs?.ext?.gdpr).to.not.be.ok; - expect(request[0].data.user.ext.consent).to.equal(bidderRequest.gdprConsent.consentString); - expect(request[1].data.user.ext.consent).to.equal(bidderRequest.gdprConsent.consentString); - }); - - it('when consent string is undefined, should not send the consent string, ', function () { - delete bidderRequest.gdprConsent.consentString; - bidderRequest.bids = bidRequests; - const request = spec.buildRequests(bidRequests, syncAddFPDToBidderRequest(bidderRequest)); - expect(request[0].data.imp[0].ext.consent).to.equal(undefined); - expect(request[1].data.imp[0].ext.consent).to.equal(undefined); - }); - }); - }); - - context('coppa', function() { - it('when there are no coppa param settings, should not send a coppa flag', function () { - const request = spec.buildRequests(bidRequestsWithMediaTypes, syncAddFPDToBidderRequest(mockBidderRequest)); - expect(request[0].data.regs?.coppa).to.be.not.ok; - }); - - it('should send a coppa flag there is when there is coppa param settings in the bid requests', function () { - let mockConfig = { - coppa: true - }; - - sinon.stub(config, 'getConfig').callsFake((key) => { - return utils.deepAccess(mockConfig, key); - }); - - const request = spec.buildRequests(bidRequestsWithMediaTypes, syncAddFPDToBidderRequest(mockBidderRequest)); - expect(request[0].data.regs.coppa).to.equal(1); - }); - - it('should send a coppa flag there is when there is coppa param settings in the bid params', function () { - const request = spec.buildRequests(bidRequestsWithMediaTypes, syncAddFPDToBidderRequest(mockBidderRequest)); - request.params = {coppa: true}; - expect(request[0].data.regs.coppa).to.equal(1); - }); - - after(function () { - config.getConfig.restore() - }); - }); - - context('do not track (DNT)', function() { - let doNotTrackStub; - - beforeEach(function () { - doNotTrackStub = sinon.stub(utils, 'getDNT'); - }); - afterEach(function() { - doNotTrackStub.restore(); - }); - - it('when there is a do not track, should send a dnt', function () { - doNotTrackStub.returns(1); - - const request = spec.buildRequests(bidRequestsWithMediaTypes, syncAddFPDToBidderRequest(mockBidderRequest)); - expect(request[0].data.device.dnt).to.equal(1); - }); - - it('when there is not do not track, don\'t send dnt', function () { - doNotTrackStub.returns(0); - - const request = spec.buildRequests(bidRequestsWithMediaTypes, syncAddFPDToBidderRequest(mockBidderRequest)); - expect(request[0].data.device.dnt).to.equal(0); - }); - - it('when there is no defined do not track, don\'t send dnt', function () { - doNotTrackStub.returns(null); - - const request = spec.buildRequests(bidRequestsWithMediaTypes, syncAddFPDToBidderRequest(mockBidderRequest)); - expect(request[0].data.device.dnt).to.equal(0); - }); - }); - - context('supply chain (schain)', function () { - let bidRequests; - let schainConfig; - const supplyChainNodePropertyOrder = ['asi', 'sid', 'hp', 'rid', 'name', 'domain']; - - beforeEach(function () { - schainConfig = { - ver: '1.0', - complete: 1, - nodes: [ - { - asi: 'exchange1.com', - sid: '1234', - hp: 1, - rid: 'bid-request-1', - name: 'publisher', - domain: 'publisher.com' - // omitted ext - }, - { - asi: 'exchange2.com', - sid: 'abcd', - hp: 1, - rid: 'bid-request-2', - // name field missing - domain: 'intermediary.com' - }, - { - asi: 'exchange3.com', - sid: '4321', - hp: 1, - // request id - // name field missing - domain: 'intermediary-2.com' - } - ] - }; - - bidRequests = [{ - bidder: 'trafficgate', - params: { - placementId: '11', - host: 'example' - }, - adUnitCode: '/adunit-code/test-path', - mediaTypes: { - banner: { - sizes: [[300, 250], [300, 600]] - } - }, - bidId: 'test-bid-id-1', - bidderRequestId: 'test-bid-request-1', - auctionId: 'test-auction-1', - schain: schainConfig - }]; - }); - - it('should send a supply chain object', function () { - const request = spec.buildRequests(bidRequests, mockBidderRequest); - expect(request[0].data.source.ext.schain).to.equal(schainConfig); - }); - - it('should send the supply chain object with the right version', function () { - const request = spec.buildRequests(bidRequests, mockBidderRequest); - expect(request[0].data.source.ext.schain.ver).to.equal(schainConfig.ver); - }); - - it('should send the supply chain object with the right complete value', function () { - const request = spec.buildRequests(bidRequests, mockBidderRequest); - expect(request[0].data.source.ext.schain.complete).to.equal(schainConfig.complete); - }); - }); - - context('when there are userid providers', function () { - const userIdAsEids = [ - { - source: 'adserver.org', - uids: [{ - id: 'some-random-id-value', - atype: 1, - ext: { - rtiPartner: 'TDID' - } - }] - }, - { - source: 'id5-sync.com', - uids: [{ - id: 'some-random-id-value', - atype: 1 - }] - }, - { - source: 'sharedid.org', - uids: [{ - id: 'some-random-id-value', - atype: 1, - ext: { - third: 'some-random-id-value' - } - }] - } - ]; - - it(`should send the user id under the extended ids`, function () { - const bidRequestsWithUserId = [{ - bidder: 'trafficgate', - params: { - placementId: '11', - host: 'example' - }, - userId: { - }, - adUnitCode: 'adunit-code', - mediaTypes: { - banner: { - sizes: [[300, 250], [300, 600]] - } - }, - bidId: 'test-bid-id-1', - bidderRequestId: 'test-bid-request-1', - auctionId: 'test-auction-1', - userIdAsEids: userIdAsEids - }]; - // enrich bid request with userId key/value - - const request = spec.buildRequests(bidRequestsWithUserId, mockBidderRequest); - expect(request[0].data.user.ext.eids).to.equal(userIdAsEids); - }); - - it(`when no user ids are available, it should not send any extended ids`, function () { - const request = spec.buildRequests(bidRequestsWithMediaTypes, mockBidderRequest); - 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, - fledgeEnabled: true - }); - expect(request[0].data.imp[0].ext.ae).to.equal(2); - }); - }); - }); - - context('banner', function () { - it('should send bid request with a mediaTypes specified with banner type', function () { - const request = spec.buildRequests(bidRequestsWithMediaTypes, mockBidderRequest); - expect(request[0].data.imp[0]).to.have.any.keys(BANNER); - }); - }); - - if (FEATURES.VIDEO) { - context('video', function () { - it('should send bid request with a mediaTypes specified with video type', function () { - const request = spec.buildRequests(bidRequestsWithMediaTypes, mockBidderRequest); - expect(request[1].data.imp[0]).to.have.any.keys(VIDEO); - }); - - it('Update imp.video with OpenRTB options from mimeTypes and params', function() { - const bid01 = new BidRequestBuilder({ - adUnitCode: 'adunit-code-01', - mediaTypes: { - banner: { sizes: [[300, 250]] }, - video: { - context: 'outstream', - playerSize: [[300, 250]], - mimes: ['video/mp4'], - protocols: [8] - } - }, - }).withParams({ - // options in video, will merge - video: { - skip: 1, - skipafter: 4, - minduration: 10, - maxduration: 30 - } - }).build(); - - const bidderRequest = new BidderRequestBuilder().build(); - const expected = { - mimes: ['video/mp4'], - skip: 1, - skipafter: 4, - minduration: 10, - maxduration: 30, - placement: 4, - protocols: [8], - w: 300, - h: 250 - }; - const requests = spec.buildRequests([bid01], bidderRequest); - expect(requests).to.have.lengthOf(2); - expect(requests[1].data.imp[0].video).to.deep.equal(expected); - }); - }); } - }); - - describe('interpretResponse()', function () { - let bidRequestConfigs; - let bidRequest; - let bidResponse; - let bid; - - context('when there is an nbr response', function () { - let bids; - beforeEach(function () { - bidRequestConfigs = [{ - bidder: 'trafficgate', - params: { - placementId: '98765', - host: 'example' - }, - 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 = {nbr: 0}; // Unknown error - bids = spec.interpretResponse({body: bidResponse}, bidRequest); - }); + for (const obj of validData) { + it(`Should interpret response has meta.advertiserDomains`, () => { + const response = spec.interpretResponse(obj) - it('should not return any bids', function () { - expect(bids.length).to.equal(0); - }); - }); - - context('when no seatbid in response', function () { - let bids; - beforeEach(function () { - bidRequestConfigs = [{ - bidder: 'trafficgate', - params: { - placementId: '98765', - host: 'example' - }, - 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 = {ext: {}, id: 'test-bid-id'}; - bids = spec.interpretResponse({body: bidResponse}, bidRequest); - }); - - it('should not return any bids', function () { - expect(bids.length).to.equal(0); - }); - }); - - context('when there is no response', function () { - let bids; - beforeEach(function () { - bidRequestConfigs = [{ - bidder: 'trafficgate', - params: { - placementId: '98765', - host: 'example' - }, - 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 = ''; // Unknown error - bids = spec.interpretResponse({body: bidResponse}, bidRequest); - }); - - it('should not return any bids', function () { - expect(bids.length).to.equal(0); - }); - }); + expect(response[0]['meta']['advertiserDomains']).to.be.an('array') + expect(response[0]['meta']['advertiserDomains'][0]).to.be.an('string') + }) + } - const SAMPLE_BID_REQUESTS = [{ - bidder: 'trafficgate', - params: { - placementId: '98765', - host: 'example' + const invalidData = [ + { + body: [{ + width: 300, + cpm: 0.4, + ad: 'Test', + requestId: '9ec5b177515ee2e5', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1' + }] }, - adUnitCode: 'adunit-code', - mediaTypes: { - banner: { - sizes: [[300, 250], [300, 600]], - }, + { + body: [{ + mediaType: 'video', + cpm: 0.5, + requestId: '9ec5b177515ee2e5', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1' + }] }, - bidId: 'test-bid-id', - bidderRequestId: 'test-bidder-request-id', - auctionId: 'test-auction-id' - }]; - - const SAMPLE_BID_RESPONSE = { - seatbid: [{ - bid: [{ - impid: 'test-bid-id', - price: 3.5, - w: 300, - h: 250, - crid: 'test-creative-id', - dealid: 'test-deal-id', - adm: 'test-ad-markup', - adomain: ['brand.com'], - ext: { - networkId: 123, - advertiserDomains: ['domain.com'], - } + { + body: [{ + mediaType: 'native', + clickUrl: 'example.com', + title: 'Test', + impressionTrackers: ['example.com'], + ttl: 120, + requestId: '9ec5b177515ee2e5', + creativeId: '2', + netRevenue: true, + currency: 'USD', }] - }], - cur: 'USD' - }; - - context('when there is a response, the common response properties', function () { - beforeEach(function () { - bidRequestConfigs = deepClone(SAMPLE_BID_REQUESTS); - bidRequest = spec.buildRequests(bidRequestConfigs, {refererInfo: {}})[0]; - bidResponse = deepClone(SAMPLE_BID_RESPONSE); - - bid = spec.interpretResponse({body: bidResponse}, bidRequest)[0]; - }); - - it('should return a price', function () { - expect(bid.cpm).to.equal(bidResponse.seatbid[0].bid[0].price); - }); - - it('should return a request id', function () { - expect(bid.requestId).to.equal(bidResponse.seatbid[0].bid[0].impid); - }); - - it('should return width and height for the creative', function () { - expect(bid.width).to.equal(bidResponse.seatbid[0].bid[0].w); - expect(bid.height).to.equal(bidResponse.seatbid[0].bid[0].h); - }); + } + ] - it('should return a creativeId', function () { - expect(bid.creativeId).to.equal(bidResponse.seatbid[0].bid[0].crid); - }); + for (const obj of invalidData) { + const { mediaType } = obj.body[0] - it('should return an ad', function () { - expect(bid.ad).to.equal(bidResponse.seatbid[0].bid[0].adm); - }); + it(`Should return an empty array if invalid ${mediaType} response is passed `, () => { + const response = spec.interpretResponse(obj) - it('should return a deal id if it exists', function () { - expect(bid.dealId).to.equal(bidResponse.seatbid[0].bid[0].dealid); - }); - - it('should have a time-to-live of 5 minutes', function () { - expect(bid.ttl).to.equal(300); - }); - - it('should always return net revenue', function () { - expect(bid.netRevenue).to.equal(true); - }); - - it('should return a currency', function () { - expect(bid.currency).to.equal(bidResponse.cur); - }); - - it('should return a networkId', function () { - expect(bid.meta.networkId).to.equal(bidResponse.seatbid[0].bid[0].ext.networkId); - }); - - it('should return adomain', function () { - expect(bid.meta.advertiserDomains).to.equal(bidResponse.seatbid[0].bid[0].ext.advertiserDomains); - }); - }); - - context('when the response is a banner', function() { - beforeEach(function () { - bidRequestConfigs = [{ - bidder: 'trafficgate', - params: { - placementId: '98765', - host: 'example' - }, - 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' - }; - - bid = spec.interpretResponse({body: bidResponse}, bidRequest)[0]; - }); - - it('should return the proper mediaType', function () { - it('should return a creativeId', function () { - expect(bid.mediaType).to.equal(Object.keys(bidRequestConfigs[0].mediaTypes)[0]); - }); - }); - }); - - if (FEATURES.VIDEO) { - context('when the response is a video', function() { - beforeEach(function () { - bidRequestConfigs = [{ - bidder: 'trafficgate', - params: { - placementId: '98765', - host: 'example' - }, - adUnitCode: 'adunit-code', - mediaTypes: { - video: { - playerSize: [[640, 360], [854, 480]], - }, - }, - 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: 854, - h: 480, - crid: 'test-creative-id', - dealid: 'test-deal-id', - adm: 'test-ad-markup', - }] - }], - cur: 'AUS' - }; - }); - - it('should return the proper mediaType', function () { - bid = spec.interpretResponse({body: bidResponse}, bidRequest)[0]; - expect(bid.mediaType).to.equal(Object.keys(bidRequestConfigs[0].mediaTypes)[0]); - }); + expect(response).to.be.an('array') + strictEqual(0, response.length) + }) + } - 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]; + it('Should return an empty array if invalid response is passed', () => { + const response = spec.interpretResponse({ + body: [{ + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1' + }] + }) - expect(bid.vastUrl).to.equal(winUrl); - }); - }); - } - }); -}); + expect(response).to.be.an('array') + strictEqual(0, response.length) + }) + }) +}) diff --git a/test/spec/modules/tripleliftBidAdapter_spec.js b/test/spec/modules/tripleliftBidAdapter_spec.js index 275b9b3bfee..718d030be91 100644 --- a/test/spec/modules/tripleliftBidAdapter_spec.js +++ b/test/spec/modules/tripleliftBidAdapter_spec.js @@ -8,7 +8,6 @@ import * as utils from 'src/utils.js'; const ENDPOINT = 'https://tlx.3lift.com/header/auction?'; const GDPR_CONSENT_STR = 'BOONm0NOONm0NABABAENAa-AAAARh7______b9_3__7_9uz_Kv_K7Vf7nnG072lPVA9LTOQ6gEaY'; -const GPP_CONSENT_STR = 'DBACNYA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA~1YNN' describe('triplelift adapter', function () { const adapter = newBidder(tripleliftAdapterSpec); @@ -1040,12 +1039,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.fledgeEnabled = 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); @@ -1130,46 +1123,6 @@ describe('triplelift adapter', function () { expect(logErrorSpy.calledOnce).to.equal(true); }); - it('should add ortb2 ext object if global fpd is available', function() { - const ortb2 = { - site: { - domain: 'page.example.com', - cat: ['IAB2'], - sectioncat: ['IAB2-2'], - pagecat: ['IAB2-2'], - page: 'https://page.example.com/here.html', - }, - user: { - yob: 1985, - gender: 'm', - keywords: 'a,b', - data: [ - { - name: 'dataprovider.com', - ext: { segtax: 4 }, - segment: [{ id: '1' }] - } - ], - ext: { - data: { - registered: true, - interests: ['cars'] - } - } - } - }; - - 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({ - domain: 'page.example.com', - cat: ['IAB2'], - sectioncat: ['IAB2-2'], - pagecat: ['IAB2-2'], - page: 'https://page.example.com/here.html', - }); - }); it('should send global config fpd if kvps are available', function() { const sens = null; const category = ['news', 'weather', 'hurricane']; @@ -1548,63 +1501,11 @@ describe('triplelift adapter', function () { 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 } - } - } - } - ]; - - let result = tripleliftAdapterSpec.interpretResponse(response, {bidderRequest}); - - expect(result).to.have.property('bids'); - expect(result).to.have.property('fledgeAuctionConfigs'); - expect(result.fledgeAuctionConfigs.length).to.equal(2); - expect(result.fledgeAuctionConfigs[0].bidId).to.equal('30b31c1838de1e'); - expect(result.fledgeAuctionConfigs[1].bidId).to.equal('73edc0ba8de203'); - expect(result.fledgeAuctionConfigs[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.fledgeAuctionConfigs[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() { let expectedIframeSyncUrl = 'https://eb2.3lift.com/sync?gdpr=true&cmp_cs=' + GDPR_CONSENT_STR + '&'; let expectedImageSyncUrl = 'https://eb2.3lift.com/sync?px=1&src=prebid&gdpr=true&cmp_cs=' + GDPR_CONSENT_STR + '&'; - let expectedGppSyncUrl = 'https://eb2.3lift.com/sync?gdpr=true&cmp_cs=' + GDPR_CONSENT_STR + '&gpp=' + GPP_CONSENT_STR + '&gpp_sid=2%2C8' + '&'; it('returns undefined when syncing is not enabled', function() { expect(tripleliftAdapterSpec.getUserSyncs({})).to.equal(undefined); @@ -1642,19 +1543,8 @@ describe('triplelift adapter', function () { let syncOptions = { iframeEnabled: true }; - let result = tripleliftAdapterSpec.getUserSyncs(syncOptions, null, null, '1YYY', null); + let result = tripleliftAdapterSpec.getUserSyncs(syncOptions, null, null, '1YYY'); expect(result[0].url).to.match(/(\?|&)us_privacy=1YYY/); }); - it('returns a user sync pixel with GPP signals when available', function() { - let syncOptions = { - iframeEnabled: true - }; - let gppConsent = { - 'applicableSections': [2, 8], - 'gppString': 'DBACNYA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA~1YNN' - } - let result = tripleliftAdapterSpec.getUserSyncs(syncOptions, null, null, null, gppConsent); - expect(result[0].url).to.equal(expectedGppSyncUrl); - }); }); }); diff --git a/test/spec/modules/ttdBidAdapter_spec.js b/test/spec/modules/ttdBidAdapter_spec.js index 1fe504ba8e8..56c506dea6b 100644 --- a/test/spec/modules/ttdBidAdapter_spec.js +++ b/test/spec/modules/ttdBidAdapter_spec.js @@ -262,11 +262,6 @@ describe('ttdBidAdapter', function () { expect(request.data).to.be.not.null; }); - it('sets bidrequest.id to bidderRequestId', function () { - const requestBody = testBuildRequests(baseBannerBidRequests, baseBidderRequest).data; - expect(requestBody.id).to.equal('18084284054531'); - }); - it('sets impression id to ad unit\'s bid id', function () { const requestBody = testBuildRequests(baseBannerBidRequests, baseBidderRequest).data; expect(requestBody.imp[0].id).to.equal('243310435309b5'); diff --git a/test/spec/modules/ucfunnelBidAdapter_spec.js b/test/spec/modules/ucfunnelBidAdapter_spec.js index 998e0db6fe8..992708bd2ea 100644 --- a/test/spec/modules/ucfunnelBidAdapter_spec.js +++ b/test/spec/modules/ucfunnelBidAdapter_spec.js @@ -1,7 +1,6 @@ import { expect } from 'chai'; import { spec } from 'modules/ucfunnelBidAdapter.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'; @@ -30,7 +29,7 @@ const validBannerBidReq = { params: { adid: 'ad-34BBD2AA24B678BBFD4E7B9EE3B872D' }, - sizes: [[300, 250], [336, 280]], + sizes: [[300, 250]], bidId: '263be71e91dd9d', auctionId: '9ad1fa8d-2297-4660-a018-b39945054746', ortb2Imp: { @@ -151,10 +150,7 @@ describe('ucfunnel Adapter', function () { }); }); describe('build request', function () { - let request; - before(() => { - request = spec.buildRequests([validBannerBidReq], bidderRequest); - }) + const request = spec.buildRequests([validBannerBidReq], bidderRequest); it('should create a POST request for every bid', function () { expect(request[0].method).to.equal('GET'); expect(request[0].url).to.equal(spec.ENDPOINT); @@ -180,19 +176,18 @@ describe('ucfunnel Adapter', function () { expect(data.schain).to.equal('1.0,1!exchange1.com,1234,1,bid-request-1,publisher,publisher.com'); }); - it('should support multiple size', function () { - const sizes = [[300, 250], [336, 280]]; - const format = '300,250;336,280'; - validBannerBidReq.sizes = sizes; + it('must parse bid size from a nested array', function () { + const width = 640; + const height = 480; + validBannerBidReq.sizes = [[ width, height ]]; const requests = spec.buildRequests([ validBannerBidReq ], bidderRequest); const data = requests[0].data; - expect(data.w).to.equal(sizes[0][0]); - expect(data.h).to.equal(sizes[0][1]); - expect(data.format).to.equal(format); + expect(data.w).to.equal(width); + expect(data.h).to.equal(height); }); it('should set bidfloor if configured', function() { - let bid = deepClone(validBannerBidReq); + let bid = Object.assign({}, validBannerBidReq); bid.getFloor = function() { return { currency: 'USD', @@ -205,7 +200,7 @@ describe('ucfunnel Adapter', function () { }); it('should set bidfloor if configured', function() { - let bid = deepClone(validBannerBidReq); + let bid = Object.assign({}, validBannerBidReq); bid.params.bidfloor = 2.01; const requests = spec.buildRequests([ bid ], bidderRequest); const data = requests[0].data; @@ -213,7 +208,7 @@ describe('ucfunnel Adapter', function () { }); it('should set bidfloor if configured', function() { - let bid = deepClone(validBannerBidReq); + let bid = Object.assign({}, validBannerBidReq); bid.getFloor = function() { return { currency: 'USD', @@ -229,12 +224,8 @@ describe('ucfunnel Adapter', function () { describe('interpretResponse', function () { describe('should support banner', function () { - let request, result; - before(() => { - request = spec.buildRequests([ validBannerBidReq ], bidderRequest); - result = spec.interpretResponse({body: validBannerBidRes}, request[0]); - }); - + const request = spec.buildRequests([ validBannerBidReq ], bidderRequest); + const result = spec.interpretResponse({body: validBannerBidRes}, request[0]); it('should build bid array for banner', function () { expect(result.length).to.equal(1); }); @@ -252,11 +243,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]); - }) + const request = spec.buildRequests([ validBannerBidReq ], bidderRequest); + const result = spec.interpretResponse({body: invalidBannerBidRes}, request[0]); it('should build bid array for banner', function () { expect(result.length).to.equal(1); }); @@ -273,11 +261,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]); - }) + const request = spec.buildRequests([ validBannerBidReq ], bidderRequest); + const result = spec.interpretResponse({body: invalidBannerBidRes}, request[0]); it('should build bid array for banner', function () { expect(result.length).to.equal(1); }); @@ -294,11 +279,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]); - }) + const request = spec.buildRequests([ validVideoBidReq ], bidderRequest); + const result = spec.interpretResponse({body: validVideoBidRes}, request[0]); it('should build bid array', function () { expect(result.length).to.equal(1); }); @@ -317,11 +299,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]); - }) + const request = spec.buildRequests([ validNativeBidReq ], bidderRequest); + const result = spec.interpretResponse({body: validNativeBidRes}, request[0]); it('should build bid array', function () { expect(result.length).to.equal(1); }); diff --git a/test/spec/modules/uid2IdSystem_helpers.js b/test/spec/modules/uid2IdSystem_helpers.js index e0bef047acb..65d52c1d7c3 100644 --- a/test/spec/modules/uid2IdSystem_helpers.js +++ b/test/spec/modules/uid2IdSystem_helpers.js @@ -1,6 +1,6 @@ -import {setConsentConfig} from 'modules/consentManagement.js'; -import {server} from 'test/mocks/xhr.js'; -import {coreStorage, requestBidsHook} from 'modules/userId/index.js'; +import { setConsentConfig } from 'modules/consentManagement.js'; +import { server } from 'test/mocks/xhr.js'; +import {coreStorage, init, setSubmoduleRegistry, requestBidsHook} from 'modules/userId/index.js'; const msIn12Hours = 60 * 60 * 12 * 1000; const expireCookieDate = 'Thu, 01 Jan 1970 00:00:01 GMT'; @@ -26,16 +26,16 @@ export const runAuction = async () => { } export const apiHelpers = { - makeTokenResponse: (token, shouldRefresh = false, expired = false, refreshExpired = false) => ({ + makeTokenResponse: (token, shouldRefresh = false, expired = false) => ({ advertising_token: token, refresh_token: 'fake-refresh-token', identity_expires: expired ? Date.now() - 1000 : Date.now() + 60 * 60 * 1000, refresh_from: shouldRefresh ? Date.now() - 1000 : Date.now() + 60 * 1000, - refresh_expires: refreshExpired ? Date.now() - 1000 : Date.now() + 24 * 60 * 60 * 1000, // 24 hours + refresh_expires: Date.now() + 24 * 60 * 60 * 1000, // 24 hours refresh_response_key: 'wR5t6HKMfJ2r4J7fEGX9Gw==', // Fake data }), - respondAfterDelay: (delay, srv = server) => new Promise((resolve) => setTimeout(() => { - srv.respond(); + respondAfterDelay: (delay) => new Promise((resolve) => setTimeout(() => { + server.respond(); setTimeout(() => resolve()); }, delay)), } diff --git a/test/spec/modules/uid2IdSystem_spec.js b/test/spec/modules/uid2IdSystem_spec.js index 901e0c57e32..20a38a292bb 100644 --- a/test/spec/modules/uid2IdSystem_spec.js +++ b/test/spec/modules/uid2IdSystem_spec.js @@ -1,17 +1,17 @@ /* eslint-disable no-console */ -import {coreStorage, init, setSubmoduleRegistry} from 'modules/userId/index.js'; +import {coreStorage, init, setSubmoduleRegistry, requestBidsHook} 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 'src/prebid.js'; import 'modules/consentManagement.js'; import { getGlobal } from 'src/prebidGlobal.js'; +import { server } from 'test/mocks/xhr.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 uninstallGdprEnforcement} from 'modules/gdprEnforcement.js'; -import {server} from 'test/mocks/xhr'; let expect = require('chai').expect; @@ -24,22 +24,16 @@ const auctionDelayMs = 10; const initialToken = `initial-advertising-token`; const legacyToken = 'legacy-advertising-token'; const refreshedToken = 'refreshed-advertising-token'; -const clientSideGeneratedToken = 'client-side-generated-advertising-token'; 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}}); let useLocalStorage = false; const makePrebidConfig = (params = null, extraSettings = {}, debug = false) => ({ userSync: { auctionDelay: auctionDelayMs, userIds: [{name: 'uid2', params: {storage: useLocalStorage ? 'localStorage' : 'cookie', ...params}}] }, debug, ...extraSettings }); -const makeOriginalIdentity = (identity, salt = 1) => ({ - identity: utils.cyrb53Hash(identity, salt), - salt -}) const getFromAppropriateStorage = () => { if (useLocalStorage) return coreStorage.getDataFromLocalStorage(moduleCookieName); @@ -53,18 +47,31 @@ const expectGlobalToHaveToken = (token) => expect(getGlobal().getUserIds()).to.d const expectGlobalToHaveNoUid2 = () => expect(getGlobal().getUserIds()).to.not.haveOwnProperty('uid2'); const expectNoLegacyToken = (bid) => expect(bid.userId).to.not.deep.include(makeUid2IdentityContainer(legacyToken)); const expectModuleStorageEmptyOrMissing = () => expect(getFromAppropriateStorage()).to.be.null; -const expectModuleStorageToContain = (originalAdvertisingToken, latestAdvertisingToken, originalIdentity) => { +const expectModuleStorageToContain = (initialIdentity, latestIdentity) => { const cookie = JSON.parse(getFromAppropriateStorage()); - if (originalAdvertisingToken) expect(cookie.originalToken.advertising_token).to.equal(originalAdvertisingToken); - if (latestAdvertisingToken) expect(cookie.latestToken.advertising_token).to.equal(latestAdvertisingToken); - if (originalIdentity) expect(cookie.originalIdentity).to.eql(makeOriginalIdentity(Object.values(originalIdentity)[0], cookie.originalIdentity.salt)); + if (initialIdentity) expect(cookie.originalToken.advertising_token).to.equal(initialIdentity); + if (latestIdentity) expect(cookie.latestToken.advertising_token).to.equal(latestIdentity); } -const apiUrl = 'https://prod.uidapi.com/v2/token' -const refreshApiUrl = `${apiUrl}/refresh`; +const apiUrl = 'https://prod.uidapi.com/v2/token/refresh'; const headers = { 'Content-Type': 'application/json' }; -const makeSuccessResponseBody = (responseToken) => btoa(JSON.stringify({ status: 'success', body: { ...apiHelpers.makeTokenResponse(initialToken), advertising_token: responseToken } })); -const cstgApiUrl = `${apiUrl}/client-generate`; +const makeSuccessResponseBody = () => btoa(JSON.stringify({ status: 'success', body: { ...apiHelpers.makeTokenResponse(initialToken), advertising_token: refreshedToken } })); +const configureUid2Response = (httpStatus, response) => server.respondWith('POST', apiUrl, (xhr) => xhr.respond(httpStatus, headers, response)); +const configureUid2ApiSuccessResponse = () => configureUid2Response(200, makeSuccessResponseBody()); +const configureUid2ApiFailResponse = () => configureUid2Response(500, 'Error'); + +// Runs the provided test twice - once with a successful API mock, once with one which returns a server error +const testApiSuccessAndFailure = (act, testDescription, failTestDescription, only = false) => { + const testFn = only ? it.only : it; + testFn(`API responds successfully: ${testDescription}`, async function() { + configureUid2ApiSuccessResponse(); + await act(true); + }); + testFn(`API responds with an error: ${failTestDescription ?? testDescription}`, async function() { + configureUid2ApiFailResponse(); + await act(false); + }); +} const testCookieAndLocalStorage = (description, test, only = false) => { const describeFn = only ? describe.only : describe; @@ -97,18 +104,10 @@ describe(`UID2 module`, function () { // I've confirmed it's available in Firefox since v34 (it seems to be unavailable on BrowserStack in Firefox v106). if (typeof window.crypto.subtle === 'undefined') { restoreSubtleToUndefined = true; - window.crypto.subtle = { importKey: () => {}, digest: () => {}, decrypt: () => {}, deriveKey: () => {}, encrypt: () => {}, generateKey: () => {}, exportKey: () => {} }; + window.crypto.subtle = { importKey: () => {}, decrypt: () => {} }; } suiteSandbox.stub(window.crypto.subtle, 'importKey').callsFake(() => Promise.resolve()); - suiteSandbox.stub(window.crypto.subtle, 'digest').callsFake(() => Promise.resolve('hashed_value')); suiteSandbox.stub(window.crypto.subtle, 'decrypt').callsFake((settings, key, data) => Promise.resolve(new Uint8Array([...settings.iv, ...data]))); - suiteSandbox.stub(window.crypto.subtle, 'deriveKey').callsFake(() => Promise.resolve()); - suiteSandbox.stub(window.crypto.subtle, 'exportKey').callsFake(() => Promise.resolve()); - suiteSandbox.stub(window.crypto.subtle, 'encrypt').callsFake(() => Promise.resolve(new ArrayBuffer())); - suiteSandbox.stub(window.crypto.subtle, 'generateKey').callsFake(() => Promise.resolve({ - privateKey: {}, - publicKey: {} - })); }); after(function () { @@ -117,30 +116,14 @@ describe(`UID2 module`, function () { if (restoreSubtleToUndefined) window.crypto.subtle = undefined; }); - const configureUid2Response = (apiUrl, httpStatus, response) => server.respondWith('POST', apiUrl, (xhr) => xhr.respond(httpStatus, headers, response)); - const configureUid2ApiSuccessResponse = (apiUrl, responseToken) => configureUid2Response(apiUrl, 200, makeSuccessResponseBody(responseToken)); - const configureUid2ApiFailResponse = (apiUrl) => configureUid2Response(apiUrl, 500, 'Error'); - // Runs the provided test twice - once with a successful API mock, once with one which returns a server error - const testApiSuccessAndFailure = (act, apiUrl, testDescription, failTestDescription, only = false, responseToken = refreshedToken) => { - const testFn = only ? it.only : it; - testFn(`API responds successfully: ${testDescription}`, async function() { - configureUid2ApiSuccessResponse(apiUrl, responseToken); - await act(true); - }); - testFn(`API responds with an error: ${failTestDescription ?? testDescription}`, async function() { - configureUid2ApiFailResponse(apiUrl); - await act(false); - }); - } - const getFullTestTitle = (test) => `${test.parent.title ? getFullTestTitle(test.parent) + ' | ' : ''}${test.title}`; - beforeEach(function () { debugOutput(`----------------- START TEST ------------------`); fullTestTitle = getFullTestTitle(this.test.ctx.currentTest); debugOutput(fullTestTitle); testSandbox = sinon.sandbox.create(); testSandbox.stub(utils, 'logWarn'); + init(config); setSubmoduleRegistry([uid2IdSubmodule]); }); @@ -160,6 +143,7 @@ describe(`UID2 module`, function () { } cookieHelpers.clearCookies(moduleCookieName, publisherCookieName); coreStorage.removeDataFromLocalStorage(moduleCookieName); + debugOutput('----------------- END TEST ------------------'); }); @@ -167,13 +151,13 @@ describe(`UID2 module`, function () { it('When no baseUrl is provided in config, the module calls the production endpoint', function() { const uid2Token = apiHelpers.makeTokenResponse(initialToken, true, true); config.setConfig(makePrebidConfig({uid2Token})); - expect(server.requests[0]?.url).to.have.string('https://prod.uidapi.com/v2/token/refresh'); + expect(server.requests[0]?.url).to.have.string('https://prod.uidapi.com/'); }); it('When a baseUrl is provided in config, the module calls the provided endpoint', function() { const uid2Token = apiHelpers.makeTokenResponse(initialToken, true, true); config.setConfig(makePrebidConfig({uid2Token, uid2ApiBase: 'https://operator-integ.uidapi.com'})); - expect(server.requests[0]?.url).to.have.string('https://operator-integ.uidapi.com/v2/token/refresh'); + expect(server.requests[0]?.url).to.have.string('https://operator-integ.uidapi.com/'); }); }); @@ -254,7 +238,7 @@ describe(`UID2 module`, function () { cookieHelpers.setPublisherCookie(publisherCookieName, token); config.setConfig(makePrebidConfig(serverCookieConfigParams, extraConfig)); }, - }, + } ] scenarios.forEach(function(scenario) { @@ -263,16 +247,16 @@ describe(`UID2 module`, function () { describe('When the refresh is available in time', function() { testApiSuccessAndFailure(async function(apiSucceeds) { scenario.setConfig(apiHelpers.makeTokenResponse(initialToken, true, true)); - apiHelpers.respondAfterDelay(auctionDelayMs / 10, server); + apiHelpers.respondAfterDelay(auctionDelayMs / 10); const bid = await runAuction(); if (apiSucceeds) expectToken(bid, refreshedToken); else expectNoIdentity(bid); - }, refreshApiUrl, 'it should be used in the auction', 'the auction should have no uid2'); + }, 'it should be used in the auction', 'the auction should have no uid2'); testApiSuccessAndFailure(async function(apiSucceeds) { scenario.setConfig(apiHelpers.makeTokenResponse(initialToken, true, true)); - apiHelpers.respondAfterDelay(auctionDelayMs / 10, server); + apiHelpers.respondAfterDelay(auctionDelayMs / 10); await runAuction(); if (apiSucceeds) { @@ -280,18 +264,18 @@ describe(`UID2 module`, function () { } else { expectModuleStorageEmptyOrMissing(); } - }, refreshApiUrl, 'the refreshed token should be stored in the module storage', 'the module storage should not be set'); + }, 'the refreshed token should be stored in the module storage', 'the module storage should not be set'); }); describe(`when the response doesn't arrive before the auction timer`, function() { testApiSuccessAndFailure(async function() { scenario.setConfig(apiHelpers.makeTokenResponse(initialToken, true, true)); const bid = await runAuction(); expectNoIdentity(bid); - }, refreshApiUrl, 'it should run the auction'); + }, 'it should run the auction'); testApiSuccessAndFailure(async function(apiSucceeds) { scenario.setConfig(apiHelpers.makeTokenResponse(initialToken, true, true)); - const promise = apiHelpers.respondAfterDelay(auctionDelayMs * 2, server); + const promise = apiHelpers.respondAfterDelay(auctionDelayMs * 2); const bid = await runAuction(); expectNoIdentity(bid); @@ -299,7 +283,7 @@ describe(`UID2 module`, function () { await promise; if (apiSucceeds) expectGlobalToHaveToken(refreshedToken); else expectGlobalToHaveNoUid2(); - }, refreshApiUrl, 'it should update the userId after the auction', 'there should be no global identity'); + }, 'it should update the userId after the auction', 'there should be no global identity'); }) describe('and there is a refreshed token in the module cookie', function() { it('the refreshed value from the cookie is used', async function() { @@ -335,13 +319,13 @@ describe(`UID2 module`, function () { scenario.setConfig(apiHelpers.makeTokenResponse(initialToken, true), {auctionDelay: 0, syncDelay: 1}); }); testApiSuccessAndFailure(async function() { - apiHelpers.respondAfterDelay(10, server); + apiHelpers.respondAfterDelay(10); const bid = await runAuction(); expectToken(bid, initialToken); - }, refreshApiUrl, 'it should not be refreshed before the auction runs'); + }, 'it should not be refreshed before the auction runs'); testApiSuccessAndFailure(async function(success) { - const promise = apiHelpers.respondAfterDelay(1, server); + const promise = apiHelpers.respondAfterDelay(1); await runAuction(); await promise; if (success) { @@ -349,7 +333,7 @@ describe(`UID2 module`, function () { } else { expectModuleStorageToContain(initialToken, initialToken); } - }, refreshApiUrl, 'the refreshed token should be stored in the module cookie after the auction runs', 'the module cookie should only have the original token'); + }, 'the refreshed token should be stored in the module cookie after the auction runs', 'the module cookie should only have the original token'); it('it should use the current token in the auction', async function() { const bid = await runAuction(); @@ -358,273 +342,4 @@ describe(`UID2 module`, function () { }); }); }); - - if (FEATURES.UID2_CSTG) { - describe('When CSTG is enabled provided', function () { - const scenarios = [ - { - name: 'email provided in config', - identity: { email: 'test@example.com' }, - setConfig: function (extraConfig) { config.setConfig(makePrebidConfig({ ...cstgConfigParams, ...this.identity }, extraConfig)) }, - setInvalidConfig: (extraConfig) => config.setConfig(makePrebidConfig({ ...cstgConfigParams, email: 'test . test@gmail.com' }, extraConfig)) - }, - { - name: 'phone provided in config', - identity: { phone: '+12345678910' }, - setConfig: function (extraConfig) { config.setConfig(makePrebidConfig({ ...cstgConfigParams, ...this.identity }, extraConfig)) }, - setInvalidConfig: (extraConfig) => config.setConfig(makePrebidConfig({ ...cstgConfigParams, phone: 'test123' }, extraConfig)) - }, - { - name: 'email hash provided in config', - identity: { email_hash: 'lz3+Rj7IV4X1+Vr1ujkG7tstkxwk5pgkqJ6mXbpOgTs=' }, - setConfig: function (extraConfig) { config.setConfig(makePrebidConfig({ ...cstgConfigParams, emailHash: this.identity.email_hash }, extraConfig)) }, - setInvalidConfig: (extraConfig) => config.setConfig(makePrebidConfig({ ...cstgConfigParams, emailHash: 'test@example.com' }, extraConfig)) - }, - { - name: 'phone hash provided in config', - identity: { phone_hash: 'kVJ+4ilhrqm3HZDDnCQy4niZknvCoM4MkoVzZrQSdJw=' }, - setConfig: function (extraConfig) { config.setConfig(makePrebidConfig({ ...cstgConfigParams, phoneHash: this.identity.phone_hash }, extraConfig)) }, - setInvalidConfig: (extraConfig) => config.setConfig(makePrebidConfig({ ...cstgConfigParams, phoneHash: '614332222111' }, extraConfig)) - }, - ] - scenarios.forEach(function(scenario) { - describe(`And ${scenario.name}`, function() { - describe(`When invalid identity is provided`, function() { - it('the auction should have no uid2', async function () { - scenario.setInvalidConfig() - const bid = await runAuction(); - expectNoIdentity(bid); - expectGlobalToHaveNoUid2(); - expectModuleStorageEmptyOrMissing(); - }) - }); - - describe('When valid identity is provided, and the auction is set to run immediately', function() { - it('it should ignores token provided in config, and the auction should have no uid2', async function() { - scenario.setConfig({ uid2Token: apiHelpers.makeTokenResponse(initialToken), auctionDelay: 0, syncDelay: 1 }); - const bid = await runAuction(); - expectNoIdentity(bid); - expectGlobalToHaveNoUid2(); - expectModuleStorageEmptyOrMissing(); - }) - - it('it should ignores token provided in server-set cookie', async function() { - cookieHelpers.setPublisherCookie(publisherCookieName, initialToken); - scenario.setConfig({ ...newServerCookieConfigParams, auctionDelay: 0, syncDelay: 1 }) - const bid = await runAuction(); - expectNoIdentity(bid); - expectGlobalToHaveNoUid2(); - expectModuleStorageEmptyOrMissing(); - }) - - describe('When the token generated in time', function() { - testApiSuccessAndFailure(async function(apiSucceeds) { - scenario.setConfig(); - apiHelpers.respondAfterDelay(auctionDelayMs / 10, server); - const bid = await runAuction(); - - if (apiSucceeds) expectToken(bid, clientSideGeneratedToken); - else expectNoIdentity(bid); - }, cstgApiUrl, 'it should be used in the auction', 'the auction should have no uid2', false, clientSideGeneratedToken); - - testApiSuccessAndFailure(async function(apiSucceeds) { - scenario.setConfig(); - apiHelpers.respondAfterDelay(auctionDelayMs / 10, server); - - await runAuction(); - if (apiSucceeds) { - expectModuleStorageToContain(undefined, clientSideGeneratedToken, scenario.identity); - } else { - expectModuleStorageEmptyOrMissing(); - } - }, cstgApiUrl, 'the generated token should be stored in the module storage', 'the module storage should not be set', false, clientSideGeneratedToken); - }); - }); - }); - }); - describe(`when the response doesn't arrive before the auction timer`, function() { - testApiSuccessAndFailure(async function() { - config.setConfig(makePrebidConfig({ ...cstgConfigParams, email: 'test@test.com' })); - const bid = await runAuction(); - expectNoIdentity(bid); - }, cstgApiUrl, 'it should run the auction', undefined, false, clientSideGeneratedToken); - - testApiSuccessAndFailure(async function(apiSucceeds) { - config.setConfig(makePrebidConfig({ ...cstgConfigParams, email: 'test@test.com' })); - const promise = apiHelpers.respondAfterDelay(auctionDelayMs * 2, server); - - const bid = await runAuction(); - expectNoIdentity(bid); - expectGlobalToHaveNoUid2(); - await promise; - if (apiSucceeds) expectGlobalToHaveToken(clientSideGeneratedToken); - else expectGlobalToHaveNoUid2(); - }, cstgApiUrl, 'it should update the userId after the auction', 'there should be no global identity', false, clientSideGeneratedToken); - }) - - describe('when there is a token in the module cookie', function() { - describe('when originalIdentity matches', 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}; - coreStorage.setCookie(moduleCookieName, JSON.stringify(moduleCookie), cookieHelpers.getFutureCookieExpiry()); - config.setConfig(makePrebidConfig({ ...cstgConfigParams, email: 'test@test.com', auctionDelay: 0, syncDelay: 1 })); - const bid = await runAuction(); - expectToken(bid, refreshedToken); - }); - }) - - 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}; - coreStorage.setCookie(moduleCookieName, JSON.stringify(moduleCookie), cookieHelpers.getFutureCookieExpiry()); - config.setConfig(makePrebidConfig({ ...cstgConfigParams, email: 'test@test.com' })); - apiHelpers.respondAfterDelay(auctionDelayMs / 10, server); - - const bid = await runAuction(); - - if (apiSucceeds) expectToken(bid, refreshedToken); - else expectNoIdentity(bid); - }, refreshApiUrl, 'it should use refreshed token in the auction', 'the auction should have no uid2'); - }) - - 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}; - coreStorage.setCookie(moduleCookieName, JSON.stringify(moduleCookie), cookieHelpers.getFutureCookieExpiry()); - config.setConfig(makePrebidConfig({ ...cstgConfigParams, email: 'test@test.com' })); - apiHelpers.respondAfterDelay(auctionDelayMs / 10, server); - - const bid = await runAuction(); - - if (apiSucceeds) expectToken(bid, clientSideGeneratedToken); - else expectNoIdentity(bid); - }, cstgApiUrl, 'it should use generated token in the auction', 'the auction should have no uid2', false, clientSideGeneratedToken); - }) - }) - - 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}; - coreStorage.setCookie(moduleCookieName, JSON.stringify(moduleCookie), cookieHelpers.getFutureCookieExpiry()); - config.setConfig(makePrebidConfig({ ...cstgConfigParams, email: 'test@test.com' })); - const bid = await runAuction(); - expectNoIdentity(bid); - }); - }) - }); - describe('When invalid CSTG configuration is provided', function () { - const invalidConfigs = [ - { - name: 'CSTG option is not a object', - cstgOptions: '' - }, - { - name: 'CSTG option is null', - cstgOptions: '' - }, - { - name: 'serverPublicKey is not a string', - cstgOptions: { subscriptionId: cstgConfigParams.subscriptionId, serverPublicKey: {} } - }, - { - name: 'serverPublicKey not match regular expression', - cstgOptions: { subscriptionId: cstgConfigParams.subscriptionId, serverPublicKey: 'serverPublicKey' } - }, - { - name: 'subscriptionId is not a string', - cstgOptions: { subscriptionId: {}, serverPublicKey: cstgConfigParams.serverPublicKey } - }, - { - name: 'subscriptionId is empty', - cstgOptions: { subscriptionId: '', serverPublicKey: cstgConfigParams.serverPublicKey } - }, - ] - invalidConfigs.forEach(function(scenario) { - describe(`When ${scenario.name}`, function() { - it('should not generate token using identity', async () => { - config.setConfig(makePrebidConfig({ ...scenario.cstgOptions, email: 'test@email.com' })); - const bid = await runAuction(); - expectNoIdentity(bid); - expectGlobalToHaveNoUid2(); - expectModuleStorageEmptyOrMissing(); - }); - }); - }); - }); - describe('When email is provided in different format', function () { - const testCases = [ - { originalEmail: 'TEst.TEST@Test.com ', normalizedEmail: 'test.test@test.com' }, - { originalEmail: 'test+test@test.com', normalizedEmail: 'test+test@test.com' }, - { originalEmail: ' testtest@test.com ', normalizedEmail: 'testtest@test.com' }, - { originalEmail: 'TEst.TEst+123@GMail.Com', normalizedEmail: 'testtest@gmail.com' } - ]; - testCases.forEach((testCase) => { - describe('it should normalize the email and generate token on normalized email', async () => { - testApiSuccessAndFailure(async function(apiSucceeds) { - config.setConfig(makePrebidConfig({ ...cstgConfigParams, email: testCase.originalEmail })); - apiHelpers.respondAfterDelay(auctionDelayMs / 10, server); - - await runAuction(); - if (apiSucceeds) { - expectModuleStorageToContain(undefined, clientSideGeneratedToken, { email: testCase.normalizedEmail }); - } else { - expectModuleStorageEmptyOrMissing(); - } - }, cstgApiUrl, 'the generated token should be stored in the module storage', 'the module storage should not be set', false, clientSideGeneratedToken); - }); - }); - }); - } - - describe('When neither token nor CSTG config provided', 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}; - coreStorage.setCookie(moduleCookieName, JSON.stringify(moduleCookie), cookieHelpers.getFutureCookieExpiry()); - config.setConfig(makePrebidConfig({})); - const bid = await runAuction(); - expectToken(bid, initialToken); - }) - - 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}; - coreStorage.setCookie(moduleCookieName, JSON.stringify(moduleCookie), cookieHelpers.getFutureCookieExpiry()); - config.setConfig(makePrebidConfig({})); - const bid = await runAuction(); - expectNoIdentity(bid); - }) - }) - - 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}; - coreStorage.setCookie(moduleCookieName, JSON.stringify(moduleCookie), cookieHelpers.getFutureCookieExpiry()); - config.setConfig(makePrebidConfig({})); - const bid = await runAuction(); - expectToken(bid, initialToken); - }) - - 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}; - coreStorage.setCookie(moduleCookieName, JSON.stringify(moduleCookie), cookieHelpers.getFutureCookieExpiry()); - config.setConfig(makePrebidConfig({})); - const bid = await runAuction(); - expectNoIdentity(bid); - }) - }) - - it('the auction should has no uid2', async function () { - config.setConfig(makePrebidConfig({})); - const bid = await runAuction(); - expectNoIdentity(bid); - }) - }) }); diff --git a/test/spec/modules/underdogmediaBidAdapter_spec.js b/test/spec/modules/underdogmediaBidAdapter_spec.js index c0e2e8dddce..2d7c1f11178 100644 --- a/test/spec/modules/underdogmediaBidAdapter_spec.js +++ b/test/spec/modules/underdogmediaBidAdapter_spec.js @@ -5,7 +5,6 @@ import { spec, resetUserSync } from 'modules/underdogmediaBidAdapter.js'; -import { config } from '../../../src/config'; describe('UnderdogMedia adapter', function () { let bidRequests; @@ -764,20 +763,6 @@ describe('UnderdogMedia adapter', function () { expect(request.data.ref).to.equal(undefined); }); - it('should have pbTimeout to be 3001 if bidder timeout does not exists', function () { - config.setConfig({ bidderTimeout: '' }) - const request = spec.buildRequests(bidRequests, bidderRequest); - - expect(request.data.pbTimeout).to.equal(3001) - }) - - it('should have pbTimeout to be a numerical value if bidder timeout is in a string', function () { - config.setConfig({ bidderTimeout: '1000' }) - const request = spec.buildRequests(bidRequests, bidderRequest); - - expect(request.data.pbTimeout).to.equal(1000) - }) - it('should have pubcid if it exists', function () { let bidRequests = [{ adUnitCode: 'div-gpt-ad-1460505748561-0', diff --git a/test/spec/modules/undertoneBidAdapter_spec.js b/test/spec/modules/undertoneBidAdapter_spec.js index 5cf53c661a9..7f9c2e7b3d5 100644 --- a/test/spec/modules/undertoneBidAdapter_spec.js +++ b/test/spec/modules/undertoneBidAdapter_spec.js @@ -39,19 +39,12 @@ const videoBidReq = [{ maxDuration: 30 } }, - ortb2Imp: { - ext: { - gpid: '/1111/gpid#728x90', - } - }, - mediaTypes: { - video: { - context: 'outstream', - playerSize: [640, 480], - placement: 1, - plcmt: 1 - } - }, + mediaTypes: {video: { + context: 'outstream', + playerSize: [640, 480], + placement: 1, + plcmt: 1 + }}, sizes: [[300, 250], [300, 600]], bidId: '263be71e91dd9d', auctionId: '9ad1fa8d-2297-4660-a018-b39945054746' @@ -63,19 +56,10 @@ const videoBidReq = [{ placementId: '10433395', publisherId: 12345 }, - ortb2Imp: { - ext: { - data: { - pbadslot: '/1111/pbadslot#728x90' - } - } - }, - mediaTypes: { - video: { - context: 'outstream', - playerSize: [640, 480] - } - }, + mediaTypes: {video: { + context: 'outstream', + playerSize: [640, 480] + }}, sizes: [[300, 250], [300, 600]], bidId: '263be71e91dd9d', auctionId: '9ad1fa8d-2297-4660-a018-b39945054746' @@ -479,14 +463,12 @@ describe('Undertone Adapter', () => { expect(bidVideo.video.skippable).to.equal(true); expect(bidVideo.video.placement).to.equal(1); expect(bidVideo.video.plcmt).to.equal(1); - expect(bidVideo.gpid).to.equal('/1111/gpid#728x90'); expect(bidVideo2.video.skippable).to.equal(null); expect(bidVideo2.video.maxDuration).to.equal(null); expect(bidVideo2.video.playbackMethod).to.equal(null); expect(bidVideo2.video.placement).to.equal(null); expect(bidVideo2.video.plcmt).to.equal(null); - expect(bidVideo2.gpid).to.equal('/1111/pbadslot#728x90'); }); it('should send all userIds data to server', function () { const request = spec.buildRequests(bidReqUserIds, bidderReq); diff --git a/test/spec/modules/unicornBidAdapter_spec.js b/test/spec/modules/unicornBidAdapter_spec.js index bd9175dac1e..0abb09bfb78 100644 --- a/test/spec/modules/unicornBidAdapter_spec.js +++ b/test/spec/modules/unicornBidAdapter_spec.js @@ -1,5 +1,4 @@ import {assert, expect} from 'chai'; -import * as utils from 'src/utils.js'; import {spec} from 'modules/unicornBidAdapter.js'; import * as _ from 'lodash'; @@ -497,17 +496,6 @@ describe('unicornBidAdapterTest', () => { }); describe('buildBidRequest', () => { - const removeUntestableAttrs = data => { - delete data['device']; - delete data['site']['domain']; - delete data['site']['page']; - delete data['id']; - data['imp'].forEach(imp => { - delete imp['id']; - }) - delete data['user']['id']; - return data; - }; before(function () { $$PREBID_GLOBAL$$.bidderSettings = { unicorn: { @@ -520,6 +508,17 @@ describe('unicornBidAdapterTest', () => { }); it('buildBidRequest', () => { const req = spec.buildRequests(validBidRequests, bidderRequest); + const removeUntestableAttrs = data => { + delete data['device']; + delete data['site']['domain']; + delete data['site']['page']; + delete data['id']; + data['imp'].forEach(imp => { + delete imp['id']; + }) + delete data['user']['id']; + return data; + }; const uid = JSON.parse(req.data)['user']['id']; const reqData = removeUntestableAttrs(JSON.parse(req.data)); const openRTBRequestData = removeUntestableAttrs(openRTBRequest); @@ -528,28 +527,6 @@ describe('unicornBidAdapterTest', () => { const uid2 = JSON.parse(req2.data)['user']['id']; assert.deepStrictEqual(uid, uid2); }); - it('test if contains ID5', () => { - let _validBidRequests = utils.deepClone(validBidRequests); - _validBidRequests[0].userId = { - id5id: { - uid: 'id5_XXXXX' - } - } - const req = spec.buildRequests(_validBidRequests, bidderRequest); - const reqData = removeUntestableAttrs(JSON.parse(req.data)); - const openRTBRequestData = removeUntestableAttrs(utils.deepClone(openRTBRequest)); - openRTBRequestData.user.eids = [ - { - source: 'id5-sync.com', - uids: [ - { - id: 'id5_XXXXX' - } - ] - } - ] - assert.deepStrictEqual(reqData, openRTBRequestData); - }) }); describe('interpretResponse', () => { diff --git a/test/spec/modules/unrulyBidAdapter_spec.js b/test/spec/modules/unrulyBidAdapter_spec.js index abf1a54787d..6d1d8f9949f 100644 --- a/test/spec/modules/unrulyBidAdapter_spec.js +++ b/test/spec/modules/unrulyBidAdapter_spec.js @@ -42,40 +42,9 @@ 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) { - let bids = []; - if (Array.isArray(bidList)) { - bids = bidList; - } else if (bidList) { - bids.push(bidList); - } - - if (!auctionConfigs) { - return { - 'body': {bids} - }; - } - - return { - 'body': { - bids, - auctionConfigs - } - } - }; + const createExchangeResponse = (...bids) => ({ + body: {bids} + }); const inStreamServerResponse = { 'requestId': '262594d5d1f8104', @@ -517,8 +486,7 @@ describe('UnrulyAdapter', function () { 'bidderRequestId': '12e00d17dff07b' } ], - 'invalidBidsCount': 0, - 'prebidVersion': '$prebid.version$' + 'invalidBidsCount': 0 } }; @@ -592,8 +560,7 @@ describe('UnrulyAdapter', function () { 'bidderRequestId': '12e00d17dff07b', } ], - 'invalidBidsCount': 0, - 'prebidVersion': '$prebid.version$' + 'invalidBidsCount': 0 } }; @@ -684,235 +651,13 @@ describe('UnrulyAdapter', function () { 'bidderRequestId': '12e00d17dff07b', } ], - 'invalidBidsCount': 0, - 'prebidVersion': '$prebid.version$' + 'invalidBidsCount': 0 } }; let 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', - 'fledgeEnabled': 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 - } - } - } - ] - }; - - let 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', - 'fledgeEnabled': 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': {} - } - } - ] - }; - - let 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 - } - } - } - ] - }; - - let 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 () { @@ -960,167 +705,7 @@ describe('UnrulyAdapter', function () { renderer: fakeRenderer, mediaType: 'video' } - ]); - }); - - it('should return object with an array of bids and an array of auction configs when it receives a successful response from server', function () { - let 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' - } - ], - 'fledgeAuctionConfigs': [{ - '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 () { - let 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': [], - 'fledgeAuctionConfigs': [{ - '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 () { @@ -1290,7 +875,7 @@ describe('UnrulyAdapter', function () { it('should return correct response for multiple bids', function () { const outStreamServerResponse = createOutStreamExchangeBid({adUnitCode: 'video1', requestId: 'mockBidId'}); - const mockServerResponse = createExchangeResponse([outStreamServerResponse, inStreamServerResponse, bannerServerResponse]); + const mockServerResponse = createExchangeResponse(outStreamServerResponse, inStreamServerResponse, bannerServerResponse); const expectedOutStreamResponse = outStreamServerResponse; expectedOutStreamResponse.mediaType = 'video'; @@ -1305,7 +890,7 @@ describe('UnrulyAdapter', function () { it('should return only valid bids', function () { const {ad, ...bannerServerResponseNoAd} = bannerServerResponse; - const mockServerResponse = createExchangeResponse([bannerServerResponseNoAd, inStreamServerResponse]); + 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 18f49f4943e..e2d4062399b 100644 --- a/test/spec/modules/userId_spec.js +++ b/test/spec/modules/userId_spec.js @@ -2,7 +2,7 @@ import { attachIdSystem, auctionDelay, coreStorage, dep, - findRootDomain, getConsentHash, + findRootDomain, init, PBJS_USER_ID_OPTOUT_NAME, requestBidsHook, @@ -12,7 +12,7 @@ import { setSubmoduleRegistry, syncDelay, } from 'modules/userId/index.js'; -import {createEidsArray, EID_CONFIG} from 'modules/userId/eids.js'; +import {createEidsArray} from 'modules/userId/eids.js'; import {config} from 'src/config.js'; import * as utils from 'src/utils.js'; import {getPrebidInternal} from 'src/utils.js'; @@ -21,6 +21,7 @@ import CONSTANTS from 'src/constants.json'; import {getGlobal} from 'src/prebidGlobal.js'; import {resetConsentData, } from 'modules/consentManagement.js'; import {server} from 'test/mocks/xhr.js'; +import {find} from 'src/polyfill.js'; import {unifiedIdSubmodule} from 'modules/unifiedIdSystem.js'; import {britepoolIdSubmodule} from 'modules/britepoolIdSystem.js'; import {id5IdSubmodule} from 'modules/id5IdSystem.js'; @@ -55,7 +56,7 @@ import {hook} from '../../../src/hook.js'; import {mockGdprConsent} from '../../helpers/consentData.js'; import {getPPID} from '../../../src/adserver.js'; import {uninstall as uninstallGdprEnforcement} from 'modules/gdprEnforcement.js'; -import {allConsent, GDPR_GVLIDS, gdprDataHandler} from '../../../src/consentHandler.js'; +import {GDPR_GVLIDS} 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'; @@ -85,20 +86,6 @@ describe('User ID', function () { } } - function createMockIdSubmodule(name, value, aliasName, eids) { - return { - name, - getId() { - return value; - }, - decode(v) { - return v; - }, - aliasName, - eids - } - } - function getAdUnitMock(code = 'adUnit-code') { return { code, @@ -122,6 +109,12 @@ describe('User ID', function () { return cfg; } + function findEid(eids, source) { + return find(eids, (eid) => { + if (eid.source === source) { return true; } + }); + } + let sandbox, consentData, startDelay, callbackDelay; function clearStack() { @@ -379,96 +372,11 @@ describe('User ID', function () { expect(bid).to.not.have.deep.nested.property('userIdAsEids'); }); }); + // setCookie is called once in order to store consentData + expect(coreStorage.setCookie.callCount).to.equal(1); }); }); - describe('createEidsArray', () => { - beforeEach(() => { - init(config); - setSubmoduleRegistry([ - createMockIdSubmodule('mockId1', null, null, - {'mockId1': {source: 'mock1source', atype: 1}}), - createMockIdSubmodule('mockId2v1', null, null, - {'mockId2v1': {source: 'mock2source', atype: 2, getEidExt: () => ({v: 1})}}), - createMockIdSubmodule('mockId2v2', null, null, - {'mockId2v2': {source: 'mock2source', atype: 2, getEidExt: () => ({v: 2})}}), - ]); - }); - - it('should group UIDs by source and ext', () => { - const eids = createEidsArray({ - mockId1: ['mock-1-1', 'mock-1-2'], - mockId2v1: ['mock-2-1', 'mock-2-2'], - mockId2v2: ['mock-2-1', 'mock-2-2'] - }); - expect(eids).to.eql([ - { - source: 'mock1source', - uids: [ - { - id: 'mock-1-1', - atype: 1, - }, - { - id: 'mock-1-2', - atype: 1, - } - ] - }, - { - source: 'mock2source', - ext: { - v: 1 - }, - uids: [ - { - id: 'mock-2-1', - atype: 2, - }, - { - id: 'mock-2-2', - atype: 2, - } - ] - }, - { - source: 'mock2source', - ext: { - v: 2 - }, - uids: [ - { - id: 'mock-2-1', - atype: 2, - }, - { - id: 'mock-2-2', - atype: 2, - } - ] - } - ]) - }); - - it('when merging with pubCommonId, should not alter its eids', () => { - const uid = { - pubProvidedId: [ - { - source: 'mock1Source', - uids: [ - {id: 'uid2'} - ] - } - ], - mockId1: 'uid1', - }; - 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'}]); - }); - }) - it('pbjs.getUserIds', function (done) { init(config); setSubmoduleRegistry([sharedIdSystemSubmodule]); @@ -489,139 +397,6 @@ describe('User ID', function () { }) }); - it('pbjs.getUserIds(Async) should prioritize user ids according to config available to core', () => { - 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'}}) - ]); - - config.setConfig({ - userSync: { - idPriority: { - mockId1: ['mockId3Module', 'mockId1Module'], - mockId4: ['mockId4Module', 'mockId3Module'] - }, - auctionDelay: 10, // with auctionDelay > 0, no auction is needed to complete init - userIds: [ - { name: 'mockId1Module' }, - { name: 'mockId2Module' }, - { name: 'mockId3Module' }, - { name: 'mockId4Module' } - ] - } - }); - - return getGlobal().getUserIdsAsync().then((uids) => { - expect(uids['mockId1']).to.deep.equal('mockId1_value_from_mockId3Module'); - expect(uids['mockId2']).to.deep.equal('mockId2_value'); - expect(uids['mockId3']).to.deep.equal('mockId3_value_from_mockId2Module'); - expect(uids['mockId4']).to.deep.equal('mockId4_value'); - }); - }); - - it('pbjs.getUserIds(Async) should prioritize user ids according to config available to core when config uses aliases', () => { - 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'}}) - ]); - - config.setConfig({ - userSync: { - idPriority: { - mockId1: ['mockId3Module_alias', 'mockId1Module'], - mockId4: ['mockId4Module', 'mockId3Module'] - }, - auctionDelay: 10, // with auctionDelay > 0, no auction is needed to complete init - userIds: [ - { name: 'mockId1Module' }, - { name: 'mockId2Module' }, - { name: 'mockId3Module' }, - { name: 'mockId4Module' } - ] - } - }); - - return getGlobal().getUserIdsAsync().then((uids) => { - expect(uids['mockId1']).to.deep.equal('mockId1_value_from_mockId3Module'); - expect(uids['mockId2']).to.deep.equal('mockId2_value'); - expect(uids['mockId3']).to.deep.equal('mockId3_value_from_mockId2Module'); - expect(uids['mockId4']).to.deep.equal('mockId4_value'); - }); - }); - - it('pbjs.getUserIds(Async) should prioritize user ids according to config available to core when called multiple times', () => { - 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'}}), - ]); - - config.setConfig({ - userSync: { - idPriority: { - mockId1: ['mockId2Module', 'mockId1Module'], - mockId2: ['mockId1Module', 'mockId2Module'] - }, - auctionDelay: 10, // with auctionDelay > 0, no auction is needed to complete init - userIds: [ - { name: 'mockId1Module' }, - { name: 'mockId2Module' } - ] - } - }); - - return getGlobal().getUserIdsAsync().then((uidsFirstRequest) => { - getGlobal().getUserIdsAsync().then((uidsSecondRequest) => { - expect(uidsFirstRequest['mockId1']).to.deep.equal('mockId1_value_from_mockId2Module'); - expect(uidsFirstRequest['mockId2']).to.deep.equal('mockId2_value_from_mockId1Module'); - expect(uidsFirstRequest).to.deep.equal(uidsSecondRequest); - }) - }); - }); - - it('pbjs.getUserIds(Async) with priority config but no collision', () => { - 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'}}) - ]); - - config.setConfig({ - userSync: { - idPriority: { - mockId1: ['mockId3Module', 'mockId1Module'], - mockId4: ['mockId2Module', 'mockId4Module'] - }, - auctionDelay: 10, // with auctionDelay > 0, no auction is needed to complete init - userIds: [ - { name: 'mockId1Module' }, - { name: 'mockId2Module' }, - { name: 'mockId3Module' }, - { name: 'mockId4Module' } - ] - } - }); - - return getGlobal().getUserIdsAsync().then((uids) => { - expect(uids['mockId1']).to.deep.equal('mockId1_value'); - expect(uids['mockId2']).to.deep.equal('mockId2_value'); - expect(uids['mockId3']).to.be.undefined; - expect(uids['mockId4']).to.deep.equal('mockId4_value'); - }); - }); - it('pbjs.getUserIdsAsEids', function (done) { init(config); setSubmoduleRegistry([sharedIdSystemSubmodule]); @@ -641,88 +416,6 @@ 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'}}}), - createMockIdSubmodule('mockId2Module', {id: {pubcid: 'pubcid_value', lipb: {lipbid: 'lipbid_value_from_mockId2Module'}}}), - createMockIdSubmodule('mockId3Module', {id: {uid2: {id: 'uid2_value_from_mockId3Module'}, pubcid: 'pubcid_value_from_mockId3Module', lipb: {lipbid: 'lipbid_value'}, merkleId: {id: 'merkleId_value_from_mockId3Module'}}}), - createMockIdSubmodule('mockId4Module', {id: {merkleId: {id: 'merkleId_value'}}}) - ]); - config.setConfig({ - userSync: { - idPriority: { - uid2: ['mockId3Module', 'mockId1Module'], - merkleId: ['mockId4Module', 'mockId3Module'] - }, - auctionDelay: 10, // with auctionDelay > 0, no auction is needed to complete init - userIds: [ - { name: 'mockId1Module' }, - { name: 'mockId2Module' }, - { name: 'mockId3Module' }, - { name: 'mockId4Module' } - ] - } - }); - - const ids = { - 'uid2': { id: 'uid2_value_from_mockId3Module' }, - 'pubcid': 'pubcid_value', - 'lipb': { lipbid: 'lipbid_value_from_mockId2Module' }, - 'merkleId': { id: 'merkleId_value' } - }; - - return getGlobal().getUserIdsAsync().then(() => { - const eids = getGlobal().getUserIdsAsEids(); - const expected = createEidsArray(ids); - expect(eids).to.deep.equal(expected); - }); - }); - - describe('EID updateConfig', () => { - function mockSubmod(name, eids) { - return createMockIdSubmodule(name, null, null, eids); - } - - it('does not choke if a submod does not provide an eids map', () => { - setSubmoduleRegistry([ - mockSubmod('mock1'), - mockSubmod('mock2') - ]); - expect(EID_CONFIG.size).to.equal(0); - }); - - it('should merge together submodules\' eid configs', () => { - setSubmoduleRegistry([ - 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}); - }); - - it('should respect idPriority', () => { - config.setConfig({ - userSync: { - idPriority: { - m1: ['mod2', 'mod1'], - m2: ['mod1', 'mod2'] - }, - userIds: [ - { name: 'mod1' }, - { name: 'mod2' }, - ] - } - }); - setSubmoduleRegistry([ - 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}); - }); - }) - it('should set googletag ppid correctly', function () { let adUnits = [getAdUnitMock()]; init(config); @@ -748,59 +441,6 @@ describe('User ID', function () { }); }); - it('should set googletag ppid correctly when prioritized according to config available to core', () => { - let adUnits = [getAdUnitMock()]; - 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('mockId3Module', { - id: { - uid2: { - id: 'uid2_value_from_mockId3Module_7ac66c0f148de9519b8bd264312c4d64' - }, - pubcid: 'pubcid_value_from_mockId3Module_7ac66c0f148de9519b8bd264312c4d64', - lipb: { - lipbid: 'lipbid_value_7ac66c0f148de9519b8bd264312c4d64' - }, - merkleId: { - id: 'merkleId_value_from_mockId3Module_7ac66c0f148de9519b8bd264312c4d64' - } - } - }, null, { - uid2: { - source: 'uidapi.com', - getValue(data) { return data.id } - } - }), - createMockIdSubmodule('mockId4Module', {id: {merkleId: {id: 'merkleId_value_7ac66c0f148de9519b8bd264312c4d64'}}}) - ]); - - // before ppid should not be set - expect(window.googletag._ppid).to.equal(undefined); - - config.setConfig({ - userSync: { - ppid: 'uidapi.com', - idPriority: { - uid2: ['mockId3Module', 'mockId1Module'], - merkleId: ['mockId4Module', 'mockId3Module'] - }, - userIds: [ - { name: 'mockId1Module' }, - { name: 'mockId2Module' }, - { name: 'mockId3Module' }, - { name: 'mockId4Module' } - ] - } - }); - - return expectImmediateBidHook(() => {}, {adUnits}).then(() => { - expect(window.googletag._ppid).to.equal('uid2valuefrommockId3Module7ac66c0f148de9519b8bd264312c4d64'); - }); - }); - describe('submodule callback', () => { const TEST_KEY = 'testKey'; @@ -864,11 +504,6 @@ describe('User ID', function () { }, decode(d) { return d - }, - eids: { - pubcid: { - source: 'pubcid.org', - } } }]); config.setConfig({ @@ -954,58 +589,6 @@ describe('User ID', function () { }) }); - it('should make PPID available to core and respect priority', () => { - 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('mockId3Module', { - id: { - uid2: { - id: 'uid2_value_from_mockId3Module_7ac66c0f148de9519b8bd264312c4d64' - }, - pubcid: 'pubcid_value_from_mockId3Module_7ac66c0f148de9519b8bd264312c4d64', - lipb: { - lipbid: 'lipbid_value_7ac66c0f148de9519b8bd264312c4d64' - }, - merkleId: { - id: 'merkleId_value_from_mockId3Module_7ac66c0f148de9519b8bd264312c4d64' - } - } - }, null, { - uid2: { - source: 'uidapi.com', - getValue(data) { return data.id } - } - }), - createMockIdSubmodule('mockId4Module', {id: {merkleId: {id: 'merkleId_value_7ac66c0f148de9519b8bd264312c4d64'}}}) - ]); - - // before ppid should not be set - expect(window.googletag._ppid).to.equal(undefined); - - config.setConfig({ - userSync: { - ppid: 'uidapi.com', - idPriority: { - uid2: ['mockId3Module', 'mockId1Module'], - merkleId: ['mockId4Module', 'mockId3Module'] - }, - userIds: [ - { name: 'mockId1Module' }, - { name: 'mockId2Module' }, - { name: 'mockId3Module' }, - { name: 'mockId4Module' } - ] - } - }); - - return getGlobal().refreshUserIds().then(() => { - expect(getPPID()).to.eql('uid2valuefrommockId3Module7ac66c0f148de9519b8bd264312c4d64'); - }) - }); - describe('refreshing before init is complete', () => { const MOCK_ID = {'MOCKID': '1111'}; let mockIdCallback; @@ -1013,7 +596,6 @@ describe('User ID', function () { beforeEach(() => { mockIdCallback = sinon.stub(); - coreStorage.setCookie('MOCKID', '', EXPIRED_COOKIE_DATE); let mockIdSystem = { name: 'mockId', decode: function(value) { @@ -1181,66 +763,9 @@ describe('User ID', function () { }); }); - it('pbjs.refreshUserIds updates priority config', () => { - 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'}}) - ]); - - config.setConfig({ - userSync: { - idPriority: { - mockId1: ['mockId3Module', 'mockId1Module'], - mockId4: ['mockId4Module', 'mockId3Module'] - }, - auctionDelay: 10, // with auctionDelay > 0, no auction is needed to complete init - userIds: [ - { name: 'mockId1Module' }, - { name: 'mockId2Module' }, - { name: 'mockId3Module' }, - { name: 'mockId4Module' } - ] - } - }); - - return getGlobal().getUserIdsAsync().then((uids) => { - expect(uids['mockId1']).to.deep.equal('mockId1_value_from_mockId3Module'); - expect(uids['mockId2']).to.deep.equal('mockId2_value'); - expect(uids['mockId3']).to.deep.equal('mockId3_value_from_mockId2Module'); - expect(uids['mockId4']).to.deep.equal('mockId4_value'); - - config.setConfig({ - userSync: { - idPriority: { - mockId1: ['mockId1Module', 'mockId3Module'], - mockId4: ['mockId3Module', 'mockId4Module'] - }, - auctionDelay: 10, // with auctionDelay > 0, no auction is needed to complete init - userIds: [ - { name: 'mockId1Module' }, - { name: 'mockId2Module' }, - { name: 'mockId3Module' }, - { name: 'mockId4Module' } - ] - } - }); - - return getGlobal().getUserIdsAsync().then((uids) => { - expect(uids['mockId1']).to.deep.equal('mockId1_value'); - expect(uids['mockId2']).to.deep.equal('mockId2_value'); - expect(uids['mockId3']).to.deep.equal('mockId3_value_from_mockId2Module'); - expect(uids['mockId4']).to.deep.equal('mockId4_value_from_mockId3Module'); - }); - }); - }); - it('pbjs.refreshUserIds refreshes single', function() { coreStorage.setCookie('MOCKID', '', EXPIRED_COOKIE_DATE); - coreStorage.setCookie('refreshedid', '', EXPIRED_COOKIE_DATE); + coreStorage.setCookie('REFRESH', '', EXPIRED_COOKIE_DATE); let sandbox = sinon.createSandbox(); let mockIdCallback = sandbox.stub().returns({id: {'MOCKID': '1111'}}); @@ -3090,7 +2615,7 @@ describe('User ID', function () { expect(server.requests).to.be.empty; return endAuction(); }).then(() => { - expect(server.requests[0].url).to.match(/\/any\/unifiedid\/url/); + expect(server.requests[0].url).to.equal('/any/unifiedid/url'); }); }); @@ -3204,12 +2729,17 @@ describe('User ID', function () { } }; + consentData = { + gdprApplies: true, + consentString: 'mockString', + apiVersion: 1, + hasValidated: true // mock presence of GPDR enforcement module + } // clear cookies expStr = (new Date(Date.now() + 25000).toUTCString()); coreStorage.setCookie(mockIdCookieName, '', EXPIRED_COOKIE_DATE); coreStorage.setCookie(`${mockIdCookieName}_last`, '', EXPIRED_COOKIE_DATE); - coreStorage.setCookie(`${mockIdCookieName}_cst`, '', EXPIRED_COOKIE_DATE); - allConsent.reset(); + coreStorage.setCookie(CONSENT_LOCAL_STORAGE_NAME, '', EXPIRED_COOKIE_DATE); // init adUnits = [getAdUnitMock()]; @@ -3223,20 +2753,10 @@ describe('User ID', function () { config.resetConfig(); }); - function setStorage({ - val = JSON.stringify({id: '1234'}), - lastDelta = 60 * 1000, - cst = null - } = {}) { - coreStorage.setCookie(mockIdCookieName, val, expStr); - coreStorage.setCookie(`${mockIdCookieName}_last`, (new Date(Date.now() - lastDelta).toUTCString()), expStr); - if (cst != null) { - coreStorage.setCookie(`${mockIdCookieName}_cst`, cst, expStr); - } - } - it('calls getId if no stored consent data and refresh is not needed', function () { - setStorage({lastDelta: 1000}); + coreStorage.setCookie(mockIdCookieName, JSON.stringify({id: '1234'}), expStr); + coreStorage.setCookie(`${mockIdCookieName}_last`, (new Date(Date.now() - 1 * 1000).toUTCString()), expStr); + config.setConfig(userIdConfig); let innerAdUnits; @@ -3250,7 +2770,9 @@ describe('User ID', function () { }); it('calls getId if no stored consent data but refresh is needed', function () { - setStorage(); + coreStorage.setCookie(mockIdCookieName, JSON.stringify({id: '1234'}), expStr); + coreStorage.setCookie(`${mockIdCookieName}_last`, (new Date(Date.now() - 60 * 1000).toUTCString()), expStr); + config.setConfig(userIdConfig); let innerAdUnits; @@ -3264,7 +2786,11 @@ describe('User ID', function () { }); it('calls getId if empty stored consent and refresh not needed', function () { - setStorage({cst: ''}); + coreStorage.setCookie(mockIdCookieName, JSON.stringify({id: '1234'}), expStr); + coreStorage.setCookie(`${mockIdCookieName}_last`, (new Date(Date.now() - 1 * 1000).toUTCString()), expStr); + + setStoredConsentData(); + config.setConfig(userIdConfig); let innerAdUnits; @@ -3278,10 +2804,10 @@ describe('User ID', function () { }); it('calls getId if stored consent does not match current consent and refresh not needed', function () { - setStorage({cst: getConsentHash()}); - gdprDataHandler.setConsentData({ - consentString: 'different' - }); + coreStorage.setCookie(mockIdCookieName, JSON.stringify({id: '1234'}), expStr); + coreStorage.setCookie(`${mockIdCookieName}_last`, (new Date(Date.now() - 1 * 1000).toUTCString()), expStr); + + setStoredConsentData({...consentData, consentString: 'different'}); config.setConfig(userIdConfig); @@ -3296,7 +2822,10 @@ 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()}); + coreStorage.setCookie(mockIdCookieName, JSON.stringify({id: '1234'}), expStr); + coreStorage.setCookie(`${mockIdCookieName}_last`, (new Date(Date.now() - 1 * 1000).toUTCString()), expStr); + + setStoredConsentData({...consentData}); config.setConfig(userIdConfig); @@ -3421,73 +2950,6 @@ describe('User ID', function () { }).catch(done); }); - it('pbjs.getEncryptedEidsForSource should return prioritized id as non-encrypted string', (done) => { - init(config); - setSubmoduleRegistry([ - createMockIdSubmodule('mockId1Module', {id: {uid2: {id: 'uid2_value'}}}), - createMockIdSubmodule('mockId2Module', {id: {pubcid: 'pubcid_value', lipb: {lipbid: 'lipbid_value_from_mockId2Module'}}}), - 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, { - uid2: { - source: 'uidapi.com', - getValue(data) { - return data.id - } - }, - pubcid: { - source: 'pubcid.org', - }, - lipb: { - source: 'liveintent.com', - getValue(data) { - return data.lipbid - } - } - }), - createMockIdSubmodule('mockId4Module', {id: {merkleId: {id: 'merkleId_value'}}}, null, { - merkleId: { - source: 'merkleinc.com', - getValue(data) { - return data.id - } - } - }) - ]); - config.setConfig({ - userSync: { - idPriority: { - uid2: ['mockId3Module', 'mockId1Module'], - merkleId: ['mockId4Module', 'mockId3Module'] - }, - auctionDelay: 10, // with auctionDelay > 0, no auction is needed to complete init - userIds: [ - { name: 'mockId1Module' }, - { name: 'mockId2Module' }, - { name: 'mockId3Module' }, - { name: 'mockId4Module' } - ] - } - }); - - const expctedIds = [ - 'pubcid_value', - 'uid2_value_from_mockId3Module', - 'merkleId_value', - 'lipbid_value_from_mockId2Module' - ]; - - const encrypt = false; - - Promise.all([ - getGlobal().getEncryptedEidsForSource('pubcid.org', encrypt), - getGlobal().getEncryptedEidsForSource('uidapi.com', encrypt), - getGlobal().getEncryptedEidsForSource('merkleinc.com', encrypt), - getGlobal().getEncryptedEidsForSource('liveintent.com', encrypt) - ]).then((result) => { - expect(result).to.deep.equal(expctedIds); - done(); - }) - }); - describe('pbjs.getEncryptedEidsForSource', () => { beforeEach(() => { init(config); @@ -3551,45 +3013,6 @@ describe('User ID', function () { done(); }); }); - - it('pbjs.getUserIdsAsEidBySource with priority config available to core', () => { - init(config); - setSubmoduleRegistry([ - createMockIdSubmodule('mockId1Module', {id: {uid2: {id: 'uid2_value'}}}), - createMockIdSubmodule('mockId2Module', {id: {pubcid: 'pubcid_value', lipb: {lipbid: 'lipbid_value_from_mockId2Module'}}}), - createMockIdSubmodule('mockId3Module', {id: {uid2: {id: 'uid2_value_from_mockId3Module'}, pubcid: 'pubcid_value_from_mockId3Module', lipb: {lipbid: 'lipbid_value'}, merkleId: {id: 'merkleId_value_from_mockId3Module'}}}), - createMockIdSubmodule('mockId4Module', {id: {merkleId: {id: 'merkleId_value'}}}) - ]); - config.setConfig({ - userSync: { - idPriority: { - uid2: ['mockId3Module', 'mockId1Module'], - merkleId: ['mockId4Module', 'mockId3Module'] - }, - auctionDelay: 10, // with auctionDelay > 0, no auction is needed to complete init - userIds: [ - { name: 'mockId1Module' }, - { name: 'mockId2Module' }, - { name: 'mockId3Module' }, - { name: 'mockId4Module' } - ] - } - }); - - 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'}} - }; - - return getGlobal().getUserIdsAsync().then(() => { - expect(getGlobal().getUserIdsAsEidBySource('pubcid.org')).to.deep.equal(createEidsArray(ids['pubcid.org'])[0]); - expect(getGlobal().getUserIdsAsEidBySource('uidapi.com')).to.deep.equal(createEidsArray(ids['uidapi.com'])[0]); - expect(getGlobal().getUserIdsAsEidBySource('merkleinc.com')).to.deep.equal(createEidsArray(ids['merkleinc.com'])[0]); - expect(getGlobal().getUserIdsAsEidBySource('liveintent.com')).to.deep.equal(createEidsArray(ids['liveintent.com'])[0]); - }); - }); }) }); }); diff --git a/test/spec/modules/viantOrtbBidAdapter_spec.js b/test/spec/modules/viantOrtbBidAdapter_spec.js deleted file mode 100644 index 73fdb7f3dc8..00000000000 --- a/test/spec/modules/viantOrtbBidAdapter_spec.js +++ /dev/null @@ -1,475 +0,0 @@ -import { spec, converter } from 'modules/viantOrtbBidAdapter.js'; -import {assert, expect} from 'chai'; -import { deepClone } from '../../../src/utils'; -import {buildWindowTree} from '../../helpers/refererDetectionHelper'; -import {detectReferer} from '../../../src/refererDetection'; -describe('viantOrtbBidAdapter', function () { - function testBuildRequests(bidRequests, bidderRequestBase) { - let clonedBidderRequest = deepClone(bidderRequestBase); - clonedBidderRequest.bids = bidRequests; - let requests = spec.buildRequests(bidRequests, clonedBidderRequest); - return requests - } - describe('isBidRequestValid', function() { - function makeBid() { - return { - 'bidder': 'viant', - 'params': { - 'publisherId': '464', - 'placementId': 'some-PlacementId_1' - }, - 'mediaTypes': { - 'banner': { - 'sizes': [ - [728, 90] - ] - } - }, - 'adUnitCode': 'adunit-code', - 'bidId': '30b31c1838de1e', - 'bidderRequestId': '22edbae2733bf6', - 'auctionId': '1d1a030790a475', - }; - } - - describe('core', function () { - it('should return true when required params found', function () { - expect(spec.isBidRequestValid(makeBid())).to.equal(true); - }); - - it('should return false when publisherId not passed', function () { - let bid = makeBid(); - delete bid.params.publisherId; - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - - it('should return true if placementId is not passed ', function () { - let bid = makeBid(); - delete bid.params.placementId; - bid.ortb2Imp = { - - } - expect(spec.isBidRequestValid(bid)).to.equal(true); - }); - - it('should return false if mediaTypes.banner is Not passed', function () { - let bid = makeBid(); - delete bid.mediaTypes - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - }); - - describe('banner', function () { - it('should return true if banner.pos is passed correctly', function () { - let bid = makeBid(); - bid.mediaTypes.banner.pos = 1; - expect(spec.isBidRequestValid(bid)).to.equal(true); - }); - }); - - describe('video', function () { - describe('and request config uses mediaTypes', () => { - function makeBid() { - return { - 'bidder': 'viant', - 'params': { - 'unit': '12345678', - 'delDomain': 'test-del-domain', - 'publisherId': '464', - 'placementId': 'some-PlacementId_2' - }, - 'mediaTypes': { - 'video': { - 'context': 'instream', - 'playerSize': [[640, 480]], - 'mimes': ['video/mp4'], - 'protocols': [1, 2, 3, 4, 5, 6, 7, 8], - 'api': [1, 3], - 'skip': 1, - 'skipafter': 5, - 'minduration': 10, - 'maxduration': 30 - } - }, - 'adUnitCode': 'adunit-code', - 'bidId': '30b31c1838de1e', - 'bidderRequestId': '22edbae2733bf6', - 'auctionId': '1d1a030790a475', - 'transactionId': '4008d88a-8137-410b-aa35-fbfdabcb478e' - } - } - it('should return true when required params found', function () { - expect(spec.isBidRequestValid(makeBid())).to.equal(true); - }); - - it('should return false when required params are not passed', function () { - let videoBidWithMediaTypes = Object.assign({}, makeBid()); - videoBidWithMediaTypes.params = {}; - expect(spec.isBidRequestValid(videoBidWithMediaTypes)).to.equal(false); - }); - }); - }); - - describe('native', function () { - describe('and request config uses mediaTypes', () => { - function makeBid() { - return { - 'bidder': 'viant', - 'params': { - 'unit': '12345678', - 'delDomain': 'test-del-domain', - 'publisherId': '464', - 'placementId': 'some-PlacementId_2' - }, - 'mediaTypes': { - 'video': { - 'context': 'instream', - 'playerSize': [[640, 480]], - 'mimes': ['video/mp4'], - 'protocols': [1, 2, 3, 4, 5, 6, 7, 8], - 'api': [1, 3], - 'skip': 1, - 'skipafter': 5, - 'minduration': 10, - 'maxduration': 30 - } - }, - 'adUnitCode': 'adunit-code', - 'bidId': '30b31c1838de1e', - 'bidderRequestId': '22edbae2733bf6', - 'auctionId': '1d1a030790a475', - 'transactionId': '4008d88a-8137-410b-aa35-fbfdabcb478e' - } - } - it('should return true when required params found', function () { - expect(spec.isBidRequestValid(makeBid())).to.equal(true); - }); - - it('should return false when required params are not passed', function () { - let nativeBidWithMediaTypes = Object.assign({}, makeBid()); - nativeBidWithMediaTypes.params = {}; - expect(spec.isBidRequestValid(nativeBidWithMediaTypes)).to.equal(false); - }); - }); - }); - }); - - describe('buildRequests-banner', function () { - const baseBannerBidRequests = [{ - 'bidder': 'viant', - 'params': { - 'publisherId': '464', - 'placementId': '1' - }, - 'mediaTypes': { - 'banner': { - 'sizes': [[728, 90]] - } - }, - 'gdprConsent': { - 'consentString': 'consentString', - 'gdprApplies': true, - }, - 'uspConsent': '1YYY', - 'sizes': [[728, 90]], - 'transactionId': '1111474f-58b1-4368-b812-84f8c937a099', - 'adUnitCode': 'div-gpt-ad-1460505748561-0', - 'bidId': '243310435309b5', - 'bidderRequestId': '18084284054531', - 'auctionId': 'e7b34fa3-8654-424e-8c49-03e509e53d8c', - 'src': 'client', - 'bidRequestsCount': 1 - }]; - - const testWindow = buildWindowTree(['https://www.example.com/test', 'https://www.example.com/other/page', 'https://www.example.com/third/page'], 'https://othersite.com/', 'https://example.com/canonical/page'); - const baseBidderRequestReferer = detectReferer(testWindow)(); - const baseBidderRequest = { - 'bidderCode': 'viant', - 'auctionId': 'e7b34fa3-8654-424e-8c49-03e509e53d8c', - 'bidderRequestId': '18084284054531', - 'auctionStart': 1540945362095, - 'timeout': 3000, - 'refererInfo': baseBidderRequestReferer, - 'start': 1540945362099, - 'doneCbCallCount': 0 - }; - - it('test regs', function () { - const gdprBaseBidderRequest = Object.assign({}, baseBidderRequest, { - gdprConsent: { - consentString: 'consentString', - gdprApplies: true, - }, - uspConsent: '1YYN' - }); - const request = testBuildRequests(baseBannerBidRequests, gdprBaseBidderRequest)[0]; - expect(request.data.regs.ext).to.have.property('gdpr', 1); - expect(request.data.regs.ext).to.have.property('us_privacy', '1YYN'); - }); - - it('sends bid request to our endpoint that makes sense', function () { - const request = testBuildRequests(baseBannerBidRequests, baseBidderRequest)[0]; - expect(request.method).to.equal('POST'); - expect(request.url).to.be.not.empty; - expect(request.data).to.be.not.null; - }); - it('sends bid requests to the correct endpoint', function () { - const url = testBuildRequests(baseBannerBidRequests, baseBidderRequest)[0].url; - expect(url).to.equal('https://bidders-us-east-1.adelphic.net/d/rtb/v25/prebid/bidder'); - }); - - it('sends site', function () { - const requestBody = testBuildRequests(baseBannerBidRequests, baseBidderRequest)[0].data; - expect(requestBody.site).to.be.not.null; - }); - - it('includes the ad size in the bid request', function () { - const requestBody = testBuildRequests(baseBannerBidRequests, baseBidderRequest)[0].data; - expect(requestBody.imp[0].banner.format[0].w).to.equal(728); - expect(requestBody.imp[0].banner.format[0].h).to.equal(90); - }); - - it('sets the banner pos correctly if sent', function () { - let clonedBannerRequests = deepClone(baseBannerBidRequests); - clonedBannerRequests[0].mediaTypes.banner.pos = 1; - - const requestBody = testBuildRequests(clonedBannerRequests, baseBidderRequest)[0].data; - expect(requestBody.imp[0].banner.pos).to.equal(1); - }); - }); - - if (FEATURES.VIDEO) { - describe('buildRequests-video', function () { - function makeBid() { - return { - 'bidder': 'viant', - 'params': { - 'unit': '12345678', - 'delDomain': 'test-del-domain', - 'publisherId': '464', - 'placementId': 'some-PlacementId_2' - }, - 'mediaTypes': { - 'video': { - 'context': 'instream', - 'playerSize': [[640, 480]], - 'mimes': ['video/mp4'], - 'protocols': [1, 2, 3, 4, 5, 6, 7, 8], - 'api': [1, 3], - 'skip': 1, - 'skipafter': 5, - 'minduration': 10, - 'maxduration': 31 - } - }, - 'adUnitCode': 'adunit-code', - 'bidId': '30b31c1838de1e', - 'bidderRequestId': '22edbae2733bf6', - 'auctionId': '1d1a030790a475', - 'transactionId': '4008d88a-8137-410b-aa35-fbfdabcb478e' - } - } - - it('assert video and its fields is present in imp ', function () { - let requests = spec.buildRequests([makeBid()], {referrerInfo: {}}); - let 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) - assert.equal(clonedRequests[0].data.imp[0].video.placement, 1) - assert.equal(clonedRequests[0].method, 'POST') - }); - }); - } - - describe('interpretResponse', function () { - const baseBannerBidRequests = [{ - 'bidder': 'viant', - 'params': { - 'publisherId': '464', - 'placementId': '1' - }, - 'mediaTypes': { - 'banner': { - 'sizes': [[728, 90]] - } - }, - 'sizes': [[728, 90]], - 'transactionId': '1111474f-58b1-4368-b812-84f8c937a099', - 'adUnitCode': 'div-gpt-ad-1460505748561-0', - 'bidId': '243310435309b5', - 'bidderRequestId': '18084284054531', - 'auctionId': 'e7b34fa3-8654-424e-8c49-03e509e53d8c', - 'src': 'client', - 'bidRequestsCount': 1 - }]; - - const testWindow = buildWindowTree(['https://www.example.com/test', 'https://www.example.com/other/page', 'https://www.example.com/third/page'], 'https://othersite.com/', 'https://example.com/canonical/page'); - const baseBidderRequestReferer = detectReferer(testWindow)(); - const baseBidderRequest = { - 'bidderCode': 'viant', - 'auctionId': 'e7b34fa3-8654-424e-8c49-03e509e53d8c', - 'bidderRequestId': '18084284054531', - 'auctionStart': 1540945362095, - 'timeout': 3000, - 'refererInfo': baseBidderRequestReferer, - 'start': 1540945362099, - 'doneCbCallCount': 0 - }; - - it('empty bid response test', function () { - const request = testBuildRequests(baseBannerBidRequests, baseBidderRequest)[0]; - let bidResponse = {nbr: 0}; // Unknown error - let bids = spec.interpretResponse({body: bidResponse}, request); - expect(bids.length).to.equal(0); - }); - - it('bid response is a banner', function () { - const request = testBuildRequests(baseBannerBidRequests, baseBidderRequest)[0]; - let bidResponse = { - seatbid: [{ - bid: [{ - impid: '243310435309b5', - price: 2, - w: 728, - h: 90, - crid: 'test-creative-id', - dealid: 'test-deal-id', - adm: 'test-ad-markup', - }] - }], - cur: 'USD' - }; - let bids = spec.interpretResponse({body: bidResponse}, request); - expect(bids.length).to.equal(1); - let bid = bids[0]; - it('should return the proper mediaType', function () { - it('should return a creativeId', function () { - expect(bid.mediaType).to.equal('banner'); - }); - }); - it('should return a price', function () { - expect(bid.cpm).to.equal(bidResponse.seatbid[0].bid[0].price); - }); - - it('should return a request id', function () { - expect(bid.requestId).to.equal(bidResponse.seatbid[0].bid[0].impid); - }); - - it('should return width and height for the creative', function () { - expect(bid.width).to.equal(bidResponse.seatbid[0].bid[0].w); - expect(bid.height).to.equal(bidResponse.seatbid[0].bid[0].h); - }); - it('should return a creativeId', function () { - expect(bid.creativeId).to.equal(bidResponse.seatbid[0].bid[0].crid); - }); - it('should return an ad', function () { - expect(bid.ad).to.equal(bidResponse.seatbid[0].bid[0].adm); - }); - - it('should return a deal id if it exists', function () { - expect(bid.dealId).to.equal(bidResponse.seatbid[0].bid[0].dealid); - }); - - it('should have a time-to-live of 5 minutes', function () { - expect(bid.ttl).to.equal(300); - }); - - it('should always return net revenue', function () { - expect(bid.netRevenue).to.equal(true); - }); - it('should return a currency', function () { - expect(bid.currency).to.equal(bidResponse.cur); - }); - }); - }); - describe('interpretResponse-Video', function () { - const baseVideoBidRequests = [{ - 'bidder': 'viant', - 'params': { - 'publisherId': '464', - 'placementId': '1' - }, - 'mediaTypes': { - 'video': { - 'context': 'instream', - 'playerSize': [[640, 480]], - 'mimes': ['video/mp4'], - 'protocols': [1, 2, 3, 4, 5, 6, 7, 8], - 'api': [1, 3], - 'skip': 1, - 'skipafter': 5, - 'minduration': 10, - 'maxduration': 31 - } - }, - 'sizes': [[640, 480]], - 'transactionId': '1111474f-58b1-4368-b812-84f8c937a099', - 'adUnitCode': 'div-gpt-ad-1460505748561-0', - 'bidId': '243310435309b5', - 'bidderRequestId': '18084284054531', - 'auctionId': 'e7b34fa3-8654-424e-8c49-03e509e53d8c', - 'src': 'client', - 'bidRequestsCount': 1 - }]; - - const testWindow = buildWindowTree(['https://www.example.com/test', 'https://www.example.com/other/page', 'https://www.example.com/third/page'], 'https://othersite.com/', 'https://example.com/canonical/page'); - const baseBidderRequestReferer = detectReferer(testWindow)(); - const baseBidderRequest = { - 'bidderCode': 'viant', - 'auctionId': 'e7b34fa3-8654-424e-8c49-03e509e53d8c', - 'bidderRequestId': '18084284054531', - 'auctionStart': 1540945362095, - 'timeout': 3000, - 'refererInfo': baseBidderRequestReferer, - 'start': 1540945362099, - 'doneCbCallCount': 0 - }; - - it('bid response is a video', function () { - const request = testBuildRequests(baseVideoBidRequests, baseBidderRequest)[0]; - const VIDEO_BID_RESPONSE = { - 'id': 'bidderRequestId', - 'bidid': 'e7b34fa3-8654-424e-8c49-03e509e53d8c', - 'seatbid': [ - { - 'bid': [ - { - 'id': '1', - 'impid': '243310435309b5', - 'price': 1.09, - 'adid': '144762342', - 'nurl': 'http://0.0.0.0:8181/nurl', - 'adm': '', - 'adomain': [ - 'https://dummydomain.com' - ], - 'cid': 'cid', - 'crid': 'crid', - 'iurl': 'iurl', - 'cat': [], - 'h': 480, - 'w': 640 - } - ] - } - ], - 'cur': 'USD' - }; - let bids = spec.interpretResponse({body: VIDEO_BID_RESPONSE}, request); - expect(bids.length).to.equal(1); - let bid = bids[0]; - it('should return the proper mediaType', function () { - expect(bid.mediaType).to.equal('video'); - }); - it('should return correct Ad Markup', function () { - expect(bid.vastXml).to.equal(''); - }); - it('should return correct Notification', function () { - expect(bid.vastUrl).to.equal('http://0.0.0.0:8181/nurl'); - }); - it('should return correct Cpm', function () { - expect(bid.cpm).to.equal(1.09); - }); - }); - }); -}); diff --git a/test/spec/modules/vidazooBidAdapter_spec.js b/test/spec/modules/vidazooBidAdapter_spec.js index 864f2b8551c..6e92bae8d59 100644 --- a/test/spec/modules/vidazooBidAdapter_spec.js +++ b/test/spec/modules/vidazooBidAdapter_spec.js @@ -40,6 +40,7 @@ const BID = { 'placementCode': 'div-gpt-ad-1460505748561-0', 'sizes': [[300, 250], [300, 600]], 'bidderRequestId': '1fdb5ff1b6eaa7', + 'auctionId': 'auction_id', 'bidRequestsCount': 4, 'bidderRequestsCount': 3, 'bidderWinsCount': 1, @@ -63,6 +64,7 @@ const VIDEO_BID = { tid: '56e184c6-bde9-497b-b9b9-cf47a61381ee', } }, + 'auctionId': 'auction_id', 'bidRequestsCount': 4, 'bidderRequestsCount': 3, 'bidderWinsCount': 1, @@ -140,7 +142,6 @@ const SERVER_RESPONSE = { cid: 'testcid123', results: [{ 'ad': '', - 'bidId': '2d52001cabd527-response', 'price': 0.8, 'creativeId': '12610997325162499419', 'exp': 30, @@ -286,6 +287,7 @@ describe('VidazooBidAdapter', function () { usPrivacy: 'consent_string', gppString: 'gpp_string', gppSid: [7], + auctionId: 'auction_id', bidRequestsCount: 4, bidderRequestsCount: 3, bidderWinsCount: 1, @@ -359,6 +361,7 @@ describe('VidazooBidAdapter', function () { usPrivacy: 'consent_string', gppString: 'gpp_string', gppSid: [7], + auctionId: 'auction_id', bidRequestsCount: 4, bidderRequestsCount: 3, bidderWinsCount: 1, @@ -410,122 +413,8 @@ describe('VidazooBidAdapter', function () { }); }); - it('should build single banner request for multiple bids', function () { - config.setConfig({ - bidderTimeout: 3000, - vidazoo: { - singleRequest: true, - chunkSize: 2 - } - }); - - const hashUrl = hashCode(BIDDER_REQUEST.refererInfo.page); - - const BID2 = utils.deepClone(BID); - BID2.bidId = '2d52001cabd528'; - BID2.adUnitCode = 'div-gpt-ad-12345-1'; - BID2.sizes = [[300, 250]]; - - const REQUEST_DATA = { - gdprConsent: 'consent_string', - gdpr: 1, - usPrivacy: 'consent_string', - gppString: 'gpp_string', - gppSid: [7], - bidRequestsCount: 4, - bidderRequestsCount: 3, - bidderWinsCount: 1, - bidderTimeout: 3000, - transactionId: 'c881914b-a3b5-4ecf-ad9c-1c2f37c6aabf', - 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': '' - }, - 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', - dealId: 3, - sessionId: '', - uniqueDealId: `${hashUrl}_${Date.now().toString()}`, - bidderVersion: adapter.version, - prebidVersion: version, - schain: BID.schain, - ptrace: '1000', - res: `${window.top.screen.width}x${window.top.screen.height}`, - mediaTypes: [BANNER], - uqs: getTopWindowQueryParams(), - 'ext.param1': 'loremipsum', - 'ext.param2': 'dolorsitamet', - isStorageAllowed: true, - gpid: '1234567890', - cat: ['IAB2'], - pagecat: ['IAB2-2'], - webSessionId: webSessionId - }; - - const REQUEST_DATA2 = utils.deepClone(REQUEST_DATA); - REQUEST_DATA2.bidId = '2d52001cabd528'; - REQUEST_DATA2.adUnitCode = 'div-gpt-ad-12345-1'; - REQUEST_DATA2.sizes = ['300x250']; - REQUEST_DATA2.dealId = 4; - - const requests = adapter.buildRequests([BID, BID2], BIDDER_REQUEST); - expect(requests).to.have.length(1); - - expect(requests[0]).to.deep.equal({ - method: 'POST', - url: `${createDomain(SUB_DOMAIN)}/prebid/multi/59db6b3b4ffaa70004f45cdc`, - data: {bids: [REQUEST_DATA, REQUEST_DATA2]} - }); - }); - - it('should return seperated requests for video and banner if singleRequest is true', function () { - config.setConfig({ - bidderTimeout: 3000, - vidazoo: { - singleRequest: true, - chunkSize: 2 - } - }); - - const requests = adapter.buildRequests([BID, VIDEO_BID], BIDDER_REQUEST); - expect(requests).to.have.length(2); - }); - - it('should chunk requests if requests exceed chunkSize and singleRequest is true', function () { - config.setConfig({ - bidderTimeout: 3000, - vidazoo: { - singleRequest: true, - chunkSize: 2 - } - }); - - const requests = adapter.buildRequests([BID, BID, BID, BID], BIDDER_REQUEST); - expect(requests).to.have.length(2); - }); - after(function () { $$PREBID_GLOBAL$$.bidderSettings = {}; - config.resetConfig(); sandbox.restore(); }); }); @@ -626,21 +515,6 @@ describe('VidazooBidAdapter', function () { }); }); - it('should populate requestId from response in case of singleRequest true', function () { - config.setConfig({ - vidazoo: { - singleRequest: true, - chunkSize: 2 - } - }); - - const responses = adapter.interpretResponse(SERVER_RESPONSE, REQUEST); - expect(responses).to.have.length(1); - expect(responses[0].requestId).to.equal('2d52001cabd527-response'); - - config.resetConfig(); - }); - it('should take default TTL', function () { const serverResponse = utils.deepClone(SERVER_RESPONSE); delete serverResponse.body.results[0].exp; diff --git a/test/spec/modules/visxBidAdapter_spec.js b/test/spec/modules/visxBidAdapter_spec.js index 5528705efd7..9a486cd6c34 100755 --- a/test/spec/modules/visxBidAdapter_spec.js +++ b/test/spec/modules/visxBidAdapter_spec.js @@ -1,5 +1,5 @@ import { expect } from 'chai'; -import { spec, storage } from 'modules/visxBidAdapter.js'; +import { spec } from 'modules/visxBidAdapter.js'; import { config } from 'src/config.js'; import { newBidder } from 'src/adapters/bidderFactory.js'; import * as utils from 'src/utils.js'; @@ -82,9 +82,6 @@ describe('VisxAdapter', function () { }); return res; } - - let cookiesAreEnabledStub, localStorageIsEnabledStub; - const bidderRequest = { timeout: 3000, refererInfo: { @@ -183,24 +180,6 @@ describe('VisxAdapter', function () { 'ext': {'bidder': {'uid': 903537}} }]; - before(() => { - $$PREBID_GLOBAL$$.bidderSettings = { - visx: { - storageAllowed: false - } - }; - localStorageIsEnabledStub = sinon.stub(storage, 'localStorageIsEnabled'); - cookiesAreEnabledStub = sinon.stub(storage, 'cookiesAreEnabled'); - localStorageIsEnabledStub.returns(false); - cookiesAreEnabledStub.returns(false); - }); - - after(() => { - localStorageIsEnabledStub.restore(); - cookiesAreEnabledStub.restore(); - $$PREBID_GLOBAL$$.bidderSettings = {}; - }); - it('should attach valid params to the tag', function () { const firstBid = bidRequests[0]; const bids = [firstBid]; @@ -443,7 +422,6 @@ describe('VisxAdapter', function () { }); return res; } - let cookiesAreEnabledStub, localStorageIsEnabledStub; const bidderRequest = { timeout: 3000, refererInfo: { @@ -471,24 +449,6 @@ describe('VisxAdapter', function () { } ]; - before(() => { - $$PREBID_GLOBAL$$.bidderSettings = { - visx: { - storageAllowed: false - } - }; - localStorageIsEnabledStub = sinon.stub(storage, 'localStorageIsEnabled'); - cookiesAreEnabledStub = sinon.stub(storage, 'cookiesAreEnabled'); - localStorageIsEnabledStub.returns(false); - cookiesAreEnabledStub.returns(false); - }); - - after(() => { - localStorageIsEnabledStub.restore(); - cookiesAreEnabledStub.restore(); - $$PREBID_GLOBAL$$.bidderSettings = {}; - }); - it('should send requst for banner bid', function () { const request = spec.buildRequests([bidRequests[0]], bidderRequest); const payload = parseRequest(request.url); @@ -526,7 +486,6 @@ describe('VisxAdapter', function () { }); return res; } - let cookiesAreEnabledStub, localStorageIsEnabledStub; const bidderRequest = { timeout: 3000, refererInfo: { @@ -570,23 +529,10 @@ describe('VisxAdapter', function () { documentStub.withArgs('visx-adunit-element-2').returns({ id: 'visx-adunit-element-2' }); - - $$PREBID_GLOBAL$$.bidderSettings = { - visx: { - storageAllowed: false - } - }; - localStorageIsEnabledStub = sinon.stub(storage, 'localStorageIsEnabled'); - cookiesAreEnabledStub = sinon.stub(storage, 'cookiesAreEnabled'); - localStorageIsEnabledStub.returns(false); - cookiesAreEnabledStub.returns(false); }); after(function() { sandbox.restore(); - localStorageIsEnabledStub.restore(); - cookiesAreEnabledStub.restore(); - $$PREBID_GLOBAL$$.bidderSettings = {}; }); it('should find ad slot by ad unit code as element id', function () { @@ -1257,7 +1203,6 @@ describe('VisxAdapter', function () { const request = spec.buildRequests(bidRequests); const pendingUrl = 'https://t.visx.net/track/pending/123123123'; const winUrl = 'https://t.visx.net/track/win/53245341'; - const runtimeUrl = 'https://t.visx.net/track/status/12345678'; const expectedResponse = [ { 'requestId': '300bfeb0d71a5b', @@ -1282,8 +1227,7 @@ describe('VisxAdapter', function () { 'ext': { 'events': { 'pending': pendingUrl, - 'win': winUrl, - 'runtime': runtimeUrl + 'win': winUrl }, 'targeting': { 'hb_visx_product': 'understitial', @@ -1300,9 +1244,6 @@ describe('VisxAdapter', function () { pending: pendingUrl, win: winUrl, }); - utils.deepSetValue(serverResponse.bid[0], 'ext.visx.events', { - runtime: runtimeUrl - }); const result = spec.interpretResponse({'body': {'seatbid': [serverResponse]}}, request); expect(result).to.deep.equal(expectedResponse); }); @@ -1330,39 +1271,6 @@ describe('VisxAdapter', function () { expect(utils.triggerPixel.calledOnceWith(trackUrl)).to.equal(true); }); - it('onBidWon with runtime tracker (0 < timeToRespond <= 5000 )', function () { - const trackUrl = 'https://t.visx.net/track/win/123123123'; - const runtimeUrl = 'https://t.visx.net/track/status/12345678/{STATUS_CODE}'; - const bid = { auctionId: '1', ext: { events: { win: trackUrl, runtime: runtimeUrl } }, timeToRespond: 100 }; - spec.onBidWon(bid); - expect(utils.triggerPixel.calledTwice).to.equal(true); - expect(utils.triggerPixel.calledWith(trackUrl)).to.equal(true); - expect(utils.triggerPixel.calledWith(runtimeUrl.replace('{STATUS_CODE}', 999002))).to.equal(true); - }); - - it('onBidWon with runtime tracker (timeToRespond <= 0 )', function () { - const runtimeUrl = 'https://t.visx.net/track/status/12345678/{STATUS_CODE}'; - const bid = { auctionId: '2', ext: { events: { runtime: runtimeUrl } }, timeToRespond: 0 }; - spec.onBidWon(bid); - expect(utils.triggerPixel.calledOnceWith(runtimeUrl.replace('{STATUS_CODE}', 999000))).to.equal(true); - }); - - it('onBidWon with runtime tracker (timeToRespond > 5000 )', function () { - const runtimeUrl = 'https://t.visx.net/track/status/12345678/{STATUS_CODE}'; - const bid = { auctionId: '3', ext: { events: { runtime: runtimeUrl } }, timeToRespond: 5001 }; - spec.onBidWon(bid); - expect(utils.triggerPixel.calledOnceWith(runtimeUrl.replace('{STATUS_CODE}', 999100))).to.equal(true); - }); - - it('onBidWon runtime tracker should be called once per auction', function () { - const runtimeUrl = 'https://t.visx.net/track/status/12345678/{STATUS_CODE}'; - const bid1 = { auctionId: '4', ext: { events: { runtime: runtimeUrl } }, timeToRespond: 100 }; - spec.onBidWon(bid1); - const bid2 = { auctionId: '4', ext: { events: { runtime: runtimeUrl } }, timeToRespond: 200 }; - spec.onBidWon(bid2); - expect(utils.triggerPixel.calledOnceWith(runtimeUrl.replace('{STATUS_CODE}', 999002))).to.equal(true); - }); - it('onTimeout', function () { const data = [{ timeout: 3000, adUnitCode: 'adunit-code-1', auctionId: '1cbd2feafe5e8b', bidder: 'visx', bidId: '23423', params: [{ uid: '1' }] }]; const expectedData = [{ timeout: 3000, params: [{ uid: 1 }] }]; @@ -1415,100 +1323,4 @@ describe('VisxAdapter', function () { expect(query).to.deep.equal({}); }); }); - - describe('first party user id', function () { - const USER_ID_KEY = '__vads'; - const USER_ID_DUMMY_VALUE_COOKIE = 'dummy_id_cookie'; - const USER_ID_DUMMY_VALUE_LOCAL_STORAGE = 'dummy_id_local_storage'; - - let getDataFromLocalStorageStub, localStorageIsEnabledStub; - let getCookieStub, cookiesAreEnabledStub; - - const bidRequests = [ - { - 'bidder': 'visx', - 'params': { - 'uid': 903535 - }, - 'adUnitCode': 'adunit-code-1', - 'sizes': [[300, 250], [300, 600]], - 'bidId': '30b31c1838de1e', - 'bidderRequestId': '22edbae2733bf6', - 'auctionId': '1d1a030790a475', - } - ]; - const bidderRequest = { - timeout: 3000, - refererInfo: { - page: 'https://example.com' - } - }; - - beforeEach(() => { - $$PREBID_GLOBAL$$.bidderSettings = { - visx: { - storageAllowed: true - } - }; - cookiesAreEnabledStub = sinon.stub(storage, 'cookiesAreEnabled'); - localStorageIsEnabledStub = sinon.stub(storage, 'localStorageIsEnabled'); - }); - - afterEach(() => { - cookiesAreEnabledStub.restore(); - localStorageIsEnabledStub.restore(); - getCookieStub && getCookieStub.restore(); - getDataFromLocalStorageStub && getDataFromLocalStorageStub.restore(); - $$PREBID_GLOBAL$$.bidderSettings = {}; - }); - - it('should not pass user id if both cookies and local storage are not available', function () { - cookiesAreEnabledStub.returns(false); - localStorageIsEnabledStub.returns(false); - - const request = spec.buildRequests(bidRequests, bidderRequest); - - expect(request.data.user).to.be.undefined; - }); - - it('should get user id from cookie if available', function () { - cookiesAreEnabledStub.returns(true); - localStorageIsEnabledStub.returns(false); - getCookieStub = sinon.stub(storage, 'getCookie'); - getCookieStub.withArgs(USER_ID_KEY).returns(USER_ID_DUMMY_VALUE_COOKIE); - - const request = spec.buildRequests(bidRequests, bidderRequest); - - expect(request.data.user.ext.vads).to.equal(USER_ID_DUMMY_VALUE_COOKIE); - }); - - it('should get user id from local storage if available', function () { - cookiesAreEnabledStub.returns(false); - localStorageIsEnabledStub.returns(true); - getDataFromLocalStorageStub = sinon.stub(storage, 'getDataFromLocalStorage'); - getDataFromLocalStorageStub.withArgs(USER_ID_KEY).returns(USER_ID_DUMMY_VALUE_LOCAL_STORAGE); - - const request = spec.buildRequests(bidRequests, bidderRequest); - - expect(request.data.user.ext.vads).to.equal(USER_ID_DUMMY_VALUE_LOCAL_STORAGE); - }); - - it('should create user id and store it in cookies (if user id does not exist)', function () { - cookiesAreEnabledStub.returns(true); - localStorageIsEnabledStub.returns(false); - - const request = spec.buildRequests(bidRequests, bidderRequest); - expect(storage.getCookie(USER_ID_KEY)).to.be.a('string'); - expect(request.data.user.ext.vads).to.be.a('string'); - }); - - it('should create user id and store it in local storage (if user id does not exist)', function () { - cookiesAreEnabledStub.returns(false); - localStorageIsEnabledStub.returns(true); - - const request = spec.buildRequests(bidRequests, bidderRequest); - expect(storage.getDataFromLocalStorage(USER_ID_KEY)).to.be.a('string'); - expect(request.data.user.ext.vads).to.be.a('string'); - }); - }); }); diff --git a/test/spec/modules/voxBidAdapter_spec.js b/test/spec/modules/voxBidAdapter_spec.js index 5f4ada06c65..923b0465e6c 100644 --- a/test/spec/modules/voxBidAdapter_spec.js +++ b/test/spec/modules/voxBidAdapter_spec.js @@ -1,6 +1,5 @@ import { expect } from 'chai' import { spec } from 'modules/voxBidAdapter.js' -import {config} from 'src/config.js' function getSlotConfigs(mediaTypes, params) { return { @@ -176,98 +175,6 @@ describe('VOX Adapter', function() { expect(bid.transactionId).to.equal('31a58515-3634-4e90-9c96-f86196db1459') }) }) - it('should not set userid if not specified', function () { - const request = spec.buildRequests(validBidRequests, bidderRequest) - const data = JSON.parse(request.data) - data.bidRequests.forEach(bid => { - expect(bid.userId).to.be.undefined - }) - }) - - it('should set userid if specified', function () { - const requests = validBidRequests.map(bid => ({ - ...bid, - userId: { - tdid: 'TDID_USER_ID', - pubcid: 'PUBID_USER_ID' - } - })) - const request = spec.buildRequests(requests, bidderRequest) - const data = JSON.parse(request.data) - data.bidRequests.forEach(bid => { - expect(bid.userId.tdid).to.equal('TDID_USER_ID') - expect(bid.userId.pubcid).to.equal('PUBID_USER_ID') - }) - }) - - it('should not set schain if not specified', function () { - const request = spec.buildRequests(validBidRequests, bidderRequest) - const data = JSON.parse(request.data) - data.bidRequests.forEach(bid => { - expect(bid.schain).to.be.undefined - }) - }) - - it('should set schain if not specified', function () { - const requests = validBidRequests.map(bid => ({ - ...bid, - schain: { - validation: 'strict', - config: { - ver: '1.0' - } - } - })) - const request = spec.buildRequests(requests, bidderRequest) - const data = JSON.parse(request.data) - data.bidRequests.forEach(bid => { - expect(bid.schain.validation).to.equal('strict') - expect(bid.schain.config.ver).to.equal('1.0') - }) - }) - - describe('price floors', function () { - it('should be empty if floors module not configured', function () { - const request = spec.buildRequests(validBidRequests, bidderRequest) - const data = JSON.parse(request.data) - data.bidRequests.forEach(bid => { - expect(bid.floorInfo).to.be.empty - }) - }) - - it('should add correct floor values', function () { - const expectedFloors = [ 2, 2.7, 1.4 ] - const validBidRequests = expectedFloors.map(getBidWithFloor) - const request = spec.buildRequests(validBidRequests, bidderRequest) - const data = JSON.parse(request.data) - expectedFloors.forEach((floor, index) => { - expect(data.bidRequests[index].floorInfo.floor).to.equal(floor) - expect(data.bidRequests[index].floorInfo.currency).to.equal('USD') - }) - }) - - it('should request floor price in adserver currency', function () { - const configCurrency = 'DKK' - config.setConfig({ currency: { adServerCurrency: configCurrency } }) - const request = spec.buildRequests([ getBidWithFloor() ], bidderRequest) - const data = JSON.parse(request.data) - data.bidRequests.forEach(bid => { - expect(bid.floorInfo.currency).to.equal(configCurrency) - }) - }) - - function getBidWithFloor(floor) { - return { - ...validBidRequests[0], - getFloor: ({ currency }) => { - return { - currency: currency, - floor - } - } - } - } - }) describe('GDPR params', function() { describe('when there are not consent management platform', function() { diff --git a/test/spec/modules/vrtcalBidAdapter_spec.js b/test/spec/modules/vrtcalBidAdapter_spec.js index 938934170e9..764db3fa4f0 100644 --- a/test/spec/modules/vrtcalBidAdapter_spec.js +++ b/test/spec/modules/vrtcalBidAdapter_spec.js @@ -29,12 +29,7 @@ describe('vrtcalBidAdapter', function () { 'bidderRequestId': 'br0001', 'auctionId': 'auction0001', 'userIdAsEids': {}, - timeout: 435, - - refererInfo: { - page: 'page' - } - + timeout: 435 } ]; @@ -84,12 +79,9 @@ describe('vrtcalBidAdapter', function () { }); it('pass 3rd party IDs with the request when present', function () { - bidRequests[0].userIdAsEids = [ - { - source: 'adserver.org', - uids: [{id: 'TTD_ID_FROM_USER_ID_MODULE', atype: 1, ext: {rtiPartner: 'TDID'}}] - } - ]; + bidRequests[0].userIdAsEids = createEidsArray({ + tdid: 'TTD_ID_FROM_USER_ID_MODULE' + }); request = spec.buildRequests(bidRequests); expect(request[0].data).to.include(JSON.stringify({ext: {consent: 'gdpr-consent-string', eids: [{source: 'adserver.org', uids: [{id: 'TTD_ID_FROM_USER_ID_MODULE', atype: 1, ext: {rtiPartner: 'TDID'}}]}]}})); @@ -134,39 +126,4 @@ describe('vrtcalBidAdapter', function () { ).to.be.true }) }) - - describe('getUserSyncs', function() { - const syncurl_iframe = 'https://usync.vrtcal.com/i?ssp=1804&synctype=iframe'; - const syncurl_redirect = 'https://usync.vrtcal.com/i?ssp=1804&synctype=redirect'; - - it('base iframe sync pper config', function() { - expect(spec.getUserSyncs({ iframeEnabled: true }, {}, undefined, undefined)).to.deep.equal([{ - type: 'iframe', url: syncurl_iframe + '&us_privacy=&gdpr=0&gdpr_consent=&gpp=&gpp_sid=&surl=' - }]); - }); - - it('base redirect sync per config', function() { - expect(spec.getUserSyncs({ iframeEnabled: false }, {}, undefined, undefined)).to.deep.equal([{ - type: 'image', url: syncurl_redirect + '&us_privacy=&gdpr=0&gdpr_consent=&gpp=&gpp_sid=&surl=' - }]); - }); - - it('pass with ccpa data', function() { - expect(spec.getUserSyncs({ iframeEnabled: true }, {}, undefined, 'ccpa_consent_string', undefined)).to.deep.equal([{ - type: 'iframe', url: syncurl_iframe + '&us_privacy=ccpa_consent_string&gdpr=0&gdpr_consent=&gpp=&gpp_sid=&surl=' - }]); - }); - - it('pass with gdpr data', function() { - expect(spec.getUserSyncs({ iframeEnabled: true }, {}, {gdprApplies: 1, consentString: 'gdpr_consent_string'}, undefined, undefined)).to.deep.equal([{ - type: 'iframe', url: syncurl_iframe + '&us_privacy=&gdpr=1&gdpr_consent=gdpr_consent_string&gpp=&gpp_sid=&surl=' - }]); - }); - - it('pass with gpp data', function() { - expect(spec.getUserSyncs({ iframeEnabled: true }, {}, undefined, undefined, {gppString: 'gpp_consent_string', applicableSections: [1, 5]})).to.deep.equal([{ - type: 'iframe', url: syncurl_iframe + '&us_privacy=&gdpr=0&gdpr_consent=&gpp=gpp_consent_string&gpp_sid=1,5&surl=' - }]); - }); - }) }) diff --git a/test/spec/modules/xeBidAdapter_spec.js b/test/spec/modules/xeBidAdapter_spec.js index 914b0cacd71..38b334c32c5 100644 --- a/test/spec/modules/xeBidAdapter_spec.js +++ b/test/spec/modules/xeBidAdapter_spec.js @@ -41,412 +41,414 @@ defaultRequestVideo.mediaTypes = { skipppable: true } }; -describe('xeBidAdapter', () => { - 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 env param is missing', function () { - const invalidRequest = deepClone(defaultRequest); - delete invalidRequest.params.env; - expect(spec.isBidRequestValid(invalidRequest)).to.equal(false); - }); - it('should return false when required placement param is missing', function () { - const invalidRequest = deepClone(defaultRequest); - delete invalidRequest.params.placement; - expect(spec.isBidRequestValid(invalidRequest)).to.equal(false); - }); +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 video.playerSize is missing', function () { - const invalidRequest = deepClone(defaultRequestVideo); - delete invalidRequest.mediaTypes.video.playerSize; - expect(spec.isBidRequestValid(invalidRequest)).to.equal(false); - }); + it('should return false when required env param is missing', function () { + const invalidRequest = deepClone(defaultRequest); + delete invalidRequest.params.env; + expect(spec.isBidRequestValid(invalidRequest)).to.equal(false); + }); - it('should return true when required params found', function () { - expect(spec.isBidRequestValid(defaultRequest)).to.equal(true); - }); + it('should return false when required placement param is missing', function () { + const invalidRequest = deepClone(defaultRequest); + delete invalidRequest.params.placement; + expect(spec.isBidRequestValid(invalidRequest)).to.equal(false); }); - describe('buildRequests', function () { - beforeEach(function () { - config.resetConfig(); - }); + 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 send request with correct structure', function () { - const request = spec.buildRequests([defaultRequest], {}); - expect(request.method).to.equal('POST'); - expect(request.url).to.equal(ENDPOINT); - expect(request.options).to.have.property('contentType').and.to.equal('application/json'); - expect(request).to.have.property('data'); - }); + it('should return true when required params found', function () { + expect(spec.isBidRequestValid(defaultRequest)).to.equal(true); + }); +}); - it('should build basic request structure', function () { - const request = JSON.parse(spec.buildRequests([defaultRequest], {}).data)[0]; - expect(request).to.have.property('bidId').and.to.equal(defaultRequest.bidId); - expect(request).to.have.property('auctionId').and.to.equal(defaultRequest.auctionId); - 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('gdprApplies').and.to.equal(0); - expect(request).to.have.property('consentString').and.to.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('coppa').and.to.equal(0); - 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({ - env: 'xe', - placement: 'test-banner' - }); - expect(request).to.have.property('device').and.to.deep.equal({ - ua: navigator.userAgent, - lang: navigator.language - }); - }); +describe('buildRequests', function () { + beforeEach(function () { + config.resetConfig(); + }); - it('should build request with schain', function () { - const schainRequest = deepClone(defaultRequest); - schainRequest.schain = { - validation: 'strict', - config: { - ver: '1.0' - } - }; - const request = JSON.parse(spec.buildRequests([schainRequest], {}).data)[0]; - expect(request).to.have.property('schain').and.to.deep.equal({ - validation: 'strict', - config: { - ver: '1.0' - } - }); - }); + 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); + expect(request.options).to.have.property('contentType').and.to.equal('application/json'); + expect(request).to.have.property('data'); + }); - 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 basic request structure', function () { + const request = JSON.parse(spec.buildRequests([defaultRequest], {}).data)[0]; + expect(request).to.have.property('bidId').and.to.equal(defaultRequest.bidId); + expect(request).to.have.property('auctionId').and.to.equal(defaultRequest.auctionId); + 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('gdprApplies').and.to.equal(0); + expect(request).to.have.property('consentString').and.to.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('coppa').and.to.equal(0); + 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({ + env: 'xe', + placement: 'test-banner' + }); + expect(request).to.have.property('device').and.to.deep.equal({ + ua: navigator.userAgent, + lang: navigator.language }); + }); - 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 schain', function () { + const schainRequest = deepClone(defaultRequest); + schainRequest.schain = { + validation: 'strict', + config: { + ver: '1.0' + } + }; + const request = JSON.parse(spec.buildRequests([schainRequest], {}).data)[0]; + expect(request).to.have.property('schain').and.to.deep.equal({ + validation: 'strict', + config: { + ver: '1.0' + } }); + }); - 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 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 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 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 gdpr consent data if applies', function () { - const bidderRequest = { - gdprConsent: { - gdprApplies: true, - consentString: 'qwerty' + 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' } - }; - const request = JSON.parse(spec.buildRequests([defaultRequest], bidderRequest).data)[0]; - expect(request).to.have.property('gdprApplies').and.equals(1); - expect(request).to.have.property('consentString').and.equals('qwerty'); + } }); + }); - 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 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 coppa 1', function () { - config.setConfig({ - coppa: true - }); - const request = JSON.parse(spec.buildRequests([defaultRequest], {}).data)[0]; - expect(request).to.have.property('coppa').and.equals(1); - }); + it('should build request with gdpr consent data if applies', function () { + const bidderRequest = { + gdprConsent: { + gdprApplies: true, + consentString: 'qwerty' + } + }; + const request = JSON.parse(spec.buildRequests([defaultRequest], bidderRequest).data)[0]; + expect(request).to.have.property('gdprApplies').and.equals(1); + expect(request).to.have.property('consentString').and.equals('qwerty'); + }); - 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 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 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', - skipppable: true - }); - expect(request).to.have.property('sizes').and.to.deep.equal(['640x480']); + it('should build request with coppa 1', function () { + config.setConfig({ + coppa: true }); + const request = JSON.parse(spec.buildRequests([defaultRequest], {}).data)[0]; + expect(request).to.have.property('coppa').and.equals(1); }); - describe('interpretResponse', function () { - it('should return empty bids', function () { - const serverResponse = { - body: { - data: null - } - }; + it('should build request with extended ids', function () { + const idRequest = deepClone(defaultRequest); + idRequest.userIdAsEids = createEidsArray({ + tdid: 'TTD_ID_FROM_USER_ID_MODULE', + pubcid: 'pubCommonId_FROM_USER_ID_MODULE' + }); + const request = JSON.parse(spec.buildRequests([idRequest], {}).data)[0]; + expect(request).to.have.property('userEids').and.deep.equal([ + { 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 invalidResponse = spec.interpretResponse(serverResponse, {}); - expect(invalidResponse).to.be.an('array').that.is.empty; + 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', + skipppable: 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: ['xe.works'] - }, - ext: { - pixels: [ - [ 'iframe', 'surl1' ], - [ 'image', 'surl2' ], - ] - } - }] - } - }; - - 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'); - 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: ['xe.works'] }); - }); + it('should interpret valid response', function () { + const serverResponse = { + body: { + data: [{ + requestId: 'qwerty', + cpm: 1, + currency: 'USD', + width: 300, + height: 250, + ttl: 600, + meta: { + advertiserDomains: ['xe.works'] + }, + ext: { + pixels: [ + [ 'iframe', 'surl1' ], + [ 'image', 'surl2' ], + ] + } + }] + } + }; + + 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'); + 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: ['xe.works'] }); + }); - 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: 'xe-demo-banner', - ad: 'ad', - meta: {} - }] - } - }; - - 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'); - expect(bid.creativeId).to.equal('xe-demo-banner'); - expect(bid.ad).to.equal('ad'); - }); + 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: 'xe-demo-banner', + ad: 'ad', + meta: {} + }] + } + }; + + 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'); + expect(bid.creativeId).to.equal('xe-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: 'xe-demo-video', - ad: 'vast-xml', - meta: {} - }] - } - }; - - 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'); - expect(bid.creativeId).to.equal('xe-demo-video'); - expect(bid.ad).to.equal('vast-xml'); - }); + 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: 'xe-demo-video', + ad: 'vast-xml', + meta: {} + }] + } + }; + + 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'); + expect(bid.creativeId).to.equal('xe-demo-video'); + expect(bid.ad).to.equal('vast-xml'); }); +}); - describe('getUserSyncs', function () { - it('shoukd handle no params', function () { - const opts = spec.getUserSyncs({}, []); - expect(opts).to.be.an('array').that.is.empty; - }); +describe('getUserSyncs', function () { + it('shoukd 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 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 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', 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-'); - }); + 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; - }); +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 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 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 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); - }); + 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/yahoosspBidAdapter_spec.js b/test/spec/modules/yahoosspBidAdapter_spec.js index 40dc2b3c63b..b0030c89371 100644 --- a/test/spec/modules/yahoosspBidAdapter_spec.js +++ b/test/spec/modules/yahoosspBidAdapter_spec.js @@ -3,6 +3,7 @@ import { config } from 'src/config.js'; import { BANNER, VIDEO } from 'src/mediaTypes.js'; import { spec } from 'modules/yahoosspBidAdapter.js'; import {createEidsArray} from '../../../modules/userId/eids'; +import {deepClone} from '../../../src/utils'; const DEFAULT_BID_ID = '84ab500420319d'; const DEFAULT_BID_DCN = '2093845709823475'; @@ -12,20 +13,18 @@ const DEFAULT_AD_UNIT_CODE = '/19968336/header-bid-tag-1'; const DEFAULT_AD_UNIT_TYPE = 'banner'; const DEFAULT_PARAMS_BID_OVERRIDE = {}; const DEFAULT_VIDEO_CONTEXT = 'instream'; -const ADAPTER_VERSION = '1.1.0'; -const DEFAULT_BIDDER_CODE = 'yahooAds'; -const VALID_BIDDER_CODES = [DEFAULT_BIDDER_CODE, 'yahoossp', 'yahooAdvertising']; +const ADAPTER_VERSION = '1.0.2'; const PREBID_VERSION = '$prebid.version$'; const INTEGRATION_METHOD = 'prebid.js'; // Utility functions -const generateBidRequest = ({bidderCode, bidId, pos, adUnitCode, adUnitType, bidOverrideObject, videoContext, pubIdMode, ortb2}) => { +const generateBidRequest = ({bidId, pos, adUnitCode, adUnitType, bidOverrideObject, videoContext, pubIdMode, ortb2}) => { const bidRequest = { adUnitCode, auctionId: 'b06c5141-fe8f-4cdf-9d7d-54415490a917', bidId, bidderRequestsCount: 1, - bidder: bidderCode, + bidder: 'yahoossp', bidderRequestId: '7101db09af0db2', bidderWinsCount: 0, mediaTypes: {}, @@ -80,7 +79,7 @@ let generateBidderRequest = (bidRequestArray, adUnitCode, ortb2 = {}) => { adUnitCode: adUnitCode || 'default-adUnitCode', auctionId: 'd4c83a3b-18e4-4208-b98b-63848449c7aa', auctionStart: new Date().getTime(), - bidderCode: bidRequestArray[0].bidder, + bidderCode: 'yahoossp', bidderRequestId: '112f1c7c5d399a', bids: bidRequestArray, refererInfo: { @@ -108,9 +107,8 @@ let generateBidderRequest = (bidRequestArray, adUnitCode, ortb2 = {}) => { return bidderRequest; }; -const generateBuildRequestMock = ({bidderCode, bidId, pos, adUnitCode, adUnitType, bidOverrideObject, videoContext, pubIdMode, ortb2}) => { +const generateBuildRequestMock = ({bidId, pos, adUnitCode, adUnitType, bidOverrideObject, videoContext, pubIdMode, ortb2}) => { const bidRequestConfig = { - bidderCode: bidderCode || DEFAULT_BIDDER_CODE, bidId: bidId || DEFAULT_BID_ID, pos: pos || DEFAULT_BID_POS, adUnitCode: adUnitCode || DEFAULT_AD_UNIT_CODE, @@ -176,28 +174,21 @@ const generateResponseMock = (admPayloadType, vastVersion, videoContext) => { seatbid: [{ bid: [ bidResponse ], seat: 13107 }] } }; - const { validBidRequests, bidderRequest } = generateBuildRequestMock({adUnitType: admPayloadType, videoContext}); + const { validBidRequests, bidderRequest } = generateBuildRequestMock({adUnitType: admPayloadType, videoContext: videoContext}); const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + return {serverResponse, data, bidderRequest}; } // Unit tests -describe('Yahoo Advertising Bid Adapter:', () => { - beforeEach(() => { - config.resetConfig(); - }); - +describe('YahooSSP Bid Adapter:', () => { describe('Validate basic properties', () => { it('should define the correct bidder code', () => { - expect(spec.code).to.equal('yahooAds'); - }); - - it('should define the correct bidder aliases', () => { - expect(spec.aliases).to.deep.equal(['yahoossp', 'yahooAdvertising']); + expect(spec.code).to.equal('yahoossp') }); it('should define the correct vendor ID', () => { - expect(spec.gvlid).to.equal(25); + expect(spec.gvlid).to.equal(25) }); }); @@ -280,62 +271,33 @@ describe('Yahoo Advertising Bid Adapter:', () => { }); describe('user consent parameters are updated', () => { - const syncOptions = { + let syncOptions = { iframeEnabled: true, pixelEnabled: true }; - describe('when all consent data is set', () => { - const pixelObjects = spec.getUserSyncs( - syncOptions, - SERVER_RESPONSES, - bidderRequest.gdprConsent, - bidderRequest.uspConsent, - bidderRequest.gppConsent - ); - pixelObjects.forEach(pixelObject => { - let url = pixelObject.url; - let urlParams = new URL(url).searchParams; - const expectedParams = { - 'baz': 'true', - 'gdpr_consent': bidderRequest.gdprConsent.consentString, - 'gdpr': bidderRequest.gdprConsent.gdprApplies ? '1' : '0', - 'us_privacy': bidderRequest.uspConsent, - 'gpp': bidderRequest.gppConsent.gppString, - 'gpp_sid': Array.isArray(bidderRequest.gppConsent.applicableSections) ? bidderRequest.gppConsent.applicableSections.join(',') : '' - } - for (const [key, value] of Object.entries(expectedParams)) { - it(`Updates the ${key} consent param in user sync URL ${url}`, () => { - expect(urlParams.get(key)).to.equal(value); - }); - }; - }); - }); - - describe('when no consent data is set', () => { - const pixelObjects = spec.getUserSyncs( - syncOptions, - SERVER_RESPONSES, - undefined, - undefined, - undefined - ); - pixelObjects.forEach(pixelObject => { - let url = pixelObject.url; - let urlParams = new URL(url).searchParams; - const expectedParams = { - 'baz': 'true', - 'gdpr_consent': '', - 'gdpr': '0', - 'us_privacy': '', - 'gpp': '', - 'gpp_sid': '' - } - for (const [key, value] of Object.entries(expectedParams)) { - it(`Updates the ${key} consent param in user sync URL ${url}`, () => { - expect(urlParams.get(key)).to.equal(value); - }); - }; - }); + let pixelObjects = spec.getUserSyncs( + syncOptions, + SERVER_RESPONSES, + bidderRequest.gdprConsent, + bidderRequest.uspConsent, + bidderRequest.gppConsent + ); + pixelObjects.forEach(pixelObject => { + let url = pixelObject.url; + let urlParams = new URL(url).searchParams; + const expectedParams = { + 'baz': 'true', + 'gdpr_consent': bidderRequest.gdprConsent.consentString, + 'gdpr': bidderRequest.gdprConsent.gdprApplies ? '1' : '0', + 'us_privacy': bidderRequest.uspConsent, + 'gpp': bidderRequest.gppConsent.gppString, + 'gpp_sid': Array.isArray(bidderRequest.gppConsent.applicableSections) ? bidderRequest.gppConsent.applicableSections.join(',') : '' + } + for (const [key, value] of Object.entries(expectedParams)) { + it(`Updates the ${key} consent param in user sync URL ${url}`, () => { + expect(urlParams.get(key)).to.equal(value); + }); + }; }); }); }); @@ -685,9 +647,9 @@ describe('Yahoo Advertising Bid Adapter:', () => { }; const { validBidRequests, bidderRequest } = generateBuildRequestMock({ortb2}); const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; - const user = data.user; - expect(user[param]).to.be.a('object'); - expect(user[param]).to.be.deep.include({[param]: {a: '123', b: '456'}}); + expect(data.user[param]).to.be.a('object'); + expect(data.user[param]).to.be.deep.include({[param]: {a: '123', b: '456'}}); + config.setConfig({ortb2: {}}); }); }); @@ -707,14 +669,12 @@ describe('Yahoo Advertising Bid Adapter:', () => { }; const { validBidRequests, bidderRequest } = generateBuildRequestMock({ortb2}); const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; - const user = data.user; - const site = data.site; - expect(user.data[0][param]).to.exist; - expect(user.data[0][param]).to.be.a('string'); - expect(user.data[0][param]).to.be.equal(ortb2.user.data[0][param]); - expect(site.content.data[0][param]).to.exist; - expect(site.content.data[0][param]).to.be.a('string'); - expect(site.content.data[0][param]).to.be.equal(ortb2.site.content.data[0][param]); + expect(data.user.data[0][param]).to.exist; + expect(data.user.data[0][param]).to.be.a('string'); + expect(data.user.data[0][param]).to.be.equal(ortb2.user.data[0][param]); + expect(data.site.content.data[0][param]).to.exist; + expect(data.site.content.data[0][param]).to.be.a('string'); + expect(data.site.content.data[0][param]).to.be.equal(ortb2.site.content.data[0][param]); }); }); @@ -728,10 +688,9 @@ describe('Yahoo Advertising Bid Adapter:', () => { }; const { validBidRequests, bidderRequest } = generateBuildRequestMock({ortb2}); const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; - const user = data.user; - expect(user.data[0][param]).to.exist; - expect(user.data[0][param]).to.be.a('array'); - expect(user.data[0][param]).to.be.equal(ortb2.user.data[0][param]); + expect(data.user.data[0][param]).to.exist; + expect(data.user.data[0][param]).to.be.a('array'); + expect(data.user.data[0][param]).to.be.equal(ortb2.user.data[0][param]); }); }); @@ -745,10 +704,10 @@ describe('Yahoo Advertising Bid Adapter:', () => { }; const { validBidRequests, bidderRequest } = generateBuildRequestMock({ortb2}); const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; - const user = data.user; - expect(user.data[0][param]).to.exist; - expect(user.data[0][param]).to.be.a('object'); - expect(user.data[0][param]).to.be.equal(ortb2.user.data[0][param]); + expect(data.user.data[0][param]).to.exist; + expect(data.user.data[0][param]).to.be.a('object'); + expect(data.user.data[0][param]).to.be.equal(ortb2.user.data[0][param]); + config.setConfig({ortb2: {}}); }); }); @@ -856,33 +815,26 @@ describe('Yahoo Advertising Bid Adapter:', () => { }); describe('Endpoint & Impression Request Mode:', () => { - afterEach(() => { + it('should route request to config override endpoint', () => { + const { validBidRequests, bidderRequest } = generateBuildRequestMock({}); + const testOverrideEndpoint = 'http://foo.bar.baz.com/bidderRequest'; config.setConfig({ - yahooAds: { - singleRequestMode: undefined - } - }); - }); - - VALID_BIDDER_CODES.forEach(bidderCode => { - it(`should route request to config override endpoint for ${bidderCode} override config`, () => { - const { validBidRequests, bidderRequest } = generateBuildRequestMock({bidderCode}); - const testOverrideEndpoint = 'http://foo.bar.baz.com/bidderRequest'; - const cfg = {}; - cfg[bidderCode] = { + yahoossp: { endpoint: testOverrideEndpoint - }; - config.setConfig(cfg); - const response = spec.buildRequests(validBidRequests, bidderRequest)[0]; - expect(response).to.deep.include( - { - method: 'POST', - url: testOverrideEndpoint - }); + } }); + const response = spec.buildRequests(validBidRequests, bidderRequest)[0]; + expect(response).to.deep.include( + { + method: 'POST', + url: testOverrideEndpoint + }); }); it('should route request to /bidRequest endpoint when dcn & pos present', () => { + config.setConfig({ + yahoossp: {} + }); const { validBidRequests, bidderRequest } = generateBuildRequestMock({}); const response = spec.buildRequests(validBidRequests, bidderRequest); expect(response[0]).to.deep.include({ @@ -910,15 +862,15 @@ describe('Yahoo Advertising Bid Adapter:', () => { bidderRequest.bids = validBidRequests; config.setConfig({ - yahooAds: { + yahoossp: { singleRequestMode: true } }); - const responsePayload = spec.buildRequests(validBidRequests, bidderRequest)[0].data; - expect(responsePayload.imp).to.be.an('array').with.lengthOf(2); + const data = spec.buildRequests(validBidRequests, bidderRequest).data; + expect(data.imp).to.be.an('array').with.lengthOf(2); - expect(responsePayload.imp[0]).to.deep.include({ + expect(data.imp[0]).to.deep.include({ id: DEFAULT_BID_ID, ext: { pos: DEFAULT_BID_POS, @@ -926,7 +878,7 @@ describe('Yahoo Advertising Bid Adapter:', () => { } }); - expect(responsePayload.imp[1]).to.deep.include({ + expect(data.imp[1]).to.deep.include({ id: BID_ID_2, ext: { pos: BID_POS_2, @@ -944,9 +896,8 @@ describe('Yahoo Advertising Bid Adapter:', () => { it('buildRequests(): should return an array with the correct amount of request objects', () => { const { validBidRequests, bidderRequest } = generateBuildRequestMock({}); - const reqs = spec.buildRequests(validBidRequests, bidderRequest); - expect(reqs).to.be.an('array').to.have.lengthOf(1); - expect(reqs[0]).to.be.an('object').that.has.keys('method', 'url', 'data', 'options', 'bidderRequest'); + const response = spec.buildRequests(validBidRequests, bidderRequest).bidderRequest; + expect(response.bids).to.be.an('array').to.have.lengthOf(1); }); }); @@ -954,7 +905,7 @@ describe('Yahoo Advertising Bid Adapter:', () => { it('should return request objects with the relevant custom headers and content type declaration', () => { const { validBidRequests, bidderRequest } = generateBuildRequestMock({}); bidderRequest.gdprConsent.gdprApplies = false; - const options = spec.buildRequests(validBidRequests, bidderRequest)[0].options; + const options = spec.buildRequests(validBidRequests, bidderRequest).options; expect(options).to.deep.equal( { contentType: 'application/json', @@ -969,8 +920,22 @@ describe('Yahoo Advertising Bid Adapter:', () => { describe('User data', () => { it('should set the allowed sources user eids', () => { const { validBidRequests, bidderRequest } = generateBuildRequestMock({}); - validBidRequests[0].userIdAsEids = [ - {source: 'yahoo.com', uids: [{id: 'connectId_FROM_USER_ID_MODULE', atype: 3}]}, + validBidRequests[0].userIdAsEids = createEidsArray({ + admixerId: 'admixerId_FROM_USER_ID_MODULE', + adtelligentId: 'adtelligentId_FROM_USER_ID_MODULE', + amxId: 'amxId_FROM_USER_ID_MODULE', + britepoolid: 'britepoolid_FROM_USER_ID_MODULE', + deepintentId: 'deepintentId_FROM_USER_ID_MODULE', + publinkId: 'publinkId_FROM_USER_ID_MODULE', + intentIqId: 'intentIqId_FROM_USER_ID_MODULE', + idl_env: 'idl_env_FROM_USER_ID_MODULE', + imuid: 'imuid_FROM_USER_ID_MODULE', + criteoId: 'criteoId_FROM_USER_ID_MODULE', + fabrickId: 'fabrickId_FROM_USER_ID_MODULE', + }); + const data = spec.buildRequests(validBidRequests, bidderRequest).data; + + expect(data.user.ext.eids).to.deep.equal([ {source: 'admixer.net', uids: [{id: 'admixerId_FROM_USER_ID_MODULE', atype: 3}]}, {source: 'adtelligent.com', uids: [{id: 'adtelligentId_FROM_USER_ID_MODULE', atype: 3}]}, {source: 'amxdt.net', uids: [{id: 'amxId_FROM_USER_ID_MODULE', atype: 1}]}, @@ -982,10 +947,7 @@ describe('Yahoo Advertising Bid Adapter:', () => { {source: 'intimatemerger.com', uids: [{id: 'imuid_FROM_USER_ID_MODULE', atype: 1}]}, {source: 'criteo.com', uids: [{id: 'criteoId_FROM_USER_ID_MODULE', atype: 1}]}, {source: 'neustar.biz', uids: [{id: 'fabrickId_FROM_USER_ID_MODULE', atype: 1}]} - ]; - const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; - - expect(data.user.ext.eids).to.deep.equal(validBidRequests[0].userIdAsEids); + ]); }); it('should not set not allowed user eids sources', () => { @@ -993,7 +955,7 @@ describe('Yahoo Advertising Bid Adapter:', () => { validBidRequests[0].userIdAsEids = createEidsArray({ justId: 'justId_FROM_USER_ID_MODULE' }); - const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + const data = spec.buildRequests(validBidRequests, bidderRequest).data; expect(data.user.ext.eids).to.deep.equal([]); }); @@ -1002,7 +964,7 @@ describe('Yahoo Advertising Bid Adapter:', () => { describe('Request Payload oRTB bid validation:', () => { it('should generate a valid openRTB bid-request object in the data field', () => { const { validBidRequests, bidderRequest } = generateBuildRequestMock({}); - const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + const data = spec.buildRequests(validBidRequests, bidderRequest).data; expect(data.site).to.deep.equal({ id: bidderRequest.bids[0].params.dcn, page: bidderRequest.refererInfo.page @@ -1051,7 +1013,7 @@ describe('Yahoo Advertising Bid Adapter:', () => { it('should generate a valid openRTB imp.ext object in the bid-request', () => { const { validBidRequests, bidderRequest } = generateBuildRequestMock({}); const bid = validBidRequests[0]; - const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + const data = spec.buildRequests(validBidRequests, bidderRequest).data; expect(data.imp[0].ext).to.deep.equal({ pos: bid.params.pos, dfp_ad_unit_code: DEFAULT_AD_UNIT_CODE @@ -1061,7 +1023,7 @@ describe('Yahoo Advertising Bid Adapter:', () => { it('should use siteId value as site.id in the outbound bid-request when using "pubId" integration mode', () => { let { validBidRequests, bidderRequest } = generateBuildRequestMock({pubIdMode: true}); validBidRequests[0].params.siteId = '1234567'; - const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + const data = spec.buildRequests(validBidRequests, bidderRequest).data; expect(data.site.id).to.equal('1234567'); }); @@ -1077,7 +1039,7 @@ describe('Yahoo Advertising Bid Adapter:', () => { } } let { validBidRequests, bidderRequest } = generateBuildRequestMock({ortb2}); - const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + const data = spec.buildRequests(validBidRequests, bidderRequest).data; expect(data.site.publisher).to.deep.equal({ ext: { publisherblob: 'pblob', @@ -1098,7 +1060,7 @@ describe('Yahoo Advertising Bid Adapter:', () => { } } let { validBidRequests, bidderRequest } = generateBuildRequestMock({pubIdMode: true, ortb2}); - const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + const data = spec.buildRequests(validBidRequests, bidderRequest).data; expect(data.site.publisher).to.deep.equal({ id: DEFAULT_PUBID, ext: { @@ -1111,13 +1073,17 @@ describe('Yahoo Advertising Bid Adapter:', () => { it('should use placementId value as imp.tagid in the outbound bid-request when using "pubId" integration mode', () => { let { validBidRequests, bidderRequest } = generateBuildRequestMock({pubIdMode: true}); validBidRequests[0].params.placementId = 'header-300x250'; - const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + const data = spec.buildRequests(validBidRequests, bidderRequest).data; expect(data.imp[0].tagid).to.deep.equal('header-300x250'); }); }); describe('Request Payload oRTB bid.imp validation:', () => { - it('should generate a valid "Banner" imp object when mode config override is undefined', () => { + // Validate Banner imp imp when yahoossp.mode=undefined + it('should generate a valid "Banner" imp object', () => { + config.setConfig({ + yahoossp: {} + }); const { validBidRequests, bidderRequest } = generateBuildRequestMock({}); const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; expect(data.imp[0].video).to.not.exist; @@ -1127,82 +1093,74 @@ describe('Yahoo Advertising Bid Adapter:', () => { }); }); - // Validate Banner imp when config value for mode="banner" - VALID_BIDDER_CODES.forEach(bidderCode => { - it(`should generate a valid "Banner" imp object for ${bidderCode} config override`, () => { - const cfg = {}; - cfg[bidderCode] = { - mode: BANNER - }; - config.setConfig(cfg); - const { validBidRequests, bidderRequest } = generateBuildRequestMock({bidderCode}); - const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; - expect(data.imp[0].video).to.not.exist; - expect(data.imp[0].banner).to.deep.equal({ - mimes: ['text/html', 'text/javascript', 'application/javascript', 'image/jpg'], - format: [{w: 300, h: 250}, {w: 300, h: 600}] - }); + // Validate Banner imp when yahoossp.mode="banner" + it('should generate a valid "Banner" imp object', () => { + config.setConfig({ + yahoossp: { mode: 'banner' } }); + const { validBidRequests, bidderRequest } = generateBuildRequestMock({}); + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + expect(data.imp[0].video).to.not.exist; + expect(data.imp[0].banner).to.deep.equal({ + mimes: ['text/html', 'text/javascript', 'application/javascript', 'image/jpg'], + format: [{w: 300, h: 250}, {w: 300, h: 600}] + }); + }); - // Validate Video imp - it(`should generate a valid "Video" only imp object for ${bidderCode} config override`, () => { - const cfg = {}; - cfg[bidderCode] = { - mode: VIDEO - }; - config.setConfig(cfg); - const { validBidRequests, bidderRequest } = generateBuildRequestMock({bidderCode, adUnitType: 'video'}); - const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; - expect(data.imp[0].banner).to.not.exist; - expect(data.imp[0].video).to.deep.equal({ - mimes: ['video/mp4', 'application/javascript'], - w: 300, - h: 250, - api: [2], - protocols: [2, 5], - startdelay: 0, - linearity: 1, - maxbitrate: undefined, - maxduration: undefined, - minduration: undefined, - delivery: undefined, - pos: undefined, - playbackmethod: undefined, - rewarded: undefined, - placement: undefined - }); + // Validate Video imp + it('should generate a valid "Video" only imp object', () => { + config.setConfig({ + yahoossp: { mode: 'video' } }); + const { validBidRequests, bidderRequest } = generateBuildRequestMock({adUnitType: 'video'}); + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + expect(data.imp[0].banner).to.not.exist; + expect(data.imp[0].video).to.deep.equal({ + mimes: ['video/mp4', 'application/javascript'], + w: 300, + h: 250, + api: [2], + protocols: [2, 5], + startdelay: 0, + linearity: 1, + maxbitrate: undefined, + maxduration: undefined, + minduration: undefined, + delivery: undefined, + pos: undefined, + playbackmethod: undefined, + rewarded: undefined, + placement: undefined + }); + }); - // Validate multi-format Video+banner imp - it(`should generate a valid multi-format "Video + Banner" imp object for ${bidderCode} config override`, () => { - const cfg = {}; - cfg[bidderCode] = { - mode: 'all' - }; - config.setConfig(cfg); - const { validBidRequests, bidderRequest } = generateBuildRequestMock({bidderCode, adUnitType: 'multi-format'}); - const data = spec.buildRequests(validBidRequests, 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}, {w: 300, h: 600}] - }); - expect(data.imp[0].video).to.deep.equal({ - mimes: ['video/mp4', 'application/javascript'], - w: 300, - h: 250, - api: [2], - protocols: [2, 5], - startdelay: 0, - linearity: 1, - maxbitrate: undefined, - maxduration: undefined, - minduration: undefined, - delivery: undefined, - pos: undefined, - playbackmethod: undefined, - rewarded: undefined, - placement: undefined - }); + // Validate multi-format Video+banner imp + it('should generate a valid multi-format "Video + Banner" imp object', () => { + config.setConfig({ + yahoossp: { mode: 'all' } + }); + const { validBidRequests, bidderRequest } = generateBuildRequestMock({adUnitType: 'multi-format'}); + const data = spec.buildRequests(validBidRequests, 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}, {w: 300, h: 600}] + }); + expect(data.imp[0].video).to.deep.equal({ + mimes: ['video/mp4', 'application/javascript'], + w: 300, + h: 250, + api: [2], + protocols: [2, 5], + startdelay: 0, + linearity: 1, + maxbitrate: undefined, + maxduration: undefined, + minduration: undefined, + delivery: undefined, + pos: undefined, + playbackmethod: undefined, + rewarded: undefined, + placement: undefined }); }); @@ -1221,6 +1179,7 @@ describe('Yahoo Advertising Bid Adapter:', () => { invalidKey5: undefined }; const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + expect(data.imp[0].ext.kvs).to.deep.equal({ key1: 'String', key2: 123456, @@ -1233,6 +1192,10 @@ describe('Yahoo Advertising Bid Adapter:', () => { describe('Multiple adUnit validations:', () => { // Multiple banner adUnits it('should generate multiple bid-requests for each adUnit - 2 banner only', () => { + config.setConfig({ + yahoossp: { mode: 'banner' } + }); + const BID_ID_2 = '84ab50xxxxx'; const BID_POS_2 = 'footer'; const AD_UNIT_CODE_2 = 'test-ad-unit-code-123'; @@ -1246,12 +1209,12 @@ describe('Yahoo Advertising Bid Adapter:', () => { validBidRequests = [bidRequest, bidRequest2, bidRequest3]; bidderRequest.bids = validBidRequests; - const reqs = spec.buildRequests(validBidRequests, bidderRequest) - expect(reqs).to.be.a('array'); - expect(reqs.length).to.equal(2); - reqs.forEach(req => { - expect(req.data.imp[0].video).to.not.exist - expect(req.data.imp[0].banner).to.deep.equal({ + const response = spec.buildRequests(validBidRequests, bidderRequest) + expect(response).to.be.a('array'); + expect(response.length).to.equal(2); + response.forEach((obj) => { + expect(obj.data.imp[0].video).to.not.exist + expect(obj.data.imp[0].banner).to.deep.equal({ mimes: ['text/html', 'text/javascript', 'application/javascript', 'image/jpg'], format: [{w: 300, h: 250}, {w: 300, h: 600}] }); @@ -1260,11 +1223,9 @@ describe('Yahoo Advertising Bid Adapter:', () => { // Multiple video adUnits it('should generate multiple bid-requests for each adUnit - 2 video only', () => { - const cfg = {}; - cfg[DEFAULT_BIDDER_CODE] = { - mode: VIDEO - }; - config.setConfig(cfg); + config.setConfig({ + yahoossp: { mode: 'video' } + }); const BID_ID_2 = '84ab50xxxxx'; const BID_POS_2 = 'footer'; const AD_UNIT_CODE_2 = 'test-ad-unit-code-123'; @@ -1272,18 +1233,18 @@ describe('Yahoo Advertising Bid Adapter:', () => { const BID_POS_3 = 'hero'; const AD_UNIT_CODE_3 = 'video-ad-unit'; - let {bidRequest, validBidRequests, bidderRequest} = generateBuildRequestMock({adUnitType: 'video'}); // video - const {bidRequest: bidRequest2} = generateBuildRequestMock({bidId: BID_ID_2, pos: BID_POS_2, adUnitCode: AD_UNIT_CODE_2, adUnitType: 'video'}); // video - const {bidRequest: bidRequest3} = generateBuildRequestMock({bidId: BID_ID_3, pos: BID_POS_3, adUnitCode: AD_UNIT_CODE_3}); // banner (should be filtered) + let { bidRequest, validBidRequests, bidderRequest } = generateBuildRequestMock({adUnitType: 'video'}); // video + const { bidRequest: bidRequest2 } = generateBuildRequestMock({bidId: BID_ID_2, pos: BID_POS_2, adUnitCode: AD_UNIT_CODE_2, adUnitType: 'video'}); // video + const { bidRequest: bidRequest3 } = generateBuildRequestMock({bidId: BID_ID_3, pos: BID_POS_3, adUnitCode: AD_UNIT_CODE_3}); // banner (should be filtered) validBidRequests = [bidRequest, bidRequest2, bidRequest3]; bidderRequest.bids = validBidRequests; - const reqs = spec.buildRequests(validBidRequests, bidderRequest) - expect(reqs).to.be.a('array'); - expect(reqs.length).to.equal(2); - reqs.forEach(req => { - expect(req.data.imp[0].banner).to.not.exist - expect(req.data.imp[0].video).to.deep.equal({ + const response = spec.buildRequests(validBidRequests, bidderRequest) + expect(response).to.be.a('array'); + expect(response.length).to.equal(2); + response.forEach((obj) => { + expect(obj.data.imp[0].banner).to.not.exist + expect(obj.data.imp[0].video).to.deep.equal({ mimes: ['video/mp4', 'application/javascript'], w: 300, h: 250, @@ -1304,9 +1265,9 @@ describe('Yahoo Advertising Bid Adapter:', () => { }); // Mixed adUnits 1-banner, 1-video, 1-native (should filter out native) it('should generate multiple bid-requests for both "video & banner" adUnits', () => { - const cfg = {}; - cfg[DEFAULT_BIDDER_CODE] = { mode: 'all' }; - config.setConfig(cfg); + config.setConfig({ + yahoossp: { mode: 'all' } + }); const BID_ID_2 = '84ab50xxxxx'; const BID_POS_2 = 'footer'; const AD_UNIT_CODE_2 = 'video-ad-unit'; @@ -1320,21 +1281,21 @@ describe('Yahoo Advertising Bid Adapter:', () => { validBidRequests = [bidRequest, bidRequest2, bidRequest3]; bidderRequest.bids = validBidRequests; - const reqs = spec.buildRequests(validBidRequests, bidderRequest); - expect(reqs).to.be.a('array'); - expect(reqs.length).to.equal(2); - reqs.forEach(req => { - expect(req.data.imp[0].native).to.not.exist; + const response = spec.buildRequests(validBidRequests, bidderRequest); + expect(response).to.be.a('array'); + expect(response.length).to.equal(2); + response.forEach((obj) => { + expect(obj.data.imp[0].native).to.not.exist; }); - const data1 = reqs[0].data; + const data1 = response[0].data; expect(data1.imp[0].video).to.not.exist; expect(data1.imp[0].banner).to.deep.equal({ mimes: ['text/html', 'text/javascript', 'application/javascript', 'image/jpg'], format: [{w: 300, h: 250}, {w: 300, h: 600}] }); - const data2 = reqs[1].data; + const data2 = response[1].data; expect(data2.imp[0].banner).to.not.exist; expect(data2.imp[0].video).to.deep.equal({ mimes: ['video/mp4', 'application/javascript'], @@ -1357,98 +1318,90 @@ describe('Yahoo Advertising Bid Adapter:', () => { }); describe('Video params firstlook & bidOverride validations:', () => { - VALID_BIDDER_CODES.forEach(bidderCode => { - it(`should first look at params.bidOverride for video placement data for ${bidderCode} config override`, () => { - const cfg = {}; - cfg[bidderCode] = { - mode: VIDEO - }; - config.setConfig(cfg); - const bidOverride = { - imp: { - video: { - mimes: ['video/mp4'], - w: 400, - h: 350, - api: [1], - protocols: [1, 3], - startdelay: 0, - linearity: 1, - maxbitrate: 400000, - maxduration: 3600, - minduration: 1500, - delivery: 1, - pos: 123456, - playbackmethod: 1, - rewarded: 1, - placement: 1 - } + it('should first look at params.bidOverride for video placement data', () => { + config.setConfig({ + yahoossp: { mode: 'video' } + }); + const bidOverride = { + imp: { + video: { + mimes: ['video/mp4'], + w: 400, + h: 350, + api: [1], + protocols: [1, 3], + startdelay: 0, + linearity: 1, + maxbitrate: 400000, + maxduration: 3600, + minduration: 1500, + delivery: 1, + pos: 123456, + playbackmethod: 1, + rewarded: 1, + placement: 1 } } - const { validBidRequests, bidderRequest } = generateBuildRequestMock({bidderCode, adUnitType: 'video', bidOverrideObject: bidOverride}); - const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; - expect(data.imp[0].video).to.deep.equal(bidOverride.imp.video); - }); + } + const { validBidRequests, bidderRequest } = generateBuildRequestMock({adUnitType: 'video', bidOverrideObject: bidOverride}); + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + expect(data.imp[0].video).to.deep.equal(bidOverride.imp.video); + }); - it(`should second look at bid.mediaTypes.video for video placement data for ${bidderCode} config override`, () => { - const cfg = {}; - cfg[bidderCode] = { - mode: VIDEO - }; - config.setConfig(cfg); - let { bidRequest, bidderRequest } = generateBuildRequestMock({bidderCode, adUnitType: 'video'}); - bidRequest.mediaTypes.video = { - mimes: ['video/mp4'], - playerSize: [400, 350], - api: [1], - protocols: [1, 3], - startdelay: 0, - linearity: 1, - maxbitrate: 400000, - maxduration: 3600, - minduration: 1500, - delivery: 1, - pos: 123456, - playbackmethod: 1, - placement: 1 - } - const validBidRequests = [bidRequest]; - bidderRequest.bids = validBidRequests; - const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; - expect(data.imp[0].video).to.deep.equal({ - mimes: ['video/mp4'], - w: 400, - h: 350, - api: [1], - protocols: [1, 3], - startdelay: 0, - linearity: 1, - maxbitrate: 400000, - maxduration: 3600, - minduration: 1500, - delivery: 1, - pos: 123456, - playbackmethod: 1, - placement: 1, - rewarded: undefined - }); + it('should second look at bid.mediaTypes.video for video placement data', () => { + config.setConfig({ + yahoossp: { mode: 'video' } }); + let { bidRequest, bidderRequest } = generateBuildRequestMock({adUnitType: 'video'}); + bidRequest.mediaTypes.video = { + mimes: ['video/mp4'], + playerSize: [400, 350], + api: [1], + protocols: [1, 3], + startdelay: 0, + linearity: 1, + maxbitrate: 400000, + maxduration: 3600, + minduration: 1500, + delivery: 1, + pos: 123456, + playbackmethod: 1, + placement: 1 + } + const validBidRequests = [bidRequest]; + bidderRequest.bids = validBidRequests; + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + expect(data.imp[0].video).to.deep.equal({ + mimes: ['video/mp4'], + w: 400, + h: 350, + api: [1], + protocols: [1, 3], + startdelay: 0, + linearity: 1, + maxbitrate: 400000, + maxduration: 3600, + minduration: 1500, + delivery: 1, + pos: 123456, + playbackmethod: 1, + placement: 1, + rewarded: undefined + }); + }); - it(`should use params.bidOverride.device.ip override for ${bidderCode} config override`, () => { - const cfg = {}; - cfg[bidderCode] = { - mode: 'all' - }; - config.setConfig(cfg); - const bidOverride = { - device: { - ip: '1.2.3.4' - } - } - const { validBidRequests, bidderRequest } = generateBuildRequestMock({bidderCode, adUnitType: 'video', bidOverrideObject: bidOverride}); - const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; - expect(data.device.ip).to.deep.equal(bidOverride.device.ip); + it('should use params.bidOverride.device.ip override', () => { + config.setConfig({ + yahoossp: { mode: 'all' } }); + const bidOverride = { + device: { + ip: '1.2.3.4' + } + } + const { validBidRequests, bidderRequest } = generateBuildRequestMock({adUnitType: 'video', bidOverrideObject: bidOverride}); + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + expect(data.device.ip).to.deep.equal(bidOverride.device.ip); }); }); // #endregion buildRequests(): @@ -1464,22 +1417,6 @@ describe('Yahoo Advertising Bid Adapter:', () => { }); describe('for mediaTypes: "video"', () => { - beforeEach(() => { - config.setConfig({ - yahooAds: { - mode: VIDEO - } - }); - }); - - afterEach(() => { - config.setConfig({ - yahooAds: { - mode: undefined - } - }); - }); - it('should insert video VPAID payload into vastXml', () => { const { serverResponse, bidderRequest } = generateResponseMock('video'); const response = spec.interpretResponse(serverResponse, {bidderRequest}); @@ -1497,38 +1434,28 @@ describe('Yahoo Advertising Bid Adapter:', () => { expect(response[0].mediaType).to.equal('video'); }) - describe('wrapped in video players for display inventory', () => { - beforeEach(() => { - config.setConfig({ - yahooAds: { - mode: undefined - } - }); - }); - - it('should insert video DAP O2 Player into ad', () => { - const { serverResponse, bidderRequest } = generateResponseMock('dap-o2', 'vpaid'); - const response = spec.interpretResponse(serverResponse, {bidderRequest}); - expect(response[0].ad).to.equal(''); - expect(response[0].vastUrl).to.be.undefined; - expect(response[0].vastXml).to.be.undefined; - expect(response[0].mediaType).to.equal('banner'); - }); - - it('should insert video DAP Unified Player into ad', () => { - const { serverResponse, bidderRequest } = generateResponseMock('dap-up', 'vpaid'); - const response = spec.interpretResponse(serverResponse, {bidderRequest}); - expect(response[0].ad).to.equal(''); - expect(response[0].vastUrl).to.be.undefined; - expect(response[0].vastXml).to.be.undefined; - expect(response[0].mediaType).to.equal('banner'); - }) + it('should insert video DAP O2 Player into ad', () => { + const { serverResponse, bidderRequest } = generateResponseMock('dap-o2', 'vpaid'); + const response = spec.interpretResponse(serverResponse, {bidderRequest}); + expect(response[0].ad).to.equal(''); + expect(response[0].vastUrl).to.be.undefined; + expect(response[0].vastXml).to.be.undefined; + expect(response[0].mediaType).to.equal('banner'); }); + + it('should insert video DAP Unified Player into ad', () => { + const { serverResponse, bidderRequest } = generateResponseMock('dap-up', 'vpaid'); + const response = spec.interpretResponse(serverResponse, {bidderRequest}); + expect(response[0].ad).to.equal(''); + expect(response[0].vastUrl).to.be.undefined; + expect(response[0].vastXml).to.be.undefined; + expect(response[0].mediaType).to.equal('banner'); + }) }); describe('Support Advertiser domains', () => { it('should append bid-response adomain to meta.advertiserDomains', () => { - const { serverResponse, bidderRequest } = generateResponseMock('banner'); + const { serverResponse, bidderRequest } = generateResponseMock('video', 'vpaid'); const response = spec.interpretResponse(serverResponse, {bidderRequest}); expect(response[0].meta.advertiserDomains).to.be.a('array'); expect(response[0].meta.advertiserDomains[0]).to.equal('advertiser-domain.com'); @@ -1563,55 +1490,53 @@ describe('Yahoo Advertising Bid Adapter:', () => { }); describe('Time To Live (ttl)', () => { - VALID_BIDDER_CODES.forEach(bidderCode => { - const UNSUPPORTED_TTL_FORMATS = ['string', [1, 2, 3], true, false, null, undefined]; - UNSUPPORTED_TTL_FORMATS.forEach(param => { - it(`should not allow unsupported global ${bidderCode}.ttl formats and default to 300`, () => { - const { serverResponse, bidderRequest } = generateResponseMock('banner'); - const cfg = {}; - cfg['yahooAds'] = { ttl: param }; - config.setConfig(cfg); - const response = spec.interpretResponse(serverResponse, {bidderRequest}); - expect(response[0].ttl).to.equal(300); - }); - - it('should not allow unsupported params.ttl formats and default to 300', () => { - const { serverResponse, bidderRequest } = generateResponseMock('banner'); - bidderRequest.bids[0].params.ttl = param; - const response = spec.interpretResponse(serverResponse, {bidderRequest}); - expect(response[0].ttl).to.equal(300); + const UNSUPPORTED_TTL_FORMATS = ['string', [1, 2, 3], true, false, null, undefined]; + UNSUPPORTED_TTL_FORMATS.forEach(param => { + it('should not allow unsupported global yahoossp.ttl formats and default to 300', () => { + const { serverResponse, bidderRequest } = generateResponseMock('banner'); + config.setConfig({ + yahoossp: { ttl: param } }); + const response = spec.interpretResponse(serverResponse, {bidderRequest}); + expect(response[0].ttl).to.equal(300); }); - const UNSUPPORTED_TTL_VALUES = [-1, 3601]; - UNSUPPORTED_TTL_VALUES.forEach(param => { - it('should not allow invalid global config ttl values 3600 < ttl < 0 and default to 300', () => { - const { serverResponse, bidderRequest } = generateResponseMock('banner'); - config.setConfig({ - yahooAds: { ttl: param } - }); - const response = spec.interpretResponse(serverResponse, {bidderRequest}); - expect(response[0].ttl).to.equal(300); - }); - - it('should not allow invalid params.ttl values 3600 < ttl < 0 and default to 300', () => { - const { serverResponse, bidderRequest } = generateResponseMock('banner'); - bidderRequest.bids[0].params.ttl = param; - const response = spec.interpretResponse(serverResponse, {bidderRequest}); - expect(response[0].ttl).to.equal(300); - }); + it('should not allow unsupported params.ttl formats and default to 300', () => { + const { serverResponse, bidderRequest } = generateResponseMock('banner'); + bidderRequest.bids[0].params.ttl = param; + const response = spec.interpretResponse(serverResponse, {bidderRequest}); + expect(response[0].ttl).to.equal(300); }); + }); - it('should give presedence to Gloabl ttl over params.ttl ', () => { + const UNSUPPORTED_TTL_VALUES = [-1, 3601]; + UNSUPPORTED_TTL_VALUES.forEach(param => { + it('should not allow invalid global yahoossp.ttl values 3600 < ttl < 0 and default to 300', () => { const { serverResponse, bidderRequest } = generateResponseMock('banner'); config.setConfig({ - yahooAds: { ttl: 500 } + yahoossp: { ttl: param } }); - bidderRequest.bids[0].params.ttl = 400; const response = spec.interpretResponse(serverResponse, {bidderRequest}); - expect(response[0].ttl).to.equal(500); + expect(response[0].ttl).to.equal(300); + }); + + it('should not allow invalid params.ttl values 3600 < ttl < 0 and default to 300', () => { + const { serverResponse, bidderRequest } = generateResponseMock('banner'); + bidderRequest.bids[0].params.ttl = param; + const response = spec.interpretResponse(serverResponse, {bidderRequest}); + expect(response[0].ttl).to.equal(300); }); }); + + it('should give presedence to Gloabl ttl over params.ttl ', () => { + const { serverResponse, bidderRequest } = generateResponseMock('banner'); + config.setConfig({ + yahoossp: { ttl: 500 } + }); + bidderRequest.bids[0].params.ttl = 400; + const response = spec.interpretResponse(serverResponse, {bidderRequest}); + expect(response[0].ttl).to.equal(500); + }); }); describe('Aliasing support', () => { diff --git a/test/spec/modules/yandexBidAdapter_spec.js b/test/spec/modules/yandexBidAdapter_spec.js index c5f088a2306..12d413a9c93 100644 --- a/test/spec/modules/yandexBidAdapter_spec.js +++ b/test/spec/modules/yandexBidAdapter_spec.js @@ -1,8 +1,8 @@ import { assert, expect } from 'chai'; import { spec, NATIVE_ASSETS } from 'modules/yandexBidAdapter.js'; -import * as utils from 'src/utils.js'; +import { parseUrl } from 'src/utils.js'; import { BANNER, NATIVE } from '../../../src/mediaTypes'; -import { config } from '../../../src/config'; +import {OPENRTB} from '../../../modules/rtbhouseBidAdapter'; describe('Yandex adapter', function () { describe('isBidRequestValid', function () { @@ -71,7 +71,7 @@ describe('Yandex adapter', function () { expect(method).to.equal('POST'); - const parsedRequestUrl = utils.parseUrl(url); + const parsedRequestUrl = parseUrl(url); const { search: query } = parsedRequestUrl expect(parsedRequestUrl.hostname).to.equal('bs.yandex.ru'); @@ -90,65 +90,6 @@ describe('Yandex adapter', function () { expect(data.site.ref).to.equal('https://ya.ru/'); }); - it('should send currency if defined', function () { - config.setConfig({ - currency: { - adServerCurrency: 'USD' - } - }); - - const bannerRequest = getBidRequest(); - const requests = spec.buildRequests([bannerRequest], bidderRequest); - const { url } = requests[0]; - const parsedRequestUrl = utils.parseUrl(url); - const { search: query } = parsedRequestUrl - - expect(query['ssp-cur']).to.equal('USD'); - }); - - it('should send eids and ortb2 user data if defined', function() { - const bidRequestExtra = { - userIdAsEids: [{ - source: 'sharedid.org', - uids: [{ id: '01', atype: 1 }], - }], - ortb2: { - user: { - data: [ - { - ext: { segtax: 600, segclass: '1' }, - name: 'example.com', - segment: [{ id: '243' }], - }, - { - ext: { segtax: 600, segclass: '1' }, - name: 'ads.example.org', - segment: [{ id: '243' }], - }, - ], - }, - }, - }; - const expected = { - ext: { - eids: bidRequestExtra.userIdAsEids, - }, - data: bidRequestExtra.ortb2.user.data, - }; - - const bannerRequest = getBidRequest(bidRequestExtra); - const requests = spec.buildRequests([bannerRequest], bidderRequest); - - expect(requests).to.have.lengthOf(1); - const request = requests[0]; - - expect(request.data).to.exist; - const { data } = request; - - expect(data.user).to.exist; - expect(data.user).to.deep.equal(expected); - }); - describe('banner', () => { it('should create valid banner object', () => { const bannerRequest = getBidRequest({ @@ -332,7 +273,7 @@ describe('Yandex adapter', function () { }, }); }); - }); + }) }); describe('interpretResponse', function () { @@ -353,7 +294,6 @@ describe('Yandex adapter', function () { 'example.com' ], adid: 'yabs.123=', - nurl: 'https://example.com/nurl/?price=${AUCTION_PRICE}&cur=${AUCTION_CURRENCY}', } ] }], @@ -374,12 +314,11 @@ describe('Yandex adapter', function () { const rtbBid = result[0]; expect(rtbBid.width).to.equal(300); expect(rtbBid.height).to.equal(250); - expect(rtbBid.cpm).to.be.within(0.3, 0.3); + expect(rtbBid.cpm).to.be.within(0.1, 0.5); expect(rtbBid.ad).to.equal(''); expect(rtbBid.currency).to.equal('USD'); expect(rtbBid.netRevenue).to.equal(true); expect(rtbBid.ttl).to.equal(180); - expect(rtbBid.nurl).to.equal('https://example.com/nurl/?price=0.3&cur=USD'); expect(rtbBid.meta.advertiserDomains).to.deep.equal(['example.com']); }); @@ -486,55 +425,6 @@ describe('Yandex adapter', function () { }); }); }); - - describe('onBidWon', function() { - beforeEach(function() { - sinon.stub(utils, 'triggerPixel'); - }); - afterEach(function() { - utils.triggerPixel.restore(); - }); - - it('Should not trigger pixel if bid does not contain nurl', function() { - const result = spec.onBidWon({}); - expect(utils.triggerPixel.callCount).to.equal(0) - }) - - it('Should trigger pixel if bid has nurl', function() { - const result = spec.onBidWon({ - nurl: 'https://example.com/some-tracker', - timeToRespond: 378, - }); - expect(utils.triggerPixel.callCount).to.equal(1) - expect(utils.triggerPixel.getCall(0).args[0]).to.equal('https://example.com/some-tracker?rtt=378') - }) - - it('Should trigger pixel if bid has nurl with path & params', function() { - const result = spec.onBidWon({ - nurl: 'https://example.com/some-tracker/abcdxyz?param1=1¶m2=2', - timeToRespond: 378, - }); - expect(utils.triggerPixel.callCount).to.equal(1) - expect(utils.triggerPixel.getCall(0).args[0]).to.equal('https://example.com/some-tracker/abcdxyz?param1=1¶m2=2&rtt=378') - }) - - it('Should trigger pixel if bid has nurl with path & params and rtt macros', function() { - const result = spec.onBidWon({ - nurl: 'https://example.com/some-tracker/abcdxyz?param1=1¶m2=2&custom-rtt=${RTT}', - timeToRespond: 378, - }); - expect(utils.triggerPixel.callCount).to.equal(1) - expect(utils.triggerPixel.getCall(0).args[0]).to.equal('https://example.com/some-tracker/abcdxyz?param1=1¶m2=2&custom-rtt=378') - }) - - it('Should trigger pixel if bid has nurl and there is no timeToRespond param, but has rtt macros in nurl', function() { - const result = spec.onBidWon({ - nurl: 'https://example.com/some-tracker/abcdxyz?param1=1¶m2=2&custom-rtt=${RTT}', - }); - expect(utils.triggerPixel.callCount).to.equal(1) - expect(utils.triggerPixel.getCall(0).args[0]).to.equal('https://example.com/some-tracker/abcdxyz?param1=1¶m2=2&custom-rtt=-1') - }) - }) }); function getBidConfig() { diff --git a/test/spec/modules/yieldloveBidAdapter_spec.js b/test/spec/modules/yieldloveBidAdapter_spec.js deleted file mode 100644 index b142eef0ffa..00000000000 --- a/test/spec/modules/yieldloveBidAdapter_spec.js +++ /dev/null @@ -1,128 +0,0 @@ -import { expect } from 'chai'; -import { spec } from 'modules/yieldloveBidAdapter.js'; - -const ENDPOINT_URL = 'https://s2s.yieldlove-ad-serving.net/openrtb2/auction'; - -// test params -const pid = 34437; -const rid = 'website.com'; - -describe('Yieldlove Bid Adaper', function () { - const bidRequests = [ - { - 'bidder': 'yieldlove', - 'adUnitCode': 'adunit-code', - 'sizes': [ [300, 250] ], - 'params': { - pid, - rid - } - } - ]; - - const serverResponse = { - body: { - seatbid: [ - { - bid: [ - { - impid: 'aaaa', - price: 0.5, - w: 300, - h: 250, - adm: '
test
', - crid: '1234', - } - ] - } - ], - ext: {} - } - } - - describe('isBidRequestValid', () => { - const bid = bidRequests[0]; - - it('should return true when required params found', function () { - expect(spec.isBidRequestValid(bid)).to.equal(true); - }); - - it('should return false when required params are not present', function () { - const invalidBid = { ...bid, params: {} }; - expect(spec.isBidRequestValid(invalidBid)).to.equal(false); - }); - - it('should return false when required param "pid" is not present', function () { - const invalidBid = { ...bid, params: { ...bid.params, pid: undefined } }; - expect(spec.isBidRequestValid(invalidBid)).to.equal(false); - }); - - it('should return false when required param "rid" is not present', function () { - const invalidBid = { ...bid, params: { ...bid.params, rid: undefined } }; - expect(spec.isBidRequestValid(invalidBid)).to.equal(false); - }); - }); - - describe('buildRequests', () => { - it('should build the request', function () { - const request = spec.buildRequests(bidRequests, {}); - const payload = request.data; - const url = request.url; - - expect(url).to.equal(ENDPOINT_URL); - - expect(payload.site).to.exist; - expect(payload.site.publisher).to.exist; - expect(payload.site.publisher.id).to.exist; - expect(payload.site.publisher.id).to.equal(rid); - expect(payload.site.domain).to.exist; - expect(payload.site.domain).to.equal(rid); - - expect(payload.imp).to.exist; - expect(payload.imp[0]).to.exist; - expect(payload.imp[0].ext).to.exist; - expect(payload.imp[0].ext.prebid).to.exist; - expect(payload.imp[0].ext.prebid.storedrequest).to.exist; - expect(payload.imp[0].ext.prebid.storedrequest.id).to.exist; - expect(payload.imp[0].ext.prebid.storedrequest.id).to.equal(pid.toString()); - }); - }); - - describe('interpretResponse', () => { - it('should interpret the response by pushing it in the bids elem', function () { - const allResponses = spec.interpretResponse(serverResponse); - const response = allResponses[0]; - const seatbid = serverResponse.body.seatbid[0].bid[0]; - - expect(response.requestId).to.exist; - expect(response.requestId).to.equal(seatbid.impid); - expect(response.cpm).to.exist; - expect(response.cpm).to.equal(seatbid.price); - expect(response.width).to.exist; - expect(response.width).to.equal(seatbid.w); - expect(response.height).to.exist; - expect(response.height).to.equal(seatbid.h); - expect(response.ad).to.exist; - expect(response.ad).to.equal(seatbid.adm); - expect(response.ttl).to.exist; - expect(response.creativeId).to.exist; - expect(response.creativeId).to.equal(seatbid.crid); - expect(response.netRevenue).to.exist; - expect(response.currency).to.exist; - }); - }); - - describe('getUserSyncs', function() { - it('should retrieve user iframe syncs', function () { - expect(spec.getUserSyncs({ iframeEnabled: true }, [serverResponse], undefined, undefined)).to.deep.equal([{ - type: 'iframe', - url: 'https://cdn-a.yieldlove.com/load-cookie.html?endpoint=yieldlove&max_sync_count=100&gdpr=NaN&gdpr_consent=&' - }]); - - expect(spec.getUserSyncs({ iframeEnabled: true }, [serverResponse], { gdprApplies: true, consentString: 'example' }, undefined)).to.deep.equal([{ - type: 'iframe', - url: 'https://cdn-a.yieldlove.com/load-cookie.html?endpoint=yieldlove&max_sync_count=100&gdpr=1&gdpr_consent=example&' - }]); - }); - }); -}) diff --git a/test/spec/modules/yieldmoBidAdapter_spec.js b/test/spec/modules/yieldmoBidAdapter_spec.js index edb3ef3af27..3706f770da8 100644 --- a/test/spec/modules/yieldmoBidAdapter_spec.js +++ b/test/spec/modules/yieldmoBidAdapter_spec.js @@ -47,7 +47,7 @@ describe('YieldmoAdapter', function () { video: { playerSize: [640, 480], context: 'instream', - mimes: ['video/mp4'], + mimes: ['video/mp4'] }, }, params: { @@ -61,11 +61,11 @@ describe('YieldmoAdapter', function () { api: [2, 3], skipppable: true, playbackmethod: [1, 2], - ...videoParams, - }, + ...videoParams + } }, transactionId: '54a58774-7a41-494e-8cbc-fa7b79164f0c', - ...rootParams, + ...rootParams }); const mockBidderRequest = (params = {}, bids = [mockBannerBid()]) => ({ @@ -74,6 +74,7 @@ describe('YieldmoAdapter', function () { bidderRequestId: '14c4ede8c693f', bids, auctionStart: 1520001292880, + timeout: 3000, start: 1520001292884, doneCbCallCount: 0, refererInfo: { @@ -168,14 +169,6 @@ describe('YieldmoAdapter', function () { expect(requests[0].url).to.be.equal(BANNER_ENDPOINT); }); - it('should pass default timeout in bid request', function () { - const requests = build([mockBannerBid()]); - expect(requests[0].data.tmax).to.equal(400); - }); - it('should pass tmax to bid request', function () { - const requests = build([mockBannerBid()], mockBidderRequest({timeout: 1000})); - expect(requests[0].data.tmax).to.equal(1000); - }); it('should not blow up if crumbs is undefined', function () { expect(function () { build([mockBannerBid({crumbs: undefined})]); @@ -432,18 +425,6 @@ describe('YieldmoAdapter', function () { expect(requests[0].url).to.be.equal(VIDEO_ENDPOINT); }); - it('should not require params.video if required props in mediaTypes.video', function () { - videoBid.mediaTypes.video = { - ...videoBid.mediaTypes.video, - ...videoBid.params.video - }; - delete videoBid.params.video; - const requests = build([videoBid]); - expect(requests.length).to.equal(1); - expect(requests[0].method).to.equal('POST'); - expect(requests[0].url).to.be.equal(VIDEO_ENDPOINT); - }); - it('should add mediaTypes.video prop to the imp.video prop', function () { utils.deepAccess(videoBid, 'mediaTypes.video')['minduration'] = 40; expect(buildVideoBidAndGetVideoParam().minduration).to.equal(40); @@ -460,36 +441,12 @@ describe('YieldmoAdapter', function () { expect(buildVideoBidAndGetVideoParam().minduration).to.deep.equal(['video/mp4']); }); - it('should add plcmt value to the imp.video', function () { - const videoBid = mockVideoBid({}, {}, { plcmt: 1 }); - expect(utils.deepAccess(videoBid, 'params.video')['plcmt']).to.equal(1); - }); - - it('should add start delay if plcmt value is not 1', function () { - const videoBid = mockVideoBid({}, {}, { plcmt: 2 }); - expect(build([videoBid])[0].data.imp[0].video.startdelay).to.equal(0); - }); - it('should override mediaTypes.video.mimes prop if params.video.mimes is present', function () { utils.deepAccess(videoBid, 'mediaTypes.video')['mimes'] = ['video/mp4']; utils.deepAccess(videoBid, 'params.video')['mimes'] = ['video/mkv']; expect(buildVideoBidAndGetVideoParam().mimes).to.deep.equal(['video/mkv']); }); - it('should validate protocol in video bid request', function () { - expect( - spec.isBidRequestValid( - mockVideoBid({}, {}, { protocols: [2, 3, 11] }) - ) - ).to.be.true; - - expect( - spec.isBidRequestValid( - mockVideoBid({}, {}, { protocols: [2, 3, 10] }) - ) - ).to.be.false; - }); - describe('video.skip state check', () => { it('should not set video.skip if neither *.video.skip nor *.video.skippable is present', function () { utils.deepAccess(videoBid, 'mediaTypes.video')['skippable'] = false; diff --git a/test/spec/modules/zeta_global_sspAnalyticsAdapter_spec.js b/test/spec/modules/zeta_global_sspAnalyticsAdapter_spec.js index 5194a6a526a..15a1155f378 100644 --- a/test/spec/modules/zeta_global_sspAnalyticsAdapter_spec.js +++ b/test/spec/modules/zeta_global_sspAnalyticsAdapter_spec.js @@ -1,21 +1,20 @@ import zetaAnalyticsAdapter from 'modules/zeta_global_sspAnalyticsAdapter.js'; import {config} from 'src/config'; import CONSTANTS from 'src/constants.json'; -import {server} from '../../mocks/xhr.js'; import {logError} from '../../../src/utils'; let utils = require('src/utils'); let events = require('src/events'); -const EVENTS = { +const MOCK = { + STUB: { + 'auctionId': '25c6d7f5-699a-4bfc-87c9-996f915341fa' + }, AUCTION_END: { 'auctionId': '75e394d9-ccce-4978-9238-91e6a1ac88a1', 'timestamp': 1638441234544, 'auctionEnd': 1638441234784, 'auctionStatus': 'completed', - 'metrics': { - 'someMetric': 1 - }, 'adUnits': [ { 'code': '/19968336/header-bid-tag-0', @@ -75,6 +74,13 @@ const EVENTS = { 'bids': [ { 'bidder': 'zeta_global_ssp', + 'params': { + 'sid': 111, + 'tags': { + 'shortname': 'prebid_analytics_event_test_shortname', + 'position': 'test_position' + } + }, 'mediaTypes': { 'banner': { 'sizes': [ @@ -303,9 +309,6 @@ const EVENTS = { 'cpm': 2.258302852806723, 'currency': 'USD', 'ad': 'test_ad', - 'metrics': { - 'someMetric': 0 - }, 'ttl': 200, 'creativeId': '456456456', 'netRevenue': true, @@ -341,7 +344,11 @@ const EVENTS = { 'status': 'rendered', 'params': [ { - 'nonZetaParam': 'nonZetaValue' + 'sid': 111, + 'tags': { + 'shortname': 'prebid_analytics_event_test_shortname', + 'position': 'test_position' + } } ] }, @@ -351,11 +358,14 @@ const EVENTS = { describe('Zeta Global SSP Analytics Adapter', function() { let sandbox; + let xhr; let requests; beforeEach(function() { sandbox = sinon.sandbox.create(); - requests = server.requests; + requests = []; + xhr = sandbox.useFakeXMLHttpRequest(); + xhr.onCreate = request => requests.push(request); sandbox.stub(events, 'getEvents').returns([]); }); @@ -385,45 +395,33 @@ describe('Zeta Global SSP Analytics Adapter', function() { zetaAnalyticsAdapter.disableAnalytics(); }); - it('Move ZetaParams through analytics events', function() { - this.timeout(3000); - - events.emit(CONSTANTS.EVENTS.AUCTION_END, EVENTS.AUCTION_END); - events.emit(CONSTANTS.EVENTS.AD_RENDER_SUCCEEDED, EVENTS.AD_RENDER_SUCCEEDED); - - expect(requests.length).to.equal(2); - const auctionEnd = JSON.parse(requests[0].requestBody); - const auctionSucceeded = JSON.parse(requests[1].requestBody); - - expect(auctionSucceeded.bid.params[0]).to.be.deep.equal(EVENTS.AUCTION_END.adUnits[0].bids[0].params); - expect(EVENTS.AUCTION_END.adUnits[0].bids[0].bidder).to.be.equal('zeta_global_ssp'); - }); - - it('Keep only needed fields', function() { - this.timeout(3000); - - events.emit(CONSTANTS.EVENTS.AUCTION_END, EVENTS.AUCTION_END); - events.emit(CONSTANTS.EVENTS.AD_RENDER_SUCCEEDED, EVENTS.AD_RENDER_SUCCEEDED); + it('events are sent', function() { + this.timeout(5000); + events.emit(CONSTANTS.EVENTS.AUCTION_INIT, MOCK.STUB); + events.emit(CONSTANTS.EVENTS.AUCTION_END, MOCK.AUCTION_END); + events.emit(CONSTANTS.EVENTS.BID_ADJUSTMENT, MOCK.STUB); + events.emit(CONSTANTS.EVENTS.BID_TIMEOUT, MOCK.STUB); + events.emit(CONSTANTS.EVENTS.BID_REQUESTED, MOCK.STUB); + events.emit(CONSTANTS.EVENTS.BID_RESPONSE, MOCK.STUB); + events.emit(CONSTANTS.EVENTS.NO_BID, MOCK.STUB); + events.emit(CONSTANTS.EVENTS.BID_WON, MOCK.STUB); + events.emit(CONSTANTS.EVENTS.BIDDER_DONE, MOCK.STUB); + events.emit(CONSTANTS.EVENTS.BIDDER_ERROR, MOCK.STUB); + events.emit(CONSTANTS.EVENTS.SET_TARGETING, MOCK.STUB); + events.emit(CONSTANTS.EVENTS.BEFORE_REQUEST_BIDS, MOCK.STUB); + events.emit(CONSTANTS.EVENTS.BEFORE_BIDDER_HTTP, MOCK.STUB); + events.emit(CONSTANTS.EVENTS.REQUEST_BIDS, MOCK.STUB); + events.emit(CONSTANTS.EVENTS.ADD_AD_UNITS, MOCK.STUB); + events.emit(CONSTANTS.EVENTS.AD_RENDER_FAILED, MOCK.STUB); + events.emit(CONSTANTS.EVENTS.AD_RENDER_SUCCEEDED, MOCK.AD_RENDER_SUCCEEDED); + events.emit(CONSTANTS.EVENTS.TCF2_ENFORCEMENT, MOCK.STUB); + events.emit(CONSTANTS.EVENTS.AUCTION_DEBUG, MOCK.STUB); + events.emit(CONSTANTS.EVENTS.BID_VIEWABLE, MOCK.STUB); + events.emit(CONSTANTS.EVENTS.STALE_RENDER, MOCK.STUB); expect(requests.length).to.equal(2); - const auctionEnd = JSON.parse(requests[0].requestBody); - const auctionSucceeded = JSON.parse(requests[1].requestBody); - - expect(auctionEnd.adUnitCodes[0]).to.be.equal('/19968336/header-bid-tag-0'); - expect(auctionEnd.adUnits[0].bids[0].bidder).to.be.equal('zeta_global_ssp'); - expect(auctionEnd.auctionEnd).to.be.equal(1638441234784); - expect(auctionEnd.auctionId).to.be.equal('75e394d9-ccce-4978-9238-91e6a1ac88a1'); - expect(auctionEnd.bidderRequests[0].bidderCode).to.be.equal('zeta_global_ssp'); - expect(auctionEnd.bidsReceived[0].bidderCode).to.be.equal('zeta_global_ssp'); - expect(auctionEnd.noBids[0].bidder).to.be.equal('appnexus'); - - expect(auctionSucceeded.adId).to.be.equal('5759bb3ef7be1e8'); - expect(auctionSucceeded.bid.auctionId).to.be.equal('75e394d9-ccce-4978-9238-91e6a1ac88a1'); - expect(auctionSucceeded.bid.requestId).to.be.equal('206be9a13236af'); - expect(auctionSucceeded.bid.bidderCode).to.be.equal('zeta_global_ssp'); - expect(auctionSucceeded.bid.creativeId).to.be.equal('456456456'); - expect(auctionSucceeded.bid.size).to.be.equal('480x320'); - expect(auctionSucceeded.doc.location.hostname).to.be.equal('localhost'); + expect(JSON.parse(requests[0].requestBody)).to.deep.equal(MOCK.AUCTION_END); + expect(JSON.parse(requests[1].requestBody)).to.deep.equal(MOCK.AD_RENDER_SUCCEEDED); }); }); }); diff --git a/test/spec/modules/zeta_global_sspBidAdapter_spec.js b/test/spec/modules/zeta_global_sspBidAdapter_spec.js index 9ef97019a98..601f4546a29 100644 --- a/test/spec/modules/zeta_global_sspBidAdapter_spec.js +++ b/test/spec/modules/zeta_global_sspBidAdapter_spec.js @@ -1,6 +1,5 @@ import {spec} from '../../../modules/zeta_global_sspBidAdapter.js' import {BANNER, VIDEO} from '../../../src/mediaTypes'; -import {deepClone} from '../../../src/utils'; describe('Zeta Ssp Bid Adapter', function () { const eids = [ @@ -51,6 +50,7 @@ describe('Zeta Ssp Bid Adapter', function () { someTag: 444, }, sid: 'publisherId', + shortname: 'test_shortname', tagid: 'test_tag_id', site: { page: 'testPage' @@ -124,24 +124,7 @@ describe('Zeta Ssp Bid Adapter', function () { uspConsent: 'someCCPAString', params: params, userIdAsEids: eids, - timeout: 500, - ortb2: { - user: { - data: [ - { - ext: { - segtax: 600, - segclass: 'classifier_v1' - }, - segment: [ - { id: '3' }, - { id: '44' }, - { id: '59' } - ] - } - ] - } - } + timeout: 500 }]; const bannerWithFewSizesRequest = [{ @@ -270,13 +253,11 @@ describe('Zeta Ssp Bid Adapter', function () { }; it('Test the bid validation function', function () { - const invalidBid = deepClone(bannerRequest[0]); - invalidBid.params = {}; - const isValidBid = spec.isBidRequestValid(bannerRequest[0]); - const isInvalidBid = spec.isBidRequestValid(null); + const validBid = spec.isBidRequestValid(bannerRequest[0]); + const invalidBid = spec.isBidRequestValid(null); - expect(isValidBid).to.be.true; - expect(isInvalidBid).to.be.false; + expect(validBid).to.be.true; + expect(invalidBid).to.be.false; }); it('Test provide eids', function () { @@ -472,7 +453,7 @@ describe('Zeta Ssp Bid Adapter', function () { it('Test required params in banner request', function () { const request = spec.buildRequests(bannerRequest, bannerRequest[0]); const payload = JSON.parse(request.data); - expect(request.url).to.eql('https://ssp.disqus.com/bid/prebid?sid=publisherId'); + expect(request.url).to.eql('https://ssp.disqus.com/bid/prebid?shortname=test_shortname'); expect(payload.ext.sid).to.eql('publisherId'); expect(payload.ext.tags.someTag).to.eql(444); expect(payload.ext.tags.shortname).to.be.undefined; @@ -481,7 +462,7 @@ describe('Zeta Ssp Bid Adapter', function () { it('Test required params in video request', function () { const request = spec.buildRequests(videoRequest, videoRequest[0]); const payload = JSON.parse(request.data); - expect(request.url).to.eql('https://ssp.disqus.com/bid/prebid?sid=publisherId'); + expect(request.url).to.eql('https://ssp.disqus.com/bid/prebid?shortname=test_shortname'); expect(payload.ext.sid).to.eql('publisherId'); expect(payload.ext.tags.someTag).to.eql(444); expect(payload.ext.tags.shortname).to.be.undefined; @@ -490,7 +471,7 @@ describe('Zeta Ssp Bid Adapter', function () { it('Test multi imp', function () { const request = spec.buildRequests(multiImpRequest, multiImpRequest[0]); const payload = JSON.parse(request.data); - expect(request.url).to.eql('https://ssp.disqus.com/bid/prebid?sid=publisherId'); + expect(request.url).to.eql('https://ssp.disqus.com/bid/prebid?shortname=test_shortname'); expect(payload.imp.length).to.eql(2); @@ -623,13 +604,4 @@ describe('Zeta Ssp Bid Adapter', function () { expect(bidResponse[0].ad).to.eql(zetaResponse.body.seatbid[0].bid[0].adm); expect(bidResponse[0].vastXml).to.be.undefined; }); - - it('Test provide segments into the request', function () { - const request = spec.buildRequests(bannerRequest, bannerRequest[0]); - const payload = JSON.parse(request.data); - expect(payload.user.data[0].segment.length).to.eql(3); - expect(payload.user.data[0].segment[0].id).to.eql('3'); - expect(payload.user.data[0].segment[1].id).to.eql('44'); - expect(payload.user.data[0].segment[2].id).to.eql('59'); - }); }); diff --git a/test/spec/modules/zmaticooBidAdapter_spec.js b/test/spec/modules/zmaticooBidAdapter_spec.js deleted file mode 100644 index 898dccf1fde..00000000000 --- a/test/spec/modules/zmaticooBidAdapter_spec.js +++ /dev/null @@ -1,83 +0,0 @@ -import {spec} from '../../../modules/zmaticooBidAdapter.js' - -describe('zMaticoo Bidder Adapter', function () { - const bannerRequest = [{ - bidId: '1234511', - auctionId: '223', - mediaTypes: { - banner: { - sizes: [[320, 50]], - } - }, - refererInfo: { - page: 'testprebid.com' - }, - params: { - user: { - uid: '12345', - buyeruid: '12345' - }, - device: { - ip: '111.222.33.44', - geo: { - country: 'USA' - } - }, - pubId: 'prebid-test', - test: 1 - } - }]; - - it('Test the bid validation function', function () { - const validBid = spec.isBidRequestValid(bannerRequest[0]); - const invalidBid = spec.isBidRequestValid(null); - - expect(validBid).to.be.true; - expect(invalidBid).to.be.false; - }); - - it('Test the request processing function', function () { - const request = spec.buildRequests(bannerRequest, bannerRequest[0]); - expect(request).to.not.be.empty; - - const payload = request.data; - expect(payload).to.not.be.empty; - }); - - const responseBody = { - id: '12345', - seatbid: [ - { - bid: [ - { - id: 'auctionId', - impid: 'impId', - price: 0.0, - adm: 'adMarkup', - crid: 'creativeId', - h: 50, - w: 320 - } - ] - } - ], - cur: 'USD' - }; - - it('Test the response parsing function', function () { - const receivedBid = responseBody.seatbid[0].bid[0]; - const response = {}; - response.body = responseBody; - - const bidResponse = spec.interpretResponse(response, null); - expect(bidResponse).to.not.be.empty; - - const bid = bidResponse[0]; - expect(bid).to.not.be.empty; - expect(bid.ad).to.equal(receivedBid.adm); - expect(bid.cpm).to.equal(receivedBid.price); - expect(bid.height).to.equal(receivedBid.h); - expect(bid.width).to.equal(receivedBid.w); - expect(bid.requestId).to.equal(receivedBid.impid); - }); -}); diff --git a/test/spec/native_spec.js b/test/spec/native_spec.js index 9cfee6f5cd8..dee177d4b9b 100644 --- a/test/spec/native_spec.js +++ b/test/spec/native_spec.js @@ -631,8 +631,7 @@ describe('native.js', function () { eventtrackers: [ { event: 1, method: 1, url: 'https://sampleurl.com' }, { event: 1, method: 2, url: 'https://sampleurljs.com' } - ], - imptrackers: [ 'https://sample-imp.com' ] + ] } describe('toLegacyResponse', () => { it('returns assets in legacy format for ortb responses', () => { @@ -641,9 +640,8 @@ describe('native.js', function () { expect(actual.title).to.equal('vtitle'); expect(actual.clickUrl).to.equal('url'); expect(actual.javascriptTrackers).to.equal(''); - expect(actual.impressionTrackers.length).to.equal(2); - expect(actual.impressionTrackers).to.contain('https://sampleurl.com'); - expect(actual.impressionTrackers).to.contain('https://sample-imp.com'); + expect(actual.impressionTrackers.length).to.equal(1); + expect(actual.impressionTrackers[0]).to.equal('https://sampleurl.com'); }); }); }); @@ -862,9 +860,6 @@ describe('validate native', function () { }] }, address: {}, - privacyLink: { - required: true - } }, }, }; @@ -920,7 +915,6 @@ describe('validate native', function () { type: 9, } }); - expect(ortb.privacy).to.equal(1); }); ['bogusKey', 'clickUrl', 'privacyLink'].forEach(nativeKey => { @@ -1028,14 +1022,11 @@ describe('validate native', function () { expect(oldNativeRequest.sponsoredBy).to.include({ required: true, len: 25 - }); + }) expect(oldNativeRequest.body).to.include({ required: true, len: 140 - }); - expect(oldNativeRequest.privacyLink).to.include({ - required: false - }); + }) }); if (FEATURES.NATIVE) { @@ -1206,12 +1197,6 @@ describe('legacyPropertiesToOrtbNative', () => { expect(native.jstracker).to.eql('some-markupsome-other-markup'); }) }); - describe('privacylink', () => { - it('should convert privacyLink to privacy', () => { - const native = legacyPropertiesToOrtbNative({privacyLink: 'https:/my-privacy-link.com'}); - expect(native.privacy).to.eql('https:/my-privacy-link.com'); - }) - }) }); describe('fireImpressionTrackers', () => { diff --git a/test/spec/unit/core/adapterManager_spec.js b/test/spec/unit/core/adapterManager_spec.js index dac70696b4b..cff26df2e4d 100644 --- a/test/spec/unit/core/adapterManager_spec.js +++ b/test/spec/unit/core/adapterManager_spec.js @@ -964,42 +964,15 @@ describe('adapterManager tests', function () { 'start': 1462918897460 }]; - describe('invokes callBids on the S2S adapter', () => { - let onTimelyResponse, timedOut, done; - beforeEach(() => { - done = sinon.stub(); - onTimelyResponse = sinon.stub(); - prebidServerAdapterMock.callBids.callsFake((_1, _2, _3, done) => { - done(timedOut); - }); - }) - - function runTest() { - adapterManager.callBids( - getAdUnits(), - bidRequests, - () => {}, - done, - undefined, - undefined, - onTimelyResponse - ); - sinon.assert.calledTwice(prebidServerAdapterMock.callBids); - sinon.assert.calledTwice(done); - } - - it('and marks requests as timely if the adapter says timedOut = false', function () { - timedOut = false; - runTest(); - bidRequests.forEach(br => sinon.assert.calledWith(onTimelyResponse, br.bidderRequestId)); - }); - - it('and does NOT mark them as timely if it says timedOut = true', () => { - timedOut = true; - runTest(); - sinon.assert.notCalled(onTimelyResponse); - }) - }) + it('invokes callBids on the S2S adapter', function () { + adapterManager.callBids( + getAdUnits(), + bidRequests, + () => {}, + () => () => {} + ); + sinon.assert.calledTwice(prebidServerAdapterMock.callBids); + }); // Enable this test when prebidServer adapter is made 1.0 compliant it('invokes callBids with only s2s bids', function () { diff --git a/test/spec/unit/core/ajax_spec.js b/test/spec/unit/core/ajax_spec.js deleted file mode 100644 index dd03ad1a761..00000000000 --- a/test/spec/unit/core/ajax_spec.js +++ /dev/null @@ -1,429 +0,0 @@ -import {attachCallbacks, dep, fetcherFactory, toFetchRequest} from '../../../../src/ajax.js'; -import {config} from 'src/config.js'; -import {server} from '../../../mocks/xhr.js'; -import * as utils from 'src/utils.js'; -import {logError} from 'src/utils.js'; - -const EXAMPLE_URL = 'https://www.example.com'; - -describe('fetcherFactory', () => { - let clock; - - beforeEach(() => { - clock = sinon.useFakeTimers(); - server.autoTimeout = true; - }); - - afterEach(() => { - clock.runAll(); - clock.restore(); - config.resetConfig(); - }); - - Object.entries({ - 'URL': EXAMPLE_URL, - 'request object': new Request(EXAMPLE_URL) - }).forEach(([t, resource]) => { - it(`times out after timeout when fetching ${t}`, (done) => { - const fetch = fetcherFactory(1000); - const resp = fetch(resource); - clock.tick(900); - expect(server.requests[0].fetch.request.signal.aborted).to.be.false; - clock.tick(100); - expect(server.requests[0].fetch.request.signal.aborted).to.be.true; - resp.catch(() => done()); - }); - }); - - it('does not timeout after it completes', () => { - const fetch = fetcherFactory(1000); - const resp = fetch(EXAMPLE_URL); - server.requests[0].respond(); - return resp.then(() => { - clock.tick(2000); - expect(server.requests[0].fetch.request.signal.aborted).to.be.false; - }); - }); - - Object.entries({ - 'disableAjaxTimeout is set'() { - const fetcher = fetcherFactory(1000); - config.setConfig({disableAjaxTimeout: true}); - return fetcher; - }, - 'timeout is null'() { - return fetcherFactory(null); - }, - }).forEach(([t, mkFetcher]) => { - it(`does not timeout if ${t}`, (done) => { - const fetch = mkFetcher(); - const pm = fetch(EXAMPLE_URL); - clock.tick(2000); - server.requests[0].respond(); - pm.then(() => done()); - }); - }); - - Object.entries({ - 'local URL': ['/local.html', window.origin], - 'remote URL': [EXAMPLE_URL + '/remote.html', EXAMPLE_URL], - 'request with local URL': [new Request('/local.html'), window.origin], - 'request with remote URL': [new Request(EXAMPLE_URL + '/remote.html'), EXAMPLE_URL] - }).forEach(([t, [resource, expectedOrigin]]) => { - describe(`using ${t}`, () => { - it('calls request, passing origin', () => { - const request = sinon.stub(); - const fetch = fetcherFactory(1000, {request}); - fetch(resource); - sinon.assert.calledWith(request, expectedOrigin); - }); - - Object.entries({ - success: 'respond', - error: 'error' - }).forEach(([t, method]) => { - it(`calls done on ${t}, passing origin`, () => { - const done = sinon.stub(); - const fetch = fetcherFactory(1000, {done}); - const req = fetch(resource).catch(() => null).then(() => { - sinon.assert.calledWith(done, expectedOrigin); - }); - server.requests[0][method](); - return req; - }); - }); - }); - }); -}); - -describe('toFetchRequest', () => { - Object.entries({ - 'simple POST': { - url: EXAMPLE_URL, - data: 'data', - expect: { - request: { - url: EXAMPLE_URL + '/', - method: 'POST', - }, - text: 'data', - headers: { - 'content-type': 'text/plain' - } - } - }, - 'POST with headers': { - url: EXAMPLE_URL, - data: '{"json": "body"}', - options: { - contentType: 'application/json', - customHeaders: { - 'x-custom': 'value' - } - }, - expect: { - request: { - url: EXAMPLE_URL + '/', - method: 'POST', - }, - text: '{"json": "body"}', - headers: { - 'content-type': 'application/json', - 'X-Custom': 'value' - } - } - }, - 'simple GET': { - url: EXAMPLE_URL, - data: {p1: 'v1', p2: 'v2'}, - options: { - method: 'GET', - }, - expect: { - request: { - url: EXAMPLE_URL + '/?p1=v1&p2=v2', - method: 'GET' - }, - text: '', - headers: { - 'content-type': 'text/plain' - } - } - }, - 'GET with credentials': { - url: EXAMPLE_URL, - data: null, - options: { - method: 'GET', - withCredentials: true, - }, - expect: { - request: { - url: EXAMPLE_URL + '/', - method: 'GET', - credentials: 'include' - }, - text: '', - headers: { - 'content-type': 'text/plain' - } - } - } - }).forEach(([t, {url, data, options, expect: {request, text, headers}}]) => { - it(`can build ${t}`, () => { - const req = toFetchRequest(url, data, options); - return req.text().then(body => { - Object.entries(request).forEach(([prop, val]) => { - expect(req[prop]).to.eql(val); - }); - const hdr = new Headers(headers); - Array.from(req.headers.entries()).forEach(([name, val]) => { - expect(hdr.get(name)).to.eql(val); - }); - expect(body).to.eql(text); - }); - }); - }); - - describe('browsingTopics', () => { - Object.entries({ - 'browsingTopics = true': [{browsingTopics: true}, true], - 'browsingTopics = false': [{browsingTopics: false}, false], - 'browsingTopics is undef': [{}, false] - }).forEach(([t, [opts, shouldBeSet]]) => { - describe(`when options has ${t}`, () => { - const sandbox = sinon.createSandbox(); - afterEach(() => { - sandbox.restore(); - }); - - it(`should ${!shouldBeSet ? 'not ' : ''}be set when in a secure context`, () => { - sandbox.stub(window, 'isSecureContext').get(() => true); - toFetchRequest(EXAMPLE_URL, null, opts); - sinon.assert.calledWithMatch(dep.makeRequest, sinon.match.any, {browsingTopics: shouldBeSet ? true : undefined}); - }); - it(`should not be set when not in a secure context`, () => { - sandbox.stub(window, 'isSecureContext').get(() => false); - toFetchRequest(EXAMPLE_URL, null, opts); - sinon.assert.calledWithMatch(dep.makeRequest, sinon.match.any, {browsingTopics: undefined}); - }); - }) - }) - }) -}); - -describe('attachCallbacks', () => { - const sampleHeaders = new Headers({ - 'x-1': 'v1', - 'x-2': 'v2' - }); - - function responseFactory(body, props) { - props = Object.assign({headers: sampleHeaders, url: EXAMPLE_URL}, props); - return function () { - return { - response: Object.defineProperties(new Response(body, props), { - url: { - get: () => props.url - } - }), - body: body || '' - }; - }; - } - - function expectNullXHR(response, reason) { - return new Promise((resolve, reject) => { - attachCallbacks(Promise.resolve(response), { - success: () => { - reject(new Error('should not succeed')); - }, - error(statusText, xhr) { - expect(statusText).to.eql(''); - sinon.assert.match(xhr, { - readyState: XMLHttpRequest.DONE, - status: 0, - statusText: '', - responseText: '', - response: '', - responseXML: null, - reason - }); - expect(xhr.getResponseHeader('any')).to.be.null; - resolve(); - } - }); - }); - } - - it('runs error callback on rejections', () => { - const err = new Error(); - return expectNullXHR(Promise.reject(err), err); - }); - - it('sets timedOut = true on fetch timeout', (done) => { - const ctl = new AbortController(); - ctl.abort(); - attachCallbacks(fetch('/', {signal: ctl.signal}), { - error(_, xhr) { - expect(xhr.timedOut).to.be.true; - done(); - } - }); - }) - - Object.entries({ - '2xx response': { - success: true, - makeResponse: responseFactory('body', {status: 200, statusText: 'OK'}) - }, - '2xx response with no body': { - success: true, - makeResponse: responseFactory(null, {status: 204, statusText: 'No content'}) - }, - '2xx response with XML': { - success: true, - xml: true, - makeResponse: responseFactory('', { - status: 200, - statusText: 'OK', - headers: {'content-type': 'application/xml;charset=UTF8'} - }) - }, - '2xx response with HTML': { - success: true, - xml: true, - makeResponse: responseFactory('

', { - status: 200, - statusText: 'OK', - headers: {'content-type': 'text/html;charset=UTF-8'} - }) - }, - '304 response': { - success: true, - makeResponse: responseFactory(null, {status: 304, statusText: 'Moved permanently'}) - }, - '4xx response': { - success: false, - makeResponse: responseFactory('body', {status: 400, statusText: 'Invalid request'}) - }, - '5xx response': { - success: false, - makeResponse: responseFactory('body', {status: 503, statusText: 'Gateway error'}) - }, - '4xx response with XML': { - success: false, - xml: true, - makeResponse: responseFactory('', { - status: 404, - statusText: 'Not found', - headers: { - 'content-type': 'application/xml' - } - }) - } - }).forEach(([t, {success, makeResponse, xml}]) => { - const cbType = success ? 'success' : 'error'; - - describe(`for ${t}`, () => { - let sandbox, response, body; - beforeEach(() => { - sandbox = sinon.sandbox.create(); - sandbox.spy(utils, 'logError'); - ({response, body} = makeResponse()); - }); - - afterEach(() => { - sandbox.restore(); - }) - - function checkXHR(xhr) { - utils.logError.resetHistory(); - const serialized = JSON.parse(JSON.stringify(xhr)) - // serialization of `responseXML` should not generate console messages - sinon.assert.notCalled(utils.logError); - - sinon.assert.match(serialized, { - readyState: XMLHttpRequest.DONE, - status: response.status, - statusText: response.statusText, - responseType: '', - responseURL: response.url, - response: body, - responseText: body, - }); - if (xml) { - expect(xhr.responseXML.querySelectorAll('*').length > 0).to.be.true; - } else { - expect(serialized.responseXML).to.not.exist; - } - Array.from(response.headers.entries()).forEach(([name, value]) => { - expect(xhr.getResponseHeader(name)).to.eql(value); - }); - expect(xhr.getResponseHeader('$$missing-header')).to.be.null; - } - - it(`runs ${cbType} callback`, (done) => { - attachCallbacks(Promise.resolve(response), { - success(payload, xhr) { - expect(success).to.be.true; - expect(payload).to.eql(body); - checkXHR(xhr); - done(); - }, - error(statusText, xhr) { - expect(success).to.be.false; - expect(statusText).to.eql(response.statusText); - checkXHR(xhr); - done(); - } - }); - }); - - it(`runs error callback if body cannot be retrieved`, () => { - const err = new Error(); - response.text = () => Promise.reject(err); - return expectNullXHR(response, err); - }); - - if (success) { - it('accepts a single function as success callback', (done) => { - attachCallbacks(Promise.resolve(response), function (payload, xhr) { - expect(payload).to.eql(body); - checkXHR(xhr); - done(); - }) - }) - } - }); - }); - - describe('callback exceptions', () => { - Object.entries({ - success: responseFactory(null, {status: 204}), - error: responseFactory('', {status: 400}), - }).forEach(([cbType, makeResponse]) => { - it(`do not choke ${cbType} callbacks`, () => { - const {response} = makeResponse(); - return new Promise((resolve) => { - const result = {success: false, error: false}; - attachCallbacks(Promise.resolve(response), { - success() { - result.success = true; - throw new Error(); - }, - error() { - result.error = true; - throw new Error(); - } - }); - setTimeout(() => resolve(result), 20); - }).then(result => { - Object.entries(result).forEach(([typ, ran]) => { - expect(ran).to.be[typ === cbType ? 'true' : 'false'] - }) - }); - }); - }); - }); -}); diff --git a/test/spec/unit/core/bidderFactory_spec.js b/test/spec/unit/core/bidderFactory_spec.js index 16042ca52dd..751c90af50f 100644 --- a/test/spec/unit/core/bidderFactory_spec.js +++ b/test/spec/unit/core/bidderFactory_spec.js @@ -13,8 +13,9 @@ import {stubAuctionIndex} from '../../../helpers/indexStub.js'; import {bidderSettings} from '../../../../src/bidderSettings.js'; import {decorateAdUnitsWithNativeParams} from '../../../../src/native.js'; import * as activityRules from 'src/activities/rules.js'; +import {sandbox} from 'sinon'; import {MODULE_TYPE_BIDDER} from '../../../../src/activities/modules.js'; -import {ACTIVITY_TRANSMIT_TID, ACTIVITY_TRANSMIT_UFPD} from '../../../../src/activities/activities.js'; +import {ACTIVITY_TRANSMIT_TID} from '../../../../src/activities/activities.js'; const CODE = 'sampleBidder'; const MOCK_BIDS_REQUEST = { @@ -38,112 +39,133 @@ const MOCK_BIDS_REQUEST = { ] } +function onTimelyResponseStub() { + +} + before(() => { hook.ready(); }); let wrappedCallback = config.callbackWithBidder(CODE); -describe('bidderFactory', () => { - let onTimelyResponseStub; - beforeEach(() => { - onTimelyResponseStub = sinon.stub(); - }) - describe('bidders created by newBidder', function () { - let spec; - let bidder; - let addBidResponseStub; - let doneStub; +describe('bidders created by newBidder', function () { + let spec; + let bidder; + let addBidResponseStub; + let doneStub; + + beforeEach(function () { + spec = { + code: CODE, + isBidRequestValid: sinon.stub(), + buildRequests: sinon.stub(), + interpretResponse: sinon.stub(), + getUserSyncs: sinon.stub() + }; + + addBidResponseStub = sinon.stub(); + addBidResponseStub.reject = sinon.stub(); + doneStub = sinon.stub(); + }); - beforeEach(function () { - spec = { - code: CODE, - isBidRequestValid: sinon.stub(), - buildRequests: sinon.stub(), - interpretResponse: sinon.stub(), - getUserSyncs: sinon.stub() - }; + describe('when the ajax response is irrelevant', function () { + let sandbox; + let ajaxStub; + let getConfigSpy; + let aliasRegistryStub, aliasRegistry; - addBidResponseStub = sinon.stub(); - addBidResponseStub.reject = sinon.stub(); - doneStub = sinon.stub(); + beforeEach(function () { + sandbox = sinon.sandbox.create(); + sandbox.stub(activityRules, 'isActivityAllowed').callsFake(() => true); + ajaxStub = sandbox.stub(ajax, 'ajax'); + addBidResponseStub.reset(); + getConfigSpy = sandbox.spy(config, 'getConfig'); + doneStub.reset(); + aliasRegistry = {}; + aliasRegistryStub = sandbox.stub(adapterManager, 'aliasRegistry'); + aliasRegistryStub.get(() => aliasRegistry); }); - describe('when the ajax response is irrelevant', function () { - let sandbox; - let ajaxStub; - let getConfigSpy; - let aliasRegistryStub, aliasRegistry; + afterEach(function () { + sandbox.restore(); + }); - beforeEach(function () { - sandbox = sinon.sandbox.create(); - sandbox.stub(activityRules, 'isActivityAllowed').callsFake(() => true); - ajaxStub = sandbox.stub(ajax, 'ajax'); - addBidResponseStub.reset(); - getConfigSpy = sandbox.spy(config, 'getConfig'); - doneStub.reset(); - aliasRegistry = {}; - aliasRegistryStub = sandbox.stub(adapterManager, 'aliasRegistry'); - aliasRegistryStub.get(() => aliasRegistry); + it('should let registerSyncs run with invalid alias and aliasSync enabled', function () { + config.setConfig({ + userSync: { + aliasSyncEnabled: true + } }); + spec.code = 'fakeBidder'; + const bidder = newBidder(spec); + bidder.callBids({ bids: [] }, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); + expect(getConfigSpy.withArgs('userSync.filterSettings').calledOnce).to.equal(true); + }); - afterEach(function () { - sandbox.restore(); + it('should let registerSyncs run with valid alias and aliasSync enabled', function () { + config.setConfig({ + userSync: { + aliasSyncEnabled: true + } }); + spec.code = 'aliasBidder'; + const bidder = newBidder(spec); + bidder.callBids({ bids: [] }, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); + expect(getConfigSpy.withArgs('userSync.filterSettings').calledOnce).to.equal(true); + }); - it('should let registerSyncs run with invalid alias and aliasSync enabled', function () { - config.setConfig({ - userSync: { - aliasSyncEnabled: true - } - }); - spec.code = 'fakeBidder'; - const bidder = newBidder(spec); - bidder.callBids({ bids: [] }, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); - expect(getConfigSpy.withArgs('userSync.filterSettings').calledOnce).to.equal(true); + it('should let registerSyncs run with invalid alias and aliasSync disabled', function () { + config.setConfig({ + userSync: { + aliasSyncEnabled: false + } }); + spec.code = 'fakeBidder'; + const bidder = newBidder(spec); + bidder.callBids({ bids: [] }, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); + expect(getConfigSpy.withArgs('userSync.filterSettings').calledOnce).to.equal(true); + }); - it('should let registerSyncs run with valid alias and aliasSync enabled', function () { - config.setConfig({ - userSync: { - aliasSyncEnabled: true - } - }); - spec.code = 'aliasBidder'; - const bidder = newBidder(spec); - bidder.callBids({ bids: [] }, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); - expect(getConfigSpy.withArgs('userSync.filterSettings').calledOnce).to.equal(true); + it('should not let registerSyncs run with valid alias and aliasSync disabled', function () { + config.setConfig({ + userSync: { + aliasSyncEnabled: false + } }); + spec.code = 'aliasBidder'; + const bidder = newBidder(spec); + aliasRegistry = {[spec.code]: CODE}; + bidder.callBids({ bids: [] }, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); + expect(getConfigSpy.withArgs('userSync.filterSettings').calledOnce).to.equal(false); + }); - it('should let registerSyncs run with invalid alias and aliasSync disabled', function () { - config.setConfig({ - userSync: { - aliasSyncEnabled: false - } - }); - spec.code = 'fakeBidder'; - const bidder = newBidder(spec); - bidder.callBids({ bids: [] }, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); - expect(getConfigSpy.withArgs('userSync.filterSettings').calledOnce).to.equal(true); - }); + describe('transaction IDs', () => { + Object.entries({ + 'be hidden': false, + 'not be hidden': true, + }).forEach(([t, allowed]) => { + const expectation = allowed ? (val) => expect(val).to.exist : (val) => expect(val).to.not.exist; - it('should not let registerSyncs run with valid alias and aliasSync disabled', function () { - config.setConfig({ - userSync: { - aliasSyncEnabled: false - } - }); - spec.code = 'aliasBidder'; - const bidder = newBidder(spec); - aliasRegistry = {[spec.code]: CODE}; - bidder.callBids({ bids: [] }, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); - expect(getConfigSpy.withArgs('userSync.filterSettings').calledOnce).to.equal(false); - }); + function checkBidRequest(br) { + ['auctionId', 'transactionId'].forEach((prop) => expectation(br[prop])); + } - describe('transaction IDs', () => { - beforeEach(() => { - activityRules.isActivityAllowed.reset(); - ajaxStub.callsFake((_, callback) => callback.success(null, {getResponseHeader: sinon.stub()})); + function checkBidderRequest(br) { + expectation(br.auctionId); + br.bids.forEach(checkBidRequest); + } + + it(`should ${t} from the spec logic when the transmitTid activity is${allowed ? '' : ' not'} allowed`, () => { + spec.isBidRequestValid.callsFake(br => { + checkBidRequest(br); + return true; + }); + spec.buildRequests.callsFake((bidReqs, bidderReq) => { + checkBidderRequest(bidderReq); + bidReqs.forEach(checkBidRequest); + return {method: 'POST'}; + }); spec.interpretResponse.callsFake(() => [ { requestId: 'bid', @@ -154,929 +176,599 @@ describe('bidderFactory', () => { currency: 'USD' } ]) - }); - - Object.entries({ - 'be hidden': false, - 'not be hidden': true, - }).forEach(([t, allowed]) => { - const expectation = allowed ? (val) => expect(val).to.exist : (val) => expect(val).to.not.exist; - - function checkBidRequest(br) { - ['auctionId', 'transactionId'].forEach((prop) => expectation(br[prop])); - } + activityRules.isActivityAllowed.reset(); + activityRules.isActivityAllowed.callsFake(() => allowed); - function checkBidderRequest(br) { - expectation(br.auctionId); - br.bids.forEach(checkBidRequest); - } + ajaxStub.callsFake((_, callback) => callback.success(null, {getResponseHeader: sinon.stub()})); - it(`should ${t} from the spec logic when the transmitTid activity is${allowed ? '' : ' not'} allowed`, () => { - spec.isBidRequestValid.callsFake(br => { - checkBidRequest(br); - return true; - }); - spec.buildRequests.callsFake((bidReqs, bidderReq) => { - checkBidderRequest(bidderReq); - bidReqs.forEach(checkBidRequest); - return {method: 'POST'}; - }); - activityRules.isActivityAllowed.callsFake(() => allowed); - - const bidder = newBidder(spec); - - bidder.callBids({ - bidderCode: 'mockBidder', - auctionId: 'aid', - bids: [ - { - adUnitCode: 'mockAU', - bidId: 'bid', - transactionId: 'tid', - auctionId: 'aid' - } - ] - }, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); - - sinon.assert.calledWithMatch(activityRules.isActivityAllowed, ACTIVITY_TRANSMIT_TID, { - componentType: MODULE_TYPE_BIDDER, - componentName: 'mockBidder' - }); - sinon.assert.calledWithMatch(addBidResponseStub, sinon.match.any, { - transactionId: 'tid', - auctionId: 'aid' - }) - }); - }); + const bidder = newBidder(spec); - it('should not be hidden from request methods', (done) => { - const bidderRequest = { + bidder.callBids({ bidderCode: 'mockBidder', auctionId: 'aid', - getAID() { return this.auctionId }, bids: [ { adUnitCode: 'mockAU', bidId: 'bid', transactionId: 'tid', - auctionId: 'aid', - getTIDs() { - return [this.auctionId, this.transactionId] - } + auctionId: 'aid' } ] - }; - activityRules.isActivityAllowed.callsFake(() => false); - spec.isBidRequestValid.returns(true); - spec.buildRequests.callsFake((reqs, bidderReq) => { - expect(bidderReq.getAID()).to.eql('aid'); - expect(reqs[0].getTIDs()).to.eql(['aid', 'tid']); - done(); + }, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); + + sinon.assert.calledWithMatch(activityRules.isActivityAllowed, ACTIVITY_TRANSMIT_TID, { + componentType: MODULE_TYPE_BIDDER, + componentName: 'mockBidder' }); - newBidder(spec).callBids(bidderRequest, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); - }) + sinon.assert.calledWithMatch(addBidResponseStub, sinon.match.any, { + transactionId: 'tid', + auctionId: 'aid' + }) + }); }); + }); - it('should handle bad bid requests gracefully', function () { - const bidder = newBidder(spec); - - spec.getUserSyncs.returns([]); + it('should handle bad bid requests gracefully', function () { + const bidder = newBidder(spec); - bidder.callBids({}); - bidder.callBids({ bids: 'nothing useful' }, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); + spec.getUserSyncs.returns([]); - expect(ajaxStub.called).to.equal(false); - expect(spec.isBidRequestValid.called).to.equal(false); - expect(spec.buildRequests.called).to.equal(false); - expect(spec.interpretResponse.called).to.equal(false); - }); + bidder.callBids({}); + bidder.callBids({ bids: 'nothing useful' }, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); - it('should call buildRequests(bidRequest) the params are valid', function () { - const bidder = newBidder(spec); + expect(ajaxStub.called).to.equal(false); + expect(spec.isBidRequestValid.called).to.equal(false); + expect(spec.buildRequests.called).to.equal(false); + expect(spec.interpretResponse.called).to.equal(false); + }); - spec.isBidRequestValid.returns(true); - spec.buildRequests.returns([]); + it('should call buildRequests(bidRequest) the params are valid', function () { + const bidder = newBidder(spec); - bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); + spec.isBidRequestValid.returns(true); + spec.buildRequests.returns([]); - expect(ajaxStub.called).to.equal(false); - expect(spec.isBidRequestValid.calledTwice).to.equal(true); - expect(spec.buildRequests.calledOnce).to.equal(true); - expect(spec.buildRequests.firstCall.args[0]).to.deep.equal(MOCK_BIDS_REQUEST.bids); - }); + bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); - it('should not call buildRequests the params are invalid', function () { - const bidder = newBidder(spec); + expect(ajaxStub.called).to.equal(false); + expect(spec.isBidRequestValid.calledTwice).to.equal(true); + expect(spec.buildRequests.calledOnce).to.equal(true); + expect(spec.buildRequests.firstCall.args[0]).to.deep.equal(MOCK_BIDS_REQUEST.bids); + }); - spec.isBidRequestValid.returns(false); - spec.buildRequests.returns([]); + it('should not call buildRequests the params are invalid', function () { + const bidder = newBidder(spec); - bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); + spec.isBidRequestValid.returns(false); + spec.buildRequests.returns([]); - expect(ajaxStub.called).to.equal(false); - expect(spec.isBidRequestValid.calledTwice).to.equal(true); - expect(spec.buildRequests.called).to.equal(false); - }); + bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); - it('should filter out invalid bids before calling buildRequests', function () { - const bidder = newBidder(spec); - - spec.isBidRequestValid.onFirstCall().returns(true); - spec.isBidRequestValid.onSecondCall().returns(false); - spec.buildRequests.returns([]); + expect(ajaxStub.called).to.equal(false); + expect(spec.isBidRequestValid.calledTwice).to.equal(true); + expect(spec.buildRequests.called).to.equal(false); + }); - bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); + it('should filter out invalid bids before calling buildRequests', function () { + const bidder = newBidder(spec); - expect(ajaxStub.called).to.equal(false); - expect(spec.isBidRequestValid.calledTwice).to.equal(true); - expect(spec.buildRequests.calledOnce).to.equal(true); - expect(spec.buildRequests.firstCall.args[0]).to.deep.equal([MOCK_BIDS_REQUEST.bids[0]]); - }); + spec.isBidRequestValid.onFirstCall().returns(true); + spec.isBidRequestValid.onSecondCall().returns(false); + spec.buildRequests.returns([]); - it('should make no server requests if the spec doesn\'t return any', function () { - const bidder = newBidder(spec); + bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); - spec.isBidRequestValid.returns(true); - spec.buildRequests.returns([]); + expect(ajaxStub.called).to.equal(false); + expect(spec.isBidRequestValid.calledTwice).to.equal(true); + expect(spec.buildRequests.calledOnce).to.equal(true); + expect(spec.buildRequests.firstCall.args[0]).to.deep.equal([MOCK_BIDS_REQUEST.bids[0]]); + }); - bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); + it('should make no server requests if the spec doesn\'t return any', function () { + const bidder = newBidder(spec); - expect(ajaxStub.called).to.equal(false); - }); + spec.isBidRequestValid.returns(true); + spec.buildRequests.returns([]); - it('should make the appropriate POST request', function () { - const bidder = newBidder(spec); - const url = 'test.url.com'; - const data = { arg: 2 }; - spec.isBidRequestValid.returns(true); - spec.buildRequests.returns({ - method: 'POST', - url: url, - data: data - }); + bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); - bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); + expect(ajaxStub.called).to.equal(false); + }); - expect(ajaxStub.calledOnce).to.equal(true); - expect(ajaxStub.firstCall.args[0]).to.equal(url); - expect(ajaxStub.firstCall.args[2]).to.equal(JSON.stringify(data)); - sinon.assert.match(ajaxStub.firstCall.args[3], { - method: 'POST', - contentType: 'text/plain', - withCredentials: true - }); + it('should make the appropriate POST request', function () { + const bidder = newBidder(spec); + const url = 'test.url.com'; + const data = { arg: 2 }; + spec.isBidRequestValid.returns(true); + spec.buildRequests.returns({ + method: 'POST', + url: url, + data: data }); - it('should make the appropriate POST request when options are passed', function () { - const bidder = newBidder(spec); - const url = 'test.url.com'; - const data = { arg: 2 }; - const options = { contentType: 'application/json' }; - spec.isBidRequestValid.returns(true); - spec.buildRequests.returns({ - method: 'POST', - url: url, - data: data, - options: options - }); - - bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); + bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); - expect(ajaxStub.calledOnce).to.equal(true); - expect(ajaxStub.firstCall.args[0]).to.equal(url); - expect(ajaxStub.firstCall.args[2]).to.equal(JSON.stringify(data)); - sinon.assert.match(ajaxStub.firstCall.args[3], { - method: 'POST', - contentType: 'application/json', - withCredentials: true - }) + expect(ajaxStub.calledOnce).to.equal(true); + expect(ajaxStub.firstCall.args[0]).to.equal(url); + expect(ajaxStub.firstCall.args[2]).to.equal(JSON.stringify(data)); + expect(ajaxStub.firstCall.args[3]).to.deep.equal({ + method: 'POST', + contentType: 'text/plain', + withCredentials: true }); + }); - it('should make the appropriate GET request', function () { - const bidder = newBidder(spec); - const url = 'test.url.com'; - const data = { arg: 2 }; - spec.isBidRequestValid.returns(true); - spec.buildRequests.returns({ - method: 'GET', - url: url, - data: data - }); - - bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); - - expect(ajaxStub.calledOnce).to.equal(true); - expect(ajaxStub.firstCall.args[0]).to.equal(`${url}?arg=2`); - expect(ajaxStub.firstCall.args[2]).to.be.undefined; - sinon.assert.match(ajaxStub.firstCall.args[3], { - method: 'GET', - withCredentials: true - }) + it('should make the appropriate POST request when options are passed', function () { + const bidder = newBidder(spec); + const url = 'test.url.com'; + const data = { arg: 2 }; + const options = { contentType: 'application/json' }; + spec.isBidRequestValid.returns(true); + spec.buildRequests.returns({ + method: 'POST', + url: url, + data: data, + options: options }); - it('should make the appropriate GET request when options are passed', function () { - const bidder = newBidder(spec); - const url = 'test.url.com'; - const data = { arg: 2 }; - const opt = { withCredentials: false } - spec.isBidRequestValid.returns(true); - spec.buildRequests.returns({ - method: 'GET', - url: url, - data: data, - options: opt - }); + bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); - bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); + expect(ajaxStub.calledOnce).to.equal(true); + expect(ajaxStub.firstCall.args[0]).to.equal(url); + expect(ajaxStub.firstCall.args[2]).to.equal(JSON.stringify(data)); + expect(ajaxStub.firstCall.args[3]).to.deep.equal({ + method: 'POST', + contentType: 'application/json', + withCredentials: true + }); + }); - expect(ajaxStub.calledOnce).to.equal(true); - expect(ajaxStub.firstCall.args[0]).to.equal(`${url}?arg=2`); - expect(ajaxStub.firstCall.args[2]).to.be.undefined; - sinon.assert.match(ajaxStub.firstCall.args[3], { - method: 'GET', - withCredentials: false - }) + it('should make the appropriate GET request', function () { + const bidder = newBidder(spec); + const url = 'test.url.com'; + const data = { arg: 2 }; + spec.isBidRequestValid.returns(true); + spec.buildRequests.returns({ + method: 'GET', + url: url, + data: data }); - it('should make multiple calls if the spec returns them', function () { - const bidder = newBidder(spec); - const url = 'test.url.com'; - const data = { arg: 2 }; - spec.isBidRequestValid.returns(true); - spec.buildRequests.returns([ - { - method: 'POST', - url: url, - data: data - }, - { - method: 'GET', - url: url, - data: data - } - ]); + bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); - bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); + expect(ajaxStub.calledOnce).to.equal(true); + expect(ajaxStub.firstCall.args[0]).to.equal(`${url}?arg=2`); + expect(ajaxStub.firstCall.args[2]).to.be.undefined; + expect(ajaxStub.firstCall.args[3]).to.deep.equal({ + method: 'GET', + withCredentials: true + }); + }); - expect(ajaxStub.calledTwice).to.equal(true); + it('should make the appropriate GET request when options are passed', function () { + const bidder = newBidder(spec); + const url = 'test.url.com'; + const data = { arg: 2 }; + const opt = { withCredentials: false } + spec.isBidRequestValid.returns(true); + spec.buildRequests.returns({ + method: 'GET', + url: url, + data: data, + options: opt }); - describe('browsingTopics ajax option', () => { - let transmitUfpdAllowed, bidder, origBS; - before(() => { - origBS = window.$$PREBID_GLOBAL$$.bidderSettings; - }) + bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); - after(() => { - window.$$PREBID_GLOBAL$$.bidderSettings = origBS; - }); + expect(ajaxStub.calledOnce).to.equal(true); + expect(ajaxStub.firstCall.args[0]).to.equal(`${url}?arg=2`); + expect(ajaxStub.firstCall.args[2]).to.be.undefined; + expect(ajaxStub.firstCall.args[3]).to.deep.equal({ + method: 'GET', + withCredentials: false + }); + }); - beforeEach(() => { - activityRules.isActivityAllowed.reset(); - activityRules.isActivityAllowed.callsFake((activity) => activity === ACTIVITY_TRANSMIT_UFPD ? transmitUfpdAllowed : true); - bidder = newBidder(spec); - spec.isBidRequestValid.returns(true); - }); + it('should make multiple calls if the spec returns them', function () { + const bidder = newBidder(spec); + const url = 'test.url.com'; + const data = { arg: 2 }; + spec.isBidRequestValid.returns(true); + spec.buildRequests.returns([ + { + method: 'POST', + url: url, + data: data + }, + { + method: 'GET', + url: url, + data: data + } + ]); - it(`should be set to false when adapter sets browsingTopics = false`, () => { - transmitUfpdAllowed = true; - spec.buildRequests.returns([ - { - method: 'GET', - url: 'url', - options: { - browsingTopics: false - } - } - ]); - bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); - sinon.assert.calledWith(ajaxStub, 'url', sinon.match.any, sinon.match.any, sinon.match({ - browsingTopics: false - })); - }); + bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); - Object.entries({ - 'omitted': [undefined, true], - 'enabled': [true, true], - 'disabled': [false, false] - }).forEach(([t, [topicsHeader, enabled]]) => { - describe(`when bidderSettings.topicsHeader is ${t}`, () => { - beforeEach(() => { - window.$$PREBID_GLOBAL$$.bidderSettings = { - [CODE]: { - topicsHeader: topicsHeader - } - } - }); - - afterEach(() => { - delete window.$$PREBID_GLOBAL$$.bidderSettings[CODE]; - }); - - Object.entries({ - 'allowed': true, - 'not allowed': false - }).forEach(([t, allow]) => { - const shouldBeSet = allow && enabled; - - it(`should be set to ${shouldBeSet} when transmitUfpd is ${t}`, () => { - transmitUfpdAllowed = allow; - spec.buildRequests.returns([ - { - method: 'GET', - url: '1', - }, - { - method: 'POST', - url: '2', - data: {} - }, - { - method: 'GET', - url: '3', - options: { - browsingTopics: true - } - }, - { - method: 'POST', - url: '4', - data: {}, - options: { - browsingTopics: true - } - } - ]); - bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); - ['1', '2', '3', '4'].forEach(url => { - sinon.assert.calledWith( - ajaxStub, - url, - sinon.match.any, - sinon.match.any, - sinon.match({browsingTopics: shouldBeSet}) - ); - }); - }); - }); - }) - }) - }); + expect(ajaxStub.calledTwice).to.equal(true); + }); - it('should not add bids for each placement code if no requests are given', function () { - const bidder = newBidder(spec); + it('should not add bids for each placement code if no requests are given', function () { + const bidder = newBidder(spec); - spec.isBidRequestValid.returns(true); - spec.buildRequests.returns([]); - spec.interpretResponse.returns([]); - spec.getUserSyncs.returns([]); + spec.isBidRequestValid.returns(true); + spec.buildRequests.returns([]); + spec.interpretResponse.returns([]); + spec.getUserSyncs.returns([]); - bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); + bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); - expect(addBidResponseStub.callCount).to.equal(0); - }); + expect(addBidResponseStub.callCount).to.equal(0); + }); - it('should emit BEFORE_BIDDER_HTTP events before network requests', function () { - const bidder = newBidder(spec); - const req = { - method: 'POST', - url: 'test.url.com', - data: { arg: 2 } - }; + it('should emit BEFORE_BIDDER_HTTP events before network requests', function () { + const bidder = newBidder(spec); + const req = { + method: 'POST', + url: 'test.url.com', + data: { arg: 2 } + }; - spec.isBidRequestValid.returns(true); - spec.buildRequests.returns([req, req]); + spec.isBidRequestValid.returns(true); + spec.buildRequests.returns([req, req]); - const eventEmitterSpy = sinon.spy(events, 'emit'); - bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); + const eventEmitterSpy = sinon.spy(events, 'emit'); + bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); - expect(ajaxStub.calledTwice).to.equal(true); - expect(eventEmitterSpy.getCalls() - .filter(call => call.args[0] === CONSTANTS.EVENTS.BEFORE_BIDDER_HTTP) - ).to.length(2); + expect(ajaxStub.calledTwice).to.equal(true); + expect(eventEmitterSpy.getCalls() + .filter(call => call.args[0] === CONSTANTS.EVENTS.BEFORE_BIDDER_HTTP) + ).to.length(2); - eventEmitterSpy.restore(); - }); + eventEmitterSpy.restore(); }); + }); - describe('when the ajax call succeeds', function () { - let ajaxStub; - let userSyncStub; - let logErrorSpy; - - beforeEach(function () { - ajaxStub = sinon.stub(ajax, 'ajax').callsFake(function(url, callbacks) { - const fakeResponse = sinon.stub(); - fakeResponse.returns('headerContent'); - callbacks.success('response body', { getResponseHeader: fakeResponse }); - }); - addBidResponseStub.reset(); - doneStub.resetBehavior(); - userSyncStub = sinon.stub(userSync, 'registerSync') - logErrorSpy = sinon.spy(utils, 'logError'); - }); + describe('when the ajax call succeeds', function () { + let ajaxStub; + let userSyncStub; + let logErrorSpy; - afterEach(function () { - ajaxStub.restore(); - userSyncStub.restore(); - utils.logError.restore(); + beforeEach(function () { + ajaxStub = sinon.stub(ajax, 'ajax').callsFake(function(url, callbacks) { + const fakeResponse = sinon.stub(); + fakeResponse.returns('headerContent'); + callbacks.success('response body', { getResponseHeader: fakeResponse }); }); + addBidResponseStub.reset(); + doneStub.resetBehavior(); + userSyncStub = sinon.stub(userSync, 'registerSync') + logErrorSpy = sinon.spy(utils, 'logError'); + }); - it('should call onTimelyResponse', () => { - const bidder = newBidder(spec); - spec.isBidRequestValid.returns(true); - spec.buildRequests.returns({method: 'POST', url: 'test', data: {}}); - bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); - sinon.assert.called(onTimelyResponseStub); - }) - - it('should call spec.interpretResponse() with the response content', function () { - const bidder = newBidder(spec); - - spec.isBidRequestValid.returns(true); - spec.buildRequests.returns({ - method: 'POST', - url: 'test.url.com', - data: {} - }); - spec.getUserSyncs.returns([]); + afterEach(function () { + ajaxStub.restore(); + userSyncStub.restore(); + utils.logError.restore(); + }); - bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); + it('should call spec.interpretResponse() with the response content', function () { + const bidder = newBidder(spec); - expect(spec.interpretResponse.calledOnce).to.equal(true); - const response = spec.interpretResponse.firstCall.args[0] - expect(response.body).to.equal('response body') - expect(response.headers.get('some-header')).to.equal('headerContent'); - expect(spec.interpretResponse.firstCall.args[1]).to.deep.equal({ - method: 'POST', - url: 'test.url.com', - data: {} - }); - expect(doneStub.calledOnce).to.equal(true); + spec.isBidRequestValid.returns(true); + spec.buildRequests.returns({ + method: 'POST', + url: 'test.url.com', + data: {} }); + spec.getUserSyncs.returns([]); - it('should call spec.interpretResponse() once for each request made', function () { - const bidder = newBidder(spec); - - spec.isBidRequestValid.returns(true); - spec.buildRequests.returns([ - { - method: 'POST', - url: 'test.url.com', - data: {} - }, - { - method: 'POST', - url: 'test.url.com', - data: {} - }, - ]); - spec.getUserSyncs.returns([]); - - bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); + bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); - expect(spec.interpretResponse.calledTwice).to.equal(true); - expect(doneStub.calledOnce).to.equal(true); + expect(spec.interpretResponse.calledOnce).to.equal(true); + const response = spec.interpretResponse.firstCall.args[0] + expect(response.body).to.equal('response body') + expect(response.headers.get('some-header')).to.equal('headerContent'); + expect(spec.interpretResponse.firstCall.args[1]).to.deep.equal({ + method: 'POST', + url: 'test.url.com', + data: {} }); + expect(doneStub.calledOnce).to.equal(true); + }); - it('should only add bids for valid adUnit code into the auction, even if the bidder doesn\'t bid on all of them', function () { - const bidder = newBidder(spec); + it('should call spec.interpretResponse() once for each request made', function () { + const bidder = newBidder(spec); - const bid = { - creativeId: 'creative-id', - requestId: '1', - ad: 'ad-url.com', - cpm: 0.5, - height: 200, - width: 300, - adUnitCode: 'mock/placement', - currency: 'USD', - netRevenue: true, - ttl: 300, - bidderCode: 'sampleBidder', - sampleBidder: {advertiserId: '12345', networkId: '111222'} - }; - const bidderRequest = Object.assign({}, MOCK_BIDS_REQUEST); - bidderRequest.bids[0].bidder = 'sampleBidder'; - spec.isBidRequestValid.returns(true); - spec.buildRequests.returns({ + spec.isBidRequestValid.returns(true); + spec.buildRequests.returns([ + { method: 'POST', url: 'test.url.com', data: {} - }); - spec.getUserSyncs.returns([]); - - spec.interpretResponse.returns(bid); - - bidder.callBids(bidderRequest, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); - - expect(addBidResponseStub.calledOnce).to.equal(true); - expect(addBidResponseStub.firstCall.args[0]).to.equal('mock/placement'); - let bidObject = addBidResponseStub.firstCall.args[1]; - // checking the fields added by our code - expect(bidObject.originalCpm).to.equal(bid.cpm); - expect(bidObject.originalCurrency).to.equal(bid.currency); - expect(doneStub.calledOnce).to.equal(true); - expect(logErrorSpy.callCount).to.equal(0); - expect(bidObject.meta).to.exist; - expect(bidObject.meta).to.deep.equal({advertiserId: '12345', networkId: '111222'}); - }); - - it('should call spec.getUserSyncs() with the response', function () { - const bidder = newBidder(spec); - - spec.isBidRequestValid.returns(true); - spec.buildRequests.returns({ + }, + { method: 'POST', url: 'test.url.com', data: {} - }); - spec.getUserSyncs.returns([]); - - bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); - - expect(spec.getUserSyncs.calledOnce).to.equal(true); - expect(spec.getUserSyncs.firstCall.args[1].length).to.equal(1); - expect(spec.getUserSyncs.firstCall.args[1][0].body).to.equal('response body'); - expect(spec.getUserSyncs.firstCall.args[1][0].headers).to.have.property('get'); - expect(spec.getUserSyncs.firstCall.args[1][0].headers.get).to.be.a('function'); - }); + }, + ]); + spec.getUserSyncs.returns([]); - it('should register usersync pixels', function () { - const bidder = newBidder(spec); + bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); - spec.isBidRequestValid.returns(false); - spec.buildRequests.returns([]); - spec.getUserSyncs.returns([{ - type: 'iframe', - url: 'usersync.com' - }]); + expect(spec.interpretResponse.calledTwice).to.equal(true); + expect(doneStub.calledOnce).to.equal(true); + }); - bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); + it('should only add bids for valid adUnit code into the auction, even if the bidder doesn\'t bid on all of them', function () { + const bidder = newBidder(spec); - expect(userSyncStub.called).to.equal(true); - expect(userSyncStub.firstCall.args[0]).to.equal('iframe'); - expect(userSyncStub.firstCall.args[1]).to.equal(spec.code); - expect(userSyncStub.firstCall.args[2]).to.equal('usersync.com'); + const bid = { + creativeId: 'creative-id', + requestId: '1', + ad: 'ad-url.com', + cpm: 0.5, + height: 200, + width: 300, + adUnitCode: 'mock/placement', + currency: 'USD', + netRevenue: true, + ttl: 300, + bidderCode: 'sampleBidder', + sampleBidder: {advertiserId: '12345', networkId: '111222'} + }; + const bidderRequest = Object.assign({}, MOCK_BIDS_REQUEST); + bidderRequest.bids[0].bidder = 'sampleBidder'; + spec.isBidRequestValid.returns(true); + spec.buildRequests.returns({ + method: 'POST', + url: 'test.url.com', + data: {} }); + spec.getUserSyncs.returns([]); - it('should logError and reject bid when required bid response params are missing', function () { - const bidder = newBidder(spec); + spec.interpretResponse.returns(bid); - const bid = { - requestId: '1', - ad: 'ad-url.com', - cpm: 0.5, - height: 200, - width: 300, - placementCode: 'mock/placement' - }; - spec.isBidRequestValid.returns(true); - spec.buildRequests.returns({ - method: 'POST', - url: 'test.url.com', - data: {} - }); - spec.getUserSyncs.returns([]); + bidder.callBids(bidderRequest, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); - spec.interpretResponse.returns(bid); + expect(addBidResponseStub.calledOnce).to.equal(true); + expect(addBidResponseStub.firstCall.args[0]).to.equal('mock/placement'); + let bidObject = addBidResponseStub.firstCall.args[1]; + // checking the fields added by our code + expect(bidObject.originalCpm).to.equal(bid.cpm); + expect(bidObject.originalCurrency).to.equal(bid.currency); + expect(doneStub.calledOnce).to.equal(true); + expect(logErrorSpy.callCount).to.equal(0); + expect(bidObject.meta).to.exist; + expect(bidObject.meta).to.deep.equal({advertiserId: '12345', networkId: '111222'}); + }); - bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); + it('should call spec.getUserSyncs() with the response', function () { + const bidder = newBidder(spec); - expect(logErrorSpy.calledOnce).to.equal(true); - expect(addBidResponseStub.reject.calledOnce).to.be.true; + spec.isBidRequestValid.returns(true); + spec.buildRequests.returns({ + method: 'POST', + url: 'test.url.com', + data: {} }); + spec.getUserSyncs.returns([]); - it('should logError and reject bid when required response params are undefined', function () { - const bidder = newBidder(spec); + bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); - const bid = { - 'ad': 'creative', - 'cpm': '1.99', - 'width': 300, - 'height': 250, - 'requestId': '1', - 'creativeId': 'some-id', - 'currency': undefined, - 'netRevenue': true, - 'ttl': 360 - }; - - spec.isBidRequestValid.returns(true); - spec.buildRequests.returns({ - method: 'POST', - url: 'test.url.com', - data: {} - }); - spec.getUserSyncs.returns([]); + expect(spec.getUserSyncs.calledOnce).to.equal(true); + expect(spec.getUserSyncs.firstCall.args[1].length).to.equal(1); + expect(spec.getUserSyncs.firstCall.args[1][0].body).to.equal('response body'); + expect(spec.getUserSyncs.firstCall.args[1][0].headers).to.have.property('get'); + expect(spec.getUserSyncs.firstCall.args[1][0].headers.get).to.be.a('function'); + }); - spec.interpretResponse.returns(bid); + it('should register usersync pixels', function () { + const bidder = newBidder(spec); - bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); + spec.isBidRequestValid.returns(false); + spec.buildRequests.returns([]); + spec.getUserSyncs.returns([{ + type: 'iframe', + url: 'usersync.com' + }]); - expect(logErrorSpy.calledOnce).to.equal(true); - expect(addBidResponseStub.reject.calledOnce).to.be.true; - }); + bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); - it('should require requestId from interpretResponse', () => { - const bidder = newBidder(spec); - const bid = { - 'ad': 'creative', - 'cpm': '1.99', - 'creativeId': 'some-id', - 'currency': 'USD', - 'netRevenue': true, - 'ttl': 360 - }; - spec.isBidRequestValid.returns(true); - spec.buildRequests.returns({ - method: 'POST', - url: 'test.url.com', - data: {} - }); - spec.getUserSyncs.returns([]); - spec.interpretResponse.returns(bid); + expect(userSyncStub.called).to.equal(true); + expect(userSyncStub.firstCall.args[0]).to.equal('iframe'); + expect(userSyncStub.firstCall.args[1]).to.equal(spec.code); + expect(userSyncStub.firstCall.args[2]).to.equal('usersync.com'); + }); - bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); + it('should logError and reject bid when required bid response params are missing', function () { + const bidder = newBidder(spec); - expect(addBidResponseStub.called).to.be.false; - expect(addBidResponseStub.reject.calledOnce).to.be.true; + const bid = { + requestId: '1', + ad: 'ad-url.com', + cpm: 0.5, + height: 200, + width: 300, + placementCode: 'mock/placement' + }; + spec.isBidRequestValid.returns(true); + spec.buildRequests.returns({ + method: 'POST', + url: 'test.url.com', + data: {} }); - }); + spec.getUserSyncs.returns([]); - describe('when the ajax call fails', function () { - let ajaxStub; - let callBidderErrorStub; - let eventEmitterStub; - let xhrErrorMock; + spec.interpretResponse.returns(bid); - beforeEach(function () { - xhrErrorMock = { - status: 500, - statusText: 'Internal Server Error' - }; - ajaxStub = sinon.stub(ajax, 'ajax').callsFake(function(url, callbacks) { - callbacks.error('ajax call failed.', xhrErrorMock); - }); - callBidderErrorStub = sinon.stub(adapterManager, 'callBidderError'); - eventEmitterStub = sinon.stub(events, 'emit'); - addBidResponseStub.reset(); - doneStub.reset(); - }); + bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); - afterEach(function () { - ajaxStub.restore(); - callBidderErrorStub.restore(); - eventEmitterStub.restore(); - }); + expect(logErrorSpy.calledOnce).to.equal(true); + expect(addBidResponseStub.reject.calledOnce).to.be.true; + }); - Object.entries({ - 'timeouts': true, - 'other errors': false - }).forEach(([t, timedOut]) => { - it(`should ${timedOut ? 'NOT ' : ''}call onTimelyResponse on ${t}`, () => { - Object.assign(xhrErrorMock, {timedOut}); - const bidder = newBidder(spec); - spec.isBidRequestValid.returns(true); - spec.buildRequests.returns({method: 'POST', url: 'test', data: {}}); - bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); - sinon.assert[timedOut ? 'notCalled' : 'called'](onTimelyResponseStub); - }) - }) + it('should logError and reject bid when required response params are undefined', function () { + const bidder = newBidder(spec); - it('should not spec.interpretResponse()', function () { - const bidder = newBidder(spec); + const bid = { + 'ad': 'creative', + 'cpm': '1.99', + 'width': 300, + 'height': 250, + 'requestId': '1', + 'creativeId': 'some-id', + 'currency': undefined, + 'netRevenue': true, + 'ttl': 360 + }; - spec.isBidRequestValid.returns(true); - spec.buildRequests.returns({ - method: 'POST', - url: 'test.url.com', - data: {} - }); - spec.getUserSyncs.returns([]); - - bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); - - expect(spec.interpretResponse.called).to.equal(false); - expect(doneStub.calledOnce).to.equal(true); - expect(callBidderErrorStub.calledOnce).to.equal(true); - expect(callBidderErrorStub.firstCall.args[0]).to.equal(CODE); - expect(callBidderErrorStub.firstCall.args[1]).to.equal(xhrErrorMock); - expect(callBidderErrorStub.firstCall.args[2]).to.equal(MOCK_BIDS_REQUEST); - sinon.assert.calledWith(eventEmitterStub, CONSTANTS.EVENTS.BIDDER_ERROR, { - error: xhrErrorMock, - bidderRequest: MOCK_BIDS_REQUEST - }); + spec.isBidRequestValid.returns(true); + spec.buildRequests.returns({ + method: 'POST', + url: 'test.url.com', + data: {} }); + spec.getUserSyncs.returns([]); - it('should not add bids for each adunit code into the auction', function () { - const bidder = newBidder(spec); + spec.interpretResponse.returns(bid); - spec.isBidRequestValid.returns(true); - spec.buildRequests.returns({ - method: 'POST', - url: 'test.url.com', - data: {} - }); - spec.interpretResponse.returns([]); - spec.getUserSyncs.returns([]); - - bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); - - expect(addBidResponseStub.callCount).to.equal(0); - expect(doneStub.calledOnce).to.equal(true); - expect(callBidderErrorStub.calledOnce).to.equal(true); - expect(callBidderErrorStub.firstCall.args[0]).to.equal(CODE); - expect(callBidderErrorStub.firstCall.args[1]).to.equal(xhrErrorMock); - expect(callBidderErrorStub.firstCall.args[2]).to.equal(MOCK_BIDS_REQUEST); - sinon.assert.calledWith(eventEmitterStub, CONSTANTS.EVENTS.BIDDER_ERROR, { - error: xhrErrorMock, - bidderRequest: MOCK_BIDS_REQUEST - }); - }); + bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); - it('should call spec.getUserSyncs() with no responses', function () { - const bidder = newBidder(spec); + expect(logErrorSpy.calledOnce).to.equal(true); + expect(addBidResponseStub.reject.calledOnce).to.be.true; + }); - spec.isBidRequestValid.returns(true); - spec.buildRequests.returns({ - method: 'POST', - url: 'test.url.com', - data: {} - }); - spec.getUserSyncs.returns([]); - - bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); - - expect(spec.getUserSyncs.calledOnce).to.equal(true); - expect(spec.getUserSyncs.firstCall.args[1]).to.deep.equal([]); - expect(doneStub.calledOnce).to.equal(true); - expect(callBidderErrorStub.calledOnce).to.equal(true); - expect(callBidderErrorStub.firstCall.args[0]).to.equal(CODE); - expect(callBidderErrorStub.firstCall.args[1]).to.equal(xhrErrorMock); - expect(callBidderErrorStub.firstCall.args[2]).to.equal(MOCK_BIDS_REQUEST); - sinon.assert.calledWith(eventEmitterStub, CONSTANTS.EVENTS.BIDDER_ERROR, { - error: xhrErrorMock, - bidderRequest: MOCK_BIDS_REQUEST - }); + it('should require requestId from interpretResponse', () => { + const bidder = newBidder(spec); + const bid = { + 'ad': 'creative', + 'cpm': '1.99', + 'creativeId': 'some-id', + 'currency': 'USD', + 'netRevenue': true, + 'ttl': 360 + }; + spec.isBidRequestValid.returns(true); + spec.buildRequests.returns({ + method: 'POST', + url: 'test.url.com', + data: {} }); + spec.getUserSyncs.returns([]); + spec.interpretResponse.returns(bid); - it('should call spec.getUserSyncs() with no responses', function () { - const bidder = newBidder(spec); + bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); - spec.isBidRequestValid.returns(true); - spec.buildRequests.returns({ - method: 'POST', - url: 'test.url.com', - data: {} - }); - spec.getUserSyncs.returns([]); - - bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); - - expect(spec.getUserSyncs.calledOnce).to.equal(true); - expect(spec.getUserSyncs.firstCall.args[1]).to.deep.equal([]); - expect(doneStub.calledOnce).to.equal(true); - expect(callBidderErrorStub.calledOnce).to.equal(true); - expect(callBidderErrorStub.firstCall.args[0]).to.equal(CODE); - expect(callBidderErrorStub.firstCall.args[1]).to.equal(xhrErrorMock); - expect(callBidderErrorStub.firstCall.args[2]).to.equal(MOCK_BIDS_REQUEST); - sinon.assert.calledWith(eventEmitterStub, CONSTANTS.EVENTS.BIDDER_ERROR, { - error: xhrErrorMock, - bidderRequest: MOCK_BIDS_REQUEST - }); - }); + expect(addBidResponseStub.called).to.be.false; + expect(addBidResponseStub.reject.calledOnce).to.be.true; }); }); - describe('registerBidder', function () { - let registerBidAdapterStub; - let aliasBidAdapterStub; + describe('when the ajax call fails', function () { + let ajaxStub; + let callBidderErrorStub; + let eventEmitterStub; + let xhrErrorMock = { + status: 500, + statusText: 'Internal Server Error' + }; beforeEach(function () { - registerBidAdapterStub = sinon.stub(adapterManager, 'registerBidAdapter'); - aliasBidAdapterStub = sinon.stub(adapterManager, 'aliasBidAdapter'); + ajaxStub = sinon.stub(ajax, 'ajax').callsFake(function(url, callbacks) { + callbacks.error('ajax call failed.', xhrErrorMock); + }); + callBidderErrorStub = sinon.stub(adapterManager, 'callBidderError'); + eventEmitterStub = sinon.stub(events, 'emit'); + addBidResponseStub.reset(); + doneStub.reset(); }); afterEach(function () { - registerBidAdapterStub.restore(); - aliasBidAdapterStub.restore(); + ajaxStub.restore(); + callBidderErrorStub.restore(); + eventEmitterStub.restore(); }); - function newEmptySpec() { - return { - code: CODE, - isBidRequestValid: function() { }, - buildRequests: function() { }, - interpretResponse: function() { }, - }; - } - - it('should register a bidder with the adapterManager', function () { - registerBidder(newEmptySpec()); - expect(registerBidAdapterStub.calledOnce).to.equal(true); - expect(registerBidAdapterStub.firstCall.args[0]).to.have.property('callBids'); - expect(registerBidAdapterStub.firstCall.args[0].callBids).to.be.a('function'); - - expect(registerBidAdapterStub.firstCall.args[1]).to.equal(CODE); - expect(registerBidAdapterStub.firstCall.args[2]).to.be.undefined; - }); + it('should not spec.interpretResponse()', function () { + const bidder = newBidder(spec); - it('should register a bidder with the appropriate mediaTypes', function () { - const thisSpec = Object.assign(newEmptySpec(), { supportedMediaTypes: ['video'] }); - registerBidder(thisSpec); - expect(registerBidAdapterStub.calledOnce).to.equal(true); - expect(registerBidAdapterStub.firstCall.args[2]).to.deep.equal({supportedMediaTypes: ['video']}); + spec.isBidRequestValid.returns(true); + spec.buildRequests.returns({ + method: 'POST', + url: 'test.url.com', + data: {} + }); + spec.getUserSyncs.returns([]); + + bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); + + expect(spec.interpretResponse.called).to.equal(false); + expect(doneStub.calledOnce).to.equal(true); + expect(callBidderErrorStub.calledOnce).to.equal(true); + expect(callBidderErrorStub.firstCall.args[0]).to.equal(CODE); + expect(callBidderErrorStub.firstCall.args[1]).to.equal(xhrErrorMock); + expect(callBidderErrorStub.firstCall.args[2]).to.equal(MOCK_BIDS_REQUEST); + sinon.assert.calledWith(eventEmitterStub, CONSTANTS.EVENTS.BIDDER_ERROR, { + error: xhrErrorMock, + bidderRequest: MOCK_BIDS_REQUEST + }); }); - it('should register bidders with the appropriate aliases', function () { - const thisSpec = Object.assign(newEmptySpec(), { aliases: ['foo', 'bar'] }); - registerBidder(thisSpec); - - expect(registerBidAdapterStub.calledThrice).to.equal(true); - - // Make sure our later calls don't override the bidder code from previous calls. - expect(registerBidAdapterStub.firstCall.args[0].getBidderCode()).to.equal(CODE); - expect(registerBidAdapterStub.secondCall.args[0].getBidderCode()).to.equal('foo') - expect(registerBidAdapterStub.thirdCall.args[0].getBidderCode()).to.equal('bar') + it('should not add bids for each adunit code into the auction', function () { + const bidder = newBidder(spec); - expect(registerBidAdapterStub.firstCall.args[1]).to.equal(CODE); - expect(registerBidAdapterStub.secondCall.args[1]).to.equal('foo') - expect(registerBidAdapterStub.thirdCall.args[1]).to.equal('bar') + spec.isBidRequestValid.returns(true); + spec.buildRequests.returns({ + method: 'POST', + url: 'test.url.com', + data: {} + }); + spec.interpretResponse.returns([]); + spec.getUserSyncs.returns([]); + + bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); + + expect(addBidResponseStub.callCount).to.equal(0); + expect(doneStub.calledOnce).to.equal(true); + expect(callBidderErrorStub.calledOnce).to.equal(true); + expect(callBidderErrorStub.firstCall.args[0]).to.equal(CODE); + expect(callBidderErrorStub.firstCall.args[1]).to.equal(xhrErrorMock); + expect(callBidderErrorStub.firstCall.args[2]).to.equal(MOCK_BIDS_REQUEST); + sinon.assert.calledWith(eventEmitterStub, CONSTANTS.EVENTS.BIDDER_ERROR, { + error: xhrErrorMock, + bidderRequest: MOCK_BIDS_REQUEST + }); }); - it('should register alias with their gvlid', function() { - const aliases = [ - { - code: 'foo', - gvlid: 1 - }, - { - code: 'bar', - gvlid: 2 - }, - { - code: 'baz' - } - ] - const thisSpec = Object.assign(newEmptySpec(), { aliases: aliases }); - registerBidder(thisSpec); - - expect(registerBidAdapterStub.getCall(1).args[0].getSpec().gvlid).to.equal(1); - expect(registerBidAdapterStub.getCall(2).args[0].getSpec().gvlid).to.equal(2); - expect(registerBidAdapterStub.getCall(3).args[0].getSpec().gvlid).to.equal(undefined); - }) - - it('should register alias with skipPbsAliasing', function() { - const aliases = [ - { - code: 'foo', - skipPbsAliasing: true - }, - { - code: 'bar', - skipPbsAliasing: false - }, - { - code: 'baz' - } - ] - const thisSpec = Object.assign(newEmptySpec(), { aliases: aliases }); - registerBidder(thisSpec); - - expect(registerBidAdapterStub.getCall(1).args[0].getSpec().skipPbsAliasing).to.equal(true); - expect(registerBidAdapterStub.getCall(2).args[0].getSpec().skipPbsAliasing).to.equal(false); - expect(registerBidAdapterStub.getCall(3).args[0].getSpec().skipPbsAliasing).to.equal(undefined); - }) - }) - - describe('validate bid response: ', function () { - let spec; - let indexStub, adUnits, bidderRequests; - let addBidResponseStub; - let doneStub; - let ajaxStub; - let logErrorSpy; + it('should call spec.getUserSyncs() with no responses', function () { + const bidder = newBidder(spec); - let bids = [{ - 'ad': 'creative', - 'cpm': '1.99', - 'width': 300, - 'height': 250, - 'requestId': '1', - 'creativeId': 'some-id', - 'currency': 'USD', - 'netRevenue': true, - 'ttl': 360 - }]; + spec.isBidRequestValid.returns(true); + spec.buildRequests.returns({ + method: 'POST', + url: 'test.url.com', + data: {} + }); + spec.getUserSyncs.returns([]); + + bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); + + expect(spec.getUserSyncs.calledOnce).to.equal(true); + expect(spec.getUserSyncs.firstCall.args[1]).to.deep.equal([]); + expect(doneStub.calledOnce).to.equal(true); + expect(callBidderErrorStub.calledOnce).to.equal(true); + expect(callBidderErrorStub.firstCall.args[0]).to.equal(CODE); + expect(callBidderErrorStub.firstCall.args[1]).to.equal(xhrErrorMock); + expect(callBidderErrorStub.firstCall.args[2]).to.equal(MOCK_BIDS_REQUEST); + sinon.assert.calledWith(eventEmitterStub, CONSTANTS.EVENTS.BIDDER_ERROR, { + error: xhrErrorMock, + bidderRequest: MOCK_BIDS_REQUEST + }); + }); - beforeEach(function () { - spec = { - code: CODE, - isBidRequestValid: sinon.stub(), - buildRequests: sinon.stub(), - interpretResponse: sinon.stub(), - }; + it('should call spec.getUserSyncs() with no responses', function () { + const bidder = newBidder(spec); spec.isBidRequestValid.returns(true); spec.buildRequests.returns({ @@ -1084,138 +776,212 @@ describe('bidderFactory', () => { url: 'test.url.com', data: {} }); - - addBidResponseStub = sinon.stub(); - addBidResponseStub.reject = sinon.stub(); - doneStub = sinon.stub(); - ajaxStub = sinon.stub(ajax, 'ajax').callsFake(function(url, callbacks) { - const fakeResponse = sinon.stub(); - fakeResponse.returns('headerContent'); - callbacks.success('response body', { getResponseHeader: fakeResponse }); + spec.getUserSyncs.returns([]); + + bidder.callBids(MOCK_BIDS_REQUEST, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); + + expect(spec.getUserSyncs.calledOnce).to.equal(true); + expect(spec.getUserSyncs.firstCall.args[1]).to.deep.equal([]); + expect(doneStub.calledOnce).to.equal(true); + expect(callBidderErrorStub.calledOnce).to.equal(true); + expect(callBidderErrorStub.firstCall.args[0]).to.equal(CODE); + expect(callBidderErrorStub.firstCall.args[1]).to.equal(xhrErrorMock); + expect(callBidderErrorStub.firstCall.args[2]).to.equal(MOCK_BIDS_REQUEST); + sinon.assert.calledWith(eventEmitterStub, CONSTANTS.EVENTS.BIDDER_ERROR, { + error: xhrErrorMock, + bidderRequest: MOCK_BIDS_REQUEST }); - logErrorSpy = sinon.spy(utils, 'logError'); - indexStub = sinon.stub(auctionManager, 'index'); - adUnits = []; - bidderRequests = []; - indexStub.get(() => stubAuctionIndex({adUnits: adUnits, bidderRequests: bidderRequests})) }); + }); +}); - afterEach(function () { - ajaxStub.restore(); - logErrorSpy.restore(); - indexStub.restore; - }); +describe('registerBidder', function () { + let registerBidAdapterStub; + let aliasBidAdapterStub; - if (FEATURES.NATIVE) { - it('should add native bids that do have required assets', function () { - adUnits = [{ - transactionId: 'au', - nativeParams: { - title: {'required': true}, - } - }] - decorateAdUnitsWithNativeParams(adUnits); - let bidRequest = { - bids: [{ - bidId: '1', - auctionId: 'first-bid-id', - adUnitCode: 'mock/placement', - transactionId: 'au', - params: { - param: 5 - }, - mediaType: 'native', - }] - }; - - let bids1 = Object.assign({}, - bids[0], - { - 'mediaType': 'native', - 'native': { - 'title': 'Native Creative', - 'clickUrl': 'https://www.link.example', - } - } - ); + beforeEach(function () { + registerBidAdapterStub = sinon.stub(adapterManager, 'registerBidAdapter'); + aliasBidAdapterStub = sinon.stub(adapterManager, 'aliasBidAdapter'); + }); - const bidder = newBidder(spec); + afterEach(function () { + registerBidAdapterStub.restore(); + aliasBidAdapterStub.restore(); + }); - spec.interpretResponse.returns(bids1); - bidder.callBids(bidRequest, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); + function newEmptySpec() { + return { + code: CODE, + isBidRequestValid: function() { }, + buildRequests: function() { }, + interpretResponse: function() { }, + }; + } + + it('should register a bidder with the adapterManager', function () { + registerBidder(newEmptySpec()); + expect(registerBidAdapterStub.calledOnce).to.equal(true); + expect(registerBidAdapterStub.firstCall.args[0]).to.have.property('callBids'); + expect(registerBidAdapterStub.firstCall.args[0].callBids).to.be.a('function'); + + expect(registerBidAdapterStub.firstCall.args[1]).to.equal(CODE); + expect(registerBidAdapterStub.firstCall.args[2]).to.be.undefined; + }); - expect(addBidResponseStub.calledOnce).to.equal(true); - expect(addBidResponseStub.firstCall.args[0]).to.equal('mock/placement'); - expect(logErrorSpy.callCount).to.equal(0); - }); + it('should register a bidder with the appropriate mediaTypes', function () { + const thisSpec = Object.assign(newEmptySpec(), { supportedMediaTypes: ['video'] }); + registerBidder(thisSpec); + expect(registerBidAdapterStub.calledOnce).to.equal(true); + expect(registerBidAdapterStub.firstCall.args[2]).to.deep.equal({supportedMediaTypes: ['video']}); + }); - it('should not add native bids that do not have required assets', function () { - adUnits = [{ - transactionId: 'au', - nativeParams: { - title: {'required': true}, - }, - }]; - decorateAdUnitsWithNativeParams(adUnits); - let bidRequest = { - bids: [{ - bidId: '1', - auctionId: 'first-bid-id', - adUnitCode: 'mock/placement', - transactionId: 'au', - params: { - param: 5 - }, - mediaType: 'native', - }] - }; - let bids1 = Object.assign({}, - bids[0], - { - bidderCode: CODE, - mediaType: 'native', - native: { - title: undefined, - clickUrl: 'https://www.link.example', - } - } - ); + it('should register bidders with the appropriate aliases', function () { + const thisSpec = Object.assign(newEmptySpec(), { aliases: ['foo', 'bar'] }); + registerBidder(thisSpec); - const bidder = newBidder(spec); - spec.interpretResponse.returns(bids1); - bidder.callBids(bidRequest, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); + expect(registerBidAdapterStub.calledThrice).to.equal(true); - expect(addBidResponseStub.called).to.equal(false); - expect(addBidResponseStub.reject.calledOnce).to.be.true; - expect(logErrorSpy.calledWithMatch('Ignoring bid: Native bid missing some required properties.')).to.equal(true); - }); - } + // Make sure our later calls don't override the bidder code from previous calls. + expect(registerBidAdapterStub.firstCall.args[0].getBidderCode()).to.equal(CODE); + expect(registerBidAdapterStub.secondCall.args[0].getBidderCode()).to.equal('foo') + expect(registerBidAdapterStub.thirdCall.args[0].getBidderCode()).to.equal('bar') + + expect(registerBidAdapterStub.firstCall.args[1]).to.equal(CODE); + expect(registerBidAdapterStub.secondCall.args[1]).to.equal('foo') + expect(registerBidAdapterStub.thirdCall.args[1]).to.equal('bar') + }); + + it('should register alias with their gvlid', function() { + const aliases = [ + { + code: 'foo', + gvlid: 1 + }, + { + code: 'bar', + gvlid: 2 + }, + { + code: 'baz' + } + ] + const thisSpec = Object.assign(newEmptySpec(), { aliases: aliases }); + registerBidder(thisSpec); + + expect(registerBidAdapterStub.getCall(1).args[0].getSpec().gvlid).to.equal(1); + expect(registerBidAdapterStub.getCall(2).args[0].getSpec().gvlid).to.equal(2); + expect(registerBidAdapterStub.getCall(3).args[0].getSpec().gvlid).to.equal(undefined); + }) + + it('should register alias with skipPbsAliasing', function() { + const aliases = [ + { + code: 'foo', + skipPbsAliasing: true + }, + { + code: 'bar', + skipPbsAliasing: false + }, + { + code: 'baz' + } + ] + const thisSpec = Object.assign(newEmptySpec(), { aliases: aliases }); + registerBidder(thisSpec); + + expect(registerBidAdapterStub.getCall(1).args[0].getSpec().skipPbsAliasing).to.equal(true); + expect(registerBidAdapterStub.getCall(2).args[0].getSpec().skipPbsAliasing).to.equal(false); + expect(registerBidAdapterStub.getCall(3).args[0].getSpec().skipPbsAliasing).to.equal(undefined); + }) +}) + +describe('validate bid response: ', function () { + let spec; + let indexStub, adUnits, bidderRequests; + let addBidResponseStub; + let doneStub; + let ajaxStub; + let logErrorSpy; + + let bids = [{ + 'ad': 'creative', + 'cpm': '1.99', + 'width': 300, + 'height': 250, + 'requestId': '1', + 'creativeId': 'some-id', + 'currency': 'USD', + 'netRevenue': true, + 'ttl': 360 + }]; + + beforeEach(function () { + spec = { + code: CODE, + isBidRequestValid: sinon.stub(), + buildRequests: sinon.stub(), + interpretResponse: sinon.stub(), + }; + + spec.isBidRequestValid.returns(true); + spec.buildRequests.returns({ + method: 'POST', + url: 'test.url.com', + data: {} + }); + + addBidResponseStub = sinon.stub(); + addBidResponseStub.reject = sinon.stub(); + doneStub = sinon.stub(); + ajaxStub = sinon.stub(ajax, 'ajax').callsFake(function(url, callbacks) { + const fakeResponse = sinon.stub(); + fakeResponse.returns('headerContent'); + callbacks.success('response body', { getResponseHeader: fakeResponse }); + }); + logErrorSpy = sinon.spy(utils, 'logError'); + indexStub = sinon.stub(auctionManager, 'index'); + adUnits = []; + bidderRequests = []; + indexStub.get(() => stubAuctionIndex({adUnits: adUnits, bidderRequests: bidderRequests})) + }); + + afterEach(function () { + ajaxStub.restore(); + logErrorSpy.restore(); + indexStub.restore; + }); - it('should add bid when renderer is present on outstream bids', function () { + if (FEATURES.NATIVE) { + it('should add native bids that do have required assets', function () { adUnits = [{ transactionId: 'au', - mediaTypes: { - video: {context: 'outstream'} + nativeParams: { + title: {'required': true}, } }] + decorateAdUnitsWithNativeParams(adUnits); let bidRequest = { bids: [{ bidId: '1', auctionId: 'first-bid-id', - transactionId: 'au', adUnitCode: 'mock/placement', + transactionId: 'au', params: { param: 5 }, + mediaType: 'native', }] }; let bids1 = Object.assign({}, bids[0], { - bidderCode: CODE, - mediaType: 'video', - renderer: {render: () => true, url: 'render.js'}, + 'mediaType': 'native', + 'native': { + 'title': 'Native Creative', + 'clickUrl': 'https://www.link.example', + } } ); @@ -1229,335 +995,394 @@ describe('bidderFactory', () => { expect(logErrorSpy.callCount).to.equal(0); }); - it('should add banner bids that have no width or height but single adunit size', function () { + it('should not add native bids that do not have required assets', function () { + adUnits = [{ + transactionId: 'au', + nativeParams: { + title: {'required': true}, + }, + }]; + decorateAdUnitsWithNativeParams(adUnits); let bidRequest = { bids: [{ - bidder: CODE, bidId: '1', auctionId: 'first-bid-id', adUnitCode: 'mock/placement', + transactionId: 'au', params: { param: 5 }, - sizes: [[300, 250]], + mediaType: 'native', }] }; - bidderRequests = [bidRequest]; let bids1 = Object.assign({}, bids[0], { - width: undefined, - height: undefined + bidderCode: CODE, + mediaType: 'native', + native: { + title: undefined, + clickUrl: 'https://www.link.example', + } } ); const bidder = newBidder(spec); - spec.interpretResponse.returns(bids1); bidder.callBids(bidRequest, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); - expect(addBidResponseStub.calledOnce).to.equal(true); - expect(addBidResponseStub.firstCall.args[0]).to.equal('mock/placement'); - expect(logErrorSpy.callCount).to.equal(0); + expect(addBidResponseStub.called).to.equal(false); + expect(addBidResponseStub.reject.calledOnce).to.be.true; + expect(logErrorSpy.calledWithMatch('Ignoring bid: Native bid missing some required properties.')).to.equal(true); }); + } + + it('should add bid when renderer is present on outstream bids', function () { + adUnits = [{ + transactionId: 'au', + mediaTypes: { + video: {context: 'outstream'} + } + }] + let bidRequest = { + bids: [{ + bidId: '1', + auctionId: 'first-bid-id', + transactionId: 'au', + adUnitCode: 'mock/placement', + params: { + param: 5 + }, + }] + }; + + let bids1 = Object.assign({}, + bids[0], + { + bidderCode: CODE, + mediaType: 'video', + renderer: {render: () => true, url: 'render.js'}, + } + ); + + const bidder = newBidder(spec); + + spec.interpretResponse.returns(bids1); + bidder.callBids(bidRequest, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); + + expect(addBidResponseStub.calledOnce).to.equal(true); + expect(addBidResponseStub.firstCall.args[0]).to.equal('mock/placement'); + expect(logErrorSpy.callCount).to.equal(0); + }); + + it('should add banner bids that have no width or height but single adunit size', function () { + let bidRequest = { + bids: [{ + bidder: CODE, + bidId: '1', + auctionId: 'first-bid-id', + adUnitCode: 'mock/placement', + params: { + param: 5 + }, + sizes: [[300, 250]], + }] + }; + bidderRequests = [bidRequest]; + let bids1 = Object.assign({}, + bids[0], + { + width: undefined, + height: undefined + } + ); + + const bidder = newBidder(spec); + + spec.interpretResponse.returns(bids1); + bidder.callBids(bidRequest, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); + + expect(addBidResponseStub.calledOnce).to.equal(true); + expect(addBidResponseStub.firstCall.args[0]).to.equal('mock/placement'); + expect(logErrorSpy.callCount).to.equal(0); + }); + + describe(' Check for alternateBiddersList ', function() { + let bidRequest; + let bids1; + let logWarnSpy; + let bidderSettingStub, aliasRegistryStub; + let aliasRegistry; - it('should disregard auctionId/transactionId set by the adapter', () => { - let bidderRequest = { + beforeEach(function () { + bidRequest = { bids: [{ - bidder: CODE, bidId: '1', - auctionId: 'aid', - transactionId: 'tid', - adUnitCode: 'au', + bidder: CODE, + auctionId: 'first-bid-id', + adUnitCode: 'mock/placement', + transactionId: 'au', }] }; - const bidder = newBidder(spec); - spec.interpretResponse.returns(Object.assign({}, bids[0], {transactionId: 'ignored', auctionId: 'ignored'})); - bidder.callBids(bidderRequest, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); - sinon.assert.calledWith(addBidResponseStub, sinon.match.any, sinon.match({ - transactionId: 'tid', - auctionId: 'aid' - })); - }) - - describe(' Check for alternateBiddersList ', function() { - let bidRequest; - let bids1; - let logWarnSpy; - let bidderSettingStub, aliasRegistryStub; - let aliasRegistry; - beforeEach(function () { - bidRequest = { - bids: [{ - bidId: '1', - bidder: CODE, - auctionId: 'first-bid-id', - adUnitCode: 'mock/placement', - transactionId: 'au', - }] - }; - - bids1 = Object.assign({}, - bids[0], - { - bidderCode: 'validalternatebidder', - adapterCode: 'knownadapter1' - } - ); - logWarnSpy = sinon.spy(utils, 'logWarn'); - bidderSettingStub = sinon.stub(bidderSettings, 'get'); - aliasRegistry = {}; - aliasRegistryStub = sinon.stub(adapterManager, 'aliasRegistry'); - aliasRegistryStub.get(() => aliasRegistry); - }); + bids1 = Object.assign({}, + bids[0], + { + bidderCode: 'validalternatebidder', + adapterCode: 'knownadapter1' + } + ); + logWarnSpy = sinon.spy(utils, 'logWarn'); + bidderSettingStub = sinon.stub(bidderSettings, 'get'); + aliasRegistry = {}; + aliasRegistryStub = sinon.stub(adapterManager, 'aliasRegistry'); + aliasRegistryStub.get(() => aliasRegistry); + }); - afterEach(function () { - logWarnSpy.restore(); - bidderSettingStub.restore(); - aliasRegistryStub.restore(); - }); + afterEach(function () { + logWarnSpy.restore(); + bidderSettingStub.restore(); + aliasRegistryStub.restore(); + }); - it('should log warning when bidder is unknown and allowAlternateBidderCodes flag is false', function () { - bidderSettingStub.returns(false); + it('should log warning when bidder is unknown and allowAlternateBidderCodes flag is false', function () { + bidderSettingStub.returns(false); - const bidder = newBidder(spec); - spec.interpretResponse.returns(bids1); - bidder.callBids(bidRequest, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); + const bidder = newBidder(spec); + spec.interpretResponse.returns(bids1); + bidder.callBids(bidRequest, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); - expect(addBidResponseStub.called).to.equal(false); - expect(addBidResponseStub.reject.calledOnce).to.be.true; - expect(logWarnSpy.callCount).to.equal(1); - }); + expect(addBidResponseStub.called).to.equal(false); + expect(addBidResponseStub.reject.calledOnce).to.be.true; + expect(logWarnSpy.callCount).to.equal(1); + }); - it('should reject the bid, when allowAlternateBidderCodes flag is undefined (default should be false)', function () { - bidderSettingStub.returns(undefined); + it('should reject the bid, when allowAlternateBidderCodes flag is undefined (default should be false)', function () { + bidderSettingStub.returns(undefined); - const bidder = newBidder(spec); - spec.interpretResponse.returns(bids1); - bidder.callBids(bidRequest, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); + const bidder = newBidder(spec); + spec.interpretResponse.returns(bids1); + bidder.callBids(bidRequest, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); - expect(addBidResponseStub.called).to.equal(false); - expect(addBidResponseStub.reject.calledOnce).to.be.true; - }); + expect(addBidResponseStub.called).to.equal(false); + expect(addBidResponseStub.reject.calledOnce).to.be.true; + }); - it('should log warning when the particular bidder is not specified in allowedAlternateBidderCodes and allowAlternateBidderCodes flag is true', function () { - bidderSettingStub.withArgs(CODE, 'allowAlternateBidderCodes').returns(true); - bidderSettingStub.withArgs(CODE, 'allowedAlternateBidderCodes').returns(['invalidAlternateBidder02']); + it('should log warning when the particular bidder is not specified in allowedAlternateBidderCodes and allowAlternateBidderCodes flag is true', function () { + bidderSettingStub.withArgs(CODE, 'allowAlternateBidderCodes').returns(true); + bidderSettingStub.withArgs(CODE, 'allowedAlternateBidderCodes').returns(['invalidAlternateBidder02']); - const bidder = newBidder(spec); - spec.interpretResponse.returns(bids1); - bidder.callBids(bidRequest, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); + const bidder = newBidder(spec); + spec.interpretResponse.returns(bids1); + bidder.callBids(bidRequest, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); - expect(addBidResponseStub.called).to.equal(false); - expect(addBidResponseStub.reject.calledOnce).to.be.true; - expect(logWarnSpy.callCount).to.equal(1); - }); + expect(addBidResponseStub.called).to.equal(false); + expect(addBidResponseStub.reject.calledOnce).to.be.true; + expect(logWarnSpy.callCount).to.equal(1); + }); - it('should accept the bid, when allowedAlternateBidderCodes is empty and allowAlternateBidderCodes flag is true', function () { - bidderSettingStub.withArgs(CODE, 'allowAlternateBidderCodes').returns(true); - bidderSettingStub.withArgs(CODE, 'allowedAlternateBidderCodes').returns(); + it('should accept the bid, when allowedAlternateBidderCodes is empty and allowAlternateBidderCodes flag is true', function () { + bidderSettingStub.withArgs(CODE, 'allowAlternateBidderCodes').returns(true); + bidderSettingStub.withArgs(CODE, 'allowedAlternateBidderCodes').returns(); - const bidder = newBidder(spec); - spec.interpretResponse.returns(bids1); - bidder.callBids(bidRequest, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); + const bidder = newBidder(spec); + spec.interpretResponse.returns(bids1); + bidder.callBids(bidRequest, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); - expect(addBidResponseStub.calledOnce).to.equal(true); - expect(logWarnSpy.callCount).to.equal(0); - expect(logErrorSpy.callCount).to.equal(0); - }); + expect(addBidResponseStub.calledOnce).to.equal(true); + expect(logWarnSpy.callCount).to.equal(0); + expect(logErrorSpy.callCount).to.equal(0); + }); - it('should accept the bid, when allowedAlternateBidderCodes is marked as * and allowAlternateBidderCodes flag is true', function () { - bidderSettingStub.withArgs(CODE, 'allowAlternateBidderCodes').returns(true); - bidderSettingStub.withArgs(CODE, 'allowedAlternateBidderCodes').returns(['*']); + it('should accept the bid, when allowedAlternateBidderCodes is marked as * and allowAlternateBidderCodes flag is true', function () { + bidderSettingStub.withArgs(CODE, 'allowAlternateBidderCodes').returns(true); + bidderSettingStub.withArgs(CODE, 'allowedAlternateBidderCodes').returns(['*']); - const bidder = newBidder(spec); - spec.interpretResponse.returns(bids1); - bidder.callBids(bidRequest, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); + const bidder = newBidder(spec); + spec.interpretResponse.returns(bids1); + bidder.callBids(bidRequest, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); - expect(addBidResponseStub.calledOnce).to.equal(true); - expect(logWarnSpy.callCount).to.equal(0); - expect(logErrorSpy.callCount).to.equal(0); - }); + expect(addBidResponseStub.calledOnce).to.equal(true); + expect(logWarnSpy.callCount).to.equal(0); + expect(logErrorSpy.callCount).to.equal(0); + }); - it('should accept the bid, when allowedAlternateBidderCodes is marked as * (with space) and allowAlternateBidderCodes flag is true', function () { - bidderSettingStub.withArgs(CODE, 'allowAlternateBidderCodes').returns(true); - bidderSettingStub.withArgs(CODE, 'allowedAlternateBidderCodes').returns([' * ']); + it('should accept the bid, when allowedAlternateBidderCodes is marked as * (with space) and allowAlternateBidderCodes flag is true', function () { + bidderSettingStub.withArgs(CODE, 'allowAlternateBidderCodes').returns(true); + bidderSettingStub.withArgs(CODE, 'allowedAlternateBidderCodes').returns([' * ']); - const bidder = newBidder(spec); - spec.interpretResponse.returns(bids1); - bidder.callBids(bidRequest, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); + const bidder = newBidder(spec); + spec.interpretResponse.returns(bids1); + bidder.callBids(bidRequest, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); - expect(addBidResponseStub.calledOnce).to.equal(true); - expect(logWarnSpy.callCount).to.equal(0); - expect(logErrorSpy.callCount).to.equal(0); - }); + expect(addBidResponseStub.calledOnce).to.equal(true); + expect(logWarnSpy.callCount).to.equal(0); + expect(logErrorSpy.callCount).to.equal(0); + }); - it('should not accept the bid, when allowedAlternateBidderCodes is marked as empty array and allowAlternateBidderCodes flag is true', function () { - bidderSettingStub.withArgs(CODE, 'allowAlternateBidderCodes').returns(true); - bidderSettingStub.withArgs(CODE, 'allowedAlternateBidderCodes').returns([]); + it('should not accept the bid, when allowedAlternateBidderCodes is marked as empty array and allowAlternateBidderCodes flag is true', function () { + bidderSettingStub.withArgs(CODE, 'allowAlternateBidderCodes').returns(true); + bidderSettingStub.withArgs(CODE, 'allowedAlternateBidderCodes').returns([]); - const bidder = newBidder(spec); - spec.interpretResponse.returns(bids1); - bidder.callBids(bidRequest, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); + const bidder = newBidder(spec); + spec.interpretResponse.returns(bids1); + bidder.callBids(bidRequest, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); - expect(addBidResponseStub.called).to.equal(false); - expect(addBidResponseStub.reject.calledOnce).to.be.true; - expect(logWarnSpy.callCount).to.equal(1); - }); + expect(addBidResponseStub.called).to.equal(false); + expect(addBidResponseStub.reject.calledOnce).to.be.true; + expect(logWarnSpy.callCount).to.equal(1); + }); - it('should accept the bid, when allowedAlternateBidderCodes contains bidder name and allowAlternateBidderCodes flag is true', function () { - bidderSettingStub.withArgs(CODE, 'allowAlternateBidderCodes').returns(true); - bidderSettingStub.withArgs(CODE, 'allowedAlternateBidderCodes').returns(['validAlternateBidder']); + it('should accept the bid, when allowedAlternateBidderCodes contains bidder name and allowAlternateBidderCodes flag is true', function () { + bidderSettingStub.withArgs(CODE, 'allowAlternateBidderCodes').returns(true); + bidderSettingStub.withArgs(CODE, 'allowedAlternateBidderCodes').returns(['validAlternateBidder']); - const bidder = newBidder(spec); - spec.interpretResponse.returns(bids1); - bidder.callBids(bidRequest, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); + const bidder = newBidder(spec); + spec.interpretResponse.returns(bids1); + bidder.callBids(bidRequest, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); - expect(addBidResponseStub.called).to.equal(true); - expect(logWarnSpy.callCount).to.equal(0); - expect(logErrorSpy.callCount).to.equal(0); - }); + expect(addBidResponseStub.called).to.equal(true); + expect(logWarnSpy.callCount).to.equal(0); + expect(logErrorSpy.callCount).to.equal(0); + }); - it('should not accept the bid, when bidder is an alias but bidderSetting is missing for the bidder. It should fallback to standard setting and reject the bid', function () { - bidderSettingStub.withArgs(CODE, 'allowAlternateBidderCodes').returns(false); - aliasRegistry = {'validAlternateBidder': CODE}; + it('should not accept the bid, when bidder is an alias but bidderSetting is missing for the bidder. It should fallback to standard setting and reject the bid', function () { + bidderSettingStub.withArgs(CODE, 'allowAlternateBidderCodes').returns(false); + aliasRegistry = {'validAlternateBidder': CODE}; - const bidder = newBidder(spec); - spec.interpretResponse.returns(bids1); - bidder.callBids(bidRequest, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); + const bidder = newBidder(spec); + spec.interpretResponse.returns(bids1); + bidder.callBids(bidRequest, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); - expect(addBidResponseStub.called).to.equal(false); - expect(logWarnSpy.callCount).to.equal(1); - expect(addBidResponseStub.reject.calledOnce).to.be.true; - }); + expect(addBidResponseStub.called).to.equal(false); + expect(logWarnSpy.callCount).to.equal(1); + expect(addBidResponseStub.reject.calledOnce).to.be.true; + }); - it('should not accept the bid, when bidderSetting is missing for the bidder. It should fallback to standard setting and reject the bid', function () { - bidderSettingStub.withArgs(CODE, 'allowAlternateBidderCodes').returns(false); + it('should not accept the bid, when bidderSetting is missing for the bidder. It should fallback to standard setting and reject the bid', function () { + bidderSettingStub.withArgs(CODE, 'allowAlternateBidderCodes').returns(false); - const bidder = newBidder(spec); - spec.interpretResponse.returns(bids1); - bidder.callBids(bidRequest, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); + const bidder = newBidder(spec); + spec.interpretResponse.returns(bids1); + bidder.callBids(bidRequest, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); - expect(addBidResponseStub.called).to.equal(false); - expect(addBidResponseStub.reject.calledOnce).to.be.true; - expect(logWarnSpy.callCount).to.equal(1); - }); + expect(addBidResponseStub.called).to.equal(false); + expect(addBidResponseStub.reject.calledOnce).to.be.true; + expect(logWarnSpy.callCount).to.equal(1); }); + }); - describe('when interpretResponse returns BidderAuctionResponse', function() { - const bidRequest = { - auctionId: 'aid', - bids: [{ - bidId: '1', - bidder: CODE, - auctionId: 'aid', - adUnitCode: 'mock/placement', - transactionId: 'au', - }] - }; - const fledgeAuctionConfig = { + describe('when interpretResponse returns BidderAuctionResponse', function() { + const bidRequest = { + bids: [{ bidId: '1', - config: { - foo: 'bar' - } + bidder: CODE, + auctionId: 'first-bid-id', + adUnitCode: 'mock/placement', + transactionId: 'au', + }] + }; + const fledgeAuctionConfig = { + bidId: '1', + config: { + foo: 'bar' } - describe('when response has FLEDGE auction config', function() { - let fledgeStub; + } + describe('when response has FLEDGE auction config', function() { + let fledgeStub; - function fledgeHook(next, ...args) { - fledgeStub(...args); - } + function fledgeHook(next, ...args) { + fledgeStub(...args); + } - before(() => { - addComponentAuction.before(fledgeHook); - }); + before(() => { + addComponentAuction.before(fledgeHook); + }); - after(() => { - addComponentAuction.getHooks({hook: fledgeHook}).remove(); - }) + after(() => { + addComponentAuction.getHooks({hook: fledgeHook}).remove(); + }) - beforeEach(function () { - fledgeStub = sinon.stub(); - }); + beforeEach(function () { + fledgeStub = sinon.stub(); + }); - it('should unwrap bids', function() { - const bidder = newBidder(spec); - spec.interpretResponse.returns({ - bids: bids, - fledgeAuctionConfigs: [] - }); - bidder.callBids(bidRequest, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); - expect(addBidResponseStub.calledOnce).to.equal(true); - expect(addBidResponseStub.firstCall.args[0]).to.equal('mock/placement'); + it('should unwrap bids', function() { + const bidder = newBidder(spec); + spec.interpretResponse.returns({ + bids: bids, + fledgeAuctionConfigs: [] }); + bidder.callBids(bidRequest, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); + expect(addBidResponseStub.calledOnce).to.equal(true); + expect(addBidResponseStub.firstCall.args[0]).to.equal('mock/placement'); + }); - it('should call fledgeManager with FLEDGE configs', function() { - const bidder = newBidder(spec); - spec.interpretResponse.returns({ - bids: bids, - fledgeAuctionConfigs: [fledgeAuctionConfig] - }); - bidder.callBids(bidRequest, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); + it('should call fledgeManager with FLEDGE configs', function() { + const bidder = newBidder(spec); + spec.interpretResponse.returns({ + bids: bids, + fledgeAuctionConfigs: [fledgeAuctionConfig] + }); + bidder.callBids(bidRequest, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); - expect(fledgeStub.calledOnce).to.equal(true); - sinon.assert.calledWith(fledgeStub, bidRequest.bids[0], fledgeAuctionConfig.config); - expect(addBidResponseStub.calledOnce).to.equal(true); - expect(addBidResponseStub.firstCall.args[0]).to.equal('mock/placement'); - }) + expect(fledgeStub.calledOnce).to.equal(true); + sinon.assert.calledWith(fledgeStub, 'mock/placement', fledgeAuctionConfig.config); + expect(addBidResponseStub.calledOnce).to.equal(true); + expect(addBidResponseStub.firstCall.args[0]).to.equal('mock/placement'); + }) - it('should call fledgeManager with FLEDGE configs even if no bids returned', function() { - const bidder = newBidder(spec); - spec.interpretResponse.returns({ - bids: [], - fledgeAuctionConfigs: [fledgeAuctionConfig] - }); - bidder.callBids(bidRequest, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); + it('should call fledgeManager with FLEDGE configs even if no bids returned', function() { + const bidder = newBidder(spec); + spec.interpretResponse.returns({ + bids: [], + fledgeAuctionConfigs: [fledgeAuctionConfig] + }); + bidder.callBids(bidRequest, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); - expect(fledgeStub.calledOnce).to.be.true; - sinon.assert.calledWith(fledgeStub, bidRequest.bids[0], fledgeAuctionConfig.config); - expect(addBidResponseStub.calledOnce).to.equal(false); - }) + expect(fledgeStub.calledOnce).to.be.true; + sinon.assert.calledWith(fledgeStub, 'mock/placement', fledgeAuctionConfig.config); + expect(addBidResponseStub.calledOnce).to.equal(false); }) }) - }); + }) +}); - describe('bid response isValid', () => { - describe('size check', () => { - let req, index; +describe('bid response isValid', () => { + describe('size check', () => { + let req, index; - beforeEach(() => { - req = { - ...MOCK_BIDS_REQUEST.bids[0], - mediaTypes: { - banner: { - sizes: [[1, 2], [3, 4]] - } + beforeEach(() => { + req = { + ...MOCK_BIDS_REQUEST.bids[0], + mediaTypes: { + banner: { + sizes: [[1, 2], [3, 4]] } } - }); - - function mkResponse(width, height) { - return { - requestId: req.bidId, - width, - height, - cpm: 1, - ttl: 60, - creativeId: '123', - netRevenue: true, - currency: 'USD', - mediaType: 'banner', - } } + }); - function checkValid(bid) { - return isValid('au', bid, {index: stubAuctionIndex({bidRequests: [req]})}); + function mkResponse(width, height) { + return { + requestId: req.bidId, + width, + height, + cpm: 1, + ttl: 60, + creativeId: '123', + netRevenue: true, + currency: 'USD', + mediaType: 'banner', } + } - it('should succeed when response has a size that was in request', () => { - expect(checkValid(mkResponse(3, 4))).to.be.true; - }); - }) - }); -}) + function checkValid(bid) { + return isValid('au', bid, {index: stubAuctionIndex({bidRequests: [req]})}); + } + + it('should succeed when response has a size that was in request', () => { + expect(checkValid(mkResponse(3, 4))).to.be.true; + }); + }) +}); diff --git a/test/spec/unit/core/consentHandler_spec.js b/test/spec/unit/core/consentHandler_spec.js index 1bcad3216ce..98b317e0d36 100644 --- a/test/spec/unit/core/consentHandler_spec.js +++ b/test/spec/unit/core/consentHandler_spec.js @@ -1,4 +1,4 @@ -import {ConsentHandler, gvlidRegistry, multiHandler} from '../../../../src/consentHandler.js'; +import {ConsentHandler, gvlidRegistry} from '../../../../src/consentHandler.js'; describe('Consent data handler', () => { let handler; @@ -56,88 +56,6 @@ describe('Consent data handler', () => { }) }) }); - - describe('getHash', () => { - it('is defined when null', () => { - expect(handler.hash).be.a('string'); - }); - it('changes when a field is updated', () => { - const h1 = handler.hash; - handler.setConsentData({field: 'value', enabled: false}); - const h2 = handler.hash; - expect(h2).to.not.eql(h1); - handler.setConsentData({field: 'value', enabled: true}); - const h3 = handler.hash; - expect(h3).to.not.eql(h2); - expect(h3).to.not.eql(h1); - }); - it('does not change when fields are unchanged', () => { - handler.setConsentData({field: 'value', enabled: true}); - const h1 = handler.hash; - handler.setConsentData({field: 'value', enabled: true}); - expect(handler.hash).to.eql(h1); - }); - it('does not change when non-hashFields are updated', () => { - handler.hashFields = ['field', 'enabled']; - handler.setConsentData({field: 'value', enabled: true}); - const h1 = handler.hash; - handler.setConsentData({field: 'value', enabled: true, other: 'data'}); - expect(handler.hash).to.eql(h1); - }) - }) -}); - -describe('multiHandler', () => { - let handlers, multi; - beforeEach(() => { - handlers = {h1: {}, h2: {}}; - multi = multiHandler(handlers); - }); - - ['getConsentData', 'getConsentMeta'].forEach(method => { - describe(method, () => { - it('combines results from underlying handlers', () => { - handlers.h1[method] = () => 'one'; - handlers.h2[method] = () => 'two'; - expect(multi[method]()).to.eql({ - h1: 'one', - h2: 'two', - }) - }); - }); - }); - - describe('.promise', () => { - it('resolves all underlying promises', (done) => { - handlers.h1.promise = Promise.resolve('one'); - let resolver, result; - handlers.h2.promise = new Promise((resolve) => { resolver = resolve }); - multi.promise.then((val) => { - result = val; - expect(result).to.eql({ - h1: 'one', - h2: 'two' - }); - done(); - }) - handlers.h1.promise.then(() => { - expect(result).to.not.exist; - resolver('two'); - }); - }) - }); - - describe('.hash', () => { - ['h1', 'h2'].forEach((handler, i) => { - it(`changes when handler #${i + 1} changes hash`, () => { - handlers.h1.hash = 'one'; - handlers.h2.hash = 'two' - const first = multi.hash; - handlers[handler].hash = 'new'; - expect(multi.hash).to.not.eql(first); - }) - }) - }) }) describe('gvlidRegistry', () => { diff --git a/test/spec/unit/core/events_spec.js b/test/spec/unit/core/events_spec.js deleted file mode 100644 index e1451f657b5..00000000000 --- a/test/spec/unit/core/events_spec.js +++ /dev/null @@ -1,45 +0,0 @@ -import {config} from 'src/config.js'; -import {emit, clearEvents, getEvents, on, off} from '../../../../src/events.js'; -import * as utils from '../../../../src/utils.js' - -describe('events', () => { - let clock; - beforeEach(() => { - clock = sinon.useFakeTimers(); - clearEvents(); - }); - afterEach(() => { - clock.restore(); - }); - - it('should clear event log using eventHistoryTTL config', () => { - emit('testEvent', {}); - expect(getEvents().length).to.eql(1); - config.setConfig({eventHistoryTTL: 1}); - clock.tick(500); - expect(getEvents().length).to.eql(1); - clock.tick(6000); - expect(getEvents().length).to.eql(0); - }); - - it('should take history TTL in seconds', () => { - emit('testEvent', {}); - config.setConfig({eventHistoryTTL: 1000}); - clock.tick(10000); - expect(getEvents().length).to.eql(1); - }); - - it('should include the eventString if a callback fails', () => { - const logErrorStub = sinon.stub(utils, 'logError'); - const eventString = 'bidWon'; - let fn = function() { throw new Error('Test error'); }; - on(eventString, fn); - - emit(eventString, {}); - - sinon.assert.calledWith(logErrorStub, 'Error executing handler:', 'events.js', sinon.match.instanceOf(Error), eventString); - - off(eventString, fn); - logErrorStub.restore(); - }); -}) diff --git a/test/spec/unit/core/targeting_spec.js b/test/spec/unit/core/targeting_spec.js index ba9aeff70d1..f16d6208087 100644 --- a/test/spec/unit/core/targeting_spec.js +++ b/test/spec/unit/core/targeting_spec.js @@ -1,19 +1,13 @@ -import {expect} from 'chai'; -import { - filters, - getHighestCpmBidsFromBidPool, - sortByDealAndPriceBucketOrCpm, - targeting as targetingInstance -} from 'src/targeting.js'; -import {config} from 'src/config.js'; -import {createBidReceived} from 'test/fixtures/fixtures.js'; +import { expect } from 'chai'; +import { targeting as targetingInstance, filters, getHighestCpmBidsFromBidPool, sortByDealAndPriceBucketOrCpm } from 'src/targeting.js'; +import { config } from 'src/config.js'; +import { createBidReceived } from 'test/fixtures/fixtures.js'; import CONSTANTS from 'src/constants.json'; -import {auctionManager} from 'src/auctionManager.js'; +import { auctionManager } from 'src/auctionManager.js'; import * as utils from 'src/utils.js'; import {deepClone} from 'src/utils.js'; import {createBid} from '../../../../src/bidfactory.js'; import {hook} from '../../../../src/hook.js'; -import {getHighestCpm} from '../../../../src/utils/reducers.js'; function mkBid(bid, status = CONSTANTS.STATUS.GOOD) { return Object.assign(createBid(status), bid); @@ -457,7 +451,7 @@ describe('targeting tests', function () { } }); - const bids = getHighestCpmBidsFromBidPool(bidsReceived, getHighestCpm, 2); + const bids = getHighestCpmBidsFromBidPool(bidsReceived, utils.getHighestCpm, 2); expect(bids.length).to.equal(3); expect(bids[0].adId).to.equal('8383838'); @@ -473,7 +467,7 @@ describe('targeting tests', function () { } }); - const bids = getHighestCpmBidsFromBidPool(bidsReceived, getHighestCpm, 2); + const bids = getHighestCpmBidsFromBidPool(bidsReceived, utils.getHighestCpm, 2); expect(bids.length).to.equal(3); expect(bids[0].adId).to.equal('8383838'); @@ -955,7 +949,6 @@ describe('targeting tests', function () { expect(bids.length).to.equal(1); expect(bids[0].adId).to.equal('adid-1'); - expect(bids[0].latestTargetedAuctionId).to.equal(2); useBidCache = false; @@ -963,7 +956,6 @@ describe('targeting tests', function () { expect(bids.length).to.equal(1); expect(bids[0].adId).to.equal('adid-2'); - expect(bids[0].latestTargetedAuctionId).to.equal(2); }); it('should use bidCacheFilterFunction', function() { @@ -991,13 +983,9 @@ describe('targeting tests', function () { expect(bids.length).to.equal(4); expect(bids[0].adId).to.equal('adid-1'); - expect(bids[0].latestTargetedAuctionId).to.equal(2); expect(bids[1].adId).to.equal('adid-4'); - expect(bids[1].latestTargetedAuctionId).to.equal(2); expect(bids[2].adId).to.equal('adid-5'); - expect(bids[2].latestTargetedAuctionId).to.equal(2); expect(bids[3].adId).to.equal('adid-8'); - expect(bids[3].latestTargetedAuctionId).to.equal(2); // Bid Caching Off, No Filter Function useBidCache = false; @@ -1006,13 +994,9 @@ describe('targeting tests', function () { expect(bids.length).to.equal(4); expect(bids[0].adId).to.equal('adid-2'); - expect(bids[0].latestTargetedAuctionId).to.equal(2); expect(bids[1].adId).to.equal('adid-4'); - expect(bids[1].latestTargetedAuctionId).to.equal(2); expect(bids[2].adId).to.equal('adid-6'); - expect(bids[2].latestTargetedAuctionId).to.equal(2); expect(bids[3].adId).to.equal('adid-8'); - expect(bids[3].latestTargetedAuctionId).to.equal(2); // Bid Caching On AGAIN, No Filter Function (should be same as first time) useBidCache = true; @@ -1021,13 +1005,9 @@ describe('targeting tests', function () { expect(bids.length).to.equal(4); expect(bids[0].adId).to.equal('adid-1'); - expect(bids[0].latestTargetedAuctionId).to.equal(2); expect(bids[1].adId).to.equal('adid-4'); - expect(bids[1].latestTargetedAuctionId).to.equal(2); expect(bids[2].adId).to.equal('adid-5'); - expect(bids[2].latestTargetedAuctionId).to.equal(2); expect(bids[3].adId).to.equal('adid-8'); - expect(bids[3].latestTargetedAuctionId).to.equal(2); // Bid Caching On, with Filter Function to Exclude video useBidCache = true; @@ -1040,13 +1020,9 @@ describe('targeting tests', function () { expect(bids.length).to.equal(4); expect(bids[0].adId).to.equal('adid-1'); - expect(bids[0].latestTargetedAuctionId).to.equal(2); expect(bids[1].adId).to.equal('adid-4'); - expect(bids[1].latestTargetedAuctionId).to.equal(2); expect(bids[2].adId).to.equal('adid-6'); - expect(bids[2].latestTargetedAuctionId).to.equal(2); expect(bids[3].adId).to.equal('adid-8'); - expect(bids[3].latestTargetedAuctionId).to.equal(2); // filter function should have been called for each cached bid (4 times) expect(bcffCalled).to.equal(4); @@ -1062,13 +1038,9 @@ describe('targeting tests', function () { expect(bids.length).to.equal(4); expect(bids[0].adId).to.equal('adid-2'); - expect(bids[0].latestTargetedAuctionId).to.equal(2); expect(bids[1].adId).to.equal('adid-4'); - expect(bids[1].latestTargetedAuctionId).to.equal(2); expect(bids[2].adId).to.equal('adid-6'); - expect(bids[2].latestTargetedAuctionId).to.equal(2); expect(bids[3].adId).to.equal('adid-8'); - expect(bids[3].latestTargetedAuctionId).to.equal(2); // filter function should not have been called expect(bcffCalled).to.equal(0); }); diff --git a/test/spec/unit/pbjs_api_spec.js b/test/spec/unit/pbjs_api_spec.js index 39123d4aa41..5c361d186c0 100644 --- a/test/spec/unit/pbjs_api_spec.js +++ b/test/spec/unit/pbjs_api_spec.js @@ -14,7 +14,7 @@ import { config as configObj } from 'src/config.js'; import * as ajaxLib from 'src/ajax.js'; import * as auctionModule from 'src/auction.js'; import { registerBidder } from 'src/adapters/bidderFactory.js'; -import {resizeRemoteCreative} from 'src/secureCreatives.js'; +import { _sendAdToCreative } from 'src/secureCreatives.js'; import {find} from 'src/polyfill.js'; import * as pbjsModule from 'src/prebid.js'; import {hook} from '../../../src/hook.js'; @@ -25,7 +25,7 @@ import {stubAuctionIndex} from '../../helpers/indexStub.js'; import {createBid} from '../../../src/bidfactory.js'; import {enrichFPD} from '../../../src/fpd/enrichment.js'; import {mockFpdEnrichments} from '../../helpers/fpd.js'; -import {generateUUID} from '../../../src/utils.js'; + var assert = require('chai').assert; var expect = require('chai').expect; @@ -43,13 +43,13 @@ var adUnits = getAdUnits(); var adUnitCodes = getAdUnits().map(unit => unit.code); var bidsBackHandler = function() {}; const timeout = 2000; -const auctionId = generateUUID(); -let auction; +var auction = auctionManager.createAuction({adUnits, adUnitCodes, callback: bidsBackHandler, cbTimeout: timeout}); +auction.getBidRequests = getBidRequests; +auction.getBidsReceived = getBidResponses; +auction.getAdUnits = getAdUnits; +auction.getAuctionStatus = function() { return auctionModule.AUCTION_COMPLETED } function resetAuction() { - if (auction == null) { - auction = auctionManager.createAuction({adUnits, adUnitCodes, callback: bidsBackHandler, cbTimeout: timeout, labels: undefined, auctionId: auctionId}); - } $$PREBID_GLOBAL$$.setConfig({ enableSendAllBids: false }); auction.getBidRequests = getBidRequests; auction.getBidsReceived = getBidResponses; @@ -1104,7 +1104,7 @@ describe('Unit: Prebid Module', function () { adUnitCode: config.adUnitCodes[0], }; - resizeRemoteCreative(mockAdObject); + _sendAdToCreative(mockAdObject, sinon.stub()); expect(slots[0].spyGetSlotElementId.called).to.equal(false); expect(slots[1].spyGetSlotElementId.called).to.equal(true); @@ -1233,8 +1233,7 @@ describe('Unit: Prebid Module', function () { } }, getElementsByTagName: sinon.stub(), - querySelector: sinon.stub(), - createElement: sinon.stub(), + querySelector: sinon.stub() }; elStub = { @@ -1265,7 +1264,7 @@ describe('Unit: Prebid Module', function () { it('should require doc and id params', function () { $$PREBID_GLOBAL$$.renderAd(); - var error = 'Error rendering ad (id: undefined): missing adId'; + var error = 'Error trying to write ad Id :undefined to the page. Missing adId'; assert.ok(spyLogError.calledWith(error), 'expected param error was logged'); }); @@ -1290,13 +1289,14 @@ describe('Unit: Prebid Module', function () { adUrl: 'http://server.example.com/ad/ad.js' }); $$PREBID_GLOBAL$$.renderAd(doc, bidId); - sinon.assert.calledWith(doc.createElement, 'iframe'); + assert.ok(elStub.insertBefore.called, 'url was written to iframe in doc'); }); it('should log an error when no ad or url', function () { pushBidResponseToAuction({}); $$PREBID_GLOBAL$$.renderAd(doc, bidId); - sinon.assert.called(spyLogError); + var error = 'Error trying to write ad. No ad for bid response id: ' + bidId; + assert.ok(spyLogError.calledWith(error), 'expected error was logged'); }); it('should log an error when not in an iFrame', function () { @@ -1305,7 +1305,7 @@ describe('Unit: Prebid Module', function () { }); inIframe = false; $$PREBID_GLOBAL$$.renderAd(document, bidId); - const error = `Error rendering ad (id: ${bidId}): renderAd was prevented from writing to the main document.`; + const error = 'Error trying to write ad. Ad render call ad id ' + bidId + ' was prevented from writing to the main document.'; assert.ok(spyLogError.calledWith(error), 'expected error was logged'); }); @@ -1326,14 +1326,14 @@ describe('Unit: Prebid Module', function () { doc.write = sinon.stub().throws(error); $$PREBID_GLOBAL$$.renderAd(doc, bidId); - var errorMessage = `Error rendering ad (id: ${bidId}): doc write error` + var errorMessage = 'Error trying to write ad Id :' + bidId + ' to the page:' + error.message; assert.ok(spyLogError.calledWith(errorMessage), 'expected error was logged'); }); it('should log an error when ad not found', function () { var fakeId = 99; $$PREBID_GLOBAL$$.renderAd(doc, fakeId); - var error = `Error rendering ad (id: ${fakeId}): Cannot find ad '${fakeId}'` + var error = 'Error trying to write ad. Cannot find ad by given id : ' + fakeId; assert.ok(spyLogError.calledWith(error), 'expected error was logged'); }); @@ -1345,6 +1345,14 @@ describe('Unit: Prebid Module', function () { assert.deepEqual($$PREBID_GLOBAL$$.getAllWinningBids()[0], adResponse); }); + it('should replace ${CLICKTHROUGH} macro in winning bids response', function () { + pushBidResponseToAuction({ + ad: "" + }); + $$PREBID_GLOBAL$$.renderAd(doc, bidId, {clickThrough: 'https://someadserverclickurl.com'}); + expect(adResponse).to.have.property('ad').and.to.match(/https:\/\/someadserverclickurl\.com/i); + }); + it('fires billing url if present on s2s bid', function () { const burl = 'http://www.example.com/burl'; pushBidResponseToAuction({ @@ -2728,13 +2736,6 @@ describe('Unit: Prebid Module', function () { events.on.restore(); }); - it('should emit event BID_ACCEPTED when invoked', function () { - var callback = sinon.spy(); - $$PREBID_GLOBAL$$.onEvent('bidAccepted', callback); - events.emit(CONSTANTS.EVENTS.BID_ACCEPTED); - sinon.assert.calledOnce(callback); - }); - describe('beforeRequestBids', function () { let bidRequestedHandler; let beforeRequestBidsHandler; @@ -3303,20 +3304,16 @@ describe('Unit: Prebid Module', function () { const highestBid = $$PREBID_GLOBAL$$.getHighestUnusedBidResponseForAdUnitCode('/19968336/header-bid-tag-0'); expect(highestBid).to.deep.equal(_bidsReceived[2]) }) - }); + }) - describe('getHighestCpmBids', () => { + describe('getHighestCpm', () => { after(() => { resetAuction(); }); it('returns an array containing the highest bid object for the given adUnitCode', function () { - const adUnitcode = '/19968336/header-bid-tag-0'; - targeting.setLatestAuctionForAdUnit(adUnitcode, auctionId) - const highestCpmBids = $$PREBID_GLOBAL$$.getHighestCpmBids(adUnitcode); + const highestCpmBids = $$PREBID_GLOBAL$$.getHighestCpmBids('/19968336/header-bid-tag-0'); expect(highestCpmBids.length).to.equal(1); - const expectedBid = auctionManager.getBidsReceived()[1]; - expectedBid.latestTargetedAuctionId = auctionId; - expect(highestCpmBids[0]).to.deep.equal(expectedBid); + expect(highestCpmBids[0]).to.deep.equal(auctionManager.getBidsReceived()[1]); }); it('returns an empty array when the given adUnit is not found', function () { diff --git a/test/spec/unit/secureCreatives_spec.js b/test/spec/unit/secureCreatives_spec.js index 895bf03165a..7d5f9af35dd 100644 --- a/test/spec/unit/secureCreatives_spec.js +++ b/test/spec/unit/secureCreatives_spec.js @@ -1,4 +1,6 @@ -import {getReplier, receiveMessage} from 'src/secureCreatives.js'; +import { + _sendAdToCreative, getReplier, receiveMessage +} from 'src/secureCreatives.js'; import * as utils from 'src/utils.js'; import {getAdUnits, getBidRequests, getBidResponses} from 'test/fixtures/fixtures.js'; import {auctionManager} from 'src/auctionManager.js'; @@ -6,11 +8,10 @@ import * as auctionModule from 'src/auction.js'; import * as native from 'src/native.js'; import {fireNativeTrackers, getAllAssetsMessage} from 'src/native.js'; import * as events from 'src/events.js'; -import {config as configObj} from 'src/config.js'; +import { config as configObj } from 'src/config.js'; import 'src/prebid.js'; -import {expect} from 'chai'; -import {handleRender} from '../../../src/adRendering.js'; +import { expect } from 'chai'; var CONSTANTS = require('src/constants.json'); @@ -53,45 +54,37 @@ describe('secureCreatives', () => { }); }); - describe('handleRender', () => { - let bidResponse, renderFn, result; - beforeEach(() => { - result = null; - renderFn = sinon.stub().callsFake((r) => { result = r; }); - bidResponse = { - adId: 123 - } + describe('_sendAdToCreative', () => { + beforeEach(function () { + sinon.stub(utils, 'logError'); + sinon.stub(utils, 'logWarn'); }); - it('does not invoke renderFn, but the renderer instead, if the ad has one', () => { - const renderer = { - url: 'some-custom-renderer', - render: sinon.spy() - } - handleRender(renderFn, {bidResponse: {renderer}}); - sinon.assert.notCalled(renderFn); - sinon.assert.called(renderer.render); + afterEach(function () { + utils.logError.restore(); + utils.logWarn.restore(); }); - - ['ad', 'adUrl'].forEach((prop) => { - describe(`on ${prop}`, () => { - it('replaces AUCTION_PRICE macro', () => { - bidResponse[prop] = 'pre${AUCTION_PRICE}post'; - bidResponse.cpm = 123; - handleRender(renderFn, {adId: 123, bidResponse}); - expect(result[prop]).to.eql('pre123post'); - }); - it('replaces CLICKTHROUGH macro', () => { - bidResponse[prop] = 'pre${CLICKTHROUGH}post'; - handleRender(renderFn, {adId: 123, bidResponse, options: {clickUrl: 'clk'}}); - expect(result[prop]).to.eql('preclkpost'); - }); - it('defaults CLICKTHROUGH to empty string', () => { - bidResponse[prop] = 'pre${CLICKTHROUGH}post'; - handleRender(renderFn, {adId: 123, bidResponse}); - expect(result[prop]).to.eql('prepost'); - }); - }); + it('should macro replace ${AUCTION_PRICE} with the winning bid for ad and adUrl', () => { + const oldVal = window.googletag; + const oldapntag = window.apntag; + window.apntag = null + window.googletag = null; + const mockAdObject = { + adId: 'someAdId', + ad: '', + adUrl: 'http://creative.prebid.org/${AUCTION_PRICE}', + width: 300, + height: 250, + renderer: null, + cpm: '1.00', + adUnitCode: 'some_dom_id' + }; + const reply = sinon.spy(); + _sendAdToCreative(mockAdObject, reply); + expect(reply.args[0][0].ad).to.equal(''); + expect(reply.args[0][0].adUrl).to.equal('http://creative.prebid.org/1.00'); + window.googletag = oldVal; + window.apntag = oldapntag; }); }); diff --git a/test/spec/unit/utils/reducers_spec.js b/test/spec/unit/utils/reducers_spec.js deleted file mode 100644 index 95bf3b74041..00000000000 --- a/test/spec/unit/utils/reducers_spec.js +++ /dev/null @@ -1,124 +0,0 @@ -import { - tiebreakCompare, - keyCompare, - simpleCompare, - minimum, - maximum, - getHighestCpm, - getOldestHighestCpmBid, getLatestHighestCpmBid, reverseCompare -} from '../../../../src/utils/reducers.js'; -import assert from 'assert'; - -describe('reducers', () => { - describe('simpleCompare', () => { - Object.entries({ - '<': [10, 20, -1], - '===': [123, 123, 0], - '>': [30, -10, 1] - }).forEach(([t, [a, b, expected]]) => { - it(`returns ${expected} when a ${t} b`, () => { - expect(simpleCompare(a, b)).to.equal(expected); - }) - }) - }); - - describe('keyCompare', () => { - Object.entries({ - '<': [{k: -123}, {k: 0}, -1], - '===': [{k: 0}, {k: 0}, 0], - '>': [{k: 2}, {k: 1}, 1] - }).forEach(([t, [a, b, expected]]) => { - it(`returns ${expected} when key(a) ${t} key(b)`, () => { - expect(keyCompare(item => item.k)(a, b)).to.equal(expected); - }) - }) - }); - - describe('tiebreakCompare', () => { - Object.entries({ - 'first compare says a < b': [{main: 1, tie: 2}, {main: 2, tie: 1}, -1], - 'first compare says a > b': [{main: 2, tie: 1}, {main: 1, tie: 2}, 1], - 'first compare ties, second says a < b': [{main: 0, tie: 1}, {main: 0, tie: 2}, -1], - 'first compare ties, second says a > b': [{main: 0, tie: 2}, {main: 0, tie: 1}, 1], - 'all compares tie': [{main: 0, tie: 0}, {main: 0, tie: 0}, 0] - }).forEach(([t, [a, b, expected]]) => { - it(`should return ${expected} when ${t}`, () => { - const cmp = tiebreakCompare(keyCompare(item => item.main), keyCompare(item => item.tie)); - expect(cmp(a, b)).to.equal(expected); - }) - }) - }); - - const SAMPLE_ARR = [-10, 20, 20, 123, 400]; - - Object.entries({ - 'minimum': [minimum, ['minimum', -10], ['maximum', 400]], - 'maximum': [maximum, ['maximum', 400], ['minimum', -10]] - }).forEach(([t, [fn, simple, reversed]]) => { - describe(t, () => { - it(`should find ${simple[0]} using simple compare`, () => { - expect(SAMPLE_ARR.reduce(fn(simpleCompare))).to.equal(simple[1]); - }); - it(`should find ${reversed[0]} using reverse compare`, () => { - expect(SAMPLE_ARR.reduce(fn(reverseCompare()))).to.equal(reversed[1]); - }); - }) - }); - - describe('getHighestCpm', function () { - it('should pick the highest cpm', function () { - let a = { - cpm: 2, - timeToRespond: 100 - }; - let b = { - cpm: 1, - timeToRespond: 100 - }; - expect(getHighestCpm(a, b)).to.eql(a); - expect(getHighestCpm(b, a)).to.eql(a); - }); - - it('should pick the lowest timeToRespond cpm in case of tie', function () { - let a = { - cpm: 1, - timeToRespond: 100 - }; - let b = { - cpm: 1, - timeToRespond: 50 - }; - expect(getHighestCpm(a, b)).to.eql(b); - expect(getHighestCpm(b, a)).to.eql(b); - }); - }); - - describe('getOldestHighestCpmBid', () => { - it('should pick the oldest in case of tie using responseTimeStamp', function () { - let a = { - cpm: 1, - responseTimestamp: 1000 - }; - let b = { - cpm: 1, - responseTimestamp: 2000 - }; - expect(getOldestHighestCpmBid(a, b)).to.eql(a); - expect(getOldestHighestCpmBid(b, a)).to.eql(a); - }); - }); - describe('getLatestHighestCpmBid', () => { - it('should pick the latest in case of tie using responseTimeStamp', function () { - let a = { - cpm: 1, - responseTimestamp: 1000 - }; - let b = { - cpm: 1, - responseTimestamp: 2000 - }; - expect(getLatestHighestCpmBid(a, b)).to.eql(b); - expect(getLatestHighestCpmBid(b, a)).to.eql(b); - }); - }); -}) diff --git a/test/spec/unit/utils/ttlCollection_spec.js b/test/spec/unit/utils/ttlCollection_spec.js deleted file mode 100644 index 29c6c438855..00000000000 --- a/test/spec/unit/utils/ttlCollection_spec.js +++ /dev/null @@ -1,180 +0,0 @@ -import {ttlCollection} from '../../../../src/utils/ttlCollection.js'; - -describe('ttlCollection', () => { - it('can add & retrieve items', () => { - const coll = ttlCollection(); - expect(coll.toArray()).to.eql([]); - coll.add(1); - coll.add(2); - expect(coll.toArray()).to.eql([1, 2]); - }); - - it('can clear', () => { - const coll = ttlCollection(); - coll.add('item'); - coll.clear(); - expect(coll.toArray()).to.eql([]); - }); - - it('can be iterated over', () => { - const coll = ttlCollection(); - coll.add('1'); - coll.add('2'); - expect(Array.from(coll)).to.eql(['1', '2']); - }) - - describe('autopurge', () => { - let clock, pms, waitForPromises; - const SLACK = 2000; - beforeEach(() => { - clock = sinon.useFakeTimers(); - pms = []; - waitForPromises = () => Promise.all(pms); - }); - afterEach(() => { - clock.restore(); - }); - - Object.entries({ - 'defer': (value) => { - const pm = Promise.resolve(value); - pms.push(pm); - return pm; - }, - 'do not defer': (value) => value, - }).forEach(([t, resolve]) => { - describe(`when ttl/startTime ${t}`, () => { - let coll; - beforeEach(() => { - coll = ttlCollection({ - startTime: (item) => resolve(item.start == null ? new Date().getTime() : item.start), - ttl: (item) => resolve(item.ttl), - slack: SLACK - }) - }); - - it('should clear items after enough time has passed', () => { - coll.add({no: 'ttl'}); - coll.add({ttl: 1000}); - coll.add({ttl: 4000}); - return waitForPromises().then(() => { - clock.tick(500); - expect(coll.toArray()).to.eql([{no: 'ttl'}, {ttl: 1000}, {ttl: 4000}]); - clock.tick(SLACK + 500); - expect(coll.toArray()).to.eql([{no: 'ttl'}, {ttl: 4000}]); - clock.tick(3000); - expect(coll.toArray()).to.eql([{no: 'ttl'}]); - }); - }); - - it('should not wait too long if a shorter ttl shows up', () => { - coll.add({ttl: 4000}); - coll.add({ttl: 1000}); - return waitForPromises().then(() => { - clock.tick(1000 + SLACK); - expect(coll.toArray()).to.eql([ - {ttl: 4000} - ]); - }); - }); - - it('should not wait more if later ttls are within slack', () => { - coll.add({start: 0, ttl: 4000}); - return waitForPromises().then(() => { - clock.tick(4000); - coll.add({start: 0, ttl: 5000}); - return waitForPromises().then(() => { - clock.tick(SLACK); - expect(coll.toArray()).to.eql([]); - }); - }); - }); - - it('should clear items ASAP if they expire in the past', () => { - clock.tick(10000); - coll.add({start: 0, ttl: 1000}); - return waitForPromises().then(() => { - clock.tick(SLACK); - expect(coll.toArray()).to.eql([]); - }); - }); - - it('should clear items ASAP if they have ttl = 0', () => { - coll.add({ttl: 0}); - return waitForPromises().then(() => { - clock.tick(SLACK); - expect(coll.toArray()).to.eql([]); - }); - }); - - describe('refresh', () => { - it('should refresh missing TTLs', () => { - const item = {}; - coll.add(item); - return waitForPromises().then(() => { - item.ttl = 1000; - return waitForPromises().then(() => { - clock.tick(1000 + SLACK); - expect(coll.toArray()).to.eql([item]); - coll.refresh(); - return waitForPromises().then(() => { - clock.tick(1); - expect(coll.toArray()).to.eql([]); - }); - }); - }); - }); - - it('should refresh existing TTLs', () => { - const item = { - ttl: 1000 - }; - coll.add(item); - return waitForPromises().then(() => { - clock.tick(1000); - item.ttl = 4000; - coll.refresh(); - return waitForPromises().then(() => { - clock.tick(SLACK); - expect(coll.toArray()).to.eql([item]); - clock.tick(3000); - expect(coll.toArray()).to.eql([]); - }); - }); - }); - - it('should discard initial TTL if it does not resolve before a refresh', () => { - let resolveTTL; - const item = { - ttl: new Promise((resolve) => { - resolveTTL = resolve; - }) - }; - coll.add(item); - item.ttl = null; - coll.refresh(); - resolveTTL(1000); - return waitForPromises().then(() => { - clock.tick(1000 + SLACK + 1000); - expect(coll.toArray()).to.eql([item]); - }); - }); - - it('should discard TTLs on clear', () => { - const item = { - ttl: 1000 - }; - coll.add(item); - coll.clear(); - item.ttl = null; - coll.add(item); - return waitForPromises().then(() => { - clock.tick(1000 + SLACK + 1000); - expect(coll.toArray()).to.eql([item]); - }); - }); - }); - }); - }); - }); -}); diff --git a/test/spec/utils_spec.js b/test/spec/utils_spec.js index 098582c0af6..e26683074c8 100644 --- a/test/spec/utils_spec.js +++ b/test/spec/utils_spec.js @@ -2,9 +2,7 @@ import {getAdServerTargeting} from 'test/fixtures/fixtures.js'; import {expect} from 'chai'; import CONSTANTS from 'src/constants.json'; import * as utils from 'src/utils.js'; -import {getHighestCpm, getLatestHighestCpmBid, getOldestHighestCpmBid} from '../../src/utils/reducers.js'; -import {binarySearch, deepEqual, memoize, waitForElementToLoad} from 'src/utils.js'; -import {convertCamelToUnderscore} from '../../libraries/appnexusUtils/anUtils.js'; +import {deepEqual, memoize, waitForElementToLoad} from 'src/utils.js'; var assert = require('assert'); @@ -41,6 +39,28 @@ describe('Utils', function () { }); }); + describe('tryAppendQueryString', function () { + it('should append query string to existing url', function () { + var url = 'www.a.com?'; + var key = 'b'; + var value = 'c'; + + var output = utils.tryAppendQueryString(url, key, value); + + var expectedResult = url + key + '=' + encodeURIComponent(value) + '&'; + assert.equal(output, expectedResult); + }); + + it('should return existing url, if the value is empty', function () { + var url = 'www.a.com?'; + var key = 'b'; + var value = ''; + + var output = utils.tryAppendQueryString(url, key, value); + assert.equal(output, url); + }); + }); + describe('parseQueryStringParameters', function () { it('should append query string to existing using the input obj', function () { var obj = { @@ -518,6 +538,72 @@ describe('Utils', function () { }); }); + describe('getHighestCpm', function () { + it('should pick the existing highest cpm', function () { + let previous = { + cpm: 2, + timeToRespond: 100 + }; + let current = { + cpm: 1, + timeToRespond: 100 + }; + assert.equal(utils.getHighestCpm(previous, current), previous); + }); + + it('should pick the new highest cpm', function () { + let previous = { + cpm: 1, + timeToRespond: 100 + }; + let current = { + cpm: 2, + timeToRespond: 100 + }; + assert.equal(utils.getHighestCpm(previous, current), current); + }); + + it('should pick the fastest cpm in case of tie', function () { + let previous = { + cpm: 1, + timeToRespond: 100 + }; + let current = { + cpm: 1, + timeToRespond: 50 + }; + assert.equal(utils.getHighestCpm(previous, current), current); + }); + + it('should pick the oldest in case of tie using responseTimeStamp', function () { + let previous = { + cpm: 1, + timeToRespond: 100, + responseTimestamp: 1000 + }; + let current = { + cpm: 1, + timeToRespond: 50, + responseTimestamp: 2000 + }; + assert.equal(utils.getOldestHighestCpmBid(previous, current), previous); + }); + + it('should pick the latest in case of tie using responseTimeStamp', function () { + let previous = { + cpm: 1, + timeToRespond: 100, + responseTimestamp: 1000 + }; + let current = { + cpm: 1, + timeToRespond: 50, + responseTimestamp: 2000 + }; + assert.equal(utils.getLatestHighestCpmBid(previous, current), current); + }); + }); + describe('polyfill test', function () { it('should not add polyfill to array', function() { var arr = ['hello', 'world']; @@ -679,15 +765,43 @@ describe('Utils', function () { describe('convertCamelToUnderscore', function () { it('returns converted string value using underscore syntax instead of camelCase', function () { let var1 = 'placementIdTest'; - let test1 = convertCamelToUnderscore(var1); + let test1 = utils.convertCamelToUnderscore(var1); expect(test1).to.equal('placement_id_test'); let var2 = 'my_test_value'; - let test2 = convertCamelToUnderscore(var2); + let test2 = utils.convertCamelToUnderscore(var2); expect(test2).to.equal(var2); }); }); + describe('getAdUnitSizes', function () { + it('returns an empty response when adUnits is undefined', function () { + let sizes = utils.getAdUnitSizes(); + expect(sizes).to.be.undefined; + }); + + it('returns an empty array when invalid data is present in adUnit object', function () { + let sizes = utils.getAdUnitSizes({ sizes: 300 }); + expect(sizes).to.deep.equal([]); + }); + + it('retuns an array of arrays when reading from adUnit.sizes', function () { + let sizes = utils.getAdUnitSizes({ sizes: [300, 250] }); + expect(sizes).to.deep.equal([[300, 250]]); + + sizes = utils.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 = utils.getAdUnitSizes({ mediaTypes: { banner: { sizes: [300, 250] } } }); + expect(sizes).to.deep.equal([[300, 250]]); + + sizes = utils.getAdUnitSizes({ mediaTypes: { banner: { sizes: [[300, 250], [300, 600]] } } }); + expect(sizes).to.deep.equal([[300, 250], [300, 600]]); + }); + }); + describe('URL helpers', function () { describe('parseUrl()', function () { let parsed; @@ -1119,44 +1233,5 @@ describe('memoize', () => { mem('one', 'three'); expect(mem('one', 'three')).to.eql(['one', 'three']); expect(fn.callCount).to.eql(2); - }); - - describe('binarySearch', () => { - [ - { - arr: [], - tests: [ - ['any', 0] - ] - }, - { - arr: [10], - tests: [ - [5, 0], - [10, 0], - [20, 1], - ], - }, - { - arr: [10, 20, 30, 30, 40], - tests: [ - [5, 0], - [15, 1], - [10, 0], - [30, 2], - [35, 4], - [40, 4], - [100, 5] - ] - } - ].forEach(({arr, tests}) => { - describe(`on ${arr}`, () => { - tests.forEach(([el, pos]) => { - it(`finds index for ${el} => ${pos}`, () => { - expect(binarySearch(arr, el)).to.equal(pos); - }); - }); - }); - }) - }); + }) }) diff --git a/test/spec/videoCache_spec.js b/test/spec/videoCache_spec.js index c746fdd2afd..c7c0b2eb329 100644 --- a/test/spec/videoCache_spec.js +++ b/test/spec/videoCache_spec.js @@ -174,7 +174,7 @@ describe('The video cache', function () { const request = server.requests[0]; request.method.should.equal('POST'); request.url.should.equal('https://prebid.adnxs.com/pbc/v1/cache'); - request.requestHeaders['Content-Type'].should.equal('text/plain'); + request.requestHeaders['Content-Type'].should.equal('text/plain;charset=utf-8'); let payload = { puts: [{ type: 'xml', @@ -224,7 +224,7 @@ describe('The video cache', function () { const request = server.requests[0]; request.method.should.equal('POST'); request.url.should.equal('https://prebid.adnxs.com/pbc/v1/cache'); - request.requestHeaders['Content-Type'].should.equal('text/plain'); + request.requestHeaders['Content-Type'].should.equal('text/plain;charset=utf-8'); let payload = { puts: [{ type: 'xml', @@ -295,7 +295,7 @@ describe('The video cache', function () { const request = server.requests[0]; request.method.should.equal('POST'); request.url.should.equal('https://prebid.adnxs.com/pbc/v1/cache'); - request.requestHeaders['Content-Type'].should.equal('text/plain'); + request.requestHeaders['Content-Type'].should.equal('text/plain;charset=utf-8'); let payload = { puts: [{ type: 'xml', @@ -356,7 +356,7 @@ describe('The video cache', function () { const request = server.requests[0]; request.method.should.equal('POST'); request.url.should.equal('https://prebid.adnxs.com/pbc/v1/cache'); - request.requestHeaders['Content-Type'].should.equal('text/plain'); + request.requestHeaders['Content-Type'].should.equal('text/plain;charset=utf-8'); JSON.parse(request.requestBody).should.deep.equal({ puts: [{ diff --git a/test/spec/video_spec.js b/test/spec/video_spec.js index 35d0a4fef24..61621c7ec42 100644 --- a/test/spec/video_spec.js +++ b/test/spec/video_spec.js @@ -1,4 +1,4 @@ -import {fillVideoDefaults, isValidVideoBid} from 'src/video.js'; +import { isValidVideoBid } from 'src/video.js'; import {hook} from '../../src/hook.js'; import {stubAuctionIndex} from '../helpers/indexStub.js'; @@ -7,169 +7,97 @@ describe('video.js', function () { hook.ready(); }); - describe('fillVideoDefaults', () => { - function fillDefaults(videoMediaType = {}) { - const adUnit = {mediaTypes: {video: videoMediaType}}; - fillVideoDefaults(adUnit); - return adUnit.mediaTypes.video; - } - - describe('should set plcmt = 4 when', () => { - it('context is "outstream"', () => { - expect(fillDefaults({context: 'outstream'})).to.eql({ - context: 'outstream', - plcmt: 4 - }) - }); - [2, 3, 4].forEach(placement => { - it(`placemement is "${placement}"`, () => { - expect(fillDefaults({placement})).to.eql({ - placement, - plcmt: 4 - }); - }) - }); - }); - describe('should set plcmt = 2 when', () => { - [2, 6].forEach(playbackmethod => { - it(`playbackmethod is "${playbackmethod}"`, () => { - expect(fillDefaults({playbackmethod})).to.eql({ - playbackmethod, - plcmt: 2, - }); - }); - }); - }); - describe('should not set plcmt when', () => { - Object.entries({ - 'it was set by pub (context=outstream)': { - expected: 1, - video: { - context: 'outstream', - plcmt: 1 - } - }, - 'it was set by pub (placement=2)': { - expected: 1, - video: { - placement: 2, - plcmt: 1 - } - }, - 'placement not in 2, 3, 4': { - expected: undefined, - video: { - placement: 1 - } - }, - 'it was set by pub (playbackmethod=2)': { - expected: 1, - video: { - plcmt: 1, - playbackmethod: 2 - } - } - }).forEach(([t, {expected, video}]) => { - it(t, () => { - expect(fillDefaults(video).plcmt).to.eql(expected); - }) - }) - }) - }) - - describe('isValidVideoBid', () => { - it('validates valid instream bids', function () { - const bid = { - adId: '456xyz', - vastUrl: 'http://www.example.com/vastUrl', - transactionId: 'au' - }; - const adUnits = [{ - transactionId: 'au', - mediaTypes: { - video: {context: 'instream'} - } - }]; - const valid = isValidVideoBid(bid, {index: stubAuctionIndex({adUnits})}); - expect(valid).to.equal(true); - }); + it('validates valid instream bids', function () { + const bid = { + adId: '456xyz', + vastUrl: 'http://www.example.com/vastUrl', + transactionId: 'au' + }; + const adUnits = [{ + transactionId: 'au', + mediaTypes: { + video: {context: 'instream'} + } + }]; + const valid = isValidVideoBid(bid, {index: stubAuctionIndex({adUnits})}); + expect(valid).to.equal(true); + }); - it('catches invalid instream bids', function () { - const bid = { - transactionId: 'au' - }; - const adUnits = [{ - transactionId: 'au', - mediaTypes: { - video: {context: 'instream'} - } - }]; - const valid = isValidVideoBid(bid, {index: stubAuctionIndex({adUnits})}); - expect(valid).to.equal(false); - }); + it('catches invalid instream bids', function () { + const bid = { + transactionId: 'au' + }; + const adUnits = [{ + transactionId: 'au', + mediaTypes: { + video: {context: 'instream'} + } + }]; + const valid = isValidVideoBid(bid, {index: stubAuctionIndex({adUnits})}); + expect(valid).to.equal(false); + }); - it('catches invalid bids when prebid-cache is disabled', function () { - const adUnits = [{ - transactionId: 'au', - bidder: 'vastOnlyVideoBidder', - mediaTypes: {video: {}}, - }]; + it('catches invalid bids when prebid-cache is disabled', function () { + const adUnits = [{ + transactionId: 'au', + bidder: 'vastOnlyVideoBidder', + mediaTypes: {video: {}}, + }]; - const valid = isValidVideoBid({ transactionId: 'au', vastXml: 'vast' }, {index: stubAuctionIndex({adUnits})}); + const valid = isValidVideoBid({ transactionId: 'au', vastXml: 'vast' }, {index: stubAuctionIndex({adUnits})}); - expect(valid).to.equal(false); - }); + expect(valid).to.equal(false); + }); - it('validates valid outstream bids', function () { - const bid = { - transactionId: 'au', - renderer: { - url: 'render.url', - render: () => true, - } - }; - const adUnits = [{ - transactionId: 'au', - mediaTypes: { - video: {context: 'outstream'} - } - }]; - const valid = isValidVideoBid(bid, {index: stubAuctionIndex({adUnits})}); - expect(valid).to.equal(true); - }); + it('validates valid outstream bids', function () { + const bid = { + transactionId: 'au', + renderer: { + url: 'render.url', + render: () => true, + } + }; + const adUnits = [{ + transactionId: 'au', + mediaTypes: { + video: {context: 'outstream'} + } + }]; + const valid = isValidVideoBid(bid, {index: stubAuctionIndex({adUnits})}); + expect(valid).to.equal(true); + }); - it('validates valid outstream bids with a publisher defined renderer', function () { - const bid = { - transactionId: 'au', - }; - const adUnits = [{ - transactionId: 'au', - mediaTypes: { - video: { - context: 'outstream', - } - }, - renderer: { - url: 'render.url', - render: () => true, + it('validates valid outstream bids with a publisher defined renderer', function () { + const bid = { + transactionId: 'au', + }; + const adUnits = [{ + transactionId: 'au', + mediaTypes: { + video: { + context: 'outstream', } - }]; - const valid = isValidVideoBid(bid, {index: stubAuctionIndex({adUnits})}); - expect(valid).to.equal(true); - }); + }, + renderer: { + url: 'render.url', + render: () => true, + } + }]; + const valid = isValidVideoBid(bid, {index: stubAuctionIndex({adUnits})}); + expect(valid).to.equal(true); + }); - it('catches invalid outstream bids', function () { - const bid = { - transactionId: 'au', - }; - const adUnits = [{ - transactionId: 'au', - mediaTypes: { - video: {context: 'outstream'} - } - }]; - const valid = isValidVideoBid(bid, {index: stubAuctionIndex({adUnits})}); - expect(valid).to.equal(false); - }); - }) + it('catches invalid outstream bids', function () { + const bid = { + transactionId: 'au', + }; + const adUnits = [{ + transactionId: 'au', + mediaTypes: { + video: {context: 'outstream'} + } + }]; + const valid = isValidVideoBid(bid, {index: stubAuctionIndex({adUnits})}); + expect(valid).to.equal(false); + }); }); diff --git a/test/test_deps.js b/test/test_deps.js index c8a3bcc9426..35713106f8c 100644 --- a/test/test_deps.js +++ b/test/test_deps.js @@ -4,16 +4,6 @@ window.process = { } }; -window.addEventListener('error', function (ev) { - // eslint-disable-next-line no-console - console.error('Uncaught exception:', ev.error, ev.error?.stack); -}) - -window.addEventListener('unhandledrejection', function (ev) { - // eslint-disable-next-line no-console - console.error('Unhandled rejection:', ev.reason); -}) - require('test/helpers/consentData.js'); require('test/helpers/prebidGlobal.js'); require('test/mocks/adloaderStub.js'); diff --git a/webpack.creative.js b/webpack.creative.js deleted file mode 100644 index 7279455e155..00000000000 --- a/webpack.creative.js +++ /dev/null @@ -1,19 +0,0 @@ -const path = require('path'); - -module.exports = { - mode: 'production', - resolve: { - modules: [ - path.resolve('.'), - 'node_modules' - ], - }, - entry: { - 'creative': { - import: './libraries/creativeRender/crossDomain.js', - }, - }, - output: { - path: path.resolve('./build/creative'), - }, -}