Commit 9b3225f4 authored by Paolo Ambrosio's avatar Paolo Ambrosio
Browse files

Merge #116 fixes #79

parents bbf75ca1 fbc49a34
......@@ -7,7 +7,7 @@
### Bugfixes
None yet
* Fixed suggested step definition when step sentence contains double quote ([#116](https://github.com/cucumber/cucumber-cpp/issues/116) Kamil Strzempowicz, [fbc49a3](https://github.com/cucumber/cucumber-cpp/commit/fbc49a34e12a0b9b2a6e121d97ba1ad8f46dce8f) Paolo Ambrosio)
## [0.3.1](https://github.com/cucumber/cucumber-cpp/compare/v0.3...v0.3.1) (11 April 2016)
......
Feature: Escaping
In order to copy and paste the undefined steps snippets
As a developer
I want the regex string to be correctly escaped
Scenario Outline: <characters> in step definition
Given a scenario with:
"""
Given the step contains '<scenario step string>'
"""
And the steps have no mappings
When Cucumber executes the scenario
Then a step definition snippet with "^the step contains '<step definition string>'$" is suggested
# Remember that in Gherkin's data tables:
# \n -> newline
# \| -> |
# \\ -> \
# \<other> -> <other>
#
# Unfortunately the behaviour is just odd when chaining backslashes:
# \\\\\\. -> \\. (most common case)
# \\\\\\\\+ -> \\+
# \\\\\\\\\\\\\\ -> \\\\
Examples:
| characters | scenario step string | step definition string |
| Double quotes | " | \\" |
| Backslash | \\ | \\\\\\\\\\\\\\ |
| Dot | . | \\\\\\. |
| Caret | ^ | \\\\\\^ |
| Dollar | $ | \\\\\\$ |
| Asterisk | * | \\\\\\* |
| Plus | + | \\\\\\\\+ |
| Question mark | ? | \\\\\\? |
| Brackets | ( ) | \\\\\\( \\\\\\) |
| Square brackets | [ ] | \\\\\\[ \\\\\\] |
| Curly brackets | { } | \\\\\\{ \\\\\\} |
| Pipe | \| | \\\\\\\| |
......@@ -6,3 +6,6 @@ Then /^the step output should contain:$/ do |output|
expect(@steps_out.gets).to eq(output)
end
Then /^a step definition snippet with (".*") is suggested$/ do |regex_string|
assert_partial_output("(#{regex_string}) {", all_output)
end
......@@ -25,13 +25,17 @@ class CukeCommands {
public:
CukeCommands();
virtual ~CukeCommands();
void beginScenario(const TagExpression::tag_list *tags);
void endScenario();
const std::string snippetText(const std::string stepKeyword, const std::string stepName) const;
MatchResult stepMatches(const std::string description) const;
InvokeResult invoke(step_id_type id, const InvokeArgs * pArgs);
protected:
const std::string escapeRegex(const std::string regex) const;
const std::string escapeCString(const std::string str) const;
private:
StepManager stepManager;
HookRegistrar hookRegistrar;
......
......@@ -34,11 +34,22 @@ void CukeCommands::endScenario() {
}
const std::string CukeCommands::snippetText(const std::string stepKeyword, const std::string stepName) const {
std::stringstream snippetText; // TODO Escape stepName
snippetText << boost::to_upper_copy(stepKeyword) << "(\"^" << stepName << "$\") {" << std::endl;
snippetText << " pending();" << std::endl;
snippetText << "}" << std::endl;
return snippetText.str();
std::stringstream text;
text << boost::to_upper_copy(stepKeyword)
<< "(\""
<< escapeCString("^" + escapeRegex(stepName) + "$")
<< "\") {\n"
<< " pending();\n"
<< "}\n";
return text.str();
}
const std::string CukeCommands::escapeRegex(const std::string reg) const {
return regex_replace(reg, boost::regex("[\\|\\(\\)\\[\\]\\{\\}\\^\\$\\*\\+\\?\\.\\\\]"), "\\\\&", boost::match_default | boost::format_sed);
}
const std::string CukeCommands::escapeCString(const std::string str) const {
return regex_replace(str, boost::regex("[\"\\\\]"), "\\\\&", boost::match_default | boost::format_sed);
}
MatchResult CukeCommands::stepMatches(const std::string description) const {
......
......@@ -83,7 +83,7 @@ public:
TEST_F(CukeCommandsTest, matchesCorrectly) {
addStepWithMatcher(STATIC_MATCHER);
MatchResult result = cukeCommands.stepMatches(STATIC_MATCHER);
MatchResult result = stepMatches(STATIC_MATCHER);
EXPECT_EQ(stepInfoPtr->id, result.getResultSet().at(0).stepInfo->id);
}
......@@ -96,3 +96,20 @@ TEST_F(CukeCommandsTest, invokeHandlesParametersWithMacro) {
// The real test is in CheckAllParameters::body()
runStepBodyTest<CheckAllParametersWithMacro>();
}
TEST_F(CukeCommandsTest, producesSnippetsEscapingTitle) {
EXPECT_EQ("THEN(\"^x\\\\|y\\\"z$\") {\n"
" pending();\n"
"}\n",
snippetText("then","x|y\"z"));
}
TEST_F(CukeCommandsTest, escapesCaractersInRegexes) {
// abc|()[]{}^$*+?.\def <= abc\|\(\)\[\]\{\}\^\$\*\+\?\.\\def
EXPECT_EQ("abc\\|\\(\\)\\[\\]\\{\\}\\^\\$\\*\\+\\?\\.\\\\def", escapeRegex("abc|()[]{}^$*+?.\\def"));
}
TEST_F(CukeCommandsTest, escapesCharactersInCStrings) {
// abc\"def\\ghi <= abc"def\ghi
EXPECT_EQ("abc\\\"def\\\\ghi", escapeCString("abc\"def\\ghi"));
}
......@@ -14,13 +14,12 @@ class EmptyStep : public GenericStep {
void body() {}
};
class CukeCommandsFixture : public ::testing::Test {
class CukeCommandsFixture : public ::testing::Test, public CukeCommands {
StepManagerTestDouble stepManager;
public:
const static std::string STATIC_MATCHER;
protected:
CukeCommands cukeCommands;
shared_ptr<StepInfo> stepInfoPtr;
template<class T>
......@@ -28,7 +27,7 @@ protected:
addStepToManager<T>(STATIC_MATCHER);
const InvokeArgs *pArgs = T::buildInvokeArgs();
shared_ptr<const InvokeArgs> spArgs(pArgs);
cukeCommands.invoke(stepInfoPtr->id, pArgs);
invoke(stepInfoPtr->id, pArgs);
}
template<class T>
......
......@@ -99,20 +99,20 @@ protected:
}
void beginScenario(const TagExpression::tag_list *tags) {
cukeCommands.beginScenario(tags);
CukeCommandsFixture::beginScenario(tags);
}
void beginScenario(const TagExpression::tag_list & tags) {
TagExpression::tag_list *pTags = new TagExpression::tag_list(tags.begin(), tags.end());
beginScenario(pTags);
CukeCommandsFixture::beginScenario(pTags);
}
void invokeStep() {
cukeCommands.invoke(stepInfoPtr->id, &NO_INVOKE_ARGS);
invoke(stepInfoPtr->id, &NO_INVOKE_ARGS);
}
void endScenario() {
cukeCommands.endScenario();
CukeCommandsFixture::endScenario();
}
std::string sort(std::string str) {
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment