320 lines
10 KiB
JavaScript
320 lines
10 KiB
JavaScript
import path from 'path';
|
|
import test from 'ava';
|
|
import {copy, outputFile} from 'fs-extra';
|
|
import {stub} from 'sinon';
|
|
import tempy from 'tempy';
|
|
import getPlugins from '../../lib/plugins';
|
|
|
|
// Save the current working diretory
|
|
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', t => {
|
|
const plugins = 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', t => {
|
|
const plugins = getPlugins(
|
|
{
|
|
cwd,
|
|
logger: t.context.logger,
|
|
options: {
|
|
verifyConditions: ['./test/fixtures/plugin-noop', {path: './test/fixtures/plugin-noop'}],
|
|
generateNotes: './test/fixtures/plugin-noop',
|
|
analyzeCommits: {path: './test/fixtures/plugin-noop'},
|
|
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 = getPlugins(
|
|
{cwd, logger: t.context.logger, options: {plugins: [plugin1, plugin2], verifyRelease: () => {}}},
|
|
{}
|
|
);
|
|
|
|
await plugins.verifyConditions({});
|
|
t.true(plugin1.verifyConditions.calledOnce);
|
|
t.true(plugin2.verifyConditions.calledOnce);
|
|
|
|
await plugins.publish({});
|
|
t.true(plugin1.publish.calledOnce);
|
|
|
|
await plugins.verifyRelease({});
|
|
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 = getPlugins({cwd, logger: t.context.logger, options: {plugins: plugin1}}, {});
|
|
|
|
await plugins.verifyConditions({});
|
|
t.true(plugin1.verifyConditions.calledOnce);
|
|
|
|
await plugins.publish({});
|
|
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 sptep options', async t => {
|
|
const plugin1 = [{verifyConditions: stub(), publish: stub()}, {pluginOpt1: 'plugin1'}];
|
|
const plugin2 = [{verifyConditions: stub()}, {pluginOpt2: 'plugin2'}];
|
|
const plugin3 = [stub(), {pluginOpt3: 'plugin3'}];
|
|
const plugins = getPlugins(
|
|
{
|
|
cwd,
|
|
logger: t.context.logger,
|
|
options: {globalOpt: 'global', plugins: [plugin1, plugin2], verifyRelease: [plugin3]},
|
|
},
|
|
{}
|
|
);
|
|
|
|
await plugins.verifyConditions({});
|
|
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({});
|
|
t.deepEqual(plugin1[0].publish.args[0][0], {globalOpt: 'global', pluginOpt1: 'plugin1'});
|
|
|
|
await plugins.verifyRelease({});
|
|
t.deepEqual(plugin3[0].args[0][0], {globalOpt: 'global', pluginOpt3: 'plugin3'});
|
|
});
|
|
|
|
test('Unknown steps of plugins configured in "plugins" are ignored', t => {
|
|
const plugin1 = {verifyConditions: () => {}, unknown: () => {}};
|
|
const plugins = 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 = tempy.directory();
|
|
await copy(
|
|
'./test/fixtures/plugin-noop.js',
|
|
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 = 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 = tempy.directory();
|
|
await copy('./test/fixtures/plugin-noop.js', path.resolve(cwd, 'plugin/plugin-noop.js'));
|
|
await outputFile(path.resolve(cwd, 'shareable-config.js'), '');
|
|
|
|
const plugins = getPlugins(
|
|
{
|
|
cwd,
|
|
logger: t.context.logger,
|
|
options: {
|
|
verifyConditions: ['./plugin/plugin-noop', {path: './plugin/plugin-noop'}],
|
|
generateNotes: './plugin/plugin-noop',
|
|
analyzeCommits: {path: './plugin/plugin-noop'},
|
|
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', t => {
|
|
const analyzeCommits = {};
|
|
const generateNotes = {};
|
|
const publish = {};
|
|
const success = () => {};
|
|
const fail = [() => {}];
|
|
|
|
const plugins = 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 = 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();
|
|
|
|
t.deepEqual(result.pluginConfig, {localOpt: 'local', globalOpt: 'global', otherOpt: 'locally-defined'});
|
|
});
|
|
|
|
test('Throw an error for each invalid plugin configuration', t => {
|
|
const errors = [
|
|
...t.throws(() =>
|
|
getPlugins(
|
|
{
|
|
cwd,
|
|
logger: t.context.logger,
|
|
options: {
|
|
plugins: ['@semantic-release/commit-analyzer', '@semantic-release/release-notes-generator'],
|
|
verifyConditions: 1,
|
|
analyzeCommits: [],
|
|
verifyRelease: [{}],
|
|
generateNotes: [{path: null}],
|
|
},
|
|
},
|
|
{}
|
|
)
|
|
),
|
|
];
|
|
|
|
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)', t => {
|
|
const errors = [
|
|
...t.throws(() =>
|
|
getPlugins(
|
|
{
|
|
cwd,
|
|
logger: t.context.logger,
|
|
options: {plugins: ['./test/fixtures/multi-plugin', './test/fixtures/plugin-noop', () => {}]},
|
|
},
|
|
{}
|
|
)
|
|
),
|
|
];
|
|
|
|
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', t => {
|
|
const errors = [
|
|
...t.throws(() =>
|
|
getPlugins({cwd, logger: t.context.logger, options: {plugins: [1, {path: 1}, [() => {}, {}, {}]]}}, {})
|
|
),
|
|
];
|
|
|
|
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');
|
|
});
|