Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FIX] ui5Framework: Allow providing exact prerelease versions #326

Merged
merged 4 commits into from
May 29, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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*)$/;
Copy link
Member Author

@matz3 matz3 May 29, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was able to simplify this one as the full pattern also matches MAJOR.MINOR.PATCH


/**
* 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);
});