Skip to content
This repository has been archived by the owner on Sep 21, 2022. It is now read-only.

Commit

Permalink
feat: allow to extend cli parser via CLI event
Browse files Browse the repository at this point in the history
  • Loading branch information
j0tunn committed Dec 6, 2017
1 parent 5cbdb9f commit 81fb51f
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 46 deletions.
2 changes: 2 additions & 0 deletions doc/events.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Gemini events

* `CLI` - emitted right at start, before cli is parsed. Allows to add new commands and extend help message. The event is emitted with 1 argument `parser` which is the [commander](https://github.com/tj/commander.js) instance used inside gemini itself.

* `INIT` - emitted before any job start (`test`, `update` or `readTests`). If handler returns a promise then job will start only after the promise will be resolved. Emitted only once no matter how many times job will be performed.

* `AFTER_TESTS_READ` - emitted after all tests were read (during `test`, `update` or `readTests` call). The event is emitted with 1 argument `data`:
Expand Down
101 changes: 55 additions & 46 deletions lib/cli/index.js
Original file line number Diff line number Diff line change
@@ -1,32 +1,36 @@
'use strict';

const _ = require('lodash');
const program = require('commander');
const Promise = require('bluebird');
const {Command} = require('commander');

const Config = require('../config');
const Events = require('../constants/events');
const Gemini = require('../gemini');
const pkg = require('../../package.json');
const handleErrors = require('./errors').handleErrors;
const checkForDeprecations = require('./deprecations').checkForDeprecations;
const handleUncaughtExceptions = require('./errors').handleUncaughtExceptions;
const {checkForDeprecations} = require('./deprecations');
const {handleErrors, handleUncaughtExceptions} = require('./errors');

let exitCode;

exports.run = () => {
const program = new Command();

program
.version(pkg.version)
.option('-c, --config <file>', 'config file');

const configPath = preparseOption(program, 'config');
const gemini = mkGemini(configPath);

program
.option('-b, --browser <browser>', 'run test only in specified browser', collect)
.option('-c, --config <file>', 'config file')
.option('--grep <pattern>', 'run only suites matching the pattern', RegExp);

program.command('update [paths...]')
.allowUnknownOption(true)
.option('--diff', 'update only screenshots with diff')
.option('--new', 'save only new screenshots')
.description('update the changed screenshots or gather if they doesn\'t exist')
.action((paths, options) => runGemini('update', paths, options).done());
.action((paths, options) => mkRunFn(gemini, 'update', program)(paths, options).done());

program.command('test [paths...]')
.allowUnknownOption(true)
Expand All @@ -39,7 +43,11 @@ exports.run = () => {
console.log(' vflat verbose console reporter');
console.log('');
})
.action((paths, options) => runGemini('test', paths, options).done());
.action((paths, options) => mkRunFn(gemini, 'test', program)(paths, options).done());

program.command('list <key>')
.description(`Use 'list browsers' or 'list sets' to get all available browsers or sets.`)
.action((key) => logOptionFromConfig(key, gemini));

program.on('--help', () => {
console.log('');
Expand All @@ -66,58 +74,59 @@ exports.run = () => {
console.log('');
});

program.option('list', 'Use \'list browsers\' or \'list sets\' to get all available browsers or sets.')
.action((option) => logOptionFromConfig(option));
gemini.extendCli(program);

program.parse(process.argv);
};

function logOptionFromConfig(option) {
const config = parseConfig(program.config);
function preparseOption(program, option) {
// do not display any help, do not exit
const configFileParser = Object.create(program);
configFileParser.options = [].concat(program.options);
configFileParser.option('-h, --help');

console.log(config[option] || `Cannot list option ${option}. Available options are: sets, browsers`);
configFileParser.parse(process.argv);
return configFileParser[option];
}

function parseConfig(configPath) {
const config = new Config(configPath);
function mkGemini(configPath) {
checkForDeprecations();

return {
sets: _.keys(config.sets).join(', '),
browsers: config.getBrowserIds().join(', ')
};
}
const gemini = new Gemini(configPath, {cli: true, env: true});
gemini.on(Events.INTERRUPT, (data) => exitCode = data.exitCode);

function collect(newValue, array) {
array = array || [];
return array.concat(newValue);
return gemini;
}

function runGemini(method, paths, options) {
options = options || {};

handleUncaughtExceptions();

return Promise.try(() => {
checkForDeprecations();

const gemini = new Gemini(program.config, {cli: true, env: true});
gemini.on(Events.INTERRUPT, (data) => exitCode = data.exitCode);

return gemini;
})
.then((gemini) => {
return gemini[method](paths, {
sets: options.set,
reporters: parseReporterOptions(options),
grep: program.grep,
browsers: program.browser,
diff: options.diff,
new: options.new
});
function mkRunFn(gemini, method, globalOpts) {
return (paths, opts = {}) => {
handleUncaughtExceptions();

return gemini[method](paths, {
sets: opts.set,
reporters: parseReporterOptions(opts),
grep: globalOpts.grep,
browsers: globalOpts.browser,
diff: opts.diff,
new: opts.new
})
.then((stats) => stats.failed > 0 ? 2 : 0)
.catch(handleErrors)
.then(exit);
};
}

function logOptionFromConfig(key, {config}) {
const data = {
sets: _.keys(config.sets).join(', '),
browsers: config.getBrowserIds().join(', ')
};

console.log(data[key] || `Cannot list option ${key}. Available options are: sets, browsers`);
}

function collect(newValue, array = []) {
return array.concat(newValue);
}

function parseReporterOptions(options) {
Expand Down
1 change: 1 addition & 0 deletions lib/constants/events.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
'use strict';

module.exports = {
CLI: 'cli',
INIT: 'init',

AFTER_TESTS_READ: 'afterTestsRead',
Expand Down
4 changes: 4 additions & 0 deletions lib/gemini.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ module.exports = class Gemini extends PassthroughEmitter {
this._loadPlugins();
}

extendCli(parser) {
this.emit(Events.CLI, parser);
}

getScreenshotPath(suite, stateName, browserId) {
return this.config.forBrowser(browserId).getScreenshotPath(suite, stateName);
}
Expand Down
14 changes: 14 additions & 0 deletions test/unit/gemini.js
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,20 @@ describe('gemini', () => {
});
});

describe('extendCli', () => {
it ('should emit CLI event with passed parser', () => {
const gemini = initGemini({});
const onCli = sinon.spy().named('onCli');
const parser = {foo: 'bar'};

gemini.on(Events.CLI, onCli);

gemini.extendCli(parser);

assert.calledOnceWith(onCli, parser);
});
});

describe('readTests', () => {
const readTests_ = (rootSuite, options) => {
gemini = initGemini({rootSuite});
Expand Down

0 comments on commit 81fb51f

Please sign in to comment.