cucumber-core 0.2.0 → 1.0.0.beta.1

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 (43) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -1
  3. data/cucumber-core.gemspec +1 -1
  4. data/lib/cucumber/core.rb +6 -6
  5. data/lib/cucumber/core/ast/data_table.rb +8 -0
  6. data/lib/cucumber/core/ast/describes_itself.rb +1 -1
  7. data/lib/cucumber/core/ast/examples_table.rb +8 -2
  8. data/lib/cucumber/core/ast/feature.rb +2 -3
  9. data/lib/cucumber/core/ast/location.rb +11 -6
  10. data/lib/cucumber/core/ast/names.rb +10 -1
  11. data/lib/cucumber/core/ast/outline_step.rb +6 -3
  12. data/lib/cucumber/core/ast/scenario.rb +0 -1
  13. data/lib/cucumber/core/ast/scenario_outline.rb +2 -3
  14. data/lib/cucumber/core/ast/step.rb +2 -2
  15. data/lib/cucumber/core/gherkin/ast_builder.rb +4 -1
  16. data/lib/cucumber/core/test/case.rb +10 -6
  17. data/lib/cucumber/core/test/filters/debug_filter.rb +28 -0
  18. data/lib/cucumber/core/test/filters/tag_filter.rb +1 -1
  19. data/lib/cucumber/core/test/hooks.rb +76 -0
  20. data/lib/cucumber/core/test/mapper.rb +101 -19
  21. data/lib/cucumber/core/test/mapping.rb +15 -4
  22. data/lib/cucumber/core/test/result.rb +39 -27
  23. data/lib/cucumber/core/test/runner.rb +76 -81
  24. data/lib/cucumber/core/test/step.rb +10 -18
  25. data/lib/cucumber/core/version.rb +1 -1
  26. data/spec/cucumber/core/ast/data_table_spec.rb +12 -0
  27. data/spec/cucumber/core/ast/location_spec.rb +8 -1
  28. data/spec/cucumber/core/ast/outline_step_spec.rb +11 -4
  29. data/spec/cucumber/core/ast/step_spec.rb +2 -2
  30. data/spec/cucumber/core/compiler_spec.rb +6 -6
  31. data/spec/cucumber/core/gherkin/parser_spec.rb +31 -18
  32. data/spec/cucumber/core/test/case_spec.rb +24 -24
  33. data/spec/cucumber/core/test/hooks_spec.rb +30 -0
  34. data/spec/cucumber/core/test/mapper_spec.rb +115 -1
  35. data/spec/cucumber/core/test/mapping_spec.rb +22 -6
  36. data/spec/cucumber/core/test/result_spec.rb +0 -8
  37. data/spec/cucumber/core/test/runner_spec.rb +31 -97
  38. data/spec/cucumber/core/test/step_spec.rb +24 -16
  39. data/spec/cucumber/core_spec.rb +109 -16
  40. data/spec/report_api_spy.rb +24 -0
  41. metadata +32 -28
  42. data/lib/cucumber/core/test/hook_compiler.rb +0 -109
  43. data/spec/cucumber/core/test/hook_compiler_spec.rb +0 -78
@@ -0,0 +1,30 @@
1
+ require 'cucumber/core/test/hooks'
2
+ module Cucumber::Core::Test::Hooks
3
+ shared_examples_for 'a source node' do
4
+ it "responds to name" do
5
+ expect( subject.name ).to be_a(String)
6
+ end
7
+
8
+ it "responds to location" do
9
+ expect( subject.location ).to eq(location)
10
+ end
11
+
12
+ it "responds to match_locations?" do
13
+ expect( subject.match_locations? [location] ).to be_truthy
14
+ expect( subject.match_locations? [] ).to be_falsey
15
+ end
16
+ end
17
+
18
+ require 'cucumber/core/ast/location'
19
+ describe BeforeHook do
20
+ subject { BeforeHook.new(location) }
21
+ let(:location) { Cucumber::Core::Ast::Location.new('hooks.rb', 1) }
22
+ it_behaves_like 'a source node'
23
+ end
24
+
25
+ describe AfterHook do
26
+ subject { AfterHook.new(location) }
27
+ let(:location) { Cucumber::Core::Ast::Location.new('hooks.rb', 1) }
28
+ it_behaves_like 'a source node'
29
+ end
30
+ end
@@ -1,6 +1,7 @@
1
1
  require 'cucumber/core/test/mapper'
2
2
  require 'cucumber/core/test/case'
3
3
  require 'cucumber/core/test/step'
4
+ require 'cucumber/core/ast'
4
5
 
5
6
  module Cucumber
6
7
  module Core
@@ -8,6 +9,9 @@ module Cucumber
8
9
  describe Mapper do
9
10
 
10
11
  ExampleMappings = Struct.new(:app) do
12
+ def test_case(test_case, mapper)
13
+ end
14
+
11
15
  def test_step(test_step, mapper)
12
16
  mapper.map { app.do_something } if test_step.name == 'mapped'
13
17
  end
@@ -15,7 +19,7 @@ module Cucumber
15
19
 
16
20
  let(:mapper) { Mapper.new(mappings, receiver) }
17
21
  let(:receiver) { double('receiver') }
18
- before { receiver.stub(:test_case).and_yield }
22
+ before { allow(receiver).to receive(:test_case).and_yield(receiver) }
19
23
  let(:mappings) { ExampleMappings.new(app) }
20
24
  let(:app) { double('app') }
21
25
 
@@ -61,7 +65,117 @@ module Cucumber
61
65
  test_case.describe_to mapper
62
66
  end
63
67
  end
68
+
69
+ context "mapping hooks" do
70
+ let(:test_case) { Case.new([test_step], source) }
71
+ let(:test_step) { Step.new([Ast::Step.new(:language, :location, :keyword, :name, :multiline_arg)]) }
72
+ let(:source) { [feature, scenario] }
73
+ let(:feature) { double('feature') }
74
+ let(:scenario) { double('scenario', location: 'test') }
75
+
76
+ it "prepends before hooks to the test case" do
77
+ allow( mappings ).to receive(:test_case) do |test_case, mapper|
78
+ mapper.before {}
79
+ end
80
+ expect( receiver ).to receive(:test_case) do |test_case|
81
+ expect( test_case.step_count ).to eq 2
82
+ end
83
+ test_case.describe_to mapper
84
+ end
85
+
86
+ it "appends after hooks to the test case" do
87
+ allow( mappings ).to receive(:test_case) do |test_case, mapper|
88
+ mapper.after {}
89
+ end
90
+ expect( receiver ).to receive(:test_case) do |test_case|
91
+ expect( test_case.step_count ).to eq 2
92
+ end
93
+ test_case.describe_to mapper
94
+ end
95
+
96
+ it "adds hooks in the right order" do
97
+ log = double
98
+ allow(mappings).to receive(:test_case) do |test_case, mapper|
99
+ mapper.before { log.before }
100
+ mapper.after { log.after }
101
+ end
102
+ mapped_step = test_step.with_mapping { log.step }
103
+ test_case = Case.new([mapped_step], source)
104
+
105
+ expect( log ).to receive(:before).ordered
106
+ expect( log ).to receive(:step).ordered
107
+ expect( log ).to receive(:after).ordered
108
+
109
+ allow(receiver).to receive(:test_case).and_yield(receiver)
110
+ allow(receiver).to receive(:test_step) do |test_step|
111
+ test_step.execute
112
+ end
113
+
114
+ test_case.describe_to mapper
115
+ end
116
+
117
+ it "sets the source to include the before hook, scenario and feature" do
118
+ test_case = Case.new([], source)
119
+ allow(mappings).to receive(:test_case) do |test_case_to_be_mapped, mapper|
120
+ mapper.before {}
121
+ end
122
+ allow(receiver).to receive(:test_case).and_yield(receiver)
123
+ allow(receiver).to receive(:test_step) do |test_step|
124
+ args = double('args')
125
+ visitor = double('visitor')
126
+ expect( feature ).to receive(:describe_to)
127
+ expect( scenario ).to receive(:describe_to)
128
+ expect( visitor ).to receive(:before_hook) do |hook, hook_args|
129
+ expect( args ).to eq(hook_args)
130
+ expect( hook.location.to_s ).to eq("#{__FILE__}:120")
131
+ end
132
+ test_step.describe_source_to(visitor, args)
133
+ end
134
+ test_case.describe_to mapper
135
+ end
136
+
137
+ it "sets the source to include the after hook" do
138
+ test_case = Case.new([], source)
139
+ allow(mappings).to receive(:test_case) do |test_case_to_be_mapped, mapper|
140
+ mapper.after {}
141
+ end
142
+ allow(receiver).to receive(:test_case).and_yield(receiver)
143
+ allow(receiver).to receive(:test_step) do |test_step|
144
+ args = double('args')
145
+ visitor = double('visitor')
146
+ expect( feature ).to receive(:describe_to)
147
+ expect( scenario ).to receive(:describe_to)
148
+ expect( visitor ).to receive(:after_hook) do |hook, hook_args|
149
+ expect( args ).to eq(hook_args)
150
+ expect( hook.location.to_s ).to eq("#{__FILE__}:140")
151
+ end
152
+ test_step.describe_source_to(visitor, args)
153
+ end
154
+ test_case.describe_to mapper
155
+ end
156
+
157
+ it "appends after_step hooks to the test step" do
158
+ allow(mappings).to receive(:test_step) do |test_step, mapper|
159
+ mapper.after {}
160
+ end
161
+ args = double('args')
162
+ visitor = double('visitor')
163
+ allow(receiver).to receive(:test_case).and_yield(receiver)
164
+ allow(receiver).to receive(:test_step) do |test_step|
165
+ test_step.describe_source_to(visitor, args)
166
+ end
167
+ expect( visitor ).to receive(:step).ordered
168
+ expect( visitor ).to receive(:after_step_hook) do |hook, hook_args|
169
+ expect( args ).to eq(hook_args)
170
+ expect( hook.location.to_s ).to eq("#{__FILE__}:159")
171
+ end.once.ordered
172
+ expect( visitor ).to receive(:step).ordered
173
+ test_case.describe_to mapper
174
+ end
175
+
176
+ end
64
177
  end
178
+
65
179
  end
66
180
  end
67
181
  end
@@ -17,7 +17,7 @@ module Cucumber
17
17
  executed = false
18
18
  mapping = Mapping.new { executed = true }
19
19
  mapping.execute
20
- expect( executed ).to be_true
20
+ expect( executed ).to be_truthy
21
21
  end
22
22
 
23
23
  it "returns a passed result if the block doesn't fail" do
@@ -33,7 +33,7 @@ module Cucumber
33
33
  expect( result.exception ).to eq exception
34
34
  end
35
35
 
36
- it "returns a pending result if a pending error is raised" do
36
+ it "returns a pending result if a Result::Pending error is raised" do
37
37
  exception = Result::Pending.new("TODO")
38
38
  mapping = Mapping.new { raise exception }
39
39
  result = mapping.execute
@@ -41,12 +41,28 @@ module Cucumber
41
41
  expect( result.message ).to eq "TODO"
42
42
  end
43
43
 
44
+ it "returns a skipped result if a Result::Skipped error is raised" do
45
+ exception = Result::Skipped.new("Not working right now")
46
+ mapping = Mapping.new { raise exception }
47
+ result = mapping.execute
48
+ expect( result ).to be_skipped
49
+ expect( result.message ).to eq "Not working right now"
50
+ end
51
+
52
+ it "returns an undefined result if a Result::Undefined error is raised" do
53
+ exception = Result::Undefined.new("new step")
54
+ mapping = Mapping.new { raise exception }
55
+ result = mapping.execute
56
+ expect( result ).to be_undefined
57
+ expect( result.message ).to eq "new step"
58
+ end
59
+
44
60
  context "recording the duration" do
45
61
  before do
46
62
  time = double
47
- Time.stub(now: time)
48
- time.stub(:nsec).and_return(946752000, 946752001)
49
- time.stub(:to_i).and_return(1377009235, 1377009235)
63
+ allow( Time ).to receive(:now) { time }
64
+ allow( time ).to receive(:nsec).and_return(946752000, 946752001)
65
+ allow( time ).to receive(:to_i).and_return(1377009235, 1377009235)
50
66
  end
51
67
 
52
68
  it "records the nanoseconds duration of the execution on the result" do
@@ -69,7 +85,7 @@ module Cucumber
69
85
  executed = false
70
86
  mapping = Mapping.new { executed = true }
71
87
  mapping.skip
72
- expect( executed ).to be_false
88
+ expect( executed ).to be_falsey
73
89
  end
74
90
 
75
91
  it "returns a skipped result" do
@@ -72,10 +72,6 @@ module Cucumber::Core::Test
72
72
  result.describe_to(visitor, args)
73
73
  end
74
74
 
75
- it "has no duration" do
76
- expect { result.duration }.to raise_error NoMethodError
77
- end
78
-
79
75
  it { expect( result ).not_to be_passed }
80
76
  it { expect( result ).not_to be_failed }
81
77
  it { expect( result ).not_to be_undefined }
@@ -106,10 +102,6 @@ module Cucumber::Core::Test
106
102
  result.describe_to(visitor, args)
107
103
  end
108
104
 
109
- it "has no duration" do
110
- expect { result.duration }.to raise_error NoMethodError
111
- end
112
-
113
105
  it { expect( result ).not_to be_passed }
114
106
  it { expect( result ).not_to be_failed }
115
107
  it { expect( result ).not_to be_undefined }
@@ -9,18 +9,23 @@ module Cucumber::Core::Test
9
9
  let(:source) { double }
10
10
  let(:runner) { Runner.new(report) }
11
11
  let(:report) { double.as_null_object }
12
- let(:passing) { Step.new([double]).map {} }
13
- let(:failing) { Step.new([double]).map { raise exception } }
14
- let(:pending) { Step.new([double]).map { raise Result::Pending.new("TODO") } }
12
+ let(:passing) { Step.new([double]).with_mapping {} }
13
+ let(:failing) { Step.new([double]).with_mapping { raise exception } }
14
+ let(:pending) { Step.new([double]).with_mapping { raise Result::Pending.new("TODO") } }
15
+ let(:skipping) { Step.new([double]).with_mapping { raise Result::Skipped.new } }
15
16
  let(:undefined) { Step.new([double]) }
16
17
  let(:exception) { StandardError.new('test error') }
17
18
 
19
+ before do
20
+ allow(report).to receive(:before_test_case)
21
+ end
22
+
18
23
  context "reporting the duration of a test case" do
19
24
  before do
20
25
  time = double
21
- Time.stub(now: time)
22
- time.stub(:nsec).and_return(946752000, 946752001)
23
- time.stub(:to_i).and_return(1377009235, 1377009235)
26
+ allow(Time).to receive(:now).and_return(time)
27
+ allow(time).to receive(:nsec).and_return(946752000, 946752001)
28
+ allow(time).to receive(:to_i).and_return(1377009235, 1377009235)
24
29
  end
25
30
 
26
31
  context "for a passing test case" do
@@ -49,6 +54,7 @@ module Cucumber::Core::Test
49
54
  context "reporting the exception that failed a test case" do
50
55
  let(:test_steps) { [failing] }
51
56
  it "sets the exception on the result" do
57
+ allow(report).to receive(:before_test_case)
52
58
  expect( report ).to receive(:after_test_case) do |reported_test_case, result|
53
59
  expect( result.exception ).to eq exception
54
60
  end
@@ -108,6 +114,17 @@ module Cucumber::Core::Test
108
114
  end
109
115
  end
110
116
 
117
+ context "a skipping step" do
118
+ let(:test_steps) { [skipping] }
119
+
120
+ it "reports a skipped test case" do
121
+ expect( report ).to receive(:after_test_case) do |test_case, result|
122
+ expect( result ).to be_skipped
123
+ end
124
+ test_case.describe_to runner
125
+ end
126
+ end
127
+
111
128
  context 'that fail' do
112
129
  let(:test_steps) { [ failing ] }
113
130
 
@@ -122,6 +139,14 @@ module Cucumber::Core::Test
122
139
  context 'where the first step fails' do
123
140
  let(:test_steps) { [ failing, passing ] }
124
141
 
142
+ it 'executes the after hook at the end regardless of the failure' do
143
+ expect( report ).to receive(:after_test_case) do |test_case, result|
144
+ expect( result ).to be_failed
145
+ expect( result.exception ).to eq exception
146
+ end
147
+ test_case.describe_to runner
148
+ end
149
+
125
150
  it 'reports the first step as failed' do
126
151
  expect( report ).to receive(:after_test_step).with(failing, anything) do |test_step, result|
127
152
  expect( result ).to be_failed
@@ -171,95 +196,4 @@ module Cucumber::Core::Test
171
196
  end
172
197
 
173
198
  end
174
-
175
- describe 'with the dry run strategy' do
176
-
177
- let(:report) { double(:report).as_null_object }
178
- let(:source) { double(:source) }
179
- let(:runner) { Runner.new(report, :run_mode => :dry_run) }
180
- let(:passing) { Step.new([double]).map {} }
181
- let(:undefined) { Step.new([double]) }
182
- let(:test_case) { Case.new(test_steps, source) }
183
-
184
- context 'with a passing step' do
185
- let(:test_steps) { [passing] }
186
-
187
- it 'reports the test case as skipped' do
188
- report.should_receive(:after_test_case) do |test_case, result|
189
- result.should be_skipped
190
- end
191
- test_case.describe_to runner
192
- end
193
-
194
- it 'reports the test step has been skipped' do
195
- report.should_receive(:after_test_step) do |test_step, result|
196
- result.should be_skipped
197
- end
198
- test_case.describe_to runner
199
- end
200
- end
201
-
202
- context 'with a undefined step' do
203
- let(:test_steps) { [undefined] }
204
-
205
- it 'reports the test case as undefined' do
206
- report.should_receive(:after_test_case) do |test_case, result|
207
- result.should be_undefined
208
- end
209
- test_case.describe_to runner
210
- end
211
-
212
-
213
- it 'reports the test step as undefined' do
214
- report.should_receive(:after_test_step) do |test_step, result|
215
- result.should be_undefined
216
- end
217
- test_case.describe_to runner
218
- end
219
- end
220
-
221
- context 'with passing and undefined steps' do
222
- let(:test_steps) { [passing, undefined] }
223
-
224
- it 'reports the test case as undefined' do
225
- report.should_receive(:after_test_case) do |test_case, result|
226
- result.should be_undefined
227
- end
228
- test_case.describe_to runner
229
- end
230
-
231
- it 'reports the passing step as skipped' do
232
- report.should_receive(:after_test_step).with(passing, anything) do |test_case, result|
233
- result.should be_skipped
234
- end
235
- test_case.describe_to runner
236
- end
237
-
238
- it 'reports the undefined step as undefined' do
239
- report.should_receive(:after_test_step).with(undefined, anything) do |test_case, result|
240
- result.should be_undefined
241
- end
242
- test_case.describe_to runner
243
- end
244
- end
245
-
246
- context 'with multiple test cases' do
247
- context 'when the first test case is undefined' do
248
- let(:first_test_case) { Case.new([undefined], source) }
249
- let(:last_test_case) { Case.new([passing], source) }
250
- let(:test_cases) { [first_test_case, last_test_case] }
251
-
252
- it 'reports the results correctly for the following test case' do
253
- report.
254
- should_receive(:after_test_case).
255
- with(last_test_case, anything) do |reported_test_case, result|
256
- result.should be_skipped
257
- end
258
-
259
- test_cases.each { |c| c.describe_to runner }
260
- end
261
- end
262
- end
263
- end
264
-
265
199
  end
@@ -13,45 +13,53 @@ module Cucumber::Core::Test
13
13
  end
14
14
 
15
15
  it "describes its source to a visitor" do
16
- feature, scenario, step = double, double, double
16
+ feature, scenario, step_or_hook = double, double, double
17
17
  visitor = double
18
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])
19
+ expect( feature ).to receive(:describe_to).with(visitor, args)
20
+ expect( scenario ).to receive(:describe_to).with(visitor, args)
21
+ expect( step_or_hook ).to receive(:describe_to).with(visitor, args)
22
+ test_step = Step.new([feature, scenario, step_or_hook])
23
23
  test_step.describe_source_to(visitor, args)
24
24
  end
25
25
  end
26
26
 
27
- describe "executing a step" do
27
+ describe "executing" do
28
28
  let(:ast_step) { double }
29
29
 
30
- context "when a passing mapping exists for the step" do
30
+ context "when a passing mapping exists" do
31
31
  it "returns a passing result" do
32
- test_step = Step.new([ast_step]).map {}
32
+ test_step = Step.new([ast_step]).with_mapping {}
33
33
  expect( test_step.execute ).to be_passed
34
34
  end
35
35
  end
36
36
 
37
- context "when a failing mapping exists for the step" do
37
+ context "when a failing mapping exists" do
38
38
  let(:exception) { StandardError.new('oops') }
39
39
 
40
40
  it "returns a failing result" do
41
- test_step = Step.new([ast_step]).map { raise exception }
41
+ test_step = Step.new([ast_step]).with_mapping { raise exception }
42
42
  result = test_step.execute
43
43
  expect( result ).to be_failed
44
44
  expect( result.exception ).to eq exception
45
45
  end
46
46
  end
47
+
48
+ context "with no mapping" do
49
+ it "returns an Undefined result" do
50
+ test_step = Step.new([ast_step])
51
+ result = test_step.execute
52
+ expect( result ).to be_undefined
53
+ end
54
+ end
47
55
  end
48
56
 
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
57
+ it "exposes the name and location of the AST step or hook as attributes" do
58
+ name, location = double, double
59
+ step_or_hook = double(name: name, location: location)
60
+ test_step = Step.new([step_or_hook])
61
+ expect( test_step.name ).to eq name
62
+ expect( test_step.location ).to eq location
55
63
  end
56
64
 
57
65
  end