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.
Files changed (69) hide show
  1. checksums.yaml +7 -0
  2. data/.coveralls.yml +1 -0
  3. data/.rspec +1 -0
  4. data/.ruby-gemset +1 -0
  5. data/.travis.yml +16 -0
  6. data/Gemfile +2 -0
  7. data/LICENSE +20 -0
  8. data/README.md +9 -0
  9. data/Rakefile +24 -0
  10. data/cucumber-core.gemspec +32 -0
  11. data/lib/cucumber/core.rb +37 -0
  12. data/lib/cucumber/core/ast.rb +13 -0
  13. data/lib/cucumber/core/ast/background.rb +33 -0
  14. data/lib/cucumber/core/ast/comment.rb +17 -0
  15. data/lib/cucumber/core/ast/data_table.rb +326 -0
  16. data/lib/cucumber/core/ast/describes_itself.rb +16 -0
  17. data/lib/cucumber/core/ast/doc_string.rb +83 -0
  18. data/lib/cucumber/core/ast/empty_background.rb +12 -0
  19. data/lib/cucumber/core/ast/examples_table.rb +95 -0
  20. data/lib/cucumber/core/ast/feature.rb +62 -0
  21. data/lib/cucumber/core/ast/location.rb +140 -0
  22. data/lib/cucumber/core/ast/multiline_argument.rb +33 -0
  23. data/lib/cucumber/core/ast/names.rb +19 -0
  24. data/lib/cucumber/core/ast/outline_step.rb +51 -0
  25. data/lib/cucumber/core/ast/scenario.rb +43 -0
  26. data/lib/cucumber/core/ast/scenario_outline.rb +44 -0
  27. data/lib/cucumber/core/ast/step.rb +38 -0
  28. data/lib/cucumber/core/ast/tag.rb +14 -0
  29. data/lib/cucumber/core/compiler.rb +136 -0
  30. data/lib/cucumber/core/gherkin/ast_builder.rb +315 -0
  31. data/lib/cucumber/core/gherkin/document.rb +20 -0
  32. data/lib/cucumber/core/gherkin/parser.rb +45 -0
  33. data/lib/cucumber/core/gherkin/writer.rb +220 -0
  34. data/lib/cucumber/core/gherkin/writer/helpers.rb +178 -0
  35. data/lib/cucumber/core/platform.rb +30 -0
  36. data/lib/cucumber/core/test/case.rb +143 -0
  37. data/lib/cucumber/core/test/filters.rb +48 -0
  38. data/lib/cucumber/core/test/filters/tag_filter.rb +110 -0
  39. data/lib/cucumber/core/test/hook_compiler.rb +109 -0
  40. data/lib/cucumber/core/test/mapper.rb +56 -0
  41. data/lib/cucumber/core/test/mapping.rb +67 -0
  42. data/lib/cucumber/core/test/result.rb +191 -0
  43. data/lib/cucumber/core/test/runner.rb +149 -0
  44. data/lib/cucumber/core/test/step.rb +69 -0
  45. data/lib/cucumber/core/test/timer.rb +31 -0
  46. data/lib/cucumber/core/version.rb +9 -0
  47. data/lib/cucumber/initializer.rb +18 -0
  48. data/spec/capture_warnings.rb +68 -0
  49. data/spec/coverage.rb +10 -0
  50. data/spec/cucumber/core/ast/data_table_spec.rb +139 -0
  51. data/spec/cucumber/core/ast/doc_string_spec.rb +77 -0
  52. data/spec/cucumber/core/ast/examples_table_spec.rb +87 -0
  53. data/spec/cucumber/core/ast/location_spec.rb +105 -0
  54. data/spec/cucumber/core/ast/outline_step_spec.rb +77 -0
  55. data/spec/cucumber/core/ast/step_spec.rb +44 -0
  56. data/spec/cucumber/core/compiler_spec.rb +249 -0
  57. data/spec/cucumber/core/gherkin/parser_spec.rb +182 -0
  58. data/spec/cucumber/core/gherkin/writer_spec.rb +332 -0
  59. data/spec/cucumber/core/test/case_spec.rb +416 -0
  60. data/spec/cucumber/core/test/hook_compiler_spec.rb +78 -0
  61. data/spec/cucumber/core/test/mapper_spec.rb +68 -0
  62. data/spec/cucumber/core/test/mapping_spec.rb +103 -0
  63. data/spec/cucumber/core/test/result_spec.rb +178 -0
  64. data/spec/cucumber/core/test/runner_spec.rb +265 -0
  65. data/spec/cucumber/core/test/step_spec.rb +58 -0
  66. data/spec/cucumber/core/test/timer_spec.rb +13 -0
  67. data/spec/cucumber/core_spec.rb +419 -0
  68. data/spec/cucumber/initializer_spec.rb +49 -0
  69. 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,13 @@
1
+ require 'cucumber/core/test/timer'
2
+
3
+ module Cucumber
4
+ module Core
5
+ module Test
6
+ describe Timer do
7
+ it "would be slow to test" do
8
+ # so we won't
9
+ end
10
+ end
11
+ end
12
+ end
13
+ 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