From f10ab085ffbd326de1b8a2795b2411f40f8dc1ab Mon Sep 17 00:00:00 2001 From: Nathan Oliver Date: Thu, 20 Jun 2024 01:30:58 +0100 Subject: [PATCH 1/2] moving user agent code to libraries --- libraries/userAgentUtils/index.js | 58 ++++++++++ .../userAgentUtils/userAgentTypes.enums.js | 22 ++++ modules/pubxaiAnalyticsAdapter.js | 81 +------------ test/spec/libraries/userAgentUtils_spec.js | 108 ++++++++++++++++++ .../modules/pubxaiAnalyticsAdapter_spec.js | 22 ++-- 5 files changed, 198 insertions(+), 93 deletions(-) create mode 100644 libraries/userAgentUtils/index.js create mode 100644 libraries/userAgentUtils/userAgentTypes.enums.js create mode 100644 test/spec/libraries/userAgentUtils_spec.js diff --git a/libraries/userAgentUtils/index.js b/libraries/userAgentUtils/index.js new file mode 100644 index 00000000000..4e0cffbce44 --- /dev/null +++ b/libraries/userAgentUtils/index.js @@ -0,0 +1,58 @@ +import { deviceTypes, browserTypes, osTypes } from './userAgentTypes.enums.js'; + +/** + * Get the approximate device type enum from the user agent + * @returns {string} + */ +export const getDeviceType = () => { + if ( + /ipad|android 3.0|xoom|sch-i800|playbook|tablet|kindle/i.test( + navigator.userAgent.toLowerCase() + ) + ) return deviceTypes.TABLET; + if ( + /iphone|ipod|android|blackberry|opera|mini|windows\sce|palm|smartphone|iemobile/i.test( + navigator.userAgent.toLowerCase() + ) + ) return deviceTypes.MOBILE; + return deviceTypes.DESKTOP; +}; + +/** + * Get the approximate browser type enum from the user agent (or vendor + * if available) + * @returns {string} + */ +export const getBrowser = () => { + if (/Edg/.test(navigator.userAgent)) return browserTypes.EDGE; + else if ( + /Chrome/.test(navigator.userAgent) && + /Google Inc/.test(navigator.vendor) + ) return browserTypes.CHROME; + else if (navigator.userAgent.match('CriOS')) return browserTypes.CHROME; + else if (/Firefox/.test(navigator.userAgent)) return browserTypes.FIREFOX; + else if ( + /Safari/.test(navigator.userAgent) && + /Apple Computer/.test(navigator.vendor) + ) return browserTypes.SAFARI; + else if ( + /Trident/.test(navigator.userAgent) || + /MSIE/.test(navigator.userAgent) + ) return browserTypes.INTERNET_EXPLORER; + else return browserTypes.OTHER; +}; + +/** + * Get the approximate OS enum from the user agent (or app version, + * if available) + * @returns {string} + */ +export const getOS = () => { + if (navigator.userAgent.indexOf('Android') != -1) return osTypes.ANDROID; + if (navigator.userAgent.indexOf('like Mac') != -1) return osTypes.IOS; + if (navigator.userAgent.indexOf('Win') != -1) return osTypes.WINDOWS; + if (navigator.userAgent.indexOf('Mac') != -1) return osTypes.MAC; + if (navigator.userAgent.indexOf('Linux') != -1) return osTypes.LINUX; + if (navigator.appVersion.indexOf('X11') != -1) return osTypes.UNIX; + return osTypes.OTHER; +}; diff --git a/libraries/userAgentUtils/userAgentTypes.enums.js b/libraries/userAgentUtils/userAgentTypes.enums.js new file mode 100644 index 00000000000..8a0255e88bf --- /dev/null +++ b/libraries/userAgentUtils/userAgentTypes.enums.js @@ -0,0 +1,22 @@ +export const deviceTypes = Object.freeze({ + DESKTOP: 0, + MOBILE: 1, + TABLET: 2, +}) +export const browserTypes = Object.freeze({ + CHROME: 0, + FIREFOX: 1, + SAFARI: 2, + EDGE: 3, + INTERNET_EXPLORER: 4, + OTHER: 5 +}) +export const osTypes = Object.freeze({ + WINDOWS: 0, + MAC: 1, + LINUX: 2, + UNIX: 3, + IOS: 4, + ANDROID: 5, + OTHER: 6 +}) diff --git a/modules/pubxaiAnalyticsAdapter.js b/modules/pubxaiAnalyticsAdapter.js index 7794a8a3ed1..2bbdbf6277c 100644 --- a/modules/pubxaiAnalyticsAdapter.js +++ b/modules/pubxaiAnalyticsAdapter.js @@ -9,6 +9,7 @@ import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; import { EVENTS } from '../src/constants.js'; import { getGlobal } from '../src/prebidGlobal.js'; +import { getDeviceType, getBrowser, getOS } from '../libraries/userAgentUtils/index.js'; import { getGptSlotInfoForAdUnitCode, getGptSlotForAdUnitCode, @@ -27,31 +28,6 @@ const auctionPath = '/analytics/auction'; const winningBidPath = '/analytics/bidwon'; const storage = getStorageManager({ moduleType: MODULE_TYPE_ANALYTICS, moduleName: adapterCode }) -const deviceTypes = Object.freeze({ - DESKTOP: 0, - MOBILE: 1, - TABLET: 2, -}) - -const browserTypes = Object.freeze({ - CHROME: 0, - FIREFOX: 1, - SAFARI: 2, - EDGE: 3, - INTERNET_EXPLORER: 4, - OTHER: 5 -}) - -const osTypes = Object.freeze({ - WINDOWS: 0, - MAC: 1, - LINUX: 2, - UNIX: 3, - IOS: 4, - ANDROID: 5, - OTHER: 6 -}) - /** * The sendCache is a global cache object which tracks the pending sends * back to pubx.ai. The data may be removed from this cache, post send. @@ -252,61 +228,6 @@ const track = ({ eventType, args }) => { } }; -/** - * Get the approximate device type from the user agent - * @returns {string} - */ -export const getDeviceType = () => { - if ( - /ipad|android 3.0|xoom|sch-i800|playbook|tablet|kindle/i.test( - navigator.userAgent.toLowerCase() - ) - ) return deviceTypes.TABLET; - if ( - /iphone|ipod|android|blackberry|opera|mini|windows\sce|palm|smartphone|iemobile/i.test( - navigator.userAgent.toLowerCase() - ) - ) return deviceTypes.MOBILE; - return deviceTypes.DESKTOP; -}; - -/** - * Get the approximate browser type from the user agent (or vendor if available) - * @returns {string} - */ -export const getBrowser = () => { - if (/Edg/.test(navigator.userAgent)) return browserTypes.EDGE; - else if ( - /Chrome/.test(navigator.userAgent) && - /Google Inc/.test(navigator.vendor) - ) return browserTypes.CHROME; - else if (navigator.userAgent.match('CriOS')) return browserTypes.CHROME; - else if (/Firefox/.test(navigator.userAgent)) return browserTypes.FIREFOX; - else if ( - /Safari/.test(navigator.userAgent) && - /Apple Computer/.test(navigator.vendor) - ) return browserTypes.SAFARI - else if ( - /Trident/.test(navigator.userAgent) || - /MSIE/.test(navigator.userAgent) - ) return browserTypes.INTERNET_EXPLORER - else return browserTypes.OTHER; -}; - -/** - * Get the approximate OS from the user agent (or app version, if available) - * @returns {string} - */ -export const getOS = () => { - if (navigator.userAgent.indexOf('Android') != -1) return osTypes.ANDROID - if (navigator.userAgent.indexOf('like Mac') != -1) return osTypes.IOS - if (navigator.userAgent.indexOf('Win') != -1) return osTypes.WINDOWS - if (navigator.userAgent.indexOf('Mac') != -1) return osTypes.MAC - if (navigator.userAgent.indexOf('Linux') != -1) return osTypes.LINUX - if (navigator.appVersion.indexOf('X11') != -1) return osTypes.UNIX - return osTypes.OTHER; -}; - /** * If true, send data back to pubxai * @param {string} auctionId diff --git a/test/spec/libraries/userAgentUtils_spec.js b/test/spec/libraries/userAgentUtils_spec.js new file mode 100644 index 00000000000..17a36d6dbf9 --- /dev/null +++ b/test/spec/libraries/userAgentUtils_spec.js @@ -0,0 +1,108 @@ +/* globals describe, beforeEach, afterEach, sinon */ +import { expect } from 'chai'; +import { getDeviceType, getBrowser, getOS } from 'libraries/userAgentUtils'; +import { deviceTypes, browserTypes, osTypes } from 'libraries/userAgentUtils/userAgentTypes.enums'; + +const ORIGINAL_USER_AGENT = window.navigator.userAgent; +const ORIGINAL_VENDOR = window.navigator.vendor; +const ORIGINAL_APP_VERSION = window.navigator.appVersion; + +describe('Test user agent categorization', () => { + afterEach(() => { + window.navigator.__defineGetter__('userAgent', () => ORIGINAL_USER_AGENT); + window.navigator.__defineGetter__('vendor', () => ORIGINAL_VENDOR); + window.navigator.__defineGetter__('appVersion', () => ORIGINAL_APP_VERSION); + }) + + describe('test getDeviceType', () => { + it('user agent device type is tablet', () => { + const tabletUserAgent = 'Mozilla/5.0 (iPad; CPU OS 8_4_1 like Mac OS X) AppleWebKit/600.1.4.17.9 (KHTML, like Gecko) Version/5.1 Mobile/9B206 Safari/7534.48.3' + window.navigator.__defineGetter__('userAgent', () => tabletUserAgent); + expect(getDeviceType()).to.equal(deviceTypes.TABLET); + }) + it('user agent device type is mobile', () => { + const mobileUserAgent = 'Mozilla/5.0 (Linux; Android 12; M2102J20SG) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36' + window.navigator.__defineGetter__('userAgent', () => mobileUserAgent); + expect(getDeviceType()).to.equal(deviceTypes.MOBILE); + }) + it('user agent device type is desktop', () => { + const desktopUserAgent = 'Mozilla/5.0 (X11; CrOS x86_64 8172.45.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.64 Safari/537.36' + window.navigator.__defineGetter__('userAgent', () => desktopUserAgent); + expect(getDeviceType()).to.equal(deviceTypes.DESKTOP); + }) + }) + + describe('test getBrowser', () => { + it('user agent browser is edge', () => { + const edgeUserAgent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36 Edge/12.10532' + window.navigator.__defineGetter__('userAgent', () => edgeUserAgent); + expect(getBrowser()).to.equal(browserTypes.EDGE); + }) + it('user agent browser is chrome', () => { + const chromeUserAgent = 'Mozilla/5.0 (iPad; CPU OS 8_4 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) CriOS/44.0.2403.67 Mobile/12H143 Safari/600.1.4' + window.navigator.__defineGetter__('userAgent', () => chromeUserAgent); + expect(getBrowser()).to.equal(browserTypes.CHROME); + }) + it('user agent browser is firefox', () => { + const firefoxUserAgent = 'Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:40.0) Gecko/20100101 Firefox/40.0.2 Waterfox/40.0.2' + window.navigator.__defineGetter__('userAgent', () => firefoxUserAgent); + expect(getBrowser()).to.equal(browserTypes.FIREFOX); + }) + it('user agent browser is safari', () => { + const safariUserAgent = 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.124 Safari/537.36' + window.navigator.__defineGetter__('userAgent', () => safariUserAgent); + window.navigator.__defineGetter__('vendor', () => 'Apple Computer, Inc.'); + expect(getBrowser()).to.equal(browserTypes.SAFARI); + }) + it('user agent browser is internet explorer', () => { + const internetexplorerUserAgent = 'Mozilla/5.0 (MSIE 9.0; Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko' + window.navigator.__defineGetter__('userAgent', () => internetexplorerUserAgent); + expect(getBrowser()).to.equal(browserTypes.INTERNET_EXPLORER); + }) + it('user agent is other', () => { + const otherUserAgent = 'Dalvik/2.1.0 (Linux; U; Android 9; ADT-2 Build/PTT5.181126.002)' + window.navigator.__defineGetter__('userAgent', () => otherUserAgent); + expect(getBrowser()).to.equal(browserTypes.OTHER); + }) + }) + + describe('test getOS', () => { + it('user agent is android', () => { + const androidUserAgent = 'Mozilla/5.0 (Android; Mobile; rv:40.0) Gecko/40.0 Firefox/40.0' + window.navigator.__defineGetter__('userAgent', () => androidUserAgent); + expect(getOS()).to.equal(osTypes.ANDROID); + }) + it('user agent is ios', () => { + const iosUserAgent = 'Mozilla/5.0 (iPad; CPU OS 8_4_1 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Version/8.0 Mobile/12H321 Safari/600.1.4' + window.navigator.__defineGetter__('userAgent', () => iosUserAgent); + expect(getOS()).to.equal(osTypes.IOS); + }) + it('user agent is windows', () => { + const windowsUserAgent = 'Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.154 Safari/537.36' + window.navigator.__defineGetter__('userAgent', () => windowsUserAgent); + expect(getOS()).to.equal(osTypes.WINDOWS); + }) + it('user agent is mac', () => { + const macUserAgent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:33.0) Gecko/20100101 Firefox/33.0' + window.navigator.__defineGetter__('userAgent', () => macUserAgent); + expect(getOS()).to.equal(osTypes.MAC); + }) + it('user agent is linux', () => { + const linuxUserAgent = 'Mozilla/5.0 (X11; Linux x86_64; rv:31.0) Gecko/20100101 Firefox/31.0' + window.navigator.__defineGetter__('userAgent', () => linuxUserAgent); + expect(getOS()).to.equal(osTypes.LINUX); + }) + it('user agent is unix', () => { + const unixUserAgent = 'Mozilla/5.0 (X11; CrOS armv7l 7077.134.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.156 Safari/537.36' + const unixappVersion = '5.0 (X11; CrOS armv7l 7077.134.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.156 Safari/537.36' + window.navigator.__defineGetter__('userAgent', () => unixUserAgent); + window.navigator.__defineGetter__('appVersion', () => unixappVersion); + expect(getOS()).to.equal(osTypes.UNIX); + }) + it('user agent is other', () => { + const otherUserAgent = 'Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)' + window.navigator.__defineGetter__('userAgent', () => otherUserAgent); + expect(getOS()).to.equal(osTypes.OTHER); + }) + }) +}) diff --git a/test/spec/modules/pubxaiAnalyticsAdapter_spec.js b/test/spec/modules/pubxaiAnalyticsAdapter_spec.js index 1a3bd137df1..e4f413ef78e 100644 --- a/test/spec/modules/pubxaiAnalyticsAdapter_spec.js +++ b/test/spec/modules/pubxaiAnalyticsAdapter_spec.js @@ -1,17 +1,15 @@ +/* globals describe, beforeEach, afterEach, sinon */ +import { expect } from 'chai'; +import { getGptSlotInfoForAdUnitCode } from 'libraries/gptUtils/gptUtils.js'; +import { getDeviceType, getBrowser, getOS } from 'libraries/userAgentUtils'; import pubxaiAnalyticsAdapter, { - getDeviceType, - getOS, - getBrowser, auctionCache, } from 'modules/pubxaiAnalyticsAdapter.js'; -import { expect } from 'chai'; +import { EVENTS } from 'src/constants.js'; import adapterManager from 'src/adapterManager.js'; -import * as utils from 'src/utils.js'; -import { getGlobal } from '../../../src/prebidGlobal.js'; -import { getGptSlotInfoForAdUnitCode } from '../../../libraries/gptUtils/gptUtils.js'; -import { EVENTS } from '../../../src/constants.js'; - -let events = require('src/events'); +import { getWindowLocation } from 'src/utils.js'; +import { getGlobal } from 'src/prebidGlobal.js'; +import * as events from 'src/events.js' const readBlobSafariCompat = (blob) => { return new Promise((resolve, reject) => { @@ -25,7 +23,6 @@ const readBlobSafariCompat = (blob) => { describe('pubxai analytics adapter', () => { beforeEach(() => { sinon.stub(events, 'getEvents').returns([]); - sinon.stub() }); afterEach(() => { @@ -38,10 +35,9 @@ describe('pubxai analytics adapter', () => { pubxId: '6c415fc0-8b0e-4cf5-be73-01526a4db625', }; - let originalHD; let originalVS; - let location = utils.getWindowLocation(); + let location = getWindowLocation(); const replaceProperty = (obj, params) => { let strObj = JSON.stringify(obj); From 5f24f74f7869972ca9175c1b780ff0351626f636 Mon Sep 17 00:00:00 2001 From: Nathan Oliver Date: Thu, 20 Jun 2024 01:34:41 +0100 Subject: [PATCH 2/2] updated return types --- libraries/userAgentUtils/index.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/userAgentUtils/index.js b/libraries/userAgentUtils/index.js index 4e0cffbce44..7300bbd519a 100644 --- a/libraries/userAgentUtils/index.js +++ b/libraries/userAgentUtils/index.js @@ -2,7 +2,7 @@ import { deviceTypes, browserTypes, osTypes } from './userAgentTypes.enums.js'; /** * Get the approximate device type enum from the user agent - * @returns {string} + * @returns {number} */ export const getDeviceType = () => { if ( @@ -21,7 +21,7 @@ export const getDeviceType = () => { /** * Get the approximate browser type enum from the user agent (or vendor * if available) - * @returns {string} + * @returns {number} */ export const getBrowser = () => { if (/Edg/.test(navigator.userAgent)) return browserTypes.EDGE; @@ -45,7 +45,7 @@ export const getBrowser = () => { /** * Get the approximate OS enum from the user agent (or app version, * if available) - * @returns {string} + * @returns {number} */ export const getOS = () => { if (navigator.userAgent.indexOf('Android') != -1) return osTypes.ANDROID;