From 0748ecf4c801e8fd1fceb819d538a62f54db18bd Mon Sep 17 00:00:00 2001 From: Lars Kissel Date: Fri, 19 Nov 2021 14:37:49 +0100 Subject: [PATCH 01/10] [FEATURE] Introduce SpecVersionUtil Introduce a utility module which handles SpecVersion comparators for an easier SpecVersion maintainance within the UI5 Tooling source code. This utility is a kind of semver compatibility layer which allows using semver functions with the SpecVersion typical format "Major.Minor". JIRA: CPOUI5FOUNDATION-224 --- lib/SpecVersionUtil.js | 71 +++++++++++++++++++ test/lib/SpecVersionUtil.js | 135 ++++++++++++++++++++++++++++++++++++ 2 files changed, 206 insertions(+) create mode 100644 lib/SpecVersionUtil.js create mode 100644 test/lib/SpecVersionUtil.js diff --git a/lib/SpecVersionUtil.js b/lib/SpecVersionUtil.js new file mode 100644 index 000000000..90141a523 --- /dev/null +++ b/lib/SpecVersionUtil.js @@ -0,0 +1,71 @@ +const semver = require("semver"); + +const SPEC_VERSION_PATTERN = /^\d+\.\d+$/; +const SUPPORTED_VERSIONS = [ + "0.1", "1.0", "1.1", + "2.0", "2.1", "2.2", "2.3", "2.4", "2.5", "2.6" +]; + +function isSupportedSpecVersion(specVersion) { + return SUPPORTED_VERSIONS.includes(specVersion); +} + +function getSemverCompatibleVersion(specVersion) { + if (isSupportedSpecVersion(specVersion)) { + return specVersion + ".0"; + } + // TODO 3.0: sync error text with error of projectPreprocessor.js + throw new Error( + `Unsupported specification version ${specVersion} defined. Your UI5 CLI installation might be outdated. ` + + `For details see https://sap.github.io/ui5-tooling/pages/Configuration/#specification-versions`); +} + +function major(specVersion) { + const version = getSemverCompatibleVersion(specVersion); + return semver.major(version); +} + +function minor(specVersion) { + const version = getSemverCompatibleVersion(specVersion); + return semver.minor(version); +} + +function satisfies(specVersion, range) { + const version = getSemverCompatibleVersion(specVersion); + return semver.satisfies(version, range); +} + +function handleSemverComparator(comparator, specVersion, expectedVersion) { + if (SPEC_VERSION_PATTERN.test(expectedVersion)) { + const a = getSemverCompatibleVersion(specVersion); + const b = expectedVersion + ".0"; + return comparator(a, b); + } + throw new Error("Invalid spec version expectation given in comparator: " + expectedVersion); +} +const gt = (specVersion, expectedVersion) => handleSemverComparator(semver.gt, specVersion, expectedVersion); +const gte = (specVersion, expectedVersion) => handleSemverComparator(semver.gte, specVersion, expectedVersion); +const lt = (specVersion, expectedVersion) => handleSemverComparator(semver.lt, specVersion, expectedVersion); +const lte = (specVersion, expectedVersion) => handleSemverComparator(semver.lte, specVersion, expectedVersion); +const eq = (specVersion, expectedVersion) => handleSemverComparator(semver.eq, specVersion, expectedVersion); +const neq = (specVersion, expectedVersion) => handleSemverComparator(semver.neq, specVersion, expectedVersion); + +module.exports = { + isSupportedSpecVersion, + // semver functions + major, + minor, + satisfies, + gt, + gte, + lt, + lte, + eq, + neq +}; + +// Export local function for testing only +if (process.env.NODE_ENV === "test") { + module.exports._getSemverCompatibleVersion = getSemverCompatibleVersion; + module.exports._handleSemverComparator = handleSemverComparator; +} diff --git a/test/lib/SpecVersionUtil.js b/test/lib/SpecVersionUtil.js new file mode 100644 index 000000000..05db2d4f2 --- /dev/null +++ b/test/lib/SpecVersionUtil.js @@ -0,0 +1,135 @@ +const test = require("ava"); +const sinon = require("sinon"); +const SpecVersionUtil = require("../../lib/SpecVersionUtil"); + +const unsupportedSpecVersionText = (specVersion) => + `Unsupported specification version ${specVersion} defined. Your UI5 CLI installation might be outdated. ` + + `For details see https://sap.github.io/ui5-tooling/pages/Configuration/#specification-versions`; + +test("isSupportedSpecVersion", (t) => { + t.is(SpecVersionUtil.isSupportedSpecVersion("0.1"), true); + t.is(SpecVersionUtil.isSupportedSpecVersion("1.0"), true); + t.is(SpecVersionUtil.isSupportedSpecVersion("1.1"), true); + t.is(SpecVersionUtil.isSupportedSpecVersion("2.0"), true); + t.is(SpecVersionUtil.isSupportedSpecVersion("2.4"), true); + t.is(SpecVersionUtil.isSupportedSpecVersion("0.2"), false); + t.is(SpecVersionUtil.isSupportedSpecVersion("1.2"), false); + t.is(SpecVersionUtil.isSupportedSpecVersion(1.1), false); + t.is(SpecVersionUtil.isSupportedSpecVersion("foo"), false); + t.is(SpecVersionUtil.isSupportedSpecVersion(""), false); + t.is(SpecVersionUtil.isSupportedSpecVersion(), false); +}); + +test("major", (t) => { + t.is(SpecVersionUtil.major("0.1"), 0); + t.is(SpecVersionUtil.major("1.1"), 1); + t.is(SpecVersionUtil.major("2.1"), 2); + + t.is(t.throws(() => { + SpecVersionUtil.major("0.2"); + }).message, unsupportedSpecVersionText("0.2")); +}); + +test("minor", (t) => { + t.is(SpecVersionUtil.minor("2.1"), 1); + t.is(SpecVersionUtil.minor("2.2"), 2); + t.is(SpecVersionUtil.minor("2.3"), 3); + + t.is(t.throws(() => { + SpecVersionUtil.minor("1.2"); + }).message, unsupportedSpecVersionText("1.2")); +}); + +test("satisfies", (t) => { + // range: 1.x + t.is(SpecVersionUtil.satisfies("1.0", "1.x"), true); + t.is(SpecVersionUtil.satisfies("1.1", "1.x"), true); + t.is(SpecVersionUtil.satisfies("2.0", "1.x"), false); + + // range: ^2.2 + t.is(SpecVersionUtil.satisfies("2.1", "^2.2"), false); + t.is(SpecVersionUtil.satisfies("2.2", "^2.2"), true); + t.is(SpecVersionUtil.satisfies("2.3", "^2.2"), true); + + // range: > 1.0 + t.is(SpecVersionUtil.satisfies("1.0", "> 1.0"), false); + t.is(SpecVersionUtil.satisfies("1.1", "> 1.0"), true); + t.is(SpecVersionUtil.satisfies("2.2", "> 1.0"), true); + + // range: 2.2 - 2.4 + t.is(SpecVersionUtil.satisfies("2.1", "2.2 - 2.4"), false); + t.is(SpecVersionUtil.satisfies("2.2", "2.2 - 2.4"), true); + t.is(SpecVersionUtil.satisfies("2.3", "2.2 - 2.4"), true); + t.is(SpecVersionUtil.satisfies("2.4", "2.2 - 2.4"), true); + t.is(SpecVersionUtil.satisfies("2.5", "2.2 - 2.4"), false); + + // range: 0.1 || 1.0 - 1.1 || ^2.5 + t.is(SpecVersionUtil.satisfies("0.1", "0.1 || 1.0 - 1.1 || ^2.5"), true); + t.is(SpecVersionUtil.satisfies("1.0", "0.1 || 1.0 - 1.1 || ^2.5"), true); + t.is(SpecVersionUtil.satisfies("1.1", "0.1 || 1.0 - 1.1 || ^2.5"), true); + t.is(SpecVersionUtil.satisfies("2.4", "0.1 || 1.0 - 1.1 || ^2.5"), false); + t.is(SpecVersionUtil.satisfies("2.5", "0.1 || 1.0 - 1.1 || ^2.5"), true); + t.is(SpecVersionUtil.satisfies("2.6", "0.1 || 1.0 - 1.1 || ^2.5"), true); + + // unsupported spec version + t.is(t.throws(() => { + SpecVersionUtil.satisfies("0.2", "1.x"); + }).message, unsupportedSpecVersionText("0.2")); +}); + +test("low level comparator", (t) => { + t.is(SpecVersionUtil.gt("2.1", "2.2"), false); + t.is(SpecVersionUtil.gt("2.2", "2.2"), false); + t.is(SpecVersionUtil.gt("2.3", "2.2"), true); + + t.is(SpecVersionUtil.gte("2.1", "2.2"), false); + t.is(SpecVersionUtil.gte("2.2", "2.2"), true); + t.is(SpecVersionUtil.gte("2.3", "2.2"), true); + + t.is(SpecVersionUtil.lt("2.1", "2.2"), true); + t.is(SpecVersionUtil.lt("2.2", "2.2"), false); + t.is(SpecVersionUtil.lt("2.3", "2.2"), false); + + t.is(SpecVersionUtil.lte("2.1", "2.2"), true); + t.is(SpecVersionUtil.lte("2.2", "2.2"), true); + t.is(SpecVersionUtil.lte("2.3", "2.2"), false); + + t.is(SpecVersionUtil.eq("2.0", "2.2"), false); + t.is(SpecVersionUtil.eq("2.2", "2.2"), true); + + t.is(SpecVersionUtil.neq("2.0", "2.2"), true); + t.is(SpecVersionUtil.neq("2.2", "2.2"), false); +}); + +test("getSemverCompatibleVersion", (t) => { + t.is(SpecVersionUtil._getSemverCompatibleVersion("0.1"), "0.1.0"); + t.is(SpecVersionUtil._getSemverCompatibleVersion("1.1"), "1.1.0"); + t.is(SpecVersionUtil._getSemverCompatibleVersion("2.0"), "2.0.0"); + + t.is(t.throws(() => { + SpecVersionUtil._getSemverCompatibleVersion("1.2.3"); + }).message, unsupportedSpecVersionText("1.2.3")); + t.is(t.throws(() => { + SpecVersionUtil._getSemverCompatibleVersion("0.99"); + }).message, unsupportedSpecVersionText("0.99")); + t.is(t.throws(() => { + SpecVersionUtil._getSemverCompatibleVersion("foo"); + }).message, unsupportedSpecVersionText("foo")); + t.is(t.throws(() => { + SpecVersionUtil._getSemverCompatibleVersion(); + }).message, unsupportedSpecVersionText("undefined")); +}); + +test("handleSemverComparator", (t) => { + const comparatorStub = sinon.stub().returns("foobar"); + t.is(SpecVersionUtil._handleSemverComparator(comparatorStub, "1.1", "2.2"), "foobar"); + t.deepEqual(comparatorStub.getCall(0).args, ["1.1.0", "2.2.0"]); + + t.is(t.throws(() => { + SpecVersionUtil._handleSemverComparator(undefined, "a.b", "2.2"); + }).message, unsupportedSpecVersionText("a.b")); + + t.is(t.throws(() => { + SpecVersionUtil._handleSemverComparator(undefined, undefined, "a.b"); + }).message, "Invalid spec version expectation given in comparator: a.b"); +}); From 84807a4a15d266b430d429fd1997f38c9e911ea3 Mon Sep 17 00:00:00 2001 From: Merlin Beutlberger Date: Sun, 13 Nov 2022 21:46:24 +0100 Subject: [PATCH 02/10] [INTERNAL] SpecVersionUtil: Transform to SpecVersionComparator class Add factory function to Specification class --- lib/SpecVersionUtil.js | 71 ------- lib/specifications/Specification.js | 21 +- .../utils/SpecVersionComparator.js | 117 +++++++++++ test/lib/SpecVersionUtil.js | 135 ------------- test/lib/specifications/Specification.js | 35 +++- .../utils/SpecVersionComparator.js | 190 ++++++++++++++++++ 6 files changed, 352 insertions(+), 217 deletions(-) delete mode 100644 lib/SpecVersionUtil.js create mode 100644 lib/specifications/utils/SpecVersionComparator.js delete mode 100644 test/lib/SpecVersionUtil.js create mode 100644 test/lib/specifications/utils/SpecVersionComparator.js diff --git a/lib/SpecVersionUtil.js b/lib/SpecVersionUtil.js deleted file mode 100644 index 90141a523..000000000 --- a/lib/SpecVersionUtil.js +++ /dev/null @@ -1,71 +0,0 @@ -const semver = require("semver"); - -const SPEC_VERSION_PATTERN = /^\d+\.\d+$/; -const SUPPORTED_VERSIONS = [ - "0.1", "1.0", "1.1", - "2.0", "2.1", "2.2", "2.3", "2.4", "2.5", "2.6" -]; - -function isSupportedSpecVersion(specVersion) { - return SUPPORTED_VERSIONS.includes(specVersion); -} - -function getSemverCompatibleVersion(specVersion) { - if (isSupportedSpecVersion(specVersion)) { - return specVersion + ".0"; - } - // TODO 3.0: sync error text with error of projectPreprocessor.js - throw new Error( - `Unsupported specification version ${specVersion} defined. Your UI5 CLI installation might be outdated. ` + - `For details see https://sap.github.io/ui5-tooling/pages/Configuration/#specification-versions`); -} - -function major(specVersion) { - const version = getSemverCompatibleVersion(specVersion); - return semver.major(version); -} - -function minor(specVersion) { - const version = getSemverCompatibleVersion(specVersion); - return semver.minor(version); -} - -function satisfies(specVersion, range) { - const version = getSemverCompatibleVersion(specVersion); - return semver.satisfies(version, range); -} - -function handleSemverComparator(comparator, specVersion, expectedVersion) { - if (SPEC_VERSION_PATTERN.test(expectedVersion)) { - const a = getSemverCompatibleVersion(specVersion); - const b = expectedVersion + ".0"; - return comparator(a, b); - } - throw new Error("Invalid spec version expectation given in comparator: " + expectedVersion); -} -const gt = (specVersion, expectedVersion) => handleSemverComparator(semver.gt, specVersion, expectedVersion); -const gte = (specVersion, expectedVersion) => handleSemverComparator(semver.gte, specVersion, expectedVersion); -const lt = (specVersion, expectedVersion) => handleSemverComparator(semver.lt, specVersion, expectedVersion); -const lte = (specVersion, expectedVersion) => handleSemverComparator(semver.lte, specVersion, expectedVersion); -const eq = (specVersion, expectedVersion) => handleSemverComparator(semver.eq, specVersion, expectedVersion); -const neq = (specVersion, expectedVersion) => handleSemverComparator(semver.neq, specVersion, expectedVersion); - -module.exports = { - isSupportedSpecVersion, - // semver functions - major, - minor, - satisfies, - gt, - gte, - lt, - lte, - eq, - neq -}; - -// Export local function for testing only -if (process.env.NODE_ENV === "test") { - module.exports._getSemverCompatibleVersion = getSemverCompatibleVersion; - module.exports._handleSemverComparator = handleSemverComparator; -} diff --git a/lib/specifications/Specification.js b/lib/specifications/Specification.js index 227c9937d..7a13a8b20 100644 --- a/lib/specifications/Specification.js +++ b/lib/specifications/Specification.js @@ -1,5 +1,6 @@ import logger from "@ui5/logger"; import {createReader} from "@ui5/fs/resourceFactory"; +import SpecVersionComparator from "./utils/SpecVersionComparator.js"; /** * Abstract superclass for all projects and extensions @@ -50,8 +51,9 @@ class Specification { const config = JSON.parse(JSON.stringify(configuration)); const {validate} = await import("../validation/validator.js"); - if (config.specVersion === "0.1" || config.specVersion === "1.0" || - config.specVersion === "1.1") { + // SpecVersionComparator throws for invalid/unknown specVersion on instantiation + const specVersionComparator = new SpecVersionComparator(config.specVersion); + if (specVersionComparator.major() <= 1) { const originalSpecVersion = config.specVersion; this._log.verbose(`Detected legacy specification version ${config.specVersion}, defined for ` + `${config.kind} ${config.metadata.name}. ` + @@ -75,14 +77,6 @@ class Specification { `An attempted migration to a supported specification version failed, ` + `likely due to unrecognized configuration. Check verbose log for details.`); } - } else if (config.specVersion !== "2.0" && - config.specVersion !== "2.1" && config.specVersion !== "2.2" && - config.specVersion !== "2.3" && config.specVersion !== "2.4" && - config.specVersion !== "2.5" && config.specVersion !== "2.6") { - throw new Error( - `Unsupported specification version ${config.specVersion} defined in ${config.kind} ` + - `${config.metadata.name}. Your UI5 CLI installation might be outdated. ` + - `For details see https://sap.github.io/ui5-tooling/pages/Configuration/#specification-versions`); } else { await validate({ config, @@ -157,6 +151,13 @@ class Specification { * @public * @returns {string} Project version */ + getSpecVersionComparator() { + return new SpecVersionComparator(this.getSpecVersion()); + } + + /** + * @public + */ getVersion() { return this._version; } diff --git a/lib/specifications/utils/SpecVersionComparator.js b/lib/specifications/utils/SpecVersionComparator.js new file mode 100644 index 000000000..40141caea --- /dev/null +++ b/lib/specifications/utils/SpecVersionComparator.js @@ -0,0 +1,117 @@ +import semver from "semver"; + +const SPEC_VERSION_PATTERN = /^\d+\.\d+$/; +const SUPPORTED_VERSIONS = [ + "0.1", "1.0", "1.1", + "2.0", "2.1", "2.2", "2.3", "2.4", "2.5", "2.6", + "3.0" +]; + +class SpecVersionComparator { + constructor(specVersion) { + this._specVersion = specVersion; + + if (!SpecVersionComparator.isSupportedSpecVersion(specVersion)) { + throw new Error(getUnsupportedSpecVersionMessage(specVersion)); + } + } + + major() { + return SpecVersionComparator.major(this._specVersion); + } + + minor() { + return SpecVersionComparator.minor(this._specVersion); + } + + satisfies(range) { + return SpecVersionComparator.satisfies(this._specVersion); + } + + // Test whether project's specVersion is greater than testVersion + gt(testVersion) { + return SpecVersionComparator.gt(this._specVersion, testVersion); + } + + gte(testVersion) { + return SpecVersionComparator.gte(this._specVersion, testVersion); + } + + lt(testVersion) { + return SpecVersionComparator.lt(this._specVersion, testVersion); + } + + lte(testVersion) { + return SpecVersionComparator.lte(this._specVersion, testVersion); + } + + eq(testVersion) { + return SpecVersionComparator.eq(this._specVersion, testVersion); + } + + neq(testVersion) { + return SpecVersionComparator.neq(this._specVersion, testVersion); + } + + static isSupportedSpecVersion(specVersion) { + return SUPPORTED_VERSIONS.includes(specVersion); + } + + static major(specVersion) { + const version = getSemverCompatibleVersion(specVersion); + return semver.major(version); + } + static minor(specVersion) { + const version = getSemverCompatibleVersion(specVersion); + return semver.minor(version); + } + static satisfies(specVersion, range) { + const version = getSemverCompatibleVersion(specVersion); + return semver.satisfies(version, range); + } + static gt(specVersion, expectedVersion) { + return handleSemverComparator(semver.gt, specVersion, expectedVersion); + } + static gte(specVersion, expectedVersion) { + return handleSemverComparator(semver.gte, specVersion, expectedVersion); + } + static lt(specVersion, expectedVersion) { + return handleSemverComparator(semver.lt, specVersion, expectedVersion); + } + static lte(specVersion, expectedVersion) { + return handleSemverComparator(semver.lte, specVersion, expectedVersion); + } + static eq(specVersion, expectedVersion) { + return handleSemverComparator(semver.eq, specVersion, expectedVersion); + } + static neq(specVersion, expectedVersion) { + return handleSemverComparator(semver.neq, specVersion, expectedVersion); + } +} + +function getUnsupportedSpecVersionMessage(specVersion) { + return `Unsupported specification version ${specVersion} defined. Your UI5 CLI installation might be outdated. ` + + `For details see https://sap.github.io/ui5-tooling/pages/Configuration/#specification-versions`; +} + +function getSemverCompatibleVersion(specVersion) { + if (SpecVersionComparator.isSupportedSpecVersion(specVersion)) { + return specVersion + ".0"; + } + throw new Error(getUnsupportedSpecVersionMessage(specVersion)); +} + +function handleSemverComparator(comparator, specVersion, expectedVersion) { + if (SPEC_VERSION_PATTERN.test(expectedVersion)) { + const a = getSemverCompatibleVersion(specVersion); + const b = expectedVersion + ".0"; + return comparator(a, b); + } + throw new Error("Invalid spec version expectation given in comparator: " + expectedVersion); +} + +export default SpecVersionComparator; + +// Export local function for testing only +export const __localFunctions__ = (process.env.NODE_ENV === "test") ? + {getSemverCompatibleVersion, handleSemverComparator} : /* istanbul ignore next */ undefined; diff --git a/test/lib/SpecVersionUtil.js b/test/lib/SpecVersionUtil.js deleted file mode 100644 index 05db2d4f2..000000000 --- a/test/lib/SpecVersionUtil.js +++ /dev/null @@ -1,135 +0,0 @@ -const test = require("ava"); -const sinon = require("sinon"); -const SpecVersionUtil = require("../../lib/SpecVersionUtil"); - -const unsupportedSpecVersionText = (specVersion) => - `Unsupported specification version ${specVersion} defined. Your UI5 CLI installation might be outdated. ` + - `For details see https://sap.github.io/ui5-tooling/pages/Configuration/#specification-versions`; - -test("isSupportedSpecVersion", (t) => { - t.is(SpecVersionUtil.isSupportedSpecVersion("0.1"), true); - t.is(SpecVersionUtil.isSupportedSpecVersion("1.0"), true); - t.is(SpecVersionUtil.isSupportedSpecVersion("1.1"), true); - t.is(SpecVersionUtil.isSupportedSpecVersion("2.0"), true); - t.is(SpecVersionUtil.isSupportedSpecVersion("2.4"), true); - t.is(SpecVersionUtil.isSupportedSpecVersion("0.2"), false); - t.is(SpecVersionUtil.isSupportedSpecVersion("1.2"), false); - t.is(SpecVersionUtil.isSupportedSpecVersion(1.1), false); - t.is(SpecVersionUtil.isSupportedSpecVersion("foo"), false); - t.is(SpecVersionUtil.isSupportedSpecVersion(""), false); - t.is(SpecVersionUtil.isSupportedSpecVersion(), false); -}); - -test("major", (t) => { - t.is(SpecVersionUtil.major("0.1"), 0); - t.is(SpecVersionUtil.major("1.1"), 1); - t.is(SpecVersionUtil.major("2.1"), 2); - - t.is(t.throws(() => { - SpecVersionUtil.major("0.2"); - }).message, unsupportedSpecVersionText("0.2")); -}); - -test("minor", (t) => { - t.is(SpecVersionUtil.minor("2.1"), 1); - t.is(SpecVersionUtil.minor("2.2"), 2); - t.is(SpecVersionUtil.minor("2.3"), 3); - - t.is(t.throws(() => { - SpecVersionUtil.minor("1.2"); - }).message, unsupportedSpecVersionText("1.2")); -}); - -test("satisfies", (t) => { - // range: 1.x - t.is(SpecVersionUtil.satisfies("1.0", "1.x"), true); - t.is(SpecVersionUtil.satisfies("1.1", "1.x"), true); - t.is(SpecVersionUtil.satisfies("2.0", "1.x"), false); - - // range: ^2.2 - t.is(SpecVersionUtil.satisfies("2.1", "^2.2"), false); - t.is(SpecVersionUtil.satisfies("2.2", "^2.2"), true); - t.is(SpecVersionUtil.satisfies("2.3", "^2.2"), true); - - // range: > 1.0 - t.is(SpecVersionUtil.satisfies("1.0", "> 1.0"), false); - t.is(SpecVersionUtil.satisfies("1.1", "> 1.0"), true); - t.is(SpecVersionUtil.satisfies("2.2", "> 1.0"), true); - - // range: 2.2 - 2.4 - t.is(SpecVersionUtil.satisfies("2.1", "2.2 - 2.4"), false); - t.is(SpecVersionUtil.satisfies("2.2", "2.2 - 2.4"), true); - t.is(SpecVersionUtil.satisfies("2.3", "2.2 - 2.4"), true); - t.is(SpecVersionUtil.satisfies("2.4", "2.2 - 2.4"), true); - t.is(SpecVersionUtil.satisfies("2.5", "2.2 - 2.4"), false); - - // range: 0.1 || 1.0 - 1.1 || ^2.5 - t.is(SpecVersionUtil.satisfies("0.1", "0.1 || 1.0 - 1.1 || ^2.5"), true); - t.is(SpecVersionUtil.satisfies("1.0", "0.1 || 1.0 - 1.1 || ^2.5"), true); - t.is(SpecVersionUtil.satisfies("1.1", "0.1 || 1.0 - 1.1 || ^2.5"), true); - t.is(SpecVersionUtil.satisfies("2.4", "0.1 || 1.0 - 1.1 || ^2.5"), false); - t.is(SpecVersionUtil.satisfies("2.5", "0.1 || 1.0 - 1.1 || ^2.5"), true); - t.is(SpecVersionUtil.satisfies("2.6", "0.1 || 1.0 - 1.1 || ^2.5"), true); - - // unsupported spec version - t.is(t.throws(() => { - SpecVersionUtil.satisfies("0.2", "1.x"); - }).message, unsupportedSpecVersionText("0.2")); -}); - -test("low level comparator", (t) => { - t.is(SpecVersionUtil.gt("2.1", "2.2"), false); - t.is(SpecVersionUtil.gt("2.2", "2.2"), false); - t.is(SpecVersionUtil.gt("2.3", "2.2"), true); - - t.is(SpecVersionUtil.gte("2.1", "2.2"), false); - t.is(SpecVersionUtil.gte("2.2", "2.2"), true); - t.is(SpecVersionUtil.gte("2.3", "2.2"), true); - - t.is(SpecVersionUtil.lt("2.1", "2.2"), true); - t.is(SpecVersionUtil.lt("2.2", "2.2"), false); - t.is(SpecVersionUtil.lt("2.3", "2.2"), false); - - t.is(SpecVersionUtil.lte("2.1", "2.2"), true); - t.is(SpecVersionUtil.lte("2.2", "2.2"), true); - t.is(SpecVersionUtil.lte("2.3", "2.2"), false); - - t.is(SpecVersionUtil.eq("2.0", "2.2"), false); - t.is(SpecVersionUtil.eq("2.2", "2.2"), true); - - t.is(SpecVersionUtil.neq("2.0", "2.2"), true); - t.is(SpecVersionUtil.neq("2.2", "2.2"), false); -}); - -test("getSemverCompatibleVersion", (t) => { - t.is(SpecVersionUtil._getSemverCompatibleVersion("0.1"), "0.1.0"); - t.is(SpecVersionUtil._getSemverCompatibleVersion("1.1"), "1.1.0"); - t.is(SpecVersionUtil._getSemverCompatibleVersion("2.0"), "2.0.0"); - - t.is(t.throws(() => { - SpecVersionUtil._getSemverCompatibleVersion("1.2.3"); - }).message, unsupportedSpecVersionText("1.2.3")); - t.is(t.throws(() => { - SpecVersionUtil._getSemverCompatibleVersion("0.99"); - }).message, unsupportedSpecVersionText("0.99")); - t.is(t.throws(() => { - SpecVersionUtil._getSemverCompatibleVersion("foo"); - }).message, unsupportedSpecVersionText("foo")); - t.is(t.throws(() => { - SpecVersionUtil._getSemverCompatibleVersion(); - }).message, unsupportedSpecVersionText("undefined")); -}); - -test("handleSemverComparator", (t) => { - const comparatorStub = sinon.stub().returns("foobar"); - t.is(SpecVersionUtil._handleSemverComparator(comparatorStub, "1.1", "2.2"), "foobar"); - t.deepEqual(comparatorStub.getCall(0).args, ["1.1.0", "2.2.0"]); - - t.is(t.throws(() => { - SpecVersionUtil._handleSemverComparator(undefined, "a.b", "2.2"); - }).message, unsupportedSpecVersionText("a.b")); - - t.is(t.throws(() => { - SpecVersionUtil._handleSemverComparator(undefined, undefined, "a.b"); - }).message, "Invalid spec version expectation given in comparator: a.b"); -}); diff --git a/test/lib/specifications/Specification.js b/test/lib/specifications/Specification.js index 214f56f32..1df65f969 100644 --- a/test/lib/specifications/Specification.js +++ b/test/lib/specifications/Specification.js @@ -48,6 +48,9 @@ test("Configurations", async (t) => { const project = await Specification.create(t.context.basicProjectInput); t.is(project.getKind(), "project", "Returned correct kind configuration"); t.is(project.getType(), "application", "Returned correct type configuration"); + t.is(project.getSpecVersion(), "2.3", "Returned correct specification version"); + t.is(project.getSpecVersionComparator().major(), 2, + "SpecVersionComparator returned correct major version"); }); test("Access project root resources via reader", async (t) => { @@ -126,7 +129,7 @@ test("Migrate legacy project unexpected configuration", async (t) => { "Threw with expected error message"); }); -test("Migrate legacy module", async (t) => { +test("Migrate legacy module: specVersion 1.0", async (t) => { const project = await Specification.create({ id: "my.task", version: "3.4.7-beta", @@ -147,6 +150,27 @@ test("Migrate legacy module", async (t) => { t.is(project.getSpecVersion(), "2.6", "Project got migrated to latest specVersion"); }); +test("Migrate legacy module: specVersion 0.1", async (t) => { + const project = await Specification.create({ + id: "my.task", + version: "3.4.7-beta", + modulePath: genericExtensionPath, + configuration: { + specVersion: "0.1", + kind: "extension", + type: "task", + metadata: { + name: "task-a" + }, + task: { + path: "lib/extensionModule.js" + } + } + }); + + t.is(project.getSpecVersion(), "2.6", "Project got migrated to latest specVersion"); +}); + test("Migrate legacy extension", async (t) => { const project = await Specification.create({ id: "module.a.id", @@ -266,3 +290,12 @@ test("create: Unknown type", async (t) => { message: "Unable to create Specification instance: Unknown specification type 'foo'" }); }); + +test("Invalid specVersion", async (t) => { + t.context.basicProjectInput.configuration.specVersion = "0.5"; + await t.throwsAsync(Specification.create(t.context.basicProjectInput), { + message: + "Unsupported specification version 0.5 defined. Your UI5 CLI installation might be outdated. " + + "For details see https://sap.github.io/ui5-tooling/pages/Configuration/#specification-versions" + }, "Threw with expected error message"); +}); diff --git a/test/lib/specifications/utils/SpecVersionComparator.js b/test/lib/specifications/utils/SpecVersionComparator.js new file mode 100644 index 000000000..517dc714a --- /dev/null +++ b/test/lib/specifications/utils/SpecVersionComparator.js @@ -0,0 +1,190 @@ +import test from "ava"; +import sinonGlobal from "sinon"; +import SpecVersionComparator from "../../../../lib/specifications/utils/SpecVersionComparator.js"; +import {__localFunctions__} from "../../../../lib/specifications/utils/SpecVersionComparator.js"; + +const unsupportedSpecVersionText = (specVersion) => + `Unsupported specification version ${specVersion} defined. Your UI5 CLI installation might be outdated. ` + + `For details see https://sap.github.io/ui5-tooling/pages/Configuration/#specification-versions`; + +test.beforeEach((t) => { + t.context.sinon = sinonGlobal.createSandbox(); +}); + +test.afterEach.always((t) => { + t.context.sinon.restore(); +}); + +test.serial("Invalid specVersion", (t) => { + const {sinon} = t.context; + const isSupportedSpecVersionStub = + sinon.stub(SpecVersionComparator, "isSupportedSpecVersion").returns(false); + + t.throws(() => { + new SpecVersionComparator("2.5"); + }, { + message: unsupportedSpecVersionText("2.5") + }, "Threw with expected error message"); + + t.is(isSupportedSpecVersionStub.callCount, 1, "Static isSupportedSpecVersionStub has been called once"); + t.deepEqual(isSupportedSpecVersionStub.getCall(0).args, ["2.5"], + "Static isSupportedSpecVersionStub has been called with expected arguments"); +}); + + +function testCallThroughToStatic(functionName, arg) { + test.serial(functionName, (t) => { + const {sinon} = t.context; + const staticFunctionStub = + sinon.stub(SpecVersionComparator, functionName).returns("return value"); + + const specVersionComparator = new SpecVersionComparator("2.5"); + const res = specVersionComparator[functionName](arg); + t.is(staticFunctionStub.callCount, 1, `Static #${functionName} has been called once`); + const expectedArgs = ["2.5"]; + if (arg) { + expectedArgs.push(arg); + } + t.deepEqual(staticFunctionStub.getCall(0).args, expectedArgs, + `Static #${functionName} has been called with expected arguments`); + t.is(res, "return value", "Returned value of static function"); + }); +} + +testCallThroughToStatic("major"); +testCallThroughToStatic("minor"); +testCallThroughToStatic("satisfies"); +testCallThroughToStatic("gt", "5.2"); +testCallThroughToStatic("gte", "5.2"); +testCallThroughToStatic("lt", "5.2"); +testCallThroughToStatic("lte", "5.2"); +testCallThroughToStatic("eq", "5.2"); +testCallThroughToStatic("neq", "5.2"); + +test("(static) isSupportedSpecVersion", (t) => { + t.is(SpecVersionComparator.isSupportedSpecVersion("0.1"), true); + t.is(SpecVersionComparator.isSupportedSpecVersion("1.0"), true); + t.is(SpecVersionComparator.isSupportedSpecVersion("1.1"), true); + t.is(SpecVersionComparator.isSupportedSpecVersion("2.0"), true); + t.is(SpecVersionComparator.isSupportedSpecVersion("2.4"), true); + t.is(SpecVersionComparator.isSupportedSpecVersion("0.2"), false); + t.is(SpecVersionComparator.isSupportedSpecVersion("1.2"), false); + t.is(SpecVersionComparator.isSupportedSpecVersion(1.1), false); + t.is(SpecVersionComparator.isSupportedSpecVersion("foo"), false); + t.is(SpecVersionComparator.isSupportedSpecVersion(""), false); + t.is(SpecVersionComparator.isSupportedSpecVersion(), false); +}); + +test("(static) major", (t) => { + t.is(SpecVersionComparator.major("0.1"), 0); + t.is(SpecVersionComparator.major("1.1"), 1); + t.is(SpecVersionComparator.major("2.1"), 2); + + t.is(t.throws(() => { + SpecVersionComparator.major("0.2"); + }).message, unsupportedSpecVersionText("0.2")); +}); + +test("(static) minor", (t) => { + t.is(SpecVersionComparator.minor("2.1"), 1); + t.is(SpecVersionComparator.minor("2.2"), 2); + t.is(SpecVersionComparator.minor("2.3"), 3); + + t.is(t.throws(() => { + SpecVersionComparator.minor("1.2"); + }).message, unsupportedSpecVersionText("1.2")); +}); + +test("(static) satisfies", (t) => { + // range: 1.x + t.is(SpecVersionComparator.satisfies("1.0", "1.x"), true); + t.is(SpecVersionComparator.satisfies("1.1", "1.x"), true); + t.is(SpecVersionComparator.satisfies("2.0", "1.x"), false); + + // range: ^2.2 + t.is(SpecVersionComparator.satisfies("2.1", "^2.2"), false); + t.is(SpecVersionComparator.satisfies("2.2", "^2.2"), true); + t.is(SpecVersionComparator.satisfies("2.3", "^2.2"), true); + + // range: > 1.0 + t.is(SpecVersionComparator.satisfies("1.0", "> 1.0"), false); + t.is(SpecVersionComparator.satisfies("1.1", "> 1.0"), true); + t.is(SpecVersionComparator.satisfies("2.2", "> 1.0"), true); + + // range: 2.2 - 2.4 + t.is(SpecVersionComparator.satisfies("2.1", "2.2 - 2.4"), false); + t.is(SpecVersionComparator.satisfies("2.2", "2.2 - 2.4"), true); + t.is(SpecVersionComparator.satisfies("2.3", "2.2 - 2.4"), true); + t.is(SpecVersionComparator.satisfies("2.4", "2.2 - 2.4"), true); + t.is(SpecVersionComparator.satisfies("2.5", "2.2 - 2.4"), false); + + // range: 0.1 || 1.0 - 1.1 || ^2.5 + t.is(SpecVersionComparator.satisfies("0.1", "0.1 || 1.0 - 1.1 || ^2.5"), true); + t.is(SpecVersionComparator.satisfies("1.0", "0.1 || 1.0 - 1.1 || ^2.5"), true); + t.is(SpecVersionComparator.satisfies("1.1", "0.1 || 1.0 - 1.1 || ^2.5"), true); + t.is(SpecVersionComparator.satisfies("2.4", "0.1 || 1.0 - 1.1 || ^2.5"), false); + t.is(SpecVersionComparator.satisfies("2.5", "0.1 || 1.0 - 1.1 || ^2.5"), true); + t.is(SpecVersionComparator.satisfies("2.6", "0.1 || 1.0 - 1.1 || ^2.5"), true); + + // unsupported spec version + t.is(t.throws(() => { + SpecVersionComparator.satisfies("0.2", "1.x"); + }).message, unsupportedSpecVersionText("0.2")); +}); + +test("(static) low level comparator", (t) => { + t.is(SpecVersionComparator.gt("2.1", "2.2"), false); + t.is(SpecVersionComparator.gt("2.2", "2.2"), false); + t.is(SpecVersionComparator.gt("2.3", "2.2"), true); + + t.is(SpecVersionComparator.gte("2.1", "2.2"), false); + t.is(SpecVersionComparator.gte("2.2", "2.2"), true); + t.is(SpecVersionComparator.gte("2.3", "2.2"), true); + + t.is(SpecVersionComparator.lt("2.1", "2.2"), true); + t.is(SpecVersionComparator.lt("2.2", "2.2"), false); + t.is(SpecVersionComparator.lt("2.3", "2.2"), false); + + t.is(SpecVersionComparator.lte("2.1", "2.2"), true); + t.is(SpecVersionComparator.lte("2.2", "2.2"), true); + t.is(SpecVersionComparator.lte("2.3", "2.2"), false); + + t.is(SpecVersionComparator.eq("2.0", "2.2"), false); + t.is(SpecVersionComparator.eq("2.2", "2.2"), true); + + t.is(SpecVersionComparator.neq("2.0", "2.2"), true); + t.is(SpecVersionComparator.neq("2.2", "2.2"), false); +}); + +test("(static) getSemverCompatibleVersion", (t) => { + t.is(__localFunctions__.getSemverCompatibleVersion("0.1"), "0.1.0"); + t.is(__localFunctions__.getSemverCompatibleVersion("1.1"), "1.1.0"); + t.is(__localFunctions__.getSemverCompatibleVersion("2.0"), "2.0.0"); + + t.is(t.throws(() => { + __localFunctions__.getSemverCompatibleVersion("1.2.3"); + }).message, unsupportedSpecVersionText("1.2.3")); + t.is(t.throws(() => { + __localFunctions__.getSemverCompatibleVersion("0.99"); + }).message, unsupportedSpecVersionText("0.99")); + t.is(t.throws(() => { + __localFunctions__.getSemverCompatibleVersion("foo"); + }).message, unsupportedSpecVersionText("foo")); + t.is(t.throws(() => { + __localFunctions__.getSemverCompatibleVersion(); + }).message, unsupportedSpecVersionText("undefined")); +}); + +test("(static) handleSemverComparator", (t) => { + const comparatorStub = t.context.sinon.stub().returns("foobar"); + t.is(__localFunctions__.handleSemverComparator(comparatorStub, "1.1", "2.2"), "foobar"); + t.deepEqual(comparatorStub.getCall(0).args, ["1.1.0", "2.2.0"]); + + t.is(t.throws(() => { + __localFunctions__.handleSemverComparator(undefined, "a.b", "2.2"); + }).message, unsupportedSpecVersionText("a.b")); + + t.is(t.throws(() => { + __localFunctions__.handleSemverComparator(undefined, undefined, "a.b"); + }).message, "Invalid spec version expectation given in comparator: a.b"); +}); From af1b134d282d9ae6f85cd7f99c4c028ff04b4662 Mon Sep 17 00:00:00 2001 From: Merlin Beutlberger Date: Wed, 23 Nov 2022 15:53:33 +0100 Subject: [PATCH 03/10] [INTERNAL] SpecVersionComparator: Refactor static methods, add JSDoc Static methods now create an instance and always throw for unsupported versions (except isSupportedSpecVersion) --- lib/specifications/Specification.js | 19 +- .../utils/SpecVersionComparator.js | 227 ++++++++++++++---- test/lib/specifications/Specification.js | 4 +- .../utils/SpecVersionComparator.js | 119 ++++++--- 4 files changed, 281 insertions(+), 88 deletions(-) diff --git a/lib/specifications/Specification.js b/lib/specifications/Specification.js index 7a13a8b20..3e743e452 100644 --- a/lib/specifications/Specification.js +++ b/lib/specifications/Specification.js @@ -51,11 +51,9 @@ class Specification { const config = JSON.parse(JSON.stringify(configuration)); const {validate} = await import("../validation/validator.js"); - // SpecVersionComparator throws for invalid/unknown specVersion on instantiation - const specVersionComparator = new SpecVersionComparator(config.specVersion); - if (specVersionComparator.major() <= 1) { + if (SpecVersionComparator.major(config.specVersion) <= 1) { const originalSpecVersion = config.specVersion; - this._log.verbose(`Detected legacy specification version ${config.specVersion}, defined for ` + + this._log.verbose(`Detected legacy Specification Version ${config.specVersion}, defined for ` + `${config.kind} ${config.metadata.name}. ` + `Attempting to migrate the project to a supported specification version...`); this._migrateLegacyProject(config); @@ -71,7 +69,7 @@ class Specification { `Validation error after migration of ${config.kind} ${config.metadata.name}:`); this._log.verbose(err.message); throw new Error( - `${config.kind} ${config.metadata.name} defines unsupported specification version ` + + `${config.kind} ${config.metadata.name} defines unsupported Specification Version ` + `${originalSpecVersion}. Please manually upgrade to 2.0 or higher. ` + `For details see https://sap.github.io/ui5-tooling/pages/Configuration/#specification-versions - ` + `An attempted migration to a supported specification version failed, ` + @@ -146,18 +144,21 @@ class Specification { } /** - * Get the specification's generic version, as typically defined in a package.json + * Returns an instance of a helper class to compare the Specification Version with * * @public - * @returns {string} Project version + * @returns {@ui5/project/specifications/utils/SpecVersionComparator} */ getSpecVersionComparator() { return new SpecVersionComparator(this.getSpecVersion()); } /** - * @public - */ + * Get the specification's generic version, as typically defined in a package.json + * + * @public + * @returns {string} Project version + */ getVersion() { return this._version; } diff --git a/lib/specifications/utils/SpecVersionComparator.js b/lib/specifications/utils/SpecVersionComparator.js index 40141caea..2ea10af25 100644 --- a/lib/specifications/utils/SpecVersionComparator.js +++ b/lib/specifications/utils/SpecVersionComparator.js @@ -7,90 +7,235 @@ const SUPPORTED_VERSIONS = [ "3.0" ]; +/** + * Utility class for comparing Specification Versions + * + * @public + * @class + * @alias @ui5/project/specifications/utils/SpecVersionComparator + */ class SpecVersionComparator { - constructor(specVersion) { - this._specVersion = specVersion; + #specVersion; + #semverVersion; - if (!SpecVersionComparator.isSupportedSpecVersion(specVersion)) { - throw new Error(getUnsupportedSpecVersionMessage(specVersion)); - } + /** + * @param {string} specVersion Specification Version to use for all comparison operations + * @throws {Error} Throws if provided Specification Version is not supported by this version of @ui5/project + */ + constructor(specVersion) { + this.#specVersion = specVersion; + this.#semverVersion = getSemverCompatibleVersion(specVersion); // Throws for unsupported versions } + /** + * Returns the major-version of the instance's Specification Version + * + * @returns {integer} Major version + */ major() { - return SpecVersionComparator.major(this._specVersion); + return semver.major(this.#semverVersion); } + /** + * Returns the minor-version of the instance's Specification Version + * + * @returns {integer} Minor version + */ minor() { - return SpecVersionComparator.minor(this._specVersion); + return semver.minor(this.#semverVersion); } + /** + * Test whether the instance's Specification Version falls into the provided range + * + * @param {string} range [Semver]{@link https://www.npmjs.com/package/semver}-style version range. + * For example 2.2 - 2.4 + * @returns {boolean} True if the instance's Specification Version falls into the provided range + */ satisfies(range) { - return SpecVersionComparator.satisfies(this._specVersion); + return semver.satisfies(this.#semverVersion, range); } - // Test whether project's specVersion is greater than testVersion + /** + * Test whether the instance's Specification Version is greater than the provided test version + * + * @param {string} testVersion A Specification Version to compare the instance's Specification Version to + * @returns {boolean} True if the instance's Specification Version is greater than the provided version + */ gt(testVersion) { - return SpecVersionComparator.gt(this._specVersion, testVersion); + return handleSemverComparator(semver.gt, this.#semverVersion, testVersion); } + /** + * Test whether the instance's Specification Version is greater than or equal the provided test version + * + * @param {string} testVersion A Specification Version to compare the instance's Specification Version to + * @returns {boolean} True if the instance's Specification Version is greater than or equal the provided version + */ gte(testVersion) { - return SpecVersionComparator.gte(this._specVersion, testVersion); + return handleSemverComparator(semver.gte, this.#semverVersion, testVersion); } + /** + * Test whether the instance's Specification Version is smaller than the provided test version + * + * @param {string} testVersion A Specification Version to compare the instance's Specification Version to + * @returns {boolean} True if the instance's Specification Version is smaller than the provided version + */ lt(testVersion) { - return SpecVersionComparator.lt(this._specVersion, testVersion); + return handleSemverComparator(semver.lt, this.#semverVersion, testVersion); } + /** + * Test whether the instance's Specification Version is smaller than or equal the provided test version + * + * @param {string} testVersion A Specification Version to compare the instance's Specification Version to + * @returns {boolean} True if the instance's Specification Version is smaller than or equal the provided version + */ lte(testVersion) { - return SpecVersionComparator.lte(this._specVersion, testVersion); + return handleSemverComparator(semver.lte, this.#semverVersion, testVersion); } + /** + * Test whether the instance's Specification Version is equal to the provided test version + * + * @param {string} testVersion A Specification Version to compare the instance's Specification Version to + * @returns {boolean} True if the instance's Specification Version is equal to the provided version + */ eq(testVersion) { - return SpecVersionComparator.eq(this._specVersion, testVersion); + return handleSemverComparator(semver.eq, this.#semverVersion, testVersion); } + /** + * Test whether the instance's Specification Version is not equal to the provided test version + * + * @param {string} testVersion A Specification Version to compare the instance's Specification Version to + * @returns {boolean} True if the instance's Specification Version is not equal to the provided version + */ neq(testVersion) { - return SpecVersionComparator.neq(this._specVersion, testVersion); + return handleSemverComparator(semver.neq, this.#semverVersion, testVersion); } - static isSupportedSpecVersion(specVersion) { - return SUPPORTED_VERSIONS.includes(specVersion); + /** + * Test whether the provided Specification Version is supported by this version of @ui5/project + * + * @param {string} testVersion A Specification Version to compare the instance's Specification Version to + * @returns {boolean} True if the provided Specification Version is supported + */ + static isSupportedSpecVersion(testVersion) { + return SUPPORTED_VERSIONS.includes(testVersion); } + /** + * Returns the major-version of provided Specification Version + * + * @param {string} specVersion Specification Version + * @returns {integer} Major version + */ static major(specVersion) { - const version = getSemverCompatibleVersion(specVersion); - return semver.major(version); + const comparator = new SpecVersionComparator(specVersion); + return comparator.major(); } + + /** + * Returns the minor-version of the provided Specification Version + * + * @param {string} specVersion Specification Version + * @returns {integer} Minor version + */ static minor(specVersion) { - const version = getSemverCompatibleVersion(specVersion); - return semver.minor(version); + const comparator = new SpecVersionComparator(specVersion); + return comparator.minor(); } + + /** + * Test whether the provided Specification Version falls into the provided range + * + * @param {string} specVersion Specification Version + * @param {string} range [Semver]{@link https://www.npmjs.com/package/semver}-style version range. + * For example 2.2 - 2.4 + * @returns {boolean} True if the provided Specification Version falls into the provided range + */ static satisfies(specVersion, range) { - const version = getSemverCompatibleVersion(specVersion); - return semver.satisfies(version, range); + const comparator = new SpecVersionComparator(specVersion); + return comparator.satisfies(range); } - static gt(specVersion, expectedVersion) { - return handleSemverComparator(semver.gt, specVersion, expectedVersion); + + /** + * Test whether the provided Specification Version is greater than the provided test version + * + * @param {string} specVersion Specification Version + * @param {string} testVersion A Specification Version to compare the provided Specification Version to + * @returns {boolean} True if the provided Specification Version is greater than the provided version + */ + static gt(specVersion, testVersion) { + const comparator = new SpecVersionComparator(specVersion); + return comparator.gt(testVersion); } - static gte(specVersion, expectedVersion) { - return handleSemverComparator(semver.gte, specVersion, expectedVersion); + + /** + * Test whether the provided Specification Version is greater than or equal the provided test version + * + * @param {string} specVersion Specification Version + * @param {string} testVersion A Specification Version to compare the provided Specification Version to + * @returns {boolean} True if the provided Specification Version is greater than or equal the provided version + */ + static gte(specVersion, testVersion) { + const comparator = new SpecVersionComparator(specVersion); + return comparator.gte(testVersion); } - static lt(specVersion, expectedVersion) { - return handleSemverComparator(semver.lt, specVersion, expectedVersion); + + /** + * Test whether the provided Specification Version is smaller than the provided test version + * + * @param {string} specVersion Specification Version + * @param {string} testVersion A Specification Version to compare the provided Specification Version to + * @returns {boolean} True if the provided Specification Version is smaller than the provided version + */ + static lt(specVersion, testVersion) { + const comparator = new SpecVersionComparator(specVersion); + return comparator.lt(testVersion); } - static lte(specVersion, expectedVersion) { - return handleSemverComparator(semver.lte, specVersion, expectedVersion); + + /** + * Test whether the provided Specification Version is smaller than or equal the provided test version + * + * @param {string} specVersion Specification Version + * @param {string} testVersion A Specification Version to compare the provided Specification Version to + * @returns {boolean} True if the provided Specification Version is smaller than or equal the provided version + */ + static lte(specVersion, testVersion) { + const comparator = new SpecVersionComparator(specVersion); + return comparator.lte(testVersion); } - static eq(specVersion, expectedVersion) { - return handleSemverComparator(semver.eq, specVersion, expectedVersion); + + /** + * Test whether the provided Specification Version is equal to the provided test version + * + * @param {string} specVersion Specification Version + * @param {string} testVersion A Specification Version to compare the provided Specification Version to + * @returns {boolean} True if the provided Specification Version is equal to the provided version + */ + static eq(specVersion, testVersion) { + const comparator = new SpecVersionComparator(specVersion); + return comparator.eq(testVersion); } - static neq(specVersion, expectedVersion) { - return handleSemverComparator(semver.neq, specVersion, expectedVersion); + + /** + * Test whether the provided Specification Version is not equal to the provided test version + * + * @param {string} specVersion Specification Version + * @param {string} testVersion A Specification Version to compare the provided Specification Version to + * @returns {boolean} True if the provided Specification Version is not equal to the provided version + */ + static neq(specVersion, testVersion) { + const comparator = new SpecVersionComparator(specVersion); + return comparator.neq(testVersion); } } function getUnsupportedSpecVersionMessage(specVersion) { - return `Unsupported specification version ${specVersion} defined. Your UI5 CLI installation might be outdated. ` + + return `Unsupported Specification Version ${specVersion} defined. Your UI5 CLI installation might be outdated. ` + `For details see https://sap.github.io/ui5-tooling/pages/Configuration/#specification-versions`; } @@ -101,13 +246,13 @@ function getSemverCompatibleVersion(specVersion) { throw new Error(getUnsupportedSpecVersionMessage(specVersion)); } -function handleSemverComparator(comparator, specVersion, expectedVersion) { - if (SPEC_VERSION_PATTERN.test(expectedVersion)) { - const a = getSemverCompatibleVersion(specVersion); - const b = expectedVersion + ".0"; +function handleSemverComparator(comparator, baseVersion, testVersion) { + if (SPEC_VERSION_PATTERN.test(testVersion)) { + const a = baseVersion; + const b = testVersion + ".0"; return comparator(a, b); } - throw new Error("Invalid spec version expectation given in comparator: " + expectedVersion); + throw new Error("Invalid spec version expectation given in comparator: " + testVersion); } export default SpecVersionComparator; diff --git a/test/lib/specifications/Specification.js b/test/lib/specifications/Specification.js index 1df65f969..904be2140 100644 --- a/test/lib/specifications/Specification.js +++ b/test/lib/specifications/Specification.js @@ -122,7 +122,7 @@ test("Migrate legacy project unexpected configuration", async (t) => { const err = await t.throwsAsync(Specification.create(t.context.basicProjectInput)); t.is(err.message, - "project application.a defines unsupported specification version 1.0. Please manually upgrade to 2.0 or " + + "project application.a defines unsupported Specification Version 1.0. Please manually upgrade to 2.0 or " + "higher. For details see https://sap.github.io/ui5-tooling/pages/Configuration/#specification-versions - " + "An attempted migration to a supported specification version failed, likely due to unrecognized " + "configuration. Check verbose log for details.", @@ -295,7 +295,7 @@ test("Invalid specVersion", async (t) => { t.context.basicProjectInput.configuration.specVersion = "0.5"; await t.throwsAsync(Specification.create(t.context.basicProjectInput), { message: - "Unsupported specification version 0.5 defined. Your UI5 CLI installation might be outdated. " + + "Unsupported Specification Version 0.5 defined. Your UI5 CLI installation might be outdated. " + "For details see https://sap.github.io/ui5-tooling/pages/Configuration/#specification-versions" }, "Threw with expected error message"); }); diff --git a/test/lib/specifications/utils/SpecVersionComparator.js b/test/lib/specifications/utils/SpecVersionComparator.js index 517dc714a..9587c3fcf 100644 --- a/test/lib/specifications/utils/SpecVersionComparator.js +++ b/test/lib/specifications/utils/SpecVersionComparator.js @@ -4,7 +4,7 @@ import SpecVersionComparator from "../../../../lib/specifications/utils/SpecVers import {__localFunctions__} from "../../../../lib/specifications/utils/SpecVersionComparator.js"; const unsupportedSpecVersionText = (specVersion) => - `Unsupported specification version ${specVersion} defined. Your UI5 CLI installation might be outdated. ` + + `Unsupported Specification Version ${specVersion} defined. Your UI5 CLI installation might be outdated. ` + `For details see https://sap.github.io/ui5-tooling/pages/Configuration/#specification-versions`; test.beforeEach((t) => { @@ -31,35 +31,86 @@ test.serial("Invalid specVersion", (t) => { "Static isSupportedSpecVersionStub has been called with expected arguments"); }); +test("(instance) major", (t) => { + t.is(new SpecVersionComparator("0.1").major(), 0); + t.is(new SpecVersionComparator("1.1").major(), 1); + t.is(new SpecVersionComparator("2.1").major(), 2); -function testCallThroughToStatic(functionName, arg) { - test.serial(functionName, (t) => { - const {sinon} = t.context; - const staticFunctionStub = - sinon.stub(SpecVersionComparator, functionName).returns("return value"); - - const specVersionComparator = new SpecVersionComparator("2.5"); - const res = specVersionComparator[functionName](arg); - t.is(staticFunctionStub.callCount, 1, `Static #${functionName} has been called once`); - const expectedArgs = ["2.5"]; - if (arg) { - expectedArgs.push(arg); - } - t.deepEqual(staticFunctionStub.getCall(0).args, expectedArgs, - `Static #${functionName} has been called with expected arguments`); - t.is(res, "return value", "Returned value of static function"); - }); -} - -testCallThroughToStatic("major"); -testCallThroughToStatic("minor"); -testCallThroughToStatic("satisfies"); -testCallThroughToStatic("gt", "5.2"); -testCallThroughToStatic("gte", "5.2"); -testCallThroughToStatic("lt", "5.2"); -testCallThroughToStatic("lte", "5.2"); -testCallThroughToStatic("eq", "5.2"); -testCallThroughToStatic("neq", "5.2"); + t.is(t.throws(() => { + new SpecVersionComparator("0.2").major(); + }).message, unsupportedSpecVersionText("0.2")); +}); + +test("(instance) minor", (t) => { + t.is(new SpecVersionComparator("2.1").minor(), 1); + t.is(new SpecVersionComparator("2.2").minor(), 2); + t.is(new SpecVersionComparator("2.3").minor(), 3); + + t.is(t.throws(() => { + new SpecVersionComparator("1.2").minor(); + }).message, unsupportedSpecVersionText("1.2")); +}); + +test("(instance) satisfies", (t) => { + // range: 1.x + t.is(new SpecVersionComparator("1.0").satisfies("1.x"), true); + t.is(new SpecVersionComparator("1.1").satisfies("1.x"), true); + t.is(new SpecVersionComparator("2.0").satisfies("1.x"), false); + + // range: ^2.2 + t.is(new SpecVersionComparator("2.1").satisfies("^2.2"), false); + t.is(new SpecVersionComparator("2.2").satisfies("^2.2"), true); + t.is(new SpecVersionComparator("2.3").satisfies("^2.2"), true); + + // range: > 1.0 + t.is(new SpecVersionComparator("1.0").satisfies("> 1.0"), false); + t.is(new SpecVersionComparator("1.1").satisfies("> 1.0"), true); + t.is(new SpecVersionComparator("2.2").satisfies("> 1.0"), true); + + // range: 2.2 - 2.4 + t.is(new SpecVersionComparator("2.1").satisfies("2.2 - 2.4"), false); + t.is(new SpecVersionComparator("2.2").satisfies("2.2 - 2.4"), true); + t.is(new SpecVersionComparator("2.3").satisfies("2.2 - 2.4"), true); + t.is(new SpecVersionComparator("2.4").satisfies("2.2 - 2.4"), true); + t.is(new SpecVersionComparator("2.5").satisfies("2.2 - 2.4"), false); + + // range: 0.1 || 1.0 - 1.1 || ^2.5 + t.is(new SpecVersionComparator("0.1").satisfies("0.1 || 1.0 - 1.1 || ^2.5"), true); + t.is(new SpecVersionComparator("1.0").satisfies("0.1 || 1.0 - 1.1 || ^2.5"), true); + t.is(new SpecVersionComparator("1.1").satisfies("0.1 || 1.0 - 1.1 || ^2.5"), true); + t.is(new SpecVersionComparator("2.4").satisfies("0.1 || 1.0 - 1.1 || ^2.5"), false); + t.is(new SpecVersionComparator("2.5").satisfies("0.1 || 1.0 - 1.1 || ^2.5"), true); + t.is(new SpecVersionComparator("2.6").satisfies("0.1 || 1.0 - 1.1 || ^2.5"), true); + + // unsupported spec version + t.is(t.throws(() => { + new SpecVersionComparator("0.2").satisfies("1.x"); + }).message, unsupportedSpecVersionText("0.2")); +}); + +test("(instance) low level comparator", (t) => { + t.is(new SpecVersionComparator("2.1").gt("2.2"), false); + t.is(new SpecVersionComparator("2.2").gt("2.2"), false); + t.is(new SpecVersionComparator("2.3").gt("2.2"), true); + + t.is(new SpecVersionComparator("2.1").gte("2.2"), false); + t.is(new SpecVersionComparator("2.2").gte("2.2"), true); + t.is(new SpecVersionComparator("2.3").gte("2.2"), true); + + t.is(new SpecVersionComparator("2.1").lt("2.2"), true); + t.is(new SpecVersionComparator("2.2").lt("2.2"), false); + t.is(new SpecVersionComparator("2.3").lt("2.2"), false); + + t.is(new SpecVersionComparator("2.1").lte("2.2"), true); + t.is(new SpecVersionComparator("2.2").lte("2.2"), true); + t.is(new SpecVersionComparator("2.3").lte("2.2"), false); + + t.is(new SpecVersionComparator("2.0").eq("2.2"), false); + t.is(new SpecVersionComparator("2.2").eq("2.2"), true); + + t.is(new SpecVersionComparator("2.0").neq("2.2"), true); + t.is(new SpecVersionComparator("2.2").neq("2.2"), false); +}); test("(static) isSupportedSpecVersion", (t) => { t.is(SpecVersionComparator.isSupportedSpecVersion("0.1"), true); @@ -156,7 +207,7 @@ test("(static) low level comparator", (t) => { t.is(SpecVersionComparator.neq("2.2", "2.2"), false); }); -test("(static) getSemverCompatibleVersion", (t) => { +test("getSemverCompatibleVersion", (t) => { t.is(__localFunctions__.getSemverCompatibleVersion("0.1"), "0.1.0"); t.is(__localFunctions__.getSemverCompatibleVersion("1.1"), "1.1.0"); t.is(__localFunctions__.getSemverCompatibleVersion("2.0"), "2.0.0"); @@ -175,15 +226,11 @@ test("(static) getSemverCompatibleVersion", (t) => { }).message, unsupportedSpecVersionText("undefined")); }); -test("(static) handleSemverComparator", (t) => { +test("handleSemverComparator", (t) => { const comparatorStub = t.context.sinon.stub().returns("foobar"); - t.is(__localFunctions__.handleSemverComparator(comparatorStub, "1.1", "2.2"), "foobar"); + t.is(__localFunctions__.handleSemverComparator(comparatorStub, "1.1.0", "2.2"), "foobar"); t.deepEqual(comparatorStub.getCall(0).args, ["1.1.0", "2.2.0"]); - t.is(t.throws(() => { - __localFunctions__.handleSemverComparator(undefined, "a.b", "2.2"); - }).message, unsupportedSpecVersionText("a.b")); - t.is(t.throws(() => { __localFunctions__.handleSemverComparator(undefined, undefined, "a.b"); }).message, "Invalid spec version expectation given in comparator: a.b"); From 80746152990a32e7c66bd30dbd97aa5dddf56db8 Mon Sep 17 00:00:00 2001 From: Merlin Beutlberger Date: Thu, 24 Nov 2022 17:29:52 +0100 Subject: [PATCH 04/10] [INTERNAL] Update JSDoc --- lib/specifications/Specification.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/specifications/Specification.js b/lib/specifications/Specification.js index 3e743e452..c86f03118 100644 --- a/lib/specifications/Specification.js +++ b/lib/specifications/Specification.js @@ -144,7 +144,8 @@ class Specification { } /** - * Returns an instance of a helper class to compare the Specification Version with + * Returns an instance of a helper class allowing for convenient comparison + * operations against this instance's Specification Version * * @public * @returns {@ui5/project/specifications/utils/SpecVersionComparator} From 1890a14d27fbcf1777a36d2ea52c0fefefa33f6a Mon Sep 17 00:00:00 2001 From: Merlin Beutlberger Date: Thu, 24 Nov 2022 21:26:25 +0100 Subject: [PATCH 05/10] [INTERNAL] TaskUtil: Accept SpecVersionComparator instance instead of specVersion string --- lib/build/TaskRunner.js | 2 +- lib/build/helpers/TaskUtil.js | 26 ++++------- .../utils/SpecVersionComparator.js | 30 +++++++++++++ test/lib/build/TaskRunner.js | 30 ++++++++----- test/lib/build/helpers/TaskUtil.js | 44 +++++-------------- .../utils/SpecVersionComparator.js | 5 +++ 6 files changed, 74 insertions(+), 63 deletions(-) diff --git a/lib/build/TaskRunner.js b/lib/build/TaskRunner.js index 605619a8d..bf8da96e3 100644 --- a/lib/build/TaskRunner.js +++ b/lib/build/TaskRunner.js @@ -314,7 +314,7 @@ class TaskRunner { params.log = logger.getGroupLogger(`builder:custom-task:${newTaskName}`); } - const taskUtilInterface = taskUtil.getInterface(task.getSpecVersion()); + const taskUtilInterface = taskUtil.getInterface(task.getSpecVersionComparator()); // Interface is undefined if specVersion does not support taskUtil if (taskUtilInterface) { params.taskUtil = taskUtilInterface; diff --git a/lib/build/helpers/TaskUtil.js b/lib/build/helpers/TaskUtil.js index 1529accf1..378a6f8f2 100644 --- a/lib/build/helpers/TaskUtil.js +++ b/lib/build/helpers/TaskUtil.js @@ -269,11 +269,13 @@ class TaskUtil { * Get an interface to an instance of this class that only provides those functions * that are supported by the given custom task extension specification version. * - * @param {string} specVersion Specification version of custom task extension + * @param {@ui5/project/specifications/utils/SpecVersionComparator} specVersion + * SpecVersionComparator instance of the custom task * @returns {object} An object with bound instance methods supported by the given specification version */ getInterface(specVersion) { - if (["0.1", "1.0", "1.1", "2.0", "2.1"].includes(specVersion)) { + if (specVersion.lte("2.1")) { + // Tasks defining specVersion <= 2.1 do not have access to any TaskUtil APIs return undefined; } @@ -283,14 +285,8 @@ class TaskUtil { bindFunctions(this, baseInterface, [ "setTag", "clearTag", "getTag", "isRootProject", "registerCleanupTask" ]); - switch (specVersion) { - case "2.2": - case "2.3": - case "2.4": - case "2.5": - case "2.6": - return baseInterface; - case "3.0": + + if (specVersion.gte("3.0")) { // getProject function, returning an interfaced project instance baseInterface.getProject = (projectName) => { const project = this.getProject(projectName); @@ -299,10 +295,7 @@ class TaskUtil { "getSpecVersion", "getType", "getName", "getVersion", "getNamespace", "getRootReader", "getReader", "getCustomConfiguration", "isFrameworkProject" ]); - switch (specVersion) { - case "3.0": - return baseProjectInterface; - } + return baseProjectInterface; }; // getDependencies function, returning an array of project names baseInterface.getDependencies = (projectName) => { @@ -318,11 +311,8 @@ class TaskUtil { ].forEach((factoryFunction) => { baseInterface.resourceFactory[factoryFunction] = this.resourceFactory[factoryFunction]; }); - - return baseInterface; - default: - throw new Error(`TaskUtil: Unknown or unsupported Specification Version ${specVersion}`); } + return baseInterface; } } diff --git a/lib/specifications/utils/SpecVersionComparator.js b/lib/specifications/utils/SpecVersionComparator.js index 2ea10af25..e59c11be4 100644 --- a/lib/specifications/utils/SpecVersionComparator.js +++ b/lib/specifications/utils/SpecVersionComparator.js @@ -19,6 +19,7 @@ class SpecVersionComparator { #semverVersion; /** + * @public * @param {string} specVersion Specification Version to use for all comparison operations * @throws {Error} Throws if provided Specification Version is not supported by this version of @ui5/project */ @@ -27,9 +28,20 @@ class SpecVersionComparator { this.#semverVersion = getSemverCompatibleVersion(specVersion); // Throws for unsupported versions } + /** + * Returns the Specification Version that is used in all comparisons + * + * @public + * @returns {string} Specification Version + */ + getSpecVersion() { + return this.#specVersion; + } + /** * Returns the major-version of the instance's Specification Version * + * @public * @returns {integer} Major version */ major() { @@ -39,6 +51,7 @@ class SpecVersionComparator { /** * Returns the minor-version of the instance's Specification Version * + * @public * @returns {integer} Minor version */ minor() { @@ -48,6 +61,7 @@ class SpecVersionComparator { /** * Test whether the instance's Specification Version falls into the provided range * + * @public * @param {string} range [Semver]{@link https://www.npmjs.com/package/semver}-style version range. * For example 2.2 - 2.4 * @returns {boolean} True if the instance's Specification Version falls into the provided range @@ -59,6 +73,7 @@ class SpecVersionComparator { /** * Test whether the instance's Specification Version is greater than the provided test version * + * @public * @param {string} testVersion A Specification Version to compare the instance's Specification Version to * @returns {boolean} True if the instance's Specification Version is greater than the provided version */ @@ -69,6 +84,7 @@ class SpecVersionComparator { /** * Test whether the instance's Specification Version is greater than or equal the provided test version * + * @public * @param {string} testVersion A Specification Version to compare the instance's Specification Version to * @returns {boolean} True if the instance's Specification Version is greater than or equal the provided version */ @@ -79,6 +95,7 @@ class SpecVersionComparator { /** * Test whether the instance's Specification Version is smaller than the provided test version * + * @public * @param {string} testVersion A Specification Version to compare the instance's Specification Version to * @returns {boolean} True if the instance's Specification Version is smaller than the provided version */ @@ -89,6 +106,7 @@ class SpecVersionComparator { /** * Test whether the instance's Specification Version is smaller than or equal the provided test version * + * @public * @param {string} testVersion A Specification Version to compare the instance's Specification Version to * @returns {boolean} True if the instance's Specification Version is smaller than or equal the provided version */ @@ -99,6 +117,7 @@ class SpecVersionComparator { /** * Test whether the instance's Specification Version is equal to the provided test version * + * @public * @param {string} testVersion A Specification Version to compare the instance's Specification Version to * @returns {boolean} True if the instance's Specification Version is equal to the provided version */ @@ -109,6 +128,7 @@ class SpecVersionComparator { /** * Test whether the instance's Specification Version is not equal to the provided test version * + * @public * @param {string} testVersion A Specification Version to compare the instance's Specification Version to * @returns {boolean} True if the instance's Specification Version is not equal to the provided version */ @@ -119,6 +139,7 @@ class SpecVersionComparator { /** * Test whether the provided Specification Version is supported by this version of @ui5/project * + * @public * @param {string} testVersion A Specification Version to compare the instance's Specification Version to * @returns {boolean} True if the provided Specification Version is supported */ @@ -129,6 +150,7 @@ class SpecVersionComparator { /** * Returns the major-version of provided Specification Version * + * @public * @param {string} specVersion Specification Version * @returns {integer} Major version */ @@ -140,6 +162,7 @@ class SpecVersionComparator { /** * Returns the minor-version of the provided Specification Version * + * @public * @param {string} specVersion Specification Version * @returns {integer} Minor version */ @@ -151,6 +174,7 @@ class SpecVersionComparator { /** * Test whether the provided Specification Version falls into the provided range * + * @public * @param {string} specVersion Specification Version * @param {string} range [Semver]{@link https://www.npmjs.com/package/semver}-style version range. * For example 2.2 - 2.4 @@ -164,6 +188,7 @@ class SpecVersionComparator { /** * Test whether the provided Specification Version is greater than the provided test version * + * @public * @param {string} specVersion Specification Version * @param {string} testVersion A Specification Version to compare the provided Specification Version to * @returns {boolean} True if the provided Specification Version is greater than the provided version @@ -176,6 +201,7 @@ class SpecVersionComparator { /** * Test whether the provided Specification Version is greater than or equal the provided test version * + * @public * @param {string} specVersion Specification Version * @param {string} testVersion A Specification Version to compare the provided Specification Version to * @returns {boolean} True if the provided Specification Version is greater than or equal the provided version @@ -188,6 +214,7 @@ class SpecVersionComparator { /** * Test whether the provided Specification Version is smaller than the provided test version * + * @public * @param {string} specVersion Specification Version * @param {string} testVersion A Specification Version to compare the provided Specification Version to * @returns {boolean} True if the provided Specification Version is smaller than the provided version @@ -200,6 +227,7 @@ class SpecVersionComparator { /** * Test whether the provided Specification Version is smaller than or equal the provided test version * + * @public * @param {string} specVersion Specification Version * @param {string} testVersion A Specification Version to compare the provided Specification Version to * @returns {boolean} True if the provided Specification Version is smaller than or equal the provided version @@ -212,6 +240,7 @@ class SpecVersionComparator { /** * Test whether the provided Specification Version is equal to the provided test version * + * @public * @param {string} specVersion Specification Version * @param {string} testVersion A Specification Version to compare the provided Specification Version to * @returns {boolean} True if the provided Specification Version is equal to the provided version @@ -224,6 +253,7 @@ class SpecVersionComparator { /** * Test whether the provided Specification Version is not equal to the provided test version * + * @public * @param {string} specVersion Specification Version * @param {string} testVersion A Specification Version to compare the provided Specification Version to * @returns {boolean} True if the provided Specification Version is not equal to the provided version diff --git a/test/lib/build/TaskRunner.js b/test/lib/build/TaskRunner.js index 77f46f011..92ad52eb5 100644 --- a/test/lib/build/TaskRunner.js +++ b/test/lib/build/TaskRunner.js @@ -393,7 +393,8 @@ test("Custom task is called correctly", async (t) => { const taskStub = sinon.stub(); graph.getExtension.returns({ getTask: () => taskStub, - getSpecVersion: () => "2.6" + getSpecVersion: () => "2.6", + getSpecVersionComparator: () => "specVersionComparator 2.6" }); t.context.taskUtil.getInterface.returns("taskUtil interface"); const project = getMockProject("module"); @@ -426,7 +427,7 @@ test("Custom task is called correctly", async (t) => { }, "Task got called with one argument"); t.is(taskUtil.getInterface.callCount, 1, "taskUtil#getInterface got called once"); - t.is(taskUtil.getInterface.getCall(0).args[0], "2.6", + t.is(taskUtil.getInterface.getCall(0).args[0], "specVersionComparator 2.6", "taskUtil#getInterface got called with correct argument"); }); @@ -435,7 +436,8 @@ test("Custom task with legacy spec version", async (t) => { const taskStub = sinon.stub(); graph.getExtension.returns({ getTask: () => taskStub, - getSpecVersion: () => "1.0" + getSpecVersion: () => "1.0", + getSpecVersionComparator: () => "specVersionComparator 1.0" }); t.context.taskUtil.getInterface.returns(undefined); // simulating no taskUtil for old specVersion const project = getMockProject("module"); @@ -467,7 +469,7 @@ test("Custom task with legacy spec version", async (t) => { }, "Task got called with one argument"); t.is(taskUtil.getInterface.callCount, 1, "taskUtil#getInterface got called once"); - t.is(taskUtil.getInterface.getCall(0).args[0], "1.0", + t.is(taskUtil.getInterface.getCall(0).args[0], "specVersionComparator 1.0", "taskUtil#getInterface got called with correct argument"); }); @@ -476,7 +478,8 @@ test("Custom task with specVersion 3.0", async (t) => { const taskStub = sinon.stub(); graph.getExtension.returns({ getTask: () => taskStub, - getSpecVersion: () => "3.0" + getSpecVersion: () => "3.0", + getSpecVersionComparator: () => "specVersionComparator 3.0" }); t.context.taskUtil.getInterface.returns(undefined); // simulating no taskUtil for old specVersion const project = getMockProject("module"); @@ -510,7 +513,7 @@ test("Custom task with specVersion 3.0", async (t) => { }, "Task got called with one argument"); t.is(taskUtil.getInterface.callCount, 1, "taskUtil#getInterface got called once"); - t.is(taskUtil.getInterface.getCall(0).args[0], "3.0", + t.is(taskUtil.getInterface.getCall(0).args[0], "specVersionComparator 3.0", "taskUtil#getInterface got called with correct argument"); }); @@ -521,15 +524,18 @@ test("Multiple custom tasks with same name are called correctly", async (t) => { const taskStub3 = sinon.stub(); graph.getExtension.onFirstCall().returns({ getTask: () => taskStub1, - getSpecVersion: () => "2.5" + getSpecVersion: () => "2.5", + getSpecVersionComparator: () => "specVersionComparator 2.5" }); graph.getExtension.onSecondCall().returns({ getTask: () => taskStub2, - getSpecVersion: () => "2.6" + getSpecVersion: () => "2.6", + getSpecVersionComparator: () => "specVersionComparator 2.6" }); graph.getExtension.onThirdCall().returns({ getTask: () => taskStub3, - getSpecVersion: () => "3.0" + getSpecVersion: () => "3.0", + getSpecVersionComparator: () => "specVersionComparator 3.0" }); const project = getMockProject("module"); project.getCustomTasks = () => [ @@ -599,11 +605,11 @@ test("Multiple custom tasks with same name are called correctly", async (t) => { }, "Task 3 got called with one argument"); t.is(taskUtil.getInterface.callCount, 3, "taskUtil#getInterface got called once"); - t.is(taskUtil.getInterface.getCall(0).args[0], "2.5", + t.is(taskUtil.getInterface.getCall(0).args[0], "specVersionComparator 2.5", "taskUtil#getInterface got called with correct argument on first call"); - t.is(taskUtil.getInterface.getCall(1).args[0], "3.0", + t.is(taskUtil.getInterface.getCall(1).args[0], "specVersionComparator 3.0", "taskUtil#getInterface got called with correct argument on second call"); - t.is(taskUtil.getInterface.getCall(2).args[0], "2.6", + t.is(taskUtil.getInterface.getCall(2).args[0], "specVersionComparator 2.6", "taskUtil#getInterface got called with correct argument on third call"); }); diff --git a/test/lib/build/helpers/TaskUtil.js b/test/lib/build/helpers/TaskUtil.js index bc2a57c5f..392aa3d0a 100644 --- a/test/lib/build/helpers/TaskUtil.js +++ b/test/lib/build/helpers/TaskUtil.js @@ -1,11 +1,16 @@ import test from "ava"; import sinon from "sinon"; import TaskUtil from "../../../../lib/build/helpers/TaskUtil.js"; +import SpecVersionComparator from "../../../../lib/specifications/utils/SpecVersionComparator.js"; test.afterEach.always((t) => { sinon.restore(); }); +function getSpecVersionComparator(specVersion) { + return new SpecVersionComparator(specVersion); +} + const STANDARD_TAGS = Object.freeze({ IsDebugVariant: "ui5:IsDebugVariant", HasDebugVariant: "ui5:HasDebugVariant", @@ -202,7 +207,7 @@ test("getInterface: specVersion 1.0", (t) => { projectBuildContext: {} }); - const interfacedTaskUtil = taskUtil.getInterface("1.0"); + const interfacedTaskUtil = taskUtil.getInterface(getSpecVersionComparator("1.0")); t.is(interfacedTaskUtil, undefined, "no interface provided"); }); @@ -212,7 +217,7 @@ test("getInterface: specVersion 2.2", (t) => { projectBuildContext: {} }); - const interfacedTaskUtil = taskUtil.getInterface("2.2"); + const interfacedTaskUtil = taskUtil.getInterface(getSpecVersionComparator("2.2")); t.deepEqual(Object.keys(interfacedTaskUtil), [ "STANDARD_TAGS", @@ -236,7 +241,7 @@ test("getInterface: specVersion 2.3", (t) => { projectBuildContext: {} }); - const interfacedTaskUtil = taskUtil.getInterface("2.3"); + const interfacedTaskUtil = taskUtil.getInterface(getSpecVersionComparator("2.3")); t.deepEqual(Object.keys(interfacedTaskUtil), [ "STANDARD_TAGS", @@ -260,7 +265,7 @@ test("getInterface: specVersion 2.4", (t) => { projectBuildContext: {} }); - const interfacedTaskUtil = taskUtil.getInterface("2.4"); + const interfacedTaskUtil = taskUtil.getInterface(getSpecVersionComparator("2.4")); t.deepEqual(Object.keys(interfacedTaskUtil), [ "STANDARD_TAGS", @@ -284,7 +289,7 @@ test("getInterface: specVersion 2.5", (t) => { projectBuildContext: {} }); - const interfacedTaskUtil = taskUtil.getInterface("2.5"); + const interfacedTaskUtil = taskUtil.getInterface(getSpecVersionComparator("2.5")); t.deepEqual(Object.keys(interfacedTaskUtil), [ "STANDARD_TAGS", @@ -308,7 +313,7 @@ test("getInterface: specVersion 2.6", (t) => { projectBuildContext: {} }); - const interfacedTaskUtil = taskUtil.getInterface("2.6"); + const interfacedTaskUtil = taskUtil.getInterface(getSpecVersionComparator("2.6")); t.deepEqual(Object.keys(interfacedTaskUtil), [ "STANDARD_TAGS", @@ -350,7 +355,7 @@ test("getInterface: specVersion 3.0", (t) => { } }); - const interfacedTaskUtil = taskUtil.getInterface("3.0"); + const interfacedTaskUtil = taskUtil.getInterface(getSpecVersionComparator("3.0")); t.deepEqual(Object.keys(interfacedTaskUtil), [ "STANDARD_TAGS", @@ -415,28 +420,3 @@ test("getInterface: specVersion 3.0", (t) => { t.is(typeof resourceFactory.createFlatReader, "function", "resourceFactory function createFlatReader is available"); }); - -test("getInterface: specVersion undefined", (t) => { - const taskUtil = new TaskUtil({ - projectBuildContext: {} - }); - - const err = t.throws(() => { - taskUtil.getInterface(); - }); - - t.is(err.message, "TaskUtil: Unknown or unsupported Specification Version undefined", - "Throw with correct error message"); -}); - -test("getInterface: specVersion unknown", (t) => { - const taskUtil = new TaskUtil({ - projectBuildContext: {} - }); - const err = t.throws(() => { - taskUtil.getInterface("1.5"); - }); - - t.is(err.message, "TaskUtil: Unknown or unsupported Specification Version 1.5", - "Throw with correct error message"); -}); diff --git a/test/lib/specifications/utils/SpecVersionComparator.js b/test/lib/specifications/utils/SpecVersionComparator.js index 9587c3fcf..4cac7cc72 100644 --- a/test/lib/specifications/utils/SpecVersionComparator.js +++ b/test/lib/specifications/utils/SpecVersionComparator.js @@ -31,6 +31,11 @@ test.serial("Invalid specVersion", (t) => { "Static isSupportedSpecVersionStub has been called with expected arguments"); }); +test("(instance) getSpecVersion", (t) => { + t.is(new SpecVersionComparator("0.1").getSpecVersion(), "0.1"); + t.is(new SpecVersionComparator("1.1").getSpecVersion(), "1.1"); +}); + test("(instance) major", (t) => { t.is(new SpecVersionComparator("0.1").major(), 0); t.is(new SpecVersionComparator("1.1").major(), 1); From e4c2433172efaa6a7dc3ce97e4b348078b19bbf8 Mon Sep 17 00:00:00 2001 From: Merlin Beutlberger Date: Fri, 25 Nov 2022 09:51:37 +0100 Subject: [PATCH 06/10] [INTERNAL] Apply suggestions from UA review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Günter Klatt <57760635+KlattG@users.noreply.github.com> --- .../utils/SpecVersionComparator.js | 28 +++++++++---------- test/lib/specifications/Specification.js | 2 +- .../utils/SpecVersionComparator.js | 2 +- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/lib/specifications/utils/SpecVersionComparator.js b/lib/specifications/utils/SpecVersionComparator.js index e59c11be4..27841ff8b 100644 --- a/lib/specifications/utils/SpecVersionComparator.js +++ b/lib/specifications/utils/SpecVersionComparator.js @@ -62,8 +62,8 @@ class SpecVersionComparator { * Test whether the instance's Specification Version falls into the provided range * * @public - * @param {string} range [Semver]{@link https://www.npmjs.com/package/semver}-style version range. - * For example 2.2 - 2.4 +@param {string} range [Semver]{@link https://www.npmjs.com/package/semver}-style version range, +for example 2.2 - 2.4 * @returns {boolean} True if the instance's Specification Version falls into the provided range */ satisfies(range) { @@ -82,11 +82,11 @@ class SpecVersionComparator { } /** - * Test whether the instance's Specification Version is greater than or equal the provided test version + * Test whether the instance's Specification Version is greater than or equal to the provided test version * * @public * @param {string} testVersion A Specification Version to compare the instance's Specification Version to - * @returns {boolean} True if the instance's Specification Version is greater than or equal the provided version + * @returns {boolean} True if the instance's Specification Version is greater than or equal to the provided version */ gte(testVersion) { return handleSemverComparator(semver.gte, this.#semverVersion, testVersion); @@ -104,11 +104,11 @@ class SpecVersionComparator { } /** - * Test whether the instance's Specification Version is smaller than or equal the provided test version + * Test whether the instance's Specification Version is smaller than or equal to the provided test version * * @public * @param {string} testVersion A Specification Version to compare the instance's Specification Version to - * @returns {boolean} True if the instance's Specification Version is smaller than or equal the provided version + * @returns {boolean} True if the instance's Specification Version is smaller than or equal to the provided version */ lte(testVersion) { return handleSemverComparator(semver.lte, this.#semverVersion, testVersion); @@ -148,7 +148,7 @@ class SpecVersionComparator { } /** - * Returns the major-version of provided Specification Version + * Returns the major-version of the provided Specification Version * * @public * @param {string} specVersion Specification Version @@ -176,8 +176,8 @@ class SpecVersionComparator { * * @public * @param {string} specVersion Specification Version - * @param {string} range [Semver]{@link https://www.npmjs.com/package/semver}-style version range. - * For example 2.2 - 2.4 + * @param {string} range [Semver]{@link https://www.npmjs.com/package/semver}-style version range, + * for example 2.2 - 2.4 * @returns {boolean} True if the provided Specification Version falls into the provided range */ static satisfies(specVersion, range) { @@ -199,12 +199,12 @@ class SpecVersionComparator { } /** - * Test whether the provided Specification Version is greater than or equal the provided test version + * Test whether the provided Specification Version is greater than or equal to the provided test version * * @public * @param {string} specVersion Specification Version * @param {string} testVersion A Specification Version to compare the provided Specification Version to - * @returns {boolean} True if the provided Specification Version is greater than or equal the provided version + * @returns {boolean} True if the provided Specification Version is greater than or equal to the provided version */ static gte(specVersion, testVersion) { const comparator = new SpecVersionComparator(specVersion); @@ -225,12 +225,12 @@ class SpecVersionComparator { } /** - * Test whether the provided Specification Version is smaller than or equal the provided test version + * Test whether the provided Specification Version is smaller than or equal to the provided test version * * @public * @param {string} specVersion Specification Version * @param {string} testVersion A Specification Version to compare the provided Specification Version to - * @returns {boolean} True if the provided Specification Version is smaller than or equal the provided version + * @returns {boolean} True if the provided Specification Version is smaller than or equal to the provided version */ static lte(specVersion, testVersion) { const comparator = new SpecVersionComparator(specVersion); @@ -266,7 +266,7 @@ class SpecVersionComparator { function getUnsupportedSpecVersionMessage(specVersion) { return `Unsupported Specification Version ${specVersion} defined. Your UI5 CLI installation might be outdated. ` + - `For details see https://sap.github.io/ui5-tooling/pages/Configuration/#specification-versions`; + `For details, see https://sap.github.io/ui5-tooling/pages/Configuration/#specification-versions`; } function getSemverCompatibleVersion(specVersion) { diff --git a/test/lib/specifications/Specification.js b/test/lib/specifications/Specification.js index 904be2140..cddfb6af4 100644 --- a/test/lib/specifications/Specification.js +++ b/test/lib/specifications/Specification.js @@ -296,6 +296,6 @@ test("Invalid specVersion", async (t) => { await t.throwsAsync(Specification.create(t.context.basicProjectInput), { message: "Unsupported Specification Version 0.5 defined. Your UI5 CLI installation might be outdated. " + - "For details see https://sap.github.io/ui5-tooling/pages/Configuration/#specification-versions" + "For details, see https://sap.github.io/ui5-tooling/pages/Configuration/#specification-versions" }, "Threw with expected error message"); }); diff --git a/test/lib/specifications/utils/SpecVersionComparator.js b/test/lib/specifications/utils/SpecVersionComparator.js index 4cac7cc72..227e19e43 100644 --- a/test/lib/specifications/utils/SpecVersionComparator.js +++ b/test/lib/specifications/utils/SpecVersionComparator.js @@ -5,7 +5,7 @@ import {__localFunctions__} from "../../../../lib/specifications/utils/SpecVersi const unsupportedSpecVersionText = (specVersion) => `Unsupported Specification Version ${specVersion} defined. Your UI5 CLI installation might be outdated. ` + - `For details see https://sap.github.io/ui5-tooling/pages/Configuration/#specification-versions`; + `For details, see https://sap.github.io/ui5-tooling/pages/Configuration/#specification-versions`; test.beforeEach((t) => { t.context.sinon = sinonGlobal.createSandbox(); From 22f62de49ddc765b161742d9bc57ccff3e2eec7f Mon Sep 17 00:00:00 2001 From: Merlin Beutlberger Date: Fri, 25 Nov 2022 17:59:24 +0100 Subject: [PATCH 07/10] [INTERNAL] TaskRunner: Use SpecVersionComparator --- lib/build/TaskRunner.js | 5 ++-- test/lib/build/TaskRunner.js | 54 ++++++++++++++++++++++++++++-------- 2 files changed, 45 insertions(+), 14 deletions(-) diff --git a/lib/build/TaskRunner.js b/lib/build/TaskRunner.js index bf8da96e3..f2c0fcb9f 100644 --- a/lib/build/TaskRunner.js +++ b/lib/build/TaskRunner.js @@ -309,12 +309,13 @@ class TaskRunner { params.dependencies = dependencies; } - if (task.getSpecVersion() === "3.0") { + const specVersion = task.getSpecVersionComparator(); + if (specVersion.gte("3.0")) { params.options.taskName = newTaskName; params.log = logger.getGroupLogger(`builder:custom-task:${newTaskName}`); } - const taskUtilInterface = taskUtil.getInterface(task.getSpecVersionComparator()); + const taskUtilInterface = taskUtil.getInterface(specVersion); // Interface is undefined if specVersion does not support taskUtil if (taskUtilInterface) { params.taskUtil = taskUtilInterface; diff --git a/test/lib/build/TaskRunner.js b/test/lib/build/TaskRunner.js index 92ad52eb5..f14960afd 100644 --- a/test/lib/build/TaskRunner.js +++ b/test/lib/build/TaskRunner.js @@ -391,10 +391,14 @@ test("Custom tasks is unknown", async (t) => { test("Custom task is called correctly", async (t) => { const {sinon, graph, taskUtil, taskRepository, TaskRunner} = t.context; const taskStub = sinon.stub(); + const specVersionComparatorGteStub = sinon.stub().returns(false); + const mockSpecVersionComparator = { + gte: specVersionComparatorGteStub + }; graph.getExtension.returns({ getTask: () => taskStub, getSpecVersion: () => "2.6", - getSpecVersionComparator: () => "specVersionComparator 2.6" + getSpecVersionComparator: () => mockSpecVersionComparator }); t.context.taskUtil.getInterface.returns("taskUtil interface"); const project = getMockProject("module"); @@ -413,6 +417,9 @@ test("Custom task is called correctly", async (t) => { dependencies: "dependencies" }); + t.is(specVersionComparatorGteStub.callCount, 1, "SpecVersionComparator#gte got called once"); + t.is(specVersionComparatorGteStub.getCall(0).args[0], "3.0", + "SpecVersionComparator#gte got called with correct arguments"); t.is(taskStub.callCount, 1, "Task got called once"); t.is(taskStub.getCall(0).args.length, 1, "Task got called with one argument"); t.deepEqual(taskStub.getCall(0).args[0], { @@ -427,17 +434,21 @@ test("Custom task is called correctly", async (t) => { }, "Task got called with one argument"); t.is(taskUtil.getInterface.callCount, 1, "taskUtil#getInterface got called once"); - t.is(taskUtil.getInterface.getCall(0).args[0], "specVersionComparator 2.6", + t.is(taskUtil.getInterface.getCall(0).args[0], mockSpecVersionComparator, "taskUtil#getInterface got called with correct argument"); }); test("Custom task with legacy spec version", async (t) => { const {sinon, graph, taskUtil, taskRepository, TaskRunner} = t.context; const taskStub = sinon.stub(); + const specVersionComparatorGteStub = sinon.stub().returns(false); + const mockSpecVersionComparator = { + gte: specVersionComparatorGteStub + }; graph.getExtension.returns({ getTask: () => taskStub, getSpecVersion: () => "1.0", - getSpecVersionComparator: () => "specVersionComparator 1.0" + getSpecVersionComparator: () => mockSpecVersionComparator }); t.context.taskUtil.getInterface.returns(undefined); // simulating no taskUtil for old specVersion const project = getMockProject("module"); @@ -456,6 +467,9 @@ test("Custom task with legacy spec version", async (t) => { dependencies: "dependencies" }); + t.is(specVersionComparatorGteStub.callCount, 1, "SpecVersionComparator#gte got called once"); + t.is(specVersionComparatorGteStub.getCall(0).args[0], "3.0", + "SpecVersionComparator#gte got called with correct arguments"); t.is(taskStub.callCount, 1, "Task got called once"); t.is(taskStub.getCall(0).args.length, 1, "Task got called with one argument"); t.deepEqual(taskStub.getCall(0).args[0], { @@ -469,17 +483,21 @@ test("Custom task with legacy spec version", async (t) => { }, "Task got called with one argument"); t.is(taskUtil.getInterface.callCount, 1, "taskUtil#getInterface got called once"); - t.is(taskUtil.getInterface.getCall(0).args[0], "specVersionComparator 1.0", + t.is(taskUtil.getInterface.getCall(0).args[0], mockSpecVersionComparator, "taskUtil#getInterface got called with correct argument"); }); test("Custom task with specVersion 3.0", async (t) => { const {sinon, graph, taskUtil, taskRepository, TaskRunner} = t.context; const taskStub = sinon.stub(); + const specVersionComparatorGteStub = sinon.stub().returns(true); + const mockSpecVersionComparator = { + gte: specVersionComparatorGteStub + }; graph.getExtension.returns({ getTask: () => taskStub, getSpecVersion: () => "3.0", - getSpecVersionComparator: () => "specVersionComparator 3.0" + getSpecVersionComparator: () => mockSpecVersionComparator }); t.context.taskUtil.getInterface.returns(undefined); // simulating no taskUtil for old specVersion const project = getMockProject("module"); @@ -498,6 +516,9 @@ test("Custom task with specVersion 3.0", async (t) => { dependencies: "dependencies" }, "log"); + t.is(specVersionComparatorGteStub.callCount, 1, "SpecVersionComparator#gte got called once"); + t.is(specVersionComparatorGteStub.getCall(0).args[0], "3.0", + "SpecVersionComparator#gte got called with correct arguments"); t.is(taskStub.callCount, 1, "Task got called once"); t.is(taskStub.getCall(0).args.length, 1, "Task got called with one argument"); t.deepEqual(taskStub.getCall(0).args[0], { @@ -513,7 +534,7 @@ test("Custom task with specVersion 3.0", async (t) => { }, "Task got called with one argument"); t.is(taskUtil.getInterface.callCount, 1, "taskUtil#getInterface got called once"); - t.is(taskUtil.getInterface.getCall(0).args[0], "specVersionComparator 3.0", + t.is(taskUtil.getInterface.getCall(0).args[0], mockSpecVersionComparator, "taskUtil#getInterface got called with correct argument"); }); @@ -522,20 +543,29 @@ test("Multiple custom tasks with same name are called correctly", async (t) => { const taskStub1 = sinon.stub(); const taskStub2 = sinon.stub(); const taskStub3 = sinon.stub(); + const mockSpecVersionComparatorA = { + gte: () => false + }; + const mockSpecVersionComparatorB = { + gte: () => false + }; + const mockSpecVersionComparatorC = { + gte: () => true + }; graph.getExtension.onFirstCall().returns({ getTask: () => taskStub1, getSpecVersion: () => "2.5", - getSpecVersionComparator: () => "specVersionComparator 2.5" + getSpecVersionComparator: () => mockSpecVersionComparatorA }); graph.getExtension.onSecondCall().returns({ getTask: () => taskStub2, getSpecVersion: () => "2.6", - getSpecVersionComparator: () => "specVersionComparator 2.6" + getSpecVersionComparator: () => mockSpecVersionComparatorB }); graph.getExtension.onThirdCall().returns({ getTask: () => taskStub3, getSpecVersion: () => "3.0", - getSpecVersionComparator: () => "specVersionComparator 3.0" + getSpecVersionComparator: () => mockSpecVersionComparatorC }); const project = getMockProject("module"); project.getCustomTasks = () => [ @@ -605,11 +635,11 @@ test("Multiple custom tasks with same name are called correctly", async (t) => { }, "Task 3 got called with one argument"); t.is(taskUtil.getInterface.callCount, 3, "taskUtil#getInterface got called once"); - t.is(taskUtil.getInterface.getCall(0).args[0], "specVersionComparator 2.5", + t.is(taskUtil.getInterface.getCall(0).args[0], mockSpecVersionComparatorA, "taskUtil#getInterface got called with correct argument on first call"); - t.is(taskUtil.getInterface.getCall(1).args[0], "specVersionComparator 3.0", + t.is(taskUtil.getInterface.getCall(1).args[0], mockSpecVersionComparatorC, "taskUtil#getInterface got called with correct argument on second call"); - t.is(taskUtil.getInterface.getCall(2).args[0], "specVersionComparator 2.6", + t.is(taskUtil.getInterface.getCall(2).args[0], mockSpecVersionComparatorB, "taskUtil#getInterface got called with correct argument on third call"); }); From 7bd4c09835391cea5dc6170bd5ada3af20067d82 Mon Sep 17 00:00:00 2001 From: Merlin Beutlberger Date: Mon, 28 Nov 2022 09:23:12 +0100 Subject: [PATCH 08/10] [INTERNAL] package.json: Export SpecVersionComparator --- package.json | 1 + test/lib/package-exports.js | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 1fb5dab89..fc6420fa7 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ ], "type": "module", "exports": { + "./specifications/utils/SpecVersionComparator": "./lib/specifications/utils/SpecVersionComparator.js", "./ui5Framework/Openui5Resolver": "./lib/ui5Framework/Openui5Resolver.js", "./ui5Framework/Sapui5Resolver": "./lib/ui5Framework/Sapui5Resolver.js", "./validation/validator": "./lib/validation/validator.js", diff --git a/test/lib/package-exports.js b/test/lib/package-exports.js index 7d85d4e45..2437027fb 100644 --- a/test/lib/package-exports.js +++ b/test/lib/package-exports.js @@ -13,11 +13,12 @@ test("export of package.json", (t) => { // Check number of definied exports test("check number of exports", (t) => { const packageJson = require("@ui5/project/package.json"); - t.is(Object.keys(packageJson.exports).length, 8); + t.is(Object.keys(packageJson.exports).length, 9); }); // Public API contract (exported modules) [ + "specifications/utils/SpecVersionComparator", "ui5Framework/Openui5Resolver", "ui5Framework/Sapui5Resolver", "validation/validator", From 7630ad23f5cc20a6e08764ee5a82924e6d36858c Mon Sep 17 00:00:00 2001 From: Merlin Beutlberger Date: Mon, 28 Nov 2022 13:35:36 +0100 Subject: [PATCH 09/10] [INTERNAL] TaskUtil: Add missing resourceFactory test --- test/lib/build/helpers/TaskUtil.js | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/test/lib/build/helpers/TaskUtil.js b/test/lib/build/helpers/TaskUtil.js index 392aa3d0a..c8f7f5354 100644 --- a/test/lib/build/helpers/TaskUtil.js +++ b/test/lib/build/helpers/TaskUtil.js @@ -20,9 +20,7 @@ const STANDARD_TAGS = Object.freeze({ test("Instantiation", (t) => { const taskUtil = new TaskUtil({ - projectBuildContext: { - // STANDARD_TAGS: ["some tag", "some other tag", "Thursday"] - } + projectBuildContext: {} }); t.deepEqual(taskUtil.STANDARD_TAGS, STANDARD_TAGS, "Correct standard tags exposed"); @@ -188,6 +186,22 @@ test("getDependencies", (t) => { t.is(res, "Pony farm!", "Correct result"); }); +test("resourceFactory", (t) => { + const {resourceFactory} = new TaskUtil({ + projectBuildContext: {} + }); + t.is(typeof resourceFactory.createResource, "function", + "resourceFactory function createResource is available"); + t.is(typeof resourceFactory.createReaderCollectionPrioritized, "function", + "resourceFactory function createReaderCollectionPrioritized is available"); + t.is(typeof resourceFactory.createFilterReader, "function", + "resourceFactory function createFilterReader is available"); + t.is(typeof resourceFactory.createLinkReader, "function", + "resourceFactory function createLinkReader is available"); + t.is(typeof resourceFactory.createFlatReader, "function", + "resourceFactory function createFlatReader is available"); +}); + test("registerCleanupTask", (t) => { const registerCleanupTaskStub = sinon.stub(); const taskUtil = new TaskUtil({ From f1e97b7365c4d088bc3e4d9f0bcb4cfdf3f59dea Mon Sep 17 00:00:00 2001 From: Merlin Beutlberger Date: Mon, 28 Nov 2022 14:38:35 +0100 Subject: [PATCH 10/10] [INTERNAL] Rename SpecVersionComparator => SpecificationVersion Use it as the main representation of a specification version (instead of the raw string as before). This also helps motivate the use of the helper functions to compare the version. Which is the most common use of the specification version anyways. --- lib/build/TaskRunner.js | 2 +- lib/build/definitions/application.js | 2 +- lib/build/definitions/library.js | 2 +- lib/build/helpers/TaskUtil.js | 5 +- lib/build/helpers/createBuildManifest.js | 2 +- lib/specifications/Specification.js | 22 +- ...nComparator.js => SpecificationVersion.js} | 33 +-- package.json | 2 +- test/lib/build/ProjectBuilder.js | 1 - test/lib/build/TaskRunner.js | 84 +++--- test/lib/build/definitions/application.js | 21 +- test/lib/build/definitions/library.js | 21 +- test/lib/build/definitions/themeLibrary.js | 6 +- test/lib/build/helpers/TaskUtil.js | 22 +- test/lib/package-exports.js | 2 +- test/lib/specifications/Specification.js | 12 +- .../utils/SpecVersionComparator.js | 242 ------------------ .../utils/SpecificationVersion.js | 242 ++++++++++++++++++ 18 files changed, 374 insertions(+), 349 deletions(-) rename lib/specifications/{utils/SpecVersionComparator.js => SpecificationVersion.js} (91%) delete mode 100644 test/lib/specifications/utils/SpecVersionComparator.js create mode 100644 test/lib/specifications/utils/SpecificationVersion.js diff --git a/lib/build/TaskRunner.js b/lib/build/TaskRunner.js index f2c0fcb9f..3f0e4e7c4 100644 --- a/lib/build/TaskRunner.js +++ b/lib/build/TaskRunner.js @@ -309,7 +309,7 @@ class TaskRunner { params.dependencies = dependencies; } - const specVersion = task.getSpecVersionComparator(); + const specVersion = task.getSpecVersion(); if (specVersion.gte("3.0")) { params.options.taskName = newTaskName; params.log = logger.getGroupLogger(`builder:custom-task:${newTaskName}`); diff --git a/lib/build/definitions/application.js b/lib/build/definitions/application.js index 24416540f..5a21557fc 100644 --- a/lib/build/definitions/application.js +++ b/lib/build/definitions/application.js @@ -34,7 +34,7 @@ export default function({project, taskUtil, getTask}) { // Support rules should not be minified to have readable code in the Support Assistant const minificationPattern = ["/**/*.js", "!**/*.support.js"]; - if (["2.6"].includes(project.getSpecVersion())) { + if (project.getSpecVersion().gte("2.6")) { const minificationExcludes = project.getMinificationExcludes(); if (minificationExcludes.length) { enhancePatternWithExcludes(minificationPattern, minificationExcludes, "/resources/"); diff --git a/lib/build/definitions/library.js b/lib/build/definitions/library.js index 82de4da1a..0d86a2d6d 100644 --- a/lib/build/definitions/library.js +++ b/lib/build/definitions/library.js @@ -73,7 +73,7 @@ export default function({project, taskUtil, getTask}) { // Support rules should not be minified to have readable code in the Support Assistant const minificationPattern = ["/resources/**/*.js", "!**/*.support.js"]; - if (["2.6"].includes(project.getSpecVersion())) { + if (project.getSpecVersion().gte("2.6")) { const minificationExcludes = project.getMinificationExcludes(); if (minificationExcludes.length) { enhancePatternWithExcludes(minificationPattern, minificationExcludes, "/resources/"); diff --git a/lib/build/helpers/TaskUtil.js b/lib/build/helpers/TaskUtil.js index 378a6f8f2..d71689106 100644 --- a/lib/build/helpers/TaskUtil.js +++ b/lib/build/helpers/TaskUtil.js @@ -184,7 +184,6 @@ class TaskUtil { * * @public * @typedef {object} @ui5/project/build/helpers/TaskUtil~ProjectInterface - * @property {Function} getSpecVersion Get the project Specification Version * @property {Function} getType Get the project type * @property {Function} getName Get the project name * @property {Function} getVersion Get the project version @@ -269,7 +268,7 @@ class TaskUtil { * Get an interface to an instance of this class that only provides those functions * that are supported by the given custom task extension specification version. * - * @param {@ui5/project/specifications/utils/SpecVersionComparator} specVersion + * @param {@ui5/project/specifications/SpecificationVersion} specVersion * SpecVersionComparator instance of the custom task * @returns {object} An object with bound instance methods supported by the given specification version */ @@ -292,7 +291,7 @@ class TaskUtil { const project = this.getProject(projectName); const baseProjectInterface = {}; bindFunctions(project, baseProjectInterface, [ - "getSpecVersion", "getType", "getName", "getVersion", "getNamespace", + "getType", "getName", "getVersion", "getNamespace", "getRootReader", "getReader", "getCustomConfiguration", "isFrameworkProject" ]); return baseProjectInterface; diff --git a/lib/build/helpers/createBuildManifest.js b/lib/build/helpers/createBuildManifest.js index ebc0b3ead..c1f1d08e7 100644 --- a/lib/build/helpers/createBuildManifest.js +++ b/lib/build/helpers/createBuildManifest.js @@ -38,7 +38,7 @@ export default async function(project, buildConfig) { const metadata = { project: { - specVersion: project.getSpecVersion(), + specVersion: project.getSpecVersion().toString(), type, metadata: { name: projectName, diff --git a/lib/specifications/Specification.js b/lib/specifications/Specification.js index c86f03118..3c16e69bd 100644 --- a/lib/specifications/Specification.js +++ b/lib/specifications/Specification.js @@ -1,6 +1,6 @@ import logger from "@ui5/logger"; import {createReader} from "@ui5/fs/resourceFactory"; -import SpecVersionComparator from "./utils/SpecVersionComparator.js"; +import SpecificationVersion from "./SpecificationVersion.js"; /** * Abstract superclass for all projects and extensions @@ -51,7 +51,7 @@ class Specification { const config = JSON.parse(JSON.stringify(configuration)); const {validate} = await import("../validation/validator.js"); - if (SpecVersionComparator.major(config.specVersion) <= 1) { + if (SpecificationVersion.major(config.specVersion) <= 1) { const originalSpecVersion = config.specVersion; this._log.verbose(`Detected legacy Specification Version ${config.specVersion}, defined for ` + `${config.kind} ${config.metadata.name}. ` + @@ -94,7 +94,8 @@ class Specification { this._name = config.metadata.name; this._kind = config.kind; this._type = config.type; - this._specVersion = config.specVersion; + this._specVersionString = config.specVersion; + this._specVersion = new SpecificationVersion(this._specVersionString); this._config = config; return this; @@ -134,26 +135,15 @@ class Specification { } /** - * Get the Specification Version + * Returns an instance of a helper class representing a Specification Version * * @public - * @returns {string} Specification Version + * @returns {@ui5/project/specifications/SpecificationVersion} */ getSpecVersion() { return this._specVersion; } - /** - * Returns an instance of a helper class allowing for convenient comparison - * operations against this instance's Specification Version - * - * @public - * @returns {@ui5/project/specifications/utils/SpecVersionComparator} - */ - getSpecVersionComparator() { - return new SpecVersionComparator(this.getSpecVersion()); - } - /** * Get the specification's generic version, as typically defined in a package.json * diff --git a/lib/specifications/utils/SpecVersionComparator.js b/lib/specifications/SpecificationVersion.js similarity index 91% rename from lib/specifications/utils/SpecVersionComparator.js rename to lib/specifications/SpecificationVersion.js index 27841ff8b..ae48edee3 100644 --- a/lib/specifications/utils/SpecVersionComparator.js +++ b/lib/specifications/SpecificationVersion.js @@ -8,13 +8,14 @@ const SUPPORTED_VERSIONS = [ ]; /** - * Utility class for comparing Specification Versions + * Helper class representing a Specification Version. Featuring helper functions for easy comparison + * of versions. * * @public * @class - * @alias @ui5/project/specifications/utils/SpecVersionComparator + * @alias @ui5/project/specifications/utils/SpecificationVersion */ -class SpecVersionComparator { +class SpecificationVersion { #specVersion; #semverVersion; @@ -29,12 +30,12 @@ class SpecVersionComparator { } /** - * Returns the Specification Version that is used in all comparisons + * Returns the Specification Version * * @public * @returns {string} Specification Version */ - getSpecVersion() { + toString() { return this.#specVersion; } @@ -155,7 +156,7 @@ for example 2.2 - 2.4 * @returns {integer} Major version */ static major(specVersion) { - const comparator = new SpecVersionComparator(specVersion); + const comparator = new SpecificationVersion(specVersion); return comparator.major(); } @@ -167,7 +168,7 @@ for example 2.2 - 2.4 * @returns {integer} Minor version */ static minor(specVersion) { - const comparator = new SpecVersionComparator(specVersion); + const comparator = new SpecificationVersion(specVersion); return comparator.minor(); } @@ -181,7 +182,7 @@ for example 2.2 - 2.4 * @returns {boolean} True if the provided Specification Version falls into the provided range */ static satisfies(specVersion, range) { - const comparator = new SpecVersionComparator(specVersion); + const comparator = new SpecificationVersion(specVersion); return comparator.satisfies(range); } @@ -194,7 +195,7 @@ for example 2.2 - 2.4 * @returns {boolean} True if the provided Specification Version is greater than the provided version */ static gt(specVersion, testVersion) { - const comparator = new SpecVersionComparator(specVersion); + const comparator = new SpecificationVersion(specVersion); return comparator.gt(testVersion); } @@ -207,7 +208,7 @@ for example 2.2 - 2.4 * @returns {boolean} True if the provided Specification Version is greater than or equal to the provided version */ static gte(specVersion, testVersion) { - const comparator = new SpecVersionComparator(specVersion); + const comparator = new SpecificationVersion(specVersion); return comparator.gte(testVersion); } @@ -220,7 +221,7 @@ for example 2.2 - 2.4 * @returns {boolean} True if the provided Specification Version is smaller than the provided version */ static lt(specVersion, testVersion) { - const comparator = new SpecVersionComparator(specVersion); + const comparator = new SpecificationVersion(specVersion); return comparator.lt(testVersion); } @@ -233,7 +234,7 @@ for example 2.2 - 2.4 * @returns {boolean} True if the provided Specification Version is smaller than or equal to the provided version */ static lte(specVersion, testVersion) { - const comparator = new SpecVersionComparator(specVersion); + const comparator = new SpecificationVersion(specVersion); return comparator.lte(testVersion); } @@ -246,7 +247,7 @@ for example 2.2 - 2.4 * @returns {boolean} True if the provided Specification Version is equal to the provided version */ static eq(specVersion, testVersion) { - const comparator = new SpecVersionComparator(specVersion); + const comparator = new SpecificationVersion(specVersion); return comparator.eq(testVersion); } @@ -259,7 +260,7 @@ for example 2.2 - 2.4 * @returns {boolean} True if the provided Specification Version is not equal to the provided version */ static neq(specVersion, testVersion) { - const comparator = new SpecVersionComparator(specVersion); + const comparator = new SpecificationVersion(specVersion); return comparator.neq(testVersion); } } @@ -270,7 +271,7 @@ function getUnsupportedSpecVersionMessage(specVersion) { } function getSemverCompatibleVersion(specVersion) { - if (SpecVersionComparator.isSupportedSpecVersion(specVersion)) { + if (SpecificationVersion.isSupportedSpecVersion(specVersion)) { return specVersion + ".0"; } throw new Error(getUnsupportedSpecVersionMessage(specVersion)); @@ -285,7 +286,7 @@ function handleSemverComparator(comparator, baseVersion, testVersion) { throw new Error("Invalid spec version expectation given in comparator: " + testVersion); } -export default SpecVersionComparator; +export default SpecificationVersion; // Export local function for testing only export const __localFunctions__ = (process.env.NODE_ENV === "test") ? diff --git a/package.json b/package.json index fc6420fa7..454f6f0d8 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ ], "type": "module", "exports": { - "./specifications/utils/SpecVersionComparator": "./lib/specifications/utils/SpecVersionComparator.js", + "./specifications/SpecificationVersion": "./lib/specifications/SpecificationVersion.js", "./ui5Framework/Openui5Resolver": "./lib/ui5Framework/Openui5Resolver.js", "./ui5Framework/Sapui5Resolver": "./lib/ui5Framework/Sapui5Resolver.js", "./validation/validator": "./lib/validation/validator.js", diff --git a/test/lib/build/ProjectBuilder.js b/test/lib/build/ProjectBuilder.js index 8a0227f55..3bca28270 100644 --- a/test/lib/build/ProjectBuilder.js +++ b/test/lib/build/ProjectBuilder.js @@ -13,7 +13,6 @@ function getMockProject(type, id = "b") { getType: () => type, getCopyright: noop, getVersion: noop, - getSpecVersion: () => "0.1", getReader: () => "reader", getWorkspace: () => "workspace", }; diff --git a/test/lib/build/TaskRunner.js b/test/lib/build/TaskRunner.js index f14960afd..d7410cdd9 100644 --- a/test/lib/build/TaskRunner.js +++ b/test/lib/build/TaskRunner.js @@ -24,8 +24,12 @@ function getMockProject(type) { getPropertiesFileSourceEncoding: noop, getCopyright: noop, getVersion: noop, - getSpecVersion: () => "0.1", getMinificationExcludes: emptyarray, + getSpecVersion: () => { + return { + gte: () => false + }; + }, getComponentPreloadPaths: () => [ "project/b/**/Component.js" ], @@ -391,14 +395,14 @@ test("Custom tasks is unknown", async (t) => { test("Custom task is called correctly", async (t) => { const {sinon, graph, taskUtil, taskRepository, TaskRunner} = t.context; const taskStub = sinon.stub(); - const specVersionComparatorGteStub = sinon.stub().returns(false); - const mockSpecVersionComparator = { - gte: specVersionComparatorGteStub + const specVersionGteStub = sinon.stub().returns(false); + const mockSpecVersion = { + toString: () => "2.6", + gte: specVersionGteStub }; graph.getExtension.returns({ getTask: () => taskStub, - getSpecVersion: () => "2.6", - getSpecVersionComparator: () => mockSpecVersionComparator + getSpecVersion: () => mockSpecVersion }); t.context.taskUtil.getInterface.returns("taskUtil interface"); const project = getMockProject("module"); @@ -417,9 +421,9 @@ test("Custom task is called correctly", async (t) => { dependencies: "dependencies" }); - t.is(specVersionComparatorGteStub.callCount, 1, "SpecVersionComparator#gte got called once"); - t.is(specVersionComparatorGteStub.getCall(0).args[0], "3.0", - "SpecVersionComparator#gte got called with correct arguments"); + t.is(specVersionGteStub.callCount, 1, "SpecificationVersion#gte got called once"); + t.is(specVersionGteStub.getCall(0).args[0], "3.0", + "SpecificationVersion#gte got called with correct arguments"); t.is(taskStub.callCount, 1, "Task got called once"); t.is(taskStub.getCall(0).args.length, 1, "Task got called with one argument"); t.deepEqual(taskStub.getCall(0).args[0], { @@ -434,21 +438,21 @@ test("Custom task is called correctly", async (t) => { }, "Task got called with one argument"); t.is(taskUtil.getInterface.callCount, 1, "taskUtil#getInterface got called once"); - t.is(taskUtil.getInterface.getCall(0).args[0], mockSpecVersionComparator, + t.is(taskUtil.getInterface.getCall(0).args[0], mockSpecVersion, "taskUtil#getInterface got called with correct argument"); }); test("Custom task with legacy spec version", async (t) => { const {sinon, graph, taskUtil, taskRepository, TaskRunner} = t.context; const taskStub = sinon.stub(); - const specVersionComparatorGteStub = sinon.stub().returns(false); - const mockSpecVersionComparator = { - gte: specVersionComparatorGteStub + const specVersionGteStub = sinon.stub().returns(false); + const mockSpecVersion = { + toString: () => "1.0", + gte: specVersionGteStub }; graph.getExtension.returns({ getTask: () => taskStub, - getSpecVersion: () => "1.0", - getSpecVersionComparator: () => mockSpecVersionComparator + getSpecVersion: () => mockSpecVersion }); t.context.taskUtil.getInterface.returns(undefined); // simulating no taskUtil for old specVersion const project = getMockProject("module"); @@ -467,9 +471,9 @@ test("Custom task with legacy spec version", async (t) => { dependencies: "dependencies" }); - t.is(specVersionComparatorGteStub.callCount, 1, "SpecVersionComparator#gte got called once"); - t.is(specVersionComparatorGteStub.getCall(0).args[0], "3.0", - "SpecVersionComparator#gte got called with correct arguments"); + t.is(specVersionGteStub.callCount, 1, "SpecificationVersion#gte got called once"); + t.is(specVersionGteStub.getCall(0).args[0], "3.0", + "SpecificationVersion#gte got called with correct arguments"); t.is(taskStub.callCount, 1, "Task got called once"); t.is(taskStub.getCall(0).args.length, 1, "Task got called with one argument"); t.deepEqual(taskStub.getCall(0).args[0], { @@ -483,21 +487,21 @@ test("Custom task with legacy spec version", async (t) => { }, "Task got called with one argument"); t.is(taskUtil.getInterface.callCount, 1, "taskUtil#getInterface got called once"); - t.is(taskUtil.getInterface.getCall(0).args[0], mockSpecVersionComparator, + t.is(taskUtil.getInterface.getCall(0).args[0], mockSpecVersion, "taskUtil#getInterface got called with correct argument"); }); test("Custom task with specVersion 3.0", async (t) => { const {sinon, graph, taskUtil, taskRepository, TaskRunner} = t.context; const taskStub = sinon.stub(); - const specVersionComparatorGteStub = sinon.stub().returns(true); - const mockSpecVersionComparator = { - gte: specVersionComparatorGteStub + const specVersionGteStub = sinon.stub().returns(true); + const mockSpecVersion = { + toString: () => "3.0", + gte: specVersionGteStub }; graph.getExtension.returns({ getTask: () => taskStub, - getSpecVersion: () => "3.0", - getSpecVersionComparator: () => mockSpecVersionComparator + getSpecVersion: () => mockSpecVersion }); t.context.taskUtil.getInterface.returns(undefined); // simulating no taskUtil for old specVersion const project = getMockProject("module"); @@ -516,9 +520,9 @@ test("Custom task with specVersion 3.0", async (t) => { dependencies: "dependencies" }, "log"); - t.is(specVersionComparatorGteStub.callCount, 1, "SpecVersionComparator#gte got called once"); - t.is(specVersionComparatorGteStub.getCall(0).args[0], "3.0", - "SpecVersionComparator#gte got called with correct arguments"); + t.is(specVersionGteStub.callCount, 1, "SpecificationVersion#gte got called once"); + t.is(specVersionGteStub.getCall(0).args[0], "3.0", + "SpecificationVersion#gte got called with correct arguments"); t.is(taskStub.callCount, 1, "Task got called once"); t.is(taskStub.getCall(0).args.length, 1, "Task got called with one argument"); t.deepEqual(taskStub.getCall(0).args[0], { @@ -534,7 +538,7 @@ test("Custom task with specVersion 3.0", async (t) => { }, "Task got called with one argument"); t.is(taskUtil.getInterface.callCount, 1, "taskUtil#getInterface got called once"); - t.is(taskUtil.getInterface.getCall(0).args[0], mockSpecVersionComparator, + t.is(taskUtil.getInterface.getCall(0).args[0], mockSpecVersion, "taskUtil#getInterface got called with correct argument"); }); @@ -543,29 +547,29 @@ test("Multiple custom tasks with same name are called correctly", async (t) => { const taskStub1 = sinon.stub(); const taskStub2 = sinon.stub(); const taskStub3 = sinon.stub(); - const mockSpecVersionComparatorA = { + const mockSpecVersionA = { + toString: () => "2.5", gte: () => false }; - const mockSpecVersionComparatorB = { + const mockSpecVersionB = { + toString: () => "2.6", gte: () => false }; - const mockSpecVersionComparatorC = { + const mockSpecVersionC = { + toString: () => "3.0", gte: () => true }; graph.getExtension.onFirstCall().returns({ getTask: () => taskStub1, - getSpecVersion: () => "2.5", - getSpecVersionComparator: () => mockSpecVersionComparatorA + getSpecVersion: () => mockSpecVersionA }); graph.getExtension.onSecondCall().returns({ getTask: () => taskStub2, - getSpecVersion: () => "2.6", - getSpecVersionComparator: () => mockSpecVersionComparatorB + getSpecVersion: () => mockSpecVersionB }); graph.getExtension.onThirdCall().returns({ getTask: () => taskStub3, - getSpecVersion: () => "3.0", - getSpecVersionComparator: () => mockSpecVersionComparatorC + getSpecVersion: () => mockSpecVersionC }); const project = getMockProject("module"); project.getCustomTasks = () => [ @@ -635,11 +639,11 @@ test("Multiple custom tasks with same name are called correctly", async (t) => { }, "Task 3 got called with one argument"); t.is(taskUtil.getInterface.callCount, 3, "taskUtil#getInterface got called once"); - t.is(taskUtil.getInterface.getCall(0).args[0], mockSpecVersionComparatorA, + t.is(taskUtil.getInterface.getCall(0).args[0], mockSpecVersionA, "taskUtil#getInterface got called with correct argument on first call"); - t.is(taskUtil.getInterface.getCall(1).args[0], mockSpecVersionComparatorC, + t.is(taskUtil.getInterface.getCall(1).args[0], mockSpecVersionC, "taskUtil#getInterface got called with correct argument on second call"); - t.is(taskUtil.getInterface.getCall(2).args[0], mockSpecVersionComparatorB, + t.is(taskUtil.getInterface.getCall(2).args[0], mockSpecVersionB, "taskUtil#getInterface got called with correct argument on third call"); }); diff --git a/test/lib/build/definitions/application.js b/test/lib/build/definitions/application.js index 76240a617..4fe95a23a 100644 --- a/test/lib/build/definitions/application.js +++ b/test/lib/build/definitions/application.js @@ -14,7 +14,12 @@ function getMockProject() { getPropertiesFileSourceEncoding: () => "UTF-412", getCopyright: () => "copyright", getVersion: () => "version", - getSpecVersion: () => "2.6", + getSpecVersion: () => { + return { + toString: () => "2.6", + gte: () => true + }; + }, getMinificationExcludes: emptyarray, getComponentPreloadPaths: emptyarray, getComponentPreloadNamespaces: emptyarray, @@ -103,7 +108,12 @@ test("Standard build", (t) => { test("Standard build with legacy spec version", (t) => { const {project, taskUtil, getTask} = t.context; - project.getSpecVersion = () => "0.1"; + project.getSpecVersion = () => { + return { + toString: () => "0.1", + gte: () => false + }; + }; const generateBundleTaskStub = sinon.stub(); getTask.returns({ task: generateBundleTaskStub @@ -364,7 +374,12 @@ test("Minification excludes", (t) => { test("Minification excludes not applied for legacy specVersion", (t) => { const {project, taskUtil, getTask} = t.context; - project.getSpecVersion = () => "2.5"; + project.getSpecVersion = () => { + return { + toString: () => "2.5", + gte: () => false + }; + }; project.getMinificationExcludes = () => ["**.html"]; const tasks = application({ diff --git a/test/lib/build/definitions/library.js b/test/lib/build/definitions/library.js index 444266638..165c8103d 100644 --- a/test/lib/build/definitions/library.js +++ b/test/lib/build/definitions/library.js @@ -14,7 +14,12 @@ function getMockProject() { getPropertiesFileSourceEncoding: () => "UTF-412", getCopyright: () => "copyright", getVersion: () => "version", - getSpecVersion: () => "2.6", + getSpecVersion: () => { + return { + toString: () => "2.6", + gte: () => true + }; + }, getMinificationExcludes: emptyarray, getComponentPreloadPaths: emptyarray, getComponentPreloadNamespaces: emptyarray, @@ -151,7 +156,12 @@ test("Standard build", async (t) => { test("Standard build with legacy spec version", (t) => { const {project, taskUtil, getTask} = t.context; - project.getSpecVersion = () => "0.1"; + project.getSpecVersion = () => { + return { + toString: () => "0.1", + gte: () => false + }; + }; const tasks = library({ project, taskUtil, getTask @@ -432,7 +442,12 @@ test("Minification excludes", (t) => { test("Minification excludes not applied for legacy specVersion", (t) => { const {project, taskUtil, getTask} = t.context; - project.getSpecVersion = () => "2.5"; + project.getSpecVersion = () => { + return { + toString: () => "2.5", + gte: () => false + }; + }; project.getMinificationExcludes = () => ["**.html"]; const tasks = library({ diff --git a/test/lib/build/definitions/themeLibrary.js b/test/lib/build/definitions/themeLibrary.js index e7c04a425..a5f72c5a4 100644 --- a/test/lib/build/definitions/themeLibrary.js +++ b/test/lib/build/definitions/themeLibrary.js @@ -13,7 +13,11 @@ function getMockProject() { getType: () => "theme-library", getCopyright: () => "copyright", getVersion: () => "version", - getSpecVersion: () => "2.6", + getSpecVersion: () => { + return { + toString: () => "2.6" + }; + }, getMinificationExcludes: emptyarray, getComponentPreloadPaths: emptyarray, getComponentPreloadNamespaces: emptyarray, diff --git a/test/lib/build/helpers/TaskUtil.js b/test/lib/build/helpers/TaskUtil.js index c8f7f5354..d960db74b 100644 --- a/test/lib/build/helpers/TaskUtil.js +++ b/test/lib/build/helpers/TaskUtil.js @@ -1,14 +1,14 @@ import test from "ava"; import sinon from "sinon"; import TaskUtil from "../../../../lib/build/helpers/TaskUtil.js"; -import SpecVersionComparator from "../../../../lib/specifications/utils/SpecVersionComparator.js"; +import SpecificationVersion from "../../../../lib/specifications/SpecificationVersion.js"; test.afterEach.always((t) => { sinon.restore(); }); -function getSpecVersionComparator(specVersion) { - return new SpecVersionComparator(specVersion); +function getSpecificationVersion(specVersion) { + return new SpecificationVersion(specVersion); } const STANDARD_TAGS = Object.freeze({ @@ -221,7 +221,7 @@ test("getInterface: specVersion 1.0", (t) => { projectBuildContext: {} }); - const interfacedTaskUtil = taskUtil.getInterface(getSpecVersionComparator("1.0")); + const interfacedTaskUtil = taskUtil.getInterface(getSpecificationVersion("1.0")); t.is(interfacedTaskUtil, undefined, "no interface provided"); }); @@ -231,7 +231,7 @@ test("getInterface: specVersion 2.2", (t) => { projectBuildContext: {} }); - const interfacedTaskUtil = taskUtil.getInterface(getSpecVersionComparator("2.2")); + const interfacedTaskUtil = taskUtil.getInterface(getSpecificationVersion("2.2")); t.deepEqual(Object.keys(interfacedTaskUtil), [ "STANDARD_TAGS", @@ -255,7 +255,7 @@ test("getInterface: specVersion 2.3", (t) => { projectBuildContext: {} }); - const interfacedTaskUtil = taskUtil.getInterface(getSpecVersionComparator("2.3")); + const interfacedTaskUtil = taskUtil.getInterface(getSpecificationVersion("2.3")); t.deepEqual(Object.keys(interfacedTaskUtil), [ "STANDARD_TAGS", @@ -279,7 +279,7 @@ test("getInterface: specVersion 2.4", (t) => { projectBuildContext: {} }); - const interfacedTaskUtil = taskUtil.getInterface(getSpecVersionComparator("2.4")); + const interfacedTaskUtil = taskUtil.getInterface(getSpecificationVersion("2.4")); t.deepEqual(Object.keys(interfacedTaskUtil), [ "STANDARD_TAGS", @@ -303,7 +303,7 @@ test("getInterface: specVersion 2.5", (t) => { projectBuildContext: {} }); - const interfacedTaskUtil = taskUtil.getInterface(getSpecVersionComparator("2.5")); + const interfacedTaskUtil = taskUtil.getInterface(getSpecificationVersion("2.5")); t.deepEqual(Object.keys(interfacedTaskUtil), [ "STANDARD_TAGS", @@ -327,7 +327,7 @@ test("getInterface: specVersion 2.6", (t) => { projectBuildContext: {} }); - const interfacedTaskUtil = taskUtil.getInterface(getSpecVersionComparator("2.6")); + const interfacedTaskUtil = taskUtil.getInterface(getSpecificationVersion("2.6")); t.deepEqual(Object.keys(interfacedTaskUtil), [ "STANDARD_TAGS", @@ -369,7 +369,7 @@ test("getInterface: specVersion 3.0", (t) => { } }); - const interfacedTaskUtil = taskUtil.getInterface(getSpecVersionComparator("3.0")); + const interfacedTaskUtil = taskUtil.getInterface(getSpecificationVersion("3.0")); t.deepEqual(Object.keys(interfacedTaskUtil), [ "STANDARD_TAGS", @@ -394,7 +394,6 @@ test("getInterface: specVersion 3.0", (t) => { // getProject const interfacedProject = interfacedTaskUtil.getProject("pony"); t.deepEqual(Object.keys(interfacedProject), [ - "getSpecVersion", "getType", "getName", "getVersion", @@ -405,7 +404,6 @@ test("getInterface: specVersion 3.0", (t) => { "isFrameworkProject", ], "Correct methods are provided"); - t.is(interfacedProject.getSpecVersion(), "specVersion", "getSpecVersion function is bound correctly"); t.is(interfacedProject.getType(), "type", "getType function is bound correctly"); t.is(interfacedProject.getName(), "name", "getName function is bound correctly"); t.is(interfacedProject.getVersion(), "version", "getVersion function is bound correctly"); diff --git a/test/lib/package-exports.js b/test/lib/package-exports.js index 2437027fb..349f99de2 100644 --- a/test/lib/package-exports.js +++ b/test/lib/package-exports.js @@ -18,7 +18,7 @@ test("check number of exports", (t) => { // Public API contract (exported modules) [ - "specifications/utils/SpecVersionComparator", + "specifications/SpecificationVersion", "ui5Framework/Openui5Resolver", "ui5Framework/Sapui5Resolver", "validation/validator", diff --git a/test/lib/specifications/Specification.js b/test/lib/specifications/Specification.js index cddfb6af4..b292b9e76 100644 --- a/test/lib/specifications/Specification.js +++ b/test/lib/specifications/Specification.js @@ -48,8 +48,8 @@ test("Configurations", async (t) => { const project = await Specification.create(t.context.basicProjectInput); t.is(project.getKind(), "project", "Returned correct kind configuration"); t.is(project.getType(), "application", "Returned correct type configuration"); - t.is(project.getSpecVersion(), "2.3", "Returned correct specification version"); - t.is(project.getSpecVersionComparator().major(), 2, + t.is(project.getSpecVersion().toString(), "2.3", "Returned correct specification version"); + t.is(project.getSpecVersion().major(), 2, "SpecVersionComparator returned correct major version"); }); @@ -113,7 +113,7 @@ test("Migrate legacy project", async (t) => { t.context.basicProjectInput.configuration.specVersion = "1.0"; const project = await Specification.create(t.context.basicProjectInput); - t.is(project.getSpecVersion(), "2.6", "Project got migrated to latest specVersion"); + t.is(project.getSpecVersion().toString(), "2.6", "Project got migrated to latest specVersion"); }); test("Migrate legacy project unexpected configuration", async (t) => { @@ -147,7 +147,7 @@ test("Migrate legacy module: specVersion 1.0", async (t) => { } }); - t.is(project.getSpecVersion(), "2.6", "Project got migrated to latest specVersion"); + t.is(project.getSpecVersion().toString(), "2.6", "Project got migrated to latest specVersion"); }); test("Migrate legacy module: specVersion 0.1", async (t) => { @@ -168,7 +168,7 @@ test("Migrate legacy module: specVersion 0.1", async (t) => { } }); - t.is(project.getSpecVersion(), "2.6", "Project got migrated to latest specVersion"); + t.is(project.getSpecVersion().toString(), "2.6", "Project got migrated to latest specVersion"); }); test("Migrate legacy extension", async (t) => { @@ -195,7 +195,7 @@ test("Migrate legacy extension", async (t) => { } }); - t.is(project.getSpecVersion(), "2.6", "Project got migrated to latest specVersion"); + t.is(project.getSpecVersion().toString(), "2.6", "Project got migrated to latest specVersion"); }); [{ diff --git a/test/lib/specifications/utils/SpecVersionComparator.js b/test/lib/specifications/utils/SpecVersionComparator.js deleted file mode 100644 index 227e19e43..000000000 --- a/test/lib/specifications/utils/SpecVersionComparator.js +++ /dev/null @@ -1,242 +0,0 @@ -import test from "ava"; -import sinonGlobal from "sinon"; -import SpecVersionComparator from "../../../../lib/specifications/utils/SpecVersionComparator.js"; -import {__localFunctions__} from "../../../../lib/specifications/utils/SpecVersionComparator.js"; - -const unsupportedSpecVersionText = (specVersion) => - `Unsupported Specification Version ${specVersion} defined. Your UI5 CLI installation might be outdated. ` + - `For details, see https://sap.github.io/ui5-tooling/pages/Configuration/#specification-versions`; - -test.beforeEach((t) => { - t.context.sinon = sinonGlobal.createSandbox(); -}); - -test.afterEach.always((t) => { - t.context.sinon.restore(); -}); - -test.serial("Invalid specVersion", (t) => { - const {sinon} = t.context; - const isSupportedSpecVersionStub = - sinon.stub(SpecVersionComparator, "isSupportedSpecVersion").returns(false); - - t.throws(() => { - new SpecVersionComparator("2.5"); - }, { - message: unsupportedSpecVersionText("2.5") - }, "Threw with expected error message"); - - t.is(isSupportedSpecVersionStub.callCount, 1, "Static isSupportedSpecVersionStub has been called once"); - t.deepEqual(isSupportedSpecVersionStub.getCall(0).args, ["2.5"], - "Static isSupportedSpecVersionStub has been called with expected arguments"); -}); - -test("(instance) getSpecVersion", (t) => { - t.is(new SpecVersionComparator("0.1").getSpecVersion(), "0.1"); - t.is(new SpecVersionComparator("1.1").getSpecVersion(), "1.1"); -}); - -test("(instance) major", (t) => { - t.is(new SpecVersionComparator("0.1").major(), 0); - t.is(new SpecVersionComparator("1.1").major(), 1); - t.is(new SpecVersionComparator("2.1").major(), 2); - - t.is(t.throws(() => { - new SpecVersionComparator("0.2").major(); - }).message, unsupportedSpecVersionText("0.2")); -}); - -test("(instance) minor", (t) => { - t.is(new SpecVersionComparator("2.1").minor(), 1); - t.is(new SpecVersionComparator("2.2").minor(), 2); - t.is(new SpecVersionComparator("2.3").minor(), 3); - - t.is(t.throws(() => { - new SpecVersionComparator("1.2").minor(); - }).message, unsupportedSpecVersionText("1.2")); -}); - -test("(instance) satisfies", (t) => { - // range: 1.x - t.is(new SpecVersionComparator("1.0").satisfies("1.x"), true); - t.is(new SpecVersionComparator("1.1").satisfies("1.x"), true); - t.is(new SpecVersionComparator("2.0").satisfies("1.x"), false); - - // range: ^2.2 - t.is(new SpecVersionComparator("2.1").satisfies("^2.2"), false); - t.is(new SpecVersionComparator("2.2").satisfies("^2.2"), true); - t.is(new SpecVersionComparator("2.3").satisfies("^2.2"), true); - - // range: > 1.0 - t.is(new SpecVersionComparator("1.0").satisfies("> 1.0"), false); - t.is(new SpecVersionComparator("1.1").satisfies("> 1.0"), true); - t.is(new SpecVersionComparator("2.2").satisfies("> 1.0"), true); - - // range: 2.2 - 2.4 - t.is(new SpecVersionComparator("2.1").satisfies("2.2 - 2.4"), false); - t.is(new SpecVersionComparator("2.2").satisfies("2.2 - 2.4"), true); - t.is(new SpecVersionComparator("2.3").satisfies("2.2 - 2.4"), true); - t.is(new SpecVersionComparator("2.4").satisfies("2.2 - 2.4"), true); - t.is(new SpecVersionComparator("2.5").satisfies("2.2 - 2.4"), false); - - // range: 0.1 || 1.0 - 1.1 || ^2.5 - t.is(new SpecVersionComparator("0.1").satisfies("0.1 || 1.0 - 1.1 || ^2.5"), true); - t.is(new SpecVersionComparator("1.0").satisfies("0.1 || 1.0 - 1.1 || ^2.5"), true); - t.is(new SpecVersionComparator("1.1").satisfies("0.1 || 1.0 - 1.1 || ^2.5"), true); - t.is(new SpecVersionComparator("2.4").satisfies("0.1 || 1.0 - 1.1 || ^2.5"), false); - t.is(new SpecVersionComparator("2.5").satisfies("0.1 || 1.0 - 1.1 || ^2.5"), true); - t.is(new SpecVersionComparator("2.6").satisfies("0.1 || 1.0 - 1.1 || ^2.5"), true); - - // unsupported spec version - t.is(t.throws(() => { - new SpecVersionComparator("0.2").satisfies("1.x"); - }).message, unsupportedSpecVersionText("0.2")); -}); - -test("(instance) low level comparator", (t) => { - t.is(new SpecVersionComparator("2.1").gt("2.2"), false); - t.is(new SpecVersionComparator("2.2").gt("2.2"), false); - t.is(new SpecVersionComparator("2.3").gt("2.2"), true); - - t.is(new SpecVersionComparator("2.1").gte("2.2"), false); - t.is(new SpecVersionComparator("2.2").gte("2.2"), true); - t.is(new SpecVersionComparator("2.3").gte("2.2"), true); - - t.is(new SpecVersionComparator("2.1").lt("2.2"), true); - t.is(new SpecVersionComparator("2.2").lt("2.2"), false); - t.is(new SpecVersionComparator("2.3").lt("2.2"), false); - - t.is(new SpecVersionComparator("2.1").lte("2.2"), true); - t.is(new SpecVersionComparator("2.2").lte("2.2"), true); - t.is(new SpecVersionComparator("2.3").lte("2.2"), false); - - t.is(new SpecVersionComparator("2.0").eq("2.2"), false); - t.is(new SpecVersionComparator("2.2").eq("2.2"), true); - - t.is(new SpecVersionComparator("2.0").neq("2.2"), true); - t.is(new SpecVersionComparator("2.2").neq("2.2"), false); -}); - -test("(static) isSupportedSpecVersion", (t) => { - t.is(SpecVersionComparator.isSupportedSpecVersion("0.1"), true); - t.is(SpecVersionComparator.isSupportedSpecVersion("1.0"), true); - t.is(SpecVersionComparator.isSupportedSpecVersion("1.1"), true); - t.is(SpecVersionComparator.isSupportedSpecVersion("2.0"), true); - t.is(SpecVersionComparator.isSupportedSpecVersion("2.4"), true); - t.is(SpecVersionComparator.isSupportedSpecVersion("0.2"), false); - t.is(SpecVersionComparator.isSupportedSpecVersion("1.2"), false); - t.is(SpecVersionComparator.isSupportedSpecVersion(1.1), false); - t.is(SpecVersionComparator.isSupportedSpecVersion("foo"), false); - t.is(SpecVersionComparator.isSupportedSpecVersion(""), false); - t.is(SpecVersionComparator.isSupportedSpecVersion(), false); -}); - -test("(static) major", (t) => { - t.is(SpecVersionComparator.major("0.1"), 0); - t.is(SpecVersionComparator.major("1.1"), 1); - t.is(SpecVersionComparator.major("2.1"), 2); - - t.is(t.throws(() => { - SpecVersionComparator.major("0.2"); - }).message, unsupportedSpecVersionText("0.2")); -}); - -test("(static) minor", (t) => { - t.is(SpecVersionComparator.minor("2.1"), 1); - t.is(SpecVersionComparator.minor("2.2"), 2); - t.is(SpecVersionComparator.minor("2.3"), 3); - - t.is(t.throws(() => { - SpecVersionComparator.minor("1.2"); - }).message, unsupportedSpecVersionText("1.2")); -}); - -test("(static) satisfies", (t) => { - // range: 1.x - t.is(SpecVersionComparator.satisfies("1.0", "1.x"), true); - t.is(SpecVersionComparator.satisfies("1.1", "1.x"), true); - t.is(SpecVersionComparator.satisfies("2.0", "1.x"), false); - - // range: ^2.2 - t.is(SpecVersionComparator.satisfies("2.1", "^2.2"), false); - t.is(SpecVersionComparator.satisfies("2.2", "^2.2"), true); - t.is(SpecVersionComparator.satisfies("2.3", "^2.2"), true); - - // range: > 1.0 - t.is(SpecVersionComparator.satisfies("1.0", "> 1.0"), false); - t.is(SpecVersionComparator.satisfies("1.1", "> 1.0"), true); - t.is(SpecVersionComparator.satisfies("2.2", "> 1.0"), true); - - // range: 2.2 - 2.4 - t.is(SpecVersionComparator.satisfies("2.1", "2.2 - 2.4"), false); - t.is(SpecVersionComparator.satisfies("2.2", "2.2 - 2.4"), true); - t.is(SpecVersionComparator.satisfies("2.3", "2.2 - 2.4"), true); - t.is(SpecVersionComparator.satisfies("2.4", "2.2 - 2.4"), true); - t.is(SpecVersionComparator.satisfies("2.5", "2.2 - 2.4"), false); - - // range: 0.1 || 1.0 - 1.1 || ^2.5 - t.is(SpecVersionComparator.satisfies("0.1", "0.1 || 1.0 - 1.1 || ^2.5"), true); - t.is(SpecVersionComparator.satisfies("1.0", "0.1 || 1.0 - 1.1 || ^2.5"), true); - t.is(SpecVersionComparator.satisfies("1.1", "0.1 || 1.0 - 1.1 || ^2.5"), true); - t.is(SpecVersionComparator.satisfies("2.4", "0.1 || 1.0 - 1.1 || ^2.5"), false); - t.is(SpecVersionComparator.satisfies("2.5", "0.1 || 1.0 - 1.1 || ^2.5"), true); - t.is(SpecVersionComparator.satisfies("2.6", "0.1 || 1.0 - 1.1 || ^2.5"), true); - - // unsupported spec version - t.is(t.throws(() => { - SpecVersionComparator.satisfies("0.2", "1.x"); - }).message, unsupportedSpecVersionText("0.2")); -}); - -test("(static) low level comparator", (t) => { - t.is(SpecVersionComparator.gt("2.1", "2.2"), false); - t.is(SpecVersionComparator.gt("2.2", "2.2"), false); - t.is(SpecVersionComparator.gt("2.3", "2.2"), true); - - t.is(SpecVersionComparator.gte("2.1", "2.2"), false); - t.is(SpecVersionComparator.gte("2.2", "2.2"), true); - t.is(SpecVersionComparator.gte("2.3", "2.2"), true); - - t.is(SpecVersionComparator.lt("2.1", "2.2"), true); - t.is(SpecVersionComparator.lt("2.2", "2.2"), false); - t.is(SpecVersionComparator.lt("2.3", "2.2"), false); - - t.is(SpecVersionComparator.lte("2.1", "2.2"), true); - t.is(SpecVersionComparator.lte("2.2", "2.2"), true); - t.is(SpecVersionComparator.lte("2.3", "2.2"), false); - - t.is(SpecVersionComparator.eq("2.0", "2.2"), false); - t.is(SpecVersionComparator.eq("2.2", "2.2"), true); - - t.is(SpecVersionComparator.neq("2.0", "2.2"), true); - t.is(SpecVersionComparator.neq("2.2", "2.2"), false); -}); - -test("getSemverCompatibleVersion", (t) => { - t.is(__localFunctions__.getSemverCompatibleVersion("0.1"), "0.1.0"); - t.is(__localFunctions__.getSemverCompatibleVersion("1.1"), "1.1.0"); - t.is(__localFunctions__.getSemverCompatibleVersion("2.0"), "2.0.0"); - - t.is(t.throws(() => { - __localFunctions__.getSemverCompatibleVersion("1.2.3"); - }).message, unsupportedSpecVersionText("1.2.3")); - t.is(t.throws(() => { - __localFunctions__.getSemverCompatibleVersion("0.99"); - }).message, unsupportedSpecVersionText("0.99")); - t.is(t.throws(() => { - __localFunctions__.getSemverCompatibleVersion("foo"); - }).message, unsupportedSpecVersionText("foo")); - t.is(t.throws(() => { - __localFunctions__.getSemverCompatibleVersion(); - }).message, unsupportedSpecVersionText("undefined")); -}); - -test("handleSemverComparator", (t) => { - const comparatorStub = t.context.sinon.stub().returns("foobar"); - t.is(__localFunctions__.handleSemverComparator(comparatorStub, "1.1.0", "2.2"), "foobar"); - t.deepEqual(comparatorStub.getCall(0).args, ["1.1.0", "2.2.0"]); - - t.is(t.throws(() => { - __localFunctions__.handleSemverComparator(undefined, undefined, "a.b"); - }).message, "Invalid spec version expectation given in comparator: a.b"); -}); diff --git a/test/lib/specifications/utils/SpecificationVersion.js b/test/lib/specifications/utils/SpecificationVersion.js new file mode 100644 index 000000000..8094f75cb --- /dev/null +++ b/test/lib/specifications/utils/SpecificationVersion.js @@ -0,0 +1,242 @@ +import test from "ava"; +import sinonGlobal from "sinon"; +import SpecificationVersion from "../../../../lib/specifications/SpecificationVersion.js"; +import {__localFunctions__} from "../../../../lib/specifications/SpecificationVersion.js"; + +const unsupportedSpecVersionText = (specVersion) => + `Unsupported Specification Version ${specVersion} defined. Your UI5 CLI installation might be outdated. ` + + `For details, see https://sap.github.io/ui5-tooling/pages/Configuration/#specification-versions`; + +test.beforeEach((t) => { + t.context.sinon = sinonGlobal.createSandbox(); +}); + +test.afterEach.always((t) => { + t.context.sinon.restore(); +}); + +test.serial("Invalid specVersion", (t) => { + const {sinon} = t.context; + const isSupportedSpecVersionStub = + sinon.stub(SpecificationVersion, "isSupportedSpecVersion").returns(false); + + t.throws(() => { + new SpecificationVersion("2.5"); + }, { + message: unsupportedSpecVersionText("2.5") + }, "Threw with expected error message"); + + t.is(isSupportedSpecVersionStub.callCount, 1, "Static isSupportedSpecVersionStub has been called once"); + t.deepEqual(isSupportedSpecVersionStub.getCall(0).args, ["2.5"], + "Static isSupportedSpecVersionStub has been called with expected arguments"); +}); + +test("(instance) toString", (t) => { + t.is(new SpecificationVersion("0.1").toString(), "0.1"); + t.is(new SpecificationVersion("1.1").toString(), "1.1"); +}); + +test("(instance) major", (t) => { + t.is(new SpecificationVersion("0.1").major(), 0); + t.is(new SpecificationVersion("1.1").major(), 1); + t.is(new SpecificationVersion("2.1").major(), 2); + + t.is(t.throws(() => { + new SpecificationVersion("0.2").major(); + }).message, unsupportedSpecVersionText("0.2")); +}); + +test("(instance) minor", (t) => { + t.is(new SpecificationVersion("2.1").minor(), 1); + t.is(new SpecificationVersion("2.2").minor(), 2); + t.is(new SpecificationVersion("2.3").minor(), 3); + + t.is(t.throws(() => { + new SpecificationVersion("1.2").minor(); + }).message, unsupportedSpecVersionText("1.2")); +}); + +test("(instance) satisfies", (t) => { + // range: 1.x + t.is(new SpecificationVersion("1.0").satisfies("1.x"), true); + t.is(new SpecificationVersion("1.1").satisfies("1.x"), true); + t.is(new SpecificationVersion("2.0").satisfies("1.x"), false); + + // range: ^2.2 + t.is(new SpecificationVersion("2.1").satisfies("^2.2"), false); + t.is(new SpecificationVersion("2.2").satisfies("^2.2"), true); + t.is(new SpecificationVersion("2.3").satisfies("^2.2"), true); + + // range: > 1.0 + t.is(new SpecificationVersion("1.0").satisfies("> 1.0"), false); + t.is(new SpecificationVersion("1.1").satisfies("> 1.0"), true); + t.is(new SpecificationVersion("2.2").satisfies("> 1.0"), true); + + // range: 2.2 - 2.4 + t.is(new SpecificationVersion("2.1").satisfies("2.2 - 2.4"), false); + t.is(new SpecificationVersion("2.2").satisfies("2.2 - 2.4"), true); + t.is(new SpecificationVersion("2.3").satisfies("2.2 - 2.4"), true); + t.is(new SpecificationVersion("2.4").satisfies("2.2 - 2.4"), true); + t.is(new SpecificationVersion("2.5").satisfies("2.2 - 2.4"), false); + + // range: 0.1 || 1.0 - 1.1 || ^2.5 + t.is(new SpecificationVersion("0.1").satisfies("0.1 || 1.0 - 1.1 || ^2.5"), true); + t.is(new SpecificationVersion("1.0").satisfies("0.1 || 1.0 - 1.1 || ^2.5"), true); + t.is(new SpecificationVersion("1.1").satisfies("0.1 || 1.0 - 1.1 || ^2.5"), true); + t.is(new SpecificationVersion("2.4").satisfies("0.1 || 1.0 - 1.1 || ^2.5"), false); + t.is(new SpecificationVersion("2.5").satisfies("0.1 || 1.0 - 1.1 || ^2.5"), true); + t.is(new SpecificationVersion("2.6").satisfies("0.1 || 1.0 - 1.1 || ^2.5"), true); + + // unsupported spec version + t.is(t.throws(() => { + new SpecificationVersion("0.2").satisfies("1.x"); + }).message, unsupportedSpecVersionText("0.2")); +}); + +test("(instance) low level comparator", (t) => { + t.is(new SpecificationVersion("2.1").gt("2.2"), false); + t.is(new SpecificationVersion("2.2").gt("2.2"), false); + t.is(new SpecificationVersion("2.3").gt("2.2"), true); + + t.is(new SpecificationVersion("2.1").gte("2.2"), false); + t.is(new SpecificationVersion("2.2").gte("2.2"), true); + t.is(new SpecificationVersion("2.3").gte("2.2"), true); + + t.is(new SpecificationVersion("2.1").lt("2.2"), true); + t.is(new SpecificationVersion("2.2").lt("2.2"), false); + t.is(new SpecificationVersion("2.3").lt("2.2"), false); + + t.is(new SpecificationVersion("2.1").lte("2.2"), true); + t.is(new SpecificationVersion("2.2").lte("2.2"), true); + t.is(new SpecificationVersion("2.3").lte("2.2"), false); + + t.is(new SpecificationVersion("2.0").eq("2.2"), false); + t.is(new SpecificationVersion("2.2").eq("2.2"), true); + + t.is(new SpecificationVersion("2.0").neq("2.2"), true); + t.is(new SpecificationVersion("2.2").neq("2.2"), false); +}); + +test("(static) isSupportedSpecVersion", (t) => { + t.is(SpecificationVersion.isSupportedSpecVersion("0.1"), true); + t.is(SpecificationVersion.isSupportedSpecVersion("1.0"), true); + t.is(SpecificationVersion.isSupportedSpecVersion("1.1"), true); + t.is(SpecificationVersion.isSupportedSpecVersion("2.0"), true); + t.is(SpecificationVersion.isSupportedSpecVersion("2.4"), true); + t.is(SpecificationVersion.isSupportedSpecVersion("0.2"), false); + t.is(SpecificationVersion.isSupportedSpecVersion("1.2"), false); + t.is(SpecificationVersion.isSupportedSpecVersion(1.1), false); + t.is(SpecificationVersion.isSupportedSpecVersion("foo"), false); + t.is(SpecificationVersion.isSupportedSpecVersion(""), false); + t.is(SpecificationVersion.isSupportedSpecVersion(), false); +}); + +test("(static) major", (t) => { + t.is(SpecificationVersion.major("0.1"), 0); + t.is(SpecificationVersion.major("1.1"), 1); + t.is(SpecificationVersion.major("2.1"), 2); + + t.is(t.throws(() => { + SpecificationVersion.major("0.2"); + }).message, unsupportedSpecVersionText("0.2")); +}); + +test("(static) minor", (t) => { + t.is(SpecificationVersion.minor("2.1"), 1); + t.is(SpecificationVersion.minor("2.2"), 2); + t.is(SpecificationVersion.minor("2.3"), 3); + + t.is(t.throws(() => { + SpecificationVersion.minor("1.2"); + }).message, unsupportedSpecVersionText("1.2")); +}); + +test("(static) satisfies", (t) => { + // range: 1.x + t.is(SpecificationVersion.satisfies("1.0", "1.x"), true); + t.is(SpecificationVersion.satisfies("1.1", "1.x"), true); + t.is(SpecificationVersion.satisfies("2.0", "1.x"), false); + + // range: ^2.2 + t.is(SpecificationVersion.satisfies("2.1", "^2.2"), false); + t.is(SpecificationVersion.satisfies("2.2", "^2.2"), true); + t.is(SpecificationVersion.satisfies("2.3", "^2.2"), true); + + // range: > 1.0 + t.is(SpecificationVersion.satisfies("1.0", "> 1.0"), false); + t.is(SpecificationVersion.satisfies("1.1", "> 1.0"), true); + t.is(SpecificationVersion.satisfies("2.2", "> 1.0"), true); + + // range: 2.2 - 2.4 + t.is(SpecificationVersion.satisfies("2.1", "2.2 - 2.4"), false); + t.is(SpecificationVersion.satisfies("2.2", "2.2 - 2.4"), true); + t.is(SpecificationVersion.satisfies("2.3", "2.2 - 2.4"), true); + t.is(SpecificationVersion.satisfies("2.4", "2.2 - 2.4"), true); + t.is(SpecificationVersion.satisfies("2.5", "2.2 - 2.4"), false); + + // range: 0.1 || 1.0 - 1.1 || ^2.5 + t.is(SpecificationVersion.satisfies("0.1", "0.1 || 1.0 - 1.1 || ^2.5"), true); + t.is(SpecificationVersion.satisfies("1.0", "0.1 || 1.0 - 1.1 || ^2.5"), true); + t.is(SpecificationVersion.satisfies("1.1", "0.1 || 1.0 - 1.1 || ^2.5"), true); + t.is(SpecificationVersion.satisfies("2.4", "0.1 || 1.0 - 1.1 || ^2.5"), false); + t.is(SpecificationVersion.satisfies("2.5", "0.1 || 1.0 - 1.1 || ^2.5"), true); + t.is(SpecificationVersion.satisfies("2.6", "0.1 || 1.0 - 1.1 || ^2.5"), true); + + // unsupported spec version + t.is(t.throws(() => { + SpecificationVersion.satisfies("0.2", "1.x"); + }).message, unsupportedSpecVersionText("0.2")); +}); + +test("(static) low level comparator", (t) => { + t.is(SpecificationVersion.gt("2.1", "2.2"), false); + t.is(SpecificationVersion.gt("2.2", "2.2"), false); + t.is(SpecificationVersion.gt("2.3", "2.2"), true); + + t.is(SpecificationVersion.gte("2.1", "2.2"), false); + t.is(SpecificationVersion.gte("2.2", "2.2"), true); + t.is(SpecificationVersion.gte("2.3", "2.2"), true); + + t.is(SpecificationVersion.lt("2.1", "2.2"), true); + t.is(SpecificationVersion.lt("2.2", "2.2"), false); + t.is(SpecificationVersion.lt("2.3", "2.2"), false); + + t.is(SpecificationVersion.lte("2.1", "2.2"), true); + t.is(SpecificationVersion.lte("2.2", "2.2"), true); + t.is(SpecificationVersion.lte("2.3", "2.2"), false); + + t.is(SpecificationVersion.eq("2.0", "2.2"), false); + t.is(SpecificationVersion.eq("2.2", "2.2"), true); + + t.is(SpecificationVersion.neq("2.0", "2.2"), true); + t.is(SpecificationVersion.neq("2.2", "2.2"), false); +}); + +test("getSemverCompatibleVersion", (t) => { + t.is(__localFunctions__.getSemverCompatibleVersion("0.1"), "0.1.0"); + t.is(__localFunctions__.getSemverCompatibleVersion("1.1"), "1.1.0"); + t.is(__localFunctions__.getSemverCompatibleVersion("2.0"), "2.0.0"); + + t.is(t.throws(() => { + __localFunctions__.getSemverCompatibleVersion("1.2.3"); + }).message, unsupportedSpecVersionText("1.2.3")); + t.is(t.throws(() => { + __localFunctions__.getSemverCompatibleVersion("0.99"); + }).message, unsupportedSpecVersionText("0.99")); + t.is(t.throws(() => { + __localFunctions__.getSemverCompatibleVersion("foo"); + }).message, unsupportedSpecVersionText("foo")); + t.is(t.throws(() => { + __localFunctions__.getSemverCompatibleVersion(); + }).message, unsupportedSpecVersionText("undefined")); +}); + +test("handleSemverComparator", (t) => { + const comparatorStub = t.context.sinon.stub().returns("foobar"); + t.is(__localFunctions__.handleSemverComparator(comparatorStub, "1.1.0", "2.2"), "foobar"); + t.deepEqual(comparatorStub.getCall(0).args, ["1.1.0", "2.2.0"]); + + t.is(t.throws(() => { + __localFunctions__.handleSemverComparator(undefined, undefined, "a.b"); + }).message, "Invalid spec version expectation given in comparator: a.b"); +});