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,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