diff --git a/README.md b/README.md index ee53c41..dd04390 100644 --- a/README.md +++ b/README.md @@ -111,6 +111,13 @@ const isValidBrowser = browser.satisfies({ chrome: ">20.1.1432", firefox: ">31", opera: ">22" + + // also supports equality operator + chrome: "=20.1.1432", // will match particular build only + + // and loose-equality operator + chrome: "~20" // will match any 20.* sub-version + chrome: "~20.1" // will match any 20.1.* sub-version (20.1.19 as well as 20.1.12.42-alpha.1) }); ``` diff --git a/src/parser.js b/src/parser.js index df98540..283f7df 100644 --- a/src/parser.js +++ b/src/parser.js @@ -397,6 +397,7 @@ class Parser { compareVersion(version) { let expectedResult = 0; let comparableVersion = version; + let isLoose = false; if (version[0] === '>') { expectedResult = 1; @@ -406,8 +407,12 @@ class Parser { comparableVersion = version.substr(1); } else if (version[0] === '=') { comparableVersion = version.substr(1); + } else if (version[0] === '~') { + isLoose = true; + comparableVersion = version.substr(1); } - return compareVersions(this.getBrowserVersion(), comparableVersion) === expectedResult; + + return compareVersions(this.getBrowserVersion(), comparableVersion, isLoose) === expectedResult; } isOS(osName) { diff --git a/src/utils.js b/src/utils.js index b40a849..4d313e6 100644 --- a/src/utils.js +++ b/src/utils.js @@ -68,23 +68,26 @@ class Utils { * Calculate browser version weight * * @example - * compareVersions(['1.10.2.1', '1.8.2.1.90']) // 1 - * compareVersions(['1.010.2.1', '1.09.2.1.90']); // 1 - * compareVersions(['1.10.2.1', '1.10.2.1']); // 0 - * compareVersions(['1.10.2.1', '1.0800.2']); // -1 + * compareVersions('1.10.2.1', '1.8.2.1.90') // 1 + * compareVersions('1.010.2.1', '1.09.2.1.90'); // 1 + * compareVersions('1.10.2.1', '1.10.2.1'); // 0 + * compareVersions('1.10.2.1', '1.0800.2'); // -1 + * compareVersions('1.10.2.1', '1.10', true); // 0 * * @param {String} versionA versions versions to compare * @param {String} versionB versions versions to compare + * @param {boolean} [isLoose] enable loose comparison * @return {Number} comparison result: -1 when versionA is lower, * 1 when versionA is bigger, 0 when both equal */ /* eslint consistent-return: 1 */ - static compareVersions(versionA, versionB) { + static compareVersions(versionA, versionB, isLoose = false) { // 1) get common precision for both versions, for example for "10.0" and "9" it should be 2 - let precision = Math.max( - Utils.getVersionPrecision(versionA), - Utils.getVersionPrecision(versionB), - ); + const versionAPrecision = Utils.getVersionPrecision(versionA); + const versionBPrecision = Utils.getVersionPrecision(versionB); + + let precision = Math.max(versionAPrecision, versionBPrecision); + let lastPrecision = 0; const chunks = Utils.map([versionA, versionB], (version) => { const delta = precision - Utils.getVersionPrecision(version); @@ -96,14 +99,19 @@ class Utils { return Utils.map(_version.split('.'), chunk => new Array(20 - chunk.length).join('0') + chunk).reverse(); }); + // adjust precision for loose comparison + if (isLoose) { + lastPrecision = precision - Math.min(versionAPrecision, versionBPrecision); + } + // iterate in reverse order by reversed chunks array precision -= 1; - while (precision >= 0) { + while (precision >= lastPrecision) { // 4) compare: "000000009" > "000000010" = false (but "9" > "10" = true) if (chunks[0][precision] > chunks[1][precision]) { return 1; } else if (chunks[0][precision] === chunks[1][precision]) { - if (precision === 0) { + if (precision === lastPrecision) { // all version chunks are same return 0; } diff --git a/test/unit/parser.js b/test/unit/parser.js index afb643e..5e37a0b 100644 --- a/test/unit/parser.js +++ b/test/unit/parser.js @@ -57,8 +57,13 @@ test('Skip parsing shouldn\'t parse', (t) => { t.deepEqual((new Parser(UA, true)).getResult(), {}); }); -test('Parser.check should make simple comparison', (t) => { +test('Parser.check should make simple comparisons', (t) => { + // also covers Parser.compareVersion() method t.is(parser.satisfies({ opera: '>42' }), true); + t.is(parser.satisfies({ opera: '<44' }), true); + t.is(parser.satisfies({ opera: '=43.0.2442.1165' }), true); + t.is(parser.satisfies({ opera: '~43.0' }), true); + t.is(parser.satisfies({ opera: '~43' }), true); }); test('Parser.check should make complex comparison', (t) => { diff --git a/test/unit/utils.js b/test/unit/utils.js index 0240976..4d73cbc 100644 --- a/test/unit/utils.js +++ b/test/unit/utils.js @@ -22,6 +22,9 @@ test('compareVersions', (t) => { ['1.10.2.1', '1.8.2.1.90', 1], ['1.010.2.1', '1.08.2.1.90', 1], ['1.10.2.1', '1.10.2.1', 0], + ['1.10.2.1', '1.10.2', 0, true], + ['1.10.2.1', '1.10', 0, true], + ['1.10.2.1', '1', 0, true], ['1.10.2.1', '1.0800.2', -1], ['1.0.0-alpha', '1.0.0-alpha.1', -1], ['1.0.0-alpha.1', '1.0.0-alpha.beta', -1], @@ -35,7 +38,8 @@ test('compareVersions', (t) => { const versionA = testingParams[0]; const versionB = testingParams[1]; const result = testingParams[2]; - let matching = ' == '; + const isLoose = testingParams.length > 3 ? testingParams[3] : false; + let matching = isLoose ? '~' : ' == '; if (result > 0) { matching = ' > '; @@ -43,6 +47,6 @@ test('compareVersions', (t) => { matching = ' < '; } - t.is(compareVersions(versionA, versionB), result, `version ${versionA} should be ${matching} version ${versionB}`); + t.is(compareVersions(versionA, versionB, isLoose), result, `version ${versionA} should be ${matching} version ${versionB}`); }); });