refactor: pass argv via proxyquire for cli tests

This commit is contained in:
Pierre Vanduynslager 2018-07-16 16:28:07 -04:00
parent 264472c998
commit 12e4155cd3
3 changed files with 58 additions and 81 deletions

View File

@ -37,6 +37,10 @@ execa
}); });
// Node 8+ from this point on // Node 8+ from this point on
require('../cli')().catch(() => { require('../cli')()
process.exitCode = 1; .then(exitCode => {
}); process.exitCode = exitCode;
})
.catch(() => {
process.exitCode = 1;
});

12
cli.js
View File

@ -1,3 +1,5 @@
const {argv} = require('process');
const stringList = { const stringList = {
type: 'string', type: 'string',
array: true, array: true,
@ -36,11 +38,10 @@ Usage:
.exitProcess(false); .exitProcess(false);
try { try {
const {help, version, ...opts} = cli.argv; const {help, version, ...opts} = cli.parse(argv.slice(2));
if (Boolean(help) || Boolean(version)) { if (Boolean(help) || Boolean(version)) {
process.exitCode = 0; return 0;
return;
} }
// Set the `noCi` options as yargs sets the `ci` options instead (because arg starts with `--no`) // Set the `noCi` options as yargs sets the `ci` options instead (because arg starts with `--no`)
@ -52,13 +53,12 @@ Usage:
// Debug must be enabled before other requires in order to work // Debug must be enabled before other requires in order to work
require('debug').enable('semantic-release:*'); require('debug').enable('semantic-release:*');
} }
await require('.')(opts); await require('.')(opts);
process.exitCode = 0; return 0;
} catch (err) { } catch (err) {
if (err.name !== 'YError') { if (err.name !== 'YError') {
console.error(err); console.error(err);
} }
process.exitCode = 1; return 1;
} }
}; };

View File

@ -1,14 +1,10 @@
import test from 'ava'; import test from 'ava';
import proxyquire from 'proxyquire'; import proxyquire from 'proxyquire';
import clearModule from 'clear-module';
import {stub} from 'sinon'; import {stub} from 'sinon';
// Save the current process.env and process.argv const requireNoCache = proxyquire.noPreserveCache();
const envBackup = Object.assign({}, process.env);
const argvBackup = Object.assign({}, process.argv);
test.beforeEach(t => { test.beforeEach(t => {
clearModule('yargs');
t.context.logs = ''; t.context.logs = '';
t.context.errors = ''; t.context.errors = '';
t.context.stdout = stub(process.stdout, 'write').callsFake(val => { t.context.stdout = stub(process.stdout, 'write').callsFake(val => {
@ -20,18 +16,13 @@ test.beforeEach(t => {
}); });
test.afterEach.always(t => { test.afterEach.always(t => {
process.env = envBackup;
process.argv = argvBackup;
t.context.stdout.restore(); t.context.stdout.restore();
t.context.stderr.restore(); t.context.stderr.restore();
delete process.exitCode;
}); });
test.serial('Pass options to semantic-release API', async t => { test.serial('Pass options to semantic-release API', async t => {
const run = stub().resolves(true); const run = stub().resolves(true);
const cli = proxyquire('../cli', {'.': run}); const argv = [
process.argv = [
'', '',
'', '',
'-b', '-b',
@ -68,8 +59,9 @@ test.serial('Pass options to semantic-release API', async t => {
'--debug', '--debug',
'-d', '-d',
]; ];
const cli = requireNoCache('../cli', {'.': run, process: {...process, argv}});
await cli(); const exitCode = await cli();
t.is(run.args[0][0].branch, 'master'); t.is(run.args[0][0].branch, 'master');
t.is(run.args[0][0].repositoryUrl, 'https://github/com/owner/repo.git'); t.is(run.args[0][0].repositoryUrl, 'https://github/com/owner/repo.git');
@ -86,14 +78,12 @@ test.serial('Pass options to semantic-release API', async t => {
t.is(run.args[0][0].debug, true); t.is(run.args[0][0].debug, true);
t.is(run.args[0][0].dryRun, true); t.is(run.args[0][0].dryRun, true);
t.is(process.exitCode, 0); t.is(exitCode, 0);
}); });
test.serial('Pass options to semantic-release API with alias arguments', async t => { test.serial('Pass options to semantic-release API with alias arguments', async t => {
const run = stub().resolves(true); const run = stub().resolves(true);
const cli = proxyquire('../cli', {'.': run}); const argv = [
process.argv = [
'', '',
'', '',
'--branch', '--branch',
@ -107,8 +97,9 @@ test.serial('Pass options to semantic-release API with alias arguments', async t
'config2', 'config2',
'--dry-run', '--dry-run',
]; ];
const cli = requireNoCache('../cli', {'.': run, process: {...process, argv}});
await cli(); const exitCode = await cli();
t.is(run.args[0][0].branch, 'master'); t.is(run.args[0][0].branch, 'master');
t.is(run.args[0][0].repositoryUrl, 'https://github/com/owner/repo.git'); t.is(run.args[0][0].repositoryUrl, 'https://github/com/owner/repo.git');
@ -116,122 +107,104 @@ test.serial('Pass options to semantic-release API with alias arguments', async t
t.deepEqual(run.args[0][0].extends, ['config1', 'config2']); t.deepEqual(run.args[0][0].extends, ['config1', 'config2']);
t.is(run.args[0][0].dryRun, true); t.is(run.args[0][0].dryRun, true);
t.is(process.exitCode, 0); t.is(exitCode, 0);
}); });
test.serial('Pass unknown options to semantic-release API', async t => { test.serial('Pass unknown options to semantic-release API', async t => {
const run = stub().resolves(true); const run = stub().resolves(true);
const cli = proxyquire('../cli', {'.': run}); const argv = ['', '', '--bool', '--first-option', 'value1', '--second-option', 'value2', '--second-option', 'value3'];
const cli = requireNoCache('../cli', {'.': run, process: {...process, argv}});
process.argv = [ const exitCode = await cli();
'',
'',
'--bool',
'--first-option',
'value1',
'--second-option',
'value2',
'--second-option',
'value3',
];
await cli();
t.is(run.args[0][0].bool, true); t.is(run.args[0][0].bool, true);
t.is(run.args[0][0].firstOption, 'value1'); t.is(run.args[0][0].firstOption, 'value1');
t.deepEqual(run.args[0][0].secondOption, ['value2', 'value3']); t.deepEqual(run.args[0][0].secondOption, ['value2', 'value3']);
t.is(process.exitCode, 0); t.is(exitCode, 0);
}); });
test.serial('Pass empty Array to semantic-release API for list option set to "false"', async t => { test.serial('Pass empty Array to semantic-release API for list option set to "false"', async t => {
const run = stub().resolves(true); const run = stub().resolves(true);
const cli = proxyquire('../cli', {'.': run}); const argv = ['', '', '--publish', 'false'];
const cli = requireNoCache('../cli', {'.': run, process: {...process, argv}});
process.argv = ['', '', '--publish', 'false']; const exitCode = await cli();
await cli();
t.deepEqual(run.args[0][0].publish, []); t.deepEqual(run.args[0][0].publish, []);
t.is(process.exitCode, 0); t.is(exitCode, 0);
}); });
test.serial('Do not set properties in option for which arg is not in command line', async t => { test.serial('Do not set properties in option for which arg is not in command line', async t => {
const run = stub().resolves(true); const run = stub().resolves(true);
const cli = proxyquire('../cli', {'.': run}); const argv = ['', '', '-b', 'master'];
const cli = requireNoCache('../cli', {'.': run, process: {...process, argv}});
process.argv = ['', '', '-b', 'master'];
await cli(); await cli();
t.false(Reflect.apply(Object.prototype.hasOwnProperty, run.args[0][0], ['ci'])); t.false('ci' in run.args[0][0]);
t.false(Reflect.apply(Object.prototype.hasOwnProperty, run.args[0][0], ['d'])); t.false('d' in run.args[0][0]);
t.false(Reflect.apply(Object.prototype.hasOwnProperty, run.args[0][0], ['dry-run'])); t.false('dry-run' in run.args[0][0]);
t.false(Reflect.apply(Object.prototype.hasOwnProperty, run.args[0][0], ['debug'])); t.false('debug' in run.args[0][0]);
t.false(Reflect.apply(Object.prototype.hasOwnProperty, run.args[0][0], ['r'])); t.false('r' in run.args[0][0]);
t.false(Reflect.apply(Object.prototype.hasOwnProperty, run.args[0][0], ['t'])); t.false('t' in run.args[0][0]);
}); });
test.serial('Set "noCi" options to "true" with "--no-ci"', async t => { test.serial('Set "noCi" options to "true" with "--no-ci"', async t => {
const run = stub().resolves(true); const run = stub().resolves(true);
const cli = proxyquire('../cli', {'.': run}); const argv = ['', '', '--no-ci'];
const cli = requireNoCache('../cli', {'.': run, process: {...process, argv}});
process.argv = ['', '', '--no-ci']; const exitCode = await cli();
await cli();
t.is(run.args[0][0].noCi, true); t.is(run.args[0][0].noCi, true);
t.is(process.exitCode, 0); t.is(exitCode, 0);
}); });
test.serial('Display help', async t => { test.serial('Display help', async t => {
const run = stub().resolves(true); const run = stub().resolves(true);
const cli = proxyquire('../cli', {'.': run}); const argv = ['', '', '--help'];
const cli = requireNoCache('../cli', {'.': run, process: {...process, argv}});
process.argv = ['', '', '--help']; const exitCode = await cli();
await cli();
t.regex(t.context.logs, /Run automated package publishing/); t.regex(t.context.logs, /Run automated package publishing/);
t.is(process.exitCode, 0); t.is(exitCode, 0);
}); });
test.serial('Returns error code and prints help if called with a command', async t => { test.serial('Returns error code and prints help if called with a command', async t => {
const run = stub().resolves(true); const run = stub().resolves(true);
const cli = proxyquire('../cli', {'.': run}); const argv = ['', '', 'pre'];
const cli = requireNoCache('../cli', {'.': run, process: {...process, argv}});
process.argv = ['', '', 'pre']; const exitCode = await cli();
await cli();
t.regex(t.context.errors, /Run automated package publishing/); t.regex(t.context.errors, /Run automated package publishing/);
t.regex(t.context.errors, /Too many non-option arguments/); t.regex(t.context.errors, /Too many non-option arguments/);
t.is(process.exitCode, 1); t.is(exitCode, 1);
}); });
test.serial('Return error code if multiple plugin are set for single plugin', async t => { test.serial('Return error code if multiple plugin are set for single plugin', async t => {
const run = stub().resolves(true); const run = stub().resolves(true);
const cli = proxyquire('../cli', {'.': run}); const argv = ['', '', '--analyze-commits', 'analyze1', 'analyze2'];
const cli = requireNoCache('../cli', {'.': run, process: {...process, argv}});
process.argv = ['', '', '--analyze-commits', 'analyze1', 'analyze2']; const exitCode = await cli();
await cli();
t.regex(t.context.errors, /Run automated package publishing/); t.regex(t.context.errors, /Run automated package publishing/);
t.regex(t.context.errors, /Too many non-option arguments/); t.regex(t.context.errors, /Too many non-option arguments/);
t.is(process.exitCode, 1); t.is(exitCode, 1);
}); });
test.serial('Return error code if semantic-release throw error', async t => { test.serial('Return error code if semantic-release throw error', async t => {
const run = stub().rejects(new Error('semantic-release error')); const run = stub().rejects(new Error('semantic-release error'));
const cli = proxyquire('../cli', {'.': run}); const argv = ['', ''];
const cli = requireNoCache('../cli', {'.': run, process: {...process, argv}});
process.argv = ['', '']; const exitCode = await cli();
await cli();
t.regex(t.context.errors, /semantic-release error/); t.regex(t.context.errors, /semantic-release error/);
t.is(process.exitCode, 1); t.is(exitCode, 1);
}); });