Skip to content

Add command line argument handling for running single scenarios in features #168

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 13 additions & 16 deletions bin/cucumber.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,20 @@ var Cucumber = require('../lib/cucumber');
var cli = Cucumber.Cli(process.argv);
cli.run(function(succeeded) {
var code = succeeded ? 0 : 1;
var exitFunction = function() {
process.exit(code);
};

// --- exit after waiting for all pending output ---
var waitingIO = false;
process.stdout.on('drain', function() {
if (waitingIO) {
// the kernel buffer is now empty
exitFunction();
}
process.on('exit', function() {
process.exit(code);
});
if (process.stdout.write("")) {
// no buffer left, exit now:
exitFunction();
} else {
// write() returned false, kernel buffer is not empty yet...
waitingIO = true;

var timeoutId = setTimeout(function () {
console.error('Cucumber process timed out after waiting 60 seconds for the node.js event loop to empty. There may be a resource leak. Have all resources like database connections and network connections been closed properly?');
process.exit(code);
}, 60 * 1000);

if (timeoutId.unref) {
timeoutId.unref();
}
else {
clearTimeout(timeoutId);
}
});
28 changes: 28 additions & 0 deletions features/cli.feature
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,34 @@ Feature: Command line interface

"""

Scenario: run a single scenario within feature
Given a file named "features/a.feature" with:
"""
Feature: some feature
Scenario: first scenario
When a step is passing

Scenario: second scenario
When a step does not exist
"""
And a file named "features/step_definitions/cucumber_steps.js" with:
"""
var cucumberSteps = function() {
this.When(/^a step is passing$/, function(callback) { callback(); });
};
module.exports = cucumberSteps;
"""
When I run `cucumber.js features/a.feature:2`
Then it should pass with:
"""
.

1 scenario (1 passed)
1 step (1 passed)

"""


Scenario: run a single feature without step definitions
Given a file named "features/a.feature" with:
"""
Expand Down
4 changes: 2 additions & 2 deletions features/step_definitions/cli_steps.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,12 @@ var cliSteps = function cliSteps() {
});
});

this.Then(/^it passes with:$/, function(expectedOutput, callback) {
this.Then(/^it should pass with:$/, function(expectedOutput, callback) {
var actualOutput = lastRun['stdout'];
var actualError = lastRun['error'];
var actualStderr = lastRun['stderr'];

if (actualOutput.indexOf(expectedOutput) == -1)
if (actualOutput == actualError)
throw new Error("Expected output to match the following:\n'" + expectedOutput + "'\nGot:\n'" + actualOutput + "'.\n" +
"Error:\n'" + actualError + "'.\n" +
"stderr:\n'" + actualStderr +"'.");
Expand Down
1 change: 1 addition & 0 deletions lib/cucumber/ast/filter.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ var Filter = function(rules) {
};
Filter.AnyOfTagsRule = require('./filter/any_of_tags_rule');
Filter.ElementMatchingTagSpec = require('./filter/element_matching_tag_spec');
Filter.ScenarioAtLineRule = require('./filter/only_run_scenario_at_line_rule');
module.exports = Filter;
19 changes: 19 additions & 0 deletions lib/cucumber/ast/filter/only_run_scenario_at_line_rule.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
var ScenarioAtLineRule = function(tags) {
var Cucumber = require('../../../cucumber');

var self = {
isSatisfiedByElement: function isSatisfiedByElement(element) {
if (element.getUri && element.getLine){
var matches = Cucumber.Cli.ArgumentParser.FEATURE_FILENAME_AND_LINENUM_REGEXP.exec(element.getUri());
var specifiedLineNum = matches && matches[2];
if (specifiedLineNum) {
return parseInt(specifiedLineNum) === element.getLine();
}
return true;
}
return true;
}
};
return self;
};
module.exports = ScenarioAtLineRule;
3 changes: 2 additions & 1 deletion lib/cucumber/cli/argument_parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,8 @@ var ArgumentParser = function(argv) {
};
ArgumentParser.NUMBER_OF_LEADING_ARGS_TO_SLICE = 2;
ArgumentParser.DEFAULT_FEATURES_DIRECTORY = "features";
ArgumentParser.FEATURE_FILENAME_REGEXP = /[\/\\][^\/\\]+\.feature$/i;
ArgumentParser.FEATURE_FILENAME_REGEXP = /[\/\\][^\/\\]+\.feature(:\d+)*$/i;
ArgumentParser.FEATURE_FILENAME_AND_LINENUM_REGEXP = /(.*)\:(\d*)$|.*/; //fullmatch, filewithoutlinenum, linenum
ArgumentParser.LONG_OPTION_PREFIX = "--";
ArgumentParser.REQUIRE_OPTION_NAME = "require";
ArgumentParser.REQUIRE_OPTION_SHORT_NAME = "r";
Expand Down
2 changes: 1 addition & 1 deletion lib/cucumber/cli/argument_parser/feature_path_expander.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ var FeaturePathExpander = {
return expandedPaths;
}
};
FeaturePathExpander.FEATURE_FILES_IN_DIR_REGEXP = /\.feature$/;
FeaturePathExpander.FEATURE_FILES_IN_DIR_REGEXP = /\.feature(:\d+)*$/;
module.exports = FeaturePathExpander;
8 changes: 7 additions & 1 deletion lib/cucumber/cli/argument_parser/path_expander.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,20 @@ var PathExpander = {
},

expandPathWithRegexp: function expandPathWithRegexp(path, regexp) {
var Cucumber = require('../../../cucumber');
var matches = Cucumber.Cli.ArgumentParser.FEATURE_FILENAME_AND_LINENUM_REGEXP.exec(path);
var lineNum = matches && matches[2];
if (lineNum) {
path = matches[1];
}
var realPath = fs.realpathSync(path);
var stats = fs.statSync(realPath);
if (stats.isDirectory()) {
var paths = PathExpander.expandDirectoryWithRegexp(realPath, regexp);
return paths;
}
else
return [realPath];
return [realPath + (lineNum ? (':' + lineNum) : '')];
},

expandDirectoryWithRegexp: function expandDirectoryWithRegexp(directory, regexp) {
Expand Down
10 changes: 8 additions & 2 deletions lib/cucumber/cli/configuration.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,9 @@ var Configuration = function(argv) {
},

getAstFilter: function getAstFilter() {
var tagRules = self.getTagAstFilterRules();
var astFilter = Cucumber.Ast.Filter(tagRules);
var rules = self.getTagAstFilterRules();
rules.push(self.getSingleScenarioAstFilterRule());
var astFilter = Cucumber.Ast.Filter(rules);
return astFilter;
},

Expand All @@ -58,6 +59,11 @@ var Configuration = function(argv) {
return rules;
},

getSingleScenarioAstFilterRule: function getSingleScenarioAstFilterRule() {
var rule = Cucumber.Ast.Filter.ScenarioAtLineRule();
return rule;
},

isHelpRequested: function isHelpRequested() {
var isHelpRequested = argumentParser.isHelpRequested();
return isHelpRequested;
Expand Down
6 changes: 6 additions & 0 deletions lib/cucumber/cli/feature_source_loader.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
var FeatureSourceLoader = function(featureFilePaths) {
var Cucumber = require('../../cucumber');
var fs = require('fs');

var self = {
Expand All @@ -12,6 +13,11 @@ var FeatureSourceLoader = function(featureFilePaths) {
},

getSource: function getSource(featureFilePath) {
var matches = Cucumber.Cli.ArgumentParser.FEATURE_FILENAME_AND_LINENUM_REGEXP.exec(featureFilePath);
var wasLineNumSpecified = matches && matches[2];
if (wasLineNumSpecified) {
featureFilePath = matches[1];
}
var featureSource = fs.readFileSync(featureFilePath);
return featureSource;
}
Expand Down
4 changes: 3 additions & 1 deletion spec/cucumber/cli/configuration_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -179,9 +179,11 @@ describe("Cucumber.Cli.Configuration", function () {

beforeEach(function () {
astFilter = createSpyWithStubs("AST filter");
tagFilterRules = createSpy("tag specs");
tagFilterRules = [];
scenarioByLineFilterRules = createSpy("line specs");
spyOn(Cucumber.Ast, 'Filter').andReturn(astFilter);
spyOn(configuration, 'getTagAstFilterRules').andReturn(tagFilterRules);
spyOn(configuration, 'getSingleScenarioAstFilterRule').andReturn(scenarioByLineFilterRules);
});

it("gets the tag filter rules", function () {
Expand Down