cucumber-cucumber-expressions 17.1.0 → 18.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,216 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'cucumber/cucumber_expressions/cucumber_expression'
4
- require 'cucumber/cucumber_expressions/regular_expression'
5
- require 'cucumber/cucumber_expressions/parameter_type_registry'
6
-
7
- module Cucumber
8
- module CucumberExpressions
9
- class Color
10
- attr_reader :name
11
-
12
- def initialize(name)
13
- @name = name
14
- end
15
-
16
- def ==(other)
17
- other.is_a?(Color) && other.name == name
18
- end
19
- end
20
-
21
- class CssColor
22
- attr_reader :name
23
-
24
- def initialize(name)
25
- @name = name
26
- end
27
-
28
- def ==(other)
29
- other.is_a?(CssColor) && other.name == name
30
- end
31
- end
32
-
33
- class Coordinate
34
- attr_reader :x, :y, :z
35
-
36
- def initialize(x, y, z)
37
- @x, @y, @z = x, y, z
38
- end
39
-
40
- def ==(other)
41
- other.is_a?(Coordinate) && other.x == x && other.y == y && other.z == z
42
- end
43
- end
44
-
45
- describe 'Custom parameter type' do
46
- before do
47
- parameter_type_registry = ParameterTypeRegistry.new
48
- parameter_type_registry.define_parameter_type(
49
- ParameterType.new(
50
- 'color', # name
51
- /red|blue|yellow/, # regexp
52
- Color, # type
53
- ->(s) { Color.new(s) }, # transform
54
- true, # use_for_snippets
55
- false # prefer_for_regexp_match
56
- )
57
- )
58
- @parameter_type_registry = parameter_type_registry
59
- end
60
-
61
- it 'throws exception for illegal character in parameter name' do
62
- expect do
63
- ParameterType.new(
64
- '[string]',
65
- /.*/,
66
- String,
67
- ->(s) { s },
68
- true,
69
- false
70
- )
71
- end.to raise_error("Illegal character in parameter name {[string]}. Parameter names may not contain '[]()$.|?*+'")
72
- end
73
-
74
- describe CucumberExpression do
75
- it 'matches parameters with custom parameter type' do
76
- expression = described_class.new('I have a {color} ball', @parameter_type_registry)
77
- transformed_argument_value = expression.match('I have a red ball')[0].value(nil)
78
-
79
- expect(transformed_argument_value).to eq(Color.new('red'))
80
- end
81
-
82
- it 'matches parameters with multiple capture groups' do
83
- @parameter_type_registry.define_parameter_type(
84
- ParameterType.new(
85
- 'coordinate',
86
- /(\d+),\s*(\d+),\s*(\d+)/,
87
- Coordinate,
88
- ->(x, y, z) { Coordinate.new(x.to_i, y.to_i, z.to_i) },
89
- true,
90
- false
91
- )
92
- )
93
-
94
- expression = described_class.new('A {int} thick line from {coordinate} to {coordinate}', @parameter_type_registry)
95
- args = expression.match('A 5 thick line from 10,20,30 to 40,50,60')
96
-
97
- thick = args[0].value(nil)
98
- expect(thick).to eq(5)
99
-
100
- from = args[1].value(nil)
101
- expect(from).to eq(Coordinate.new(10, 20, 30))
102
-
103
- to = args[2].value(nil)
104
- expect(to).to eq(Coordinate.new(40, 50, 60))
105
- end
106
-
107
- it 'matches parameters with custom parameter type using optional capture group' do
108
- parameter_type_registry = ParameterTypeRegistry.new
109
- parameter_type_registry.define_parameter_type(
110
- ParameterType.new(
111
- 'color',
112
- [/red|blue|yellow/, /(?:dark|light) (?:red|blue|yellow)/],
113
- Color,
114
- ->(s) { Color.new(s) },
115
- true,
116
- false
117
- )
118
- )
119
- expression = described_class.new('I have a {color} ball', parameter_type_registry)
120
- transformed_argument_value = expression.match('I have a dark red ball')[0].value(nil)
121
-
122
- expect(transformed_argument_value).to eq(Color.new('dark red'))
123
- end
124
-
125
- it 'defers transformation until queried from argument' do
126
- @parameter_type_registry.define_parameter_type(
127
- ParameterType.new(
128
- 'throwing',
129
- /bad/,
130
- CssColor,
131
- ->(s) { raise "Can't transform [#{s}]" },
132
- true,
133
- false
134
- )
135
- )
136
- expression = described_class.new('I have a {throwing} parameter', @parameter_type_registry)
137
- args = expression.match('I have a bad parameter')
138
-
139
- expect { args[0].value(nil) }.to raise_error("Can't transform [bad]")
140
- end
141
-
142
- describe 'conflicting parameter type' do
143
- it 'is detected for type name' do
144
- expect {
145
- @parameter_type_registry.define_parameter_type(
146
- ParameterType.new(
147
- 'color',
148
- /.*/,
149
- CssColor,
150
- ->(s) { CssColor.new(s) },
151
- true,
152
- false
153
- )
154
- )
155
- }.to raise_error('There is already a parameter with name color')
156
- end
157
-
158
- it 'is not detected for type' do
159
- @parameter_type_registry.define_parameter_type(
160
- ParameterType.new(
161
- 'whatever',
162
- /.*/,
163
- Color,
164
- ->(s) { Color.new(s) },
165
- false,
166
- false
167
- )
168
- )
169
- end
170
-
171
- it 'is not detected for regexp' do
172
- @parameter_type_registry.define_parameter_type(
173
- ParameterType.new(
174
- 'css-color',
175
- /red|blue|yellow/,
176
- CssColor,
177
- ->(s) { CssColor.new(s) },
178
- true,
179
- false
180
- )
181
- )
182
- css_color = described_class.new('I have a {css-color} ball', @parameter_type_registry)
183
- css_color_value = css_color.match('I have a blue ball')[0].value(nil)
184
-
185
- expect(css_color_value).to eq(CssColor.new('blue'))
186
-
187
- color = described_class.new('I have a {color} ball', @parameter_type_registry)
188
- color_value = color.match('I have a blue ball')[0].value(nil)
189
-
190
- expect(color_value).to eq(Color.new('blue'))
191
- end
192
- end
193
- end
194
-
195
- describe RegularExpression do
196
- it 'matches arguments with custom parameter types without a name' do
197
- parameter_type_registry = ParameterTypeRegistry.new
198
- parameter_type_registry.define_parameter_type(
199
- ParameterType.new(
200
- nil,
201
- /red|blue|yellow/,
202
- Color,
203
- ->(s) { Color.new(s) },
204
- true,
205
- false
206
- )
207
- )
208
- expression = described_class.new(/I have a (red|blue|yellow) ball/, parameter_type_registry)
209
- value = expression.match('I have a red ball')[0].value(nil)
210
-
211
- expect(value).to eq(Color.new('red'))
212
- end
213
- end
214
- end
215
- end
216
- end
@@ -1,21 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'cucumber/cucumber_expressions/expression_factory'
4
-
5
- module Cucumber
6
- module CucumberExpressions
7
- describe ExpressionFactory do
8
- before do
9
- @expression_factory = described_class.new(ParameterTypeRegistry.new)
10
- end
11
-
12
- it 'creates a RegularExpression' do
13
- expect(@expression_factory.create_expression(/x/).class).to eq(RegularExpression)
14
- end
15
-
16
- it 'creates a CucumberExpression' do
17
- expect(@expression_factory.create_expression('{int}').class).to eq(CucumberExpression)
18
- end
19
- end
20
- end
21
- end
@@ -1,81 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'cucumber/cucumber_expressions/parameter_type_registry'
4
- require 'cucumber/cucumber_expressions/parameter_type'
5
- require 'cucumber/cucumber_expressions/errors'
6
-
7
- module Cucumber
8
- module CucumberExpressions
9
- CAPITALISED_WORD = /[A-Z]+\w+/.freeze
10
-
11
- class Name; end
12
- class Person; end
13
- class Place; end
14
-
15
- describe ParameterTypeRegistry do
16
- before do
17
- @registry = described_class.new
18
- end
19
-
20
- it 'does not allow more than one prefer_for_regexp_match parameter type for each regexp' do
21
- @registry.define_parameter_type(ParameterType.new('name', CAPITALISED_WORD, Name, ->(_) { Name.new }, true, true))
22
- @registry.define_parameter_type(ParameterType.new('person', CAPITALISED_WORD, Person, ->(_) { Person.new }, true, false))
23
- expect do
24
- @registry.define_parameter_type(ParameterType.new('place', CAPITALISED_WORD, Place, ->(_) { Place.new }, true, true))
25
- end.to raise_error(
26
- CucumberExpressionError,
27
- 'There can only be one preferential parameter type per regexp. The regexp /[A-Z]+\\w+/ is used for two: {name} and {place}'
28
- )
29
- end
30
-
31
- it 'looks up prefer_for_regexp_match parameter type by regexp' do
32
- name = ParameterType.new('name', CAPITALISED_WORD, Name, ->(_) { Name.new }, true, false)
33
- person = ParameterType.new('person', CAPITALISED_WORD, Person, ->(_) { Person.new }, true, true)
34
- place = ParameterType.new('place', CAPITALISED_WORD, Place, ->(_) { Place.new }, true, false)
35
-
36
- @registry.define_parameter_type(name)
37
- @registry.define_parameter_type(person)
38
- @registry.define_parameter_type(place)
39
-
40
- expect(@registry.lookup_by_regexp(CAPITALISED_WORD.source, /([A-Z]+\w+) and ([A-Z]+\w+)/, 'Lisa and Bob')).to eq(person)
41
- end
42
-
43
- it 'throws ambiguous exception when no parameter types are prefer_for_regexp_match' do
44
- name = ParameterType.new('name', CAPITALISED_WORD, Name, ->(_) { Name.new }, true, false)
45
- person = ParameterType.new('person', CAPITALISED_WORD, Person, ->(_) { Person.new }, true, false)
46
- place = ParameterType.new('place', CAPITALISED_WORD, Place, ->(_) { Place.new }, true, false)
47
-
48
- @registry.define_parameter_type(name)
49
- @registry.define_parameter_type(person)
50
- @registry.define_parameter_type(place)
51
-
52
- expect do
53
- expect(@registry.lookup_by_regexp(CAPITALISED_WORD.source, /([A-Z]+\w+) and ([A-Z]+\w+)/, 'Lisa and Bob')).to eq(person)
54
- end.to raise_error(
55
- CucumberExpressionError,
56
- "Your Regular Expression /([A-Z]+\\w+) and ([A-Z]+\\w+)/\n" \
57
- "matches multiple parameter types with regexp /[A-Z]+\\w+/:\n " \
58
- "{name}\n " \
59
- "{person}\n " \
60
- "{place}\n" \
61
- "\n" \
62
- "I couldn't decide which one to use. You have two options:\n" \
63
- "\n" \
64
- "1) Use a Cucumber Expression instead of a Regular Expression. Try one of these:\n " \
65
- "{name} and {name}\n " \
66
- "{name} and {person}\n " \
67
- "{name} and {place}\n " \
68
- "{person} and {name}\n " \
69
- "{person} and {person}\n " \
70
- "{person} and {place}\n " \
71
- "{place} and {name}\n " \
72
- "{place} and {person}\n " \
73
- "{place} and {place}\n" \
74
- "\n" \
75
- "2) Make one of the parameter types preferential and continue to use a Regular Expression.\n" \
76
- "\n"
77
- )
78
- end
79
- end
80
- end
81
- end
@@ -1,18 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'cucumber/cucumber_expressions/parameter_type'
4
-
5
- module Cucumber
6
- module CucumberExpressions
7
- describe ParameterType do
8
- it 'does not allow ignore flag on regexp' do
9
- expect do
10
- described_class.new('case-insensitive', /[a-z]+/i, String, ->(s) { s }, true, true)
11
- end.to raise_error(
12
- CucumberExpressionError,
13
- "ParameterType Regexps can't use option Regexp::IGNORECASE"
14
- )
15
- end
16
- end
17
- end
18
- end
@@ -1,93 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'yaml'
4
- require 'cucumber/cucumber_expressions/regular_expression'
5
- require 'cucumber/cucumber_expressions/parameter_type_registry'
6
-
7
- module Cucumber
8
- module CucumberExpressions
9
- describe RegularExpression do
10
- Dir['../testdata/regular-expression/matching/*.yaml'].each do |path|
11
- expectation = YAML.load_file(path)
12
-
13
- it "matches #{path}" do
14
- parameter_registry = ParameterTypeRegistry.new
15
- expression = described_class.new(Regexp.new(expectation['expression']), parameter_registry)
16
- matches = expression.match(expectation['text'])
17
- values = matches.map { |arg| arg.value(nil) }
18
-
19
- expect(values).to eq(expectation['expected_args'])
20
- end
21
- end
22
-
23
- it 'does not transform by default' do
24
- expect(match(/(\d\d)/, '22')).to eq(['22'])
25
- end
26
-
27
- it 'does not transform anonymous' do
28
- expect(match(/(.*)/, '22')).to eq(['22'])
29
- end
30
-
31
- it 'transforms negative int' do
32
- expect(match(/(-?\d+)/, '-22')).to eq([-22])
33
- end
34
-
35
- it 'transforms positive int' do
36
- expect(match(/(\d+)/, '22')).to eq([22])
37
- end
38
-
39
- it 'returns nil when there is no match' do
40
- expect(match(/hello/, 'world')).to be_nil
41
- end
42
-
43
- it 'matches empty string when there is an empty string match' do
44
- expect(match(/^The value equals "([^"]*)"$/, 'The value equals ""')).to eq([''])
45
- end
46
-
47
- it 'matches nested capture group without match' do
48
- expect(match(/^a user( named "([^"]*)")?$/, 'a user')).to eq([nil])
49
- end
50
-
51
- it 'matches nested capture group with match' do
52
- expect(match(/^a user( named "([^"]*)")?$/, 'a user named "Charlie"')).to eq(['Charlie'])
53
- end
54
-
55
- it 'ignores non capturing groups' do
56
- expect(
57
- match(
58
- /(\S+) ?(can|cannot) (?:delete|cancel) the (\d+)(?:st|nd|rd|th) (attachment|slide) ?(?:upload)?/,
59
- 'I can cancel the 1st slide upload'
60
- )
61
- ).to eq(['I', 'can', 1, 'slide'])
62
- end
63
-
64
- it 'matches capture group nested in optional one' do
65
- regexp = /^a (pre-commercial transaction |pre buyer fee model )?purchase(?: for \$(\d+))?$/
66
-
67
- expect(match(regexp, 'a purchase')).to eq([nil, nil])
68
- expect(match(regexp, 'a purchase for $33')).to eq([nil, 33])
69
- expect(match(regexp, 'a pre buyer fee model purchase')).to eq(['pre buyer fee model ', nil])
70
- end
71
-
72
- it 'works with escaped parentheses' do
73
- expect(match(/Across the line\(s\)/, 'Across the line(s)')).to eq([])
74
- end
75
-
76
- it 'exposes source and regexp' do
77
- regexp = /I have (\d+) cukes? in my (\+) now/
78
- expression = described_class.new(regexp, ParameterTypeRegistry.new)
79
-
80
- expect(expression.regexp).to eq(regexp)
81
- expect(expression.source).to eq(regexp.source)
82
- end
83
-
84
- def match(expression, text)
85
- regular_expression = RegularExpression.new(expression, ParameterTypeRegistry.new)
86
- arguments = regular_expression.match(text)
87
- return nil if arguments.nil?
88
-
89
- arguments.map { |arg| arg.value(nil) }
90
- end
91
- end
92
- end
93
- end
@@ -1,186 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'cucumber/cucumber_expressions/tree_regexp'
4
-
5
- module Cucumber
6
- module CucumberExpressions
7
- describe TreeRegexp do
8
- it 'exposes the group source' do
9
- tr = described_class.new(/(a(?:b)?)(c)/)
10
- expect(tr.group_builder.children.map { |gb| gb.source }).to eq(['a(?:b)?', 'c'])
11
- end
12
-
13
- it 'builds a tree' do
14
- tr = described_class.new(/(a(?:b)?)(c)/)
15
- group = tr.match('ac')
16
- expect(group.value).to eq('ac')
17
- expect(group.children[0].value).to eq('a')
18
- expect(group.children[0].children).to eq([])
19
- expect(group.children[1].value).to eq('c')
20
- end
21
-
22
- it 'ignores `?:` as a non-capturing group' do
23
- tr = described_class.new(/a(?:b)(c)/)
24
- group = tr.match('abc')
25
- expect(group.value).to eq('abc')
26
- expect(group.children.length).to eq 1
27
- expect(group.children[0].value).to eq('c')
28
- end
29
-
30
- it 'ignores `?!` as a non-capturing group' do
31
- tr = described_class.new(/a(?!b)(.+)/)
32
- group = tr.match('aBc')
33
- expect(group.value).to eq('aBc')
34
- expect(group.children.length).to eq 1
35
- end
36
-
37
- it 'ignores `?=` as a non-capturing group' do
38
- tr = described_class.new(/a(?=b)(.+)$/)
39
- group = tr.match('abc')
40
- expect(group.value).to eq('abc')
41
- expect(group.children[0].value).to eq('bc')
42
- expect(group.children.length).to eq 1
43
- end
44
-
45
- it 'ignores `?<=` as a non-capturing group' do
46
- tr = described_class.new(/a(.+)(?<=c)$/)
47
- group = tr.match('abc')
48
- expect(group.value).to eq('abc')
49
- expect(group.children[0].value).to eq('bc')
50
- expect(group.children.length).to eq 1
51
- end
52
-
53
- it 'ignores `?<!` as a non-capturing group' do
54
- tr = described_class.new(/a(.+)(?<!b)$/)
55
- group = tr.match('abc')
56
- expect(group.value).to eq('abc')
57
- expect(group.children[0].value).to eq('bc')
58
- expect(group.children.length).to eq 1
59
- end
60
-
61
- it 'ignores `?<!` as a non-capturing group' do
62
- tr = described_class.new(/a(.+)(?<!b)$/)
63
- group = tr.match('abc')
64
- expect(group.value).to eq('abc')
65
- expect(group.children[0].value).to eq('bc')
66
- expect(group.children.length).to eq 1
67
- end
68
-
69
- it 'ignores `?>` as a non-capturing group' do
70
- tr = described_class.new(/a(?>b)c/)
71
- group = tr.match('abc')
72
- expect(group.value).to eq('abc')
73
- expect(group.children.length).to eq 0
74
- end
75
-
76
- it 'throws an error when there are named capture groups because they are buggy in Ruby' do
77
- # https://github.com/cucumber/cucumber/issues/329
78
- expect { described_class.new(/^I am a person( named "(?<first_name>.+) (?<last_name>.+)")?$/) }
79
- .to raise_error(/Named capture groups are not supported/)
80
- end
81
-
82
- it 'matches an optional group' do
83
- tr = described_class.new(/^Something( with an optional argument)?/)
84
- group = tr.match('Something')
85
- expect(group.children[0].value).to be_nil
86
- end
87
-
88
- it 'matches nested groups' do
89
- tr = described_class.new(/^A (\d+) thick line from ((\d+),\s*(\d+),\s*(\d+)) to ((\d+),\s*(\d+),\s*(\d+))/)
90
- group = tr.match('A 5 thick line from 10,20,30 to 40,50,60')
91
-
92
- expect(group.children[0].value).to eq('5')
93
- expect(group.children[1].value).to eq('10,20,30')
94
- expect(group.children[1].children[0].value).to eq('10')
95
- expect(group.children[1].children[1].value).to eq('20')
96
- expect(group.children[1].children[2].value).to eq('30')
97
- expect(group.children[2].value).to eq('40,50,60')
98
- expect(group.children[2].children[0].value).to eq('40')
99
- expect(group.children[2].children[1].value).to eq('50')
100
- expect(group.children[2].children[2].value).to eq('60')
101
- end
102
-
103
- it 'detects multiple non-capturing groups' do
104
- tr = described_class.new(/(?:a)(:b)(\?c)(d)/)
105
- group = tr.match('a:b?cd')
106
- expect(group.children.length).to eq(3)
107
- end
108
-
109
- it 'works with escaped backslashes' do
110
- tr = described_class.new(/foo\\(bar|baz)/)
111
- group = tr.match('foo\\bar')
112
- expect(group.children.length).to eq(1)
113
- end
114
-
115
- it 'works with escaped slashes' do
116
- tr = described_class.new(/^I go to '\/(.+)'$/)
117
- group = tr.match("I go to '/hello'")
118
- expect(group.children.length).to eq(1)
119
- end
120
-
121
- it 'works with digit and word regexp metacharacters' do
122
- tr = described_class.new(/^(\d) (\w+)$/)
123
- group = tr.match('2 you')
124
- expect(group.children.length).to eq(2)
125
- end
126
-
127
- it 'captures non-capturing groups with capturing groups inside' do
128
- tr = described_class.new(/the stdout(?: from "(.*?)")?/)
129
- group = tr.match('the stdout')
130
- expect(group.value).to eq('the stdout')
131
- expect(group.children[0].value).to eq(nil)
132
- expect(group.children.length).to eq(1)
133
- end
134
-
135
- it 'works with flags' do
136
- tr = described_class.new(/HELLO/i)
137
- group = tr.match('hello')
138
- expect(group.value).to eq('hello')
139
- end
140
-
141
- it('does not consider parentheses in regexp character classes as a group') do
142
- tr = described_class.new(/^drawings: ([A-Z_, ()]+)$/)
143
- group = tr.match('drawings: ONE, TWO(ABC)')
144
- expect(group.value).to eq('drawings: ONE, TWO(ABC)')
145
- expect(group.children[0].value).to eq('ONE, TWO(ABC)')
146
- expect(group.children.length).to eq(1)
147
- end
148
-
149
- it 'works with inline flags' do
150
- tr = described_class.new(/(?i)HELLO/)
151
- group = tr.match('hello')
152
- expect(group.value).to eq('hello')
153
- expect(group.children.length).to eq 0
154
- end
155
-
156
- it 'works with non-capturing inline flags' do
157
- tr = described_class.new(/(?i:HELLO)/)
158
- group = tr.match('hello')
159
- expect(group.value).to eq('hello')
160
- expect(group.children.length).to eq 0
161
- end
162
-
163
- it 'works with empty capturing groups' do
164
- tr = described_class.new(/()/)
165
- group = tr.match('')
166
- expect(group.value).to eq('')
167
- expect(group.children[0].value).to eq('')
168
- expect(group.children.length).to eq 1
169
- end
170
-
171
- it 'works with empty non-capturing groups' do
172
- tr = described_class.new(/(?:)/)
173
- group = tr.match('')
174
- expect(group.value).to eq('')
175
- expect(group.children.length).to eq 0
176
- end
177
-
178
- it 'works with empty non-look ahead groups' do
179
- tr = described_class.new(/(?<=)/)
180
- group = tr.match('')
181
- expect(group.value).to eq('')
182
- expect(group.children.length).to eq 0
183
- end
184
- end
185
- end
186
- end