Skip to content

Commit

Permalink
cucumber-expressions: Use Unicode symbols as a parameter boundary in …
Browse files Browse the repository at this point in the history
…snippets (#1108)

Numerical parameters should be bounded by whitespace, punctuation or symbols
on both sides.

```
$1.50 -> ${double}
15° Celsius -> {int}° Celcius
i18n -> i18n
$15m -> $15m
```
Fixes: #844
  • Loading branch information
mpkorstanje authored Jul 10, 2020
1 parent 82f837e commit 35cdfbe
Show file tree
Hide file tree
Showing 12 changed files with 79 additions and 26 deletions.
17 changes: 4 additions & 13 deletions cucumber-expressions/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
### Removed

### Fixed

## [0.0.0] - 2020-07-03

### Added

### Changed

### Deprecated

### Removed

### Fixed

* Use Unicode symbols as a parameter boundary in snippets
([#1108](https://github.com/cucumber/cucumber/pull/1108)
[mpkorstanje])

## [10.2.1] - 2020-06-23

### Fixed
Expand Down
4 changes: 4 additions & 0 deletions cucumber-expressions/examples.txt
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,7 @@ I have 22 cukes in my belly now
/^a (pre-commercial transaction |pre buyer fee model )?purchase(?: for \$(\d+))?$/
a purchase for $33
[null,33]
---
Some ${float} of cukes at {int}° Celsius
Some $3.50 of cukes at 42° Celsius
[3.5,42]
18 changes: 18 additions & 0 deletions cucumber-expressions/go/cucumber_expression_generator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,24 @@ func TestCucumberExpressionGeneratory(t *testing.T) {
)
})

t.Run("generates expression for numbers with symbol and currency", func(t *testing.T) {
assertExpression(
t,
"Some ${float} of cukes at {int}° Celsius",
[]string{"float", "int"},
"Some $3.50 of cukes at 42° Celsius",
)
})

t.Run("generates expression for numbers with text on both sides", func(t *testing.T) {
assertExpression(
t,
"i18n",
[]string{},
"i18n",
)
})

t.Run("generates expression for strings", func(t *testing.T) {
assertExpression(
t,
Expand Down
4 changes: 4 additions & 0 deletions cucumber-expressions/go/examples.txt
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,7 @@ I have 22 cukes in my belly now
/^a (pre-commercial transaction |pre buyer fee model )?purchase(?: for \$(\d+))?$/
a purchase for $33
[null,33]
---
Some ${float} of cukes at {int}° Celsius
Some $3.50 of cukes at 42° Celsius
[3.5,42]
2 changes: 1 addition & 1 deletion cucumber-expressions/go/parameter_type_matcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ func (p *ParameterTypeMatcher) MatchEndWord() bool {
}

func (p *ParameterTypeMatcher) CharacterIsWordDelimiter(index int) bool {
matched, _ := regexp.MatchString(`\s|\p{P}`, p.text[index:index+1])
matched, _ := regexp.MatchString(`\p{Z}|\p{P}|\p{S}`, p.text[index:index+1])
return matched
}

Expand Down
4 changes: 4 additions & 0 deletions cucumber-expressions/java/examples.txt
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,7 @@ I have 22 cukes in my belly now
/^a (pre-commercial transaction |pre buyer fee model )?purchase(?: for \$(\d+))?$/
a purchase for $33
[null,33]
---
Some ${float} of cukes at {int}° Celsius
Some $3.50 of cukes at 42° Celsius
[3.5,42]
Original file line number Diff line number Diff line change
Expand Up @@ -14,33 +14,41 @@ final class ParameterTypeMatcher implements Comparable<ParameterTypeMatcher> {
this.text = text;
}

private static boolean isWhitespaceOrPunctuation(char c) {
return Pattern.matches("[\\s\\p{P}]", new String(new char[]{c}));
private static boolean isWhitespaceOrPunctuationOrSymbol(char c) {
return Pattern.matches("[\\p{Z}\\p{P}\\p{S}]", new String(new char[] { c }));
}

boolean advanceToAndFind(int newMatchPos) {
// Unlike js, ruby and go, the matcher is stateful
// so we can't use the immutable semantics.
matcher.region(newMatchPos, text.length());
while (matcher.find()) {
if (!group().isEmpty() && groupMatchesFullWord()) {
if (group().isEmpty()) {
continue;
}
if (groupHasWordBoundaryOnBothSides()) {
return true;
}
}
return false;
}

private boolean groupMatchesFullWord() {
private boolean groupHasWordBoundaryOnBothSides() {
return groupHasLeftWordBoundary() && groupHasRightWordBoundary();
}

private boolean groupHasLeftWordBoundary() {
if (matcher.start() > 0) {
char before = text.charAt(matcher.start() - 1);
if (!isWhitespaceOrPunctuation(before)) {
return false;
}
return isWhitespaceOrPunctuationOrSymbol(before);
}
return true;
}

private boolean groupHasRightWordBoundary() {
if (matcher.end() < text.length()) {
char after = text.charAt(matcher.end());
return isWhitespaceOrPunctuation(after);
return isWhitespaceOrPunctuationOrSymbol(after);
}
return true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,20 @@ public void generates_expression_for_int_double_arg() {
"I have 2 cukes and 1.5 euro");
}

@Test
public void generates_expression_for_numbers_with_symbols_and_currency() {
assertExpression(
"Some ${double} of cukes at {int}° Celsius", asList("double1", "int1"),
"Some $5000.00 of cukes at 42° Celsius");
}

@Test
public void generates_expression_for_numbers_with_text_on_both_sides() {
assertExpression(
"i18n", asList(),
"i18n");
}

@Test
public void generates_expression_for_strings() {
assertExpression(
Expand Down
4 changes: 4 additions & 0 deletions cucumber-expressions/javascript/examples.txt
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,7 @@ I have 22 cukes in my belly now
/^a (pre-commercial transaction |pre buyer fee model )?purchase(?: for \$(\d+))?$/
a purchase for $33
[null,33]
---
Some ${float} of cukes at {int}° Celsius
Some $3.50 of cukes at 42° Celsius
[3.5,42]
6 changes: 4 additions & 2 deletions cucumber-expressions/javascript/src/ParameterTypeMatcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,16 @@ export default class ParameterTypeMatcher {
}

get matchStartWord() {
return this.start === 0 || this.text[this.start - 1].match(/\s|\p{P}/u)
return (
this.start === 0 || this.text[this.start - 1].match(/\p{Z}|\p{P}|\p{S}/u)
)
}

get matchEndWord() {
const nextCharacterIndex = this.start + this.group.length
return (
nextCharacterIndex === this.text.length ||
this.text[nextCharacterIndex].match(/\s|\p{P}/u)
this.text[nextCharacterIndex].match(/\p{Z}|\p{P}|\p{S}/u)
)
}

Expand Down
4 changes: 4 additions & 0 deletions cucumber-expressions/ruby/examples.txt
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,7 @@ I have 22 cukes in my belly now
/^a (pre-commercial transaction |pre buyer fee model )?purchase(?: for \$(\d+))?$/
a purchase for $33
[null,33]
---
Some ${float} of cukes at {int}° Celsius
Some $3.50 of cukes at 42° Celsius
[3.5,42]
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,12 @@ def <=>(other)

def space_before_match_or_sentence_start?
match_begin = @match.begin(0)
match_begin == 0 || @text[match_begin - 1].match(/\s|\p{P}/)
match_begin == 0 || @text[match_begin - 1].match(/\p{Z}|\p{P}|\p{S}/)
end

def space_after_match_or_sentence_end?
match_end = @match.end(0)
match_end == @text.length || @text[match_end].match(/\s|\p{P}/)
match_end == @text.length || @text[match_end].match(/\p{Z}|\p{P}|\p{S}/)
end
end
end
Expand Down

0 comments on commit 35cdfbe

Please sign in to comment.