278 lines
12 KiB
JavaScript
278 lines
12 KiB
JavaScript
import test from "ava";
|
|
import { union } from "lodash-es";
|
|
import semver from "semver";
|
|
import * as td from "testdouble";
|
|
|
|
const getBranch = (branches, branch) => branches.find(({ name }) => name === branch);
|
|
const release = (branches, name, version) => getBranch(branches, name).tags.push({ version });
|
|
const merge = (branches, source, target, tag) => {
|
|
getBranch(branches, target).tags = union(
|
|
getBranch(branches, source).tags.filter(({ version }) => !tag || semver.cmp(version, "<=", tag)),
|
|
getBranch(branches, target).tags
|
|
);
|
|
};
|
|
const remoteBranches = [];
|
|
const repositoryUrl = "repositoryUrl";
|
|
let expand, getTags, getBranches;
|
|
|
|
test.beforeEach(async (t) => {
|
|
getTags = (await td.replaceEsm("../../lib/branches/get-tags.js")).default;
|
|
expand = (await td.replaceEsm("../../lib/branches/expand.js")).default;
|
|
getBranches = (await import("../../lib/branches/index.js")).default;
|
|
});
|
|
|
|
test.afterEach.always((t) => {
|
|
td.reset();
|
|
});
|
|
|
|
test.serial("Enforce ranges with branching release workflow", async (t) => {
|
|
const branches = [
|
|
{ name: "1.x", tags: [] },
|
|
{ name: "1.0.x", tags: [] },
|
|
{ name: "master", tags: [] },
|
|
{ name: "next", tags: [] },
|
|
{ name: "next-major", tags: [] },
|
|
{ name: "beta", prerelease: true, tags: [] },
|
|
{ name: "alpha", prerelease: true, tags: [] },
|
|
];
|
|
const context = { options: { branches } };
|
|
td.when(expand(repositoryUrl, context, branches)).thenResolve(remoteBranches);
|
|
td.when(getTags(context, remoteBranches)).thenResolve(branches);
|
|
|
|
let result = (await getBranches(repositoryUrl, "master", context)).map(({ name, range }) => ({ name, range }));
|
|
t.is(getBranch(result, "1.0.x").range, ">=1.0.0 <1.0.0", "Cannot release on 1.0.x before a releasing on master");
|
|
t.is(getBranch(result, "1.x").range, ">=1.1.0 <1.0.0", "Cannot release on 1.x before a releasing on master");
|
|
t.is(getBranch(result, "master").range, ">=1.0.0");
|
|
t.is(getBranch(result, "next").range, ">=1.0.0");
|
|
t.is(getBranch(result, "next-major").range, ">=1.0.0");
|
|
|
|
release(branches, "master", "1.0.0");
|
|
result = (await getBranches("repositoryUrl", "master", context)).map(({ name, range }) => ({ name, range }));
|
|
t.is(getBranch(result, "1.0.x").range, ">=1.0.0 <1.0.0", "Cannot release on 1.0.x before a releasing on master");
|
|
t.is(getBranch(result, "1.x").range, ">=1.1.0 <1.0.0", "Cannot release on 1.x before a releasing on master");
|
|
t.is(getBranch(result, "master").range, ">=1.0.0");
|
|
t.is(getBranch(result, "next").range, ">=1.0.0");
|
|
t.is(getBranch(result, "next-major").range, ">=1.0.0");
|
|
|
|
release(branches, "master", "1.0.1");
|
|
result = (await getBranches("repositoryUrl", "master", { options: { branches } })).map(({ name, range }) => ({
|
|
name,
|
|
range,
|
|
}));
|
|
t.is(getBranch(result, "master").range, ">=1.0.1", "Can release only > than 1.0.1 on master");
|
|
t.is(getBranch(result, "next").range, ">=1.0.1", "Can release only > than 1.0.1 on next");
|
|
t.is(getBranch(result, "next-major").range, ">=1.0.1", "Can release only > than 1.0.1 on next-major");
|
|
|
|
merge(branches, "master", "next");
|
|
merge(branches, "master", "next-major");
|
|
result = (await getBranches("repositoryUrl", "master", { options: { branches } })).map(({ name, range }) => ({
|
|
name,
|
|
range,
|
|
}));
|
|
t.is(getBranch(result, "master").range, ">=1.0.1", "Can release only > than 1.0.1 on master");
|
|
t.is(getBranch(result, "next").range, ">=1.0.1", "Can release only > than 1.0.1 on next");
|
|
t.is(getBranch(result, "next-major").range, ">=1.0.1", "Can release only > than 1.0.1 on next-major");
|
|
|
|
release(branches, "next", "1.1.0");
|
|
release(branches, "next", "1.1.1");
|
|
result = (await getBranches("repositoryUrl", "master", { options: { branches } })).map(({ name, range }) => ({
|
|
name,
|
|
range,
|
|
}));
|
|
t.is(getBranch(result, "master").range, ">=1.0.1 <1.1.0", "Can release only patch, > than 1.0.1 on master");
|
|
t.is(getBranch(result, "next").range, ">=1.1.1", "Can release only > than 1.1.1 on next");
|
|
t.is(getBranch(result, "next-major").range, ">=1.1.1", "Can release > than 1.1.1 on next-major");
|
|
|
|
release(branches, "next-major", "2.0.0");
|
|
release(branches, "next-major", "2.0.1");
|
|
result = (await getBranches("repositoryUrl", "master", { options: { branches } })).map(({ name, range }) => ({
|
|
name,
|
|
range,
|
|
}));
|
|
t.is(getBranch(result, "master").range, ">=1.0.1 <1.1.0", "Can release only patch, > than 1.0.1 on master");
|
|
t.is(getBranch(result, "next").range, ">=1.1.1 <2.0.0", "Can release only patch or minor, > than 1.1.0 on next");
|
|
t.is(getBranch(result, "next-major").range, ">=2.0.1", "Can release any version, > than 2.0.1 on next-major");
|
|
|
|
merge(branches, "next-major", "beta");
|
|
release(branches, "beta", "3.0.0-beta.1");
|
|
merge(branches, "beta", "alpha");
|
|
release(branches, "alpha", "4.0.0-alpha.1");
|
|
result = (await getBranches("repositoryUrl", "master", { options: { branches } })).map(({ name, range }) => ({
|
|
name,
|
|
range,
|
|
}));
|
|
t.is(getBranch(result, "next-major").range, ">=2.0.1", "Can release any version, > than 2.0.1 on next-major");
|
|
|
|
merge(branches, "master", "1.0.x");
|
|
merge(branches, "master", "1.x");
|
|
result = (await getBranches("repositoryUrl", "master", { options: { branches } })).map(({ name, range }) => ({
|
|
name,
|
|
range,
|
|
}));
|
|
t.is(getBranch(result, "master").range, ">=1.0.1 <1.1.0", "Can release only patch, > than 1.0.1 on master");
|
|
t.is(
|
|
getBranch(result, "1.0.x").range,
|
|
">=1.0.1 <1.0.1",
|
|
"Cannot release on 1.0.x before >= 1.1.0 is released on master"
|
|
);
|
|
t.is(getBranch(result, "1.x").range, ">=1.1.0 <1.0.1", "Cannot release on 1.x before >= 1.2.0 is released on master");
|
|
|
|
release(branches, "master", "1.0.2");
|
|
release(branches, "master", "1.0.3");
|
|
release(branches, "master", "1.0.4");
|
|
result = (await getBranches("repositoryUrl", "master", { options: { branches } })).map(({ name, range }) => ({
|
|
name,
|
|
range,
|
|
}));
|
|
t.is(getBranch(result, "master").range, ">=1.0.4 <1.1.0", "Can release only patch, > than 1.0.4 on master");
|
|
t.is(
|
|
getBranch(result, "1.0.x").range,
|
|
">=1.0.1 <1.0.2",
|
|
"Cannot release on 1.0.x before >= 1.1.0 is released on master"
|
|
);
|
|
t.is(getBranch(result, "1.x").range, ">=1.1.0 <1.0.2", "Cannot release on 1.x before >= 1.2.0 is released on master");
|
|
|
|
merge(branches, "next", "master");
|
|
result = (await getBranches("repositoryUrl", "master", { options: { branches } })).map(({ name, range }) => ({
|
|
name,
|
|
range,
|
|
}));
|
|
|
|
t.is(getBranch(result, "master").range, ">=1.1.1", "Can release only > than 1.1.1 on master");
|
|
t.is(getBranch(result, "next").range, ">=1.1.1 <2.0.0", "Can release only patch or minor, > than 1.1.1 on next");
|
|
t.is(getBranch(result, "next-major").range, ">=2.0.1", "Can release any version, > than 2.0.1 on next-major");
|
|
t.is(
|
|
getBranch(result, "1.0.x").range,
|
|
">=1.0.1 <1.0.2",
|
|
"Cannot release on 1.0.x before 1.0.x version from master are merged"
|
|
);
|
|
t.is(getBranch(result, "1.x").range, ">=1.1.0 <1.0.2", "Cannot release on 1.x before >= 2.0.0 is released on master");
|
|
|
|
merge(branches, "master", "1.0.x", "1.0.4");
|
|
result = (await getBranches("repositoryUrl", "master", { options: { branches } })).map(({ name, range }) => ({
|
|
name,
|
|
range,
|
|
}));
|
|
t.is(getBranch(result, "master").range, ">=1.1.1", "Can release only > than 1.1.1 on master");
|
|
t.is(getBranch(result, "1.0.x").range, ">=1.0.4 <1.1.0", "Can release on 1.0.x only within range");
|
|
t.is(getBranch(result, "1.x").range, ">=1.1.0 <1.1.0", "Cannot release on 1.x before >= 2.0.0 is released on master");
|
|
|
|
merge(branches, "master", "1.x");
|
|
result = (await getBranches("repositoryUrl", "master", { options: { branches } })).map(({ name, range }) => ({
|
|
name,
|
|
range,
|
|
}));
|
|
t.is(getBranch(result, "master").range, ">=1.1.1", "Can release only > than 1.1.1 on master");
|
|
t.is(getBranch(result, "1.0.x").range, ">=1.0.4 <1.1.0", "Can release on 1.0.x only within range");
|
|
t.is(getBranch(result, "1.x").range, ">=1.1.1 <1.1.1", "Cannot release on 1.x before >= 2.0.0 is released on master");
|
|
|
|
merge(branches, "next-major", "next");
|
|
merge(branches, "next", "master");
|
|
result = (await getBranches("repositoryUrl", "master", { options: { branches } })).map(({ name, range }) => ({
|
|
name,
|
|
range,
|
|
}));
|
|
t.is(getBranch(result, "master").range, ">=2.0.1", "Can release only > than 2.0.1 on master");
|
|
t.is(getBranch(result, "next").range, ">=2.0.1", "Can release only > than 2.0.1 on next");
|
|
t.is(getBranch(result, "next-major").range, ">=2.0.1", "Can release only > than 2.0.1 on next-major");
|
|
t.is(getBranch(result, "1.x").range, ">=1.1.1 <2.0.0", "Can release on 1.x only within range");
|
|
|
|
merge(branches, "beta", "master");
|
|
release(branches, "master", "3.0.0");
|
|
result = (await getBranches("repositoryUrl", "master", { options: { branches } })).map(({ name, range }) => ({
|
|
name,
|
|
range,
|
|
}));
|
|
t.is(getBranch(result, "master").range, ">=3.0.0", "Can release only > than 3.0.0 on master");
|
|
t.is(getBranch(result, "next").range, ">=3.0.0", "Can release only > than 3.0.0 on next");
|
|
t.is(getBranch(result, "next-major").range, ">=3.0.0", "Can release only > than 3.0.0 on next-major");
|
|
|
|
branches.push({ name: "1.1.x", tags: [] });
|
|
merge(branches, "1.x", "1.1.x");
|
|
result = (await getBranches("repositoryUrl", "master", { options: { branches } })).map(({ name, range }) => ({
|
|
name,
|
|
range,
|
|
}));
|
|
t.is(getBranch(result, "1.0.x").range, ">=1.0.4 <1.1.0", "Can release on 1.0.x only within range");
|
|
t.is(getBranch(result, "1.1.x").range, ">=1.1.1 <1.2.0", "Can release on 1.1.x only within range");
|
|
t.is(getBranch(result, "1.x").range, ">=1.2.0 <2.0.0", "Can release on 1.x only within range");
|
|
});
|
|
|
|
test.serial("Throw SemanticReleaseError for invalid configurations", async (t) => {
|
|
const branches = [
|
|
{ name: "123", range: "123", tags: [] },
|
|
{ name: "1.x", tags: [] },
|
|
{ name: "maintenance-1", range: "1.x", tags: [] },
|
|
{ name: "1.x.x", tags: [] },
|
|
{ name: "beta", prerelease: "", tags: [] },
|
|
{ name: "alpha", prerelease: "alpha", tags: [] },
|
|
{ name: "preview", prerelease: "alpha", tags: [] },
|
|
];
|
|
const context = { options: { branches } };
|
|
td.when(expand(repositoryUrl, context, branches)).thenResolve(remoteBranches);
|
|
td.when(getTags(context, remoteBranches)).thenResolve(branches);
|
|
|
|
const error = await t.throwsAsync(getBranches(repositoryUrl, "master", context));
|
|
const errors = [...error.errors];
|
|
|
|
t.is(errors[0].name, "SemanticReleaseError");
|
|
t.is(errors[0].code, "EMAINTENANCEBRANCH");
|
|
t.truthy(errors[0].message);
|
|
t.truthy(errors[0].details);
|
|
t.is(errors[1].name, "SemanticReleaseError");
|
|
t.is(errors[1].code, "EMAINTENANCEBRANCHES");
|
|
t.truthy(errors[1].message);
|
|
t.truthy(errors[1].details);
|
|
t.is(errors[2].name, "SemanticReleaseError");
|
|
t.is(errors[2].code, "EPRERELEASEBRANCH");
|
|
t.truthy(errors[2].message);
|
|
t.truthy(errors[2].details);
|
|
t.is(errors[3].name, "SemanticReleaseError");
|
|
t.is(errors[3].code, "EPRERELEASEBRANCHES");
|
|
t.truthy(errors[3].message);
|
|
t.truthy(errors[3].details);
|
|
t.is(errors[4].name, "SemanticReleaseError");
|
|
t.is(errors[4].code, "ERELEASEBRANCHES");
|
|
t.truthy(errors[4].message);
|
|
t.truthy(errors[4].details);
|
|
});
|
|
|
|
test.serial("Throw a SemanticReleaseError if there is duplicate branches", async (t) => {
|
|
const branches = [
|
|
{ name: "master", tags: [] },
|
|
{ name: "master", tags: [] },
|
|
];
|
|
const context = { options: { branches } };
|
|
td.when(expand(repositoryUrl, context, branches)).thenResolve(remoteBranches);
|
|
td.when(getTags(context, remoteBranches)).thenResolve(branches);
|
|
|
|
const errors = [...(await t.throwsAsync(getBranches(repositoryUrl, "master", context))).errors];
|
|
|
|
t.is(errors[0].name, "SemanticReleaseError");
|
|
t.is(errors[0].code, "EDUPLICATEBRANCHES");
|
|
t.truthy(errors[0].message);
|
|
t.truthy(errors[0].details);
|
|
});
|
|
|
|
test.serial("Throw a SemanticReleaseError for each invalid branch name", async (t) => {
|
|
const branches = [
|
|
{ name: "~master", tags: [] },
|
|
{ name: "^master", tags: [] },
|
|
];
|
|
const context = { options: { branches } };
|
|
const remoteBranches = [];
|
|
td.when(expand(repositoryUrl, context, branches)).thenResolve(remoteBranches);
|
|
td.when(getTags(context, remoteBranches)).thenResolve(branches);
|
|
|
|
const errors = [...(await t.throwsAsync(getBranches(repositoryUrl, "master", context))).errors];
|
|
|
|
t.is(errors[0].name, "SemanticReleaseError");
|
|
t.is(errors[0].code, "EINVALIDBRANCHNAME");
|
|
t.truthy(errors[0].message);
|
|
t.truthy(errors[0].details);
|
|
t.is(errors[1].name, "SemanticReleaseError");
|
|
t.is(errors[1].code, "EINVALIDBRANCHNAME");
|
|
t.truthy(errors[1].message);
|
|
t.truthy(errors[1].details);
|
|
});
|