diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2652439..79b896a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -12,12 +12,23 @@ If it's a small hot-fix, an improvement to the docs, or added support for a new Following these simple rules will really help maintain the repo! Thanks ❤️ -## Adding Tests +## Adding Browser Support and Tests See the list in `test/acceptance/useragentstrings.yml` with example user agents and their expected `bowser` object. -Whenever you add support for new browsers or notice a bug / mismatch, please update the list and -check if all tests are still passing. +Whenever you add support for new browsers or notice a bug / mismatch, please update the list and check if all tests are still passing. Also, make sure to keep the list of browser aliases up-to-date in `src/constants.js`. + +For creating aliases, keep the following guidelines in mind: + - use only lowercase letters for names + - replace special characters such as space and dashes by underscore + - whenever possible drop the word `browser` from the original browser name + - always check for possible duplicates + - aliases are supposed to also be a shorter version of the original name + +Examples: +`Opera Coast` --> `opera_coast` +`UC Browser` --> `uc` +`SeaMonkey` --> `seamonkey` ## Testing diff --git a/src/constants.js b/src/constants.js new file mode 100644 index 0000000..d0e084c --- /dev/null +++ b/src/constants.js @@ -0,0 +1,41 @@ +// NOTE: this list must be up-to-date with browsers listed in +// test/acceptance/useragentstrings.yml +const BROWSER_ALIASES_MAP = { + 'Amazon Silk': 'amazon_silk', + 'Android Browser': 'android', + Bada: 'bada', + BlackBerry: 'blackberry', + Chrome: 'chrome', + Chromium: 'chromium', + Epiphany: 'epiphany', + Firefox: 'firefox', + Focus: 'focus', + Generic: 'generic', + Googlebot: 'googlebot', + 'Internet Explorer': 'ie', + 'K-Meleon': 'k_meleon', + Maxthon: 'maxthon', + 'Microsoft Edge': 'edge', + 'MZ Browser': 'mz', + 'NAVER Whale Browser': 'naver', + Opera: 'opera', + 'Opera Coast': 'opera_coast', + PhantomJS: 'phantomjs', + Puffin: 'puffin', + QupZilla: 'qupzilla', + Safari: 'safari', + Sailfish: 'sailfish', + SeaMonkey: 'seamonkey', + Sleipnir: 'sleipnir', + Swing: 'swing', + Tizen: 'tizen', + 'UC Browser': 'uc', + Vivaldi: 'vivaldi', + 'WebOS Browser': 'webos', + WeChat: 'wechat', + 'Yandex Browser': 'yandex', +}; + +module.exports = { + BROWSER_ALIASES_MAP, +}; diff --git a/src/parser.js b/src/parser.js index daef7aa..88706a8 100644 --- a/src/parser.js +++ b/src/parser.js @@ -392,7 +392,7 @@ class Parser { if (browsersCounter > 0) { const browserNames = Object.keys(browsers); - const matchingDefinition = browserNames.find(name => (this.isBrowser(name))); + const matchingDefinition = browserNames.find(name => (this.isBrowser(name, true))); if (matchingDefinition !== void 0) { return this.compareVersion(browsers[matchingDefinition]); @@ -402,8 +402,15 @@ class Parser { return undefined; } - isBrowser(browserName) { - return this.getBrowserName(true) === String(browserName).toLowerCase(); + isBrowser(browserName, loosely = false) { + const defaultBrowserName = this.getBrowserName(); + const possibleNames = [defaultBrowserName.toLowerCase()]; + + if (loosely) { + possibleNames.push(Utils.getBrowserAlias(defaultBrowserName).toLowerCase()); + } + + return possibleNames.indexOf(browserName.toLowerCase()) !== -1; } compareVersion(version) { diff --git a/src/utils.js b/src/utils.js index 8b12773..942c3db 100644 --- a/src/utils.js +++ b/src/utils.js @@ -1,3 +1,5 @@ +import { BROWSER_ALIASES_MAP } from './constants.js'; + export default class Utils { /** * Get first matched item for a string @@ -187,4 +189,17 @@ export default class Utils { } return result; } + + /** + * Get short version/alias for a browser name + * + * @example + * getBrowserAlias('Microsoft Edge') // edge + * + * @param {string} browserName + * @return {string} + */ + static getBrowserAlias(browserName) { + return BROWSER_ALIASES_MAP[browserName]; + } } diff --git a/test/unit/constants.js b/test/unit/constants.js new file mode 100644 index 0000000..a8b2be6 --- /dev/null +++ b/test/unit/constants.js @@ -0,0 +1,15 @@ +import test from 'ava'; +import { BROWSER_ALIASES_MAP } from '../../src/constants'; + +test('check duplicate aliases', (t) => { + const aliasesList = Object.keys(BROWSER_ALIASES_MAP).map(value => (BROWSER_ALIASES_MAP[value])); + let foundOnce, foundTwice; + + const duplicates = aliasesList.filter(item => { + foundOnce = aliasesList.indexOf(item); + foundTwice = aliasesList.indexOf(item, foundOnce + 1); + return +foundTwice !== -1; + }); + + t.deepEqual(duplicates, []); +}); diff --git a/test/unit/parser.js b/test/unit/parser.js index 17a020f..10ec0d4 100644 --- a/test/unit/parser.js +++ b/test/unit/parser.js @@ -5,6 +5,9 @@ import Parser from '../../src/parser'; const UA = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36 OPR/43.0.2442.1165'; const parser = new Parser(UA, true); +const EDGE_UA = 'Mozilla/5.0 (Linux; Android 8.0; Pixel XL Build/OPP3.170518.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.0 Mobile Safari/537.36 EdgA/41.1.35.1'; +const edgeParser = new Parser(EDGE_UA, true); + test('constructor', (t) => { t.truthy(parser instanceof Parser); }); @@ -145,6 +148,13 @@ test('Parser.satisfies for versionless UA strings', (t) => { }), void 0); }); +test('Parser.satisfies should consider aliases while handling browsers', (t) => { + t.is(edgeParser.satisfies({ 'Microsoft Edge': '=41.1.35.1' }), true); + t.is(edgeParser.satisfies({ 'microsoft edge': '=41.1.35.1' }), true); + t.is(edgeParser.satisfies({ 'edge': '=41.1.35.1' }), true); + t.is(edgeParser.satisfies({ 'Edge': '=41.1.35.1' }), true); +}); + test('Parser.is should pass', (t) => { t.is(parser.is('opera'), true); t.is(parser.is('desktop'), true); @@ -158,3 +168,19 @@ test('Parser.some should pass', (t) => { t.is(parser.some([]), false); t.is(parser.some(), false); }); + +test('Parser.isBrowser should pass when not loosely checking', (t) => { + t.is(edgeParser.isBrowser('Microsoft Edge', false), true); + t.is(edgeParser.isBrowser('microsoft edge', false), true); + t.is(edgeParser.isBrowser('mIcrosoft eDge', false), true); + t.is(edgeParser.isBrowser('edge', false), false); + t.is(edgeParser.isBrowser('Edge', false), false); +}); + +test('Parser.isBrowser should pass when loosely checking', (t) => { + t.is(edgeParser.isBrowser('Microsoft Edge', true), true); + t.is(edgeParser.isBrowser('microsoft edge', true), true); + t.is(edgeParser.isBrowser('mIcrosoft eDge', true), true); + t.is(edgeParser.isBrowser('edge', true), true); + t.is(edgeParser.isBrowser('Edge', true), true); +}); diff --git a/test/unit/utils.js b/test/unit/utils.js index 4d73cbc..ecbf85f 100644 --- a/test/unit/utils.js +++ b/test/unit/utils.js @@ -1,5 +1,6 @@ import test from 'ava'; import { + getBrowserAlias, getFirstMatch, getWindowsVersionName, compareVersions, @@ -50,3 +51,8 @@ test('compareVersions', (t) => { t.is(compareVersions(versionA, versionB, isLoose), result, `version ${versionA} should be ${matching} version ${versionB}`); }); }); + +test('getBrowserAlias', (t) => { + t.is(getBrowserAlias('Microsoft Edge'), 'edge'); + t.is(getBrowserAlias('Unexisting Browser'), void 0); +});