diff --git a/packages/core/__tests__/utils/getClientInfo.test.ts b/packages/core/__tests__/utils/getClientInfo.test.ts deleted file mode 100644 index b119ae423dd..00000000000 --- a/packages/core/__tests__/utils/getClientInfo.test.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { getClientInfo } from '../../src/utils/getClientInfo'; - -describe('getClientInfo', () => { - // create spies - const userAgentSpy = jest.spyOn(window.navigator, 'userAgent', 'get'); - - afterEach(() => { - userAgentSpy.mockReset(); - }); - - it('gets opera info', () => { - userAgentSpy.mockReturnValue( - 'Opera/9.80 (Macintosh; Intel Mac OS X; U; en) Presto/2.2.15 Version/10.00' - ); - expect(getClientInfo().model).toBe('n'); - expect(getClientInfo().version).toBe('10.00'); - }); - - it('gets ie info', () => { - userAgentSpy.mockReturnValue( - 'Internet Explorer 10 Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)' - ); - expect(getClientInfo().model).toBe('Trident'); - expect(getClientInfo().version).toBe('6.0'); - }); - - it('gets safari info', () => { - userAgentSpy.mockReturnValue( - 'Mozilla/5.0 (iPhone; CPU iPhone OS 13_5_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1.1 Mobile/15E148 Safari/604.1' - ); - expect(getClientInfo().model).toBe('Safari'); - expect(getClientInfo().version).toBe('604.1'); - }); - - it('gets safari info', () => { - userAgentSpy.mockReturnValue( - 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36' - ); - expect(getClientInfo().model).toBe('Chrome'); - expect(getClientInfo().version).toBe('51.0.2704.103'); - }); -}); diff --git a/packages/core/__tests__/utils/getClientInfo/getClientInfo.test.ts b/packages/core/__tests__/utils/getClientInfo/getClientInfo.test.ts new file mode 100644 index 00000000000..800a94c9cb1 --- /dev/null +++ b/packages/core/__tests__/utils/getClientInfo/getClientInfo.test.ts @@ -0,0 +1,182 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { getClientInfo } from '../../../src/utils/getClientInfo/getClientInfo'; + +const testCases: [string, string, object][] = [ + [ + 'latest Chrome on Windows', + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36', + { + model: 'Chrome', + version: '119.0.0.0', + }, + ], + [ + 'latest Chrome on macOS', + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36', + { + model: 'Chrome', + version: '119.0.0.0', + }, + ], + [ + 'latest Chrome on Linux', + 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36', + { + model: 'Chrome', + version: '119.0.0.0', + }, + ], + [ + 'latest Chrome on iOS', + 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/120.0.6099.50 Mobile/15E148 Safari/604.1', + { + model: 'CriOS', + version: '120.0.6099.50', + }, + ], + [ + 'Latest Chrome on Android', + 'Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.6045.193 Mobile Safari/537.36', + { + model: 'Chrome', + version: '119.0.6045.193', + }, + ], + [ + 'Firefox on Windows', + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/120.0', + { + model: 'Firefox', + version: '120.0', + }, + ], + [ + 'Firefox on macOS', + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 14.1; rv:109.0) Gecko/20100101 Firefox/120.0', + { + model: 'Firefox', + version: '120.0', + }, + ], + [ + 'Firefox on Linux', + 'Mozilla/5.0 (X11; Linux i686; rv:109.0) Gecko/20100101 Firefox/120.0', + { + model: 'Firefox', + version: '120.0', + }, + ], + [ + 'Firefox on Linux', + 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/120.0', + { + model: 'Firefox', + version: '120.0', + }, + ], + [ + 'Firefox on iOS', + 'Mozilla/5.0 (iPhone; CPU iPhone OS 14_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) FxiOS/120.0 Mobile/15E148 Safari/605.1.15', + { + model: 'FxiOS', + version: '120.0', + }, + ], + [ + 'Firefox on Android', + 'Mozilla/5.0 (Android 14; Mobile; rv:109.0) Gecko/120.0 Firefox/120.0', + { + model: 'Firefox', + version: '120.0', + }, + ], + [ + 'Safari on macOS', + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 14_1) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.1 Safari/605.1.15', + { + model: 'Safari', + version: '605.1.15', + }, + ], + [ + 'Safari on iOS', + 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_1_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.1 Mobile/15E148 Safari/604.1', + { + model: 'Safari', + version: '604.1', + }, + ], + [ + 'Edge on Windows', + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36 Edg/119.0.2151.97', + { + model: 'Edg', + version: '119.0.2151.97', + }, + ], + [ + 'Edge on macOS', + '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 Edg/119.0.2151.97', + { + model: 'Edg', + version: '119.0.2151.97', + }, + ], + [ + 'Edge on Android', + 'Mozilla/5.0 (Linux; Android 10; HD1913) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.6045.193 Mobile Safari/537.36 EdgA/119.0.2151.78', + { + model: 'EdgA', + version: '119.0.2151.78', + }, + ], + [ + 'Edge on iOS', + 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_1_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 EdgiOS/119.2151.96 Mobile/15E148 Safari/605.1.15', + { + model: 'EdgiOS', + version: '119.2151.96', + }, + ], + [ + 'Opera on Windows', + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36 OPR/105.0.0.0', + { + model: 'OPR', + version: '105.0.0.0', + }, + ], + [ + 'Opera on macOS', + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 14_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36 OPR/105.0.0.0', + { + model: 'OPR', + version: '105.0.0.0', + }, + ], +]; + +describe('getClientInfo', () => { + let mockNavigator: jest.SpyInstance; + + beforeAll(() => { + mockNavigator = jest.spyOn(window, 'navigator', 'get'); + }); + + afterAll(() => { + jest.clearAllMocks(); + }); + + test.each(testCases)( + 'given %p with user agent: %p, should return %p', + (_, userAgent, expectedResult) => { + mockNavigator.mockReturnValueOnce({ + userAgent, + } as any); + const result = getClientInfo(); + expect(result).toEqual(expect.objectContaining(expectedResult)); + } + ); +}); diff --git a/packages/core/src/utils/getClientInfo/getClientInfo.ts b/packages/core/src/utils/getClientInfo/getClientInfo.ts index b729e31de17..90136330bc5 100644 --- a/packages/core/src/utils/getClientInfo/getClientInfo.ts +++ b/packages/core/src/utils/getClientInfo/getClientInfo.ts @@ -15,12 +15,14 @@ export function getClientInfo() { function browserClientInfo() { if (typeof window === 'undefined') { logger.warn('No window object available to get browser client info'); + return {}; } const nav = window.navigator; if (!nav) { logger.warn('No navigator object available to get browser client info'); + return {}; } @@ -41,38 +43,49 @@ function browserClientInfo() { function browserTimezone() { const tzMatch = /\(([A-Za-z\s].*)\)/.exec(new Date().toString()); + return tzMatch ? tzMatch[1] || '' : ''; } function getBrowserType(userAgent: string) { - const operaMatch = /.+(Opera[\s[A-Z]*|OPR[\sA-Z]*)\/([0-9\.]+).*/i.exec( + // The latest user agents for Opera: https://www.whatismybrowser.com/guides/the-latest-user-agent/opera + const operaMatch = /.+(Opera[\s[A-Z]*|OPR[\sA-Z]*)\/([0-9.]+).*/i.exec( userAgent ); if (operaMatch) { return { type: operaMatch[1], version: operaMatch[2] }; } - const ieMatch = /.+(Trident|Edge)\/([0-9\.]+).*/i.exec(userAgent); + // The latest user agents for Edge: https://www.whatismybrowser.com/guides/the-latest-user-agent/edge + const ieMatch = /.+(Trident|Edge|Edg|EdgA|EdgiOS)\/([0-9.]+).*/i.exec( + userAgent + ); if (ieMatch) { return { type: ieMatch[1], version: ieMatch[2] }; } - const cfMatch = /.+(Chrome|Firefox|FxiOS)\/([0-9\.]+).*/i.exec(userAgent); + // The latest user agents for web browsers on Firefox and Chrome + // https://www.whatismybrowser.com/guides/the-latest-user-agent/firefox + // https://www.whatismybrowser.com/guides/the-latest-user-agent/chrome + const cfMatch = /.+(Chrome|CriOS|Firefox|FxiOS)\/([0-9.]+).*/i.exec( + userAgent + ); if (cfMatch) { return { type: cfMatch[1], version: cfMatch[2] }; } - const sMatch = /.+(Safari)\/([0-9\.]+).*/i.exec(userAgent); + // The latest user agents for Safari: https://www.whatismybrowser.com/guides/the-latest-user-agent/safari + const sMatch = /.+(Safari)\/([0-9.]+).*/i.exec(userAgent); if (sMatch) { return { type: sMatch[1], version: sMatch[2] }; } - const awkMatch = /.+(AppleWebKit)\/([0-9\.]+).*/i.exec(userAgent); + const awkMatch = /.+(AppleWebKit)\/([0-9.]+).*/i.exec(userAgent); if (awkMatch) { return { type: awkMatch[1], version: awkMatch[2] }; } - const anyMatch = /.*([A-Z]+)\/([0-9\.]+).*/i.exec(userAgent); + const anyMatch = /.*([A-Z]+)\/([0-9.]+).*/i.exec(userAgent); if (anyMatch) { return { type: anyMatch[1], version: anyMatch[2] }; }