From 16b416d9ead3853e0a3bd73e39ad779e22fd7d38 Mon Sep 17 00:00:00 2001 From: Faisal Salman Date: Sun, 9 Apr 2023 05:34:02 +0700 Subject: [PATCH] Move feature detection into its own method: `withFeatureCheck` --- .gitignore | 1 + changelog.md | 1 + readme.md | 15 +++++ src/ua-parser.js | 22 ++++++- test/playwright-test-browser.spec.mjs | 89 +++++++++++++++++---------- 5 files changed, 91 insertions(+), 37 deletions(-) diff --git a/.gitignore b/.gitignore index 0cd892f..4a99144 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ node_modules/ npm-debug.log playwright-report/ +test-results/ ### vim ### .*.s[a-w][a-z] diff --git a/changelog.md b/changelog.md index 76d4ef8..ac8e493 100644 --- a/changelog.md +++ b/changelog.md @@ -7,6 +7,7 @@ - What's new: - Add some new methods in result object: - Add support for client hints: `withClientHints()` + - Add support for feature detection: `withFeatureCheck()` - Utility for easy comparison: `is()` - Utility to print full-name: `toString()` - Add support for ES module `import { UAParser } from 'ua-parser-js'` diff --git a/readme.md b/readme.md index 9998985..b73df8d 100644 --- a/readme.md +++ b/readme.md @@ -346,6 +346,21 @@ new UAParser(request.headers) }); ``` +#### * `withFeatureCheck():object` `since@2.0` + +This method allows us to examine other features beyond `navigator.userAgent` to further improve detection of the following: +- browser : Brave (check for `navigator.isBrave`) +- device : iPad (check for `navigator.standalone` & `navigator.maxTouchPoints`) + +```js +// suppose this code runs on iPad +const withoutFeatureCheck = UAParser(); +const withFeatureCheck = UAParser().withFeatureCheck(); + +console.log(withoutFeatureCheck.device); // { vendor : "Apple", model : "Macintosh", type : undefined } +console.log(withFeatureCheck.device); // { vendor : "Apple", model : "iPad", type : "tablet" } +``` + ## Extending Regex If you want to detect something that's not currently provided by UAParser.js (eg: `bots`, specific apps, etc), you can pass a list of regexes to extend internal UAParser.js regexes with your own. diff --git a/src/ua-parser.js b/src/ua-parser.js index 1e04b59..68ac98d 100755 --- a/src/ua-parser.js +++ b/src/ua-parser.js @@ -862,6 +862,10 @@ }); }; + UAParserData.prototype.withFeatureCheck = function () { + return item.detectFeature().get(); + }; + if (itemType != UA_RESULT) { UAParserData.prototype.is = function (strToCheck) { var is = false; @@ -978,6 +982,20 @@ if (!this.get(NAME) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[PLATFORM]) { this.set(NAME, NAVIGATOR_UADATA[PLATFORM]); } + break; + case UA_RESULT: + var data = this.data; + var detect = function (itemType) { + return data[itemType] + .getItem() + .detectFeature() + .get(); + }; + this.set(UA_BROWSER, detect(UA_BROWSER)) + .set(UA_CPU, detect(UA_CPU)) + .set(UA_DEVICE, detect(UA_DEVICE)) + .set(UA_ENGINE, detect(UA_ENGINE)) + .set(UA_OS, detect(UA_OS)); } } return this; @@ -1046,8 +1064,7 @@ .parseCH() .get(); }; - this.set('ua', ua) - .set(UA_BROWSER, parse(UA_BROWSER)) + this.set(UA_BROWSER, parse(UA_BROWSER)) .set(UA_CPU, parse(UA_CPU)) .set(UA_DEVICE, parse(UA_DEVICE)) .set(UA_ENGINE, parse(UA_ENGINE)) @@ -1118,7 +1135,6 @@ return function () { return new UAParserItem(itemType, userAgent, regexMap[itemType], HTTP_UACH) .parseUA() - .detectFeature() .get(); }; } diff --git a/test/playwright-test-browser.spec.mjs b/test/playwright-test-browser.spec.mjs index a8efd22..a3fb5b8 100644 --- a/test/playwright-test-browser.spec.mjs +++ b/test/playwright-test-browser.spec.mjs @@ -3,42 +3,63 @@ import { test, expect } from '@playwright/test'; import path from 'path'; import url from 'url'; +const localHtml = `file://${path.resolve(path.dirname(url.fileURLToPath(import.meta.url)), '../')}/dist/ua-parser.html`; + test('read client hints data', async ({ page }) => { - await page.addInitScript(() => { - Object.defineProperty(navigator, 'userAgentData', { - value : { - brands : [], - platform : '', - mobile : false, - getHighEntropyValues : () => { - return Promise.resolve({ - brands : [ - { - brand : 'Chromium', - version : '110' - }, - { - brand : 'Not(A:Brand', - version : '110' - }, - { - brand : 'New Browser', - version : '110' - } - ], - platform : 'New OS' - }); - } - } + await page.addInitScript(() => { + Object.defineProperty(navigator, 'userAgentData', { + value: { + brands: [], + platform: '', + mobile: false, + getHighEntropyValues: () => { + return Promise.resolve({ + brands: [ + { + brand: 'Chromium', + version: '110' + }, + { + brand: 'Not(A:Brand', + version: '110' + }, + { + brand: 'New Browser', + version: '110' + } + ], + platform: 'New OS' + }); + } + } + }); }); - }); - const dirname = path.resolve(path.dirname(url.fileURLToPath(import.meta.url)), '../'); - await page.goto(`file://${dirname}/dist/ua-parser.html`); + await page.goto(localHtml); - // @ts-ignore - const uap = await page.evaluate(async () => await UAParser().withClientHints()); - - expect(uap).toHaveProperty('browser.name', 'New Browser'); - expect(uap).toHaveProperty('os.name', 'New OS'); + // @ts-ignore + const uap = await page.evaluate(async () => await UAParser().withClientHints()); + + expect(uap).toHaveProperty('browser.name', 'New Browser'); + expect(uap).toHaveProperty('os.name', 'New OS'); +}); + +test('detect Brave', async ({ page }) => { + await page.addInitScript(() => { + Object.defineProperty(navigator, 'brave', { + value: { + isBrave: () => true + } + }); + }); + + await page.goto(localHtml); + + // @ts-ignore + let uap = await page.evaluate(() => UAParser()); + expect(uap).toHaveProperty('browser.name', 'Chrome Headless'); + + // @ts-ignore + uap = await page.evaluate(() => UAParser().withFeatureCheck()); + expect(uap).toHaveProperty('browser.name', 'Brave'); }); \ No newline at end of file