cucumber-core 4.0.0 → 5.0.0
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 +5 -5
- data/CHANGELOG.md +7 -1
- data/README.md +1 -2
- data/lib/cucumber/core/compiler.rb +23 -21
- data/lib/cucumber/core/event_bus.rb +1 -1
- data/lib/cucumber/core/gherkin/parser.rb +4 -5
- data/lib/cucumber/core/version.rb +1 -1
- data/spec/capture_warnings.rb +74 -0
- data/spec/coverage.rb +11 -0
- data/spec/cucumber/core/compiler_spec.rb +178 -0
- data/spec/cucumber/core/event_bus_spec.rb +163 -0
- data/spec/cucumber/core/event_spec.rb +40 -0
- data/spec/cucumber/core/filter_spec.rb +101 -0
- data/spec/cucumber/core/gherkin/parser_spec.rb +95 -0
- data/spec/cucumber/core/gherkin/writer_spec.rb +333 -0
- data/spec/cucumber/core/report/summary_spec.rb +167 -0
- data/spec/cucumber/core/test/action_spec.rb +154 -0
- data/spec/cucumber/core/test/case_spec.rb +126 -0
- data/spec/cucumber/core/test/data_table_spec.rb +81 -0
- data/spec/cucumber/core/test/doc_string_spec.rb +114 -0
- data/spec/cucumber/core/test/duration_matcher.rb +20 -0
- data/spec/cucumber/core/test/empty_multiline_argument_spec.rb +28 -0
- data/spec/cucumber/core/test/filters/locations_filter_spec.rb +271 -0
- data/spec/cucumber/core/test/location_spec.rb +126 -0
- data/spec/cucumber/core/test/result_spec.rb +474 -0
- data/spec/cucumber/core/test/runner_spec.rb +318 -0
- data/spec/cucumber/core/test/step_spec.rb +87 -0
- data/spec/cucumber/core/test/timer_spec.rb +25 -0
- data/spec/cucumber/core_spec.rb +262 -0
- data/spec/report_api_spy.rb +25 -0
- metadata +83 -14
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'cucumber/core/event'
|
2
|
+
|
3
|
+
module Cucumber
|
4
|
+
module Core
|
5
|
+
describe Event do
|
6
|
+
|
7
|
+
describe ".new" do
|
8
|
+
it "generates new types of events" do
|
9
|
+
my_event_type = Event.new
|
10
|
+
my_event = my_event_type.new
|
11
|
+
expect(my_event).to be_kind_of(Core::Event)
|
12
|
+
end
|
13
|
+
|
14
|
+
it "generates events with attributes" do
|
15
|
+
my_event_type = Event.new(:foo, :bar)
|
16
|
+
my_event = my_event_type.new(1,2)
|
17
|
+
expect(my_event.attributes).to eq [1, 2]
|
18
|
+
expect(my_event.foo).to eq 1
|
19
|
+
expect(my_event.bar).to eq 2
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe "a generated event" do
|
24
|
+
class MyEventType < Event.new(:foo, :bar)
|
25
|
+
end
|
26
|
+
|
27
|
+
it "can be converted to a hash" do
|
28
|
+
my_event = MyEventType.new(1,2)
|
29
|
+
expect(my_event.to_h).to eq foo: 1, bar: 2
|
30
|
+
end
|
31
|
+
|
32
|
+
it "has an event_id" do
|
33
|
+
expect(MyEventType.event_id).to eq :my_event_type
|
34
|
+
expect(MyEventType.new(1,2).event_id).to eq :my_event_type
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'cucumber/core/gherkin/writer'
|
3
|
+
require 'cucumber/core'
|
4
|
+
require 'cucumber/core/filter'
|
5
|
+
|
6
|
+
module Cucumber::Core
|
7
|
+
describe Filter do
|
8
|
+
include Cucumber::Core::Gherkin::Writer
|
9
|
+
include Cucumber::Core
|
10
|
+
|
11
|
+
describe ".new" do
|
12
|
+
let(:receiver) { double.as_null_object }
|
13
|
+
|
14
|
+
let(:doc) {
|
15
|
+
gherkin do
|
16
|
+
feature do
|
17
|
+
scenario 'x' do
|
18
|
+
step 'a step'
|
19
|
+
end
|
20
|
+
|
21
|
+
scenario 'y' do
|
22
|
+
step 'a step'
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
}
|
27
|
+
|
28
|
+
it "creates a filter class that can pass-through by default" do
|
29
|
+
my_filter_class = Filter.new
|
30
|
+
my_filter = my_filter_class.new
|
31
|
+
expect(receiver).to receive(:test_case) { |test_case|
|
32
|
+
expect(test_case.test_steps.length).to eq 1
|
33
|
+
expect(test_case.test_steps.first.text).to eq 'a step'
|
34
|
+
}.exactly(2).times
|
35
|
+
compile [doc], receiver, [my_filter]
|
36
|
+
end
|
37
|
+
|
38
|
+
context "customizing by subclassing" do
|
39
|
+
|
40
|
+
# Each filter imlicitly gets a :receiver attribute
|
41
|
+
# that you need to call with the new test case
|
42
|
+
# once you've received yours and modified it.
|
43
|
+
class BasicBlankingFilter < Filter.new
|
44
|
+
def test_case(test_case)
|
45
|
+
test_case.with_steps([]).describe_to(receiver)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# You can pass the names of attributes when building a
|
50
|
+
# filter, allowing you to have custom attributes.
|
51
|
+
class NamedBlankingFilter < Filter.new(:name_pattern)
|
52
|
+
def test_case(test_case)
|
53
|
+
if test_case.name =~ name_pattern
|
54
|
+
test_case.with_steps([]).describe_to(receiver)
|
55
|
+
else
|
56
|
+
test_case.describe_to(receiver) # or just call `super`
|
57
|
+
end
|
58
|
+
self
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
it "can override methods from the base class" do
|
63
|
+
expect(receiver).to receive(:test_case) { |test_case|
|
64
|
+
expect(test_case.test_steps.length).to eq 0
|
65
|
+
}.exactly(2).times
|
66
|
+
run BasicBlankingFilter.new
|
67
|
+
end
|
68
|
+
|
69
|
+
it "can take arguments" do
|
70
|
+
expect(receiver).to receive(:test_case) { |test_case|
|
71
|
+
expect(test_case.test_steps.length).to eq 0
|
72
|
+
}.once.ordered
|
73
|
+
expect(receiver).to receive(:test_case) { |test_case|
|
74
|
+
expect(test_case.test_steps.length).to eq 1
|
75
|
+
}.once.ordered
|
76
|
+
run NamedBlankingFilter.new(/x/)
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
context "customizing by using a block" do
|
82
|
+
BlockBlankingFilter = Filter.new do
|
83
|
+
def test_case(test_case)
|
84
|
+
test_case.with_steps([]).describe_to(receiver)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
it "allows methods to be overridden" do
|
89
|
+
expect(receiver).to receive(:test_case) { |test_case|
|
90
|
+
expect(test_case.test_steps.length).to eq 0
|
91
|
+
}.exactly(2).times
|
92
|
+
run BlockBlankingFilter.new
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def run(filter)
|
97
|
+
compile [doc], receiver, [filter]
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
# frozen_string_literal: true
|
3
|
+
require 'cucumber/core/gherkin/parser'
|
4
|
+
require 'cucumber/core/gherkin/writer'
|
5
|
+
|
6
|
+
module Cucumber
|
7
|
+
module Core
|
8
|
+
module Gherkin
|
9
|
+
describe Parser do
|
10
|
+
let(:receiver) { double }
|
11
|
+
let(:event_bus) { double }
|
12
|
+
let(:parser) { Parser.new(receiver, event_bus) }
|
13
|
+
let(:visitor) { double }
|
14
|
+
|
15
|
+
before do
|
16
|
+
allow( event_bus ).to receive(:gherkin_source_parsed)
|
17
|
+
end
|
18
|
+
|
19
|
+
def parse
|
20
|
+
parser.document(source)
|
21
|
+
end
|
22
|
+
|
23
|
+
context "for invalid gherkin" do
|
24
|
+
let(:source) { Gherkin::Document.new(path, "\nnot gherkin\n\nFeature: \n") }
|
25
|
+
let(:path) { 'path_to/the.feature' }
|
26
|
+
|
27
|
+
it "raises an error" do
|
28
|
+
expect { parse }.to raise_error(ParseError) do |error|
|
29
|
+
expect( error.message ).to match(/not gherkin/)
|
30
|
+
expect( error.message ).to match(/#{path}/)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
context "for valid gherkin" do
|
36
|
+
let(:source) { Gherkin::Document.new(path, 'Feature:') }
|
37
|
+
let(:path) { 'path_to/the.feature' }
|
38
|
+
|
39
|
+
it "issues a gherkin_source_parsed event" do
|
40
|
+
expect( event_bus ).to receive(:gherkin_source_parsed)
|
41
|
+
parse
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context "for empty files" do
|
46
|
+
let(:source) { Gherkin::Document.new(path, '') }
|
47
|
+
let(:path) { 'path_to/the.feature' }
|
48
|
+
|
49
|
+
it "passes on no pickles" do
|
50
|
+
expect( receiver ).not_to receive(:pickle)
|
51
|
+
parse
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
include Writer
|
56
|
+
def self.source(&block)
|
57
|
+
let(:source) { gherkin(&block) }
|
58
|
+
end
|
59
|
+
|
60
|
+
RSpec::Matchers.define :pickle_with_language do |language|
|
61
|
+
match { |actual| actual.language == language }
|
62
|
+
end
|
63
|
+
|
64
|
+
context "when the Gherkin has a language header" do
|
65
|
+
source do
|
66
|
+
feature(language: 'ja', keyword: '機能') do
|
67
|
+
scenario(keyword: 'シナリオ')
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
it "the pickles have the correct language" do
|
72
|
+
expect( receiver ).to receive(:pickle).with(pickle_with_language('ja'))
|
73
|
+
parse
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
context "when the Gherkin produces one pickle" do
|
78
|
+
source do
|
79
|
+
feature do
|
80
|
+
scenario do
|
81
|
+
step 'text'
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
it "passes on the pickle" do
|
87
|
+
expect( receiver ).to receive(:pickle)
|
88
|
+
parse
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,333 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'cucumber/core/gherkin/writer'
|
3
|
+
require 'unindent'
|
4
|
+
|
5
|
+
module Cucumber::Core::Gherkin
|
6
|
+
describe Writer do
|
7
|
+
include Writer
|
8
|
+
|
9
|
+
context 'specifying uri' do
|
10
|
+
it 'generates a uri by default' do
|
11
|
+
source = gherkin { feature }
|
12
|
+
expect( source.uri ).to eq 'features/test.feature'
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'allows you to specify a URI' do
|
16
|
+
source = gherkin('features/path/to/my.feature') { feature }
|
17
|
+
expect( source.uri ).to eq 'features/path/to/my.feature'
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
context 'a feature' do
|
22
|
+
|
23
|
+
it 'generates the feature statement' do
|
24
|
+
source = gherkin { feature }
|
25
|
+
expect( source ).to eq "Feature:\n"
|
26
|
+
end
|
27
|
+
|
28
|
+
context 'when a name is provided' do
|
29
|
+
it 'includes the name in the feature statement' do
|
30
|
+
source = gherkin do
|
31
|
+
feature "A Feature\n"
|
32
|
+
end
|
33
|
+
expect( source ).to eq "Feature: A Feature\n"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
context 'when a description is provided' do
|
38
|
+
it 'includes the description in the feature statement' do
|
39
|
+
source = gherkin do
|
40
|
+
feature "A Feature", description: <<-END
|
41
|
+
This is the description
|
42
|
+
which can span
|
43
|
+
multiple lines.
|
44
|
+
END
|
45
|
+
end
|
46
|
+
expected = <<-END
|
47
|
+
Feature: A Feature
|
48
|
+
This is the description
|
49
|
+
which can span
|
50
|
+
multiple lines.
|
51
|
+
END
|
52
|
+
|
53
|
+
expect( source ).to eq expected.unindent
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
context 'when a keyword is provided' do
|
58
|
+
it 'uses the supplied keyword' do
|
59
|
+
source = gherkin do
|
60
|
+
feature "A Feature", keyword: "Business Need"
|
61
|
+
end
|
62
|
+
expect( source ).to eq "Business Need: A Feature\n"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
context 'when a language is supplied' do
|
67
|
+
it 'inserts a language statement' do
|
68
|
+
source = gherkin do
|
69
|
+
feature language: 'ru'
|
70
|
+
end
|
71
|
+
|
72
|
+
expect( source ).to eq "# language: ru\nFeature:\n"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
context 'when a comment is supplied' do
|
77
|
+
it 'inserts a comment' do
|
78
|
+
source = gherkin do
|
79
|
+
comment 'wow'
|
80
|
+
comment 'great'
|
81
|
+
feature
|
82
|
+
end
|
83
|
+
|
84
|
+
expect( source.to_s ).to eq "# wow\n# great\nFeature:\n"
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
context 'with a scenario' do
|
89
|
+
it 'includes the scenario statement' do
|
90
|
+
source = gherkin do
|
91
|
+
feature "A Feature" do
|
92
|
+
scenario
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
expect( source.to_s ).to match(/Scenario:/)
|
97
|
+
end
|
98
|
+
|
99
|
+
context 'when a comment is provided' do
|
100
|
+
it 'includes the comment in the scenario statement' do
|
101
|
+
source = gherkin do
|
102
|
+
feature do
|
103
|
+
comment 'wow'
|
104
|
+
scenario
|
105
|
+
end
|
106
|
+
end
|
107
|
+
expect( source.to_s ).to eq <<-END.unindent
|
108
|
+
Feature:
|
109
|
+
|
110
|
+
# wow
|
111
|
+
Scenario:
|
112
|
+
END
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
context 'when a description is provided' do
|
117
|
+
it 'includes the description in the scenario statement' do
|
118
|
+
source = gherkin do
|
119
|
+
feature do
|
120
|
+
scenario description: <<-END
|
121
|
+
This is the description
|
122
|
+
which can span
|
123
|
+
multiple lines.
|
124
|
+
END
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
expect( source ).to eq <<-END.unindent
|
129
|
+
Feature:
|
130
|
+
|
131
|
+
Scenario:
|
132
|
+
This is the description
|
133
|
+
which can span
|
134
|
+
multiple lines.
|
135
|
+
END
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
context 'with a step' do
|
140
|
+
it 'includes the step statement' do
|
141
|
+
source = gherkin do
|
142
|
+
feature "A Feature" do
|
143
|
+
scenario do
|
144
|
+
step 'passing'
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
expect( source.to_s ).to match(/Given passing\Z/m)
|
150
|
+
end
|
151
|
+
|
152
|
+
context 'when a docstring is provided' do
|
153
|
+
it 'includes the content type when provided' do
|
154
|
+
source = gherkin do
|
155
|
+
feature do
|
156
|
+
scenario do
|
157
|
+
step 'failing' do
|
158
|
+
doc_string 'some text', 'text/plain'
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
end
|
164
|
+
|
165
|
+
expect( source ).to eq <<-END.unindent
|
166
|
+
Feature:
|
167
|
+
|
168
|
+
Scenario:
|
169
|
+
Given failing
|
170
|
+
"""text/plain
|
171
|
+
some text
|
172
|
+
"""
|
173
|
+
END
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
context 'with a background' do
|
180
|
+
it 'can have a description' do
|
181
|
+
source = gherkin do
|
182
|
+
feature do
|
183
|
+
background description: "One line,\nand two.."
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
expect( source ).to eq <<-END.unindent
|
188
|
+
Feature:
|
189
|
+
|
190
|
+
Background:
|
191
|
+
One line,
|
192
|
+
and two..
|
193
|
+
END
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
context 'with a scenario outline' do
|
198
|
+
it 'can have a description' do
|
199
|
+
source = gherkin do
|
200
|
+
feature do
|
201
|
+
scenario_outline description: "Doesn't need to be multi-line."
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
expect( source ).to eq <<-END.unindent
|
206
|
+
Feature:
|
207
|
+
|
208
|
+
Scenario Outline:
|
209
|
+
Doesn't need to be multi-line.
|
210
|
+
END
|
211
|
+
end
|
212
|
+
|
213
|
+
context 'and examples table' do
|
214
|
+
it 'can have a description' do
|
215
|
+
source = gherkin do
|
216
|
+
feature do
|
217
|
+
scenario_outline do
|
218
|
+
examples description: "Doesn't need to be multi-line." do
|
219
|
+
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
expect( source ).to eq <<-END.unindent
|
226
|
+
Feature:
|
227
|
+
|
228
|
+
Scenario Outline:
|
229
|
+
|
230
|
+
Examples:
|
231
|
+
Doesn't need to be multi-line.
|
232
|
+
END
|
233
|
+
end
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
it 'generates a complex feature' do
|
239
|
+
source = gherkin do
|
240
|
+
comment 'wow'
|
241
|
+
feature 'Fully featured', language: 'en', tags: '@always' do
|
242
|
+
comment 'cool'
|
243
|
+
background do
|
244
|
+
step 'passing'
|
245
|
+
end
|
246
|
+
|
247
|
+
scenario do
|
248
|
+
step 'passing'
|
249
|
+
end
|
250
|
+
|
251
|
+
comment 'here'
|
252
|
+
scenario 'with doc string', tags: '@first @second' do
|
253
|
+
comment 'and here'
|
254
|
+
step 'passing'
|
255
|
+
step 'failing', keyword: 'When' do
|
256
|
+
doc_string <<-END
|
257
|
+
I wish I was a little bit taller.
|
258
|
+
I wish I was a baller.
|
259
|
+
END
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
scenario 'with a table...' do
|
264
|
+
step 'passes:' do
|
265
|
+
table do
|
266
|
+
row 'name', 'age', 'location'
|
267
|
+
row 'Janine', '43', 'Antarctica'
|
268
|
+
end
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
comment 'yay'
|
273
|
+
scenario_outline 'eating' do
|
274
|
+
step 'there are <start> cucumbers'
|
275
|
+
step 'I eat <eat> cucumbers', keyword: 'When'
|
276
|
+
step 'I should have <left> cucumbers', keyword: 'Then'
|
277
|
+
|
278
|
+
comment 'hmmm'
|
279
|
+
examples do
|
280
|
+
row 'start', 'eat', 'left'
|
281
|
+
row '12', '5', '7'
|
282
|
+
row '20', '5', '15'
|
283
|
+
end
|
284
|
+
end
|
285
|
+
end
|
286
|
+
end
|
287
|
+
|
288
|
+
expect( source.to_s ).to eq <<-END.unindent
|
289
|
+
# language: en
|
290
|
+
# wow
|
291
|
+
@always
|
292
|
+
Feature: Fully featured
|
293
|
+
|
294
|
+
# cool
|
295
|
+
Background:
|
296
|
+
Given passing
|
297
|
+
|
298
|
+
Scenario:
|
299
|
+
Given passing
|
300
|
+
|
301
|
+
# here
|
302
|
+
@first @second
|
303
|
+
Scenario: with doc string
|
304
|
+
# and here
|
305
|
+
Given passing
|
306
|
+
When failing
|
307
|
+
"""
|
308
|
+
I wish I was a little bit taller.
|
309
|
+
I wish I was a baller.
|
310
|
+
"""
|
311
|
+
|
312
|
+
Scenario: with a table...
|
313
|
+
Given passes:
|
314
|
+
| name | age | location |
|
315
|
+
| Janine | 43 | Antarctica |
|
316
|
+
|
317
|
+
# yay
|
318
|
+
Scenario Outline: eating
|
319
|
+
Given there are <start> cucumbers
|
320
|
+
When I eat <eat> cucumbers
|
321
|
+
Then I should have <left> cucumbers
|
322
|
+
|
323
|
+
# hmmm
|
324
|
+
Examples:
|
325
|
+
| start | eat | left |
|
326
|
+
| 12 | 5 | 7 |
|
327
|
+
| 20 | 5 | 15 |
|
328
|
+
END
|
329
|
+
|
330
|
+
end
|
331
|
+
end
|
332
|
+
end
|
333
|
+
|