295 lines
		
	
	
		
			9.3 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			295 lines
		
	
	
		
			9.3 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| import test from "ava";
 | |
| import { noop } from "lodash-es";
 | |
| import { stub } from "sinon";
 | |
| import normalize from "../../lib/plugins/normalize.js";
 | |
| 
 | |
| const cwd = process.cwd();
 | |
| 
 | |
| test.beforeEach((t) => {
 | |
|   // Stub the logger functions
 | |
|   t.context.log = stub();
 | |
|   t.context.error = stub();
 | |
|   t.context.success = stub();
 | |
|   t.context.stderr = { write: stub() };
 | |
|   t.context.logger = {
 | |
|     log: t.context.log,
 | |
|     error: t.context.error,
 | |
|     success: t.context.success,
 | |
|     scope: () => t.context.logger,
 | |
|   };
 | |
| });
 | |
| 
 | |
| test("Normalize and load plugin from string", async (t) => {
 | |
|   const plugin = await normalize(
 | |
|     { cwd, options: {}, logger: t.context.logger },
 | |
|     "verifyConditions",
 | |
|     "./test/fixtures/plugin-noop.cjs",
 | |
|     {}
 | |
|   );
 | |
| 
 | |
|   t.is(plugin.pluginName, "./test/fixtures/plugin-noop.cjs");
 | |
|   t.is(typeof plugin, "function");
 | |
|   t.deepEqual(t.context.success.args[0], ['Loaded plugin "verifyConditions" from "./test/fixtures/plugin-noop.cjs"']);
 | |
| });
 | |
| 
 | |
| test("Normalize and load plugin from object", async (t) => {
 | |
|   const plugin = await normalize(
 | |
|     { cwd, options: {}, logger: t.context.logger },
 | |
|     "publish",
 | |
|     { path: "./test/fixtures/plugin-noop.cjs" },
 | |
|     {}
 | |
|   );
 | |
| 
 | |
|   t.is(plugin.pluginName, "./test/fixtures/plugin-noop.cjs");
 | |
|   t.is(typeof plugin, "function");
 | |
|   t.deepEqual(t.context.success.args[0], ['Loaded plugin "publish" from "./test/fixtures/plugin-noop.cjs"']);
 | |
| });
 | |
| 
 | |
| test("Normalize and load plugin from a base file path", async (t) => {
 | |
|   const plugin = await normalize(
 | |
|     { cwd, options: {}, logger: t.context.logger },
 | |
|     "verifyConditions",
 | |
|     "./plugin-noop.cjs",
 | |
|     {
 | |
|       "./plugin-noop.cjs": "./test/fixtures",
 | |
|     }
 | |
|   );
 | |
| 
 | |
|   t.is(plugin.pluginName, "./plugin-noop.cjs");
 | |
|   t.is(typeof plugin, "function");
 | |
|   t.deepEqual(t.context.success.args[0], [
 | |
|     'Loaded plugin "verifyConditions" from "./plugin-noop.cjs" in shareable config "./test/fixtures"',
 | |
|   ]);
 | |
| });
 | |
| 
 | |
| test('Wrap plugin in a function that add the "pluginName" to the error"', async (t) => {
 | |
|   const plugin = await normalize({ cwd, options: {}, logger: t.context.logger }, "verifyConditions", "./plugin-error", {
 | |
|     "./plugin-error": "./test/fixtures",
 | |
|   });
 | |
| 
 | |
|   const error = await t.throwsAsync(plugin({ options: {} }));
 | |
| 
 | |
|   t.is(error.pluginName, "./plugin-error");
 | |
| });
 | |
| 
 | |
| test('Wrap plugin in a function that add the "pluginName" to multiple errors"', async (t) => {
 | |
|   const plugin = await normalize(
 | |
|     { cwd, options: {}, logger: t.context.logger },
 | |
|     "verifyConditions",
 | |
|     "./plugin-errors",
 | |
|     {
 | |
|       "./plugin-errors": "./test/fixtures",
 | |
|     }
 | |
|   );
 | |
| 
 | |
|   const errors = [...(await t.throwsAsync(plugin({ options: {} }))).errors];
 | |
|   for (const error of errors) {
 | |
|     t.is(error.pluginName, "./plugin-errors");
 | |
|   }
 | |
| });
 | |
| 
 | |
| test("Normalize and load plugin from function", async (t) => {
 | |
|   const pluginFunction = () => {};
 | |
|   const plugin = await normalize({ cwd, options: {}, logger: t.context.logger }, "", pluginFunction, {});
 | |
| 
 | |
|   t.is(plugin.pluginName, "[Function: pluginFunction]");
 | |
|   t.is(typeof plugin, "function");
 | |
| });
 | |
| 
 | |
| test("Normalize and load plugin that retuns multiple functions", async (t) => {
 | |
|   const plugin = await normalize(
 | |
|     { cwd, options: {}, logger: t.context.logger },
 | |
|     "verifyConditions",
 | |
|     "./test/fixtures/multi-plugin.cjs",
 | |
|     {}
 | |
|   );
 | |
| 
 | |
|   t.is(typeof plugin, "function");
 | |
|   t.deepEqual(t.context.success.args[0], ['Loaded plugin "verifyConditions" from "./test/fixtures/multi-plugin.cjs"']);
 | |
| });
 | |
| 
 | |
| test('Wrap "analyzeCommits" plugin in a function that validate the output of the plugin', async (t) => {
 | |
|   const analyzeCommits = stub().resolves(2);
 | |
|   const plugin = await normalize(
 | |
|     { cwd, options: {}, stderr: t.context.stderr, logger: t.context.logger },
 | |
|     "analyzeCommits",
 | |
|     analyzeCommits,
 | |
|     {}
 | |
|   );
 | |
| 
 | |
|   const error = await t.throwsAsync(plugin({ options: {} }));
 | |
| 
 | |
|   t.is(error.code, "EANALYZECOMMITSOUTPUT");
 | |
|   t.is(error.name, "SemanticReleaseError");
 | |
|   t.truthy(error.message);
 | |
|   t.truthy(error.details);
 | |
|   t.regex(error.details, /2/);
 | |
| });
 | |
| 
 | |
| test('Wrap "generateNotes" plugin in a function that validate the output of the plugin', async (t) => {
 | |
|   const generateNotes = stub().resolves(2);
 | |
|   const plugin = await normalize(
 | |
|     { cwd, options: {}, stderr: t.context.stderr, logger: t.context.logger },
 | |
|     "generateNotes",
 | |
|     generateNotes,
 | |
|     {}
 | |
|   );
 | |
| 
 | |
|   const error = await t.throwsAsync(plugin({ options: {} }));
 | |
| 
 | |
|   t.is(error.code, "EGENERATENOTESOUTPUT");
 | |
|   t.is(error.name, "SemanticReleaseError");
 | |
|   t.truthy(error.message);
 | |
|   t.truthy(error.details);
 | |
|   t.regex(error.details, /2/);
 | |
| });
 | |
| 
 | |
| test('Wrap "publish" plugin in a function that validate the output of the plugin', async (t) => {
 | |
|   const publish = stub().resolves(2);
 | |
|   const plugin = await normalize(
 | |
|     { cwd, options: {}, stderr: t.context.stderr, logger: t.context.logger },
 | |
|     "publish",
 | |
|     publish,
 | |
|     {}
 | |
|   );
 | |
| 
 | |
|   const error = await t.throwsAsync(plugin({ options: {} }));
 | |
| 
 | |
|   t.is(error.code, "EPUBLISHOUTPUT");
 | |
|   t.is(error.name, "SemanticReleaseError");
 | |
|   t.truthy(error.message);
 | |
|   t.truthy(error.details);
 | |
|   t.regex(error.details, /2/);
 | |
| });
 | |
| 
 | |
| test('Wrap "addChannel" plugin in a function that validate the output of the plugin', async (t) => {
 | |
|   const addChannel = stub().resolves(2);
 | |
|   const plugin = await normalize(
 | |
|     { cwd, options: {}, stderr: t.context.stderr, logger: t.context.logger },
 | |
|     "addChannel",
 | |
|     addChannel,
 | |
|     {}
 | |
|   );
 | |
| 
 | |
|   const error = await t.throwsAsync(plugin({ options: {} }));
 | |
| 
 | |
|   t.is(error.code, "EADDCHANNELOUTPUT");
 | |
|   t.is(error.name, "SemanticReleaseError");
 | |
|   t.truthy(error.message);
 | |
|   t.truthy(error.details);
 | |
|   t.regex(error.details, /2/);
 | |
| });
 | |
| 
 | |
| test('Plugin is called with "pluginConfig" (with object definition) and input', async (t) => {
 | |
|   const pluginFunction = stub().resolves();
 | |
|   const pluginConf = { path: pluginFunction, conf: "confValue" };
 | |
|   const options = { global: "globalValue" };
 | |
|   const plugin = await normalize({ cwd, options, logger: t.context.logger }, "", pluginConf, {});
 | |
|   await plugin({ options: {}, param: "param" });
 | |
| 
 | |
|   t.true(
 | |
|     pluginFunction.calledWithMatch(
 | |
|       { conf: "confValue", global: "globalValue" },
 | |
|       { param: "param", logger: t.context.logger }
 | |
|     )
 | |
|   );
 | |
| });
 | |
| 
 | |
| test('Plugin is called with "pluginConfig" (with array definition) and input', async (t) => {
 | |
|   const pluginFunction = stub().resolves();
 | |
|   const pluginConf = [pluginFunction, { conf: "confValue" }];
 | |
|   const options = { global: "globalValue" };
 | |
|   const plugin = await normalize({ cwd, options, logger: t.context.logger }, "", pluginConf, {});
 | |
|   await plugin({ options: {}, param: "param" });
 | |
| 
 | |
|   t.true(
 | |
|     pluginFunction.calledWithMatch(
 | |
|       { conf: "confValue", global: "globalValue" },
 | |
|       { param: "param", logger: t.context.logger }
 | |
|     )
 | |
|   );
 | |
| });
 | |
| 
 | |
| test('Prevent plugins to modify "pluginConfig"', async (t) => {
 | |
|   const pluginFunction = stub().callsFake((pluginConfig) => {
 | |
|     pluginConfig.conf.subConf = "otherConf";
 | |
|   });
 | |
|   const pluginConf = { path: pluginFunction, conf: { subConf: "originalConf" } };
 | |
|   const options = { globalConf: { globalSubConf: "originalGlobalConf" } };
 | |
|   const plugin = await normalize({ cwd, options, logger: t.context.logger }, "", pluginConf, {});
 | |
|   await plugin({ options: {} });
 | |
| 
 | |
|   t.is(pluginConf.conf.subConf, "originalConf");
 | |
|   t.is(options.globalConf.globalSubConf, "originalGlobalConf");
 | |
| });
 | |
| 
 | |
| test("Prevent plugins to modify its input", async (t) => {
 | |
|   const pluginFunction = stub().callsFake((pluginConfig, options) => {
 | |
|     options.param.subParam = "otherParam";
 | |
|   });
 | |
|   const input = { param: { subParam: "originalSubParam" }, options: {} };
 | |
|   const plugin = await normalize({ cwd, options: {}, logger: t.context.logger }, "", pluginFunction, {});
 | |
|   await plugin(input);
 | |
| 
 | |
|   t.is(input.param.subParam, "originalSubParam");
 | |
| });
 | |
| 
 | |
| test("Return noop if the plugin is not defined", async (t) => {
 | |
|   const plugin = await normalize({ cwd, options: {}, logger: t.context.logger });
 | |
| 
 | |
|   t.is(plugin, noop);
 | |
| });
 | |
| 
 | |
| test('Always pass a defined "pluginConfig" for plugin defined with string', async (t) => {
 | |
|   // Call the normalize function with the path of a plugin that returns its config
 | |
|   const plugin = await normalize(
 | |
|     { cwd, options: {}, logger: t.context.logger },
 | |
|     "",
 | |
|     "./test/fixtures/plugin-result-config",
 | |
|     {}
 | |
|   );
 | |
|   const pluginResult = await plugin({ options: {} });
 | |
| 
 | |
|   t.deepEqual(pluginResult.pluginConfig, {});
 | |
| });
 | |
| 
 | |
| test('Always pass a defined "pluginConfig" for plugin defined with path', async (t) => {
 | |
|   // Call the normalize function with the path of a plugin that returns its config
 | |
|   const plugin = await normalize(
 | |
|     { cwd, options: {}, logger: t.context.logger },
 | |
|     "",
 | |
|     { path: "./test/fixtures/plugin-result-config" },
 | |
|     {}
 | |
|   );
 | |
|   const pluginResult = await plugin({ options: {} });
 | |
| 
 | |
|   t.deepEqual(pluginResult.pluginConfig, {});
 | |
| });
 | |
| 
 | |
| test("Throws an error if the plugin return an object without the expected plugin function", async (t) => {
 | |
|   const error = await t.throwsAsync(() =>
 | |
|     normalize(
 | |
|       { cwd, options: {}, logger: t.context.logger },
 | |
|       "nonExistentPlugin",
 | |
|       "./test/fixtures/multi-plugin.cjs",
 | |
|       {}
 | |
|     )
 | |
|   );
 | |
| 
 | |
|   t.is(error.code, "EPLUGIN");
 | |
|   t.is(error.name, "SemanticReleaseError");
 | |
|   t.truthy(error.message);
 | |
|   t.truthy(error.details);
 | |
| });
 | |
| 
 | |
| test("Throws an error if the plugin is not found", async (t) => {
 | |
|   await t.throwsAsync(
 | |
|     () => normalize({ cwd, options: {}, logger: t.context.logger }, "nonExistentPlugin", "non-existing-path", {}),
 | |
|     {
 | |
|       message: /Cannot find module 'non-existing-path'/,
 | |
|       code: "MODULE_NOT_FOUND",
 | |
|       instanceOf: Error,
 | |
|     }
 | |
|   );
 | |
| });
 |