diff --git a/lib/build/TaskRunner.js b/lib/build/TaskRunner.js
index 605619a8d..3f0e4e7c4 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.getSpecVersion();
+ if (specVersion.gte("3.0")) {
params.options.taskName = newTaskName;
params.log = logger.getGroupLogger(`builder:custom-task:${newTaskName}`);
}
- const taskUtilInterface = taskUtil.getInterface(task.getSpecVersion());
+ const taskUtilInterface = taskUtil.getInterface(specVersion);
// Interface is undefined if specVersion does not support taskUtil
if (taskUtilInterface) {
params.taskUtil = taskUtilInterface;
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 1529accf1..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,11 +268,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/SpecificationVersion} 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,26 +284,17 @@ 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);
const baseProjectInterface = {};
bindFunctions(project, baseProjectInterface, [
- "getSpecVersion", "getType", "getName", "getVersion", "getNamespace",
+ "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 +310,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/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 227c9937d..3c16e69bd 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 SpecificationVersion from "./SpecificationVersion.js";
/**
* Abstract superclass for all projects and extensions
@@ -50,10 +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") {
+ if (SpecificationVersion.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);
@@ -69,20 +69,12 @@ 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, ` +
`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,
@@ -102,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;
@@ -142,10 +135,10 @@ 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;
diff --git a/lib/specifications/SpecificationVersion.js b/lib/specifications/SpecificationVersion.js
new file mode 100644
index 000000000..ae48edee3
--- /dev/null
+++ b/lib/specifications/SpecificationVersion.js
@@ -0,0 +1,293 @@
+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"
+];
+
+/**
+ * Helper class representing a Specification Version. Featuring helper functions for easy comparison
+ * of versions.
+ *
+ * @public
+ * @class
+ * @alias @ui5/project/specifications/utils/SpecificationVersion
+ */
+class SpecificationVersion {
+ #specVersion;
+ #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
+ */
+ constructor(specVersion) {
+ this.#specVersion = specVersion;
+ this.#semverVersion = getSemverCompatibleVersion(specVersion); // Throws for unsupported versions
+ }
+
+ /**
+ * Returns the Specification Version
+ *
+ * @public
+ * @returns {string} Specification Version
+ */
+ toString() {
+ return this.#specVersion;
+ }
+
+ /**
+ * Returns the major-version of the instance's Specification Version
+ *
+ * @public
+ * @returns {integer} Major version
+ */
+ major() {
+ return semver.major(this.#semverVersion);
+ }
+
+ /**
+ * Returns the minor-version of the instance's Specification Version
+ *
+ * @public
+ * @returns {integer} Minor version
+ */
+ minor() {
+ return semver.minor(this.#semverVersion);
+ }
+
+ /**
+ * 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
+ */
+ satisfies(range) {
+ return semver.satisfies(this.#semverVersion, range);
+ }
+
+ /**
+ * 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
+ */
+ gt(testVersion) {
+ return handleSemverComparator(semver.gt, this.#semverVersion, testVersion);
+ }
+
+ /**
+ * 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 to the provided version
+ */
+ gte(testVersion) {
+ return handleSemverComparator(semver.gte, this.#semverVersion, testVersion);
+ }
+
+ /**
+ * 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
+ */
+ lt(testVersion) {
+ return handleSemverComparator(semver.lt, this.#semverVersion, testVersion);
+ }
+
+ /**
+ * 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 to the provided version
+ */
+ lte(testVersion) {
+ return handleSemverComparator(semver.lte, this.#semverVersion, testVersion);
+ }
+
+ /**
+ * 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
+ */
+ eq(testVersion) {
+ return handleSemverComparator(semver.eq, this.#semverVersion, testVersion);
+ }
+
+ /**
+ * 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
+ */
+ neq(testVersion) {
+ return handleSemverComparator(semver.neq, this.#semverVersion, testVersion);
+ }
+
+ /**
+ * 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
+ */
+ static isSupportedSpecVersion(testVersion) {
+ return SUPPORTED_VERSIONS.includes(testVersion);
+ }
+
+ /**
+ * Returns the major-version of the provided Specification Version
+ *
+ * @public
+ * @param {string} specVersion Specification Version
+ * @returns {integer} Major version
+ */
+ static major(specVersion) {
+ const comparator = new SpecificationVersion(specVersion);
+ return comparator.major();
+ }
+
+ /**
+ * Returns the minor-version of the provided Specification Version
+ *
+ * @public
+ * @param {string} specVersion Specification Version
+ * @returns {integer} Minor version
+ */
+ static minor(specVersion) {
+ const comparator = new SpecificationVersion(specVersion);
+ return comparator.minor();
+ }
+
+ /**
+ * 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
+ * @returns {boolean} True if the provided Specification Version falls into the provided range
+ */
+ static satisfies(specVersion, range) {
+ const comparator = new SpecificationVersion(specVersion);
+ return comparator.satisfies(range);
+ }
+
+ /**
+ * 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
+ */
+ static gt(specVersion, testVersion) {
+ const comparator = new SpecificationVersion(specVersion);
+ return comparator.gt(testVersion);
+ }
+
+ /**
+ * 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 to the provided version
+ */
+ static gte(specVersion, testVersion) {
+ const comparator = new SpecificationVersion(specVersion);
+ return comparator.gte(testVersion);
+ }
+
+ /**
+ * 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
+ */
+ static lt(specVersion, testVersion) {
+ const comparator = new SpecificationVersion(specVersion);
+ return comparator.lt(testVersion);
+ }
+
+ /**
+ * 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 to the provided version
+ */
+ static lte(specVersion, testVersion) {
+ const comparator = new SpecificationVersion(specVersion);
+ return comparator.lte(testVersion);
+ }
+
+ /**
+ * 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
+ */
+ static eq(specVersion, testVersion) {
+ const comparator = new SpecificationVersion(specVersion);
+ return comparator.eq(testVersion);
+ }
+
+ /**
+ * 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
+ */
+ static neq(specVersion, testVersion) {
+ const comparator = new SpecificationVersion(specVersion);
+ return comparator.neq(testVersion);
+ }
+}
+
+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 (SpecificationVersion.isSupportedSpecVersion(specVersion)) {
+ return specVersion + ".0";
+ }
+ throw new Error(getUnsupportedSpecVersionMessage(specVersion));
+}
+
+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: " + testVersion);
+}
+
+export default SpecificationVersion;
+
+// Export local function for testing only
+export const __localFunctions__ = (process.env.NODE_ENV === "test") ?
+ {getSemverCompatibleVersion, handleSemverComparator} : /* istanbul ignore next */ undefined;
diff --git a/package.json b/package.json
index 1fb5dab89..454f6f0d8 100644
--- a/package.json
+++ b/package.json
@@ -18,6 +18,7 @@
],
"type": "module",
"exports": {
+ "./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 77f46f011..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,9 +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 specVersionGteStub = sinon.stub().returns(false);
+ const mockSpecVersion = {
+ toString: () => "2.6",
+ gte: specVersionGteStub
+ };
graph.getExtension.returns({
getTask: () => taskStub,
- getSpecVersion: () => "2.6"
+ getSpecVersion: () => mockSpecVersion
});
t.context.taskUtil.getInterface.returns("taskUtil interface");
const project = getMockProject("module");
@@ -412,6 +421,9 @@ test("Custom task is called correctly", async (t) => {
dependencies: "dependencies"
});
+ 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], {
@@ -426,16 +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], "2.6",
+ 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 specVersionGteStub = sinon.stub().returns(false);
+ const mockSpecVersion = {
+ toString: () => "1.0",
+ gte: specVersionGteStub
+ };
graph.getExtension.returns({
getTask: () => taskStub,
- getSpecVersion: () => "1.0"
+ getSpecVersion: () => mockSpecVersion
});
t.context.taskUtil.getInterface.returns(undefined); // simulating no taskUtil for old specVersion
const project = getMockProject("module");
@@ -454,6 +471,9 @@ test("Custom task with legacy spec version", async (t) => {
dependencies: "dependencies"
});
+ 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], {
@@ -467,16 +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], "1.0",
+ 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 specVersionGteStub = sinon.stub().returns(true);
+ const mockSpecVersion = {
+ toString: () => "3.0",
+ gte: specVersionGteStub
+ };
graph.getExtension.returns({
getTask: () => taskStub,
- getSpecVersion: () => "3.0"
+ getSpecVersion: () => mockSpecVersion
});
t.context.taskUtil.getInterface.returns(undefined); // simulating no taskUtil for old specVersion
const project = getMockProject("module");
@@ -495,6 +520,9 @@ test("Custom task with specVersion 3.0", async (t) => {
dependencies: "dependencies"
}, "log");
+ 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], {
@@ -510,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], "3.0",
+ t.is(taskUtil.getInterface.getCall(0).args[0], mockSpecVersion,
"taskUtil#getInterface got called with correct argument");
});
@@ -519,17 +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 mockSpecVersionA = {
+ toString: () => "2.5",
+ gte: () => false
+ };
+ const mockSpecVersionB = {
+ toString: () => "2.6",
+ gte: () => false
+ };
+ const mockSpecVersionC = {
+ toString: () => "3.0",
+ gte: () => true
+ };
graph.getExtension.onFirstCall().returns({
getTask: () => taskStub1,
- getSpecVersion: () => "2.5"
+ getSpecVersion: () => mockSpecVersionA
});
graph.getExtension.onSecondCall().returns({
getTask: () => taskStub2,
- getSpecVersion: () => "2.6"
+ getSpecVersion: () => mockSpecVersionB
});
graph.getExtension.onThirdCall().returns({
getTask: () => taskStub3,
- getSpecVersion: () => "3.0"
+ getSpecVersion: () => mockSpecVersionC
});
const project = getMockProject("module");
project.getCustomTasks = () => [
@@ -599,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], "2.5",
+ 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], "3.0",
+ 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], "2.6",
+ 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 bc2a57c5f..d960db74b 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 SpecificationVersion from "../../../../lib/specifications/SpecificationVersion.js";
test.afterEach.always((t) => {
sinon.restore();
});
+function getSpecificationVersion(specVersion) {
+ return new SpecificationVersion(specVersion);
+}
+
const STANDARD_TAGS = Object.freeze({
IsDebugVariant: "ui5:IsDebugVariant",
HasDebugVariant: "ui5:HasDebugVariant",
@@ -15,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");
@@ -183,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({
@@ -202,7 +221,7 @@ test("getInterface: specVersion 1.0", (t) => {
projectBuildContext: {}
});
- const interfacedTaskUtil = taskUtil.getInterface("1.0");
+ const interfacedTaskUtil = taskUtil.getInterface(getSpecificationVersion("1.0"));
t.is(interfacedTaskUtil, undefined, "no interface provided");
});
@@ -212,7 +231,7 @@ test("getInterface: specVersion 2.2", (t) => {
projectBuildContext: {}
});
- const interfacedTaskUtil = taskUtil.getInterface("2.2");
+ const interfacedTaskUtil = taskUtil.getInterface(getSpecificationVersion("2.2"));
t.deepEqual(Object.keys(interfacedTaskUtil), [
"STANDARD_TAGS",
@@ -236,7 +255,7 @@ test("getInterface: specVersion 2.3", (t) => {
projectBuildContext: {}
});
- const interfacedTaskUtil = taskUtil.getInterface("2.3");
+ const interfacedTaskUtil = taskUtil.getInterface(getSpecificationVersion("2.3"));
t.deepEqual(Object.keys(interfacedTaskUtil), [
"STANDARD_TAGS",
@@ -260,7 +279,7 @@ test("getInterface: specVersion 2.4", (t) => {
projectBuildContext: {}
});
- const interfacedTaskUtil = taskUtil.getInterface("2.4");
+ const interfacedTaskUtil = taskUtil.getInterface(getSpecificationVersion("2.4"));
t.deepEqual(Object.keys(interfacedTaskUtil), [
"STANDARD_TAGS",
@@ -284,7 +303,7 @@ test("getInterface: specVersion 2.5", (t) => {
projectBuildContext: {}
});
- const interfacedTaskUtil = taskUtil.getInterface("2.5");
+ const interfacedTaskUtil = taskUtil.getInterface(getSpecificationVersion("2.5"));
t.deepEqual(Object.keys(interfacedTaskUtil), [
"STANDARD_TAGS",
@@ -308,7 +327,7 @@ test("getInterface: specVersion 2.6", (t) => {
projectBuildContext: {}
});
- const interfacedTaskUtil = taskUtil.getInterface("2.6");
+ const interfacedTaskUtil = taskUtil.getInterface(getSpecificationVersion("2.6"));
t.deepEqual(Object.keys(interfacedTaskUtil), [
"STANDARD_TAGS",
@@ -350,7 +369,7 @@ test("getInterface: specVersion 3.0", (t) => {
}
});
- const interfacedTaskUtil = taskUtil.getInterface("3.0");
+ const interfacedTaskUtil = taskUtil.getInterface(getSpecificationVersion("3.0"));
t.deepEqual(Object.keys(interfacedTaskUtil), [
"STANDARD_TAGS",
@@ -375,7 +394,6 @@ test("getInterface: specVersion 3.0", (t) => {
// getProject
const interfacedProject = interfacedTaskUtil.getProject("pony");
t.deepEqual(Object.keys(interfacedProject), [
- "getSpecVersion",
"getType",
"getName",
"getVersion",
@@ -386,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");
@@ -415,28 +432,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/package-exports.js b/test/lib/package-exports.js
index 7d85d4e45..349f99de2 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/SpecificationVersion",
"ui5Framework/Openui5Resolver",
"ui5Framework/Sapui5Resolver",
"validation/validator",
diff --git a/test/lib/specifications/Specification.js b/test/lib/specifications/Specification.js
index 214f56f32..b292b9e76 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().toString(), "2.3", "Returned correct specification version");
+ t.is(project.getSpecVersion().major(), 2,
+ "SpecVersionComparator returned correct major version");
});
test("Access project root resources via reader", async (t) => {
@@ -110,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) => {
@@ -119,14 +122,14 @@ 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.",
"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",
@@ -144,7 +147,28 @@ test("Migrate legacy module", 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) => {
+ 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().toString(), "2.6", "Project got migrated to latest specVersion");
});
test("Migrate legacy extension", async (t) => {
@@ -171,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");
});
[{
@@ -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/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");
+});