Skip to content

Commit

Permalink
[FIX] ui5Framework: Allow providing exact prerelease versions (#326)
Browse files Browse the repository at this point in the history
- Prereleases can now be passed, but "latest" won't resolve to them.
- Add comments about RegExp patterns / test against schema pattern
- Adopt error handling / add tests
  • Loading branch information
matz3 authored May 29, 2020
1 parent 3017598 commit 6ce985c
Show file tree
Hide file tree
Showing 2 changed files with 138 additions and 11 deletions.
39 changes: 28 additions & 11 deletions lib/ui5Framework/AbstractResolver.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,18 @@ const path = require("path");
const log = require("@ui5/logger").getLogger("ui5Framework:AbstractResolver");
const semver = require("semver");

const versionRegExp = /^(0|[1-9]\d*)\.(0|[1-9]\d*)(?:\.(0|[1-9]\d*))?$/;
// Matches Semantic Versioning 2.0.0 versions
// https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string
//
// This needs to be aligned with the ui5.yaml JSON schema:
// lib/validation/schema/specVersion/2.0/kind/project.json#/definitions/framework/properties/version/pattern
//
// eslint-disable-next-line max-len
const SEMVER_VERSION_REGEXP = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/;

// Reduced Semantic Versioning pattern
// Matches MAJOR.MINOR as a simple version range to be resolved to the latest patch
const VERSION_RANGE_REGEXP = /^(0|[1-9]\d*)\.(0|[1-9]\d*)$/;

/**
* Abstract Resolver
Expand Down Expand Up @@ -163,23 +174,24 @@ class AbstractResolver {
let spec;
if (version === "latest") {
spec = "*";
} else if (versionRegExp.test(version)) {
} else if (VERSION_RANGE_REGEXP.test(version) || SEMVER_VERSION_REGEXP.test(version)) {
spec = version;
} else {
throw new Error(`Framework version specifier "${version}" is incorrect or not supported`);
}
const versions = await this.fetchAllVersions({ui5HomeDir, cwd});
const resolvedVersion = semver.maxSatisfying(versions, spec);
if (!resolvedVersion) {
if (this.name === "Sapui5Resolver" && semver.lt(spec, "1.76.0")) {
throw new Error(`Could not resolve framework version ${version}. ` +
`Note that SAPUI5 framework libraries can only be consumed by the UI5 Tooling ` +
`starting with SAPUI5 v1.76.0`);
} else
if (this.name === "Openui5Resolver" && semver.lt(spec, "1.52.5")) {
throw new Error(`Could not resolve framework version ${version}. ` +
`Note that OpenUI5 framework libraries can only be consumed by the UI5 Tooling ` +
`starting with OpenUI5 v1.52.5`);
if (semver.valid(spec)) {
if (this.name === "Sapui5Resolver" && semver.lt(spec, "1.76.0")) {
throw new Error(`Could not resolve framework version ${version}. ` +
`Note that SAPUI5 framework libraries can only be consumed by the UI5 Tooling ` +
`starting with SAPUI5 v1.76.0`);
} else if (this.name === "Openui5Resolver" && semver.lt(spec, "1.52.5")) {
throw new Error(`Could not resolve framework version ${version}. ` +
`Note that OpenUI5 framework libraries can only be consumed by the UI5 Tooling ` +
`starting with OpenUI5 v1.52.5`);
}
}
throw new Error(`Could not resolve framework version ${version}`);
}
Expand All @@ -198,4 +210,9 @@ class AbstractResolver {
}
}

if (process.env.NODE_ENV === "test") {
// Export pattern for testing to be checked against JSON schema pattern
AbstractResolver._SEMVER_VERSION_REGEXP = SEMVER_VERSION_REGEXP;
}

module.exports = AbstractResolver;
110 changes: 110 additions & 0 deletions test/lib/ui5framework/AbstractResolver.js
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,42 @@ test.serial("AbstractResolver: Static resolveVersion resolves 'MAJOR.MINOR.PATCH
}], "fetchAllVersions should be called with expected arguments");
});

test.serial("AbstractResolver: Static resolveVersion resolves 'MAJOR.MINOR.PATCH-prerelease'", async (t) => {
const fetchAllVersionsStub = sinon.stub(MyResolver, "fetchAllVersions")
.returns(["1.76.0", "1.77.0", "1.78.0", "1.79.0-SNAPSHOT"]);

const version = await MyResolver.resolveVersion("1.79.0-SNAPSHOT", {
cwd: "/cwd",
ui5HomeDir: "/ui5HomeDir"
});

t.is(version, "1.79.0-SNAPSHOT", "Resolved version should be correct");

t.is(fetchAllVersionsStub.callCount, 1, "fetchAllVersions should be called once");
t.deepEqual(fetchAllVersionsStub.getCall(0).args, [{
cwd: "/cwd",
ui5HomeDir: "/ui5HomeDir"
}], "fetchAllVersions should be called with expected arguments");
});

test.serial("AbstractResolver: Static resolveVersion does not include prereleases for 'latest' version", async (t) => {
const fetchAllVersionsStub = sinon.stub(MyResolver, "fetchAllVersions")
.returns(["1.76.0", "1.77.0", "1.78.0", "1.79.0-SNAPSHOT"]);

const version = await MyResolver.resolveVersion("latest", {
cwd: "/cwd",
ui5HomeDir: "/ui5HomeDir"
});

t.is(version, "1.78.0", "Resolved version should be correct");

t.is(fetchAllVersionsStub.callCount, 1, "fetchAllVersions should be called once");
t.deepEqual(fetchAllVersionsStub.getCall(0).args, [{
cwd: "/cwd",
ui5HomeDir: "/ui5HomeDir"
}], "fetchAllVersions should be called with expected arguments");
});

test.serial("AbstractResolver: Static resolveVersion without options", async (t) => {
const fetchAllVersionsStub = sinon.stub(MyResolver, "fetchAllVersions")
.returns(["1.75.0"]);
Expand Down Expand Up @@ -494,3 +530,77 @@ test.serial(
`Could not resolve framework version 1.75.0. Note that SAPUI5 framework libraries can only be ` +
`consumed by the UI5 Tooling starting with SAPUI5 v1.76.0`);
});

test.serial(
"AbstractResolver: Static resolveVersion throws error when latest OpenUI5 version cannot be found", async (t) => {
class Openui5Resolver extends AbstractResolver {
static async fetchAllVersions() {}
}

sinon.stub(Openui5Resolver, "fetchAllVersions")
.returns([]);

const error = await t.throwsAsync(Openui5Resolver.resolveVersion("latest", {
cwd: "/cwd",
ui5HomeDir: "/ui5HomeDir"
}));

t.is(error.message, `Could not resolve framework version latest`);
});

test.serial(
"AbstractResolver: Static resolveVersion throws error when latest SAPUI5 version cannot be found", async (t) => {
class Sapui5Resolver extends AbstractResolver {
static async fetchAllVersions() {}
}

sinon.stub(Sapui5Resolver, "fetchAllVersions")
.returns([]);

const error = await t.throwsAsync(Sapui5Resolver.resolveVersion("latest", {
cwd: "/cwd",
ui5HomeDir: "/ui5HomeDir"
}));

t.is(error.message, `Could not resolve framework version latest`);
});

test.serial(
"AbstractResolver: Static resolveVersion throws error when OpenUI5 version range cannot be resolved", async (t) => {
class Openui5Resolver extends AbstractResolver {
static async fetchAllVersions() {}
}

sinon.stub(Openui5Resolver, "fetchAllVersions")
.returns([]);

const error = await t.throwsAsync(Openui5Resolver.resolveVersion("1.99", {
cwd: "/cwd",
ui5HomeDir: "/ui5HomeDir"
}));

t.is(error.message, `Could not resolve framework version 1.99`);
});

test.serial(
"AbstractResolver: Static resolveVersion throws error when SAPUI5 version range cannot be resolved", async (t) => {
class Sapui5Resolver extends AbstractResolver {
static async fetchAllVersions() {}
}

sinon.stub(Sapui5Resolver, "fetchAllVersions")
.returns([]);

const error = await t.throwsAsync(Sapui5Resolver.resolveVersion("1.99", {
cwd: "/cwd",
ui5HomeDir: "/ui5HomeDir"
}));

t.is(error.message, `Could not resolve framework version 1.99`);
});

test.serial("AbstractResolver: SEMVER_VERSION_REGEXP should be aligned with JSON schema", async (t) => {
const projectSchema = require("../../../lib/validation/schema/specVersion/2.0/kind/project.json");
const schemaPattern = projectSchema.definitions.framework.properties.version.pattern;
t.is(schemaPattern, AbstractResolver._SEMVER_VERSION_REGEXP.source);
});

0 comments on commit 6ce985c

Please sign in to comment.