From 413ba445be4168b5165eb47ce1ee2f2e2adf3c4a Mon Sep 17 00:00:00 2001 From: Bryce Tham Date: Thu, 12 Oct 2023 09:23:45 -0400 Subject: [PATCH] feat: add API to check browser info --- README.md | 10 +++ cspell.json | 4 +- package.json | 3 + src/browser-info.spec.ts | 132 ++++++++++++++++++++++++++++++++++ src/browser-info.ts | 150 +++++++++++++++++++++++++++++++++++++++ src/index.ts | 1 + yarn.lock | 5 ++ 7 files changed, 304 insertions(+), 1 deletion(-) create mode 100644 src/browser-info.spec.ts create mode 100644 src/browser-info.ts diff --git a/README.md b/README.md index 6d4c835..98e930b 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,16 @@ if (WebCapabilities.isCapableOfBackgroundNoiseRemoval() === CapabilityState.CAPA } ``` +Use the `BrowserInfo` class to check certain details about the browser. + +```javascript +import { BrowserInfo } from '@webex/web-capabilities'; + +if (BrowserInfo.isChrome() && BrowserInfo.isSubVersionOf('110')) { + console.log('This browser is Chrome version 110!'); +} +``` + # Setup 1. Run `yarn` to install dependencies. diff --git a/cspell.json b/cspell.json index 6bac052..6ff0d7f 100644 --- a/cspell.json +++ b/cspell.json @@ -8,6 +8,7 @@ "automock", "bitauth", "bitjson", + "Bowser", "cimg", "circleci", "clientjoin", @@ -28,6 +29,7 @@ "globby", "gohri", "inferencing", + "KHTML", "libauth", "mindmeld", "mkdir", @@ -38,8 +40,8 @@ "prettierignore", "rohit", "sandboxed", - "tfjs", "SSDK", + "tfjs", "trackingid", "transcoding", "transpiled", diff --git a/package.json b/package.json index 3834795..e8f3f61 100644 --- a/package.json +++ b/package.json @@ -87,5 +87,8 @@ "ts-jest": "^27.1.2", "typedoc": "^0.22.10", "typescript": "^4.5.4" + }, + "dependencies": { + "bowser": "^2.11.0" } } diff --git a/src/browser-info.spec.ts b/src/browser-info.spec.ts new file mode 100644 index 0000000..ffe66ca --- /dev/null +++ b/src/browser-info.spec.ts @@ -0,0 +1,132 @@ +import Bowser from 'bowser'; +import { BrowserInfo, BrowserName } from './browser-info'; + +/** + * Given a browser name, mock a specific user agent. + * + * @param browserName - The name of the browser to mock. + */ +const mockUserAgent = (browserName: BrowserName): void => { + let userAgent; + switch (browserName) { + case BrowserName.CHROME: + userAgent = + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36'; + break; + case BrowserName.FIREFOX: + userAgent = + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/118.0'; + break; + case BrowserName.EDGE: + userAgent = + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36 Edg/117.0.2045.60'; + break; + case BrowserName.SAFARI: + userAgent = + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Safari/605.1.15'; + break; + default: + throw new Error(`Unknown browser name ${browserName}`); + } + // eslint-disable-next-line dot-notation + BrowserInfo['browser'] = Bowser.getParser(userAgent); +}; + +describe('BrowserInfo', () => { + describe('getting details', () => { + it('should get browser details correctly', () => { + expect.hasAssertions(); + mockUserAgent(BrowserName.CHROME); + + expect(BrowserInfo.getBrowserDetails().name).toBe(BrowserName.CHROME); + expect(BrowserInfo.getBrowserDetails().version).toBe('118.0.0.0'); + }); + + it('should get OS details correctly', () => { + expect.hasAssertions(); + mockUserAgent(BrowserName.CHROME); + + expect(BrowserInfo.getOSDetails().name).toBe('Windows'); + expect(BrowserInfo.getOSDetails().version).toBe('NT 10.0'); + expect(BrowserInfo.getOSDetails().versionName).toBe('10'); + }); + + it('should get platform details correctly', () => { + expect.hasAssertions(); + mockUserAgent(BrowserName.CHROME); + + expect(BrowserInfo.getPlatformDetails().type).toBe('desktop'); + }); + + it('should get engine details correctly', () => { + expect.hasAssertions(); + mockUserAgent(BrowserName.CHROME); + + expect(BrowserInfo.getEngineDetails().name).toBe('Blink'); + }); + }); + + describe('identifying browsers', () => { + it('should identify a Chrome browser', () => { + expect.hasAssertions(); + mockUserAgent(BrowserName.CHROME); + + expect(BrowserInfo.isChrome()).toBeTruthy(); + expect(BrowserInfo.isFirefox()).toBeFalsy(); + expect(BrowserInfo.isEdge()).toBeFalsy(); + expect(BrowserInfo.isSafari()).toBeFalsy(); + }); + + it('should identify a Firefox browser', () => { + expect.hasAssertions(); + mockUserAgent(BrowserName.FIREFOX); + + expect(BrowserInfo.isChrome()).toBeFalsy(); + expect(BrowserInfo.isFirefox()).toBeTruthy(); + expect(BrowserInfo.isEdge()).toBeFalsy(); + expect(BrowserInfo.isSafari()).toBeFalsy(); + }); + + it('should identify a Edge browser', () => { + expect.hasAssertions(); + mockUserAgent(BrowserName.EDGE); + + expect(BrowserInfo.isChrome()).toBeFalsy(); + expect(BrowserInfo.isFirefox()).toBeFalsy(); + expect(BrowserInfo.isEdge()).toBeTruthy(); + expect(BrowserInfo.isSafari()).toBeFalsy(); + }); + + it('should identify a Safari browser', () => { + expect.hasAssertions(); + mockUserAgent(BrowserName.SAFARI); + + expect(BrowserInfo.isChrome()).toBeFalsy(); + expect(BrowserInfo.isFirefox()).toBeFalsy(); + expect(BrowserInfo.isEdge()).toBeFalsy(); + expect(BrowserInfo.isSafari()).toBeTruthy(); + }); + }); + + describe('comparing versions', () => { + it('should compare browser versions correctly', () => { + expect.hasAssertions(); + mockUserAgent(BrowserName.CHROME); + + expect(BrowserInfo.isVersionGreaterThan('117')).toBeTruthy(); + expect(BrowserInfo.isVersionGreaterThanOrEqualTo('117')).toBeTruthy(); + expect(BrowserInfo.isVersionGreaterThanOrEqualTo('118')).toBeTruthy(); + + expect(BrowserInfo.isVersionLessThan('119')).toBeTruthy(); + expect(BrowserInfo.isVersionLessThanOrEqualTo('119')).toBeTruthy(); + expect(BrowserInfo.isVersionLessThanOrEqualTo('118')).toBeTruthy(); + }); + + it('should get browser sub-version correctly', () => { + expect.hasAssertions(); + mockUserAgent(BrowserName.CHROME); + + expect(BrowserInfo.isSubVersionOf('118')).toBeTruthy(); + }); + }); +}); diff --git a/src/browser-info.ts b/src/browser-info.ts new file mode 100644 index 0000000..e44a080 --- /dev/null +++ b/src/browser-info.ts @@ -0,0 +1,150 @@ +import Bowser from 'bowser'; + +/** + * Used by the {@link BrowserInfo} class to check the name of the browser. + */ +export enum BrowserName { + CHROME = 'Chrome', + FIREFOX = 'Firefox', + EDGE = 'Microsoft Edge', + SAFARI = 'Safari', +} + +/** + * A class that retrieves and checks certain information about the browser. + */ +export class BrowserInfo { + private static browser = Bowser.getParser(window.navigator.userAgent); + + /** + * Get details about the browser, including name and version. + * + * @returns Browser details. + */ + static getBrowserDetails(): Bowser.Parser.BrowserDetails { + return this.browser.getBrowser(); + } + + /** + * Get details about the OS, including name, version, and version name. + * + * @returns OS details. + */ + static getOSDetails(): Bowser.Parser.OSDetails { + return this.browser.getOS(); + } + + /** + * Get details about the platform, including type, vendor, and model. + * + * @returns Platform details. + */ + static getPlatformDetails(): Bowser.Parser.PlatformDetails { + return this.browser.getPlatform(); + } + + /** + * Get details about the engine, including name and version. + * + * @returns Engine details. + */ + static getEngineDetails(): Bowser.Parser.EngineDetails { + return this.browser.getEngine(); + } + + /** + * Check if current browser is Chrome. + * + * @returns True if Chrome, false otherwise. + */ + static isChrome(): boolean { + return this.browser.getBrowserName() === BrowserName.CHROME; + } + + /** + * Check if current browser is Firefox. + * + * @returns True if Firefox, false otherwise. + */ + static isFirefox(): boolean { + return this.browser.getBrowserName() === BrowserName.FIREFOX; + } + + /** + * Check if current browser is Edge. + * + * @returns True if Edge, false otherwise. + */ + static isEdge(): boolean { + return this.browser.getBrowserName() === BrowserName.EDGE; + } + + /** + * Check if current browser is Safari. + * + * @returns True if Safari, false otherwise. + */ + static isSafari(): boolean { + return this.browser.getBrowserName() === BrowserName.SAFARI; + } + + /** + * Check if current browser version is greater than the given version. + * + * @param version - The version to compare with the current browser version. + * @returns True if greater than, false otherwise. + */ + static isVersionGreaterThan(version: string): boolean { + const browserName = this.browser.getBrowserName(); + const checkTree = { [browserName]: `>${version}` }; + return this.browser.satisfies(checkTree) as boolean; + } + + /** + * Check if current browser version is greater than or equal to the given version. + * + * @param version - The version to compare with the current browser version. + * @returns True if greater than or equal to, false otherwise. + */ + static isVersionGreaterThanOrEqualTo(version: string): boolean { + const browserName = this.browser.getBrowserName(); + const checkTree = { [browserName]: `>=${version}` }; + return this.browser.satisfies(checkTree) as boolean; + } + + /** + * Check if current browser version is less than the given version. + * + * @param version - The version to compare with the current browser version. + * @returns True if less than, false otherwise. + */ + static isVersionLessThan(version: string): boolean { + const browserName = this.browser.getBrowserName(); + const checkTree = { [browserName]: `<${version}` }; + return this.browser.satisfies(checkTree) as boolean; + } + + /** + * Check if current browser version is less than or equal to the given version. + * + * @param version - The version to compare with the current browser version. + * @returns True if less than or equal to, false otherwise. + */ + static isVersionLessThanOrEqualTo(version: string): boolean { + const browserName = this.browser.getBrowserName(); + const checkTree = { [browserName]: `<=${version}` }; + return this.browser.satisfies(checkTree) as boolean; + } + + /** + * Check if current browser version is a sub-version of the given version. + * + * @param version - The version to compare with the current browser version. + * @returns True if is sub-version of, false otherwise. + */ + static isSubVersionOf(version: string): boolean { + const browserName = this.browser.getBrowserName(); + const checkTree = { [browserName]: `~${version}` }; + return this.browser.satisfies(checkTree) as boolean; + } +} diff --git a/src/index.ts b/src/index.ts index 3fe5bc7..579ae52 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1 +1,2 @@ +export * from './browser-info'; export * from './web-capabilities'; diff --git a/yarn.lock b/yarn.lock index 97cd355..1e2439c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2119,6 +2119,11 @@ bottleneck@^2.18.1: resolved "https://registry.yarnpkg.com/bottleneck/-/bottleneck-2.19.5.tgz#5df0b90f59fd47656ebe63c78a98419205cadd91" integrity sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw== +bowser@^2.11.0: + version "2.11.0" + resolved "https://registry.yarnpkg.com/bowser/-/bowser-2.11.0.tgz#5ca3c35757a7aa5771500c70a73a9f91ef420a8f" + integrity sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA== + brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"