Skip to content

Commit c37d1ce

Browse files
author
charlierudolph
committed
Use commander for cli parsing
closes #434 resolves #433
1 parent d294d04 commit c37d1ce

17 files changed

+307
-1556
lines changed

README.md

+54-46
Original file line numberDiff line numberDiff line change
@@ -427,75 +427,83 @@ var myAfterHooks = function () {
427427
module.exports = myAfterHooks;
428428
```
429429

430-
### Transpilers
430+
### CLI
431431

432-
Step definitions and support files can be written in other languages that transpile to javascript. This done with the CLI option `--compiler <file_extension>:<module_name>`.
433-
434-
#### CoffeeScript
435-
436-
Install the [coffee-script](https://www.npmjs.com/package/coffee-script) NPM package and invoke Cucumber with `--compiler coffee:coffee-script/register`.
437-
438-
#### TypeScript
432+
Cucumber.js includes a binary file to execute the features.
439433

440-
Install the [typescript-node](https://www.npmjs.com/package/typescript-node) NPM package and invoke Cucumber with `--compiler ts:typescript-node/register`.
434+
If you installed cucumber.js globally, you may run it with:
441435

442-
As usual, all your step definition and support files must export a function to be run by Cucumber. This is how it is done in TS:
436+
``` shell
437+
$ cucumber.js
438+
```
443439

440+
If you installed Cucumber locally, you may need to specify the path to the binary:
444441

445-
```typescript
446-
declare var module: any;
447-
module.exports = function () {
448-
this.Given(/.*/, function () {
449-
// ...
450-
})
451-
}
442+
``` shell
443+
$ ./node_modules/.bin/cucumber.js
452444
```
453445

454-
#### PogoScript
446+
**Note to Windows users:** invoke Cucumber.js with `cucumber-js` instead of `cucumber.js`. The latter is causing the operating system to invoke JScript instead of Node.js, because of the so-called file extension.
455447

456-
Install the [pogo](https://www.npmjs.com/package/pogo) NPM package and invoke Cucumber with `--compiler pogo:pogo`.
448+
#### Running specific features
457449

458-
### Custom Snippet Syntax
450+
* Specify a feature file
451+
* `$ cucumber.js features/my_feature.feature`
452+
* Specify a scenario by its line number
453+
* `$ cucumber.js features/my_feature.feature:3`
454+
* Use [Tags](#tags)
459455

460-
Undefined steps snippets are printed in javascript by default.
461-
Custom snippet snytaxes can be used with `--snippet-syntax <FILE>`.
462-
See [here](/features/step_definition_snippets_custom_syntax.feature) for an example.
456+
#### Requiring support files
463457

464-
##### Building a custom snippet syntax
458+
Use `--require <FILE|DIR>` to require files before executing the features.
459+
If not used, all "*.js" files (and other extensions specifed by `--compiler`) that are siblings
460+
or below the features will be loaded automatically. Automatic
461+
loading is disabled when this option is specified, and all loading becomes explicit.
462+
Files under directories named "support" are always loaded first
465463

466-
* See the [JavaScript syntax](/lib/cucumber/support_code/step_definition_snippet_builder/javascript_syntax.js) for an example. Please open an issue if you need more information.
467-
* Please add the keywords `cucumber` and `snippets` to your package,
468-
so it can easily be found by searching [npm](https://www.npmjs.com/search?q=cucumber+snippets).
464+
#### Formatters
469465

470-
### Run cucumber
466+
Use `--format <TYPE[:PATH]>` to specify the format of the output.
467+
If PATH is not supplied, the formatter prints to stdout.
468+
If PATH is supplied, it prints to the given file.
469+
If multiple formats are specified with the same output, only the last is used.
471470

472-
Cucumber.js includes a binary file to execute the features.
471+
Built-in formatters
472+
* pretty - prints the feature as is (default)
473+
* progress - prints one character per scenario
474+
* json - prints the feature as JSON
475+
* summary - prints a summary only, after all scenarios were executed
473476

474-
If you installed cucumber.js globally, you may run it with:
477+
#### Tags
475478

476-
``` shell
477-
$ cucumber.js
478-
```
479+
Use `--tags <EXPRESSION>` to run specific features or scenarios.
479480

480-
You may specify the features to run:
481+
* `--tag @dev`: tagged with @dev
482+
* `--tag ~@dev`: NOT tagged with `@dev`
483+
* `--tags @foo,@bar`: tagged with `@foo` OR `bar`
484+
* `--tags @foo --tags @bar`: tagged with `@foo` AND `bar`
481485

482-
``` shell
483-
$ cucumber.js features/my_feature.feature
484-
```
486+
#### Transpilers
485487

486-
And require specific step definitions and support code files with the --require option:
488+
Step definitions and support files can be written in other languages that transpile to javascript.
489+
This done with the CLI option `--compiler <file_extension>:<module_name>`.
490+
Below are some examples
487491

488-
``` shell
489-
$ cucumber.js features/my_feature.feature --require features/step_definitions/my_step_definitions.js
490-
```
492+
* [CoffeeScript](https://www.npmjs.com/package/coffee-script): `--compiler coffee:coffee-script/register`
493+
* [TypeScript](https://www.npmjs.com/package/ts-node): `--compiler ts:ts-node/register`
494+
* [Pogo](https://www.npmjs.com/package/pogo): `--compiler pogo:pogo`
491495

492-
If you installed Cucumber locally or with `npm install --save-dev`, you'll need to specify the path to the binary:
496+
### Custom Snippet Syntax
493497

494-
``` shell
495-
$ ./node_modules/.bin/cucumber.js
496-
```
498+
Undefined steps snippets are printed in javascript by default.
499+
Custom snippet snytaxes can be used with `--snippet-syntax <FILE>`.
500+
See [here](/features/step_definition_snippets_custom_syntax.feature) for an example.
497501

498-
**Note to Windows users:** invoke Cucumber.js with `cucumber-js` instead of `cucumber.js`. The latter is causing the operating system to invoke JScript instead of Node.js, because of the so-called file extension.
502+
##### Building a custom snippet syntax
503+
504+
* See the [JavaScript syntax](/lib/cucumber/support_code/step_definition_snippet_builder/javascript_syntax.js) for an example. Please open an issue if you need more information.
505+
* Please add the keywords `cucumber` and `snippets` to your package,
506+
so it can easily be found by searching [npm](https://www.npmjs.com/search?q=cucumber+snippets).
499507

500508
### Examples
501509

lib/cucumber/ast/filter/scenario_at_line_rule.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ function ScenarioAtLineRule(suppliedPaths) {
55

66
var mapping = {};
77
suppliedPaths.forEach(function(path){
8-
var matches = Cucumber.Cli.ArgumentParser.FEATURE_FILENAME_AND_LINENUM_REGEXP.exec(path);
8+
var matches = Cucumber.Cli.Configuration.FEATURE_FILENAME_AND_LINENUM_REGEXP.exec(path);
99
var specifiedLineNums = matches && matches[2];
1010
if (specifiedLineNums) {
1111
var realPath = fs.realpathSync(matches[1]);

lib/cucumber/cli.js

+51-97
Original file line numberDiff line numberDiff line change
@@ -1,116 +1,70 @@
11
function Cli(argv) {
22
var Cucumber = require('../cucumber');
3+
var Command = require('commander').Command;
4+
var path = require('path');
5+
6+
function collect(val, memo) {
7+
memo.push(val);
8+
return memo;
9+
}
10+
11+
function getConfiguration() {
12+
var program = getProgram();
13+
program.parse(argv);
14+
var profileArgs = Cucumber.Cli.ProfilesLoader.getArgs(program.profile);
15+
argv.splice.apply(argv, [2, 0].concat(profileArgs));
16+
program.parse(argv);
17+
var configuration = Cucumber.Cli.Configuration(program.opts(), program.args);
18+
return configuration;
19+
}
20+
21+
function getProgram () {
22+
var program = new Command(path.basename(argv[1]));
23+
24+
program
25+
.usage('[options] [<DIR|FILE[:LINE]>...]')
26+
.version(Cucumber.VERSION, '-v, --version')
27+
.option('-b, --backtrace', 'show full backtrace for errors')
28+
.option('--compiler <EXTENSION:MODULE>', 'require files with the given EXTENSION after requiring MODULE (repeatable)', collect, [])
29+
.option('-d, --dry-run', 'invoke formatters without executing steps')
30+
.option('--fail-fast', 'abort the run on first failure')
31+
.option('-f, --format <TYPE[:PATH]>', 'specify the output format, optionally supply PATH to redirect formatter output (repeatable)', collect, ['pretty'])
32+
.option('--no-colors', 'disable colors in formatter output')
33+
.option('--no-snippets', 'hide step definition snippets for pending steps')
34+
.option('--no-source', 'hide source uris')
35+
.option('-p, --profile <NAME>', 'specify the profile to use (repeatable)', collect, [])
36+
.option('-r, --require <FILE|DIR>', 'require files before executing features (repeatable)', collect, [])
37+
.option('--snippet-syntax <FILE>', 'specify a custom snippet syntax')
38+
.option('-S, --strict', 'fail if there are any undefined or pending steps')
39+
.option('-t, --tags <EXPRESSION>', 'only execute the features or scenarios with tags matching the expression (repeatable)', collect, []);
40+
41+
program.on('--help', function(){
42+
console.log(' For more details please visit https://github.com/cucumber/cucumber-js#cli\n');
43+
});
44+
45+
return program;
46+
}
347

448
var self = {
549
run: function run(callback) {
6-
var configuration = Cli.Configuration(argv);
7-
if (configuration.isHelpRequested())
8-
self.displayHelp(callback);
9-
else if (configuration.isVersionRequested())
10-
self.displayVersion(callback);
11-
else
12-
self.runSuiteWithConfiguration(configuration, callback);
13-
},
14-
15-
runSuiteWithConfiguration: function runSuiteWithConfiguration(configuration, callback) {
50+
var configuration = getConfiguration();
1651
var runtime = Cucumber.Runtime(configuration);
1752
var formatters = configuration.getFormatters();
1853
formatters.forEach(function (formatter) {
1954
runtime.attachListener(formatter);
2055
});
2156
runtime.start(callback);
22-
},
23-
24-
displayHelp: function displayHelp(callback) {
25-
/* jshint -W109 */
26-
process.stdout.write("Usage: cucumber.js [options] [[FILE|DIR][:LINE]]+\n\
27-
\n\
28-
-r, --require LIBRARY|DIR Require files before executing the features. If\n\
29-
this option is not specified, all *.js and\n\
30-
*.coffee files that are siblings or below the\n\
31-
features will be loaded automatically. Automatic\n\
32-
loading is disabled when this option is specified,\n\
33-
and all loading becomes explicit.\n\
34-
\n\
35-
Files under directories named \"support\" are\n\
36-
always loaded first.\n\
37-
\n\
38-
-t, --tags TAG_EXPRESSION Only execute the features or scenarios with tags\n\
39-
matching TAG_EXPRESSION. Scenarios inherit tags\n\
40-
declared on the Feature level. The simplest\n\
41-
TAG_EXPRESSION is simply a tag. Example:\n\
42-
--tags @dev\n\
43-
\n\
44-
When a tag in a tag expression starts with a ~,\n\
45-
this represents boolean NOT. Example:\n\
46-
--tags ~@dev\n\
47-
\n\
48-
A tag expression can have several tags separated\n\
49-
by a comma, which represents logical OR. Example:\n\
50-
--tags @dev,@wip\n\
51-
\n\
52-
The --tags option can be specified several times,\n\
53-
and this represents logical AND. Example:\n\
54-
--tags @foo,~@bar --tags @zap.\n\
55-
\n\
56-
This represents the following boolean expression:\n\
57-
(@foo || !@bar) && @zap.\n\
58-
\n\
59-
Beware that if you want to use several negative\n\
60-
tags to exclude several tags you have to use\n\
61-
logical AND: --tags ~@fixme --tags ~@buggy.\n\
62-
\n\
63-
-f, --format FORMAT[:PATH] How to format features (default: progress).\n\
64-
Supply PATH to redirect that formatters output.\n\
65-
Available formats:\n\
66-
pretty : prints the feature as is\n\
67-
progress: prints one character per scenario\n\
68-
json : prints the feature as JSON\n\
69-
summary : prints a summary only, after all\n\
70-
scenarios were executed\n\
71-
\n\
72-
-p, --profile PROFILE Pull command line arguments from cucumber.js.\n\
73-
When a 'default' profile is defined and no profile\n\
74-
is specified it is always used.\n\
75-
\n\
76-
--compiler SUFFIX:MODULE Include step definitions and support files with the\n\
77-
given file suffix and require the given module to\n\
78-
load those files.\n\
79-
\n\
80-
-i, --no-snippets Don't print snippets for pending steps.\n\
81-
\n\
82-
--snippet-syntax FILE Specify a custom snippet syntax.\n\
83-
\n\
84-
-b, --backtrace Show full backtrace for all errors.\n\
85-
\n\
86-
-S, --strict Fail if there are any undefined or pending steps.\n\
87-
\n\
88-
-d, --dry-run Invokes formatters without executing the steps.\n\
89-
\n\
90-
--no-source Don't print the source uris.\n\
91-
\n\
92-
--[no-]colors Enable/disable colors in output.\n\
93-
\n\
94-
--fail-fast Abort the run on first failure.\n\
95-
\n\
96-
-v, --version Display Cucumber.js's version.\n\
97-
\n\
98-
-h, --help You're looking at it.\n");
99-
/* jshint +W109 */
100-
callback(true);
101-
},
102-
103-
displayVersion: function displayVersion(callback) {
104-
process.stdout.write(Cucumber.VERSION + '\n');
105-
callback(true);
10657
}
10758
};
10859
return self;
10960
}
11061

111-
Cli.ArgumentParser = require('./cli/argument_parser');
112-
Cli.Configuration = require('./cli/configuration');
62+
Cli.Configuration = require('./cli/configuration');
63+
Cli.FeaturePathExpander = require('./cli/feature_path_expander');
11364
Cli.FeatureSourceLoader = require('./cli/feature_source_loader');
114-
Cli.SupportCodeLoader = require('./cli/support_code_loader');
65+
Cli.PathExpander = require('./cli/path_expander');
66+
Cli.ProfilesLoader = require('./cli/profiles_loader');
67+
Cli.SupportCodeLoader = require('./cli/support_code_loader');
68+
Cli.SupportCodePathExpander = require('./cli/support_code_path_expander');
11569

11670
module.exports = Cli;

0 commit comments

Comments
 (0)