diff --git a/integrationExamples/videoModule/jwplayer/bidMarkedAsUsed.html b/integrationExamples/videoModule/jwplayer/bidMarkedAsUsed.html index d0b261043e4..2246f4e76e3 100644 --- a/integrationExamples/videoModule/jwplayer/bidMarkedAsUsed.html +++ b/integrationExamples/videoModule/jwplayer/bidMarkedAsUsed.html @@ -48,7 +48,9 @@ params: { vendorConfig: { file: 'http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/SubaruOutbackOnStreetAndDirt.mp4', - advertising: { client: 'vast' } + advertising: { client: 'vast' }, + width: 640, + height: 480 } } }, diff --git a/integrationExamples/videoModule/jwplayer/bidRequestScheduling.html b/integrationExamples/videoModule/jwplayer/bidRequestScheduling.html index c40af32cac2..fac6b51b44e 100644 --- a/integrationExamples/videoModule/jwplayer/bidRequestScheduling.html +++ b/integrationExamples/videoModule/jwplayer/bidRequestScheduling.html @@ -24,6 +24,8 @@ params: { vendorConfig: { file: 'http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/SubaruOutbackOnStreetAndDirt.mp4', + width: 640, + height: 480, advertising: { client: "googima", schedule: { diff --git a/integrationExamples/videoModule/jwplayer/bidsBackHandlerOverride.html b/integrationExamples/videoModule/jwplayer/bidsBackHandlerOverride.html index 75a72ba3501..308809caa87 100644 --- a/integrationExamples/videoModule/jwplayer/bidsBackHandlerOverride.html +++ b/integrationExamples/videoModule/jwplayer/bidsBackHandlerOverride.html @@ -47,6 +47,8 @@ params: { vendorConfig: { file: 'http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/SubaruOutbackOnStreetAndDirt.mp4', + width: 640, + height: 480, advertising: { client: 'vast' } } } diff --git a/integrationExamples/videoModule/jwplayer/eventListeners.html b/integrationExamples/videoModule/jwplayer/eventListeners.html index 6f04f37264b..e6f49af3e24 100644 --- a/integrationExamples/videoModule/jwplayer/eventListeners.html +++ b/integrationExamples/videoModule/jwplayer/eventListeners.html @@ -49,6 +49,8 @@ vendorConfig: { file: 'http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/SubaruOutbackOnStreetAndDirt.mp4', mediaid: 'd9J2zcaA', + width: 640, + height: 480, advertising: { client: 'vast' } } } diff --git a/integrationExamples/videoModule/jwplayer/eventsUI.html b/integrationExamples/videoModule/jwplayer/eventsUI.html index cfd1efe7624..f86721a8b7e 100644 --- a/integrationExamples/videoModule/jwplayer/eventsUI.html +++ b/integrationExamples/videoModule/jwplayer/eventsUI.html @@ -53,6 +53,8 @@ file: 'http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/SubaruOutbackOnStreetAndDirt.mp4', title: "Subaru Outback on Street and Dirt", }], + width: 640, + height: 480, advertising: { client: 'vast' } } } diff --git a/integrationExamples/videoModule/jwplayer/gamAdServerMediation.html b/integrationExamples/videoModule/jwplayer/gamAdServerMediation.html index 1f4331785ea..b8228615fae 100644 --- a/integrationExamples/videoModule/jwplayer/gamAdServerMediation.html +++ b/integrationExamples/videoModule/jwplayer/gamAdServerMediation.html @@ -47,6 +47,8 @@ params: { vendorConfig: { file: 'http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/SubaruOutbackOnStreetAndDirt.mp4', + width: 640, + height: 480, advertising: { client: 'googima' } } } diff --git a/integrationExamples/videoModule/jwplayer/mediaMetadata.html b/integrationExamples/videoModule/jwplayer/mediaMetadata.html index 63e62aa4b82..11762733f18 100644 --- a/integrationExamples/videoModule/jwplayer/mediaMetadata.html +++ b/integrationExamples/videoModule/jwplayer/mediaMetadata.html @@ -51,7 +51,9 @@ file: 'http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/SubaruOutbackOnStreetAndDirt.mp4', title: 'Subaru Outback On Street And Dirt', description: 'Smoking Tire takes the all-new Subaru Outback to the highest point we can find in hopes our customer-appreciation Balloon Launch will get some free T-shirts into the hands of our viewers.', - advertising: { client: 'googima' } + advertising: { client: 'googima' }, + width: 640, + height: 480 } } } diff --git a/integrationExamples/videoModule/jwplayer/playlist.html b/integrationExamples/videoModule/jwplayer/playlist.html index 9e89f606f23..447817ffd80 100644 --- a/integrationExamples/videoModule/jwplayer/playlist.html +++ b/integrationExamples/videoModule/jwplayer/playlist.html @@ -63,7 +63,9 @@ title : "Sintel", description: "Sintel is an independently produced short film, initiated by the Blender Foundation as a means to further improve and validate the free/open source 3D creation suite Blender. With initial funding provided by 1000s of donations via the internet community, it has again proven to be a viable development model for both open 3D technology as for independent animation film.\nThis 15 minute film has been realized in the studio of the Amsterdam Blender Institute, by an international team of artists and developers. In addition to that, several crucial technical and creative targets have been realized online, by developers and artists and teams all over the world.\nwww.sintel.org", }], - advertising: { client: 'vast' } + advertising: { client: 'vast' }, + width: 640, + height: 480 } } } diff --git a/libraries/vidazooUtils/bidderUtils.js b/libraries/vidazooUtils/bidderUtils.js index df947142a4c..9de95248c74 100644 --- a/libraries/vidazooUtils/bidderUtils.js +++ b/libraries/vidazooUtils/bidderUtils.js @@ -247,6 +247,7 @@ export function buildRequestData(bid, topWindowUrl, sizes, bidderRequest, bidder const userData = deepAccess(bidderRequest, 'ortb2.user.data', []); const contentLang = deepAccess(bidderRequest, 'ortb2.site.content.language') || document.documentElement.lang; const coppa = deepAccess(bidderRequest, 'ortb2.regs.coppa', 0); + const device = deepAccess(bidderRequest, 'ortb2.device', {}); if (isFn(bid.getFloor)) { const floorInfo = bid.getFloor({ @@ -290,6 +291,7 @@ export function buildRequestData(bid, topWindowUrl, sizes, bidderRequest, bidder bidderRequestsCount: bidderRequestsCount, bidderWinsCount: bidderWinsCount, bidderTimeout: bidderTimeout, + device, ...uniqueRequestData }; diff --git a/modules/conceptxBidAdapter.js b/modules/conceptxBidAdapter.js index 87ac96f2131..47c50a4c0ad 100644 --- a/modules/conceptxBidAdapter.js +++ b/modules/conceptxBidAdapter.js @@ -5,10 +5,12 @@ import { BANNER } from '../src/mediaTypes.js'; const BIDDER_CODE = 'conceptx'; const ENDPOINT_URL = 'https://conceptx.cncpt-central.com/openrtb'; // const LOG_PREFIX = 'ConceptX: '; +const GVLID = 1340; export const spec = { code: BIDDER_CODE, supportedMediaTypes: [BANNER], + gvlid: GVLID, isBidRequestValid: function (bid) { return !!(bid.bidId && bid.params.site && bid.params.adunit); }, diff --git a/modules/ixBidAdapter.js b/modules/ixBidAdapter.js index 0ec60b51ca2..e2a712e0afc 100644 --- a/modules/ixBidAdapter.js +++ b/modules/ixBidAdapter.js @@ -751,8 +751,9 @@ function buildRequest(validBidRequests, bidderRequest, impressions, version) { method: 'POST', url: exchangeUrl, data: deepClone(r), - option: { + options: { contentType: 'text/plain', + withCredentials: true }, validBidRequests }); diff --git a/modules/jwplayerVideoProvider.js b/modules/jwplayerVideoProvider.js index 54de1949e6f..e2b194057fd 100644 --- a/modules/jwplayerVideoProvider.js +++ b/modules/jwplayerVideoProvider.js @@ -50,6 +50,8 @@ export function JWPlayerProvider(config, jwplayer_, adState_, timeState_, callba VIDEO_MIME_TYPE.AAC, VIDEO_MIME_TYPE.HLS ]; + let height = null; + let width = null; function init() { if (!jwplayer) { @@ -92,6 +94,20 @@ export function JWPlayerProvider(config, jwplayer_, adState_, timeState_, callba const adConfig = config.advertising || {}; supportedMediaTypes = supportedMediaTypes || utils.getSupportedMediaTypes(MEDIA_TYPES); + if (height === null) { + height = utils.getPlayerHeight(player, config); + } + + if (width === null) { + width = utils.getPlayerWidth(player, config); + } + + if (config.aspectratio && !height && !width) { + const size = utils.getPlayerSizeFromAspectRatio(player, config); + height = size.height; + width = size.width; + } + const video = { mimes: supportedMediaTypes, protocols: [ @@ -102,8 +118,8 @@ export function JWPlayerProvider(config, jwplayer_, adState_, timeState_, callba PROTOCOLS.VAST_3_0_WRAPPER, PROTOCOLS.VAST_4_0_WRAPPER ], - h: player.getHeight(), // TODO does player call need optimization ? - w: player.getWidth(), // TODO does player call need optimization ? + h: height, + w: width, startdelay: utils.getStartDelay(), placement: utils.getPlacement(adConfig, player), // linearity is omitted because both forms are supported. @@ -414,10 +430,14 @@ export function JWPlayerProvider(config, jwplayer_, adState_, timeState_, callba break; case PLAYER_RESIZE: - getEventPayload = e => ({ - height: e.height, - width: e.width, - }); + getEventPayload = e => { + height = e.height; + width = e.width; + return { + height, + width + }; + }; break; case VIEWABLE: @@ -585,6 +605,79 @@ export const utils = { return jwConfig; }, + getPlayerHeight: function(player, config) { + let height; + + if (player.getHeight) { + height = player.getHeight(); + } + + // Height is undefined when player has not yet rendered + if (height !== undefined) { + return height; + } + + return config.height; + }, + + getPlayerWidth: function(player, config) { + let width; + + if (player.getWidth) { + width = player.getWidth(); + } + + // Width is undefined when player has not yet rendered + if (width !== undefined) { + return width; + } + + // Width can be a string when aspectratio is set + if (typeof config.width === 'number') { + return config.width; + } + }, + + getPlayerSizeFromAspectRatio: function(player, config) { + const aspectRatio = config.aspectratio; + let percentageWidth = config.width; + + if (typeof aspectRatio !== 'string' || typeof percentageWidth !== 'string') { + return {}; + } + + const ratios = aspectRatio.split(':'); + + if (ratios.length !== 2) { + return {}; + } + + const containerElement = player.getContainer && player.getContainer(); + if (!containerElement) { + return {}; + } + + const containerWidth = containerElement.clientWidth; + const containerHeight = containerElement.clientHeight; + + const xRatio = parseInt(ratios[0], 10); + const yRatio = parseInt(ratios[1], 10); + + if (isNaN(xRatio) || isNaN(yRatio)) { + return {}; + } + + const numericWidthPercentage = parseInt(percentageWidth, 10); + + const desiredWidth = containerWidth * numericWidthPercentage / 100; + const desiredHeight = Math.min(desiredWidth * yRatio / xRatio, containerHeight); + + return { + height: desiredHeight, + width: desiredWidth + }; + }, + getJwEvent: function(eventName) { switch (eventName) { case SETUP_COMPLETE: diff --git a/test/spec/modules/excoBidAdapter_spec.js b/test/spec/modules/excoBidAdapter_spec.js index 39844f0bc6a..be85d7735d6 100644 --- a/test/spec/modules/excoBidAdapter_spec.js +++ b/test/spec/modules/excoBidAdapter_spec.js @@ -94,6 +94,36 @@ const VIDEO_BID = { } } +const ORTB2_DEVICE = { + sua: { + 'source': 2, + 'platform': { + 'brand': 'Android', + 'version': ['8', '0', '0'] + }, + 'browsers': [ + {'brand': 'Not_A Brand', 'version': ['99', '0', '0', '0']}, + {'brand': 'Google Chrome', 'version': ['109', '0', '5414', '119']}, + {'brand': 'Chromium', 'version': ['109', '0', '5414', '119']} + ], + 'mobile': 1, + 'model': 'SM-G955U', + 'bitness': '64', + 'architecture': '' + }, + w: 980, + h: 1720, + dnt: 0, + ua: 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/125.0.6422.80 Mobile/15E148 Safari/604.1', + language: 'en', + devicetype: 1, + make: 'Apple', + model: 'iPhone 12 Pro Max', + os: 'iOS', + osv: '17.4', + ext: {fiftyonedegrees_deviceId: '17595-133085-133468-18092'}, +}; + const BIDDER_REQUEST = { 'gdprConsent': { 'consentString': 'consent_string', @@ -117,24 +147,7 @@ const BIDDER_REQUEST = { 'gpp_sid': [7], 'coppa': 0 }, - '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': '' - } - } + 'device': ORTB2_DEVICE, } }; @@ -315,6 +328,7 @@ describe('ExcoBidAdapter', function () { 'bitness': '64', 'architecture': '' }, + device: ORTB2_DEVICE, uniqueDealId: `${hashUrl}_${Date.now().toString()}`, uqs: getTopWindowQueryParams(), mediaTypes: { @@ -386,6 +400,7 @@ describe('ExcoBidAdapter', function () { 'bitness': '64', 'architecture': '' }, + device: ORTB2_DEVICE, url: 'https%3A%2F%2Fwww.greatsite.com', referrer: 'https://www.somereferrer.com', cb: 1000, diff --git a/test/spec/modules/illuminBidAdapter_spec.js b/test/spec/modules/illuminBidAdapter_spec.js index 3cd79c7468d..b4578a8d61f 100644 --- a/test/spec/modules/illuminBidAdapter_spec.js +++ b/test/spec/modules/illuminBidAdapter_spec.js @@ -94,6 +94,36 @@ const VIDEO_BID = { } } +const ORTB2_DEVICE = { + sua: { + 'source': 2, + 'platform': { + 'brand': 'Android', + 'version': ['8', '0', '0'] + }, + 'browsers': [ + {'brand': 'Not_A Brand', 'version': ['99', '0', '0', '0']}, + {'brand': 'Google Chrome', 'version': ['109', '0', '5414', '119']}, + {'brand': 'Chromium', 'version': ['109', '0', '5414', '119']} + ], + 'mobile': 1, + 'model': 'SM-G955U', + 'bitness': '64', + 'architecture': '' + }, + w: 980, + h: 1720, + dnt: 0, + ua: 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/125.0.6422.80 Mobile/15E148 Safari/604.1', + language: 'en', + devicetype: 1, + make: 'Apple', + model: 'iPhone 12 Pro Max', + os: 'iOS', + osv: '17.4', + ext: {fiftyonedegrees_deviceId: '17595-133085-133468-18092'}, +}; + const BIDDER_REQUEST = { 'gdprConsent': { 'consentString': 'consent_string', @@ -117,24 +147,7 @@ const BIDDER_REQUEST = { 'gpp_sid': [7], 'coppa': 0 }, - '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': '' - } - } + 'device': ORTB2_DEVICE, } }; @@ -315,6 +328,7 @@ describe('IlluminBidAdapter', function () { 'bitness': '64', 'architecture': '' }, + device: ORTB2_DEVICE, uniqueDealId: `${hashUrl}_${Date.now().toString()}`, uqs: getTopWindowQueryParams(), mediaTypes: { @@ -386,6 +400,7 @@ describe('IlluminBidAdapter', function () { 'bitness': '64', 'architecture': '' }, + device: ORTB2_DEVICE, url: 'https%3A%2F%2Fwww.greatsite.com', referrer: 'https://www.somereferrer.com', cb: 1000, diff --git a/test/spec/modules/ixBidAdapter_spec.js b/test/spec/modules/ixBidAdapter_spec.js index e0162617be3..5f75e224b50 100644 --- a/test/spec/modules/ixBidAdapter_spec.js +++ b/test/spec/modules/ixBidAdapter_spec.js @@ -4,6 +4,7 @@ import { expect } from 'chai'; import { newBidder } from 'src/adapters/bidderFactory.js'; import { spec, storage, FEATURE_TOGGLES, LOCAL_STORAGE_FEATURE_TOGGLES_KEY, REQUESTED_FEATURE_TOGGLES, combineImps, bidToVideoImp, bidToNativeImp, deduplicateImpExtFields, removeSiteIDs, addDeviceInfo } from '../../../modules/ixBidAdapter.js'; import { deepAccess, deepClone } from '../../../src/utils.js'; +import * as ajaxLib from 'src/ajax.js'; describe('IndexexchangeAdapter', function () { const IX_SECURE_ENDPOINT = 'https://htlb.casalemedia.com/openrtb/pbjs'; @@ -2088,7 +2089,11 @@ describe('IndexexchangeAdapter', function () { it('request should be made to IX endpoint with POST method and siteId in query param', function () { expect(requestMethod).to.equal('POST'); expect(requestUrl).to.equal(IX_SECURE_ENDPOINT + '?s=' + DEFAULT_BANNER_VALID_BID[0].params.siteId); - expect(request.option.contentType).to.equal('text/plain') + }); + + it('request made to IX endpoint with POST method should have correct options fields set', function () { + expect(request.options.contentType).to.equal('text/plain') + expect(request.options.withCredentials).to.equal(true) }); it('auction type should be set correctly', function () { @@ -5003,6 +5008,7 @@ describe('IndexexchangeAdapter', function () { expect(result['b8c6b5d5-76a1-4a90-b635-0c7eae1bfaa7'].ixImps.length).to.equal(1); }); }); + describe('apply floors test', function () { it('video test', function() { const bid = utils.deepClone(DEFAULT_VIDEO_VALID_BID[0]); @@ -5367,6 +5373,7 @@ describe('IndexexchangeAdapter', function () { expect(removeSiteIDs(request)).to.deep.equal(expected); }); }); + describe('addDeviceInfo', () => { it('should add device to request when device already exists', () => { let r = { @@ -5385,4 +5392,53 @@ describe('IndexexchangeAdapter', function () { expect(r.device.h).to.exist; }); }); + + describe('fetch requests', function () { + let ajaxStub; + + beforeEach(function () { + ajaxStub = sinon.stub(ajaxLib, 'ajaxBuilder').callsFake(() => { + return sinon.spy(function (url, callback, data, options) { + callback.success('OK'); + }); + }); + }); + + afterEach(function () { + ajaxStub.restore(); + }); + + it('should send the correct headers in the actual fetch call', function (done) { + const requests = spec.buildRequests(DEFAULT_BANNER_VALID_BID, DEFAULT_OPTION); + const request = requests[0]; + const ajax = ajaxLib.ajaxBuilder(); + + ajax( + request.url, + { + success: () => { + try { + sinon.assert.calledOnce(ajaxStub); + const ajaxCall = ajaxStub.returnValues[0]; + sinon.assert.calledOnce(ajaxCall); + const [calledUrl, callback, calledData, calledOptions] = ajaxCall.getCall(0).args; + + expect(calledUrl).to.equal(request.url); + expect(calledData).to.equal(request.data); + + expect(calledOptions.contentType).to.equal('text/plain'); + expect(calledOptions.withCredentials).to.be.true; + + done(); + } catch (err) { + done(err); + } + }, + error: (err) => done(err || new Error('Ajax request failed')), + }, + request.data, + request.options + ); + }); + }); }); diff --git a/test/spec/modules/kueezRtbBidAdapter_spec.js b/test/spec/modules/kueezRtbBidAdapter_spec.js index d3323a205b3..0bb65d0aef0 100644 --- a/test/spec/modules/kueezRtbBidAdapter_spec.js +++ b/test/spec/modules/kueezRtbBidAdapter_spec.js @@ -93,6 +93,36 @@ const VIDEO_BID = { } } +const ORTB2_DEVICE = { + sua: { + 'source': 2, + 'platform': { + 'brand': 'Android', + 'version': ['8', '0', '0'] + }, + 'browsers': [ + {'brand': 'Not_A Brand', 'version': ['99', '0', '0', '0']}, + {'brand': 'Google Chrome', 'version': ['109', '0', '5414', '119']}, + {'brand': 'Chromium', 'version': ['109', '0', '5414', '119']} + ], + 'mobile': 1, + 'model': 'SM-G955U', + 'bitness': '64', + 'architecture': '' + }, + w: 980, + h: 1720, + dnt: 0, + ua: 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/125.0.6422.80 Mobile/15E148 Safari/604.1', + language: 'en', + devicetype: 1, + make: 'Apple', + model: 'iPhone 12 Pro Max', + os: 'iOS', + osv: '17.4', + ext: {fiftyonedegrees_deviceId: '17595-133085-133468-18092'}, +}; + const BIDDER_REQUEST = { 'gdprConsent': { 'consentString': 'consent_string', @@ -116,24 +146,7 @@ const BIDDER_REQUEST = { 'gpp_sid': [7], 'coppa': 0 }, - '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': '' - } - } + 'device': ORTB2_DEVICE, } }; @@ -321,6 +334,7 @@ describe('KueezRtbBidAdapter', function () { 'bitness': '64', 'architecture': '' }, + device: ORTB2_DEVICE, uniqueDealId: `${hashUrl}_${Date.now().toString()}`, uqs: getTopWindowQueryParams(), mediaTypes: { @@ -394,6 +408,7 @@ describe('KueezRtbBidAdapter', function () { 'bitness': '64', 'architecture': '' }, + device: ORTB2_DEVICE, url: 'https%3A%2F%2Fwww.greatsite.com', referrer: 'https://www.somereferrer.com', cb: 1000, diff --git a/test/spec/modules/shinezRtbBidAdapter_spec.js b/test/spec/modules/shinezRtbBidAdapter_spec.js index ebd2e987491..892d88b3b7b 100644 --- a/test/spec/modules/shinezRtbBidAdapter_spec.js +++ b/test/spec/modules/shinezRtbBidAdapter_spec.js @@ -94,6 +94,36 @@ const VIDEO_BID = { } } +const ORTB2_DEVICE = { + sua: { + 'source': 2, + 'platform': { + 'brand': 'Android', + 'version': ['8', '0', '0'] + }, + 'browsers': [ + {'brand': 'Not_A Brand', 'version': ['99', '0', '0', '0']}, + {'brand': 'Google Chrome', 'version': ['109', '0', '5414', '119']}, + {'brand': 'Chromium', 'version': ['109', '0', '5414', '119']} + ], + 'mobile': 1, + 'model': 'SM-G955U', + 'bitness': '64', + 'architecture': '' + }, + w: 980, + h: 1720, + dnt: 0, + ua: 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/125.0.6422.80 Mobile/15E148 Safari/604.1', + language: 'en', + devicetype: 1, + make: 'Apple', + model: 'iPhone 12 Pro Max', + os: 'iOS', + osv: '17.4', + ext: {fiftyonedegrees_deviceId: '17595-133085-133468-18092'}, +}; + const BIDDER_REQUEST = { 'gdprConsent': { 'consentString': 'consent_string', @@ -119,24 +149,7 @@ const BIDDER_REQUEST = { 'gpp_sid': [7], 'coppa': 0 }, - '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': '' - } - } + 'device': ORTB2_DEVICE, } }; @@ -317,6 +330,7 @@ describe('ShinezRtbBidAdapter', function () { 'bitness': '64', 'architecture': '' }, + device: ORTB2_DEVICE, uniqueDealId: `${hashUrl}_${Date.now().toString()}`, uqs: getTopWindowQueryParams(), mediaTypes: { @@ -388,6 +402,7 @@ describe('ShinezRtbBidAdapter', function () { 'bitness': '64', 'architecture': '' }, + device: ORTB2_DEVICE, url: 'https%3A%2F%2Fwww.greatsite.com', referrer: 'https://www.somereferrer.com', cb: 1000, diff --git a/test/spec/modules/tagorasBidAdapter_spec.js b/test/spec/modules/tagorasBidAdapter_spec.js index 6ebe2896ffc..8d8c8cb62b7 100644 --- a/test/spec/modules/tagorasBidAdapter_spec.js +++ b/test/spec/modules/tagorasBidAdapter_spec.js @@ -94,6 +94,36 @@ const VIDEO_BID = { } } +const ORTB2_DEVICE = { + sua: { + 'source': 2, + 'platform': { + 'brand': 'Android', + 'version': ['8', '0', '0'] + }, + 'browsers': [ + {'brand': 'Not_A Brand', 'version': ['99', '0', '0', '0']}, + {'brand': 'Google Chrome', 'version': ['109', '0', '5414', '119']}, + {'brand': 'Chromium', 'version': ['109', '0', '5414', '119']} + ], + 'mobile': 1, + 'model': 'SM-G955U', + 'bitness': '64', + 'architecture': '' + }, + w: 980, + h: 1720, + dnt: 0, + ua: 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/125.0.6422.80 Mobile/15E148 Safari/604.1', + language: 'en', + devicetype: 1, + make: 'Apple', + model: 'iPhone 12 Pro Max', + os: 'iOS', + osv: '17.4', + ext: {fiftyonedegrees_deviceId: '17595-133085-133468-18092'}, +}; + const BIDDER_REQUEST = { 'gdprConsent': { 'consentString': 'consent_string', @@ -117,24 +147,7 @@ const BIDDER_REQUEST = { 'gpp_sid': [7], 'coppa': 0 }, - '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': '' - } - } + 'device': ORTB2_DEVICE, } }; @@ -314,6 +327,7 @@ describe('TagorasBidAdapter', function () { 'bitness': '64', 'architecture': '' }, + device: ORTB2_DEVICE, uniqueDealId: `${hashUrl}_${Date.now().toString()}`, uqs: getTopWindowQueryParams(), mediaTypes: { @@ -384,6 +398,7 @@ describe('TagorasBidAdapter', function () { 'bitness': '64', 'architecture': '' }, + device: ORTB2_DEVICE, url: 'https%3A%2F%2Fwww.greatsite.com', referrer: 'https://www.somereferrer.com', cb: 1000, diff --git a/test/spec/modules/twistDigitalBidAdapter_spec.js b/test/spec/modules/twistDigitalBidAdapter_spec.js index c15a6d1d909..4ddac3261f1 100644 --- a/test/spec/modules/twistDigitalBidAdapter_spec.js +++ b/test/spec/modules/twistDigitalBidAdapter_spec.js @@ -94,6 +94,36 @@ const VIDEO_BID = { } } +const ORTB2_DEVICE = { + sua: { + 'source': 2, + 'platform': { + 'brand': 'Android', + 'version': ['8', '0', '0'] + }, + 'browsers': [ + {'brand': 'Not_A Brand', 'version': ['99', '0', '0', '0']}, + {'brand': 'Google Chrome', 'version': ['109', '0', '5414', '119']}, + {'brand': 'Chromium', 'version': ['109', '0', '5414', '119']} + ], + 'mobile': 1, + 'model': 'SM-G955U', + 'bitness': '64', + 'architecture': '' + }, + w: 980, + h: 1720, + dnt: 0, + ua: 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/125.0.6422.80 Mobile/15E148 Safari/604.1', + language: 'en', + devicetype: 1, + make: 'Apple', + model: 'iPhone 12 Pro Max', + os: 'iOS', + osv: '17.4', + ext: {fiftyonedegrees_deviceId: '17595-133085-133468-18092'}, +}; + const BIDDER_REQUEST = { 'gdprConsent': { 'consentString': 'consent_string', @@ -129,24 +159,7 @@ const BIDDER_REQUEST = { 'gpp_sid': [7], 'coppa': 0 }, - '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': '' - } - }, + device: ORTB2_DEVICE, user: { data: [ { @@ -341,6 +354,7 @@ describe('TwistDigitalBidAdapter', function () { }, contentLang: 'en', coppa: 0, + device: ORTB2_DEVICE, contentData: [{ 'name': 'example.com', 'ext': { @@ -422,6 +436,7 @@ describe('TwistDigitalBidAdapter', function () { 'bitness': '64', 'architecture': '' }, + device: ORTB2_DEVICE, url: 'https%3A%2F%2Fwww.greatsite.com', referrer: 'https://www.somereferrer.com', cb: 1000, @@ -510,6 +525,7 @@ describe('TwistDigitalBidAdapter', function () { 'bitness': '64', 'architecture': '' }, + device: ORTB2_DEVICE, url: 'https%3A%2F%2Fwww.greatsite.com', referrer: 'https://www.somereferrer.com', cb: 1000, diff --git a/test/spec/modules/vidazooBidAdapter_spec.js b/test/spec/modules/vidazooBidAdapter_spec.js index 14a49b21cdc..01d7aa20e53 100644 --- a/test/spec/modules/vidazooBidAdapter_spec.js +++ b/test/spec/modules/vidazooBidAdapter_spec.js @@ -98,6 +98,36 @@ const VIDEO_BID = { } } +const ORTB2_DEVICE = { + sua: { + 'source': 2, + 'platform': { + 'brand': 'Android', + 'version': ['8', '0', '0'] + }, + 'browsers': [ + {'brand': 'Not_A Brand', 'version': ['99', '0', '0', '0']}, + {'brand': 'Google Chrome', 'version': ['109', '0', '5414', '119']}, + {'brand': 'Chromium', 'version': ['109', '0', '5414', '119']} + ], + 'mobile': 1, + 'model': 'SM-G955U', + 'bitness': '64', + 'architecture': '' + }, + w: 980, + h: 1720, + dnt: 0, + ua: 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/125.0.6422.80 Mobile/15E148 Safari/604.1', + language: 'en', + devicetype: 1, + make: 'Apple', + model: 'iPhone 12 Pro Max', + os: 'iOS', + osv: '17.4', + ext: {fiftyonedegrees_deviceId: '17595-133085-133468-18092'}, +}; + const BIDDER_REQUEST = { 'gdprConsent': { 'consentString': 'consent_string', @@ -133,24 +163,7 @@ const BIDDER_REQUEST = { 'gpp_sid': [7], 'coppa': 0 }, - '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': '' - } - }, + device: ORTB2_DEVICE, user: { data: [ { @@ -346,6 +359,7 @@ describe('VidazooBidAdapter', function () { }, contentLang: 'en', coppa: 0, + device: ORTB2_DEVICE, contentData: [{ 'name': 'example.com', 'ext': { @@ -430,6 +444,7 @@ describe('VidazooBidAdapter', function () { 'bitness': '64', 'architecture': '' }, + device: ORTB2_DEVICE, url: 'https%3A%2F%2Fwww.greatsite.com', referrer: 'https://www.somereferrer.com', cb: 1000, @@ -523,6 +538,7 @@ describe('VidazooBidAdapter', function () { 'bitness': '64', 'architecture': '' }, + device: ORTB2_DEVICE, url: 'https%3A%2F%2Fwww.greatsite.com', referrer: 'https://www.somereferrer.com', cb: 1000, diff --git a/test/spec/modules/videoModule/submodules/jwplayerVideoProvider_spec.js b/test/spec/modules/videoModule/submodules/jwplayerVideoProvider_spec.js index 3cede6c8eda..1a9643c7d3d 100644 --- a/test/spec/modules/videoModule/submodules/jwplayerVideoProvider_spec.js +++ b/test/spec/modules/videoModule/submodules/jwplayerVideoProvider_spec.js @@ -26,6 +26,7 @@ function getPlayerMock() { getVolume: function () {}, getConfig: function () {}, getHeight: function () {}, + getContainer: function () {}, getWidth: function () {}, getFullscreen: function () {}, getPlaylistItem: function () {}, @@ -51,6 +52,9 @@ function makePlayerFactoryMock(playerMock_) { function getUtilsMock() { return { getJwConfig: function () {}, + getPlayerHeight: function () {}, + getPlayerWidth: function () {}, + getPlayerSizeFromAspectRatio: function () {}, getSupportedMediaTypes: function () {}, getStartDelay: function () {}, getPlacement: function () {}, @@ -212,6 +216,8 @@ describe('JWPlayerProvider', function () { player.getWidth = () => test_width; player.getFullscreen = () => true; // + utils.getPlayerHeight = () => 100; + utils.getPlayerWidth = () => 200; utils.getSupportedMediaTypes = () => [test_media_type]; utils.getStartDelay = () => test_start_delay; utils.getPlacement = () => test_placement; @@ -690,6 +696,81 @@ describe('utils', function () { expect(jwConfig.advertising).to.have.property('client', 'vast'); }); }); + + describe('getPlayerHeight', function () { + const getPlayerHeight = utils.getPlayerHeight; + + it('should return height from API when defined', function () { + const expectedHeight = 500; + const playerMock = { getHeight: () => expectedHeight }; + expect(getPlayerHeight(playerMock, {})).to.equal(expectedHeight); + }); + + it('should return height from config when API returns undefined', function () { + const expectedHeight = 500; + const playerMock = { getHeight: () => undefined }; + expect(getPlayerHeight(playerMock, { height: 500 })).to.equal(expectedHeight); + }); + }); + + describe('getPlayerWidth', function () { + const getPlayerWidth = utils.getPlayerWidth; + + it('should return width from API when defined', function () { + const expectedWidth = 1000; + const playerMock = { getWidth: () => expectedWidth }; + expect(getPlayerWidth(playerMock, {})).to.equal(expectedWidth); + }); + + it('should return width from config when API returns undefined', function () { + const expectedWidth = 1000; + const playerMock = { getWidth: () => undefined }; + expect(getPlayerWidth(playerMock, { width: expectedWidth })).to.equal(expectedWidth); + }); + + it('should return undefined when width is string', function () { + const playerMock = { getWidth: () => undefined }; + expect(getPlayerWidth(playerMock, { width: '50%' })).to.be.undefined; + }); + }); + + describe('getPlayerSizeFromAspectRatio', function () { + const getPlayerSizeFromAspectRatio = utils.getPlayerSizeFromAspectRatio; + const testContainer = { + clientWidth: 640, + clientHeight: 480 + }; + + it('should return an empty object when width and aspectratio are not strings', function () { + expect(getPlayerSizeFromAspectRatio({ getContainer: () => testContainer }, {})).to.deep.equal({}); + expect(getPlayerSizeFromAspectRatio({ getContainer: () => testContainer }, {width: 100})).to.deep.equal({}); + expect(getPlayerSizeFromAspectRatio({ getContainer: () => testContainer }, {aspectratio: '1:2', width: 100})).to.deep.equal({}); + }); + + it('should return an empty object when aspectratio is malformed', function () { + expect(getPlayerSizeFromAspectRatio({ getContainer: () => testContainer }, {aspectratio: '0.5', width: '100%'})).to.deep.equal({}); + expect(getPlayerSizeFromAspectRatio({ getContainer: () => testContainer }, {aspectratio: '1-2', width: '100%'})).to.deep.equal({}); + expect(getPlayerSizeFromAspectRatio({ getContainer: () => testContainer }, {aspectratio: '1:', width: '100%'})).to.deep.equal({}); + expect(getPlayerSizeFromAspectRatio({ getContainer: () => testContainer }, {aspectratio: ':2', width: '100%'})).to.deep.equal({}); + expect(getPlayerSizeFromAspectRatio({ getContainer: () => testContainer }, {aspectratio: ':', width: '100%'})).to.deep.equal({}); + expect(getPlayerSizeFromAspectRatio({ getContainer: () => testContainer }, {aspectratio: '1:2:3', width: '100%'})).to.deep.equal({}); + }); + + it('should return an empty object when player container cannot be obtained', function () { + expect(getPlayerSizeFromAspectRatio({}, {aspectratio: '1:2', width: '100%'})).to.deep.equal({}); + expect(getPlayerSizeFromAspectRatio({ getContainer: () => undefined }, {aspectratio: '1:2', width: '100%'})).to.deep.equal({}); + }); + + it('should calculate the size given the width percentage and aspect ratio', function () { + expect(getPlayerSizeFromAspectRatio({ getContainer: () => testContainer }, {aspectratio: '2:1', width: '100%'})).to.deep.equal({ height: 320, width: 640 }); + expect(getPlayerSizeFromAspectRatio({ getContainer: () => testContainer }, {aspectratio: '4:1', width: '70%'})).to.deep.equal({ height: 112, width: 448 }); + }); + + it('should return the container height when smaller than the calculated height', function () { + expect(getPlayerSizeFromAspectRatio({ getContainer: () => testContainer }, {aspectratio: '1:1', width: '100%'})).to.deep.equal({ height: 480, width: 640 }); + }); + }); + describe('getSkipParams', function () { const getSkipParams = utils.getSkipParams;