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

Commit 81fb51f

Browse files
committed
feat: allow to extend cli parser via CLI event
1 parent 5cbdb9f commit 81fb51f

File tree

5 files changed

+76
-46
lines changed

5 files changed

+76
-46
lines changed

doc/events.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# Gemini events
22

3+
* `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.
4+
35
* `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.
46

57
* `AFTER_TESTS_READ` - emitted after all tests were read (during `test`, `update` or `readTests` call). The event is emitted with 1 argument `data`:

lib/cli/index.js

Lines changed: 55 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,36 @@
11
'use strict';
22

33
const _ = require('lodash');
4-
const program = require('commander');
5-
const Promise = require('bluebird');
4+
const {Command} = require('commander');
65

7-
const Config = require('../config');
86
const Events = require('../constants/events');
97
const Gemini = require('../gemini');
108
const pkg = require('../../package.json');
11-
const handleErrors = require('./errors').handleErrors;
12-
const checkForDeprecations = require('./deprecations').checkForDeprecations;
13-
const handleUncaughtExceptions = require('./errors').handleUncaughtExceptions;
9+
const {checkForDeprecations} = require('./deprecations');
10+
const {handleErrors, handleUncaughtExceptions} = require('./errors');
1411

1512
let exitCode;
1613

1714
exports.run = () => {
15+
const program = new Command();
16+
1817
program
1918
.version(pkg.version)
19+
.option('-c, --config <file>', 'config file');
20+
21+
const configPath = preparseOption(program, 'config');
22+
const gemini = mkGemini(configPath);
23+
24+
program
2025
.option('-b, --browser <browser>', 'run test only in specified browser', collect)
21-
.option('-c, --config <file>', 'config file')
2226
.option('--grep <pattern>', 'run only suites matching the pattern', RegExp);
2327

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

3135
program.command('test [paths...]')
3236
.allowUnknownOption(true)
@@ -39,7 +43,11 @@ exports.run = () => {
3943
console.log(' vflat verbose console reporter');
4044
console.log('');
4145
})
42-
.action((paths, options) => runGemini('test', paths, options).done());
46+
.action((paths, options) => mkRunFn(gemini, 'test', program)(paths, options).done());
47+
48+
program.command('list <key>')
49+
.description(`Use 'list browsers' or 'list sets' to get all available browsers or sets.`)
50+
.action((key) => logOptionFromConfig(key, gemini));
4351

4452
program.on('--help', () => {
4553
console.log('');
@@ -66,58 +74,59 @@ exports.run = () => {
6674
console.log('');
6775
});
6876

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

7279
program.parse(process.argv);
7380
};
7481

75-
function logOptionFromConfig(option) {
76-
const config = parseConfig(program.config);
82+
function preparseOption(program, option) {
83+
// do not display any help, do not exit
84+
const configFileParser = Object.create(program);
85+
configFileParser.options = [].concat(program.options);
86+
configFileParser.option('-h, --help');
7787

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

81-
function parseConfig(configPath) {
82-
const config = new Config(configPath);
92+
function mkGemini(configPath) {
93+
checkForDeprecations();
8394

84-
return {
85-
sets: _.keys(config.sets).join(', '),
86-
browsers: config.getBrowserIds().join(', ')
87-
};
88-
}
95+
const gemini = new Gemini(configPath, {cli: true, env: true});
96+
gemini.on(Events.INTERRUPT, (data) => exitCode = data.exitCode);
8997

90-
function collect(newValue, array) {
91-
array = array || [];
92-
return array.concat(newValue);
98+
return gemini;
9399
}
94100

95-
function runGemini(method, paths, options) {
96-
options = options || {};
97-
98-
handleUncaughtExceptions();
99-
100-
return Promise.try(() => {
101-
checkForDeprecations();
102-
103-
const gemini = new Gemini(program.config, {cli: true, env: true});
104-
gemini.on(Events.INTERRUPT, (data) => exitCode = data.exitCode);
105-
106-
return gemini;
107-
})
108-
.then((gemini) => {
109-
return gemini[method](paths, {
110-
sets: options.set,
111-
reporters: parseReporterOptions(options),
112-
grep: program.grep,
113-
browsers: program.browser,
114-
diff: options.diff,
115-
new: options.new
116-
});
101+
function mkRunFn(gemini, method, globalOpts) {
102+
return (paths, opts = {}) => {
103+
handleUncaughtExceptions();
104+
105+
return gemini[method](paths, {
106+
sets: opts.set,
107+
reporters: parseReporterOptions(opts),
108+
grep: globalOpts.grep,
109+
browsers: globalOpts.browser,
110+
diff: opts.diff,
111+
new: opts.new
117112
})
118113
.then((stats) => stats.failed > 0 ? 2 : 0)
119114
.catch(handleErrors)
120115
.then(exit);
116+
};
117+
}
118+
119+
function logOptionFromConfig(key, {config}) {
120+
const data = {
121+
sets: _.keys(config.sets).join(', '),
122+
browsers: config.getBrowserIds().join(', ')
123+
};
124+
125+
console.log(data[key] || `Cannot list option ${key}. Available options are: sets, browsers`);
126+
}
127+
128+
function collect(newValue, array = []) {
129+
return array.concat(newValue);
121130
}
122131

123132
function parseReporterOptions(options) {

lib/constants/events.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
'use strict';
22

33
module.exports = {
4+
CLI: 'cli',
45
INIT: 'init',
56

67
AFTER_TESTS_READ: 'afterTestsRead',

lib/gemini.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,10 @@ module.exports = class Gemini extends PassthroughEmitter {
4949
this._loadPlugins();
5050
}
5151

52+
extendCli(parser) {
53+
this.emit(Events.CLI, parser);
54+
}
55+
5256
getScreenshotPath(suite, stateName, browserId) {
5357
return this.config.forBrowser(browserId).getScreenshotPath(suite, stateName);
5458
}

test/unit/gemini.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,20 @@ describe('gemini', () => {
159159
});
160160
});
161161

162+
describe('extendCli', () => {
163+
it ('should emit CLI event with passed parser', () => {
164+
const gemini = initGemini({});
165+
const onCli = sinon.spy().named('onCli');
166+
const parser = {foo: 'bar'};
167+
168+
gemini.on(Events.CLI, onCli);
169+
170+
gemini.extendCli(parser);
171+
172+
assert.calledOnceWith(onCli, parser);
173+
});
174+
});
175+
162176
describe('readTests', () => {
163177
const readTests_ = (rootSuite, options) => {
164178
gemini = initGemini({rootSuite});

0 commit comments

Comments
 (0)