cucumber-core 0.2.0 → 1.0.0.beta.1

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