cucumber-core 0.1.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 +7 -0
- data/.coveralls.yml +1 -0
- data/.rspec +1 -0
- data/.ruby-gemset +1 -0
- data/.travis.yml +16 -0
- data/Gemfile +2 -0
- data/LICENSE +20 -0
- data/README.md +9 -0
- data/Rakefile +24 -0
- data/cucumber-core.gemspec +32 -0
- data/lib/cucumber/core.rb +37 -0
- data/lib/cucumber/core/ast.rb +13 -0
- data/lib/cucumber/core/ast/background.rb +33 -0
- data/lib/cucumber/core/ast/comment.rb +17 -0
- data/lib/cucumber/core/ast/data_table.rb +326 -0
- data/lib/cucumber/core/ast/describes_itself.rb +16 -0
- data/lib/cucumber/core/ast/doc_string.rb +83 -0
- data/lib/cucumber/core/ast/empty_background.rb +12 -0
- data/lib/cucumber/core/ast/examples_table.rb +95 -0
- data/lib/cucumber/core/ast/feature.rb +62 -0
- data/lib/cucumber/core/ast/location.rb +140 -0
- data/lib/cucumber/core/ast/multiline_argument.rb +33 -0
- data/lib/cucumber/core/ast/names.rb +19 -0
- data/lib/cucumber/core/ast/outline_step.rb +51 -0
- data/lib/cucumber/core/ast/scenario.rb +43 -0
- data/lib/cucumber/core/ast/scenario_outline.rb +44 -0
- data/lib/cucumber/core/ast/step.rb +38 -0
- data/lib/cucumber/core/ast/tag.rb +14 -0
- data/lib/cucumber/core/compiler.rb +136 -0
- data/lib/cucumber/core/gherkin/ast_builder.rb +315 -0
- data/lib/cucumber/core/gherkin/document.rb +20 -0
- data/lib/cucumber/core/gherkin/parser.rb +45 -0
- data/lib/cucumber/core/gherkin/writer.rb +220 -0
- data/lib/cucumber/core/gherkin/writer/helpers.rb +178 -0
- data/lib/cucumber/core/platform.rb +30 -0
- data/lib/cucumber/core/test/case.rb +143 -0
- data/lib/cucumber/core/test/filters.rb +48 -0
- data/lib/cucumber/core/test/filters/tag_filter.rb +110 -0
- data/lib/cucumber/core/test/hook_compiler.rb +109 -0
- data/lib/cucumber/core/test/mapper.rb +56 -0
- data/lib/cucumber/core/test/mapping.rb +67 -0
- data/lib/cucumber/core/test/result.rb +191 -0
- data/lib/cucumber/core/test/runner.rb +149 -0
- data/lib/cucumber/core/test/step.rb +69 -0
- data/lib/cucumber/core/test/timer.rb +31 -0
- data/lib/cucumber/core/version.rb +9 -0
- data/lib/cucumber/initializer.rb +18 -0
- data/spec/capture_warnings.rb +68 -0
- data/spec/coverage.rb +10 -0
- data/spec/cucumber/core/ast/data_table_spec.rb +139 -0
- data/spec/cucumber/core/ast/doc_string_spec.rb +77 -0
- data/spec/cucumber/core/ast/examples_table_spec.rb +87 -0
- data/spec/cucumber/core/ast/location_spec.rb +105 -0
- data/spec/cucumber/core/ast/outline_step_spec.rb +77 -0
- data/spec/cucumber/core/ast/step_spec.rb +44 -0
- data/spec/cucumber/core/compiler_spec.rb +249 -0
- data/spec/cucumber/core/gherkin/parser_spec.rb +182 -0
- data/spec/cucumber/core/gherkin/writer_spec.rb +332 -0
- data/spec/cucumber/core/test/case_spec.rb +416 -0
- data/spec/cucumber/core/test/hook_compiler_spec.rb +78 -0
- data/spec/cucumber/core/test/mapper_spec.rb +68 -0
- data/spec/cucumber/core/test/mapping_spec.rb +103 -0
- data/spec/cucumber/core/test/result_spec.rb +178 -0
- data/spec/cucumber/core/test/runner_spec.rb +265 -0
- data/spec/cucumber/core/test/step_spec.rb +58 -0
- data/spec/cucumber/core/test/timer_spec.rb +13 -0
- data/spec/cucumber/core_spec.rb +419 -0
- data/spec/cucumber/initializer_spec.rb +49 -0
- metadata +221 -0
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'cucumber/core/test/step'
|
2
|
+
|
3
|
+
module Cucumber::Core::Test
|
4
|
+
describe Step do
|
5
|
+
|
6
|
+
describe "describing itself" do
|
7
|
+
it "describes itself to a visitor" do
|
8
|
+
visitor = double
|
9
|
+
args = double
|
10
|
+
test_step = Step.new([double])
|
11
|
+
expect( visitor ).to receive(:test_step).with(test_step, args)
|
12
|
+
test_step.describe_to(visitor, args)
|
13
|
+
end
|
14
|
+
|
15
|
+
it "describes its source to a visitor" do
|
16
|
+
feature, scenario, step = double, double, double
|
17
|
+
visitor = double
|
18
|
+
args = double
|
19
|
+
expect( feature ).to receive(:describe_to).with(visitor, args)
|
20
|
+
expect( scenario ).to receive(:describe_to).with(visitor, args)
|
21
|
+
expect( step ).to receive(:describe_to).with(visitor, args)
|
22
|
+
test_step = Step.new([feature, scenario, step])
|
23
|
+
test_step.describe_source_to(visitor, args)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe "executing a step" do
|
28
|
+
let(:ast_step) { double }
|
29
|
+
|
30
|
+
context "when a passing mapping exists for the step" do
|
31
|
+
it "returns a passing result" do
|
32
|
+
test_step = Step.new([ast_step]).map {}
|
33
|
+
expect( test_step.execute ).to be_passed
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
context "when a failing mapping exists for the step" do
|
38
|
+
let(:exception) { StandardError.new('oops') }
|
39
|
+
|
40
|
+
it "returns a failing result" do
|
41
|
+
test_step = Step.new([ast_step]).map { raise exception }
|
42
|
+
result = test_step.execute
|
43
|
+
expect( result ).to be_failed
|
44
|
+
expect( result.exception ).to eq exception
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
it "exposes the name and multiline_arg of the ast step as attributes" do
|
50
|
+
name, multiline_arg = double, double
|
51
|
+
ast_step = double(name: name, multiline_arg: multiline_arg)
|
52
|
+
test_step = Step.new([ast_step])
|
53
|
+
expect( test_step.name ).to eq name
|
54
|
+
expect( test_step.multiline_arg ).to eq multiline_arg
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,419 @@
|
|
1
|
+
require 'cucumber/core'
|
2
|
+
require 'cucumber/core/gherkin/writer'
|
3
|
+
require 'cucumber/core/platform'
|
4
|
+
|
5
|
+
module Cucumber
|
6
|
+
describe Core do
|
7
|
+
include Core
|
8
|
+
include Core::Gherkin::Writer
|
9
|
+
|
10
|
+
describe "parsing Gherkin" do
|
11
|
+
it "calls the compiler with a valid AST" do
|
12
|
+
compiler = double(done: nil)
|
13
|
+
expect( compiler ).to receive(:feature) do |feature|
|
14
|
+
expect( feature ).to respond_to(:describe_to)
|
15
|
+
expect( feature ).to be_an_instance_of(Core::Ast::Feature)
|
16
|
+
end
|
17
|
+
|
18
|
+
gherkin = gherkin do
|
19
|
+
feature do
|
20
|
+
scenario do
|
21
|
+
step
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
parse([gherkin], compiler)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe "compiling features to a test suite" do
|
31
|
+
it "compiles two scenarios into two test cases" do
|
32
|
+
visitor = double
|
33
|
+
expect( visitor ).to receive(:test_case).exactly(2).times.and_yield.ordered
|
34
|
+
expect( visitor ).to receive(:test_step).exactly(5).times.ordered
|
35
|
+
expect( visitor ).to receive(:done).once.ordered
|
36
|
+
|
37
|
+
compile([
|
38
|
+
gherkin do
|
39
|
+
feature do
|
40
|
+
background do
|
41
|
+
step
|
42
|
+
end
|
43
|
+
scenario do
|
44
|
+
step
|
45
|
+
end
|
46
|
+
scenario do
|
47
|
+
step
|
48
|
+
step
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
], visitor)
|
53
|
+
end
|
54
|
+
|
55
|
+
it "filters out test cases based on a tag expression" do
|
56
|
+
visitor = double.as_null_object
|
57
|
+
expect( visitor ).to receive(:test_case) do |test_case|
|
58
|
+
expect( test_case.name ).to eq 'Scenario Outline: foo, bar (row 1)'
|
59
|
+
end.exactly(1).times
|
60
|
+
|
61
|
+
gherkin = gherkin do
|
62
|
+
feature do
|
63
|
+
scenario tags: '@b' do
|
64
|
+
step
|
65
|
+
end
|
66
|
+
|
67
|
+
scenario_outline 'foo' do
|
68
|
+
step '<arg>'
|
69
|
+
|
70
|
+
examples tags: '@a'do
|
71
|
+
row 'arg'
|
72
|
+
row 'x'
|
73
|
+
end
|
74
|
+
|
75
|
+
examples 'bar', tags: '@a @b' do
|
76
|
+
row 'arg'
|
77
|
+
row 'y'
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
compile [gherkin], visitor, [[Cucumber::Core::Test::TagFilter, [['@a', '@b']]]]
|
84
|
+
end
|
85
|
+
|
86
|
+
describe 'with tag filters that have limits' do
|
87
|
+
let(:visitor) { double.as_null_object }
|
88
|
+
let(:gherkin_doc) do
|
89
|
+
gherkin do
|
90
|
+
feature tags: '@feature' do
|
91
|
+
scenario tags: '@one @three' do
|
92
|
+
step
|
93
|
+
end
|
94
|
+
|
95
|
+
scenario tags: '@one' do
|
96
|
+
step
|
97
|
+
end
|
98
|
+
|
99
|
+
scenario_outline do
|
100
|
+
step '<arg>'
|
101
|
+
|
102
|
+
examples tags: '@three'do
|
103
|
+
row 'arg'
|
104
|
+
row 'x'
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
scenario tags: '@ignore' do
|
109
|
+
step
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
require 'unindent'
|
116
|
+
def expect_tag_excess(error_message)
|
117
|
+
expect {
|
118
|
+
compile [gherkin_doc], visitor, tag_filters
|
119
|
+
}.to raise_error(
|
120
|
+
Cucumber::Core::Test::TagFilter::TagExcess, error_message.unindent.chomp
|
121
|
+
)
|
122
|
+
end
|
123
|
+
|
124
|
+
context 'on scenarios' do
|
125
|
+
let(:tag_filters) {
|
126
|
+
[[Cucumber::Core::Test::TagFilter, [['@one:1']]]]
|
127
|
+
}
|
128
|
+
|
129
|
+
it 'raises a tag excess error with the location of the test cases' do
|
130
|
+
expect_tag_excess <<-STR
|
131
|
+
@one occurred 2 times, but the limit was set to 1
|
132
|
+
features/test.feature:5
|
133
|
+
features/test.feature:9
|
134
|
+
STR
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
context 'on scenario outlines' do
|
139
|
+
let(:tag_filters) {
|
140
|
+
[[Cucumber::Core::Test::TagFilter, [['@three:1']]]]
|
141
|
+
}
|
142
|
+
|
143
|
+
it 'raises a tag excess error with the location of the test cases' do
|
144
|
+
expect_tag_excess <<-STR
|
145
|
+
@three occurred 2 times, but the limit was set to 1
|
146
|
+
features/test.feature:5
|
147
|
+
features/test.feature:18
|
148
|
+
STR
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
context 'on a feature with scenarios' do
|
153
|
+
let(:tag_filters) {
|
154
|
+
[[Cucumber::Core::Test::TagFilter, [['@feature:2']]]]
|
155
|
+
}
|
156
|
+
|
157
|
+
it 'raises a tag excess error with the location of the test cases' do
|
158
|
+
expect_tag_excess <<-STR
|
159
|
+
@feature occurred 4 times, but the limit was set to 2
|
160
|
+
features/test.feature:5
|
161
|
+
features/test.feature:9
|
162
|
+
features/test.feature:18
|
163
|
+
features/test.feature:21
|
164
|
+
STR
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
context 'with negated tags' do
|
169
|
+
let(:tag_filters) {
|
170
|
+
[[Cucumber::Core::Test::TagFilter, [['~@one:1']]]]
|
171
|
+
}
|
172
|
+
|
173
|
+
it 'raises a tag excess error with the location of the test cases' do
|
174
|
+
expect_tag_excess <<-STR
|
175
|
+
@one occurred 2 times, but the limit was set to 1
|
176
|
+
features/test.feature:5
|
177
|
+
features/test.feature:9
|
178
|
+
STR
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
context 'whith multiple tag limits' do
|
183
|
+
let(:tag_filters) {
|
184
|
+
[[Cucumber::Core::Test::TagFilter, [['@one:1, @three:1', '~@feature:3']]]]
|
185
|
+
}
|
186
|
+
|
187
|
+
it 'raises a tag excess error with the location of the test cases' do
|
188
|
+
expect_tag_excess <<-STR
|
189
|
+
@one occurred 2 times, but the limit was set to 1
|
190
|
+
features/test.feature:5
|
191
|
+
features/test.feature:9
|
192
|
+
@three occurred 2 times, but the limit was set to 1
|
193
|
+
features/test.feature:5
|
194
|
+
features/test.feature:18
|
195
|
+
@feature occurred 4 times, but the limit was set to 3
|
196
|
+
features/test.feature:5
|
197
|
+
features/test.feature:9
|
198
|
+
features/test.feature:18
|
199
|
+
features/test.feature:21
|
200
|
+
STR
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
end
|
205
|
+
|
206
|
+
end
|
207
|
+
|
208
|
+
describe "executing a test suite" do
|
209
|
+
class SummaryReport
|
210
|
+
attr_reader :test_cases, :test_steps
|
211
|
+
|
212
|
+
def initialize
|
213
|
+
@test_cases = Core::Test::Result::Summary.new
|
214
|
+
@test_steps = Core::Test::Result::Summary.new
|
215
|
+
end
|
216
|
+
|
217
|
+
def after_test_case(test_case, result)
|
218
|
+
result.describe_to test_cases
|
219
|
+
end
|
220
|
+
|
221
|
+
def after_test_step(test_step, result)
|
222
|
+
result.describe_to test_steps
|
223
|
+
end
|
224
|
+
|
225
|
+
def method_missing(*)
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
context "without hooks" do
|
230
|
+
class StepTestMappings
|
231
|
+
Failure = Class.new(StandardError)
|
232
|
+
|
233
|
+
def test_case(test_case, mapper)
|
234
|
+
self
|
235
|
+
end
|
236
|
+
|
237
|
+
def test_step(step, mapper)
|
238
|
+
mapper.map { raise Failure } if step.name =~ /fail/
|
239
|
+
mapper.map {} if step.name =~ /pass/
|
240
|
+
self
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
it "executes the test cases in the suite" do
|
245
|
+
gherkin = gherkin do
|
246
|
+
feature 'Feature name' do
|
247
|
+
scenario 'The one that passes' do
|
248
|
+
step 'passing'
|
249
|
+
end
|
250
|
+
|
251
|
+
scenario 'The one that fails' do
|
252
|
+
step 'passing'
|
253
|
+
step 'failing'
|
254
|
+
step 'passing'
|
255
|
+
step 'undefined'
|
256
|
+
end
|
257
|
+
end
|
258
|
+
end
|
259
|
+
report = SummaryReport.new
|
260
|
+
mappings = StepTestMappings.new
|
261
|
+
|
262
|
+
execute [gherkin], mappings, report
|
263
|
+
|
264
|
+
expect( report.test_cases.total ).to eq 2
|
265
|
+
expect( report.test_cases.total_passed ).to eq 1
|
266
|
+
expect( report.test_cases.total_failed ).to eq 1
|
267
|
+
expect( report.test_steps.total ).to eq 5
|
268
|
+
expect( report.test_steps.total_failed ).to eq 1
|
269
|
+
expect( report.test_steps.total_passed ).to eq 2
|
270
|
+
expect( report.test_steps.total_skipped ).to eq 1
|
271
|
+
expect( report.test_steps.total_undefined ).to eq 1
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
context "with hooks" do
|
276
|
+
class HookTestMappings
|
277
|
+
Failure = Class.new(StandardError)
|
278
|
+
|
279
|
+
def test_case(test_case, mapper)
|
280
|
+
case test_case.name
|
281
|
+
when /fail before/
|
282
|
+
mapper.before { raise Failure }
|
283
|
+
mapper.after { 'This hook will be skipped' }
|
284
|
+
when /fail after/
|
285
|
+
mapper.after { raise Failure }
|
286
|
+
end
|
287
|
+
self
|
288
|
+
end
|
289
|
+
|
290
|
+
def test_step(step, mapper)
|
291
|
+
mapper.map {} # all steps pass
|
292
|
+
self
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
it "executes the test cases in the suite" do
|
297
|
+
gherkin = gherkin do
|
298
|
+
feature do
|
299
|
+
scenario 'fail before' do
|
300
|
+
step 'passing'
|
301
|
+
end
|
302
|
+
|
303
|
+
scenario 'fail after' do
|
304
|
+
step 'passing'
|
305
|
+
end
|
306
|
+
|
307
|
+
scenario 'passing' do
|
308
|
+
step 'passing'
|
309
|
+
end
|
310
|
+
end
|
311
|
+
end
|
312
|
+
report = SummaryReport.new
|
313
|
+
mappings = HookTestMappings.new
|
314
|
+
|
315
|
+
execute [gherkin], mappings, report
|
316
|
+
|
317
|
+
expect( report.test_steps.total ).to eq(6)
|
318
|
+
expect( report.test_steps.total_failed ).to eq(2)
|
319
|
+
expect( report.test_cases.total ).to eq(3)
|
320
|
+
expect( report.test_cases.total_passed ).to eq(1)
|
321
|
+
expect( report.test_cases.total_failed ).to eq(2)
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
325
|
+
context "with around hooks" do
|
326
|
+
class AroundHookTestMappings
|
327
|
+
attr_reader :logger
|
328
|
+
|
329
|
+
def initialize
|
330
|
+
@logger = []
|
331
|
+
end
|
332
|
+
|
333
|
+
def test_case(test_case, mapper)
|
334
|
+
logger = @logger
|
335
|
+
mapper.around do |run_scenario|
|
336
|
+
logger << :before
|
337
|
+
run_scenario.call
|
338
|
+
logger << :middle
|
339
|
+
run_scenario.call
|
340
|
+
logger << :after
|
341
|
+
end
|
342
|
+
self
|
343
|
+
end
|
344
|
+
|
345
|
+
def test_step(step, mapper)
|
346
|
+
logger = @logger
|
347
|
+
mapper.map do
|
348
|
+
logger << :during
|
349
|
+
end
|
350
|
+
self
|
351
|
+
end
|
352
|
+
end
|
353
|
+
|
354
|
+
it "executes the test cases in the suite" do
|
355
|
+
gherkin = gherkin do
|
356
|
+
feature do
|
357
|
+
scenario do
|
358
|
+
step
|
359
|
+
end
|
360
|
+
end
|
361
|
+
end
|
362
|
+
report = SummaryReport.new
|
363
|
+
mappings = AroundHookTestMappings.new
|
364
|
+
|
365
|
+
execute [gherkin], mappings, report
|
366
|
+
|
367
|
+
expect( report.test_cases.total ).to eq 1
|
368
|
+
expect( report.test_cases.total_passed ).to eq 1
|
369
|
+
expect( report.test_cases.total_failed ).to eq 0
|
370
|
+
expect( mappings.logger ).to eq [:before, :during, :middle, :during, :after]
|
371
|
+
end
|
372
|
+
end
|
373
|
+
|
374
|
+
require 'cucumber/core/test/filters'
|
375
|
+
it "filters test cases by tag" do
|
376
|
+
gherkin = gherkin do
|
377
|
+
feature do
|
378
|
+
scenario do
|
379
|
+
step
|
380
|
+
end
|
381
|
+
|
382
|
+
scenario tags: '@a @b' do
|
383
|
+
step
|
384
|
+
end
|
385
|
+
|
386
|
+
scenario tags: '@a' do
|
387
|
+
step
|
388
|
+
end
|
389
|
+
end
|
390
|
+
end
|
391
|
+
report = SummaryReport.new
|
392
|
+
mappings = HookTestMappings.new
|
393
|
+
|
394
|
+
execute [gherkin], mappings, report, [[Cucumber::Core::Test::TagFilter, ['@a']]]
|
395
|
+
|
396
|
+
expect( report.test_cases.total ).to eq 2
|
397
|
+
end
|
398
|
+
|
399
|
+
it "filters test cases by name" do
|
400
|
+
gherkin = gherkin do
|
401
|
+
feature 'first feature' do
|
402
|
+
scenario 'first scenario' do
|
403
|
+
step 'missing'
|
404
|
+
end
|
405
|
+
scenario 'second' do
|
406
|
+
step 'missing'
|
407
|
+
end
|
408
|
+
end
|
409
|
+
end
|
410
|
+
report = SummaryReport.new
|
411
|
+
mappings = HookTestMappings.new
|
412
|
+
|
413
|
+
execute [gherkin], mappings, report, [[Cucumber::Core::Test::NameFilter, [[/scenario/]]]]
|
414
|
+
|
415
|
+
expect( report.test_cases.total ).to eq 1
|
416
|
+
end
|
417
|
+
end
|
418
|
+
end
|
419
|
+
end
|