325 lines
11 KiB
JavaScript
325 lines
11 KiB
JavaScript
import path from "path";
|
|
import test from "ava";
|
|
import { copy, outputFile } from "fs-extra";
|
|
import { stub } from "sinon";
|
|
import { temporaryDirectory } from "tempy";
|
|
import getPlugins from "../../lib/plugins/index.js";
|
|
|
|
// Save the current working directory
|
|
const cwd = process.cwd();
|
|
|
|
test.beforeEach((t) => {
|
|
// Stub the logger functions
|
|
t.context.log = stub();
|
|
t.context.success = stub();
|
|
t.context.logger = { log: t.context.log, success: t.context.success, scope: () => t.context.logger };
|
|
});
|
|
|
|
test("Export default plugins", async (t) => {
|
|
const plugins = await getPlugins({ cwd, options: {}, logger: t.context.logger }, {});
|
|
|
|
// Verify the module returns a function for each plugin
|
|
t.is(typeof plugins.verifyConditions, "function");
|
|
t.is(typeof plugins.analyzeCommits, "function");
|
|
t.is(typeof plugins.verifyRelease, "function");
|
|
t.is(typeof plugins.generateNotes, "function");
|
|
t.is(typeof plugins.prepare, "function");
|
|
t.is(typeof plugins.publish, "function");
|
|
t.is(typeof plugins.success, "function");
|
|
t.is(typeof plugins.fail, "function");
|
|
});
|
|
|
|
test("Export plugins based on steps config", async (t) => {
|
|
const plugins = await getPlugins(
|
|
{
|
|
cwd,
|
|
logger: t.context.logger,
|
|
options: {
|
|
verifyConditions: ["./test/fixtures/plugin-noop.cjs", { path: "./test/fixtures/plugin-noop.cjs" }],
|
|
generateNotes: "./test/fixtures/plugin-noop.cjs",
|
|
analyzeCommits: { path: "./test/fixtures/plugin-noop.cjs" },
|
|
verifyRelease: () => {},
|
|
},
|
|
},
|
|
{}
|
|
);
|
|
|
|
// Verify the module returns a function for each plugin
|
|
t.is(typeof plugins.verifyConditions, "function");
|
|
t.is(typeof plugins.analyzeCommits, "function");
|
|
t.is(typeof plugins.verifyRelease, "function");
|
|
t.is(typeof plugins.generateNotes, "function");
|
|
t.is(typeof plugins.prepare, "function");
|
|
t.is(typeof plugins.publish, "function");
|
|
t.is(typeof plugins.success, "function");
|
|
t.is(typeof plugins.fail, "function");
|
|
});
|
|
|
|
test('Export plugins based on "plugins" config (array)', async (t) => {
|
|
const plugin1 = { verifyConditions: stub(), publish: stub() };
|
|
const plugin2 = { verifyConditions: stub(), verifyRelease: stub() };
|
|
const plugins = await getPlugins(
|
|
{ cwd, logger: t.context.logger, options: { plugins: [plugin1, [plugin2, {}]], verifyRelease: () => {} } },
|
|
{}
|
|
);
|
|
await plugins.verifyConditions({ options: {} });
|
|
t.true(plugin1.verifyConditions.calledOnce);
|
|
t.true(plugin2.verifyConditions.calledOnce);
|
|
|
|
await plugins.publish({ options: {} });
|
|
t.true(plugin1.publish.calledOnce);
|
|
|
|
await plugins.verifyRelease({ options: {} });
|
|
t.true(plugin2.verifyRelease.notCalled);
|
|
|
|
// Verify the module returns a function for each plugin
|
|
t.is(typeof plugins.verifyConditions, "function");
|
|
t.is(typeof plugins.analyzeCommits, "function");
|
|
t.is(typeof plugins.verifyRelease, "function");
|
|
t.is(typeof plugins.generateNotes, "function");
|
|
t.is(typeof plugins.prepare, "function");
|
|
t.is(typeof plugins.publish, "function");
|
|
t.is(typeof plugins.success, "function");
|
|
t.is(typeof plugins.fail, "function");
|
|
});
|
|
|
|
test('Export plugins based on "plugins" config (single definition)', async (t) => {
|
|
const plugin1 = { verifyConditions: stub(), publish: stub() };
|
|
const plugins = await getPlugins({ cwd, logger: t.context.logger, options: { plugins: plugin1 } }, {});
|
|
|
|
await plugins.verifyConditions({ options: {} });
|
|
t.true(plugin1.verifyConditions.calledOnce);
|
|
|
|
await plugins.publish({ options: {} });
|
|
t.true(plugin1.publish.calledOnce);
|
|
|
|
// Verify the module returns a function for each plugin
|
|
t.is(typeof plugins.verifyConditions, "function");
|
|
t.is(typeof plugins.analyzeCommits, "function");
|
|
t.is(typeof plugins.verifyRelease, "function");
|
|
t.is(typeof plugins.generateNotes, "function");
|
|
t.is(typeof plugins.prepare, "function");
|
|
t.is(typeof plugins.publish, "function");
|
|
t.is(typeof plugins.success, "function");
|
|
t.is(typeof plugins.fail, "function");
|
|
});
|
|
|
|
test('Merge global options, "plugins" options and step options', async (t) => {
|
|
const plugin1 = [{ verifyConditions: stub(), publish: stub() }, { pluginOpt1: "plugin1" }];
|
|
const plugin2 = [{ verifyConditions: stub() }, { pluginOpt2: "plugin2" }];
|
|
const plugin3 = [stub(), { pluginOpt3: "plugin3" }];
|
|
const plugins = await getPlugins(
|
|
{
|
|
cwd,
|
|
logger: t.context.logger,
|
|
options: { globalOpt: "global", plugins: [plugin1, plugin2], verifyRelease: [plugin3] },
|
|
},
|
|
{}
|
|
);
|
|
|
|
await plugins.verifyConditions({ options: {} });
|
|
t.deepEqual(plugin1[0].verifyConditions.args[0][0], { globalOpt: "global", pluginOpt1: "plugin1" });
|
|
t.deepEqual(plugin2[0].verifyConditions.args[0][0], { globalOpt: "global", pluginOpt2: "plugin2" });
|
|
|
|
await plugins.publish({ options: {} });
|
|
t.deepEqual(plugin1[0].publish.args[0][0], { globalOpt: "global", pluginOpt1: "plugin1" });
|
|
|
|
await plugins.verifyRelease({ options: {} });
|
|
t.deepEqual(plugin3[0].args[0][0], { globalOpt: "global", pluginOpt3: "plugin3" });
|
|
});
|
|
|
|
test('Unknown steps of plugins configured in "plugins" are ignored', async (t) => {
|
|
const plugin1 = { verifyConditions: () => {}, unknown: () => {} };
|
|
const plugins = await getPlugins({ cwd, logger: t.context.logger, options: { plugins: [plugin1] } }, {});
|
|
|
|
t.is(typeof plugins.verifyConditions, "function");
|
|
t.is(plugins.unknown, undefined);
|
|
});
|
|
|
|
test("Export plugins loaded from the dependency of a shareable config module", async (t) => {
|
|
const cwd = temporaryDirectory();
|
|
await copy(
|
|
"./test/fixtures/plugin-noop.cjs",
|
|
path.resolve(cwd, "node_modules/shareable-config/node_modules/custom-plugin/index.js")
|
|
);
|
|
await outputFile(path.resolve(cwd, "node_modules/shareable-config/index.js"), "");
|
|
|
|
const plugins = await getPlugins(
|
|
{
|
|
cwd,
|
|
logger: t.context.logger,
|
|
options: {
|
|
verifyConditions: ["custom-plugin", { path: "custom-plugin" }],
|
|
generateNotes: "custom-plugin",
|
|
analyzeCommits: { path: "custom-plugin" },
|
|
verifyRelease: () => {},
|
|
},
|
|
},
|
|
{ "custom-plugin": "shareable-config" }
|
|
);
|
|
|
|
// Verify the module returns a function for each plugin
|
|
t.is(typeof plugins.verifyConditions, "function");
|
|
t.is(typeof plugins.analyzeCommits, "function");
|
|
t.is(typeof plugins.verifyRelease, "function");
|
|
t.is(typeof plugins.generateNotes, "function");
|
|
t.is(typeof plugins.prepare, "function");
|
|
t.is(typeof plugins.publish, "function");
|
|
t.is(typeof plugins.success, "function");
|
|
t.is(typeof plugins.fail, "function");
|
|
});
|
|
|
|
test("Export plugins loaded from the dependency of a shareable config file", async (t) => {
|
|
const cwd = temporaryDirectory();
|
|
await copy("./test/fixtures/plugin-noop.cjs", path.resolve(cwd, "plugin/plugin-noop.cjs"));
|
|
await outputFile(path.resolve(cwd, "shareable-config.js"), "");
|
|
|
|
const plugins = await getPlugins(
|
|
{
|
|
cwd,
|
|
logger: t.context.logger,
|
|
options: {
|
|
verifyConditions: ["./plugin/plugin-noop.cjs", { path: "./plugin/plugin-noop.cjs" }],
|
|
generateNotes: "./plugin/plugin-noop.cjs",
|
|
analyzeCommits: { path: "./plugin/plugin-noop.cjs" },
|
|
verifyRelease: () => {},
|
|
},
|
|
},
|
|
{ "./plugin/plugin-noop": "./shareable-config.js" }
|
|
);
|
|
|
|
// Verify the module returns a function for each plugin
|
|
t.is(typeof plugins.verifyConditions, "function");
|
|
t.is(typeof plugins.analyzeCommits, "function");
|
|
t.is(typeof plugins.verifyRelease, "function");
|
|
t.is(typeof plugins.generateNotes, "function");
|
|
t.is(typeof plugins.prepare, "function");
|
|
t.is(typeof plugins.publish, "function");
|
|
t.is(typeof plugins.success, "function");
|
|
t.is(typeof plugins.fail, "function");
|
|
});
|
|
|
|
test("Use default when only options are passed for a single plugin", async (t) => {
|
|
const analyzeCommits = {};
|
|
const generateNotes = {};
|
|
const publish = {};
|
|
const success = () => {};
|
|
const fail = [() => {}];
|
|
|
|
const plugins = await getPlugins(
|
|
{
|
|
cwd,
|
|
logger: t.context.logger,
|
|
options: {
|
|
plugins: ["@semantic-release/commit-analyzer", "@semantic-release/release-notes-generator"],
|
|
analyzeCommits,
|
|
generateNotes,
|
|
publish,
|
|
success,
|
|
fail,
|
|
},
|
|
},
|
|
{}
|
|
);
|
|
|
|
// Verify the module returns a function for each plugin
|
|
t.is(typeof plugins.analyzeCommits, "function");
|
|
t.is(typeof plugins.generateNotes, "function");
|
|
t.is(typeof plugins.success, "function");
|
|
t.is(typeof plugins.fail, "function");
|
|
|
|
// Verify only the plugins defined as an object with no `path` are set to the default value
|
|
t.falsy(success.path);
|
|
t.falsy(fail.path);
|
|
});
|
|
|
|
test("Merge global options with plugin options", async (t) => {
|
|
const plugins = await getPlugins(
|
|
{
|
|
cwd,
|
|
logger: t.context.logger,
|
|
options: {
|
|
globalOpt: "global",
|
|
otherOpt: "globally-defined",
|
|
verifyRelease: { path: "./test/fixtures/plugin-result-config", localOpt: "local", otherOpt: "locally-defined" },
|
|
},
|
|
},
|
|
{}
|
|
);
|
|
|
|
const [result] = await plugins.verifyRelease({ options: {} });
|
|
|
|
t.deepEqual(result.pluginConfig, { localOpt: "local", globalOpt: "global", otherOpt: "locally-defined" });
|
|
});
|
|
|
|
test("Throw an error for each invalid plugin configuration", async (t) => {
|
|
const errors = [
|
|
...(
|
|
await t.throwsAsync(() =>
|
|
getPlugins(
|
|
{
|
|
cwd,
|
|
logger: t.context.logger,
|
|
options: {
|
|
plugins: ["@semantic-release/commit-analyzer", "@semantic-release/release-notes-generator"],
|
|
verifyConditions: 1,
|
|
analyzeCommits: [],
|
|
verifyRelease: [{}],
|
|
generateNotes: [{ path: null }],
|
|
},
|
|
},
|
|
{}
|
|
)
|
|
)
|
|
).errors,
|
|
];
|
|
|
|
t.is(errors[0].name, "SemanticReleaseError");
|
|
t.is(errors[0].code, "EPLUGINCONF");
|
|
t.is(errors[1].name, "SemanticReleaseError");
|
|
t.is(errors[1].code, "EPLUGINCONF");
|
|
t.is(errors[2].name, "SemanticReleaseError");
|
|
t.is(errors[2].code, "EPLUGINCONF");
|
|
t.is(errors[3].name, "SemanticReleaseError");
|
|
t.is(errors[3].code, "EPLUGINCONF");
|
|
});
|
|
|
|
test('Throw EPLUGINSCONF error if the "plugins" option contains an old plugin definition (returns a function)', async (t) => {
|
|
const errors = [
|
|
...(
|
|
await t.throwsAsync(() =>
|
|
getPlugins(
|
|
{
|
|
cwd,
|
|
logger: t.context.logger,
|
|
options: { plugins: ["./test/fixtures/multi-plugin.cjs", "./test/fixtures/plugin-noop.cjs", () => {}] },
|
|
},
|
|
{}
|
|
)
|
|
)
|
|
).errors,
|
|
];
|
|
|
|
t.is(errors[0].name, "SemanticReleaseError");
|
|
t.is(errors[0].code, "EPLUGINSCONF");
|
|
t.is(errors[1].name, "SemanticReleaseError");
|
|
t.is(errors[1].code, "EPLUGINSCONF");
|
|
});
|
|
|
|
test('Throw EPLUGINSCONF error for each invalid definition if the "plugins" option', async (t) => {
|
|
const errors = [
|
|
...(
|
|
await t.throwsAsync(() =>
|
|
getPlugins({ cwd, logger: t.context.logger, options: { plugins: [1, { path: 1 }, [() => {}, {}, {}]] } }, {})
|
|
)
|
|
).errors,
|
|
];
|
|
|
|
t.is(errors[0].name, "SemanticReleaseError");
|
|
t.is(errors[0].code, "EPLUGINSCONF");
|
|
t.is(errors[1].name, "SemanticReleaseError");
|
|
t.is(errors[1].code, "EPLUGINSCONF");
|
|
t.is(errors[2].name, "SemanticReleaseError");
|
|
t.is(errors[2].code, "EPLUGINSCONF");
|
|
});
|