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,416 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'cucumber/core'
3
+ require 'cucumber/core/gherkin/writer'
4
+ require 'cucumber/core/platform'
5
+ require 'cucumber/core/test/case'
6
+ require 'unindent'
7
+
8
+ module Cucumber
9
+ module Core
10
+ module Test
11
+ describe Case do
12
+ include Core
13
+ include Core::Gherkin::Writer
14
+
15
+ let(:test_case) { Test::Case.new(test_steps, [feature, scenario]) }
16
+ let(:feature) { double }
17
+ let(:scenario) { double }
18
+ let(:test_steps) { [double, double] }
19
+
20
+ context 'describing itself' do
21
+ it "describes itself to a visitor" do
22
+ visitor = double
23
+ args = double
24
+ expect( visitor ).to receive(:test_case).with(test_case, args)
25
+ test_case.describe_to(visitor, args)
26
+ end
27
+
28
+ it "asks each test_step to describe themselves to the visitor" do
29
+ visitor = double
30
+ args = double
31
+ test_steps.each do |test_step|
32
+ expect( test_step ).to receive(:describe_to).with(visitor, args)
33
+ end
34
+ visitor.stub(:test_case).and_yield
35
+ test_case.describe_to(visitor, args)
36
+ end
37
+
38
+ it "describes around hooks in order" do
39
+ visitor = double
40
+ visitor.stub(:test_case).and_yield
41
+ first_hook, second_hook = double, double
42
+ first_hook.should_receive(:describe_to).ordered.and_yield
43
+ second_hook.should_receive(:describe_to).ordered.and_yield
44
+ around_hooks = [first_hook, second_hook]
45
+ Test::Case.new([], [], around_hooks).describe_to(visitor, double)
46
+ end
47
+
48
+ it "describes its source to a visitor" do
49
+ visitor = double
50
+ args = double
51
+ expect( feature ).to receive(:describe_to).with(visitor, args)
52
+ expect( scenario ).to receive(:describe_to).with(visitor, args)
53
+ test_case.describe_source_to(visitor, args)
54
+ end
55
+
56
+ end
57
+
58
+ describe "#name" do
59
+ context "created from a scenario" do
60
+ it "takes its name from the name of a scenario" do
61
+ gherkin = gherkin do
62
+ feature do
63
+ scenario 'Scenario name' do
64
+ step 'passing'
65
+ end
66
+ end
67
+ end
68
+ receiver = double.as_null_object
69
+
70
+ expect( receiver ).to receive(:test_case) do |test_case|
71
+ expect( test_case.name ).to eq 'Scenario: Scenario name'
72
+ end
73
+ compile([gherkin], receiver)
74
+ end
75
+ end
76
+
77
+ context "created from a scenario outline example" do
78
+ it "takes its name from the name of the scenario outline and examples table" do
79
+ gherkin = gherkin do
80
+ feature do
81
+ scenario_outline 'outline name' do
82
+ step 'passing with arg'
83
+
84
+ examples 'examples name' do
85
+ row 'arg'
86
+ row 'a'
87
+ row 'b'
88
+ end
89
+
90
+ examples '' do
91
+ row 'arg'
92
+ row 'c'
93
+ end
94
+ end
95
+ end
96
+ end
97
+ receiver = double.as_null_object
98
+ expect( receiver ).to receive(:test_case) do |test_case|
99
+ expect( test_case.name ).to eq 'Scenario Outline: outline name, examples name (row 1)'
100
+ end.once.ordered
101
+ expect( receiver ).to receive(:test_case) do |test_case|
102
+ expect( test_case.name ).to eq 'Scenario Outline: outline name, examples name (row 2)'
103
+ end.once.ordered
104
+ expect( receiver ).to receive(:test_case) do |test_case|
105
+ expect( test_case.name ).to eq 'Scenario Outline: outline name, Examples (row 1)'
106
+ end.once.ordered
107
+ compile [gherkin], receiver
108
+ end
109
+ end
110
+ end
111
+
112
+ describe "#location" do
113
+ context "created from a scenario" do
114
+ it "takes its location from the location of the scenario" do
115
+ gherkin = gherkin('features/foo.feature') do
116
+ feature do
117
+ scenario do
118
+ step
119
+ end
120
+ end
121
+ end
122
+ receiver = double.as_null_object
123
+ expect( receiver ).to receive(:test_case) do |test_case|
124
+ expect( test_case.location.to_s ).to eq 'features/foo.feature:3'
125
+ end
126
+ compile([gherkin], receiver)
127
+ end
128
+ end
129
+
130
+ context "created from a scenario outline example" do
131
+ it "takes its location from the location of the scenario outline example row" do
132
+ gherkin = gherkin('features/foo.feature') do
133
+ feature do
134
+ scenario_outline do
135
+ step 'passing with arg'
136
+
137
+ examples do
138
+ row 'arg'
139
+ row '1'
140
+ row '2'
141
+ end
142
+ end
143
+ end
144
+ end
145
+ receiver = double.as_null_object
146
+ expect( receiver ).to receive(:test_case) do |test_case|
147
+ expect( test_case.location.to_s ).to eq 'features/foo.feature:8'
148
+ end.once.ordered
149
+ expect( receiver ).to receive(:test_case) do |test_case|
150
+ expect( test_case.location.to_s ).to eq 'features/foo.feature:9'
151
+ end.once.ordered
152
+ compile [gherkin], receiver
153
+ end
154
+ end
155
+ end
156
+
157
+ describe "#tags" do
158
+ it "includes all tags from the parent feature" do
159
+ gherkin = gherkin do
160
+ feature tags: ['@a', '@b'] do
161
+ scenario tags: ['@c'] do
162
+ step
163
+ end
164
+ scenario_outline tags: ['@d'] do
165
+ step 'passing with arg'
166
+ examples tags: ['@e'] do
167
+ row 'arg'
168
+ row 'x'
169
+ end
170
+ end
171
+ end
172
+ end
173
+ receiver = double.as_null_object
174
+ expect( receiver ).to receive(:test_case) do |test_case|
175
+ expect( test_case.tags.map(&:name) ).to eq ['@a', '@b', '@c']
176
+ end.once.ordered
177
+ expect( receiver ).to receive(:test_case) do |test_case|
178
+ expect( test_case.tags.map(&:name) ).to eq ['@a', '@b', '@d', '@e']
179
+ end.once.ordered
180
+ compile [gherkin], receiver
181
+ end
182
+ end
183
+
184
+ describe "matching tags" do
185
+ it "matches boolean expressions of tags" do
186
+ gherkin = gherkin do
187
+ feature tags: ['@a', '@b'] do
188
+ scenario tags: ['@c'] do
189
+ step
190
+ end
191
+ end
192
+ end
193
+ receiver = double.as_null_object
194
+ expect( receiver ).to receive(:test_case) do |test_case|
195
+ expect( test_case.match_tags?('@a') ).to be_true
196
+ end
197
+ compile [gherkin], receiver
198
+ end
199
+ end
200
+
201
+ describe "matching names" do
202
+ it "matches names against regexp" do
203
+ gherkin = gherkin do
204
+ feature 'first feature' do
205
+ scenario 'scenario' do
206
+ step 'missing'
207
+ end
208
+ end
209
+ end
210
+ receiver = double.as_null_object
211
+ expect( receiver ).to receive(:test_case) do |test_case|
212
+ expect( test_case.match_name?(/feature/) ).to be_true
213
+ end
214
+ compile [gherkin], receiver
215
+ end
216
+ end
217
+
218
+ describe "#language" do
219
+ it 'takes its language from the feature' do
220
+ gherkin = Gherkin::Document.new('features/treasure.feature', %{# language: en-pirate
221
+ Ahoy matey!: Treasure map
222
+ Heave to: Find the treasure
223
+ Gangway!: a map
224
+ })
225
+ receiver = double.as_null_object
226
+ expect( receiver ).to receive(:test_case) do |test_case|
227
+ expect( test_case.language.iso_code ).to eq 'en-pirate'
228
+ end
229
+ compile([gherkin], receiver)
230
+ end
231
+ end
232
+
233
+ describe "matching location" do
234
+ let(:file) { 'features/path/to/the.feature' }
235
+ let(:test_cases) do
236
+ receiver = double.as_null_object
237
+ result = []
238
+ receiver.stub(:test_case) { |test_case| result << test_case }
239
+ compile [source], receiver
240
+ result
241
+ end
242
+
243
+ context "for a scenario" do
244
+ let(:source) do
245
+ Gherkin::Document.new(file, <<-END.unindent)
246
+ Feature:
247
+
248
+ Scenario: one
249
+ Given one a
250
+
251
+ # comment
252
+ @tags
253
+ Scenario: two
254
+ Given two a
255
+ And two b
256
+
257
+ Scenario: three
258
+ Given three b
259
+
260
+ Scenario: with docstring
261
+ Given a docstring
262
+ """
263
+ this is a docstring
264
+ """
265
+
266
+ Scenario: with a table
267
+ Given a table
268
+ | a | b |
269
+ | 1 | 2 |
270
+
271
+ Scenario: empty
272
+ END
273
+ end
274
+
275
+ let(:test_case) do
276
+ test_cases.find { |c| c.name == 'Scenario: two' }
277
+ end
278
+
279
+ it 'matches the precise location of the scenario' do
280
+ location = Ast::Location.new(file, 8)
281
+ expect( test_case.match_locations?([location]) ).to be_true
282
+ end
283
+
284
+ it 'matches the precise location of an empty scenario' do
285
+ empty_scenario_test_case = test_cases.find { |c| c.name == 'Scenario: empty' }
286
+ location = Ast::Location.new(file, 26)
287
+ expect( empty_scenario_test_case.match_locations?([location]) ).to be_true
288
+ end
289
+
290
+ it 'matches multiple locations' do
291
+ good_location = Ast::Location.new(file, 8)
292
+ bad_location = Ast::Location.new(file, 5)
293
+ expect( test_case.match_locations?([good_location, bad_location]) ).to be_true
294
+ end
295
+
296
+ it 'matches a location on the last step of the scenario' do
297
+ location = Ast::Location.new(file, 10)
298
+ expect( test_case.match_locations?([location]) ).to be_true
299
+ end
300
+
301
+ it "matches a location on the scenario's comment" do
302
+ location = Ast::Location.new(file, 6)
303
+ expect( test_case.match_locations?([location]) ).to be_true
304
+ end
305
+
306
+ it "matches a location on the scenario's tags" do
307
+ location = Ast::Location.new(file, 7)
308
+ expect( test_case.match_locations?([location]) ).to be_true
309
+ end
310
+
311
+ it "doesn't match a location after the last step of the scenario" do
312
+ location = Ast::Location.new(file, 11)
313
+ expect( test_case.match_locations?([location]) ).to be_false
314
+ end
315
+
316
+ it "doesn't match a location before the scenario" do
317
+ location = Ast::Location.new(file, 5)
318
+ expect( test_case.match_locations?([location]) ).to be_false
319
+ end
320
+
321
+ context "with a docstring" do
322
+ let(:test_case) do
323
+ test_cases.find { |c| c.name == 'Scenario: with docstring' }
324
+ end
325
+
326
+ it "matches a location at the start the docstring" do
327
+ location = Ast::Location.new(file, 17)
328
+ expect( test_case.match_locations?([location]) ).to be_true
329
+ end
330
+
331
+ it "matches a location in the middle of the docstring" do
332
+ location = Ast::Location.new(file, 18)
333
+ expect( test_case.match_locations?([location]) ).to be_true
334
+ end
335
+ end
336
+
337
+ context "with a table" do
338
+ let(:test_case) do
339
+ test_cases.find { |c| c.name == 'Scenario: with a table' }
340
+ end
341
+
342
+ it "matches a location on the first table row" do
343
+ location = Ast::Location.new(file, 23)
344
+ expect( test_case.match_locations?([location]) ).to be_true
345
+ end
346
+ end
347
+ end
348
+
349
+ context "for a scenario outline" do
350
+ let(:source) do
351
+ Gherkin::Document.new(file, <<-END.unindent)
352
+ Feature:
353
+
354
+ Scenario: one
355
+ Given one a
356
+
357
+ # comment on line 6
358
+ @tags-on-line-7
359
+ Scenario Outline: two
360
+ Given two a
361
+ And two <arg>
362
+
363
+ # comment on line 12
364
+ @tags-on-line-13
365
+ Examples: x1
366
+ | arg |
367
+ | b |
368
+
369
+ Examples: x2
370
+ | arg |
371
+ | c |
372
+
373
+ Scenario: three
374
+ Given three b
375
+ END
376
+ end
377
+
378
+ let(:test_case) do
379
+ test_cases.find { |c| c.name == "Scenario Outline: two, x1 (row 1)" }
380
+ end
381
+
382
+ it 'matches the precise location of the scenario outline examples table row' do
383
+ location = Ast::Location.new(file, 16)
384
+ expect( test_case.match_locations?([location]) ).to be_true
385
+ end
386
+
387
+ it 'matches a location on a step of the scenario outline' do
388
+ location = Ast::Location.new(file, 10)
389
+ expect( test_case.match_locations?([location]) ).to be_true
390
+ end
391
+
392
+ it "matches a location on the scenario outline's comment" do
393
+ location = Ast::Location.new(file, 6)
394
+ expect( test_case.match_locations?([location]) ).to be_true
395
+ end
396
+
397
+ it "matches a location on the scenario outline's tags" do
398
+ location = Ast::Location.new(file, 7)
399
+ expect( test_case.match_locations?([location]) ).to be_true
400
+ end
401
+
402
+ it "doesn't match a location after the last row of the examples table" do
403
+ location = Ast::Location.new(file, 17)
404
+ expect( test_case.match_locations?([location]) ).to be_false
405
+ end
406
+
407
+ it "doesn't match a location before the scenario outline" do
408
+ location = Ast::Location.new(file, 5)
409
+ expect( test_case.match_locations?([location]) ).to be_false
410
+ end
411
+ end
412
+ end
413
+ end
414
+ end
415
+ end
416
+ end
@@ -0,0 +1,78 @@
1
+ require 'cucumber/core/test/hook_compiler'
2
+ require 'cucumber/core/test/case'
3
+ require 'cucumber/core/test/step'
4
+ require 'cucumber/core/test/runner'
5
+ require 'cucumber/core/test/mapper'
6
+
7
+ module Cucumber::Core::Test
8
+ describe HookCompiler do
9
+
10
+ subject(:hook_compiler) { HookCompiler.new(mappings, receiver) }
11
+ let(:mappings) { double('mappings', test_case: nil) }
12
+ let(:receiver) { double('receiver', test_case: nil) }
13
+ let(:test_case) { Case.new([test_step], source) }
14
+ let(:test_step) { Step.new([double('step', name: 'passing')]) }
15
+ let(:source) { [feature, scenario] }
16
+ let(:feature) { double('feature') }
17
+ let(:scenario) { double('scenario') }
18
+
19
+ before do
20
+ expect( receiver ).to receive(:test_case) do |test_case|
21
+ expect( test_case ).to have(1).test_steps
22
+ end
23
+ test_case.describe_to hook_compiler
24
+ end
25
+
26
+ it "prepends before hooks to the test case" do
27
+ mappings.stub(:test_case) do |test_case, mapper|
28
+ mapper.before {}
29
+ end
30
+ expect( receiver ).to receive(:test_case) do |test_case|
31
+ expect( test_case ).to have(2).test_steps
32
+ end
33
+ test_case.describe_to hook_compiler
34
+ end
35
+
36
+ it "appends after hooks to the test case" do
37
+ mappings.stub(:test_case) do |test_case, mapper|
38
+ mapper.after {}
39
+ end
40
+ expect( receiver ).to receive(:test_case) do |test_case|
41
+ expect( test_case ).to have(2).test_steps
42
+ end
43
+ test_case.describe_to hook_compiler
44
+ end
45
+
46
+ it "adds hooks in the right order" do
47
+ log = double
48
+ mappings.stub(:test_case) do |test_case, mapper|
49
+ mapper.before { log.before }
50
+ mapper.after { log.after }
51
+ end
52
+ mappings.stub(:test_step) do |test_step, mapper|
53
+ mapper.map { log.step }
54
+ end
55
+ [:before, :step, :after].each do |message|
56
+ expect( log ).to receive(message).ordered
57
+ end
58
+ runner = Runner.new(double.as_null_object)
59
+ mapper = Mapper.new(mappings, runner)
60
+ hook_compiler = HookCompiler.new(mappings, mapper)
61
+ test_case.describe_to hook_compiler
62
+ end
63
+
64
+ it "sets the source on the hook step to be just the hook" do
65
+ test_case = Case.new([], source)
66
+ mappings.stub(:test_case) do |test_case_to_be_mapped, mapper|
67
+ mapper.before {}
68
+ end
69
+ receiver.stub(:test_case).and_yield
70
+ receiver.stub(:test_step) do |test_step|
71
+ expect( receiver ).to receive(:hook)
72
+ test_step.describe_source_to(receiver)
73
+ end
74
+ test_case.describe_to(hook_compiler)
75
+ end
76
+
77
+ end
78
+ end