cucumber-cucumber-expressions 10.2.0 → 11.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rsync +1 -0
- data/VERSION +1 -1
- data/cucumber-cucumber-expressions.gemspec +2 -2
- data/default.mk +12 -0
- data/examples.txt +13 -1
- data/lib/cucumber/cucumber_expressions/ast.rb +201 -0
- data/lib/cucumber/cucumber_expressions/cucumber_expression.rb +80 -76
- data/lib/cucumber/cucumber_expressions/cucumber_expression_generator.rb +4 -8
- data/lib/cucumber/cucumber_expressions/cucumber_expression_parser.rb +219 -0
- data/lib/cucumber/cucumber_expressions/cucumber_expression_tokenizer.rb +95 -0
- data/lib/cucumber/cucumber_expressions/errors.rb +175 -4
- data/lib/cucumber/cucumber_expressions/group.rb +1 -1
- data/lib/cucumber/cucumber_expressions/parameter_type.rb +12 -7
- data/lib/cucumber/cucumber_expressions/parameter_type_matcher.rb +2 -2
- data/lib/cucumber/cucumber_expressions/parameter_type_registry.rb +1 -1
- data/lib/cucumber/cucumber_expressions/tree_regexp.rb +53 -46
- data/spec/cucumber/cucumber_expressions/cucumber_expression_parser_spec.rb +24 -0
- data/spec/cucumber/cucumber_expressions/cucumber_expression_spec.rb +73 -124
- data/spec/cucumber/cucumber_expressions/cucumber_expression_tokenizer_spec.rb +24 -0
- data/spec/cucumber/cucumber_expressions/custom_parameter_type_spec.rb +1 -1
- data/spec/cucumber/cucumber_expressions/expression_examples_spec.rb +1 -1
- data/spec/cucumber/cucumber_expressions/expression_factory_spec.rb +0 -4
- data/spec/cucumber/cucumber_expressions/tree_regexp_spec.rb +58 -5
- data/testdata/ast/alternation-followed-by-optional.yaml +17 -0
- data/testdata/ast/alternation-phrase.yaml +16 -0
- data/testdata/ast/alternation-with-parameter.yaml +27 -0
- data/testdata/ast/alternation-with-unused-end-optional.yaml +15 -0
- data/testdata/ast/alternation-with-unused-start-optional.yaml +8 -0
- data/testdata/ast/alternation-with-white-space.yaml +12 -0
- data/testdata/ast/alternation.yaml +12 -0
- data/testdata/ast/anonymous-parameter.yaml +5 -0
- data/testdata/ast/closing-brace.yaml +5 -0
- data/testdata/ast/closing-parenthesis.yaml +5 -0
- data/testdata/ast/empty-alternation.yaml +8 -0
- data/testdata/ast/empty-alternations.yaml +9 -0
- data/testdata/ast/empty-string.yaml +3 -0
- data/testdata/ast/escaped-alternation.yaml +5 -0
- data/testdata/ast/escaped-backslash.yaml +5 -0
- data/testdata/ast/escaped-opening-parenthesis.yaml +5 -0
- data/testdata/ast/escaped-optional-followed-by-optional.yaml +15 -0
- data/testdata/ast/escaped-optional-phrase.yaml +10 -0
- data/testdata/ast/escaped-optional.yaml +7 -0
- data/testdata/ast/opening-brace.yaml +8 -0
- data/testdata/ast/opening-parenthesis.yaml +8 -0
- data/testdata/ast/optional-containing-nested-optional.yaml +15 -0
- data/testdata/ast/optional-phrase.yaml +12 -0
- data/testdata/ast/optional.yaml +7 -0
- data/testdata/ast/parameter.yaml +7 -0
- data/testdata/ast/phrase.yaml +9 -0
- data/testdata/ast/unfinished-parameter.yaml +8 -0
- data/testdata/expression/allows-escaped-optional-parameter-types.yaml +4 -0
- data/testdata/expression/allows-parameter-type-in-alternation-1.yaml +4 -0
- data/testdata/expression/allows-parameter-type-in-alternation-2.yaml +4 -0
- data/testdata/expression/does-allow-parameter-adjacent-to-alternation.yaml +5 -0
- data/testdata/expression/does-not-allow-alternation-in-optional.yaml +9 -0
- data/testdata/expression/does-not-allow-alternation-with-empty-alternative-by-adjacent-left-parameter.yaml +10 -0
- data/testdata/expression/does-not-allow-alternation-with-empty-alternative-by-adjacent-optional.yaml +9 -0
- data/testdata/expression/does-not-allow-alternation-with-empty-alternative-by-adjacent-right-parameter.yaml +9 -0
- data/testdata/expression/does-not-allow-alternation-with-empty-alternative.yaml +9 -0
- data/testdata/expression/does-not-allow-empty-optional.yaml +9 -0
- data/testdata/expression/does-not-allow-nested-optional.yaml +8 -0
- data/testdata/expression/does-not-allow-optional-parameter-types.yaml +9 -0
- data/testdata/expression/does-not-allow-parameter-name-with-reserved-characters.yaml +10 -0
- data/testdata/expression/does-not-allow-unfinished-parenthesis-1.yaml +8 -0
- data/testdata/expression/does-not-allow-unfinished-parenthesis-2.yaml +8 -0
- data/testdata/expression/does-not-allow-unfinished-parenthesis-3.yaml +8 -0
- data/testdata/expression/does-not-match-misquoted-string.yaml +4 -0
- data/testdata/expression/doesnt-match-float-as-int.yaml +5 -0
- data/testdata/expression/matches-alternation.yaml +4 -0
- data/testdata/expression/matches-anonymous-parameter-type.yaml +5 -0
- data/testdata/expression/matches-double-quoted-empty-string-as-empty-string-along-with-other-strings.yaml +4 -0
- data/testdata/expression/matches-double-quoted-empty-string-as-empty-string.yaml +4 -0
- data/testdata/expression/matches-double-quoted-string-with-escaped-double-quote.yaml +4 -0
- data/testdata/expression/matches-double-quoted-string-with-single-quotes.yaml +4 -0
- data/testdata/expression/matches-double-quoted-string.yaml +4 -0
- data/testdata/expression/matches-doubly-escaped-parenthesis.yaml +4 -0
- data/testdata/expression/matches-doubly-escaped-slash-1.yaml +4 -0
- data/testdata/expression/matches-doubly-escaped-slash-2.yaml +4 -0
- data/testdata/expression/matches-escaped-parenthesis-1.yaml +4 -0
- data/testdata/expression/matches-escaped-parenthesis-2.yaml +4 -0
- data/testdata/expression/matches-escaped-parenthesis-3.yaml +4 -0
- data/testdata/expression/matches-escaped-slash.yaml +4 -0
- data/testdata/expression/matches-float-1.yaml +5 -0
- data/testdata/expression/matches-float-2.yaml +5 -0
- data/testdata/expression/matches-int.yaml +5 -0
- data/testdata/expression/matches-multiple-double-quoted-strings.yaml +4 -0
- data/testdata/expression/matches-multiple-single-quoted-strings.yaml +4 -0
- data/testdata/expression/matches-optional-before-alternation-1.yaml +4 -0
- data/testdata/expression/matches-optional-before-alternation-2.yaml +4 -0
- data/testdata/expression/matches-optional-before-alternation-with-regex-characters-1.yaml +4 -0
- data/testdata/expression/matches-optional-before-alternation-with-regex-characters-2.yaml +4 -0
- data/testdata/expression/matches-optional-in-alternation-1.yaml +5 -0
- data/testdata/expression/matches-optional-in-alternation-2.yaml +5 -0
- data/testdata/expression/matches-optional-in-alternation-3.yaml +5 -0
- data/testdata/expression/matches-single-quoted-empty-string-as-empty-string-along-with-other-strings.yaml +4 -0
- data/testdata/expression/matches-single-quoted-empty-string-as-empty-string.yaml +4 -0
- data/testdata/expression/matches-single-quoted-string-with-double-quotes.yaml +4 -0
- data/testdata/expression/matches-single-quoted-string-with-escaped-single-quote.yaml +4 -0
- data/testdata/expression/matches-single-quoted-string.yaml +4 -0
- data/testdata/expression/matches-word.yaml +4 -0
- data/testdata/expression/throws-unknown-parameter-type.yaml +10 -0
- data/testdata/regex/alternation-with-optional.yaml +2 -0
- data/testdata/regex/alternation.yaml +2 -0
- data/testdata/regex/empty.yaml +2 -0
- data/testdata/regex/escape-regex-characters.yaml +2 -0
- data/testdata/regex/optional.yaml +2 -0
- data/testdata/regex/parameter.yaml +2 -0
- data/testdata/regex/text.yaml +2 -0
- data/testdata/regex/unicode.yaml +2 -0
- data/testdata/tokens/alternation-phrase.yaml +13 -0
- data/testdata/tokens/alternation.yaml +9 -0
- data/testdata/tokens/empty-string.yaml +6 -0
- data/testdata/tokens/escape-non-reserved-character.yaml +8 -0
- data/testdata/tokens/escaped-alternation.yaml +9 -0
- data/testdata/tokens/escaped-char-has-start-index-of-text-token.yaml +9 -0
- data/testdata/tokens/escaped-end-of-line.yaml +8 -0
- data/testdata/tokens/escaped-optional.yaml +7 -0
- data/testdata/tokens/escaped-parameter.yaml +7 -0
- data/testdata/tokens/escaped-space.yaml +7 -0
- data/testdata/tokens/optional-phrase.yaml +13 -0
- data/testdata/tokens/optional.yaml +9 -0
- data/testdata/tokens/parameter-phrase.yaml +13 -0
- data/testdata/tokens/parameter.yaml +9 -0
- data/testdata/tokens/phrase.yaml +11 -0
- metadata +118 -13
- data/spec/cucumber/cucumber_expressions/cucumber_expression_regexp_spec.rb +0 -57
@@ -9,12 +9,16 @@ module Cucumber
|
|
9
9
|
attr_reader :name, :type, :regexps
|
10
10
|
|
11
11
|
def self.check_parameter_type_name(type_name)
|
12
|
+
unless is_valid_parameter_type_name(type_name)
|
13
|
+
raise CucumberExpressionError.new("Illegal character in parameter name {#{type_name}}. Parameter names may not contain '[]()$.|?*+'")
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.is_valid_parameter_type_name(type_name)
|
12
18
|
unescaped_type_name = type_name.gsub(UNESCAPE_PATTERN) do
|
13
19
|
$2
|
14
20
|
end
|
15
|
-
|
16
|
-
raise CucumberExpressionError.new("Illegal character '#{$1}' in parameter name {#{unescaped_type_name}}")
|
17
|
-
end
|
21
|
+
!(ILLEGAL_PARAMETER_NAME_PATTERN =~ unescaped_type_name)
|
18
22
|
end
|
19
23
|
|
20
24
|
def prefer_for_regexp_match?
|
@@ -58,16 +62,17 @@ module Cucumber
|
|
58
62
|
|
59
63
|
private
|
60
64
|
|
65
|
+
|
61
66
|
def string_array(regexps)
|
62
67
|
array = regexps.is_a?(Array) ? regexps : [regexps]
|
63
|
-
array.map {|regexp| regexp.is_a?(String) ? regexp : regexp_source(regexp)}
|
68
|
+
array.map { |regexp| regexp.is_a?(String) ? regexp : regexp_source(regexp) }
|
64
69
|
end
|
65
70
|
|
66
71
|
def regexp_source(regexp)
|
67
72
|
[
|
68
|
-
|
69
|
-
|
70
|
-
|
73
|
+
'EXTENDED',
|
74
|
+
'IGNORECASE',
|
75
|
+
'MULTILINE'
|
71
76
|
].each do |option_name|
|
72
77
|
option = Regexp.const_get(option_name)
|
73
78
|
if regexp.options & option != 0
|
@@ -47,12 +47,12 @@ module Cucumber
|
|
47
47
|
|
48
48
|
def space_before_match_or_sentence_start?
|
49
49
|
match_begin = @match.begin(0)
|
50
|
-
match_begin == 0 || @text[match_begin - 1].match(/\
|
50
|
+
match_begin == 0 || @text[match_begin - 1].match(/\p{Z}|\p{P}|\p{S}/)
|
51
51
|
end
|
52
52
|
|
53
53
|
def space_after_match_or_sentence_end?
|
54
54
|
match_end = @match.end(0)
|
55
|
-
match_end == @text.length || @text[match_end].match(/\
|
55
|
+
match_end == @text.length || @text[match_end].match(/\p{Z}|\p{P}|\p{S}/)
|
56
56
|
end
|
57
57
|
end
|
58
58
|
end
|
@@ -18,7 +18,7 @@ module Cucumber
|
|
18
18
|
define_parameter_type(ParameterType.new('int', INTEGER_REGEXPS, Integer, lambda {|s = nil| s && s.to_i}, true, true))
|
19
19
|
define_parameter_type(ParameterType.new('float', FLOAT_REGEXP, Float, lambda {|s = nil| s && s.to_f}, true, false))
|
20
20
|
define_parameter_type(ParameterType.new('word', WORD_REGEXP, String, lambda {|s = nil| s}, false, false))
|
21
|
-
define_parameter_type(ParameterType.new('string', STRING_REGEXP, String, lambda {|
|
21
|
+
define_parameter_type(ParameterType.new('string', STRING_REGEXP, String, lambda { |s1, s2| arg = s1 != nil ? s1 : s2; arg.gsub(/\\"/, '"').gsub(/\\'/, "'")}, true, false))
|
22
22
|
define_parameter_type(ParameterType.new('', ANONYMOUS_REGEXP, String, lambda {|s = nil| s}, false, true))
|
23
23
|
end
|
24
24
|
|
@@ -8,68 +8,75 @@ module Cucumber
|
|
8
8
|
|
9
9
|
def initialize(regexp)
|
10
10
|
@regexp = regexp.is_a?(Regexp) ? regexp : Regexp.new(regexp)
|
11
|
-
@
|
11
|
+
@group_builder = create_group_builder(@regexp)
|
12
|
+
end
|
13
|
+
|
14
|
+
def match(s)
|
15
|
+
match = @regexp.match(s)
|
16
|
+
return nil if match.nil?
|
17
|
+
group_indices = (0..match.length).to_a.to_enum
|
18
|
+
@group_builder.build(match, group_indices)
|
19
|
+
end
|
20
|
+
|
21
|
+
private def is_non_capturing(source, i)
|
22
|
+
# Regex is valid. Bounds check not required.
|
23
|
+
if source[i+1] != '?'
|
24
|
+
# (X)
|
25
|
+
return false
|
26
|
+
end
|
27
|
+
|
28
|
+
if source[i+2] != '<'
|
29
|
+
# (?:X)
|
30
|
+
# (?idmsuxU-idmsuxU)
|
31
|
+
# (?idmsux-idmsux:X)
|
32
|
+
# (?=X)
|
33
|
+
# (?!X)
|
34
|
+
# (?>X)
|
35
|
+
return true
|
36
|
+
end
|
37
|
+
|
38
|
+
if source[i+3] == '=' || source[i+3] == '!'
|
39
|
+
# (?<=X)
|
40
|
+
# (?<!X)
|
41
|
+
return true
|
42
|
+
end
|
43
|
+
|
44
|
+
# (?<name>X)
|
45
|
+
raise CucumberExpressionError.new("Named capture groups are not supported. See https://github.com/cucumber/cucumber/issues/329")
|
46
|
+
end
|
47
|
+
|
48
|
+
private def create_group_builder(regexp)
|
49
|
+
source = regexp.source
|
50
|
+
stack = [GroupBuilder.new]
|
12
51
|
group_start_stack = []
|
13
|
-
last = nil
|
14
52
|
escaping = false
|
15
|
-
@non_capturing_maybe = false
|
16
|
-
@name_capturing_maybe = false
|
17
53
|
char_class = false
|
18
|
-
|
19
|
-
@regexp.source.each_char.with_index do |c, n|
|
54
|
+
source.each_char.with_index do |c, i|
|
20
55
|
if c == '[' && !escaping
|
21
56
|
char_class = true
|
22
57
|
elsif c == ']' && !escaping
|
23
58
|
char_class = false
|
24
59
|
elsif c == '(' && !escaping && !char_class
|
25
|
-
|
26
|
-
|
27
|
-
|
60
|
+
group_start_stack.push(i)
|
61
|
+
group_builder = GroupBuilder.new
|
62
|
+
non_capturing = is_non_capturing(source, i)
|
63
|
+
if non_capturing
|
64
|
+
group_builder.set_non_capturing!
|
65
|
+
end
|
66
|
+
stack.push(group_builder)
|
28
67
|
elsif c == ')' && !escaping && !char_class
|
29
|
-
gb =
|
68
|
+
gb = stack.pop
|
30
69
|
group_start = group_start_stack.pop
|
31
70
|
if gb.capturing?
|
32
|
-
gb.source =
|
33
|
-
|
71
|
+
gb.source = source[group_start + 1...i]
|
72
|
+
stack.last.add(gb)
|
34
73
|
else
|
35
|
-
gb.move_children_to(
|
74
|
+
gb.move_children_to(stack.last)
|
36
75
|
end
|
37
|
-
end_group
|
38
|
-
elsif c == '?' && last == '('
|
39
|
-
@non_capturing_maybe = true
|
40
|
-
elsif (c == '<') && @non_capturing_maybe
|
41
|
-
@name_capturing_maybe = true
|
42
|
-
elsif (c == ':' || c == '!' || c == '=') && last == '?' && @non_capturing_maybe
|
43
|
-
end_non_capturing_group
|
44
|
-
elsif (c == '=' || c == '!') && last == '<' && @name_capturing_maybe
|
45
|
-
end_non_capturing_group
|
46
|
-
elsif @name_capturing_maybe
|
47
|
-
raise CucumberExpressionError.new("Named capture groups are not supported. See https://github.com/cucumber/cucumber/issues/329")
|
48
76
|
end
|
49
|
-
|
50
77
|
escaping = c == '\\' && !escaping
|
51
|
-
last = c
|
52
78
|
end
|
53
|
-
|
54
|
-
end
|
55
|
-
|
56
|
-
def match(s)
|
57
|
-
match = @regexp.match(s)
|
58
|
-
return nil if match.nil?
|
59
|
-
group_indices = (0..match.length).to_a.to_enum
|
60
|
-
@group_builder.build(match, group_indices)
|
61
|
-
end
|
62
|
-
|
63
|
-
private
|
64
|
-
|
65
|
-
def end_non_capturing_group
|
66
|
-
@stack.last.set_non_capturing!
|
67
|
-
end_group
|
68
|
-
end
|
69
|
-
|
70
|
-
def end_group
|
71
|
-
@non_capturing_maybe = false
|
72
|
-
@name_capturing_maybe = false
|
79
|
+
stack.pop
|
73
80
|
end
|
74
81
|
end
|
75
82
|
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'json'
|
3
|
+
require 'cucumber/cucumber_expressions/cucumber_expression_parser'
|
4
|
+
require 'cucumber/cucumber_expressions/errors'
|
5
|
+
|
6
|
+
module Cucumber
|
7
|
+
module CucumberExpressions
|
8
|
+
describe 'Cucumber expression parser' do
|
9
|
+
Dir['testdata/ast/*.yaml'].each do |testcase|
|
10
|
+
expectation = YAML.load_file(testcase) # encoding?
|
11
|
+
it "#{testcase}" do
|
12
|
+
parser = CucumberExpressionParser.new
|
13
|
+
if expectation['exception'].nil?
|
14
|
+
node = parser.parse(expectation['expression'])
|
15
|
+
node_hash = node.to_hash
|
16
|
+
expect(node_hash).to eq(JSON.parse(expectation['expected']))
|
17
|
+
else
|
18
|
+
expect { parser.parse(expectation['expression']) }.to raise_error(expectation['exception'])
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -1,9 +1,39 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'json'
|
1
3
|
require 'cucumber/cucumber_expressions/cucumber_expression'
|
2
4
|
require 'cucumber/cucumber_expressions/parameter_type_registry'
|
3
5
|
|
4
6
|
module Cucumber
|
5
7
|
module CucumberExpressions
|
6
8
|
describe CucumberExpression do
|
9
|
+
|
10
|
+
Dir['testdata/expression/*.yaml'].each do |testcase|
|
11
|
+
expectation = YAML.load_file(testcase) # encoding?
|
12
|
+
it "#{testcase}" do
|
13
|
+
parameter_registry = ParameterTypeRegistry.new
|
14
|
+
if expectation['exception'].nil?
|
15
|
+
cucumber_expression = CucumberExpression.new(expectation['expression'], parameter_registry)
|
16
|
+
matches = cucumber_expression.match(expectation['text'])
|
17
|
+
values = matches.nil? ? nil : matches.map { |arg| arg.value(nil) }
|
18
|
+
expect(values).to eq(JSON.parse(expectation['expected']))
|
19
|
+
else
|
20
|
+
expect {
|
21
|
+
cucumber_expression = CucumberExpression.new(expectation['expression'], parameter_registry)
|
22
|
+
cucumber_expression.match(expectation['text'])
|
23
|
+
}.to raise_error(expectation['exception'])
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
Dir['testdata/regex/*.yaml'].each do |testcase|
|
29
|
+
expectation = YAML.load_file(testcase) # encoding?
|
30
|
+
it "#{testcase}" do
|
31
|
+
parameter_registry = ParameterTypeRegistry.new
|
32
|
+
cucumber_expression = CucumberExpression.new(expectation['expression'], parameter_registry)
|
33
|
+
expect(cucumber_expression.regexp.source).to eq(expectation['expected'])
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
7
37
|
it "documents match arguments" do
|
8
38
|
parameter_registry = ParameterTypeRegistry.new
|
9
39
|
|
@@ -15,82 +45,6 @@ module Cucumber
|
|
15
45
|
### [capture-match-arguments]
|
16
46
|
end
|
17
47
|
|
18
|
-
it "matches word" do
|
19
|
-
expect(match("three {word} mice", "three blind mice")).to eq(['blind'])
|
20
|
-
end
|
21
|
-
|
22
|
-
it('matches double quoted string') do
|
23
|
-
expect(match('three {string} mice', 'three "blind" mice')).to eq(['blind'])
|
24
|
-
end
|
25
|
-
|
26
|
-
it('matches multiple double quoted strings') do
|
27
|
-
expect(match('three {string} and {string} mice', 'three "blind" and "crippled" mice')).to eq(['blind', 'crippled'])
|
28
|
-
end
|
29
|
-
|
30
|
-
it('matches single quoted string') do
|
31
|
-
expect(match('three {string} mice', "three 'blind' mice")).to eq(['blind'])
|
32
|
-
end
|
33
|
-
|
34
|
-
it('matches multiple single quoted strings') do
|
35
|
-
expect(match('three {string} and {string} mice', "three 'blind' and 'crippled' mice")).to eq(['blind', 'crippled'])
|
36
|
-
end
|
37
|
-
|
38
|
-
it('does not match misquoted string') do
|
39
|
-
expect(match('three {string} mice', 'three "blind\' mice')).to eq(nil)
|
40
|
-
end
|
41
|
-
|
42
|
-
it('matches single quoted string with double quotes') do
|
43
|
-
expect(match('three {string} mice', 'three \'"blind"\' mice')).to eq(['"blind"'])
|
44
|
-
end
|
45
|
-
|
46
|
-
it('matches double quoted string with single quotes') do
|
47
|
-
expect(match('three {string} mice', 'three "\'blind\'" mice')).to eq(["'blind'"])
|
48
|
-
end
|
49
|
-
|
50
|
-
it('matches double quoted string with escaped double quote') do
|
51
|
-
expect(match('three {string} mice', 'three "bl\\"nd" mice')).to eq(['bl"nd'])
|
52
|
-
end
|
53
|
-
|
54
|
-
it('matches single quoted string with escaped single quote') do
|
55
|
-
expect(match('three {string} mice', "three 'bl\\'nd' mice")).to eq(["bl'nd"])
|
56
|
-
end
|
57
|
-
|
58
|
-
it('matches single quoted empty string as empty string') do
|
59
|
-
expect(match('three {string} mice', "three '' mice")).to eq([''])
|
60
|
-
end
|
61
|
-
|
62
|
-
it('matches double quoted empty string as empty string') do
|
63
|
-
expect(match('three {string} mice', 'three "" mice')).to eq([''])
|
64
|
-
end
|
65
|
-
|
66
|
-
it('matches single quoted empty string as empty string, along with other strings') do
|
67
|
-
expect(match('three {string} and {string} mice', "three '' and 'handsome' mice")).to eq(['', 'handsome'])
|
68
|
-
end
|
69
|
-
|
70
|
-
it('matches double quoted empty string as empty string, along with other strings') do
|
71
|
-
expect(match('three {string} and {string} mice', 'three "" and "handsome" mice')).to eq(['', 'handsome'])
|
72
|
-
end
|
73
|
-
|
74
|
-
it 'matches escaped parentheses' do
|
75
|
-
expect(match('three \\(exceptionally) {string} mice', 'three (exceptionally) "blind" mice')).to eq(['blind'])
|
76
|
-
end
|
77
|
-
|
78
|
-
it "matches escaped slash" do
|
79
|
-
expect(match("12\\/2020", "12/2020")).to eq([])
|
80
|
-
end
|
81
|
-
|
82
|
-
it "matches int" do
|
83
|
-
expect(match("{int}", "22")).to eq([22])
|
84
|
-
end
|
85
|
-
|
86
|
-
it "doesn't match float as int" do
|
87
|
-
expect(match("{int}", "1.22")).to be_nil
|
88
|
-
end
|
89
|
-
|
90
|
-
it "matches int as float" do
|
91
|
-
expect(match("{float}", "0")).to eq([0.0])
|
92
|
-
end
|
93
|
-
|
94
48
|
it "matches float" do
|
95
49
|
expect(match("{float}", "")).to eq(nil)
|
96
50
|
expect(match("{float}", ".")).to eq(nil)
|
@@ -126,58 +80,64 @@ module Cucumber
|
|
126
80
|
expect(match("{float}", "-.1E2")).to eq([-10])
|
127
81
|
end
|
128
82
|
|
129
|
-
it "
|
130
|
-
expect(match("{}", "0
|
131
|
-
end
|
132
|
-
|
133
|
-
'[]()$.|?*+'.split('').each do |char|
|
134
|
-
it "does not allow parameter type with #{char}" do
|
135
|
-
expect {match("{#{char}string}", "something")}.to raise_error("Illegal character '#{char}' in parameter name {#{char}string}")
|
136
|
-
end
|
83
|
+
it "float with zero" do
|
84
|
+
expect(match("{float}", "0")).to eq([0.0])
|
137
85
|
end
|
138
86
|
|
139
|
-
it "
|
140
|
-
expect
|
87
|
+
it "matches anonymous" do
|
88
|
+
expect(match("{}", "0.22")).to eq(["0.22"])
|
141
89
|
end
|
142
90
|
|
143
|
-
it "
|
144
|
-
|
91
|
+
it "exposes source" do
|
92
|
+
expr = "I have {int} cuke(s)"
|
93
|
+
expect(CucumberExpression.new(expr, ParameterTypeRegistry.new).source).to eq(expr)
|
145
94
|
end
|
146
95
|
|
147
|
-
it "
|
148
|
-
|
149
|
-
|
96
|
+
it "unmatched optional groups have undefined values" do
|
97
|
+
parameter_type_registry = ParameterTypeRegistry.new
|
98
|
+
parameter_type_registry.define_parameter_type(
|
99
|
+
ParameterType.new(
|
100
|
+
'textAndOrNumber',
|
101
|
+
/([A-Z]+)?(?: )?([0-9]+)?/,
|
102
|
+
Object,
|
103
|
+
-> (s1, s2) {
|
104
|
+
[s1, s2]
|
105
|
+
},
|
106
|
+
false,
|
107
|
+
true
|
108
|
+
)
|
109
|
+
)
|
110
|
+
expression = CucumberExpression.new(
|
111
|
+
'{textAndOrNumber}',
|
112
|
+
parameter_type_registry
|
113
|
+
)
|
150
114
|
|
151
|
-
|
152
|
-
|
153
|
-
end
|
115
|
+
class World
|
116
|
+
end
|
154
117
|
|
155
|
-
|
156
|
-
expect
|
118
|
+
expect(expression.match("TLA")[0].value(World.new)).to eq(["TLA", nil])
|
119
|
+
expect(expression.match("123")[0].value(World.new)).to eq([nil, "123"])
|
157
120
|
end
|
158
121
|
|
159
|
-
|
160
|
-
expr = "I have {int} cuke(s)"
|
161
|
-
expect(CucumberExpression.new(expr, ParameterTypeRegistry.new).source).to eq(expr)
|
162
|
-
end
|
122
|
+
# Ruby specific
|
163
123
|
|
164
124
|
it "delegates transform to custom object" do
|
165
125
|
parameter_type_registry = ParameterTypeRegistry.new
|
166
126
|
parameter_type_registry.define_parameter_type(
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
127
|
+
ParameterType.new(
|
128
|
+
'widget',
|
129
|
+
/\w+/,
|
130
|
+
Object,
|
131
|
+
-> (s) {
|
132
|
+
self.create_widget(s)
|
133
|
+
},
|
134
|
+
false,
|
135
|
+
true
|
136
|
+
)
|
177
137
|
)
|
178
138
|
expression = CucumberExpression.new(
|
179
|
-
|
180
|
-
|
139
|
+
'I have a {widget}',
|
140
|
+
parameter_type_registry
|
181
141
|
)
|
182
142
|
|
183
143
|
class World
|
@@ -190,22 +150,11 @@ module Cucumber
|
|
190
150
|
expect(args[0].value(World.new)).to eq('widget:bolt')
|
191
151
|
end
|
192
152
|
|
193
|
-
describe "escapes special characters" do
|
194
|
-
%w(\\ [ ] ^ $ . | ? * +).each do |character|
|
195
|
-
it "escapes #{character}" do
|
196
|
-
expr = "I have {int} cuke(s) and #{character}"
|
197
|
-
expression = CucumberExpression.new(expr, ParameterTypeRegistry.new)
|
198
|
-
arg1 = expression.match("I have 800 cukes and #{character}")[0]
|
199
|
-
expect(arg1.value(nil)).to eq(800)
|
200
|
-
end
|
201
|
-
end
|
202
|
-
end
|
203
|
-
|
204
153
|
def match(expression, text)
|
205
154
|
cucumber_expression = CucumberExpression.new(expression, ParameterTypeRegistry.new)
|
206
155
|
args = cucumber_expression.match(text)
|
207
156
|
return nil if args.nil?
|
208
|
-
args.map {|arg| arg.value(nil)}
|
157
|
+
args.map { |arg| arg.value(nil) }
|
209
158
|
end
|
210
159
|
end
|
211
160
|
end
|