cucumber-cucumber-expressions 17.0.2 → 18.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,38 +0,0 @@
1
- # -*- encoding: utf-8 -*-
2
- # frozen_string_literal: true
3
-
4
- version = File.read(File.expand_path('VERSION', __dir__)).strip
5
-
6
- Gem::Specification.new do |s|
7
- s.name = 'cucumber-cucumber-expressions'
8
- s.version = version
9
- s.authors = ['Aslak Hellesøy']
10
- s.description = 'Cucumber Expressions - a simpler alternative to Regular Expressions'
11
- s.summary = "cucumber-expressions-#{s.version}"
12
- s.email = 'cukes@googlegroups.com'
13
- s.homepage = 'https://github.com/cucumber/cucumber-expressions'
14
- s.platform = Gem::Platform::RUBY
15
- s.license = 'MIT'
16
- s.required_ruby_version = '>= 2.5'
17
-
18
- s.metadata = {
19
- 'bug_tracker_uri' => 'https://github.com/cucumber/cucumber/issues',
20
- 'changelog_uri' => 'https://github.com/cucumber/common/blob/main/cucumber-expressions/CHANGELOG.md',
21
- 'documentation_uri' => 'https://cucumber.io/docs/cucumber/cucumber-expressions/',
22
- 'mailing_list_uri' => 'https://groups.google.com/forum/#!forum/cukes',
23
- 'source_code_uri' => 'https://github.com/cucumber/common/blob/main/cucumber-expressions/ruby',
24
- }
25
-
26
- s.add_runtime_dependency 'bigdecimal'
27
-
28
- s.add_development_dependency 'rake', '~> 13.0', '>= 13.0.6'
29
- s.add_development_dependency 'rspec', '~> 3.11', '>= 3.11.0'
30
- s.add_development_dependency 'rubocop', '~> 1.27.0'
31
- s.add_development_dependency 'rubocop-performance', '~> 1.7.0'
32
- s.add_development_dependency 'rubocop-rake', '~> 0.5.0'
33
- s.add_development_dependency 'rubocop-rspec', '~> 2.0.0'
34
-
35
- s.files = `git ls-files`.split("\n").reject { |path| path =~ /\.gitignore$/ }
36
- s.rdoc_options = ['--charset=UTF-8']
37
- s.require_path = 'lib'
38
- end
@@ -1,19 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'cucumber/cucumber_expressions/argument'
4
- require 'cucumber/cucumber_expressions/tree_regexp'
5
- require 'cucumber/cucumber_expressions/parameter_type_registry'
6
-
7
- module Cucumber
8
- module CucumberExpressions
9
- describe Argument do
10
- it 'exposes parameter_type' do
11
- tree_regexp = TreeRegexp.new(/three (.*) mice/)
12
- parameter_type_registry = ParameterTypeRegistry.new
13
- arguments = described_class.build(tree_regexp, 'three blind mice', [parameter_type_registry.lookup_by_type_name('string')])
14
- argument = arguments[0]
15
- expect(argument.parameter_type.name).to eq('string')
16
- end
17
- end
18
- end
19
- end
@@ -1,41 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'cucumber/cucumber_expressions/parameter_type'
4
- require 'cucumber/cucumber_expressions/combinatorial_generated_expression_factory'
5
-
6
- describe Cucumber::CucumberExpressions::CombinatorialGeneratedExpressionFactory do
7
- let(:klazz) { Class.new }
8
- let(:color_parameter_type) do
9
- Cucumber::CucumberExpressions::ParameterType.new('color', /red|blue|yellow/, klazz, ->(_) { klazz.new }, true, false)
10
- end
11
- let(:css_color_parameter_type) do
12
- Cucumber::CucumberExpressions::ParameterType.new('csscolor', /red|blue|yellow/, klazz, ->(_) { klazz.new }, true, false)
13
- end
14
- let(:date_parameter_type) do
15
- Cucumber::CucumberExpressions::ParameterType.new('date', /\d{4}-\d{2}-\d{2}/, klazz, ->(_) { klazz.new }, true, false)
16
- end
17
- let(:date_time_parameter_type) do
18
- Cucumber::CucumberExpressions::ParameterType.new('datetime', /\d{4}-\d{2}-\d{2}/, klazz, ->(_) { klazz.new }, true, false)
19
- end
20
- let(:timestamp_parameter_type) do
21
- Cucumber::CucumberExpressions::ParameterType.new('timestamp', /\d{4}-\d{2}-\d{2}/, klazz, ->(_) { klazz.new }, true, false)
22
- end
23
-
24
- it 'generates multiple expressions' do
25
- parameter_type_combinations = [
26
- [color_parameter_type, css_color_parameter_type],
27
- [date_parameter_type, date_time_parameter_type, timestamp_parameter_type]
28
- ]
29
-
30
- factory = described_class.new('I bought a {%s} ball on {%s}', parameter_type_combinations)
31
- expressions = factory.generate_expressions.map { |generated_expression| generated_expression.source }
32
- expect(expressions).to eq([
33
- 'I bought a {color} ball on {date}',
34
- 'I bought a {color} ball on {datetime}',
35
- 'I bought a {color} ball on {timestamp}',
36
- 'I bought a {csscolor} ball on {date}',
37
- 'I bought a {csscolor} ball on {datetime}',
38
- 'I bought a {csscolor} ball on {timestamp}',
39
- ])
40
- end
41
- end
@@ -1,234 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'cucumber/cucumber_expressions/cucumber_expression_generator'
4
- require 'cucumber/cucumber_expressions/cucumber_expression'
5
- require 'cucumber/cucumber_expressions/parameter_type'
6
- require 'cucumber/cucumber_expressions/parameter_type_registry'
7
-
8
- module Cucumber
9
- module CucumberExpressions
10
- describe CucumberExpressionGenerator do
11
- class Currency
12
- end
13
-
14
- before do
15
- @parameter_type_registry = ParameterTypeRegistry.new
16
- @generator = described_class.new(@parameter_type_registry)
17
- end
18
-
19
- it 'documents expression generation' do
20
- parameter_registry = ParameterTypeRegistry.new
21
- ### [generate-expression]
22
- generator = described_class.new(parameter_registry)
23
- undefined_step_text = 'I have 2 cucumbers and 1.5 tomato'
24
- generated_expression = generator.generate_expressions(undefined_step_text)[0]
25
- expect(generated_expression.source).to eq('I have {int} cucumbers and {float} tomato')
26
- expect(generated_expression.parameter_types[1].type).to eq(Float)
27
- ### [generate-expression]
28
- end
29
-
30
- it 'generates expression for no args' do
31
- assert_expression('hello', [], 'hello')
32
- end
33
-
34
- it 'generates expression with escaped left parenthesis' do
35
- assert_expression('\\(iii)', [], '(iii)')
36
- end
37
-
38
- it 'generates expression with escaped left curly brace' do
39
- assert_expression('\\{iii}', [], '{iii}')
40
- end
41
-
42
- it 'generates expression with escaped slashes' do
43
- assert_expression('The {int}\\/{int}\\/{int} hey', ['int', 'int2', 'int3'], 'The 1814/05/17 hey')
44
- end
45
-
46
- it 'generates expression for int float arg' do
47
- assert_expression('I have {int} cukes and {float} euro', ['int', 'float'], 'I have 2 cukes and 1.5 euro')
48
- end
49
-
50
- it 'generates expression for strings' do
51
- assert_expression('I like {string} and {string}', ['string', 'string2'], 'I like "bangers" and \'mash\'')
52
- end
53
-
54
- it 'generates expression with % sign' do
55
- assert_expression('I am {int}% foobar', ['int'], 'I am 20% foobar')
56
- end
57
-
58
- it 'generates expression for just int' do
59
- assert_expression('{int}', ['int'], '99999')
60
- end
61
-
62
- it 'numbers only second argument when builtin type is not reserved keyword' do
63
- assert_expression('I have {int} cukes and {int} euro', ['int', 'int2'], 'I have 2 cukes and 5 euro')
64
- end
65
-
66
- it 'numbers only second argument when type is not reserved keyword' do
67
- @parameter_type_registry.define_parameter_type(
68
- ParameterType.new(
69
- 'currency',
70
- '[A-Z]{3}',
71
- Currency,
72
- ->(s) { Currency.new(s) },
73
- true,
74
- true
75
- )
76
- )
77
-
78
- assert_expression('I have a {currency} account and a {currency} account', ['currency', 'currency2'], 'I have a EUR account and a GBP account')
79
- end
80
-
81
- it 'exposes parameters in a generated expression' do
82
- expression = @generator.generate_expressions('I have 2 cukes and 1.5 euro')[0]
83
- types = expression.parameter_types.map(&:type)
84
-
85
- expect(types).to eq([Integer, Float])
86
- end
87
-
88
- it 'matches parameter types with optional capture groups' do
89
- @parameter_type_registry.define_parameter_type(
90
- ParameterType.new(
91
- 'optional-flight',
92
- /(1st flight)?/,
93
- String,
94
- ->(s) { s },
95
- true,
96
- false
97
- )
98
- )
99
- @parameter_type_registry.define_parameter_type(
100
- ParameterType.new(
101
- 'optional-hotel',
102
- /(1 hotel)?/,
103
- String,
104
- ->(s) { s },
105
- true,
106
- false
107
- )
108
- )
109
-
110
- expression = @generator.generate_expressions('I reach Stage 4: 1st flight -1 hotel')[0]
111
- # While you would expect this to be `I reach Stage {int}: {optional-flight} -{optional-hotel}`
112
- # the `-1` causes {int} to match just before {optional-hotel}.
113
-
114
- expect(expression.source).to eq('I reach Stage {int}: {optional-flight} {int} hotel')
115
- end
116
-
117
- it 'generates at most 256 expressions' do
118
- for i in 0..3
119
- @parameter_type_registry.define_parameter_type(
120
- ParameterType.new(
121
- "my-type-#{i}",
122
- /([a-z] )*?[a-z]/,
123
- String,
124
- ->(s) { s },
125
- true,
126
- false
127
- )
128
- )
129
- end
130
- # This would otherwise generate 4^11=4194300 expressions and consume just shy of 1.5GB.
131
- expressions = @generator.generate_expressions('a s i m p l e s t e p')
132
-
133
- expect(expressions.length).to eq(256)
134
- end
135
-
136
- it 'prefers expression with longest non empty match' do
137
- @parameter_type_registry.define_parameter_type(
138
- ParameterType.new(
139
- 'zero-or-more',
140
- /[a-z]*/,
141
- String,
142
- ->(s) { s },
143
- true,
144
- false
145
- )
146
- )
147
- @parameter_type_registry.define_parameter_type(
148
- ParameterType.new(
149
- 'exactly-one',
150
- /[a-z]/,
151
- String,
152
- ->(s) { s },
153
- true,
154
- false
155
- )
156
- )
157
- expressions = @generator.generate_expressions('a simple step')
158
-
159
- expect(expressions.length).to eq(2)
160
- expect(expressions[0].source).to eq('{exactly-one} {zero-or-more} {zero-or-more}')
161
- expect(expressions[1].source).to eq('{zero-or-more} {zero-or-more} {zero-or-more}')
162
- end
163
-
164
- context 'does not suggest parameter when match is' do
165
- before do
166
- @parameter_type_registry.define_parameter_type(
167
- ParameterType.new(
168
- 'direction',
169
- /(up|down)/,
170
- String,
171
- ->(s) { s },
172
- true,
173
- false
174
- )
175
- )
176
- end
177
-
178
- it 'at the beginning of a word' do
179
- expect(@generator.generate_expressions('When I download a picture')[0].source).not_to eq('When I {direction}load a picture')
180
- expect(@generator.generate_expressions('When I download a picture')[0].source).to eq('When I download a picture')
181
- end
182
-
183
- it 'inside a word' do
184
- expect(@generator.generate_expressions('When I watch the muppet show')[0].source).not_to eq('When I watch the m{direction}pet show')
185
- expect(@generator.generate_expressions('When I watch the muppet show')[0].source).to eq('When I watch the muppet show')
186
- end
187
-
188
- it 'at the end of a word' do
189
- expect(@generator.generate_expressions('When I create a group')[0].source).not_to eq('When I create a gro{direction}')
190
- expect(@generator.generate_expressions('When I create a group')[0].source).to eq('When I create a group')
191
- end
192
- end
193
-
194
- context 'does suggest parameter when match is' do
195
- before do
196
- @parameter_type_registry.define_parameter_type(
197
- ParameterType.new(
198
- 'direction',
199
- /(up|down)/,
200
- String,
201
- ->(s) { s },
202
- true,
203
- false
204
- )
205
- )
206
- end
207
-
208
- it 'a full word' do
209
- expect(@generator.generate_expressions('When I go down the road')[0].source).to eq('When I go {direction} the road')
210
- expect(@generator.generate_expressions('When I walk up the hill')[0].source).to eq('When I walk {direction} the hill')
211
- expect(@generator.generate_expressions('up the hill, the road goes down')[0].source).to eq('{direction} the hill, the road goes {direction}')
212
- end
213
-
214
- it 'wrapped around punctuation characters' do
215
- expect(@generator.generate_expressions('When direction is:down')[0].source).to eq('When direction is:{direction}')
216
- expect(@generator.generate_expressions('Then direction is down.')[0].source).to eq('Then direction is {direction}.')
217
- end
218
- end
219
-
220
- def assert_expression(expected_expression, expected_argument_names, text)
221
- generated_expression = @generator.generate_expressions(text)[0]
222
- expect(generated_expression.parameter_names).to eq(expected_argument_names)
223
- expect(generated_expression.source).to eq(expected_expression)
224
-
225
- cucumber_expression = CucumberExpression.new(generated_expression.source, @parameter_type_registry)
226
- match = cucumber_expression.match(text)
227
-
228
- raise "Expected text '#{text}' to match generated expression '#{generated_expression.source}'" if match.nil?
229
-
230
- expect(match.length).to eq(expected_argument_names.length)
231
- end
232
- end
233
- end
234
- end
@@ -1,25 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'yaml'
4
- require 'cucumber/cucumber_expressions/cucumber_expression_parser'
5
- require 'cucumber/cucumber_expressions/errors'
6
-
7
- module Cucumber
8
- module CucumberExpressions
9
- describe CucumberExpressionParser do
10
- Dir['../testdata/cucumber-expression/parser/*.yaml'].each do |path|
11
- expectation = YAML.load_file(path)
12
- it "parses #{path}" do
13
- parser = described_class.new
14
- if expectation['exception']
15
- expect { parser.parse(expectation['expression']) }.to raise_error(expectation['exception'])
16
- else
17
- node = parser.parse(expectation['expression'])
18
- node_hash = node.to_hash
19
- expect(node_hash).to eq(expectation['expected_ast'])
20
- end
21
- end
22
- end
23
- end
24
- end
25
- end
@@ -1,178 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'yaml'
4
- require 'cucumber/cucumber_expressions/cucumber_expression'
5
- require 'cucumber/cucumber_expressions/parameter_type_registry'
6
-
7
- module Cucumber
8
- module CucumberExpressions
9
- describe CucumberExpression do
10
- Dir['../testdata/cucumber-expression/matching/*.yaml'].each do |path|
11
- expectation = YAML.load_file(path)
12
-
13
- it "matches #{path}" do
14
- parameter_registry = ParameterTypeRegistry.new
15
- if expectation['exception']
16
- expect {
17
- cucumber_expression = described_class.new(expectation['expression'], parameter_registry)
18
- cucumber_expression.match(expectation['text'])
19
- }.to raise_error(expectation['exception'])
20
- else
21
- cucumber_expression = described_class.new(expectation['expression'], parameter_registry)
22
- matches = cucumber_expression.match(expectation['text'])
23
- values = matches.nil? ? nil : matches.map do |arg|
24
- value = arg.value(nil)
25
- case value
26
- when BigDecimal
27
- # Format {bigdecimal} as string (because it must be a string in matches-bigdecimal.yaml)
28
- value.to_s('f')
29
- when Integer
30
- # Format {bigint} as string (because it must be a string in matches-bigint.yaml)
31
- value.bit_length > 64 ? value.to_s : value
32
- else
33
- value
34
- end
35
- end
36
- expect(values).to eq(expectation['expected_args'])
37
- end
38
- end
39
- end
40
-
41
- it 'documents match arguments' do
42
- parameter_registry = ParameterTypeRegistry.new
43
-
44
- ### [capture-match-arguments]
45
- expr = 'I have {int} cuke(s)'
46
- expression = described_class.new(expr, parameter_registry)
47
- args = expression.match('I have 7 cukes')
48
-
49
- expect(args[0].value(nil)).to eq(7)
50
- ### [capture-match-arguments]
51
- end
52
-
53
- it 'matches float' do
54
- expect(match('{float}', '')).to eq(nil)
55
- expect(match('{float}', '.')).to eq(nil)
56
- expect(match('{float}', ',')).to eq(nil)
57
- expect(match('{float}', '-')).to eq(nil)
58
- expect(match('{float}', 'E')).to eq(nil)
59
- expect(match('{float}', '1,')).to eq(nil)
60
- expect(match('{float}', ',1')).to eq(nil)
61
- expect(match('{float}', '1.')).to eq(nil)
62
-
63
- expect(match('{float}', '1')).to eq([1])
64
- expect(match('{float}', '-1')).to eq([-1])
65
- expect(match('{float}', '1.1')).to eq([1.1])
66
- expect(match('{float}', '1,000')).to eq(nil)
67
- expect(match('{float}', '1,000,0')).to eq(nil)
68
- expect(match('{float}', '1,000.1')).to eq(nil)
69
- expect(match('{float}', '1,000,10')).to eq(nil)
70
- expect(match('{float}', '1,0.1')).to eq(nil)
71
- expect(match('{float}', '1,000,000.1')).to eq(nil)
72
- expect(match('{float}', '-1.1')).to eq([-1.1])
73
-
74
- expect(match('{float}', '.1')).to eq([0.1])
75
- expect(match('{float}', '-.1')).to eq([-0.1])
76
- expect(match('{float}', '-.1000001')).to eq([-0.1000001])
77
- expect(match('{float}', '1E1')).to eq([10.0])
78
- expect(match('{float}', '.1E1')).to eq([1])
79
- expect(match('{float}', 'E1')).to eq(nil)
80
- expect(match('{float}', '-.1E-1')).to eq([-0.01])
81
- expect(match('{float}', '-.1E-2')).to eq([-0.001])
82
- expect(match('{float}', '-.1E+1')).to eq([-1])
83
- expect(match('{float}', '-.1E+2')).to eq([-10])
84
- expect(match('{float}', '-.1E1')).to eq([-1])
85
- expect(match('{float}', '-.1E2')).to eq([-10])
86
- end
87
-
88
- it 'float with zero' do
89
- expect(match('{float}', '0')).to eq([0.0])
90
- end
91
-
92
- it 'matches anonymous' do
93
- expect(match('{}', '0.22')).to eq(['0.22'])
94
- end
95
-
96
- it 'exposes source' do
97
- expr = 'I have {int} cuke(s)'
98
-
99
- expect(described_class.new(expr, ParameterTypeRegistry.new).source).to eq(expr)
100
- end
101
-
102
- it 'exposes source via #to_s' do
103
- expr = 'I have {int} cuke(s)'
104
-
105
- expect(described_class.new(expr, ParameterTypeRegistry.new).to_s).to eq(expr.inspect)
106
- end
107
-
108
- it 'unmatched optional groups have undefined values' do
109
- parameter_type_registry = ParameterTypeRegistry.new
110
- parameter_type_registry.define_parameter_type(
111
- ParameterType.new(
112
- 'textAndOrNumber',
113
- /([A-Z]+)?(?: )?([0-9]+)?/,
114
- Object,
115
- ->(s1, s2) { [s1, s2] },
116
- false,
117
- true
118
- )
119
- )
120
- expression = described_class.new('{textAndOrNumber}', parameter_type_registry)
121
-
122
- class World; end
123
-
124
- expect(expression.match('TLA')[0].value(World.new)).to eq(['TLA', nil])
125
- expect(expression.match('123')[0].value(World.new)).to eq([nil, '123'])
126
- end
127
-
128
- # Ruby specific
129
-
130
- it 'delegates transform to custom object' do
131
- parameter_type_registry = ParameterTypeRegistry.new
132
- parameter_type_registry.define_parameter_type(
133
- ParameterType.new(
134
- 'widget',
135
- /\w+/,
136
- Object,
137
- ->(s) { self.create_widget(s) },
138
- false,
139
- true
140
- )
141
- )
142
- expression = described_class.new(
143
- 'I have a {widget}',
144
- parameter_type_registry
145
- )
146
-
147
- class World
148
- def create_widget(s)
149
- "widget:#{s}"
150
- end
151
- end
152
-
153
- args = expression.match('I have a bolt')
154
-
155
- expect(args[0].value(World.new)).to eq('widget:bolt')
156
- end
157
-
158
- it 'reports undefined parameter type name' do
159
- parameter_type_registry = ParameterTypeRegistry.new
160
-
161
- described_class.new(
162
- 'I have {int} {widget}(s) in {word}',
163
- parameter_type_registry
164
- )
165
- rescue UndefinedParameterTypeError => e
166
- expect(e.undefined_parameter_type_name).to eq('widget')
167
- end
168
-
169
- def match(expression, text)
170
- cucumber_expression = CucumberExpression.new(expression, ParameterTypeRegistry.new)
171
- args = cucumber_expression.match(text)
172
- return nil if args.nil?
173
-
174
- args.map { |arg| arg.value(nil) }
175
- end
176
- end
177
- end
178
- end
@@ -1,25 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'yaml'
4
- require 'cucumber/cucumber_expressions/cucumber_expression_tokenizer'
5
- require 'cucumber/cucumber_expressions/errors'
6
-
7
- module Cucumber
8
- module CucumberExpressions
9
- describe CucumberExpressionTokenizer do
10
- Dir['../testdata/cucumber-expression/tokenizer/*.yaml'].each do |path|
11
- expectation = YAML.load_file(path)
12
- it "tokenizes #{path}" do
13
- tokenizer = described_class.new
14
- if expectation['exception']
15
- expect { tokenizer.tokenize(expectation['expression']) }.to raise_error(expectation['exception'])
16
- else
17
- tokens = tokenizer.tokenize(expectation['expression'])
18
- token_hashes = tokens.map { |token| token.to_hash }
19
- expect(token_hashes).to eq(expectation['expected_tokens'])
20
- end
21
- end
22
- end
23
- end
24
- end
25
- end
@@ -1,29 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'yaml'
4
- require 'cucumber/cucumber_expressions/cucumber_expression'
5
- require 'cucumber/cucumber_expressions/parameter_type_registry'
6
-
7
- module Cucumber
8
- module CucumberExpressions
9
- describe CucumberExpression do
10
- Dir['../testdata/cucumber-expression/transformation/*.yaml'].each do |path|
11
- expectation = YAML.load_file(path)
12
- it "transforms #{path}" do
13
- parameter_registry = ParameterTypeRegistry.new
14
- if expectation['exception']
15
- expect {
16
- cucumber_expression = described_class.new(expectation['expression'], parameter_registry)
17
- cucumber_expression.match(expectation['text'])
18
- }.to raise_error(expectation['exception'])
19
- else
20
- cucumber_expression = described_class.new(expectation['expression'], parameter_registry)
21
- matches = cucumber_expression.match(expectation['text'])
22
- values = matches.nil? ? nil : matches.map { |arg| arg.value(nil) }
23
- expect(values).to eq(expectation['expected_args'])
24
- end
25
- end
26
- end
27
- end
28
- end
29
- end