Skip to content

Commit

Permalink
Allow for unsafe regexp characters in stepdef string patterns (close #77
Browse files Browse the repository at this point in the history
)
  • Loading branch information
jbpros committed Oct 9, 2012
1 parent 729f927 commit 1fa696b
Show file tree
Hide file tree
Showing 5 changed files with 31 additions and 16 deletions.
13 changes: 8 additions & 5 deletions features/step_definitions/cucumber_js_mappings.rb
Original file line number Diff line number Diff line change
Expand Up @@ -344,30 +344,33 @@ def write_main_step_definitions_file
end

def write_coffee_script_definition_file
@mapping_name = "a CoffeeScript mapping"
append_to_file COFFEE_SCRIPT_DEFINITIONS_FILE, <<-EOF
fs = require('fs')
stepDefinitions = () ->
this.defineStep(/^a mapping$/, (callback) ->
fs.writeFileSync('a_mapping.step', '')
this.defineStep(/^#{@mapping_name}$/, (callback) ->
fs.writeFileSync('#{step_file(@mapping_name)}', '')
callback()
)
module.exports = stepDefinitions
EOF
end

def write_string_based_pattern_mapping
@mapping_name = "a mapping + fancy characters"
append_support_code <<-EOF
this.defineStep("a mapping", function(callback) {
fs.writeFileSync("#{step_file("a mapping")}", "");
this.defineStep("a mapping + fancy characters", function(callback) {
fs.writeFileSync("#{step_file(@mapping_name)}", "");
callback();
});
EOF
end

def write_string_based_pattern_mapping_with_parameters
@mapping_name = "a string-based mapping with parameters"
append_support_code <<-EOF
this.defineStep('a mapping with $word_param "$multi_word_param"', function(p1, p2, callback) {
fs.writeFileSync("#{step_file("a mapping")}", p1 + "\\n" + p2);
fs.writeFileSync("#{step_file(@mapping_name)}", p1 + "\\n" + p2);
callback();
});
EOF
Expand Down
6 changes: 3 additions & 3 deletions features/step_definitions/cucumber_steps.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
write_feature <<-EOF
Feature:
Scenario:
Given a mapping
Given #{@mapping_name}
EOF
run_feature
end
Expand All @@ -67,11 +67,11 @@
end

Then /^the mapping is run$/ do
assert_passed "a mapping"
assert_passed @mapping_name
end

Then /^the mapping receives the arguments$/ do
assert_passed_with_arguments "a mapping", @mapping_arguments
assert_passed_with_arguments @mapping_name, @mapping_arguments
end

Then /^the explicit World object function should have been called$/ do
Expand Down
12 changes: 7 additions & 5 deletions features/step_definitions/cucumber_world.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ proto.runAScenario = function runAScenario(callback) {
};

proto.runAScenarioCallingMapping = function runAScenarioCallingMapping(callback) {
this.addScenario("", "Given a mapping");
this.addScenario("", "Given " + this.mappingName);
this.runFeature({}, callback);
};

Expand Down Expand Up @@ -89,15 +89,17 @@ proto.isStepTouched = function isStepTouched(pattern) {
};

proto.addStringBasedPatternMapping = function addStringBasedPatternMapping() {
this.stepDefinitions += "Given('a mapping', function(callback) {\
world.logCycleEvent('a mapping');\
this.mappingName = "/a string-based mapping with fancy characters |\\ ^*-{(})+[a].?";
this.stepDefinitions += "Given('/a string-based mapping with fancy characters |\\\\ ^*-{(})+[a].?', function(callback) {\
world.logCycleEvent('/a string-based mapping with fancy characters |\\\\ ^*-{(})+[a].?');\
callback();\
});";
};

proto.addStringBasedPatternMappingWithParameters = function addStringBasedPatternMappingWithParameters() {
this.mappingName = "a string-based mapping";
this.stepDefinitions += "Given('a mapping with $word_param \"$multi_word_param\"', function(p1, p2, callback) {\
world.logCycleEvent('a mapping');\
world.logCycleEvent('a string-based mapping');\
world.actualMappingArguments = [p1, p2];\
callback();\
});";
Expand Down Expand Up @@ -193,7 +195,7 @@ proto.assertSkippedStep = function assertSkippedStep(stepName) {
};

proto.assertPassedMapping = function assertPassedMapping() {
this.assertCycleSequence("a mapping");
this.assertCycleSequence(this.mappingName);
};

proto.assertPassedMappingWithArguments = function assertPassedMappingWithArguments() {
Expand Down
3 changes: 3 additions & 0 deletions lib/cucumber/support_code/step_definition.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ var StepDefinition = function (pattern, code) {
var regexp;
if (pattern.replace) {
var regexpString = pattern
.replace(StepDefinition.UNSAFE_STRING_CHARACTERS_REGEXP, StepDefinition.PREVIOUS_REGEXP_MATCH)
.replace(StepDefinition.QUOTED_DOLLAR_PARAMETER_REGEXP, StepDefinition.QUOTED_DOLLAR_PARAMETER_SUBSTITUTION)
.replace(StepDefinition.DOLLAR_PARAMETER_REGEXP, StepDefinition.DOLLAR_PARAMETER_SUBSTITUTION);
regexpString =
Expand Down Expand Up @@ -90,10 +91,12 @@ var StepDefinition = function (pattern, code) {

StepDefinition.DOLLAR_PARAMETER_REGEXP = /\$[a-zA-Z_-]+/;
StepDefinition.DOLLAR_PARAMETER_SUBSTITUTION = '(.*)';
StepDefinition.PREVIOUS_REGEXP_MATCH = "\\$&";
StepDefinition.QUOTED_DOLLAR_PARAMETER_REGEXP = /"\$[a-zA-Z_-]+"/;
StepDefinition.QUOTED_DOLLAR_PARAMETER_SUBSTITUTION = '"([^"]*)"';
StepDefinition.STRING_PATTERN_REGEXP_PREFIX = '^';
StepDefinition.STRING_PATTERN_REGEXP_SUFFIX = '$';
StepDefinition.UNSAFE_STRING_CHARACTERS_REGEXP = /[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\|]/g;
StepDefinition.UNKNOWN_STEP_FAILURE_MESSAGE = "Step failure";

module.exports = StepDefinition;
13 changes: 10 additions & 3 deletions spec/cucumber/support_code/step_definition_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,26 @@ describe("Cucumber.SupportCode.StepDefinition", function () {
});

describe("when the pattern is a String", function () {
var regexp, quotedDollarParameterSubstitutedPattern, regexpString;
var regexp, quotedDollarParameterSubstitutedPattern, safeString, regexpString;

beforeEach(function () {
regexp = createSpy("regexp");
regexpString = "regexp string";
safeString = createSpyWithStubs("safe string");
quotedDollarParameterSubstitutedPattern = createSpyWithStubs("quoted dollar param substituted pattern", {replace: regexpString});
spyOnStub(pattern, 'replace').andReturn(quotedDollarParameterSubstitutedPattern);
spyOnStub(pattern, 'replace').andReturn(safeString);
spyOnStub(safeString, 'replace').andReturn(quotedDollarParameterSubstitutedPattern);
global.RegExp.andReturn(regexp);
});

it("escapes unsafe regexp characters from the string", function () {
stepDefinition.getPatternRegexp();
expect(pattern.replace).toHaveBeenCalledWith(Cucumber.SupportCode.StepDefinition.UNSAFE_STRING_CHARACTERS_REGEXP, Cucumber.SupportCode.StepDefinition.PREVIOUS_REGEXP_MATCH);
});

it("replaces quoted dollar-prefixed parameters with the regexp equivalent", function () {
stepDefinition.getPatternRegexp();
expect(pattern.replace).toHaveBeenCalledWith(Cucumber.SupportCode.StepDefinition.QUOTED_DOLLAR_PARAMETER_REGEXP, Cucumber.SupportCode.StepDefinition.QUOTED_DOLLAR_PARAMETER_SUBSTITUTION);
expect(safeString.replace).toHaveBeenCalledWith(Cucumber.SupportCode.StepDefinition.QUOTED_DOLLAR_PARAMETER_REGEXP, Cucumber.SupportCode.StepDefinition.QUOTED_DOLLAR_PARAMETER_SUBSTITUTION);
});

it("replaces other dollar-prefixed parameter with the regexp equivalent", function () {
Expand Down

0 comments on commit 1fa696b

Please sign in to comment.