cucumber-core 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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